サンプルコードを用意

Swift + iOS で リバースジオコーディングして取得した CLPlacemark の中身がどういうデータになっているか調べるサンプルコードを書いてみた。

今回の環境: Xcode 7.3.1 + iPhone 6 実機 + iOS 9.3.5

Xcode のメニューから [File] → [New] → [Project] → [iOS] → [Application] → [Single View Application] で新しいプロジェクトを作成して、以下のコードを ViewController.swift に貼り付けて、実行すればOK (コピペでお手軽に)。


import UIKit
import MapKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        ViewController.invoke()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    private static func invoke() {
        
        let locations = [
            ["0,0", "0", "0"],
            ["35,135", "35", "135"],
            ["名古屋駅", "35.1707", "136.8826"],
            ["名古屋城", "35.1856", "136.8991"],
            ["木曽川", "35.2564", "136.6913"],
            ["琵琶湖", "35.2676", "136.0937"],
            ["インド洋", "-22.3918", "80.0957"],
            ["スペリオル湖", "47.8247", "-87.3552"],
            ["エッフェル塔", "48.858244", "2.294598"],
        ]

        for location in locations {
            let latlon = CLLocation(latitude: Double(location[1])!, longitude: Double(location[2])!)
            reverseGeocode(location[0], location: latlon, completion: printLocation)
        }
    }
    
    private static func reverseGeocode(name: String, location: CLLocation, completion:(name: String, location: CLLocation, placemarks: [CLPlacemark]?, error: NSError?) -> Void) {
        
        let geocorder = CLGeocoder()
        geocorder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
            completion(name: name, location: location, placemarks: placemarks, error: error)
        })
    }

    private static func printLocation(name: String, location: CLLocation, placemarks: [CLPlacemark]?, error: NSError?) {
        
        print("**********************************************************************")
        print("[\(name)]")
        print("latitude: \(location.coordinate.latitude)")
        print("longitude: \(location.coordinate.longitude)")
        
        if let ps = placemarks {
            print("CLPlacemarks.count: \(ps.count)")
            for p in ps {
                print("CLPlacemark: [")
                // The location object containing latitude and longitude information.
                if p.location != nil {
                    print("  location: \(p.location!)")
                }
                // The name of the placemark.
                if p.name != nil {
                    print("  name: \(p.name!)")
                }
                // A dictionary containing the Address Book keys and values for the placemark.
                if let ad = p.addressDictionary {
                    print("  addressDictionary: [")
                    for k in ad.keys {
                        if let v = ad[k] {
                            print("    \(k): \(v) (\(v.dynamicType))")
                            if let array = v as? NSMutableArray {
                                print("    [")
                                for elem in array {
                                    print("      \(elem)")
                                }
                                print("    ]")
                            }
                        }
                    }
                    print("  ]")
                }
                // The abbreviated country name.
                if p.ISOcountryCode != nil {
                    print("  ISOcountryCode: \(p.ISOcountryCode!)")
                }
                // The name of the country associated with the placemark.
                if p.country != nil {
                    print("  country: \(p.country!)")
                }
                // The postal code associated with the placemark.
                if p.postalCode != nil {
                    print("  postalCode: \(p.postalCode!)")
                }
                // The state or province associated with the placemark.
                if p.administrativeArea != nil {
                    print("  administrativeArea: \(p.administrativeArea!)")
                }
                // Additional administrative area information for the placemark.
                if p.subAdministrativeArea != nil {
                    print("  subAdministrativeArea: \(p.subAdministrativeArea!)")
                }
                // The city associated with the placemark.
                if p.locality != nil {
                    print("  locality: \(p.locality!)")
                }
                // Additional city-level information for the placemark.
                if p.subLocality != nil {
                    print("  subLocality: \(p.subLocality!)")
                }
                // The street address associated with the placemark.
                if p.thoroughfare != nil {
                    print("  thoroughfare: \(p.thoroughfare!)")
                }
                // Additional street-level information for the placemark.
                if p.subThoroughfare != nil {
                    print("  subThoroughfare: \(p.subThoroughfare!)")
                }
                // The geographic region associated with the placemark.
                if p.region != nil {
                    print("  region: \(p.region!)")
                }
                // The time zone associated with the placemark.
                if p.timeZone != nil {
                    print("  timeZone: \(p.timeZone!)")
                }
                // The name of the inland water body associated with the placemark.
                if p.inlandWater != nil {
                    print("  inlandWater: \(p.inlandWater!)")
                }
                // The name of the ocean associated with the placemark.
                if p.ocean != nil {
                    print("  ocean: \(p.ocean!)")
                }
                // The relevant areas of interest associated with the placemark.
                if p.areasOfInterest != nil {
                    print("  areasOfInterest: \(p.areasOfInterest!)")
                }
                print("]")
            }
        }

        if let e = error {
            print("Error: \(e)")
        }
    }
}

