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

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

[复制链接]
+ v* u! P( M4 ]

Folium 简介

1 w, L' k' ]5 C+ i' p

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

9 }+ F& S: O( ?; `2 ?, ]5 C

创建地图

& u7 G4 N/ x2 B9 t! s" V

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

: X! n* T$ `5 v' i$ o: Z; R
import folium + W |9 s3 n0 z7 X5 o% h# J- t %matplotlib inline S2 d& b! r% n/ V8 a# w ( x1 L% w2 o+ L$ i import webbrowser0 y1 h- p5 s; z5 [3 h ! I4 P% [5 ?& r0 P, x print(folium.__version__) ]" X. y% H0 _# t1 h% g ! A! ~1 [5 ~' j/ W8 m' z' r # define the world map : Q! Q8 E! J2 P8 F/ r9 v1 I3 j" ? world_map = folium.Map() 2 n5 n; D, w% Y0 A! h* f # display world map g7 a T3 @+ f* E0 i9 ?! R world_map 2 q$ Z: m: w; W3 J2 X
0 @* Q0 J* q3 c; _; s) {
世界地图

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

) f2 \* g) V/ K8 d: J, F

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

$ R' b$ V2 b# N! ~3 X9 @: i, @

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

1 X" e' v/ y3 P+ J3 a

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

5 S) ?5 }. ^' Q/ y) D5 }
# latitude and longitude in Singapore city 8 b. `" G0 e ` coordinate_sentosa = [1.248946, 103.834306] ! P4 [: R7 I, w4 T coordinate_orchard_road = [1.304247, 103.833264]% R' |' X; @ j0 _; o* Q" _; y coordinate_changi_airport = [1.357557, 103.98847]0 t( ]$ Q. J a% t coordinate_nus = [1.296202,103.776899]9 G* u' G- ?( t h8 r coordinate_ntu = [1.34841, 103.682933]8 L; _( s6 x/ K3 @+ n coordinate_zoo = [1.403717, 103.793974] ( B: l; L0 T* V) W: t coordinate_ang_mo_kio = [1.37008, 103.849523] o% n* x' B6 c. Z coordinate_yi_shun = [1.429384, 103.835028] 2 A* \# ]+ y: ^5 `: J3 E1 S 7 {) K& F8 J( p. X! w # icon 9 F+ [7 `. s6 C2 x6 S. e icon_cloud = "cloud" 3 s; p' f0 D2 L$ Q2 M i( Z icon_sign = "info-sign" 0 G& I+ q9 V. F+ K5 p* P 2 W# O4 B+ P$ `) l& ?/ u # define the city map 6 t$ F% {, C( {+ v # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} ; A3 }: i. j, l6 v d( l city_map = folium.Map( ' z) m! K8 f! g0 L/ K0 { location=coordinate_orchard_road, + [+ T, j7 C1 X# c: s3 O7 h+ [ zoom_start=11,! k5 X L1 Z7 P2 E; \1 o( G tiles=OpenStreetMap) ~" z' t/ q# n5 i 5 l2 k' i9 t; \" d # add marker in the city map ( F6 ^0 ]3 Z: |9 }* ^8 f' e6 @ folium.Marker( 1 S5 |8 ^! P8 j coordinate_sentosa, 1 }* I$ E m9 P% d" ~$ C; } icon=folium.Icon(color=blue) - j# v; {4 M U6 r ).add_to(city_map) ! d6 e. E6 y" s# N. T# m folium.Marker( . i4 R& a$ T8 M+ ]. u5 P! @3 _ coordinate_orchard_road, 7 b7 M) C3 z, S! I; S icon=folium.Icon(color=green, icon=icon_cloud)& N y5 _) s+ j' v5 r6 [ ).add_to(city_map) 7 f* b8 f4 s2 }/ o" y V9 b% E. Q" `8 {2 s/ f # add popup& T7 Y+ A% k0 v" z/ w folium.Marker( 9 O. |+ z0 Y7 h" x: m1 z" C. F coordinate_changi_airport,$ E* c% z$ g% B( A) V; } popup=Changi Airport,& V4 T! {( ^* Q9 ~; n icon=folium.Icon(color=red, icon=icon_sign)7 X+ T) Y& w3 Y7 Y. {" _9 b0 b0 `( `* Q ).add_to(city_map)6 W, s5 c* O6 x) @ % l- G- f! |7 J: M% ~; a' \/ |7 A # add tooltips and popup! V7 ^2 Y* F X tooltip = "Click me!" & c% `, {4 F; `5 h& V9 ]( o! w Y& v; e6 }* j# z9 v% u6 | folium.Marker(1 ?, s& J! S1 | coordinate_nus,1 y$ q. X Q' U popup="<i>National University of Singapore</i>",5 H9 K! B. G, S8 |3 d tooltip=tooltip ' W& A L5 u5 ]( Z7 U' a1 c, q4 z ).add_to(city_map). n8 n, v2 S0 E# K D4 F ! Y$ P6 i' ]; W" ^6 H folium.Marker( # q9 F1 O" m5 y coordinate_ntu,/ ?0 _- D6 P& Z* i popup="<b>Nanyang Technological University</b>", 8 ~, H% x( l6 n$ C5 e8 \) Q6 K( x- ^, \ tooltip=tooltip, |/ B7 Z7 x3 c# ?, L f3 t# y2 g4 x, q* i ).add_to(city_map): V, i+ v7 Z/ }/ U0 P7 Z1 q, y 0 C* G; L% [6 E; F3 S) P # display city map 6 D4 A, h: U6 C F$ w8 J city_map
$ t$ W7 J: K! K7 o+ W& J) C
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

