ffi-geos 1.2.2 → 2.3.0

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +49 -0
  3. data/.rubocop.yml +5117 -4
  4. data/FUNDING.yml +2 -0
  5. data/Gemfile +9 -16
  6. data/Guardfile +3 -4
  7. data/MIT-LICENSE +1 -1
  8. data/README.rdoc +2 -20
  9. data/Rakefile +3 -2
  10. data/ffi-geos.gemspec +7 -2
  11. data/lib/ffi-geos/buffer_params.rb +1 -1
  12. data/lib/ffi-geos/coordinate_sequence.rb +179 -177
  13. data/lib/ffi-geos/geometry.rb +118 -31
  14. data/lib/ffi-geos/geometry_collection.rb +26 -12
  15. data/lib/ffi-geos/interrupt.rb +11 -14
  16. data/lib/ffi-geos/line_string.rb +64 -49
  17. data/lib/ffi-geos/multi_line_string.rb +1 -1
  18. data/lib/ffi-geos/point.rb +18 -18
  19. data/lib/ffi-geos/polygon.rb +44 -30
  20. data/lib/ffi-geos/prepared_geometry.rb +1 -1
  21. data/lib/ffi-geos/strtree.rb +28 -30
  22. data/lib/ffi-geos/tools.rb +1 -1
  23. data/lib/ffi-geos/utils.rb +16 -23
  24. data/lib/ffi-geos/version.rb +1 -1
  25. data/lib/ffi-geos/wkb_reader.rb +1 -1
  26. data/lib/ffi-geos/wkb_writer.rb +4 -5
  27. data/lib/ffi-geos/wkt_reader.rb +1 -1
  28. data/lib/ffi-geos/wkt_writer.rb +7 -13
  29. data/lib/ffi-geos.rb +134 -48
  30. data/sonar-project.properties +16 -0
  31. data/test/coordinate_sequence_tests.rb +148 -126
  32. data/test/geometry_collection_tests.rb +41 -67
  33. data/test/geometry_tests.rb +341 -40
  34. data/test/interrupt_tests.rb +7 -7
  35. data/test/line_string_tests.rb +23 -15
  36. data/test/point_tests.rb +5 -5
  37. data/test/polygon_tests.rb +6 -7
  38. data/test/prepared_geometry_tests.rb +8 -8
  39. data/test/strtree_tests.rb +13 -12
  40. data/test/test_helper.rb +74 -56
  41. data/test/utils_tests.rb +69 -59
  42. data/test/wkb_reader_tests.rb +9 -9
  43. data/test/wkb_writer_tests.rb +14 -12
  44. data/test/wkt_reader_tests.rb +0 -1
  45. data/test/wkt_writer_tests.rb +2 -5
  46. metadata +12 -10
  47. data/.travis.yml +0 -21
@@ -69,28 +69,26 @@ module Geos
69
69
  lengths = points.collect(&:length).uniq
70
70
 
71
71
  if lengths.empty?
72
- [ 0, 0 ]
72
+ [0, 0]
73
73
  elsif lengths.length != 1
74
74
  raise ParseError, 'Different sized points found in Array'
75
75
  elsif !lengths.first.between?(1, 3)
76
76
  raise ParseError, 'Expected points to contain 1-3 elements'
77
77
  else
78
- [ points.length, points.first.length ]
78
+ [points.length, points.first.length]
79
79
  end
80
80
  elsif args.first.is_a?(Hash)
81
81
  args.first.values_at(:size, :dimensions)
82
+ elsif !args.length.between?(0, 2)
83
+ raise ArgumentError, "wrong number of arguments (#{args.length} for 0-2)"
82
84
  else
83
- if !args.length.between?(0, 2)
84
- raise ArgumentError, "wrong number of arguments (#{args.length} for 0-2)"
85
- else
86
- [ args[0], args[1] ]
87
- end
85
+ [args[0], args[1]]
88
86
  end
89
87
 
90
88
  size ||= 0
91
89
  dimensions ||= 0
92
90
 
93
- [ FFIGeos.GEOSCoordSeq_create_r(Geos.current_handle_pointer, size, dimensions), true ]
91
+ [FFIGeos.GEOSCoordSeq_create_r(Geos.current_handle_pointer, size, dimensions), true]
94
92
  end
95
93
 