ref. CLPlacemark Class Reference

CLPlacemark の中身

サンプルコードで出力したデータをひとつずつ見ていく。

[0,0]

緯度0度、経度0度では、name プロパティが「北大西洋」で、 ocean プロパティも「北大西洋」。


[0,0]
latitude: 0.0
longitude: 0.0
CLPlacemarks.count: 1
CLPlacemark: [
  location: <+30.70548840,-40.57543660> +/- 100.00m (speed -1.00 mps / course -1.00) @ 2016/08/28 12時30分16秒 日本標準時
  name: 北大西洋
  addressDictionary: [
    FormattedAddressLines: (
    "\U5317\U5927\U897f\U6d0b"
) (__NSArrayM)
    [
      北大西洋
    ]
    Name: 北大西洋 (__NSCFString)
    Ocean: 北大西洋 (__NSCFString)
  ]
  region: CLCircularRegion (identifier:'<+31.73555050,-38.20364380> radius 5941202.70', center:<+31.73555050,-38.20364380>, radius:5941202.70m)
  timeZone: GMT (GMT) offset 0
  ocean: 北大西洋
]

[インド洋]

name プロパティが「インド洋」で、 ocean プロパティも「インド洋」。海の情報はシンプル。


**********************************************************************
[インド洋]
latitude: -22.3918
longitude: 80.0957
CLPlacemarks.count: 1
CLPlacemark: [
  location: <-15.91310440,+79.25161300> +/- 100.00m (speed -1.00 mps / course -1.00) @ 2016/08/28 12時30分16秒 日本標準時
  name: インド洋
  addressDictionary: [
    FormattedAddressLines: (
    "\U30a4\U30f3\U30c9\U6d0b"
) (__NSArrayM)
    [
      インド洋
    ]
    Name: インド洋 (__NSCFString)
    Ocean: インド洋 (__NSCFString)
  ]
  region: CLCircularRegion (identifier:'<-20.60060190,+83.44213305> radius 6918767.42', center:<-20.60060190,+83.44213305>, radius:6918767.42m)
  timeZone: GMT+0500 (GMT+5) offset 18000
  ocean: インド洋
]

[35,135]

住所情報が administrativeArea, locality, subLocality, thoroughfare プロパティにセットされている。これを単純につなげてしまうと「兵庫県 西脇市 上比延町 上比延町」と「上比延町」が重複してしまう。データとしてとても扱いづらい。

addressDictionary プロパティが持つ FormattedAddressLines には配列で「兵庫県西脇市」「上比延町」が入っているので、これをつなげたほうが綺麗かもしれない。


