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

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

[复制链接]
5 \; h9 {" w/ d, [0 M5 _

Folium 简介

1 ? N* l0 g3 _8 z3 o7 a

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

8 G' ?% a6 B$ ^9 Y! w3 ^

创建地图

7 r) l' u0 _* ]+ N$ m6 p' o# [

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

/ P2 f8 f& R, H$ {% s; v8 q
import folium/ ^5 {" e8 d1 ^6 ]- ]1 ^ %matplotlib inline % w: e4 m. e' s R; x, m + L6 h! t/ H3 o7 X) n. { import webbrowser - ^ W, |$ Z! k4 d* [* S b, b' N+ W1 l7 Z% w print(folium.__version__)) O3 c$ f' ]$ G# S! {: J ! x2 ^' S/ v% m0 D! r& k; w( m # define the world map 5 M) A$ ?! Z: P# V B world_map = folium.Map()! M! d/ |( {# m3 p( T% U/ I) O # display world map $ @' ]( A+ Y0 x$ J' ?! S world_map& H. J4 C1 S0 n# [6 n
+ U6 r2 D2 T- c1 O( K, S
世界地图

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

+ |* e5 Z; c. _ @( r$ m }

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

/ q/ T9 i; o" `

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

5 Y7 r9 D4 {: B& U3 |

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

8 T2 Y6 A6 p( }+ w- d o6 \
# latitude and longitude in Singapore city3 G8 W$ O6 s |. P coordinate_sentosa = [1.248946, 103.834306] 1 q" c! q; C2 T" }4 F/ n* F coordinate_orchard_road = [1.304247, 103.833264] 3 z9 P( @+ H1 c coordinate_changi_airport = [1.357557, 103.98847]& D7 l5 W! h* X# _3 f coordinate_nus = [1.296202,103.776899]! H* s5 k9 e9 @' q% E1 x( E4 i coordinate_ntu = [1.34841, 103.682933] # ?; [: n1 j( c# Z: b Y coordinate_zoo = [1.403717, 103.793974] 9 h Q0 \: z( f* j4 g5 j coordinate_ang_mo_kio = [1.37008, 103.849523] 3 B- i! ?! M+ E7 g4 ]- ^ coordinate_yi_shun = [1.429384, 103.835028] 6 ~0 ~% O% L% g& A1 a5 p( C' l7 M4 i; @" r3 k # icon/ R1 s C' I8 ^1 L icon_cloud = "cloud"; @" f5 x+ l& V) _ icon_sign = "info-sign" % b- V j; H! V" S6 p# r' p6 E& X* M( A9 Y' v l( E4 B( ]0 ? # define the city map& N+ k8 V5 e: A* v # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright} r l8 _" A& P' A city_map = folium.Map( 7 g" v3 G( E- W- i& l location=coordinate_orchard_road,/ ?# x) r/ J0 v' s6 p, N zoom_start=11, ( N, u9 ?* l5 R( W' T) I( P tiles=OpenStreetMap)( ]# o3 C9 d1 K # ]! P1 F* M; P" ?4 v # add marker in the city map% X5 u$ u u; L0 O: P8 L2 A folium.Marker(. l' H+ Q0 f$ E6 t N coordinate_sentosa,0 M+ f3 o& Z- J1 Z: e1 c icon=folium.Icon(color=blue)7 X7 X4 w+ T5 c+ c; e: h/ v; z3 Z ).add_to(city_map) . y- ?3 b4 Z2 \; ~0 ? folium.Marker( . n* O) u$ Z9 w; ~4 \. Z+ I coordinate_orchard_road,/ y6 j) _+ f# O7 s8 ~; j icon=folium.Icon(color=green, icon=icon_cloud) 4 \4 ?6 a& M( N5 A( r ).add_to(city_map) & p2 Z4 D% k, j+ f1 Q / M1 R! b, A6 L: ?3 O, e6 k # add popup 7 j6 s, {; B4 V folium.Marker(# M; N$ z: d( s coordinate_changi_airport, ( m* {' t8 z& p+ S0 p$ E7 W popup=Changi Airport,) u, L7 J$ y5 p: w9 R+ p; Y icon=folium.Icon(color=red, icon=icon_sign) " @. v; _( f; K) w& X! q ).add_to(city_map); X' U" y p0 q* k: J 5 @5 h% D6 p q; V0 t # add tooltips and popup$ Y) e4 S/ Q" d# c tooltip = "Click me!"$ ^+ e7 W) {1 y+ N: T4 z # k' I2 U$ g6 B# f( ^ folium.Marker($ { S" x3 g9 ^ coordinate_nus,) c8 w1 ~# M4 M5 {" _0 d popup="<i>National University of Singapore</i>", * ~6 M7 E, b$ a2 j, R- E- N4 c tooltip=tooltip 4 ^) r0 w0 U$ r0 e& g ).add_to(city_map) : b" y+ W0 H. m7 s% y + T: P' ~4 }, L7 G2 U9 I; S2 p; W, i+ X+ C folium.Marker(; Y) ?, G: x7 _! R coordinate_ntu,8 H7 t. \6 a- ?1 Y- t3 q. c3 a popup="<b>Nanyang Technological University</b>",. x/ l8 Q! B4 J7 Z+ E tooltip=tooltip. {. B) Z1 f6 V9 W7 w! J2 | ).add_to(city_map)7 w* m# t8 {4 Y4 c/ w; R) G; F3 k # u5 w! q3 W- m& [, C/ p! O2 D # display city map 1 p5 i8 a# ^$ e, k1 d9 ~ n2 ^ city_map
: o0 u5 v: W0 Y6 C+ S# q; H
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

/ R: d" M& g7 e4 P
# define the city map+ A/ @5 }9 p+ A7 j% }0 |5 T: S city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) : q2 _8 \6 X- _0 U% g7 M1 J6 P! n / ?- D; |1 C5 y2 A # 在地图中添加经纬度, add latitude and longitude in the map when click& L( g$ Y: ]5 p, l! T8 k1 L1 ] city_map.add_child(folium.LatLngPopup()) . u: ]- u9 K6 F8 V4 F$ M, c" e- n- w6 M) v6 {) C& w3 g3 E8 Y city_map
8 l6 A# P+ A* R1 t4 Z, ]* }# V; S6 W % E+ W! u' u! |+ [

几何形状

8 Q$ }& m. ]! q6 T& o' u3 e

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

4 A- l3 Q( o ^
# define the city map: l6 ~0 x, \) \9 C" q6 r8 j8 F city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)' G0 U1 X l y$ B8 h- @ 5 X1 R* ~7 o+ |- a# x7 j # 在地图中如何添加形状 " a' ]: F( b5 D # 多条边 * S& A2 Z F& t points_1 = [ ' b% P' G0 A! r6 O coordinate_ntu, ) {* {3 v f2 V8 w+ I coordinate_nus, ' i1 Z+ v. ?* o; p9 W# ] coordinate_zoo 3 Q: B+ [& l5 I; t ] / o+ S, j! R( {( L/ m U3 R5 I3 a& y6 U! c2 w9 b # 在 city_map 中添加多条边,第一种添加方式 . c6 U6 f/ S0 g% P- ]2 d( } city_map.add_child(folium.PolyLine( 9 e& R' ~$ S8 [% E locations=points_1, # 坐标列表 9 Y y& @2 K( B7 x& ?( A weight=3, # 线条宽度 + j" Q1 [; K0 m' _5 Z: e) P* f: ~ color=gray))0 a6 F7 ^( m( a. \2 K/ j ; c+ z. H! F- H5 s% j: ~ # 在 city_map 中添加多条边,第二种添加方式9 ]3 t. T5 d' N. ^+ ]3 m2 H7 W folium.PolyLine( ; ~6 r' j" \/ T# L locations=points_1, # 坐标列表" `6 M3 U, d& p! u1 S6 X7 Q! } weight=3, # 线条宽度 # i, N- R! l- g: P t, m3 M. @2 ~& e color=gray).add_to(city_map)+ G( K% q2 `; S0 b3 j5 o ! K" n! |/ m9 O, ^4 V( X # 多边形" P; I# V" _/ [; R* e. q- k: e points_2 = [9 f; H, D* {& |5 s3 x2 | coordinate_orchard_road, ; K! o! Z0 ~" x1 h coordinate_sentosa,$ t9 Y- a* F+ Y4 [; |- G! j coordinate_changi_airport3 P5 W7 J- N( E$ Q ]7 [! A5 B" [+ @0 V( Z4 { 6 G G* @# f @ V city_map.add_child(folium.Polygon(8 d2 S0 L8 D! ?) _' p7 Y locations=points_2, # 坐标列表 7 R* Y& m2 G7 R9 s/ b weight=3, # 线条宽度& a8 w2 R/ X8 h% s. Q' i color=yellow))/ P& ]0 J& K" O# w; y q( E+ p+ }$ t9 |+ Y # 矩形 3 \" D2 Q$ K3 U$ n7 [+ i bounds = [ / S0 |3 p+ W) C8 | coordinate_ang_mo_kio,# H! O% f a. o8 n& C9 Q coordinate_yi_shun* z/ ?6 n& }; L3 I4 d+ A ] % q% [$ F$ r! b# T! C ! }9 m! y3 u9 k city_map.add_child(folium.Rectangle($ p C% s. A1 J" }0 S bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting)9 v! h0 U' z7 O: O5 E5 A* l4 x& Y weight=2, # 线条宽度6 T# D P' f6 W- [9 g color=blue))6 w4 H/ z! J0 Z; S M1 _4 S7 ?, S # 圆形, circle, radius units meters ( M$ L+ I- h& ~5 Z. g+ J folium.Circle( 2 g" S: l3 ]5 d7 p/ }: A* i% d$ s radius=1000,. |. g ?( H: c/ ?* k" f location=coordinate_nus,0 z( P. @& v6 u! x5 k popup="National University of Singapore", 6 E$ n$ x% I: A+ q color="crimson",$ X3 Q4 m) x. P# \5 @ fill=False, 9 ^; K5 c5 s5 F1 p$ q% } ).add_to(city_map) ) w% S5 c% Q" [2 G/ G: r6 Q1 A0 s 2 U8 |" q3 r; A, n) w # 圆形, circle, radius units pixels 9 s( j& f6 M# t/ G: R- I folium.CircleMarker( 5 e3 z0 _0 Z' ^4 q+ e# H location=coordinate_ntu, . v9 y/ w j3 l2 |. X& } radius=30, ' E: V# G% P7 X' o" f2 ^ popup="Nanyang Technological University",) T* `* Q" E* B( } color="#3186cc", ! O# |1 x& k7 D) S$ F. A fill=True,1 p4 e# V3 n* T% G6 _' q3 L6 V( s fill_opacity=0.3, # 透明度1 V& G. y/ z" Q3 `4 R9 r fill_color="#3186cc",! W8 X1 A! v4 f* L8 W ).add_to(city_map) # g/ F7 R; N/ H+ M 4 _) O% N5 a2 d city_map
1 W9 g; B& b+ y, N
Folium 中的画出各种形状

热力图

2 ?5 l' x5 c* T6 e, x1 i m% G

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

5 s7 |4 P6 I" v- ^, ~
import numpy as np 7 t' E) U3 I+ V2 G- _) e from folium.plugins import HeatMap ) U! {2 v* _- |7 K" Y% f3 s$ ]# k8 x # define the city map ; b7 A# u7 ?: N9 W6 z! G city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) V# E( K% A% |- P( p+ P ; a+ A3 I u& t/ P7 C' V7 U, @& t # 构建随机数据3 w: _+ C2 K9 w data = (3 ?7 o8 }) ^8 Y# d9 _/ m6 v np.random.normal(/ O1 ]7 \/ A+ ^ E+ `1 ? size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) +' B' C* M9 o9 I* ], P" J np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])0 z& H& U, ]) Y0 e ).tolist()0 L+ u3 U- ?3 n& H! e : _' T+ z1 O1 j. b1 o city_map.add_child(HeatMap(data=data))6 N' S: I3 c0 C* m. z city_map
" r; ^8 k+ A) e! U
6 a" r/ r9 y7 [3 n

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

- B' o1 _6 s) y
import numpy as np! s: t! s1 ]3 E; o0 Z from folium.plugins import HeatMapWithTime+ y) P2 V: d) H! p8 \ P 6 X S3 u$ Y; E$ x8 c+ l2 y* Y # define the city map4 W( U# ]6 P, }9 U city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 5 R/ ]/ \& @( V3 _3 j# i) [" v8 K& c, O% k7 Q2 v # 使用 numpy 建立初始数据# @3 h& p5 n! k4 z9 P initial_data = (np.random.normal(size=(200, 2)) * ' C( f# e5 c% ~8 a" \! u, B% z8 _ np.array([[0.02, 0.02]]) + 7 i5 V6 R3 Q4 l- u np.array([coordinate_orchard_road]))5 V3 t# w) w; E( E. `8 p* a5 ~ - Q+ [7 {) I! C1 B4 P7 Z9 T # 建立连续的数据 4 Z' ?$ G1 _8 K6 X data = [initial_data.tolist()]: @, H1 I8 ]' r2 s for i in range(20):$ S8 R! |* i4 p5 S) E data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist())7 c) ~0 l1 p! |2 _, @6 L3 u) t * j9 J6 m( a q' W, ~1 d3 X& c # 显示连续的热力图: ~0 y3 |$ W: D( k( m7 A% z M' G city_map.add_child(HeatMapWithTime(data)) 4 r% ~3 l4 y" W" y& U1 j city_map
- y& P+ P# k" r. S" T8 W P7 f; S 1 w! z0 K6 g. }) @$ K- x0 A& x

