CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-osmapi

Python wrapper for the OSM API

Pending
Overview
Eval results
Files

ways.mddocs/

Way Operations

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.

Capabilities

Way Retrieval

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")

Way Creation

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")

Way Updates

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")

Way Deletion

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']}")  # False

Way History

Retrieve 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')}")

Way Relationships

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']}")

Way with Full Data

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']})")

Bulk Way Operations

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}")

Way Data Structure

WayData Dictionary

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
}

Common Way Tags

# 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"
}

Way Types and Patterns

Linear Ways

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"}
}

Closed Ways (Areas)

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 Validation

  • Must have at least 2 nodes
  • Maximum of 2000 nodes per way
  • All referenced nodes must exist
  • For areas, first and last node should be the same
  • Nodes should be ordered correctly for the intended geometry

Error Handling

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

docs

authentication.md

changesets.md

errors.md

index.md

nodes.md

notes.md

relations.md

ways.md

tile.json