terraformer 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4bdd47e750c746d35d7e55ac4ecbca82548ca9b0
4
- data.tar.gz: b5612978bf4ba8145a4cbb4103574680e3e1e8a9
3
+ metadata.gz: 8870157d8b1074418f97a8760cece253047829e0
4
+ data.tar.gz: de0f63a4d2a320e9e3c8a2c5b70025d2208a56fc
5
5
  SHA512:
6
- metadata.gz: de22c0fe889529a57e7cbd8538344b1e2aa73ce6a218c1f0908d239c177b56eb77f9fa47f436f81dd90302ab93f5c3aaec05f28fda7f38a98d8091b084619891
7
- data.tar.gz: a7b86a474675c9c63023820afa8131d6288a6158780ed2e03b0f9fcd43c7a13a82d83df287c34cfe04d2057d155859059caf343f8a239d5047903d249d476048
6
+ metadata.gz: 252e0d88535889c9bc9fe641bba534917288d77412b86db9de20a99fecfdd422ef245f6e2b8cbefc16cc69001726084d4633847d284aed1ab522bea4b2dc4954
7
+ data.tar.gz: c92efd04400e6a059fae22304b6d6f7b7344a79ffa530fd3a1dff08aa58e4690588c0c986bc765c7fb364ff255a09ebb755e150749f8f8cd4a32fa2e4b62158f
data/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ changelog
2
+ =========
3
+
4
+ ### 0.2.0 3 feb 2015
5
+
6
+ thanks: @davy
7
+
8
+ * major refactor to compose from Array vs. descend from it (#5)
data/README.md CHANGED
@@ -25,8 +25,8 @@ require 'terraformer'
25
25
 
26
26
  ##### Create a Terraformer primitive from GeoJSON
27
27
 
28
- ```
29
- > polygon = Terraformer.parse '{
28
+ ```ruby
29
+ polygon = Terraformer.parse '{
30
30
  "type": "Polygon",
31
31
  "coordinates": [
32
32
  [
@@ -41,24 +41,32 @@ require 'terraformer'
41
41
  ]
42
42
  }'
43
43
 
44
- > point = Terraformer.parse '{
44
+ point = Terraformer.parse '{
45
45
  "type": "Point",
46
46
  "coordinates": [-122.66947746276854, 45.51775972687403]
47
47
  }'
48
48
 
49
49
  ```
50
+
50
51
  Now that you have a point and a polygon primitive you can use the primitive helper methods.
51
52
 
52
- ```
53
+ ```ruby
53
54
  # add a new vertex to our polygon
54
- > new_point = Terraformer::Point.new -122.6708507537842, 45.513188859735436
55
- > polygon.insert_vertex 2, new_point
55
+ new_point = Terraformer::Point.new -122.6708507537842, 45.513188859735436
56
+ polygon.insert_vertex 2, new_point
56
57
  ```
58
+
57
59
  You can also have Terraformer perform many geometric operations like convex hulls and bounding boxes.
