pyroutelib3.osm

Graph

class pyroutelib3.osm.Graph(profile: Profile)

Bases: SimpleGraph[GraphNode]

Graph implements GraphLike over OpenStreetMap data.

add_features(features: Iterable[Node | Way | Relation]) None

add_features adds OpenStreetMap data to the graph.

While it is permitted to call this function multiple times on the same graph, each call must be made with a complete, self-contained dataset. That is, ways may only refer to nodes from the features iterable, and relations as well may only refer to ways and nodes from the features iterable.

If called on a non-empty graph the data is merged:

  • for duplicate nodes - the already-existing data takes precedence,

  • for duplicate edges between two nodes - the incoming data takes precedence,

  • for duplicate turn restrictions - the incoming restriction is processed,

    which should be a no-op, as the restriction was already applied.

Due to linear processing of the provided iterable, a feature may only refer to features that were defined beforehand. The easiest way to satisfy this condition is to ensure that features are listed grouped by type, first nodes, followed by ways, followed by relations. Most OSM XML files exported by other tools follow this ordering.

Any issues with incoming OSM data are reported as warnings through the pyroutelib3.osm logger.

find_nearest_node(position: Tuple[float, float]) GraphNode

find_nearest_node finds the closest node to the provided Position. Phantom nodes nd.id != nd.osm_id created by turn restrictions are not considered.

This function iterates over every contained GraphNode, and can be very slow for big graphs. Use a helper data structure (like a K-D tree) if this function becomes a performance bottleneck.

classmethod from_features(profile: Profile, features: Iterable[Node | Way | Relation]) Self

Creates a Graph based on the provided profile and features.

classmethod from_file(profile: Profile, buf: IO[bytes], format: Literal['xml', 'gz', 'bz2', 'pbf'] | None = None, chunk_size: int = 8192) Self

Creates a Graph based on the provided Profile and features from the provided possibly-compressed OSM XML or a OSM PBF file.

format and chunk_size are passed through to osm.reader.read_features().

profile: Profile
class pyroutelib3.osm.LiveGraph(profile: ~pyroutelib3.osm.profile.Profile, tile_cache_directory: str | ~os.PathLike[str] = 'tilecache', tile_expiry_seconds: int = 2592000, tile_zoom: int = 15, osm_api_url: str = 'https://api.openstreetmap.org/api/0.6/map?bbox={left},{bottom},{right},{top}', file_lock: ~typing.Callable[[~pathlib.Path], ~typing.ContextManager[~typing.Any]] = <class 'filelock._unix.UnixFileLock'>)

Bases: Graph

LiveGraph extends the osm.Graph by automatically downloading data from the OpenStreetMap API in tiles.

Tiles downloads can be triggered by find_nearest_node() and get_edges().

The inherited from_file and from_features methods should not be used.

Usage of this class is discouraged, it is much more wise to use osm.Graph directly with OSM data extracts, further filtered with osmosis <https://wiki.openstreetmap.org/wiki/Osmosis>. Example command:

osmosis \
    --read-pbf-fast south-korea-latest.osm.pbf \
    --bounding-box top=35.3207 left=128.8298 bottom=35.0303 right=129.1608 \
    --tag-filter accept-ways 'highway=*' \
    --tag-filter accept-relations 'type=restriction' \
    --used-node \
    --write-pbf south-korea-trimmed-simplified.osm.pbf
download_tile(tile: Tuple[int, int], tile_file: Path) None

Downloads the provided tile (using osm_api_url) to the provided path.

find_nearest_node(position: Tuple[float, float]) GraphNode

find_nearest_node finds the closest node to the provided Position. Phantom nodes nd.id != nd.osm_id created by turn restrictions are not considered.

This function iterates over every contained GraphNode, and can be very slow for big graphs. Use a helper data structure (like a K-D tree) if this function becomes a performance bottleneck.

classmethod from_features(profile: Profile, features: Iterable[Node | Way | Relation]) Self

Creates a Graph based on the provided profile and features.

