: V- q% a+ u& k1 j K6 n: \: Y Folium 简介/ ~( K( L: J1 n" d6 E6 M4 l
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
6 ~5 O _, E! a& c, O$ F 创建地图
& C# Y* F( {7 M! ?5 C 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。
; f T" E( x( `+ ] import folium
. S& E4 w9 S+ C. u %matplotlib inline
! E& _6 \1 r0 q5 R
: @; c' _: j$ n import webbrowser4 Z$ c8 E, M6 J+ c
8 Q. a, p( B/ }0 ?/ l% [( M print(folium.__version__)
5 r: g/ @- ?/ x) K2 z% C
8 u4 \' j( j# Y # define the world map: S, y. V$ T9 \
world_map = folium.Map()" j9 g7 [2 F+ T. }" M4 v
# display world map; p( s/ Y0 B! d) e' o2 U
world_map
" C5 @. N% O* B/ e( M3 E/ B & N' R8 P" R* c% {* P9 v
世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。 - ~* D+ _1 X, Y' B p5 [
在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
) ~1 G i: m/ e" Q1 Q2 _) N, |' q# S 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。 O& p! Q+ I. h! E1 b
另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。
; p- |; L# ]6 }! q # latitude and longitude in Singapore city
& k1 A/ o/ q/ ^- h X- [) I coordinate_sentosa = [1.248946, 103.834306]
9 h! x) O% y+ \ coordinate_orchard_road = [1.304247, 103.833264]% s# j! s5 q6 E5 W6 a4 o, `4 f$ W0 B
coordinate_changi_airport = [1.357557, 103.98847]
9 O9 T1 J2 n' f1 Q( n coordinate_nus = [1.296202,103.776899]6 u4 l9 f- e4 g, v/ Z5 ]! J8 i
coordinate_ntu = [1.34841, 103.682933]2 |* }4 ^" d0 |
coordinate_zoo = [1.403717, 103.793974]
! p( U% e4 {0 K, k) w6 T+ }' v coordinate_ang_mo_kio = [1.37008, 103.849523]
% y4 W( P4 u# c- |; Z. Y coordinate_yi_shun = [1.429384, 103.835028]
+ Z2 Y' z8 |7 \/ s8 I) \6 D
! @* m" L/ p' g' e/ V5 q8 S # icon
8 J, Q) i7 R) Q" a9 o icon_cloud = "cloud". p; Y! q' e/ T9 [6 c
icon_sign = "info-sign": I( D6 g1 w' O$ F& w, K8 w4 ^
& o' Q% ]. ]7 S4 x # define the city map
! O, E7 R/ m- c1 m# O* I # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}
4 I* K b! `8 t: @3 \ city_map = folium.Map(
9 X( I2 {- d0 {8 E% |4 i/ w5 r location=coordinate_orchard_road,) D) y1 ]1 j, `* w0 @
zoom_start=11, b( K% o0 `5 b4 h6 o
tiles=OpenStreetMap), w* M# f$ l: ?2 ^& J2 Z
: g1 e: ^# @3 m% |! x- P" ^ # add marker in the city map
9 u- G8 f: x% [7 z5 m folium.Marker(0 a& r6 l# \2 x2 T" m; o! d
coordinate_sentosa,; D. q8 ?% {; v6 ?
icon=folium.Icon(color=blue)
) E/ `, f6 Y q3 g) f4 i- S- a ).add_to(city_map). v: U+ {' \- S) G
folium.Marker(7 @! ^ X. q3 _4 [( F* D, f
coordinate_orchard_road,
1 n+ C/ W0 {7 m8 J& o9 D% o icon=folium.Icon(color=green, icon=icon_cloud)
! Q7 Z' c4 n7 m; Y: M3 p- E ).add_to(city_map)5 C8 ~2 e7 a" u, l; m# J# R
7 `/ P& Q& X5 T) _' V7 N5 Z # add popup
" j5 M4 Q2 ~6 w, x# e* b3 ?+ v folium.Marker(
: u6 A; ]! \* u( {$ S coordinate_changi_airport,
3 n# b+ z, u+ [ popup=Changi Airport,
, p# u% n/ T% C4 \ icon=folium.Icon(color=red, icon=icon_sign)
% A8 s% O, }7 V ).add_to(city_map)
. e. \4 H7 i6 C0 V: k8 R
* Y# X0 g: G. S2 J9 i& n' v # add tooltips and popup9 t, F$ F7 Q: w, u& h3 ]
tooltip = "Click me!"0 I8 y, v9 y% d; s
" s1 b3 l6 c3 @0 D
folium.Marker(4 f# |2 p/ ~# |7 M
coordinate_nus,
; p0 I: U& Q2 C0 e! {4 K popup="<i>National University of Singapore</i>",& s! r6 ]' f" g5 C0 x# B, Q
tooltip=tooltip d% R% X* _1 c7 u( c, L
).add_to(city_map)
0 Z9 Q( \# q: R) f; n' z
! T3 C( U9 Y* A- z, j% H7 _ folium.Marker(& U! s/ O) p8 y) F% ~. k6 L" J' E
coordinate_ntu,
1 m- r/ v7 X# w; P popup="<b>Nanyang Technological University</b>",& Q Q( a5 I6 D. L$ \
tooltip=tooltip, s/ `8 n4 U; q
).add_to(city_map)
0 h6 T' ?' V1 p4 c+ }+ Q$ |# V. o3 g
# display city map
4 u" J9 y- o. _( Q7 }! C8 N city_map
9 T( ^" C) t0 ~$ ]" H, g5 l6 u: W4 V; G Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。 ! f9 @9 M3 o1 C9 m2 L7 ]3 M$ X. W
# define the city map
- k" X) e# Y2 @8 N! S3 k city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)$ t6 S% w8 s: {1 O/ K" g, A
+ b, G/ i& h/ ^: y2 R # 在地图中添加经纬度, add latitude and longitude in the map when click
n+ Q E0 o0 l6 H city_map.add_child(folium.LatLngPopup())# h" t& b& Y1 _/ y D# X$ w5 |: F
9 t; |. s- F! x8 p8 K
city_map $ N7 X+ S) x- U h
; n3 u0 H! ^' F; j" v |' R0 i- L 几何形状7 o* R. a* g) |; p& y' k
在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。 " B8 t+ ? r Z7 E: t- v
# define the city map
% Z% k3 W F3 {( [( u- B city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
% U* s" f) c4 W O- g$ U
/ l3 K, u2 h+ [ # 在地图中如何添加形状
# u9 J- X5 S/ n' }1 O/ R2 C, Q1 { # 多条边
9 T: v! k0 n( u O points_1 = [
2 n+ t0 T8 f, p/ z3 [ coordinate_ntu,
% [" q! ~( O- N* A1 v6 O coordinate_nus,. v6 _% @. K3 l$ x% I$ M
coordinate_zoo
, D1 S! L/ y0 d ]7 R3 T H' N0 Z! N1 @% R
# j! h+ T2 S, ? O # 在 city_map 中添加多条边,第一种添加方式+ r0 M; C) A$ d& f( R" z/ y6 B0 c
city_map.add_child(folium.PolyLine(: R, y+ E4 |+ a; c& ]+ G
locations=points_1, # 坐标列表
1 P# b9 f; M% L7 G, @( W% A( h6 ^! f weight=3, # 线条宽度" d: T1 ~0 |7 ?; I" l4 Z- n3 E/ o% e
color=gray))
/ N: l8 u" }. z: {+ S
) L6 s% Q$ c6 U) G0 u( Y # 在 city_map 中添加多条边,第二种添加方式
3 \! d7 d2 |. R, R. S folium.PolyLine(
9 d4 [- m% W0 Q8 {4 o2 ^) P4 c locations=points_1, # 坐标列表
; r5 o. Z# {+ Z7 b+ L weight=3, # 线条宽度8 A* D. I& E3 {# O
color=gray).add_to(city_map): b& m; z9 b8 c4 m7 Z. e! p2 B
1 {5 C4 |: o2 L& p, Y G9 P # 多边形' Z, n5 e. |% ^
points_2 = [
9 b B/ z+ g" w/ I" o( S; F& h3 U coordinate_orchard_road,
6 J2 `0 C( h2 Z6 r; h! Y/ d coordinate_sentosa," v# {5 _* _5 l. p
coordinate_changi_airport# y% E o1 Z* U+ L1 }: ]: n
]) D4 J" o& ^" j, v+ y- V
. h! H+ ~7 B2 C) v0 o! h
city_map.add_child(folium.Polygon(# d9 ~) x- f$ _+ E1 p; A+ [9 y
locations=points_2, # 坐标列表
& t, Q& k2 r7 P. X( T weight=3, # 线条宽度. _' x, E6 H Z& W5 n
color=yellow))( I" K( s7 M8 F9 r X" ^8 M
) h* V& L4 u& l
# 矩形: r$ `6 ]! h" B: A) b, D9 F5 b
bounds = [
( U+ j# X8 K6 v; e8 H coordinate_ang_mo_kio,+ s+ t5 a1 U% k: L# y
coordinate_yi_shun
- S- `5 Q. T# }! X% C, a3 b ]7 E+ P" y6 a( b1 f& t) r
' a( Y# [& l; Z3 I$ {0 L' ]4 q* c
city_map.add_child(folium.Rectangle(( i8 h' N" a: O; d- }8 G1 F) y
bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)3 J4 B) i& S0 b- u2 q# R6 g
weight=2, # 线条宽度. V3 k) d: I# g9 W, B
color=blue))
9 E5 A5 u. \3 p6 P7 J% [# }
9 [, B' n4 M- l( ~ # 圆形, circle, radius units meters
4 X( d1 K F+ Z folium.Circle(
! I: h/ V( ]" W4 X9 U4 H radius=1000,
G4 q+ }: X0 X4 h T" A V, H0 C location=coordinate_nus, j$ h% {' S" V, b( H, x/ b: D" l
popup="National University of Singapore",4 q$ ]: d( d3 M: x' X
color="crimson",
1 a" q7 \( S2 a- ], d' k( C fill=False,
/ y! A4 k! |8 M: s/ i ).add_to(city_map)2 C4 s) ~5 |1 e$ K2 R# g6 j# X: X
, h1 ~" J- h$ W0 |- L # 圆形, circle, radius units pixels; H: ]" ~( I6 S4 Y5 e4 w9 a( M
folium.CircleMarker(
+ j' a0 y6 S9 ]; N/ S1 K* a location=coordinate_ntu,5 T# w n) {9 s- \
radius=30, S. l( v! C; ]8 ~# `+ K! ?
popup="Nanyang Technological University"," j9 y9 d! M! c
color="#3186cc",2 n& P# |! L: W8 e# ~; S
fill=True," N* R- n5 M8 G1 T( @/ m8 P
fill_opacity=0.3, # 透明度% [- ^0 n' L. Y9 Z7 H e, n
fill_color="#3186cc",
3 r8 v `8 F. T; h: }& y" e ).add_to(city_map)/ k/ W4 K6 A7 d! X' s4 n' c
1 C* W0 i9 ^( F/ ~) w8 ` city_map
, a8 Y+ O) o1 t4 C% P; \ Folium 中的画出各种形状热力图, e4 J: S+ J6 `6 Y. T% k
在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。 # J3 E( r+ x D. L% D7 | E& p, c
import numpy as np
7 Y' Z: |$ C: L! ? from folium.plugins import HeatMap
5 `5 D9 D' \3 }
/ \) G3 `% o+ @6 T, K0 N+ u% E # define the city map
0 n, E l& i2 d& Y5 f6 k city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
, ^3 d' l7 c q8 R4 J8 K6 B) w0 O
# 构建随机数据
) s8 e4 u2 ^3 M# U7 q data = (6 V& F7 R. a3 c; {! g
np.random.normal(
j8 E2 N. o; D6 T# t size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +5 v% i8 u! j$ J7 L) `7 B0 Z
np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])4 Z3 x. [8 j8 ^/ {
).tolist()9 Z( }8 m$ h' I
) V5 ^6 \) a( ~! }& f' C city_map.add_child(HeatMap(data=data))
7 E* v6 t5 i* _7 p, e% ~ city_map
+ q; K7 d2 B4 _; C & O! L0 H; j2 ]' T
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。 4 M( ?2 Z! v) A& S' x( L( G
import numpy as np
! s8 l! K3 y0 z9 H3 P! @$ z0 T" a from folium.plugins import HeatMapWithTime
7 p1 Q( q4 a: }* z3 D# |. I& I! U
! q0 t& r" `, _! w3 ]% [. G # define the city map
, [& ]" w4 v: | city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)' R {) T+ y( [8 ~- z6 T5 g, }% Y! G
) m# J j! \9 s/ o" ~' m+ h( u # 使用 numpy 建立初始数据9 j' b V K( K. q0 P: K) w
initial_data = (np.random.normal(size=(200, 2)) *6 J% y) p, \3 ]6 q0 d; ~
np.array([[0.02, 0.02]]) +
8 }6 w- c4 U4 L3 q4 A4 q* P np.array([coordinate_orchard_road]))" |) S" o; y6 _4 ?8 B
) s/ e' `2 S: k5 i- i% Y. P7 R
# 建立连续的数据
6 Q, J$ M3 Q# F8 X* q- q0 z: H1 f data = [initial_data.tolist()]
3 R$ n+ ?. N7 @) j& l5 l/ Q& ~& N for i in range(20):
* t& z$ w, N" [" b$ t data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())# ^! l0 k- h6 C
" v) y# L. W& @8 e4 q
# 显示连续的热力图
( I2 m7 M, M! x5 A z, J4 g city_map.add_child(HeatMapWithTime(data))3 P: Q2 T9 W+ {; j" d1 `
city_map 3 {; s! o2 t8 `& s' V% |8 N9 C" Z
7 Q( Y# Q7 _# b& t1 | 经纬度点的聚类
: ~) o8 y. R) P; f6 X% {- P8 j 除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
7 |. D- K4 Z( ^/ ]: z6 J( L2 \1 _ from folium.plugins import MarkerCluster' d3 h; |5 m2 l. ?0 ]$ j+ M
5 T9 i# w/ k$ a
# define the city map" L3 g# M7 F9 \
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)% K/ b$ T1 V9 V1 i5 l
, g# P2 Y2 m! J& L8 K9 c& l2 d
# 经纬度的聚类/ a c2 F3 v3 g5 S9 w( E
# 在 NUS 的经纬度附近随机生成 100 个点;
) r/ P+ @, f7 K4 t5 m2 Z6 Y; w data = (
3 ]/ z% w0 _; f5 c3 n/ j0 y1 R! [% h }4 J np.random.normal(size=(100, 2))& G1 a- D- `7 `& V9 @6 m
* np.array([[0.001, 0.001]]) +: m# T* k4 N+ D
np.array([coordinate_nus]))
: i8 W$ {+ Q) B. W5 k( m2 d1 w0 u- c1 L1 F% U
# create a mark cluster object3 b# P& L8 a1 c& H1 y0 Q* [
marker_cluster = MarkerCluster().add_to(city_map)
6 E' W" x, X. O7 H% v/ P" b: [$ j% R9 W0 U( T) w$ Q
# 将这些经纬度数据加入聚类
) |; Y' J! A0 e# u$ _ for element in data:
$ R0 b/ b4 G3 \ folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)" R+ C B4 \* E- M5 l) W2 _
( V: Z& g: i& _, R5 b6 }" m0 o
# add marker_cluster to map
- T& ]# W! @& z7 R city_map.add_child(marker_cluster)
2 j# n8 b2 E* s- b- h# H* i3 J' a) r& v4 E
# 作图) x3 J q6 r. x
city_map + C- P$ n4 F; P, i* W( m' H
" |% d$ M1 ^) [; A 以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。 # E1 a) B# U' @8 d
参考文献; A& B$ }/ U' b3 k$ u- C
Folium 官方文档:Folium - Folium 0.12.1 documentation
B" L+ { b" G( S; |1 h
. C6 a7 K9 L$ {) n- F+ l5 V$ V1 l3 ~7 Z0 n( p; b5 k$ P# E. q: x% X+ v
" I/ @7 o& H+ e: B$ s) g2 I1 [0 s
/ |# _, k' l# t. u |