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

[复制链接]
/ I+ _: ^4 v- I- U( f* N/ |

Folium 简介

, T' N7 m+ n, }% Z# L

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

( A2 N% W4 G0 r. X: f6 A+ Y

创建地图

& Z" D1 ]5 a# N: j

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

) Z N9 S& w* B' L, U
import folium 1 m8 h p% Q( d5 j/ m) O+ Z %matplotlib inline " r0 h$ V: F$ N+ |- d6 \! { G ! \; K0 U0 M/ ]; n2 z import webbrowser * f+ D# d1 M& U7 j9 L' _" H & u* E; s4 }8 W8 s2 n0 c print(folium.__version__) / e8 W# N, L1 k* t $ k( Z$ l5 i6 n* m Q6 Q # define the world map" o. v7 T8 c4 Q4 G7 V1 M world_map = folium.Map()3 P) T$ j G4 O6 C9 t7 ? # display world map5 c4 }# {/ ]$ w/ M world_map / n/ C8 X& F/ W( v; e. y0 B7 K/ P
X0 Y& l3 `- ~
世界地图

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

4 V; T ]* g8 J% @+ X; l

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

# H% H* I; q/ L" L7 {

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

1 B! N5 t3 w5 @

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

* F1 q8 u, Z# z5 B! ~3 ^
# latitude and longitude in Singapore city - x! A5 i1 W3 V0 H" @4 @ coordinate_sentosa = [1.248946, 103.834306]. k, t6 T0 X8 E% u/ V F# T! I coordinate_orchard_road = [1.304247, 103.833264]: A% R; _3 F+ c1 R1 F9 x coordinate_changi_airport = [1.357557, 103.98847] / g+ P; Z: v. a coordinate_nus = [1.296202,103.776899] + p% W$ X4 I2 L/ [" J; ^ coordinate_ntu = [1.34841, 103.682933] M8 E4 t m# E' _; R coordinate_zoo = [1.403717, 103.793974] # m6 p! i0 O3 z& H1 d coordinate_ang_mo_kio = [1.37008, 103.849523]5 l# h8 K5 n0 i& Z coordinate_yi_shun = [1.429384, 103.835028] 6 D3 y( c* q& R! N) P1 a8 `- O+ ` F& y # icon' d, c N5 G* U; q! H; a& d/ n/ Z: G4 b icon_cloud = "cloud"- x$ {% T0 o8 A( p+ ?3 ~; W) _ icon_sign = "info-sign"8 L5 ~* a. X5 t' O # N A4 \; P5 c; X # define the city map5 \* ~6 Z* v9 Y5 h- A # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}8 Z; {2 d* ^! A; O! R( D city_map = folium.Map( 3 D6 C0 H& h: B8 I* E& q location=coordinate_orchard_road, 7 D: |8 Z: S: z( R1 ?- J zoom_start=11, - Y0 z( m' z+ ^! n8 T tiles=OpenStreetMap) 5 \; F# j, W, n . w$ D0 s: C3 ~6 d # add marker in the city map - L$ j8 ?- C( Y c7 z5 Z. d+ S folium.Marker(! |" R3 E* m, D3 s coordinate_sentosa,5 u* g N9 v& e; [5 S/ ? icon=folium.Icon(color=blue) % e( R) a x v8 V# D# ] ).add_to(city_map) 9 g6 K4 w- K6 {3 H3 ~% _ folium.Marker($ Z' |6 L% o1 y# X3 s2 w; q coordinate_orchard_road,1 k) _5 \5 Y6 a* @8 D0 U3 B, r icon=folium.Icon(color=green, icon=icon_cloud)+ G1 A6 L: r' c+ M ).add_to(city_map) : _$ k6 q5 W0 e5 S8 x( s4 V5 W$ h# [1 [ z( M% C& U' x+ ^' } # add popup% y( G7 @' L, c5 @/ Y2 U folium.Marker(: s+ T% u7 X/ Q N' x. i coordinate_changi_airport,7 O/ R4 o8 @/ Q: W7 J1 t popup=Changi Airport, / w2 W$ |/ {/ c2 B icon=folium.Icon(color=red, icon=icon_sign) 3 `' y0 Y- ~" a4 m& w/ e2 m ).add_to(city_map) " j/ n% d; z0 @5 X ; u% a+ y' R3 D# C% t e # add tooltips and popup 4 O+ N9 b" A6 l, S2 w% G M0 r, U tooltip = "Click me!"8 N! J2 M' m- d+ J8 E9 h) I- R ) u" Y, [+ m1 S0 ^6 J( |* v+ W folium.Marker(( x; _7 T+ ^( d7 C2 Y& i7 n coordinate_nus,* X2 F4 e7 Y) W6 K. {& ? popup="<i>National University of Singapore</i>", 4 i3 E" [# N9 `" X tooltip=tooltip$ R4 O# j- t, W4 `5 Y! @) s9 F ).add_to(city_map)) f# w w3 L9 ~$ H9 | & \; k" M' D9 P; W folium.Marker( ! W& K& P0 r1 G! W) x( @; V coordinate_ntu,0 `; G: k6 z9 F! {" i popup="<b>Nanyang Technological University</b>", ! [7 J. c9 ^3 Y" T- @ tooltip=tooltip5 W9 ]1 }5 t" l0 p ).add_to(city_map)# W1 g7 z2 W5 B% l' ` # a/ L) X) D" S/ C # display city map1 D) s3 X( f/ G g city_map
+ O7 P8 o9 w% ~$ L1 M
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

