TL;DR
基本的にはどれを選んでもすぐ出来ますが、一長一短あるので、目的によって使い分けるのがよいです。
- Folium (+MarkerCluster)
- GitHub with GeoJSON
- Geolonia with GeoJSON
地図の触り心地については、実際に確認できるサンプルを用意したので試してみてください。
https://terukizm.github.io/easy-geo-plotting-example/
事前準備
オープンデータ用北海道施設位置情報データベースで提供されている「北海道の施設の位置情報(35,000件くらい)」を地図上にプロットしてみます。
- CSV(cp932)をpandas.DataFrameとして読み込む
- 該当データ中に不正なlatlng(lat=lngになってる)が含まれているので除去
- CSVから空値を
float(NaN)
で読むとGeoJSONに出力した場合に読み込みに失敗するので""
に
pandas(Python)を使わないといけない理由は特にないのですが、(上記の不正データ除去のように)データ加工するときに便利なので…
$ pip install pandas
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となっており作成ミスっぽいので削除
1. Folium (+MarkerCluster)
通常のマーカー作成だと1000件〜3000件くらいから実用に耐えない感じになるので、MarkerClusterを使います。
35,000件だと初回読み込みは相当もっさりしますが、一度読みきってしまえば結構サクサク動く感じ。
とりあえずこれで試して微妙だなーって場合、他の方法を試すのが良さそう。
https://terukizm.github.io/easy-geo-plotting-example/folium.html
$ pip install folium
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依存かもしれない
- カスタマイズとかも工夫すれば色々できそうだが、それなりに大変そう
- ファイルサイズがでかい(上記で60MBくらい)
2. GitHub with GeoJSON
GeoJSONに変換してリポジトリにコミット、GitHub上からプレビューするだけです。
https://github.com/terukizm/easy-geo-plotting-example/blob/gh-pages/example.geojson
とりあえずコミットするだけなのでこれも試してみて、ダメだったらそのまま3.で紹介しているGeoloniaにぶっこむのがよいかと。
$ pip install geojson
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はlocalhost
、github.io
、codepen.io
上からのアクセスについては、 geolonia-api-key=YOUR-API-KEY
を指定することで、わざわざAPIKeyを払い出すことなく試用することができます。(2021/02時点)
参考: APIキーを取得 | Geolonia 公式ドキュメント
適当にHTMLを書いてホスティングし、2.で生成したGeoJSONを読ませてやればいっちょ上がりです。
https://terukizm.github.io/easy-geo-plotting-example/geolonia.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互換なのでカスタマイズしたり作り込みがしやすい
- 公式ドキュメントが丁寧
- デメリット
-
YOUR-API-KEY
が使えない環境に配置する場合、ユーザ登録からアクセスキー払い出しが必要 - GeoJSONにして、さらに(ペライチとはいえ)わざわざhtml用意して配置しないといけないので、面倒といえば面倒
- 上記は
python -m http.server 18080
で動作確認してた - GeoJSONはGitHubに突っ込んでHTMLとかはCodepenに書く、が楽かもしれない。おこのみで
- 上記は
- Simplestyleの範囲以上にデザインをカスタマイズしようとすると、Mapbox GL JSに関する知識とかが必要になってきてハードルが高くなる
-
おわりに
上記全ソースを以下に置いておきます。
https://github.com/terukizm/easy-geo-plotting-example
位置情報を雑にプロットして確認したいときの参考になれば幸いです。
追伸
本記事はこちらのサービスを作ったときの副産物で、カスタマイズ性とかの問題でGeoloniaを採用しています。