rgeo 0.1.13 → 0.1.14

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 (41) hide show
  1. data/History.rdoc +11 -0
  2. data/Version +1 -1
  3. data/ext/geos_c_impl/factory.c +35 -40
  4. data/ext/geos_c_impl/factory.h +4 -1
  5. data/ext/geos_c_impl/geometry_collection.c +5 -5
  6. data/ext/geos_c_impl/geometry_collection.h +1 -1
  7. data/ext/geos_c_impl/line_string.c +129 -116
  8. data/ext/geos_c_impl/point.c +22 -33
  9. data/ext/geos_c_impl/point.h +1 -6
  10. data/ext/geos_c_impl/polygon.c +4 -4
  11. data/ext/geos_c_impl/polygon.h +1 -1
  12. data/lib/rgeo.rb +1 -0
  13. data/lib/rgeo/cartesian/simple_factory.rb +20 -4
  14. data/lib/rgeo/errors.rb +8 -0
  15. data/lib/rgeo/features/factory.rb +35 -1
  16. data/lib/rgeo/features/point.rb +22 -0
  17. data/lib/rgeo/features/polygon.rb +1 -2
  18. data/lib/rgeo/features/types.rb +70 -16
  19. data/lib/rgeo/geography/factories.rb +40 -3
  20. data/lib/rgeo/geography/factory.rb +25 -5
  21. data/lib/rgeo/geography/simple_mercator/feature_methods.rb +2 -6
  22. data/lib/rgeo/geography/simple_mercator/projector.rb +1 -1
  23. data/lib/rgeo/geos/factory.rb +40 -46
  24. data/lib/rgeo/geos/interface.rb +10 -0
  25. data/lib/rgeo/impl_helpers.rb +0 -1
  26. data/lib/rgeo/impl_helpers/basic_geometry_methods.rb +2 -2
  27. data/lib/rgeo/impl_helpers/basic_point_methods.rb +12 -14
  28. data/lib/rgeo/wkrep.rb +59 -0
  29. data/lib/rgeo/wkrep/wkb_generator.rb +181 -0
  30. data/lib/rgeo/wkrep/wkb_parser.rb +205 -0
  31. data/lib/rgeo/wkrep/wkt_generator.rb +187 -0
  32. data/lib/rgeo/wkrep/wkt_parser.rb +321 -0
  33. data/tests/common/line_string_tests.rb +26 -2
  34. data/tests/common/point_tests.rb +26 -0
  35. data/tests/geos/tc_point.rb +1 -20
  36. data/tests/simple_cartesian/tc_point.rb +1 -0
  37. data/tests/simple_mercator/tc_point.rb +1 -0
  38. data/tests/simple_spherical/tc_point.rb +1 -0
  39. data/tests/tc_oneoff.rb +3 -2
  40. metadata +8 -4
  41. data/lib/rgeo/impl_helpers/serialization.rb +0 -130
@@ -94,11 +94,11 @@ static VALUE method_point_y(VALUE self)
94
94
  }
95
95
 
96
96
 
