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

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

[复制链接]
: x: K* k6 }! V: E' ^$ G, z! V

Folium 简介

$ v7 D) Y/ { y

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

$ H6 Y& H' |1 Y4 J: D* P

创建地图

2 ~. {+ a8 ^! _& ]( f$ [, b

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

! l$ c. H6 b3 N0 ?, P
import folium . Y' _2 e* I& e# A %matplotlib inline 9 @5 G8 w8 `+ Q) K0 Z, p1 K, o- R6 K I) H8 n Z" {6 X$ Q8 a import webbrowser: \) J9 f3 Z5 |' d , {% a2 U1 g" p- M( _; z8 i# m5 I! n print(folium.__version__), r4 }( p [% I3 q 2 I6 a _9 K& y # define the world map ; Y) H% O( B; a; B) J- t6 r world_map = folium.Map() ! M9 r: A" X: o2 z5 f2 h- O+ y # display world map ; W) D% x2 P, ~( s world_map# e' y1 K6 m$ ^" P; {$ I7 j5 U- ?" _
: C. {0 B6 @* k4 |& m) Z" r2 S3 Z, ~6 c
世界地图

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

; W* Z6 i3 A& ]- M

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

5 w+ Z3 O: z1 n2 G* @

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

1 I- k8 ?( p! U) k& I

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

+ ~- e* @) N; E" i! r
# latitude and longitude in Singapore city 0 g. |$ j3 P% ~) t- Q coordinate_sentosa = [1.248946, 103.834306] 0 r2 l2 h5 D+ @' t8 t t) C7 L- O coordinate_orchard_road = [1.304247, 103.833264] 5 ^) n) w p/ X% v9 g/ p coordinate_changi_airport = [1.357557, 103.98847], j& ~/ ]0 L j: a$ I coordinate_nus = [1.296202,103.776899] , W0 ^6 B5 ^, C6 ?4 L* ` coordinate_ntu = [1.34841, 103.682933]8 M+ e' H: p2 A2 c# c coordinate_zoo = [1.403717, 103.793974]: n$ K! K% M! l" L& S( I coordinate_ang_mo_kio = [1.37008, 103.849523] 0 o) |" q. ]- Q9 [ coordinate_yi_shun = [1.429384, 103.835028] 3 }: E+ n. {6 a& O4 K- i' _, F% L9 B" q5 S0 g0 ^ D3 f$ P # icon + c" I; t" \: b icon_cloud = "cloud"/ b$ q5 A6 ?1 p2 [1 u3 v$ u# t icon_sign = "info-sign"! y- h( M" c( ~; D5 ?5 d7 V 9 l( V+ h$ j! ` # define the city map ; O/ i* @. j; i& s- ^9 m. L* f5 A # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} / X( T+ {, k/ l% ~& h! Z+ t- F city_map = folium.Map(6 e, ]. T {1 O) V location=coordinate_orchard_road,# g* Y( i1 r& N. [* R& x zoom_start=11,# H1 P; ^" I! T% Q! B: H Z& Z7 Q tiles=OpenStreetMap)5 c2 h0 H& } k( {+ z0 @- { , g" u& H0 ^: V$ M3 P' E1 ` # add marker in the city map) o3 p) T, ^3 W# N N M folium.Marker( : R6 x8 N% `, W2 R% ~; y7 u4 [ coordinate_sentosa, 1 V, N7 x4 H! M. L5 d5 N6 o N6 _ icon=folium.Icon(color=blue) # L" ^1 Z1 ^' Q% P2 G) B" J( ^0 ? ).add_to(city_map)2 r+ Z! V0 t& @7 r! }* o2 ~ folium.Marker( ) ?9 z8 x9 ]) K, [+ P6 G coordinate_orchard_road, / i3 S7 @" |; @( L7 E1 H icon=folium.Icon(color=green, icon=icon_cloud)8 j& `+ C- I0 B2 W. L+ L& N ).add_to(city_map), q/ D% g: @' ]' ~ U2 ?* s) b4 i" t0 E! U" _& f # add popup7 G8 R0 o, K, A- b2 \+ u0 K0 p folium.Marker( : N- L4 W0 L# e$ F+ p q# ] coordinate_changi_airport,% T* \, e) ~; N3 p7 D popup=Changi Airport, 0 ]' z7 w+ Q6 H) v/ E5 o5 R icon=folium.Icon(color=red, icon=icon_sign) & u2 m: k* V; _- }/ G1 z5 ? ).add_to(city_map)& ]$ D. V S0 @5 P% Q 9 g1 V: H3 @) o" `! p0 o0 W # add tooltips and popup( i* C+ Y' [, K1 t; _' ]) u' c tooltip = "Click me!"" p& H& B% g- f2 O5 {+ l' ^6 T : |! f( w, D: R# V% a3 O# l$ Y1 i% \ folium.Marker(: f9 H) H: A: c8 [8 w* W! [ coordinate_nus,( B) H, |* d2 S, f( i8 V! u! v0 ~ popup="<i>National University of Singapore</i>",/ g; O! m, [* V8 v4 [5 H tooltip=tooltip: {* x* V3 K) f- p( s; H0 v: _2 k8 D ).add_to(city_map) 3 ]6 h6 i" T2 P5 d. O 2 \9 H; ?. j# |: a) I p5 Q2 F folium.Marker(1 Z- E ^; g4 J! p! l+ J coordinate_ntu,, f$ n# O8 y2 P0 H( T popup="<b>Nanyang Technological University</b>", |& Q- ~" q" H+ ]0 f tooltip=tooltip , d. L& x# X8 Q% o, S ).add_to(city_map) 8 ^% w- q" A9 u- Y + W5 y2 T% `4 T# {/ ] # display city map 0 x; c9 o( X& W( h( k$ E city_map
/ y' W$ b1 a3 `+ D, y4 d6 w* M. h
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

- W4 t9 E E) F4 F, E: X9 {
# define the city map $ J1 \% O, P$ t+ ^- I) _ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11): K+ { A1 m$ {( g5 o, }: | . ?1 d& d8 ?6 u. o) u [& V # 在地图中添加经纬度, add latitude and longitude in the map when click c' l' D k0 p' F0 K9 ~, J city_map.add_child(folium.LatLngPopup())8 K/ i M- x% R: J 3 ~' U6 \. \7 F( B4 g- O4 J2 W2 U city_map
& m5 V* z, m, H 0 D f/ j+ v* o: p2 T2 X2 N

几何形状

/ y- D% s' \( Y$ [7 e1 J% t

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

# J, y, l" U4 X) t
# define the city map! [1 }% f8 C) O: C7 a3 F city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)2 J& K7 n7 d' e$ _8 k) x5 U " Q5 I) t: b+ Y1 U8 T, w- q, F # 在地图中如何添加形状7 q/ e4 I( k3 O% Z # 多条边3 Q5 M1 \" z2 O points_1 = [ 6 @4 v. z. ^+ T. _ coordinate_ntu,0 ]: d+ U1 M3 W9 B coordinate_nus,4 V% [; A/ k; b; w. l+ i coordinate_zoo$ n3 }8 c* N, g# i d8 z1 b ] & Z. s/ ]" a8 d7 B( v/ p+ C8 L . @2 V% K1 c1 W/ E8 J # 在 city_map 中添加多条边,第一种添加方式4 Q1 Y, ?5 w6 y1 [4 |3 X city_map.add_child(folium.PolyLine(, d% q( Q2 s6 T locations=points_1, # 坐标列表9 p) @" I- L% y$ X- s weight=3, # 线条宽度 9 J d% V% l7 }6 i color=gray))- @$ i$ n. x8 @ R& ]$ W & Q6 o7 x4 n# p # 在 city_map 中添加多条边,第二种添加方式 9 J5 {9 a7 P# I4 E% l/ b8 f0 V folium.PolyLine( * `8 h$ m% S4 M- I( l8 J locations=points_1, # 坐标列表 4 @. s/ B" M p' H% [" F5 Y- y& R weight=3, # 线条宽度 : Y+ S& N9 ~7 h0 b color=gray).add_to(city_map)0 K9 D+ A' q: L; `4 e$ g . K% q# \3 `" D9 N7 S # 多边形 9 Q4 p! E3 ]% |- g4 M points_2 = [, j4 n, Y3 C/ C. c8 Z, j0 ]* [ coordinate_orchard_road, 8 h0 q& _& W: P: T9 t$ s/ Q coordinate_sentosa,0 \3 s( p2 s- H# J h, U coordinate_changi_airport- ]4 u' T- i! H$ b3 c' k3 P7 \ ]) o4 u8 u* v6 l H N) m % B4 ^; X& x" x9 b* x" @' g& H city_map.add_child(folium.Polygon( * ~. ]3 [5 X; C5 v; o: g locations=points_2, # 坐标列表 b* ?4 I, x [) h weight=3, # 线条宽度# t$ `4 V- J7 O9 m. ` color=yellow)) $ U) j& i; ?" b# p# U : e1 M! K" D; D+ t. ]/ R" g # 矩形 # g3 `4 F3 q: U+ l bounds = [6 Q! I" o0 l) u coordinate_ang_mo_kio, 7 T7 }- Z1 w, k7 n3 M7 V' d coordinate_yi_shun $ w R/ _* f$ _( O ] 9 }) G4 d1 X1 r: l! b9 f- w . ? ~! N* B4 I# Z% v) x city_map.add_child(folium.Rectangle( ' \+ K7 l: y0 ^# K( z0 y bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) + x$ x* o# {# _$ s" p' R/ {1 { weight=2, # 线条宽度- F' g5 A; S; Q" l w$ p1 e color=blue))4 N, g3 ?1 ^2 [5 T ) W M# h8 ?* \" e9 A # 圆形, circle, radius units meters 5 J4 F& | E/ j0 r% P; [ folium.Circle(( ?9 w' X4 [& z% }. U/ s( o0 L" g radius=1000,: E/ [; I+ D6 r! N; }0 W" P2 ~ location=coordinate_nus, 6 j. \$ e! d! B5 T0 o popup="National University of Singapore", 5 D! x, r( k' L( q" O# `; ^0 _6 t& R color="crimson", ' J& l6 @: r F! R fill=False,& Z1 g* L4 s( o1 p# r' _ ).add_to(city_map) ; w3 T7 k2 t' s; n U& K _- R/ e" I) F0 V # 圆形, circle, radius units pixels% n+ G/ |, n# ?* Q- C folium.CircleMarker( % n2 m& p6 A/ o7 x7 m+ o' j3 e! j location=coordinate_ntu, ! f6 `* N2 Q+ b8 |8 l( E0 w radius=30, / b R: L9 [' @$ c& x- f1 N, G popup="Nanyang Technological University",0 l! E; t. G/ \" m# ^$ H9 ? color="#3186cc",2 n( x7 b7 Y2 H1 F0 P fill=True, ) }8 @6 V$ ?! m7 f" C fill_opacity=0.3, # 透明度 : Z0 s& b6 ~; d fill_color="#3186cc", . _8 L5 M& a" G0 d ).add_to(city_map)0 \. ?5 c- `! r0 o) a 7 s, d; [; }2 H$ |/ l city_map
: r' @# G) _8 b( ?
Folium 中的画出各种形状

热力图

`: L5 ^% a& o& y3 w

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

3 o2 a1 M! U- b
import numpy as np5 a, T2 T" n0 R# D from folium.plugins import HeatMap + z% {) Y( a0 P! r8 h4 e" k6 Y9 S; L+ e" o # define the city map 8 e) U+ X, V6 K, |9 W* V' s* k city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) # ~# E# F" z# y2 A 5 q2 U2 W4 K1 l- n # 构建随机数据 : v; j+ a6 g, w6 X9 e* O data = (: Y* s+ ^; Q: n5 D# s0 l0 m& J* F) B np.random.normal( 0 m7 @: a3 F0 {3 {. o size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + % ^) o$ E' A6 w+ C' @) g$ A np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) ) Q" O2 ^1 B% S6 I, H2 b6 g ).tolist() 2 W. }3 }# U* ~ $ j2 ^' y" I/ K: I city_map.add_child(HeatMap(data=data)) 6 i( G' r3 w" X { city_map
& I7 Q$ _$ j+ x" g+ z- e' X2 I
% S* B" s% v! K4 {5 l

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

( j+ G Q" W7 ~
import numpy as np l$ ]0 |1 ?) U4 P from folium.plugins import HeatMapWithTime 3 u5 M. X* j! u5 o- x: k& o+ ^" g7 [- Z # define the city map " P% _+ [5 L% s: |5 c% j city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) ( ?* f$ @4 s3 c. _: Q0 [9 X. ~8 F2 g$ s! M # 使用 numpy 建立初始数据 / C$ t% ]& E- C# }# K- y initial_data = (np.random.normal(size=(200, 2)) * % K4 T5 C: r& ]3 K5 t5 r) E! {2 L* k( G% K np.array([[0.02, 0.02]]) +, d) D$ j, o; A) G8 A! J* U np.array([coordinate_orchard_road])); Z6 \6 H r2 |* i0 a3 {4 h. D' K ; z8 ?" u) C, s! B& E% h # 建立连续的数据 8 j" ^! o4 q, n+ L( A% r data = [initial_data.tolist()]9 @/ P* N1 V7 b/ U& i for i in range(20):0 x8 \/ X0 h) v3 r3 x3 S data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()) # Z5 n7 A7 i4 Z" x' M : j% Z4 f, d2 `' ]. K4 p # 显示连续的热力图 ' i$ i2 p% ~7 h5 l3 _+ s city_map.add_child(HeatMapWithTime(data)) 5 d; k( t1 \. l4 ~- X' p city_map
( Y1 v3 \$ d! e0 Q5 P+ U2 D ? 2 Z* y' p+ s3 D" ^! p) }4 K6 ?

经纬度点的聚类

' F+ d, H. K; c! a% f3 m4 R# c4 ^* J3 k5 ?

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

" y$ k7 v, s# E6 ~2 k' L
from folium.plugins import MarkerCluster - J5 f5 u2 `0 v; g8 n# ?* y# w! [ # define the city map 1 o- S6 R+ `5 q0 `" e: R9 w4 E+ R city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)9 h3 u7 f( B8 U# R7 ^% t $ B+ U4 L! y4 r! r. |8 `; j # 经纬度的聚类/ U& @. c0 Q! e' @# E! F, h5 e5 {5 ? # 在 NUS 的经纬度附近随机生成 100 个点; 5 H7 G9 v2 g" g: j5 n- R data = ( * c% ~% m; l7 t" Z np.random.normal(size=(100, 2))! u& H! y# @2 l& t' p) P * np.array([[0.001, 0.001]]) + & E; d/ F1 b$ H+ |# l. v- @- f np.array([coordinate_nus]))$ N& s( z+ k. J* Q) q) `4 }' \4 E$ s& w , l3 ^+ X" U' P- E: e8 p) c # create a mark cluster object7 ~0 l! p/ h7 Y" K marker_cluster = MarkerCluster().add_to(city_map)# N- c8 ~2 ]4 O' G - p) m# l1 s8 ]6 D* P # 将这些经纬度数据加入聚类 ) [* k9 P7 _3 M, g0 {* n for element in data: 1 x' {$ Z: I' g( ]3 ~! ^; d folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) 4 O) F; t* ^ \9 G) y3 `( @% j# Z+ @' _) q' g9 v9 P, L5 W( } # add marker_cluster to map6 K S+ n7 Y! X0 b6 ` city_map.add_child(marker_cluster)+ J1 \& A0 s. K; a6 t, ^1 N9 m ' O5 \0 i: D G8 ^6 B # 作图 - e1 \/ k& U! A: Z( H" R city_map
( i- @) [8 b5 b& @ * x. o" _5 C2 ]# H+ Q0 f W

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

$ {( S) o. M% {3 g, w4 l

参考文献

5 X# c$ s6 U' X0 Y. w

Folium 官方文档:Folium - Folium 0.12.1 documentation

# [# `, d- P: Z" @$ k# z- Y4 H5 S' z 7 Q2 s9 v5 S, `+ {' ? ) I) M2 t8 E. j# A3 M/ m0 {, m$ B% u. S# P) d9 u
回复

举报 使用道具

相关帖子

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