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

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

[复制链接]
' t0 Y: |. {3 D' V& p

Folium 简介

- b' X- W# N4 \# ^# Z2 e# ]

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

6 [; J E$ Q/ ?$ v* T

创建地图

( f0 |8 y* J0 O) z. p* f

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

! u6 H% h$ ]7 b( V, v
import folium- t! Q9 _# P" N, I$ u& c %matplotlib inline 4 {0 k; f8 t! H% ^* |* r# H* ]; D( ]( d- x# U import webbrowser & X) B( P3 d/ N$ } 1 h/ ?7 [* R9 w8 { P# w print(folium.__version__)3 i, _5 U2 N& [. ^7 ` & w+ @2 l' x7 P$ h8 }8 _1 h, X # define the world map) S* {1 T# g) ], h1 b world_map = folium.Map() ; v; b3 g. l' t" ^6 H3 f2 X$ `" b% C: ~ # display world map / C9 C4 [5 G$ ?) a2 b# [7 q6 F world_map$ T! D \/ O2 J6 [
8 x$ e1 q+ H, L
世界地图

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

! }2 E+ c4 O i# v* `) E' A

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

5 y- e+ V& }/ a' l* I0 O' R

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

. s; o$ r* [: X0 V ^" |

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

; s0 ^ b+ }4 U# D6 Y
# latitude and longitude in Singapore city# i3 M9 r4 c+ ?1 {% ` coordinate_sentosa = [1.248946, 103.834306] : ?9 X1 U# l* Z% O coordinate_orchard_road = [1.304247, 103.833264]- g( [+ \! ]7 U4 z$ M* ]5 }2 L coordinate_changi_airport = [1.357557, 103.98847] . _) W4 r/ |, a' s coordinate_nus = [1.296202,103.776899]8 X/ a& A2 Q; V7 q/ G coordinate_ntu = [1.34841, 103.682933] 1 ]' ]+ c, b7 b2 \" N' A/ [/ h coordinate_zoo = [1.403717, 103.793974]' p' W' e0 Q1 ?6 C5 x @, a+ c/ o9 Q coordinate_ang_mo_kio = [1.37008, 103.849523] ; I& v1 A2 h1 o) R" {0 s% x7 G coordinate_yi_shun = [1.429384, 103.835028] * U; o. [. n0 e, M _: L$ ~9 G 0 ~. N1 N+ Q3 S# N) | # icon5 m& e2 [* H- |9 K5 J icon_cloud = "cloud" % I5 e0 X& R" m2 O2 o$ Z icon_sign = "info-sign" 8 n* V& B/ c5 e/ S5 q+ D% ]" o% u0 e' s1 y/ d: N/ K) O # define the city map8 h$ X: S/ C* _' { # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}$ d' b3 {& T7 W! X- t; C city_map = folium.Map( # b% E x* `: \- I! u) z# s7 ~ location=coordinate_orchard_road,% W! C3 @" m# @) B5 G zoom_start=11,. Q- A% L( m0 A tiles=OpenStreetMap) 2 i0 Y% A8 _% C2 j5 ?% j( g; Y: V5 x9 m: w # add marker in the city map& }# G" X0 A7 } folium.Marker(4 j" J9 y- M. L$ s0 i: e0 a7 ` coordinate_sentosa, : @5 C8 W& c, K# r% { icon=folium.Icon(color=blue)/ B% e1 K6 a6 b9 t# Y ).add_to(city_map)9 r1 b ?4 E% `! J- P; t folium.Marker( 6 j; t9 q6 ]! L4 x1 b coordinate_orchard_road,! B: q9 a; |3 ^$ W! o" s D7 n icon=folium.Icon(color=green, icon=icon_cloud) % Y3 j8 h; B* I6 h! v ).add_to(city_map) 8 r, o# C A _; B7 K " W9 P4 K9 y$ L, w( }; g # add popup # J" v, J2 G% j# D/ k folium.Marker(. _- g1 ~) c$ g2 p coordinate_changi_airport, . @: K- x& F- Z; N, ` popup=Changi Airport,0 `) t `2 w) d c icon=folium.Icon(color=red, icon=icon_sign) 7 I9 Z( R5 J/ u! Y g( n4 D ).add_to(city_map)4 r8 ]" O& w. h# i 5 O. F' z4 W2 m1 J1 a # add tooltips and popup% F6 @, r9 E5 Y2 p9 i y tooltip = "Click me!"8 i* H2 ~/ g: X$ A- `) E9 t " m( D0 o3 Q- ^ i" F" F folium.Marker(9 l5 r0 S5 T5 J coordinate_nus, . Z% n: E* H9 H4 |0 ? popup="<i>National University of Singapore</i>", 7 Q+ B4 x( N# R' \4 b' u, Y tooltip=tooltip) {4 A5 y- Y+ {1 [ ).add_to(city_map) 3 }0 p( x& E/ \: Q" E+ i# E8 ` folium.Marker( ; y% G$ w6 D' p6 R% p coordinate_ntu, 5 C/ F: n! N0 Y' |5 g: } popup="<b>Nanyang Technological University</b>",7 p3 t: b( A% a j1 s tooltip=tooltip1 w5 D) M# i$ P) J0 K7 k$ D. k4 W ).add_to(city_map)2 Z+ S, Y4 v% ?" a- D ( R# z7 [' [4 d # display city map8 q# b& |3 h$ u$ t city_map
r+ D: C% h/ b: d- w0 [' B
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

$ M% Q5 U- l0 ?" n: F1 O! N; H$ l
# define the city map ' A$ B1 A% Q1 t2 V3 h3 ^. n; Z city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)8 Y) ?# [+ W* k( U ; @% s6 [* J! f0 K8 l% }' H # 在地图中添加经纬度, add latitude and longitude in the map when click 8 L3 E4 \2 k3 Q& B$ p* M& F city_map.add_child(folium.LatLngPopup()) 5 S5 U0 ]' s1 S9 ]- c L2 z% G& I! u5 _& w/ D city_map
( P4 J: t: S5 L2 p& x3 x$ a& D # O8 X+ h4 P5 `. J7 e/ u# H

几何形状

% o, K- K8 K2 M: w) j6 b$ W

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

$ }6 E# C) S9 B* q, I9 Y0 y
# define the city map 1 ^0 G2 i6 t5 Q: W city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11), l3 Z( r9 ]2 x" W4 F' \ k) I$ D7 G 9 D0 k S M" \% u6 h% q # 在地图中如何添加形状 2 ^7 S8 v) V; F# ` # 多条边4 g2 Y! j" @' g% C points_1 = [& L: J& u: v1 @ coordinate_ntu, ( f5 v3 n* [- D. p R: x* i coordinate_nus,2 l- ?6 F$ U3 O& C ] coordinate_zoo. n1 z, Q2 O) H1 ` ], t: y; k7 g; Z9 ?( L0 L ; f2 `- O7 m3 c9 l # 在 city_map 中添加多条边,第一种添加方式 * I _# O7 s0 G5 ?2 L city_map.add_child(folium.PolyLine(# h; D9 ^7 p+ n& C& u locations=points_1, # 坐标列表% I+ n; t2 u" T4 U8 D! d weight=3, # 线条宽度 6 h7 F, H# x) t5 x6 h) m color=gray))8 F* Z; A) _1 r2 ~5 Q2 C! B ) Z( i+ e( G; f& ^. g # 在 city_map 中添加多条边,第二种添加方式' J5 b* F2 @9 E* d" c folium.PolyLine(0 X2 c4 G7 P9 l4 F' w1 P% a* k locations=points_1, # 坐标列表- z2 P: I& _# H5 h weight=3, # 线条宽度 5 ~$ |2 G/ E* w# g0 {$ h1 { color=gray).add_to(city_map) 8 m* F: m a' _, S, b% C * e7 Q: r& {9 p # 多边形, m/ v5 t ]1 a& n* ^ points_2 = [ & J2 R' U% b! @! L7 g. m/ k coordinate_orchard_road,5 d, J. f1 ~! E4 o5 q coordinate_sentosa,! {7 h* J* |6 O- H coordinate_changi_airport% O9 ]2 n4 M9 S3 O6 K ]* N( E( m% w! O+ |' c1 ^' T + W% B {2 }+ T5 Z city_map.add_child(folium.Polygon( / Y; G2 H" C- N3 f7 K3 B locations=points_2, # 坐标列表 & j) i0 w% v7 |: t8 ^ weight=3, # 线条宽度 0 S" ?# x0 d. c4 i& o$ m color=yellow)) + k7 r! [6 K' k1 Y" z % O \2 n$ }3 @1 {+ q) J. A # 矩形+ o* }4 L5 A# M' V bounds = [" o0 h. `3 U$ i' o9 ]4 C- i coordinate_ang_mo_kio,- X% B2 k1 i u) v. N' _* J3 n! U coordinate_yi_shun- ]' I/ }/ N4 H6 R ] + ~' W E$ B9 C9 b7 I & `0 [/ M+ I# n city_map.add_child(folium.Rectangle( k7 r' B4 T# N2 \ bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)& Q, [4 o, g8 g weight=2, # 线条宽度 ; |1 K" N& a0 M' H L) }$ `- ? color=blue)) ; s, N6 G: ~; E/ @8 [( _5 k/ I0 _& R6 j* y # 圆形, circle, radius units meters % K- D9 U- l# f! x folium.Circle($ } N" S: r8 ~0 D radius=1000, - M+ x F, G1 w9 Z9 N4 m u- g# m location=coordinate_nus, ( F; ]7 g. y) @% W' @ popup="National University of Singapore",. ]: C6 T9 U/ v/ f; c# K0 Y6 l: p color="crimson", 4 [: T$ E6 `3 P, @4 R4 }* v fill=False, a( I9 n/ s- I' _) [+ g$ V ).add_to(city_map); R0 w8 Z0 q* _4 z% w . w! q" F; g; z- v1 | # 圆形, circle, radius units pixels ( p# R4 ~2 R) G- ^7 B# E) B% c4 a folium.CircleMarker(, L# x+ h; E& F2 x. V2 { location=coordinate_ntu, : U) u3 x+ m# k* u radius=30,% f u5 E' G1 J: j2 n popup="Nanyang Technological University", ! N3 B3 |# {2 J% N: N- N color="#3186cc",+ J3 ]+ g! S. M( f fill=True, , m5 K `! w/ D/ S( g2 t5 W C fill_opacity=0.3, # 透明度 # C+ v1 Y; a0 m3 t% U fill_color="#3186cc",3 T, U% A* e+ n) z ).add_to(city_map) : u- j! _# O# u1 o+ g$ g9 c 1 @# u9 e2 l% y, _; e city_map
% ? C3 O3 F8 |) B& l9 u
Folium 中的画出各种形状

