% g5 E' i/ K# a
Folium 简介" m: h) C) q7 R0 O$ U/ u8 E! l7 Z
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。 q! N$ _" m% c- n6 `8 _
创建地图
& F. g) g3 s/ \ 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。 3 |8 N t$ `7 E! N r
import folium
0 v, A8 O# v5 D; ?% B %matplotlib inline
{. @4 R0 z" E6 }# {* M% w5 K
9 ]4 H2 j4 a* K2 j2 W2 ] import webbrowser3 I$ D) ^, }* U' G8 x$ p$ b$ Z& [
+ }& u% `! b+ w6 s" H* Q print(folium.__version__)# m: _8 ]! b- V2 E$ O, W) Y7 G* O6 U. ]
+ E ^/ [% D& l! W' n # define the world map8 q! u. }# h# F5 ^
world_map = folium.Map()8 [/ q' o& m4 S/ I. f! n0 ] |
# display world map% p1 y) s9 P! C1 ?3 M
world_map- T: x3 e' M+ u7 w
4 j( O, y1 s& B7 K 世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。
. j; J0 p* i. N- j" i( T 在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。 ) p; `! d9 E2 ~4 D. y2 @2 B6 a
有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。 9 i5 d/ _: U& ]4 K( B3 E( v
另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。
* g* ~/ A3 X% G0 f! A" D2 h- S # latitude and longitude in Singapore city# r, ^0 T/ ?) C* ]
coordinate_sentosa = [1.248946, 103.834306]. y- B# \* T, J' `; D6 h8 N g1 M
coordinate_orchard_road = [1.304247, 103.833264]
# n+ n4 c5 s' ]& V! w coordinate_changi_airport = [1.357557, 103.98847]
4 }) F4 A3 b- @2 y, p coordinate_nus = [1.296202,103.776899]4 y0 y' K8 o. `* J+ E* ~
coordinate_ntu = [1.34841, 103.682933]; W' s5 L7 P: _% O8 m
coordinate_zoo = [1.403717, 103.793974]
7 M$ s( d8 u* R" F d% R$ Q coordinate_ang_mo_kio = [1.37008, 103.849523]
8 H$ D5 |# y6 f" x coordinate_yi_shun = [1.429384, 103.835028]# C3 o6 w. c2 b2 |
) r) ~3 H7 H* {) r # icon6 E8 V& v8 D" l2 v5 Z0 _: i* x
icon_cloud = "cloud"
% J6 i, q7 P: E, J icon_sign = "info-sign"5 q9 h6 P9 y$ I- K) H# V
# M& @& E' f1 |9 G) U2 g7 G # define the city map
( S: d& K& _ \: ]2 @+ {# t # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}
3 |9 f+ t2 W) g3 y city_map = folium.Map(
; p+ |0 y2 J; u6 {+ r" x2 m" M location=coordinate_orchard_road,
" L! D6 C5 E- q zoom_start=11,; a6 B- Q) J! K
tiles=OpenStreetMap); @/ [5 f2 X) f% }: c! o& \$ X; I
8 ^# T3 w3 N5 z9 W! G0 p # add marker in the city map* d. y2 L" E/ z: G4 \5 K+ g' N
folium.Marker(5 e* {) i P" w& N" }. f
coordinate_sentosa,
; `0 u* T- W8 p4 T! a5 J. ^0 |' c- ^ icon=folium.Icon(color=blue)
1 ~+ s2 i& P% L8 P+ C* p ).add_to(city_map)/ I/ |6 }8 V4 C+ U+ g, ]- Y4 X; \
folium.Marker(# v7 m3 z0 G* ]- p
coordinate_orchard_road, @9 J% P4 y" s0 |3 T z
icon=folium.Icon(color=green, icon=icon_cloud) o/ Y. e2 g# G
).add_to(city_map)
9 K1 _( k$ Z, ? K$ ]% i- C- u5 d
# add popup
E- y5 E; v6 ?( H8 ~: N folium.Marker(
K% l) c% N4 g% O9 y coordinate_changi_airport,
. N5 E4 G# l8 k% |" M9 U popup=Changi Airport,: R" y* s+ R# p$ ]9 a" y
icon=folium.Icon(color=red, icon=icon_sign)5 w1 H E; h7 \
).add_to(city_map)5 \$ L4 P$ |# [( T; y
2 K0 H( O/ c, m # add tooltips and popup
" r' r* O# S1 T$ R0 ^% p tooltip = "Click me!"
; ^5 ^& c" A* O+ }
! I9 p) g: o" |5 x folium.Marker(& l; W" M: S) C+ Y: z9 I
coordinate_nus,
0 ?7 _ K1 T6 a2 ^* _ popup="<i>National University of Singapore</i>",+ @0 [. g2 I$ q
tooltip=tooltip
% G) [5 M4 L# \& ` N4 U: V ).add_to(city_map)7 b( |% d+ m2 C+ p" ~
# R, V. a, c# |6 k
folium.Marker(
8 t2 P6 Q a! T Q O4 h coordinate_ntu,# O+ l/ @1 ~6 C& l0 c/ m
popup="<b>Nanyang Technological University</b>",- a$ |$ H6 T$ U9 Z" \* |
tooltip=tooltip
$ b E$ U0 B3 R6 u6 V: y( O K ).add_to(city_map)+ _1 E# I1 \' b5 R0 r4 c8 |0 u, ]; k" C9 O
. \5 B2 t8 m l3 E4 z! u: W # display city map, {6 i! w5 e6 }' l' Z$ c
city_map
2 x$ N* }. Q/ m* E& S Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
x+ ]" E& H3 \0 b( Y, M # define the city map
1 D1 Y0 L9 x3 ? city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)! H$ w" k1 E1 D$ M, ^
# `5 m- \; n' H% h
# 在地图中添加经纬度, add latitude and longitude in the map when click
, }% X5 d! I2 | city_map.add_child(folium.LatLngPopup())
. |: ~8 _' {9 v$ }4 v* Y: F$ b j4 W4 c: n7 ^6 X" U
city_map + B) t$ w0 w! `5 Q. y, {
* N0 n* u! q2 P- d
几何形状
+ I* E3 @7 R" S$ y! d 在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。
1 Y' X9 P) N: F # define the city map- Q2 `9 N# U+ ?2 \3 `% K8 N$ c
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)* _. P& b( s: c2 R5 O5 Y5 ~
2 d4 c; R, w8 J6 c6 D # 在地图中如何添加形状+ H0 c0 j& }$ s0 w* S# V( ~
# 多条边& Z/ ?" F$ C* N/ l( z$ A$ {& O
points_1 = [
a5 ^3 m. @3 s* H0 w" v coordinate_ntu,
9 d3 A* z4 z4 ?2 I/ w coordinate_nus,
1 G: o6 e7 _; V; f9 @8 l coordinate_zoo
% ^9 r+ C. M& o! X. N- W9 D* d% D ]+ m2 B& X2 ?( d; H* ^$ O
) m0 t( Y: z, ~9 C& v6 u # 在 city_map 中添加多条边,第一种添加方式
/ g8 I% [& |8 w8 O6 a7 A city_map.add_child(folium.PolyLine( V/ k6 K8 T) t8 L" B
locations=points_1, # 坐标列表& K4 L* g" W1 m. K6 e% U
weight=3, # 线条宽度
0 b6 T, j2 D- E/ ]2 M+ _ color=gray))6 @ T. z: k; q, t
0 u* w+ h1 E, r1 E4 d! L8 W # 在 city_map 中添加多条边,第二种添加方式
4 ~5 e9 X- t3 M9 l/ T: k folium.PolyLine( w% c" I" K9 E# [& O1 X
locations=points_1, # 坐标列表: l4 `" K2 C& U8 D4 R2 K
weight=3, # 线条宽度
M& ?/ }5 d4 Q( _7 L9 Y4 A0 b1 ? color=gray).add_to(city_map)
6 m& S4 N9 X) Y U7 N
6 J! q$ w. V9 d3 [) C# M5 ^ # 多边形0 a7 \ U1 h+ m2 r" k' ]+ h
points_2 = [
" {6 h6 B9 \$ C7 }+ J4 b% m coordinate_orchard_road,0 z# v/ g- c/ p/ h
coordinate_sentosa,
" D+ ~8 d' f, B* ` ] coordinate_changi_airport
* O1 s3 D, L1 K4 n' }' T& p5 H ]
4 |. ~( Y9 |4 J8 v0 c+ M9 V. }# R T7 y; s- h
city_map.add_child(folium.Polygon(' f# l! P/ {3 @4 r, }
locations=points_2, # 坐标列表5 W" a$ w1 C5 u R- P
weight=3, # 线条宽度* w5 S. t) f" z1 P% ~ L" a3 ?- c
color=yellow))6 t; ~! V2 G8 O/ J% O0 R
8 r$ V w5 _: `' C6 Z- B # 矩形
+ X O o1 N B0 K+ X; k0 h f bounds = [
6 Q2 { R* i, C- }9 G% l coordinate_ang_mo_kio,
% R9 q( [( P6 @7 M coordinate_yi_shun
8 ?$ f! F: D7 V7 n! } ]
& K) I1 b; e( o6 \
+ @) i% w- r2 w city_map.add_child(folium.Rectangle(
* C3 w) z6 u! U$ A$ [ bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
! W3 i2 K+ \2 j# @5 g6 A weight=2, # 线条宽度
; z' U2 L8 }8 |; k8 [ color=blue))8 ]( [2 L) b$ E ~5 w& l* e. F- J
* v" e8 U* R6 F8 l$ v7 b; t9 d # 圆形, circle, radius units meters* U$ @! m. ?- g5 a8 X* K2 f6 G
folium.Circle(; G2 D4 R. N. _7 m2 _! w
radius=1000,
. D3 e }$ Q' X) b) v* f location=coordinate_nus,
2 ]2 O# x, f" O popup="National University of Singapore",
+ N# L3 X' v$ t V+ J' m( l6 n color="crimson",; T& }# a+ ~* t* w, \% w6 c; \3 m9 t
fill=False,) J% {# ~8 W9 u y
).add_to(city_map)
/ Y5 V' c; m3 J% ]% A) Y
2 r% B$ L' @4 D* f" [( G # 圆形, circle, radius units pixels
& K1 K0 |: Q& l9 a4 }" i; f folium.CircleMarker(3 Q _1 @* P+ i. x, Q
location=coordinate_ntu,/ G. Q1 c- u; h* \: r2 L+ `
radius=30,8 x7 b: w; R: g
popup="Nanyang Technological University",7 a2 Z( c1 Q* a
color="#3186cc",6 s! N% B$ n3 m, z! l( B
fill=True,
* ?* @$ k( n A5 T2 z9 @ fill_opacity=0.3, # 透明度( T/ M! U1 N3 A) Z: m3 F
fill_color="#3186cc",: Z: J* @7 ?; E# T& \- a; k1 N
).add_to(city_map)/ @" r, O% R( U2 R8 Z; E; r) T
' B4 k' }, Q# ~/ } city_map
7 Y) s* ^+ t- g( ^( b R7 _4 o Folium 中的画出各种形状热力图
$ d' V5 z9 R$ o, c- Z% ~1 u: [ 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。
5 e7 F" \1 x6 x; i+ ^4 j8 ~( W2 Z2 M import numpy as np
`1 Y, l" T6 }- {9 a$ | from folium.plugins import HeatMap
. J$ m* S* h6 X6 H! G$ d, s9 D8 w+ ^
2 [% s3 D' x9 X7 ]8 S# |" r7 h* F- l # define the city map
: Y$ \3 r: b$ T, ` city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
, n/ e! S j( m) s$ F5 J2 W4 ]+ j+ E
# 构建随机数据' L0 F B% t( L# m/ E/ W+ {3 k, ~
data = (
3 U2 r, D7 [) Q8 W' d5 ~ np.random.normal(; p, c! S9 Z9 K8 U! V
size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +( B% n# I& d5 _# u/ ^
np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]). w z4 D; K+ o% j" F" Q
).tolist()
9 f4 E: k! h D6 |' e# ^- y6 T7 T$ `: ?' G1 `. K- V
city_map.add_child(HeatMap(data=data))
6 Q/ W; K0 J z; S city_map
?! g' W _9 w: E
9 d% @# T; p E, y# i 除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。 4 v1 [% e, a% U' `
import numpy as np5 O7 K7 y9 T2 t( @: t9 c
from folium.plugins import HeatMapWithTime: R, j8 n/ [$ y! x, ^
& {' m$ V2 i' H( X6 c% ] # define the city map) W3 t4 P2 @" L. ~8 e @
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
2 ?- a7 ?( o; ~' ] b& F! c9 H1 D# J* y# b; \6 \* `
# 使用 numpy 建立初始数据
* y! S* [3 w6 V+ M& O initial_data = (np.random.normal(size=(200, 2)) *% D! q( Z3 v j
np.array([[0.02, 0.02]]) +# @8 ?4 b7 C9 u
np.array([coordinate_orchard_road]))
+ w0 |6 r4 |7 @" k
* u: N+ c7 C1 r # 建立连续的数据3 \, k9 }5 O* d# t8 }* v) S6 d
data = [initial_data.tolist()]
* F- k; _ n F, ]$ N for i in range(20):
# T! [1 f+ H" R6 b0 b data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()); K( m9 ]( a2 j# f' p8 M
+ e% }8 Y. y- v \0 H( `
# 显示连续的热力图
. \3 `7 f; b1 w6 U city_map.add_child(HeatMapWithTime(data))
* ^6 j" g$ w. I9 r city_map
" e% G2 D% A: o0 `2 H $ v' N) J* y- q+ `( A
经纬度点的聚类2 a" m( M/ s# V+ k0 C' U
除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
; A0 b1 ]$ n2 Z O% W3 O2 v# k from folium.plugins import MarkerCluster8 j, D% b' a- d/ U# v+ v- }% T; F( F
?/ U b( N7 ^ E
# define the city map
, G- V [# ^: e% _& A8 ?1 m city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
/ c. u1 M" Z- M& v
+ e( z. E. g* I4 l: y5 ` # 经纬度的聚类
/ x) _& c* m, n& z4 n3 U; ? # 在 NUS 的经纬度附近随机生成 100 个点;
5 q6 H. H9 b) _2 w5 Z) ? data = (2 \# D* k. V& o, T( m
np.random.normal(size=(100, 2))
: r7 J& u! P9 h1 s/ m- ~% o) l * np.array([[0.001, 0.001]]) +0 p# m7 C, z/ ^# l* g9 W: j
np.array([coordinate_nus]))
# x5 {4 A. n* v8 `7 m2 ]/ V% M: w# n3 y$ w2 n& a" x
# create a mark cluster object
8 F% \0 @4 J8 S7 T) S# P$ W marker_cluster = MarkerCluster().add_to(city_map)( c! X* C& a4 x7 {; J/ F! V
, C4 D$ I% U8 X. X: J
# 将这些经纬度数据加入聚类& a# @" ]3 H( [+ _1 v& F* c2 A
for element in data:5 O' s% Y( n1 W1 \1 t- d7 L
folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)( s7 P7 u' R2 `9 f' w- h4 m) m
" q: P! |3 V/ O* h$ S # add marker_cluster to map
& O0 a- h: w; G; b- M city_map.add_child(marker_cluster)
, S! E6 y; @' C- m; ?7 _. f, _, e/ {0 u6 W$ r
# 作图, K9 o/ H# }, T8 F7 t% F2 k" c* U
city_map - Z2 e W9 p1 W; K1 u! i
4 |, X9 H9 J+ n7 h3 c" ^3 r7 E+ n; K
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。
1 B! L; a7 I: n 参考文献
; I- J8 y0 ^) h) D Folium 官方文档:Folium - Folium 0.12.1 documentation
2 o& d& j( J) T" p, Z1 j4 I1 x# k0 P2 u- D6 {
4 v/ b1 q V! D( ^2 d% f& q2 z( D
+ l0 c( w% ]$ V5 y5 [
* Y0 c- P9 @/ \7 @% d# |
|