underpass

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.

# Require the library if it's not autoloaded
require 'underpass'

# Define a polygon to be used as bounding box
wkt = <<-WKT
  POLYGON ((
    23.669 47.65,
    23.725 47.65,
    23.725 47.674,
    23.669 47.674,
    23.669 47.65
  ))
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
# 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)
matcher      = Underpass::Matcher.new(response)

# We'll have our matches in
matcher.matches

Relations

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


Comprehensive Examples with Real Data

The following examples demonstrate various features of the library using real data from OpenStreetMap. These examples cover all return types and functionality.

Example 1: Node Queries - Restaurants in Bucharest

Query for restaurants (nodes) in central Bucharest:

require 'underpass'

# Define a bounding box for central Bucharest
wkt = <<-WKT
  POLYGON ((
    26.08 44.42,
    26.12 44.42,
    26.12 44.45,
    26.08 44.45,
    26.08 44.42
  ))
WKT

bbox = RGeo::Geographic.spherical_factory.parse_wkt(wkt)

# Query for restaurants (nodes)
query = 'node["amenity"="restaurant"];'
features = Underpass::QL::Query.perform(bbox, query)

# Process results
features.each do |f|
  puts "#{f.properties[:name]} - #{f.properties[:cuisine]}"
  puts "  Geometry: #{f.geometry.geometry_type}"  # => Point
  puts "  Type: #{f.type}"                        # => node
  puts "  ID: #{f.id}"
end

# Sample output:
# Pizza Hut - pizza
#   Geometry: Point
#   Type: node
#   ID: 286859702

Example 2: Way Queries - LineString (Roads)

Query for primary roads (ways - LineString geometries):

require 'underpass'

# Using the same Bucharest bounding box
query = 'way["highway"="primary"];'
roads = Underpass::QL::Query.perform(bbox, query)

roads.each do |road|
  puts "#{road.properties[:name]}"
  puts "  Geometry: #{road.geometry.geometry_type}"  # => LineString
  puts "  Type: #{road.type}"                        # => way
end

# Sample output:
# Piața Unirii
#   Geometry: LineString
#   Type: way

Example 3: Way Queries - Polygon (Buildings)

Query for buildings (ways - Polygon geometries):

require 'underpass'

query = 'way["building"="yes"];'
buildings = Underpass::QL::Query.perform(bbox, query)

buildings.each do |building|
  puts "#{building.properties[:name]}"
  puts "  Geometry: #{building.geometry.geometry_type}"  # => Polygon
  puts "  Type: #{building.type}"                        # => way
end

# Sample output:
# Unirea Shopping Center
#   Geometry: Polygon
#   Type: way

Example 4: Way Queries - Polygon (Parks)

Query for parks (ways - Polygon geometries):

require 'underpass'

query = 'way["leisure"="park"];'
parks = Underpass::QL::Query.perform(bbox, query)

parks.each do |park|
  puts "#{park.properties[:name]}"
  puts "  Geometry: #{park.geometry.geometry_type}"  # => Polygon
  puts "  Type: #{park.type}"                        # => way
end

# Sample output:
# Parcul Cișmigiu
#   Geometry: Polygon
#   Type: way

Example 5: Relation Queries - Multipolygon (Lakes)

Query for lakes as multipolygon relations in Romanian mountains:

require 'underpass'

# Define a bounding box for Romanian mountain lakes area
wkt_mountains = <<-WKT
  POLYGON ((
    25.0 45.5,
    26.0 45.5,
    26.0 46.0,
    25.0 46.0,
    25.0 45.5
  ))
WKT

bbox_mountains = RGeo::Geographic.spherical_factory.parse_wkt(wkt_mountains)

# Query for lakes as multipolygon relations
query = 'relation["type"="multipolygon"]["water"="lake"];'
lakes = Underpass::QL::Query.perform(bbox_mountains, query)

