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
@@ -69,41 +69,78 @@ module RGeo
69
69
  end
70
70
 
71
71
  def intersects_segment?(seg)
72
+ !segment_intersection(seg).nil?
73
+ end
74
+
75
+ # If this and the other segment intersect, this method will return the coordinate
76
+ # at which they intersect, otherwise nil.
77
+ # In the case of a partial overlap (parallel segments), this will return
78
+ # a single point on the overlapping portion.
79
+ #
80
+ # @param seg [Segment]
81
+ #
82
+ # @return [RGeo::Feature::Point, nil]
83
+ def segment_intersection(seg)
72
84
  s2 = seg.s
73
85
  # Handle degenerate cases
74
86
  if seg.degenerate?
75
- if @lensq == 0
76
- return @s == s2
87
+ if @lensq == 0 && @s == s2
88
+ return @s
77
89
  else
78
- return contains_point?(s2)
90
+ return contains_point?(s2) ? s2 : nil
79
91
  end
80
92
  elsif @lensq == 0
81
- return seg.contains_point?(@s)
93
+ return seg.contains_point?(@s) ? @s : nil
82
94
  end
95
+
83
96
  # Both segments have nonzero length.
84
97
  sx2 = s2.x
85
98
  sy2 = s2.y
86
99
  dx2 = seg.dx
87
100
  dy2 = seg.dy
88
101
  denom = @dx * dy2 - @dy * dx2
102
+
89
103
  if denom == 0
90
104
  # Segments are parallel. Make sure they are collinear.
91
- return false unless side(s2) == 0
92
- # 1-D check.
93
- ts = (@dx * (sx2 - @sx) + @dy * (sy2 - @sy)) / @lensq
94
- te = (@dx * (sx2 + dx2 - @sx) + @dy * (sy2 + dy2 - @sy)) / @lensq
95
- if ts < te
96
- te >= 0.0 && ts <= 1.0
97
- else
98
- ts >= 0.0 && te <= 1.0
99
- end
105
+ return nil unless side(s2) == 0
106
+
107
+ # return the first point it finds that intersects another line.
108
+ # In many cases, the intersection is actually another line
109
+ # segment, but for now, we will just return a single point.
110
+ return s2 if contains_point?(s2)
111
+ return seg.e if contains_point?(seg.e)
112
+ return @s if seg.contains_point?(@s)
113
+ return @e if seg.contains_point?(@e)
114
+ nil
100
115
  else
101
116
  # Segments are not parallel. Check the intersection of their
102
117
  # containing lines.
103
- t = (dy2 * (sx2 - @sx) + dx2 * (@sy - sy2)) / denom
104
- return false if t < 0.0 || t > 1.0
105
- t2 = (@dy * (sx2 - @sx) + @dx * (@sy - sy2)) / denom
106
- t2 >= 0.0 && t2 <= 1.0
118
+ num1 = dx2 * (@sy - sy2) - (dy2 * (@sx - sx2))
119
+ num2 = @dx * (@sy - sy2) - (@dy * (@sx - sx2))
120
+ cross1 = num1 / denom
121
+ cross2 = num2 / denom
122
+
123
+ return nil if cross1 < 0.0 || cross1 > 1.0
124
+ if cross2 >= 0.0 && cross2 <= 1.0
125
+ x = @sx + (cross1 * @dx)
126
+ y = @sy + (cross1 * @dy)
127
+
128
+ # Check if this segment contains the point.
129
+ # Sometimes round-off errors occur and intersections
130
+ # are recorded as off the line segments.
131
+ #
132
+ # If this is the case, return the closest point from
133
+ # either segment.
134
+ int_pt = @s.factory.point(x, y)
135
+ if contains_point?(int_pt)
136
+ int_pt
137
+ else
138
+ # find closest of @s, @e, seg.s, seg.e
139
+ [@e, seg.s, seg.e].reduce(@s) do |closest, pt|
140
+ int_pt.distance(pt) < int_pt.distance(closest) ? pt : closest
141
+ end
142
+ end
143
+ end
107
144
  end
108
145
  end
109
146
 
@@ -42,7 +42,6 @@ module RGeo
42
42
  end
43
43
  srid ||= @coord_sys.authority_code if @coord_sys
44
44
  @srid = srid.to_i
45
- @lenient_assertions = opts[:uses_lenient_assertions] ? true : false
46
45
  @buffer_resolution = opts[:buffer_resolution].to_i
47
46
  @buffer_resolution = 1 if @buffer_resolution < 1