97
- static VALUE method_point_z(VALUE self)
97
+ static VALUE get_3d_point(VALUE self, int flag)
98
98
  {
99
99
  VALUE result = Qnil;
100
100
  const GEOSGeometry* self_geom = RGEO_GET_GEOS_GEOMETRY(self);
101
- if (self_geom && GEOSHasZ_r(RGEO_CONTEXT_FROM_GEOMETRY(self), self_geom) == 1) {
101
+ if (self_geom && RGEO_FACTORY_DATA_FROM_GEOMETRY(self)->flags & flag) {
102
102
  const GEOSCoordSequence* coord_seq = GEOSGeom_getCoordSeq_r(RGEO_CONTEXT_FROM_GEOMETRY(self), self_geom);
103
103
  if (coord_seq) {
104
104
  double val;
@@ -111,25 +111,32 @@ static VALUE method_point_z(VALUE self)
111
111
  }
112
112
 
113
113
 
114
+ static VALUE method_point_z(VALUE self)
115
+ {
116
+ return get_3d_point(self, RGEO_FACTORYFLAGS_SUPPORTS_Z);
117
+ }
118
+
119
+
120
+ static VALUE method_point_m(VALUE self)
121
+ {
122
+ return get_3d_point(self, RGEO_FACTORYFLAGS_SUPPORTS_M);
123
+ }
124
+
125
+
114
126
  static VALUE method_point_eql(VALUE self, VALUE rhs)
115
127
  {
116
128
  VALUE result = rgeo_geos_klasses_and_factories_eql(self, rhs);
117
129
  if (RTEST(result)) {
118
- result = rgeo_geos_coordseqs_eql(RGEO_CONTEXT_FROM_GEOMETRY(self), RGEO_GET_GEOS_GEOMETRY(self), RGEO_GET_GEOS_GEOMETRY(rhs));
130
+ result = rgeo_geos_coordseqs_eql(RGEO_CONTEXT_FROM_GEOMETRY(self), RGEO_GET_GEOS_GEOMETRY(self), RGEO_GET_GEOS_GEOMETRY(rhs), RGEO_FACTORY_DATA_FROM_GEOMETRY(self)->flags & RGEO_FACTORYFLAGS_SUPPORTS_Z_OR_M);
119
131
  }
120
132
  return result;
121
133
  }
122
134
 
123
135
 
124
- static VALUE cmethod_create(VALUE module, VALUE factory, VALUE x, VALUE y)
125
- {
126
- return rgeo_create_geos_point_2d(factory, rb_num2dbl(x), rb_num2dbl(y));
127
- }
128
-
129
-
130
- static VALUE cmethod_create3d(VALUE module, VALUE factory, VALUE x, VALUE y, VALUE z)
136
+ static VALUE cmethod_create(VALUE module, VALUE factory, VALUE x, VALUE y, VALUE z)
131
137
  {
132
- return rgeo_create_geos_point_3d(factory, rb_num2dbl(x), rb_num2dbl(y), rb_num2dbl(z));
138
+ return rgeo_create_geos_point(factory, rb_num2dbl(x), rb_num2dbl(y),
139
+ RGEO_FACTORY_DATA_PTR(factory)->flags & RGEO_FACTORYFLAGS_SUPPORTS_Z_OR_M ? rb_num2dbl(z) : 0);
133
140
  }
134
141
 
135
142
 
@@ -137,39 +144,21 @@ void rgeo_init_geos_point(RGeo_Globals* globals)
137
144
  {
138
145
  VALUE geos_point_class = rb_define_class_under(globals->geos_module, "PointImpl", rb_const_get_at(globals->geos_module, rb_intern("GeometryImpl")));
139
146
 
140
- rb_define_module_function(geos_point_class, "create", cmethod_create, 3);
141
- rb_define_module_function(geos_point_class, "create3d", cmethod_create3d, 4);
147
+ rb_define_module_function(geos_point_class, "create", cmethod_create, 4);
142
148
 
143
149
  rb_define_method(geos_point_class, "eql?", method_point_eql, 1);
144
150
  rb_define_method(geos_point_class, "geometry_type", method_point_geometry_type, 0);
145
151
  rb_define_method(geos_point_class, "x", method_point_x, 0);
146
152
  rb_define_method(geos_point_class, "y", method_point_y, 0);
147
153
  rb_define_method(geos_point_class, "z", method_point_z, 0);
154
+ rb_define_method(geos_point_class, "m", method_point_m, 0);
148
155
  }
149
156
 
150
157
 
151
- VALUE rgeo_create_geos_point_2d(VALUE factory, double x, double y)
152
- {
153
- VALUE result = Qnil;
154
- GEOSCoordSequence* coord_seq = GEOSCoordSeq_create_r(RGEO_CONTEXT_FROM_FACTORY(factory), 1, 2);
155
- if (coord_seq) {
156
- if (GEOSCoordSeq_setX_r(RGEO_CONTEXT_FROM_FACTORY(factory), coord_seq, 0, x)) {
157
- if (GEOSCoordSeq_setY_r(RGEO_CONTEXT_FROM_FACTORY(factory), coord_seq, 0, y)) {
158
- GEOSGeometry* geom = GEOSGeom_createPoint_r(RGEO_CONTEXT_FROM_FACTORY(factory), coord_seq);
159
- if (geom) {
160
- result = rgeo_wrap_geos_geometry(factory, geom, rb_const_get_at(RGEO_GLOBALS_FROM_FACTORY(factory)->geos_module, rb_intern("PointImpl")));
161
- }
162
- }
163
- }
164
- }
165
- return result;
166
- }
167
-
168
-
169
- VALUE rgeo_create_geos_point_3d(VALUE factory, double x, double y, double z)
158
+ VALUE rgeo_create_geos_point(VALUE factory, double x, double y, double z)
170
159
  {
171
160
  VALUE result = Qnil;
172
- GEOSCoordSequence* coord_seq = GEOSCoordSeq_create_r(RGEO_CONTEXT_FROM_FACTORY(factory), 1, 2);
161
+ GEOSCoordSequence* coord_seq = GEOSCoordSeq_create_r(RGEO_CONTEXT_FROM_FACTORY(factory), 1, 3);
173
162
  if (coord_seq) {
174
163
  if (GEOSCoordSeq_setX_r(RGEO_CONTEXT_FROM_FACTORY(factory), coord_seq, 0, x)) {
175
164
  if (GEOSCoordSeq_setY_r(RGEO_CONTEXT_FROM_FACTORY(factory), coord_seq, 0, y)) {
@@ -51,15 +51,10 @@ RGEO_BEGIN_C
51
51
  */
52
52
  void rgeo_init_geos_point(RGeo_Globals* globals);
53
53
 
54
- /*
55
- Creates a 2d point and returns the ruby object.
56
- */
57
- VALUE rgeo_create_geos_point_2d(VALUE factory, double x, double y);
58
-
59
54
  /*
60
55
  Creates a 3d point and returns the ruby object.
61
56
  */
62
- VALUE rgeo_create_geos_point_3d(VALUE factory, double x, double y, double z);
57
+ VALUE rgeo_create_geos_point(VALUE factory, double x, double y, double z);
63
58
 
64
59
 
65
60
  RGEO_END_C
@@ -54,7 +54,7 @@ static VALUE method_polygon_eql(VALUE self, VALUE rhs)
54
54
  {
55
55
  VALUE result = rgeo_geos_klasses_and_factories_eql(self, rhs);
56
56
  if (RTEST(result)) {
57
- result = rgeo_geos_polygons_eql(RGEO_CONTEXT_FROM_GEOMETRY(self), RGEO_GET_GEOS_GEOMETRY(self), RGEO_GET_GEOS_GEOMETRY(rhs));
57
+ result = rgeo_geos_polygons_eql(RGEO_CONTEXT_FROM_GEOMETRY(self), RGEO_GET_GEOS_GEOMETRY(self), RGEO_GET_GEOS_GEOMETRY(rhs), RGEO_FACTORY_DATA_FROM_GEOMETRY(self)->flags & RGEO_FACTORYFLAGS_SUPPORTS_Z_OR_M);
58
58
  }
59
59
  return result;
60
60
  }
@@ -214,11 +214,11 @@ void rgeo_init_geos_polygon(RGeo_Globals* globals)
214
214
  }
215
215
 
216
216
 
217
- VALUE rgeo_geos_polygons_eql(GEOSContextHandle_t context, const GEOSGeometry* geom1, const GEOSGeometry* geom2)
217
+ VALUE rgeo_geos_polygons_eql(GEOSContextHandle_t context, const GEOSGeometry* geom1, const GEOSGeometry* geom2, char check_z)
218
218
  {
219
219
  VALUE result = Qnil;
220
220
  if (geom1 && geom2) {
221
- result = rgeo_geos_coordseqs_eql(context, GEOSGetExteriorRing_r(context, geom1), GEOSGetExteriorRing_r(context, geom2));
221
+ result = rgeo_geos_coordseqs_eql(context, GEOSGetExteriorRing_r(context, geom1), GEOSGetExteriorRing_r(context, geom2), check_z);
222
222
  if (RTEST(result)) {
223
223
  int len1 = GEOSGetNumInteriorRings_r(context, geom1);
224
224
  int len2 = GEOSGetNumInteriorRings_r(context, geom2);
@@ -226,7 +226,7 @@ VALUE rgeo_geos_polygons_eql(GEOSContextHandle_t context, const GEOSGeometry* ge
226
226
  if (len1 == len2) {
227
227
  int i;
228
228
  for (i=0; i<len1; ++i) {
229
- result = rgeo_geos_coordseqs_eql(context, GEOSGetInteriorRingN_r(context, geom1, i), GEOSGetInteriorRingN_r(context, geom2, i));
229
+ result = rgeo_geos_coordseqs_eql(context, GEOSGetInteriorRingN_r(context, geom1, i), GEOSGetInteriorRingN_r(context, geom2, i), check_z);
230
230
  if (!RTEST(result)) {
231
231
  break;
232
232
  }
@@ -58,7 +58,7 @@ void rgeo_init_geos_polygon(RGeo_Globals* globals);
58
58
  Returns Qtrue if the polygons are equal, Qfalse if they are inequal, or
59
59
  Qnil if an error occurs.
60
60
  */
61
- VALUE rgeo_geos_polygons_eql(GEOSContextHandle_t context, const GEOSGeometry* geom1, const GEOSGeometry* geom2);
61
+ VALUE rgeo_geos_polygons_eql(GEOSContextHandle_t context, const GEOSGeometry* geom1, const GEOSGeometry* geom2, char check_z);
62
62
 
63
63
 
64
64
  RGEO_END_C
data/lib/rgeo.rb CHANGED
@@ -78,6 +78,7 @@ paths_ = [
78
78
  'errors',
79
79
  'features',
80
80
  'impl_helpers',
81
+ 'wkrep',
81
82
  'geos',
82
83
  'cartesian',
83
84
  'geography',
@@ -57,6 +57,8 @@ module RGeo
57
57
 
58
58
  def initialize(opts_={})
59
59
  @srid = opts_[:srid].to_i
60
+ @support_z = opts_[:support_z_coordinate] ? true : false
61
+ @support_m = opts_[:support_m_coordinate] ? true : false
60
62
  end
61
63
 
62
64
 
@@ -75,24 +77,38 @@ module RGeo
75
77
  end
76
78
 
77
79
 
80
+ # See ::RGeo::Features::Factory#has_capability?
81
+
82
+ def has_capability?(name_)
83
+ case name_
84
+ when :z_coordinate
85
+ @support_z
86
+ when :m_coordinate
87
+ @support_m
88
+ else
89
+ nil
90
+ end
91
+ end
92
+
93
+
78
94
  # See ::RGeo::Features::Factory#parse_wkt
79
95
 
80
96
  def parse_wkt(str_)
81
- ImplHelpers::Serialization.parse_wkt(str_, self)
97
+ WKRep::WKTParser.new(self, :support_higher_dimensions => true).parse(str_)
82
98
  end
83
99
 
84
100
 
85
101
  # See ::RGeo::Features::Factory#parse_wkb
86
102
 
87
103
  def parse_wkb(str_)
88
- ImplHelpers::Serialization.parse_wkb(str_, self)
104
+ WKRep::WKBParser.new(self).parse(str_)
89
105
  end
90
106
 
91
107
 
92
108
  # See ::RGeo::Features::Factory#point
93
109
 
94
- def point(x_, y_)
95
- SimplePointImpl.new(self, x_, y_) rescue nil
110
+ def point(x_, y_, *extra_)
111
+ SimplePointImpl.new(self, x_, y_, *extra_) rescue nil
96
112
  end
97
113
 
98
114
 
data/lib/rgeo/errors.rb CHANGED
@@ -53,6 +53,14 @@ module RGeo
53
53
  class InvalidGeometry < RGeoError
54
54
  end
55
55
 
56
+ # The specified capability is not supported
57
+ class UnsupportedCapability < RGeoError
58
+ end
59
+
60
+ # Parsing failed
61
+ class ParseError < RGeoError
62
+ end
63
+
56
64
  end
57
65
 
58
66
 
@@ -71,6 +71,36 @@ module RGeo
71
71
  end
72
72
 
73
73
 
74
+ # Determine support for the given capability, identified by a
75
+ # symbolic name. This method may be used to test this factory, and
76
+ # any features created by it, to determine whether they support
77
+ # certain capabilities or operations. Most queries return a boolean
78
+ # value, though some may return other values to indicate different
79
+ # levels of support. Generally speaking, if a query returns a false
80
+ # or nil value, support for that capability is not guaranteed, and
81
+ # calls related to that function may fail or raise exceptions.
82
+ #
83
+ # Each capability must have a symbolic name. Names that have no
84
+ # periods are considered well-known names and are reserved for use
85
+ # by RGeo. If you want to define your own capabilities, use a name
86
+ # that is namespaced, such as <tt>:'mycompany.mycapability'</tt>.
87
+ #
88
+ # Currently defined standard capabilities are:
89
+ #
90
+ # <tt>:z_coordinate</tt>::
91
+ # Supports a "z" coordinate. When an implementation supports
92
+ # z_coordinate, the Factory#epoint and Point#z methods are
93
+ # available.
94
+ # <tt>:m_coordinate</tt>::
95
+ # Supports a "m" coordinate. When an implementation supports
96
+ # m_coordinate, the Factory#epoint and Point#m methods are
97
+ # available.
98
+
99
+ def has_capability?(name_)
100
+ nil
101
+ end
102
+
103
+
74
104
  # Parse the given string in well-known-text format and return the
75
105
  # resulting feature. Returns nil if the string couldn't be parsed.
76
106
 
@@ -89,8 +119,12 @@ module RGeo
89
119
 
90
120
  # Create a feature of type Point.
91
121
  # The x and y parameters should be Float values.
122
+ #
123
+ # The extra parameters should be the Z and/or M coordinates, if the
124
+ # capabilities are supported. If both Z and M capabilities are
125
+ # supported, Z should be passed first.
92
126
 
93
- def point(x_, y_)
127
+ def point(x_, y_, *extra_)
94
128
  nil
95
129
  end
96
130
 
@@ -90,6 +90,28 @@ module RGeo
90
90
  end
91
91
 
92
92
 
93
+ # Returns the z-coordinate for this Point as a floating-point
94
+ # scalar value.
95
+ #
96
+ # This method may not be available if the point's factory does
97
+ # not support the <tt>z_coordinate</tt> capability.
98
+
99
+ def z
100
+ raise Errors::MethodUnimplemented
101
+ end
102
+
103
+
104
+ # Returns the m-coordinate for this Point as a floating-point
105
+ # scalar value.
106
+ #
107
+ # This method may not be available if the point's factory does
108
+ # not support the <tt>m_coordinate</tt> capability.
109
+
110
+ def m
111
+ raise Errors::MethodUnimplemented
112
+ end
113
+
114
+
93
115
  end
94
116
 
95
117
 
@@ -117,7 +117,7 @@ module RGeo
117
117
  #
118
118
  # === Notes
119
119
  #
120
- # Returns an object that supports the LineString interface, or nil
120
+ # Returns an object that supports the LinearRing interface, or nil
121
121
  # if the given n is out of range.
122
122
 
123
123
  def interior_ring_n(n_)
@@ -133,7 +133,6 @@ module RGeo
133
133
  end
134
134
 
135
135
 
136
-
137
136
  end
138
137
 
139
138
 
@@ -39,7 +39,7 @@ module RGeo
39
39
  module Features
40
40
 
41
41
 
42
- # These methods are available as class methods (not instance methods)
42
+ # These methods are available as module methods (not instance methods)
43
43
  # of the various feature types.
44
44
  # For example, you may determine whether a feature object is a
45
45
  # point by calling:
@@ -78,6 +78,21 @@ module RGeo
78
78
  alias_method :===, :check_type
79
79
 
80
80
 
81
+ # Returns true if this type is the same type or a subtype of the
82
+ # given type.
83
+
84
+ def subtype_of?(type_)
85
+ self == type_ || self.include?(type_)
86
+ end
87
+
88
+
89
+ # Returns the OpenGIS type name of this type.
90
+
91
+ def type_name
92
+ self.name.sub('RGeo::Features::', '')
93
+ end
94
+
95
+
81
96
  end
82
97
 
83
98
 
@@ -135,7 +150,14 @@ module RGeo
135
150
  force_new_ ? obj_.dup : obj_
136
151
  elsif ntype_ == type_
137
152
  if type_ == Point
138
- nfactory_.point(obj_.x, obj_.y)
153
+ extra_ = []
154
+ if nfactory_.has_capability?(:z_coordinate)
155
+ extra_ << (factory_.has_capability?(:z_coordinate) ? obj_.z : 0.0)
156
+ end
157
+ if nfactory_.has_capability?(:m_coordinate)
158
+ extra_ << (factory_.has_capability?(:m_coordinate) ? obj_.m : 0.0)
159
+ end
160
+ nfactory_.point(obj_.x, obj_.y, *extra_)
139
161
  elsif type_ == Line
140
162
  nfactory_.line(obj_.start_point, obj_.end_point)
141
163
  elsif type_ == LinearRing
@@ -165,24 +187,56 @@ module RGeo
165
187
  else
166
188
  nil
167
189
  end
168
- elsif ntype_ == Line && type_ == LineString
169
- if obj_.num_points == 2
190
+ elsif ntype_ == Point
191
+ nil
192
+ elsif ntype_ == Line
193
+ if type_ == LineString && obj_.num_points == 2
170
194
  nfactory_.line(obj_.point_n(0), obj_.point_n(1))
171
195
  else
172
196
  nil
173
197
  end
174
- elsif ntype_ == LinearRing && type_ == LineString
175
- nfactory_.linear_ring(obj_.points)
176
- elsif ntype_ == LineString && (type_ == Line || type_ == LinearRing)
177
- nfactory_.line_string(obj_.points)
178
- elsif ntype_ == MultiPoint && type_ == GeometryCollection
179
- nfactory_.multi_point(obj_)
180
- elsif ntype_ == MultiLineString && type_ == GeometryCollection
181
- nfactory_.multi_line_string(obj_)
182
- elsif ntype_ == MultiPolygon && type_ == GeometryCollection
183
- nfactory_.multi_polygon(obj_)
184
- elsif ntype_ == GeometryCollection && (type_ == MultiPoint || type_ == MultiLineString || type_ == MultiPolygon)
185
- nfactory_.collection(obj_)
198
+ elsif ntype_ == LinearRing
199
+ if type_ == LineString
200
+ nfactory_.linear_ring(obj_.points)
201
+ else
202
+ nil
203
+ end
204
+ elsif ntype_ == LineString
205
+ if type_ == Line || type_ == LinearRing
206
+ nfactory_.line_string(obj_.points)
207
+ else
208
+ nil
209
+ end
210
+ elsif ntype_ == MultiPoint
211
+ if type_ == Point
212
+ nfactory_.multi_point([obj_])
213
+ elsif type_ == GeometryCollection
214
+ nfactory_.multi_point(obj_)
215
+ else
216
+ nil
217
+ end
218
+ elsif ntype_ == MultiLineString
219
+ if type_ == Line || type_ == LinearRing || type_ == LineString
220
+ nfactory_.multi_line_string([obj_])
221
+ elsif type_ == GeometryCollection
222
+ nfactory_.multi_line_string(obj_)
223
+ else
224
+ nil
225
+ end
226
+ elsif ntype_ == MultiPolygon
227
+ if type_ == Polygon
228
+ nfactory_.multi_polygon([obj_])
229
+ elsif type_ == GeometryCollection
230
+ nfactory_.multi_polygon(obj_)
231
+ else
232
+ nil
233
+ end
234
+ elsif ntype_ == GeometryCollection
235
+ if type_ == MultiPoint || type_ == MultiLineString || type_ == MultiPolygon
236
+ nfactory_.collection(obj_)
237
+ else
238
+ nfactory_.collection([obj_])
239
+ end
186
240
  else
187
241
  nil
188
242
  end