Q1 ^/ V# c' }
Folium 简介4 _( |/ y& F9 I4 U, C8 L
作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
3 Q* f+ b# K" F" Q @5 M 创建地图
8 u {. ]5 z8 F2 N& y 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。 ! R( K' E9 [9 q P' C
import folium
* n9 u8 W( N( O% _) t %matplotlib inline
2 A2 Q$ `+ z1 H* D7 o8 _ T( O
' S. I& m& o; t0 E0 Y! X/ D& f import webbrowser
1 w# k, U) [ E7 p& i1 D# `- p$ A! P' u9 E$ c3 p! D
print(folium.__version__)
. n% _/ J- a, }4 h
% e, y9 |" A# @$ m # define the world map
1 }, q& P5 ~, C* s4 a4 t7 X world_map = folium.Map()
: t0 j9 _. ]# q9 L8 S" y) ]- q # display world map
4 h1 p. D3 s1 R, q5 e& o& e7 b: u world_map
+ u1 w+ s' `" H Z2 S
0 V) e) c" H$ N4 |/ U% k4 ^0 X 世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。
+ ^4 z) \# P7 \+ i5 j v 在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。 0 F1 c+ t$ l+ j2 l! _, {9 V
有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。 ( K/ Q) u% b5 f: N% x5 h+ \ ?
另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。
# G: N. ^3 e0 K" M0 J # latitude and longitude in Singapore city- v5 o& I- `' {3 g: H
coordinate_sentosa = [1.248946, 103.834306]: e' U6 G6 `. j. |" R
coordinate_orchard_road = [1.304247, 103.833264]
0 H; e6 D# D0 P/ k. d, A2 q coordinate_changi_airport = [1.357557, 103.98847]8 H o7 C8 Z* ~7 U
coordinate_nus = [1.296202,103.776899]
" D2 v# P0 H% I3 @, A coordinate_ntu = [1.34841, 103.682933]* v W7 k9 V" P3 r! Y5 A
coordinate_zoo = [1.403717, 103.793974]0 T# X5 h0 B+ Y. D* B
coordinate_ang_mo_kio = [1.37008, 103.849523]/ K4 n, q8 g+ H# M3 r1 d U
coordinate_yi_shun = [1.429384, 103.835028]
3 J$ `/ p3 Z% q% R3 i2 I- A9 K" S4 B; ?; L ^- j
# icon
0 q( H$ v" x! w& n, E1 } icon_cloud = "cloud"& u- o' I3 C" L3 v
icon_sign = "info-sign". e) J2 q6 f2 p4 n. L! ?# t3 {* s
5 r4 {5 c C* Z8 i! \+ I, O- d0 \
# define the city map, r$ r4 D, y' K- [7 ` X8 W
# tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}
; \) Z2 H& ?; j city_map = folium.Map(
2 j2 A7 z+ ~0 H5 q/ | location=coordinate_orchard_road,
# D" z/ M$ R6 R* y2 J zoom_start=11,
0 K5 m& _2 G t9 O. j2 A tiles=OpenStreetMap)
0 K/ {! O2 \# D" {9 B
. D0 c, Y1 h2 ?1 F8 J # add marker in the city map! W4 { P, B( W) r$ k& ?7 k
folium.Marker(
- K$ c0 f5 H$ [) a coordinate_sentosa,
: b7 B, T4 o9 L$ F4 W icon=folium.Icon(color=blue)6 c" }3 d. A$ ?9 ~5 X. X! {
).add_to(city_map)1 B5 i# r: @4 }# H: M$ Y0 F
folium.Marker(
5 l* a! C0 R( m' \- F7 d coordinate_orchard_road,3 X9 X( G- R; q H# I$ y4 w+ s( T) s
icon=folium.Icon(color=green, icon=icon_cloud)
) i; u$ [# y. e8 L ).add_to(city_map)" _4 ^# G0 {* }3 G4 s! h5 f6 O
% b4 O! @% Z' I& y& I4 U$ m$ o # add popup6 [' h4 A: X6 h* E; n& O7 P
folium.Marker(
) h. [* U5 R7 W6 G# a' x/ e coordinate_changi_airport,
- l2 B6 q+ A, P& B6 D1 t popup=Changi Airport,$ s# |: T( w5 {: p
icon=folium.Icon(color=red, icon=icon_sign)* `: r' z" ^5 Z
).add_to(city_map)5 Q! s" j8 w2 ^2 q: d' E9 [% g
: G5 H3 J" b) i6 ^, ]% p
# add tooltips and popup' o, P: X- Q, N0 C( U S$ q
tooltip = "Click me!"% H0 N+ I$ T& B7 R
1 @8 P u) ], P5 \) F
folium.Marker(
8 O% c9 y& z' X# e0 S coordinate_nus,1 h, x* A8 l) q6 V! B* ?4 E ?! k
popup="<i>National University of Singapore</i>",
3 b# v6 z0 q$ Z" ~: \ tooltip=tooltip
- D- ]- Z6 K: f$ u1 _* B0 C) s ).add_to(city_map)& Y7 L; ^! t3 c6 b! S
; ]/ M$ d$ R4 p0 L
folium.Marker(( }2 s& e8 T' L3 m5 Z, u
coordinate_ntu,
9 x$ w, P; r7 s% H$ k! v$ J. e L popup="<b>Nanyang Technological University</b>",
# t! s! g% C9 x1 f+ l& N' B tooltip=tooltip, _1 P) G# M( P$ n; X
).add_to(city_map)' j, L( G# i5 ?( G' n
7 b+ w1 |4 a4 p; O1 T# E1 z0 K) X! {
# display city map
, G5 c% H1 ]3 B. k$ n city_map # Z. b& v, s$ W- X8 d9 Y4 |, Y
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。 9 d* U, R* ^- a! O; n! @& _
# define the city map
( Y, o# n1 \5 i" _2 m city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
: W- e; L7 _' C0 Q& V3 l. k! L! Y$ U! r4 F: u) ~
# 在地图中添加经纬度, add latitude and longitude in the map when click
+ Q* O& O3 h7 [ city_map.add_child(folium.LatLngPopup())
1 [% D, H W/ |% W
% n( W3 b1 e$ a city_map
5 q" |; K$ c+ s/ S
" X; I, s H$ q3 S& Y+ u 几何形状1 l1 Q6 N) \9 g7 w
在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。 8 T$ Q6 ]* \; g- L
# define the city map# ~: C/ L8 y2 g, z# l2 A9 O
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
l6 C5 _1 Q# @( _( j
, Z7 E- ~% P- T+ G, K # 在地图中如何添加形状! \, N$ o: ^0 `8 Z1 k% F
# 多条边& R6 K! k5 h' u. b& J2 h. l
points_1 = [
: H1 Q3 @& [5 k! r coordinate_ntu,7 \; P9 d) z6 D5 h3 |
coordinate_nus,
+ E6 I% p R) a coordinate_zoo
* e8 d0 \# S5 \# Y3 k, J ]
5 }* L: E4 ?& C+ U1 s
; q6 u1 _ g1 _ f9 J0 n+ A- x # 在 city_map 中添加多条边,第一种添加方式
- O3 F4 H, [5 {# b# b. R city_map.add_child(folium.PolyLine(
2 I8 g8 i9 a! s locations=points_1, # 坐标列表4 k: m. @5 S$ I1 l9 o+ g/ M
weight=3, # 线条宽度7 z$ P0 C6 W. ?
color=gray))
3 `% ?' }* y Q( h4 I4 \. X9 |1 _: [9 A5 C
# 在 city_map 中添加多条边,第二种添加方式2 x1 s# t9 U2 |2 F0 ?( Q7 B! D
folium.PolyLine(7 e( N# I6 u; @
locations=points_1, # 坐标列表
6 u7 C0 `1 }7 i& d weight=3, # 线条宽度
) r4 [* Q* D# R& u color=gray).add_to(city_map)
2 K$ l* ~2 C" D2 m; S$ V
. h A; N, c" D5 k8 c7 m# C; k # 多边形, w; r) Z& }) K9 r" M: s* N% a! C
points_2 = [: i6 b, u1 Z; Y/ g& T8 Q
coordinate_orchard_road,+ U3 z9 a5 X7 [" e! q) p* b
coordinate_sentosa,
- _- N. B8 V8 z6 e, i* u9 H coordinate_changi_airport2 G* k$ d) |3 c# ?% N$ U# S, }
], ?7 P8 Q7 V+ E
3 h0 y5 R6 ^7 D9 r& y
city_map.add_child(folium.Polygon(
/ f' E I: R8 o+ H1 G* C locations=points_2, # 坐标列表
4 l; J/ |0 q4 T$ u+ Y; a weight=3, # 线条宽度& S+ B5 }0 F1 i% D
color=yellow))
5 @5 u! ]. R$ `% @0 O+ Q8 O
$ ^! b6 s! L# x* Z+ Q # 矩形
% a* @# m- Y f% `0 M9 m bounds = [
3 v$ z5 z0 M0 {! T/ R% E coordinate_ang_mo_kio,' W: U0 \' A, V' p4 A6 ^9 U
coordinate_yi_shun
; H) f$ b5 j) d ]
. P! Q* k* ^7 n% i# ]5 H* T S% U& S3 m `& ]0 K9 Z
city_map.add_child(folium.Rectangle(
M* ]" t/ f( B3 v7 C8 A bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)/ L8 x. F. h# Y# I+ C$ C- |" W
weight=2, # 线条宽度4 `% N& T# h4 n4 w! i, U
color=blue))4 M. }% T7 e% {% W3 e9 Q5 b6 x
8 y. M5 Y5 ?1 e% S. _" e* U, b # 圆形, circle, radius units meters
: ?' e4 D9 _7 c3 _& r# E folium.Circle(, Z4 X- e. E4 B" _4 m
radius=1000,
T, ?5 l6 v$ ~, N7 ` location=coordinate_nus,0 U, G- y0 ]6 K
popup="National University of Singapore",1 S, c' ?; x, x( i; j5 Y8 @" I
color="crimson"," M0 D; G, L, Y4 e+ e1 F
fill=False,
4 K8 O6 ^3 h. X4 C0 d ).add_to(city_map) O6 A" t6 l* \5 t O1 r4 t& C( I
1 o L1 A4 Q3 |% w2 T # 圆形, circle, radius units pixels
4 b3 n% _- X; y6 f0 `8 \" I folium.CircleMarker(
" h$ y' c* @1 Q3 J9 ^: w/ \# H: L location=coordinate_ntu,5 J9 d" E/ G6 y( P- ?3 g( v
radius=30,; E& j: q2 T! F2 ^7 h+ T
popup="Nanyang Technological University",% s( f" H! T7 Y+ E, P4 K
color="#3186cc",
: T- m) c2 z0 @6 L" N! I1 i fill=True,4 @3 c: m3 D9 Z B( x
fill_opacity=0.3, # 透明度
8 q# c3 N; C [+ Y/ X2 g- j fill_color="#3186cc",
* w1 `! l6 l. Q% {3 I ).add_to(city_map)
3 B8 w3 y( t& x; L! X
y+ q/ U0 t. R4 l/ h* M city_map
* ]# h3 r6 Y# T& j Folium 中的画出各种形状热力图0 s" H' ]8 X2 m; T" D: i, z
在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。 " s. |6 V6 T3 m, i( L! v
import numpy as np: N. h9 b9 _8 ^& B, I
from folium.plugins import HeatMap. c. `7 P1 x# A2 B6 G! z
1 J0 V ]/ W# B* U2 N, K6 [ # define the city map5 k7 c) l" r+ U& J8 }/ n! ]9 u% n
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)- B2 P# W# u/ M
, p2 H2 l' v7 q" ?: a # 构建随机数据2 W& f- n: v, l# @! I# k8 ^* |
data = (: `8 D$ m1 f3 z/ D( J. o: R
np.random.normal(9 P1 b+ s' L, C I7 ^/ v9 Q
size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
4 E _4 H& z4 J H" P- Y np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]); ?; d+ P/ A) C& F9 P3 S
).tolist()
3 J0 e/ N/ i+ y5 D6 }
8 j# _ G- }, ]# _+ i" Z! [ city_map.add_child(HeatMap(data=data))! l" @9 |) M/ ?
city_map 9 \9 C5 U2 M' ?% S& v
+ u' o- t' W2 t, @& o1 P0 r( W9 c& E
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。
0 X& l9 j3 i1 \- [+ s import numpy as np+ i g8 Q6 Z3 X7 b0 J6 t
from folium.plugins import HeatMapWithTime
4 \8 r2 d# M8 ~. \
; v: \/ G, b- f # define the city map
/ U% ` l7 I1 u7 ]' d6 t8 u0 m city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
8 A( ?8 {( H R, I& B! h. ?0 V" k& Q# j |9 P) ?8 t
# 使用 numpy 建立初始数据
$ O% Z ~ A+ t9 i initial_data = (np.random.normal(size=(200, 2)) *6 N$ T6 }1 b& r4 v4 C+ K1 n
np.array([[0.02, 0.02]]) +* q3 p7 R+ w4 E2 F1 ?3 V/ @+ V; `
np.array([coordinate_orchard_road]))
/ s0 m+ H5 s# _! `5 Y+ V; ~, U6 P4 s S: [
4 J. {0 i; a# S # 建立连续的数据" h9 ~( `1 S x* b
data = [initial_data.tolist()]
5 q- T. _; \; P7 X( H for i in range(20):
. H" e/ b0 Z% g3 w; o0 l data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()): G4 M+ k2 ~. ~/ t
) z3 O6 H. I4 r6 r
# 显示连续的热力图9 F: B/ s H1 {$ ?) n; B5 t/ v
city_map.add_child(HeatMapWithTime(data)). ~# b: `" }& _7 V# H9 n' ~% M
city_map 7 x" C2 S5 G0 g( R' Y
4 T2 z% j3 ^# g) ]/ j& P2 A 经纬度点的聚类
( q& Q0 a" ]( E- X9 j9 E 除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。
& O! u# f* |; M, x, M& u from folium.plugins import MarkerCluster* Y1 }! `3 j: H/ }- I9 O3 `
: k) ~0 m- b) h. t+ j3 D4 W # define the city map
# h7 N5 c9 V. g* d9 q( R+ D2 J7 n city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)$ v) p2 I+ H: D; P
. l+ v, Y) G- N6 S/ d6 P; B
# 经纬度的聚类7 b! c9 R: |8 ~6 M. l
# 在 NUS 的经纬度附近随机生成 100 个点;
0 u5 A' ~, q. n data = (
; ?" \5 V. z$ c5 z np.random.normal(size=(100, 2))/ L8 }6 ^" w7 ~$ I6 ~4 s1 h; N
* np.array([[0.001, 0.001]]) +9 o, b4 S4 X! O1 b- P! C% j; b0 c+ r0 [) _
np.array([coordinate_nus])), G9 d$ L: t- k' k7 [
2 C8 l3 v* S# G+ x7 M B$ l! Y1 y. @
# create a mark cluster object
2 R7 p0 ^7 n) @+ L5 s$ {; a% B marker_cluster = MarkerCluster().add_to(city_map)
3 f% [, B* [/ x5 ^% H; E( r4 K: n& m, s
# 将这些经纬度数据加入聚类
3 D5 F. t: G. R X1 @7 N for element in data:
5 B; _& J, X' q! C! W4 ], s9 s3 q folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster). K. v/ a( X' P4 ^5 y
& ]6 A/ h0 f( } D) A6 A T
# add marker_cluster to map
/ o1 c( A- S' d. P4 n; I city_map.add_child(marker_cluster)* f4 M- y* z3 ]0 h/ m
; f! f* i. z4 C# _. o+ H
# 作图& O% X& [9 B2 y1 x
city_map 4 p, R& N% A _3 j! {
; v [1 T0 L) J: u7 R" c6 C7 ^
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。
; w" M$ d- k3 |* S, j4 d) k 参考文献
) w% f! L: Z) x7 d1 J$ v6 C Folium 官方文档:Folium - Folium 0.12.1 documentation 8 K7 c0 j; P3 W' D6 \
, x3 V+ d4 f' K, R) `9 l
: T- T. G, {8 o' U% s# k
; }/ j: b# f6 R6 N& o& K5 h5 f/ J6 [( s, c
|