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

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

[复制链接]
! p& z. N {7 ]

Folium 简介

) O1 @% G6 `9 k& h4 Q

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

/ j3 Z9 e V; ^8 k

创建地图

3 v7 X' U; J- X

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

% ~4 s$ h. ^9 ^3 k& m
import folium6 B) D' G! ~+ j# u %matplotlib inline 1 n+ j. ]8 I# _7 d+ s 6 u1 b, y$ P0 J( q# N. A import webbrowser : B; S- F1 z$ `* r0 u) V) e$ ^0 x print(folium.__version__); F4 Z! X+ U1 E! N) q, r, } 1 T# T+ X* J, M # define the world map 0 y r$ ^" q' {+ s. V9 y; u$ ~ world_map = folium.Map() : S- E3 Z# x4 ~6 e7 R3 B0 v6 I # display world map8 u& B5 o' N- i& y0 a: p world_map# i( p, E) q$ B3 Y
1 {; l/ x9 q2 G6 \4 X$ t
世界地图

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

! G( ^ W$ `4 I) u( Y2 j

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

$ }/ H' l# x$ k% a6 T% d; i: Q$ R

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

; F' T, a# `( T \( ^

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

3 X C* D4 e: u, U0 a
# latitude and longitude in Singapore city + h& Q# G9 |. \1 y+ ~1 s6 i6 o5 V' s coordinate_sentosa = [1.248946, 103.834306] 7 X+ ~6 E- P. n" z; _4 s coordinate_orchard_road = [1.304247, 103.833264]5 B, h9 M! F9 w: D coordinate_changi_airport = [1.357557, 103.98847] ( A2 G/ d# T+ E coordinate_nus = [1.296202,103.776899] ' w- U A1 V# V; c+ R/ y3 c( N coordinate_ntu = [1.34841, 103.682933]! B$ n4 R1 N8 K coordinate_zoo = [1.403717, 103.793974] ! [8 O$ Z( z0 P( E3 w coordinate_ang_mo_kio = [1.37008, 103.849523] 8 a9 u2 j( d E# |( J0 s coordinate_yi_shun = [1.429384, 103.835028]& J# Y3 o# a9 \$ A1 c ' L( |! Y+ G" w# ^6 R3 }' r # icon ' m [! P+ ? B icon_cloud = "cloud" % a4 [( s' T) |7 T3 P5 a8 L icon_sign = "info-sign" 4 u n! T! e4 B' c# z& X, w( g& f. C4 M0 `) L # define the city map & H$ o' g$ c$ |0 ?# d # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} * L/ }6 i! p4 q% v7 X+ @ city_map = folium.Map(1 x* s6 g$ \% `% B1 S8 s location=coordinate_orchard_road, i8 U3 g; b0 n: z h) U+ y s zoom_start=11,: @* @) f% X8 ]1 Q _# d tiles=OpenStreetMap)" w) z- H# x- x' ^: e) d / R/ W+ p2 F0 X5 e4 Y # add marker in the city map ( ?- u; v' r- y. k) v folium.Marker(! x' L$ b6 T: u2 B( p- {- a5 J coordinate_sentosa, , e& W- z" a# c' h1 N- P icon=folium.Icon(color=blue)$ d" n) ]6 }. z7 q ).add_to(city_map) % Q4 h$ ^* }, D folium.Marker(9 C' m! U4 i8 i9 H coordinate_orchard_road,4 S' \! A* S( |; G, V; v3 ^ icon=folium.Icon(color=green, icon=icon_cloud)" U. b/ R; N4 B& K0 l ).add_to(city_map) , s3 s( K1 l) E & O, f* S6 Z' L$ P3 @2 T # add popup2 f( S$ k& r. @ Y& O0 ?& n9 [3 Z folium.Marker() |& c5 j7 j) [* S, b coordinate_changi_airport,( M4 g1 z: @ s; Y- c+ `! r popup=Changi Airport, 3 c+ j o) x; Y1 Y: l icon=folium.Icon(color=red, icon=icon_sign)( ^- v, f0 P% M1 M& \8 ^, y ).add_to(city_map)/ Q. m7 E) H, R7 I - x- y# U# j# @# c' x: z/ c # add tooltips and popup 0 H% E/ R" ~: \$ W5 D tooltip = "Click me!" , c6 t" _4 I* D) c; k$ S. \$ T) S# @0 }( Y: L/ k folium.Marker( 5 s+ R0 v w" w& ~& e- R8 m coordinate_nus,7 g3 `$ a7 u! _2 x popup="<i>National University of Singapore</i>",( v5 m; n4 t) }/ E tooltip=tooltip# O- Y" S9 g9 q7 P q ).add_to(city_map)/ k) I9 f; B2 z i; \) o4 ?# @$ x( h. T( b- K folium.Marker( 6 P" z% J+ B- O$ F, _' ]# @; S coordinate_ntu, " J( w* W) V* [' Z9 B popup="<b>Nanyang Technological University</b>",* G' i# M' h6 e6 r3 Q tooltip=tooltip 3 J1 `. w) G: S' H! f4 g( h ).add_to(city_map)* E5 y4 ?0 H) m9 [9 `7 }; U $ `: i! [5 _* V # display city map + g' d0 V, j4 Q% d% m' ] city_map
1 h: F7 G) Q' `. a
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

& K' Q/ T& }9 D' K% i/ C
# define the city map0 q" C' d# u+ g1 B& R city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)" U5 S8 p* n1 O$ Y8 y1 s - X/ N2 r0 K* h P# k" Q" s# c4 s # 在地图中添加经纬度, add latitude and longitude in the map when click ' a" @; W0 X: y' M9 }# H- ?* \9 D city_map.add_child(folium.LatLngPopup())1 a/ ?# Q x4 D " V+ }' U; s2 g. `6 I7 _- o city_map
3 p6 d3 {' d: Z G9 g: X) W ) ^$ }0 A! A* X2 j

几何形状

: \% ]# ~# z( }2 ~+ Z/ K

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

/ B* v6 k9 B7 m, E! u( H2 E; g
# define the city map H0 a y5 u/ n) r9 ^ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 5 }2 P. v( A' C; k 7 l$ b5 @( t/ v# ? # 在地图中如何添加形状: N o7 ? M! I- z # 多条边" C$ H$ M, q* G3 O! ? points_1 = [ 9 c- [% M" q1 c- H& u) V coordinate_ntu,; Y" P0 X* n: Q$ t9 c7 _: |! C0 i coordinate_nus, 3 e, r& G4 y2 w% \: _# } coordinate_zoo! G& v9 h; k* r; p$ c# k& S8 M* j ]7 f8 ~, b5 i( b4 Q* \ % }" A( t! ?: n6 d% [; i4 b # 在 city_map 中添加多条边,第一种添加方式 6 O4 q* q' V# N9 Z4 y/ l4 O city_map.add_child(folium.PolyLine( 1 k! f5 A+ p( O8 W locations=points_1, # 坐标列表 ' x2 J9 V4 p; S0 M8 o weight=3, # 线条宽度. L$ X- P6 U6 \ color=gray))/ p. q" U! o6 q9 B1 g 1 I+ @( b% e& O$ K* z% c t1 v( E # 在 city_map 中添加多条边,第二种添加方式0 c, F$ n! [& { o3 b: D$ E0 y folium.PolyLine(: K; m# ~# ?) c locations=points_1, # 坐标列表 . W( J5 s( t( H/ w @% c6 `6 c# b weight=3, # 线条宽度 . B6 x" f; Q( @ color=gray).add_to(city_map)" j/ I o$ a8 }* V4 {; o ' K, R$ `4 ?4 j( S9 e # 多边形 ) T6 B% f F) C, M: G% Y& l i" N, g points_2 = [( y3 _# V# l5 V3 T coordinate_orchard_road,( m i1 B- ?$ A( V) N coordinate_sentosa,1 E2 [8 z0 _( B2 z0 N coordinate_changi_airport+ {8 e& I" H5 o- H$ K ]8 n7 E3 Y- e( B ! b1 ^+ I! q" S2 P$ u/ l city_map.add_child(folium.Polygon( 6 K/ C' k. {) f5 ~3 B0 I! ~ locations=points_2, # 坐标列表; p1 o8 a4 [5 k7 t. b weight=3, # 线条宽度 " v0 ^2 W2 o" L1 X1 g3 @9 x* b6 V$ k+ S$ e color=yellow))( n1 i1 O1 y1 E# s/ G3 R 2 G" X' r$ q: j8 r# v # 矩形, C% [& {6 w- M7 N1 T1 O' a bounds = [ - i* N1 F7 r6 E' q) r4 t coordinate_ang_mo_kio, / {, g4 }1 b# B0 ?! f coordinate_yi_shun : _ p4 M! ]3 u9 N ]. H" k0 f0 B" }6 N% T - n( f/ x. }& M) \% F U" C city_map.add_child(folium.Rectangle( & r1 C3 q0 D. x5 R, v bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) ; V6 ?4 }2 f% |. L weight=2, # 线条宽度 , {8 ?1 M; T2 O. S9 \& x( G color=blue))0 Q* f1 E7 i/ b) j! i4 | " W) L. H7 g- e% ]& d # 圆形, circle, radius units meters* L( G+ X; [7 ~4 k+ y5 t folium.Circle(4 C5 ?; `- x4 y3 j' W8 x+ q' z radius=1000, 9 r! Q1 U" f5 f/ O& \ g& ]/ [ location=coordinate_nus,2 g# O+ y1 w F popup="National University of Singapore", ; E! y5 k- t& l5 r7 _( [; R color="crimson",0 M8 j- A6 j% E2 M0 M, K7 w a. r fill=False, * K: H: C8 j& [, O1 d# S3 X- V" ] ).add_to(city_map): P$ G3 j7 j- S, w: l, } ! c) g8 J1 K: m, o2 K) S # 圆形, circle, radius units pixels % B* a! [0 T \% D' |) B folium.CircleMarker( ! H. ^' p- [4 H$ u5 X: L location=coordinate_ntu,$ ~4 X- q9 g4 T: G6 h radius=30, 4 s3 ^: `. X: a( g8 ^/ ? popup="Nanyang Technological University",. }9 X- i" J" m: e" h0 y' H color="#3186cc",3 P7 n! l- U" d9 U a fill=True, 2 S% Z/ `9 ]/ m t2 Q fill_opacity=0.3, # 透明度 " s, K, t$ N# z5 s ]: | fill_color="#3186cc", : X+ J% g( s3 c9 x: ]3 ` ).add_to(city_map) ( }! L. w4 s/ D, i6 K& u; f 4 H0 H+ _/ f9 s* H6 @: D1 e city_map
& t' C" T$ B# M( u
Folium 中的画出各种形状

热力图

7 O0 Z5 U/ t9 l$ I# m1 Y- O

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

u; m$ b8 K: l* ]9 C) D6 g
import numpy as np9 O \- ` N4 T- m1 k( B from folium.plugins import HeatMap8 S+ ?3 S0 x9 p6 H+ i" i, F7 F 5 V- ~# `& _5 ~ # define the city map+ J e% o0 r5 T+ T# H city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)8 ]: a0 G# J# d! Z. ~ G$ Y8 A, m( r& G! ? # 构建随机数据 / W' a P* T# K2 V. [- ]# z7 n; Y+ I data = (. s6 w$ ^8 t$ `/ V$ E np.random.normal( * a$ q7 \. E2 }, [8 J) C$ h1 v; J size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + . E/ C0 O h8 X# f+ b+ | np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])! c/ H8 J* |+ @/ W* ?1 D& l U _ ).tolist()7 ?# E: U9 N g/ E6 F8 D% Z- v$ G; l& b : L1 s1 R$ v1 b* h' x city_map.add_child(HeatMap(data=data)) 4 B* X0 C# x& |" }7 y& G0 j6 w city_map
2 S! M5 t/ t+ Y. J
! j( Y& x+ R& ], i: z0 q

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

3 H9 g& H4 P) |% V" I$ l9 h+ m0 p
import numpy as np+ w6 J6 V( N% o, g# D0 v a' L from folium.plugins import HeatMapWithTime 6 L7 M: o* I8 ~ 9 d5 R% F' o4 E* a: s9 i # define the city map1 \6 t' e0 A7 e A: G& w city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) ' V, J9 \' n/ _- X# B3 B( }+ S. g # 使用 numpy 建立初始数据5 u3 b/ M8 D% Z1 n% e( d2 n initial_data = (np.random.normal(size=(200, 2)) *3 C8 _* K# b \ m; D1 } A& [ np.array([[0.02, 0.02]]) + 4 h$ J" E! P R/ O+ g( J' J$ O np.array([coordinate_orchard_road])) ( p' W4 Z: @* @2 @* z0 N x6 ?( L # 建立连续的数据% q) }( i6 G, e' P( m! ? data = [initial_data.tolist()]6 x. _( @4 u1 A6 t) X for i in range(20): , ?' C" }8 F5 H3 v5 } data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()) ' T- Y' p8 b8 l3 H 5 p. D2 c( ]% \# |3 C4 r- ` # 显示连续的热力图6 P) `+ |& f& J# f. d7 Q city_map.add_child(HeatMapWithTime(data))1 W! d& W# r2 r2 b$ y% T city_map
7 R5 G% }: ^1 I2 Q$ Y0 _3 v " ?& \7 [; \) Y% ^$ i6 d2 Q+ U

经纬度点的聚类

+ \& s' ]- f1 c U' L2 F2 o. S- V

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

" _! m' w2 i+ J0 d3 X
from folium.plugins import MarkerCluster& r) x4 S+ [4 L- L1 {8 B# H * L9 {% s4 E( a; {4 F/ C1 M# i # define the city map2 K* L8 c, O2 C* A( I) _ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) * G* }% `* E/ n0 q! ~/ v 6 |% g; ^: f4 b' a # 经纬度的聚类3 g0 d2 @' I% C* k # 在 NUS 的经纬度附近随机生成 100 个点;% @2 n7 |, k1 m* q' U data = (* b/ B7 ]- L3 m2 Q* B' d& j8 [" g np.random.normal(size=(100, 2)) ) @- _+ v2 | S. N% P! L * np.array([[0.001, 0.001]]) +, K3 q8 E! V$ G6 ^ np.array([coordinate_nus])) ) R& R" o4 H/ @: k + [5 a( E7 n' X8 ]/ j7 r' e # create a mark cluster object; y& _% C0 N9 z) R9 d8 ?! u marker_cluster = MarkerCluster().add_to(city_map): x1 Z3 d- q' |8 w1 }6 ~5 A - N9 q- {# Y. w5 Z _! G# L& R # 将这些经纬度数据加入聚类 p9 X4 d! j5 E: _6 t4 g, I for element in data: 1 f7 b k: _9 j folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) ! R: t% }: Z, \6 ]1 g4 R( R) d , p$ f; T4 T6 J! r5 Z! G6 b: ? # add marker_cluster to map [$ H3 A' C% x: C9 O+ x U1 {/ N city_map.add_child(marker_cluster) 4 x: V* l+ Z" C! i8 G- H % r% k' T8 P/ t1 s # 作图 , u5 T0 ` V- Z$ `' \8 E8 ] city_map
6 t/ n3 t3 g' s6 K4 ]$ e! G / Y4 \9 @- O7 j# x

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

# D& s$ P ?: z! K# @% `

参考文献

8 F/ O2 Y" n+ ~2 d* I

Folium 官方文档:Folium - Folium 0.12.1 documentation

! r- O1 ?# g$ G& W0 a! `0 J ' b% f) v# h1 W7 w4 [1 K% I9 ]6 p" Z6 k; x ( y: S# ?* I6 n( u M. W$ r % W# G' y( ` ]
回复

举报 使用道具

相关帖子

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