geos-extensions 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +17 -0
  3. data/MIT-LICENSE +1 -1
  4. data/Rakefile +2 -0
  5. data/lib/geos-extensions.rb +6 -0
  6. data/lib/geos/coordinate_sequence.rb +290 -12
  7. data/lib/geos/extensions/exceptions.rb +2 -0
  8. data/lib/geos/extensions/version.rb +3 -1
  9. data/lib/geos/geometry.rb +3 -1
  10. data/lib/geos/geometry_collection.rb +52 -0
  11. data/lib/geos/geos_helper.rb +2 -0
  12. data/lib/geos/google_maps.rb +2 -0
  13. data/lib/geos/google_maps/api_2.rb +2 -0
  14. data/lib/geos/google_maps/api_3.rb +2 -0
  15. data/lib/geos/google_maps/api_common.rb +2 -0
  16. data/lib/geos/google_maps/polyline_encoder.rb +2 -0
  17. data/lib/geos/line_string.rb +123 -0
  18. data/lib/geos/multi_line_string.rb +2 -0
  19. data/lib/geos/multi_point.rb +2 -0
  20. data/lib/geos/multi_polygon.rb +2 -0
  21. data/lib/geos/point.rb +59 -0
  22. data/lib/geos/polygon.rb +97 -0
  23. data/lib/geos/yaml.rb +1 -0
  24. data/lib/geos/yaml/psych.rb +1 -0
  25. data/lib/geos/yaml/syck.rb +1 -0
  26. data/lib/geos_extensions.rb +2 -0
  27. data/test/coordinate_sequence_tests.rb +256 -0
  28. data/test/geometry_collection_tests.rb +429 -0
  29. data/test/geometry_tests.rb +57 -0
  30. data/test/google_maps_api_2_tests.rb +2 -0
  31. data/test/google_maps_api_3_tests.rb +2 -0
  32. data/test/google_maps_api_common_tests.rb +2 -0
  33. data/test/google_maps_polyline_encoder_tests.rb +2 -0
  34. data/test/helper_tests.rb +2 -0
  35. data/test/line_string_tests.rb +211 -0
  36. data/test/misc_tests.rb +2 -0
  37. data/test/point_tests.rb +173 -0
  38. data/test/polygon_tests.rb +329 -0
  39. data/test/reader_tests.rb +2 -0
  40. data/test/test_helper.rb +73 -3
  41. data/test/writer_tests.rb +2 -0
  42. data/test/yaml_tests.rb +1 -0
  43. metadata +16 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6f5adfee693cca7bcf4ddf2aa10487d225c0c542
4
- data.tar.gz: ec1a90597c0676234ea1493331aff022f89699fd
3
+ metadata.gz: 376ec274e23a76e893dfe82e9a129328b8c22802
4
+ data.tar.gz: b9b7638b716febc1f4cf5f073ae33d3f11a79e02
5
5
  SHA512:
6
- metadata.gz: 60eb6880603184b2545c6642ca97e870dfd8f87fccc1fc517fa959c532bcb50b4919a41025a5b085128d098d0324240c20d8b8747e4bfec130480f20052b8e5d
7
- data.tar.gz: cfd7ee85ece68614b1c271751de00c9664273420c56cd28d73066d4db8e95dca29541ba542679a3ae4d2a274400ba0930786ec8662ed2b23620c789e05e20e0e
6
+ metadata.gz: 06d9bb0eaabeb674ed1e6569894e06bb928f932326143e821772e2732486fb82822feada9db361b727321398fa351a3fc899ba6951f8e789897eb0cc5429ab01
7
+ data.tar.gz: 57d7ef96672139403b437f3c755d32280a20ac2c34e79b8eb3e36f5961d0f9867fe989475a4ce62f7f3423289a0808c16c2589e14069026f569ff27811681169
@@ -0,0 +1,17 @@
1
+ cache: bundler
2
+ sudo: false
3
+ language: ruby
4
+ rvm:
5
+ - 2.4.0
6
+ - 2.3.3
7
+ - 2.2.6
8
+ - rbx-3
9
+ - jruby-19mode
10
+ matrix:
11
+ allow_failures:
12
+ - rvm: rbx-3
13
+ - rvm: jruby-19mode
14
+ addons:
15
+ apt:
16
+ packages:
17
+ - libgeos-c1
@@ -1,4 +1,4 @@
1
- Copyright (c) 2008-2016 J Smith <dark.panda@gmail.com>
1
+ Copyright (c) 2008-2017 J Smith <dark.panda@gmail.com>
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person
4
4
  obtaining a copy of this software and associated documentation
