Edited at

iOS(Objective-C)で日本測地系と世界測地系の変換

More than 5 years have passed since last update.

日本測地系(旧日本測地系、TokyoDatum)世界測地系(WGS84)との相互変換をObjective-C上で行う方法です。その他の空間参照系の変換も可能です。


PROJ.4を利用する

業務レベルの測量目的のアプリを作成する場合など、座標変換に極めて高い精度が要求される場合は、国土地理院が提供している日本測地系と法令上の世界測地系(JGD2000)とを変換するソフトウェア、TKY2JGDなどを使用しなければなりません。

日常レベル(最大十数メートル程度の誤差が許容される)のであれば、測地系と地図投影法の相互変換が行えるオープンソースライブラリ、PROJ.4を用いることで、測地系の変換を実現することができます。

このライブラリはC言語で記述されているため、上位互換言語であるObjective-Cでそのまま動かすことができます。


簡単な導入方法

しかし自分がそうなのですが、Objective-Cから入ったために、C言語ライブラリをどう組み込めばいいのか分からないといったことがあると思います。

そこでiOS向けオープンソースマップライブラリ、route-meを利用することで、C言語に関する知識がなくても簡単に導入することができます。

route-meについては、地図SDKは MapKit だけじゃない!OSMやBingが使える iOS用 Map SDK、route-me のセットアップ方法の記事などが参考になります。ただしここではroute-meの地図表示機能は全く使いませんので、route-meの詳細について理解する必要はありません

ではなぜroute-meが必要なのかと言いますと、route-meは様々な地図投影法、測地系のマップタイルソースを扱うために、内部でPROJ.4を使用しています。

このため、PROJ.4のスタティックライブラリを生成するXCode内部プロジェクトを含んでいるので、これを拝借して自分のプロジェクトへ組み込むのが、もっとも簡単な導入方法になるのです。


設定方法

以下の記述は、How To Use Proj4 In Your iOS Projectを参考にしています。


1.route-meライブラリをダウンロードする

githubのroute-meリポジトリから、最新のroute-meライブラリをダウンロードします。


2.Proj4.xcodeprojをサブプロジェクトとして追加する

ダウンロードしたroute-meライブラリ内の、/Proj4/Proj4.xcodeprojを、測地系変換を行いたいXCodeプロジェクト(以下メインプロジェクト)に追加します。xcodeprojファイルそのものをファイルとして追加するか、左ペインのファイル一覧へドロップします。

このとき、Proj4.xcodeprojを参照するプロジェクト(route-meやProj4自身)を開いていない状態にして下さい。


3.依存ライブラリとして設定する

メインプロジェクトの"Build Phase"より、"Target Dependencies"Proj4を追加します。左下の「+」を押すと最初の候補として出現していると思います。


4.Proj4ライブラリのパスを通す

メインプロジェクトの"Build Settings"より、"Library Search Path""User Header Search Paths"両方に、route-meライブラリのディレクトリ/Proj4/のパスを指定します。このとき、recursiveにチェックを入れて子ディレクトリも再帰的に探索するようにしてください。


5.リンカフラグを設定する

最後に、メインプロジェクトの"Build Settings"より、"Other Linker Flags"-lProj4を設定します。

以上でProj4ライブラリをObjective-Cで使うために必要な準備が整いました。


変換を行う

以下は変換の擬似コードです。(机上のコードなので後ほど検証して間違いがあれば差し替えます。)


TransformSample.m

#import <proj_api.h>

#import <CoreLocation/CoreLocation.h>

- (CLLocationCoordinate2D) transformTKY2WGS:(CLLocationCoordinate2D)location
{
projPJ tokyo = pj_init_plus("+proj=longlat +ellps=bessel +towgs84=-146.336,506.832,680.254,0,0,0,0 +no_defs")
projPJ wgs84 = pj_init_plus("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");

double latitude = location.latitude * DEG_TO_RAD;
double longitude = location.longitude * DEG_TO_RAD;

pj_transform(tokyo, wgs84, 1, 1, &longitude, &latitude, NULL);

pj_free(tokyo);
pj_free(wgs84);

return CLLocationCoordinate2DMake(latitude * RAD_TO_DEG, longitude *RAD_TO_DEG);
}


proj4のAPI群を使うためには、proj_api.hをインポートする必要があります。ここでは座標値をCLLocationCoordinate2D構造体で保持して、CLLocationCoordinate2DMake()関数も使っていますので、CoreLocationフレームワークも必要になります。

変換の流れは次の通りです。



  1. pj_init_plus()関数に測地系の定義を渡して、proPJ構造体を生成する。


  2. pj_transform()関数に1.で作成したproPJ構造体および、緯度経度値を示すdouble型変数へのポインタを渡す。成功すると、緯度経度の値が書き換えられる。


  3. pj_free()関数を使って、proPJ構造体を解放する。

注意すべき点として、pj_init_plus()関数にはEPSGコードを渡すこともできますが、日本測地系のEPSG:4326をそのまま渡した場合、spatialreference.orgの定義文に並行移動パラメータが含まれていないので、大きなズレが生じてしまいます。

その理由については、分かりません。非常に初心者殺しなので注意してください。

また、pj_init_plus()に渡す座標値は、弧度法で渡す必要があります。度数法から変換する定数DEG_TO_RAD、および弧度法へ変換する定数RAD_TO_DEGは事前に定義されています。


付則事項

詳しいProj4のAPIの使い方については、API Referenceを参照して下さい。

日本測地系と世界測地系の違いや、渡している変換パラメータの意味、測地系変換の原理など、測地系変換について深い理解を得たい場合、飛田幹男氏著「世界測地系と座標変換」 が教科書として最適です。

測地系変換が要求される場合、JavaScriptのマップライブラリを使用するケースも多いと思います。変換処理をネイティブアプリ内で実行したい場合、上記の方法が有効です。もしUIWebView内で処理を完結させたい場合は、ProJ4のJavaScript用姉妹ライブラリProJ4jsを使うこともできます。こちらの導入方法は簡単なので割愛します。