classmethod from_file(profile: Profile, buf: IO[bytes], format: Literal['xml', 'gz', 'bz2', 'pbf'] | None = None, chunk_size: int = 8192) Self

Creates a Graph based on the provided Profile and features from the provided possibly-compressed OSM XML or a OSM PBF file.

format and chunk_size are passed through to osm.reader.read_features().

get_edges(id: int) Iterable[Tuple[int, float]]

get_edges must return all edges outgoing from a node with the provided ID. The edges are a pair of (node_id, cost). All returned neighbor nodes must exist in the graph.

If there are no outgoing edges from the provided node, must return an empty iterable. If a node with the given ID, may return an empty iterable, or raise KeyError.

get_tile_directory(tile: Tuple[int, int]) Path

Returns the directory in which the provided tile data should be stored.

has_up_to_date_tile(tile_file: Path) bool

Checks if a tile stored at the provided path is reasonably up-to-date (as defined by tile_expiry_seconds). If the provided file doesn’t exist, returns False.

load_tile(tile: Tuple[int, int], tile_file: Path) None

Loads the provided tile into the graph.

load_tile_around(position: Tuple[float, float]) None

Ensures the tile in which position falls is loaded into the graph.

If that tile is loaded, this function does nothing.

If that tile is cached (present in tile_cache_directory) and reasonably up-to-date (as defined by tile_expiry_seconds) it is simply loaded.

Otherwise, this function downloads the tile before loading it.

file_lock: Callable[[Path], ContextManager[Any]]

Context manager used to ensure tiles are not downloaded simultaneously. Useful if multiple LiveGraphs use the same tile cache directory at the same time. Defaults to filelock.FileLock.

osm_api_url: str

Format string used for generating URLs with tile data. Must have left, bottom, right and top placeholders.

tile_cache_directory: Path

tile_cache_directory is the directory where downloaded tiles are stored. Defaults to “tilecache” in current directory. If missing, this directory (and its parents) are created.

tile_expiry_seconds: int

Tiles are re-downloaded if older than tile_expiry_seconds. Defaults to 30 days.

tile_zoom: int

How large should the downloaded tiles be? See https://wiki.openstreetmap.org/wiki/Zoom_levels. Note that the OSM API rejects requests with too much data with “400 Bad Request” - if that is the case, increase the zoom level.

class pyroutelib3.osm.GraphNode(id: int, position: Tuple[float, float], external_id: int)

Bases: SimpleExternalNode

GraphNode is a node in a Graph.

property osm_id: int

Profiles

Concrete Profiles

Highway-based

class pyroutelib3.osm.BicycleProfile(name: str | None = None, penalties: Dict[str, float] | None = None, access: List[str] | None = None)

Bases: NonMotorroadHighwayProfile

BicycleProfile is a NonMotorroadHighwayProfile with default parameters which can be used for bicycle routing.

class pyroutelib3.osm.BusProfile(name: str | None = None, penalties: Dict[str, float] | None = None, access: List[str] | None = None)

Bases: HighwayProfile

BusProfile is a HighwayProfile with default parameters which can be used for bus routing.

class pyroutelib3.osm.CarProfile(name: str | None = None, penalties: Dict[str, float] | None = None, access: List[str] | None = None)

Bases: HighwayProfile

CarProfile is a HighwayProfile with default parameters which can be used for car routing.

class pyroutelib3.osm.FootProfile(name: str | None = None, penalties: Dict[str, float] | None = None, access: List[str] | None = None)

Bases: NonMotorroadHighwayProfile

FootProfile is a NonMotorroadHighwayProfile with default parameters which can be used for on-foot routing.

FootProfile treats several tags differently to HighwayProfile:

  • public_transport=platform and railway=platform are treated as-if highway=platform

  • oneway tags are ignored, unless on highway=footway, highway=path,

    highway=steps or highway=platform. On other ways, only oneway:foot is used.

  • Only restriction:foot turn restrictions are considered.

