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

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

[复制链接]
- M) f2 f1 G+ \+ o# }5 n

Folium 简介

* l; N, H2 c3 F" N ?4 H; e! K% C

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

# X6 v3 u8 `* F1 C1 j1 V, K

创建地图

* F2 o! o6 g/ S0 {, G) G7 R* a

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

. y4 U$ t. M; |
import folium . X6 C! M3 P- u8 A %matplotlib inline 3 R! z3 E# `( q; p2 D ) S& S4 F6 A3 q import webbrowser$ n2 _* [* W1 ~% I8 a5 c5 h0 Z ; W( N! Y0 m' C- S print(folium.__version__)2 j% p+ p7 U# ]; u/ E# L % p' t( j7 m/ X: b: q# ^, \* F0 { # define the world map 4 h1 S: `( c6 w world_map = folium.Map() 7 h. w5 w/ m4 i" D4 e% y # display world map% }1 U; c0 n/ Y world_map 0 u/ C# L" e8 j
0 C0 P/ c, m5 ?' X1 U& r
世界地图

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

( Q' C+ W6 T/ g. y3 Z

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

7 E7 t. m+ n% y2 _$ a0 k/ S( ^

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

3 O! @* `& I3 U2 p

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

, a; |7 E+ B( g% A
# latitude and longitude in Singapore city5 A) {) O! S3 C3 F coordinate_sentosa = [1.248946, 103.834306] _' B) D! q- D0 [0 ^' x+ `1 J( S! X coordinate_orchard_road = [1.304247, 103.833264]: y k9 |& h$ p/ s. |% v2 V% d coordinate_changi_airport = [1.357557, 103.98847]1 v$ A& w& b8 s9 y5 c5 L7 i coordinate_nus = [1.296202,103.776899] ! O; J6 P' L+ A$ R* J" A7 q! { coordinate_ntu = [1.34841, 103.682933] 4 R$ B1 O G* K3 f q; D1 ^2 _1 @& c coordinate_zoo = [1.403717, 103.793974]7 y) I2 x& a3 r) K% s7 ?9 s2 H coordinate_ang_mo_kio = [1.37008, 103.849523] ; E' h; n. L# A0 ~$ e/ {+ p coordinate_yi_shun = [1.429384, 103.835028] 6 s" t# M. z3 J, k $ l! [6 G6 C. [* z # icon + n# ?( U2 D) \' g icon_cloud = "cloud" ( Z8 |5 n$ j$ G7 u' H% N9 d: e( A icon_sign = "info-sign"( q9 B3 R2 B9 P6 Q% ? L/ `3 @ 1 Q Z$ b5 m; |1 ]* d4 U! n' _ # define the city map ! Q W+ S8 F! _# b6 R, Q # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} " F( B# o- x- G8 q" B city_map = folium.Map(6 I5 a! X3 r3 t7 U1 H' E* U. ? location=coordinate_orchard_road, & C' ]/ B& O3 u& z G" L n zoom_start=11,( c/ h2 V/ d/ k/ j* n/ B tiles=OpenStreetMap) * a) C6 i; [3 q, l+ K4 n$ [ ) L, u4 {9 G/ k1 w1 [; V # add marker in the city map2 V1 x& V2 j/ c! C7 | folium.Marker( 9 y6 r) L. D! `+ [ A coordinate_sentosa,' w, t& b" r9 d8 ^% f icon=folium.Icon(color=blue), {1 G+ c u O. H$ H2 M' g ).add_to(city_map) . I7 q8 k0 H0 |3 x3 N, \5 ^ folium.Marker( 6 l& I s) R+ [! P5 G1 Z" x2 k coordinate_orchard_road,8 h- ~6 w! E3 z/ F$ W5 f* k icon=folium.Icon(color=green, icon=icon_cloud) + `( v4 _& N9 L' x o9 P2 r8 U ).add_to(city_map) - \. e: d/ @% Z, D 9 H3 {9 a* H% S( b # add popup& H& b. l; A, l' _' Z8 Z; L folium.Marker( m0 G, k% N% |( V2 \+ | coordinate_changi_airport, : W' t6 w* m6 z9 a popup=Changi Airport, ' K' T; N/ r+ x1 @! n3 m% d icon=folium.Icon(color=red, icon=icon_sign) 3 k+ x ~: }9 f4 r) x ).add_to(city_map)1 p; o/ T1 {/ A# K2 V$ H& w 6 t( ~8 _, j1 x/ V # add tooltips and popup7 A& g% W: D/ ] tooltip = "Click me!" h0 E1 m. I1 Z2 ?2 V Z 4 c; m8 ~; v+ r9 { { folium.Marker(5 ^ M, r6 d; \* w- ^ coordinate_nus,+ B: s& Z5 f( V popup="<i>National University of Singapore</i>", - I$ a- l! U2 F/ E6 p tooltip=tooltip ! v) Y) E4 } f ).add_to(city_map) 4 {7 \3 u+ M% a w3 l# H 2 h! x2 a9 ~6 U9 A$ M folium.Marker(5 Q4 P+ C7 ~- y4 R9 \ coordinate_ntu, / d% Y! Q; O+ {0 e% Y$ q9 L popup="<b>Nanyang Technological University</b>",( r4 b5 }) d, A7 ?6 V' P, \ O5 s tooltip=tooltip& z ~! C* c5 r ).add_to(city_map)9 H* n4 r! S# |. W* P) w 4 P% ?/ Z7 ]; I8 A" E) _) F7 }0 p # display city map - i+ E/ t& Q) z: b0 z1 p( j city_map
% D% @1 C( [! s$ N8 Q/ J0 i
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

2 _+ D5 e3 L+ ?7 p7 @9 A: e9 Q
# define the city map ! n0 [! z" d6 F5 u city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) / g0 `" s9 `) F: u% q% d. ^1 y2 D) E9 w0 r! A6 m # 在地图中添加经纬度, add latitude and longitude in the map when click 6 E6 h* N( n, I9 t& a/ p city_map.add_child(folium.LatLngPopup()) - _ t0 Q& A% a! U! X6 k$ D4 ~. E& K# e7 f( ~( f, l" N" O city_map
7 }+ T/ X# q! l& S6 m0 C4 y" X7 \ % {- a! c# @2 n7 }: b! w

几何形状

" ~# o9 s) B5 R# _/ A

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

1 I3 s; _; k: }: V! o
# define the city map8 o' {. j5 l( ^ u. b city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 3 B% T2 w, T2 I X7 W* o9 O9 l: V3 n # 在地图中如何添加形状3 X# h) v( j: I* x4 J6 ^+ z # 多条边& a' }- G x$ Q. V/ | points_1 = [ ( ?: y1 l l% p/ C& V coordinate_ntu, 0 a h+ y- Z6 L* ` coordinate_nus, # n }, O# m. R- r+ |6 B8 Z6 [* T coordinate_zoo # q0 v, y' o# m, m2 o6 f) K ] - [; `( e0 Z( w; s / e* ?- i4 Z/ p- G1 f9 z # 在 city_map 中添加多条边,第一种添加方式 , t" ?. n! n) w% t& v city_map.add_child(folium.PolyLine(+ t, }% ~: K5 w x locations=points_1, # 坐标列表 * L4 ?8 i, \6 p weight=3, # 线条宽度8 S; F& h& A, k* W2 J color=gray))" _+ U+ y6 j$ e: g( _. P2 v8 } * [& i, i$ b7 x8 C9 }# d% D0 A) U # 在 city_map 中添加多条边,第二种添加方式 " H) N: D% A C, `8 }3 o- p3 @; z6 K folium.PolyLine(( V6 k0 D7 v) k' v locations=points_1, # 坐标列表) O/ o0 n, v# K" Z8 Z weight=3, # 线条宽度# N ?- { B; p& a$ J7 | color=gray).add_to(city_map) G# z$ C/ h/ b 5 L4 |- \0 a5 b$ G0 `2 S # 多边形 1 b0 R. l0 J% V4 W( C5 C& h; X5 s points_2 = [3 n% q, t& a' M( i% v M# _ coordinate_orchard_road, 1 [2 f$ C9 ~& \9 h" ?4 c coordinate_sentosa, 4 _/ l; ]& `; l5 @# `' B! V coordinate_changi_airport2 G% j( u1 |. c* a+ @ ]2 {/ L, j4 U4 g 6 ]3 v1 C7 U0 t/ T3 s& w& a city_map.add_child(folium.Polygon(! \( o8 M9 ^, R8 u, f locations=points_2, # 坐标列表 + g; l+ H, C$ B/ F7 V) }. O' m( ?, W weight=3, # 线条宽度" A- v: C( H( [ R color=yellow)) - I* u& H. A' S% ^ y; H6 b G: e) K( N { J1 U4 A # 矩形 @. B2 O& c( q" `* A bounds = [- ]! [7 i4 j( {( `& I' m coordinate_ang_mo_kio," F3 P# U( W2 t$ S. D- Y% N coordinate_yi_shun o0 v. p3 [, R! B' V$ F ] 6 B$ C( g4 d6 t) W) e3 C: A# h' a% ?) q city_map.add_child(folium.Rectangle( ) h+ K: K# {( y9 y bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) + O" S% U- V3 O4 z* a. C weight=2, # 线条宽度 5 `; o! d$ [: H- c4 c. I0 x color=blue)) n6 U" j/ Y! b7 L) \: X) b ) C$ K! e0 F2 D' v4 Z' F% k U$ L # 圆形, circle, radius units meters % p: T" E7 d0 J8 m% f7 P folium.Circle(8 n9 O" n) y# k! \) u radius=1000, ( n) d" B) d3 |, ]7 E location=coordinate_nus,$ E0 G2 @4 ]4 N popup="National University of Singapore",( X6 A" u( Y9 ?3 Q+ x; `+ C1 T color="crimson",4 V+ v" H/ T8 k* e fill=False, ' S* X; r3 {6 v+ m ).add_to(city_map) 4 n' R q# r& n6 a0 J $ H7 F. R% F+ t4 N7 r8 x: l" S # 圆形, circle, radius units pixels 4 a: i7 u7 T, i+ Q4 y+ @ folium.CircleMarker(2 N5 T+ n& D' ~9 Y location=coordinate_ntu, / E4 F9 C2 j' k) q$ d3 @2 m radius=30, r5 ^- @1 x+ e' |& a popup="Nanyang Technological University",: ~$ v+ {( a7 D; \ color="#3186cc", : R8 o; v4 ^4 \- w( L; c c fill=True, i2 L- E' t$ |! n" M3 D6 Q fill_opacity=0.3, # 透明度 ! E* G2 v$ r3 ]+ Y fill_color="#3186cc",! b" a; ^* e' m+ X- ^/ a+ t ).add_to(city_map) $ d; U- { H7 s! b$ J & j1 ^6 B; p7 \2 y3 q4 ] city_map
! C& J! j6 X+ n8 T8 k4 O" G7 Q% Z
Folium 中的画出各种形状

热力图

7 `6 C2 i% K6 J6 J) {' x

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

$ ~# W! S# s0 J9 n
import numpy as np0 f g9 v) o+ p5 r9 Z from folium.plugins import HeatMap' t% k0 h* ^1 A( y ' F' y& U( l, Q1 X5 C1 m # define the city map/ M7 Z7 }, E6 C% [4 ] city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) ! W/ v. R( d* d& B# m 2 Y2 Z9 n }8 Y" r # 构建随机数据/ {, h; E0 o$ I* H' l4 u data = (4 E, T5 M6 v" N& M4 v4 A) l np.random.normal(: {7 M& w6 _* ]& v size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + ) S6 h* R3 V& W8 e np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) 7 Q& L- B+ m; K) I8 Y# K ).tolist() 4 ^6 x6 L3 Z" M9 i/ G9 d: e 1 t- x x# s4 f& f/ n city_map.add_child(HeatMap(data=data)) / w5 C4 M% M0 v! D city_map
* N$ p$ Z4 z; ]# n) L v8 {! M
) q2 a1 q, D @) V

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

8 a6 y: ~ d g9 s; q% M' S+ h' O
import numpy as np ' ]: Q3 L' ~2 |8 J% ~$ ]( b from folium.plugins import HeatMapWithTime6 B2 n% A" B0 T5 L9 v# O! A# V6 F # \' A- J( I# E( z" J/ ] # define the city map : v& q4 w" h* y" Y city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) # T4 e$ `0 H- i P7 |2 E/ b1 Y# F: Z/ g# |3 A% O6 ~& \ # 使用 numpy 建立初始数据 " o. ]1 V! f2 m+ h initial_data = (np.random.normal(size=(200, 2)) *. X3 @3 ~ E* i3 m" R* N1 S np.array([[0.02, 0.02]]) ++ g. p: w' o9 K np.array([coordinate_orchard_road]))1 b/ g9 \; i0 s9 b* Y , o4 k( T6 h& x; I0 S # 建立连续的数据 8 q9 Z I% T, c data = [initial_data.tolist()]5 @3 L2 P. t C" x" y for i in range(20):) P R; i/ U) Q: | data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()), P. H9 w: d8 _ ; V, W, \* B0 m3 o # 显示连续的热力图$ X" A! A# e6 ]- I1 K city_map.add_child(HeatMapWithTime(data)) ( m8 b: ?2 X& N8 f- M, i city_map
4 W. a! b( c3 m4 m 8 t5 D* i$ n7 T- o

经纬度点的聚类

2 g0 y2 M' X3 j/ ^& K; Y% }4 x* A$ Q& G

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

* \1 ?3 p+ O! X, i5 W2 y6 X0 [
from folium.plugins import MarkerCluster ! X5 K7 i% L6 N' F/ p' c: y9 L ; B' m, j6 `2 j p1 r" `' ?% x- G1 y # define the city map$ C# W, b: x: v! q# _7 y city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)& {# s7 {) @2 n% t$ D0 E * ^; c3 B3 q9 n( z x # 经纬度的聚类 8 v* E) B. ?6 o* x. m7 ]+ U3 g # 在 NUS 的经纬度附近随机生成 100 个点; * u+ K5 X( M, B2 L; ?7 Z data = (+ ^" q8 w }* Z* J" _, V. k- o np.random.normal(size=(100, 2)) 3 S2 @: W4 X' r4 e * np.array([[0.001, 0.001]]) + 3 [( Q7 g, z6 [ np.array([coordinate_nus])) 3 m1 s! L1 q; z2 d& i$ I/ z8 o/ M5 c( G( A7 T0 y: U, f6 a # create a mark cluster object' C7 \2 v \5 {* c) [ marker_cluster = MarkerCluster().add_to(city_map)7 {/ i/ X' ]" L. H* Z2 H ( U1 p# O9 | B, o% o5 e4 N # 将这些经纬度数据加入聚类, m5 A T" O! }5 u5 S$ N for element in data: 6 l) T; d9 I" l* J8 d folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) 0 o/ n- d. V: K. r/ | 0 A' X: X3 ?- ?& N, G5 ~: w # add marker_cluster to map / y0 } h4 W3 ~4 ~+ G9 U city_map.add_child(marker_cluster) 7 m) j/ ]$ o3 c& ?! D- B. E `9 ?& z/ F" z # 作图 $ l% D0 k7 v g6 R3 y city_map
5 L0 ^- d8 M3 T0 Z6 V , B. y4 C" f' e" u

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

- q$ K; C: X. c

参考文献

; f0 A" v) m0 P3 c0 @3 |

Folium 官方文档:Folium - Folium 0.12.1 documentation

! I# N5 D( `7 W; {4 u9 [ ) _0 K( N: S5 v8 h1 D3 O% U( ` , X$ v( b, A! q9 r* m1 z # m; z! A1 w" p$ O' x+ {- r( g4 C X8 y! v
回复

举报 使用道具

相关帖子

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