docs/areas.md
How to route inside pedestrian areas, or over the interior of an area where you can travel freely in all directions.
%OSRM can create routes crossing the interior of an area by generating virtual ways along its lines of sight. This process is called @em meshing. The generated ways avoid obstacles and use existing nodes.
The mesh is pruned: of the whole visibility graph, %OSRM keeps the shortest-path tree rooted at each entry point, plus every edge of every ring. That is enough to be exact. A coordinate inside the area is snapped to whichever vertex makes its journey shortest, and from that vertex the tree already holds the shortest way to every entry point, so the route is the one the whole visibility graph would have given -- for a mesh that grows as entry points times vertices rather than as vertices squared. The ring edges are what make the perimeter of a plaza and the sides of an obstacle walkable in their own right. An entry point is where another way connects to the perimeter of the area.
Set area_emit_visibility_graph = true in your profile's properties to keep the whole
graph instead. The only journey that needs it is one that both begins and ends inside the
same area, which wants a path between two arbitrary vertices rather than a path out.
This feature is still EXPERIMENTAL.
The bundled foot profile already carries everything below, switched off. Open profiles/foot.lua and set the flag near the top of the file:
local enable_area_meshing = true
Or leave that file alone and extract with profiles/foot_area.lua, which is the same profile with the flag turned on:
osrm-extract -p profiles/foot_area.lua your-data.osm.pbf
When meshing is off the extractor writes no .osrm.openareas files and the profile
behaves exactly as it did before the feature existed. The rest of this section is for
adding meshing to a profile of your own.
To opt-in to this feature, you must declare an algorithm to be used for area meshing. Find your LUA profile's @ref setup function and insert this line:
function setup()
...
area_manager:init('visgraph+dijkstra')
...
end
Note: Only the visgraph+dijkstra algorithm is available at present.
All areas to be meshed must be registered with the @ref AreaManager. In OpenStreetMap <a href="https://wiki.openstreetmap.org/wiki/Tag:highway%3Dpedestrian#Squares_and_plazas"> areas are mapped</a> either as a closed way or as a multipolygon relation. Both flavours must be configured separately.
To mesh a closed way you must register it in your @ref process_way function. Insert
following lines into your existing process_way function, immediately after the "quick
initial test":
function process_way(profile, way, result, relations)
...
if way:has_tag('highway', 'pedestrian') and way:has_true_tag('area') then
-- register the way
area_manager:way(way)
return
end
...
end
(Note that open ways cannot be meshed and will be ignored.)
To mesh a multipolygon relation you must register it in the @ref process_relation
function. The process_relation function is a newly introduced function that is called
for every relation in the input file. You'll have to create the function like this:
function process_relation(profile, relation, relations)
if relation:has_tag('type', 'multipolygon') and relation:has_tag('highway', 'pedestrian') then
-- register the relation
area_manager:relation(relation)
end
end
And you must also return the process_relation function at the end of your profile:
return {
setup = setup,
process_way = process_way,
process_node = process_node,
process_relation = process_relation, -- << add this line
...
}
At this point you have a working basic configuration. Remember that you must run
osrm-extract before your changes become effective.
While not necessary, you may want to apply further processing to the @em generated ways. The generated ways are passed to the @ref process_way function in the usual fashion. They have the same tags as the original way or relation, except:
area tag is removed on ways,type tag is removed on relations,osrm:virtual=yes tag is added.You can pick generated ways like this:
function process_way(profile, way, result, relations)
...
if way:has_key('osrm:virtual') then
-- do something with the way here
end
...
end
The feature ships EXPERIMENTAL, and a few deliberate trade-offs were left in place to keep the initial change reviewable. They are recorded here rather than in scattered TODOs so that whoever picks them up can see the reasoning.
The sweep tests every edge of the status instead of only the nearest one. The textbook keeps the sweep status ordered by distance along the sweep ray and tests only its nearest edge. That ordering cannot be maintained here: whenever the ray runs exactly through a vertex the ray/segment intersection is rejected as an endpoint hit and the edge keeps a stale distance, which is enough to put a non-blocking edge in front and report a blocked vertex as visible. Testing the whole status answers the same question without depending on the distances, at a cost that is irrelevant because the status only ever holds the few edges the ray currently crosses. See VisibilityGraph.
Boost.Geometry is still used for the geometry plumbing. bg::distance() in
AreaMesher::run_dijkstra could use %OSRM's own haversine, and the Mercator projection
could use util::web_mercator -- which would additionally remove a global object living
in an anonymous namespace in a header. Note that bg::distance() returns radians while
coordinate_calculation returns metres: the swap rescales every edge weight by the
earth radius, which leaves shortest paths unchanged but changes what
Dijkstra::distance_epsilon -- an absolute epsilon -- considers a tie. That epsilon has
to be reconsidered in the same change. The polygon and ring models, the traits
adaptations and the ~56 boost::geometry::get() accessor calls every geometry predicate
is written against are a deeper dependency and are not worth unpicking.
The area module has its own priority queue. See IndexPriorityQueue for why, and for
what would have to change to use util::QueryHeap instead.
The visibility graph has no unit tests. VisibilityGraph defines its member functions
in the header without inline, so the header can only ever be included by a single
translation unit -- today area_mesher.cpp. Since the unit test binary links
osrm_extract, a test translation unit including the header would be a duplicate-symbol
link error. Moving the definitions into a src/extractor/area/visibility_graph.cpp would
fix that, remove the same-header anonymous-namespace projection object, and let the
differential test that was used to validate the sweep (comparing it against a brute-force
reference over random polygons with holes) live in the repository.
@sa AreaManager
A complete example profile is found in the file: profiles/foot.lua,
where every step above is guarded by the enable_area_meshing flag.
https://wiki.openstreetmap.org/wiki/Relation:multipolygon
https://wiki.openstreetmap.org/wiki/Key:area
https://wiki.openstreetmap.org/wiki/Tag:highway%3Dpedestrian#Squares_and_plazas