' t0 Y: |. {3 D' V& p
Folium 简介
- b' X- W# N4 \# ^# Z2 e# ] 作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。 6 [; J E$ Q/ ?$ v* T
创建地图( f0 |8 y* J0 O) z. p* f
通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。
! u6 H% h$ ]7 b( V, v import folium- t! Q9 _# P" N, I$ u& c
%matplotlib inline
4 {0 k; f8 t! H% ^* |* r# H* ]; D( ]( d- x# U
import webbrowser
& X) B( P3 d/ N$ }
1 h/ ?7 [* R9 w8 { P# w print(folium.__version__)3 i, _5 U2 N& [. ^7 `
& w+ @2 l' x7 P$ h8 }8 _1 h, X
# define the world map) S* {1 T# g) ], h1 b
world_map = folium.Map()
; v; b3 g. l' t" ^6 H3 f2 X$ `" b% C: ~ # display world map
/ C9 C4 [5 G$ ?) a2 b# [7 q6 F world_map$ T! D \/ O2 J6 [
8 x$ e1 q+ H, L 世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。 ! }2 E+ c4 O i# v* `) E' A
在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。 5 y- e+ V& }/ a' l* I0 O' R
有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
. s; o$ r* [: X0 V ^" | 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。
; s0 ^ b+ }4 U# D6 Y # latitude and longitude in Singapore city# i3 M9 r4 c+ ?1 {% `
coordinate_sentosa = [1.248946, 103.834306]
: ?9 X1 U# l* Z% O coordinate_orchard_road = [1.304247, 103.833264]- g( [+ \! ]7 U4 z$ M* ]5 }2 L
coordinate_changi_airport = [1.357557, 103.98847]
. _) W4 r/ |, a' s coordinate_nus = [1.296202,103.776899]8 X/ a& A2 Q; V7 q/ G
coordinate_ntu = [1.34841, 103.682933]
1 ]' ]+ c, b7 b2 \" N' A/ [/ h coordinate_zoo = [1.403717, 103.793974]' p' W' e0 Q1 ?6 C5 x @, a+ c/ o9 Q
coordinate_ang_mo_kio = [1.37008, 103.849523]
; I& v1 A2 h1 o) R" {0 s% x7 G coordinate_yi_shun = [1.429384, 103.835028]
* U; o. [. n0 e, M _: L$ ~9 G
0 ~. N1 N+ Q3 S# N) | # icon5 m& e2 [* H- |9 K5 J
icon_cloud = "cloud"
% I5 e0 X& R" m2 O2 o$ Z icon_sign = "info-sign"
8 n* V& B/ c5 e/ S5 q+ D% ]" o% u0 e' s1 y/ d: N/ K) O
# define the city map8 h$ X: S/ C* _' {
# tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}$ d' b3 {& T7 W! X- t; C
city_map = folium.Map(
# b% E x* `: \- I! u) z# s7 ~ location=coordinate_orchard_road,% W! C3 @" m# @) B5 G
zoom_start=11,. Q- A% L( m0 A
tiles=OpenStreetMap)
2 i0 Y% A8 _% C2 j5 ?% j( g; Y: V5 x9 m: w
# add marker in the city map& }# G" X0 A7 }
folium.Marker(4 j" J9 y- M. L$ s0 i: e0 a7 `
coordinate_sentosa,
: @5 C8 W& c, K# r% { icon=folium.Icon(color=blue)/ B% e1 K6 a6 b9 t# Y
).add_to(city_map)9 r1 b ?4 E% `! J- P; t
folium.Marker(
6 j; t9 q6 ]! L4 x1 b coordinate_orchard_road,! B: q9 a; |3 ^$ W! o" s D7 n
icon=folium.Icon(color=green, icon=icon_cloud)
% Y3 j8 h; B* I6 h! v ).add_to(city_map)
8 r, o# C A _; B7 K
" W9 P4 K9 y$ L, w( }; g # add popup
# J" v, J2 G% j# D/ k folium.Marker(. _- g1 ~) c$ g2 p
coordinate_changi_airport,
. @: K- x& F- Z; N, ` popup=Changi Airport,0 `) t `2 w) d c
icon=folium.Icon(color=red, icon=icon_sign)
7 I9 Z( R5 J/ u! Y g( n4 D ).add_to(city_map)4 r8 ]" O& w. h# i
5 O. F' z4 W2 m1 J1 a
# add tooltips and popup% F6 @, r9 E5 Y2 p9 i y
tooltip = "Click me!"8 i* H2 ~/ g: X$ A- `) E9 t
" m( D0 o3 Q- ^ i" F" F
folium.Marker(9 l5 r0 S5 T5 J
coordinate_nus,
. Z% n: E* H9 H4 |0 ? popup="<i>National University of Singapore</i>",
7 Q+ B4 x( N# R' \4 b' u, Y tooltip=tooltip) {4 A5 y- Y+ {1 [
).add_to(city_map)
3 }0 p( x& E/ \: Q" E+ i# E8 `
folium.Marker(
; y% G$ w6 D' p6 R% p coordinate_ntu,
5 C/ F: n! N0 Y' |5 g: } popup="<b>Nanyang Technological University</b>",7 p3 t: b( A% a j1 s
tooltip=tooltip1 w5 D) M# i$ P) J0 K7 k$ D. k4 W
).add_to(city_map)2 Z+ S, Y4 v% ?" a- D
( R# z7 [' [4 d # display city map8 q# b& |3 h$ u$ t
city_map r+ D: C% h/ b: d- w0 [' B
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
$ M% Q5 U- l0 ?" n: F1 O! N; H$ l # define the city map
' A$ B1 A% Q1 t2 V3 h3 ^. n; Z city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)8 Y) ?# [+ W* k( U
; @% s6 [* J! f0 K8 l% }' H
# 在地图中添加经纬度, add latitude and longitude in the map when click
8 L3 E4 \2 k3 Q& B$ p* M& F city_map.add_child(folium.LatLngPopup())
5 S5 U0 ]' s1 S9 ]- c L2 z% G& I! u5 _& w/ D
city_map ( P4 J: t: S5 L2 p& x3 x$ a& D
# O8 X+ h4 P5 `. J7 e/ u# H
几何形状
% o, K- K8 K2 M: w) j6 b$ W 在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。
$ }6 E# C) S9 B* q, I9 Y0 y # define the city map
1 ^0 G2 i6 t5 Q: W city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11), l3 Z( r9 ]2 x" W4 F' \ k) I$ D7 G
9 D0 k S M" \% u6 h% q
# 在地图中如何添加形状
2 ^7 S8 v) V; F# ` # 多条边4 g2 Y! j" @' g% C
points_1 = [& L: J& u: v1 @
coordinate_ntu,
( f5 v3 n* [- D. p R: x* i coordinate_nus,2 l- ?6 F$ U3 O& C ]
coordinate_zoo. n1 z, Q2 O) H1 `
], t: y; k7 g; Z9 ?( L0 L
; f2 `- O7 m3 c9 l # 在 city_map 中添加多条边,第一种添加方式
* I _# O7 s0 G5 ?2 L city_map.add_child(folium.PolyLine(# h; D9 ^7 p+ n& C& u
locations=points_1, # 坐标列表% I+ n; t2 u" T4 U8 D! d
weight=3, # 线条宽度
6 h7 F, H# x) t5 x6 h) m color=gray))8 F* Z; A) _1 r2 ~5 Q2 C! B
) Z( i+ e( G; f& ^. g # 在 city_map 中添加多条边,第二种添加方式' J5 b* F2 @9 E* d" c
folium.PolyLine(0 X2 c4 G7 P9 l4 F' w1 P% a* k
locations=points_1, # 坐标列表- z2 P: I& _# H5 h
weight=3, # 线条宽度
5 ~$ |2 G/ E* w# g0 {$ h1 { color=gray).add_to(city_map)
8 m* F: m a' _, S, b% C
* e7 Q: r& {9 p # 多边形, m/ v5 t ]1 a& n* ^
points_2 = [
& J2 R' U% b! @! L7 g. m/ k coordinate_orchard_road,5 d, J. f1 ~! E4 o5 q
coordinate_sentosa,! {7 h* J* |6 O- H
coordinate_changi_airport% O9 ]2 n4 M9 S3 O6 K
]* N( E( m% w! O+ |' c1 ^' T
+ W% B {2 }+ T5 Z
city_map.add_child(folium.Polygon(
/ Y; G2 H" C- N3 f7 K3 B locations=points_2, # 坐标列表
& j) i0 w% v7 |: t8 ^ weight=3, # 线条宽度
0 S" ?# x0 d. c4 i& o$ m color=yellow))
+ k7 r! [6 K' k1 Y" z
% O \2 n$ }3 @1 {+ q) J. A # 矩形+ o* }4 L5 A# M' V
bounds = [" o0 h. `3 U$ i' o9 ]4 C- i
coordinate_ang_mo_kio,- X% B2 k1 i u) v. N' _* J3 n! U
coordinate_yi_shun- ]' I/ }/ N4 H6 R
]
+ ~' W E$ B9 C9 b7 I
& `0 [/ M+ I# n city_map.add_child(folium.Rectangle(
k7 r' B4 T# N2 \ bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)& Q, [4 o, g8 g
weight=2, # 线条宽度
; |1 K" N& a0 M' H L) }$ `- ? color=blue))
; s, N6 G: ~; E/ @8 [( _5 k/ I0 _& R6 j* y
# 圆形, circle, radius units meters
% K- D9 U- l# f! x folium.Circle($ } N" S: r8 ~0 D
radius=1000,
- M+ x F, G1 w9 Z9 N4 m u- g# m location=coordinate_nus,
( F; ]7 g. y) @% W' @ popup="National University of Singapore",. ]: C6 T9 U/ v/ f; c# K0 Y6 l: p
color="crimson",
4 [: T$ E6 `3 P, @4 R4 }* v fill=False,
a( I9 n/ s- I' _) [+ g$ V ).add_to(city_map); R0 w8 Z0 q* _4 z% w
. w! q" F; g; z- v1 |
# 圆形, circle, radius units pixels
( p# R4 ~2 R) G- ^7 B# E) B% c4 a folium.CircleMarker(, L# x+ h; E& F2 x. V2 {
location=coordinate_ntu,
: U) u3 x+ m# k* u radius=30,% f u5 E' G1 J: j2 n
popup="Nanyang Technological University",
! N3 B3 |# {2 J% N: N- N color="#3186cc",+ J3 ]+ g! S. M( f
fill=True,
, m5 K `! w/ D/ S( g2 t5 W C fill_opacity=0.3, # 透明度
# C+ v1 Y; a0 m3 t% U fill_color="#3186cc",3 T, U% A* e+ n) z
).add_to(city_map)
: u- j! _# O# u1 o+ g$ g9 c
1 @# u9 e2 l% y, _; e city_map
% ? C3 O3 F8 |) B& l9 u Folium 中的画出各种形状热力图) y) b2 a* K4 ^0 k" K
在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。 , n$ p( m/ ^3 }. a% Q, z
import numpy as np
- @1 b% J! X" I6 _: b* o* p( y from folium.plugins import HeatMap
) ~$ k' y$ v* Q+ n! E, P2 u
* V7 g' q% s, y/ C+ O8 O( i- u # define the city map
; n! `3 r6 e1 S. L% G& m' R city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
& l9 C* d. U4 D7 t" q6 V ^' G7 [! m; C; y) }" n
# 构建随机数据( a5 |8 T5 {4 b" o- ^4 A
data = (; I; @$ F" ~* T% @5 N
np.random.normal( j. V1 k* t( b
size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
2 e) y2 |; B: r: F) k np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])/ K: w: K* ~5 i) q5 u
).tolist()
9 l! y4 ^& m! _+ _* K* x# g$ x3 w% O* W! ?6 t& [% B( [" y
city_map.add_child(HeatMap(data=data))
9 z0 c0 Z# U l city_map # l8 o! @" V) }0 ?9 r6 E' h. G6 t* w
8 h6 Y T7 ? X 除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。 * z" v2 L/ P2 h' T8 N
import numpy as np9 a- [# q' [! ~7 P3 O1 D# n
from folium.plugins import HeatMapWithTime' s- B* |5 L4 p+ S/ J
* r! |% `7 U2 \" ~6 a9 w; |6 j
# define the city map( v0 I7 u+ ^7 L+ u
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
V T$ I0 _ ^, {6 F
/ B/ g4 Y5 k$ T) X! T- N # 使用 numpy 建立初始数据
( C& E6 t+ ], Z* }$ V! c3 a& G initial_data = (np.random.normal(size=(200, 2)) *1 v X9 l; l: C: M1 K, g- n* y; I$ s n
np.array([[0.02, 0.02]]) +& z3 J. F. W2 J& [8 P
np.array([coordinate_orchard_road]))
: S+ G l6 z8 A8 z5 g! G/ E& s8 k" B8 A( i
# 建立连续的数据
* [& X1 Y9 O8 Z data = [initial_data.tolist()]+ e4 i- e6 V$ o: ` ^" y- t
for i in range(20):2 _! ?. h' M( e/ d9 I+ c: e
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())& Q1 E6 i6 p! {2 S
1 [! ?' k% \' g p8 X # 显示连续的热力图
8 L6 X, L. E4 s$ b Z city_map.add_child(HeatMapWithTime(data))
3 s3 B. n2 ?' ^" c( K( o city_map " I# A& D8 s$ b- R4 v8 m! o, v
3 L( \# G/ {4 q2 R 经纬度点的聚类
5 F6 W9 H$ {4 ~) K1 r 除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。 ! P5 p7 k! q2 @
from folium.plugins import MarkerCluster
$ _- H7 y, d4 i# ?7 _
' J; w( q1 W0 N S # define the city map
~: L o8 {; S, o# Z. p city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)1 j4 x8 S) B; q5 `
" E" m- @) ^7 {% y' l y3 V
# 经纬度的聚类
$ u ?; ?* w2 \( v7 Y1 e$ c/ o # 在 NUS 的经纬度附近随机生成 100 个点;" _% H; s" K% c7 O3 v- H M
data = (
4 b3 L, M& ?8 [- y; T! t o/ H i+ O np.random.normal(size=(100, 2)) j) I8 I- T w5 K4 L. b
* np.array([[0.001, 0.001]]) +
& C6 C- I) ]! \/ x5 v3 S np.array([coordinate_nus]))
9 c% ~% `# y% t' M& X3 R" }6 N% l; f
# create a mark cluster object- K% `8 B' E0 _+ ]$ T6 w
marker_cluster = MarkerCluster().add_to(city_map): M6 G/ e* N, V7 ^3 w* T0 K
0 }9 C4 F0 c$ T- b
# 将这些经纬度数据加入聚类
& T0 X# W' O" g5 | x: k, u5 o for element in data:
) N' ~# S" x' ?! r3 l- z' } folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)( H4 `0 [/ S1 Q
$ a( K5 J# L1 i" O2 B: e # add marker_cluster to map
1 X6 A7 E8 X" w, ? city_map.add_child(marker_cluster)0 P& Y, X1 z( Q% `: S5 T) z
: {& h" W* \: A. y8 K" j" ^' U$ q
# 作图: e j6 }: N* [, T3 {
city_map : G1 Z' W5 g! o+ G" U- b! h- `; g
, s# u4 B2 x# `* P1 O+ f 以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。
6 E, y4 S' P* q- O# o* O9 L 参考文献2 p U; |# k: t. S
Folium 官方文档:Folium - Folium 0.12.1 documentation
- W8 ?5 f5 \: a
! }( b& G3 F, r1 s
( ~" |0 W D" |7 T% a! f* W8 ^: d" c9 N5 r6 q
, r- c9 U! c5 Y- R
|