收藏本站 劰载中...网站公告 | 吾爱海洋论坛交流QQ群:835383472

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

[复制链接]
$ g1 p: b% }# D1 O1 m

Folium 简介

( e8 \# X$ b( s- }7 T

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

5 a, |5 P6 F$ Q' c# k: R- f

创建地图

# G2 x/ h) F% F9 K

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

a% @, X0 o# T: Z0 H
import folium 5 a- `' u; z- w* Q %matplotlib inline% Y7 |0 k9 I2 | V ! p, `' q% F3 h import webbrowser$ K o- }7 o! E" |; O) j+ u7 W 7 q6 t- [$ a& z9 J6 ~- A print(folium.__version__) j" S( X& e5 O' m& Q. g ; l7 @% s3 ]: K( T @0 }) |, z # define the world map % z4 z* q0 G# q1 g$ _5 N world_map = folium.Map() 0 T0 f+ n# s6 Q7 B- W( n # display world map7 I5 R& g2 ^; k3 D world_map9 S) z7 A5 s* {, L9 Y, m' t
' I0 T8 L! j3 U4 \6 q8 l
世界地图

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

' h/ r- \. C0 P) F/ j6 f* z6 T, Y

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

- d+ @6 {2 ?7 b! K" J- b

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

, f! u+ R; M; P5 A' g* K

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

+ I: C8 Y( c4 x% R# h" t7 a
# latitude and longitude in Singapore city; k/ h4 X5 G' ^5 l: R) G coordinate_sentosa = [1.248946, 103.834306]. R0 ]9 a9 O0 p3 d v0 X coordinate_orchard_road = [1.304247, 103.833264]- \# s9 ^9 l! o* T8 U! l coordinate_changi_airport = [1.357557, 103.98847]/ [7 j8 g% t9 E; C! [% x coordinate_nus = [1.296202,103.776899] , s$ j& n* d6 C3 c( C1 |0 c1 c coordinate_ntu = [1.34841, 103.682933]7 [& ]5 y3 {, O4 \# ? coordinate_zoo = [1.403717, 103.793974]7 a- ?8 K! H8 O- o# Z coordinate_ang_mo_kio = [1.37008, 103.849523]4 z* [, q9 f: P5 R$ o+ m/ j coordinate_yi_shun = [1.429384, 103.835028] - w! G& h; U7 U8 m2 \) J3 o- x- R, `5 m9 W7 N3 Z% B2 p9 o # icon+ g0 b, y" t8 V5 r6 }5 c icon_cloud = "cloud"; h/ u9 F8 u% R- o0 n1 b8 u0 @ icon_sign = "info-sign" ! D' |2 w8 Q& b* n9 M8 l8 \) P- u$ U! `% O' q # define the city map7 ]" r/ M$ B! ?& u+ @ # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}# K4 j1 | r& A0 \- W city_map = folium.Map(; ], X: \1 F9 ? e) \+ K9 N location=coordinate_orchard_road,0 t" l7 F# _. t8 O1 v2 Z zoom_start=11,. V$ a$ S2 x' Q6 b+ f tiles=OpenStreetMap)+ \; V& B+ @( q! u. n' i& o$ l$ u+ X $ V3 O2 {2 ?- @0 C( t) Y4 j0 q6 ^" n # add marker in the city map ! _3 Z) \ J% [: ?/ c folium.Marker(. b. c' G# C# j coordinate_sentosa,* E* k6 U% c! e4 ^ icon=folium.Icon(color=blue) 7 W& ?5 ~- ~! F+ }3 y6 w ).add_to(city_map)1 Y2 ~4 R" N; w. r folium.Marker(+ B$ L; M+ j9 z' p( I* ] coordinate_orchard_road, 7 H& ?* F3 r3 |6 R, f icon=folium.Icon(color=green, icon=icon_cloud)7 G0 M1 l8 F0 T5 C" ?. e' ? ).add_to(city_map) 7 W$ \ ?' ` ? + q- i% O' O/ S7 x # add popup ( E# ?! G! G+ ?# x& s2 S# c) x, | folium.Marker( 9 F) t O. e# @4 p$ [% ^/ X coordinate_changi_airport,3 ~2 v% U& b" Y- m0 ` popup=Changi Airport,: [- \" ^; ], b6 _7 a5 p0 y" ` icon=folium.Icon(color=red, icon=icon_sign) ' k0 S( Z$ o- } `& E ).add_to(city_map) & C6 Q; P6 `2 K! P: o) A: L& S3 S " i* F0 D) y r$ P# b8 N # add tooltips and popup2 i z' f( b2 `- i- _) r- Z& h8 h tooltip = "Click me!"0 |. ^ r. L3 L7 _5 b% x5 V . O8 @. g$ j9 [& f# R folium.Marker($ a8 O# \2 T1 B. h( [9 j coordinate_nus,1 o y" L9 c. I2 q- c+ } popup="<i>National University of Singapore</i>", . {# t0 ^# s w tooltip=tooltip " a, F/ q7 K3 V5 b( h5 o) Q ).add_to(city_map) ! S' ^1 v( B; v f3 W2 Y$ x2 e- F$ ~ folium.Marker(- C- g3 Y; X) _: B' u6 u5 b coordinate_ntu," `8 |3 D% V& S popup="<b>Nanyang Technological University</b>",8 X" [; M5 O7 M; Z$ e A$ d tooltip=tooltip . e3 R' i. q0 f; ~ ).add_to(city_map)% J4 w& Z! E% i& c7 n% I g, Q$ M9 V& Z r # display city map- n+ j# Y, t) u city_map
# i! R0 J& i7 N, j" C$ }8 X
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

0 e/ f% @) o* @6 L9 z2 z
# define the city map 1 N5 o" \ h6 G" x- @ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) / w# T5 a' p( v, U' a/ V2 _ - w! L) M* b5 S* ~5 i# I/ @7 Z( l # 在地图中添加经纬度, add latitude and longitude in the map when click % B( S1 }( Q7 D5 t) x4 E city_map.add_child(folium.LatLngPopup()) 9 j2 j7 i7 u; c& }$ e, E 7 D$ Q3 W; f& L; D9 c city_map
0 o+ h3 V- j' q6 \( C. k' R 9 o( t5 U- |( w

几何形状

! K+ L# \: c# n3 t

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

( t4 a7 I' [7 ?$ D& v
# define the city map 8 O1 N6 v: p! i/ {4 R4 ^ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) $ R8 t. m4 c9 K' L* L% S; _# u7 P2 L* ] # 在地图中如何添加形状 3 S4 O; h0 y4 x! j$ `- { # 多条边 U$ ~+ `% Q) h* ~4 d6 A points_1 = [4 E1 r3 \# @1 o8 C/ \' u" G coordinate_ntu, 5 Z8 D8 `0 t/ B- L coordinate_nus, . ~9 x" J+ b9 t( g0 o+ s coordinate_zoo5 Y* ?5 |8 _, W1 k0 j ]' |+ v1 ^ Z" K$ |, n8 w 8 J, T% E3 e y F3 ]2 z # 在 city_map 中添加多条边,第一种添加方式 . S( \/ u. S" M% p6 F8 P8 z) P city_map.add_child(folium.PolyLine() s1 Q, u7 S8 d locations=points_1, # 坐标列表# m3 x }7 C2 U' L# G. S weight=3, # 线条宽度8 E' v* k. w9 p Q6 P color=gray)) ( r4 Y3 o+ `- _% I$ U: u1 R0 }6 k) H) O% W # 在 city_map 中添加多条边,第二种添加方式' ^8 q# J6 I% w7 `- \0 Y* L3 Z folium.PolyLine(( q" x; }3 ~1 D7 H+ g+ ?) T locations=points_1, # 坐标列表 $ z. D" R* B3 f. U7 i9 V+ G# B% e- o weight=3, # 线条宽度 ; F. w5 k8 I; ]. a4 h" B9 i& y color=gray).add_to(city_map) ; i0 K$ h- ^% S; S $ F6 P" Y8 ?/ W" J # 多边形1 a9 |7 m) ^7 F# U. w' e points_2 = [. Q$ C$ ^6 ]; l, E coordinate_orchard_road,' C# E3 d9 C5 j! l e coordinate_sentosa, " g. v& G4 ~4 q coordinate_changi_airport 6 E3 r1 O* t# r/ U( l ] - K1 T$ i9 ?; @: q9 L% p9 H& ^$ w& P! i: B city_map.add_child(folium.Polygon(+ z9 P- @% A& Z8 G4 `$ E. C locations=points_2, # 坐标列表. o$ E% m4 q8 u& {# { S5 C weight=3, # 线条宽度 2 @7 g$ P f- Y& A- J8 @4 K color=yellow)) $ n5 o/ J6 d! B. n* w' g( `; K ' B% u# n& d) f$ A) ~ t& a7 C: K # 矩形' s' _0 `) z2 b& w# m# m) M% x" B1 j bounds = [; q/ n# V5 m) S# V coordinate_ang_mo_kio, - c7 H/ D8 C' {4 u; g) O coordinate_yi_shun 3 ^& }$ d& r$ T ]7 Z# d* q; o$ K0 S! I6 L6 L9 F' s9 T " Y" O! y/ D4 h- Z$ d city_map.add_child(folium.Rectangle(/ a. Z; D( `3 o9 ~! y& K' B+ i/ i, ` bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) 8 k o, g9 ^6 _% G7 g) ]; n weight=2, # 线条宽度& Q+ ]4 X/ E* K# t0 x- b color=blue))2 f v; C. g5 i1 T; S . t8 ~2 w: ~6 j8 b, T$ D q$ ` # 圆形, circle, radius units meters& ~1 g7 W2 w/ N2 Z/ f6 ]8 n2 u folium.Circle(% b1 f$ I) U; P) n" e$ U radius=1000, # }0 G/ D- r) f) M/ S location=coordinate_nus, 2 }+ Y" x6 K. l; ^9 G popup="National University of Singapore",6 Q* p" W v n* D5 O color="crimson",! G- l/ J6 p2 c* H0 z, N fill=False, - G7 ^5 q: ~' L" B9 G* E ).add_to(city_map) + Q0 k b& F+ ~3 g 1 E* U6 D M+ d I- p$ o # 圆形, circle, radius units pixels& w O _: g& f, `* Q9 j- _9 v folium.CircleMarker(9 W! x4 j$ q1 n" w4 Y c location=coordinate_ntu,: Q' ~/ a, x# n& m radius=30, , s* f" H& [$ I; z popup="Nanyang Technological University",2 P: T4 I; b' W8 }6 J: b color="#3186cc", ; I W7 y Q2 B/ Q fill=True, t! o) G1 o- L1 ^. ?) k fill_opacity=0.3, # 透明度 9 l9 `3 v7 V- y N4 }: s" ~' W fill_color="#3186cc",0 g) k) w) E ?+ C G! Q ).add_to(city_map)' d1 `4 \' ?! S' D5 l' a# | 5 O9 {) o6 g% e: p1 y7 M city_map
8 d# e$ x, a7 }: S V& @& J. q& y
Folium 中的画出各种形状

热力图

% F. a; {- w3 I

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

) g& ` X8 r# i$ t) C Y8 j( b/ a$ K
import numpy as np- j! E6 A) S8 j: w from folium.plugins import HeatMap8 ~, U, }: P4 u# j8 ^, _/ R 1 x: o/ P: v. y9 ^& l+ d$ N* i # define the city map 5 D* t- _! V9 a0 T; }% C city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)4 x* ~8 I( {0 i' w- h' |( z ) o- X& A; r) E; Z( V; J2 \/ ^ # 构建随机数据 7 R# }2 l+ a0 P1 m3 {- Z7 S data = ( ]- v0 {9 Z3 [: _. `/ x: P np.random.normal(8 c* H+ C6 @$ z2 I size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + . O6 v9 h+ E3 W0 Z( k, B np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) ) g0 C) v7 Y, O0 x ).tolist()7 }. a2 B% D" ?9 Z * d0 [+ u' N. G6 T1 {9 D" U: | city_map.add_child(HeatMap(data=data)) # P5 {& G6 C+ X/ S' N. B# ^( j city_map
6 q4 x0 |* u% j7 c2 y- _4 ?7 ]& O
1 {6 w- x; W: \- {7 n

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

7 x! Z6 z6 ~% _4 {( G% i% L
import numpy as np 0 ]. h2 T1 m" A. j2 f from folium.plugins import HeatMapWithTime$ h2 E% f/ {6 z' f4 H. z! m ' P/ a: [6 u7 P* X* w# z3 v$ p( n0 ] # define the city map* y5 S/ Z5 E5 q3 B! ?7 r) _0 a2 b city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11). `( \* f% ^+ c9 z- C- r ! a2 g' `: T" A6 Q% T+ x" g) I # 使用 numpy 建立初始数据 0 O4 {# O) @+ N) N' H& K: j3 q) q9 \ initial_data = (np.random.normal(size=(200, 2)) * % w/ `' ^5 O+ `5 j+ k! _ np.array([[0.02, 0.02]]) +. M$ ]1 J9 c- O3 T/ @ np.array([coordinate_orchard_road])), g1 ~9 ^. t0 e. c: e3 {$ m$ B* @ ) c6 g# g$ N: U/ n0 i7 C: @9 T( a # 建立连续的数据 ; d4 v) _. A% W data = [initial_data.tolist()]1 M' Y( g1 V* ]( W: b for i in range(20):$ m8 \; `$ I+ T, @3 ? data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())6 |" {2 k* K4 }4 P 9 M* @1 d. @& @6 U: l9 A # 显示连续的热力图( A! }+ g% U6 {$ |5 B G) \ city_map.add_child(HeatMapWithTime(data)) 8 Z( ^3 A S @, Q9 R0 n city_map
7 N" q$ l& q% d! B( e. l1 ` ! C9 I' ~2 z* p7 T

经纬度点的聚类

' \/ V: B# l/ ~' [$ {

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

* ~; c6 p# l- e, Q* i% l
from folium.plugins import MarkerCluster 7 S/ t! B+ z$ y5 O' h D0 N }3 N \) R- {! X! d # define the city map 6 {7 a1 E+ Y6 R' n) @ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)' A6 O) {1 T. v' _. c) n" ]' h9 d* ` ; f/ p5 v( e$ d0 Y7 X9 x2 a6 _7 r) p # 经纬度的聚类' e/ l2 }2 d7 Q3 W4 N # 在 NUS 的经纬度附近随机生成 100 个点;- l6 H" Y- \) W data = (" ]/ X! |& E& ? np.random.normal(size=(100, 2)) * b7 [$ R$ t( R" E: ]( J * np.array([[0.001, 0.001]]) +! H8 e7 u! f" E. M+ @+ S5 g np.array([coordinate_nus])) 5 Z) s- t' _# m 2 D8 G X+ w0 X" p2 q. h/ ] N # create a mark cluster object ( F( s/ i4 l( s# O3 J* Q1 t marker_cluster = MarkerCluster().add_to(city_map); O d# f" s; [: G0 z) Y5 U7 v + ^% a2 `* W9 H1 z # 将这些经纬度数据加入聚类 1 e4 a- F% G7 { for element in data:5 V" r) b) d4 N8 S7 g folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)2 ?. _9 L& H3 ~4 t' e4 u 3 i. @* n7 F9 i( R. X # add marker_cluster to map' G) w/ s* r( x4 P7 L% {# _ city_map.add_child(marker_cluster) 4 U) U. k' Q) ?! w% O) F 1 d2 H; J0 _9 V C7 Y2 o # 作图 $ B4 o& ~, C' x: Q ~ city_map
/ S1 W2 X" J$ i 0 J/ z+ k. c- W- y+ T0 ^3 W, X9 W

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

! R1 _; ^% F9 H! F2 n- U

参考文献

+ j$ l; B1 t; e# }) G& c

Folium 官方文档:Folium - Folium 0.12.1 documentation

0 ~* b' w" O. @! B" W- d& A* X% R) h% k8 R0 w2 C8 `5 e5 H . v) W6 A! B. y1 z: s( c$ z 4 _) @2 [2 f$ R" b4 T; t3 k / u- \' h7 i* ?
回复

举报 使用道具

相关帖子

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