4 i* _; o g( ? Folium 简介% E9 K! y. R1 a7 i, S
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。 * r R/ [9 i6 v2 t& B7 a
创建地图
9 p: B: A. h/ w! d Y 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。 3 m' {5 v4 i1 Y/ W! L! ]
import folium% W8 t8 W: N# B6 R# G) U- w' b
%matplotlib inline
2 R5 L, O" T' O8 {5 i
4 } C, Y5 K9 ?3 q* J6 z, b, P import webbrowser
. V9 D, T' Y. \% |; a8 r2 J9 L2 ?" G# `3 H) {
print(folium.__version__)
% m% _6 c3 J) M3 Z7 G, y$ f; |6 @1 c9 d: N; o
# define the world map. g8 `! k; O8 `+ C7 j9 L
world_map = folium.Map()
0 k2 y8 V1 z9 u% r7 S$ V" Q # display world map5 P/ o( t1 h$ Y" z% g( R
world_map, y/ V) {- r$ k
, A3 e0 l5 `0 ]% \" [ 世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。
" X9 [5 p/ Z( S. o7 j* s 在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。 2 `9 g7 C% ^" y# z( t: ~# l1 I5 o
有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。 ' M9 R* _5 w3 R) b+ T! R
另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。 $ i* C- |2 z% o* h" o% u- a: n* b
# latitude and longitude in Singapore city
- `1 y, O/ T4 T1 c. ]8 a2 v! W coordinate_sentosa = [1.248946, 103.834306]" w- p2 N$ s- i& {5 ~- Z
coordinate_orchard_road = [1.304247, 103.833264]
9 U& J3 E+ q1 J4 r coordinate_changi_airport = [1.357557, 103.98847]4 O8 p$ E1 m- o+ y/ E. s- y
coordinate_nus = [1.296202,103.776899]
- N q- X& ~$ f! o% k9 i coordinate_ntu = [1.34841, 103.682933]
! I- S2 `6 e$ G. n coordinate_zoo = [1.403717, 103.793974]
3 ^- r: c& x G; o/ J c' _) @ coordinate_ang_mo_kio = [1.37008, 103.849523]
) c, ?' \7 I N3 f7 I4 C coordinate_yi_shun = [1.429384, 103.835028]7 ^$ B5 h! ^+ {% f
* t! y' m* z4 y* ]8 M" [0 } # icon
, A7 {0 ~; p- V2 n G q/ X icon_cloud = "cloud"
/ B5 Q" [6 d4 E+ w) V icon_sign = "info-sign"4 U: i& R, [' K" h6 G4 [9 E9 T
& Q# _, A/ i+ [) W # define the city map2 g: o; u9 F1 n9 T
# tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}
$ N9 \' z# K6 h city_map = folium.Map(5 P" q0 d8 R$ {: x+ O7 G
location=coordinate_orchard_road,: q. z$ p4 ~" L) j$ C
zoom_start=11,$ j/ v% @1 C, T8 u0 V6 U2 ] C* d
tiles=OpenStreetMap)
; D3 C4 l. ` \- j$ v X/ B/ `" ~3 b' a5 ?8 a2 e$ l' }
# add marker in the city map
+ o- l5 K6 K/ N9 l4 G# p, ] folium.Marker(
1 M; A" e0 T" k% S/ F9 z coordinate_sentosa,- i7 Y/ K' H% v; p# d2 V1 h
icon=folium.Icon(color=blue)
+ M* |4 K$ O2 x6 t ).add_to(city_map)
% `2 k+ T" d9 N folium.Marker(
3 y1 G% E7 |, x9 a coordinate_orchard_road,* ]; e& X$ K) D1 o) b4 R0 B
icon=folium.Icon(color=green, icon=icon_cloud)2 c* Z/ D5 a" p; {) N# c* j" G
).add_to(city_map)
; ~; ]. W' y" T6 }. V6 v( U
# S8 N* F# I" Y4 Z, |: l2 b # add popup) v0 d: \) t" Y
folium.Marker(
- v1 w! b2 @! O# y5 c7 } coordinate_changi_airport,, r' o9 {8 ~# s
popup=Changi Airport,
# l3 [. v# A3 t; V5 C, w) R! E/ Y icon=folium.Icon(color=red, icon=icon_sign)
1 q2 Z, _* [/ H/ T6 K( J# P ).add_to(city_map)
* V( ^4 E, c) A3 s2 w0 @9 T8 |" C0 G t* w/ }$ }
# add tooltips and popup/ v+ M R& k9 R
tooltip = "Click me!"% S# b* a; b( ]) H
5 j* |6 I. E5 G
folium.Marker(
. C- J5 `6 z0 i" w$ m coordinate_nus,
7 h. ?$ O8 y0 x$ ^( z8 `+ ] popup="<i>National University of Singapore</i>",
a1 Y5 V$ e+ @6 l! Z tooltip=tooltip
& f3 k/ V; E. H ).add_to(city_map)6 b! M8 g/ H/ t) T+ ^- [
8 W% F: g, X, q folium.Marker(+ D) a: o2 H* @
coordinate_ntu,
4 f2 W8 \4 V, D7 l# e. ? popup="<b>Nanyang Technological University</b>",% F& { k5 P/ h- K" U% t
tooltip=tooltip/ T5 F# h, x6 y# U
).add_to(city_map)0 a" T. L8 T1 P: U: X6 {
) e0 R- n4 B6 q' e # display city map
l/ ]; S: d6 v) w' m$ _ city_map # [+ f7 v) a1 L, \9 T; v3 J) w5 V2 v/ E
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。 ; f* V! W7 j2 c0 e" {# D
# define the city map6 j/ T4 M9 t( Q
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
0 C0 G% [4 X+ s Z; O1 @7 Z3 V X' r$ p+ ], d
# 在地图中添加经纬度, add latitude and longitude in the map when click0 a- O1 V' _6 s( _
city_map.add_child(folium.LatLngPopup())
; _* ]& j# \) R' T6 M# ~
3 y M2 d7 V: g9 X* ~ city_map $ p4 c7 V6 n7 Q+ s% e
# {; T/ B( v5 ` 几何形状
9 A1 `/ g7 _ k; R 在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。
/ O4 k' ~- K3 Z. k # define the city map
% {- y, a8 ]% S, o3 f city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
2 L' L* t H. l1 O) G* \+ |
. m) Z* h+ m- f) i/ I% P9 t1 h # 在地图中如何添加形状
$ B/ h1 Y2 o5 h" s: F! Y5 L) c # 多条边1 A9 V& [+ j f: p% L) ]) G
points_1 = [
& ~' ~4 g$ E5 P J. z; A coordinate_ntu,
8 }$ O9 h! k/ y2 i$ | coordinate_nus,
6 z) B& o* M3 \7 \ coordinate_zoo( L, W$ \* b* I7 v6 H, P2 B
]
$ U6 z, ^! Z4 F* o+ q3 \1 M7 o6 v
R( ~" |: |8 l8 v- { # 在 city_map 中添加多条边,第一种添加方式; ?( Y. n4 v' p3 Y" }( @: a7 k
city_map.add_child(folium.PolyLine(0 |6 X2 {$ w5 s1 ?+ e
locations=points_1, # 坐标列表
4 \8 n; }" A$ P9 D: l8 t' s! [& ? weight=3, # 线条宽度& J( d% e( {- `8 Z
color=gray))( E4 I8 h8 |: {. V+ I! |5 f* a ?
- u& ^; x- c* x- N5 L
# 在 city_map 中添加多条边,第二种添加方式: W; k: U' ]* Y- s8 P2 D' a+ _
folium.PolyLine(" }8 @% w! ^, K9 v" E
locations=points_1, # 坐标列表: E/ F$ t+ R7 W- `# b/ v
weight=3, # 线条宽度" z" R+ U9 L' J2 g2 U
color=gray).add_to(city_map) P; w: ~% B2 H D2 ]
$ C# \2 h r# p # 多边形
?% F$ u( `: G% D. b" y6 H points_2 = [+ \) D I4 [7 _$ A& A1 @9 W4 |: o2 l3 n6 F
coordinate_orchard_road,; _2 D1 W) T1 r+ \3 N; v* A5 D( z5 h2 x
coordinate_sentosa,
5 a: T8 g' _ ?5 X m coordinate_changi_airport
7 x+ }5 M+ B. x& _ ]
8 b6 F! o! j$ u x. O* B' I" X
9 ?1 w; B, u' H. C5 S city_map.add_child(folium.Polygon(* Q1 k. f" I3 p) P4 \1 |0 Z- C. \
locations=points_2, # 坐标列表0 C o$ h4 {9 E9 U) H
weight=3, # 线条宽度/ i3 j$ x0 Z9 G7 j' L2 ~
color=yellow))
. X9 p+ S+ r5 s$ S; Y3 _& U$ d
9 z, i4 [; J% ]5 s5 m" A7 w # 矩形( n! E- x1 A* C* ~4 Y2 {' V
bounds = [; w) Q2 M4 @/ y: b) N
coordinate_ang_mo_kio,
" i$ E6 @. h+ w4 I+ j coordinate_yi_shun7 ]0 Q1 K. n+ U
]
9 V4 }7 F0 L, U( x- ^5 P: ]. E" R) s$ f
city_map.add_child(folium.Rectangle(4 U$ p: j0 w) j+ m
bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
' [) W( P; p4 a* F7 } weight=2, # 线条宽度
9 L9 K0 }. a. X6 h' V% @$ } color=blue))
6 f i0 }( |8 M" I
6 B( c: Y, j1 B3 n3 p2 [ # 圆形, circle, radius units meters
4 }; D; W" [1 \3 F. i5 R& x folium.Circle(
, W; w2 |4 e: o; b ?5 }# `" s radius=1000,
5 K+ a: o, q5 U1 p$ y location=coordinate_nus,) ?- t; n4 D' ]. V) ^
popup="National University of Singapore",
& E9 z, f4 v1 |, a color="crimson",8 v6 X( L* p n! |& {+ L
fill=False,
$ P! Y( Q' h5 u! }( W4 b! E+ z/ a ).add_to(city_map)
7 U0 n2 A/ b* @
9 G5 e% x: ~5 ~ # 圆形, circle, radius units pixels9 C# ^3 N T/ w7 z. p
folium.CircleMarker(3 T1 a$ B+ n6 S6 V; G
location=coordinate_ntu," F2 A) h2 E: j* e+ I& m* U6 a5 e
radius=30,0 t' K- o0 d: M6 o( K3 K
popup="Nanyang Technological University",6 F; U3 ~+ }. u2 O* a
color="#3186cc"," M# ~& a L6 A7 Z
fill=True,
. i F, ?8 C# f, f fill_opacity=0.3, # 透明度" i0 g7 Q; ?* r6 N9 x
fill_color="#3186cc",7 w1 t/ y6 {' Z4 U5 R. L! L
).add_to(city_map)& U8 z" Y% g8 b$ {) N" X
# c m5 m# P) ?5 b3 j$ X& p city_map
" N$ N: d/ b3 V1 j Folium 中的画出各种形状热力图
6 T6 g, s* Q; v. T 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。 - G, U6 o( z- Z
import numpy as np" ]( B6 h1 b& n/ ~# ^/ U
from folium.plugins import HeatMap& L+ g/ y. e- Q8 h6 z9 b
1 z) w( P( ?+ s( D. H3 z
# define the city map
! P" h5 ]$ @+ I0 W city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11). X. n7 q: t" @2 Q# z& U
% w8 ~0 F0 q2 c1 ]' Z
# 构建随机数据
7 ]) C! c, c$ f% e data = (
V! E P4 v2 F ~ W np.random.normal(
* t) N# B4 o+ ?7 h3 J size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +; b( ?4 N' A. s0 \
np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
. I, |) T' L! s3 ? ).tolist() ?6 l5 Z* B) j; m
+ S+ s/ [% \0 m7 u8 {1 D. Y" p city_map.add_child(HeatMap(data=data))
: ^/ d$ d2 Q+ t) m city_map
5 `8 `4 m) d4 M& e; Q# k ; ~6 |0 ?) p) g% `6 y( n
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。 " P- \1 X+ o* n/ Z5 [
import numpy as np
3 N. N; ?/ G6 u4 o from folium.plugins import HeatMapWithTime
_% M U8 ?* h) b: Q' M3 a5 C: l0 p. W
# define the city map
8 }( i% A: N, z7 \# G city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)4 T8 L. V2 Z9 |" d+ d# m
; ^3 i* [6 T5 Q1 S% r- F7 w
# 使用 numpy 建立初始数据5 }- j* y! Q# ?' N/ g
initial_data = (np.random.normal(size=(200, 2)) *) U) C2 l/ N2 s4 r
np.array([[0.02, 0.02]]) +
5 m0 x. c2 E$ s1 Z np.array([coordinate_orchard_road]))! v) O( d% t( @4 Z5 _; e. u S
! a: }9 L2 B3 n. K # 建立连续的数据' a+ ^& @7 d- Y: o! G
data = [initial_data.tolist()]
3 I9 X2 v1 D* K5 ?' m for i in range(20):6 r( v" a4 G3 M( z9 U1 r4 ~
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())
8 C. a" ^: {- w. T# S
, |+ x& W' l6 ^$ U% \9 N # 显示连续的热力图6 f% Y5 w* {+ U9 M$ k- U: {0 B
city_map.add_child(HeatMapWithTime(data))4 p; L8 J. y" l1 n
city_map : M5 `- a+ K3 m& a
/ v; ?- H* H$ F4 b& ]" x6 E8 }
经纬度点的聚类
' m& Z9 O9 d9 H' D# w, a+ u7 n1 v- f' J 除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。 7 J3 D* n4 U3 P) J+ q
from folium.plugins import MarkerCluster; j. q/ f" _! n7 H* M5 X2 o
/ W1 E4 w7 Q9 G4 n5 c, M' { # define the city map4 f: ~1 O6 H0 q+ ]1 k
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
4 C# G% `# E# |6 f3 I" ~7 C. e% @, u0 I+ y1 O- Q w
# 经纬度的聚类
: [" n0 U# k+ [2 l7 l- z. m # 在 NUS 的经纬度附近随机生成 100 个点;5 {. S$ p) H3 y) u4 z0 p7 c
data = (
- L! T& {' f7 K) S np.random.normal(size=(100, 2))* F/ o9 v9 L7 Q" q1 d$ \9 H4 L" b
* np.array([[0.001, 0.001]]) +
. e& l }! ~* d9 M np.array([coordinate_nus]))) |4 W1 s- j- u# R9 M5 H
& w# P7 D# }# Y: Y* m, t3 d3 D
# create a mark cluster object
P! T: U" A1 g marker_cluster = MarkerCluster().add_to(city_map)2 K! W5 K8 C/ c) [
# o" p4 I4 d6 n( F # 将这些经纬度数据加入聚类
r+ t) l$ ?7 ] for element in data:! a/ C2 j' h$ O7 a; X4 t- F
folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)7 a2 ^: H: V/ C) y3 J0 z
8 ?7 X/ m. C! ~; B8 q
# add marker_cluster to map
! `" [: X/ z; C. k4 Q' D6 M: [* y0 s city_map.add_child(marker_cluster)
% U) ], g! v1 L! X0 v/ Q7 a5 p. @8 C; }/ O
# 作图
, @, I- `) `: y& E+ s city_map
3 S: v, Y; C6 Y/ m9 G8 W 5 Q) z) P; p, x( C" \! B" B; R* v; @& }
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。 6 q7 X+ G. X: B! E6 s9 i
参考文献
0 w* P) _1 H: A8 j6 t% n: S& p Folium 官方文档:Folium - Folium 0.12.1 documentation
8 l# ?5 `/ f- a4 H& ? J2 D; b: W
; {9 i0 I# Z$ |) y% P4 W5 [. u u# p+ x% H. V6 o5 y8 C7 L# X
5 ~4 S0 g6 H$ O% X
) x: q6 }* ^5 ?7 H |