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

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

[复制链接]
1 {; _0 g& _, @! ^6 O7 e

Folium 简介

7 b5 X! w. K: C) x

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

" n' C/ c& a4 h1 F. ?

创建地图

+ n0 p9 C5 ~" S3 D+ i% G5 n

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

. Z5 Y" Z1 M$ K& \. j* X* r+ L% A
import folium P' Y9 n: ]! b %matplotlib inline # E) |, K& g7 l+ N8 [ & g& O3 V' q2 z import webbrowser* M: y. C0 Z# A ! w l* e: u- K( q x; b1 q print(folium.__version__) + q }) C+ g7 c) a& Z& I# _6 p 8 c- V0 ^9 f6 T4 ^4 W # define the world map ! F9 p* f* l* x world_map = folium.Map() & a2 o& B3 b6 t/ \" l! P/ \ # display world map- j* V6 R) i5 J, L$ F; u world_map' D2 m q, ^/ M$ t* Z+ @# l+ e
% T6 w4 y$ B5 \
世界地图

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

7 B, \, M6 }- {8 x' n

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

. }) a g7 A: ?( j i- m

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

% w4 | B6 X& U) d: T' |

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

' n/ X% G- D, ]; G! V' M( d& a
# latitude and longitude in Singapore city 7 I1 u1 f( N0 [; B& E$ k# x. V coordinate_sentosa = [1.248946, 103.834306]5 o- A7 [3 F2 ^2 ]/ S+ j coordinate_orchard_road = [1.304247, 103.833264]# e0 [& i3 \) t9 X0 }3 v coordinate_changi_airport = [1.357557, 103.98847]7 {, F4 S# p, G* n# v coordinate_nus = [1.296202,103.776899]1 }1 h5 f ]/ S' N* q% a coordinate_ntu = [1.34841, 103.682933] . v4 Q! D4 J! I) x, [ coordinate_zoo = [1.403717, 103.793974]1 z# e* Q1 H# e8 R. W coordinate_ang_mo_kio = [1.37008, 103.849523]9 p1 G* T4 u7 W* l! C; N coordinate_yi_shun = [1.429384, 103.835028]0 w% r( a% ~* m" z- r. }* L8 g 7 V0 s0 n2 X, R$ Z- l3 n # icon $ s, E5 [: E! A% R) s6 m icon_cloud = "cloud" : a+ Y; e# H) K/ p% a icon_sign = "info-sign"9 O& x1 e3 Q( O* g $ J7 Q: d3 c, b3 @9 d3 P8 S # define the city map V( E( L5 {) ?4 M5 C$ K5 {6 @, F! C # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}& L3 L# _+ S/ ]- y' F- h city_map = folium.Map( 7 z4 }. `* u5 A1 `! K location=coordinate_orchard_road, * l& W/ D Z$ ]; O8 K5 r zoom_start=11,, x+ T5 A" R! C" G tiles=OpenStreetMap)6 E& F1 |' Y5 m) P/ J% h! ` * c% u6 ^2 Y1 T) i" h" d # add marker in the city map9 _2 f7 q( K W+ i folium.Marker(+ d' l0 U! v. H i& {5 D8 x& I, n coordinate_sentosa,( G s0 E0 T7 z+ E, G4 e icon=folium.Icon(color=blue) , |" I1 v- }5 }4 ~: w ).add_to(city_map), c& V1 z( W$ x3 o, ?2 m folium.Marker(5 ~5 h0 j8 Z- q& ?8 U' f coordinate_orchard_road,+ p; b& A: u& i. V; W2 P icon=folium.Icon(color=green, icon=icon_cloud) z; f, u! o8 K( H4 r+ d2 Y ).add_to(city_map) ) W; h- i7 Z0 {6 U' ?! G" n+ F4 T# H! [& g3 f5 g # add popup 0 w# }* g1 K2 N4 F) I folium.Marker(% t8 P7 T; [, w& n: ` coordinate_changi_airport, : A; f1 z) L0 S" T2 ] popup=Changi Airport,' \+ B1 ^7 j' ?2 T' ]: Z2 H icon=folium.Icon(color=red, icon=icon_sign) 2 {3 k$ A' m/ |* C7 ^6 o3 l ).add_to(city_map) + n2 ^( j6 D; M# s " |) j8 i$ K2 y; h; I. L # add tooltips and popup 8 v$ T! a- {& R) b2 A$ O tooltip = "Click me!"9 a0 E- B6 H7 h- g% ?; U# P; o & Z9 C# m+ m I: x3 F5 _ folium.Marker( / h6 ?: I+ N7 z6 Z/ X+ A5 ^ coordinate_nus,* p' ^9 m. C2 E: q4 T popup="<i>National University of Singapore</i>",6 w$ r4 m: s& f: R1 V) P" b4 ?+ D* r tooltip=tooltip9 e( Y6 s9 p1 `( H2 X5 y0 J& n. b' [ ).add_to(city_map)! a8 F C4 G; R* \( P6 U; { ; _# k- Z( X) Z5 | folium.Marker( G5 ^9 p2 U$ Y# u8 q coordinate_ntu,2 h7 \+ j5 U ?* s9 P: P; I) s popup="<b>Nanyang Technological University</b>",0 P0 ^$ } G8 H2 i tooltip=tooltip# O6 [4 | j+ Y7 D+ q ).add_to(city_map) 2 u2 f' W0 R; C8 y1 d& b. m, c, ? # display city map# _- [& j- r( Y city_map
; Z7 _% ~% s6 H0 ~$ C! A
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

* c2 a0 S1 J/ x
# define the city map; B( I$ r7 U: ~9 z5 B city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) % E6 n# U' J3 n+ ~9 |7 R+ j4 n. w: X # 在地图中添加经纬度, add latitude and longitude in the map when click ' W& O) {: l7 o& G0 [9 R, ] city_map.add_child(folium.LatLngPopup()) " _: A, k; E( j& y& r% m, c% C$ `$ A5 r6 P8 S6 G8 V city_map
: l' O* @: _* y3 g Y 0 L7 B& X: t7 J1 n

几何形状

- f) K/ M4 I2 c4 \* {- n! u, ]

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

