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

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

[复制链接]
: u" k) c k0 y+ P

Folium 简介

+ h$ c: b/ \3 \

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

9 c- e# D7 A+ l* U7 d1 C5 h' ]

创建地图

3 f" @) E9 W8 n5 c, y; u

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

0 R, w& f% g5 c, y3 l- Y2 \
import folium4 v2 k% C; {+ k# a %matplotlib inline/ M4 X A- ?9 d8 X/ \1 e5 J 8 f. D! F/ ?* S% M$ k/ G import webbrowser V' I7 ~) ^) x; s& H7 r , U1 j2 |9 O. N+ L; z4 _ print(folium.__version__)! w, q9 I1 ^0 f ; [& i" @1 Q8 ~5 ~0 l # define the world map " t# w4 H% F# @* [; w world_map = folium.Map() & i' s" ^' h+ G) G # display world map 0 x3 J& Q1 A/ E' r- u8 C world_map* g1 X. C1 b4 d h! i% i& Q
/ K7 h3 ~& r) c3 q
世界地图

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

, Z* e @ F) P! V" u4 s

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

% U9 n5 ^. L3 U7 |& [

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

" x$ a& a) ?% w9 v

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

, Q0 B9 h5 C6 h
# latitude and longitude in Singapore city ! U) f7 k5 x! x* j- r) V coordinate_sentosa = [1.248946, 103.834306]5 Z% h9 c. f) z$ k; ? coordinate_orchard_road = [1.304247, 103.833264] ' ~0 e5 J/ U% ^5 R2 W9 g" t* h& W' t4 T coordinate_changi_airport = [1.357557, 103.98847]8 y% o* z9 w/ I3 q( r# x coordinate_nus = [1.296202,103.776899]4 {. S! R( V3 O. D coordinate_ntu = [1.34841, 103.682933]- e) g P$ V5 T8 e& O coordinate_zoo = [1.403717, 103.793974] 7 w. @$ u4 d. t3 g1 e' S% C coordinate_ang_mo_kio = [1.37008, 103.849523]6 m* k7 L- q3 f$ n5 ` coordinate_yi_shun = [1.429384, 103.835028]5 l9 M8 Z2 k3 Q& ?4 L * X# U1 ~+ a: M, `3 ~( l$ E+ j7 P2 f. S # icon; W6 ^! ^" t/ x7 s) w, Q icon_cloud = "cloud" , F& p6 i4 P T6 l$ W- A* g& |; m icon_sign = "info-sign" 6 ~ o2 x6 s Y" \% F# M/ W 8 w8 \" U2 u. v# \7 E # define the city map 0 A- y' L" R; Z$ v( c/ M z # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}2 }2 s! T2 r# y city_map = folium.Map(. |4 z$ m$ {9 W9 q" D B location=coordinate_orchard_road,6 K6 w. ^. Z% c: ?+ ^: r k zoom_start=11,) [, C3 v; D: H& T9 H$ n tiles=OpenStreetMap)# U% t' u: p) @/ i; T : U2 v! \ _1 e # add marker in the city map : M7 H+ I/ [7 [6 _( `. {5 T, ^ folium.Marker(. ^9 J( C2 q% ^& [ ?# K coordinate_sentosa,- ]( S7 M" P" N2 k$ ~5 Y( ~ icon=folium.Icon(color=blue)7 _3 C1 N0 d- @2 ^! w2 b6 z ).add_to(city_map), b0 ?+ [$ s4 q5 [9 l" d folium.Marker( 8 O" ]1 Q* J, K. l' s' I' k1 _ coordinate_orchard_road,5 x2 O& z* h1 \4 p+ I5 C icon=folium.Icon(color=green, icon=icon_cloud)* c P0 s5 N( G4 {" a) I ).add_to(city_map)$ y8 ]5 B$ s- ?! H 5 R6 s6 @- {4 S T0 n8 C # add popup 7 P0 ?. R3 {/ G# K# E) P folium.Marker(- d. `( S9 B, v/ Q# E coordinate_changi_airport, ) F' _ q- [# X# V6 b; B9 i popup=Changi Airport, ! B0 J b6 b8 p1 N u! R% H; d/ B. H icon=folium.Icon(color=red, icon=icon_sign)) b3 _, I+ i* E" @ ).add_to(city_map)2 L0 `( g0 V( _5 l7 P4 o) [6 ^ * n u6 J" [' z! Z# | # add tooltips and popup 4 Z- z. s6 @" I, E+ r6 @4 u tooltip = "Click me!" 6 x( Z5 q/ Q- `1 ` - d7 f% Q% ^1 p$ s t2 D0 }. t folium.Marker( 8 m" ~& p7 T3 M4 T& j% i coordinate_nus,% D; y/ a4 G: q popup="<i>National University of Singapore</i>", % u; w% ]$ |8 J3 t7 u tooltip=tooltip & O5 d5 }. }! D r ).add_to(city_map) ( V- j1 U$ X1 l $ w7 ^ J, o& u9 { folium.Marker( 3 z/ T5 x A0 V6 r N0 J- e9 S coordinate_ntu, ) G# B' Y+ d8 ]# {- e9 G- R/ A" c popup="<b>Nanyang Technological University</b>",7 J4 J' x- b2 L) _0 q; P* k tooltip=tooltip3 d; P2 |) u8 N8 `: q3 h. Q ).add_to(city_map)% t) o l7 g5 z6 J! ] 8 r# o2 f& f3 Q2 U( x # display city map# T9 A1 ]' m' h* A city_map
8 ?+ d% a+ [" ?1 t9 t k1 y
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

4 U1 W% k, S$ _/ o! Y A
# define the city map+ i- J2 }2 w2 w- `( F city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) , \0 G, J, C2 Q8 o/ r- d3 d7 J4 O0 c' \ }; V4 d& T# q # 在地图中添加经纬度, add latitude and longitude in the map when click2 G [6 i, q7 ]. M9 [5 @; A city_map.add_child(folium.LatLngPopup())& a# o+ ~) t' Y 9 Z# B8 E5 u: z2 M city_map
+ Q8 x9 [. U5 r$ j8 n; V ' `2 ~, f/ T. s4 {+ |. ~5 d1 I$ l! R

几何形状

* t3 _/ S' x% ]! z5 l+ g

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

% `" q, O# k. { |" {5 w
# define the city map , D$ ^0 f- W# b7 o! L+ j city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) c( u7 q& T X9 H7 K! C* H0 _ 7 A* x$ m9 f2 w7 y& m Q* e # 在地图中如何添加形状4 ^# E& a3 h0 D& M6 ^ G' v5 E* F # 多条边 # k; {! p- Z5 s3 ^' L2 a points_1 = [/ |; G! q# X- \; H+ ] coordinate_ntu,& @2 `: q# ~+ y" S# M% ^' f coordinate_nus,% t; Y5 l0 ?1 _; Z coordinate_zoo & {8 d- R# A* z/ l ]7 @$ ]3 w; o) L1 h/ E% Y # Z" F/ g. Y! d& G # 在 city_map 中添加多条边,第一种添加方式8 ^/ v( n& Z5 X% w& H9 ? city_map.add_child(folium.PolyLine(: `& H" G) x! B6 p3 X* H, k- x7 C locations=points_1, # 坐标列表! J( m" `) b1 I! m1 ]' x3 ^" k& ^$ O weight=3, # 线条宽度 " G. G; ]% M; N# W+ z# d color=gray))' `5 D3 L7 r k8 [3 l* d; f ' j! A8 d$ {% p. v7 C # 在 city_map 中添加多条边,第二种添加方式 % {& D5 L6 n' ^# l/ O( m folium.PolyLine(' U/ X' m% w7 m0 C locations=points_1, # 坐标列表7 W+ ~4 }# J/ |4 Q weight=3, # 线条宽度) G9 t' {$ F- O4 r9 C$ i. t7 _1 E color=gray).add_to(city_map) ; o( [ l( o& L$ e" m1 Z5 C 6 j) v; \5 ?- J # 多边形5 Q/ t) j( N' j, @/ t. i points_2 = [* \3 f4 G7 r8 t L% ]/ E2 O" D9 \: P coordinate_orchard_road, 0 x3 y9 L6 G& H4 F coordinate_sentosa,1 n! m! o, \5 j5 O coordinate_changi_airport( W1 K9 {, r; X" E" [6 a' q2 l& I ]' r: t4 u1 u$ d8 ~% c3 m . t5 l, Y' v4 J {, a/ _" A city_map.add_child(folium.Polygon( # B& O: q" F: L+ D; l( S/ Z locations=points_2, # 坐标列表3 E3 q2 ~* h4 m2 [& r2 p' @ weight=3, # 线条宽度 % K- Y% B1 G1 C: k* V2 z. u color=yellow)) , y) U- |; r- ~" |4 ~2 C% q ' ]/ o: D! n& l- Y # 矩形8 j- ]; }! F2 j bounds = [ - E+ z7 a+ j8 x# l% p- m coordinate_ang_mo_kio, ( z% j- C( F& ]# E' v4 i coordinate_yi_shun ( u+ f3 v8 l. k2 B) _1 I2 f ] " N2 s2 u; O2 _1 Z1 i. V v1 R' t9 `0 h4 r/ l4 p# S. V% J1 n city_map.add_child(folium.Rectangle( ! d" a. }' ]! {, n1 h6 l bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) 9 ^3 n6 c, n; H0 m weight=2, # 线条宽度6 \( @7 @8 _( h2 }# \6 C$ Z color=blue)) " `# t5 O) ~8 M% @( A* f* e" y3 \* N& ~2 I0 S # 圆形, circle, radius units meters % {) Q! [% _% J; R4 f; g, F8 H folium.Circle(5 w0 c5 X1 g& _3 Y radius=1000,8 D1 M' `" e. p7 m% k; f location=coordinate_nus,# T2 g+ U& X# y K& A7 d popup="National University of Singapore", 4 S9 v! k9 b/ C# D$ Q color="crimson", 5 {# E( R& t# U3 F1 i5 d- x fill=False, % P& J2 f; Z7 P: |% k4 N2 C8 r7 W/ a ).add_to(city_map) 2 j$ _. Q& o1 t ' v7 `# N4 }' S6 g1 n6 { # 圆形, circle, radius units pixels4 S2 _ C! v, X& b* ^, o3 S9 H8 E folium.CircleMarker( 7 K W8 U/ {3 |9 c( p& ~' R location=coordinate_ntu,' O% q4 Z* W) f. z c8 Q' j radius=30,. y( s" _6 @8 t8 ^/ d popup="Nanyang Technological University",- J1 b8 p J! X8 h color="#3186cc",# z& r8 O/ y# {5 ^( A% ` fill=True, ' j D& u; c& C* z! p3 l- p' D fill_opacity=0.3, # 透明度 - m5 o* t/ [# D) m2 ? fill_color="#3186cc",9 o# f, b, k( j/ F- R8 y- V% [8 y- U+ } ).add_to(city_map) 6 ^4 @* f3 R7 z# A' O- ?- M4 Q3 p/ s% k ~2 c) j city_map
' F: b0 b/ F$ ^3 G6 V( J
Folium 中的画出各种形状

热力图

' P s- [- @3 ?

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

6 I' z, ]# K3 V0 l* B4 e
import numpy as np , {; O" X( _9 J. C r1 S from folium.plugins import HeatMap4 Y! m- `* V* g, i . Y; I o g8 J7 n6 ` # define the city map R$ S0 o J, q6 {1 o, P city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) & ]: o1 i3 Y: {) [* m& ^* j) r+ P4 ~1 i! P) p9 u( [/ t& N # 构建随机数据& t+ A; \5 H; W! P4 @' _$ ~! ~2 s8 q data = (9 u$ e, R5 f' r% T np.random.normal( * y; s5 c6 Y* |; j4 U+ q size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + 0 L* y/ }6 U2 H- {- Z9 ^ np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) # {: c% B5 M7 v4 b- G. } ).tolist() ! P5 z: v* H% l7 v# Y. _$ k& | 8 t# e: a6 P B7 _& h- h# A city_map.add_child(HeatMap(data=data))0 f% J5 M: T; U: Z city_map
7 v, b1 X: h* m$ i3 j$ S# c
4 Z8 y- k8 R% v8 o

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

5 J0 X# P0 S3 Z
import numpy as np % N6 B" }" p8 E% t" C+ C* E1 p from folium.plugins import HeatMapWithTime 7 [ L& B. x9 e# c5 V6 y: t 4 k- r3 O _9 P. L; S' `( \; X # define the city map" z4 X! }, Y) R5 k/ _( y city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) - u- j+ g- Z* [$ Q! x( \- c+ Y0 ?2 R& p3 \& X) }9 x6 c) H+ [" I # 使用 numpy 建立初始数据 : L) w/ Y: ]; v- }1 ] initial_data = (np.random.normal(size=(200, 2)) * d" V* A$ b; B2 U) [; r np.array([[0.02, 0.02]]) + * Z) Y6 R: r9 s( [8 h( o np.array([coordinate_orchard_road])) ]: w$ G+ g% l r8 f # w6 ]7 H$ y8 ]5 X' u # 建立连续的数据# }; L- w. S3 }2 l- Z3 j* N i$ S data = [initial_data.tolist()]$ P" W `, N4 p- ^* I; V for i in range(20): : L8 T! n) j/ D: \5 m data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())- O* [5 g% Y3 r1 Y9 E , Z, _8 v) E H( i # 显示连续的热力图3 y( E6 S w1 }+ H$ y1 ?: S5 h1 n city_map.add_child(HeatMapWithTime(data)) H* I3 L) H* i3 n city_map
5 K7 X/ q7 e& Y4 {. ?! E2 f : m+ h) e* E, m) t( r8 Y

经纬度点的聚类

& u' b- b1 u2 D t$ x, q( |

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

% P1 l1 b# V: p" I
from folium.plugins import MarkerCluster 1 Z1 g; `% k$ S: E: J& L. t7 x& u. J: U+ a/ d7 I' Z) m& {8 H% P # define the city map ; R/ t* \3 ]- G) e city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) / S$ D6 {4 @, G! j % [6 w* c7 C" r5 ~$ b8 S # 经纬度的聚类 ; T' ~/ G( [% w, d/ O; } # 在 NUS 的经纬度附近随机生成 100 个点;' R8 V: W9 @3 `( O+ L data = (5 ^6 E: f: T8 `# x np.random.normal(size=(100, 2)), n! Z$ Q/ N8 w8 B4 B. R: m * np.array([[0.001, 0.001]]) + - x" P9 d0 u M8 c6 C: L# C- j7 [ np.array([coordinate_nus])) ; E" I3 `( r" {* H1 ^) H) E- C& ~9 x9 H ) v, k" Z! W7 x, E0 g # create a mark cluster object6 h: b, H Z5 }9 p5 z marker_cluster = MarkerCluster().add_to(city_map) / g3 \/ J1 T# j : h" @6 |5 i9 u; b, {7 a; c # 将这些经纬度数据加入聚类, U# @, c7 C" `; U' \5 G for element in data: 2 s( Y7 m9 F* K2 l9 T5 \9 z folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) 5 q: N( h4 q% }1 _! H 8 v: |. u' s+ L # add marker_cluster to map ) g- t s- `+ I city_map.add_child(marker_cluster) P) r3 L/ y; X; c, b+ Q2 F. K4 b3 j# x: q # 作图 ) y9 S. a. c9 E8 O( V% k city_map
" S+ e& E9 t9 @5 ]/ W/ d8 J" R) g4 l 9 `2 H( G* w! u/ L1 O" w5 ~9 P, _

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

( i- F3 E3 ]$ g9 {9 I

参考文献

, H; \$ o8 l" T

Folium 官方文档:Folium - Folium 0.12.1 documentation

8 U% t6 J$ b/ W% { ! [0 `/ ~# c! ^; V9 ^ # }& X/ X/ n# ?/ X5 ?. f. I' ?. N1 T' z7 F# z# i : W- k# E6 `3 V) I; X& q0 ?2 U
回复

举报 使用道具

相关帖子

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