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

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

[复制链接]
0 c4 y( i* h. f& M6 F

Folium 简介

0 w9 ?6 U! Y" Q9 j

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

q+ ?! }9 O' E r; f4 w- w7 E6 {

创建地图

( J& y7 d3 \. [% o

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

$ I* O# S7 \( S# q) F1 Y
import folium/ `* L" e0 v5 E' ~- p! J %matplotlib inline2 G2 r8 x4 {) Z- d4 R 1 S6 I# ^ x' X3 M, }, X7 N import webbrowser1 T% g" E2 y! V; }* w$ U0 m 3 Y- B3 O$ Z% E print(folium.__version__)+ ^* X% f# b, P . E% ^5 I2 A% C! k! }. V# }8 n # define the world map 3 q/ {+ `2 v( B6 I* @: k5 a world_map = folium.Map() * X1 a% m& I' o1 w1 f # display world map 8 Q% g' ?) l# t; k% h* e world_map _4 T! _4 l. S
/ f9 S. L- _( D5 P1 ~9 p
世界地图

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

[9 N% @- [: I

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

' Z8 {: z% j5 ]. q' b0 {

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

~3 P+ P+ f. X* h( A* S& y; n4 A

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

. e3 b. T: H6 E0 @! n( C1 i n
# latitude and longitude in Singapore city9 o2 T" V+ O+ P1 B. L p6 o' X coordinate_sentosa = [1.248946, 103.834306]4 W& v/ P( S) A K' V coordinate_orchard_road = [1.304247, 103.833264] {% s6 V( X4 ]% Q- \! d3 L1 Y coordinate_changi_airport = [1.357557, 103.98847]8 ~6 @$ k& g) U0 ^ coordinate_nus = [1.296202,103.776899]: Q% E8 b, h4 }9 }& W9 _ x coordinate_ntu = [1.34841, 103.682933]' t N: e6 o; ?7 @" M! u coordinate_zoo = [1.403717, 103.793974]5 y W m+ y5 u7 R coordinate_ang_mo_kio = [1.37008, 103.849523] 5 m; f* Z( z5 k! Q coordinate_yi_shun = [1.429384, 103.835028] 3 E, \ @" F7 }4 ?, N" X5 k/ D6 O8 _3 ]& o 0 ~; K; R% |. F # icon9 s |; M$ X$ o0 C' l icon_cloud = "cloud" / h# s; h6 d! T6 h- _$ O icon_sign = "info-sign"8 V0 ?1 l+ y) i & M; u# P" w3 F' `* O1 f # define the city map6 c9 x, X! m) B8 h # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}% d/ T; Y# J' q+ ] city_map = folium.Map( 5 k6 n$ V2 y8 v- Z' U& Y location=coordinate_orchard_road,1 H) h0 Z- q8 q$ h% ]0 T zoom_start=11, ) a. s' q% V% m" \ tiles=OpenStreetMap) + F: N; i& D" W) Z; d- U8 j! O , ~) \+ R. }3 w# ] # add marker in the city map . q8 l, n6 m& A folium.Marker( 8 R3 S, d5 B* K. |3 N1 N7 } coordinate_sentosa, & p. a+ n$ m: d: U. ?) U( S icon=folium.Icon(color=blue) 1 v' u3 ^# O) o# V4 G( s3 c$ Y% I ).add_to(city_map)3 M; Q- X* ~. C5 i, Q( h folium.Marker( " _1 T2 \$ U! ^2 k) _0 D coordinate_orchard_road,% m2 f5 P: I- _1 i( j" U icon=folium.Icon(color=green, icon=icon_cloud) ?: m" o" L8 P8 U8 V+ z9 |8 V ).add_to(city_map) + F. z3 N& q. r. a$ `. M4 A; a( q3 J+ Y # add popup5 x: L) b( y) H$ j folium.Marker(+ o4 G6 C) w! }0 _ coordinate_changi_airport," ^9 j! Y) v$ o. ]/ E2 Q( R2 Z popup=Changi Airport, ' k; X7 R+ f2 k icon=folium.Icon(color=red, icon=icon_sign)- n4 a. F3 t8 A6 `2 ]- _ ).add_to(city_map) 6 K# L4 f0 E- U0 i Q1 ?- ]+ w/ q6 K # add tooltips and popup , o/ f# O& q- e, W tooltip = "Click me!"0 |# _' f V- ^2 [ \7 \ 2 S9 [) [1 ?3 L# ^ folium.Marker(8 _$ d7 k- _4 U3 K5 U6 t; e coordinate_nus, $ C, h4 \8 c5 ~) q! |1 @ popup="<i>National University of Singapore</i>", 8 a2 m8 {7 |& O1 k. e tooltip=tooltip! e" z, J: j" L/ P- g ).add_to(city_map) l3 ^' z+ g3 H: M6 w0 B 3 N$ i6 {" C' R* U2 F) \ folium.Marker( & C/ P! f4 T/ \) b5 B. S6 a coordinate_ntu, / c' K5 A5 d0 V popup="<b>Nanyang Technological University</b>",* a, O& {- G C: N tooltip=tooltip 7 ^7 a/ t+ v+ V4 E, d3 T2 R9 O9 o ).add_to(city_map)2 i6 P' C1 U1 q 6 f! R( W+ O6 ^ # display city map$ O2 w9 t" r' k' A* r city_map
$ C" T) c6 N0 h# n& g+ p' {7 \
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

. t9 ]/ M9 Z c" S" f! l* D
# define the city map / L5 D+ ]- S+ { city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)$ ^# k% f- c; K8 F9 P 4 T3 O1 @- a# E # 在地图中添加经纬度, add latitude and longitude in the map when click 5 L4 o, m& t7 b9 p" Q1 F/ ^3 z city_map.add_child(folium.LatLngPopup()) 9 S! g9 X+ X: Q$ h N2 ?- h* ~9 z- S% G city_map
/ L6 }! h2 @. \+ [& j! R ) p. q, ~; j: F1 h: g X( C

几何形状

( y( d7 H. [ g# j# f3 ^

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

7 L5 [( L Y, S0 Z( w( B+ g% Z8 \
# define the city map . `4 y- {/ c7 Q. q0 F& d& ^% U city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) ( A5 ^; f6 M |; m1 V" i% \; A2 E # 在地图中如何添加形状! K$ h' b" S$ [& _ # 多条边8 ^7 l3 Q3 |; r! O' t! H% K points_1 = [ % P0 y- [" D7 m: P coordinate_ntu,6 ^! ]6 @3 h5 G coordinate_nus, ; ` _' Y8 h( H0 p coordinate_zoo, i4 i7 v" O8 H4 _ ] / o+ l& N( ?# X 9 A$ O1 b' E. D6 L8 O # 在 city_map 中添加多条边,第一种添加方式( [6 i# e$ ~- R% ~8 d. r( X7 B- b0 y. l city_map.add_child(folium.PolyLine( ' O9 ]. N/ g1 B" F. M' A& A locations=points_1, # 坐标列表+ d: q2 U$ F; P7 F. V' A) q weight=3, # 线条宽度0 E/ L2 S! g2 s, l color=gray))$ C8 h7 o% |6 \ D/ {$ M : S7 k: e% g6 [# n/ F8 k, _ # 在 city_map 中添加多条边,第二种添加方式 3 x5 z- B% C" b1 Y$ \$ A# T' k folium.PolyLine( ! ^; B6 V" o( F3 F: K( S. N locations=points_1, # 坐标列表) `3 [0 `; f: E0 I weight=3, # 线条宽度( {6 i: V$ I3 v A; ^$ P color=gray).add_to(city_map)3 E8 u* I7 {; d/ X3 N. m. J - _9 L5 P+ [- F" C9 r # 多边形) V, _3 P- v! y+ V8 y points_2 = [ 3 x, c" E- H5 `% W coordinate_orchard_road, & }% j& \$ b6 a: L7 q coordinate_sentosa, $ O" t: j/ h3 h* e coordinate_changi_airport ) p- f6 o2 u# x* H2 F( r ] - _0 A0 L8 m1 R9 B2 I8 w2 c. f; }- x/ H city_map.add_child(folium.Polygon( & R1 }; i a# m/ l7 b locations=points_2, # 坐标列表 ( J ~* o5 ?, X1 a weight=3, # 线条宽度 : |9 T& E+ z, g* g/ c; g' W$ H color=yellow))* M" a K3 N3 W# o# b $ N* V" }* N! o # 矩形, p) u4 E! [& i# Q a% `- M" ] bounds = [ . ~7 B0 K k5 G& H& _ coordinate_ang_mo_kio,9 l$ b! t" f9 ~ Z7 `$ N3 R coordinate_yi_shun 8 A7 q9 M. T, X" m) ~, g ` ] / x+ q& a1 y1 @- [; f% G; {, h, g* W; ]9 k6 Y city_map.add_child(folium.Rectangle(: L, Z2 l, x! H& _9 q% D bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) - o J" a! |5 S0 o* f weight=2, # 线条宽度, A9 ]5 o! F; ?( l$ ? color=blue))3 Y' t; e( b8 v8 b4 d # r, E$ B5 w& M4 ? # 圆形, circle, radius units meters ' `5 T( f* C! R6 c/ _( y9 x folium.Circle(. P" e- c' @* Y, q: y0 F radius=1000,) _9 }+ z* z9 _$ s' i* C location=coordinate_nus,0 Y+ G! ?( |4 f. ? popup="National University of Singapore", + L1 K# z# @0 m, G0 t- o3 J color="crimson",3 `+ |1 i3 g [* [- ] fill=False,3 q/ `9 j7 [ W: ^2 M8 a ).add_to(city_map)' \; Y# d7 U3 e9 O# N5 F ; i7 O+ O8 [1 A # 圆形, circle, radius units pixels . X E! m: l4 e! H folium.CircleMarker(: n" y# I) _* }( B- \' u v0 B% V location=coordinate_ntu,, F3 }3 D. x; x radius=30, ( E" S3 A% A- ?' V popup="Nanyang Technological University", , ] R X4 ^, I# W$ i color="#3186cc",6 h/ x) [% ]! [" I( J fill=True,. j, u1 T3 t" E& k$ a5 U, \ fill_opacity=0.3, # 透明度. v+ m o* S+ w0 w5 @% T fill_color="#3186cc", * i, E5 ^( @( O! Y* y( } ).add_to(city_map)0 S. O7 {/ g. U3 e ; J4 z% u! }6 W/ F& u5 ?0 r- l7 N city_map
) C3 `# }5 g: u. g2 Z" d
Folium 中的画出各种形状

热力图

1 j6 s& p- w0 L! `# O6 ]

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

5 t) U0 w; n& n4 X+ b" s8 p
import numpy as np! C ^7 \3 A# O5 J* |. } from folium.plugins import HeatMap / j( N. O3 ~, A% t2 J. J3 S! ? 1 v r9 {1 D9 p- s3 o g* b # define the city map, e) i. x8 d) X/ S+ S! y( r5 u city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 7 c8 h/ S0 A! r e& P( Q) r) t3 B }3 i- q* e# i K # 构建随机数据 ) r) k% t6 x) `* N- i* h# s# T3 \ data = (9 s: O! [; ~3 o* p np.random.normal( K, B1 y& | m' F! ]7 g1 A# | size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +; Y3 [ I. K. Q1 a np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) ( ]3 r7 y) L H6 B, t( [0 {6 O ).tolist()' V' n. x( L; _6 x1 d+ X8 } & Y% n9 K% E$ u$ T" f city_map.add_child(HeatMap(data=data))* X: L; i% {1 K city_map
. ` C4 N' c0 `+ r; Q' R
; {. e' i" b J1 @, g; u. j2 _

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

) G9 ?) ]+ w$ e+ N. j" J2 x
import numpy as np- k8 h4 {1 d: z* \ from folium.plugins import HeatMapWithTime ; l# h$ Z: A8 M) } 5 r2 `" ]1 F4 C2 T. Z4 R # define the city map9 q1 D2 K9 r; t6 L J$ n city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)9 B9 D. p/ {- G2 R+ ^+ H/ Y, E 4 h3 W! j8 m f. W3 N | # 使用 numpy 建立初始数据 ) ~9 O7 |/ V0 n2 t S+ R) r K initial_data = (np.random.normal(size=(200, 2)) *$ X( @5 a5 r" l: ?' f np.array([[0.02, 0.02]]) +/ q6 n# w' |- `" C! q+ y) U, @% I np.array([coordinate_orchard_road]))7 h$ A0 v0 r, T2 h$ v/ q " {) a5 S ?: ]/ t% p # 建立连续的数据 # ^6 u1 \3 v0 H/ O6 x2 Z data = [initial_data.tolist()]8 _0 p9 G6 l. Y for i in range(20): y# @: D9 G4 I9 }! W7 n+ l data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())# G( D! G8 t' B" \' o* e , _2 i) d5 U1 v # 显示连续的热力图 2 P3 P8 u; h% S2 k- D0 M7 u city_map.add_child(HeatMapWithTime(data)) 6 Q# P9 i8 }2 D, y( H) \0 E5 s city_map
' @! s) P3 T, Z8 e2 ~1 _) F4 n% d7 x + e6 s0 ^! L( `# j4 R

经纬度点的聚类

, Y: v& M2 z P8 x; D9 e

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

3 U( {- m7 j, ~. q! l
from folium.plugins import MarkerCluster& V9 J3 V$ C. g ( B U m7 _+ R9 | # define the city map7 |6 Z7 e- k. [" F" [( F city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) + V( `5 Z+ Y* E9 Y r7 T: l $ p6 j s: ~5 {- x; x% u* t # 经纬度的聚类 P* g- [, n& N# j7 E% N # 在 NUS 的经纬度附近随机生成 100 个点;/ S2 l9 Q7 |5 _: f0 i data = ( % O8 |7 }( \ u np.random.normal(size=(100, 2)) / }: d x6 l6 V U8 Q; w5 |, i% L( E * np.array([[0.001, 0.001]]) + . ^( k$ R1 Q( v& A9 I; l7 w np.array([coordinate_nus]))+ K2 G+ W2 ~1 Z & k7 n8 q' z- C0 w/ p% ] # create a mark cluster object " a2 Y9 O+ _, |5 K marker_cluster = MarkerCluster().add_to(city_map) # p% S; Q- a$ c7 I! N+ d4 u" k& s. H & O9 X" c* @* l! [ U # 将这些经纬度数据加入聚类, m4 J" M8 a' A, h for element in data: # ^' T8 v4 z% W+ i. E4 j folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)- C* W' `) ~& J, P* R 7 D9 Y/ ~5 Q, N( D, }3 ] # add marker_cluster to map- |6 K, V, W; u& ? city_map.add_child(marker_cluster)8 s1 D% x. X$ A: B1 H2 N5 R0 g 7 I. ]. ?1 K+ i/ V: r # 作图# _9 `8 C7 M. X% A6 `5 g# n b city_map
) f/ H4 \9 G' S7 Y5 W9 \5 O7 [; N $ f" C0 {+ r, O3 @$ W

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

8 V: [' k. S5 m) X

参考文献

5 U& g; y' {' z

Folium 官方文档:Folium - Folium 0.12.1 documentation

1 x) r* D1 E/ g 2 `3 {0 k" R" t; m6 O9 i) m . b9 G+ {, e" k# y) N9 o% i5 I$ I3 T) \: A$ h 0 x) l3 b4 h5 t% O" u) D4 w
回复

举报 使用道具

相关帖子

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