48
47
 
@@ -103,7 +102,6 @@ module RGeo
103
102
  "wkbg" => @wkb_generator.properties,
104
103
  "wktp" => @wkt_parser.properties,
105
104
  "wkbp" => @wkb_parser.properties,
106
- "lena" => @lenient_assertions,
107
105
  "bufr" => @buffer_resolution
108
106
  }
109
107
  hash_["proj4"] = @proj4.marshal_dump if @proj4
@@ -131,7 +129,6 @@ module RGeo
131
129
  wkb_generator: symbolize_hash(data["wkbg"]),
132
130
  wkt_parser: symbolize_hash(data["wktp"]),
133
131
  wkb_parser: symbolize_hash(data["wkbp"]),
134
- uses_lenient_assertions: data["lena"],
135
132
  buffer_resolution: data["bufr"],
136
133
  proj4: proj4,
137
134
  coord_sys: coord_sys
@@ -144,7 +141,6 @@ module RGeo
144
141
  coder["has_z_coordinate"] = @has_z
145
142
  coder["has_m_coordinate"] = @has_m
146
143
  coder["srid"] = @srid
147
- coder["lenient_assertions"] = @lenient_assertions
148
144
  coder["buffer_resolution"] = @buffer_resolution
149
145
  coder["wkt_generator"] = @wkt_generator.properties
150
146
  coder["wkb_generator"] = @wkb_generator.properties
@@ -180,7 +176,6 @@ module RGeo
180
176
  wkb_generator: symbolize_hash(coder["wkb_generator"]),
181
177
  wkt_parser: symbolize_hash(coder["wkt_parser"]),
182
178
  wkb_parser: symbolize_hash(coder["wkb_parser"]),
183
- uses_lenient_assertions: coder["lenient_assertions"],
184
179
  buffer_resolution: coder["buffer_resolution"],
185
180
  proj4: proj4,
186
181
  coord_sys: coord_sys
@@ -199,8 +194,6 @@ module RGeo
199
194
  @has_z
200
195
  when :has_m_coordinate
201
196
  @has_m
202
- when :uses_lenient_assertions
203
- @lenient_assertions
204
197
  when :buffer_resolution
205
198
  @buffer_resolution
206
199
  when :is_cartesian
@@ -9,76 +9,96 @@
9
9
  module RGeo
10
10
  module Cartesian
11
11
  class PointImpl # :nodoc:
12
- include RGeo::Feature::Point
13
- include RGeo::ImplHelper::BasicGeometryMethods
14
- include RGeo::ImplHelper::BasicPointMethods
15
- include RGeo::Cartesian::GeometryMethods
16
- include RGeo::Cartesian::PointMethods
12
+ include Feature::Point
13
+ include ImplHelper::ValidityCheck
14
+ include ImplHelper::BasicGeometryMethods
15
+ include ImplHelper::BasicPointMethods
16
+ include ImplHelper::ValidOp
17
+ include GeometryMethods
18
+ include PointMethods
17
19
  end
18
20
 
19
21
  class LineStringImpl # :nodoc:
20
- include RGeo::Feature::LineString
21
- include RGeo::ImplHelper::BasicGeometryMethods
22
- include RGeo::ImplHelper::BasicLineStringMethods
23
- include RGeo::Cartesian::GeometryMethods
24
- include RGeo::Cartesian::LineStringMethods
22
+ include Feature::LineString
23
+ include ImplHelper::ValidityCheck
24
+ include ImplHelper::BasicGeometryMethods
25
+ include ImplHelper::BasicLineStringMethods
26
+ include ImplHelper::ValidOp
27
+ include GeometryMethods
28
+ include LineStringMethods
25
29
  end
26
30
 
27
31
  class LineImpl # :nodoc:
28
- include RGeo::Feature::Line
29
- include RGeo::ImplHelper::BasicGeometryMethods
30
- include RGeo::ImplHelper::BasicLineStringMethods
31
- include RGeo::ImplHelper::BasicLineMethods
32
- include RGeo::Cartesian::GeometryMethods
33
- include RGeo::Cartesian::LineStringMethods
32
+ include Feature::Line
33
+ include ImplHelper::ValidityCheck
34
+ include ImplHelper::BasicGeometryMethods
35
+ include ImplHelper::BasicLineStringMethods
36
+ include ImplHelper::BasicLineMethods
37
+ include ImplHelper::ValidOp
38
+ include GeometryMethods
39
+ include LineStringMethods
34
40
  end
