13#include <QtLocation/private/qgeotilespec_p.h>
14#include <QtNetwork/QNetworkAccessManager>
15#include <QtNetwork/QNetworkRequest>
24 constexpr int kMaxCarpetGridSize = 10000;
31 return _terrainTileManager();
36 , _networkManager(new QNetworkAccessManager(this))
38 qCDebug(TerrainTileManagerLog) <<
this;
47 qCDebug(TerrainTileManagerLog) <<
this;
54 const QString elevationProviderName = SettingsManager::instance()->flightMapSettings()->
elevationMapProvider()->rawValue().toString();
56 for (
const QGeoCoordinate &coordinate: coordinates) {
58 provider->getMapName(),
59 provider->long2tileX(coordinate.longitude(), 1),
60 provider->lat2tileY(coordinate.latitude(), 1),
63 qCDebug(TerrainTileManagerLog) <<
"hash:coordinate" << tileHash << coordinate;
67 const double elevation = tile->
elevation(coordinate);
68 if (qIsNaN(elevation)) {
70 qCWarning(TerrainTileManagerLog) <<
"Internal Error: missing elevation in tile cache";
72 qCDebug(TerrainTileManagerLog) <<
"returning elevation from tile cache" << elevation;
74 altitudes.push_back(elevation);
77 spec.setX(provider->long2tileX(coordinate.longitude(), 1));
78 spec.setY(provider->lat2tileY(coordinate.latitude(), 1));
80 spec.setMapId(provider->getMapId());
83 (void) connect(reply, &QGeoTiledMapReplyQGC::finished,
this, &TerrainTileManager::_terrainDone);
100 qCDebug(TerrainTileManagerLog) <<
"count" << coordinates.count();
102 if (coordinates.isEmpty()) {
107 QList<double> altitudes;
109 qCDebug(TerrainTileManagerLog) <<
"queue count" << _requestQueue.count();
110 const QueuedRequestInfo_t queuedRequestInfo = {
111 terrainQueryInterface,
120 _requestQueue.enqueue(queuedRequestInfo);
125 QList<double> noAltitudes;
126 qCWarning(TerrainTileManagerLog) <<
"signalling failure due to internal error";
131 qCDebug(TerrainTileManagerLog) <<
"all altitudes taken from cached data";
137 double distanceBetween;
138 double finalDistanceBetween;
139 const QList<QGeoCoordinate> coordinates = _pathQueryToCoords(startPoint, endPoint, distanceBetween, finalDistanceBetween);
142 QList<double> altitudes;
144 qCDebug(TerrainTileManagerLog) <<
"queue count" << _requestQueue.count();
145 const QueuedRequestInfo_t queuedRequestInfo = {
146 terrainQueryInterface,
149 finalDistanceBetween,
155 _requestQueue.enqueue(queuedRequestInfo);
160 QList<double> noAltitudes;
161 qCWarning(TerrainTileManagerLog) <<
"signalling failure due to internal error";
162 terrainQueryInterface->
signalPathHeights(
false, distanceBetween, finalDistanceBetween, noAltitudes);
166 qCDebug(TerrainTileManagerLog) <<
"all altitudes taken from cached data";
167 terrainQueryInterface->
signalPathHeights((coordinates.count() == altitudes.count()), distanceBetween, finalDistanceBetween, altitudes);
172 if (swCoord.longitude() > neCoord.longitude() || swCoord.latitude() > neCoord.latitude()) {
173 qCWarning(TerrainTileManagerLog) <<
"Invalid carpet bounds: SW must be south-west of NE";
174 terrainQueryInterface->
signalCarpetHeights(
false, qQNaN(), qQNaN(), QList<QList<double>>());
181 if (gridSizeLat <= 0 || gridSizeLon <= 0) {
182 qCWarning(TerrainTileManagerLog) <<
"Carpet area too small";
183 terrainQueryInterface->
signalCarpetHeights(
false, qQNaN(), qQNaN(), QList<QList<double>>());
187 if (gridSizeLat > kMaxCarpetGridSize || gridSizeLon > kMaxCarpetGridSize) {
188 qCWarning(TerrainTileManagerLog) <<
"Carpet area too large"
189 <<
"gridSizeLat:" << gridSizeLat
190 <<
"gridSizeLon:" << gridSizeLon
191 <<
"maxGridSize:" << kMaxCarpetGridSize;
192 terrainQueryInterface->
signalCarpetHeights(
false, qQNaN(), qQNaN(), QList<QList<double>>());
196 QList<QGeoCoordinate> coordinates;
197 for (
int latIdx = 0; latIdx <= gridSizeLat; latIdx++) {
199 for (
int lonIdx = 0; lonIdx <= gridSizeLon; lonIdx++) {
201 (void) coordinates.append(QGeoCoordinate(lat, lon));
206 QList<double> altitudes;
208 qCDebug(TerrainTileManagerLog) <<
"carpet query queued, count" << _requestQueue.count();
209 const QueuedRequestInfo_t queuedRequestInfo = {
210 terrainQueryInterface,
219 _requestQueue.enqueue(queuedRequestInfo);
224 qCWarning(TerrainTileManagerLog) <<
"signalling carpet failure due to internal error";
225 terrainQueryInterface->
signalCarpetHeights(
false, qQNaN(), qQNaN(), QList<QList<double>>());
229 double minHeight, maxHeight;
230 QList<QList<double>> carpet;
231 _processCarpetResults(altitudes, gridSizeLat + 1, gridSizeLon + 1, statsOnly, minHeight, maxHeight, carpet);
233 qCDebug(TerrainTileManagerLog) <<
"carpet altitudes from cached data, min:" << minHeight <<
"max:" << maxHeight;
237QList<QGeoCoordinate> TerrainTileManager::_pathQueryToCoords(
const QGeoCoordinate &fromCoord,
const QGeoCoordinate &toCoord,
double &distanceBetween,
double &finalDistanceBetween)
245 if (coordinates.size() >= 2) {
249 distanceBetween = finalDistanceBetween = totalDistance;
252 qCDebug(TerrainTileManagerLog) <<
"fromCoord:toCoord:distanceBetween:finalDistanceBetween:coordCount"
253 << fromCoord << toCoord << distanceBetween << finalDistanceBetween << coordinates.count();
258void TerrainTileManager::_tileFailed()
260 QList<double> noAltitudes;
262 for (
const QueuedRequestInfo_t &requestInfo: _requestQueue) {
263 if (requestInfo.terrainQueryInterface.isNull()) {
266 switch (requestInfo.queryMode) {
268 requestInfo.terrainQueryInterface->signalCoordinateHeights(
false, noAltitudes);
271 requestInfo.terrainQueryInterface->signalPathHeights(
false, requestInfo.distanceBetween, requestInfo.finalDistanceBetween, noAltitudes);
274 requestInfo.terrainQueryInterface->signalCarpetHeights(
false, qQNaN(), qQNaN(), QList<QList<double>>());
281 _requestQueue.clear();
284void TerrainTileManager::_terrainDone()
290 qCWarning(TerrainTileManagerLog) <<
"Elevation tile fetched but invalid reply data type.";
293 reply->deleteLater();
295 const QByteArray responseBytes = reply->mapImageData();
296 const QGeoTileSpec spec = reply->tileSpec();
298 if (reply->error() != QGeoTiledMapReplyQGC::NoError) {
299 qCWarning(TerrainTileManagerLog) <<
"Elevation tile fetching returned error:" << reply->errorString();
304 if (responseBytes.isEmpty()) {
305 qCWarning(TerrainTileManagerLog) <<
"Error in fetching elevation tile. Empty response.";
310 qCDebug(TerrainTileManagerLog) <<
"Received some bytes of terrain data:" << responseBytes.size();
313 _cacheTile(responseBytes, hash);
315 for (qsizetype i = _requestQueue.count() - 1; i >= 0; i--) {
317 QList<double> altitudes;
318 QueuedRequestInfo_t &requestInfo = _requestQueue[i];
320 if (requestInfo.terrainQueryInterface.isNull()) {
321 _requestQueue.removeAt(i);
329 switch (requestInfo.queryMode) {
332 qCWarning(TerrainTileManagerLog) <<
"signalling failure due to internal error";
333 QList<double> noAltitudes;
334 requestInfo.terrainQueryInterface->signalCoordinateHeights(
false, noAltitudes);
336 qCDebug(TerrainTileManagerLog) <<
"All altitudes taken from cached data";
337 requestInfo.terrainQueryInterface->signalCoordinateHeights(requestInfo.coordinates.count() == altitudes.count(), altitudes);
342 qCWarning(TerrainTileManagerLog) <<
"signalling failure due to internal error";
343 QList<double> noAltitudes;
344 requestInfo.terrainQueryInterface->signalPathHeights(
false, requestInfo.distanceBetween, requestInfo.finalDistanceBetween, noAltitudes);
346 qCDebug(TerrainTileManagerLog) <<
"All altitudes taken from cached data";
347 requestInfo.terrainQueryInterface->signalPathHeights(requestInfo.coordinates.count() == altitudes.count(), requestInfo.distanceBetween, requestInfo.finalDistanceBetween, altitudes);
352 qCWarning(TerrainTileManagerLog) <<
"signalling carpet failure due to internal error";
353 requestInfo.terrainQueryInterface->signalCarpetHeights(
false, qQNaN(), qQNaN(), QList<QList<double>>());
355 double minHeight, maxHeight;
356 QList<QList<double>> carpet;
357 _processCarpetResults(altitudes, requestInfo.carpetGridSizeLat, requestInfo.carpetGridSizeLon,
358 requestInfo.carpetStatsOnly, minHeight, maxHeight, carpet);
360 qCDebug(TerrainTileManagerLog) <<
"carpet altitudes from cached data, min:" << minHeight <<
"max:" << maxHeight;
361 requestInfo.terrainQueryInterface->signalCarpetHeights(
true, minHeight, maxHeight, carpet);
368 _requestQueue.removeAt(i);
372void TerrainTileManager::_cacheTile(
const QByteArray &data,
const QString &hash)
377 qCWarning(TerrainTileManagerLog) <<
"Received invalid tile";
381 QMutexLocker locker(&_tilesMutex);
382 if (!_tiles.contains(hash)) {
383 (void) _tiles.insert(hash, terrainTile);
389TerrainTile *TerrainTileManager::_getCachedTile(
const QString &hash)
391 QMutexLocker locker(&_tilesMutex);
393 if (!_tiles.contains(hash)) {
405void TerrainTileManager::_processCarpetResults(
const QList<double> &altitudes,
int gridSizeLat,
int gridSizeLon,
406 bool statsOnly,
double &minHeight,
double &maxHeight, QList<QList<double>> &carpet)
408 minHeight = std::numeric_limits<double>::max();
409 maxHeight = std::numeric_limits<double>::lowest();
412 for (
int latIdx = 0; latIdx < gridSizeLat; latIdx++) {
414 for (
int lonIdx = 0; lonIdx < gridSizeLon; lonIdx++) {
415 const double height = altitudes[idx++];
416 minHeight = qMin(minHeight, height);
417 maxHeight = qMax(maxHeight, height);
419 (void) row.append(height);
423 (void) carpet.append(row);
Q_GLOBAL_STATIC(FirmwarePluginFactoryRegister, _firmwarePluginFactoryRegisterInstance)
Geographic coordinate conversion utilities using GeographicLib.
#define QGC_LOGGING_CATEGORY(name, categoryStr)
std::shared_ptr< const MapProvider > SharedMapProvider
Fact *elevationMapProvider READ elevationMapProvider CONSTANT Fact * elevationMapProvider()
static QNetworkRequest getNetworkRequest(int mapId, int x, int y, int zoom)
Base class for offline/online terrain queries.
void signalPathHeights(bool success, double distanceBetween, double finalDistanceBetween, const QList< double > &heights)
void signalCoordinateHeights(bool success, const QList< double > &heights)
void signalCarpetHeights(bool success, double minHeight, double maxHeight, const QList< QList< double > > &carpet)
static constexpr double kTileValueSpacingDegrees
1 Arc-Second spacing of elevation values
static constexpr double kTileValueSpacingMeters
bool getAltitudesForCoordinates(const QList< QGeoCoordinate > &coordinates, QList< double > &altitudes, bool &error)
TerrainTileManager(QObject *parent=nullptr)
void addPathQuery(TerrainQueryInterface *terrainQueryInterface, const QGeoCoordinate &startPoint, const QGeoCoordinate &endPoint)
void addCarpetQuery(TerrainQueryInterface *terrainQueryInterface, const QGeoCoordinate &swCoord, const QGeoCoordinate &neCoord, bool statsOnly)
void addCoordinateQuery(TerrainQueryInterface *terrainQueryInterface, const QList< QGeoCoordinate > &coordinates)
double elevation(const QGeoCoordinate &coordinate) const
static QString getTileHash(QStringView type, int x, int y, int z)
static QString getProviderTypeFromQtMapId(int qtMapId)
static std::shared_ptr< const MapProvider > getMapProviderFromProviderType(QStringView type)
QList< QGeoCoordinate > interpolatePath(const QGeoCoordinate &from, const QGeoCoordinate &to, int numPoints)
double geodesicDistance(const QGeoCoordinate &from, const QGeoCoordinate &to)
void configureProxy(QNetworkAccessManager *manager)
Set up default proxy configuration on a network manager.