ArcREST

Ruby Gem wrapper around the ArcGIS REST API

Requirements

Ruby >= 2.4

Current Limitations

API FeatureServer query capabilities only at present.

Installation

Add this line to your application's Gemfile:

$ gem 'arcrest'

And then execute:

$ bundle

Or install it yourself as:

$ gem install arcrest

Usage

The API defines a resource heirarchy which includes a Catalog of Services (MapServer or FeatureServer). Services have one or more Layers and Layers have Features, which may be queried in various ways, including by spatial coordinates.

require 'arcrest'

catalog = ArcREST::Catalog.new 'https://sampleserver6.arcgisonline.com/arcgis/rest/services'
puts catalog.services
#=> {"name"=>"911CallsHotspot", "type"=>"GPServer"}
#=> {"name"=>"911CallsHotspot", "type"=>"MapServer"}
#=> {"name"=>"Census", "type"=>"MapServer"}
...

puts catalog.folders
#=> AGP
#=> Elevation
#=> Energy
...

It is also possible to pass a Referer (or any other) header which will be added to all HTTP requests:

catalog = ArcREST::Catalog.new('https://gojdippmaps.azurewebsites.net/proxy.ashx?https://maps.gov.je/arcgis/rest/services', headers: {referer: 'https://www.gov.je//citizen/Planning/Pages/HistoricEnvironmentDetail.aspx'})

A Service (FeatureServer) is instantiated like this:

service = ArcREST::Service.new 'https://sampleserver6.arcgisonline.com/arcgis/rest/services/EmergencyFacilities/FeatureServer'
puts service.layers
#=> {"id"=>0, "name"=>"Emergency Facilities", "parentLayerId"=>-1, "defaultVisibility"=>true, "subLayerIds"=>nil, "minScale"=>150000, "maxScale"=>0, "type"=>"Feature Layer", "geometryType"=>"esriGeometryPoint"}

layer = ArcREST::Layer.new "#{service.url}/0"
puts layer.name
#=> Emergency Facilities

puts layer.type
#=> Feature Layer

puts layer.max_record_count
#=> 1000 # maximum number of features that can be queried, see below

puts layer.count
#=> 16 # count of the layer's features

puts layer.object_ids.inspect
#=> [29489, 29503, 29490, 29491, 29492, 29493, 29494, 29495, 29496, 29497, 29498, 29499, 29500, 29501, 29502, 29504]

puts layer.fields
#=> {"name"=>"objectid", "type"=>"esriFieldTypeOID", "alias"=>"OBJECTID", "domain"=>nil, "editable"=>false, "nullable"=>false, "defaultValue"=>nil, "modelName"=>"System-maintained ObjectID"}
#=> {"name"=>"facilityid", "type"=>"esriFieldTypeString", "alias"=>"Emergency Facility ID", "domain"=>nil, "editable"=>true, "nullable"=>true, "length"=>20, "defaultValue"=>nil, "modelName"=>"FACILITYID"},
...

Catalog, Service and Layer have a json method which returns information from the relevant server as a Hash. In addition to the example methods above this can be parsed in the usual way - e.g:

puts layer.json.keys.inspect
#=> ["currentVersion", "id", "name", "type", "parentLayer", "defaultVisibility", ...

Once you have a Layer object, you can perform queries on it. The documention shows the possibilities. Here is a very simple example:

The query method returns the whole server response as a Hash:

puts layer.query(where: '1=0').inspect
#=> {"objectIdFieldName"=>"objectid", "globalIdFieldName"=>"globalid", "geometryType"=>"esriGeometryPoint", "spatialReference"=>{"wkid"=>4326, "latestWkid"=>4326}, "features"=>[]}

If you just want the features, use the features method:

puts features.count
#=> 16

puts features[1]
#=> {"attributes"=>{"objectid"=>29503, "facilityid"=>"K2032", "facname"=>"Redlands Mall", "factype"=>3, ...

features = layer.features(where: "factype=3", returnGeometry: false)
puts features.size
#=> 7

puts features.first['attributes']
#=> {"objectid"=>29503, "facilityid"=>"K2032", "facname"=>"Redlands Mall", "factype"=>3, "organiz"=>"City of Redlands", ...

query and features take an options hash of API call params. Invalid key values raise an error. Valid params for the server can be listed like this:

puts layer.valid_opts.inspect
#=> ["dbVersion", "distance", "geometry", "geometryPrecision", ... , "where"]

or by consulting the docs. One default is set: outFields: '*' - which requests data for all fields.

The :where key is used with any valid SQL to query the layer fields. The default is '1=1' which returns all records (up to the layer.max_record_count value, usually 1,000). An error is raised if the server gives a 400 error of this form:

{
  "error": {
    "code": 400,
    "message": "Unable to complete operation.",
    "details": [
      "Unable to perform query operation.",
      "Invalid query"
    ]
  }
}

Specification & Tests

Full specification documentation is available by running the test suite thus:

$ bundle exec rake spec

Contributing

Bug reports and pull requests are welcome on GitHub at https://gitlab.com/matzfan/arcrest

License

The gem is available as open source under the terms of the MIT License.