Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get the geometry of a relation from OpenStreetMap?

I have a numpy.ndarray of geocoordinates and I want to see which of them lie inside Alaska. For that, I want to get the multipolygon of the state of Alaska from OpenStreetMap and then use some shape library (probably Shapely) to query which of the points lie inside. However, I am stuck at step 1: I cannot get the geometry of the multipolygon. I have the OSMPythonTools installed (but if there is a better tool for the job, I'm happy to switch) and I can query them for Alaska like this

from OSMPythonTools.nominatim import Nominatim
from OSMPythonTools.api import Api

nominatim = Nominatim()
api = Api()

alaska_id = nominatim.query("Alaska, United States of America").areaId()

alaska = api.query('relation/{:}'.format(alaska_id - 3600000000))

I then want to get the geometry of this object using alaska.geometry(), but that returns only

Exception: [OSMPythonTools.Element] Cannot build geometry: geometry information not included. (way/193430587)

This exception is raised because the ways constituting the outer boundary of Alaska in alaska.__members() do not contain geometry, and then the API assumes that a relation has been encountered and raises a confusing exception. I assume that I need to run an intermediate step that queries all these members from OSM and loads their geometry, how do I do that?

Alternatively, I know that the Overpass API can return geometries, so I assume something like

query = overpassQueryBuilder(
    area=alaska_id,
    elementType=['relation'],
    selector='"id"="1116270"',
    includeGeometry=True)

might work, but this specific query is empty and using the Overpass API for a single Relation object whose ID I know feels very wrong, is it not?

like image 943
Anaphory Avatar asked Oct 28 '25 10:10

Anaphory


1 Answers

I found a GIS question on SX describing how to convert an overpass query result into a multipolygon – well, actually just a list of polygons, but I do know how to convert those to a multipolygon.

Using the Overpass query by element ID I can actually get just a single object, so Overpass is not a bad API for this task.

That linked question uses overpy instead of OSMPythonTools, but OSMPythonTools insists on a boundary box or area to limit the search, and it also applies some magic to build a query from its parameters instead of just taking the provided query, so switching libraries may be the right thing to do.

The resulting code is then surprisingly long for what should be a simple query, and converting every coordinate pair in my ndarray into a shapely.geometry.Point sounds inefficient, but at least this works.

import overpy
import shapely.geometry as geometry
from shapely.ops import linemerge, unary_union, polygonize

query = """[out:json][timeout:25];
rel(1116270);
out body;
>;
out skel qt; """
api = overpy.Overpass()
result = api.query(query)

lss = [] #convert ways to linstrings

for ii_w,way in enumerate(result.ways):
    ls_coords = []

    for node in way.nodes:
        ls_coords.append((node.lon,node.lat)) # create a list of node coordinates

    lss.append(geometry.LineString(ls_coords)) # create a LineString from coords


merged = linemerge([*lss]) # merge LineStrings
borders = unary_union(merged) # linestrings to a MultiLineString
polygons = list(polygonize(borders))
alaska = geometry.MultiPolygon(polygons)

assert alaska.contains(geometry.Point(-147.7798220, 64.8564400))
like image 118
Anaphory Avatar answered Oct 29 '25 23:10

Anaphory



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!