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

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

[复制链接]
/ n& H8 \. T' X1 o* @

Folium 简介

" J3 \- P/ x$ C: J8 Q# I; Q

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

5 K$ D5 d. l: @" M/ a

创建地图

- X# y7 Y* U, ]. L

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

% h1 R/ E7 T, n/ Q5 b2 b
import folium 1 r) ?, q8 f) U ` %matplotlib inline ; C6 n z& ~4 L$ c4 u: G4 I6 D2 i; s8 e% K import webbrowser5 b. e, V& B% n+ F* s# ]3 S, Z8 o # }+ Q% a- U+ H5 i3 E/ l print(folium.__version__). v# z) r6 g4 \( |' k/ \2 D( j d- i - I1 J2 @6 w; j8 \9 b! A # define the world map: q! Y& Z- n2 j4 t. y world_map = folium.Map()' l: ^5 k6 T4 U+ m) R1 e1 H # display world map- m4 T# J, v0 `+ p9 a/ T world_map . i/ K% U' H, U0 _9 x0 _0 d1 b
% p4 m6 B' h3 y( R' K6 O; V
世界地图

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

. [; m7 d/ V: d- ^% |" j( `2 `

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

9 G, c1 ~- F2 F* P5 C. x

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

' F& _; s) h% n4 C: Y$ e

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

* ~2 S6 a! C, W0 S7 W4 z9 O4 H+ l
# latitude and longitude in Singapore city G9 v$ M! C' Z* g8 n! c coordinate_sentosa = [1.248946, 103.834306] & V. [7 M: s4 K; Q" w' A coordinate_orchard_road = [1.304247, 103.833264] 3 K( ?+ u E- I% L coordinate_changi_airport = [1.357557, 103.98847] ! m0 O7 t- k, a1 o1 u9 Z coordinate_nus = [1.296202,103.776899] ! {0 b9 H* A# P5 d9 V' B coordinate_ntu = [1.34841, 103.682933] - a8 E/ d1 S5 H% p* p" j coordinate_zoo = [1.403717, 103.793974]$ M- _ H* s1 B* ^: |0 N4 o8 N; \ coordinate_ang_mo_kio = [1.37008, 103.849523] 5 o& G% Y( I, |! O8 i% V coordinate_yi_shun = [1.429384, 103.835028]8 p) [( |) y- x* X8 i- c ) e `, q; y9 V5 ~ # icon0 g5 R4 z( c3 I5 M icon_cloud = "cloud". M! u$ M2 e! o( c+ T' B! p" _ icon_sign = "info-sign" 5 P: r" x! }/ O z- B* v; c) q5 _( w* J3 Z, G% Y* N+ g3 a # define the city map . o$ C8 ^3 L; ^ # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}: {$ Q: O" r( @# Y city_map = folium.Map( ' g' G5 C9 s# g n location=coordinate_orchard_road, ; E7 b! R) M! `0 M/ V7 @ zoom_start=11, 1 s5 n c8 P4 p( g3 E tiles=OpenStreetMap) ! \+ X6 R. [- `' _( O, x4 R& @3 A' s+ E' x, k+ w # add marker in the city map$ Y' c% G2 s; C. a; G. P folium.Marker(* j& }, y- h# T9 |0 R2 g( \ coordinate_sentosa, 9 c" f1 h+ M8 v5 z icon=folium.Icon(color=blue)2 o" W! e( `+ I' b, A, K7 Q ).add_to(city_map) 1 k6 K! L! n- N* h" u# I c7 D0 ] folium.Marker(5 ]" M$ @! h; T) a0 K$ I coordinate_orchard_road, ; G$ O; F2 J) j- B icon=folium.Icon(color=green, icon=icon_cloud) 3 d- l* n: d* ] ).add_to(city_map)4 l$ F# B% g5 X6 h' F 0 j2 z A/ b9 n6 h* H. p # add popup . k) K2 I8 i( w& F" j! R8 z folium.Marker( 7 x( o1 s4 `4 ?$ k coordinate_changi_airport,9 j+ L5 x4 f* i7 J* O8 @. K( a popup=Changi Airport, 4 O/ ^- \* ^/ Z icon=folium.Icon(color=red, icon=icon_sign) / n( o5 v# c T5 Y% R ).add_to(city_map) . o+ ?! v7 b) S6 }1 a& H8 S# Q6 u/ M4 \2 @5 c8 j# w# ? # add tooltips and popup# V: |' c, t6 k1 O2 ]8 D7 \ tooltip = "Click me!"5 ?, W5 ]$ h4 z! K2 v5 U5 I ' t7 O# u8 s1 F8 N, \1 d, V folium.Marker(6 T, |$ l) {8 D m3 [3 {' n L& C coordinate_nus, 5 [# h: e5 P& G- I# W- U( Z popup="<i>National University of Singapore</i>", - O% @9 y8 c! } m0 K& X: @% t tooltip=tooltip- O0 T/ R4 C' `& J4 r ).add_to(city_map) # {1 Y3 s9 ]( B5 V5 Z5 W% Y% { 6 o+ [1 J) w4 F9 H8 @! L folium.Marker(9 J8 q3 u) y9 j' ` coordinate_ntu,4 _( p; t+ J; R0 G2 ~( V popup="<b>Nanyang Technological University</b>", 4 c# S6 t- u" x( X9 u tooltip=tooltip1 i2 g, A1 G- a ).add_to(city_map)' u& r3 y" ~$ ^; }# b# e # K# _; ^: ^* O% h9 M8 K # display city map 1 L2 y g! C0 S city_map
4 P) i0 |6 J, Y: g
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

