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

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

[复制链接]
/ V- c; z$ P* f* ?; K, g

Folium 简介

* U: j3 u) O: Z5 B* o q' R+ @

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

* S a7 G' S M+ h1 w

创建地图

. Q8 \4 h! `8 m7 a5 [, i" r6 \5 r

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

Z( H; v/ I5 j3 L. ]# a
import folium 6 X+ H8 E/ S9 h& U! \ %matplotlib inline ; G% P( c: w+ R: o- |8 E8 D. A {9 ` import webbrowser 4 n& X, o" b3 H6 _ l3 l' M5 D0 Z, [2 p- [& y. N+ [ print(folium.__version__) 4 n) m( ?1 N# G- \8 A* V, S8 t; Z+ n' W # define the world map( W& n' ?! w8 J" m world_map = folium.Map()/ B! S8 S. P1 X" C! ?/ t # display world map + e( w' t$ S8 D6 l; z world_map+ H9 R' X" t0 p5 b! D. b
% h! | w3 V$ n' t. ~
世界地图

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

* Z- a2 Y# Q* v- \8 B

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

[ S: _+ p _# P

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

" X) g+ R3 l$ A" H7 c

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

: ~. u C! e! P; K
# latitude and longitude in Singapore city' {4 B7 G, S1 @4 d coordinate_sentosa = [1.248946, 103.834306] 9 z: w6 T9 {! ~) z9 K coordinate_orchard_road = [1.304247, 103.833264]" i1 F4 P1 Z3 G( d coordinate_changi_airport = [1.357557, 103.98847]/ n" ?/ H0 a9 S coordinate_nus = [1.296202,103.776899] 3 J. E b, I" j" z/ s& t' Z& \ coordinate_ntu = [1.34841, 103.682933] * d8 l7 ~" K! `9 ] coordinate_zoo = [1.403717, 103.793974]2 ?, j, ^* F) Z coordinate_ang_mo_kio = [1.37008, 103.849523] 4 r/ n: X: q1 }3 n! u% d0 W# e( r coordinate_yi_shun = [1.429384, 103.835028] 7 e7 n4 {; T' \ Y4 ]0 n5 i* i: f. S, R8 z # icon+ b( T( P; D& E icon_cloud = "cloud"6 P5 ] x* J+ Y- h3 x/ u) n icon_sign = "info-sign" * W4 I; @2 w5 B0 C$ }+ M/ @9 c0 L7 \% T; K # define the city map6 e, E( ` L. e+ p r0 z* {# v # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} ' z: v8 L1 b% M' F city_map = folium.Map( 3 D: M# y. k& }* M4 q, ^ location=coordinate_orchard_road, : ~+ L* q- d, G8 R3 {3 h2 h$ O zoom_start=11,3 U) S @( f2 q0 o) Y0 [- F tiles=OpenStreetMap). B1 n4 P, @5 ~- Z# O ! y% a* {' e7 s3 I K1 B, J% P # add marker in the city map # v. q; L7 K$ {$ w; Y! v folium.Marker(0 {: u: k. ~0 B' Y+ y7 N% l coordinate_sentosa, 0 Y& M G5 U) G* C# X icon=folium.Icon(color=blue) " _4 L* F8 R y* p" h' k7 y ).add_to(city_map)8 G! h& h2 ]6 w! G8 I folium.Marker(2 Y" q$ A7 P- D1 B- E) F9 n! v1 ? coordinate_orchard_road, " Z7 H, M; ] x7 k8 B- w2 F7 M icon=folium.Icon(color=green, icon=icon_cloud)% x+ J5 {. E9 N' `' P" T" [3 T ).add_to(city_map)" H7 p' N; _/ h : p2 r6 B! _& ?# k$ f1 ]/ L # add popup 3 C$ V5 ]! Z' { folium.Marker( , O5 ?* s: ^+ V2 P# H0 ^ coordinate_changi_airport, 9 I; q; l: I3 t- Z popup=Changi Airport, h0 F7 Y9 }+ i4 C G3 f! C icon=folium.Icon(color=red, icon=icon_sign)! n' ?, U% f$ f6 n3 x, T4 {( D ).add_to(city_map) 9 x9 L; Q% f" q: n; a* U8 ?. t% L; O # add tooltips and popup ^, q6 J; U. C7 C2 c8 C: q- N tooltip = "Click me!" & s- W1 F4 F5 h3 m8 H4 H 6 P( K X6 ?3 {1 h3 w folium.Marker( a9 S& _. |3 }& B N8 j coordinate_nus,) }2 _2 x3 ~$ A% U) n popup="<i>National University of Singapore</i>", . {" J! u1 \" C# I tooltip=tooltip/ D2 t* A6 E! T ).add_to(city_map) 9 Q' ?! Z* R- g3 \ W- d. Y7 z) R8 q* Q3 z& P0 c folium.Marker( ; N' z8 a. I4 ?7 K coordinate_ntu,% p d3 [/ e6 {* ~' v popup="<b>Nanyang Technological University</b>", & N+ h$ A' z- H1 d, ^# Y: M tooltip=tooltip" j7 ?% N0 \" N ).add_to(city_map) " N2 Z8 @2 X1 c, d0 r- E5 U2 [# L8 f # display city map( r, M! m, Q9 B' i city_map
" o4 }9 c/ Z, }4 o1 z
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

" M( N. {9 f. K' ]- X( U( g! E
# define the city map) v1 }2 S: k/ N+ m1 m2 H" s. Y1 O city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 8 g/ \1 I! O7 @" ]8 U. v! B" e6 e$ n* a- E5 [" x # 在地图中添加经纬度, add latitude and longitude in the map when click- x8 M6 |0 a8 W% R- n city_map.add_child(folium.LatLngPopup())9 f, O5 W9 j% ?: h6 }0 f7 P # T* b; a: ^( L- c city_map
; ]6 u6 E, G2 x0 V$ u- y ' {7 Z% S1 J! `4 s5 V k6 V