96
94
  @ptr = FFI::AutoPointer.new(
@@ -125,7 +123,7 @@ module Geos
125
123
  @z = CoordinateAccessor.new(self, 2)
126
124
  end
127
125
 
128
- def self.release(ptr) #:nodoc:
126
+ def self.release(ptr) # :nodoc:
129
127
  FFIGeos.GEOSCoordSeq_destroy_r(Geos.current_handle_pointer, ptr)
130
128
  end
131
129
 
@@ -147,7 +145,7 @@ module Geos
147
145
  def [](*args)
148
146
  if args.length == 1 && args.first.is_a?(Numeric) && args.first >= 0
149
147
  i = args.first
150
- ary = [ get_x(i), get_y(i) ]
148
+ ary = [get_x(i), get_y(i)]
151
149
  ary << get_z(i) if has_z?
152
150
  ary
153
151
  else
@@ -235,6 +233,16 @@ module Geos
235
233
  end
236
234
  end
237
235
 
236
+ if FFIGeos.respond_to?(:GEOSCoordSeq_isCCW_r)
237
+ # Available in GEOS 3.7+.
238
+ def counter_clockwise?
239
+ char_ptr = FFI::MemoryPointer.new(:char)
240
+ FFIGeos.GEOSCoordSeq_isCCW_r(Geos.current_handle_pointer, ptr, char_ptr)
241
+ Tools.bool_result(char_ptr.read_char)
242
+ end
243
+ alias ccw? counter_clockwise?
244
+ end
245
+
238
246
  def to_point(options = {})
239
247
  Geos.create_point(self, srid: options[:srid])
240
248
  end
@@ -258,11 +266,11 @@ module Geos
258
266
  end
259
267
 
260
268
  %w{ x y z }.each do |m|
261
- class_eval(<<-EOF, __FILE__, __LINE__ + 1)
269
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
262
270
  def #{m}_max
263
271
  ret = nil
264
- self.length.times do |i|
265
- value = self.get_#{m}(i)
272
+ length.times do |i|
273
+ value = get_#{m}(i)
266
274
  ret = value if !ret || value >= ret
267
275
  end
268
276
  ret
@@ -270,34 +278,32 @@ module Geos
270
278
 
271
279
  def #{m}_min
272
280
  ret = nil
273
- self.length.times do |i|
274
- value = self.get_#{m}(i)
281
+ length.times do |i|
282
+ value = get_#{m}(i)
275
283
  ret = value if !ret || value <= ret
276
284
  end
277
285
  ret
278
286
  end
279
- EOF
287
+ RUBY
280
288
  end
281
289
 
282
- def snap_to_grid!(*args)
290
+ def snap_to_grid!(*args, **kwargs)
283
291
  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 # -
292
+ offset_x: 0, # 1
293
+ offset_y: 0, # 2
294
+ offset_z: 0, # -
295
+ size_x: 0, # 3
296
+ size_y: 0, # 4
297
+ size_z: 0 # -
290
298
  }
291
299
 
292
300
  if args.length == 1 && args[0].is_a?(Numeric)
293
301
  grid[:size_x] = grid[:size_y] = grid[:size_z] = args[0]
294
- elsif args[0].is_a?(Hash)
295
- grid.merge!(args[0])
302
+ elsif !kwargs.empty?
303
+ grid.merge!(kwargs)
296
304
  end
297
305
 
298
- if grid[:size]
299
- grid[:size_x] = grid[:size_y] = grid[:size_z] = grid[:size]
300
- end
306
+ grid[:size_x] = grid[:size_y] = grid[:size_z] = grid[:size] if grid[:size]
301
307
 
302
308
  if grid[:offset]
303
309
  case grid[:offset]
@@ -310,58 +316,54 @@ module Geos
310
316
  when Array
311
317
  grid[:offset_x], grid[:offset_y], grid[:offset_z] = grid[:offset]
312
318
  else
313
- raise ArgumentError.new("Expected :offset option to be a Geos::Point")
319
+ raise ArgumentError, 'Expected :offset option to be a Geos::Point'
314
320
  end
315
321
  end
316
322
 
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
323
+ length.times do |i|
324
+ x[i] = (((x[i] - grid[:offset_x]) / grid[:size_x]).round * grid[:size_x]) + grid[:offset_x] if grid[:size_x] != 0
321
325
 
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
326
+ y[i] = (((y[i] - grid[:offset_y]) / grid[:size_y]).round * grid[:size_y]) + grid[:offset_y] if grid[:size_y] != 0
325
327
 
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
328
+ z[i] = (((z[i] - grid[:offset_z]) / grid[:size_z]).round * grid[:size_z]) + grid[:offset_z] if has_z? && grid[:size_z] != 0
329
329
  end
330
330
 
331
- cs = self.remove_duplicate_coords
331
+ cs = remove_duplicate_coords
332
332
  @ptr = cs.ptr
333
333
 
334
334
  self
335
335
  end
336
336
 
