GoogleMaps APIの結果をFoliumで可視化してみる
背景
以前からGoogleMaps API自体は気になっていましたが、今回触ってみたので、簡単な使い方をまとめます。
検索してもあまり出てこなかったのですが、GoogleMaps APIはあまり使われていないのでしょうか?無料枠で動かしていますが、料金が高いとか?一つには、ドキュメントがJavaのAPIなどとごっちゃになってて結構読みにくいこともあると思うので、ここでまとめておこうと思います。
アプローチ
以下のリポジトリのサンプルコードをもとに、Foliumによる可視化を追加します。今回は、以下の機能を試しています。
住所テキストから地点データを取得する
緯度・経度から地点データを取得する
ある地点からある地点への経路を検索する
サンプル
Prerequisites
必要に応じて、以下のパッケージをインストールしてください。また、以下からGoogleMaps APIのAPIキーを取得してください。Googleアカウントが必要です。
!pip install googlemaps folium
!pip install python-dotenv
from dotenv import find_dotenv,load_dotenv
load_dotenv(find_dotenv())
以下のようにAPIキーを.envに書いて読み込みます。
GOOGLE_MAPS_API_KEY=*******
必要なパッケージをインポートします。加えて、GoogleMaps APIのAPIキーを渡してクライアントを構成します。
import os,json
from datetime import datetime
import numpy as np
import folium
from branca.element import Figure
import googlemaps
gmaps = googlemaps.Client(key=os.environ['GOOGLE_MAPS_API_KEY'])
Get data from address
まずは、アドレスを入力し、データを取得してみます。
# Geocoding an address
address = '1600 Amphitheatre Parkway, Mountain View, CA'
geocode_result = gmaps.geocode(address)
print(json.dumps(geocode_result,indent=2))
[
{
"address_components": [
{
"long_name": "Google Building 40",
"short_name": "Google Building 40",
"types": [
"premise"
]
},
{
"long_name": "1600",
"short_name": "1600",
"types": [
"street_number"
]
},
{
"long_name": "Amphitheatre Parkway",
"short_name": "Amphitheatre Pkwy",
"types": [
"route"
]
},
{
"long_name": "Mountain View",
"short_name": "Mountain View",
"types": [
"locality",
"political"
]
},
{
"long_name": "Santa Clara County",
"short_name": "Santa Clara County",
"types": [
"administrative_area_level_2",
"political"
]
},
{
"long_name": "California",
"short_name": "CA",
"types": [
"administrative_area_level_1",
"political"
]
},
{
"long_name": "United States",
"short_name": "US",
"types": [
"country",
"political"
]
},
{
"long_name": "94043",
"short_name": "94043",
"types": [
"postal_code"
]
}
],
"formatted_address": "Google Building 40, 1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA",
"geometry": {
"bounds": {
"northeast": {
"lat": 37.4226618,
"lng": -122.0829302
},
"southwest": {
"lat": 37.4220699,
"lng": -122.084958
}
},
"location": {
"lat": 37.4223878,
"lng": -122.0841877
},
"location_type": "ROOFTOP",
"viewport": {
"northeast": {
"lat": 37.42372298029149,
"lng": -122.0825951197085
},
"southwest": {
"lat": 37.4210250197085,
"lng": -122.0852930802915
}
}
},
"place_id": "ChIJj38IfwK6j4ARNcyPDnEGa9g",
"types": [
"premise"
]
}
]
候補として一つの地点のデータが返されています。「address_components」には、住所の各部分の表記や種類(国、自治体、番地など)が格納されていますね。「geometry」には、その住所の範囲やその中心位置が格納されています。location_typeというキーは、ROOFTOPという値が入っています。GoogleMapsでいう建物がこれでしょうか。
シンプルに位置を使う場合は、こちらを使えますね。
print(geocode_result[0]['geometry']['location'])
{'lat': 37.4223878, 'lng': -122.0841877}
次に、Foliumで可視化していきます。
loc = geocode_result[0]['geometry']['location']
map = folium.Map(location=[loc['lat'],loc['lng']], zoom_start=14)
folium.Marker(location=[loc['lat'],loc['lng']], popup=address).add_to(map)
fig = Figure(width=800, height=600)
fig.add_child(map)
これが簡単にできるのはすごいですね。
Get Address from lat and lng
次に、緯度・経度からデータを取得します。
# Look up an address with reverse geocoding
reverse_geocode_result = gmaps.reverse_geocode((40.714224, -73.961452))
print(json.dumps(reverse_geocode_result,indent=2))
[
{
"address_components": [
{
"long_name": "277",
"short_name": "277",
"types": [
"street_number"
]
},
{
"long_name": "Bedford Avenue",
"short_name": "Bedford Ave",
"types": [
"route"
]
},
{
"long_name": "Williamsburg",
"short_name": "Williamsburg",
"types": [
"neighborhood",
"political"
]
},
{
"long_name": "Brooklyn",
"short_name": "Brooklyn",
"types": [
"political",
"sublocality",
"sublocality_level_1"
]
},
{
"long_name": "Kings County",
"short_name": "Kings County",
"types": [
"administrative_area_level_2",
"political"
]
},
{
"long_name": "New York",
"short_name": "NY",
"types": [
"administrative_area_level_1",
"political"
]
},
{
"long_name": "United States",
"short_name": "US",
"types": [
"country",
"political"
]
},
{
"long_name": "11211",
"short_name": "11211",
"types": [
"postal_code"
]
}
],
"formatted_address": "277 Bedford Ave, Brooklyn, NY 11211, USA",
"geometry": {
"location": {
"lat": 40.7142205,
"lng": -73.9612903
},
"location_type": "ROOFTOP",
"viewport": {
"northeast": {
"lat": 40.71556948029149,
"lng": -73.95994131970849
},
"southwest": {
"lat": 40.7128715197085,
"lng": -73.9626392802915
}
}
},
"place_id": "ChIJd8BlQ2BZwokRAFUEcm_qrcA",
"plus_code": {
"compound_code": "P27Q+MF New York, NY, USA",
"global_code": "87G8P27Q+MF"
},
"types": [
"street_address"
]
},
...
]
13個もデータが出てきました。形式は先程と同じです。Foliumで可視化してみます。
locs = [
[res['geometry']['location']['lat'],res['geometry']['location']['lng']]
for res in reverse_geocode_result
]
map = folium.Map(location=list(np.mean(locs,axis=0)), zoom_start=5)
for loc,res in zip(locs,reverse_geocode_result):
# print(loc)
folium.Marker(location=loc, popup=res['formatted_address']).add_to(map)
fig = Figure(width=800, height=600)
fig.add_child(map)
ここでは、結構離れた地点も含めて、13個のピンが表示されています。離れている点を見てみると、「United States」だったり、「New York」だったりしました。「bounds」に検索した緯度・経度を含んでいるデータが出力されていると考えられます。アプリケーションでは、最も近いいくつかを候補として出す、といった使い方になるのでしょうか。
Get directions via public transport
最後に、経路検索を行います。ここでは、"Sydney Town Hall"から"Parramatta, NSW"への経路を検索します。GoogleMapsのAPIでは、建物などの名前でも検索できるんですね。どういう入力ができるんだろうと思い、directionsのソースコードを見ましたが、もちろん緯度・経度でも指定できます。
now = datetime.now()
directions_result = gmaps.directions(
"Sydney Town Hall",
"Parramatta, NSW",
mode="transit",
departure_time=now
)
print(json.dumps(directions_result,indent=2))
[
{
"bounds": {
"northeast": {
"lat": -33.8148186,
"lng": 151.208821
},
"southwest": {
"lat": -33.89778769999999,
"lng": 151.0017629
}
},
"copyrights": "Map data \u00a92023 Google",
"legs": [
{
"arrival_time": {
"text": "12:16\u202fAM",
"time_zone": "Australia/Sydney",
"value": 1690899385
},
"departure_time": {
"text": "11:33\u202fPM",
"time_zone": "Australia/Sydney",
"value": 1690896820
},
"distance": {
"text": "25.1 km",
"value": 25054
},
"duration": {
"text": "43 mins",
"value": 2565
},
"end_address": "Parramatta NSW 2150, Australia",
"end_location": {
"lat": -33.8148186,
"lng": 151.0017629
},
"start_address": "483 George St, Sydney NSW 2000, Australia",
"start_location": {
"lat": -33.8731812,
"lng": 151.2070056
},
"steps": [
{
"distance": {
"text": "88 m",
"value": 88
},
"duration": {
"text": "1 min",
"value": 85
},
"end_location": {
"lat": -33.8735668,
"lng": 151.2069088
},
"html_instructions": "Walk to Town Hall",
"polyline": {
"points": "jzvmEyr{y[@@nADCr@@?Ag@"
},
"start_location": {
"lat": -33.8731812,
"lng": 151.2070056
},
"steps": [
{
"distance": {
"text": "46 m",
"value": 46
},
"duration": {
"text": "1 min",
"value": 33
},
"end_location": {
"lat": -33.8735922,
"lng": 151.2069691
},
"html_instructions": "Head <b>south</b> on <b>George St</b>",
"polyline": {
"points": "jzvmEyr{y[@@nAD"
},
"start_location": {
"lat": -33.8731812,
"lng": 151.2070056
},
"travel_mode": "WALKING"
},
{
"distance": {
"text": "24 m",
"value": 24
},
"duration": {
"text": "1 min",
"value": 16
},
"end_location": {
"lat": -33.8735685,
"lng": 151.2067097
},
"html_instructions": "Turn <b>right</b>",
"maneuver": "turn-right",
"polyline": {
"points": "||vmEqr{y[Cr@"
},
"start_location": {
"lat": -33.8735922,
"lng": 151.2069691
},
"travel_mode": "WALKING"
},
{
"distance": {
"text": "1 m",
"value": 0
},
"duration": {
"text": "1 min",
"value": 3
},
"end_location": {
"lat": -33.8735826,
"lng": 151.2067078
},
"html_instructions": "Take entrance <span class=\"location\">George St</span>",
"polyline": {
"points": "z|vmE}p{y["
},
"start_location": {
"lat": -33.8735826,
"lng": 151.2067078
},
"travel_mode": "WALKING"
},
{
"distance": {
"text": "1 m",
"value": 0
},
"duration": {
"text": "1 min",
"value": 3
},
"end_location": {
"lat": -33.8735826,
"lng": 151.2067078
},
"polyline": {
"points": "z|vmE}p{y["
},
"start_location": {
"lat": -33.8735826,
"lng": 151.2067078
},
"travel_mode": "WALKING"
},
{
"distance": {
"text": "1 m",
"value": 0
},
"duration": {
"text": "1 min",
"value": 2
},
"end_location": {
"lat": -33.8735826,
"lng": 151.2067078
},
"polyline": {
"points": "z|vmE}p{y["
},
"start_location": {
"lat": -33.8735826,
"lng": 151.2067078
},
"travel_mode": "WALKING"
},
{
"distance": {
"text": "18 m",
"value": 18
},
"duration": {
"text": "1 min",
"value": 28
},
"end_location": {
"lat": -33.8735668,
"lng": 151.2069088
},
"polyline": {
"points": "z|vmE}p{y[Ag@"
},
"start_location": {
"lat": -33.8735826,
"lng": 151.2067078
},
"travel_mode": "WALKING"
}
],
"travel_mode": "WALKING"
},
{
"distance": {
"text": "24.4 km",
"value": 24449
},
"duration": {
"text": "34 mins",
"value": 2040
},
"end_location": {
"lat": -33.8175443,
"lng": 151.0054982
},
"html_instructions": "Train towards Penrith Via Central",
"polyline": {
"points": "x|vmEer{y[?Ej@?fCRpCt@\\DN?TCRCZIRGTIf@]b@_@b@e@LQLWLWLULUl@m@t@i@x@c@z@]d@MVI\\I~AQn@CF@hAN@?L@H?H?^Dt@LZDHBB?H@LBZF~BZl@JH@@@TDhARd@JZH`@JB@LBd@NdA^PFPHb@TTLhAp@p@f@`DvEt@d@Z^PR^j@zAvBz@nAf@n@r@zALd@n@dAT^`@l@V\\j@n@PP@@RRNNVRTRZV~B`Bv@j@JHZXJHJHt@p@p@j@t@n@r@n@^^PNJL\\`@X\\JNVZf@l@^l@Zd@BFFJXf@p@nAf@bAP`@Zr@b@hAXr@`@fAXv@Z~@Rp@Pp@Lb@FZJd@RbAFf@Jl@Jn@Ff@Jh@Hj@FZLj@Ld@Pr@Pt@FZ^bBTbAJ`@Lh@Lj@FXNl@BJH\\XlANr@FVPl@Rr@h@pB\\zAV`ALl@Lf@HZLl@R~@FR?BDNBL^|APr@@FZvAJl@NbA\\~C@DFl@F`A@J@VBnA?Z@l@?j@BnA?xA?P?~A?^Av@Cx@G~ACz@Al@?xA?JAXAr@CVCXCTEZKt@Qt@Q|@GRGTK^[`AQf@Sd@IRWh@KTc@x@k@`AYj@OXQ^Sd@KTITKZCFCJ[jAI^ERAFCHENg@pBCHEROd@]nAELa@tAYhAQx@ABCPQdAIh@Ip@CJAJCj@ANAJ?RCf@?JAxA?N@z@?d@B`@?JD~@FrABV@`@NtB@D?DXxC@NNvADf@Db@D|@Bh@A|@AdAGdAKtAMbAc@bCUfAk@bCWvAEXGr@Gp@ALIbAGjA?jABzA@PNbBd@dELrANlBH`B?D?pACjBKjBIfAWdCGt@KdASnBQdAMp@S~@_@vAEPOn@Qt@YpAIb@WrAGVGTc@jBCJc@hBGTI^A@Sx@ABOl@g@rBCLKj@q@|CW`AELa@bB_@rAg@xAOd@ITSl@St@Mf@K`@Ox@QbAMt@QlAKr@K`AGv@Ez@EdBA`AC|@?LA|@Ct@Ap@ATAnAA\\CnACdAAd@Ad@C\\?@Cf@?BEh@Gz@K~@Il@Gd@It@G\\CRId@I^I`@U|@Qx@EPUt@?@Mt@IZGTOn@ABSt@]nA_@rAMh@Oj@Ql@Sj@Mb@Oj@Oj@Ol@Sv@On@K`@On@Ov@Ib@ERADAHCHId@Mn@Kh@On@Mn@Qx@Ov@St@S|@St@YhAQr@Kb@CJIZA?GVGRWx@Sn@M`@KXITABCFKXSf@MXMX_@z@Sd@S`@cAxBc@`AOZEJ{@lBSb@m@tA]p@s@hAa@n@S\\MTOTEJaAbBA@MRGLYh@Ub@S\\k@bAi@v@m@z@eAvAKLcBzBkBdCa@j@u@nAOVCDQVWf@_@v@g@pAWt@Of@W~@Sv@Ml@I`@WnAc@|BWpAQdAWdBWrBGb@OxACXCVCRYnC?B_@rDObBMz@AJOt@Mj@IZUt@AFKXYt@_@z@m@nAEJGJWb@S\\SXUXa@d@]^GF_@`@STUVk@h@w@r@_Ax@q@j@a@^ILQNi@l@c@n@W^GJm@dAs@pAi@~@IJk@z@WZ[\\CD[\\[\\_@`@STIJYZKNKLi@n@e@n@UZ_@h@[d@Ub@OXUd@Yn@Sj@Yx@Ut@Wv@]~@Wn@e@|@k@fA[l@A@OZOVO^EL]v@_@dAUn@EL[x@i@zA[~@?@Sp@IZMh@Ol@Id@S~@ETG\\CJKf@CJEPGPCNOt@EPKf@Ov@Or@S`AI\\?BWjAS`AUjASbAQv@In@CNG\\Gl@Gl@Gp@Cf@Ep@Ab@AZAn@A|@?l@@d@@d@@j@B`@?FBf@?@Dj@JdAJfALlAJxABp@Fz@B|@Dt@@R@R@TBt@B|@?r@?~@At@C|@Ad@Er@Ej@Eh@Gv@Gl@y@lHUfB[|AGb@EXEf@Gd@Gp@g@rESdDGtAGfCE|A?b@CnB?hB@jC@rABdCBlAHxAHhANxALfARtAHXHf@Jr@L~@JnA@T@TBt@@z@Av@CbBEx@EnACfAA~BB|CAfDAtA?~AGrG?DAlADnAAdBAhAKxBC\\Kz@It@Kx@GZGd@Qt@Ol@CLKb@Sl@Up@K\\A@Sl@Wn@O^Wf@OVOVU^QTOTW^W^UX]`@YZUT[XOPWRQNWPMJSLMJMHUNkEnC_Aj@yBrAyH|EiAp@eEhCIFsD~BaHhE[ROJu@d@A?[R}@h@_BbAeAp@SLiAt@g@\\EBqBvAIFgAv@{@t@[X[X[XONCB]\\[ZIH{A~AIJs@r@EFq@p@uAxAEB_A`AYXMHWPGFq@t@e@f@UVUNSPEDQLIHa@ZWRIHEFe@f@ST]\\WXa@`@MLILEFk@h@i@n@u@v@cAfAq@t@w@v@i@f@c@d@_@\\OHIF]\\cAdAyD|DaCjCyAbBe@l@o@~@y@lAoB`DoAhCkAxCiAvB]p@ELYh@ABc@n@KPEVaDbGg@`AOTwB~DaCpEMVMR?@qArB_CpEa@v@[l@y@~AABEFiAtB_@n@cBdDq@|@EHCDY`@ML_@b@ABc@b@e@b@o@d@q@b@SJ_@PIBMDgCfAq@Tq@RmA^aDz@[FUDSDK@O?m@FgBHyBDiDMeDYcE{@mCUsBCS@aABaBFkB`@oAd@a@V]RQLgAr@oA`Aa@\\OLMJa@\\{@`A]d@KRMK"
},
"start_location": {
"lat": -33.8735668,
"lng": 151.2069088
},
"transit_details": {
"arrival_stop": {
"location": {
"lat": -33.8175443,
"lng": 151.0054982
},
"name": "Parramatta"
},
"arrival_time": {
"text": "12:10\u202fAM",
"time_zone": "Australia/Sydney",
"value": 1690899000
},
"departure_stop": {
"location": {
"lat": -33.8735668,
"lng": 151.2069088
},
"name": "Town Hall"
},
"departure_time": {
"text": "11:36\u202fPM",
"time_zone": "Australia/Sydney",
"value": 1690896960
},
"headsign": "Penrith Via Central",
"line": {
"agencies": [
{
"name": "Sydney Trains",
"url": "https://transportnsw.info/"
}
],
"color": "#f2991b",
"name": "North Shore & Western Line",
"short_name": "T1",
"text_color": "#ffffff",
"vehicle": {
"icon": "//maps.gstatic.com/mapfiles/transit/iw2/6/rail2.png",
"local_icon": "//maps.gstatic.com/mapfiles/transit/iw2/6/au-sydney-train.png",
"name": "Train",
"type": "HEAVY_RAIL"
}
},
"num_stops": 7
},
"travel_mode": "TRANSIT"
},
{
"distance": {
"text": "0.5 km",
"value": 517
},
"duration": {
"text": "6 mins",
"value": 385
},
"end_location": {
"lat": -33.8148186,
"lng": 151.0017629
},
"html_instructions": "Walk to Parramatta NSW 2150, Australia",
"polyline": {
"points": "r~kmEkgtx[gBxBECOj@ETCNAHGXGZCLAD?@CLG^CRERALAFAJKFMFSHGBEBGBMDq@JmAPE@EBKHMFIFOBG@E?E?IACNAHIh@Ip@YpB"
},
"start_location": {
"lat": -33.8175443,
"lng": 151.0054982
},
"steps": [
{
"distance": {
"text": "1 m",
"value": 0
},
"duration": {
"text": "1 min",
"value": 58
},
"end_location": {
"lat": -33.8175443,
"lng": 151.0054982
},
"polyline": {
"points": "r~kmEkgtx["
},
"start_location": {
"lat": -33.8175443,
"lng": 151.0054982
},
"travel_mode": "WALKING"
},
{
"distance": {
"text": "1 m",
"value": 0
},
"duration": {
"text": "1 min",
"value": 12
},
"end_location": {
"lat": -33.8175443,
"lng": 151.0054982
},
"polyline": {
"points": "r~kmEkgtx["
},
"start_location": {
"lat": -33.8175443,
"lng": 151.0054982
},
"travel_mode": "WALKING"
},
{
"distance": {
"text": "1 m",
"value": 0
},
"duration": {
"text": "1 min",
"value": 6
},
"end_location": {
"lat": -33.8175443,
"lng": 151.0054982
},
"polyline": {
"points": "r~kmEkgtx["
},
"start_location": {
"lat": -33.8175443,
"lng": 151.0054982
},
"travel_mode": "WALKING"
},
{
"distance": {
"text": "1 m",
"value": 0
},
"duration": {
"text": "1 min",
"value": 8
},
"end_location": {
"lat": -33.8175443,
"lng": 151.0054982
},
"polyline": {
"points": "r~kmEkgtx["
},
"start_location": {
"lat": -33.8175443,
"lng": 151.0054982
},
"travel_mode": "WALKING"
},
{
"distance": {
"text": "80 m",
"value": 80
},
"duration": {
"text": "1 min",
"value": 2
},
"end_location": {
"lat": -33.8170243,
"lng": 151.0048913
},
"html_instructions": "Take exit <span class=\"location\">Darcy St</span>",
"polyline": {
"points": "r~kmEkgtx[gBxB"
},
"start_location": {
"lat": -33.8175443,
"lng": 151.0054982
},
"travel_mode": "WALKING"
},
{
"distance": {
"text": "0.1 km",
"value": 143
},
"duration": {
"text": "2 mins",
"value": 94
},
"end_location": {
"lat": -33.8166036,
"lng": 151.0034729
},
"html_instructions": "Head <b>northwest</b> on <b>Darcy St</b>/<wbr/><b>Parramatta Sq</b> toward <b>Church St</b>",
"polyline": {
"points": "d{kmEuctx[Oj@ETCNAHGXGZCLAD?@CLG^CRERALAFAJ"
},
"start_location": {
"lat": -33.8169924,
"lng": 151.0049083
},
"travel_mode": "WALKING"
},
{
"distance": {
"text": "0.2 km",
"value": 180
},
"duration": {
"text": "2 mins",
"value": 128
},
"end_location": {
"lat": -33.815075,
"lng": 151.0029162
},
"html_instructions": "Turn <b>right</b> onto <b>Church St</b>",
"maneuver": "turn-right",
"polyline": {
"points": "vxkmEuzsx[KFMFSHGBEBGBMDq@JmAPE@EBKHMFIFOBG@E?E?IA"
},
"start_location": {
"lat": -33.8166036,
"lng": 151.0034729
},
"travel_mode": "WALKING"
},
{
"distance": {
"text": "0.1 km",
"value": 114
},
"duration": {
"text": "1 min",
"value": 77
},
"end_location": {
"lat": -33.8148186,
"lng": 151.0017629
},
"html_instructions": "Turn <b>left</b> onto <b>Macquarie St</b>",
"maneuver": "turn-left",
"polyline": {
"points": "fokmEgwsx[CNAHIh@Ip@YpB"
},
"start_location": {
"lat": -33.815075,
"lng": 151.0029162
},
"travel_mode": "WALKING"
}
],
"travel_mode": "WALKING"
}
],
"traffic_speed_entry": [],
"via_waypoint": []
}
],
"overview_polyline": {
"points": "jzvmEyr{y[pAFAr@Am@j@?fCRpCt@\\Dd@Cn@Mh@QjA}@p@w@v@{Al@m@t@i@x@c@z@]|@W\\I~AQn@CF@jAN`AFrG`AnATnB^|@T|Bt@b@Px@b@hAp@p@f@`DvEt@d@l@r@zBbDbB~Br@zALd@dAdBx@jArAvAxAnAvDlCf@b@~BpBzCnCnA|A~@hAz@rAvAjCx@dB~@|BpBrFz@bD^hBRtAh@lDt@`D~BjKv@dD|@lDlB|Hx@pD`BfHZpB^dDRrCDdEBzGAvAKxCEhB?dBK~BIp@]jBm@fCm@hB]x@sBzDoAjCe@nAo@jCs@rCw@rCg@bBk@bCa@dCStBGvA?pDHrC\\bG\\tDT~BJ`B@fBIjCYxCy@jEk@bCWvAMlAI~@QnCBfDPtBd@dE\\`EHfBC|DUrD_@zD_@tD_@vBiAxEmA~FoBfImA~EOx@iA~Eg@pBgAlDaA~CYhAa@|B_@bCWtBMrBGfDI~DSvKGjAMdBUlB]lCs@dDm@bCo@vCsA|EcAnD}@hD_AxD_A~E{@bEkA`FwAtFeAdD[z@oAvCoCbGeBxDkAfCuAxBw@tAyAfCoBnDiDxEoE`GwAzB}@|AgAhCg@|Ak@vBWnAeBdJo@xEW|Be@tE_@vD]~CQ`AWfA}@lC{AbDk@`Ai@r@gBnBmCjCqBdBk@l@{@|@{@nAsCbFmAbBoD~DgB|Bu@dAq@hAe@~@m@zAeBfF}@lBgAtBq@tAyAzDgBbFk@xBs@hD}@fEgBtIoAhG]jCYxDGlDDdDFrAj@fGZdGJrBFrB?rBMlE[zDoAtKc@`C[xCg@rESdDO|EE`CCxEJrLRbDNxA`@|CR`AXrBLdBDjA?rBStH@|GC|FIfMDnAAdBAhAKxBOxAUnBO`Aa@bBy@pCy@|Bg@fA_@n@oAjBm@x@w@|@yAtAkAz@{XdQiW`P_GrDkCdB{B~AcClBcB|AgFnFmEpEg@b@_@XwA|Ak@f@u@n@iA`AoBtBoG~GwDzDo@f@IF]\\}FbG{EnFuAlBiDnFoAhCkAxCgBhD_@v@e@r@Qh@iEdIwG~LMTqArB_CpE}@dB{@bBsErIuAnBsAxAuAhAeAn@_EbBcBh@oFzAqATO?m@FaFNiDMeDYcE{@mCUgCAcDJkB`@oAd@_Aj@yA`AqB~A]Xa@\\{@`Ai@x@MKgBxBECOj@Id@UlA[rBAJKFa@Pc@PkCb@YPYJ]?YtBYpB"
},
"summary": "",
"warnings": [
"Walking directions are in beta. Use caution \u2013 This route may be missing sidewalks or pedestrian paths."
],
"waypoint_order": []
}
]
すごく大きいdictですが、一つの経路情報が入っています。「legs」の意味がわかりませんが、ここに経路情報がほぼ入っていますね。全体の出発・到着時間、距離・所要時間、開始・終了地点などが入っています。また、「steps」に、同様に各ステップの出発・到着時間、距離・所要時間、開始・終了地点が入っています。
最後に、この経路検索結果を可視化してみます。「polyline」というのが、経路上の各地点の緯度・経度の情報を含んでいます。どのような仕組みかは今回調べていませんが、以下のコードでデコードできることを確認しました。そのままコピペしています。
Google Maps APIのPolyLineをデコードする関数(Python3) - Qiita
今回は、WALKINGを青、TRANSITを赤で描画します。
color_map = {
'WALKING': '#0000ff',
'TRANSIT': '#ff0000',
}
まずは、polylineをデコードします。
steps = []
for i,s in enumerate(directions_result[0]['legs'][0]['steps']):
print(i,s['travel_mode'])
steps.append({
'polyline': decode_polyline(s['polyline']['points']),
'travel_mode': s['travel_mode'],
'duration': s['duration']['text'],
'duration_sec': s['duration']['value']
})
以下のコードで、開始・終了地点のMarker、経路のPolyLineを描画します。
start,end = res['legs'][0]['start_location'],res['legs'][0]['end_location']
lat,lng = (start['lat']+end['lat'])/2,(start['lng']+end['lng'])/2
map = folium.Map(location=[lat,lng], zoom_start=12)
folium.Marker(location=[start['lat'],start['lng']], popup=res['legs'][0]['start_address']).add_to(map)
folium.Marker(location=[end['lat'],end['lng']], popup=res['legs'][0]['end_address']).add_to(map)
for step in steps:
folium.vector_layers.PolyLine(
locations=step['polyline'], popup=f'{step["travel_mode"]} ({step["duration"]})', color=color_map[step['travel_mode']]
).add_to(map)
fig = Figure(width=800, height=600)
fig.add_child(map)
いい感じに表示されました。小さいですが、よく見ると経路の両端が徒歩として青くなっています。GoogleMapsにあるような表示は簡単にできそうですね。ちなみになんですが、表示した地図はズームやスクロールができるので、色々見てみてください。popupで指定したテキストも、Markerをクリックすると表示されるようになっています。
まとめ
GoogleMaps APIを使い、以下の機能の実行と可視化をしてみました。
住所テキストから地点データを取得する
緯度・経度から地点データを取得する
ある地点からある地点への経路を検索する
経路検索は、GoogleMapsのアプリと同様のリッチな検索が可能です。料金にもよりますが、サービスに使ってみたらおもしろそうですね。次回は、OpenAI APIと組み合わせて観光プランを作成する機能を試作してみようと思います。
参考
GitHub - googlemaps/google-maps-services-python: Python client library for Google Maps API Web Services
Google Cloud プラットフォーム
Google Maps APIのPolyLineをデコードする関数(Python3) - Qiita