[PATCH 04/10] Completely rewrite the GeoJSON runner plugin for RFC7946 compliance
John Zaitseff
J.Zaitseff at zap.org.au
Tue Aug 27 11:55:06 BST 2019
In particular, the plugin now handles GeoJSON files that do not contain a
FeatureCollection top-level object. It also correctly handles
GeometryCollection objects that can nest other GeoJSON geometry objects
arbitrarily deep, by using a recursive parser. Furthermore, altitude
values are now imported along with longitude and latitude. Properties on
Feature objects are now handled in such a way that extending these to
cover styles is made easier.
---
src/plugins/runner/json/JsonParser.cpp | 472 ++++++++++++++-----------
src/plugins/runner/json/JsonParser.h | 34 +-
2 files changed, 287 insertions(+), 219 deletions(-)
diff --git a/src/plugins/runner/json/JsonParser.cpp b/src/plugins/runner/json/JsonParser.cpp
index 3fcc5bed5..d9c1008ec 100644
--- a/src/plugins/runner/json/JsonParser.cpp
+++ b/src/plugins/runner/json/JsonParser.cpp
@@ -1,11 +1,16 @@
/*
- This file is part of the Marble Virtual Globe.
+ This file is part of the Marble Virtual Globe.
- This program is free software licensed under the GNU LGPL. You can
- find a copy of this license in LICENSE.txt in the top directory of
- the source code.
+ The JsonParser class reads in a GeoJSON document that conforms to
+ RFC7946 (including relevant errata). Attributes are stored as OSM
+ tags.
- Copyright 2013 Ander Pijoan <ander.pijoan at deusto.es>
+ This program is free software licensed under the GNU LGPL. You can
+ find a copy of this license in LICENSE.txt in the top directory of
+ the source code.
+
+ Copyright 2013 Ander Pijoan <ander.pijoan at deusto.es>
+ Copyright 2019 John Zaitseff <J.Zaitseff at zap.org.au>
*/
#include "JsonParser.h"
@@ -14,6 +19,7 @@
#include "GeoDataPolygon.h"
#include "GeoDataLinearRing.h"
#include "GeoDataPoint.h"
+#include "GeoDataMultiGeometry.h"
#include "MarbleDebug.h"
#include "StyleBuilder.h"
#include "osm/OsmPlacemarkData.h"
@@ -44,274 +50,316 @@ GeoDataDocument *JsonParser::releaseDocument()
bool JsonParser::read( QIODevice* device )
{
- // Assert previous document got released.
+ // Release the previous document if required
delete m_document;
m_document = new GeoDataDocument;
Q_ASSERT( m_document );
- // Read file data
+ // Read JSON file data
QJsonParseError error;
const QJsonDocument jsonDoc = QJsonDocument::fromJson(device->readAll(), &error);
if (jsonDoc.isNull()) {
- qDebug() << "Error parsing GeoJSON : " << error.errorString();
+ qDebug() << "Error parsing GeoJSON:" << error.errorString();
+ return false;
+ } else if (! jsonDoc.isObject()) {
+ qDebug() << "Invalid file, does not contain a GeoJSON object";
return false;
}
- // Start parsing
- const QJsonValue featuresValue = jsonDoc.object().value(QStringLiteral("features"));
+ // Valid GeoJSON documents may not always contain a FeatureCollection object with subsidiary
+ // Feature objects, or even a single Feature object: they might contain just a single geometry
+ // object. Handle such cases by creating a wrapper Feature object if required.
+
+ const QString jsonObjectType = jsonDoc.object().value(QStringLiteral("type")).toString();
+
+ if (jsonObjectType == QStringLiteral("FeatureCollection")
+ || jsonObjectType == QStringLiteral("Feature")) {
+
+ // A normal GeoJSON document: parse it recursively
+ return parseGeoJsonTopLevel(jsonDoc.object());
+
+ } else {
+ // Create a wrapper Feature object and parse that
+
+ QJsonObject jsonWrapper;
+ QJsonObject jsonWrapperProperties;
+
+ jsonWrapper["type"] = QStringLiteral("Feature");
+ jsonWrapper["geometry"] = jsonDoc.object();
+ jsonWrapper["properties"] = jsonWrapperProperties;
+
+ return parseGeoJsonTopLevel(jsonWrapper);
+ }
+}
+
+bool JsonParser::parseGeoJsonTopLevel( const QJsonObject& jsonObject )
+{
+ // Every GeoJSON object must have a case-sensitive "type" member (see RFC7946 section 3)
+ const QString jsonObjectType = jsonObject.value(QStringLiteral("type")).toString();
- // In GeoJSON format, geometries are stored in features, so we iterate on features
- if (featuresValue.isArray()) {
- const QJsonArray featureArray = featuresValue.toArray();
+ if (jsonObjectType == QStringLiteral("FeatureCollection")) {
+ // Handle the FeatureCollection object, which may contain multiple Feature objects in it
- // Parse each feature
+ const QJsonArray featureArray = jsonObject.value(QStringLiteral("features")).toArray();
for (int featureIndex = 0; featureIndex < featureArray.size(); ++featureIndex) {
- const QJsonObject featureObject = featureArray[featureIndex].toObject();
+ if (! parseGeoJsonTopLevel( featureArray[featureIndex].toObject() )) {
+ return false;
+ }
+ }
+ return true;
+
+ } else if (jsonObjectType == QStringLiteral("Feature")) {
+ // Handle the Feature object, which contains a single geometry object and possibly
+ // associated properties. Note that only Feature objects can have recognised properties.
- // Check if the feature contains a geometry
- const QJsonValue geometryValue = featureObject.value(QStringLiteral("geometry"));
- if (geometryValue.isObject()) {
- const QJsonObject geometryObject = geometryValue.toObject();
+ QVector<GeoDataGeometry*> geometryList; // Populated by parseGeoJsonSubLevel()
- // Variables for creating the geometry
- QList<GeoDataGeometry*> geometryList;
- QList<GeoDataPlacemark*> placemarkList;
+ if (! parseGeoJsonSubLevel( jsonObject.value(QStringLiteral("geometry")).toObject(),
+ geometryList )) {
+ return false;
+ }
- // Create the different geometry types
- const QString geometryType = geometryObject.value(QStringLiteral("type")).toString().toUpper();
+ // Create the placemark for this feature object with appropriate geometry
- if (geometryType == QLatin1String("POLYGON")) {
- // Check first that there are coordinates
- const QJsonValue coordinatesValue = geometryObject.value(QStringLiteral("coordinates"));
- if (coordinatesValue.isArray()) {
- const QJsonArray coordinateArray = coordinatesValue.toArray();
+ GeoDataPlacemark* placemark = new GeoDataPlacemark();
- GeoDataPolygon * geom = new GeoDataPolygon( RespectLatitudeCircle | Tessellate );
+ if (geometryList.length() < 1) {
+ // No geometries available to add to the placemark
+ ;
- // Coordinates first array will be the outer boundary, if there are more
- // positions those will be inner holes
- for (int ringIndex = 0 ; ringIndex < coordinateArray.size(); ++ringIndex) {
- const QJsonArray ringArray = coordinateArray[ringIndex].toArray();
+ } else if (geometryList.length() == 1) {
+ // Single geometry
+ placemark->setGeometry(geometryList[0]);
- GeoDataLinearRing linearRing;
+ } else {
+ // Multiple geometries require a GeoDataMultiGeometry class
- for (int coordinatePairIndex = 0; coordinatePairIndex < ringArray.size(); ++coordinatePairIndex) {
- const QJsonArray coordinatePairArray = ringArray[coordinatePairIndex].toArray();
+ GeoDataMultiGeometry* geom = new GeoDataMultiGeometry();
+ for (int i = 0; i < geometryList.length(); ++i) {
+ geom->append(geometryList[i]);
+ }
+ placemark->setGeometry(geom);
+ }
- const qreal longitude = coordinatePairArray.at(0).toDouble();
- const qreal latitude = coordinatePairArray.at(1).toDouble();
+ // Parse any associated properties
- linearRing.append( GeoDataCoordinates( longitude , latitude , 0 , GeoDataCoordinates::Degree ) );
- }
+ const QJsonObject propertiesObject = jsonObject.value(QStringLiteral("properties")).toObject();
+ QJsonObject::ConstIterator iter = propertiesObject.begin();
+ const QJsonObject::ConstIterator end = propertiesObject.end();
- // Outer ring
- if (ringIndex == 0) {
- geom->setOuterBoundary( linearRing );
- }
- // Inner holes
- else {
- geom->appendInnerBoundary( linearRing );
- }
- }
- geometryList.append( geom );
- }
+ OsmPlacemarkData osmData;
- } else if (geometryType == QLatin1String("MULTIPOLYGON")) {
- // Check first that there are coordinates
- const QJsonValue coordinatesValue = geometryObject.value(QStringLiteral("coordinates"));
- if (coordinatesValue.isArray()) {
- const QJsonArray coordinateArray = coordinatesValue.toArray();
+ for ( ; iter != end; ++iter) {
+ // Pass the value through QVariant to also get booleans and numbers
+ const QString propertyValue = iter.value().toVariant().toString();
+ const QString propertyKey = iter.key();
- for (int polygonIndex = 0; polygonIndex < coordinateArray.size(); ++polygonIndex) {
- const QJsonArray polygonArray = coordinateArray[polygonIndex].toArray();
+ if (iter.value().isObject() || iter.value().isArray()) {
+ qDebug() << "Skipping unsupported JSON property containing an object or array:" << propertyKey;
+ continue;
+ }
- GeoDataPolygon * geom = new GeoDataPolygon( RespectLatitudeCircle | Tessellate );
+ osmData.addTag(propertyKey, propertyValue);
- // Coordinates first array will be the outer boundary, if there are more
- // positions those will be inner holes
- for (int ringIndex = 0 ; ringIndex < polygonArray.size(); ++ringIndex) {
- const QJsonArray ringArray = polygonArray[ringIndex].toArray();
+ if (propertyKey == QStringLiteral("name")) {
+ placemark->setName(propertyValue);
+ }
+ }
- GeoDataLinearRing linearRing;
+ placemark->setOsmData(osmData);
+ placemark->setVisible(true);
- for (int coordinatePairIndex = 0; coordinatePairIndex < ringArray.size(); ++coordinatePairIndex) {
- const QJsonArray coordinatePairArray = ringArray[coordinatePairIndex].toArray();
+ const GeoDataPlacemark::GeoDataVisualCategory category =
+ StyleBuilder::determineVisualCategory(osmData);
+ if (category != GeoDataPlacemark::None) {
+ placemark->setVisualCategory(category);
+ }
- const qreal longitude = coordinatePairArray.at(0).toDouble();
- const qreal latitude = coordinatePairArray.at(1).toDouble();
+ m_document->append(placemark);
+ return true;
- linearRing.append( GeoDataCoordinates( longitude , latitude , 0 , GeoDataCoordinates::Degree ) );
- }
+ } else {
+ qDebug() << "Missing FeatureCollection or Feature object in GeoJSON file";
+ return false;
+ }
+}
- // Outer ring
- if (ringIndex == 0) {
- geom->setOuterBoundary( linearRing );
- }
- // Inner holes
- else {
- geom->appendInnerBoundary( linearRing );
- }
- }
- geometryList.append( geom );
- }
- }
+bool JsonParser::parseGeoJsonSubLevel( const QJsonObject& jsonObject,
+ QVector<GeoDataGeometry*>& geometryList )
+{
+ // The GeoJSON object type
+ const QString jsonObjectType = jsonObject.value(QStringLiteral("type")).toString();
- } else if (geometryType == QLatin1String("LINESTRING")) {
+ if (jsonObjectType == QStringLiteral("FeatureCollection")
+ || jsonObjectType == QStringLiteral("Feature")) {
- // Check first that there are coordinates
- const QJsonValue coordinatesValue = geometryObject.value(QStringLiteral("coordinates"));
- if (coordinatesValue.isArray()) {
- const QJsonArray coordinateArray = coordinatesValue.toArray();
+ qDebug() << "Cannot have FeatureCollection or Feature objects at this level of the GeoJSON file";
+ return false;
- GeoDataLineString * geom = new GeoDataLineString( RespectLatitudeCircle | Tessellate );
+ } else if (jsonObjectType == QStringLiteral("GeometryCollection")) {
+ // Handle the GeometryCollection object, which may contain multiple geometry objects
- for (int coordinatePairIndex = 0; coordinatePairIndex < coordinateArray.size(); ++coordinatePairIndex) {
- const QJsonArray coordinatePairArray = coordinateArray[coordinatePairIndex].toArray();
+ const QJsonArray geometryArray = jsonObject.value(QStringLiteral("geometries")).toArray();
+ for (int geometryIndex = 0; geometryIndex < geometryArray.size(); ++geometryIndex) {
+ if (! parseGeoJsonSubLevel( geometryArray[geometryIndex].toObject(), geometryList )) {
+ return false;
+ }
+ }
- const qreal longitude = coordinatePairArray.at(0).toDouble();
- const qreal latitude = coordinatePairArray.at(1).toDouble();
+ return true;
+ }
- geom->append( GeoDataCoordinates( longitude , latitude , 0 , GeoDataCoordinates::Degree ) );
- }
- geometryList.append( geom );
- }
+ // Handle remaining GeoJSON objects, which each have a "coordinates" member (an array)
- } else if (geometryType == QLatin1String("MULTILINESTRING")) {
+ const QJsonArray coordinateArray = jsonObject.value(QStringLiteral("coordinates")).toArray();
- // Check first that there are coordinates
- const QJsonValue coordinatesValue = geometryObject.value(QStringLiteral("coordinates"));
- if (coordinatesValue.isArray()) {
- const QJsonArray coordinateArray = coordinatesValue.toArray();
-
- for (int lineStringIndex = 0; lineStringIndex < coordinateArray.size(); ++lineStringIndex) {
- const QJsonArray lineStringArray = coordinateArray[lineStringIndex].toArray();
+ if (jsonObjectType == QStringLiteral("Point")) {
+ // A Point object has a single GeoJSON position: an array of at least two values
- GeoDataLineString * geom = new GeoDataLineString( RespectLatitudeCircle | Tessellate );
+ GeoDataPoint* geom = new GeoDataPoint();
+ const qreal lon = coordinateArray.at(0).toDouble();
+ const qreal lat = coordinateArray.at(1).toDouble();
+ const qreal alt = coordinateArray.at(2).toDouble(); // If missing, uses 0 as the default
- for (int coordinatePairIndex = 0; coordinatePairIndex < lineStringArray.size(); ++coordinatePairIndex) {
- const QJsonArray coordinatePairArray = lineStringArray[coordinatePairIndex].toArray();
+ geom->setCoordinates( GeoDataCoordinates( lon, lat, alt, GeoDataCoordinates::Degree ));
+ geometryList.append(geom);
- const qreal longitude = coordinatePairArray.at(0).toDouble();
- const qreal latitude = coordinatePairArray.at(1).toDouble();
+ return true;
- geom->append( GeoDataCoordinates( longitude , latitude , 0 , GeoDataCoordinates::Degree ) );
- }
- geometryList.append( geom );
- }
- }
-
- } else if (geometryType == QLatin1String("POINT")) {
-
- // Check first that there are coordinates
- const QJsonValue coordinatesValue = geometryObject.value(QStringLiteral("coordinates"));
- if (coordinatesValue.isArray()) {
- const QJsonArray coordinatePairArray = coordinatesValue.toArray();
-
- GeoDataPoint * geom = new GeoDataPoint();
-
- const qreal longitude = coordinatePairArray.at(0).toDouble();
- const qreal latitude = coordinatePairArray.at(1).toDouble();
+ } else if (jsonObjectType == QStringLiteral("MultiPoint")) {
+ // A MultiPoint object has an array of GeoJSON positions (ie, a two-level array)
- geom->setCoordinates( GeoDataCoordinates( longitude , latitude , 0 , GeoDataCoordinates::Degree ) );
+ for (int positionIndex = 0; positionIndex < coordinateArray.size(); ++positionIndex) {
+ const QJsonArray positionArray = coordinateArray[positionIndex].toArray();
- geometryList.append( geom );
- }
- } else if (geometryType == QLatin1String("MULTIPOINT")) {
+ GeoDataPoint* geom = new GeoDataPoint();
+ const qreal lon = positionArray.at(0).toDouble();
+ const qreal lat = positionArray.at(1).toDouble();
+ const qreal alt = positionArray.at(2).toDouble();
- // Check first that there are coordinates
- const QJsonValue coordinatesValue = geometryObject.value(QStringLiteral("coordinates"));
- if (coordinatesValue.isArray()) {
- const QJsonArray coordinateArray = coordinatesValue.toArray();
+ geom->setCoordinates( GeoDataCoordinates( lon, lat, alt, GeoDataCoordinates::Degree ));
+ geometryList.append(geom);
+ }
- for (int pointIndex = 0; pointIndex < coordinateArray.size(); ++pointIndex) {
- const QJsonArray coordinatePairArray = coordinateArray[pointIndex].toArray();
+ return true;
- GeoDataPoint * geom = new GeoDataPoint();
+ } else if (jsonObjectType == QStringLiteral("LineString")) {
+ // A LineString object has an array of GeoJSON positions (ie, a two-level array)
- const qreal longitude = coordinatePairArray.at(0).toDouble();
- const qreal latitude = coordinatePairArray.at(1).toDouble();
+ GeoDataLineString* geom = new GeoDataLineString( RespectLatitudeCircle | Tessellate );
- geom->setCoordinates( GeoDataCoordinates( longitude , latitude , 0 , GeoDataCoordinates::Degree ) );
+ for (int positionIndex = 0; positionIndex < coordinateArray.size(); ++positionIndex) {
+ const QJsonArray positionArray = coordinateArray[positionIndex].toArray();
- geometryList.append( geom );
- }
- }
- }
-
-
- // Parse the features properties
- const QJsonValue propertiesValue = featureObject.value(QStringLiteral("properties"));
- if (!geometryList.isEmpty() && propertiesValue.isObject()) {
- const QJsonObject propertiesObject = propertiesValue.toObject();
-
- // First create a placemark for each geometry, there could be multi geometries
- // that are translated into more than one geometry/placemark
- for ( int numberGeometries = 0 ; numberGeometries < geometryList.length() ; numberGeometries++ ) {
- GeoDataPlacemark * placemark = new GeoDataPlacemark();
- placemarkList.append( placemark );
- }
+ const qreal lon = positionArray.at(0).toDouble();
+ const qreal lat = positionArray.at(1).toDouble();
+ const qreal alt = positionArray.at(2).toDouble();
- OsmPlacemarkData osmData;
+ geom->append( GeoDataCoordinates( lon, lat, alt, GeoDataCoordinates::Degree ));
+ }
+ geometryList.append(geom);
- QJsonObject::ConstIterator it = propertiesObject.begin();
- const QJsonObject::ConstIterator end = propertiesObject.end();
- for ( ; it != end; ++it) {
- if (it.value().isObject() || it.value().isArray()) {
- qDebug() << "Skipping property, values of type arrays and objects not supported:" << it.key();
- continue;
- }
+ return true;
- // pass value through QVariant to also get bool & numbers
- osmData.addTag(it.key(), it.value().toVariant().toString());
- }
+ } else if (jsonObjectType == QStringLiteral("MultiLineString")) {
+ // A MultiLineString object has an array of arrays of GeoJSON positions (three-level)
- // If the property read, is the features name
- const auto tagIter = osmData.findTag(QStringLiteral("name"));
- if (tagIter != osmData.tagsEnd()) {
- const QString& name = tagIter.value();
- for (int pl = 0 ; pl < placemarkList.length(); ++pl) {
- placemarkList.at(pl)->setName(name);
- }
- }
+ for (int lineStringIndex = 0; lineStringIndex < coordinateArray.size(); ++lineStringIndex) {
+ const QJsonArray lineStringArray = coordinateArray[lineStringIndex].toArray();
- const GeoDataPlacemark::GeoDataVisualCategory category = StyleBuilder::determineVisualCategory(osmData);
- if (category != GeoDataPlacemark::None) {
- // Add the visual category to all the placemarks
- for (int pl = 0 ; pl < placemarkList.length(); ++pl) {
- placemarkList.at(pl)->setVisualCategory(category);
- placemarkList.at(pl)->setOsmData(osmData);
- }
- }
- }
+ GeoDataLineString* geom = new GeoDataLineString( RespectLatitudeCircle | Tessellate );
- // Add the geometry to the document
- if ( geometryList.length() == placemarkList.length() ) {
-
- while( placemarkList.length() > 0 ) {
-
- GeoDataPlacemark * placemark = placemarkList.last();
- placemarkList.pop_back();
-
- GeoDataGeometry * geom = geometryList.last();
- geometryList.pop_back();
-
- placemark->setGeometry( geom );
- placemark->setVisible( true );
- m_document->append( placemark );
- }
- }
-
- // If geometries or placemarks missing inside the lists, delete them
- qDeleteAll( geometryList.begin(), geometryList.end() );
- geometryList.clear();
- qDeleteAll( placemarkList.begin(), placemarkList.end() );
- placemarkList.clear();
- }
- }
+ for (int positionIndex = 0; positionIndex < lineStringArray.size(); ++positionIndex) {
+ const QJsonArray positionArray = lineStringArray[positionIndex].toArray();
+
+ const qreal lon = positionArray.at(0).toDouble();
+ const qreal lat = positionArray.at(1).toDouble();
+ const qreal alt = positionArray.at(2).toDouble();
+
+ geom->append( GeoDataCoordinates( lon, lat, alt, GeoDataCoordinates::Degree ));
+ }
+ geometryList.append(geom);
+ }
+
+ return true;
+
+ } else if (jsonObjectType == QStringLiteral("Polygon")) {
+ // A Polygon object has an array of arrays of GeoJSON positions: the first array within the
+ // top-level Polygon coordinates array is the outer boundary, following arrays are inner
+ // holes (if any)
+
+ GeoDataPolygon* geom = new GeoDataPolygon( RespectLatitudeCircle | Tessellate );
+
+ for (int ringIndex = 0; ringIndex < coordinateArray.size(); ++ringIndex) {
+ const QJsonArray ringArray = coordinateArray[ringIndex].toArray();
+
+ GeoDataLinearRing linearRing;
+
+ for (int positionIndex = 0; positionIndex < ringArray.size(); ++positionIndex) {
+ const QJsonArray positionArray = ringArray[positionIndex].toArray();
+
+ const qreal lon = positionArray.at(0).toDouble();
+ const qreal lat = positionArray.at(1).toDouble();
+ const qreal alt = positionArray.at(2).toDouble();
+
+ linearRing.append( GeoDataCoordinates( lon, lat, alt, GeoDataCoordinates::Degree ));
+ }
+
+ if (ringIndex == 0) {
+ // Outer boundary of the polygon
+ geom->setOuterBoundary(linearRing);
+ } else {
+ geom->appendInnerBoundary(linearRing);
+ }
+ }
+ geometryList.append(geom);
+
+ return true;
+
+ } else if (jsonObjectType == QStringLiteral("MultiPolygon")) {
+ // A MultiPolygon object has an array of Polygon arrays (ie, a four-level array)
+
+ for (int polygonIndex = 0; polygonIndex < coordinateArray.size(); ++polygonIndex) {
+ const QJsonArray polygonArray = coordinateArray[polygonIndex].toArray();
+
+ GeoDataPolygon* geom = new GeoDataPolygon( RespectLatitudeCircle | Tessellate );
+
+ for (int ringIndex = 0; ringIndex < polygonArray.size(); ++ringIndex) {
+ const QJsonArray ringArray = polygonArray[ringIndex].toArray();
+
+ GeoDataLinearRing linearRing;
+
+ for (int positionIndex = 0; positionIndex < ringArray.size(); ++positionIndex) {
+ const QJsonArray positionArray = ringArray[positionIndex].toArray();
+
+ const qreal lon = positionArray.at(0).toDouble();
+ const qreal lat = positionArray.at(1).toDouble();
+ const qreal alt = positionArray.at(2).toDouble();
+
+ linearRing.append( GeoDataCoordinates( lon, lat, alt, GeoDataCoordinates::Degree ));
+ }
+
+ if (ringIndex == 0) {
+ // Outer boundary of the polygon
+ geom->setOuterBoundary(linearRing);
+ } else {
+ geom->appendInnerBoundary(linearRing);
+ }
+ }
+ geometryList.append(geom);
+ }
+
+ return true;
+
+ } else if (jsonObjectType == QStringLiteral("")) {
+ // Unlocated Feature objects have a null value for "geometry" (RFC7946 section 3.2)
+ return true;
+
+ } else {
+ qDebug() << "Unknown GeoJSON object type" << jsonObjectType;
+ return false;
}
- return true;
}
}
-
diff --git a/src/plugins/runner/json/JsonParser.h b/src/plugins/runner/json/JsonParser.h
index a9c758841..69fda8c73 100644
--- a/src/plugins/runner/json/JsonParser.h
+++ b/src/plugins/runner/json/JsonParser.h
@@ -1,21 +1,26 @@
/*
- This file is part of the Marble Virtual Globe.
+ This file is part of the Marble Virtual Globe.
- This program is free software licensed under the GNU LGPL. You can
- find a copy of this license in LICENSE.txt in the top directory of
- the source code.
+ This program is free software licensed under the GNU LGPL. You can
+ find a copy of this license in LICENSE.txt in the top directory of
+ the source code.
- Copyright 2013 Ander Pijoan <ander.pijoan at deusto.es>
+ Copyright 2013 Ander Pijoan <ander.pijoan at deusto.es>
+ Copyright 2019 John Zaitseff <J.Zaitseff at zap.org.au>
*/
#ifndef MARBLE_JSONPARSER_H
#define MARBLE_JSONPARSER_H
class QIODevice;
+class QJsonObject;
+
+#include <QVector>
namespace Marble {
class GeoDataDocument;
+class GeoDataGeometry;
class JsonParser
{
@@ -24,8 +29,8 @@ public:
~JsonParser();
/**
- * @brief parse the json file
- * @return true if the parsed has been successful
+ * @brief parse the GeoJSON file
+ * @return true if parsing of the file was successful
*/
bool read(QIODevice*);
@@ -39,6 +44,21 @@ public:
private:
GeoDataDocument* m_document;
+
+ /**
+ * @brief parse a top-level GeoJSON object (FeatureCollection or Feature)
+ * @param jsonObject the object to parse
+ * @return true if parsing of the top-level object was successful
+ */
+ bool parseGeoJsonTopLevel(const QJsonObject&);
+
+ /**
+ * @brief parse a sub-level GeoJSON object
+ * @param jsonObject the object to parse
+ * @param geometryList a list of geometries pass back to the caller
+ * @return true if parsing of the object was successful
+ */
+ bool parseGeoJsonSubLevel(const QJsonObject&, QVector<GeoDataGeometry*>&);
};
}
--
2.20.1
More information about the Marble-devel
mailing list