% _4 p/ h W9 D: j8 B# z& J
Folium 简介
. P( z. q! v1 P. @: _1 b/ y7 u 作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
6 L8 P: m0 }0 w7 B 创建地图( w7 W% ^( Q$ X
通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。
: \! _) k" T/ k$ R# r import folium
e1 @1 N+ S# {: }0 l" Y6 B( a/ ] %matplotlib inline7 e$ {$ s1 O) f9 O, c1 x
2 b, Q( V a4 x" B& s# I import webbrowser2 p$ T; L* [) S1 I- s
8 z8 x, E S; ?: k8 y& R- H5 [- a, L/ w
print(folium.__version__)& f: N+ R0 ?4 ~3 c' R( B
& z) H, L* m7 N! K) g- o
# define the world map1 g) z8 G/ Z) d9 f* q- T5 Y6 t
world_map = folium.Map()2 b! C9 d% w+ k; T: E
# display world map/ g4 l: ^9 E( i/ |
world_map
. [* H- {# y' n9 S9 o2 y
' F6 v4 f2 h) l, V( y" N 世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。 . }8 w- I3 f5 S6 w
在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。 7 Z6 i3 v. n" F5 P J
有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
. u# r8 ^ U5 ^/ c/ ` 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。
4 R" g3 H3 e* \9 Z. H/ d$ ^ # latitude and longitude in Singapore city
; H$ `% T9 K, k8 p coordinate_sentosa = [1.248946, 103.834306]! I6 f: s' d0 m& Z' v4 H
coordinate_orchard_road = [1.304247, 103.833264]
( C7 j3 n0 I7 |2 y2 x( i9 V. q coordinate_changi_airport = [1.357557, 103.98847]6 U. K: ]8 _4 s
coordinate_nus = [1.296202,103.776899], ]- |0 G J0 V
coordinate_ntu = [1.34841, 103.682933]3 x9 T+ O& [2 h8 @! m! o
coordinate_zoo = [1.403717, 103.793974]
% P& m& B( t6 b- Q coordinate_ang_mo_kio = [1.37008, 103.849523]8 z0 J3 r# }) F6 C8 G, B
coordinate_yi_shun = [1.429384, 103.835028]+ k# t1 H# k% D( e* S" S
, b1 e, p' e$ h9 S # icon; Y& B% l% j) A# R. `
icon_cloud = "cloud"# w. ~2 s0 E7 e8 W7 z7 _) D: w5 N' T. w
icon_sign = "info-sign"9 W q5 E Q; i, W
" t/ f- X1 [$ K4 y1 w& X) O8 `
# define the city map
( ]5 T; f' q: I # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}0 q/ p3 q% k3 w
city_map = folium.Map(
! p/ ?+ {4 T: A" G8 f location=coordinate_orchard_road,
3 M2 j1 j7 y: m! f" B% R( k; {/ y zoom_start=11,
6 n0 S& b$ }2 k# N tiles=OpenStreetMap)+ I4 ?0 B1 ^1 D0 P
+ [( X" a& }; E; I
# add marker in the city map
) F2 h" D6 L5 I L2 M \& R folium.Marker( E* r9 o. x+ }/ E# l
coordinate_sentosa,* i7 H7 z! E5 E# F1 U
icon=folium.Icon(color=blue)
6 ^6 ?8 k% m; V2 k" } ).add_to(city_map)
! Z) N! R: }9 t3 O5 d/ b4 w L folium.Marker(
# [% u- N3 c! C7 q( ~ coordinate_orchard_road,
, i# k& E( e" \" H5 _ icon=folium.Icon(color=green, icon=icon_cloud)
7 {' b+ f+ v, h ).add_to(city_map)* O; \* c }' x5 s2 I
8 h2 H4 W5 z @$ ]1 E ]" x- h # add popup
4 L) s0 k3 y8 g% x# D+ T folium.Marker(* k, t2 _& b" P3 [4 [ [4 R8 \
coordinate_changi_airport,
" R0 b- t0 V8 R2 f popup=Changi Airport,) E* F8 M. T- l! {2 z
icon=folium.Icon(color=red, icon=icon_sign)
- v& K# { E# z6 T. w$ f7 C# W ).add_to(city_map)
7 n' l, _- U) r4 G9 t( Q2 w9 N
4 e, A6 I G" C: [0 L% e2 k' Q # add tooltips and popup
3 e; t) l& s n0 t; o5 s; F tooltip = "Click me!"- z& r8 g" G. |- p& ~" {7 M" _
8 w$ k {. V* Q/ q8 ] folium.Marker(0 z8 W [& m) e* D6 u5 K2 k* D
coordinate_nus,
5 G' `4 ~1 H8 x6 f" [( c" @ popup="<i>National University of Singapore</i>",
- |) ?3 A- \: F8 g: o$ @ tooltip=tooltip
: {1 C* S3 ~ i Q7 w+ t- K' [4 r ).add_to(city_map)
/ Q7 }8 P; t3 T, }
+ h* ?5 T) X( a0 z1 N folium.Marker(
( b$ L( L, r( g5 J g) o Z/ H coordinate_ntu,; A% g1 F/ x, x/ X1 |
popup="<b>Nanyang Technological University</b>",& N5 j0 y" g$ [8 f& }2 u- z# Y) k
tooltip=tooltip
/ u+ s9 w' _, V) j8 Z ).add_to(city_map)
2 G' M4 y! H' l6 q2 U2 b# B3 |- p. E, v, |# S+ |& h. S( k* j
# display city map
8 u9 ^+ M8 Q0 y( [3 ?% _# U$ t7 A city_map 4 R$ O/ J! c- o6 d
Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。
, W- `( w0 i, x, \+ p; x # define the city map9 V! t7 t8 w5 n. R, f
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
. X( P! j2 s, |% e" D: z; N* D
5 B a0 k- `3 I4 R3 c/ W # 在地图中添加经纬度, add latitude and longitude in the map when click
6 e( M1 q6 o4 H3 j& L city_map.add_child(folium.LatLngPopup())
- |; Q* {$ [3 r& d6 p7 q. T9 K* v8 Y* e6 W. a" [5 o
city_map
0 r/ `- X9 ?% g' a2 q) l4 s ( V; Z% I( S( U- v0 T0 p7 K) Q, [1 i
几何形状6 y5 }! G+ ` a( t' I; i
在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。 ( b- L2 E9 d- ]/ n
# define the city map
9 N) c% E6 N* G city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
; f& k7 L9 f. A8 `# G
* j1 s8 z8 q) y" y # 在地图中如何添加形状" P1 m, v! g, R8 D
# 多条边
( k" `$ N! G3 |6 r% b points_1 = [
, Y3 R, S# P/ ~) g4 B: ~1 D% y coordinate_ntu,' n x/ j# } [ l8 {0 d
coordinate_nus,
$ E" }) q ^* b% o( S+ { coordinate_zoo. ^$ E' ^0 v G3 k
] C/ \* G- \$ i) s& \. q( B; v
: X C0 D$ i5 |7 `$ w
# 在 city_map 中添加多条边,第一种添加方式1 j8 @1 s8 `; A& s: `
city_map.add_child(folium.PolyLine(: W* y6 v: q0 g9 F6 `# y
locations=points_1, # 坐标列表
% ]1 q/ O4 g$ j/ R/ ?& x weight=3, # 线条宽度
! t* h! ], P2 x! y color=gray))
# y1 c* r0 Y# c2 A: q* G' |! ?6 t7 ]
# 在 city_map 中添加多条边,第二种添加方式
/ ]. I! h2 C" o; b$ N }! ? folium.PolyLine(* S0 k0 U+ D) y# D. P* w
locations=points_1, # 坐标列表
: _+ `7 z# ~7 [7 J m9 A$ ?, C2 x! O weight=3, # 线条宽度
& D: c7 W, P l- B/ i color=gray).add_to(city_map)! G+ w, g4 d6 n, ?; y
# _3 j w9 r) B8 m! u # 多边形0 o, K4 b# |7 @ y7 J
points_2 = [9 O: c2 r" G0 k$ Z- l
coordinate_orchard_road,
& ?3 o3 a2 X! Q- o' A7 O1 l coordinate_sentosa,) w% Z+ t1 p% Y5 x
coordinate_changi_airport
- } z! }+ [- ~# s w( s& ? ]
# a+ Y& L/ }8 x% }, }. Q B* l1 W' v! m
: p* O l( ]0 ^: E* K1 L city_map.add_child(folium.Polygon(! k( B' @: }- Q7 ^ I
locations=points_2, # 坐标列表) |( z! S6 T1 o J+ r+ ?
weight=3, # 线条宽度
+ [1 \7 ~& d8 k) I color=yellow))
3 q: a- D0 l! {* u3 F2 _% n1 u6 }, H; k8 Q
# 矩形; p( G) z: ~5 M/ m- ~. y b
bounds = [
- ^6 Q. `8 g7 C) l4 @$ J coordinate_ang_mo_kio,
# `& o8 z; R( ]) M* e: M0 } coordinate_yi_shun
6 @5 m% Y- X6 w# f, o" D ]
' z9 s4 d5 X w: ^% F7 L- \8 p- i8 k$ \: X
city_map.add_child(folium.Rectangle(
6 y' _/ I, S2 }2 ]; T6 k' x e6 [ bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
" \2 _% j$ ^6 Y. @, S* W5 U* O6 H2 j weight=2, # 线条宽度+ N9 H, E# I; S% ]
color=blue))
" \- I' _9 U- n0 f6 D) d% w- B9 \" t4 U" J( D, s
# 圆形, circle, radius units meters+ Q2 n9 Y! r, i# _' {& E
folium.Circle(5 l: V7 m; \; u, ^' e5 S. l) t
radius=1000,
$ W0 P0 G6 j+ I8 z2 w- P* z location=coordinate_nus,
; K8 x K- i& a- W' _- A popup="National University of Singapore",
+ ~: [5 t* O6 n% R1 K0 W( a9 D color="crimson",
5 }7 F" f. I+ D% [* X+ L- c0 | fill=False,8 ^% Z v+ } r+ r8 c D" {8 @$ }; `
).add_to(city_map)2 B2 w7 K/ Q8 j1 B/ e
( A- }' k$ o0 g* m. @ # 圆形, circle, radius units pixels( E R! W: E* V" Q+ [# G- i. A) i
folium.CircleMarker(
9 E8 a2 H& x2 W location=coordinate_ntu,
" ?- t8 P5 z ~ _) a i' M9 A radius=30, [/ ~- ^3 K0 w2 Y1 `
popup="Nanyang Technological University",/ N" Z: |( [5 Z9 X& s l. f% e
color="#3186cc",
! W1 ~: s/ y5 ^7 F fill=True,
5 a" W# S! C" _, `$ [ fill_opacity=0.3, # 透明度& H( R; C u% r3 S
fill_color="#3186cc",- Q% L2 f+ U' |0 [5 u/ q
).add_to(city_map)6 O5 k5 s0 g; J& o! [5 G
. G6 d' k, o& v5 D* R/ n city_map 8 ]# I: M' N# A' D, p
Folium 中的画出各种形状热力图
. S' B: d' t [3 b0 r+ \ 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。
' O: _& S6 o- c. x3 u% P import numpy as np# K: R1 Z. s* n
from folium.plugins import HeatMap
, A! @+ X: u6 H, s5 T( r7 @5 j: o4 X; T
# define the city map
/ a1 P3 f4 L: R/ y7 a, e6 s# u city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)' g \1 O, y& M4 y
4 z, H( r" \$ O* @$ L2 L- g
# 构建随机数据1 A. Y" y) |" t3 p$ o
data = (
+ ?& ~! R/ D4 t g1 K np.random.normal(
3 t( K+ G' @+ d U) R. f size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +3 `0 R" P3 Y* ~9 b' r) ~3 L# R7 t
np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
8 Y9 \; b! N8 G3 v ).tolist()( T2 m$ [/ @1 N$ U v" D7 w' T
# F/ t4 {5 I; ^8 o( z! w city_map.add_child(HeatMap(data=data))5 |* C' O8 [6 Y$ g Y a! e
city_map
; D# h5 K- P: r+ ^: u
, h# [" R# \( ] l 除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。 5 W1 p0 N/ \! u3 H5 r& j
import numpy as np% ?9 a6 a6 _, G! `
from folium.plugins import HeatMapWithTime% D! s, Y% E$ ?- m
, i9 v5 O R+ ?( G% ?7 j # define the city map
2 J9 @5 U: E/ G* R$ _ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)0 J# w+ K# a: w+ h" t+ ?
8 Z" H) V6 U' L8 G7 G. Q. o( C# T% } # 使用 numpy 建立初始数据. p) h1 S1 H& ~; V4 l w
initial_data = (np.random.normal(size=(200, 2)) *- _# V$ |. A- e* o t9 R8 t
np.array([[0.02, 0.02]]) +2 ` s' \6 H( ]/ {8 H; `
np.array([coordinate_orchard_road]))- t/ f6 v6 w" u$ S
' k; F! z, J( v! l- b/ ? # 建立连续的数据
0 f& {5 h! e( z# `4 E4 A3 M/ ? data = [initial_data.tolist()]
' ^( ~. a7 b# q3 _" J# n' g$ d7 T for i in range(20):
! z4 @9 _' F& N% ]; i data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())! A4 E5 u- C7 E
- L0 V+ X9 M# G# Y$ X+ D- w& B
# 显示连续的热力图
1 F, I5 o0 Q/ H8 d1 G& _ K city_map.add_child(HeatMapWithTime(data))
- s5 {0 h' A; N! O# }5 k% k9 X city_map
8 z6 N0 a6 b9 c/ v J8 d3 d7 @! X3 V
经纬度点的聚类/ s' {6 C6 `" g* r4 F
除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。 4 ~1 C2 z+ j, K* {
from folium.plugins import MarkerCluster
) b) d3 f" _- r( g! Y" k3 p+ c" `% E! n* H6 v% l4 R
# define the city map
( n0 ^+ o7 ?) ]# ]0 s city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)$ W& Z% _8 `! |/ [6 C4 o
* g, p5 t2 F$ ] x # 经纬度的聚类7 d! X! P! ?$ C
# 在 NUS 的经纬度附近随机生成 100 个点;/ V3 J# f: F1 v, p. e
data = (
& V. \% j! U+ U. q np.random.normal(size=(100, 2))
! f3 K( ?8 o( T0 m0 n2 Y * np.array([[0.001, 0.001]]) +
( E% n, k7 m1 C V/ ^: p5 B) u+ M5 u np.array([coordinate_nus]))1 b& N( h) G; H. C
2 S% b; M7 w; O7 S; n
# create a mark cluster object: p9 p9 m( O O" i
marker_cluster = MarkerCluster().add_to(city_map)2 U) e* j; o) v, f
; @9 i; s% n/ q# Z9 l6 [# z
# 将这些经纬度数据加入聚类" O2 C6 O& r8 E! a( L
for element in data:
- r; R; p& ~1 _2 W. b/ r folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)
+ h/ o7 R0 ~- u" g4 ], N3 Y2 G9 g5 P7 O& P, V& Z$ F: ?
# add marker_cluster to map
3 e3 k' B4 G2 U* s# v city_map.add_child(marker_cluster)
3 n b" K' G) v7 z" T) ^- q6 F E* P0 E2 A' Q" ?# E) N" v# H
# 作图
- e; J- N5 j* I city_map # j. J/ o5 K2 k4 } I
' J1 ~( k j) C7 g4 o8 O
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。
/ i4 l/ e$ Y6 x 参考文献' q! v3 g+ [* W1 @; y3 ]9 i
Folium 官方文档:Folium - Folium 0.12.1 documentation
9 e4 e' A5 _( @8 o
4 q2 \* a3 e; U: {+ Y( ?: g6 o* k: ~( d' B F/ E
8 }. f$ e) a( B1 D) e6 K% }# ? a$ |9 c* B$ V1 c
|