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,41 +1,144 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Geos
5
4
  class Polygon < Geometry
6
5
  def num_interior_rings
7
- FFIGeos.GEOSGetNumInteriorRings_r(Geos.current_handle_pointer, self.ptr)
6
+ FFIGeos.GEOSGetNumInteriorRings_r(Geos.current_handle_pointer, ptr)
8
7
  end
9
8
 
10
9
  def interior_ring_n(n)
11
- if n < 0 || n >= self.num_interior_rings
12
- raise Geos::IndexBoundsError.new
13
- else
14
- cast_geometry_ptr(
15
- FFIGeos.GEOSGetInteriorRingN_r(Geos.current_handle_pointer, self.ptr, n), {
16
- :auto_free => false,
17
- :srid_copy => self.srid,
18
- :parent => self
19
- }
20
- )
21
- end
10
+ raise Geos::IndexBoundsError if n.negative? || n >= num_interior_rings
11
+
12
+ cast_geometry_ptr(
13
+ FFIGeos.GEOSGetInteriorRingN_r(Geos.current_handle_pointer, ptr, n),
14
+ auto_free: false,
15
+ srid_copy: srid,
16
+ parent: self
17
+ )
22
18
  end
23
- alias_method :interior_ring, :interior_ring_n
19
+ alias interior_ring interior_ring_n
24
20
 
25
21
  def exterior_ring
26
22
  cast_geometry_ptr(
27
- FFIGeos.GEOSGetExteriorRing_r(Geos.current_handle_pointer, self.ptr), {
28
- :auto_free => false,
29
- :srid_copy => self.srid,
30
- :parent => self
31
- }
23
+ FFIGeos.GEOSGetExteriorRing_r(Geos.current_handle_pointer, ptr),
24
+ auto_free: false,
25
+ srid_copy: srid,
26
+ parent: self
32
27
  )
33
28
  end
34
29
 
35
30
  def interior_rings
36
- self.num_interior_rings.times.collect do |n|
37
- self.interior_ring_n(n)
31
+ num_interior_rings.times.collect do |n|
32
+ interior_ring_n(n)
33
+ end
34
+ end
35
+
36
+ def dump_points(cur_path = [])
37
+ points = [exterior_ring.dump_points]
38
+
39
+ interior_rings.each do |ring|
40
+ points.push(ring.dump_points)
38
41
  end
42
+
43
+ cur_path.concat(points)
44
+ end
45
+
46
+ def snap_to_grid!(*args)
47
+ unless empty?
48
+ exterior_ring = self.exterior_ring.coord_seq.snap_to_grid!(*args)
49
+
50
+ if exterior_ring.empty?
51
+ @ptr = Geos.create_empty_polygon(srid: srid).ptr
52
+ elsif exterior_ring.length < 4
53
+ raise Geos::InvalidGeometryError, "snap_to_grid! produced an invalid number of points in exterior ring - found #{exterior_ring.length} - must be 0 or >= 4"
54
+ else
55
+ interior_rings = []
56
+
57
+ num_interior_rings.times do |i|
58
+ interior_ring = interior_ring_n(i).coord_seq.snap_to_grid!(*args)
59
+
60
+ interior_rings << interior_ring unless interior_ring.length < 4
61
+ end
62
+
63
+ interior_rings.compact!
64
+
65
+ polygon = Geos.create_polygon(exterior_ring, interior_rings, srid: srid)
66
+ @ptr = polygon.ptr
67
+ end
68
+ end
69
+
70
+ self
71
+ end
72
+
73
+ def snap_to_grid(*args)
74
+ ret = dup.snap_to_grid!(*args)
75
+ ret.srid = pick_srid_according_to_policy(srid)
76
+ ret
77
+ end
78
+
79
+ %w{ max min }.each do |op|
80
+ %w{ x y }.each do |dimension|
81
+ native_method = "GEOSGeom_get#{dimension.upcase}#{op[0].upcase}#{op[1..-1]}_r"
82
+
83
+ if FFIGeos.respond_to?(native_method)
84
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
85
+ def #{dimension}_#{op}
86
+ return if empty?
87
+
88
+ double_ptr = FFI::MemoryPointer.new(:double)
89
+ FFIGeos.#{native_method}(Geos.current_handle_pointer, ptr, double_ptr)
90
+ double_ptr.read_double
91
+ end
92
+ RUBY
93
+ else
94
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
95
+ def #{dimension}_#{op}
96
+ unless empty?
97
+ envelope.exterior_ring.#{dimension}_#{op}
98
+ end
99
+ end
100
+ RUBY
101
+ end
102
+ end
103
+
104
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
105
+ def z_#{op}
106
+ unless empty?
107
+ if has_z?
108
+ exterior_ring.z_#{op}
109
+ else
110
+ 0
111
+ end
112
+ end
113
+ end
114
+ RUBY
115
+ end
116
+
117
+ %w{
118
+ affine
119
+ rotate
120
+ rotate_x
121
+ rotate_y
122
+ rotate_z
123
+ scale
124
+ trans_scale
125
+ translate
126
+ }.each do |m|
127
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
128
+ def #{m}!(*args)
129
+ exterior_ring.coord_seq.#{m}!(*args)
130
+ interior_rings.each do |ring|
131
+ ring.coord_seq.#{m}!(*args)
132
+ end
133
+ self
134
+ end
135
+
136
+ def #{m}(*args)
137
+ ret = dup.#{m}!(*args)
138
+ ret.srid = pick_srid_according_to_policy(srid)
139
+ ret
140
+ end
141
+ RUBY
39
142
  end
