それなりに多い(数万件単位の)位置情報を、簡単に無料で地図上にプロットして確認する方法3選

# TL;DR

基本的にはどれを選んでもすぐ出来ますが、一長一短あるので、目的によって使い分けるのがよいです。

1. Folium (+MarkerCluster)
2. GitHub with GeoJSON
3. Geolonia with GeoJSON

地図の触り心地については、実際に確認できるサンプルを用意したので試してみてください。
https://terukizm.github.io/easy-geo-plotting-example/


# 事前準備

[オープンデータ用北海道施設位置情報データベース](https://koukita.github.io/hokkaido_od_geodatabase/)で提供されている「北海道の施設の位置情報(35,000件くらい)」を地図上にプロットしてみます。

* CSV(cp932)をpandas.DataFrameとして読み込む
  * 該当データ中に不正なlatlng(lat=lngになってる)が含まれているので除去
  * CSVから空値を`float(NaN)`で読むとGeoJSONに出力した場合に読み込みに失敗するので`""`に

pandas(Python)を使わないといけない理由は特にないのですが、(上記の不正データ除去のように)データ加工するときに便利なので…

```shell
$ pip install pandas
```

```python
import pandas as pd

url = "https://koukita.github.io/hokkaido_od_geodatabase/data/Hokkaido_OD_GeoDataBase2018.csv"

# GeoJSONはnullが入るとコケるので空文字に倒す
df = pd.read_csv(url, encoding="cp932").fillna("")
df = df.query('データ区分 != "国・都道府県機関"')  # 該当データはlat=lngとなっており作成ミスっぽいので削除
```
![スクリーンショット 2021-02-15 18.43.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50184/8daaf88e-c7a0-c185-77f7-4623f0b0d18f.png)



# 1. Folium (+MarkerCluster)

通常のマーカー作成だと1000件〜3000件くらいから実用に耐えない感じになるので、MarkerClusterを使います。
35,000件だと初回読み込みは相当もっさりしますが、一度読みきってしまえば結構サクサク動く感じ。
とりあえずこれで試して微妙だなーって場合、他の方法を試すのが良さそう。

https://terukizm.github.io/easy-geo-plotting-example/folium.html
![スクリーンショット 2021-02-15 17.52.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50184/cf3f2730-afb5-ee34-ae51-a8b2e3173728.png)

```shell
$ pip install folium
```

```python 
from folium import Map, Popup, Marker, Icon, IFrame
from folium.plugins import MarkerCluster

def folium(dest="docs/folium.html"):
    """ genreate folium.html """
    my_map = Map(
        location=[43.0645597, 141.3481196],
        zoom_start=10,
        width="100%",
        height="90%",
        tiles="openstreetmap",
    )
    marker_cluster = MarkerCluster()

    for _, row in df.iterrows():
        lat = row["緯度"]
        lng = row["経度"]
        name = row["施設名"]
        address = row["検索用住所"]
        data_type = row["データ区分"]

        popup_html = f"""
    <h1>{name}</h1>
    <h2>{address}</h2>
    <table>
    <tbody>
        <tr>
            <th>緯度</th>
            <td>{lat}</td>
        </tr>
        <tr>
            <th>経度</th>
            <td>{lng}</td>
        </tr>
        <tr>
            <th>データ区分</th>
            <td>{data_type}</td>
        </tr>
    </tbody>
    </table>
    """
        popup = Popup(
            IFrame(popup_html), min_width=400, max_width=400
        )
        Marker(
            location=[lat, lng], popup=popup, icon=Icon(color="red")
        ).add_to(marker_cluster)

    marker_cluster.add_to(my_map)
    my_map.save(dest)

```

* メリット
  * 楽
  * 見た目がきれい、地名とかも日本語で出る
  * jupyter上からとかでも動かせる(かもしれない、Google Colabの場合はうまく動かなかった)
  * htmlひとつに全部まとめられるので共有が楽
* デメリット
    * ファイルサイズがでかい(上記で60MBくらい)
        * 初回読み込みがめちゃくちゃ遅い
    * HTML生成にもわりと時間がかかる
    * マーカー数が多くなると操作が厳しいかもしれない(10万オーダーとか)
        * (PCだと)触ってて重さを感じる -> GPU依存かもしれない
    * カスタマイズとかも工夫すれば色々できそうだが、それなりに大変そう 

# 2. GitHub with GeoJSON

GeoJSONに変換してリポジトリにコミット、GitHub上からプレビューするだけです。

https://github.com/terukizm/easy-geo-plotting-example/blob/gh-pages/example.geojson
![スクリーンショット 2021-02-15 18.02.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50184/842669e3-b2c2-0d59-d8b4-62938d828912.png)

とりあえずコミットするだけなのでこれも試してみて、ダメだったらそのまま3.で紹介しているGeoloniaにぶっこむのがよいかと。

```shell
$ pip install geojson
```

```python
import json
from collections import OrderedDict
from geojson import Feature, FeatureCollection, Point

def geojson(dest="docs/example.geojson", pretty=True):
    """ genreate example.geojson """
    def __feature(row: pd.Series):
        """ GeoJSONのFeature要素(Point)を作成 """
        lat = row["緯度"]
        lng = row["経度"]
        name = row["施設名"]
        address = row["検索用住所"]
        data_type = row["データ区分"]

        coords = (lng, lat)  # 順番に注意
        description = f"""
        <p>{address}</p>
    <table>
    <tbody>
        <tr>
            <th>緯度</th>
            <td>{lat}</td>
        </tr>
        <tr>
            <th>経度</th>
            <td>{lng}</td>
        </tr>
        <tr>
            <th>データ区分</th>
            <td>{data_type}</td>
        </tr>
    </tbody>
    </table>
    """
        props = OrderedDict(
            {
                "title": name,
                "description": description,
            }
        )
        return Feature(geometry=Point(coords), properties=props)

    # genrerate feature list
    features = df.apply(__feature, axis=1).tolist()

    # pretty or minify
    # (ファイルサイズはあまり関係なく、properties数が多くなりすぎるとGitHubで表示できなくなる)
    with open(dest, "w", encoding="utf-8") as f:
        if pretty:
            json.dump(
                FeatureCollection(features), f, ensure_ascii=False, indent=2
            )
        else:
            json.dump(
                FeatureCollection(features),
                f,
                ensure_ascii=False,
                separators=(",", ":"),
            )
```

* メリット
  * 楽
  * GeoJSON生成 → git commit するだけなので数秒で終わる
  * (GitHubリポジトリが見られれば)情報共有するのも楽
* デメリット
    * 地名とかが英語なのでパッと見でわかりずらい
    * データ量が多くなると表示できないことがあるが、基準が謎
        * ファイルサイズ、件数ではなくpropetiesの総数に依存している気がする
        * 同じデータ量でもhtmlタグにして一つのpropertiesにまとめたりすると表示できるようになる、謎
            * GeoJSONのparseに時間がかかるとタイムアウト、とかなのかもしれない
    * 画面サイズとデザインが固定、若干変な挙動をしたりすることがあり、微妙に使いづらい
        * 画面が狭いとかなり厳しい


# 3. Geolonia with GeoJSON

[Geolonia](https://geolonia.com/)は`localhost`、`github.io`、`codepen.io`上からのアクセスについては、 `geolonia-api-key=YOUR-API-KEY`を指定することで、わざわざAPIKeyを払い出すことなく試用することができます。(2021/02時点)

参考: [APIキーを取得 \| Geolonia 公式ドキュメント](https://docs.geolonia.com/tutorial/002/#your-api-key-%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)

適当にHTMLを書いてホスティングし、2.で生成したGeoJSONを読ませてやればいっちょ上がりです。
https://terukizm.github.io/easy-geo-plotting-example/geolonia.html

![スクリーンショット 2021-02-15 18.21.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50184/268a1586-5eaa-d1a7-7843-e916c1a30d7f.png)


```index.html
<!doctype html>

<html lang="ja">

<head>
  <meta charset="utf-8">
  <title>geolonia example</title>
  <style>
    .geolonia {
      width: 100%;
      height: 90vh;
    }
  </style>
</head>

<body>
  <div class="geolonia" data-style="geolonia/basic" data-geojson="./example.geojson"></div>
  <script type="text/javascript" src="https://cdn.geolonia.com/v1/embed?geolonia-api-key=YOUR-API-KEY"></script>
</body>

</html>
```

* メリット
  * 楽
  * 動作も軽い
  * 見ためもきれい、地名とかも日本語で出る
  * デフォルトでタイトルにはラベルが出るのでいちいちマーカーをクリックしなくてよい
  * Mapbox GL JS互換なのでカスタマイズしたり作り込みがしやすい
  * [公式ドキュメント](https://docs.geolonia.com/)が丁寧
* デメリット
  * `YOUR-API-KEY`が使えない環境に配置する場合、ユーザ登録からアクセスキー払い出しが必要
  * GeoJSONにして、さらに(ペライチとはいえ)わざわざhtml用意して配置しないといけないので、面倒といえば面倒
      * 上記は`python -m http.server 18080`で動作確認してた
      * GeoJSONはGitHubに突っ込んでHTMLとかはCodepenに書く、が楽かもしれない。おこのみで
  * [Simplestyle](https://github.com/mapbox/simplestyle-spec)の範囲以上にデザインをカスタマイズしようとすると、Mapbox GL JSに関する知識とかが必要になってきてハードルが高くなる


# おわりに

上記全ソースを以下に置いておきます。
https://github.com/terukizm/easy-geo-plotting-example

位置情報を雑にプロットして確認したいときの参考になれば幸いです。

# 追伸

本記事はこちらのサービスを作ったときの副産物で、カスタマイズ性とかの問題でGeoloniaを採用しています。

[5万円分のGoToEatプレミアム食事券を使い切るためにWebサービスを作った話 \- Qiita](https://qiita.com/terukizm/items/60c901d0bcb0bcc70a8a)