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

2

9

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.

Syphon answered 8/9, 2018 at 15:19 Comment(0)
S
8

{{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.

Solemnize answered 10/9, 2018 at 9:15 Comment(3)
Thanks for the answer @scai! I now added a Nominatim call via geopy in my and it's working. Only thing is that it's bid clumsy, cause it needs the right city relation in the first few results.Syphon
@Syphon would you mind sharing your implementation of this? I'm trying to replicate the geocodeArea behavior in my code and I though I was successful but I've found some scenarios where it doesn't render the same result.Shangrila
Hey @CristianoDalbem, the solution I ended up with is the code I added to the end of my question.Syphon
D
8

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

Disembogue answered 2/10, 2019 at 0:23 Comment(1)
this only works for big cities that have an area defining perimeter, not for small cities only with node not area named as suchHusserl

© 2022 - 2024 — McMap. All rights reserved.