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
qdeclarativegeocodemodel.cpp
Go to the documentation of this file.
1// Copyright (C) 2015 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7
8#include <QtCore/QCoreApplication>
9#include <QtQml/QQmlInfo>
10#include <QtPositioning/QGeoCircle>
11#include <QtLocation/QGeoServiceProvider>
12#include <QtLocation/QGeoCodingManager>
13#include <QtLocation/private/qgeocodereply_p.h>
14#include <QtPositioning/QGeoPolygon>
15
17
18/*!
19 \qmltype GeocodeModel
20 \nativetype QDeclarativeGeocodeModel
21 \inqmlmodule QtLocation
22 \ingroup qml-QtLocation5-geocoding
23 \since QtLocation 5.5
24
25 \brief The GeocodeModel type provides support for searching operations
26 related to geographic information.
27
28 The GeocodeModel type is used as part of a model/view grouping to
29 match addresses or search strings with geographic locations. How the
30 geographic locations generated are used or displayed is decided by any
31 Views attached to the GeocodeModel (for example a \l MapItemView or \l{ListView}).
32
33 Like \l Map and \l RouteModel, all the data for a GeocodeModel to work
34 comes from a services plugin. This is contained in the \l{plugin} property,
35 and this must be set before the GeocodeModel can do any useful work.
36
37 Once the plugin is set, the \l{query} property can be used to specify the
38 address or search string to match. If \l{autoUpdate} is enabled, the Model
39 will update its output automatically. Otherwise, the \l{update} method may
40 be used. By default, autoUpdate is disabled.
41
42 The data stored and returned in the GeocodeModel consists of \l [QML] {Location}
43 objects, as a list with the role name "locationData". See the documentation
44 for \l [QML] {Location} for further details on its structure and contents.
45
46 \section2 Example Usage
47
48 The following snippet is two-part, showing firstly the declaration of
49 objects, and secondly a short piece of procedural code using it. We set
50 the geocodeModel's \l{autoUpdate} property to false, and call \l{update} once
51 the query is set up. In this case, as we use a string value in \l{query},
52 only one update would occur, even with autoUpdate enabled. However, if we
53 provided an \l{Address} object we may inadvertently trigger multiple
54 requests whilst setting its properties.
55
56 \code
57 Plugin {
58 id: aPlugin
59 }
60
61 GeocodeModel {
62 id: geocodeModel
63 plugin: aPlugin
64 autoUpdate: false
65 }
66 \endcode
67
68 \code
69 {
70 geocodeModel.query = "53 Brandl St, Eight Mile Plains, Australia"
71 geocodeModel.update()
72 }
73 \endcode
74*/
75
76/*!
77 \qmlsignal QtLocation::GeocodeModel::locationsChanged()
78
79 This signal is emitted when locations in the model have changed.
80
81 \sa count
82*/
83
84
85QDeclarativeGeocodeModel::QDeclarativeGeocodeModel(QObject *parent)
86 : QAbstractListModel(parent)
87{
88}
89
90QDeclarativeGeocodeModel::~QDeclarativeGeocodeModel()
91{
92 qDeleteAll(declarativeLocations_);
93 declarativeLocations_.clear();
94 delete reply_;
95}
96
97/*!
98 \internal
99 From QQmlParserStatus
100*/
101void QDeclarativeGeocodeModel::componentComplete()
102{
103 complete_ = true;
104 if (autoUpdate_)
105 update();
106}
107
108/*!
109 \qmlmethod void QtLocation::GeocodeModel::update()
110
111 Instructs the GeocodeModel to update its data. This is most useful
112 when \l autoUpdate is disabled, to force a refresh when the query
113 has been changed.
114*/
115void QDeclarativeGeocodeModel::update()
116{
117 if (!complete_)
118 return;
119
120 if (!plugin_) {
121 setError(EngineNotSetError, tr("Cannot geocode, plugin not set."));
122 return;
123 }
124
125 QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider();
126 if (!serviceProvider)
127 return;
128
129 QGeoCodingManager *geocodingManager = serviceProvider->geocodingManager();
130 if (!geocodingManager) {
131 setError(EngineNotSetError, tr("Cannot geocode, geocode manager not set."));
132 return;
133 }
134 if (!coordinate_.isValid() && (!address_ || address_->address().isEmpty()) &&
135 (searchString_.isEmpty())) {
136 setError(ParseError, tr("Cannot geocode, valid query not set."));
137 return;
138 }
139 abortRequest(); // abort possible previous requests
140 setError(NoError, QString());
141
142 if (coordinate_.isValid()) {
143 setStatus(QDeclarativeGeocodeModel::Loading);
144 reply_ = geocodingManager->reverseGeocode(coordinate_, boundingArea_);
145 if (reply_->isFinished()) {
146 if (reply_->error() == QGeoCodeReply::NoError) {
147 geocodeFinished(reply_);
148 } else {
149 geocodeError(reply_, reply_->error(), reply_->errorString());
150 }
151 }
152 } else if (address_) {
153 setStatus(QDeclarativeGeocodeModel::Loading);
154 reply_ = geocodingManager->geocode(address_->address(), boundingArea_);
155 if (reply_->isFinished()) {
156 if (reply_->error() == QGeoCodeReply::NoError) {
157 geocodeFinished(reply_);
158 } else {
159 geocodeError(reply_, reply_->error(), reply_->errorString());
160 }
161 }
162 } else if (!searchString_.isEmpty()) {
163 setStatus(QDeclarativeGeocodeModel::Loading);
164 reply_ = geocodingManager->geocode(searchString_, limit_, offset_, boundingArea_);
165 if (reply_->isFinished()) {
166 if (reply_->error() == QGeoCodeReply::NoError) {
167 geocodeFinished(reply_);
168 } else {
169 geocodeError(reply_, reply_->error(), reply_->errorString());
170 }
171 }
172 }
173}
174
175/*!
176 \internal
177*/
178void QDeclarativeGeocodeModel::abortRequest()
179{
180 if (reply_) {
181 reply_->abort();
182 }
183 if (reply_) {
184 reply_->deleteLater();
185 reply_ = 0;
186 }
187}
188
189/*!
190 \internal
191*/
192void QDeclarativeGeocodeModel::queryContentChanged()
193{
194 if (autoUpdate_)
195 update();
196}
197
198/*!
199 \internal
200 From QAbstractListModel
201*/
202int QDeclarativeGeocodeModel::rowCount(const QModelIndex &parent) const
203{
204 Q_UNUSED(parent);
205 return declarativeLocations_.count();
206}
207
208/*!
209 \internal
210*/
211QVariant QDeclarativeGeocodeModel::data(const QModelIndex &index, int role) const
212{
213 if (!index.isValid())
214 return QVariant();
215 if (index.row() >= declarativeLocations_.count())
216 return QVariant();
217 if (role == QDeclarativeGeocodeModel::LocationRole) {
218 QObject *locationObject = declarativeLocations_.at(index.row());
219 Q_ASSERT(locationObject);
220 return QVariant::fromValue(locationObject);
221 }
222 return QVariant();
223}
224
225QHash<int, QByteArray> QDeclarativeGeocodeModel::roleNames() const
226{
227 QHash<int, QByteArray> roleNames = QAbstractItemModel::roleNames();
228 roleNames.insert(LocationRole, "locationData");
229 return roleNames;
230}
231
232/*!
233 \internal
234*/
235void QDeclarativeGeocodeModel::setPlugin(QDeclarativeGeoServiceProvider *plugin)
236{
237 if (plugin_ == plugin)
238 return;
239
240 reset(); // reset the model
241 plugin_ = plugin;
242 if (complete_)
243 emit pluginChanged();
244
245 if (!plugin)
246 return;
247
248 if (plugin_->isAttached()) {
249 pluginReady();
250 } else {
251 connect(plugin_, &QDeclarativeGeoServiceProvider::attached,
252 this, &QDeclarativeGeocodeModel::pluginReady);
253 }
254}
255
256/*!
257 \internal
258*/
259void QDeclarativeGeocodeModel::pluginReady()
260{
261 QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider();
262 QGeoCodingManager *geocodingManager = serviceProvider->geocodingManager();
263
264 if (serviceProvider->geocodingError() != QGeoServiceProvider::NoError) {
265 QDeclarativeGeocodeModel::GeocodeError newError = UnknownError;
266 switch (serviceProvider->geocodingError()) {
267 case QGeoServiceProvider::NotSupportedError:
268 newError = EngineNotSetError; break;
269 case QGeoServiceProvider::UnknownParameterError:
270 newError = UnknownParameterError; break;
271 case QGeoServiceProvider::MissingRequiredParameterError:
272 newError = MissingRequiredParameterError; break;
273 case QGeoServiceProvider::ConnectionError:
274 newError = CommunicationError; break;
275 default:
276 break;
277 }
278
279 setError(newError, serviceProvider->geocodingErrorString());
280 return;
281 }
282
283 if (!geocodingManager) {
284 setError(EngineNotSetError,tr("Plugin does not support (reverse) geocoding."));
285 return;
286 }
287
288 connect(geocodingManager, &QGeoCodingManager::finished,
289 this, &QDeclarativeGeocodeModel::geocodeFinished);
290 connect(geocodingManager, &QGeoCodingManager::errorOccurred,
291 this, &QDeclarativeGeocodeModel::geocodeError);
292
293 if (complete_ && autoUpdate_)
294 update();
295}
296
297/*!
298 \qmlproperty Plugin QtLocation::GeocodeModel::plugin
299
300 This property holds the plugin that provides the actual geocoding service.
301 Note that all plugins do not necessarily provide geocoding (could for example provide
302 only routing or maps).
303
304 \sa Plugin
305*/
306
307QDeclarativeGeoServiceProvider *QDeclarativeGeocodeModel::plugin() const
308{
309 return plugin_;
310}
311
312void QDeclarativeGeocodeModel::setBounds(const QVariant &boundingArea)
313{
314 QGeoShape s;
315
316 if (boundingArea.userType() == qMetaTypeId<QGeoRectangle>())
317 s = boundingArea.value<QGeoRectangle>();
318 else if (boundingArea.userType() == qMetaTypeId<QGeoCircle>())
319 s = boundingArea.value<QGeoCircle>();
320 else if (boundingArea.userType() == qMetaTypeId<QGeoShape>())
321 s = boundingArea.value<QGeoShape>();
322
323
324 if (boundingArea_ == s)
325 return;
326
327 boundingArea_ = s;
328 emit boundsChanged();
329}
330
331/*!
332 \qmlproperty geoShape QtLocation::GeocodeModel::bounds
333
334 This property holds the bounding area used to limit the results to those
335 within the area. This is particularly useful if query is only partially filled out,
336 as the service will attempt to (reverse) geocode all matches for the specified data.
337
338 Accepted types are \l {geoRectangle} and
339 \l {geoCircle}.
340*/
341QVariant QDeclarativeGeocodeModel::bounds() const
342{
343 if (boundingArea_.type() == QGeoShape::RectangleType)
344 return QVariant::fromValue(QGeoRectangle(boundingArea_));
345 else if (boundingArea_.type() == QGeoShape::CircleType)
346 return QVariant::fromValue(QGeoCircle(boundingArea_));
347 else if (boundingArea_.type() == QGeoShape::PolygonType)
348 return QVariant::fromValue(QGeoPolygon(boundingArea_));
349 else
350 return QVariant::fromValue(boundingArea_);
351}
352
353void QDeclarativeGeocodeModel::geocodeFinished(QGeoCodeReply *reply)
354{
355 if (reply != reply_ || reply->error() != QGeoCodeReply::NoError)
356 return;
357
358 reply->deleteLater();
359 reply_ = 0;
360 int oldCount = declarativeLocations_.count();
361 setLocations(reply->locations());
362 setError(NoError, QString());
363 setStatus(QDeclarativeGeocodeModel::Ready);
364 emit locationsChanged();
365 if (oldCount != declarativeLocations_.count())
366 emit countChanged();
367}
368
369/*!
370 \internal
371*/
372void QDeclarativeGeocodeModel::geocodeError(QGeoCodeReply *reply,
373 QGeoCodeReply::Error error,
374 const QString &errorString)
375{
376 if (reply != reply_)
377 return;
378
379 reply->deleteLater();
380 reply_ = 0;
381 int oldCount = declarativeLocations_.count();
382 if (oldCount > 0) {
383 // Reset the model
384 setLocations(reply->locations());
385 emit locationsChanged();
386 emit countChanged();
387 }
388 setError(static_cast<QDeclarativeGeocodeModel::GeocodeError>(error), errorString);
389 setStatus(QDeclarativeGeocodeModel::Error);
390}
391
392/*!
393 \qmlproperty enumeration QtLocation::GeocodeModel::status
394
395 This read-only property holds the current status of the model.
396
397 \list
398 \li GeocodeModel.Null - No geocode requests have been issued or \l reset has been called.
399 \li GeocodeModel.Ready - Geocode request(s) have finished successfully.
400 \li GeocodeModel.Loading - Geocode request has been issued but not yet finished
401 \li GeocodeModel.Error - Geocoding error has occurred, details are in \l error and \l errorString
402 \endlist
403*/
404
405QDeclarativeGeocodeModel::Status QDeclarativeGeocodeModel::status() const
406{
407 return status_;
408}
409
410void QDeclarativeGeocodeModel::setStatus(QDeclarativeGeocodeModel::Status status)
411{
412 if (status_ == status)
413 return;
414 status_ = status;
415 emit statusChanged();
416}
417
418/*!
419 \qmlproperty enumeration QtLocation::GeocodeModel::error
420
421 This read-only property holds the latest error value of the geocoding request.
422
423 \list
424 \li GeocodeModel.NoError - No error has occurred.
425 \li GeocodeModel.CombinationError - An error occurred while results where being combined from multiple sources.
426 \li GeocodeModel.CommunicationError - An error occurred while communicating with the service provider.
427 \li GeocodeModel.EngineNotSetError - The model's plugin property was not set or there is no geocoding manager associated with the plugin.
428 \li GeocodeModel.MissingRequiredParameterError - A required parameter was not specified.
429 \li GeocodeModel.ParseError - The response from the service provider was in an unrecognizable format.
430 \li GeocodeModel.UnknownError - An error occurred which does not fit into any of the other categories.
431 \li GeocodeModel.UnknownParameterError - The plugin did not recognize one of the parameters it was given.
432 \li GeocodeModel.UnsupportedOptionError - The requested operation is not supported by the geocoding provider.
433 This may happen when the loaded engine does not support a particular geocoding request
434 such as reverse geocoding.
435 \endlist
436*/
437
438QDeclarativeGeocodeModel::GeocodeError QDeclarativeGeocodeModel::error() const
439{
440 return error_;
441}
442
443void QDeclarativeGeocodeModel::setError(GeocodeError error, const QString &errorString)
444{
445 if (error_ == error && errorString_ == errorString)
446 return;
447 error_ = error;
448 errorString_ = errorString;
449 emit errorChanged();
450}
451
452/*!
453 \qmlproperty string QtLocation::GeocodeModel::errorString
454
455 This read-only property holds the textual presentation of the latest geocoding error.
456 If no error has occurred or the model has been reset, an empty string is returned.
457
458 An empty string may also be returned if an error occurred which has no associated
459 textual representation.
460*/
461
462QString QDeclarativeGeocodeModel::errorString() const
463{
464 return errorString_;
465}
466
467/*!
468 \internal
469*/
470void QDeclarativeGeocodeModel::setLocations(const QList<QGeoLocation> &locations)
471{
472 beginResetModel();
473 qDeleteAll(declarativeLocations_);
474 declarativeLocations_.clear();
475 for (const auto &location : locations)
476 declarativeLocations_.append(new QDeclarativeGeoLocation(location, this));
477 endResetModel();
478}
479
480/*!
481 \qmlproperty int QtLocation::GeocodeModel::count
482
483 This property holds how many locations the model currently has.
484 Amongst other uses, you can use this value when accessing locations
485 via the GeocodeModel::get -method.
486*/
487
488int QDeclarativeGeocodeModel::count() const
489{
490 return declarativeLocations_.count();
491}
492
493/*!
494 \qmlmethod Location QtLocation::GeocodeModel::get(int index)
495
496 Returns the \l [QML] {Location} at given \a index. Use \l count property to check the
497 amount of locations available. The locations are indexed from zero, so the accessible range
498 is 0...(count - 1).
499
500 If you access out of bounds, a zero (null object) is returned and a warning is issued.
501*/
502
503QDeclarativeGeoLocation *QDeclarativeGeocodeModel::get(int index)
504{
505 if (index < 0 || index >= declarativeLocations_.count()) {
506 qmlWarning(this) << QStringLiteral("Index '%1' out of range").arg(index);
507 return nullptr;
508 }
509 return declarativeLocations_.at(index);
510}
511
512/*!
513 \qmlproperty int QtLocation::GeocodeModel::limit
514
515 This property holds the maximum number of results. The limit and \l offset values are only
516 applicable with free string geocoding (that is they are not considered when using addresses
517 or coordinates in the search query).
518
519 If limit is -1 the entire result set will be returned, otherwise at most limit results will be
520 returned. The limit and \l offset results can be used together to implement paging.
521*/
522
523int QDeclarativeGeocodeModel::limit() const
524{
525 return limit_;
526}
527
528void QDeclarativeGeocodeModel::setLimit(int limit)
529{
530 if (limit == limit_)
531 return;
532 limit_ = limit;
533 if (autoUpdate_) {
534 update();
535 }
536 emit limitChanged();
537}
538
539/*!
540 \qmlproperty int QtLocation::GeocodeModel::offset
541
542 This property tells not to return the first 'offset' number of the results. The \l limit and
543 offset values are only applicable with free string geocoding (that is they are not considered
544 when using addresses or coordinates in the search query).
545
546 The \l limit and offset results can be used together to implement paging.
547*/
548
549int QDeclarativeGeocodeModel::offset() const
550{
551 return offset_;
552}
553
554void QDeclarativeGeocodeModel::setOffset(int offset)
555{
556 if (offset == offset_)
557 return;
558 offset_ = offset;
559 if (autoUpdate_) {
560 update();
561 }
562 emit offsetChanged();
563}
564
565/*!
566 \qmlmethod void QtLocation::GeocodeModel::reset()
567
568 Resets the model. All location data is cleared, any outstanding requests
569 are aborted and possible errors are cleared. Model status will be set
570 to GeocodeModel.Null
571*/
572
573void QDeclarativeGeocodeModel::reset()
574{
575 beginResetModel();
576 if (!declarativeLocations_.isEmpty()) {
577 setLocations(QList<QGeoLocation>());
578 emit countChanged();
579 }
580 endResetModel();
581
582 abortRequest();
583 setError(NoError, QString());
584 setStatus(QDeclarativeGeocodeModel::Null);
585}
586
587/*!
588 \qmlmethod void QtLocation::GeocodeModel::cancel()
589
590 Cancels any outstanding requests and clears errors. Model status will be set to either
591 GeocodeModel.Null or GeocodeModel.Ready.
592*/
593void QDeclarativeGeocodeModel::cancel()
594{
595 abortRequest();
596 setError(NoError, QString());
597 setStatus(declarativeLocations_.isEmpty() ? Null : Ready);
598}
599
600/*!
601 \qmlproperty QVariant QtLocation::GeocodeModel::query
602
603 This property holds the data of the geocoding request.
604 The property accepts three types of queries which determine both the data and
605 the type of action to be performed:
606
607 \list
608 \li Address - Geocoding (address to coordinate)
609 \li \l [QtPositioning]{geoCoordinate} - Reverse geocoding (coordinate to address)
610 \li string - Geocoding (address to coordinate)
611 \endlist
612*/
613
614QVariant QDeclarativeGeocodeModel::query() const
615{
616 return queryVariant_;
617}
618
619void QDeclarativeGeocodeModel::setQuery(const QVariant &query)
620{
621 if (query == queryVariant_)
622 return;
623
624 if (query.userType() == qMetaTypeId<QGeoCoordinate>()) {
625 if (address_) {
626 address_->disconnect(this);
627 address_ = 0;
628 }
629 searchString_.clear();
630
631 coordinate_ = query.value<QGeoCoordinate>();
632 } else if (query.typeId() == QMetaType::QString) {
633 searchString_ = query.toString();
634 if (address_) {
635 address_->disconnect(this);
636 address_ = 0;
637 }
638 coordinate_ = QGeoCoordinate();
639 } else if (QObject *object = query.value<QObject *>()) {
640 if (QDeclarativeGeoAddress *address = qobject_cast<QDeclarativeGeoAddress *>(object)) {
641 if (address_)
642 address_->disconnect(this);
643 coordinate_ = QGeoCoordinate();
644 searchString_.clear();
645
646 address_ = address;
647 connect(address_, &QDeclarativeGeoAddress::countryChanged,
648 this, &QDeclarativeGeocodeModel::queryContentChanged);
649 connect(address_, &QDeclarativeGeoAddress::countryCodeChanged,
650 this, &QDeclarativeGeocodeModel::queryContentChanged);
651 connect(address_, &QDeclarativeGeoAddress::stateChanged,
652 this, &QDeclarativeGeocodeModel::queryContentChanged);
653 connect(address_, &QDeclarativeGeoAddress::countyChanged,
654 this, &QDeclarativeGeocodeModel::queryContentChanged);
655 connect(address_, &QDeclarativeGeoAddress::cityChanged,
656 this, &QDeclarativeGeocodeModel::queryContentChanged);
657 connect(address_, &QDeclarativeGeoAddress::districtChanged,
658 this, &QDeclarativeGeocodeModel::queryContentChanged);
659 connect(address_, &QDeclarativeGeoAddress::streetChanged,
660 this, &QDeclarativeGeocodeModel::queryContentChanged);
661 connect(address_, &QDeclarativeGeoAddress::postalCodeChanged,
662 this, &QDeclarativeGeocodeModel::queryContentChanged);
663 } else {
664 qmlWarning(this) << QStringLiteral("Unsupported query type for geocode model ")
665 << QStringLiteral("(coordinate, string and Address supported).");
666 return;
667 }
668 } else {
669 qmlWarning(this) << QStringLiteral("Unsupported query type for geocode model ")
670 << QStringLiteral("(coordinate, string and Address supported).");
671 return;
672 }
673
674 queryVariant_ = query;
675 emit queryChanged();
676 if (autoUpdate_)
677 update();
678}
679
680/*!
681 \qmlproperty bool QtLocation::GeocodeModel::autoUpdate
682
683 This property controls whether the Model automatically updates in response
684 to changes in its attached query. The default value of this property
685 is false.
686
687 If setting this value to 'true' and using an Address or
688 \l [QtPositioning]{geoCoordinate} as the query, note that any change at all
689 in the
690 object's properties will trigger a new request to be sent. If you are adjusting many
691 properties of the object whilst autoUpdate is enabled, this can generate large numbers of
692 useless (and later discarded) requests.
693*/
694
695bool QDeclarativeGeocodeModel::autoUpdate() const
696{
697 return autoUpdate_;
698}
699
700void QDeclarativeGeocodeModel::setAutoUpdate(bool update)
701{
702 if (autoUpdate_ == update)
703 return;
704 autoUpdate_ = update;
705 emit autoUpdateChanged();
706}
707
708QT_END_NAMESPACE
Combined button and popup list for selecting options.