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,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)