8.16 地理數(shù)據(jù)和 Basemap
原文:Geographic Data with Basemap
譯者:飛龍
協(xié)議:CC BY-NC-SA 4.0
本節(jié)是《Python 數(shù)據(jù)科學(xué)手冊(cè)》(Python Data Science Handbook)的摘錄渔嚷。
數(shù)據(jù)科學(xué)中一種常見的可視化類型是地理數(shù)據(jù)。Matplotlib 用于此類可視化的主要工具是 Basemap 工具包,它是位于mpl_toolkits
命名空間下的幾個(gè) Matplotlib 工具包之一易遣。不可否認(rèn)将鸵,Basemap 使用時(shí)有點(diǎn)笨拙弧械,甚至簡(jiǎn)單的可視化渲染也要花費(fèi)更長(zhǎng)的時(shí)間,超出你的想象嫁怀。
更傳統(tǒng)的解決方案(如 leaflet 或 Google Maps API)可能是更加密集的地圖可視化的更好選擇。盡管如此借浊,Basemap 仍然是 Python 用戶在其虛擬工具欄中擁有的有用工具塘淑。在本節(jié)中,我們將展示使用此工具包可以實(shí)現(xiàn)的地圖可視化類型的幾個(gè)示例蚂斤。
Basemap 的安裝很簡(jiǎn)單存捺;如果你正在使用 conda,你可以輸入這個(gè)橡淆,然后下載包:
$ conda install basemap
我們只在標(biāo)準(zhǔn)樣板中添加一個(gè)新的導(dǎo)入:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
一旦安裝并導(dǎo)入了 Basemap 工具包召噩,地理繪圖就能在幾行之內(nèi)實(shí)現(xiàn)(下面的圖形也需要 Python 2 中的PIL
包母赵,或者 Python 3 中的pillow
包):
plt.figure(figsize=(8, 8))
m = Basemap(projection='ortho', resolution=None, lat_0=50, lon_0=-100)
m.bluemarble(scale=0.5);
Basemap
參數(shù)的含義將立即討論。
有用的是這里顯示的地球不僅僅是一個(gè)圖像; 它是一個(gè)功能齊全的 Matplotlib 軸域具滴,它可以理解球面坐標(biāo)凹嘲,這使我們可以輕松地在地圖上繪制數(shù)據(jù)!例如构韵,我們可以使用不同的地圖投影周蹭,放大到北美并繪制西雅圖的位置。我們將使用 etopo 圖像(顯示陸地和海底的地形特征)作為地圖背景:
fig = plt.figure(figsize=(8, 8))
m = Basemap(projection='lcc', resolution=None,
width=8E6, height=8E6,
lat_0=45, lon_0=-100,)
m.etopo(scale=0.5, alpha=0.5)
# 將 (long, lat) 映射為 (x, y) 以便繪圖
x, y = m(-122.3, 47.6)
plt.plot(x, y, 'ok', markersize=5)
plt.text(x, y, ' Seattle', fontsize=12);
通過幾行 Python疲恢,你可以輕松了解可能的地理可視化類型凶朗。我們現(xiàn)在將更深入地討論 Basemap 的功能,并提供幾個(gè)可視化地圖數(shù)據(jù)的示例显拳。使用這些簡(jiǎn)短的示例作為積木棚愤,你應(yīng)該能夠創(chuàng)建幾乎任何你想要的地圖可視化。
地圖投影
使用地圖時(shí)要決定的第一件事杂数,是要使用什么投影宛畦。你可能已經(jīng)熟悉這樣一個(gè)事實(shí):不可能將球形地圖(例如地球的地圖)投影到平坦的表面上,而不會(huì)以某種方式扭曲或破壞其連續(xù)性揍移。這些預(yù)測(cè)是在人類歷史進(jìn)程中發(fā)展起來的次和,有很多選擇!取決于地圖投影的預(yù)期用途那伐,有一些地圖特征踏施,保留它們很有用(例如,方向罕邀,區(qū)域畅形,距離,形狀或其他考慮因素)诉探。
Basemap 包實(shí)現(xiàn)了幾十個(gè)這樣的投影束亏,全部由短格式代碼引用。在這里阵具,我們將簡(jiǎn)要介紹一些比較常見的。我們首先定義一個(gè)便利例程來繪制我們的世界地圖以及經(jīng)緯線:
from itertools import chain
def draw_map(m, scale=0.2):
# 繪制陰影浮雕圖像
m.shadedrelief(scale=scale)
# 將 lats 和 lons 作為字典返回
lats = m.drawparallels(np.linspace(-90, 90, 13))
lons = m.drawmeridians(np.linspace(-180, 180, 13))
# 鍵包含 plt.Line2D 實(shí)例
lat_lines = chain(*(tup[1][0] for tup in lats.items()))
lon_lines = chain(*(tup[1][0] for tup in lons.items()))
all_lines = chain(lat_lines, lon_lines)
# 遍歷這些線并設(shè)置所需的樣式
for line in all_lines:
line.set(linestyle='-', alpha=0.3, color='w')
圓柱投影
最簡(jiǎn)單的地圖投影是圓柱投影定铜,其中恒定緯度和經(jīng)度的線分別映射到水平線和垂直線阳液。這種類型的映射很好地代表了赤道區(qū)域,但產(chǎn)生了極點(diǎn)附近的極端扭曲揣炕。緯線的間距在不同的圓柱投影之間變化帘皿,產(chǎn)生不同的保留特征,并且在極點(diǎn)附近的不同的變形畸陡。在下圖中鹰溜,我們展示了等距圓柱投影的示例虽填,它選擇了沿子午線保留距離的緯度縮放。其他圓柱投影是墨卡托(projection='merc'
)和圓柱等積(projection='cea'
)投影曹动。
fig = plt.figure(figsize=(8, 6), edgecolor='w')
m = Basemap(projection='cyl', resolution=None,
llcrnrlat=-90, urcrnrlat=90,
llcrnrlon=-180, urcrnrlon=180, )
draw_map(m)
此視圖的 Basemap 的附加參數(shù)斋日,為所需的地圖指定左下角(llcrnr
)和右上角(urcrnr
)的緯度(lat
)和經(jīng)度(lon
),以度為單位墓陈。
偽圓柱投影
偽圓柱投影放松了子午線(恒定經(jīng)線)保持垂直的要求; 這可以在投影的極點(diǎn)附近提供更好的特性恶守。墨卡托投影(projection ='moll'
)是這方面的一個(gè)常見例子,其中所有經(jīng)線都是橢圓弧贡必。它的構(gòu)造是為了保留地圖上的區(qū)域:盡管兩極附近存在扭曲兔港,但小塊的區(qū)域反映了真實(shí)區(qū)域。其他偽圓柱投影是正弦曲線(projection='sinu'
)和羅賓遜(projection='robin'
)投影仔拟。
fig = plt.figure(figsize=(8, 6), edgecolor='w')
m = Basemap(projection='moll', resolution=None,
lat_0=0, lon_0=0)
draw_map(m)
這里 Basemap 的額外參數(shù)是指所需映射的中心緯度(lat_0
)和經(jīng)度(lon_0
)衫樊。
透視投影
透視投影使用透視點(diǎn)的特定選擇構(gòu)建,類似于你從空間中的特定點(diǎn)拍攝地球(對(duì)于某些投影利花,技術(shù)上點(diǎn)位于地球內(nèi)部?瞥蕖)。一個(gè)常見的例子是正交投影(projection='ortho'
)晋被,它顯示了遠(yuǎn)處的觀察者看到的地球的一側(cè)兑徘。因此,它一次只能顯示全球的一半羡洛。
其他基于視角的投影包括 gnomonic 投影(projection='gnom'
)和立體投影(projection='stere'
)挂脑。這些通常對(duì)于顯示地圖的一小部分最有用。
以下是正交投影的示例:
fig = plt.figure(figsize=(8, 8))
m = Basemap(projection='ortho', resolution=None,
lat_0=50, lon_0=0)
draw_map(m);
圓錐投影
圓錐投影將地圖投影到單個(gè)圓錐上欲侮,然后展開崭闲。這可以產(chǎn)生非常好的局部特性,但是遠(yuǎn)離圓錐焦點(diǎn)的區(qū)域可能變得非常扭曲威蕉。其中一個(gè)例子是 Lambert Conformal 圓錐投影(projection='lcc'
)刁俭,我們之前在北美地圖中看到過。
它將地圖投影到一個(gè)圓錐上韧涨,這個(gè)圓錐的排列方式使得兩個(gè)標(biāo)準(zhǔn)平行線(在 Basemap 中由lat_1
和lat_2
規(guī)定)的距離是良好表示的牍戚,比例在它們之間減小并且在它們之外增加。其他有用的圓錐投影是等距圓錐投影(projection='eqdc'
)和 Albers 等面投影(projection='aea'
)虑粥。圓錐投影如孝,就像透視投影,往往是表示地球中小塊區(qū)域的良好選擇娩贷。
fig = plt.figure(figsize=(8, 8))
m = Basemap(projection='lcc', resolution=None,
lon_0=0, lat_0=50, lat_1=45, lat_2=55,
width=1.6E7, height=1.2E7)
draw_map(m)
其它投影
如果你要對(duì)基于地圖的可視化做很多事情第晰,我建議你了解其他可用的投影,以及它們的屬性,優(yōu)點(diǎn)和缺點(diǎn)茁瘦。
最有可能的是品抽,它們可以在 Basemap 包中找到。
如果你深入研究這個(gè)主題甜熔,你會(huì)發(fā)現(xiàn)一個(gè)令人難以置信的極客的亞文化 geo-viz圆恤,他們?yōu)闊崃覡?zhēng)論做好了準(zhǔn)備,來為任何給定應(yīng)用支持他們最喜歡的投影纺非!
繪制地圖背景
之前我們看過bluemarble()
和shadedrelief()
方法哑了,用于在地圖上投影全球圖像,以及drawparallels()
和drawmeridians()
方法用于繪制恒定經(jīng)緯度的線烧颖。Basemap 包包含一系列有用的函數(shù)弱左,用于繪制物理特征的邊界,例如大陸炕淮,海洋拆火,湖泊和河流等,以及政治邊界涂圆,例如國(guó)家地區(qū)们镜,以及美國(guó)各州和縣。以下是一些可用繪圖功能润歉,你可能希望使用 IPython 幫助特性來探索:
-
物理邊界和水體
-
drawcoastlines()
:繪制大陸海岸線 -
drawlsmask()
:繪制陸地和海洋之間的掩碼模狭,用于在一個(gè)或另一個(gè)上投射圖像 -
drawmapboundary()
:繪制地圖邊界,包括海洋的填充顏色踩衩。 -
drawrivers()
:在地圖上繪制河流 -
fillcontinents()
:用給定的顏色填充大陸嚼鹉;可選擇用另一種顏色填充湖泊
-
-
政治邊界
-
drawcountries()
:繪制國(guó)界 -
drawstates()
:繪制美國(guó)國(guó)界 -
drawcounties()
:繪制美國(guó)縣界
-
-
地圖功能
-
drawgreatcircle()
:在兩點(diǎn)之間繪制大圓圈 -
drawparallels()
:繪制恒定緯度的線條 -
drawmeridians()
:繪制恒定經(jīng)度的線條 -
drawmapscale()
:在地圖上繪制線性刻度
-
-
全球圖像
-
bluemarble()
:將 NASA 的藍(lán)色大理石圖像投影到地圖上 -
shadedrelief()
:將陰影浮雕圖像投影到地圖上 -
etopo()
:在地圖上繪制一個(gè) etopo 浮雕圖像 -
warpimage()
:將用戶提供的圖像投影到地圖上
-
對(duì)于基于邊界的特性,必須在創(chuàng)建 Basemap 圖像時(shí)設(shè)置所需的分辨率驱富。Basemap
類的resolution
參數(shù)設(shè)置邊界中的細(xì)節(jié)級(jí)別锚赤,他們是'c'
(原始),'l'
(低)褐鸥,'i'`(中)线脚,
'h'(高),
'f'``(完整)或None
(如果沒有使用邊界)叫榕。這個(gè)選項(xiàng)很重要:例如浑侥,在全局地圖上設(shè)置高分辨率邊界可能非常慢。
這是繪制陸地/海洋邊界晰绎,以及分辨率參數(shù)的效果的示例锭吨。我們將創(chuàng)建蘇格蘭的美麗的斯凱島的低分辨率和高分辨率地圖。它位于北緯 57.3°寒匙,6.2°W,90,000×120,000 公里的地圖很好顯示它:
fig, ax = plt.subplots(1, 2, figsize=(12, 8))
for i, res in enumerate(['l', 'h']):
m = Basemap(projection='gnom', lat_0=57.3, lon_0=-6.2,
width=90000, height=120000, resolution=res, ax=ax[i])
m.fillcontinents(color="#FFDDCC", lake_color='#DDEEFF')
m.drawmapboundary(fill_color="#DDEEFF")
m.drawcoastlines()
ax[i].set_title("resolution='{0}'".format(res));
請(qǐng)注意,低分辨率海岸線不適合此級(jí)別的縮放锄弱,而高分辨率的工作正常考蕾。然而,低水平對(duì)于全局視圖來說效果會(huì)很好会宪,并且比加載整個(gè)地球的高分辨率邊界數(shù)據(jù)要快得多肖卧!可能需要進(jìn)行一些實(shí)驗(yàn),才能找到給定視圖的正確分辨率參數(shù):最佳路徑是從快速低分辨率的繪圖開始掸鹅,并根據(jù)需要增加分辨率塞帐。
在 Basemap 上繪制數(shù)據(jù)
也許 Basemap 工具包中最有用的部分,是將各種數(shù)據(jù)繪制到地圖背景上的能力巍沙。對(duì)于簡(jiǎn)單的繪圖和文本妖碉,任何plt
函數(shù)都可以在地圖上執(zhí)行另锋;你可以使用Basemap
實(shí)例將緯度和經(jīng)度坐標(biāo)投影到(x, y)
坐標(biāo),用于plt
的繪圖,正如我們?cè)谖餮艌D示例中所見豪诲。
除此之外,還有許多特定于地圖的函數(shù)滑臊,可用作Basemap
實(shí)例的方法蚪拦。這些東西與它們的標(biāo)準(zhǔn) Matplotlib 對(duì)應(yīng)物非常相似,但是有一個(gè)額外的布爾參數(shù)latlon
蠢笋,如果設(shè)置為True
拨齐,它允許你將原始緯度和經(jīng)度傳遞給方法,而不是投影(x, y)
坐標(biāo)昨寞。
其中一些特定于地圖的方法是:
-
contour()/contourf()
:繪制等高線或填充的等高線 -
imshow()
:繪制圖像 -
pcolor()/pcolormesh()
:為不規(guī)則/規(guī)則網(wǎng)格繪制偽彩色圖 -
plot()
:繪制線條和/或標(biāo)記瞻惋。 -
scatter()
:繪制帶標(biāo)記的點(diǎn)。 -
quiver()
:繪制向量编矾。 -
barbs()
:繪制風(fēng)向熟史。 -
drawgreatcircle()
:繪制大圓圈。
我們將繼續(xù)并看到其中一些例子窄俏。這些函數(shù)的更多信息蹂匹,包括幾個(gè)示例圖,請(qǐng)參閱在線 Basemap 文檔凹蜈。
示例:加利福尼亞的城市
回想一下限寞,在“自定義圖例”中,我們演示了在散點(diǎn)圖中使用大小和顏色仰坦,來傳達(dá)加州城市的位置履植,大小和人口的信息。在這里悄晃,我們將再次創(chuàng)建此繪圖玫霎,但使用 Basemap 將數(shù)據(jù)放在上下文中凿滤。
我們開始加載數(shù)據(jù),就像我們之前做的那樣:
import pandas as pd
cities = pd.read_csv('data/california_cities.csv')
# 提取我們感興趣的數(shù)據(jù)
lat = cities['latd'].values
lon = cities['longd'].values
population = cities['population_total'].values
area = cities['area_total_km2'].values
接下來庶近,我們?cè)O(shè)置地圖投影翁脆,繪制數(shù)據(jù)的散點(diǎn)圖,然后創(chuàng)建顏色條和圖例:
# 1. 繪制地圖北京
fig = plt.figure(figsize=(8, 8))
m = Basemap(projection='lcc', resolution='h',
lat_0=37.5, lon_0=-119,
width=1E6, height=1.2E6)
m.shadedrelief()
m.drawcoastlines(color='gray')
m.drawcountries(color='gray')
m.drawstates(color='gray')
# 2. 繪制城市數(shù)據(jù)的散點(diǎn)圖鼻种,其中顏色反映人口
# 尺寸反映面積
m.scatter(lon, lat, latlon=True,
c=np.log10(population), s=area,
cmap='Reds', alpha=0.5)
# 3. 創(chuàng)建顏色條和圖例
plt.colorbar(label=r'$\log_{10}({\rm population})$')
plt.clim(3, 7)
# 使用虛擬的點(diǎn)生成圖例
for a in [100, 300, 500]:
plt.scatter([], [], c='k', alpha=0.5, s=a,
label=str(a) + ' km$^2$')
plt.legend(scatterpoints=1, frameon=False,
labelspacing=1, loc='lower left');
這向我們展示了大量人口在加利福尼亞定居的地方:它們聚集在洛杉磯和舊金山地區(qū)的海岸附近反番,沿著平坦的中央山谷中的高速公路延伸,幾乎完全避開了沿著國(guó)界的山區(qū)叉钥。
示例:表面溫度數(shù)據(jù)
作為一些更連續(xù)的地理數(shù)據(jù)的可視化示例罢缸,讓我們考慮一下 2014 年 1 月襲擊美國(guó)東部的“極渦”。任何氣候數(shù)據(jù)的重要來源是美國(guó)宇航局 Goddard 空間研究所投队。這里我們將使用 GIS 250 溫度數(shù)據(jù)枫疆,我們可以使用 shell 命令下載(在 Windows 機(jī)器上,這些命令可能必須修改)蛾洛。此處使用的數(shù)據(jù)下載于 2016 年 6 月 12 日养铸,文件大小約為 9MB:
# !curl -O http://data.giss.nasa.gov/pub/gistemp/gistemp250.nc.gz
# !gunzip gistemp250.nc.gz
數(shù)據(jù)采用NetCDF格式,可以通過netCDF4
庫(kù)在 Python 中讀取轧膘。你可以像此處所示安裝此庫(kù):
$ conda install netcdf4
我們這樣讀取數(shù)據(jù):
from netCDF4 import Dataset
data = Dataset('gistemp250.nc')
該文件包含不同日期的許多全球溫度讀數(shù)钞螟;我們需要選擇我們感興趣的日期的索引 - 這里是 2014 年 1 月 15 日:
from netCDF4 import date2index
from datetime import datetime
timeindex = date2index(datetime(2014, 1, 15),
data.variables['time'])
現(xiàn)在我們可以加載經(jīng)緯度數(shù)據(jù),以及這個(gè)索引的溫度異常:
lat = data.variables['lat'][:]
lon = data.variables['lon'][:]
lon, lat = np.meshgrid(lon, lat)
temp_anomaly = data.variables['tempanomaly'][timeindex]
最后谎碍,我們將使用pcolormesh()
方法繪制數(shù)據(jù)的顏色網(wǎng)格鳞滨。我們將看看北美,并在背景中使用陰影浮雕地圖蟆淀。請(qǐng)注意拯啦,對(duì)于此數(shù)據(jù),我們專門選擇了一個(gè)離散顏色表熔任,其中零處為中性色褒链,負(fù)值和正值為兩個(gè)對(duì)比色。我們還會(huì)在顏色上輕輕劃出海岸線以供參考:
fig = plt.figure(figsize=(10, 8))
m = Basemap(projection='lcc', resolution='c',
width=8E6, height=8E6,
lat_0=45, lon_0=-100,)
m.shadedrelief(scale=0.5)
m.pcolormesh(lon, lat, temp_anomaly,
latlon=True, cmap='RdBu_r')
plt.clim(-8, 8)
m.drawcoastlines(color='lightgray')
plt.title('January 2014 Temperature Anomaly')
plt.colorbar(label='temperature anomaly (°C)');
該數(shù)據(jù)描繪了該月發(fā)生的局部極端溫度異常疑苔。美國(guó)東部比正常情況要冷得多甫匹,而西部和阿拉斯加的溫度要高得多。沒有記錄溫度的區(qū)域顯示地圖背景惦费。