: x: K* k6 }! V: E' ^$ G, z! V Folium 简介
$ v7 D) Y/ { y 作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。 $ H6 Y& H' |1 Y4 J: D* P
创建地图2 ~. {+ a8 ^! _& ]( f$ [, b
通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。
! l$ c. H6 b3 N0 ?, P import folium
. Y' _2 e* I& e# A %matplotlib inline
9 @5 G8 w8 `+ Q) K0 Z, p1 K, o- R6 K I) H8 n Z" {6 X$ Q8 a
import webbrowser: \) J9 f3 Z5 |' d
, {% a2 U1 g" p- M( _; z8 i# m5 I! n print(folium.__version__), r4 }( p [% I3 q
2 I6 a _9 K& y # define the world map
; Y) H% O( B; a; B) J- t6 r world_map = folium.Map()
! M9 r: A" X: o2 z5 f2 h- O+ y # display world map
; W) D% x2 P, ~( s world_map# e' y1 K6 m$ ^" P; {$ I7 j5 U- ?" _
: C. {0 B6 @* k4 |& m) Z" r2 S3 Z, ~6 c
世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。
; W* Z6 i3 A& ]- M 在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
5 w+ Z3 O: z1 n2 G* @ 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
1 I- k8 ?( p! U) k& I 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。
+ ~- e* @) N; E" i! r # latitude and longitude in Singapore city
0 g. |$ j3 P% ~) t- Q coordinate_sentosa = [1.248946, 103.834306]
0 r2 l2 h5 D+ @' t8 t t) C7 L- O coordinate_orchard_road = [1.304247, 103.833264]
5 ^) n) w p/ X% v9 g/ p coordinate_changi_airport = [1.357557, 103.98847], j& ~/ ]0 L j: a$ I
coordinate_nus = [1.296202,103.776899]
, W0 ^6 B5 ^, C6 ?4 L* ` coordinate_ntu = [1.34841, 103.682933]8 M+ e' H: p2 A2 c# c
coordinate_zoo = [1.403717, 103.793974]: n$ K! K% M! l" L& S( I
coordinate_ang_mo_kio = [1.37008, 103.849523]
0 o) |" q. ]- Q9 [ coordinate_yi_shun = [1.429384, 103.835028]
3 }: E+ n. {6 a& O4 K- i' _, F% L9 B" q5 S0 g0 ^ D3 f$ P
# icon
+ c" I; t" \: b icon_cloud = "cloud"/ b$ q5 A6 ?1 p2 [1 u3 v$ u# t
icon_sign = "info-sign"! y- h( M" c( ~; D5 ?5 d7 V
9 l( V+ h$ j! ` # define the city map
; O/ i* @. j; i& s- ^9 m. L* f5 A # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}
/ X( T+ {, k/ l% ~& h! Z+ t- F city_map = folium.Map(6 e, ]. T {1 O) V
location=coordinate_orchard_road,# g* Y( i1 r& N. [* R& x
zoom_start=11,# H1 P; ^" I! T% Q! B: H Z& Z7 Q
tiles=OpenStreetMap)5 c2 h0 H& } k( {+ z0 @- {
, g" u& H0 ^: V$ M3 P' E1 ` # add marker in the city map) o3 p) T, ^3 W# N N M
folium.Marker(
: R6 x8 N% `, W2 R% ~; y7 u4 [ coordinate_sentosa,
1 V, N7 x4 H! M. L5 d5 N6 o N6 _ icon=folium.Icon(color=blue)
# L" ^1 Z1 ^' Q% P2 G) B" J( ^0 ? ).add_to(city_map)2 r+ Z! V0 t& @7 r! }* o2 ~
folium.Marker(
) ?9 z8 x9 ]) K, [+ P6 G coordinate_orchard_road,
/ i3 S7 @" |; @( L7 E1 H icon=folium.Icon(color=green, icon=icon_cloud)8 j& `+ C- I0 B2 W. L+ L& N
).add_to(city_map), q/ D% g: @' ]' ~
U2 ?* s) b4 i" t0 E! U" _& f
# add popup7 G8 R0 o, K, A- b2 \+ u0 K0 p
folium.Marker(
: N- L4 W0 L# e$ F+ p q# ] coordinate_changi_airport,% T* \, e) ~; N3 p7 D
popup=Changi Airport,
0 ]' z7 w+ Q6 H) v/ E5 o5 R icon=folium.Icon(color=red, icon=icon_sign)
& u2 m: k* V; _- }/ G1 z5 ? ).add_to(city_map)& ]$ D. V S0 @5 P% Q
9 g1 V: H3 @) o" `! p0 o0 W
# add tooltips and popup( i* C+ Y' [, K1 t; _' ]) u' c
tooltip = "Click me!"" p& H& B% g- f2 O5 {+ l' ^6 T
: |! f( w, D: R# V% a3 O# l$ Y1 i% \ folium.Marker(: f9 H) H: A: c8 [8 w* W! [
coordinate_nus,( B) H, |* d2 S, f( i8 V! u! v0 ~
popup="<i>National University of Singapore</i>",/ g; O! m, [* V8 v4 [5 H
tooltip=tooltip: {* x* V3 K) f- p( s; H0 v: _2 k8 D
).add_to(city_map)
3 ]6 h6 i" T2 P5 d. O
2 \9 H; ?. j# |: a) I p5 Q2 F folium.Marker(1 Z- E ^; g4 J! p! l+ J
coordinate_ntu,, f$ n# O8 y2 P0 H( T
popup="<b>Nanyang Technological University</b>", |& Q- ~" q" H+ ]0 f
tooltip=tooltip
, d. L& x# X8 Q% o, S ).add_to(city_map)
8 ^% w- q" A9 u- Y
+ W5 y2 T% `4 T# {/ ] # display city map
0 x; c9 o( X& W( h( k$ E city_map / y' W$ b1 a3 `+ D, y4 d6 w* M. h
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
- W4 t9 E E) F4 F, E: X9 { # define the city map
$ J1 \% O, P$ t+ ^- I) _ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11): K+ { A1 m$ {( g5 o, }: |
. ?1 d& d8 ?6 u. o) u [& V # 在地图中添加经纬度, add latitude and longitude in the map when click c' l' D k0 p' F0 K9 ~, J
city_map.add_child(folium.LatLngPopup())8 K/ i M- x% R: J
3 ~' U6 \. \7 F( B4 g- O4 J2 W2 U
city_map
& m5 V* z, m, H
0 D f/ j+ v* o: p2 T2 X2 N 几何形状
/ y- D% s' \( Y$ [7 e1 J% t 在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。
# J, y, l" U4 X) t # define the city map! [1 }% f8 C) O: C7 a3 F
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)2 J& K7 n7 d' e$ _8 k) x5 U
" Q5 I) t: b+ Y1 U8 T, w- q, F # 在地图中如何添加形状7 q/ e4 I( k3 O% Z
# 多条边3 Q5 M1 \" z2 O
points_1 = [
6 @4 v. z. ^+ T. _ coordinate_ntu,0 ]: d+ U1 M3 W9 B
coordinate_nus,4 V% [; A/ k; b; w. l+ i
coordinate_zoo$ n3 }8 c* N, g# i d8 z1 b
]
& Z. s/ ]" a8 d7 B( v/ p+ C8 L
. @2 V% K1 c1 W/ E8 J # 在 city_map 中添加多条边,第一种添加方式4 Q1 Y, ?5 w6 y1 [4 |3 X
city_map.add_child(folium.PolyLine(, d% q( Q2 s6 T
locations=points_1, # 坐标列表9 p) @" I- L% y$ X- s
weight=3, # 线条宽度
9 J d% V% l7 }6 i color=gray))- @$ i$ n. x8 @ R& ]$ W
& Q6 o7 x4 n# p
# 在 city_map 中添加多条边,第二种添加方式
9 J5 {9 a7 P# I4 E% l/ b8 f0 V folium.PolyLine(
* `8 h$ m% S4 M- I( l8 J locations=points_1, # 坐标列表
4 @. s/ B" M p' H% [" F5 Y- y& R weight=3, # 线条宽度
: Y+ S& N9 ~7 h0 b color=gray).add_to(city_map)0 K9 D+ A' q: L; `4 e$ g
. K% q# \3 `" D9 N7 S # 多边形
9 Q4 p! E3 ]% |- g4 M points_2 = [, j4 n, Y3 C/ C. c8 Z, j0 ]* [
coordinate_orchard_road,
8 h0 q& _& W: P: T9 t$ s/ Q coordinate_sentosa,0 \3 s( p2 s- H# J h, U
coordinate_changi_airport- ]4 u' T- i! H$ b3 c' k3 P7 \
]) o4 u8 u* v6 l H N) m
% B4 ^; X& x" x9 b* x" @' g& H
city_map.add_child(folium.Polygon(
* ~. ]3 [5 X; C5 v; o: g locations=points_2, # 坐标列表 b* ?4 I, x [) h
weight=3, # 线条宽度# t$ `4 V- J7 O9 m. `
color=yellow))
$ U) j& i; ?" b# p# U
: e1 M! K" D; D+ t. ]/ R" g # 矩形
# g3 `4 F3 q: U+ l bounds = [6 Q! I" o0 l) u
coordinate_ang_mo_kio,
7 T7 }- Z1 w, k7 n3 M7 V' d coordinate_yi_shun
$ w R/ _* f$ _( O ]
9 }) G4 d1 X1 r: l! b9 f- w
. ? ~! N* B4 I# Z% v) x city_map.add_child(folium.Rectangle(
' \+ K7 l: y0 ^# K( z0 y bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
+ x$ x* o# {# _$ s" p' R/ {1 { weight=2, # 线条宽度- F' g5 A; S; Q" l w$ p1 e
color=blue))4 N, g3 ?1 ^2 [5 T
) W M# h8 ?* \" e9 A # 圆形, circle, radius units meters
5 J4 F& | E/ j0 r% P; [ folium.Circle(( ?9 w' X4 [& z% }. U/ s( o0 L" g
radius=1000,: E/ [; I+ D6 r! N; }0 W" P2 ~
location=coordinate_nus,
6 j. \$ e! d! B5 T0 o popup="National University of Singapore",
5 D! x, r( k' L( q" O# `; ^0 _6 t& R color="crimson",
' J& l6 @: r F! R fill=False,& Z1 g* L4 s( o1 p# r' _
).add_to(city_map)
; w3 T7 k2 t' s; n U& K _- R/ e" I) F0 V
# 圆形, circle, radius units pixels% n+ G/ |, n# ?* Q- C
folium.CircleMarker(
% n2 m& p6 A/ o7 x7 m+ o' j3 e! j location=coordinate_ntu,
! f6 `* N2 Q+ b8 |8 l( E0 w radius=30,
/ b R: L9 [' @$ c& x- f1 N, G popup="Nanyang Technological University",0 l! E; t. G/ \" m# ^$ H9 ?
color="#3186cc",2 n( x7 b7 Y2 H1 F0 P
fill=True,
) }8 @6 V$ ?! m7 f" C fill_opacity=0.3, # 透明度
: Z0 s& b6 ~; d fill_color="#3186cc",
. _8 L5 M& a" G0 d ).add_to(city_map)0 \. ?5 c- `! r0 o) a
7 s, d; [; }2 H$ |/ l
city_map
: r' @# G) _8 b( ? Folium 中的画出各种形状热力图 `: L5 ^% a& o& y3 w
在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。 3 o2 a1 M! U- b
import numpy as np5 a, T2 T" n0 R# D
from folium.plugins import HeatMap
+ z% {) Y( a0 P! r8 h4 e" k6 Y9 S; L+ e" o
# define the city map
8 e) U+ X, V6 K, |9 W* V' s* k city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
# ~# E# F" z# y2 A
5 q2 U2 W4 K1 l- n # 构建随机数据
: v; j+ a6 g, w6 X9 e* O data = (: Y* s+ ^; Q: n5 D# s0 l0 m& J* F) B
np.random.normal(
0 m7 @: a3 F0 {3 {. o size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
% ^) o$ E' A6 w+ C' @) g$ A np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
) Q" O2 ^1 B% S6 I, H2 b6 g ).tolist()
2 W. }3 }# U* ~
$ j2 ^' y" I/ K: I city_map.add_child(HeatMap(data=data))
6 i( G' r3 w" X { city_map & I7 Q$ _$ j+ x" g+ z- e' X2 I
% S* B" s% v! K4 {5 l 除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。 ( j+ G Q" W7 ~
import numpy as np
l$ ]0 |1 ?) U4 P from folium.plugins import HeatMapWithTime
3 u5 M. X* j! u5 o- x: k& o+ ^" g7 [- Z
# define the city map
" P% _+ [5 L% s: |5 c% j city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
( ?* f$ @4 s3 c. _: Q0 [9 X. ~8 F2 g$ s! M
# 使用 numpy 建立初始数据
/ C$ t% ]& E- C# }# K- y initial_data = (np.random.normal(size=(200, 2)) *
% K4 T5 C: r& ]3 K5 t5 r) E! {2 L* k( G% K np.array([[0.02, 0.02]]) +, d) D$ j, o; A) G8 A! J* U
np.array([coordinate_orchard_road])); Z6 \6 H r2 |* i0 a3 {4 h. D' K
; z8 ?" u) C, s! B& E% h
# 建立连续的数据
8 j" ^! o4 q, n+ L( A% r data = [initial_data.tolist()]9 @/ P* N1 V7 b/ U& i
for i in range(20):0 x8 \/ X0 h) v3 r3 x3 S
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())
# Z5 n7 A7 i4 Z" x' M
: j% Z4 f, d2 `' ]. K4 p # 显示连续的热力图
' i$ i2 p% ~7 h5 l3 _+ s city_map.add_child(HeatMapWithTime(data))
5 d; k( t1 \. l4 ~- X' p city_map ( Y1 v3 \$ d! e0 Q5 P+ U2 D ?
2 Z* y' p+ s3 D" ^! p) }4 K6 ? 经纬度点的聚类
' F+ d, H. K; c! a% f3 m4 R# c4 ^* J3 k5 ? 除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
" y$ k7 v, s# E6 ~2 k' L from folium.plugins import MarkerCluster
- J5 f5 u2 `0 v; g8 n# ?* y# w! [
# define the city map
1 o- S6 R+ `5 q0 `" e: R9 w4 E+ R city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)9 h3 u7 f( B8 U# R7 ^% t
$ B+ U4 L! y4 r! r. |8 `; j # 经纬度的聚类/ U& @. c0 Q! e' @# E! F, h5 e5 {5 ?
# 在 NUS 的经纬度附近随机生成 100 个点;
5 H7 G9 v2 g" g: j5 n- R data = (
* c% ~% m; l7 t" Z np.random.normal(size=(100, 2))! u& H! y# @2 l& t' p) P
* np.array([[0.001, 0.001]]) +
& E; d/ F1 b$ H+ |# l. v- @- f np.array([coordinate_nus]))$ N& s( z+ k. J* Q) q) `4 }' \4 E$ s& w
, l3 ^+ X" U' P- E: e8 p) c
# create a mark cluster object7 ~0 l! p/ h7 Y" K
marker_cluster = MarkerCluster().add_to(city_map)# N- c8 ~2 ]4 O' G
- p) m# l1 s8 ]6 D* P
# 将这些经纬度数据加入聚类
) [* k9 P7 _3 M, g0 {* n for element in data:
1 x' {$ Z: I' g( ]3 ~! ^; d folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)
4 O) F; t* ^ \9 G) y3 `( @% j# Z+ @' _) q' g9 v9 P, L5 W( }
# add marker_cluster to map6 K S+ n7 Y! X0 b6 `
city_map.add_child(marker_cluster)+ J1 \& A0 s. K; a6 t, ^1 N9 m
' O5 \0 i: D G8 ^6 B
# 作图
- e1 \/ k& U! A: Z( H" R city_map ( i- @) [8 b5 b& @
* x. o" _5 C2 ]# H+ Q0 f W 以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。 $ {( S) o. M% {3 g, w4 l
参考文献
5 X# c$ s6 U' X0 Y. w Folium 官方文档:Folium - Folium 0.12.1 documentation
# [# `, d- P: Z" @$ k# z- Y4 H5 S' z
7 Q2 s9 v5 S, `+ {' ?
) I) M2 t8 E. j# A3 M/ m0 {, m$ B% u. S# P) d9 u
|