ffi-geos 1.2.1 → 1.2.2

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 (49) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +20 -0
  3. data/.travis.yml +7 -3
  4. data/Gemfile +1 -1
  5. data/Guardfile +4 -5
  6. data/ffi-geos.gemspec +1 -1
  7. data/lib/ffi-geos.rb +212 -196
  8. data/lib/ffi-geos/buffer_params.rb +9 -20
  9. data/lib/ffi-geos/coordinate_sequence.rb +342 -58
  10. data/lib/ffi-geos/geometry.rb +167 -178
  11. data/lib/ffi-geos/geometry_collection.rb +60 -12
  12. data/lib/ffi-geos/interrupt.rb +2 -4
  13. data/lib/ffi-geos/line_string.rb +146 -37
  14. data/lib/ffi-geos/linear_ring.rb +2 -3
  15. data/lib/ffi-geos/multi_line_string.rb +1 -2
  16. data/lib/ffi-geos/multi_point.rb +0 -1
  17. data/lib/ffi-geos/multi_polygon.rb +0 -1
  18. data/lib/ffi-geos/point.rb +69 -14
  19. data/lib/ffi-geos/polygon.rb +110 -21
  20. data/lib/ffi-geos/prepared_geometry.rb +11 -12
  21. data/lib/ffi-geos/strtree.rb +41 -52
  22. data/lib/ffi-geos/tools.rb +15 -18
  23. data/lib/ffi-geos/utils.rb +27 -44
  24. data/lib/ffi-geos/version.rb +1 -3
  25. data/lib/ffi-geos/wkb_reader.rb +4 -9
  26. data/lib/ffi-geos/wkb_writer.rb +14 -18
  27. data/lib/ffi-geos/wkt_reader.rb +2 -5
  28. data/lib/ffi-geos/wkt_writer.rb +17 -22
  29. data/test/.rubocop.yml +36 -0
  30. data/test/coordinate_sequence_tests.rb +263 -14
  31. data/test/geometry_collection_tests.rb +412 -1
  32. data/test/geometry_tests.rb +156 -86
  33. data/test/interrupt_tests.rb +2 -4
  34. data/test/line_string_tests.rb +212 -23
  35. data/test/linear_ring_tests.rb +1 -2
  36. data/test/misc_tests.rb +28 -29
  37. data/test/multi_line_string_tests.rb +0 -1
  38. data/test/point_tests.rb +158 -1
  39. data/test/polygon_tests.rb +284 -1
  40. data/test/prepared_geometry_tests.rb +1 -3
  41. data/test/strtree_tests.rb +9 -10
  42. data/test/test_helper.rb +49 -18
  43. data/test/tools_tests.rb +1 -3
  44. data/test/utils_tests.rb +22 -22
  45. data/test/wkb_reader_tests.rb +10 -9
  46. data/test/wkb_writer_tests.rb +5 -13
  47. data/test/wkt_reader_tests.rb +1 -2
  48. data/test/wkt_writer_tests.rb +9 -14
  49. metadata +6 -3
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Geos
@@ -33,7 +32,7 @@ module Geos
33
32
 
34
33
  @params = {}
35
34
  VALID_PARAMETERS.each do |param|
36
- self.send("#{param}=", params[param])
35
+ send("#{param}=", params[param])
37
36
  end
38
37
  end
39
38
 
@@ -44,43 +43,33 @@ module Geos
44
43
  def endcap=(value)
45
44
  check_enum_value(Geos::BufferCapStyles, value)
46
45
 
47
- if bool_result(FFIGeos.GEOSBufferParams_setEndCapStyle_r(Geos.current_handle_pointer, ptr, value))
48
- @params[:endcap] = symbol_for_enum(Geos::BufferCapStyles, value)
49
- end
46
+ @params[:endcap] = symbol_for_enum(Geos::BufferCapStyles, value) if bool_result(FFIGeos.GEOSBufferParams_setEndCapStyle_r(Geos.current_handle_pointer, ptr, value))
50
47
  end
51
48
 
52
49
  def join=(value)
53
50
  check_enum_value(Geos::BufferJoinStyles, value)
