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

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

[复制链接]
% g5 E' i/ K# a

Folium 简介

" m: h) C) q7 R0 O$ U/ u8 E! l7 Z

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

q! N$ _" m% c- n6 `8 _

创建地图

& F. g) g3 s/ \

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

3 |8 N t$ `7 E! N r
import folium 0 v, A8 O# v5 D; ?% B %matplotlib inline {. @4 R0 z" E6 }# {* M% w5 K 9 ]4 H2 j4 a* K2 j2 W2 ] import webbrowser3 I$ D) ^, }* U' G8 x$ p$ b$ Z& [ + }& u% `! b+ w6 s" H* Q print(folium.__version__)# m: _8 ]! b- V2 E$ O, W) Y7 G* O6 U. ] + E ^/ [% D& l! W' n # define the world map8 q! u. }# h# F5 ^ world_map = folium.Map()8 [/ q' o& m4 S/ I. f! n0 ] | # display world map% p1 y) s9 P! C1 ?3 M world_map- T: x3 e' M+ u7 w
4 j( O, y1 s& B7 K
世界地图

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

. j; J0 p* i. N- j" i( T

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

) p; `! d9 E2 ~4 D. y2 @2 B6 a

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

9 i5 d/ _: U& ]4 K( B3 E( v

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

* g* ~/ A3 X% G0 f! A" D2 h- S
# latitude and longitude in Singapore city# r, ^0 T/ ?) C* ] coordinate_sentosa = [1.248946, 103.834306]. y- B# \* T, J' `; D6 h8 N g1 M coordinate_orchard_road = [1.304247, 103.833264] # n+ n4 c5 s' ]& V! w coordinate_changi_airport = [1.357557, 103.98847] 4 }) F4 A3 b- @2 y, p coordinate_nus = [1.296202,103.776899]4 y0 y' K8 o. `* J+ E* ~ coordinate_ntu = [1.34841, 103.682933]; W' s5 L7 P: _% O8 m coordinate_zoo = [1.403717, 103.793974] 7 M$ s( d8 u* R" F d% R$ Q coordinate_ang_mo_kio = [1.37008, 103.849523] 8 H$ D5 |# y6 f" x coordinate_yi_shun = [1.429384, 103.835028]# C3 o6 w. c2 b2 | ) r) ~3 H7 H* {) r # icon6 E8 V& v8 D" l2 v5 Z0 _: i* x icon_cloud = "cloud" % J6 i, q7 P: E, J icon_sign = "info-sign"5 q9 h6 P9 y$ I- K) H# V # M& @& E' f1 |9 G) U2 g7 G # define the city map ( S: d& K& _ \: ]2 @+ {# t # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} 3 |9 f+ t2 W) g3 y city_map = folium.Map( ; p+ |0 y2 J; u6 {+ r" x2 m" M location=coordinate_orchard_road, " L! D6 C5 E- q zoom_start=11,; a6 B- Q) J! K tiles=OpenStreetMap); @/ [5 f2 X) f% }: c! o& \$ X; I 8 ^# T3 w3 N5 z9 W! G0 p # add marker in the city map* d. y2 L" E/ z: G4 \5 K+ g' N folium.Marker(5 e* {) i P" w& N" }. f coordinate_sentosa, ; `0 u* T- W8 p4 T! a5 J. ^0 |' c- ^ icon=folium.Icon(color=blue) 1 ~+ s2 i& P% L8 P+ C* p ).add_to(city_map)/ I/ |6 }8 V4 C+ U+ g, ]- Y4 X; \ folium.Marker(# v7 m3 z0 G* ]- p coordinate_orchard_road, @9 J% P4 y" s0 |3 T z icon=folium.Icon(color=green, icon=icon_cloud) o/ Y. e2 g# G ).add_to(city_map) 9 K1 _( k$ Z, ? K$ ]% i- C- u5 d # add popup E- y5 E; v6 ?( H8 ~: N folium.Marker( K% l) c% N4 g% O9 y coordinate_changi_airport, . N5 E4 G# l8 k% |" M9 U popup=Changi Airport,: R" y* s+ R# p$ ]9 a" y icon=folium.Icon(color=red, icon=icon_sign)5 w1 H E; h7 \ ).add_to(city_map)5 \$ L4 P$ |# [( T; y 2 K0 H( O/ c, m # add tooltips and popup " r' r* O# S1 T$ R0 ^% p tooltip = "Click me!" ; ^5 ^& c" A* O+ } ! I9 p) g: o" |5 x folium.Marker(& l; W" M: S) C+ Y: z9 I coordinate_nus, 0 ?7 _ K1 T6 a2 ^* _ popup="<i>National University of Singapore</i>",+ @0 [. g2 I$ q tooltip=tooltip % G) [5 M4 L# \& ` N4 U: V ).add_to(city_map)7 b( |% d+ m2 C+ p" ~ # R, V. a, c# |6 k folium.Marker( 8 t2 P6 Q a! T Q O4 h coordinate_ntu,# O+ l/ @1 ~6 C& l0 c/ m popup="<b>Nanyang Technological University</b>",- a$ |$ H6 T$ U9 Z" \* | tooltip=tooltip $ b E$ U0 B3 R6 u6 V: y( O K ).add_to(city_map)+ _1 E# I1 \' b5 R0 r4 c8 |0 u, ]; k" C9 O . \5 B2 t8 m l3 E4 z! u: W # display city map, {6 i! w5 e6 }' l' Z$ c city_map
2 x$ N* }. Q/ m* E& S
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