: W3 H" \' N' U1 X7 I+ A- o
# define the city map ) t" b+ A. l# [ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)# S9 ^3 q& R1 T+ M ; t, F. T: q5 a; J4 s$ o2 ? # 在地图中添加经纬度, add latitude and longitude in the map when click/ f) E, j$ }- }( `) Y$ b3 t city_map.add_child(folium.LatLngPopup())' t, o* T+ x0 l2 U3 o% S : l4 r9 O/ n H' V* I/ Q* c: d city_map
% ~4 j# W( [+ r; @7 M) ^ : s. f. s# f7 x- a

几何形状

, t& P1 Q7 V, |: Z; W) s3 u

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

: k& `# ?) C9 n1 u" C
# define the city map! d/ `& \) E1 p8 D7 g3 R; [! U& I city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)7 }5 L& d9 ~5 s) a0 G8 B, j 7 ?7 s) y' Y5 e6 H # 在地图中如何添加形状5 s% p) y! @5 B& i- \7 Z9 Z # 多条边9 Q( Z) ?& h" K O# m points_1 = [ + {8 D5 _5 M; Y" T( P: l coordinate_ntu, & v' g, J7 p- B+ @ coordinate_nus, ' Z' e1 a/ _# T* c- G* U0 O! G' C coordinate_zoo: y. A9 r" l, a ] / T" F1 [' z' ]6 f, j% c @ % ^! ?9 L$ y/ G" G # 在 city_map 中添加多条边,第一种添加方式* Z$ y$ v+ `5 a( O( c ] city_map.add_child(folium.PolyLine( + d: t% U, }/ f; {7 n locations=points_1, # 坐标列表 $ P* [, U3 j$ \4 [* P0 j' q2 Q3 h weight=3, # 线条宽度 0 Y. H$ `" \: N- @, b& {* H color=gray)) ( g$ q2 C8 q7 X9 j7 x4 |: g0 H& D * q, g2 I+ z: { # 在 city_map 中添加多条边,第二种添加方式 F; _" ?3 _" O folium.PolyLine( ( w* T1 o: w& R3 x1 |" z locations=points_1, # 坐标列表- j5 t6 f5 M) G weight=3, # 线条宽度 . Z0 M9 ~0 u* a- a5 K color=gray).add_to(city_map) ; m" n3 i# }! P4 W8 D4 c( |, [& W$ k8 F* K: t i+ y0 b # 多边形7 U+ }- D9 D, {$ ?& V) V) { points_2 = [ , R- g9 u" T! i coordinate_orchard_road,0 C! m% l; r" S x coordinate_sentosa, , k+ S$ |. M8 [$ Y) Z6 l( g coordinate_changi_airport : @! J* z/ Z/ ^- t ]0 q5 M9 U/ G/ H A: L! ]8 V ! b1 `4 G" t6 X0 z4 r/ d6 D city_map.add_child(folium.Polygon( % x7 o; U1 S, V6 H0 Y! h& l locations=points_2, # 坐标列表 " w( |/ C/ E; T) L" x6 Y) J! _. F weight=3, # 线条宽度 2 ~6 @3 k: _6 g0 Z color=yellow)) e0 _3 ` Z! u" M 2 C8 Y5 u+ M+ c f6 n _( f # 矩形: F; ]; [3 z5 _5 t$ Y; L bounds = [( n2 J" S5 t1 h9 B# b5 p" K coordinate_ang_mo_kio,& w- ]( _% ]9 | coordinate_yi_shun 5 w/ k! e% i) w3 h: H7 a/ j ] # f" G' `; O2 m1 S& N + [! [# e3 r8 N city_map.add_child(folium.Rectangle( * w, j0 |& k; l1 _2 M1 ?, j bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) % f0 J( h* B" r0 n' ? weight=2, # 线条宽度 / @+ `5 Y2 E" ` color=blue))8 ~! m6 a# R0 W L+ ^ * G2 E) s- T( f) m& x # 圆形, circle, radius units meters : d1 \/ x* y. S- K; {3 D$ F/ G folium.Circle( " V `+ x* G: v( b. ^ radius=1000, 2 a9 n" E. `) q' z location=coordinate_nus, / G8 c, d5 \2 [4 q' f3 v. f popup="National University of Singapore",% o/ [9 X; G% y/ {1 Q1 v v, a color="crimson", ' ?5 x9 |+ R: M# E2 I X" Q fill=False, 4 B( g6 ^! \% \, \* a ).add_to(city_map)( G( t2 u* w6 B, _8 [- ^. E. L4 G $ h3 r" u/ h* l _- Q* @ # 圆形, circle, radius units pixels' d7 q) _$ f9 u' I7 L folium.CircleMarker(9 {* J( e$ `& S! k) R9 L location=coordinate_ntu,: a1 W7 K# y$ [, @ radius=30, 4 G. D3 e( e4 e, d2 @ popup="Nanyang Technological University", 8 G0 Q/ o( x- R3 x& f9 | color="#3186cc",3 t& |, f* \2 r% h& O fill=True,3 L0 O, c, u8 m6 P! G/ S4 J fill_opacity=0.3, # 透明度- p/ M$ Z( t1 |" s/ u* Q6 W fill_color="#3186cc",$ w! j5 M% ~+ D5 S ).add_to(city_map) 9 c( @5 [+ k6 H) e, b 6 P, S2 y- H+ j0 I& r0 _ city_map
) n+ m5 e! f" n7 T" r& k
Folium 中的画出各种形状

