{"id":28974792,"url":"https://github.com/motis-project/osr","last_synced_at":"2025-06-24T12:06:58.983Z","repository":{"id":227227047,"uuid":"769874111","full_name":"motis-project/osr","owner":"motis-project","description":"osr is a memory efficient multi-mode OpenStreetMap router","archived":false,"fork":false,"pushed_at":"2025-06-10T15:24:04.000Z","size":5541,"stargazers_count":33,"open_issues_count":6,"forks_count":18,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-06-10T16:36:12.369Z","etag":null,"topics":["high-performance","intermodal","openstreetmap","routing","routing-algorithm","routing-engine"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/motis-project.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-03-10T10:07:29.000Z","updated_at":"2025-06-04T10:23:07.000Z","dependencies_parsed_at":"2024-03-15T00:37:43.997Z","dependency_job_id":"e8f8fe31-f342-434a-aaaa-9e3c6aee773c","html_url":"https://github.com/motis-project/osr","commit_stats":null,"previous_names":["felixguendling/open-street-router","motis-project/open-street-router"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/motis-project/osr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/motis-project%2Fosr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/motis-project%2Fosr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/motis-project%2Fosr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/motis-project%2Fosr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/motis-project","download_url":"https://codeload.github.com/motis-project/osr/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/motis-project%2Fosr/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261669002,"owners_count":23192362,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["high-performance","intermodal","openstreetmap","routing","routing-algorithm","routing-engine"],"created_at":"2025-06-24T12:06:55.650Z","updated_at":"2025-06-24T12:06:58.959Z","avatar_url":"https://github.com/motis-project.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\u003cimg src=\"docs/screenshot.png\"\u003e\u003c/p\u003e\n\n# Open Street Router\n\nThis router is the most memory-efficient multi-profile routing for planet wide street routing (pedestrian, bike, car, etc.) on OpenStreetMap. The\ngoal is to make it possible to import data on affordable low-end machines. This is mainly achieved by using compact data\nstructures and [memory mapped](https://en.wikipedia.org/wiki/Memory-mapped_file) files. A planet import should not need\nmore than 10GB of RAM. More RAM (and a fast SSD) will speed up the import.\n\nDirectory with all created files after extract (only routing data has to be in-memory):\n\n```bash\n# routing data 12.7G\n# recommended to lock to memory\n 22K multi_level_elevators.bin\n1,2G node_in_way_idx_data.bin\n1,1G node_in_way_idx_index.bin\n542M node_properties.bin\n 34M node_restricted.bin\n4,7M node_restrictions_data.bin\n1,1G node_restrictions_index.bin\n2,3G node_ways_data.bin\n1,1G node_ways_index.bin\n748M way_node_dist_data.bin\n833M way_node_dist_index.bin\n2,3G way_nodes_data.bin\n833M way_nodes_index.bin\n625M way_properties.bin\n\n# mapping to osm ids\n2,2G node_to_osm.bin\n1,7G way_osm_idx.bin\n\n# way osm nodes (only memory mapped)\n 19G way_osm_nodes_data.bin\n1,7G way_osm_nodes_index.bin\n\n# way geometry (only memory mapped)\n 19G way_polylines_data.bin\n1,7G way_polylines_index.bin\n```\n\n## Multi-Level Indoor Routing\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"docs/levels.png\"\u003e\u003c/p\u003e\n\n## Car Outdoor Routing\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"docs/car.png\"\u003e\u003c/p\u003e\n\n## Usage\n\n```bash\n# --in     | -i     input file\n# --out    | -o     output directory (will be deleted + created)\n./osr-extract -i planet-latest.osm.pbf -o osr-planet\n\n# --data   | -d     the output from osr-extract\n# --static | -s     static HTML/JS/CSS assets to serve\n./osr-backend -d osr-planet -s web\n```\n\n## Data Model\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"docs/data_model.png\"\u003e\u003c/p\u003e\n\n### Multi Nodes\n\nThe data model is not an explicit graph. Instead, the OpenStreetMap data (nodes and ways) is stored more or less as it\nis. For routing purposes (i.e. finding shortest paths), only nodes are relevant that are part of more than one way.\nThose nodes are given special IDs (`node_idx_t` in contrast to `osm_node_idx_t` which is the node index from\nOpenStreetMap).\n\n### Lookup\n\nSince only a fraction of ways in OpenStreetMap are relevant for routing, we give the extracted ways internal\nindices (`way_idx_t` in contrast to `osm_way_idx_t` which is the way index from OpenStreetMap). To be able to map\nfrom `node_idx_t` to `osm_node_idx_t` and from `way_idx_t` to `osm_way_idx_t` and vice versa, we have a lookup table in\nboth directions.\n\n### Way to Node and Node to Way\n\nFor routing, it's important to have a fast way to know which ways are reachable from which nodes and which nodes are\nreachable from which way. To achieve this, we store for each `way_idx_t` a list of `node_idx_t` and for\neach `node_idx_t` a list of `way_idx_t` coupled with the information which index this node has in the corresponding way.\nIf a node is part of a way multiple times, this mapping will contain the node multiple times. The order from the source\nway in OpenStreetMap is maintained.\n\n## Routing\n\nDijkstra`s algorithm with a time-based cutoff is used currently for routing.\n\n### Node Neighborhood\n\nFor routing (here: Dijkstra's algorithm for now), it's necessary to know the neighborhood of a node. This can be looked\nup by iterating all `way_idx_t` this node is contained in (see above) and the corresponding index `i` this node has in\nthe way. Neighbors are then node indices `i-1` and `i+1` in those ways. For the case that `i=0` or points to the last\nindex, `i-1` or `i+1` do not exist. With the definition of this neighborhood, the textbook version of Dijkstra's\nalgorithm can be applied.\n\n### From Coordinate to Graph\n\nTo be able to resolve a geo coordinate into nodes in the graph for routing, a geo index data like a quad tree or r-tree\nis required. As there are less `way_idx_t` than `node_idx_t`, we store the bounding boxes of all ways into the rtree. A\nlookup gives us all `way_idx_t` in the area. Sorting those ways by perpendicular line distance from the query coordinate\nto the way gives us the closes `way_idx_t` for start and destination. After that, the two closest routing nodes (\"left\"\nand \"right\") on that `way_idx_t` can be initialized.\n\n### Routing Profiles\n\nAll attributes of the OpenStreetMap ways that are relevant for routing are packed into a compact struct. Distances\nbetween routing nodes (i.e. `node_idx_t`) on a way are stored for fast access.\nThe profile (see blow) function then computes the edge weights and feasibility of a way based on the stored OpenStreetMap attributes.\nTo implement more finegrained control over path finding, it might be necessary to extract more attributes from OpenSteetMap.\n\nThe current set of routing profiles can be found in the [`include/osr/routing/profiles`](https://github.com/motis-project/osr/tree/master/include/osr/routing/profiles) folder. Here's what a profile needs to provide in order to be usable by the shortest path algorithm:\n\n#### Types\n\n- `node` struct: a node defines what is considered a node for this routing profile. The most basic definition of a node would basically be just what's considered a node in the data model (`node_idx_t`, see above). However, for many profiles, this is not succifient. Let's take the foot routing profile as an example: for indoor routing, it should be possible to distinguish the same OSM node on different levels. Therefore, the level has to be part of the foot profile's node definition. Another example is the car profile: to be able to detect u-turns (just changing the direction but staying on the same way), the node has to define the current direction. In order to properly handle turn restrictions, also the last used way has to be part of the node definition. The member function `node::get_key()` returns the key (see `key`).\n- `label` struct: basically the same as the `node` struct but it should additionally carry the routing costs (currently tracked in seconds). The label has to provide `get_node()` and `cost()`  member functions.\n- `key`: The shortest path algorithm (e.g. Dijkstra) tracks minimal costs to each node in a hash map. In theory, it would be sufficient to use `node` as hash map key as we need to only track one shortest path to each profile `node`. To allow for a more efficient storage, multiple nodes can share the same hash map key, therefore reducing the hash map size (which can speedup the routing significantly). Therefore, a profile can define a `key` which can be the same as `node` but doesn't have to be. The key doesn't need any member functions and can therefore just be a typedef to `node_idx_t` (in the most simple case).\n- `entry`: The entry is the hash map entry and is stored for each `key`. It has to store the costs and precessor. If `key` maps several `node`s to the same `entry`, then the `entry` has to store costs and predecessory for each of the `node`s. It has to provide the following member functions:\n  - `std::optional\u003cnode\u003e pred(node)`: returns the predecessor for the node, or `std::nullopt` if this `node` has never been visited.\n  - `cost_t cost(node)`: returns the shortest path costs to this `node`\n  - `bool update(label, node, cost_t, node pred)`: updates the costs for this node if they are better than the previously stored costs and returns `true` if the costs where updated and `false` otherwise.\n\n#### Static functions\n\n  - `resolve_start_node(ways::routing, way_idx_t, node_idx_t, level_t, direction, Fn\u0026\u0026 f)`: resolves all nodes that belong to this particular (`way_idx_t`, `node_idx_t`, `level_t`, `direction`) combination. `Fn f` will be called with each `node`. It's the task of the profile to give the routing algorithm and entry point to its overlay graph.\n  - `resolve_all(ways::routing, node_ix_t, level_t, Fn\u0026\u0026 f)`: Same as `resolve_start_node`, just without the condition that `way_idx_t` has to match.\n  - `adjacent\u003cSearcHdir, WithBlocked, Fn\u003e(ways::routing, node, bitvec\u003cnode_idx_t\u003e* blocked, sharing_data*, elevation_storage*, Fn\u0026\u0026 f)`: Calls `Fn f` with each adjacent neighbor of the given `node`. This is used in the shortest path algorithm to expand a node and visit all its neighbors. This takes a runtime provided bit vector `blocked` into account where bit `i` indicates if `i` can be visited or not. This allows us to dynamically block nodes depending on the routing query.\n\nAs we can see, each profile can define its own overlay graph on top of the data model. This gives us the flexibility to define a routing for anything we want from pedestrians or wheelchair users over cars, trucks, trains to ships without any additional memory overhead. Even combined profiles (e.g. walking, taking a bike, walking) can be implemented. Commonly, routing engines have to have a graph for each profile which makes it quite expensive (in terms of memory) to add a new profile on a global routing server. With our approach, a new profile doesn't come with extra costs.\n\nProfiles have to be defined at compile time. However, it would be possible to parameterize the cost calculation (or any other aspect, like restrictions, etc.) at runtime.\n\n\n### Reconstruction\n\nReconstruction works as in the textbook version of Dijkstra's algorithm except for the first and last part of the path\nwhich is not part of the routing \"graph\" (geo coordinate to first `node_idx_t` and last `node_idx_t` to the geo\ncoordinate).\n\n## Future Work\n\nThis is only a first proof-of-concept. Many basic as well as advanced features can follow.\n\nKnown Issues:\n\n- Routing performance can be improved\n  - by using A* or bidirectional A* for one to one queries\n  - explore preprocessing-based approaches: landmarks, arc flags, transit node routing, multi-level-dijkstra, etc.\n- If source and target are mapped to the same way, the path should not be forced to go through routing nodes\n- Consider the routing profile for initialization\n\nBasic:\n\n- Extract street names of ways for reconstruction.\n- Reconstruct description of way for navigation (including street names)\n- Turn restriction relations ([example](https://www.openstreetmap.org/relation/1654115), [example](https://www.openstreetmap.org/node/516914))\n- Penalize u-turns\n\nAdvanced:\n\n- Create a compact memory-mapped or serializable version of [rtree.c](https://github.com/tidwall/rtree.c)\n- Enable routing algorithm to have weights for nodes, not just edges (e.g. elevators, street crossings, etc.)\n- Incremental live update with OpenStreetMap change sets (should be doable as the data model is not far away from OSM)\n- Exclusion zones as part of the routing query (e.g. for e-scooter routing)\n- Time-dependent routing (consider traffic flow forecast and/or live traffic)\n- Add height profile information to edges (relevant e.g. for bike routing) and use it in routing profiles\n- Combined routing for sharing mobility (e.g. walk to e-scooter, ride e-scooter, walk to destination)\n- `vehicle=destination` ([example](https://www.openstreetmap.org/way/61914850))\n- Start heading + destination heading\n- Make barriers / inaccessible nodes routing nodes (example: [way](https://www.openstreetmap.org/way/940718404), [node](https://www.openstreetmap.org/node/8712182900))\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmotis-project%2Fosr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmotis-project%2Fosr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmotis-project%2Fosr/lists"}