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

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

[复制链接]
# B3 G$ q1 \( `3 [

Folium 简介

& }& H+ W& J/ z' H5 n! O

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

4 P# K, E6 C$ Z" s( _& W

创建地图

6 `/ S" ]6 x' e/ z0 e, s

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

0 o9 @, W: \! o* f
import folium & h1 P x+ z& B- g' U9 M %matplotlib inline/ X, m- ^& L! }7 n- j # o; ^* ` x, e: ^$ I2 R5 A9 P import webbrowser7 n z1 W5 I3 q, o% l 2 X$ G% A" {3 Q9 W& |$ ~% b" j print(folium.__version__) 4 F; r5 v% ^" a* Y$ m2 s/ S: k# b% |: j: J2 w # define the world map / D D8 P# a) B world_map = folium.Map()8 ?( N1 a8 `, g # display world map, C. n8 t1 E( X$ _ world_map; L1 X2 \6 d6 [, J# t( ~# m4 h
; ?! x1 J& u) u: X
世界地图

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

$ z8 @* M5 }% j

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

) k8 H% H: p( }, D8 a$ a3 P1 M

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

, m; F# M; k8 |2 \# _3 _0 y

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

1 k; l' U9 D" {; S
# latitude and longitude in Singapore city9 Y( ^( Z" W% {: c$ F coordinate_sentosa = [1.248946, 103.834306] % P" Z2 J6 ?2 W& B) A5 J* \ coordinate_orchard_road = [1.304247, 103.833264]4 D1 g0 o0 I; s1 B( T coordinate_changi_airport = [1.357557, 103.98847] ) Y4 H6 k. K/ d k9 M( {, |- _) y coordinate_nus = [1.296202,103.776899] * [/ X: r3 S4 Z$ E* { coordinate_ntu = [1.34841, 103.682933]* Y3 a* K& Z/ i9 Z' h coordinate_zoo = [1.403717, 103.793974]9 o) p' t5 T1 c- d o- v7 I, y$ I coordinate_ang_mo_kio = [1.37008, 103.849523]$ d% L" g, Q. c8 E/ D! b coordinate_yi_shun = [1.429384, 103.835028]8 h0 R3 l2 L+ E, Q / i' X, x& G6 a) \: V! ~4 r( t: u- [ # icon 0 Y# g/ R% X, y icon_cloud = "cloud"6 _, ]7 }. `# j. X icon_sign = "info-sign"+ P$ s8 s' @+ h) ^ w1 q; Y t " ?3 @3 x* |. ]7 w5 j& X- V; H3 A: W # define the city map, Z' T5 S4 H- p0 X( V0 K- ]$ R # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}" w2 A, z8 g: W city_map = folium.Map(* V0 K7 y! G. d/ C9 h0 l location=coordinate_orchard_road, 9 c5 t' F- ~# e& u4 U% o6 e# w; { zoom_start=11, + P s4 K; j! L3 }/ r tiles=OpenStreetMap)4 N7 F& u, F; y' G: W" ~ & F4 m9 {' R* V6 o3 p) e+ b # add marker in the city map% o9 }! Z, Z6 [) C folium.Marker($ k& J5 e. I0 \# v* I coordinate_sentosa, 0 r( ]2 b4 q! t9 I, t+ H& N4 J icon=folium.Icon(color=blue)' l% F& s+ P; B7 v/ q ).add_to(city_map) / W% i! j6 L2 w: l: O folium.Marker(- s2 F# F8 r. N4 G$ E coordinate_orchard_road,0 j$ i9 E; {& Q, J icon=folium.Icon(color=green, icon=icon_cloud)( b, V* G* D$ ]6 @! S# @ ).add_to(city_map) 6 ~5 \# g2 @0 U# \" M0 @, X H! R! u$ {- q9 [* b6 v # add popup6 U4 n! q/ e" p) J4 O( L+ f folium.Marker( $ R: B5 k+ `( |% J coordinate_changi_airport, 5 Z3 k! a. `2 N6 x popup=Changi Airport,: l) \8 p8 A" M) {% k icon=folium.Icon(color=red, icon=icon_sign)8 _* j6 N8 d' B" Z K( B* C6 Z ).add_to(city_map)7 B1 n: N% y( u- X" } s ! U1 l4 w4 [ V5 t' w3 W$ @, B # add tooltips and popup + u8 V+ x4 B' T# z) d tooltip = "Click me!"2 M* X7 {/ f ~$ T% z3 h9 Z( L" F ! _- D7 |5 y" S# t X2 O folium.Marker(3 t' h4 Z1 ]& f$ W5 N coordinate_nus,; t9 O8 N" L6 |( j) f$ f% R popup="<i>National University of Singapore</i>",- P) D u( G+ y- A0 T& K tooltip=tooltip 8 q( ]7 ]2 N6 |% s, ] ).add_to(city_map): ?6 [1 {) f$ G) y. I4 E- ? ' [0 Z% y$ Y! D7 z) i! N folium.Marker( ' @( S6 T$ j* d" `1 I coordinate_ntu," I7 x% W) Y* { popup="<b>Nanyang Technological University</b>", u8 H; x1 ~: J6 }. Z7 ^1 n tooltip=tooltip % V1 J6 v) ^* @) j. w; H0 U ).add_to(city_map) ; ^3 ^' W' ]- c* c& ]. @8 [ o& H' M$ q! m0 o9 }' F # display city map: T$ a" G$ P' G" e$ _* ^& h city_map
- C" n5 Z( R/ `* o) {
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

