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

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

[复制链接]
1 E5 s: `% D7 p8 C5 a

Folium 简介

e) u& ^6 Q* c# b. D! v' |

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

5 ?4 [$ P9 R- [: s

创建地图

3 G+ k$ E8 Z% b. R2 [

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

) c' |; J# f0 w8 b
import folium. i/ f6 w7 P2 x% Y" R %matplotlib inline 8 B! R& H2 o k# }& {1 w/ F5 O) p, K) L( I* l import webbrowser " U r5 `' l3 p$ z; V7 N; {! U% N4 @! x- o5 z print(folium.__version__) 4 r5 ]( _2 }1 \$ D6 ~' l! w$ O / D. |) v; f+ Q8 v% d7 g9 l9 ^, r # define the world map* u! y% ?* o7 m; x# _2 M) N world_map = folium.Map() 0 e2 z0 O* b3 U4 q # display world map - A' ]: B7 D' q+ M world_map% J# @0 z1 U/ q' e
3 _4 X8 ^: h" n. g* B6 u4 x
世界地图

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

, m$ [; n" b3 s% J, y5 \4 j

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

0 T8 r8 j' x) I7 U4 ?

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

0 _# }# {& k! u: i5 x

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

7 P7 f7 m# V I! I& X1 w
# latitude and longitude in Singapore city% S( H, R3 {4 D1 q- m3 p; Q coordinate_sentosa = [1.248946, 103.834306]: h# u5 G) {) X4 Y9 ~2 W: L; ~ coordinate_orchard_road = [1.304247, 103.833264] + k- E" z6 r" l4 { coordinate_changi_airport = [1.357557, 103.98847] 5 K4 t7 A# n0 T' Z- J coordinate_nus = [1.296202,103.776899] + o5 P/ p: G5 B# U; }. k5 O coordinate_ntu = [1.34841, 103.682933] ' j6 H. a5 ^ }. W" O coordinate_zoo = [1.403717, 103.793974]- ]+ z! `: q W/ r1 ^0 y coordinate_ang_mo_kio = [1.37008, 103.849523] * J% A8 R: r7 |* \ X coordinate_yi_shun = [1.429384, 103.835028]: p% q' O7 s9 B ! d* r! `* ]; e' l # icon% a0 ?$ ^0 x, R icon_cloud = "cloud" $ h0 d2 j4 Y. Q8 a. n3 E icon_sign = "info-sign" + J9 [ y$ U' z$ Q0 c ; B: Z6 A# m0 N # define the city map 1 C. m: w- ]; B1 \ # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} 5 _/ Z: |) w5 _) d$ Q. G city_map = folium.Map(/ Q3 \6 w; l2 g' y1 ~1 k* S; h; @0 p location=coordinate_orchard_road,9 K6 r, A$ z: m: f zoom_start=11, 2 G: I+ M: E' r" J* ?# }! d* ^8 H tiles=OpenStreetMap) 8 w! B" h6 h b. s" X: T* j % k1 Y! \" ^% u9 n' E # add marker in the city map* B [4 s3 ?6 _' @ folium.Marker( 9 p) f3 n7 j# ]3 K) ?9 H ? coordinate_sentosa,% y7 Y- Q) E p8 g9 t icon=folium.Icon(color=blue) 9 i2 ?( @$ U; A4 Y4 I1 A ).add_to(city_map), D# \0 l9 @: g5 m+ G$ _3 O folium.Marker( + B# ], j8 W8 F! X% e coordinate_orchard_road, 2 a9 `) ^" l! C) V& H& b icon=folium.Icon(color=green, icon=icon_cloud) 7 z/ b6 P" C) d( A! V- p* @ ).add_to(city_map) 6 Y9 H! n$ ~2 b9 m( \& a1 H * }( i5 i4 M2 v% f # add popup# G/ H; V8 J# e folium.Marker(0 U2 M( J2 p7 _3 F, @ coordinate_changi_airport,; n' d1 D2 ^7 j& l popup=Changi Airport,, o |9 N5 s( m: ^8 W8 J$ M1 | icon=folium.Icon(color=red, icon=icon_sign)1 w: |" J, g# x# z2 ` ).add_to(city_map) , P5 \* a7 ?% p. Q d" d5 }! p6 h h' w # add tooltips and popup * G$ E$ K9 Y# _! Y$ S& f' k7 A J tooltip = "Click me!" 6 ]- {) `( G: D& ?# L: h) B9 r" i2 e5 A folium.Marker(" g3 U X4 Z: g/ m+ l3 g8 X& [: c- [ coordinate_nus,( i& a% k3 ^2 ]! I: T popup="<i>National University of Singapore</i>",1 e" i6 T3 F& q! d- L7 d# K tooltip=tooltip : X, @' e) o+ _0 w ).add_to(city_map)' P, F: r0 D; \! w * g9 g/ h4 k3 J; z folium.Marker( + ?. a9 P0 B0 h& V6 ^ coordinate_ntu,' V2 e/ _& t& G! M popup="<b>Nanyang Technological University</b>", k7 R9 q: ^0 G4 ~ tooltip=tooltip l* g$ F e2 M) P. |/ ~& y ).add_to(city_map)3 W# v, \, y4 q% F# {+ C3 @* F/ d 4 N$ u8 ]$ T4 ?/ H, `! g # display city map & v2 Z) w: D2 B F) U9 Q+ @ C8 \ city_map
: ?. [& O$ W+ j9 r7 `( H' G
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

( T1 t; S" y6 A' Q4 f0 I
# define the city map, B, _) {. ]' h' i city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 4 ?; S! e# \$ i( }' W- T) L5 l A- v# A5 n/ f& ~! |$ s # 在地图中添加经纬度, add latitude and longitude in the map when click) [8 A. n0 {) ]& ?, E W0 W; C city_map.add_child(folium.LatLngPopup()) ' r& @; U% z8 a% U8 ? ; U3 e7 ~- P" ^9 l+ d( i0 ^ city_map
% d! Z; J3 H/ `8 v 3 e- @. I8 V9 l$ l8 x3 w& ]! G

几何形状

/ S& M/ p0 @( N9 n; e. X( w

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

/ r0 a9 |( c, |: y; ]' _- a
# define the city map ( h& F# a+ `* s city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)) F7 F6 a6 U' D- W! j8 _' m2 I, l 1 N1 w, l" b" W+ `, U # 在地图中如何添加形状 " s' @ k1 w6 @$ a # 多条边 2 o7 @ [+ H0 O/ ?# f, m points_1 = [& ~4 {) Z2 V# U% R( P coordinate_ntu, + ^( n% `2 s# h2 @ [4 m coordinate_nus, . K, _) m8 v6 Y: n3 A% x+ C# y coordinate_zoo6 T) e+ J/ N9 F9 i; \ ] * x o" N; e, `2 J& s! w& b 8 W6 S' I* F) @: @ # 在 city_map 中添加多条边,第一种添加方式. |" e4 e3 A9 Y city_map.add_child(folium.PolyLine(/ i- w* m" y; R% B8 f locations=points_1, # 坐标列表7 k9 E& Q& t3 K! y' ]/ o weight=3, # 线条宽度 9 {1 i4 p6 W' j: f color=gray)) ( Y/ S6 z) D$ L# ^# L* l( l. X3 X; G3 K' N% @) a: G( B# T9 K # 在 city_map 中添加多条边,第二种添加方式 s9 L: _& n; }, s+ \6 c folium.PolyLine(6 @2 k# y' ^: q4 M+ [- N& h0 V. I: J locations=points_1, # 坐标列表 : o# x! m: k, I* z weight=3, # 线条宽度5 l+ h& W2 h+ f color=gray).add_to(city_map), L' K: @8 Q; V* S2 i: z 4 B" h/ U6 U. Z/ k! y5 C4 K # 多边形0 ~( b( B( l2 b6 A1 Z( ^/ e* O points_2 = [1 g- i' i6 K& O% ]# t7 D9 V coordinate_orchard_road, : g3 ^# N5 E/ ^0 _, d" I coordinate_sentosa, : K$ [. R3 ?& n, P# q+ |! J1 K( U9 ? coordinate_changi_airport ( t+ @! E/ j) s t2 N ] 7 j9 }9 G$ z% T& ]. Y 0 e+ g/ r1 @( @; P, A5 W city_map.add_child(folium.Polygon( % h: C% o |. B# f! ~- D& t locations=points_2, # 坐标列表6 K; x1 [- m5 T0 @4 Y6 D7 ~ weight=3, # 线条宽度( | Q/ @% Q, ~7 D0 c color=yellow)) $ U" Q0 J8 g5 w8 T; q0 h. ]9 K9 x% v, L% o% R # 矩形. h" g3 M1 |1 g9 c5 c6 V bounds = [5 s7 m2 Z* x3 N7 d coordinate_ang_mo_kio, 5 x4 F+ e; U0 n/ {9 w) H coordinate_yi_shun ' \* _* d$ p% W3 U ] 9 Z( S+ v, S/ T. v6 m1 T( R! g, @( z" |5 ?7 f& t city_map.add_child(folium.Rectangle(' \6 r0 q3 b/ B! f3 W1 I bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)/ x- k$ D! q1 L1 x7 b J weight=2, # 线条宽度. k" ?4 a1 ~ _) N7 f$ T9 L; y* g color=blue)) ; e o* Y o& V7 T; M" l0 h7 \! Z) m" H7 n) M% X1 Z6 a; S0 [1 _ # 圆形, circle, radius units meters * a# V, h: m$ V folium.Circle(+ M# }5 F+ c! m8 z" n9 v3 x radius=1000, 7 f: e+ K6 l( \0 w location=coordinate_nus,& i" ^, U+ v3 `, `/ M3 q popup="National University of Singapore",8 A/ {5 [8 o7 k/ ]3 F- \/ U* Z color="crimson", 5 t" x6 L/ Q1 B fill=False,% r6 Z# [2 D( R! M4 Q- ]: ^: `0 \ ).add_to(city_map)( E+ y& R8 x* O4 W" [9 {* ?! O% Z( v T1 R$ [* T. ~% [ # 圆形, circle, radius units pixels 8 `: O' m( G; j8 b folium.CircleMarker(6 j2 U; S) j' t5 q8 Y0 N! k location=coordinate_ntu, 3 [: V4 o' H4 u7 k; c radius=30,9 }7 C" g3 o7 i7 | e- p popup="Nanyang Technological University"," K( u" w9 b& G& {; u color="#3186cc", S) G. F* m( @8 V7 @2 X" h fill=True, - l4 A ~+ ^& q2 u. s. E& p fill_opacity=0.3, # 透明度 Q2 U, {0 l% o7 {' G; z2 t1 m fill_color="#3186cc",) I- J3 g" ?; t. A$ i ).add_to(city_map) : _: P; H+ `3 P& E u 3 U! V- K+ T4 P+ E, ^& r0 N4 p. O$ j city_map
+ c8 E6 j5 l4 U$ L1 U$ `0 T
Folium 中的画出各种形状

热力图

4 s, m: n3 A$ b% g/ e: y

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

% a5 q1 d& Z* V* x* L/ q( |$ T
import numpy as np! x, S" {/ L' w; t) Q0 @ from folium.plugins import HeatMap 0 p3 s! H6 b5 {+ h2 ^9 T& g A! v4 s; T3 h. l # define the city map3 O& c3 b# y& q city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)4 \$ D7 W1 s0 @3 I& X9 Z ) ^" R; S- Z4 G$ W2 K& ]3 d # 构建随机数据 8 @) H- `1 [2 E data = (4 s7 }$ B5 n( s1 k np.random.normal(6 y/ l1 \& a' \/ r" W size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + $ p s9 ] @* D5 q4 {7 l2 H6 N: a np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) 0 r2 r/ i' O5 t6 l8 ^ ).tolist()9 g5 ?4 R5 C0 K7 ?% h 0 j2 L' w& ^& s" B E4 d city_map.add_child(HeatMap(data=data))5 l# Y3 z' A0 O city_map
+ \3 e% u- G% W/ ^' c0 H
/ a5 G. W& B2 F ?" n

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

9 {! S/ _" e: t, ~- m2 J' q. m! S4 e
import numpy as np4 U& t4 o7 t( I- m; U6 o) O" R from folium.plugins import HeatMapWithTime / s& H! @+ E7 w6 L) m+ Y! d( O H m& U; V4 k # define the city map3 T1 W5 q3 c7 Q city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)5 h" D+ \, g6 f, d3 H4 C# I B$ u m7 V) N; i! V # 使用 numpy 建立初始数据1 M. ]" ~2 X _; H initial_data = (np.random.normal(size=(200, 2)) *+ k7 A n* @9 U9 P4 ~ np.array([[0.02, 0.02]]) + 2 V' I8 Q" Y, h7 N3 X, R np.array([coordinate_orchard_road])) 2 _: Y' H F! o # M+ p4 H' x8 R& ~2 L [1 Z # 建立连续的数据 + S/ T: h: ^! Q- d% |5 d data = [initial_data.tolist()], e' }/ w0 e& ]* ] _ for i in range(20):6 s- {' I) M. K2 _& w data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())1 G# `7 K; ~9 d# r 0 P# M! @- ?9 E* `( q # 显示连续的热力图 $ I6 I$ B8 g- e, ^ city_map.add_child(HeatMapWithTime(data))7 |# D* n6 D5 M, s city_map
% j# O& i) [$ ~# q. l8 u 1 U$ w# c0 ^0 Y! t

经纬度点的聚类

+ |. Z: O8 H3 C6 P" e5 T

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

: a( K& n C; ?3 f
from folium.plugins import MarkerCluster 9 @' W) V5 ?7 {7 ~6 s" p4 n0 Q3 K) S - `! T- D. Y; b; a$ N # define the city map* C, E; d3 o2 j8 D7 q city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 8 ^7 v6 a: R. V; m% J0 S6 e; @9 M0 P# b( u. L5 Q& Q7 t! B # 经纬度的聚类; U+ N4 N5 @3 ~! {/ _% s # 在 NUS 的经纬度附近随机生成 100 个点; 2 i# a' N/ V7 A8 @8 N/ ?6 q8 S- ^ data = (; J) z1 k( H0 p np.random.normal(size=(100, 2))! n9 ^9 o1 D- x * np.array([[0.001, 0.001]]) +% d6 b% C) x& i* O" W& r) j np.array([coordinate_nus])) I* e! w* A3 A1 ~ + @& ~$ n" Z1 ^/ S1 S- \( | # create a mark cluster object 4 Z6 L1 C6 \" }* N* l1 w' v marker_cluster = MarkerCluster().add_to(city_map) 0 F* N. A- M! T/ t/ e) d! { 4 Y% I. P' x8 }- G! D$ \7 `4 L- x # 将这些经纬度数据加入聚类6 Q+ Y$ i v$ w for element in data: 9 A/ M2 |5 D9 \, P9 B$ U folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) G) G) |" S6 {* k- U/ ` # [# j. B7 W/ i1 _ # add marker_cluster to map r* Z- G/ ~4 Z- P# \ city_map.add_child(marker_cluster) 9 Z# z: k" N( W; e0 v* v# ]% ~$ p, c }; v # 作图 ! r0 E j. E" s city_map
& ?) F* ?* G6 {+ k 5 e& J3 a& }4 i! \

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

" P4 C/ t" G! R

参考文献

$ V k' _. K+ a, L- q+ V/ c3 R" ^

Folium 官方文档:Folium - Folium 0.12.1 documentation

i: N2 f0 S; i) P" m ; G; H- ^3 l# s" K* S" [) K: o; D" ?# x' x% A . g; }, y; m& s8 y' e. B. D. Z a0 v- u6 Y0 [5 D* H, ]
回复

举报 使用道具

相关帖子

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