店選びは任せろ！Streamlitでグルメ王に俺はなる！！

# 目次
1.作ったもの
2.きっかけ
3.リンク
4.具体的な使い方
5.コード


# 作ったもの
Streamlitを使えばPythonのみでWebアプリを作れるとのことだったので，以前からあればいいなと思っていたものを作ってみました！その名も**施設選びお助けアプリ**です！すべてコードを公開しているのでPlacesAPIやStreamlitの使い方も参考になれば幸いです．

# きっかけ
　GoogleMap信者の私は飲食店(主にラーメン屋)を探す際に，必ずGoogleMapの評価を見てから行くのですが，最近，評価が4以上で口コミ数も1000件を超えているようなお店はとても美味しいことが多いなと気が付きました．
　また，旅行先などでご当地料理を食べに行く際に，ガイドブックに載っているような観光客で賑わっている有名なお店もいいのですが，地元の方が贔屓にしているような隠れた名店にいきたいなと思うこともしばしば．そんな時は，いつもGoogleMapをチェックするのですが，評価や口コミ数を指定することができないので，名店探しは骨の折れる作業になります．
　そこで，「GoogleMapの口コミ数や評価，価格などの条件を満たした店を表示してくれるようなWebアプリを作ろう!」と思った次第です．

# リンク
「施設選びお助けアプリ」のリンクはこちらになります．是非使ってみてください！
[追記]幸いにも多くの方にご使用いただき，無料で使用可能なデータを使い切ってしまったので限定公開にしております．

https://rossi0114-web-app-googlemap-app-ytp6wu.streamlitapp.com/


# 具体的な使い方
このアプリの具体的な使い方をご説明していきたいと思います．

まず，地点を入力します．
今回は今年行く予定(コロナ次第ですが...)の「那覇」にしてみます．
![スクリーンショット 2022-08-02 6.18.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627906/2eafd3d2-0dcb-9aa3-5401-be36ab7a1cff.png)

次に，先ほど入力した地点を中心に「指定の範囲から施設の情報を取得する」か，「地点から最寄りの20件を取得する」かを選択します．今回は「指定の範囲から検索する」を選択します．
![スクリーンショット 2022-08-02 6.22.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627906/d59aeb95-17a1-c710-9db4-d2f5f55a39b5.png)

すると以下のような画面が表示され，地点からの半径を入力することができます．
今回は1000m(1km)を選択しましたが，最大5000m(5km)まで指定することが可能です．
![スクリーンショット 2022-08-02 6.24.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627906/ff1a5067-a8ec-e1d7-d577-048894127e75.png)

次に，施設の種類を選択します．「飲食店」だけではなく「病院」なども選択することができ，様々な施設を検索することが可能になっています．
![スクリーンショット 2022-08-02 6.28.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627906/d2e344ae-d51b-04af-2605-612c3f8ab57b.png)

次にキーワードを入力します．今回は沖縄といえばステーキということで「ステーキ」を入力します．
![スクリーンショット 2022-08-02 6.32.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627906/aa8bc967-47ba-97c6-dd41-72cb076abaf9.png)

ここで条件の入力に移ります．

まず，営業中がどうかを指定することができます．今回はいますぐに行くわけではないので，営業中の店も営業時間外の店の「どちらも表示する」を選択します．
![スクリーンショット 2022-08-02 6.37.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627906/c58c2657-88c7-557b-6244-39076714c237.png)

次に，「評価」「口コミ数」「価格」を指定します．
評価と口コミ数は下限を，価格は上限を指定することができます．
今回は評価が４以上，隠れた名店を見つけたいので口コミ数は０件以上，そして価格はリーズナブルが良いので０以上から２以下で指定します．

![スクリーンショット 2022-08-02 6.43.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627906/5f2117a9-fe41-4401-4c4a-77fb30251379.png)
![スクリーンショット 2022-08-02 6.43.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627906/2e2c72b0-d892-f2fe-3743-9ce4d7f9b5a9.png)

そして，検索開始ボタンを押すと
このような結果が返ってきました．

![スクリーンショット 2022-08-02 6.55.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627906/89e21e0a-5924-bbf3-1ed8-a1ba4afa4133.png)


おおーやっぱり「ジャッキーステーキハウス」はかなり有名ですね．
この中だと，「CASA FELIZ RIBSダイナー」が隠れ名店の匂いがするぞ！

ということで下のurlを埋め込んだ店名をクリックすることで「CASA FELIZ RIBSダイナー」のGoogleMapのページへ飛びます．
![スクリーンショット 2022-08-02 6.58.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627906/67014bb0-65e6-34e6-9441-3d9c3695d55b.png)


