3 k+ h! T: s$ ?! J6 j8 u* \& P0 Z- a
Folium 简介2 m. B/ ~8 j9 p9 Z
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。 : W+ D4 t' J3 m# ^
创建地图! r( a# R! B ~3 G
通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。
% r- w! U8 f* w/ E2 F( } import folium
) W1 M7 y E# n; t; d6 N %matplotlib inline' f- y0 J) U8 {( N( p5 A1 t+ A
\( ~& j/ _/ C+ Q+ u import webbrowser/ Z+ Z. E( c% R
8 {6 |% |, o9 r9 M print(folium.__version__) p, T3 v4 J$ F5 J
3 n7 }( R+ ]2 Q
# define the world map
) j2 c: j2 V7 H* H world_map = folium.Map()
4 M: b) ^2 k- D, ~$ n1 _ # display world map# \1 d* L; T1 V c9 J
world_map
1 a8 S3 W7 {4 y: l, Q1 v 7 U+ i5 K5 O9 s( n' W( O. r
世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。
6 t1 n+ g: _& M; o 在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
5 w$ J& O6 R% P# X4 X. W8 Y 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。 J* }0 I# l+ L( N7 @' |2 L
另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。
: N5 W- \4 [! S # latitude and longitude in Singapore city2 Y) \" {' P) T% R
coordinate_sentosa = [1.248946, 103.834306]
! T7 C l7 a6 R5 C2 D2 |: ~ coordinate_orchard_road = [1.304247, 103.833264]6 B" c) w1 S/ |
coordinate_changi_airport = [1.357557, 103.98847]
5 K6 B% o+ g; r( p7 V' | coordinate_nus = [1.296202,103.776899]
6 O6 X8 v! [' h+ A: ]9 R coordinate_ntu = [1.34841, 103.682933]1 Z) D" n0 y( i
coordinate_zoo = [1.403717, 103.793974]6 Y' z/ t I( j2 w8 o! m
coordinate_ang_mo_kio = [1.37008, 103.849523]6 T j+ Y! Q8 g" @" K a
coordinate_yi_shun = [1.429384, 103.835028]9 @) X: A q o
& }4 Z/ i1 Q: W- i9 M # icon
' l: h4 h! }1 ~+ q$ M+ @: i& v icon_cloud = "cloud"
6 G! q% e' B0 C h. ^' f$ L9 d8 R icon_sign = "info-sign"! y% T- W/ y) u, }3 j
4 C1 ~9 s4 m! f- k% @3 _' a # define the city map
) ]& K1 z2 q% x # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}
2 G0 H0 P0 h6 t6 W2 g# h& m city_map = folium.Map(
) P4 [: l' [5 ? location=coordinate_orchard_road,6 o2 D4 ?5 R6 L; X
zoom_start=11,
9 f% ]. P, R$ [6 v( Q( O tiles=OpenStreetMap)
% y% s: \" x$ ^
. [# r3 ~. s& w/ M0 g3 n9 T # add marker in the city map5 L0 O6 U+ w" u8 ?2 W) _
folium.Marker(
( f0 F1 t+ r2 y, h coordinate_sentosa,6 P4 u& i6 p' y: ?& {& g q
icon=folium.Icon(color=blue); V4 j: }7 J$ h
).add_to(city_map)
' p" J( M0 X3 S; N* G4 i$ P folium.Marker(9 m" W/ g1 Y- n. g! F7 q ~
coordinate_orchard_road,
5 z% ]6 u3 p# x* B icon=folium.Icon(color=green, icon=icon_cloud)
, G3 I( C5 B' p* U) @ ).add_to(city_map)
4 W0 S' D9 k$ j# \( r7 R( ?3 d8 m$ H- O" {/ ~4 F2 S$ q& }" h% p
# add popup! N( A0 M( N0 z m
folium.Marker(4 `! Z4 o( R( o" _( ]0 r+ j
coordinate_changi_airport,
/ n7 |7 E1 t8 n+ @7 ?5 A popup=Changi Airport,
: L% _9 J: T, o1 r6 l icon=folium.Icon(color=red, icon=icon_sign)
?& j! u {* `* N' F ).add_to(city_map)1 p/ _) A% n& e% I; b- S
4 [$ V9 F0 Q. Z0 K # add tooltips and popup5 i+ a; P. T# M8 J7 s ?
tooltip = "Click me!"
/ x9 F# n1 g2 P
2 c- I5 E/ J+ J5 J6 ~, L folium.Marker(
) M M% T& S# X. s& W; L coordinate_nus," ]( [# O; b& a" d: C6 n0 u
popup="<i>National University of Singapore</i>",. c0 b+ x3 I* q, f6 e0 K
tooltip=tooltip( J+ C: F5 C: m$ ^/ V @
).add_to(city_map)
! T' P9 D% Z' |& ~. Z6 b9 Y- D/ A
; m% n8 ]6 }! R8 b' F9 f! z _ folium.Marker(
7 ^& s, l) @* m- l9 H" n. P4 g' W coordinate_ntu,6 `% e$ i9 l" x& a6 [4 }+ L6 {
popup="<b>Nanyang Technological University</b>",
4 P! b( G+ ~0 U6 C# j tooltip=tooltip& n" F$ X% u$ J6 e
).add_to(city_map)
- v6 m* E& W9 q- _% H* C4 P }. k0 c& x: k2 y
# display city map% p& c, P9 s( g' R# z& |6 @
city_map
U$ T8 W' M/ ^5 J( U8 E! P% ] Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。 9 e$ n7 J" ~4 N H8 ~
# define the city map- T8 e& _- }* O& P) v
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
; l6 d+ t0 L7 q- o# M# b7 F8 E( h: T
# 在地图中添加经纬度, add latitude and longitude in the map when click
1 p: W I* k" a3 X# ~ city_map.add_child(folium.LatLngPopup())
Z- n* M' z( m; n* |" B" |
6 ~8 ]( j5 f/ N: v city_map
& ]" q3 n, m+ {9 O& i# d+ }2 J % y/ A% `1 j- [& ~* s* n/ h5 H
几何形状
7 Z# y9 J* I5 d6 b/ h$ v 在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。
4 L6 E( o" d4 m. M5 D # define the city map2 M r, @+ N0 U, p5 b0 O; X
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)* A( [8 R/ v: h5 B
% N1 ~& Y$ n7 L/ Y9 v' T( W
# 在地图中如何添加形状# @+ t& {# ?- I
# 多条边/ b+ G8 a ?% j
points_1 = [
4 C9 a3 `9 Z2 r0 {# t2 @ coordinate_ntu,9 V$ i- t$ d' U
coordinate_nus,' j8 Z& y9 i3 f) i2 K3 `8 I
coordinate_zoo* v# ~2 q* Z! d' U
]) r& Z0 S, H. m0 ]% [' G
0 w( v& E; N( w4 R# ?4 \ # 在 city_map 中添加多条边,第一种添加方式5 Y. x1 Z. |4 I9 X2 T4 d6 ~
city_map.add_child(folium.PolyLine(
+ R) Z% z; T1 s R6 R locations=points_1, # 坐标列表
, L* ?- R; P+ A6 W+ M weight=3, # 线条宽度5 l) x E' V; I7 V
color=gray)) |9 s5 G) e' O0 q( l
0 v1 h3 m* t% {! x
# 在 city_map 中添加多条边,第二种添加方式% o8 f6 s- |, f- K5 D, D& y
folium.PolyLine(5 f x, q6 ~6 L$ E3 ]
locations=points_1, # 坐标列表3 a; W* M }+ u% l7 z q: T7 ^/ ^# t9 U
weight=3, # 线条宽度
# c4 ], V3 ~, Z% T8 _; ` color=gray).add_to(city_map)( E) v0 M5 t; ^6 ~# P
4 R5 u1 U( Q* Q) b, z: d # 多边形; T+ q) r: @: a' q- E7 [
points_2 = [
( ]: ?4 L( ?: D+ n `, ~3 G9 Y/ z coordinate_orchard_road,
: }' `4 f+ @3 C coordinate_sentosa,/ _6 h+ j" V- a
coordinate_changi_airport7 @* R; U2 m; }; S* q0 W
]1 H) K E9 t" j! X
6 M' l" B' h# X city_map.add_child(folium.Polygon(
% `2 X" x' D' t6 X' L! Z locations=points_2, # 坐标列表
; X: {9 O& x, B4 T7 j; H% Y2 c; d weight=3, # 线条宽度- _7 {; z2 u0 a- r
color=yellow))
- x9 J2 ^% N8 I7 b7 H
2 L8 A& T: \/ t9 k* T # 矩形$ V3 M( N6 }6 l6 Q t
bounds = [6 e1 `/ x5 Q# R- M) z. ?- \
coordinate_ang_mo_kio,
& r* _! o( g6 ]) w! v" t6 A coordinate_yi_shun
* T" y$ `2 K0 R; d& \. n" Z) x O' X ]
' I! C6 s+ Y: L6 a) E1 S3 p# ]/ S
. @. I( r" o* ]. m! I city_map.add_child(folium.Rectangle(" T, l' O: q% a. }2 m
bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)/ ?5 m" `/ E6 R- Z! B& m W4 ~
weight=2, # 线条宽度
# ?" M$ U# j+ ~$ I/ ^: }8 E0 K color=blue))
. D+ k# ?' f- t" t5 X& \9 g9 v5 w2 T
" m$ `% t- s/ y. O+ m6 H # 圆形, circle, radius units meters' j" P# l% `' ?. v- Z+ |
folium.Circle(
; R8 y$ C, h* ~9 [ radius=1000,2 k8 S+ ]6 E1 k# V( I9 w
location=coordinate_nus,( s+ V: ?6 N- n0 _& z4 Q# L
popup="National University of Singapore",$ `+ _/ k: ]1 B, M8 F. Y: J
color="crimson",
0 f" W3 k8 q9 X0 n) h3 H! u6 G fill=False,
7 |. T& ^# v1 e* c4 O5 h# ?7 N$ y ).add_to(city_map)
' K8 m/ A6 Z& Y
) J) ?5 O* ]# C( u+ ~, b) i( `& B& y # 圆形, circle, radius units pixels: W; [/ ]8 k7 `3 j/ ]- F
folium.CircleMarker(& `0 y! v( X5 V9 j
location=coordinate_ntu,( b& ?0 b! K$ F+ r; ?5 i
radius=30,
8 @6 S- T; s: D! Q' p" v! q popup="Nanyang Technological University",
/ Y, v+ \3 \; G$ v color="#3186cc",7 h: ~9 x5 `- v: M# n
fill=True,
" ]$ v: d) s* l; b fill_opacity=0.3, # 透明度
; F* s( Y! E: i5 i+ F2 D7 g" R fill_color="#3186cc",
2 _+ s4 t; m! `$ W! Q% K ).add_to(city_map)$ \1 z# |0 ~' r3 F' q2 d/ D& p
/ V7 m* H( a5 u: ^8 i% y
city_map
0 E7 U9 r+ t1 l* s% J3 T0 ~9 s Folium 中的画出各种形状热力图0 o& u1 @) i) Q( ^% \
在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。
( ^( v$ z% p; b* a6 S import numpy as np& o+ ?, H2 C2 M8 K
from folium.plugins import HeatMap
, c" D6 C* x) B# O9 x" {2 ~8 ^ X. W- y0 F3 r! }
# define the city map. \2 C! F; t2 a. f& {
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11); B" h- j7 i U/ S% |
" C% a! B! b: C' n$ t& D3 s8 \/ m
# 构建随机数据9 _* r9 T) a$ R# T/ m6 \
data = (
( p9 Q/ |4 L$ d' d np.random.normal(8 Y7 y6 _& f2 c$ n& r6 e
size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
; J2 k7 o! g6 f! Q7 S np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])- N, ~+ T c: ~# m* G2 I% z. A5 [
).tolist()7 ]) }+ g0 A) {" [; P5 V W
, Y# G# d( |: u2 W) m# \4 [/ \/ |% | city_map.add_child(HeatMap(data=data))# H# j- G2 g: y: C; O7 y* z
city_map ! n9 \6 T @9 B5 N% u. x
- V3 {" Y8 t" ^) R& w$ J. J8 D2 O8 q
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。
9 w* s: e" m2 Q4 @5 E" ~/ X import numpy as np
( S4 P E( e" x4 [/ K from folium.plugins import HeatMapWithTime3 H' o5 g8 H, I; @8 d- J
7 ~5 Y2 e5 p: t- i4 g. w1 W # define the city map, K+ s6 z0 x4 {; {4 q7 L, K, I
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)+ A+ [, U7 x# h+ C* M7 q
3 w9 Y, Y, I( B. |! b7 w # 使用 numpy 建立初始数据
; o( t1 P' V0 C' ]# L! N5 N# R initial_data = (np.random.normal(size=(200, 2)) *! V) k# ^2 @) g1 d0 F+ s# N" z4 j
np.array([[0.02, 0.02]]) +8 q o" m0 i4 i$ d+ ?
np.array([coordinate_orchard_road]))- r6 M) l( h) u7 h. c0 [
% G9 p, o2 |6 J# u# T( A # 建立连续的数据0 q- g/ v) X u* b& Z7 ?$ l& P
data = [initial_data.tolist()]
# X: q3 V" n4 L% Q$ l for i in range(20):
7 ]( T5 K0 E3 B* p% S8 g: p data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())
) u1 s% k1 }1 R& a. F( h% A# F) E" q" s5 A, g
# 显示连续的热力图
+ d) z0 k+ x2 Z2 r city_map.add_child(HeatMapWithTime(data))/ l3 H7 }; A1 B
city_map 2 V c# e' g: t. K
5 ^" E/ S2 O1 [% P# c+ \ 经纬度点的聚类2 Q3 G- F) U6 o3 L/ F. l
除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。 3 j: w2 ?5 @* W0 ?8 S
from folium.plugins import MarkerCluster
- q5 L+ {9 ]" W3 N8 t. _9 z) y
: Q% d' c6 Q! y! P. [5 ~" a # define the city map* e8 `# Z4 k$ c! N6 u
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
* V, v+ D) h1 `/ f/ i
5 w' y# Y* e# N$ q # 经纬度的聚类! I$ v: W( Q$ J I: e1 d
# 在 NUS 的经纬度附近随机生成 100 个点;
4 l8 g: V' I( N8 w- u6 ` data = (# g$ r/ P. J( Q, R" x6 Y' ?( k6 z0 p
np.random.normal(size=(100, 2))- n# }- `" O7 O* |9 K
* np.array([[0.001, 0.001]]) +
7 v9 C5 E4 E2 l% E. h& y7 ~: S np.array([coordinate_nus]))
1 h. p! y. U8 ]0 P1 a( d* ]# N- b# z4 J( s& V
# create a mark cluster object
9 C2 r; t6 Q0 r! h: e marker_cluster = MarkerCluster().add_to(city_map)
& R0 F. e" w% S/ h9 a* h
" Y: U, A6 `% u, I8 S8 U # 将这些经纬度数据加入聚类
2 Y8 _ N- r2 I5 e' b) T0 e" s for element in data:8 a7 o% \8 o: j
folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)9 v% J8 i9 O$ a; B1 i# D
N3 R" `* O6 k7 u& T- x$ F K # add marker_cluster to map
o% A, {) z# Q6 H' G: Y city_map.add_child(marker_cluster)) j1 w" ^/ k# t1 d
- h# }- M r8 }& h8 d # 作图: X6 T4 d2 u7 x7 b
city_map ) m% {$ `0 I# y& m
& e6 \" T6 M. e4 O. z
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。 + l6 i' x, n! d: b* v- B7 U" V
参考文献% T( _2 ?4 I0 m1 c2 M
Folium 官方文档:Folium - Folium 0.12.1 documentation 3 l8 G, M7 W, J% W
. l0 {, u6 |0 C5 b9 c
5 E, \& Z$ C7 ?% u; N
7 I, p# T% J' \+ W
: Y+ I! d, l; Y* g- ?, F& I |