Skip to content

Commit

Permalink
Merge 42a73df into 5469a3f
Browse files Browse the repository at this point in the history
  • Loading branch information
janosrusiczki committed Oct 11, 2019
2 parents 5469a3f + 42a73df commit 53707ff
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 45 deletions.
27 changes: 23 additions & 4 deletions .github/workflows/gempush.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,36 @@ name: Ruby Gem
on: [push]

jobs:
build:
rubocop:
name: Rubocop
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@master

- name: Set up Ruby 2.6
uses: actions/setup-ruby@v1
with:
ruby-version: 2.6.x

- name: Run Rubocop
run: |
bundle install
bundle exec rubocop
rspec:
name: Specs
runs-on: ubuntu-latest

needs: rubocop

steps:
- uses: actions/checkout@master

- name: Set up Ruby 2.6
uses: actions/setup-ruby@v1
with:
version: 2.6.x
ruby-version: 2.6.x

- name: Run specs
run: |
Expand All @@ -24,15 +43,15 @@ jobs:
name: Publish
runs-on: ubuntu-latest

needs: build
needs: rspec

steps:
- uses: actions/checkout@master

- name: Set up Ruby 2.6
uses: actions/setup-ruby@v1
with:
version: 2.6.x
ruby-version: 2.6.x

- name: Publish to RubyGems
if: github.ref == 'master'
Expand Down
64 changes: 40 additions & 24 deletions lib/underpass/matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,62 @@ 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
@nodes = response.nodes
@ways = response.ways
@relations = response.relations
@matches = nil
end

@ways.each_value do |way|
@matches << way_matches(way) if way.key?(:tags)
def matches
unless @matches
@matches = []
add_node_matches
add_way_matches
add_relation_matches
end
@matches
end

private

def way_matches(way)
if open_way?(way)
polygon_from_way(way, @nodes)
else
line_string_from_way(way, @nodes)
def add_node_matches
@nodes.each_value do |node|
@matches << Shape.point_from_node(node) if node.key?(:tags)
end
end

def open_way?(way)
Underpass::Shape.open_way?(way)
def add_way_matches
@ways.each_value do |way|
@matches << way_match(way) if way.key?(:tags)
end
end

def point_from_node(node)
Underpass::Shape.point_from_node(node)
def add_relation_matches
@relations.each_value do |relation|
@matches.push(*relation_match(relation)) if relation.key?(:tags)
end
end

def polygon_from_way(way, nodes)
Underpass::Shape.polygon_from_way(way, nodes)
def relation_match(relation)
matches = []
relation[:members].each do |member|
case member[:type]
when 'node'
matches << Shape.point_from_node(@nodes[member[:ref]])
when 'way'
matches << way_match(@ways[member[:ref]])
end
end
matches
end

def line_string_from_way(way, nodes)
Underpass::Shape.line_string_from_way(way, nodes)
def way_match(way)
if Shape.open_way?(way)
Shape.polygon_from_way(way, @nodes)
else
Shape.line_string_from_way(way, @nodes)
end
end
end
end
19 changes: 14 additions & 5 deletions lib/underpass/ql/bounding_box.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,20 @@ module Underpass
module QL
# Bounding box related utilities
class BoundingBox
# Returns the Overpass query language bounding box string
# when provided with an RGeo geometry
def self.from_geometry(geometry)
r_bb = RGeo::Cartesian::BoundingBox.create_from_geometry(geometry)
"bbox:#{r_bb.min_y},#{r_bb.min_x},#{r_bb.max_y},#{r_bb.max_x}"
class << self
# Returns the Overpass query language bounding box string
# when provided with WKT (Well Known Text)
def from_wkt(wkt)
geometry = RGeo::Geographic.spherical_factory.parse_wkt(wkt)
from_geometry(geometry)
end

# Returns the Overpass query language bounding box string
# when provided with an RGeo geometry
def from_geometry(geometry)
r_bb = RGeo::Cartesian::BoundingBox.create_from_geometry(geometry)
"bbox:#{r_bb.min_y},#{r_bb.min_x},#{r_bb.max_y},#{r_bb.max_x}"
end
end
end
end
Expand Down
27 changes: 23 additions & 4 deletions lib/underpass/ql/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,33 @@ def initialize(api_response)

# 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
mapped_hash('node')
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
mapped_hash('way')
end

# Returns all relation type elements from the response
def relations
mapped_hash('relation')
end

private

# Returns a hash of elements where the key is the element's id
# This makes it easy to quickly access an element knowing its id
def mapped_hash(type)
mapped_elements = elements_of_type(type).map do |element|
[element[:id], element]
end
mapped_elements.to_h
end

# Filters elements of a certain type (node, way, relation)
def elements_of_type(type)
@elements.select { |e| e[:type] == type }
end
end
end
Expand Down
56 changes: 56 additions & 0 deletions spec/underpass/matcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
before do
allow(response_double).to receive(:nodes).and_return(nodes)
allow(response_double).to receive(:ways).and_return(ways)
allow(response_double).to receive(:relations).and_return(relations)
end

