ffi-geos 1.2.0 → 2.2.0

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