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.
- checksums.yaml +5 -5
- data/.rubocop.yml +4851 -0
- data/.travis.yml +24 -9
- data/FUNDING.yml +2 -0
- data/Gemfile +12 -16
- data/Guardfile +6 -8
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -20
- data/Rakefile +4 -2
- data/ffi-geos.gemspec +13 -14
- data/lib/ffi-geos.rb +342 -244
- data/lib/ffi-geos/buffer_params.rb +9 -20
- data/lib/ffi-geos/coordinate_sequence.rb +351 -65
- data/lib/ffi-geos/geometry.rb +267 -191
- data/lib/ffi-geos/geometry_collection.rb +74 -12
- data/lib/ffi-geos/interrupt.rb +11 -16
- data/lib/ffi-geos/line_string.rb +157 -33
- data/lib/ffi-geos/linear_ring.rb +2 -3
- data/lib/ffi-geos/multi_line_string.rb +1 -2
- data/lib/ffi-geos/multi_point.rb +0 -1
- data/lib/ffi-geos/multi_polygon.rb +0 -1
- data/lib/ffi-geos/point.rb +70 -15
- data/lib/ffi-geos/polygon.rb +124 -21
- data/lib/ffi-geos/prepared_geometry.rb +11 -12
- data/lib/ffi-geos/strtree.rb +64 -77
- data/lib/ffi-geos/tools.rb +16 -19
- data/lib/ffi-geos/utils.rb +36 -60
- data/lib/ffi-geos/version.rb +1 -3
- data/lib/ffi-geos/wkb_reader.rb +4 -9
- data/lib/ffi-geos/wkb_writer.rb +15 -20
- data/lib/ffi-geos/wkt_reader.rb +2 -5
- data/lib/ffi-geos/wkt_writer.rb +20 -31
- data/sonar-project.properties +16 -0
- data/test/.rubocop.yml +36 -0
- data/test/coordinate_sequence_tests.rb +322 -52
- data/test/geometry_collection_tests.rb +388 -4
- data/test/geometry_tests.rb +466 -121
- data/test/interrupt_tests.rb +9 -12
- data/test/line_string_tests.rb +213 -25
- data/test/linear_ring_tests.rb +1 -3
- data/test/misc_tests.rb +28 -30
- data/test/multi_line_string_tests.rb +0 -2
- data/test/point_tests.rb +158 -2
- data/test/polygon_tests.rb +283 -2
- data/test/prepared_geometry_tests.rb +8 -11
- data/test/strtree_tests.rb +14 -15
- data/test/test_helper.rb +75 -51
- data/test/tools_tests.rb +1 -4
- data/test/utils_tests.rb +85 -76
- data/test/wkb_reader_tests.rb +18 -18
- data/test/wkb_writer_tests.rb +15 -22
- data/test/wkt_reader_tests.rb +1 -4
- data/test/wkt_writer_tests.rb +8 -17
- metadata +11 -7
data/lib/ffi-geos/polygon.rb
CHANGED
@@ -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,
|
6
|
+
FFIGeos.GEOSGetNumInteriorRings_r(Geos.current_handle_pointer, ptr)
|
8
7
|
end
|
9
8
|
|
10
9
|
def interior_ring_n(n)
|
11
|
-
if n
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
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,
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
37
|
-
|
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
|
-
:
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
78
|
+
bool_result(FFIGeos.GEOSPreparedWithin_r(Geos.current_handle_pointer, ptr, geom.ptr))
|
80
79
|
end
|
81
80
|
end
|
82
81
|
end
|
data/lib/ffi-geos/strtree.rb
CHANGED
@@ -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(
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
args.first
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
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
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
84
|
-
raise AlreadyBuiltError.new
|
85
|
-
else
|
86
|
-
check_geometry(geom)
|
81
|
+
raise AlreadyBuiltError if built?
|
87
82
|
|
88
|
-
|
89
|
-
key_ptr = FFI::MemoryPointer.new(:pointer)
|
90
|
-
key_ptr.write_int(key)
|
83
|
+
check_geometry(geom)
|
91
84
|
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
-
|
99
|
-
|
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 { |
|
101
|
+
key = if (storage = @storage.detect { |_k, v| v[:item] == item })
|
106
102
|
storage[0]
|
107
103
|
end
|
108
104
|
|
109
|
-
|
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
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
-
|
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
|
-
|
149
|
-
item =
|
150
|
-
|
151
|
-
|
152
|
-
memo
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
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
|
-
|
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
|
-
|
171
|
-
storage[:geometry].tap
|
172
|
-
if block_given?
|
173
|
-
|
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
|
-
|
167
|
+
alias query_geoms query_geometries
|
179
168
|
|
180
|
-
def iterate
|
181
|
-
@storage.
|
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,
|
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
|
-
|
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
|
-
|
209
|
+
alias nearest_geometry nearest
|
223
210
|
|
224
211
|
def nearest_item(geom)
|
225
212
|
item = nearest_generic(geom)
|