QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
HeightSource.cc
Go to the documentation of this file.
1#include "HeightSource.h"
2
3#include <QtCore/QTimer>
4
5#include <cmath>
6#include <utility>
7
8#include "HeightField.h"
9
10HeightSource::HeightSource(QObject* parent) : QObject(parent) {}
11
13{
14 return ++_requestIdCounter;
15}
16
18{
19 const int requestId = _nextRequestId();
20 if (gridSize < 1) {
21 _pending.insert(requestId, QList<float>());
22 QTimer::singleShot(0, this, [this, requestId] {
23 if (_pending.remove(requestId)) {
24 emit patchHeightsFailed(requestId);
25 }
26 });
27 return requestId;
28 }
29
30 const QPointF minCorner = TileMath::tileMinCorner(key);
31 const double span = TileMath::tileSpanAtZoom(key.zoom);
32 const double step = span / gridSize;
33 const int verticesPerEdge = gridSize + 1;
34
35 QList<float> heights;
36 heights.reserve(qsizetype(verticesPerEdge) * verticesPerEdge);
37 // Row-major from the north-west corner (max y), matching texture/tile row order
38 for (int row = 0; row < verticesPerEdge; row++) {
39 const double y = minCorner.y() + span - (row * step);
40 for (int col = 0; col < verticesPerEdge; col++) {
41 const double x = minCorner.x() + (col * step);
42 heights.append(heightAtWorld(QPointF(x, y)));
43 }
44 }
45
46 _pending.insert(requestId, heights);
47 QTimer::singleShot(0, this, [this, requestId] { _deliver(requestId); });
48 return requestId;
49}
50
52{
53 _pending.remove(requestId);
54}
55
57{
58 if (!_heightField || !TileMath::isValidKey(key)) {
59 return false;
60 }
61 if (_heightField->hasTile(key)) {
62 return true;
63 }
64
65 // Sample-center convention matching the terrarium tile layout (see
66 // HeightField's bilinear sampling)
67 const QPointF minCorner = TileMath::tileMinCorner(key);
68 const double span = TileMath::tileSpanAtZoom(key.zoom);
70 grid.width = kSynthGridSize;
72 grid.heights.reserve(qsizetype(kSynthGridSize) * kSynthGridSize);
73 for (int row = 0; row < kSynthGridSize; row++) {
74 const double y = minCorner.y() + span - (span * (row + 0.5) / kSynthGridSize);
75 for (int col = 0; col < kSynthGridSize; col++) {
76 const double x = minCorner.x() + (span * (col + 0.5) / kSynthGridSize);
77 grid.heights.append(heightAtWorld(QPointF(x, y)));
78 }
79 }
80
81 // Deliver through the event loop so consumers see the same async flow as
82 // a real tile fetch
83 QTimer::singleShot(0, this, [this, key, grid = std::move(grid)]() mutable {
84 if (_heightField && !_heightField->hasTile(key)) {
85 _heightField->insertTile(key, std::move(grid));
86 }
87 });
88 return true;
89}
90
91void ProceduralHeightSource::_deliver(int requestId)
92{
93 const auto it = _pending.constFind(requestId);
94 if (it == _pending.constEnd()) {
95 return; // cancelled
96 }
97 const QList<float> heights = it.value();
98 _pending.erase(it);
99 emit patchHeightsReady(requestId, heights);
100}
101
102float FlatHeightSource::heightAtWorld(const QPointF& world) const
103{
104 Q_UNUSED(world);
105 return 0.0f;
106}
107
108float DebugHeightSource::heightAtWorld(const QPointF& world) const
109{
110 const double phase = (2.0 * M_PI) / kWavelength;
111 const double normalized = (std::sin(world.x() * phase) * std::cos(world.y() * phase) + 1.0) / 2.0;
112 return static_cast<float>(normalized * kAmplitude);
113}
float heightAtWorld(const QPointF &world) const override
Height in meters at a world-space ground position.
static constexpr double kWavelength
hill spacing (world m)
static constexpr double kAmplitude
peak height (m)
float heightAtWorld(const QPointF &world) const override
Height in meters at a world-space ground position.
bool hasTile(const TileMath::TileKey &key) const
True when the backing pyramid holds this exact tile.
Definition HeightField.h:71
void patchHeightsReady(int requestId, const QList< float > &heights)
heights has (gridSize+1)^2 entries, row-major from the north-west corner
HeightField * _heightField
tile delivery target for requestTile (not owned)
HeightSource(QObject *parent=nullptr)
int _nextRequestId()
static constexpr int kSynthGridSize
samples per edge of a synthesized tile
void cancelRequest(int requestId) final
Cancel a pending request. No signal is emitted for a cancelled request.
virtual float heightAtWorld(const QPointF &world) const =0
Height in meters at a world-space ground position.
int requestPatchHeights(const TileMath::TileKey &key, int gridSize) final
Request the vertex height grid for a patch. Returns a request id (> 0).
bool requestTile(const TileMath::TileKey &key) override
QPointF tileMinCorner(const TileKey &key)
South-west (minimum x/y) corner of a tile in world meters.
Definition TileMath.cc:58
bool isValidKey(const TileKey &key)
True if zoom is within [kMinZoom, kMaxZoom] and x/y address a tile at that zoom.
Definition TileMath.cc:44
double tileSpanAtZoom(int zoom)
Edge length of one tile at zoom, in world meters.
Definition TileMath.cc:53
Decoded elevation samples for one tile, row-major from the NW corner.