rgeo 2.4.0 → 3.0.0.pre.rc.1

Sign up to get free protection for your applications and to get access to all the features.
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