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

[复制链接]
# x- T( _- U& Y) X- d/ |

Folium 简介

4 c) Z# k6 k3 M' B

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

# U% f" f; C% R3 D

创建地图

( d ?/ A' ?/ f N: E

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

2 N8 o6 f5 N( Y! s: q
import folium+ ?7 G. }* s8 _$ k) t) U) b% A %matplotlib inline ! _9 z6 l7 z3 H9 N8 z( A; a% } 0 P- P% T' |, l3 |; C& o' E import webbrowser # H- P% Y. P8 y+ ^3 W+ d; i/ H, v; c' \1 m print(folium.__version__) 7 X* d$ H: c- ]- o1 K* v 4 Q9 {+ g6 C6 e4 l # define the world map1 Y5 ?/ T3 N+ Z( p+ }: A% O' p5 i world_map = folium.Map() 7 x: L+ j) J5 W; V. y2 `4 M2 h # display world map 0 v2 o& p: p7 z5 w x world_map : H/ }# K ~( Q$ k' @, ?
5 m% v0 }3 i# z) W7 ?1 j
世界地图

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

) T* M/ O! [9 l) F: J$ D9 G

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

1 i) D7 r" w8 O/ d& z0 s2 ~

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

) p: h. e" o8 i# ?! J

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

/ t' Z) ^) E5 L9 D( u
# latitude and longitude in Singapore city 4 h3 g, Z7 p4 l9 k: j1 | coordinate_sentosa = [1.248946, 103.834306] 6 H0 P7 g6 V L coordinate_orchard_road = [1.304247, 103.833264] # [3 ]8 o' ?1 i/ [1 D# r* B/ W coordinate_changi_airport = [1.357557, 103.98847]( W- O* j3 t$ C$ X7 w0 J0 x coordinate_nus = [1.296202,103.776899] * Y( S/ Y/ @" g7 H2 L coordinate_ntu = [1.34841, 103.682933] % B8 `8 S# A: \8 c coordinate_zoo = [1.403717, 103.793974]8 ^# n* U) S- N/ h# m; A coordinate_ang_mo_kio = [1.37008, 103.849523] & \3 E- A/ g5 K" O( K/ ]# ~) ]6 O coordinate_yi_shun = [1.429384, 103.835028] % X5 p( f% y% n7 E; H: z- A % g) K0 b! h m- q1 P1 a # icon / y( C; G- \ a" r icon_cloud = "cloud" ! `6 f+ U) s6 G, O9 `: P6 ` icon_sign = "info-sign"8 i) X& D+ h0 C9 }) T. j. D * ^" I9 s3 M1 }' `4 z # define the city map % k( r4 x) ?% V& v/ ?5 X # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} % |, x5 z& P5 W0 A city_map = folium.Map( , e4 F6 g0 y( @8 i! G location=coordinate_orchard_road,/ k; o* O+ C. V7 z- z* U4 m zoom_start=11,9 Q! y ?9 T A% J+ k! K; c1 |: k tiles=OpenStreetMap), S5 u9 w+ t# y# }2 R4 g' Z4 x - _, p2 {/ y; K/ a # add marker in the city map 9 r: f! _5 G3 P+ ] folium.Marker(8 q2 \: h2 i/ ^' @# q coordinate_sentosa, * j+ R( I: Z1 I' A icon=folium.Icon(color=blue)2 A& i) y5 k! X, b ).add_to(city_map)! f; K T {( l2 ]$ s folium.Marker(9 v& R- z4 h; }, B+ m' O" ] coordinate_orchard_road,5 L* ]* t# [' u( r8 l1 T icon=folium.Icon(color=green, icon=icon_cloud)' ~( f+ G" r- Y( C$ h9 Z( f ).add_to(city_map) ! S4 \/ ]; q( K2 ?) ^0 O/ o & A4 {3 o; h3 _$ d0 D- f% H3 e1 [ # add popup1 X) w. w1 Z! |0 L) g/ k7 E folium.Marker( x* \& N5 g0 T4 x/ m* i coordinate_changi_airport,8 R5 C! v Y& }/ d popup=Changi Airport,' `( p# L* B' L6 N% w7 c: y icon=folium.Icon(color=red, icon=icon_sign)4 y6 {. E: \2 v/ S ).add_to(city_map)7 E h$ S- x3 O! I 7 B5 }2 Z4 ?, M J; G # add tooltips and popup & d- y- P# Q4 v( S3 P; @ tooltip = "Click me!" ' r2 A- \" B) j; t& f# f) Y1 I: a; d8 t) A folium.Marker(5 T' m$ q( e4 R5 x coordinate_nus, 9 r) Z: c4 M2 x' o# \7 M( N$ I popup="<i>National University of Singapore</i>",& v6 O+ t8 a& c1 Y1 v* M" h8 w( U tooltip=tooltip- Q" S. L* S; h ).add_to(city_map) 5 K, D7 H; x- W1 W5 W% v# X1 A% h; |: W. ^8 t$ T. N. G folium.Marker( 2 q% L0 K U+ W2 m# v coordinate_ntu, ; j9 V) g- k$ |" {& V' @' f popup="<b>Nanyang Technological University</b>",5 a/ M* U _6 L4 q' B) q* p tooltip=tooltip $ V% S; C+ Z. _: b8 M ).add_to(city_map)1 {# U% o8 ?1 ?2 h4 h7 R7 X 4 b/ [ l X. C5 g # display city map8 x/ u& s6 d2 I E2 v2 i* ? city_map
% b- C5 M# h7 g
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