沖縄のランチはここに決定！
![スクリーンショット 2022-08-02 7.04.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627906/a423a0a8-254e-ccaa-f5f6-f96bf94836ac.png)

# コード
まずはGoogleのPlacesAPIを使用するので，APIKeyを取得します．ここではYOUR_KEYとなっています．

インポート
```python
import requests
import pandas as pd
import streamlit as st
import folium
from streamlit_folium import folium_static
```
地名から緯度・経度を取得
```python
st.title("施設選びお助けアプリ")
st.markdown("""#### 概要：  
GoogleMap上での評価や口コミ数,価格を指定することで,あなたのニーズにマッチした最大20件の施設を選ぶことができます.""")

st.markdown("""### 1 地点""")
plc=st.text_input("以下に入力してください.ex)札幌駅",value="")

url =f"""https://maps.googleapis.com/maps/api/geocode/json?address={plc}&language=ja&region=jp&key=YOUR_KEY"""
payload={}
headers={}
response = requests.request("GET", url, headers=headers, data=payload)
results=response.json()["results"]
for result in results:
    lat=float(result["geometry"]["location"]["lat"])
    lng=float(result["geometry"]["location"]["lng"])
    lat='{:.013f}'.format(lat)
    lng='{:.013f}'.format(lng)
```
緯度，経度，半径，施設の種類，キーワードから情報を取得する
```python
#半径を指定する場合
st.markdown("""### 2 範囲or最寄り""")
option1 = st.selectbox("以下から1つ選択してください.「指定の範囲内から検索する」を選択した場合は,人通りの多い場所が優先的に選ばれます．", ("指定の範囲内から検索する","地点から最寄りの20件を検索する"),index=0)
##施設の種類
facility_type={
    "飲食店":"restaurant",
    "カフェ":"cafe",
    "お店":"store",
    "バー":"bar",
    "パン屋":"bakery",
    "遊園地":"amusement_park",
    "歯医者":"dentist",
    "病院":"hospital",
    "宿泊":"lodging",
    "キャンプ場":"campground"
}
if option1=="指定の範囲内から検索する":
    rmax=st.number_input("範囲を指定してください(最大5000m)",min_value=0, max_value=5000, value=1000, step=1000)
    st.markdown("""### 3 施設の種類""")
    ty=st.selectbox("以下から一つ選んでください",list(facility_type.keys()))
    typ=facility_type[ty]
    ##　キーワードの入力
    st.markdown("""### 4 キーワード""")
    kyw=st.text_input("入力してください.ex)味噌ラーメン",value="")
    url =f"""https://maps.googleapis.com/maps/api/place/nearbysearch/json?location={lat}%2C{lng}&language=ja&radius={rmax}&type={typ}&keyword={kyw}&key=YOUR_KEY"""
    payload={}
    headers = {}
    response = requests.request("GET", url, headers=headers, data=payload)
    results=response.json()["results"]
    facilities_info=[]
    for result in results:
            facility_info={}
            facility_info["緯度"]=result["geometry"]["location"]["lat"]
            facility_info["経度"]=result["geometry"]["location"]["lng"]
            facility_info["名前"]=result["name"]
            try:
                facility_info["営業中"]=result['opening_hours']["open_now"]
            except:
                facility_info["営業中"]=None
            try:
                facility_info["価格"]=result["price_level"]
            except:
                facility_info["価格"]=None
    
            facility_info["評価"]=result["rating"]
            facility_info["口コミ数"]=result["user_ratings_total"]
            facility_info["id"]=result["place_id"]
            facilities_info.append(facility_info)
           
    df_info=pd.DataFrame(facilities_info)
```
```python
#最寄りの20件を取得する場合
else:
    st.markdown("""### 3 施設の種類""")
    ty=st.selectbox("以下から一つ選んでください",list(facility_type.keys()))
    typ=facility_type[ty]
    ##　キーワードの入力
    st.markdown("""### 4 キーワード""")
    kyw=st.text_input("入力してください.ex)味噌ラーメン",value="")

    url =f"""https://maps.googleapis.com/maps/api/place/nearbysearch/json?location={lat}%2C{lng}&language=ja&rankby=distance&type={typ}&keyword={kyw}&key=YOUR_KEY"""
    payload={}
    headers = {}
    response = requests.request("GET", url, headers=headers, data=payload)
    results=response.json()["results"]
    facilities_info=[]
    for result in results:
            facility_info={}
            facility_info["緯度"]=result["geometry"]["location"]["lat"]
            facility_info["経度"]=result["geometry"]["location"]["lng"]
            facility_info["名前"]=result["name"]
            try:
                facility_info["営業中"]=result['opening_hours']["open_now"]
            except:
                facility_info["営業中"]=None
            try:
                facility_info["価格"]=result["price_level"]
            except:
                facility_info["価格"]=None
    
            facility_info["評価"]=result["rating"]
            facility_info["口コミ数"]=result["user_ratings_total"]
            facility_info["id"]=result["place_id"]
            facilities_info.append(facility_info)
           
    df_info=pd.DataFrame(facilities_info)

```

