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

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

[复制链接]
5 d+ c* p3 \# z1 Y1 Q- f" w! n

Folium 简介

: Q+ B/ J9 j) ^: b$ x J( n0 V

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

; ~/ e" u6 o7 Z w7 o+ F

创建地图

" K/ e* H. I9 U

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

6 B* o+ r# C: S9 F' U9 e! A
import folium 7 J/ B$ Z5 B6 \, @5 |. a1 N %matplotlib inline ' b8 x: `% X1 m( g. @7 `# p/ }( S/ A' o9 q7 P, l% @& f; M* ~ import webbrowser6 ^! x/ I' U8 C1 m& m' D 0 Z& A6 j' G# [( ` print(folium.__version__) $ k* W a6 n2 Z2 q0 L* q , \2 j4 P$ W6 v; K6 W* P- N K' L' Z # define the world map7 u# h! i7 e$ i+ p' q world_map = folium.Map(); B6 Q" E* Z! M2 c5 ~# ~% [ # display world map: @5 H% k/ e( g world_map 6 M# i$ j0 L- Q
) |4 c3 {9 }6 D) j0 B& d; N
世界地图

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

5 R2 m1 }. C: n

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

% u5 o' k+ d8 m8 U2 E% C

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

1 G$ B" m* {$ E/ M- F1 e4 D1 u8 w7 K. {

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

