geos-extensions 1.0.0 → 2.0.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.
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