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

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

[复制链接]
z* Q" l( V" v p' Q

Folium 简介

& e+ f# A, _; K! U

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

9 u) c" s1 U3 A Q2 G [5 d/ ]+ x

创建地图

; b! t# i- U) c! o4 c, M% W6 }

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

# `) _$ M4 _; m: l
import folium+ K* N; {& N/ h. T %matplotlib inline$ @6 ^* ~+ g4 B g9 O# w) D4 G* k 6 ]4 T0 q' g6 ^; n6 D8 ?5 q import webbrowser! G+ I' `" f# P p5 E8 y4 ^; I ) a" [9 H( d+ E; g- C print(folium.__version__) ! ?# s/ P$ M, @/ I/ s) \( {4 K& w( d" _; Q' L. Y+ O3 h2 E # define the world map0 h# E6 {3 L. J1 C+ r. ` world_map = folium.Map() ' {7 k! J/ T, G9 X. C9 n$ { # display world map/ y1 m/ |7 w( W' e/ U, p; P world_map \9 z( Q3 h/ K' ]5 l8 V1 _: V
' {/ R! t5 @# q, v
世界地图

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

, `- z" ~) L5 |( P8 e/ o. {2 \ d3 Q

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

* [+ E- ^: V, R2 l, @) K

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

5 F& L: c+ i$ r5 ^# B% l. p

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

0 ^7 g) r& V) \# x* \
# latitude and longitude in Singapore city. M% j$ H. o7 n" R coordinate_sentosa = [1.248946, 103.834306]- f0 b6 I' z7 J, m8 a! a/ m2 T coordinate_orchard_road = [1.304247, 103.833264] / p3 j: k9 P) h) w6 W coordinate_changi_airport = [1.357557, 103.98847]* f, B) ]$ B7 T. W J9 E coordinate_nus = [1.296202,103.776899] 4 v& N6 l0 x* g coordinate_ntu = [1.34841, 103.682933] 5 K7 b3 S( K3 Z5 m" _ coordinate_zoo = [1.403717, 103.793974]) t# T* O8 X) A4 I; e, y coordinate_ang_mo_kio = [1.37008, 103.849523] ) V- d8 m1 J z+ w- c/ N coordinate_yi_shun = [1.429384, 103.835028]/ ]* c4 S5 \: m) w& p " ^3 }, P1 l7 c3 j0 H5 ?% M9 f # icon - r: L/ S: [7 J1 ?) [ icon_cloud = "cloud" ( Y' T$ X% Q& I+ j+ p0 y icon_sign = "info-sign"9 f# D! T2 k! e* m3 k: j% P7 @7 ~ ; k9 F! @- ]: ]5 i0 d$ ~ # define the city map 6 b, P4 K: N8 W( f! Z5 K # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}5 L$ b6 ?7 Y) ^+ U* W- n city_map = folium.Map( / q- I/ C D; U1 z# k; } location=coordinate_orchard_road,, N5 @2 c0 b4 ?) p zoom_start=11,! B/ I9 p! M2 s tiles=OpenStreetMap)+ @( ^4 {0 d: k9 d; O1 H& b* v6 T - U5 T9 i# T% [5 x3 p # add marker in the city map ; H" k8 s0 ]" K5 ]4 X0 H5 Y folium.Marker(. F- C2 n3 J# c6 M7 u coordinate_sentosa,: e: }5 p8 F" }4 z; D* q! O icon=folium.Icon(color=blue), v6 K7 {( M5 M ~$ U u ).add_to(city_map)* ?. o8 W5 B' z" R# T1 D folium.Marker(+ f, v% I# H& e% u coordinate_orchard_road,: L9 n/ M2 x' a5 C: c2 E5 T, q+ e icon=folium.Icon(color=green, icon=icon_cloud)* K& [+ q: y# b# \7 |: F9 Q! V ).add_to(city_map) ) }8 J- `% D3 A) r o. n. S! B3 r/ ` # add popup! r; n g$ P5 T' ?. T- ] folium.Marker(; I' I3 n) f3 K C coordinate_changi_airport,6 q1 F1 L8 F4 g2 ^& k9 l popup=Changi Airport,# ^# a; I5 f7 r/ W icon=folium.Icon(color=red, icon=icon_sign) : {! H. M# O/ z1 P- o5 c, T ).add_to(city_map)1 V T. W! H6 T' l9 W ] 5 H" ?9 {. |# i! C, ^7 M # add tooltips and popup * p; C! B# l2 ~8 F! _7 e tooltip = "Click me!" 4 `, C8 i& Z: p8 ~2 `& r# a3 h3 W2 |2 t folium.Marker( " t6 }" R9 e8 S# B+ L% @# r coordinate_nus,: Z G9 P% S' Y/ L2 Z' b: Z1 ` popup="<i>National University of Singapore</i>",# r$ T: c% F, o) p# U& J/ z( Z tooltip=tooltip( V: y: B: w) ~! E2 G ).add_to(city_map)' ?1 Y5 W* [: E/ o a3 G& ~4 N3 ~ 1 A$ K( w3 v- I0 {3 V/ r9 P folium.Marker(" y1 M/ y/ S; W4 n coordinate_ntu,8 w/ `0 n- S; Y) B; Y3 F. L) ` popup="<b>Nanyang Technological University</b>", ' j, r$ h* b. K# o tooltip=tooltip ' y5 Y8 y0 w- I& R% i1 N' j ).add_to(city_map) 3 N- s7 A9 X2 B# [% ^ - Y3 m# L4 j5 h* }3 ~) Z # display city map 5 p, n1 X+ _9 J, F, c city_map
, q2 \3 i/ Z1 V
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

