Google Mapsの場所情報が使えるPlaces APIを使ったアプリケーションを作ってみたく、実装ためしたメモ。

Google Maps API

単にGoogle Maps APIといってもその用途によって、ドキュメントや料金体系が別れている。

ドキュメント一覧はここで、目的に合ったAPIを選ぶ。 https://developers.google.com/maps/documentation

料金についてはここ。記事を書いている時点で、モバイル向けマップ表示は無料だったり、ある道路の制限速度を取得するAPIは1000件あたり$20と細かい。1ヶ月$200までは無料で使える。
https://cloud.google.com/maps-platform/pricing/

今回はPlaces API for Webを使ってみる。指定した場所周辺の飲食店一覧や、その飲食店のレビュー情報なんかが取得できる。

Places APIの中でも、場所を探す、場所の詳細情報を取得する、その周辺の写真を取得する、で料金体系が違うので注意。

APIの有効化

ここに書いてあるとおり順にやってく。
https://developers.google.com/places/web-service/get-api-key

手順通り進めたら、API Keyが発行できる。このキーをクエリパラメータに含めるなどして扱う。

発行できたらcurlつかってテスト。

curl "https://maps.googleapis.com/maps/api/place/nearbysearch/json?key={replace your API Key}&location=35.6812362,139.7649361&radius=100&language=ja&keyword=ramen"

上記はNearby Searchを使って、東京駅周辺半径100mで、キーワード"ramen"でお店を探すサンプル。

ドキュメントに書かれているとおり、API Keyには利用元の制限をかけるべき。IPアドレスやリファラなどで設定可能。

実装

Java向け公式のクライアントがあったのでそれ利用。

https://github.com/googlemaps/google-maps-services-java

依存を追加。

// build.gradle.kts
dependencies {
    implementation("com.google.maps:google-maps-services:0.11.0")
}

使い方は次のような感じ。

// https://github.com/googlemaps/google-maps-services-java/blob/master/README.md より
GeoApiContext context = new GeoApiContext.Builder()
    .apiKey("AIza...")
    .build();
GeocodingResult[] results =  GeocodingApi.geocode(context,
    "1600 Amphitheatre Parkway Mountain View, CA 94043").await();
Gson gson = new GsonBuilder().setPrettyPrinting().create();
System.out.println(gson.toJson(results[0].addressComponents));

GeoApiContextにAPI Keyを詰めてインスタンス化、それをGeocodingApi.geocode()といったstatic methodの引数に詰めて実行できる。

Springで実装するとき、このcontextをどう扱えばよいか。やっぱりDIしてしまうのが後々別のクラスでもAPI呼び出したいってとき便利なので、Bean登録する。

@Configuration
class GeoApiConfig {
    @Bean
    fun createGeoApiConfig(@Value("\${google.api.api-key}") apiKey: String): GeoApiContext {
        return GeoApiContext.Builder()
                .apiKey(apiKey)
                .build()
    }
}

application.ymlやコマンドライン引数などでgoogle.api.api-keyを設定しておくこと。

そして実際に使うところはこのような感じ。Kotlinだと@Autowiredを省略できたりする。

@Service
class PlaceApiService(private val context: GeoApiContext) {

    companion object {
        const val langCode = "ja"
    }

    override fun getLocationFromKeyword(keyword: String): PlacesSearchResponse {
        val request = PlacesApi
                .textSearchQuery(context, keyword)
                .language(langCode)
        try {
            return request.await()
        } catch (e: Exception) {
            throw RuntimeException("Some error occured.")
        }
    }
}

勿論エラーハンドリングはもう少し考えたほうなおよし。

まとめ

Spring向けってわけではないJavaライブラリを扱うとき、うまくBean登録してあげるのが肝かなーと思った次第です。