6 F* ^ H% R+ c* X7 B
# define the city map 1 ~' k; H/ o/ o9 W city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)' z2 S* r9 v4 F) t7 i, y 6 H; E. R( @0 d1 e ` # 在地图中如何添加形状 1 J' ]# Y m" v' i0 ? # 多条边; k- b# ~7 \4 R" i3 S# v points_1 = [ $ M1 Y# \0 j: v. P# ]; v" Q coordinate_ntu,4 U2 z& U5 @+ L1 h' S coordinate_nus, + W$ R/ S' P$ \9 ]; n G" L! G coordinate_zoo ( N# L o$ }2 r7 ? ] ) C' g6 \7 ~4 \, W9 [. d+ }8 r' X( O9 T w! Y$ k2 s' P # 在 city_map 中添加多条边,第一种添加方式* P+ G. ~! d* u city_map.add_child(folium.PolyLine( 5 Z& z2 S% N; e0 f$ N locations=points_1, # 坐标列表 % m8 J8 ?3 Z" S+ B weight=3, # 线条宽度" x- O4 r1 I! f. G& ] f& R color=gray)) 8 m) k3 ] n' Z2 g8 p" Q6 I3 G- S' \) _ r; Q+ V # 在 city_map 中添加多条边,第二种添加方式 * ^+ `; m$ ?* ] |+ e folium.PolyLine( + T. u! M) ]* W! [ locations=points_1, # 坐标列表 & b9 P" b* c% o. [ X+ m" s weight=3, # 线条宽度" V9 S6 ~. N( D+ n0 G, t. W9 N color=gray).add_to(city_map)- n- y" \+ B% M$ c' h, J# G! P+ F # M5 ]( O s6 M- \5 w' A/ \% i7 w2 @ # 多边形: f2 d3 t# P3 V6 g* i0 I; j! ] points_2 = [5 F k I, q( I( W coordinate_orchard_road,& `/ ?- U. [+ u coordinate_sentosa,- I/ o, n# U5 ?" H8 R) ^. q: u coordinate_changi_airport6 e# Q! H) e3 W, B5 f# o% H" ? ] {, [! A- A# z# u: ?8 c. O; o( I# j5 E2 k# ? city_map.add_child(folium.Polygon(- H; o5 C3 D+ @- I) J8 y& W' ?9 ~# _ locations=points_2, # 坐标列表- D! I2 G0 j, `2 D) @ weight=3, # 线条宽度 6 d6 |+ }1 x- E color=yellow)); ~# o) Q2 c# Z& B" p+ [ ' r% N' M( ~* z( I7 j # 矩形# ]) R: c* A$ Q* z6 \7 j* Z& ]; ] bounds = [; f! V: n& Q. ^5 f, j9 Z2 J1 ?! A0 S coordinate_ang_mo_kio,6 c# m6 C9 j1 n! k coordinate_yi_shun5 J- u9 ]( S) Y; s ] 4 h5 y" V% J/ K) f * V' z# {) _9 b# s9 Q" G. p city_map.add_child(folium.Rectangle( ]& n& c+ _7 [. q6 Q P6 H$ Z3 P bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)' Z! S3 G; J! o4 g: z6 a* m weight=2, # 线条宽度% c6 j5 [9 x; c" J9 @, j. J color=blue)). ^$ G% B" Y- V# ^ ; L# G7 [4 i4 P) |1 E, L5 M) ~9 E/ A # 圆形, circle, radius units meters# I9 c. d8 Q* b7 g folium.Circle(' m4 ?. K- }$ r+ X! a0 U+ E radius=1000,3 B! G7 F, b6 h4 X* F( }. O) r location=coordinate_nus, 6 P* Z* b# K' d; O popup="National University of Singapore", 9 `* D; V. ], p, X+ k) L color="crimson",4 d z+ {5 o) r' s; X, n9 T fill=False,9 q! f( I% Y/ a$ a. i6 } z0 ?+ k ).add_to(city_map) , {- p# a/ X% \% ~- l0 L9 ^/ {* {- }5 h* X1 E! A3 O# p5 @ # 圆形, circle, radius units pixels! y; V& W' f, b1 y; V# ^ folium.CircleMarker( " Q: w- o% s# _ location=coordinate_ntu,- ~7 d ~5 Q/ y8 d% f* A3 U radius=30,3 _" y5 _) z: N" `& q9 E; W popup="Nanyang Technological University",5 T* `$ B4 m7 W5 h% }. V+ \6 P color="#3186cc",5 L3 B8 q! P F; M fill=True, 1 V. j2 B/ k8 K1 N. S% R fill_opacity=0.3, # 透明度/ b; j: r _$ l5 z5 l4 g fill_color="#3186cc", : Y. X$ W3 p+ R" i, f3 H% K ).add_to(city_map) : c/ N+ ~. x; v+ t5 b7 m/ \$ T4 O* y7 o( g. d3 w# \/ _ city_map
) W. W, W/ M0 `8 S" ?, g6 u1 Z; i
Folium 中的画出各种形状

热力图

6 X1 {7 Z( y; m1 o5 C" ~

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

6 Z2 ^0 {& Y3 t$ m6 P( _: r; ^* Q
import numpy as np , a+ s1 X% L) E& m# ^" M from folium.plugins import HeatMap% S6 K+ ]( k3 x% n( j+ I2 h, N! y * S2 L' ^: i i4 f$ } # define the city map; j( a2 `( _- ?2 Q. } city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) M7 \: z* z b4 E9 d & d' G+ r6 y8 _" \ # 构建随机数据 9 s4 G9 u9 K- d$ J% ]# u data = (. x+ N* w( q: h1 s- [ np.random.normal( & C' i' K& A" F1 u6 l. C( y size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + - K3 c' n P9 q4 t+ w3 F, l* J np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])+ D) c& t0 j/ K: Y+ Y! k8 r ).tolist() - q$ f+ {; E2 w* p" g$ @4 i4 a+ d9 C) H: z1 T: } city_map.add_child(HeatMap(data=data))' m7 w5 y$ ?) u$ \' P city_map
( C4 n9 @4 q- Z5 h( s1 @* D4 S
2 d" Y% { }- u+ b+ \2 ^& V" @; d

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

) z: ?7 g# W3 `4 i( t1 M
import numpy as np - E3 e8 T8 Z7 R from folium.plugins import HeatMapWithTime 7 z/ ~. `; K9 L: ?4 K9 P* J4 w, l, d1 s # define the city map + m ~7 ~5 ?( z* x# |& V( T9 l city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) ( Q* ^! {* j8 T/ `0 \3 G% M# D9 f # 使用 numpy 建立初始数据- I' {2 u( T9 Q- Q3 g initial_data = (np.random.normal(size=(200, 2)) *: I2 b/ Q' f. y; p np.array([[0.02, 0.02]]) +% P+ {- _9 U, x# V; d np.array([coordinate_orchard_road])) % A8 S- v, l6 P" @+ Q5 g0 s- Q0 x8 x/ }' _7 [. ]! S6 U: V # 建立连续的数据 " z, J) F% m3 I data = [initial_data.tolist()]2 S! A) A) E O1 O4 {) P, k4 R0 A for i in range(20):1 h" M; L/ E; R" j8 \! A data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())+ S% ?! ^, I: v1 a 0 R$ E7 P' b! M$ z0 _ # 显示连续的热力图 - r: n( C' {! L9 r5 _3 S p city_map.add_child(HeatMapWithTime(data))# r/ P- F, P& n2 h5 F! m+ c! Q city_map
1 w4 k9 X4 K% W9 L+ I5 y ) Y; V& @5 K/ d# `

经纬度点的聚类

$ {7 P4 M/ p; g# u$ n& b

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

: T2 l+ [) A! U
from folium.plugins import MarkerCluster, r: P e0 c& J2 x$ _ $ u5 d9 }: F& t9 D( ]# [$ r # define the city map) r; ~; f' Q8 z0 X" h8 ?2 P3 p city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 1 S. ~: R% X- Z. m+ T" }) Q $ N+ L. n& A/ Q& U- ? # 经纬度的聚类# a+ _ S2 p+ s # 在 NUS 的经纬度附近随机生成 100 个点;% _; H3 V" M) M8 I ?% O data = (8 L, e% N! u+ @* ^ np.random.normal(size=(100, 2)) . m! e! P) |# b/ Q * np.array([[0.001, 0.001]]) + ) H% G9 T' k9 U, b/ k/ D# W np.array([coordinate_nus]))) q; i+ p2 g" N% y' ~' p( w % [$ I; I2 H/ t# I8 l1 x# m # create a mark cluster object/ d$ _; f% b# s, j6 U: g. ^/ T6 { marker_cluster = MarkerCluster().add_to(city_map)7 w5 `+ Z9 F0 e3 r0 T6 ^& k X3 w" b9 I& V" ?# f% v# b$ L # 将这些经纬度数据加入聚类 ) s+ M5 I3 ]! |& I for element in data: " G) H0 f3 [ ~# e% P4 A folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) , {, O* f q; g1 X4 i ) c; z* u( s1 ~4 p # add marker_cluster to map 8 n4 x5 F- u( c; H6 J: P2 E; w city_map.add_child(marker_cluster)! c: C" T/ t7 n8 r8 { } ' k* j- F% S( Q; \3 Q. K0 t # 作图2 o( E2 e. l8 Z3 q+ W# D2 }; D city_map
3 Y( ?. p. e7 ?+ m6 | 9 ~5 |% p' h' Z- q9 s: c1 C

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

- @& l% X( \" E* Y6 K

参考文献

0 h: p8 a6 c5 L i6 W7 }0 P

Folium 官方文档:Folium - Folium 0.12.1 documentation

$ i) q7 P* {) Z& N* M% r+ C$ U r' r8 L3 @# ?$ W* Z U4 b/ G1 M! b3 B & F0 r( g0 a0 B: q* p4 u 8 L5 e1 _3 M, h! p$ h
回复

举报 使用道具

相关帖子

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