12 February 2018

OruxMapsを中国で使う時の位置ずれに対処

Android版OruxMapsを中国で使う場合、中国独自の測地系で位置ずれする問題に対処する必要がある。

測地系

中国 : GCJ-02測地系
中国以外 : WGS84

詳細はWikipediaの『中国における地理的データの制限』を参照

中国で利用可能なマップ

OruxMapsで利用可能なのは「OpenStreetMap」と、そのデータを流用したマップのみ。下のキャプチャ画像のように、OpenStreetMapではGPSで取得した現在値と地図のズレは無い。

20180212-zhenjiang-openstreetmap.jpg

しかし、WGS84で作られているGoogleMapsを使うと、約500m西北西に現在値がずれて表示される。

20180212-zhenjiang-googlemap.jpg
GoogleMapsでGPSログを表示

ただし、GoogleMapsの衛星画像レイヤーではズレがない

20180212-zhenjiang-googlemapsat.jpg
GoogleMaps衛星画像でGPSログを表示

測地系が違うのであれば、地図も衛星画像も同様に約500mズレるはず。 しかし、衛星画像だけはピッタリとGPSで取得する現在値と一致している。

Googleはいつまで衛星画像レイヤーと地図レイヤーをずれたまま放置するつもりなのだろうか…

OpenStreetMapのデータを用いた地図のバリエーションは「OpenStreetMap Tile servers」にまとめられているので、OruxMapsの onlinemapsources.xml に書き加えれば、これらの地図も使えることになる。

WGS84で作成したウェイポイント・データなどをインポートする場合

パソコンのGoogleマイマップなどで作成したKMLデータ(ズレているデータ)をインポートした場合はややこしい。こんどは、OpenStreetMapの方でズレるので、現地でナビ機能を使う場合に困ることになる。

20180212-hankou-googlemaps.jpg
外部KMLをGoogleMapsにインポート

GoogleMapsのズレた地図で作成したデータは、現実の地球上の位置(緯度・経度)からずれているのだが、ズレている元の地図(GoogleMaps)の上では正しいかのように表示されてしまう。

20180212-hankou-openstreetmap.jpg
外部KMLをOpenStreetMapにインポート

現実の地球上での位置は、もちろんずれた場所に表示されることになる。これでは、中国でナビに使うことは出来ない。

KMLファイルの緯度・経度座標を自動修正するスクリプト

Phythonスクリプト「ChinaMapShift」を用いて、座標を一括変換することが出来る。

これから記述するLinux用修正などを行ったファイル ChinaMapShift-20180212-linux.zipをダウンロードする

このスクリプトの座標変換計算のライブラリがWindows用なので、Linuxの場合は次のように共有ライブラリとしてコンパイルする必要がある。

gcc -shared -rdynamic -fPIC china_shift.c -o china_shift.so

Phythonスクリプトの方も、ライブラリファイル名を書き換えておく

china_shift.py
import argparse  # for parsing input arguments
import csv       # for reading comma-separated floats
 
# load dynamic C library for map shift
from ctypes import *
shift = cdll.LoadLibrary('./china_shift.so')
 
class Location(Structure):
    _fields_ = [
        ('lon', c_double),
        ('lat', c_double)]
 
  〜 以下省略 〜

では、Googleマイマップで作成したKMLを変換してみる。GoogleMapsからエクスポートしたKMLファイルは次のようなものだ

googlemap.kml
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <name>漢口租界</name>
    <Style id="icon-1899-0288D1-nodesc-normal">
      <IconStyle>
        <color>ffd18802</color>
        <scale>1</scale>
 
 〜 中略 〜
 
    <Placemark>
      <name>江汉关大楼</name>
      <styleUrl>#icon-1899-0288D1-nodesc</styleUrl>
      <Point>
        <coordinates>
          114.2974992,30.5761217,0
        </coordinates>
      </Point>
    </Placemark>
 
〜 以下省略 〜

次のコマンドで座標変換を行う。出力ファイル名は output.kml に固定されている。

python china_shift.py -w test.kml

