?! w; m" b6 \* S2 T m( q Folium 简介. Y' N' M) L2 r) X6 \1 m( k
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
4 d# ^* }) s+ v5 H 创建地图
9 U1 q5 O/ Z# j' j1 w( J 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。 . B1 k I/ U, V' T" O6 o
import folium
( f- Z. m- P& q% ] %matplotlib inline
H. y. G8 l0 k! Q! C- z
! Y! o- c0 H2 p. K7 O9 O import webbrowser
& G( a! S0 l ~4 H# m" T! O+ \7 `) I9 {2 p1 q. Y( |
print(folium.__version__)
' K( U$ q( x( M, J# |" }' @5 a
; z" q7 f+ \( h5 n+ b # define the world map& i, o+ d) v) s: h' i" K
world_map = folium.Map()$ I. O7 M: c {
# display world map2 m- F0 `: A5 l3 s8 B1 g
world_map
2 _- X2 F$ y8 C8 q! q9 A- ?1 h4 }+ U - F6 Z. N8 U) f- Q* Y7 k' ~. M# Z
世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。
8 y" a; _1 D1 s9 z& v4 }& Q 在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
5 `! L( G# G3 w6 I7 ] 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
a6 v2 N0 l7 @" D5 Y 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。 & t$ ]. o: s+ I9 s, a0 P
# latitude and longitude in Singapore city; f/ h) @" p+ C, \
coordinate_sentosa = [1.248946, 103.834306], \' Y# ~4 o/ t2 ?
coordinate_orchard_road = [1.304247, 103.833264]& Z' r ^+ X+ E" P2 q, R. p: z
coordinate_changi_airport = [1.357557, 103.98847]3 B+ `% A! _2 N* ]' a! @
coordinate_nus = [1.296202,103.776899]
3 ]5 q& q0 ?) s$ L: Y9 f5 j6 m coordinate_ntu = [1.34841, 103.682933]4 P' W& h5 ]4 R! a/ i3 \
coordinate_zoo = [1.403717, 103.793974]' z* D2 r# z/ r. g I5 X+ d6 B
coordinate_ang_mo_kio = [1.37008, 103.849523]4 E3 I, S- ]5 h, u
coordinate_yi_shun = [1.429384, 103.835028]" Y) X/ \/ ?* i; e3 K' a) u
; ?* H8 k2 h9 T5 [. [+ F j# n$ L # icon+ a, c9 ^' G0 V! J2 d
icon_cloud = "cloud", z) y% w: W- D1 E# j6 f8 @
icon_sign = "info-sign"$ T v) g% O& }" T" M! n9 D
8 B. M2 b. W! P! x8 I
# define the city map8 v3 ?% F! Y V6 P+ n. p7 b
# tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}. f& K3 w \1 P( X L6 f5 O
city_map = folium.Map(' I- K# E6 J% I# g- {- J
location=coordinate_orchard_road,
" d% i) m* Y+ r/ G3 w& n8 k) X5 d zoom_start=11,
2 }3 @* `' v9 S% u$ @ tiles=OpenStreetMap)
6 X; l. `# D4 w6 v' N4 o0 {* t% i9 W
# add marker in the city map
* w# C* E( X+ w% @2 Q folium.Marker(
1 h2 {- s- i7 ]$ H/ [" k coordinate_sentosa,
, [- g+ a( y9 X( h icon=folium.Icon(color=blue)
- e) X2 N& X- t0 n8 W ).add_to(city_map)
6 z. N1 E5 E T% N folium.Marker($ ~1 ?8 w+ k' G$ H6 t4 k" o0 o' N
coordinate_orchard_road,5 z/ E. a4 U; T/ h9 a. c
icon=folium.Icon(color=green, icon=icon_cloud)0 y9 x% U. ?5 W; k% a8 z
).add_to(city_map)7 Q# d! x2 D7 V0 R. {$ G
' S1 H& n, ]5 j; N0 l$ ] # add popup
. P8 d5 `$ d, O folium.Marker(/ Q T# |) @% S$ r% [8 n
coordinate_changi_airport,; B b% b9 f: ^$ r1 H( {
popup=Changi Airport, j, \1 R: O. }% m* A1 Q# E- G: b
icon=folium.Icon(color=red, icon=icon_sign)
9 c* Y x2 z" t. ~/ Q7 v; R& a ).add_to(city_map)/ B1 e0 M, `7 f9 v' c) |& \! g8 K
# c! R, b* a1 F+ S
# add tooltips and popup
/ e# |- R2 O! J( i& B4 X1 r; e tooltip = "Click me!"
& w; E4 }, z0 n) }! O6 M7 _! Q- m9 H5 X* E) u5 c
folium.Marker(
; E) b( {- g9 f* [) m8 P- Q coordinate_nus,/ A+ [- j, W! j0 r6 J# b& O
popup="<i>National University of Singapore</i>",4 P* M. X* U& _, \. z& ~
tooltip=tooltip
- B8 _) J/ e# A9 O% {9 R5 X7 m ).add_to(city_map)
- X4 Y6 |* {& V1 w1 c
; U$ }1 i4 p8 {$ d# H: M p folium.Marker(
2 a4 o7 f5 A2 V+ w9 g coordinate_ntu,* b6 b0 n+ y& m5 v6 J
popup="<b>Nanyang Technological University</b>",
0 a s' D7 e& T: |% |: a tooltip=tooltip2 ^) j- I4 y8 A7 ^
).add_to(city_map)
4 T8 ~2 ^; \0 ^) H# Q/ |1 F, f
: i. U" V/ d4 }3 t; k # display city map
8 B# w" }6 L9 r. E city_map % s+ ^9 n) S: l& t$ r; \* l
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
. |3 a0 }/ z6 p5 P # define the city map
+ i+ H \) |7 A5 o' S city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)% L. D$ v9 z: f6 A& S6 d7 ]
2 h6 l4 D) i* s6 ^ ^# Y # 在地图中添加经纬度, add latitude and longitude in the map when click$ ?. t& e% A$ J8 W% a A( m
city_map.add_child(folium.LatLngPopup())! S+ x$ b* Z8 Z" I
: W# E% I5 F1 M ]2 l. X1 E city_map 4 P, d; n/ ^* }# D, G6 A
, g& e: q. y. N" g
几何形状/ }, {, Q1 |. @' ~: u' f" l
在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。
A8 U0 r F9 ` # define the city map5 ^" P' k$ @ H/ _2 B4 h- W
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
3 I3 K8 `" ~0 X. f1 K4 F/ W, E2 U4 b& b3 `/ r) A% q! f5 ~
# 在地图中如何添加形状
) b$ a; K6 P4 [ Q) j0 A; A # 多条边
3 w4 v8 D/ z2 {1 h3 `6 t points_1 = [
' e5 E$ ]1 W. D1 b4 { coordinate_ntu,
* |( F3 e6 o- g+ W2 ` m* g coordinate_nus,& O E9 Y+ x4 Z1 ]3 [. t9 @" w* g
coordinate_zoo L1 j1 b. L, r% G5 D4 M5 z) B: G
]" P- G4 ^* s& M) U
& G l$ p" ]8 ~. Z, `" ^/ c # 在 city_map 中添加多条边,第一种添加方式
& X0 E7 E0 T+ e/ o' T( D city_map.add_child(folium.PolyLine(
5 ?/ v7 Q! h2 v$ {" i) P locations=points_1, # 坐标列表& w z% Q, d* D$ h" L) F" U
weight=3, # 线条宽度1 H6 U; N ~8 |
color=gray))! G; d& p7 O' |" e( m+ e
2 G3 W, _& i( J: |! M; G, [ # 在 city_map 中添加多条边,第二种添加方式
3 ^4 q: z: T H$ V. c folium.PolyLine(
. }" v" L0 B) {% p" V3 n locations=points_1, # 坐标列表3 r- u7 i+ \. ~+ y% p) l. K
weight=3, # 线条宽度- ?' v: A# ~4 x6 S. j, C; @
color=gray).add_to(city_map)
T1 x$ z* F, B1 T5 h7 S# u4 [! ]2 W8 N9 T; b t
# 多边形
! I2 I/ i/ @7 t3 H0 F+ ` O points_2 = [
o; J7 s4 u9 h k% ^ coordinate_orchard_road,
0 y' N1 G+ A6 B2 B8 a8 E' [7 N7 h coordinate_sentosa,/ p- N# S* K* k1 a& \
coordinate_changi_airport$ c$ C* Y. N4 b' ]
]
/ D% W1 p* B+ Y' k% t ?3 b) Q! f7 P6 j
city_map.add_child(folium.Polygon(6 f3 m6 { R; p% e3 B2 {6 y# z
locations=points_2, # 坐标列表
) D+ }) m& d8 _/ `/ i e1 G weight=3, # 线条宽度6 h; m, r. L H3 C' c: Y
color=yellow))
2 }0 L7 z5 n0 t3 B( E' _4 z; [
" I8 H0 d+ J6 B" \0 } # 矩形
, w+ d, h5 p% B bounds = [
# K, u% ^# P- J; K coordinate_ang_mo_kio,- o" F" k/ ^9 q1 u* b4 v
coordinate_yi_shun( R6 n. O/ H* C6 H2 F
]7 O# `3 ]: T* d
; V4 G- ^; Z2 Q! j. p; x city_map.add_child(folium.Rectangle(' p' N( E4 }2 Q6 @7 ~* ~' C
bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
7 ^( R6 [$ R" c$ [' j/ u6 Z weight=2, # 线条宽度+ g: X- _' d/ ^% d$ ]6 |6 c
color=blue))& M L9 t, K/ \* t" O s
' C+ c- ?4 o5 Y1 _! V& u; N5 P0 i) E
# 圆形, circle, radius units meters
( Q. T: ?& L$ V/ e! n% Z folium.Circle(6 R/ e! d" I/ e J7 E- k/ k
radius=1000,9 r& ]3 L3 U% E$ S# U, ?
location=coordinate_nus,3 A4 X3 `* B' }# i
popup="National University of Singapore",. M: ~7 I4 \8 t i: e+ p8 O
color="crimson",' ] ~1 I$ L0 c
fill=False,5 m6 I; y6 j$ _% ~0 P9 h! y/ E
).add_to(city_map)6 s9 Q" Y [* \% H* ^7 q4 Q
3 F5 S7 a! {$ n # 圆形, circle, radius units pixels! `8 |" L" \7 |( w2 U
folium.CircleMarker(
7 N: j1 S9 a! E' g2 P. a$ E location=coordinate_ntu,
6 ~/ H5 y# x8 U b5 m, A# r radius=30,% y/ \1 w' q# u* I- c
popup="Nanyang Technological University",
% p2 n# y$ k+ l/ o color="#3186cc",
& ^+ p9 E# w5 a, d! N fill=True,
' H3 F8 R( g& i6 {4 r) H6 Y fill_opacity=0.3, # 透明度
- Y; e7 K; j; B: F fill_color="#3186cc",
4 d) G( V1 w* m) [# ^/ C4 | ).add_to(city_map)& V! S& P K7 r- v: P$ i4 T/ Y
% Q/ ^$ ?, c6 y2 E9 E# B4 X
city_map
8 x/ [, w) A/ n- e1 _ Folium 中的画出各种形状热力图
/ F/ V7 G4 U( F7 y9 y9 A 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。 9 A3 N; h; }' c6 e
import numpy as np( E! o) P" D* O5 j' l4 A
from folium.plugins import HeatMap
( `2 d m/ l5 R3 O8 ~7 A- @* Z3 A
# define the city map
$ q8 |7 y) a# E' v* w8 E. R1 ]+ D' c city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)5 i/ q' }5 z2 w- e5 ]2 s/ J% L
1 J$ c( w- u1 [( f5 Y7 N # 构建随机数据
! a/ b0 L, y% [+ z! q: h: L3 G data = (
% x8 S, e' r4 _0 z1 R0 t* | np.random.normal(& g2 L7 f& p( I
size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +9 g' S' M" o+ m, M& \4 ?0 A# ?
np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
, ]! t4 b- `( k ).tolist()5 G1 m/ ~: e, _( k, x1 l
4 {- a9 m/ k4 |( E6 u7 J
city_map.add_child(HeatMap(data=data))+ C, I, p* C# r! B
city_map - E' R1 Q- G3 R2 k6 |5 _( v
F! ^5 G/ |2 f: A) b2 Q, P
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。
/ R; e/ B- u2 }, \; f' B import numpy as np0 O: S% c8 T* C; x% z& `
from folium.plugins import HeatMapWithTime
) @/ l* t! h: V! f6 O: G, K2 X% t* y$ V# T" u& y7 R u: _
# define the city map; W; o$ F" K, d5 W8 u
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
! i# G( w% R& o, Q
7 @3 f z) D- D2 ? # 使用 numpy 建立初始数据
- t' [( P) I8 u9 `$ ~2 e initial_data = (np.random.normal(size=(200, 2)) *
4 U' d' E4 P- ?. F, s1 B np.array([[0.02, 0.02]]) +
& y+ g" b& N; l! G8 L. c np.array([coordinate_orchard_road]))
( N+ F+ D6 e; r7 F
5 K) D- I3 Q( u" S) | # 建立连续的数据0 o p# E2 }" |! X" o$ G
data = [initial_data.tolist()]
3 v4 I5 @0 k( v& O3 M for i in range(20):) T6 |( U8 L( U6 A" A9 ?
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())% v$ }0 h& ^& |
: b1 L/ ]; ^/ E8 T& P # 显示连续的热力图
" F2 S1 c0 z% o city_map.add_child(HeatMapWithTime(data))2 O1 g$ M, u. \3 g/ j
city_map $ Y" I# z- g$ Y9 C2 e: N
. g* G, K$ X4 i! Q1 l: M+ u 经纬度点的聚类6 x6 m1 B8 T( V2 n: X
除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
4 ?% ]* n+ k- R! t% b from folium.plugins import MarkerCluster
2 Y/ C$ I$ ]* Z5 I' F) U/ I; D' f/ F* L
# define the city map/ N5 X; e. \+ E$ B" M
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)4 P4 p$ z' ?3 o3 q8 Z2 k. c- |
2 o4 _3 J$ w7 u3 t: I1 h # 经纬度的聚类
/ {& Y* P+ c( A5 ?! L # 在 NUS 的经纬度附近随机生成 100 个点;
% N C! T% t6 D: t* O8 E/ o3 h+ N data = (. h/ G; T, @; H5 t; G
np.random.normal(size=(100, 2))7 a* V4 V* X$ A- j/ v
* np.array([[0.001, 0.001]]) +
2 j8 ^6 `$ Z7 t8 w np.array([coordinate_nus]))
# f# E7 I4 W. M3 h. W* O7 u9 N% J6 L* J: k$ i9 @2 q
# create a mark cluster object y. s5 Y. M! H; l2 Q
marker_cluster = MarkerCluster().add_to(city_map)2 d' ]( k7 K& X0 H. c1 _
F& x) P( v. |3 X. T% q
# 将这些经纬度数据加入聚类, s( ^* B2 P6 z6 {! a
for element in data:) G% [. E' Y6 L( [3 ]
folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)
; e/ h$ x( P* ?
& L9 ?0 p( [+ [- W% u' a7 a+ u, y; z # add marker_cluster to map
4 a I% i4 j# n/ c city_map.add_child(marker_cluster)) |4 m J/ W& c' m Z; b
. `5 A# J& W) R& g
# 作图; N$ F/ Y$ V4 o0 Z7 R& S- T
city_map
# n) `1 h: P6 p4 o2 z% | 0 x* D2 d) ?' w. z1 q0 [
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。 $ U: ]9 i* ~' @2 {
参考文献7 ?2 w6 q9 s- V- l& W$ [
Folium 官方文档:Folium - Folium 0.12.1 documentation
4 z# I3 C+ }' C' f5 D/ A, f. s# K! B% ?6 |2 Y% }
5 D6 i' ~: G- a* r; s/ ?
/ S5 \% A3 a& k$ N5 p7 p+ t; O# q m/ t: | f+ `) J
|