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

[复制链接]
Q1 ^/ V# c' }

Folium 简介

4 _( |/ y& F9 I4 U, C8 L

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

3 Q* f+ b# K" F" Q @5 M

创建地图

8 u {. ]5 z8 F2 N& y

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

! R( K' E9 [9 q P' C
import folium * n9 u8 W( N( O% _) t %matplotlib inline 2 A2 Q$ `+ z1 H* D7 o8 _ T( O ' S. I& m& o; t0 E0 Y! X/ D& f import webbrowser 1 w# k, U) [ E7 p& i1 D# `- p$ A! P' u9 E$ c3 p! D print(folium.__version__) . n% _/ J- a, }4 h % e, y9 |" A# @$ m # define the world map 1 }, q& P5 ~, C* s4 a4 t7 X world_map = folium.Map() : t0 j9 _. ]# q9 L8 S" y) ]- q # display world map 4 h1 p. D3 s1 R, q5 e& o& e7 b: u world_map + u1 w+ s' `" H Z2 S
0 V) e) c" H$ N4 |/ U% k4 ^0 X
世界地图

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

+ ^4 z) \# P7 \+ i5 j v

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

0 F1 c+ t$ l+ j2 l! _, {9 V

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

( K/ Q) u% b5 f: N% x5 h+ \ ?

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

# G: N. ^3 e0 K" M0 J
# latitude and longitude in Singapore city- v5 o& I- `' {3 g: H coordinate_sentosa = [1.248946, 103.834306]: e' U6 G6 `. j. |" R coordinate_orchard_road = [1.304247, 103.833264] 0 H; e6 D# D0 P/ k. d, A2 q coordinate_changi_airport = [1.357557, 103.98847]8 H o7 C8 Z* ~7 U coordinate_nus = [1.296202,103.776899] " D2 v# P0 H% I3 @, A coordinate_ntu = [1.34841, 103.682933]* v W7 k9 V" P3 r! Y5 A coordinate_zoo = [1.403717, 103.793974]0 T# X5 h0 B+ Y. D* B coordinate_ang_mo_kio = [1.37008, 103.849523]/ K4 n, q8 g+ H# M3 r1 d U coordinate_yi_shun = [1.429384, 103.835028] 3 J$ `/ p3 Z% q% R3 i2 I- A9 K" S4 B; ?; L ^- j # icon 0 q( H$ v" x! w& n, E1 } icon_cloud = "cloud"& u- o' I3 C" L3 v icon_sign = "info-sign". e) J2 q6 f2 p4 n. L! ?# t3 {* s 5 r4 {5 c C* Z8 i! \+ I, O- d0 \ # define the city map, r$ r4 D, y' K- [7 ` X8 W # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} ; \) Z2 H& ?; j city_map = folium.Map( 2 j2 A7 z+ ~0 H5 q/ | location=coordinate_orchard_road, # D" z/ M$ R6 R* y2 J zoom_start=11, 0 K5 m& _2 G t9 O. j2 A tiles=OpenStreetMap) 0 K/ {! O2 \# D" {9 B . D0 c, Y1 h2 ?1 F8 J # add marker in the city map! W4 { P, B( W) r$ k& ?7 k folium.Marker( - K$ c0 f5 H$ [) a coordinate_sentosa, : b7 B, T4 o9 L$ F4 W icon=folium.Icon(color=blue)6 c" }3 d. A$ ?9 ~5 X. X! { ).add_to(city_map)1 B5 i# r: @4 }# H: M$ Y0 F folium.Marker( 5 l* a! C0 R( m' \- F7 d coordinate_orchard_road,3 X9 X( G- R; q H# I$ y4 w+ s( T) s icon=folium.Icon(color=green, icon=icon_cloud) ) i; u$ [# y. e8 L ).add_to(city_map)" _4 ^# G0 {* }3 G4 s! h5 f6 O % b4 O! @% Z' I& y& I4 U$ m$ o # add popup6 [' h4 A: X6 h* E; n& O7 P folium.Marker( ) h. [* U5 R7 W6 G# a' x/ e coordinate_changi_airport, - l2 B6 q+ A, P& B6 D1 t popup=Changi Airport,$ s# |: T( w5 {: p icon=folium.Icon(color=red, icon=icon_sign)* `: r' z" ^5 Z ).add_to(city_map)5 Q! s" j8 w2 ^2 q: d' E9 [% g : G5 H3 J" b) i6 ^, ]% p # add tooltips and popup' o, P: X- Q, N0 C( U S$ q tooltip = "Click me!"% H0 N+ I$ T& B7 R 1 @8 P u) ], P5 \) F folium.Marker( 8 O% c9 y& z' X# e0 S coordinate_nus,1 h, x* A8 l) q6 V! B* ?4 E ?! k popup="<i>National University of Singapore</i>", 3 b# v6 z0 q$ Z" ~: \ tooltip=tooltip - D- ]- Z6 K: f$ u1 _* B0 C) s ).add_to(city_map)& Y7 L; ^! t3 c6 b! S ; ]/ M$ d$ R4 p0 L folium.Marker(( }2 s& e8 T' L3 m5 Z, u coordinate_ntu, 9 x$ w, P; r7 s% H$ k! v$ J. e L popup="<b>Nanyang Technological University</b>", # t! s! g% C9 x1 f+ l& N' B tooltip=tooltip, _1 P) G# M( P$ n; X ).add_to(city_map)' j, L( G# i5 ?( G' n 7 b+ w1 |4 a4 p; O1 T# E1 z0 K) X! { # display city map , G5 c% H1 ]3 B. k$ n city_map
# Z. b& v, s$ W- X8 d9 Y4 |, Y
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

