7 Z" m0 r. y. p& M% M7 v Folium 简介
" f: Y5 i6 U; H9 o# a A$ c 作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。 & G6 a q1 |" \9 j& P ]
创建地图
" k& s& o- w+ d( { k% u# `9 a 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。 " G+ Q: ]: n, d, p
import folium' i* l+ [# H" o! ~
%matplotlib inline
3 m& J2 q: i% R! Y+ U; F5 s0 O3 N& t
: V; C% P% K2 b( b. i' I4 x' }5 { import webbrowser' q7 r$ D, h1 _0 K- L
* _8 r5 a4 T$ c
print(folium.__version__)$ t4 P* }0 S+ H' w
I; N/ y! H4 ]" n
# define the world map" C7 s {" Y% y$ s; u
world_map = folium.Map()
# W; |$ k; `1 q" p2 H # display world map
: j: U i I1 a# ?9 k- d5 ?% ~ world_map) H" V5 S* z/ y: _9 c9 P
" X% v: z7 h3 ~6 O( o1 \
世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。 / W0 J7 b" }) s Y# b
在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
( Y, y9 }. T- q: g5 l$ \ 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
6 n/ v! [6 V' L2 ]( n M 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。 $ q5 x2 }" L/ ?) |2 Q
# latitude and longitude in Singapore city! E! D- [. y0 d+ a3 E! x
coordinate_sentosa = [1.248946, 103.834306]3 f J ]" s' ]* S' S3 b
coordinate_orchard_road = [1.304247, 103.833264]
|/ Y% X+ _7 D6 q/ ^. O coordinate_changi_airport = [1.357557, 103.98847]
. G, Y# ^. a7 W- V# O% z# ^ coordinate_nus = [1.296202,103.776899]
# y) X7 {& A# p. @4 N; x/ W4 d" K coordinate_ntu = [1.34841, 103.682933]
' a" N- P6 r3 d- D coordinate_zoo = [1.403717, 103.793974]
' i9 L- Q3 @, K+ Y k coordinate_ang_mo_kio = [1.37008, 103.849523]- P: u5 b9 X. M" }
coordinate_yi_shun = [1.429384, 103.835028]2 O6 s4 r* t1 G' P( Q% a
9 ]5 A2 W+ w+ h# H; `* b
# icon
+ L9 `+ O" \7 v% i) M icon_cloud = "cloud"
, p4 Y, A0 D; C2 X; F icon_sign = "info-sign"' [3 \" K5 F* g( r
- H1 {+ J; r1 O$ K- B # define the city map8 P7 o u ^; B/ j
# tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}3 e0 ^. c3 l* E
city_map = folium.Map(
: ^, E" y" y) l1 E location=coordinate_orchard_road,3 A& X- ?) T7 r2 }7 s9 U- z0 _& W
zoom_start=11," m! B1 U. R1 V( W) _- l* N& ?( Q& {
tiles=OpenStreetMap)" ~0 V2 X! O0 P% m* K# o) c+ y
2 Z) Z! V7 \+ a& k/ ]( U3 H/ Q7 C
# add marker in the city map! ]" n9 x; H |- I( F
folium.Marker(& f3 c* y9 i& }; ?+ v
coordinate_sentosa, `8 X1 J3 E* C8 H
icon=folium.Icon(color=blue)
8 T! r# \- k' _1 N# {4 N ).add_to(city_map)
0 {/ W/ ^$ }( w1 [, p folium.Marker(
, K& C6 d- Q5 a) M: J4 z" ~* D9 d coordinate_orchard_road,
* d1 t( Q4 O" o4 |# I icon=folium.Icon(color=green, icon=icon_cloud)% d2 l2 R/ k6 |) U6 e# L+ S
).add_to(city_map)
' U( h- m* w1 W, b/ p% f/ A+ B; o/ o" V$ O3 Y
# add popup* j; r: T+ {& B; @! @
folium.Marker(
6 s0 G+ Q) d1 M4 D coordinate_changi_airport,) z- g. c; H( J" B U) I! H& e
popup=Changi Airport,
5 g& D) `8 e8 e& w) P* U' G icon=folium.Icon(color=red, icon=icon_sign); `0 I7 F# k7 R3 P
).add_to(city_map)
: X1 ~% e9 s" @ J/ C& n8 \2 O% D4 Y1 x1 a' f( @- v! }) G
# add tooltips and popup
2 R3 g& _* W$ z' l% D( R9 y) x tooltip = "Click me!"8 ]( e( V! `2 I6 Y/ e1 s- F, S
4 u! E( g3 w* S' j+ F0 C
folium.Marker(
2 n0 p- |, ?- Q# v) L0 N coordinate_nus,# W0 o$ k; t. M d
popup="<i>National University of Singapore</i>",
' J2 r U0 f! J d tooltip=tooltip) z1 U, q& o' u5 _4 X- d
).add_to(city_map)2 V1 O7 f' ]/ E0 H, Q
$ W( O& ~0 f- u( z9 N folium.Marker(
5 B( d9 y* L: U coordinate_ntu,: K1 i3 V( J9 I9 @9 l
popup="<b>Nanyang Technological University</b>",
- Q, l. |' K7 T/ Z1 ? tooltip=tooltip8 M0 L0 P& }) J/ P5 d a( a
).add_to(city_map)
$ b! j3 f! ~ C' |
% S4 b* I9 g7 p # display city map* S T% F: Z- ^1 h/ s
city_map
8 Q' Z! \" B/ M8 d/ C- r Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
$ A0 \+ t4 l t # define the city map
$ I; x: R- @/ G2 A4 B* s! W city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
0 W$ Y9 e r' x( w& @1 Y: v4 E) ]% R& t4 n
# 在地图中添加经纬度, add latitude and longitude in the map when click& \! K# a g# V/ P+ T, j
city_map.add_child(folium.LatLngPopup())
7 |) |# i X# G6 F/ ^
2 c- ]& q. }. @ q7 K4 M city_map
* w/ y. [! ]( i9 d4 d3 A d2 O " c- G2 ^5 E. @# F: L: r5 h
几何形状) W! `1 ^- U/ g" L7 ?
在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。 ; Z" { A, J8 A
# define the city map2 d) R& v( ]" J F, b# {
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)( H5 V3 a/ [: d. ^$ B* t5 ^% @+ F
! K- H: r/ `6 F, F) P% q8 i
# 在地图中如何添加形状3 i/ q, }8 j/ i7 r [: U- `' U
# 多条边4 O: B5 u: }, }
points_1 = [3 s1 o, x" w6 | q% v! y) I
coordinate_ntu,
! F! H* r9 H! \5 _' ]. G( F coordinate_nus,
1 R6 ?+ W' f5 Q1 i coordinate_zoo; |4 V/ y2 |6 E1 {$ C9 I
]
0 N0 X) R7 J( {6 \0 \" i% u: x
3 N1 u- C8 E' I& F2 _7 p # 在 city_map 中添加多条边,第一种添加方式
% d9 B# u2 ?; a% \4 s% Q+ U+ H city_map.add_child(folium.PolyLine(
6 S) H, i/ |4 G8 A0 l+ B5 s locations=points_1, # 坐标列表& s4 a% w$ e, p) {5 D
weight=3, # 线条宽度
% H$ w* x! ~9 V% U/ d color=gray))
9 X: C8 e5 d3 C6 S) H: a. x/ u
0 p0 Z. {, K+ ~* x, D) P$ P1 [8 v- E # 在 city_map 中添加多条边,第二种添加方式
* c8 C5 h) n8 ~ folium.PolyLine(- p9 C/ _+ O. r8 S* c- j
locations=points_1, # 坐标列表
! }" _/ C% h- K" h( Q& f weight=3, # 线条宽度2 `- [& L# B& p; t4 Y0 `3 ?, k: c
color=gray).add_to(city_map)
/ c: c6 i- Q2 p9 t5 m; P
0 y7 X. F: ^& o' m2 t, L6 I # 多边形
6 e" c' ?6 b/ h: C points_2 = [
8 J8 \: f3 n4 H5 I' \5 s coordinate_orchard_road,# J$ ~: x! k- t( ~$ V5 a% N, X
coordinate_sentosa,
9 C6 d2 R" o+ P/ W! K coordinate_changi_airport
& H0 G- D P3 ~ ~ ]) p% M, O) S3 w! Y Q
+ z4 H: l4 p2 c! d; O% U. @ city_map.add_child(folium.Polygon(- E/ L; e- q4 s3 p
locations=points_2, # 坐标列表% K% d/ x% Z5 A3 K D# o
weight=3, # 线条宽度, t: K4 T- O7 B3 U5 y- p5 ^
color=yellow)) O" a, M& W: p3 j/ y) F9 c- }
; D$ p( S- S3 \1 R/ N$ l
# 矩形
4 T9 Y% |; b+ V' u$ K% M bounds = [
1 B& }3 J' p! a+ o2 B coordinate_ang_mo_kio,
: W' w2 i# w/ P1 S. t8 s2 x coordinate_yi_shun
" P+ S2 K) \% Q1 f. y ]1 y/ G1 v* o: K" ~! p
0 p+ V# B) W$ ]" q: q5 }+ y) m
city_map.add_child(folium.Rectangle(1 Q$ Q5 o8 L, ~. b; u6 L
bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
- P& l& R- F& r5 M: O( C weight=2, # 线条宽度
( G" U. S- X6 Q( R color=blue))
3 e9 O$ d+ G4 G9 |. ^$ g! N0 h2 a1 x$ X9 P5 K% S' @, y
# 圆形, circle, radius units meters* U: u: g! j+ n' W( t- v# L/ ]
folium.Circle(8 C. g8 y3 {6 B6 e; y
radius=1000,& w' n% d9 _4 s
location=coordinate_nus,, Y" G' N0 Q! T2 I8 A
popup="National University of Singapore",/ `% z V' o% Y! u" P. s @
color="crimson",
+ G, m2 Y' f* L- I3 X/ O fill=False,) K3 ^" x1 F& r. c* l. g
).add_to(city_map)0 n d6 z6 O/ O. X# Q1 x
! n, d5 }1 |3 ~3 B # 圆形, circle, radius units pixels
# @/ e4 ^* H% c5 R" O0 g# ] folium.CircleMarker(
9 ^) A5 h& r) `( D location=coordinate_ntu,
" b( @- A5 s. [/ Y) J- b* u2 V1 N9 b radius=30,
6 r- a* P3 F9 q2 d' ~1 R popup="Nanyang Technological University",
# H4 [. F) m9 y, M color="#3186cc",1 A+ ]' |% w5 {
fill=True,$ _6 e: _' j9 O- e- L* D, t
fill_opacity=0.3, # 透明度* M+ O0 v; j. S8 I9 H% W! k
fill_color="#3186cc",* m, z" h- x8 d$ ? b
).add_to(city_map)
% Y4 c1 o% l; ?; d1 w& ?+ j3 P$ n. e9 K! K6 z! b7 r% O
city_map
+ x- c. {" k. G' L$ B, w1 `, i( B Folium 中的画出各种形状热力图
S1 M/ p$ y+ v) h 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。 ( ?+ z6 P$ h4 e! m4 J
import numpy as np5 C2 q. g& b5 Y( D
from folium.plugins import HeatMap
* w& v) W+ ]* w1 W4 M, d/ D/ |$ D
, T* f& b9 q6 f" U( ]9 z # define the city map
5 m. }: o5 f: F8 e- }" T( n- U# G city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
* o. H2 L* H* f3 i' l
! M# B! A; j- B$ V* x6 K* ~. w # 构建随机数据0 W- A2 B4 b; F8 w! B
data = (
+ |5 j1 z2 E/ C$ ? np.random.normal(
[! C) }1 F. q5 Q; H; } size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
& B0 z! y9 L3 c3 c; g0 B np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])( W! j$ L% y7 }0 i& p
).tolist()
% ?( p8 ~ ~) |7 s
. i! P7 E# X# b U( ~- ]; p7 F city_map.add_child(HeatMap(data=data))
$ ?6 A9 c, y$ {. U# I city_map $ H7 z* {, l' E1 B2 T
" e6 d) z, _! T1 `
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。
) z2 A; ]8 C5 d9 v" V: _* O import numpy as np! v- Z: y1 N/ ]$ e$ k9 D+ b$ u
from folium.plugins import HeatMapWithTime' K# s# o& O% k' p) B
; H& ~$ k3 `# J8 R, B # define the city map5 @+ [ V( q" ]/ J3 R0 q
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)9 b; G& V' l$ F. ~( K' v5 ^
8 h8 T0 a6 Q6 W) h2 I # 使用 numpy 建立初始数据
3 Q% Z: z, A0 c, K1 T" G initial_data = (np.random.normal(size=(200, 2)) *& F& L9 |0 d/ K6 N9 L7 G$ C% N% E
np.array([[0.02, 0.02]]) +
! ]$ ?! V+ u# j1 @: B6 I+ c np.array([coordinate_orchard_road]))! o3 }! w+ `1 B* V
8 p E0 }6 w- x6 |% h
# 建立连续的数据 \2 S2 u% ?7 ?+ u' n. a+ f& ~" l
data = [initial_data.tolist()] S3 C1 O' ~ T* }+ P6 C1 b; \
for i in range(20):" Q5 @# W: l& h; S& B w
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())
& q$ o! A4 f/ C2 f2 u
8 p7 m" M8 o! F # 显示连续的热力图; C; L$ l/ @/ Q5 D& S; o4 _
city_map.add_child(HeatMapWithTime(data))1 D$ N2 E& q9 U" u7 z2 @
city_map
* \' ?/ R* I6 o
4 ]- N: U; z. a f( s0 T. M 经纬度点的聚类* C. l4 F4 t! }* x( i
除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
9 g1 H6 d |+ {9 q4 Q* z* j" C. x from folium.plugins import MarkerCluster
3 |$ e/ o" h( d) D* x7 N0 }7 C
# define the city map* _( A8 H }/ e0 k9 H( [9 Z
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
* Q8 b" F: b4 F3 w8 k
& g. `& t! G9 B5 V# O+ K # 经纬度的聚类! a- ?5 [2 p3 M3 W D& v
# 在 NUS 的经纬度附近随机生成 100 个点;
0 x e6 W5 A6 i, i0 I7 {/ _ data = (5 l* I( L. t6 Q; S
np.random.normal(size=(100, 2))4 ]- H! l; N/ P' V4 j1 I
* np.array([[0.001, 0.001]]) +- u7 o! t3 T% l
np.array([coordinate_nus]))* A6 S/ _( L! G6 }
, f" u; p1 C. s/ W # create a mark cluster object
* _" n; ], A# v+ Z! H1 | D marker_cluster = MarkerCluster().add_to(city_map)
. H0 A; {, t% L) _. F
# `# f5 u% Z$ V/ ^9 N! v3 e% J # 将这些经纬度数据加入聚类
0 U. n( R- L$ H for element in data:
9 D- a( P$ k+ N folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)- }5 b/ B4 @" u) O3 L/ y- c
+ H: K# r p9 ` # add marker_cluster to map0 Q/ q- s y/ {- s/ Q* W2 F
city_map.add_child(marker_cluster)
) H: S( i0 ^: n: h6 w: Q3 E
. T! C' q+ Y+ l% ~ # 作图# X! r' S& p+ D- L3 r Z9 ]5 k
city_map + c! C/ _1 H; E: b% q' |- ^
/ U3 r/ t4 w* D+ N9 P2 Q
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。 - {% b8 J. r# e( [
参考文献4 V; y1 f7 q5 _; J
Folium 官方文档:Folium - Folium 0.12.1 documentation 3 }) P! C% p5 b: H" ~% a
* i) s8 \4 Q3 C
# e- [! a/ u! w* B' g& g
; W) A2 W" E2 \" d4 c1 s( {( \6 N m/ `- q% u+ f7 l- C% _( ^
|