- M) f2 f1 G+ \+ o# }5 n
Folium 简介
* l; N, H2 c3 F" N ?4 H; e! K% C 作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
# X6 v3 u8 `* F1 C1 j1 V, K 创建地图
* F2 o! o6 g/ S0 {, G) G7 R* a 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。
. y4 U$ t. M; | import folium
. X6 C! M3 P- u8 A %matplotlib inline
3 R! z3 E# `( q; p2 D
) S& S4 F6 A3 q import webbrowser$ n2 _* [* W1 ~% I8 a5 c5 h0 Z
; W( N! Y0 m' C- S print(folium.__version__)2 j% p+ p7 U# ]; u/ E# L
% p' t( j7 m/ X: b: q# ^, \* F0 { # define the world map
4 h1 S: `( c6 w world_map = folium.Map()
7 h. w5 w/ m4 i" D4 e% y # display world map% }1 U; c0 n/ Y
world_map
0 u/ C# L" e8 j 0 C0 P/ c, m5 ?' X1 U& r
世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。 ( Q' C+ W6 T/ g. y3 Z
在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。 7 E7 t. m+ n% y2 _$ a0 k/ S( ^
有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
3 O! @* `& I3 U2 p 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。
, a; |7 E+ B( g% A # latitude and longitude in Singapore city5 A) {) O! S3 C3 F
coordinate_sentosa = [1.248946, 103.834306] _' B) D! q- D0 [0 ^' x+ `1 J( S! X
coordinate_orchard_road = [1.304247, 103.833264]: y k9 |& h$ p/ s. |% v2 V% d
coordinate_changi_airport = [1.357557, 103.98847]1 v$ A& w& b8 s9 y5 c5 L7 i
coordinate_nus = [1.296202,103.776899]
! O; J6 P' L+ A$ R* J" A7 q! { coordinate_ntu = [1.34841, 103.682933]
4 R$ B1 O G* K3 f q; D1 ^2 _1 @& c coordinate_zoo = [1.403717, 103.793974]7 y) I2 x& a3 r) K% s7 ?9 s2 H
coordinate_ang_mo_kio = [1.37008, 103.849523]
; E' h; n. L# A0 ~$ e/ {+ p coordinate_yi_shun = [1.429384, 103.835028]
6 s" t# M. z3 J, k
$ l! [6 G6 C. [* z # icon
+ n# ?( U2 D) \' g icon_cloud = "cloud"
( Z8 |5 n$ j$ G7 u' H% N9 d: e( A icon_sign = "info-sign"( q9 B3 R2 B9 P6 Q% ? L/ `3 @
1 Q Z$ b5 m; |1 ]* d4 U! n' _ # define the city map
! Q W+ S8 F! _# b6 R, Q # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}
" F( B# o- x- G8 q" B city_map = folium.Map(6 I5 a! X3 r3 t7 U1 H' E* U. ?
location=coordinate_orchard_road,
& C' ]/ B& O3 u& z G" L n zoom_start=11,( c/ h2 V/ d/ k/ j* n/ B
tiles=OpenStreetMap)
* a) C6 i; [3 q, l+ K4 n$ [
) L, u4 {9 G/ k1 w1 [; V # add marker in the city map2 V1 x& V2 j/ c! C7 |
folium.Marker(
9 y6 r) L. D! `+ [ A coordinate_sentosa,' w, t& b" r9 d8 ^% f
icon=folium.Icon(color=blue), {1 G+ c u O. H$ H2 M' g
).add_to(city_map)
. I7 q8 k0 H0 |3 x3 N, \5 ^ folium.Marker(
6 l& I s) R+ [! P5 G1 Z" x2 k coordinate_orchard_road,8 h- ~6 w! E3 z/ F$ W5 f* k
icon=folium.Icon(color=green, icon=icon_cloud)
+ `( v4 _& N9 L' x o9 P2 r8 U ).add_to(city_map)
- \. e: d/ @% Z, D
9 H3 {9 a* H% S( b # add popup& H& b. l; A, l' _' Z8 Z; L
folium.Marker( m0 G, k% N% |( V2 \+ |
coordinate_changi_airport,
: W' t6 w* m6 z9 a popup=Changi Airport,
' K' T; N/ r+ x1 @! n3 m% d icon=folium.Icon(color=red, icon=icon_sign)
3 k+ x ~: }9 f4 r) x ).add_to(city_map)1 p; o/ T1 {/ A# K2 V$ H& w
6 t( ~8 _, j1 x/ V
# add tooltips and popup7 A& g% W: D/ ]
tooltip = "Click me!" h0 E1 m. I1 Z2 ?2 V Z
4 c; m8 ~; v+ r9 { {
folium.Marker(5 ^ M, r6 d; \* w- ^
coordinate_nus,+ B: s& Z5 f( V
popup="<i>National University of Singapore</i>",
- I$ a- l! U2 F/ E6 p tooltip=tooltip
! v) Y) E4 } f ).add_to(city_map)
4 {7 \3 u+ M% a w3 l# H
2 h! x2 a9 ~6 U9 A$ M folium.Marker(5 Q4 P+ C7 ~- y4 R9 \
coordinate_ntu,
/ d% Y! Q; O+ {0 e% Y$ q9 L popup="<b>Nanyang Technological University</b>",( r4 b5 }) d, A7 ?6 V' P, \ O5 s
tooltip=tooltip& z ~! C* c5 r
).add_to(city_map)9 H* n4 r! S# |. W* P) w
4 P% ?/ Z7 ]; I8 A" E) _) F7 }0 p # display city map
- i+ E/ t& Q) z: b0 z1 p( j city_map % D% @1 C( [! s$ N8 Q/ J0 i
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。 2 _+ D5 e3 L+ ?7 p7 @9 A: e9 Q
# define the city map
! n0 [! z" d6 F5 u city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
/ g0 `" s9 `) F: u% q% d. ^1 y2 D) E9 w0 r! A6 m
# 在地图中添加经纬度, add latitude and longitude in the map when click
6 E6 h* N( n, I9 t& a/ p city_map.add_child(folium.LatLngPopup())
- _ t0 Q& A% a! U! X6 k$ D4 ~. E& K# e7 f( ~( f, l" N" O
city_map 7 }+ T/ X# q! l& S6 m0 C4 y" X7 \
% {- a! c# @2 n7 }: b! w 几何形状
" ~# o9 s) B5 R# _/ A 在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。 1 I3 s; _; k: }: V! o
# define the city map8 o' {. j5 l( ^ u. b
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
3 B% T2 w, T2 I X7 W* o9 O9 l: V3 n
# 在地图中如何添加形状3 X# h) v( j: I* x4 J6 ^+ z
# 多条边& a' }- G x$ Q. V/ |
points_1 = [
( ?: y1 l l% p/ C& V coordinate_ntu,
0 a h+ y- Z6 L* ` coordinate_nus,
# n }, O# m. R- r+ |6 B8 Z6 [* T coordinate_zoo
# q0 v, y' o# m, m2 o6 f) K ]
- [; `( e0 Z( w; s
/ e* ?- i4 Z/ p- G1 f9 z # 在 city_map 中添加多条边,第一种添加方式
, t" ?. n! n) w% t& v city_map.add_child(folium.PolyLine(+ t, }% ~: K5 w x
locations=points_1, # 坐标列表
* L4 ?8 i, \6 p weight=3, # 线条宽度8 S; F& h& A, k* W2 J
color=gray))" _+ U+ y6 j$ e: g( _. P2 v8 }
* [& i, i$ b7 x8 C9 }# d% D0 A) U
# 在 city_map 中添加多条边,第二种添加方式
" H) N: D% A C, `8 }3 o- p3 @; z6 K folium.PolyLine(( V6 k0 D7 v) k' v
locations=points_1, # 坐标列表) O/ o0 n, v# K" Z8 Z
weight=3, # 线条宽度# N ?- { B; p& a$ J7 |
color=gray).add_to(city_map) G# z$ C/ h/ b
5 L4 |- \0 a5 b$ G0 `2 S # 多边形
1 b0 R. l0 J% V4 W( C5 C& h; X5 s points_2 = [3 n% q, t& a' M( i% v M# _
coordinate_orchard_road,
1 [2 f$ C9 ~& \9 h" ?4 c coordinate_sentosa,
4 _/ l; ]& `; l5 @# `' B! V coordinate_changi_airport2 G% j( u1 |. c* a+ @
]2 {/ L, j4 U4 g
6 ]3 v1 C7 U0 t/ T3 s& w& a city_map.add_child(folium.Polygon(! \( o8 M9 ^, R8 u, f
locations=points_2, # 坐标列表
+ g; l+ H, C$ B/ F7 V) }. O' m( ?, W weight=3, # 线条宽度" A- v: C( H( [ R
color=yellow))
- I* u& H. A' S% ^ y; H6 b G: e) K( N { J1 U4 A
# 矩形 @. B2 O& c( q" `* A
bounds = [- ]! [7 i4 j( {( `& I' m
coordinate_ang_mo_kio," F3 P# U( W2 t$ S. D- Y% N
coordinate_yi_shun
o0 v. p3 [, R! B' V$ F ]
6 B$ C( g4 d6 t) W) e3 C: A# h' a% ?) q
city_map.add_child(folium.Rectangle(
) h+ K: K# {( y9 y bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
+ O" S% U- V3 O4 z* a. C weight=2, # 线条宽度
5 `; o! d$ [: H- c4 c. I0 x color=blue))
n6 U" j/ Y! b7 L) \: X) b
) C$ K! e0 F2 D' v4 Z' F% k U$ L # 圆形, circle, radius units meters
% p: T" E7 d0 J8 m% f7 P folium.Circle(8 n9 O" n) y# k! \) u
radius=1000,
( n) d" B) d3 |, ]7 E location=coordinate_nus,$ E0 G2 @4 ]4 N
popup="National University of Singapore",( X6 A" u( Y9 ?3 Q+ x; `+ C1 T
color="crimson",4 V+ v" H/ T8 k* e
fill=False,
' S* X; r3 {6 v+ m ).add_to(city_map)
4 n' R q# r& n6 a0 J
$ H7 F. R% F+ t4 N7 r8 x: l" S # 圆形, circle, radius units pixels
4 a: i7 u7 T, i+ Q4 y+ @ folium.CircleMarker(2 N5 T+ n& D' ~9 Y
location=coordinate_ntu,
/ E4 F9 C2 j' k) q$ d3 @2 m radius=30,
r5 ^- @1 x+ e' |& a popup="Nanyang Technological University",: ~$ v+ {( a7 D; \
color="#3186cc",
: R8 o; v4 ^4 \- w( L; c c fill=True,
i2 L- E' t$ |! n" M3 D6 Q fill_opacity=0.3, # 透明度
! E* G2 v$ r3 ]+ Y fill_color="#3186cc",! b" a; ^* e' m+ X- ^/ a+ t
).add_to(city_map)
$ d; U- { H7 s! b$ J
& j1 ^6 B; p7 \2 y3 q4 ] city_map ! C& J! j6 X+ n8 T8 k4 O" G7 Q% Z
Folium 中的画出各种形状热力图
7 `6 C2 i% K6 J6 J) {' x 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。
$ ~# W! S# s0 J9 n import numpy as np0 f g9 v) o+ p5 r9 Z
from folium.plugins import HeatMap' t% k0 h* ^1 A( y
' F' y& U( l, Q1 X5 C1 m # define the city map/ M7 Z7 }, E6 C% [4 ]
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
! W/ v. R( d* d& B# m
2 Y2 Z9 n }8 Y" r # 构建随机数据/ {, h; E0 o$ I* H' l4 u
data = (4 E, T5 M6 v" N& M4 v4 A) l
np.random.normal(: {7 M& w6 _* ]& v
size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
) S6 h* R3 V& W8 e np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
7 Q& L- B+ m; K) I8 Y# K ).tolist()
4 ^6 x6 L3 Z" M9 i/ G9 d: e
1 t- x x# s4 f& f/ n city_map.add_child(HeatMap(data=data))
/ w5 C4 M% M0 v! D city_map
* N$ p$ Z4 z; ]# n) L v8 {! M
) q2 a1 q, D @) V 除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。
8 a6 y: ~ d g9 s; q% M' S+ h' O import numpy as np
' ]: Q3 L' ~2 |8 J% ~$ ]( b from folium.plugins import HeatMapWithTime6 B2 n% A" B0 T5 L9 v# O! A# V6 F
# \' A- J( I# E( z" J/ ]
# define the city map
: v& q4 w" h* y" Y city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
# T4 e$ `0 H- i P7 |2 E/ b1 Y# F: Z/ g# |3 A% O6 ~& \
# 使用 numpy 建立初始数据
" o. ]1 V! f2 m+ h initial_data = (np.random.normal(size=(200, 2)) *. X3 @3 ~ E* i3 m" R* N1 S
np.array([[0.02, 0.02]]) ++ g. p: w' o9 K
np.array([coordinate_orchard_road]))1 b/ g9 \; i0 s9 b* Y
, o4 k( T6 h& x; I0 S
# 建立连续的数据
8 q9 Z I% T, c data = [initial_data.tolist()]5 @3 L2 P. t C" x" y
for i in range(20):) P R; i/ U) Q: |
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()), P. H9 w: d8 _
; V, W, \* B0 m3 o
# 显示连续的热力图$ X" A! A# e6 ]- I1 K
city_map.add_child(HeatMapWithTime(data))
( m8 b: ?2 X& N8 f- M, i city_map 4 W. a! b( c3 m4 m
8 t5 D* i$ n7 T- o 经纬度点的聚类2 g0 y2 M' X3 j/ ^& K; Y% }4 x* A$ Q& G
除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
* \1 ?3 p+ O! X, i5 W2 y6 X0 [ from folium.plugins import MarkerCluster
! X5 K7 i% L6 N' F/ p' c: y9 L
; B' m, j6 `2 j p1 r" `' ?% x- G1 y # define the city map$ C# W, b: x: v! q# _7 y
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)& {# s7 {) @2 n% t$ D0 E
* ^; c3 B3 q9 n( z x
# 经纬度的聚类
8 v* E) B. ?6 o* x. m7 ]+ U3 g # 在 NUS 的经纬度附近随机生成 100 个点;
* u+ K5 X( M, B2 L; ?7 Z data = (+ ^" q8 w }* Z* J" _, V. k- o
np.random.normal(size=(100, 2))
3 S2 @: W4 X' r4 e * np.array([[0.001, 0.001]]) +
3 [( Q7 g, z6 [ np.array([coordinate_nus]))
3 m1 s! L1 q; z2 d& i$ I/ z8 o/ M5 c( G( A7 T0 y: U, f6 a
# create a mark cluster object' C7 \2 v \5 {* c) [
marker_cluster = MarkerCluster().add_to(city_map)7 {/ i/ X' ]" L. H* Z2 H
( U1 p# O9 | B, o% o5 e4 N # 将这些经纬度数据加入聚类, m5 A T" O! }5 u5 S$ N
for element in data:
6 l) T; d9 I" l* J8 d folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)
0 o/ n- d. V: K. r/ |
0 A' X: X3 ?- ?& N, G5 ~: w # add marker_cluster to map
/ y0 } h4 W3 ~4 ~+ G9 U city_map.add_child(marker_cluster)
7 m) j/ ]$ o3 c& ?! D- B. E `9 ?& z/ F" z
# 作图
$ l% D0 k7 v g6 R3 y city_map
5 L0 ^- d8 M3 T0 Z6 V , B. y4 C" f' e" u
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。
- q$ K; C: X. c 参考文献; f0 A" v) m0 P3 c0 @3 |
Folium 官方文档:Folium - Folium 0.12.1 documentation ! I# N5 D( `7 W; {4 u9 [
) _0 K( N: S5 v8 h1 D3 O% U( `
, X$ v( b, A! q9 r* m1 z
# m; z! A1 w" p$ O' x+ {- r( g4 C X8 y! v
|