qHexWalker 0.0.1
Hexagonal Grid Pathfinding & Maze Visualization on Interactive Maps
Loading...
Searching...
No Matches
astar.cpp
Go to the documentation of this file.
1#include "astar.h"
2#include "pathfindingErrors.h"
3
4#include <queue>
5
6H3AStar::H3AStar(QObject *parent) : QObject(parent) {}
7
8H3AStar::~H3AStar() = default;
9
10void H3AStar::setBlockedCells(const std::unordered_set<H3Index> &blocked) { blockedCells = blocked; }
11
12std::vector<H3Index> H3AStar::findShortestPath(const H3Index start, const H3Index end) {
13 // Strategy: Throw for invalid input, return empty for blocked/no path
14 if (!isValidCell(start) || !isValidCell(end)) {
15 spdlog::error("Invalid H3 cell: start=0x{:x}, end=0x{:x}", start, end);
17 }
18 if (start == end) {
19 spdlog::error("Start and end points are identical: 0x{:x}", start);
21 }
22
23 // Strategy: Warn and return empty for blocked cells
24 if (!blockedCells.empty()) {
25 if (blockedCells.contains(start)) {
26 spdlog::warn("Start cell is blocked: 0x{:x}", start);
27 return {};
28 }
29 if (blockedCells.contains(end)) {
30 spdlog::warn("End cell is blocked: 0x{:x}", end);
31 return {};
32 }
33 }
34
35 const H3Index originalStart = start;
36 const H3Index originalEnd = end;
37 const int startRes = getResolution(start);
38 const int endRes = getResolution(end);
39
40 const H3Index startRes3 = startRes != 3 ? cellToParentRes3(start) : start;
41 const H3Index endRes3 = endRes != 3 ? cellToParentRes3(end) : end;
42
43 if (startRes3 == H3_NULL || endRes3 == H3_NULL) {
44 spdlog::error("Failed to convert to resolution 3: start=0x{:x}, end=0x{:x}", start, end);
46 }
47
48 // Warn and return empty for blocked coarse cells
49 if (!blockedCells.empty()) {
50 if (blockedCells.contains(startRes3)) {
51 spdlog::warn("Start cell (res 3) is blocked: 0x{:x}", startRes3);
52 return {};
53 }
54 if (blockedCells.contains(endRes3)) {
55 spdlog::warn("End cell (res 3) is blocked: 0x{:x}", endRes3);
56 return {};
57 }
58 }
59
60 LatLng endCoord;
61 if (cellToLatLng(endRes3, &endCoord) != E_SUCCESS) {
62 spdlog::error("Failed to get coordinates for end cell: 0x{:x}", endRes3);
64 }
65
66 const std::vector<H3Index> pathRes3 = findPathAtResolution3(startRes3, endRes3, endCoord);
67 if (pathRes3.empty()) {
68 return {};
69 }
70
71 return refinePath(pathRes3, originalStart, originalEnd, startRes, endRes);
72}
73
74std::vector<H3Index> H3AStar::findPathAtResolution3(const H3Index start, const H3Index end, const LatLng &endCoord) {
75 // Bidirectional A*: поиск одновременно с двух сторон
76
77 // Forward search (от start к end)
78 std::priority_queue<Node, std::vector<Node>, std::greater<>> forwardOpen;
79 std::unordered_map<H3Index, double, H3IndexHash> forwardG;
80 std::unordered_map<H3Index, H3Index, H3IndexHash> forwardPrev;
81 std::unordered_set<H3Index, H3IndexHash> forwardClosed;
82
83 // Backward search (от end к start)
84 std::priority_queue<Node, std::vector<Node>, std::greater<>> backwardOpen;
85 std::unordered_map<H3Index, double, H3IndexHash> backwardG;
86 std::unordered_map<H3Index, H3Index, H3IndexHash> backwardPrev;
87 std::unordered_set<H3Index, H3IndexHash> backwardClosed;
88
89 // Динамическое резервирование памяти на основе расстояния между точками
90 int64_t estimatedDistance = 0;
91 if (gridDistance(start, end, &estimatedDistance) == E_SUCCESS && estimatedDistance > 0) {
92 // Для bidirectional A* резервируем (distance + 50%) на каждое направление
93 // +50% для учета препятствий и непрямых путей
94 const size_t reserveSize = std::max(size_t(estimatedDistance * 1.5), size_t(150));
95 forwardG.reserve(reserveSize);
96 forwardPrev.reserve(reserveSize);
97 forwardClosed.reserve(reserveSize);
98 backwardG.reserve(reserveSize);
99 backwardPrev.reserve(reserveSize);
100 backwardClosed.reserve(reserveSize);
101 } else {
102 // Fallback на константное значение если gridDistance не работает
103 constexpr size_t defaultReserve = 150;
104 forwardG.reserve(defaultReserve);
105 forwardPrev.reserve(defaultReserve);
106 forwardClosed.reserve(defaultReserve);
107 backwardG.reserve(defaultReserve);
108 backwardPrev.reserve(defaultReserve);
109 backwardClosed.reserve(defaultReserve);
110 }
111
112 // Инициализация
113 LatLng startCoord;
114 if (cellToLatLng(start, &startCoord) != E_SUCCESS) {
115 return {};
116 }
117
118 forwardG[start] = 0.0;
119 forwardOpen.emplace(Node{start, 0.0, heuristic(start, endCoord)});
120
121 backwardG[end] = 0.0;
122 backwardOpen.emplace(Node{end, 0.0, heuristic(end, startCoord)});
123
124 // Переменные для отслеживания встречи
125 H3Index meetingPoint = H3_NULL;
126 double bestPathCost = std::numeric_limits<double>::infinity();
127 int nodesExplored = 0;
128
129 // Попеременный поиск с двух сторон
130 while (!forwardOpen.empty() && !backwardOpen.empty()) {
131 // Проверка терминации: если лучший путь уже найден
132 const double forwardMin = forwardOpen.top().fScore;
133 const double backwardMin = backwardOpen.top().fScore;
134
135 if (forwardMin + backwardMin >= bestPathCost) {
136 break; // Оптимальный путь найден
137 }
138
139 // === FORWARD STEP ===
140 if (!forwardOpen.empty()) {
141 Node current = forwardOpen.top();
142 forwardOpen.pop();
143 nodesExplored++;
144
145 if (!forwardClosed.contains(current.cell)) {
146 forwardClosed.insert(current.cell);
147 emit newCell(current.cell);
148
149 // Проверка встречи: нашли ли мы эту ячейку с обратной стороны?
150 if (backwardClosed.contains(current.cell)) {
151 double pathCost = forwardG[current.cell] + backwardG[current.cell];
152 if (pathCost < bestPathCost) {
153 bestPathCost = pathCost;
154 meetingPoint = current.cell;
155 }
156 }
157
158 // Расширение узла
159 for (const auto neighbors = getNeighbors(current.cell); const H3Index &neighbor : neighbors) {
160 if (neighbor == H3_NULL || forwardClosed.contains(neighbor) || blockedCells.contains(neighbor)) {
161 continue;
162 }
163
164 double edgeDistance = getDistanceBetweenCells(current.cell, neighbor);
165 double tentativeG = forwardG[current.cell] + edgeDistance;
166
167 if (!forwardG.contains(neighbor) || tentativeG < forwardG[neighbor]) {
168 forwardPrev[neighbor] = current.cell;
169 forwardG[neighbor] = tentativeG;
170 double h = heuristic(neighbor, endCoord);
171 forwardOpen.emplace(Node{neighbor, tentativeG, tentativeG + h});
172 }
173 }
174 }
175 }
176
177 // === BACKWARD STEP ===
178 if (!backwardOpen.empty()) {
179 Node current = backwardOpen.top();
180 backwardOpen.pop();
181 nodesExplored++;
182
183 if (!backwardClosed.contains(current.cell)) {
184 backwardClosed.insert(current.cell);
185 emit newCell(current.cell);
186
187 // Проверка встречи
188 if (forwardClosed.contains(current.cell)) {
189 double pathCost = forwardG[current.cell] + backwardG[current.cell];
190 if (pathCost < bestPathCost) {
191 bestPathCost = pathCost;
192 meetingPoint = current.cell;
193 }
194 }
195
196 // Расширение узла
197 for (const auto neighbors = getNeighbors(current.cell); const H3Index &neighbor : neighbors) {
198 if (neighbor == H3_NULL || backwardClosed.contains(neighbor) || blockedCells.contains(neighbor)) {
199 continue;
200 }
201
202 double edgeDistance = getDistanceBetweenCells(current.cell, neighbor);
203 double tentativeG = backwardG[current.cell] + edgeDistance;
204
205 if (!backwardG.contains(neighbor) || tentativeG < backwardG[neighbor]) {
206 backwardPrev[neighbor] = current.cell;
207 backwardG[neighbor] = tentativeG;
208 double h = heuristic(neighbor, startCoord);
209 backwardOpen.emplace(Node{neighbor, tentativeG, tentativeG + h});
210 }
211 }
212 }
213 }
214 }
215
216 // Реконструкция пути через точку встречи
217 if (meetingPoint != H3_NULL) {
218 // Путь от start до meetingPoint
219 std::vector<H3Index> forwardPath;
220 H3Index current = meetingPoint;
221 while (current != start) {
222 forwardPath.emplace_back(current);
223 auto it = forwardPrev.find(current);
224 if (it == forwardPrev.end()) {
225 break;
226 }
227 current = it->second;
228 }
229 forwardPath.emplace_back(start);
230 std::ranges::reverse(forwardPath);
231
232 // Путь от meetingPoint до end
233 std::vector<H3Index> backwardPath;
234 current = meetingPoint;
235 while (current != end) {
236 auto it = backwardPrev.find(current);
237 if (it == backwardPrev.end()) {
238 break;
239 }
240 current = it->second;
241 backwardPath.emplace_back(current);
242 }
243 backwardPath.emplace_back(end);
244
245 // Объединение путей (без дублирования meetingPoint)
246 forwardPath.insert(forwardPath.end(), backwardPath.begin(), backwardPath.end());
247
248 spdlog::info("Bidirectional A* found path, explored {} nodes (meeting at 0x{:x})", nodesExplored, meetingPoint);
249 return forwardPath;
250 }
251
252 spdlog::warn("No path found, explored {} nodes", nodesExplored);
253 return {};
254}
255
256std::vector<H3Index> H3AStar::findLocalPathAtResolution(H3Index start, H3Index end, H3Index limitParent) const {
257 if (start == end) {
258 return {start};
259 }
260 if (blockedCells.contains(start) || blockedCells.contains(end)) {
261 return {}; // Blocked endpoint in local search
262 }
263
264 LatLng endCoord;
265 if (cellToLatLng(end, &endCoord) != E_SUCCESS) {
266 return {start, end};
267 }
268
269 std::priority_queue<Node, std::vector<Node>, std::greater<>> openSet;
270 std::unordered_map<H3Index, double, H3IndexHash> gScores;
271 std::unordered_map<H3Index, H3Index, H3IndexHash> previous;
272 std::unordered_set<H3Index, H3IndexHash> closedSet;
273
274 gScores[start] = 0.0;
275 openSet.push({start, 0.0, heuristic(start, endCoord)});
276
277 constexpr int maxIterations = MAX_CELLS_RES_3;
278 int iterations = 0;
279
280 while (!openSet.empty() && iterations++ < maxIterations) {
281 Node current = openSet.top();
282 openSet.pop();
283
284 if (current.cell == end) {
285 return reconstructPath(previous, start, end);
286 }
287
288 if (closedSet.contains(current.cell))
289 continue;
290 closedSet.insert(current.cell);
291
292 for (const auto neighbors = getNeighbors(current.cell); const H3Index &neighbor : neighbors) {
293 if (neighbor == H3_NULL)
294 continue;
295
296 if (closedSet.contains(neighbor) || blockedCells.contains(neighbor)) {
297 continue;
298 }
299
300 int parentRes = getResolution(limitParent);
301 H3Index neighborParent = H3_NULL;
302 cellToParent(neighbor, parentRes, &neighborParent);
303 if (neighborParent != limitParent) {
304 continue;
305 }
306
307 const double edgeDistance = getDistanceBetweenCells(current.cell, neighbor);
308 const double tentativeGScore = gScores[current.cell] + edgeDistance;
309
310 if (!gScores.contains(neighbor) || tentativeGScore < gScores[neighbor]) {
311 previous[neighbor] = current.cell;
312 gScores[neighbor] = tentativeGScore;
313 double h = heuristic(neighbor, endCoord);
314 openSet.push({neighbor, tentativeGScore, tentativeGScore + h});
315 }
316 }
317 }
318
319 // Fallback if no path (could be due to blocks)
320 return {start, end};
321}
322std::vector<H3Index> H3AStar::refineEndSegmentGradual(const H3Index prevInPath, const H3Index parentEnd,
323 const H3Index originalEnd, const int endRes) {
324 std::vector<H3Index> segment;
325 // res from 2 to 15
326 segment.reserve(15);
327
328 // Строим путь с постепенным увеличением разрешения от 2 до endRes
329 H3Index currentCell = parentEnd;
330
331 for (int res = 4; res <= endRes; ++res) {
332 // Получаем дочерние ячейки текущей ячейки на разрешении res
333 std::vector<H3Index> children = getChildrenAtResolution(currentCell, res);
334 if (children.empty()) {
335 break;
336 }
337
338 // Определяем целевую ячейку на этом разрешении
339 H3Index targetAtRes = H3_NULL;
340 if (res == endRes) {
341 targetAtRes = originalEnd;
342 } else {
343 // Находим дочернюю ячейку, содержащую originalEnd
344 for (const H3Index &child : children) {
345 H3Index childParent = H3_NULL;
346 cellToParent(originalEnd, res, &childParent);
347 if (child == childParent) {
348 targetAtRes = child;
349 break;
350 }
351 }
352 }
353
354 if (targetAtRes == H3_NULL) {
355 break;
356 }
357
358 // Находим точку входа - ячейку, ближайшую к предыдущему направлению
359 if (const H3Index entryCell = findBoundaryCellInDirection(children, targetAtRes, prevInPath);
360 entryCell != H3_NULL) {
361 // Ищем путь от точки входа к цели на текущем разрешении
362 std::vector<H3Index> subPath;
363 subPath.reserve(children.size());
364 subPath = findLocalPathAtResolution(entryCell, targetAtRes, currentCell);
365
366 segment.insert(segment.end(), subPath.begin(), subPath.end());
367 currentCell = targetAtRes;
368 }
369 }
370
371 return segment;
372}
373std::vector<H3Index> H3AStar::refineStartSegmentGradual(const H3Index originalStart, const H3Index nextInPath,
374 const int startRes) const {
375 std::vector<H3Index> segment;
376 segment.emplace_back(originalStart);
377
378 // Строим путь с постепенным уменьшением разрешения от startRes до 2
379 H3Index currentCell = originalStart;
380
381 for (int res = startRes - 1; res >= 3; --res) {
382 // Получаем родителя текущей ячейки на разрешении res
383 H3Index parent = H3_NULL;
384 if (cellToParent(currentCell, res, &parent) != E_SUCCESS) {
385 break;
386 }
387
388 // Получаем дочерние ячейки родителя на разрешении res+1
389 std::vector<H3Index> siblings = getChildrenAtResolution(parent, res + 1);
390
391 // Находим границу - ячейку, ближайшую к направлению движения
392
393 if (const H3Index boundaryCell = findBoundaryCellInDirection(siblings, currentCell, nextInPath);
394 boundaryCell != H3_NULL && boundaryCell != currentCell) {
395 // Ищем путь к границе на текущем разрешении
396
397 // Добавляем путь (без первого элемента, т.к. Он уже добавлен)
398 if (std::vector<H3Index> subPath = findLocalPathAtResolution(currentCell, boundaryCell, parent);
399 subPath.size() > 1) {
400 segment.insert(segment.end(), subPath.begin() + 1, subPath.end());
401 // FIXME do I need this?
402 currentCell = boundaryCell;
403 }
404 }
405
406 // Переходим на уровень выше (меньше разрешение)
407 currentCell = parent;
408 segment.emplace_back(currentCell);
409 }
410
411 return segment;
412}
413std::vector<H3Index> H3AStar::refinePath(const std::vector<H3Index> &pathRes3, const H3Index originalStart,
414 const H3Index originalEnd, const int startRes, const int endRes) {
415 if (pathRes3.size() < 2) {
416 return {};
417 }
418
419 std::vector<H3Index> refinedPath;
420 refinedPath.reserve(pathRes3.size() - 1);
421
422 // 1. Детализируем начало пути с плавным переходом разрешений
423 if (startRes > 3) {
424 std::vector<H3Index> startSegment = refineStartSegmentGradual(originalStart, pathRes3.at(1), startRes);
425 refinedPath.insert(refinedPath.end(), startSegment.begin(), startSegment.end());
426 } else {
427 refinedPath.emplace_back(pathRes3.front());
428 }
429
430 // 2. Добавляем средние элементы пути (если есть)
431 for (size_t i = 1; i < pathRes3.size() - 1; ++i) {
432 refinedPath.emplace_back(pathRes3[i]);
433 }
434
435 // 3. Детализируем конец пути с плавным переходом разрешений
436 if (endRes > 3 && pathRes3.size() >= 2) {
437 std::vector<H3Index> endSegment;
438 endSegment.reserve(pathRes3.size() - 1);
439 endSegment =
440 refineEndSegmentGradual(pathRes3[pathRes3.size() - 2], pathRes3[pathRes3.size() - 1], originalEnd, endRes);
441 refinedPath.insert(refinedPath.end(), endSegment.begin(), endSegment.end());
442 } else {
443 refinedPath.emplace_back(pathRes3.back());
444 }
445
446 return refinedPath;
447}
448H3Index H3AStar::findBoundaryCellInDirection(const std::vector<H3Index> &cells, const H3Index from,
449 const H3Index direction) {
450 if (cells.empty()) {
451 return H3_NULL;
452 }
453
454 LatLng fromCoord, directionCoord;
455 if (cellToLatLng(from, &fromCoord) != E_SUCCESS || cellToLatLng(direction, &directionCoord) != E_SUCCESS) {
456 return cells.front();
457 }
458
459 // Вычисляем вектор направления
460 const double dirLat = directionCoord.lat - fromCoord.lat;
461 const double dirLon = directionCoord.lng - fromCoord.lng;
462
463 H3Index bestCell = H3_NULL;
464 double maxProjection = -std::numeric_limits<double>::max();
465
466 for (const H3Index &cell : cells) {
467 LatLng cellCoord;
468 if (cellToLatLng(cell, &cellCoord) != E_SUCCESS) {
469 continue;
470 }
471
472 // Вычисляем вектор к ячейке
473 const double toLat = cellCoord.lat - fromCoord.lat;
474 const double toLon = cellCoord.lng - fromCoord.lng;
475
476 // Скалярное произведение (проекция на направление)
477 if (const double projection = toLat * dirLat + toLon * dirLon; projection > maxProjection) {
478 maxProjection = projection;
479 bestCell = cell;
480 }
481 }
482
483 return bestCell != H3_NULL ? bestCell : cells.front();
484}
485std::vector<H3Index> H3AStar::getChildrenAtResolution(const H3Index parent, const int resolution) {
486 std::vector<H3Index> children;
487
488 int64_t childrenSize = 0;
489 if (cellToChildrenSize(parent, resolution, &childrenSize) != E_SUCCESS) {
490 return children;
491 }
492
493 children.resize(childrenSize);
494 if (cellToChildren(parent, resolution, children.data()) != E_SUCCESS) {
495 return {};
496 }
497
498 // Фильтруем нулевые индексы
499 std::erase(children, H3_NULL);
500
501 return children;
502}
503
504std::array<H3Index, H3AStar::MAX_NEIGHBORS> H3AStar::getNeighbors(const H3Index cell) {
505 std::array<H3Index, 7> ring = {};
506 if (const H3Error err = gridDisk(cell, 1, ring.data()); err != E_SUCCESS) {
507 return {};
508 }
509
510 int64_t maxSize = 0;
511 if (maxGridDiskSize(1, &maxSize) != E_SUCCESS) {
512 return {};
513 }
514
515 std::array<H3Index, MAX_NEIGHBORS> neighbors_ = {};
516 int count = 0;
517 for (auto i = 0; i < maxSize; i++) {
518 if (ring[i] != 0 && ring[i] != cell) {
519 neighbors_[count++] = ring[i];
520 }
521 }
522
523 return neighbors_;
524}
525double H3AStar::getDistanceBetweenCells(const H3Index cell1, const H3Index cell2) {
526 LatLng coord1, coord2;
527
528 // Получаем координаты центров ячеек
529 // Возвращаем единичное расстояние по умолчанию
530 H3Error err = cellToLatLng(cell1, &coord1);
531 if (err != E_SUCCESS) {
532 return 1.0;
533 }
534 err = cellToLatLng(cell2, &coord2);
535 if (err != E_SUCCESS) {
536 return 1.0;
537 }
538
539 // Вычисляем расстояние
540 return greatCircleDistanceM(&coord1, &coord2);
541}
542double H3AStar::heuristic(const H3Index cell, const LatLng &targetCoord) {
543 LatLng cellCoord;
544 if (cellToLatLng(cell, &cellCoord) != E_SUCCESS) {
545 return 0.0;
546 }
547
548 return greatCircleDistanceM(&cellCoord, &targetCoord);
549}
550
551std::vector<H3Index> H3AStar::reconstructPath(const std::unordered_map<H3Index, H3Index, H3IndexHash> &previous,
552 const H3Index start, const H3Index end) {
553
554 std::vector<H3Index> path;
555 path.reserve(previous.size());
556 H3Index current = end;
557
558 while (current != start) {
559 path.emplace_back(current);
560 auto it = previous.find(current);
561 if (it == previous.end()) {
562 return {}; // Путь не найден
563 }
564 current = it->second;
565 }
566
567 path.emplace_back(start);
568 std::ranges::reverse(path);
569
570 return path;
571}
572
573H3Index H3AStar::cellToParentRes3(const H3Index index) {
574 H3Index indexRes3 = H3_NULL;
575 if (cellToParent(index, 3, &indexRes3) != E_SUCCESS) {
576 return H3_NULL;
577 }
578 return indexRes3;
579}
580H3Index H3AStar::cellToChildRes3(const H3Index index) {
581 H3Index indexRes3 = H3_NULL;
582 if (cellToCenterChild(index, 3, &indexRes3) != E_SUCCESS) {
583 return H3_NULL;
584 }
585 return indexRes3;
586}
Bidirectional A* pathfinding algorithm for H3 hexagonal grids.
std::vector< H3Index > findShortestPath(H3Index start, H3Index end)
Finds the shortest path between two H3 cells.
Definition astar.cpp:12
static H3Index cellToParentRes3(H3Index index)
Converts any cell to its parent at resolution 3.
Definition astar.cpp:573
std::vector< H3Index > refinePath(const std::vector< H3Index > &pathRes3, H3Index originalStart, H3Index originalEnd, int startRes, int endRes)
Refines entire coarse path to target resolution.
Definition astar.cpp:413
static H3Index findBoundaryCellInDirection(const std::vector< H3Index > &cells, H3Index from, H3Index direction)
Finds boundary cell in a direction for path refinement.
Definition astar.cpp:448
std::vector< H3Index > refineEndSegmentGradual(H3Index prevInPath, H3Index parentEnd, H3Index originalEnd, int endRes)
Refines path segment near the end point to target resolution.
Definition astar.cpp:322
void setBlockedCells(const std::unordered_set< H3Index > &blocked)
Sets cells that cannot be traversed (obstacles).
Definition astar.cpp:10
static double heuristic(H3Index cell, const LatLng &targetCoord)
Heuristic function for A* (distance to target).
Definition astar.cpp:542
~H3AStar() override
Destructor.
static H3Index cellToChildRes3(H3Index index)
Converts resolution 3 cell to child at original resolution.
Definition astar.cpp:580
H3AStar(QObject *parent=nullptr)
Constructs an H3AStar pathfinder.
Definition astar.cpp:6
std::vector< H3Index > findLocalPathAtResolution(H3Index start, H3Index end, H3Index limitParent) const
Finds local path within a parent cell boundary.
Definition astar.cpp:256
static constexpr uint16_t MAX_CELLS_RES_3
Maximum number of cells at resolution 3 (~41K cells globally).
Definition astar.h:99
std::vector< H3Index > findPathAtResolution3(H3Index start, H3Index end, const LatLng &endCoord)
Finds path at resolution 3 using bidirectional A*.
Definition astar.cpp:74
static std::array< H3Index, MAX_NEIGHBORS > getNeighbors(H3Index cell)
Gets the 6 neighbors of a hexagonal cell.
Definition astar.cpp:504
std::vector< H3Index > refineStartSegmentGradual(H3Index originalStart, H3Index nextInPath, int startRes) const
Refines path segment near the start point to target resolution.
Definition astar.cpp:373
static std::vector< H3Index > getChildrenAtResolution(H3Index parent, int resolution)
Gets all children of a cell at specified resolution.
Definition astar.cpp:485
std::unordered_set< H3Index > blockedCells
Set of blocked (impassable) cells.
Definition astar.h:130
static std::vector< H3Index > reconstructPath(const std::unordered_map< H3Index, H3Index, H3IndexHash > &previous, H3Index start, H3Index end)
Reconstructs path from the visited nodes map.
Definition astar.cpp:551
static double getDistanceBetweenCells(H3Index cell1, H3Index cell2)
Calculates geographic distance between two cells.
Definition astar.cpp:525
Exception class for pathfinding errors.
Error handling strategy for pathfinding algorithms.
@ ConversionError
Error converting between H3 resolutions.
@ SameStartEnd
Start and end points are identical.
@ CoordinateError
Error getting cell coordinates.
@ InvalidCell
One or both cells are invalid (H3_NULL or failed isValidCell)
Internal node structure for A* priority queue.
Definition astar.h:108
H3Index cell
The H3 cell index.
Definition astar.h:109