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 | |
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 | |
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(position: (positionsList.at(i))); |
546 | returnedCoordinates.append(t: singlePosition); // Populating the QList of coordinates |
547 | } |
548 | return returnedCoordinates; |
549 | } |
550 | |
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(arrayOfPositions: (positionsList.at(i))); |
558 | returnedCoordinates << arrayOfPositions; |
559 | } |
560 | return returnedCoordinates; |
561 | } |
562 | |
563 | static QGeoCircle importPoint(const QVariantMap &inputMap) |
564 | { |
565 | QGeoCircle returnedObject; |
566 | QGeoCoordinate center; |
567 | QVariant valueCoords = inputMap.value(QStringLiteral("coordinates" )); |
568 | center = importPosition(position: valueCoords); |
569 | returnedObject.setCenter(center); |
570 | return returnedObject; |
571 | } |
572 | |
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(arrayOfPositions: valueCoordinates); |
579 | returnedObject.setPath(coordinatesList); |
580 | return returnedObject; |
581 | } |
582 | |
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(arrayOfArrayofPositions: 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(holePath: perimeters.at(i)); // Inner perimeters |
593 | } |
594 | return returnedObject; |
595 | } |
596 | |
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(position: coordinatesList.at(i))); |
605 | singlePointMap.insert(QStringLiteral("type" ), QStringLiteral("Point" )); |
606 | singlePointMap.insert(QStringLiteral("data" ), avalue: QVariant::fromValue(value: parsedPoint)); |
607 | returnedObject.append(t: QVariant::fromValue(value: singlePointMap)); |
608 | } |
609 | return returnedObject; |
610 | } |
611 | |
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(arrayOfPositions: list.at(i)); |
622 | singleLinestringMap.insert(QStringLiteral("type" ), QStringLiteral("LineString" )); |
623 | parsedLineString.setPath(coordinatesList); |
624 | singleLinestringMap.insert(QStringLiteral("data" ), avalue: QVariant::fromValue(value: parsedLineString)); |
625 | returnedObject.append(t: QVariant::fromValue(value: singleLinestringMap)); |
626 | } |
627 | return returnedObject; |
628 | } |
629 | |
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(arrayOfArrayofPositions: list.at(i)); |
640 | |
641 | for (int j = 0; j < coordinatesList.size(); ++j) { |
642 | if (j == 0) |
643 | singlePoly.setPath(coordinatesList.at(i: j)); |
644 | else |
645 | singlePoly.addHole(holePath: coordinatesList.at(i: j)); |
646 | } |
647 | singlePolygonMap.insert(QStringLiteral("type" ), QStringLiteral("Polygon" )); |
648 | singlePolygonMap.insert(QStringLiteral("data" ), avalue: QVariant::fromValue(value: singlePoly)); |
649 | returnedObject.append(t: QVariant::fromValue(value: singlePolygonMap)); |
650 | } |
651 | return returnedObject; |
652 | } |
653 | |
654 | static QVariantMap importGeometry(const QVariantMap &inputMap); // Function prototype for a tail recursion |
655 | |
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(inputMap: geometryMap); |
664 | returnedObject.append(t: geoMap); |
665 | } |
666 | return returnedObject; |
667 | } |
668 | |
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" ), avalue: QVariant::fromValue(value: importPoint(inputMap))); |
696 | break; |
697 | } |
698 | case MultiPoint: { |
699 | returnedObject.insert(QStringLiteral("type" ), QStringLiteral("MultiPoint" )); |
700 | returnedObject.insert(QStringLiteral("data" ), avalue: QVariant::fromValue(value: importMultiPoint(inputMap))); |
701 | break; |
702 | } |
703 | case LineString: { |
704 | returnedObject.insert(QStringLiteral("type" ), QStringLiteral("LineString" )); |
705 | returnedObject.insert(QStringLiteral("data" ), avalue: QVariant::fromValue(value: importLineString(inputMap))); |
706 | break; |
707 | } |
708 | case MultiLineString: { |
709 | returnedObject.insert(QStringLiteral("type" ), QStringLiteral("MultiLineString" )); |
710 | returnedObject.insert(QStringLiteral("data" ), avalue: QVariant::fromValue(value: importMultiLineString(inputMap))); |
711 | break; |
712 | } |
713 | case Polygon: { |
714 | returnedObject.insert(QStringLiteral("type" ), QStringLiteral("Polygon" )); |
715 | returnedObject.insert(QStringLiteral("data" ), avalue: QVariant::fromValue(value: importPolygon(inputMap))); |
716 | break; |
717 | } |
718 | case MultiPolygon: { |
719 | returnedObject.insert(QStringLiteral("type" ), QStringLiteral("MultiPolygon" )); |
720 | returnedObject.insert(QStringLiteral("data" ), avalue: QVariant::fromValue(value: importMultiPolygon(inputMap))); |
721 | break; |
722 | } |
723 | case GeometryCollection: { |
724 | returnedObject.insert(QStringLiteral("type" ), QStringLiteral("GeometryCollection" )); |
725 | returnedObject.insert(QStringLiteral("data" ), avalue: QVariant::fromValue(value: importGeometryCollection(inputMap))); |
726 | break; |
727 | } |
728 | default: |
729 | break; |
730 | } |
731 | } |
732 | } |
733 | return returnedObject; |
734 | } |
735 | |
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(inputMap: inputFeatureMap.value(QStringLiteral("geometry" )).value<QVariantMap>()); |
743 | const QVariantMap importedProperties = inputFeatureMap.value(QStringLiteral("properties" )).value<QVariantMap>(); |
744 | singleFeatureMap.insert(QStringLiteral("properties" ), avalue: importedProperties); |
745 | if (inputFeatureMap.contains(QStringLiteral("id" ))) { |
746 | QVariant importedId = inputFeatureMap.value(QStringLiteral("id" )).value<QVariant>(); |
747 | singleFeatureMap.insert(QStringLiteral("id" ), avalue: importedId); |
748 | } |
749 | returnedObject.append(t: singleFeatureMap); |
750 | } |
751 | return returnedObject; |
752 | } |
753 | |
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(d: obtainedCoordinates.altitude())) { |
761 | geoAlt = obtainedCoordinates.altitude(); |
762 | array.append(value: geoAlt); |
763 | } |
764 | QJsonValue geoArray = array; |
765 | return geoArray; |
766 | } |
767 | |
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(obtainedCoordinates: obtainedCoordinatesList.at(i)); |
775 | arrayPosition.append(value: multiPosition); |
776 | } |
777 | lineCoordinates = arrayPosition; |
778 | return lineCoordinates; |
779 | } |
780 | |
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(obtainedCoordinatesList: obtainedCoordinates.at(i)); |
788 | arrayPath.append(value: lineCoordinates); |
789 | } |
790 | polyCoordinates = arrayPath; |
791 | return polyCoordinates; |
792 | } |
793 | |
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" ), value: QJsonValue(QStringLiteral("Point" ))); |
799 | parsedPoint.insert(QStringLiteral("coordinates" ), value: exportPosition(obtainedCoordinates: circle.center())); |
800 | return parsedPoint; |
801 | } |
802 | |
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" ), value: QJsonValue(QStringLiteral("LineString" ))); |
808 | parsedLineString.insert(QStringLiteral("coordinates" ), value: exportArrayOfPositions(obtainedCoordinatesList: linestringPath)); |
809 | return parsedLineString; |
810 | } |
811 | |
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(index: i); |
823 | } |
824 | polyCoordinates = exportArrayOfArrayOfPositions(obtainedCoordinates: obtainedCoordinatesPoly); |
825 | parsedPolygon.insert(QStringLiteral("type" ), value: QJsonValue(QStringLiteral("Polygon" ))); |
826 | parsedPolygon.insert(QStringLiteral("coordinates" ), value: polyCoordinates); |
827 | return parsedPolygon; |
828 | } |
829 | |
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(obtainedCoordinatesList: obtainedCoordinatesMP); |
839 | parsedMultiPoint.insert(QStringLiteral("type" ), value: QJsonValue(QStringLiteral("MultiPoint" ))); |
840 | parsedMultiPoint.insert(QStringLiteral("coordinates" ), value: multiPosition); |
841 | return parsedMultiPoint; |
842 | } |
843 | |
844 | static QJsonObject exportMultiLineString(const QVariantMap &multiLineStringMap) |
845 | { |
846 | QJsonObject parsedMultiLineString; |
847 | QList<QList<QGeoCoordinate>> ; |
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(obtainedCoordinates: extractedCoordinatesValue); |
854 | parsedMultiLineString.insert(QStringLiteral("type" ), value: QJsonValue(QStringLiteral("MultiLineString" ))); |
855 | parsedMultiLineString.insert(QStringLiteral("coordinates" ), value: exportedCoordinatesValue); |
856 | return parsedMultiLineString; |
857 | } |
858 | |
859 | static QJsonObject exportMultiPolygon(const QVariantMap &multiPolygonMap) |
860 | { |
861 | QJsonObject parsedMultiPolygon; |
862 | QJsonValue polyCoordinates; |
863 | QJsonArray parsedArrayPolygon; |
864 | QList<QList<QGeoCoordinate>> ; |
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(index: currentHole); |
875 | polyCoordinates = exportArrayOfArrayOfPositions(obtainedCoordinates: extractedCoordinatesValue); // Generates QJsonDocument compatible value |
876 | parsedArrayPolygon.append(value: 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" ), value: QJsonValue(QStringLiteral("MultiPolygon" ))); |
881 | parsedMultiPolygon.insert(QStringLiteral("coordinates" ), value: exportedCoordinatesNodeValue); |
882 | return parsedMultiPolygon; |
883 | } |
884 | |
885 | static QJsonObject exportGeometry(const QVariantMap &geometryMap); // Function prototype |
886 | |
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(geometryMap: geometriesList.at(i).value<QVariantMap>()); |
896 | valueGeometries = parsedGeometry; |
897 | parsedGeometries.append(value: valueGeometries); |
898 | } |
899 | QJsonValue exportedGeometriesValue = parsedGeometries; |
900 | parsed.insert(QStringLiteral("type" ), value: QJsonValue(QStringLiteral("GeometryCollection" ))); |
901 | parsed.insert(QStringLiteral("geometries" ), value: exportedGeometriesValue); |
902 | return parsed; |
903 | } |
904 | |
905 | static QJsonObject exportGeometry(const QVariantMap &geometryMap) |
906 | { |
907 | QJsonObject exportedGeometry; |
908 | if (geometryMap.value(QStringLiteral("type" )) == QStringLiteral("Point" )) |
909 | exportedGeometry = exportPoint(pointMap: geometryMap); |
910 | if (geometryMap.value(QStringLiteral("type" )) == QStringLiteral("MultiPoint" )) |
911 | exportedGeometry = exportMultiPoint(multiPointMap: geometryMap); |
912 | if (geometryMap.value(QStringLiteral("type" )) == QStringLiteral("LineString" )) |
913 | exportedGeometry = exportLineString(lineStringMap: geometryMap); |
914 | if (geometryMap.value(QStringLiteral("type" )) == QStringLiteral("MultiLineString" )) |
915 | exportedGeometry = exportMultiLineString(multiLineStringMap: geometryMap); |
916 | if (geometryMap.value(QStringLiteral("type" )) == QStringLiteral("Polygon" )) |
917 | exportedGeometry = exportPolygon(polygonMap: geometryMap); |
918 | if (geometryMap.value(QStringLiteral("type" )) == QStringLiteral("MultiPolygon" )) |
919 | exportedGeometry = exportMultiPolygon(multiPolygonMap: geometryMap); |
920 | if (geometryMap.value(QStringLiteral("type" )) == QStringLiteral("GeometryCollection" )) |
921 | exportedGeometry = exportGeometryCollection(geometryCollection: geometryMap); |
922 | return exportedGeometry; |
923 | } |
924 | |
925 | static QJsonObject exportFeature(const QVariantMap &featureMap) |
926 | { |
927 | QJsonObject exportedFeature; |
928 | QJsonValue geometryNodeValue = QJsonValue(exportGeometry(geometryMap: 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" ), value: QJsonValue(QStringLiteral("Feature" ))); |
932 | exportedFeature.insert(QStringLiteral("geometry" ), value: geometryNodeValue); |
933 | exportedFeature.insert(QStringLiteral("properties" ), value: propertiesNodeValue); |
934 | exportedFeature.insert(QStringLiteral("id" ), value: idNodeValue); |
935 | return exportedFeature; |
936 | } |
937 | |
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(value: QJsonValue(exportFeature(featureMap: featureList.at(i).value<QVariantMap>()))); |
945 | } |
946 | exportedFeatureCollection.insert(QStringLiteral("type" ), value: QJsonValue(QStringLiteral("FeatureCollection" ))); |
947 | exportedFeatureCollection.insert(QStringLiteral("features" ), value: 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 | */ |
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(inputMap: rootGeoJsonObject); |
1002 | QVariant dataNodeValue = QVariant::fromValue(value: circle); |
1003 | parsedGeoJsonMap.insert(QStringLiteral("type" ), QStringLiteral("Point" )); |
1004 | parsedGeoJsonMap.insert(QStringLiteral("data" ), avalue: dataNodeValue); |
1005 | break; |
1006 | } |
1007 | case MultiPoint: { |
1008 | QVariantList multiCircle = importMultiPoint(inputMap: rootGeoJsonObject); |
1009 | QVariant dataNodeValue = QVariant::fromValue(value: multiCircle); |
1010 | QList <QGeoCircle> testlist; |
1011 | parsedGeoJsonMap.insert(QStringLiteral("type" ), QStringLiteral("MultiPoint" )); |
1012 | parsedGeoJsonMap.insert(QStringLiteral("data" ), avalue: dataNodeValue); |
1013 | break; |
1014 | } |
1015 | case LineString: { |
1016 | QGeoPath lineString = importLineString(inputMap: rootGeoJsonObject); |
1017 | QVariant dataNodeValue = QVariant::fromValue(value: lineString); |
1018 | parsedGeoJsonMap.insert(QStringLiteral("type" ), QStringLiteral("LineString" )); |
1019 | parsedGeoJsonMap.insert(QStringLiteral("data" ), avalue: dataNodeValue); |
1020 | break; |
1021 | } |
1022 | case MultiLineString: { |
1023 | QVariantList multiLineString = importMultiLineString(inputMap: rootGeoJsonObject); |
1024 | QVariant dataNodeValue = QVariant::fromValue(value: multiLineString); |
1025 | parsedGeoJsonMap.insert(QStringLiteral("type" ), QStringLiteral("MultiLineString" )); |
1026 | parsedGeoJsonMap.insert(QStringLiteral("data" ), avalue: dataNodeValue); |
1027 | break; |
1028 | } |
1029 | case Polygon: { |
1030 | QGeoPolygon poly = importPolygon(inputMap: rootGeoJsonObject); |
1031 | QVariant dataNodeValue = QVariant::fromValue(value: poly); |
1032 | parsedGeoJsonMap.insert(QStringLiteral("type" ), QStringLiteral("Polygon" )); |
1033 | parsedGeoJsonMap.insert(QStringLiteral("data" ), avalue: dataNodeValue); |
1034 | break; |
1035 | } |
1036 | case MultiPolygon: { |
1037 | QVariantList multiPoly = importMultiPolygon(inputMap: rootGeoJsonObject); |
1038 | QVariant dataNodeValue = QVariant::fromValue(value: multiPoly); |
1039 | parsedGeoJsonMap.insert(QStringLiteral("type" ), QStringLiteral("MultiPolygon" )); |
1040 | parsedGeoJsonMap.insert(QStringLiteral("data" ), avalue: dataNodeValue); |
1041 | break; |
1042 | } |
1043 | // List of GeoJson geometry objects |
1044 | case GeometryCollection: { |
1045 | QVariantList multiGeo = importGeometryCollection(inputMap: rootGeoJsonObject); |
1046 | QVariant dataNodeValue = QVariant::fromValue(value: multiGeo); |
1047 | parsedGeoJsonMap.insert(QStringLiteral("type" ), QStringLiteral("GeometryCollection" )); |
1048 | parsedGeoJsonMap.insert(QStringLiteral("data" ), avalue: dataNodeValue); |
1049 | break; |
1050 | } |
1051 | // Single GeoJson geometry object with properties |
1052 | case Feature: { |
1053 | parsedGeoJsonMap = importGeometry(inputMap: rootGeoJsonObject.value(QStringLiteral("geometry" )).value<QVariantMap>()); |
1054 | QVariantMap importedProperties = rootGeoJsonObject.value(QStringLiteral("properties" )).value<QVariantMap>(); |
1055 | parsedGeoJsonMap.insert(QStringLiteral("properties" ), avalue: importedProperties); |
1056 | if (rootGeoJsonObject.contains(QStringLiteral("id" ))){ |
1057 | QVariant importedId = rootGeoJsonObject.value(QStringLiteral("id" )).value<QVariant>(); |
1058 | parsedGeoJsonMap.insert(QStringLiteral("id" ), avalue: importedId); |
1059 | } |
1060 | break; |
1061 | } |
1062 | // Heterogeneous list of GeoJSON geometries with properties |
1063 | case FeatureCollection: { |
1064 | QVariantList featCollection = importFeatureCollection(inputMap: rootGeoJsonObject); |
1065 | QVariant dataNodeValue = QVariant::fromValue(value: featCollection); |
1066 | parsedGeoJsonMap.insert(QStringLiteral("type" ), QStringLiteral("FeatureCollection" )); |
1067 | parsedGeoJsonMap.insert(QStringLiteral("data" ), avalue: 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" ), avalue: bboxNodeValue); |
1076 | } |
1077 | returnedList.append(t: 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 | */ |
1095 | QJsonDocument QGeoJson::exportGeoJson(const QVariantList &geoData) |
1096 | { |
1097 | QVariantMap exportMap = geoData.at(i: 0).value<QVariantMap>(); // Extracting the QVMap |
1098 | QJsonObject newObject; |
1099 | QJsonDocument newDocument; |
1100 | if (exportMap.contains(QStringLiteral("properties" ))) { |
1101 | newObject = exportFeature(featureMap: exportMap); |
1102 | } else { |
1103 | if (exportMap.value(QStringLiteral("type" )) == QStringLiteral("Point" )) // Check the value corresponding to the key "Point" |
1104 | newObject = exportPoint(pointMap: exportMap); |
1105 | if (exportMap.value(QStringLiteral("type" )) == QStringLiteral("MultiPoint" )) |
1106 | newObject = exportMultiPoint(multiPointMap: exportMap); |
1107 | if (exportMap.value(QStringLiteral("type" )) == QStringLiteral("LineString" )) |
1108 | newObject = exportLineString(lineStringMap: exportMap); |
1109 | if (exportMap.value(QStringLiteral("type" )) == QStringLiteral("MultiLineString" )) |
1110 | newObject = exportMultiLineString(multiLineStringMap: exportMap); |
1111 | if (exportMap.value(QStringLiteral("type" )) == QStringLiteral("Polygon" )) |
1112 | newObject = exportPolygon(polygonMap: exportMap); |
1113 | if (exportMap.value(QStringLiteral("type" )) == QStringLiteral("MultiPolygon" )) |
1114 | newObject = exportMultiPolygon(multiPolygonMap: exportMap); |
1115 | if (exportMap.value(QStringLiteral("type" )) == QStringLiteral("GeometryCollection" )) |
1116 | newObject = exportGeometryCollection(geometryCollection: exportMap); |
1117 | if (exportMap.value(QStringLiteral("type" )) == QStringLiteral("FeatureCollection" )) |
1118 | newObject = exportFeatureCollection(featureCollection: exportMap); |
1119 | } |
1120 | if (exportMap.contains(akey: (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(value: QJsonValue(bboxList.at(i).value<double>())); |
1125 | } |
1126 | newObject.insert(QStringLiteral("bbox" ), value: QJsonValue(bboxArray)); |
1127 | } |
1128 | newDocument.setObject(newObject); |
1129 | return newDocument; |
1130 | } |
1131 | |
1132 | // Functions for toString |
1133 | QTextStream &operator << (QTextStream &stream, const QGeoCoordinate &crd) |
1134 | { |
1135 | stream << "{ " << QString::number(crd.latitude(), f: 'f', prec: 3) << ", " |
1136 | << QString::number(crd.longitude(), f: 'f', prec: 3) << ", " |
1137 | << QString::number(crd.altitude(), f: 'f', prec: 3) << " }" ; |
1138 | return stream; |
1139 | } |
1140 | |
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 | |
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(v: l.at(i), tabs: 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(v: map[QStringLiteral("type" )], tabs: tabs + 1).remove(rx: 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(v: iter.value(), tabs: tabs + 1).remove(rx: QRegExp(QStringLiteral("^[ ]*" )));; |
1203 | } |
1204 | } |
1205 | for (int i = 0; i < l.size(); ++i) |
1206 | stream << printQvariant(v: l.at(i), tabs: 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 | */ |
1238 | QString QGeoJson::toString(const QVariantList &geoData) { |
1239 | return printQvariant(v: geoData.first(), tabs: 0); |
1240 | } |
1241 | |
1242 | QT_END_NAMESPACE |
1243 | |