Coverage for pyguymer3/geo/bufferSrc/buffer_MultiPolygon.py: 3%
29 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-08 18:47 +0000
1#!/usr/bin/env python3
3# Define function ...
4def buffer_MultiPolygon(
5 multipoly,
6 dist,
7 /,
8 *,
9 debug = __debug__,
10 eps = 1.0e-12,
11 fill = 1.0,
12 fillSpace = "EuclideanSpace",
13 keepInteriors = True,
14 nAng = 9,
15 nIter = 100,
16 prefix = ".",
17 ramLimit = 1073741824,
18 simp = 0.1,
19 tol = 1.0e-10,
20):
21 """Buffer a MultiPolygon
23 This function reads in a MultiPolygon, made up of Polygons (with an exterior
24 and any number of interiors), that exists on the surface of the Earth and
25 returns a [Multi]Polygon of the same MultiPolygon buffered by a constant
26 distance (in metres).
28 Parameters
29 ----------
30 multipoly : shapely.geometry.multipolygon.MultiPolygon
31 the MultiPolygon
32 dist : float
33 the Geodesic distance to buffer each point within the MultiPolygon by
34 (in metres)
35 debug : bool, optional
36 print debug messages
37 eps : float, optional
38 the tolerance of the Vincenty formula iterations
39 fill : float, optional
40 the Euclidean or Geodesic distance to fill in between each point within
41 the shapes by (in degrees or metres)
42 fillSpace : str, optional
43 the geometric space to perform the filling in (either "EuclideanSpace"
44 or "GeodesicSpace")
45 keepInteriors : bool, optional
46 keep the interiors of the Polygon
47 nAng : int, optional
48 the number of angles around each point within the MultiPolygon that are
49 calculated when buffering
50 nIter : int, optional
51 the maximum number of iterations (particularly the Vincenty formula)
52 prefix : str, optional
53 change the name of the output debugging CSVs
54 ramLimit : int, optional
55 the maximum RAM usage of each "large" array (in bytes)
56 simp : float, optional
57 how much intermediary [Multi]Polygons are simplified by; negative values
58 disable simplification (in degrees)
59 tol : float, optional
60 the Euclidean distance that defines two points as being the same (in
61 degrees)
63 Returns
64 -------
65 buffs : shapely.geometry.polygon.Polygon, shapely.geometry.multipolygon.MultiPolygon
66 the buffered MultiPolygon
68 Notes
69 -----
70 According to the `Shapely documentation for the method object.buffer()
71 <https://shapely.readthedocs.io/en/stable/manual.html#object.buffer>`_ :
73 "Passed a distance of 0, buffer() can sometimes be used to "clean"
74 self-touching or self-crossing polygons such as the classic "bowtie".
75 Users have reported that very small distance values sometimes produce
76 cleaner results than 0. Your mileage may vary when cleaning surfaces."
78 According to the `Shapely documentation for the function
79 shapely.geometry.polygon.orient()
80 <https://shapely.readthedocs.io/en/stable/manual.html#shapely.geometry.polygon.orient>`_ :
82 "A sign of 1.0 means that the coordinates of the product's exterior ring
83 will be oriented counter-clockwise."
85 Copyright 2017 Thomas Guymer [1]_
87 References
88 ----------
89 .. [1] PyGuymer3, https://github.com/Guymer/PyGuymer3
90 """
92 # Import special modules ...
93 try:
94 import shapely
95 import shapely.geometry
96 import shapely.ops
97 except:
98 raise Exception("\"shapely\" is not installed; run \"pip install --user Shapely\"") from None
100 # Import sub-functions ...
101 from ..check import check
102 from ..fillin import fillin
103 from .buffer_Polygon import buffer_Polygon
105 # **************************************************************************
107 # Check argument ...
108 assert isinstance(multipoly, shapely.geometry.multipolygon.MultiPolygon), "\"multipoly\" is not a MultiPolygon"
109 if debug:
110 check(multipoly, prefix = prefix)
112 # Initialize list ...
113 buffs = []
115 # Loop over Polygons ...
116 for poly in multipoly.geoms:
117 # Append buffer of Polygon to list ...
118 buffs.append(
119 buffer_Polygon(
120 poly,
121 dist,
122 debug = debug,
123 eps = eps,
124 fill = fill,
125 fillSpace = fillSpace,
126 keepInteriors = keepInteriors,
127 nAng = nAng,
128 nIter = nIter,
129 prefix = prefix,
130 ramLimit = ramLimit,
131 simp = simp,
132 tol = tol,
133 )
134 )
136 # Convert list of [Multi]Polygons to a (unified) [Multi]Polygon ...
137 buffs = shapely.ops.unary_union(buffs).simplify(tol)
138 if debug:
139 check(buffs, prefix = prefix)
141 # Check if the user wants to fill in the [Multi]Polygon ...
142 # NOTE: This is only needed because the "shapely.ops.unary_union()" call
143 # above includes a "simplify()".
144 if simp < 0.0 < fill:
145 # Fill in [Multi]Polygon ...
146 buffs = fillin(
147 buffs,
148 fill,
149 debug = debug,
150 eps = eps,
151 fillSpace = fillSpace,
152 nIter = nIter,
153 prefix = prefix,
154 ramLimit = ramLimit,
155 tol = tol,
156 )
157 if debug:
158 check(buffs, prefix = prefix)
160 # Check if the user wants to simplify the [Multi]Polygon ...
161 # NOTE: This is only needed because the "shapely.ops.unary_union()" call
162 # above might allow more simplification.
163 if simp > 0.0:
164 # Simplify [Multi]Polygon ...
165 buffsSimp = buffs.simplify(simp)
166 if debug:
167 check(buffsSimp, prefix = prefix)
169 # Return simplified answer ...
170 return buffsSimp
172 # Return answer ...
173 return buffs