54
51
 
55
- if bool_result(FFIGeos.GEOSBufferParams_setJoinStyle_r(Geos.current_handle_pointer, ptr, value))
56
- @params[:join] = symbol_for_enum(Geos::BufferJoinStyles, value)
57
- end
52
+ @params[:join] = symbol_for_enum(Geos::BufferJoinStyles, value) if bool_result(FFIGeos.GEOSBufferParams_setJoinStyle_r(Geos.current_handle_pointer, ptr, value))
58
53
  end
59
54
 
60
55
  def mitre_limit=(value)
61
- if bool_result(FFIGeos.GEOSBufferParams_setMitreLimit_r(Geos.current_handle_pointer, ptr, value))
62
- @params[:mitre_limit] = value
63
- end
56
+ @params[:mitre_limit] = value if bool_result(FFIGeos.GEOSBufferParams_setMitreLimit_r(Geos.current_handle_pointer, ptr, value))
64
57
  end
65
58
 
66
59
  def quad_segs=(value)
67
- if bool_result(FFIGeos.GEOSBufferParams_setQuadrantSegments_r(Geos.current_handle_pointer, ptr, value))
68
- @params[:quad_segs] = value
69
- end
60
+ @params[:quad_segs] = value if bool_result(FFIGeos.GEOSBufferParams_setQuadrantSegments_r(Geos.current_handle_pointer, ptr, value))
70
61
  end
71
62
 
72
63
  def single_sided=(value)
73
- if bool_result(FFIGeos.GEOSBufferParams_setSingleSided_r(Geos.current_handle_pointer, ptr, Geos::Tools.bool_to_int(value)))
74
- @params[:single_sided] = value
75
- end
64
+ @params[:single_sided] = value if bool_result(FFIGeos.GEOSBufferParams_setSingleSided_r(Geos.current_handle_pointer, ptr, Geos::Tools.bool_to_int(value)))
76
65
  end
77
66
 
78
67
  VALID_PARAMETERS.each do |param|
79
- self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
68
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
80
69
  def #{param}
