Cartopy调用天地图作为底图
- 2019 年 10 月 4 日
- 筆記
概述
在捍卫祖国领土从每一张地图开始,Python绘制气象实用地图[Code+Data](续)中我们介绍了cartopy这个库,专门用于绘制地图和地理空间信息分析的python库,但是cartopy中的底图都是国外资源,一个是未经审核,另外调用时加载速度也会受影响。本期文章将会介绍如何在cartopy中使用天地图提供的影像,矢量和地形的图层服务。
cartopy调用天地图图层
cartopy自带的底图是有Google、MapQuest、Stamen、Mapbox、Quadtree等多家图层服务,提供影像、矢量和地形图,可以在img_tiles.py文件中查看具体的实现形式。在国内,调用天地图的服务是最具有保证和方便的形式,我们可以通过继承cartopy提供的GoogleWTS类,自定义天地图图层的类,从而使用天地图的服务。
import matplotlib.pyplot as plt import cartopy.io.img_tiles as cimgt import cartopy.crs as ccrs from cartopy.io import shapereader from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER class TDT(cimgt.GoogleWTS): def _image_url(self, tile): x, y, z = tile url = 'http://t3.tianditu.gov.cn/DataServer?T=vec_w&x=%s&y=%s&l=%s&tk=a4ee5c551598a1889adfabff55a5fc27'% (x, y, z) return url class TDT_ter(cimgt.GoogleWTS): def _image_url(self, tile): x, y, z = tile url = 'http://t3.tianditu.gov.cn/DataServer?T=ter_w&x=%s&y=%s&l=%s&tk=a4ee5c551598a1889adfabff55a5fc27'% (x, y, z) return url class TDT_img(cimgt.GoogleWTS): def _image_url(self, tile): x, y, z = tile url = 'http://t3.tianditu.gov.cn/DataServer?T=img_w&x=%s&y=%s&l=%s&tk=a4ee5c551598a1889adfabff55a5fc27'% (x, y, z) return url def make_map(projection=ccrs.PlateCarree()): fig, ax = plt.subplots(figsize=(9, 13), subplot_kw=dict(projection=projection)) gl = ax.gridlines(draw_labels=True) gl.xlabels_top = gl.ylabels_right = False gl.xformatter = LONGITUDE_FORMATTER gl.yformatter = LATITUDE_FORMATTER return fig, ax extent = [115, 118, 39, 41.5] #北京市区图 request = cimgt.TDT() #矢量图层 #request = cimgt.TDT_img() #影像 #request = cimgt.TDT_ter() #地形 fig, ax = make_map(projection=request.crs) ax.set_extent(extent) ax.add_image(request, 10)# level=10 缩放等级 plt.show()

矢量图层

影像图层