条件からデータを絞り込む
```python
#df_infoから営業中，口コミ数，評価，価格の条件を満たすものを抽出
##営業中
st.markdown("""#### 5.1 営業中か否か""")
option2 = st.selectbox("以下の3つから1つ選択してください",("営業中の施設のみを表示する","どちらも表示する"),index=1)
if option2=="どちらも表示する":
    pass
elif option2=="営業中の施設のみを表示する":
    df_info1=pd.DataFrame()
    df_info2=pd.DataFrame()
    df_info1=df_info[df_info["営業中"]==None]
    df_info2=df_info[df_info["営業中"]==True]
    df_info=pd.concat([df_info1,df_info2])
else:
    df_info1=pd.DataFrame()
    df_info2=pd.DataFrame()
    df_info1=df_info[df_info["営業中"]==None]
    df_info2=df_info[df_info["営業中"]==False]
    df_info=pd.concat([df_info1,df_info2])
##評価
st.markdown("""#### 5.2 評価""")
xmin=st.number_input("評価の下限を0から4で指定してください.ex)4を選んだ場合は,評価が4から5の施設が表示されます.",min_value=0, max_value=4, value=3, step=1)
df_info=df_info[df_info["評価"]>=xmin]

##口コミ数
st.markdown("""#### 5.3 口コミ数""")
ymin=st.number_input("口コミ数の下限を指定してください.ex)100を選んだ場合は,口コミ数が100以上の施設が表示されます.",min_value=0,value=0,step=100)
df_info=df_info[df_info["口コミ数"]>=ymin]

##価格
st.markdown("""#### 5.4 価格""")
st.markdown("""
0：無料\\
1：安価\\
2：普通\\
3：高価\\
4：とても高価""")

zmax=st.number_input("価格の上限を0から4で指定してください.ex)3を選んだ場合は,価格が0から3の施設が表示されます.",min_value=0, max_value=4, value=4, step=1)

df_info3=pd.DataFrame()
df_info4=pd.DataFrame()
df_info5=pd.DataFrame()
df_info3=df_info[df_info["価格"]==None]
df_info4=df_info[df_info["価格"]!=None]
df_info5=df_info4[df_info4["価格"]<=zmax]
df_info=pd.concat([df_info3,df_info5])
```
GoogleMapのurlを取得する
```python
##idからurlを取得する
df_info["url"]=""
lis1=[]
for item in df_info["id"]:
    id=item
    url = f"""https://maps.googleapis.com/maps/api/place/details/json?place_id={id}&fields=url&key=YOUR_KEY"""
    payload={}
    headers = {}
    response = requests.request("GET", url, headers=headers, data=payload)
    url=response.json()["result"]["url"]
    lis1.append(url)
df_info["url"]=lis1
```
検索結果の画面を作る
```python
st.write("検索を開始しますか？")
if st.button("開始"):
    st.markdown("""### 検索結果""")
    #画面に表示するようのデータフレームを作る
    st.markdown("""##### データ""")
    df_display=df_info.copy()
    df_display["営業中"]=df_display["営業中"].replace(True,"Open").replace(False,"Close")
    df_display=df_display[["名前","営業中","価格","評価","口コミ数"]].sort_values("評価",ascending=False).reset_index(drop=True)
    st.write("上から評価順になっています．また，<NA>は不明なことを示しています．")
    st.dataframe(df_display)


    #url
    st.markdown("""##### GoogleMapに移動する""")
    for item,name in zip(df_info["url"],df_info["名前"]):
        link = f"""[{name}]({item})"""
        st.markdown(link, unsafe_allow_html=True)

    st.markdown("""##### 地図""")
    ##条件に合う店をマッピングする
    m = folium.Map(location=[lat,lng], zoom_start=13)
    folium.Marker(location=[lat, lng],popup="中心地").add_to(m)#ピン
    try:
        folium.Circle(radius=rmax,location=[lat, lng],popup="検索範囲",color="blue",fill=True,fill_opacity=0.07).add_to(m)
    except:
        pass  
    for name,lat,lng in zip(df_info["名前"],df_info["緯度"],df_info["経度"]):
        folium.Marker(location=[lat, lng],popup=name,icon=folium.Icon(color="red",icon="eye-open")).add_to(m)
    folium_static(m)  

```


## 参考

https://developers.google.com/maps/documentation/places/web-service/details#maps_http_places_details_fields-py

https://streamlit.io/