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

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

[复制链接]
% _4 p/ h W9 D: j8 B# z& J

Folium 简介

. P( z. q! v1 P. @: _1 b/ y7 u

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

6 L8 P: m0 }0 w7 B

创建地图

( w7 W% ^( Q$ X

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

: \! _) k" T/ k$ R# r
import folium e1 @1 N+ S# {: }0 l" Y6 B( a/ ] %matplotlib inline7 e$ {$ s1 O) f9 O, c1 x 2 b, Q( V a4 x" B& s# I import webbrowser2 p$ T; L* [) S1 I- s 8 z8 x, E S; ?: k8 y& R- H5 [- a, L/ w print(folium.__version__)& f: N+ R0 ?4 ~3 c' R( B & z) H, L* m7 N! K) g- o # define the world map1 g) z8 G/ Z) d9 f* q- T5 Y6 t world_map = folium.Map()2 b! C9 d% w+ k; T: E # display world map/ g4 l: ^9 E( i/ | world_map . [* H- {# y' n9 S9 o2 y
' F6 v4 f2 h) l, V( y" N
世界地图

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

. }8 w- I3 f5 S6 w

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

7 Z6 i3 v. n" F5 P J

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

. u# r8 ^ U5 ^/ c/ `

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

4 R" g3 H3 e* \9 Z. H/ d$ ^
# latitude and longitude in Singapore city ; H$ `% T9 K, k8 p coordinate_sentosa = [1.248946, 103.834306]! I6 f: s' d0 m& Z' v4 H coordinate_orchard_road = [1.304247, 103.833264] ( C7 j3 n0 I7 |2 y2 x( i9 V. q coordinate_changi_airport = [1.357557, 103.98847]6 U. K: ]8 _4 s coordinate_nus = [1.296202,103.776899], ]- |0 G J0 V coordinate_ntu = [1.34841, 103.682933]3 x9 T+ O& [2 h8 @! m! o coordinate_zoo = [1.403717, 103.793974] % P& m& B( t6 b- Q coordinate_ang_mo_kio = [1.37008, 103.849523]8 z0 J3 r# }) F6 C8 G, B coordinate_yi_shun = [1.429384, 103.835028]+ k# t1 H# k% D( e* S" S , b1 e, p' e$ h9 S # icon; Y& B% l% j) A# R. ` icon_cloud = "cloud"# w. ~2 s0 E7 e8 W7 z7 _) D: w5 N' T. w icon_sign = "info-sign"9 W q5 E Q; i, W " t/ f- X1 [$ K4 y1 w& X) O8 ` # define the city map ( ]5 T; f' q: I # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}0 q/ p3 q% k3 w city_map = folium.Map( ! p/ ?+ {4 T: A" G8 f location=coordinate_orchard_road, 3 M2 j1 j7 y: m! f" B% R( k; {/ y zoom_start=11, 6 n0 S& b$ }2 k# N tiles=OpenStreetMap)+ I4 ?0 B1 ^1 D0 P + [( X" a& }; E; I # add marker in the city map ) F2 h" D6 L5 I L2 M \& R folium.Marker( E* r9 o. x+ }/ E# l coordinate_sentosa,* i7 H7 z! E5 E# F1 U icon=folium.Icon(color=blue) 6 ^6 ?8 k% m; V2 k" } ).add_to(city_map) ! Z) N! R: }9 t3 O5 d/ b4 w L folium.Marker( # [% u- N3 c! C7 q( ~ coordinate_orchard_road, , i# k& E( e" \" H5 _ icon=folium.Icon(color=green, icon=icon_cloud) 7 {' b+ f+ v, h ).add_to(city_map)* O; \* c }' x5 s2 I 8 h2 H4 W5 z @$ ]1 E ]" x- h # add popup 4 L) s0 k3 y8 g% x# D+ T folium.Marker(* k, t2 _& b" P3 [4 [ [4 R8 \ coordinate_changi_airport, " R0 b- t0 V8 R2 f popup=Changi Airport,) E* F8 M. T- l! {2 z icon=folium.Icon(color=red, icon=icon_sign) - v& K# { E# z6 T. w$ f7 C# W ).add_to(city_map) 7 n' l, _- U) r4 G9 t( Q2 w9 N 4 e, A6 I G" C: [0 L% e2 k' Q # add tooltips and popup 3 e; t) l& s n0 t; o5 s; F tooltip = "Click me!"- z& r8 g" G. |- p& ~" {7 M" _ 8 w$ k {. V* Q/ q8 ] folium.Marker(0 z8 W [& m) e* D6 u5 K2 k* D coordinate_nus, 5 G' `4 ~1 H8 x6 f" [( c" @ popup="<i>National University of Singapore</i>", - |) ?3 A- \: F8 g: o$ @ tooltip=tooltip : {1 C* S3 ~ i Q7 w+ t- K' [4 r ).add_to(city_map) / Q7 }8 P; t3 T, } + h* ?5 T) X( a0 z1 N folium.Marker( ( b$ L( L, r( g5 J g) o Z/ H coordinate_ntu,; A% g1 F/ x, x/ X1 | popup="<b>Nanyang Technological University</b>",& N5 j0 y" g$ [8 f& }2 u- z# Y) k tooltip=tooltip / u+ s9 w' _, V) j8 Z ).add_to(city_map) 2 G' M4 y! H' l6 q2 U2 b# B3 |- p. E, v, |# S+ |& h. S( k* j # display city map 8 u9 ^+ M8 Q0 y( [3 ?% _# U$ t7 A city_map
4 R$ O/ J! c- o6 d
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

