/ n& H8 \. T' X1 o* @ Folium 简介" J3 \- P/ x$ C: J8 Q# I; Q
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。 5 K$ D5 d. l: @" M/ a
创建地图
- X# y7 Y* U, ]. L 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。
% h1 R/ E7 T, n/ Q5 b2 b import folium
1 r) ?, q8 f) U ` %matplotlib inline
; C6 n z& ~4 L$ c4 u: G4 I6 D2 i; s8 e% K
import webbrowser5 b. e, V& B% n+ F* s# ]3 S, Z8 o
# }+ Q% a- U+ H5 i3 E/ l print(folium.__version__). v# z) r6 g4 \( |' k/ \2 D( j d- i
- I1 J2 @6 w; j8 \9 b! A # define the world map: q! Y& Z- n2 j4 t. y
world_map = folium.Map()' l: ^5 k6 T4 U+ m) R1 e1 H
# display world map- m4 T# J, v0 `+ p9 a/ T
world_map
. i/ K% U' H, U0 _9 x0 _0 d1 b % p4 m6 B' h3 y( R' K6 O; V
世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。
. [; m7 d/ V: d- ^% |" j( `2 ` 在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。 9 G, c1 ~- F2 F* P5 C. x
有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。 ' F& _; s) h% n4 C: Y$ e
另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。 * ~2 S6 a! C, W0 S7 W4 z9 O4 H+ l
# latitude and longitude in Singapore city
G9 v$ M! C' Z* g8 n! c coordinate_sentosa = [1.248946, 103.834306]
& V. [7 M: s4 K; Q" w' A coordinate_orchard_road = [1.304247, 103.833264]
3 K( ?+ u E- I% L coordinate_changi_airport = [1.357557, 103.98847]
! m0 O7 t- k, a1 o1 u9 Z coordinate_nus = [1.296202,103.776899]
! {0 b9 H* A# P5 d9 V' B coordinate_ntu = [1.34841, 103.682933]
- a8 E/ d1 S5 H% p* p" j coordinate_zoo = [1.403717, 103.793974]$ M- _ H* s1 B* ^: |0 N4 o8 N; \
coordinate_ang_mo_kio = [1.37008, 103.849523]
5 o& G% Y( I, |! O8 i% V coordinate_yi_shun = [1.429384, 103.835028]8 p) [( |) y- x* X8 i- c
) e `, q; y9 V5 ~ # icon0 g5 R4 z( c3 I5 M
icon_cloud = "cloud". M! u$ M2 e! o( c+ T' B! p" _
icon_sign = "info-sign"
5 P: r" x! }/ O z- B* v; c) q5 _( w* J3 Z, G% Y* N+ g3 a
# define the city map
. o$ C8 ^3 L; ^ # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}: {$ Q: O" r( @# Y
city_map = folium.Map(
' g' G5 C9 s# g n location=coordinate_orchard_road,
; E7 b! R) M! `0 M/ V7 @ zoom_start=11,
1 s5 n c8 P4 p( g3 E tiles=OpenStreetMap)
! \+ X6 R. [- `' _( O, x4 R& @3 A' s+ E' x, k+ w
# add marker in the city map$ Y' c% G2 s; C. a; G. P
folium.Marker(* j& }, y- h# T9 |0 R2 g( \
coordinate_sentosa,
9 c" f1 h+ M8 v5 z icon=folium.Icon(color=blue)2 o" W! e( `+ I' b, A, K7 Q
).add_to(city_map)
1 k6 K! L! n- N* h" u# I c7 D0 ] folium.Marker(5 ]" M$ @! h; T) a0 K$ I
coordinate_orchard_road,
; G$ O; F2 J) j- B icon=folium.Icon(color=green, icon=icon_cloud)
3 d- l* n: d* ] ).add_to(city_map)4 l$ F# B% g5 X6 h' F
0 j2 z A/ b9 n6 h* H. p # add popup
. k) K2 I8 i( w& F" j! R8 z folium.Marker(
7 x( o1 s4 `4 ?$ k coordinate_changi_airport,9 j+ L5 x4 f* i7 J* O8 @. K( a
popup=Changi Airport,
4 O/ ^- \* ^/ Z icon=folium.Icon(color=red, icon=icon_sign)
/ n( o5 v# c T5 Y% R ).add_to(city_map)
. o+ ?! v7 b) S6 }1 a& H8 S# Q6 u/ M4 \2 @5 c8 j# w# ?
# add tooltips and popup# V: |' c, t6 k1 O2 ]8 D7 \
tooltip = "Click me!"5 ?, W5 ]$ h4 z! K2 v5 U5 I
' t7 O# u8 s1 F8 N, \1 d, V
folium.Marker(6 T, |$ l) {8 D m3 [3 {' n L& C
coordinate_nus,
5 [# h: e5 P& G- I# W- U( Z popup="<i>National University of Singapore</i>",
- O% @9 y8 c! } m0 K& X: @% t tooltip=tooltip- O0 T/ R4 C' `& J4 r
).add_to(city_map)
# {1 Y3 s9 ]( B5 V5 Z5 W% Y% {
6 o+ [1 J) w4 F9 H8 @! L folium.Marker(9 J8 q3 u) y9 j' `
coordinate_ntu,4 _( p; t+ J; R0 G2 ~( V
popup="<b>Nanyang Technological University</b>",
4 c# S6 t- u" x( X9 u tooltip=tooltip1 i2 g, A1 G- a
).add_to(city_map)' u& r3 y" ~$ ^; }# b# e
# K# _; ^: ^* O% h9 M8 K # display city map
1 L2 y g! C0 S city_map
4 P) i0 |6 J, Y: g Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。 9 J9 \ O* o3 O, E
# define the city map
2 }3 V4 H/ u% } city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
' N# L* i* j, b! M( O; {
9 a9 t) J4 d+ u* \$ F! k6 k; z # 在地图中添加经纬度, add latitude and longitude in the map when click
& w; ~$ H9 T, v/ { city_map.add_child(folium.LatLngPopup())
% ~ a7 Z* X% l$ e5 I
$ d+ Q1 e, n9 B# ^( @- g* i city_map
5 y& ?- ?! r- S) ?& j/ X
) F) |9 B* Y2 I& D 几何形状
/ n8 B. y) n/ J- r4 L$ e" Z$ e 在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。 . ?$ G5 _5 G4 e
# define the city map
- a% k+ b* D4 S city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
8 O9 ? I3 `5 A9 N% E0 O5 P2 z8 t7 D( H( o1 H2 l8 `+ }9 {
# 在地图中如何添加形状7 H2 }" ^, w8 M; W7 O
# 多条边
9 d4 U J4 n! h points_1 = [
8 f% C3 G7 [, s, |$ X coordinate_ntu,2 D" J( o4 k; K
coordinate_nus,
' ^1 j; D$ d/ b) N: e coordinate_zoo
' u, i# d" |* P ], O! e6 h0 c% S; |
8 _9 p1 n& f" S # 在 city_map 中添加多条边,第一种添加方式
! v- a. b1 ]) l" @2 T- e city_map.add_child(folium.PolyLine(
0 D% q" ?4 W; w0 R% y8 r' D locations=points_1, # 坐标列表
9 H; l" @4 ]) a4 x8 Y, F+ H9 i! h weight=3, # 线条宽度6 M& L3 b1 ~8 f9 J& B9 ?* ?& I* F
color=gray))& D8 [' L+ E7 {7 S+ u
0 ?7 A' q3 s+ z) g8 g; x3 e: b$ i: Z, S # 在 city_map 中添加多条边,第二种添加方式
4 k9 a' p% t" ^4 |3 b- p. w folium.PolyLine(
( S& X& A/ Q: s5 i, d# ^ locations=points_1, # 坐标列表
* T+ A* F; ^! y- {5 ?9 y& J weight=3, # 线条宽度! `7 L" U, `; S$ Y. Y0 {, R
color=gray).add_to(city_map)
* [5 B8 }8 J. e z* D' O9 m* m- i; @+ |2 Z6 W% u4 @
# 多边形
- m6 ?. X; T3 A& h7 A$ m points_2 = [
n+ `$ H' `# G5 s coordinate_orchard_road,
' Q+ c# [1 h- W coordinate_sentosa,: {7 x: O* g/ G Y h# B% F) M( `1 O
coordinate_changi_airport: j/ N3 h5 @1 J" Y0 g% ^+ d
]! ^, o3 W. }( J) ]$ b
/ x* u7 W a9 u& d0 p S; F n
city_map.add_child(folium.Polygon(
, C1 Q( h9 L8 c4 y8 ?) D locations=points_2, # 坐标列表; N& y& W/ w$ H; a) |) Q) s
weight=3, # 线条宽度" A) o0 A. C3 S
color=yellow))
: k8 `; S- \- i6 R/ w5 E* D2 ~3 X1 \/ {& @5 k$ F6 Z- u- w9 s
# 矩形- f- d4 {# W% S) Q
bounds = [
* q9 @4 J0 {. j5 k) Z* }! W coordinate_ang_mo_kio,
& p4 o, W% X9 h5 C) U& A coordinate_yi_shun- i5 F; e/ z, k e- ~# l
] r9 ~7 c& s$ W) h7 Z1 u. i2 e i
: @# ^ \& ^; h' Q+ R( } city_map.add_child(folium.Rectangle(
" {! P/ C, K) ]1 M7 Y bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
' N n. s( \4 k' A4 F4 c weight=2, # 线条宽度
3 |# F3 R$ p3 L6 U! K8 x color=blue))+ v* t: Q1 ^6 D
+ |6 e8 H1 x. A+ j3 u* m- i5 I) l # 圆形, circle, radius units meters
; i1 I- N. {0 ~( s7 f- g$ _ folium.Circle(8 `7 n3 e7 b7 \4 M7 d" g2 S W
radius=1000,
7 v; B! ]. v s3 w/ p" B location=coordinate_nus,( Z" D3 p& n1 g e* a/ Z
popup="National University of Singapore",1 z; o. \& v6 T8 F& ?' ~' l
color="crimson",
, F+ N$ |3 z/ ]( Y, N fill=False,
! t$ y* w X9 l5 s% T ).add_to(city_map) C, t4 A% @0 L+ L
% z. F( K/ C2 @6 `; v. N
# 圆形, circle, radius units pixels
$ p) j. B) _- m5 Z0 @7 n7 s0 Z7 y5 b folium.CircleMarker(; }4 s+ |2 O' ~% u
location=coordinate_ntu,. b% T' Y6 c+ f8 K9 T
radius=30,
$ Q I i( B1 J3 c popup="Nanyang Technological University",
2 y' k5 X9 N# B color="#3186cc",
9 c* a' Y- G4 O1 }" q- s/ w fill=True,
) O$ v+ p3 M" @+ O- k2 O fill_opacity=0.3, # 透明度5 B" E |6 h* _
fill_color="#3186cc",& a# M6 T+ \, _8 |! ^+ O" g" d+ Z. ]
).add_to(city_map)
9 |- m9 U H# J+ r( g) t6 u6 p, d8 [% k
: K; ~+ l) t9 J+ K/ ~6 g city_map
' S* B" J2 M7 Y Folium 中的画出各种形状热力图
% p$ P' X5 i* S0 ^* K6 _; o/ W3 A. Q { 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。
* j8 y+ z, z6 O) ]- z import numpy as np# J# m. ~ `6 ^; Z7 @2 p ?
from folium.plugins import HeatMap
9 d' G/ \' p/ C5 {) B. c4 Q# U( k% S) ?- D6 ?
# define the city map/ e( J5 f, Y* A6 y% h6 d
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
2 h k# a) e/ l% @4 H- f+ }1 i; i- n9 @0 k: g0 T, C, M: Q& c
# 构建随机数据
, |2 ]) J1 t q/ [ data = (8 |# E# ^* p& e1 u
np.random.normal(
- J- v5 p# T# f. b size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +; ^& D, m, C3 S% D2 o
np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
+ M$ ]; B+ Z4 b" E5 ? ).tolist()
! Y1 u* K7 c0 l) V4 ~% i# [
! ~7 S: f6 W+ l8 O# S G' v city_map.add_child(HeatMap(data=data))' J* U6 Q8 W/ W
city_map
2 R' R' G* u% |, a% J $ |/ @ S% d% V/ P1 D4 h
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。
4 S R4 _9 V4 p4 }& w2 M import numpy as np# |1 \5 K8 n9 {4 w& n
from folium.plugins import HeatMapWithTime7 F: Y y1 `- j
+ {8 }8 U: ^. d! l # define the city map% X' `6 i0 ^% ?4 q
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)9 z: f$ k( o$ ~7 n- ]
' n( s3 z, P4 {
# 使用 numpy 建立初始数据
1 x0 @9 }2 n7 V4 u) p initial_data = (np.random.normal(size=(200, 2)) *0 {* }) y- M& |/ V; i
np.array([[0.02, 0.02]]) +
+ V8 x; I, q7 I+ Z1 F3 Q. [9 l5 F. L np.array([coordinate_orchard_road]))) b( h* d5 k/ M n. m) L: s6 Y
& J8 _ p" u% Q- M # 建立连续的数据
+ P3 H9 D3 L( f& S data = [initial_data.tolist()]
; t0 F A6 T2 ]& f for i in range(20):
4 d/ @0 \4 c( l" D* l3 ? u/ c data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())
7 s" ]! h) Z8 g" }* O
v' H! O8 `1 z7 G' } # 显示连续的热力图
! `* y0 ]- f5 ]6 D2 J8 z; J city_map.add_child(HeatMapWithTime(data))
) c4 N6 c0 w C8 A* R( u city_map
) |, K% l. u* b( O& f5 a. \! Z
2 }4 k0 h* g* P- M- b, s% Y2 Z 经纬度点的聚类6 ~# {! `5 @% u* m* ^ E
除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
: E4 C( b/ C# {/ K from folium.plugins import MarkerCluster3 a# s8 ~5 O% B, r8 p" A7 v
7 b3 M/ _& A) k2 V( R # define the city map5 x) T X4 k, y% E: N
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
' h1 t5 x4 W0 C S" t
) X e& R, n) p2 ], {+ `3 S( ` # 经纬度的聚类
! T3 r" O! C- q* O4 U" p1 ]2 p # 在 NUS 的经纬度附近随机生成 100 个点;
6 {" B7 r# V8 c6 b& S: _ data = (1 u2 ]9 z3 c5 y7 t$ L+ P8 W% }
np.random.normal(size=(100, 2))
5 l" f4 h2 {% X5 B* O * np.array([[0.001, 0.001]]) +0 \' j1 z8 C* Z6 u* P/ g
np.array([coordinate_nus]))
* p/ \- o. ]6 d& `9 {* g3 N$ s8 e9 q: E* A6 ^/ ^2 f) {
# create a mark cluster object/ X P0 u. o2 _( S0 j# x' x, x& @
marker_cluster = MarkerCluster().add_to(city_map)
3 u% t5 ~- X2 X6 z; j) i
: F- U/ H3 z; X, g% K" P1 _+ ] # 将这些经纬度数据加入聚类
9 }0 K0 \" T4 p for element in data:. \8 P; `/ N: x5 ?
folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)1 D% H3 [* R8 t3 B: H
4 c, U" F5 ?. |+ f' F$ y
# add marker_cluster to map
7 E, A7 i5 D; m2 ? city_map.add_child(marker_cluster)# ]1 _9 B6 b. S" P0 ^8 O
) M; E w$ V1 X& k8 e. L1 X # 作图; |5 o K% G: x" T. ]
city_map
6 J2 U/ T- d& e0 H 5 Y" ^0 d# f" s$ e
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。 : o/ L* j& l+ C) N
参考文献6 x3 \% A8 ^ l
Folium 官方文档:Folium - Folium 0.12.1 documentation
4 Y6 A/ v: ^" a4 [2 P' F# B, D2 r- n+ f; @
" C c) v8 H4 c2 D8 }8 X, C4 e
! c1 x4 o9 i" v6 ]3 s- ~% H
7 X9 c1 K$ ?" \
|