Module: Terraformer::ArcGIS

Defined in:
lib/terraformer/arcgis.rb

Constant Summary collapse

COMPRESSED_REGEX =
/((\+|\-)[^\+\-]+)/
OBJECT_ID =
'OBJECTID'

Class Method Summary collapse

Class Method Details

.clockwise?(ring) ⇒ Boolean

Returns:

  • (Boolean)


37
38
39
40
41
42
43
44
45
46
47
# File 'lib/terraformer/arcgis.rb', line 37

def clockwise? ring
  require_array ring
  total, i = 0, 0
  r_lim = ring.length - 1
  ring.each_cons(2) do |a,b|
    total += (b[0] - a[0]) * (b[1] + a[1])
    i += 1
    break if i == r_lim
  end
  total >= 0
end

.close_ring(cs) ⇒ Object



31
32
33
34
35
# File 'lib/terraformer/arcgis.rb', line 31

def close_ring cs
  require_array cs
  cs << cs.first if cs.first != cs.last
  cs
end

.convert(geojson, opts = {}) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/terraformer/arcgis.rb', line 173

def convert geojson, opts = {}
  geojson = Terraformer.parse geojson unless Primitive === geojson
  opts[:id_attribute] ||= OBJECT_ID

  sr = {wkid: opts[:sr] || 4326}
  geojson_crs = GeometryCollection === geojson ? geojson.geometries.first.crs : geojson.crs
  sr[:wkid] = 102100 if geojson_crs == Terraformer::MERCATOR_CRS

  arcgis = {}

  case geojson
  when Point
    fc = geojson.first_coordinate
    arcgis[:x] = fc.x
    arcgis[:y] = fc.y
    arcgis[:z] = fc.z if fc.z
    arcgis[:spatialReference] = sr

  when MultiPoint
    arcgis[:points] = geojson.coordinates.clone
    arcgis[:spatialReference] = sr

  when LineString
    arcgis[:paths] = [geojson.coordinates.clone]
    arcgis[:spatialReference] = sr

  when MultiLineString
    arcgis[:paths] = geojson.coordinates.clone
    arcgis[:spatialReference] = sr

  when Polygon
    arcgis[:rings] = orient_rings geojson.coordinates.clone
    arcgis[:spatialReference] = sr

  when MultiPolygon
    arcgis[:rings] = flatten_multi_polygon_rings geojson.coordinates.clone
    arcgis[:spatialReference] = sr

  when Feature
    arcgis[:geometry] = convert(geojson.geometry, opts) if geojson.geometry
    arcgis[:attributes] = geojson.properties ? geojson.properties.clone : {}
    arcgis[:attributes][opts[:id_attribute]] = geojson.id if geojson.id

  when FeatureCollection
    arcgis = geojson.features.map {|f| convert f, opts}

  when GeometryCollection
    arcgis = geojson.geometries.map {|f| convert f, opts}

  end

  arcgis
end

.convert_rings(rings) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/terraformer/arcgis.rb', line 67

def convert_rings rings
  require_array rings
  outer_rings, holes = [], []

  rings.each do |ring|
    ring = close_ring ring
    next if ring.length < 4
    if clockwise? ring
      outer_rings << [ring]
    else
      holes << ring
    end
  end

  holes.each do |hole|
    matched = false
    outer_rings.each do |oring|
      if Polygon.new(oring[0]).contains? Polygon.new(hole)
        oring << hole
        matched = true
        break
      end
    end
    outer_rings << [hole.reverse] unless matched
  end

  if outer_rings.length == 1
    Polygon.new outer_rings.first
  else
    polygons = outer_rings.map {|r| Polygon.new r}
    MultiPolygon.new *polygons
  end
end

.decompress_geometry(str) ⇒ Object



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/terraformer/arcgis.rb', line 14

