這個題目取得有點莫名其妙,這是因爲我在使用Python的Basemap畫自帶省界的地圖時候,保存svg格式圖片時容易產生區域溢出誤差。回頭我會舉個例子。
- Basemap畫省界
各個軟件畫省界還是挺容易的,但是想單獨畫出某個省界還是不容易的。
1.1 默認畫省界法(畫出全部省界)
#先導入一些必要的包和庫
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib.patches import Polygon
import os
#設置目標區域經緯度範圍
lat_min,lat_max=37,55
lon_min,lon_max=115,135
#設置圖片大小
fig = plt.figure(figsize=(18,10))
ax1 = fig.add_axes([0.1,0.1,0.8,0.8]
m=Basemap(projection='cyl',llcrnrlat=lat_min,llcrnrlon=lon_min,
urcrnrlat=lat_max,urcrnrlon=lon_max,resolution='l',ax=ax1) #設置投影方式和經緯度範圍
後面注意了:
#讀取省界數據。下載省界數據鏈接爲:https://blog.csdn.net/weixin_36677127/article/details/83314583
m.readshapefile('gadm36_CHN_shp/gadm36_CHN_1', 'states',
drawbounds = True,linewidth=1) #設置線寬,name='states',表示獲取省界;
#尤其需要注意,drawbounds=True是默認畫出省界數據參數
m.drawcoastlines() #畫出海岸線
m.drawcountries() #畫出國界線
#需要注意,如果plt.show()在save之前的話,save的結果將是一片空白
plt.savefig('test1.svg') #保存爲.svg格式
plt.savefig('test1.pdf') #保存爲.pdf格式
plt.show()
在console上輸出結果如上,看上去基本上沒有什麼問題
但是,如果沒有打開pdf和svg格式來看的話,就發現問題了
左圖是生成的pdf文件,看上去很正常。右圖是保存的.svg格式文件,卻長得奇形怪狀的,在我們設定的地圖範圍外,也畫出了省界線,這根本不符合我們的要求。
爲什麼會這樣呢,因爲Basemap畫省界時,如果設置了drawbounds=True,則會將所有省界都畫出來。只是在一些格式中隱藏了起來。
如,將圖片保存爲標量圖如jpg.png等格式,顯示是正常的。
但是在科研論文圖中,常常需要我們將其保存爲矢量圖格式,常用的矢量圖格式爲.eps .pdf. .svg ,其中前兩者不能直接插入到文檔裏面去(需要藉助一些轉化工具如 Adobe illustrator /ghost View等(需要安裝包的可以聯繫我)。.svg格式可以直接插入到文檔裏面去,會顯得比較方便。
那像上面這種情況怎麼辦呢,如果想天真的通過先生成pdf或者eps格式,再通過AI等工具轉化成.svg,那其實也會出錯的,雖然原pdf沒問題,但是轉化出來的.svg圖片還是有溢出邊界範圍的省界錯誤。
這個時候我們希望能Basemap畫省界過程是可控的,即能夠知道省界是怎麼畫的,
1.2 單獨控制畫出指定省界
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib.patches import Polygon
import os
#設置目標區域經緯度範圍
lat_min,lat_max=37,55
lon_min,lon_max=115,135
#設置圖片大小
fig = plt.figure(figsize=(18,10))
ax1 = fig.add_axes([0.1,0.1,0.8,0.8]
m=Basemap(projection='cyl',llcrnrlat=lat_min,llcrnrlon=lon_min,
urcrnrlat=lat_max,urcrnrlon=lon_max,resolution='l',ax=ax1) #設置投影方式和經緯度範圍
#讀取省界數據。下載省界數據鏈接爲:https://blog.csdn.net/weixin_36677127/article/details/83314583
m.readshapefile('gadm36_CHN_shp/gadm36_CHN_1', 'states',
drawbounds = False, linewidth=1) #設置線寬,name='states',表示獲取省界;
#尤其需要注意,drawbounds=False是默認不畫出省界數據,但是可以傳入相關參數
name_list=['Liaoning', 'Jilin', 'Nei Mongol'] #設置畫出遼寧、吉林和內蒙古省界
for info, shp in zip(m.states_info, m.states):
proid = info['NAME_1'] #獲取省界名稱
if proid in name_list:
poly = Polygon(shp,facecolor=‘ b’ ,edgecolor='k', lw=1) #facecolor設置填充省界區域內的顏色,edgecolor是設置省界顏色
ax1.add_patch(poly)
m.drawcoastlines() #畫出海岸線
m.drawcountries() #畫出國界線
#需要注意,如果plt.show()在save之前的話,save的結果將是一片空白
plt.savefig('test1.svg') #保存爲.svg格式
plt.savefig('test1.pdf') #保存爲.pdf格式
plt.show()
結果如圖上圖
下圖爲保存的pdf 和svg格式文件
的確能畫出省界,但是如果超出指定區域範圍的其他地方的省界,也默認安排上了,這也不是我們想要的。
除此之外,還有一個問題,
poly = Polygon(shp,facecolor=‘ b’ ,edgecolor='k', lw=1) #facecolor設置填充省界區域內的顏色,edgecolor是設置省界顏色,
這裏設置facecolor後,即使你設置顏色 facecolor=‘w’,後面在地圖上疊加其他數據時候,會出現問題
比如我要畫等高圖,就會這樣。
這個時候需要設置一個參數
poly = Polygon(shp,facecolor=None,edgecolor='k', lw=1,fill=False) #即設置fill=False,即表示閉環內不給與填充,可以允許後面進行相關疊加
正確結果如下:
1.3 參數介紹
在1.2 裏面,使用了m.states和m.states_info參數,這些參數裏面的意思爲:
states=m.states
states_info=m.states_info
參數內容如圖
說了半天,沒有解決省界溢出問題。下面進行介紹
- 可控省界繪製
在這之前,先提取各個省界的經緯度位置信息,形成文件。具體過程可以參考我的上一篇博客
https://blog.csdn.net/weixin_43718675/article/details/93410875
最後形成這樣一個內容的hdf文件。
import h5py
import numpy as np
import os
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
f=h5py.File('province_boundary_lon_lat.hdf') #打開.hdf文件
#設置地圖經緯度範圍
lat_min,lat_max=37,55
lon_min,lon_max=115,135
fig = plt.figure(figsize=(18,10))
ax1 = fig.add_axes([0.1,0.1,0.8,0.8])
m=Basemap(projection='cyl',llcrnrlat=lat_min,llcrnrlon=lon_min,
urcrnrlat=lat_max,urcrnrlon=lon_max,resolution='l',ax=ax1)
aim_province=['Nei Mongol','Hebei','Beijing','Liaoning','Jilin','Heilongjiang'] #設置想要畫邊界的省的名稱
for name in aim_province[0:]:
#得到對應省份的邊界經緯度數據
lat=f[name]['latitude'][:]
lon=f[name]['longitude'][:]
#因爲有些省界對應的經緯度數據不在給定的地圖範圍內,因此將這些點進行剔除
index=np.where(((lat>=lat_min) & (lat<=lat_max)) & ((lon>=lon_min) & (lon<=lon_max)))
#獲取只在給定範圍內的省邊界經緯度數據
valid_lat=lat[index]
valid_lon=lon[index]
# m.plot(valid_lon,valid_lat,'-k')
m.plot(valid_lon,valid_lat,'k.',markersize=0.3) #因爲有些省份的邊界除了閉環之外,還有幾個小島,比如遼寧省,所以用.而不是-來表示
#不過缺點是這樣畫下來的圖片比較大,佔內存。
#建議對界內的省份,使用1.2節方法,對越界省份的邊界,使用截斷描點的方法
m.drawcoastlines()
m.drawcountries()
#設置座標刻度
x_grid=np.arange(lon_min,lon_max+1,5)
y_grid=np.arange(lat_min,lat_max+1,5)
m.drawparallels(y_grid)
m.drawmeridians(x_grid)
plt.xticks(x_grid,x_grid,fontsize=16)
plt.yticks(y_grid,y_grid,fontsize=16)
plt.xlabel('longitude:°E',fontsize=20)
plt.ylabel('latitude:°N',fontsize=20)
plt.savefig('test2.svg')
plt.savefig('test2.pdf')
plt.show()
這個時候來看看保存下來的pdf和svg格式的圖片
可以看到,即便是最難搞的svg格式圖片,也並沒有出現越界現象
通過下面程序可以完整的讀取各個省份的邊界數據
f=h5py.File('province_boundary_lon_lat.hdf')
province_names=[]
province_boundary=[]
for key in f.keys():
province_name=f[key].name
print(province_name)
lat=f[province_name]['latitude'][:].reshape(-1,1)
lon=f[province_name]['longitude'][:].reshape(-1,1)
loc=np.concatenate((lon,lat),axis=1)
province_names.append(province_name[1:])
province_boundary.append(loc)
如上圖
- Polygon和截斷結合
大功告成啦!希望能幫到大家
import h5py
import numpy as np
import os
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib.patches import Polygon
lat_min,lat_max=37,55
lon_min,lon_max=115,135
fig = plt.figure(figsize=(18,10))
ax1 = fig.add_axes([0.1,0.1,0.8,0.8])
m=Basemap(projection='cyl',llcrnrlat=lat_min,llcrnrlon=lon_min,
urcrnrlat=lat_max,urcrnrlon=lon_max,resolution='l',ax=ax1)
m.readshapefile('gadm36_CHN_shp/gadm36_CHN_1','states',drawbounds=False)
#在地圖範圍內的省份,省界直接使用Polygon畫,這樣可以減少圖片大小
name_list=['Liaoning', 'Jilin','Heilongjiang','Beijing']
for info, shp in zip(m.states_info, m.states):
proid = info['NAME_1']
if proid in name_list:
poly = Polygon(shp,facecolor=None,edgecolor='k', lw=1,fill=False)
ax1.add_patch(poly)
f=h5py.File('province_boundary_lon_lat.hdf')
##越過地圖範圍的省的省界用截斷方式描述
aim_province=['Nei Mongol','Hebei']
for name in aim_province[0:]:
lat=f[name]['latitude'][:]
lon=f[name]['longitude'][:]
#因爲有些省界對應的經緯度數據不在給定的地圖範圍內,因此將這些點進行剔除
index=np.where(((lat>=lat_min) & (lat<=lat_max)) & ((lon>=lon_min) & (lon<=lon_max)))
#獲取只在給定範圍內的省邊界經緯度數據
valid_lat=lat[index]
valid_lon=lon[index]
# m.plot(valid_lon,valid_lat,'-k')
m.plot(valid_lon,valid_lat,'k.',markersize=0.5)
m.drawcoastlines()
m.drawcountries()
#設置座標刻度
x_grid=np.arange(lon_min,lon_max+1,5)
y_grid=np.arange(lat_min,lat_max+1,5)
m.drawparallels(y_grid)
m.drawmeridians(x_grid)
plt.xticks(x_grid,x_grid,fontsize=16)
plt.yticks(y_grid,y_grid,fontsize=16)
plt.xlabel('longitude:°E',fontsize=20)
plt.ylabel('latitude:°N',fontsize=20)