ffi-geos 1.2.0 → 2.2.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 (54) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +4851 -0
  3. data/.travis.yml +24 -9
  4. data/FUNDING.yml +2 -0
  5. data/Gemfile +12 -16
  6. data/Guardfile +6 -8
  7. data/MIT-LICENSE +1 -1
  8. data/README.rdoc +2 -20
  9. data/Rakefile +4 -2
  10. data/ffi-geos.gemspec +13 -14
  11. data/lib/ffi-geos.rb +342 -244
  12. data/lib/ffi-geos/buffer_params.rb +9 -20
  13. data/lib/ffi-geos/coordinate_sequence.rb +351 -65
  14. data/lib/ffi-geos/geometry.rb +267 -191
  15. data/lib/ffi-geos/geometry_collection.rb +74 -12
  16. data/lib/ffi-geos/interrupt.rb +11 -16
  17. data/lib/ffi-geos/line_string.rb +157 -33
  18. data/lib/ffi-geos/linear_ring.rb +2 -3
  19. data/lib/ffi-geos/multi_line_string.rb +1 -2
  20. data/lib/ffi-geos/multi_point.rb +0 -1
  21. data/lib/ffi-geos/multi_polygon.rb +0 -1
  22. data/lib/ffi-geos/point.rb +70 -15
  23. data/lib/ffi-geos/polygon.rb +124 -21
  24. data/lib/ffi-geos/prepared_geometry.rb +11 -12
  25. data/lib/ffi-geos/strtree.rb +64 -77
  26. data/lib/ffi-geos/tools.rb +16 -19
  27. data/lib/ffi-geos/utils.rb +36 -60
  28. data/lib/ffi-geos/version.rb +1 -3
  29. data/lib/ffi-geos/wkb_reader.rb +4 -9
  30. data/lib/ffi-geos/wkb_writer.rb +15 -20
  31. data/lib/ffi-geos/wkt_reader.rb +2 -5
  32. data/lib/ffi-geos/wkt_writer.rb +20 -31
  33. data/sonar-project.properties +16 -0
  34. data/test/.rubocop.yml +36 -0
  35. data/test/coordinate_sequence_tests.rb +322 -52
  36. data/test/geometry_collection_tests.rb +388 -4
  37. data/test/geometry_tests.rb +466 -121
  38. data/test/interrupt_tests.rb +9 -12
  39. data/test/line_string_tests.rb +213 -25
  40. data/test/linear_ring_tests.rb +1 -3
  41. data/test/misc_tests.rb +28 -30
  42. data/test/multi_line_string_tests.rb +0 -2
  43. data/test/point_tests.rb +158 -2
  44. data/test/polygon_tests.rb +283 -2
  45. data/test/prepared_geometry_tests.rb +8 -11
  46. data/test/strtree_tests.rb +14 -15
  47. data/test/test_helper.rb +75 -51
  48. data/test/tools_tests.rb +1 -4
  49. data/test/utils_tests.rb +85 -76
  50. data/test/wkb_reader_tests.rb +18 -18
  51. data/test/wkb_writer_tests.rb +15 -22
  52. data/test/wkt_reader_tests.rb +1 -4
  53. data/test/wkt_writer_tests.rb +8 -17
  54. metadata +11 -7
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Geos
@@ -8,34 +7,97 @@ module Geos
8
7
  # Yields each Geometry in the GeometryCollection.
9
8
  def each
10
9
  if block_given?
11
- self.num_geometries.times do |n|
12
- yield self.get_geometry_n(n)
10
+ num_geometries.times do |n|
11
+ yield get_geometry_n(n)
13
12
  end
14
13
  self
15
14
  else
