Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Le Great Refactor #13

Merged
merged 4 commits into from
Sep 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 1 addition & 8 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,6 @@

source 'https://rubygems.org'

group :development do
gem 'guard', '~> 2.15.0', require: false
gem 'guard-rspec', '~> 4.7.3', require: false
gem 'guard-rubocop', '~> 1.3.0', require: false
gem 'rspec', '~> 3.8.0'
end

group :test do
gem 'coveralls', require: false
gem 'rubocop', require: false
Expand All @@ -17,4 +10,4 @@ group :test do
gem 'webmock'
end

gem 'rgeo'
gemspec
29 changes: 22 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,25 @@ Or put it in your Gemfile:
## Usage

```ruby
# require the library if it's not autoloaded
# Require the library if it's not autoloaded
require 'underpass'
# Define a polygon via WKT (Well Known Text)
wkt = <<-WKT
POLYGON ((
23.669 47.65,
23.725 47.65,
23.725 47.674,
23.669 47.674,
23.669 47.65
))
WKT
# create a bounding box in which the query will be run
f = RGeo::Geographic.spherical_factory
bbox = f.parse_wkt('POLYGON ((23.669 47.65, 23.725 47.65, 23.725 47.674, 23.669 47.674, 23.669 47.65))')
bbox = f.parse_wkt(wkt)
# provide the query
op_query = 'way["heritage:operator"="lmi"]["ref:ro:lmi"="MM-II-m-B-04508"];'
# perform the query and get your results
result = Underpass::QL::Query.perform(bbox, op_query)
# perform the query and get your matches
matches = Underpass::QL::Query.perform(bbox, op_query)
```

See [more usage examples](usage-examples.md).
Expand All @@ -36,15 +46,20 @@ See [more usage examples](usage-examples.md).

