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

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

[复制链接]
) M3 R. m1 m! S5 U

Folium 简介

7 C! D. x9 L. Z5 ?% c% J& Y: A

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

- z1 c% Z# @* u9 h9 W

创建地图

# R' P7 v2 \4 f4 b

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

. }( h& x: G5 L( X
import folium $ g+ U0 {& J* B %matplotlib inline : Q) S( W$ F3 q' U! @+ T% ?6 k9 k: a& { import webbrowser/ b5 z& H2 `* A+ G1 [& ~ 9 c, ]" y) t: Y5 c) u# w; x! C/ x* P print(folium.__version__)( A9 W# U9 R$ [5 }- N/ K$ M " {8 C, p6 X6 ?: `7 k1 h: N/ @ # define the world map& S/ c' }/ J. y8 b world_map = folium.Map() 4 b3 F5 p& z* R; e! [; c0 f) ]2 G # display world map 9 h3 |- s2 I/ D world_map' |/ E0 V3 S% ]
5 a9 Z/ R7 ^! k7 p# P1 x
世界地图

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

9 s2 m: n- f' L& H+ J5 S& U3 [4 e

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

1 H1 N* Y" I7 V: b6 W5 v: U

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

[$ ]0 k8 C( j- F/ _( b

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

8 D, Q3 \' U( P$ w. F" _3 Q
# latitude and longitude in Singapore city* R! _2 o1 h3 {# H9 }0 `1 H coordinate_sentosa = [1.248946, 103.834306] - m* F) w0 i' }* M _4 K" L' Q coordinate_orchard_road = [1.304247, 103.833264]5 b& b. r# s+ y# t$ K2 l/ g8 S# e coordinate_changi_airport = [1.357557, 103.98847]& [' J: |* N, Y3 U3 K7 I# @ coordinate_nus = [1.296202,103.776899] & L& S7 X4 p0 X+ N( N$ C coordinate_ntu = [1.34841, 103.682933]4 T& T5 r, V" g# f9 I1 b coordinate_zoo = [1.403717, 103.793974] - C l* o. c: o! N coordinate_ang_mo_kio = [1.37008, 103.849523]8 B0 g! R$ X# I# E& s8 u% Z- ^ coordinate_yi_shun = [1.429384, 103.835028]3 y8 @4 B: ]0 f& d5 R, q( J# K 2 M) v+ m5 t" Y W& U1 S4 h" d # icon l) B. U% A5 b/ s4 s icon_cloud = "cloud"; A V4 d8 |3 ~3 M icon_sign = "info-sign". d Y. G1 C2 i; U/ |; s H9 j; u" m* E% u; E$ a9 x # define the city map# {# t' a$ J0 I5 T: x0 M, l # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} . z2 P/ X& U1 R city_map = folium.Map( - j9 t; e# M) o; b- D6 X ~ location=coordinate_orchard_road, " c) p! _1 r! p6 a& K! U zoom_start=11,) [ E5 b& ]# m. q$ T5 r! S# o# E tiles=OpenStreetMap) & F- Q& C% s* H0 S/ { 0 R" n1 x, Q! c* V u( j: Z # add marker in the city map# V9 t' E; B/ X$ g7 ~6 [% n folium.Marker(. ] t. C V5 e1 S, k1 F$ t coordinate_sentosa, ( ?! f% J2 F) _; f# B icon=folium.Icon(color=blue)4 m3 i( [- C0 p ).add_to(city_map)) o8 h0 j/ F6 V2 s9 E" v folium.Marker( 5 k! U- E! @9 L- g coordinate_orchard_road, $ I$ ~, P# `$ P/ }# {+ y; J icon=folium.Icon(color=green, icon=icon_cloud)) t6 `* R _# ?. X6 A2 l ).add_to(city_map) - |0 b2 E6 c& L- h/ n. F! ~6 s$ L0 c% m+ |4 P7 S # add popup2 a3 \* d+ I. O; [% t2 `/ e4 j folium.Marker() l* o9 A- {# e T$ e coordinate_changi_airport, |3 z6 `0 ?- A8 c* Q# h popup=Changi Airport,. ?5 h# {% t: I icon=folium.Icon(color=red, icon=icon_sign)& x) z) F+ t) D# f( k5 {. F ).add_to(city_map)7 J& Q8 Q. S4 c P& p8 a* N ' g1 n4 l& l% p2 @/ n1 B, d # add tooltips and popup V+ D* b+ ? o: u tooltip = "Click me!", G: I1 z; x% O% U3 y& w' q% k ; Y, f# U$ [; l: t4 R folium.Marker( " F2 s$ @. S! s/ i- \ coordinate_nus, & Q5 E: U% G, m* @$ F popup="<i>National University of Singapore</i>",, N1 r) H# f% z7 R. s. D tooltip=tooltip1 M7 [- a0 y' b ).add_to(city_map)* i; D# X u& D* } : j+ y# Q/ Q4 Y! ~1 c folium.Marker(# P, I# F0 ^. U1 Q coordinate_ntu,3 W- r9 }. {+ u popup="<b>Nanyang Technological University</b>",& I0 j1 J8 E# C& k ? tooltip=tooltip $ s1 @ W$ c) t# k; I ).add_to(city_map) " n; s5 O8 M) K / k4 q+ [4 X+ E* j: n1 J5 C # display city map 3 ?; A, j# }/ b# G9 D$ x city_map
* D5 m/ e4 j2 S; G5 y( t4 J
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

+ b- q2 o- @$ ~: d- M
# define the city map$ C; o O' `) J; _9 v Z city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) , b& q7 q* G. j! {! a, n, N+ p& s. r2 v( G! _6 T6 H( l/ s) X # 在地图中添加经纬度, add latitude and longitude in the map when click/ M( E k% A7 u9 y. u city_map.add_child(folium.LatLngPopup()) - e+ V- f2 B, ?+ o- A$ X 7 n, K& \- D3 H city_map
0 E! O4 H' \* S' ]) |& K/ d 5 U# ]% L) c2 Q- n

几何形状

. e$ Q. S4 k6 \5 s1 H

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

: K; K. q' ^2 X" Q3 w6 L* o
# define the city map 1 l" l( Z: {9 U( f' T1 Z9 ]$ ^ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 9 N! K& |' \1 R% R3 b) P8 N" l+ r8 T. m, Q # 在地图中如何添加形状6 y6 u; V8 j( _" j: S3 b& D # 多条边3 W$ x$ b5 j2 A" O4 @* B points_1 = [6 f" y! E+ M2 H6 N coordinate_ntu, - o# q% s( z" _2 B coordinate_nus," y Y2 Z" u$ {4 P coordinate_zoo ; p2 @! d9 Y; F) G4 | ]) u {4 X6 W! h7 r& |! ~9 n 9 K# J& G7 E2 c( W# ~+ K, f # 在 city_map 中添加多条边,第一种添加方式1 Y M. a: W5 t5 ^2 O city_map.add_child(folium.PolyLine( ]1 `0 ?+ L6 h/ ~2 J p locations=points_1, # 坐标列表 - F. F4 N7 ]* a4 @ weight=3, # 线条宽度 8 Z) K% c2 c4 E1 j5 p6 n5 I color=gray))$ n) }- S; t2 Z4 Q! R 3 @3 A% V# u/ s& p& u' I9 g # 在 city_map 中添加多条边,第二种添加方式; P# J9 M2 K( q7 i( J9 j folium.PolyLine(4 g+ U0 t# m# n) G locations=points_1, # 坐标列表 2 F4 {3 y3 b2 n5 C' ^ weight=3, # 线条宽度 c2 q3 O* v# j& Z8 H6 q color=gray).add_to(city_map) # A2 V/ J# [1 @5 ~2 w3 N4 p8 w # H( m5 s" y4 b0 ^4 H+ ?: d7 A # 多边形; t* p! C1 x/ D points_2 = [. e9 J' l. y8 i( m- v. y coordinate_orchard_road,6 W% r0 h( x& {5 _4 S4 a coordinate_sentosa, |* N& y$ p: G* Q coordinate_changi_airport 9 A: N% Z' T+ ]. V: _ ] 8 {: t$ P: b v2 _. b! u( @/ ~8 s2 Z% S c. v6 X9 F2 K1 e city_map.add_child(folium.Polygon(+ d$ ]' \, S5 L8 v# i+ G# [' h/ | locations=points_2, # 坐标列表 8 R- d1 b3 x7 M/ _: c weight=3, # 线条宽度& q* e, v2 t' B6 e* W# b r color=yellow)). O7 s N% z; { " C" e2 \, C5 x. I: v' I) X% C # 矩形/ b. y7 Q4 p! z1 _- \' d bounds = [ $ _" d3 B9 v7 U) G coordinate_ang_mo_kio, * s1 u* c7 P3 ], K1 \ coordinate_yi_shun9 R4 Q3 s9 z- `0 \. a1 F, O ]: c! F* ]+ ?' Z8 m0 J, j 8 E, V+ h; a- U3 Y4 C* N city_map.add_child(folium.Rectangle(& a& G& S5 _7 l7 L6 @. e6 r# j+ M bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)) _# N/ x% h! f) o! [. d weight=2, # 线条宽度 & h6 h, i1 p- |* K color=blue)) 6 f& \7 T9 h$ @$ [, F6 L7 l2 I" |/ S& s 8 G6 B6 m- a7 D O7 D7 Q& a # 圆形, circle, radius units meters# t7 j- B8 [% X( d folium.Circle( ( [4 i* B& Z3 w! T& ~; | ~ radius=1000, Q7 [6 E0 |6 c5 F$ i" K location=coordinate_nus,! `6 g# H% O. T: t% E* Z popup="National University of Singapore",* V! A! D/ N! |0 @; h, _% ], b color="crimson", 4 h. w/ w1 c4 S; }3 t$ V fill=False, - u8 B3 ?" v# V4 j3 _ ).add_to(city_map)6 @# o+ l9 J2 z : I8 D0 L, o$ ^) s" s # 圆形, circle, radius units pixels& n. t$ `6 o% q' F folium.CircleMarker( $ k* A h0 x- j) P: B9 t location=coordinate_ntu,9 o) l" J P1 ^/ `2 V radius=30,, B+ M$ N+ v1 t- M) J+ e popup="Nanyang Technological University", ; [* w5 n' l7 C5 u color="#3186cc",8 w _& O6 o/ M5 b fill=True, 4 d% P. I" ^9 C$ N" I fill_opacity=0.3, # 透明度7 @) K5 W( }7 m$ K' }: ` fill_color="#3186cc", 3 S) l2 ]* }! R4 y% i7 o ).add_to(city_map) 1 K+ V, n' \$ e; a9 p+ u1 |3 m+ P9 I( c- z' ]8 R7 }0 Q city_map
: u0 t3 \ `7 c% X& V V2 m
Folium 中的画出各种形状

热力图

. Y7 w: B7 P# x6 m* K

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

: v& `1 T& A3 e* u8 B
import numpy as np" {9 `4 R; t% k( g from folium.plugins import HeatMap$ J/ s" i6 n2 l* |& ` 4 F/ m* b- w* c, N, s. k3 w # define the city map 5 J2 Q" I; Q& D8 s# e city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) Y9 F9 B1 p( u" Z 6 h F, `5 b6 _, J: X5 w # 构建随机数据, S% o; N# S* {8 d+ x; x' i. R data = ( ( l# j! i: S5 A% ]% h& X np.random.normal( 3 E& {9 z1 z( d6 J size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + 6 R' k K5 X( [, @ np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) ! B }; N! E" B9 O# x7 G ).tolist() 1 w, d* D; V8 L: f1 P4 _' _ L6 Y( d$ _8 E d% b; x city_map.add_child(HeatMap(data=data))5 H( m s+ {5 [4 U2 ]" _ city_map
. f7 M8 o1 i# Y% {+ L4 O
7 D8 l: t4 {* o. |

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

/ {0 p! F" S) J& F
import numpy as np H) H8 D0 g8 ^1 J" u5 u from folium.plugins import HeatMapWithTime # [9 t) n4 t3 a/ d: t7 K & u0 u9 N5 `4 J! V2 C- ~ # define the city map& U! J/ l1 K! B: g9 p$ s3 @( v city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) # W7 Z, k: `+ ? ( B4 d( P$ x- m3 l) W& ?/ G4 g # 使用 numpy 建立初始数据 # l1 [" k: k: y2 Y; @ initial_data = (np.random.normal(size=(200, 2)) *" z# o4 W) O% U; V9 D np.array([[0.02, 0.02]]) + / z! m; g- i' b" Q9 s. i7 z np.array([coordinate_orchard_road])) ' W q, v2 Z: {# F" X& ]7 j 1 S* Y3 R+ q% e. }( ]' ]7 a9 C3 U# p # 建立连续的数据# K' w: N% e+ F data = [initial_data.tolist()] $ u/ x: A r |6 I for i in range(20):7 y8 G; }5 k/ c w+ ~. V data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()) ) G& j" v( T% E5 b% a ( @7 Y! F1 t& a/ r# l # 显示连续的热力图& `3 C% U. t- I3 d/ h city_map.add_child(HeatMapWithTime(data)) 5 O, W, L0 |0 g city_map
0 m9 L6 x/ _7 V& ]! Y + \1 ~" q, n& V8 H

经纬度点的聚类

5 d+ y& S! a1 u/ {3 L9 U/ E

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

& I, q z- Z t$ D2 r" \
from folium.plugins import MarkerCluster # Y! G/ F# q/ A. O, }5 S ' E; H& [& U' c# f* ^' S: G6 O5 c # define the city map( }& Q( q* _9 x- V9 X; g4 ]4 q city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)) s7 l2 t* o) J( J6 ^ 4 G2 E8 A$ s4 D7 U; F" t/ R # 经纬度的聚类 4 T& _: u& h% `7 o! G* x # 在 NUS 的经纬度附近随机生成 100 个点;2 m- n0 T% D0 z% P" X data = ( 5 e, s! ~$ Y& L) Q np.random.normal(size=(100, 2))9 A6 G- `9 y' m7 a3 h( j0 ~, I * np.array([[0.001, 0.001]]) + 9 e0 W0 Q. m4 e( P4 r0 R np.array([coordinate_nus])) 1 h" m- ]/ T% Z0 M h6 e r# H& p 5 W" Z5 [8 |3 z8 s! x! Y # create a mark cluster object + e! K( f" X& p marker_cluster = MarkerCluster().add_to(city_map)$ D- \3 v$ K7 p6 T: e/ R( {) |# v & I. m1 d$ U6 _: e # 将这些经纬度数据加入聚类* T$ A- K0 p3 Q9 u+ ]: `% A! I for element in data: ; L" S, I! J. p folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) $ S! z! K: r6 w $ B- Z& E3 ?2 i # add marker_cluster to map. J5 l y/ y6 n9 b" v' d city_map.add_child(marker_cluster)2 u' ]( D9 T+ ?$ w) z$ ^: D * ]# ?- g# t/ k7 B0 l, _ # 作图 X& ]: t: m+ z0 o5 l% ^9 @7 F! p8 ~ ~ city_map
+ I7 e( U! h5 U6 G 1 {8 M# }' k, O) P. N* H! e

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

; B& c8 T# Y: P; I3 \9 f) r L

参考文献

$ F9 y" r9 C" M) E7 p

Folium 官方文档:Folium - Folium 0.12.1 documentation

9 ]9 {8 [; G" z0 f ) y/ I" [' ?' P; k8 J, [7 {0 ^4 O0 ]2 E 1 `' r1 i! V% A/ M% |" F$ q) g! \7 {& F
回复

举报 使用道具

相关帖子

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