2012年5月29日火曜日

CoreLocationサンプル3 - ジオコーディング

前回は「CoreLocationサンプル2 - ヘディングイベント」でした。今回は、「緯度・経度から住所」「住所から緯度・経度」に変換するジオコーディングについてです。

iOS Developer Libraryの「位置情報対応プログラミングガイド」(英語版はMaking Your Application Location-Aware)に沿って、サンプルを公開します。

注意点

ジオコーディングは、サーバにリクエストを送り、サーバ側で処理をするものです。そのため、過度にジオコーディング要求をしてはいけません。詳細は「位置情報対応プログラミングガイド」23ページ目「Geocoderオブジェクトについて」をご覧ください。
また、MapKitを利用した逆ジオコーディングは、Googleマップの利用規約により、必ずGoogleマップと組み合わせなければなりません。他の会社の地図サービスなどに出力したりすることはできません。(Googleマップの利用規約(section 10.12)
詳細は、Googleマップの利用規約(Google Maps Terms of Service)(全文)をご覧ください。


正ジオコーディングと逆ジオコーディング

逆ジオコーディングはiOS 3.0以降ですが、正ジオコーディングはiOS 5.0以降でなければ使えません。
概要フレームワーク
正ジオコーディング「住所」→「緯度・経度」CoreLocation【iOS 5.0以降】
逆ジオコーディング「緯度・経度」→「住所」MapKit【iOS 3.0以降】【Deprecated in iOS 5.0
CoreLocation【iOS 5.0以降】
iOS 4.3以前にも対応させる必要があるならば、MapKitを使うことになり、正ジオコーディングは使えません。

実装手順

正ジオコーディング(CoreLocation)

  1. 「CoreLocation.framework」の追加
  2. ヘッダのインポート
    #import <CoreLocation/CoreLocation.h>
  3. 正・逆ジオコーディングの開始
    // インスタンスの生成
    CLGeocoder *geocoder = [[[CLGeocoder alloc] init] autorelease];
    
    // 正ジオコーディングの開始
    [geocoder geocodeAddressString:@"住所".text completionHandler:^(NSArray *placemarks, NSError *error) {
        if (error) {
            // エラーが発生している
        } else {
            for (CLPlacemark *p in placemarks) {
                // 複数の結果が存在する場合もある
            }
        }
    }];
    
    // 逆ジオコーディングの開始
    CLLocation *location = [[[CLLocation alloc] initWithLatitude:37.332708 longitude:-122.030336] autorelease];
    [geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
        if (error) {
            // エラーが発生している
        } else {
            if (0 < [placemarks count]) {
                // 結果はひとつしかない
            }
        }
    }];

