rgeo 0.3.9 → 0.3.10

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.
data/History.rdoc CHANGED
@@ -1,3 +1,8 @@
1
+ === 0.3.10 / 2012-04-12
2
+
3
+ * Implemented subdivision and other analysis features on Cartesian::BoundingBox.
4
+ * Operators +, -, and * were not implemented in the ffi-geos factory. Fixed.
5
+
1
6
  === 0.3.9 / 2012-04-10
2
7
 
3
8
  * Implemented LineString#length and MultiLineString#length for simple cartesian and simple spherical factories.
data/Version CHANGED
@@ -1 +1 @@
1
- 0.3.9
1
+ 0.3.10
@@ -61,10 +61,18 @@ module RGeo
61
61
 
62
62
  def self.create_from_points(point1_, point2_, opts_={})
63
63
  factory_ = point1_.factory
64
- box_ = new(factory_, opts_)
65
- box_._add_geometry(point1_)
66
- box_.add(point2_)
67
- box_
64
+ new(factory_, opts_)._add_geometry(point1_).add(point2_)
65
+ end
66
+
67
+
68
+ # Create a bounding box given a geometry to surround.
69
+ # The bounding box will be given the factory of the geometry.
70
+ # You may also provide the same options available to
71
+ # BoundingBox.new.
72
+
73
+ def self.create_from_geometry(geom_, opts_={})
74
+ factory_ = geom_.factory
75
+ new(factory_, opts_)._add_geometry(geom_)
68
76
  end
69
77
 
70
78
 
@@ -87,9 +95,13 @@ module RGeo
87
95
 
88
96
  def initialize(factory_, opts_={})
89
97
  @factory = factory_
90
- @has_z = !opts_[:ignore_z] && factory_.property(:has_z_coordinate) ? true : false
91
- @has_m = !opts_[:ignore_m] && factory_.property(:has_m_coordinate) ? true : false
92
- @min_x = @max_x = @min_y = @max_y = @min_z = @max_z = @min_m = @max_m = nil
98
+ if (values_ = opts_[:raw])
99
+ @has_z, @has_m, @min_x, @max_x, @min_y, @max_y, @min_z, @max_z, @min_m, @max_m = values_
100
+ else
101
+ @has_z = !opts_[:ignore_z] && factory_.property(:has_z_coordinate) ? true : false
102
+ @has_m = !opts_[:ignore_m] && factory_.property(:has_m_coordinate) ? true : false
103
+ @min_x = @max_x = @min_y = @max_y = @min_z = @max_z = @min_m = @max_m = nil
104
+ end
93
105
  end
94
106
 
95
107
 
@@ -117,6 +129,25 @@ module RGeo
117
129
  end
118
130
 
119
131
 
132
+ # Returns true if this bounding box is degenerate. That is,
133
+ # it is nonempty but contains only a single point because both
134
+ # the X and Y spans are 0. Infinitesimal boxes are also
135
+ # always degenerate.
136
+
137
+ def infinitesimal?
138
+ @min_x && @min_x == @max_x && @min_y == @max_y
139
+ end
140
+
141
+
142
+ # Returns true if this bounding box is degenerate. That is,
143
+ # it is nonempty but has zero area because either or both
144
+ # of the X or Y spans are 0.
145
+
146
+ def degenerate?
147
+ @min_x && (@min_x == @max_x || @min_y == @max_y)
148
+ end
149
+
150
+
120
151
  # Returns true if this bounding box tracks Z coordinates.
121
152
 
122
153
  def has_z
@@ -145,6 +176,20 @@ module RGeo
145
176
  end
146
177
 
147
178
 
179
+ # Returns the midpoint X, or nil if this bounding box is empty.
180
+
181
+ def center_x
182
+ @max_x ? (@max_x + @min_x) * 0.5 : nil
183
+ end
184
+
185
+
186
+ # Returns the X span, or 0 if this bounding box is empty.
187
+
188
+ def x_span
189
+ @max_x ? @max_x - @min_x : 0
190
+ end
191
+
192
+
148
193
  # Returns the minimum Y, or nil if this bounding box is empty.
149
194
 
150
195
  def min_y
@@ -159,6 +204,20 @@ module RGeo
159
204
  end
