1 V- J& d, Q3 d9 O8 z3 Q Folium 简介) H1 y- n* j9 p' J% [+ }' A
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
& \# E* p8 G0 v( k 创建地图
% m! G2 w1 T$ P& v; o" C# N 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。 $ ]4 _7 @ U. m; h
import folium, p j9 \8 B7 f R/ C
%matplotlib inline
' u o) q' V7 \$ i5 h4 C d* z) Y* ^1 ^1 Y. [
import webbrowser0 {6 C u+ Y# I& O l% g* ]
7 r5 Z0 r. v, l
print(folium.__version__)2 S: h! n# a2 t1 w/ _ S
" H# W# c6 T8 h9 _+ B& i7 I
# define the world map$ `1 [/ R# l* p+ L
world_map = folium.Map()
/ Q N: E- u: g$ O- _. o' G # display world map9 D- Z6 m! w' h+ @/ C* s* w
world_map o% c5 J) f: k$ S# Z
! H3 y/ r5 j' u8 N 世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。
7 n' _% v) R x! U9 T, A1 y 在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
& U) z' Z1 b6 i$ `5 ] 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
/ {) {% `! D$ }, D% i+ l 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。 ( O9 j7 i( J2 q0 {& M, K5 S
# latitude and longitude in Singapore city- N G5 R" a8 ]5 }3 }6 T8 X
coordinate_sentosa = [1.248946, 103.834306]
5 n W/ _& }# ?7 C m" B G coordinate_orchard_road = [1.304247, 103.833264]
% |: B1 Y; }# M+ T8 o coordinate_changi_airport = [1.357557, 103.98847]
" _1 D7 G. P2 [ coordinate_nus = [1.296202,103.776899]5 n$ p" d \" s: b8 `- l& D
coordinate_ntu = [1.34841, 103.682933]
: [2 ?, u$ b6 x8 z0 e2 P, R. a6 z6 B coordinate_zoo = [1.403717, 103.793974]3 c" {; W- I* I1 X$ m
coordinate_ang_mo_kio = [1.37008, 103.849523]# @4 T" J" W" j9 p' U
coordinate_yi_shun = [1.429384, 103.835028], F$ q9 Z9 B [& b9 I
' v: W5 q9 G- d5 ~! D# l* }2 M0 u # icon& }" z% w v! ~' A
icon_cloud = "cloud"& z4 ?6 J2 v$ k9 r2 ~
icon_sign = "info-sign"/ C; f' W0 t9 G
, a7 D0 g$ Y0 w. _. S
# define the city map: x! S, N" P) l7 H/ ?
# tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}# B, |3 |: P c8 S' r
city_map = folium.Map(
1 f Z, _4 r z- p6 t3 b: ?5 n location=coordinate_orchard_road,
! \" K2 Z5 i5 v/ i* ^ zoom_start=11,
7 M& X0 v2 w. u2 `! R- v+ @6 u tiles=OpenStreetMap): c) |* Y7 Y8 L: J! @
6 J2 Z3 n/ [ e' u' { # add marker in the city map2 Y3 X" {# x: p* Z/ Z
folium.Marker(( G7 T D; l' R, D; V
coordinate_sentosa,# _, ^$ X; T7 B& o M% J
icon=folium.Icon(color=blue)
# V% O; s: K. D: a* A V ).add_to(city_map)( [8 o4 ^, `: @; Y0 y
folium.Marker(- ?0 @9 G) Q& l3 s) [: Z1 G
coordinate_orchard_road," @# N: v4 R9 x" {9 n9 W( I
icon=folium.Icon(color=green, icon=icon_cloud)) q4 k0 g1 D4 N
).add_to(city_map)! p0 ?' t$ x% M( b. w
2 Y( I6 }' t' ^ # add popup
2 ?/ K4 I, U4 S folium.Marker(
+ n4 Y( E# K1 B# \* G- d+ u% ^ coordinate_changi_airport,
' o/ {% W5 U% L" t; J% v popup=Changi Airport,
- ~+ A& G: T! \+ L* Q; v icon=folium.Icon(color=red, icon=icon_sign)
5 S9 V( j8 ~# h1 V1 }0 D ).add_to(city_map)
) u% r1 F4 e* P, ?6 d2 H* ?3 ]0 n$ u' ^0 a" z0 h- L E
# add tooltips and popup
% r4 F- U+ O* r5 P) a/ T9 q. A5 q tooltip = "Click me!"* V- v& L( V8 _' O; q
3 H2 U! U( r$ H- b, F" e
folium.Marker(
0 `9 N0 v' {9 n! h. M coordinate_nus,
8 ~+ P% i2 e0 R5 K" X# t% o popup="<i>National University of Singapore</i>",5 ~6 m5 Z q- _3 w% e$ f
tooltip=tooltip
/ Q7 o* Y9 ~. \: p5 T; ] ).add_to(city_map)/ R0 B) N6 k6 n
& [ F1 o8 C$ y
folium.Marker(/ }! O. u. w4 A6 c$ W$ {# O* p
coordinate_ntu,
& ?) n; i9 j6 X! F/ @ popup="<b>Nanyang Technological University</b>",3 d0 z9 E1 {3 k5 I" e# C' E
tooltip=tooltip
# h1 v F" @! O2 r( X0 l6 w% D ).add_to(city_map)9 V; G" G! R1 a
3 u% e# u* y( G # display city map
* c7 P l" T. ]9 i! ] city_map & y7 [! V; j2 F" N' {
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
* E0 c$ ^* x$ f* z+ P # define the city map; e9 m0 K, w% `
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
- Z/ v( e+ j" V3 _) T! H
9 e" u) X* J! }6 U! |/ d2 s # 在地图中添加经纬度, add latitude and longitude in the map when click
1 W1 t# G6 E; Z; X2 A! ]- x city_map.add_child(folium.LatLngPopup())
$ f+ G2 j+ n( `$ r/ L. u7 H7 N3 F. c
- u2 K* w/ X% U4 Z0 u& ` city_map & N0 M3 d) h& l. S+ l9 L2 a8 T3 Z
9 }) R- a" d/ K, R5 ~ 几何形状0 d* I( o5 b$ e% g; a D
在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。
7 j& Z8 o. w% \$ K # define the city map/ v* t) r2 I- S, p! M
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)$ j% W% Q: E/ h+ l/ z
# ?2 X c. L1 b6 ?/ d3 {) |9 g; C$ i # 在地图中如何添加形状
9 q* C( m i4 x- H; z, t% u # 多条边6 O$ s) P. O9 }5 [
points_1 = [& M5 h* n! T6 d
coordinate_ntu,2 j; K4 R* V. ~: F. {$ c
coordinate_nus,& H; B* X b) {# x& H% {
coordinate_zoo
- }) z' ~' p, z$ A, ]5 q! ` ]
( K* s! y1 K1 r% a7 |* a4 J* X1 J5 m- [+ _
# 在 city_map 中添加多条边,第一种添加方式' g" ^2 g4 C( J- k% ~3 {
city_map.add_child(folium.PolyLine(
) h3 h2 {7 p! b: { locations=points_1, # 坐标列表0 N7 J9 Z& J6 O$ N0 J
weight=3, # 线条宽度% n) ^, }, C9 [8 L4 ~# c! C1 y& `8 }
color=gray))
' N5 N9 H, K5 {% l. f3 B( f" l" _# ~
# 在 city_map 中添加多条边,第二种添加方式/ C, n. [7 R" n/ }( n9 ~9 J3 m
folium.PolyLine(
+ Z l( m$ H0 a locations=points_1, # 坐标列表
! ?1 V2 N5 V, I* ?' f weight=3, # 线条宽度
/ w8 S; G& T" f5 ~# e color=gray).add_to(city_map)
% u3 O, f0 ?1 B' H+ }
. m- e6 N( H" U3 s/ J+ E U # 多边形2 s) \4 r& {' p& j* m+ [
points_2 = [
1 Q6 x6 p7 |" R' F j1 \" k5 `: ]+ r" P coordinate_orchard_road,; [$ k0 H8 Y7 V6 s
coordinate_sentosa,
# [. E+ Z9 p/ } y coordinate_changi_airport0 p& T$ w2 \$ [, G) w W2 c
]" _, Z V3 m/ l. h# w
! _" x8 [4 e* c" _7 G9 j
city_map.add_child(folium.Polygon(% M/ {8 F" {( u& s; r
locations=points_2, # 坐标列表; d3 w( V: e1 W- D
weight=3, # 线条宽度9 B# Q, t; ~& R
color=yellow))- A' Q4 y& r* @" s- m; Y1 Y
+ s4 ]" ?# F0 t; D1 v # 矩形
, g) V h" M5 M! M bounds = [
! e1 e v+ L: ^) M- \* k. { coordinate_ang_mo_kio,) y4 `8 b5 b b
coordinate_yi_shun
/ D1 W) ~8 P( d6 `4 d ]
4 Y5 G' y& m4 P7 f$ h' Q7 C- \' K' N. ^9 W! A6 \
city_map.add_child(folium.Rectangle(
3 ~' Y; |; g/ o4 t bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
5 k& I, f7 \; }+ v2 n weight=2, # 线条宽度
$ D+ z6 y* Q6 f6 D* ~ color=blue))5 I# g- k. c" u, a4 g4 A0 A
! ?" e% B3 `* x# h# v& F" k2 n2 w
# 圆形, circle, radius units meters
& b. s3 ], F* g/ p/ f7 J folium.Circle( a& h6 j& \- f$ E6 h' y. x
radius=1000,( {+ x- f. J4 G% P- z
location=coordinate_nus,5 q5 J/ _( N' G- I, O9 m
popup="National University of Singapore",
. q) h8 T- |/ Y0 Z color="crimson",
" y9 C1 `& g$ r6 C8 { fill=False,
7 {: R" B1 C: V ).add_to(city_map), K* O# y2 s7 I2 r2 p# D
/ Q: d3 l8 E8 f! P1 e # 圆形, circle, radius units pixels& v" s# u1 Q z
folium.CircleMarker(; i% f; E: j# P8 c' N
location=coordinate_ntu,
. Y4 K$ s; r1 r radius=30,
V6 T2 R/ ~' M6 ~4 P2 G6 ^- G popup="Nanyang Technological University",0 R m% U, @# Z E+ @0 j
color="#3186cc",
& ?% r+ m1 b) Y* _( t fill=True,! @, d/ K( B+ P( u0 ]
fill_opacity=0.3, # 透明度
1 u8 S( `' A4 B- u/ @% u; D0 R fill_color="#3186cc",
5 b( a; m% j& p& O ).add_to(city_map)
) F. |9 g/ W3 ^; }6 k7 a% H6 x! _0 h1 ]% N( N8 l/ I! b
city_map * V# |, w L6 E+ ^
Folium 中的画出各种形状热力图
# M6 l+ c7 j8 a+ ^( Q 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。
, ]9 W' s1 Q* J! v5 E9 [ import numpy as np
4 {! m8 p. P0 v' g+ P from folium.plugins import HeatMap: _7 N" v& s3 u: d L0 p: h0 Z' f
5 h1 P2 F x' D8 e
# define the city map
0 s' l6 |2 D- U; k3 p city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)% r; N0 A# I1 S, l
+ ?& ~2 ]# n& y* W
# 构建随机数据
0 @7 q2 i9 G% x% u j/ B1 J+ B data = (* g7 V- D7 F" ?
np.random.normal(
5 u! g& |* `) U- k1 N size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +4 ~7 f( q: h% f; M
np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
! d% B0 d3 v6 I9 e ).tolist()2 h/ x3 e/ E% ^; u# K) ^2 p
0 q. \! F/ _8 o# `
city_map.add_child(HeatMap(data=data)). S: X) i) T# j. n7 p
city_map
# H k& e p) o; D
" R4 q, a' d$ D% }3 e 除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。 . v/ c& T) b" Q, c6 G# T
import numpy as np6 u( T0 w& i# Q6 @5 P: I
from folium.plugins import HeatMapWithTime ~+ ^/ E* f# C3 b
( @ p! s+ R0 K, h
# define the city map
" z! c2 V5 k# N3 _5 [1 k* |" a city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11): g1 _# V9 ^ r" x4 c/ z
! [7 `; @" A) q) _% ?. G # 使用 numpy 建立初始数据
1 v. M" r- v/ Z0 g2 U8 p# o initial_data = (np.random.normal(size=(200, 2)) *% W9 }( k, b4 Z8 j0 O
np.array([[0.02, 0.02]]) +5 z7 Z2 r! o% \4 X
np.array([coordinate_orchard_road]))
* ?. Y5 v8 V$ e% _& v% v! j4 i+ m. X
# 建立连续的数据, q# g" R4 J$ o1 M1 q3 {
data = [initial_data.tolist()]5 [+ x# D3 @0 v
for i in range(20):
8 B7 n4 ^( Y( R) y2 y) Y3 r& H data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()): X+ u' A- m9 J3 u
" Q" f; M6 t: }& {: C
# 显示连续的热力图' X; {; X( C1 \% |! |) M
city_map.add_child(HeatMapWithTime(data))7 q# D# N( R: C$ c
city_map
: |$ h V; P2 M! I. p
8 B" Y: V H: _- }* u7 m7 I7 B 经纬度点的聚类
! j/ ^+ a' p# B 除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
$ U' y- k/ b4 l$ Y, c) X from folium.plugins import MarkerCluster
+ ^5 O5 \: u/ Q" L: ]5 }3 B3 @ f; F6 M) r$ A
# define the city map9 `8 {+ _$ a) s" X' Q, P
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)# U$ l: c* U: D
P* x+ y+ |# x! W. U
# 经纬度的聚类
& G @+ g1 F0 T, j* t: _$ Y8 C/ ~ # 在 NUS 的经纬度附近随机生成 100 个点;+ D; z. { M" T, M) O% S/ A
data = ($ r( U9 E. o' g4 o: v1 Y" _
np.random.normal(size=(100, 2))
2 O9 g0 v- z$ i" V. H% z% S% U" ~& _6 w9 J * np.array([[0.001, 0.001]]) +
: ?% o5 q# \! {6 U5 t+ G np.array([coordinate_nus]))% K% B7 e6 ^# @. t- i' h5 K' ^
4 f$ ]+ s" P ~$ J* ]
# create a mark cluster object
) o L8 N# x( v/ ?/ Q marker_cluster = MarkerCluster().add_to(city_map)
- k/ w8 R! A6 [
T' U) n0 G: `4 a! Y! F# M0 x5 { # 将这些经纬度数据加入聚类
8 c3 p+ j. ^# K" n for element in data:
% C3 p: d* H! B5 V& { folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)
' N) p, ^2 Y* B% t5 S1 } f
# x8 @0 w0 U& C( ?$ g' e # add marker_cluster to map
0 ?! A1 U) v( e; a, x9 N city_map.add_child(marker_cluster) r; {" Q( u& ^ n" n2 y& J
1 @. F# v% l/ Y! s% K/ S! @ # 作图' }1 U' {" @4 ]# U! P* F
city_map * M( ?% c$ k A, @+ c9 s' l
7 G3 D/ p& {5 v6 G4 r( F 以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。
, A9 o$ |7 ]8 Q! t6 d/ o 参考文献
/ x/ b4 s% g# T1 U' q! O* s. W) Y Folium 官方文档:Folium - Folium 0.12.1 documentation " W( Z& \& V3 s( Y
- M) b d/ h# G7 _: e) F; d5 z5 p( M' V2 r$ `7 O) \
1 r7 `% n/ t6 `5 |/ o7 B, u6 p9 }4 G2 _0 i1 I
|