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

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

[复制链接]
9 J3 K- S6 y0 X2 a- @4 _: @

Folium 简介

, o. H' e6 r$ ^ z2 |

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

. o$ _! T) i: t) H5 g7 N# C1 p

创建地图

- O# w/ T3 E6 @4 d Q# `: z

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

{4 P8 ]6 H" g3 a; p. o5 c2 Y
import folium + Y* V" M8 d- J* `! `6 o %matplotlib inline9 p- I. e) U; ]% C" T$ l! J( T4 c% R . u/ V& G4 h9 Z9 _! N' ?( ~/ h; x import webbrowser. b7 l4 r* Q. ^4 F ) P1 R, _* \5 T& m print(folium.__version__)' k' J, `8 `: P / _* ^! E' X/ w' ?. V8 q K # define the world map - g% [7 C8 ?6 `: X- a) m world_map = folium.Map()* D$ r( b( }2 `! x3 r3 l, Q # display world map $ U0 X% D5 m) u world_map0 L! V" _1 n5 H% r3 x) t0 m" c* I3 L
2 M5 Q7 F! ]) X, j" `" D
世界地图

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

( D) `) r1 v _1 U! q9 e) w

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

1 d& s9 j0 [7 o9 u' E. _0 \+ l! f4 I6 ~

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

( F9 g3 B- S3 g% c

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

h1 g7 S; } U( T1 e
# latitude and longitude in Singapore city % o1 R0 U7 G* t( M" Q coordinate_sentosa = [1.248946, 103.834306] 7 p2 L3 ~) D' U) B3 U) p2 F, W coordinate_orchard_road = [1.304247, 103.833264]; t1 E. s8 b# r, n; q: ^4 @- U7 h coordinate_changi_airport = [1.357557, 103.98847] 7 s7 g7 a1 I7 j( m coordinate_nus = [1.296202,103.776899]+ W8 }: a% r0 h! T: R) g6 W6 N$ ? coordinate_ntu = [1.34841, 103.682933]: z# r2 `# g5 n. ~$ N* d+ a1 I# r0 @ coordinate_zoo = [1.403717, 103.793974] * J% E0 Y, N2 J coordinate_ang_mo_kio = [1.37008, 103.849523] r! D- r* s6 |; \: ?5 J coordinate_yi_shun = [1.429384, 103.835028] " R+ U0 \3 {2 D2 r% b2 O* E L5 [5 k( y$ F$ n- ^% W& P- n # icon 2 [; S3 l2 ?6 U w3 O icon_cloud = "cloud" 7 |* e0 I9 H7 K2 r0 w; Z" z icon_sign = "info-sign") A; u g2 r3 |8 l: S3 E : X1 y$ t. ]$ n7 i # define the city map) H2 g; m# k% t3 F8 m4 Y # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}5 T9 F1 q5 _+ i! i& r city_map = folium.Map(2 q9 k' m5 L8 E3 R location=coordinate_orchard_road, 8 H h2 W* P0 e9 Y4 [$ c8 p zoom_start=11,0 _. t/ m" ?! u; C5 s" H6 k9 t tiles=OpenStreetMap) 8 K( R% B+ K0 f2 z4 @! T. w# @. }: n # add marker in the city map ! u3 D) A8 H8 x folium.Marker(4 z4 f" o$ y5 o2 Z9 I9 l coordinate_sentosa,$ F! g, j+ X3 k2 i+ H8 o0 G icon=folium.Icon(color=blue) ( f+ E: Y2 T6 |1 s ).add_to(city_map)1 a C3 Z( w7 Q8 u) b }) X8 O* _- D7 {) S folium.Marker( 5 p ^7 C+ z! I( A coordinate_orchard_road,# m* I& p7 e) s icon=folium.Icon(color=green, icon=icon_cloud) 2 Z9 W0 ?. b. E8 n: r ).add_to(city_map) % L/ o0 q& C7 [ & x* @' q! r6 S6 _4 l4 y6 j# d G # add popup% q Z9 y, ]8 B folium.Marker( ! e8 S- O" D! G5 P6 b$ x& M coordinate_changi_airport, 4 p6 l8 l4 }- \: Y" r3 S1 _7 P) n popup=Changi Airport,! V6 M: \) W, U0 f/ g* Q icon=folium.Icon(color=red, icon=icon_sign)) z: E m0 r; _! @ ).add_to(city_map) ( R( z) b% C U ! D; e% O8 X3 F0 T R6 O! w4 J # add tooltips and popup + u/ R. e: j c/ K8 h tooltip = "Click me!"$ D0 r. V' n9 v2 x4 d / n7 H- [9 U' I* ~; u folium.Marker( j0 z5 [7 F2 E$ d; I coordinate_nus,- {# k. H6 v+ G0 Y popup="<i>National University of Singapore</i>",( w: Y) a# V& R U tooltip=tooltip , \* P/ j% H d! @, M/ R9 e& [+ k ).add_to(city_map)- k6 `& f$ Y1 `- K- Y 2 E' W+ {0 }# e K7 n4 @* q" ~ folium.Marker(1 h- S# G: @$ x- U: i coordinate_ntu, " \1 e& E) A& s popup="<b>Nanyang Technological University</b>", $ [9 k! t" w2 ^3 _# H tooltip=tooltip / x4 z6 c8 _4 k9 x% [& x ).add_to(city_map) {7 d* u. w2 V0 N0 g ( s, R7 e6 d3 s3 e q # display city map: p* a# c; n8 L, z city_map
) T+ |; ~; y5 r4 C
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

