Folium Popup
Folium交互式地图及popup中文显示¶
本文将介绍使用Folium生成交互式地图并使popup弹框支持中文显示。
Folium的说明文档不太完整,但是几个例子还是能把功能说明白的,主页中就有。当然,如果出现莫名其妙的bug,就得考验大家的智慧了。在我个人的使用过程中就遇到了一个困扰很久的bug,popup弹框不能显示中文。受python4oceanographers的一篇博文的启发,终于解决,在此特别感谢,并分享给中文世界的朋友们!
本文将通过一个应用场景尝试解决以下问题:
- 找到距离某地最近的若干记录(并用公里显示距离)
- 显示在Folium地图上(配置合适的标志)
- 点击显示详细信息(中文!)
数据预处理¶
首先还是下载中国气象站点数据,读入pandas并转换成十进制度,上一篇博文有介绍,不再赘述。
import pandas as pd
data = pd.read_excel('SURF_CHN_MUL_HOR_STATION.xlsx')
def min2deg(x):
y = int(x)
y = y + (x - y)*1.66666667
return y
data['经度'] = data['经度'].apply(min2deg)
data['纬度'] = data['纬度'].apply(min2deg)
整理一下数据精确的有效数字
data['观测场拔海高度(米)'] = data['观测场拔海高度(米)'].apply(lambda x:round(x,1))
data['纬度'] = data['纬度'].apply(lambda x:round(x,2))
data['经度'] = data['经度'].apply(lambda x:round(x,2))
data.head(5)
将这个版本的数据存起来,以便下次使用
计算两点距离(公里表示)¶
已知两点经纬度计算两点距离有其公式,详见Movable-Type的介绍(包括JS代码和网页计算器)。以下Python代码来自Bruno Rocha的分享在此感谢!
# Haversine formula example in Python
# Author: Wayne Dyck
import math
def distance(origin, destination):
lat1, lon1 = origin
lat2, lon2 = destination
radius = 6371 # km
dlat = math.radians(lat2-lat1)
dlon = math.radians(lon2-lon1)
a = math.sin(dlat/2) * math.sin(dlat/2) + math.cos(math.radians(lat1)) \
* math.cos(math.radians(lat2)) * math.sin(dlon/2) * math.sin(dlon/2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
d = radius * c
return d
赤道上1度约111公里,北纬30度附近1度越96公里:
distance((0,1),(0,0))
distance((30,0),(30,1))
用Harversine函数计算所有记录的距离:
data['距离雄县(km)'] = data.apply(lambda r:distance((r['纬度'],r['经度']),(39.02,116.10)),axis=1)
选择距离雄县50km以内的记录
data[data['距离雄县(km)']<50].sort_values('距离雄县(km)')
剔除雄县本身后即为要找的50km内的记录:
selected_st = data[data['距离雄县(km)']<50].sort_values('距离雄县(km)').iloc[1:,:]
Folium地图显示¶
Folium设置初始地图很简单,只需指定一个中心坐标。找到雄县的坐标:
import folium
data.ix[data['站名']=='雄县',['站名','纬度','经度']]
xmap = folium.Map(location=[39.0,116.1])
xmap
在雄县这个位置添加一个标记,设置标记的图形样式:
这个icon的设置很有趣,可以变化出很多用途,Folium.Icon类的介绍可以通过在jupyter notebook里执行Folium.Icon?
显示。
简单说这个类可以设置color, icon_color, icon, angle, prefix这5个参数。color的可选项包括:['red', 'blue', 'green', 'purple', 'orange', 'darkred', 'lightred', 'beige', 'darkblue', 'darkgreen', 'cadetblue', 'darkpurple', 'white', 'pink', 'lightblue', 'lightgreen', 'gray', 'black', 'lightgray']
,或者HTML颜色代码;icon_color同上;icon可以在Font-Awesome网站中找到对应的名字,并设置prefix参数为’fa';最后,angle以度为单位设置。
icon_kw = dict(prefix='fa', color='orange', icon_color='darkred', icon='cny')
icon = folium.Icon(**icon_kw)
创建一个Marker对象,然后加入xmap地图中,注意,这里popup我直接写上了汉字“雄县”:
folium.Marker(
location=[39.0,116.1],
popup='雄县',
icon=icon
).add_to(xmap);
xmap
点击标志,发现显示的是乱码,其原因在于这里popup只能使用ascii编码。
中文显示¶
我们不妨先看一下系统默认编码是什么:
import sys
print(sys.getdefaultencoding())
Folium自带一个IFrame类可以在其中嵌入html代码并解释,因此给了我们一个机会借道HTML来实现中文显示。那么首先要把汉字编码为ascii xml字符的形式。经过多次试验,以下函数可以实现正确显示:
def utf2asc(s):
return str(str(s).encode('ascii', 'xmlcharrefreplace'))[2:-1]
utf2asc('雄县')
写一个最简单的html标记:
heading3 = """<h3>{}</h3>""".format
heading3(utf2asc('雄县'))
from folium.element import IFrame
iframe = IFrame(html=heading3(utf2asc('雄县¥')),width=100,height=50)
加上一个¥以测试对中文符号的显示能力
popup = folium.Popup(iframe)
xmap = folium.Map(location=[39.0,116.1])
folium.Marker(
location=[39.0,116.1],
popup=popup,
icon=icon
).add_to(xmap);
xmap
此时popup可以正确显示中文及符号
popup美化¶
借鉴Filipe的博文中的HTML代码以美化popup,使其呈现表格:
列名也需要使用编码映射:
{k:utf2asc(k) for k in selected_st.columns.tolist()}
popup中的HTML,以站名、站号、观测场海拔的顺序构建列表,注意看<tr>
部分
table = """
<!DOCTYPE html>
<html>
<head>
<style>
table {{
width:100%;
}}
table, th, td {{
border: 1px solid black;
border-collapse: collapse;
}}
th, td {{
padding: 5px;
text-align: left;
}}
table#t01 tr:nth-child(odd) {{
background-color: #eee;
}}
table#t01 tr:nth-child(even) {{
background-color:#fff;
}}
</style>
</head>
<body>
<table id="t01">
<tr>
<td>站名</td>
<td>{}</td>
</tr>
<tr>
<td>区站号</td>
<td>{}</td>
</tr>
<tr>
<td>观测场拔海高度(米)</td>
<td>{}</td>
</tr>
</table>
</body>
</html>
""".format
循环列表各行,将信息添加进地图:
for k,v in selected_st.iterrows():
iframe = IFrame(html=table(utf2asc(v['站名']),utf2asc(v['区站号']),utf2asc(v['观测场拔海高度(米)']))
,width=310,height=130)
popup = folium.Popup(iframe,max_width=400)
folium.Marker(
location=[v.纬度,v.经度],
popup=popup
).add_to(xmap);
xmap
注释