foliumでアメダス観測所を地図上にプロットする

#### はじめに

建物や史跡などモノの場所を地図上にプロットしたいと思い、調べていたところ、GoogleMapAPI以外でも地図上にピンを立てたり、ヒートマップを描いたりすることができるfoliumというライブラリが各所に紹介されていました。
これを使って、オープンデータなどで所在地（住所）や地理情報をもつデータから地図上にプロットしてみました。

先人たちの知恵をお借りするなどして解決できたことを、この場をお借りして感謝するとともに、大変恐縮ですが**自分のメモ**として、こちらへまとめておきます。

:::note info
今回使用する環境等のバージョン

Windows 11 Pro 23H2
Python 3.12.0
:::

### 利用するデータ

毎日の生活や行動予定に影響する天気・気象。これを日々観測している観測所・観測点があります。
天気予報などで耳にしたこともあるかと思いますが、アメダス観測所の所在地情報を地図上にプロットしてみます。

#### データの在り処：
国土交通省 気象庁のホームページより：
ホーム > 知識・解説 > 気象衛星・気象観測 > 地域気象観測システム（アメダス）

https://www.jma.go.jp/jma/kishou/know/amedas/kaisetsu.html

[地域気象観測所一覧 [ZIP圧縮形式]](https://www.jma.go.jp/jma/kishou/know/amedas/ame_master.zip)
[地域気象観測所一覧（雪） [ZIP圧縮形式]](https://www.jma.go.jp/jma/kishou/know/amedas/snow_master.zip)

上記2つのデータを利用します。zipファイルを展開するとcsvファイルが格納されています。

#### アメダスとは：
> アメダス（AMeDAS）とは「Automated Meteorological Data Acquisition System」の略で、「地域気象観測システム」といいます。 雨、風、雪などの気象状況を時間的、地域的に細かく監視するために、降水量、風向・風速、気温、湿度の観測を自動的におこない、気象災害の防止・軽減に重要な役割を果たしています。

（上記ページより引用）

### foliumとは

foliumはleaflet.jsというJavaScriptで利用できるマップをPythonライブラリ化したもので、これによりPythonでマップを用いてデータを可視化することが容易となります。

https://python-visualization.github.io/folium/latest/index.html

https://github.com/python-visualization/folium

以下のpipコマンドでインストール可能です。
```terminal
pip install folium
```

### 完成イメージ
htmlファイルで保存し、Webブラウザ上でズームイン／ズームアウトしたり、ピンをクリックすることでポップアップウィンドウを開いて情報表示させることもできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/902793/152b9975-7c92-30ba-68b4-fdb79740b879.png)

### コードの説明

- 地図上で観測所の所在地にピンを立てるイメージですので、MarkerClusterクラスを利用します。

```python
import pandas as pd
import folium
from folium.plugins import MarkerCluster
```

- pandasデータフレームにそれぞれcsvファイルを読み込ませて、連結します。
 （csvファイルの文字コードが特殊文字ありのshift-jisでしたため、`encoding='cp932'` としています。）

```python
# csvからのデータ読み込み(必要なカラムだけを抽出)
df1 = pd.read_csv(
    './data/ame_master_20230824.csv',
    usecols=['観測所番号','種類','観測所名','所在地','緯度(度)','緯度(分)','経度(度)','経度(分)','海面上の高さ(ｍ)'],
    encoding='cp932'
    )
df2 = pd.read_csv(
    './data/ame_master_20230824.csv',
    usecols=['観測所番号','種類','観測所名','所在地','緯度(度)','緯度(分)','経度(度)','経度(分)','海面上の高さ(ｍ)'],
    encoding='cp932'
    )

df = pd.concat([df1, df2])
```

- 所在地の地理情報が、緯度（度）：10進、緯度（分）：60進 であったため、これを計算で分秒を含めて10進にします。
　（※元データでは、分の小数として秒をもっています。）

```python
df['緯度(10進)'] = df['緯度(度)'] + df['緯度(分)'] / 60
df['経度(10進)'] = df['経度(度)'] + df['経度(分)'] / 60
```

- 地図を生成します。東京千代田区北の丸公園露場を中心としました。ズーム倍率は`zoom_start`の値で変更できます。

```python
# 地図生成（東京千代田区北の丸公園露場 中心）
folium_map = folium.Map(location=[35.691666, 135.751666], zoom_start=14)
```

- マーカーをプロットしていきます。アメダス観測所には観測できる要素や役割により、5つも種類があることも分かりました。種類ごとに色を変えてプロットしてみます。
　マーカーを地図上にプロットしたい場合は`folium.Marker`を使います。
- 地図上に表示されたマーカーをクリックすることで、ポップアップウィンドウを表示して観測所名を表示するようにしました。（`popup=row['観測所名']`）

```python
marker_cluster = MarkerCluster().add_to(folium_map)

# マーカープロット
for i, row in df.iterrows():
    count = row['種類']
    if count == '官':  # 気象官署
        folium.Marker(
            location=[row['緯度(10進)'], row['経度(10進)']],
            popup=row['観測所名'],
            icon=folium.Icon(color='red')
        ).add_to(marker_cluster)
    elif count == '四':  # 四要素観測所
        folium.Marker(
            location=[row['緯度(10進)'], row['経度(10進)']],
            popup=row['観測所名'],
            icon=folium.Icon(color='orange')
        ).add_to(marker_cluster)
    elif count == '三':  # 三要素観測所
        folium.Marker(
            location=[row['緯度(10進)'], row['経度(10進)']],
            popup=row['観測所名'],
            icon=folium.Icon(color='lightgreen')
        ).add_to(marker_cluster)
    elif count == '雨':  # 雨量観測所
        folium.Marker(
            location=[row['緯度(10進)'], row['経度(10進)']],
            popup=row['観測所名'],
            icon=folium.Icon(color='blue')
        ).add_to(marker_cluster)
    else:  # 積雪観測所
        folium.Marker(
            location=[row['緯度(10進)'], row['経度(10進)']],
            popup=row['観測所名'],
            icon=folium.Icon(color='gray')
        ).add_to(marker_cluster)
```

- 地図を保存するため`folium_map.save`を使います。保存形式は.htmlファイル形式です。
　Jupyter Notebookで表示した場合は、`folimn_map` のみで可です。

```python
# 地図表示
folium_map.save('./data/amedas-map.html')
```

<details><summary>コード全体</summary>

```python
import pandas as pd
import folium
from folium.plugins import MarkerCluster

# csvからのデータ読み込み(必要なカラムだけを抽出)
df1 = pd.read_csv(
    './data/ame_master_20230824.csv',
    usecols=['観測所番号','種類','観測所名','所在地','緯度(度)','緯度(分)','経度(度)','経度(分)','海面上の高さ(ｍ)'],
    encoding='cp932'
    )
df2 = pd.read_csv(
    './data/ame_master_20230824.csv',
    usecols=['観測所番号','種類','観測所名','所在地','緯度(度)','緯度(分)','経度(度)','経度(分)','海面上の高さ(ｍ)'],
    encoding='cp932'
    )

df = pd.concat([df1, df2])

df['緯度(10進)'] = df['緯度(度)'] + df['緯度(分)'] / 60
df['経度(10進)'] = df['経度(度)'] + df['経度(分)'] / 60

# 地図生成（東京千代田区北の丸公園露場 中心）
folium_map = folium.Map(location=[35.691666, 135.751666], zoom_start=14)
marker_cluster = MarkerCluster().add_to(folium_map)

# マーカープロット
for i, row in df.iterrows():
    count = row['種類']
    if count == '官':  # 気象官署
        folium.Marker(
            location=[row['緯度(10進)'], row['経度(10進)']],
            popup=row['観測所名'],
            icon=folium.Icon(color='red')
        ).add_to(marker_cluster)
    elif count == '四':  # 四要素観測所
        folium.Marker(
            location=[row['緯度(10進)'], row['経度(10進)']],
            popup=row['観測所名'],
            icon=folium.Icon(color='orange')
        ).add_to(marker_cluster)
    elif count == '三':  # 三要素観測所
        folium.Marker(
            location=[row['緯度(10進)'], row['経度(10進)']],
            popup=row['観測所名'],
            icon=folium.Icon(color='lightgreen')
        ).add_to(marker_cluster)
    elif count == '雨':  # 雨量観測所
        folium.Marker(
            location=[row['緯度(10進)'], row['経度(10進)']],
            popup=row['観測所名'],
            icon=folium.Icon(color='blue')
        ).add_to(marker_cluster)
    else:  # 積雪観測所
        folium.Marker(
            location=[row['緯度(10進)'], row['経度(10進)']],
            popup=row['観測所名'],
            icon=folium.Icon(color='gray')
        ).add_to(marker_cluster)

# 地図表示
folium_map.save('./data/amedas-map.html')

```
</details>

　
↓　完成した地図から **千代田区北の丸公園露場にある観測地点** のプロット地点を拡大表示した画像：
　ダウンロードしたcsvの備考欄を見ると、「気温、雨」の観測地点と、「風、日照」の観測地点と2行データがあり、座標位置は同じなのですが、地図を拡大表示して、ピンを表示すると若干ズレて表示されます。
![amedas-map.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/902793/c432238d-f0e1-2f73-6e9d-56f2faf920ed.png)

https://www.env.go.jp/garden/kokyogaien/news/2015/08/post_82.html

---
今回のソースコードはGitHubにコミット済みです。

https://github.com/whitecat-22/amedas-map/tree/main

---
#### 参考

https://tech.fragment.co.jp/python/python%E3%81%A7%E6%97%A5%E6%9C%AC%E6%B8%AC%E5%9C%B0%E7%B3%BB%E3%81%AE%E4%BD%8D%E7%BD%AE%E6%83%85%E5%A0%B1%E3%82%92%E4%B8%96%E7%95%8C%E6%B8%AC%E5%9C%B0%E7%B3%BB%E3%81%AB/

今回は、60進の日本測地系を単純に10進に変換したのみでしたが、正しくはこれを世界測地系の値に変換すべきだったかも知れません。

---
#### (編集後記)
Pythonのfoliumライブラリを使うことで地図上へのプロットが容易にできました。
オープンデータなど公開されているデータで住所や地理情報をもつものについて、プロットしたり、ヒートマップをつくったりすることができます。
foliumのユーザーガイドを読み込んで、みなさまもぜひデータの可視化にチャレンジしてみてください。