get_active_highway_value(tags: Mapping[str, str]) str

get_active_highway_value gets the string to lookup in penalties - the value of the “highway” tag, normalized through EQUIVALENT_TAGS.

get_active_oneway_value(tags: Mapping[str, str]) str

get_active_oneway_value returns the most specific “oneway:MODE” tag, falling back to “oneway” - to use when checking way directionality.

get_active_restriction_value(tags: Mapping[str, str]) str

get_active_restriction_value returns the most specific “restriction:MODE” tag, falling back to “restriction” - to use when checking turn restriction type.

Railway-based

class pyroutelib3.osm.RailwayProfile(name: str = 'railway', penalties: ~typing.Dict[str, float] = <factory>)

Bases: object

RailwayProfile implements Profile for routing over railway=* ways.

Only access=no or access=private can prevent a way from being excluded from the graph. There are no default one ways, and only oneway values of yes and -1 are accepted. All type=restriction with restriction tag set to only/no right_turn/left_turn/u_turn/straight_on are passed through.

RailwayProfile() can be used as a profile for routing over railway=rail, light_rail, subway and narrow_gauge.

is_turn_restriction(tags: Mapping[str, str]) TurnRestriction
way_direction(tags: Mapping[str, str]) Tuple[bool, bool]
way_penalty(tags: Mapping[str, str]) float | None
name: str = 'railway'
penalties: Dict[str, float]
class pyroutelib3.osm.SubwayProfile

Bases: RailwayProfile

SubwayProfile is a RailwayProfile which allows routing only over railway=subway ways.

class pyroutelib3.osm.TramProfile

Bases: RailwayProfile

TramProfile is a RailwayProfile which allows routing only over railway=tram or light_rail ways.

Other

class pyroutelib3.osm.SkeletonProfile(*args, **kwargs)

Bases: Profile

SkeletonProfile implements Profile for routing over every way in OSM data, regardless of used tags. This profile is meant for holding graphs in OSM XML/OSM PBF formats, without following OpenStreetMap mapping conventions. All relations (and thus turn restrictions) are ignored.

The only introspected tag is oneway, which may be set to yes or -1.

is_turn_restriction(relation_tags: Mapping[str, str]) TurnRestriction

is_turn_restriction must determine whether the relation (given by its tags) is an applicable turn restriction.

If the relation is not a turn restriction, or is a turn restriction not applicable to this Profile, must return TurnRestriction.INAPPLICABLE.

If following the route of the restriction is forbidden, must return TurnRestriction.PROHIBITORY.

If following the route of the restriction is forced, must return TurnRestriction.MANDATORY.

way_direction(way_tags: Mapping[str, str]) Tuple[bool, bool]

way_direction must determine whether a way with the provided tags is traversable forward and backward. First element in the returned tuple must represent the forward direction, while the second - backward direction.

way_penalty(way_tags: Mapping[str, str]) float | None

way_penalty must return the penalty for traversing a way with the provided tags, or None if the way is not traversable.

The returned penalty is then multiplied by the each way’s segment length to get the cost of traversing an edge.

The returned value must be finite and at least 1.

Base classes

class pyroutelib3.osm.Profile(*args, **kwargs)

Bases: Protocol

Profile instructs how Graph should convert OSM features into a routing graph.

is_turn_restriction(_Profile__relation_tags: Mapping[str, str]) TurnRestriction

is_turn_restriction must determine whether the relation (given by its tags) is an applicable turn restriction.

If the relation is not a turn restriction, or is a turn restriction not applicable to this Profile, must return TurnRestriction.INAPPLICABLE.

If following the route of the restriction is forbidden, must return TurnRestriction.PROHIBITORY.

If following the route of the restriction is forced, must return TurnRestriction.MANDATORY.

way_direction(_Profile__way_tags: Mapping[str, str]) Tuple[bool, bool]

way_direction must determine whether a way with the provided tags is traversable forward and backward. First element in the returned tuple must represent the forward direction, while the second - backward direction.

