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

[复制链接]
+ \" Z% w7 T- B5 G' h& \# ~6 j$ s

Folium 简介

6 _/ x% g& l# b f9 d

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

# o! \2 k( s2 d6 {% U. E

创建地图

' O2 Y& u# i, P' p3 ]% z

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

4 P ~/ _3 u5 M/ h
import folium7 K/ E6 K0 T+ p% @ %matplotlib inline 1 O) F c9 B" P9 G ( m) C2 N+ q5 H& N0 N4 } import webbrowser |, @7 z& R$ L7 \/ e; @+ G9 D 1 O) o5 k7 g0 S7 B; i print(folium.__version__) + p2 b, {/ k4 N5 J& x# a- Q* M9 u& `$ f+ C2 J" o0 D # define the world map % ]1 [3 D+ T) m& _2 l8 _* c world_map = folium.Map() 4 i1 Y! h1 f: G/ `' w; z8 p # display world map" k, A! L, i! E world_map0 C, F/ u$ K& L( H
! m1 E3 \5 z% l4 Q5 [* V2 u( u+ K3 [
世界地图

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

' e: P+ E; @! |+ T

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

- [( o# D" P3 M8 t6 V9 V1 D! l

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

$ U9 R# @+ c. G: f* s e) W( ?

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

" R) c0 H) w0 L" f5 D6 ^* ]
# latitude and longitude in Singapore city ' \( G# L6 W! U) }" V2 f coordinate_sentosa = [1.248946, 103.834306] 8 f) e% _* U2 E _ coordinate_orchard_road = [1.304247, 103.833264] 9 `/ {6 U: c* y' x coordinate_changi_airport = [1.357557, 103.98847] 2 O/ i( |# V: f& h' B& W7 d coordinate_nus = [1.296202,103.776899] 7 `/ t/ t7 {0 z5 T2 P( c9 z$ x: F% y coordinate_ntu = [1.34841, 103.682933]$ T9 P5 u8 _8 X9 I* Z coordinate_zoo = [1.403717, 103.793974] ) r) p7 s0 O/ d# a& h5 W! J* E coordinate_ang_mo_kio = [1.37008, 103.849523]0 r# D# P6 a0 \# @# w0 a& z coordinate_yi_shun = [1.429384, 103.835028]$ c! l6 d# e. E0 a! E7 Q6 u. h - P! D% |( {9 M2 Q2 l' b # icon" P A3 V5 ^# |6 R# P9 N# ~ icon_cloud = "cloud" 1 Q" d) r0 x/ p icon_sign = "info-sign" 1 ~$ |7 y" k: @; A) r' o, T" _# ?. L) [" D: Y) {3 o7 J/ g # define the city map8 \1 W1 e( E7 F # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} 9 \' f* m8 A+ N city_map = folium.Map(# M) Z7 r1 N% v1 w8 D8 U location=coordinate_orchard_road,5 V0 p. H2 m: [7 E" B zoom_start=11, ' a. \% W4 O2 N f+ N7 r tiles=OpenStreetMap) & A9 I0 e! x2 g* F, K. n; v 1 I# |; z: P" \+ l0 W # add marker in the city map; _+ [% _' P" v! J% j folium.Marker( ( ~- z3 U, J: }( o* g coordinate_sentosa, 7 `; s0 x- n$ X6 A8 {9 @ icon=folium.Icon(color=blue) / H I! Y- H) p/ I+ z0 A% J ).add_to(city_map)! R \% b& |0 z# B5 [/ k folium.Marker( ( e/ I8 l# P- c& y coordinate_orchard_road, 1 V e2 e* t, U# P2 p8 D0 n icon=folium.Icon(color=green, icon=icon_cloud)0 J8 |" [5 z& o5 u ).add_to(city_map) 5 V& Y$ _+ `9 {; r, M$ S+ ^/ ~4 K9 t1 o/ |4 ^ # add popup + T- d X& q% z9 S5 w5 J$ ] folium.Marker(' V5 E: d# R0 n8 S: x% D9 x0 l coordinate_changi_airport,+ p) T# w; _* k: }3 d! ]4 i* a popup=Changi Airport, 5 c& \7 z1 a/ Z4 N icon=folium.Icon(color=red, icon=icon_sign)3 S4 q& s) Z7 M [- ^9 s ).add_to(city_map) ! u9 ]0 y7 k& O/ v7 f! T3 l8 }3 S0 b5 F. l) c- E7 Z2 F$ y # add tooltips and popup + g' L9 @ u0 n* O# _5 D6 t tooltip = "Click me!" , b; [0 G3 Q6 c7 \0 y: X8 X9 T! P( g7 o8 N1 y2 ? x! i folium.Marker(4 S! h# o* I; X3 p& U" [ coordinate_nus,5 V, N, S( X! O& G& U$ V popup="<i>National University of Singapore</i>",% M+ S0 W. M8 X) d6 @" z3 p tooltip=tooltip$ p9 Q5 r: R! a$ @- W ).add_to(city_map)$ v# f1 D) L3 J1 h3 E* ?! w/ z # H; B+ \" Q" ?( { folium.Marker( : j: C1 ~+ m$ C* D coordinate_ntu, - n- `1 d; b2 ]2 R8 f popup="<b>Nanyang Technological University</b>", L4 p! i; a( D1 _0 E7 c; Q tooltip=tooltip / J# r' O" W5 Z8 q& C% b+ g ).add_to(city_map)& z3 I- |" a1 I2 ?0 H* ]0 w : W2 b S) ^8 n # display city map 7 ~: F# f$ o/ l city_map
9 x0 q4 P- s/ c6 L, `, i
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

' N. C1 N9 o D2 \$ v
# define the city map* J l$ n. |% J# t( l& p7 h city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)) O: w G/ S& e7 \ 3 Y/ ^. W' x. M8 p+ {, I # 在地图中添加经纬度, add latitude and longitude in the map when click 4 [' ~1 `! q$ N3 w4 w* V city_map.add_child(folium.LatLngPopup()) $ x1 K( `1 i- W8 _& u8 [7 p. V2 q3 O L3 X city_map
$ T3 @; r# J' p+ h3 U $ J" E( w& B/ H, ^

几何形状

9 p" j1 i" `3 M. `+ A

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

6 x$ h* y6 _& t N
# define the city map # J% F% I5 Z5 [+ u city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)7 y8 a" m p% p7 f$ I% l! o F3 }; y6 A( o/ k; F5 o # 在地图中如何添加形状 8 Z7 K+ Q' F) r: n' J # 多条边6 J3 j! R) g7 T! Q' ^ points_1 = [* y, W* u2 B2 v$ ]! }9 L: ^ coordinate_ntu, , J% u5 Y- q7 L8 W Q: B coordinate_nus,6 \" d. r- ^0 |+ y. [ t coordinate_zoo! |6 I2 b/ ~8 H. C% H ]" N4 r1 A4 Q. A1 X$ k & k9 A- I! p, G! ^- X; P # 在 city_map 中添加多条边,第一种添加方式1 @1 I7 a' s, B' a, r' \' h city_map.add_child(folium.PolyLine( , O' Z+ f( y, J0 \7 q' | locations=points_1, # 坐标列表$ a; p9 P" ~3 {) d" d weight=3, # 线条宽度. t+ U9 `$ W3 o; ~ color=gray)) ( P! i0 [0 J8 Y% N; m) L2 x1 i9 l& v # 在 city_map 中添加多条边,第二种添加方式 2 Y, ^# G9 H8 B% U2 K8 ^ folium.PolyLine(7 {& a8 Q& ^2 O1 q* y locations=points_1, # 坐标列表/ V4 F$ k! \' c& @$ K- \ weight=3, # 线条宽度 ; q/ u6 h- R( A$ s color=gray).add_to(city_map)8 Z* A+ m+ s+ S2 G $ ~8 k. s3 v2 i. `+ @9 j # 多边形 ' p1 u; e. ?' k. @& j$ l points_2 = [ 0 p/ }( c2 {% Y6 E: r2 Z, S coordinate_orchard_road, 8 U L& Z w' F% r9 k, s- N$ O7 W+ U coordinate_sentosa, F4 a" h, k. I9 O+ I! o% y# k coordinate_changi_airport 8 I. E8 n I+ l Q: b# |+ z ] 4 e; l/ \% a2 V7 N' O# J/ ~ 5 f2 J7 |$ ?7 Y% e4 [6 n# K city_map.add_child(folium.Polygon(, O1 v- U/ c8 S' B locations=points_2, # 坐标列表1 ~+ c6 n# ]8 T8 j8 r# U+ q* | weight=3, # 线条宽度$ j3 Q. M! C1 @( a& s3 |' E color=yellow))5 W* h/ `# g; l2 [0 {1 V $ g' n% N8 u; N4 [4 x& i+ T # 矩形 ) F) p& V! z9 j- F bounds = [ 2 G4 I7 S1 m ^: H8 m coordinate_ang_mo_kio, 0 |6 l S/ J# y; C coordinate_yi_shun . Z" ] e* d$ M( M ] 2 n3 }2 O1 f; j' B8 E w' T b3 g ; A7 _. n) ~, _$ y0 x7 t city_map.add_child(folium.Rectangle( `; e) H+ a. q7 U3 I bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) * z) R: x q. d; a weight=2, # 线条宽度 7 Z9 \+ W; _$ X; C5 E color=blue)) 8 l; ^- ]+ n9 ]! Z G5 \4 G! i" e0 c" v$ N V9 C- [ # 圆形, circle, radius units meters : z& v$ L' _7 ]3 b) m8 c/ b folium.Circle(. H+ w; `/ t3 ? radius=1000, 7 s3 m1 T$ A6 o% Y" H; n( Z: q location=coordinate_nus, . |: ?9 C7 L) ]# W3 W( Z popup="National University of Singapore", # \* i+ K8 J& [, v6 [ color="crimson",1 R' _4 O2 w1 X; A8 k. s fill=False, y+ P" j4 @6 z1 Z% D2 ` ).add_to(city_map) / C, S, n/ X+ o/ _4 {+ ~' l) J) @0 _+ V* N! a7 a% _ # 圆形, circle, radius units pixels& k+ U# X* q. i# z3 ` folium.CircleMarker( 5 C( u7 G4 W+ e; f$ H location=coordinate_ntu,6 ]" q( y/ T8 @$ X radius=30, 4 Y4 `' Y- F" t6 f& n) j1 M1 x popup="Nanyang Technological University", & m$ q- {8 b: P. C$ `- V6 n color="#3186cc", 5 C; U* y" k' ~, e% t9 E0 u v fill=True, # |: x K6 K9 c6 @+ T2 l& f) m/ i' q fill_opacity=0.3, # 透明度 : a- j1 W$ [" d7 |& H. C fill_color="#3186cc", 7 ~: J7 N' I! q G7 p( q# k2 f8 j ).add_to(city_map)9 y9 N$ E( b% q5 m" h# q! ? " M5 N* O1 n, _' P- _# V2 T) J9 m city_map
7 i: E( o3 v2 K: k- F
Folium 中的画出各种形状

热力图

b5 c) K- F- ^! {' X

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

0 ~& V/ B }6 X9 |
import numpy as np7 u& f6 I- T7 u/ N% m6 A from folium.plugins import HeatMap3 A( {3 b% N- R : Z; I5 `- u; v6 F% M # define the city map) D# r* E+ M5 }3 L1 @ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 5 |, D# U3 \2 Z' B0 P+ T ) B. s4 [" K8 O; b% p) h # 构建随机数据 ( a4 Q0 J3 D8 ]$ c data = ( p5 l; W- O& n/ I np.random.normal(8 H. v ]; s5 i size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + ) }5 o& q- ] b np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) c! h- w" p1 c1 D7 x ).tolist(); T5 a" F' F( C1 ?1 ^+ K; _ 7 D% I2 G! ^7 c: @3 ]0 j4 x( g city_map.add_child(HeatMap(data=data))' T |, e# b8 V" k0 E city_map
# ]2 R6 n/ ^# m- G2 d) }: L2 O: F9 R
" ?% H" ]: h1 @* K. B7 z

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

3 S4 M$ G C& Y' I
import numpy as np 1 `- X: ?* W: P' z* M8 n' j+ }/ p: P from folium.plugins import HeatMapWithTime' B. y. T0 v- c & t' O4 j# q; [ # define the city map. @; {( u& d) O. H, x city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)- B8 [7 h$ d$ X0 | : [* A1 F) X) {7 S3 n" p # 使用 numpy 建立初始数据* j1 L* v$ [$ F- X/ [ initial_data = (np.random.normal(size=(200, 2)) *! \" J; i& [. B+ ^* |1 K6 z# O np.array([[0.02, 0.02]]) +8 w7 B/ A" K) W( {4 P$ R$ | np.array([coordinate_orchard_road])); D$ j- C* w: x& F3 B9 z$ A5 j $ Z* o( H# c, b/ h # 建立连续的数据7 E6 X/ E# {- `% r$ {9 o9 o data = [initial_data.tolist()]8 `% `0 R- b8 ~) ] for i in range(20): * k1 l# r( a& ^4 x) ^" Z5 T data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())7 T3 `/ T7 O: ^4 s, G % r$ ?2 j- B# F9 k* r2 v # 显示连续的热力图 ; {. b$ m# K$ _' P- ~" \, D. B( p city_map.add_child(HeatMapWithTime(data)) 3 i' W8 J& x, I) G% P city_map
7 Q( d4 K: Z% r& S6 K8 T2 G2 n 7 M! Z$ N5 [' |! h( D# f: B, K9 W

经纬度点的聚类

9 x) Q) l4 O+ N# V2 K5 D0 T+ K3 d

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

+ H3 B$ K% x7 S% Q7 _* ^$ c8 k: m
from folium.plugins import MarkerCluster5 j @( |' [4 f: I7 C8 U . M% J5 q. f6 [3 P1 ? # define the city map$ t* R! W6 \- N! E1 ]" n city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 3 V/ P5 V$ Y; r2 w. {" ` A! W4 V/ v) l& u8 a1 t+ } # 经纬度的聚类2 U/ x$ M$ c' O: _! @ # 在 NUS 的经纬度附近随机生成 100 个点; ' h- d8 b8 n& C data = (7 {! n$ W$ y; m: C. Z! I6 Z np.random.normal(size=(100, 2)). ]- n& v! Y, H% m& L8 k1 L2 } * np.array([[0.001, 0.001]]) + 9 R y+ x) D7 h' n9 V3 q [ np.array([coordinate_nus])) x0 r/ ^) I6 h8 o/ N4 g/ o7 j6 @7 u: u # create a mark cluster object( U+ T2 A r) y5 G. @ marker_cluster = MarkerCluster().add_to(city_map) 0 h+ u) w! H2 _2 {, e6 u% Y8 ]2 Z * m0 k) m! b' P# ]4 Y/ c% b: E8 o # 将这些经纬度数据加入聚类 ) H& X9 T& m: X4 x( q3 u for element in data: # l. B+ L4 Y1 D5 J& x1 [6 @4 v folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)7 N9 S$ M( e+ f6 t1 B4 e# x . r$ F. y9 O; @/ Z # add marker_cluster to map 6 t* F, P6 }# A+ I b( [ city_map.add_child(marker_cluster) 8 B2 S; i) _! D% i0 W5 D1 S- v) D- A # 作图! Q* O6 K$ ~2 l3 G J* W city_map
; {: c" [, P) N! t A ! r+ r) {+ o2 @1 J5 [

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

- Q. C/ p6 B1 \" R8 M2 j

参考文献

! A: x0 f$ h0 m2 ~7 |3 ~6 n

Folium 官方文档:Folium - Folium 0.12.1 documentation

$ g% W" F4 p; }0 O- P0 I( H - F1 a" K# e6 I6 L6 U8 [ & \/ j4 |6 U& Y8 X% Y$ U : S4 o7 F6 N$ f( f0 i- W4 x+ I( j2 H+ Z& A6 {; j2 O" I

相关帖子

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