7 K4 B0 `) b# ^5 c/ ~
Folium 简介5 p- k5 ^8 u5 @. O. g
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
3 t8 }& Y. [) P. }+ i 创建地图
0 f- N) w: V: I$ m 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。 2 D( k" h; i8 ]& t6 |
import folium
$ \" z- G: W0 W" W# Q# w1 j J %matplotlib inline1 W4 a' E: x8 K8 K
! f7 n& T- n. D, B- ^
import webbrowser
5 L! O) Z9 Z% a2 A' i) }4 Y/ {5 N6 P. N' A
print(folium.__version__)
, h: e0 W* Q) r& N/ H* u: X0 S/ L- E: _, D" `) ^
# define the world map* `( m# E6 u6 c& G- L* `$ p
world_map = folium.Map()
! R6 @( {- ?+ [ q- H- R$ t% e. _ # display world map9 b V! N# `2 ?$ @1 c
world_map# U" s0 F1 w, n, D
) P, G9 w' p% ]% r6 X- w3 t5 x* o9 d, @ 世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。 % K5 ?; y+ o. o
在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。 4 i$ q8 U! k. v X0 v
有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。 , N) B% i, p3 y' `& |: X
另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。 ) o) ~2 e$ y) }( l% m' X; t
# latitude and longitude in Singapore city
3 S2 ?* I/ C. O: S/ n coordinate_sentosa = [1.248946, 103.834306] U; z5 X1 R4 c8 M
coordinate_orchard_road = [1.304247, 103.833264]' Y( n) ~: w- h! V3 a
coordinate_changi_airport = [1.357557, 103.98847]
6 C. c/ j* r4 M; M# ` coordinate_nus = [1.296202,103.776899]: |+ }- K$ ?5 u
coordinate_ntu = [1.34841, 103.682933]2 q; f6 [/ w: {: D
coordinate_zoo = [1.403717, 103.793974]
: q$ A: k$ J# [1 ?2 ~ v w coordinate_ang_mo_kio = [1.37008, 103.849523]
+ l- s$ q3 m& Q; a coordinate_yi_shun = [1.429384, 103.835028]
, y% l. q e+ s, O/ B
/ k- V2 L0 R& k# q: Q2 L* T$ O # icon
9 Q& R0 }/ J7 e% ]' x2 X icon_cloud = "cloud"
/ f8 f- \9 y- N) U icon_sign = "info-sign"
; E& d$ p% g0 p$ u$ L1 S
) P# w5 N3 c- P8 x$ B }! Y$ } # define the city map
4 O# ]: z, h4 W6 a: p6 | # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}
2 z! j" ^. X: e& n city_map = folium.Map(/ K/ X6 R! A' S6 E- T! U
location=coordinate_orchard_road,
0 e) j' i5 F: l" N' k# e8 E- X- Q zoom_start=11,
8 k( b. K3 \2 M" l tiles=OpenStreetMap)
% V0 b" z4 ` _& n% w' H" s
0 h" B9 X8 G* z+ R9 s4 x& Y # add marker in the city map
1 Q3 V: H9 J0 `& t! w: M. ~/ V folium.Marker(
m0 q" c1 B) n8 K5 s coordinate_sentosa,/ f! F I% c: d) m& ^ c, j! ]
icon=folium.Icon(color=blue)! {& u$ O2 U0 p
).add_to(city_map)
0 F' F5 |, u; d9 t. t# M folium.Marker(
$ D3 v* o+ ~0 X7 F. }- Y coordinate_orchard_road,
9 T( e" u; l' y, X7 L icon=folium.Icon(color=green, icon=icon_cloud)0 k- s' v5 ?8 y {
).add_to(city_map)
9 \5 Y8 l G$ u' w! N T3 K" u5 R1 H1 n' j
# add popup, q$ W) v) h1 ~0 c7 |' u" C
folium.Marker(6 k0 h8 a' H$ H! k
coordinate_changi_airport,- s* i# F. e0 }5 l
popup=Changi Airport,
/ T1 q- F+ {! l" x icon=folium.Icon(color=red, icon=icon_sign)
2 @! r7 o: r# ?1 | ).add_to(city_map)) y- a- K v% d8 I: N4 Z" e+ l" X2 _. H
( `+ T/ J6 C3 A7 l& y0 ~
# add tooltips and popup9 C2 A1 ~: e- a: e/ a% D! i, h! F, m
tooltip = "Click me!"* w+ E" g. E* D8 C: J, k+ A" S
0 x. g, v: x3 g, K* }% j$ i folium.Marker(# V4 T+ r2 K* `- \( N2 t5 W
coordinate_nus,
) Q. V5 [8 @& R popup="<i>National University of Singapore</i>",3 o" G2 Q3 p- U
tooltip=tooltip
& K6 B, Q6 a! \2 |+ @7 w* p ).add_to(city_map)" ~9 G6 F* p5 L) ~
8 a' b' ?2 R+ J( F9 S
folium.Marker(
! s7 C: R* F7 b" j- ~2 V" V6 R9 A coordinate_ntu,
/ n" g; c6 F+ E- m: X# ]" y, l popup="<b>Nanyang Technological University</b>",
& Q+ [9 b& c6 j2 S tooltip=tooltip
0 S0 G: ~2 ?, _ ).add_to(city_map)
3 O5 }* A0 B& v& Q* k1 v- `1 C) ^8 X! X) a- ^. l0 v
# display city map
$ n7 a3 p& B2 V3 B) E. c city_map
& D |7 L* {# m9 H6 _/ X5 p& | Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。 , B+ J0 n& v" s1 Z
# define the city map
; U9 Y) S g* R3 t8 n; { city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)7 l# Q7 _7 h4 O' B8 s
+ s# o( L, y# c$ L E1 g
# 在地图中添加经纬度, add latitude and longitude in the map when click- ^5 l- e( [" k$ Q7 m% m. b. r t
city_map.add_child(folium.LatLngPopup())
; C3 ]$ P ]5 W; w" `, Y( t5 C2 j( F7 v( Q. R) k" t3 h* n
city_map 5 D/ k0 @) l1 U! F4 t
1 s8 i8 H& _- A; E! k
几何形状
Y8 o. K. z* H- o 在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。
- {: L* A! b; l4 Q0 Z& S # define the city map! B1 p+ Q8 `# M( ~" X. [+ Q
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)5 v* r j- p5 W4 E8 i
1 V8 e0 h$ d& L; U# G* F
# 在地图中如何添加形状
1 n, }4 e# _) B9 j # 多条边$ a/ X& O& i3 w
points_1 = [
' r1 E. V& y8 i, |! L coordinate_ntu,
# P1 L* I+ T1 T+ N coordinate_nus,# z; F# Q& I! h5 s! U) {4 c
coordinate_zoo
$ x2 T; h1 V1 |. S ]
) T6 J4 g3 y7 Z0 y
5 ` C* g0 X& l # 在 city_map 中添加多条边,第一种添加方式: [6 G0 `$ M8 W8 q4 V8 A7 c" J
city_map.add_child(folium.PolyLine(
5 R: J3 b7 P, ~# D/ \ locations=points_1, # 坐标列表0 |. w& d$ g* T: z: r. ]& x
weight=3, # 线条宽度+ `: W! S: {; Q0 C3 L: }
color=gray))
: |# p# N+ g+ A; j! q
; a# F- s4 H$ [' q3 g/ E6 x) N. V# w # 在 city_map 中添加多条边,第二种添加方式% d0 {$ A: F% x5 M% F
folium.PolyLine(
: C# r" o4 s3 R: a X0 g locations=points_1, # 坐标列表& S9 c6 O- j. @
weight=3, # 线条宽度+ o% p7 O% I$ V
color=gray).add_to(city_map)9 _, H/ j( r5 p$ y+ Z( @3 W( Z
! f% }" j4 P o+ F' w2 T" B2 R8 A
# 多边形! x3 Q) i" ~8 {0 J& G
points_2 = [
9 R/ y1 c1 ?7 q q coordinate_orchard_road,) V' f9 J9 p* ^' F0 _+ [
coordinate_sentosa,' G3 H% e3 ^% l$ }* u5 z& s
coordinate_changi_airport7 V. k, v9 |$ v/ ]2 E/ q
] \% v* L; c; j* q
( [8 K, Z6 h# A2 K
city_map.add_child(folium.Polygon(
" [0 {9 B9 A9 X3 P' B locations=points_2, # 坐标列表) E+ {9 x1 G: G. q+ v
weight=3, # 线条宽度
; R6 N+ ^/ j; {% {9 J color=yellow))" f. X) c8 G7 K
% s5 R; p1 H7 ~! ~3 Z5 i6 e" o$ ^ # 矩形5 Z& { C3 d+ `; e1 I: P2 i/ e5 v3 d
bounds = [0 ]3 G7 N2 p0 Q5 p
coordinate_ang_mo_kio,
" B! s: n8 V T9 C0 S coordinate_yi_shun9 @' K1 V1 S# C+ X1 L' |
]7 x6 ^3 @9 M1 R8 U) F0 X' V
- L& B! z D) o- N4 X1 G% g city_map.add_child(folium.Rectangle(4 }* ~/ v V% O+ h, @& C
bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
' O- Q( g \. d0 \7 Z% k: h weight=2, # 线条宽度: o% Y6 V& ~: q* r: ^
color=blue))' F2 i) V; V, m& `: }1 D; S9 O( I
8 [/ ^/ v4 S& P$ J' o # 圆形, circle, radius units meters
, L: n6 A ^9 {' l" j folium.Circle(
. M0 Q A& {. g radius=1000,+ i6 J0 ~ A8 y3 U, A/ G5 u
location=coordinate_nus,
a* q, q, f% u. p- k popup="National University of Singapore",0 S* l0 s( U) g% T6 T
color="crimson",
% T/ }- J7 H5 e5 X5 F fill=False,
" z# R) I4 V7 r3 L1 e. Z1 P' X ).add_to(city_map)% j" W. \5 |8 c: q/ R9 Q# _
7 f3 {# W. H' x # 圆形, circle, radius units pixels/ w( o, a5 Q8 D6 W! O5 Q) b- ]
folium.CircleMarker(; d {& W' ^8 ?
location=coordinate_ntu,
! s g, {' ?3 G, i4 | radius=30,
1 W9 u. l' T& \4 h1 [" L c popup="Nanyang Technological University",- u/ F+ Z( M7 E( G/ ?5 p7 Z8 Z
color="#3186cc",1 W+ ?/ {' L( T. D
fill=True," m* |/ S9 q7 u$ {3 o3 q
fill_opacity=0.3, # 透明度+ V v9 Q5 {2 G* d0 X
fill_color="#3186cc",& r g+ u W$ g0 ` O
).add_to(city_map)0 D) I! [$ X. ~: t+ @
0 ?1 k3 \4 I; [3 H/ }) w city_map 5 X/ \" A/ E- \: m `" R; A9 y' [
Folium 中的画出各种形状热力图
2 H4 B, V! c e( s2 b7 Q5 B 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。 7 d: c. e) `8 I& |6 \$ d* S3 g: y
import numpy as np, Z9 u# ]: z6 E0 ^! ^* u( X1 h$ B
from folium.plugins import HeatMap
( q) g1 o. F8 S% Q$ k7 u) P- ]) t) G1 O% M) |5 M
# define the city map% ] N* V2 N6 d. I
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)7 `4 H3 U8 @" b' A
0 t$ Y1 B# _0 `" n& R
# 构建随机数据. Q( H0 y+ D/ V! x4 G9 a
data = (* ^ h( h) o/ @8 d A2 g
np.random.normal(
8 a" r0 r1 g- h7 M2 e! m size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
* W# f# n& b) x0 m# r np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
! E4 w4 _+ a; N! a( y ).tolist()- X! J G$ e# F" [$ c/ V& C- ?8 y0 G
( v5 Y1 A, }. ~- S# y
city_map.add_child(HeatMap(data=data))2 W' s% q+ G6 U
city_map
$ \: i1 l' k! @ j # @+ w8 N7 n9 q; ^" N% c3 [; J
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。
9 F2 b' P, |. |. D5 w import numpy as np3 s% N3 S. L2 h
from folium.plugins import HeatMapWithTime
; K- x, c: `+ F, f& v' V3 D) _- Q$ u8 i+ g& G! q4 i5 K1 z; D
# define the city map# n8 g; ]$ _+ O
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)( V2 O! b1 ]7 a. O: J: |
, i: L9 C) [+ e
# 使用 numpy 建立初始数据
& [/ i- P$ k- D* r initial_data = (np.random.normal(size=(200, 2)) *3 ~* S3 B$ ?5 p: c- W" H; w7 B
np.array([[0.02, 0.02]]) +
; O+ N! ]# }' U( b5 u np.array([coordinate_orchard_road]))
( a/ p2 s( Y! G
5 ]1 ]3 D' ?5 q( @# w% y+ a E, K # 建立连续的数据1 r/ A4 E, J% K4 H7 H
data = [initial_data.tolist()]8 ?- E' c; c+ u7 c- r
for i in range(20):
) K/ _0 O, P! R) n data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())
i( J( g1 T4 m5 W- K' h' R& O8 X
# 显示连续的热力图5 f T$ M/ R% {- `
city_map.add_child(HeatMapWithTime(data))
- O4 Q* Y) P- \ city_map 0 B- m- A- V+ k6 e: |
) V& f2 y9 {, } Q9 ?, r2 v+ N
经纬度点的聚类$ ?! N+ N& P2 N
除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。 9 I7 c; |+ ?3 B; U4 Z( Y7 q
from folium.plugins import MarkerCluster. w, [- V. V' {+ ]) \6 @2 T
" w% Q$ R# y7 M5 G8 d7 n' B
# define the city map* O2 q- j1 u% w& y+ w
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
8 Z( }' T8 m2 W/ K
2 Y; X' x3 ~8 v! e # 经纬度的聚类
( q; `1 \% b3 J. O- l # 在 NUS 的经纬度附近随机生成 100 个点;8 N2 X0 V: G* X! S* ], y- N
data = (% v5 E9 d7 e; r. A
np.random.normal(size=(100, 2))
' b+ r1 [8 f* K4 \0 W * np.array([[0.001, 0.001]]) +/ ]/ Y: _' o" U6 s) m
np.array([coordinate_nus]))3 U* [$ F _1 F* u
7 Q; K4 [* `* K2 ^5 ~ s # create a mark cluster object, y( c O H3 ^9 x1 u2 A( o3 j
marker_cluster = MarkerCluster().add_to(city_map)
9 N* d( t' c& [8 Q9 W2 c$ y. w- G- h8 u) V: w4 C
# 将这些经纬度数据加入聚类
/ h i; k M) t5 C for element in data:
; K4 J3 S( ?$ Y folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)
. V1 h: O* o" ]& f- i+ I
! A+ M% ~1 I6 E* s5 B, ]1 E8 F" f6 j # add marker_cluster to map
8 h9 ~9 z$ F' ~ e6 |/ T4 P city_map.add_child(marker_cluster)! a. e; w# q' }$ T R; m6 K
: ~6 w* X9 I% g- T, | # 作图0 K3 y+ F; M& t! c# E+ P* e( u
city_map * z9 h4 `3 V- e/ y/ W# i$ T) M$ g
) I9 d" a0 n! M) y5 A 以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。 % V2 B6 v3 P; t/ ~+ `
参考文献
% D2 V( n( B, X/ \4 h Folium 官方文档:Folium - Folium 0.12.1 documentation
f3 u- e2 N- r/ | r
, K1 t2 b3 [2 P# p( N' J# t2 j: A" d
6 v# ^8 R0 n: a" y/ E2 u% @# k9 O0 e. r( N4 l# q9 T& U
|