rgeo 2.4.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -0
  3. data/README.md +1 -0
  4. data/ext/geos_c_impl/analysis.c +4 -4
  5. data/ext/geos_c_impl/errors.c +8 -6
  6. data/ext/geos_c_impl/errors.h +7 -3
  7. data/ext/geos_c_impl/extconf.rb +2 -0
  8. data/ext/geos_c_impl/factory.c +80 -7
  9. data/ext/geos_c_impl/factory.h +28 -14
  10. data/ext/geos_c_impl/geometry.c +46 -14
  11. data/ext/geos_c_impl/geometry.h +7 -0
  12. data/ext/geos_c_impl/geometry_collection.c +2 -104
  13. data/ext/geos_c_impl/geometry_collection.h +0 -11
  14. data/ext/geos_c_impl/line_string.c +1 -1
  15. data/ext/geos_c_impl/point.c +1 -1
  16. data/ext/geos_c_impl/polygon.c +1 -37
  17. data/ext/geos_c_impl/preface.h +3 -0
  18. data/lib/rgeo/cartesian/calculations.rb +54 -17
  19. data/lib/rgeo/cartesian/factory.rb +0 -7
  20. data/lib/rgeo/cartesian/feature_classes.rb +66 -46
  21. data/lib/rgeo/cartesian/feature_methods.rb +51 -20
  22. data/lib/rgeo/cartesian/interface.rb +0 -6
  23. data/lib/rgeo/cartesian/planar_graph.rb +379 -0
  24. data/lib/rgeo/cartesian/sweepline_intersector.rb +149 -0
  25. data/lib/rgeo/cartesian/valid_op.rb +71 -0
  26. data/lib/rgeo/cartesian.rb +3 -0
  27. data/lib/rgeo/coord_sys/cs/wkt_parser.rb +6 -6
  28. data/lib/rgeo/error.rb +15 -0
  29. data/lib/rgeo/feature/geometry.rb +28 -28
  30. data/lib/rgeo/feature/geometry_collection.rb +13 -5
  31. data/lib/rgeo/feature/line_string.rb +3 -3
  32. data/lib/rgeo/feature/multi_surface.rb +3 -3
  33. data/lib/rgeo/feature/point.rb +4 -4
  34. data/lib/rgeo/feature/surface.rb +3 -3
  35. data/lib/rgeo/geographic/factory.rb +0 -7
  36. data/lib/rgeo/geographic/interface.rb +5 -20
  37. data/lib/rgeo/geographic/proj4_projector.rb +0 -2
  38. data/lib/rgeo/geographic/projected_feature_classes.rb +21 -9
  39. data/lib/rgeo/geographic/projected_feature_methods.rb +51 -28
  40. data/lib/rgeo/geographic/simple_mercator_projector.rb +0 -2
  41. data/lib/rgeo/geographic/spherical_feature_classes.rb +29 -9
  42. data/lib/rgeo/geographic/spherical_feature_methods.rb +62 -1
  43. data/lib/rgeo/geos/capi_factory.rb +21 -31
  44. data/lib/rgeo/geos/capi_feature_classes.rb +35 -11
  45. data/lib/rgeo/geos/ffi_factory.rb +0 -28
  46. data/lib/rgeo/geos/ffi_feature_classes.rb +34 -10
  47. data/lib/rgeo/geos/ffi_feature_methods.rb +23 -5
  48. data/lib/rgeo/geos/interface.rb +0 -7
  49. data/lib/rgeo/geos/zm_factory.rb +0 -12
  50. data/lib/rgeo/impl_helper/basic_geometry_collection_methods.rb +4 -4
  51. data/lib/rgeo/impl_helper/basic_geometry_methods.rb +1 -1
  52. data/lib/rgeo/impl_helper/basic_line_string_methods.rb +15 -19
  53. data/lib/rgeo/impl_helper/basic_point_methods.rb +1 -1
  54. data/lib/rgeo/impl_helper/basic_polygon_methods.rb +1 -1
  55. data/lib/rgeo/impl_helper/valid_op.rb +354 -0
  56. data/lib/rgeo/impl_helper/validity_check.rb +138 -0
  57. data/lib/rgeo/impl_helper.rb +1 -0
  58. data/lib/rgeo/version.rb +1 -1
  59. metadata +31 -10
