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

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

[复制链接]
7 K4 B0 `) b# ^5 c/ ~

Folium 简介

5 p- k5 ^8 u5 @. O. g

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

3 t8 }& Y. [) P. }+ i

创建地图

0 f- N) w: V: I$ m

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

2 D( k" h; i8 ]& t6 |
import folium $ \" z- G: W0 W" W# Q# w1 j J %matplotlib inline1 W4 a' E: x8 K8 K ! f7 n& T- n. D, B- ^ import webbrowser 5 L! O) Z9 Z% a2 A' i) }4 Y/ {5 N6 P. N' A print(folium.__version__) , h: e0 W* Q) r& N/ H* u: X0 S/ L- E: _, D" `) ^ # define the world map* `( m# E6 u6 c& G- L* `$ p world_map = folium.Map() ! R6 @( {- ?+ [ q- H- R$ t% e. _ # display world map9 b V! N# `2 ?$ @1 c world_map# U" s0 F1 w, n, D
) P, G9 w' p% ]% r6 X- w3 t5 x* o9 d, @
世界地图

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

% K5 ?; y+ o. o

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

4 i$ q8 U! k. v X0 v

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

, N) B% i, p3 y' `& |: X

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

) o) ~2 e$ y) }( l% m' X; t
# latitude and longitude in Singapore city 3 S2 ?* I/ C. O: S/ n coordinate_sentosa = [1.248946, 103.834306] U; z5 X1 R4 c8 M coordinate_orchard_road = [1.304247, 103.833264]' Y( n) ~: w- h! V3 a coordinate_changi_airport = [1.357557, 103.98847] 6 C. c/ j* r4 M; M# ` coordinate_nus = [1.296202,103.776899]: |+ }- K$ ?5 u coordinate_ntu = [1.34841, 103.682933]2 q; f6 [/ w: {: D coordinate_zoo = [1.403717, 103.793974] : q$ A: k$ J# [1 ?2 ~ v w coordinate_ang_mo_kio = [1.37008, 103.849523] + l- s$ q3 m& Q; a coordinate_yi_shun = [1.429384, 103.835028] , y% l. q e+ s, O/ B / k- V2 L0 R& k# q: Q2 L* T$ O # icon 9 Q& R0 }/ J7 e% ]' x2 X icon_cloud = "cloud" / f8 f- \9 y- N) U icon_sign = "info-sign" ; E& d$ p% g0 p$ u$ L1 S ) P# w5 N3 c- P8 x$ B }! Y$ } # define the city map 4 O# ]: z, h4 W6 a: p6 | # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} 2 z! j" ^. X: e& n city_map = folium.Map(/ K/ X6 R! A' S6 E- T! U location=coordinate_orchard_road, 0 e) j' i5 F: l" N' k# e8 E- X- Q zoom_start=11, 8 k( b. K3 \2 M" l tiles=OpenStreetMap) % V0 b" z4 ` _& n% w' H" s 0 h" B9 X8 G* z+ R9 s4 x& Y # add marker in the city map 1 Q3 V: H9 J0 `& t! w: M. ~/ V folium.Marker( m0 q" c1 B) n8 K5 s coordinate_sentosa,/ f! F I% c: d) m& ^ c, j! ] icon=folium.Icon(color=blue)! {& u$ O2 U0 p ).add_to(city_map) 0 F' F5 |, u; d9 t. t# M folium.Marker( $ D3 v* o+ ~0 X7 F. }- Y coordinate_orchard_road, 9 T( e" u; l' y, X7 L icon=folium.Icon(color=green, icon=icon_cloud)0 k- s' v5 ?8 y { ).add_to(city_map) 9 \5 Y8 l G$ u' w! N T3 K" u5 R1 H1 n' j # add popup, q$ W) v) h1 ~0 c7 |' u" C folium.Marker(6 k0 h8 a' H$ H! k coordinate_changi_airport,- s* i# F. e0 }5 l popup=Changi Airport, / T1 q- F+ {! l" x icon=folium.Icon(color=red, icon=icon_sign) 2 @! r7 o: r# ?1 | ).add_to(city_map)) y- a- K v% d8 I: N4 Z" e+ l" X2 _. H ( `+ T/ J6 C3 A7 l& y0 ~ # add tooltips and popup9 C2 A1 ~: e- a: e/ a% D! i, h! F, m tooltip = "Click me!"* w+ E" g. E* D8 C: J, k+ A" S 0 x. g, v: x3 g, K* }% j$ i folium.Marker(# V4 T+ r2 K* `- \( N2 t5 W coordinate_nus, ) Q. V5 [8 @& R popup="<i>National University of Singapore</i>",3 o" G2 Q3 p- U tooltip=tooltip & K6 B, Q6 a! \2 |+ @7 w* p ).add_to(city_map)" ~9 G6 F* p5 L) ~ 8 a' b' ?2 R+ J( F9 S folium.Marker( ! s7 C: R* F7 b" j- ~2 V" V6 R9 A coordinate_ntu, / n" g; c6 F+ E- m: X# ]" y, l popup="<b>Nanyang Technological University</b>", & Q+ [9 b& c6 j2 S tooltip=tooltip 0 S0 G: ~2 ?, _ ).add_to(city_map) 3 O5 }* A0 B& v& Q* k1 v- `1 C) ^8 X! X) a- ^. l0 v # display city map $ n7 a3 p& B2 V3 B) E. c city_map
& D |7 L* {# m9 H6 _/ X5 p& |
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

, B+ J0 n& v" s1 Z
# define the city map ; U9 Y) S g* R3 t8 n; { city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)7 l# Q7 _7 h4 O' B8 s + s# o( L, y# c$ L E1 g # 在地图中添加经纬度, add latitude and longitude in the map when click- ^5 l- e( [" k$ Q7 m% m. b. r t city_map.add_child(folium.LatLngPopup()) ; C3 ]$ P ]5 W; w" `, Y( t5 C2 j( F7 v( Q. R) k" t3 h* n city_map
5 D/ k0 @) l1 U! F4 t 1 s8 i8 H& _- A; E! k

几何形状

Y8 o. K. z* H- o

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

- {: L* A! b; l4 Q0 Z& S
# define the city map! B1 p+ Q8 `# M( ~" X. [+ Q city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)5 v* r j- p5 W4 E8 i 1 V8 e0 h$ d& L; U# G* F # 在地图中如何添加形状 1 n, }4 e# _) B9 j # 多条边$ a/ X& O& i3 w points_1 = [ ' r1 E. V& y8 i, |! L coordinate_ntu, # P1 L* I+ T1 T+ N coordinate_nus,# z; F# Q& I! h5 s! U) {4 c coordinate_zoo $ x2 T; h1 V1 |. S ] ) T6 J4 g3 y7 Z0 y 5 ` C* g0 X& l # 在 city_map 中添加多条边,第一种添加方式: [6 G0 `$ M8 W8 q4 V8 A7 c" J city_map.add_child(folium.PolyLine( 5 R: J3 b7 P, ~# D/ \ locations=points_1, # 坐标列表0 |. w& d$ g* T: z: r. ]& x weight=3, # 线条宽度+ `: W! S: {; Q0 C3 L: } color=gray)) : |# p# N+ g+ A; j! q ; a# F- s4 H$ [' q3 g/ E6 x) N. V# w # 在 city_map 中添加多条边,第二种添加方式% d0 {$ A: F% x5 M% F folium.PolyLine( : C# r" o4 s3 R: a X0 g locations=points_1, # 坐标列表& S9 c6 O- j. @ weight=3, # 线条宽度+ o% p7 O% I$ V color=gray).add_to(city_map)9 _, H/ j( r5 p$ y+ Z( @3 W( Z ! f% }" j4 P o+ F' w2 T" B2 R8 A # 多边形! x3 Q) i" ~8 {0 J& G points_2 = [ 9 R/ y1 c1 ?7 q q coordinate_orchard_road,) V' f9 J9 p* ^' F0 _+ [ coordinate_sentosa,' G3 H% e3 ^% l$ }* u5 z& s coordinate_changi_airport7 V. k, v9 |$ v/ ]2 E/ q ] \% v* L; c; j* q ( [8 K, Z6 h# A2 K city_map.add_child(folium.Polygon( " [0 {9 B9 A9 X3 P' B locations=points_2, # 坐标列表) E+ {9 x1 G: G. q+ v weight=3, # 线条宽度 ; R6 N+ ^/ j; {% {9 J color=yellow))" f. X) c8 G7 K % s5 R; p1 H7 ~! ~3 Z5 i6 e" o$ ^ # 矩形5 Z& { C3 d+ `; e1 I: P2 i/ e5 v3 d bounds = [0 ]3 G7 N2 p0 Q5 p coordinate_ang_mo_kio, " B! s: n8 V T9 C0 S coordinate_yi_shun9 @' K1 V1 S# C+ X1 L' | ]7 x6 ^3 @9 M1 R8 U) F0 X' V - L& B! z D) o- N4 X1 G% g city_map.add_child(folium.Rectangle(4 }* ~/ v V% O+ h, @& C bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) ' O- Q( g \. d0 \7 Z% k: h weight=2, # 线条宽度: o% Y6 V& ~: q* r: ^ color=blue))' F2 i) V; V, m& `: }1 D; S9 O( I 8 [/ ^/ v4 S& P$ J' o # 圆形, circle, radius units meters , L: n6 A ^9 {' l" j folium.Circle( . M0 Q A& {. g radius=1000,+ i6 J0 ~ A8 y3 U, A/ G5 u location=coordinate_nus, a* q, q, f% u. p- k popup="National University of Singapore",0 S* l0 s( U) g% T6 T color="crimson", % T/ }- J7 H5 e5 X5 F fill=False, " z# R) I4 V7 r3 L1 e. Z1 P' X ).add_to(city_map)% j" W. \5 |8 c: q/ R9 Q# _ 7 f3 {# W. H' x # 圆形, circle, radius units pixels/ w( o, a5 Q8 D6 W! O5 Q) b- ] folium.CircleMarker(; d {& W' ^8 ? location=coordinate_ntu, ! s g, {' ?3 G, i4 | radius=30, 1 W9 u. l' T& \4 h1 [" L c popup="Nanyang Technological University",- u/ F+ Z( M7 E( G/ ?5 p7 Z8 Z color="#3186cc",1 W+ ?/ {' L( T. D fill=True," m* |/ S9 q7 u$ {3 o3 q fill_opacity=0.3, # 透明度+ V v9 Q5 {2 G* d0 X fill_color="#3186cc",& r g+ u W$ g0 ` O ).add_to(city_map)0 D) I! [$ X. ~: t+ @ 0 ?1 k3 \4 I; [3 H/ }) w city_map
5 X/ \" A/ E- \: m `" R; A9 y' [
Folium 中的画出各种形状

热力图

2 H4 B, V! c e( s2 b7 Q5 B

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

7 d: c. e) `8 I& |6 \$ d* S3 g: y
import numpy as np, Z9 u# ]: z6 E0 ^! ^* u( X1 h$ B from folium.plugins import HeatMap ( q) g1 o. F8 S% Q$ k7 u) P- ]) t) G1 O% M) |5 M # define the city map% ] N* V2 N6 d. I city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)7 `4 H3 U8 @" b' A 0 t$ Y1 B# _0 `" n& R # 构建随机数据. Q( H0 y+ D/ V! x4 G9 a data = (* ^ h( h) o/ @8 d A2 g np.random.normal( 8 a" r0 r1 g- h7 M2 e! m size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + * W# f# n& b) x0 m# r np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) ! E4 w4 _+ a; N! a( y ).tolist()- X! J G$ e# F" [$ c/ V& C- ?8 y0 G ( v5 Y1 A, }. ~- S# y city_map.add_child(HeatMap(data=data))2 W' s% q+ G6 U city_map
$ \: i1 l' k! @ j
# @+ w8 N7 n9 q; ^" N% c3 [; J

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

9 F2 b' P, |. |. D5 w
import numpy as np3 s% N3 S. L2 h from folium.plugins import HeatMapWithTime ; K- x, c: `+ F, f& v' V3 D) _- Q$ u8 i+ g& G! q4 i5 K1 z; D # define the city map# n8 g; ]$ _+ O city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)( V2 O! b1 ]7 a. O: J: | , i: L9 C) [+ e # 使用 numpy 建立初始数据 & [/ i- P$ k- D* r initial_data = (np.random.normal(size=(200, 2)) *3 ~* S3 B$ ?5 p: c- W" H; w7 B np.array([[0.02, 0.02]]) + ; O+ N! ]# }' U( b5 u np.array([coordinate_orchard_road])) ( a/ p2 s( Y! G 5 ]1 ]3 D' ?5 q( @# w% y+ a E, K # 建立连续的数据1 r/ A4 E, J% K4 H7 H data = [initial_data.tolist()]8 ?- E' c; c+ u7 c- r for i in range(20): ) K/ _0 O, P! R) n data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()) i( J( g1 T4 m5 W- K' h' R& O8 X # 显示连续的热力图5 f T$ M/ R% {- ` city_map.add_child(HeatMapWithTime(data)) - O4 Q* Y) P- \ city_map
0 B- m- A- V+ k6 e: | ) V& f2 y9 {, } Q9 ?, r2 v+ N

