valhalla / valhalla/valhalla

Marking Islands with Reachability

Open
#1,906 0 comments 0 reactions 0 assignees View on GitHub
bug mjolnir
Dominant language
C++
Stars
6.2k
Forks
981
Avg merge
2d 14h
Merged PRs (30d)
14

Description

back in #711 we went to great lengths to measure reachability about a location. we did this so we could quickly tell if an edge candidate for a route location was on an isolated island. this works pretty well in practice in that the router will see disconnected areas in the graph and make sure to use candidates which have a chance of having a connection between them.

the problem is, some islands are pretty freaking large. what happens then is that the reachability checks pass but there isnt actually a path from one large island to the next. we have another check for this that basically says if no adjacent set of tiles exists between the two locations dont even bother. the problem with this mitigation strategy is that it its only a coarse approximation (tiles are .25 degrees by .25 degress at their finest granularity).

one great example of this is routing between two large islands known as North America and South America. it turns out that there is no path between these areas, not even a ferry. unfortunately all of our mitigation techniques above fail in this scenario (that is they fail to see that the route is impossible and they actually attempt to compute the route).

we've been thinking for a while about how to solve this problem and we think a possible solution is numbering continuous groups of edges by which island they reside in. the idea is something like the following:

```python
//before validator does edge binning
//which island id you are currently working on
island_id = 0
//a place to know which edges have been already assigned and to what island
edge_island_sequence = {}
//a place to know how large each island is (in number of edges)
island_sizes_sequence = {}
//look at each tile
for each tile
for each edge in tile that is not in edge_island_sequence
//start walking from this edge collecting all connected edges, dont use costing, just use any access allowed
edge_set_sequence = walk from edge until no more adjacent edges
//mark each edges island id and how many edges were in its island
for each edge in edge_set_sequence
edge_island_sequence.push_back({edge, island})
island_sizes_sequence.push_back({island++, edge_set_sequence.size()})

//we sort edge_island_sequence by tile then by edgeid and then enumerate the start of each tile in the sequence
edge_island_sequence.sort(tile_predicate)
tile_enumeration = edge_island_sequence.enumerate(tile_enum_predicate)

//we then need to sort the island sizes sequence by size in decending order
island_sizes_sequence.sort(decending_predicate)
//and trim off the islands at the bottom who are at position >= 2^18 of the list
//we do this because we have 18 bits spare in the graphid, which we store in the bins
//we'll set the largest island id to map to 2^18-1 and the smallest one to 1, 0 is reserved
//for all islands who were so small they no longer fit in the list, before resizing we can
//print the size of the largest island that would be removed for informational purposes
island_sizes_sequence.resize(2^18 -1)
//build a lookup table from old island id to new
island_look_up = {}
new_island_id = 2^18-1
for id_size in island_sizes_sequence:
island_look_up.emplace({id_size.id, new_island_id--})

//then as we are binning edges we will look them up and set their spare bits to their island id
for edge in binned_edges:
tile_start = tile_enumeration.find(edge.tile)
island = edge_island_squence[tile_start + edge.id].island
found = island_look_up.find(island)
if(found)
edge.island = found.new_island_id
else
edge.island = 0
```

Then in loki when we get edge candidates we can set a new parameter on PathEdges that tells what island the edge is in. This is all optional as well. We can making building this dataset optional. Basically since 0 means we dont know what island its in the logic as it is today will work the same. And we can just add additional logic that says, `if locationA.island != locationB.island then fail`. When we dont have the data they will both be 0. So its a backwards compatible change as well.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.