1 {; _0 g& _, @! ^6 O7 e
Folium 简介
7 b5 X! w. K: C) x 作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。 " n' C/ c& a4 h1 F. ?
创建地图+ n0 p9 C5 ~" S3 D+ i% G5 n
通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。
. Z5 Y" Z1 M$ K& \. j* X* r+ L% A import folium
P' Y9 n: ]! b %matplotlib inline
# E) |, K& g7 l+ N8 [
& g& O3 V' q2 z import webbrowser* M: y. C0 Z# A
! w l* e: u- K( q x; b1 q
print(folium.__version__)
+ q }) C+ g7 c) a& Z& I# _6 p
8 c- V0 ^9 f6 T4 ^4 W # define the world map
! F9 p* f* l* x world_map = folium.Map()
& a2 o& B3 b6 t/ \" l! P/ \ # display world map- j* V6 R) i5 J, L$ F; u
world_map' D2 m q, ^/ M$ t* Z+ @# l+ e
% T6 w4 y$ B5 \
世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。 7 B, \, M6 }- {8 x' n
在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
. }) a g7 A: ?( j i- m 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
% w4 | B6 X& U) d: T' | 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。 ' n/ X% G- D, ]; G! V' M( d& a
# latitude and longitude in Singapore city
7 I1 u1 f( N0 [; B& E$ k# x. V coordinate_sentosa = [1.248946, 103.834306]5 o- A7 [3 F2 ^2 ]/ S+ j
coordinate_orchard_road = [1.304247, 103.833264]# e0 [& i3 \) t9 X0 }3 v
coordinate_changi_airport = [1.357557, 103.98847]7 {, F4 S# p, G* n# v
coordinate_nus = [1.296202,103.776899]1 }1 h5 f ]/ S' N* q% a
coordinate_ntu = [1.34841, 103.682933]
. v4 Q! D4 J! I) x, [ coordinate_zoo = [1.403717, 103.793974]1 z# e* Q1 H# e8 R. W
coordinate_ang_mo_kio = [1.37008, 103.849523]9 p1 G* T4 u7 W* l! C; N
coordinate_yi_shun = [1.429384, 103.835028]0 w% r( a% ~* m" z- r. }* L8 g
7 V0 s0 n2 X, R$ Z- l3 n # icon
$ s, E5 [: E! A% R) s6 m icon_cloud = "cloud"
: a+ Y; e# H) K/ p% a icon_sign = "info-sign"9 O& x1 e3 Q( O* g
$ J7 Q: d3 c, b3 @9 d3 P8 S # define the city map V( E( L5 {) ?4 M5 C$ K5 {6 @, F! C
# tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}& L3 L# _+ S/ ]- y' F- h
city_map = folium.Map(
7 z4 }. `* u5 A1 `! K location=coordinate_orchard_road,
* l& W/ D Z$ ]; O8 K5 r zoom_start=11,, x+ T5 A" R! C" G
tiles=OpenStreetMap)6 E& F1 |' Y5 m) P/ J% h! `
* c% u6 ^2 Y1 T) i" h" d # add marker in the city map9 _2 f7 q( K W+ i
folium.Marker(+ d' l0 U! v. H i& {5 D8 x& I, n
coordinate_sentosa,( G s0 E0 T7 z+ E, G4 e
icon=folium.Icon(color=blue)
, |" I1 v- }5 }4 ~: w ).add_to(city_map), c& V1 z( W$ x3 o, ?2 m
folium.Marker(5 ~5 h0 j8 Z- q& ?8 U' f
coordinate_orchard_road,+ p; b& A: u& i. V; W2 P
icon=folium.Icon(color=green, icon=icon_cloud)
z; f, u! o8 K( H4 r+ d2 Y ).add_to(city_map)
) W; h- i7 Z0 {6 U' ?! G" n+ F4 T# H! [& g3 f5 g
# add popup
0 w# }* g1 K2 N4 F) I folium.Marker(% t8 P7 T; [, w& n: `
coordinate_changi_airport,
: A; f1 z) L0 S" T2 ] popup=Changi Airport,' \+ B1 ^7 j' ?2 T' ]: Z2 H
icon=folium.Icon(color=red, icon=icon_sign)
2 {3 k$ A' m/ |* C7 ^6 o3 l ).add_to(city_map)
+ n2 ^( j6 D; M# s
" |) j8 i$ K2 y; h; I. L # add tooltips and popup
8 v$ T! a- {& R) b2 A$ O tooltip = "Click me!"9 a0 E- B6 H7 h- g% ?; U# P; o
& Z9 C# m+ m I: x3 F5 _
folium.Marker(
/ h6 ?: I+ N7 z6 Z/ X+ A5 ^ coordinate_nus,* p' ^9 m. C2 E: q4 T
popup="<i>National University of Singapore</i>",6 w$ r4 m: s& f: R1 V) P" b4 ?+ D* r
tooltip=tooltip9 e( Y6 s9 p1 `( H2 X5 y0 J& n. b' [
).add_to(city_map)! a8 F C4 G; R* \( P6 U; {
; _# k- Z( X) Z5 | folium.Marker(
G5 ^9 p2 U$ Y# u8 q coordinate_ntu,2 h7 \+ j5 U ?* s9 P: P; I) s
popup="<b>Nanyang Technological University</b>",0 P0 ^$ } G8 H2 i
tooltip=tooltip# O6 [4 | j+ Y7 D+ q
).add_to(city_map)
2 u2 f' W0 R; C8 y1 d& b. m, c, ?
# display city map# _- [& j- r( Y
city_map
; Z7 _% ~% s6 H0 ~$ C! A Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
* c2 a0 S1 J/ x # define the city map; B( I$ r7 U: ~9 z5 B
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
% E6 n# U' J3 n+ ~9 |7 R+ j4 n. w: X
# 在地图中添加经纬度, add latitude and longitude in the map when click
' W& O) {: l7 o& G0 [9 R, ] city_map.add_child(folium.LatLngPopup())
" _: A, k; E( j& y& r% m, c% C$ `$ A5 r6 P8 S6 G8 V
city_map : l' O* @: _* y3 g Y
0 L7 B& X: t7 J1 n
几何形状
- f) K/ M4 I2 c4 \* {- n! u, ] 在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。 6 F* ^ H% R+ c* X7 B
# define the city map
1 ~' k; H/ o/ o9 W city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)' z2 S* r9 v4 F) t7 i, y
6 H; E. R( @0 d1 e ` # 在地图中如何添加形状
1 J' ]# Y m" v' i0 ? # 多条边; k- b# ~7 \4 R" i3 S# v
points_1 = [
$ M1 Y# \0 j: v. P# ]; v" Q coordinate_ntu,4 U2 z& U5 @+ L1 h' S
coordinate_nus,
+ W$ R/ S' P$ \9 ]; n G" L! G coordinate_zoo
( N# L o$ }2 r7 ? ]
) C' g6 \7 ~4 \, W9 [. d+ }8 r' X( O9 T w! Y$ k2 s' P
# 在 city_map 中添加多条边,第一种添加方式* P+ G. ~! d* u
city_map.add_child(folium.PolyLine(
5 Z& z2 S% N; e0 f$ N locations=points_1, # 坐标列表
% m8 J8 ?3 Z" S+ B weight=3, # 线条宽度" x- O4 r1 I! f. G& ] f& R
color=gray))
8 m) k3 ] n' Z2 g8 p" Q6 I3 G- S' \) _ r; Q+ V
# 在 city_map 中添加多条边,第二种添加方式
* ^+ `; m$ ?* ] |+ e folium.PolyLine(
+ T. u! M) ]* W! [ locations=points_1, # 坐标列表
& b9 P" b* c% o. [ X+ m" s weight=3, # 线条宽度" V9 S6 ~. N( D+ n0 G, t. W9 N
color=gray).add_to(city_map)- n- y" \+ B% M$ c' h, J# G! P+ F
# M5 ]( O s6 M- \5 w' A/ \% i7 w2 @ # 多边形: f2 d3 t# P3 V6 g* i0 I; j! ]
points_2 = [5 F k I, q( I( W
coordinate_orchard_road,& `/ ?- U. [+ u
coordinate_sentosa,- I/ o, n# U5 ?" H8 R) ^. q: u
coordinate_changi_airport6 e# Q! H) e3 W, B5 f# o% H" ?
]
{, [! A- A# z# u: ?8 c. O; o( I# j5 E2 k# ?
city_map.add_child(folium.Polygon(- H; o5 C3 D+ @- I) J8 y& W' ?9 ~# _
locations=points_2, # 坐标列表- D! I2 G0 j, `2 D) @
weight=3, # 线条宽度
6 d6 |+ }1 x- E color=yellow)); ~# o) Q2 c# Z& B" p+ [
' r% N' M( ~* z( I7 j # 矩形# ]) R: c* A$ Q* z6 \7 j* Z& ]; ]
bounds = [; f! V: n& Q. ^5 f, j9 Z2 J1 ?! A0 S
coordinate_ang_mo_kio,6 c# m6 C9 j1 n! k
coordinate_yi_shun5 J- u9 ]( S) Y; s
]
4 h5 y" V% J/ K) f
* V' z# {) _9 b# s9 Q" G. p city_map.add_child(folium.Rectangle(
]& n& c+ _7 [. q6 Q P6 H$ Z3 P bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)' Z! S3 G; J! o4 g: z6 a* m
weight=2, # 线条宽度% c6 j5 [9 x; c" J9 @, j. J
color=blue)). ^$ G% B" Y- V# ^
; L# G7 [4 i4 P) |1 E, L5 M) ~9 E/ A
# 圆形, circle, radius units meters# I9 c. d8 Q* b7 g
folium.Circle(' m4 ?. K- }$ r+ X! a0 U+ E
radius=1000,3 B! G7 F, b6 h4 X* F( }. O) r
location=coordinate_nus,
6 P* Z* b# K' d; O popup="National University of Singapore",
9 `* D; V. ], p, X+ k) L color="crimson",4 d z+ {5 o) r' s; X, n9 T
fill=False,9 q! f( I% Y/ a$ a. i6 } z0 ?+ k
).add_to(city_map)
, {- p# a/ X% \% ~- l0 L9 ^/ {* {- }5 h* X1 E! A3 O# p5 @
# 圆形, circle, radius units pixels! y; V& W' f, b1 y; V# ^
folium.CircleMarker(
" Q: w- o% s# _ location=coordinate_ntu,- ~7 d ~5 Q/ y8 d% f* A3 U
radius=30,3 _" y5 _) z: N" `& q9 E; W
popup="Nanyang Technological University",5 T* `$ B4 m7 W5 h% }. V+ \6 P
color="#3186cc",5 L3 B8 q! P F; M
fill=True,
1 V. j2 B/ k8 K1 N. S% R fill_opacity=0.3, # 透明度/ b; j: r _$ l5 z5 l4 g
fill_color="#3186cc",
: Y. X$ W3 p+ R" i, f3 H% K ).add_to(city_map)
: c/ N+ ~. x; v+ t5 b7 m/ \$ T4 O* y7 o( g. d3 w# \/ _
city_map
) W. W, W/ M0 `8 S" ?, g6 u1 Z; i Folium 中的画出各种形状热力图
6 X1 {7 Z( y; m1 o5 C" ~ 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。
6 Z2 ^0 {& Y3 t$ m6 P( _: r; ^* Q import numpy as np
, a+ s1 X% L) E& m# ^" M from folium.plugins import HeatMap% S6 K+ ]( k3 x% n( j+ I2 h, N! y
* S2 L' ^: i i4 f$ }
# define the city map; j( a2 `( _- ?2 Q. }
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) M7 \: z* z b4 E9 d
& d' G+ r6 y8 _" \
# 构建随机数据
9 s4 G9 u9 K- d$ J% ]# u data = (. x+ N* w( q: h1 s- [
np.random.normal(
& C' i' K& A" F1 u6 l. C( y size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
- K3 c' n P9 q4 t+ w3 F, l* J np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])+ D) c& t0 j/ K: Y+ Y! k8 r
).tolist()
- q$ f+ {; E2 w* p" g$ @4 i4 a+ d9 C) H: z1 T: }
city_map.add_child(HeatMap(data=data))' m7 w5 y$ ?) u$ \' P
city_map
( C4 n9 @4 q- Z5 h( s1 @* D4 S 2 d" Y% { }- u+ b+ \2 ^& V" @; d
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。
) z: ?7 g# W3 `4 i( t1 M import numpy as np
- E3 e8 T8 Z7 R from folium.plugins import HeatMapWithTime
7 z/ ~. `; K9 L: ?4 K9 P* J4 w, l, d1 s
# define the city map
+ m ~7 ~5 ?( z* x# |& V( T9 l city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
( Q* ^! {* j8 T/ `0 \3 G% M# D9 f
# 使用 numpy 建立初始数据- I' {2 u( T9 Q- Q3 g
initial_data = (np.random.normal(size=(200, 2)) *: I2 b/ Q' f. y; p
np.array([[0.02, 0.02]]) +% P+ {- _9 U, x# V; d
np.array([coordinate_orchard_road]))
% A8 S- v, l6 P" @+ Q5 g0 s- Q0 x8 x/ }' _7 [. ]! S6 U: V
# 建立连续的数据
" z, J) F% m3 I data = [initial_data.tolist()]2 S! A) A) E O1 O4 {) P, k4 R0 A
for i in range(20):1 h" M; L/ E; R" j8 \! A
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())+ S% ?! ^, I: v1 a
0 R$ E7 P' b! M$ z0 _
# 显示连续的热力图
- r: n( C' {! L9 r5 _3 S p city_map.add_child(HeatMapWithTime(data))# r/ P- F, P& n2 h5 F! m+ c! Q
city_map
1 w4 k9 X4 K% W9 L+ I5 y ) Y; V& @5 K/ d# `
经纬度点的聚类
$ {7 P4 M/ p; g# u$ n& b 除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
: T2 l+ [) A! U from folium.plugins import MarkerCluster, r: P e0 c& J2 x$ _
$ u5 d9 }: F& t9 D( ]# [$ r
# define the city map) r; ~; f' Q8 z0 X" h8 ?2 P3 p
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
1 S. ~: R% X- Z. m+ T" }) Q
$ N+ L. n& A/ Q& U- ? # 经纬度的聚类# a+ _ S2 p+ s
# 在 NUS 的经纬度附近随机生成 100 个点;% _; H3 V" M) M8 I ?% O
data = (8 L, e% N! u+ @* ^
np.random.normal(size=(100, 2))
. m! e! P) |# b/ Q * np.array([[0.001, 0.001]]) +
) H% G9 T' k9 U, b/ k/ D# W np.array([coordinate_nus]))) q; i+ p2 g" N% y' ~' p( w
% [$ I; I2 H/ t# I8 l1 x# m
# create a mark cluster object/ d$ _; f% b# s, j6 U: g. ^/ T6 {
marker_cluster = MarkerCluster().add_to(city_map)7 w5 `+ Z9 F0 e3 r0 T6 ^& k
X3 w" b9 I& V" ?# f% v# b$ L # 将这些经纬度数据加入聚类
) s+ M5 I3 ]! |& I for element in data:
" G) H0 f3 [ ~# e% P4 A folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)
, {, O* f q; g1 X4 i
) c; z* u( s1 ~4 p # add marker_cluster to map
8 n4 x5 F- u( c; H6 J: P2 E; w city_map.add_child(marker_cluster)! c: C" T/ t7 n8 r8 { }
' k* j- F% S( Q; \3 Q. K0 t # 作图2 o( E2 e. l8 Z3 q+ W# D2 }; D
city_map 3 Y( ?. p. e7 ?+ m6 |
9 ~5 |% p' h' Z- q9 s: c1 C
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。
- @& l% X( \" E* Y6 K 参考文献
0 h: p8 a6 c5 L i6 W7 }0 P Folium 官方文档:Folium - Folium 0.12.1 documentation
$ i) q7 P* {) Z& N* M% r+ C$ U r' r8 L3 @# ?$ W* Z
U4 b/ G1 M! b3 B
& F0 r( g0 a0 B: q* p4 u
8 L5 e1 _3 M, h! p$ h |