2024.03.19
Pythonで地図を描く ~Foliumで冒険マップを作ってみた~#プログラミング編
#Python #Folium
トップ > 技術ナレッジのアーカイブ > Pythonで地図を描く ~Foliumで冒険マップを作ってみた~#プログラミング編
2024.03.19
#Python #Folium
先に定義しておきます。
- import os
- import seaborn as sns
- import numpy as np
- import csv
- import pandas as pd
- import seaborn as sns
- import cv2
- import io
- import glob
- from PIL import Image
- import matplotlib
- import matplotlib.pyplot as plt
- from matplotlib import ticker
- import datetime
- import matplotlib.font_manager as fm #https://python-academia.com/matplotlib-font/
- import matplotlib.dates as mdates
- from matplotlib.gridspec import GridSpec
- import geopandas
- import folium
- from selenium import webdriver
- from selenium.webdriver.chrome.options import Options
- import chromedriver_binary
- import json
- from time import sleep as slp
コード例とその説明は以下のとおりです。
- import (上に定義したすべてのインポートファイル)
- def map_visualization():
- pref_geojson = "N03-23_22_230101.geojson" # 静岡県の市町村緯度経度情報
- # 緯度経度情報読み込みと描画
- pref_location = [35.0, 138.1] # 表示する地図中心の緯度経度設定
- f = open(pref_geojson, 'r', encoding="utf-8_sig")
- pref_geojson = json.load(f) # jsonファイルをpythonオブジェクトとして読み込み
- m = folium.Map(location=pref_location,zoom_start=8.5) # ベースマップを作製
- folium.GeoJson(pref_geojson).add_to(m) # 地図上に緯度経度の点群を追加
- m.save('outline.html') # 追加情報含めてhtmlファイルとして保存
- browser = webdriver.Chrome() # Chromeを立ち上げる
- tmpurl = 'C:/AI/test/wedding_movie/outline.html'
- browser.get(tmpurl) # htmlファイルを開く
- slp(1)
- filename = 'outline.png'
- browser.save_screenshot(filename) # スクリーンショットを保存
- browser.quit()
- if __name__ == '__main__':
- map_visualization()
実行結果は以下の通りです。
以下の変更を行うことで、色が変化します(これ以降は紙面の都合上、変更した箇所だけ変更前後のコードを示します)。
変更前
- folium.GeoJson(pref_geojson).add_to(m)
変更後
- folium.GeoJson(pref_geojson,
- style_function=lambda x: {'color': 'aqua'}
- ).add_to(m)
実行結果は以下の通りです。
Folium.Map()関数のオプションであるtilesやattrの設定をすることで選択、カスタマイズができます。 (参照元.1)(参照元.2)
今回は以下①~③の3つの方法をご紹介します。
変更前はこちら
- m = folium.Map( location=pref_location, zoom_start=8.5)
変更後はこちら
Foliumにビルトインされている地図タイルを使う場合、以下のようにtiles=’openstreetmap’, ‘cartodbpositron’などの名称の指定でタイルを変更することが可能です。
- m = folium.Map(location=pref_location,
- tiles='cartodbpositron', zoom_start=8.5)
方法①の実行結果は以下の通りです
Foliumにビルトインされていない地図タイルを使うこともできます。こちらの記事によると、folium.Map関数のオプションのtilesにpngファイル、attrに名称を入れるとFoliumにビルトインされていない地図を背景にできることが分かりました。その記事では国土地理院の地図を使っていました。
今回の目的に合いそうなゲーム画面の地形図に似たタイルが欲しく色々調べていたところ、参考サイトにかなり多くの地形図の素材が落ちていることが分かりました。以下はサイトの画面ですが、画面右の任意の地形図名を選択すると、地形図名に応じた地図が表示されます。
今回Esri.WorldImageryが条件に一番近そうだったので、これを使うことにしました。サイト画面に”Plain JavaScript”の欄があり、
'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'
という記述があったので、pngを付けてtileに設定したところ無事読み込むことができました。
前置きが長くなりましたが変更前後のコードをこちらに示します。attrは適当につけましたが、任意の名前でよさそうです(ただし、つけないとエラーになります)。
方法②による変更前がこちら
- m = folium.Map(location=pref_location,
- tiles='cartodbpositron',zoom_start=8.5)
方法②による変更後
- m =
- folium.Map(location=pref_location,tiles="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}.png",
- attr='hogehoge',
- zoom_start=8.5)
実行した結果が以下になります。
方法②の結果で表示された市区町村の水色の輪郭を消し、地形図のみを保存しました。保存名もworldimagery.pngに変更しました。変更前後のコードと結果を下記に示します。
変更前はこちら
- m = folium.Map(location=pref_location,
- tiles="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}.png",
- attr='hogehoge',
- zoom_start=8.5)
- folium.GeoJson(pref_geojson,
- style_function=lambda x: {'color': 'aqua'}
- ).add_to(m)
- m.save('outline.html') # 追加情報含めてhtmlファイルとして保存
- browser = webdriver.Chrome() # Chromeを立ち上げる
- tmpurl = 'C:/AI/test/wedding_movie/outline.html'
- browser.get(tmpurl) # htmlファイルを開く
- slp(1)
- filename = 'outline.png'
変更後はこちら
- m = folium.Map(location=pref_location,
- tiles="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}.png",
- attr='hogehoge',
- zoom_start=8.5)
- m.save('outline.html') # 追加情報含めてhtmlファイルとして保存
- browser = webdriver.Chrome() # Chromeを立ち上げる
- tmpurl = 'C:/AI/test/wedding_movie/outline.html'
- browser.get(tmpurl) # htmlファイルを開く
- slp(1)
- filename = 'worldimagery.png'
実行結果はこちら
方法②のようなネット上の画像ではなく、自前で用意した画像ファイルを使うこともできます。今回のゲーム画面では、地図情報が解放されていないエリアは濃い青の下地にグリッドが入った背景が使われていますが、これに似せた以下の画像「map_example.jpg」を使ってゲーム画面に近づけることもできました。
方法③による変更前はこちら
- m = folium.Map(location=pref_location,
- tiles="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}.png",
- attr='hogehoge',
- zoom_start=8.5)
- m.save('outline.html') # 追加情報含めてhtmlファイルとして保存
- browser = webdriver.Chrome() # Chromeを立ち上げる
- tmpurl = 'C:/AI/test/wedding_movie/outline.html'
- browser.get(tmpurl) # htmlファイルを開く
- slp(1)
- filename = 'worldimagery.png'
方法③による変更後はこちら
- m = folium.Map(location=pref_location,
- tiles="map_example.jpg",
- attr='hogehoge',
- zoom_start=8.5)
- # 静岡県の市区町村の境界を表示
- folium.GeoJson(pref_geojson,
- style_function=lambda x: {'color': 'aqua'}
- ).add_to(m)
- m.save('outline.html') # 追加情報含めてhtmlファイルとして保存
- browser = webdriver.Chrome() # Chromeを立ち上げる
- tmpurl = 'C:/AI/test/wedding_movie/outline.html'
- browser.get(tmpurl) # htmlファイルを開く
- slp(1)
- filename = 'outline.png'
方法③を実行した結果がこちら
方法③では静岡県の市区町村の境界を表示していましたが、追加で日本地図の各県の境界も表示させ、保存しました。保存名は方法③まで手順を踏んだ場合outline.pngになっているはずです。変更前後のコードと結果を下記に示します。
変更前はこちら
- #背景の表示
- m = folium.Map(location=pref_location,
- tiles="map_example.jpg",
- attr='hogehoge',
- zoom_start=8.5)
- #静岡県の市区町村の境界を表示
- folium.GeoJson(pref_geojson,
- style_function=lambda x: {'color': 'aqua'}
- ).add_to(m)
- m.save('outline.html') # 追加情報含めてhtmlファイルとして保存
変更後はこちら
- #背景の表示
- m = folium.Map(location=pref_location,
- tiles="map_example.jpg",
- attr='hogehoge',
- zoom_start=8.5)
- # 静岡県の市区町村の境界を表示
- folium.GeoJson(pref_geojson,
- style_function=lambda x: {'color': 'aqua'}
- ).add_to(m)
- # 日本地図の県境を表示
- pref_geojson = "japan.geojson" # 日本の各県の境界の緯度経度
- f = open(pref_geojson, 'r', encoding="utf-8_sig")
- pref_geojson = json.load(f) # jsonファイルをpythonオブジェクトとして読み込み
- folium.GeoJson(pref_geojson,
- style_function=lambda x: {'color': 'aqua'}
- ).add_to(m)
- m.save('outline.html') # 追加情報含めてhtmlファイルとして保存
実行した結果がこちらになります。
輪郭設定、背景画像のカスタマイズではfolium.Map()関数とfolium.GeoJson()関数の変更を行ってきました。
ここではfolium.GeoJson()関数を消し、エリア塗りつぶしのためfolium.Choropleth()関数を追加します。また、わかりやすさのためfolium.Map()関数に設定していたタイルをNoneとし、背景を無色にします。
変更前はこちら
- #背景の表示
- m = folium.Map(location=pref_location,
- tiles="map_example.jpg",
- attr='hogehoge',
- zoom_start=8.5)
- # 静岡県の市区町村の境界を表示
- folium.GeoJson(pref_geojson,
- style_function=lambda x: {'color': 'aqua'}
- ).add_to(m)
- # 日本地図の県境を表示
- pref_geojson = "japan.geojson" # 日本の各県の境界の緯度経度
- f = open(pref_geojson, 'r', encoding="utf-8_sig")
- pref_geojson = json.load(f) # jsonファイルをpythonオブジェクトとして読み込み
- folium.GeoJson(pref_geojson,
- style_function=lambda x: {'color': 'aqua'}
- ).add_to(m)
- m.save('outline.html') # 追加情報含めてhtmlファイルとして保存
- browser = webdriver.Chrome() # Chromeを立ち上げる
- tmpurl = 'C:/AI/test/wedding_movie/outline.html'
- browser.get(tmpurl) # htmlファイルを開く
- slp(1)
- filename = 'outline.png'
変更後はこちら
- #背景の表示
- m = folium.Map(location=pref_location,
- tiles=None,
- attr='hogehoge',
- zoom_start=8.5) # "Stamen Terrain",
- folium.Choropleth(geo_data=pref_geojson).add_to(m) #エリア塗りつぶし
- m.save('outline.html')
- browser = webdriver.Chrome() # Chromeを立ち上げる
- tmpurl = 'C:/AI/test/wedding_movie/outline.html'
- browser.get(tmpurl) # htmlファイルを開く
- slp(1)
- filename = 'choropleth.png'
実行した結果がこちらになります。すべての県が塗りつぶされました。
ここでは一つの市町村だけ塗ってみます。市区町村コード一覧のサイトを参考に静岡県の5桁の自治体コード(code)とそれに対応する値(value)のリストを作り、特定のvalue(ここでは静岡市葵区)のみ値を1にします。このリストをcsvファイル(Shizuoka_visited.csv)として保存し、読み込むようにします。
number | city | code | value |
---|---|---|---|
1 | 静岡市 | 22100 | |
2 | 葵区 | 22101 | 1 |
3 | 駿河区 | 22102 | |
4 | 清水区 | 22103 | |
5 | 浜松市 | 22130 | |
6 | 中区 | 22131 | |
7 | 東区 | 22132 | |
8 | 西区 | 22133 | |
9 | 南区 | 22134 | |
10 | 北区 | 22135 | |
11 | 浜北区 | 22136 | |
12 | 天竜区 | 22137 | |
13 | 沼津市 | 22203 | |
14 | 熱海市 | 22205 | |
15 | 三島市 | 22206 | |
16 | 富士宮市 | 22207 | |
17 | 伊東市 | 22208 | |
18 | 島田市 | 22209 | |
19 | 富士市 | 22210 | |
20 | 磐田市 | 22211 | |
21 | 焼津市 | 22212 | |
22 | 掛川市 | 22213 | |
23 | 藤枝市 | 22214 | |
24 | 御殿場市 | 22215 | |
25 | 袋井市 | 22216 | |
26 | 下田市 | 22219 | |
27 | 裾野市 | 22220 | |
28 | 湖西市 | 22221 | |
29 | 伊豆市 | 22222 | |
30 | 御前崎市 | 22223 | |
31 | 菊川市 | 22224 | |
32 | 伊豆の国市 | 22225 | |
33 | 牧之原市 | 22226 | |
34 | 東伊豆町 | 22301 | |
35 | 河津町 | 22302 | |
36 | 南伊豆町 | 22304 | |
37 | 松崎町 | 22305 | |
38 | 西伊豆町 | 22306 | |
39 | 函南町 | 22325 | |
40 | 清水町 | 22341 | |
41 | 長泉町 | 22342 | |
42 | 小山町 | 22344 | |
43 | 吉田町 | 22424 | |
44 | 川根本町 | 22429 | |
45 | 森町 | 22461 |
csvファイルを読み込むための処理をm=folium.Map()関数の直前に追加し、特定の市区町村を特定の色で塗りつぶすためにfolium.Choropleth()関数の引数を追加しました。追加した引数のいくつかについて補足します。
変更前後のコードを下記に示します。ここまでの手順を踏んでいれば、変更後の塗りつぶし画像がchoropleth.pngで保存されるはずです。
変更前がこちら
- pref_geojson = json.load(f) # jsonファイルをpythonオブジェクトとして読み込み
- m = folium.Map(location=pref_location,
- tiles=None,
- attr='hogehoge',
- zoom_start=8.5) # "Stamen Terrain",
- folium.Choropleth(geo_data=pref_geojson).add_to(m) #エリア塗りつぶし
- m.save('outline.html')
変更前がこちら
- pref_geojson = json.load(f) # jsonファイルをpythonオブジェクトとして読み込み
- # 静岡県の自治体コードと各市区群に対応する値のリスト
- pref_data = pd.read_csv('shizuoka_visited.csv', encoding='utf-8_sig')
- pref_data['code'] = pref_data['code'].astype(str)
- print(pref_data[['code', 'value']])
- m = folium.Map(location=pref_location,
- tiles=None,
- attr='hogehoge',
- zoom_start=8.5) # "Stamen Terrain",
- folium.Choropleth(geo_data=pref_geojson,
- data=pref_data,
- columns=["code", "value"],
- key_on="properties.N03_007",
- fill_color="RdPu",
- nan_fill_color='transparent'
- ).add_to(m) # 塗分け
- m.save('outline.html')
実行結果がこちら。葵区だけ塗りつぶされました。