x+ ]" E& H3 \0 b( Y, M
# define the city map 1 D1 Y0 L9 x3 ? city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)! H$ w" k1 E1 D$ M, ^ # `5 m- \; n' H% h # 在地图中添加经纬度, add latitude and longitude in the map when click , }% X5 d! I2 | city_map.add_child(folium.LatLngPopup()) . |: ~8 _' {9 v$ }4 v* Y: F$ b j4 W4 c: n7 ^6 X" U city_map
+ B) t$ w0 w! `5 Q. y, { * N0 n* u! q2 P- d

几何形状

+ I* E3 @7 R" S$ y! d

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

1 Y' X9 P) N: F
# define the city map- Q2 `9 N# U+ ?2 \3 `% K8 N$ c city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)* _. P& b( s: c2 R5 O5 Y5 ~ 2 d4 c; R, w8 J6 c6 D # 在地图中如何添加形状+ H0 c0 j& }$ s0 w* S# V( ~ # 多条边& Z/ ?" F$ C* N/ l( z$ A$ {& O points_1 = [ a5 ^3 m. @3 s* H0 w" v coordinate_ntu, 9 d3 A* z4 z4 ?2 I/ w coordinate_nus, 1 G: o6 e7 _; V; f9 @8 l coordinate_zoo % ^9 r+ C. M& o! X. N- W9 D* d% D ]+ m2 B& X2 ?( d; H* ^$ O ) m0 t( Y: z, ~9 C& v6 u # 在 city_map 中添加多条边,第一种添加方式 / g8 I% [& |8 w8 O6 a7 A city_map.add_child(folium.PolyLine( V/ k6 K8 T) t8 L" B locations=points_1, # 坐标列表& K4 L* g" W1 m. K6 e% U weight=3, # 线条宽度 0 b6 T, j2 D- E/ ]2 M+ _ color=gray))6 @ T. z: k; q, t 0 u* w+ h1 E, r1 E4 d! L8 W # 在 city_map 中添加多条边,第二种添加方式 4 ~5 e9 X- t3 M9 l/ T: k folium.PolyLine( w% c" I" K9 E# [& O1 X locations=points_1, # 坐标列表: l4 `" K2 C& U8 D4 R2 K weight=3, # 线条宽度 M& ?/ }5 d4 Q( _7 L9 Y4 A0 b1 ? color=gray).add_to(city_map) 6 m& S4 N9 X) Y U7 N 6 J! q$ w. V9 d3 [) C# M5 ^ # 多边形0 a7 \ U1 h+ m2 r" k' ]+ h points_2 = [ " {6 h6 B9 \$ C7 }+ J4 b% m coordinate_orchard_road,0 z# v/ g- c/ p/ h coordinate_sentosa, " D+ ~8 d' f, B* ` ] coordinate_changi_airport * O1 s3 D, L1 K4 n' }' T& p5 H ] 4 |. ~( Y9 |4 J8 v0 c+ M9 V. }# R T7 y; s- h city_map.add_child(folium.Polygon(' f# l! P/ {3 @4 r, } locations=points_2, # 坐标列表5 W" a$ w1 C5 u R- P weight=3, # 线条宽度* w5 S. t) f" z1 P% ~ L" a3 ?- c color=yellow))6 t; ~! V2 G8 O/ J% O0 R 8 r$ V w5 _: `' C6 Z- B # 矩形 + X O o1 N B0 K+ X; k0 h f bounds = [ 6 Q2 { R* i, C- }9 G% l coordinate_ang_mo_kio, % R9 q( [( P6 @7 M coordinate_yi_shun 8 ?$ f! F: D7 V7 n! } ] & K) I1 b; e( o6 \ + @) i% w- r2 w city_map.add_child(folium.Rectangle( * C3 w) z6 u! U$ A$ [ bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) ! W3 i2 K+ \2 j# @5 g6 A weight=2, # 线条宽度 ; z' U2 L8 }8 |; k8 [ color=blue))8 ]( [2 L) b$ E ~5 w& l* e. F- J * v" e8 U* R6 F8 l$ v7 b; t9 d # 圆形, circle, radius units meters* U$ @! m. ?- g5 a8 X* K2 f6 G folium.Circle(; G2 D4 R. N. _7 m2 _! w radius=1000, . D3 e }$ Q' X) b) v* f location=coordinate_nus, 2 ]2 O# x, f" O popup="National University of Singapore", + N# L3 X' v$ t V+ J' m( l6 n color="crimson",; T& }# a+ ~* t* w, \% w6 c; \3 m9 t fill=False,) J% {# ~8 W9 u y ).add_to(city_map) / Y5 V' c; m3 J% ]% A) Y 2 r% B$ L' @4 D* f" [( G # 圆形, circle, radius units pixels & K1 K0 |: Q& l9 a4 }" i; f folium.CircleMarker(3 Q _1 @* P+ i. x, Q location=coordinate_ntu,/ G. Q1 c- u; h* \: r2 L+ ` radius=30,8 x7 b: w; R: g popup="Nanyang Technological University",7 a2 Z( c1 Q* a color="#3186cc",6 s! N% B$ n3 m, z! l( B fill=True, * ?* @$ k( n A5 T2 z9 @ fill_opacity=0.3, # 透明度( T/ M! U1 N3 A) Z: m3 F fill_color="#3186cc",: Z: J* @7 ?; E# T& \- a; k1 N ).add_to(city_map)/ @" r, O% R( U2 R8 Z; E; r) T ' B4 k' }, Q# ~/ } city_map
7 Y) s* ^+ t- g( ^( b R7 _4 o
Folium 中的画出各种形状

热力图

$ d' V5 z9 R$ o, c- Z% ~1 u: [

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

5 e7 F" \1 x6 x; i+ ^4 j8 ~( W2 Z2 M
import numpy as np `1 Y, l" T6 }- {9 a$ | from folium.plugins import HeatMap . J$ m* S* h6 X6 H! G$ d, s9 D8 w+ ^ 2 [% s3 D' x9 X7 ]8 S# |" r7 h* F- l # define the city map : Y$ \3 r: b$ T, ` city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) , n/ e! S j( m) s$ F5 J2 W4 ]+ j+ E # 构建随机数据' L0 F B% t( L# m/ E/ W+ {3 k, ~ data = ( 3 U2 r, D7 [) Q8 W' d5 ~ np.random.normal(; p, c! S9 Z9 K8 U! V size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +( B% n# I& d5 _# u/ ^ np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]). w z4 D; K+ o% j" F" Q ).tolist() 9 f4 E: k! h D6 |' e# ^- y6 T7 T$ `: ?' G1 `. K- V city_map.add_child(HeatMap(data=data)) 6 Q/ W; K0 J z; S city_map
?! g' W _9 w: E
9 d% @# T; p E, y# i

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