9 d* U, R* ^- a! O; n! @& _
# define the city map ( Y, o# n1 \5 i" _2 m city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) : W- e; L7 _' C0 Q& V3 l. k! L! Y$ U! r4 F: u) ~ # 在地图中添加经纬度, add latitude and longitude in the map when click + Q* O& O3 h7 [ city_map.add_child(folium.LatLngPopup()) 1 [% D, H W/ |% W % n( W3 b1 e$ a city_map
5 q" |; K$ c+ s/ S " X; I, s H$ q3 S& Y+ u

几何形状

1 l1 Q6 N) \9 g7 w

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

8 T$ Q6 ]* \; g- L
# define the city map# ~: C/ L8 y2 g, z# l2 A9 O city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) l6 C5 _1 Q# @( _( j , Z7 E- ~% P- T+ G, K # 在地图中如何添加形状! \, N$ o: ^0 `8 Z1 k% F # 多条边& R6 K! k5 h' u. b& J2 h. l points_1 = [ : H1 Q3 @& [5 k! r coordinate_ntu,7 \; P9 d) z6 D5 h3 | coordinate_nus, + E6 I% p R) a coordinate_zoo * e8 d0 \# S5 \# Y3 k, J ] 5 }* L: E4 ?& C+ U1 s ; q6 u1 _ g1 _ f9 J0 n+ A- x # 在 city_map 中添加多条边,第一种添加方式 - O3 F4 H, [5 {# b# b. R city_map.add_child(folium.PolyLine( 2 I8 g8 i9 a! s locations=points_1, # 坐标列表4 k: m. @5 S$ I1 l9 o+ g/ M weight=3, # 线条宽度7 z$ P0 C6 W. ? color=gray)) 3 `% ?' }* y Q( h4 I4 \. X9 |1 _: [9 A5 C # 在 city_map 中添加多条边,第二种添加方式2 x1 s# t9 U2 |2 F0 ?( Q7 B! D folium.PolyLine(7 e( N# I6 u; @ locations=points_1, # 坐标列表 6 u7 C0 `1 }7 i& d weight=3, # 线条宽度 ) r4 [* Q* D# R& u color=gray).add_to(city_map) 2 K$ l* ~2 C" D2 m; S$ V . h A; N, c" D5 k8 c7 m# C; k # 多边形, w; r) Z& }) K9 r" M: s* N% a! C points_2 = [: i6 b, u1 Z; Y/ g& T8 Q coordinate_orchard_road,+ U3 z9 a5 X7 [" e! q) p* b coordinate_sentosa, - _- N. B8 V8 z6 e, i* u9 H coordinate_changi_airport2 G* k$ d) |3 c# ?% N$ U# S, } ], ?7 P8 Q7 V+ E 3 h0 y5 R6 ^7 D9 r& y city_map.add_child(folium.Polygon( / f' E I: R8 o+ H1 G* C locations=points_2, # 坐标列表 4 l; J/ |0 q4 T$ u+ Y; a weight=3, # 线条宽度& S+ B5 }0 F1 i% D color=yellow)) 5 @5 u! ]. R$ `% @0 O+ Q8 O $ ^! b6 s! L# x* Z+ Q # 矩形 % a* @# m- Y f% `0 M9 m bounds = [ 3 v$ z5 z0 M0 {! T/ R% E coordinate_ang_mo_kio,' W: U0 \' A, V' p4 A6 ^9 U coordinate_yi_shun ; H) f$ b5 j) d ] . P! Q* k* ^7 n% i# ]5 H* T S% U& S3 m `& ]0 K9 Z city_map.add_child(folium.Rectangle( M* ]" t/ f( B3 v7 C8 A bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)/ L8 x. F. h# Y# I+ C$ C- |" W weight=2, # 线条宽度4 `% N& T# h4 n4 w! i, U color=blue))4 M. }% T7 e% {% W3 e9 Q5 b6 x 8 y. M5 Y5 ?1 e% S. _" e* U, b # 圆形, circle, radius units meters : ?' e4 D9 _7 c3 _& r# E folium.Circle(, Z4 X- e. E4 B" _4 m radius=1000, T, ?5 l6 v$ ~, N7 ` location=coordinate_nus,0 U, G- y0 ]6 K popup="National University of Singapore",1 S, c' ?; x, x( i; j5 Y8 @" I color="crimson"," M0 D; G, L, Y4 e+ e1 F fill=False, 4 K8 O6 ^3 h. X4 C0 d ).add_to(city_map) O6 A" t6 l* \5 t O1 r4 t& C( I 1 o L1 A4 Q3 |% w2 T # 圆形, circle, radius units pixels 4 b3 n% _- X; y6 f0 `8 \" I folium.CircleMarker( " h$ y' c* @1 Q3 J9 ^: w/ \# H: L location=coordinate_ntu,5 J9 d" E/ G6 y( P- ?3 g( v radius=30,; E& j: q2 T! F2 ^7 h+ T popup="Nanyang Technological University",% s( f" H! T7 Y+ E, P4 K color="#3186cc", : T- m) c2 z0 @6 L" N! I1 i fill=True,4 @3 c: m3 D9 Z B( x fill_opacity=0.3, # 透明度 8 q# c3 N; C [+ Y/ X2 g- j fill_color="#3186cc", * w1 `! l6 l. Q% {3 I ).add_to(city_map) 3 B8 w3 y( t& x; L! X y+ q/ U0 t. R4 l/ h* M city_map
* ]# h3 r6 Y# T& j
Folium 中的画出各种形状

