1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-24 11:45:53 +01:00

Added support for arbitrary CRSs in GeoJSON files

This commit is contained in:
Martin Tůma 2024-03-09 17:51:43 +01:00
parent b7b03c1d5a
commit 8780a40b9f
2 changed files with 318 additions and 140 deletions

View File

@ -1,5 +1,6 @@
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonArray> #include <QJsonArray>
#include "map/crs.h"
#include "geojsonparser.h" #include "geojsonparser.h"
#define MARKER_SIZE_MEDIUM 12 #define MARKER_SIZE_MEDIUM 12
@ -125,6 +126,35 @@ static bool isJSONObject(QFile *file)
return false; return false;
} }
static Coordinates coordinates(const QJsonArray &data, const Projection &proj)
{
if (data.count() >= 2 && data.at(0).isDouble() && data.at(1).isDouble())
return proj.xy2ll(PointD(data.at(0).toDouble(), data.at(1).toDouble()));
else
return Coordinates();
}
bool GeoJSONParser::crs(const QJsonObject &object, Projection &proj)
{
if (!object.contains("crs"))
return true;
QJsonObject crsObj(object["crs"].toObject());
if (crsObj["type"].toString() != "name" || !crsObj.contains("properties")) {
_errorString = "Invalid crs object";
return false;
}
QString str(crsObj["properties"].toObject()["name"].toString());
proj = CRS::projection(str);
if (proj.isValid())
return true;
else {
_errorString = QString("%1: unknown CRS").arg(str);
return false;
}
}
GeoJSONParser::Type GeoJSONParser::type(const QJsonObject &json) GeoJSONParser::Type GeoJSONParser::type(const QJsonObject &json)
{ {
QString str(json["type"].toString()); QString str(json["type"].toString());
@ -151,206 +181,337 @@ GeoJSONParser::Type GeoJSONParser::type(const QJsonObject &json)
return Unknown; return Unknown;
} }
bool GeoJSONParser::point(const QJsonArray &coordinates, Waypoint &waypoint, bool GeoJSONParser::point(const QJsonObject &object, const Projection &parent,
const QJsonValue &properties) const QJsonValue &properties, Waypoint &waypoint)
{ {
if (coordinates.count() < 2 || !coordinates.at(0).isDouble() if (!object.contains("coordinates")) {
|| !coordinates.at(1).isDouble()) { _errorString = "Missing Point coordinates array";
_errorString = "Invalid Point Coordinates"; return false;
}
if (object["coordinates"].isNull())
return true;
QJsonArray coordinates(object["coordinates"].toArray());
if (coordinates.isEmpty())
return true;
Projection proj;
if (!crs(object, proj))
return false;
Coordinates c(::coordinates(coordinates, proj.isNull() ? parent : proj));
if (!c.isValid()) {
_errorString = "Invalid Point coordinates";
return false; return false;
} }
setWaypointProperties(waypoint, properties); waypoint.setCoordinates(c);
waypoint.setCoordinates(Coordinates(coordinates.at(0).toDouble(),
coordinates.at(1).toDouble()));
if (coordinates.count() == 3 && coordinates.at(2).isDouble()) if (coordinates.count() == 3 && coordinates.at(2).isDouble())
waypoint.setElevation(coordinates.at(2).toDouble()); waypoint.setElevation(coordinates.at(2).toDouble());
setWaypointProperties(waypoint, properties);
return true; return true;
} }
bool GeoJSONParser::multiPoint(const QJsonArray &coordinates, bool GeoJSONParser::multiPoint(const QJsonObject &object,
QVector<Waypoint> &waypoints, const QJsonValue &properties) const Projection &parent, const QJsonValue &properties,
QVector<Waypoint> &waypoints)
{ {
if (!object.contains("coordinates")) {
_errorString = "Missing MultiPoint coordinates array";
return false;
}
if (object["coordinates"].isNull())
return true;
QJsonArray coordinates(object["coordinates"].toArray());
if (coordinates.isEmpty())
return true;
Projection proj;
if (!crs(object, proj))
return false;
for (int i = 0; i < coordinates.size(); i++) { for (int i = 0; i < coordinates.size(); i++) {
if (!coordinates.at(i).isArray()) { if (!coordinates.at(i).isArray()) {
_errorString = "Invalid MultiPoint coordinates"; _errorString = "Invalid MultiPoint data";
return false; return false;
} else { } else {
waypoints.resize(waypoints.size() + 1); waypoints.resize(waypoints.size() + 1);
if (!point(coordinates.at(i).toArray(), waypoints.last(), properties))
QJsonArray data(coordinates.at(i).toArray());
Coordinates c(::coordinates(data, proj.isNull() ? parent : proj));
if (!c.isValid()) {
_errorString = "Invalid MultiPoint coordinates";
return false; return false;
} }
waypoints.last().setCoordinates(c);
if (data.count() == 3 && data.at(2).isDouble())
waypoints.last().setElevation(data.at(2).toDouble());
setWaypointProperties(waypoints.last(), properties);
}
} }
return true; return true;
} }
bool GeoJSONParser::lineString(const QJsonArray &coordinates, bool GeoJSONParser::lineString(const QJsonObject &object,
SegmentData &segment) const Projection &parent, const QJsonValue &properties, TrackData &track)
{ {
if (!object.contains("coordinates")) {
_errorString = "Missing LineString coordinates array";
return false;
}
if (object["coordinates"].isNull())
return true;
QJsonArray coordinates(object["coordinates"].toArray());
if (coordinates.isEmpty())
return true;
Projection proj;
if (!crs(object, proj))
return false;
track.append(SegmentData());
for (int i = 0; i < coordinates.size(); i++) { for (int i = 0; i < coordinates.size(); i++) {
QJsonArray point(coordinates.at(i).toArray()); if (!coordinates.at(i).isArray()) {
if (point.count() < 2 || !point.at(0).isDouble() _errorString = "Invalid LineString data";
|| !point.at(1).isDouble()) { return false;
}
QJsonArray data(coordinates.at(i).toArray());
Coordinates c(::coordinates(data, proj.isNull() ? parent : proj));
if (!c.isValid()) {
_errorString = "Invalid LineString coordinates"; _errorString = "Invalid LineString coordinates";
return false; return false;
} }
Trackpoint t(Coordinates(point.at(0).toDouble(), Trackpoint t(c);
point.at(1).toDouble())); if (data.count() == 3 && data.at(2).isDouble())
if (point.count() == 3 && point.at(2).isDouble()) t.setElevation(data.at(2).toDouble());
t.setElevation(point.at(2).toDouble()); track.last().append(t);
segment.append(t);
} }
setTrackProperties(track, properties);
return true; return true;
} }
bool GeoJSONParser::lineString(const QJsonArray &coordinates, TrackData &track, bool GeoJSONParser::multiLineString(const QJsonObject &object,
const QJsonValue &properties) const Projection &parent, const QJsonValue &properties, TrackData &track)
{ {
setTrackProperties(track, properties); if (!object.contains("coordinates")) {
_errorString = "Missing MultiLineString coordinates array";
track.append(SegmentData()); return false;
lineString(coordinates, track.last());
return true;
} }
if (object["coordinates"].isNull())
bool GeoJSONParser::multiLineString(const QJsonArray &coordinates, return true;
TrackData &track, const QJsonValue &properties) QJsonArray coordinates(object["coordinates"].toArray());
{ if (coordinates.isEmpty())
setTrackProperties(track, properties); return true;
Projection proj;
if (!crs(object, proj))
return false;
for (int i = 0; i < coordinates.size(); i++) { for (int i = 0; i < coordinates.size(); i++) {
if (!coordinates.at(i).isArray()) { if (!coordinates.at(i).isArray()) {
_errorString = "Invalid MultiLineString coordinates"; _errorString = "Invalid MultiLineString data";
return false; return false;
} else { } else {
track.append(SegmentData()); track.append(SegmentData());
if (!lineString(coordinates.at(i).toArray(), track.last()))
QJsonArray ls(coordinates.at(i).toArray());
for (int j = 0; j < ls.size(); j++) {
if (!ls.at(j).isArray()) {
_errorString = "Invalid MultiLineString LineString data";
return false; return false;
} }
QJsonArray data(ls.at(j).toArray());
Coordinates c(::coordinates(data, proj.isNull() ? parent : proj));
if (!c.isValid()) {
_errorString = "Invalid MultiLineString coordinates";
return false;
} }
Trackpoint t(c);
if (data.count() == 3 && data.at(2).isDouble())
t.setElevation(data.at(2).toDouble());
track.last().append(t);
}
}
}
setTrackProperties(track, properties);
return true; return true;
} }
bool GeoJSONParser::polygon(const QJsonArray &coordinates, ::Polygon &pg) bool GeoJSONParser::polygon(const QJsonObject &object, const Projection &parent,
const QJsonValue &properties, Area &area)
{ {
if (!object.contains("coordinates")) {
_errorString = "Missing Polygon coordinates array";
return false;
}
if (object["coordinates"].isNull())
return true;
QJsonArray coordinates(object["coordinates"].toArray());
if (coordinates.isEmpty())
return true;
Projection proj;
if (!crs(object, proj))
return false;
::Polygon pg;
for (int i = 0; i < coordinates.size(); i++) { for (int i = 0; i < coordinates.size(); i++) {
if (!coordinates.at(i).isArray()) { if (!coordinates.at(i).isArray()) {
_errorString = "Invalid Polygon linear ring"; _errorString = "Invalid Polygon linear ring";
return false; return false;
} }
const QJsonArray lr(coordinates.at(i).toArray()); QJsonArray lr(coordinates.at(i).toArray());
QVector<Coordinates> data; QVector<Coordinates> data;
for (int j = 0; j < lr.size(); j++) { for (int j = 0; j < lr.size(); j++) {
if (!lr.at(j).isArray()) {
_errorString = "Invalid Polygon linear ring data";
return false;
}
QJsonArray point(lr.at(j).toArray()); QJsonArray point(lr.at(j).toArray());
if (point.count() < 2 || !point.at(0).isDouble() Coordinates c(::coordinates(point, proj.isNull() ? parent : proj));
|| !point.at(1).isDouble()) { if (!c.isValid()) {
_errorString = "Invalid Polygon linear ring coordinates"; _errorString = "Invalid Polygon linear ring coordinates";
return false; return false;
} }
data.append(Coordinates(point.at(0).toDouble(), data.append(c);
point.at(1).toDouble()));
} }
pg.append(data); pg.append(data);
} }
area.append(pg);
setAreaProperties(area, properties);
return true; return true;
} }
bool GeoJSONParser::polygon(const QJsonArray &coordinates, Area &area, bool GeoJSONParser::multiPolygon(const QJsonObject &object,
const QJsonValue &properties) const Projection &parent, const QJsonValue &properties, Area &area)
{ {
setAreaProperties(area, properties); if (!object.contains("coordinates")) {
_errorString = "Missing MultiPolygon coordinates array";
::Polygon p;
if (!polygon(coordinates, p))
return false; return false;
area.append(p);
return true;
} }
if (object["coordinates"].isNull())
bool GeoJSONParser::multiPolygon(const QJsonArray &coordinates, return true;
Area &area, const QJsonValue &properties) QJsonArray coordinates(object["coordinates"].toArray());
{ if (coordinates.isEmpty())
setAreaProperties(area, properties); return true;
Projection proj;
if (!crs(object, proj))
return false;
for (int i = 0; i < coordinates.size(); i++) { for (int i = 0; i < coordinates.size(); i++) {
if (!coordinates.at(i).isArray()) { if (!coordinates.at(i).isArray()) {
_errorString = "Invalid MultiPolygon coordinates"; _errorString = "Invalid MultiPolygon data";
return false; return false;
} else { } else {
::Polygon p; ::Polygon pg;
if (!polygon(coordinates.at(i).toArray(), p))
QJsonArray polygon(coordinates.at(i).toArray());
for (int j = 0; j < polygon.size(); j++) {
if (!polygon.at(j).isArray()) {
_errorString = "Invalid MultiPolygon linear ring";
return false; return false;
area.append(p); }
QJsonArray lr(polygon.at(j).toArray());
QVector<Coordinates> data;
for (int k = 0; k < lr.size(); k++) {
if (!lr.at(k).isArray()) {
_errorString = "Invalid MultiPolygon linear ring data";
return false;
}
QJsonArray point(lr.at(k).toArray());
Coordinates c(::coordinates(point, proj.isNull() ? parent : proj));
if (!c.isValid()) {
_errorString = "Invalid MultiPolygon linear ring coordinates";
return false;
}
data.append(c);
}
pg.append(data);
}
area.append(pg);
} }
} }
setAreaProperties(area, properties);
return true; return true;
} }
bool GeoJSONParser::geometryCollection(const QJsonObject &json, bool GeoJSONParser::geometryCollection(const QJsonObject &object,
QList<TrackData> &tracks, QList<Area> &areas, const Projection &parent, const QJsonValue &properties,
QVector<Waypoint> &waypoints, const QJsonValue &properties) QList<TrackData> &tracks, QList<Area> &areas, QVector<Waypoint> &waypoints)
{ {
if (!json.contains("geometries") || !json["geometries"].isArray()) { if (!object.contains("geometries") || !object["geometries"].isArray()) {
_errorString = "Invalid/missing GeometryCollection geometries array"; _errorString = "Invalid/missing GeometryCollection geometries array";
return false; return false;
} }
QJsonArray geometries(json["geometries"].toArray()); QJsonArray geometries(object["geometries"].toArray());
Projection proj;
if (!crs(object, proj))
return false;
for (int i = 0; i < geometries.size(); i++) { for (int i = 0; i < geometries.size(); i++) {
QJsonObject geometry(geometries.at(i).toObject()); QJsonObject geometry(geometries.at(i).toObject());
switch (type(geometry)) { switch (type(geometry)) {
case Point: case Point:
waypoints.resize(waypoints.size() + 1); waypoints.resize(waypoints.size() + 1);
if (!point(geometry["coordinates"].toArray(), waypoints.last(), if (!point(geometry, proj.isNull() ? parent : proj, properties,
properties)) waypoints.last()))
return false; return false;
break; break;
case MultiPoint: case MultiPoint:
if (!multiPoint(geometry["coordinates"].toArray(), waypoints, if (!multiPoint(geometry, proj.isNull() ? parent : proj,
properties)) properties, waypoints))
return false; return false;
break; break;
case LineString: case LineString:
tracks.append(TrackData()); tracks.append(TrackData());
if (!lineString(geometry["coordinates"].toArray(), if (!lineString(geometry, proj.isNull() ? parent : proj,
tracks.last(), properties)) properties, tracks.last()))
return false; return false;
break; break;
case MultiLineString: case MultiLineString:
tracks.append(TrackData()); tracks.append(TrackData());
if (!multiLineString(geometry["coordinates"].toArray(), if (!multiLineString(geometry, proj.isNull() ? parent : proj,
tracks.last(), properties)) properties, tracks.last()))
return false; return false;
break; break;
case Polygon: case Polygon:
areas.append(Area()); areas.append(Area());
if (!polygon(geometry["coordinates"].toArray(), areas.last(), if (!polygon(geometry, proj.isNull() ? parent : proj, properties,
properties)) areas.last()))
return false; return false;
break; break;
case MultiPolygon: case MultiPolygon:
areas.append(Area()); areas.append(Area());
if (!multiPolygon(geometry["coordinates"].toArray(), if (!multiPolygon(geometry, proj.isNull() ? parent : proj,
areas.last(), properties)) properties, areas.last()))
return false; return false;
break; break;
case GeometryCollection: case GeometryCollection:
if (!geometryCollection(geometry, tracks, areas, waypoints, if (!geometryCollection(geometry, proj.isNull() ? parent : proj,
properties)) properties, tracks, areas, waypoints))
return false; return false;
break; break;
default: default:
@ -363,38 +524,47 @@ bool GeoJSONParser::geometryCollection(const QJsonObject &json,
return true; return true;
} }
bool GeoJSONParser::feature(const QJsonObject &json, QList<TrackData> &tracks, bool GeoJSONParser::feature(const QJsonObject &object, const Projection &parent,
QList<Area> &areas, QVector<Waypoint> &waypoints) QList<TrackData> &tracks, QList<Area> &areas, QVector<Waypoint> &waypoints)
{ {
QJsonValue properties(json["properties"]); if (!object.contains("geometry") || !object["geometry"].isObject()) {
QJsonObject geometry(json["geometry"].toObject()); _errorString = "Invalid/missing Feature geometry object";
return false;
}
QJsonValue properties(object["properties"]);
QJsonObject geometry(object["geometry"].toObject());
Projection proj;
if (!crs(object, proj))
return false;
switch (type(geometry)) { switch (type(geometry)) {
case Point: case Point:
waypoints.resize(waypoints.size() + 1); waypoints.resize(waypoints.size() + 1);
return point(geometry["coordinates"].toArray(), waypoints.last(), return point(geometry, proj.isNull() ? parent : proj, properties,
properties); waypoints.last());
case MultiPoint: case MultiPoint:
return multiPoint(geometry["coordinates"].toArray(), waypoints, return multiPoint(geometry, proj.isNull() ? parent : proj,
properties); properties, waypoints);
case LineString: case LineString:
tracks.append(TrackData()); tracks.append(TrackData());
return lineString(geometry["coordinates"].toArray(), tracks.last(), return lineString(geometry, proj.isNull() ? parent : proj,
properties); properties, tracks.last());
case MultiLineString: case MultiLineString:
tracks.append(TrackData()); tracks.append(TrackData());
return multiLineString(geometry["coordinates"].toArray(), return multiLineString(geometry, proj.isNull() ? parent : proj,
tracks.last(), properties); properties, tracks.last());
case GeometryCollection: case GeometryCollection:
return geometryCollection(geometry, tracks, areas, waypoints); return geometryCollection(geometry, proj.isNull() ? parent : proj,
properties, tracks, areas, waypoints);
case Polygon: case Polygon:
areas.append(Area()); areas.append(Area());
return polygon(geometry["coordinates"].toArray(), areas.last(), return polygon(geometry, proj.isNull() ? parent : proj, properties,
properties); areas.last());
case MultiPolygon: case MultiPolygon:
areas.append(Area()); areas.append(Area());
return multiPolygon(geometry["coordinates"].toArray(), areas.last(), return multiPolygon(geometry, proj.isNull() ? parent : proj,
properties); properties, areas.last());
default: default:
_errorString = geometry["type"].toString() _errorString = geometry["type"].toString()
+ ": invalid/missing Feature geometry"; + ": invalid/missing Feature geometry";
@ -402,18 +572,23 @@ bool GeoJSONParser::feature(const QJsonObject &json, QList<TrackData> &tracks,
} }
} }
bool GeoJSONParser::featureCollection(const QJsonObject &json, bool GeoJSONParser::featureCollection(const QJsonObject &object,
QList<TrackData> &tracks, QList<Area> &areas, const Projection &parent, QList<TrackData> &tracks, QList<Area> &areas,
QVector<Waypoint> &waypoints) QVector<Waypoint> &waypoints)
{ {
if (!json.contains("features") || !json["features"].isArray()) { if (!object.contains("features") || !object["features"].isArray()) {
_errorString = "Invalid/missing FeatureCollection features array"; _errorString = "Invalid/missing FeatureCollection features array";
return false; return false;
} }
QJsonArray features(json["features"].toArray()); QJsonArray features(object["features"].toArray());
Projection proj;
if (!crs(object, proj))
return false;
for (int i = 0; i < features.size(); i++) for (int i = 0; i < features.size(); i++)
if (!feature(features.at(i).toObject(), tracks, areas, waypoints)) if (!feature(features.at(i).toObject(), proj.isNull() ? parent : proj,
tracks, areas, waypoints))
return false; return false;
return true; return true;
@ -426,7 +601,7 @@ bool GeoJSONParser::parse(QFile *file, QList<TrackData> &tracks,
Q_UNUSED(routes); Q_UNUSED(routes);
if (!isJSONObject(file)) { if (!isJSONObject(file)) {
_errorString = "Not a GeoJSON file"; _errorString = "Not a JSON file";
return false; return false;
} else } else
file->reset(); file->reset();
@ -440,37 +615,39 @@ bool GeoJSONParser::parse(QFile *file, QList<TrackData> &tracks,
return false; return false;
} }
QJsonObject json(doc.object()); QJsonObject object(doc.object());
Projection proj(GCS::WGS84());
switch (type(json)) { switch (type(object)) {
case Point: case Point:
waypoints.resize(waypoints.size() + 1); waypoints.resize(waypoints.size() + 1);
return point(json["coordinates"].toArray(), waypoints.last()); return point(object, proj, QJsonValue(), waypoints.last());
case MultiPoint: case MultiPoint:
return multiPoint(json["coordinates"].toArray(), waypoints); return multiPoint(object, proj, QJsonValue(), waypoints);
case LineString: case LineString:
tracks.append(TrackData()); tracks.append(TrackData());
return lineString(json["coordinates"].toArray(), tracks.last()); return lineString(object, proj, QJsonValue(), tracks.last());
case MultiLineString: case MultiLineString:
tracks.append(TrackData()); tracks.append(TrackData());
return multiLineString(json["coordinates"].toArray(), tracks.last()); return multiLineString(object, proj, QJsonValue(), tracks.last());
case GeometryCollection: case GeometryCollection:
return geometryCollection(json, tracks, areas, waypoints); return geometryCollection(object, proj, QJsonValue(), tracks, areas,
waypoints);
case Feature: case Feature:
return feature(json, tracks, areas, waypoints); return feature(object, proj, tracks, areas, waypoints);
case FeatureCollection: case FeatureCollection:
return featureCollection(json, tracks, areas, waypoints); return featureCollection(object, proj, tracks, areas, waypoints);
case Polygon: case Polygon:
areas.append(Area()); areas.append(Area());
return polygon(json["coordinates"].toArray(), areas.last()); return polygon(object, proj, QJsonValue(), areas.last());
case MultiPolygon: case MultiPolygon:
areas.append(Area()); areas.append(Area());
return multiPolygon(json["coordinates"].toArray(), areas.last()); return multiPolygon(object, proj, QJsonValue(), areas.last());
case Unknown: case Unknown:
if (json["type"].toString().isNull()) if (object["type"].toString().isNull())
_errorString = "Not a GeoJSON file"; _errorString = "Not a GeoJSON file";
else else
_errorString = json["type"].toString() _errorString = object["type"].toString()
+ ": unknown GeoJSON object"; + ": unknown GeoJSON object";
return false; return false;
} }

View File

@ -6,6 +6,7 @@
class QJsonObject; class QJsonObject;
class QJsonArray; class QJsonArray;
class Projection;
class GeoJSONParser : public Parser class GeoJSONParser : public Parser
{ {
@ -30,27 +31,27 @@ private:
}; };
Type type(const QJsonObject &json); Type type(const QJsonObject &json);
bool point(const QJsonArray &coordinates, Waypoint &waypoint, bool crs(const QJsonObject &object, Projection &proj);
const QJsonValue &properties = QJsonValue()); bool point(const QJsonObject &object, const Projection &parent,
bool multiPoint(const QJsonArray &coordinates, const QJsonValue &properties, Waypoint &waypoint);
QVector<Waypoint> &waypoints, const QJsonValue &properties = QJsonValue()); bool multiPoint(const QJsonObject &object, const Projection &parent,
bool lineString(const QJsonArray &coordinates, SegmentData &segment); const QJsonValue &properties, QVector<Waypoint> &waypoints);
bool lineString(const QJsonArray &coordinates, TrackData &track, bool lineString(const QJsonObject &coordinates, const Projection &parent,
const QJsonValue &properties = QJsonValue()); const QJsonValue &properties, TrackData &track);
bool multiLineString(const QJsonArray &coordinates, bool multiLineString(const QJsonObject &object, const Projection &proj,
TrackData &track, const QJsonValue &properties = QJsonValue()); const QJsonValue &properties, TrackData &track);
bool polygon(const QJsonArray &coordinates, ::Polygon &pg); bool polygon(const QJsonObject &object, const Projection &parent,
bool polygon(const QJsonArray &coordinates, Area &area, const QJsonValue &properties, Area &area);
const QJsonValue &properties = QJsonValue()); bool multiPolygon(const QJsonObject &object, const Projection &proj,
bool multiPolygon(const QJsonArray &coordinates, Area &area, const QJsonValue &properties, Area &area);
const QJsonValue &properties = QJsonValue()); bool geometryCollection(const QJsonObject &json, const Projection &parent,
bool geometryCollection(const QJsonObject &json, QList<TrackData> &tracks, const QJsonValue &properties, QList<TrackData> &tracks,
QList<Area> &areas, QVector<Waypoint> &waypoints,
const QJsonValue &properties = QJsonValue());
bool feature(const QJsonObject &json, QList<TrackData> &tracks,
QList<Area> &areas, QVector<Waypoint> &waypoints);
bool featureCollection(const QJsonObject &json, QList<TrackData> &tracks,
QList<Area> &areas, QVector<Waypoint> &waypoints); QList<Area> &areas, QVector<Waypoint> &waypoints);
bool feature(const QJsonObject &json, const Projection &parent,
QList<TrackData> &tracks, QList<Area> &areas,
QVector<Waypoint> &waypoints);
bool featureCollection(const QJsonObject &object, const Projection &parent,
QList<TrackData> &tracks, QList<Area> &areas, QVector<Waypoint> &waypoints);
QString _errorString; QString _errorString;
}; };