热力图

/ _) o% N% k& e5 T3 ^. R

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

6 z- `" Q: p; h0 p8 Z2 z
import numpy as np ' P5 K& B. Q# _$ G& D* K from folium.plugins import HeatMap 1 }% _6 u" d) w4 l5 b' V+ n- X 1 Q$ V! u* k. Q4 U6 i # define the city map : P$ z* u) C8 b7 S- D4 M/ E1 g& l city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 9 l! b, n9 ? W1 v! o3 r3 S. @) G8 v2 ~' t- l5 |. p4 `, C+ j$ ? # 构建随机数据4 r, E+ |" G5 b7 v3 a4 v data = () D. E6 q7 H' y/ n np.random.normal( , u g% W+ o6 p size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +$ p8 ]& E+ }$ l) b! M7 o) |3 C: Z np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) ; }$ ?$ q- w+ |; i/ R ).tolist() 4 A, U( ^+ F# M* f, n; F1 C 6 C* t% f w$ l; E city_map.add_child(HeatMap(data=data))3 s7 o5 o& J, o city_map
3 l' Z" E- c2 d
0 A' n* b e ]8 ^

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

" C# { \3 z& z
import numpy as np2 G% z9 y* D+ U5 @7 ]3 F8 N/ e from folium.plugins import HeatMapWithTime6 r$ W: M4 _! v' m! G # o% q4 \# [5 \, w# d# a# r+ V! Z3 O # define the city map 4 J: Q8 }( A# b9 l; M0 _6 u: @ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)7 r+ _! M6 l' q( Y" u) E % j! E: ]7 w$ w# d$ F7 T8 s # 使用 numpy 建立初始数据 ) p7 j3 x/ d) M! }# H initial_data = (np.random.normal(size=(200, 2)) *% m9 U( ?& w8 \4 N3 T" q- Q np.array([[0.02, 0.02]]) + 7 P6 w, ]# F" `! Y/ z np.array([coordinate_orchard_road])) 8 `2 O: E1 b8 b% Q! ] 0 r% I( z" _/ n2 ^, o3 n: r # 建立连续的数据 ) s+ N$ h6 g- i$ \2 A data = [initial_data.tolist()]: O0 m# f; ?5 N5 N5 k for i in range(20):" Y \% {6 |' u data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())' C* n! F2 o& U+ ]* d) x- i' c , [* i. h0 K, Y! C6 }0 _) \, y # 显示连续的热力图" }) f9 F. D$ d city_map.add_child(HeatMapWithTime(data)) ) a6 ~* J/ Z. n: D* B city_map
2 e) N z2 q8 L% m7 ~) E& J 1 q: `6 r/ e0 u

经纬度点的聚类

& r3 o! `. U. }$ n& Q* [

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

& B; e5 R: o( b. W0 I5 i" ^
from folium.plugins import MarkerCluster- g! X: K; x J2 h$ G ! e4 c6 ^. {7 O7 c# A+ z2 a- B # define the city map7 S, v) L" X$ V f- u city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 5 B1 F2 e4 }9 l7 S3 f) w2 h " M7 A3 m/ Q9 Y # 经纬度的聚类/ v* s$ W( T/ i' y# ?. [ # 在 NUS 的经纬度附近随机生成 100 个点; # R6 u5 u$ ~. [* W. C! j5 m4 O$ C data = (6 v' p6 c Y2 S1 h& ^& @6 n$ @ np.random.normal(size=(100, 2)) 3 D( P) T# }4 ^' R( ~+ q' J * np.array([[0.001, 0.001]]) + 1 B. O8 o6 K C- Y3 q np.array([coordinate_nus])) 5 R5 T7 V0 `; z$ y- y% @8 X1 o# m7 F # create a mark cluster object3 @ n k: \: f+ U5 j marker_cluster = MarkerCluster().add_to(city_map)* k: F. M( Y6 S6 o: E5 s0 { ! u8 C; T' G% a8 X/ [ # 将这些经纬度数据加入聚类 & k& S& H0 \- [% }, a3 }8 d for element in data: * g7 z4 H7 k+ I) [) L* o folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) - C+ S4 Y# C+ Y/ R; [2 {8 Z6 N, x, ] ; ~' E" X. F3 W8 a0 D- M- ` # add marker_cluster to map0 R3 g. }( N2 I# t/ @- K P. x city_map.add_child(marker_cluster) ' P3 q t- b! R3 T5 m) d' W, k4 s2 s" a! U, V/ \: w$ C2 O7 n # 作图' N) ]3 h. z X# L5 ^ city_map
& B. b' W' z2 v. T* m P9 h9 S" o7 u

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

2 Z5 P' E8 r* Q/ Y5 R& A) \) ~2 e9 m

参考文献

9 [: ^/ Y% V/ |/ X2 m- H% @

Folium 官方文档:Folium - Folium 0.12.1 documentation

0 d8 P- ^( a% K5 ]( m/ z 6 T! D. K, h, ?, Y _. `6 S. z+ o4 H+ _# @ g3 S' {7 H5 G * V+ h' F* w5 m5 ~7 X$ K, m: }" p3 d5 @: j4 q8 c4 ^. F
回复

举报 使用道具

相关帖子

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