1 E5 s: `% D7 p8 C5 a
Folium 简介
e) u& ^6 Q* c# b. D! v' | 作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。 5 ?4 [$ P9 R- [: s
创建地图
3 G+ k$ E8 Z% b. R2 [ 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。 ) c' |; J# f0 w8 b
import folium. i/ f6 w7 P2 x% Y" R
%matplotlib inline
8 B! R& H2 o k# }& {1 w/ F5 O) p, K) L( I* l
import webbrowser
" U r5 `' l3 p$ z; V7 N; {! U% N4 @! x- o5 z
print(folium.__version__)
4 r5 ]( _2 }1 \$ D6 ~' l! w$ O
/ D. |) v; f+ Q8 v% d7 g9 l9 ^, r # define the world map* u! y% ?* o7 m; x# _2 M) N
world_map = folium.Map()
0 e2 z0 O* b3 U4 q # display world map
- A' ]: B7 D' q+ M world_map% J# @0 z1 U/ q' e
3 _4 X8 ^: h" n. g* B6 u4 x
世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。
, m$ [; n" b3 s% J, y5 \4 j 在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。 0 T8 r8 j' x) I7 U4 ?
有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
0 _# }# {& k! u: i5 x 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。 7 P7 f7 m# V I! I& X1 w
# latitude and longitude in Singapore city% S( H, R3 {4 D1 q- m3 p; Q
coordinate_sentosa = [1.248946, 103.834306]: h# u5 G) {) X4 Y9 ~2 W: L; ~
coordinate_orchard_road = [1.304247, 103.833264]
+ k- E" z6 r" l4 { coordinate_changi_airport = [1.357557, 103.98847]
5 K4 t7 A# n0 T' Z- J coordinate_nus = [1.296202,103.776899]
+ o5 P/ p: G5 B# U; }. k5 O coordinate_ntu = [1.34841, 103.682933]
' j6 H. a5 ^ }. W" O coordinate_zoo = [1.403717, 103.793974]- ]+ z! `: q W/ r1 ^0 y
coordinate_ang_mo_kio = [1.37008, 103.849523]
* J% A8 R: r7 |* \ X coordinate_yi_shun = [1.429384, 103.835028]: p% q' O7 s9 B
! d* r! `* ]; e' l # icon% a0 ?$ ^0 x, R
icon_cloud = "cloud"
$ h0 d2 j4 Y. Q8 a. n3 E icon_sign = "info-sign"
+ J9 [ y$ U' z$ Q0 c
; B: Z6 A# m0 N # define the city map
1 C. m: w- ]; B1 \ # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}
5 _/ Z: |) w5 _) d$ Q. G city_map = folium.Map(/ Q3 \6 w; l2 g' y1 ~1 k* S; h; @0 p
location=coordinate_orchard_road,9 K6 r, A$ z: m: f
zoom_start=11,
2 G: I+ M: E' r" J* ?# }! d* ^8 H tiles=OpenStreetMap)
8 w! B" h6 h b. s" X: T* j
% k1 Y! \" ^% u9 n' E # add marker in the city map* B [4 s3 ?6 _' @
folium.Marker(
9 p) f3 n7 j# ]3 K) ?9 H ? coordinate_sentosa,% y7 Y- Q) E p8 g9 t
icon=folium.Icon(color=blue)
9 i2 ?( @$ U; A4 Y4 I1 A ).add_to(city_map), D# \0 l9 @: g5 m+ G$ _3 O
folium.Marker(
+ B# ], j8 W8 F! X% e coordinate_orchard_road,
2 a9 `) ^" l! C) V& H& b icon=folium.Icon(color=green, icon=icon_cloud)
7 z/ b6 P" C) d( A! V- p* @ ).add_to(city_map)
6 Y9 H! n$ ~2 b9 m( \& a1 H
* }( i5 i4 M2 v% f # add popup# G/ H; V8 J# e
folium.Marker(0 U2 M( J2 p7 _3 F, @
coordinate_changi_airport,; n' d1 D2 ^7 j& l
popup=Changi Airport,, o |9 N5 s( m: ^8 W8 J$ M1 |
icon=folium.Icon(color=red, icon=icon_sign)1 w: |" J, g# x# z2 `
).add_to(city_map)
, P5 \* a7 ?% p. Q d" d5 }! p6 h h' w
# add tooltips and popup
* G$ E$ K9 Y# _! Y$ S& f' k7 A J tooltip = "Click me!"
6 ]- {) `( G: D& ?# L: h) B9 r" i2 e5 A
folium.Marker(" g3 U X4 Z: g/ m+ l3 g8 X& [: c- [
coordinate_nus,( i& a% k3 ^2 ]! I: T
popup="<i>National University of Singapore</i>",1 e" i6 T3 F& q! d- L7 d# K
tooltip=tooltip
: X, @' e) o+ _0 w ).add_to(city_map)' P, F: r0 D; \! w
* g9 g/ h4 k3 J; z folium.Marker(
+ ?. a9 P0 B0 h& V6 ^ coordinate_ntu,' V2 e/ _& t& G! M
popup="<b>Nanyang Technological University</b>",
k7 R9 q: ^0 G4 ~ tooltip=tooltip l* g$ F e2 M) P. |/ ~& y
).add_to(city_map)3 W# v, \, y4 q% F# {+ C3 @* F/ d
4 N$ u8 ]$ T4 ?/ H, `! g # display city map
& v2 Z) w: D2 B F) U9 Q+ @ C8 \ city_map
: ?. [& O$ W+ j9 r7 `( H' G Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。 ( T1 t; S" y6 A' Q4 f0 I
# define the city map, B, _) {. ]' h' i
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
4 ?; S! e# \$ i( }' W- T) L5 l A- v# A5 n/ f& ~! |$ s
# 在地图中添加经纬度, add latitude and longitude in the map when click) [8 A. n0 {) ]& ?, E W0 W; C
city_map.add_child(folium.LatLngPopup())
' r& @; U% z8 a% U8 ?
; U3 e7 ~- P" ^9 l+ d( i0 ^ city_map
% d! Z; J3 H/ `8 v 3 e- @. I8 V9 l$ l8 x3 w& ]! G
几何形状/ S& M/ p0 @( N9 n; e. X( w
在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。
/ r0 a9 |( c, |: y; ]' _- a # define the city map
( h& F# a+ `* s city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)) F7 F6 a6 U' D- W! j8 _' m2 I, l
1 N1 w, l" b" W+ `, U
# 在地图中如何添加形状
" s' @ k1 w6 @$ a # 多条边
2 o7 @ [+ H0 O/ ?# f, m points_1 = [& ~4 {) Z2 V# U% R( P
coordinate_ntu,
+ ^( n% `2 s# h2 @ [4 m coordinate_nus,
. K, _) m8 v6 Y: n3 A% x+ C# y coordinate_zoo6 T) e+ J/ N9 F9 i; \
]
* x o" N; e, `2 J& s! w& b
8 W6 S' I* F) @: @ # 在 city_map 中添加多条边,第一种添加方式. |" e4 e3 A9 Y
city_map.add_child(folium.PolyLine(/ i- w* m" y; R% B8 f
locations=points_1, # 坐标列表7 k9 E& Q& t3 K! y' ]/ o
weight=3, # 线条宽度
9 {1 i4 p6 W' j: f color=gray))
( Y/ S6 z) D$ L# ^# L* l( l. X3 X; G3 K' N% @) a: G( B# T9 K
# 在 city_map 中添加多条边,第二种添加方式
s9 L: _& n; }, s+ \6 c folium.PolyLine(6 @2 k# y' ^: q4 M+ [- N& h0 V. I: J
locations=points_1, # 坐标列表
: o# x! m: k, I* z weight=3, # 线条宽度5 l+ h& W2 h+ f
color=gray).add_to(city_map), L' K: @8 Q; V* S2 i: z
4 B" h/ U6 U. Z/ k! y5 C4 K
# 多边形0 ~( b( B( l2 b6 A1 Z( ^/ e* O
points_2 = [1 g- i' i6 K& O% ]# t7 D9 V
coordinate_orchard_road,
: g3 ^# N5 E/ ^0 _, d" I coordinate_sentosa,
: K$ [. R3 ?& n, P# q+ |! J1 K( U9 ? coordinate_changi_airport
( t+ @! E/ j) s t2 N ]
7 j9 }9 G$ z% T& ]. Y
0 e+ g/ r1 @( @; P, A5 W city_map.add_child(folium.Polygon(
% h: C% o |. B# f! ~- D& t locations=points_2, # 坐标列表6 K; x1 [- m5 T0 @4 Y6 D7 ~
weight=3, # 线条宽度( | Q/ @% Q, ~7 D0 c
color=yellow))
$ U" Q0 J8 g5 w8 T; q0 h. ]9 K9 x% v, L% o% R
# 矩形. h" g3 M1 |1 g9 c5 c6 V
bounds = [5 s7 m2 Z* x3 N7 d
coordinate_ang_mo_kio,
5 x4 F+ e; U0 n/ {9 w) H coordinate_yi_shun
' \* _* d$ p% W3 U ]
9 Z( S+ v, S/ T. v6 m1 T( R! g, @( z" |5 ?7 f& t
city_map.add_child(folium.Rectangle(' \6 r0 q3 b/ B! f3 W1 I
bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)/ x- k$ D! q1 L1 x7 b J
weight=2, # 线条宽度. k" ?4 a1 ~ _) N7 f$ T9 L; y* g
color=blue))
; e o* Y o& V7 T; M" l0 h7 \! Z) m" H7 n) M% X1 Z6 a; S0 [1 _
# 圆形, circle, radius units meters
* a# V, h: m$ V folium.Circle(+ M# }5 F+ c! m8 z" n9 v3 x
radius=1000,
7 f: e+ K6 l( \0 w location=coordinate_nus,& i" ^, U+ v3 `, `/ M3 q
popup="National University of Singapore",8 A/ {5 [8 o7 k/ ]3 F- \/ U* Z
color="crimson",
5 t" x6 L/ Q1 B fill=False,% r6 Z# [2 D( R! M4 Q- ]: ^: `0 \
).add_to(city_map)( E+ y& R8 x* O4 W" [9 {* ?! O% Z( v
T1 R$ [* T. ~% [
# 圆形, circle, radius units pixels
8 `: O' m( G; j8 b folium.CircleMarker(6 j2 U; S) j' t5 q8 Y0 N! k
location=coordinate_ntu,
3 [: V4 o' H4 u7 k; c radius=30,9 }7 C" g3 o7 i7 | e- p
popup="Nanyang Technological University"," K( u" w9 b& G& {; u
color="#3186cc", S) G. F* m( @8 V7 @2 X" h
fill=True,
- l4 A ~+ ^& q2 u. s. E& p fill_opacity=0.3, # 透明度 Q2 U, {0 l% o7 {' G; z2 t1 m
fill_color="#3186cc",) I- J3 g" ?; t. A$ i
).add_to(city_map)
: _: P; H+ `3 P& E u
3 U! V- K+ T4 P+ E, ^& r0 N4 p. O$ j city_map
+ c8 E6 j5 l4 U$ L1 U$ `0 T Folium 中的画出各种形状热力图
4 s, m: n3 A$ b% g/ e: y 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。 % a5 q1 d& Z* V* x* L/ q( |$ T
import numpy as np! x, S" {/ L' w; t) Q0 @
from folium.plugins import HeatMap
0 p3 s! H6 b5 {+ h2 ^9 T& g A! v4 s; T3 h. l
# define the city map3 O& c3 b# y& q
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)4 \$ D7 W1 s0 @3 I& X9 Z
) ^" R; S- Z4 G$ W2 K& ]3 d # 构建随机数据
8 @) H- `1 [2 E data = (4 s7 }$ B5 n( s1 k
np.random.normal(6 y/ l1 \& a' \/ r" W
size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
$ p s9 ] @* D5 q4 {7 l2 H6 N: a np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
0 r2 r/ i' O5 t6 l8 ^ ).tolist()9 g5 ?4 R5 C0 K7 ?% h
0 j2 L' w& ^& s" B E4 d city_map.add_child(HeatMap(data=data))5 l# Y3 z' A0 O
city_map
+ \3 e% u- G% W/ ^' c0 H
/ a5 G. W& B2 F ?" n 除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。 9 {! S/ _" e: t, ~- m2 J' q. m! S4 e
import numpy as np4 U& t4 o7 t( I- m; U6 o) O" R
from folium.plugins import HeatMapWithTime
/ s& H! @+ E7 w6 L) m+ Y! d( O
H m& U; V4 k # define the city map3 T1 W5 q3 c7 Q
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)5 h" D+ \, g6 f, d3 H4 C# I
B$ u m7 V) N; i! V # 使用 numpy 建立初始数据1 M. ]" ~2 X _; H
initial_data = (np.random.normal(size=(200, 2)) *+ k7 A n* @9 U9 P4 ~
np.array([[0.02, 0.02]]) +
2 V' I8 Q" Y, h7 N3 X, R np.array([coordinate_orchard_road]))
2 _: Y' H F! o
# M+ p4 H' x8 R& ~2 L [1 Z # 建立连续的数据
+ S/ T: h: ^! Q- d% |5 d data = [initial_data.tolist()], e' }/ w0 e& ]* ] _
for i in range(20):6 s- {' I) M. K2 _& w
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())1 G# `7 K; ~9 d# r
0 P# M! @- ?9 E* `( q # 显示连续的热力图
$ I6 I$ B8 g- e, ^ city_map.add_child(HeatMapWithTime(data))7 |# D* n6 D5 M, s
city_map
% j# O& i) [$ ~# q. l8 u
1 U$ w# c0 ^0 Y! t 经纬度点的聚类
+ |. Z: O8 H3 C6 P" e5 T 除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
: a( K& n C; ?3 f from folium.plugins import MarkerCluster
9 @' W) V5 ?7 {7 ~6 s" p4 n0 Q3 K) S
- `! T- D. Y; b; a$ N # define the city map* C, E; d3 o2 j8 D7 q
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
8 ^7 v6 a: R. V; m% J0 S6 e; @9 M0 P# b( u. L5 Q& Q7 t! B
# 经纬度的聚类; U+ N4 N5 @3 ~! {/ _% s
# 在 NUS 的经纬度附近随机生成 100 个点;
2 i# a' N/ V7 A8 @8 N/ ?6 q8 S- ^ data = (; J) z1 k( H0 p
np.random.normal(size=(100, 2))! n9 ^9 o1 D- x
* np.array([[0.001, 0.001]]) +% d6 b% C) x& i* O" W& r) j
np.array([coordinate_nus])) I* e! w* A3 A1 ~
+ @& ~$ n" Z1 ^/ S1 S- \( |
# create a mark cluster object
4 Z6 L1 C6 \" }* N* l1 w' v marker_cluster = MarkerCluster().add_to(city_map)
0 F* N. A- M! T/ t/ e) d! {
4 Y% I. P' x8 }- G! D$ \7 `4 L- x # 将这些经纬度数据加入聚类6 Q+ Y$ i v$ w
for element in data:
9 A/ M2 |5 D9 \, P9 B$ U folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) G) G) |" S6 {* k- U/ `
# [# j. B7 W/ i1 _ # add marker_cluster to map r* Z- G/ ~4 Z- P# \
city_map.add_child(marker_cluster)
9 Z# z: k" N( W; e0 v* v# ]% ~$ p, c }; v
# 作图
! r0 E j. E" s city_map & ?) F* ?* G6 {+ k
5 e& J3 a& }4 i! \
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。 " P4 C/ t" G! R
参考文献$ V k' _. K+ a, L- q+ V/ c3 R" ^
Folium 官方文档:Folium - Folium 0.12.1 documentation
i: N2 f0 S; i) P" m
; G; H- ^3 l# s" K* S" [) K: o; D" ?# x' x% A
. g; }, y; m& s8 y' e. B. D. Z a0 v- u6 Y0 [5 D* H, ]
|