: u" k) c k0 y+ P
Folium 简介+ h$ c: b/ \3 \
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
9 c- e# D7 A+ l* U7 d1 C5 h' ] 创建地图3 f" @) E9 W8 n5 c, y; u
通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。
0 R, w& f% g5 c, y3 l- Y2 \ import folium4 v2 k% C; {+ k# a
%matplotlib inline/ M4 X A- ?9 d8 X/ \1 e5 J
8 f. D! F/ ?* S% M$ k/ G import webbrowser V' I7 ~) ^) x; s& H7 r
, U1 j2 |9 O. N+ L; z4 _
print(folium.__version__)! w, q9 I1 ^0 f
; [& i" @1 Q8 ~5 ~0 l # define the world map
" t# w4 H% F# @* [; w world_map = folium.Map()
& i' s" ^' h+ G) G # display world map
0 x3 J& Q1 A/ E' r- u8 C world_map* g1 X. C1 b4 d h! i% i& Q
/ K7 h3 ~& r) c3 q 世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。
, Z* e @ F) P! V" u4 s 在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。 % U9 n5 ^. L3 U7 |& [
有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。 " x$ a& a) ?% w9 v
另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。 , Q0 B9 h5 C6 h
# latitude and longitude in Singapore city
! U) f7 k5 x! x* j- r) V coordinate_sentosa = [1.248946, 103.834306]5 Z% h9 c. f) z$ k; ?
coordinate_orchard_road = [1.304247, 103.833264]
' ~0 e5 J/ U% ^5 R2 W9 g" t* h& W' t4 T coordinate_changi_airport = [1.357557, 103.98847]8 y% o* z9 w/ I3 q( r# x
coordinate_nus = [1.296202,103.776899]4 {. S! R( V3 O. D
coordinate_ntu = [1.34841, 103.682933]- e) g P$ V5 T8 e& O
coordinate_zoo = [1.403717, 103.793974]
7 w. @$ u4 d. t3 g1 e' S% C coordinate_ang_mo_kio = [1.37008, 103.849523]6 m* k7 L- q3 f$ n5 `
coordinate_yi_shun = [1.429384, 103.835028]5 l9 M8 Z2 k3 Q& ?4 L
* X# U1 ~+ a: M, `3 ~( l$ E+ j7 P2 f. S
# icon; W6 ^! ^" t/ x7 s) w, Q
icon_cloud = "cloud"
, F& p6 i4 P T6 l$ W- A* g& |; m icon_sign = "info-sign"
6 ~ o2 x6 s Y" \% F# M/ W
8 w8 \" U2 u. v# \7 E # define the city map
0 A- y' L" R; Z$ v( c/ M z # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}2 }2 s! T2 r# y
city_map = folium.Map(. |4 z$ m$ {9 W9 q" D B
location=coordinate_orchard_road,6 K6 w. ^. Z% c: ?+ ^: r k
zoom_start=11,) [, C3 v; D: H& T9 H$ n
tiles=OpenStreetMap)# U% t' u: p) @/ i; T
: U2 v! \ _1 e
# add marker in the city map
: M7 H+ I/ [7 [6 _( `. {5 T, ^ folium.Marker(. ^9 J( C2 q% ^& [ ?# K
coordinate_sentosa,- ]( S7 M" P" N2 k$ ~5 Y( ~
icon=folium.Icon(color=blue)7 _3 C1 N0 d- @2 ^! w2 b6 z
).add_to(city_map), b0 ?+ [$ s4 q5 [9 l" d
folium.Marker(
8 O" ]1 Q* J, K. l' s' I' k1 _ coordinate_orchard_road,5 x2 O& z* h1 \4 p+ I5 C
icon=folium.Icon(color=green, icon=icon_cloud)* c P0 s5 N( G4 {" a) I
).add_to(city_map)$ y8 ]5 B$ s- ?! H
5 R6 s6 @- {4 S T0 n8 C
# add popup
7 P0 ?. R3 {/ G# K# E) P folium.Marker(- d. `( S9 B, v/ Q# E
coordinate_changi_airport,
) F' _ q- [# X# V6 b; B9 i popup=Changi Airport,
! B0 J b6 b8 p1 N u! R% H; d/ B. H icon=folium.Icon(color=red, icon=icon_sign)) b3 _, I+ i* E" @
).add_to(city_map)2 L0 `( g0 V( _5 l7 P4 o) [6 ^
* n u6 J" [' z! Z# |
# add tooltips and popup
4 Z- z. s6 @" I, E+ r6 @4 u tooltip = "Click me!"
6 x( Z5 q/ Q- `1 `
- d7 f% Q% ^1 p$ s t2 D0 }. t folium.Marker(
8 m" ~& p7 T3 M4 T& j% i coordinate_nus,% D; y/ a4 G: q
popup="<i>National University of Singapore</i>",
% u; w% ]$ |8 J3 t7 u tooltip=tooltip
& O5 d5 }. }! D r ).add_to(city_map)
( V- j1 U$ X1 l
$ w7 ^ J, o& u9 { folium.Marker(
3 z/ T5 x A0 V6 r N0 J- e9 S coordinate_ntu,
) G# B' Y+ d8 ]# {- e9 G- R/ A" c popup="<b>Nanyang Technological University</b>",7 J4 J' x- b2 L) _0 q; P* k
tooltip=tooltip3 d; P2 |) u8 N8 `: q3 h. Q
).add_to(city_map)% t) o l7 g5 z6 J! ]
8 r# o2 f& f3 Q2 U( x
# display city map# T9 A1 ]' m' h* A
city_map 8 ?+ d% a+ [" ?1 t9 t k1 y
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
4 U1 W% k, S$ _/ o! Y A # define the city map+ i- J2 }2 w2 w- `( F
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
, \0 G, J, C2 Q8 o/ r- d3 d7 J4 O0 c' \ }; V4 d& T# q
# 在地图中添加经纬度, add latitude and longitude in the map when click2 G [6 i, q7 ]. M9 [5 @; A
city_map.add_child(folium.LatLngPopup())& a# o+ ~) t' Y
9 Z# B8 E5 u: z2 M
city_map + Q8 x9 [. U5 r$ j8 n; V
' `2 ~, f/ T. s4 {+ |. ~5 d1 I$ l! R 几何形状
* t3 _/ S' x% ]! z5 l+ g 在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。
% `" q, O# k. { |" {5 w # define the city map
, D$ ^0 f- W# b7 o! L+ j city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) c( u7 q& T X9 H7 K! C* H0 _
7 A* x$ m9 f2 w7 y& m Q* e # 在地图中如何添加形状4 ^# E& a3 h0 D& M6 ^ G' v5 E* F
# 多条边
# k; {! p- Z5 s3 ^' L2 a points_1 = [/ |; G! q# X- \; H+ ]
coordinate_ntu,& @2 `: q# ~+ y" S# M% ^' f
coordinate_nus,% t; Y5 l0 ?1 _; Z
coordinate_zoo
& {8 d- R# A* z/ l ]7 @$ ]3 w; o) L1 h/ E% Y
# Z" F/ g. Y! d& G
# 在 city_map 中添加多条边,第一种添加方式8 ^/ v( n& Z5 X% w& H9 ?
city_map.add_child(folium.PolyLine(: `& H" G) x! B6 p3 X* H, k- x7 C
locations=points_1, # 坐标列表! J( m" `) b1 I! m1 ]' x3 ^" k& ^$ O
weight=3, # 线条宽度
" G. G; ]% M; N# W+ z# d color=gray))' `5 D3 L7 r k8 [3 l* d; f
' j! A8 d$ {% p. v7 C # 在 city_map 中添加多条边,第二种添加方式
% {& D5 L6 n' ^# l/ O( m folium.PolyLine(' U/ X' m% w7 m0 C
locations=points_1, # 坐标列表7 W+ ~4 }# J/ |4 Q
weight=3, # 线条宽度) G9 t' {$ F- O4 r9 C$ i. t7 _1 E
color=gray).add_to(city_map)
; o( [ l( o& L$ e" m1 Z5 C
6 j) v; \5 ?- J # 多边形5 Q/ t) j( N' j, @/ t. i
points_2 = [* \3 f4 G7 r8 t L% ]/ E2 O" D9 \: P
coordinate_orchard_road,
0 x3 y9 L6 G& H4 F coordinate_sentosa,1 n! m! o, \5 j5 O
coordinate_changi_airport( W1 K9 {, r; X" E" [6 a' q2 l& I
]' r: t4 u1 u$ d8 ~% c3 m
. t5 l, Y' v4 J {, a/ _" A
city_map.add_child(folium.Polygon(
# B& O: q" F: L+ D; l( S/ Z locations=points_2, # 坐标列表3 E3 q2 ~* h4 m2 [& r2 p' @
weight=3, # 线条宽度
% K- Y% B1 G1 C: k* V2 z. u color=yellow))
, y) U- |; r- ~" |4 ~2 C% q
' ]/ o: D! n& l- Y # 矩形8 j- ]; }! F2 j
bounds = [
- E+ z7 a+ j8 x# l% p- m coordinate_ang_mo_kio,
( z% j- C( F& ]# E' v4 i coordinate_yi_shun
( u+ f3 v8 l. k2 B) _1 I2 f ]
" N2 s2 u; O2 _1 Z1 i. V v1 R' t9 `0 h4 r/ l4 p# S. V% J1 n
city_map.add_child(folium.Rectangle(
! d" a. }' ]! {, n1 h6 l bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
9 ^3 n6 c, n; H0 m weight=2, # 线条宽度6 \( @7 @8 _( h2 }# \6 C$ Z
color=blue))
" `# t5 O) ~8 M% @( A* f* e" y3 \* N& ~2 I0 S
# 圆形, circle, radius units meters
% {) Q! [% _% J; R4 f; g, F8 H folium.Circle(5 w0 c5 X1 g& _3 Y
radius=1000,8 D1 M' `" e. p7 m% k; f
location=coordinate_nus,# T2 g+ U& X# y K& A7 d
popup="National University of Singapore",
4 S9 v! k9 b/ C# D$ Q color="crimson",
5 {# E( R& t# U3 F1 i5 d- x fill=False,
% P& J2 f; Z7 P: |% k4 N2 C8 r7 W/ a ).add_to(city_map)
2 j$ _. Q& o1 t
' v7 `# N4 }' S6 g1 n6 { # 圆形, circle, radius units pixels4 S2 _ C! v, X& b* ^, o3 S9 H8 E
folium.CircleMarker(
7 K W8 U/ {3 |9 c( p& ~' R location=coordinate_ntu,' O% q4 Z* W) f. z c8 Q' j
radius=30,. y( s" _6 @8 t8 ^/ d
popup="Nanyang Technological University",- J1 b8 p J! X8 h
color="#3186cc",# z& r8 O/ y# {5 ^( A% `
fill=True,
' j D& u; c& C* z! p3 l- p' D fill_opacity=0.3, # 透明度
- m5 o* t/ [# D) m2 ? fill_color="#3186cc",9 o# f, b, k( j/ F- R8 y- V% [8 y- U+ }
).add_to(city_map)
6 ^4 @* f3 R7 z# A' O- ?- M4 Q3 p/ s% k ~2 c) j
city_map ' F: b0 b/ F$ ^3 G6 V( J
Folium 中的画出各种形状热力图
' P s- [- @3 ? 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。 6 I' z, ]# K3 V0 l* B4 e
import numpy as np
, {; O" X( _9 J. C r1 S from folium.plugins import HeatMap4 Y! m- `* V* g, i
. Y; I o g8 J7 n6 ` # define the city map
R$ S0 o J, q6 {1 o, P city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
& ]: o1 i3 Y: {) [* m& ^* j) r+ P4 ~1 i! P) p9 u( [/ t& N
# 构建随机数据& t+ A; \5 H; W! P4 @' _$ ~! ~2 s8 q
data = (9 u$ e, R5 f' r% T
np.random.normal(
* y; s5 c6 Y* |; j4 U+ q size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
0 L* y/ }6 U2 H- {- Z9 ^ np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
# {: c% B5 M7 v4 b- G. } ).tolist()
! P5 z: v* H% l7 v# Y. _$ k& |
8 t# e: a6 P B7 _& h- h# A city_map.add_child(HeatMap(data=data))0 f% J5 M: T; U: Z
city_map
7 v, b1 X: h* m$ i3 j$ S# c
4 Z8 y- k8 R% v8 o 除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。 5 J0 X# P0 S3 Z
import numpy as np
% N6 B" }" p8 E% t" C+ C* E1 p from folium.plugins import HeatMapWithTime
7 [ L& B. x9 e# c5 V6 y: t
4 k- r3 O _9 P. L; S' `( \; X # define the city map" z4 X! }, Y) R5 k/ _( y
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
- u- j+ g- Z* [$ Q! x( \- c+ Y0 ?2 R& p3 \& X) }9 x6 c) H+ [" I
# 使用 numpy 建立初始数据
: L) w/ Y: ]; v- }1 ] initial_data = (np.random.normal(size=(200, 2)) *
d" V* A$ b; B2 U) [; r np.array([[0.02, 0.02]]) +
* Z) Y6 R: r9 s( [8 h( o np.array([coordinate_orchard_road])) ]: w$ G+ g% l r8 f
# w6 ]7 H$ y8 ]5 X' u # 建立连续的数据# }; L- w. S3 }2 l- Z3 j* N i$ S
data = [initial_data.tolist()]$ P" W `, N4 p- ^* I; V
for i in range(20):
: L8 T! n) j/ D: \5 m data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())- O* [5 g% Y3 r1 Y9 E
, Z, _8 v) E H( i # 显示连续的热力图3 y( E6 S w1 }+ H$ y1 ?: S5 h1 n
city_map.add_child(HeatMapWithTime(data))
H* I3 L) H* i3 n city_map 5 K7 X/ q7 e& Y4 {. ?! E2 f
: m+ h) e* E, m) t( r8 Y
经纬度点的聚类
& u' b- b1 u2 D t$ x, q( | 除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
% P1 l1 b# V: p" I from folium.plugins import MarkerCluster
1 Z1 g; `% k$ S: E: J& L. t7 x& u. J: U+ a/ d7 I' Z) m& {8 H% P
# define the city map
; R/ t* \3 ]- G) e city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
/ S$ D6 {4 @, G! j
% [6 w* c7 C" r5 ~$ b8 S # 经纬度的聚类
; T' ~/ G( [% w, d/ O; } # 在 NUS 的经纬度附近随机生成 100 个点;' R8 V: W9 @3 `( O+ L
data = (5 ^6 E: f: T8 `# x
np.random.normal(size=(100, 2)), n! Z$ Q/ N8 w8 B4 B. R: m
* np.array([[0.001, 0.001]]) +
- x" P9 d0 u M8 c6 C: L# C- j7 [ np.array([coordinate_nus]))
; E" I3 `( r" {* H1 ^) H) E- C& ~9 x9 H
) v, k" Z! W7 x, E0 g # create a mark cluster object6 h: b, H Z5 }9 p5 z
marker_cluster = MarkerCluster().add_to(city_map)
/ g3 \/ J1 T# j
: h" @6 |5 i9 u; b, {7 a; c # 将这些经纬度数据加入聚类, U# @, c7 C" `; U' \5 G
for element in data:
2 s( Y7 m9 F* K2 l9 T5 \9 z folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)
5 q: N( h4 q% }1 _! H
8 v: |. u' s+ L # add marker_cluster to map
) g- t s- `+ I city_map.add_child(marker_cluster)
P) r3 L/ y; X; c, b+ Q2 F. K4 b3 j# x: q
# 作图
) y9 S. a. c9 E8 O( V% k city_map " S+ e& E9 t9 @5 ]/ W/ d8 J" R) g4 l
9 `2 H( G* w! u/ L1 O" w5 ~9 P, _
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。
( i- F3 E3 ]$ g9 {9 I 参考文献
, H; \$ o8 l" T Folium 官方文档:Folium - Folium 0.12.1 documentation
8 U% t6 J$ b/ W% {
! [0 `/ ~# c! ^; V9 ^
# }& X/ X/ n# ?/ X5 ?. f. I' ?. N1 T' z7 F# z# i
: W- k# E6 `3 V) I; X& q0 ?2 U |