3 w) j) M' I. d
# define the city map 1 j- l# Y, `* I( b' ^* o- j city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11), F/ f* H8 q" o- F - d* f+ ~* k8 a # 在地图中添加经纬度, add latitude and longitude in the map when click6 O+ I# U# z1 f' r9 S city_map.add_child(folium.LatLngPopup()) ! S6 W. M3 {( Q! R( v! H' ?1 |( ?/ }8 c8 k; P4 p2 z city_map
& b5 W, J# }" Y+ X1 x6 \ ! `% _4 N) K4 S# x

几何形状

1 @ Q2 S6 N' S

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

! U# `) ^4 Q- o$ }+ {
# define the city map . p1 j8 Y! k' b+ l/ c' O: a. L, v city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)& U. f6 w/ ?8 ]+ Y# } + p1 u# Q4 m! ~7 ~+ A$ } # 在地图中如何添加形状$ ]7 S& x2 i+ O5 D" f W # 多条边 7 q6 E6 E+ O& P0 S& t points_1 = [ - Y' M1 z7 Q. n- s% D( g coordinate_ntu,( m( @5 R9 ^- G5 s: ^3 X3 R+ `; j; h coordinate_nus, . V ]& ^! x# ]$ J/ L& T coordinate_zoo/ G8 J |$ i0 @# W. U* x# ^% k ]% J3 s( a( i3 M; V, s$ W 7 V* s- ?9 d# Y) R # 在 city_map 中添加多条边,第一种添加方式 0 F+ g/ i6 k8 ^7 C5 B- ]0 q city_map.add_child(folium.PolyLine(7 g( q2 T3 G; T- s: U- W locations=points_1, # 坐标列表 ; {, Q- f- g& H! K. ~" W3 g weight=3, # 线条宽度 ! {! ]4 ?9 ?: v5 X5 ]4 K9 s color=gray)) ! y9 J* ?1 d; d) e' s! D% n1 O( E; n5 Z" g- ?1 B! c/ z # 在 city_map 中添加多条边,第二种添加方式5 W- L+ r) ~4 ]) k; d folium.PolyLine(- h* Z9 T2 F# A: n( E4 m$ {2 B+ ` locations=points_1, # 坐标列表 & n" Z: k/ E7 d$ m4 f- X weight=3, # 线条宽度 + E3 ] T7 Q) Q* L8 q color=gray).add_to(city_map)( E, x& W& u/ X% o. i/ o* K / |( n8 V+ P+ Z# T0 ~# c # 多边形+ U8 ^" w" p+ ]! s points_2 = [ , }* ? _7 l+ G4 C/ m) v coordinate_orchard_road, ' D- B) z# l( J) ?3 _/ H coordinate_sentosa, ; t- m; D+ ?1 X5 |& O* c/ C# b coordinate_changi_airport 5 Y8 F- b# y$ B+ {5 A# q9 G: f ] $ [$ K& S9 u4 g2 {- e6 j i 5 Y3 N( E7 e7 P* g4 ]9 ? city_map.add_child(folium.Polygon(/ r1 t* t. ] e) H# I9 l; k) n locations=points_2, # 坐标列表 " N$ f2 q7 G$ y0 s weight=3, # 线条宽度 % t! ^- ?" x; r# A# l8 y0 _ color=yellow)) 2 j* L4 n3 p- \* e \1 l( a& ^) Y9 z, h # 矩形9 D, z) W% M7 ?* S; L9 z bounds = [ # Y" }3 X) p8 `' z4 F9 J coordinate_ang_mo_kio,6 `- Y1 H/ E9 K- X1 \1 V coordinate_yi_shun6 a/ J! K7 R4 r6 h1 Z ]( m8 z/ \+ P8 W 6 a7 y' w4 p' h5 X, H( { city_map.add_child(folium.Rectangle( 2 E- N* x& @' n# X2 S% X. Z2 }7 n bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)6 a: m* A2 F5 K" m weight=2, # 线条宽度; b8 b/ f5 P0 b2 z9 j$ Y, S color=blue)), f5 @& Z: R1 P ; ^* h0 c" [$ V. `% i8 S/ l: c # 圆形, circle, radius units meters3 b2 @/ A4 Q6 G4 L: z, }& D# W folium.Circle(: G2 t2 d! w" L4 q* S) Y radius=1000, , j: V# d8 R* a7 e location=coordinate_nus, * d) J, L3 w2 T5 J1 _# T popup="National University of Singapore",- e! M- r3 T# C% V5 N) e6 Q# X8 E- r color="crimson", * q; a `0 Z/ _, _8 B7 ]) @' t4 c fill=False,! R* b" l) r i, `- N ).add_to(city_map) 3 s% u) @& q) m1 n # @, d$ z8 E' `! U # 圆形, circle, radius units pixels 6 ~" C8 i8 G- f9 i8 B8 f/ S folium.CircleMarker(9 d9 q7 U- Y* |9 ~( \ location=coordinate_ntu,. h% w i; I; J9 E; I! F4 i radius=30,# I/ v3 @1 `6 M popup="Nanyang Technological University", & ^3 w( H( [" S+ s2 G color="#3186cc", ( ]' A+ f* f* I0 B fill=True, % x& U7 |! L7 Q5 h5 a fill_opacity=0.3, # 透明度+ }0 I( {2 G% X; `' S7 i! l fill_color="#3186cc", % D4 ^; w6 ] m+ B! ?2 S ).add_to(city_map)& d' u: |# k+ r ( R, r* t. ]( ~5 b3 n city_map
+ ]0 v1 C9 L+ A0 P/ o
Folium 中的画出各种形状

