2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[はじめてのiOSアプリ]xcodeで地図アプリを作成(その8)

Last updated at Posted at 2019-12-29

はじめに

iOSアプリを作ってみたいけど
何から始めて良いのかわからない

とりあえず、
「やってみました」記事を参考に
地図アプリを真似てみようと思う

という記事の8回目です。

今回は、地名の取得と表示(Reverse Geocoding)までします。

位置情報から地名に変換するサービス

  • 今回使うのは Finds.jp
    • 【なぜ?】
      • 利用する準備が簡単(登録不要で利用可能)
        • やっぱり「やってみました」は簡単にやってみることができるのは重要!
      • 今回は、日本国内の地名に変換できれば十分だから
  • キーワード「reverse geocoding」で検索してみると、いろいろ見つかる
    • 気になる人は、いろいろ試してみると良いですよ

簡単な Finds.jp の使い方

200 13 東京都 千代田区 13101
丸の内一丁目
9 128.53601336433 35.681252 139.767235 35.6804725 139.7661837 500 1 ```

{"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()
    

位置情報から地名を表示する

  1. 地名表示領域とボタンを配置

    • 地名表示領域として、テキストフィールドを配置する
    • 【なぜ?】
      • 地名表示にテキストフィールドを使うのは、クリップボードにコピー可能とするため
      • ボタンを配置するのは、地名を確認したいときだけ更新するため
        • 位置情報が更新されるたびに地名を確認(取得)していると、不必要な通信が多発するから
        • ほんの少しの移動で地名を確認(取得)するのは無駄な処理だし、通信データの無駄使いだから
      • 今回は、画面の上部に配置した
    • 前の記事でも書いたことなので、簡単に書きます
      1. [Main.storyboard]を表示
      2. メニュー [View]-[Show Library]を選択
      3. [Text Field]を [MapView]に配置(ドラッグ&ドロップし大きさを調整)
      4. ボタンも同様に配置
  2. テキストフィールド用の変数(locationName)を追加

    • ViewController.swift を以下のように修正
    • 【なぜ?】
      • 確認(取得)した地名を設定するため
      • プログラムからアプリの状態を変化させるには変数を使うから
    ViewController.swift
    @IBOutlet var mapView: MKMapView!
    var locationManager: CLLocationManager!
    @IBOutlet weak var locationName: UITextField! // この行を追加
    
  3. ボタンの関連づけ

    • 前の記事でも書いたことなので、簡単に書きます

      1. [Main.storyboard]と[ViewController.swift]を表示
      2. ボタンを Ctrl を押しながらクリックし[Touch down]を[ViewController.swift]にドラッグ&ドロップ(ドロップ位置は、以下を参照)
      ViewController.swift
      @IBOutlet weak var locationName: UITextField!
      // この行あたりにドロップする
      @IBAction func clickLocation(_ sender: Any) {
      }
      
    • 【なぜ?】

      • ボタンをタップしたときだけ、位置情報から地名に変換する処理を実行するため
      • 利用者がアプリケーションに対し指示を出すには UI 部品を使うため
  4. 表示している地図の中心位置(緯度と経度)を取得

    • 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で位置情報は更新されるので、排他処理は必須です
      • 排他処理は、できるだけ短時間にするため、緯度・経度を取得するだけ
  5. 逆 GeoCoding サービス URL 文字列作成準備

    • ViewController.swift を以下のように修正
      • func clickLocation の直前に1行追加
    ViewController.swift
    let FMT_url_rev_geo = "https://www.finds.jp/ws/rgeocode.php?lat=%f&lon=%f&json"  // この行を追加
    @IBAction func clickLocation(_ sender: Any) {
    
    • 【なぜ?】
      • printf 書式を定義
      • プログラムで、変数の値を文字列に埋め込むなら sprinf (と同じような機能)を使うから
  6. 位置情報(緯度・経度)を地名に変換

    • 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番目以降の要素は、今回使用しない
      • [都道府県]+[市区町村]+[町丁目や字など]をテキストフィールドに設定
  7. テスト実行

    • Simulatorを起動
      • Simulator メニューから [Debug]-[Location]-[Custom Location...]を選択し、東京の中心を設定
        • 緯度(Latitude)に35.6804725
        • 経度に(Longitude)に139.7661837
    • 地図を拡大表示し、地名表示のボタンを押すと、地名が表示される
    • 【注意】地図の中心は、日本国内で、人が住んでいる場所を選ばないと、異常終了する
      • 【なぜ?】
        • 国外とか海とかは、位置情報に対応する地名が存在しない(取得できない)ため
        • エラー処理を省いたため

今回の到達点

  • 日本国内の地図を表示し、ボタンを押すことで地名が表示されるようになった

連載

  1. [はじめてのiOSアプリ]xcodeで地図アプリを作成(その1:プロジェクト作成)
  2. [はじめてのiOSアプリ]xcodeで地図アプリを作成(その2:地図表示)
  3. [はじめてのiOSアプリ]xcodeで地図アプリを作成(その3:位置情報取得)
  4. [はじめてのiOSアプリ]xcodeで地図アプリを作成(その4:位置情報と連携した地図表示)
  5. [はじめてのiOSアプリ]xcodeで地図アプリを作成(その5:アプリアイコン設定)
  6. [はじめてのiOSアプリ]xcodeで地図アプリを作成(その6:拡大・縮小ボタン追加)
  7. [はじめてのiOSアプリ]xcodeで地図アプリを作成(その7:地図を拡大・縮小)
  8. [はじめてのiOSアプリ]xcodeで地図アプリを作成(その8:地名表示)
  9. [はじめてのiOSアプリ]xcodeで地図アプリを作成(その9:ソースコード管理)
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?