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

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

[复制链接]
7 Z" m0 r. y. p& M% M7 v

Folium 简介

" f: Y5 i6 U; H9 o# a A$ c

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

& G6 a q1 |" \9 j& P ]

创建地图

" k& s& o- w+ d( { k% u# `9 a

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

" G+ Q: ]: n, d, p
import folium' i* l+ [# H" o! ~ %matplotlib inline 3 m& J2 q: i% R! Y+ U; F5 s0 O3 N& t : V; C% P% K2 b( b. i' I4 x' }5 { import webbrowser' q7 r$ D, h1 _0 K- L * _8 r5 a4 T$ c print(folium.__version__)$ t4 P* }0 S+ H' w I; N/ y! H4 ]" n # define the world map" C7 s {" Y% y$ s; u world_map = folium.Map() # W; |$ k; `1 q" p2 H # display world map : j: U i I1 a# ?9 k- d5 ?% ~ world_map) H" V5 S* z/ y: _9 c9 P
" X% v: z7 h3 ~6 O( o1 \
世界地图

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

/ W0 J7 b" }) s Y# b

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

( Y, y9 }. T- q: g5 l$ \

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

6 n/ v! [6 V' L2 ]( n M

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

$ q5 x2 }" L/ ?) |2 Q
# latitude and longitude in Singapore city! E! D- [. y0 d+ a3 E! x coordinate_sentosa = [1.248946, 103.834306]3 f J ]" s' ]* S' S3 b coordinate_orchard_road = [1.304247, 103.833264] |/ Y% X+ _7 D6 q/ ^. O coordinate_changi_airport = [1.357557, 103.98847] . G, Y# ^. a7 W- V# O% z# ^ coordinate_nus = [1.296202,103.776899] # y) X7 {& A# p. @4 N; x/ W4 d" K coordinate_ntu = [1.34841, 103.682933] ' a" N- P6 r3 d- D coordinate_zoo = [1.403717, 103.793974] ' i9 L- Q3 @, K+ Y k coordinate_ang_mo_kio = [1.37008, 103.849523]- P: u5 b9 X. M" } coordinate_yi_shun = [1.429384, 103.835028]2 O6 s4 r* t1 G' P( Q% a 9 ]5 A2 W+ w+ h# H; `* b # icon + L9 `+ O" \7 v% i) M icon_cloud = "cloud" , p4 Y, A0 D; C2 X; F icon_sign = "info-sign"' [3 \" K5 F* g( r - H1 {+ J; r1 O$ K- B # define the city map8 P7 o u ^; B/ j # tiles in {OpenStreetMap, Stamen Terrain, Stamen Toner, Mapbox Bright}3 e0 ^. c3 l* E city_map = folium.Map( : ^, E" y" y) l1 E location=coordinate_orchard_road,3 A& X- ?) T7 r2 }7 s9 U- z0 _& W zoom_start=11," m! B1 U. R1 V( W) _- l* N& ?( Q& { tiles=OpenStreetMap)" ~0 V2 X! O0 P% m* K# o) c+ y 2 Z) Z! V7 \+ a& k/ ]( U3 H/ Q7 C # add marker in the city map! ]" n9 x; H |- I( F folium.Marker(& f3 c* y9 i& }; ?+ v coordinate_sentosa, `8 X1 J3 E* C8 H icon=folium.Icon(color=blue) 8 T! r# \- k' _1 N# {4 N ).add_to(city_map) 0 {/ W/ ^$ }( w1 [, p folium.Marker( , K& C6 d- Q5 a) M: J4 z" ~* D9 d coordinate_orchard_road, * d1 t( Q4 O" o4 |# I icon=folium.Icon(color=green, icon=icon_cloud)% d2 l2 R/ k6 |) U6 e# L+ S ).add_to(city_map) ' U( h- m* w1 W, b/ p% f/ A+ B; o/ o" V$ O3 Y # add popup* j; r: T+ {& B; @! @ folium.Marker( 6 s0 G+ Q) d1 M4 D coordinate_changi_airport,) z- g. c; H( J" B U) I! H& e popup=Changi Airport, 5 g& D) `8 e8 e& w) P* U' G icon=folium.Icon(color=red, icon=icon_sign); `0 I7 F# k7 R3 P ).add_to(city_map) : X1 ~% e9 s" @ J/ C& n8 \2 O% D4 Y1 x1 a' f( @- v! }) G # add tooltips and popup 2 R3 g& _* W$ z' l% D( R9 y) x tooltip = "Click me!"8 ]( e( V! `2 I6 Y/ e1 s- F, S 4 u! E( g3 w* S' j+ F0 C folium.Marker( 2 n0 p- |, ?- Q# v) L0 N coordinate_nus,# W0 o$ k; t. M d popup="<i>National University of Singapore</i>", ' J2 r U0 f! J d tooltip=tooltip) z1 U, q& o' u5 _4 X- d ).add_to(city_map)2 V1 O7 f' ]/ E0 H, Q $ W( O& ~0 f- u( z9 N folium.Marker( 5 B( d9 y* L: U coordinate_ntu,: K1 i3 V( J9 I9 @9 l popup="<b>Nanyang Technological University</b>", - Q, l. |' K7 T/ Z1 ? tooltip=tooltip8 M0 L0 P& }) J/ P5 d a( a ).add_to(city_map) $ b! j3 f! ~ C' | % S4 b* I9 g7 p # display city map* S T% F: Z- ^1 h/ s city_map
8 Q' Z! \" B/ M8 d/ C- r
Folium 的经纬度作图(OpenStreetMap)
Folium 的经纬度作图(Stamen Terrain)
Folium 的经纬度作图(Stamen Toner)

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