40
143
  end
41
144
  end
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Geos
@@ -13,7 +12,7 @@ module Geos
13
12
  check_geometry(geom)
14
13
 
15
14
  options = {
16
- :auto_free => true
15
+ auto_free: true
17
16
  }.merge(options)
18
17
 
19
18
  @ptr = FFI::AutoPointer.new(
@@ -31,52 +30,52 @@ module Geos
31
30
 
32
31
  def contains?(geom)
33
32
  check_geometry(geom)
34
- bool_result(FFIGeos.GEOSPreparedContains_r(Geos.current_handle_pointer, self.ptr, geom.ptr))
33
+ bool_result(FFIGeos.GEOSPreparedContains_r(Geos.current_handle_pointer, ptr, geom.ptr))
35
34
  end
36
35
 
37
36
  def contains_properly?(geom)
38
37
  check_geometry(geom)
39
- bool_result(FFIGeos.GEOSPreparedContainsProperly_r(Geos.current_handle_pointer, self.ptr, geom.ptr))
38
+ bool_result(FFIGeos.GEOSPreparedContainsProperly_r(Geos.current_handle_pointer, ptr, geom.ptr))
40
39
  end
41
40
 
42
41
  def covered_by?(geom)
43
42
  check_geometry(geom)
44
- bool_result(FFIGeos.GEOSPreparedCoveredBy_r(Geos.current_handle_pointer, self.ptr, geom.ptr))
43
+ bool_result(FFIGeos.GEOSPreparedCoveredBy_r(Geos.current_handle_pointer, ptr, geom.ptr))
45
44
  end
46
45
 
47
46
  def covers?(geom)
48
47
  check_geometry(geom)
49
- bool_result(FFIGeos.GEOSPreparedCovers_r(Geos.current_handle_pointer, self.ptr, geom.ptr))
48
+ bool_result(FFIGeos.GEOSPreparedCovers_r(Geos.current_handle_pointer, ptr, geom.ptr))
50
49
  end
51
50
 
52
51
  def crosses?(geom)
53
52
  check_geometry(geom)
54
- bool_result(FFIGeos.GEOSPreparedCrosses_r(Geos.current_handle_pointer, self.ptr, geom.ptr))
53
+ bool_result(FFIGeos.GEOSPreparedCrosses_r(Geos.current_handle_pointer, ptr, geom.ptr))
55
54
  end
56
55
 
57
56
  def disjoint?(geom)
58
57
  check_geometry(geom)
59
- bool_result(FFIGeos.GEOSPreparedDisjoint_r(Geos.current_handle_pointer, self.ptr, geom.ptr))
58
+ bool_result(FFIGeos.GEOSPreparedDisjoint_r(Geos.current_handle_pointer, ptr, geom.ptr))
60
59
  end
61
60
 
62
61
  def intersects?(geom)
63
62
  check_geometry(geom)
64
- bool_result(FFIGeos.GEOSPreparedIntersects_r(Geos.current_handle_pointer, self.ptr, geom.ptr))
63
+ bool_result(FFIGeos.GEOSPreparedIntersects_r(Geos.current_handle_pointer, ptr, geom.ptr))
65
64
  end
66
65
 
67
66
  def overlaps?(geom)
68
67
  check_geometry(geom)
69
- bool_result(FFIGeos.GEOSPreparedOverlaps_r(Geos.current_handle_pointer, self.ptr, geom.ptr))
68
+ bool_result(FFIGeos.GEOSPreparedOverlaps_r(Geos.current_handle_pointer, ptr, geom.ptr))
70
69
  end
71
70
 
72
71
  def touches?(geom)
73
72
  check_geometry(geom)
74
- bool_result(FFIGeos.GEOSPreparedTouches_r(Geos.current_handle_pointer, self.ptr, geom.ptr))
73
+ bool_result(FFIGeos.GEOSPreparedTouches_r(Geos.current_handle_pointer, ptr, geom.ptr))
75
74
  end
76
75
 
77
76
  def within?(geom)
78
77
  check_geometry(geom)
79
- bool_result(FFIGeos.GEOSPreparedWithin_r(Geos.current_handle_pointer, self.ptr, geom.ptr))
78
+ bool_result(FFIGeos.GEOSPreparedWithin_r(Geos.current_handle_pointer, ptr, geom.ptr))
80
79
  end
81
80
  end
82
81
  end
@@ -1,4 +1,3 @@
1
- # encoding: UTF-8
2
1
  # frozen_string_literal: true
3
2
 
4
3
  module Geos
@@ -12,7 +11,7 @@ module Geos
12
11
 
13
12
  class AlreadyBuiltError < Geos::Error
14
13
  def initialize(*)
15
- super("STRtree has already been built")
14
+ super('STRtree has already been built')
16
15
  end
17
16
  end
18
17
 
@@ -24,23 +23,22 @@ module Geos
24
23
  geoms_and_objects = nil # forward declaration
25
24
  capacity = 10
26
25
 
27
- if args.first.is_a?(Integer)
28
- capacity = args.first
29
- elsif args.first.is_a?(Array)
30
- geoms_and_objects = if args.first.first.is_a?(Array)
31
- args.first
32
- else
33
- args
34
- end
26
+ case args.first
27
+ when Integer
28
+ capacity = args.first
29
+ when Array
30
+ geoms_and_objects = if args.first.first.is_a?(Array)
31
+ args.first
32
+ else
33
+ args
34
+ end
35
35
 
36
- geoms_and_objects.each do |geom, obj|
37
- check_geometry(geom)
38
- end
36
+ geoms_and_objects.each do |geom, _obj|
37
+ check_geometry(geom)
38
+ end
39
39
  end
40
40
 
41
- if capacity <= 0
42
- raise ArgumentError.new("STRtree capacity must be greater than 0")
43
- end
41
+ raise ArgumentError, 'STRtree capacity must be greater than 0' if capacity <= 0
44
42
 
45
43
  ptr = FFIGeos.GEOSSTRtree_create_r(Geos.current_handle_pointer, capacity)
46
44
 
@@ -55,10 +53,10 @@ module Geos
55
53
  @storage_key = 0
56
54
  @built = false
57
55
 
58
- if geoms_and_objects
59
- geoms_and_objects.each do |geom, obj|
60
- self.insert(geom, obj)
61
- end
56
+ return unless geoms_and_objects
57
+
58
+ geoms_and_objects.each do |geom, obj|
59
+ insert(geom, obj)
62
60
  end
63
61
  end
64
62
 
@@ -80,41 +78,37 @@ module Geos
80
78
  private :next_key
81
79
 
82
80
  def insert(geom, item = nil)
83
- if self.built?
84
- raise AlreadyBuiltError.new
85
- else
86
- check_geometry(geom)
81
+ raise AlreadyBuiltError if built?
87
82
 
88
- key = next_key
89
- key_ptr = FFI::MemoryPointer.new(:pointer)
90
- key_ptr.write_int(key)
83
+ check_geometry(geom)
91
84
 
92
- @storage[key] = {
93
- :item => item,
94
- :geometry => geom
95
- }
96
- @ptrs[key] = key_ptr
85
+ key = next_key
86
+ key_ptr = FFI::MemoryPointer.new(:pointer)
87
+ key_ptr.write_int(key)
97
88
 
98
- FFIGeos.GEOSSTRtree_insert_r(Geos.current_handle_pointer, self.ptr, geom.ptr, key_ptr)
99
- end
89
+ @storage[key] = {
90
+ item: item,
91
+ geometry: geom
92
+ }
93
+ @ptrs[key] = key_ptr
94
+
95
+ FFIGeos.GEOSSTRtree_insert_r(Geos.current_handle_pointer, ptr, geom.ptr, key_ptr)
100
96
  end
101
97
 
102
98
  def remove(geom, item)
103
99
  check_geometry(geom)
104
100
 
105
- key = if storage = @storage.detect { |k, v| v[:item] == item }
101
+ key = if (storage = @storage.detect { |_k, v| v[:item] == item })
106
102
  storage[0]
107
103
  end
108
104
 
109
- if key
110
- key_ptr = @ptrs[key]
111
- result = FFIGeos.GEOSSTRtree_remove_r(Geos.current_handle_pointer, self.ptr, geom.ptr, key_ptr)
112
- built!
105
+ return unless key
113
106
 
114
- if result == 1
115
- @storage.delete(key)
116
- end
117
- end
107
+ key_ptr = @ptrs[key]
108
+ result = FFIGeos.GEOSSTRtree_remove_r(Geos.current_handle_pointer, ptr, geom.ptr, key_ptr)
109
+ built!
110
+
111
+ @storage.delete(key) if result == 1
118
112
  end
119
113
 
120
114
  def query_all(geom)
@@ -128,14 +122,12 @@ module Geos
128
122
  storage = @storage[key]
129
123
  retval << storage
130
124
 
131
- if block_given?
132
- yield(storage)
133
- end
125
+ yield(storage) if block_given?
134
126
  }