337
- def snap_to_grid(*args)
338
- self.dup.snap_to_grid!(*args)
337
+ def snap_to_grid(*args, **)
338
+ dup.snap_to_grid!(*args)
339
339
  end
340
340
 
341
341
  def remove_duplicate_coords
342
- Geos::CoordinateSequence.new(self.to_a.inject([]) { |memo, v|
342
+ Geos::CoordinateSequence.new(to_a.each_with_object([]) do |v, memo|
343
343
  memo << v unless memo.last == v
344
- memo
345
- })
344
+ end)
346
345
  end
347
346
 
348
347
  def affine!(options)
349
348
  options.default = 0.0
350
349
 
351
- if self.has_z?
352
- self.length.times do |i|
353
- x, y, z = self.x[i], self.y[i], self.z[i]
350
+ if has_z?
351
+ length.times do |i|
352
+ x = self.x[i]
353
+ y = self.y[i]
354
+ z = self.z[i]
354
355
 
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]
356
+ self.x[i] = (options[:afac] * x) + (options[:bfac] * y) + (options[:cfac] * z) + options[:xoff]
357
+ self.y[i] = (options[:dfac] * x) + (options[:efac] * y) + (options[:ffac] * z) + options[:yoff]
358
+ self.z[i] = (options[:gfac] * x) + (options[:hfac] * y) + (options[:ifac] * z) + options[:zoff]
358
359
  end
359
360
  else
360
- self.length.times do |i|
361
- x, y = self.x[i], self.y[i]
361
+ length.times do |i|
362
+ x = self.x[i]
363
+ y = self.y[i]
362
364
 
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
+ self.x[i] = (options[:afac] * x) + (options[:bfac] * y) + options[:xoff]
366
+ self.y[i] = (options[:dfac] * x) + (options[:efac] * y) + options[:yoff]
365
367
  end
366
368
  end
367
369
 
@@ -369,126 +371,126 @@ module Geos
369
371
  end
370
372
 
371
373
  def affine(options)
372
- self.dup.affine!(options)
374
+ dup.affine!(options)
373
375
  end
374
376
 
375
- def rotate!(radians, origin = [ 0.0, 0.0 ])
377
+ def rotate!(radians, origin = [0.0, 0.0])
376
378
  origin = case origin
377
379
  when Array
378
380
  origin
379
381
  when Geos::Geometry
380
382
  center = origin.centroid
381
- [ center.x, center.y ]
383
+ [center.x, center.y]
382
384
  else
383
- raise ArgumentError.new("Expected an Array or a Geos::Geometry for the origin")
385
+ raise ArgumentError, 'Expected an Array or a Geos::Geometry for the origin'
384
386
  end
385
387
 
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
- })
388
+ affine!(
389
+ afac: Math.cos(radians),
390
+ bfac: -Math.sin(radians),
391
+ cfac: 0,
392
+ dfac: Math.sin(radians),
393
+ efac: Math.cos(radians),
394
+ ffac: 0,
395
+ gfac: 0,
396
+ hfac: 0,
397
+ ifac: 1,
398
+ xoff: origin[0] - (Math.cos(radians) * origin[0]) + (Math.sin(radians) * origin[1]),
399
+ yoff: origin[1] - (Math.sin(radians) * origin[0]) - (Math.cos(radians) * origin[1]),
400
+ zoff: 0
401
+ )
400
402
  end
401
403
 
402
- def rotate(radians, origin = [ 0.0, 0.0 ])
403
- self.dup.rotate!(radians, origin)
404
+ def rotate(radians, origin = [0.0, 0.0])
405
+ dup.rotate!(radians, origin)
404
406
  end
405
407
 
406
408
  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
- })
409
+ affine!(
410
+ afac: 1,
411
+ bfac: 0,
412
+ cfac: 0,
413
+ dfac: 0,
414
+ efac: Math.cos(radians),
415
+ ffac: -Math.sin(radians),
416
+ gfac: 0,
417
+ hfac: Math.sin(radians),
418
+ ifac: Math.cos(radians),
419
+ xoff: 0,
420
+ yoff: 0,
421
+ zoff: 0
422
+ )
421
423
  end
422
424
 
423
425
  def rotate_x(radians)
424
- self.dup.rotate_x!(radians)
426
+ dup.rotate_x!(radians)
425
427
  end
426
428
 
427
429
  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
- })
430
+ affine!(
431
+ afac: Math.cos(radians),
432
+ bfac: 0,
433
+ cfac: Math.sin(radians),
434
+ dfac: 0,
435
+ efac: 1,
436
+ ffac: 0,
437
+ gfac: -Math.sin(radians),
438
+ hfac: 0,
439
+ ifac: Math.cos(radians),
440
+ xoff: 0,
441
+ yoff: 0,
442
+ zoff: 0
443
+ )
442
444
  end
