Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdeclarativegeojsondata.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 Julian Sherollari <jdotsh@gmail.com>
2// Copyright (C) 2023 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
6
7#include <QtLocation/private/qgeojson_p.h>
8
9QT_BEGIN_NAMESPACE
10
11namespace extractor
12{
13 static bool hasProperties(QQuickItem *item)
14 {
15 QVariant props = item->property("props");
16 return !props.isNull();
17 }
18
19 static QVariant toVariant(QObject *mapItem)
20 {
21 if (QDeclarativeGeoMapItemView *miv = qobject_cast<QDeclarativeGeoMapItemView *>(mapItem)) {
22 return toVariant(miv);
23 } else if (QDeclarativePolylineMapItem *polyline = qobject_cast<QDeclarativePolylineMapItem *>(mapItem)) {
24 return toVariant(polyline);
25 } else if (QDeclarativePolygonMapItem *polygon = qobject_cast<QDeclarativePolygonMapItem *>(mapItem)) {
26 return toVariant(polygon);
27 } else if (QDeclarativeCircleMapItem *circle = qobject_cast<QDeclarativeCircleMapItem *>(mapItem)) {
28 return toVariant(circle); // If GeoJSON Point type is visualized in other ways, handle those types here instead.
29 } else if (QDeclarativeRectangleMapItem *rectangle = qobject_cast<QDeclarativeRectangleMapItem *>(mapItem)) {
30 return toVariant(rectangle); // For the self-drawn rectangles. Will be exported as Polygons
31 }
32 return QVariant();
33 }
34
35 static QVariantMap toVariant(QDeclarativePolygonMapItem *mapPolygon)
36 {
37 QVariantMap ls;
38 ls[QStringLiteral("type")] = QStringLiteral("Polygon");
39 ls[QStringLiteral("data")] = QVariant::fromValue(mapPolygon->geoShape());
40 if (hasProperties(mapPolygon))
41 ls[QStringLiteral("properties")] = mapPolygon->property("props").toMap();
42 return ls;
43 }
44
45 static QVariantMap toVariant(QDeclarativePolylineMapItem *mapPolyline)
46 {
47 QVariantMap ls;
48 ls[QStringLiteral("type")] = QStringLiteral("LineString");
49 ls[QStringLiteral("data")] = QVariant::fromValue(mapPolyline->geoShape());
50 if (hasProperties(mapPolyline))
51 ls[QStringLiteral("properties")] = mapPolyline->property("props").toMap();
52 return ls;
53 }
54
55 static QVariantMap toVariant(QDeclarativeCircleMapItem *mapCircle)
56 {
57 QVariantMap pt;
58 pt[QStringLiteral("type")] = QStringLiteral("Point");
59 pt[QStringLiteral("data")] = QVariant::fromValue(mapCircle->geoShape());
60 QVariantMap propMap = mapCircle->property("props").toMap();
61 propMap[QStringLiteral("radius")] = mapCircle->radius();
62 pt[QStringLiteral("properties")] = propMap;
63 return pt;
64 }
65
66 static QVariantMap toVariant(QDeclarativeRectangleMapItem *mapRectangle)
67 {
68 QVariantMap pt;
69 pt[QStringLiteral("type")] = QStringLiteral("Polygon");
70 QGeoRectangle rectanlge = mapRectangle->geoShape();
71 QGeoPolygon poly;
72 poly.addCoordinate(rectanlge.topLeft());
73 poly.addCoordinate(rectanlge.topRight());
74 poly.addCoordinate(rectanlge.bottomRight());
75 poly.addCoordinate(rectanlge.bottomLeft());
76 pt[QStringLiteral("data")] = QVariant::fromValue(poly);
77 if (hasProperties(mapRectangle))
78 pt[QStringLiteral("properties")] = mapRectangle->property("props").toMap();
79 return pt;
80 }
81};
82
83
84/*!
85 \qmltype GeoJsonData
86 \nativetype QDeclarativeGeoJsonData
87 \inqmlmodule QtLocation
88 \ingroup qml-QtLocation5-maps
89 \since QtLocation 6.7
90
91 \brief A model to represent, load and save GeoJSON documents.
92
93
94 The GeoJsonData type reads and writes GeoJson formatted documents.
95 GeoJsonData has functions to open and save the model at the URL set
96 to the \l sourceUrl property. The loaded model is internally represented by
97 QVariant and binds to the \l model property. Use
98 \l{Models and Views in Qt Quick#View Delegates}{Delegates} to visualize the items
99 in a view.
100
101 For information about GeoJSON, visit the \l{https://geojson.org/}{GeoJSON} website.
102
103 \section1 GeoJSON Object
104
105 The GeoJSON object is a valid JSON object that represents geometry, a feature, or a
106 collection of geometries or features.
107
108 A GeoJSON object must be one of these types:
109 \list
110 \li \c Point
111 \li \c MultiPoint
112 \li \c LineString
113 \li \c MultiLineString
114 \li \c Polygon
115 \li \c MultiPolygon
116 \li \c GeometryCollection
117 \li \c Feature
118 \li \c FeatureCollection
119 \endlist
120
121 To set the type, bind the \c type member to a GeoJSON type. The \c coordinates member can
122 be a type of QGeoShape or a list, depending on the GeoJSON type. The \c Feature type has an additional
123 \c geometry and \c properties member.
124
125 A list of the geometric types and their equivalent QVariant representations:
126
127 \list
128 \li For a \c Point object, \c coordinates pairs with QGeoCircle.
129 For example:
130 \code
131 {
132 "type": "Point",
133 "coordinates": [11, 60]
134 }
135 \endcode
136 This GeoJSON object has a corresponding QVariantMap representation:
137 \code
138 {
139 type: "Point"
140 coordinates: QGeoCircle({11.000, 60.000}, -1)
141 }
142 \endcode
143
144 \li For a \c LineString object, \c coordinates pairs with QGeoPath.
145 For example:
146 \code
147 {
148 "type" : "LineString",
149 "coordinates" : [
150 [13.5, 43],
151 [10.73, 59.92]
152 ]
153 }
154 \endcode
155 This GeoJSON object has a corresponding QVariantMap representation:
156 \code
157 {
158 type : "LineString"
159 data : QGeoPath([{43.000, 13.500}, {59.920, 10.730}])
160 }
161 \endcode
162
163 \li For a \c{Polygon} object, \c coordinates member pairs with QGeoPolygon (holes are possible). The polygon is a
164 \e{linear ring}, whose final coordinate is the same as the first coordinate, thereby opening and closing
165 the ring. The \c bbox member is an optional member and is for setting the area's range, useful for concave boundaries.
166 For more information about the accepted polygon coordinates, read about the
167 \l{https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6Polygon}{Polygon} type in the GeoJson specification.
168 For example:
169 \code
170 {
171 "type": "Polygon",
172 "coordinates": [
173 [
174 [17.13, 51.11],
175 [30.54, 50.42],
176 [26.70, 58.36],
177 [17.13, 51.11]
178 ]
179 ],
180 "bbox": [50, -50, 10, -10]
181 }
182 \endcode
183 This GeoJSON object has a corresponding QVariantMap representation:
184 \code
185 {
186 type : "Polygon"
187 coordinates : QGeoPolygon([{51.110, 17.130}, {50.420,30.540}, {58.360, 26.700}, {51.110, 17.130}])
188 }
189 \endcode
190
191 \endlist
192
193 For the \c MultiPoint, \c MultiLineString, and \c MultiPolygon types, \c coordinates pairs with a QVariantList.
194 The list element is a QVariantMap containing geometry.
195
196 \list
197 \li For a \c MultiPoint object, \c coordinates pairs with a list of Point coordinates.
198 For example:
199 \code
200 {
201 "type": "MultiPoint",
202 "coordinates": [
203 [11, 60],
204 [5.5, 60.3],
205 [5.7, 58.90]
206 ]
207 }
208 \endcode
209 This GeoJSON object has a corresponding QVariantMap representation:
210 \code
211 {
212 type : "MultiPoint"
213 coordinates : [
214 {
215 type : "Point"
216 coordinates : QGeoCircle({60.000, 11.000}, -1)
217 },
218 {
219 type : "Point"
220 coordinates : QGeoCircle({60.300, 5.500}, -1)
221 },
222 {
223 type : "Point"
224 coordinates : QGeoCircle({58.900, 5.700}, -1)
225 }
226 ]
227 }
228 \endcode
229
230 \li For a \c MultiLineString object, \c coordinates pairs with a list of LineString coordinates.
231 The following GeoJSON object constructs two non-parallel lines:
232 \code
233 {
234 "type" : "MultiLineString",
235 "coordinates" : [
236 [[13.5, 43], [10.73, 59.92]],
237 [[9.15, 45], [-3.15, 58.90]]
238 ]
239 }
240 \endcode
241 This GeoJSON object has a corresponding QVariantMap representation:
242 \code
243 {
244 type : "MultiLineString"
245 coordinates : [
246 {
247 type : "LineString"
248 coordinates : QGeoPath([{45.000, 9.150}, {58.900, -3.150}])
249 },
250 {
251 type : "LineString"
252 coordinates : QGeoPath([{43.000, 13.500}, {59.920, 10.730}])
253 }
254 ]
255 }
256 \endcode
257
258 \li For a \c MultiPolygon type, \c coordinates pairs with a list of Polygon coordinates.
259 The polygon is a \e{linear ring} and the \c Polygon type has more information about accepted formats.
260 The following GeoJSON object contains a list of two triangles:
261 \code
262 {
263 "type" : "MultiPolygon",
264 "coordinates" : [
265 [
266 [[17.13, 51.11],
267 [30.54, 50.42],
268 [26.74, 58.36],
269 [17.13, 51.11]
270 ]],
271 [
272 [[19.84, 41.33],
273 [30.45, 49.26],
274 [17.07, 50.10],
275 [19.84, 41.33]
276 ]]
277 ]
278 }
279 \endcode
280 This GeoJSON object has a corresponding QVariantMap representation:
281 \code
282 {
283 type : "MultiPolygon"
284 coordinates : [
285 {
286 type : "Polygon"
287 coordinates : QGeoPolygon([{51.110, 17.130}, {50.420,30.540}, {58.360, 26.740}])
288 },
289 {
290 type : "Polygon"
291 coordinates : QGeoPolygon([{41.330, 19.840}, {49.260,30.450}, {50.100, 17.070}])
292 }
293 ]
294 }
295 \endcode
296
297 \endlist
298
299 The \c GeometryCollection type is a composition of other geometry types. The value of the
300 \c geometries member is a QVariantList containing QVariantMaps of various types,
301 including other GeometryCollection types.
302
303 For example, the following \c GeometryCollection type contains several other geometries:
304 \code
305 {
306 "type" : "GeometryCollection",
307 "geometries" : [
308 {
309 "type" : "MultiPoint",
310 "coordinates" : [
311 [11,60], [5.5,60.3], [5.7,58.90]
312 ]
313 },
314 {
315 "type" : "MultiLineString",
316 "coordinates": [
317 [[13.5, 43], [10.73, 59.92]],
318 [[9.15, 45], [-3.15, 58.90]]
319 ]
320 },
321 {
322 "type" : "MultiPolygon",
323 "coordinates" : [
324 [
325 [
326 [17.13, 51.11],
327 [30.54, 50.42],
328 [26.74, 58.36],
329 [17.13, 51.11]
330 ]
331 ],
332 [
333 [
334 [19.84, 41.33],
335 [30.45, 49.26],
336 [17.07, 50.10],
337 [19.84, 41.33]
338 ]
339 ]
340 ]
341 }
342 ]
343 }
344 \endcode
345 This GeoJSON object has a corresponding QVariantMap representation:
346 \code
347 {
348 type : "GeometryCollection"
349 coordinates : [
350 {
351 type : "MultiPolygon"
352 coordinates : [
353 {
354 type : "Polygon"
355 coordinates : QGeoPolygon([{41.330, 19.840}, {49.260, 30.450}, {50.100, 17.070}])
356 },
357 {
358 type : "Polygon"
359 coordinates : QGeoPolygon([{51.110, 17.130}, {50.420, 30.540}, {58.360, 26.740}])
360 }
361 ]
362 }
363 {
364 type : "MultiLineString"
365 coordinates : [
366 {
367 type : "LineString"
368 coordinates : QGeoPath([{45.000, 9.150}, {58.900, -3.150}])
369 },
370 {
371 type : "LineString"
372 coordinates : QGeoPath([{43.000, 13.500}, {59.920, 10.730}])
373 }
374 ]
375 }
376 {
377 type : "MultiPoint"
378 coordinates : [
379 {
380 type : Point
381 coordinates : QGeoCircle({58.900, 5.700}, -1)
382 },
383 {
384 type : Point
385 coordinates : QGeoCircle({60.300, 5.500}, -1)
386 },
387 {
388 type : Point
389 coordinates : QGeoCircle({60.000, 11.000}, -1)
390 }
391 ]
392 }
393 ]
394 }
395 \endcode
396
397 The \c Feature type contains an additional \c geometry and \c properties
398 member. The only way to distinguish a Feature object from other geometrical objects
399 is to check for the existence of a \c properties node in the QVariantMap object.
400
401 For example, the following \c Feature has a geometry and properties members:
402 \code
403 {
404 "type": "Feature",
405 "id": "Poly",
406 "properties": {
407 "name": "Poly",
408 "text": "This is a Feature with a Polygon",
409 "color": "limegreen"
410 },
411 "geometry": {
412 "type": "Polygon",
413 "coordinates": [
414 [
415 [17.13, 51.11],
416 [30.54, 50.42],
417 [26.70, 58.36],
418 [17.13, 51.11]
419 ],
420 [
421 [23.46, 54.36],
422 [20.52, 51.91],
423 [28.25, 51.50],
424 [26.80, 54.36],
425 [23.46, 54.36]
426 ]
427 ]
428 }
429 }
430 \endcode
431 This GeoJSON object has a corresponding QVariantMap representation:
432 \code
433 {
434 type : "Polygon"
435 data : QGeoPolygon([{51.110, 17.130}, {50.420,30.540}, {58.360, 26.700}, {51.110, 17.130}])
436 properties : {text : "This is a Feature with a Polygon"}
437 }
438 \endcode
439
440 The \c FeatureCollection is a composition of Feature objects. THe \c features
441 member binds to a QVariantList object containing other Feature objects.
442
443 For example, the following \c FeatureCollection has several Features and geometries:
444 \code
445 {
446 "type" : "FeatureCollection",
447 "properties" : {
448 "color" : "crimson"
449 },
450 "features" : [
451 {
452 "type" : "Feature",
453 "id" : "Poly",
454 "properties" : {
455 "text" : "This is a Feature with a Polygon"
456 },
457 "geometry" : {
458 "type" : "Polygon",
459 "coordinates" : [
460 [
461 [17.13, 51.11],
462 [30.54, 50.42],
463 [26.70, 58.36],
464 [17.13, 51.11]
465 ],
466 [
467 [23.46, 54.36],
468 [20.52, 51.91],
469 [28.25, 51.50],
470 [26.80, 54.36],
471 [23.46, 54.36]
472 ]
473 ]
474 }
475 },
476 {
477 "type" : "Feature",
478 "id" : "MultiLine",
479 "properties" : {
480 "text" : "This is a Feature with a MultiLineString",
481 "color" : "deepskyblue"
482 },
483 "geometry" : {
484 "type" : "MultiLineString",
485 "coordinates" : [
486 [[13.5, 43], [10.73, 59.92]],
487 [[9.15, 45], [-3.15, 58.90]]
488 ]
489 }
490 }
491 ]
492 }
493 \endcode
494 This GeoJSON object has a corresponding QVariantMap representation:
495 \code
496 {
497 type: "FeatureCollection"
498 data: [{
499 type: "MultiLineString"
500 data: [{
501 type: "LineString"
502 data: QGeoPath( [{45.000, 9.150}, {58.900, -3.150}] )
503 } {
504 type: "LineString"
505 data: QGeoPath( [{43.000, 13.500}, {59.920, 10.730}] )
506 }]
507 properties: { text: "This is a Feature with a MultiLineString" }
508 },
509 {
510 type: "Polygon"
511 data: QGeoPolygon( {51.110, 17.130},
512 {50.420, 30.540},
513 {58.360, 26.700},
514 {51.110, 17.130}
515 )
516 properties: { text: "This is a Feature with a Polygon" }
517 }
518 ]
519 }
520 \endcode
521
522 \section1 GeoJson Example
523
524 The \l{GeoJson Viewer (QML)}{GeoJson Viewer} example demonstrates the use of the GeoJsonData QML type to
525 load and visualize coordinates on a map.
526*/
527
528
529QDeclarativeGeoJsonData::QDeclarativeGeoJsonData(QObject *parent)
530 : QObject(parent)
531{
532
533}
534
535QDeclarativeGeoJsonData::~QDeclarativeGeoJsonData()
536{
537
538}
539
540/*!
541 \qmlproperty QVariant QtLocation::GeoJsonData::model
542
543 A QVariant representation of the GeoJSON document. QML
544 delegates can display the contents in views.
545
546 \since 6.7
547*/
548QVariant QDeclarativeGeoJsonData::model() const
549{
550 return m_content;
551}
552
553void QDeclarativeGeoJsonData::setModel(const QVariant &model)
554{
555 m_content = model;
556 emit modelChanged();
557}
558
559
560/*!
561 \qmlproperty url QtLocation::GeoJsonData::sourceUrl
562
563 The URL of a GeoJSON document. Setting this property loads the
564 document and binds the object to the \l model member.
565
566 \since 6.7
567*/
568
569
570QUrl QDeclarativeGeoJsonData::sourceUrl() const
571{
572 return m_url;
573}
574
575/*!
576 \qmlmethod void QtLocation::GeoJsonData::clear()
577
578 Deletes all items bound to the \l model.
579*/
580void QDeclarativeGeoJsonData::clear()
581{
582 m_content = QVariantList();
583 emit modelChanged();
584}
585
586/*!
587 \qmlmethod bool QtLocation::GeoJsonData::addItem(Item item)
588
589 Adds the \a item to the \l model object.
590
591 Returns \c true if adding is successful, \c false otherwise.
592*/
593void QDeclarativeGeoJsonData::addItem(QQuickItem *item)
594{
595 QVariant entry;
596 if (auto *polyline = qobject_cast<QDeclarativePolylineMapItem *>(item))
597 entry = extractor::toVariant(polyline);
598 else if (auto *polygon = qobject_cast<QDeclarativePolygonMapItem *>(item))
599 entry = extractor::toVariant(polygon);
600 else if (auto *circle = qobject_cast<QDeclarativeCircleMapItem *>(item))
601 entry = extractor::toVariant(circle);
602 else if (auto *rectangle = qobject_cast<QDeclarativeRectangleMapItem *>(item))
603 entry = extractor::toVariant(rectangle);
604 else
605 return;
606
607 QVariantList geoJson = m_content.toList();
608 if (!geoJson.isEmpty()){
609 QVariantList geoData = (geoJson[0].toMap()[QStringLiteral("type")] == QStringLiteral("FeatureCollection")) ? geoJson[0].toMap()[QStringLiteral("data")].toList() : geoJson;
610 geoData.append(entry);
611 geoJson[0] = QVariantMap{{QStringLiteral("type"), QStringLiteral("FeatureCollection")}, {QStringLiteral("data"), geoData}};
612 }
613 else {
614 geoJson.append(entry);
615 }
616 m_content = geoJson;
617 emit modelChanged();
618}
619
620/*!
621 \qmlmethod bool QtLocation::GeoJsonData::open()
622
623 Loads the content of the file at \l sourceUrl.
624
625 Returns \c true if opening is successful, \c false otherwise.
626*/
627bool QDeclarativeGeoJsonData::open()
628{
629 return openUrl(m_url);
630}
631
632/*!
633 \qmlmethod bool QtLocation::GeoJsonData::openUrl(Url url)
634
635 Loads the GeoJson document at \a url and binds it to the \l model. The property
636 \l sourceUrl is set to \a url if opening the file is successful.
637
638 Returns \c true if opening is successful, \c false otherwise.
639*/
640bool QDeclarativeGeoJsonData::openUrl(const QUrl &url)
641{
642 // Reading GeoJSON file
643 QFile loadFile(url.toLocalFile());
644 if (!loadFile.open(QIODevice::ReadOnly)) {
645 qWarning() << "Error while opening the file: " << url;
646 qWarning() << loadFile.errorString();
647 return false;
648 }
649
650 // Load the GeoJSON file using Qt's API
651 QJsonParseError err;
652 QJsonDocument loadDoc(QJsonDocument::fromJson(loadFile.readAll(), &err));
653 if (err.error) {
654 qWarning() << "Parsing while importing the JSON document:\n" << err.errorString();
655 return false;
656 }
657
658 // Import geographic data to a QVariantList
659 m_content = QGeoJson::importGeoJson(loadDoc);
660 if (m_url != url) {
661 m_url = url;
662 emit sourceUrlChanged();
663 }
664 emit modelChanged();
665 return true;
666}
667
668/*!
669 \qmlmethod bool QtLocation::GeoJsonData::saveAs(Url url)
670
671 Saves the \l model at \a url.
672 The \l sourceUrl property is set to \a url if successful.
673
674 Returns \c true if saving is successful, \c false otherwise.
675*/
676bool QDeclarativeGeoJsonData::saveAs(const QUrl &url)
677{
678 if (m_url != url){
679 m_url = url;
680 emit sourceUrlChanged();
681 }
682 return save();
683}
684
685/*!
686 \qmlmethod bool QtLocation::GeoJsonData::save()
687
688 Saves the model at \l{sourceUrl}.
689
690 Returns \c true if saving is successful, \c false otherwise.
691*/
692bool QDeclarativeGeoJsonData::save()
693{
694 return dumpGeoJSON(m_content.toList(), m_url);
695}
696
697/*!
698 \qmlmethod void QtLocation::GeoJsonData::setModelToMapContents(MapView mapItemView)
699
700 Adds all map items of \a mapItemView to the \l model of the GeoJsonData
701 object. Deletes previously stored map items from the \l model.
702
703 Returns \c true if setting is successful, \c false otherwise.
704
705 \sa addItem
706*/
707void QDeclarativeGeoJsonData::setModelToMapContents(QDeclarativeGeoMap *map)
708{
709 m_content = toVariant(map);
710 emit modelChanged();
711}
712
713/*! \internal
714 A helper function to convert the children of a map to a \l QVariantList that
715 represents the items and that can be exported to a Json file.
716
717 Returns a \l QVariantList containing all data of the mapItems.
718*/
719QVariantList QDeclarativeGeoJsonData::toVariant(QDeclarativeGeoMap *map)
720{
721 QVariantList res;
722 const QList<QObject*> mapChildren = map->children();
723 for (QObject *child : mapChildren) {
724 QVariant mapItemAsVariant = extractor::toVariant(child);
725 if (mapItemAsVariant.isValid())
726 res.append(mapItemAsVariant);
727 }
728 return res;
729}
730
731/*! \internal
732 A helper function to dump the VariantList \a geoJson into the file at \a url.
733
734 Returns \c true if the file was saved successfully, \c false otherwise.
735*/
736bool QDeclarativeGeoJsonData::dumpGeoJSON(const QVariantList &geoJson, const QUrl &url)
737{
738 QJsonDocument json = QGeoJson::exportGeoJson(geoJson);
739 QFile jsonFile(url.toLocalFile());
740 if (!jsonFile.open(QIODevice::WriteOnly))
741 return false;
742 jsonFile.write(json.toJson());
743 jsonFile.close();
744 return true;
745}
746
747/*! \internal
748 A helper function to write a human interpretable representation of
749 \a geoJson into the file at \a url.
750
751 Returns \c true if the file was saved successfully, \c false otherwise.
752*/
753bool QDeclarativeGeoJsonData::writeDebug(const QVariantList &geoJson, const QUrl &url)
754{
755 QString prettyPrint = QGeoJson::toString(geoJson);
756 QFile debugFile(url.toLocalFile());
757 if (!debugFile.open(QIODevice::WriteOnly))
758 return false;
759 debugFile.write(prettyPrint.toUtf8());
760 debugFile.close();
761 return true;
762}
763
764/*! \internal
765 A helper function to generate a human interpretable representation of
766 \a geoJson.
767
768 Returns a string with the human interpretable representation of \a geoJson.
769*/
770QString QDeclarativeGeoJsonData::toString(const QVariantList &geoJson)
771{
772 return QGeoJson::toString(geoJson);
773}
774
775QT_END_NAMESPACE
static QVariant toVariant(QObject *mapItem)
static bool hasProperties(QQuickItem *item)