data/Rakefile CHANGED
@@ -26,3 +26,5 @@ Rake::RDocTask.new do |t|
26
26
  t.rdoc_files.include('README.rdoc', 'MIT-LICENSE', 'lib/**/*.rb')
27
27
  end
28
28
 
29
+ task :default => :test
30
+
@@ -1,5 +1,11 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  require File.join(File.dirname(__FILE__), *%w{ geos_extensions })
3
5
 
4
6
  Geos::GoogleMaps.use_api(3)
5
7
 
8
+ module Geos
9
+ class InvalidGeometryError < Geos::Error
10
+ end
11
+ end
@@ -1,18 +1,8 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos
3
5
  class CoordinateSequence
4
- # Returns a Ruby Array of Arrays of coordinates within the
5
- # CoordinateSequence in the form [ x, y, z ].
6
- def to_a
7
- (0...self.length).to_a.collect do |p|
8
- [
9
- self.get_x(p),
10
- (self.dimensions >= 2 ? self.get_y(p) : nil),
11
- (self.dimensions >= 3 && self.get_z(p) > 1.7e-306 ? self.get_z(p) : nil)
12
- ].compact
13
- end
14
- end
15
-
16
6
  # Build some XmlMarkup for KML. You can set various KML options like
17
7
  # tessellate, altitudeMode, etc. Use Rails/Ruby-style code and it
18
8
  # will be converted automatically, i.e. :altitudeMode, not
@@ -87,6 +77,294 @@ module Geos
87
77
  def to_geojson(options = {})
88
78
  self.to_geojsonable(options).to_json
89
79
  end
