5 d+ c* p3 \# z1 Y1 Q- f" w! n Folium 简介
: Q+ B/ J9 j) ^: b$ x J( n0 V 作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。
; ~/ e" u6 o7 Z w7 o+ F 创建地图
" K/ e* H. I9 U 通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。 6 B* o+ r# C: S9 F' U9 e! A
import folium
7 J/ B$ Z5 B6 \, @5 |. a1 N %matplotlib inline
' b8 x: `% X1 m( g. @7 `# p/ }( S/ A' o9 q7 P, l% @& f; M* ~
import webbrowser6 ^! x/ I' U8 C1 m& m' D
0 Z& A6 j' G# [( ` print(folium.__version__)
$ k* W a6 n2 Z2 q0 L* q
, \2 j4 P$ W6 v; K6 W* P- N K' L' Z # define the world map7 u# h! i7 e$ i+ p' q
world_map = folium.Map(); B6 Q" E* Z! M2 c5 ~# ~% [
# display world map: @5 H% k/ e( g
world_map
6 M# i$ j0 L- Q
) |4 c3 {9 }6 D) j0 B& d; N 世界地图除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。
5 R2 m1 }. C: n 在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。
% u5 o' k+ d8 m8 U2 E% C 有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。
1 G$ B" m* {$ E/ M- F1 e4 D1 u8 w7 K. { 另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。
( e T/ I, s/ q. Z. w Q # latitude and longitude in Singapore city( `5 {9 h! H( V! d4 I
coordinate_sentosa = [1.248946, 103.834306]
( _; _7 z7 k+ q ^6 ^8 e$ g) g3 |" e coordinate_orchard_road = [1.304247, 103.833264]
6 {0 R( S y; i/ h6 ?8 V# o5 w7 W coordinate_changi_airport = [1.357557, 103.98847]
. B( j* @8 f7 }7 C coordinate_nus = [1.296202,103.776899]' I7 e: h6 I$ Y$ g6 {
coordinate_ntu = [1.34841, 103.682933] f2 }5 j* b9 p8 }, G2 E! M
coordinate_zoo = [1.403717, 103.793974]1 O6 l" o4 j( T g5 k9 S1 }4 z
coordinate_ang_mo_kio = [1.37008, 103.849523]1 O6 M$ A5 p; S
coordinate_yi_shun = [1.429384, 103.835028]5 h4 S7 |3 Z, ~8 Q i. {
4 E6 s( E$ t3 C/ b0 G # icon) w4 D5 t$ ?/ S+ O8 o
icon_cloud = "cloud"
. |( Z" J2 h7 @. m* v8 I icon_sign = "info-sign"
2 @& B: A( x1 i+ R. x+ P. B3 c1 C$ d, k+ O! o0 L! k6 F) ~7 j6 I9 j
# define the city map
7 M7 W/ H# z& u: L; K: `; K" k # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}
: Q0 h8 ?- ~1 l3 k- r$ w city_map = folium.Map(
- `% M/ G0 B* w' ^7 s location=coordinate_orchard_road,$ P& H |0 b: G$ N4 B
zoom_start=11,
7 C; r2 \8 q8 T8 L6 @; M! C+ r" f tiles=OpenStreetMap)
6 e4 O% f; [) M/ q( [% V3 @% i& O; e' k) c5 C D
# add marker in the city map0 P% Q# C; k0 s; `
folium.Marker(
/ W: f) I7 ^) K# h coordinate_sentosa,
% l& ^5 \, V; x% Q) e icon=folium.Icon(color=blue)
: z& C/ I3 h0 y ).add_to(city_map)" ~" G2 |; U; W3 A
folium.Marker(
4 }6 |; f$ x e' X+ } coordinate_orchard_road,0 B( z* X5 K" P8 V
icon=folium.Icon(color=green, icon=icon_cloud)
- o' d4 \; g( d8 J$ J# x/ Z6 |6 d8 ? ).add_to(city_map)
" Z% c/ T, [( `' L7 v9 `4 _7 E( ~! K/ O0 @( n: W
# add popup$ j. L" o/ B' f; A
folium.Marker(& P" I. p1 d3 u! O. Y3 b! S
coordinate_changi_airport,
- Z3 G' w. E# G; } popup=Changi Airport,' [4 Q5 t# S9 g+ Y* E' p' n- k
icon=folium.Icon(color=red, icon=icon_sign)1 w& z9 }$ Q" @4 q% [
).add_to(city_map)% e, {6 s5 d/ L! W, S; w
8 V( w/ F- f% j # add tooltips and popup
# r; ^/ L& @7 O0 d" } tooltip = "Click me!"
# A0 g$ s5 v. m3 e% x4 k8 Z. _# y1 K: W
folium.Marker(
7 s; U- D. l. U9 @' ` coordinate_nus,
2 N$ e; i9 F, W$ W5 q popup="<i>National University of Singapore</i>",* I% d/ q/ k8 a4 q% {
tooltip=tooltip
/ t% V& x) R& l2 n ).add_to(city_map)
" N* N0 i' `& D6 W0 P
3 |0 D- |+ v$ j, G9 ?& r: ~ ^7 Z folium.Marker(
9 u. \- q2 }" f2 |7 ] coordinate_ntu,6 i0 U: ~5 ~2 t! l& G! k
popup="<b>Nanyang Technological University</b>",2 A2 x+ f: J4 p8 k4 f+ N/ j2 g
tooltip=tooltip
4 B- v: o9 |- e" x+ ~% ?7 M ).add_to(city_map)
/ ^( z1 r5 c: s3 ~# n; r" i! W6 i' u9 G9 ?4 S- F+ M
# display city map
% I- y- ?0 P) R; U* h city_map
4 `; t$ ]! @$ K+ E; G9 m# b6 n1 n Folium 的经纬度作图(OpenStreetMap)Folium 的经纬度作图(Stamen Terrain)Folium 的经纬度作图(Stamen Toner)有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。 # k% P3 s0 L& v G" _" X& i* ^
# define the city map
8 V# c: ]. x) @ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)( D- w) L: d6 ?1 R! y% P Y6 `
/ S- F! X9 M& L; {# `8 T9 `# D # 在地图中添加经纬度, add latitude and longitude in the map when click
- f2 C2 P2 {$ o5 c city_map.add_child(folium.LatLngPopup())6 E0 y# ?, l# J$ X7 s/ n
* Z4 C: x: I- k2 Q G' Y city_map 2 n* }; ~. i/ t- K) s1 @
1 V, d( P+ q5 y; F
几何形状
! @9 n4 q* m/ z( I 在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。
+ G9 U7 L( U& r4 t/ q- p # define the city map
9 i9 S9 P& |6 H7 a! C+ s1 o7 t city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)$ A7 x- r, p4 j; G3 A1 z, S! ]8 h
7 d0 S3 e& `8 [1 R `4 k' U/ e6 Q
# 在地图中如何添加形状2 X9 p- g% e2 ]. f9 L" Y
# 多条边
, g/ }% N" m4 N/ F( g. L points_1 = [
# V% V# d, U3 _$ F6 _ coordinate_ntu,
$ Y9 V/ n# x- n& U J4 d7 c coordinate_nus,
6 E; ?: p ]- [# @2 i6 M coordinate_zoo
. j" V$ |6 S: F/ a' W, B ]6 |0 ~# N: ~+ I
4 C9 Q f$ o/ s# @5 U% G# X$ ` # 在 city_map 中添加多条边,第一种添加方式
/ M$ ^% ^1 T3 A/ `- X city_map.add_child(folium.PolyLine(
3 i) @, _; f: X! Y locations=points_1, # 坐标列表& ~: `) M% R) y: k$ n
weight=3, # 线条宽度
2 _' f% O/ N& u$ B: I/ [4 V: ^ color=gray))
- {4 d2 ^" c) N, ]) T3 _$ f8 I3 K! L" z( Z# f- {* ~: B
# 在 city_map 中添加多条边,第二种添加方式" F* f/ P0 j6 n Y" ^4 _
folium.PolyLine(
8 ^6 N1 {2 a# e+ d locations=points_1, # 坐标列表. K8 W/ \8 X9 u0 N6 e) r
weight=3, # 线条宽度
6 t( e# m2 j9 \& V! `4 q" G color=gray).add_to(city_map)# ~+ I' |4 |0 F
3 e$ C/ X- ^1 K) w # 多边形$ L8 E' A! |. A5 v ]
points_2 = [9 Y/ e- S) K& V- v
coordinate_orchard_road,7 j' r6 h1 S6 O- N; {* {
coordinate_sentosa,
. ~" Y' p o, I g* A0 N% [8 p8 | coordinate_changi_airport
6 p1 M9 n2 v2 X% a/ q ]
! _$ q# v+ e# O2 x) x# \& e% X
3 d$ N& U, S3 v# d) P+ f; G6 s" G5 @/ B city_map.add_child(folium.Polygon(
: V; e; K) M6 Z' c2 P C. ]( f3 b locations=points_2, # 坐标列表, k" m* c8 v7 o6 [& f
weight=3, # 线条宽度
# b3 t F" \" W1 K7 O$ z; C6 j color=yellow))
: T( u2 A- E$ [4 t" N9 M/ ?( }$ ]& x2 \. }2 a5 B9 [. |
# 矩形
+ }$ D" r% u( t. a$ S ~ bounds = [
$ u& d0 v5 N! T+ J coordinate_ang_mo_kio,
3 d# s0 R: S6 k; | A coordinate_yi_shun
& F* z6 K# u. d8 ~ C ]
7 E- }! _9 E, q+ S& o N$ A8 S/ u5 I: M! ?" s( v
city_map.add_child(folium.Rectangle($ B9 E) ?. t8 B
bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)
: t0 F6 [3 C$ W) W$ Y8 G' w weight=2, # 线条宽度
O2 n4 u) i- M color=blue))# Q& s" s7 J' R/ ^: Q' k
" \! U0 y/ r9 _1 ~/ O u, F6 |5 D& S
# 圆形, circle, radius units meters% U) d% E6 k$ H
folium.Circle(8 H7 @: q' N: L8 t2 }5 J1 u
radius=1000,$ x" D6 {7 x; \' n# v
location=coordinate_nus,
+ d) l: t" x, Y+ M2 |" | popup="National University of Singapore",9 ?/ t. U$ J6 o2 e7 O0 h
color="crimson",
$ _" f6 K Z9 a' \/ N# `* j fill=False,
- L: A% \$ |$ C ).add_to(city_map), u2 D4 C# A' Y
1 `2 m, t2 o8 Y3 f, ]
# 圆形, circle, radius units pixels
$ o! {% _! B8 {- B% R& T% P4 F/ w7 A folium.CircleMarker(9 H7 N, l, p6 T; u; u9 J. a
location=coordinate_ntu,
+ _# B+ m. C# J( ~ radius=30,' [ V1 h1 }0 p% Z7 {& V8 _( }, I. U9 @+ _
popup="Nanyang Technological University",
7 p( Q) y1 B% Z. {. E- c g color="#3186cc",& ^- O% c/ L/ i& t: e! [
fill=True,
" L' ^' q1 D+ N$ U/ ]% p fill_opacity=0.3, # 透明度
% f' p5 S- x/ v, ~0 |3 O0 k. T fill_color="#3186cc",$ j/ F4 X; @6 H
).add_to(city_map)
: T/ m: X/ V; z5 K4 K
: p ^9 S( M5 e( R1 x3 K$ ^, R% \ city_map ) W) }# r W, \
Folium 中的画出各种形状热力图
0 _) l i; a2 n3 U/ p& l 在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。 8 `8 n0 a* N% C
import numpy as np4 Z N8 Y- T! c7 i7 Q! ~
from folium.plugins import HeatMap# Y, Y$ D* k- d
6 \1 x* J; T$ x6 j # define the city map; ~. `& Q( r8 h E& N" X* P7 C
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11). i) b6 @' `* v. X/ c
' q+ c; K7 H# F% a# N. U) c
# 构建随机数据6 R2 z# P' k( J& h, C, [$ }( m# M
data = (
3 j) \+ I1 k2 z5 \ np.random.normal( |3 A: ~* {8 Q/ _. Y# Q( k6 ]+ i
size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +6 P4 b6 b' Q. V5 S
np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])
j) S& R3 E/ B) m& Y ).tolist()/ s7 K' A# \4 G& F
' k; p' ?/ p! v M2 m city_map.add_child(HeatMap(data=data))6 _# a# I! v2 W1 _ E
city_map & E- e0 ?" `. k5 o- D0 w
! o5 I" I2 t4 e! \# B. z8 Z W 除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。
* @5 _5 J! C. `7 H) u import numpy as np. u. u# ]1 b- s; L; \/ q
from folium.plugins import HeatMapWithTime
: ]: ?8 x, h q1 H' c6 G" a- C' S6 q
# define the city map4 \) h4 z& c% W0 j
city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)
- E/ D. ?/ s( \ l; x0 n6 S7 Y, c G- ^
# 使用 numpy 建立初始数据
% ]. n; [) D H& \' [$ ~6 t initial_data = (np.random.normal(size=(200, 2)) *
; r J+ Z' e+ Y& }. j np.array([[0.02, 0.02]]) +
( A8 B2 P2 b; A# J1 v& R np.array([coordinate_orchard_road]))
: r$ a6 |* j+ @& w% ]6 K0 L- b% r h* u
# 建立连续的数据$ R" F% e4 a5 `# f
data = [initial_data.tolist()]
3 f+ m2 k; p0 q& e for i in range(20):1 o8 {) }& z1 t
data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())
/ D2 i ~ o: @" Q: G* p/ U& ?7 ~: t/ y5 P; h9 c
# 显示连续的热力图# Y: h$ P4 u; m9 t4 ^( A) Y6 T/ W
city_map.add_child(HeatMapWithTime(data)), r. f; b: U% o6 T, g) q
city_map
( F3 c( F: w$ b$ T& y, K + g" ~( ~, o% @/ i
经纬度点的聚类0 Z6 h3 g4 L* ]% Q4 n2 T! Y( U
除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。 6 C5 G9 l( {) j
from folium.plugins import MarkerCluster
! ?: N/ b9 K0 I! H0 l. L' o# p2 h2 F7 v7 a, j3 a
# define the city map
% u* h, `! z/ {, }% _ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)- `/ r4 |) ~7 j4 J5 m) {
' u+ _, C+ i" o* O7 |3 v: l # 经纬度的聚类
" x, t2 W5 ~) [, w+ m/ D" R # 在 NUS 的经纬度附近随机生成 100 个点;8 h# O8 m2 {% t+ E
data = (
- i- c7 L8 a- T0 u9 i3 z5 } np.random.normal(size=(100, 2))7 r& d) L o! Z) _! O
* np.array([[0.001, 0.001]]) +3 U# h* t. K: ]) U. j) v# P8 V H
np.array([coordinate_nus]))
" I2 a7 \: U) o0 Y5 @6 g0 i: ? I& Z' q' X6 Y7 T
# create a mark cluster object, L7 x' t) p" J5 h" i+ i
marker_cluster = MarkerCluster().add_to(city_map)5 f4 V3 ]+ } i6 b
! n8 L G3 r5 K; T8 A+ p. R( Q
# 将这些经纬度数据加入聚类
2 \; m! I+ e* K$ I3 V0 H& m. M: M for element in data:
9 t7 p( @/ H: K3 [8 M! W! ~ folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)% |8 _ [7 d- `9 \- I) j1 A
8 e2 t. k: _. h3 c4 l # add marker_cluster to map
* H3 E) o! Q+ F2 m city_map.add_child(marker_cluster) t9 e+ _5 O, i; w7 O( N3 G, R/ L; e
9 l% Q8 K# Y. K) }
# 作图
5 Q9 D8 _! v }2 Z9 I3 i. D" d& s city_map ! i" R7 A5 P* X g" y
# P. o' H- |1 M0 x) d. G/ e
以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。
: G" A0 X# K! D/ `1 ?3 j 参考文献. [$ Q1 Q6 l4 s3 n7 r0 L5 Q
Folium 官方文档:Folium - Folium 0.12.1 documentation ; F; Z3 i+ v# ?' O; c8 h
# A% R0 b) ?, s9 p( W- q' W! Q( `$ `4 R5 j$ }
4 K/ y! `0 k0 E3 ~; i( N) E
c. n0 N9 H& c0 { |