Python wrapper for the OSM API
—
Comprehensive relation management including creation, modification, deletion, and nested relation handling. Relations group other OSM elements (nodes, ways, and other relations) together to represent complex geographic features like multipolygons, routes, and administrative boundaries.
Get individual relations by ID with optional version specification.
def RelationGet(RelationId, RelationVersion=-1):
"""
Returns relation with RelationId as a dict.
Parameters:
- RelationId (int): Unique identifier of the relation
- RelationVersion (int, optional): Specific version to retrieve (-1 for latest)
Returns:
dict: Relation data including id, member (list), tag, changeset, version,
user, uid, timestamp, visible
Raises:
- ElementDeletedApiError: If relation has been deleted
- ElementNotFoundApiError: If relation cannot be found
"""Usage Example:
import osmapi
api = osmapi.OsmApi()
# Get latest version of relation
relation = api.RelationGet(789)
print(f"Relation {relation['id']}: {relation['tag'].get('name', 'Unnamed')}")
print(f"Type: {relation['tag'].get('type', 'unknown')}")
print(f"Members: {len(relation['member'])}")
# Show member details
for member in relation['member']:
print(f" {member['type']} {member['ref']} as '{member['role']}'")
# Get specific version
relation_v2 = api.RelationGet(789, RelationVersion=2)
print(f"Version 2 had {len(relation_v2['member'])} members")Create new relations by grouping existing elements with defined roles.
def RelationCreate(RelationData):
"""
Creates a relation based on the supplied RelationData dict.
Parameters:
- RelationData (dict): Relation data with member list and optional tag
Required: member (list[dict]) - list of member objects
Optional: tag (dict)
Returns:
dict: Updated RelationData with assigned id, version, changeset,
user, uid, visible
Raises:
- UsernamePasswordMissingError: If no authentication provided
- NoChangesetOpenError: If no changeset is open
- OsmTypeAlreadyExistsError: If relation data contains existing ID
- ChangesetClosedApiError: If changeset is closed
- PreconditionFailedApiError: If referenced elements don't exist
"""Usage Example:
import osmapi
api = osmapi.OsmApi(username="your_username", password="your_password")
with api.Changeset({"comment": "Creating bus route relation"}) as changeset_id:
# Create a bus route relation
new_relation = api.RelationCreate({
"member": [
{"type": "node", "ref": 12345, "role": "stop"},
{"type": "way", "ref": 67890, "role": ""},
{"type": "way", "ref": 67891, "role": ""},
{"type": "node", "ref": 12346, "role": "stop"},
{"type": "way", "ref": 67892, "role": ""},
{"type": "node", "ref": 12347, "role": "stop"}
],
"tag": {
"type": "route",
"route": "bus",
"name": "Bus Route 42",
"ref": "42",
"operator": "City Transit",
"public_transport:version": "2"
}
})
print(f"Created relation {new_relation['id']} with {len(new_relation['member'])} members")Modify existing relations by updating members, tags, or other attributes.
def RelationUpdate(RelationData):
"""
Updates relation with the supplied RelationData dict.
Parameters:
- RelationData (dict): Relation data with id, version, and updated fields
Required: id (int), member (list[dict]), version (int)
Optional: tag (dict)
Returns:
dict: Updated RelationData with new version, changeset, user, uid, visible
Raises:
- UsernamePasswordMissingError: If no authentication provided
- NoChangesetOpenError: If no changeset is open
- VersionMismatchApiError: If version doesn't match current
- ChangesetClosedApiError: If changeset is closed
- PreconditionFailedApiError: If referenced elements don't exist
"""Usage Example:
import osmapi
api = osmapi.OsmApi(username="your_username", password="your_password")
# Get current relation data
relation = api.RelationGet(12345)
with api.Changeset({"comment": "Adding stop to bus route"}) as changeset_id:
# Add a new stop to the route
new_stop = {"type": "node", "ref": 98765, "role": "stop"}
relation["member"].append(new_stop)
# Update route information
relation["tag"]["note"] = "Route extended to new terminal"
updated_relation = api.RelationUpdate(relation)
print(f"Updated relation {updated_relation['id']} to version {updated_relation['version']}")
print(f"Now has {len(updated_relation['member'])} members")Delete relations from OpenStreetMap (marks as invisible).
def RelationDelete(RelationData):
"""
Delete relation with RelationData.
Parameters:
- RelationData (dict): Relation data with id, version, and current attributes
Required: id (int), member (list[dict]), version (int)
Optional: tag (dict)
Returns:
dict: Updated RelationData with visible=False and new version
Raises:
- UsernamePasswordMissingError: If no authentication provided
- NoChangesetOpenError: If no changeset is open
- VersionMismatchApiError: If version doesn't match current
- ChangesetClosedApiError: If changeset is closed
- ElementDeletedApiError: If relation already deleted
- ElementNotFoundApiError: If relation cannot be found
- PreconditionFailedApiError: If relation is still used by other relations
"""Usage Example:
import osmapi
api = osmapi.OsmApi(username="your_username", password="your_password")
# Get relation to delete
relation = api.RelationGet(12345)
# Check if relation is used in other relations first
parent_relations = api.RelationRelations(relation["id"])
if parent_relations:
print(f"Warning: Relation {relation['id']} is used in {len(parent_relations)} other relations")
with api.Changeset({"comment": "Removing obsolete relation"}) as changeset_id:
deleted_relation = api.RelationDelete(relation)
print(f"Deleted relation {deleted_relation['id']}, now version {deleted_relation['version']}")
print(f"Visible: {deleted_relation['visible']}") # FalseRetrieve complete version history for a relation.
def RelationHistory(RelationId):
"""
Returns dict with version as key containing all relation versions.
Parameters:
- RelationId (int): Unique identifier of the relation
Returns:
dict: Version history with version numbers as keys and
RelationData dicts as values
"""Usage Example:
import osmapi
api = osmapi.OsmApi()
# Get complete history
history = api.RelationHistory(789)
for version, relation_data in history.items():
print(f"Version {version}: {relation_data['user']} at {relation_data['timestamp']}")
print(f" Members: {len(relation_data['member'])}")
print(f" Type: {relation_data['tag'].get('type', 'unknown')}")
# Show member changes
member_types = {}
for member in relation_data['member']:
member_types[member['type']] = member_types.get(member['type'], 0) + 1
print(f" Composition: {dict(member_types)}")Find relations that reference a specific relation.
def RelationRelations(RelationId):
"""
Returns a list of relations containing the specified relation.
Parameters:
- RelationId (int): Unique identifier of the relation
Returns:
list[dict]: List of RelationData dicts containing the relation
"""Usage Example:
import osmapi
api = osmapi.OsmApi()
relation_id = 789
# Find parent relations
parent_relations = api.RelationRelations(relation_id)
print(f"Relation {relation_id} is used in {len(parent_relations)} other relations:")
for parent in parent_relations:
print(f" Relation {parent['id']}: {parent['tag'].get('name', 'Unnamed')}")
print(f" Type: {parent['tag'].get('type', 'unknown')}")
# Find this relation's role in the parent
for member in parent['member']:
if member['type'] == 'relation' and member['ref'] == relation_id:
print(f" Role: {member['role']}")Retrieve relation with all referenced elements at two levels deep.
def RelationFull(RelationId):
"""
Returns the full data (two levels) for relation RelationId as list of dicts.
Parameters:
- RelationId (int): Unique identifier of the relation
Returns:
list[dict]: List of elements with type and data keys, including
the relation itself and all directly referenced elements
Raises:
- ElementDeletedApiError: If relation has been deleted
- ElementNotFoundApiError: If relation cannot be found
"""Retrieve relation with all referenced elements at all levels (recursive).
def RelationFullRecur(RelationId):
"""
Returns the full data (all levels) for relation RelationId as list of dicts.
Parameters:
- RelationId (int): Unique identifier of the relation
Returns:
list[dict]: List of elements with type and data keys, including
the relation itself and all recursively referenced elements
Raises:
- ElementDeletedApiError: If any relation has been deleted
- ElementNotFoundApiError: If relation cannot be found
"""Usage Example:
import osmapi
api = osmapi.OsmApi()
# Get relation with two levels of data
full_data = api.RelationFull(789)
relation_data = None
nodes_data = []
ways_data = []
child_relations_data = []
for element in full_data:
if element['type'] == 'relation':
if element['data']['id'] == 789:
relation_data = element['data']
else:
child_relations_data.append(element['data'])
elif element['type'] == 'way':
ways_data.append(element['data'])
elif element['type'] == 'node':
nodes_data.append(element['data'])
print(f"Relation {relation_data['id']}: {relation_data['tag'].get('name', 'Unnamed')}")
print(f"Contains {len(nodes_data)} nodes, {len(ways_data)} ways, {len(child_relations_data)} relations")
# For complex nested relations, use recursive version
if child_relations_data:
print("Getting recursive data for complex relation...")
recursive_data = api.RelationFullRecur(789)
print(f"Recursive data contains {len(recursive_data)} total elements")Retrieve multiple relations in a single API call for efficiency.
def RelationsGet(RelationIdList):
"""
Returns dict with relation IDs as keys for multiple relations.
Parameters:
- RelationIdList (list[int]): List of relation IDs to retrieve
Returns:
dict: Relation IDs as keys with RelationData dicts as values
"""Usage Example:
import osmapi
api = osmapi.OsmApi()
# Get multiple relations efficiently
relation_ids = [789, 101112, 131415, 161718]
relations = api.RelationsGet(relation_ids)
for relation_id, relation_data in relations.items():
relation_type = relation_data['tag'].get('type', 'unknown')
name = relation_data['tag'].get('name', 'Unnamed')
print(f"Relation {relation_id}: {relation_type} - {name}")
print(f" Members: {len(relation_data['member'])}")
# Check for missing relations
found_ids = set(relations.keys())
missing_ids = set(relation_ids) - found_ids
if missing_ids:
print(f"Missing relations: {missing_ids}")RelationData = {
'id': int, # Relation ID (assigned by OSM after creation)
'member': list[dict], # List of member objects (required)
'tag': dict, # Key-value pairs for attributes
'version': int, # Version number (starts at 1)
'changeset': int, # ID of changeset that last modified this relation
'user': str, # Username of last editor
'uid': int, # User ID of last editor
'timestamp': str, # ISO timestamp of last modification
'visible': bool # True if visible, False if deleted
}
RelationMember = {
'type': str, # 'node', 'way', or 'relation'
'ref': int, # Referenced element ID
'role': str # Role description (can be empty string)
}Complex areas with holes or multiple parts.
multipolygon_relation = {
"member": [
{"type": "way", "ref": outer_way_id, "role": "outer"},
{"type": "way", "ref": inner_way_id, "role": "inner"}
],
"tag": {
"type": "multipolygon",
"natural": "forest",
"name": "Central Forest Reserve"
}
}Transportation or hiking routes.
bus_route_relation = {
"member": [
{"type": "node", "ref": stop1_id, "role": "stop"},
{"type": "way", "ref": way1_id, "role": ""},
{"type": "node", "ref": stop2_id, "role": "stop"},
{"type": "way", "ref": way2_id, "role": ""}
],
"tag": {
"type": "route",
"route": "bus",
"name": "Bus Line 42",
"ref": "42",
"operator": "Metro Transit"
}
}Administrative or other boundaries.
boundary_relation = {
"member": [
{"type": "way", "ref": boundary_way1_id, "role": "outer"},
{"type": "way", "ref": boundary_way2_id, "role": "outer"}
],
"tag": {
"type": "boundary",
"boundary": "administrative",
"admin_level": "6",
"name": "Example County"
}
}Relation operations can raise various exceptions:
import osmapi
api = osmapi.OsmApi(username="user", password="pass")
try:
relation = api.RelationGet(999999)
except osmapi.ElementNotFoundApiError:
print("Relation does not exist")
except osmapi.ElementDeletedApiError:
print("Relation has been deleted")
try:
with api.Changeset({"comment": "Test"}) as changeset_id:
# This will fail if referenced elements don't exist
api.RelationCreate({
"member": [
{"type": "node", "ref": 999999, "role": "stop"} # Non-existent node
],
"tag": {"type": "route", "route": "bus"}
})
except osmapi.PreconditionFailedApiError:
print("Referenced elements don't exist or are not visible")
except osmapi.VersionMismatchApiError:
print("Version conflict - relation was modified by another user")Install with Tessl CLI
npx tessl i tessl/pypi-osmapi