80
+
81
+ %w{ x y z }.each do |m|
82
+ class_eval(<<-EOF, __FILE__, __LINE__ + 1)
83
+ def #{m}_max
84
+ ret = nil
85
+ self.length.times do |i|
86
+ value = self.get_#{m}(i)
87
+ ret = value if !ret || value >= ret
88
+ end
89
+ ret
90
+ end
91
+
92
+ def #{m}_min
93
+ ret = nil
94
+ self.length.times do |i|
95
+ value = self.get_#{m}(i)
96
+ ret = value if !ret || value <= ret
97
+ end
98
+ ret
99
+ end
100
+ EOF
101
+ end
102
+
103
+ def snap_to_grid!(*args)
104
+ grid = {
105
+ :offset_x => 0, # 1
106
+ :offset_y => 0, # 2
107
+ :offset_z => 0, # -
108
+ :size_x => 0, # 3
109
+ :size_y => 0, # 4
110
+ :size_z => 0 # -
111
+ }
112
+
113
+ if args.length == 1 && args[0].is_a?(Numeric)
114
+ grid[:size_x] = grid[:size_y] = grid[:size_z] = args[0]
115
+ elsif args[0].is_a?(Hash)
116
+ grid.merge!(args[0])
117
+ end
118
+
119
+ if grid[:size]
120
+ grid[:size_x] = grid[:size_y] = grid[:size_z] = grid[:size]
121
+ end
122
+
123
+ if grid[:offset]
124
+ case grid[:offset]
125
+ when Geos::Geometry
126
+ point = grid[:offset].centroid
127
+
128
+ grid[:offset_x] = point.x
129
+ grid[:offset_y] = point.y
130
+ grid[:offset_z] = point.z
131
+ when Array
132
+ grid[:offset_x], grid[:offset_y], grid[:offset_z] = grid[:offset]
133
+ else
134
+ raise ArgumentError.new("Expected :offset option to be a Geos::Point")
135
+ end
136
+ end
137
+
138
+ self.length.times do |i|
139
+ if grid[:size_x] != 0
140
+ self.x[i] = ((self.x[i] - grid[:offset_x]) / grid[:size_x]).round * grid[:size_x] + grid[:offset_x]
141
+ end
142
+
143
+ if grid[:size_y] != 0
144
+ self.y[i] = ((self.y[i] - grid[:offset_y]) / grid[:size_y]).round * grid[:size_y] + grid[:offset_y]
145
+ end
146
+
147
+ if self.has_z? && grid[:size_z] != 0
148
+ self.z[i] = ((self.z[i] - grid[:offset_z]) / grid[:size_z]).round * grid[:size_z] + grid[:offset_z]
149
+ end
150
+ end
151
+
152
+ cs = self.remove_duplicate_coords
153
+ @ptr = cs.ptr
154
+
155
+ self
156
+ end
157
+
158
+ def snap_to_grid(*args)
159
+ self.dup.snap_to_grid!(*args)
160
+ end
161
+
162
+ def remove_duplicate_coords
163
+ Geos::CoordinateSequence.new(self.to_a.inject([]) { |memo, v|
164
+ memo << v unless memo.last == v
165
+ memo
166
+ })
167
+ end
168
+
169
+ def affine!(options)
170
+ options.default = 0.0
171
+
172
+ if self.has_z?
173
+ self.length.times do |i|
174
+ x, y, z = self.x[i], self.y[i], self.z[i]
175
+
176
+ self.x[i] = options[:afac] * x + options[:bfac] * y + options[:cfac] * z + options[:xoff]
177
+ self.y[i] = options[:dfac] * x + options[:efac] * y + options[:ffac] * z + options[:yoff]
178
+ self.z[i] = options[:gfac] * x + options[:hfac] * y + options[:ifac] * z + options[:zoff]
179
+ end
180
+ else
181
+ self.length.times do |i|
182
+ x, y = self.x[i], self.y[i]
183
+
184
+ self.x[i] = options[:afac] * x + options[:bfac] * y + options[:xoff]
185
+ self.y[i] = options[:dfac] * x + options[:efac] * y + options[:yoff]
186
+ end
187
+ end
188
+
189
+ self
190
+ end
191
+
192
+ def affine(options)
193
+ self.dup.affine!(options)
194
+ end
195
+
196
+ def rotate!(radians, origin = [ 0.0, 0.0 ])
197
+ origin = case origin
198
+ when Array
199
+ origin
200
+ when Geos::Geometry
201
+ center = origin.centroid
202
+ [ center.x, center.y ]
203
+ else
204
+ raise ArgumentError.new("Expected an Array or a Geos::Geometry for the origin")
205
+ end
206
+
207
+ self.affine!({
208
+ :afac => Math.cos(radians),
209
+ :bfac => -Math.sin(radians),
210
+ :cfac => 0,
211
+ :dfac => Math.sin(radians),
212
+ :efac => Math.cos(radians),
213
+ :ffac => 0,
214
+ :gfac => 0,
215
+ :hfac => 0,
216
+ :ifac => 1,
217
+ :xoff => origin[0] - Math.cos(radians) * origin[0] + Math.sin(radians) * origin[1],
218
+ :yoff => origin[1] - Math.sin(radians) * origin[0] - Math.cos(radians) * origin[1],
219
+ :zoff => 0
220
+ })
221
+ end
222
+
223
+ def rotate(radians, origin = [ 0.0, 0.0 ])
224
+ self.dup.rotate!(radians, origin)
225
+ end
226
+
227
+ def rotate_x!(radians)
228
+ self.affine!({
229
+ :afac => 1,
230
+ :bfac => 0,
231
+ :cfac => 0,
232
+ :dfac => 0,
233
+ :efac => Math.cos(radians),
234
+ :ffac => -Math.sin(radians),
235
+ :gfac => 0,
236
+ :hfac => Math.sin(radians),
237
+ :ifac => Math.cos(radians),
238
+ :xoff => 0,
239
+ :yoff => 0,
240
+ :zoff => 0
241
+ })
242
+ end
243
+
244
+ def rotate_x(radians)
245
+ self.dup.rotate_x!(radians)
246
+ end
247
+
248
+ def rotate_y!(radians)
249
+ self.affine!({
250
+ :afac => Math.cos(radians),
251
+ :bfac => 0,
252
+ :cfac => Math.sin(radians),
253
+ :dfac => 0,
254
+ :efac => 1,
255
+ :ffac => 0,
256
+ :gfac => -Math.sin(radians),
257
+ :hfac => 0,
258
+ :ifac => Math.cos(radians),
259
+ :xoff => 0,
260
+ :yoff => 0,
261
+ :zoff => 0
262
+ })
263
+ end
264
+
265
+ def rotate_y(radians)
266
+ self.dup.rotate_y!(radians)
267
+ end
268
+
269
+ def rotate_z!(radians)
270
+ self.rotate!(radians)
271
+ end
272
+
273
+ def rotate_z(radians)
274
+ self.dup.rotate!(radians)
275
+ end
276
+
277
+ def scale!(*args)
278
+ x, y, z = if args.length == 1 && args[0].is_a?(Hash)
279
+ args[0].values_at(:x, :y, :z)
280
+ elsif args.length.between?(1, 3)
281
+ args.values_at(0...3)
282
+ else
283
+ raise ArgumentError.new("Wrong number of arguments #{args.length} for 1-3")
284
+ end
285
+
286
+ self.affine!({
287
+ :afac => x || 1,
288
+ :bfac => 0,
289
+ :cfac => 0,
290
+ :dfac => 0,
291
+ :efac => y || 1,
292
+ :ffac => 0,
293
+ :gfac => 0,
294
+ :hfac => 0,
295
+ :ifac => z || 1,
296
+ :xoff => 0,
297
+ :yoff => 0,
298
+ :zoff => 0
299
+ })
300
+ end
301
+
302
+ def scale(*args)
303
+ self.dup.scale!(*args)
304
+ end
305
+
306
+ def trans_scale!(*args)
307
+ delta_x, delta_y, x_factor, y_factor = if args.length == 1 && args[0].is_a?(Hash)
308
+ args[0].values_at(:delta_x, :delta_y, :x_factor, :y_factor)
309
+ elsif args.length.between?(1, 4)
310
+ args.values_at(0...4)
311
+ else
312
+ raise ArgumentError.new("Wrong number of arguments #{args.length} for 1-4")
313
+ end
314
+
315
+ x_factor ||= 1
316
+ y_factor ||= 1
317
+ delta_x ||= 0
318
+ delta_y ||= 0
319
+
320
+ self.affine!({
321
+ :afac => x_factor,
322
+ :bfac => 0,
323
+ :cfac => 0,
324
+ :dfac => 0,
325
+ :efac => y_factor,
326
+ :ffac => 0,
327
+ :gfac => 0,
328
+ :hfac => 0,
329
+ :ifac => 1,
330
+ :xoff => delta_x * x_factor,
331
+ :yoff => delta_y * y_factor,
332
+ :zoff => 0
333
+ })
334
+ end
335
+
336
+ def trans_scale(*args)
337
+ self.dup.trans_scale!(*args)
338
+ end
339
+
340
+ def translate!(*args)
341
+ x, y, z = if args.length == 1 && args[0].is_a?(Hash)
342
+ args[0].values_at(:x, :y, :z)
343
+ elsif args.length.between?(1, 3)
344
+ args.values_at(0...3)
345
+ else
346
+ raise ArgumentError.new("Wrong number of arguments #{args.length} for 1-3")
347
+ end
348
+
349
+ self.affine!({
350
+ :afac => 1,
351
+ :bfac => 0,
352
+ :cfac => 0,
353
+ :dfac => 0,
354
+ :efac => 1,
355
+ :ffac => 0,
356
+ :gfac => 0,
357
+ :hfac => 0,
358
+ :ifac => 1,
359
+ :xoff => x || 0,
360
+ :yoff => y || 0,
361
+ :zoff => z || 1
362
+ })
363
+ end
364
+
365
+ def translate(*args)
366
+ self.dup.translate!(*args)
367
+ end
90
368
  end
