0 E; _( x* L @3 K. E
Folium 简介
( w# j; x7 q/ J. X. a 作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
3 p; K) o$ \1 ~2 d 创建地图
& D& \2 D8 j, t% r7 w+ E8 x 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。
5 m+ s, e4 c; k7 a9 O# z import folium
, N- Y5 w0 P" m, k3 @" c %matplotlib inline
! H, X* y3 J$ d* k5 l- s- l! i2 H+ a
import webbrowser
! Y4 b1 l; `% A. Z3 E
, D! M- E7 X, e8 J& i1 M' ^1 K3 C0 j print(folium.__version__)! s: o) V P9 N* q4 |# @ T
0 F X! L- ~& A/ u # define the world map
& ?6 ]* J3 S% x% B6 d) ?2 d world_map = folium.Map()
% U# s. t3 i- m; f+ o # display world map# k* B- C- a- G% r0 D8 j
world_map. R9 ~, E7 [0 R: U
: Z3 ~* A8 h3 q. I6 W" {5 Y
世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。 ) ?2 i& ]) w2 Z+ e7 x% {
在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
8 C- W, M7 C+ l0 b6 b$ L- z5 d 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
8 _3 L8 j! i4 u 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。 # @2 D) H( T7 C7 ~* \4 m: W
# latitude and longitude in Singapore city
6 C1 X5 x' N( G8 Y* y4 m) P" q4 C coordinate_sentosa = [1.248946, 103.834306]
. J3 p# D; q% a$ X coordinate_orchard_road = [1.304247, 103.833264]6 s3 l1 s7 L, t2 g1 f+ x% L7 [; d
coordinate_changi_airport = [1.357557, 103.98847] o6 |8 G3 }5 c
coordinate_nus = [1.296202,103.776899], C5 Q/ G, u M1 a( y
coordinate_ntu = [1.34841, 103.682933]
* I- x- U0 g" n: n; g y$ i coordinate_zoo = [1.403717, 103.793974]0 O- v$ @6 L9 ^" q
coordinate_ang_mo_kio = [1.37008, 103.849523]
# ]& I% T5 E x) `2 H coordinate_yi_shun = [1.429384, 103.835028]
% @: q4 `1 y: O6 K. @5 I' @4 J% E/ [, F2 s3 T8 h
# icon
1 T. D6 L" Z1 L4 y/ H0 G7 C icon_cloud = "cloud"0 y3 }5 v. P( W5 h& {* c
icon_sign = "info-sign"
9 N) }: K3 ^) ~
, w& }" r+ _# ^1 d' H) W# e # define the city map& |4 F% G. E! Z) P$ k6 F4 @# n* l
# tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}' x5 G+ l6 ~% L2 H+ Y% f7 q
city_map = folium.Map(* c: A! P9 v! \
location=coordinate_orchard_road,! r% b1 ? @3 Y5 B( M4 V
zoom_start=11,
$ r5 K% H1 Q. G v/ \$ F. r tiles=OpenStreetMap)
f7 _+ |! v* w) I% v1 y
8 r" A4 W6 {6 k1 \: A- A: v8 c # add marker in the city map
- X+ Y# k" X, Z2 H folium.Marker(
. ~' K/ A7 H' e) q& D* ?/ Y coordinate_sentosa,
) L9 Z: I! H- s: C/ [6 l' | icon=folium.Icon(color=blue)
' H E- Y4 ~ F9 x. f3 P* o. U ).add_to(city_map); Z8 E/ a- b* F% d$ g) a
folium.Marker(& T$ U d" }, z/ Q
coordinate_orchard_road,
; V0 }, P5 b# O/ o icon=folium.Icon(color=green, icon=icon_cloud)8 G; y. E3 B3 d, R2 V) c& Y
).add_to(city_map)
7 I- y" T# Q! f0 L$ ]
~; o5 u# u2 l2 e$ R9 Y/ | # add popup
# ?8 Z% r4 J1 V3 i/ j% {& t folium.Marker(
+ V2 Z5 g+ k$ ^( G coordinate_changi_airport,
3 Q9 d) y$ R, ?2 |4 G popup=Changi Airport,. V) s3 m: ~# ^4 I3 @$ w ?3 K
icon=folium.Icon(color=red, icon=icon_sign)5 V1 E: E1 v* s$ V& _, W/ e2 e) E7 y
).add_to(city_map)2 M- u( ~0 A1 R3 Y9 S
0 W+ l' U( o/ O # add tooltips and popup" w$ Z5 I. m, {4 w; [' R0 y
tooltip = "Click me!"& j! E. v1 x( D' i- W7 T. n9 L# B
+ f9 _( ~6 D" s+ A5 t4 h8 [4 ?9 v1 P$ E
folium.Marker(
) d) O. |3 V R; \9 j coordinate_nus,1 m5 h+ h% T; B3 f2 |
popup="<i>National University of Singapore</i>",, O9 {! t. v m: j& W: \* N
tooltip=tooltip e/ N% V: M1 P; s- z- I# Z/ p2 z
).add_to(city_map): i! L2 M4 }) K" R N8 H+ @. ^" |
6 h. k! m+ Z4 H; E' j7 _- V( d, G
folium.Marker(
$ c0 W" I2 n0 J; a; T* z4 _ coordinate_ntu,
l7 p% L1 |* S9 g popup="<b>Nanyang Technological University</b>",
# V. `( [* d. Z- |4 B4 H( z6 N tooltip=tooltip! h# ^% N- l, U# z! B6 Y, s
).add_to(city_map)$ k2 F2 ^9 R! u+ e4 h2 U4 ]" ~
- c! P8 R; M* g% a& t# \ # display city map
2 h$ l4 T9 ^! ~8 E+ S5 o+ v, D city_map 7 q0 g9 K* x$ o1 @1 o+ c
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。 ' Y* J( ^4 X: m0 ^( a% v6 j
# define the city map+ a, ]$ J3 Z- `9 Q0 K# h2 |
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
" \5 K/ E: |. \1 H) {9 h# n+ ?! G& p' `! I
# 在地图中添加经纬度, add latitude and longitude in the map when click
4 |$ h4 u# K, z+ R, ] city_map.add_child(folium.LatLngPopup())+ s% e h. r! I, f
4 j" C/ c$ m& w/ W. @! e
city_map & a8 U3 s6 c6 w, m! t9 T
8 T! n" K9 ^* ]$ Q7 ]
几何形状! |; }; I: c( Q6 x
在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。 : s' g6 o- V3 r$ h
# define the city map
: v. @& n3 |- c6 v# I" a$ @ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)1 p. a0 u! Y5 l
1 D( }' _# P, n `. k% ]! [
# 在地图中如何添加形状* H6 U& Y8 K/ C+ S1 T1 K
# 多条边
A0 x* i3 Y0 _& H, W s; e4 e points_1 = [1 `1 }8 I7 J& N- C+ J/ l
coordinate_ntu,
7 e7 Y( r! o& @; D M coordinate_nus,
- a( g, ]+ V4 d3 R. Y coordinate_zoo& z0 p% B. ?3 H5 @3 T, L1 J
]
$ Y8 ^1 ` y8 L9 B
* a5 J; v& S3 R6 ]6 Z- ^ # 在 city_map 中添加多条边,第一种添加方式5 t# ?+ [* N: V& c" r0 r+ `
city_map.add_child(folium.PolyLine(
6 F% z3 q6 }- T locations=points_1, # 坐标列表4 \, I) b2 ~% {; F' c9 n9 a, j
weight=3, # 线条宽度
$ V& F9 D( W7 @* t6 N" h# H- C, S color=gray))1 D% H# y. ]' L! [
2 A+ W1 Y4 b2 p1 Q3 n) R # 在 city_map 中添加多条边,第二种添加方式
: z8 s3 C$ i+ ?7 d7 h folium.PolyLine(
6 o; w3 E6 [4 u( D1 Z locations=points_1, # 坐标列表
1 X' {" j. A5 r$ p J weight=3, # 线条宽度3 D' E p+ P0 z4 G& H
color=gray).add_to(city_map)
N/ @7 f, P6 i1 @. d: _$ s2 |8 C( _' i5 K* j# c. b6 B
# 多边形2 u7 ], B5 E- ^, T4 X- U. s
points_2 = [$ S, W7 `: ^7 X7 F
coordinate_orchard_road,
9 U5 I+ S4 o ~0 z$ }* A9 b5 p! K# C# j coordinate_sentosa,: }# A B& O9 @
coordinate_changi_airport3 `: [* o \0 b, b$ ~( z
]& {6 B: D8 L( M3 _( t9 }3 V
, l! i# N# M$ H; w) u
city_map.add_child(folium.Polygon(0 E! _# r* A$ Y1 n
locations=points_2, # 坐标列表0 L+ w0 {0 q: X$ O. K: N
weight=3, # 线条宽度
9 M" A( @& v3 H# r# D color=yellow))8 `, P4 K8 Z! q/ V
6 v' W% M0 ]# D # 矩形
$ b& h! m; R8 I% ^, v3 W. |/ | bounds = [
+ O& p' o& f( \# T6 H coordinate_ang_mo_kio,
$ N2 D# Y7 _1 s; ^. { coordinate_yi_shun- ^9 Q4 r9 `% e: |7 F) g
]/ s: A- M7 Y7 z9 l+ [$ b7 W
; [# v' x1 K7 N# R+ T4 w city_map.add_child(folium.Rectangle(
* e. h; d5 C# F bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)$ y3 [/ [1 I; V% `2 X2 ~
weight=2, # 线条宽度
9 K# G+ }% n' W) h4 w color=blue))
! ?- h7 G7 F( R; a! y; O# M3 V$ D1 K6 Z2 S* J$ L
# 圆形, circle, radius units meters
' c0 ^! @1 z* N; ^ folium.Circle(
$ h- |# _7 H7 V8 B/ O" X radius=1000,8 V: L9 w$ Z( E: X7 |
location=coordinate_nus,
7 r: v! w- u4 h# Z2 Y# b! B popup="National University of Singapore",
* }3 `+ Q4 q. k! Q) M6 ?5 M color="crimson",( g E* s! s! H8 U4 q. F0 M
fill=False,
- L3 Y, U# n; ?7 g. c" D ).add_to(city_map)
( x' w8 U6 _- E! w9 ^, N
# K7 h* S7 u# \& g8 r9 ]* K! U # 圆形, circle, radius units pixels
$ h; J f4 q) c. ?$ ?* Y folium.CircleMarker(( c% {) F$ I" W% {' ~0 T
location=coordinate_ntu,: [3 z6 B+ B. e5 Z$ ^. v' X) S
radius=30,' F* N" ? `0 {) ^
popup="Nanyang Technological University",
8 s* }7 J5 M* a8 ~ L$ K, {- U color="#3186cc",
+ C& q" r% d3 ^# J/ q. [. f fill=True,
* _ S' R6 i5 d% Z( Y. a9 g- m fill_opacity=0.3, # 透明度$ |: e! H7 n6 M, _
fill_color="#3186cc",& K+ `* K1 Z& Q8 x8 q6 W
).add_to(city_map): k( V. E @' O
) Q% z: d8 @# f
city_map
% B$ j0 T' O) b5 j Folium 中的画出各种形状热力图
4 l) K4 C# D8 o# j 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。
" _) b9 C9 w$ x E0 |2 L import numpy as np
6 E8 Y7 b# c% A, V; d7 c from folium.plugins import HeatMap* U* ^* e* ]% j7 d1 {9 A) Q
& O- S' i$ A/ c0 N # define the city map& |5 m# A; M1 P& G- `
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
. Y( [: I& O6 E. K7 @1 M, v4 ~) S$ r; y* d D
# 构建随机数据0 S, @9 M% ?# ^! c5 u
data = ($ Z, e* d* o+ K3 l7 N- u$ p0 K
np.random.normal(/ s. P" D6 S, w/ c
size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +' d( O0 p0 X4 ]8 ?5 C: Q
np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])' K* l c, z( F
).tolist()" [# l( S8 M( R6 K- u4 e
& D, {8 f# Q/ N$ n6 u- N
city_map.add_child(HeatMap(data=data))$ q8 r/ E/ }/ ?2 ~+ x
city_map 0 e! r- h7 i; J1 E5 R
5 G! L5 l0 e0 p) p p 除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。
9 }) b1 T5 W6 v3 d2 k- A import numpy as np
! e9 O/ Q8 y7 \# f from folium.plugins import HeatMapWithTime
, m d2 E4 r- K; q* R( s# u5 d. ?# }. \7 v; ^- c( D$ U' A* O% Y
# define the city map6 I# @. G/ Y, J, w: M
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
. W* j3 n, D) I: t1 W- p7 M2 a* c
; F; f' A. B. Y/ S! h$ F # 使用 numpy 建立初始数据
; v* z4 n7 A/ u3 Y' O* C9 \( J initial_data = (np.random.normal(size=(200, 2)) *
! o; r( O* p. N1 d. Q( s np.array([[0.02, 0.02]]) +
8 U5 l4 ?3 ^ _4 y- g np.array([coordinate_orchard_road]))
/ m$ \3 r2 t7 k0 H& k" P6 t
- |% N5 |1 m! M4 v( N8 Z # 建立连续的数据& |8 A' m7 J2 a0 g L
data = [initial_data.tolist()]
. V$ |- H' Z9 K+ D3 f/ R9 s& N( s for i in range(20):
) N: ^3 \' C. H- p# |4 n; k data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())' L3 Y" W" m6 }0 G8 a0 M
7 Z. |, q e! E; r8 S( W1 R) T3 ] # 显示连续的热力图
/ E4 K6 R; L. f3 c4 W# f0 g2 y city_map.add_child(HeatMapWithTime(data))
$ S, O5 A/ b6 n" S& e' c% J city_map
# I) e& v D' F& A ( e2 h/ }0 b" ~
经纬度点的聚类) V/ ^/ ~4 o4 f6 H: H
除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
( }$ q& x* I# n1 r O( |0 m from folium.plugins import MarkerCluster
8 R4 |& ^+ B# m; W% N
/ B0 J) e8 p; ], y5 ] ] # define the city map
% `- g% l) @0 p: U5 E city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)6 z: K6 N3 k P+ |
/ w( A* X# H% @. {2 ^ # 经纬度的聚类" J- a7 m K/ O( g' v
# 在 NUS 的经纬度附近随机生成 100 个点;
; X1 q; h1 V# l: \ data = (: v( n) d! Q2 y. Q/ y Y
np.random.normal(size=(100, 2))
2 y& l' ^ D' X, Z * np.array([[0.001, 0.001]]) +. p0 `6 A: d8 C! ]6 m
np.array([coordinate_nus]))$ z! E1 {" K1 m/ O2 \4 H& W; {
' ?' H( _8 g% H- Z( U& _ # create a mark cluster object
, F% G N$ Q4 [: J% ]& a marker_cluster = MarkerCluster().add_to(city_map)6 E2 q& I) J9 q( J c( u
b6 J% Z% ^- d5 e2 O # 将这些经纬度数据加入聚类$ G. ^( w1 T7 n$ }! c3 T( l
for element in data:8 k0 ?# q7 B' ~2 @: C
folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)
* I" j* T. ~+ x l" y* j: _' Q1 q! [7 @; Q0 ~7 |; @$ ]
# add marker_cluster to map- O5 D% O& }0 U+ s6 Q& g/ B. B
city_map.add_child(marker_cluster), ~& k! Y7 Z% U5 |6 q6 {
6 @, v9 K1 S3 Y2 B0 N, F' }2 z$ f
# 作图
- z' N$ q2 s! Y) w3 l H# W city_map " D, z: e- ^! N u
! y2 ~* l) a" w+ v; S5 e 以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。 ! T1 ^! o4 j( V6 B2 V& G0 G
参考文献
1 V* ?8 P3 w( d& s8 s: e Folium 官方文档:Folium - Folium 0.12.1 documentation & D' V2 l' [3 r4 _
: g& {; h: [; c! [# e m- r7 G& n* R! t5 J8 v/ b: @
& m! g4 ~/ B( }$ |* u7 o1 j V/ a5 Z/ H4 G1 Y. a
|