Revision control

++ // Copyright (C) 2025 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++
++ #include <QtCore/qjsondocument.h>
++ #include <QtCore/qjsonobject.h>
++ #include <QtCore/qregularexpression.h>
++ #include <QtCore/qstringlist.h>
++ #include <QtLocation/private/unsupportedreplies_p.h>
++ #include <QtLocation/qplacecategory.h>
++ #include <QtNetwork/qnetworkaccessmanager.h>
++ #include <qplacemanagerengineohosmapkit.h>
++ #include <qplacesearchreplyohosmapkit.h>
++ #include <qplacesearchsuggestionreplyohosmapkit.h>
++ #include <qohosmapkitcommon.h>
++ #include <qpoicategoryohosmapkit.h>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace {
++
++ const QString placeSearchApiUrlBase =
++ const auto searchByTextApiUrl = QUrl(placeSearchApiUrlBase + QLatin1String("searchByText"));
++ const auto autoCompleteApiUrl = QUrl(placeSearchApiUrlBase + QLatin1String("queryAutoComplete"));
++
++ class QPlaceManagerEngineOhosMapKit : public QPlaceManagerEngine
++ {
++ public:
++ QPlaceManagerEngineOhosMapKit(
++ const QVariantMap &parameters, QGeoServiceProvider::Error *error, QString *errorString);
++
++ QPlaceSearchReply *search(const QPlaceSearchRequest &) override;
++ QPlaceSearchSuggestionReply *searchSuggestions(const QPlaceSearchRequest &request) override;
++
++ QPlaceReply *initializeCategories() override;
++ QString parentCategoryId(const QString &categoryId) const override;
++ QStringList childCategoryIds(const QString &categoryId) const override;
++ QPlaceCategory category(const QString &categoryId) const override;
++ QList<QPlaceCategory> childCategories(const QString &parentId) const override;
++
++ private:
++ void onReplyFinished();
++ void onReplyError(QPlaceReply::Error errorCode, const QString &errorString);
++
++ QNetworkAccessManager m_networkManager;
++ QString m_userAgent;
++ QString m_authenticationKey;
++ };
++
++ class DummyPlaceReply : public QPlaceReply
++ {
++ public:
++ DummyPlaceReply(QObject *parent);
++
++ void finish();
++ };
++
++ DummyPlaceReply::DummyPlaceReply(QObject *parent)
++ : QPlaceReply(parent)
++ {}
++
++ void DummyPlaceReply::finish()
++ {
++ setFinished(true);
++ Q_EMIT finished();
++ }
++
++ QJsonObject createPlaceSearchRequestBody(const QPlaceSearchRequest &request)
++ {
++ QJsonObject placeSearchRequestBody;
++
++ placeSearchRequestBody.insert(QStringLiteral("query"), request.searchTerm());
++
++ auto searchArea = request.searchArea();
++ if (searchArea.isValid() && !searchArea.isEmpty()) {
++ constexpr int restApiMinRadiusInMeters = 1;
++ constexpr int restApiMaxRadiusInMeters = 50000;
++ auto radius = qBound(
++ restApiMinRadiusInMeters,
++ qRound(searchArea.center().distanceTo(searchArea.boundingGeoRectangle().topLeft())),
++ restApiMaxRadiusInMeters);
++
++ placeSearchRequestBody.insert(
++ QStringLiteral("location"),
++ OhosMapKit::CoordinateJson::tryConvertFromQGeoCoordinate(searchArea.center()));
++ placeSearchRequestBody.insert(QStringLiteral("radius"), radius);
++ }
++
++ placeSearchRequestBody.insert(QStringLiteral("language"), OhosMapKit::getLanguageCode());
++
++ return placeSearchRequestBody;
++ }
++
++ QString convertCategoryIdToCategoryName(const QString &categoryId)
++ {
++ auto base = categoryId;
++ if (!base.startsWith("ADMINISTRATIVE_AREA"))
++ base.remove(QRegularExpression(R"((_LEVEL_\d+)$)"));
++
++ auto parts = base.split(QLatin1Char('_'), Qt::SkipEmptyParts);
++ for (int i = 0; i < parts.size(); ++i) {
++ parts[i] = parts[i].toLower();
++ if (i == 0 && !parts[i].isEmpty())
++ parts[i][0] = parts[i][0].toUpper();
++ }
++ return parts.join(QLatin1Char(' '));
++ }
++
++
++ QPlaceManagerEngineOhosMapKit::QPlaceManagerEngineOhosMapKit(
++ const QVariantMap &parameters, QGeoServiceProvider::Error *error, QString *errorString)
++ : QPlaceManagerEngine(parameters)
++ , m_userAgent(OhosMapKit::getUserAgentParameterOrDefault(parameters))
++ , m_authenticationKey(OhosMapKit::getAuthenticationKeyParameterOrEmpty(parameters))
++ {
++ if (error != nullptr)
++ *error = QGeoServiceProvider::NoError;
++
++ if (errorString != nullptr)
++ errorString->clear();
++ }
++
++ QPlaceSearchReply *QPlaceManagerEngineOhosMapKit::search(const QPlaceSearchRequest &request)
++ {
++ if (request.categories().size() > 1) {
++ return new QPlaceSearchReplyUnsupported(
++ QPlaceReply::UnsupportedError,
++ tr("OHOS Map Kit allows search of only one category type."),
++ this);
++ }
++
++ auto placeSearchRequestBody = createPlaceSearchRequestBody(request);
++
++ constexpr auto restApiMinNumOfResultsPerPage = 1;
++ constexpr auto restApiMaxNumOfResultsPerPage = 20;
++ if (request.limit() > 0) {
++ placeSearchRequestBody.insert(
++ QStringLiteral("pageSize"),
++ qBound(restApiMinNumOfResultsPerPage, request.limit(), restApiMaxNumOfResultsPerPage));
++ }
++
++ if (!request.categories().isEmpty())
++ placeSearchRequestBody.insert(QStringLiteral("hwPoiType"), request.categories().first().categoryId());
++
++ auto *reply = makeQPlaceSearchReplyOhosMapKit(
++ request,
++ m_networkManager.post(
++ OhosMapKit::createOhosMapKitNetworkRequestWithJsonBody(
++ searchByTextApiUrl, m_userAgent, m_authenticationKey),
++ QJsonDocument(placeSearchRequestBody).toJson(QJsonDocument::Compact)),
++ this);
++
++ connect(
++ reply, &QPlaceSearchReply::finished,
++ this, &QPlaceManagerEngineOhosMapKit::onReplyFinished);
++ connect(
++ reply,
++ &QPlaceSearchReply::errorOccurred,
++ this,
++ &QPlaceManagerEngineOhosMapKit::onReplyError);
++
++ return reply;
++ }
++
++ QPlaceSearchSuggestionReply *QPlaceManagerEngineOhosMapKit::searchSuggestions(
++ const QPlaceSearchRequest &request)
++ {
++ auto *reply = makeQPlaceSearchSuggestionReplyOhosMapKit(
++ m_networkManager.post(
++ OhosMapKit::createOhosMapKitNetworkRequestWithJsonBody(
++ autoCompleteApiUrl, m_userAgent, m_authenticationKey),
++ QJsonDocument(createPlaceSearchRequestBody(request)).toJson(QJsonDocument::Compact)),
++ this);
++
++ connect(
++ reply, &QPlaceSearchSuggestionReply::finished,
++ this, &QPlaceManagerEngineOhosMapKit::onReplyFinished);
++ connect(
++ reply,
++ &QPlaceSearchSuggestionReply::errorOccurred,
++ this,
++ &QPlaceManagerEngineOhosMapKit::onReplyError);
++
++ return reply;
++ }
++
++ QPlaceReply *QPlaceManagerEngineOhosMapKit::initializeCategories()
++ {
++ auto *reply = new DummyPlaceReply(this);
++ connect(reply, &QPlaceReply::finished, this, &QPlaceManagerEngineOhosMapKit::onReplyFinished);
++ QMetaObject::invokeMethod(reply, &DummyPlaceReply::finish, Qt::QueuedConnection);
++
++ return reply;
++ }
++
++ QString QPlaceManagerEngineOhosMapKit::parentCategoryId(const QString &categoryId) const
++ {
++ return QPoiCategoryOhosMapKit::tryGetParentCategoryIdOrEmpty(categoryId);
++ }
++
++ QStringList QPlaceManagerEngineOhosMapKit::childCategoryIds(const QString &categoryId) const
++ {
++ return QPoiCategoryOhosMapKit::tryGetChildCategoryIdsOrEmpty(categoryId);
++ }
++
++ QPlaceCategory QPlaceManagerEngineOhosMapKit::category(const QString &categoryId) const
++ {
++ QPlaceCategory placeCategory;
++ placeCategory.setCategoryId(categoryId);
++ placeCategory.setName(convertCategoryIdToCategoryName(categoryId));
++ return placeCategory;
++ }
++
++ QList<QPlaceCategory> QPlaceManagerEngineOhosMapKit::childCategories(const QString &parentId) const
++ {
++ auto categoryIds = childCategoryIds(parentId);
++
++ QList<QPlaceCategory> childCategories;
++ for (const auto &categoryId : categoryIds)
++ childCategories.push_back(category(categoryId));
++
++ return childCategories;
++ }
++
++ void QPlaceManagerEngineOhosMapKit::onReplyFinished()
++ {
++ auto *reply = qobject_cast<QPlaceReply *>(sender());
++ if (reply != nullptr)
++ Q_EMIT finished(reply);
++ }
++
++ void QPlaceManagerEngineOhosMapKit::onReplyError(
++ QPlaceReply::Error errorCode, const QString &errorString)
++ {
++ auto *reply = qobject_cast<QPlaceReply *>(sender());
++ if (reply != nullptr)
++ Q_EMIT errorOccurred(reply, errorCode, errorString);
++ }
++
++ }
++
++ QPlaceManagerEngine *makeQPlaceManagerEngineOhosMapKit(
++ const QVariantMap &parameters, QGeoServiceProvider::Error *error, QString *errorString)
++ {
++ return new QPlaceManagerEngineOhosMapKit(parameters, error, errorString);
++ }
++
++ QT_END_NAMESPACE