6 B: X3 Q/ y# [ s
# define the city map+ i* G5 [7 i0 O4 ]+ R! v L/ \ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) * @) s+ a9 c3 i3 u" I4 N' H9 y& s1 a/ p # 在地图中添加经纬度, add latitude and longitude in the map when click ! k6 M l" k$ O9 x n: E- c city_map.add_child(folium.LatLngPopup()) 1 O: k; g$ c _: i" `3 |2 o5 W+ [6 G8 } x1 R, A2 r city_map
, N, a8 W4 V: S. [8 b# l! N0 x* L : Z/ f' {; P% u$ q0 i7 ]

几何形状

9 h1 s( S% k2 g; k

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

6 y& y- h, C) a: P' J
# define the city map" d: G+ \# O. ], V% Q/ m city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 3 [+ |; j6 }0 z, F# Q. ?- g7 R. j. ]- Z6 ` X) A8 `1 w h # 在地图中如何添加形状 , a9 u ^8 ^* j: Q0 `3 Y # 多条边/ U! a) z* s% K b) b' ~' _ points_1 = [ 0 W- N& b* F& S, T4 M W4 } coordinate_ntu, / q! z" W! V* ~* k l: A coordinate_nus,5 i7 ]: [" g& T4 N3 o coordinate_zoo - O$ Q' H% y2 ^+ z& l. q ]* o, t0 {4 J9 o$ ~/ u6 j$ D) K7 A , e1 s7 ?9 u3 ?( p+ C) g# A8 D # 在 city_map 中添加多条边,第一种添加方式! A0 L5 A# Q3 o% L city_map.add_child(folium.PolyLine(/ M$ Q/ j9 g6 u. v( E9 h5 ?/ K locations=points_1, # 坐标列表 7 C* p c/ i" o- }6 q- | weight=3, # 线条宽度/ z- [# R7 w( ~ color=gray)) - |& {" x& {' \( G5 c* s* n: Y# j, z # 在 city_map 中添加多条边,第二种添加方式 ' T" m- X! D1 Y1 R8 q) ? folium.PolyLine( 0 Q' y4 C) `% j. \ locations=points_1, # 坐标列表' R, g* W5 S2 a. B weight=3, # 线条宽度 " h2 R5 Q5 w6 @. a+ @6 B% K color=gray).add_to(city_map) # O1 _0 Z0 `+ p; Y0 R4 ~. i% y$ c, V$ k9 I' B1 N # 多边形 8 d0 _4 x4 S3 O' ~& t7 w points_2 = [' @$ w7 m' b3 Z2 j- k coordinate_orchard_road,+ ] }- ^0 `- P coordinate_sentosa,, i% c3 f. \$ H, e coordinate_changi_airport( j5 k, A* e! Y& J# w% E/ p ] 0 Q, n f/ n u) [* }+ g* K - _, P0 H' t! `* d& ~/ m# z1 C city_map.add_child(folium.Polygon( ) d* M7 | g: @2 U; t locations=points_2, # 坐标列表 3 }7 l: F3 H) r+ ~ weight=3, # 线条宽度$ }& D. {" ^, o+ t/ \- @ color=yellow)) 7 d( c3 ~5 l' P1 P2 v/ B* l- M9 U+ x. c+ @/ j8 g # 矩形 7 \( R3 V9 h0 m* G& h' @+ w3 s bounds = [ ! P: Q% N3 B T' F5 f2 ^ coordinate_ang_mo_kio,/ k" R+ `- ^& ~- B- M; C" I coordinate_yi_shun a' D7 k- l2 \ a$ E. }2 J$ C; `$ u ]" R* F# b. q; _ 7 [9 j3 N# j9 Y7 x1 V* v city_map.add_child(folium.Rectangle(" T6 y! k$ u4 _% j# ] bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) 5 i' x' r( [. K- Z# |: ? weight=2, # 线条宽度6 E8 l' X/ F/ W color=blue)) + @9 Y' O0 H; Z6 K 5 s; b* |2 j2 n( x" d( ] # 圆形, circle, radius units meters . ]: X$ j. M, w' y) D folium.Circle(7 H1 U' p' w! d) b8 Z7 j; U" ~ radius=1000,; a) p: l2 I2 I+ n( `( e& l! Y location=coordinate_nus,! ?6 {' M* |9 ?5 k popup="National University of Singapore",7 A: [5 `7 t7 c% F6 }) T# c% s color="crimson",3 B& X) X) \* G: [1 b; T; {" ?( F4 r fill=False,& D! S- @' a% h& O2 Z2 S. a; R ).add_to(city_map) 1 n6 T$ ~& K: J' O% _5 @) M; }& o4 a* W1 `5 u% }& r+ u' x # 圆形, circle, radius units pixels 3 Q- o Z# T$ Q% n! h4 N folium.CircleMarker( + V; }! i$ Y+ ] b: z% W' q0 { location=coordinate_ntu, - s% C. Q4 o- U4 Q radius=30, 0 |2 a$ e6 a/ T0 L5 |8 L) @4 q$ F4 f popup="Nanyang Technological University", : d I; T" b2 K- g) t; v color="#3186cc",& a2 p* ~ ]: B8 F fill=True,# w+ I1 E' a: `! V$ l9 h5 ?7 K: w fill_opacity=0.3, # 透明度& w* g' A0 I3 s9 S9 n7 v fill_color="#3186cc",, V) }0 h6 f0 a: g. i ).add_to(city_map)7 s8 S$ {4 _/ ]- {7 u' p) ] ( X/ W r s3 c, e8 V* m city_map
3 s k# _+ W; [& W; I
Folium 中的画出各种形状

热力图

0 r* u; E5 y$ V! {

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

; n& @) D3 I1 A+ x. \" V( h% d
import numpy as np 0 Z- D2 d5 j$ R. @! d from folium.plugins import HeatMap - H& \; j4 u. d2 c0 t/ Z0 M- j" a$ | # define the city map& o. k( f% K k1 y, H city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)! i5 ` y$ @5 w3 M9 `0 ^ # m3 @ k; p8 u2 t. P; @; d7 y # 构建随机数据4 Y8 ~6 \% G" L) P data = ($ [8 u: Z: ~) L np.random.normal( 0 u; p* w$ U; H" F3 v+ k size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + . Y! [. ~# R; a' S7 `+ I' I5 ] np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])% G9 T7 p0 Y) G1 G. w. h ).tolist() # `# q8 {7 K; Q4 \1 m) Y) X0 e) ]% n! U) w$ M; M% W/ Z city_map.add_child(HeatMap(data=data)) ( q/ I$ P8 N' Y- u' ^5 S" s) o city_map
- m9 \7 {1 U: K0 `0 _
8 |$ `! w- v [6 \5 u3 O

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

! d+ ?# F% t; H& [' q5 d) ~
import numpy as np8 h% W3 N2 }) o0 I! E0 M, Z from folium.plugins import HeatMapWithTime( c, h+ R+ b+ U: Z {% b : G4 u" x7 Q1 c5 k& z3 h # define the city map : a) F6 Z5 e% E city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)7 g4 t* ^. n3 c: G6 P # r. ~/ J- Z3 z; j# \, c # 使用 numpy 建立初始数据- K$ J. M. f# r initial_data = (np.random.normal(size=(200, 2)) * " u$ h% `; q3 J# C& r' u% ^ np.array([[0.02, 0.02]]) +8 I4 W7 H1 `; l np.array([coordinate_orchard_road]))4 @8 n& f/ ~! ~% d2 h5 [ 3 r( F+ ` `8 z* ? # 建立连续的数据/ D# w3 n: ^* R3 { data = [initial_data.tolist()] . i/ u9 y0 `# @# y# W9 K$ Z for i in range(20): ! h4 X; j4 i5 X2 X" t( j data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()). a- W/ D2 l: x# `& l6 w8 x: ? + a, ]. G# {1 V& q- b8 c e3 w # 显示连续的热力图, ~4 r* N2 O7 r! l, Q$ h city_map.add_child(HeatMapWithTime(data))& a7 ^ q' x6 f$ a3 \ city_map
. G6 Y- E" ~% C* [- j 9 Q% |5 U" ?- h0 L# Y9 m) w% b

经纬度点的聚类

6 ^( J+ C5 p3 W5 X5 E5 ]

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

8 B8 T& l( D n% ~* N, U
from folium.plugins import MarkerCluster6 V5 |& v" o* |5 Y# U& o/ y6 D' J ( S9 ~. R$ Z( F, X # define the city map + F3 n2 W0 E5 X1 l0 o city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)2 W3 O* Q: P1 R# Y 4 I$ a9 z" k% M1 [! R% Z2 i6 @ # 经纬度的聚类 # x* v+ `9 }" ~3 b! B # 在 NUS 的经纬度附近随机生成 100 个点; 2 k; j8 E5 Y- e data = ( , K7 ?# H" s: c; J np.random.normal(size=(100, 2))3 C' R) n% e; i+ r% v/ q; F * np.array([[0.001, 0.001]]) + 3 ?# p1 \- z7 J4 B9 ^* V np.array([coordinate_nus]))9 j$ k* t# ~) d2 N1 c / N9 X! P& x" z- X( S # create a mark cluster object- V5 ?& q, X3 C2 _! M2 w, V! R$ G( y! A marker_cluster = MarkerCluster().add_to(city_map)3 s2 d% X* Z1 R) X , ?) {" J" J6 `9 t0 c% a1 T # 将这些经纬度数据加入聚类9 T$ i' K' V3 X3 T2 R3 O" {/ f for element in data:9 l3 q7 w8 T& t5 H3 K$ t! I folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) + \2 s) ]+ y5 }7 A# ~5 l ! J1 }4 I- M( I0 y # add marker_cluster to map 9 S! i1 q! _+ v5 M city_map.add_child(marker_cluster) 3 l" |; \1 {* C ; a1 L0 R1 i; U1 m+ ` # 作图, {/ V# N+ t, ]- J city_map
3 C ^/ g; q/ w; _0 v 9 ]; T* u' S; y: P% U3 _* ^- p

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

2 q7 ?8 e: ?3 T" Q

参考文献

" d) t# b( `/ E5 N6 @ K: [) K

Folium 官方文档:Folium - Folium 0.12.1 documentation

$ m- F4 u# T1 g6 M2 D 9 o" x) k2 P- s) G6 C4 F6 I3 L# A: M! T! b$ |! Z& B. y% u: L% \5 O: S . s1 H% Z1 G( J" W7 z( ^2 D7 o' o% f! J; b2 R
回复

举报 使用道具

相关帖子

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