135
127
 
136
128
  FFIGeos.GEOSSTRtree_query_r(
137
129
  Geos.current_handle_pointer,
138
- self.ptr,
130
+ ptr,
139
131
  geom.ptr,
140
132
  callback,
141
133
  nil
@@ -145,42 +137,37 @@ module Geos
145
137
  end
146
138
 
147
139
  def query(geom, ret = :item)
148
- self.query_all(geom).collect { |storage|
149
- item = if ret.is_a?(Array)
150
- storage.inject({}) do |memo, k|
151
- memo.tap {
152
- memo[k] = storage[k]
153
- }
154
- end
155
- elsif ret == :all
156
- storage
157
- else
158
- storage[ret]
140
+ query_all(geom).collect { |storage|
141
+ item = case ret
142
+ when Array
143
+ storage.inject({}) do |memo, k|
144
+ memo.tap do
145
+ memo[k] = storage[k]
146
+ end
147
+ end
148
+ when :all
149
+ storage
150
+ else
151
+ storage[ret]
159
152
  end
160
153
 
161
- item.tap {
162
- if block_given?
163
- yield(item)
164
- end
165
- }
154
+ item.tap do
155
+ yield(item) if block_given?
156
+ end
166
157
  }.compact
167
158
  end
168
159
 
169
160
  def query_geometries(geom)
170
- self.query_all(geom).collect { |storage|
171
- storage[:geometry].tap { |val|
172
- if block_given?
173
- yield(val)
174
- end
175
- }
161
+ query_all(geom).collect { |storage|
162
+ storage[:geometry].tap do |val|
163
+ yield(val) if block_given?
164
+ end
176
165
  }.compact
177
166
  end
178
- alias_method :query_geoms, :query_geometries
167
+ alias query_geoms query_geometries
179
168
 
180
- def iterate
181
- @storage.values.each do |v|
182
- yield(v)
183
- end
169
+ def iterate(&block)
170
+ @storage.each_value(&block)
184
171
  end
185
172
 
186
173
  if FFIGeos.respond_to?(:GEOSSTRtree_nearest_generic_r)
@@ -191,7 +178,7 @@ module Geos
191
178
 
192
179
  return nil if @storage.empty?
193
180
 
194
- callback = proc { |item, _item2, distance_ptr|
181
+ callback = proc { |item, _item_2, distance_ptr|
195
182
  key = item.read_int
196
183
  geom_from_storage = @storage[key][:geometry]
197
184
 
@@ -205,7 +192,7 @@ module Geos
205
192
 
206
193
  key_ptr = FFIGeos.GEOSSTRtree_nearest_generic_r(
207
194
  Geos.current_handle_pointer,
208
- self.ptr,
195
+ ptr,
209
196
  geom.ptr,
210
197
  geom.envelope.ptr,
211
198
  callback,
@@ -219,7 +206,7 @@ module Geos
219
206
  item = nearest_generic(geom)
220
207
  item[:geometry] if item
221
208
  end
222
- alias_method :nearest_geometry, :nearest
209
+ alias nearest_geometry nearest
223
210
 
224
211
  def nearest_item(geom)
225
212
  item = nearest_generic(geom)