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
@@ -1,4 +1,3 @@
|
|
1
|
-
# encoding: UTF-8
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
module Geos
|
@@ -33,7 +32,7 @@ module Geos
|
|
33
32
|
|
34
33
|
@params = {}
|
35
34
|
VALID_PARAMETERS.each do |param|
|
36
|
-
|
35
|
+
send("#{param}=", params[param])
|
37
36
|
end
|
38
37
|
end
|
39
38
|
|
@@ -44,43 +43,33 @@ module Geos
|
|
44
43
|
def endcap=(value)
|
45
44
|
check_enum_value(Geos::BufferCapStyles, value)
|
46
45
|
|
47
|
-
if bool_result(FFIGeos.GEOSBufferParams_setEndCapStyle_r(Geos.current_handle_pointer, ptr, value))
|
48
|
-
@params[:endcap] = symbol_for_enum(Geos::BufferCapStyles, value)
|
49
|
-
end
|
46
|
+
@params[:endcap] = symbol_for_enum(Geos::BufferCapStyles, value) if bool_result(FFIGeos.GEOSBufferParams_setEndCapStyle_r(Geos.current_handle_pointer, ptr, value))
|
50
47
|
end
|
51
48
|
|
52
49
|
def join=(value)
|
53
50
|
check_enum_value(Geos::BufferJoinStyles, value)
|
54
51
|
|
55
|
-
if bool_result(FFIGeos.GEOSBufferParams_setJoinStyle_r(Geos.current_handle_pointer, ptr, value))
|
56
|
-
@params[:join] = symbol_for_enum(Geos::BufferJoinStyles, value)
|
57
|
-
end
|
52
|
+
@params[:join] = symbol_for_enum(Geos::BufferJoinStyles, value) if bool_result(FFIGeos.GEOSBufferParams_setJoinStyle_r(Geos.current_handle_pointer, ptr, value))
|
58
53
|
end
|
59
54
|
|
60
55
|
def mitre_limit=(value)
|
61
|
-
if bool_result(FFIGeos.GEOSBufferParams_setMitreLimit_r(Geos.current_handle_pointer, ptr, value))
|
62
|
-
@params[:mitre_limit] = value
|
63
|
-
end
|
56
|
+
@params[:mitre_limit] = value if bool_result(FFIGeos.GEOSBufferParams_setMitreLimit_r(Geos.current_handle_pointer, ptr, value))
|
64
57
|
end
|
65
58
|
|
66
59
|
def quad_segs=(value)
|
67
|
-
if bool_result(FFIGeos.GEOSBufferParams_setQuadrantSegments_r(Geos.current_handle_pointer, ptr, value))
|
68
|
-
@params[:quad_segs] = value
|
69
|
-
end
|
60
|
+
@params[:quad_segs] = value if bool_result(FFIGeos.GEOSBufferParams_setQuadrantSegments_r(Geos.current_handle_pointer, ptr, value))
|
70
61
|
end
|
71
62
|
|
72
63
|
def single_sided=(value)
|
73
|
-
if bool_result(FFIGeos.GEOSBufferParams_setSingleSided_r(Geos.current_handle_pointer, ptr, Geos::Tools.bool_to_int(value)))
|
74
|
-
@params[:single_sided] = value
|
75
|
-
end
|
64
|
+
@params[:single_sided] = value if bool_result(FFIGeos.GEOSBufferParams_setSingleSided_r(Geos.current_handle_pointer, ptr, Geos::Tools.bool_to_int(value)))
|
76
65
|
end
|
77
66
|
|
78
67
|
VALID_PARAMETERS.each do |param|
|
79
|
-
|
68
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
80
69
|
def #{param}
|
81
70
|
@params[:#{param}]
|
82
71
|
end
|
83
|
-
|
72
|
+
RUBY
|
84
73
|
end
|
85
74
|
else
|
86
75
|
attr_accessor(*VALID_PARAMETERS)
|
@@ -89,7 +78,7 @@ module Geos
|
|
89
78
|
params = Geos::Constants::BUFFER_PARAM_DEFAULTS.merge(params)
|
90
79
|
|
91
80
|
VALID_PARAMETERS.each do |param|
|
92
|
-
|
81
|
+
send("#{param}=", params[param])
|
93
82
|
end
|
94
83
|
end
|
95
84
|
end
|
@@ -1,8 +1,6 @@
|
|
1
|
-
# encoding: UTF-8
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
module Geos
|
5
|
-
|
6
4
|
# A CoordinateSequence is a list of coordinates in a Geometry.
|
7
5
|
class CoordinateSequence
|
8
6
|
class ParseError < Geos::ParseError
|
@@ -19,22 +17,22 @@ module Geos
|
|
19
17
|
end
|
20
18
|
|
21
19
|
def [](idx)
|
22
|
-
parent.get_ordinate(idx,
|
20
|
+
parent.get_ordinate(idx, dimension)
|
23
21
|
end
|
24
22
|
|
25
23
|
def []=(idx, value)
|
26
|
-
parent.set_ordinate(idx,
|
24
|
+
parent.set_ordinate(idx, dimension, value)
|
27
25
|
end
|
28
26
|
|
29
27
|
def each
|
30
28
|
if block_given?
|
31
29
|
parent.length.times do |n|
|
32
|
-
yield parent.get_ordinate(n,
|
30
|
+
yield parent.get_ordinate(n, dimension)
|
33
31
|
end
|
34
32
|
self
|
35
33
|
else
|
36
34
|
parent.length.times.collect { |n|
|
37
|
-
parent.get_ordinate(n,
|
35
|
+
parent.get_ordinate(n, dimension)
|
38
36
|
}.to_enum
|
39
37
|
end
|
40
38
|
end
|
@@ -71,28 +69,26 @@ module Geos
|
|
71
69
|
lengths = points.collect(&:length).uniq
|
72
70
|
|
73
71
|
if lengths.empty?
|
74
|
-
[
|
72
|
+
[0, 0]
|
75
73
|
elsif lengths.length != 1
|
76
|
-
raise ParseError
|
74
|
+
raise ParseError, 'Different sized points found in Array'
|
77
75
|
elsif !lengths.first.between?(1, 3)
|
78
|
-
raise ParseError
|
76
|
+
raise ParseError, 'Expected points to contain 1-3 elements'
|
79
77
|
else
|
80
|
-
[
|
78
|
+
[points.length, points.first.length]
|
81
79
|
end
|
82
80
|
elsif args.first.is_a?(Hash)
|
83
81
|
args.first.values_at(:size, :dimensions)
|
82
|
+
elsif !args.length.between?(0, 2)
|
83
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for 0-2)"
|
84
84
|
else
|
85
|
-
|
86
|
-
raise ArgumentError.new("wrong number of arguments (#{args.length} for 0-2)")
|
87
|
-
else
|
88
|
-
[ args[0], args[1] ]
|
89
|
-
end
|
85
|
+
[args[0], args[1]]
|
90
86
|
end
|
91
87
|
|
92
88
|
size ||= 0
|
93
89
|
dimensions ||= 0
|
94
90
|
|
95
|
-
[
|
91
|
+
[FFIGeos.GEOSCoordSeq_create_r(Geos.current_handle_pointer, size, dimensions), true]
|
96
92
|
end
|
97
93
|
|
98
94
|
@ptr = FFI::AutoPointer.new(
|
@@ -107,11 +103,11 @@ module Geos
|
|
107
103
|
@y = CoordinateAccessor.new(self, 1)
|
108
104
|
@z = CoordinateAccessor.new(self, 2)
|
109
105
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
106
|
+
return unless points
|
107
|
+
|
108
|
+
points.each_with_index do |point, idx|
|
109
|
+
point.each_with_index do |val, dim|
|
110
|
+
set_ordinate(idx, dim, val)
|
115
111
|
end
|
116
112
|
end
|
117
113
|
end
|
@@ -135,13 +131,13 @@ module Geos
|
|
135
131
|
# 2-dimensional CoordinateSequences.
|
136
132
|
def each
|
137
133
|
if block_given?
|
138
|
-
|
139
|
-
yield
|
134
|
+
length.times do |n|
|
135
|
+
yield build_coordinate(n)
|
140
136
|
end
|
141
137
|
self
|
142
138
|
else
|
143
|
-
|
144
|
-
|
139
|
+
length.times.collect { |n|
|
140
|
+
build_coordinate(n)
|
145
141
|
}.to_enum
|
146
142
|
end
|
147
143
|
end
|
@@ -149,82 +145,82 @@ module Geos
|
|
149
145
|
def [](*args)
|
150
146
|
if args.length == 1 && args.first.is_a?(Numeric) && args.first >= 0
|
151
147
|
i = args.first
|
152
|
-
ary = [
|
153
|
-
ary <<
|
148
|
+
ary = [get_x(i), get_y(i)]
|
149
|
+
ary << get_z(i) if has_z?
|
154
150
|
ary
|
155
151
|
else
|
156
|
-
|
152
|
+
to_a[*args]
|
157
153
|
end
|
158
154
|
end
|
159
|
-
|
155
|
+
alias slice []
|
160
156
|
|
161
157
|
def has_z?
|
162
|
-
|
158
|
+
dimensions == 3
|
163
159
|
end
|
164
160
|
|
165
161
|
# Sets the x value of a coordinate. Can also be set via #x[]=.
|
166
162
|
def set_x(idx, val)
|
167
|
-
|
168
|
-
FFIGeos.GEOSCoordSeq_setX_r(Geos.current_handle_pointer,
|
163
|
+
check_bounds(idx)
|
164
|
+
FFIGeos.GEOSCoordSeq_setX_r(Geos.current_handle_pointer, ptr, idx, val.to_f)
|
169
165
|
end
|
170
166
|
|
171
167
|
# Sets the y value of a coordinate. Can also be set via #y[]=.
|
172
168
|
def set_y(idx, val)
|
173
|
-
|
174
|
-
FFIGeos.GEOSCoordSeq_setY_r(Geos.current_handle_pointer,
|
169
|
+
check_bounds(idx)
|
170
|
+
FFIGeos.GEOSCoordSeq_setY_r(Geos.current_handle_pointer, ptr, idx, val.to_f)
|
175
171
|
end
|
176
172
|
|
177
173
|
# Sets the z value of a coordinate. Can also be set via #z[]=.
|
178
174
|
def set_z(idx, val)
|
179
|
-
|
180
|
-
FFIGeos.GEOSCoordSeq_setZ_r(Geos.current_handle_pointer,
|
175
|
+
check_bounds(idx)
|
176
|
+
FFIGeos.GEOSCoordSeq_setZ_r(Geos.current_handle_pointer, ptr, idx, val.to_f)
|
181
177
|
end
|
182
178
|
|
183
179
|
def set_ordinate(idx, dim, val)
|
184
|
-
|
185
|
-
FFIGeos.GEOSCoordSeq_setOrdinate_r(Geos.current_handle_pointer,
|
180
|
+
check_bounds(idx)
|
181
|
+
FFIGeos.GEOSCoordSeq_setOrdinate_r(Geos.current_handle_pointer, ptr, idx, dim, val.to_f)
|
186
182
|
end
|
187
183
|
|
188
184
|
# Gets the x value of a coordinate. Can also be retrieved via #x[].
|
189
185
|
def get_x(idx)
|
190
|
-
|
186
|
+
check_bounds(idx)
|
191
187
|
double_ptr = FFI::MemoryPointer.new(:double)
|
192
|
-
FFIGeos.GEOSCoordSeq_getX_r(Geos.current_handle_pointer,
|
188
|
+
FFIGeos.GEOSCoordSeq_getX_r(Geos.current_handle_pointer, ptr, idx, double_ptr)
|
193
189
|
double_ptr.read_double
|
194
190
|
end
|
195
191
|
|
196
192
|
# Gets the y value of a coordinate. Can also be retrieved via #y[].
|
197
193
|
def get_y(idx)
|
198
|
-
|
194
|
+
check_bounds(idx)
|
199
195
|
double_ptr = FFI::MemoryPointer.new(:double)
|
200
|
-
FFIGeos.GEOSCoordSeq_getY_r(Geos.current_handle_pointer,
|
196
|
+
FFIGeos.GEOSCoordSeq_getY_r(Geos.current_handle_pointer, ptr, idx, double_ptr)
|
201
197
|
double_ptr.read_double
|
202
198
|
end
|
203
199
|
|
204
200
|
# Gets the z value of a coordinate. Can also be retrieved via #z[].
|
205
201
|
def get_z(idx)
|
206
|
-
|
202
|
+
check_bounds(idx)
|
207
203
|
double_ptr = FFI::MemoryPointer.new(:double)
|
208
|
-
FFIGeos.GEOSCoordSeq_getZ_r(Geos.current_handle_pointer,
|
204
|
+
FFIGeos.GEOSCoordSeq_getZ_r(Geos.current_handle_pointer, ptr, idx, double_ptr)
|
209
205
|
double_ptr.read_double
|
210
206
|
end
|
211
207
|
|
212
208
|
def get_ordinate(idx, dim)
|
213
|
-
|
209
|
+
check_bounds(idx)
|
214
210
|
double_ptr = FFI::MemoryPointer.new(:double)
|
215
|
-
FFIGeos.GEOSCoordSeq_getOrdinate_r(Geos.current_handle_pointer,
|
211
|
+
FFIGeos.GEOSCoordSeq_getOrdinate_r(Geos.current_handle_pointer, ptr, idx, dim, double_ptr)
|
216
212
|
double_ptr.read_double
|
217
213
|
end
|
218
214
|
|
219
215
|
def length
|
220
216
|
int_ptr = FFI::MemoryPointer.new(:int)
|
221
|
-
FFIGeos.GEOSCoordSeq_getSize_r(Geos.current_handle_pointer,
|
217
|
+
FFIGeos.GEOSCoordSeq_getSize_r(Geos.current_handle_pointer, ptr, int_ptr)
|
222
218
|
int_ptr.read_int
|
223
219
|
end
|
224
|
-
|
220
|
+
alias size length
|
225
221
|
|
226
222
|
def empty?
|
227
|
-
|
223
|
+
length.zero?
|
228
224
|
end
|
229
225
|
|
230
226
|
def dimensions
|
@@ -232,47 +228,337 @@ module Geos
|
|
232
228
|
@dimensions
|
233
229
|
else
|
234
230
|
int_ptr = FFI::MemoryPointer.new(:int)
|
235
|
-
FFIGeos.GEOSCoordSeq_getDimensions_r(Geos.current_handle_pointer,
|
231
|
+
FFIGeos.GEOSCoordSeq_getDimensions_r(Geos.current_handle_pointer, ptr, int_ptr)
|
236
232
|
@dimensions = int_ptr.read_int
|
237
233
|
end
|
238
234
|
end
|
239
235
|
|
236
|
+
if FFIGeos.respond_to?(:GEOSCoordSeq_isCCW_r)
|
237
|
+
# Available in GEOS 3.7+.
|
238
|
+
def counter_clockwise?
|
239
|
+
char_ptr = FFI::MemoryPointer.new(:char)
|
240
|
+
FFIGeos.GEOSCoordSeq_isCCW_r(Geos.current_handle_pointer, ptr, char_ptr)
|
241
|
+
Tools.bool_result(char_ptr.read_char)
|
242
|
+
end
|
243
|
+
alias ccw? counter_clockwise?
|
244
|
+
end
|
245
|
+
|
240
246
|
def to_point(options = {})
|
241
|
-
Geos.create_point(self, :
|
247
|
+
Geos.create_point(self, srid: options[:srid])
|
242
248
|
end
|
243
249
|
|
244
250
|
def to_linear_ring(options = {})
|
245
|
-
Geos.create_linear_ring(self, :
|
251
|
+
Geos.create_linear_ring(self, srid: options[:srid])
|
246
252
|
end
|
247
253
|
|
248
254
|
def to_line_string(options = {})
|
249
|
-
Geos.create_line_string(self, :
|
255
|
+
Geos.create_line_string(self, srid: options[:srid])
|
250
256
|
end
|
251
257
|
|
252
258
|
def to_polygon(options = {})
|
253
|
-
Geos.create_polygon(self, :
|
259
|
+
Geos.create_polygon(self, srid: options[:srid])
|
254
260
|
end
|
255
261
|
|
256
262
|
def to_s
|
257
|
-
|
263
|
+
entries.collect { |entry|
|
258
264
|
entry.join(' ')
|
259
265
|
}.join(', ')
|
260
266
|
end
|
261
267
|
|
262
|
-
|
268
|
+
%w{ x y z }.each do |m|
|
269
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
270
|
+
def #{m}_max
|
271
|
+
ret = nil
|
272
|
+
length.times do |i|
|
273
|
+
value = get_#{m}(i)
|
274
|
+
ret = value if !ret || value >= ret
|
275
|
+
end
|
276
|
+
ret
|
277
|
+
end
|
278
|
+
|
279
|
+
def #{m}_min
|
280
|
+
ret = nil
|
281
|
+
length.times do |i|
|
282
|
+
value = get_#{m}(i)
|
283
|
+
ret = value if !ret || value <= ret
|
284
|
+
end
|
285
|
+
ret
|
286
|
+
end
|
287
|
+
RUBY
|
288
|
+
end
|
289
|
+
|
290
|
+
def snap_to_grid!(*args)
|
291
|
+
grid = {
|
292
|
+
offset_x: 0, # 1
|
293
|
+
offset_y: 0, # 2
|
294
|
+
offset_z: 0, # -
|
295
|
+
size_x: 0, # 3
|
296
|
+
size_y: 0, # 4
|
297
|
+
size_z: 0 # -
|
298
|
+
}
|
299
|
+
|
300
|
+
if args.length == 1 && args[0].is_a?(Numeric)
|
301
|
+
grid[:size_x] = grid[:size_y] = grid[:size_z] = args[0]
|
302
|
+
elsif args[0].is_a?(Hash)
|
303
|
+
grid.merge!(args[0])
|
304
|
+
end
|
305
|
+
|
306
|
+
grid[:size_x] = grid[:size_y] = grid[:size_z] = grid[:size] if grid[:size]
|
263
307
|
|
264
|
-
|
265
|
-
|
266
|
-
|
308
|
+
if grid[:offset]
|
309
|
+
case grid[:offset]
|
310
|
+
when Geos::Geometry
|
311
|
+
point = grid[:offset].centroid
|
312
|
+
|
313
|
+
grid[:offset_x] = point.x
|
314
|
+
grid[:offset_y] = point.y
|
315
|
+
grid[:offset_z] = point.z
|
316
|
+
when Array
|
317
|
+
grid[:offset_x], grid[:offset_y], grid[:offset_z] = grid[:offset]
|
318
|
+
else
|
319
|
+
raise ArgumentError, 'Expected :offset option to be a Geos::Point'
|
320
|
+
end
|
267
321
|
end
|
322
|
+
|
323
|
+
length.times do |i|
|
324
|
+
x[i] = ((x[i] - grid[:offset_x]) / grid[:size_x]).round * grid[:size_x] + grid[:offset_x] if grid[:size_x] != 0
|
325
|
+
|
326
|
+
y[i] = ((y[i] - grid[:offset_y]) / grid[:size_y]).round * grid[:size_y] + grid[:offset_y] if grid[:size_y] != 0
|
327
|
+
|
328
|
+
z[i] = ((z[i] - grid[:offset_z]) / grid[:size_z]).round * grid[:size_z] + grid[:offset_z] if has_z? && grid[:size_z] != 0
|
329
|
+
end
|
330
|
+
|
331
|
+
cs = remove_duplicate_coords
|
332
|
+
@ptr = cs.ptr
|
333
|
+
|
334
|
+
self
|
335
|
+
end
|
336
|
+
|
337
|
+
def snap_to_grid(*args)
|
338
|
+
dup.snap_to_grid!(*args)
|
339
|
+
end
|
340
|
+
|
341
|
+
def remove_duplicate_coords
|
342
|
+
Geos::CoordinateSequence.new(to_a.each_with_object([]) do |v, memo|
|
343
|
+
memo << v unless memo.last == v
|
344
|
+
end)
|
268
345
|
end
|
269
346
|
|
270
|
-
def
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
347
|
+
def affine!(options)
|
348
|
+
options.default = 0.0
|
349
|
+
|
350
|
+
if has_z?
|
351
|
+
length.times do |i|
|
352
|
+
x = self.x[i]
|
353
|
+
y = self.y[i]
|
354
|
+
z = self.z[i]
|
355
|
+
|
356
|
+
self.x[i] = options[:afac] * x + options[:bfac] * y + options[:cfac] * z + options[:xoff]
|
357
|
+
self.y[i] = options[:dfac] * x + options[:efac] * y + options[:ffac] * z + options[:yoff]
|
358
|
+
self.z[i] = options[:gfac] * x + options[:hfac] * y + options[:ifac] * z + options[:zoff]
|
359
|
+
end
|
360
|
+
else
|
361
|
+
length.times do |i|
|
362
|
+
x = self.x[i]
|
363
|
+
y = self.y[i]
|
364
|
+
|
365
|
+
self.x[i] = options[:afac] * x + options[:bfac] * y + options[:xoff]
|
366
|
+
self.y[i] = options[:dfac] * x + options[:efac] * y + options[:yoff]
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
self
|
371
|
+
end
|
372
|
+
|
373
|
+
def affine(options)
|
374
|
+
dup.affine!(options)
|
375
|
+
end
|
376
|
+
|
377
|
+
def rotate!(radians, origin = [0.0, 0.0])
|
378
|
+
origin = case origin
|
379
|
+
when Array
|
380
|
+
origin
|
381
|
+
when Geos::Geometry
|
382
|
+
center = origin.centroid
|
383
|
+
[center.x, center.y]
|
384
|
+
else
|
385
|
+
raise ArgumentError, 'Expected an Array or a Geos::Geometry for the origin'
|
386
|
+
end
|
387
|
+
|
388
|
+
affine!(
|
389
|
+
afac: Math.cos(radians),
|
390
|
+
bfac: -Math.sin(radians),
|
391
|
+
cfac: 0,
|
392
|
+
dfac: Math.sin(radians),
|
393
|
+
efac: Math.cos(radians),
|
394
|
+
ffac: 0,
|
395
|
+
gfac: 0,
|
396
|
+
hfac: 0,
|
397
|
+
ifac: 1,
|
398
|
+
xoff: origin[0] - Math.cos(radians) * origin[0] + Math.sin(radians) * origin[1],
|
399
|
+
yoff: origin[1] - Math.sin(radians) * origin[0] - Math.cos(radians) * origin[1],
|
400
|
+
zoff: 0
|
401
|
+
)
|
276
402
|
end
|
403
|
+
|
404
|
+
def rotate(radians, origin = [0.0, 0.0])
|
405
|
+
dup.rotate!(radians, origin)
|
406
|
+
end
|
407
|
+
|
408
|
+
def rotate_x!(radians)
|
409
|
+
affine!(
|
410
|
+
afac: 1,
|
411
|
+
bfac: 0,
|
412
|
+
cfac: 0,
|
413
|
+
dfac: 0,
|
414
|
+
efac: Math.cos(radians),
|
415
|
+
ffac: -Math.sin(radians),
|
416
|
+
gfac: 0,
|
417
|
+
hfac: Math.sin(radians),
|
418
|
+
ifac: Math.cos(radians),
|
419
|
+
xoff: 0,
|
420
|
+
yoff: 0,
|
421
|
+
zoff: 0
|
422
|
+
)
|
423
|
+
end
|
424
|
+
|
425
|
+
def rotate_x(radians)
|
426
|
+
dup.rotate_x!(radians)
|
427
|
+
end
|
428
|
+
|
429
|
+
def rotate_y!(radians)
|
430
|
+
affine!(
|
431
|
+
afac: Math.cos(radians),
|
432
|
+
bfac: 0,
|
433
|
+
cfac: Math.sin(radians),
|
434
|
+
dfac: 0,
|
435
|
+
efac: 1,
|
436
|
+
ffac: 0,
|
437
|
+
gfac: -Math.sin(radians),
|
438
|
+
hfac: 0,
|
439
|
+
ifac: Math.cos(radians),
|
440
|
+
xoff: 0,
|
441
|
+
yoff: 0,
|
442
|
+
zoff: 0
|
443
|
+
)
|
444
|
+
end
|
445
|
+
|
446
|
+
def rotate_y(radians)
|
447
|
+
dup.rotate_y!(radians)
|
448
|
+
end
|
449
|
+
|
450
|
+
def rotate_z!(radians)
|
451
|
+
rotate!(radians)
|
452
|
+
end
|
453
|
+
|
454
|
+
def rotate_z(radians)
|
455
|
+
dup.rotate!(radians)
|
456
|
+
end
|
457
|
+
|
458
|
+
def scale!(*args)
|
459
|
+
x, y, z = if args.length == 1 && args[0].is_a?(Hash)
|
460
|
+
args[0].values_at(:x, :y, :z)
|
461
|
+
elsif args.length.between?(1, 3)
|
462
|
+
args.values_at(0...3)
|
463
|
+
else
|
464
|
+
raise ArgumentError, "Wrong number of arguments #{args.length} for 1-3"
|
465
|
+
end
|
466
|
+
|
467
|
+
affine!(
|
468
|
+
afac: x || 1,
|
469
|
+
bfac: 0,
|
470
|
+
cfac: 0,
|
471
|
+
dfac: 0,
|
472
|
+
efac: y || 1,
|
473
|
+
ffac: 0,
|
474
|
+
gfac: 0,
|
475
|
+
hfac: 0,
|
476
|
+
ifac: z || 1,
|
477
|
+
xoff: 0,
|
478
|
+
yoff: 0,
|
479
|
+
zoff: 0
|
480
|
+
)
|
481
|
+
end
|
482
|
+
|
483
|
+
def scale(*args)
|
484
|
+
dup.scale!(*args)
|
485
|
+
end
|
486
|
+
|
487
|
+
def trans_scale!(*args)
|
488
|
+
delta_x, delta_y, x_factor, y_factor = if args.length == 1 && args[0].is_a?(Hash)
|
489
|
+
args[0].values_at(:delta_x, :delta_y, :x_factor, :y_factor)
|
490
|
+
elsif args.length.between?(1, 4)
|
491
|
+
args.values_at(0...4)
|
492
|
+
else
|
493
|
+
raise ArgumentError, "Wrong number of arguments #{args.length} for 1-4"
|
494
|
+
end
|
495
|
+
|
496
|
+
x_factor ||= 1
|
497
|
+
y_factor ||= 1
|
498
|
+
delta_x ||= 0
|
499
|
+
delta_y ||= 0
|
500
|
+
|
501
|
+
affine!(
|
502
|
+
afac: x_factor,
|
503
|
+
bfac: 0,
|
504
|
+
cfac: 0,
|
505
|
+
dfac: 0,
|
506
|
+
efac: y_factor,
|
507
|
+
ffac: 0,
|
508
|
+
gfac: 0,
|
509
|
+
hfac: 0,
|
510
|
+
ifac: 1,
|
511
|
+
xoff: delta_x * x_factor,
|
512
|
+
yoff: delta_y * y_factor,
|
513
|
+
zoff: 0
|
514
|
+
)
|
515
|
+
end
|
516
|
+
|
517
|
+
def trans_scale(*args)
|
518
|
+
dup.trans_scale!(*args)
|
519
|
+
end
|
520
|
+
|
521
|
+
def translate!(*args)
|
522
|
+
x, y, z = if args.length == 1 && args[0].is_a?(Hash)
|
523
|
+
args[0].values_at(:x, :y, :z)
|
524
|
+
elsif args.length.between?(1, 3)
|
525
|
+
args.values_at(0...3)
|
526
|
+
else
|
527
|
+
raise ArgumentError, "Wrong number of arguments #{args.length} for 1-3"
|
528
|
+
end
|
529
|
+
|
530
|
+
affine!(
|
531
|
+
afac: 1,
|
532
|
+
bfac: 0,
|
533
|
+
cfac: 0,
|
534
|
+
dfac: 0,
|
535
|
+
efac: 1,
|
536
|
+
ffac: 0,
|
537
|
+
gfac: 0,
|
538
|
+
hfac: 0,
|
539
|
+
ifac: 1,
|
540
|
+
xoff: x || 0,
|
541
|
+
yoff: y || 0,
|
542
|
+
zoff: z || 1
|
543
|
+
)
|
544
|
+
end
|
545
|
+
|
546
|
+
def translate(*args)
|
547
|
+
dup.translate!(*args)
|
548
|
+
end
|
549
|
+
|
550
|
+
protected
|
551
|
+
|
552
|
+
def check_bounds(idx) #:nodoc:
|
553
|
+
raise Geos::IndexBoundsError, 'Index out of bounds' if idx.negative? || idx >= length
|
554
|
+
end
|
555
|
+
|
556
|
+
def build_coordinate(n) #:nodoc:
|
557
|
+
[
|
558
|
+
get_x(n),
|
559
|
+
(dimensions >= 2 ? get_y(n) : nil),
|
560
|
+
(dimensions >= 3 ? get_z(n) : nil)
|
561
|
+
].compact
|
562
|
+
end
|
277
563
|
end
|
278
564
|
end
|