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

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

[复制链接]
; N' ]1 o9 ]4 X& l& I/ Z# g

Folium 简介

O% Z, q4 ^+ f4 M" F" t/ f

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

6 o" b) `# T) k. p! m9 O" z/ C

创建地图

) T5 a3 i9 X3 P* n& w. `

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

8 o( b5 {3 q7 s4 S6 O
import folium# g# T, @# v" f+ s %matplotlib inline0 j# K- _. [" J . G. U) e& Q6 d7 M. E import webbrowser + ^! h S5 c# f8 w/ T9 K& |( i. i% v) `/ w: W: B8 H print(folium.__version__)3 x) q- t y: a5 r! R8 M1 c 1 b) w9 P0 }2 j% C% n0 u # define the world map . V: O a, O, z) A! X$ f world_map = folium.Map() ! r# l; F# ]: J( a # display world map- H. E0 [: x3 i world_map + i" s# F7 m: @6 a' L- k. H
8 ^- S# q3 f+ r5 Z% p- c
世界地图

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

3 A0 a- l; M, i" x8 h9 l( L. p

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

6 O6 _" [& s0 g" g: G

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

7 }- T' M# R0 M

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

. W% B( n: }' b" C! F
# latitude and longitude in Singapore city1 m f" x1 q( Z! y& Q: d coordinate_sentosa = [1.248946, 103.834306] 1 I" m; O2 o! p' }5 ~, M coordinate_orchard_road = [1.304247, 103.833264] . \: `* v3 c# P' `* Z$ |% m coordinate_changi_airport = [1.357557, 103.98847]4 ]# N( ?6 a9 A% E6 C+ F coordinate_nus = [1.296202,103.776899]+ C! U, H* `# g) O9 f coordinate_ntu = [1.34841, 103.682933]1 p) m# J( b* }& s2 r8 t# ` coordinate_zoo = [1.403717, 103.793974]. |) l/ A# O' h2 V# s* o1 N coordinate_ang_mo_kio = [1.37008, 103.849523]! n: h; Y% V, o& _4 T coordinate_yi_shun = [1.429384, 103.835028] 3 e# c) U( I( Q0 P6 G8 e A, B, C0 q7 B O2 E # icon , R0 O0 k8 q% j( D" {6 \, b icon_cloud = "cloud" I: l: P' z/ ^8 M icon_sign = "info-sign" 9 N" R& v: q. ~ ?$ n, L& Q( ~' q/ a6 t # define the city map : V/ f1 [' h" C/ K' v # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} 4 B' X# g- d8 ~6 l9 ] \* v' X city_map = folium.Map(+ O( I7 F. {1 Y& F$ D location=coordinate_orchard_road, 6 \4 H8 t+ J, }( c L' A5 t zoom_start=11,9 h: C0 `9 y: T4 H$ V9 ?( J tiles=OpenStreetMap)* e) t: u6 k9 r0 c 7 Y# i, [2 y+ O # add marker in the city map q7 B( _1 F$ M3 r9 t; p7 X6 g folium.Marker( . v; }9 x0 a" V9 H- I coordinate_sentosa,' n' {5 r1 ~, B. G icon=folium.Icon(color=blue) ) q# t+ ~: Q/ X# ]0 X ).add_to(city_map)& Y, A/ {& e# Y% B, z( i folium.Marker(6 ]" q- b) j+ { U, ?& d coordinate_orchard_road,0 k# {7 H; N1 Z3 d+ e {8 N icon=folium.Icon(color=green, icon=icon_cloud)/ S" D4 ~; ]5 B ).add_to(city_map) 1 O" i5 c/ s m6 n/ S; h* F1 b: e5 p0 Y$ e S # add popup " s$ j$ \) z$ V! d folium.Marker(( m. S% c; ?$ M) W* f2 L coordinate_changi_airport, 0 A. D; ]; U, Z& g1 u4 a popup=Changi Airport,! W6 r) v! R5 g1 M! S" Z icon=folium.Icon(color=red, icon=icon_sign)1 [( M4 @, D+ H5 B! e4 h ).add_to(city_map): G$ V$ V+ u; }8 t ) w8 `" U, b! Y: L$ s6 G # add tooltips and popup% C& o1 j e6 z( Q tooltip = "Click me!"* m, K- O- b9 C( p ' V$ j; Q9 G- G2 u folium.Marker( ; E2 Y, E( \ u- g8 N+ f/ E coordinate_nus,6 V4 y. p& w, k popup="<i>National University of Singapore</i>"," ^' K7 x' z, g; B tooltip=tooltip ) J+ w$ i. J2 X ).add_to(city_map); S2 o: k# b& V0 i ( Z% f3 K# {4 a* W j- I folium.Marker( |# {* p, @+ a( a coordinate_ntu, $ L0 o8 O+ d5 S/ L; O8 H popup="<b>Nanyang Technological University</b>", 3 r* V; Y: l Z0 q2 s7 w: J8 j tooltip=tooltip 1 i( Q7 }' Y- Y1 l ).add_to(city_map) ; p% o! v) ]5 p4 F. U% T ) s& Q# ^2 t$ @* u. T6 M # display city map " O1 l& Q/ n3 C$ d5 M4 L, Y! ~ city_map
, D8 W5 |8 Q6 \5 N' I
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