way_penalty(_Profile__way_tags: Mapping[str, str]) float | None

way_penalty must return the penalty for traversing a way with the provided tags, or None if the way is not traversable.

The returned penalty is then multiplied by the each way’s segment length to get the cost of traversing an edge.

The returned value must be finite and at least 1.

class pyroutelib3.osm.HighwayProfile(name: str, penalties: Dict[str, float], access: List[str])

Bases: Profile

HighwayProfile implements Profile for routing over highway=* ways.

get_active_highway_value(tags: Mapping[str, str]) str

get_active_highway_value gets the string to lookup in penalties - the value of the “highway” tag, normalized through EQUIVALENT_TAGS.

get_active_oneway_value(tags: Mapping[str, str]) str

get_active_oneway_value returns the most specific “oneway:MODE” tag, falling back to “oneway” - to use when checking way directionality.

get_active_restriction_value(tags: Mapping[str, str]) str

get_active_restriction_value returns the most specific “restriction:MODE” tag, falling back to “restriction” - to use when checking turn restriction type.

is_allowed(way_tags: Mapping[str, str]) bool

is_allowed checks if a way is allowed, as per the access tags and the hierarchy defined in access. Only values of “no” and “private” can exclude a way, any other value (even “destination” or “permit”) is assumed to allow a way to be used.

is_exempted(restriction_tags: Mapping[str, str]) bool

is_exempted returns True if any of the transportation modes under the except tag are present in access.

is_turn_restriction(tags: Mapping[str, str]) TurnRestriction

is_turn_restriction checks relation tags to determine what kind of TurnRestriction this is. The relation must have a “type=restriction” tag, however, the restriction type may be under “restriction” or “restriction:MODE” keys (see get_active_restriction_value()), and is_exempted() must return False. Only only/no right_turn/left_turn/u_turn/straight_on restrictions are accepted.

way_direction(way_tags: Mapping[str, str]) Tuple[bool, bool]

way_direction returns the direction of travel of the provided way. Apart from considering the oneway tag (as returned by get_active_oneway_value()), highway=motorway, highway=motorway_link, junction=roundabout and junction=circular default to being oneway.

way_penalty(way_tags: Mapping[str, str]) float | None

way_penalty returns the penalty of using this way, by looking up the return value of get_active_highway_value() in penalties, unless is_allowed() return False.

EQUIVALENT_TAGS: ClassVar[Mapping[str, str]] = {'minor': 'unclassified', 'motorway_link': 'motorway', 'primary_link': 'primary', 'secondary_link': 'secondary', 'tertiary_link': 'tertiary', 'trunk_link': 'trunk'}
access: List[str]

access is the hierarchy of access tags to consider when checking if a route is traversable. Keys must be listed from least-specific first.

name: str
penalties: Dict[str, float]

penalties maps highway tag values (after transformation through EQUIVALENT_TAGS) into their corresponding penalties. All penalties must be finite and not smaller than 1.

class pyroutelib3.osm.NonMotorroadHighwayProfile(name: str, penalties: Dict[str, float], access: List[str])

Bases: HighwayProfile

NonMotorroadHighwayProfile is a base class for profiles over highway=* ways, for which motorroad=yes implies no access.

is_allowed(way_tags: Mapping[str, str]) bool

is_allowed checks if a way is allowed, as per the access tags and the hierarchy defined in access. Only values of “no” and “private” can exclude a way, any other value (even “destination” or “permit”) is assumed to allow a way to be used.

class pyroutelib3.osm.TurnRestriction(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: Enum

TurnRestriction indicates whether an OSM relation is an applicable turn restriction. Used as the return value of Profile.is_turn_restriction().

INAPPLICABLE = 0

Not a turn restriction, or a restriction not applicable to the current Profile.

MANDATORY = 2

Mandatory (only_*) restriction, stepping from the “from” onto the “via” member forces the use of the restriction’s route.

PROHIBITORY = 1

Prohibitory (no_*) restriction, following the route of restriction is not allowed.