! t2 ~3 `+ [. _1 F. \
# define the city map + G7 p! }2 o: Y( s B% A city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) $ d/ i0 d8 e: q$ U# m# s" k1 Y0 p2 m # 在地图中添加经纬度, add latitude and longitude in the map when click* o* n6 x9 M( Z+ O7 I city_map.add_child(folium.LatLngPopup())1 R, \4 B+ Z# i4 _8 L ( F" t1 g# q/ \. D) D city_map
, j9 j, a' l+ t $ |2 s* P: L* @" ?* S$ ?0 ]1 e

几何形状

\: i! @' X! q* b

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

2 K( H, t5 d B; Z2 Y) A0 r
# define the city map ( x: H+ H6 F( K; v city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) , r+ J) w( w9 r6 S- m ) T* ]# V2 Q f, N- G% G: j # 在地图中如何添加形状5 d6 t, G! _: b. j # 多条边# c" T' u6 `: N7 E2 | points_1 = [+ Z- \0 P1 F% ^1 G% h X8 z6 T coordinate_ntu, # x r; o, t) V3 v$ Y& Q& Q coordinate_nus,. I3 G1 T6 m; O X6 l: T. ~4 S coordinate_zoo 5 Z# R' f% o. c4 z, J( _( Z$ r ]+ s, D; g0 V' o ! e3 L# B, ~! g1 k # 在 city_map 中添加多条边,第一种添加方式7 I) [. j5 A5 h3 H$ ~: \" I city_map.add_child(folium.PolyLine(3 C p6 V, |8 |* z locations=points_1, # 坐标列表4 s$ a, z+ Z% ~2 W/ o+ w+ s weight=3, # 线条宽度- E" g" l1 m+ P: g4 V color=gray)) 0 F" d! q* Q; i( R' u* t) @ n& k0 Y% V' D6 u2 i% l! w% x4 y # 在 city_map 中添加多条边,第二种添加方式% \* S, ]: Y2 @% I5 T/ V, y9 } folium.PolyLine( 3 s S6 B3 n8 o3 l# c locations=points_1, # 坐标列表 & g3 O3 K" `3 R% K4 M! K( D9 E1 V weight=3, # 线条宽度 j$ `6 E1 P! b# P2 N* r/ l color=gray).add_to(city_map) , Q% j" P( C3 C+ W% ~2 E# v3 H: D5 d8 M # 多边形3 G$ Q g: s. `' g% J' C points_2 = [2 X4 g3 n$ f6 ?/ t coordinate_orchard_road, ! J1 S3 j" v. }4 i% } coordinate_sentosa,& a7 J4 E- Z8 d+ H$ q5 U' {# C coordinate_changi_airport' c [3 f1 O" E1 [* m$ ^ ]5 ^# P- m. K- R' z f $ Y9 @/ g3 H- M city_map.add_child(folium.Polygon() J+ |- q, K& ?; l! v locations=points_2, # 坐标列表 + I7 ^% J5 j6 G n# z4 ~% E0 N weight=3, # 线条宽度5 _. Z+ Z0 y4 l color=yellow))1 C6 d5 V! ]$ Q7 O; I # _( e: [6 o$ c# T# n; @/ k6 \ # 矩形 1 L0 T4 P; A: N bounds = [9 ^! F% m) s( g8 O* _/ p3 i0 b4 X coordinate_ang_mo_kio, 0 _3 v# m+ L$ }1 Y& G* s8 i& |# q coordinate_yi_shun8 B8 }* z: h1 l3 O% o ]6 y* ~. u5 I+ x + j6 i; s' N; [7 H" S city_map.add_child(folium.Rectangle(% g; i8 i. f* s" }) R. E# Y3 E bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) % q% A- w+ R0 d3 \% N) V8 v4 r0 ^2 Z weight=2, # 线条宽度5 K7 w" D/ O. l, ~1 u7 i color=blue)) + ]" B4 u) E( ]0 |& P; Z 9 o* @6 |1 P6 H' R4 w0 ]0 Z, t # 圆形, circle, radius units meters 3 n' \2 n' N$ u folium.Circle( & V+ {5 _ G: W radius=1000,, i/ b! y* T' c: D' t: n location=coordinate_nus, 4 Y M* |/ q' E' D4 ?% n j9 X7 [) _2 x popup="National University of Singapore", - P# H! ?* h9 f& u: h* P: _. Z1 z0 C/ _ color="crimson", ; ^8 W! P; k/ t& F- |8 ?$ G fill=False,+ ~7 L2 I% x! f9 d$ e ).add_to(city_map)3 B9 K; N1 j' U8 Z( p" `/ z # ^1 O! ]; n7 z6 m5 c # 圆形, circle, radius units pixels$ |9 Y3 e$ T- Q9 U folium.CircleMarker(- T2 E' U& q( F' E! A7 I location=coordinate_ntu, L& N* U$ t, o2 W radius=30,* E/ e N7 Q7 F! S7 m+ Y; T* t! Z popup="Nanyang Technological University", {: O7 f; ` G color="#3186cc", 6 N4 F/ e/ e9 E% M7 }6 S+ W ^ fill=True, # q2 J2 P/ Y" V/ _' T fill_opacity=0.3, # 透明度1 _% s1 k; b z0 F fill_color="#3186cc",* {, I% o- I- } ).add_to(city_map) 5 g5 a, _/ ^* |' V: X7 S# B4 z4 r& M7 m8 X9 L city_map
) h& \8 Y$ b2 {) u/ Q
Folium 中的画出各种形状