91
369
  end
92
370
 
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos
3
5
  unless defined?(Geos::Error)
@@ -1,7 +1,9 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos
3
5
  module Extensions
4
- VERSION = '1.0.0'.freeze
6
+ VERSION = '2.0.0'.freeze
5
7
  end
6
8
  end
7
9
 
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos
3
5
  # This is our base module that we use for some generic methods used all
@@ -60,7 +62,7 @@ module Geos
60
62
  []
61
63
  end
62
64
 
63
- ret = ''
65
+ ret = ''.dup
64
66
 
65
67
  if options[:include_srid]
66
68
  srid = if options[:srid]
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
1
3
 
2
4
  module Geos
3
5
  class GeometryCollection
@@ -55,6 +57,56 @@ module Geos
55
57
  }
56
58
  end
57
59
  alias_method :to_geojsonable, :as_geojson
60
+
61
+ # Dumps points similarly to the PostGIS `ST_DumpPoints` function.
62
+ def dump_points(cur_path = [])
63
+ self.each do |geom|
64
+ cur_path << geom.dump_points
65
+ end
66
+ cur_path
67
+ end
68
+
69
+ %w{ x y z }.each do |dimension|
70
+ %w{ max min }.each do |op|
71
+ self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
72
+ def #{dimension}_#{op}
73
+ unless self.empty?
74
+ self.collect(&:#{dimension}_#{op}).#{op}
75
+ end
76
+ end
77
+ EOF
78
+ end
79
+ end
80
+
81
+ %w{
82
+ affine
83
+ rotate
84
+ rotate_x
85
+ rotate_y
86
+ rotate_z
87
+ scale
88
+ snap_to_grid
89
+ trans_scale
90
+ translate
91
+ }.each do |m|
92
+ self.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
93
+ def #{m}!(*args)
94
+ unless self.empty?
95
+ self.num_geometries.times do |i|
96
+ self[i].#{m}!(*args)
97
+ end
98
+ end
99
+
100
+ self
101
+ end
102
+
103
+ def #{m}(*args)
104
+ ret = self.dup.#{m}!(*args)
105
+ ret.srid = pick_srid_according_to_policy(self.srid)
106
+ ret
107
+ end
108
+ EOF
109
+ end
58
110
  end
59
111
  end
60
112