0 c4 y( i* h. f& M6 F
Folium 简介0 w9 ?6 U! Y" Q9 j
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
q+ ?! }9 O' E r; f4 w- w7 E6 { 创建地图( J& y7 d3 \. [% o
通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。
$ I* O# S7 \( S# q) F1 Y import folium/ `* L" e0 v5 E' ~- p! J
%matplotlib inline2 G2 r8 x4 {) Z- d4 R
1 S6 I# ^ x' X3 M, }, X7 N import webbrowser1 T% g" E2 y! V; }* w$ U0 m
3 Y- B3 O$ Z% E
print(folium.__version__)+ ^* X% f# b, P
. E% ^5 I2 A% C! k! }. V# }8 n
# define the world map
3 q/ {+ `2 v( B6 I* @: k5 a world_map = folium.Map()
* X1 a% m& I' o1 w1 f # display world map
8 Q% g' ?) l# t; k% h* e world_map
_4 T! _4 l. S
/ f9 S. L- _( D5 P1 ~9 p 世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。 [9 N% @- [: I
在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
' Z8 {: z% j5 ]. q' b0 { 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
~3 P+ P+ f. X* h( A* S& y; n4 A 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。 . e3 b. T: H6 E0 @! n( C1 i n
# latitude and longitude in Singapore city9 o2 T" V+ O+ P1 B. L p6 o' X
coordinate_sentosa = [1.248946, 103.834306]4 W& v/ P( S) A K' V
coordinate_orchard_road = [1.304247, 103.833264]
{% s6 V( X4 ]% Q- \! d3 L1 Y coordinate_changi_airport = [1.357557, 103.98847]8 ~6 @$ k& g) U0 ^
coordinate_nus = [1.296202,103.776899]: Q% E8 b, h4 }9 }& W9 _ x
coordinate_ntu = [1.34841, 103.682933]' t N: e6 o; ?7 @" M! u
coordinate_zoo = [1.403717, 103.793974]5 y W m+ y5 u7 R
coordinate_ang_mo_kio = [1.37008, 103.849523]
5 m; f* Z( z5 k! Q coordinate_yi_shun = [1.429384, 103.835028]
3 E, \ @" F7 }4 ?, N" X5 k/ D6 O8 _3 ]& o
0 ~; K; R% |. F # icon9 s |; M$ X$ o0 C' l
icon_cloud = "cloud"
/ h# s; h6 d! T6 h- _$ O icon_sign = "info-sign"8 V0 ?1 l+ y) i
& M; u# P" w3 F' `* O1 f
# define the city map6 c9 x, X! m) B8 h
# tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}% d/ T; Y# J' q+ ]
city_map = folium.Map(
5 k6 n$ V2 y8 v- Z' U& Y location=coordinate_orchard_road,1 H) h0 Z- q8 q$ h% ]0 T
zoom_start=11,
) a. s' q% V% m" \ tiles=OpenStreetMap)
+ F: N; i& D" W) Z; d- U8 j! O
, ~) \+ R. }3 w# ] # add marker in the city map
. q8 l, n6 m& A folium.Marker(
8 R3 S, d5 B* K. |3 N1 N7 } coordinate_sentosa,
& p. a+ n$ m: d: U. ?) U( S icon=folium.Icon(color=blue)
1 v' u3 ^# O) o# V4 G( s3 c$ Y% I ).add_to(city_map)3 M; Q- X* ~. C5 i, Q( h
folium.Marker(
" _1 T2 \$ U! ^2 k) _0 D coordinate_orchard_road,% m2 f5 P: I- _1 i( j" U
icon=folium.Icon(color=green, icon=icon_cloud)
?: m" o" L8 P8 U8 V+ z9 |8 V ).add_to(city_map)
+ F. z3 N& q. r. a$ `. M4 A; a( q3 J+ Y
# add popup5 x: L) b( y) H$ j
folium.Marker(+ o4 G6 C) w! }0 _
coordinate_changi_airport," ^9 j! Y) v$ o. ]/ E2 Q( R2 Z
popup=Changi Airport,
' k; X7 R+ f2 k icon=folium.Icon(color=red, icon=icon_sign)- n4 a. F3 t8 A6 `2 ]- _
).add_to(city_map)
6 K# L4 f0 E- U0 i
Q1 ?- ]+ w/ q6 K # add tooltips and popup
, o/ f# O& q- e, W tooltip = "Click me!"0 |# _' f V- ^2 [ \7 \
2 S9 [) [1 ?3 L# ^
folium.Marker(8 _$ d7 k- _4 U3 K5 U6 t; e
coordinate_nus,
$ C, h4 \8 c5 ~) q! |1 @ popup="<i>National University of Singapore</i>",
8 a2 m8 {7 |& O1 k. e tooltip=tooltip! e" z, J: j" L/ P- g
).add_to(city_map) l3 ^' z+ g3 H: M6 w0 B
3 N$ i6 {" C' R* U2 F) \ folium.Marker(
& C/ P! f4 T/ \) b5 B. S6 a coordinate_ntu,
/ c' K5 A5 d0 V popup="<b>Nanyang Technological University</b>",* a, O& {- G C: N
tooltip=tooltip
7 ^7 a/ t+ v+ V4 E, d3 T2 R9 O9 o ).add_to(city_map)2 i6 P' C1 U1 q
6 f! R( W+ O6 ^
# display city map$ O2 w9 t" r' k' A* r
city_map $ C" T) c6 N0 h# n& g+ p' {7 \
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
. t9 ]/ M9 Z c" S" f! l* D # define the city map
/ L5 D+ ]- S+ { city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)$ ^# k% f- c; K8 F9 P
4 T3 O1 @- a# E
# 在地图中添加经纬度, add latitude and longitude in the map when click
5 L4 o, m& t7 b9 p" Q1 F/ ^3 z city_map.add_child(folium.LatLngPopup())
9 S! g9 X+ X: Q$ h N2 ?- h* ~9 z- S% G
city_map
/ L6 }! h2 @. \+ [& j! R
) p. q, ~; j: F1 h: g X( C 几何形状
( y( d7 H. [ g# j# f3 ^ 在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。 7 L5 [( L Y, S0 Z( w( B+ g% Z8 \
# define the city map
. `4 y- {/ c7 Q. q0 F& d& ^% U city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
( A5 ^; f6 M |; m1 V" i% \; A2 E
# 在地图中如何添加形状! K$ h' b" S$ [& _
# 多条边8 ^7 l3 Q3 |; r! O' t! H% K
points_1 = [
% P0 y- [" D7 m: P coordinate_ntu,6 ^! ]6 @3 h5 G
coordinate_nus,
; ` _' Y8 h( H0 p coordinate_zoo, i4 i7 v" O8 H4 _
]
/ o+ l& N( ?# X
9 A$ O1 b' E. D6 L8 O # 在 city_map 中添加多条边,第一种添加方式( [6 i# e$ ~- R% ~8 d. r( X7 B- b0 y. l
city_map.add_child(folium.PolyLine(
' O9 ]. N/ g1 B" F. M' A& A locations=points_1, # 坐标列表+ d: q2 U$ F; P7 F. V' A) q
weight=3, # 线条宽度0 E/ L2 S! g2 s, l
color=gray))$ C8 h7 o% |6 \ D/ {$ M
: S7 k: e% g6 [# n/ F8 k, _
# 在 city_map 中添加多条边,第二种添加方式
3 x5 z- B% C" b1 Y$ \$ A# T' k folium.PolyLine(
! ^; B6 V" o( F3 F: K( S. N locations=points_1, # 坐标列表) `3 [0 `; f: E0 I
weight=3, # 线条宽度( {6 i: V$ I3 v A; ^$ P
color=gray).add_to(city_map)3 E8 u* I7 {; d/ X3 N. m. J
- _9 L5 P+ [- F" C9 r # 多边形) V, _3 P- v! y+ V8 y
points_2 = [
3 x, c" E- H5 `% W coordinate_orchard_road,
& }% j& \$ b6 a: L7 q coordinate_sentosa,
$ O" t: j/ h3 h* e coordinate_changi_airport
) p- f6 o2 u# x* H2 F( r ]
- _0 A0 L8 m1 R9 B2 I8 w2 c. f; }- x/ H
city_map.add_child(folium.Polygon(
& R1 }; i a# m/ l7 b locations=points_2, # 坐标列表
( J ~* o5 ?, X1 a weight=3, # 线条宽度
: |9 T& E+ z, g* g/ c; g' W$ H color=yellow))* M" a K3 N3 W# o# b
$ N* V" }* N! o
# 矩形, p) u4 E! [& i# Q a% `- M" ]
bounds = [
. ~7 B0 K k5 G& H& _ coordinate_ang_mo_kio,9 l$ b! t" f9 ~ Z7 `$ N3 R
coordinate_yi_shun
8 A7 q9 M. T, X" m) ~, g ` ]
/ x+ q& a1 y1 @- [; f% G; {, h, g* W; ]9 k6 Y
city_map.add_child(folium.Rectangle(: L, Z2 l, x! H& _9 q% D
bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
- o J" a! |5 S0 o* f weight=2, # 线条宽度, A9 ]5 o! F; ?( l$ ?
color=blue))3 Y' t; e( b8 v8 b4 d
# r, E$ B5 w& M4 ?
# 圆形, circle, radius units meters
' `5 T( f* C! R6 c/ _( y9 x folium.Circle(. P" e- c' @* Y, q: y0 F
radius=1000,) _9 }+ z* z9 _$ s' i* C
location=coordinate_nus,0 Y+ G! ?( |4 f. ?
popup="National University of Singapore",
+ L1 K# z# @0 m, G0 t- o3 J color="crimson",3 `+ |1 i3 g [* [- ]
fill=False,3 q/ `9 j7 [ W: ^2 M8 a
).add_to(city_map)' \; Y# d7 U3 e9 O# N5 F
; i7 O+ O8 [1 A
# 圆形, circle, radius units pixels
. X E! m: l4 e! H folium.CircleMarker(: n" y# I) _* }( B- \' u v0 B% V
location=coordinate_ntu,, F3 }3 D. x; x
radius=30,
( E" S3 A% A- ?' V popup="Nanyang Technological University",
, ] R X4 ^, I# W$ i color="#3186cc",6 h/ x) [% ]! [" I( J
fill=True,. j, u1 T3 t" E& k$ a5 U, \
fill_opacity=0.3, # 透明度. v+ m o* S+ w0 w5 @% T
fill_color="#3186cc",
* i, E5 ^( @( O! Y* y( } ).add_to(city_map)0 S. O7 {/ g. U3 e
; J4 z% u! }6 W/ F& u5 ?0 r- l7 N
city_map ) C3 `# }5 g: u. g2 Z" d
Folium 中的画出各种形状热力图
1 j6 s& p- w0 L! `# O6 ] 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。
5 t) U0 w; n& n4 X+ b" s8 p import numpy as np! C ^7 \3 A# O5 J* |. }
from folium.plugins import HeatMap
/ j( N. O3 ~, A% t2 J. J3 S! ?
1 v r9 {1 D9 p- s3 o g* b # define the city map, e) i. x8 d) X/ S+ S! y( r5 u
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
7 c8 h/ S0 A! r e& P( Q) r) t3 B }3 i- q* e# i K
# 构建随机数据
) r) k% t6 x) `* N- i* h# s# T3 \ data = (9 s: O! [; ~3 o* p
np.random.normal(
K, B1 y& | m' F! ]7 g1 A# | size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +; Y3 [ I. K. Q1 a
np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
( ]3 r7 y) L H6 B, t( [0 {6 O ).tolist()' V' n. x( L; _6 x1 d+ X8 }
& Y% n9 K% E$ u$ T" f city_map.add_child(HeatMap(data=data))* X: L; i% {1 K
city_map
. ` C4 N' c0 `+ r; Q' R ; {. e' i" b J1 @, g; u. j2 _
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。 ) G9 ?) ]+ w$ e+ N. j" J2 x
import numpy as np- k8 h4 {1 d: z* \
from folium.plugins import HeatMapWithTime
; l# h$ Z: A8 M) }
5 r2 `" ]1 F4 C2 T. Z4 R # define the city map9 q1 D2 K9 r; t6 L J$ n
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)9 B9 D. p/ {- G2 R+ ^+ H/ Y, E
4 h3 W! j8 m f. W3 N | # 使用 numpy 建立初始数据
) ~9 O7 |/ V0 n2 t S+ R) r K initial_data = (np.random.normal(size=(200, 2)) *$ X( @5 a5 r" l: ?' f
np.array([[0.02, 0.02]]) +/ q6 n# w' |- `" C! q+ y) U, @% I
np.array([coordinate_orchard_road]))7 h$ A0 v0 r, T2 h$ v/ q
" {) a5 S ?: ]/ t% p
# 建立连续的数据
# ^6 u1 \3 v0 H/ O6 x2 Z data = [initial_data.tolist()]8 _0 p9 G6 l. Y
for i in range(20): y# @: D9 G4 I9 }! W7 n+ l
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())# G( D! G8 t' B" \' o* e
, _2 i) d5 U1 v # 显示连续的热力图
2 P3 P8 u; h% S2 k- D0 M7 u city_map.add_child(HeatMapWithTime(data))
6 Q# P9 i8 }2 D, y( H) \0 E5 s city_map
' @! s) P3 T, Z8 e2 ~1 _) F4 n% d7 x
+ e6 s0 ^! L( `# j4 R 经纬度点的聚类
, Y: v& M2 z P8 x; D9 e 除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
3 U( {- m7 j, ~. q! l from folium.plugins import MarkerCluster& V9 J3 V$ C. g
( B U m7 _+ R9 | # define the city map7 |6 Z7 e- k. [" F" [( F
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
+ V( `5 Z+ Y* E9 Y r7 T: l
$ p6 j s: ~5 {- x; x% u* t # 经纬度的聚类 P* g- [, n& N# j7 E% N
# 在 NUS 的经纬度附近随机生成 100 个点;/ S2 l9 Q7 |5 _: f0 i
data = (
% O8 |7 }( \ u np.random.normal(size=(100, 2))
/ }: d x6 l6 V U8 Q; w5 |, i% L( E * np.array([[0.001, 0.001]]) +
. ^( k$ R1 Q( v& A9 I; l7 w np.array([coordinate_nus]))+ K2 G+ W2 ~1 Z
& k7 n8 q' z- C0 w/ p% ] # create a mark cluster object
" a2 Y9 O+ _, |5 K marker_cluster = MarkerCluster().add_to(city_map)
# p% S; Q- a$ c7 I! N+ d4 u" k& s. H
& O9 X" c* @* l! [ U # 将这些经纬度数据加入聚类, m4 J" M8 a' A, h
for element in data:
# ^' T8 v4 z% W+ i. E4 j folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)- C* W' `) ~& J, P* R
7 D9 Y/ ~5 Q, N( D, }3 ]
# add marker_cluster to map- |6 K, V, W; u& ?
city_map.add_child(marker_cluster)8 s1 D% x. X$ A: B1 H2 N5 R0 g
7 I. ]. ?1 K+ i/ V: r
# 作图# _9 `8 C7 M. X% A6 `5 g# n b
city_map
) f/ H4 \9 G' S7 Y5 W9 \5 O7 [; N $ f" C0 {+ r, O3 @$ W
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。
8 V: [' k. S5 m) X 参考文献5 U& g; y' {' z
Folium 官方文档:Folium - Folium 0.12.1 documentation 1 x) r* D1 E/ g
2 `3 {0 k" R" t; m6 O9 i) m
. b9 G+ {, e" k# y) N9 o% i5 I$ I3 T) \: A$ h
0 x) l3 b4 h5 t% O" u) D4 w
|