热力图

0 s" H' ]8 X2 m; T" D: i, z

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

" s. |6 V6 T3 m, i( L! v
import numpy as np: N. h9 b9 _8 ^& B, I from folium.plugins import HeatMap. c. `7 P1 x# A2 B6 G! z 1 J0 V ]/ W# B* U2 N, K6 [ # define the city map5 k7 c) l" r+ U& J8 }/ n! ]9 u% n city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)- B2 P# W# u/ M , p2 H2 l' v7 q" ?: a # 构建随机数据2 W& f- n: v, l# @! I# k8 ^* | data = (: `8 D$ m1 f3 z/ D( J. o: R np.random.normal(9 P1 b+ s' L, C I7 ^/ v9 Q size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + 4 E _4 H& z4 J H" P- Y np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]); ?; d+ P/ A) C& F9 P3 S ).tolist() 3 J0 e/ N/ i+ y5 D6 } 8 j# _ G- }, ]# _+ i" Z! [ city_map.add_child(HeatMap(data=data))! l" @9 |) M/ ? city_map
9 \9 C5 U2 M' ?% S& v
+ u' o- t' W2 t, @& o1 P0 r( W9 c& E

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

0 X& l9 j3 i1 \- [+ s
import numpy as np+ i g8 Q6 Z3 X7 b0 J6 t from folium.plugins import HeatMapWithTime 4 \8 r2 d# M8 ~. \ ; v: \/ G, b- f # define the city map / U% ` l7 I1 u7 ]' d6 t8 u0 m city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 8 A( ?8 {( H R, I& B! h. ?0 V" k& Q# j |9 P) ?8 t # 使用 numpy 建立初始数据 $ O% Z ~ A+ t9 i initial_data = (np.random.normal(size=(200, 2)) *6 N$ T6 }1 b& r4 v4 C+ K1 n np.array([[0.02, 0.02]]) +* q3 p7 R+ w4 E2 F1 ?3 V/ @+ V; ` np.array([coordinate_orchard_road])) / s0 m+ H5 s# _! `5 Y+ V; ~, U6 P4 s S: [ 4 J. {0 i; a# S # 建立连续的数据" h9 ~( `1 S x* b data = [initial_data.tolist()] 5 q- T. _; \; P7 X( H for i in range(20): . H" e/ b0 Z% g3 w; o0 l data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()): G4 M+ k2 ~. ~/ t ) z3 O6 H. I4 r6 r # 显示连续的热力图9 F: B/ s H1 {$ ?) n; B5 t/ v city_map.add_child(HeatMapWithTime(data)). ~# b: `" }& _7 V# H9 n' ~% M city_map
7 x" C2 S5 G0 g( R' Y 4 T2 z% j3 ^# g) ]/ j& P2 A

经纬度点的聚类

( q& Q0 a" ]( E- X9 j9 E

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

& O! u# f* |; M, x, M& u
from folium.plugins import MarkerCluster* Y1 }! `3 j: H/ }- I9 O3 ` : k) ~0 m- b) h. t+ j3 D4 W # define the city map # h7 N5 c9 V. g* d9 q( R+ D2 J7 n city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)$ v) p2 I+ H: D; P . l+ v, Y) G- N6 S/ d6 P; B # 经纬度的聚类7 b! c9 R: |8 ~6 M. l # 在 NUS 的经纬度附近随机生成 100 个点; 0 u5 A' ~, q. n data = ( ; ?" \5 V. z$ c5 z np.random.normal(size=(100, 2))/ L8 }6 ^" w7 ~$ I6 ~4 s1 h; N * np.array([[0.001, 0.001]]) +9 o, b4 S4 X! O1 b- P! C% j; b0 c+ r0 [) _ np.array([coordinate_nus])), G9 d$ L: t- k' k7 [ 2 C8 l3 v* S# G+ x7 M B$ l! Y1 y. @ # create a mark cluster object 2 R7 p0 ^7 n) @+ L5 s$ {; a% B marker_cluster = MarkerCluster().add_to(city_map) 3 f% [, B* [/ x5 ^% H; E( r4 K: n& m, s # 将这些经纬度数据加入聚类 3 D5 F. t: G. R X1 @7 N for element in data: 5 B; _& J, X' q! C! W4 ], s9 s3 q folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster). K. v/ a( X' P4 ^5 y & ]6 A/ h0 f( } D) A6 A T # add marker_cluster to map / o1 c( A- S' d. P4 n; I city_map.add_child(marker_cluster)* f4 M- y* z3 ]0 h/ m ; f! f* i. z4 C# _. o+ H # 作图& O% X& [9 B2 y1 x city_map
4 p, R& N% A _3 j! { ; v [1 T0 L) J: u7 R" c6 C7 ^

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

; w" M$ d- k3 |* S, j4 d) k

参考文献

) w% f! L: Z) x7 d1 J$ v6 C

Folium 官方文档:Folium - Folium 0.12.1 documentation

8 K7 c0 j; P3 W' D6 \ , x3 V+ d4 f' K, R) `9 l : T- T. G, {8 o' U% s# k ; }/ j: b# f6 R6 N& o& K5 h5 f/ J6 [( s, c
回复

举报 使用道具

相关帖子

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