arcrest 0.0.0.pre → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4facbf131295458bba59afadece248118b806cf9
4
- data.tar.gz: 4453da319fa914ff6a03e9b42caf6fd2c6720b6f
3
+ metadata.gz: 948887626e3cbf6c38a60ab4d3f3f96f38815a64
4
+ data.tar.gz: ee36b210de6e5354e2afae62ec6b3ba3bdbb44d4
5
5
  SHA512:
6
- metadata.gz: 6f78ad5e3bd3d33f9f75da848670163a8a3b1fd07c541046e8125de5f61b99723005b680dc2eaabd55674fc9b5e4c78c611039e75ee63d9f52fac42f27b2ab9a
7
- data.tar.gz: e08922265ac4e258c8ba4ac1cdd5e45a98092949f88b9398e9614637aebfc2854cd51cde97b2ccffb3100a890fb440e19dc47bcd11553f1251be8849e093c2a6
6
+ metadata.gz: a3bfa45f4ea1fa30629e216122fe40783d3519b78e9e592e6e14ea41082b0e88d18bc8e284f101da40a07610ec69154772a99256b9748cf30f62e4905d89edbd
7
+ data.tar.gz: 736154f7a57441c74bb37234b500a3126c258ce173adc4aa5e0a7b5af2ca2be38f7c55d7aad7952cace2f82337d7529a638538ff238e02cf630b7f578ccf9509
data/.rspec CHANGED
@@ -1,2 +1,3 @@
1
1
  --format documentation
2
2
  --color
3
+ --require spec_helper
data/.travis.yml CHANGED
@@ -2,9 +2,8 @@ sudo: false
2
2
  language: ruby
3
3
 
4
4
  rvm:
5
- - 2.3.0
6
- - 2.2.0
7
- - 2.1
5
+ - 2.3.1
6
+ - 2.2.5
8
7
 
9
8
  before_install: gem install bundler -v 1.12.5
10
9
 