35
41
 
36
42
  class LinearRingImpl # :nodoc:
37
- include RGeo::Feature::LinearRing
38
- include RGeo::ImplHelper::BasicGeometryMethods
39
- include RGeo::ImplHelper::BasicLineStringMethods
40
- include RGeo::ImplHelper::BasicLinearRingMethods
41
- include RGeo::Cartesian::GeometryMethods
42
- include RGeo::Cartesian::LineStringMethods
43
+ include Feature::LinearRing
44
+ include ImplHelper::ValidityCheck
45
+ include ImplHelper::BasicGeometryMethods
46
+ include ImplHelper::BasicLineStringMethods
47
+ include ImplHelper::BasicLinearRingMethods
48
+ include ImplHelper::ValidOp
49
+ include GeometryMethods
50
+ include LineStringMethods
43
51
  end
44
52
 
45
53
  class PolygonImpl # :nodoc:
46
- include RGeo::Feature::Polygon
47
- include RGeo::ImplHelper::BasicGeometryMethods
48
- include RGeo::ImplHelper::BasicPolygonMethods
49
- include RGeo::Cartesian::GeometryMethods
54
+ include Feature::Polygon
55
+ include ImplHelper::ValidityCheck
56
+ include ImplHelper::BasicGeometryMethods
57
+ include ImplHelper::BasicPolygonMethods
58
+ include ValidOp
59
+ include GeometryMethods
50
60
  end
51
61
 
52
62
  class GeometryCollectionImpl # :nodoc:
53
- include RGeo::Feature::GeometryCollection
54
- include RGeo::ImplHelper::BasicGeometryMethods
55
- include RGeo::ImplHelper::BasicGeometryCollectionMethods
56
- include RGeo::Cartesian::GeometryMethods
63
+ include Feature::GeometryCollection
64
+ include ImplHelper::ValidityCheck
65
+ include ImplHelper::BasicGeometryMethods
66
+ include ImplHelper::BasicGeometryCollectionMethods
67
+ include ImplHelper::ValidOp
68
+ include GeometryMethods
57
69
  end
58
70
 
59
71
  class MultiPointImpl # :nodoc:
60
- include RGeo::Feature::MultiPoint
61
- include RGeo::ImplHelper::BasicGeometryMethods
62
- include RGeo::ImplHelper::BasicGeometryCollectionMethods
63
- include RGeo::ImplHelper::BasicMultiPointMethods
64
- include RGeo::Cartesian::GeometryMethods
72
+ include Feature::MultiPoint
73
+ include ImplHelper::ValidityCheck
74
+ include ImplHelper::BasicGeometryMethods
75
+ include ImplHelper::BasicGeometryCollectionMethods
76
+ include ImplHelper::BasicMultiPointMethods
77
+ include ImplHelper::ValidOp
78
+ include GeometryMethods
65
79
  end
66
80
 
67
81
  class MultiLineStringImpl # :nodoc:
68
- include RGeo::Feature::MultiLineString
69
- include RGeo::ImplHelper::BasicGeometryMethods
70
- include RGeo::ImplHelper::BasicGeometryCollectionMethods
71
- include RGeo::ImplHelper::BasicMultiLineStringMethods
72
- include RGeo::Cartesian::GeometryMethods
73
- include RGeo::Cartesian::MultiLineStringMethods
82
+ include Feature::MultiLineString
83
+ include ImplHelper::ValidityCheck
84
+ include ImplHelper::BasicGeometryMethods
85
+ include ImplHelper::BasicGeometryCollectionMethods
86
+ include ImplHelper::BasicMultiLineStringMethods
87
+ include ImplHelper::ValidOp
88
+ include GeometryMethods
89
+ include MultiLineStringMethods
74
90
  end
75
91
 
76
92
  class MultiPolygonImpl # :nodoc:
77
- include RGeo::Feature::MultiPolygon
78
- include RGeo::ImplHelper::BasicGeometryMethods
79
- include RGeo::ImplHelper::BasicGeometryCollectionMethods
80
- include RGeo::ImplHelper::BasicMultiPolygonMethods
81
- include RGeo::Cartesian::GeometryMethods
93
+ include Feature::MultiPolygon
94
+ include ImplHelper::ValidityCheck
95
+ include ImplHelper::BasicGeometryMethods
96
+ include ImplHelper::BasicGeometryCollectionMethods
97
+ include ImplHelper::BasicMultiPolygonMethods
98
+ include ImplHelper::ValidOp
99
+ include GeometryMethods
82
100
  end
