1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 The Qt Company Ltd.
4 ** Copyright (C) 2018 Julian Sherollari <jdotsh@gmail.com>
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the QtCore module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40 
41 #include "qgeojson_p.h"
42 #include <qjsonobject.h>
43 #include <qjsonvalue.h>
44 #include <qjsonarray.h>
45 #include <qgeocoordinate.h>
46 #include <qgeocircle.h>
47 #include <qgeopath.h>
48 #include <qgeopolygon.h>
49 #include <qtextstream.h>
50 
51 QT_BEGIN_NAMESPACE
52 
53 /*! \class QGeoJson
54     \inmodule QtLocation
55     \since 5.13
56 
57     QGeoJson class can be used to convert between a GeoJSON document (see the
58     \l {https://en.wikipedia.org/wiki/GeoJSON} {Wikipedia page}, \l
59     {https://tools.ietf.org/html/rfc7946} {RFC}) and a \l
60     {http://doc.qt.io/qt-5/qvariant.html#QVariantList-typedef} {QVariantList}
61     of \l QVariantMap elements ready to be used as Model in a \l MapItemView.
62     WARNING! This private class is part of Qt labs, thus not stable API, it is
63     part of the experimental components of QtLocation. Until it is promoted to
64     public API, it may be subject to source and binary-breaking changes.
65 
66     \section2 Importing GeoJSON
67 
68     The importGeoJson() method accepts a \l
69     {http://doc.qt.io/qt-5/qjsondocument.html} {QJsonDocument} from which it
70     extracts a single \l {https://tools.ietf.org/html/rfc7159} {JSON} object,
71     since the GeoJSON RFC expects that a valid GeoJSON Document has in its root
72     a single JSON object. This method doesn't perform any validation on the
73     input. The importer returns a QVariantList containing a single QVariantMap.
74     This map has always at least 2 (key, value) pairs. The first one has \c
75     type as key, and the corresponding value is a string identifying the
76     GeoJSON type. This value can be one of the GeoJSON object types: \c Point,
77     \c MultiPoint, \c LineString, \c MultiLineString, \c Polygon, \c
78     MultiPolygon, \c GeometryCollection, \c FeatureCollection. The second pair
79     has \c data as key, and the corresponding value can be either a QGeoShape
80     or a list, depending on the GeoJSON type. The next section provides details
81     about this node. The \c Feature type is converted into the type of the
82     geometry contained within, with an additional (key, value) pair, where the
83     key is \c properties and the value is a \l QVariantMap. Thus, a feature Map
84     is distinguishable from the corresponding geometry, by looking for a \c
85     properties member.
86 
87     \section3 Structure of the data node
88 
89     For the single type geometry objects (\c Point, \c LineString, and \c
90     Polygon), the value corresponding to the \c data key is a QGeoShape:
91 
92     \list
93     \li When the type is \c Point, the data is a QGeoCircle with the point
94         coordinates stored in the center property.
95 
96         For example, the following GeoJSON document contains a \c Point
97         geometry:
98 
99         \code
100         {
101           "type" : "Point",
102           "data" : [60.0, 11.0]
103         }
104         \endcode
105 
106         it is converted to a QVariantMap with the following structure:
107 
108         \code
109         {
110           type : Point
111           data : QGeoCircle({60.000, 11.000}, -1)
112         }
113         \endcode
114 
115     \li When the type is \c LineString the data ia a QGeoPath.
116 
117         For example, the following GeoJSON document contains a \c LineString
118         geometry:
119 
120         \code
121         {
122             "type" : "LineString",
123             "coordinates" : [[13.5, 43],[10.73, 59.92]]
124         }
125         \endcode
126 
127         it is converted to a QVariantMap with the following structure:
128 
129         \code
130         {
131           type : LineString,
132           data : QGeoPath([{43.000, 13.500}, {59.920, 10.730}])
133         }
134         \endcode
135 
136     \li When the type is \c Polygon, the data is a QGeoPolygon (holes are
137         supported).
138 
139         For example, the following GeoJSON document contains a \c Polygon
140         geometry:
141 
142         \code
143         {
144             "type" : "Polygon",
145             "coordinates" : [
146               [[17.13, 51.11],
147                [30.54, 50.42],
148                [26.70, 58.36],
149                [17.13, 51.11]]
150             ],
151             "bbox" : [60, 60, -60, -60]
152 
153         }
154         \endcode
155 
156         it is converted to a QVariantMap with the following structure:
157 
158         \code
159         {
160           type : Polygon
161           data : QGeoPolygon([{51.110, 17.130}, {50.420,30.540}, {58.360, 26.700}, {51.110, 17.130}])
162         }
163         \endcode
164 
165     \endlist
166 
167     For the homogeneously typed multipart geometry objects (\c MultiPoint, \c
168     MultiLineString, \c MultiPolygon) the value corresponding to the \c data
169     key is a QVariantList. Each element of the list is a QVariantMap of one of
170     the above listed types. The elements in this list will be all of the same
171     GeoJSON type:
172 
173     \list
174     \li When the type is \c MultiPoint, the data is a List of Points.
175 
176         For example, the following GeoJSON document contains a \c MultiPoint
177         geometry:
178 
179         \code
180         {
181             "type" : "MultiPoint",
182             "coordinates" : [
183               [11,60],
184               [5.5,60.3],
185               [5.7,58.90]
186             ]
187         }
188         \endcode
189 
190         it is converted to a QVariantMap with the following structure:
191 
192         \code
193         {
194           type : MultiPoint
195           data : [
196             {
197               type : Point
198               data : QGeoCircle({60.000, 11.000}, -1)
199             },
200             {
201               type : Point
202               data : QGeoCircle({60.300, 5.500}, -1)
203             },
204             {
205               type : Point
206               data : QGeoCircle({58.900, 5.700}, -1)
207             }
208           ]
209         }
210         \endcode
211 
212     \li When the type is \c MultiLineString, the data is a List of LineStrings.
213 
214         For example, the following GeoJSON document contains a \c MultiLineString
215         geometry:
216 
217         \code
218         {
219             "type" : "MultiLineString",
220             "coordinates" : [
221               [[13.5, 43], [10.73, 59.92]],
222               [[9.15, 45], [-3.15, 58.90]]
223             ]
224         }
225         \endcode
226 
227         it is converted to a QVariantMap with the following structure:
228 
229         \code
230         {
231           type : MultiLineString
232           data : [
233             {
234               type : LineString
235               data : QGeoPath([{45.000, 9.150}, {58.900, -3.150}])
236             },
237             {
238               type : LineString
239               data : QGeoPath([{43.000, 13.500}, {59.920, 10.730}])
240             }
241           ]
242         }
243         \endcode
244 
245     \li When the type is \c MultiPolygon, the data is a List of Polygons.
246 
247         For example, the following GeoJSON document contains a \c MultiPolygon
248         geometry:
249 
250         \code
251         {
252             "type" : "MultiPoint",
253             "coordinates" : [
254               [11,60],
255               [5.5,60.3],
256               [5.7,58.90]
257             ]
258         }
259         \endcode
260 
261         it is converted to a QVariantMap with the following structure:
262 
263         \code
264         {
265           type : MultiPoint
266           data : [
267             {
268               type : Point
269               data : QGeoCircle({60.000, 11.000}, -1)
270             },
271             {
272               type : Point
273               data : QGeoCircle({60.300, 5.500}, -1)
274             },
275             {
276               type : Point
277               data : QGeoCircle({58.900, 5.700}, -1)
278             }
279           ]
280         }
281         \endcode
282 
283     \endlist
284 
285     The \c GeometryCollection is a heterogeneous composition of other geometry
286     types. In the resulting QVariantMap, the value of the \c data member is a
287     QVariantList populated by QVariantMaps of various geometries, including the
288     GeometryCollection itself.
289 
290     For example, the following \c GeometryCollection:
291 
292     \code
293     {
294         "type" : "GeometryCollection",
295         "geometries" : [
296             {
297                 "type" : "MultiPoint",
298                 "coordinates" : [
299                   [11,60], [5.5,60.3], [5.7,58.90]
300                 ]
301             },
302             {
303                 "type" : "MultiLineString",
304                 "coordinates" : [
305                   [[13.5, 43], [10.73, 59.92]],
306                   [[9.15, 45], [-3.15, 58.90]]
307                 ]
308             },
309             {
310                 "type" : "MultiPolygon",
311                 "coordinates" : [
312                     [
313                       [[17.13, 51.11],
314                        [30.54, 50.42],
315                        [26.74, 58.36],
316                        [17.13, 51.11]]
317                     ],
318                     [
319                       [[19.84, 41.33],
320                        [30.45, 49.26],
321                        [17.07, 50.10],
322                        [19.84, 41.33]]
323                     ]
324                 ]
325             }
326         ]
327     }
328     \endcode
329 
330     it is converted to a QVariantMap with the following structure:
331 
332     \code
333     {
334       type : GeometryCollection
335       data : [
336         {
337           type : MultiPolygon
338           data : [
339             {
340               type : Polygon
341               data : QGeoPolygon([{41.330, 19.840}, {49.260, 30.450}, {50.100, 17.070}, {41.330, 19.840}])
342             }
343             {
344               type : Polygon
345               data : QGeoPolygon([{51.110, 17.130}, {50.420, 30.540}, {58.360, 26.740}, {51.110, 17.130}])
346             }
347           ]
348         }
349         {
350           type : MultiLineString
351           data : [
352             {
353               type : LineString
354               data : QGeoPath([{45.000, 9.150}, {58.900, -3.150}])
355             }
356             {
357               type : LineString
358               data : QGeoPath([{43.000, 13.500}, {59.920, 10.730}])
359             }
360           ]
361         }
362         {
363           type : MultiPoint
364           data : [
365             {
366               type : Point
367               data : QGeoCircle({58.900, 5.700}, -1)
368             },
369             {
370               type : Point
371               data : QGeoCircle({60.300, 5.500}, -1)
372             },
373             {
374               type : Point
375               data : QGeoCircle({60.000, 11.000}, -1)
376             }
377           ]
378         }
379       ]
380     }
381     \endcode
382 
383     The \c Feature object, which consists of one of the previous geometries
384     together with related attributes, is structured like one of the 7 above
385     mentioned geometry types, plus a \c properties member. The value of this
386     member is a QVariantMap. The only way to distinguish a Feature from the
387     included geometry is to check if a \c properties node is present in the
388     QVariantMap.
389 
390     For example, the following \c Feature:
391 
392     \code
393     {
394         "type" : "Feature",
395         "id" : "Poly",
396         "properties" : {
397             "text" : "This is a Feature with a Polygon"
398         },
399         "geometry" : {
400             "type" : "Polygon",
401             "coordinates" : [
402                 [[17.13, 51.11],
403                  [30.54, 50.42],
404                  [26.70, 58.36],
405                  [17.13, 51.11]],
406                 [[23.46, 54.36],
407                  [20.52, 51.91],
408                  [28.25, 51.50],
409                  [26.80, 54.36],
410                  [23.46, 54.36]]
411             ]
412         }
413     }
414     \endcode
415 
416     it is converted to a QVariantMap with the following structure:
417 
418     \code
419     {
420       type : Polygon
421       data : QGeoPolygon([{51.110, 17.130}, {50.420,30.540}, {58.360, 26.700}, {51.110, 17.130}])
422       properties : {text : This is a Feature with a Polygon}
423     }
424     \endcode
425 
426     The \c FeatureCollection is a composition of Feature objects. The value of
427     the \c data member in a FeatureCollection is a QVariantList populated by
428     Feature type QVariantMaps.
429 
430     For example, the following \c FeatureCollection:
431 
432     \code
433     {
434         "type" : "FeatureCollection",
435         "features" : [
436             {
437                 "type" : "Feature",
438                 "id" : "Poly",
439                 "properties" : {
440                     "text" : "This is a Feature with a Polygon"
441                 },
442                 "geometry" : {
443                    "type" : "Polygon",
444                    "coordinates" : [
445                       [[17.13, 51.11],
446                        [30.54, 50.42],
447                        [26.70, 58.36],
448                        [17.13, 51.11]],
449                       [[23.46, 54.36],
450                        [20.52, 51.91],
451                        [28.25, 51.50],
452                        [26.80, 54.36],
453                        [23.46, 54.36]]
454                    ]
455                }
456            },
457            {
458                "type" : "Feature",
459                "id" : "MultiLine",
460                "properties" : {
461                    "text" : "This is a Feature with a MultiLineString"
462                },
463                "geometry" : {
464                   "type" : "MultiLineString",
465                   "coordinates" : [
466                     [[13.5, 43], [10.73, 59.92]],
467                     [[9.15, 45], [-3.15, 58.90]]
468                   ]
469                }
470            }
471         ]
472     }
473     \endcode
474 
475     it is converted to a QVariantMap with the following structure:
476 
477     \code
478     {
479       type : FeatureCollection
480       data : [
481         {
482           type : MultiLineString
483           data : [
484             {
485               type : LineString
486               data : QGeoPath([{45.000, 9.150}, {58.900, -3.150}])
487             }
488             {
489               type : LineString
490               data : QGeoPath([{43.000, 13.500}, {59.920, 10.730}])
491             }
492           ]
493           properties : {text : This is a Feature with a MultiLineString}
494         },
495         {
496           type : Polygon
497           data : QGeoPolygon({51.110, 17.130}, {50.420, 30.540}, {58.360, 26.700}, {51.110, 17.130})
498           properties : {text : This is a Feature with a Polygon}
499         }
500       ]
501     }
502     \endcode
503 
504     \section2 Exporting GeoJSON
505 
506     The exporter accepts the QVariantList returned by the \l {Importing GeoJSON}
507     {importer}, and returns a JSON document. The exporter is complementary to
508     the importer because it executes the inverse action.
509 
510     \section2 The toString function
511 
512     The \l toString outputs, for debugging purposes, the content of a
513     QVariantList structured like \l importGeoJson does, to a QString using a
514     prettyfied format.
515 */
516 
importPosition(const QVariant & position)517 static QGeoCoordinate importPosition(const QVariant &position)
518 {
519     QGeoCoordinate returnedCoordinates;
520     const QVariantList positionList = position.value<QVariantList>();
521     for (int i  = 0; i < positionList.size(); ++i) { // Iterating Point coordinates arrays
522         switch (i) {
523         case 0:
524             returnedCoordinates.setLongitude(positionList.at(i).toDouble());
525             break;
526         case 1:
527             returnedCoordinates.setLatitude(positionList.at(i).toDouble());
528             break;
529         case 2:
530             returnedCoordinates.setAltitude(positionList.at(i).toDouble());
531             break;
532         default:
533             break;
534         }
535     }
536     return returnedCoordinates;
537 }
538 
importArrayOfPositions(const QVariant & arrayOfPositions)539 static QList<QGeoCoordinate> importArrayOfPositions(const QVariant &arrayOfPositions)
540 {
541     QList <QGeoCoordinate> returnedCoordinates;
542     const QVariantList positionsList = arrayOfPositions.value<QVariantList>();
543     QGeoCoordinate singlePosition;
544     for (int i  = 0; i < positionsList.size(); ++i) { // Iterating the LineString coordinates nested arrays
545         singlePosition = importPosition((positionsList.at(i)));
546         returnedCoordinates.append(singlePosition); // Populating the QList of coordinates
547     }
548     return returnedCoordinates;
549 }
550 
importArrayOfArrayOfPositions(const QVariant & arrayOfArrayofPositions)551 static QList<QList<QGeoCoordinate>> importArrayOfArrayOfPositions(const QVariant &arrayOfArrayofPositions)
552 {
553     QList<QList<QGeoCoordinate>> returnedCoordinates;
554     const QVariantList positionsList = arrayOfArrayofPositions.value<QVariantList>();
555     QList<QGeoCoordinate> arrayOfPositions;
556     for (int i  = 0; i < positionsList.size(); ++i) { // Iterating the Polygon coordinates nested arrays
557         arrayOfPositions = importArrayOfPositions((positionsList.at(i)));
558         returnedCoordinates << arrayOfPositions;
559     }
560     return returnedCoordinates;
561 }
562 
importPoint(const QVariantMap & inputMap)563 static QGeoCircle importPoint(const QVariantMap &inputMap)
564 {
565     QGeoCircle returnedObject;
566     QGeoCoordinate center;
567     QVariant valueCoords = inputMap.value(QStringLiteral("coordinates"));
568     center = importPosition(valueCoords);
569     returnedObject.setCenter(center);
570     return returnedObject;
571 }
572 
importLineString(const QVariantMap & inputMap)573 static QGeoPath importLineString(const QVariantMap &inputMap)
574 {
575     QGeoPath returnedObject;
576     QList <QGeoCoordinate> coordinatesList;
577     const QVariant valueCoordinates = inputMap.value(QStringLiteral("coordinates"));
578     coordinatesList = importArrayOfPositions(valueCoordinates);
579     returnedObject.setPath(coordinatesList);
580     return returnedObject;
581 }
582 
importPolygon(const QVariantMap & inputMap)583 static QGeoPolygon importPolygon(const QVariantMap &inputMap)
584 {
585     QGeoPolygon returnedObject;
586     const QVariant valueCoordinates = inputMap.value(QStringLiteral("coordinates"));
587     QList<QList<QGeoCoordinate>> perimeters = importArrayOfArrayOfPositions(valueCoordinates);
588     for (int i  = 0; i < perimeters.size(); ++i) { // Import an array of QList<QGeocoordinates>
589         if (i == 0)
590             returnedObject.setPath(perimeters.at(i)); // External perimeter
591         else
592             returnedObject.addHole(perimeters.at(i)); // Inner perimeters
593     }
594     return returnedObject;
595 }
596 
importMultiPoint(const QVariantMap & inputMap)597 static QVariantList importMultiPoint(const QVariantMap &inputMap)
598 {
599     QVariantList returnedObject;
600     const QVariantList coordinatesList = inputMap.value(QStringLiteral("coordinates")).value<QVariantList>();
601     QVariantMap singlePointMap;
602     QGeoCircle parsedPoint;
603     for (int i  = 0; i < coordinatesList.size(); ++i) { // Iterating MultiPoint coordinates nasted arrays
604         parsedPoint.setCenter(importPosition(coordinatesList.at(i)));
605         singlePointMap.insert(QStringLiteral("type"), QStringLiteral("Point"));
606         singlePointMap.insert(QStringLiteral("data"), QVariant::fromValue(parsedPoint));
607         returnedObject.append(QVariant::fromValue(singlePointMap));
608     }
609     return returnedObject;
610 }
611 
importMultiLineString(const QVariantMap & inputMap)612 static QVariantList importMultiLineString(const QVariantMap &inputMap)
613 {
614     QVariantList returnedObject;
615     QGeoPath parsedLineString;
616     const QVariant listCoords = inputMap.value(QStringLiteral("coordinates"));
617     const QVariantList list = listCoords.value<QVariantList>();
618     QVariantMap singleLinestringMap;
619     for (int i  = 0; i < list.size(); ++i) { // Iterating the MultiLineString coordinates nasted arrays using importArrayOfPositions
620         singleLinestringMap.clear();
621         const QList <QGeoCoordinate> coordinatesList = importArrayOfPositions(list.at(i));
622         singleLinestringMap.insert(QStringLiteral("type"), QStringLiteral("LineString"));
623         parsedLineString.setPath(coordinatesList);
624         singleLinestringMap.insert(QStringLiteral("data"), QVariant::fromValue(parsedLineString));
625         returnedObject.append(QVariant::fromValue(singleLinestringMap));
626     }
627     return returnedObject;
628 }
629 
importMultiPolygon(const QVariantMap & inputMap)630 static QVariantList importMultiPolygon(const QVariantMap &inputMap)
631 {
632     QVariantList returnedObject;
633     QGeoPolygon singlePoly;
634     QVariantMap singlePolygonMap;
635     const QVariant valueCoordinates = inputMap.value(QStringLiteral("coordinates"));
636     const QVariantList list = valueCoordinates.value<QVariantList>();
637     for (int i = 0; i < list.size(); ++i) { // Iterating the MultiPolygon coordinates nasted arrays
638     singlePolygonMap.clear();
639         const QList<QList<QGeoCoordinate>> coordinatesList = importArrayOfArrayOfPositions(list.at(i));
640 
641         for (int j = 0; j < coordinatesList.size(); ++j) {
642             if (j == 0)
643                 singlePoly.setPath(coordinatesList.at(j));
644             else
645                 singlePoly.addHole(coordinatesList.at(j));
646         }
647         singlePolygonMap.insert(QStringLiteral("type"), QStringLiteral("Polygon"));
648         singlePolygonMap.insert(QStringLiteral("data"), QVariant::fromValue(singlePoly));
649         returnedObject.append(QVariant::fromValue(singlePolygonMap));
650     }
651     return returnedObject;
652 }
653 
654 static QVariantMap importGeometry(const QVariantMap &inputMap); // Function prototype for a tail recursion
655 
importGeometryCollection(const QVariantMap & inputMap)656 static QVariantList importGeometryCollection(const QVariantMap &inputMap)
657 {
658     QVariantList returnedObject;
659     const QVariant listGeometries = inputMap.value(QStringLiteral("geometries"));
660     const QVariantList list = listGeometries.value<QVariantList>(); // QVariantList of heterogeneous composition of the other geometry types
661     for (int i = 0; i < list.size(); ++i) {
662         QVariantMap geometryMap = list.at(i).value<QVariantMap>();
663         QVariantMap geoMap = importGeometry(geometryMap);
664         returnedObject.append(geoMap);
665     }
666     return returnedObject;
667 }
668 
importGeometry(const QVariantMap & inputMap)669 static QVariantMap importGeometry(const QVariantMap &inputMap)
670 {
671     QVariantMap returnedObject;
672     QString geometryTypes[] = {
673         QStringLiteral("Point"),
674         QStringLiteral("MultiPoint"),
675         QStringLiteral("LineString"),
676         QStringLiteral("MultiLineString"),
677         QStringLiteral("Polygon"),
678         QStringLiteral("MultiPolygon"),
679         QStringLiteral("GeometryCollection")
680     };
681     enum geoTypeSwitch {
682         Point,
683         MultiPoint,
684         LineString,
685         MultiLineString,
686         Polygon,
687         MultiPolygon,
688         GeometryCollection
689     };
690     for (int i = 0; i<7; ++i) {
691         if (inputMap.value(QStringLiteral("type")).value<QString>() == geometryTypes[i]) {
692             switch (i) {
693             case Point: {
694                 returnedObject.insert(QStringLiteral("type"), QStringLiteral("Point"));
695                 returnedObject.insert(QStringLiteral("data"), QVariant::fromValue(importPoint(inputMap)));
696                 break;
697             }
698             case MultiPoint: {
699                 returnedObject.insert(QStringLiteral("type"), QStringLiteral("MultiPoint"));
700                 returnedObject.insert(QStringLiteral("data"), QVariant::fromValue(importMultiPoint(inputMap)));
701                 break;
702             }
703             case LineString: {
704                 returnedObject.insert(QStringLiteral("type"), QStringLiteral("LineString"));
705                 returnedObject.insert(QStringLiteral("data"), QVariant::fromValue(importLineString(inputMap)));
706                 break;
707             }
708             case MultiLineString: {
709                 returnedObject.insert(QStringLiteral("type"), QStringLiteral("MultiLineString"));
710                 returnedObject.insert(QStringLiteral("data"), QVariant::fromValue(importMultiLineString(inputMap)));
711                 break;
712             }
713             case Polygon: {
714                 returnedObject.insert(QStringLiteral("type"), QStringLiteral("Polygon"));
715                 returnedObject.insert(QStringLiteral("data"), QVariant::fromValue(importPolygon(inputMap)));
716                 break;
717             }
718             case MultiPolygon: {
719                 returnedObject.insert(QStringLiteral("type"), QStringLiteral("MultiPolygon"));
720                 returnedObject.insert(QStringLiteral("data"), QVariant::fromValue(importMultiPolygon(inputMap)));
721                 break;
722             }
723             case GeometryCollection: {
724                 returnedObject.insert(QStringLiteral("type"), QStringLiteral("GeometryCollection"));
725                 returnedObject.insert(QStringLiteral("data"), QVariant::fromValue(importGeometryCollection(inputMap)));
726                 break;
727             }
728             default:
729                 break;
730             }
731         }
732     }
733     return returnedObject;
734 }
735 
importFeatureCollection(const QVariantMap & inputMap)736 static QVariantList importFeatureCollection(const QVariantMap &inputMap)
737 {
738     QVariantList returnedObject;
739     const QVariantList featuresList = inputMap.value(QStringLiteral("features")).value<QVariantList>();
740     for (int i = 0; i < featuresList.size(); ++i) {
741         QVariantMap inputFeatureMap = featuresList.at(i).value<QVariantMap>();
742         QVariantMap singleFeatureMap = importGeometry(inputFeatureMap.value(QStringLiteral("geometry")).value<QVariantMap>());
743         const QVariantMap importedProperties = inputFeatureMap.value(QStringLiteral("properties")).value<QVariantMap>();
744         singleFeatureMap.insert(QStringLiteral("properties"), importedProperties);
745         if (inputFeatureMap.contains(QStringLiteral("id"))) {
746             QVariant importedId = inputFeatureMap.value(QStringLiteral("id")).value<QVariant>();
747             singleFeatureMap.insert(QStringLiteral("id"), importedId);
748         }
749         returnedObject.append(singleFeatureMap);
750     }
751     return returnedObject;
752 }
753 
exportPosition(const QGeoCoordinate & obtainedCoordinates)754 static QJsonValue exportPosition(const QGeoCoordinate &obtainedCoordinates)
755 {
756     QJsonValue geoLat = obtainedCoordinates.latitude();
757     QJsonValue geoLong = obtainedCoordinates.longitude();
758     QJsonArray array = {geoLong, geoLat};
759     QJsonValue geoAlt;
760     if (!qIsNaN(obtainedCoordinates.altitude())) {
761         geoAlt = obtainedCoordinates.altitude();
762         array.append(geoAlt);
763     }
764     QJsonValue geoArray = array;
765     return geoArray;
766 }
767 
exportArrayOfPositions(const QList<QGeoCoordinate> & obtainedCoordinatesList)768 static QJsonValue exportArrayOfPositions(const QList<QGeoCoordinate> &obtainedCoordinatesList)
769 {
770     QJsonValue lineCoordinates;
771     QJsonValue multiPosition;
772     QJsonArray arrayPosition;
773     for (int i = 0; i < obtainedCoordinatesList.size(); ++i) {
774         multiPosition = exportPosition(obtainedCoordinatesList.at(i));
775         arrayPosition.append(multiPosition);
776     }
777     lineCoordinates = arrayPosition;
778     return lineCoordinates;
779 }
780 
exportArrayOfArrayOfPositions(const QList<QList<QGeoCoordinate>> & obtainedCoordinates)781 static QJsonValue exportArrayOfArrayOfPositions(const QList<QList<QGeoCoordinate>> &obtainedCoordinates)
782 {
783     QJsonValue lineCoordinates;
784     QJsonValue polyCoordinates;
785     QJsonArray arrayPath;
786     for (int i = 0; i < obtainedCoordinates.size(); ++i) {
787         lineCoordinates = exportArrayOfPositions(obtainedCoordinates.at(i));
788         arrayPath.append(lineCoordinates);
789     }
790     polyCoordinates = arrayPath;
791     return polyCoordinates;
792 }
793 
exportPoint(const QVariantMap & pointMap)794 static QJsonObject exportPoint(const QVariantMap &pointMap)
795 {
796     QJsonObject parsedPoint;
797     QGeoCircle circle = pointMap.value(QStringLiteral("data")).value<QGeoCircle>();
798     parsedPoint.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("Point")));
799     parsedPoint.insert(QStringLiteral("coordinates"), exportPosition(circle.center()));
800     return parsedPoint;
801 }
802 
exportLineString(const QVariantMap & lineStringMap)803 static QJsonObject exportLineString(const QVariantMap &lineStringMap)
804 {
805     QJsonObject parsedLineString;
806     QList <QGeoCoordinate> linestringPath = lineStringMap.value(QStringLiteral("data")).value<QGeoPath>().path();
807     parsedLineString.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("LineString")));
808     parsedLineString.insert(QStringLiteral("coordinates"), exportArrayOfPositions(linestringPath));
809     return parsedLineString;
810 }
811 
exportPolygon(const QVariantMap & polygonMap)812 static QJsonObject exportPolygon(const QVariantMap &polygonMap)
813 {
814     QVariant polygonVariant = polygonMap.value(QStringLiteral("data"));
815     QJsonObject parsedPolygon;
816     QJsonValue polyCoordinates;
817     QList<QList<QGeoCoordinate>> obtainedCoordinatesPoly;
818     QGeoPolygon parsedPoly = polygonVariant.value<QGeoPolygon>();
819     obtainedCoordinatesPoly << parsedPoly.path();
820     if (parsedPoly.holesCount()!=0)
821         for (int i = 0; i < parsedPoly.holesCount(); ++i) {
822             obtainedCoordinatesPoly << parsedPoly.holePath(i);
823         }
824     polyCoordinates = exportArrayOfArrayOfPositions(obtainedCoordinatesPoly);
825     parsedPolygon.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("Polygon")));
826     parsedPolygon.insert(QStringLiteral("coordinates"), polyCoordinates);
827     return parsedPolygon;
828 }
829 
exportMultiPoint(const QVariantMap & multiPointMap)830 static QJsonObject exportMultiPoint(const QVariantMap &multiPointMap)
831 {
832     QJsonObject parsedMultiPoint;
833     QList <QGeoCoordinate> obtainedCoordinatesMP;
834     QVariantList multiCircleVariantList = multiPointMap.value(QStringLiteral("data")).value<QVariantList>();
835     for (const QVariant &exCircleVariantMap: multiCircleVariantList) {
836         obtainedCoordinatesMP << exCircleVariantMap.value<QVariantMap>().value(QStringLiteral("data")).value<QGeoCircle>().center();
837     }
838     QJsonValue multiPosition = exportArrayOfPositions(obtainedCoordinatesMP);
839     parsedMultiPoint.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("MultiPoint")));
840     parsedMultiPoint.insert(QStringLiteral("coordinates"), multiPosition);
841     return parsedMultiPoint;
842 }
843 
exportMultiLineString(const QVariantMap & multiLineStringMap)844 static QJsonObject exportMultiLineString(const QVariantMap &multiLineStringMap)
845 {
846     QJsonObject parsedMultiLineString;
847     QList<QList<QGeoCoordinate>> extractedCoordinatesValue;
848     QVariant multiPathVariant = multiLineStringMap.value(QStringLiteral("data"));
849     QVariantList multiPathList = multiPathVariant.value<QVariantList>();
850     for (int i = 0; i < multiPathList.size(); ++i) {
851         extractedCoordinatesValue << multiPathList.at(i).value<QVariantMap>().value(QStringLiteral("data")).value<QGeoPath>().path();
852     }
853     QJsonValue exportedCoordinatesValue = exportArrayOfArrayOfPositions(extractedCoordinatesValue);
854     parsedMultiLineString.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("MultiLineString")));
855     parsedMultiLineString.insert(QStringLiteral("coordinates"), exportedCoordinatesValue);
856     return parsedMultiLineString;
857 }
858 
exportMultiPolygon(const QVariantMap & multiPolygonMap)859 static QJsonObject exportMultiPolygon(const QVariantMap &multiPolygonMap)
860 {
861     QJsonObject parsedMultiPolygon;
862     QJsonValue polyCoordinates;
863     QJsonArray parsedArrayPolygon;
864     QList<QList<QGeoCoordinate>> extractedCoordinatesValue;
865     QVariant multiPolygonVariant = multiPolygonMap.value(QStringLiteral("data"));
866     QVariantList multiPolygonList = multiPolygonVariant.value<QVariantList>();
867     int polyHoles = 0;
868     int currentHole;
869     for (int i = 0; i < multiPolygonList.size(); ++i) { // Start parsing Polygon list
870         extractedCoordinatesValue << multiPolygonList.at(i).value<QVariantMap>().value(QStringLiteral("data")).value<QGeoPolygon>().path(); // Extract external polygon path
871         polyHoles = multiPolygonList.at(i).value<QVariantMap>().value(QStringLiteral("data")).value<QGeoPolygon>().holesCount();
872         if (polyHoles) // Check if the polygon has holes
873             for (currentHole = 0 ; currentHole < polyHoles; currentHole++)
874                 extractedCoordinatesValue << multiPolygonList.at(i).value<QVariantMap>().value(QStringLiteral("data")).value<QGeoPolygon>().holePath(currentHole);
875         polyCoordinates = exportArrayOfArrayOfPositions(extractedCoordinatesValue); // Generates QJsonDocument compatible value
876         parsedArrayPolygon.append(polyCoordinates); // Adds one level of nesting in coordinates
877         extractedCoordinatesValue.clear(); // Clears the temporary polygon linear ring storage
878     }
879     QJsonValue exportedCoordinatesNodeValue = parsedArrayPolygon;
880     parsedMultiPolygon.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("MultiPolygon")));
881     parsedMultiPolygon.insert(QStringLiteral("coordinates"), exportedCoordinatesNodeValue);
882     return parsedMultiPolygon;
883 }
884 
885 static QJsonObject exportGeometry(const QVariantMap &geometryMap); // Function prototype
886 
exportGeometryCollection(const QVariantMap & geometryCollection)887 static QJsonObject exportGeometryCollection(const QVariantMap &geometryCollection)
888 {
889     QJsonObject parsed;
890     QJsonObject parsedGeometry;
891     QJsonValue valueGeometries;
892     QJsonArray parsedGeometries;
893     QVariantList geometriesList = geometryCollection.value(QStringLiteral("data")).value<QVariantList>();
894     for (int i = 0; i < geometriesList.size(); ++i) {
895         parsedGeometry = exportGeometry(geometriesList.at(i).value<QVariantMap>());
896         valueGeometries = parsedGeometry;
897         parsedGeometries.append(valueGeometries);
898     }
899     QJsonValue exportedGeometriesValue = parsedGeometries;
900     parsed.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("GeometryCollection")));
901     parsed.insert(QStringLiteral("geometries"), exportedGeometriesValue);
902     return parsed;
903 }
904 
exportGeometry(const QVariantMap & geometryMap)905 static QJsonObject exportGeometry(const QVariantMap &geometryMap)
906 {
907     QJsonObject exportedGeometry;
908     if (geometryMap.value(QStringLiteral("type")) == QStringLiteral("Point"))
909         exportedGeometry = exportPoint(geometryMap);
910     if (geometryMap.value(QStringLiteral("type")) == QStringLiteral("MultiPoint"))
911         exportedGeometry = exportMultiPoint(geometryMap);
912     if (geometryMap.value(QStringLiteral("type")) == QStringLiteral("LineString"))
913         exportedGeometry = exportLineString(geometryMap);
914     if (geometryMap.value(QStringLiteral("type")) == QStringLiteral("MultiLineString"))
915         exportedGeometry = exportMultiLineString(geometryMap);
916     if (geometryMap.value(QStringLiteral("type")) == QStringLiteral("Polygon"))
917         exportedGeometry = exportPolygon(geometryMap);
918     if (geometryMap.value(QStringLiteral("type")) == QStringLiteral("MultiPolygon"))
919         exportedGeometry = exportMultiPolygon(geometryMap);
920     if (geometryMap.value(QStringLiteral("type")) == QStringLiteral("GeometryCollection"))
921         exportedGeometry = exportGeometryCollection(geometryMap);
922     return exportedGeometry;
923 }
924 
exportFeature(const QVariantMap & featureMap)925 static QJsonObject exportFeature(const QVariantMap &featureMap)
926 {
927     QJsonObject exportedFeature;
928     QJsonValue geometryNodeValue = QJsonValue(exportGeometry(featureMap));
929     QJsonValue propertiesNodeValue = featureMap.value(QStringLiteral("properties")).value<QVariant>().toJsonValue();
930     QJsonValue idNodeValue = featureMap.value(QStringLiteral("id")).value<QVariant>().toJsonValue();
931     exportedFeature.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("Feature")));
932     exportedFeature.insert(QStringLiteral("geometry"), geometryNodeValue);
933     exportedFeature.insert(QStringLiteral("properties"), propertiesNodeValue);
934     exportedFeature.insert(QStringLiteral("id"), idNodeValue);
935     return exportedFeature;
936 }
937 
exportFeatureCollection(const QVariantMap & featureCollection)938 static QJsonObject exportFeatureCollection(const QVariantMap &featureCollection)
939 {
940     QJsonObject exportedFeatureCollection;
941     QJsonArray featureArray;
942     QVariantList featureList  = featureCollection.value(QStringLiteral("data")).value<QVariantList>();
943     for (int i = 0; i < featureList.size(); ++i) {
944         featureArray.append(QJsonValue(exportFeature(featureList.at(i).value<QVariantMap>())));
945     }
946     exportedFeatureCollection.insert(QStringLiteral("type"), QJsonValue(QStringLiteral("FeatureCollection")));
947     exportedFeatureCollection.insert(QStringLiteral("features"), QJsonValue(featureArray) );
948     return exportedFeatureCollection;
949 }
950 
951 /*!
952 This method imports the \a geoJson document, expected to contain valid GeoJSON
953 data, into a QVariantList structured like described in the section \l
954 {Importing GeoJSON}.
955 
956 \note This method performs no validation on the input.
957 
958 \sa exportGeoJson
959 */
importGeoJson(const QJsonDocument & geoJson)960 QVariantList QGeoJson::importGeoJson(const QJsonDocument &geoJson)
961 {
962     QVariantList returnedList;
963     QJsonObject object = geoJson.object(); // Read json object from imported doc
964     QVariantMap rootGeoJsonObject = object.toVariantMap(); // Extraced map using Qt's API
965     QString geoType[] = {
966         QStringLiteral("Point"),
967         QStringLiteral("MultiPoint"),
968         QStringLiteral("LineString"),
969         QStringLiteral("MultiLineString"),
970         QStringLiteral("Polygon"),
971         QStringLiteral("MultiPolygon"),
972         QStringLiteral("GeometryCollection"),
973         QStringLiteral("Feature"),
974         QStringLiteral("FeatureCollection")
975     };
976     enum geoTypeSwitch {
977         Point,
978         MultiPoint,
979         LineString,
980         MultiLineString,
981         Polygon,
982         MultiPolygon,
983         GeometryCollection,
984         Feature,
985         FeatureCollection
986     };
987     QVariantMap parsedGeoJsonMap;
988 
989     // Checking whether the JSON object has a "type" member
990     const QVariant keyVariant = rootGeoJsonObject.value(QStringLiteral("type"));
991     if (keyVariant == QVariant::Invalid) {
992         // Type check failed
993     }
994     QString valueType = keyVariant.value<QString>();
995 
996     // Checking whether the "type" member has a GeoJSON admitted value
997     for (int i = 0; i < 9; ++i) {
998         if (valueType == geoType[i]) {
999             switch (i) {
1000             case Point: {
1001                 QGeoCircle circle = importPoint(rootGeoJsonObject);
1002                 QVariant dataNodeValue = QVariant::fromValue(circle);
1003                 parsedGeoJsonMap.insert(QStringLiteral("type"), QStringLiteral("Point"));
1004                 parsedGeoJsonMap.insert(QStringLiteral("data"), dataNodeValue);
1005                 break;
1006             }
1007             case MultiPoint: {
1008                 QVariantList multiCircle = importMultiPoint(rootGeoJsonObject);
1009                 QVariant dataNodeValue = QVariant::fromValue(multiCircle);
1010                 QList <QGeoCircle> testlist;
1011                 parsedGeoJsonMap.insert(QStringLiteral("type"), QStringLiteral("MultiPoint"));
1012                 parsedGeoJsonMap.insert(QStringLiteral("data"), dataNodeValue);
1013                 break;
1014             }
1015             case LineString: {
1016                 QGeoPath lineString = importLineString(rootGeoJsonObject);
1017                 QVariant dataNodeValue = QVariant::fromValue(lineString);
1018                 parsedGeoJsonMap.insert(QStringLiteral("type"), QStringLiteral("LineString"));
1019                 parsedGeoJsonMap.insert(QStringLiteral("data"), dataNodeValue);
1020                 break;
1021             }
1022             case MultiLineString: {
1023                 QVariantList multiLineString = importMultiLineString(rootGeoJsonObject);
1024                 QVariant dataNodeValue = QVariant::fromValue(multiLineString);
1025                 parsedGeoJsonMap.insert(QStringLiteral("type"), QStringLiteral("MultiLineString"));
1026                 parsedGeoJsonMap.insert(QStringLiteral("data"), dataNodeValue);
1027                 break;
1028             }
1029             case Polygon: {
1030                 QGeoPolygon poly = importPolygon(rootGeoJsonObject);
1031                 QVariant dataNodeValue = QVariant::fromValue(poly);
1032                 parsedGeoJsonMap.insert(QStringLiteral("type"), QStringLiteral("Polygon"));
1033                 parsedGeoJsonMap.insert(QStringLiteral("data"), dataNodeValue);
1034                 break;
1035             }
1036             case MultiPolygon: {
1037                 QVariantList multiPoly = importMultiPolygon(rootGeoJsonObject);
1038                 QVariant dataNodeValue = QVariant::fromValue(multiPoly);
1039                 parsedGeoJsonMap.insert(QStringLiteral("type"), QStringLiteral("MultiPolygon"));
1040                 parsedGeoJsonMap.insert(QStringLiteral("data"), dataNodeValue);
1041                 break;
1042             }
1043             // List of GeoJson geometry objects
1044             case GeometryCollection: {
1045                 QVariantList multiGeo = importGeometryCollection(rootGeoJsonObject);
1046                 QVariant dataNodeValue = QVariant::fromValue(multiGeo);
1047                 parsedGeoJsonMap.insert(QStringLiteral("type"), QStringLiteral("GeometryCollection"));
1048                 parsedGeoJsonMap.insert(QStringLiteral("data"), dataNodeValue);
1049                 break;
1050             }
1051             // Single GeoJson geometry object with properties
1052             case Feature: {
1053                 parsedGeoJsonMap = importGeometry(rootGeoJsonObject.value(QStringLiteral("geometry")).value<QVariantMap>());
1054                 QVariantMap importedProperties = rootGeoJsonObject.value(QStringLiteral("properties")).value<QVariantMap>();
1055                 parsedGeoJsonMap.insert(QStringLiteral("properties"), importedProperties);
1056                 if (rootGeoJsonObject.contains(QStringLiteral("id"))){
1057                     QVariant importedId = rootGeoJsonObject.value(QStringLiteral("id")).value<QVariant>();
1058                     parsedGeoJsonMap.insert(QStringLiteral("id"), importedId);
1059                 }
1060                 break;
1061             }
1062             // Heterogeneous list of GeoJSON geometries with properties
1063             case FeatureCollection: {
1064                 QVariantList featCollection = importFeatureCollection(rootGeoJsonObject);
1065                 QVariant dataNodeValue = QVariant::fromValue(featCollection);
1066                 parsedGeoJsonMap.insert(QStringLiteral("type"), QStringLiteral("FeatureCollection"));
1067                 parsedGeoJsonMap.insert(QStringLiteral("data"), dataNodeValue);
1068                 break;
1069             }
1070             default:
1071                 break;
1072             }
1073             QVariant bboxNodeValue = rootGeoJsonObject.value(QStringLiteral("bbox"));
1074             if (bboxNodeValue != QVariant::Invalid) {
1075                 parsedGeoJsonMap.insert(QStringLiteral("bbox"), bboxNodeValue);
1076             }
1077             returnedList.append(parsedGeoJsonMap);
1078         } else if (i >= 9) {
1079             // Error
1080             break;
1081         }
1082     }
1083     return returnedList;
1084 }
1085 
1086 /*!
1087 This method exports the QVariantList \a geoData, expected to be structured like
1088 described in the section \l {Importing GeoJSON}, to a QJsonDocument containing
1089 the data converted to GeoJSON.
1090 
1091 \note This method performs no validation on the input.
1092 
1093 \sa importGeoJson
1094 */
exportGeoJson(const QVariantList & geoData)1095 QJsonDocument QGeoJson::exportGeoJson(const QVariantList &geoData)
1096 {
1097     QVariantMap exportMap = geoData.at(0).value<QVariantMap>(); // Extracting the QVMap
1098     QJsonObject newObject;
1099     QJsonDocument newDocument;
1100     if (exportMap.contains(QStringLiteral("properties"))) {
1101         newObject = exportFeature(exportMap);
1102     } else {
1103         if (exportMap.value(QStringLiteral("type")) == QStringLiteral("Point")) // Check the value corresponding to the key "Point"
1104             newObject = exportPoint(exportMap);
1105         if (exportMap.value(QStringLiteral("type")) == QStringLiteral("MultiPoint"))
1106             newObject = exportMultiPoint(exportMap);
1107         if (exportMap.value(QStringLiteral("type")) == QStringLiteral("LineString"))
1108             newObject = exportLineString(exportMap);
1109         if (exportMap.value(QStringLiteral("type")) == QStringLiteral("MultiLineString"))
1110             newObject = exportMultiLineString(exportMap);
1111         if (exportMap.value(QStringLiteral("type")) == QStringLiteral("Polygon"))
1112             newObject = exportPolygon(exportMap);
1113         if (exportMap.value(QStringLiteral("type")) == QStringLiteral("MultiPolygon"))
1114             newObject = exportMultiPolygon(exportMap);
1115         if (exportMap.value(QStringLiteral("type")) == QStringLiteral("GeometryCollection"))
1116             newObject = exportGeometryCollection(exportMap);
1117         if (exportMap.value(QStringLiteral("type")) == QStringLiteral("FeatureCollection"))
1118             newObject = exportFeatureCollection(exportMap);
1119     }
1120     if (exportMap.contains((QStringLiteral("bbox")))) {
1121         QJsonArray bboxArray;
1122         QVariantList bboxList = exportMap.value(QStringLiteral("bbox")).value<QVariantList>();
1123         for (int i = 0; i < bboxList.size(); ++i) {
1124             bboxArray.append(QJsonValue(bboxList.at(i).value<double>()));
1125         }
1126         newObject.insert(QStringLiteral("bbox"), QJsonValue(bboxArray));
1127     }
1128     newDocument.setObject(newObject);
1129     return newDocument;
1130 }
1131 
1132 // Functions for toString
operator <<(QTextStream & stream,const QGeoCoordinate & crd)1133 QTextStream &operator << (QTextStream &stream, const QGeoCoordinate &crd)
1134 {
1135     stream << "{ " << QString::number(crd.latitude(), 'f', 3) << ", "
1136                    << QString::number(crd.longitude(), 'f', 3) << ", "
1137                    << QString::number(crd.altitude(), 'f', 3) << " }";
1138     return stream;
1139 }
1140 
operator <<(QTextStream & stream,const QGeoShape & shape)1141 QTextStream &operator << (QTextStream &stream, const QGeoShape &shape)
1142 {
1143     switch (shape.type()) {
1144     case QGeoShape::CircleType: {
1145             QGeoCircle circle(shape);
1146             stream << "QGeoCircle(" <<circle.center() << ", "<< QString::number(circle.radius()) << ")";
1147             break;
1148         }
1149     case QGeoShape::PathType: {
1150             QGeoPath path(shape);
1151             stream << "QGeoPath(";
1152             for (auto c: path.path())
1153                 stream << c << ", ";
1154             stream << ")";
1155             break;
1156         }
1157     case QGeoShape::PolygonType: {
1158             QGeoPolygon poly(shape);
1159             stream << "QGeoPolygon(";
1160             for (auto c: poly.path())
1161                 stream << c << ", ";
1162             stream << ")";
1163             break;
1164         }
1165     default:
1166         stream << "QGeoShape(Unknown)";
1167         break;
1168     }
1169     return stream;
1170 }
1171 
1172 static const QString sTab = QStringLiteral("  ");
1173 
printQvariant(const QVariant v,int tabs=0)1174 QString printQvariant(const QVariant v, int tabs = 0) {
1175     QString sTabs;
1176     QString res;
1177     QTextStream stream(&res);
1178     for (int i  = 0; i< tabs; i++) {
1179         sTabs += sTab;
1180     }
1181     if (v.type() == QVariant::List) {
1182         stream << sTabs << "[\n";
1183         const QVariantList &l = v.toList();
1184         for (int i  = 0; i < l.size(); ++i)
1185             stream << printQvariant(l.at(i), tabs + 1);
1186         stream << sTabs << "]\n";
1187     } else if (v.type() == QVariant::Map) {
1188         stream  << sTabs << "{\n";
1189         const QVariantList &l = v.toList();
1190         const QVariantMap &map = v.toMap();
1191 
1192         // Either one or the other are valid
1193         if (!map.keys().isEmpty()) {
1194             // Handle type first, to easy reading
1195             if (map.contains(QStringLiteral("type"))) {
1196                 stream  << sTabs << sTab << QStringLiteral("type") << " : "
1197                         << printQvariant(map[QStringLiteral("type")], tabs + 1).remove(QRegExp(QStringLiteral("^[ ]*")));;
1198             }
1199             for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) {
1200                 if (iter.key() == QStringLiteral("type"))
1201                     continue;
1202                 stream  << sTabs << sTab << iter.key() << " : " << printQvariant(iter.value(), tabs + 1).remove(QRegExp(QStringLiteral("^[ ]*")));;
1203             }
1204         }
1205         for (int i  = 0; i < l.size(); ++i)
1206             stream << printQvariant(l.at(i), tabs + 1);
1207         stream << sTabs << "}\n";
1208     } else {
1209         stream << sTabs;
1210         QGeoShape workigGeometry;
1211         if ( v.canConvert<QGeoShape>()) {
1212             workigGeometry = v.value<QGeoShape>();
1213             if (workigGeometry.type() == QGeoShape::CircleType) {
1214                 QGeoCircle circle = v.value<QGeoCircle>();
1215                 stream << circle<< "\n";
1216             } else if (workigGeometry.type() == QGeoShape::PathType) {
1217                 QGeoPath path = v.value<QGeoPath>();
1218                 stream << path<< "\n";
1219             } else if (workigGeometry.type() == QGeoShape::PolygonType) {
1220                 QGeoPolygon polygon = v.value<QGeoPolygon>();
1221                 stream << polygon<< "\n";
1222             }
1223         } else {
1224             if (v.isNull())
1225                 stream << "null\n";
1226             else
1227                 stream << v.toString() << "\n";
1228         }
1229     }
1230     return res;
1231 }
1232 
1233 /*!
1234   This method accepts the QVariantList \a geoData, structured as described in
1235   \l {Importing GeoJSON}, and returns a string containing the same data in a
1236   readable form.
1237 */
toString(const QVariantList & geoData)1238 QString QGeoJson::toString(const QVariantList &geoData) {
1239     return printQvariant(geoData.first(), 0);
1240 }
1241 
1242 QT_END_NAMESPACE
1243