MapKit.framework

  1. 「MapKit.framework」の追加
  2. ヘッダのインポート
    #import <MapKit/MapKit.h>
  3. MKReverseGeocoderDelegateプロトコルの宣言
    @interface ViewController : UIViewController <MKReverseGeocoderDelegate>
    @property (nonatomic, retain) MKReverseGeocoder *geocoder;
    @end
  4. 逆ジオコーディングの開始
    @synthesize geocoder = _geocoder;
    
    - (void)reverseGeocode:(CLLocationCoordinate2D)coord {
        self.geocoder = [[[MKReverseGeocoder alloc] initWithCoordinate:coord] autorelease];
        _geocoder.delegate = self;
        [_geocoder start];
    }
    ガイドラインに書かれている以下のような方法だと、MKReverseGeocoderオブジェクトが解放されずにiOS 5.0以降ではメモリリークします。(Xcode付属のサンプル「CurrentAddress」では正しく書かれています。)
    - (void)reverseGeocode:(CLLocationCoordinate2D)coord {
        // theGeocoderは解放されない
        MKReverseGeocoder *theGeocoder = [[MKReverseGeocoder alloc] initWithCoordinate:coord];
        theGeocoder.delegate = self;
        [theGeocoder start];
    }
  5. 逆ジオコーディングの受け取り
    /* エラー発生時 */
    - (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error
    {
        // ここで任意の処理
        NSLog(@"%s | %@", __PRETTY_FUNCTION__, error);
    }
    
    /* ジオコーディング成功時 */
    - (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark
    {
        // ここで任意の処理
    }
    

ジオコーディングの実行例

デバイスの言語の設定により、得られる情報が異なります。また、正ジオコーディング結果の緯度・経度も、regionも値が返ってくるわけでもないようです。

CoreLocationでの正ジオコーディングの例

住所緯度, 経度
東京タワー+35.658608, +139.745396
Apple Inc. 2 Infinite Loop+37.332708, -122.030336


MapKitでの逆ジオコーディング「東京タワー」の例(iOS 5.1)

プロパティ日本語環境英語環境
addressDictionary{
    City = "港区";
    Country = "日本";
    CountryCode = JP;
    FormattedAddressLines = (
        "日本",
        "東京都港区4丁目2−8"
    );
    State = "東京都";
    Street = "4丁目 2−8";
    SubLocality = "芝公園";
    SubThoroughfare = "2−8";
    Thoroughfare = "4丁目";
}
{
    City = Minato;
    Country = Japan;
    CountryCode = JP;
    FormattedAddressLines = (
        Japan,
        "Tokyo, Minato, Shibakoen, 4丁目2−8"
    );
    State = Tokyo;
    Street = "4丁目 2−8";
    SubLocality = Shibakoen;
    SubThoroughfare = "2−8";
    Thoroughfare = "4丁目";
}
administrativeArea東京都Tokyo
areasOfInterest(null)(null)
country日本Japan
inlandWater(null)(null)
locality港区Minato
name(null)(null)
ocean(null)(null)
postalCode(null)(null)
region(null)(null)
subAdministrativeArea(null)(null)
subLocality芝公園Shibakoen
subThoroughfare2−82−8
thoroughfare4丁目4丁目

MapKitでの逆ジオコーディング「Apple Inc.」の例(iOS 5.1)

プロパティ日本語環境英語環境
addressDictionary{
    City = "クパチーノ";
    Country = "アメリカ合衆国";
    CountryCode = US;
    FormattedAddressLines = (
        "2 インフィニート・ループ",
        "クパチーノ カリフォルニア 95014",
        "アメリカ合衆国"
    );
    State = "カリフォルニア";
    Street = "2 インフィニート・ループ";
    SubThoroughfare = 2;
    Thoroughfare = "インフィニート・ループ";
    ZIP = 95014;
}
{
    City = Cupertino;
    Country = "United States";
    CountryCode = US;
    FormattedAddressLines = (
        "2 Infinite Loop",
        "Cupertino, CA 95014",
        USA
    );
    State = California;
    Street = "2 Infinite Loop";
    SubThoroughfare = 2;
    Thoroughfare = "Infinite Loop";
    ZIP = 95014;
}
administrativeAreaカリフォルニアCalifornia
areasOfInterest(null)(null)
countryアメリカ合衆国United States
inlandWater(null)(null)
localityクパチーノCupertino
name(null)(null)
ocean(null)(null)
postalCode9501495014
region(null)(null)
subAdministrativeArea(null)(null)
subLocality(null)(null)
subThoroughfare22
thoroughfareインフィニート・ループInfinite Loop

CoreLocationでの逆ジオコーディング「東京タワー」の例(iOS 5.1)

プロパティ日本語環境英語環境
addressDictionary{
    City = "東京";
    Country = "日本";
    CountryCode = JP;
    FormattedAddressLines = (
        "東京タワー",
        "〒105-0011",
        "東京 港区",
        "芝公園 4丁目2番",
        "日本"
    );
    Name = "東京タワー";
    State = "東京";
    Street = "芝公園 4丁目2番";
    SubLocality = "芝公園";
    SubThoroughfare = "2番";
    Thoroughfare = "芝公園 4丁目2番";
    ZIP = "〒105-0011";
}
{
    City = Minato;
    Country = Japan;
    CountryCode = JP;
    FormattedAddressLines = (
        "Tokyo Tower",
        "〒105-0011",
        "Tokyo Minato",
        "2, Shibakoen 4-Chōme",
        Japan
    );
    Name = "Tokyo Tower";
    State = Tokyo;
    Street = "2, Shibakoen 4-Chōme";
    SubLocality = Shibakoen;
    SubThoroughfare = 2;
    Thoroughfare = "Shibakoen 4-Chōme";
    ZIP = "〒105-0011";
}
administrativeArea東京Tokyo
areasOfInterest("東京タワー")("Tokyo Tower")
country日本Japan
inlandWater(null)(null)
locality港区Minato
name東京タワーTokyo Tower
ocean(null)(null)
postalCode〒105-0011〒105-0011
region(identifier <+35.65860800,+139.74534650> radius 133.46) <+35.65860800,+139.74534650> radius 133.46m(identifier <+35.65860800,+139.74534650> radius 133.46) <+35.65860800,+139.74534650> radius 133.46m
subAdministrativeArea(null)(null)
subLocality芝公園Shibakoen
subThoroughfare2番2
thoroughfare芝公園 4丁目Shibakoen 4-Chōme

CoreLocationでの逆ジオコーディング「Apple Inc.」の例(iOS 5.1)

プロパティ日本語環境英語環境
addressDictionary{
    City = "クパチーノ";
    Country = "合衆国";
    CountryCode = US;
    FormattedAddressLines = (
        "Apple Inc.",
        "2 Infinite Loop",
        "Cupertino, CA 95014-2083",
        "合衆国"
    );
    Name = "Apple Inc.";
    PostCodeExtension = 2083;
    State = "カリフォルニア";
    Street = "2 Infinite Loop";
    SubAdministrativeArea = "サンタクララ";
    SubLocality = "Bay Area";
    SubThoroughfare = 2;
    Thoroughfare = "Infinite Loop";
    ZIP = 95014;
}
{
    City = Cupertino;
    Country = "United States";
    CountryCode = US;
    FormattedAddressLines = (
        "Apple Inc.",
        "2 Infinite Loop",
        "Cupertino, CA 95014-2083",
        "United States"
    );
    Name = "Apple Inc.";
    PostCodeExtension = 2083;
    State = California;
    Street = "2 Infinite Loop";
    SubAdministrativeArea = "Santa Clara";
    SubLocality = "Bay Area";
    SubThoroughfare = 2;
    Thoroughfare = "Infinite Loop";
    ZIP = 95014;
}
administrativeAreaカリフォルニアCalifornia
areasOfInterest("Apple Inc.")("Apple Inc.")
country合衆国United States
inlandWater(null)(null)
localityクパチーノCupertino
nameApple Inc.Apple Inc.
ocean(null)(null)
postalCode9501495014
region(identifier <+37.33100000,-122.03085000> radius 414.66) <+37.33100000,-122.03085000> radius 414.66m(identifier <+37.33100000,-122.03085000> radius 414.66) <+37.33100000,-122.03085000> radius 414.66m
subAdministrativeAreaサンタクララSanta Clara
subLocalityBay AreaBay Area
subThoroughfare22
thoroughfareInfinite LoopInfinite Loop



サンプル

ソースコードはGitHubにて公開しています。
  1. CoreLocationを利用
    1. MapKitを利用

      CoreLocationを利用したサンプル


      MapKitを利用したサンプル



      今回はサンプルでUITableViewやUIMapViewを利用していますが、残念ながらiOS 5.1ではUIScrollViewが内部でメモリリークしています。
      フォーラムでも報告されており「Developer Forums: iOS 5.1 libsystem_c.dylib leaks memory when UIScrollview scrolling hits a bound」、日本語の説明ですと「iOS 5.1 ではスクロールビューをスクロールするたびに少しずつメモリリークが起こる。 - 24/7 twenty-four seven」にも書かれております。
      そのため、UIScrollViewを継承しているUITableViewやUIMapViewでも同様にメモリリークしてしまいます。

      次回

      次回は、「MapKitサンプル1 - 地図の表示」です。

      0 コメント:

      コメントを投稿