9 J9 \ O* o3 O, E
# define the city map 2 }3 V4 H/ u% } city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) ' N# L* i* j, b! M( O; { 9 a9 t) J4 d+ u* \$ F! k6 k; z # 在地图中添加经纬度, add latitude and longitude in the map when click & w; ~$ H9 T, v/ { city_map.add_child(folium.LatLngPopup()) % ~ a7 Z* X% l$ e5 I $ d+ Q1 e, n9 B# ^( @- g* i city_map
5 y& ?- ?! r- S) ?& j/ X ) F) |9 B* Y2 I& D

几何形状

/ n8 B. y) n/ J- r4 L$ e" Z$ e

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

. ?$ G5 _5 G4 e
# define the city map - a% k+ b* D4 S city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 8 O9 ? I3 `5 A9 N% E0 O5 P2 z8 t7 D( H( o1 H2 l8 `+ }9 { # 在地图中如何添加形状7 H2 }" ^, w8 M; W7 O # 多条边 9 d4 U J4 n! h points_1 = [ 8 f% C3 G7 [, s, |$ X coordinate_ntu,2 D" J( o4 k; K coordinate_nus, ' ^1 j; D$ d/ b) N: e coordinate_zoo ' u, i# d" |* P ], O! e6 h0 c% S; | 8 _9 p1 n& f" S # 在 city_map 中添加多条边,第一种添加方式 ! v- a. b1 ]) l" @2 T- e city_map.add_child(folium.PolyLine( 0 D% q" ?4 W; w0 R% y8 r' D locations=points_1, # 坐标列表 9 H; l" @4 ]) a4 x8 Y, F+ H9 i! h weight=3, # 线条宽度6 M& L3 b1 ~8 f9 J& B9 ?* ?& I* F color=gray))& D8 [' L+ E7 {7 S+ u 0 ?7 A' q3 s+ z) g8 g; x3 e: b$ i: Z, S # 在 city_map 中添加多条边,第二种添加方式 4 k9 a' p% t" ^4 |3 b- p. w folium.PolyLine( ( S& X& A/ Q: s5 i, d# ^ locations=points_1, # 坐标列表 * T+ A* F; ^! y- {5 ?9 y& J weight=3, # 线条宽度! `7 L" U, `; S$ Y. Y0 {, R color=gray).add_to(city_map) * [5 B8 }8 J. e z* D' O9 m* m- i; @+ |2 Z6 W% u4 @ # 多边形 - m6 ?. X; T3 A& h7 A$ m points_2 = [ n+ `$ H' `# G5 s coordinate_orchard_road, ' Q+ c# [1 h- W coordinate_sentosa,: {7 x: O* g/ G Y h# B% F) M( `1 O coordinate_changi_airport: j/ N3 h5 @1 J" Y0 g% ^+ d ]! ^, o3 W. }( J) ]$ b / x* u7 W a9 u& d0 p S; F n city_map.add_child(folium.Polygon( , C1 Q( h9 L8 c4 y8 ?) D locations=points_2, # 坐标列表; N& y& W/ w$ H; a) |) Q) s weight=3, # 线条宽度" A) o0 A. C3 S color=yellow)) : k8 `; S- \- i6 R/ w5 E* D2 ~3 X1 \/ {& @5 k$ F6 Z- u- w9 s # 矩形- f- d4 {# W% S) Q bounds = [ * q9 @4 J0 {. j5 k) Z* }! W coordinate_ang_mo_kio, & p4 o, W% X9 h5 C) U& A coordinate_yi_shun- i5 F; e/ z, k e- ~# l ] r9 ~7 c& s$ W) h7 Z1 u. i2 e i : @# ^ \& ^; h' Q+ R( } city_map.add_child(folium.Rectangle( " {! P/ C, K) ]1 M7 Y bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) ' N n. s( \4 k' A4 F4 c weight=2, # 线条宽度 3 |# F3 R$ p3 L6 U! K8 x color=blue))+ v* t: Q1 ^6 D + |6 e8 H1 x. A+ j3 u* m- i5 I) l # 圆形, circle, radius units meters ; i1 I- N. {0 ~( s7 f- g$ _ folium.Circle(8 `7 n3 e7 b7 \4 M7 d" g2 S W radius=1000, 7 v; B! ]. v s3 w/ p" B location=coordinate_nus,( Z" D3 p& n1 g e* a/ Z popup="National University of Singapore",1 z; o. \& v6 T8 F& ?' ~' l color="crimson", , F+ N$ |3 z/ ]( Y, N fill=False, ! t$ y* w X9 l5 s% T ).add_to(city_map) C, t4 A% @0 L+ L % z. F( K/ C2 @6 `; v. N # 圆形, circle, radius units pixels $ p) j. B) _- m5 Z0 @7 n7 s0 Z7 y5 b folium.CircleMarker(; }4 s+ |2 O' ~% u location=coordinate_ntu,. b% T' Y6 c+ f8 K9 T radius=30, $ Q I i( B1 J3 c popup="Nanyang Technological University", 2 y' k5 X9 N# B color="#3186cc", 9 c* a' Y- G4 O1 }" q- s/ w fill=True, ) O$ v+ p3 M" @+ O- k2 O fill_opacity=0.3, # 透明度5 B" E |6 h* _ fill_color="#3186cc",& a# M6 T+ \, _8 |! ^+ O" g" d+ Z. ] ).add_to(city_map) 9 |- m9 U H# J+ r( g) t6 u6 p, d8 [% k : K; ~+ l) t9 J+ K/ ~6 g city_map
' S* B" J2 M7 Y
Folium 中的画出各种形状

