rgeo 2.3.0 → 3.0.0.pre.rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +6 -0
- data/README.md +1 -0
- data/ext/geos_c_impl/analysis.c +8 -6
- data/ext/geos_c_impl/analysis.h +1 -3
- data/ext/geos_c_impl/errors.c +10 -8
- data/ext/geos_c_impl/errors.h +7 -3
- data/ext/geos_c_impl/extconf.rb +3 -0
- data/ext/geos_c_impl/factory.c +251 -182
- data/ext/geos_c_impl/factory.h +43 -62
- data/ext/geos_c_impl/geometry.c +56 -24
- data/ext/geos_c_impl/geometry.h +8 -3
- data/ext/geos_c_impl/geometry_collection.c +41 -148
- data/ext/geos_c_impl/geometry_collection.h +1 -14
- data/ext/geos_c_impl/globals.c +91 -0
- data/ext/geos_c_impl/globals.h +45 -0
- data/ext/geos_c_impl/line_string.c +28 -29
- data/ext/geos_c_impl/line_string.h +1 -3
- data/ext/geos_c_impl/main.c +10 -9
- data/ext/geos_c_impl/point.c +9 -8
- data/ext/geos_c_impl/point.h +1 -3
- data/ext/geos_c_impl/polygon.c +15 -51
- data/ext/geos_c_impl/polygon.h +1 -3
- data/ext/geos_c_impl/preface.h +8 -0
- data/lib/rgeo/cartesian/analysis.rb +2 -2
- data/lib/rgeo/cartesian/calculations.rb +54 -17
- data/lib/rgeo/cartesian/factory.rb +0 -7
- data/lib/rgeo/cartesian/feature_classes.rb +66 -46
- data/lib/rgeo/cartesian/feature_methods.rb +56 -20
- data/lib/rgeo/cartesian/interface.rb +0 -6
- data/lib/rgeo/cartesian/planar_graph.rb +379 -0
- data/lib/rgeo/cartesian/sweepline_intersector.rb +149 -0
- data/lib/rgeo/cartesian/valid_op.rb +71 -0
- data/lib/rgeo/cartesian.rb +3 -0
- data/lib/rgeo/coord_sys/cs/wkt_parser.rb +6 -6
- data/lib/rgeo/error.rb +15 -0
- data/lib/rgeo/feature/curve.rb +12 -2
- data/lib/rgeo/feature/geometry.rb +38 -28
- data/lib/rgeo/feature/geometry_collection.rb +13 -5
- data/lib/rgeo/feature/line_string.rb +3 -3
- data/lib/rgeo/feature/multi_curve.rb +6 -1
- data/lib/rgeo/feature/multi_surface.rb +3 -3
- data/lib/rgeo/feature/point.rb +4 -4
- data/lib/rgeo/feature/surface.rb +3 -3
- data/lib/rgeo/geographic/factory.rb +0 -7
- data/lib/rgeo/geographic/interface.rb +4 -18
- data/lib/rgeo/geographic/proj4_projector.rb +0 -2
- data/lib/rgeo/geographic/projected_feature_classes.rb +21 -9
- data/lib/rgeo/geographic/projected_feature_methods.rb +63 -30
- data/lib/rgeo/geographic/simple_mercator_projector.rb +0 -2
- data/lib/rgeo/geographic/spherical_feature_classes.rb +29 -9
- data/lib/rgeo/geographic/spherical_feature_methods.rb +68 -2
- data/lib/rgeo/geos/capi_factory.rb +21 -31
- data/lib/rgeo/geos/capi_feature_classes.rb +64 -11
- data/lib/rgeo/geos/ffi_factory.rb +0 -28
- data/lib/rgeo/geos/ffi_feature_classes.rb +34 -10
- data/lib/rgeo/geos/ffi_feature_methods.rb +53 -10
- data/lib/rgeo/geos/interface.rb +18 -10
- data/lib/rgeo/geos/zm_factory.rb +0 -12
- data/lib/rgeo/geos/zm_feature_methods.rb +30 -5
- data/lib/rgeo/impl_helper/basic_geometry_collection_methods.rb +18 -8
- data/lib/rgeo/impl_helper/basic_geometry_methods.rb +1 -1
- data/lib/rgeo/impl_helper/basic_line_string_methods.rb +37 -26
- data/lib/rgeo/impl_helper/basic_point_methods.rb +13 -3
- data/lib/rgeo/impl_helper/basic_polygon_methods.rb +8 -3
- data/lib/rgeo/impl_helper/valid_op.rb +354 -0
- data/lib/rgeo/impl_helper/validity_check.rb +138 -0
- data/lib/rgeo/impl_helper.rb +1 -0
- data/lib/rgeo/version.rb +1 -1
- data/lib/rgeo/wkrep/wkb_generator.rb +1 -1
- data/lib/rgeo/wkrep/wkt_generator.rb +6 -6
- metadata +30 -7
@@ -0,0 +1,354 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
4
|
+
|
5
|
+
module RGeo
|
6
|
+
module ImplHelper
|
7
|
+
# Mixin based off of the JTS/GEOS IsValidOp class.
|
8
|
+
# Implements #valid? and #invalid_reason on Features that include this.
|
9
|
+
#
|
10
|
+
# @see https://github.com/locationtech/jts/blob/master/modules/core/src/main/java/org/locationtech/jts/operation/valid/IsValidOp.java
|
11
|
+
module ValidOp
|
12
|
+
# Validity of geometry
|
13
|
+
#
|
14
|
+
# @return Boolean
|
15
|
+
def valid?
|
16
|
+
invalid_reason.nil?
|
17
|
+
end
|
18
|
+
|
19
|
+
# Reason for invalidity or nil if valid
|
20
|
+
#
|
21
|
+
# @return String
|
22
|
+
def invalid_reason
|
23
|
+
return @invalid_reason if defined?(@invalid_reason)
|
24
|
+
@invalid_reason = check_valid
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def validity_helper
|
30
|
+
ValidOpHelpers
|
31
|
+
end
|
32
|
+
|
33
|
+
# Method that performs validity checking. Just checks the type of geometry
|
34
|
+
# and delegates to the proper validity checker.
|
35
|
+
#
|
36
|
+
# Returns a string describing the error or nil if it's a valid geometry.
|
37
|
+
# In some cases, "Unkown Validity" is returned if a dependent method has
|
38
|
+
# not been implemented.
|
39
|
+
#
|
40
|
+
# @return String
|
41
|
+
def check_valid
|
42
|
+
case self
|
43
|
+
when Feature::Point
|
44
|
+
check_valid_point
|
45
|
+
when Feature::LinearRing
|
46
|
+
check_valid_linear_ring
|
47
|
+
when Feature::LineString
|
48
|
+
check_valid_line_string
|
49
|
+
when Feature::Polygon
|
50
|
+
check_valid_polygon
|
51
|
+
when Feature::MultiPoint
|
52
|
+
check_valid_multi_point
|
53
|
+
when Feature::MultiPolygon
|
54
|
+
check_valid_multi_polygon
|
55
|
+
when Feature::GeometryCollection
|
56
|
+
check_valid_geometry_collection
|
57
|
+
else
|
58
|
+
raise NotImplementedError, "check_valid is not implemented for #{self}"
|
59
|
+
end
|
60
|
+
rescue RGeo::Error::UnsupportedOperation, NoMethodError
|
61
|
+
"Unkown Validity"
|
62
|
+
end
|
63
|
+
|
64
|
+
def check_valid_point
|
65
|
+
validity_helper.check_invalid_coordinate(self)
|
66
|
+
end
|
67
|
+
|
68
|
+
def check_valid_line_string
|
69
|
+
# check coordinates are all valid
|
70
|
+
points.each do |pt|
|
71
|
+
check = validity_helper.check_invalid_coordinate(pt)
|
72
|
+
return check unless check.nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
# check more than 1 point
|
76
|
+
return Error::TOO_FEW_POINTS unless num_points > 1
|
77
|
+
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
|
81
|
+
def check_valid_linear_ring
|
82
|
+
# check coordinates are all valid
|
83
|
+
points.each do |pt|
|
84
|
+
check = validity_helper.check_invalid_coordinate(pt)
|
85
|
+
return check unless check.nil?
|
86
|
+
end
|
87
|
+
|
88
|
+
# check closed
|
89
|
+
return Error::UNCLOSED_RING unless closed?
|
90
|
+
|
91
|
+
# check more than 3 points
|
92
|
+
return Error::TOO_FEW_POINTS unless num_points > 3
|
93
|
+
|
94
|
+
# check no self-intersections
|
95
|
+
validity_helper.check_no_self_intersections(self)
|
96
|
+
end
|
97
|
+
|
98
|
+
def check_valid_polygon
|
99
|
+
# check coordinates are all valid
|
100
|
+
exterior_ring.points.each do |pt|
|
101
|
+
check = validity_helper.check_invalid_coordinate(pt)
|
102
|
+
return check unless check.nil?
|
103
|
+
end
|
104
|
+
interior_rings.each do |ring|
|
105
|
+
ring.points.each do |pt|
|
106
|
+
check = validity_helper.check_invalid_coordinate(pt)
|
107
|
+
return check unless check.nil?
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# check closed
|
112
|
+
return Error::UNCLOSED_RING unless exterior_ring.closed?
|
113
|
+
return Error::UNCLOSED_RING unless interior_rings.all?(&:closed?)
|
114
|
+
|
115
|
+
# check more than 3 points in each ring
|
116
|
+
return Error::TOO_FEW_POINTS unless exterior_ring.num_points > 3
|
117
|
+
return Error::TOO_FEW_POINTS unless interior_rings.all? { |r| r.num_points > 3 }
|
118
|
+
|
119
|
+
# can skip this check if there's no holes
|
120
|
+
unless interior_rings.empty?
|
121
|
+
check = validity_helper.check_consistent_area(self)
|
122
|
+
return check unless check.nil?
|
123
|
+
end
|
124
|
+
|
125
|
+
# check that there are no self-intersections
|
126
|
+
check = validity_helper.check_no_self_intersecting_rings(self)
|
127
|
+
return check unless check.nil?
|
128
|
+
|
129
|
+
# can skip these checks if there's no holes
|
130
|
+
unless interior_rings.empty?
|
131
|
+
check = validity_helper.check_holes_in_shell(self)
|
132
|
+
return check unless check.nil?
|
133
|
+
|
134
|
+
check = validity_helper.check_holes_not_nested(self)
|
135
|
+
return check unless check.nil?
|
136
|
+
|
137
|
+
check = validity_helper.check_connected_interiors(self)
|
138
|
+
return check unless check.nil?
|
139
|
+
end
|
140
|
+
|
141
|
+
nil
|
142
|
+
end
|
143
|
+
|
144
|
+
def check_valid_multi_point
|
145
|
+
geometries.each do |pt|
|
146
|
+
check = validity_helper.check_invalid_coordinate(pt)
|
147
|
+
return check unless check.nil?
|
148
|
+
end
|
149
|
+
nil
|
150
|
+
end
|
151
|
+
|
152
|
+
def check_valid_multi_polygon
|
153
|
+
geometries.each do |poly|
|
154
|
+
return poly.invalid_reason unless poly.invalid_reason.nil?
|
155
|
+
end
|
156
|
+
|
157
|
+
check = validity_helper.check_consistent_area_mp(self)
|
158
|
+
return check unless check.nil?
|
159
|
+
|
160
|
+
# check no shells are nested
|
161
|
+
check = validity_helper.check_shells_not_nested(self)
|
162
|
+
return check unless check.nil?
|
163
|
+
|
164
|
+
nil
|
165
|
+
end
|
166
|
+
|
167
|
+
def check_valid_geometry_collection
|
168
|
+
geometries.each do |geom|
|
169
|
+
return geom.invalid_reason unless geom.invalid_reason.nil?
|
170
|
+
end
|
171
|
+
|
172
|
+
nil
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
##
|
177
|
+
# Helper functions for specific validity checks
|
178
|
+
##
|
179
|
+
module ValidOpHelpers
|
180
|
+
module_function
|
181
|
+
|
182
|
+
# Checks that the given point has valid coordinates.
|
183
|
+
#
|
184
|
+
# @param pt [RGeo::Feature::Point]
|
185
|
+
#
|
186
|
+
# @return [String] invalid_reason
|
187
|
+
def check_invalid_coordinate(pt)
|
188
|
+
x = pt.x
|
189
|
+
y = pt.y
|
190
|
+
return if x.finite? && y.finite? && x.real? && y.real?
|
191
|
+
|
192
|
+
Error::INVALID_COORDINATE
|
193
|
+
end
|
194
|
+
|
195
|
+
# Checks that the edges in the polygon form a consistent area.
|
196
|
+
#
|
197
|
+
# Specifically, checks that there are intersections no between the
|
198
|
+
# holes and the shell.
|
199
|
+
#
|
200
|
+
# Also checks that there are no duplicate rings.
|
201
|
+
#
|
202
|
+
# @param poly [RGeo::Feature::Polygon]
|
203
|
+
#
|
204
|
+
# @return [String] invalid_reason
|
205
|
+
def check_consistent_area(poly)
|
206
|
+
# Holes don't cross exterior check.
|
207
|
+
exterior = poly.exterior_ring
|
208
|
+
poly.interior_rings.each do |ring|
|
209
|
+
return Error::SELF_INTERSECTION if ring.crosses?(exterior)
|
210
|
+
end
|
211
|
+
|
212
|
+
# check interiors do not cross
|
213
|
+
poly.interior_rings.combination(2).each do |ring1, ring2|
|
214
|
+
return Error::SELF_INTERSECTION if ring1.crosses?(ring2)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Duplicate rings check
|
218
|
+
rings = [exterior] + poly.interior_rings
|
219
|
+
return Error::SELF_INTERSECTION if rings.uniq.size != rings.size
|
220
|
+
|
221
|
+
nil
|
222
|
+
end
|
223
|
+
|
224
|
+
# Checks that the ring does not self-intersect. This is just a simplicity
|
225
|
+
# check on the ring.
|
226
|
+
#
|
227
|
+
# @param ring [RGeo::Feature::LinearRing]
|
228
|
+
#
|
229
|
+
# @return [String] invalid_reason
|
230
|
+
def check_no_self_intersections(ring)
|
231
|
+
return Error::SELF_INTERSECTION unless ring.simple?
|
232
|
+
end
|
233
|
+
|
234
|
+
# Check that rings do not self intersect in a polygon
|
235
|
+
#
|
236
|
+
# @param poly [RGeo::Feature::Polygon]
|
237
|
+
#
|
238
|
+
# @return [String] invalid_reason
|
239
|
+
def check_no_self_intersecting_rings(poly)
|
240
|
+
exterior = poly.exterior_ring
|
241
|
+
|
242
|
+
check = check_no_self_intersections(exterior)
|
243
|
+
return check unless check.nil?
|
244
|
+
|
245
|
+
poly.interior_rings.each do |ring|
|
246
|
+
check = check_no_self_intersections(ring)
|
247
|
+
return check unless check.nil?
|
248
|
+
end
|
249
|
+
|
250
|
+
nil
|
251
|
+
end
|
252
|
+
|
253
|
+
# Checks holes are contained inside the exterior of a polygon.
|
254
|
+
# Assuming check_consistent_area has already passed on the polygon,
|
255
|
+
# a simple point in polygon check can be done on one of the points
|
256
|
+
# in each hole to verify (since we know none of them intersect).
|
257
|
+
#
|
258
|
+
# @param poly [RGeo::Feature::Polygon]
|
259
|
+
#
|
260
|
+
# @return [String] invalid_reason
|
261
|
+
def check_holes_in_shell(poly)
|
262
|
+
# get hole-less shell as test polygon
|
263
|
+
shell = poly.exterior_ring
|
264
|
+
shell = shell.factory.polygon(shell)
|
265
|
+
|
266
|
+
poly.interior_rings.each do |interior|
|
267
|
+
test_pt = interior.start_point
|
268
|
+
unless shell.contains?(test_pt) || poly.exterior_ring.contains?(test_pt)
|
269
|
+
return Error::HOLE_OUTSIDE_SHELL
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
nil
|
274
|
+
end
|
275
|
+
|
276
|
+
# Checks that holes are not nested within each other.
|
277
|
+
#
|
278
|
+
# @param poly [RGeo::Feature::Polygon]
|
279
|
+
#
|
280
|
+
# @return [String] invalid_reason
|
281
|
+
def check_holes_not_nested(poly)
|
282
|
+
# convert holes from linear_rings to polygons
|
283
|
+
# Same logic that applies to check_holes_in_shell applies here
|
284
|
+
# since we've already passed the consistent area test, we just
|
285
|
+
# have to check if one point from each hole is contained in the other.
|
286
|
+
holes = poly.interior_rings
|
287
|
+
holes = holes.map { |v| v.factory.polygon(v) }
|
288
|
+
holes.combination(2).each do |p1, p2|
|
289
|
+
if p1.contains?(p2.exterior_ring.start_point) || p2.contains?(p1.exterior_ring.start_point)
|
290
|
+
return Error::NESTED_HOLES
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
nil
|
295
|
+
end
|
296
|
+
|
297
|
+
# Checks that the interior of the polygon is connected.
|
298
|
+
# A disconnected interior can be described by this polygon for example
|
299
|
+
# POLYGON((0 0, 10 0, 10 10, 0 10, 0 0), (5 0, 10 5, 5 10, 0 5, 5 0))
|
300
|
+
#
|
301
|
+
# Which is a square with a diamond inside of it.
|
302
|
+
#
|
303
|
+
# @param poly [RGeo::Feature::Polygon]
|
304
|
+
#
|
305
|
+
# @return [String] invalid_reason
|
306
|
+
def check_connected_interiors(poly)
|
307
|
+
# This is not proper and will flag valid geometries as invalid, but
|
308
|
+
# is an ok approximation.
|
309
|
+
# Idea is to check if a single hole has multiple points on the
|
310
|
+
# exterior ring.
|
311
|
+
poly.interior_rings.each do |ring|
|
312
|
+
touches = Set.new
|
313
|
+
ring.points.each do |pt|
|
314
|
+
touches.add(pt) if poly.exterior_ring.contains?(pt)
|
315
|
+
end
|
316
|
+
|
317
|
+
return Error::DISCONNECTED_INTERIOR if touches.size > 1
|
318
|
+
end
|
319
|
+
|
320
|
+
nil
|
321
|
+
end
|
322
|
+
|
323
|
+
# Checks that polygons do not intersect in a multipolygon.
|
324
|
+
#
|
325
|
+
# @param mp [RGeo::Feature::MultiPolygon]
|
326
|
+
#
|
327
|
+
# @return [String] invalid_reason
|
328
|
+
def check_consistent_area_mp(mp)
|
329
|
+
mp.geometries.combination(2) do |p1, p2|
|
330
|
+
if p1.exterior_ring.crosses?(p2.exterior_ring)
|
331
|
+
return Error::SELF_INTERSECTION
|
332
|
+
end
|
333
|
+
end
|
334
|
+
nil
|
335
|
+
end
|
336
|
+
|
337
|
+
# Checks that individual polygons within a multipolygon are not nested.
|
338
|
+
#
|
339
|
+
# @param mp [RGeo::Feature::MultiPolygon]
|
340
|
+
#
|
341
|
+
# @return [String] invalid_reason
|
342
|
+
def check_shells_not_nested(mp)
|
343
|
+
# Since we've passed the consistent area test, we can just check
|
344
|
+
# that one point lies in the other.
|
345
|
+
mp.geometries.combination(2) do |p1, p2|
|
346
|
+
if p1.contains?(p2.exterior_ring.start_point) || p2.contains?(p1.exterior_ring.start_point)
|
347
|
+
return Error::NESTED_SHELLS
|
348
|
+
end
|
349
|
+
end
|
350
|
+
nil
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RGeo
|
4
|
+
module ImplHelper
|
5
|
+
# This helper enforces valid geometry computation, avoiding results such
|
6
|
+
# as a 0 area for a bowtie shaped polygon. Implementations that are part
|
7
|
+
# of RGeo core should all include this.
|
8
|
+
#
|
9
|
+
# You can play around validity checks if needed:
|
10
|
+
#
|
11
|
+
# - {check_validity!} is the method that will raise if your geometry is
|
12
|
+
# not valid. Its message will be the same as {invalid_reason}.
|
13
|
+
# - {make_valid} is the method you can call to get a valid copy of the
|
14
|
+
# current geometry.
|
15
|
+
# - finally, you can bypass any checked method by prepending `unsafe_` to
|
16
|
+
# it. At your own risk.
|
17
|
+
module ValidityCheck
|
18
|
+
# Every method that should not be overriden by the validity check.
|
19
|
+
# Those methods are either accessors or very basic methods not related
|
20
|
+
# to validity checks, or are used to check validity, in which case the
|
21
|
+
# `true/false` gives a correct information, no need to raise).
|
22
|
+
UNCHECKED_METHODS = [
|
23
|
+
# Basic methods
|
24
|
+
:factory, :geometry_type, :as_text, :as_binary, :srid,
|
25
|
+
# Tests
|
26
|
+
:simple?, :closed?, :empty?,
|
27
|
+
# Accessors
|
28
|
+
:exterior_ring, :interior_rings, :[], :num_geometries, :num_interior_rings,
|
29
|
+
:geometry_n, :each, :points, :point_n, :start_point, :end_point, :x, :y, :z, :m,
|
30
|
+
# Trivial methods
|
31
|
+
:num_points,
|
32
|
+
# Comparison
|
33
|
+
:equals?, :rep_equals?, :eql?, :==, :'!='
|
34
|
+
].freeze
|
35
|
+
private_constant :UNCHECKED_METHODS
|
36
|
+
|
37
|
+
# Since methods have their unsafe_ counter part, it means that the `+`
|
38
|
+
# method would lead to having an `unsafe_+` method that is not simply
|
39
|
+
# callable. Here's a simple fallback:
|
40
|
+
SYMBOL2NAME = {
|
41
|
+
:+ => "add",
|
42
|
+
:- => "remove",
|
43
|
+
:* => "multiply"
|
44
|
+
}.tap { |h| h.default_proc = ->(_, key) { key.to_s } }.freeze
|
45
|
+
private_constant :SYMBOL2NAME
|
46
|
+
|
47
|
+
class << self
|
48
|
+
# Note for contributors: this should be called after all methods
|
49
|
+
# are loaded for a given feature classe. No worries though, this
|
50
|
+
# is tested.
|
51
|
+
def override_classes # :nodoc:
|
52
|
+
# Using pop here to be thread safe.
|
53
|
+
while (klass = classes.pop)
|
54
|
+
override(klass)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def included(klass) # :nodoc:
|
59
|
+
classes << klass
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def classes
|
65
|
+
@classes ||= []
|
66
|
+
end
|
67
|
+
|
68
|
+
def override(klass)
|
69
|
+
methods_to_check = feature_methods(klass)
|
70
|
+
|
71
|
+
klass.class_eval do
|
72
|
+
methods_to_check.each do |method_sym|
|
73
|
+
copy = "unsafe_#{SYMBOL2NAME[method_sym]}".to_sym
|
74
|
+
alias_method copy, method_sym
|
75
|
+
undef_method method_sym
|
76
|
+
define_method(method_sym) do |*args|
|
77
|
+
check_validity!
|
78
|
+
args.each do |arg|
|
79
|
+
arg.check_validity! if RGeo::Feature::Geometry.check_type(arg)
|
80
|
+
end
|
81
|
+
method(copy).call(*args)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def feature_methods(klass)
|
88
|
+
feature_defs = Set.new
|
89
|
+
klass
|
90
|
+
.ancestors
|
91
|
+
.select { |ancestor| ancestor <= RGeo::Feature::Geometry }
|
92
|
+
.each { |ancestor| feature_defs.merge(ancestor.instance_methods(false)) }
|
93
|
+
feature_defs & klass.instance_methods - UNCHECKED_METHODS
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Raises {invalid_reason} if the polygon is not valid, does nothing
|
98
|
+
# otherwise.
|
99
|
+
def check_validity!
|
100
|
+
# This method will use a cached invalid_reason for performance purposes.
|
101
|
+
# DO NOT MUTATE GEOMETRIES.
|
102
|
+
return unless invalid_reason_memo
|
103
|
+
|
104
|
+
raise Error::InvalidGeometry, invalid_reason_memo
|
105
|
+
end
|
106
|
+
|
107
|
+
# Tell why the geometry is not valid, `nil` means it is valid.
|
108
|
+
def invalid_reason
|
109
|
+
if defined?(super) == "super"
|
110
|
+
raise Error::RGeoError, "ValidityCheck MUST be loaded before " \
|
111
|
+
"definition of #{self.class}##{__method__}."
|
112
|
+
end
|
113
|
+
|
114
|
+
raise Error::UnsupportedOperation, "Method #{self.class}##{__method__} not defined."
|
115
|
+
end
|
116
|
+
|
117
|
+
# Try and make the geometry valid, this may change its shape.
|
118
|
+
# Returns a valid copy of the geometry.
|
119
|
+
def make_valid
|
120
|
+
if defined?(super) == "super"
|
121
|
+
raise Error::RGeoError, "ValidityCheck MUST be loaded before " \
|
122
|
+
"definition of #{self.class}##{__method__}."
|
123
|
+
end
|
124
|
+
|
125
|
+
raise Error::UnsupportedOperation, "Method #{self.class}##{__method__} not defined."
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def invalid_reason_memo
|
131
|
+
# `defined?` is a bit faster than `instance_variable_defined?`.
|
132
|
+
return @invalid_reason_memo if defined?(@invalid_reason_memo)
|
133
|
+
|
134
|
+
@invalid_reason_memo = invalid_reason
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
data/lib/rgeo/impl_helper.rb
CHANGED
data/lib/rgeo/version.rb
CHANGED
@@ -160,7 +160,7 @@ module RGeo
|
|
160
160
|
end
|
161
161
|
|
162
162
|
def generate_line_string(obj)
|
163
|
-
if obj.
|
163
|
+
if obj.empty?
|
164
164
|
"EMPTY"
|
165
165
|
else
|
166
166
|
"#{@begin_bracket}#{obj.points.map { |p| generate_coords(p) }.join(', ')}#{@end_bracket}"
|
@@ -168,7 +168,7 @@ module RGeo
|
|
168
168
|
end
|
169
169
|
|
170
170
|
def generate_polygon(obj)
|
171
|
-
if obj.
|
171
|
+
if obj.empty?
|
172
172
|
"EMPTY"
|
173
173
|
else
|
174
174
|
"#{@begin_bracket}#{([generate_line_string(obj.exterior_ring)] + obj.interior_rings.map { |r| generate_line_string(r) }).join(', ')}#{@end_bracket}"
|
@@ -176,7 +176,7 @@ module RGeo
|
|
176
176
|
end
|
177
177
|
|
178
178
|
def generate_geometry_collection(obj)
|
179
|
-
if obj.
|
179
|
+
if obj.empty?
|
180
180
|
"EMPTY"
|
181
181
|
else
|
182
182
|
"#{@begin_bracket}#{obj.map { |f| generate_feature(f) }.join(', ')}#{@end_bracket}"
|
@@ -184,7 +184,7 @@ module RGeo
|
|
184
184
|
end
|
185
185
|
|
186
186
|
def generate_multi_point(obj)
|
187
|
-
if obj.
|
187
|
+
if obj.empty?
|
188
188
|
"EMPTY"
|
189
189
|
else
|
190
190
|
"#{@begin_bracket}#{obj.map { |f| generate_point(f) }.join(', ')}#{@end_bracket}"
|
@@ -192,7 +192,7 @@ module RGeo
|
|
192
192
|
end
|
193
193
|
|
194
194
|
def generate_multi_line_string(obj)
|
195
|
-
if obj.
|
195
|
+
if obj.empty?
|
196
196
|
"EMPTY"
|
197
197
|
else
|
198
198
|
"#{@begin_bracket}#{obj.map { |f| generate_line_string(f) }.join(', ')}#{@end_bracket}"
|
@@ -200,7 +200,7 @@ module RGeo
|
|
200
200
|
end
|
201
201
|
|
202
202
|
def generate_multi_polygon(obj)
|
203
|
-
if obj.
|
203
|
+
if obj.empty?
|
204
204
|
"EMPTY"
|
205
205
|
else
|
206
206
|
"#{@begin_bracket}#{obj.map { |f| generate_polygon(f) }.join(', ')}#{@end_bracket}"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rgeo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0.pre.rc.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Azuma
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-03-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi-geos
|
@@ -17,14 +17,14 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '
|
20
|
+
version: '2.2'
|
21
21
|
type: :development
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '
|
27
|
+
version: '2.2'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: minitest
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,6 +81,20 @@ dependencies:
|
|
81
81
|
- - "~>"
|
82
82
|
- !ruby/object:Gem::Version
|
83
83
|
version: 1.8.1
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: yard
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0.9'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0.9'
|
84
98
|
description: RGeo is a geospatial data library for Ruby. It provides an implementation
|
85
99
|
of the Open Geospatial Consortium's Simple Features Specification, used by most
|
86
100
|
standard spatial/geographic data storage systems such as PostGIS. A number of add-on
|
@@ -90,11 +104,13 @@ email:
|
|
90
104
|
- dazuma@gmail.com
|
91
105
|
- parhameter@gmail.com
|
92
106
|
- kfdoggett@gmail.com
|
107
|
+
- buonomo.ulysse@gmail.com
|
93
108
|
executables: []
|
94
109
|
extensions:
|
95
110
|
- ext/geos_c_impl/extconf.rb
|
96
111
|
extra_rdoc_files: []
|
97
112
|
files:
|
113
|
+
- ".yardopts"
|
98
114
|
- LICENSE.txt
|
99
115
|
- README.md
|
100
116
|
- ext/geos_c_impl/analysis.c
|
@@ -110,6 +126,8 @@ files:
|
|
110
126
|
- ext/geos_c_impl/geometry.h
|
111
127
|
- ext/geos_c_impl/geometry_collection.c
|
112
128
|
- ext/geos_c_impl/geometry_collection.h
|
129
|
+
- ext/geos_c_impl/globals.c
|
130
|
+
- ext/geos_c_impl/globals.h
|
113
131
|
- ext/geos_c_impl/line_string.c
|
114
132
|
- ext/geos_c_impl/line_string.h
|
115
133
|
- ext/geos_c_impl/main.c
|
@@ -127,6 +145,9 @@ files:
|
|
127
145
|
- lib/rgeo/cartesian/feature_classes.rb
|
128
146
|
- lib/rgeo/cartesian/feature_methods.rb
|
129
147
|
- lib/rgeo/cartesian/interface.rb
|
148
|
+
- lib/rgeo/cartesian/planar_graph.rb
|
149
|
+
- lib/rgeo/cartesian/sweepline_intersector.rb
|
150
|
+
- lib/rgeo/cartesian/valid_op.rb
|
130
151
|
- lib/rgeo/coord_sys.rb
|
131
152
|
- lib/rgeo/coord_sys/cs/entities.rb
|
132
153
|
- lib/rgeo/coord_sys/cs/factories.rb
|
@@ -183,6 +204,8 @@ files:
|
|
183
204
|
- lib/rgeo/impl_helper/basic_polygon_methods.rb
|
184
205
|
- lib/rgeo/impl_helper/math.rb
|
185
206
|
- lib/rgeo/impl_helper/utils.rb
|
207
|
+
- lib/rgeo/impl_helper/valid_op.rb
|
208
|
+
- lib/rgeo/impl_helper/validity_check.rb
|
186
209
|
- lib/rgeo/version.rb
|
187
210
|
- lib/rgeo/wkrep.rb
|
188
211
|
- lib/rgeo/wkrep/wkb_generator.rb
|
@@ -204,11 +227,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
204
227
|
version: 2.5.0
|
205
228
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
206
229
|
requirements:
|
207
|
-
- - "
|
230
|
+
- - ">"
|
208
231
|
- !ruby/object:Gem::Version
|
209
|
-
version:
|
232
|
+
version: 1.3.1
|
210
233
|
requirements: []
|
211
|
-
rubygems_version: 3.
|
234
|
+
rubygems_version: 3.1.4
|
212
235
|
signing_key:
|
213
236
|
specification_version: 4
|
214
237
|
summary: RGeo is a geospatial data library for Ruby.
|