& M n; K0 y, _% M8 _$ j& z) L/ P" a+ X Folium 简介. h" o& h* d& m( H% B
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。 7 O; }2 V5 b" D( Y, [4 U J( p
创建地图
3 o' z; h1 f! p2 a) y 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。 - q. I; F( F U x# Y
import folium/ |& e. U2 F3 c1 ?% C ~4 V/ q
%matplotlib inline3 V& s/ e& }* y0 l* H0 p) p* u
* A$ W- V" X! b3 b. g
import webbrowser
, ^) `6 @8 m ?8 U& n/ g% g* b4 `
print(folium.__version__)
F( S" S9 m1 |- F+ ^* @8 x2 B8 I1 C, g4 s# u! m& ~
# define the world map
' h3 n6 j: M7 L, r6 O world_map = folium.Map()0 l- O9 a) e7 \" U W: Q/ f
# display world map4 G# M' ]7 u0 I* X
world_map
) t5 x7 u* P4 n+ E, M ; ^( F* p5 T6 X5 z- {2 W: E; `
世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。 : N- F! X9 _8 ~" ?
在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。 4 t! ]! o8 l: u o, Z2 E
有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
$ g( V0 g" P% | 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。 : \9 m0 y' r$ q
# latitude and longitude in Singapore city" a$ l6 ]0 Y/ b/ |' O: B7 g
coordinate_sentosa = [1.248946, 103.834306]
9 ^- E6 N; o( Q( {/ q, u coordinate_orchard_road = [1.304247, 103.833264]; n7 e/ a$ L. M
coordinate_changi_airport = [1.357557, 103.98847]
e5 Q- V% r9 H7 j coordinate_nus = [1.296202,103.776899]% @0 p4 m, n, V* T% ?
coordinate_ntu = [1.34841, 103.682933]
" C' ?& R" e" P2 [ coordinate_zoo = [1.403717, 103.793974]
/ n2 y" O- P- Y2 x; _- b2 Z. ] coordinate_ang_mo_kio = [1.37008, 103.849523]
) i2 R- M m* @" Y3 }- m. L0 A5 O; e coordinate_yi_shun = [1.429384, 103.835028]
2 z$ F2 p0 a. l8 ~: \( I; `0 Z5 ]5 \& S3 Z" `) n; G4 F
# icon
* A" D( k( {$ Y, L$ ` icon_cloud = "cloud"
- Q) o: e: z4 F4 ]- C- h. a icon_sign = "info-sign"- \+ y$ f- B8 C2 G
8 h5 Z& @- q8 ~( t9 U2 ~7 j
# define the city map
( b: E1 i$ B: X6 m: |! i # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}
# ^' \8 Y( e; [7 @$ e7 Z city_map = folium.Map(
' k" [5 Y, m, Z location=coordinate_orchard_road,
; {4 A, R' Y' j% ?* Q zoom_start=11,
# P7 w* R1 t: l" e tiles=OpenStreetMap)0 O/ ^3 B3 K' \6 k8 D4 N1 ?
+ ~9 ]7 ^0 H7 H0 Q # add marker in the city map+ I L/ |4 [5 L2 S# L' Q
folium.Marker(+ s+ R. b; c1 m% J/ b+ @1 ?
coordinate_sentosa,
/ D! U) G% C( G& B* b) p' L# l icon=folium.Icon(color=blue)) [+ W, F6 b" u p& s
).add_to(city_map)6 H, t' I% ?* }
folium.Marker(2 D( G3 A% H' ^5 b* F
coordinate_orchard_road,& {9 W! Z; d5 j @% d& J
icon=folium.Icon(color=green, icon=icon_cloud)( c: C6 I1 a7 ^9 }
).add_to(city_map)
9 v' o2 g+ K& [
4 Q5 L4 Y' H7 c3 J+ l # add popup* L- K: e& V6 V4 g
folium.Marker(
$ d$ M# B5 ^ ~# I0 u coordinate_changi_airport,: x# Q0 t; ^ I7 S0 c
popup=Changi Airport,
- n* D& r7 W+ Z$ Z icon=folium.Icon(color=red, icon=icon_sign)
. I6 [, Q/ k H, p B4 _4 [' n) \ ).add_to(city_map)
# p" h' s& x5 [# \$ O* r% x. r2 i3 `* x$ j1 x6 p: c# C# @8 G
# add tooltips and popup$ [0 }- S1 |9 p0 F
tooltip = "Click me!"
+ [9 M7 `8 O" a0 ]' u2 P' q
; }# K" V- I5 Y, F- @ folium.Marker(
1 _" z3 d8 r) ]8 E1 p+ L2 h coordinate_nus,
& S9 O9 j: w& d5 Z popup="<i>National University of Singapore</i>",
7 r0 H* V7 f9 Q6 s3 r tooltip=tooltip7 v, p1 X3 N: F9 ~
).add_to(city_map)1 y, j+ v+ o* P$ h$ W
7 |1 D2 A6 d a
folium.Marker(0 X2 L6 n& s0 G1 Y% R G5 e
coordinate_ntu,; T- a" s' S5 P! D! \/ G6 O6 w% V2 p
popup="<b>Nanyang Technological University</b>",( \4 v" N* R1 ~0 R
tooltip=tooltip# `8 P7 m9 L' s# J. N. H
).add_to(city_map)
- p+ F% h p$ U1 W$ g9 c
' b h2 F9 o+ ^3 Z* `7 A* [* m # display city map* M/ X) J2 i2 j/ J6 I$ [
city_map
) z" ?* x: u6 ^7 } a$ z Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
0 s& h7 r1 z2 ^4 P. z0 ]) I # define the city map. h: f3 W, K. e( \* T, G, S- V
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
- p5 N9 E5 P% l2 `" b* _0 {
6 A/ J9 H& Q) r* V2 B' t5 i # 在地图中添加经纬度, add latitude and longitude in the map when click
) b/ O; x, L5 g5 p1 j city_map.add_child(folium.LatLngPopup())! f+ Y! ?2 Y: |0 \5 F+ r
% l1 b% g' u6 D! r" b; d
city_map / E& M% q( e k( j g
/ G6 w4 o# t+ `2 y M9 ]& s
几何形状; T* J" Z7 X, x1 S: q; }7 V
在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。 / L& ^8 u! m& H0 u$ _# o5 w
# define the city map
$ V+ r0 S6 U5 }. E4 ^% ~ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)8 N* ]# H7 K" ~* C5 e" b# h
: V5 W: B6 Y5 p' |2 _, m
# 在地图中如何添加形状
6 R3 ^; q e$ D- c # 多条边. w# @5 F- M" k5 I& C4 Z( x
points_1 = [ f- ?- Q8 c5 y1 w8 D/ }
coordinate_ntu,$ K; e6 H) e. T2 b8 z$ A$ s
coordinate_nus,
+ E" V4 A+ O8 ^$ G& }! L coordinate_zoo+ \- ^$ K6 j2 |' ]& O
]
- p: w6 n6 [3 @- S; r
1 Q: T8 ]2 ?5 \+ [' K # 在 city_map 中添加多条边,第一种添加方式$ y* ?4 v+ i$ m3 G
city_map.add_child(folium.PolyLine(' Q- r$ A1 k7 g8 D' e/ n# L
locations=points_1, # 坐标列表
, F; |" u' Q. @$ i: H weight=3, # 线条宽度
/ D# S. q2 s9 N! e* g/ q color=gray))5 y' Q5 m6 z( k
7 f& H. O9 j/ n2 `+ Y O # 在 city_map 中添加多条边,第二种添加方式7 R5 k* g+ m& g, q
folium.PolyLine(+ e) C+ p. _3 j9 n! v
locations=points_1, # 坐标列表
7 y# I$ V. Z- a1 S weight=3, # 线条宽度7 `& ^4 z* R' z
color=gray).add_to(city_map)
7 U" z2 \" {3 t( T( O! f. B( W% d$ U6 v4 [7 `$ C5 O4 Y
# 多边形2 g( K5 R% ]* o: w. ~7 R; {+ i# C
points_2 = [. Y- p. D/ a0 L9 f- c* J i
coordinate_orchard_road,( B- T$ b& j1 i& f8 `4 \
coordinate_sentosa," b" G8 i+ y8 Y% H: x$ }) m
coordinate_changi_airport, ~+ ~; ~/ D) r
]
2 I- |+ k) d* w$ F
% N( R- z; c6 f4 `7 X7 \0 N2 Y0 B city_map.add_child(folium.Polygon(9 D% ?! |# ~8 `2 Z/ \7 p
locations=points_2, # 坐标列表
& y) Q- d7 M% B% o# P weight=3, # 线条宽度
3 ~4 Y$ K* ?8 |" F( g* t color=yellow))9 J' o+ W P2 Z* d
' o2 p: Q1 }) V6 i, d' g # 矩形
0 ^! ]0 t: U% o- M9 N3 c4 D$ y bounds = [7 r- v* |7 \& A; b4 f) O! ^
coordinate_ang_mo_kio,
2 j/ e' X0 w& V0 R7 x coordinate_yi_shun
7 I. E- P/ u; ^* M- E: D! ^" x ]! Q* |' }9 M% H& F
4 B2 X( W- y/ h; Y* t
city_map.add_child(folium.Rectangle( ]3 L, @/ K, S; R+ t+ u; `2 E+ p
bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)$ n+ f' D# K7 k n
weight=2, # 线条宽度
# O9 i6 {* H7 g% ? color=blue))+ j: A2 o( C; r5 m0 I
6 ^# ?4 D! u# k# j7 F
# 圆形, circle, radius units meters
) \4 H: a, i! L. d! T folium.Circle(
, C5 {) y3 W9 @7 Q radius=1000,+ X* e# N" x9 ?% p
location=coordinate_nus,% U" t# t8 C! k7 a3 f2 e3 D
popup="National University of Singapore",
6 O+ h+ q1 k) u0 m# T' @ color="crimson",
% M8 V* I8 n* o- f, Z$ N fill=False,
A0 Q% ?* E# S- U ).add_to(city_map)
) m" o( x+ |9 S
/ D2 t4 ?" c, K7 |2 p # 圆形, circle, radius units pixels" K V7 m0 Y' c9 g2 G# M, [
folium.CircleMarker(0 f5 X/ | x" N' K
location=coordinate_ntu,( T* j- c r. W4 C3 `
radius=30,3 T7 j: O) R0 y3 `) |: K) U$ z
popup="Nanyang Technological University",
4 j3 @( m }/ w' i: k, {9 p color="#3186cc"," q% u6 h6 w# N! h. |
fill=True,% d# e' X _: p
fill_opacity=0.3, # 透明度
! h' `1 ?" a$ q! K$ ~- W1 [ fill_color="#3186cc",* ]* u7 h% Y6 } y; M9 M
).add_to(city_map)
! V3 l8 ^0 S6 r. p1 Q" t
& D& J( n' _0 H! k" z! m city_map ' s/ H% C9 Y/ w. G+ R: M2 S$ ?1 E2 d
Folium 中的画出各种形状热力图
: c- s' f7 @; y 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。
. e p9 ], ^9 }5 x import numpy as np' M8 ~! { Q0 q7 S
from folium.plugins import HeatMap
% m, E3 t$ f1 a9 o& S
( T7 P. M( D$ F" C; J' c # define the city map8 [1 `3 M$ C# k- o2 T) ^
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)- D/ U* ^0 }5 _
7 \4 V# k& r! _7 W) x x # 构建随机数据0 D8 U( t7 n9 U
data = (& L( L/ Z& {# d8 j i+ n: O2 s
np.random.normal(
' X+ }3 O6 |' L7 ^ size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
' V# l' ~! m! u" ]9 p1 [ np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
! N4 F2 i4 s6 `& N8 S* A ).tolist()/ m4 G# V( y4 r ]
* O8 s. ]6 S e# A s
city_map.add_child(HeatMap(data=data))( ]1 p8 G/ Z. A; S5 m
city_map 9 ~) r& {6 q. U: S& ^6 c
& a* C: n( `) t0 S 除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。
1 |+ A' l# c/ K7 i9 y# j import numpy as np
, `& v5 z5 c8 s8 z8 g4 |7 V from folium.plugins import HeatMapWithTime
& D3 W& p: x3 V7 O4 M7 W
. C8 x3 `) ~! B- }$ S # define the city map
( y$ x* J0 f4 t+ x% E city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)- _" @1 i6 }. ~. I4 Z6 _
+ s* ~! h) k8 K4 U" [ # 使用 numpy 建立初始数据
$ B( N/ l( O: y" z5 k initial_data = (np.random.normal(size=(200, 2)) *
! s% u" T& l7 V np.array([[0.02, 0.02]]) +% ~2 A/ i9 P/ y, e9 z4 I8 }
np.array([coordinate_orchard_road]))* _3 m' @6 R! m
: k8 l6 n6 ]1 p& k/ J # 建立连续的数据" W3 y7 F4 G2 k Y1 }3 Y# w* A
data = [initial_data.tolist()]$ x3 h, `& h V- L4 F* y
for i in range(20):* Q% f! e4 H4 p, T! J
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()), x; y( }$ B9 ?5 x- x: \& R& e
3 u( |$ i. {+ ^
# 显示连续的热力图
9 H( k$ B# \, c I" l city_map.add_child(HeatMapWithTime(data))
3 k/ \: ?- b5 r city_map ' G) t4 x& e7 j, c
4 ~6 t. o: i( [4 F" B7 _4 p5 Y 经纬度点的聚类
: P% u; ~! f( q. v( M- @6 V. c 除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
" y1 @4 q" \2 ~5 w from folium.plugins import MarkerCluster7 G: b' e+ O) p" Z
5 [5 E- `% g0 y8 Y$ F( U! e
# define the city map
) P6 Q" L7 \% x( R' q% G, T4 P9 g) B city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
9 {4 K5 Y3 S$ r+ l8 H
D0 Q/ m& _6 A$ e% r; O # 经纬度的聚类1 \% Q" y: y5 U+ |- c% c/ U
# 在 NUS 的经纬度附近随机生成 100 个点;
1 }3 v& q2 P- z' c+ Z+ T data = (
( H( D+ A$ q7 Y+ ?' h np.random.normal(size=(100, 2))
; \( a$ f: L$ [* u * np.array([[0.001, 0.001]]) +" m' L# j, ^- }8 w. F6 c
np.array([coordinate_nus]))3 V; a3 [3 b9 l, J+ \' s$ u
( S P9 b% o- p0 f9 j5 o( d # create a mark cluster object" D9 [: x: U3 \3 T/ V0 {4 s1 F
marker_cluster = MarkerCluster().add_to(city_map)% I5 B3 h2 C& A- v8 d! N( v. C
- y# B: K. V$ H. z$ M
# 将这些经纬度数据加入聚类* r* X9 l9 _2 E j
for element in data:) z, N* }& U9 x8 E2 C% a" k3 [
folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)
7 _6 m# D. o% P7 J4 n/ g% ^' O# d$ p2 e0 E2 \- c' Y
# add marker_cluster to map, L6 a3 }; j7 ]6 M) G. b% R8 R% l
city_map.add_child(marker_cluster)
% V0 X/ g/ \0 f- w* A8 R0 t7 |( H& x$ d" d
# 作图
! Z+ {5 [- r( h. Q city_map ' W# v$ z* D- H: P: Y+ x
% g4 M) B) M" y. U$ B
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。
6 E8 k* u3 ?+ B1 R( } 参考文献
3 N" R9 j; A% @1 O' f$ \+ A Folium 官方文档:Folium - Folium 0.12.1 documentation
" H0 J" g9 C( V/ K/ c- N$ Z" M- K) ? R9 p# r& A1 a$ J3 S
( J/ [4 ]. A, w* x* ~9 ]
1 D# K5 ~8 e" } }1 ~: s7 |' V/ H: V- M( W2 d
|