( e T/ I, s/ q. Z. w Q
# latitude and longitude in Singapore city( `5 {9 h! H( V! d4 I coordinate_sentosa = [1.248946, 103.834306] ( _; _7 z7 k+ q ^6 ^8 e$ g) g3 |" e coordinate_orchard_road = [1.304247, 103.833264] 6 {0 R( S y; i/ h6 ?8 V# o5 w7 W coordinate_changi_airport = [1.357557, 103.98847] . B( j* @8 f7 }7 C coordinate_nus = [1.296202,103.776899]' I7 e: h6 I$ Y$ g6 { coordinate_ntu = [1.34841, 103.682933] f2 }5 j* b9 p8 }, G2 E! M coordinate_zoo = [1.403717, 103.793974]1 O6 l" o4 j( T g5 k9 S1 }4 z coordinate_ang_mo_kio = [1.37008, 103.849523]1 O6 M$ A5 p; S coordinate_yi_shun = [1.429384, 103.835028]5 h4 S7 |3 Z, ~8 Q i. { 4 E6 s( E$ t3 C/ b0 G # icon) w4 D5 t$ ?/ S+ O8 o icon_cloud = "cloud" . |( Z" J2 h7 @. m* v8 I icon_sign = "info-sign" 2 @& B: A( x1 i+ R. x+ P. B3 c1 C$ d, k+ O! o0 L! k6 F) ~7 j6 I9 j # define the city map 7 M7 W/ H# z& u: L; K: `; K" k # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} : Q0 h8 ?- ~1 l3 k- r$ w city_map = folium.Map( - `% M/ G0 B* w' ^7 s location=coordinate_orchard_road,$ P& H |0 b: G$ N4 B zoom_start=11, 7 C; r2 \8 q8 T8 L6 @; M! C+ r" f tiles=OpenStreetMap) 6 e4 O% f; [) M/ q( [% V3 @% i& O; e' k) c5 C D # add marker in the city map0 P% Q# C; k0 s; ` folium.Marker( / W: f) I7 ^) K# h coordinate_sentosa, % l& ^5 \, V; x% Q) e icon=folium.Icon(color=blue) : z& C/ I3 h0 y ).add_to(city_map)" ~" G2 |; U; W3 A folium.Marker( 4 }6 |; f$ x e' X+ } coordinate_orchard_road,0 B( z* X5 K" P8 V icon=folium.Icon(color=green, icon=icon_cloud) - o' d4 \; g( d8 J$ J# x/ Z6 |6 d8 ? ).add_to(city_map) " Z% c/ T, [( `' L7 v9 `4 _7 E( ~! K/ O0 @( n: W # add popup$ j. L" o/ B' f; A folium.Marker(& P" I. p1 d3 u! O. Y3 b! S coordinate_changi_airport, - Z3 G' w. E# G; } popup=Changi Airport,' [4 Q5 t# S9 g+ Y* E' p' n- k icon=folium.Icon(color=red, icon=icon_sign)1 w& z9 }$ Q" @4 q% [ ).add_to(city_map)% e, {6 s5 d/ L! W, S; w 8 V( w/ F- f% j # add tooltips and popup # r; ^/ L& @7 O0 d" } tooltip = "Click me!" # A0 g$ s5 v. m3 e% x4 k8 Z. _# y1 K: W folium.Marker( 7 s; U- D. l. U9 @' ` coordinate_nus, 2 N$ e; i9 F, W$ W5 q popup="<i>National University of Singapore</i>",* I% d/ q/ k8 a4 q% { tooltip=tooltip / t% V& x) R& l2 n ).add_to(city_map) " N* N0 i' `& D6 W0 P 3 |0 D- |+ v$ j, G9 ?& r: ~ ^7 Z folium.Marker( 9 u. \- q2 }" f2 |7 ] coordinate_ntu,6 i0 U: ~5 ~2 t! l& G! k popup="<b>Nanyang Technological University</b>",2 A2 x+ f: J4 p8 k4 f+ N/ j2 g tooltip=tooltip 4 B- v: o9 |- e" x+ ~% ?7 M ).add_to(city_map) / ^( z1 r5 c: s3 ~# n; r" i! W6 i' u9 G9 ?4 S- F+ M # display city map % I- y- ?0 P) R; U* h city_map
4 `; t$ ]! @$ K+ E; G9 m# b6 n1 n
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

# k% P3 s0 L& v G" _" X& i* ^
# define the city map 8 V# c: ]. x) @ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)( D- w) L: d6 ?1 R! y% P Y6 ` / S- F! X9 M& L; {# `8 T9 `# D # 在地图中添加经纬度, add latitude and longitude in the map when click - f2 C2 P2 {$ o5 c city_map.add_child(folium.LatLngPopup())6 E0 y# ?, l# J$ X7 s/ n * Z4 C: x: I- k2 Q G' Y city_map
2 n* }; ~. i/ t- K) s1 @ 1 V, d( P+ q5 y; F

几何形状

! @9 n4 q* m/ z( I

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

+ G9 U7 L( U& r4 t/ q- p
# define the city map 9 i9 S9 P& |6 H7 a! C+ s1 o7 t city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)$ A7 x- r, p4 j; G3 A1 z, S! ]8 h 7 d0 S3 e& `8 [1 R `4 k' U/ e6 Q # 在地图中如何添加形状2 X9 p- g% e2 ]. f9 L" Y # 多条边 , g/ }% N" m4 N/ F( g. L points_1 = [ # V% V# d, U3 _$ F6 _ coordinate_ntu, $ Y9 V/ n# x- n& U J4 d7 c coordinate_nus, 6 E; ?: p ]- [# @2 i6 M coordinate_zoo . j" V$ |6 S: F/ a' W, B ]6 |0 ~# N: ~+ I 4 C9 Q f$ o/ s# @5 U% G# X$ ` # 在 city_map 中添加多条边,第一种添加方式 / M$ ^% ^1 T3 A/ `- X city_map.add_child(folium.PolyLine( 3 i) @, _; f: X! Y locations=points_1, # 坐标列表& ~: `) M% R) y: k$ n weight=3, # 线条宽度 2 _' f% O/ N& u$ B: I/ [4 V: ^ color=gray)) - {4 d2 ^" c) N, ]) T3 _$ f8 I3 K! L" z( Z# f- {* ~: B # 在 city_map 中添加多条边,第二种添加方式" F* f/ P0 j6 n Y" ^4 _ folium.PolyLine( 8 ^6 N1 {2 a# e+ d locations=points_1, # 坐标列表. K8 W/ \8 X9 u0 N6 e) r weight=3, # 线条宽度 6 t( e# m2 j9 \& V! `4 q" G color=gray).add_to(city_map)# ~+ I' |4 |0 F 3 e$ C/ X- ^1 K) w # 多边形$ L8 E' A! |. A5 v ] points_2 = [9 Y/ e- S) K& V- v coordinate_orchard_road,7 j' r6 h1 S6 O- N; {* { coordinate_sentosa, . ~" Y' p o, I g* A0 N% [8 p8 | coordinate_changi_airport 6 p1 M9 n2 v2 X% a/ q ] ! _$ q# v+ e# O2 x) x# \& e% X 3 d$ N& U, S3 v# d) P+ f; G6 s" G5 @/ B city_map.add_child(folium.Polygon( : V; e; K) M6 Z' c2 P C. ]( f3 b locations=points_2, # 坐标列表, k" m* c8 v7 o6 [& f weight=3, # 线条宽度 # b3 t F" \" W1 K7 O$ z; C6 j color=yellow)) : T( u2 A- E$ [4 t" N9 M/ ?( }$ ]& x2 \. }2 a5 B9 [. | # 矩形 + }$ D" r% u( t. a$ S ~ bounds = [ $ u& d0 v5 N! T+ J coordinate_ang_mo_kio, 3 d# s0 R: S6 k; | A coordinate_yi_shun & F* z6 K# u. d8 ~ C ] 7 E- }! _9 E, q+ S& o N$ A8 S/ u5 I: M! ?" s( v city_map.add_child(folium.Rectangle($ B9 E) ?. t8 B bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) : t0 F6 [3 C$ W) W$ Y8 G' w weight=2, # 线条宽度 O2 n4 u) i- M color=blue))# Q& s" s7 J' R/ ^: Q' k " \! U0 y/ r9 _1 ~/ O u, F6 |5 D& S # 圆形, circle, radius units meters% U) d% E6 k$ H folium.Circle(8 H7 @: q' N: L8 t2 }5 J1 u radius=1000,$ x" D6 {7 x; \' n# v location=coordinate_nus, + d) l: t" x, Y+ M2 |" | popup="National University of Singapore",9 ?/ t. U$ J6 o2 e7 O0 h color="crimson", $ _" f6 K Z9 a' \/ N# `* j fill=False, - L: A% \$ |$ C ).add_to(city_map), u2 D4 C# A' Y 1 `2 m, t2 o8 Y3 f, ] # 圆形, circle, radius units pixels $ o! {% _! B8 {- B% R& T% P4 F/ w7 A folium.CircleMarker(9 H7 N, l, p6 T; u; u9 J. a location=coordinate_ntu, + _# B+ m. C# J( ~ radius=30,' [ V1 h1 }0 p% Z7 {& V8 _( }, I. U9 @+ _ popup="Nanyang Technological University", 7 p( Q) y1 B% Z. {. E- c g color="#3186cc",& ^- O% c/ L/ i& t: e! [ fill=True, " L' ^' q1 D+ N$ U/ ]% p fill_opacity=0.3, # 透明度 % f' p5 S- x/ v, ~0 |3 O0 k. T fill_color="#3186cc",$ j/ F4 X; @6 H ).add_to(city_map) : T/ m: X/ V; z5 K4 K : p ^9 S( M5 e( R1 x3 K$ ^, R% \ city_map
) W) }# r W, \
Folium 中的画出各种形状

