使用python 绘制地图并在地图上绘制坐标点
文章目录
1. 引言
本来想详细写一写Basemap的详细使用,但是看到了已经有人写得非常详细,如果想使用Basemap 的同学强烈推荐这篇文章我用Python之basemap画图27问 ,本文就从挑选其中一方面详细介绍一下,并对其中未提到的一些细节补充一下。
本文将详细介绍如何使用Python (Basemap库函数)绘制一张美观的测站分布图(解决了plt.text()文字重叠的问题,以及标签的设置),先上最终结果图,分布图包含了测站的名称和接收机类型。首先上最终效果图:
关于Basemap的安装,教程很多不再赘述,自行百度,传送门傻瓜式教学:window下安装basemap。关于Basemap的使用,必须再次推荐一下篇文章:我用Python之basemap画图27问
2.绘制一张简单的地图
绘制地图,确定地图的经纬度范围,添加海岸线、经纬线,添加国家线、颜色设置,透明度设置。
from mpl_toolkits.basemap import Basemap #导入Basemap库
import matplotlib.pyplot as plt
#import pandas as pd
#styles = plt.style.available
#plt.style.use(['science', 'high-vis', 'no-latex'])
plt.rcParams['font.family'] = 'Arial' # 设置字体样式
plt.rcParams['font.weight'] = 'bold' #加粗
#绘制地图 加入站点
plt.figure(figsize=(40, 30)) #它的参数figsize=(16,8)定义了图的大小。
m = Basemap() #使用Basemap()创建一个地图
# 限定地图的范围
"""m = Basemap(llcrnrlon= lons[0],
llcrnrlat=lats[0],
urcrnrlon=lons[1],
urcrnrlat=lats[1],
projection='merc',
lat_1=33,
lat_2=45,
lon_0=100) #使用Basemap()创建一个地图
"""
m.drawcoastlines() #把海岸线画上
m.drawcountries(color='grey', linewidth=2) # 开始画上国家
# 填充陆地、胡泊、海洋的颜色
m.fillcontinents(
color='g', # 陆地颜色
lake_color='b', # 湖泊颜色
alpha=0.2)
#m.drawmapboundary(fill_color='blue') # 填充海洋
# 添加经纬线
m.drawmeridians(
np.arange(0, 360, 30),#设置纬线的其实范围,以及维度的间隔
color='pink', # 颜色
linewidth=8, # 线宽
labels=[1, True, 0, True],
fontsize=50,
)
m.drawparallels(
np.arange(-90, 90, 30),
color='green', # 颜色
linewidth=8, # 线宽
labels=[1, True, 0, 1],
fontsize=50,
)
效果如如下:
3.测站坐标数据读取
import pandas as pd
from collections import OrderedDict
from adjustText import adjust_text
styles = plt.style.available
plt.style.use(['science', 'high-vis', 'no-latex'])
filepath = r'F:\MP Summary\B1IB2IB3I.csv'
filepath = r'F:\MP Summary\B1CB2aB2b.csv'
fpath = r'F:\Thesis\Results\GPS\endoutput\statistic.txt'
#fpath = r'F:\Thesis\Results\TRIMBLE_GPS\endoutput\statistic.txt'
data = pd.read_csv(filepath, )
d = data.set_index(data['staname'])
sta = list(d['staname'])
rectype = list(d['RecType'])
tp = list(d.drop_duplicates(subset=['RecType'], keep='first')['RecType'])
stainfo = {sta[i]: rectype[i] for i in range(len(sta))}
colors = [
'blue', 'r', 'black', 'm', 'darkolivegreen', 'purple', 'dodgerblue',
'brown'
]
rec_color = {tp[i]: colors[i] for i in range(len(tp))}
colormap = {sta[i]: rec_color[stainfo[sta[i]]] for i in range(len(sta))} #不同接收机类型映射不同的颜色
StationPos = pd.read_table(fpath,
sep=r'\s+',
skiprows=0,
engine='python',
header=None)
StationPos = pd.DataFrame(StationPos).loc[:, (0, 2, 3)].drop_duplicates()
StationPos.columns = ['StaName', 'latitude', 'longitude']
StationPos.index = range(0, len(StationPos.index))
StationPos = StationPos.drop_duplicates(subset=['StaName'], keep='first')
StationPos = StationPos[StationPos['StaName'].isin(sta)]
#StationPos = StationPos.loc[0:(StaNum-1),:];
#-----------需要绘制的站点的经纬度信息测站名称----------#
lon = list(StationPos.longitude)
#on = StationPos.longitude;
lat = list(StationPos.latitude)
#at = StationPos.latitude;
staname = list(StationPos.StaName)
print('lon:{}\n lat:{}\n staname:{}\n'.format(lon,lat,staname))
lon:[149.0, 74.7, -52.8, 21.0, -4.4, 5.1, -4.0, -15.6, -104.0, -68.8,
-69.4, 116.2, -46.0, -22.0, -120.9, 144.9, -76.8, 14.8, 141.8, -147.5, -156.3, 39.6, -66.6, 148.3, -149.6, -108.1, -114.5, 114.4, 12.9, 87.6, -65.4, 107.1, 6.4, -52.7, 55.5, -67.8, -67.8, 121.0, 13.1, 170.5, 11.9, 141.1, 24.4, 45.3, -57.9, -61.0, 12.4, 70.3, -52.6, 21.0, 114.5, 14.8, 127.9, -45.0, -4.5, -71.5, -149.6, 4.4, 66.9, -28.0, 20.3, 21.1, 120.2, 118.4]
lat:[-35.3, 42.7, 5.3, 67.9, 40.5, 50.0, 40.4, 27.8, 30.7, 76.5, -35.8, -31.0, 60.7, 70.5, 40.0, 13.6, 39.0, 49.9, 43.5, 65.0, 20.7, -69.0, 46.0, -33.0, -17.6, 34.3, 62.5, 30.5, 49.1, 43.8, -24.7, 47.9, 51.0, 47.6, -4.7, -53.8, -53.8, 14.5, 52.4, -45.9, 78.9, 39.1, 60.2, -12.8, -34.9, 14.6, 51.4, -49.4, 5.1, 52.1, 30.5, 49.9, 35.6, -22.7, 48.4, -16.5, -17.6, 50.8, 39.1, 39.1, 49.0, 67.9, 23.0, 24.5]
staname:['str2', 'pol2', 'kour', 'kiru', 'cebr', 'redu', 'vill', 'mas1', 'mgo5', 'thu2', 'mgue', 'nnor', 'qaq1', 'scor', 'quin', 'guam', 'godn', 'gop6', 'stk2', 'gcgo', 'mao0', 'syog', 'unb3', 'park', 'faa1', 'pie1', 'yel2', 'wuh2', 'wtzs', 'urum', 'unsa', 'ulab', 'tit2', 'stj3', 'seyg', 'rio2', 'rgdg', 'ptgg', 'pots', 'ous2', 'nya2', 'mizu', 'metg', 'mayg', 'lpgs', 'lmmf', 'leij', 'krgg', 'koug', 'joze', 'jfng', 'gope', 'gamg', 'chpg', 'brst', 'areg', 'thtg', 'brux', 'kit3', 'enao', 'ganp', 'kir8', 'cksv', 'kmnm'] ```
4.在地图上绘制点
#----------在地图上绘制坐标点,添加文字------------#
for i in range(len(staname)):
m.scatter(lon[i],
lat[i],
s=150,
c=colormap[staname[i]],
marker='o',
label=stainfo[staname[i]])
#此处可以直接使用 m.scatter(lon,lat)
#---------在坐标点添加文字------#
plt.text(lon, #坐标
lat,
staname, #文字字符串
fontsize=40,
style="italic",
weight="light",
verticalalignment='center',
horizontalalignment='right',
rotation=0)) #给散点加标签
绘制的地图存在着两个问题:
- legend标签重复
- 文字重叠
5. adjustText解决plt.text()文字重叠的问题
文字重叠解决起来非常麻烦,但是谁让它遇到的是除了生小孩无所不能的python 呢。python为我们提供了一个库函数很好的解决了plt.text()文字重叠的问题。
关于这个库函数的更多使用细节请看:
adjustText解决matplotlib plt.text()文字重叠问题
from collections import OrderedDict
from adjustText import adjust_text #导入文字调整的库函数
texts = []
#plt.legend(loc ='best')
for i in range(len(staname)):
texts.append(
plt.text(lon[i],
lat[i],
staname[i],
fontsize=40,
color=colormap[staname[i]],
style="italic",
weight="light",
verticalalignment='center',
horizontalalignment='right',
rotation=0)) #给散点加标签
adjust_text(
texts,
only_move={'text': 'xy'},
arrowprops=OrderedDict(arrowstyle='->', color='red'),
)
plt.legend(loc ='best')
#save_steps=True,save_prefix=savepath)
6.删除多余冗余的legend
我们看到图中有很多重复的图例可以使用以下方法删除
plt.legend(loc ='best')
#save_steps=True,save_prefix=savepath)
#----------设置图片的标题---------------#
plt.title(
'Station(B1CB2aB2b) Distribution Map ',
loc='center',
fontsize=50,
c='black',
verticalalignment='top',
)
# bbox=dict(facecolor='g', edgecolor='b', alpha=0.65 )); #设置标题边框jk
plt.tight_layout()
#----------删除多余重复图例----------------------#
handles, labels = plt.gca().get_legend_handles_labels()
by_label = OrderedDict(zip(labels, handles))
plt.legend(by_label.values(),
by_label.keys(),
loc='upper right',
fontsize='xx-large')
# plt.savefig(savepath + 'Station_Distribution_Map_B1C2.svg',#保存图片
# bbox_inches='tight',
# dpi=600) #dpi = 300); #保存站点图
plt.show() #显示这个地图
最后我们就能够得到一张测站分布图啦