Skip to content

Commit

Permalink
Optimizing matrix generation: it is now generated at init and modifie…
Browse files Browse the repository at this point in the history
…d depending on the current steps
  • Loading branch information
JustineFricou committed May 2, 2024
1 parent bd03181 commit 429a2b6
Showing 1 changed file with 71 additions and 38 deletions.
109 changes: 71 additions & 38 deletions geotrek/core/path_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ def __init__(self):
graph = self.graph_edges_nodes_of_qs(Path.objects.exclude(draft=True))
self.nodes = graph['nodes']
self.edges = graph['edges']
self.dijk_matrix = self.get_cs_graph()

def generate_id(self):
new_id = self.id_count
self.id_count += 1
return new_id

def graph_edges_nodes_of_qs(self, qs):
"""
Expand Down Expand Up @@ -75,10 +81,38 @@ def get_key_optimizer():
'nodes': dict(nodes),
}

def generate_id(self):
new_id = self.id_count
self.id_count += 1
return new_id
def get_cs_graph(self):

nb_of_nodes = len(self.nodes)
matrix = np.zeros((nb_of_nodes, nb_of_nodes))

nodes_list = list(self.nodes.items())
for i, (key1, value1) in enumerate(nodes_list[:-1]):
# The last row is left blank and j starts at i + 1 because only the
# upper triangle is filled and the main diagonal is all zeros (the
# weight from a node to itself is 0)
for j, (key2, value2) in enumerate(nodes_list[i + 1:]):
if key2 in value1.keys():
# If the nodes are linked by a single edge, the weight is
# the edge length ; if not, the weight is 0
edge_id = self.get_edge_id_by_nodes(value1, value2)
edge_weight = self.get_edge_weight(edge_id)
if edge_weight is not None:
matrix[i][j + i + 1] = edge_weight
# TODO: add matrix[j + i + 1][i] = edge_weight
# instead of using triangles

# Fill the lower triangle of the matrix symmetrically to the upper one
lower_triangle = np.triu(matrix, 1).T
matrix += lower_triangle

return matrix

def get_edge_weight(self, edge_id):
edge = self.edges.get(edge_id)
if edge is None:
return None
return edge.get('length')

def get_route(self, steps):
self.steps = steps
Expand All @@ -103,6 +137,7 @@ def compute_list_of_paths(self):

def compute_two_steps_line_strings(self, from_step, to_step):
from_node_info, to_node_info = self.add_steps_to_graph(from_step, to_step)
self.add_steps_to_matrix(from_node_info, to_node_info)

shortest_path = self.get_shortest_path(from_node_info['node_id'],
to_node_info['node_id'])
Expand All @@ -112,6 +147,7 @@ def compute_two_steps_line_strings(self, from_step, to_step):
# Restore the graph (remove the steps)
self.remove_step_from_graph(from_node_info)
self.remove_step_from_graph(to_node_info)
self.remove_steps_from_matrix()
return line_strings

def add_steps_to_graph(self, from_step, to_step):
Expand Down Expand Up @@ -288,9 +324,37 @@ def split_edge_in_three(self, from_point, to_point):
}
return new_node_info_1, new_node_info_2

def add_steps_to_matrix(self, from_node_info, to_node_info):
length = len(self.dijk_matrix)
# Add the last two rows
new_rows = [np.zeros(length) for i in range(2)]
self.dijk_matrix = np.vstack((self.dijk_matrix, new_rows))
# add the last two columns
new_columns = np.zeros((length + 2, 2))
self.dijk_matrix = np.hstack((self.dijk_matrix, new_columns))

# Add the weights
# TODO: this is duplicate code -> create a method
from_node = self.nodes[from_node_info['node_id']]
to_node = self.nodes[to_node_info['node_id']]
for i, (key, value) in enumerate(list(self.nodes.items())):
for j, step in enumerate([from_node, to_node]):
if key in step.keys():
edge_id = self.get_edge_id_by_nodes(step, value)
edge_weight = self.get_edge_weight(edge_id)
if edge_weight is not None:
self.dijk_matrix[i][length + j] = edge_weight
self.dijk_matrix[length + j][i] = edge_weight

def remove_steps_from_matrix(self):
length = len(self.dijk_matrix)
# Remove the last two rows
self.dijk_matrix = np.delete(self.dijk_matrix, [length - 1, length - 2], 0)
# Remove the last two columns
self.dijk_matrix = np.delete(self.dijk_matrix, [length - 1, length - 2], 1)

def get_shortest_path(self, from_node_id, to_node_id):
cs_graph = self.get_cs_graph()
matrix = csr_matrix(cs_graph)
matrix = csr_matrix(self.dijk_matrix)

# List of all nodes IDs -> to interprete dijkstra results
self.nodes_ids = list(self.nodes.keys())
Expand All @@ -302,7 +366,7 @@ def get_node_idx_per_id(node_id):
return None

def get_node_id_per_idx(node_idx):
if node_idx >= len(self.nodes_ids):
if node_idx < 0 or node_idx >= len(self.nodes_ids):
return None
return self.nodes_ids[node_idx]

Expand All @@ -323,37 +387,6 @@ def get_node_id_per_idx(node_idx):
path.reverse()
return path

def get_cs_graph(self):

def get_edge_weight(edge_id):
edge = self.edges.get(edge_id)
if edge is None:
return None
return edge.get('length')

nb_of_nodes = len(self.nodes)
matrix = np.zeros((nb_of_nodes, nb_of_nodes))

nodes_list = list(self.nodes.items())
for i, (key1, value1) in enumerate(nodes_list[:-1]):
# The last row is left blank and j starts at i + 1 because only the
# upper triangle is filled and the main diagonal is all zeros (the
# weight from a node to itself is 0)
for j, (key2, value2) in enumerate(nodes_list[i + 1:]):
if key2 in value1.keys():
# If the nodes are linked by a single edge, the weight is
# the edge length ; if not, the weight is 0
edge_id = self.get_edge_id_by_nodes(value1, value2)
edge_weight = get_edge_weight(edge_id)
if edge_weight is not None:
matrix[i][j + i + 1] = edge_weight

# Fill the lower triangle of the matrix symmetrically to the upper one
lower_triangle = np.triu(matrix, 1).T
matrix += lower_triangle

return matrix

def node_list_to_line_strings(self, node_list, from_node_info, to_node_info):
line_strings = []
# Get a LineString for each pair of adjacent nodes in the path
Expand Down

0 comments on commit 429a2b6

Please sign in to comment.