はじめに
この記事はElixirアドベントカレンダー2024のシリーズ2、16日目の記事です
Google Places APIと通信する部分を作ります
Google Places APIとは
テキストや緯度経度から周辺の施設を検索するAPIです
今回はライブラリを使うほどでもないので、GoogleのAPIを直に叩きます
使用するAPIキーは以下を参考にして取得してください
ライブラリの追加
APIと通信を行うHTTP Clientを追加します
defp deps do
[
...
- {:ex_machina, "~> 2.8.0", only: :test}
+ {:ex_machina, "~> 2.8.0", only: :test},
+ {:req, "~> 0.5.0"}
]
end
API通信部分作成
通信部分を実装します
APIエンドポイントやキーをモジュール定数として設定します
このモジュールでは以下の関数を実装します
テキスト検索を行う serach_by_text
緯度経度から周辺の施設を検索する near_search
一覧から詳細を取得する search_by_place_id
スポットの写真(写真はレスポンスにないのでIDを元に別APIを叩く)を取得するget_place_photo
住所から国と郵便番号を除外するsormat_address
基本的にAPIを叩いてレスポンスから必要なデータだけ取り出すだけです
defmodule Trarecord.GoogleApi do
@base_url "https://maps.googleapis.com/maps/api/place/"
@api_key Application.compile_env(:trarecord, :google_api_key)
@key "key=#{@api_key}"
@lang "&language=ja"
def search_by_text(text) do
(@base_url <> "textsearch/json?" <> @key <> "&query=#{text}" <> @lang)
|> URI.encode()
|> Req.post!(headers: [{"Content-Length", 0}])
|> Map.get(:body)
|> Map.get("results")
|> Enum.map(fn result ->
%{
name: result["name"],
address: result["formatted_address"] |> format_address(),
lat: result["geometry"]["lat"],
lng: result["geometry"]["lng"],
place_id: result["place_id"],
icon: result["icon"]
}
end)
end
def near_search(lat, lng) do
(@base_url <>
"nearbysearch/json?" <>
@key <>
"&location=#{lat},#{lng}" <>
"&radius=500" <>
@lang)
|> URI.encode()
|> Req.post!(headers: [{"Content-Length", 0}])
|> Map.get(:body)
|> Map.get("results")
|> Enum.map(fn result ->
%{
name: result["name"],
address: result["vicinity"],
lat: result["geometry"]["lat"],
lng: result["geometry"]["lng"],
place_id: result["place_id"],
icon: result["icon"]
}
end)
end
def search_by_place_id(id) do
endpoint = "details/json?"
ref = "&reference=#{id}"
params = [
"formatted_address",
"geometry",
"icon",
"name",
"formatted_phone_number",
"place_id",
"photos",
"url",
"website"
]
fields = "&fields=#{Enum.join(params, ",")}"
(@base_url <> endpoint <> @key <> ref <> fields <> @lang)
|> URI.encode()
|> Req.post!(headers: [{"Content-Length", 0}])
|> Map.get(:body)
|> Map.get("result")
|> then(&[&1])
|> Enum.map(fn result ->
image =
case result["photos"] do
nil ->
"/images/blank.png"
photos ->
List.first(photos) |> Map.get("photo_reference") |> get_place_photo()
end
%{
name: result["name"],
address: result["formatted_address"] |> format_address(),
lat: result["geometry"]["location"]["lat"],
lng: result["geometry"]["location"]["lng"],
place_id: result["place_id"],
icon: result["icon"],
image: image,
map_url: result["url"],
website: result["website"],
phone: result["formatted_phone_number"]
}
end)
|> List.first()
end
def get_place_photo(photo_ref) do
endpoint = "photo?"
q = "&maxwidth=400&photoreference=#{photo_ref}"
(@base_url <> endpoint <> @key <> q)
|> URI.encode()
|> Req.post!(redirect: false, headers: [{"Content-Length", 0}])
|> Map.get(:headers)
|> Enum.find_value(fn {k, v} -> if k == "location", do: v end)
|> List.first()
end
def format_address(str) do
str
|> String.replace(~r/日本、/, "")
|> String.replace(~r/〒\d{3}-?\d{4}/, "")
end
end
最後に
GooglePlacesAPIをElixirから叩いて必要なデータを取得するモジュールを作成しました
Elixirだとレスポンスを|>で繋いでデータを整形でき、シンプルにできて非常に楽でした
次は実際に使ってデータを保存する箇所を実装していきます
本記事は以上になりますありがとうございました