rgeo 0.3.9 → 0.3.10

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