16
- self.num_geometries.times.collect { |n|
17
- self.get_geometry_n(n)
15
+ num_geometries.times.collect { |n|
16
+ get_geometry_n(n)
18
17
  }.to_enum
19
18
  end
20
19
  end
21
20
 
22
21
  def get_geometry_n(n)
23
- if n < 0 || n >= self.num_geometries
22
+ if n.negative? || n >= num_geometries
24
23
  nil
25
24
  else
26
- cast_geometry_ptr(FFIGeos.GEOSGetGeometryN_r(Geos.current_handle_pointer, self.ptr, n), :auto_free => false)
25
+ cast_geometry_ptr(FFIGeos.GEOSGetGeometryN_r(Geos.current_handle_pointer, ptr, n), auto_free: false)
27
26
  end
28
27
  end
29
- alias_method :geometry_n, :get_geometry_n
28
+ alias geometry_n get_geometry_n
30
29
 
31
30
  def [](*args)
32
31
  if args.length == 1 && args.first.is_a?(Numeric) && args.first >= 0
33
- self.get_geometry_n(args.first)
32
+ get_geometry_n(args.first)
34
33
  else
35
- self.to_a[*args]
34
+ to_a[*args]
36
35
  end
37
36
  end
38
- alias_method :slice, :[]
39
- alias_method :at, :[]
37
+ alias slice []
38
+ alias at []
39
+
40
+ def dump_points(cur_path = [])
41
+ each do |geom|
42
+ cur_path << geom.dump_points
43
+ end
44
+ cur_path
45
+ end
46
+
47
+ %w{ x y z }.each do |dimension|
48
+ %w{ max min }.each do |op|
49
+ native_method = "GEOSGeom_get#{dimension.upcase}#{op[0].upcase}#{op[1..-1]}_r"
50
+
51
+ if FFIGeos.respond_to?(native_method)
52
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
53
+ def #{dimension}_#{op}
54
+ return if empty?
55
+
56
+ double_ptr = FFI::MemoryPointer.new(:double)
57
+ FFIGeos.#{native_method}(Geos.current_handle_pointer, ptr, double_ptr)
58
+ double_ptr.read_double
59
+ end
60
+ RUBY
61
+ else
62
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
63
+ def #{dimension}_#{op}
64
+ unless self.empty?
65
+ self.collect(&:#{dimension}_#{op}).#{op}
66
+ end
67
+ end
68
+ RUBY
69
+ end
70
+ end
71
+ end
72
+
73
+ %w{
74
+ affine
75
+ rotate
76
+ rotate_x
77
+ rotate_y
78
+ rotate_z
79
+ scale
80
+ snap_to_grid
81
+ trans_scale
82
+ translate
83
+ }.each do |m|
84
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
85
+ def #{m}!(*args)
86
+ unless self.empty?
87
+ self.num_geometries.times do |i|
88
+ self[i].#{m}!(*args)
89
+ end
90
+ end
91
+
92
+ self
93
+ end
94
+
95
+ def #{m}(*args)
96
+ ret = self.dup.#{m}!(*args)
97
+ ret.srid = pick_srid_according_to_policy(self.srid)
98
+ ret
99
+ end
100
+ RUBY
101
+ end
40
102
  end
41
103
  end
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Geos
@@ -26,23 +25,20 @@ module Geos
26
25
  # collector may not play nicely with GEOS and objects may get cleaned
27
26
  # up in unexpected ways while interrupts are firing.
28
27
  def register(method_or_block = nil, &block)
29
- if method_or_block.nil? && !block_given?
30
- raise ArgumentError.new("Expected either a method or a block for Geos::Interrupt.register")
31
- elsif !method_or_block.nil? && block_given?
32
- raise ArgumentError.new("Cannot use both a method and a block for Geos::Interrupt.register")
33
- else
34
- retval = @current_interrupt_callback
28
+ raise ArgumentError, 'Expected either a method or a block for Geos::Interrupt.register' if method_or_block.nil? && !block_given?
29
+ raise ArgumentError, 'Cannot use both a method and a block for Geos::Interrupt.register' if !method_or_block.nil? && block_given?
35
30
 
36
- @current_interrupt_callback = if method_or_block
37
- FFIGeos.GEOS_interruptRegisterCallback(method_or_block)
38
- method_or_block
39
- elsif block_given?
40
- FFIGeos.GEOS_interruptRegisterCallback(block)
41
- block
42
- end
31
+ retval = @current_interrupt_callback
43
32
 
44
- retval
33
+ @current_interrupt_callback = if method_or_block
34
+ FFIGeos.GEOS_interruptRegisterCallback(method_or_block)
35
+ method_or_block
36
+ elsif block_given?
37
+ FFIGeos.GEOS_interruptRegisterCallback(block)
38
+ block
45
39
  end
40
+
41
+ retval
46
42
  end
47
43
 
48
44
  # Interrupt the current operation. This method should generally be
@@ -72,4 +68,3 @@ module Geos
72
68
  end
73
69
  end
74
70
  end
75
-
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Geos
@@ -7,82 +6,207 @@ module Geos
7
6
 
8
7
  def each
9
8
  if block_given?
10
- self.num_points.times do |n|
11
- yield self.point_n(n)
9
+ num_points.times do |n|
10
+ yield point_n(n)
12
11
  end
13
12
  self
14
13
  else
15
- self.num_points.times.collect { |n|
16
- self.point_n(n)
14
+ num_points.times.collect { |n|
15
+ point_n(n)
17
16
  }.to_enum
18
17
  end
19
18
  end
20
19
 
21
20
  if FFIGeos.respond_to?(:GEOSGeomGetNumPoints_r)
22
21
  def num_points
23
- FFIGeos.GEOSGeomGetNumPoints_r(Geos.current_handle_pointer, self.ptr)
22
+ FFIGeos.GEOSGeomGetNumPoints_r(Geos.current_handle_pointer, ptr)
24
23
  end
25
24
  else
26
25
  def num_points
27
- self.coord_seq.length
26
+ coord_seq.length
28
27
  end
29
28
  end
30
29
 
31
30
  def point_n(n)
32
- if n < 0 || n >= self.num_points
33
- raise Geos::IndexBoundsError.new
34
- else
35
- cast_geometry_ptr(
36
- FFIGeos.GEOSGeomGetPointN_r(Geos.current_handle_pointer, self.ptr, n), {
37
- :srid_copy => self.srid
38
- }
39
- )
40
- end
31
+ raise Geos::IndexBoundsError if n.negative? || n >= num_points
32
+
33
+ cast_geometry_ptr(FFIGeos.GEOSGeomGetPointN_r(Geos.current_handle_pointer, ptr, n), srid_copy: srid)
41
34
  end
42
35
 
43
36
  def [](*args)
44
37
  if args.length == 1 && args.first.is_a?(Numeric) && args.first >= 0
45
- self.point_n(args.first)
38
+ point_n(args.first)
46
39
  else
47
- self.to_a[*args]
40
+ to_a[*args]
48
41
  end
49
42
  end
50
- alias_method :slice, :[]
43
+ alias slice []
51
44
 
52
45
  def offset_curve(width, options = {})
53
46
  options = Constants::BUFFER_PARAM_DEFAULTS.merge(options)
54
47
 
55
- cast_geometry_ptr(FFIGeos.GEOSOffsetCurve_r(
48
+ cast_geometry_ptr(
49
+ FFIGeos.GEOSOffsetCurve_r(
56
50
  Geos.current_handle_pointer,
57
- self.ptr,
51
+ ptr,
58
52
  width,
59
53
  options[:quad_segs],
60
54
  options[:join],
61
55
  options[:mitre_limit]
62
- ), {
63
- :srid_copy => self.srid
64
- })
56
+ ),
57
+ srid_copy: srid
58
+ )
65
59
  end
66
60
 
67
61
  if FFIGeos.respond_to?(:GEOSisClosed_r)
68
62
  def closed?
69
- bool_result(FFIGeos.GEOSisClosed_r(Geos.current_handle_pointer, self.ptr))
63
+ bool_result(FFIGeos.GEOSisClosed_r(Geos.current_handle_pointer, ptr))
70
64
  end
71
65
  end
72
66
 
73
67
  def to_linear_ring
74
- if self.closed?
75
- Geos.create_linear_ring(self.coord_seq, :srid => pick_srid_according_to_policy(self.srid))
76
- else
77
- self_cs = self.coord_seq.to_a
78
- self_cs.push(self_cs[0])
68
+ return Geos.create_linear_ring(coord_seq, srid: pick_srid_according_to_policy(srid)) if closed?
79
69
 
80
- Geos.create_linear_ring(self_cs, :srid => pick_srid_according_to_policy(self.srid))
81
- end
70
+ self_cs = coord_seq.to_a
71
+ self_cs.push(self_cs[0])
72
+
73
+ Geos.create_linear_ring(self_cs, srid: pick_srid_according_to_policy(srid))
82
74
  end
83
75
 
84
76
  def to_polygon
85
- self.to_linear_ring.to_polygon
77
+ to_linear_ring.to_polygon
78
+ end
79
+
80
+ def dump_points(cur_path = [])
81
+ cur_path.concat(to_a)
82
+ end
83
+
84
+ def snap_to_grid!(*args)
85
+ unless empty?
86
+ cs = coord_seq.snap_to_grid!(*args)
87
+
88
+ if cs.empty?
89
+ @ptr = Geos.create_empty_line_string(srid: srid).ptr
90
+ elsif cs.length <= 1
91
+ raise Geos::InvalidGeometryError, "snap_to_grid! produced an invalid number of points in for a LineString - found #{cs.length} - must be 0 or > 1"
92
+ else
93
+ @ptr = Geos.create_line_string(cs).ptr
94
+ end
95
+ end
96
+
97
+ self
98
+ end
99
+
100
+ def snap_to_grid(*args)
101
+ ret = dup.snap_to_grid!(*args)
102
+ ret.srid = pick_srid_according_to_policy(srid)
103
+ ret
104
+ end
105
+
106
+ def line_interpolate_point(fraction)
107
+ raise ArgumentError, 'fraction must be between 0 and 1' unless fraction.between?(0, 1)
108
+
109
+ case fraction
110
+ when 0
111
+ start_point
112
+ when 1
113
+ end_point
114
+ else
115
+ length = self.length
116
+ total_length = 0
117
+ segs = num_points - 1
118
+
119
+ segs.times do |i|
120
+ p_1 = self[i]
121
+ p_2 = self[i + 1]
122
+
123
+ seg_length = p_1.distance(p_2) / length
124
+
125
+ if fraction < total_length + seg_length
126
+ dseg = (fraction - total_length) / seg_length
127
+
128
+ args = []
129
+ args << p_1.x + ((p_2.x - p_1.x) * dseg)
130
+ args << p_1.y + ((p_2.y - p_1.y) * dseg)
131
+ args << p_1.z + ((p_2.z - p_1.z) * dseg) if has_z?
132
+
133
+ args << { srid: pick_srid_according_to_policy(srid) } unless srid.zero?
134
+
135
+ return Geos.create_point(*args)
136
+ end
137
+
138
+ total_length += seg_length
139
+ end
140
+
141
+ # if all else fails...
142
+ end_point
143
+ end
144
+ end
145
+ alias interpolate_point line_interpolate_point
146
+
147
+ %w{ max min }.each do |op|
148
+ %w{ x y }.each do |dimension|
149
+ native_method = "GEOSGeom_get#{dimension.upcase}#{op[0].upcase}#{op[1..-1]}_r"
150
+
151
+ if FFIGeos.respond_to?(native_method)
152
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
153
+ def #{dimension}_#{op}
154
+ return if empty?
155
+
156
+ double_ptr = FFI::MemoryPointer.new(:double)
157
+ FFIGeos.#{native_method}(Geos.current_handle_pointer, ptr, double_ptr)
158
+ double_ptr.read_double
159
+ end
160
+ RUBY
161
+ else
162
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
163
+ def #{dimension}_#{op}
164
+ unless self.empty?
165
+ self.coord_seq.#{dimension}_#{op}
166
+ end
167
+ end
168
+ RUBY
169
+ end
170
+ end
171
+
172
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
173
+ def z_#{op}
174
+ unless self.empty?
175
+ if self.has_z?
176
+ self.coord_seq.z_#{op}
177
+ else
178
+ 0
179
+ end
180
+ end
181
+ end
182
+ RUBY
183
+ end
184
+
185
+ %w{
186
+ affine
187
+ rotate
188
+ rotate_x
189
+ rotate_y
190
+ rotate_z
191
+ scale
192
+ trans_scale
193
+ translate
194
+ }.each do |m|
195
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
196
+ def #{m}!(*args)
197
+ unless self.empty?
198
+ self.coord_seq.#{m}!(*args)
199
+ end
200
+
201
+ self
202
+ end
203
+
204
+ def #{m}(*args)
205
+ ret = self.dup.#{m}!(*args)
206
+ ret.srid = pick_srid_according_to_policy(self.srid)
207
+ ret
208
+ end
209
+ RUBY
86
210
  end
87
211
  end
88
212
  end
@@ -1,14 +1,13 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Geos
5
4
  class LinearRing < LineString
6
5
  def to_polygon
7
- Geos.create_polygon(self, :srid => pick_srid_according_to_policy(self.srid))
6
+ Geos.create_polygon(self, srid: pick_srid_according_to_policy(srid))
8
7
  end
9
8
 
10
9
  def to_line_string
11
- Geos.create_line_string(self.coord_seq, :srid => pick_srid_according_to_policy(self.srid))
10
+ Geos.create_line_string(coord_seq, srid: pick_srid_according_to_policy(srid))
12
11
  end
13
12
  end
14
13
  end
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Geos
@@ -6,7 +5,7 @@ module Geos
6
5
  if FFIGeos.respond_to?(:GEOSisClosed_r) && Geos::GEOS_VERSION >= '3.5.0'
7
6
  # Available in GEOS 3.5.0+.
8
7
  def closed?
9
- bool_result(FFIGeos.GEOSisClosed_r(Geos.current_handle_pointer, self.ptr))
8
+ bool_result(FFIGeos.GEOSisClosed_r(Geos.current_handle_pointer, ptr))
10
9
  end
11
10
  end
12
11
  end
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Geos
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Geos
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Geos
@@ -6,41 +5,41 @@ module Geos
6
5
  if FFIGeos.respond_to?(:GEOSGeomGetX_r)
7
6
  def get_x
8
7
  double_ptr = FFI::MemoryPointer.new(:double)
9
- FFIGeos.GEOSGeomGetX_r(Geos.current_handle_pointer, self.ptr, double_ptr)
8
+ FFIGeos.GEOSGeomGetX_r(Geos.current_handle_pointer, ptr, double_ptr)
10
9
  double_ptr.read_double
11
10
  end
12
11
  else
13
12
  def get_x
14
- self.coord_seq.get_x(0)
13
+ coord_seq.get_x(0)
15
14
  end
16
15
  end
17
- alias_method :x, :get_x
16
+ alias x get_x
18
17
 
19
18
  if FFIGeos.respond_to?(:GEOSGeomGetY_r)
20
19
  def get_y
21
20
  double_ptr = FFI::MemoryPointer.new(:double)
22
- FFIGeos.GEOSGeomGetY_r(Geos.current_handle_pointer, self.ptr, double_ptr)
21
+ FFIGeos.GEOSGeomGetY_r(Geos.current_handle_pointer, ptr, double_ptr)
23
22
  double_ptr.read_double
24
23
  end
25
24
  else
26
25
  def get_y
27
- self.coord_seq.get_y(0)
26
+ coord_seq.get_y(0)
28
27
  end
29
28
  end
30
- alias_method :y, :get_y
29
+ alias y get_y
31
30
 
32
31
  if FFIGeos.respond_to?(:GEOSGeomGetZ_r)
33
32
  def get_z
34
33
  double_ptr = FFI::MemoryPointer.new(:double)
35
- FFIGeos.GEOSGeomGetZ_r(Geos.current_handle_pointer, self.ptr, double_ptr)
34
+ FFIGeos.GEOSGeomGetZ_r(Geos.current_handle_pointer, ptr, double_ptr)
36
35
  double_ptr.read_double
37
36
  end
38
37
  else
39
38
  def get_z
40
- self.coord_seq.get_z(0)
39
+ coord_seq.get_z(0)
41
40
  end
42
41
  end
43
- alias_method :z, :get_z
42
+ alias z get_z
44
43
 
45
44
  def area
46
45
  0
@@ -61,7 +60,7 @@ module Geos
61
60
  def normalize!
62
61
  self
63
62
  end
64
- alias_method :normalize, :normalize!
63
+ alias normalize normalize!
65
64
 
66
65
  %w{
67
66
  convex_hull
@@ -70,13 +69,69 @@ module Geos
70
69
  envelope
71
70
  topology_preserve_simplify
72
71
  }.each do |method|
73
- self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
72
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
74
73
  def #{method}(*args)
75
- self.dup.tap { |ret|
74
+ dup.tap do |ret|
76
75
  ret.srid = pick_srid_according_to_policy(ret.srid)
77
- }
76
+ end
78
77
  end
79
- EOF
78
+ RUBY
79
+ end
80
+
81
+ def dump_points(cur_path = [])
82
+ cur_path.push(dup)
83
+ end
84
+
85
+ %w{ max min }.each do |op|
86
+ %w{ x y }.each do |dimension|
87
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
88
+ def #{dimension}_#{op}
89
+ unless empty?
90
+ #{dimension}
91
+ end
92
+ end
93
+ RUBY
94
+ end
95
+
96
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
97
+ def z_#{op}
98
+ unless empty?
99
+ if has_z?
100
+ z
101
+ else
102
+ 0
103
+ end
104
+ end
105
+ end
106
+ RUBY
107
+ end
108
+
109
+ %w{
110
+ affine
111
+ rotate
112
+ rotate_x
113
+ rotate_y
114
+ rotate_z
115
+ scale
116
+ snap_to_grid
117
+ trans_scale
118
+ translate
119
+ }.each do |m|
120
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
121
+ def #{m}!(*args)
122
+ unless empty?
123
+ coord_seq.#{m}!(*args)
124
+ end
125
+
126
+ self
127
+ end
128
+
129
+ def #{m}(*args)
130
+ ret = dup.#{m}!(*args)
131
+ ret.srid = pick_srid_according_to_policy(srid)
132
+ ret
133
+ end
134
+ RUBY
80
135
  end
81
136
  end
82
137
  end