如何用 PYTHON 绘制漂亮的地图?— FOLIUM 作图工具介绍

[复制链接]
" I5 `$ E6 b4 y

Folium 简介

# l! _' F' D( U: f0 r/ H# R+ m4 C

作为 Python 的一个可视化工具包 Folium,它通过 Leaflet 的地图服务,可以在 Jupyter Notebook 上实现可视化的地理位置作图,制作各种各样精美的地图信息。它不仅可以针对某个经纬度进行地理位置的可视化操作,还能够根据实时的人群地理位置信息来构建静态与动态热力图,甚至还能够针对经纬度的数量来进行必要的聚类可视化。本文将会基于新加坡的地图,对 Folium 的一些功能做简要的介绍,对此工具有兴趣的读者可以参阅 Folium 的官方文档。

9 C! E* D/ x4 J& r# K$ Z3 w2 y! S

创建地图

) Q5 ?2 u* m1 o, Z

通过 Folium 工具,可以直接作出一张世界地图,其代码也十分地简洁明了。

; D* |# U! u' L, x/ I
import folium1 [0 ?- u9 C$ n5 O2 q %matplotlib inline ; [0 l; k# C+ G ) S$ r$ ~+ H% m3 r import webbrowser % r% U9 \4 e3 o7 s2 ?; G" Q `6 L$ a ) p7 g% P5 g& d print(folium.__version__) 2 o, {" S, b- P* j, K( b# S/ R z8 |" a9 ?. p: b # define the world map 8 ^1 i5 v1 E4 l$ _0 Q world_map = folium.Map()0 K+ ?. I* o- @( b( [* l # display world map, |" G0 y! h# N7 e) \/ l- Z9 E world_map - |5 ^8 H. X. Y# G7 j1 ^( n
v1 v. q6 P$ [9 i! ]. B
世界地图

除了能够作出一张完整的世界地图之外,通常程序员最常见的需求是针对某个或者某一些经纬度,来作出一张局部地图。地图中不仅需要包括经纬度信息,也需要有街道信息等必要的内容,甚至需要对经纬度的标记做一些必要的定制化工作。

9 Q( Y c3 Y) [% c% l

在初始化一张地图的时候,需要指定它的经纬度信息,也就是 location 的位置。也需要根据需要放大的尺寸来指定相应的 zoom_start 值。另外,tiles 是 str 型,用于控制绘图调用的地图样式,默认为OpenStreetMap,也有一些其他的内建地图样式,如Stamen Terrain,Stamen Toner。

- ?" g1 s6 S ]8 f# Y7 ]

有的时候需要在地图上标识出相应的经纬度,此时需要使用 folium.Marker 函数。其使用方法就是直接输入相应的经纬度信息,以及 icon 的形状(例如 cloud, info-sign 等);除此之外,还可以对其颜色进行标记,一般是对参数 color 进行调整即可。

3 V6 r# v* B3 v: @. x/ w

另外,在某些经纬度上,还可以使用“点击-弹出“的控件 tooltip 和 popup,一旦鼠标指向该位置,就会呈现出相应的弹出信息。

7 u$ U+ z0 w- M
# latitude and longitude in Singapore city) L5 R, h* l( }, R coordinate_sentosa = [1.248946, 103.834306] 5 y& ?" [( }, l coordinate_orchard_road = [1.304247, 103.833264] ' k# {, x# G) O9 \: j, c' S coordinate_changi_airport = [1.357557, 103.98847] " d2 D" b% P% z5 E E coordinate_nus = [1.296202,103.776899]. F) v9 y: d+ v; F# O coordinate_ntu = [1.34841, 103.682933] 6 l; p1 h4 W8 a4 n; f2 x coordinate_zoo = [1.403717, 103.793974] / b$ y# N- |9 [ coordinate_ang_mo_kio = [1.37008, 103.849523]& b+ k' @% l, j' B' o: e, ]4 y7 o coordinate_yi_shun = [1.429384, 103.835028] 6 A7 ?: ^5 `8 v0 H# G2 P" E( I& `# e! j) A2 { # icon & K5 W% E) }: m3 \+ m icon_cloud = "cloud" & Q9 r6 d+ `& J; m- [ icon_sign = "info-sign" 6 t4 ^( u/ b3 h4 V, F: t: f6 V. n/ F/ E4 I0 @8 G1 J # define the city map 0 `7 i/ D+ r w # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} 4 Z6 A* W* x- K7 ~1 [! v2 j) ^ city_map = folium.Map(, j( F1 p& q& O4 v1 S9 U location=coordinate_orchard_road,) R ]6 l/ K# x6 B% F( n# L zoom_start=11, K% [: y& g# T4 j, a/ T. b tiles=OpenStreetMap)3 @* @" @# W, F/ R5 G 4 Z0 f# z7 g7 O # add marker in the city map9 _' g B5 M7 M4 e folium.Marker(/ W% o# B3 \- Y coordinate_sentosa, / S/ l" P9 R* S) u2 ~* E) l icon=folium.Icon(color=blue) 1 ~6 f' D* V7 u. B ).add_to(city_map)' \0 t( J4 p2 T2 V, L& R) f4 H folium.Marker(, x1 j! y2 o. A% g coordinate_orchard_road, % D$ y3 G+ y4 ~! Q8 b# p icon=folium.Icon(color=green, icon=icon_cloud) 6 [+ G9 k3 u9 N) C ).add_to(city_map)& c' i/ X9 M2 u+ i* f% J3 K- c. K % @' m5 X' J y0 ^ y5 ]& t8 `! _ # add popup8 U' P! i( D( u, S' |8 R: A/ J folium.Marker(0 w5 ^' X- d% Q% D8 W9 D coordinate_changi_airport,7 T3 Q* w: O7 Y# K# x popup=Changi Airport,7 U: o+ z+ J: W5 U9 B+ | icon=folium.Icon(color=red, icon=icon_sign) ( G0 e) f; c& U) P" p6 x/ l ).add_to(city_map)% t4 N0 v! }( {- Z" v . f) \8 w9 M$ z5 U1 d4 ^ # add tooltips and popup 4 J6 U# y) j7 ]% T( ~& ` tooltip = "Click me!"# D( F. D G! t- b 9 U7 t% q2 Y I7 K. R4 ~ folium.Marker(% l' z4 ]) p8 `; i* } coordinate_nus,1 |, Z6 h9 c& b& E; F' q8 ?& y popup="<i>National University of Singapore</i>", 4 z& Y* A z6 p& m8 K$ Z tooltip=tooltip 1 ^8 `1 x, v0 b( [6 X7 W ).add_to(city_map). c- P# R8 V% W. y* e # e: ]9 r% q) H/ h8 I folium.Marker( + H' r1 E; L! c) \7 g& \ m5 ~ coordinate_ntu,: j$ N6 A. u/ E" e. C popup="<b>Nanyang Technological University</b>",8 _% V3 ?# e# H, Z tooltip=tooltip; O+ k( x, _% s ).add_to(city_map)0 W, m& Q8 }) u O8 H 5 p4 H6 ^9 K2 d5 c3 { # display city map : _7 p- x6 F5 w% e! C, n city_map
/ r& X1 [) g) {( {' g; ~& [# ~* U
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

有的时候,我们只知道某个地点,但是并不知道相应的经纬度数据,此时,只需要使用 folium.LatLngPopup() 就可以在鼠标指向位置上呈现相应的经纬度值,可以方便的查阅和使用。

, c9 _ Y3 K, H& {: ^: j% O
# define the city map , j5 ~" }7 l1 f% w- I( e* { city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 4 \- h# w4 F/ c& j& v5 v* A ; T/ l( C- `- Z9 K8 j # 在地图中添加经纬度, add latitude and longitude in the map when click+ i) C5 k" x7 {" }+ x; W1 B4 | city_map.add_child(folium.LatLngPopup())/ k- x* @. y% ?' f - }& N: ?! I i T city_map
/ ]/ ^! n+ h9 v) Z8 f' C+ C ; C3 ?; C9 z3 M- c" E% V

几何形状

! [( O$ j+ e8 P$ O

在作出关键的经纬度点之后,有的时候我们需要作出相应的几何图形将其显示得更加清楚。Folium 提供线段相连,多边形,圆形,矩形等诸多图形,只需要使用相应的函数即可。

8 z$ z3 g& R5 M% L3 ]' ~' k. y& c$ |1 Y
# define the city map / W( c; t9 {/ q city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)6 a8 p0 m$ F9 E0 \' Q, G( @ 1 z0 c& P$ c3 T( `' B # 在地图中如何添加形状) w# V8 `' c# M, b # 多条边 % W y: Q0 k0 D: f4 a2 j$ Y points_1 = [) l+ C* O$ A; ?: i! o0 m9 t coordinate_ntu, 3 S+ m9 h* W8 H* y coordinate_nus,) @( ], C4 T. a% L2 b8 B" c9 P coordinate_zoo 6 J g9 N, l6 |: i, J+ S+ G. ~8 Q ]: O9 G4 c2 \, O1 r6 H9 `: ~ G* V; _' T! n! x # 在 city_map 中添加多条边,第一种添加方式( L$ z' F% F% f4 x. z- g+ Q9 l: W+ V city_map.add_child(folium.PolyLine(0 p9 Y; Q5 i" v3 e locations=points_1, # 坐标列表; |2 o a0 O) b. `" K weight=3, # 线条宽度3 g* i( d5 }& W5 d. t color=gray))2 w8 J, S; f5 r M8 s/ h n0 P + A7 U3 j0 b# W. x# ^: @3 N # 在 city_map 中添加多条边,第二种添加方式 # m6 |9 U/ |& |' k8 _ a5 O7 w: ` folium.PolyLine(& i; P* N" d" T) ~8 k1 S locations=points_1, # 坐标列表 6 \. V5 ?( k; l8 k9 S% i3 O0 b7 w weight=3, # 线条宽度 % R: M% B2 x% M) C' N5 A) n2 D color=gray).add_to(city_map) 5 }+ x! o9 A. W) M$ k) [ 6 T. b! J) K% {" c: Y! F# w # 多边形 q* v% {" T! R/ E' A! g1 \ points_2 = [ 2 j: A2 ?$ C' ?$ |: U* A" N% W coordinate_orchard_road, O- J- C8 x i$ {3 Q/ w( Q coordinate_sentosa, 5 G+ y. F' [5 R. z coordinate_changi_airport/ H7 ^* e; d' P* g0 s! K7 \+ s k ] 6 H1 I$ ~8 P2 v/ P0 M- ~8 P* Z. C n: k/ S- r$ Z. Q city_map.add_child(folium.Polygon(7 T7 @9 N n/ W( Q* J0 ~) r locations=points_2, # 坐标列表, T G2 p0 L; E3 ?# p weight=3, # 线条宽度 5 {; T _5 ]6 b color=yellow))5 k! L" t- z S4 m C5 M # B: K$ u2 m0 v6 H # 矩形 + n; m- t; Z3 | bounds = [/ P+ `5 A$ t* ?& R. S1 R& y coordinate_ang_mo_kio, : z# Q7 g' s+ Q3 [1 v7 {0 i+ H( S coordinate_yi_shun & t: s. A: _, F# m0 d9 s. w2 \* L ]2 W+ K4 o: r0 I9 S t7 x, Z" M . Y; v1 n, n& K$ b city_map.add_child(folium.Rectangle(0 z9 l6 g. Q' k bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)7 Q7 j* i6 X% Y5 ? weight=2, # 线条宽度( E) H6 u$ B7 ?% M4 Q5 i4 S7 p" B color=blue)): n* ^# h, R* m) Y( b- N 0 U+ A( B* g5 J+ b0 Q; B # 圆形, circle, radius units meters 2 Y. Y, ~: X/ U% z8 Y folium.Circle( 8 u; ^# r( e, q( y: A radius=1000,9 t$ T l, M8 y. A* E location=coordinate_nus,) Z/ T2 n4 y6 F( c2 N. E- ~* i1 I popup="National University of Singapore",- m. \* d. l/ D% V# D$ M color="crimson",0 O; {5 K1 J) c5 y fill=False,( Y. B+ ~+ L4 H- ~7 t( X) Q8 G* Y8 I% E ).add_to(city_map) 6 d( @- i4 K6 o - r4 D+ Z' e/ N) o- f0 W # 圆形, circle, radius units pixels 3 c1 R9 H4 G5 x# \ folium.CircleMarker(; r7 Z/ [+ a( A4 ?6 w9 N, }; Q1 F& t location=coordinate_ntu,0 R( x$ T% V0 ~/ ` radius=30,) S, g$ k/ l* k9 |( I4 T3 s: [ popup="Nanyang Technological University",$ L: R r/ g# N, X color="#3186cc",4 d! b8 A/ i0 R7 B' l fill=True, 9 v- W9 a0 J* Y% t% D4 w2 } fill_opacity=0.3, # 透明度3 }6 }7 }& a3 W6 M5 ]2 b8 R* R7 L fill_color="#3186cc", , v% w. R* ^7 d7 O2 K1 z( q ).add_to(city_map) / E: v% v" _ m4 s2 [, h# |) Z* r2 }! A% B city_map
$ n$ R; n( a. u. o! u6 C+ P n1 v
Folium 中的画出各种形状

热力图

- g1 ?! g' F+ Y4 ~( |

在实际使用经纬度信息的时候,通常来说会针对某个 APP 或者多款 APP 的实时经纬度信息来获取路况的拥挤程度,景区的人流量等信息,在这种情况下,就可以做出热力图。通过实时的热力图信息,我们可以获得相应的人流量信息进行必要的数据分析工作。

4 s( C) ^( O! s! u
import numpy as np ! X! m! ]4 Y) j# X0 U from folium.plugins import HeatMap3 P7 \ y' H) X" V / {7 A. U& S0 i # define the city map 1 P: b5 O4 Y; P. j2 K4 c0 y) q city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) & X3 o% t a0 U% [0 r" }. I/ k2 ? y- q# }7 ~2 E # 构建随机数据 : o& y6 ?6 B' Y! a data = ( 6 f+ x" n8 C7 Z+ G% [7 t/ o np.random.normal( 6 B* A5 K% l* r" H0 C size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +; q0 r( W/ j1 B4 e; ^9 y& T' U3 c, C np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])- I& z5 D S1 R' z" ? ).tolist() # R$ @2 o d8 @' w9 K; b1 U' c . U3 T% x% t( [( o" [) _* A city_map.add_child(HeatMap(data=data)) ' \# e4 k+ I4 C0 r0 z city_map
# R' R% N Z' e- R# `" f
1 L7 e' e) A7 ~! j

除了单张图的热力图之外,Folium 还能够计算一段时间的连续热力图信息。

' X1 d8 v; ~/ F Z3 p8 A) V
import numpy as np 6 G5 n2 X4 j$ c& q. _. K; w" a, _ from folium.plugins import HeatMapWithTime : _% T6 ~3 y A2 |, N" V+ E. V, c; g: i: X* k8 g, F% [' G # define the city map. h. v+ q8 F6 G4 M( M4 } city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)+ {6 q. p. a A+ v) E 5 q3 e* F* B5 M8 W. a$ [; w # 使用 numpy 建立初始数据$ R# m% l. V. P _5 v initial_data = (np.random.normal(size=(200, 2)) * 1 ^. x" I8 _6 Y( y: q+ t np.array([[0.02, 0.02]]) + 1 K$ B! w$ ]1 u- M( [1 Y. H np.array([coordinate_orchard_road])) 9 u9 `6 n/ | _! x8 N. p: V 3 A# o. D8 X8 q' {7 _5 W # 建立连续的数据 ) L& G- @, f, Y2 U1 Z4 F, n* o9 { data = [initial_data.tolist()] , W' R/ u; ?& O* F+ g0 J! T for i in range(20): + L' j" o2 r" y data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()) ( g8 T4 l2 L9 d) y# y E, o * b/ @5 Y! ~ P, V* M6 l3 O. F: _ # 显示连续的热力图0 V1 o7 n8 E% e5 }( F$ {$ ] city_map.add_child(HeatMapWithTime(data))+ M, V, S, W% a5 T3 u( t0 d u city_map
3 W2 @6 f9 v0 Z! r0 u 2 \" G! y; [8 K: v' ?, P

经纬度点的聚类

) b+ D* e, j( t/ b* G. u4 q! [+ \

除了热力图能反映人流量的信息,基于地理位置的聚类算法同样能够反映一个地区的拥挤程度,Folium 的 MarkerCluster() 函数可以对一个区域中的点来做聚类,在地图中可以放大和缩小,从而知道局部最拥挤的点在哪里了,最稀疏的区域是哪里。

9 x; \7 _$ v3 |; M$ }
from folium.plugins import MarkerCluster& s# J+ _( u" h8 p% ~0 w; b 1 I2 f1 o% x# ^; ^; C7 `! T6 L # define the city map 8 ?, w( r2 }+ ^ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) # b, Z' y8 r. j1 B) R/ i2 U# w! p% [: ^0 v7 w" @ # 经纬度的聚类6 D/ _3 t1 x0 R( U, U4 c # 在 NUS 的经纬度附近随机生成 100 个点; ( Y+ U% c4 ^1 h% S6 `% O/ o9 V data = ( 5 r; R+ G" x. C& }* B; ?+ ^ np.random.normal(size=(100, 2)) & @; |( c: |9 L% z% h * np.array([[0.001, 0.001]]) +/ l3 Z$ J" f% k) @. t# _ np.array([coordinate_nus])) 3 i, l, ]( d5 z- Z$ ]; N 3 a4 m4 e( _. ]! k8 @ # create a mark cluster object % |# V K3 u5 r0 D" j marker_cluster = MarkerCluster().add_to(city_map) 6 D5 Q3 p, `+ u. f6 X- X, p; {7 j/ ? r) r0 C5 H: C. d # 将这些经纬度数据加入聚类 + X x4 |8 H N for element in data:" o7 h ~! Q! e0 p Z folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)! `) ^4 G/ |5 v: U' O) G2 N # |+ D8 m }! w # add marker_cluster to map 0 j6 X, c& H, j5 Z% _! E2 c, J& n city_map.add_child(marker_cluster)! t- g3 ^6 D' ~4 P . R5 c. h) a$ V8 B) P3 Z3 e- `: H # 作图. p5 L. ]2 D% n. Z2 f$ S) j city_map
" Q; W) H) ~" n$ g2 S* o+ |% @ : Q0 L% e2 \) {$ E" h

以上就是关于 Folium 的基础内容,有兴趣的读者可以自行参阅 Folium 的官方文档。

u5 O7 ]" L* ?' N

参考文献

3 @+ g3 F6 W/ [( A/ {6 x: V/ t

Folium 官方文档:Folium - Folium 0.12.1 documentation

9 y( w1 e1 f0 i l( H% t% k2 k : B6 P$ I4 P/ ]" ] + D- B/ _6 y: l5 r5 J2 D- g: H , J5 M/ G/ R6 M$ t+ F
回复

举报 使用道具

全部回帖
暂无回帖,快来参与回复吧
懒得打字?点击右侧快捷回复 【吾爱海洋论坛发文有奖】
您需要登录后才可以回帖 登录 | 立即注册
冰死铁
活跃在1 小时前
快速回复 返回顶部 返回列表