! p& z. N {7 ] Folium 简介
) O1 @% G6 `9 k& h4 Q 作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
/ j3 Z9 e V; ^8 k 创建地图
3 v7 X' U; J- X 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。
% ~4 s$ h. ^9 ^3 k& m import folium6 B) D' G! ~+ j# u
%matplotlib inline
1 n+ j. ]8 I# _7 d+ s
6 u1 b, y$ P0 J( q# N. A import webbrowser
: B; S- F1 z$ `* r0 u) V) e$ ^0 x
print(folium.__version__); F4 Z! X+ U1 E! N) q, r, }
1 T# T+ X* J, M
# define the world map
0 y r$ ^" q' {+ s. V9 y; u$ ~ world_map = folium.Map()
: S- E3 Z# x4 ~6 e7 R3 B0 v6 I # display world map8 u& B5 o' N- i& y0 a: p
world_map# i( p, E) q$ B3 Y
1 {; l/ x9 q2 G6 \4 X$ t
世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。 ! G( ^ W$ `4 I) u( Y2 j
在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。 $ }/ H' l# x$ k% a6 T% d; i: Q$ R
有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。 ; F' T, a# `( T \( ^
另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。
3 X C* D4 e: u, U0 a # latitude and longitude in Singapore city
+ h& Q# G9 |. \1 y+ ~1 s6 i6 o5 V' s coordinate_sentosa = [1.248946, 103.834306]
7 X+ ~6 E- P. n" z; _4 s coordinate_orchard_road = [1.304247, 103.833264]5 B, h9 M! F9 w: D
coordinate_changi_airport = [1.357557, 103.98847]
( A2 G/ d# T+ E coordinate_nus = [1.296202,103.776899]
' w- U A1 V# V; c+ R/ y3 c( N coordinate_ntu = [1.34841, 103.682933]! B$ n4 R1 N8 K
coordinate_zoo = [1.403717, 103.793974]
! [8 O$ Z( z0 P( E3 w coordinate_ang_mo_kio = [1.37008, 103.849523]
8 a9 u2 j( d E# |( J0 s coordinate_yi_shun = [1.429384, 103.835028]& J# Y3 o# a9 \$ A1 c
' L( |! Y+ G" w# ^6 R3 }' r
# icon
' m [! P+ ? B icon_cloud = "cloud"
% a4 [( s' T) |7 T3 P5 a8 L icon_sign = "info-sign"
4 u n! T! e4 B' c# z& X, w( g& f. C4 M0 `) L
# define the city map
& H$ o' g$ c$ |0 ?# d # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}
* L/ }6 i! p4 q% v7 X+ @ city_map = folium.Map(1 x* s6 g$ \% `% B1 S8 s
location=coordinate_orchard_road, i8 U3 g; b0 n: z h) U+ y s
zoom_start=11,: @* @) f% X8 ]1 Q _# d
tiles=OpenStreetMap)" w) z- H# x- x' ^: e) d
/ R/ W+ p2 F0 X5 e4 Y
# add marker in the city map
( ?- u; v' r- y. k) v folium.Marker(! x' L$ b6 T: u2 B( p- {- a5 J
coordinate_sentosa,
, e& W- z" a# c' h1 N- P icon=folium.Icon(color=blue)$ d" n) ]6 }. z7 q
).add_to(city_map)
% Q4 h$ ^* }, D folium.Marker(9 C' m! U4 i8 i9 H
coordinate_orchard_road,4 S' \! A* S( |; G, V; v3 ^
icon=folium.Icon(color=green, icon=icon_cloud)" U. b/ R; N4 B& K0 l
).add_to(city_map)
, s3 s( K1 l) E
& O, f* S6 Z' L$ P3 @2 T # add popup2 f( S$ k& r. @ Y& O0 ?& n9 [3 Z
folium.Marker() |& c5 j7 j) [* S, b
coordinate_changi_airport,( M4 g1 z: @ s; Y- c+ `! r
popup=Changi Airport,
3 c+ j o) x; Y1 Y: l icon=folium.Icon(color=red, icon=icon_sign)( ^- v, f0 P% M1 M& \8 ^, y
).add_to(city_map)/ Q. m7 E) H, R7 I
- x- y# U# j# @# c' x: z/ c
# add tooltips and popup
0 H% E/ R" ~: \$ W5 D tooltip = "Click me!"
, c6 t" _4 I* D) c; k$ S. \$ T) S# @0 }( Y: L/ k
folium.Marker(
5 s+ R0 v w" w& ~& e- R8 m coordinate_nus,7 g3 `$ a7 u! _2 x
popup="<i>National University of Singapore</i>",( v5 m; n4 t) }/ E
tooltip=tooltip# O- Y" S9 g9 q7 P q
).add_to(city_map)/ k) I9 f; B2 z
i; \) o4 ?# @$ x( h. T( b- K
folium.Marker(
6 P" z% J+ B- O$ F, _' ]# @; S coordinate_ntu,
" J( w* W) V* [' Z9 B popup="<b>Nanyang Technological University</b>",* G' i# M' h6 e6 r3 Q
tooltip=tooltip
3 J1 `. w) G: S' H! f4 g( h ).add_to(city_map)* E5 y4 ?0 H) m9 [9 `7 }; U
$ `: i! [5 _* V
# display city map
+ g' d0 V, j4 Q% d% m' ] city_map
1 h: F7 G) Q' `. a Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
& K' Q/ T& }9 D' K% i/ C # define the city map0 q" C' d# u+ g1 B& R
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)" U5 S8 p* n1 O$ Y8 y1 s
- X/ N2 r0 K* h P# k" Q" s# c4 s # 在地图中添加经纬度, add latitude and longitude in the map when click
' a" @; W0 X: y' M9 }# H- ?* \9 D city_map.add_child(folium.LatLngPopup())1 a/ ?# Q x4 D
" V+ }' U; s2 g. `6 I7 _- o city_map 3 p6 d3 {' d: Z G9 g: X) W
) ^$ }0 A! A* X2 j
几何形状
: \% ]# ~# z( }2 ~+ Z/ K 在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。
/ B* v6 k9 B7 m, E! u( H2 E; g # define the city map H0 a y5 u/ n) r9 ^
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
5 }2 P. v( A' C; k
7 l$ b5 @( t/ v# ? # 在地图中如何添加形状: N o7 ? M! I- z
# 多条边" C$ H$ M, q* G3 O! ?
points_1 = [
9 c- [% M" q1 c- H& u) V coordinate_ntu,; Y" P0 X* n: Q$ t9 c7 _: |! C0 i
coordinate_nus,
3 e, r& G4 y2 w% \: _# } coordinate_zoo! G& v9 h; k* r; p$ c# k& S8 M* j
]7 f8 ~, b5 i( b4 Q* \
% }" A( t! ?: n6 d% [; i4 b
# 在 city_map 中添加多条边,第一种添加方式
6 O4 q* q' V# N9 Z4 y/ l4 O city_map.add_child(folium.PolyLine(
1 k! f5 A+ p( O8 W locations=points_1, # 坐标列表
' x2 J9 V4 p; S0 M8 o weight=3, # 线条宽度. L$ X- P6 U6 \
color=gray))/ p. q" U! o6 q9 B1 g
1 I+ @( b% e& O$ K* z% c t1 v( E # 在 city_map 中添加多条边,第二种添加方式0 c, F$ n! [& { o3 b: D$ E0 y
folium.PolyLine(: K; m# ~# ?) c
locations=points_1, # 坐标列表
. W( J5 s( t( H/ w @% c6 `6 c# b weight=3, # 线条宽度
. B6 x" f; Q( @ color=gray).add_to(city_map)" j/ I o$ a8 }* V4 {; o
' K, R$ `4 ?4 j( S9 e
# 多边形
) T6 B% f F) C, M: G% Y& l i" N, g points_2 = [( y3 _# V# l5 V3 T
coordinate_orchard_road,( m i1 B- ?$ A( V) N
coordinate_sentosa,1 E2 [8 z0 _( B2 z0 N
coordinate_changi_airport+ {8 e& I" H5 o- H$ K
]8 n7 E3 Y- e( B
! b1 ^+ I! q" S2 P$ u/ l city_map.add_child(folium.Polygon(
6 K/ C' k. {) f5 ~3 B0 I! ~ locations=points_2, # 坐标列表; p1 o8 a4 [5 k7 t. b
weight=3, # 线条宽度
" v0 ^2 W2 o" L1 X1 g3 @9 x* b6 V$ k+ S$ e color=yellow))( n1 i1 O1 y1 E# s/ G3 R
2 G" X' r$ q: j8 r# v
# 矩形, C% [& {6 w- M7 N1 T1 O' a
bounds = [
- i* N1 F7 r6 E' q) r4 t coordinate_ang_mo_kio,
/ {, g4 }1 b# B0 ?! f coordinate_yi_shun
: _ p4 M! ]3 u9 N ]. H" k0 f0 B" }6 N% T
- n( f/ x. }& M) \% F U" C
city_map.add_child(folium.Rectangle(
& r1 C3 q0 D. x5 R, v bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
; V6 ?4 }2 f% |. L weight=2, # 线条宽度
, {8 ?1 M; T2 O. S9 \& x( G color=blue))0 Q* f1 E7 i/ b) j! i4 |
" W) L. H7 g- e% ]& d
# 圆形, circle, radius units meters* L( G+ X; [7 ~4 k+ y5 t
folium.Circle(4 C5 ?; `- x4 y3 j' W8 x+ q' z
radius=1000,
9 r! Q1 U" f5 f/ O& \ g& ]/ [ location=coordinate_nus,2 g# O+ y1 w F
popup="National University of Singapore",
; E! y5 k- t& l5 r7 _( [; R color="crimson",0 M8 j- A6 j% E2 M0 M, K7 w a. r
fill=False,
* K: H: C8 j& [, O1 d# S3 X- V" ] ).add_to(city_map): P$ G3 j7 j- S, w: l, }
! c) g8 J1 K: m, o2 K) S
# 圆形, circle, radius units pixels
% B* a! [0 T \% D' |) B folium.CircleMarker(
! H. ^' p- [4 H$ u5 X: L location=coordinate_ntu,$ ~4 X- q9 g4 T: G6 h
radius=30,
4 s3 ^: `. X: a( g8 ^/ ? popup="Nanyang Technological University",. }9 X- i" J" m: e" h0 y' H
color="#3186cc",3 P7 n! l- U" d9 U a
fill=True,
2 S% Z/ `9 ]/ m t2 Q fill_opacity=0.3, # 透明度
" s, K, t$ N# z5 s ]: | fill_color="#3186cc",
: X+ J% g( s3 c9 x: ]3 ` ).add_to(city_map)
( }! L. w4 s/ D, i6 K& u; f
4 H0 H+ _/ f9 s* H6 @: D1 e city_map
& t' C" T$ B# M( u Folium 中的画出各种形状热力图
7 O0 Z5 U/ t9 l$ I# m1 Y- O 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。 u; m$ b8 K: l* ]9 C) D6 g
import numpy as np9 O \- ` N4 T- m1 k( B
from folium.plugins import HeatMap8 S+ ?3 S0 x9 p6 H+ i" i, F7 F
5 V- ~# `& _5 ~
# define the city map+ J e% o0 r5 T+ T# H
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)8 ]: a0 G# J# d! Z. ~
G$ Y8 A, m( r& G! ?
# 构建随机数据
/ W' a P* T# K2 V. [- ]# z7 n; Y+ I data = (. s6 w$ ^8 t$ `/ V$ E
np.random.normal(
* a$ q7 \. E2 }, [8 J) C$ h1 v; J size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
. E/ C0 O h8 X# f+ b+ | np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])! c/ H8 J* |+ @/ W* ?1 D& l U _
).tolist()7 ?# E: U9 N g/ E6 F8 D% Z- v$ G; l& b
: L1 s1 R$ v1 b* h' x
city_map.add_child(HeatMap(data=data))
4 B* X0 C# x& |" }7 y& G0 j6 w city_map
2 S! M5 t/ t+ Y. J
! j( Y& x+ R& ], i: z0 q 除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。
3 H9 g& H4 P) |% V" I$ l9 h+ m0 p import numpy as np+ w6 J6 V( N% o, g# D0 v a' L
from folium.plugins import HeatMapWithTime
6 L7 M: o* I8 ~
9 d5 R% F' o4 E* a: s9 i # define the city map1 \6 t' e0 A7 e A: G& w
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
' V, J9 \' n/ _- X# B3 B( }+ S. g
# 使用 numpy 建立初始数据5 u3 b/ M8 D% Z1 n% e( d2 n
initial_data = (np.random.normal(size=(200, 2)) *3 C8 _* K# b \ m; D1 } A& [
np.array([[0.02, 0.02]]) +
4 h$ J" E! P R/ O+ g( J' J$ O np.array([coordinate_orchard_road]))
( p' W4 Z: @* @2 @* z0 N x6 ?( L
# 建立连续的数据% q) }( i6 G, e' P( m! ?
data = [initial_data.tolist()]6 x. _( @4 u1 A6 t) X
for i in range(20):
, ?' C" }8 F5 H3 v5 } data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())
' T- Y' p8 b8 l3 H
5 p. D2 c( ]% \# |3 C4 r- ` # 显示连续的热力图6 P) `+ |& f& J# f. d7 Q
city_map.add_child(HeatMapWithTime(data))1 W! d& W# r2 r2 b$ y% T
city_map 7 R5 G% }: ^1 I2 Q$ Y0 _3 v
" ?& \7 [; \) Y% ^$ i6 d2 Q+ U 经纬度点的聚类
+ \& s' ]- f1 c U' L2 F2 o. S- V 除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。 " _! m' w2 i+ J0 d3 X
from folium.plugins import MarkerCluster& r) x4 S+ [4 L- L1 {8 B# H
* L9 {% s4 E( a; {4 F/ C1 M# i
# define the city map2 K* L8 c, O2 C* A( I) _
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
* G* }% `* E/ n0 q! ~/ v
6 |% g; ^: f4 b' a # 经纬度的聚类3 g0 d2 @' I% C* k
# 在 NUS 的经纬度附近随机生成 100 个点;% @2 n7 |, k1 m* q' U
data = (* b/ B7 ]- L3 m2 Q* B' d& j8 [" g
np.random.normal(size=(100, 2))
) @- _+ v2 | S. N% P! L * np.array([[0.001, 0.001]]) +, K3 q8 E! V$ G6 ^
np.array([coordinate_nus]))
) R& R" o4 H/ @: k
+ [5 a( E7 n' X8 ]/ j7 r' e # create a mark cluster object; y& _% C0 N9 z) R9 d8 ?! u
marker_cluster = MarkerCluster().add_to(city_map): x1 Z3 d- q' |8 w1 }6 ~5 A
- N9 q- {# Y. w5 Z _! G# L& R
# 将这些经纬度数据加入聚类 p9 X4 d! j5 E: _6 t4 g, I
for element in data:
1 f7 b k: _9 j folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)
! R: t% }: Z, \6 ]1 g4 R( R) d
, p$ f; T4 T6 J! r5 Z! G6 b: ? # add marker_cluster to map [$ H3 A' C% x: C9 O+ x U1 {/ N
city_map.add_child(marker_cluster)
4 x: V* l+ Z" C! i8 G- H
% r% k' T8 P/ t1 s # 作图
, u5 T0 ` V- Z$ `' \8 E8 ] city_map
6 t/ n3 t3 g' s6 K4 ]$ e! G / Y4 \9 @- O7 j# x
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。
# D& s$ P ?: z! K# @% ` 参考文献8 F/ O2 Y" n+ ~2 d* I
Folium 官方文档:Folium - Folium 0.12.1 documentation ! r- O1 ?# g$ G& W0 a! `0 J
' b% f) v# h1 W7 w4 [1 K% I9 ]6 p" Z6 k; x
( y: S# ?* I6 n( u M. W$ r
% W# G' y( ` ] |