Summary
Add a new geometry: relation_members option to YAML custom maps that allows processing individual members of OSM relations as separate features. Instead of emitting one feature per relation, this emits one feature per qualifying member, using each member's geometry and tags.
Key Features:
- Select relations using existing
include_when/exclude_when filters
- Emit one feature per qualifying member (way/node)
- Filter members by type, role, and tags
- Add member-level attributes from member tags
- Support inline script expressions for member filtering and attributes
Problem
Currently, when working with OSM relations in YAML custom maps, you can only emit a single feature for the entire relation. However, many use cases require processing individual members of a relation as separate features:
- Route relations (
type=route): Extract individual road/rail segments that make up a route
- Boundary relations (
type=boundary): Extract individual boundary segments
- Public transport routes: Extract individual stops or route segments
- Waterway relations: Extract individual river segments
For example, a type=route relation with route=railway might contain 50 way members representing different track segments. Currently, you'd get one feature for the relation, but you need 50 features - one for each segment - with each segment's own geometry and tags.
Proposed Solution
Add a new geometry: relation_members option that:
- Selects relations using the existing
include_when/exclude_when filters (applied to relation tags)
- Emits one feature per qualifying member instead of one feature per relation
- Uses each member's geometry (way → line/polygon, node → point)
- Supports member-level filtering by type, role, and tags
- Supports member-level attributes from member tags
Use Cases
1. Railway Route Segments
Extract individual track segments from railway route relations:
layers:
- id: railway_segments
features:
- source: osm
geometry: relation_members
include_when:
type: route
route: railway
member_types: [way]
member_include_when:
railway: rail
attributes:
- key: route_name
tag_value: name
member_attributes:
- key: segment_ref
tag_value: ref
2. Administrative Boundary Segments
Extract individual boundary segments from administrative boundary relations:
layers:
- id: boundary_segments
features:
- source: osm
geometry: relation_members
include_when:
type: boundary
admin_level: "6"
member_types: [way]
member_roles: ["outer"]
member_attributes:
- key: boundary_type
tag_value: boundary
3. Bus Route Stops
Extract individual stops from public transport route relations:
layers:
- id: bus_stops
features:
- source: osm
geometry: relation_members
include_when:
type: route
route: bus
member_types: [node]
member_roles: ["stop"]
member_include_when:
public_transport: stop_position
attributes:
- key: route_ref
tag_value: ref
member_attributes:
- key: stop_name
tag_value: name
Configuration
New Geometry Type
geometry: relation_members
When used, the feature selection (include_when/exclude_when) applies to OSM relations, but features are emitted for each qualifying member using the member's geometry.
New Configuration Fields
All fields are optional and only valid with geometry: relation_members:
member_types
Filter members by OSM element type.
- Type: Array of strings
- Values:
"node", "way", "relation" (or subset)
- Default: All types (if not specified)
member_types: [way] # Only process way members
member_types: [node, way] # Process nodes and ways
member_roles
Filter members by their role in the relation.
- Type: Array of strings
- Default: All roles (if not specified)
- Note: Empty string
"" matches members with no role
member_roles: ["outer", "inner"] # Only process outer/inner members
member_roles: [""] # Only process members with no role
member_roles: ["stop", "platform"] # Only process stop/platform members
member_include_when
Filter members by their tags. Supports both structured expressions and inline script expressions.
- Type: Boolean expression (structured or inline script)
- Default: Include all members (if not specified)
Structured example:
member_include_when:
railway: rail
highway: [primary, secondary]
Inline script example:
member_include_when: '${ member.tags.has("highway") && member.tags.highway == "primary" }'
member_exclude_when
Exclude members by their tags. Applied after member_include_when.
- Type: Boolean expression (structured or inline script)
- Default: No exclusions (if not specified)
member_exclude_when:
service: __any__
member_attributes
Add attributes to each member feature from the member's tags.
- Type: Array of attribute definitions
- Supports: All attribute features including inline script expressions
member_attributes:
- key: segment_ref
tag_value: ref
- key: segment_name
tag_value: name
- key: combined_name
value: '${ member.tags.name + " (" + feature.tags.name + ")" }'
Member Context Variables
When using inline script expressions in member_include_when, member_exclude_when, or member_attributes, the following variables are available:
member.tags - Map of tags from the member element (way/node)
member.role - Role of the member in the relation (string, may be empty)
member.type - OSM element type: "node", "way", or "relation" (string)
member.ref - OSM ID of the member element (long)
member.id - Same as member.ref (long)
feature.tags - Map of tags from the parent relation (inherited from parent context)
- All other variables from parent contexts (
args, feature.id, etc.)
Complete Example
layers:
- id: route_segments
features:
- source: osm
geometry: relation_members
include_when:
type: route
route: railway
member_types: [way]
member_roles: [""] # empty role
member_include_when:
railway: rail
member_exclude_when:
service: __any__
attributes:
- key: route_name
tag_value: name
- key: route_ref
tag_value: ref
member_attributes:
- key: segment_ref
tag_value: ref
- key: segment_name
tag_value: name
- key: combined_name
value: '${ member.tags.name + " (" + feature.tags.name + ")" }'
Behavior Notes
- Feature Selection:
include_when/exclude_when filters apply to the relation, not individual members
- Member Filtering Order: Type → Role →
member_include_when → member_exclude_when
- Feature IDs: Each member feature gets a unique ID generated from relation ID and member reference
- Duplicate Members: If a relation contains the same member multiple times, only the first occurrence is processed
- Missing Members: Missing ways/nodes (common in extracts) are skipped with a logged error
- Nested Relations: Nested relation members are currently skipped (not recursively processed)
- Empty Relations: If all members are filtered out, no features are emitted
- Geometry Types:
- Nodes → point geometry
- Ways → line or polygon geometry (determined by whether way is closed and
area tag)
- Relations → skipped (not supported)
- Attributes: Both relation-level attributes (
attributes) and member-level attributes (member_attributes) are applied to each member feature
- Post-processing: Post-processing operations like
merge_linestrings can be used with relation_members geometry
Validation
member_types, member_roles, member_include_when, member_exclude_when, and member_attributes can only be used with geometry: relation_members
member_types values must be "node", "way", or "relation"
- Clear error messages if these fields are used with other geometry types
Related
This feature follows the same pattern as multipolygon processing, where relation members are processed in two phases:
- Phase 1: Identify relations matching
relation_members features
- Phase 2: Store member geometries/tags, then process relations to emit member features
Summary
Add a new
geometry: relation_membersoption to YAML custom maps that allows processing individual members of OSM relations as separate features. Instead of emitting one feature per relation, this emits one feature per qualifying member, using each member's geometry and tags.Key Features:
include_when/exclude_whenfiltersProblem
Currently, when working with OSM relations in YAML custom maps, you can only emit a single feature for the entire relation. However, many use cases require processing individual members of a relation as separate features:
type=route): Extract individual road/rail segments that make up a routetype=boundary): Extract individual boundary segmentsFor example, a
type=routerelation withroute=railwaymight contain 50 way members representing different track segments. Currently, you'd get one feature for the relation, but you need 50 features - one for each segment - with each segment's own geometry and tags.Proposed Solution
Add a new
geometry: relation_membersoption that:include_when/exclude_whenfilters (applied to relation tags)Use Cases
1. Railway Route Segments
Extract individual track segments from railway route relations:
2. Administrative Boundary Segments
Extract individual boundary segments from administrative boundary relations:
3. Bus Route Stops
Extract individual stops from public transport route relations:
Configuration
New Geometry Type
When used, the feature selection (
include_when/exclude_when) applies to OSM relations, but features are emitted for each qualifying member using the member's geometry.New Configuration Fields
All fields are optional and only valid with
geometry: relation_members:member_typesFilter members by OSM element type.
"node","way","relation"(or subset)member_rolesFilter members by their role in the relation.
""matches members with no rolemember_include_whenFilter members by their tags. Supports both structured expressions and inline script expressions.
Structured example:
Inline script example:
member_exclude_whenExclude members by their tags. Applied after
member_include_when.member_attributesAdd attributes to each member feature from the member's tags.
Member Context Variables
When using inline script expressions in
member_include_when,member_exclude_when, ormember_attributes, the following variables are available:member.tags- Map of tags from the member element (way/node)member.role- Role of the member in the relation (string, may be empty)member.type- OSM element type:"node","way", or"relation"(string)member.ref- OSM ID of the member element (long)member.id- Same asmember.ref(long)feature.tags- Map of tags from the parent relation (inherited from parent context)args,feature.id, etc.)Complete Example
Behavior Notes
include_when/exclude_whenfilters apply to the relation, not individual membersmember_include_when→member_exclude_whenareatag)attributes) and member-level attributes (member_attributes) are applied to each member featuremerge_linestringscan be used withrelation_membersgeometryValidation
member_types,member_roles,member_include_when,member_exclude_when, andmember_attributescan only be used withgeometry: relation_membersmember_typesvalues must be"node","way", or"relation"Related
This feature follows the same pattern as multipolygon processing, where relation members are processed in two phases:
relation_membersfeatures