Skip to content

Commit

Permalink
Refactor into a more adapter like pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
janosrusiczki committed Sep 6, 2019
1 parent 1994430 commit cbdf5ac
Show file tree
Hide file tree
Showing 19 changed files with 311 additions and 261 deletions.
18 changes: 14 additions & 4 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 Down
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

0 comments on commit cbdf5ac

Please sign in to comment.