@@ -20,9 +20,6 @@ module RGeo
20
20
  # See RGeo::Geos.factory for a list of supported options.
21
21
 
22
22
  def initialize(opts = {})
23
- # Main flags
24
- @uses_lenient_multi_polygon_assertions = opts[:uses_lenient_assertions] ||
25
- opts[:lenient_multi_polygon_assertions] || opts[:uses_lenient_multi_polygon_assertions]
26
23
  @has_z = opts[:has_z_coordinate] ? true : false
27
24
  @has_m = opts[:has_m_coordinate] ? true : false
28
25
  if @has_z && @has_m
@@ -145,7 +142,6 @@ module RGeo
145
142
  "wkbg" => @wkb_generator.properties,
146
143
  "wktp" => @wkt_parser.properties,
147
144
  "wkbp" => @wkb_parser.properties,
148
- "lmpa" => @uses_lenient_multi_polygon_assertions,
149
145
  "apre" => @_auto_prepare
150
146
  }
151
147
  hash["proj4"] = @proj4.marshal_dump if @proj4
@@ -174,7 +170,6 @@ module RGeo
174
170
  wkb_generator: symbolize_hash(data["wkbg"]),
175
171
  wkt_parser: symbolize_hash(data["wktp"]),
176
172
  wkb_parser: symbolize_hash(data["wkbp"]),
177
- uses_lenient_multi_polygon_assertions: data["lmpa"],
178
173
  auto_prepare: (data["apre"] ? :simple : :disabled),
179
174
  proj4: proj4,
180
175
  coord_sys: coord_sys
@@ -188,7 +183,6 @@ module RGeo
188
183
  coder["has_m_coordinate"] = @has_m
189
184
  coder["srid"] = @srid
190
185
  coder["buffer_resolution"] = @buffer_resolution
191
- coder["lenient_multi_polygon_assertions"] = @uses_lenient_multi_polygon_assertions
192
186
  coder["wkt_generator"] = @wkt_generator.properties
193
187
  coder["wkb_generator"] = @wkb_generator.properties
194
188
  coder["wkt_parser"] = @wkt_parser.properties
@@ -227,7 +221,6 @@ module RGeo
227
221
  wkt_parser: symbolize_hash(coder["wkt_parser"]),
228
222
  wkb_parser: symbolize_hash(coder["wkb_parser"]),
229
223
  auto_prepare: coder["auto_prepare"] == "disabled" ? :disabled : :simple,
230
- uses_lenient_multi_polygon_assertions: coder["lenient_multi_polygon_assertions"],
231
224
  proj4: proj4,
232
225
  coord_sys: coord_sys
233
226
  )
@@ -242,14 +235,7 @@ module RGeo
242
235
 
243
236
  attr_reader :buffer_resolution
244
237
 
245
- # Returns true if this factory is lenient with MultiPolygon assertions
246
-
247
- def lenient_multi_polygon_assertions?
248
- @uses_lenient_multi_polygon_assertions
249
- end
250
-
251
238
  # See RGeo::Feature::Factory#property
252
-
253
239
  def property(name_)
254
240
  case name_
255
241
  when :has_z_coordinate
@@ -260,15 +246,12 @@ module RGeo
260
246
  true
261
247
  when :buffer_resolution
262
248
  @buffer_resolution
263
- when :uses_lenient_multi_polygon_assertions
264
- @uses_lenient_multi_polygon_assertions
265
249
  when :auto_prepare
266
250
  @_auto_prepare ? :simple : :disabled
267
251
  end
268
252
  end