几何形状

+ @7 x& ]4 I2 _. ?

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

, B# N0 p' o z; o7 u) J9 ]
# define the city map # e# e3 N4 @1 V4 G- _2 ?( e city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)9 K% D4 g* F+ f3 S & m' ?) J* k" R3 ] # 在地图中如何添加形状 8 \. f) B' a6 v; D# ? # 多条边9 o+ |( h4 P' T' G" C points_1 = [; a! W9 h8 L2 t+ t6 g coordinate_ntu, 3 o ^0 d3 Z4 w7 A% @' Z8 N coordinate_nus,/ g/ [! S1 \" _, `2 F coordinate_zoo$ ~! w6 b" q: H* q ]$ a# g( R) {( B . V* C {/ g3 p # 在 city_map 中添加多条边,第一种添加方式, @7 X% R, }2 L7 Q8 ~3 j8 h city_map.add_child(folium.PolyLine(: |' r) W8 ^" d3 E" `& }5 d locations=points_1, # 坐标列表 + y* i- b9 {, z; H3 E; {+ M weight=3, # 线条宽度 5 z- R: @! d4 B. A3 _; x0 t G color=gray)) * K7 L, w; t \7 g; |$ J * p% O1 D8 ^6 \ # 在 city_map 中添加多条边,第二种添加方式 5 U. f9 O# D. U* |# F: y folium.PolyLine( 8 W+ r/ C( E1 i5 u+ _- w' O locations=points_1, # 坐标列表 $ c5 V& s4 J6 z( ^ weight=3, # 线条宽度 7 p- l- d U5 s5 s3 J color=gray).add_to(city_map)8 ?! u" y# c% L4 O) a! W1 r* K / _8 {. M1 e0 a- u+ e$ U0 A5 } # 多边形* [1 u# Q/ t$ }2 `) m3 A4 A) [ points_2 = [ " R) y( T/ z7 A6 E8 d c9 j coordinate_orchard_road, U3 N* t. G+ H& a coordinate_sentosa, / [" J; j' {, w$ S$ I$ ` S coordinate_changi_airport) r7 S( E% |1 b5 A ]# u4 Z8 |$ D7 M5 y1 \. h* r 1 D; {3 `' R, P9 r city_map.add_child(folium.Polygon(: j8 z# F3 C. e0 ?% O& | locations=points_2, # 坐标列表 6 S# k! ~ D8 k8 l0 b+ p weight=3, # 线条宽度 - |/ M5 A% S8 l- N) x( |$ G color=yellow))8 {& z3 Y ~. j! Z$ @ 6 c, @: @0 J* M% F. J # 矩形 ! L6 i2 j5 l- m6 l/ u+ ] bounds = [ 3 O) k6 o9 C. N' f- B coordinate_ang_mo_kio, % Q! n! _, u% F coordinate_yi_shun3 h+ P* P1 P% u$ f/ {) ^* E ] 9 b/ p, T; _2 y- L" C8 |2 P- ^4 c& ~ c9 ]! F city_map.add_child(folium.Rectangle( 9 {8 N" B3 n, a2 A' N1 I bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)( u8 B( G/ a P9 z+ H ], t/ W weight=2, # 线条宽度 $ S- k$ ^3 c7 u3 F, y: n color=blue)). G6 b& e" F- g& W2 m$ C " F) n& p. l; Q # 圆形, circle, radius units meters $ s7 W; P, R1 V& t% n6 V folium.Circle( 8 ]" ]+ A9 M& m v radius=1000, ' o: q. \4 P( f+ V+ M) p- R) E9 ~" F3 d location=coordinate_nus, ' n( T/ S2 s7 Z2 X popup="National University of Singapore", ) D: c3 r4 I. V/ K% |4 p color="crimson",) l. H4 D+ ^# y6 `8 j M fill=False, 3 C( {- I: V; S5 C7 b# J( {/ n b. g ).add_to(city_map) 0 P c; j: y2 _7 | ; Z4 t# X6 m( q# ]# @# o # 圆形, circle, radius units pixels$ _7 _9 |6 d# N! ]( q( E% m folium.CircleMarker( , W g: Q! Z3 ` location=coordinate_ntu,4 R& m1 z( |" E radius=30,- |4 b' `/ {( y8 g( }. Y popup="Nanyang Technological University",+ Y; h" R, _8 {8 _0 @5 |/ U( N color="#3186cc", 2 L$ K/ G, [- w, Z fill=True,+ ]9 H, Z/ K' Q. m& F% L9 O2 v# g fill_opacity=0.3, # 透明度 ; t; d6 q7 q* ]& ~ fill_color="#3186cc", 7 z5 y3 R! U" D3 c7 l* J s& _" K' h ).add_to(city_map)3 w9 @/ L1 p# l - B* J3 d1 D2 V city_map
% {; q# {6 w7 J9 Q
Folium 中的画出各种形状

热力图

, A, D/ m. w9 n, Q" O& R

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

, y# ^' l: g7 }9 s5 v; D+ M
import numpy as np % s7 e3 T0 b$ W4 u; h% u# ~) w* f from folium.plugins import HeatMap 9 G$ G! ^5 W4 l( I& p$ Q! v2 A7 m' c z( f+ G; Y # define the city map ' R; c2 H' G/ a9 ~4 t; h city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)8 H5 v, P: D4 g) y ; |0 D7 E6 y) x9 ?. X! B/ M# V! E3 h2 H # 构建随机数据: z7 f: V3 K1 {( |: ~ data = (2 D0 a }# b$ a& h3 g np.random.normal( 7 v+ [7 }. U; q: c6 ~ size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +- `' @+ V6 a$ w0 L- ?# ^ np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) j: |8 e4 O# S/ O# B% } ).tolist()5 s" s: E! N$ n, @! E0 t " N' G* h8 i% i1 ?7 \# C! @( k) u city_map.add_child(HeatMap(data=data))+ j% j/ F( v1 |- U' G' n city_map
, o# p8 H1 \3 s. C3 w
8 L1 v3 ?9 N! Q; t, R

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

* X3 K, y- t5 _7 _8 i, Z) k
import numpy as np8 b. X8 ^* L9 ^1 v6 ]2 o from folium.plugins import HeatMapWithTime $ r# ]4 l, h" R ( s* v& }' Y5 Z9 M, M* a # define the city map9 t+ @% z& i5 v: A0 U city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) A1 Z& s0 x) s. _# [( T5 {, K- o1 Y7 E # 使用 numpy 建立初始数据 : v: ?3 T5 T7 j5 G initial_data = (np.random.normal(size=(200, 2)) * 3 o$ @( r1 h8 F, ?; u, `6 F# X np.array([[0.02, 0.02]]) + & u7 b) H( ?$ Q9 w0 _ np.array([coordinate_orchard_road]))2 C; Y& N5 G9 N% Y f5 [ ; c0 \; t$ n* g4 Z2 x4 ~* @* g # 建立连续的数据( Y6 l! O, J, \; p' e data = [initial_data.tolist()] ; F! S1 W# d; H8 d& {, a for i in range(20):- l1 l) y+ ~: C0 L data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())& J" t$ ~6 B' z \0 z + @7 D+ A0 o$ H# E) ?# A # 显示连续的热力图2 I" p* S) S8 O' a city_map.add_child(HeatMapWithTime(data))7 z W9 o4 G" G: J& c4 Y city_map
+ M6 j3 ^2 J" c9 y) N, L ' t! n$ O$ a# m5 I) H+ j$ _# |