热力图

% p$ P' X5 i* S0 ^* K6 _; o/ W3 A. Q {

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

* j8 y+ z, z6 O) ]- z
import numpy as np# J# m. ~ `6 ^; Z7 @2 p ? from folium.plugins import HeatMap 9 d' G/ \' p/ C5 {) B. c4 Q# U( k% S) ?- D6 ? # define the city map/ e( J5 f, Y* A6 y% h6 d city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 2 h k# a) e/ l% @4 H- f+ }1 i; i- n9 @0 k: g0 T, C, M: Q& c # 构建随机数据 , |2 ]) J1 t q/ [ data = (8 |# E# ^* p& e1 u np.random.normal( - J- v5 p# T# f. b size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +; ^& D, m, C3 S% D2 o np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) + M$ ]; B+ Z4 b" E5 ? ).tolist() ! Y1 u* K7 c0 l) V4 ~% i# [ ! ~7 S: f6 W+ l8 O# S G' v city_map.add_child(HeatMap(data=data))' J* U6 Q8 W/ W city_map
2 R' R' G* u% |, a% J
$ |/ @ S% d% V/ P1 D4 h

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

4 S R4 _9 V4 p4 }& w2 M
import numpy as np# |1 \5 K8 n9 {4 w& n from folium.plugins import HeatMapWithTime7 F: Y y1 `- j + {8 }8 U: ^. d! l # define the city map% X' `6 i0 ^% ?4 q city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)9 z: f$ k( o$ ~7 n- ] ' n( s3 z, P4 { # 使用 numpy 建立初始数据 1 x0 @9 }2 n7 V4 u) p initial_data = (np.random.normal(size=(200, 2)) *0 {* }) y- M& |/ V; i np.array([[0.02, 0.02]]) + + V8 x; I, q7 I+ Z1 F3 Q. [9 l5 F. L np.array([coordinate_orchard_road]))) b( h* d5 k/ M n. m) L: s6 Y & J8 _ p" u% Q- M # 建立连续的数据 + P3 H9 D3 L( f& S data = [initial_data.tolist()] ; t0 F A6 T2 ]& f for i in range(20): 4 d/ @0 \4 c( l" D* l3 ? u/ c data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()) 7 s" ]! h) Z8 g" }* O v' H! O8 `1 z7 G' } # 显示连续的热力图 ! `* y0 ]- f5 ]6 D2 J8 z; J city_map.add_child(HeatMapWithTime(data)) ) c4 N6 c0 w C8 A* R( u city_map
) |, K% l. u* b( O& f5 a. \! Z 2 }4 k0 h* g* P- M- b, s% Y2 Z