101
+
102
+ ImplHelper::ValidityCheck.override_classes
83
103
  end
84
104
  end
@@ -16,6 +16,12 @@ module RGeo
16
16
  def envelope
17
17
  BoundingBox.new(factory).add(self).to_geometry
18
18
  end
19
+
20
+ private
21
+
22
+ def graph
23
+ @graph ||= GeometryGraph.new(self)
24
+ end
19
25
  end
20
26
 
21
27
  module PointMethods # :nodoc:
@@ -50,26 +56,14 @@ module RGeo
50
56
  end
51
57
 
52
58
  def simple?
53
- len = segments.length
54
- return false if segments.any?(&:degenerate?)
55
- return true if len == 1
56
- return segments[0].s != segments[1].e if len == 2
57
- segments.each_with_index do |seg, index|
58
- nindex = index + 1
59
- nindex = nil if nindex == len
60
- return false if nindex && seg.contains_point?(segments[nindex].e)
61
- pindex = index - 1
62
- pindex = nil if pindex < 0
63
- return false if pindex && seg.contains_point?(segments[pindex].s)
64
- next unless nindex
65
- oindex = nindex + 1
66
- while oindex < len
67
- oseg = segments[oindex]
68
- return false if !(index == 0 && oindex == len - 1 && seg.s == oseg.e) && seg.intersects_segment?(oseg)
69
- oindex += 1
70
- end
71
- end
72
- true
59
+ # Use a SweeplineIntersector to determine if there are any self-intersections
60
+ # in the ring. The GeometryGraph of the ring could be used by comparing the
61
+ # edges to number of segments (graph.incident_edges.length == segments.length),
62
+ # but this adds computational and memory overhead if graph isn't already memoized.
63
+ # Since graph is not used elsewhere in LineStringMethods, we will just use the
64
+ # SweeplineIntersector for now.
65
+ li = SweeplineIntersector.new(segments)
66
+ li.proper_intersections.empty?
73
67
  end
74
68
 
75
69
  def is_simple?
@@ -80,6 +74,43 @@ module RGeo
80
74
  def length
81
75
  segments.inject(0.0) { |sum, seg| sum + seg.length }
82
76
  end
77
+
78
+ def crosses?(rhs)
79
+ case rhs
80
+ when Feature::LineString
81
+ crosses_line_string?(rhs)
82
+ else
83
+ super
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ # Determines if a cross occurs with another linestring.
90
+ # Process is to get the number of proper intersections in each geom
91
+ # then overlay and get the number of proper intersections from that.
92
+ # If the overlaid number is higher than the sum of individual self-ints
93
+ # then there is an intersection. Finally, we need to check the intersection
94
+ # to see that it is not a boundary point of either segment.
95
+ #
96
+ # @param rhs [Feature::LineString]
97
+ #
98
+ # @return [Boolean]
99
+ def crosses_line_string?(rhs)
100
+ self_ints = SweeplineIntersector.new(segments).proper_intersections
101
+ self_ints += SweeplineIntersector.new(rhs.segments).proper_intersections
102
+ overlay_ints = SweeplineIntersector.new(segments + rhs.segments).proper_intersections
103
+
104
+ (overlay_ints - self_ints).each do |int|
105
+ s1s = int.s1.s
106
+ s1e = int.s1.e
107
+ s2s = int.s2.s
108
+ s2e = int.s2.e
109
+ return true unless [s1s, s1e, s2s, s2e].include?(int.point)
110
+ end
111
+
112
+ false
113
+ end
83
114
  end
84
115
 
85
116
  module MultiLineStringMethods # :nodoc:
@@ -78,12 +78,6 @@ module RGeo
78
78
  # Support a Z coordinate. Default is false.
79
79
  # [<tt>:has_m_coordinate</tt>]
80
80
  # Support an M coordinate. Default is false.
81
- # [<tt>:uses_lenient_assertions</tt>]
82
- # If set to true, assertion checking is disabled. This includes
83
- # simplicity checking on LinearRing, and validity checks on
84
- # Polygon and MultiPolygon. This may speed up creation of certain
85
- # objects, at the expense of not doing the proper checking for
86
- # OGC compliance. Default is false.
87
81
  # [<tt>:wkt_parser</tt>]
88
82
  # Configure the parser for WKT. The value is a hash of
89
83
  # configuration parameters for WKRep::WKTParser.new. Default is