经纬度点的聚类

# s' N% s$ j' n' a- W, T3 q

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

4 q$ s/ P- n) a. z$ k; h
from folium.plugins import MarkerCluster : x1 `8 } U8 u1 z6 Z 3 p4 Z W5 ~2 B% S # define the city map" C. F" u" l1 [/ r: ~+ n city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) ( a' j+ P, y1 ~) {) S# k- D7 b. G* d: q0 ~- W: I # 经纬度的聚类 ' {8 w c6 w" D: s # 在 NUS 的经纬度附近随机生成 100 个点;" t2 l) {3 Q' @) n" ?. S. J data = (/ t7 M5 Y* R( K np.random.normal(size=(100, 2))2 f* Z8 A$ p0 F2 H * np.array([[0.001, 0.001]]) +( V! W5 ^9 z* S* k np.array([coordinate_nus])) # Y8 j( R7 C& |) b% i t7 ^$ n- V/ \2 U" F; _$ W # create a mark cluster object9 L5 \$ j9 i7 B marker_cluster = MarkerCluster().add_to(city_map)& W- w! V9 C3 p% k/ s2 d , h5 Z2 P! N! A # 将这些经纬度数据加入聚类 - w' R/ @/ }+ r, i! e for element in data: ( D. i* C1 I6 M; H) \/ _ folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster) 9 s& n- `: j+ M$ c! _9 w8 l' S: L0 t' V # add marker_cluster to map 8 c' P/ X; V: q8 [2 c city_map.add_child(marker_cluster)& V" `- D4 y9 X' _ ) q; Q: \8 D# X2 R. q" o # 作图 P3 k, Q6 c' K8 U* J; s5 b city_map
; U7 i( u7 s# ~. r A2 q# R% r " ?$ ]: e" E0 ^

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

% G' p- ?9 Q9 a! R! @9 N$ u* l

参考文献

, c. j. l5 W* e. o2 g4 @

Folium 官方文档:Folium - Folium 0.12.1 documentation

3 } |& @% t6 n 5 {+ I+ f- H2 B/ m0 x# |# V! F4 z( w2 O ; k, h( n" E9 c8 p& x$ Q( n * h3 |! J. Y" d3 J4 k
回复

举报 使用道具

相关帖子

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