) M3 R. m1 m! S5 U
Folium 简介7 C! D. x9 L. Z5 ?% c% J& Y: A
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。 - z1 c% Z# @* u9 h9 W
创建地图
# R' P7 v2 \4 f4 b 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。 . }( h& x: G5 L( X
import folium
$ g+ U0 {& J* B %matplotlib inline
: Q) S( W$ F3 q' U! @+ T% ?6 k9 k: a& {
import webbrowser/ b5 z& H2 `* A+ G1 [& ~
9 c, ]" y) t: Y5 c) u# w; x! C/ x* P print(folium.__version__)( A9 W# U9 R$ [5 }- N/ K$ M
" {8 C, p6 X6 ?: `7 k1 h: N/ @ # define the world map& S/ c' }/ J. y8 b
world_map = folium.Map()
4 b3 F5 p& z* R; e! [; c0 f) ]2 G # display world map
9 h3 |- s2 I/ D world_map' |/ E0 V3 S% ]
5 a9 Z/ R7 ^! k7 p# P1 x 世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。 9 s2 m: n- f' L& H+ J5 S& U3 [4 e
在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
1 H1 N* Y" I7 V: b6 W5 v: U 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。 [$ ]0 k8 C( j- F/ _( b
另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。
8 D, Q3 \' U( P$ w. F" _3 Q # latitude and longitude in Singapore city* R! _2 o1 h3 {# H9 }0 `1 H
coordinate_sentosa = [1.248946, 103.834306]
- m* F) w0 i' }* M _4 K" L' Q coordinate_orchard_road = [1.304247, 103.833264]5 b& b. r# s+ y# t$ K2 l/ g8 S# e
coordinate_changi_airport = [1.357557, 103.98847]& [' J: |* N, Y3 U3 K7 I# @
coordinate_nus = [1.296202,103.776899]
& L& S7 X4 p0 X+ N( N$ C coordinate_ntu = [1.34841, 103.682933]4 T& T5 r, V" g# f9 I1 b
coordinate_zoo = [1.403717, 103.793974]
- C l* o. c: o! N coordinate_ang_mo_kio = [1.37008, 103.849523]8 B0 g! R$ X# I# E& s8 u% Z- ^
coordinate_yi_shun = [1.429384, 103.835028]3 y8 @4 B: ]0 f& d5 R, q( J# K
2 M) v+ m5 t" Y W& U1 S4 h" d
# icon l) B. U% A5 b/ s4 s
icon_cloud = "cloud"; A V4 d8 |3 ~3 M
icon_sign = "info-sign". d Y. G1 C2 i; U/ |; s
H9 j; u" m* E% u; E$ a9 x
# define the city map# {# t' a$ J0 I5 T: x0 M, l
# tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}
. z2 P/ X& U1 R city_map = folium.Map(
- j9 t; e# M) o; b- D6 X ~ location=coordinate_orchard_road,
" c) p! _1 r! p6 a& K! U zoom_start=11,) [ E5 b& ]# m. q$ T5 r! S# o# E
tiles=OpenStreetMap)
& F- Q& C% s* H0 S/ {
0 R" n1 x, Q! c* V u( j: Z # add marker in the city map# V9 t' E; B/ X$ g7 ~6 [% n
folium.Marker(. ] t. C V5 e1 S, k1 F$ t
coordinate_sentosa,
( ?! f% J2 F) _; f# B icon=folium.Icon(color=blue)4 m3 i( [- C0 p
).add_to(city_map)) o8 h0 j/ F6 V2 s9 E" v
folium.Marker(
5 k! U- E! @9 L- g coordinate_orchard_road,
$ I$ ~, P# `$ P/ }# {+ y; J icon=folium.Icon(color=green, icon=icon_cloud)) t6 `* R _# ?. X6 A2 l
).add_to(city_map)
- |0 b2 E6 c& L- h/ n. F! ~6 s$ L0 c% m+ |4 P7 S
# add popup2 a3 \* d+ I. O; [% t2 `/ e4 j
folium.Marker() l* o9 A- {# e T$ e
coordinate_changi_airport, |3 z6 `0 ?- A8 c* Q# h
popup=Changi Airport,. ?5 h# {% t: I
icon=folium.Icon(color=red, icon=icon_sign)& x) z) F+ t) D# f( k5 {. F
).add_to(city_map)7 J& Q8 Q. S4 c P& p8 a* N
' g1 n4 l& l% p2 @/ n1 B, d # add tooltips and popup V+ D* b+ ? o: u
tooltip = "Click me!", G: I1 z; x% O% U3 y& w' q% k
; Y, f# U$ [; l: t4 R folium.Marker(
" F2 s$ @. S! s/ i- \ coordinate_nus,
& Q5 E: U% G, m* @$ F popup="<i>National University of Singapore</i>",, N1 r) H# f% z7 R. s. D
tooltip=tooltip1 M7 [- a0 y' b
).add_to(city_map)* i; D# X u& D* }
: j+ y# Q/ Q4 Y! ~1 c
folium.Marker(# P, I# F0 ^. U1 Q
coordinate_ntu,3 W- r9 }. {+ u
popup="<b>Nanyang Technological University</b>",& I0 j1 J8 E# C& k ?
tooltip=tooltip
$ s1 @ W$ c) t# k; I ).add_to(city_map)
" n; s5 O8 M) K
/ k4 q+ [4 X+ E* j: n1 J5 C # display city map
3 ?; A, j# }/ b# G9 D$ x city_map * D5 m/ e4 j2 S; G5 y( t4 J
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。 + b- q2 o- @$ ~: d- M
# define the city map$ C; o O' `) J; _9 v Z
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
, b& q7 q* G. j! {! a, n, N+ p& s. r2 v( G! _6 T6 H( l/ s) X
# 在地图中添加经纬度, add latitude and longitude in the map when click/ M( E k% A7 u9 y. u
city_map.add_child(folium.LatLngPopup())
- e+ V- f2 B, ?+ o- A$ X
7 n, K& \- D3 H city_map
0 E! O4 H' \* S' ]) |& K/ d 5 U# ]% L) c2 Q- n
几何形状. e$ Q. S4 k6 \5 s1 H
在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。
: K; K. q' ^2 X" Q3 w6 L* o # define the city map
1 l" l( Z: {9 U( f' T1 Z9 ]$ ^ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
9 N! K& |' \1 R% R3 b) P8 N" l+ r8 T. m, Q
# 在地图中如何添加形状6 y6 u; V8 j( _" j: S3 b& D
# 多条边3 W$ x$ b5 j2 A" O4 @* B
points_1 = [6 f" y! E+ M2 H6 N
coordinate_ntu,
- o# q% s( z" _2 B coordinate_nus," y Y2 Z" u$ {4 P
coordinate_zoo
; p2 @! d9 Y; F) G4 | ]) u {4 X6 W! h7 r& |! ~9 n
9 K# J& G7 E2 c( W# ~+ K, f # 在 city_map 中添加多条边,第一种添加方式1 Y M. a: W5 t5 ^2 O
city_map.add_child(folium.PolyLine( ]1 `0 ?+ L6 h/ ~2 J p
locations=points_1, # 坐标列表
- F. F4 N7 ]* a4 @ weight=3, # 线条宽度
8 Z) K% c2 c4 E1 j5 p6 n5 I color=gray))$ n) }- S; t2 Z4 Q! R
3 @3 A% V# u/ s& p& u' I9 g
# 在 city_map 中添加多条边,第二种添加方式; P# J9 M2 K( q7 i( J9 j
folium.PolyLine(4 g+ U0 t# m# n) G
locations=points_1, # 坐标列表
2 F4 {3 y3 b2 n5 C' ^ weight=3, # 线条宽度
c2 q3 O* v# j& Z8 H6 q color=gray).add_to(city_map)
# A2 V/ J# [1 @5 ~2 w3 N4 p8 w
# H( m5 s" y4 b0 ^4 H+ ?: d7 A # 多边形; t* p! C1 x/ D
points_2 = [. e9 J' l. y8 i( m- v. y
coordinate_orchard_road,6 W% r0 h( x& {5 _4 S4 a
coordinate_sentosa, |* N& y$ p: G* Q
coordinate_changi_airport
9 A: N% Z' T+ ]. V: _ ]
8 {: t$ P: b v2 _. b! u( @/ ~8 s2 Z% S c. v6 X9 F2 K1 e
city_map.add_child(folium.Polygon(+ d$ ]' \, S5 L8 v# i+ G# [' h/ |
locations=points_2, # 坐标列表
8 R- d1 b3 x7 M/ _: c weight=3, # 线条宽度& q* e, v2 t' B6 e* W# b r
color=yellow)). O7 s N% z; {
" C" e2 \, C5 x. I: v' I) X% C
# 矩形/ b. y7 Q4 p! z1 _- \' d
bounds = [
$ _" d3 B9 v7 U) G coordinate_ang_mo_kio,
* s1 u* c7 P3 ], K1 \ coordinate_yi_shun9 R4 Q3 s9 z- `0 \. a1 F, O
]: c! F* ]+ ?' Z8 m0 J, j
8 E, V+ h; a- U3 Y4 C* N
city_map.add_child(folium.Rectangle(& a& G& S5 _7 l7 L6 @. e6 r# j+ M
bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)) _# N/ x% h! f) o! [. d
weight=2, # 线条宽度
& h6 h, i1 p- |* K color=blue))
6 f& \7 T9 h$ @$ [, F6 L7 l2 I" |/ S& s
8 G6 B6 m- a7 D O7 D7 Q& a # 圆形, circle, radius units meters# t7 j- B8 [% X( d
folium.Circle(
( [4 i* B& Z3 w! T& ~; | ~ radius=1000, Q7 [6 E0 |6 c5 F$ i" K
location=coordinate_nus,! `6 g# H% O. T: t% E* Z
popup="National University of Singapore",* V! A! D/ N! |0 @; h, _% ], b
color="crimson",
4 h. w/ w1 c4 S; }3 t$ V fill=False,
- u8 B3 ?" v# V4 j3 _ ).add_to(city_map)6 @# o+ l9 J2 z
: I8 D0 L, o$ ^) s" s # 圆形, circle, radius units pixels& n. t$ `6 o% q' F
folium.CircleMarker(
$ k* A h0 x- j) P: B9 t location=coordinate_ntu,9 o) l" J P1 ^/ `2 V
radius=30,, B+ M$ N+ v1 t- M) J+ e
popup="Nanyang Technological University",
; [* w5 n' l7 C5 u color="#3186cc",8 w _& O6 o/ M5 b
fill=True,
4 d% P. I" ^9 C$ N" I fill_opacity=0.3, # 透明度7 @) K5 W( }7 m$ K' }: `
fill_color="#3186cc",
3 S) l2 ]* }! R4 y% i7 o ).add_to(city_map)
1 K+ V, n' \$ e; a9 p+ u1 |3 m+ P9 I( c- z' ]8 R7 }0 Q
city_map
: u0 t3 \ `7 c% X& V V2 m Folium 中的画出各种形状热力图. Y7 w: B7 P# x6 m* K
在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。
: v& `1 T& A3 e* u8 B import numpy as np" {9 `4 R; t% k( g
from folium.plugins import HeatMap$ J/ s" i6 n2 l* |& `
4 F/ m* b- w* c, N, s. k3 w
# define the city map
5 J2 Q" I; Q& D8 s# e city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) Y9 F9 B1 p( u" Z
6 h F, `5 b6 _, J: X5 w # 构建随机数据, S% o; N# S* {8 d+ x; x' i. R
data = (
( l# j! i: S5 A% ]% h& X np.random.normal(
3 E& {9 z1 z( d6 J size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
6 R' k K5 X( [, @ np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
! B }; N! E" B9 O# x7 G ).tolist()
1 w, d* D; V8 L: f1 P4 _' _ L6 Y( d$ _8 E d% b; x
city_map.add_child(HeatMap(data=data))5 H( m s+ {5 [4 U2 ]" _
city_map
. f7 M8 o1 i# Y% {+ L4 O 7 D8 l: t4 {* o. |
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。
/ {0 p! F" S) J& F import numpy as np
H) H8 D0 g8 ^1 J" u5 u from folium.plugins import HeatMapWithTime
# [9 t) n4 t3 a/ d: t7 K
& u0 u9 N5 `4 J! V2 C- ~ # define the city map& U! J/ l1 K! B: g9 p$ s3 @( v
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
# W7 Z, k: `+ ?
( B4 d( P$ x- m3 l) W& ?/ G4 g # 使用 numpy 建立初始数据
# l1 [" k: k: y2 Y; @ initial_data = (np.random.normal(size=(200, 2)) *" z# o4 W) O% U; V9 D
np.array([[0.02, 0.02]]) +
/ z! m; g- i' b" Q9 s. i7 z np.array([coordinate_orchard_road]))
' W q, v2 Z: {# F" X& ]7 j
1 S* Y3 R+ q% e. }( ]' ]7 a9 C3 U# p # 建立连续的数据# K' w: N% e+ F
data = [initial_data.tolist()]
$ u/ x: A r |6 I for i in range(20):7 y8 G; }5 k/ c w+ ~. V
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())
) G& j" v( T% E5 b% a
( @7 Y! F1 t& a/ r# l # 显示连续的热力图& `3 C% U. t- I3 d/ h
city_map.add_child(HeatMapWithTime(data))
5 O, W, L0 |0 g city_map
0 m9 L6 x/ _7 V& ]! Y + \1 ~" q, n& V8 H
经纬度点的聚类5 d+ y& S! a1 u/ {3 L9 U/ E
除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
& I, q z- Z t$ D2 r" \ from folium.plugins import MarkerCluster
# Y! G/ F# q/ A. O, }5 S
' E; H& [& U' c# f* ^' S: G6 O5 c # define the city map( }& Q( q* _9 x- V9 X; g4 ]4 q
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)) s7 l2 t* o) J( J6 ^
4 G2 E8 A$ s4 D7 U; F" t/ R
# 经纬度的聚类
4 T& _: u& h% `7 o! G* x # 在 NUS 的经纬度附近随机生成 100 个点;2 m- n0 T% D0 z% P" X
data = (
5 e, s! ~$ Y& L) Q np.random.normal(size=(100, 2))9 A6 G- `9 y' m7 a3 h( j0 ~, I
* np.array([[0.001, 0.001]]) +
9 e0 W0 Q. m4 e( P4 r0 R np.array([coordinate_nus]))
1 h" m- ]/ T% Z0 M h6 e r# H& p
5 W" Z5 [8 |3 z8 s! x! Y # create a mark cluster object
+ e! K( f" X& p marker_cluster = MarkerCluster().add_to(city_map)$ D- \3 v$ K7 p6 T: e/ R( {) |# v
& I. m1 d$ U6 _: e # 将这些经纬度数据加入聚类* T$ A- K0 p3 Q9 u+ ]: `% A! I
for element in data:
; L" S, I! J. p folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)
$ S! z! K: r6 w
$ B- Z& E3 ?2 i # add marker_cluster to map. J5 l y/ y6 n9 b" v' d
city_map.add_child(marker_cluster)2 u' ]( D9 T+ ?$ w) z$ ^: D
* ]# ?- g# t/ k7 B0 l, _ # 作图
X& ]: t: m+ z0 o5 l% ^9 @7 F! p8 ~ ~ city_map + I7 e( U! h5 U6 G
1 {8 M# }' k, O) P. N* H! e
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。 ; B& c8 T# Y: P; I3 \9 f) r L
参考文献
$ F9 y" r9 C" M) E7 p Folium 官方文档:Folium - Folium 0.12.1 documentation 9 ]9 {8 [; G" z0 f
) y/ I" [' ?' P; k8 J, [7 {0 ^4 O0 ]2 E
1 `' r1 i! V% A/ M% |" F$ q) g! \7 {& F
|