8 _5 _& l, \6 c* j
# define the city map( s$ F& d0 v2 ~5 E1 O* Z8 r city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)4 k3 J% @9 ?3 ?- W! h/ ~+ l% u $ g" z0 t; h' d5 F4 o* R" R2 c3 a/ t # 在地图中添加经纬度, add latitude and longitude in the map when click / a _/ \& B! y. E z3 l7 L4 ^ city_map.add_child(folium.LatLngPopup()) 2 ] Y: M8 l! A. q5 u& `7 T- b: B/ a4 y7 G- ] city_map
, H8 e2 B( W. {$ ^; e 0 B- A( U8 \) U( d; J- `

几何形状

5 u( Z6 _4 e1 U7 K( F; Q. {+ k* n

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

, Y1 Q# k' F# J; ?+ L- @4 M( y$ E6 t/ v
# define the city map + C& q* O/ M3 p/ h8 c city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) , A; P0 ~: E7 l" V+ ~ 3 {: L' N7 _2 a6 q& N # 在地图中如何添加形状8 J! H B- j, z; Z: J4 i2 T0 o4 m8 O # 多条边& E& _- K- d- _3 N6 `: P points_1 = [5 ^; e P6 a8 H! Y, ~) F5 C: d) c coordinate_ntu,' u0 u1 I+ Z# d. P1 Y$ G% j. B: e# ` coordinate_nus, # y( W2 z7 y7 C/ V coordinate_zoo & h: n( z( n3 A8 K ] 4 F5 C7 b6 p* F5 g/ e' ~% C1 o# P, n) b ^ J1 |; I/ w. h # 在 city_map 中添加多条边,第一种添加方式 ; w6 ~- N5 H' A/ f2 A city_map.add_child(folium.PolyLine( , `+ ^5 ~4 [- J4 S% v% U locations=points_1, # 坐标列表5 }/ A' n- B; P/ u. X# W* M3 W3 \ weight=3, # 线条宽度 5 ~; D& R6 w) a9 { color=gray)) ( t; W3 y0 D0 _0 J3 J. \( B1 Q- E , D- L, t H7 h; Z) R: U. K # 在 city_map 中添加多条边,第二种添加方式- q* T/ L: `" E P6 z folium.PolyLine( , M- s* B& B7 e) s) k locations=points_1, # 坐标列表 y& |! E' y+ P. @ weight=3, # 线条宽度 + ^/ E- D1 @ I; S color=gray).add_to(city_map)( G* x3 i. [6 |( V! r5 Q2 R 4 H' @( g, n- Z# S" @2 ~5 d # 多边形 & ^, m4 O& J. L! q' U. z+ A' A points_2 = [( E; |" Z% e+ @2 q coordinate_orchard_road, , K: T z8 G1 ?& I3 n coordinate_sentosa,7 g/ e% A- u" j/ S2 R8 v5 l coordinate_changi_airport & ^2 h0 l' e# ]/ C& z2 I2 g ] " L) E' [; S4 c& [! | ) ]% z# j1 V ?3 u city_map.add_child(folium.Polygon( 6 f1 _( U& f+ G/ Q1 ? locations=points_2, # 坐标列表& N6 q- B- e+ h |) p8 ?. Z3 j weight=3, # 线条宽度 1 x6 g8 x m0 [: c% S# ~ color=yellow)) / H0 c t+ W# w$ ? `2 E1 |% ^$ }# ^ # 矩形3 K; V- n2 n) v$ @" f+ Z8 B bounds = [ : `6 v! c4 x9 |' M9 K coordinate_ang_mo_kio, ( h+ K0 y! R, Y8 `4 r( ? coordinate_yi_shun# m7 e0 M- x/ E. e( K3 t ] * @1 L, u2 g0 c( y4 { + h" v4 q" l, S7 F; F. Z city_map.add_child(folium.Rectangle( + i% u, V: t# L7 j+ m# z bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)6 ]7 [. M# U8 o& Z* w# f/ z! a weight=2, # 线条宽度" V) O# o8 ^2 _ h) z; c6 ` color=blue)), j5 q6 r5 `, C! P9 b " P6 j$ [& U+ g5 T # 圆形, circle, radius units meters& G$ r: Y, V: P: A7 m$ @ folium.Circle( : t2 A4 P* b# ]7 g, ~ radius=1000,2 A9 `5 C0 j0 _4 p# l location=coordinate_nus,- C* L% w/ h' n+ S" k* S, M popup="National University of Singapore", 3 c, r# V6 ^( ?1 R' m# ~ color="crimson",4 u0 w/ T3 @; ~" r( Y. P: w fill=False, 6 k6 G/ |- O3 J' E9 c ).add_to(city_map) + c: J: c! x" J* o. J+ W) h' ?$ i0 I( d0 `" o( }; X # 圆形, circle, radius units pixels( y, Q9 u( t/ o& H# J/ b/ z8 r, _ folium.CircleMarker() T+ V$ x, v2 p5 c& c location=coordinate_ntu, / o' L+ x2 g; V radius=30,, _ g8 W4 B7 Z- n5 Q; T$ Z popup="Nanyang Technological University",/ T i' K0 E" Y! o% M color="#3186cc",9 ~& s: G/ q2 H) \% H fill=True,5 z' r6 o* H+ R0 }' f1 n fill_opacity=0.3, # 透明度 2 j5 \/ @0 h- H( m1 H fill_color="#3186cc", 1 c8 q* x& G# d- t ).add_to(city_map)! o% p$ J- f% R3 K; s' a + k0 d* {- Q4 i; p$ T! V5 Z M/ K$ | city_map
! k, R3 Q/ D3 y4 F. i
Folium 中的画出各种形状

热力图

! x% e5 `7 ~( }0 @6 W/ G4 C- z

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

: C$ H3 y* v7 @5 X7 _3 _% h2 j% T
import numpy as np $ h8 D1 I5 c- N l3 h3 [: Z# O from folium.plugins import HeatMap 0 n2 x# |" m- G/ Y 5 n4 ?8 C+ [; |* w8 p # define the city map 5 }4 y& H2 s& i o' |/ U city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)& @8 M/ a @2 W% R7 `4 G9 A2 M; o1 @. P 9 k; E% `; x. y9 f& l0 V7 @ # 构建随机数据 6 s7 c3 V7 F3 Z H3 e2 s* I/ Q data = (0 X. M! }, c$ S! U" W& z6 K, i np.random.normal(2 u+ E% S6 c2 D# n size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + 6 a! s. P& d: |& ` np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]) 2 V! ~- V/ M- |) z# G% t ).tolist()+ V4 r0 n# r+ ~/ F1 f7 v/ r - z7 a3 | B2 Q+ U7 g/ f! c4 s city_map.add_child(HeatMap(data=data)) ! V1 r& d, v7 \8 {, ]0 Q city_map
! L' V! q4 A2 Q2 C9 x
4 j0 k$ |1 o$ U9 z k

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