269
253
 
270
254
  # See RGeo::Feature::Factory#parse_wkt
271
-
272
255
  def parse_wkt(str)
273
256
  if @wkt_reader
274
257
  wrap_fg_geom(@wkt_reader.read(str), nil)
@@ -417,17 +400,6 @@ module RGeo
417
400
  raise(RGeo::Error::InvalidGeometry, "Could not cast to polygon: #{elem}") unless elem
418
401
  elem.detach_fg_geom
419
402
  end
420
- unless @uses_lenient_multi_polygon_assertions
421
- (1...elems.size).each do |i|
422
- (0...i).each do |j|
423
- igeom = elems[i]
424
- jgeom = elems[j]
425
- if igeom.relate_pattern(jgeom, "2********") || igeom.relate_pattern(jgeom, "****1****")
426
- raise(RGeo::Error::InvalidGeometry, "Invalid relate pattern: #{jgeom}")
427
- end
428
- end
429
- end
430
- end
431
403
  klasses = Array.new(elems.size, FFIPolygonImpl)
432
404
  fg_geom = ::Geos::Utils.create_collection(::Geos::GeomTypes::GEOS_MULTIPOLYGON, elems)
433
405
  FFIMultiPolygonImpl.new(self, fg_geom, klasses)
@@ -6,60 +6,84 @@
6
6
  #
7
7
  # -----------------------------------------------------------------------------
8
8
 
9
+ require_relative "../impl_helper/validity_check"
10
+
9
11
  module RGeo
10
12
  module Geos
11
- class FFIGeometryImpl # :nodoc:
13
+ class FFIGeometryImpl
14
+ include Feature::Geometry
15
+ include ImplHelper::ValidityCheck
12
16
  include FFIGeometryMethods
13
17
  end
14
18
 
15
- class FFIPointImpl # :nodoc:
19
+ class FFIPointImpl
20
+ include Feature::Point
21
+ include ImplHelper::ValidityCheck
16
22
  include FFIGeometryMethods
17
23
  include FFIPointMethods
18
24
  end
19
25
 
20
- class FFILineStringImpl # :nodoc:
26
+ class FFILineStringImpl
27
+ include Feature::LineString
28
+ include ImplHelper::ValidityCheck
21
29
  include FFIGeometryMethods
22
30
  include FFILineStringMethods
23
31
  end
24
32
 
25
- class FFILinearRingImpl # :nodoc:
33
+ class FFILinearRingImpl
34
+ include Feature::LinearRing
35
+ include ImplHelper::ValidityCheck
26
36
  include FFIGeometryMethods
27
37
  include FFILineStringMethods
28
38
  include FFILinearRingMethods
29
39
  end
30
40
 
31
- class FFILineImpl # :nodoc:
41
+ class FFILineImpl
42
+ include Feature::Line
43
+ include ImplHelper::ValidityCheck
32
44
  include FFIGeometryMethods
33
45
  include FFILineStringMethods
34
46
  include FFILineMethods
35
47
  end
36
48
 
37
- class FFIPolygonImpl # :nodoc:
49
+ class FFIPolygonImpl
50
+ include Feature::Polygon
51
+ include ImplHelper::ValidityCheck
38
52
  include FFIGeometryMethods
39
53
  include FFIPolygonMethods
40
54
  end
41
55
 
42
- class FFIGeometryCollectionImpl # :nodoc:
56
+ class FFIGeometryCollectionImpl
57
+ include Feature::GeometryCollection
58
+ include ImplHelper::ValidityCheck
43
59
  include FFIGeometryMethods
44
60
  include FFIGeometryCollectionMethods
45
61
  end
46
62
 
47
- class FFIMultiPointImpl # :nodoc:
63
+ class FFIMultiPointImpl
64
+ include Feature::MultiPoint
65
+ include ImplHelper::ValidityCheck
48
66
  include FFIGeometryMethods
49
67
  include FFIGeometryCollectionMethods