lakes.each do |lake|
  puts "#{lake.properties[:name]}"
  puts "  Geometry: #{lake.geometry.geometry_type}"  # => Polygon or MultiPolygon
  puts "  Type: #{lake.type}"                        # => relation
end

# Sample output:
# Lacul Gémvári
#   Geometry: MultiPolygon
#   Type: relation

Example 6: Relation Queries - MultiLineString (Routes)

Query for bus routes (relations - MultiLineString geometries):

require 'underpass'

# Using the Bucharest bounding box
query = 'relation["type"="route"]["route"="bus"];'
routes = Underpass::QL::Query.perform(bbox, query)

routes.each do |route|
  puts "#{route.properties[:name]}"
  puts "  Geometry: #{route.geometry.geometry_type}"  # => MultiLineString
  puts "  Type: #{route.type}"                        # => relation
end

# Sample output:
# Bus 135: C.E.T. Sud Vitan → C.F.R. Constanța
#   Geometry: MultiLineString
#   Type: relation

Example 7: Area Queries - Using perform_in_area

Query within a named area instead of a bounding box:

require 'underpass'

# Query within a named area (note: no semicolon at end)
query = 'node["amenity"="cafe"]'
cafes = Underpass::QL::Query.perform_in_area('Bucharest', query)

cafes.each do |cafe|
  puts "#{cafe.properties[:name]}"
  puts "  Geometry: #{cafe.geometry.geometry_type}"  # => Point
  puts "  Type: #{cafe.type}"                        # => node
end

Example 8: Around Queries - Proximity Search

Find elements within a radius of a point:

require 'underpass'

# Find restaurants within 500m of University Square, Bucharest
lat = 44.4325
lon = 26.1025

query = Underpass::QL::Builder.new
           .node(amenity: 'restaurant')
           .around(500, lat, lon)
           .to_ql

restaurants_nearby = Underpass::QL::Query.perform(bbox, query)

restaurants_nearby.each do |restaurant|
  puts "#{restaurant.properties[:name]} - #{restaurant.properties[:cuisine]}"
  puts "  Geometry: #{restaurant.geometry.geometry_type}"  # => Point
  puts "  Type: #{restaurant.type}"                        # => node
end

# Sample output:
# Caru' cu Bere - balkan;grill;regional;steak_house
#   Geometry: Point
#   Type: node

Example 9: Builder DSL - Multiple Types

Using the Builder DSL to query multiple element types:

require 'underpass'

# Using Builder DSL to query multiple types
builder = Underpass::QL::Builder.new
          .node(amenity: 'restaurant')
          .way(amenity: 'restaurant')
          .relation(amenity: 'restaurant')

all_restaurants = Underpass::QL::Query.perform(bbox, builder)

all_restaurants.each do |restaurant|
  puts "#{restaurant.properties[:name]}"
  puts "  Geometry: #{restaurant.geometry.geometry_type}"  # Point, Polygon, or MultiPolygon
  puts "  Type: #{restaurant.type}"                        # node, way, or relation
end

Example 10: Builder DSL - Multiple Tag Filters

Query with multiple tag filters:

require 'underpass'

# Query with multiple tag filters
builder = Underpass::QL::Builder.new
          .way('heritage:operator': 'lmi', 'ref:ro:lmi': 'MM-II-m-B-04508')

heritage = Underpass::QL::Query.perform(bbox, builder)

heritage.each do |building|
  puts "#{building.properties[:name]}"
  puts "  Geometry: #{building.geometry.geometry_type}"  # => Polygon
  puts "  Type: #{building.type}"                        # => way
end

Example 11: Filtering - Post-Query Filtering

Filter results by tag properties after querying:

require 'underpass'

# Query all amenities
query = 'node["amenity"];'
amenities = Underpass::QL::Query.perform(bbox, query)

# Filter for restaurants only
restaurants = Underpass::Filter.new(amenities).where(amenity: 'restaurant')
puts "Restaurants: #{restaurants.size}"

# Filter for multiple amenity types (OR)
food_places = Underpass::Filter.new(amenities).where(amenity: %w[restaurant cafe bar])
puts "Food places: #{food_places.size}"

