42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
# File 'lib/nswtopo/gis/arcgis_server.rb', line 42
def arcgis_layer(url, where: nil, layer: nil, per_page: nil, margin: {})
ArcGISServer.start url do |connection, service, projection, id|
id = service["layers"].find do |info|
layer.to_s == info["name"]
end&.dig("id") if layer
id ? nil : layer ? raise("no such ArcGIS layer: %s" % layer) : raise("not an ArcGIS layer url: %s" % url)
layer = connection.get_json id.to_s
query_path = "#{id}/query"
max_record_count, fields, types, type_id_field, geometry_type, capabilities = layer.values_at "maxRecordCount", "fields", "types", "typeIdField", "geometryType", "capabilities"
raise Error, "no query capability available: #{url}" unless capabilities =~ /Query|Data/
if type_id_field && types
type_id_field = fields.find do |field|
field.values_at("alias", "name").include? type_id_field
end&.fetch("name")
type_values = types.map do |type|
type.values_at "id", "name"
end.to_h
subtype_coded_values = types.map do |type|
type.values_at "id", "domains"
end.map do |id, domains|
coded_values = domains.map do |name, domain|
[name, domain["codedValues"]]
end.select(&:last).map do |name, pairs|
values = pairs.map do |pair|
pair.values_at "code", "name"
end.to_h
[name, values]
end.to_h
[id, coded_values]
end.to_h
end
coded_values = fields.map do |field|
[field["name"], field.dig("domain", "codedValues")]
end.select(&:last).map do |name, pairs|
values = pairs.map do |pair|
pair.values_at "code", "name"
end.to_h
[name, values]
end.to_h
geometry = { rings: @map.bounding_box(margin).reproject_to(projection).coordinates.map(&:reverse) }.to_json
where = Array(where).map { |clause| "(#{clause})"}.join " AND "
query = { geometry: geometry, geometryType: "esriGeometryPolygon", returnIdsOnly: true, where: where }
object_ids = connection.get_json(query_path, query)["objectIds"]
next GeoJSON::Collection.new projection unless object_ids
features = Enumerator.new do |yielder|
per_page, total = [*per_page, *max_record_count, 500].min, object_ids.length
while object_ids.any?
yield total - object_ids.length, total if block_given? && total > 0
yielder << begin
connection.get_json query_path, outFields: ?*, objectIds: object_ids.take(per_page).join(?,)
rescue Error => error
(per_page /= 2) > 0 ? retry : raise(error)
end
object_ids.shift per_page
end
end.inject [] do |features, page|
features += page["features"]
end.map do |feature|
next unless geometry = feature["geometry"]
attributes = feature.fetch "attributes", {}
values = attributes.map do |name, value|
case
when type_id_field == name
type_values[value]
when decode = subtype_coded_values&.dig(attributes[type_id_field], name)
decode[value]
when decode = coded_values.dig(name)
decode[value]
when %w[null Null NULL <null> <Null> <NULL>].include?(value)
nil
else value
end
end
attributes = attributes.keys.zip(values).to_h
case geometry_type
when "esriGeometryPoint"
point = geometry.values_at "x", "y"
next unless point.all?
next GeoJSON::Point.new point, attributes
when "esriGeometryMultipoint"
points = geometry["points"]
next unless points&.any?
next GeoJSON::MultiPoint.new points.transpose.take(2).transpose, attributes
when "esriGeometryPolyline"
raise Error, "ArcGIS curve geometries not supported" if geometry.key? "curvePaths"
paths = geometry["paths"]
next unless paths&.any?
next GeoJSON::LineString.new paths[0], attributes if paths.one?
next GeoJSON::MultiLineString.new paths, attributes
when "esriGeometryPolygon"
raise Error, "ArcGIS curve geometries not supported" if geometry.key? "curveRings"
rings = geometry["rings"]
next unless rings&.any?
rings.each(&:reverse!) unless rings[0].anticlockwise?
next GeoJSON::Polygon.new rings, attributes if rings.one?
next GeoJSON::MultiPolygon.new rings.slice_before(&:anticlockwise?).to_a, attributes
else
raise Error, "unsupported ArcGIS geometry type: #{geometry_type}"
end
end.compact
GeoJSON::Collection.new projection, features
end
end
|