爱气象,爱气象家园! 

气象家园

 找回密码
 立即注册

QQ登录

只需一步,快速开始

新浪微博登陆

只需一步, 快速开始

搜索
查看: 16289|回复: 2

[源代码] 吐槽 Basemap保存的带省界的svg格式图片出错(溢出),用自己生成的省边界数据画图

[复制链接]

新浪微博达人勋

发表于 2019-6-24 16:07:10 | 显示全部楼层 |阅读模式

登录后查看更多精彩内容~

您需要 登录 才可以下载或查看,没有帐号?立即注册 新浪微博登陆

x
CSDN上的链接为:https://blog.csdn.net/weixin_43718675/article/details/93473389

这个题目取得有点莫名其妙,这是因为我在使用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
```


                               
登录/注册后可看大图


参数内容如图


说了半天,没有解决省界溢出问题。下面进行介绍

2. 可控省界绘制
在这之前,先提取各个省界的经纬度位置信息,形成文件。具体过程可以参考我的上一篇博客
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)

```


                               
登录/注册后可看大图


如上图


大功告成啦!希望能帮到大家






密码修改失败请联系微信:mofangbao

新浪微博达人勋

发表于 2019-6-24 17:36:48 | 显示全部楼层
矢量图互转本来就是坑,与其控制画图不如选个好的post工具链
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

新浪微博达人勋

 楼主| 发表于 2019-6-24 20:07:38 | 显示全部楼层
Lancelot 发表于 2019-6-24 17:36
矢量图互转本来就是坑,与其控制画图不如选个好的post工具链

请问,什么是post工具链啊
密码修改失败请联系微信:mofangbao
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册 新浪微博登陆

本版积分规则

Copyright ©2011-2014 bbs.06climate.com All Rights Reserved.  Powered by Discuz! (京ICP-10201084)

本站信息均由会员发表,不代表气象家园立场,禁止在本站发表与国家法律相抵触言论

快速回复 返回顶部 返回列表