ffi-geos 1.2.1 → 1.2.2

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