8#include <QtConcurrent/qtconcurrentrun.h>
12 auto zoomToResolution = [&](
const double zoom) {
13 return std::max(std::min(zoom / 1.5,
static_cast<double>(
maxZoom_c)), 0.0);
16 zoomToRes_.emplace(zoom, std::floor(zoomToResolution(zoom)));
37 if (!index.isValid() || index.row() >=
pathCells_.size()) {
44 return QVariant::fromValue(
data->res());
46 return QVariant::fromValue(
data->index());
48 return QVariant::fromValue(
data->color());
50 return QVariant::fromValue(
data->path());
70 connect(
worker_, &H3_VIEWER::H3Worker::finished,
worker_, &H3_VIEWER::H3Worker::deleteLater, Qt::QueuedConnection);
71 connect(
thread_, &QThread::finished,
thread_, &QThread::deleteLater, Qt::QueuedConnection);
79 Qt::QueuedConnection);
89 Qt::QueuedConnection);
94 [
this](
const std::unordered_set<H3Index> &walls) {
worker_->
setWalls(walls); }, Qt::QueuedConnection);
102 [
this](
const QGeoCoordinate ¢er,
double radiusMeters) {
110 spdlog::info(
"H3Model: Maze bounds received - center ({}, {}), radius {} meters", center.latitude(),
111 center.longitude(), radiusMeters);
113 Qt::QueuedConnection);
117 constexpr double mazeLat = 0.0;
118 constexpr double mazeLon = 0.0;
119 constexpr int kRingRadius = 45;
122 }
catch (
const std::exception &e) {
123 spdlog::critical(
"{}", e.what());
128 if (!coordinate.isValid()) {
139 const auto it = std::ranges::find_if(
pathCells_, [res](
const auto &cell) {
return cell->res() == res; });
147 const auto it = std::ranges::find_if(
pathCells_, [
id](
const auto &cell) {
return cell->index() ==
id; });
158void H3Model::addCell(
const quint8 res,
const H3Index index,
const QVariantList &polygon,
const QColor &color) {
168 auto cell =
new H3Cell(
this);
170 cell->setIndex(index);
171 cell->setPath(polygon);
172 cell->setColor(color);
174 beginInsertRows(QModelIndex(),
static_cast<int>(
pathCells_.size()),
static_cast<int>(
pathCells_.size()));
179 std::vector<H3Index> pentagons;
180 pentagons.resize(pentagonCount());
181 for (
auto res = 2; res < 15; res++) {
182 if (
const auto err = getPentagons(res, pentagons.data()); err != E_SUCCESS) {
183 spdlog::warn(describeH3Error(err));
185 for (
const auto &pentagon : pentagons) {
187 if (!pentagonPolygon.has_value()) {
190 onCellComputed(getResolution(pentagon), pentagon, pentagonPolygon.value(),
false);
201 const bool isSearching) {
209 const QColor cellColor =
212 addCell(res, index, polygon, cellColor);
221 std::vector<std::tuple<quint8, H3Index, QVariantList, QColor>> newCells;
222 newCells.reserve(cells.size());
224 for (
const auto &[res, index, polygon] : cells) {
227 newCells.emplace_back(res, index, polygon, cellColor);
231 if (newCells.empty()) {
236 const int first =
static_cast<int>(
pathCells_.size());
237 const int last = first +
static_cast<int>(newCells.size()) - 1;
239 beginInsertRows(QModelIndex(), first, last);
241 for (
const auto &[res, index, polygon, color] : newCells) {
242 auto cell =
new H3Cell(
this);
244 cell->setIndex(index);
245 cell->setPath(polygon);
246 cell->setColor(color);
252 spdlog::debug(
"Batch inserted {} path cells", newCells.size());
256 if (indexes.empty()) {
274 spdlog::info(
"Already clearing, skipping...");
284 emit clearingStarted();
301 mazePolygons_.reserve(
static_cast<qsizetype
>(polygons.size()));
303 for (
const auto &polygon : polygons) {
307 spdlog::info(
"Maze polygons updated: {} polygons",
mazePolygons_.size());
312 searchStatsText_ = QString(
"Explored: %1 cells | Time: %2 ms | Path: %3 cells")
314 .arg(timeMs, 0,
'f', 2)
317 spdlog::info(
"Search stats: {} explored, {:.2f} ms, {} path length", exploredCells, timeMs, pathLength);
void mazeWallsGenerated(const std::unordered_set< H3Index > &walls)
void generateMazeAsync(double lat, double lon, int kRingRadius)
void mazeRadiusComputed(const QGeoCoordinate ¢er, double radiusMeters)
~H3Model() override
Destructor.
QHash< int, QByteArray > roleNames() const override
Returns the role names for QML access.
void mazeRadiusChanged()
Emitted when maze radius changes.
QString searchStatsText_
Formatted search statistics.
void mazePolygonsChanged()
Emitted when maze polygons change.
void requestCells(const std::vector< H3Index > &indexes)
Requests visualization of specific cells.
void mazeWallsGenerated(const std::unordered_set< H3Index > &walls)
Emitted when maze walls are generated.
int rowCount(const QModelIndex &parent) const override
Returns the number of cells in the model.
bool isCoordinateTargetValid(quint8 zoom, const QGeoCoordinate &coordinate) const
Validates if a coordinate is a valid target.
void coordinatesChanged()
Emitted when coordinates property changes.
QGeoCoordinate mazeCenter_
Maze center coordinate.
void addCell(quint8 res, H3Index index, const QVariantList &polygon, const QColor &color)
Adds a new cell to the model.
void clearingFinished()
Emitted when cell clearing completes.
void mazeBoundsGenerated(const QGeoCoordinate ¢er, double radiusMeters)
Emitted with maze boundary information.
const uint8_t maxZoom_c
Maximum supported zoom level.
void onCellComputed(quint8 res, H3Index index, const QVariantList &polygon, bool isSearching)
Handles individual cell computation during search.
H3Model(QObject *parent=nullptr)
Constructs an H3Model.
std::optional< H3Cell * > findCellByRes(quint8 res) const
Finds a cell by its resolution.
void onSearchStats(int exploredCells, double timeMs, int pathLength)
Handles search statistics update.
void onMazePolygonsComputed(const std::vector< QVariantList > &polygons)
Handles maze polygon computation results.
void Init()
Initializes the model and starts worker thread.
QString getColorForResolution(quint8 resolution) const
Gets the display color for a resolution level.
void onCellsComputed(const QVariantList &list)
Handles batch cell computation results.
double mazeRadius_
Maze boundary radius in meters.
@ PathRole
Whether cell is part of path.
@ CellColor
Cell display color.
@ IndexRole
H3 cell index.
@ ResRole
Cell resolution (3-15).
QList< H3Cell * > pathCells_
Cells that are part of the current path.
const uint8_t minZoom_c
Minimum supported zoom level.
QVariantList coordinates_
Cell coordinates for display.
QThread * thread_
Worker thread.
QVariant data(const QModelIndex &index, int role) const override
Returns data for a cell at the given index.
H3_VIEWER::H3Worker * worker_
Worker for async operations.
std::optional< H3Cell * > findCellByID(quint64 id) const
Finds a cell by its H3 index.
std::atomic_bool isClearing_
Flag indicating clearing in progress.
void clearAllCells()
Clears all cells from the model.
std::unordered_map< uint8_t, uint8_t > zoomToRes_
Zoom to resolution mapping.
const QHash< int, QString > resolutionColors_c
Color palette for different resolutions.
void searchStatsChanged()
Emitted when search statistics update.
H3MazeAdapter * mazeAdapter_
Maze generation adapter.
void addPentagons()
Adds pentagon cells to the model.
void onPathCellsBatch(const std::vector< std::tuple< quint8, H3Index, QVariantList > > &cells)
Handles batch cell computation for path visualization (optimized).
QList< QVariantList > mazePolygons_
Maze wall polygons.
void mazeCenterChanged()
Emitted when maze center changes.
Worker thread for asynchronous H3 operations.
void cellsComputed(const QVariantList &polygon)
Emitted when batch cell computation completes.
void mazePolygonsComputed(const std::vector< QVariantList > &polygons)
Emitted with merged maze wall polygons for rendering.
void requestCell(const std::vector< H3Index > &index)
Requests computation for a set of H3 cells.
void searchStats(int exploredCells, double timeMs, int pathLength)
Emitted with pathfinding statistics after search completes.
void cellComputed(quint8 res, H3Index id, const QVariantList &polygon, bool isSearching)
Emitted when a single cell computation completes.
void setWalls(const std::unordered_set< H3Index > &mazeWalls)
Sets maze walls for pathfinding obstacle avoidance.
void doWork()
Main work loop executed in the worker thread.
Qt list model for managing H3 hexagonal cells and pathfinding visualization.
Worker thread for asynchronous H3 cell processing and pathfinding.
static std::optional< QVariantList > indexToPolygon(const H3Index index)