/**************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Copyright (C) 2018 Julian Sherollari ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qgeojson_p.h" #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE /*! \class QGeoJson \inmodule QtLocation \since 5.13 QGeoJson class can be used to convert between a GeoJSON document (see the \l {https://en.wikipedia.org/wiki/GeoJSON} {Wikipedia page}, \l {https://tools.ietf.org/html/rfc7946} {RFC}) and a \l {http://doc.qt.io/qt-5/qvariant.html#QVariantList-typedef} {QVariantList} of \l QVariantMap elements ready to be used as Model in a \l MapItemView. WARNING! This private class is part of Qt labs, thus not stable API, it is part of the experimental components of QtLocation. Until it is promoted to public API, it may be subject to source and binary-breaking changes. \section2 Importing GeoJSON The importGeoJson() method accepts a \l {http://doc.qt.io/qt-5/qjsondocument.html} {QJsonDocument} from which it extracts a single \l {https://tools.ietf.org/html/rfc7159} {JSON} object, since the GeoJSON RFC expects that a valid GeoJSON Document has in its root a single JSON object. This method doesn't perform any validation on the input. The importer returns a QVariantList containing a single QVariantMap. This map has always at least 2 (key, value) pairs. The first one has \c type as key, and the corresponding value is a string identifying the GeoJSON type. This value can be one of the GeoJSON object types: \c Point, \c MultiPoint, \c LineString, \c MultiLineString, \c Polygon, \c MultiPolygon, \c GeometryCollection, \c FeatureCollection. The second pair has \c data as key, and the corresponding value can be either a QGeoShape or a list, depending on the GeoJSON type. The next section provides details about this node. The \c Feature type is converted into the type of the geometry contained within, with an additional (key, value) pair, where the key is \c properties and the value is a \l QVariantMap. Thus, a feature Map is distinguishable from the corresponding geometry, by looking for a \c properties member. \section3 Structure of the data node For the single type geometry objects (\c Point, \c LineString, and \c Polygon), the value corresponding to the \c data key is a QGeoShape: \list \li When the type is \c Point, the data is a QGeoCircle with the point coordinates stored in the center property. For example, the following GeoJSON document contains a \c Point geometry: \code { "type" : "Point", "data" : [60.0, 11.0] } \endcode it is converted to a QVariantMap with the following structure: \code { type : Point data : QGeoCircle({60.000, 11.000}, -1) } \endcode \li When the type is \c LineString the data ia a QGeoPath. For example, the following GeoJSON document contains a \c LineString geometry: \code { "type" : "LineString", "coordinates" : [[13.5, 43],[10.73, 59.92]] } \endcode it is converted to a QVariantMap with the following structure: \code { type : LineString, data : QGeoPath([{43.000, 13.500}, {59.920, 10.730}]) } \endcode \li When the type is \c Polygon, the data is a QGeoPolygon (holes are supported). For example, the following GeoJSON document contains a \c Polygon geometry: \code { "type" : "Polygon", "coordinates" : [ [[17.13, 51.11], [30.54, 50.42], [26.70, 58.36], [17.13, 51.11]] ], "bbox" : [60, 60, -60, -60] } \endcode it is converted to a QVariantMap with the following structure: \code { type : Polygon data : QGeoPolygon([{51.110, 17.130}, {50.420,30.540}, {58.360, 26.700}, {51.110, 17.130}]) } \endcode \endlist For the homogeneously typed multipart geometry objects (\c MultiPoint, \c MultiLineString, \c MultiPolygon) the value corresponding to the \c data key is a QVariantList. Each element of the list is a QVariantMap of one of the above listed types. The elements in this list will be all of the same GeoJSON type: \list \li When the type is \c MultiPoint, the data is a List of Points. For example, the following GeoJSON document contains a \c MultiPoint geometry: \code { "type" : "MultiPoint", "coordinates" : [ [11,60], [5.5,60.3], [5.7,58.90] ] } \endcode it is converted to a QVariantMap with the following structure: \code { type : MultiPoint data : [ { type : Point data : QGeoCircle({60.000, 11.000}, -1) }, { type : Point data : QGeoCircle({60.300, 5.500}, -1) }, { type : Point data : QGeoCircle({58.900, 5.700}, -1) } ] } \endcode \li When the type is \c MultiLineString, the data is a List of LineStrings. For example, the following GeoJSON document contains a \c MultiLineString geometry: \code { "type" : "MultiLineString", "coordinates" : [ [[13.5, 43], [10.73, 59.92]], [[9.15, 45], [-3.15, 58.90]] ] } \endcode it is converted to a QVariantMap with the following structure: \code { type : MultiLineString data : [ { type : LineString data : QGeoPath([{45.000, 9.150}, {58.900, -3.150}]) }, { type : LineString data : QGeoPath([{43.000, 13.500}, {59.920, 10.730}]) } ] } \endcode \li When the type is \c MultiPolygon, the data is a List of Polygons. For example, the following GeoJSON document contains a \c MultiPolygon geometry: \code { "type" : "MultiPoint", "coordinates" : [ [11,60], [5.5,60.3], [5.7,58.90] ] } \endcode it is converted to a QVariantMap with the following structure: \code { type : MultiPoint data : [ { type : Point data : QGeoCircle({60.000, 11.000}, -1) }, { type : Point data : QGeoCircle({60.300, 5.500}, -1) }, { type : Point data : QGeoCircle({58.900, 5.700}, -1) } ] } \endcode \endlist The \c GeometryCollection is a heterogeneous composition of other geometry types. In the resulting QVariantMap, the value of the \c data member is a QVariantList populated by QVariantMaps of various geometries, including the GeometryCollection itself. For example, the following \c GeometryCollection: \code { "type" : "GeometryCollection", "geometries" : [ { "type" : "MultiPoint", "coordinates" : [ [11,60], [5.5,60.3], [5.7,58.90] ] }, { "type" : "MultiLineString", "coordinates" : [ [[13.5, 43], [10.73, 59.92]], [[9.15, 45], [-3.15, 58.90]] ] }, { "type" : "MultiPolygon", "coordinates" : [ [ [[17.13, 51.11], [30.54, 50.42], [26.74, 58.36], [17.13, 51.11]] ], [ [[19.84, 41.33], [30.45, 49.26], [17.07, 50.10], [19.84, 41.33]] ] ] } ] } \endcode it is converted to a QVariantMap with the following structure: \code { type : GeometryCollection data : [ { type : MultiPolygon data : [ { type : Polygon data : QGeoPolygon([{41.330, 19.840}, {49.260, 30.450}, {50.100, 17.070}, {41.330, 19.840}]) } { type : Polygon data : QGeoPolygon([{51.110, 17.130}, {50.420, 30.540}, {58.360, 26.740}, {51.110, 17.130}]) } ] } { type : MultiLineString data : [ { type : LineString data : QGeoPath([{45.000, 9.150}, {58.900, -3.150}]) } { type : LineString data : QGeoPath([{43.000, 13.500}, {59.920, 10.730}]) } ] } { type : MultiPoint data : [ { type : Point data : QGeoCircle({58.900, 5.700}, -1) }, { type : Point data : QGeoCircle({60.300, 5.500}, -1) }, { type : Point data : QGeoCircle({60.000, 11.000}, -1) } ] } ] } \endcode The \c Feature object, which consists of one of the previous geometries together with related attributes, is structured like one of the 7 above mentioned geometry types, plus a \c properties member. The value of this member is a QVariantMap. The only way to distinguish a Feature from the included geometry is to check if a \c properties node is present in the QVariantMap. For example, the following \c Feature: \code { "type" : "Feature", "id" : "Poly", "properties" : { "text" : "This is a Feature with a Polygon" }, "geometry" : { "type" : "Polygon", "coordinates" : [ [[17.13, 51.11], [30.54, 50.42], [26.70, 58.36], [17.13, 51.11]], [[23.46, 54.36], [20.52, 51.91], [28.25, 51.50], [26.80, 54.36], [23.46, 54.36]] ] } } \endcode it is converted to a QVariantMap with the following structure: \code { type : Polygon data : QGeoPolygon([{51.110, 17.130}, {50.420,30.540}, {58.360, 26.700}, {51.110, 17.130}]) properties : {text : This is a Feature with a Polygon} } \endcode The \c FeatureCollection is a composition of Feature objects. The value of the \c data member in a FeatureCollection is a QVariantList populated by Feature type QVariantMaps. For example, the following \c FeatureCollection: \code { "type" : "FeatureCollection", "features" : [ { "type" : "Feature", "id" : "Poly", "properties" : { "text" : "This is a Feature with a Polygon" }, "geometry" : { "type" : "Polygon", "coordinates" : [ [[17.13, 51.11], [30.54, 50.42], [26.70, 58.36], [17.13, 51.11]], [[23.46, 54.36], [20.52, 51.91], [28.25, 51.50], [26.80, 54.36], [23.46, 54.36]] ] } }, { "type" : "Feature", "id" : "MultiLine", "properties" : { "text" : "This is a Feature with a MultiLineString" }, "geometry" : { "type" : "MultiLineString", "coordinates" : [ [[13.5, 43], [10.73, 59.92]], [[9.15, 45], [-3.15, 58.90]] ] } } ] } \endcode it is converted to a QVariantMap with the following structure: \code { type : FeatureCollection data : [ { type : MultiLineString data : [ { type : LineString data : QGeoPath([{45.000, 9.150}, {58.900, -3.150}]) } { type : LineString data : QGeoPath([{43.000, 13.500}, {59.920, 10.730}]) } ] properties : {text : This is a Feature with a MultiLineString} }, { type : Polygon data : QGeoPolygon({51.110, 17.130}, {50.420, 30.540}, {58.360, 26.700}, {51.110, 17.130}) properties : {text : This is a Feature with a Polygon} } ] } \endcode \section2 Exporting GeoJSON The exporter accepts the QVariantList returned by the \l {Importing GeoJSON} {importer}, and returns a JSON document. The exporter is complementary to the importer because it executes the inverse action. \section2 The toString function The \l toString outputs, for debugging purposes, the content of a QVariantList structured like \l importGeoJson does, to a QString using a prettyfied format. */ static QGeoCoordinate importPosition(const QVariant &position) { QGeoCoordinate returnedCoordinates; const QVariantList positionList = position.value(); for (int i = 0; i < positionList.size(); ++i) { // Iterating Point coordinates arrays switch (i) { case 0: returnedCoordinates.setLongitude(positionList.at(i).toDouble()); break; case 1: returnedCoordinates.setLatitude(positionList.at(i).toDouble()); break; case 2: returnedCoordinates.setAltitude(positionList.at(i).toDouble()); break; default: break; } } return returnedCoordinates; } static QList importArrayOfPositions(const QVariant &arrayOfPositions) { QList returnedCoordinates; const QVariantList positionsList = arrayOfPositions.value(); QGeoCoordinate singlePosition; for (int i = 0; i < positionsList.size(); ++i) { // Iterating the LineString coordinates nested arrays singlePosition = importPosition((positionsList.at(i))); returnedCoordinates.append(singlePosition); // Populating the QList of coordinates } return returnedCoordinates; } static QList> importArrayOfArrayOfPositions(const QVariant &arrayOfArrayofPositions) { QList> returnedCoordinates; const QVariantList positionsList = arrayOfArrayofPositions.value(); QList arrayOfPositions; for (int i = 0; i < positionsList.size(); ++i) { // Iterating the Polygon coordinates nested arrays arrayOfPositions = importArrayOfPositions((positionsList.at(i))); returnedCoordinates << arrayOfPositions; } return returnedCoordinates; } static QGeoCircle importPoint(const QVariantMap &inputMap) { QGeoCircle returnedObject; QGeoCoordinate center; QVariant valueCoords = inputMap.value(QStringLiteral("coordinates")); center = importPosition(valueCoords); returnedObject.setCenter(center); return returnedObject; } static QGeoPath importLineString(const QVariantMap &inputMap) { QGeoPath returnedObject; QList coordinatesList; const QVariant valueCoordinates = inputMap.value(QStringLiteral("coordinates")); coordinatesList = importArrayOfPositions(valueCoordinates); returnedObject.setPath(coordinatesList); return returnedObject; } static QGeoPolygon importPolygon(const QVariantMap &inputMap) { QGeoPolygon returnedObject; const QVariant valueCoordinates = inputMap.value(QStringLiteral("coordinates")); QList> perimeters = importArrayOfArrayOfPositions(valueCoordinates); for (int i = 0; i < perimeters.size(); ++i) { // Import an array of QList if (i == 0) returnedObject.setPath(perimeters.at(i)); // External perimeter else returnedObject.addHole(perimeters.at(i)); // Inner perimeters } return returnedObject; } static QVariantList importMultiPoint(const QVariantMap &inputMap) { QVariantList returnedObject; const QVariantList coordinatesList = inputMap.value(QStringLiteral("coordinates")).value(); QVariantMap singlePointMap; QGeoCircle parsedPoint; for (int i = 0; i < coordinatesList.size(); ++i) { // Iterating MultiPoint coordinates nasted arrays parsedPoint.setCenter(importPosition(coordinatesList.at(i))); singlePointMap.insert(QStringLiteral("type"), QStringLiteral("Point")); singlePointMap.insert(QStringLiteral("data"), QVariant::fromValue(parsedPoint)); returnedObject.append(QVariant::fromValue(singlePointMap)); } return returnedObject; } static QVariantList importMultiLineString(const QVariantMap &inputMap) { QVariantList returnedObject; QGeoPath parsedLineString; const QVariant listCoords = inputMap.value(QStringLiteral("coordinates")); const QVariantList list = listCoords.value(); QVariantMap singleLinestringMap; for (int i = 0; i < list.size(); ++i) { // Iterating the MultiLineString coordinates nasted arrays using importArrayOfPositions singleLinestringMap.clear(); const QList coordinatesList = importArrayOfPositions(list.at(i)); singleLinestringMap.insert(QStringLiteral("type"), QStringLiteral("LineString")); parsedLineString.setPath(coordinatesList); singleLinestringMap.insert(QStringLiteral("data"), QVariant::fromValue(parsedLineString)); returnedObject.append(QVariant::fromValue(singleLinestringMap)); } return returnedObject; } static QVariantList importMultiPolygon(const QVariantMap &inputMap) { QVariantList returnedObject; QGeoPolygon singlePoly; QVariantMap singlePolygonMap; const QVariant valueCoordinates = inputMap.value(QStringLiteral("coordinates")); const QVariantList list = valueCoordinates.value(); for (int i = 0; i < list.size(); ++i) { // Iterating the MultiPolygon coordinates nasted arrays singlePolygonMap.clear(); const QList> coordinatesList = importArrayOfArrayOfPositions(list.at(i)); for (int j = 0; j < coordinatesList.size(); ++j) { if (j == 0) singlePoly.setPath(coordinatesList.at(j)); else singlePoly.addHole(coordinatesList.at(j)); } singlePolygonMap.insert(QStringLiteral("type"), QStringLiteral("Polygon")); singlePolygonMap.insert(QStringLiteral("data"), QVariant::fromValue(singlePoly)); returnedObject.append(QVariant::fromValue(singlePolygonMap)); } return returnedObject; } static QVariantMap importGeometry(const QVariantMap &inputMap); // Function prototype for a tail recursion static QVariantList importGeometryCollection(const QVariantMap &inputMap) { QVariantList returnedObject; const QVariant listGeometries = inputMap.value(QStringLiteral("geometries")); const QVariantList list = listGeometries.value(); // QVariantList of heterogeneous composition of the other geometry types for (int i = 0; i < list.size(); ++i) { QVariantMap geometryMap = list.at(i).value(); QVariantMap geoMap = importGeometry(geometryMap); returnedObject.append(geoMap); } return returnedObject; } static QVariantMap importGeometry(const QVariantMap &inputMap) { QVariantMap returnedObject; QString geometryTypes[] = { QStringLiteral("Point"), QStringLiteral("MultiPoint"), QStringLiteral("LineString"), QStringLiteral("MultiLineString"), QStringLiteral("Polygon"), QStringLiteral("MultiPolygon"), QStringLiteral("GeometryCollection") }; enum geoTypeSwitch { Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon, GeometryCollection }; for (int i = 0; i<7; ++i) { if (inputMap.value(QStringLiteral("type")).value() == geometryTypes[i]) { switch (i) { case Point: { returnedObject.insert(QStringLiteral("type"), QStringLiteral("Point")); returnedObject.insert(QStringLiteral("data"), QVariant::fromValue(importPoint(inputMap))); break; } case MultiPoint: { returnedObject.insert(QStringLiteral("type"), QStringLiteral("MultiPoint")); returnedObject.insert(QStringLiteral("data"), QVariant::fromValue(importMultiPoint(inputMap))); break; } case LineString: { returnedObject.insert(QStringLiteral("type"), QStringLiteral("LineString")); returnedObject.insert(QStringLiteral("data"), QVariant::fromValue(importLineString(inputMap))); break; } case MultiLineString: { returnedObject.insert(QStringLiteral("type"), QStringLiteral("MultiLineString")); returnedObject.insert(QStringLiteral("data"), QVariant::fromValue(importMultiLineString(inputMap))); break; } case Polygon: { returnedObject.insert(QStringLiteral("type"), QStringLiteral("Polygon")); returnedObject.insert(QStringLiteral("data"), QVariant::fromValue(importPolygon(inputMap))); break; } case MultiPolygon: { returnedObject.insert(QStringLiteral("type"), QStringLiteral("MultiPolygon")); returnedObject.insert(QStringLiteral("data"), QVariant::fromValue(importMultiPolygon(inputMap))); break; } case GeometryCollection: { returnedObject.insert(QStringLiteral("type"), QStringLiteral("GeometryCollection")); returnedObject.insert(QStringLiteral("data"), QVariant::fromValue(importGeometryCollection(inputMap))); break; } default: break; } } } return returnedObject; } static QVariantList importFeatureCollection(const QVariantMap &inputMap) { QVariantList returnedObject; const QVariantList featuresList = inputMap.value(QStringLiteral("features")).value(); for (int i = 0; i < featuresList.size(); ++i) { QVariantMap inputFeatureMap = featuresList.at(i).value(); QVariantMap singleFeatureMap = importGeometry(inputFeatureMap.value(QStringLiteral("geometry")).value()); const QVariantMap importedProperties = inputFeatureMap.value(QStringLiteral("properties")).value(); singleFeatureMap.insert(QStringLiteral("properties"), importedProperties); if (inputFeatureMap.contains(QStringLiteral("id"))) { QVariant importedId = inputFeatureMap.value(QStringLiteral("id")).value(); singleFeatureMap.insert(QStringLiteral("id"), importedId); } returnedObject.append(singleFeatureMap); } return returnedObject; } static QJsonValue exportPosition(const QGeoCoordinate &obtainedCoordinates) { QJsonValue geoLat = obtainedCoordinates.latitude(); QJsonValue geoLong = obtainedCoordinates.longitude(); QJsonArray array = {geoLong, geoLat}; QJsonValue geoAlt; if (!qIsNaN(obtainedCoordinates.altitude())) { geoAlt = obtainedCoordinates.altitude(); array.append(geoAlt); } QJsonValue geoArray = array; return geoArray; } static QJsonValue exportArrayOfPositions(const QList &obtainedCoordinatesList) { QJsonValue lineCoordinates; QJsonValue multiPosition; QJsonArray arrayPosition; for (int i = 0; i < obtainedCoordinatesList.size(); ++i) { multiPosition = exportPosition(obtainedCoordinatesList.at(i)); arrayPosition.append(multiPosition); } lineCoordinates = arrayPosition; return lineCoordinates; } static QJsonValue exportArrayOfArrayOfPositions(const QList> &obtainedCoordinates) { QJsonValue lineCoordinates; QJsonValue polyCoordinates; QJsonArray arrayPath; for (int i = 0; i < obtainedCoordinates.size(); ++i) { lineCoordinates = exportArrayOfPositions(obtainedCoordinates.at(i)); arrayPath.append(lineCoordinates); } polyCoordinates = arrayPath; return polyCoordinates; } static QJsonObject exportPoint(const QVariantMap &pointMap) { QJsonObject parsedPoint; QGeoCircle circle = pointMap.value(QStringLiteral("data")).value(); parsedPoint.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("Point"))); parsedPoint.insert(QStringLiteral("coordinates"), exportPosition(circle.center())); return parsedPoint; } static QJsonObject exportLineString(const QVariantMap &lineStringMap) { QJsonObject parsedLineString; QList linestringPath = lineStringMap.value(QStringLiteral("data")).value().path(); parsedLineString.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("LineString"))); parsedLineString.insert(QStringLiteral("coordinates"), exportArrayOfPositions(linestringPath)); return parsedLineString; } static QJsonObject exportPolygon(const QVariantMap &polygonMap) { QVariant polygonVariant = polygonMap.value(QStringLiteral("data")); QJsonObject parsedPolygon; QJsonValue polyCoordinates; QList> obtainedCoordinatesPoly; QGeoPolygon parsedPoly = polygonVariant.value(); obtainedCoordinatesPoly << parsedPoly.path(); if (parsedPoly.holesCount()!=0) for (int i = 0; i < parsedPoly.holesCount(); ++i) { obtainedCoordinatesPoly << parsedPoly.holePath(i); } polyCoordinates = exportArrayOfArrayOfPositions(obtainedCoordinatesPoly); parsedPolygon.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("Polygon"))); parsedPolygon.insert(QStringLiteral("coordinates"), polyCoordinates); return parsedPolygon; } static QJsonObject exportMultiPoint(const QVariantMap &multiPointMap) { QJsonObject parsedMultiPoint; QList obtainedCoordinatesMP; QVariantList multiCircleVariantList = multiPointMap.value(QStringLiteral("data")).value(); for (const QVariant &exCircleVariantMap: multiCircleVariantList) { obtainedCoordinatesMP << exCircleVariantMap.value().value(QStringLiteral("data")).value().center(); } QJsonValue multiPosition = exportArrayOfPositions(obtainedCoordinatesMP); parsedMultiPoint.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("MultiPoint"))); parsedMultiPoint.insert(QStringLiteral("coordinates"), multiPosition); return parsedMultiPoint; } static QJsonObject exportMultiLineString(const QVariantMap &multiLineStringMap) { QJsonObject parsedMultiLineString; QList> extractedCoordinatesValue; QVariant multiPathVariant = multiLineStringMap.value(QStringLiteral("data")); QVariantList multiPathList = multiPathVariant.value(); for (int i = 0; i < multiPathList.size(); ++i) { extractedCoordinatesValue << multiPathList.at(i).value().value(QStringLiteral("data")).value().path(); } QJsonValue exportedCoordinatesValue = exportArrayOfArrayOfPositions(extractedCoordinatesValue); parsedMultiLineString.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("MultiLineString"))); parsedMultiLineString.insert(QStringLiteral("coordinates"), exportedCoordinatesValue); return parsedMultiLineString; } static QJsonObject exportMultiPolygon(const QVariantMap &multiPolygonMap) { QJsonObject parsedMultiPolygon; QJsonValue polyCoordinates; QJsonArray parsedArrayPolygon; QList> extractedCoordinatesValue; QVariant multiPolygonVariant = multiPolygonMap.value(QStringLiteral("data")); QVariantList multiPolygonList = multiPolygonVariant.value(); int polyHoles = 0; int currentHole; for (int i = 0; i < multiPolygonList.size(); ++i) { // Start parsing Polygon list extractedCoordinatesValue << multiPolygonList.at(i).value().value(QStringLiteral("data")).value().path(); // Extract external polygon path polyHoles = multiPolygonList.at(i).value().value(QStringLiteral("data")).value().holesCount(); if (polyHoles) // Check if the polygon has holes for (currentHole = 0 ; currentHole < polyHoles; currentHole++) extractedCoordinatesValue << multiPolygonList.at(i).value().value(QStringLiteral("data")).value().holePath(currentHole); polyCoordinates = exportArrayOfArrayOfPositions(extractedCoordinatesValue); // Generates QJsonDocument compatible value parsedArrayPolygon.append(polyCoordinates); // Adds one level of nesting in coordinates extractedCoordinatesValue.clear(); // Clears the temporary polygon linear ring storage } QJsonValue exportedCoordinatesNodeValue = parsedArrayPolygon; parsedMultiPolygon.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("MultiPolygon"))); parsedMultiPolygon.insert(QStringLiteral("coordinates"), exportedCoordinatesNodeValue); return parsedMultiPolygon; } static QJsonObject exportGeometry(const QVariantMap &geometryMap); // Function prototype static QJsonObject exportGeometryCollection(const QVariantMap &geometryCollection) { QJsonObject parsed; QJsonObject parsedGeometry; QJsonValue valueGeometries; QJsonArray parsedGeometries; QVariantList geometriesList = geometryCollection.value(QStringLiteral("data")).value(); for (int i = 0; i < geometriesList.size(); ++i) { parsedGeometry = exportGeometry(geometriesList.at(i).value()); valueGeometries = parsedGeometry; parsedGeometries.append(valueGeometries); } QJsonValue exportedGeometriesValue = parsedGeometries; parsed.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("GeometryCollection"))); parsed.insert(QStringLiteral("geometries"), exportedGeometriesValue); return parsed; } static QJsonObject exportGeometry(const QVariantMap &geometryMap) { QJsonObject exportedGeometry; if (geometryMap.value(QStringLiteral("type")) == QStringLiteral("Point")) exportedGeometry = exportPoint(geometryMap); if (geometryMap.value(QStringLiteral("type")) == QStringLiteral("MultiPoint")) exportedGeometry = exportMultiPoint(geometryMap); if (geometryMap.value(QStringLiteral("type")) == QStringLiteral("LineString")) exportedGeometry = exportLineString(geometryMap); if (geometryMap.value(QStringLiteral("type")) == QStringLiteral("MultiLineString")) exportedGeometry = exportMultiLineString(geometryMap); if (geometryMap.value(QStringLiteral("type")) == QStringLiteral("Polygon")) exportedGeometry = exportPolygon(geometryMap); if (geometryMap.value(QStringLiteral("type")) == QStringLiteral("MultiPolygon")) exportedGeometry = exportMultiPolygon(geometryMap); if (geometryMap.value(QStringLiteral("type")) == QStringLiteral("GeometryCollection")) exportedGeometry = exportGeometryCollection(geometryMap); return exportedGeometry; } static QJsonObject exportFeature(const QVariantMap &featureMap) { QJsonObject exportedFeature; QJsonValue geometryNodeValue = QJsonValue(exportGeometry(featureMap)); QJsonValue propertiesNodeValue = featureMap.value(QStringLiteral("properties")).value().toJsonValue(); QJsonValue idNodeValue = featureMap.value(QStringLiteral("id")).value().toJsonValue(); exportedFeature.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("Feature"))); exportedFeature.insert(QStringLiteral("geometry"), geometryNodeValue); exportedFeature.insert(QStringLiteral("properties"), propertiesNodeValue); exportedFeature.insert(QStringLiteral("id"), idNodeValue); return exportedFeature; } static QJsonObject exportFeatureCollection(const QVariantMap &featureCollection) { QJsonObject exportedFeatureCollection; QJsonArray featureArray; QVariantList featureList = featureCollection.value(QStringLiteral("data")).value(); for (int i = 0; i < featureList.size(); ++i) { featureArray.append(QJsonValue(exportFeature(featureList.at(i).value()))); } exportedFeatureCollection.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("FeatureCollection"))); exportedFeatureCollection.insert(QStringLiteral("features"), QJsonValue(featureArray) ); return exportedFeatureCollection; } /*! This method imports the \a geoJson document, expected to contain valid GeoJSON data, into a QVariantList structured like described in the section \l {Importing GeoJSON}. \note This method performs no validation on the input. \sa exportGeoJson */ QVariantList QGeoJson::importGeoJson(const QJsonDocument &geoJson) { QVariantList returnedList; QJsonObject object = geoJson.object(); // Read json object from imported doc QVariantMap rootGeoJsonObject = object.toVariantMap(); // Extraced map using Qt's API QString geoType[] = { QStringLiteral("Point"), QStringLiteral("MultiPoint"), QStringLiteral("LineString"), QStringLiteral("MultiLineString"), QStringLiteral("Polygon"), QStringLiteral("MultiPolygon"), QStringLiteral("GeometryCollection"), QStringLiteral("Feature"), QStringLiteral("FeatureCollection") }; enum geoTypeSwitch { Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon, GeometryCollection, Feature, FeatureCollection }; QVariantMap parsedGeoJsonMap; // Checking whether the JSON object has a "type" member const QVariant keyVariant = rootGeoJsonObject.value(QStringLiteral("type")); if (keyVariant == QVariant::Invalid) { // Type check failed } QString valueType = keyVariant.value(); // Checking whether the "type" member has a GeoJSON admitted value for (int i = 0; i < 9; ++i) { if (valueType == geoType[i]) { switch (i) { case Point: { QGeoCircle circle = importPoint(rootGeoJsonObject); QVariant dataNodeValue = QVariant::fromValue(circle); parsedGeoJsonMap.insert(QStringLiteral("type"), QStringLiteral("Point")); parsedGeoJsonMap.insert(QStringLiteral("data"), dataNodeValue); break; } case MultiPoint: { QVariantList multiCircle = importMultiPoint(rootGeoJsonObject); QVariant dataNodeValue = QVariant::fromValue(multiCircle); QList testlist; parsedGeoJsonMap.insert(QStringLiteral("type"), QStringLiteral("MultiPoint")); parsedGeoJsonMap.insert(QStringLiteral("data"), dataNodeValue); break; } case LineString: { QGeoPath lineString = importLineString(rootGeoJsonObject); QVariant dataNodeValue = QVariant::fromValue(lineString); parsedGeoJsonMap.insert(QStringLiteral("type"), QStringLiteral("LineString")); parsedGeoJsonMap.insert(QStringLiteral("data"), dataNodeValue); break; } case MultiLineString: { QVariantList multiLineString = importMultiLineString(rootGeoJsonObject); QVariant dataNodeValue = QVariant::fromValue(multiLineString); parsedGeoJsonMap.insert(QStringLiteral("type"), QStringLiteral("MultiLineString")); parsedGeoJsonMap.insert(QStringLiteral("data"), dataNodeValue); break; } case Polygon: { QGeoPolygon poly = importPolygon(rootGeoJsonObject); QVariant dataNodeValue = QVariant::fromValue(poly); parsedGeoJsonMap.insert(QStringLiteral("type"), QStringLiteral("Polygon")); parsedGeoJsonMap.insert(QStringLiteral("data"), dataNodeValue); break; } case MultiPolygon: { QVariantList multiPoly = importMultiPolygon(rootGeoJsonObject); QVariant dataNodeValue = QVariant::fromValue(multiPoly); parsedGeoJsonMap.insert(QStringLiteral("type"), QStringLiteral("MultiPolygon")); parsedGeoJsonMap.insert(QStringLiteral("data"), dataNodeValue); break; } // List of GeoJson geometry objects case GeometryCollection: { QVariantList multiGeo = importGeometryCollection(rootGeoJsonObject); QVariant dataNodeValue = QVariant::fromValue(multiGeo); parsedGeoJsonMap.insert(QStringLiteral("type"), QStringLiteral("GeometryCollection")); parsedGeoJsonMap.insert(QStringLiteral("data"), dataNodeValue); break; } // Single GeoJson geometry object with properties case Feature: { parsedGeoJsonMap = importGeometry(rootGeoJsonObject.value(QStringLiteral("geometry")).value()); QVariantMap importedProperties = rootGeoJsonObject.value(QStringLiteral("properties")).value(); parsedGeoJsonMap.insert(QStringLiteral("properties"), importedProperties); if (rootGeoJsonObject.contains(QStringLiteral("id"))){ QVariant importedId = rootGeoJsonObject.value(QStringLiteral("id")).value(); parsedGeoJsonMap.insert(QStringLiteral("id"), importedId); } break; } // Heterogeneous list of GeoJSON geometries with properties case FeatureCollection: { QVariantList featCollection = importFeatureCollection(rootGeoJsonObject); QVariant dataNodeValue = QVariant::fromValue(featCollection); parsedGeoJsonMap.insert(QStringLiteral("type"), QStringLiteral("FeatureCollection")); parsedGeoJsonMap.insert(QStringLiteral("data"), dataNodeValue); break; } default: break; } QVariant bboxNodeValue = rootGeoJsonObject.value(QStringLiteral("bbox")); if (bboxNodeValue != QVariant::Invalid) { parsedGeoJsonMap.insert(QStringLiteral("bbox"), bboxNodeValue); } returnedList.append(parsedGeoJsonMap); } else if (i >= 9) { // Error break; } } return returnedList; } /*! This method exports the QVariantList \a geoData, expected to be structured like described in the section \l {Importing GeoJSON}, to a QJsonDocument containing the data converted to GeoJSON. \note This method performs no validation on the input. \sa importGeoJson */ QJsonDocument QGeoJson::exportGeoJson(const QVariantList &geoData) { QVariantMap exportMap = geoData.at(0).value(); // Extracting the QVMap QJsonObject newObject; QJsonDocument newDocument; if (exportMap.contains(QStringLiteral("properties"))) { newObject = exportFeature(exportMap); } else { if (exportMap.value(QStringLiteral("type")) == QStringLiteral("Point")) // Check the value corresponding to the key "Point" newObject = exportPoint(exportMap); if (exportMap.value(QStringLiteral("type")) == QStringLiteral("MultiPoint")) newObject = exportMultiPoint(exportMap); if (exportMap.value(QStringLiteral("type")) == QStringLiteral("LineString")) newObject = exportLineString(exportMap); if (exportMap.value(QStringLiteral("type")) == QStringLiteral("MultiLineString")) newObject = exportMultiLineString(exportMap); if (exportMap.value(QStringLiteral("type")) == QStringLiteral("Polygon")) newObject = exportPolygon(exportMap); if (exportMap.value(QStringLiteral("type")) == QStringLiteral("MultiPolygon")) newObject = exportMultiPolygon(exportMap); if (exportMap.value(QStringLiteral("type")) == QStringLiteral("GeometryCollection")) newObject = exportGeometryCollection(exportMap); if (exportMap.value(QStringLiteral("type")) == QStringLiteral("FeatureCollection")) newObject = exportFeatureCollection(exportMap); } if (exportMap.contains((QStringLiteral("bbox")))) { QJsonArray bboxArray; QVariantList bboxList = exportMap.value(QStringLiteral("bbox")).value(); for (int i = 0; i < bboxList.size(); ++i) { bboxArray.append(QJsonValue(bboxList.at(i).value())); } newObject.insert(QStringLiteral("bbox"), QJsonValue(bboxArray)); } newDocument.setObject(newObject); return newDocument; } // Functions for toString QTextStream &operator << (QTextStream &stream, const QGeoCoordinate &crd) { stream << "{ " << QString::number(crd.latitude(), 'f', 3) << ", " << QString::number(crd.longitude(), 'f', 3) << ", " << QString::number(crd.altitude(), 'f', 3) << " }"; return stream; } QTextStream &operator << (QTextStream &stream, const QGeoShape &shape) { switch (shape.type()) { case QGeoShape::CircleType: { QGeoCircle circle(shape); stream << "QGeoCircle(" <()) { workigGeometry = v.value(); if (workigGeometry.type() == QGeoShape::CircleType) { QGeoCircle circle = v.value(); stream << circle<< "\n"; } else if (workigGeometry.type() == QGeoShape::PathType) { QGeoPath path = v.value(); stream << path<< "\n"; } else if (workigGeometry.type() == QGeoShape::PolygonType) { QGeoPolygon polygon = v.value(); stream << polygon<< "\n"; } } else { if (v.isNull()) stream << "null\n"; else stream << v.toString() << "\n"; } } return res; } /*! This method accepts the QVariantList \a geoData, structured as described in \l {Importing GeoJSON}, and returns a string containing the same data in a readable form. */ QString QGeoJson::toString(const QVariantList &geoData) { return printQvariant(geoData.first(), 0); } QT_END_NAMESPACE