Hi, I want to check for thousands of points (lat, lon), if they are located inside a building polygon using the overpass API.

I know how to do it for one point (eg: https://help.openstreetmap.org/questions/55529/how-to-detect-if-point-is-inside-a-building-polygon-with-overpass) but I don't know how to do it without sending requests during hours to the overpass API.

I'm currently using this request for one point:

way(around:5, {lat}, {lon})[building];
if(count(ways) < 1) {{
    way(around:10, {lat}, {lon})[building];
  if(count(ways) < 1) {{
       way(around:15, {lat}, {lon})[building];
    if(count(ways) < 1) {{
          way(around:20, {lat}, {lon})[building];
         if(count(ways) < 1) {{
             way(around:25, {lat}, {lon})[building];
         }}}} }}}}
out geom;

where I check gradually up until 25 meters for close buildings. I don't think there is a tool in overpass for checking if the point at lat, lon is located inside any of the close buildings (except creating temporary areas and using is_in?). So, I use the shapely python library for that. The full process for the thousand points takes hours.

How could I do it efficiently?

I could query all the close buildings of all the points in one request but I don't know how to keep a reference to the node from which a building is close. Thus, I will be forced to check if a node is inside any buildings that I got, no matter from which node the building is close. In this case, it will also not be efficient.

Does anyone have an idea for the query to use? Or on how to keep the reference?

Thanks!

Vucod


The full code:

from OSMPythonTools.overpass import Overpass
import pandas as pd
from shapely.geometry import Point
from shapely.geometry.polygon import Polygon

def checkIfOnBuilding(lat, lon):
"""
Check for building in a 25 radius
lat, lon  as string
"""
point = Point(lat, lon)

result = overpass.query(f"""
way(around:5, {lat}, {lon})[building];
if(count(ways) < 1) {{
    way(around:10, {lat}, {lon})[building];
  if(count(ways) < 1) {{
       way(around:15, {lat}, {lon})[building];
    if(count(ways) < 1) {{
          way(around:20, {lat}, {lon})[building];
         if(count(ways) < 1) {{
             way(around:25, {lat}, {lon})[building];
         }}}} }}}}
out geom;
""", timeout=500)

dict_ = result.toJSON()['elements']
df = pd.DataFrame(dict_)

if len(df) > 0:
    for i, row in df.iterrows():
        nodes = [(node["lat"], node["lon"]) for node in row.geometry]
        polygon = Polygon(nodes)
        res = polygon.contains(point)
        if res:
            break
else:
    res = False

return res

asked 26 Apr, 12:59

Vucod's gravatar image

Vucod
2612
accept rate: 0%

edited 26 Apr, 13:01

There's not a contains operation in the Overpass-API query language.

You can inject text into the result using the make statement: https://dev.overpass-api.de/blog/textual_data.html

(02 May, 23:00) maxerickson

I finally found the way to do it in one request which is a lot faster. I just use my request for every point and I separate each sub request by a out count;. Therefore, as the order of the sets is kept and I have a delimiter between the sets, I can find back for each point the buildings that are close. But there is still maybe a cleaner way to do it using only overpass API?

The full code using python3 :

from OSMPythonTools.overpass import Overpass
import pandas as pd
from shapely.geometry import Point
from shapely.geometry.polygon import Polygon

def onBuilding(lats: str, lons: str) -> List[bool]:
"""
Check if coordinates are on a building.
Works only for buildings that have a node
closer than 25 meters from the coordinate.
"""

query = """    """
for lat, lon in zip(lats, lons):
    query += f"""
    way(around:5, {lat}, {lon})[building];
    if(count(ways) < 1) {{
        way(around:10, {lat}, {lon})[building];
      if(count(ways) < 1) {{
           way(around:15, {lat}, {lon})[building];
        if(count(ways) < 1) {{
              way(around:20, {lat}, {lon})[building];
             if(count(ways) < 1) {{
                 way(around:25, {lat}, {lon})[building];
             }}}} }}}}
    out count;
    out geom;
    """
result = overpass.query(query, timeout=15000)
elements = result.toJSON()["elements"]

list_ = []
for lat, lon in zip(lats, lons):
    point = Point(lat, lon)
    onBuilding = False
    sizeSet = int(elements[0]["tags"]['total'])
    elements.pop(0)
    for item in range(sizeSet):
        element = elements[item]
        nodes = [(node["lat"], node["lon"])
                 for node in element["geometry"]]
        polygon = Polygon(nodes)
        onBuilding = polygon.contains(point)
        if onBuilding:
            print("--> the coordinate is inside a building")
            break
    del elements[:sizeSet]
    list_.append(onBuilding)
return list_
permanent link

answered 02 May, 19:51

Vucod's gravatar image

Vucod
2612
accept rate: 0%

Your answer
toggle preview

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Markdown Basics

  • *italic* or _italic_
  • **bold** or __bold__
  • link:[text](http://url.com/ "title")
  • image?![alt text](/path/img.jpg "title")
  • numbered list: 1. Foo 2. Bar
  • to add a line break simply add two spaces to where you would like the new line to be.
  • basic HTML tags are also supported

Question tags:

×381
×63
×56
×7

question asked: 26 Apr, 12:59

question was seen: 218 times

last updated: 02 May, 23:00

powered by OSQA