1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example geojson_viewer
\title GeoJson Viewer (C++/QML)
\ingroup qtlocation-examples
\brief The GeoJson viewer example demonstrates how to manipulate MapItems, handle user input
and I/O to and from a GeoJson file.
\image geojson_viewer.png
The example displays a map with various MapItems. The MapItems are either imported from a
GeoJson file, using the \l {QGeoJson} API of \l {QtLocation} or drawn by the user using
\l {TapHandler}{TapHandlers}.
Examples for GeoJson files can be found in the directory data within the example directory.
They are read and written using the \l {QGeoJson::importGeoJson}{importGeoJson} and
\l {QGeoJson::exportGeoJson}{exportGeoJson} functions.
To draw a MapItem, right click on an empty part of the map and select an item type of your
choice in the appearing menu. The next clicks will define the chosen item. The example allows
to draw \l {MapCircle}{MapCircles}, \l {MapRectangle}{MapRectangles}, \l {MapPolygon}{MapPolygons}
and \l {MapPolyline}{MapPolylines}. Items that are fully defined by two points, i.e. circles
and rectangles, are drawn with two clicks of the left mouse button. Items that are defined by
multiple points, i.e. polygons and polylines, are created by an arbitrary amount of left button
clicks and completed with the right mouse button. Items drawn this way are saved as points,
polygons and polylines to fit the GeoJson specification, see \l {https://geojson.org/}.
\include examples-run.qdocinc
\section1 Creating a MapView
First we create a base map on which all items can be placed on. We take advantage of a
\l {MapView} element that combines a basic \l Map with input handling (mouse wheel, drag,
etc.). The underlying \l Map can be accessed with \l {MapView::map}{map} property. If you miss
a property in \l {MapView} it can be most likely accessed with \l {MapView::map}{MapView.map}.
\snippet geojson_viewer/main.qml MapView Creation
\section1 Setting up the GeoJson Model / Display MapItems
In order to display file contents on the map we will use a design pattern known as
\l {model-view-programming}{Model/View Programming}. First we need to set up a suitable view,
in this example a \l {MapItemView} element. Its parent must be set to the underlying map of the
\l {MapView} to correctly display all items placed in it.
\snippet geojson_viewer/main.qml MapItemView
Next we need a suitable model, representing a GeoJson file. The GeoJson file is converted into
a tree of \l {QVariantMap}{QVariantMaps} and \l{QVariantList}{QVariantLists} within a C++ class
in this example.
\snippet geojson_viewer/main.cpp GeoJsoner
The class contains a member \c{model} which will be set after reading a GeoJson file.
\snippet geojson_viewer/main.cpp Conversion QVariantList
The class is made available to the QML Engine to further process the model in QML.
\snippet geojson_viewer/main.cpp QMLEngine
The \l {MapItemView::model}{model} property of the \l{MapItemView} element can then be set to
the \l{QVariant} representation of the model:
\snippet geojson_viewer/main.qml MapItemView Model
Finally we need a delegate, translating the model data into a representation of items, filling
the \l {MapItemView}. It is set to the \l {MapItemView::delegate}{delegate} property of the
\l{MapItemView}:
\snippet geojson_viewer/main.qml MapItemView Delegate
We use a \l {DelegateChooser} element defined the file \c{GeoJsonDelegate.qml} to take into
account the varying representation of different geometry types of GeoJson objects.
\snippet geojson_viewer/GeoJsonDelegate.qml DelegateChooser
Various \l {DelegateChoice}{DelegateChoices} are included, each representing a different
geometry type to be found in a GeoJson file. The property \l {DelegateChooser::role}{role}
will be matched with \l {DelegateChoice::roleValue}{DelegateChoice.roleValue} to determine the
correct delegate.
As an example, a point, described with \c {"type":"Point"} in GeoJson, is represented by a
\l {MapCircle} on the \l {MapItemView}:
\snippet geojson_viewer/GeoJsonDelegate.qml DelegateChoice Point
Properties of the \l {MapCircle}, such as \l {MapCircle::color}{color} or
\l{MapCircle::radius}{radius} are attempted to be read from the GeoJson file that is
available in form of the modelData property. However, this is not a strict standard of GeoJson
and fallback values are set for all properties.
\section1 Writing MapItems to GeoJson
To write MapItems to a GeoJson file we first will convert the Mapitems into a representation
of \l {QVariantMap}{QVariantMaps} and \l{QVariantList}{QVariantLists}. This is conducted in C++
in this example, as part of the \c {GeoJsoner} class used before:
\snippet geojson_viewer/main.cpp Conversion QVariantList From Items
\c {extractor} is a helper class that converts Mapitems into \l {QVariant} representations, for
example the \l{MapCircle}:
\snippet geojson_viewer/main.cpp Extractor Example Circle
In a second step the \l {QVariant} representation can be dumped into a file using
\l {QJsonDocument}.
\snippet geojson_viewer/main.cpp Write QVariantList to Json
The two C++ functions that are required for writing files can be called from QML, thanks to the
definition as \l {Q_INVOKABLE}:
\snippet geojson_viewer/main.qml Write File
Note that we could use \c {geoJsoner.model} instead of rebuilding the \l {QVariant}
representation. However, the latter is done for demonstration purposes here.
\section1 User Interaction with MapItems
To handle user interactions we will use \l {PointHandler}{PointHandlers}. They are especially
well suited for the task as they conform to the exact shape of the underlying item, in contrast
to \l{MouseArea}{MouseArea}, which always covers a square shape. MapItems that are imported
from a GeoJson file get their own \l {HoverHandler} and \l {TapHandler} directly in the delegate:
\snippet geojson_viewer/GeoJsonDelegate.qml Handler Point
The \l {TapHandler} is used to write some information about the item on the console when the
item is tapped. The \l {HoverHandler} is used to highlight items that lie beneath the mouse
pointer. This is implemented by describing the property
\l {MapCircle::border::color}{border.color} depending on the property / state
\l {HoverHandler::hovered}{hovered} of the \l{HoverHandler}.
\section1 Adding new Items
A combination of \l {HoverHandler} and \l {TapHandler} for the \l {MapView} allows us to
react to mouse movements and clicks by the user.
If the \l{TapHandler} emits a \l{TapHandler::singleTapped}{singleTapped} signal, we will
create or modify a new MapItem on \l{Qt::LeftButton}{LeftButton} and finish the MapItem on
\l{Qt::RightButton}{RightButton}. If there is no item to finish then the
\l{Qt::RightButton}{RightButton} will open a menu.
\snippet geojson_viewer/main.qml Taphandler Map
The \l {HoverHandler::pointChanged}{pointChanged} signal is used to temporarily update a
MapItem, giving the user a preview.
\snippet geojson_viewer/main.qml Hoverhandler Map
Mapitems are generated from prototypes that are defined in separate qml files. They are created
using the \l{Qt::createComponent}{createComponent} function and added to the map with
\l {Map::addMapItem}{addMapItem}. A reference to the new item is stored for further manipulation
by the user.
\snippet geojson_viewer/main.qml add item
Adding the item to the \l {Map} is sufficient to display the item. However, in order to
further use the item (e.g. saving it to a file), it has has to be registered with the model.
This is done after editing is finished:
\snippet geojson_viewer/main.qml finish item
The class GeoJsoner converts the new item into a \l {QVariant} representation and inserts the
respective result into the existing \l {QVariant} representation of all items:
\snippet geojson_viewer/main.cpp add item
\section1 Removing Items
To remove all items from the map, we simply reset the model to an empty \l {QVariantList}.
This is possible because we registered all new items with the model and items not added to the
mode will not be affected. This is implemented in C++,
\snippet geojson_viewer/main.cpp clear
and executed from QML
\snippet geojson_viewer/main.qml clearAllItems
*/
|