座標変換され出力されたファイルは次のようになっている

googlemap.kml
<ns0:kml xmlns:ns0="http://www.opengis.net/kml/2.2">
  <ns0:Document>
    <ns0:name>&#28450;&#21475;&#31199;&#30028;</ns0:name>
    <ns0:Style id="icon-1899-0288D1-nodesc-normal">
      <ns0:IconStyle>
        <ns0:color>ffd18802</ns0:color>
        <ns0:scale>1</ns0:scale>
 
 〜 中略 〜
 
    <ns0:Placemark>
      <ns0:name>&#27743;&#27721;&#20851;&#22823;&#27004;</ns0:name>
      <ns0:styleUrl>#icon-1899-0288D1-nodesc</ns0:styleUrl>
      <ns0:Point>
        <ns0:coordinates>114.292071939,30.5785578338,0 </ns0:coordinates>
      </ns0:Point>
    </ns0:Placemark>
 
〜 以下省略 〜

出力ファイルに “ ns0: ” とか “ :ns0 ” というような妙なものが付いている。テキストエディタで一括置換で除去するか、ソースコードに次の1行を付け加える

china_shift.py
try:
    tree = et.ElementTree(file = args.input_file)
except IOError:
    print 'Error: input file', args.input_file, 'not found.'
    quit()
 
et.register_namespace('', 'http://www.opengis.net/kml/2.2')
 
# process the file
count = 0

http://www.opengis.net/kml/2.2 は、入力ファイルの先頭行付近にある kml xmlns= の値をコピーする。

出力KMLの先頭行にXMLファイルの定義がないのが気になるなら、次のようにソースコードに追加

china_shift.py
output = open('output.kml', 'w')
output.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
tree.write(output)
output.close()

最終的に、座標変換され出力されたファイルは次のようになる

googlemap.kml
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <name>&#28450;&#21475;&#31199;&#30028;</name>
    <Style id="icon-1899-0288D1-nodesc-normal">
      <IconStyle>
        <color>ffd18802</color>
        <scale>1</scale>
 
 〜 中略 〜
 
    <ns0:Placemark>
          <Placemark>
      <name>&#27743;&#27721;&#20851;&#22823;&#27004;</name>
      <styleUrl>#icon-1899-0288D1-nodesc</styleUrl>
      <Point>
        <coordinates>114.292071939,30.5785578338,0 </coordinates>
      </Point>
    </Placemark>
 
〜 以下省略 〜

この正しい座標に変換後のKMLファイルをOpenStreetMap上にインポートすると、正しい位置に表示される。

20180212-hankou-openstreetmap2.jpg
外部KMLを変換後にOpenStreetMapにインポート

OruxMapsで作成したトラックログをGoogleMapsなどにインポートする

OruxMapsで記録したトラックログを、パソコン上でGoogleマイマップにインポートする場合のファイル変換は、ひと手間掛かる。

(OpenStreetMapなどを用いているgpspruneを使ってブラウズする場合は、GPSログの元データを直接読み込んで問題ない)

OruxMapsで記録されたKMLファイルを「ChinaMapShift」でいきなり変換すると、Googleマイマップでは読み込んでくれない。

そこで、一旦変換しやすい形のKMLファイルに変換した後、「ChinaMapShift」に通すことにする。 (※ この処理に使うトラックログはGPXでもKML形式でもどちらでもOK)

トラックログのGPXファイルを gpsbabel コマンドで KMLファイルに変換 (GPXファイルを読み込む場合は -i gpx とし、KMLファイルを読み込む場合は -i kml とする)

gpsbabel -i gpx -f 2018-02-12\ 0646__20180212_0646.gpx -o kml -F test.kml

さらに、そのKMLファイルを「ChinaMapShift」で変換する

python china_shift.py -g test.kml

このファイルをGoogleマイマップに読み込むと、『ズレたGPSログ』が『ズレたGoogleMaps』にインポートされ、あたかもきっちりとした場所のごとく表示される…

20180212-zhenjiang-googlemaymap.jpg