+ X: a0 K0 I% F5 O- l' [2 N
# define the city map * A" A8 g: p. z! X$ G* W( R city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) " W) R8 r- d9 f; b; M 1 s5 ]4 n6 P" d$ L0 O7 l- k. M/ U$ V' {' E # 在地图中添加经纬度, add latitude and longitude in the map when click 9 V1 s% f! _. t/ H& ~$ ^ \& o city_map.add_child(folium.LatLngPopup()) 7 w& p# c" y- j5 q( V* S- i4 _, a* _: J$ D city_map
% y x' [8 Q( t1 h d1 Y( `2 X/ _9 p

几何形状

1 `& `* F* L) _$ x6 N; z

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

1 K& J. `" v' ]* I6 M' r
# define the city map4 a3 D& z$ ]6 ~; \' J2 `: ? city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) * k0 t$ F5 B2 \% ` ' Y. \+ V/ `# t+ n J( B0 d # 在地图中如何添加形状, T3 ^) X, M& ?& y- }$ Y) s) U. B # 多条边* S% G0 k5 a, [, P6 O1 s points_1 = [+ {. d: J& ]6 l" {# P coordinate_ntu,; s+ T/ ?" V E+ \" n coordinate_nus, / D7 }9 u- [( h u coordinate_zoo 5 e$ y9 F3 u+ c% M C9 N8 D ]: y' X+ T8 h% D& e0 z3 b 1 f5 D0 d* C/ C6 T # 在 city_map 中添加多条边,第一种添加方式 9 a1 b: `# b) s% E' H& D l city_map.add_child(folium.PolyLine( 0 ?# ~4 {* K/ ]* }0 n locations=points_1, # 坐标列表 * I: w3 t2 [( P" Q weight=3, # 线条宽度- N9 N. e% q4 G3 w$ m3 l color=gray)) % X5 U& h1 g; r% y3 E: O9 V* _3 H( r, ^6 V% O+ U # 在 city_map 中添加多条边,第二种添加方式 & I9 U+ ^2 M$ V! z+ t- N folium.PolyLine(8 z1 K( z) K, H locations=points_1, # 坐标列表# c2 k7 s7 g) [! {$ B, b+ }* I3 P weight=3, # 线条宽度8 V( V# x8 E9 t4 o/ n color=gray).add_to(city_map) K9 f! f& v6 i/ c6 P, [ , ?" t9 D( A f; | ~ # 多边形) l: X* v. H d& u points_2 = [ t8 r$ _% K) b8 M7 n) Y0 Z coordinate_orchard_road,, O7 Q8 H. J1 o( u! _, } coordinate_sentosa, ; m0 C" C3 b. N+ v8 M) D1 a coordinate_changi_airport 4 x4 N. G& F( ^; B5 q ] ' J8 ?# v- E* I4 ~7 ~ & x+ r6 H6 j1 S/ N- w. C% G city_map.add_child(folium.Polygon( ; g7 ~4 y$ K6 p8 ^0 G locations=points_2, # 坐标列表 / v: I* I5 f) h: H weight=3, # 线条宽度 % y# n* ? Y0 U5 O$ G3 y color=yellow))) u7 T4 Z- J$ Z3 h & [" p* c3 G, }2 R" X # 矩形" _; W. ?3 A8 E0 [1 U( m bounds = [2 r2 \5 D& ?8 T. Y coordinate_ang_mo_kio,/ G/ F1 b/ i! C; _ coordinate_yi_shun4 [2 Q$ T* j3 `) P ]7 l, f3 E% n$ l: Q' W0 P ) }% |9 q: @7 i. o4 [9 p4 s& @ city_map.add_child(folium.Rectangle(/ M8 l5 @3 T9 Z bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) ) a3 ]1 t$ b: j5 G! W6 e weight=2, # 线条宽度$ [+ |7 m) f+ ~2 } color=blue))& `2 B7 \- C2 o& M& X9 a/ \ + }3 z P0 D7 M/ @3 w # 圆形, circle, radius units meters' ^- i; z M$ b+ ~6 f( \9 e/ ^ folium.Circle( ! G9 }. I( ?' S7 E& ^, { radius=1000, , _# a1 q; L& g location=coordinate_nus,8 h1 W, |" ~) f/ c! d popup="National University of Singapore",. h' N- M0 [9 d/ F9 @9 Y color="crimson",, B+ ?% @6 \+ Y/ o fill=False,, i. A5 x8 _ A! j ).add_to(city_map) 1 Q4 K) n p8 k ! }5 h" L; O0 U J, |/ i # 圆形, circle, radius units pixels+ {' I# g" F+ n8 z# _$ y folium.CircleMarker(' c- d( V5 W5 Z location=coordinate_ntu, ( c& _1 Q8 j( f& _ radius=30,( r7 K2 `# B% V* d popup="Nanyang Technological University", / a$ W! }- _9 T E) F/ |3 C. D v, d color="#3186cc", ( Z0 G+ H; K } l fill=True, / n3 I( L5 M! j9 h5 }" Y fill_opacity=0.3, # 透明度 ) ^ P6 ^+ x; N6 M fill_color="#3186cc", $ I, Q/ G6 x# @4 l. c' K' D ).add_to(city_map)5 e0 j I5 ]8 ~" i$ k) L8 K4 g: l: s- X / ?# X+ W' r8 U [4 r; c$ u city_map
1 r# F4 A1 p, x" j
Folium 中的画出各种形状

