! o. s+ {! l1 I7 H q% Q1 u7 @+ o
Folium 简介0 R; I; u$ \' v2 h6 Q$ D5 p. H) L
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
; \6 v( R" l: b% x3 C 创建地图
4 K, b* y X8 z8 }/ o1 N 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。 & j, V' C( r/ @+ Q4 |" p. {
import folium
& n2 y5 k' z2 X1 ] %matplotlib inline
* z, p- n4 |( d4 c+ W3 z
7 H, L: t5 W$ G( P1 k/ v import webbrowser
v/ X$ k, t) v+ h# T- c# i
( e0 y& D# v' x7 I9 f print(folium.__version__)
# h# o$ @2 o. T, A2 _" ?9 t, F: P9 T* C) m4 j+ C
# define the world map
. r+ V2 F3 k/ o; d0 p/ z world_map = folium.Map()$ p% F! z& T4 O
# display world map
# v* Z; r# Y4 t1 H* x world_map
, h. N/ I9 u* |+ D! O! H8 S
: ]7 i! r) x! |% \$ G2 t6 z& [ 世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。
9 ?& p! ?$ X# G# S3 Q/ g& u 在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。 ! |5 q4 x2 G6 d7 d- v
有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。 * {1 I6 `1 i5 S( ~
另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。
& r! C* R. @6 [8 X0 b3 ^2 {6 y # latitude and longitude in Singapore city2 m+ c9 f$ a7 @# r( O
coordinate_sentosa = [1.248946, 103.834306]6 I8 p8 r* Y* Y2 p0 W& b
coordinate_orchard_road = [1.304247, 103.833264]
! N3 t' y$ g+ |8 ]% h coordinate_changi_airport = [1.357557, 103.98847]9 r3 [7 t. U1 I" J1 v
coordinate_nus = [1.296202,103.776899]4 _( X y1 L4 O
coordinate_ntu = [1.34841, 103.682933]% a0 G. R; q2 l1 j, R3 z' h2 \
coordinate_zoo = [1.403717, 103.793974]) Y; [& N: M1 V$ r7 _5 J: A0 o
coordinate_ang_mo_kio = [1.37008, 103.849523]
+ ?. [' }) A' |9 h3 f' Y# q8 e$ e coordinate_yi_shun = [1.429384, 103.835028]; j- M& ^- `; I2 }5 L) W. }* P
2 A( I1 F+ n& O7 e/ h # icon5 B8 M7 i/ z+ _/ I! T+ @ n
icon_cloud = "cloud"% z! D+ x' W' U7 K& m# k
icon_sign = "info-sign"5 y1 m- y+ H2 s" N- C7 E/ N
- }0 L' Q) K$ k* k8 D& ~ # define the city map
. d3 P1 A/ y0 @* p; s& B9 c' k # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}# _" ]6 Q. ^2 A2 ]9 u
city_map = folium.Map(
1 H7 U% @- {! s' i location=coordinate_orchard_road,
5 i! w4 Z6 P8 O1 c+ S; x! Y+ A zoom_start=11,
/ t9 k9 I$ \7 J. r- [0 ?4 R0 H) S tiles=OpenStreetMap)4 A& ^0 }6 d3 N3 g+ X) w
6 w: p0 v* I3 R; i- X # add marker in the city map
5 m! ]! l* H( I- e D, _3 v folium.Marker(7 [: l5 b! X) w' e4 V/ j
coordinate_sentosa,
+ g! V. I# g3 f8 g% p5 q( \ icon=folium.Icon(color=blue)5 e! c' n) o0 K* E& l2 R# m0 O
).add_to(city_map)
/ U: ?- s0 f+ \ folium.Marker(, N* i1 v; L8 b
coordinate_orchard_road,9 ]- Q8 p0 `% F0 W% Q( \9 c
icon=folium.Icon(color=green, icon=icon_cloud)1 u& ]$ y% K0 X3 a! t$ y
).add_to(city_map)
& d- a( B) Z' A8 f! d2 S3 x2 x0 u$ [+ D8 a: B2 |
# add popup0 t) \- d: K% x; y% M2 b0 ?9 U
folium.Marker(
2 g$ i3 v0 {+ }. ` coordinate_changi_airport,5 A) M; K8 |1 A- A1 {
popup=Changi Airport,
: E7 {4 a" i* Z8 Y+ {2 Z; R2 ? icon=folium.Icon(color=red, icon=icon_sign)
5 H' ]/ j" A7 V! F& t ).add_to(city_map)" p" b }" Q" H N$ e% a
7 M1 K; s2 X3 Z: C- S. O, Z( g6 S
# add tooltips and popup* f! f9 P$ l& S9 h5 e* `
tooltip = "Click me!"5 h% M, k" P4 }! o) m
! R+ s+ I! d7 H folium.Marker(
4 @6 u! g k& `: t7 I2 b coordinate_nus,4 D0 z; H: @2 a6 {+ M
popup="<i>National University of Singapore</i>",
& C) x: v5 x3 ~$ u1 }6 F! V tooltip=tooltip
3 C+ L0 D$ @ R) T* { ).add_to(city_map)& A$ _0 ?9 o0 h6 Y5 N. ?/ w
$ S6 T0 T; ~) O, ] folium.Marker(
% n. |! ^; F7 C2 F coordinate_ntu,7 ]7 z* k% Z9 J! s! J
popup="<b>Nanyang Technological University</b>",; U+ U, T. k) h1 y
tooltip=tooltip$ L" R7 O: d- S9 U( D4 @; K6 O
).add_to(city_map)
- C% n' l1 |- }5 W5 G6 ^' Y
* F N! s) g4 Y: y3 i# z # display city map
* a5 h: {$ Z) a; e9 R/ A city_map , i+ T5 s# v8 C
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
% w0 d( }2 b- a' L1 U # define the city map
3 L4 F7 J" Y H6 k6 F city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
( f# @# g) Z2 J3 F7 ^" l0 ~
! q- o; x+ ]1 Y! g3 z # 在地图中添加经纬度, add latitude and longitude in the map when click
9 p+ q& L0 |& i5 e) L8 w city_map.add_child(folium.LatLngPopup())
l# U- X: S9 ]/ L {# d. |$ q* a" F1 M4 n
city_map 9 ?. i/ w+ n7 A2 B5 B4 K% o# W
) w9 O5 H! F/ {; [( \
几何形状9 m2 p P7 b1 J) [. o- b
在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。 ' d1 S5 a& [/ F" o3 p1 `- O
# define the city map9 z1 p6 j I% h- C/ O# M
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
9 q" h0 x$ m3 d# f0 U3 f; {' a; a( @4 G# E" d
# 在地图中如何添加形状; M* }: O8 D3 @! J9 q' ~5 t) W
# 多条边, ?4 Y, t( G3 p# _2 Y/ H" ~
points_1 = [
* D, Y: U2 M3 P- E4 k6 O# \, B coordinate_ntu,' a, i; J& _. O" H! R
coordinate_nus,1 G3 i, c- H |
coordinate_zoo/ D2 @0 g! E1 h! p) S! d; T
]
/ O* W& G/ N. q7 o
: W, C+ d2 Y, P/ A) e& c # 在 city_map 中添加多条边,第一种添加方式
[. g4 O ~5 r9 e city_map.add_child(folium.PolyLine(
0 ^/ C. w$ `0 {: M" R0 D locations=points_1, # 坐标列表
7 V4 F5 l: c" W+ Y, I weight=3, # 线条宽度
5 G$ L+ t: `: T9 u* @0 ~$ h! O color=gray))
5 [" E c8 v* m0 V: W0 j' A; m8 L0 B$ p0 I' Y1 d7 |# ?
# 在 city_map 中添加多条边,第二种添加方式
9 o) ]. b; P/ ?1 P! G" r7 p; N) b folium.PolyLine(: P5 Z$ v1 U4 T( _4 h, @
locations=points_1, # 坐标列表
6 W; i/ G# I7 T7 | weight=3, # 线条宽度
/ B- V- W0 ~- C3 [5 Q( T& E color=gray).add_to(city_map)$ R. C) U( c7 h/ [
8 H! e; F# k9 p8 @: b+ H
# 多边形
0 {; x! Q4 J! ?, N3 P points_2 = [ `: ~' d' A% V6 Z8 f
coordinate_orchard_road,
: k" B0 w0 I2 q/ g) x coordinate_sentosa,
" ~6 X+ i9 v9 }0 X) V9 S7 R f coordinate_changi_airport: E2 x! N7 _* }
]4 U- Y4 X0 ~. e; G* W
; Y4 j1 J* V1 p3 @ city_map.add_child(folium.Polygon(; |7 e) c; I* Z7 `0 O' ]* p4 E
locations=points_2, # 坐标列表
2 C% U( I9 L1 }7 p8 J1 T1 [' h weight=3, # 线条宽度
* @5 e, G4 N7 ~' K color=yellow))
$ Q' S8 e9 [, Z4 q5 Y
+ N& D& N6 t6 @3 C # 矩形. e+ r5 ~! D; h7 G+ K
bounds = [
6 ?9 f# w+ I& v# G7 [* q, k coordinate_ang_mo_kio,) y) \9 L" ?, ~3 U; R
coordinate_yi_shun
G4 ~7 Q5 ?% ^+ C# Z+ O m ]/ x% k0 g3 j- E
0 @8 r& W! S7 k; F0 f. `. E city_map.add_child(folium.Rectangle(
7 v7 `& s) h, K+ S2 q bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
1 w$ P& F' {) z& Z weight=2, # 线条宽度
0 l, U4 K8 q9 Y& x9 Z! W color=blue))0 `/ ? k- X: K7 C F; V& \
* Q2 j) \( d; I9 e, t: C
# 圆形, circle, radius units meters% m, w4 W+ v1 J& d) c% R2 r' m
folium.Circle(
- x: g$ O, u3 k( J- }, ? radius=1000,: t/ M7 e8 H/ d- i. [
location=coordinate_nus,1 Z% y$ t! N2 P" v# |
popup="National University of Singapore",! }, L! b7 O' g2 p: c0 [
color="crimson",8 [/ O: G# Y% c, Y* q4 i
fill=False,
8 V9 e, i) p, C/ Y1 } ).add_to(city_map)
1 z3 B6 W0 H0 v( o" Y C f- m, `6 H
# 圆形, circle, radius units pixels
/ D. k% b S1 Q folium.CircleMarker(
6 |" _8 P( h4 k: Q& ?* L location=coordinate_ntu,1 y. H7 m9 A9 `# s+ Z/ v! @
radius=30,
; ~! q5 y5 K6 m2 l9 f& g8 K2 p popup="Nanyang Technological University",$ B% u! i: m8 w
color="#3186cc",8 K, {9 S* u7 }! K7 O
fill=True,: ~8 I. ]; s% P/ L
fill_opacity=0.3, # 透明度
$ }0 n2 M8 N ~, h5 i1 b fill_color="#3186cc", l/ Y0 K! f! W! U( [; y, s/ k
).add_to(city_map)- G, ~/ z$ M3 }5 z' a, D; q
+ ]' K j; Q) ?& L% c city_map }1 E1 c( i T! {6 c/ X4 r
Folium 中的画出各种形状热力图: ?7 K4 G- S3 x7 Q: F
在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。 1 S3 y& I; M# y7 N/ _) C
import numpy as np
9 z" K2 |0 w5 n& |7 ]: P+ r from folium.plugins import HeatMap
6 S, I* E6 R2 C. y7 B- _9 f' A- o) a \8 b& z
# define the city map
. V7 w) g E' [% s' m city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
6 M8 d& Y9 d2 [# C- y. Y; N+ A% a3 G4 Q. N
# 构建随机数据' v6 t6 [6 d1 K
data = (
* r& ]7 [5 q( e: } np.random.normal(/ E: K, i P, P' A+ S# {! N' v) d$ ]% ~! B; y
size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
A( I0 S' B" h4 @2 q np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
5 H* I. ~$ s' G7 K B ).tolist()
: S8 _* z" M3 R J* I* z; K( ]& x3 I/ ~' g7 P
city_map.add_child(HeatMap(data=data))3 J) Z( B% s- C
city_map 4 n. N( ` J2 g" p3 K! X
$ D& ^) |- L; v& m: z- X H" J. d
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。 4 C2 {( h3 k( }* t6 z
import numpy as np1 z* s. H" c' i- l ^! t
from folium.plugins import HeatMapWithTime
/ F2 Y2 E: C! w; v( q8 b& V5 B$ [, @( {8 C. t
# define the city map) j' X7 R: q4 m4 M+ n
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)! D$ q7 n& X$ o6 t; d
" R) x7 s* T% Y" K* m& V! i2 B9 F* F
# 使用 numpy 建立初始数据) c0 c9 k7 O2 c, R5 b3 z
initial_data = (np.random.normal(size=(200, 2)) *
4 L( `/ x3 Q4 M np.array([[0.02, 0.02]]) +
/ e2 L& r4 A7 Z np.array([coordinate_orchard_road]))! o+ F5 E7 t4 L
& {8 E$ d: p$ ~+ v: t # 建立连续的数据
F1 Q8 @# `. N1 D6 c) m; P data = [initial_data.tolist()]0 ^" A% e9 \$ |6 A4 F. k
for i in range(20):. `4 E0 ~, y- g7 @
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())
; L0 |( @) i u* V* b6 i3 E1 C5 O8 [
# 显示连续的热力图) k/ z2 I3 [/ r7 m* ?
city_map.add_child(HeatMapWithTime(data))
0 Q" x' L1 [( t city_map
1 Y: ^; x x Y) J- C2 t4 V
0 V6 E, H+ M# S# J* e5 t 经纬度点的聚类( a% ^2 r( H! G/ Q/ I
除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
, R0 v+ H: H. l. `2 \ from folium.plugins import MarkerCluster
' v4 S t7 w* G% p3 t9 \! {' a
: { Z* S" f5 K0 v5 }; x # define the city map/ p; o/ }5 |0 M7 n& t" v
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
- t# o3 I5 _) F. |, e3 R9 X( q5 c
# 经纬度的聚类, x, D; ^, M" }; {3 c x# x. f0 y
# 在 NUS 的经纬度附近随机生成 100 个点;3 q6 O- g, Y4 v5 k4 F; A
data = (4 d$ F5 i% n+ d' Z$ L+ k4 g
np.random.normal(size=(100, 2))
6 y+ n% n6 ?2 J2 s * np.array([[0.001, 0.001]]) +
0 f, e r: ^" _. r, }: ` np.array([coordinate_nus]))3 I9 z5 g9 e+ x0 d. [2 n
& i" i1 v, E. X6 N
# create a mark cluster object5 L( m$ j" Q& M n+ j7 |/ M! o9 J
marker_cluster = MarkerCluster().add_to(city_map)' }& B8 } a" I. V+ V8 X1 O
2 E8 {4 q" h: c+ R& G. T
# 将这些经纬度数据加入聚类
9 U' ^" ?; K: [ U4 h for element in data:
- Z+ a* S4 |0 [ folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) S5 A2 a( T! I A$ W( d
( q2 x4 C! m5 f6 W0 z7 Z # add marker_cluster to map. V& o! N" _$ ~" n6 Q3 J" a
city_map.add_child(marker_cluster)
/ R8 @; J) q; P- J5 d$ Q2 `* w4 n$ z; [
3 A9 ]( S* M* c) K& X- W& j5 x r5 z$ y # 作图4 D' ^% E& ~. K3 f# R
city_map * t. c2 ^+ k6 Q" G7 k% r
$ I5 O% v5 o6 T 以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。 ; w0 v: f% `7 U/ x% d
参考文献
& U" {/ G* l3 S- z; b' c4 @ Folium 官方文档:Folium - Folium 0.12.1 documentation 2 M& m# m! }5 M& T) u7 k3 O: e) w4 T' D
8 X% m- O6 k" F- p9 `
- Z2 g2 g& a, I1 ?6 s( y
3 X# g/ k9 ?2 k7 h( |# R: s7 s% h- c, g. W# \* @8 A9 F% c2 O: V
|