, W- `( w0 i, x, \+ p; x
# define the city map9 V! t7 t8 w5 n. R, f city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) . X( P! j2 s, |% e" D: z; N* D 5 B a0 k- `3 I4 R3 c/ W # 在地图中添加经纬度, add latitude and longitude in the map when click 6 e( M1 q6 o4 H3 j& L city_map.add_child(folium.LatLngPopup()) - |; Q* {$ [3 r& d6 p7 q. T9 K* v8 Y* e6 W. a" [5 o city_map
0 r/ `- X9 ?% g' a2 q) l4 s ( V; Z% I( S( U- v0 T0 p7 K) Q, [1 i

几何形状

6 y5 }! G+ ` a( t' I; i

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

( b- L2 E9 d- ]/ n
# define the city map 9 N) c% E6 N* G city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) ; f& k7 L9 f. A8 `# G * j1 s8 z8 q) y" y # 在地图中如何添加形状" P1 m, v! g, R8 D # 多条边 ( k" `$ N! G3 |6 r% b points_1 = [ , Y3 R, S# P/ ~) g4 B: ~1 D% y coordinate_ntu,' n x/ j# } [ l8 {0 d coordinate_nus, $ E" }) q ^* b% o( S+ { coordinate_zoo. ^$ E' ^0 v G3 k ] C/ \* G- \$ i) s& \. q( B; v : X C0 D$ i5 |7 `$ w # 在 city_map 中添加多条边,第一种添加方式1 j8 @1 s8 `; A& s: ` city_map.add_child(folium.PolyLine(: W* y6 v: q0 g9 F6 `# y locations=points_1, # 坐标列表 % ]1 q/ O4 g$ j/ R/ ?& x weight=3, # 线条宽度 ! t* h! ], P2 x! y color=gray)) # y1 c* r0 Y# c2 A: q* G' |! ?6 t7 ] # 在 city_map 中添加多条边,第二种添加方式 / ]. I! h2 C" o; b$ N }! ? folium.PolyLine(* S0 k0 U+ D) y# D. P* w locations=points_1, # 坐标列表 : _+ `7 z# ~7 [7 J m9 A$ ?, C2 x! O weight=3, # 线条宽度 & D: c7 W, P l- B/ i color=gray).add_to(city_map)! G+ w, g4 d6 n, ?; y # _3 j w9 r) B8 m! u # 多边形0 o, K4 b# |7 @ y7 J points_2 = [9 O: c2 r" G0 k$ Z- l coordinate_orchard_road, & ?3 o3 a2 X! Q- o' A7 O1 l coordinate_sentosa,) w% Z+ t1 p% Y5 x coordinate_changi_airport - } z! }+ [- ~# s w( s& ? ] # a+ Y& L/ }8 x% }, }. Q B* l1 W' v! m : p* O l( ]0 ^: E* K1 L city_map.add_child(folium.Polygon(! k( B' @: }- Q7 ^ I locations=points_2, # 坐标列表) |( z! S6 T1 o J+ r+ ? weight=3, # 线条宽度 + [1 \7 ~& d8 k) I color=yellow)) 3 q: a- D0 l! {* u3 F2 _% n1 u6 }, H; k8 Q # 矩形; p( G) z: ~5 M/ m- ~. y b bounds = [ - ^6 Q. `8 g7 C) l4 @$ J coordinate_ang_mo_kio, # `& o8 z; R( ]) M* e: M0 } coordinate_yi_shun 6 @5 m% Y- X6 w# f, o" D ] ' z9 s4 d5 X w: ^% F7 L- \8 p- i8 k$ \: X city_map.add_child(folium.Rectangle( 6 y' _/ I, S2 }2 ]; T6 k' x e6 [ bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) " \2 _% j$ ^6 Y. @, S* W5 U* O6 H2 j weight=2, # 线条宽度+ N9 H, E# I; S% ] color=blue)) " \- I' _9 U- n0 f6 D) d% w- B9 \" t4 U" J( D, s # 圆形, circle, radius units meters+ Q2 n9 Y! r, i# _' {& E folium.Circle(5 l: V7 m; \; u, ^' e5 S. l) t radius=1000, $ W0 P0 G6 j+ I8 z2 w- P* z location=coordinate_nus, ; K8 x K- i& a- W' _- A popup="National University of Singapore", + ~: [5 t* O6 n% R1 K0 W( a9 D color="crimson", 5 }7 F" f. I+ D% [* X+ L- c0 | fill=False,8 ^% Z v+ } r+ r8 c D" {8 @$ }; ` ).add_to(city_map)2 B2 w7 K/ Q8 j1 B/ e ( A- }' k$ o0 g* m. @ # 圆形, circle, radius units pixels( E R! W: E* V" Q+ [# G- i. A) i folium.CircleMarker( 9 E8 a2 H& x2 W location=coordinate_ntu, " ?- t8 P5 z ~ _) a i' M9 A radius=30, [/ ~- ^3 K0 w2 Y1 ` popup="Nanyang Technological University",/ N" Z: |( [5 Z9 X& s l. f% e color="#3186cc", ! W1 ~: s/ y5 ^7 F fill=True, 5 a" W# S! C" _, `$ [ fill_opacity=0.3, # 透明度& H( R; C u% r3 S fill_color="#3186cc",- Q% L2 f+ U' |0 [5 u/ q ).add_to(city_map)6 O5 k5 s0 g; J& o! [5 G . G6 d' k, o& v5 D* R/ n city_map
8 ]# I: M' N# A' D, p
Folium 中的画出各种形状

