位置データのジオコーディング

位置データは通常、地球上のある地点に対応する緯度と経度を表す一組の数値として返されます。このような座標値は、コード内で位置データを正確かつ簡単に指定する手段となりますが、ユーザにとっては直観的ではありません。ユーザには、グローバルな座標ではなく、番地、市区町村、州、県、国のように親しみのある情報を用いて示された位置のほうが理解しやすいでしょう。ユーザに親しみやすい方法で位置情報を表示する必要がある状況では、Geocoderオブジェクトを使用してその情報を取得することができます。

Geocoderオブジェクトについて

Geocoderオブジェクトは、緯度/経度と、ユーザに親しみのあるプレースマーク、すなわち番地、市区町村、州、県、国などの一連のデータとの間で変換を行うネットワークサービスを使用します。逆ジオコーディングとは、緯度と経度をプレースマークに変換する処理です。正ジオコーディングとは、プレース名情報を緯度と経度の値に変換する処理です。逆ジオコーディングはどの版のiOSでも使えますが、正ジオコーディングはiOS 5.0以降でしか使えません。正逆のジオコーディングは、OS X v10.8以降で利用できます。

Geocoderはネットワークサービスに依存するため、ジオコーディング要求が成功するためには有効なネットワーク接続が必要です。デバイスが機内モードにある場合や、ネットワークが現在設定されていない場合、Geocoderは必要なサービスに接続できないので、結果として適切なエラーを返す必要があります。ジオコーディング要求の作成時に適用すべき経験則をいくつか示します。

CLGeocoderでプレースマーク情報を取得

CLGeocoderクラスを使って逆ジオコーディング要求を開始するには、このクラスのインスタンスを作成し、reverseGeocodeLocation:completionHandler:メソッドを呼び出します。ジオコーダオブジェクトは逆ジオコーディング要求を非同期に開始し、結果を提供されたブロックオブジェクトに送ります。ブロックオブジェクトは、要求に対する結果が成功でも失敗でも実行されます。失敗であれば、その理由を示すエラーオブジェクトがブロックに渡されます。

リスト4-1に、地図上のある点を逆ジオコードする方法の例を示します。ジオコーディングに特有のコードは最初の数行だけで、ここでは必要に応じてジオコーダオブジェクトを割り当て、reverseGeocodeLocation:completionHandler:メソッドを呼び出して、逆ジオコーディング処理を起動しています(geocoder変数はジオコーダオブジェクトを格納するメンバ変数)。残りのコードはサンプルアプリケーション自身に特有のものです。この場合、サンプルアプリケーションはプレースマークを、独自の注釈オブジェクト(MapLocationクラスで定義される)とともに格納し、対応する注釈ビューの吹き出しにボタンを追加します。

リスト4-1 CLGeocoderを用いた位置のジオコーディング

@implementation MyGeocoderViewController (CustomGeocodingAdditions)
- (void)geocodeLocation:(CLLocation*)location forAnnotation:(MapLocation*)annotation
{
    if (!geocoder)
        geocoder = [[CLGeocoder alloc] init];
 
    [geocoder reverseGeocodeLocation:location completionHandler:
        ^(NSArray* placemarks, NSError* error){
            if ([placemarks count] > 0)
            {
                annotation.placemark = [placemarks objectAtIndex:0];
 
                // 注釈のビューに「More Info(詳細情報)」ボタンを追加する
                MKPinAnnotationView*  view = (MKPinAnnotationView*)[map viewForAnnotation:annotation];
                if (view && (view.rightCalloutAccessoryView == nil))
                {
                    view.canShowCallout = YES;
                    view.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
                }
            }
    }];
}
@end

サンプルではこのようにブロックオブジェクトを使っているため、(注釈オブジェクトなどの)情報に容易にアクセスし、完了ハンドラの一部として使えるという利点があります。ブロックがなかったとすれば、データ変数を獲得するプロセスは、より複雑になっていたはずです。

プレース名を座標に変換する

CLGeocoderクラスを使い、連絡先情報の辞書、または単なる文字列を指定して、正ジオコーディング要求を行います。文字列ベースの要求に、専用の書式はありません。区切り文字は、あれば分かりやすくなりますが、なくても構いません。ジオコーダサーバは文字列を、大文字と小文字の区別がないものとして扱います。たとえば以下のような文字列で、それに応じた結果が得られます。

正ジオコーダに与える情報が多いほど、よい結果が得られます。ジオコーダオブジェクトは、与えられた情報をパースし、合致が見つかれば、プレースマークオブジェクトをいくつか返します。返されるプレースマークオブジェクトの個数は、与えられた情報の具体性に大きく依存します。したがって、通り、市、県、国の情報が揃っていれば、通りと市の情報しかない場合に比べ、単一の住所を返す可能性が高くなります。ジオコーダに渡す完了ハンドラブロックは、次のように、複数のプレースマークが返された場合にも対処できるようでなければなりません。

[geocoder geocodeAddressString:@"1 Infinite Loop"
     completionHandler:^(NSArray* placemarks, NSError* error){
         for (CLPlacemark* aPlacemark in placemarks)
         {
             // プレースマークを処理する。
         }
}];