Skip to main content Link Search Menu Expand Document (external link)

Accessing Individual Features

A Feature represents a geographic element. This can be a point of interest like a mailbox or a restaurant, a park, a lake, a segment of a road, or a more abstract concept like a bus route.

Feature is the supertype of Node, Way and Relation. An object returned by a query is always one of these three subtypes. We’ll discuss their additional methods and characteristics in the next chapter.

For now, let’s focus on what you can do with features of any type.

Type, identity and equality

type() returns a FeatureType enum (NODE, WAY or RELATION).

id() returns the OSM identifier (a long). IDs are unique only within the feature type (which means a node and a way may have the same ID).

  • You can obtain a unique identifier that incorporates the type by using the FeatureId utility class.

  • id() may return 0 for anonymous nodes.

role() returns the role of the feature within a relation, if it was returned by a member query. This method returns null for features obtained via any other query (an empty string means the feature is a relation member, but has no assigned role in that particular relation).

equals(): Two features are equal if they have the same type and ID.

  • If two Feature objects are returned from different member queries, with different roles, they are considered equal as long as the above holds true.

  • Anonymous nodes are equal if they have the same location.

  • Never rely on == for equality. Queries may return identical objects for the same feature, but are by no means guaranteed to do so (even for the same node in a closed way).


Tags are key-value properties of a feature. Tags are stored as strings, but there are convenience methods to turn strings into numeric values.

Get a tag value by key:


Check for presence of a tag:

if(feature.hasTag("highway")) ...
if(feature.hasTag("shop", "bakery")) ...

Get all tags:

Tags tags = feature.tags();

Tags is a Consumable, a special kind of iterator that works like an SQL ResultSet:

    String key = tags.key();
    String value = tags.stringValue();
    int intValue = tags.intValue();

Tags can be turned into other data structures:

Map<String,Object> tagMap = tags.toMap();

Location and geometry

bounds() returns a feature’s bounding box. This is the smallest axis-aligned rectangle that encloses the feature’s geometry.

lon() and lat() return the longitude and latitude of a Feature; x() and y() return its Mercator-projected coordinates.

  • For ways and relations, this is the center point of the feature’s bounding box (not the feature’s centroid).

toGeometry() creates a JTS Geometry for this feature:

Use isArea() to check if the feature represents an area (always false for Node).

Parent relations

Any feature may belong to one or more relations.

parentRelations() returns the relations to which this feature belongs (or an empty collection if it is not part of any relations). An optional query string can be passed:

feature.parentRelations("r[route=bicycle]")  // only returns cycling routes 

Sometimes it is more convenient to inverse a query using with(): 0.2

library.relations("r[route=bicycle]").with(feature)  // same as above  

belongsTo(Feature parent) checks whether this feature belongs to a specific relation (the argument itself is of type Feature instead of Relation, because this method can also test if a Node is part of a Way; if parent is a Node, the result is always false).

belongsToRelation() checks whether a Feature is a member of any relation (without the need for querying).

Placeholder features

A placeholder feature is a feature that is not present in a dataset, but is referenced from other features in the same dataset. This commonly happens with regional abstracts: A dataset that covers only Germany may include a relation for a train route to Paris that goes through Cologne; however, the dataset most likely won’t include the train stops in Belgium and France (which are members of that relation). In order to maintain referential integrity, gol build creates placeholders for these missing features.

  • isPlaceholder() returns true.

  • type() and id() are valid.

  • isArea() always returns false, even if the feature might actually be an area.

  • A placeholder has none of the feature’s actual tags, but it may contain synthetic tags such as geodesk:error=missing that describe the problem further.

  • A placeholder Way has no nodes; a placeholder Relation has no members.

  • parentRelations() returns only relations that are contained in the library.

  • toGeometry() creates an empty Geometry.

  • All other geometry-related methods return an X-coordinates of Integer.MIN_VALUE to indicate that the feature’s location is invalid/unknown.

  • Its length and area are 0.