**********************************************************************
[35,135]
latitude: 35.0
longitude: 135.0
CLPlacemarks.count: 1
CLPlacemark: [
  location: <+35.00101560,+134.99704500> +/- 100.00m (speed -1.00 mps / course -1.00) @ 2016/08/28 12時30分16秒 日本標準時
  name: 上比延町
  addressDictionary: [
    SubLocality: 上比延町 (__NSCFString)
    Street: 上比延町 (__NSCFString)
    State: 兵庫県 (__NSCFString)
    CountryCode: JP (NSTaggedPointerString)
    Thoroughfare: 上比延町 (__NSCFString)
    Name: 上比延町 (__NSCFString)
    Country: 日本 (__NSCFString)
    FormattedAddressLines: (
    "\U5175\U5eab\U770c\U897f\U8107\U5e02",
    "\U4e0a\U6bd4\U5ef6\U753a"
) (__NSArrayM)
    [
      兵庫県西脇市
      上比延町
    ]
    City: 西脇市 (__NSCFString)
  ]
  ISOcountryCode: JP
  country: 日本
  administrativeArea: 兵庫県
  locality: 西脇市
  subLocality: 上比延町
  thoroughfare: 上比延町
  region: CLCircularRegion (identifier:'<+35.00926235,+135.00460170> radius 1563.14', center:<+35.00926235,+135.00460170>, radius:1563.14m)
  timeZone: Asia/Tokyo (JST) offset 32400
]

[名古屋駅]

住所が入っているプロパティは、administrativeArea: 愛知県, locality: 名古屋市中村区, subLocality: 名駅, thoroughfare: 名駅1丁目, subThoroughfare: 1番4号 と数が多い。

FormattedAddressLines をつなげると「〒450-0002 愛知県名古屋市中村区 名駅1丁目1番4号」と郵便番号も入ってしまうので、住所文字列として使うには郵便番号は削除したい。


**********************************************************************
[名古屋駅]
latitude: 35.1707
longitude: 136.8826
CLPlacemarks.count: 1
CLPlacemark: [
  location: <+35.17059000,+136.88300600> +/- 100.00m (speed -1.00 mps / course -1.00) @ 2016/08/28 12時30分16秒 日本標準時
  name: 名駅1丁目1番4号
  addressDictionary: [
    SubLocality: 名駅 (__NSCFString)
    Street: 名駅1丁目1番4号 (__NSCFString)
    State: 愛知県 (__NSCFString)
    SubThoroughfare: 1番4号 (__NSCFString)
    CountryCode: JP (NSTaggedPointerString)
    ZIP: 450-0002 (NSTaggedPointerString)
    Thoroughfare: 名駅1丁目 (__NSCFString)
    Name: 名駅1丁目1番4号 (__NSCFString)
    Country: 日本 (__NSCFString)
    FormattedAddressLines: (
    "\U3012450-0002",
    "\U611b\U77e5\U770c\U540d\U53e4\U5c4b\U5e02\U4e2d\U6751\U533a",
    "\U540d\U99c51\U4e01\U76ee1\U756a4\U53f7"
) (__NSArrayM)
    [
      〒450-0002
      愛知県名古屋市中村区
      名駅1丁目1番4号

    ]
    City: 名古屋市中村区 (__NSCFString)
  ]
  ISOcountryCode: JP
  country: 日本
  postalCode: 450-0002
  administrativeArea: 愛知県
  locality: 名古屋市中村区
  subLocality: 名駅
  thoroughfare: 名駅1丁目
  subThoroughfare: 1番4号
  region: CLCircularRegion (identifier:'<+35.17059000,+136.88300600> radius 70.87', center:<+35.17059000,+136.88300600>, radius:70.87m)
  timeZone: Asia/Tokyo (JST) offset 32400
]

[スペリオル湖]

アメリカの五大湖のひとつスペリオル湖。inlandWater プロパティに「スペリオル湖」がセットされている。inland water (陸水) は、 陸地に囲まれた水場のことらしい。

FormattedAddressLines を単純につなげると「スペリオル湖 MI アメリカ合衆国」となってしまう。MIはミシガン州の略称。