热力图

) y) b2 a* K4 ^0 k" K

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

, n$ p( m/ ^3 }. a% Q, z
import numpy as np - @1 b% J! X" I6 _: b* o* p( y from folium.plugins import HeatMap ) ~$ k' y$ v* Q+ n! E, P2 u * V7 g' q% s, y/ C+ O8 O( i- u # define the city map ; n! `3 r6 e1 S. L% G& m' R city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) & l9 C* d. U4 D7 t" q6 V ^' G7 [! m; C; y) }" n # 构建随机数据( a5 |8 T5 {4 b" o- ^4 A data = (; I; @$ F" ~* T% @5 N np.random.normal( j. V1 k* t( b size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + 2 e) y2 |; B: r: F) k np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])/ K: w: K* ~5 i) q5 u ).tolist() 9 l! y4 ^& m! _+ _* K* x# g$ x3 w% O* W! ?6 t& [% B( [" y city_map.add_child(HeatMap(data=data)) 9 z0 c0 Z# U l city_map
# l8 o! @" V) }0 ?9 r6 E' h. G6 t* w
8 h6 Y T7 ? X

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

* z" v2 L/ P2 h' T8 N
import numpy as np9 a- [# q' [! ~7 P3 O1 D# n from folium.plugins import HeatMapWithTime' s- B* |5 L4 p+ S/ J * r! |% `7 U2 \" ~6 a9 w; |6 j # define the city map( v0 I7 u+ ^7 L+ u city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) V T$ I0 _ ^, {6 F / B/ g4 Y5 k$ T) X! T- N # 使用 numpy 建立初始数据 ( C& E6 t+ ], Z* }$ V! c3 a& G initial_data = (np.random.normal(size=(200, 2)) *1 v X9 l; l: C: M1 K, g- n* y; I$ s n np.array([[0.02, 0.02]]) +& z3 J. F. W2 J& [8 P np.array([coordinate_orchard_road])) : S+ G l6 z8 A8 z5 g! G/ E& s8 k" B8 A( i # 建立连续的数据 * [& X1 Y9 O8 Z data = [initial_data.tolist()]+ e4 i- e6 V$ o: ` ^" y- t for i in range(20):2 _! ?. h' M( e/ d9 I+ c: e data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())& Q1 E6 i6 p! {2 S 1 [! ?' k% \' g p8 X # 显示连续的热力图 8 L6 X, L. E4 s$ b Z city_map.add_child(HeatMapWithTime(data)) 3 s3 B. n2 ?' ^" c( K( o city_map
" I# A& D8 s$ b- R4 v8 m! o, v 3 L( \# G/ {4 q2 R

经纬度点的聚类

5 F6 W9 H$ {4 ~) K1 r

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

! P5 p7 k! q2 @
from folium.plugins import MarkerCluster $ _- H7 y, d4 i# ?7 _ ' J; w( q1 W0 N S # define the city map ~: L o8 {; S, o# Z. p city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)1 j4 x8 S) B; q5 ` " E" m- @) ^7 {% y' l y3 V # 经纬度的聚类 $ u ?; ?* w2 \( v7 Y1 e$ c/ o # 在 NUS 的经纬度附近随机生成 100 个点;" _% H; s" K% c7 O3 v- H M data = ( 4 b3 L, M& ?8 [- y; T! t o/ H i+ O np.random.normal(size=(100, 2)) j) I8 I- T w5 K4 L. b * np.array([[0.001, 0.001]]) + & C6 C- I) ]! \/ x5 v3 S np.array([coordinate_nus])) 9 c% ~% `# y% t' M& X3 R" }6 N% l; f # create a mark cluster object- K% `8 B' E0 _+ ]$ T6 w marker_cluster = MarkerCluster().add_to(city_map): M6 G/ e* N, V7 ^3 w* T0 K 0 }9 C4 F0 c$ T- b # 将这些经纬度数据加入聚类 & T0 X# W' O" g5 | x: k, u5 o for element in data: ) N' ~# S" x' ?! r3 l- z' } folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)( H4 `0 [/ S1 Q $ a( K5 J# L1 i" O2 B: e # add marker_cluster to map 1 X6 A7 E8 X" w, ? city_map.add_child(marker_cluster)0 P& Y, X1 z( Q% `: S5 T) z : {& h" W* \: A. y8 K" j" ^' U$ q # 作图: e j6 }: N* [, T3 { city_map
: G1 Z' W5 g! o+ G" U- b! h- `; g , s# u4 B2 x# `* P1 O+ f

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

6 E, y4 S' P* q- O# o* O9 L

参考文献

2 p U; |# k: t. S

Folium 官方文档:Folium - Folium 0.12.1 documentation

- W8 ?5 f5 \: a ! }( b& G3 F, r1 s ( ~" |0 W D" |7 T% a! f* W8 ^: d" c9 N5 r6 q , r- c9 U! c5 Y- R
回复

举报 使用道具

相关帖子

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