# Filter with regex
italian_places = Underpass::Filter.new(amenities).where(cuisine: /italian/i)
puts "Italian places: #{italian_places.size}"

# Reject banks
no_banks = Underpass::Filter.new(amenities).reject(amenity: 'bank')
puts "Non-bank amenities: #{no_banks.size}"

Example 12: GeoJSON Export

Convert results to GeoJSON for use with web mapping libraries:

require 'underpass'
require 'json'

# Query for restaurants
query = 'node["amenity"="restaurant"];'
restaurants = Underpass::QL::Query.perform(bbox, query)

# Convert to GeoJSON
geojson = Underpass::GeoJSON.encode(restaurants)

# Serialize to JSON file
File.write('restaurants.geojson', JSON.pretty_generate(geojson))

# GeoJSON structure:
# {
#   "type" => "FeatureCollection",
#   "features" => [
#     {
#       "type" => "Feature",
#       "geometry" => { "type" => "Point", "coordinates" => [26.1025, 44.4325] },
#       "properties" => { "name" => "Pizza Hut", "amenity" => "restaurant", "cuisine" => "pizza" },
#       "id" => 286859702
#     },
#     ...
#   ]
# }

Example 13: Multiple Types in One Query

Query for nodes, ways, and relations in a single query:

require 'underpass'

# Query for nodes, ways, and relations in one query
query = <<-QL
  node["amenity"="restaurant"];
  way["amenity"="restaurant"];
  relation["type"="route"];
QL

multi_results = Underpass::QL::Query.perform(bbox, query)

multi_results.each do |feature|
  puts "#{feature.properties[:name] || 'Unnamed'}"
  puts "  Geometry: #{feature.geometry.geometry_type}"
  puts "  Type: #{feature.type}"
end

Example 14: NWR Shorthand

Using the nwr (node/way/relation) shorthand:

require 'underpass'

# Using nwr (node/way/relation) shorthand
builder = Underpass::QL::Builder.new.nwr(name: 'Universitate')
results = Underpass::QL::Query.perform(bbox, builder)

results.each do |feature|
  puts "#{feature.properties[:name]}"
  puts "  Geometry: #{feature.geometry.geometry_type}"  # Point, LineString, or Polygon
  puts "  Type: #{feature.type}"                        # node, way, or relation
end

# Sample output:
# Universitate
#   Geometry: Point
#   Type: node

Example 15: Around with RGeo Point

Using an RGeo point object for around queries:

require 'underpass'

# Using RGeo point for around query
point = RGeo::Geographic.spherical_factory(srid: 4326).point(26.1025, 44.4325)

query = Underpass::QL::Builder.new
           .node(amenity: 'cafe')
           .around(300, point)
           .to_ql

cafes_nearby = Underpass::QL::Query.perform(bbox, query)

cafes_nearby.each do |cafe|
  puts "#{cafe.properties[:name]}"
  puts "  Geometry: #{cafe.geometry.geometry_type}"  # => Point
  puts "  Type: #{cafe.type}"                        # => node
end

# Sample output:
# Sfinx Espresso Bar
#   Geometry: Point
#   Type: node

Summary of Return Types

Example Geometry Type Element Type Description
1 Point node Restaurants
2 LineString way Primary roads
3 Polygon way Buildings
4 Polygon way Parks
5 Polygon/MultiPolygon relation Lakes (multipolygon)
6 MultiLineString relation Bus routes
7 Point node Cafes (area query)
8 Point node Restaurants (around query)
9 Point/Polygon/MultiPolygon node/way/relation All restaurants
10 Polygon way Heritage buildings
11 Point node Filtered amenities
12 Point/Polygon/etc. node/way/relation GeoJSON export
13 Point/Polygon/MultiLineString node/way/relation Multiple types
14 Point/LineString/Polygon node/way/relation NWR shorthand
15 Point node Cafes (around with RGeo)