Skip to main content Link Search Menu Expand Document (external link)
This is the documentation for previous versions of GeoDesk (1.0 to 1.3). For the most recent version, please visit docs.geodesk.com.

Queries and Feature Collections

Geospatial applications typically work with subsets of features in a library, such as buildings in a town, or waterways in a particular region. These subsets are represented as feature collections, which are the result of queries.

  • Feature collections are lightweight objects that merely described what should be returned; they don’t actually contain any objects and take up minimal space. In other words, query execution is lazy: Features are fetched only once they are needed, in response to iteration or a call to count().

  • Feature collections can be ordered or unordered. Only the nodes of a way and the members of a relation are ordered; all other query results are returned in arbitrary order.

Start working with collections by creating a root collection, which contains all features in a given Geographic Object Library:

Features(const char* golFileName);

From this collection, you can create others:

Features france("path/to/france.gol");
...
Features shops = france("na[shop]");
Features thingsInParis = france(paris);  // Feature or geometry 
Features shopsInParis = shops & thingsInParis;

Filtering features

To select a subset of Features, add the constraint in parentheses, or apply a filter method. This always creates a new collection, leaving the original Features object unmodified.

By bounding box

Select the features whose bounding boxes intersect the given Box:

Features france("france.gol");
Box parisBounds = Box::ofWSEN(
    2.2, 48.8, 2.5, 48.9);
Features thingsInParis = france(parisBounds);

By type and tags

Apply a query written in GOQL (Geographic Object Query Language) to select features based on their type and tags:

Features restaurants = world(
    "na[amenity=restaurant]");   
    // nodes and areas
    
Nodes fireHydrants = world(
    "n[emergency=fire_hydrant]");
    // only nodes
    
Ways safeForCycling = world(
    "w[highway=cycleway,path,living_street],"        
    "w[highway][maxspeed < 30]");
    // linear ways

Using filter methods

Apply a spatial filter or topological filter, or a custom filter:

states.within(usa)
features("w[highway]").membersOf(route66)
parks.filter(MyFilters::containsWaterFountains);

Using set intersection

Select only features that are in both sets:

Features museums = world("na[tourism=museum]");
Features inParis = world.within(paris);
Features parisMuseums = museums(inParis);

Alternatively, you can use the & operator:

Features parisMuseums = museums & inParis;

Obtaining Feature objects

Simply iterate:

for(Feature hotel : hotels)
{
    std::cout << hotel["name"] << std::endl;
}

Create a std::vector, or populate an existing one:

std::vector<Feature> list = streets;
streets.addTo(myVector);

Check if the set is empty:

if (pubs.within(dublin))
    printf("Great, we can grab a beer in this town!");

if (!street.nodes("[traffic_calming=bump]"))
    printf("No speed bumps on this street.");

Obtaining a single Feature

first

Returns the first feature in a collection:

std::optional<Feature> city = france("n[place=city][name=Paris]").first();

Note that only the nodes of ways and members of relations are ordered collections; all others are unordered sets, which means you’ll receive a random feature if there are more than one. If the collection is empty, first() returns nullopt.

one

Returns the one and only feature of the collection. Throws a QueryException if the collection is empty or contains more than one feature.

Feature paris = world("n[place=city][name=Paris]").one();
    // will likely throw a QueryException, 
    // because there's also Paris, Texas

Testing for membership

To check if a feature belongs to a given set, use contains():

Features sushiRestaurants = 
    world("na[amenity=restaurant][cuisine=sushi]");

if (sushiRestaurants.contains(restaurant))
    std::cout << restaurant["name"] << " serves sushi";

Scalar queries

count

The total number of features in the collection:

printf("%d restaurants found.", restaurants.count());

area

The total area (square meters as double) of all areas in this set.

printf("London has %f square meters of parks.", 
    parks.within(london).area());

length

The total length (meters as double) of all features in this set. For areas, their circumference is used.

printf("The French motorway network is %f km long.", 
    france("w[highway=motorway]").length() / 1000);

Spatial filters

Features can be filtered by their spatial relationship to other geometric objects (typically a GEOSGeometry or another Feature).

containing

Selects features whose geometry contains A:

  • Every point of A is a point of the candidate feature, and the interiors of the two geometries have at least one point in common.
Features containing(Feature);
Features containing(GEOSGeometry);
Features containingXY(Coordinate);
Features containingLonLat(double, double);

For example:

// In which park (if any) is this statue of Claude Monet?
return features("a[leisure=park]")
    .containing(statueOfMonet).first();

// The county, state and country for this point -- should return 
// San Diego County, California, USA (in no particular order)  
return features("a[boundary=administrative][admin_level <= 6]")
    .containingLonLat(-117.25, 32.99); 

coveredBy

Selects features whose geometry is covered by A:

  • No point of the candidate feature’s geometry lies outside of A.
Features coveredBy(Feature);
Features coveredBy(GEOSGeometry);

crossing

Selects features whose geometry crosses A:

  • The geometries of A and the candidate feature have some (but not all) interior points in common
  • The dimension of the intersection must be less than the maximum dimension of the candidate and A.
Features crossing(Feature);
Features crossing(GEOSGeometry);

For example:

// All railway bridges across the Mississippi River
Features railwayBridges = features("w[railway][bridge]");    
return railwayBridges.crossing(mississippi);

intersecting

Selects features whose geometry intersects A:

  • The geometries of A and the candidate feature have at least one point in common.
Features intersecting(Feature);
Features intersecting(GEOSGeometry);

maxMetersFrom

Selects features whose distance to A is less or equal to m meters (measured between the closest points of the candidate feature and A).

Features maxMetersFrom(double, Feature);
Features maxMetersFrom(double, GEOSGeometry);
Features maxMetersFrom(double, Coordinate);
Features maxMetersFromLonLat(double, double, double);

For example:

// All bus stops within 500 meters of the given restaurant
Features nearbyBusStops = features("n[highway=bus_stop]")
    .maxMetersFrom(500, restaurant);
 
// All features within 3 km of the given point 
return features.maxMetersFromLonLat(3000, 76.41, 40.12); 

within

Selects features that lie entirely within A:

  • Every point of the candidate feature is a point in A, and their interiors have at least one point in common.
Features within(Feature);
Features within(GEOSGeometry);

Topological filters

These methods return a subset of those features that have a specific topological relationship with another Feature.

connectedTo

Selects all features that have at least one node (vertex) in common with the given Feature or Geometry.

Features connectedTo(Feature);
Features connectedTo(GEOSGeometry);

nodesOf

The nodes of the given way. Returns an empty set if the feature is a Node or Relation.

Nodes nodesOf(Feature);

membersOf

Features that are members of the given relation, or nodes of the given way. Returns an empty set if the feature is a Node.

Features membersOf(Feature);

parentsOf

Relations that have the given feature as a member, as well as ways to which the given node belongs.

Features parentsOf(Feature);

Custom filters

Use filter() with your own filter predicate:

// Find all parks whose area is at least 1 km²
// (one million square meters)
 
Features parks = world("a[leisure=park]");
Features largeParks = parks.filter([](Feature park)
    { return park.area() > 1'000'000; });

Important: The predicate must be threadsafe, as the query may be executed in parallel.