50
68
  include FFIMultiPointMethods
51
69
  end
52
70
 
53
- class FFIMultiLineStringImpl # :nodoc:
71
+ class FFIMultiLineStringImpl
72
+ include Feature::MultiLineString
73
+ include ImplHelper::ValidityCheck
54
74
  include FFIGeometryMethods
55
75
  include FFIGeometryCollectionMethods
56
76
  include FFIMultiLineStringMethods
57
77
  end
58
78
 
59
- class FFIMultiPolygonImpl # :nodoc:
79
+ class FFIMultiPolygonImpl
80
+ include Feature::MultiPolygon
81
+ include ImplHelper::ValidityCheck
60
82
  include FFIGeometryMethods
61
83
  include FFIGeometryCollectionMethods
62
84
  include FFIMultiPolygonMethods
63
85
  end
86
+
87
+ ImplHelper::ValidityCheck.override_classes
64
88
  end
65
89
  end
@@ -6,6 +6,8 @@
6
6
  #
7
7
  # -----------------------------------------------------------------------------
8
8
 
9
+ require "ffi-geos"
10
+
9
11
  module RGeo
10
12
  module Geos
11
13
  module FFIGeometryMethods # :nodoc:
@@ -95,11 +97,9 @@ module RGeo
95
97
  end
96
98
 
97
99
  def boundary
98
- if self.class == FFIGeometryCollectionImpl
99
- nil
100
- else
101
- @factory.wrap_fg_geom(@fg_geom.boundary, nil)
102
- end
100
+ @factory.wrap_fg_geom(@fg_geom.boundary, nil)
101
+ rescue ::Geos::GEOSException
102
+ raise Error::InvalidGeometry, "Operation not supported by GeometryCollection"
103
103
  end
104
104
 
105
105
  def as_text
@@ -131,6 +131,24 @@ module RGeo
131
131
  simple?
132
132
  end
133
133
 
134
+ def valid?
135
+ @fg_geom.valid?
136
+ end
137
+
138
+ def invalid_reason
139
+ # valid_detail gives solely the reason, or nil if valid, which is
140
+ # what we want.
141
+ fg_geom.valid_detail&.dig(:detail)&.force_encoding(Encoding::UTF_8)
142
+ end
143
+
144
+ # (see RGeo::ImplHelper::ValidityCheck#make_valid)
145
+ # Only available since GEOS 3.8+
146
+ def make_valid
147
+ @factory.wrap_fg_geom(@fg_geom.make_valid, nil)
148
+ rescue ::Geos::GEOSException
149
+ raise Error::UnsupportedOperation
150
+ end if ::Geos::FFIGeos.respond_to?(:GEOSMakeValid_r)
151
+
134
152
  def equals?(rhs)
135
153
  return false unless rhs.is_a?(RGeo::Feature::Instance)
136
154
  fg = factory.convert_to_fg_geometry(rhs)
@@ -116,13 +116,6 @@ module RGeo
116
116
  # Specifies which native interface to use. Possible values are
117
117
  # <tt>:capi</tt> and <tt>:ffi</tt>. The default is the value
118
118
  # of the preferred_native_interface.
119
- # [<tt>:uses_lenient_multi_polygon_assertions</tt>]
120
- # If set to true, assertion checking on MultiPolygon is disabled.
121
- # This may speed up creation of MultiPolygon objects, at the
122
- # expense of not doing the proper checking for OGC MultiPolygon
123
- # compliance. See RGeo::Feature::MultiPolygon for details on
124
- # the MultiPolygon assertions. Default is false. Also called
125
- # <tt>:lenient_multi_polygon_assertions</tt>.
126
119
  # [<tt>:buffer_resolution</tt>]
127
120
  # The resolution of buffers around geometries created by this
128
121
  # factory. This controls the number of line segments used to
@@ -53,8 +53,6 @@ module RGeo
53
53
  end