热力图

. S' B: d' t [3 b0 r+ \

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

' O: _& S6 o- c. x3 u% P
import numpy as np# K: R1 Z. s* n from folium.plugins import HeatMap , A! @+ X: u6 H, s5 T( r7 @5 j: o4 X; T # define the city map / a1 P3 f4 L: R/ y7 a, e6 s# u city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)' g \1 O, y& M4 y 4 z, H( r" \$ O* @$ L2 L- g # 构建随机数据1 A. Y" y) |" t3 p$ o data = ( + ?& ~! R/ D4 t g1 K np.random.normal( 3 t( K+ G' @+ d U) R. f size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +3 `0 R" P3 Y* ~9 b' r) ~3 L# R7 t np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) 8 Y9 \; b! N8 G3 v ).tolist()( T2 m$ [/ @1 N$ U v" D7 w' T # F/ t4 {5 I; ^8 o( z! w city_map.add_child(HeatMap(data=data))5 |* C' O8 [6 Y$ g Y a! e city_map
; D# h5 K- P: r+ ^: u
, h# [" R# \( ] l

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

5 W1 p0 N/ \! u3 H5 r& j
import numpy as np% ?9 a6 a6 _, G! ` from folium.plugins import HeatMapWithTime% D! s, Y% E$ ?- m , i9 v5 O R+ ?( G% ?7 j # define the city map 2 J9 @5 U: E/ G* R$ _ city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)0 J# w+ K# a: w+ h" t+ ? 8 Z" H) V6 U' L8 G7 G. Q. o( C# T% } # 使用 numpy 建立初始数据. p) h1 S1 H& ~; V4 l w initial_data = (np.random.normal(size=(200, 2)) *- _# V$ |. A- e* o t9 R8 t np.array([[0.02, 0.02]]) +2 ` s' \6 H( ]/ {8 H; ` np.array([coordinate_orchard_road]))- t/ f6 v6 w" u$ S ' k; F! z, J( v! l- b/ ? # 建立连续的数据 0 f& {5 h! e( z# `4 E4 A3 M/ ? data = [initial_data.tolist()] ' ^( ~. a7 b# q3 _" J# n' g$ d7 T for i in range(20): ! z4 @9 _' F& N% ]; i data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())! A4 E5 u- C7 E - L0 V+ X9 M# G# Y$ X+ D- w& B # 显示连续的热力图 1 F, I5 o0 Q/ H8 d1 G& _ K city_map.add_child(HeatMapWithTime(data)) - s5 {0 h' A; N! O# }5 k% k9 X city_map
8 z6 N0 a6 b9 c/ v J8 d3 d7 @! X3 V

经纬度点的聚类

/ s' {6 C6 `" g* r4 F

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

4 ~1 C2 z+ j, K* {
from folium.plugins import MarkerCluster ) b) d3 f" _- r( g! Y" k3 p+ c" `% E! n* H6 v% l4 R # define the city map ( n0 ^+ o7 ?) ]# ]0 s city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)$ W& Z% _8 `! |/ [6 C4 o * g, p5 t2 F$ ] x # 经纬度的聚类7 d! X! P! ?$ C # 在 NUS 的经纬度附近随机生成 100 个点;/ V3 J# f: F1 v, p. e data = ( & V. \% j! U+ U. q np.random.normal(size=(100, 2)) ! f3 K( ?8 o( T0 m0 n2 Y * np.array([[0.001, 0.001]]) + ( E% n, k7 m1 C V/ ^: p5 B) u+ M5 u np.array([coordinate_nus]))1 b& N( h) G; H. C 2 S% b; M7 w; O7 S; n # create a mark cluster object: p9 p9 m( O O" i marker_cluster = MarkerCluster().add_to(city_map)2 U) e* j; o) v, f ; @9 i; s% n/ q# Z9 l6 [# z # 将这些经纬度数据加入聚类" O2 C6 O& r8 E! a( L for element in data: - r; R; p& ~1 _2 W. b/ r folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) + h/ o7 R0 ~- u" g4 ], N3 Y2 G9 g5 P7 O& P, V& Z$ F: ? # add marker_cluster to map 3 e3 k' B4 G2 U* s# v city_map.add_child(marker_cluster) 3 n b" K' G) v7 z" T) ^- q6 F E* P0 E2 A' Q" ?# E) N" v# H # 作图 - e; J- N5 j* I city_map
# j. J/ o5 K2 k4 } I ' J1 ~( k j) C7 g4 o8 O

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

/ i4 l/ e$ Y6 x

参考文献

' q! v3 g+ [* W1 @; y3 ]9 i

Folium 官方文档:Folium - Folium 0.12.1 documentation

9 e4 e' A5 _( @8 o 4 q2 \* a3 e; U: {+ Y( ?: g6 o* k: ~( d' B F/ E 8 }. f$ e) a( B1 D) e6 K% }# ? a$ |9 c* B$ V1 c
回复

举报 使用道具

相关帖子

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