160
205
 
161
206
 
207
+ # Returns the midpoint Y, or nil if this bounding box is empty.
208
+
209
+ def center_y
210
+ @max_y ? (@max_y + @min_y) * 0.5 : nil
211
+ end
212
+
213
+
214
+ # Returns the Y span, or 0 if this bounding box is empty.
215
+
216
+ def y_span
217
+ @max_y ? @max_y - @min_y : 0
218
+ end
219
+
220
+
162
221
  # Returns the minimum Z, or nil if this bounding box is empty.
163
222
 
164
223
  def min_z
@@ -173,6 +232,20 @@ module RGeo
173
232
  end
174
233
 
175
234
 
235
+ # Returns the midpoint Z, or nil if this bounding box is empty or has no Z.
236
+
237
+ def center_z
238
+ @max_z ? (@max_z + @min_z) * 0.5 : nil
239
+ end
240
+
241
+
242
+ # Returns the Z span, 0 if this bounding box is empty, or nil if it has no Z.
243
+
244
+ def z_span
245
+ @has_z ? (@max_z ? @max_z - @min_z : 0) : nil
246
+ end
247
+
248
+
176
249
  # Returns the minimum M, or nil if this bounding box is empty.
177
250
 
178
251
  def min_m
@@ -187,6 +260,20 @@ module RGeo
187
260
  end
188
261
 
189
262
 
263
+ # Returns the midpoint M, or nil if this bounding box is empty or has no M.
264
+
265
+ def center_m
266
+ @max_m ? (@max_m + @min_m) * 0.5 : nil
267
+ end
268
+
269
+
270
+ # Returns the M span, 0 if this bounding box is empty, or nil if it has no M.
271
+
272
+ def m_span
273
+ @has_m ? (@max_m ? @max_m - @min_m : 0) : nil
274
+ end
275
+
276
+
190
277
  # Returns a point representing the minimum extent in all dimensions,
191
278
  # or nil if this bounding box is empty.
192
279
 
@@ -237,8 +324,10 @@ module RGeo
237
324
  end
238
325
 
239
326
 
240
- # Converts this bounding box to an envelope polygon.
241
- # Returns the empty collection if this bounding box is empty.
327
+ # Converts this bounding box to an envelope, which will be the
328
+ # empty collection (if the bounding box is empty), a point (if the
329
+ # bounding box is not empty but both spans are 0), a line (if only
330
+ # one of the two spans is 0) or a polygon (if neither span is 0).
242
331
 
243
332
  def to_geometry
244
333
  if @min_x
@@ -246,14 +335,14 @@ module RGeo
246
335
  extras_ << @min_z if @has_z
247
336
  extras_ << @min_m if @has_m
248
337
  point_min_ = @factory.point(@min_x, @min_y, *extras_)
249
- if @min_x == @max_x && @min_y == @max_y
338
+ if infinitesimal?
250
339
  point_min_
251
340
  else
252
341
  extras_ = []
253
342
  extras_ << @max_z if @has_z
254
343
  extras_ << @max_m if @has_m
255
344
  point_max_ = @factory.point(@max_x, @max_y, *extras_)
256
- if @min_x == @max_x || @min_y == @max_y
345
+ if degenerate?
257
346
  @factory.line(point_min_, point_max_)
258
347
  else
