├── .gitignore ├── .rspec ├── Gemfile ├── lib ├── version.rb ├── overpass_api_ruby.rb ├── ql.rb ├── xml.rb └── base.rb ├── .circleci └── config.yml ├── Rakefile ├── spec ├── integration │ ├── ql_spec.rb │ └── xml_spec.rb ├── unit │ ├── ql_spec.rb │ ├── xml_spec.rb │ └── base_spec.rb └── spec_helper.rb ├── CHANGELOG.md ├── Gemfile.lock ├── LICENSE ├── overpass-api-ruby.gemspec └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.gem 3 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org/' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /lib/version.rb: -------------------------------------------------------------------------------- 1 | module OverpassAPI 2 | VERSION = '0.3.1'.freeze 3 | end 4 | -------------------------------------------------------------------------------- /lib/overpass_api_ruby.rb: -------------------------------------------------------------------------------- 1 | require_relative 'version' 2 | require_relative 'xml' 3 | require_relative 'ql' 4 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | jobs: 4 | build: 5 | docker: 6 | - image: circleci/ruby:2.4.1 7 | steps: 8 | - checkout 9 | - run: bundle install 10 | - run: bundle exec rake spec 11 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | 3 | require 'rake' 4 | require 'rspec/core/rake_task' 5 | 6 | desc 'Run all tests' 7 | RSpec::Core::RakeTask.new(:spec) do |t| 8 | t.pattern = 'spec/**/*_spec.rb' 9 | t.rspec_opts = %w[--color --warnings] 10 | end 11 | 12 | task default: [:spec] 13 | -------------------------------------------------------------------------------- /lib/ql.rb: -------------------------------------------------------------------------------- 1 | require_relative 'base' 2 | 3 | module OverpassAPI 4 | # builds queries in overpass ql format 5 | class QL < Base 6 | def initialize(args = {}) 7 | super 8 | @maxsize = args[:maxsize] 9 | end 10 | 11 | def build_query(query) 12 | header = '' 13 | header << "[bbox:#{@bbox}]" if @bbox 14 | header << "[timeout:#{@timeout}]" if @timeout 15 | header << "[maxsize:#{@maxsize}]" if @maxsize 16 | 17 | header << '[out:json]' 18 | 19 | "#{header};#{query}" 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/integration/ql_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'overpass_api_ruby' 3 | 4 | describe OverpassAPI::QL do 5 | it 'should return the right elements' do 6 | options = { bbox: { s: -34.705448, n: -34.526562, 7 | w: -58.531471, e: -58.335159 }, 8 | timeout: 900, 9 | maxsize: 10_000 } 10 | 11 | overpass = OverpassAPI::QL.new(options) 12 | 13 | ba_query = "rel['route'='subway'];(._;>;);out body;" 14 | 15 | expect(overpass.query(ba_query)[:elements]).to be_a(Array) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/xml.rb: -------------------------------------------------------------------------------- 1 | require_relative 'base' 2 | 3 | module OverpassAPI 4 | # builds queries in xml format 5 | class XML < Base 6 | def initialize(args = {}) 7 | super 8 | @element_limit = args[:element_limit] 9 | end 10 | 11 | def build_query(query) 12 | bbox = @bbox ? " bbox='#{@bbox}'" : '' 13 | timeout = @timeout ? " timeout='#{@timeout}'" : '' 14 | element_limit = @element_limit ? " element-limit='#{@element_limit}'" : '' 15 | 16 | "" \ 17 | "#{query}" 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | ========= 3 | v 0.3.1 4 | ------- 5 | - Clean up / refactor per rubocop suggestions 6 | 7 | v 0.3 8 | ----- 9 | - Use POST instead of GET. 10 | - Update rack to version 2.0.6 because of a GitHub vulnerability alert. 11 | 12 | v 0.2.3 13 | -------- 14 | Removes param `data` from ENDPOINT constant (Issue #5) 15 | 16 | v 0.2.2 17 | ------- 18 | Fixes `maxsize` option in the `XML` class. (Issue #4) 19 | 20 | v 0.2 21 | ----- 22 | BREAKING CHANGES 23 | The addition of XML and QL classes breaks the compatibility with previous versions. Also, the `element_limit` option was deprecated, so now it's named `maxsize`. Check the examples. 24 | -------------------------------------------------------------------------------- /spec/integration/xml_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'overpass_api_ruby' 3 | 4 | describe OverpassAPI::XML do 5 | it 'should return the requested elements' do 6 | options = { bbox: { s: -34.705448, n: -34.526562, 7 | w: -58.531471, e: -58.335159 }, 8 | timeout: 900, 9 | element_limit: 100_000 } 10 | 11 | overpass = OverpassAPI::XML.new(options) 12 | 13 | ba_query = "" \ 14 | "" \ 15 | "" 16 | 17 | expect(overpass.query(ba_query)[:elements]).to be_a(Array) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/unit/ql_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'overpass_api_ruby' 3 | 4 | describe OverpassAPI::QL do 5 | it 'should return the right built query when no opts are passed' do 6 | overpass = OverpassAPI::QL.new 7 | built_query = overpass.build_query('a query') 8 | 9 | expect(built_query).to eq '[out:json];a query' 10 | end 11 | 12 | it 'should set the right opts' do 13 | opts = { bbox: { s: 1, n: 2, w: 3, e: 4 }, 14 | timeout: 1000, 15 | maxsize: 333 } 16 | 17 | overpass = OverpassAPI::QL.new(opts) 18 | built_query = overpass.build_query('a query') 19 | 20 | expected_built_query = '[bbox:1,3,2,4][timeout:1000]' \ 21 | '[maxsize:333][out:json];a query' 22 | expect(built_query).to eq expected_built_query 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/base.rb: -------------------------------------------------------------------------------- 1 | require 'httpi' 2 | require 'json' 3 | 4 | module OverpassAPI 5 | # base class, ql and xml extend this 6 | class Base 7 | DEFAULT_ENDPOINT = 'http://overpass-api.de/api/interpreter'.freeze 8 | 9 | def initialize(args = {}) 10 | bbox = args[:bbox] 11 | bounding_box(bbox[:s], bbox[:n], bbox[:w], bbox[:e]) if bbox 12 | 13 | @endpoint = args[:endpoint] || DEFAULT_ENDPOINT 14 | @timeout = args[:timeout] 15 | end 16 | 17 | def bounding_box(south, north, west, east) 18 | @bbox = "#{south},#{west},#{north},#{east}" 19 | end 20 | 21 | def query(query) 22 | perform build_query(query) 23 | end 24 | 25 | def raw_query(query) 26 | perform query 27 | end 28 | 29 | private 30 | 31 | def perform(query) 32 | r = HTTPI::Request.new(url: @endpoint, body: query) 33 | JSON.parse(HTTPI.post(r).body, symbolize_names: true) 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/unit/xml_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'overpass_api_ruby' 3 | 4 | describe OverpassAPI::XML do 5 | it 'should return the right built query when no opts are passed' do 6 | overpass = OverpassAPI::XML.new 7 | built_query = overpass.build_query('a query') 8 | 9 | expect(built_query).to eq "a query" \ 10 | '' 11 | end 12 | 13 | it 'should set the right opts' do 14 | opts = { bbox: { s: 1, n: 2, w: 3, e: 4 }, 15 | timeout: 1000, 16 | element_limit: 333 } 17 | 18 | overpass = OverpassAPI::XML.new(opts) 19 | built_query = overpass.build_query('a query') 20 | 21 | expected_built_query = "" \ 23 | 'a query' 24 | expect(built_query).to eq expected_built_query 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | overpass-api-ruby (0.3) 5 | httpi (~> 2.4.0) 6 | 7 | GEM 8 | remote: https://rubygems.org/ 9 | specs: 10 | coderay (1.1.1) 11 | diff-lcs (1.3) 12 | httpi (2.4.2) 13 | rack 14 | socksify 15 | method_source (0.8.2) 16 | pry (0.10.4) 17 | coderay (~> 1.1.0) 18 | method_source (~> 0.8.1) 19 | slop (~> 3.4) 20 | rack (2.0.6) 21 | rake (12.0.0) 22 | rspec (3.6.0) 23 | rspec-core (~> 3.6.0) 24 | rspec-expectations (~> 3.6.0) 25 | rspec-mocks (~> 3.6.0) 26 | rspec-core (3.6.0) 27 | rspec-support (~> 3.6.0) 28 | rspec-expectations (3.6.0) 29 | diff-lcs (>= 1.2.0, < 2.0) 30 | rspec-support (~> 3.6.0) 31 | rspec-mocks (3.6.0) 32 | diff-lcs (>= 1.2.0, < 2.0) 33 | rspec-support (~> 3.6.0) 34 | rspec-support (3.6.0) 35 | slop (3.6.0) 36 | socksify (1.7.1) 37 | 38 | PLATFORMS 39 | ruby 40 | 41 | DEPENDENCIES 42 | bundler (~> 1.3) 43 | overpass-api-ruby! 44 | pry (~> 0.10) 45 | rake (~> 12.0) 46 | rspec (~> 3.6) 47 | 48 | BUNDLED WITH 49 | 1.17.2 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Bruno Salerno 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /overpass-api-ruby.gemspec: -------------------------------------------------------------------------------- 1 | lib = File.expand_path('lib', __dir__) 2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 3 | require 'version' 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = 'overpass-api-ruby' 7 | spec.version = OverpassAPI::VERSION 8 | spec.authors = ['Bruno Salerno'] 9 | spec.email = ['br.salerno@gmail.com'] 10 | spec.description = 'A Ruby wrapper for OpenStreetMap Overpass API' 11 | spec.summary = 'This gem will allow you to perform queries to'\ 12 | 'OSM Overpass API using QL or XML' 13 | spec.homepage = 'https://github.com/BrunoSalerno/overpass-api-ruby' 14 | spec.license = 'MIT' 15 | 16 | spec.files = `git ls-files`.split($RS) 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ['lib'] 20 | 21 | spec.add_runtime_dependency 'httpi', '~> 2.4' 22 | 23 | spec.add_development_dependency 'bundler', '~> 1.3' 24 | spec.add_development_dependency 'pry', '~> 0.10' 25 | spec.add_development_dependency 'rake', '~> 12.0' 26 | spec.add_development_dependency 'rspec', '~> 3.6' 27 | end 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Overpass API Ruby [![CircleCI](https://circleci.com/gh/BrunoSalerno/overpass-api-ruby.svg?style=svg)](https://circleci.com/gh/BrunoSalerno/overpass-api-ruby) 2 | ================= 3 | 4 | A Ruby wrapper for OpenStreetMap Overpass API. Supports both QL and XML. 5 | 6 | Note: Version 0.2 introduces breaking changes. Check the file CHANGELOG.md. 7 | 8 | Install 9 | ------- 10 | 11 | `gem install overpass-api-ruby` 12 | 13 | or add `gem 'overpass-api-ruby'` to your Gemfile 14 | 15 | Usage 16 | ----- 17 | 18 | Using XML: 19 | 20 | ```ruby 21 | require 'overpass_api_ruby' 22 | 23 | options={:bbox => {:s => -34.705448, :n => -34.526562, 24 | :w => -58.531471, :e => -58.335159}, 25 | :timeout => 900, 26 | :element_limit => 1073741824} 27 | 28 | overpass = OverpassAPI::XML.new(options) 29 | 30 | query = "" << 31 | "" 32 | 33 | response = overpass.query(query) 34 | ``` 35 | 36 | Using QL: 37 | 38 | ```ruby 39 | require 'overpass_api_ruby' 40 | 41 | options={:bbox => {:s => -34.705448, :n => -34.526562, 42 | :w => -58.531471, :e => -58.335159}, 43 | :timeout => 900, 44 | :maxsize => 1073741824} 45 | 46 | overpass = OverpassAPI::QL.new(options) 47 | 48 | query = "rel['route'='subway'];(._;>;);out body;" 49 | 50 | response = overpass.query(query) 51 | ``` 52 | 53 | Common options on instantiation 54 | ------------------------ 55 | ``` 56 | bbox Hash. Global bounding box. 57 | endpoint String. 58 | Defaults to http://overpass-api.de/api/interpreter 59 | timeout Integer. 60 | ``` 61 | 62 | Specific options on instantiation 63 | ------------------------ 64 | QL 65 | ``` 66 | maxsize Integer. 67 | ``` 68 | 69 | XML 70 | ``` 71 | element_limit Integer. 72 | ``` 73 | 74 | See [Overpass API](http://wiki.openstreetmap.org/wiki/Overpass_API/Language_Guide) 75 | 76 | Public methods 77 | -------------- 78 | 79 | Both `QL` and `XML` classes have the same public methods: 80 | 81 | ```ruby 82 | query () Performs the query passed using the global values set on instantiation. 83 | 84 | raw_query () The whole query must be passed. 85 | 86 | buid_query () Returns a String containing the whole query. 87 | 88 | bounding_box (s,n,w,e) Defines the global bounding box. 89 | ``` 90 | 91 | Test 92 | ---- 93 | 94 | Run `rake spec` 95 | 96 | License 97 | ------- 98 | MIT. 99 | -------------------------------------------------------------------------------- /spec/unit/base_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'overpass_api_ruby' 3 | require 'httpi' 4 | 5 | # rubocop:disable Metrics/BlockLength 6 | describe OverpassAPI::Base do 7 | it 'should return the default endpoint' do 8 | expect(OverpassAPI::Base::DEFAULT_ENDPOINT).to eq 'http://overpass-api.de/api/interpreter' 9 | base = OverpassAPI::Base.new 10 | expect( 11 | base.instance_variable_get('@endpoint') 12 | ).to eq OverpassAPI::Base::DEFAULT_ENDPOINT 13 | end 14 | 15 | it 'should set the right args' do 16 | opts = { bbox: { s: 1, n: 2, w: 3, e: 4 }, 17 | endpoint: 'a.endpoint.com', 18 | timeout: 1000 } 19 | 20 | base = OverpassAPI::Base.new(opts) 21 | 22 | expect(base.instance_variable_get('@bbox')).to eq '1,3,2,4' 23 | expect(base.instance_variable_get('@endpoint')).to eq 'a.endpoint.com' 24 | expect(base.instance_variable_get('@timeout')).to eq 1000 25 | end 26 | 27 | it 'should set the bounding box' do 28 | base = OverpassAPI::Base.new 29 | base.bounding_box(10, 20, 30, 40) 30 | 31 | expect(base.instance_variable_get('@bbox')).to eq '10,30,20,40' 32 | end 33 | 34 | it 'should try to perform the query' do 35 | base = OverpassAPI::Base.new 36 | 37 | query = 'a query' 38 | built_query = 'built_query' 39 | expected_response = 'a response' 40 | 41 | allow(base).to receive(:perform).and_return(expected_response) 42 | allow(base).to receive(:build_query).and_return(built_query) 43 | 44 | expect(base).to receive(:build_query).with(query) 45 | expect(base).to receive(:perform).with(built_query) 46 | 47 | response = base.query(query) 48 | 49 | expect(response).to eq expected_response 50 | end 51 | 52 | it 'should try to perform a raw query' do 53 | base = OverpassAPI::Base.new 54 | 55 | query = 'as query' 56 | expected_response = 'a response' 57 | 58 | allow(base).to receive(:perform).and_return(expected_response) 59 | expect(base).to receive(:perform).with(query) 60 | 61 | response = base.raw_query(query) 62 | 63 | expect(response).to eq expected_response 64 | end 65 | 66 | it 'should try to perform an http request' do 67 | base = OverpassAPI::Base.new 68 | 69 | query = 'a query' 70 | request = 'a request' 71 | body = { key: 'value' } 72 | 73 | allow(HTTPI::Request).to receive(:new).and_return(request) 74 | expect( 75 | HTTPI::Request 76 | ).to receive(:new).with( 77 | url: OverpassAPI::Base::DEFAULT_ENDPOINT, 78 | body: query 79 | ) 80 | 81 | allow( 82 | HTTPI 83 | ).to receive(:post).and_return( 84 | OpenStruct.new(body: body.to_json) 85 | ) 86 | expect(HTTPI).to receive(:post).with(request) 87 | 88 | response = base.raw_query(query) 89 | expect(response).to eq body 90 | end 91 | end 92 | # rubocop:enable Metrics/BlockLength 93 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rspec --init` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # The generated `.rspec` file contains `--require spec_helper` which will cause 4 | # this file to always be loaded, without a need to explicitly require it in any 5 | # files. 6 | # 7 | # Given that it is always loaded, you are encouraged to keep this file as 8 | # light-weight as possible. Requiring heavyweight dependencies from this file 9 | # will add to the boot time of your test suite on EVERY test run, even for an 10 | # individual file that may not need all of that loaded. Instead, consider making 11 | # a separate helper file that requires the additional dependencies and performs 12 | # the additional setup, and require it from the spec files that actually need 13 | # it. 14 | # 15 | # The `.rspec` file also contains a few flags that are not defaults but that 16 | # users commonly want. 17 | # 18 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 19 | 20 | RSpec.configure do |config| 21 | # rspec-expectations config goes here. You can use an alternate 22 | # assertion/expectation library such as wrong or the stdlib/minitest 23 | # assertions if you prefer. 24 | config.expect_with :rspec do |expectations| 25 | # This option will default to `true` in RSpec 4. It makes the `description` 26 | # and `failure_message` of custom matchers include text for helper methods 27 | # defined using `chain`, e.g.: 28 | # be_bigger_than(2).and_smaller_than(4).description 29 | # # => "be bigger than 2 and smaller than 4" 30 | # ...rather than: 31 | # # => "be bigger than 2" 32 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 33 | end 34 | 35 | # rspec-mocks config goes here. You can use an alternate test double 36 | # library (such as bogus or mocha) by changing the `mock_with` option here. 37 | config.mock_with :rspec do |mocks| 38 | # Prevents you from mocking or stubbing a method that does not exist on 39 | # a real object. This is generally recommended, and will default to 40 | # `true` in RSpec 4. 41 | mocks.verify_partial_doubles = false 42 | end 43 | 44 | # The settings below are suggested to provide a good initial experience 45 | # with RSpec, but feel free to customize to your heart's content. 46 | 47 | # These two settings work together to allow you to limit a spec run 48 | # to individual examples or groups you care about by tagging them with 49 | # `:focus` metadata. When nothing is tagged with `:focus`, all examples 50 | # get run. 51 | # config.filter_run :focus 52 | # config.run_all_when_everything_filtered = true 53 | 54 | # Allows RSpec to persist some state between runs in order to support 55 | # the `--only-failures` and `--next-failure` CLI options. We recommend 56 | # you configure your source control system to ignore this file. 57 | # config.example_status_persistence_file_path = "spec/examples.txt" 58 | 59 | # Limits the available syntax to the non-monkey patched syntax that is 60 | # recommended. For more details, see: 61 | # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax 62 | # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 63 | # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching 64 | # config.disable_monkey_patching! 65 | 66 | # This setting enables warnings. It's recommended, but in some cases may 67 | # be too noisy due to issues in dependencies. 68 | # config.warnings = true 69 | 70 | # Many RSpec users commonly either run the entire suite or an individual 71 | # file, and it's useful to allow more verbose output when running an 72 | # individual spec file. 73 | # if config.files_to_run.one? 74 | # Use the documentation formatter for detailed output, 75 | # unless a formatter has already been configured 76 | # (e.g. via a command-line flag). 77 | # config.default_formatter = 'doc' 78 | # end 79 | 80 | # Print the 10 slowest examples and example groups at the 81 | # end of the spec run, to help surface which specs are running 82 | # particularly slow. 83 | # config.profile_examples = 10 84 | 85 | # Run specs in random order to surface order dependencies. If you find an 86 | # order dependency and want to debug it, you can fix the order by providing 87 | # the seed, which is printed after each run. 88 | # --seed 1234 89 | # config.order = :random 90 | 91 | # Seed global randomization in this process using the `--seed` CLI option. 92 | # Setting this allows you to use `--seed` to deterministically reproduce 93 | # test failures related to randomization by passing the same `--seed` value 94 | # as the one that triggered the failure. 95 | # Kernel.srand config.seed 96 | end 97 | --------------------------------------------------------------------------------