Python wrapper for the OSM API
—
Complete lifecycle management for OpenStreetMap ways including creation with node references, modification, deletion, and relationship tracking. Ways represent linear features like roads, paths, and area boundaries by connecting a series of nodes.
Get individual ways by ID with optional version specification.
def WayGet(WayId, WayVersion=-1):
"""
Returns way with WayId as a dict.
Parameters:
- WayId (int): Unique identifier of the way
- WayVersion (int, optional): Specific version to retrieve (-1 for latest)
Returns:
dict: Way data including id, nd (node list), tag, changeset, version,
user, uid, timestamp, visible
Raises:
- ElementDeletedApiError: If way has been deleted
- ElementNotFoundApiError: If way cannot be found
"""Usage Example:
import osmapi
api = osmapi.OsmApi()
# Get latest version of way
way = api.WayGet(456)
print(f"Way {way['id']} with {len(way['nd'])} nodes")
print(f"Type: {way['tag'].get('highway', way['tag'].get('building', 'other'))}")
print(f"Name: {way['tag'].get('name', 'Unnamed')}")
# Get specific version
way_v3 = api.WayGet(456, WayVersion=3)
print(f"Version 3 had {len(way_v3['nd'])} nodes")Create new ways by connecting existing nodes with optional tags.
def WayCreate(WayData):
"""
Creates a way based on the supplied WayData dict.
Parameters:
- WayData (dict): Way data with nd (node list) and optional tag
Required: nd (list[int]) - list of node IDs
Optional: tag (dict)
Returns:
dict: Updated WayData with assigned id, version, changeset,
user, uid, visible
Raises:
- UsernamePasswordMissingError: If no authentication provided
- NoChangesetOpenError: If no changeset is open
- OsmTypeAlreadyExistsError: If way data contains existing ID
- ChangesetClosedApiError: If changeset is closed
- PreconditionFailedApiError: If referenced nodes don't exist
"""Usage Example:
import osmapi
api = osmapi.OsmApi(username="your_username", password="your_password")
with api.Changeset({"comment": "Adding new street"}) as changeset_id:
# Create nodes for the way first
node1 = api.NodeCreate({"lat": 47.6060, "lon": -122.3320, "tag": {}})
node2 = api.NodeCreate({"lat": 47.6065, "lon": -122.3325, "tag": {}})
node3 = api.NodeCreate({"lat": 47.6070, "lon": -122.3330, "tag": {}})
# Create way connecting the nodes
new_way = api.WayCreate({
"nd": [node1["id"], node2["id"], node3["id"]],
"tag": {
"highway": "residential",
"name": "Example Street",
"surface": "asphalt",
"maxspeed": "25 mph"
}
})
print(f"Created way {new_way['id']} with {len(new_way['nd'])} nodes")Modify existing ways by updating node list, tags, or other attributes.
def WayUpdate(WayData):
"""
Updates way with the supplied WayData dict.
Parameters:
- WayData (dict): Way data with id, version, and updated fields
Required: id (int), nd (list[int]), version (int)
Optional: tag (dict)
Returns:
dict: Updated WayData 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 nodes don't exist
"""Usage Example:
import osmapi
api = osmapi.OsmApi(username="your_username", password="your_password")
# Get current way data
way = api.WayGet(12345)
with api.Changeset({"comment": "Updating street information"}) as changeset_id:
# Add new tag information
way["tag"]["surface"] = "concrete"
way["tag"]["lanes"] = "2"
way["tag"]["lit"] = "yes"
# Add a new node to extend the way
new_node = api.NodeCreate({"lat": 47.6075, "lon": -122.3335, "tag": {}})
way["nd"].append(new_node["id"])
updated_way = api.WayUpdate(way)
print(f"Updated way {updated_way['id']} to version {updated_way['version']}")
print(f"Now has {len(updated_way['nd'])} nodes")Delete ways from OpenStreetMap (marks as invisible).
def WayDelete(WayData):
"""
Delete way with WayData.
Parameters:
- WayData (dict): Way data with id, version, and current attributes
Required: id (int), nd (list[int]), version (int)
Optional: tag (dict)
Returns:
dict: Updated WayData 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 way already deleted
- ElementNotFoundApiError: If way cannot be found
- PreconditionFailedApiError: If way is still used by relations
"""Usage Example:
import osmapi
api = osmapi.OsmApi(username="your_username", password="your_password")
# Get way to delete
way = api.WayGet(12345)
# Check if way is used in relations first
relations = api.WayRelations(way["id"])
if relations:
print(f"Warning: Way is used in {len(relations)} relations")
with api.Changeset({"comment": "Removing obsolete way"}) as changeset_id:
deleted_way = api.WayDelete(way)
print(f"Deleted way {deleted_way['id']}, now version {deleted_way['version']}")
print(f"Visible: {deleted_way['visible']}") # FalseRetrieve complete version history for a way.
def WayHistory(WayId):
"""
Returns dict with version as key containing all way versions.
Parameters:
- WayId (int): Unique identifier of the way
Returns:
dict: Version history with version numbers as keys and
WayData dicts as values
"""Usage Example:
import osmapi
api = osmapi.OsmApi()
# Get complete history
history = api.WayHistory(456)
for version, way_data in history.items():
print(f"Version {version}: {way_data['user']} at {way_data['timestamp']}")
print(f" Nodes: {len(way_data['nd'])} ({way_data['nd'][:3]}...)")
print(f" Highway type: {way_data['tag'].get('highway', 'none')}")Find relations that reference a specific way.
def WayRelations(WayId):
"""
Returns a list of relations containing the specified way.
Parameters:
- WayId (int): Unique identifier of the way
Returns:
list[dict]: List of RelationData dicts containing the way
"""Usage Example:
import osmapi
api = osmapi.OsmApi()
way_id = 456
# Find relations using this way
relations = api.WayRelations(way_id)
print(f"Way {way_id} is used in {len(relations)} relations:")
for relation in relations:
print(f" Relation {relation['id']}: {relation['tag'].get('name', 'Unnamed')}")
print(f" Type: {relation['tag'].get('type', 'unknown')}")
# Find this way's role in the relation
for member in relation['member']:
if member['type'] == 'way' and member['ref'] == way_id:
print(f" Role: {member['role']}")Retrieve way with all referenced nodes included.
def WayFull(WayId):
"""
Returns the full data for way WayId as list of dicts.
Parameters:
- WayId (int): Unique identifier of the way
Returns:
list[dict]: List of elements with type and data keys, including
the way itself and all referenced nodes
Raises:
- ElementDeletedApiError: If way has been deleted
- ElementNotFoundApiError: If way cannot be found
"""Usage Example:
import osmapi
api = osmapi.OsmApi()
# Get way with all referenced nodes
full_data = api.WayFull(456)
way_data = None
nodes_data = []
for element in full_data:
if element['type'] == 'way':
way_data = element['data']
elif element['type'] == 'node':
nodes_data.append(element['data'])
print(f"Way {way_data['id']}: {way_data['tag'].get('name', 'Unnamed')}")
print(f"Coordinates:")
for node in nodes_data:
print(f" Node {node['id']}: ({node['lat']}, {node['lon']})")Retrieve multiple ways in a single API call for efficiency.
def WaysGet(WayIdList):
"""
Returns dict with way IDs as keys for multiple ways.
Parameters:
- WayIdList (list[int]): List of way IDs to retrieve
Returns:
dict: Way IDs as keys with WayData dicts as values
"""Usage Example:
import osmapi
api = osmapi.OsmApi()
# Get multiple ways efficiently
way_ids = [456, 789, 101112, 131415]
ways = api.WaysGet(way_ids)
for way_id, way_data in ways.items():
highway_type = way_data['tag'].get('highway', 'other')
name = way_data['tag'].get('name', 'Unnamed')
print(f"Way {way_id}: {highway_type} - {name}")
print(f" Nodes: {len(way_data['nd'])}")
# Check for missing ways
found_ids = set(ways.keys())
missing_ids = set(way_ids) - found_ids
if missing_ids:
print(f"Missing ways: {missing_ids}")WayData = {
'id': int, # Way ID (assigned by OSM after creation)
'nd': list[int], # List of node IDs that make up this way (required)
'tag': dict, # Key-value pairs for attributes
'version': int, # Version number (starts at 1)
'changeset': int, # ID of changeset that last modified this way
'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
}# Road/Highway
{
"highway": "residential",
"name": "Main Street",
"surface": "asphalt",
"lanes": "2",
"maxspeed": "30 mph",
"lit": "yes"
}
# Building outline
{
"building": "residential",
"addr:housenumber": "123",
"addr:street": "Main Street",
"addr:city": "Example City",
"building:levels": "2"
}
# Natural feature
{
"natural": "river",
"name": "Example River",
"waterway": "river",
"width": "15"
}
# Area (closed way)
{
"landuse": "park",
"name": "Central Park",
"access": "public",
"leisure": "park"
}Ways representing linear features like roads, rivers, or boundaries.
# Road way - nodes should be ordered along the path
road_way = {
"nd": [node1_id, node2_id, node3_id, node4_id],
"tag": {"highway": "primary", "name": "Highway 101"}
}Ways where the first and last nodes are the same, representing areas.
# Building way - first and last node IDs are identical
building_way = {
"nd": [node1_id, node2_id, node3_id, node4_id, node1_id],
"tag": {"building": "house", "addr:housenumber": "42"}
}Way operations can raise various exceptions:
import osmapi
api = osmapi.OsmApi(username="user", password="pass")
try:
way = api.WayGet(999999)
except osmapi.ElementNotFoundApiError:
print("Way does not exist")
except osmapi.ElementDeletedApiError:
print("Way has been deleted")
try:
with api.Changeset({"comment": "Test"}) as changeset_id:
# This will fail if nodes don't exist
api.WayCreate({
"nd": [999999, 999998], # Non-existent nodes
"tag": {"highway": "path"}
})
except osmapi.PreconditionFailedApiError:
print("Referenced nodes don't exist or are not visible")
except osmapi.VersionMismatchApiError:
print("Version conflict - way was modified by another user")Install with Tessl CLI
npx tessl i tessl/pypi-osmapi