B. F$ f a8 _
import numpy as np : b/ \* }& Q6 W' b* T from folium.plugins import HeatMapWithTime 7 Y& F& P4 ]) D3 H+ y- Q/ R! ?) A$ m # define the city map . Z( I1 }2 B4 |+ J1 x city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) : P" _' ?# \% e1 }" O- Y; X5 J6 h4 G 1 S a$ `+ z! X d9 T # 使用 numpy 建立初始数据 & C# ]5 `- b% e/ s; U9 Q( Y initial_data = (np.random.normal(size=(200, 2)) * " |, o( }) `$ k/ g6 G5 ] np.array([[0.02, 0.02]]) +' d* z q& i2 X# g8 l2 l& h np.array([coordinate_orchard_road])) , b1 t, y) U% ?9 l" V; f9 _: u6 z8 U4 M # 建立连续的数据) p. t. p$ U/ o1 M data = [initial_data.tolist()] ; t9 E, k9 K O* Z4 Y# Q% C1 l& ] for i in range(20): 0 q: Z9 ?, z& v0 ~! V data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())1 n E$ p' M$ E% {4 @0 l) [ 6 O. o9 x9 v# W% r. W+ V( @ # 显示连续的热力图; R# z! l) J0 L' N' } city_map.add_child(HeatMapWithTime(data))9 n, j5 a& S9 t% Y, C city_map
5 W) Y% v* v9 c7 z% H8 Y- | - M7 x! E- N6 F1 [" ^3 u+ s

经纬度点的聚类

$ u9 q/ w8 y+ S

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

, w& Q0 N+ l+ X$ u- B5 B
from folium.plugins import MarkerCluster* ]; `! H. Z5 d: L0 n ' C/ {# A2 s6 q2 I+ [1 A # define the city map: g6 \4 N d4 b8 c7 U' D6 D$ I city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)% w8 t7 U3 h- W, |7 B# H % r' n) @! I6 C! I. t( I3 { # 经纬度的聚类* x& ~+ w$ Q6 G # 在 NUS 的经纬度附近随机生成 100 个点;5 d9 k& n9 @+ |( K data = (; c7 l# I% l, G! k. m np.random.normal(size=(100, 2)) 3 D& V1 E" m' w" V * np.array([[0.001, 0.001]]) +6 t" Z, h8 Z2 d" \& k, L9 W np.array([coordinate_nus]))- j9 p! d2 W# M3 _ 5 K: q: |' T- S9 c* H. q4 J # create a mark cluster object: ~7 ]! {( g% B marker_cluster = MarkerCluster().add_to(city_map) 8 J- B/ S1 u6 ` ' V; h, [/ ?# X% b6 }& Y8 O* y # 将这些经纬度数据加入聚类 1 H- y1 i4 z4 _ for element in data:2 F7 z6 k4 U0 h0 k/ N7 A folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)0 n0 ]7 G# `( \' K' ~ / N8 f2 ^8 h N- S5 F # add marker_cluster to map 6 q! w* ~4 S y8 u( y city_map.add_child(marker_cluster) 1 z P% t- x8 H# v8 y+ x6 p 6 g( n5 n7 l* U. W$ q& A; Q8 b6 s # 作图6 ?, `5 I$ t! K1 L$ x6 \4 m2 N' b city_map
. Y* U- `4 |: ?8 r2 Y , y$ a& i3 d7 f* ~

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

5 r. j6 t9 g1 @. w% y

参考文献

& C7 Z0 l% b5 a5 [/ p8 L

Folium 官方文档:Folium - Folium 0.12.1 documentation

. o+ ?8 A8 Q+ o9 U- [ ! t. t9 C6 A2 ]- ^ * R7 H" k0 P0 I, @( O1 |' U' X" d; ` D1 E/ {' f0 F( W / t" T! R) X2 e: L. R
回复

举报 使用道具

相关帖子

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