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