+ \" Z% w7 T- B5 G' h& \# ~6 j$ s Folium 简介6 _/ x% g& l# b f9 d
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
# o! \2 k( s2 d6 {% U. E 创建地图
' O2 Y& u# i, P' p3 ]% z 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。
4 P ~/ _3 u5 M/ h import folium7 K/ E6 K0 T+ p% @
%matplotlib inline
1 O) F c9 B" P9 G
( m) C2 N+ q5 H& N0 N4 } import webbrowser
|, @7 z& R$ L7 \/ e; @+ G9 D
1 O) o5 k7 g0 S7 B; i print(folium.__version__)
+ p2 b, {/ k4 N5 J& x# a- Q* M9 u& `$ f+ C2 J" o0 D
# define the world map
% ]1 [3 D+ T) m& _2 l8 _* c world_map = folium.Map()
4 i1 Y! h1 f: G/ `' w; z8 p # display world map" k, A! L, i! E
world_map0 C, F/ u$ K& L( H
! m1 E3 \5 z% l4 Q5 [* V2 u( u+ K3 [
世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。
' e: P+ E; @! |+ T 在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
- [( o# D" P3 M8 t6 V9 V1 D! l 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
$ U9 R# @+ c. G: f* s e) W( ? 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。 " R) c0 H) w0 L" f5 D6 ^* ]
# latitude and longitude in Singapore city
' \( G# L6 W! U) }" V2 f coordinate_sentosa = [1.248946, 103.834306]
8 f) e% _* U2 E _ coordinate_orchard_road = [1.304247, 103.833264]
9 `/ {6 U: c* y' x coordinate_changi_airport = [1.357557, 103.98847]
2 O/ i( |# V: f& h' B& W7 d coordinate_nus = [1.296202,103.776899]
7 `/ t/ t7 {0 z5 T2 P( c9 z$ x: F% y coordinate_ntu = [1.34841, 103.682933]$ T9 P5 u8 _8 X9 I* Z
coordinate_zoo = [1.403717, 103.793974]
) r) p7 s0 O/ d# a& h5 W! J* E coordinate_ang_mo_kio = [1.37008, 103.849523]0 r# D# P6 a0 \# @# w0 a& z
coordinate_yi_shun = [1.429384, 103.835028]$ c! l6 d# e. E0 a! E7 Q6 u. h
- P! D% |( {9 M2 Q2 l' b # icon" P A3 V5 ^# |6 R# P9 N# ~
icon_cloud = "cloud"
1 Q" d) r0 x/ p icon_sign = "info-sign"
1 ~$ |7 y" k: @; A) r' o, T" _# ?. L) [" D: Y) {3 o7 J/ g
# define the city map8 \1 W1 e( E7 F
# tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}
9 \' f* m8 A+ N city_map = folium.Map(# M) Z7 r1 N% v1 w8 D8 U
location=coordinate_orchard_road,5 V0 p. H2 m: [7 E" B
zoom_start=11,
' a. \% W4 O2 N f+ N7 r tiles=OpenStreetMap)
& A9 I0 e! x2 g* F, K. n; v
1 I# |; z: P" \+ l0 W # add marker in the city map; _+ [% _' P" v! J% j
folium.Marker(
( ~- z3 U, J: }( o* g coordinate_sentosa,
7 `; s0 x- n$ X6 A8 {9 @ icon=folium.Icon(color=blue)
/ H I! Y- H) p/ I+ z0 A% J ).add_to(city_map)! R \% b& |0 z# B5 [/ k
folium.Marker(
( e/ I8 l# P- c& y coordinate_orchard_road,
1 V e2 e* t, U# P2 p8 D0 n icon=folium.Icon(color=green, icon=icon_cloud)0 J8 |" [5 z& o5 u
).add_to(city_map)
5 V& Y$ _+ `9 {; r, M$ S+ ^/ ~4 K9 t1 o/ |4 ^
# add popup
+ T- d X& q% z9 S5 w5 J$ ] folium.Marker(' V5 E: d# R0 n8 S: x% D9 x0 l
coordinate_changi_airport,+ p) T# w; _* k: }3 d! ]4 i* a
popup=Changi Airport,
5 c& \7 z1 a/ Z4 N icon=folium.Icon(color=red, icon=icon_sign)3 S4 q& s) Z7 M [- ^9 s
).add_to(city_map)
! u9 ]0 y7 k& O/ v7 f! T3 l8 }3 S0 b5 F. l) c- E7 Z2 F$ y
# add tooltips and popup
+ g' L9 @ u0 n* O# _5 D6 t tooltip = "Click me!"
, b; [0 G3 Q6 c7 \0 y: X8 X9 T! P( g7 o8 N1 y2 ? x! i
folium.Marker(4 S! h# o* I; X3 p& U" [
coordinate_nus,5 V, N, S( X! O& G& U$ V
popup="<i>National University of Singapore</i>",% M+ S0 W. M8 X) d6 @" z3 p
tooltip=tooltip$ p9 Q5 r: R! a$ @- W
).add_to(city_map)$ v# f1 D) L3 J1 h3 E* ?! w/ z
# H; B+ \" Q" ?( {
folium.Marker(
: j: C1 ~+ m$ C* D coordinate_ntu,
- n- `1 d; b2 ]2 R8 f popup="<b>Nanyang Technological University</b>", L4 p! i; a( D1 _0 E7 c; Q
tooltip=tooltip
/ J# r' O" W5 Z8 q& C% b+ g ).add_to(city_map)& z3 I- |" a1 I2 ?0 H* ]0 w
: W2 b S) ^8 n
# display city map
7 ~: F# f$ o/ l city_map 9 x0 q4 P- s/ c6 L, `, i
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。 ' N. C1 N9 o D2 \$ v
# define the city map* J l$ n. |% J# t( l& p7 h
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)) O: w G/ S& e7 \
3 Y/ ^. W' x. M8 p+ {, I # 在地图中添加经纬度, add latitude and longitude in the map when click
4 [' ~1 `! q$ N3 w4 w* V city_map.add_child(folium.LatLngPopup())
$ x1 K( `1 i- W8 _& u8 [7 p. V2 q3 O L3 X
city_map $ T3 @; r# J' p+ h3 U
$ J" E( w& B/ H, ^ 几何形状9 p" j1 i" `3 M. `+ A
在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。 6 x$ h* y6 _& t N
# define the city map
# J% F% I5 Z5 [+ u city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)7 y8 a" m p% p7 f$ I% l! o
F3 }; y6 A( o/ k; F5 o # 在地图中如何添加形状
8 Z7 K+ Q' F) r: n' J # 多条边6 J3 j! R) g7 T! Q' ^
points_1 = [* y, W* u2 B2 v$ ]! }9 L: ^
coordinate_ntu,
, J% u5 Y- q7 L8 W Q: B coordinate_nus,6 \" d. r- ^0 |+ y. [ t
coordinate_zoo! |6 I2 b/ ~8 H. C% H
]" N4 r1 A4 Q. A1 X$ k
& k9 A- I! p, G! ^- X; P # 在 city_map 中添加多条边,第一种添加方式1 @1 I7 a' s, B' a, r' \' h
city_map.add_child(folium.PolyLine(
, O' Z+ f( y, J0 \7 q' | locations=points_1, # 坐标列表$ a; p9 P" ~3 {) d" d
weight=3, # 线条宽度. t+ U9 `$ W3 o; ~
color=gray))
( P! i0 [0 J8 Y% N; m) L2 x1 i9 l& v
# 在 city_map 中添加多条边,第二种添加方式
2 Y, ^# G9 H8 B% U2 K8 ^ folium.PolyLine(7 {& a8 Q& ^2 O1 q* y
locations=points_1, # 坐标列表/ V4 F$ k! \' c& @$ K- \
weight=3, # 线条宽度
; q/ u6 h- R( A$ s color=gray).add_to(city_map)8 Z* A+ m+ s+ S2 G
$ ~8 k. s3 v2 i. `+ @9 j
# 多边形
' p1 u; e. ?' k. @& j$ l points_2 = [
0 p/ }( c2 {% Y6 E: r2 Z, S coordinate_orchard_road,
8 U L& Z w' F% r9 k, s- N$ O7 W+ U coordinate_sentosa,
F4 a" h, k. I9 O+ I! o% y# k coordinate_changi_airport
8 I. E8 n I+ l Q: b# |+ z ]
4 e; l/ \% a2 V7 N' O# J/ ~
5 f2 J7 |$ ?7 Y% e4 [6 n# K city_map.add_child(folium.Polygon(, O1 v- U/ c8 S' B
locations=points_2, # 坐标列表1 ~+ c6 n# ]8 T8 j8 r# U+ q* |
weight=3, # 线条宽度$ j3 Q. M! C1 @( a& s3 |' E
color=yellow))5 W* h/ `# g; l2 [0 {1 V
$ g' n% N8 u; N4 [4 x& i+ T # 矩形
) F) p& V! z9 j- F bounds = [
2 G4 I7 S1 m ^: H8 m coordinate_ang_mo_kio,
0 |6 l S/ J# y; C coordinate_yi_shun
. Z" ] e* d$ M( M ]
2 n3 }2 O1 f; j' B8 E w' T b3 g
; A7 _. n) ~, _$ y0 x7 t city_map.add_child(folium.Rectangle( `; e) H+ a. q7 U3 I
bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
* z) R: x q. d; a weight=2, # 线条宽度
7 Z9 \+ W; _$ X; C5 E color=blue))
8 l; ^- ]+ n9 ]! Z G5 \4 G! i" e0 c" v$ N V9 C- [
# 圆形, circle, radius units meters
: z& v$ L' _7 ]3 b) m8 c/ b folium.Circle(. H+ w; `/ t3 ?
radius=1000,
7 s3 m1 T$ A6 o% Y" H; n( Z: q location=coordinate_nus,
. |: ?9 C7 L) ]# W3 W( Z popup="National University of Singapore",
# \* i+ K8 J& [, v6 [ color="crimson",1 R' _4 O2 w1 X; A8 k. s
fill=False,
y+ P" j4 @6 z1 Z% D2 ` ).add_to(city_map)
/ C, S, n/ X+ o/ _4 {+ ~' l) J) @0 _+ V* N! a7 a% _
# 圆形, circle, radius units pixels& k+ U# X* q. i# z3 `
folium.CircleMarker(
5 C( u7 G4 W+ e; f$ H location=coordinate_ntu,6 ]" q( y/ T8 @$ X
radius=30,
4 Y4 `' Y- F" t6 f& n) j1 M1 x popup="Nanyang Technological University",
& m$ q- {8 b: P. C$ `- V6 n color="#3186cc",
5 C; U* y" k' ~, e% t9 E0 u v fill=True,
# |: x K6 K9 c6 @+ T2 l& f) m/ i' q fill_opacity=0.3, # 透明度
: a- j1 W$ [" d7 |& H. C fill_color="#3186cc",
7 ~: J7 N' I! q G7 p( q# k2 f8 j ).add_to(city_map)9 y9 N$ E( b% q5 m" h# q! ?
" M5 N* O1 n, _' P- _# V2 T) J9 m
city_map 7 i: E( o3 v2 K: k- F
Folium 中的画出各种形状热力图
b5 c) K- F- ^! {' X 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。 0 ~& V/ B }6 X9 |
import numpy as np7 u& f6 I- T7 u/ N% m6 A
from folium.plugins import HeatMap3 A( {3 b% N- R
: Z; I5 `- u; v6 F% M
# define the city map) D# r* E+ M5 }3 L1 @
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
5 |, D# U3 \2 Z' B0 P+ T
) B. s4 [" K8 O; b% p) h # 构建随机数据
( a4 Q0 J3 D8 ]$ c data = ( p5 l; W- O& n/ I
np.random.normal(8 H. v ]; s5 i
size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
) }5 o& q- ] b np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) c! h- w" p1 c1 D7 x
).tolist(); T5 a" F' F( C1 ?1 ^+ K; _
7 D% I2 G! ^7 c: @3 ]0 j4 x( g
city_map.add_child(HeatMap(data=data))' T |, e# b8 V" k0 E
city_map
# ]2 R6 n/ ^# m- G2 d) }: L2 O: F9 R " ?% H" ]: h1 @* K. B7 z
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。 3 S4 M$ G C& Y' I
import numpy as np
1 `- X: ?* W: P' z* M8 n' j+ }/ p: P from folium.plugins import HeatMapWithTime' B. y. T0 v- c
& t' O4 j# q; [
# define the city map. @; {( u& d) O. H, x
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)- B8 [7 h$ d$ X0 |
: [* A1 F) X) {7 S3 n" p
# 使用 numpy 建立初始数据* j1 L* v$ [$ F- X/ [
initial_data = (np.random.normal(size=(200, 2)) *! \" J; i& [. B+ ^* |1 K6 z# O
np.array([[0.02, 0.02]]) +8 w7 B/ A" K) W( {4 P$ R$ |
np.array([coordinate_orchard_road])); D$ j- C* w: x& F3 B9 z$ A5 j
$ Z* o( H# c, b/ h # 建立连续的数据7 E6 X/ E# {- `% r$ {9 o9 o
data = [initial_data.tolist()]8 `% `0 R- b8 ~) ]
for i in range(20):
* k1 l# r( a& ^4 x) ^" Z5 T data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())7 T3 `/ T7 O: ^4 s, G
% r$ ?2 j- B# F9 k* r2 v # 显示连续的热力图
; {. b$ m# K$ _' P- ~" \, D. B( p city_map.add_child(HeatMapWithTime(data))
3 i' W8 J& x, I) G% P city_map 7 Q( d4 K: Z% r& S6 K8 T2 G2 n
7 M! Z$ N5 [' |! h( D# f: B, K9 W
经纬度点的聚类9 x) Q) l4 O+ N# V2 K5 D0 T+ K3 d
除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
+ H3 B$ K% x7 S% Q7 _* ^$ c8 k: m from folium.plugins import MarkerCluster5 j @( |' [4 f: I7 C8 U
. M% J5 q. f6 [3 P1 ?
# define the city map$ t* R! W6 \- N! E1 ]" n
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
3 V/ P5 V$ Y; r2 w. {" ` A! W4 V/ v) l& u8 a1 t+ }
# 经纬度的聚类2 U/ x$ M$ c' O: _! @
# 在 NUS 的经纬度附近随机生成 100 个点;
' h- d8 b8 n& C data = (7 {! n$ W$ y; m: C. Z! I6 Z
np.random.normal(size=(100, 2)). ]- n& v! Y, H% m& L8 k1 L2 }
* np.array([[0.001, 0.001]]) +
9 R y+ x) D7 h' n9 V3 q [ np.array([coordinate_nus]))
x0 r/ ^) I6 h8 o/ N4 g/ o7 j6 @7 u: u
# create a mark cluster object( U+ T2 A r) y5 G. @
marker_cluster = MarkerCluster().add_to(city_map)
0 h+ u) w! H2 _2 {, e6 u% Y8 ]2 Z
* m0 k) m! b' P# ]4 Y/ c% b: E8 o # 将这些经纬度数据加入聚类
) H& X9 T& m: X4 x( q3 u for element in data:
# l. B+ L4 Y1 D5 J& x1 [6 @4 v folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)7 N9 S$ M( e+ f6 t1 B4 e# x
. r$ F. y9 O; @/ Z
# add marker_cluster to map
6 t* F, P6 }# A+ I b( [ city_map.add_child(marker_cluster)
8 B2 S; i) _! D% i0 W5 D1 S- v) D- A
# 作图! Q* O6 K$ ~2 l3 G J* W
city_map ; {: c" [, P) N! t A
! r+ r) {+ o2 @1 J5 [ 以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。
- Q. C/ p6 B1 \" R8 M2 j 参考文献! A: x0 f$ h0 m2 ~7 |3 ~6 n
Folium 官方文档:Folium - Folium 0.12.1 documentation $ g% W" F4 p; }0 O- P0 I( H
- F1 a" K# e6 I6 L6 U8 [
& \/ j4 |6 U& Y8 X% Y$ U
: S4 o7 F6 N$ f( f0 i- W4 x+ I( j2 H+ Z& A6 {; j2 O" I
|