**********************************************************************
[スペリオル湖]
latitude: 47.8247
longitude: -87.3552
CLPlacemarks.count: 1
CLPlacemark: [
  location: <+47.37232100,-86.99389700> +/- 100.00m (speed -1.00 mps / course -1.00) @ 2016/08/28 12時30分16秒 日本標準時
  name: スペリオル湖
  addressDictionary: [
    SubAdministrativeArea: キウィーナー (__NSCFString)
    State: MI (NSTaggedPointerString)
    CountryCode: US (NSTaggedPointerString)
    Country: アメリカ合衆国 (__NSCFString)
    Name: スペリオル湖 (__NSCFString)
    FormattedAddressLines: (
    "\U30b9\U30da\U30ea\U30aa\U30eb\U6e56",
    MI,
    "\U30a2\U30e1\U30ea\U30ab\U5408\U8846\U56fd"
) (__NSArrayM)
    [
      スペリオル湖
      MI
      アメリカ合衆国
    ]
    InlandWater: スペリオル湖 (__NSCFString)
  ]
  ISOcountryCode: US
  country: アメリカ合衆国
  administrativeArea: MI
  subAdministrativeArea: キウィーナー
  region: CLCircularRegion (identifier:'<+47.37232100,-87.82621875> radius 223107.40', center:<+47.37232100,-87.82621875>, radius:223107.40m)
  timeZone: America/Detroit (GMT-4) offset -14400 (Daylight)
  inlandWater: スペリオル湖
]

[琵琶湖]

日本の河川や湖には inlandWater プロパティはセットされないのかもしれない。残念。


**********************************************************************
[琵琶湖]
latitude: 35.2676
longitude: 136.0937
CLPlacemarks.count: 1
CLPlacemark: [
  location: <+35.46149990,+135.95648940> +/- 100.00m (speed -1.00 mps / course -1.00) @ 2016/08/28 12時30分16秒 日本標準時
  name: 高島市
  addressDictionary: [
    City: 高島市 (__NSCFString)
    CountryCode: JP (NSTaggedPointerString)
    Name: 高島市 (__NSCFString)
    State: 滋賀県 (__NSCFString)
    FormattedAddressLines: (
    "\U6ecb\U8cc0\U770c\U9ad8\U5cf6\U5e02"
) (__NSArrayM)
    [
      滋賀県高島市
    ]
    Country: 日本 (__NSCFString)
  ]
  ISOcountryCode: JP
  country: 日本
  administrativeArea: 滋賀県
  locality: 高島市
  region: CLCircularRegion (identifier:'<+35.38725930,+135.97175850> radius 25441.65', center:<+35.38725930,+135.97175850>, radius:25441.65m)
  timeZone: Asia/Tokyo (JST) offset 32400
]

[木曽川]

残念ながら、川らしい情報は入っていない。


**********************************************************************
[木曽川]
latitude: 35.2564
longitude: 136.6913
CLPlacemarks.count: 1
CLPlacemark: [
  location: <+35.25800180,+136.69000420> +/- 100.00m (speed -1.00 mps / course -1.00) @ 2016/08/28 12時30分16秒 日本標準時
  name: 桑原町前野
  addressDictionary: [
    SubLocality: 桑原町 (__NSCFString)
    Street: 桑原町前野 (__NSCFString)
    State: 岐阜県 (__NSCFString)
    CountryCode: JP (NSTaggedPointerString)
    Thoroughfare: 桑原町前野 (__NSCFString)
    Name: 桑原町前野 (__NSCFString)
    Country: 日本 (__NSCFString)
    FormattedAddressLines: (
    "\U5c90\U961c\U770c\U7fbd\U5cf6\U5e02",
    "\U6851\U539f\U753a\U524d\U91ce"
) (__NSArrayM)
    [
      岐阜県羽島市
      桑原町前野
    ]
    City: 羽島市 (__NSCFString)
  ]
  ISOcountryCode: JP
  country: 日本
  administrativeArea: 岐町前野
    ]
    City: 羽島市 (__NSCFString)
  ]
  ISOcountryCode: JP
  country: 日本
  administrativeArea: 岐\351阜県
  locality: 羽島市
  subLocality: 桑原町
  thoroughfare: 桑原町前野
  region: CLCircularRegion (identifier:'<+35.25605055,+136.68932470> radius 1011.70', center:<+35.25605055,+136.68932470>, radius:1011.70m)
  timeZone: Asia/Tokyo (JST) offset 32400
]

[エッフェル塔]

