def initialize(service, id: nil, layer: nil, where: nil, fields: nil, launder: nil, truncate: nil, decode: nil, mixed: true, geometry: nil, unique: nil)
def initialize(service, id: nil, layer: nil, where: nil, fields: nil, launder: nil, truncate: nil, decode: nil, mixed: true, geometry: nil, unique: nil)
raise NoLayerError, "no ArcGIS layer name or url provided" unless layer || id
@id, @name = service["layers"].find do |info|
layer ? String(layer) == info["name"] : Integer(id) == info["id"]
end&.values_at("id", "name")
raise "ArcGIS layer does not exist: #{layer || id}" unless @id
@service, @where, @decode, @mixed, @geometry, @unique = service, where, decode, mixed, geometry, unique
@layer = get_json @id
raise "ArcGIS layer is not a feature layer: #{@name}" unless @layer["type"] == "Feature Layer"
@geometry_type = @layer["geometryType"]
date_fields = @layer["fields"].select do |field|
"esriFieldTypeDate" == field["type"]
end.map do |field|
field["name"]
end.to_set
@fields = fields&.map do |name|
@layer["fields"].find(-> { raise "invalid field name: #{name}" }) do |field|
field.values_at("alias", "name").include? name
end.fetch("name")
end
[[%w[typeIdField], %w[subtypeField subtypeFieldName]], %w[types subtypes], %w[id code]].transpose.map do |name_keys, lookup_key, value_key|
next @layer.values_at(*name_keys).compact.reject(&:empty?).first, @layer[lookup_key], value_key
end.find do |name_or_alias, lookup, value_key|
name_or_alias && lookup&.any?
end&.tap do |name_or_alias, lookup, value_key|
@type_field = @layer["fields"].find do |field|
field.values_at("alias", "name").compact.include? name_or_alias
end&.fetch("name")
@type_values = lookup.map do |type|
type.values_at value_key, "name"
end.to_h
@subtype_values = lookup.map do |type|
type.values_at value_key, "domains"
end.map do |code, 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
[code, coded_values]
end.to_h
@subtype_fields = @subtype_values.values.flat_map(&:keys).uniq
end
@coded_values = @layer["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
@rename = @layer["fields"].map do |field|
field["name"]
end.map do |name|
next name, launder ? name.downcase.gsub(/[^\w]+/, ?_) : name
end.map do |name, substitute|
next name, truncate ? substitute.slice(0...truncate) : substitute
end.sort_by do |name, substitute|
[@fields&.include?(name) ? 0 : 1, substitute == name ? 0 : 1]
end.inject(Hash[]) do |lookup, (name, substitute)|
suffix, index, candidate = "_2", 3, substitute
while lookup.key? candidate
suffix, index, candidate = "_#{index}", index + 1, (truncate ? substitute.slice(0, truncate - suffix.length) : substitute) + suffix
raise "can't individualise field name: #{name}" if truncate && suffix.length >= truncate
end
lookup.merge candidate => name
end.invert.to_proc
@revalue = lambda do |name, value, properties|
case
when %w[null Null NULL <null> <Null> <NULL>].include?(value)
nil
when value.nil?
nil
when date_fields === name
Time.at(value / 1000).utc.iso8601
when !decode
value
when @type_field == name
@type_values[value]
when lookup = @subtype_values&.dig(properties[@type_field], name)
lookup[value]
when lookup = @coded_values.dig(name)
lookup[value]
else value
end
end
case @layer["capabilities"]
when /Query/ then extend Query, @layer["supportsStatistics"] ? Statistics : Renderer
when /Map/ then extend Map, Renderer
else raise "ArcGIS layer does not include Query or Map capability: #{@name}"
end
end