5 X( C3 T9 B0 N) Z
# define the city map 3 N) d5 u( c0 {3 x. Y; \/ a2 i city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) ! _( a' _- ]0 a+ z l5 v* ] i; d& K6 C0 Y # 在地图中添加经纬度, add latitude and longitude in the map when click6 g+ z J6 n, x5 Z city_map.add_child(folium.LatLngPopup()) : q% \# W) R0 @( ?. {9 s+ m& V4 A- I+ Y5 P# I$ ]. { city_map
0 |# y( a5 P8 R/ G3 ^. O% g4 Q 8 H7 k) x4 c6 e. r% T

几何形状

- Y* S3 k8 a) R9 q% j+ R# i

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

& q, `- p8 ?6 }6 i( S
# define the city map( r8 z5 T9 U% L* D' L6 e; k city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)' I& \( a3 x5 ~3 [ 1 e u! \5 ^ U0 U+ V # 在地图中如何添加形状 % m1 H( Z6 G2 l/ [2 o9 d: c7 c # 多条边 . J C- j6 j- c L; V) ^9 U points_1 = [9 X8 d7 X9 M& A, r. F) a coordinate_ntu,+ r( R1 O. U9 N4 I- N3 N coordinate_nus,- E8 n- F0 C9 g* W4 [* t coordinate_zoo ' j& f' O1 b/ w0 B ] " z, q( V: e# v) X' q, }& t- n0 U" J0 f # 在 city_map 中添加多条边,第一种添加方式 5 S/ U, C* @: N9 @. c city_map.add_child(folium.PolyLine(# @- {& T9 Z4 m locations=points_1, # 坐标列表- C' w% @, i1 T3 r2 a# G. p2 @ weight=3, # 线条宽度! c+ ?" L9 e6 C2 Z } color=gray))' _8 K+ W. f) Q 4 T) e9 R$ t; b( Q# W, e" C # 在 city_map 中添加多条边,第二种添加方式: g, w" ~# ]4 c9 E; \9 _6 z* y folium.PolyLine( $ B8 [& y1 F; l6 Y! p" A: C. | locations=points_1, # 坐标列表 # i% G! [* x' e2 t- r6 P& ]9 R weight=3, # 线条宽度 ! U6 U" ]) V6 V( S1 o/ }& h- D color=gray).add_to(city_map) N; L4 f0 F6 J) H( y& ?" \- d$ M% V5 o " O- `( X s7 k) g$ ?, @2 I7 P6 _0 y # 多边形. g- M5 p; s. X! @9 | points_2 = [ $ Q8 q0 H8 Y, v' Z5 Z8 N& a8 i; n coordinate_orchard_road,, L! ]2 C& o: U I* f* e9 k& H coordinate_sentosa,& b( x+ B% A: Q( C1 E { coordinate_changi_airport" }! c7 R# I! `9 v+ c. H7 {; k. E ] 6 h- X# k* ~$ @5 Y' d 4 d4 ?; Z: X& w city_map.add_child(folium.Polygon(& a1 [: w/ m! r& ~ locations=points_2, # 坐标列表 + }1 w( n& r; V" b6 }* H( Z @ weight=3, # 线条宽度 6 E! g) }1 m& u+ k color=yellow))# T; _. \, ?( z: i! d ' \6 D: ^# h* {( F/ K+ t: | # 矩形 5 a8 y" N1 T, ~ bounds = [ ?& S( s4 c( Z6 h" t4 h) U coordinate_ang_mo_kio,& X0 `5 _4 m6 O( f( L/ t coordinate_yi_shun * `. _/ _( D, q7 K/ o5 J ]0 T0 ?# R9 ]- w @# g! C 5 w% ]. H$ Z u% c ]) z8 {3 q$ c city_map.add_child(folium.Rectangle(. N' Y6 J' M" ^* k bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)( `4 {2 g* m6 ?0 p' _! @ weight=2, # 线条宽度7 r9 [" P" F1 R" V color=blue)) 9 d8 d' ~1 ]6 V P( p( ` , ^: J& P6 f* f7 { # 圆形, circle, radius units meters' e2 d) y- |! w, G( z folium.Circle(2 _6 g$ N& O* ?9 y! h$ {' h, G" _5 ] Y radius=1000,3 W- [: @: v! s0 s) G3 o location=coordinate_nus, & L7 x6 A+ l6 R4 x7 ]4 s9 q% F popup="National University of Singapore", 8 b* c' i& X* D+ V8 u color="crimson",, ~, l- Q+ J, z& R5 V4 Q' A fill=False," q4 i0 ~7 ]. S0 H3 g ).add_to(city_map) * o8 K8 k" e2 G5 P2 U( U; W; N; \2 o& S" P, ~) C # 圆形, circle, radius units pixels ; B" a- {2 R* R8 b% L folium.CircleMarker( # e+ V7 P% `2 L, b T; {* Y location=coordinate_ntu,7 X* L) d/ {- J, D& U radius=30, # e' F4 o/ b/ A9 k7 ^0 Q' W0 \7 E popup="Nanyang Technological University",* L3 A8 W1 ]: Z color="#3186cc",* e' h+ Q, r: I1 m/ v/ [7 m fill=True,6 X; M+ d' Z+ R" X fill_opacity=0.3, # 透明度! K2 c. B4 L1 C" K y fill_color="#3186cc",! i2 [# s6 H* F9 c ).add_to(city_map)7 {: L/ d; t" N" O# l4 \$ d + o# ?4 M9 X& o2 `" l% N/ U% h7 \ city_map
) n5 Z* W. _1 F( |( K% r" M
Folium 中的画出各种形状

热力图

; }" T7 e7 ?9 @; u; y

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

: l1 \& c1 Q- {0 C
import numpy as np 9 D7 ^, E% `* e* o5 @( ? from folium.plugins import HeatMap/ _; Z) V: D, s s1 V 7 N, t/ b' C d! l6 ]& R5 V # define the city map " U; o3 T4 o6 m city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11): L, c: e5 C d4 t# d3 P 3 s1 x% A& Y5 o" O # 构建随机数据5 K4 [! ^, D8 t# p, s. W; f data = ( 4 m: Q7 X1 U2 q2 `! F3 } np.random.normal( * B$ z% @4 `* P, P, a size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +! z7 u/ c+ L+ O+ z3 A9 \ np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]]). y" e# G' Q7 m9 N$ v8 G ).tolist()6 p/ E) y" O+ L6 z6 [" _& A $ ?. S. c# u* _% _9 w, V% ~- Q$ |; S city_map.add_child(HeatMap(data=data))2 m/ V, ~' V% ^# j$ m/ N0 d. C city_map
, t& S* M p9 Y' ^2 V
7 g) ]2 w' S; G

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

/ q( ~) j _5 a& n$ \! T7 q
import numpy as np$ E8 }# i$ J% H# G+ a4 v2 ^; Y" A4 c from folium.plugins import HeatMapWithTime ( x3 _7 I/ K0 j! n ) P2 z+ k( V6 S) o- I # define the city map5 y3 K& b( ?1 Y# c! l city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 1 n' a1 x6 i% t: Z1 m- P 3 O, Z0 H+ t" l' T. W) O( e # 使用 numpy 建立初始数据 5 W' W5 h' d+ k+ L initial_data = (np.random.normal(size=(200, 2)) *, k2 K A& q1 z2 D* n. ] np.array([[0.02, 0.02]]) + " h" A) r) C# p np.array([coordinate_orchard_road]))* Q* r0 t4 v) W( }* N( _4 F( m ( V4 Z* e6 q5 ] # 建立连续的数据 - O1 B& m' K' Y data = [initial_data.tolist()] - `4 @+ _3 R3 q* { i' T0 a1 I for i in range(20):- Y1 N. B7 n' n( X& U0 ~: W3 o data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()) / C# j* a1 J# Q: w/ b/ y& S) E+ K# l7 D # 显示连续的热力图 & A- p4 Z' }6 [0 W4 X% r city_map.add_child(HeatMapWithTime(data))( p$ ]) E0 P1 y city_map
8 B4 r# N8 C, p; K% [9 A1 W5 S 5 D& w F8 b8 ?7 C2 S6 i$ {9 S

经纬度点的聚类

5 S$ x! W" [; U* c

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

6 _1 a2 @2 @5 Q% _. S, ^) U9 U, g) _
from folium.plugins import MarkerCluster , Y" i% e0 ~* S. H; q) a9 Q + r% p; }/ e* f+ V3 m! ^ # define the city map( D: s3 H5 M' v7 ^! ]" m city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 3 j1 p; D k0 X* c) R# g% J6 W9 d8 a0 q/ P# R/ E: E # 经纬度的聚类 % @8 c' f9 S$ X' W2 R' A # 在 NUS 的经纬度附近随机生成 100 个点;% U/ ~. s4 P, |; e1 T data = ( + O( Y$ V" k$ f3 [; j4 U* s+ ` np.random.normal(size=(100, 2)) ! `8 F) d' z4 y0 z8 [ * np.array([[0.001, 0.001]]) +; B% E3 w- E7 }5 n* L np.array([coordinate_nus])) : b/ ] v: k4 \8 n9 d& p0 S" L! r* L # create a mark cluster object! C& y! Y8 [1 p* g- a2 f7 a8 r marker_cluster = MarkerCluster().add_to(city_map)( b+ w U% d3 u + b- E/ f: v0 n1 r! _; z # 将这些经纬度数据加入聚类 3 b0 u# w( q0 J5 }5 h) u/ w for element in data: , _& p# J9 p3 J+ Q* k* }$ Z' C folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) # P. d* K! G* h8 c* ` s, j' {: L" k9 q8 Z/ s: Q7 W' w o0 f' i) `7 ] # add marker_cluster to map9 a1 [$ B' Y( I2 a# ^ city_map.add_child(marker_cluster) 6 a4 X4 m Q: Q) c' Y: K7 ^- L, _" y6 c# f4 f! { # 作图 % c2 l5 b! c; X8 {" H6 R" T/ U, h city_map
, d4 m* ^6 z# X , _8 O0 D' g) d

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

, I- i0 S# f1 }" _. q2 P

参考文献

2 p5 K# y1 Y# C

Folium 官方文档:Folium - Folium 0.12.1 documentation

) M+ b2 j0 d9 Z+ ], ^5 @9 B - }( X* F Y$ H3 N) S/ ^ 1 q* \* h+ m" @- [7 u% W% q8 ?/ [% W9 x6 c : \4 o% R; w: ?' D6 n: m6 m! W
回复

举报 使用道具

相关帖子

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