- p4 Z& G3 t9 q' n, k. b1 B
Folium 简介
/ N2 x& ~( z; P: m 作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
0 \! B1 s( H: j7 [) M 创建地图+ j% v8 A5 |1 ]4 M
通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。 " T* L7 Y; r- V# E( U. u
import folium
7 i4 q5 x/ o: k* i$ o% c/ s %matplotlib inline
6 H4 `4 _) y2 D$ l% r$ P7 o
) c3 }; F2 k7 Q& \# [ f( O0 \# l import webbrowser
1 z0 Y) o# J% r5 h8 B; _2 A% `7 ]
print(folium.__version__)
^$ x7 c; l, L- x
5 N3 _6 ?9 r! N7 o1 F' A # define the world map
; l7 _3 q9 R7 o1 W0 M world_map = folium.Map()) x7 m# l0 b; W: R
# display world map
6 s E$ d( h) h7 N$ J3 u/ d world_map
! _" r; ^$ A! D0 c8 u: h8 L) {
1 l5 F# w! u6 }& i$ `, w 世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。 7 V0 n) D- B( t, Q6 [! a, m
在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。 3 i8 m6 r" @' r2 q' x }
有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。 0 J0 Y5 t# ?5 e
另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。
1 `- X, A$ e5 i. } # latitude and longitude in Singapore city9 {& ~; X4 k0 b; {+ K9 F
coordinate_sentosa = [1.248946, 103.834306] u: [( r& L3 j& W9 r6 W% {
coordinate_orchard_road = [1.304247, 103.833264]
" `, q6 n4 h$ E2 f' V9 Z) ^ coordinate_changi_airport = [1.357557, 103.98847]1 Y( M8 Q* ?6 [- J6 }0 i+ E
coordinate_nus = [1.296202,103.776899]
% p# k9 d$ l% ], o. w; p8 l coordinate_ntu = [1.34841, 103.682933]
& D4 e5 `/ M: u* B# A: x i% w+ s coordinate_zoo = [1.403717, 103.793974]# U! q9 R1 Y9 k5 `$ W; c
coordinate_ang_mo_kio = [1.37008, 103.849523]
# s& A/ D: i' O* J5 ]* I9 P! f coordinate_yi_shun = [1.429384, 103.835028]- V J% d4 u) J m% o* g
" q' [& u1 m/ d
# icon) B! w4 A z4 R
icon_cloud = "cloud"+ d4 }& f( ?3 d$ K+ C: s- N
icon_sign = "info-sign"2 I \9 T9 b5 g1 l8 {4 u
) b. L0 ~7 R, k: s8 U # define the city map
. B# {! x0 i' e+ v) | # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}
/ w& d2 C, j& { city_map = folium.Map(
# e5 _5 b+ I# U$ B location=coordinate_orchard_road,
2 k9 A5 r# m. b3 ?5 _$ p zoom_start=11,) x3 K2 |2 C2 G. s9 V
tiles=OpenStreetMap)
; w7 g- h6 n, Q3 I5 @
# ]; @& |' l3 Z6 J0 r# f # add marker in the city map! f% D8 C- l* D B ~: M4 n
folium.Marker(* ~7 v7 F) K, C3 s: Y' Y/ J
coordinate_sentosa,2 z6 I; o6 g' F6 d) n8 N' w
icon=folium.Icon(color=blue)
: G4 K, e& m. {+ {7 f5 C- l ).add_to(city_map)) }2 d. x _2 P+ j5 u3 Z1 s2 F
folium.Marker(
9 e/ z8 m5 F W& U- r% T$ C coordinate_orchard_road,8 h: H( H2 G1 c1 `
icon=folium.Icon(color=green, icon=icon_cloud)
8 H0 e1 ]; w0 a ).add_to(city_map)# @5 |2 N4 L: ~7 X! h8 T
2 @. {6 p; v u9 O
# add popup" c# j" G5 ~- }# H( S
folium.Marker(& H/ K( w) v" l& h' r
coordinate_changi_airport,, i: f' u8 `: T3 M
popup=Changi Airport,
8 D; h; ~. J& ^ icon=folium.Icon(color=red, icon=icon_sign)! n* W5 b0 w7 \! v
).add_to(city_map)3 u# E# a @" c
9 I2 h* D# ?+ J x7 U( f
# add tooltips and popup H u$ J% ^# h% L
tooltip = "Click me!"! X: X! q8 ~+ B: {# ~# J! v
' ^2 E- [8 e* u+ U
folium.Marker(
7 x$ t4 p$ b" i# F9 Q: f coordinate_nus,' l( j+ H$ p) X8 R
popup="<i>National University of Singapore</i>",
8 i# D" x. B: ~" e- l tooltip=tooltip
& e3 t) l9 ]* z% S; i y ).add_to(city_map)
0 k7 Q0 {1 N' C# O! A
/ E8 L! c6 Y! V) w: d folium.Marker(2 n! W; |9 y% Q0 i5 j4 q [
coordinate_ntu,
7 x! \2 p/ _/ I: v popup="<b>Nanyang Technological University</b>",
: _0 v7 o' g: s$ s2 w4 y( k tooltip=tooltip
5 L' J* \$ j; i2 F ).add_to(city_map)
# f0 o; H, C2 h4 i1 z* v
$ ]+ d) |6 `5 v% O$ H # display city map
3 }; o: R, }$ @& F9 J city_map 9 X" `2 V( ^( r
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。 ( f2 Y' c& o" v1 G& u1 a
# define the city map4 g# K R7 C) q
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11): O G7 _2 M2 E8 j% S3 W
y. b4 a' E6 Q+ `% W+ T
# 在地图中添加经纬度, add latitude and longitude in the map when click
+ S" R( h0 @& ^4 I7 w: l; ^ city_map.add_child(folium.LatLngPopup())
3 _5 A; p2 V4 r/ C8 ~0 {0 a# S q: U
city_map
6 t# T' f/ o+ A0 b9 q6 P/ J
6 O" M. o) l9 D 几何形状+ o: D& v$ G8 H
在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。
' a" d' J: E- r% y4 E0 q1 I # define the city map
' v, d2 W% g1 U+ }' [ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
6 E5 [. q3 ~" D- i5 d5 r& c0 l6 i' F1 o% {( V# X
# 在地图中如何添加形状
+ ~) @* `. G3 [+ B # 多条边4 O. z1 ^- R2 ~- W9 _$ t2 {4 m
points_1 = [
+ C& _8 t% n. W. w8 m. }6 K coordinate_ntu,
/ ~$ t# O* j+ h) X coordinate_nus,: n5 @+ u. |/ v. S& ~
coordinate_zoo$ U; z2 E5 V$ P( C2 x9 S& v! d
]
6 r2 i- K8 \6 O
2 W H5 q8 S( ~6 s # 在 city_map 中添加多条边,第一种添加方式
9 G+ {5 J/ M" b- P0 V2 b- ] city_map.add_child(folium.PolyLine(1 L. [' ^8 S; y# Q- m
locations=points_1, # 坐标列表6 ~- j3 p" g p8 A
weight=3, # 线条宽度 V3 m$ j( X! a. S* a5 |3 |
color=gray))
% c( Y( W; o" P! P5 l( I" V1 o' w- s0 T, Q
# 在 city_map 中添加多条边,第二种添加方式
% V/ H5 O0 G9 A3 J folium.PolyLine(
7 o+ e) K. |* Y, Y4 U8 t2 h6 p4 Q3 \ locations=points_1, # 坐标列表$ N D/ k" S, ?" p8 t1 b" U2 C
weight=3, # 线条宽度
) p' z% N3 L. B color=gray).add_to(city_map)
h/ R4 y( [: Q2 q' e, b2 U, Q+ @" z9 \( U3 k
# 多边形) k0 l& h1 r: v
points_2 = [. M2 b; a- b) h! d/ t
coordinate_orchard_road,5 S, c( P( C7 U i* V: W$ j
coordinate_sentosa,
' Z# Y/ \5 l0 F6 Q3 Y. G coordinate_changi_airport) L5 J b- W8 d2 Q/ q0 i% s
]6 \2 x6 P) T* b4 Z
2 b; S/ R. [2 w
city_map.add_child(folium.Polygon(
, }; B7 k4 l0 D( g locations=points_2, # 坐标列表
4 B0 ~1 x F7 ]& a! o weight=3, # 线条宽度! h8 g4 @7 h7 @- O
color=yellow))
( T" n* z$ V& U/ P+ }3 U0 U7 h) a6 E0 ?! ~% {' ]8 ]
# 矩形2 U( W8 w! q& x- q, u
bounds = [
, C+ @3 q; q7 `' M% | coordinate_ang_mo_kio,, Y& _0 g+ {+ p E: L2 R+ l9 N
coordinate_yi_shun: E! @1 o) K' a+ M1 n+ M
]5 M6 K7 x u$ Z7 b6 ]* d
9 r/ d2 h+ t, {# A6 w. Y
city_map.add_child(folium.Rectangle(0 Q% U4 Z$ K; U$ F
bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
. b4 A0 Y, s0 W! b. j+ O! U weight=2, # 线条宽度
* v" y, u$ `4 ?; ^; H color=blue))8 i; C1 x" ]' H- a5 j, T
* t+ s$ _" E- p* q( }. E- a+ ~& M6 Y
# 圆形, circle, radius units meters
5 q/ a! v: o3 F' O/ B- j folium.Circle(( {5 r8 N1 |, L
radius=1000,! A# G' S3 \ a: ^, J3 k
location=coordinate_nus,9 j* C1 t# o& C$ ?5 d( w4 V# Z
popup="National University of Singapore",7 }0 A& V3 m6 w$ x. g P7 M: r
color="crimson",& ]0 G- x- a- d6 E/ C6 V
fill=False,8 E; f3 ?: p4 E2 }' F9 W
).add_to(city_map)! }2 H6 E1 \; q- g1 ` q' Q1 y
3 R' {( L: S; F9 f8 z& R0 J
# 圆形, circle, radius units pixels; r) d( X) P; ?' }- n- o
folium.CircleMarker(
; q0 n+ A4 t, u* x/ G location=coordinate_ntu,1 \ V M- k6 l% S* ^: \
radius=30,
K9 a: c, a" m# y' j9 K popup="Nanyang Technological University",
! M( R& X$ D3 s! h" l0 _ color="#3186cc",# ]" L0 l# I, v- b! D8 H+ ~
fill=True,
6 y9 F) Q- }9 p$ F# w fill_opacity=0.3, # 透明度
% o2 }; w8 U& K) U" M( i, z fill_color="#3186cc",) ^0 W9 E1 v3 _, o3 M. O
).add_to(city_map)
# m" M* }' l1 P* Q! c7 b* L9 o* Q
: n9 T" W) C3 v city_map
; o1 S) A# {" J; ^7 s Folium 中的画出各种形状热力图) y I% g* O6 |9 {8 ?! ^0 }
在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。
4 h4 [; V) U$ h& c import numpy as np
/ g) Y+ S5 U0 n9 C from folium.plugins import HeatMap% ~. |3 Y! |& Y
9 u1 i7 R4 c! O1 ^% E3 F3 m6 M# D # define the city map
0 k0 d1 \4 x1 ?" M) b! @8 X( U( V1 _ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
6 B" l' c* W& A7 ^5 R* v9 k! m( F [( s+ u
# 构建随机数据- }# }& L8 l; ~7 n
data = (
! J. o- o+ {& b' `+ |6 a( d) h np.random.normal(0 F6 `! h: N0 {! K5 s( R" A" a5 Y
size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
# ]8 Y: U( f/ `; X. P4 L9 d: ~ np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])6 Z a! X1 b8 ^# |% X
).tolist()
+ ?5 X9 e9 U$ u3 t" B
4 j0 w# d8 E0 {1 Z+ z4 s city_map.add_child(HeatMap(data=data))% ^; f0 W- i# A' y1 M ]
city_map
% d% ~, ~. W3 I# B. J# h. {8 K , v4 }4 h% ^% C! ?
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。
; p" ] \5 \3 z6 N1 U" H; w% { import numpy as np' [5 E8 P P. D: I- x# ?
from folium.plugins import HeatMapWithTime
+ p% N- k2 N$ V( C z8 v
) t" H7 d' T- G, T5 `9 E5 \ # define the city map( t6 U: g3 f- Y% G1 a
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11). s- m6 P/ {% Y0 p0 G; R0 I) i
( t: P' O4 `" Z8 U! ~ # 使用 numpy 建立初始数据5 o4 \: v$ o7 g; P' p* [1 ]; o
initial_data = (np.random.normal(size=(200, 2)) *
8 e4 s8 |/ h+ }) K' h np.array([[0.02, 0.02]]) +
+ l' ]' c; s. ^6 A& h2 C" l# l np.array([coordinate_orchard_road]))
3 J E! R% G1 V) f
- g3 t# o% v. R9 D # 建立连续的数据0 n/ G8 q* B/ P0 o! E7 z
data = [initial_data.tolist()]
% P" i) R* k9 H- c1 S- g s for i in range(20):
; j; G2 d& ~9 e data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())
3 J( g" \0 U. Y1 f: \
* w. J* w3 b* H' J # 显示连续的热力图
6 {- e" W3 D, n0 Q' E city_map.add_child(HeatMapWithTime(data))# a' T6 ^+ U/ X F" A& u
city_map
4 e& x# B, c/ f* K
, B* `5 I7 g- @" Y! Q 经纬度点的聚类
# Y9 ]2 W N W3 V. W9 j* F 除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
/ ]9 r! h' Q" I p: Y from folium.plugins import MarkerCluster
b4 E, T& D: T7 O4 O- S2 E( i; W) e6 s8 y2 }
# define the city map
' `) C+ h: O! ^) E0 E& Y1 D city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)+ {! q; L, l7 E7 m
7 N2 T$ R c8 _6 v7 {
# 经纬度的聚类
; q' K1 O& M7 ^+ o" ? # 在 NUS 的经纬度附近随机生成 100 个点;3 N) S5 o- q2 z
data = (
9 p; t" r5 K0 u! e) W" } np.random.normal(size=(100, 2))5 l" F* j0 f) [
* np.array([[0.001, 0.001]]) +9 q b( z. p' Q0 I
np.array([coordinate_nus]))
, ^$ [# y3 x6 C5 G+ `3 ^8 F8 H
. q3 v+ T7 f, Y; Z- n7 E/ F! o$ O # create a mark cluster object1 {- ]! \% h1 Y0 r6 ]. h0 r8 n
marker_cluster = MarkerCluster().add_to(city_map)- J7 S" f0 Q- `
/ u* q' `; a% z) O& h7 E k
# 将这些经纬度数据加入聚类1 g) f9 t7 K4 E: v
for element in data:
! M1 O) A9 p4 z folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)
8 {1 @6 C( `6 q3 U: x) C* i; P
# add marker_cluster to map4 y3 Z; r0 P2 q, n( i& e5 q; c2 `
city_map.add_child(marker_cluster)
. F2 e! l7 i7 L+ m1 V
+ L- @0 w2 J+ J# r # 作图
% ?& v+ l$ l1 Z$ v7 E. j( c* j city_map ! t) w7 {/ M- `3 W- {. m$ u
8 H: V9 _& ^% I3 M0 \4 K, j
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。 4 K# D! S0 }4 ~# z! f6 ~
参考文献9 H1 ^. Q" V4 \& l; @4 g' N
Folium 官方文档:Folium - Folium 0.12.1 documentation
- T5 v" I- s, c( t2 F9 H
. [ `* S. ]# M R! x
3 U+ W2 R" L0 F9 Y6 d; o6 ^! T/ k5 p0 D; `* Z
. @! r. _; ^1 g& P! E |