How can I get all objects where given tag sits as a vertex?

Let’s say I am interested on which ways and areas on which barrier=log sits as a vertex

Is there any ready way to do this?

Or should I cobble something together with Osmosis?

I know I can do

[out:json][timeout:90][bbox:56.6,-4,56.8,-3];
node["barrier"="log"]; way(bn); out tags;

(thanks for help to kind people from OSM IRC!)

But this has problems: if there are more than few uses of tag it will fail. Querying all tags I want to check, even for tiny area would take months. And worldwide scan is no-go.

Also, if multipolygons are involved this will fail to find actual relevant objects.

(if anyone is curious - I am doing research for Some vertexes should require specific tags on parent way(s) · Issue #96 · ideditor/schema-builder · GitHub )

I’m not sure I entirely understand what you need.

I can’t imagine it solves the performance issue, but does something like this do what you want?

[out:json][timeout:25][bbox:{{bbox}}];
( 
  node["barrier"="block"];
  node["barrier"="log"];
  )->.a;  // group the result of several tags of interest into one result set
.a< ->.b; // non-specific recurse up
wr.b["highway"]->.b; // filter for tags of interest on parent
(.a;.b;); // return both sets
out geom;

Or do I have the wrong end of the stick here?

It does not solve performance issues which is my main limitation.

Overpass has serious problems with scale of a single city for many tags.

This QLever query finds 12,964 log vertices and their geometries globally:

PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX osmway: <https://www.openstreetmap.org/way/>
PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
SELECT * WHERE {
  ?way osmway:member/osmway:member_id ?log .
  ?log osmkey:barrier "log" .
  ?log geo:hasGeometry/geo:asWKT ?geometry .
}

that is actually promising, I suspect that when I get to more widely-used tags it will explode but that will be nice already

now I only need to figure how to call it via some kind of API

Here are 2.6 million highway=stop vertices in 17 seconds. I had to optimize the query a bit to use an inverse property path, so that it wouldn’t try to join against all the vertices in OSM:

PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX osmway: <https://www.openstreetmap.org/way/>
PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
SELECT * WHERE {
  ?stop osmkey:highway "stop" .
  ?stop ^(osmway:member/osmway:member_id) ?way .
  ?stop geo:hasGeometry/geo:asWKT ?geometry .
}

Here’s the API documentation. While the software is under active development, I wouldn’t recommend calling out to the API automatically from a public application in production, but it should be fine for less intensive usage.

To find the API endpoint in any QLever instance, click the :gear: button and look for the Backend URL. The public API endpoint for OSM is https://qlever.dev/api/osm-planet.

IT IS BEAUTIFUL AND ALMOST ENTIRELY SOLVES MY PROBLEM! THANKS!

Thanks so much! I am kind of amazed that I can go “give me all amenity=drinking_water and its associated ways across the entire world” and it actually responds.

But while I got pony already - is it by any chance possible to handle transparently multipolygons?

So for say Node: 13708695828 | OpenStreetMap that is on Way: 170983868 | OpenStreetMap list also Relation: 3897289 | OpenStreetMap multipolygon like an extra way associated with this node? Without causing huge performance issues? Is it a known problem/or having a clear solution?

(I checked QLever/Example queries - OpenStreetMap Wiki but I admit that I have nor done much more )

I ended modifying query a bit to get also tags, especially after this change it sometimes is 429ed for say highway=crossing

I tried adding limit but judging by speed of response it does not reduce how expensive query is

PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX osmway: <https://www.openstreetmap.org/way/>
PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
SELECT * WHERE {
  ?node osmkey:highway "stop" .
  ?node ^(osmway:member/osmway:member_id) ?way .
  ?node geo:hasGeometry/geo:asWKT ?geometry .
  ?way ?wayTagKey ?wayTagValue .
  FILTER(STRSTARTS(STR(?wayTagKey), STR(osmkey:)))
}
ORDER BY ?node
LIMIT 10

There are a couple methods to do this. Let’s start by adapting the query I provided earlier to return 7,534 gates connected to the perimeters of dog parks:

PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX osmway: <https://www.openstreetmap.org/way/>
PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
PREFIX osmrel: <https://www.openstreetmap.org/relation/>
SELECT * WHERE {
  ?gate osmkey:barrier "gate" .
  ?gate ^(osmway:member/osmway:member_id)/^(osmrel:member/osmrel:member_id)? ?dog_park .
  ?dog_park osmkey:leisure "dog_park" .
  ?gate geo:hasGeometry/geo:asWKT ?geometry .
}

The property path is starting to look like code golf, so let’s break it down. Normally, to find all the members of a set of relations, you use:

?multipolygon osmrel:member ?member .
?member osmrel:member_id ?ring .

If you don’t need a separate variable to filter on the member’s role or list position, then you can use a property path as shorthand:

?multipolygon osmrel:member/osmrel:member_id ?ring .

Similarly, you can get the vertex of a way using a property path:

?fence osmway:member/osmway:member_id ?gate .

Property paths are sort of like regular expressions. You can use operators like ? to make part of the path optional or + to repeat it one or more times. You can also use ^ to flip the path around. So instead of getting the fences first and then restricting the results to the fences that have child gates, you can get the gates first and restrict the set to the gates that have parent fences.

There’s a more performant method to getting more or less the same results. This very different query yields 7,646 gates along the perimeters of dog parks:

PREFIX ogc: <http://www.opengis.net/rdf#>
PREFIX geo: <http://www.opengis.net/ont/geosparql#>
PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
SELECT * WHERE {
  ?gate osmkey:barrier "gate" .
  ?gate ogc:sfTouches ?dog_park .
  ?dog_park osmkey:leisure "dog_park" .
  ?gate geo:hasGeometry/geo:asWKT ?geometry .
}

QLever has precomputed the DE-9IM spatial relationships between all the elements in OSM. sfTouches says that the gate’s geometry touches the perimeter of the dog park’s geometry. It doesn’t matter if the dog park is an area or a multipolygon, as long as the geometry is valid and closed. This is much simpler and faster, but it’s purely a spatial relationship, not a topological one. Apparently 112 gates are disconnected from the dog parks that they guard. Or maybe some of them are on a different level of a building and happen to coincide.

QLever is optimized for getting select information about the results in columnar format. It is possible to get all the tags of all the results, but this isn’t as natural for the triplestore architecture.

Figuring out which gates and dog parks to associate with each other is expensive enough that you only want QLever to do that work once per gate, not once for each tag of each dog park. To improve performance, a subquery isolates the first step from the step of getting all the tags (or geometry). Here are 28,775 tags on dog parks that have gates along their perimeters:

PREFIX ogc: <http://www.opengis.net/rdf#>
PREFIX osmkey: <https://www.openstreetmap.org/wiki/Key:>
SELECT * WHERE {
  {
    SELECT ?gate ?dog_park WHERE {
      ?gate osmkey:barrier "gate" .
      ?gate ogc:sfTouches ?dog_park .
      ?dog_park osmkey:leisure "dog_park" .
    }
  }
  ?dog_park ?key ?value .
  FILTER(STRSTARTS(STR(?key), STR(osmkey:)))
}

The results are in two columns, key and value. If you want each value in a dedicated column per key, the query needs to explicitly store each key’s value in a separate variable, one variable for each key you care about. You can wrap each statement in an OPTIONAL block. It isn’t possible to return an unlimited number of columns for any key that it comes across.

You could use a connected_to() query in GeoDesk (Python / Java).

(Let me know if you’d like a full example)