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

1#!/usr/bin/env python3 

2 

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 

22 

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). 

27 

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) 

62 

63 Returns 

64 ------- 

65 buffs : shapely.geometry.polygon.Polygon, shapely.geometry.multipolygon.MultiPolygon 

66 the buffered MultiPolygon 

67 

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>`_ : 

72 

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." 

77 

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>`_ : 

81 

82 "A sign of 1.0 means that the coordinates of the product's exterior ring 

83 will be oriented counter-clockwise." 

84 

85 Copyright 2017 Thomas Guymer [1]_ 

86 

87 References 

88 ---------- 

89 .. [1] PyGuymer3, https://github.com/Guymer/PyGuymer3 

90 """ 

91 

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 

99 

100 # Import sub-functions ... 

101 from ..check import check 

102 from ..fillin import fillin 

103 from .buffer_Polygon import buffer_Polygon 

104 

105 # ************************************************************************** 

106 

107 # Check argument ... 

108 assert isinstance(multipoly, shapely.geometry.multipolygon.MultiPolygon), "\"multipoly\" is not a MultiPolygon" 

109 if debug: 

110 check(multipoly, prefix = prefix) 

111 

112 # Initialize list ... 

113 buffs = [] 

114 

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 ) 

135 

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) 

140 

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) 

159 

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) 

168 

169 # Return simplified answer ... 

170 return buffsSimp 

171 

172 # Return answer ... 

173 return buffs