rayon
rayon

Reputation: 600

How to get a {{geocodeArea: xxx }} query to work in python using overpy?

I want to find all pubs in a specific area using the Overpass API and selecting the area with geocodeArea.

Testing the following query on overpass-turbo.eu gives me the desired result:

{{geocodeArea:berlin}}->.searchArea;
(
  node["amenity"="pub"](area.searchArea);
  way["amenity"="pub"](area.searchArea);
  relation["amenity"="pub"](area.searchArea);
);
out body;
>;
out skel qt;

But when I implement that query in python using overpy...

import overpy

api = overpy.Overpass()

result = api.query("""
        {{geocodeArea:berlin}}->.searchArea;
        (
          node["amenity"="pub"](area.searchArea);
          way["amenity"="pub"](area.searchArea);
          relation["amenity"="pub"](area.searchArea);
        );
        out body;
        >;
        out skel qt;
    """)

print("Amenities in nodes: %d" % len(result.nodes))
print("Amenities in ways: %d" % len(result.ways))

... I get the following error:

Traceback (most recent call last):
  File "testOP.py", line 15, in <module>
    """)
  File "/usr/local/lib/python2.7/dist-packages/overpy/__init__.py", line 119, in query
    msgs=msgs
overpy.exception.OverpassBadRequest: Error: line 2: parse error: Unknown type "{" 
Error: line 2: parse error: An empty query is not allowed 
Error: line 2: parse error: ';' expected - '{' found. 

I guess that the problem has to do with the double curly braces, but so far escaping them and other variations didn't help.


Possible solution with Nominatim

Thanks to @scai I know now, that with {{geocodeArea:xxx}} overpass turbo only makes a geocode request. I decided to implement that in my program by myself using geopy and Nominatim:

from geopy.geocoders import Nominatim
import overpy

city_name = "berlin"

# Geocoding request via Nominatim
geolocator = Nominatim(user_agent="city_compare")
geo_results = geolocator.geocode(city_name, exactly_one=False, limit=3)

# Searching for relation in result set
for r in geo_results:
    print(r.address, r.raw.get("osm_type"))
    if r.raw.get("osm_type") == "relation":
        city = r
        break

# Calculating area id
area_id = int(city.raw.get("osm_id")) + 3600000000

# Excecuting overpass call
api = overpy.Overpass()
result = api.query("""
    area(%s)->.searchArea;
    (
      node["amenity"="pub"](area.searchArea);
      way["amenity"="pub"](area.searchArea);
      relation["amenity"="pub"](area.searchArea);
    );
    out body;
    """ % area_id)

# Printing no. of pubs in nodes and ways
print("Amenities in nodes: %d" % len(result.nodes))
print("Amenities in ways: %d" % len(result.ways))

The code ...

  1. Does a geocoding request to Nominatim
  2. Searches for the first element in the results (max. 3) which is a relation
  3. Adds 3600000000 to get the area id from the relation id

It's not a very clean solution and I wonder if it's possible to directly use the first result (which is mostly the city just as point) for my purposes. Hints are still welcome.

Upvotes: 9

Views: 4286

Answers (2)

carpinchosaurio
carpinchosaurio

Reputation: 1216

You can achieve similar results using the area filter instead of geocodeArea. For your example:

area[name="Berlin"]->.searchArea;
(
  node["amenity"="pub"](area.searchArea);
  way["amenity"="pub"](area.searchArea);
  relation["amenity"="pub"](area.searchArea);
);
out body;
>;
out skel qt;

This could work, but for other areas maybe you need to be more specific with the tags used in the area filter, more information in the Language Guide for Overpass

Upvotes: 8

scai
scai

Reputation: 21469

{{geocodeArea: xxx }} is a special feature of overpass turbo and not part of Overpass API. overpy uses Overpass API directly which means you can't use this keyword.

However {{geocodeArea: xxx }} just tells overpass turbo to perform a geocoding request, i.e. transform an address into a geographic location. You can do the same, e.g. by making a call to Nominatim, Photon or any other geocoder.

Upvotes: 8

Related Questions