def decompress_geometry str
  raise ArgumentError.new 'argument is not a String' unless String === str
  x_diff_prev, y_diff_prev = 0, 0
  points = []
  x,y = nil,nil
  strings = str.scan(COMPRESSED_REGEX).map {|m| m[0]}
  coefficient = Integer(strings.shift, 32).to_f
  strings.each_slice(2) do |m,n|
    x = Integer(m, 32) + x_diff_prev
    x_diff_prev = x
    y = Integer(n, 32) + y_diff_prev
    y_diff_prev = y
    points << [x/coefficient, y/coefficient]
  end
  points
end

.flatten_multi_polygon_rings(rings) ⇒ Object



227
228
229
230
231
232
233
234
235
236
# File 'lib/terraformer/arcgis.rb', line 227

def flatten_multi_polygon_rings rings
  out = []
  rings.each do |r|
    polygon = orient_rings r
    polygon.reverse.each do |p|
      out << p.dup
    end
  end
  out
end

.orient_rings(polygon) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/terraformer/arcgis.rb', line 49

def orient_rings polygon
  require_array polygon
  oriented = []
  outer_ring = close_ring polygon.shift
  if outer_ring.length >= 4
    outer_ring.reverse! unless clockwise? outer_ring
    oriented << outer_ring
    polygon.each do |hole|
      hole = close_ring hole
      if hole.length >= 4
        hole.reverse if clockwise? hole
      end
      oriented << hole
    end
  end
  oriented
end

.parse(arcgis, opts = {}) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/terraformer/arcgis.rb', line 140

def parse arcgis, opts = {}
  arcgis = JSON.parse arcgis if String === arcgis
  raise ArgumentError.new 'argument not hash nor json' unless Hash === arcgis

  obj = case
        when Numeric === arcgis['x'] && Numeric === arcgis['y']
          parse_point arcgis

        when arcgis['points']
          parse_points arcgis

        when arcgis['paths']
          parse_paths arcgis

        when arcgis['rings']
          convert_rings arcgis['rings']

        when !(%w[compressedGeometry geometry attributes].map {|k| arcgis[k]}.empty?)
          parse_geometry arcgis, opts
        end

  isr = arcgis['geometry'] ? arcgis['geometry']['spatialReference'] : arcgis['spatialReference']
  if isr && Integer(isr['wkid']) == 102100
    if Feature === obj
      obj.geometry = obj.geometry.to_geographic
    else
      obj = obj.to_geographic
    end
  end

  obj
end

.parse_geometry(arcgis, opts) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/terraformer/arcgis.rb', line 119

def parse_geometry arcgis, opts
  if arcgis['compressedGeometry']
    arcgis['geometry'] = {'paths' => [decompress_geometry(arcgis['compressedGeometry'])]}
  end

  o = Feature.new
  o.geometry = parse arcgis['geometry'] if arcgis['geometry']
  if attrs = arcgis['attributes']
    o.properties = attrs.clone
    if opts[:id_attribute] && o.properties[opts[:id_attribute]]
      o.id = o.properties.delete opts[:id_attribute]
    elsif o.properties[OBJECT_ID]
      o.id = o.properties.delete OBJECT_ID
    elsif o.properties['FID']
      o.id = o.properties.delete 'FID'
    end
  end

  return o
end

.parse_paths(arcgis) ⇒ Object



110
111
112
113
114
115
116
117
# File 'lib/terraformer/arcgis.rb', line 110

def parse_paths arcgis
  require_array arcgis['paths']
  if arcgis['paths'].length == 1
    LineString.new arcgis['paths'][0]
  else
    MultiLineString.new arcgis['paths']
  end
end

.parse_point(arcgis) ⇒ Object



101
102
103
# File 'lib/terraformer/arcgis.rb', line 101

def parse_point arcgis
  Coordinate.new(%w[x y z].map {|k| arcgis[k]}).to_point
end

.parse_points(arcgis) ⇒ Object



105
106
107
108
# File 'lib/terraformer/arcgis.rb', line 105

def parse_points arcgis
  require_array arcgis['points']
  MultiPoint.new arcgis['points']
end

.require_array(a) ⇒ Object



9
10
11
# File 'lib/terraformer/arcgis.rb', line 9

def require_array a
  raise ArgumentError.new 'argument is not an Array' unless Array === a
end