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

[复制链接]
3 k+ h! T: s$ ?! J6 j8 u* \& P0 Z- a

Folium 简介

2 m. B/ ~8 j9 p9 Z

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

: W+ D4 t' J3 m# ^

创建地图

! r( a# R! B ~3 G

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

% r- w! U8 f* w/ E2 F( }
import folium ) W1 M7 y E# n; t; d6 N %matplotlib inline' f- y0 J) U8 {( N( p5 A1 t+ A \( ~& j/ _/ C+ Q+ u import webbrowser/ Z+ Z. E( c% R 8 {6 |% |, o9 r9 M print(folium.__version__) p, T3 v4 J$ F5 J 3 n7 }( R+ ]2 Q # define the world map ) j2 c: j2 V7 H* H world_map = folium.Map() 4 M: b) ^2 k- D, ~$ n1 _ # display world map# \1 d* L; T1 V c9 J world_map 1 a8 S3 W7 {4 y: l, Q1 v
7 U+ i5 K5 O9 s( n' W( O. r
世界地图

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

6 t1 n+ g: _& M; o

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

5 w$ J& O6 R% P# X4 X. W8 Y

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

J* }0 I# l+ L( N7 @' |2 L

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

: N5 W- \4 [! S
# latitude and longitude in Singapore city2 Y) \" {' P) T% R coordinate_sentosa = [1.248946, 103.834306] ! T7 C l7 a6 R5 C2 D2 |: ~ coordinate_orchard_road = [1.304247, 103.833264]6 B" c) w1 S/ | coordinate_changi_airport = [1.357557, 103.98847] 5 K6 B% o+ g; r( p7 V' | coordinate_nus = [1.296202,103.776899] 6 O6 X8 v! [' h+ A: ]9 R coordinate_ntu = [1.34841, 103.682933]1 Z) D" n0 y( i coordinate_zoo = [1.403717, 103.793974]6 Y' z/ t I( j2 w8 o! m coordinate_ang_mo_kio = [1.37008, 103.849523]6 T j+ Y! Q8 g" @" K a coordinate_yi_shun = [1.429384, 103.835028]9 @) X: A q o & }4 Z/ i1 Q: W- i9 M # icon ' l: h4 h! }1 ~+ q$ M+ @: i& v icon_cloud = "cloud" 6 G! q% e' B0 C h. ^' f$ L9 d8 R icon_sign = "info-sign"! y% T- W/ y) u, }3 j 4 C1 ~9 s4 m! f- k% @3 _' a # define the city map ) ]& K1 z2 q% x # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} 2 G0 H0 P0 h6 t6 W2 g# h& m city_map = folium.Map( ) P4 [: l' [5 ? location=coordinate_orchard_road,6 o2 D4 ?5 R6 L; X zoom_start=11, 9 f% ]. P, R$ [6 v( Q( O tiles=OpenStreetMap) % y% s: \" x$ ^ . [# r3 ~. s& w/ M0 g3 n9 T # add marker in the city map5 L0 O6 U+ w" u8 ?2 W) _ folium.Marker( ( f0 F1 t+ r2 y, h coordinate_sentosa,6 P4 u& i6 p' y: ?& {& g q icon=folium.Icon(color=blue); V4 j: }7 J$ h ).add_to(city_map) ' p" J( M0 X3 S; N* G4 i$ P folium.Marker(9 m" W/ g1 Y- n. g! F7 q ~ coordinate_orchard_road, 5 z% ]6 u3 p# x* B icon=folium.Icon(color=green, icon=icon_cloud) , G3 I( C5 B' p* U) @ ).add_to(city_map) 4 W0 S' D9 k$ j# \( r7 R( ?3 d8 m$ H- O" {/ ~4 F2 S$ q& }" h% p # add popup! N( A0 M( N0 z m folium.Marker(4 `! Z4 o( R( o" _( ]0 r+ j coordinate_changi_airport, / n7 |7 E1 t8 n+ @7 ?5 A popup=Changi Airport, : L% _9 J: T, o1 r6 l icon=folium.Icon(color=red, icon=icon_sign) ?& j! u {* `* N' F ).add_to(city_map)1 p/ _) A% n& e% I; b- S 4 [$ V9 F0 Q. Z0 K # add tooltips and popup5 i+ a; P. T# M8 J7 s ? tooltip = "Click me!" / x9 F# n1 g2 P 2 c- I5 E/ J+ J5 J6 ~, L folium.Marker( ) M M% T& S# X. s& W; L coordinate_nus," ]( [# O; b& a" d: C6 n0 u popup="<i>National University of Singapore</i>",. c0 b+ x3 I* q, f6 e0 K tooltip=tooltip( J+ C: F5 C: m$ ^/ V @ ).add_to(city_map) ! T' P9 D% Z' |& ~. Z6 b9 Y- D/ A ; m% n8 ]6 }! R8 b' F9 f! z _ folium.Marker( 7 ^& s, l) @* m- l9 H" n. P4 g' W coordinate_ntu,6 `% e$ i9 l" x& a6 [4 }+ L6 { popup="<b>Nanyang Technological University</b>", 4 P! b( G+ ~0 U6 C# j tooltip=tooltip& n" F$ X% u$ J6 e ).add_to(city_map) - v6 m* E& W9 q- _% H* C4 P }. k0 c& x: k2 y # display city map% p& c, P9 s( g' R# z& |6 @ city_map
U$ T8 W' M/ ^5 J( U8 E! P% ]
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

9 e$ n7 J" ~4 N H8 ~
# define the city map- T8 e& _- }* O& P) v city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) ; l6 d+ t0 L7 q- o# M# b7 F8 E( h: T # 在地图中添加经纬度, add latitude and longitude in the map when click 1 p: W I* k" a3 X# ~ city_map.add_child(folium.LatLngPopup()) Z- n* M' z( m; n* |" B" | 6 ~8 ]( j5 f/ N: v city_map
& ]" q3 n, m+ {9 O& i# d+ }2 J % y/ A% `1 j- [& ~* s* n/ h5 H