443
445
 
444
446
  def rotate_y(radians)
445
- self.dup.rotate_y!(radians)
447
+ dup.rotate_y!(radians)
446
448
  end
447
449
 
448
450
  def rotate_z!(radians)
449
- self.rotate!(radians)
451
+ rotate!(radians)
450
452
  end
451
453
 
452
454
  def rotate_z(radians)
453
- self.dup.rotate!(radians)
455
+ dup.rotate!(radians)
454
456
  end
455
457
 
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)
458
+ def scale!(*args, **kwargs)
459
+ x, y, z = if !kwargs.empty?
460
+ kwargs.values_at(:x, :y, :z)
459
461
  elsif args.length.between?(1, 3)
460
462
  args.values_at(0...3)
461
463
  else
462
- raise ArgumentError.new("Wrong number of arguments #{args.length} for 1-3")
464
+ raise ArgumentError, "Wrong number of arguments #{args.length} for 1-3"
463
465
  end
464
466
 
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)
467
+ affine!(
468
+ afac: x || 1,
469
+ bfac: 0,
470
+ cfac: 0,
471
+ dfac: 0,
472
+ efac: y || 1,
473
+ ffac: 0,
474
+ gfac: 0,
475
+ hfac: 0,
476
+ ifac: z || 1,
477
+ xoff: 0,
478
+ yoff: 0,
479
+ zoff: 0
480
+ )
481
+ end
482
+
483
+ def scale(*args, **kwargs)
484
+ dup.scale!(*args, **kwargs)
485
+ end
486
+
487
+ def trans_scale!(*args, **kwargs)
488
+ delta_x, delta_y, x_factor, y_factor = if !kwargs.empty?
489
+ kwargs.values_at(:delta_x, :delta_y, :x_factor, :y_factor)
488
490
  elsif args.length.between?(1, 4)
489
491
  args.values_at(0...4)
490
492
  else
491
- raise ArgumentError.new("Wrong number of arguments #{args.length} for 1-4")
493
+ raise ArgumentError, "Wrong number of arguments #{args.length} for 1-4"
492
494
  end
493
495
 
494
496
  x_factor ||= 1
@@ -496,62 +498,62 @@ module Geos
496
498
  delta_x ||= 0
497
499
  delta_y ||= 0
498
500
 
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)
501
+ affine!(
502
+ afac: x_factor,
503
+ bfac: 0,
504
+ cfac: 0,
505
+ dfac: 0,
506
+ efac: y_factor,
507
+ ffac: 0,
508
+ gfac: 0,
509
+ hfac: 0,
510
+ ifac: 1,
511
+ xoff: delta_x * x_factor,
512
+ yoff: delta_y * y_factor,
513
+ zoff: 0
514
+ )
515
+ end
516
+
517
+ def trans_scale(*args, **kwargs)
518
+ dup.trans_scale!(*args, **kwargs)
519
+ end
520
+
521
+ def translate!(*args, **kwargs)
522
+ x, y, z = if !kwargs.empty?
523
+ kwargs.values_at(:x, :y, :z)
522
524
  elsif args.length.between?(1, 3)
523
525
  args.values_at(0...3)
524
526
  else
525
- raise ArgumentError.new("Wrong number of arguments #{args.length} for 1-3")
527
+ raise ArgumentError, "Wrong number of arguments #{args.length} for 1-3"
526
528
  end
527
529
 
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
- })
530
+ affine!(
531
+ afac: 1,
532
+ bfac: 0,
533
+ cfac: 0,
534
+ dfac: 0,
535
+ efac: 1,
536
+ ffac: 0,
537
+ gfac: 0,
538
+ hfac: 0,
539
+ ifac: 1,
540
+ xoff: x || 0,
541
+ yoff: y || 0,
542
+ zoff: z || 1
543
+ )
542
544
  end
543
545
 
544
- def translate(*args)
545
- self.dup.translate!(*args)
546
+ def translate(*args, **kwargs)
547
+ dup.translate!(*args, **kwargs)
546
548
  end
547
549
 
548
550
  protected
549
551
 
550
- def check_bounds(idx) #:nodoc:
552
+ def check_bounds(idx) # :nodoc:
551
553
  raise Geos::IndexBoundsError, 'Index out of bounds' if idx.negative? || idx >= length
552
554
  end
553
555
 
554
- def build_coordinate(n) #:nodoc:
556
+ def build_coordinate(n) # :nodoc:
555
557
  [
556
558
  get_x(n),
557
559
  (dimensions >= 2 ? get_y(n) : nil),