经纬度点的聚类

$ ?! N+ N& P2 N

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

9 I7 c; |+ ?3 B; U4 Z( Y7 q
from folium.plugins import MarkerCluster. w, [- V. V' {+ ]) \6 @2 T " w% Q$ R# y7 M5 G8 d7 n' B # define the city map* O2 q- j1 u% w& y+ w city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 8 Z( }' T8 m2 W/ K 2 Y; X' x3 ~8 v! e # 经纬度的聚类 ( q; `1 \% b3 J. O- l # 在 NUS 的经纬度附近随机生成 100 个点;8 N2 X0 V: G* X! S* ], y- N data = (% v5 E9 d7 e; r. A np.random.normal(size=(100, 2)) ' b+ r1 [8 f* K4 \0 W * np.array([[0.001, 0.001]]) +/ ]/ Y: _' o" U6 s) m np.array([coordinate_nus]))3 U* [$ F _1 F* u 7 Q; K4 [* `* K2 ^5 ~ s # create a mark cluster object, y( c O H3 ^9 x1 u2 A( o3 j marker_cluster = MarkerCluster().add_to(city_map) 9 N* d( t' c& [8 Q9 W2 c$ y. w- G- h8 u) V: w4 C # 将这些经纬度数据加入聚类 / h i; k M) t5 C for element in data: ; K4 J3 S( ?$ Y folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) . V1 h: O* o" ]& f- i+ I ! A+ M% ~1 I6 E* s5 B, ]1 E8 F" f6 j # add marker_cluster to map 8 h9 ~9 z$ F' ~ e6 |/ T4 P city_map.add_child(marker_cluster)! a. e; w# q' }$ T R; m6 K : ~6 w* X9 I% g- T, | # 作图0 K3 y+ F; M& t! c# E+ P* e( u city_map
* z9 h4 `3 V- e/ y/ W# i$ T) M$ g ) I9 d" a0 n! M) y5 A

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

% V2 B6 v3 P; t/ ~+ `

参考文献

% D2 V( n( B, X/ \4 h

Folium 官方文档:Folium - Folium 0.12.1 documentation

f3 u- e2 N- r/ | r , K1 t2 b3 [2 P# p( N' J# t2 j: A" d 6 v# ^8 R0 n: a" y/ E2 u% @# k9 O0 e. r( N4 l# q9 T& U
回复

举报 使用道具

相关帖子

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