4 Y: |/ I4 w* A Folium 简介
! x1 |# h, x% Q 作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
& ]2 i/ O6 d/ u }- T, b4 h 创建地图1 T5 W$ ^' q# |7 F/ w
通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。
& g b6 O) R# q8 D% j0 o( [) g import folium. J+ H, _' e1 X& X+ F8 f% _
%matplotlib inline9 g; I; J6 W3 p5 F. t
; A- y) v3 X0 Q+ T3 k import webbrowser
. _7 s9 s; ~5 `3 m& q- J5 q
$ r% ]/ _% n9 T: v+ H# P7 ?8 y print(folium.__version__)% L( n2 {. a1 {9 _/ |- @; x
: b+ q, Q c! T T5 L # define the world map5 x- G3 Y- E6 l& N( q; @- p
world_map = folium.Map()$ J6 q+ B' _% Y
# display world map" J6 R) U6 h/ `0 {
world_map
2 m; q1 T0 Y- b5 v$ }7 H 3 q4 s/ j6 C4 p3 u9 y( i7 q# e
世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。
3 M7 o f. E: {& N% a4 [ 在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
7 r& I' c C5 X) U 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
$ @$ r' G- u' B4 z7 P 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。 ; h( A: }3 J- B& V& X
# latitude and longitude in Singapore city8 {* \' W1 ~" o7 t4 o
coordinate_sentosa = [1.248946, 103.834306]
/ S' j/ Q. @+ o/ V4 g/ E coordinate_orchard_road = [1.304247, 103.833264]2 }2 i1 z; [2 U
coordinate_changi_airport = [1.357557, 103.98847]. z4 \- R$ x8 x
coordinate_nus = [1.296202,103.776899]
9 V9 H9 @ P2 M. b4 Y4 g) w coordinate_ntu = [1.34841, 103.682933]
) K& v+ Q: o% Z coordinate_zoo = [1.403717, 103.793974]3 y- J. t' F& S
coordinate_ang_mo_kio = [1.37008, 103.849523]
8 z4 z7 O N, j; O ]% C coordinate_yi_shun = [1.429384, 103.835028]' w: N8 K1 ?: Y# U* m
! M" b0 \; O, `: z& `) M+ d # icon
{ p- \: K' |9 O- w icon_cloud = "cloud", i: l2 O9 z- ?# q- i
icon_sign = "info-sign"
( L4 }$ y$ J4 f9 ~( q. u
2 b0 z) n' c6 j; W A% \ # define the city map6 s" {; ]1 e) C
# tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}. o- \" w! U7 w' u
city_map = folium.Map(" t& a9 a( N6 w0 `4 P
location=coordinate_orchard_road,3 e* S: P; z2 d' ?% C
zoom_start=11,5 e+ j7 k9 y" U/ x
tiles=OpenStreetMap)
. b9 H a$ y2 A9 w
' t7 h3 K/ X3 J' R% V6 e* X5 _ # add marker in the city map
; O) y; }1 D/ B9 c7 a7 g folium.Marker(4 r" r! |2 i1 X- r
coordinate_sentosa,3 f; _5 Y5 E- r* u( E
icon=folium.Icon(color=blue)
4 {0 I) {8 K9 ]. F& W' d ).add_to(city_map)
, l8 f% g* t7 \( @' ?5 `! F folium.Marker(3 z7 ^% K- Q2 Z" b5 }, Y
coordinate_orchard_road,
7 a0 y3 W- ^8 K" f1 A9 L icon=folium.Icon(color=green, icon=icon_cloud)
4 I. O4 }4 z! H2 C; _/ Y ~ ).add_to(city_map): K0 b. R9 A2 @7 z" y7 D; l" N$ A
6 c" ~" {( s7 y # add popup. f2 m- I7 K, [) F
folium.Marker(
4 f( Y7 x9 T! E% ?$ F# K coordinate_changi_airport,* F( m" s: q) i) f
popup=Changi Airport,
{9 G1 p& z, n; D3 t9 [$ e7 m icon=folium.Icon(color=red, icon=icon_sign)
+ Z1 z7 E- }7 A- x2 d2 ` ).add_to(city_map)7 V. H" M# o8 E+ w0 k$ j
; t6 r ?1 E! {9 X+ J/ N # add tooltips and popup# y M2 R6 v0 l4 e/ c/ r! T
tooltip = "Click me!"
2 X- X& L8 o7 v% n
5 S7 U. g! P) N6 K. J folium.Marker(
7 C" I( I, n3 T coordinate_nus,, k6 H" q& {" ?9 ]5 d
popup="<i>National University of Singapore</i>",$ u. s( r( s, M) C) e
tooltip=tooltip1 k8 ^0 ]" a9 g; `
).add_to(city_map)4 [; Z" u0 T/ I, A6 j' U
' k% G; O$ t3 M+ M* k( W
folium.Marker(
: ~9 u: y8 `' f: d, E0 D coordinate_ntu,) h# N7 d% L7 [ c8 V
popup="<b>Nanyang Technological University</b>",
- V5 ?' y1 j, l/ [' Q. [% D! h9 r* A tooltip=tooltip
( M8 E: Q9 N4 i! O9 r" G$ M0 C+ J ).add_to(city_map)
) p- Y2 R7 T+ I3 [
. Q- k1 Y# O8 v' |4 ]9 j # display city map
; a5 I; p) _( w% F. H0 [ city_map 3 w1 E& A5 q7 x0 |2 v
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
' J. n3 V* k/ w4 i$ y+ {$ T: ] # define the city map
: l6 m# E5 y, m: V" H7 y city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)- J' m/ D7 {. v) i& h r: t3 T
0 ?5 F9 j9 A4 v% |
# 在地图中添加经纬度, add latitude and longitude in the map when click
/ c+ Y( Z/ |- W5 W. e5 J t city_map.add_child(folium.LatLngPopup())
( q2 n$ U- a& i- ^8 q$ \2 h0 W+ F9 k3 h$ _: \, r% T4 Y2 }
city_map " y: a$ r7 f+ N/ t
5 ?- g# l G; x5 Z P 几何形状3 w" e% X W. V) r+ o
在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。 5 t$ H, a0 q& C( O9 K% n0 s7 y
# define the city map
) ^: [' O. v% H6 I( Y$ o& d city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
0 ^& v; T' E8 u0 n% @" z( ~. ~) W1 @3 R
# 在地图中如何添加形状. n8 e# F' V; G8 R/ k, z& K9 V
# 多条边
8 h; C+ r3 P% g. b3 A- U6 p points_1 = [
. }) Y% }% @' @; B: M1 h coordinate_ntu,
7 Q6 p/ `: e" O coordinate_nus,
/ W" P' K2 P! c) T5 d, F' f- N coordinate_zoo
; M. G0 x* K. l3 N0 N ]5 R% N4 |. H. B. `- s6 d
1 B: t2 ?. ]0 F' p3 z # 在 city_map 中添加多条边,第一种添加方式
- P8 ]! {# o$ r& M city_map.add_child(folium.PolyLine(
2 r) z; K+ T0 J* N4 G9 S locations=points_1, # 坐标列表
9 v3 F# I# D8 M% z/ Y weight=3, # 线条宽度
6 S/ I9 q( U j color=gray)), V2 t- x+ }. t# i- d# f9 F4 ~ l
$ m' U! `# M+ n2 U, z # 在 city_map 中添加多条边,第二种添加方式
/ e( ~" r- \' b0 j* d7 @; r+ Q9 F! \/ W folium.PolyLine(. Y6 H' Z2 o2 D, Y0 | ^5 Q w
locations=points_1, # 坐标列表
: @) c A0 D1 O/ d8 _: p" E weight=3, # 线条宽度! M. A7 M/ l9 K) b: ]# k( @
color=gray).add_to(city_map)
# ~) C( x2 U' G2 V3 t' V+ E9 P2 p6 e: X! S1 V+ Z8 @) n, J" h& g
# 多边形
& X6 R: s# q. O* U/ j points_2 = [
3 [" ^6 ~3 F6 D! K G3 _1 | coordinate_orchard_road,
/ s' X: D1 N# v4 ]# m coordinate_sentosa,
+ o4 i- O1 P) D5 c$ U. \6 k coordinate_changi_airport
8 u: j# Q1 W/ v7 e$ K- W2 K/ k; A ]
3 D( h$ V3 J: X5 J( p
* A& s4 Q% j3 k& [ city_map.add_child(folium.Polygon(
$ J) H' ?( t8 {- \6 E2 |$ u6 Z locations=points_2, # 坐标列表+ \4 ^4 Y- U* t1 A, O# I
weight=3, # 线条宽度' X! @8 `( J1 x+ g
color=yellow))$ Y' @' \* D3 z4 o; o
+ t2 ?. l- S* L0 T: R
# 矩形
1 @* ~( }2 f9 T$ E* ?: [9 u0 ~ bounds = [& D/ T m& [* ?6 ^ _9 y
coordinate_ang_mo_kio,
/ N: n8 P6 u7 k# C coordinate_yi_shun
% ^( g+ r+ ~# } ]
- P# M2 a( d T1 T! Z3 L3 [
. v6 F, w- t# C% G3 g city_map.add_child(folium.Rectangle(
. v9 d/ a0 t u( x W; x h6 N9 P3 z bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)2 G5 R! b$ S4 _+ _: T
weight=2, # 线条宽度
# [2 q/ Q0 i2 e$ g6 Q8 M: Z color=blue))2 X" y8 G4 [- U
/ P# C! O/ h* I6 I7 L! D5 A2 F9 \ # 圆形, circle, radius units meters
- y4 r8 ~* ~; f$ w folium.Circle(& @1 P, T& J" ~, B X* O* z x
radius=1000,
9 m- N/ J8 \. J' H location=coordinate_nus,3 j) P' t" A) K
popup="National University of Singapore",
" m+ a1 a2 Y& D1 Z: ]* S color="crimson",7 r& W, Z2 Y5 U; q* y
fill=False, o6 G9 {. U1 C# O! d
).add_to(city_map); r! a# @0 `; ]7 I
" u) A" W8 {) z5 L6 M6 b # 圆形, circle, radius units pixels2 K3 r$ c$ @/ | Q6 [
folium.CircleMarker(
* ^4 T3 z- A% F location=coordinate_ntu,0 b- ^# U3 d5 i; G6 W9 X3 _
radius=30, H8 i* ~' C, W; Q& H0 J; a
popup="Nanyang Technological University",) J0 U, z+ ~( w" G+ ?; A
color="#3186cc",
. B+ F# U/ ?4 f9 V fill=True,# l9 Y' G8 r$ h* y4 t
fill_opacity=0.3, # 透明度
' s! g0 \( d+ ]7 L& z fill_color="#3186cc",
8 j* D7 u3 `) Z/ {7 V ).add_to(city_map)
' u! d* ^ B7 D9 n, \% v: E/ m' Q, G2 z4 k+ \7 d' X P- T
city_map + N& F6 [; e( q$ P
Folium 中的画出各种形状热力图/ {6 ]" g, A1 _. w* ^
在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。
, g/ a! g- o6 D# h5 o import numpy as np* j- L* }$ I6 q9 g' G$ L
from folium.plugins import HeatMap9 a: H: j* V0 s I: c2 ~$ I
, {+ @2 ]( n( `* M& v( ]" Y
# define the city map/ _# z+ ^) O4 C
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
" A) H+ |2 g4 Y: [
" E5 a8 t$ G' ?2 P, v3 f # 构建随机数据5 x1 _, ~/ M, m2 V
data = (
2 M# M6 h% l. e0 [. ?8 R: z np.random.normal(9 m: T3 O* L) Y
size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +
* n/ Z4 F: L z, F np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])' V+ I6 \) K* C8 j) Q
).tolist()
1 _( X/ R6 q. T/ Z& Q1 U0 `2 I
7 G8 v: a& E, p2 z" m1 U city_map.add_child(HeatMap(data=data))
! S q* ?/ j# a2 G city_map
6 W5 K& A$ w$ f$ X; U$ V ; i; M& D8 c, h$ V0 M& @
除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。
- s7 @4 i X3 P+ m: A; l1 U import numpy as np
0 d' J5 [% S0 n. i* V from folium.plugins import HeatMapWithTime
5 g+ N5 r& U- _( h
$ ?& u; F2 f7 w3 C/ K # define the city map
1 V2 S4 }/ t( ]( o1 p7 q! c city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) I$ i( a- F7 I- g" }. z3 r8 @! i
& [' q/ A% \/ b5 t* \+ \ n* o
# 使用 numpy 建立初始数据
9 O- x* F# u$ m- k# o3 U initial_data = (np.random.normal(size=(200, 2)) *, h& o1 T) b% z, D0 b/ S. V8 W3 C
np.array([[0.02, 0.02]]) +
3 Q' C2 t/ I7 c0 @ np.array([coordinate_orchard_road]))' c6 S8 _+ D5 @0 ^ k. Q9 M
1 x* v+ d- L3 P # 建立连续的数据
/ _/ ?- p: b0 j) Z( F9 I data = [initial_data.tolist()]. J' ]& L! h# n6 r. H
for i in range(20):1 e( r! m% y6 o z9 \0 S
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())
# y. I* n! @. s* Q# K: I- }8 V& l+ m: E( |) o- r; _
# 显示连续的热力图
( L% {: s/ x' g& P( W city_map.add_child(HeatMapWithTime(data))
2 z& H- K) n+ X city_map 2 e P5 @* o% ]6 s; I* h, W/ |
; K7 Q1 E) }/ y6 y- i
经纬度点的聚类
9 S/ b& G i% g# @! V2 t 除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。 " ?; D; k4 i& h3 x! Q
from folium.plugins import MarkerCluster# l( ^: H e6 P2 h
& q2 V* Y# ^# h2 ] f& `% v
# define the city map* c. F/ y1 I% s' K5 `6 y
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)+ ^; d$ j0 d5 J" T! T2 e) A
7 M, q7 g3 }- w& l# `6 U. _# F: E # 经纬度的聚类/ B) \' v# s0 q
# 在 NUS 的经纬度附近随机生成 100 个点;7 R, s$ S& G1 ~& @2 V/ _2 _
data = (* K0 }8 I8 x* S2 z9 D0 ~1 {
np.random.normal(size=(100, 2))7 F2 @% {8 L! z1 k
* np.array([[0.001, 0.001]]) +7 N* G! C* ^2 I0 h7 R
np.array([coordinate_nus]))% J" \3 {* m! z( C" Z
1 b" N/ o) E7 q% O4 Q. U
# create a mark cluster object
; Q2 Y! S: E% q2 ]% s# v6 W marker_cluster = MarkerCluster().add_to(city_map)6 M7 ?5 _% n) [4 s
3 z) m' C: }' O# V1 a* h# s: M* D
# 将这些经纬度数据加入聚类
$ m, R3 L7 P+ R for element in data:1 {, M: m J3 b3 @6 [
folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)
8 T+ D7 U7 s" O# m+ K& G8 H$ v* |3 z' D: E- T/ H' I
# add marker_cluster to map
2 X, S2 f9 U% F9 f! r4 X/ Z city_map.add_child(marker_cluster)
" A2 J+ I+ p& @7 \$ S
* v5 u0 z8 E1 k5 ~ # 作图
) }. J/ d/ b: J" r3 d9 t! |1 v# x city_map
& v6 I* }0 a% x2 `: V& E; U/ z * L5 y q6 V! _, ]8 U& K
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。 ( \# K3 A! Q- A4 x' t
参考文献2 X7 H( Q! b6 m1 y9 W# f6 u
Folium 官方文档:Folium - Folium 0.12.1 documentation 4 S9 l' z$ i( P4 z7 ]- ` l
0 _5 V. o' t. {8 W. q
7 P( n$ b1 V, e: l6 x) R/ z) r8 U4 E6 {2 P
0 N1 Q8 O! @( z, s; N. d: Q2 h |