Class: NSWTopo::ArcGIS::Layer
- Inherits:
-
Object
- Object
- NSWTopo::ArcGIS::Layer
- Extended by:
- Forwardable
- Defined in:
- lib/nswtopo/gis/arcgis/layer.rb
Constant Summary collapse
- FIELD_TYPES =
%W[esriFieldTypeOID esriFieldTypeInteger esriFieldTypeSmallInteger esriFieldTypeDouble esriFieldTypeSingle esriFieldTypeString esriFieldTypeGUID esriFieldTypeDate].to_set
- NoLayerError =
Class.new RuntimeError
Instance Attribute Summary collapse
-
#count ⇒ Object
readonly
Returns the value of attribute count.
Instance Method Summary collapse
- #codes ⇒ Object
- #counts ⇒ Object
- #decode(attributes) ⇒ Object
- #extra_field ⇒ Object
- #features(**options, &block) ⇒ Object
- #info ⇒ Object
-
#initialize(service, id: nil, layer: nil, where: nil, fields: nil, launder: nil, truncate: nil, decode: nil, mixed: true, geometry: nil, unique: nil) ⇒ Layer
constructor
A new instance of Layer.
- #join_clauses(*clauses) ⇒ Object
- #paged(per_page: nil) ⇒ Object
Constructor Details
#initialize(service, id: nil, layer: nil, where: nil, fields: nil, launder: nil, truncate: nil, decode: nil, mixed: true, geometry: nil, unique: nil) ⇒ Layer
Returns a new instance of Layer.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 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 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 12 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 |
Instance Attribute Details
#count ⇒ Object (readonly)
Returns the value of attribute count.
123 124 125 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 123 def count @count end |
Instance Method Details
#codes ⇒ Object
160 161 162 163 164 165 166 167 168 169 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 160 def codes pairs = lambda do |hash| hash.keys.zip(hash.values.map(&:sort).map(&:zip)).to_h end @coded_values.then(&pairs).tap do |result| next unless @type_field codes, lookups = @subtype_values.sort.transpose result[@type_field] = @type_values.slice(*codes).zip lookups.map(&pairs) end end |
#counts ⇒ Object
171 172 173 174 175 176 177 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 171 def counts classify(*@fields, *extra_field).group_by do |attributes, count| decode attributes end.map do |attributes, attributes_counts| [attributes, attributes_counts.sum(&:last)] end end |
#decode(attributes) ⇒ Object
133 134 135 136 137 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 133 def decode(attributes) attributes.map do |name, value| [name, @revalue[name, value, attributes]] end.to_h.slice(*@fields) end |
#extra_field ⇒ Object
125 126 127 128 129 130 131 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 125 def extra_field case when !@decode || !@type_field || !@fields when @fields.include?(@type_field) when (@subtype_fields & @fields).any? then @type_field end end |
#features(**options, &block) ⇒ Object
149 150 151 152 153 154 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 149 def features(**, &block) paged(**).inject do |collection, page| yield collection.count, self.count if block_given? collection.merge! page end end |
#info ⇒ Object
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 179 def info @layer.slice("name", "id").tap do |info| info["geometry"] = case @geometry_type when "esriGeometryPoint" then "Point" when "esriGeometryMultipoint" then "Multipoint" when "esriGeometryPolyline" then "LineString" when "esriGeometryPolygon" then "Polygon" else @geometry_type.delete_prefix("esriGeometry") end info["EPSG"] = @service["spatialReference"].values_at("latestWkid", "wkid").compact.first info["features"] = count info["fields"] = @layer["fields"].map do |field| [field["name"], field["type"].delete_prefix("esriFieldType")] end.sort_by(&:first).to_h if @layer["fields"]&.any? end.compact end |
#join_clauses(*clauses) ⇒ Object
156 157 158 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 156 def join_clauses(*clauses) "(" << clauses.join(") AND (") << ")" if clauses.any? end |
#paged(per_page: nil) ⇒ Object
139 140 141 142 143 144 145 146 147 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 139 def paged(per_page: nil) per_page = [*per_page, *@layer["maxRecordCount"], 500].min Enumerator::Lazy.new pages(per_page) do |yielder, page| page.map! do |feature| decoded = decode(feature.properties).transform_keys!(&@rename) feature.with_properties decoded end.then(&yielder) end end |