热力图

0 _) l i; a2 n3 U/ p& l

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

8 `8 n0 a* N% C
import numpy as np4 Z N8 Y- T! c7 i7 Q! ~ from folium.plugins import HeatMap# Y, Y$ D* k- d 6 \1 x* J; T$ x6 j # define the city map; ~. `& Q( r8 h E& N" X* P7 C city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11). i) b6 @' `* v. X/ c ' q+ c; K7 H# F% a# N. U) c # 构建随机数据6 R2 z# P' k( J& h, C, [$ }( m# M data = ( 3 j) \+ I1 k2 z5 \ np.random.normal( |3 A: ~* {8 Q/ _. Y# Q( k6 ]+ i size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +6 P4 b6 b' Q. V5 S np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) j) S& R3 E/ B) m& Y ).tolist()/ s7 K' A# \4 G& F ' k; p' ?/ p! v M2 m city_map.add_child(HeatMap(data=data))6 _# a# I! v2 W1 _ E city_map
& E- e0 ?" `. k5 o- D0 w
! o5 I" I2 t4 e! \# B. z8 Z W

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

* @5 _5 J! C. `7 H) u
import numpy as np. u. u# ]1 b- s; L; \/ q from folium.plugins import HeatMapWithTime : ]: ?8 x, h q1 H' c6 G" a- C' S6 q # define the city map4 \) h4 z& c% W0 j city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) - E/ D. ?/ s( \ l; x0 n6 S7 Y, c G- ^ # 使用 numpy 建立初始数据 % ]. n; [) D H& \' [$ ~6 t initial_data = (np.random.normal(size=(200, 2)) * ; r J+ Z' e+ Y& }. j np.array([[0.02, 0.02]]) + ( A8 B2 P2 b; A# J1 v& R np.array([coordinate_orchard_road])) : r$ a6 |* j+ @& w% ]6 K0 L- b% r h* u # 建立连续的数据$ R" F% e4 a5 `# f data = [initial_data.tolist()] 3 f+ m2 k; p0 q& e for i in range(20):1 o8 {) }& z1 t data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()) / D2 i ~ o: @" Q: G* p/ U& ?7 ~: t/ y5 P; h9 c # 显示连续的热力图# Y: h$ P4 u; m9 t4 ^( A) Y6 T/ W city_map.add_child(HeatMapWithTime(data)), r. f; b: U% o6 T, g) q city_map
( F3 c( F: w$ b$ T& y, K + g" ~( ~, o% @/ i

经纬度点的聚类

0 Z6 h3 g4 L* ]% Q4 n2 T! Y( U

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

6 C5 G9 l( {) j
from folium.plugins import MarkerCluster ! ?: N/ b9 K0 I! H0 l. L' o# p2 h2 F7 v7 a, j3 a # define the city map % u* h, `! z/ {, }% _ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)- `/ r4 |) ~7 j4 J5 m) { ' u+ _, C+ i" o* O7 |3 v: l # 经纬度的聚类 " x, t2 W5 ~) [, w+ m/ D" R # 在 NUS 的经纬度附近随机生成 100 个点;8 h# O8 m2 {% t+ E data = ( - i- c7 L8 a- T0 u9 i3 z5 } np.random.normal(size=(100, 2))7 r& d) L o! Z) _! O * np.array([[0.001, 0.001]]) +3 U# h* t. K: ]) U. j) v# P8 V H np.array([coordinate_nus])) " I2 a7 \: U) o0 Y5 @6 g0 i: ? I& Z' q' X6 Y7 T # create a mark cluster object, L7 x' t) p" J5 h" i+ i marker_cluster = MarkerCluster().add_to(city_map)5 f4 V3 ]+ } i6 b ! n8 L G3 r5 K; T8 A+ p. R( Q # 将这些经纬度数据加入聚类 2 \; m! I+ e* K$ I3 V0 H& m. M: M for element in data: 9 t7 p( @/ H: K3 [8 M! W! ~ folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)% |8 _ [7 d- `9 \- I) j1 A 8 e2 t. k: _. h3 c4 l # add marker_cluster to map * H3 E) o! Q+ F2 m city_map.add_child(marker_cluster) t9 e+ _5 O, i; w7 O( N3 G, R/ L; e 9 l% Q8 K# Y. K) } # 作图 5 Q9 D8 _! v }2 Z9 I3 i. D" d& s city_map
! i" R7 A5 P* X g" y # P. o' H- |1 M0 x) d. G/ e

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

: G" A0 X# K! D/ `1 ?3 j

参考文献

. [$ Q1 Q6 l4 s3 n7 r0 L5 Q

Folium 官方文档:Folium - Folium 0.12.1 documentation

; F; Z3 i+ v# ?' O; c8 h # A% R0 b) ?, s9 p( W- q' W! Q( `$ `4 R5 j$ } 4 K/ y! `0 k0 E3 ~; i( N) E c. n0 N9 H& c0 {
回复

举报 使用道具

相关帖子

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