ffi-geos 1.2.2 → 2.3.0

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