经纬度点的聚类

6 ~# {! `5 @% u* m* ^ E

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

: E4 C( b/ C# {/ K
from folium.plugins import MarkerCluster3 a# s8 ~5 O% B, r8 p" A7 v 7 b3 M/ _& A) k2 V( R # define the city map5 x) T X4 k, y% E: N city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) ' h1 t5 x4 W0 C S" t ) X e& R, n) p2 ], {+ `3 S( ` # 经纬度的聚类 ! T3 r" O! C- q* O4 U" p1 ]2 p # 在 NUS 的经纬度附近随机生成 100 个点; 6 {" B7 r# V8 c6 b& S: _ data = (1 u2 ]9 z3 c5 y7 t$ L+ P8 W% } np.random.normal(size=(100, 2)) 5 l" f4 h2 {% X5 B* O * np.array([[0.001, 0.001]]) +0 \' j1 z8 C* Z6 u* P/ g np.array([coordinate_nus])) * p/ \- o. ]6 d& `9 {* g3 N$ s8 e9 q: E* A6 ^/ ^2 f) { # create a mark cluster object/ X P0 u. o2 _( S0 j# x' x, x& @ marker_cluster = MarkerCluster().add_to(city_map) 3 u% t5 ~- X2 X6 z; j) i : F- U/ H3 z; X, g% K" P1 _+ ] # 将这些经纬度数据加入聚类 9 }0 K0 \" T4 p for element in data:. \8 P; `/ N: x5 ? folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)1 D% H3 [* R8 t3 B: H 4 c, U" F5 ?. |+ f' F$ y # add marker_cluster to map 7 E, A7 i5 D; m2 ? city_map.add_child(marker_cluster)# ]1 _9 B6 b. S" P0 ^8 O ) M; E w$ V1 X& k8 e. L1 X # 作图; |5 o K% G: x" T. ] city_map
6 J2 U/ T- d& e0 H 5 Y" ^0 d# f" s$ e

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

: o/ L* j& l+ C) N

参考文献

6 x3 \% A8 ^ l

Folium 官方文档:Folium - Folium 0.12.1 documentation

4 Y6 A/ v: ^" a4 [2 P' F# B, D2 r- n+ f; @ " C c) v8 H4 c2 D8 }8 X, C4 e ! c1 x4 o9 i" v6 ]3 s- ~% H 7 X9 c1 K$ ?" \
回复

举报 使用道具

相关帖子

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