J3 U3 d# S$ z# j/ Y7 K Folium 简介
8 x* ]- F+ X/ w5 z0 c M/ N" e 作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
3 c9 x5 N" ^! L 创建地图9 _5 ?; C- B: M# y: I- q% R/ G
通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。
8 Q9 F& ?: A, `/ [* l( E3 w4 u: Q import folium
0 E% p& n5 R W% w. g/ L1 G& V/ q %matplotlib inline* b5 F6 H7 j* Z
3 @; M% e' \9 C8 q" V* V# O import webbrowser" i8 ]+ D6 \/ w& \, M! A0 W
* O8 t" v% b; y0 _
print(folium.__version__)
! Z2 M2 ~, N$ P0 K8 R, V/ ^7 l; }. [5 y+ F2 g
# define the world map* A7 `! H+ |& O3 x3 _: Q
world_map = folium.Map()
t/ o! J+ L/ d: s. m6 i # display world map; P, x- _" Q! }) B( ?
world_map0 u4 H2 l/ a2 ]8 ]! b- j
7 z, l$ u. |- {& z$ g A* E& H9 S
世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。 / R5 N" x9 T6 o0 n+ G I
在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
2 [* ?/ ~ A6 Q' H 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
6 O: P. r5 U7 J9 |* X- K m$ n 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。
8 L+ s3 D0 o) `$ |) N # latitude and longitude in Singapore city
; e* v9 Y7 x/ O) R( P coordinate_sentosa = [1.248946, 103.834306]
* Y3 E# }8 t% ~7 T1 l coordinate_orchard_road = [1.304247, 103.833264]
$ N: A$ k! \$ v+ h* b! ] coordinate_changi_airport = [1.357557, 103.98847]
# o) ]! V+ o+ p3 {# C, O coordinate_nus = [1.296202,103.776899]9 _2 n6 z/ Z: Z4 M' r3 v
coordinate_ntu = [1.34841, 103.682933] O; }2 \# o+ v, x/ d) s$ p7 {( O- D3 ~
coordinate_zoo = [1.403717, 103.793974]
: E" V- j Z# s coordinate_ang_mo_kio = [1.37008, 103.849523]6 _) @* w& e f' Q5 M8 k; L
coordinate_yi_shun = [1.429384, 103.835028]
# r E( i& h. p. j. g9 Y. _( f& H4 }: S
# icon
0 A; Z# ~- v& P; g5 g: E icon_cloud = "cloud"8 f2 O+ a, H: Q3 s( u( [( a
icon_sign = "info-sign"7 L* Z9 V4 a7 a
" v4 ^7 @# e7 R& z1 o- Y # define the city map/ |5 u3 V Y7 \
# tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}( i/ u( r. A) J) X2 h( e
city_map = folium.Map(
( v& k9 A1 l ?! q! w/ a location=coordinate_orchard_road,
Z: g3 T4 n ^) s) v) X4 J" A. K9 O zoom_start=11,! w- j! U- Q$ o
tiles=OpenStreetMap)0 C4 X* B+ H! L- K
4 V% K; n. c4 |' E% L # add marker in the city map
% I& ~) f: O7 R; S folium.Marker(
; G8 s. M d1 l p# m4 S' k& e coordinate_sentosa,
6 K* Z( ~0 U3 [: k4 O icon=folium.Icon(color=blue)6 C& i1 Z. f t8 {
).add_to(city_map)
. g5 ]; @/ Y% q, N) X' w! G7 x folium.Marker(& \, |" l3 z" Y; p4 e. n* u% D$ C
coordinate_orchard_road,
+ w$ L6 ]1 b' s& q icon=folium.Icon(color=green, icon=icon_cloud)
! q7 _0 ^( T; C3 h2 K6 R ).add_to(city_map)
% {4 a$ b+ \* T9 p8 P3 c/ m# p4 _/ H
# add popup5 ^& k1 _, [% y7 Q! i# S" c
folium.Marker(
6 J( T' o' a$ ^: q: r% L+ h% z coordinate_changi_airport,; r( ~/ q) s+ L( \4 }# o
popup=Changi Airport,
; p9 J' \$ G( W- O0 Q) v icon=folium.Icon(color=red, icon=icon_sign)8 H, F# R% p5 M7 V# i
).add_to(city_map)1 Q* m6 X; h; B6 M9 M
$ W% [- E# E! N( v9 q' p # add tooltips and popup5 C: [5 O" j% }! I# B' x- M& @
tooltip = "Click me!"
2 j# W4 g% z3 @: ~
' \. J) J" x7 D9 S folium.Marker(
4 Q$ x9 p+ \* A8 D coordinate_nus,
v# P9 s' s5 @; A# {' X3 D popup="<i>National University of Singapore</i>",
" V& y; x; J# ^( M5 f tooltip=tooltip$ N. y9 ?6 f; S: y- [
).add_to(city_map)
3 F2 R, z9 N5 ~6 @2 `6 P7 z7 A; X( ?2 o+ W
folium.Marker(! t0 u% a; m/ P: F8 L% Z0 l. _3 w: m
coordinate_ntu,! h. ?7 `5 b6 W, k& X# ]) [
popup="<b>Nanyang Technological University</b>",
2 @: X5 F* b* T9 `0 i tooltip=tooltip
8 q/ k% |! y6 {$ G! } ).add_to(city_map)
- l9 W2 B2 U" b; d* ]
- t* @" p5 F- c% S) M, j # display city map
+ }. f; A- ?, P+ e3 |' t1 h, Z. K E city_map 5 O* Z; v- o/ i6 k9 ^
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。 7 \# U$ I. @3 {# h0 F9 ^
# define the city map# V/ g5 O/ a e, M$ n# K6 y
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
( j, ?: Q( h% Y, i1 U0 F9 x; c0 y3 W. o- t
# 在地图中添加经纬度, add latitude and longitude in the map when click
' K$ t; k) u) ]1 t# r city_map.add_child(folium.LatLngPopup())
/ r- r+ z6 I+ K" I% ~: m0 f# c9 l+ }3 `: F, Z7 w5 [/ b% u
city_map
5 [* X# V8 \+ |( U6 b " q# Z, a, v0 c+ O) f% Y9 [5 B3 l# @$ ^
几何形状
) p9 K' u, Y2 m 在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。 7 a- l+ q- |1 j% a! F+ |
# define the city map- z0 P1 Q7 l5 N8 B, d4 a1 O
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
0 }! V+ `0 R1 j: K! p. |4 \! _( r( b2 f. ?+ H2 K
# 在地图中如何添加形状8 I0 Q0 d: p( B' m
# 多条边" A! I j! T. o h6 ~1 X( k7 K
points_1 = [9 D' W9 v* L2 P, g
coordinate_ntu,
( T9 s; `% |# S8 h coordinate_nus,0 M1 C$ R# z: e( t2 h, Q
coordinate_zoo
( ?0 T6 N( y) x ]
) P8 r! U L- \/ m! H; D5 i2 U( B: T8 j& f+ C4 h+ |
# 在 city_map 中添加多条边,第一种添加方式5 G. z, t+ U" o& a- Y1 S
city_map.add_child(folium.PolyLine(8 h/ X6 L, D8 p
locations=points_1, # 坐标列表
, `7 N" T1 e+ \7 O( d g weight=3, # 线条宽度
% o( B; D# Y ^& ~; |& v color=gray))& W; I% X3 `4 X* O& s
' R/ V: Y4 D- y- T1 L l6 f # 在 city_map 中添加多条边,第二种添加方式
5 F4 M8 L) w+ I. U7 p+ T folium.PolyLine(
$ f$ W& y0 P+ b; P+ f locations=points_1, # 坐标列表
1 x3 C' ~& r2 x& C# e. [3 p5 C7 I weight=3, # 线条宽度$ `4 `' ^# L( S+ r# q( j
color=gray).add_to(city_map)
( B5 a7 _8 T% v" [2 w% B# T
0 c( d- {* E [! \$ J, j# V( n" ^ # 多边形, v0 ~! z- U% J3 k7 f, @# u
points_2 = [
' r9 {2 _9 {) E2 S coordinate_orchard_road,
% f4 S# o$ I: r7 L2 M coordinate_sentosa,
- X7 k, [% y- u coordinate_changi_airport
* T5 w0 y5 V( t ]6 A/ q1 \5 G6 y5 f+ h* `
2 q4 f% |; Y& A% R' p7 g0 G city_map.add_child(folium.Polygon(
. v8 R! {, \& q P, K6 o: Q) F locations=points_2, # 坐标列表/ S& ]2 x% ]- b
weight=3, # 线条宽度
0 h/ a2 H8 P4 P' ~ color=yellow))8 Q5 w g, t. B
+ w* a5 _$ z& ], Q3 M. r # 矩形) A5 l. b+ F0 [$ w
bounds = [
2 h, I- x; O! p0 V6 i7 a) I1 N coordinate_ang_mo_kio,, b* P- y" A- G R6 `( w! }# `
coordinate_yi_shun; `- u' U; m2 D0 Y* x; u" r2 E
]
0 J5 R9 J, A5 p7 {: Q: N- v; @, n
$ ~* F0 o% A' Z+ s city_map.add_child(folium.Rectangle(7 H2 o; P& s$ \, x$ S
bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
2 h3 k$ o6 h; M# H weight=2, # 线条宽度
: T3 D: T5 ~7 T- O: J" F9 s color=blue))" q: G) Q7 m( G' p6 C, f5 \
, R2 L2 ]' f- x7 K) n P
# 圆形, circle, radius units meters; r8 s5 f1 Q( q+ o
folium.Circle() ~ f. m+ {$ Y# L! y! r
radius=1000,( h2 | e7 U- I# E# u X. _. v
location=coordinate_nus,/ b+ |8 T$ J; @ Q
popup="National University of Singapore",
# U0 u( K% I3 `+ ]. [ color="crimson",
7 F. s6 c6 g. P& H/ R9 w) q fill=False,5 t4 I, P- A: z2 l: O9 M
).add_to(city_map)
. q/ U3 C" S/ w, i R' ]; v% w$ L$ V5 W/ P
# 圆形, circle, radius units pixels6 d- X3 @& I, e. |# `
folium.CircleMarker(5 e/ O1 u: y |
location=coordinate_ntu, `* e5 F* H: X3 u
radius=30,
6 S ^5 H' h. w! a& E8 ]' u- e popup="Nanyang Technological University",
5 d, p# B& Q# d; j( ^! M* J5 Q color="#3186cc",# G. ?4 g, Q- v5 W/ S
fill=True,
1 [4 W+ T1 m, K, i fill_opacity=0.3, # 透明度
$ Q8 b$ m% x2 g; F& B fill_color="#3186cc",
8 I: z) U8 z7 n7 t ).add_to(city_map)
7 e' Z' {' c( t( E6 Z5 h. a) f$ |$ N9 C' i
city_map ( X y2 g/ z- z. j
Folium 中的画出各种形状热力图
* K O% x) S; y1 n) e7 v: a& R4 B2 F, { 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。 ! i/ K" ^7 H( u: w- d: D: K3 D
import numpy as np3 _! I8 q. e' u. c7 E/ a6 @
from folium.plugins import HeatMap# B1 F4 T) u2 w9 k
& q3 R6 A: w: N8 C6 j3 M
# define the city map
; R" v# j- C2 v0 n1 S7 F city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) o8 v/ [- f; e
1 k0 U! _9 }# v+ H% e/ `8 S
# 构建随机数据
, F. W9 a1 [+ h+ [7 } data = (
_5 F' r# Z& R& ?3 F- R. z# l; J6 S( | np.random.normal(
4 o |: O' y! A- E6 U; v size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +6 x+ r7 s* h( t/ @% K" ^
np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
1 ^0 K6 H5 J( J# `" T% q. o ).tolist()% _3 n' [) Y# @7 e# D' Q
+ J1 R/ `2 @( z4 e
city_map.add_child(HeatMap(data=data))* L( x- J9 m; C9 l/ S) S
city_map
* d2 b8 a# F8 `) d. y1 v
: d) `% O: t- c$ t+ s; Q! T 除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。 5 h4 R' [2 L4 g0 b
import numpy as np
) C) F' G; a; l. p2 H from folium.plugins import HeatMapWithTime' o7 w# z/ ^/ G8 Z! \5 t1 A
; g. g( H$ h; M # define the city map
6 A1 u3 T7 y# s% X city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
: w$ q j8 H; t' ]' O: S2 r9 c, t3 S
# 使用 numpy 建立初始数据: J2 m+ i1 G/ F. I2 N
initial_data = (np.random.normal(size=(200, 2)) *& i: p: A1 b. @ ?* r. W! p+ @
np.array([[0.02, 0.02]]) +
( c% n# T1 i$ }6 l9 b np.array([coordinate_orchard_road]))! h/ S5 x% q& R) |
% _& _1 T/ |% c
# 建立连续的数据
5 j- c2 n; `* B% O# A! |! V t1 @ data = [initial_data.tolist()]
; x' ^; S- d* x7 F3 b for i in range(20):' h# `" i# [. a9 m1 O
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())
1 Y4 ?+ s/ l# _/ w* g, h+ _2 V- ]7 I: |- b2 X* K8 H7 I. B
# 显示连续的热力图
' W3 b6 _7 I0 W* K+ g+ T city_map.add_child(HeatMapWithTime(data))
6 ~/ D+ q- b9 `) [4 { city_map
4 j9 Y- V% ~& u- k5 S" C- `& v" k2 c
* ~, G$ Q: b- E; k, G6 w2 F 经纬度点的聚类; w1 P# H* E" ?! L0 D' [
除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
; C" ]7 B/ }. r- s" J/ y from folium.plugins import MarkerCluster* k s7 A) H7 v! p) t! K
4 k# V# j Z1 j5 u* c7 F+ p
# define the city map
' a6 b* C+ u# f% \ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)0 a5 U( T2 K3 {, C O$ K1 i: k
% j. m4 U0 b5 z- a4 L9 N* Z
# 经纬度的聚类1 p9 R- l( i- g$ U1 k1 G
# 在 NUS 的经纬度附近随机生成 100 个点;* J# a7 J* O4 j' J C4 S
data = (9 @" s4 L0 |3 B
np.random.normal(size=(100, 2))
/ V1 k2 E! D9 W& { * np.array([[0.001, 0.001]]) +6 U, A- c5 \ }, O: L1 K
np.array([coordinate_nus]))- `1 n- ~3 q9 m
, U" o0 e% f1 _: J2 h6 \
# create a mark cluster object4 m% b% ]- D8 Q/ Y8 z: w
marker_cluster = MarkerCluster().add_to(city_map)
& g9 M4 q" I' F! o# W7 p$ p q& a% O* |- h3 A) k- D; ~8 l
# 将这些经纬度数据加入聚类 ~; U% b F1 ~, F5 ^- n2 k- b3 a
for element in data:
. z4 k1 t A8 g& f" I" K folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)# ]# Q3 ~3 q( ?5 A$ }$ O$ h4 ~
' v9 i( M. Y$ c$ j6 g0 d # add marker_cluster to map @5 K/ A: _ U s+ r: s0 ?
city_map.add_child(marker_cluster)
u- M& o- ~4 b; ^% d* f2 {0 v4 t1 L2 S, }; K
# 作图
. |" h. S$ Q b; t [( n city_map 2 b3 V" c0 \. S4 W3 k1 e9 M
# `' N+ H+ J; q! l5 p0 p$ C5 J 以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。
8 I! S' d4 C* d: E9 [. Q 参考文献' r; {/ w9 a$ S& v
Folium 官方文档:Folium - Folium 0.12.1 documentation
# @( |! k* b4 I6 j( r- j
/ M, @: v- q/ q
- m1 s7 S8 u5 ]- c; N9 ]* [+ `! K0 H# X3 I" D [# B0 \
, W/ {$ a8 [0 R2 K7 C |