# B3 G$ q1 \( `3 [ Folium 简介& }& H+ W& J/ z' H5 n! O
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
4 P# K, E6 C$ Z" s( _& W 创建地图6 `/ S" ]6 x' e/ z0 e, s
通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。 0 o9 @, W: \! o* f
import folium
& h1 P x+ z& B- g' U9 M %matplotlib inline/ X, m- ^& L! }7 n- j
# o; ^* ` x, e: ^$ I2 R5 A9 P import webbrowser7 n z1 W5 I3 q, o% l
2 X$ G% A" {3 Q9 W& |$ ~% b" j print(folium.__version__)
4 F; r5 v% ^" a* Y$ m2 s/ S: k# b% |: j: J2 w
# define the world map
/ D D8 P# a) B world_map = folium.Map()8 ?( N1 a8 `, g
# display world map, C. n8 t1 E( X$ _
world_map; L1 X2 \6 d6 [, J# t( ~# m4 h
; ?! x1 J& u) u: X
世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。 $ z8 @* M5 }% j
在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
) k8 H% H: p( }, D8 a$ a3 P1 M 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
, m; F# M; k8 |2 \# _3 _0 y 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。 1 k; l' U9 D" {; S
# latitude and longitude in Singapore city9 Y( ^( Z" W% {: c$ F
coordinate_sentosa = [1.248946, 103.834306]
% P" Z2 J6 ?2 W& B) A5 J* \ coordinate_orchard_road = [1.304247, 103.833264]4 D1 g0 o0 I; s1 B( T
coordinate_changi_airport = [1.357557, 103.98847]
) Y4 H6 k. K/ d k9 M( {, |- _) y coordinate_nus = [1.296202,103.776899]
* [/ X: r3 S4 Z$ E* { coordinate_ntu = [1.34841, 103.682933]* Y3 a* K& Z/ i9 Z' h
coordinate_zoo = [1.403717, 103.793974]9 o) p' t5 T1 c- d o- v7 I, y$ I
coordinate_ang_mo_kio = [1.37008, 103.849523]$ d% L" g, Q. c8 E/ D! b
coordinate_yi_shun = [1.429384, 103.835028]8 h0 R3 l2 L+ E, Q
/ i' X, x& G6 a) \: V! ~4 r( t: u- [
# icon
0 Y# g/ R% X, y icon_cloud = "cloud"6 _, ]7 }. `# j. X
icon_sign = "info-sign"+ P$ s8 s' @+ h) ^ w1 q; Y t
" ?3 @3 x* |. ]7 w5 j& X- V; H3 A: W
# define the city map, Z' T5 S4 H- p0 X( V0 K- ]$ R
# tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}" w2 A, z8 g: W
city_map = folium.Map(* V0 K7 y! G. d/ C9 h0 l
location=coordinate_orchard_road,
9 c5 t' F- ~# e& u4 U% o6 e# w; { zoom_start=11,
+ P s4 K; j! L3 }/ r tiles=OpenStreetMap)4 N7 F& u, F; y' G: W" ~
& F4 m9 {' R* V6 o3 p) e+ b
# add marker in the city map% o9 }! Z, Z6 [) C
folium.Marker($ k& J5 e. I0 \# v* I
coordinate_sentosa,
0 r( ]2 b4 q! t9 I, t+ H& N4 J icon=folium.Icon(color=blue)' l% F& s+ P; B7 v/ q
).add_to(city_map)
/ W% i! j6 L2 w: l: O folium.Marker(- s2 F# F8 r. N4 G$ E
coordinate_orchard_road,0 j$ i9 E; {& Q, J
icon=folium.Icon(color=green, icon=icon_cloud)( b, V* G* D$ ]6 @! S# @
).add_to(city_map)
6 ~5 \# g2 @0 U# \" M0 @, X H! R! u$ {- q9 [* b6 v
# add popup6 U4 n! q/ e" p) J4 O( L+ f
folium.Marker(
$ R: B5 k+ `( |% J coordinate_changi_airport,
5 Z3 k! a. `2 N6 x popup=Changi Airport,: l) \8 p8 A" M) {% k
icon=folium.Icon(color=red, icon=icon_sign)8 _* j6 N8 d' B" Z K( B* C6 Z
).add_to(city_map)7 B1 n: N% y( u- X" } s
! U1 l4 w4 [ V5 t' w3 W$ @, B
# add tooltips and popup
+ u8 V+ x4 B' T# z) d tooltip = "Click me!"2 M* X7 {/ f ~$ T% z3 h9 Z( L" F
! _- D7 |5 y" S# t X2 O
folium.Marker(3 t' h4 Z1 ]& f$ W5 N
coordinate_nus,; t9 O8 N" L6 |( j) f$ f% R
popup="<i>National University of Singapore</i>",- P) D u( G+ y- A0 T& K
tooltip=tooltip
8 q( ]7 ]2 N6 |% s, ] ).add_to(city_map): ?6 [1 {) f$ G) y. I4 E- ?
' [0 Z% y$ Y! D7 z) i! N
folium.Marker(
' @( S6 T$ j* d" `1 I coordinate_ntu," I7 x% W) Y* {
popup="<b>Nanyang Technological University</b>", u8 H; x1 ~: J6 }. Z7 ^1 n
tooltip=tooltip
% V1 J6 v) ^* @) j. w; H0 U ).add_to(city_map)
; ^3 ^' W' ]- c* c& ]. @8 [ o& H' M$ q! m0 o9 }' F
# display city map: T$ a" G$ P' G" e$ _* ^& h
city_map
- C" n5 Z( R/ `* o) { Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
8 _5 _& l, \6 c* j # define the city map( s$ F& d0 v2 ~5 E1 O* Z8 r
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)4 k3 J% @9 ?3 ?- W! h/ ~+ l% u
$ g" z0 t; h' d5 F4 o* R" R2 c3 a/ t # 在地图中添加经纬度, add latitude and longitude in the map when click
/ a _/ \& B! y. E z3 l7 L4 ^ city_map.add_child(folium.LatLngPopup())
2 ] Y: M8 l! A. q5 u& `7 T- b: B/ a4 y7 G- ]
city_map
, H8 e2 B( W. {$ ^; e
0 B- A( U8 \) U( d; J- ` 几何形状
5 u( Z6 _4 e1 U7 K( F; Q. {+ k* n 在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。
, Y1 Q# k' F# J; ?+ L- @4 M( y$ E6 t/ v # define the city map
+ C& q* O/ M3 p/ h8 c city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
, A; P0 ~: E7 l" V+ ~
3 {: L' N7 _2 a6 q& N # 在地图中如何添加形状8 J! H B- j, z; Z: J4 i2 T0 o4 m8 O
# 多条边& E& _- K- d- _3 N6 `: P
points_1 = [5 ^; e P6 a8 H! Y, ~) F5 C: d) c
coordinate_ntu,' u0 u1 I+ Z# d. P1 Y$ G% j. B: e# `
coordinate_nus,
# y( W2 z7 y7 C/ V coordinate_zoo
& h: n( z( n3 A8 K ]
4 F5 C7 b6 p* F5 g/ e' ~% C1 o# P, n) b ^ J1 |; I/ w. h
# 在 city_map 中添加多条边,第一种添加方式
; w6 ~- N5 H' A/ f2 A city_map.add_child(folium.PolyLine(
, `+ ^5 ~4 [- J4 S% v% U locations=points_1, # 坐标列表5 }/ A' n- B; P/ u. X# W* M3 W3 \
weight=3, # 线条宽度
5 ~; D& R6 w) a9 { color=gray))
( t; W3 y0 D0 _0 J3 J. \( B1 Q- E
, D- L, t H7 h; Z) R: U. K # 在 city_map 中添加多条边,第二种添加方式- q* T/ L: `" E P6 z
folium.PolyLine(
, M- s* B& B7 e) s) k locations=points_1, # 坐标列表 y& |! E' y+ P. @
weight=3, # 线条宽度
+ ^/ E- D1 @ I; S color=gray).add_to(city_map)( G* x3 i. [6 |( V! r5 Q2 R
4 H' @( g, n- Z# S" @2 ~5 d
# 多边形
& ^, m4 O& J. L! q' U. z+ A' A points_2 = [( E; |" Z% e+ @2 q
coordinate_orchard_road,
, K: T z8 G1 ?& I3 n coordinate_sentosa,7 g/ e% A- u" j/ S2 R8 v5 l
coordinate_changi_airport
& ^2 h0 l' e# ]/ C& z2 I2 g ]
" L) E' [; S4 c& [! |
) ]% z# j1 V ?3 u city_map.add_child(folium.Polygon(
6 f1 _( U& f+ G/ Q1 ? locations=points_2, # 坐标列表& N6 q- B- e+ h |) p8 ?. Z3 j
weight=3, # 线条宽度
1 x6 g8 x m0 [: c% S# ~ color=yellow))
/ H0 c t+ W# w$ ? `2 E1 |% ^$ }# ^
# 矩形3 K; V- n2 n) v$ @" f+ Z8 B
bounds = [
: `6 v! c4 x9 |' M9 K coordinate_ang_mo_kio,
( h+ K0 y! R, Y8 `4 r( ? coordinate_yi_shun# m7 e0 M- x/ E. e( K3 t
]
* @1 L, u2 g0 c( y4 {
+ h" v4 q" l, S7 F; F. Z city_map.add_child(folium.Rectangle(
+ i% u, V: t# L7 j+ m# z bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)6 ]7 [. M# U8 o& Z* w# f/ z! a
weight=2, # 线条宽度" V) O# o8 ^2 _ h) z; c6 `
color=blue)), j5 q6 r5 `, C! P9 b
" P6 j$ [& U+ g5 T # 圆形, circle, radius units meters& G$ r: Y, V: P: A7 m$ @
folium.Circle(
: t2 A4 P* b# ]7 g, ~ radius=1000,2 A9 `5 C0 j0 _4 p# l
location=coordinate_nus,- C* L% w/ h' n+ S" k* S, M
popup="National University of Singapore",
3 c, r# V6 ^( ?1 R' m# ~ color="crimson",4 u0 w/ T3 @; ~" r( Y. P: w
fill=False,
6 k6 G/ |- O3 J' E9 c ).add_to(city_map)
+ c: J: c! x" J* o. J+ W) h' ?$ i0 I( d0 `" o( }; X
# 圆形, circle, radius units pixels( y, Q9 u( t/ o& H# J/ b/ z8 r, _
folium.CircleMarker() T+ V$ x, v2 p5 c& c
location=coordinate_ntu,
/ o' L+ x2 g; V radius=30,, _ g8 W4 B7 Z- n5 Q; T$ Z
popup="Nanyang Technological University",/ T i' K0 E" Y! o% M
color="#3186cc",9 ~& s: G/ q2 H) \% H
fill=True,5 z' r6 o* H+ R0 }' f1 n
fill_opacity=0.3, # 透明度
2 j5 \/ @0 h- H( m1 H fill_color="#3186cc",
1 c8 q* x& G# d- t ).add_to(city_map)! o% p$ J- f% R3 K; s' a
+ k0 d* {- Q4 i; p$ T! V5 Z M/ K$ |
city_map ! k, R3 Q/ D3 y4 F. i
Folium 中的画出各种形状热力图! x% e5 `7 ~( }0 @6 W/ G4 C- z
在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。 : C$ H3 y* v7 @5 X7 _3 _% h2 j% T
import numpy as np
$ h8 D1 I5 c- N l3 h3 [: Z# O from folium.plugins import HeatMap
0 n2 x# |" m- G/ Y
5 n4 ?8 C+ [; |* w8 p # define the city map
5 }4 y& H2 s& i o' |/ U city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)& @8 M/ a @2 W% R7 `4 G9 A2 M; o1 @. P
9 k; E% `; x. y9 f& l0 V7 @ # 构建随机数据
6 s7 c3 V7 F3 Z H3 e2 s* I/ Q data = (0 X. M! }, c$ S! U" W& z6 K, i
np.random.normal(2 u+ E% S6 c2 D# n
size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
6 a! s. P& d: |& ` np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
2 V! ~- V/ M- |) z# G% t ).tolist()+ V4 r0 n# r+ ~/ F1 f7 v/ r
- z7 a3 | B2 Q+ U7 g/ f! c4 s
city_map.add_child(HeatMap(data=data))
! V1 r& d, v7 \8 {, ]0 Q city_map ! L' V! q4 A2 Q2 C9 x
4 j0 k$ |1 o$ U9 z k
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。 B. F$ f a8 _
import numpy as np
: b/ \* }& Q6 W' b* T from folium.plugins import HeatMapWithTime
7 Y& F& P4 ]) D3 H+ y- Q/ R! ?) A$ m
# define the city map
. Z( I1 }2 B4 |+ J1 x city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
: P" _' ?# \% e1 }" O- Y; X5 J6 h4 G
1 S a$ `+ z! X d9 T # 使用 numpy 建立初始数据
& C# ]5 `- b% e/ s; U9 Q( Y initial_data = (np.random.normal(size=(200, 2)) *
" |, o( }) `$ k/ g6 G5 ] np.array([[0.02, 0.02]]) +' d* z q& i2 X# g8 l2 l& h
np.array([coordinate_orchard_road]))
, b1 t, y) U% ?9 l" V; f9 _: u6 z8 U4 M
# 建立连续的数据) p. t. p$ U/ o1 M
data = [initial_data.tolist()]
; t9 E, k9 K O* Z4 Y# Q% C1 l& ] for i in range(20):
0 q: Z9 ?, z& v0 ~! V data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())1 n E$ p' M$ E% {4 @0 l) [
6 O. o9 x9 v# W% r. W+ V( @ # 显示连续的热力图; R# z! l) J0 L' N' }
city_map.add_child(HeatMapWithTime(data))9 n, j5 a& S9 t% Y, C
city_map
5 W) Y% v* v9 c7 z% H8 Y- | - M7 x! E- N6 F1 [" ^3 u+ s
经纬度点的聚类
$ u9 q/ w8 y+ S 除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。 , w& Q0 N+ l+ X$ u- B5 B
from folium.plugins import MarkerCluster* ]; `! H. Z5 d: L0 n
' C/ {# A2 s6 q2 I+ [1 A
# define the city map: g6 \4 N d4 b8 c7 U' D6 D$ I
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)% w8 t7 U3 h- W, |7 B# H
% r' n) @! I6 C! I. t( I3 {
# 经纬度的聚类* x& ~+ w$ Q6 G
# 在 NUS 的经纬度附近随机生成 100 个点;5 d9 k& n9 @+ |( K
data = (; c7 l# I% l, G! k. m
np.random.normal(size=(100, 2))
3 D& V1 E" m' w" V * np.array([[0.001, 0.001]]) +6 t" Z, h8 Z2 d" \& k, L9 W
np.array([coordinate_nus]))- j9 p! d2 W# M3 _
5 K: q: |' T- S9 c* H. q4 J
# create a mark cluster object: ~7 ]! {( g% B
marker_cluster = MarkerCluster().add_to(city_map)
8 J- B/ S1 u6 `
' V; h, [/ ?# X% b6 }& Y8 O* y # 将这些经纬度数据加入聚类
1 H- y1 i4 z4 _ for element in data:2 F7 z6 k4 U0 h0 k/ N7 A
folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)0 n0 ]7 G# `( \' K' ~
/ N8 f2 ^8 h N- S5 F # add marker_cluster to map
6 q! w* ~4 S y8 u( y city_map.add_child(marker_cluster)
1 z P% t- x8 H# v8 y+ x6 p
6 g( n5 n7 l* U. W$ q& A; Q8 b6 s # 作图6 ?, `5 I$ t! K1 L$ x6 \4 m2 N' b
city_map
. Y* U- `4 |: ?8 r2 Y
, y$ a& i3 d7 f* ~ 以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。 5 r. j6 t9 g1 @. w% y
参考文献& C7 Z0 l% b5 a5 [/ p8 L
Folium 官方文档:Folium - Folium 0.12.1 documentation . o+ ?8 A8 Q+ o9 U- [
! t. t9 C6 A2 ]- ^
* R7 H" k0 P0 I, @( O1 |' U' X" d; ` D1 E/ {' f0 F( W
/ t" T! R) X2 e: L. R
|