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
- #transform(feature) ⇒ 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
164 165 166 167 168 169 170 171 172 173 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 164 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
175 176 177 178 179 180 181 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 175 def counts classify(*@fields, *extra_field).each do |attributes, count| decode attributes end.group_by(&:first).map do |attributes, attributes_counts| [attributes, attributes_counts.sum(&:last)] end end |
#decode(attributes) ⇒ Object
133 134 135 136 137 138 139 |
# 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).then do |decoded| attributes.replace decoded end 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
153 154 155 156 157 158 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 153 def features(**, &block) paged(**).inject do |collection, page| yield collection.count, self.count if block_given? collection.merge! page end end |
#info ⇒ Object
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 183 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
160 161 162 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 160 def join_clauses(*clauses) "(" << clauses.join(") AND (") << ")" if clauses.any? end |
#paged(per_page: nil) ⇒ Object
145 146 147 148 149 150 151 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 145 def paged(per_page: nil) per_page = [*per_page, *@layer["maxRecordCount"], 500].min Enumerator::Lazy.new pages(per_page) do |yielder, page| page.each(&method(:transform)) yielder << page end end |
#transform(feature) ⇒ Object
141 142 143 |
# File 'lib/nswtopo/gis/arcgis/layer.rb', line 141 def transform(feature) decode(feature.properties).transform_keys!(&@rename) end |