最近在工作上接到一项任务,就是在地图上对应城市选点并以其为圆心根据给定半径画圈,从而圈定覆盖范围,围绕这个任务进行了各种尝试,最终通过folium 和 geopandas 实现。
pyechart 没有成功,导出百度地图之后无法画圈圈定范围,目前其支持的marker 只有点,线 等四类,是无法画圆的。在百度地图上JS里marker有画圆的工具,所以理论上是可以实现的,奈何没学过JavaScript,故放弃。
geopandas实现过程:
最初想的是直接画出地图后用matplotlib直接打点画圆,但是半径的处理就比较尴尬了,吧公里转换成经纬度的话只能粗略的按1度等于111千米去段,精度不高也就凑活着用。
%matplotlib inline
import numpy as np,pandas as pd,matplotlib.pyplot as plt
from sklearn.cluster import DBSCAN
from geopy.distance import great_circle
from shapely.geometry import MultiPoint,Polygon
from geopy.geocoders import Nominatim
import geopandas as gpd
from sklearn.preprocessing import StandardScaler,minmax_scale
from shapely.geometry import Point
import seaborn as sns
sns.set()
data= gpd.read_file('温州市.json')
fig, ax = plt.subplots(figsize=(21,21))
ax.set_aspect('equal')
cs_ploy = data['geometry']
cs_ploy.plot(ax=ax, color='w', edgecolor='black',alpha = 0.1)
plt.scatter(120.672111,28.000575,s=50,alpha = 0.8,color = 'b',marker = "o",label='POS')
circle = plt.Circle((120.672111,28.000575), 50/111, color='green', fill=False)
plt.gcf().gca().add_artist(circle)
plt.show()
之后查了geopandas的文档发现其还具备更改坐标参考系功能,可以吧经纬度转化成米,然后还有一个buffer() 函数可以在指定位置以指定半径画圆,问题完美解决。
data2= gpd.read_file(‘温州市.json')
fig, ax = plt.subplots(figsize=(21,21))
ax.set_aspect('equal')
cs_ploy = data2['geometry']
#坐标参考系转换为米
cs_ploy = cs_ploy.to_crs('epsg:3395')
cs_ploy.plot(ax=ax, color='w', edgecolor='black',alpha = 0.1)
geo_point1 = gpd.GeoSeries([Point(120.672111,28.000575)])
#坐标参考系先设置为经纬度再转换为米
geo_point1.crs = {'init' :'epsg:4326'}
geo_point1 = geo_point1.to_crs('+init=epsg:3395')
geo_point1.plot(ax=ax, marker='D', c='#99cc99', edgecolor='None', alpha=0.7, markersize=20)
geo_point1.buffer(50000).plot(ax=ax,alpha = 0.2)
plt.show()
folium实现过程:
相比是用geopandas 直接调用本地的Json/shp边界,folium 可以直接把圈划在在线地图上(调用open street map),这样更直观一点但是加载地图会很慢,有时候一分钟都出不来。不在乎时间追求效果的可以用。
import json
import requests
import folium
import webbrowser
with open('温州市.json',encoding='UTF-8') as f:
wz_states = json.load(f)
wz_map = folium.Map(location=[27.791883,120.557161], zoom_start=7)
folium.GeoJson(
wz_states,
style_function=lambda feature: {
'fillColor': '#ffff00',
'color': 'black',
'weight': 2,
'dashArray': '5, 5'
}
).add_to(wz_map)
folium.Circle(
radius=50000,
location=[27.791883,120.557161],
popup='The Waterfront',
color='crimson',
fill=False,
).add_to(wz_map)
folium.Marker([27.791883,120.557161],popup='<b>center</b>',icon=folium.Icon(color='red')).add_to(wz_map)
wz_map.save('wz_map.html')