3種類の座標系
- 緯度経度
- CLLocationCoordinate2D
- 緯度経度で表される座標系、原点はアフリカ大陸西方沖
- マップ座標系
- MK〜
- メルカトル地図の座標系、原点は左上
- Core Graphics 座標系
- CG〜
- UIView における座標系、原点は左上
緯度経度
- CLLocationCoordinate2D
- 緯度経度値をもつ構造体. WGS84(米国で採用されている世界測地系)で定義される座標系
- latitude: 緯度. CLLocationDegrees (double) 型
- longitude: 経度. CLLocationDegrees (double) 型
- __『無効な値』__を表す場合、lat/lon それぞれが NaN となる模様. isnan() 関数で判定できる
- 緯度経度なので、本初子午線と赤道を境にマイナス値を取る場合もあり得る
- 日本はだいたい、{35, 135} 前後くらいになる
- {0, 0} はアフリカ大陸西方沖、赤道と子午線が交差する点
- 拡大すると妙な島がぽつんと浮かんでいる。おそらく聖地(Google Maps では存在しない?)
マップ座標系
他と区別するためにマップ座標系と勝手に呼称する。
- MKMapRect
- MKMapPoint
- MKMapSize
MapView のあの見た目はメルカトル図法になる。原点は左上、北米大陸の左上の日付変更線上が {0, 0} になる。この場合の地図は西洋を中心に描かれた世界地図である。
MKMapPoint は普通マイナス値にはならないが、何かしらのエラーの際には {-1, -1} となり得る(後述)
とりあえず Apple の資料と MKGeometry.h は眺めるべし
- ADC - 位置情報対応プログラミングガイド.pdf
- MapKit.framework / MKGeometry.h
座標系変換・衝突判定
MKMapRect を NSString に変換
文字列としてログ出力
NSLog(MKStringFromMapRect(mapRect));
MKMapView の表示範囲を MKMapRect で取得
地図の表示範囲を取得
MKMapRect visibleRect = mapView.visibleMapRect;
MKMapPoint から CLLocationCoordinate2D に変換
メルカトル図法の点から緯度経度に変換
MKMapPoint p = MKMapPointMake(100.0, 200.0);
CLLocationCoordinate2D coordinate = MKCoordinateForMapPoint;
CLLocationCoordinate2D から MKMapPoint に変換
緯度経度からメルカトル図法の点に変換
CLLocationCoordinate2D coordinate = CLLocationCoordinate2D(35.0, 135.0);
MKMapPoint p = MKMapPointForCoordinate(coordinate);
注意
例えば以下のようにあり得ない緯度経度を指定すると、MKMapPoint には {-1, -1} が返ってくる。この座標は画面上には存在しない。
こんな場所はありません
CLLocationCoordinate2D coordinate = CLLocationCoordinate2D(999999, 999999);
MKMapPoint p = MKMapPointForCoordinate(coordinate);
MKMapPoint と MKMapRect の衝突判定
マップ座標がマップ矩形の内側にあるかを判定する方法。
点が画面内にあるか
MKMapRect mapRect = mapView.visibleMapRect; // 表示している画面矩形
MKMapPoint point = MKMapPointMake(100, 200.0); // メルカトル図法上での点
// 点が画面内にあれば YES が返る
BOOL isInMapRect = MKMapRectContainsPoint(mapRect, point);
CLLocationCoordinate2D と MKCircle の衝突判定
緯度経度と MKCircle との衝突判定を行う方法。ユーザーの座標が半径〜mの円の内側にあるかを判定する。
ユーザーがヨドバシカメラ梅田の近所にいるかを判定
CLLocationCoordinate2D yodobashiUmedaPoint = CLLocationCoordinate2DMake(34.704172, 135.496324); // 中心緯度経度
CLLocationDistance yodobashiUmedaRadius = 100.0; // 半径100m
MKCircle *yodobashiUmedaArea = [MKCircle circleWithCenterCoordinate:yodobashiUmedaPoint radius: yodobashiUmedaRadius];
// ユーザーの座標
CLLocationCoordinate2D userCoordinate = mapView.userLocation.coordinate;
MKMapPoint userLocationMapPoint = MKMapPointForCoordinate(userCoordinate);
// パスをオフスクリーン描画
MKCircleRenderer *yodobashiUmedaRenderer = [[MKCircleRenderer alloc] initWithCircle: yodobashiUmedaArea];
[yodobashiUmedaRenderer createPath];
// ユーザーの座標を CGPoint に変換
CGPoint userLocationPoint = [yodobashiUmedaRenderer pointForMapPoint:userLocationMapPoint];
// ユーザーの座標がパスに含まれるかを判定
if (CGPathContainsPoint(yodobashiUmedaRenderer.path, NULL, userLocationPoint, NO)) {
// 近くにいるよ
}
else {
// いないよ
}
2点間の距離をメートル単位で計算
MKMapPoint point1 = MKMapPointForCoordinate(coordinate1);
MKMapPoint point2 = MKMapPointForCoordinate(coordinate2);
CLLocationDistance distance = MKMetersBetweenMapPoints(point1, point2);
CLLocationDistance はメートル単位。