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

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

[复制链接]
1 V- J& d, Q3 d9 O8 z3 Q

Folium 简介

) H1 y- n* j9 p' J% [+ }' A

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

& \# E* p8 G0 v( k

创建地图

% m! G2 w1 T$ P& v; o" C# N

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

$ ]4 _7 @ U. m; h
import folium, p j9 \8 B7 f R/ C %matplotlib inline ' u o) q' V7 \$ i5 h4 C d* z) Y* ^1 ^1 Y. [ import webbrowser0 {6 C u+ Y# I& O l% g* ] 7 r5 Z0 r. v, l print(folium.__version__)2 S: h! n# a2 t1 w/ _ S " H# W# c6 T8 h9 _+ B& i7 I # define the world map$ `1 [/ R# l* p+ L world_map = folium.Map() / Q N: E- u: g$ O- _. o' G # display world map9 D- Z6 m! w' h+ @/ C* s* w world_map o% c5 J) f: k$ S# Z
! H3 y/ r5 j' u8 N
世界地图

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

7 n' _% v) R x! U9 T, A1 y

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

& U) z' Z1 b6 i$ `5 ]

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

/ {) {% `! D$ }, D% i+ l

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

( O9 j7 i( J2 q0 {& M, K5 S
# latitude and longitude in Singapore city- N G5 R" a8 ]5 }3 }6 T8 X coordinate_sentosa = [1.248946, 103.834306] 5 n W/ _& }# ?7 C m" B G coordinate_orchard_road = [1.304247, 103.833264] % |: B1 Y; }# M+ T8 o coordinate_changi_airport = [1.357557, 103.98847] " _1 D7 G. P2 [ coordinate_nus = [1.296202,103.776899]5 n$ p" d \" s: b8 `- l& D coordinate_ntu = [1.34841, 103.682933] : [2 ?, u$ b6 x8 z0 e2 P, R. a6 z6 B coordinate_zoo = [1.403717, 103.793974]3 c" {; W- I* I1 X$ m coordinate_ang_mo_kio = [1.37008, 103.849523]# @4 T" J" W" j9 p' U coordinate_yi_shun = [1.429384, 103.835028], F$ q9 Z9 B [& b9 I ' v: W5 q9 G- d5 ~! D# l* }2 M0 u # icon& }" z% w v! ~' A icon_cloud = "cloud"& z4 ?6 J2 v$ k9 r2 ~ icon_sign = "info-sign"/ C; f' W0 t9 G , a7 D0 g$ Y0 w. _. S # define the city map: x! S, N" P) l7 H/ ? # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}# B, |3 |: P c8 S' r city_map = folium.Map( 1 f Z, _4 r z- p6 t3 b: ?5 n location=coordinate_orchard_road, ! \" K2 Z5 i5 v/ i* ^ zoom_start=11, 7 M& X0 v2 w. u2 `! R- v+ @6 u tiles=OpenStreetMap): c) |* Y7 Y8 L: J! @ 6 J2 Z3 n/ [ e' u' { # add marker in the city map2 Y3 X" {# x: p* Z/ Z folium.Marker(( G7 T D; l' R, D; V coordinate_sentosa,# _, ^$ X; T7 B& o M% J icon=folium.Icon(color=blue) # V% O; s: K. D: a* A V ).add_to(city_map)( [8 o4 ^, `: @; Y0 y folium.Marker(- ?0 @9 G) Q& l3 s) [: Z1 G coordinate_orchard_road," @# N: v4 R9 x" {9 n9 W( I icon=folium.Icon(color=green, icon=icon_cloud)) q4 k0 g1 D4 N ).add_to(city_map)! p0 ?' t$ x% M( b. w 2 Y( I6 }' t' ^ # add popup 2 ?/ K4 I, U4 S folium.Marker( + n4 Y( E# K1 B# \* G- d+ u% ^ coordinate_changi_airport, ' o/ {% W5 U% L" t; J% v popup=Changi Airport, - ~+ A& G: T! \+ L* Q; v icon=folium.Icon(color=red, icon=icon_sign) 5 S9 V( j8 ~# h1 V1 }0 D ).add_to(city_map) ) u% r1 F4 e* P, ?6 d2 H* ?3 ]0 n$ u' ^0 a" z0 h- L E # add tooltips and popup % r4 F- U+ O* r5 P) a/ T9 q. A5 q tooltip = "Click me!"* V- v& L( V8 _' O; q 3 H2 U! U( r$ H- b, F" e folium.Marker( 0 `9 N0 v' {9 n! h. M coordinate_nus, 8 ~+ P% i2 e0 R5 K" X# t% o popup="<i>National University of Singapore</i>",5 ~6 m5 Z q- _3 w% e$ f tooltip=tooltip / Q7 o* Y9 ~. \: p5 T; ] ).add_to(city_map)/ R0 B) N6 k6 n & [ F1 o8 C$ y folium.Marker(/ }! O. u. w4 A6 c$ W$ {# O* p coordinate_ntu, & ?) n; i9 j6 X! F/ @ popup="<b>Nanyang Technological University</b>",3 d0 z9 E1 {3 k5 I" e# C' E tooltip=tooltip # h1 v F" @! O2 r( X0 l6 w% D ).add_to(city_map)9 V; G" G! R1 a 3 u% e# u* y( G # display city map * c7 P l" T. ]9 i! ] city_map
& y7 [! V; j2 F" N' {
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

* E0 c$ ^* x$ f* z+ P
# define the city map; e9 m0 K, w% ` city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) - Z/ v( e+ j" V3 _) T! H 9 e" u) X* J! }6 U! |/ d2 s # 在地图中添加经纬度, add latitude and longitude in the map when click 1 W1 t# G6 E; Z; X2 A! ]- x city_map.add_child(folium.LatLngPopup()) $ f+ G2 j+ n( `$ r/ L. u7 H7 N3 F. c - u2 K* w/ X% U4 Z0 u& ` city_map
& N0 M3 d) h& l. S+ l9 L2 a8 T3 Z 9 }) R- a" d/ K, R5 ~

几何形状

0 d* I( o5 b$ e% g; a D

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

7 j& Z8 o. w% \$ K
# define the city map/ v* t) r2 I- S, p! M city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)$ j% W% Q: E/ h+ l/ z # ?2 X c. L1 b6 ?/ d3 {) |9 g; C$ i # 在地图中如何添加形状 9 q* C( m i4 x- H; z, t% u # 多条边6 O$ s) P. O9 }5 [ points_1 = [& M5 h* n! T6 d coordinate_ntu,2 j; K4 R* V. ~: F. {$ c coordinate_nus,& H; B* X b) {# x& H% { coordinate_zoo - }) z' ~' p, z$ A, ]5 q! ` ] ( K* s! y1 K1 r% a7 |* a4 J* X1 J5 m- [+ _ # 在 city_map 中添加多条边,第一种添加方式' g" ^2 g4 C( J- k% ~3 { city_map.add_child(folium.PolyLine( ) h3 h2 {7 p! b: { locations=points_1, # 坐标列表0 N7 J9 Z& J6 O$ N0 J weight=3, # 线条宽度% n) ^, }, C9 [8 L4 ~# c! C1 y& `8 } color=gray)) ' N5 N9 H, K5 {% l. f3 B( f" l" _# ~ # 在 city_map 中添加多条边,第二种添加方式/ C, n. [7 R" n/ }( n9 ~9 J3 m folium.PolyLine( + Z l( m$ H0 a locations=points_1, # 坐标列表 ! ?1 V2 N5 V, I* ?' f weight=3, # 线条宽度 / w8 S; G& T" f5 ~# e color=gray).add_to(city_map) % u3 O, f0 ?1 B' H+ } . m- e6 N( H" U3 s/ J+ E U # 多边形2 s) \4 r& {' p& j* m+ [ points_2 = [ 1 Q6 x6 p7 |" R' F j1 \" k5 `: ]+ r" P coordinate_orchard_road,; [$ k0 H8 Y7 V6 s coordinate_sentosa, # [. E+ Z9 p/ } y coordinate_changi_airport0 p& T$ w2 \$ [, G) w W2 c ]" _, Z V3 m/ l. h# w ! _" x8 [4 e* c" _7 G9 j city_map.add_child(folium.Polygon(% M/ {8 F" {( u& s; r locations=points_2, # 坐标列表; d3 w( V: e1 W- D weight=3, # 线条宽度9 B# Q, t; ~& R color=yellow))- A' Q4 y& r* @" s- m; Y1 Y + s4 ]" ?# F0 t; D1 v # 矩形 , g) V h" M5 M! M bounds = [ ! e1 e v+ L: ^) M- \* k. { coordinate_ang_mo_kio,) y4 `8 b5 b b coordinate_yi_shun / D1 W) ~8 P( d6 `4 d ] 4 Y5 G' y& m4 P7 f$ h' Q7 C- \' K' N. ^9 W! A6 \ city_map.add_child(folium.Rectangle( 3 ~' Y; |; g/ o4 t bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) 5 k& I, f7 \; }+ v2 n weight=2, # 线条宽度 $ D+ z6 y* Q6 f6 D* ~ color=blue))5 I# g- k. c" u, a4 g4 A0 A ! ?" e% B3 `* x# h# v& F" k2 n2 w # 圆形, circle, radius units meters & b. s3 ], F* g/ p/ f7 J folium.Circle( a& h6 j& \- f$ E6 h' y. x radius=1000,( {+ x- f. J4 G% P- z location=coordinate_nus,5 q5 J/ _( N' G- I, O9 m popup="National University of Singapore", . q) h8 T- |/ Y0 Z color="crimson", " y9 C1 `& g$ r6 C8 { fill=False, 7 {: R" B1 C: V ).add_to(city_map), K* O# y2 s7 I2 r2 p# D / Q: d3 l8 E8 f! P1 e # 圆形, circle, radius units pixels& v" s# u1 Q z folium.CircleMarker(; i% f; E: j# P8 c' N location=coordinate_ntu, . Y4 K$ s; r1 r radius=30, V6 T2 R/ ~' M6 ~4 P2 G6 ^- G popup="Nanyang Technological University",0 R m% U, @# Z E+ @0 j color="#3186cc", & ?% r+ m1 b) Y* _( t fill=True,! @, d/ K( B+ P( u0 ] fill_opacity=0.3, # 透明度 1 u8 S( `' A4 B- u/ @% u; D0 R fill_color="#3186cc", 5 b( a; m% j& p& O ).add_to(city_map) ) F. |9 g/ W3 ^; }6 k7 a% H6 x! _0 h1 ]% N( N8 l/ I! b city_map
* V# |, w L6 E+ ^
Folium 中的画出各种形状

热力图

# M6 l+ c7 j8 a+ ^( Q

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

, ]9 W' s1 Q* J! v5 E9 [
import numpy as np 4 {! m8 p. P0 v' g+ P from folium.plugins import HeatMap: _7 N" v& s3 u: d L0 p: h0 Z' f 5 h1 P2 F x' D8 e # define the city map 0 s' l6 |2 D- U; k3 p city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)% r; N0 A# I1 S, l + ?& ~2 ]# n& y* W # 构建随机数据 0 @7 q2 i9 G% x% u j/ B1 J+ B data = (* g7 V- D7 F" ? np.random.normal( 5 u! g& |* `) U- k1 N size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +4 ~7 f( q: h% f; M np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) ! d% B0 d3 v6 I9 e ).tolist()2 h/ x3 e/ E% ^; u# K) ^2 p 0 q. \! F/ _8 o# ` city_map.add_child(HeatMap(data=data)). S: X) i) T# j. n7 p city_map
# H k& e p) o; D
" R4 q, a' d$ D% }3 e

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

. v/ c& T) b" Q, c6 G# T
import numpy as np6 u( T0 w& i# Q6 @5 P: I from folium.plugins import HeatMapWithTime ~+ ^/ E* f# C3 b ( @ p! s+ R0 K, h # define the city map " z! c2 V5 k# N3 _5 [1 k* |" a city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11): g1 _# V9 ^ r" x4 c/ z ! [7 `; @" A) q) _% ?. G # 使用 numpy 建立初始数据 1 v. M" r- v/ Z0 g2 U8 p# o initial_data = (np.random.normal(size=(200, 2)) *% W9 }( k, b4 Z8 j0 O np.array([[0.02, 0.02]]) +5 z7 Z2 r! o% \4 X np.array([coordinate_orchard_road])) * ?. Y5 v8 V$ e% _& v% v! j4 i+ m. X # 建立连续的数据, q# g" R4 J$ o1 M1 q3 { data = [initial_data.tolist()]5 [+ x# D3 @0 v for i in range(20): 8 B7 n4 ^( Y( R) y2 y) Y3 r& H data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()): X+ u' A- m9 J3 u " Q" f; M6 t: }& {: C # 显示连续的热力图' X; {; X( C1 \% |! |) M city_map.add_child(HeatMapWithTime(data))7 q# D# N( R: C$ c city_map
: |$ h V; P2 M! I. p 8 B" Y: V H: _- }* u7 m7 I7 B

经纬度点的聚类

! j/ ^+ a' p# B

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

$ U' y- k/ b4 l$ Y, c) X
from folium.plugins import MarkerCluster + ^5 O5 \: u/ Q" L: ]5 }3 B3 @ f; F6 M) r$ A # define the city map9 `8 {+ _$ a) s" X' Q, P city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)# U$ l: c* U: D P* x+ y+ |# x! W. U # 经纬度的聚类 & G @+ g1 F0 T, j* t: _$ Y8 C/ ~ # 在 NUS 的经纬度附近随机生成 100 个点;+ D; z. { M" T, M) O% S/ A data = ($ r( U9 E. o' g4 o: v1 Y" _ np.random.normal(size=(100, 2)) 2 O9 g0 v- z$ i" V. H% z% S% U" ~& _6 w9 J * np.array([[0.001, 0.001]]) + : ?% o5 q# \! {6 U5 t+ G np.array([coordinate_nus]))% K% B7 e6 ^# @. t- i' h5 K' ^ 4 f$ ]+ s" P ~$ J* ] # create a mark cluster object ) o L8 N# x( v/ ?/ Q marker_cluster = MarkerCluster().add_to(city_map) - k/ w8 R! A6 [ T' U) n0 G: `4 a! Y! F# M0 x5 { # 将这些经纬度数据加入聚类 8 c3 p+ j. ^# K" n for element in data: % C3 p: d* H! B5 V& { folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) ' N) p, ^2 Y* B% t5 S1 } f # x8 @0 w0 U& C( ?$ g' e # add marker_cluster to map 0 ?! A1 U) v( e; a, x9 N city_map.add_child(marker_cluster) r; {" Q( u& ^ n" n2 y& J 1 @. F# v% l/ Y! s% K/ S! @ # 作图' }1 U' {" @4 ]# U! P* F city_map
* M( ?% c$ k A, @+ c9 s' l 7 G3 D/ p& {5 v6 G4 r( F

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

, A9 o$ |7 ]8 Q! t6 d/ o

参考文献

/ x/ b4 s% g# T1 U' q! O* s. W) Y

Folium 官方文档:Folium - Folium 0.12.1 documentation

" W( Z& \& V3 s( Y - M) b d/ h# G7 _: e) F; d5 z5 p( M' V2 r$ `7 O) \ 1 r7 `% n/ t6 `5 |/ o7 B, u6 p9 }4 G2 _0 i1 I
回复

举报 使用道具

相关帖子

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