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 return0
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
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:
feature.stringValue("opening_hours")
feature.intValue("maxspeed")
If a tag is not present,
stringValue()
returns an empty string.If a tag is not present, or its value is not a valid number,
intValue()
anddoubleValue()
return0
.
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
:
while(tags.next())
{
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:
Point
for aNode
LineString
orLinearRing
for a non-areaWay
Polygon
for aWay
that represents an areaPolygon
orMultiPolygon
for an areaRelation
GeometryCollection
for any otherRelation
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()
returnstrue
.isArea()
always returnsfalse
, 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 placeholderRelation
has no members.parentRelations()
returns only relations that are contained in the library.toGeometry()
creates an emptyGeometry
.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
.