58
- ```
59
- > convex_hull = polygon.convex_hull
60
- > point.within? convex_hull #returns true
61
- > bounding_box = polygon.bbox #returns the bounding box for this object.
60
+
61
+ ```ruby
62
+ # returns convex hull
63
+ convex_hull = polygon.convex_hull
64
+
65
+ point.within? convex_hull
66
+ => true
67
+
68
+ # returns the bounding box
69
+ bounding_box = polygon.bbox
62
70
  ```
63
71
 
64
72
  ## Contributing
data/Rakefile CHANGED
@@ -1,4 +1,7 @@
1
1
  require 'rake/testtask'
2
+ require 'bundler'
3
+
4
+ Bundler::GemHelper.install_tasks
2
5
 
3
6
  Rake::TestTask.new do |t|
4
7
  t.pattern = ENV['TEST_PATTERN'] || "test/**/*_spec.rb"
data/lib/ext/array.rb CHANGED
@@ -1,5 +1,22 @@
1
1
  class Array
2
2
 
3
+ def rotate_until &block
4
+ return if block[]
5
+ found = false
6
+ length.times do
7
+ push shift
8
+ if block[]
9
+ found = true
10
+ break
11
+ end
12
+ end
13
+ raise IndexError unless found
14
+ end
15
+
16
+ def rotate_until_first_equals obj
17
+ rotate_until { at(0) == obj }
18
+ end
19
+
3
20
  def slice_exists? slice
4
21
  start = slice.first
5
22
  len = slice.size
@@ -98,53 +98,68 @@ module Terraformer
98
98
  end
99
99
  end
100
100
 
101
+ def parse_point arcgis
102
+ Coordinate.new(%w[x y z].map {|k| arcgis[k]}).to_point
103
+ end
104
+
105
+ def parse_points arcgis
106
+ require_array arcgis['points']
107
+ MultiPoint.new arcgis['points']
108
+ end
109
+
110
+ def parse_paths arcgis
111
+ require_array arcgis['paths']
112
+ if arcgis['paths'].length == 1
113
+ LineString.new arcgis['paths'][0]
114
+ else
115
+ MultiLineString.new arcgis['paths']
116
+ end
117
+ end
118
+
119
+ def parse_geometry arcgis, opts
120
+ if arcgis['compressedGeometry']
121
+ arcgis['geometry'] = {'paths' => [decompress_geometry(arcgis['compressedGeometry'])]}
122
+ end
123
+
124
+ o = Feature.new
125
+ o.geometry = parse arcgis['geometry'] if arcgis['geometry']
126
+ if attrs = arcgis['attributes']
127
+ o.properties = attrs.clone
128
+ if opts[:id_attribute] && o.properties[opts[:id_attribute]]
129
+ o.id = o.properties.delete opts[:id_attribute]
130
+ elsif o.properties[OBJECT_ID]
131
+ o.id = o.properties.delete OBJECT_ID
132
+ elsif o.properties['FID']
133
+ o.id = o.properties.delete 'FID'
134
+ end
135
+ end
136
+
137
+ return o
138
+ end
139
+
101
140
  def parse arcgis, opts = {}
102
141
  arcgis = JSON.parse arcgis if String === arcgis
103
142
  raise ArgumentError.new 'argument not hash nor json' unless Hash === arcgis
104
143
 
105
144
  obj = case
106
145
  when Numeric === arcgis['x'] && Numeric === arcgis['y']
107
- Coordinate.new(%w[x y z].map {|k| arcgis[k]}).to_point
146
+ parse_point arcgis
108
147
 
109
148
  when arcgis['points']
110
- require_array arcgis['points']
111
- MultiPoint.new arcgis['points']
149
+ parse_points arcgis
112
150
 
113
151
  when arcgis['paths']
114
- require_array arcgis['paths']
115
- if arcgis['paths'].length == 1
116
- LineString.new arcgis['paths'][0]
117
- else
118
- MultiLineString.new arcgis['paths']
119
- end
152
+ parse_paths arcgis
120
153
 
121
154
  when arcgis['rings']
122
155
  convert_rings arcgis['rings']
123
156
 
124
157
  when !(%w[compressedGeometry geometry attributes].map {|k| arcgis[k]}.empty?)
125
-
126
- if arcgis['compressedGeometry']
127
- arcgis['geometry'] = {'paths' => [decompress_geometry(arcgis['compressedGeometry'])]}
128
- end
129
-
130
- o = Feature.new
131
- o.geometry = parse arcgis['geometry'] if arcgis['geometry']
132
- if attrs = arcgis['attributes']
133
- o.properties = attrs.clone
134
- if opts[:id_attribute] and o.properties[opts[:id_attribute]]
135
- o.id = o.properties.delete opts[:id_attribute]
136
- elsif o.properties[OBJECT_ID]
137
- o.id = o.properties.delete OBJECT_ID
138
- elsif o.properties['FID']
139
- o.id = o.properties.delete 'FID'
140
- end
141
- end
142
- o
143
-
158
+ parse_geometry arcgis, opts
144
159
  end
145
160
 
146
161
  isr = arcgis['geometry'] ? arcgis['geometry']['spatialReference'] : arcgis['spatialReference']
147
- if isr and Integer(isr['wkid']) == 102100
162
+ if isr && Integer(isr['wkid']) == 102100
148
163
  if Feature === obj
149
164
  obj.geometry = obj.geometry.to_geographic
150
165
  else
@@ -7,25 +7,19 @@ module Terraformer
7
7
 
8
8
  obj = Terraformer.parse obj unless Geometry === obj
9
9
 
10
- bbox = case obj.type
11
- when 'Point'
10
+ bbox = case obj
11
+ when Point
12
12
  [ obj.coordinates[0], obj.coordinates[1],
13
13
  obj.coordinates[0], obj.coordinates[1] ]
14
- when 'MultiPoint'
14
+ when MultiPoint, LineString,
15
+ MultiLineString, Polygon,
16
+ MultiPolygon
15
17
  bounds_for_array obj.coordinates
16
- when 'LineString'
17
- bounds_for_array obj.coordinates
18
- when 'MultiLineString'
19
- bounds_for_array obj.coordinates, 1
20
- when 'Polygon'
21
- bounds_for_array obj.coordinates, 1
22
- when 'MultiPolygon'
23
- bounds_for_array obj.coordinates, 2
24
- when 'Feature'
18
+ when Feature
25
19
  obj.geometry ? bounds(obj.geometry) : nil
26
- when 'FeatureCollection'
20
+ when FeatureCollection
27
21
  bounds_for_feature_collection obj
28
- when 'GeometryCollection'
22
+ when GeometryCollection
29
23
  bounds_for_geometry_collection obj
30
24
  else
31
25
  raise ArgumentError.new 'unknown type: ' + obj.type
@@ -45,22 +39,19 @@ module Terraformer
45
39
 
46
40
  X1, Y1, X2, Y2 = 0, 1, 2, 3
47
41
 
48
- def bounds_for_array array, nesting = 0, box = Array.new(4)
49
- if nesting > 0
50
- array.reduce box do |b, a|
51
- bounds_for_array a, (nesting - 1), b
52
- end
53
- else
54
- bbox = array.reduce box do |b, lonlat|
55
- lon, lat = *lonlat
42
+ def bounds_for_array array, box = Array.new(4)
43
+ array.reduce box do |b, a|
44
+ case a
45
+ when Coordinate
56
46
  set = ->(d, i, t){ b[i] = d if b[i].nil? or d.send(t, b[i])}
57
- set[lon, X1, :<]
58
- set[lon, X2, :>]
59
- set[lat, Y1, :<]
60
- set[lat, Y2, :>]
47
+ set[a[0], X1, :<]
48
+ set[a[0], X2, :>]
49
+ set[a[1], Y1, :<]
50
+ set[a[1], Y2, :>]
61
51
  b
52
+ else
53
+ bounds_for_array a, box
62
54
  end
63
- bbox
64
55
  end
65
56
  end
66
57
 
@@ -32,7 +32,9 @@ module Terraformer
32
32
 
33
33
  def flatten_coordinates_from points
34
34
  cs = []
35
- points.each_coordinate {|c| cs << c}
35
+ Geometry.iter_coordinate points, :each do |c|
36
+ cs << c
37
+ end
36
38
  cs.sort!.uniq!
37
39
  cs
38
40
  end
@@ -1,6 +1,6 @@
1
1
  module Terraformer
2
2
 
3
- class Coordinate < ::Array
3
+ class Coordinate
4
4
 
5
5
  # http://en.wikipedia.org/wiki/Earth_radius#Mean_radius
6
6
  #
@@ -8,14 +8,21 @@ module Terraformer
8
8
 
9
9
  attr_accessor :crs
10
10
 
11
- class << self
11
+ # array holding the numeric coordinate values
12
+ attr_accessor :ary
12
13
 
13
- def from arys
14
- arys.map {|e| Coordinate.from_array e}
15
- end
14
+ class << self
16
15
 
17
16
  def from_array a
18
- Coordinate.__send__ Numeric === a[0] ? :new : :from, a
17
+ if Coordinate === a
18
+ a.dup
19
+ elsif Numeric === a[0]
20
+ Coordinate.new a
21
+ elsif Array === a
22
+ a.map {|e| Coordinate.from_array e }
23
+ else
24
+ raise ArgumentError
25
+ end
19
26
  end
20
27
 
21
28
  def big_decimal n
@@ -34,14 +41,21 @@ module Terraformer
34
41
  end
35
42
 
36
43
  def initialize _x, _y = nil, _z = nil
37
- super 3
38
- case
39
- when Array === _x
44
+ @ary = Array.new 3
45
+ case _x
46
+ when Array
40
47
  raise ArgumentError if _y
41
48
  self.x = _x[0]
42
49
  self.y = _x[1]
43
50
  self.z = _x[2] if _x[2]
44
- when Numeric === _x || String === _x
51
+
52
+ when Coordinate
53
+ raise ArgumentError if _y
54
+ self.x = _x.x
55
+ self.y = _x.y
56
+ self.z = _x.z if _x.z
57
+
58
+ when Numeric, String
45
59
  raise ArgumentError unless _y
46
60
  self.x = _x
47
61
  self.y = _y
@@ -51,32 +65,53 @@ module Terraformer
51
65
  end
52
66
  end
53
67
 
68
+ def dup
69
+ Coordinate.new @ary.dup
70
+ end
71
+
54
72
  def x
55
- self[0]
73
+ @ary[0]
56
74
  end
75
+ alias_method :lon, :x
57
76
 
58
77
  def x= _x
59
- self[0] = Coordinate.big_decimal _x
78
+ @ary[0] = Coordinate.big_decimal _x
60
79
  end
61
80
 
62
81
  def y
63
- self[1]
82
+ @ary[1]
64
83
  end
84
+ alias_method :lat, :y
65
85
 
66
86
  def y= _y
67
- self[1] = Coordinate.big_decimal _y
87
+ @ary[1] = Coordinate.big_decimal _y
68
88
  end
69
89
 
70
90
  def z
71
- self[2]
91
+ @ary[2]
72
92
  end
73
93
 
74
94
  def z= _z
75
- self[2] = Coordinate.big_decimal _z
95
+ @ary[2] = Coordinate.big_decimal _z
96
+ end
97
+
98
+ def [] index
99
+ @ary[index]
100
+ end
101
+
102
+ def == other
103
+ case other
104
+ when Array
105
+ @ary == other
106
+ when Coordinate
107
+ @ary == other.ary
108
+ else
109
+ false
110
+ end
76
111
  end
77
112
 
78
- [:<<, :*, :&, :|].each do |sym|
79
- define_method(sym){|*a| raise NotImplementedError }
113
+ def inspect
114
+ "#<Terraformer::Coordinate lon=#{lon.to_s 'F'} lat=#{lat.to_s 'F'} #{z if z }>"
80
115
  end
81
116
 
82
117
  def to_geographic
@@ -101,11 +136,11 @@ module Terraformer
101
136
  end
102
137
 
103
138
  def to_s
104
- [x,y,z].compact.join ','
139
+ @ary.compact.join ','
105
140
  end
106
141
 
107
142
  def to_json *args
108
- compact.to_json(*args)
143
+ @ary.compact.to_json(*args)
109
144
  end
110
145
 
111
146
  def to_point
@@ -6,6 +6,10 @@ module Terraformer
6
6
  module ClassMethods
7
7
 
8
8
  def coordinates_contain_point? coordinates, point
9
+ if Terraformer::Coordinate === coordinates
10
+ return coordinates == point
11
+ end
12
+
9
13
  contains = false
10
14
  i = -1
11
15
  l = coordinates.length
@@ -13,7 +13,7 @@ module Terraformer
13
13
  case
14
14
  when args.length > 1
15
15
  self.coordinates = Coordinate.from_array args
16
- when Array === args[0]
16
+ when Array === args[0] || Coordinate === args[0]
17
17
  self.coordinates = Coordinate.from_array args[0]
18
18
  else
19
19
  super *args do |arg|
@@ -22,6 +22,28 @@ module Terraformer
22
22
  end
23
23
  end
24
24
 
25
+ def each_coordinate &block
26
+ Geometry.iter_coordinate coordinates, :each, &block
27
+ end
28
+
29
+ def map_coordinate &block
30
+ Geometry.iter_coordinate coordinates, :map, &block
31
+ end
32
+
33
+ def self.iter_coordinate obj, meth, &block
34
+ if Terraformer::Coordinate === obj
35
+ block.call obj
36
+ elsif Array === obj
37
+ obj.__send__ meth do |pair|
38
+ if Array === pair
39
+ Geometry.iter_coordinate pair, meth, &block
40
+ else
41
+ block.call pair
42
+ end
43
+ end
44
+ end
45
+ end
46
+
25
47
  def to_hash *args
26
48
  h = {
27
49
  type: type,
@@ -33,11 +55,11 @@ module Terraformer
33
55
  end
34
56
 
35
57
  def to_mercator
36
- self.class.new *coordinates.map_coordinate(&:to_mercator)
58
+ self.class.new *map_coordinate(&:to_mercator)
37
59
  end
38
60
 
39
61
  def to_geographic
40
- self.class.new *coordinates.map_coordinate(&:to_geographic)
62
+ self.class.new *map_coordinate(&:to_geographic)
41
63
  end
42
64
 
43
65
  def to_feature
@@ -85,8 +107,9 @@ module Terraformer
85
107
  raise ArgumentError.new "unsupported type: #{e.type rescue e.class}"
86
108
  end
87
109
  end
88
- return true if begin
89
- within? other or other.within? self
110
+
111
+ begin
112
+ return true if within? other or other.within? self
90
113
  rescue ArgumentError
91
114
  false
92
115
  end
@@ -2,6 +2,16 @@ module Terraformer
2
2
 
3
3
  class LineString < Geometry
4
4
 
5
+ def initialize *args
6
+ super *args
7
+
8
+ # must be an array of coordinates
9
+ unless Array === coordinates &&
10
+ Terraformer::Coordinate === coordinates[0]
11
+ raise ArgumentError.new 'invalid coordinates for Terraformer::LineString'
12
+ end
13
+ end
14
+
5
15
  def first_coordinate
6
16
  coordinates[0]
7
17
  end
@@ -3,16 +3,20 @@ module Terraformer
3
3
  class MultiLineString < Geometry
4
4
 
5
5
  def initialize *args
6
+
6
7
  case
7
- when Coordinate === args[0] # only one
8
- self.coordinates = [Coordinate.from_array(args)]
9
- when Array === args[0] # multiple?
10
- self.coordinates = Coordinate.from args
11
8
  when LineString === args[0]
12
9
  self.coordinates = args.map &:coordinates
13
10
  else
14
11
  super *args
15
12
  end
13
+
14
+ # must be an array of arrays of coordinates
15
+ unless Array === coordinates &&
16
+ Array === coordinates[0] &&
17
+ Terraformer::Coordinate === coordinates[0][0]
18
+ raise ArgumentError.new 'invalid coordinates for Terraformer::MultiLineString'
19
+ end
16
20
  end
17
21
 
18
22
  def first_coordinate
@@ -25,14 +29,9 @@ module Terraformer
25
29
 
26
30
  def == obj
27
31
  super obj do |o|
28
- equal = true
29
- lses = line_strings.sort
30
- olses = o.line_strings.sort
31
- lses.each_with_index do |ls, i|
32
- equal = ls == olses[i]
33
- break unless equal
34
- end
35
- equal
32
+ lses = line_strings.sort {|a,b| a.first_coordinate <=> b.first_coordinate }
33
+ olses = o.line_strings.sort {|a,b| a.first_coordinate <=> b.first_coordinate }
34
+ lses == olses
36
35
  end
37
36
  end
38
37
 
@@ -9,6 +9,12 @@ module Terraformer
9
9
  else
10
10
  super *args
11
11
  end
12
+
13
+ # must be an array of coordinates
14
+ unless Array === coordinates &&
15
+ Terraformer::Coordinate === coordinates[0]
16
+ raise ArgumentError.new 'invalid coordinates for Terraformer::MultiPoint'
17
+ end
12
18
  end
13
19
 
14
20
  def first_coordinate
@@ -7,18 +7,28 @@ module Terraformer
7
7
 
8
8
  # arg is an array of arrays of polygon, holes
9
9
  when Array === args[0] && Array === args[0][0] && Array === args[0][0][0] && Array === args[0][0][0][0]
10
- self.coordinates = Coordinate.from(*args)
10
+ self.coordinates = Coordinate.from_array(*args)
11
11
 
12
12
  when Coordinate === args[0] # only one
13
13
  self.coordinates = [[Coordinate.from_array(args)]]
14
+
14
15
  when Array === args[0] # multiple?
15
- self.coordinates = [Coordinate.from(args)]
16
+ self.coordinates = [Coordinate.from_array(args)]
17
+
16
18
  when Polygon === args[0]
17
19
  self.coordinates = args.map &:coordinates
18
20
 
19
21
  else
20
22
  super *args
21
23
  end
24
+
25
+ # must be an array of arrays of arrays of coordinates (whew!)
26
+ unless Array === coordinates &&
27
+ Array === coordinates[0] &&
28
+ Array === coordinates[0][0] &&
29
+ Terraformer::Coordinate === coordinates[0][0][0]
30
+ raise ArgumentError.new 'invalid coordinates for Terraformer::MultiPolygon'
31
+ end
22
32
  end
23
33
 
24
34
  def first_coordinate
@@ -2,6 +2,15 @@ module Terraformer
2
2
 
3
3
  class Point < Geometry
4
4
 
5
+ def initialize *args
6
+ super
7
+
8
+ # must be a single point
9
+ unless Terraformer::Coordinate === coordinates
10
+ raise ArgumentError.new 'invalid coordinates for Terraformer::Point'
11
+ end
12
+ end
13
+
5
14
  def first_coordinate
6
15
  coordinates
7
16
  end
@@ -3,24 +3,34 @@ module Terraformer
3
3
  class Polygon < Geometry
4
4
 
5
5
  def initialize *args
6
- case
7
6
 
8
7
  # each arg is a position of the polygon
9
- when Coordinate === args[0] || (Array === args[0] && Numeric === args[0][0])
8
+ if Coordinate === args[0] || (Array === args[0] && Numeric === args[0][0])
10
9
  self.coordinates = [Coordinate.from_array(args)]
11
10
 
12
- # each arg is an array of positions; first is polygon, rest are "holes"
13
- when Array === args[0] && Array === args[0][0] && Numeric === args[0][0][0]
14
- self.coordinates = Coordinate.from args
11
+ elsif Array === args[0]
15
12
 
16
- # arg is an array of polygon, holes
17
- when Array === args[0] && Array === args[0][0] && Array === args[0][0][0]
18
- self.coordinates = Coordinate.from *args
13
+ # each arg is an array of positions; first is polygon, rest are "holes"
14
+ if Coordinate === args[0][0] ||
15
+ Array === args[0][0] && Numeric === args[0][0][0]
16
+ self.coordinates = Coordinate.from_array args
17
+
18
+ # arg is an array of polygon, holes
19
+ elsif Array === args[0][0] && Array === args[0][0][0]
20
+ self.coordinates = Coordinate.from_array *args
21
+ end
19
22
 
20
23
  else
21
24
  super *args
22
25
  end
23
26
 
27
+ # must be an array of arrays of coordinates
28
+ unless Array === coordinates &&
29
+ Array === coordinates[0] &&
30
+ Terraformer::Coordinate === coordinates[0][0]
31
+ raise ArgumentError.new 'invalid coordinates for Terraformer::Polygon'
32
+ end
33
+
24
34
  if line_strings.map(&:linear_ring?).include? false
25
35
  raise ArgumentError.new 'not linear ring'
26
36
  end
@@ -40,7 +50,7 @@ module Terraformer
40
50
  equal = true
41
51
 
42
52
  # first check outer polygon
43
- equal = self.coordinates[0].polygonally_equal_to? obj.coordinates[0]
53
+ equal = Polygon.polygonally_equal? self.coordinates[0], obj.coordinates[0]
44
54
 
45
55
  # then inner polygons (holes)
46
56
  #
@@ -51,7 +61,7 @@ module Terraformer
51
61
  obj_holes = obj.coordinates[1..-1].sort
52
62
 
53
63
  self_holes.each_with_index do |hole, idx|
54
- equal = hole.polygonally_equal_to? obj_holes[idx]
64
+ equal = Polygon.polygonally_equal? hole, obj_holes[idx]
55
65
  break unless equal
56
66
  end
57
67
  end
@@ -130,6 +140,42 @@ module Terraformer
130
140
  coordinates[0].delete_at idx
131
141
  end
132
142
 
143
+
144
+ def self.polygonally_equal? a, b
145
+ if Terraformer::Coordinate === a ||
146
+ Terraformer::Coordinate === b
147
+ return a == b
148
+ end
149
+
150
+ raise ArgumentError unless Enumerable === a
151
+ raise ArgumentError unless Enumerable === b
152
+
153
+ # polygons must be the same length
154
+ return false if a.length != b.length
155
+
156
+ # polygons must have the last element be a duplicate
157
+ # polygon-closing coordinate
158
+ return false if a[0] != a[-1]
159
+ return false if b[0] != b[-1]
160
+
161
+ equal = true
162
+
163
+ # clone so can pop/rotate
164
+ a = a.clone
165
+ b = b.clone
166
+
167
+ # pop to drop the duplicate, polygon-closing, coordinate
168
+ a.pop
169
+ b.pop
170
+
171
+ begin
172
+ b.rotate_until_first_equals a[0]
173
+ return a == b
174
+ rescue IndexError
175
+ return false
176
+ end
177
+ end
178
+
133
179
  end
134
180
 
135
181
  end
@@ -1,3 +1,3 @@
1
1
  module Terraformer
2
- VERSION = '0.1.0'
2
+ VERSION = '0.2.0'
3
3
  end
data/lib/terraformer.rb CHANGED
@@ -5,7 +5,6 @@ require 'bigdecimal/util'
5
5
  require 'ext/array'
6
6
  require 'ext/big_decimal'
7
7
  require 'ext/big_math'
8
- require 'ext/enumerable'
9
8
  require 'forwardable'
10
9
 
11
10
  # terraformer.rb - a toolkit for working with geojson in ruby
@@ -73,7 +72,9 @@ module Terraformer
73
72
  # handles basic JSON parsing for terraformer object constructors.
74
73
  #
75
74
  def initialize *args
76
- arg = String === args[0] ? JSON.parse(args[0]) : args[0]
75
+ arg = args[0]
76
+ arg = JSON.parse(arg) if String === arg
77
+
77
78
  raise ArgumentError.new "invalid argument(s): #{args}" unless Hash === arg
78
79
  raise ArgumentError.new "invalid type: #{arg['type']}" unless arg['type'] == self.type
79
80
  yield arg if block_given?
data/terraformer.gemspec CHANGED
@@ -2,10 +2,10 @@
2
2
  require File.expand_path('../lib/terraformer/version', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |gem|
5
- gem.authors = ["Kenichi Nakamura"]
6
- gem.email = ["kenichi.nakamura@gmail.com"]
7
- gem.description = gem.summary = ""
8
- gem.homepage = "https://github.com/esripdx/terraformer-ruby"
5
+ gem.authors = ["Kenichi Nakamura", "Davy Stevenson"]
6
+ gem.email = ["kenichi.nakamura@gmail.com", "davy.stevenson@gmail.com"]
7
+ gem.description = gem.summary = "toolkit for working with GeoJSON in pure Ruby"
8
+ gem.homepage = "https://github.com/kenichi/terraformer-ruby"
9
9
  gem.files = `git ls-files | grep -Ev '^(myapp|examples)'`.split("\n")
10
10
  gem.test_files = `git ls-files -- test/*`.split("\n")
11
11
  gem.name = "terraformer"
@@ -2,35 +2,6 @@ require_relative './helper'
2
2
 
3
3
  describe Terraformer::Coordinate do
4
4
 
5
- describe 'from' do
6
- it 'should accept nested arrays' do
7
- arys = [[100,0], [101,1], [102,2]]
8
- c = Terraformer::Coordinate.from arys
9
-
10
- c.class.must_equal Array
11
- c[0].class.must_equal Terraformer::Coordinate
12
- c[0].x.must_equal 100
13
- c[0].y.must_equal 0
14
- c[1].class.must_equal Terraformer::Coordinate
15
- c[1].x.must_equal 101
16
- c[1].y.must_equal 1
17
- c[2].class.must_equal Terraformer::Coordinate
18
- c[2].x.must_equal 102
19
- c[2].y.must_equal 2
20
- end
21
-
22
- it 'should accept double nested arrays' do
23
- arys = [[[100,0], [101,1]], [[102,2], [103,3]]]
24
- c = Terraformer::Coordinate.from arys
25
-
26
- c.class.must_equal Array
27
- c[0].class.must_equal Array
28
- c[0][0].class.must_equal Terraformer::Coordinate
29
- c[0][0].x.must_equal 100
30
- c[0][0].y.must_equal 0
31
- end
32
- end
33
-
34
5
  describe 'from_array' do
35
6
 
36
7
  it 'should be called from Array.to_c' do
@@ -8,15 +8,13 @@ describe Terraformer::MultiLineString do
8
8
  a = Terraformer::Coordinate.new -122.6764, 45.5165
9
9
  b = a + [0.02, 0.02]
10
10
  c = b + [0.1, -0.1]
11
- mls = Terraformer::MultiLineString.new a, b, c
11
+ mls = Terraformer::MultiLineString.new [[a, b, c]]
12
12
  mls.to_json.must_equal '{"type":"MultiLineString","coordinates":[[[-122.6764,45.5165],[-122.6564,45.5365],[-122.5564,45.4365]]]}'
13
13
  mls.must_be_valid_geojson
14
14
  end
15
15
 
16
16
  it 'constructs from array - single line' do
17
- ## unlike point, multipoint and linestring, passing in the coordinates array directly into multilinestring doesn't work
18
- # mls = Terraformer::MultiLineString.new [[[-122.6764,45.5165],[-122.6564,45.5365],[-122.5564,45.4365]]]
19
- mls = Terraformer::MultiLineString.new [[-122.6764,45.5165],[-122.6564,45.5365],[-122.5564,45.4365]]
17
+ mls = Terraformer::MultiLineString.new [[[-122.6764,45.5165],[-122.6564,45.5365],[-122.5564,45.4365]]]
20
18
  mls.to_json.must_equal '{"type":"MultiLineString","coordinates":[[[-122.6764,45.5165],[-122.6564,45.5365],[-122.5564,45.4365]]]}'
21
19
  mls.must_be_valid_geojson
22
20
  end
@@ -47,9 +45,7 @@ describe Terraformer::MultiLineString do
47
45
  end
48
46
 
49
47
  it 'constructs from array - multiple lines' do
50
- ## unlike point, multipoint and linestring, passing in the coordinates array directly into multilinestring doesn't work
51
- # mls = Terraformer::MultiLineString.new [[[-122.6764,45.5165],[-122.6564,45.5365],[-122.5564,45.4365]],[[-121.5564,46.4365],[-121.5364,46.4565],[-121.4364,46.3565]]]
52
- mls = Terraformer::MultiLineString.new [[-122.6764,45.5165],[-122.6564,45.5365],[-122.5564,45.4365]],[[-121.5564,46.4365],[-121.5364,46.4565],[-121.4364,46.3565]]
48
+ mls = Terraformer::MultiLineString.new [[[-122.6764,45.5165],[-122.6564,45.5365],[-122.5564,45.4365]],[[-121.5564,46.4365],[-121.5364,46.4565],[-121.4364,46.3565]]]
53
49
  mls.to_json.must_equal '{"type":"MultiLineString","coordinates":[[[-122.6764,45.5165],[-122.6564,45.5365],[-122.5564,45.4365]],[[-121.5564,46.4365],[-121.5364,46.4565],[-121.4364,46.3565]]]}'
54
50
  mls.must_be_valid_geojson
55
51
  end
@@ -163,7 +163,7 @@ describe Terraformer do
163
163
  a = d.split
164
164
  a[1][a[3]..-1].length
165
165
  }
166
- c.coordinates.each_coordinate do |c|
166
+ c.each_coordinate do |c|
167
167
  splitter[c.x].must_be :<=, Terraformer::PRECISION
168
168
  splitter[c.x].must_be :<=, Terraformer::PRECISION
169
169
  end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: terraformer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenichi Nakamura
8
+ - Davy Stevenson
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2015-02-02 00:00:00.000000000 Z
12
+ date: 2015-02-03 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: launchy
@@ -24,21 +25,22 @@ dependencies:
24
25
  - - "~>"
25
26
  - !ruby/object:Gem::Version
26
27
  version: '2.4'
27
- description: ''
28
+ description: toolkit for working with GeoJSON in pure Ruby
28
29
  email:
29
30
  - kenichi.nakamura@gmail.com
31
+ - davy.stevenson@gmail.com
30
32
  executables: []
31
33
  extensions: []
32
34
  extra_rdoc_files: []
33
35
  files:
34
36
  - ".gitignore"
37
+ - CHANGELOG.md
35
38
  - Gemfile
36
39
  - README.md
37
40
  - Rakefile
38
41
  - lib/ext/array.rb
39
42
  - lib/ext/big_decimal.rb
40
43
  - lib/ext/big_math.rb
41
- - lib/ext/enumerable.rb
42
44
  - lib/terraformer.rb
43
45
  - lib/terraformer/arcgis.rb
44
46
  - lib/terraformer/bounds.rb
@@ -82,7 +84,7 @@ files:
82
84
  - test/polygon_spec.rb
83
85
  - test/primitive_spec.rb
84
86
  - test/terraformer_spec.rb
85
- homepage: https://github.com/esripdx/terraformer-ruby
87
+ homepage: https://github.com/kenichi/terraformer-ruby
86
88
  licenses:
87
89
  - apache
88
90
  metadata: {}
@@ -105,7 +107,7 @@ rubyforge_project:
105
107
  rubygems_version: 2.4.5
106
108
  signing_key:
107
109
  specification_version: 4
108
- summary: ''
110
+ summary: toolkit for working with GeoJSON in pure Ruby
109
111
  test_files:
110
112
  - test/arcgis_spec.rb
111
113
  - test/circle_spec.rb
@@ -1,79 +0,0 @@
1
- module Enumerable
2
-
3
- def each_coordinate opts = {}, &block
4
- iter_coordinate :each, opts, &block
5
- end
6
-
7
- def map_coordinate opts = {}, &block
8
- iter_coordinate :map, opts, &block
9
- end
10
- alias_method :collect_coordinate, :map_coordinate
11
-
12
- def map_coordinate! opts = {}, &block
13
- iter_coordinate :map!, opts, &block
14
- end
15
- alias_method :collect_coordinate!, :map_coordinate!
16
-
17
- def iter_coordinate meth, opts = {}, &block
18
- opts[:recurse] = true if opts[:recurse].nil?
19
-
20
- if Array === self and Numeric === self[0]
21
- yield self
22
- else
23
-
24
- self.__send__ meth do |pair|
25
- raise IndexError unless Array === pair
26
- case pair[0]
27
- when Numeric
28
- yield pair
29
- when Array
30
- pair.iter_coordinate meth, opts, &block if opts[:recurse]
31
- else
32
- raise IndexError.new "#{pair[0]} is not a Numeric or Array type"
33
- end
34
- end
35
- end
36
- end
37
-
38
- def rotate_until &block
39
- return if block[]
40
- found = false
41
- length.times do
42
- push shift
43
- if block[]
44
- found = true
45
- break
46
- end
47
- end
48
- raise IndexError unless found
49
- end
50
-
51
- def rotate_until_first_equals obj
52
- rotate_until { at(0) == obj }
53
- end
54
-
55
- def polygonally_equal_to? obj
56
- raise ArgumentError unless Enumerable === obj
57
- return false if self.length != obj.length
58
-
59
- equal = true
60
-
61
- # clone so can pop/rotate
62
- me = self.clone
63
- obj = obj.clone
64
-
65
- # pop to drop the duplicate, polygon-closing, coordinate
66
- me.pop
67
- obj.pop
68
-
69
- begin
70
- obj.rotate_until_first_equals me[0]
71
- equal = me == obj
72
- rescue IndexError
73
- equal = false
74
- end
75
-
76
- equal
77
- end
78
-
79
- end