54
54
  srid ||= coord_sys.authority_code if coord_sys
55
55
  config = {
56
- uses_lenient_multi_polygon_assertions: opts[:lenient_multi_polygon_assertions] ||
57
- opts[:uses_lenient_multi_polygon_assertions],
58
56
  buffer_resolution: opts[:buffer_resolution], auto_prepare: opts[:auto_prepare],
59
57
  wkt_generator: opts[:wkt_generator], wkt_parser: opts[:wkt_parser],
60
58
  wkb_generator: opts[:wkb_generator], wkb_parser: opts[:wkb_parser],
@@ -109,7 +107,6 @@ module RGeo
109
107
  "wkbg" => @wkb_generator.properties,
110
108
  "wktp" => @wkt_parser.properties,
111
109
  "wkbp" => @wkb_parser.properties,
112
- "lmpa" => @zfactory.lenient_multi_polygon_assertions?,
113
110
  "apre" => @zfactory.property(:auto_prepare) == :simple,
114
111
  "nffi" => @zfactory.is_a?(FFIFactory)
115
112
  }
@@ -142,7 +139,6 @@ module RGeo
142
139
  wkb_generator: symbolize_hash(data["wkbg"]),
143
140
  wkt_parser: symbolize_hash(data["wktp"]),
144
141
  wkb_parser: symbolize_hash(data["wkbp"]),
145
- uses_lenient_multi_polygon_assertions: data["lmpa"],
146
142
  auto_prepare: (data["apre"] ? :simple : :disabled),
147
143
  proj4: proj4,
148
144
  coord_sys: coord_sys
@@ -154,7 +150,6 @@ module RGeo
154
150
  def encode_with(coder) # :nodoc:
155
151
  coder["srid"] = @zfactory.srid
156
152
  coder["buffer_resolution"] = @zfactory.buffer_resolution
157
- coder["lenient_multi_polygon_assertions"] = @zfactory.lenient_multi_polygon_assertions?
158
153
  coder["wkt_generator"] = @wkt_generator.properties
159
154
  coder["wkb_generator"] = @wkb_generator.properties
160
155
  coder["wkt_parser"] = @wkt_parser.properties
@@ -197,7 +192,6 @@ module RGeo
197
192
  wkt_parser: symbolize_hash(coder["wkt_parser"]),
198
193
  wkb_parser: symbolize_hash(coder["wkb_parser"]),
199
194
  auto_prepare: coder["auto_prepare"] == "disabled" ? :disabled : :simple,
200
- uses_lenient_multi_polygon_assertions: coder["lenient_multi_polygon_assertions"],
201
195
  proj4: proj4,
202
196
  coord_sys: coord_sys
203
197
  )
@@ -216,12 +210,6 @@ module RGeo
216
210
  @zfactory.buffer_resolution
217
211
  end
218
212
 
219
- # Returns true if this factory is lenient with MultiPolygon assertions
220
-
221
- def lenient_multi_polygon_assertions?
222
- @zfactory.lenient_multi_polygon_assertions?
223
- end
224
-
225
213
  # Returns the z-only factory corresponding to this factory.
226
214
 
227
215
  def z_factory
@@ -20,7 +20,7 @@ module RGeo
20
20
  raise Error::InvalidGeometry, "Could not cast #{elem}" unless elem
21
21
  elem
22
22
  end
23
- validate_geometry
23
+ init_geometry
24
24
  end
25
25
 
26
26
  def num_geometries
@@ -91,7 +91,7 @@ module RGeo
91
91
  raise Error::InvalidGeometry, "Could not cast #{elem}" unless elem
92
92
  elem
93
93
  end
94
- validate_geometry
94
+ init_geometry
95
95
  end
96
96
 
97
97
  def geometry_type
@@ -152,7 +152,7 @@ module RGeo
152
152
  raise Error::InvalidGeometry, "Could not cast #{elem}" unless elem
153
153
  elem
154
154
  end