4 v1 [% e, a% U' `
import numpy as np5 O7 K7 y9 T2 t( @: t9 c from folium.plugins import HeatMapWithTime: R, j8 n/ [$ y! x, ^ & {' m$ V2 i' H( X6 c% ] # define the city map) W3 t4 P2 @" L. ~8 e @ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 2 ?- a7 ?( o; ~' ] b& F! c9 H1 D# J* y# b; \6 \* ` # 使用 numpy 建立初始数据 * y! S* [3 w6 V+ M& O initial_data = (np.random.normal(size=(200, 2)) *% D! q( Z3 v j np.array([[0.02, 0.02]]) +# @8 ?4 b7 C9 u np.array([coordinate_orchard_road])) + w0 |6 r4 |7 @" k * u: N+ c7 C1 r # 建立连续的数据3 \, k9 }5 O* d# t8 }* v) S6 d data = [initial_data.tolist()] * F- k; _ n F, ]$ N for i in range(20): # T! [1 f+ H" R6 b0 b data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()); K( m9 ]( a2 j# f' p8 M + e% }8 Y. y- v \0 H( ` # 显示连续的热力图 . \3 `7 f; b1 w6 U city_map.add_child(HeatMapWithTime(data)) * ^6 j" g$ w. I9 r city_map
" e% G2 D% A: o0 `2 H $ v' N) J* y- q+ `( A

经纬度点的聚类

2 a" m( M/ s# V+ k0 C' U

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

; A0 b1 ]$ n2 Z O% W3 O2 v# k
from folium.plugins import MarkerCluster8 j, D% b' a- d/ U# v+ v- }% T; F( F ?/ U b( N7 ^ E # define the city map , G- V [# ^: e% _& A8 ?1 m city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) / c. u1 M" Z- M& v + e( z. E. g* I4 l: y5 ` # 经纬度的聚类 / x) _& c* m, n& z4 n3 U; ? # 在 NUS 的经纬度附近随机生成 100 个点; 5 q6 H. H9 b) _2 w5 Z) ? data = (2 \# D* k. V& o, T( m np.random.normal(size=(100, 2)) : r7 J& u! P9 h1 s/ m- ~% o) l * np.array([[0.001, 0.001]]) +0 p# m7 C, z/ ^# l* g9 W: j np.array([coordinate_nus])) # x5 {4 A. n* v8 `7 m2 ]/ V% M: w# n3 y$ w2 n& a" x # create a mark cluster object 8 F% \0 @4 J8 S7 T) S# P$ W marker_cluster = MarkerCluster().add_to(city_map)( c! X* C& a4 x7 {; J/ F! V , C4 D$ I% U8 X. X: J # 将这些经纬度数据加入聚类& a# @" ]3 H( [+ _1 v& F* c2 A for element in data:5 O' s% Y( n1 W1 \1 t- d7 L folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)( s7 P7 u' R2 `9 f' w- h4 m) m " q: P! |3 V/ O* h$ S # add marker_cluster to map & O0 a- h: w; G; b- M city_map.add_child(marker_cluster) , S! E6 y; @' C- m; ?7 _. f, _, e/ {0 u6 W$ r # 作图, K9 o/ H# }, T8 F7 t% F2 k" c* U city_map
- Z2 e W9 p1 W; K1 u! i 4 |, X9 H9 J+ n7 h3 c" ^3 r7 E+ n; K

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

1 B! L; a7 I: n

参考文献

; I- J8 y0 ^) h) D

Folium 官方文档:Folium - Folium 0.12.1 documentation

2 o& d& j( J) T" p, Z1 j4 I1 x# k0 P2 u- D6 { 4 v/ b1 q V! D( ^2 d% f& q2 z( D + l0 c( w% ]$ V5 y5 [ * Y0 c- P9 @/ \7 @% d# |
回复

举报 使用道具

相关帖子

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