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
qplacesearchreplymapbox.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 Mapbox, Inc.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
7
8#include <QtCore/QJsonDocument>
9#include <QtCore/QJsonArray>
10#include <QtCore/QJsonObject>
11#include <QtNetwork/QNetworkReply>
12#include <QtPositioning/QGeoCircle>
13#include <QtPositioning/QGeoRectangle>
14#include <QtLocation/QPlace>
15#include <QtLocation/QPlaceCategory>
16#include <QtLocation/QPlaceIcon>
17#include <QtLocation/QPlaceResult>
18#include <QtLocation/QPlaceSearchRequest>
19#include <QtLocation/QPlaceContactDetail>
20
21#include <algorithm>
22
23QT_BEGIN_NAMESPACE
24
25namespace {
26
27// https://www.mapbox.com/api-documentation/#response-object
28QPlaceResult parsePlaceResult(const QJsonObject &response, const QString &attribution)
29{
30 QPlace place;
31
32 place.setAttribution(attribution);
33 place.setPlaceId(response.value(QStringLiteral("id")).toString());
34 place.setVisibility(QLocation::PublicVisibility);
35
36 QString placeName = response.value(QStringLiteral("text")).toString();
37 if (placeName.isEmpty())
38 placeName = response.value(QStringLiteral("place_name")).toString();
39
40 place.setName(placeName);
41 place.setDetailsFetched(true);
42
43 // Unused data: type, place_type, relevance, properties.short_code,
44 // properties.landmark, properties.wikidata
45
46 // The property object is unstable and only Carmen GeoJSON properties are
47 // guaranteed. This implementation should check for the presence of these
48 // values in a response before it attempts to use them.
49 if (response.value(QStringLiteral("properties")).isObject()) {
50 const QJsonObject properties = response.value(QStringLiteral("properties")).toObject();
51
52 const QString makiString = properties.value(QStringLiteral("maki")).toString();
53 if (!makiString.isEmpty()) {
54 QVariantMap iconParameters;
55 iconParameters.insert(QPlaceIcon::SingleUrl,
56 QUrl::fromLocalFile(QStringLiteral(":/mapbox/") + makiString + QStringLiteral(".svg")));
57
58 QPlaceIcon icon;
59 icon.setParameters(iconParameters);
60 place.setIcon(icon);
61 }
62
63 const QString phoneString = properties.value(QStringLiteral("tel")).toString();
64 if (!phoneString.isEmpty()) {
65 QPlaceContactDetail phoneDetail;
66 phoneDetail.setLabel(QPlaceContactDetail::Phone);
67 phoneDetail.setValue(phoneString);
68 place.setContactDetails(QPlaceContactDetail::Phone, QList<QPlaceContactDetail>() << phoneDetail);
69 }
70
71 const QString categoryString = properties.value(QStringLiteral("category")).toString();
72 if (!categoryString.isEmpty()) {
73 QList<QPlaceCategory> categories;
74 for (const QString &categoryId : categoryString.split(QStringLiteral(", "), Qt::SkipEmptyParts)) {
75 QPlaceCategory category;
76 category.setName(QMapboxCommon::mapboxNameForCategory(categoryId));
77 category.setCategoryId(categoryId);
78 categories.append(category);
79 }
80 place.setCategories(categories);
81 }
82 }
83
84 // XXX: matching_text, matching_place_name
85 // XXX: text_{language}, place_name_{language}
86 // XXX: language, language_{language}
87
88 place.setLocation(QMapboxCommon::parseGeoLocation(response));
89
90 // XXX: geometry, geometry.type, geometry.coordinates, geometry.interpolated
91
92 QPlaceResult result;
93 result.setPlace(place);
94 result.setTitle(place.name());
95
96 return result;
97}
98
99} // namespace
100
101QPlaceSearchReplyMapbox::QPlaceSearchReplyMapbox(const QPlaceSearchRequest &request, QNetworkReply *reply, QPlaceManagerEngineMapbox *parent)
102: QPlaceSearchReply(parent)
103{
104 Q_ASSERT(parent);
105 if (!reply) {
106 setError(UnknownError, QStringLiteral("Null reply"));
107 return;
108 }
109 setRequest(request);
110
111 connect(reply, &QNetworkReply::finished, this, &QPlaceSearchReplyMapbox::onReplyFinished);
112 connect(reply, &QNetworkReply::errorOccurred, this, &QPlaceSearchReplyMapbox::onNetworkError);
113
114 connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort);
115 connect(this, &QObject::destroyed, reply, &QObject::deleteLater);
116}
117
121
122void QPlaceSearchReplyMapbox::setError(QPlaceReply::Error errorCode, const QString &errorString)
123{
124 QPlaceReply::setError(errorCode, errorString);
125 emit errorOccurred(errorCode, errorString);
126
127 setFinished(true);
128 emit finished();
129}
130
131void QPlaceSearchReplyMapbox::onReplyFinished()
132{
133 QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
134 reply->deleteLater();
135
136 if (reply->error() != QNetworkReply::NoError)
137 return;
138
139 const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
140 if (!document.isObject()) {
141 setError(ParseError, tr("Response parse error"));
142 return;
143 }
144
145 const QJsonArray features = document.object().value(QStringLiteral("features")).toArray();
146 const QString attribution = document.object().value(QStringLiteral("attribution")).toString();
147
148 const QGeoCoordinate searchCenter = request().searchArea().center();
149 const QList<QPlaceCategory> categories = request().categories();
150
151 QList<QPlaceSearchResult> results;
152 for (const QJsonValue feature : features) {
153 QPlaceResult placeResult = parsePlaceResult(feature.toObject(), attribution);
154
155 if (!categories.isEmpty()) {
156 const QList<QPlaceCategory> placeCategories = placeResult.place().categories();
157 bool categoryMatch = false;
158 if (!placeCategories.isEmpty()) {
159 for (const QPlaceCategory &placeCategory : placeCategories) {
160 if (categories.contains(placeCategory)) {
161 categoryMatch = true;
162 break;
163 }
164 }
165 }
166 if (!categoryMatch)
167 continue;
168 }
169 placeResult.setDistance(searchCenter.distanceTo(placeResult.place().location().coordinate()));
170 results.append(placeResult);
171 }
172
173 if (request().relevanceHint() == QPlaceSearchRequest::DistanceHint) {
174 std::sort(results.begin(), results.end(), [](const QPlaceResult &a, const QPlaceResult &b) -> bool {
175 return a.distance() < b.distance();
176 });
177 } else if (request().relevanceHint() == QPlaceSearchRequest::LexicalPlaceNameHint) {
178 std::sort(results.begin(), results.end(), [](const QPlaceResult &a, const QPlaceResult &b) -> bool {
179 return a.place().name() < b.place().name();
180 });
181 }
182
183 setResults(results);
184
185 setFinished(true);
186 emit finished();
187}
188
189void QPlaceSearchReplyMapbox::onNetworkError(QNetworkReply::NetworkError error)
190{
191 Q_UNUSED(error);
192 QNetworkReply *reply = static_cast<QNetworkReply *>(sender());
193 reply->deleteLater();
194 setError(CommunicationError, reply->errorString());
195}
196
197QT_END_NAMESPACE
QPlaceResult parsePlaceResult(const QJsonObject &response, const QString &attribution)