data/Guardfile ADDED
@@ -0,0 +1,13 @@
1
+ notification :gntp
2
+
3
+ guard :rspec, cmd: 'bundle exec rspec' do
4
+ watch(%r{^lib/(.+).rb$}) do |m| # watch /lib/ files
5
+ "spec/#{m[1]}_spec.rb"
6
+ end
7
+
8
+ watch(%r{^spec/(.+).rb$}) do |m| # watch /spec/ files
9
+ "spec/#{m[1]}.rb"
10
+ end
11
+
12
+ watch('spec/spec_helper.rb') { 'spec' }
13
+ end
data/README.md CHANGED
@@ -1,19 +1,113 @@
1
- # Arcrest
1
+ # ArcREST
2
+ [![Build status](https://secure.travis-ci.org/MatzFan/ArcREST.svg)](http://travis-ci.org/MatzFan/ArcREST)
3
+ [![Gem Version](https://badge.fury.io/rb/arcrest.svg)](http://badge.fury.io/rb/arcrest)
4
+
5
+ Ruby Gem wrapper around the [ArcGIS REST API](http://services.arcgisonline.com/arcgis/sdk/rest/)
6
+
7
+ ## Requirements
8
+
9
+ bundler
10
+
11
+
12
+ ## Current Limitations
13
+
14
+ API FeatureServer query capabilities only at present. Unauthenticated calls only (please raise an issue if you wish this to be supported)
2
15
 
3
- Placeholder for arcrest Ruby Gem
4
16
 
5
17
  ## Installation
6
18
 
19
+ Add this line to your application's Gemfile:
20
+
21
+ $ gem 'arcrest'
22
+
23
+ And then execute:
24
+
25
+ $ bundle
26
+
27
+ Or install it yourself as:
28
+
7
29
  $ gem install arcrest
8
30
 
31
+
9
32
  ## Usage
10
33
 
11
- TODO: Write usage instructions here
34
+ The API defines a [resource heirarchy](http://services.arcgisonline.com/arcgis/sdk/rest/#/Resource_hierarchy) 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.
35
+
36
+ ```ruby
37
+ require 'arcrest'
38
+
39
+ catalog = ArcREST::Catalog.new('http://rmgsc.cr.usgs.gov/arcgis/rest/services')
40
+ puts catalog.services
41
+ #=> {"name"=>"cwqdr_main", "type"=>"MapServer"}
42
+ #=> {"name"=>"ecosys_Africa", "type"=>"MapServer"}
43
+ #=> {"name"=>"ecosys_SA", "type"=>"MapServer"}
44
+ #=> ...
45
+
46
+ service = ArcREST::Service.new('http://rmgsc.cr.usgs.gov/arcgis/rest/services/geomac_fires/FeatureServer')
47
+ puts service.layers
48
+ #=> {"id"=>1, "name"=>"Large Fire Points"}
49
+ #=> {"id"=>2, "name"=>"Fire Perimeters"}
50
+ #=> {"id"=>3, "name"=>"MODIS Thermal Satellite"}
51
+ #=> {"id"=>4, "name"=>"Inactive Fire Perimeters"}
52
+
53
+ layer = ArcREST::Layer.new("#{service.url}/2")
54
+ puts layer.name
55
+ #=> Fire Perimeters
56
+ puts layer.type
57
+ #=> Feature Layer
58
+ puts layer.count
59
+ #=> 14 # count of the layer's features
60
+ puts layer.object_ids.inspect
61
+ #=> [681, 682, 688, 690, 614, 618, 619, 653, 683, 684, 685, 686, 687, 689]
62
+ puts layer.fields
63
+ #=> {"name"=>"objectid", "type"=>"esriFieldTypeOID", "alias"=>"OBJECTID", "domain"=>nil, "editable"=>false, "nullable"=>false}
64
+ #=> {"name"=>"agency", "type"=>"esriFieldTypeString", "alias"=>"agency", "domain"=>nil, "editable"=>true, "nullable"=>true, "length"=>15}
65
+ #=> ...
66
+ ```
67
+
68
+ Once you have a Layer object, you can query it's features. The [documention](http://services.arcgisonline.com/arcgis/sdk/rest/index.html#/Query_Feature_Service_Layer/) shows the possibilities. Here is a very simple example:
69
+
70
+ ```ruby
71
+ features = layer.features(where: "agency='BLM'", returnGeometry: false)
72
+ puts features.count
73
+ #=> 2
74
+ puts features.first
75
+ #=> {"objectid"=>690, "agency"=>"BLM", "comments"=>" ", "active"=>"Y"...
76
+ ```
77
+
78
+ ```features``` takes an options hash of API call params. Invalid key values raise an error. Valid params for the server can be listed like this:
79
+ ```ruby
80
+ puts layer.valid_opts.inspect
81
+ #=> ["dbVersion", "distance", "geometry", "geometryPrecision"...
82
+ ```
83
+ or by consulting the [docs](http://services.arcgisonline.com/arcgis/sdk/rest/index.html#/Query_Feature_Service_Layer/). One default is set: ```outFields: '*'``` - which requests data for all fields.
84
+
85
+
86
+ 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 ```@max_record_count``` value, usually 1,000). An InvalidQuery error is raised if the server gives a 400 error of this form:
87
+ ```json
88
+ {
89
+ "error": {
90
+ "code": 400,
91
+ "message": "Unable to complete operation.",
92
+ "details": [
93
+ "Unable to perform query operation.",
94
+ "Invalid query"
95
+ ]
96
+ }
97
+ }
98
+ ```
99
+
100
+
101
+ ## Specification & Tests
102
+
103
+ Full specification documentation is available for each build at [Travis](https://travis-ci.org/MatzFan/ArcREST). To run the tests yourself clone this repo and run:
104
+
105
+ $ rake spec
12
106
 
13
107
 
14
108
  ## Contributing
15
109
 
16
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/arcrest. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
110
+ Bug reports and pull requests are welcome on GitHub at https://github.com/MatzFan/arcrest
17
111
 
18
112
 
19
113
  ## License
data/arcrest.gemspec CHANGED
@@ -24,4 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency 'bundler', '~> 1.12'
25
25
  spec.add_development_dependency 'rake', '~> 10.0'
26
26
  spec.add_development_dependency 'rspec', '~> 3.0'
27
+ spec.add_development_dependency 'guard', '~> 2.14'
28
+ spec.add_development_dependency 'guard-rspec', '~> 4.7'
29
+ spec.add_development_dependency 'ruby_gntp', '~> 0.3' # guard notifications
27
30
  end
@@ -0,0 +1,20 @@
1
+ # adds ability to dynamically set instance vars & accessors
2
+ module Attributable
3
+ def create_method(name, &block)
4
+ self.class.send(:define_method, name.to_sym, &block)
5
+ end
6
+
7
+ def create_setter(m)
8
+ create_method("#{m}=".to_sym) { |v| instance_variable_set("@#{m}", v) }
9
+ end
10
+
11
+ def create_getter(m)
12
+ create_method(m.to_sym) { instance_variable_get("@#{m}") }
13
+ end
14
+
15
+ def set_attr(method, value)
16
+ create_setter(method)
17
+ send "#{method}=".to_sym, value
18
+ create_getter(method)
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ module ArcREST
2
+ # a catalog of services
3
+ class Catalog < Server
4
+ attr_reader :folders, :services
5
+
6
+ def initialize(url)
7
+ super
8
+ @services = services
9
+ end
10
+
11
+ def folders
12
+ @json['folders']
13
+ end
14
+
15
+ def services
16
+ @json['services']
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,91 @@
1
+ module ArcREST
2
+ class InvalidOption < StandardError; end
3
+ class InvalidQuery < StandardError; end
4
+ # a layer
5
+ class Layer < Server
6
+ include Attributable
7
+
8
+ ERR = 'error'.freeze
9
+ ATTRIBUTES = %w(id name type drawing_info fields max_record_count).freeze
10
+ DEFAULT_PARAMS = { where: '1=1', outFields: '*' }.freeze
11
+ PARAMS = %w(distance geometry geometryType inSR objectIds
12
+ outFields outSR relationParam returnDistinceValues
13
+ returnIdsOnly spatialRel time where).freeze
14
+ PARAMS_SP1 = %w(returnCountOnly).freeze
15
+ PARAMS_10_1 = %w(dbVersion geometryPrecision groupByFieldsForStatistics
16
+ maxAllowableOffset multiPatchOption orderByFields
17
+ outStatistics returnGeometry returnM returnZ).freeze
18
+ PARAMS_10_3 = %w(returnExtentOnly resultOffset resultRecordCount).freeze
19
+
20
+ attr_reader :valid_params # other accessors set in constructor
21
+
22
+ def initialize(url)
23
+ super
24
+ generate_attributes # dynamically create & assign values to attributes :)
25
+ end
26
+
27
+ def count
28
+ @version > 10 ? count_only_true : object_ids.count # v10.1 onwards
29
+ end
30
+
31
+ def object_ids # care - must specify outFields to overide default '*'
32
+ query(outFields: nil, returnIdsOnly: true)['objectIds']
33
+ end
34
+
35
+ def features(options = {})
36
+ query(options)['features']
37
+ end
38
+
39
+ def valid_opts
40
+ return PARAMS if @version < 10 || @version == 10.0
41
+ return (PARAMS + PARAMS_SP1).sort if @version < 10.1
42
+ return (PARAMS + PARAMS_SP1 + PARAMS_10_1).sort if @version < 10.2
43
+ (PARAMS + PARAMS_SP1 + PARAMS_10_1 + PARAMS_10_3).sort
44
+ end
45
+
46
+ private
47
+
48
+ def generate_attributes
49
+ ATTRIBUTES.each { |name| set_attr(name, json_value(name)) }
50
+ end
51
+
52
+ def json_value(name)
53
+ @json[camelify(name)]
54
+ end
55
+
56
+ def camelify(name)
57
+ words = name.split('_')
58
+ words[1..-1].map(&:capitalize).unshift(words.first).join
59
+ end
60
+
61
+ def query(options)
62
+ validate(options.keys.map(&:to_s).sort)
63
+ valid_resp(build_uri, DEFAULT_PARAMS.merge(options))
64
+ end
65
+
66
+ def valid_resp(uri, opts)
67
+ raise InvalidQuery, m(opts) if (resp = json(uri, opts)).keys.include? ERR
68
+ resp
69
+ end
70
+
71
+ def m(options)
72
+ "The following query parameters resulted in a 400 response:\n#{options}"
73
+ end
74
+
75
+ def validate(keys)
76
+ keys.all? { |k| raise InvalidOption, msg(k) unless valid_opts.include? k }
77
+ end
78
+
79
+ def msg(key)
80
+ "'#{key}' is an invalid option, valid query options are:\n#{PARAMS}"
81
+ end
82
+
83
+ def count_only_true
84
+ query(returnCountOnly: true)['count']
85
+ end
86
+
87
+ def build_uri
88
+ URI::HTTP.build(host: @uri.host, path: "#{@uri.path}/query")
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,48 @@
1
+ require 'net/http'
2
+ require 'json'
3
+
4
+ module ArcREST
5
+ # adds metadata method
6
+ class Server
7
+ REGEX = %r{^\/arcgis\/rest\/services}i
8
+ BAD_ENDPOINT = 'Invalid ArcGIS endpoint'.freeze
9
+
10
+ attr_reader :url, :json, :version
11
+
12
+ def initialize(url)
13
+ @url = url
14
+ @uri = uri
15
+ @server_uri = server_uri
16
+ @json = json(@uri)
17
+ @version = version
18
+ end
19
+
20
+ def json(uri, options = {})
21
+ JSON.parse get(uri, options)
22
+ end
23
+
24
+ def version
25
+ json(server_uri)['currentVersion'] # subclasses use server uri
26
+ end
27
+
28
+ protected
29
+
30
+ def uri
31
+ raise ArgumentError, BAD_ENDPOINT if (URI(@url).path =~ REGEX) != 0
32
+ URI @url
33
+ end
34
+
35
+ def server_uri
36
+ URI::HTTP.build(host: @uri.host, path: '/arcgis/rest/services')
37
+ end
38
+
39
+ def add_json_param_to(hash)
40
+ { f: 'pjson' }.merge(hash) # 'pjson' guarantees id's unique
41
+ end
42
+
43
+ def get(uri, options = {})
44
+ uri.query = URI.encode_www_form(add_json_param_to(options))
45
+ Net::HTTP.get uri
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,25 @@
1
+ module ArcREST
2
+ # a FeatureService or a MapService
3
+ class Service < Server
4
+ attr_reader :max_record_count, :capabilities, :layers
5
+
6
+ def initialize(url)
7
+ super
8
+ @max_record_count = max_record_count # may be nil
9
+ @capabilities = capabilities # may be empty
10
+ @layers = layers # may be empty
11
+ end
12
+
13
+ def max_record_count
14
+ @json['maxRecordCount']
15
+ end
16
+
17
+ def capabilities
18
+ @json['capabilities'] ? @json['capabilities'].split(',') : nil
19
+ end
20
+
21
+ def layers
22
+ @json['layers']
23
+ end
24
+ end
25
+ end
@@ -1,3 +1,3 @@
1
1
  module ArcREST
2
- VERSION = '0.0.0.pre'.freeze
2
+ VERSION = '0.0.1'.freeze
3
3
  end
data/lib/arcrest.rb CHANGED
@@ -1,6 +1,9 @@
1
+ require 'arcrest/attributable' # module
1
2
  require 'arcrest/version'
3
+ require 'arcrest/server'
4
+ require 'arcrest/catalog'
5
+ require 'arcrest/service'
6
+ require 'arcrest/layer'
2
7
 
3
8
  # namespace
4
- module ArcREST
5
- # Your code goes here...
6
- end
9
+ module ArcRest; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arcrest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0.pre
4
+ version: 0.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bruce Steedman
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-05-27 00:00:00.000000000 Z
11
+ date: 2016-06-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,48 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.14'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.14'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '4.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: ruby_gntp
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.3'
55
97
  description: Wrapper for ArcGIS REST API
56
98
  email:
57
99
  - bruce.steedman@gmail.com
@@ -63,6 +105,7 @@ files:
63
105
  - ".rspec"
64
106
  - ".travis.yml"
65
107
  - Gemfile
108
+ - Guardfile
66
109
  - LICENSE.txt
67
110
  - README.md
68
111
  - Rakefile
@@ -70,6 +113,11 @@ files:
70
113
  - bin/console
71
114
  - bin/setup
72
115
  - lib/arcrest.rb
116
+ - lib/arcrest/attributable.rb
117
+ - lib/arcrest/catalog.rb
118
+ - lib/arcrest/layer.rb
119
+ - lib/arcrest/server.rb
120
+ - lib/arcrest/service.rb
73
121
  - lib/arcrest/version.rb
74
122
  homepage: https://github.com/MatzFan/arcrest
75
123
  licenses:
@@ -86,12 +134,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
86
134
  version: '0'
87
135
  required_rubygems_version: !ruby/object:Gem::Requirement
88
136
  requirements:
89
- - - ">"
137
+ - - ">="
90
138
  - !ruby/object:Gem::Version
91
- version: 1.3.1
139
+ version: '0'
92
140
  requirements: []
93
141
  rubyforge_project:
94
- rubygems_version: 2.6.2
142
+ rubygems_version: 2.5.1
95
143
  signing_key:
96
144
  specification_version: 4
97
145
  summary: Wrapper for ArcGIS REST API