Enroute Flight Navigation
A navigation app for VFR pilots
GeoMapProvider.h
1/***************************************************************************
2 * Copyright (C) 2019-2024 by Stefan Kebekus *
3 * stefan.kebekus@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 3 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20
21#pragma once
22
23#include <QCache>
24#include <QFuture>
25#include <QGeoRectangle>
26#include <QImage>
27#include <QProperty>
28#include <QQmlEngine>
29#include <QStandardPaths>
30#include <QTemporaryFile>
31#include <QTimer>
32#include <optional>
33#include <utility>
34
35#include "Airspace.h"
36#include "GlobalObject.h"
37#include "TileServer.h"
38#include "Waypoint.h"
39#include "fileFormats/MBTILES.h"
40
41using namespace Qt::Literals::StringLiterals;
42
43
44namespace GeoMaps
45{
46
65
67{
68 Q_OBJECT
69 QML_ELEMENT
70 QML_SINGLETON
71
72public:
79 explicit GeoMapProvider(QObject *parent = nullptr);
80
81 // No default constructor, important for QML singleton
82 explicit GeoMapProvider() = delete;
83
84 // deferred initialization
85 void deferredInitialization() override;
86
87 // factory function for QML singleton
88 static GeoMaps::GeoMapProvider* create(QQmlEngine* /*unused*/, QJSEngine* /*unused*/)
89 {
91 }
92
94 ~GeoMapProvider() override;
95
96
97
98 //
99 // Properties
100 //
101
106 Q_PROPERTY(QList<GeoMaps::Airspace> airspaces READ airspaces BINDABLE bindableAirspaces)
107
108
114
120 Q_PROPERTY(QString copyrightNotice READ copyrightNotice CONSTANT)
121
132
139
145 Q_PROPERTY(QByteArray geoJSON READ geoJSON BINDABLE bindableGeoJSON)
146
155 Q_PROPERTY(QStringList oversizedMaps READ oversizedMaps BINDABLE bindableOversizedMaps)
156
164 Q_PROPERTY(QString serverUrl READ serverUrl NOTIFY serverUrlChanged)
165
175 Q_PROPERTY(QString styleFileURL READ styleFileURL NOTIFY styleFileURLChanged)
176
178 Q_PROPERTY(QList<QSharedPointer<FileFormats::MBTILES>> terrainMapTiles READ terrainMapTiles NOTIFY terrainMapTilesChanged)
179
185 Q_PROPERTY(QList<GeoMaps::Waypoint> waypoints READ waypoints NOTIFY waypointsChanged)
186
187
188
189 //
190 // Getter Methods
191 //
192
197 [[nodiscard]] QList<GeoMaps::Airspace> airspaces() const {return m_airspaces.value();}
198
203 [[nodiscard]] QBindable<QList<GeoMaps::Airspace>> bindableAirspaces() {return &m_airspaces;}
204
209 [[nodiscard]] QStringList availableRasterMaps() const
210 {
211 return m_availableRasterMaps.value();
212 }
213
218 [[nodiscard]] QBindable<QStringList> bindableAvailableRasterMaps() const
219 {
220 return &m_availableRasterMaps;
221 }
222
227 [[nodiscard]] static QString copyrightNotice();
228
233 [[nodiscard]] QString currentRasterMap() const {return m_currentRasterMap.value();}
234
239 [[nodiscard]] QBindable<QString> bindableCurrentRasterMap() const {return &m_currentRasterMap;}
240
245 [[nodiscard]] int currentRasterMapTileSize() const {return m_currentRasterMapTileSize.value();}
246
251 [[nodiscard]] QBindable<int> bindableCurrentRasterMapTileSize() const {return &m_currentRasterMapTileSize;}
252
257 [[nodiscard]] QByteArray geoJSON() const {return m_combinedGeoJSON.value();}
258
263 [[nodiscard]] QBindable<QByteArray> bindableGeoJSON() {return &m_combinedGeoJSON;}
264
269 [[nodiscard]] QStringList oversizedMaps() const {return m_oversizedMaps.value();}
270
275 [[nodiscard]] QBindable<QStringList> bindableOversizedMaps() {return &m_oversizedMaps;}
276
281 [[nodiscard]] QString serverUrl() {return m_tileServer.serverUrl();}
282
287 [[nodiscard]] QString styleFileURL();
288
293 [[nodiscard]] QList<QSharedPointer<FileFormats::MBTILES>> terrainMapTiles() const
294 {
295 return m_terrainMapTiles;
296 }
297
302 [[nodiscard]] QList<Waypoint> waypoints();
303
312 [[nodiscard]] QStringList waypointSearchKeys() const { return m_waypointIndex.searchKeys; }
313
314
315
316 //
317 // Setter Methods
318 //
319
324 void setCurrentRasterMap(const QString& mapName);
325
326
327
328 //
329 // Methods
330 //
331
340 [[nodiscard]] Q_INVOKABLE QVariantList airspacesAtPosition(const QGeoCoordinate &position);
341
354 [[nodiscard]] Q_INVOKABLE GeoMaps::Waypoint closestWaypoint(QGeoCoordinate position, const QGeoCoordinate &distPosition);
355
363 [[nodiscard]] Q_INVOKABLE static GeoMaps::Waypoint createWaypoint()
364 {
365 return {};
366 }
367
375 [[nodiscard]] Q_INVOKABLE Units::Distance terrainElevationAMSL(const QGeoCoordinate& coordinate);
376
381 [[nodiscard]] Q_INVOKABLE static QByteArray emptyGeoJSON();
382
389 [[nodiscard]] Q_INVOKABLE Waypoint findByID(const QString& icaoID);
390
401 [[nodiscard]] Q_INVOKABLE QList<GeoMaps::Waypoint> nearbyWaypoints(const QGeoCoordinate& position, const QString& type);
402
403
404signals:
407
410
413
416
419
422
423private:
424 Q_DISABLE_COPY_MOVE(GeoMapProvider)
425
426 // This slot is called every time the the set of aviation maps qchanges. It
427 // fills the aviation data cache.
428 void onAviationMapsChanged();
429
430 // This slot is called every time the the set of MBTile files changes. It
431 // sets up the tile server to and generates a new style file.
432 void onMBTILESChanged();
433
434 // This slot is called just before one of the MBTile files is replaced or
435 // deleted. It releases every handle to the file, so that the file can be
436 // replaced (Windows refuses to rename over or delete an open file). The
437 // tile server is set up again with the new file in onMBTILESChanged().
438 void onMBTILESAboutToChange(const QString& fileName);
439
440 // Sets up the tile server to serve the raster map with the given name
441 // under "rasterMap" and updates the properties currentRasterMap and
442 // currentRasterMapTileSize. Does not emit styleFileURLChanged().
443 void serveRasterMap(const QString& mapName);
444
445
446 // Interal function that does most of the work for aviationMapsChanged()
447 // emits geoJSONChanged() when done. This function is meant to be run in a
448 // separate thread.
449 // Lookup structures over _waypoints_, built together with the waypoint
450 // list on the worker thread and replaced together with it. Rows are
451 // indices into _waypoints_.
452 struct WaypointIndex {
453 // First row (in name order) for every non-empty ICAO code
454 QHash<QString, int> rowByICAOCode;
455 // Per row: simplify(name) + ' ' + simplify(ICAO code)
456 QStringList searchKeys;
457 // (cell, row) pairs sorted by cell; see cellOf()
458 QList<std::pair<qint32, int>> cells;
459 };
460 struct aviationDataCacheResult {
461 QList<Waypoint> waypoints;
462 WaypointIndex index;
463 QList<Airspace> airspaces;
464 QByteArray combinedGeoJSON;
465 };
466 aviationDataCacheResult fillAviationDataCache(QStringList JSONFileNames, bool hideGlidingSectors);
467
468 // Builds the index for a waypoint list. Static and side-effect free, so
469 // that it can run on the worker thread.
470 static WaypointIndex buildWaypointIndex(const QList<Waypoint>& waypoints);
471
472 // Spatial grid cell of a coordinate: 0.1° x 0.1° cells, numbered
473 // latCell*3600 + lonCell with latCell in 0..1799 and lonCell in 0..3599.
474 static qint32 cellOf(const QGeoCoordinate& coordinate);
475
476 // Rows of all map waypoints that may lie within radiusM of position. The
477 // result is a superset of the waypoints within that distance. Returns
478 // nullopt if the position is invalid or the area is so large that the
479 // caller should scan all rows instead.
480 [[nodiscard]] std::optional<QList<int>> rowsNear(const QGeoCoordinate& position, double radiusM) const;
481
482 // This is the path under which map tiles are available on the m_tileServer.
483 // This is set to a random number that changes every time the set of MBTile
484 // files changes
485 QString _currentBaseMapPath;
486 QString _currentTerrainMapPath;
487
488 // Tile Server
489 TileServer m_tileServer;
490
491 // Temporary file that holds the current style file
492 QPointer<QTemporaryFile> m_styleFile;
493
494 //
495 // Aviation Data Cache
496 //
497 QFuture<GeoMaps::GeoMapProvider::aviationDataCacheResult> _aviationDataCacheFuture; // Future; indicates if fillAviationDataCache() is currently running
498 QTimer _aviationDataCacheTimer; // Timer used to start another run of fillAviationDataCache()
499
500 //
501 // MBTILES
502 //
503 QList<QSharedPointer<FileFormats::MBTILES>> m_baseMapVectorTiles;
504 QProperty<QList<QSharedPointer<FileFormats::MBTILES>>> m_baseMapRasterTiles;
505 QList<QSharedPointer<FileFormats::MBTILES>> m_terrainMapTiles;
506
507 QProperty<QStringList> m_availableRasterMaps;
508 QStringList computeAvailableRasterMaps();
509
510 QProperty<QString> m_currentRasterMap {u"non-empty place holder"_s};
511 QPropertyNotifier m_currentRasterMapNotifier; // Used to save the currentRasterMap
512 QProperty<int> m_currentRasterMapTileSize {512};
513
514 Q_OBJECT_BINDABLE_PROPERTY(GeoMaps::GeoMapProvider, QByteArray, m_combinedGeoJSON, &GeoMaps::GeoMapProvider::geoJSONChanged)
515 QList<Waypoint> _waypoints_; // Cache: Waypoints
516 WaypointIndex m_waypointIndex; // Lookup structures over _waypoints_
517 QProperty<QList<Airspace>> m_airspaces; // Cache: Airspaces
518
519 // Aviation maps whose file size exceeds maxAviationMapFileSize are ignored
520 // when compiling the aviation data; parsing them would exhaust the memory
521 // available on mobile devices (see issue #676). The peak memory used when
522 // parsing is roughly ten times the file size.
523 static constexpr qint64 maxAviationMapFileSize = 50*1024*1024;
524 static constexpr qint64 maxGeoJSONCacheSize = 150*1024*1024;
525 Q_OBJECT_BINDABLE_PROPERTY(GeoMaps::GeoMapProvider, QStringList, m_oversizedMaps, &GeoMaps::GeoMapProvider::oversizedMapsChanged)
526
527 // TerrainImageCache
528 QCache<qint64,QImage> terrainTileCache {6}; // Hold 6 tiles, roughly 1.2MB
529
530 // GeoJSON file
531 QString geoJSONCache {QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + u"/aviationData.json"_s};
532};
533
534} // namespace GeoMaps
A very simple class that describes an airspace.
Definition Airspace.h:33
Provides geographic information.
~GeoMapProvider() override
Destructor.
QBindable< QString > bindableCurrentRasterMap() const
Getter function for the property with the same name.
QList< Waypoint > waypoints()
Getter function for the property with the same name.
Q_INVOKABLE QList< GeoMaps::Waypoint > nearbyWaypoints(const QGeoCoordinate &position, const QString &type)
void terrainMapTilesChanged()
Notification signal for the property with the same name.
void oversizedMapsChanged()
Notification signal for the property with the same name.
QStringList oversizedMaps() const
Getter function for the property with the same name.
QBindable< QList< GeoMaps::Airspace > > bindableAirspaces()
Getter function for the property with the same name.
QBindable< QByteArray > bindableGeoJSON()
Getter function for the property with the same name.
QList< QSharedPointer< FileFormats::MBTILES > > terrainMapTiles() const
Getter function for the property with the same name.
QBindable< QStringList > bindableAvailableRasterMaps() const
Getter function for the property with the same name.
QString serverUrl()
Getter function for the property with the same name.
QStringList availableRasterMaps
Available Raster Maps.
Q_INVOKABLE Waypoint findByID(const QString &icaoID)
QString currentRasterMap
Current Raster Map.
QString currentRasterMap() const
Getter function for the property with the same name.
static Q_INVOKABLE GeoMaps::Waypoint createWaypoint()
Create invalid waypoint.
Q_INVOKABLE QVariantList airspacesAtPosition(const QGeoCoordinate &position)
List of airspaces at a given position.
static Q_INVOKABLE QByteArray emptyGeoJSON()
Create empty GeoJSON document.
QByteArray geoJSON
Union of all aviation maps in GeoJSON format.
QBindable< QStringList > bindableOversizedMaps()
Getter function for the property with the same name.
void styleFileURLChanged()
Notification signal for the property with the same name.
Q_INVOKABLE Units::Distance terrainElevationAMSL(const QGeoCoordinate &coordinate)
Elevation of terrain at a given coordinate, above sea level.
QString serverUrl
URL under which this server is presently reachable.
GeoMapProvider(QObject *parent=nullptr)
Creates a new GeoMap provider.
void geoJSONChanged()
Notification signal for the property with the same name.
QBindable< int > bindableCurrentRasterMapTileSize() const
Getter function for the property with the same name.
QString styleFileURL()
Getter function for the property with the same name.
QString copyrightNotice
Copyright notice for the map.
QStringList waypointSearchKeys() const
Search keys of the waypoints.
QList< GeoMaps::Airspace > airspaces
Available Airspaces.
QStringList oversizedMaps
Names of installed aviation maps that are too large to load.
QByteArray geoJSON() const
Getter function for the property with the same name.
QList< GeoMaps::Waypoint > waypoints
Waypoints.
void serverUrlChanged()
Notification signal for the property with the same name.
QStringList availableRasterMaps() const
Getter function for the property with the same name.
void deferredInitialization() override
Non-constructor initialization.
int currentRasterMapTileSize
Current Raster Map Tile Size.
int currentRasterMapTileSize() const
Getter function for the property with the same name.
void waypointsChanged()
Notification signal for the property with the same name.
QString styleFileURL
URL where a style file for the base map can be retrieved.
void setCurrentRasterMap(const QString &mapName)
Setter function for the property with the same name.
QListQSharedPointer< FileFormats::MBTILES > terrainMapTiles
List of terrain map MBTILES.
Q_INVOKABLE GeoMaps::Waypoint closestWaypoint(QGeoCoordinate position, const QGeoCoordinate &distPosition)
Find closest waypoint to a given position.
static QString copyrightNotice()
Getter function for the property with the same name.
Waypoint, such as an airfield, a navaid station or a reporting point.
Definition Waypoint.h:43
static Q_INVOKABLE GeoMaps::GeoMapProvider * geoMapProvider()
Pointer to appplication-wide static GeoMaps::GeoMapProvider instance.
GlobalObject(QObject *parent=nullptr)
Standard constructor.
Convenience class for distance computations.
Definition Distance.h:35