热力图

$ g4 b8 m* {8 L5 m

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

5 j4 n I6 H: D+ w0 c) o, x% c( Y. R
import numpy as np $ l# [: p \1 G6 v# z; T7 M% V from folium.plugins import HeatMap 4 ^' B5 e7 |7 Q# m+ V; W! ]' e , w# P2 U% o2 Z6 d5 M1 F # define the city map* U* p& r5 z* a, ]* b( ~! S% f city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)% h. o" r- W! v" @ y3 V 5 |7 B2 i' J% V# i' k$ v7 K! R # 构建随机数据" _0 B ~( W k0 p1 l data = (- }+ A* Q1 E' E; J; B np.random.normal(" m8 X4 w& y" w# z: }6 d5 L& W! H+ C size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + H, q: _3 U4 ~% G8 G np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) * R3 _( [+ Q: O7 P5 c: A ).tolist()8 @9 F6 P+ s* N: e$ r- v * K9 s4 m T) c+ V city_map.add_child(HeatMap(data=data))) H0 S |. [& `/ \ city_map
6 i* w% R# k* W. R# M
$ M, p9 x2 ?% c! n

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

/ x. |( u: y. U$ f1 h3 ]
import numpy as np * e7 o3 [' W3 ^5 c/ I7 |/ O from folium.plugins import HeatMapWithTime $ _+ l; y `) w3 ]* k5 }7 a7 c, L / _! Z8 L+ T5 ]* n2 r& P5 N9 T # define the city map6 Z: _ M' R1 q# y: I5 L8 H: Z city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)0 c0 [4 W: U* |" @ % A; w( k* [3 }& F # 使用 numpy 建立初始数据 ) }8 R9 G8 L# u f; D initial_data = (np.random.normal(size=(200, 2)) * . N# [2 V% c" ^ np.array([[0.02, 0.02]]) + ' q% j: q8 D0 Q6 F" g np.array([coordinate_orchard_road])) 3 a) j: C6 z1 D) o) c/ ]. z : c7 U% f; @8 S # 建立连续的数据 ) u3 A ~% J ]; _ data = [initial_data.tolist()] F$ s$ N. U- T7 f4 Q% u* n8 X4 v8 E for i in range(20):# Q, s# n! J* E( A8 w- v2 F0 A data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())0 a8 R/ j8 H* l/ R. q1 H1 c1 H * E: F2 v6 C; | W # 显示连续的热力图 1 w5 p. T5 c: O% P city_map.add_child(HeatMapWithTime(data)), L& Q: s, R3 A* q city_map
6 l/ t: d; v) I- j, b 7 i7 U; ?! e$ E

经纬度点的聚类

$ E9 d2 o3 t" ^+ u9 Z; v! n. J

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

9 N, o. R! X, v. {! Y( ]
from folium.plugins import MarkerCluster 0 E# |5 L8 b% V7 `- } % x# x' H' L- w # define the city map D1 f9 |" y9 }3 [ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)0 i3 u% w4 f0 X7 V ; U9 I3 a* L2 l2 H/ ~( k# I # 经纬度的聚类) I5 Z. R" t( T; w6 U, M3 q # 在 NUS 的经纬度附近随机生成 100 个点; ; s( k# j- `) u w. L5 x data = ( + u4 C3 `8 o7 u0 b! Y( | np.random.normal(size=(100, 2)); P0 g7 l) h4 a% z, P * np.array([[0.001, 0.001]]) +2 @. H, ?0 S/ @- X2 y0 T) r; s N' G4 g np.array([coordinate_nus]))1 K& I9 u) [& e B: i" n8 S J3 ] , P+ }* S- y, {, l# c* r # create a mark cluster object ' F; |4 T8 k0 z& U2 S- H, s d marker_cluster = MarkerCluster().add_to(city_map)# a2 S8 P8 N4 O& u0 H 7 w$ J3 u, X( }" d9 Y # 将这些经纬度数据加入聚类 ' j, c/ G/ B' H0 }* N6 K/ Z for element in data: / v/ z4 M. p' f1 y* H folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)( N/ L7 ^7 L6 o. V9 j2 b6 x 4 l0 O% N& w0 [7 h. {: [. y # add marker_cluster to map 0 J$ r2 Y" k0 ] city_map.add_child(marker_cluster) 7 E! ?- w, R0 v1 p, i ' b! i: O6 _7 [1 R: z # 作图& J8 \% }' ?4 p# h city_map
n$ W. w* ?! k% d' ^' d" }! @7 Q: Y3 q ) n& Y- W. m# d3 R

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

0 U" S, U9 ~- ^5 P9 u( M$ R; Y4 j3 W

参考文献

0 f+ J* b: L. u

Folium 官方文档:Folium - Folium 0.12.1 documentation

8 F$ b" H9 e, `: V 4 }5 d# B# r6 l8 v3 l( J4 Y $ Y: ~' P7 o' g! T6 A- `0 K- |1 D/ |6 G/ `6 H, e! V- D 7 c7 N% h2 a) V, ]% M& D8 F
回复

举报 使用道具

相关帖子

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