Azure Maps をAndroidで使ってみる

この記事は[NTTテクノクロスアドベントカレンダー2023](https://qiita.com/advent-calendar/2023/ntt-tx)、シリーズ 2 の25日目の記事です。

こんにちは、NTTテクノクロスの中島進也([なかしょ](https://twitter.com/nakasho_dev))と申します。
シリーズ 2 の1日目の記事では[よりよいペアローテーションを求めて](https://qiita.com/nakasho-dev/items/7acd356a06677177848b)という記事を書きました。
本記事ではAzure MapsをAndroidで利用する方法について自分で学習しながら紹介していきたいと思います。
本記事を書く時点で私のAzure Mapsの利用経験は0です。

## Azure Maps とは？

[Azure Maps](https://azure.microsoft.com/ja-jp/products/azure-maps)はインテリジェントな位置情報対応およびマップ ベースのエクスペリエンスを構築することを可能にする一連の地理空間マッピング サービスです。
詳しくはリンク先から公式ページを見て下さい。

Azure MapsはWebブラウザ、Androidアプリ、iOSアプリに地図を表示するSDKを提供しています。

## まずは地図を表示

公式の[クイックスタート](https://learn.microsoft.com/ja-jp/azure/azure-maps/quick-android-map?pivots=programming-language-kotlin)で、以下の対応について説明しています。

* Azure Maps アカウントを作成する
* アカウントのサブスクリプションキーを取得する
* Android Studioでプロジェクトを作成する
* 仮想デバイスを設定する
* Azure Maps Android SDKをインストールする

上記の対応を実施してアプリを起動すると以下のような画面が出ます。

![002.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146488/bd89004f-a247-7ea3-0f3e-96818c23b576.jpeg)

地図が出ました。表示された地図はどうやら経度0で表示されているようです。
Git等に登録する際はAzure MapsのSubscriptionKeyは環境変数から取得するようにするなど、Gitリポジトリで公開されないように注意してください。

## 日本を表示する

経度0だと英国になってしまうので日本に緯度経度を合わせましょう。
座標は田町の某ビルにし、カメラ設定でどこを表示するのか指定します。

```kotlin
mapControl.onReady { map: AzureMap ->
    map.setCamera(center(Point.fromLngLat(139.746093, 35.643842)))
}
```

![003.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146488/2b9fb85f-1342-a195-dd07-2adb9aa589d2.jpeg)

日本が中心に表示されましたね。

## 拡大する

先ほどのカメラ設定に拡大表示を追加します。

```kotlin
mapControl.onReady { map: AzureMap ->
    map.setCamera(center(Point.fromLngLat(139.746093, 35.643842)), zoom(10.0))
}
```

![004.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146488/7334fb42-da54-7306-a2af-b4762422af06.jpeg)

道路が表示されて地図っぽくなってきましたね。
海にある破線は海路っぽいですね。
ちなみに日本の地図は大きな幹線道路しか対応していないようです。
細かい道案内ができそうもないのは残念ですが、今後に期待です。

## スタイルを変えてみる

Azure MapsはStyleを変えることができます。
サポートされている組み込みStyleは[こちら](https://learn.microsoft.com/ja-jp/azure/azure-maps/supported-map-styles)

今回はSATELLITE_ROAD_LABELSを選択し衛星画像を表示するようにしました。

```kotlin
mapControl.onReady { map: AzureMap ->
    map.setCamera(center(Point.fromLngLat(139.746093, 35.643842)), zoom(10.0))
    map.setStyle(style(MapStyle.SATELLITE_ROAD_LABELS))
}
```
![005.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146488/97ee1234-663f-8fb0-a166-ebc0e22fdb2c.jpeg)


## GeoJsonを読み込んで表示してみる

GeoJsonとは地理空間情報を扱うJSONフォーマットです。
今回はG空間情報センターで無料で配布されている[指定緊急避難場所データ-東京都](https://www.geospatial.jp/ckan/dataset/hinanbasho-13)を使ってみます。

Azure Mapsの[チュートリアル](https://learn.microsoft.com/ja-jp/azure/azure-maps/tutorial-load-geojson-file-android?pivots=programming-language-kotlin)を参考に進めていきます
ダウンロードしてきた避難場所データをhinanbasho.geojsonとリネームしてAssetsに置きます。

余談ですが、「指定緊急避難場所」でWeb検索すると国土地理院のURLのパスはhinanbashoとヘボン式で、内閣府防災情報ではhinanbasyoと日本式でした。

```kotlin
val source = DataSource()
source.importDataFromUrl("asset://hinanbasho.geojson")
map.sources.add(source)

val bubbleLayer = BubbleLayer(
    source,
    bubbleColor("orange"),
    bubbleRadius(5f),
    bubbleStrokeColor("white"),
    bubbleStrokeWidth(2f)
)
map.layers.add(arrayOf<Layer>(bubbleLayer))
```

![006.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146488/11de434f-4b22-1104-8314-7e78125a0e4c.jpeg)

あっさりと避難場所データが可視化できました。
さて、GeoJSONはただ場所を示すものではありません。空間情報を含んでいます。
なので、次はこの避難所のマークをクリックしたら、その場所の空間情報を表示するようにしてみます。

## GeoJsonを空間情報を表示してみる

一つの避難所データは以下のような形式になっています。

```JSon
{
  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [
      142.162035037284,
      26.6349094786039
    ]
  },
  "properties": {
    "指定緊急避難場所": "評議平運動場",
    "所在地": "東京都小笠原村母島字評議平",
    "洪水": "◎",
    "がけ崩れ、土石流及び地滑り": "◎",
    "高潮": "◎",
    "地震": "◎",
    "津波": "◎",
    "大規模な火事": "◎",
    "内水氾濫": "",
    "火山現象": ""
  }
}

```

このpropertiesに含まれるものを表示してみます。
先ほどと同様に、Azure Mapsの[チュートリアル](https://learn.microsoft.com/ja-jp/azure/azure-maps/tutorial-load-geojson-file-android?pivots=programming-language-kotlin)を参考に進めていきます
表示されている避難場所マークがクリックされたら表示するという機能を実装します。

```Kotlin
val popup = Popup()
map.popups.add(popup)

map.events.add(OnFeatureClick { feature: List<Feature> ->
    val f = feature[0]

    val customView: View = LayoutInflater.from(this).inflate(R.layout.popup_text, null)

    val tv = customView.findViewById<TextView>(R.id.message)
    tv.text = String.format(
        "%s\n%s\n" +
                "洪水：%s\n" +
                "がけ崩れ、土石流及び地滑り：%s\n" +
                "高潮：%s\n" +
                "地震：%s\n" +
                "津波：%s\n" +
                "大規模な火事：%s\n" +
                "内水氾濫：%s\n" +
                "火山現象：%s",
        f.getStringProperty("指定緊急避難場所"),
        f.getStringProperty("所在地"),
        f.getStringProperty("洪水"),
        f.getStringProperty("がけ崩れ、土石流及び地滑り"),
        f.getStringProperty("高潮"),
        f.getStringProperty("地震"),
        f.getStringProperty("津波"),
        f.getStringProperty("大規模な火事"),
        f.getStringProperty("内水氾濫"),
        f.getStringProperty("火山現象")
    )

    val pos = MapMath.getPosition(f.geometry() as Point?)

    popup.setOptions(
        position(pos),
        anchor(AnchorType.BOTTOM),
        content(customView)
    )

    popup.open()

    false
}, bubbleLayer)

```

![geojson.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146488/c3b7c1e2-afc1-0c91-c48d-04eabea1d3b5.gif)

表示できました！
ここまでやると地図サービスをカスタマイズした、という感じになってきますよね。


今回の記事はここまでにしたいと思います。


## Azure Maps と Bing Maps

Microsoft にはAzure Mapsが誕生する前から[Bing Maps](https://www.microsoft.com/en-us/maps/bing-maps/choose-your-bing-maps-api)という地図サービスがあります。
こちらもAndroid, iOSのSDKがあります。
今回Bing MapsとAzure Mapsのどちらを試してみるか迷ったときに、新しい機能はAzure Maps側で増えているようなので、Azure Mapsを選択しました。
しかし、日本の地図への対応が幹線道路レベルだと正直何かサービスを作ろう、という気にはなりません。
Bing MapsはZenrinの地図を使用しているので、現時点ではBing Mapsを採用するほうがよさそうな気はします。

## 最後に

今回はAdvent Calendarにかこつけて、触っていないAzureサービスを触る、という経験をしました。
エンジニアとしての技術の幅を広げるために、新しい言語、プラットフォーム、サービス、などなど積極的に触っていきましょう。