terraformer 0.1.0 → 0.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 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