155
- validate_geometry
155
+ init_geometry
156
156
  end
157
157
 
158
158
  def geometry_type
@@ -176,7 +176,7 @@ module RGeo
176
176
  raise Error::InvalidGeometry, "Could not cast #{elem}" unless elem
177
177
  elem
178
178
  end
179
- validate_geometry
179
+ init_geometry
180
180
  end
181
181
 
182
182
  def geometry_type
@@ -52,7 +52,7 @@ module RGeo
52
52
  @factory = obj.factory
53
53
  end
54
54
 
55
- def validate_geometry
55
+ def init_geometry
56
56
  end
57
57
  end
58
58
  end
@@ -16,7 +16,13 @@ module RGeo
16
16
  raise Error::InvalidGeometry, "Could not cast #{elem}" unless elem
17
17
  elem
18
18
  end
19
- validate_geometry
19
+ # LineStrings in general need to check that there's not one point
20
+ # GEOS doesn't allow instantiation of single point LineStrings so
21
+ # we should handle it.
22
+ if @points.size == 1
23
+ raise Error::InvalidGeometry, "LineString Cannot Have 1 Point"
24
+ end
25
+ init_geometry
20
26
  end
21
27
 
22
28
  def num_points
@@ -143,12 +149,6 @@ module RGeo
143
149
  super
144
150
  @points = obj.points
145
151
  end
146
-
147
- def validate_geometry
148
- if @points.size == 1
149
- raise Error::InvalidGeometry, "LineString cannot have 1 point"
150
- end
151
- end
152
152
  end
153
153
 
154
154
  module BasicLineMethods # :nodoc:
@@ -161,7 +161,7 @@ module RGeo
161
161
  cstop = Feature.cast(stop, factory, Feature::Point)
162
162
  raise Error::InvalidGeometry, "Could not cast end: #{stop}" unless cstop
163
163
  @points = [cstart, cstop]
164
- validate_geometry
164
+ init_geometry
165
165
  end
166
166
 
167
167
  def geometry_type
@@ -171,18 +171,16 @@ module RGeo
171
171
  def coordinates
172
172
  @points.map(&:coordinates)
173
173
  end
174
+ end
174
175
 
175
- private
176
-
177
- def validate_geometry
176
+ module BasicLinearRingMethods # :nodoc:
177
+ def initialize(factory, points)
178
178
  super
179
- if @points.size > 2
180
- raise Error::InvalidGeometry, "Line must have 0 or 2 points"
179
+ unless @points.size >= 4 || @points.size == 0
180
+ raise Error::InvalidGeometry, "LinearRings must have 0 or >= 4 points"
181
181
  end
182
182
  end
183
- end
184
183
 
185
- module BasicLinearRingMethods # :nodoc:
186
184
  def geometry_type
187
185
  Feature::LinearRing
188
186
  end
@@ -193,14 +191,12 @@ module RGeo
193
191
 
194
192
  private
195
193
 
196
- def validate_geometry
194
+ # Close ring if necessary.
195
+ def init_geometry
197
196
  super
198
197
  if @points.size > 0
199
198
  @points << @points.first if @points.first != @points.last
200
199
  @points = @points.chunk { |x| x }.map(&:first)
201
- if !@factory.property(:uses_lenient_assertions) && !ring?
202
- raise Error::InvalidGeometry, "LinearRing failed ring test"
203
- end
204
200
  end
205
201
  end
206
202
  end
@@ -18,7 +18,7 @@ module RGeo
18
18
  if extra.size > 0
19
19
  raise ArgumentError, "Too many arguments for point initializer"
20
20
  end
21
- validate_geometry
21
+ init_geometry
22
22
  end
23
23
 
24
24
  def x
@@ -22,7 +22,7 @@ module RGeo
22
22
  end
23
23
  elem
24
24
  end
25
- validate_geometry
25
+ init_geometry
26
26
  end
27
27
 
28
28
  def exterior_ring