はじめに
iOSアプリを作ってみたいけど
何から始めて良いのかわからない
とりあえず、
「やってみました」記事を参考に
地図アプリを真似てみようと思う
という記事の8回目です。
今回は、地名の取得と表示(Reverse Geocoding)までします。
位置情報から地名に変換するサービス
- 今回使うのは Finds.jp
- 【なぜ?】
- 利用する準備が簡単(登録不要で利用可能)
- やっぱり「やってみました」は簡単にやってみることができるのは重要!
- 今回は、日本国内の地名に変換できれば十分だから
- 利用する準備が簡単(登録不要で利用可能)
- 【なぜ?】
- キーワード「reverse geocoding」で検索してみると、いろいろ見つかる
- 気になる人は、いろいろ試してみると良いですよ
簡単な Finds.jp の使い方
- ブラウザで以下のアドレスを表示してみる
-
https://www.finds.jp/ws/rgeocode.php?lat=35.6804725&lon=139.7661837
-
以下のよう内容が得られる(ブラウザに表示される)
- 「東京都」、「千代田区」、「丸の内一丁目」との情報が得られた
-
- JSON の方が良いので、ブラウザで以下のアドレスを表示してみる
-
https://www.finds.jp/ws/rgeocode.php?lat=35.6804725&lon=139.7661837&json
-
以下のよう内容が得られる(ブラウザに表示される)
- 改行も入っていないので読みにくいけど、プログラムで扱うので気にしない
-
{"status":200,"result":{"prefecture":{"pcode":13,"pname":"\u6771\u4eac\u90fd"},"municipality":{"mname":"\u5343\u4ee3\u7530\u533a","mcode":13101},"local":[{"section":"\u4e38\u306e\u5185\u4e00\u4e01\u76ee","homenumber":"9","distance":128.53601336433,"latitude":35.681252,"longitude":139.767235}]},"argument":{"latitude":35.6804725,"longitude":139.7661837,"localradius":500,"localmax":1},"meta":[{"name":"thanks","content":"\u3053\u306e\u30b5\u30fc\u30d3\u30b9\u306f \u56fd\u571f\u4ea4\u901a\u7701 \u63d0\u4f9b \u56fd\u571f\u6570\u5024\u60c5\u5831(\u884c\u653f\u533a\u57df) \u3092\u5229\u7528\u3057\u3066\u3044\u307e\u3059"},{"name":"thanks","content":"\u3053\u306e\u30b5\u30fc\u30d3\u30b9\u306f \u56fd\u571f\u4ea4\u901a\u7701 \u63d0\u4f9b \u8857\u533a\u30ec\u30d9\u30eb\u4f4d\u7f6e\u53c2\u7167\u60c5\u5831\u304a\u3088\u3073\u5927\u5b57\u30fb\u753a\u4e01\u76ee\u30ec\u30d9\u30eb\u4f4d\u7f6e\u53c2\u7167\u60c5\u5831 \u3092\u5229\u7528\u3057\u3066\u3044\u307e\u3059"}]}
```
簡単な Swift からの Finds.jp の使い方(http get)
-
swift では、以下のようにするのが基本形(らしい)
let url = URL(string: "https://あどれす")! let request = URLRequest(url: url) let session = URLSession.shared session.dataTask(with: request) { (data, response, error) in if error == nil, let data = data, let response = response as? HTTPURLResponse { // response.statusCode // String(data: data, encoding: String.Encoding.utf8) ?? "" } }.resume()
位置情報から地名を表示する
-
地名表示領域とボタンを配置
- 地名表示領域として、テキストフィールドを配置する
- 【なぜ?】
- 地名表示にテキストフィールドを使うのは、クリップボードにコピー可能とするため
- ボタンを配置するのは、地名を確認したいときだけ更新するため
- 位置情報が更新されるたびに地名を確認(取得)していると、不必要な通信が多発するから
- ほんの少しの移動で地名を確認(取得)するのは無駄な処理だし、通信データの無駄使いだから
- 今回は、画面の上部に配置した
- 前の記事でも書いたことなので、簡単に書きます
- [Main.storyboard]を表示
- メニュー [View]-[Show Library]を選択
- [Text Field]を [MapView]に配置(ドラッグ&ドロップし大きさを調整)
- ボタンも同様に配置
-
テキストフィールド用の変数(locationName)を追加
- ViewController.swift を以下のように修正
- 【なぜ?】
- 確認(取得)した地名を設定するため
- プログラムからアプリの状態を変化させるには変数を使うから
ViewController.swift@IBOutlet var mapView: MKMapView! var locationManager: CLLocationManager! @IBOutlet weak var locationName: UITextField! // この行を追加
-
ボタンの関連づけ
-
前の記事でも書いたことなので、簡単に書きます
- [Main.storyboard]と[ViewController.swift]を表示
- ボタンを Ctrl を押しながらクリックし[Touch down]を[ViewController.swift]にドラッグ&ドロップ(ドロップ位置は、以下を参照)
ViewController.swift@IBOutlet weak var locationName: UITextField! // この行あたりにドロップする @IBAction func clickLocation(_ sender: Any) { }
-
【なぜ?】
- ボタンをタップしたときだけ、位置情報から地名に変換する処理を実行するため
- 利用者がアプリケーションに対し指示を出すには UI 部品を使うため
-
-
表示している地図の中心位置(緯度と経度)を取得
- ViewController.swift を以下のように修正
ViewController.swift@IBAction func clickLocation(_ sender: Any) { myLock.lock() let latitude = mapView.region.center.latitude let longitude = mapView.region.center.longitude myLock.unlock() }
- 【なぜ?】
- 地図表示(MKMapView)オブジェクトが中心位置を保持しており、その情報を使うため
- GPSで位置情報は更新されるので、排他処理は必須です
- 排他処理は、できるだけ短時間にするため、緯度・経度を取得するだけ
-
逆 GeoCoding サービス URL 文字列作成準備
- ViewController.swift を以下のように修正
- func clickLocation の直前に1行追加
ViewController.swiftlet FMT_url_rev_geo = "https://www.finds.jp/ws/rgeocode.php?lat=%f&lon=%f&json" // この行を追加 @IBAction func clickLocation(_ sender: Any) {
- 【なぜ?】
- printf 書式を定義
- プログラムで、変数の値を文字列に埋め込むなら sprinf (と同じような機能)を使うから
- ViewController.swift を以下のように修正
-
位置情報(緯度・経度)を地名に変換
- ViewController.swift を以下のように修正
- func clickLocation の内容を丸々載せますので置き換えてください
ViewController.swift@IBAction func clickLocation(_ sender: Any) { myLock.lock() // let latitude = mapView.region.center.latitude let longitude = mapView.region.center.longitude myLock.unlock() let url = URL(string: String(format: FMT_url_rev_geo, latitude, longitude))! let request = URLRequest(url: url) let session = URLSession.shared session.dataTask(with: request) { (data, response, error) in if error == nil, let data = data, let response = response as? HTTPURLResponse { print("statusCode: \(response.statusCode)") let jsonString: String = String(data: data, encoding: String.Encoding.utf8) ?? "" var locationData = jsonString.data(using: String.Encoding.utf8)! do { let items = try JSONSerialization.jsonObject(with: locationData) as! Dictionary<String, Any> let result = items["result"] as! Dictionary<String, Any> let prefecture = result["prefecture"] as! Dictionary<String, Any> let municipality = result["municipality"] as! Dictionary<String, Any> let local = result["local"] as! Array<Any> let local0 = local[0] as! Dictionary<String, Any> self.locationName.text = prefecture["pname"] as! String self.locationName.text! += municipality["mname"] as! String self.locationName.text! += local0["section"] as! String } catch { print(error) } } }.resume() }
- 【なぜ?】
- エラー処理は、ほぼ省略しました(汗)
- 取得した位置情報(緯度・経度)をもとに URL 文字列を構成
- 位置情報を埋め込んだ文字列が作成される
- ブラウザに入力すれば Finds.jp を使って、位置情報から地名への変換結果が表示されるはず
- Finds.jp の変換結果(JSON形式)をSwiftのデータ型(Dictionary)に変換
- Dictionary からデータを取り出す
- ["result"]-["prefecture"]-["pname"] に都道府県
- ["result"]-["municipality"]-["mname"] に市区町村
- ["result"]-["local"] は配列
- 最初の要素の ["section"] に町丁目や字など
- 2番目以降の要素は、今回使用しない
- [都道府県]+[市区町村]+[町丁目や字など]をテキストフィールドに設定
- ViewController.swift を以下のように修正
-
テスト実行
- Simulatorを起動
- Simulator メニューから [Debug]-[Location]-[Custom Location...]を選択し、東京の中心を設定
- 緯度(Latitude)に35.6804725
- 経度に(Longitude)に139.7661837
- Simulator メニューから [Debug]-[Location]-[Custom Location...]を選択し、東京の中心を設定
- 地図を拡大表示し、地名表示のボタンを押すと、地名が表示される
- 【注意】地図の中心は、日本国内で、人が住んでいる場所を選ばないと、異常終了する
- 【なぜ?】
- 国外とか海とかは、位置情報に対応する地名が存在しない(取得できない)ため
- エラー処理を省いたため
- 【なぜ?】
- Simulatorを起動
今回の到達点
- 日本国内の地図を表示し、ボタンを押すことで地名が表示されるようになった
連載
- [はじめてのiOSアプリ]xcodeで地図アプリを作成(その1:プロジェクト作成)
- [はじめてのiOSアプリ]xcodeで地図アプリを作成(その2:地図表示)
- [はじめてのiOSアプリ]xcodeで地図アプリを作成(その3:位置情報取得)
- [はじめてのiOSアプリ]xcodeで地図アプリを作成(その4:位置情報と連携した地図表示)
- [はじめてのiOSアプリ]xcodeで地図アプリを作成(その5:アプリアイコン設定)
- [はじめてのiOSアプリ]xcodeで地図アプリを作成(その6:拡大・縮小ボタン追加)
- [はじめてのiOSアプリ]xcodeで地図アプリを作成(その7:地図を拡大・縮小)
- [はじめてのiOSアプリ]xcodeで地図アプリを作成(その8:地名表示)
- [はじめてのiOSアプリ]xcodeで地図アプリを作成(その9:ソースコード管理)