经纬度点的聚类

e$ ?/ r \/ D! l8 V* H: e

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

8 Q$ S& E% S u P3 P
from folium.plugins import MarkerCluster 9 D+ I! l( R; s2 v # o" ]. w4 s5 g7 V$ R0 }# _ # define the city map j! e1 ~/ T. k0 y city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)+ n. ^" H% j9 p% t : v4 M- R$ e! ^ # 经纬度的聚类+ t" k# ^- ?6 ? # 在 NUS 的经纬度附近随机生成 100 个点;) [( P+ ^5 l. G) R$ K; m data = (" N- L" `$ Q* R) P np.random.normal(size=(100, 2)), G# j. q8 T* F% g- D! @9 F$ ~ * np.array([[0.001, 0.001]]) +3 b: P, m3 ^) x np.array([coordinate_nus])) {7 c6 _8 q) e( O2 f8 c+ U1 w( I# w' f6 `2 N3 {' H # create a mark cluster object % j, X; m9 R$ Y5 ? marker_cluster = MarkerCluster().add_to(city_map) ' g5 S4 A& r' R) Y) S! Q! c5 E \1 L& ^/ h) L/ o, T7 f # 将这些经纬度数据加入聚类4 X$ ~' k2 e5 l3 C0 r" b( ~ for element in data: , E" n: l$ j8 N folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) # J! j/ n3 Y- t' w, E- B & c' L! ]0 u8 s4 @8 i: B6 F$ l # add marker_cluster to map# y4 o+ v3 M, m& D city_map.add_child(marker_cluster)4 X, ?, v$ m5 N* u- M7 q0 {" ]4 _ " s/ Q( U T( W1 \" z, s8 F # 作图 % \# Y: S+ g# K5 ? city_map
% v! |; ]! r; S" x# j3 u8 x$ y 3 K7 R3 @0 q1 X# Q+ |# M+ r

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

% g- {8 ]2 E: s

参考文献

4 x7 _/ y0 A. Z1 ]/ q

Folium 官方文档:Folium - Folium 0.12.1 documentation

6 ~ _) l0 \2 P1 r6 E9 e 7 j) y/ c6 p2 Q0 | ) O3 S( A0 _0 \+ e$ ^( O, C / k$ c5 q( v) v6 F7 C: o- V A3 Y4 ]6 H; W4 W$ E; J; G1 {% a5 v
回复

举报 使用道具

相关帖子

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