热力图

, z) W0 L6 p* s. o) @

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

3 U, T9 D! f. B
import numpy as np , }- H* q: z6 o( j. _! ^ from folium.plugins import HeatMap : Y5 C. _5 N6 S5 y' w$ e6 o8 x/ x% B- K8 [$ Z/ G% | # define the city map& \9 S6 T0 k( k/ u; \ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 8 l- q, o7 ?& C w3 b! Q 0 }2 z5 F; z. V# y8 v& [4 w6 @+ W # 构建随机数据 e9 A6 @& ]- L+ s" f7 K data = (4 T* K/ i5 X6 v. i- R) r$ J9 m# ? np.random.normal( ! X, k; o& b0 c$ X% k size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + " b# K7 b4 Y% a9 N N, q' v np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])( ^* m% _' z+ ] ).tolist()- j+ _# M& g% c$ _3 ~3 Y! `$ k l9 }. j( `; K' l3 z! C city_map.add_child(HeatMap(data=data))2 D- i8 v9 |; l! p7 G city_map
+ O/ u$ Y% B0 K: l7 ]8 A$ H
7 s" B3 B" N7 C* C' l O

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

; U* [! y/ {0 w E. k2 {
import numpy as np+ F6 E0 z( \0 X from folium.plugins import HeatMapWithTime 1 {3 ~/ [5 V+ E9 \4 ~. s# O @2 \2 }3 K1 C # define the city map / D# H" P/ I# A/ H city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 6 U/ \2 b+ ?- c* b2 y+ |( r* W6 ?( L" N4 D" e0 T9 T # 使用 numpy 建立初始数据 6 f+ k- U5 C8 ~ initial_data = (np.random.normal(size=(200, 2)) ** m4 D" j2 l# k! i6 Z5 n6 }0 a: r np.array([[0.02, 0.02]]) +6 x4 B- w/ g' Z3 C1 i np.array([coordinate_orchard_road]))! C# [$ Y8 y4 ?5 W 8 b( N2 H) z9 n/ |4 j7 s/ X/ C # 建立连续的数据 8 S0 s$ ^ ]' p! O' n3 T$ ]3 } data = [initial_data.tolist()] ( \' H- m G" S: R for i in range(20):! p: v) \( D2 i! L data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())- o# O8 {3 `4 Y2 u( Q9 j* D 1 C" \% t, b$ W7 l2 [5 r5 G # 显示连续的热力图 * `. _1 j! z( i. B# c city_map.add_child(HeatMapWithTime(data))4 ]3 `" n$ Q, z' G& K/ _+ S" U city_map
. a% J6 H% N5 v/ w2 t: r ) W4 c, S0 I8 c, t+ z

经纬度点的聚类

) F+ q/ h: ?/ L. i* B% J" C

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

& X' e- W4 I6 \/ _+ s" ?
from folium.plugins import MarkerCluster3 h* |' e; l1 I! t 5 y0 j0 j; h/ I$ A # define the city map. A# ^ u+ {& S1 L$ d# ~; v city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) ) P2 P8 V$ p+ ]' K5 |' z8 q: j & }* c$ n3 Z- e( i3 c # 经纬度的聚类 # m$ @9 Z# O3 O2 x! @ m # 在 NUS 的经纬度附近随机生成 100 个点; . i- h' B( b) x$ c6 m data = (3 F, o$ y( @4 |" m* } np.random.normal(size=(100, 2))$ a6 Q; b; w1 e+ ~ E1 r * np.array([[0.001, 0.001]]) +' X; g' W. `" A( f/ d+ I1 w! X np.array([coordinate_nus])), k' a8 B6 c& I% n/ _ ( D: s& h t8 v9 r! Z/ [: d: b # create a mark cluster object# @ a4 J; t: c7 T h marker_cluster = MarkerCluster().add_to(city_map) 4 ]! g8 m0 }% ?/ B - ^5 h! ?8 d- S* |8 g # 将这些经纬度数据加入聚类& q* R0 V) t# ]0 ~; Q for element in data:: B$ Z9 K) k) ^- l8 d folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) # S& l2 R* c/ Y; k. B* W3 ^, F $ Q0 t9 F$ P- G A- p+ {4 \ # add marker_cluster to map 3 T' N6 t o% a1 Q! G; N city_map.add_child(marker_cluster) X4 B& S& A$ G1 `% _+ L. O0 Q ! \" n! V* X3 I( P! H # 作图2 v# o+ _( A, P2 g city_map
7 a \4 P5 k8 f. } 7 G! f4 `- q* g: P& L

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

7 B8 a- x- S9 _, R: U0 x

参考文献

H; w7 g' z" t5 E" k

Folium 官方文档:Folium - Folium 0.12.1 documentation

, |( [: ?5 W4 r, ?$ Y$ z( Y 6 k/ [$ X# F; E5 K* \ / v* i- i. v) o) [9 J4 T. s 3 l% L$ Y# c8 Q1 v' J) o* T% D D1 I! A# i# ]
回复

举报 使用道具

相关帖子

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