几何形状

7 Z# y9 J* I5 d6 b/ h$ v

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

4 L6 E( o" d4 m. M5 D
# define the city map2 M r, @+ N0 U, p5 b0 O; X city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)* A( [8 R/ v: h5 B % N1 ~& Y$ n7 L/ Y9 v' T( W # 在地图中如何添加形状# @+ t& {# ?- I # 多条边/ b+ G8 a ?% j points_1 = [ 4 C9 a3 `9 Z2 r0 {# t2 @ coordinate_ntu,9 V$ i- t$ d' U coordinate_nus,' j8 Z& y9 i3 f) i2 K3 `8 I coordinate_zoo* v# ~2 q* Z! d' U ]) r& Z0 S, H. m0 ]% [' G 0 w( v& E; N( w4 R# ?4 \ # 在 city_map 中添加多条边,第一种添加方式5 Y. x1 Z. |4 I9 X2 T4 d6 ~ city_map.add_child(folium.PolyLine( + R) Z% z; T1 s R6 R locations=points_1, # 坐标列表 , L* ?- R; P+ A6 W+ M weight=3, # 线条宽度5 l) x E' V; I7 V color=gray)) |9 s5 G) e' O0 q( l 0 v1 h3 m* t% {! x # 在 city_map 中添加多条边,第二种添加方式% o8 f6 s- |, f- K5 D, D& y folium.PolyLine(5 f x, q6 ~6 L$ E3 ] locations=points_1, # 坐标列表3 a; W* M }+ u% l7 z q: T7 ^/ ^# t9 U weight=3, # 线条宽度 # c4 ], V3 ~, Z% T8 _; ` color=gray).add_to(city_map)( E) v0 M5 t; ^6 ~# P 4 R5 u1 U( Q* Q) b, z: d # 多边形; T+ q) r: @: a' q- E7 [ points_2 = [ ( ]: ?4 L( ?: D+ n `, ~3 G9 Y/ z coordinate_orchard_road, : }' `4 f+ @3 C coordinate_sentosa,/ _6 h+ j" V- a coordinate_changi_airport7 @* R; U2 m; }; S* q0 W ]1 H) K E9 t" j! X 6 M' l" B' h# X city_map.add_child(folium.Polygon( % `2 X" x' D' t6 X' L! Z locations=points_2, # 坐标列表 ; X: {9 O& x, B4 T7 j; H% Y2 c; d weight=3, # 线条宽度- _7 {; z2 u0 a- r color=yellow)) - x9 J2 ^% N8 I7 b7 H 2 L8 A& T: \/ t9 k* T # 矩形$ V3 M( N6 }6 l6 Q t bounds = [6 e1 `/ x5 Q# R- M) z. ?- \ coordinate_ang_mo_kio, & r* _! o( g6 ]) w! v" t6 A coordinate_yi_shun * T" y$ `2 K0 R; d& \. n" Z) x O' X ] ' I! C6 s+ Y: L6 a) E1 S3 p# ]/ S . @. I( r" o* ]. m! I city_map.add_child(folium.Rectangle(" T, l' O: q% a. }2 m bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)/ ?5 m" `/ E6 R- Z! B& m W4 ~ weight=2, # 线条宽度 # ?" M$ U# j+ ~$ I/ ^: }8 E0 K color=blue)) . D+ k# ?' f- t" t5 X& \9 g9 v5 w2 T " m$ `% t- s/ y. O+ m6 H # 圆形, circle, radius units meters' j" P# l% `' ?. v- Z+ | folium.Circle( ; R8 y$ C, h* ~9 [ radius=1000,2 k8 S+ ]6 E1 k# V( I9 w location=coordinate_nus,( s+ V: ?6 N- n0 _& z4 Q# L popup="National University of Singapore",$ `+ _/ k: ]1 B, M8 F. Y: J color="crimson", 0 f" W3 k8 q9 X0 n) h3 H! u6 G fill=False, 7 |. T& ^# v1 e* c4 O5 h# ?7 N$ y ).add_to(city_map) ' K8 m/ A6 Z& Y ) J) ?5 O* ]# C( u+ ~, b) i( `& B& y # 圆形, circle, radius units pixels: W; [/ ]8 k7 `3 j/ ]- F folium.CircleMarker(& `0 y! v( X5 V9 j location=coordinate_ntu,( b& ?0 b! K$ F+ r; ?5 i radius=30, 8 @6 S- T; s: D! Q' p" v! q popup="Nanyang Technological University", / Y, v+ \3 \; G$ v color="#3186cc",7 h: ~9 x5 `- v: M# n fill=True, " ]$ v: d) s* l; b fill_opacity=0.3, # 透明度 ; F* s( Y! E: i5 i+ F2 D7 g" R fill_color="#3186cc", 2 _+ s4 t; m! `$ W! Q% K ).add_to(city_map)$ \1 z# |0 ~' r3 F' q2 d/ D& p / V7 m* H( a5 u: ^8 i% y city_map
0 E7 U9 r+ t1 l* s% J3 T0 ~9 s
Folium 中的画出各种形状

热力图

0 o& u1 @) i) Q( ^% \

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

( ^( v$ z% p; b* a6 S
import numpy as np& o+ ?, H2 C2 M8 K from folium.plugins import HeatMap , c" D6 C* x) B# O9 x" {2 ~8 ^ X. W- y0 F3 r! } # define the city map. \2 C! F; t2 a. f& { city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11); B" h- j7 i U/ S% | " C% a! B! b: C' n$ t& D3 s8 \/ m # 构建随机数据9 _* r9 T) a$ R# T/ m6 \ data = ( ( p9 Q/ |4 L$ d' d np.random.normal(8 Y7 y6 _& f2 c$ n& r6 e size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + ; J2 k7 o! g6 f! Q7 S np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])- N, ~+ T c: ~# m* G2 I% z. A5 [ ).tolist()7 ]) }+ g0 A) {" [; P5 V W , Y# G# d( |: u2 W) m# \4 [/ \/ |% | city_map.add_child(HeatMap(data=data))# H# j- G2 g: y: C; O7 y* z city_map
! n9 \6 T @9 B5 N% u. x
- V3 {" Y8 t" ^) R& w$ J. J8 D2 O8 q

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

9 w* s: e" m2 Q4 @5 E" ~/ X
import numpy as np ( S4 P E( e" x4 [/ K from folium.plugins import HeatMapWithTime3 H' o5 g8 H, I; @8 d- J 7 ~5 Y2 e5 p: t- i4 g. w1 W # define the city map, K+ s6 z0 x4 {; {4 q7 L, K, I city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)+ A+ [, U7 x# h+ C* M7 q 3 w9 Y, Y, I( B. |! b7 w # 使用 numpy 建立初始数据 ; o( t1 P' V0 C' ]# L! N5 N# R initial_data = (np.random.normal(size=(200, 2)) *! V) k# ^2 @) g1 d0 F+ s# N" z4 j np.array([[0.02, 0.02]]) +8 q o" m0 i4 i$ d+ ? np.array([coordinate_orchard_road]))- r6 M) l( h) u7 h. c0 [ % G9 p, o2 |6 J# u# T( A # 建立连续的数据0 q- g/ v) X u* b& Z7 ?$ l& P data = [initial_data.tolist()] # X: q3 V" n4 L% Q$ l for i in range(20): 7 ]( T5 K0 E3 B* p% S8 g: p data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()) ) u1 s% k1 }1 R& a. F( h% A# F) E" q" s5 A, g # 显示连续的热力图 + d) z0 k+ x2 Z2 r city_map.add_child(HeatMapWithTime(data))/ l3 H7 }; A1 B city_map
2 V c# e' g: t. K 5 ^" E/ S2 O1 [% P# c+ \

经纬度点的聚类

2 Q3 G- F) U6 o3 L/ F. l

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

3 j: w2 ?5 @* W0 ?8 S
from folium.plugins import MarkerCluster - q5 L+ {9 ]" W3 N8 t. _9 z) y : Q% d' c6 Q! y! P. [5 ~" a # define the city map* e8 `# Z4 k$ c! N6 u city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) * V, v+ D) h1 `/ f/ i 5 w' y# Y* e# N$ q # 经纬度的聚类! I$ v: W( Q$ J I: e1 d # 在 NUS 的经纬度附近随机生成 100 个点; 4 l8 g: V' I( N8 w- u6 ` data = (# g$ r/ P. J( Q, R" x6 Y' ?( k6 z0 p np.random.normal(size=(100, 2))- n# }- `" O7 O* |9 K * np.array([[0.001, 0.001]]) + 7 v9 C5 E4 E2 l% E. h& y7 ~: S np.array([coordinate_nus])) 1 h. p! y. U8 ]0 P1 a( d* ]# N- b# z4 J( s& V # create a mark cluster object 9 C2 r; t6 Q0 r! h: e marker_cluster = MarkerCluster().add_to(city_map) & R0 F. e" w% S/ h9 a* h " Y: U, A6 `% u, I8 S8 U # 将这些经纬度数据加入聚类 2 Y8 _ N- r2 I5 e' b) T0 e" s for element in data:8 a7 o% \8 o: j folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)9 v% J8 i9 O$ a; B1 i# D N3 R" `* O6 k7 u& T- x$ F K # add marker_cluster to map o% A, {) z# Q6 H' G: Y city_map.add_child(marker_cluster)) j1 w" ^/ k# t1 d - h# }- M r8 }& h8 d # 作图: X6 T4 d2 u7 x7 b city_map
) m% {$ `0 I# y& m & e6 \" T6 M. e4 O. z

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

+ l6 i' x, n! d: b* v- B7 U" V

参考文献

% T( _2 ?4 I0 m1 c2 M

Folium 官方文档:Folium - Folium 0.12.1 documentation

3 l8 G, M7 W, J% W . l0 {, u6 |0 C5 b9 c 5 E, \& Z$ C7 ?% u; N 7 I, p# T% J' \+ W : Y+ I! d, l; Y* g- ?, F& I
回复

举报 使用道具

相关帖子

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