吐槽 Basemap保存的帶省界的svg格式圖片出錯(溢出),寧願自帶省界數據畫地圖

這個題目取得有點莫名其妙,這是因爲我在使用Python的Basemap畫自帶省界的地圖時候,保存svg格式圖片時容易產生區域溢出誤差。回頭我會舉個例子。

  1. 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

在這裏插入圖片描述
參數內容如圖

說了半天,沒有解決省界溢出問題。下面進行介紹

  1. 可控省界繪製
    在這之前,先提取各個省界的經緯度位置信息,形成文件。具體過程可以參考我的上一篇博客
    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)

在這裏插入圖片描述
如上圖

  1. 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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章