はじめに
TIG DXユニットの真野です。
ある緯度経度の座標を日本測地系から世界測地系へ変換する際に、everystreet/go-projというパッケージを用いました。私にとって初めてのcgoを用いたライブラリ利用であり、環境構築に少し悩んだので手順をまとめておきます。
なお、類似にgo-spatial/projもありますが、そちらは日本測地系がサポートされていないようでした。測地系変換の知識が無いのでノータッチですがPure Go実装にできると嬉しいですね。
日本測地系 / 世界測地系 とは
今泉さんの 郵便番号・住所・緯度経度の体系について に記載がありますので参考ください。東京付近では日本測地系と世界測地系との誤差が450m程度あるらしく、大抵のユースケースではコードを統一した方が良さそうです。
他にも以下のサイトが参考になります。
日本測地系 / 世界測地系 の変換について
日本測地系への対応 - プログラマー’sペイジ さんによると、概ね3つの変換パターンがあるようです。
- 1次式による近似
- ググるとすぐ出てくる↓の式
lonJ = lonW + latW * 0.000046047 + lonW * 0.000083049 – 0.010041
latJ = latW + latW * 0.00010696 – lonW * 0.000017467 – 0.0046020
- シンプルで素晴らしい。約2.5~2.8メートルズレる
- ググるとすぐ出てくる↓の式
- 3次元直交座標系に変換して平行移動
- PROJ が利用している方式
- 3に比べて、約0.2~0.4メートルズレる
- 地域ごとの変換パラメータで変換する
- パラメータファイルを用いた最も精度が高い方式
- Web版 TKY2JGD といったツールがある
今回は1は許容できないけど、2のレベルであれば問題ないユースケースだったので、PROJを用いることにしました。
※ズレの計算は、3のWeb版TKY2JGDで 日本測地系→世界測地系に変換したものを正とし、1,2の変換後の緯度経度を、測量計算(距離と方位角の計算) のWebツールで計測した。場所は五反田駅など関東の複数地点で試しました
この変換を覚えると嬉しいの?
日本測地系は、旧日本測地系(Tokyo Datum)と呼ばれるだけあり、明治時代に作られ、2002年4月1日まで利用されていた規格です。それ以降は日本測地系2000(JGD2000)あるいは、日本測地系2011(JGD2011)が使われています。JGD2000とJGD2011のどちらも世界測地系と言える経度・緯度の体系です。GD2011へ移行したきっかけは、東日本大震災による大規模な地殻変動の発生への対応だそうで、基本的にJGD2000と、JGD2011はTokyo Datumに比べると、スマホアプリのマップにピンを立ててナビゲートするようなケースでは同一と見なして良いレベルです。具体的には日本近辺で5cm程度の差があるとのことです。
何が言いたいかというと、2002年以降に公開されたデータは大概、世界測地系であるJGD2000またはJGD2011です。そのため、日本測地系→世界測地系にしたいユースケースってそんなに多くないと思います(古いデータを移行したいとか、その古いデータを元にGoogle Mapsなどに表示させたいなど?)。この記事に書かれたサンプルコードは、古い資産を活用したい場合など、限定された場面で参考になるんだと認識してもらえればです。
例えば、国土交通省の街区レベル位置参照情報はJGD2000形式です。同じく国土交通省の国有林野データはJGD2011でした。少なくても公共機関から取得できるようなデータは世界測地系になっているのかなと思います。
PROJとは
PROJ はこの界隈では有名な、地図投影や測地変換が行えるツールで、コマンドラインから呼び出しと、ライブラリのようにも使えるAPIも提供しています。
コマンドラインから日本測地系から世界測地系に変換する例です。
# 日本測地系(EPSG 4301)から世界測地系(EPSG 4612)に変換 |
参考: http://pen.envr.tsukuba.ac.jp/~torarimon/?%C2%AC%C3%CF%B7%CF%3A+datum
各言語からPROJを用いる
Java(proj4j), JavaScript(proj4js), Python(pyproj)など様々な言語によるポーティングが公開されています。
Goの場合は冒頭でも紹介したeverystreet/go-projがそれにあたります。
PROJはC++で実装されており、cgo経由で扱うため、PROJ環境を構築した後に、go-projから呼び出す必要があります。
go-projの利用開始
WindowsとWSL2での構築手順をまとめます。Macは手元に無かったので割愛します。
全体の注意ですが、2022.11.20時点でPROJの最新バージョンは 9.1.0
ですが、go-projは 8.1.0
に対応しています。8.1系だと最新が 8.1.1
が存在するので、この記事ではそのバージョンを利用します。
PROJのインストールは公式ドキュメント に細かく手順が載っていますが、conda
経由で行います(WSL2はapt-getも追記しておきます)。
各環境からは次のようなgo-projを用いたコードが動作することを確かめます。
package main |
Windows環境構築手順
conda
は miniconda
を利用します。環境がない方は以下からインストールください。
もし、プロキシ環境で構築する場合は %USERPROFILE%
直下に .condarc
ファイルを作成し、プロキシ情報を追記ください。
proxy_servers: |
※ {password} に記号が入っている人で、上記で認証が通らない方は、URLエンコードして設定してみてください
conda環境ができたら、インストールします。
> conda install -c conda-forge proj=8.2.1 proj-data=1.11.0 |
成功すれば以下のようなバージョンが表示されると思います。インストールが成功しているけどproj
コマンドが動かない人は、 %USERPROFILE%\Miniconda3\Library\bin
をPATHに追加してください。
>proj |
次に環境変数を設定し、go get でパッケージをインストールします。
# 環境変数 |
これで動くと思います。
>go run main.go |
もし、以下のようなエラーが出てのであれば、Cのヘッダーファイルが見つかっていないということですので、 CGO_CFLAGS
で指定している、includeフォルダのパスを確認し再設定ください。
# github.com/everystreet/go-proj/v8/cproj |
あるいは、以下のエラーの場合は、 CGO_LDFLAGS
で設定したライブラリのパスを確認し再設定ください。
~\go\go1.19.1\pkg\tool\windows_amd64\link.exe: running gcc failed: exit status 1 |
WSL2インストール手順
condaを使う場合
ほぼWindows側と同じです。miniconda3 をインストールし、PROJをインストール&環境変数を設定しておしまいです。
# minicondaインストール(プロキシ設定は割愛) |
これでgo-projが実行できるようになると思います。
$ go run main.go |
apt を使う場合
conda
を使いたくない方向けに手順を残しておきます。まず、公式のパッケージリポジトリでインストールできるprojが6系と古いため、3rdパーティのパッケージリポジトリを参照する必要があります。以下から利用するリポジトリを選択し、運用していただいていることに感謝します。
私は以下のように、 proj.list
というファイルを作って追加しました。
$ sudo vim /etc/apt/sources.list.d/proj.list |
これでPROJをインストールします。
# アップデート |
これでgo-projが実行できるようになると思います。
$ go run main.go |
apt経由の場合は、libproj-dev
でインストールすると /usr/include
にヘッダファイルなどがインストールされるため、CGO系のフラグは設定しなくても動くと思います。
高速化したい場合
proj.CRSToCRS()
ですが、処理件数が多い処理に利用すると物足りない性能でした(1変換に10msほどかかりました)。理由は、C++側のライブラリに測地系のロードを都度行うためだと思われます。そのため、proj.CRSToCRS()
をそのまま利用するのでなく、その內部で利用している cproj
を直接利用すると良いかもしれません。
例えば以下のような実装です(※ちゃんとメモリリークしないか確かめていないです)。これで1000倍くらい早くなりました。
package main |
Close()
内でじゃかじゃか Destroy()
しているのはC側のオブジェクトはGo側でGCされないためで、自分でリソースを開放する必要があるためです。
まとめ
GoからPROJを利用する方法をまとめました。 構築は cgo を使ったことがある方であればハマることは少ないと思いますが、conda経由だとCGOオプションが必要です。
測地系の変換を行うケースは一般的にはあまり多くないと思いますが、何かの参考になれば幸いです。最後まで読んでいただき、ありがとうございました!