subject { described_class.new(response_double) }
Expand All @@ -24,6 +25,7 @@
}
end
let(:ways) { {} }
let(:relations) { {} }

it 'calls point_from_node for nodes with tags and returns matches' do
expect(Underpass::Shape).to receive(:point_from_node)
Expand All @@ -34,6 +36,7 @@

context 'there are ways with tags' do
let(:nodes) { {} }
let(:relations) { {} }

context 'ways are polygons' do
let(:ways) do
Expand Down Expand Up @@ -67,5 +70,58 @@
end
end
end

context 'there are relations with tags' do
let(:nodes) { {} }
let(:ways) { {} }

context 'relation members are nodes' do
let(:relations) do
{
a: {
members: [
{
type: 'node'
},
{
type: 'node'
}
],
tags: {}
}
}
end

it 'calls point_from_node and returns matches' do
expect(Underpass::Shape).to receive(:point_from_node)
.twice.and_return('test')
expect(subject.matches.size).to eq(2)
end
end

context 'relation members are ways' do
let(:relations) do
{
a: {
members: [
{
type: 'way'
},
{
type: 'way'
}
],
tags: {}
}
}
end

it 'calls way_match and returns matches' do
expect(subject).to receive(:way_match)
.twice.and_return('test')
expect(subject.matches.size).to eq(2)
end
end
end
end
end
16 changes: 12 additions & 4 deletions spec/underpass/ql/bounding_box_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@
require 'underpass'

describe Underpass::QL::BoundingBox do
let(:wkt) { 'POLYGON ((1.0 10.0, 2.0 10.0, 2.0 11.0, 1.0 11.00, 1.0 10.0))' }
let(:bbox) { 'bbox:10.0,1.0,11.0,2.0' }

subject { described_class }

describe '#from_wkt' do
it 'returns the correct bounding box string' do
expect(subject.from_wkt(wkt)).to eq(bbox)
end
end

describe '#from_geometry' do
it 'returns the correct bounding box string' do
geometry = RGeo::Geographic.spherical_factory.parse_wkt(
'POLYGON ((1.0 10.0, 2.0 10.0, 2.0 11.0, 1.0 11.00, 1.0 10.0))'
)
expect(subject.from_geometry(geometry)).to eq('bbox:10.0,1.0,11.0,2.0')
geometry = RGeo::Geographic.spherical_factory.parse_wkt(wkt)
expect(subject.from_geometry(geometry)).to eq(bbox)
end
end
end
23 changes: 23 additions & 0 deletions test_relations.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

# Use me by doing:
# bundle console
# source './test-relatios.rb'

require 'underpass'
wkt = <<-WKT
POLYGON ((
23.65 47.65,
23.6995 47.65,
23.6995 47.71,
23.65 47.71,
23.65 47.65
))
WKT
op_query = 'relation["name"="Árok"];'
op_bbox = Underpass::QL::BoundingBox.from_wkt(wkt)
request = Underpass::QL::Request.new(op_query, op_bbox)
api_response = Underpass::Client.perform(request)
response = Underpass::QL::Response.new(api_response)
matcher = Underpass::Matcher.new(response)
matcher
36 changes: 32 additions & 4 deletions usage-examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## More usage examples

### Step by step flow

The following lets you see the flow of the library.
You can inspect the objects returned at each step for more information.

Expand All @@ -20,14 +22,12 @@ wkt = <<-WKT
))
WKT

# Create an RGeo bounding box in which the query will run
bbox = RGeo::Geographic.spherical_factory.parse_wkt(wkt)

# Define the Overpass QL query
op_query = 'way["heritage:operator"="lmi"]["ref:ro:lmi"="MM-II-m-B-04508"];'

# We won't use the Underpass::QL::Query convenience class
op_bbox = Underpass::QL::BoundingBox.from_geometry(bounding_box)
# Note that we pass the wkt directly to the from_wkt method
op_bbox = Underpass::QL::BoundingBox.from_wkt(wkt)
request = Underpass::QL::Request.new(op_query, op_bbox)
api_response = Underpass::Client.perform(request)
response = Underpass::QL::Response.new(api_response)
Expand All @@ -36,3 +36,31 @@ matcher = Underpass::Matcher.new(response)
# We'll have our matches in
matcher.matches
```

### Relations

```ruby
require 'underpass'

wkt = <<-WKT
POLYGON ((
23.65 47.65,
23.6995 47.65,
23.6995 47.71,
23.65 47.71,
23.65 47.65
))
WKT

op_query = 'relation["name"="Árok"];'
op_bbox = Underpass::QL::BoundingBox.from_wkt(wkt)

request = Underpass::QL::Request.new(op_query, op_bbox)
api_response = Underpass::Client.perform(request)
response = Underpass::QL::Response.new(api_response)
matcher = Underpass::Matcher.new(response)
```

### Tools

* [Bounding Box](http://tools.geofabrik.de/calc/#type=geofabrik_standard)

0 comments on commit 53707ff

Please sign in to comment.