地形图层
cartopy以天地图为底图画利奇马台风
前段时间的利奇马台风对我国沿海造成了巨大的破坏,我们从中国台风网[1]爬取了利奇马台风的途径数据,利用catopy,以天地图为底图,对利奇马的路径和风力等级进行展示:爬取利奇马台风的数据:
import requests import json import time import pandas as pd year = "2019" # 输入台风的年份 typhoon_id = "1909" # 输入利奇马台风的编号 1909 url = "http://d1.weather.com.cn/typhoon/typhoon_data/"+year+"/"+typhoon_id+".json?callback=getData&_="+str(int(round((time.time()*1000)))) headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36", "Referer": "http://typhoon.weather.com.cn/gis/typhoon_p.shtml", } r = requests.get(url,headers=headers) a = json.loads(r.text[8:-1])#解析json文件 with open("./test2.json",'w',encoding='utf-8') as json_file: json.dump(a["typhoon"][8],json_file,ensure_ascii=False) #台风轨迹点 start_time = [] #台风到达时间 lon = [] #台风到达地经度 lat = [] #台风到达地纬度 central_pressure = [] #台风中心气压 wind = [] #台风风速风力(米/秒) direction = [] #未来移向 feature_speed = [] #未来移速 for i in range(len(a["typhoon"][8])): start_time.append(a["typhoon"][8][i][1]) lon.append(a["typhoon"][8][i][4]) lat.append(a["typhoon"][8][i][5]) central_pressure.append(a["typhoon"][8][i][6]) wind.append(a["typhoon"][8][i][7]) direction.append(a["typhoon"][8][i][8]) feature_speed.append(a["typhoon"][8][i][9]) typhoon_data=pd.DataFrame({"start_time":start_time,"lon":lon,"lat":lat,"central_pressure":central_pressure, "wind":wind,"direction":direction,"feature_speed":feature_speed}) typhoon_data["start_time"]=pd.to_datetime(typhoon_data["start_time"],format='%Y-%m-%d %H时') typhoon_data.to_csv('%s.csv'%typhoon_id,index=False)
获取利奇马的数据文件1909.csv。对台风路径进行绘图展示:
import matplotlib.pyplot as plt import cartopy.crs as ccrs from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER import cartopy.io.img_tiles as cimgt import cartopy.feature as cfeat from cartopy.io.shapereader import Reader import pandas as pd import os from PIL import Image class TDT(cimgt.GoogleWTS): def _image_url(self, tile): x, y, z = tile url = 'http://t3.tianditu.gov.cn/DataServer?T=vec_w&x=%s&y=%s&l=%s&tk=a4ee5c551598a1889adfabff55a5fc27'% (x, y, z) return url class TDT_ter(cimgt.GoogleWTS): def _image_url(self, tile): x, y, z = tile url = 'http://t3.tianditu.gov.cn/DataServer?T=ter_w&x=%s&y=%s&l=%s&tk=a4ee5c551598a1889adfabff55a5fc27'% (x, y, z) return url class TDT_img(cimgt.GoogleWTS): def _image_url(self, tile): x, y, z = tile url = 'http://t3.tianditu.gov.cn/DataServer?T=img_w&x=%s&y=%s&l=%s&tk=a4ee5c551598a1889adfabff55a5fc27'% (x, y, z) return url def make_map(WTS,level): extent = [72, 135, 15, 54] shp= 'cartopy/shp/Province_9/Province_9.shp' proj = ccrs.PlateCarree() fig, ax = plt.subplots(figsize=(10, 6), subplot_kw=dict(projection=WTS.crs)) gl = ax.gridlines(draw_labels=True) gl.xlabels_top = gl.ylabels_right = False gl.xformatter = LONGITUDE_FORMATTER gl.yformatter = LATITUDE_FORMATTER ax.set_extent(extent) ax.add_image(WTS, level) reader = Reader(shp) provinces = cfeat.ShapelyFeature(reader.geometries(),proj, edgecolor='white', facecolor='none') ax.add_feature(provinces, linewidth=0.5) sub_extent = [105, 125, 0, 25] sub_ax = fig.add_axes([0.66, 0.11, 0.14, 0.155], projection=WTS.crs) sub_ax.set_extent(sub_extent) sub_ax.add_image(WTS, level) sub_ax.add_feature(provinces, linewidth=0.3) return fig,ax,sub_ax if __name__ == "__main__": typhoon=pd.read_csv("1909.csv") typhoon.dropna(subset=['wind'],inplace=True) typhoon['class']='' typhoon.loc[(typhoon['wind']>=10.8) & (typhoon['wind']<=17.1),'class']='TROPICAL DEPRESSION' typhoon.loc[(typhoon['wind']>17.2) & (typhoon['wind']<=24.4),'class']='TROPICAL STORM' typhoon.loc[(typhoon['wind']>24.5) & (typhoon['wind']<=32.6),'class']="SEVERE TROPICAL STORM" typhoon.loc[(typhoon['wind']>32.7) & (typhoon['wind']<=41.4),'class']="TYPHOON" typhoon.loc[(typhoon['wind']>41.5) & (typhoon['wind']<=50.9),'class']="SEVERE TYPHOON" typhoon.loc[(typhoon['wind']>51),'class']="SUPER TYPHOON" color_dict = {'TROPICAL DEPRESSION': '#7fffd4', 'TROPICAL STORM': '#008000', "SEVERE TROPICAL STORM": '#0000ff', "TYPHOON": '#ffff00', "SEVERE TYPHOON": '#ffa500', "SUPER TYPHOON": '#ff4500'} color_dict = {'TROPICAL DEPRESSION': 'lime', 'TROPICAL STORM': 'blue', "SEVERE TROPICAL STORM": 'yellow', "TYPHOON": 'orange', "SEVERE TYPHOON": 'red', "SUPER TYPHOON": 'darkred'} wts = TDT_img() for j in typhoon.index[154::1]: fig,ax,sub_ax=make_map(wts,5) for i in typhoon.index[:j]: a0,b0 = typhoon.loc[i,"lon"], typhoon.loc[i,"lat"] a1,b1 = typhoon.loc[i+1,"lon"], typhoon.loc[i+1,"lat"] ax.plot((a0,a1),(b0,b1), linewidth=2.5, color=color_dict[typhoon.loc[i+1,"class"]], transform=ccrs.Geodetic()) ax.scatter(a0, b0, s=10, c=color_dict[typhoon.loc[i,"class"]], alpha=0.8, transform=ccrs.Geodetic()) sub_ax.plot((a0,a1),(b0,b1), linewidth=2.5, color=color_dict[typhoon.loc[i+1,"class"]], transform=ccrs.Geodetic()) sub_ax.scatter(a0, b0, s=10, c=color_dict[typhoon.loc[i,"class"]], alpha=0.8, transform=ccrs.Geodetic()) ax.annotate('Typhoon: %sn Date: %sn Data: http://typhoon.weather.com.cn/n' % ('Lekima(ID-1909)', "2019-08-04 14:00:00"), xy=(0, 1), xytext=(12, -12), va='top', ha='left',xycoords='axes fraction', textcoords='offset points') # plt.show() plt.savefig("./pic/%d.png"%j) os.chdir("./pic/") imgFiles = [fn for fn in os.listdir('.') if fn.endswith('.png')] imgFiles.sort(key=lambda x:int(x[:-4])) print(imgFiles) images = [Image.open(fn) for fn in imgFiles] im = images[0] filename = 'test.gif' im.save(fp=filename, format='gif', save_all=True, append_images=images[1:], duration=500)

利奇马完整的路径

gif动态图
参考文章: python如何调用天地图来绘制底图[2] 飓风“桑迪”路径图的制作[3] 基于Python的GIS分析之台风路径可视化(三)[4]
References
[1]
中国台风网: http://typhoon.weather.com.cn [2]
python如何调用天地图来绘制底图: http://bbs.06climate.com/forum.php?mod=viewthread&tid=89165 [3]
飓风“桑迪”路径图的制作: https://www.cnblogs.com/vamei/archive/2012/11/07/2758006.html [4]
基于Python的GIS分析之台风路径可视化(三): https://beiyuan.me/tytrack-detailed/