フランスの首都パリにあるランドマーク。areasOfInterest に「["エッフェル塔", "シャン・ド・マルス公園"]」という文字列の配列型データがセットされている。

FormattedAddressLines も他のプロパティも翻訳が中途半端に見える。住所情報を使うのは難しそうだ。


**********************************************************************
[エッフェル塔]
latitude: 48.858244
longitude: 2.294598
CLPlacemarks.count: 1
CLPlacemark: [
  location: <+48.85855330,+2.29467190> +/- 100.00m (speed -1.00 mps / course -1.00) @ 2016/08/28 12時30分16秒 日本標準時
  name: エッフェル塔
  addressDictionary: [
    SubLocality: Tour Eiffel-Champs de Mars (__NSCFString)
    SubAdministrativeArea: パリ (__NSCFString)
    State: イル=ド=フランス (__NSCFString)
    Street: Avenue Anatole France (__NSCFString)
    CountryCode: FR (NSTaggedPointerString)
    ZIP: 75007 (NSTaggedPointerString)
    Thoroughfare: Avenue Anatole France (__NSCFString)
    Name: エッフェル塔 (__NSCFString)
    Country: フランス (__NSCFString)
    FormattedAddressLines: (
    "\U30a8\U30c3\U30d5\U30a7\U30eb\U5854",
    "Avenue Anatole France",
    "75007 \U30d1\U30ea",
    "\U30d5\U30e9\U30f3\U30b9"
) (__NSArrayM)
    [
      エッフェル塔
      Avenue Anatole France
      75007 パリ
      フランス
    ]
    City: パリ (__NSCFString)
  ]
  ISOcountryCode: FR
  country: フランス
  postalCode: 75007
  administrativeArea: イル=ド=フランス
  subAdministrativeArea: パリ
  locality: パリ
  subLocality: Tour Eiffel-Champs de Mars
  thoroughfare: Avenue Anatole France
  region: CLCircularRegion (identifier:'<+48.85855329,+2.29467190> radius 141.63', center:<+48.85855329,+2.29467190>, radius:141.63m)
  timeZone: Europe/Paris (GMT+2) offset 7200 (Daylight)
  areasOfInterest: ["エッフェル塔", "シャン・ド・マルス公園"]
]

[名古屋城]

ランドマークらしい情報はセットされていない。


**********************************************************************
[名古屋城]
latitude: 35.1856
longitude: 136.8991
CLPlacemarks.count: 1
CLPlacemark: [
  location: <+35.18519580,+136.89884690> +/- 100.00m (speed -1.00 mps / course -1.00) @ 2016/08/28 12時30分16秒 日本標準時
  name: 本丸
  addressDictionary: [
    SubLocality: 本丸 (__NSCFString)
    Street: 本丸 (__NSCFString)
    State: 愛知県 (__NSCFString)
    CountryCode: JP (NSTaggedPointerString)
    Thoroughfare: 本丸 (__NSCFString)
    Name: 本丸 (__NSCFString)
    Country: 日本 (__NSCFString)
    FormattedAddressLines: (
    "\U611b\U77e5\U770c\U540d\U53e4\U5c4b\U5e02\U4e2d\U533a",
    "\U672c\U4e38"
) (__NSArrayM)
    [
      愛知県名古屋市中区
      本丸
    ]
    City: 名古屋市中区 (__NSCFString)
  ]
  ISOcountryCode: JP
  country: 日本
  administrativeArea: 愛知県
  locality: 名古屋市中区
  subLocality: 本丸
  thoroughfare: 本丸
  region: CLCircularRegion (identifier:'<+35.18519595,+136.89888935> radius 386.14', center:<+35.18519595,+136.89888935>, radius:386.14m)
  timeZone: Asia/Tokyo (JST) offset 32400
]

参考資料

CLPlacemark の公式リファレンス。
location - CLPlacemark Class Reference

リバースジオコーディングが返すデータについて、日本語環境と英語環境の比較表が載っている。
UQ Times 開発の記録: CoreLocationサンプル3 - ジオコーディング

tags: swift

Posted by NI-Lab. (@nilab)