In part-1 I described how we validate flight-networks at Flytrex to make sure that no two nodes of a flight network are too close. Now let’s turn our attention to validating that no two edges are too close.

First, how does one define “edges being too close”? What is the distance between edges?

Here’s a good definition – the distance between edges e1 and e2 is

D(e1, e2) = min D(p1, p2) for p1 ∈ e1 and p2 ∈ e2

That means, the minimal distances of all distances between pairs of points taken from e1 and e2 respectively.

Given that definition – if two edges cross, then of course the distance between them is zero. This is useful – two edges crossing is a more extreme case of two edges being too close to one another.

So how can we implement this? Unfortunately our “closest pair” algorithm for vertices from previous post is not good enough here. I was unsure what to do – and so the easiest solution like all lazy programmers – is going to stackoverflow. Note, just looking for an existing question and answer pair is not good enough – I didn’t find a solution. As I wrote previously – it’s better to not be shy and actually ask for help.

What did I learn? I saw a suggestion to use Rtree. What is that?

Rtree is a wrapper around libspatialindex, which is a library that provides you with, well, a spatial index. A spatial index is a data structure that indexes objects according to their position in space. Read here for some theoretical details.

So our approach to solving our problem is relatively straightforward. For each edge in our graph, we will add it to our spatial index. Then, for each edge we will look up the closest edges to it, and make sure that distance between our edge and the closest edges is still more than our MINIMAL_DISTANCE.

How many edges should we retrieve? Well, that depends. Obviously, if two edges share a vertex then the distance between them is zero, however, we do not want to alert on edges that share a vertex. So we need to get the N closest edges such that N > number of neighbour edges of our edge. For some simple graphs that is simple, but for the general case that might include all of the edges (e.g. a sun-shaped graph with a single vertex all edges share.)

Here is some sample code:

```
def _prepare(self):
for idx, edge in enumerate(self.edges):
self.node_to_edges[edge.from_vertex_id].add(idx)
self.node_to_edges[edge.to_vertex_id].add(idx)
self.tree.add(idx, self._get_bounds(idx))
```

```
def validate(self):
for idx, edge in enumerate(self.edges):
neighbor_edges = (
self.node_to_edges[edge.from_vertex_id] |
self.node_to_edges[edge.to_vertex_id]) - {idx}
# The +10 there is because we want the few edges that are not
# our neighbors. (The distance to our neighbors will always
# be 0 and we should ignore them)
nearest = self.tree.nearest(
self._get_bounds(idx), len(neighbor_edges) + 10)
for nearest_idx in nearest:
if nearest_idx in neighbor_edges or nearest_idx <= idx:
continue
dist = self._get_distance(idx, nearest_idx)
if dist < MIN_EDGE_DISTANCE_M:
p1 = self.id_to_node[edge.from_vertex_id]
p2 = self.id_to_node[edge.to_vertex_id]
edge2 = self.edges[nearest_idx]
q1 = self.id_to_node[edge2.from_vertex_id]
q2 = self.id_to_node[edge2.to_vertex_id]
raise ValidationError(
f"The edges {p1.name} -> {p2.name}"
f"and {q1.name} -> {q2.name}"
f"are too close to one another. "
f"Distance is {dist:.1f}m but should be"
f"{MIN_EDGE_DISTANCE_M}m")
```

The result of this code worked well. It manages to find issues in flight networks, and does it in reasonable speed.

For myself, I’m happy to have added another tool to my arsenal – Rtree.

## Leave a Reply