/ V- c; z$ P* f* ?; K, g
Folium 简介* U: j3 u) O: Z5 B* o q' R+ @
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
* S a7 G' S M+ h1 w 创建地图
. Q8 \4 h! `8 m7 a5 [, i" r6 \5 r 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。 Z( H; v/ I5 j3 L. ]# a
import folium
6 X+ H8 E/ S9 h& U! \ %matplotlib inline
; G% P( c: w+ R: o- |8 E8 D. A {9 `
import webbrowser
4 n& X, o" b3 H6 _ l3 l' M5 D0 Z, [2 p- [& y. N+ [
print(folium.__version__)
4 n) m( ?1 N# G- \8 A* V, S8 t; Z+ n' W
# define the world map( W& n' ?! w8 J" m
world_map = folium.Map()/ B! S8 S. P1 X" C! ?/ t
# display world map
+ e( w' t$ S8 D6 l; z world_map+ H9 R' X" t0 p5 b! D. b
% h! | w3 V$ n' t. ~ 世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。 * Z- a2 Y# Q* v- \8 B
在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
[ S: _+ p _# P 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
" X) g+ R3 l$ A" H7 c 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。
: ~. u C! e! P; K # latitude and longitude in Singapore city' {4 B7 G, S1 @4 d
coordinate_sentosa = [1.248946, 103.834306]
9 z: w6 T9 {! ~) z9 K coordinate_orchard_road = [1.304247, 103.833264]" i1 F4 P1 Z3 G( d
coordinate_changi_airport = [1.357557, 103.98847]/ n" ?/ H0 a9 S
coordinate_nus = [1.296202,103.776899]
3 J. E b, I" j" z/ s& t' Z& \ coordinate_ntu = [1.34841, 103.682933]
* d8 l7 ~" K! `9 ] coordinate_zoo = [1.403717, 103.793974]2 ?, j, ^* F) Z
coordinate_ang_mo_kio = [1.37008, 103.849523]
4 r/ n: X: q1 }3 n! u% d0 W# e( r coordinate_yi_shun = [1.429384, 103.835028]
7 e7 n4 {; T' \ Y4 ]0 n5 i* i: f. S, R8 z
# icon+ b( T( P; D& E
icon_cloud = "cloud"6 P5 ] x* J+ Y- h3 x/ u) n
icon_sign = "info-sign"
* W4 I; @2 w5 B0 C$ }+ M/ @9 c0 L7 \% T; K
# define the city map6 e, E( ` L. e+ p r0 z* {# v
# tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}
' z: v8 L1 b% M' F city_map = folium.Map(
3 D: M# y. k& }* M4 q, ^ location=coordinate_orchard_road,
: ~+ L* q- d, G8 R3 {3 h2 h$ O zoom_start=11,3 U) S @( f2 q0 o) Y0 [- F
tiles=OpenStreetMap). B1 n4 P, @5 ~- Z# O
! y% a* {' e7 s3 I K1 B, J% P # add marker in the city map
# v. q; L7 K$ {$ w; Y! v folium.Marker(0 {: u: k. ~0 B' Y+ y7 N% l
coordinate_sentosa,
0 Y& M G5 U) G* C# X icon=folium.Icon(color=blue)
" _4 L* F8 R y* p" h' k7 y ).add_to(city_map)8 G! h& h2 ]6 w! G8 I
folium.Marker(2 Y" q$ A7 P- D1 B- E) F9 n! v1 ?
coordinate_orchard_road,
" Z7 H, M; ] x7 k8 B- w2 F7 M icon=folium.Icon(color=green, icon=icon_cloud)% x+ J5 {. E9 N' `' P" T" [3 T
).add_to(city_map)" H7 p' N; _/ h
: p2 r6 B! _& ?# k$ f1 ]/ L
# add popup
3 C$ V5 ]! Z' { folium.Marker(
, O5 ?* s: ^+ V2 P# H0 ^ coordinate_changi_airport,
9 I; q; l: I3 t- Z popup=Changi Airport,
h0 F7 Y9 }+ i4 C G3 f! C icon=folium.Icon(color=red, icon=icon_sign)! n' ?, U% f$ f6 n3 x, T4 {( D
).add_to(city_map)
9 x9 L; Q% f" q: n; a* U8 ?. t% L; O
# add tooltips and popup ^, q6 J; U. C7 C2 c8 C: q- N
tooltip = "Click me!"
& s- W1 F4 F5 h3 m8 H4 H
6 P( K X6 ?3 {1 h3 w folium.Marker(
a9 S& _. |3 }& B N8 j coordinate_nus,) }2 _2 x3 ~$ A% U) n
popup="<i>National University of Singapore</i>",
. {" J! u1 \" C# I tooltip=tooltip/ D2 t* A6 E! T
).add_to(city_map)
9 Q' ?! Z* R- g3 \ W- d. Y7 z) R8 q* Q3 z& P0 c
folium.Marker(
; N' z8 a. I4 ?7 K coordinate_ntu,% p d3 [/ e6 {* ~' v
popup="<b>Nanyang Technological University</b>",
& N+ h$ A' z- H1 d, ^# Y: M tooltip=tooltip" j7 ?% N0 \" N
).add_to(city_map)
" N2 Z8 @2 X1 c, d0 r- E5 U2 [# L8 f
# display city map( r, M! m, Q9 B' i
city_map
" o4 }9 c/ Z, }4 o1 z Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
" M( N. {9 f. K' ]- X( U( g! E # define the city map) v1 }2 S: k/ N+ m1 m2 H" s. Y1 O
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
8 g/ \1 I! O7 @" ]8 U. v! B" e6 e$ n* a- E5 [" x
# 在地图中添加经纬度, add latitude and longitude in the map when click- x8 M6 |0 a8 W% R- n
city_map.add_child(folium.LatLngPopup())9 f, O5 W9 j% ?: h6 }0 f7 P
# T* b; a: ^( L- c city_map ; ]6 u6 E, G2 x0 V$ u- y
' {7 Z% S1 J! `4 s5 V k6 V
几何形状+ @7 x& ]4 I2 _. ?
在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。 , B# N0 p' o z; o7 u) J9 ]
# define the city map
# e# e3 N4 @1 V4 G- _2 ?( e city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)9 K% D4 g* F+ f3 S
& m' ?) J* k" R3 ]
# 在地图中如何添加形状
8 \. f) B' a6 v; D# ? # 多条边9 o+ |( h4 P' T' G" C
points_1 = [; a! W9 h8 L2 t+ t6 g
coordinate_ntu,
3 o ^0 d3 Z4 w7 A% @' Z8 N coordinate_nus,/ g/ [! S1 \" _, `2 F
coordinate_zoo$ ~! w6 b" q: H* q
]$ a# g( R) {( B
. V* C {/ g3 p # 在 city_map 中添加多条边,第一种添加方式, @7 X% R, }2 L7 Q8 ~3 j8 h
city_map.add_child(folium.PolyLine(: |' r) W8 ^" d3 E" `& }5 d
locations=points_1, # 坐标列表
+ y* i- b9 {, z; H3 E; {+ M weight=3, # 线条宽度
5 z- R: @! d4 B. A3 _; x0 t G color=gray))
* K7 L, w; t \7 g; |$ J
* p% O1 D8 ^6 \ # 在 city_map 中添加多条边,第二种添加方式
5 U. f9 O# D. U* |# F: y folium.PolyLine(
8 W+ r/ C( E1 i5 u+ _- w' O locations=points_1, # 坐标列表
$ c5 V& s4 J6 z( ^ weight=3, # 线条宽度
7 p- l- d U5 s5 s3 J color=gray).add_to(city_map)8 ?! u" y# c% L4 O) a! W1 r* K
/ _8 {. M1 e0 a- u+ e$ U0 A5 } # 多边形* [1 u# Q/ t$ }2 `) m3 A4 A) [
points_2 = [
" R) y( T/ z7 A6 E8 d c9 j coordinate_orchard_road,
U3 N* t. G+ H& a coordinate_sentosa,
/ [" J; j' {, w$ S$ I$ ` S coordinate_changi_airport) r7 S( E% |1 b5 A
]# u4 Z8 |$ D7 M5 y1 \. h* r
1 D; {3 `' R, P9 r
city_map.add_child(folium.Polygon(: j8 z# F3 C. e0 ?% O& |
locations=points_2, # 坐标列表
6 S# k! ~ D8 k8 l0 b+ p weight=3, # 线条宽度
- |/ M5 A% S8 l- N) x( |$ G color=yellow))8 {& z3 Y ~. j! Z$ @
6 c, @: @0 J* M% F. J
# 矩形
! L6 i2 j5 l- m6 l/ u+ ] bounds = [
3 O) k6 o9 C. N' f- B coordinate_ang_mo_kio,
% Q! n! _, u% F coordinate_yi_shun3 h+ P* P1 P% u$ f/ {) ^* E
]
9 b/ p, T; _2 y- L" C8 |2 P- ^4 c& ~ c9 ]! F
city_map.add_child(folium.Rectangle(
9 {8 N" B3 n, a2 A' N1 I bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)( u8 B( G/ a P9 z+ H ], t/ W
weight=2, # 线条宽度
$ S- k$ ^3 c7 u3 F, y: n color=blue)). G6 b& e" F- g& W2 m$ C
" F) n& p. l; Q
# 圆形, circle, radius units meters
$ s7 W; P, R1 V& t% n6 V folium.Circle(
8 ]" ]+ A9 M& m v radius=1000,
' o: q. \4 P( f+ V+ M) p- R) E9 ~" F3 d location=coordinate_nus,
' n( T/ S2 s7 Z2 X popup="National University of Singapore",
) D: c3 r4 I. V/ K% |4 p color="crimson",) l. H4 D+ ^# y6 `8 j M
fill=False,
3 C( {- I: V; S5 C7 b# J( {/ n b. g ).add_to(city_map)
0 P c; j: y2 _7 |
; Z4 t# X6 m( q# ]# @# o # 圆形, circle, radius units pixels$ _7 _9 |6 d# N! ]( q( E% m
folium.CircleMarker(
, W g: Q! Z3 ` location=coordinate_ntu,4 R& m1 z( |" E
radius=30,- |4 b' `/ {( y8 g( }. Y
popup="Nanyang Technological University",+ Y; h" R, _8 {8 _0 @5 |/ U( N
color="#3186cc",
2 L$ K/ G, [- w, Z fill=True,+ ]9 H, Z/ K' Q. m& F% L9 O2 v# g
fill_opacity=0.3, # 透明度
; t; d6 q7 q* ]& ~ fill_color="#3186cc",
7 z5 y3 R! U" D3 c7 l* J s& _" K' h ).add_to(city_map)3 w9 @/ L1 p# l
- B* J3 d1 D2 V city_map
% {; q# {6 w7 J9 Q Folium 中的画出各种形状热力图, A, D/ m. w9 n, Q" O& R
在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。
, y# ^' l: g7 }9 s5 v; D+ M import numpy as np
% s7 e3 T0 b$ W4 u; h% u# ~) w* f from folium.plugins import HeatMap
9 G$ G! ^5 W4 l( I& p$ Q! v2 A7 m' c z( f+ G; Y
# define the city map
' R; c2 H' G/ a9 ~4 t; h city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)8 H5 v, P: D4 g) y
; |0 D7 E6 y) x9 ?. X! B/ M# V! E3 h2 H # 构建随机数据: z7 f: V3 K1 {( |: ~
data = (2 D0 a }# b$ a& h3 g
np.random.normal(
7 v+ [7 }. U; q: c6 ~ size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +- `' @+ V6 a$ w0 L- ?# ^
np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
j: |8 e4 O# S/ O# B% } ).tolist()5 s" s: E! N$ n, @! E0 t
" N' G* h8 i% i1 ?7 \# C! @( k) u
city_map.add_child(HeatMap(data=data))+ j% j/ F( v1 |- U' G' n
city_map , o# p8 H1 \3 s. C3 w
8 L1 v3 ?9 N! Q; t, R 除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。 * X3 K, y- t5 _7 _8 i, Z) k
import numpy as np8 b. X8 ^* L9 ^1 v6 ]2 o
from folium.plugins import HeatMapWithTime
$ r# ]4 l, h" R
( s* v& }' Y5 Z9 M, M* a # define the city map9 t+ @% z& i5 v: A0 U
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
A1 Z& s0 x) s. _# [( T5 {, K- o1 Y7 E
# 使用 numpy 建立初始数据
: v: ?3 T5 T7 j5 G initial_data = (np.random.normal(size=(200, 2)) *
3 o$ @( r1 h8 F, ?; u, `6 F# X np.array([[0.02, 0.02]]) +
& u7 b) H( ?$ Q9 w0 _ np.array([coordinate_orchard_road]))2 C; Y& N5 G9 N% Y f5 [
; c0 \; t$ n* g4 Z2 x4 ~* @* g
# 建立连续的数据( Y6 l! O, J, \; p' e
data = [initial_data.tolist()]
; F! S1 W# d; H8 d& {, a for i in range(20):- l1 l) y+ ~: C0 L
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())& J" t$ ~6 B' z \0 z
+ @7 D+ A0 o$ H# E) ?# A # 显示连续的热力图2 I" p* S) S8 O' a
city_map.add_child(HeatMapWithTime(data))7 z W9 o4 G" G: J& c4 Y
city_map
+ M6 j3 ^2 J" c9 y) N, L
' t! n$ O$ a# m5 I) H+ j$ _# | 经纬度点的聚类 e$ ?/ r \/ D! l8 V* H: e
除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
8 Q$ S& E% S u P3 P from folium.plugins import MarkerCluster
9 D+ I! l( R; s2 v
# o" ]. w4 s5 g7 V$ R0 }# _ # define the city map
j! e1 ~/ T. k0 y city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)+ n. ^" H% j9 p% t
: v4 M- R$ e! ^ # 经纬度的聚类+ t" k# ^- ?6 ?
# 在 NUS 的经纬度附近随机生成 100 个点;) [( P+ ^5 l. G) R$ K; m
data = (" N- L" `$ Q* R) P
np.random.normal(size=(100, 2)), G# j. q8 T* F% g- D! @9 F$ ~
* np.array([[0.001, 0.001]]) +3 b: P, m3 ^) x
np.array([coordinate_nus]))
{7 c6 _8 q) e( O2 f8 c+ U1 w( I# w' f6 `2 N3 {' H
# create a mark cluster object
% j, X; m9 R$ Y5 ? marker_cluster = MarkerCluster().add_to(city_map)
' g5 S4 A& r' R) Y) S! Q! c5 E \1 L& ^/ h) L/ o, T7 f
# 将这些经纬度数据加入聚类4 X$ ~' k2 e5 l3 C0 r" b( ~
for element in data:
, E" n: l$ j8 N folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)
# J! j/ n3 Y- t' w, E- B
& c' L! ]0 u8 s4 @8 i: B6 F$ l # add marker_cluster to map# y4 o+ v3 M, m& D
city_map.add_child(marker_cluster)4 X, ?, v$ m5 N* u- M7 q0 {" ]4 _
" s/ Q( U T( W1 \" z, s8 F # 作图
% \# Y: S+ g# K5 ? city_map % v! |; ]! r; S" x# j3 u8 x$ y
3 K7 R3 @0 q1 X# Q+ |# M+ r 以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。
% g- {8 ]2 E: s 参考文献
4 x7 _/ y0 A. Z1 ]/ q Folium 官方文档:Folium - Folium 0.12.1 documentation 6 ~ _) l0 \2 P1 r6 E9 e
7 j) y/ c6 p2 Q0 |
) O3 S( A0 _0 \+ e$ ^( O, C
/ k$ c5 q( v) v6 F7 C: o- V A3 Y4 ]6 H; W4 W$ E; J; G1 {% a5 v
|