81
70
  @params[:#{param}]
82
71
  end
83
- EOF
72
+ RUBY
84
73
  end
85
74
  else
86
75
  attr_accessor(*VALID_PARAMETERS)
@@ -89,7 +78,7 @@ module Geos
89
78
  params = Geos::Constants::BUFFER_PARAM_DEFAULTS.merge(params)
90
79
 
91
80
  VALID_PARAMETERS.each do |param|
92
- self.send("#{param}=", params[param])
81
+ send("#{param}=", params[param])
93
82
  end
94
83
  end
95
84
  end
@@ -1,8 +1,6 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Geos
5
-
6
4
  # A CoordinateSequence is a list of coordinates in a Geometry.
7
5
  class CoordinateSequence
8
6
  class ParseError < Geos::ParseError
@@ -19,22 +17,22 @@ module Geos
19
17
  end
20
18
 
21
19
  def [](idx)
22
- parent.get_ordinate(idx, self.dimension)
20
+ parent.get_ordinate(idx, dimension)
23
21
  end
24
22
 
25
23
  def []=(idx, value)
26
- parent.set_ordinate(idx, self.dimension, value)
24
+ parent.set_ordinate(idx, dimension, value)
27
25
  end
28
26
 
29
27
  def each
30
28
  if block_given?
31
29
  parent.length.times do |n|
32
- yield parent.get_ordinate(n, self.dimension)
30
+ yield parent.get_ordinate(n, dimension)
33
31
  end
34
32
  self
35
33
  else
36
34
  parent.length.times.collect { |n|
37
- parent.get_ordinate(n, self.dimension)
35
+ parent.get_ordinate(n, dimension)
38
36
  }.to_enum
39
37
  end
40
38
  end
@@ -73,9 +71,9 @@ module Geos
73
71
  if lengths.empty?
74
72
  [ 0, 0 ]
75
73
  elsif lengths.length != 1
76
- raise ParseError.new("Different sized points found in Array")
74
+ raise ParseError, 'Different sized points found in Array'
77
75
  elsif !lengths.first.between?(1, 3)
78
- raise ParseError.new("Expected points to contain 1-3 elements")
76
+ raise ParseError, 'Expected points to contain 1-3 elements'
79
77
  else
80
78
  [ points.length, points.first.length ]
81
79
  end
@@ -83,7 +81,7 @@ module Geos
83
81
  args.first.values_at(:size, :dimensions)
84
82
  else
85
83
  if !args.length.between?(0, 2)
86
- raise ArgumentError.new("wrong number of arguments (#{args.length} for 0-2)")
84
+ raise ArgumentError, "wrong number of arguments (#{args.length} for 0-2)"
87
85
  else
88
86
  [ args[0], args[1] ]
89
87
  end
@@ -107,11 +105,11 @@ module Geos
107
105
  @y = CoordinateAccessor.new(self, 1)
108
106
  @z = CoordinateAccessor.new(self, 2)
109
107
 
110
- if points
111
- points.each_with_index do |point, idx|
112
- point.each_with_index do |val, dim|
113
- self.set_ordinate(idx, dim, val)
114
- end
108
+ return unless points
109
+
110
+ points.each_with_index do |point, idx|
111
+ point.each_with_index do |val, dim|
112
+ set_ordinate(idx, dim, val)
115
113
  end
116
114
  end
117
115
  end
@@ -135,13 +133,13 @@ module Geos
135
133
  # 2-dimensional CoordinateSequences.
136
134
  def each
137
135
  if block_given?
138
- self.length.times do |n|
139
- yield self.build_coordinate(n)
136
+ length.times do |n|
137
+ yield build_coordinate(n)
140
138
  end
141
139
  self
142
140
  else
143
- self.length.times.collect { |n|
144
- self.build_coordinate(n)
141
+ length.times.collect { |n|
142
+ build_coordinate(n)
145
143
  }.to_enum
146
144
  end
147
145
  end
@@ -149,82 +147,82 @@ module Geos
149
147
  def [](*args)
150
148
  if args.length == 1 && args.first.is_a?(Numeric) && args.first >= 0
151
149
  i = args.first
152
- ary = [ self.get_x(i), self.get_y(i) ]
153
- ary << self.get_z(i) if self.has_z?
150
+ ary = [ get_x(i), get_y(i) ]
151
+ ary << get_z(i) if has_z?
154
152
  ary
155
153
  else
156
- self.to_a[*args]
154
+ to_a[*args]
157
155
  end
158
156
  end
159
- alias_method :slice, :[]
157
+ alias slice []
160
158
 
161
159
  def has_z?
162
- self.dimensions == 3
160
+ dimensions == 3
163
161
  end
164
162
 
165
163
  # Sets the x value of a coordinate. Can also be set via #x[]=.
166
164
  def set_x(idx, val)
167
- self.check_bounds(idx)
168
- FFIGeos.GEOSCoordSeq_setX_r(Geos.current_handle_pointer, self.ptr, idx, val.to_f)
165
+ check_bounds(idx)
166
+ FFIGeos.GEOSCoordSeq_setX_r(Geos.current_handle_pointer, ptr, idx, val.to_f)
169
167
  end
170
168
 
171
169
  # Sets the y value of a coordinate. Can also be set via #y[]=.
172
170
  def set_y(idx, val)
173
- self.check_bounds(idx)
174
- FFIGeos.GEOSCoordSeq_setY_r(Geos.current_handle_pointer, self.ptr, idx, val.to_f)
171
+ check_bounds(idx)
172
+ FFIGeos.GEOSCoordSeq_setY_r(Geos.current_handle_pointer, ptr, idx, val.to_f)
175
173
  end
176
174
 
177
175
  # Sets the z value of a coordinate. Can also be set via #z[]=.
178
176
  def set_z(idx, val)
179
- self.check_bounds(idx)
180
- FFIGeos.GEOSCoordSeq_setZ_r(Geos.current_handle_pointer, self.ptr, idx, val.to_f)
177
+ check_bounds(idx)
178
+ FFIGeos.GEOSCoordSeq_setZ_r(Geos.current_handle_pointer, ptr, idx, val.to_f)
181
179
  end
182
180
 
183
181
  def set_ordinate(idx, dim, val)
184
- self.check_bounds(idx)
185
- FFIGeos.GEOSCoordSeq_setOrdinate_r(Geos.current_handle_pointer, self.ptr, idx, dim, val.to_f)
182
+ check_bounds(idx)
183
+ FFIGeos.GEOSCoordSeq_setOrdinate_r(Geos.current_handle_pointer, ptr, idx, dim, val.to_f)
186
184
  end
187
185
 
188
186
  # Gets the x value of a coordinate. Can also be retrieved via #x[].
189
187
  def get_x(idx)
190
- self.check_bounds(idx)
188
+ check_bounds(idx)
191
189
  double_ptr = FFI::MemoryPointer.new(:double)
192
- FFIGeos.GEOSCoordSeq_getX_r(Geos.current_handle_pointer, self.ptr, idx, double_ptr)
190
+ FFIGeos.GEOSCoordSeq_getX_r(Geos.current_handle_pointer, ptr, idx, double_ptr)
193
191
  double_ptr.read_double
194
192
  end
195
193
 
196
194
  # Gets the y value of a coordinate. Can also be retrieved via #y[].
197
195
  def get_y(idx)
198
- self.check_bounds(idx)
196
+ check_bounds(idx)
199
197
  double_ptr = FFI::MemoryPointer.new(:double)
200
- FFIGeos.GEOSCoordSeq_getY_r(Geos.current_handle_pointer, self.ptr, idx, double_ptr)
198
+ FFIGeos.GEOSCoordSeq_getY_r(Geos.current_handle_pointer, ptr, idx, double_ptr)
201
199
  double_ptr.read_double
202
200
  end
203
201
 
204
202
  # Gets the z value of a coordinate. Can also be retrieved via #z[].
205
203
  def get_z(idx)
206
- self.check_bounds(idx)
204
+ check_bounds(idx)
207
205
  double_ptr = FFI::MemoryPointer.new(:double)
208
- FFIGeos.GEOSCoordSeq_getZ_r(Geos.current_handle_pointer, self.ptr, idx, double_ptr)
206
+ FFIGeos.GEOSCoordSeq_getZ_r(Geos.current_handle_pointer, ptr, idx, double_ptr)
209
207
  double_ptr.read_double
210
208
  end
211
209
 
212
210
  def get_ordinate(idx, dim)
213
- self.check_bounds(idx)
211
+ check_bounds(idx)
214
212
  double_ptr = FFI::MemoryPointer.new(:double)
215
- FFIGeos.GEOSCoordSeq_getOrdinate_r(Geos.current_handle_pointer, self.ptr, idx, dim, double_ptr)
213
+ FFIGeos.GEOSCoordSeq_getOrdinate_r(Geos.current_handle_pointer, ptr, idx, dim, double_ptr)
216
214
  double_ptr.read_double
217
215
  end
218
216
 
219
217
  def length
220
218
  int_ptr = FFI::MemoryPointer.new(:int)
221
- FFIGeos.GEOSCoordSeq_getSize_r(Geos.current_handle_pointer, self.ptr, int_ptr)
219
+ FFIGeos.GEOSCoordSeq_getSize_r(Geos.current_handle_pointer, ptr, int_ptr)
222
220
  int_ptr.read_int
223
221
  end
224
- alias_method :size, :length
222
+ alias size length
225
223
 
226
224
  def empty?
227
- self.length == 0
225
+ length.zero?
228
226
  end
229
227
 
230
228
  def dimensions
@@ -232,47 +230,333 @@ module Geos
232
230
  @dimensions
233
231
  else
234
232
  int_ptr = FFI::MemoryPointer.new(:int)
235
- FFIGeos.GEOSCoordSeq_getDimensions_r(Geos.current_handle_pointer, self.ptr, int_ptr)
233
+ FFIGeos.GEOSCoordSeq_getDimensions_r(Geos.current_handle_pointer, ptr, int_ptr)
236
234
  @dimensions = int_ptr.read_int
237
235
  end
238
236
  end
239
237
 
240
238
  def to_point(options = {})
241
- Geos.create_point(self, :srid => options[:srid])
239
+ Geos.create_point(self, srid: options[:srid])
242
240
  end
243
241
 
244
242
  def to_linear_ring(options = {})
245
- Geos.create_linear_ring(self, :srid => options[:srid])
243
+ Geos.create_linear_ring(self, srid: options[:srid])
246
244
  end
247
245
 
248
246
  def to_line_string(options = {})
249
- Geos.create_line_string(self, :srid => options[:srid])
247
+ Geos.create_line_string(self, srid: options[:srid])
250
248
  end
251
249
 
252
250
  def to_polygon(options = {})
253
- Geos.create_polygon(self, :srid => options[:srid])
251
+ Geos.create_polygon(self, srid: options[:srid])
254
252
  end
255
253
 
256
254
  def to_s
257
- self.entries.collect { |entry|
255
+ entries.collect { |entry|
258
256
  entry.join(' ')
259
257
  }.join(', ')
260
258
  end
261
259
 
262
- protected
260
+ %w{ x y z }.each do |m|
261
+ class_eval(<<-EOF, __FILE__, __LINE__ + 1)
262
+ def #{m}_max
263
+ ret = nil
264
+ self.length.times do |i|
265
+ value = self.get_#{m}(i)
266
+ ret = value if !ret || value >= ret
267
+ end
268
+ ret
269
+ end
270
+
271
+ def #{m}_min
272
+ ret = nil
273
+ self.length.times do |i|
274
+ value = self.get_#{m}(i)
275
+ ret = value if !ret || value <= ret
276
+ end
277
+ ret
278
+ end
279
+ EOF
280
+ end
281
+
282
+ def snap_to_grid!(*args)
283
+ grid = {
284
+ :offset_x => 0, # 1
285
+ :offset_y => 0, # 2
286
+ :offset_z => 0, # -
287
+ :size_x => 0, # 3
288
+ :size_y => 0, # 4
289
+ :size_z => 0 # -
290
+ }
291
+
292
+ if args.length == 1 && args[0].is_a?(Numeric)
293
+ grid[:size_x] = grid[:size_y] = grid[:size_z] = args[0]
294
+ elsif args[0].is_a?(Hash)
295
+ grid.merge!(args[0])
296
+ end
297
+
298
+ if grid[:size]
299
+ grid[:size_x] = grid[:size_y] = grid[:size_z] = grid[:size]
300
+ end
301
+
302
+ if grid[:offset]
303
+ case grid[:offset]
304
+ when Geos::Geometry
305
+ point = grid[:offset].centroid
306
+
307
+ grid[:offset_x] = point.x
308
+ grid[:offset_y] = point.y
309
+ grid[:offset_z] = point.z
310
+ when Array
311
+ grid[:offset_x], grid[:offset_y], grid[:offset_z] = grid[:offset]
312
+ else
313
+ raise ArgumentError.new("Expected :offset option to be a Geos::Point")
314
+ end
315
+ end
316
+
317
+ self.length.times do |i|
318
+ if grid[:size_x] != 0
319
+ self.x[i] = ((self.x[i] - grid[:offset_x]) / grid[:size_x]).round * grid[:size_x] + grid[:offset_x]
320
+ end
321
+
322
+ if grid[:size_y] != 0
323
+ self.y[i] = ((self.y[i] - grid[:offset_y]) / grid[:size_y]).round * grid[:size_y] + grid[:offset_y]
324
+ end
325
+
326
+ if self.has_z? && grid[:size_z] != 0
327
+ self.z[i] = ((self.z[i] - grid[:offset_z]) / grid[:size_z]).round * grid[:size_z] + grid[:offset_z]
328
+ end
329
+ end
330
+
331
+ cs = self.remove_duplicate_coords
332
+ @ptr = cs.ptr
333
+
334
+ self
335
+ end
336
+
337
+ def snap_to_grid(*args)
338
+ self.dup.snap_to_grid!(*args)
339
+ end
340
+
341
+ def remove_duplicate_coords
342
+ Geos::CoordinateSequence.new(self.to_a.inject([]) { |memo, v|
343
+ memo << v unless memo.last == v
344
+ memo
345
+ })
346
+ end
347
+
348
+ def affine!(options)
349
+ options.default = 0.0
350
+
351
+ if self.has_z?
352
+ self.length.times do |i|
353
+ x, y, z = self.x[i], self.y[i], self.z[i]
354
+
355
+ self.x[i] = options[:afac] * x + options[:bfac] * y + options[:cfac] * z + options[:xoff]
356
+ self.y[i] = options[:dfac] * x + options[:efac] * y + options[:ffac] * z + options[:yoff]
357
+ self.z[i] = options[:gfac] * x + options[:hfac] * y + options[:ifac] * z + options[:zoff]
358
+ end
359
+ else
360
+ self.length.times do |i|
361
+ x, y = self.x[i], self.y[i]
362
+
363
+ self.x[i] = options[:afac] * x + options[:bfac] * y + options[:xoff]
364
+ self.y[i] = options[:dfac] * x + options[:efac] * y + options[:yoff]
365
+ end
366
+ end
367
+
368
+ self
369
+ end
263
370
 
264
- def check_bounds(idx) #:nodoc:
265
- if idx < 0 || idx >= self.length
266
- raise Geos::IndexBoundsError.new("Index out of bounds")
371
+ def affine(options)
372
+ self.dup.affine!(options)
373
+ end
374
+
375
+ def rotate!(radians, origin = [ 0.0, 0.0 ])
376
+ origin = case origin
377
+ when Array
378
+ origin
379
+ when Geos::Geometry
380
+ center = origin.centroid
381
+ [ center.x, center.y ]
382
+ else
383
+ raise ArgumentError.new("Expected an Array or a Geos::Geometry for the origin")
267
384
  end
385
+
386
+ self.affine!({
387
+ :afac => Math.cos(radians),
388
+ :bfac => -Math.sin(radians),
389
+ :cfac => 0,
390
+ :dfac => Math.sin(radians),
391
+ :efac => Math.cos(radians),
392
+ :ffac => 0,
393
+ :gfac => 0,
394
+ :hfac => 0,
395
+ :ifac => 1,
396
+ :xoff => origin[0] - Math.cos(radians) * origin[0] + Math.sin(radians) * origin[1],
397
+ :yoff => origin[1] - Math.sin(radians) * origin[0] - Math.cos(radians) * origin[1],
398
+ :zoff => 0
399
+ })
400
+ end
401
+
402
+ def rotate(radians, origin = [ 0.0, 0.0 ])
403
+ self.dup.rotate!(radians, origin)
404
+ end
405
+
406
+ def rotate_x!(radians)
407
+ self.affine!({
408
+ :afac => 1,
409
+ :bfac => 0,
410
+ :cfac => 0,
411
+ :dfac => 0,
412
+ :efac => Math.cos(radians),
413
+ :ffac => -Math.sin(radians),
414
+ :gfac => 0,
415
+ :hfac => Math.sin(radians),
416
+ :ifac => Math.cos(radians),
417
+ :xoff => 0,
418
+ :yoff => 0,
419
+ :zoff => 0
420
+ })
421
+ end
422
+
423
+ def rotate_x(radians)
424
+ self.dup.rotate_x!(radians)
425
+ end
426
+
427
+ def rotate_y!(radians)
428
+ self.affine!({
429
+ :afac => Math.cos(radians),
430
+ :bfac => 0,
431
+ :cfac => Math.sin(radians),
432
+ :dfac => 0,
433
+ :efac => 1,
434
+ :ffac => 0,
435
+ :gfac => -Math.sin(radians),
436
+ :hfac => 0,
437
+ :ifac => Math.cos(radians),
438
+ :xoff => 0,
439
+ :yoff => 0,
440
+ :zoff => 0
441
+ })
442
+ end
443
+
444
+ def rotate_y(radians)
445
+ self.dup.rotate_y!(radians)
268
446
  end
269
447
 
270
- def build_coordinate(n) #:nodoc:
271
- [
272
- self.get_x(n),
273
- (self.dimensions >= 2 ? self.get_y(n) : nil),
274
- (self.dimensions >= 3 ? self.get_z(n) : nil)
275
- ].compact
448
+ def rotate_z!(radians)
449
+ self.rotate!(radians)
276
450
  end
451
+
452
+ def rotate_z(radians)
453
+ self.dup.rotate!(radians)
454
+ end
455
+
456
+ def scale!(*args)
457
+ x, y, z = if args.length == 1 && args[0].is_a?(Hash)
458
+ args[0].values_at(:x, :y, :z)
459
+ elsif args.length.between?(1, 3)
460
+ args.values_at(0...3)
461
+ else
462
+ raise ArgumentError.new("Wrong number of arguments #{args.length} for 1-3")
463
+ end
464
+
465
+ self.affine!({
466
+ :afac => x || 1,
467
+ :bfac => 0,
468
+ :cfac => 0,
469
+ :dfac => 0,
470
+ :efac => y || 1,
471
+ :ffac => 0,
472
+ :gfac => 0,
473
+ :hfac => 0,
474
+ :ifac => z || 1,
475
+ :xoff => 0,
476
+ :yoff => 0,
477
+ :zoff => 0
478
+ })
479
+ end
480
+
481
+ def scale(*args)
482
+ self.dup.scale!(*args)
483
+ end
484
+
485
+ def trans_scale!(*args)
486
+ delta_x, delta_y, x_factor, y_factor = if args.length == 1 && args[0].is_a?(Hash)
487
+ args[0].values_at(:delta_x, :delta_y, :x_factor, :y_factor)
488
+ elsif args.length.between?(1, 4)
489
+ args.values_at(0...4)
490
+ else
491
+ raise ArgumentError.new("Wrong number of arguments #{args.length} for 1-4")
492
+ end
493
+
494
+ x_factor ||= 1
495
+ y_factor ||= 1
496
+ delta_x ||= 0
497
+ delta_y ||= 0
498
+
499
+ self.affine!({
500
+ :afac => x_factor,
501
+ :bfac => 0,
502
+ :cfac => 0,
503
+ :dfac => 0,
504
+ :efac => y_factor,
505
+ :ffac => 0,
506
+ :gfac => 0,
507
+ :hfac => 0,
508
+ :ifac => 1,
509
+ :xoff => delta_x * x_factor,
510
+ :yoff => delta_y * y_factor,
511
+ :zoff => 0
512
+ })
513
+ end
514
+
515
+ def trans_scale(*args)
516
+ self.dup.trans_scale!(*args)
517
+ end
518
+
519
+ def translate!(*args)
520
+ x, y, z = if args.length == 1 && args[0].is_a?(Hash)
521
+ args[0].values_at(:x, :y, :z)
522
+ elsif args.length.between?(1, 3)
523
+ args.values_at(0...3)
524
+ else
525
+ raise ArgumentError.new("Wrong number of arguments #{args.length} for 1-3")
526
+ end
527
+
528
+ self.affine!({
529
+ :afac => 1,
530
+ :bfac => 0,
531
+ :cfac => 0,
532
+ :dfac => 0,
533
+ :efac => 1,
534
+ :ffac => 0,
535
+ :gfac => 0,
536
+ :hfac => 0,
537
+ :ifac => 1,
538
+ :xoff => x || 0,
539
+ :yoff => y || 0,
540
+ :zoff => z || 1
541
+ })
542
+ end
543
+
544
+ def translate(*args)
545
+ self.dup.translate!(*args)
546
+ end
547
+
548
+ protected
549
+
550
+ def check_bounds(idx) #:nodoc:
551
+ raise Geos::IndexBoundsError, 'Index out of bounds' if idx.negative? || idx >= length
552
+ end
553
+
554
+ def build_coordinate(n) #:nodoc:
555
+ [
556
+ get_x(n),
557
+ (dimensions >= 2 ? get_y(n) : nil),
558
+ (dimensions >= 3 ? get_z(n) : nil)
559
+ ].compact
560
+ end
277
561
  end
278
562
  end