使用python的bokeh包及geojson數據繪制地圖


最近要繪制倫敦區地圖,查閱了很多資料后最終選擇使用bokeh包以及倫敦區的geojson數據繪制。
bokeh是基於python的繪圖工具,可以繪制各種類型的圖表,支持geojson數據的讀取及繪制地圖。

安裝bokeh

$ pip install bokeh

軟件版本

python-3.7.7
bokeh-2.0.0

數據來源

倫敦地圖數據來源於Highmaps地圖數據集。下載的是英國的地圖數據united-kindom.geo.json。需要對得到的數據進行預處理才能得到只含倫敦地區的數據。這需要對geojson數據的格式有一定的了解。在對數據進行處理之前,先看如何繪制英國地圖。

繪制英國地圖

from bokeh.plotting import curdoc, figure
from bokeh.models import GeoJSONDataSource

# 讀入英國地圖數據並傳給GeoJSONDataSource
with open("united-kindom.geo.json", encoding="utf8") as f:
    geo_source = GeoJSONDataSource(geojson=f.read())
# 設置一張畫布
p = figure(width=500, height=500)
# 使用patches函數以及geo_source繪制地圖
p.patches(xs='xs', ys='ys', source=geo_source)

curdoc().add_root(p)

上述代碼可以繪制出英國地圖。將上述代碼保存為test.py,在終端運行

$ bokeh serve --show test.py

這會自動打開瀏覽器,並顯示英國地圖。
運行結果如圖:
英國地圖

獲取倫敦地區數據

獲取倫敦地區數據可以手動從united-kingdom.geo.json文件中篩選出倫敦的數據,也可以先用python先把數據過濾一遍,然后將數據傳給bokeh。這需要對geojson文件格式有一定的了解,在此不詳細介紹。

from bokeh.plotting import curdoc, figure
from bokeh.models import GeoJSONDataSource
import json

# 用json庫讀取數據
with open("united-kindom.geo.json", encoding="utf8") as f:
    data = json.loads(f.read())
# 判斷是不是倫敦地區數據
def isInLondon(district):
    if 'type' in district['properties'] and 'london borough' in district['properties']['type'].lower():
        return True
    if 'type-en' in district['properties'] and 'london borough' in district['properties']['type'].lower():
        return True
    if 'woe-name' in district['properties'] and 'city of london' in district['properties']['woe-name'].lower():
        return True
    return False
# 過濾數據
data['features'] = list(filter(isInLondon, data['features']))
#
geo_source = GeoJSONDataSource(geojson=json.dumps(data))
p = figure(width=500, height=500)
p.patches(xs='xs', ys='ys', source=geo_source)

curdoc().add_root(p)

運行結果如圖:
london

美化

上面的倫敦地圖只是一個大概的輪廓,下面對地圖添加一系列功能。

添加各區輪廓線

p.patches(xs='xs', ys='ys', fill_alpha=0.7, # 畫輪廓線
        line_color='white',  # 線的顏色
        line_width=0.5,      # 線的寬度
        source=geo_source)

現在地圖區域輪廓很清晰。

添加顏色

# 為每一個地區增加一個color屬性
for i in range(len(data['features'])):
    data['features'][i]['properties']['color'] = ['blue', 'red', 'yellow', 'orange', 'gray', 'purple'][i % 6]
p.patches(xs='xs', ys='ys', fill_alpha=0.7,
        line_color='white',
        line_width=0.5,
        color="color",   # 增加顏色屬性,這里的"color"對應每個地區的color屬性
        source=geo_source)

現在地圖五顏六色。

增加圖注

import random
# 隨機產生數據用於展示
for i in range(len(data['features'])):
    data['features'][i]['properties']['number'] = random.randint(0, 20_000)
p = figure(width=500, height=500,
        tooltips="@name, number: @number"  # 使用tooltips生成圖注,@+屬性名稱,這里的name是數據中原本有的,number是新近添加的。
    )

現在鼠標放到區域上時,會顯示"區域名, number: 數字"。

去掉坐標軸與背景線

p.axis.axis_label = None
p.axis.visible = False
p.grid.grid_line_color = None

最終代碼

from bokeh.plotting import curdoc, figure
from bokeh.models import GeoJSONDataSource
import json
import random
with open("united-kindom.geo.json", encoding="utf8") as f:
    data = json.loads(f.read())

def isInLondon(district):
    if 'type' in district['properties'] and 'london borough' in district['properties']['type'].lower():
        return True
    if 'type-en' in district['properties'] and 'london borough' in district['properties']['type'].lower():
        return True
    if 'woe-name' in district['properties'] and 'city of london' in district['properties']['woe-name'].lower():
        return True
    return False

data['features'] = list(filter(isInLondon, data['features']))
for i in range(len(data['features'])):
    data['features'][i]['properties']['color'] = ['blue', 'red', 'yellow', 'orange', 'gray', 'purple'][i % 6]
    data['features'][i]['properties']['number'] = random.randint(0, 20_000)

geo_source = GeoJSONDataSource(geojson=json.dumps(data))
p = figure(width=500, height=500,
        tooltips="@name, number: @number")
p.patches(xs='xs', ys='ys', fill_alpha=0.7,
        line_color='white',
        line_width=0.5,
        color="color",
        source=geo_source)

p.axis.axis_label = None
p.axis.visible = False
p.grid.grid_line_color = None

curdoc().add_root(p)

倫敦地圖完成了

london final

總結

  1. 最開始想用pyecharts做的,但是pyecharts並沒有倫敦的地圖。折騰半天,最后只好自己找geojson數據來畫地圖。
  2. 找到了很多關於地圖的數據和工具,比如上文中提到的highmap數據集,以及DataV.altas,這個工具可以可視化地提取中國區域的地圖數據,但感覺比起自己找數據,畫中國地圖還是pyecharts來得實在。
  3. 數據最重要。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM