z* Q" l( V" v p' Q
Folium 简介
& e+ f# A, _; K! U 作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
9 u) c" s1 U3 A Q2 G [5 d/ ]+ x 创建地图
; b! t# i- U) c! o4 c, M% W6 } 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。 # `) _$ M4 _; m: l
import folium+ K* N; {& N/ h. T
%matplotlib inline$ @6 ^* ~+ g4 B g9 O# w) D4 G* k
6 ]4 T0 q' g6 ^; n6 D8 ?5 q import webbrowser! G+ I' `" f# P p5 E8 y4 ^; I
) a" [9 H( d+ E; g- C
print(folium.__version__)
! ?# s/ P$ M, @/ I/ s) \( {4 K& w( d" _; Q' L. Y+ O3 h2 E
# define the world map0 h# E6 {3 L. J1 C+ r. `
world_map = folium.Map()
' {7 k! J/ T, G9 X. C9 n$ { # display world map/ y1 m/ |7 w( W' e/ U, p; P
world_map
\9 z( Q3 h/ K' ]5 l8 V1 _: V
' {/ R! t5 @# q, v 世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。
, `- z" ~) L5 |( P8 e/ o. {2 \ d3 Q 在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
* [+ E- ^: V, R2 l, @) K 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
5 F& L: c+ i$ r5 ^# B% l. p 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。
0 ^7 g) r& V) \# x* \ # latitude and longitude in Singapore city. M% j$ H. o7 n" R
coordinate_sentosa = [1.248946, 103.834306]- f0 b6 I' z7 J, m8 a! a/ m2 T
coordinate_orchard_road = [1.304247, 103.833264]
/ p3 j: k9 P) h) w6 W coordinate_changi_airport = [1.357557, 103.98847]* f, B) ]$ B7 T. W J9 E
coordinate_nus = [1.296202,103.776899]
4 v& N6 l0 x* g coordinate_ntu = [1.34841, 103.682933]
5 K7 b3 S( K3 Z5 m" _ coordinate_zoo = [1.403717, 103.793974]) t# T* O8 X) A4 I; e, y
coordinate_ang_mo_kio = [1.37008, 103.849523]
) V- d8 m1 J z+ w- c/ N coordinate_yi_shun = [1.429384, 103.835028]/ ]* c4 S5 \: m) w& p
" ^3 }, P1 l7 c3 j0 H5 ?% M9 f # icon
- r: L/ S: [7 J1 ?) [ icon_cloud = "cloud"
( Y' T$ X% Q& I+ j+ p0 y icon_sign = "info-sign"9 f# D! T2 k! e* m3 k: j% P7 @7 ~
; k9 F! @- ]: ]5 i0 d$ ~
# define the city map
6 b, P4 K: N8 W( f! Z5 K # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}5 L$ b6 ?7 Y) ^+ U* W- n
city_map = folium.Map(
/ q- I/ C D; U1 z# k; } location=coordinate_orchard_road,, N5 @2 c0 b4 ?) p
zoom_start=11,! B/ I9 p! M2 s
tiles=OpenStreetMap)+ @( ^4 {0 d: k9 d; O1 H& b* v6 T
- U5 T9 i# T% [5 x3 p
# add marker in the city map
; H" k8 s0 ]" K5 ]4 X0 H5 Y folium.Marker(. F- C2 n3 J# c6 M7 u
coordinate_sentosa,: e: }5 p8 F" }4 z; D* q! O
icon=folium.Icon(color=blue), v6 K7 {( M5 M ~$ U u
).add_to(city_map)* ?. o8 W5 B' z" R# T1 D
folium.Marker(+ f, v% I# H& e% u
coordinate_orchard_road,: L9 n/ M2 x' a5 C: c2 E5 T, q+ e
icon=folium.Icon(color=green, icon=icon_cloud)* K& [+ q: y# b# \7 |: F9 Q! V
).add_to(city_map)
) }8 J- `% D3 A) r o. n. S! B3 r/ `
# add popup! r; n g$ P5 T' ?. T- ]
folium.Marker(; I' I3 n) f3 K C
coordinate_changi_airport,6 q1 F1 L8 F4 g2 ^& k9 l
popup=Changi Airport,# ^# a; I5 f7 r/ W
icon=folium.Icon(color=red, icon=icon_sign)
: {! H. M# O/ z1 P- o5 c, T ).add_to(city_map)1 V T. W! H6 T' l9 W ]
5 H" ?9 {. |# i! C, ^7 M # add tooltips and popup
* p; C! B# l2 ~8 F! _7 e tooltip = "Click me!"
4 `, C8 i& Z: p8 ~2 `& r# a3 h3 W2 |2 t
folium.Marker(
" t6 }" R9 e8 S# B+ L% @# r coordinate_nus,: Z G9 P% S' Y/ L2 Z' b: Z1 `
popup="<i>National University of Singapore</i>",# r$ T: c% F, o) p# U& J/ z( Z
tooltip=tooltip( V: y: B: w) ~! E2 G
).add_to(city_map)' ?1 Y5 W* [: E/ o a3 G& ~4 N3 ~
1 A$ K( w3 v- I0 {3 V/ r9 P
folium.Marker(" y1 M/ y/ S; W4 n
coordinate_ntu,8 w/ `0 n- S; Y) B; Y3 F. L) `
popup="<b>Nanyang Technological University</b>",
' j, r$ h* b. K# o tooltip=tooltip
' y5 Y8 y0 w- I& R% i1 N' j ).add_to(city_map)
3 N- s7 A9 X2 B# [% ^
- Y3 m# L4 j5 h* }3 ~) Z # display city map
5 p, n1 X+ _9 J, F, c city_map
, q2 \3 i/ Z1 V Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
+ X: a0 K0 I% F5 O- l' [2 N # define the city map
* A" A8 g: p. z! X$ G* W( R city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
" W) R8 r- d9 f; b; M
1 s5 ]4 n6 P" d$ L0 O7 l- k. M/ U$ V' {' E # 在地图中添加经纬度, add latitude and longitude in the map when click
9 V1 s% f! _. t/ H& ~$ ^ \& o city_map.add_child(folium.LatLngPopup())
7 w& p# c" y- j5 q( V* S- i4 _, a* _: J$ D
city_map
% y x' [8 Q( t1 h d1 Y( `2 X/ _9 p
几何形状1 `& `* F* L) _$ x6 N; z
在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。 1 K& J. `" v' ]* I6 M' r
# define the city map4 a3 D& z$ ]6 ~; \' J2 `: ?
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
* k0 t$ F5 B2 \% `
' Y. \+ V/ `# t+ n J( B0 d # 在地图中如何添加形状, T3 ^) X, M& ?& y- }$ Y) s) U. B
# 多条边* S% G0 k5 a, [, P6 O1 s
points_1 = [+ {. d: J& ]6 l" {# P
coordinate_ntu,; s+ T/ ?" V E+ \" n
coordinate_nus,
/ D7 }9 u- [( h u coordinate_zoo
5 e$ y9 F3 u+ c% M C9 N8 D ]: y' X+ T8 h% D& e0 z3 b
1 f5 D0 d* C/ C6 T
# 在 city_map 中添加多条边,第一种添加方式
9 a1 b: `# b) s% E' H& D l city_map.add_child(folium.PolyLine(
0 ?# ~4 {* K/ ]* }0 n locations=points_1, # 坐标列表
* I: w3 t2 [( P" Q weight=3, # 线条宽度- N9 N. e% q4 G3 w$ m3 l
color=gray))
% X5 U& h1 g; r% y3 E: O9 V* _3 H( r, ^6 V% O+ U
# 在 city_map 中添加多条边,第二种添加方式
& I9 U+ ^2 M$ V! z+ t- N folium.PolyLine(8 z1 K( z) K, H
locations=points_1, # 坐标列表# c2 k7 s7 g) [! {$ B, b+ }* I3 P
weight=3, # 线条宽度8 V( V# x8 E9 t4 o/ n
color=gray).add_to(city_map) K9 f! f& v6 i/ c6 P, [
, ?" t9 D( A f; | ~ # 多边形) l: X* v. H d& u
points_2 = [
t8 r$ _% K) b8 M7 n) Y0 Z coordinate_orchard_road,, O7 Q8 H. J1 o( u! _, }
coordinate_sentosa,
; m0 C" C3 b. N+ v8 M) D1 a coordinate_changi_airport
4 x4 N. G& F( ^; B5 q ]
' J8 ?# v- E* I4 ~7 ~
& x+ r6 H6 j1 S/ N- w. C% G city_map.add_child(folium.Polygon(
; g7 ~4 y$ K6 p8 ^0 G locations=points_2, # 坐标列表
/ v: I* I5 f) h: H weight=3, # 线条宽度
% y# n* ? Y0 U5 O$ G3 y color=yellow))) u7 T4 Z- J$ Z3 h
& [" p* c3 G, }2 R" X
# 矩形" _; W. ?3 A8 E0 [1 U( m
bounds = [2 r2 \5 D& ?8 T. Y
coordinate_ang_mo_kio,/ G/ F1 b/ i! C; _
coordinate_yi_shun4 [2 Q$ T* j3 `) P
]7 l, f3 E% n$ l: Q' W0 P
) }% |9 q: @7 i. o4 [9 p4 s& @ city_map.add_child(folium.Rectangle(/ M8 l5 @3 T9 Z
bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
) a3 ]1 t$ b: j5 G! W6 e weight=2, # 线条宽度$ [+ |7 m) f+ ~2 }
color=blue))& `2 B7 \- C2 o& M& X9 a/ \
+ }3 z P0 D7 M/ @3 w
# 圆形, circle, radius units meters' ^- i; z M$ b+ ~6 f( \9 e/ ^
folium.Circle(
! G9 }. I( ?' S7 E& ^, { radius=1000,
, _# a1 q; L& g location=coordinate_nus,8 h1 W, |" ~) f/ c! d
popup="National University of Singapore",. h' N- M0 [9 d/ F9 @9 Y
color="crimson",, B+ ?% @6 \+ Y/ o
fill=False,, i. A5 x8 _ A! j
).add_to(city_map)
1 Q4 K) n p8 k
! }5 h" L; O0 U J, |/ i # 圆形, circle, radius units pixels+ {' I# g" F+ n8 z# _$ y
folium.CircleMarker(' c- d( V5 W5 Z
location=coordinate_ntu,
( c& _1 Q8 j( f& _ radius=30,( r7 K2 `# B% V* d
popup="Nanyang Technological University",
/ a$ W! }- _9 T E) F/ |3 C. D v, d color="#3186cc",
( Z0 G+ H; K } l fill=True,
/ n3 I( L5 M! j9 h5 }" Y fill_opacity=0.3, # 透明度
) ^ P6 ^+ x; N6 M fill_color="#3186cc",
$ I, Q/ G6 x# @4 l. c' K' D ).add_to(city_map)5 e0 j I5 ]8 ~" i$ k) L8 K4 g: l: s- X
/ ?# X+ W' r8 U [4 r; c$ u
city_map 1 r# F4 A1 p, x" j
Folium 中的画出各种形状热力图, z) W0 L6 p* s. o) @
在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。
3 U, T9 D! f. B import numpy as np
, }- H* q: z6 o( j. _! ^ from folium.plugins import HeatMap
: Y5 C. _5 N6 S5 y' w$ e6 o8 x/ x% B- K8 [$ Z/ G% |
# define the city map& \9 S6 T0 k( k/ u; \
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
8 l- q, o7 ?& C w3 b! Q
0 }2 z5 F; z. V# y8 v& [4 w6 @+ W # 构建随机数据 e9 A6 @& ]- L+ s" f7 K
data = (4 T* K/ i5 X6 v. i- R) r$ J9 m# ?
np.random.normal(
! X, k; o& b0 c$ X% k size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
" b# K7 b4 Y% a9 N N, q' v np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])( ^* m% _' z+ ]
).tolist()- j+ _# M& g% c$ _3 ~3 Y! `$ k
l9 }. j( `; K' l3 z! C city_map.add_child(HeatMap(data=data))2 D- i8 v9 |; l! p7 G
city_map + O/ u$ Y% B0 K: l7 ]8 A$ H
7 s" B3 B" N7 C* C' l O
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。
; U* [! y/ {0 w E. k2 { import numpy as np+ F6 E0 z( \0 X
from folium.plugins import HeatMapWithTime
1 {3 ~/ [5 V+ E9 \4 ~. s# O @2 \2 }3 K1 C
# define the city map
/ D# H" P/ I# A/ H city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
6 U/ \2 b+ ?- c* b2 y+ |( r* W6 ?( L" N4 D" e0 T9 T
# 使用 numpy 建立初始数据
6 f+ k- U5 C8 ~ initial_data = (np.random.normal(size=(200, 2)) ** m4 D" j2 l# k! i6 Z5 n6 }0 a: r
np.array([[0.02, 0.02]]) +6 x4 B- w/ g' Z3 C1 i
np.array([coordinate_orchard_road]))! C# [$ Y8 y4 ?5 W
8 b( N2 H) z9 n/ |4 j7 s/ X/ C # 建立连续的数据
8 S0 s$ ^ ]' p! O' n3 T$ ]3 } data = [initial_data.tolist()]
( \' H- m G" S: R for i in range(20):! p: v) \( D2 i! L
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())- o# O8 {3 `4 Y2 u( Q9 j* D
1 C" \% t, b$ W7 l2 [5 r5 G
# 显示连续的热力图
* `. _1 j! z( i. B# c city_map.add_child(HeatMapWithTime(data))4 ]3 `" n$ Q, z' G& K/ _+ S" U
city_map
. a% J6 H% N5 v/ w2 t: r ) W4 c, S0 I8 c, t+ z
经纬度点的聚类) F+ q/ h: ?/ L. i* B% J" C
除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
& X' e- W4 I6 \/ _+ s" ? from folium.plugins import MarkerCluster3 h* |' e; l1 I! t
5 y0 j0 j; h/ I$ A
# define the city map. A# ^ u+ {& S1 L$ d# ~; v
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
) P2 P8 V$ p+ ]' K5 |' z8 q: j
& }* c$ n3 Z- e( i3 c # 经纬度的聚类
# m$ @9 Z# O3 O2 x! @ m # 在 NUS 的经纬度附近随机生成 100 个点;
. i- h' B( b) x$ c6 m data = (3 F, o$ y( @4 |" m* }
np.random.normal(size=(100, 2))$ a6 Q; b; w1 e+ ~ E1 r
* np.array([[0.001, 0.001]]) +' X; g' W. `" A( f/ d+ I1 w! X
np.array([coordinate_nus])), k' a8 B6 c& I% n/ _
( D: s& h t8 v9 r! Z/ [: d: b # create a mark cluster object# @ a4 J; t: c7 T h
marker_cluster = MarkerCluster().add_to(city_map)
4 ]! g8 m0 }% ?/ B
- ^5 h! ?8 d- S* |8 g # 将这些经纬度数据加入聚类& q* R0 V) t# ]0 ~; Q
for element in data:: B$ Z9 K) k) ^- l8 d
folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)
# S& l2 R* c/ Y; k. B* W3 ^, F
$ Q0 t9 F$ P- G A- p+ {4 \ # add marker_cluster to map
3 T' N6 t o% a1 Q! G; N city_map.add_child(marker_cluster) X4 B& S& A$ G1 `% _+ L. O0 Q
! \" n! V* X3 I( P! H # 作图2 v# o+ _( A, P2 g
city_map 7 a \4 P5 k8 f. }
7 G! f4 `- q* g: P& L 以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。 7 B8 a- x- S9 _, R: U0 x
参考文献
H; w7 g' z" t5 E" k Folium 官方文档:Folium - Folium 0.12.1 documentation , |( [: ?5 W4 r, ?$ Y$ z( Y
6 k/ [$ X# F; E5 K* \
/ v* i- i. v) o) [9 J4 T. s
3 l% L$ Y# c8 Q1 v' J) o* T% D D1 I! A# i# ]
|