热力图

3 ]0 V& W* f) p/ z2 B

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

. i) w/ t6 m6 u5 B* \# ~7 y& }+ S- b
import numpy as np! f* j9 c% K0 y: P from folium.plugins import HeatMap * e& C6 V3 O6 D2 h1 H( D7 c. ?% j' f& |/ {% B2 M1 u1 C0 ` # define the city map( E) p, h3 e% s! p city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) % {# Q3 R' O3 R8 l3 ~ G- N9 s3 S: H2 a4 \ # 构建随机数据 ! Z+ z! E% a8 w7 D {1 ~: ] data = (6 U4 }" O5 r$ C. l+ T' \6 ? np.random.normal( Q) n5 N4 ~& e4 a i: ~) ^ size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + % q% Q6 n# ]5 F! Q( j( Z8 D np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) - A; F" N& `* v! ], A ).tolist()% R) Z9 Q5 h( h$ |, W9 x $ |4 M8 k" G1 b7 {' ]0 Z city_map.add_child(HeatMap(data=data)) 2 { e' \& K( r9 C# P" U5 J5 ^ city_map
, Z% U$ Z) t& v; C8 j. C
( v& z) q B! N8 I6 \

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

' T+ P+ F9 x2 k; o8 @2 [' {
import numpy as np 9 f, K% w* T# E7 i4 W. j% B/ `" b from folium.plugins import HeatMapWithTime * K' `* i/ M V$ L 1 U h8 R' o' R" O0 M # define the city map4 Z4 F2 v" w, d) F* X city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)6 ^% q$ C9 I5 R3 _: e. y % @9 J W2 J1 B& ?1 x9 X- o5 O2 d0 B # 使用 numpy 建立初始数据 ) j. X2 |0 R& E: p2 F4 y2 ? initial_data = (np.random.normal(size=(200, 2)) *( p6 G+ J% ~- S$ a5 S& V( F! b& J np.array([[0.02, 0.02]]) +3 P; P, X6 a" f1 A% f p np.array([coordinate_orchard_road]))8 B- K6 D s- d+ z + F- e6 U/ ~3 w" C' G/ g3 e w+ S # 建立连续的数据7 |5 I g1 A1 w! `8 J" I$ ^ data = [initial_data.tolist()] : O* w: |& [$ u3 D' \+ m& K+ S for i in range(20):/ Z0 I+ j7 k) M& F( f) t, C* f data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())6 h# e4 ^6 y) k: O$ B5 ^: \- e - z# \! o8 m0 y$ t0 f( ] # 显示连续的热力图 % G" F d" ]7 ? p( B# O city_map.add_child(HeatMapWithTime(data)) - k5 y2 r' G2 P+ g7 _ city_map
# S3 ~) I) W& b0 x & F+ u6 Y, Q! e8 h% b& L

经纬度点的聚类

6 L5 M7 `3 U# F3 j

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

+ b5 d4 Q) h# c6 n W( G5 K7 s7 p
from folium.plugins import MarkerCluster 7 Y" N: W1 X( T" v1 ` : U: Z* G9 n# I2 K6 G # define the city map 8 Q3 H) g5 C4 X) A" R' H city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) - z8 c; b9 [6 E% l' E$ ?7 x" x( G$ U& s) V( t( o9 I # 经纬度的聚类 # u# C R4 \% h* M) \2 u # 在 NUS 的经纬度附近随机生成 100 个点; ( O1 o% W6 \* j0 q- B- h% B4 U* ~$ _% ] data = ( 8 k& Z& j7 l; a" t! g/ O, f np.random.normal(size=(100, 2))! d' z( s2 }' r% I. h * np.array([[0.001, 0.001]]) +) ~+ {) z" m5 E* d5 a1 X np.array([coordinate_nus]))$ ^8 Y/ r" F3 l2 j8 @ ; _8 x6 @$ w* Z5 f ` # create a mark cluster object! @3 ^$ j) g$ K marker_cluster = MarkerCluster().add_to(city_map)) R3 |4 o* g2 D& B5 _ ) \) I6 e* ]9 S8 l4 V # 将这些经纬度数据加入聚类. }# @ H, ?3 V2 T for element in data: d3 i, f5 K' o0 g& u3 k folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)4 |; o9 t& M/ V* m3 v % b7 t4 x! Q% g6 N0 B) Z: z # add marker_cluster to map ) F0 J+ V0 M. r @2 ? city_map.add_child(marker_cluster)& e# z# h c% Y( V! p/ S . h+ z d) D: F5 ~4 H" E" I$ v9 A4 y # 作图 8 g h/ }: S% t; s5 Z: s- {8 x: q city_map
# h2 ]! D+ f/ ^/ ^& E 6 M7 o* G/ K$ M( N1 `

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

' X3 H0 O2 h* S6 G) Y2 t

参考文献

( n6 G: d* }# c

Folium 官方文档:Folium - Folium 0.12.1 documentation

9 O- O8 u! j- P( S2 S7 s9 j2 t. G: \ 0 T+ D$ ~0 X5 o" z3 O* I7 f* E5 ~0 E/ |. V$ Q4 X- Q * Z( K' w) i$ v8 |) h1 G, B) \' v' B% v

相关帖子

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