$ A0 \+ t4 l t
# define the city map $ I; x: R- @/ G2 A4 B* s! W city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) 0 W$ Y9 e r' x( w& @1 Y: v4 E) ]% R& t4 n # 在地图中添加经纬度, add latitude and longitude in the map when click& \! K# a g# V/ P+ T, j city_map.add_child(folium.LatLngPopup()) 7 |) |# i X# G6 F/ ^ 2 c- ]& q. }. @ q7 K4 M city_map
* w/ y. [! ]( i9 d4 d3 A d2 O " c- G2 ^5 E. @# F: L: r5 h

几何形状

) W! `1 ^- U/ g" L7 ?

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

; Z" { A, J8 A
# define the city map2 d) R& v( ]" J F, b# { city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)( H5 V3 a/ [: d. ^$ B* t5 ^% @+ F ! K- H: r/ `6 F, F) P% q8 i # 在地图中如何添加形状3 i/ q, }8 j/ i7 r [: U- `' U # 多条边4 O: B5 u: }, } points_1 = [3 s1 o, x" w6 | q% v! y) I coordinate_ntu, ! F! H* r9 H! \5 _' ]. G( F coordinate_nus, 1 R6 ?+ W' f5 Q1 i coordinate_zoo; |4 V/ y2 |6 E1 {$ C9 I ] 0 N0 X) R7 J( {6 \0 \" i% u: x 3 N1 u- C8 E' I& F2 _7 p # 在 city_map 中添加多条边,第一种添加方式 % d9 B# u2 ?; a% \4 s% Q+ U+ H city_map.add_child(folium.PolyLine( 6 S) H, i/ |4 G8 A0 l+ B5 s locations=points_1, # 坐标列表& s4 a% w$ e, p) {5 D weight=3, # 线条宽度 % H$ w* x! ~9 V% U/ d color=gray)) 9 X: C8 e5 d3 C6 S) H: a. x/ u 0 p0 Z. {, K+ ~* x, D) P$ P1 [8 v- E # 在 city_map 中添加多条边,第二种添加方式 * c8 C5 h) n8 ~ folium.PolyLine(- p9 C/ _+ O. r8 S* c- j locations=points_1, # 坐标列表 ! }" _/ C% h- K" h( Q& f weight=3, # 线条宽度2 `- [& L# B& p; t4 Y0 `3 ?, k: c color=gray).add_to(city_map) / c: c6 i- Q2 p9 t5 m; P 0 y7 X. F: ^& o' m2 t, L6 I # 多边形 6 e" c' ?6 b/ h: C points_2 = [ 8 J8 \: f3 n4 H5 I' \5 s coordinate_orchard_road,# J$ ~: x! k- t( ~$ V5 a% N, X coordinate_sentosa, 9 C6 d2 R" o+ P/ W! K coordinate_changi_airport & H0 G- D P3 ~ ~ ]) p% M, O) S3 w! Y Q + z4 H: l4 p2 c! d; O% U. @ city_map.add_child(folium.Polygon(- E/ L; e- q4 s3 p locations=points_2, # 坐标列表% K% d/ x% Z5 A3 K D# o weight=3, # 线条宽度, t: K4 T- O7 B3 U5 y- p5 ^ color=yellow)) O" a, M& W: p3 j/ y) F9 c- } ; D$ p( S- S3 \1 R/ N$ l # 矩形 4 T9 Y% |; b+ V' u$ K% M bounds = [ 1 B& }3 J' p! a+ o2 B coordinate_ang_mo_kio, : W' w2 i# w/ P1 S. t8 s2 x coordinate_yi_shun " P+ S2 K) \% Q1 f. y ]1 y/ G1 v* o: K" ~! p 0 p+ V# B) W$ ]" q: q5 }+ y) m city_map.add_child(folium.Rectangle(1 Q$ Q5 o8 L, ~. b; u6 L bounds=bounds, # 坐标列表, Latitude and Longitude of line (Northing, Easting) - P& l& R- F& r5 M: O( C weight=2, # 线条宽度 ( G" U. S- X6 Q( R color=blue)) 3 e9 O$ d+ G4 G9 |. ^$ g! N0 h2 a1 x$ X9 P5 K% S' @, y # 圆形, circle, radius units meters* U: u: g! j+ n' W( t- v# L/ ] folium.Circle(8 C. g8 y3 {6 B6 e; y radius=1000,& w' n% d9 _4 s location=coordinate_nus,, Y" G' N0 Q! T2 I8 A popup="National University of Singapore",/ `% z V' o% Y! u" P. s @ color="crimson", + G, m2 Y' f* L- I3 X/ O fill=False,) K3 ^" x1 F& r. c* l. g ).add_to(city_map)0 n d6 z6 O/ O. X# Q1 x ! n, d5 }1 |3 ~3 B # 圆形, circle, radius units pixels # @/ e4 ^* H% c5 R" O0 g# ] folium.CircleMarker( 9 ^) A5 h& r) `( D location=coordinate_ntu, " b( @- A5 s. [/ Y) J- b* u2 V1 N9 b radius=30, 6 r- a* P3 F9 q2 d' ~1 R popup="Nanyang Technological University", # H4 [. F) m9 y, M color="#3186cc",1 A+ ]' |% w5 { fill=True,$ _6 e: _' j9 O- e- L* D, t fill_opacity=0.3, # 透明度* M+ O0 v; j. S8 I9 H% W! k fill_color="#3186cc",* m, z" h- x8 d$ ? b ).add_to(city_map) % Y4 c1 o% l; ?; d1 w& ?+ j3 P$ n. e9 K! K6 z! b7 r% O city_map
+ x- c. {" k. G' L$ B, w1 `, i( B
Folium 中的画出各种形状

热力图

S1 M/ p$ y+ v) h

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

