QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GeoJsonHelper.cc
Go to the documentation of this file.
1#include "GeoJsonHelper.h"
2#include "JsonHelper.h"
3#include "JsonParsing.h"
5
6#include <QtCore/QFile>
7#include <QtCore/QJsonValue>
8#include <QtCore/QVariantMap>
9#include <QtLocation/private/qgeojson_p.h>
10#include <QtPositioning/QGeoCoordinate>
11#include <QtPositioning/QGeoPath>
12#include <QtPositioning/QGeoPolygon>
13
14QGC_LOGGING_CATEGORY(GeoJsonHelperLog, "Utilities.GeoJsonHelper")
15
17{
18 QJsonDocument _loadFile(const QString &filePath, QString &errorString);
19 QVariantList _extractShapeValues(const QVariantList &values);
20 void _extractShapeValuesRecursive(const QVariant &value, QVariantList &shapes, int depth = 0);
21
22 constexpr int _maxRecursionDepth = 32;
23 constexpr const char *_errorPrefix = QT_TR_NOOP("GeoJson file load failed. %1");
24}
25
26void GeoJsonHelper::_extractShapeValuesRecursive(const QVariant &value, QVariantList &shapes, int depth)
27{
28 if (depth >= _maxRecursionDepth) {
29 return;
30 }
31
32 if (value.canConvert<QGeoPolygon>() || value.canConvert<QGeoPath>() || value.canConvert<QGeoShape>()) {
33 (void) shapes.append(value);
34 }
35
36 if (value.typeId() == QMetaType::QVariantList) {
37 const QVariantList children = value.toList();
38 for (const QVariant &child : children) {
39 _extractShapeValuesRecursive(child, shapes, depth + 1);
40 }
41 } else if (value.typeId() == QMetaType::QVariantMap) {
42 const QVariantMap map = value.toMap();
43 for (auto it = map.cbegin(); it != map.cend(); ++it) {
44 _extractShapeValuesRecursive(it.value(), shapes, depth + 1);
45 }
46 }
47}
48
49QVariantList GeoJsonHelper::_extractShapeValues(const QVariantList &values)
50{
51 QVariantList shapes;
52 for (const QVariant &value : values) {
53 _extractShapeValuesRecursive(value, shapes);
54 }
55 return shapes;
56}
57
58QJsonDocument GeoJsonHelper::_loadFile(const QString &filePath, QString &errorString)
59{
60 errorString.clear();
61
62 QFile file(filePath);
63 if (!file.exists()) {
64 errorString = QString(_errorPrefix).arg(
65 QString(QT_TRANSLATE_NOOP("GeoJson", "File not found: %1")).arg(filePath));
66 return QJsonDocument();
67 }
68
69 if (!file.open(QIODevice::ReadOnly)) {
70 errorString = QString(_errorPrefix).arg(
71 QString(QT_TRANSLATE_NOOP("GeoJson", "Unable to open file: %1 error: %2"))
72 .arg(filePath, file.errorString()));
73 return QJsonDocument();
74 }
75
76 QJsonDocument jsonDoc;
77 const QByteArray bytes = file.readAll();
78 if (!JsonParsing::isJsonFile(bytes, jsonDoc, errorString)) {
79 errorString = QString(_errorPrefix).arg(errorString);
80 }
81
82 return jsonDoc;
83}
84
85ShapeFileHelper::ShapeType GeoJsonHelper::determineShapeType(const QString &filePath, QString &errorString)
86{
87 using ShapeType = ShapeFileHelper::ShapeType;
88
89 const QJsonDocument jsonDoc = GeoJsonHelper::_loadFile(filePath, errorString);
90 if (!errorString.isEmpty()) {
91 return ShapeType::Error;
92 }
93
94 const QVariantList imported = QGeoJson::importGeoJson(jsonDoc);
95 const QVariantList shapes = _extractShapeValues(imported);
96 if (shapes.isEmpty()) {
97 errorString = QString(_errorPrefix).arg(
98 QT_TRANSLATE_NOOP("GeoJson", "No shapes found in GeoJson file."));
99 return ShapeType::Error;
100 }
101
102 for (const QVariant &shapeVar : shapes) {
103 if (shapeVar.canConvert<QGeoPolygon>()) {
104 return ShapeType::Polygon;
105 }
106 if (shapeVar.canConvert<QGeoPath>()) {
107 return ShapeType::Polyline;
108 }
109 if (shapeVar.canConvert<QGeoShape>()) {
110 const QGeoShape shape = shapeVar.value<QGeoShape>();
111 if (shape.type() == QGeoShape::PolygonType) {
112 return ShapeType::Polygon;
113 }
114 if (shape.type() == QGeoShape::PathType) {
115 return ShapeType::Polyline;
116 }
117 }
118 }
119
120 errorString = QString(_errorPrefix).arg(
121 QT_TRANSLATE_NOOP("GeoJson", "No supported type found in GeoJson file."));
122 return ShapeType::Error;
123}
124
125bool GeoJsonHelper::loadPolygonFromFile(const QString &filePath, QList<QGeoCoordinate> &vertices, QString &errorString)
126{
127 errorString.clear();
128 vertices.clear();
129
130 const QJsonDocument jsonDoc = GeoJsonHelper::_loadFile(filePath, errorString);
131 if (!errorString.isEmpty()) {
132 return false;
133 }
134
135 const QVariantList imported = QGeoJson::importGeoJson(jsonDoc);
136 const QVariantList shapes = _extractShapeValues(imported);
137 if (shapes.isEmpty()) {
138 errorString = QString(_errorPrefix).arg(
139 QT_TRANSLATE_NOOP("GeoJson", "No polygon data found in GeoJson file."));
140 return false;
141 }
142
143 for (const QVariant &shapeVar : shapes) {
144 if (shapeVar.canConvert<QGeoPolygon>()) {
145 const QGeoPolygon poly = shapeVar.value<QGeoPolygon>();
146 vertices = poly.perimeter();
147 return true;
148 }
149 if (shapeVar.canConvert<QGeoShape>()) {
150 const QGeoShape shape = shapeVar.value<QGeoShape>();
151 if (shape.type() == QGeoShape::PolygonType) {
152 const QGeoPolygon poly(shape);
153 vertices = poly.perimeter();
154 if (!vertices.isEmpty()) {
155 return true;
156 }
157 }
158 }
159 }
160
161 errorString = QString(_errorPrefix).arg(
162 QT_TRANSLATE_NOOP("GeoJson", "No polygon found in GeoJson file."));
163 return false;
164}
165
166bool GeoJsonHelper::loadPolylineFromFile(const QString &filePath, QList<QGeoCoordinate> &coords, QString &errorString)
167{
168 errorString.clear();
169 coords.clear();
170
171 const QJsonDocument jsonDoc = GeoJsonHelper::_loadFile(filePath, errorString);
172 if (!errorString.isEmpty()) {
173 return false;
174 }
175
176 const QVariantList imported = QGeoJson::importGeoJson(jsonDoc);
177 const QVariantList shapes = _extractShapeValues(imported);
178 if (shapes.isEmpty()) {
179 errorString = QString(_errorPrefix).arg(
180 QT_TRANSLATE_NOOP("GeoJson", "No polyline data found in GeoJson file."));
181 return false;
182 }
183
184 for (const QVariant &shapeVar : shapes) {
185 if (shapeVar.canConvert<QGeoPath>()) {
186 const QGeoPath path = shapeVar.value<QGeoPath>();
187 coords = path.path();
188 return true;
189 }
190 if (shapeVar.canConvert<QGeoShape>()) {
191 const QGeoShape shape = shapeVar.value<QGeoShape>();
192 if (shape.type() == QGeoShape::PathType) {
193 const QGeoPath path(shape);
194 coords = path.path();
195 if (!coords.isEmpty()) {
196 return true;
197 }
198 }
199 }
200 }
201
202 errorString = QString(_errorPrefix).arg(
203 QT_TRANSLATE_NOOP("GeoJson", "No polyline found in GeoJson file."));
204 return false;
205}
206
207bool GeoJsonHelper::loadGeoJsonCoordinate(const QJsonValue &jsonValue, bool altitudeRequired, QGeoCoordinate &coordinate, QString &errorString)
208{
209 return JsonHelper::loadGeoCoordinate(jsonValue, altitudeRequired, coordinate, errorString, true /* geoJsonFormat */);
210}
211
212void GeoJsonHelper::saveGeoJsonCoordinate(const QGeoCoordinate &coordinate, bool writeAltitude, QJsonValue &jsonValue)
213{
214 JsonHelper::saveGeoCoordinate(coordinate, writeAltitude, jsonValue, true /* geoJsonFormat */);
215}
QString errorString
#define QGC_LOGGING_CATEGORY(name, categoryStr)
QVariantList _extractShapeValues(const QVariantList &values)
constexpr const char * _errorPrefix
ShapeFileHelper::ShapeType determineShapeType(const QString &filePath, QString &errorString)
bool loadPolylineFromFile(const QString &filePath, QList< QGeoCoordinate > &coords, QString &errorString)
bool loadGeoJsonCoordinate(const QJsonValue &jsonValue, bool altitudeRequired, QGeoCoordinate &coordinate, QString &errorString)
returned error string if load failure
void saveGeoJsonCoordinate(const QGeoCoordinate &coordinate, bool writeAltitude, QJsonValue &jsonValue)
json value to save to
QJsonDocument _loadFile(const QString &filePath, QString &errorString)
bool loadPolygonFromFile(const QString &filePath, QList< QGeoCoordinate > &vertices, QString &errorString)
constexpr int _maxRecursionDepth
void _extractShapeValuesRecursive(const QVariant &value, QVariantList &shapes, int depth=0)
void saveGeoCoordinate(const QGeoCoordinate &coordinate, bool writeAltitude, QJsonValue &jsonValue, bool geoJsonFormat=false)
bool loadGeoCoordinate(const QJsonValue &jsonValue, bool altitudeRequired, QGeoCoordinate &coordinate, QString &errorString, bool geoJsonFormat=false)
if true, use [lon, lat], [lat, lon] otherwise
bool isJsonFile(const QByteArray &bytes, QJsonDocument &jsonDoc, QString &errorString)
Determines whether an in-memory byte buffer contains parseable JSON content.