ffi-geos 1.2.1 → 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +20 -0
- data/.travis.yml +7 -3
- data/Gemfile +1 -1
- data/Guardfile +4 -5
- data/ffi-geos.gemspec +1 -1
- data/lib/ffi-geos.rb +212 -196
- data/lib/ffi-geos/buffer_params.rb +9 -20
- data/lib/ffi-geos/coordinate_sequence.rb +342 -58
- data/lib/ffi-geos/geometry.rb +167 -178
- data/lib/ffi-geos/geometry_collection.rb +60 -12
- data/lib/ffi-geos/interrupt.rb +2 -4
- data/lib/ffi-geos/line_string.rb +146 -37
- 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 +69 -14
- data/lib/ffi-geos/polygon.rb +110 -21
- data/lib/ffi-geos/prepared_geometry.rb +11 -12
- data/lib/ffi-geos/strtree.rb +41 -52
- data/lib/ffi-geos/tools.rb +15 -18
- data/lib/ffi-geos/utils.rb +27 -44
- data/lib/ffi-geos/version.rb +1 -3
- data/lib/ffi-geos/wkb_reader.rb +4 -9
- data/lib/ffi-geos/wkb_writer.rb +14 -18
- data/lib/ffi-geos/wkt_reader.rb +2 -5
- data/lib/ffi-geos/wkt_writer.rb +17 -22
- data/test/.rubocop.yml +36 -0
- data/test/coordinate_sequence_tests.rb +263 -14
- data/test/geometry_collection_tests.rb +412 -1
- data/test/geometry_tests.rb +156 -86
- data/test/interrupt_tests.rb +2 -4
- data/test/line_string_tests.rb +212 -23
- data/test/linear_ring_tests.rb +1 -2
- data/test/misc_tests.rb +28 -29
- data/test/multi_line_string_tests.rb +0 -1
- data/test/point_tests.rb +158 -1
- data/test/polygon_tests.rb +284 -1
- data/test/prepared_geometry_tests.rb +1 -3
- data/test/strtree_tests.rb +9 -10
- data/test/test_helper.rb +49 -18
- data/test/tools_tests.rb +1 -3
- data/test/utils_tests.rb +22 -22
- data/test/wkb_reader_tests.rb +10 -9
- data/test/wkb_writer_tests.rb +5 -13
- data/test/wkt_reader_tests.rb +1 -2
- data/test/wkt_writer_tests.rb +9 -14
- metadata +6 -3
data/lib/ffi-geos/polygon.rb
CHANGED
@@ -1,41 +1,130 @@
|
|
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)
|
38
33
|
end
|
39
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)
|
41
|
+
end
|
42
|
+
|
43
|
+
cur_path.concat(points)
|
44
|
+
end
|
45
|
+
|
46
|
+
def snap_to_grid!(*args)
|
47
|
+
if !self.empty?
|
48
|
+
exterior_ring = self.exterior_ring.coord_seq.snap_to_grid!(*args)
|
49
|
+
|
50
|
+
if exterior_ring.length == 0
|
51
|
+
@ptr = Geos.create_empty_polygon(:srid => self.srid).ptr
|
52
|
+
elsif exterior_ring.length < 4
|
53
|
+
raise Geos::InvalidGeometryError.new("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
|
+
self.num_interior_rings.times { |i|
|
58
|
+
interior_ring = self.interior_ring_n(i).coord_seq.snap_to_grid!(*args)
|
59
|
+
|
60
|
+
interior_rings << interior_ring unless interior_ring.length < 4
|
61
|
+
}
|
62
|
+
|
63
|
+
interior_rings.compact!
|
64
|
+
|
65
|
+
polygon = Geos.create_polygon(exterior_ring, interior_rings, :srid => self.srid)
|
66
|
+
@ptr = polygon.ptr
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
def snap_to_grid(*args)
|
74
|
+
ret = self.dup.snap_to_grid!(*args)
|
75
|
+
ret.srid = pick_srid_according_to_policy(self.srid)
|
76
|
+
ret
|
77
|
+
end
|
78
|
+
|
79
|
+
%w{ max min }.each do |op|
|
80
|
+
%w{ x y }.each do |dimension|
|
81
|
+
self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
|
82
|
+
def #{dimension}_#{op}
|
83
|
+
unless self.empty?
|
84
|
+
self.envelope.exterior_ring.#{dimension}_#{op}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
EOF
|
88
|
+
end
|
89
|
+
|
90
|
+
self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
|
91
|
+
def z_#{op}
|
92
|
+
unless self.empty?
|
93
|
+
if self.has_z?
|
94
|
+
self.exterior_ring.z_#{op}
|
95
|
+
else
|
96
|
+
0
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
EOF
|
101
|
+
end
|
102
|
+
|
103
|
+
%w{
|
104
|
+
affine
|
105
|
+
rotate
|
106
|
+
rotate_x
|
107
|
+
rotate_y
|
108
|
+
rotate_z
|
109
|
+
scale
|
110
|
+
trans_scale
|
111
|
+
translate
|
112
|
+
}.each do |m|
|
113
|
+
self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
|
114
|
+
def #{m}!(*args)
|
115
|
+
self.exterior_ring.coord_seq.#{m}!(*args)
|
116
|
+
self.interior_rings.each do |ring|
|
117
|
+
ring.coord_seq.#{m}!(*args)
|
118
|
+
end
|
119
|
+
self
|
120
|
+
end
|
121
|
+
|
122
|
+
def #{m}(*args)
|
123
|
+
ret = self.dup.#{m}!(*args)
|
124
|
+
ret.srid = pick_srid_according_to_policy(self.srid)
|
125
|
+
ret
|
126
|
+
end
|
127
|
+
EOF
|
128
|
+
end
|
40
129
|
end
|
41
130
|
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
|
|
@@ -33,13 +32,13 @@ module Geos
|
|
33
32
|
args
|
34
33
|
end
|
35
34
|
|
36
|
-
geoms_and_objects.each do |geom,
|
35
|
+
geoms_and_objects.each do |geom, _obj|
|
37
36
|
check_geometry(geom)
|
38
37
|
end
|
39
38
|
end
|
40
39
|
|
41
40
|
if capacity <= 0
|
42
|
-
raise ArgumentError
|
41
|
+
raise ArgumentError, 'STRtree capacity must be greater than 0'
|
43
42
|
end
|
44
43
|
|
45
44
|
ptr = FFIGeos.GEOSSTRtree_create_r(Geos.current_handle_pointer, capacity)
|
@@ -55,10 +54,10 @@ module Geos
|
|
55
54
|
@storage_key = 0
|
56
55
|
@built = false
|
57
56
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
57
|
+
return unless geoms_and_objects
|
58
|
+
|
59
|
+
geoms_and_objects.each do |geom, obj|
|
60
|
+
insert(geom, obj)
|
62
61
|
end
|
63
62
|
end
|
64
63
|
|
@@ -80,23 +79,21 @@ module Geos
|
|
80
79
|
private :next_key
|
81
80
|
|
82
81
|
def insert(geom, item = nil)
|
83
|
-
if
|
84
|
-
raise AlreadyBuiltError.new
|
85
|
-
else
|
86
|
-
check_geometry(geom)
|
82
|
+
raise AlreadyBuiltError if built?
|
87
83
|
|
88
|
-
|
89
|
-
key_ptr = FFI::MemoryPointer.new(:pointer)
|
90
|
-
key_ptr.write_int(key)
|
84
|
+
check_geometry(geom)
|
91
85
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
}
|
96
|
-
@ptrs[key] = key_ptr
|
86
|
+
key = next_key
|
87
|
+
key_ptr = FFI::MemoryPointer.new(:pointer)
|
88
|
+
key_ptr.write_int(key)
|
97
89
|
|
98
|
-
|
99
|
-
|
90
|
+
@storage[key] = {
|
91
|
+
item: item,
|
92
|
+
geometry: geom
|
93
|
+
}
|
94
|
+
@ptrs[key] = key_ptr
|
95
|
+
|
96
|
+
FFIGeos.GEOSSTRtree_insert_r(Geos.current_handle_pointer, ptr, geom.ptr, key_ptr)
|
100
97
|
end
|
101
98
|
|
102
99
|
def remove(geom, item)
|
@@ -106,15 +103,13 @@ module Geos
|
|
106
103
|
storage[0]
|
107
104
|
end
|
108
105
|
|
109
|
-
|
110
|
-
key_ptr = @ptrs[key]
|
111
|
-
result = FFIGeos.GEOSSTRtree_remove_r(Geos.current_handle_pointer, self.ptr, geom.ptr, key_ptr)
|
112
|
-
built!
|
106
|
+
return unless key
|
113
107
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
108
|
+
key_ptr = @ptrs[key]
|
109
|
+
result = FFIGeos.GEOSSTRtree_remove_r(Geos.current_handle_pointer, ptr, geom.ptr, key_ptr)
|
110
|
+
built!
|
111
|
+
|
112
|
+
@storage.delete(key) if result == 1
|
118
113
|
end
|
119
114
|
|
120
115
|
def query_all(geom)
|
@@ -128,14 +123,12 @@ module Geos
|
|
128
123
|
storage = @storage[key]
|
129
124
|
retval << storage
|
130
125
|
|
131
|
-
if block_given?
|
132
|
-
yield(storage)
|
133
|
-
end
|
126
|
+
yield(storage) if block_given?
|
134
127
|
}
|
135
128
|
|
136
129
|
FFIGeos.GEOSSTRtree_query_r(
|
137
130
|
Geos.current_handle_pointer,
|
138
|
-
|
131
|
+
ptr,
|
139
132
|
geom.ptr,
|
140
133
|
callback,
|
141
134
|
nil
|
@@ -145,12 +138,12 @@ module Geos
|
|
145
138
|
end
|
146
139
|
|
147
140
|
def query(geom, ret = :item)
|
148
|
-
|
141
|
+
query_all(geom).collect { |storage|
|
149
142
|
item = if ret.is_a?(Array)
|
150
143
|
storage.inject({}) do |memo, k|
|
151
|
-
memo.tap
|
144
|
+
memo.tap do
|
152
145
|
memo[k] = storage[k]
|
153
|
-
|
146
|
+
end
|
154
147
|
end
|
155
148
|
elsif ret == :all
|
156
149
|
storage
|
@@ -158,27 +151,23 @@ module Geos
|
|
158
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
169
|
def iterate
|
181
|
-
@storage.
|
170
|
+
@storage.each_value do |v|
|
182
171
|
yield(v)
|
183
172
|
end
|
184
173
|
end
|
@@ -205,7 +194,7 @@ module Geos
|
|
205
194
|
|
206
195
|
key_ptr = FFIGeos.GEOSSTRtree_nearest_generic_r(
|
207
196
|
Geos.current_handle_pointer,
|
208
|
-
|
197
|
+
ptr,
|
209
198
|
geom.ptr,
|
210
199
|
geom.envelope.ptr,
|
211
200
|
callback,
|
@@ -219,7 +208,7 @@ module Geos
|
|
219
208
|
item = nearest_generic(geom)
|
220
209
|
item[:geometry] if item
|
221
210
|
end
|
222
|
-
|
211
|
+
alias nearest_geometry nearest
|
223
212
|
|
224
213
|
def nearest_item(geom)
|
225
214
|
item = nearest_generic(geom)
|
data/lib/ffi-geos/tools.rb
CHANGED
@@ -1,16 +1,15 @@
|
|
1
|
-
# encoding: UTF-8
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
module Geos
|
5
4
|
class NullPointerError < Geos::Error
|
6
5
|
def initialize(*)
|
7
|
-
super(
|
6
|
+
super('Tried to create a Geometry from a NULL pointer!')
|
8
7
|
end
|
9
8
|
end
|
10
9
|
|
11
10
|
class InvalidGeometryTypeError < Geos::Error
|
12
11
|
def initialize(*)
|
13
|
-
super(
|
12
|
+
super('Invalid geometry type')
|
14
13
|
end
|
15
14
|
end
|
16
15
|
|
@@ -25,12 +24,10 @@ module Geos
|
|
25
24
|
|
26
25
|
def cast_geometry_ptr(geom_ptr, options = {})
|
27
26
|
options = {
|
28
|
-
:
|
27
|
+
auto_free: true
|
29
28
|
}.merge(options)
|
30
29
|
|
31
|
-
if geom_ptr.null?
|
32
|
-
raise Geos::NullPointerError.new
|
33
|
-
end
|
30
|
+
raise Geos::NullPointerError if geom_ptr.null?
|
34
31
|
|
35
32
|
klass = case FFIGeos.GEOSGeomTypeId_r(Geos.current_handle_pointer, geom_ptr)
|
36
33
|
when GEOS_POINT
|
@@ -53,7 +50,7 @@ module Geos
|
|
53
50
|
raise Geos::InvalidGeometryTypeError.new
|
54
51
|
end
|
55
52
|
|
56
|
-
klass.new(geom_ptr, options).tap
|
53
|
+
klass.new(geom_ptr, options).tap do |ret|
|
57
54
|
if options[:srid]
|
58
55
|
ret.srid = options[:srid] || 0
|
59
56
|
elsif options[:srid_copy]
|
@@ -63,11 +60,11 @@ module Geos
|
|
63
60
|
options[:srid_copy] || 0
|
64
61
|
end
|
65
62
|
end
|
66
|
-
|
63
|
+
end
|
67
64
|
end
|
68
65
|
|
69
66
|
def check_geometry(geom)
|
70
|
-
raise TypeError
|
67
|
+
raise TypeError, 'Expected Geos::Geometry' unless geom.is_a?(Geos::Geometry)
|
71
68
|
end
|
72
69
|
|
73
70
|
def pick_srid_from_geoms(srid_a, srid_b, policy = Geos.srid_copy_policy)
|
@@ -81,7 +78,7 @@ module Geos
|
|
81
78
|
when :strict
|
82
79
|
raise Geos::MixedSRIDsError.new(srid_a, srid_b)
|
83
80
|
else
|
84
|
-
raise ArgumentError
|
81
|
+
raise ArgumentError, "Unexpected policy value: #{policy}"
|
85
82
|
end
|
86
83
|
end
|
87
84
|
|
@@ -97,12 +94,12 @@ module Geos
|
|
97
94
|
|
98
95
|
def bool_result(result)
|
99
96
|
case result
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
97
|
+
when 1
|
98
|
+
true
|
99
|
+
when 0
|
100
|
+
false
|
101
|
+
else
|
102
|
+
raise Geos::UnexpectedBooleanResultError, result
|
106
103
|
end
|
107
104
|
end
|
108
105
|
|
@@ -116,7 +113,7 @@ module Geos
|
|
116
113
|
|
117
114
|
def check_enum_value(enum, value)
|
118
115
|
enum[value] or
|
119
|
-
raise TypeError
|
116
|
+
raise TypeError, "Couldn't find valid #{enum.tag} value: #{value}"
|
120
117
|
end
|
121
118
|
|
122
119
|
def symbol_for_enum(enum, value)
|