Have a look at the [issue tracker](https://github.com/haiafara/underpass/issues).

## Contributing
## How To Contribute

* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet;
* Check out the latest development branch to make sure the feature hasn't been implemented or the bug hasn't been fixed yet;
* Check out the issue tracker to make sure someone already hasn't requested it and / or contributed it;
* Fork the project;
* Fork the project, clone the fork, run `bundle install` and then make sure `rspec` runs;
* Start a feature / bugfix branch;
* Commit and push until you are happy with your contribution;
* Make sure to add specs for it. This is important so your contribution won't be broken in a future version unintentionally.

Further tips:

* To test drive the library run `bundle console`;
* Run `guard` in the project directory, it'll watch for file changes and run Rubocop and RSpec for real time feedback.

## License

underpass is released under the MIT License. See the [LICENSE file](LICENSE) for further details.
8 changes: 7 additions & 1 deletion lib/underpass.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
require 'rgeo'
require 'json'

require 'underpass/ql/ql'
require 'underpass/client'
require 'underpass/matcher'
require 'underpass/shape'
require 'underpass/ql/bounding_box'
require 'underpass/ql/query'
require 'underpass/ql/request'
require 'underpass/ql/response'

# Underpass is a library that makes it easy to query the Overpass API
# and translate its responses into RGeo objects
Expand Down
13 changes: 13 additions & 0 deletions lib/underpass/client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module Underpass
# Runs the Overpass API query
class Client
API_URI = 'https://overpass-api.de/api/interpreter'

# Performs the API request
def self.perform(request)
Net::HTTP.post_form(URI(API_URI), data: request.to_query)
end
end
end
49 changes: 49 additions & 0 deletions lib/underpass/matcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# frozen_string_literal: true

module Underpass
# Provides matches given a response object
# By matches we understand response elements that have a tags key
class Matcher
attr_reader :matches

def initialize(response)
@nodes = response.nodes
@ways = response.ways
@matches = []

@nodes.each_value do |node|
@matches << point_from_node(node) if node.key?(:tags)
end

@ways.each_value do |way|
@matches << way_matches(way) if way.key?(:tags)
end
end

private

def way_matches(way)
if open_way?(way)
polygon_from_way(way, @nodes)
else
line_string_from_way(way, @nodes)
end
end

def open_way?(way)
Underpass::Shape.open_way?(way)
end

def point_from_node(node)
Underpass::Shape.point_from_node(node)
end

def polygon_from_way(way, nodes)
Underpass::Shape.polygon_from_way(way, nodes)
end

def line_string_from_way(way, nodes)
Underpass::Shape.line_string_from_way(way, nodes)
end
end
end
2 changes: 1 addition & 1 deletion lib/underpass/ql/bounding_box.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Underpass
module QL
# Bounding box related utilities.
# Bounding box related utilities
class BoundingBox
# Returns the Overpass query language bounding box string
# when provided with an RGeo geometry
Expand Down
72 changes: 0 additions & 72 deletions lib/underpass/ql/parser.rb

This file was deleted.

15 changes: 0 additions & 15 deletions lib/underpass/ql/ql.rb

This file was deleted.

12 changes: 8 additions & 4 deletions lib/underpass/ql/query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ module QL
class Query
# Shortcut method that glues together the whole library.
# * +bounding_box+ an RGeo polygon
# * +query+ is the Overpass QL query
# * +query+ an Overpass QL query
def self.perform(bounding_box, query)
op_bbox = Underpass::QL::BoundingBox.from_geometry(bounding_box)
response = Underpass::QL::Request.new(query, op_bbox).run
Underpass::QL::Parser.new(response).parse.matches
op_bbox = Underpass::QL::BoundingBox.from_geometry(bounding_box)
request = Underpass::QL::Request.new(query, op_bbox)
api_response = Underpass::Client.perform(request)
response = Underpass::QL::Response.new(api_response)
matcher = Underpass::Matcher.new(response)

matcher.matches
end
end
end
Expand Down
14 changes: 4 additions & 10 deletions lib/underpass/ql/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

module Underpass
module QL
# Deals with performing the Overpass API request
# Prepares the Overpass query
class Request
API_URI = 'https://overpass-api.de/api/interpreter'
QUERY_TEMPLATE = <<-TEMPLATE
[out:json][timeout:25]BBOX;
(
Expand All @@ -20,14 +19,9 @@ def initialize(query, bbox)
@global_bbox ||= "[#{bbox}]"
end

# Performs the API request
def run
Net::HTTP.post_form(URI(API_URI), data: build_query)
end

private

def build_query
# Converts the object to a query string
# to be used in the next step (Client.perform)
def to_query
QUERY_TEMPLATE.sub('BBOX', @global_bbox)
.sub('QUERY', @overpass_query)
end
Expand Down
26 changes: 26 additions & 0 deletions lib/underpass/ql/response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

module Underpass
module QL
# Deals with parsing the API response into easily digestable
# nodes and ways objects
class Response
def initialize(api_response)
parsed_json = JSON.parse(api_response.body, symbolize_names: true)
@elements = parsed_json[:elements]
end

# Returns all node type elements from the response
def nodes
nodes = @elements.select { |e| e[:type] == 'node' }
nodes.map { |e| [e[:id], e] }.to_h
end

# Returns all way type elements from the response
def ways
ways = @elements.select { |e| e[:type] == 'way' }
ways.map { |e| [e[:id], e] }.to_h
end
end
end
end
15 changes: 7 additions & 8 deletions lib/underpass/ql/shape.rb → lib/underpass/shape.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
# frozen_string_literal: true

module Underpass
module QL
# Contains factories for various RGeo shapes from ways and nodes parsed
# with the Parser class
class Shape
def self.open_way?(way)
# Helper methods to convert shapes to RGeo
class Shape
class << self
def open_way?(way)
way[:nodes].first == way[:nodes].last
end

def self.polygon_from_way(way, nodes)
def polygon_from_way(way, nodes)
f = RGeo::Geographic.spherical_factory(srid: 4326)
f.polygon(line_string_from_way(way, nodes))
end

def self.line_string_from_way(way, nodes)
def line_string_from_way(way, nodes)
f = RGeo::Geographic.spherical_factory(srid: 4326)
f.line_string(
way[:nodes].map do |n|
Expand All @@ -23,7 +22,7 @@ def self.line_string_from_way(way, nodes)
)
end

def self.point_from_node(node)
def point_from_node(node)
f = RGeo::Geographic.spherical_factory(srid: 4326)
f.point(node[:lon], node[:lat])
end
Expand Down
22 changes: 22 additions & 0 deletions spec/underpass/client_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

require 'spec_helper'
require 'underpass'

describe Underpass::Client do
let(:request_double) { double }
let(:query) { 'query_test' }
let(:bbox) { 'bbox_test' }
subject { described_class }
describe '#perform' do
it 'posts the query to the API endpoint' do
allow(request_double).to receive(:to_query).and_return(
query + ' ' + bbox
)
stub = stub_request(:post, 'https://overpass-api.de/api/interpreter')
.with(body: /#{bbox}/)
subject.perform(request_double)
expect(stub).to have_been_requested
end
end
end
Loading