( ?+ z6 P$ h4 e! m4 J
import numpy as np5 C2 q. g& b5 Y( D from folium.plugins import HeatMap * w& v) W+ ]* w1 W4 M, d/ D/ |$ D , T* f& b9 q6 f" U( ]9 z # define the city map 5 m. }: o5 f: F8 e- }" T( n- U# G city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) * o. H2 L* H* f3 i' l ! M# B! A; j- B$ V* x6 K* ~. w # 构建随机数据0 W- A2 B4 b; F8 w! B data = ( + |5 j1 z2 E/ C$ ? np.random.normal( [! C) }1 F. q5 Q; H; } size=(10, 3)) * 0.03 * np.array([[1, 1, 1]]) + & B0 z! y9 L3 c3 c; g0 B np.array([[coordinate_orchard_road[0], coordinate_orchard_road[1], 1]])( W! j$ L% y7 }0 i& p ).tolist() % ?( p8 ~ ~) |7 s . i! P7 E# X# b U( ~- ]; p7 F city_map.add_child(HeatMap(data=data)) $ ?6 A9 c, y$ {. U# I city_map
$ H7 z* {, l' E1 B2 T
" e6 d) z, _! T1 `

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

) z2 A; ]8 C5 d9 v" V: _* O
import numpy as np! v- Z: y1 N/ ]$ e$ k9 D+ b$ u from folium.plugins import HeatMapWithTime' K# s# o& O% k' p) B ; H& ~$ k3 `# J8 R, B # define the city map5 @+ [ V( q" ]/ J3 R0 q city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11)9 b; G& V' l$ F. ~( K' v5 ^ 8 h8 T0 a6 Q6 W) h2 I # 使用 numpy 建立初始数据 3 Q% Z: z, A0 c, K1 T" G initial_data = (np.random.normal(size=(200, 2)) *& F& L9 |0 d/ K6 N9 L7 G$ C% N% E np.array([[0.02, 0.02]]) + ! ]$ ?! V+ u# j1 @: B6 I+ c np.array([coordinate_orchard_road]))! o3 }! w+ `1 B* V 8 p E0 }6 w- x6 |% h # 建立连续的数据 \2 S2 u% ?7 ?+ u' n. a+ f& ~" l data = [initial_data.tolist()] S3 C1 O' ~ T* }+ P6 C1 b; \ for i in range(20):" Q5 @# W: l& h; S& B w data.append((data[i] + np.random.normal(size=(200, 2)) * 0.001).tolist()) & q$ o! A4 f/ C2 f2 u 8 p7 m" M8 o! F # 显示连续的热力图; C; L$ l/ @/ Q5 D& S; o4 _ city_map.add_child(HeatMapWithTime(data))1 D$ N2 E& q9 U" u7 z2 @ city_map
* \' ?/ R* I6 o 4 ]- N: U; z. a f( s0 T. M

