Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 69 additions & 93 deletions dijkstra_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,91 +31,42 @@
__revision__ = '$Format:%H$'

from math import sqrt
import queue
from heapq import heappush, heappop
import collections

sqrt2 = sqrt(2)


def dijkstra(start_tuple, end_tuples, block, find_nearest, feedback=None):

class Grid:
def __init__(self, matrix):
self.map = matrix
self.h = len(matrix)
self.w = len(matrix[0])
self.manhattan_boundry = None
self.curr_boundry = None

def _in_bounds(self, id):
x, y = id
return 0 <= x < self.h and 0 <= y < self.w

def _passable(self, id):
x, y = id
return self.map[x][y] is not None

def is_valid(self, id):
return self._in_bounds(id) and self._passable(id)

def neighbors(self, id):
x, y = id
results = [(x + 1, y), (x, y - 1), (x - 1, y), (x, y + 1),
(x + 1, y - 1), (x + 1, y + 1), (x - 1, y - 1), (x - 1, y + 1)]
results = list(filter(self.is_valid, results))
return results

@staticmethod
def manhattan_distance(id1, id2):
x1, y1 = id1
x2, y2 = id2
return abs(x1 - x2) + abs(y1 - y2)

@staticmethod
def min_manhattan(curr_node, end_nodes):
return min(map(lambda node: Grid.manhattan_distance(curr_node, node), end_nodes))

@staticmethod
def max_manhattan(curr_node, end_nodes):
return max(map(lambda node: Grid.manhattan_distance(curr_node, node), end_nodes))

@staticmethod
def all_manhattan(curr_node, end_nodes):
return {end_node: Grid.manhattan_distance(curr_node, end_node) for end_node in end_nodes}

def simple_cost(self, cur, nex):
cx, cy = cur
nx, ny = nex
currV = self.map[cx][cy]
offsetV = self.map[nx][ny]
if cx == nx or cy == ny:
return (currV + offsetV) / 2
else:
return sqrt2 * (currV + offsetV) / 2
block_h = len(block)
block_w = len(block[0])

result = []
grid = Grid(block)
flags = [[0 for _ in line] for line in block]
IS_END = 1
IS_VISITED = 2

end_dict = collections.defaultdict(list)
end_dict = {}
for end_tuple in end_tuples:
end_dict[end_tuple[0]].append(end_tuple)
end_row_cols = set(end_dict.keys())
end_row_col_list = list(end_row_cols)
start_row_col = start_tuple[0]
end_dict.setdefault(end_tuple[0], []).append(end_tuple)
end_row_col_list = list(end_dict.keys())
for (end_x, end_y), *_ in end_tuples:
flags[end_x][end_y] |= IS_END
start_x, start_y = start_tuple[0]

frontier = [(0.0, start_x, start_y)]
came_from = [[None for _ in row] for row in block]
cost_so_far = [[None for _ in row] for row in block]

frontier = queue.PriorityQueue()
frontier.put((0, start_row_col))
came_from = {}
cost_so_far = {}
decided = set()

if not grid.is_valid(start_row_col):
return result
if not (0 <= start_x < block_h and 0 <= start_y < block_w and block[start_x][start_y] is not None):
return []

# init progress
index = 0
distance_dic = grid.all_manhattan(start_row_col, end_row_cols)
distance_dic = {
(x2, y2): abs(start_x - x2) + abs(start_y - y2)
for x2, y2 in end_row_col_list
}
if find_nearest:
total_manhattan = min(distance_dic.values())
else:
Expand All @@ -126,23 +77,32 @@ def simple_cost(self, cur, nex):
if feedback:
feedback.setProgress(1 + 100 * (1 - bound / total_manhattan))

came_from[start_row_col] = None
cost_so_far[start_row_col] = 0
came_from[start_x][start_y] = None
cost_so_far[start_x][start_y] = 0.0

update_every = len(block) * len(block[0]) // 10000
next_update = update_every

while not frontier.empty():
_, current_node = frontier.get()
if current_node in decided:
result = []
ends_found = 0

while frontier:
its += 1
_, cx, cy = heappop(frontier)
if flags[cx][cy] & IS_VISITED:
continue
decided.add(current_node)
flags[cx][cy] |= IS_VISITED

# update the progress bar
if feedback:
next_update -= 1
if feedback and next_update == 0:
next_update = update_every
if feedback.isCanceled():
return None

index = (index + 1) % len(end_row_col_list)
target_node = end_row_col_list[index]
new_manhattan = grid.manhattan_distance(current_node, target_node)
new_manhattan = abs(cx - target_node[0]) + abs(cy - target_node[1])
if new_manhattan < distance_dic[target_node]:
if find_nearest:
curr_bound = new_manhattan
Expand All @@ -157,34 +117,50 @@ def simple_cost(self, cur, nex):
feedback.setProgress(1 + 100 * (1 - bound / total_manhattan)*(1 - bound / total_manhattan))

# reacn destination
if current_node in end_row_cols:
if flags[cx][cy] & IS_END:
path = []
costs = []
traverse_node = current_node
traverse_node = (cx, cy)
while traverse_node is not None:
path.append(traverse_node)
costs.append(cost_so_far[traverse_node])
traverse_node = came_from[traverse_node]
costs.append(cost_so_far[traverse_node[0]][traverse_node[1]])
traverse_node = came_from[traverse_node[0]][traverse_node[1]]

# start point and end point overlaps
if len(path) == 1:
path.append(start_row_col)
path.append((start_x, start_y))
costs.append(0.0)
path.reverse()
costs.reverse()
result.append((path, costs, end_dict[current_node]))
result.append((path, costs, end_dict[cx, cy]))

end_row_cols.remove(current_node)
end_row_col_list.remove(current_node)
if len(end_row_cols) == 0 or find_nearest:
ends_found += 1
if len(end_row_col_list) == ends_found or find_nearest:
break

# relax distance
for nex in grid.neighbors(current_node):
new_cost = cost_so_far[current_node] + grid.simple_cost(current_node, nex)
if nex not in cost_so_far or new_cost < cost_so_far[nex]:
cost_so_far[nex] = new_cost
frontier.put((new_cost, nex))
came_from[nex] = current_node
currV = block[cx][cy]

# Test all 8 neighbors
for nx, ny in [(cx + 1, cy), (cx, cy - 1), (cx - 1, cy), (cx, cy + 1),
(cx + 1, cy - 1), (cx + 1, cy + 1), (cx - 1, cy - 1), (cx - 1, cy + 1)]:
if not (0 <= nx < block_h and 0 <= ny < block_w):
# neighbor outside grid
continue
offsetV = block[nx][ny]
if offsetV is None:
# nodata neighbor
continue

# Cost evaluation
if cx == nx or cy == ny:
new_cost = cost_so_far[cx][cy] + (currV + offsetV) / 2
else:
new_cost = cost_so_far[cx][cy] + sqrt2 * (currV + offsetV) / 2

if cost_so_far[nx][ny] is None or new_cost < cost_so_far[nx][ny]:
cost_so_far[nx][ny] = new_cost
heappush(frontier, (new_cost, nx, ny))
came_from[nx][ny] = (cx, cy)

return result