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

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

[复制链接]
& M n; K0 y, _% M8 _$ j& z) L/ P" a+ X

Folium 简介

. h" o& h* d& m( H% B

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

7 O; }2 V5 b" D( Y, [4 U J( p

创建地图

3 o' z; h1 f! p2 a) y

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

- q. I; F( F U x# Y
import folium/ |& e. U2 F3 c1 ?% C ~4 V/ q %matplotlib inline3 V& s/ e& }* y0 l* H0 p) p* u * A$ W- V" X! b3 b. g import webbrowser , ^) `6 @8 m ?8 U& n/ g% g* b4 ` print(folium.__version__) F( S" S9 m1 |- F+ ^* @8 x2 B8 I1 C, g4 s# u! m& ~ # define the world map ' h3 n6 j: M7 L, r6 O world_map = folium.Map()0 l- O9 a) e7 \" U W: Q/ f # display world map4 G# M' ]7 u0 I* X world_map ) t5 x7 u* P4 n+ E, M
; ^( F* p5 T6 X5 z- {2 W: E; `
世界地图

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

: N- F! X9 _8 ~" ?

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

4 t! ]! o8 l: u o, Z2 E

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

$ g( V0 g" P% |

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

: \9 m0 y' r$ q
# latitude and longitude in Singapore city" a$ l6 ]0 Y/ b/ |' O: B7 g coordinate_sentosa = [1.248946, 103.834306] 9 ^- E6 N; o( Q( {/ q, u coordinate_orchard_road = [1.304247, 103.833264]; n7 e/ a$ L. M coordinate_changi_airport = [1.357557, 103.98847] e5 Q- V% r9 H7 j coordinate_nus = [1.296202,103.776899]% @0 p4 m, n, V* T% ? coordinate_ntu = [1.34841, 103.682933] " C' ?& R" e" P2 [ coordinate_zoo = [1.403717, 103.793974] / n2 y" O- P- Y2 x; _- b2 Z. ] coordinate_ang_mo_kio = [1.37008, 103.849523] ) i2 R- M m* @" Y3 }- m. L0 A5 O; e coordinate_yi_shun = [1.429384, 103.835028] 2 z$ F2 p0 a. l8 ~: \( I; `0 Z5 ]5 \& S3 Z" `) n; G4 F # icon * A" D( k( {$ Y, L$ ` icon_cloud = "cloud" - Q) o: e: z4 F4 ]- C- h. a icon_sign = "info-sign"- \+ y$ f- B8 C2 G 8 h5 Z& @- q8 ~( t9 U2 ~7 j # define the city map ( b: E1 i$ B: X6 m: |! i # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} # ^' \8 Y( e; [7 @$ e7 Z city_map = folium.Map( ' k" [5 Y, m, Z location=coordinate_orchard_road, ; {4 A, R' Y' j% ?* Q zoom_start=11, # P7 w* R1 t: l" e tiles=OpenStreetMap)0 O/ ^3 B3 K' \6 k8 D4 N1 ? + ~9 ]7 ^0 H7 H0 Q # add marker in the city map+ I L/ |4 [5 L2 S# L' Q folium.Marker(+ s+ R. b; c1 m% J/ b+ @1 ? coordinate_sentosa, / D! U) G% C( G& B* b) p' L# l icon=folium.Icon(color=blue)) [+ W, F6 b" u p& s ).add_to(city_map)6 H, t' I% ?* } folium.Marker(2 D( G3 A% H' ^5 b* F coordinate_orchard_road,& {9 W! Z; d5 j @% d& J icon=folium.Icon(color=green, icon=icon_cloud)( c: C6 I1 a7 ^9 } ).add_to(city_map) 9 v' o2 g+ K& [ 4 Q5 L4 Y' H7 c3 J+ l # add popup* L- K: e& V6 V4 g folium.Marker( $ d$ M# B5 ^ ~# I0 u coordinate_changi_airport,: x# Q0 t; ^ I7 S0 c popup=Changi Airport, - n* D& r7 W+ Z$ Z icon=folium.Icon(color=red, icon=icon_sign) . I6 [, Q/ k H, p B4 _4 [' n) \ ).add_to(city_map) # p" h' s& x5 [# \$ O* r% x. r2 i3 `* x$ j1 x6 p: c# C# @8 G # add tooltips and popup$ [0 }- S1 |9 p0 F tooltip = "Click me!" + [9 M7 `8 O" a0 ]' u2 P' q ; }# K" V- I5 Y, F- @ folium.Marker( 1 _" z3 d8 r) ]8 E1 p+ L2 h coordinate_nus, & S9 O9 j: w& d5 Z popup="<i>National University of Singapore</i>", 7 r0 H* V7 f9 Q6 s3 r tooltip=tooltip7 v, p1 X3 N: F9 ~ ).add_to(city_map)1 y, j+ v+ o* P$ h$ W 7 |1 D2 A6 d a folium.Marker(0 X2 L6 n& s0 G1 Y% R G5 e coordinate_ntu,; T- a" s' S5 P! D! \/ G6 O6 w% V2 p popup="<b>Nanyang Technological University</b>",( \4 v" N* R1 ~0 R tooltip=tooltip# `8 P7 m9 L' s# J. N. H ).add_to(city_map) - p+ F% h p$ U1 W$ g9 c ' b h2 F9 o+ ^3 Z* `7 A* [* m # display city map* M/ X) J2 i2 j/ J6 I$ [ city_map
) z" ?* x: u6 ^7 } a$ z
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

0 s& h7 r1 z2 ^4 P. z0 ]) I
# define the city map. h: f3 W, K. e( \* T, G, S- V city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) - p5 N9 E5 P% l2 `" b* _0 { 6 A/ J9 H& Q) r* V2 B' t5 i # 在地图中添加经纬度, add latitude and longitude in the map when click ) b/ O; x, L5 g5 p1 j city_map.add_child(folium.LatLngPopup())! f+ Y! ?2 Y: |0 \5 F+ r % l1 b% g' u6 D! r" b; d city_map
/ E& M% q( e k( j g / G6 w4 o# t+ `2 y M9 ]& s

几何形状

; T* J" Z7 X, x1 S: q; }7 V

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

/ L& ^8 u! m& H0 u$ _# o5 w
# define the city map $ V+ r0 S6 U5 }. E4 ^% ~ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)8 N* ]# H7 K" ~* C5 e" b# h : V5 W: B6 Y5 p' |2 _, m # 在地图中如何添加形状 6 R3 ^; q e$ D- c # 多条边. w# @5 F- M" k5 I& C4 Z( x points_1 = [ f- ?- Q8 c5 y1 w8 D/ } coordinate_ntu,$ K; e6 H) e. T2 b8 z$ A$ s coordinate_nus, + E" V4 A+ O8 ^$ G& }! L coordinate_zoo+ \- ^$ K6 j2 |' ]& O ] - p: w6 n6 [3 @- S; r 1 Q: T8 ]2 ?5 \+ [' K # 在 city_map 中添加多条边,第一种添加方式$ y* ?4 v+ i$ m3 G city_map.add_child(folium.PolyLine(' Q- r$ A1 k7 g8 D' e/ n# L locations=points_1, # 坐标列表 , F; |" u' Q. @$ i: H weight=3, # 线条宽度 / D# S. q2 s9 N! e* g/ q color=gray))5 y' Q5 m6 z( k 7 f& H. O9 j/ n2 `+ Y O # 在 city_map 中添加多条边,第二种添加方式7 R5 k* g+ m& g, q folium.PolyLine(+ e) C+ p. _3 j9 n! v locations=points_1, # 坐标列表 7 y# I$ V. Z- a1 S weight=3, # 线条宽度7 `& ^4 z* R' z color=gray).add_to(city_map) 7 U" z2 \" {3 t( T( O! f. B( W% d$ U6 v4 [7 `$ C5 O4 Y # 多边形2 g( K5 R% ]* o: w. ~7 R; {+ i# C points_2 = [. Y- p. D/ a0 L9 f- c* J i coordinate_orchard_road,( B- T$ b& j1 i& f8 `4 \ coordinate_sentosa," b" G8 i+ y8 Y% H: x$ }) m coordinate_changi_airport, ~+ ~; ~/ D) r ] 2 I- |+ k) d* w$ F % N( R- z; c6 f4 `7 X7 \0 N2 Y0 B city_map.add_child(folium.Polygon(9 D% ?! |# ~8 `2 Z/ \7 p locations=points_2, # 坐标列表 & y) Q- d7 M% B% o# P weight=3, # 线条宽度 3 ~4 Y$ K* ?8 |" F( g* t color=yellow))9 J' o+ W P2 Z* d ' o2 p: Q1 }) V6 i, d' g # 矩形 0 ^! ]0 t: U% o- M9 N3 c4 D$ y bounds = [7 r- v* |7 \& A; b4 f) O! ^ coordinate_ang_mo_kio, 2 j/ e' X0 w& V0 R7 x coordinate_yi_shun 7 I. E- P/ u; ^* M- E: D! ^" x ]! Q* |' }9 M% H& F 4 B2 X( W- y/ h; Y* t city_map.add_child(folium.Rectangle( ]3 L, @/ K, S; R+ t+ u; `2 E+ p bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)$ n+ f' D# K7 k n weight=2, # 线条宽度 # O9 i6 {* H7 g% ? color=blue))+ j: A2 o( C; r5 m0 I 6 ^# ?4 D! u# k# j7 F # 圆形, circle, radius units meters ) \4 H: a, i! L. d! T folium.Circle( , C5 {) y3 W9 @7 Q radius=1000,+ X* e# N" x9 ?% p location=coordinate_nus,% U" t# t8 C! k7 a3 f2 e3 D popup="National University of Singapore", 6 O+ h+ q1 k) u0 m# T' @ color="crimson", % M8 V* I8 n* o- f, Z$ N fill=False, A0 Q% ?* E# S- U ).add_to(city_map) ) m" o( x+ |9 S / D2 t4 ?" c, K7 |2 p # 圆形, circle, radius units pixels" K V7 m0 Y' c9 g2 G# M, [ folium.CircleMarker(0 f5 X/ | x" N' K location=coordinate_ntu,( T* j- c r. W4 C3 ` radius=30,3 T7 j: O) R0 y3 `) |: K) U$ z popup="Nanyang Technological University", 4 j3 @( m }/ w' i: k, {9 p color="#3186cc"," q% u6 h6 w# N! h. | fill=True,% d# e' X _: p fill_opacity=0.3, # 透明度 ! h' `1 ?" a$ q! K$ ~- W1 [ fill_color="#3186cc",* ]* u7 h% Y6 } y; M9 M ).add_to(city_map) ! V3 l8 ^0 S6 r. p1 Q" t & D& J( n' _0 H! k" z! m city_map
' s/ H% C9 Y/ w. G+ R: M2 S$ ?1 E2 d
Folium 中的画出各种形状

热力图

: c- s' f7 @; y

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

. e p9 ], ^9 }5 x
import numpy as np' M8 ~! { Q0 q7 S from folium.plugins import HeatMap % m, E3 t$ f1 a9 o& S ( T7 P. M( D$ F" C; J' c # define the city map8 [1 `3 M$ C# k- o2 T) ^ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)- D/ U* ^0 }5 _ 7 \4 V# k& r! _7 W) x x # 构建随机数据0 D8 U( t7 n9 U data = (& L( L/ Z& {# d8 j i+ n: O2 s np.random.normal( ' X+ }3 O6 |' L7 ^ size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + ' V# l' ~! m! u" ]9 p1 [ np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) ! N4 F2 i4 s6 `& N8 S* A ).tolist()/ m4 G# V( y4 r ] * O8 s. ]6 S e# A s city_map.add_child(HeatMap(data=data))( ]1 p8 G/ Z. A; S5 m city_map
9 ~) r& {6 q. U: S& ^6 c
& a* C: n( `) t0 S

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

1 |+ A' l# c/ K7 i9 y# j
import numpy as np , `& v5 z5 c8 s8 z8 g4 |7 V from folium.plugins import HeatMapWithTime & D3 W& p: x3 V7 O4 M7 W . C8 x3 `) ~! B- }$ S # define the city map ( y$ x* J0 f4 t+ x% E city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)- _" @1 i6 }. ~. I4 Z6 _ + s* ~! h) k8 K4 U" [ # 使用 numpy 建立初始数据 $ B( N/ l( O: y" z5 k initial_data = (np.random.normal(size=(200, 2)) * ! s% u" T& l7 V np.array([[0.02, 0.02]]) +% ~2 A/ i9 P/ y, e9 z4 I8 } np.array([coordinate_orchard_road]))* _3 m' @6 R! m : k8 l6 n6 ]1 p& k/ J # 建立连续的数据" W3 y7 F4 G2 k Y1 }3 Y# w* A data = [initial_data.tolist()]$ x3 h, `& h V- L4 F* y for i in range(20):* Q% f! e4 H4 p, T! J data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()), x; y( }$ B9 ?5 x- x: \& R& e 3 u( |$ i. {+ ^ # 显示连续的热力图 9 H( k$ B# \, c I" l city_map.add_child(HeatMapWithTime(data)) 3 k/ \: ?- b5 r city_map
' G) t4 x& e7 j, c 4 ~6 t. o: i( [4 F" B7 _4 p5 Y

经纬度点的聚类

: P% u; ~! f( q. v( M- @6 V. c

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

" y1 @4 q" \2 ~5 w
from folium.plugins import MarkerCluster7 G: b' e+ O) p" Z 5 [5 E- `% g0 y8 Y$ F( U! e # define the city map ) P6 Q" L7 \% x( R' q% G, T4 P9 g) B city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 9 {4 K5 Y3 S$ r+ l8 H D0 Q/ m& _6 A$ e% r; O # 经纬度的聚类1 \% Q" y: y5 U+ |- c% c/ U # 在 NUS 的经纬度附近随机生成 100 个点; 1 }3 v& q2 P- z' c+ Z+ T data = ( ( H( D+ A$ q7 Y+ ?' h np.random.normal(size=(100, 2)) ; \( a$ f: L$ [* u * np.array([[0.001, 0.001]]) +" m' L# j, ^- }8 w. F6 c np.array([coordinate_nus]))3 V; a3 [3 b9 l, J+ \' s$ u ( S P9 b% o- p0 f9 j5 o( d # create a mark cluster object" D9 [: x: U3 \3 T/ V0 {4 s1 F marker_cluster = MarkerCluster().add_to(city_map)% I5 B3 h2 C& A- v8 d! N( v. C - y# B: K. V$ H. z$ M # 将这些经纬度数据加入聚类* r* X9 l9 _2 E j for element in data:) z, N* }& U9 x8 E2 C% a" k3 [ folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) 7 _6 m# D. o% P7 J4 n/ g% ^' O# d$ p2 e0 E2 \- c' Y # add marker_cluster to map, L6 a3 }; j7 ]6 M) G. b% R8 R% l city_map.add_child(marker_cluster) % V0 X/ g/ \0 f- w* A8 R0 t7 |( H& x$ d" d # 作图 ! Z+ {5 [- r( h. Q city_map
' W# v$ z* D- H: P: Y+ x % g4 M) B) M" y. U$ B

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

6 E8 k* u3 ?+ B1 R( }

参考文献

3 N" R9 j; A% @1 O' f$ \+ A

Folium 官方文档:Folium - Folium 0.12.1 documentation

" H0 J" g9 C( V/ K/ c- N$ Z" M- K) ? R9 p# r& A1 a$ J3 S ( J/ [4 ]. A, w* x* ~9 ] 1 D# K5 ~8 e" } }1 ~: s7 |' V/ H: V- M( W2 d
回复

举报 使用道具

相关帖子

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