经纬度点的聚类

* C. l4 F4 t! }* x( i

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

9 g1 H6 d |+ {9 q4 Q* z* j" C. x
from folium.plugins import MarkerCluster 3 |$ e/ o" h( d) D* x7 N0 }7 C # define the city map* _( A8 H }/ e0 k9 H( [9 Z city_map = folium.Map(location=coordinate_orchard_road, zoom_start=11) * Q8 b" F: b4 F3 w8 k & g. `& t! G9 B5 V# O+ K # 经纬度的聚类! a- ?5 [2 p3 M3 W D& v # 在 NUS 的经纬度附近随机生成 100 个点; 0 x e6 W5 A6 i, i0 I7 {/ _ data = (5 l* I( L. t6 Q; S np.random.normal(size=(100, 2))4 ]- H! l; N/ P' V4 j1 I * np.array([[0.001, 0.001]]) +- u7 o! t3 T% l np.array([coordinate_nus]))* A6 S/ _( L! G6 } , f" u; p1 C. s/ W # create a mark cluster object * _" n; ], A# v+ Z! H1 | D marker_cluster = MarkerCluster().add_to(city_map) . H0 A; {, t% L) _. F # `# f5 u% Z$ V/ ^9 N! v3 e% J # 将这些经纬度数据加入聚类 0 U. n( R- L$ H for element in data: 9 D- a( P$ k+ N folium.Marker(location=[element[0], element[1]],icon=None).add_to(marker_cluster)- }5 b/ B4 @" u) O3 L/ y- c + H: K# r p9 ` # add marker_cluster to map0 Q/ q- s y/ {- s/ Q* W2 F city_map.add_child(marker_cluster) ) H: S( i0 ^: n: h6 w: Q3 E . T! C' q+ Y+ l% ~ # 作图# X! r' S& p+ D- L3 r Z9 ]5 k city_map
+ c! C/ _1 H; E: b% q' |- ^ / U3 r/ t4 w* D+ N9 P2 Q

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

- {% b8 J. r# e( [

参考文献

4 V; y1 f7 q5 _; J

Folium 官方文档:Folium - Folium 0.12.1 documentation

3 }) P! C% p5 b: H" ~% a * i) s8 \4 Q3 C # e- [! a/ u! w* B' g& g ; W) A2 W" E2 \" d4 c1 s( {( \6 N m/ `- q% u+ f7 l- C% _( ^
回复

举报 使用道具

相关帖子

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