259
348
  @factory.polygon(@factory.linear_ring([point_min_,
@@ -298,6 +387,60 @@ module RGeo
298
387
  end
299
388
 
300
389
 
390
+ # Returns this bounding box subdivided, as an array of bounding boxes.
391
+ # If this bounding box is empty, returns the empty array.
392
+ # If this bounding box is a point, returns a one-element array
393
+ # containing the current point.
394
+ # If the x or y span is 0, bisects the line.
395
+ # Otherwise, generally returns a 4-1 subdivision in the X-Y plane.
396
+ # Does not subdivide on Z or M.
397
+ #
398
+ # [<tt>:bisect_factor</tt>]
399
+ # An optional floating point value that should be greater than 1.0.
400
+ # If the ratio between the larger span and the smaller span is
401
+ # greater than this factor, the bounding box is divided only in
402
+ # half instead of fourths.
403
+
404
+ def subdivide(opts_={})
405
+ return [] if empty?
406
+ if infinitesimal?
407
+ return [
408
+ BoundingBox.new(@factory, :raw => [@has_z, @has_m,
409
+ @min_x, @max_x, @min_y, @max_y, @min_z, @max_z, @min_m, @max_m])
410
+ ]
411
+ end
412
+ factor_ = opts_[:bisect_factor]
413
+ factor_ ||= 1 if degenerate?
414
+ if factor_
415
+ if x_span > y_span * factor_
416
+ return [
417
+ BoundingBox.new(@factory, :raw => [@has_z, @has_m,
418
+ @min_x, center_x, @min_y, @max_y, @min_z, @max_z, @min_m, @max_m]),
419
+ BoundingBox.new(@factory, :raw => [@has_z, @has_m,
420
+ center_x, @max_x, @min_y, @max_y, @min_z, @max_z, @min_m, @max_m])
421
+ ]
422
+ elsif y_span > x_span * factor_
423
+ return [
424
+ BoundingBox.new(@factory, :raw => [@has_z, @has_m,
425
+ @min_x, @max_x, @min_y, center_y, @min_z, @max_z, @min_m, @max_m]),
426
+ BoundingBox.new(@factory, :raw => [@has_z, @has_m,
427
+ @min_x, @max_x, center_y, @max_y, @min_z, @max_z, @min_m, @max_m])
428
+ ]
429
+ end
430
+ end
431
+ [
432
+ BoundingBox.new(@factory, :raw => [@has_z, @has_m,
433
+ @min_x, center_x, @min_y, center_y, @min_z, @max_z, @min_m, @max_m]),
434
+ BoundingBox.new(@factory, :raw => [@has_z, @has_m,
435
+ center_x, @max_x, @min_y, center_y, @min_z, @max_z, @min_m, @max_m]),
436
+ BoundingBox.new(@factory, :raw => [@has_z, @has_m,
437
+ @min_x, center_x, center_y, @max_y, @min_z, @max_z, @min_m, @max_m]),
438
+ BoundingBox.new(@factory, :raw => [@has_z, @has_m,
439
+ center_x, @max_x, center_y, @max_y, @min_z, @max_z, @min_m, @max_m])
440
+ ]
441
+ end
442
+
443
+
301
444
  def _add_geometry(geometry_) # :nodoc:
302
445
  case geometry_
303
446
  when Feature::Point
@@ -315,6 +458,7 @@ module RGeo
315
458
  when Feature::GeometryCollection
316
459
  geometry_.each{ |g_| _add_geometry(g_) }
317
460
  end
461
+ self
318
462
  end
319
463
 
320
464
 
@@ -273,7 +273,7 @@ module RGeo
273
273
  # this factory.
274
274
 
275
275
  def project(geometry_)
276
- return nil unless @projector
276
+ return nil unless @projector && geometry_
277
277
  unless geometry_.factory == self
278
278
  raise Error::InvalidGeometry, 'Wrong geometry type'
279
279
  end
@@ -287,6 +287,7 @@ module RGeo
287
287
  # the projection defined by this factory.
288
288
 
289
289
  def unproject(geometry_)
290
+ return nil unless geometry_
290
291
  unless @projector && @projector.projection_factory == geometry_.factory
291
292
  raise Error::InvalidGeometry, 'You can unproject only features that are in the projected coordinate space.'
292
293
  end
@@ -300,18 +300,24 @@ module RGeo
300
300
  fg_ ? @factory.wrap_fg_geom(@fg_geom.intersection(fg_)) : nil
301
301
  end
302
302
 
303
+ alias_method :*, :intersection
304
+
303
305
 
304
306
  def union(rhs_)
305
307
  fg_ = factory._convert_to_fg_geometry(rhs_)
306
308
  fg_ ? @factory.wrap_fg_geom(@fg_geom.union(fg_)) : nil
307
309
  end
308
310
 
311
+ alias_method :+, :union
312
+
309
313
 
310
314
  def difference(rhs_)
311
315
  fg_ = factory._convert_to_fg_geometry(rhs_)
312
316
  fg_ ? @factory.wrap_fg_geom(@fg_geom.difference(fg_)) : nil
313
317
  end
314
318
 
319
+ alias_method :-, :difference
320
+
315
321
 
316
322
  def sym_difference(rhs_)
317
323
  fg_ = factory._convert_to_fg_geometry(rhs_)
@@ -202,6 +202,31 @@ module RGeo
202
202
  end
203
203
 
204
204
 
205
+ def test_union
206
+ geom1_ = @factory.multi_point([@point1, @point2])
207
+ geom2_ = @factory.multi_point([@point1, @point3])
208
+ geom3_ = @factory.multi_point([@point1, @point2, @point3])
209
+ assert_equal(geom3_, geom1_.union(geom2_))
210
+ assert_equal(geom3_, geom1_ + geom2_)
211
+ end
212
+
213
+
214
+ def test_difference
215
+ geom1_ = @factory.multi_point([@point1, @point2])
216
+ geom2_ = @factory.multi_point([@point1, @point3])
217
+ assert_equal(@point2, geom1_.difference(geom2_))
218
+ assert_equal(@point2, geom1_ - geom2_)
219
+ end
220
+
221
+
222
+ def test_intersection
223
+ geom1_ = @factory.multi_point([@point1, @point2])
224
+ geom2_ = @factory.multi_point([@point1, @point3])
225
+ assert_equal(@point1, geom1_.intersection(geom2_))
226
+ assert_equal(@point1, geom1_ * geom2_)
227
+ end
228
+
229
+
205
230
  end
206
231
 
207
232
  end
@@ -55,6 +55,12 @@ module RGeo
55
55
  include ::RGeo::Tests::Common::MultiPointTests
56
56
 
57
57
 
58
+ # These tests suffer from floating point issues
59
+ undef_method :test_union
60
+ undef_method :test_difference
61
+ undef_method :test_intersection
62
+
63
+
58
64
  end
59
65
 
60
66
  end
@@ -58,6 +58,9 @@ module RGeo
58
58
  undef_method :test_fully_equal
59
59
  undef_method :test_geometrically_equal
60
60
  undef_method :test_not_equal
61
+ undef_method :test_union
62
+ undef_method :test_difference
63
+ undef_method :test_intersection
61
64
 
62
65
 
63
66
  end
@@ -55,6 +55,12 @@ module RGeo
55
55
  include ::RGeo::Tests::Common::MultiPointTests
56
56
 
57
57
 
58
+ # These tests suffer from floating point issues
59
+ undef_method :test_union
60
+ undef_method :test_difference
61
+ undef_method :test_intersection
62
+
63
+
58
64
  end
59
65
 
60
66
  end
@@ -58,6 +58,9 @@ module RGeo
58
58
  undef_method :test_fully_equal
59
59
  undef_method :test_geometrically_equal
60
60
  undef_method :test_not_equal
61
+ undef_method :test_union
62
+ undef_method :test_difference
63
+ undef_method :test_intersection
61
64
 
62
65
 
63
66
  end
@@ -59,27 +59,35 @@ module RGeo
59
59
  assert_equal(true, bbox_.to_geometry.is_empty?)
60
60
  assert_equal(true, bbox_.contains?(bbox_))
61
61
  assert_equal(false, bbox_.contains?(@factory.point(1, 1)))
62
+ assert_nil(bbox_.center_x)
63
+ assert_equal(0, bbox_.x_span)
64
+ assert_equal(0, bbox_.subdivide.size)
62
65
  end
63
66
 
64
67
 
65
68
  def test_point_bbox
66
69
  empty_bbox_ = ::RGeo::Cartesian::BoundingBox.new(@factory)
67
70
  bbox_ = ::RGeo::Cartesian::BoundingBox.new(@factory)
68
- bbox_.add(@factory.point(1, 1))
71
+ bbox_.add(@factory.point(1, 2))
69
72
  assert_equal(false, bbox_.empty?)
70
73
  assert_equal(false, bbox_.has_z)
71
74
  assert_equal(1.0, bbox_.min_x)
72
- assert_equal(1.0, bbox_.min_y)
75
+ assert_equal(2.0, bbox_.min_y)
73
76
  assert_equal(1.0, bbox_.max_x)
74
- assert_equal(1.0, bbox_.max_y)
77
+ assert_equal(2.0, bbox_.max_y)
75
78
  assert_equal(@factory, bbox_.factory)
76
- assert_equal(@factory.point(1, 1), bbox_.min_point)
77
- assert_equal(@factory.point(1, 1), bbox_.max_point)
78
- assert_equal(@factory.point(1, 1), bbox_.to_geometry)
79
+ assert_equal(@factory.point(1, 2), bbox_.min_point)
80
+ assert_equal(@factory.point(1, 2), bbox_.max_point)
81
+ assert_equal(@factory.point(1, 2), bbox_.to_geometry)
79
82
  assert_equal(true, bbox_.contains?(empty_bbox_))
80
83
  assert_equal(false, empty_bbox_.contains?(bbox_))
81
- assert_equal(true, bbox_.contains?(@factory.point(1, 1)))
84
+ assert_equal(true, bbox_.contains?(@factory.point(1, 2)))
82
85
  assert_equal(false, bbox_.contains?(@factory.point(2, 1)))
86
+ assert_equal(1, bbox_.center_x)
87
+ assert_equal(0, bbox_.x_span)
88
+ assert_equal(2, bbox_.center_y)
89
+ assert_equal(0, bbox_.y_span)
90
+ assert_equal([bbox_], bbox_.subdivide)
83
91
  end
84
92
 
85
93
 
@@ -102,6 +110,10 @@ module RGeo
102
110
  assert_equal(false, empty_bbox_.contains?(bbox_))
103
111
  assert_equal(true, bbox_.contains?(@factory.point(1, 3)))
104
112
  assert_equal(false, bbox_.contains?(@factory.point(2, 1)))
113
+ assert_equal(1.5, bbox_.center_x)
114
+ assert_equal(1, bbox_.x_span)
115
+ assert_equal(3.5, bbox_.center_y)
116
+ assert_equal(1, bbox_.y_span)
105
117
  end
106
118
 
107
119
 
@@ -115,6 +127,40 @@ module RGeo
115
127
  end
116
128
 
117
129
 
130
+ def test_basic_rect_subdivide
131
+ bbox_ = ::RGeo::Cartesian::BoundingBox.new(@factory)
132
+ bbox_.add(@factory.point(1, 2))
133
+ bbox_.add(@factory.point(5, 4))
134
+ quads_ = bbox_.subdivide
135
+ quads_.each{ |q_| assert_equal(2.0, q_.to_geometry.area) }
136
+ quadsum_ = quads_[0].to_geometry + quads_[1].to_geometry +
137
+ quads_[2].to_geometry + quads_[3].to_geometry
138
+ assert_equal(bbox_.to_geometry, quadsum_)
139
+ end
140
+
141
+
142
+ def test_horiz_line_subdivide
143
+ bbox_ = ::RGeo::Cartesian::BoundingBox.new(@factory)
144
+ bbox_.add(@factory.point(1, 2))
145
+ bbox_.add(@factory.point(5, 2))
146
+ lines_ = bbox_.subdivide
147
+ lines_.each{ |line_| assert_equal(2.0, line_.to_geometry.length) }
148
+ linesum_ = lines_[0].to_geometry + lines_[1].to_geometry
149
+ assert_equal(bbox_.to_geometry, linesum_)
150
+ end
151
+
152
+
153
+ def test_vert_line_subdivide
154
+ bbox_ = ::RGeo::Cartesian::BoundingBox.new(@factory)
155
+ bbox_.add(@factory.point(1, 2))
156
+ bbox_.add(@factory.point(1, 6))
157
+ lines_ = bbox_.subdivide
158
+ lines_.each{ |line_| assert_equal(2.0, line_.to_geometry.length) }
159
+ linesum_ = lines_[0].to_geometry + lines_[1].to_geometry
160
+ assert_equal(bbox_.to_geometry, linesum_)
161
+ end
162
+
163
+
118
164
  end
119
165
 
120
166
  end
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: 0.3.9
4
+ version: 0.3.10
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-11 00:00:00.000000000 Z
12
+ date: 2012-04-13 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: RGeo is a geospatial data library for Ruby. It provides an implementation
15
15
  of the Open Geospatial Consortium's Simple Features Specification, used by most