summaryrefslogtreecommitdiff
path: root/src/plugins/geoservices/osm
diff options
context:
space:
mode:
authorAaron McCarthy <mccarthy.aaron@gmail.com>2015-01-23 15:37:19 +1000
committerAlex Blasche <alexander.blasche@theqtcompany.com>2015-02-02 07:33:42 +0000
commitdbfa9eeaae4b0508a0d4de41d0578003e98d357b (patch)
treeda81c7ec0773dd8e9021227ad47d1aa7fbb7be5c /src/plugins/geoservices/osm
parentd2ff57fec56b575059737e91cf6dbdcd3d610e6f (diff)
downloadqtlocation-dbfa9eeaae4b0508a0d4de41d0578003e98d357b.tar.gz
Add support for places to Open Street Map plugin.
Implement basic places support based on the Open Street Map Nominatim service. Support for read only categories and place searching is supported. The plugin does not support getting place details, getting place content, search suggestions, saving/removing places or saving/removing categories. Change-Id: I5a185cdf25b50d5b377be4d2c3c53c8f1e807288 Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
Diffstat (limited to 'src/plugins/geoservices/osm')
-rw-r--r--src/plugins/geoservices/osm/osm.pro11
-rw-r--r--src/plugins/geoservices/osm/osm_plugin.json3
-rw-r--r--src/plugins/geoservices/osm/qgeoserviceproviderpluginosm.cpp9
-rw-r--r--src/plugins/geoservices/osm/qplacecategoriesreplyimpl.cpp59
-rw-r--r--src/plugins/geoservices/osm/qplacecategoriesreplyimpl.h55
-rw-r--r--src/plugins/geoservices/osm/qplacemanagerengineosm.cpp354
-rw-r--r--src/plugins/geoservices/osm/qplacemanagerengineosm.h90
-rw-r--r--src/plugins/geoservices/osm/qplacesearchreplyimpl.cpp213
-rw-r--r--src/plugins/geoservices/osm/qplacesearchreplyimpl.h68
9 files changed, 851 insertions, 11 deletions
diff --git a/src/plugins/geoservices/osm/osm.pro b/src/plugins/geoservices/osm/osm.pro
index aa24fbc9..bfae8284 100644
--- a/src/plugins/geoservices/osm/osm.pro
+++ b/src/plugins/geoservices/osm/osm.pro
@@ -14,8 +14,10 @@ HEADERS += \
qgeocodingmanagerengineosm.h \
qgeocodereplyosm.h \
qgeoroutingmanagerengineosm.h \
- qgeoroutereplyosm.h
-
+ qgeoroutereplyosm.h \
+ qplacemanagerengineosm.h \
+ qplacesearchreplyimpl.h \
+ qplacecategoriesreplyimpl.h
SOURCES += \
qgeoserviceproviderpluginosm.cpp \
@@ -26,7 +28,10 @@ SOURCES += \
qgeocodingmanagerengineosm.cpp \
qgeocodereplyosm.cpp \
qgeoroutingmanagerengineosm.cpp \
- qgeoroutereplyosm.cpp
+ qgeoroutereplyosm.cpp \
+ qplacemanagerengineosm.cpp \
+ qplacesearchreplyimpl.cpp \
+ qplacecategoriesreplyimpl.cpp
OTHER_FILES += \
osm_plugin.json
diff --git a/src/plugins/geoservices/osm/osm_plugin.json b/src/plugins/geoservices/osm/osm_plugin.json
index 394be376..1aaf6f7f 100644
--- a/src/plugins/geoservices/osm/osm_plugin.json
+++ b/src/plugins/geoservices/osm/osm_plugin.json
@@ -7,6 +7,7 @@
"OnlineMappingFeature",
"OnlineGeocodingFeature",
"ReverseGeocodingFeature",
- "OnlineRoutingFeature"
+ "OnlineRoutingFeature",
+ "OnlinePlacesFeature"
]
}
diff --git a/src/plugins/geoservices/osm/qgeoserviceproviderpluginosm.cpp b/src/plugins/geoservices/osm/qgeoserviceproviderpluginosm.cpp
index 085c2a49..900921d3 100644
--- a/src/plugins/geoservices/osm/qgeoserviceproviderpluginosm.cpp
+++ b/src/plugins/geoservices/osm/qgeoserviceproviderpluginosm.cpp
@@ -35,8 +35,7 @@
#include "qgeotiledmappingmanagerengineosm.h"
#include "qgeocodingmanagerengineosm.h"
#include "qgeoroutingmanagerengineosm.h"
-
-#include <QtLocation/private/qgeotiledmappingmanagerengine_p.h>
+#include "qplacemanagerengineosm.h"
QT_BEGIN_NAMESPACE
@@ -61,11 +60,7 @@ QGeoRoutingManagerEngine *QGeoServiceProviderFactoryOsm::createRoutingManagerEng
QPlaceManagerEngine *QGeoServiceProviderFactoryOsm::createPlaceManagerEngine(
const QVariantMap &parameters, QGeoServiceProvider::Error *error, QString *errorString) const
{
- Q_UNUSED(parameters)
- Q_UNUSED(error)
- Q_UNUSED(errorString)
-
- return 0;
+ return new QPlaceManagerEngineOsm(parameters, error, errorString);
}
QT_END_NAMESPACE
diff --git a/src/plugins/geoservices/osm/qplacecategoriesreplyimpl.cpp b/src/plugins/geoservices/osm/qplacecategoriesreplyimpl.cpp
new file mode 100644
index 00000000..4ad108f0
--- /dev/null
+++ b/src/plugins/geoservices/osm/qplacecategoriesreplyimpl.cpp
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Aaron McCarthy <mccarthy.aaron@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtLocation module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplacecategoriesreplyimpl.h"
+
+QT_BEGIN_NAMESPACE
+
+QPlaceCategoriesReplyImpl::QPlaceCategoriesReplyImpl(QObject *parent)
+: QPlaceReply(parent)
+{
+}
+
+QPlaceCategoriesReplyImpl::~QPlaceCategoriesReplyImpl()
+{
+}
+
+void QPlaceCategoriesReplyImpl::emitFinished()
+{
+ setFinished(true);
+ emit finished();
+}
+
+void QPlaceCategoriesReplyImpl::setError(QPlaceReply::Error errorCode, const QString &errorString)
+{
+ QPlaceReply::setError(errorCode, errorString);
+ emit error(errorCode, errorString);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/geoservices/osm/qplacecategoriesreplyimpl.h b/src/plugins/geoservices/osm/qplacecategoriesreplyimpl.h
new file mode 100644
index 00000000..a68290a9
--- /dev/null
+++ b/src/plugins/geoservices/osm/qplacecategoriesreplyimpl.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Aaron McCarthy <mccarthy.aaron@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtLocation module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPLACECATEGORIESREPLYIMPL_H
+#define QPLACECATEGORIESREPLYIMPL_H
+
+#include <QtLocation/QPlaceReply>
+
+QT_BEGIN_NAMESPACE
+
+class QPlaceCategoriesReplyImpl : public QPlaceReply
+{
+ Q_OBJECT
+
+public:
+ explicit QPlaceCategoriesReplyImpl(QObject *parent = 0);
+ ~QPlaceCategoriesReplyImpl();
+
+ void emitFinished();
+ void setError(QPlaceReply::Error errorCode, const QString &errorString);
+};
+
+QT_END_NAMESPACE
+
+#endif // QPLACECATEGORIESREPLYIMPL_H
diff --git a/src/plugins/geoservices/osm/qplacemanagerengineosm.cpp b/src/plugins/geoservices/osm/qplacemanagerengineosm.cpp
new file mode 100644
index 00000000..1c59044f
--- /dev/null
+++ b/src/plugins/geoservices/osm/qplacemanagerengineosm.cpp
@@ -0,0 +1,354 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Aaron McCarthy <mccarthy.aaron@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtFoo module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplacemanagerengineosm.h"
+#include "qplacesearchreplyimpl.h"
+#include "qplacecategoriesreplyimpl.h"
+
+#include <QtCore/QUrlQuery>
+#include <QtCore/QXmlStreamReader>
+#include <QtCore/QRegularExpression>
+#include <QtNetwork/QNetworkAccessManager>
+#include <QtNetwork/QNetworkRequest>
+#include <QtNetwork/QNetworkReply>
+#include <QtPositioning/QGeoCircle>
+#include <QtLocation/private/unsupportedreplies_p.h>
+
+#include <QtCore/QElapsedTimer>
+
+namespace
+{
+QString SpecialPhrasesBaseUrl = QStringLiteral("http://wiki.openstreetmap.org/wiki/Special:Export/Nominatim/Special_Phrases/");
+
+QString nameForTagKey(const QString &tagKey)
+{
+ if (tagKey == QLatin1String("aeroway"))
+ return QPlaceManagerEngineOsm::tr("Aeroway");
+ else if (tagKey == QLatin1String("amenity"))
+ return QPlaceManagerEngineOsm::tr("Amenity");
+ else if (tagKey == QLatin1String("building"))
+ return QPlaceManagerEngineOsm::tr("Building");
+ else if (tagKey == QLatin1String("highway"))
+ return QPlaceManagerEngineOsm::tr("Highway");
+ else if (tagKey == QLatin1String("historic"))
+ return QPlaceManagerEngineOsm::tr("Historic");
+ else if (tagKey == QLatin1String("landuse"))
+ return QPlaceManagerEngineOsm::tr("Land use");
+ else if (tagKey == QLatin1String("leisure"))
+ return QPlaceManagerEngineOsm::tr("Leisure");
+ else if (tagKey == QLatin1String("man_made"))
+ return QPlaceManagerEngineOsm::tr("Man made");
+ else if (tagKey == QLatin1String("natural"))
+ return QPlaceManagerEngineOsm::tr("Natural");
+ else if (tagKey == QLatin1String("place"))
+ return QPlaceManagerEngineOsm::tr("Place");
+ else if (tagKey == QLatin1String("railway"))
+ return QPlaceManagerEngineOsm::tr("Railway");
+ else if (tagKey == QLatin1String("shop"))
+ return QPlaceManagerEngineOsm::tr("Shop");
+ else if (tagKey == QLatin1String("tourism"))
+ return QPlaceManagerEngineOsm::tr("Tourism");
+ else if (tagKey == QLatin1String("waterway"))
+ return QPlaceManagerEngineOsm::tr("Waterway");
+ else
+ return tagKey;
+}
+
+}
+
+QPlaceManagerEngineOsm::QPlaceManagerEngineOsm(const QVariantMap &parameters,
+ QGeoServiceProvider::Error *error,
+ QString *errorString)
+: QPlaceManagerEngine(parameters), m_networkManager(new QNetworkAccessManager(this)),
+ m_categoriesReply(0)
+{
+ if (parameters.contains(QStringLiteral("useragent")))
+ m_userAgent = parameters.value(QStringLiteral("useragent")).toString().toLatin1();
+ else
+ m_userAgent = "Qt Location based application";
+
+ *error = QGeoServiceProvider::NoError;
+ errorString->clear();
+}
+
+QPlaceManagerEngineOsm::~QPlaceManagerEngineOsm()
+{
+}
+
+QPlaceSearchReply *QPlaceManagerEngineOsm::search(const QPlaceSearchRequest &request)
+{
+ bool unsupported = false;
+
+ // Only public visibility supported
+ unsupported |= request.visibilityScope() != QLocation::UnspecifiedVisibility &&
+ request.visibilityScope() != QLocation::PublicVisibility;
+ unsupported |= request.searchTerm().isEmpty() && request.categories().isEmpty();
+
+ if (unsupported)
+ return QPlaceManagerEngine::search(request);
+
+ QUrlQuery queryItems;
+
+ queryItems.addQueryItem(QStringLiteral("format"), QStringLiteral("jsonv2"));
+
+ //queryItems.addQueryItem(QStringLiteral("accept-language"), QStringLiteral("en"));
+
+ QGeoRectangle boundingBox;
+ QGeoShape searchArea = request.searchArea();
+ switch (searchArea.type()) {
+ case QGeoShape::CircleType: {
+ QGeoCircle c(searchArea);
+ qreal radius = c.radius();
+ if (radius < 0)
+ radius = 50000;
+
+ boundingBox = QGeoRectangle(c.center().atDistanceAndAzimuth(radius, -45),
+ c.center().atDistanceAndAzimuth(radius, 135));
+ break;
+ }
+ case QGeoShape::RectangleType:
+ boundingBox = searchArea;
+ break;
+ default:
+ ;
+ }
+
+ if (!boundingBox.isEmpty()) {
+ queryItems.addQueryItem(QStringLiteral("bounded"), QStringLiteral("1"));
+ QString coordinates;
+ coordinates = QString::number(boundingBox.topLeft().longitude()) + QLatin1Char(',') +
+ QString::number(boundingBox.topLeft().latitude()) + QLatin1Char(',') +
+ QString::number(boundingBox.bottomRight().longitude()) + QLatin1Char(',') +
+ QString::number(boundingBox.bottomRight().latitude());
+ queryItems.addQueryItem(QStringLiteral("viewbox"), coordinates);
+ }
+
+ QStringList queryParts;
+ if (!request.searchTerm().isEmpty())
+ queryParts.append(request.searchTerm());
+
+ foreach (const QPlaceCategory &category, request.categories()) {
+ QString id = category.categoryId();
+ int index = id.indexOf(QLatin1Char('='));
+ if (index != -1)
+ id = id.mid(index+1);
+ queryParts.append(QLatin1Char('[') + id + QLatin1Char(']'));
+ }
+
+ queryItems.addQueryItem(QStringLiteral("q"), queryParts.join(QLatin1Char('+')));
+
+ QVariantMap parameters = request.searchContext().toMap();
+
+ QStringList placeIds = parameters.value(QStringLiteral("ExcludePlaceIds")).toStringList();
+ if (!placeIds.isEmpty())
+ queryItems.addQueryItem(QStringLiteral("exclude_place_ids"), placeIds.join(QLatin1Char(',')));
+
+ queryItems.addQueryItem(QStringLiteral("addressdetails"), QStringLiteral("1"));
+
+ QUrl requestUrl(QStringLiteral("http://nominatim.openstreetmap.org/search?"));
+ requestUrl.setQuery(queryItems);
+
+ QNetworkReply *networkReply = m_networkManager->get(QNetworkRequest(requestUrl));
+
+ QPlaceSearchReplyImpl *reply = new QPlaceSearchReplyImpl(request, networkReply, this);
+ connect(reply, SIGNAL(finished()), this, SLOT(replyFinished()));
+ connect(reply, SIGNAL(error(QPlaceReply::Error,QString)),
+ this, SLOT(replyError(QPlaceReply::Error,QString)));
+
+ return reply;
+}
+
+QPlaceReply *QPlaceManagerEngineOsm::initializeCategories()
+{
+ // Only fetch categories once
+ if (m_categories.isEmpty() && !m_categoriesReply) {
+ m_categoryLocales = m_locales;
+ m_categoryLocales.append(QLocale(QLocale::English));
+ fetchNextCategoryLocale();
+ }
+
+ QPlaceCategoriesReplyImpl *reply = new QPlaceCategoriesReplyImpl(this);
+ connect(reply, SIGNAL(finished()), this, SLOT(replyFinished()));
+ connect(reply, SIGNAL(error(QPlaceReply::Error,QString)),
+ this, SLOT(replyError(QPlaceReply::Error,QString)));
+
+ // TODO delayed finished() emission
+ if (!m_categories.isEmpty())
+ reply->emitFinished();
+
+ m_pendingCategoriesReply.append(reply);
+ return reply;
+}
+
+QString QPlaceManagerEngineOsm::parentCategoryId(const QString &categoryId) const
+{
+ Q_UNUSED(categoryId)
+
+ // Only a two category levels
+ return QString();
+}
+
+QStringList QPlaceManagerEngineOsm::childCategoryIds(const QString &categoryId) const
+{
+ return m_subcategories.value(categoryId);
+}
+
+QPlaceCategory QPlaceManagerEngineOsm::category(const QString &categoryId) const
+{
+ return m_categories.value(categoryId);
+}
+
+QList<QPlaceCategory> QPlaceManagerEngineOsm::childCategories(const QString &parentId) const
+{
+ QList<QPlaceCategory> categories;
+ foreach (const QString &id, m_subcategories.value(parentId))
+ categories.append(m_categories.value(id));
+ return categories;
+}
+
+QList<QLocale> QPlaceManagerEngineOsm::locales() const
+{
+ return m_locales;
+}
+
+void QPlaceManagerEngineOsm::setLocales(const QList<QLocale> &locales)
+{
+ m_locales = locales;
+}
+
+void QPlaceManagerEngineOsm::categoryReplyFinished()
+{
+ QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
+ if (!reply)
+ return;
+
+ reply->deleteLater();
+
+ QXmlStreamReader parser(reply);
+ while (!parser.atEnd() && parser.readNextStartElement()) {
+ if (parser.name() == QLatin1String("mediawiki"))
+ continue;
+ if (parser.name() == QLatin1String("page"))
+ continue;
+ if (parser.name() == QLatin1String("revision"))
+ continue;
+ if (parser.name() == QLatin1String("text")) {
+ // parse
+ QString page = parser.readElementText();
+ QRegularExpression regex(QStringLiteral("\\| ([^|]+) \\|\\| ([^|]+) \\|\\| ([^|]+) \\|\\| ([^|]+) \\|\\| ([\\-YN])"));
+ QRegularExpressionMatchIterator i = regex.globalMatch(page);
+ while (i.hasNext()) {
+ QRegularExpressionMatch match = i.next();
+ QString name = match.capturedRef(1).toString();
+ QString tagKey = match.capturedRef(2).toString();
+ QString tagValue = match.capturedRef(3).toString();
+ QString op = match.capturedRef(4).toString();
+ QString plural = match.capturedRef(5).toString();
+
+ // Only interested in any operator plural forms
+ if (op != QLatin1String("-") || plural != QLatin1String("Y"))
+ continue;
+
+ if (!m_categories.contains(tagKey)) {
+ QPlaceCategory category;
+ category.setCategoryId(tagKey);
+ category.setName(nameForTagKey(tagKey));
+ m_categories.insert(category.categoryId(), category);
+ m_subcategories[QString()].append(tagKey);
+ emit categoryAdded(category, QString());
+ }
+
+ QPlaceCategory category;
+ category.setCategoryId(tagKey + QLatin1Char('=') + tagValue);
+ category.setName(name);
+
+ if (!m_categories.contains(category.categoryId())) {
+ m_categories.insert(category.categoryId(), category);
+ m_subcategories[tagKey].append(category.categoryId());
+ emit categoryAdded(category, tagKey);
+ }
+ }
+ }
+
+ parser.skipCurrentElement();
+ }
+
+ if (m_categories.isEmpty() && !m_categoryLocales.isEmpty()) {
+ fetchNextCategoryLocale();
+ return;
+ } else {
+ m_categoryLocales.clear();
+ }
+
+ foreach (QPlaceCategoriesReplyImpl *reply, m_pendingCategoriesReply)
+ reply->emitFinished();
+ m_pendingCategoriesReply.clear();
+}
+
+void QPlaceManagerEngineOsm::categoryReplyError()
+{
+ foreach (QPlaceCategoriesReplyImpl *reply, m_pendingCategoriesReply)
+ reply->setError(QPlaceReply::CommunicationError, tr("Network request error"));
+}
+
+void QPlaceManagerEngineOsm::replyFinished()
+{
+ QPlaceReply *reply = qobject_cast<QPlaceReply *>(sender());
+ if (reply)
+ emit finished(reply);
+}
+
+void QPlaceManagerEngineOsm::replyError(QPlaceReply::Error errorCode, const QString &errorString)
+{
+ QPlaceReply *reply = qobject_cast<QPlaceReply *>(sender());
+ if (reply)
+ emit error(reply, errorCode, errorString);
+}
+
+void QPlaceManagerEngineOsm::fetchNextCategoryLocale()
+{
+ if (m_categoryLocales.isEmpty()) {
+ qWarning("No locales specified to fetch categories for");
+ return;
+ }
+
+ QLocale locale = m_categoryLocales.takeFirst();
+
+ // FIXME: Categories should be cached.
+ QUrl requestUrl = QUrl(SpecialPhrasesBaseUrl + locale.name().left(2).toUpper());
+
+ m_categoriesReply = m_networkManager->get(QNetworkRequest(requestUrl));
+ connect(m_categoriesReply, SIGNAL(finished()), this, SLOT(categoryReplyFinished()));
+ connect(m_categoriesReply, SIGNAL(error(QNetworkReply::NetworkError)),
+ this, SLOT(categoryReplyError()));
+}
diff --git a/src/plugins/geoservices/osm/qplacemanagerengineosm.h b/src/plugins/geoservices/osm/qplacemanagerengineosm.h
new file mode 100644
index 00000000..305d3abe
--- /dev/null
+++ b/src/plugins/geoservices/osm/qplacemanagerengineosm.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Aaron McCarthy <mccarthy.aaron@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtFoo module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPLACEMANAGERENGINEOSM_H
+#define QPLACEMANAGERENGINEOSM_H
+
+#include <QtLocation/QPlaceManagerEngine>
+#include <QtLocation/QGeoServiceProvider>
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkAccessManager;
+class QNetworkReply;
+class QPlaceCategoriesReplyImpl;
+
+class QPlaceManagerEngineOsm : public QPlaceManagerEngine
+{
+ Q_OBJECT
+
+public:
+ QPlaceManagerEngineOsm(const QVariantMap &parameters, QGeoServiceProvider::Error *error,
+ QString *errorString);
+ ~QPlaceManagerEngineOsm();
+
+ QPlaceSearchReply *search(const QPlaceSearchRequest &request) Q_DECL_OVERRIDE;
+
+ QPlaceReply *initializeCategories() Q_DECL_OVERRIDE;
+ QString parentCategoryId(const QString &categoryId) const Q_DECL_OVERRIDE;
+ QStringList childCategoryIds(const QString &categoryId) const Q_DECL_OVERRIDE;
+ QPlaceCategory category(const QString &categoryId) const Q_DECL_OVERRIDE;
+
+ QList<QPlaceCategory> childCategories(const QString &parentId) const Q_DECL_OVERRIDE;
+
+ QList<QLocale> locales() const Q_DECL_OVERRIDE;
+ void setLocales(const QList<QLocale> &locales) Q_DECL_OVERRIDE;
+
+private slots:
+ void categoryReplyFinished();
+ void categoryReplyError();
+ void replyFinished();
+ void replyError(QPlaceReply::Error errorCode, const QString &errorString);
+
+private:
+ void fetchNextCategoryLocale();
+
+ QNetworkAccessManager *m_networkManager;
+ QByteArray m_userAgent;
+ QList<QLocale> m_locales;
+
+ QNetworkReply *m_categoriesReply;
+ QList<QPlaceCategoriesReplyImpl *> m_pendingCategoriesReply;
+ QHash<QString, QPlaceCategory> m_categories;
+ QHash<QString, QStringList> m_subcategories;
+
+ QList<QLocale> m_categoryLocales;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPLACEMANAGERENGINEOSM_H
diff --git a/src/plugins/geoservices/osm/qplacesearchreplyimpl.cpp b/src/plugins/geoservices/osm/qplacesearchreplyimpl.cpp
new file mode 100644
index 00000000..22043c09
--- /dev/null
+++ b/src/plugins/geoservices/osm/qplacesearchreplyimpl.cpp
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Aaron McCarthy <mccarthy.aaron@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtFoo module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplacesearchreplyimpl.h"
+#include "qplacemanagerengineosm.h"
+
+#include <QtCore/QJsonDocument>
+#include <QtCore/QJsonArray>
+#include <QtCore/QJsonObject>
+#include <QtNetwork/QNetworkReply>
+#include <QtPositioning/QGeoCircle>
+#include <QtPositioning/QGeoRectangle>
+#include <QtLocation/QPlaceResult>
+#include <QtLocation/QPlaceSearchRequest>
+
+QT_BEGIN_NAMESPACE
+
+QPlaceSearchReplyImpl::QPlaceSearchReplyImpl(const QPlaceSearchRequest &request,
+ QNetworkReply *reply, QPlaceManagerEngineOsm *parent)
+: QPlaceSearchReply(parent), m_reply(reply)
+{
+ Q_ASSERT(parent);
+
+ setRequest(request);
+
+ if (!m_reply)
+ return;
+
+ m_reply->setParent(this);
+ connect(m_reply, SIGNAL(finished()), this, SLOT(replyFinished()));
+}
+
+QPlaceSearchReplyImpl::~QPlaceSearchReplyImpl()
+{
+}
+
+void QPlaceSearchReplyImpl::abort()
+{
+ if (m_reply)
+ m_reply->abort();
+}
+
+void QPlaceSearchReplyImpl::setError(QPlaceReply::Error errorCode, const QString &errorString)
+{
+ QPlaceReply::setError(errorCode, errorString);
+ emit error(errorCode, errorString);
+ setFinished(true);
+ emit finished();
+}
+
+static QGeoRectangle parseBoundingBox(const QJsonArray &coordinates)
+{
+ if (coordinates.count() != 4)
+ return QGeoRectangle();
+
+ double bottom = coordinates.at(0).toString().toDouble();
+ double top = coordinates.at(1).toString().toDouble();
+ double left = coordinates.at(2).toString().toDouble();
+ double right = coordinates.at(3).toString().toDouble();
+
+ return QGeoRectangle(QGeoCoordinate(top, left), QGeoCoordinate(bottom, right));
+}
+
+void QPlaceSearchReplyImpl::replyFinished()
+{
+ QNetworkReply *reply = m_reply;
+ m_reply->deleteLater();
+ m_reply = 0;
+
+ if (reply->error() != QNetworkReply::NoError) {
+ setError(CommunicationError, tr("Communication error"));
+ return;
+ }
+
+ QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
+ if (!document.isArray()) {
+ setError(ParseError, tr("Response parse error"));
+ return;
+ }
+
+ QJsonArray resultsArray = document.array();
+
+ QGeoCoordinate searchCenter = request().searchArea().center();
+
+ QStringList placeIds;
+
+ QList<QPlaceSearchResult> results;
+ for (int i = 0; i < resultsArray.count(); ++i) {
+ QJsonObject item = resultsArray.at(i).toObject();
+ QPlaceResult pr = parsePlaceResult(item);
+ pr.setDistance(searchCenter.distanceTo(pr.place().location().coordinate()));
+ placeIds.append(pr.place().placeId());
+ results.append(pr);
+ }
+
+ QVariantMap searchContext = request().searchContext().toMap();
+ QStringList excludePlaceIds =
+ searchContext.value(QStringLiteral("ExcludePlaceIds")).toStringList();
+
+ if (!excludePlaceIds.isEmpty()) {
+ QPlaceSearchRequest r = request();
+ QVariantMap parameters = searchContext;
+
+ QStringList epi = excludePlaceIds;
+ epi.removeLast();
+
+ parameters.insert(QStringLiteral("ExcludePlaceIds"), epi);
+ r.setSearchContext(parameters);
+ setPreviousPageRequest(r);
+ }
+
+ if (!placeIds.isEmpty()) {
+ QPlaceSearchRequest r = request();
+ QVariantMap parameters = searchContext;
+
+ QStringList epi = excludePlaceIds;
+ epi.append(placeIds.join(QLatin1Char(',')));
+
+ parameters.insert(QStringLiteral("ExcludePlaceIds"), epi);
+ r.setSearchContext(parameters);
+ setNextPageRequest(r);
+ }
+
+ setResults(results);
+
+ setFinished(true);
+ emit finished();
+}
+
+QPlaceResult QPlaceSearchReplyImpl::parsePlaceResult(const QJsonObject &item) const
+{
+ QPlace place;
+
+ QGeoCoordinate coordinate = QGeoCoordinate(item.value(QStringLiteral("lat")).toString().toDouble(),
+ item.value(QStringLiteral("lon")).toString().toDouble());
+
+ //const QString placeRank = item.value(QStringLiteral("place_rank")).toString();
+ //const QString category = item.value(QStringLiteral("category")).toString();
+ const QString type = item.value(QStringLiteral("type")).toString();
+ //double importance = item.value(QStringLiteral("importance")).toDouble();
+
+ place.setAttribution(item.value(QStringLiteral("licence")).toString());
+ place.setPlaceId(item.value(QStringLiteral("place_id")).toString());
+
+ QVariantMap iconParameters;
+ iconParameters.insert(QPlaceIcon::SingleUrl,
+ QUrl(item.value(QStringLiteral("icon")).toString()));
+ QPlaceIcon icon;
+ icon.setParameters(iconParameters);
+ place.setIcon(icon);
+
+ QJsonObject addressDetails = item.value(QStringLiteral("address")).toObject();
+
+ const QString title = addressDetails.value(type).toString();
+
+ place.setName(title);
+
+ QGeoAddress address;
+ address.setCity(addressDetails.value(QStringLiteral("city")).toString());
+ address.setCountry(addressDetails.value(QStringLiteral("country")).toString());
+ // FIXME: country_code is alpha-2 setCountryCode takes alpha-3
+ //address.setCountryCode(addressDetails.value(QStringLiteral("country_code")).toString());
+ address.setPostalCode(addressDetails.value(QStringLiteral("postcode")).toString());
+ address.setStreet(addressDetails.value(QStringLiteral("road")).toString());
+ address.setState(addressDetails.value(QStringLiteral("state")).toString());
+ address.setDistrict(addressDetails.value(QStringLiteral("suburb")).toString());
+
+ QGeoLocation location;
+ location.setCoordinate(coordinate);
+ location.setAddress(address);
+ location.setBoundingBox(parseBoundingBox(item.value(QStringLiteral("boundingbox")).toArray()));
+
+ place.setLocation(location);
+
+ QPlaceResult result;
+ result.setIcon(icon);
+ result.setPlace(place);
+ result.setTitle(title);
+
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/geoservices/osm/qplacesearchreplyimpl.h b/src/plugins/geoservices/osm/qplacesearchreplyimpl.h
new file mode 100644
index 00000000..b2c67e71
--- /dev/null
+++ b/src/plugins/geoservices/osm/qplacesearchreplyimpl.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Aaron McCarthy <mccarthy.aaron@gmail.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtFoo module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPLACESEARCHREPLYIMPL_H
+#define QPLACESEARCHREPLYIMPL_H
+
+#include <QtLocation/QPlaceSearchReply>
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkReply;
+class QPlaceManagerEngineOsm;
+class QPlaceResult;
+
+class QPlaceSearchReplyImpl : public QPlaceSearchReply
+{
+ Q_OBJECT
+
+public:
+ QPlaceSearchReplyImpl(const QPlaceSearchRequest &request, QNetworkReply *reply,
+ QPlaceManagerEngineOsm *parent);
+ ~QPlaceSearchReplyImpl();
+
+ void abort();
+
+private slots:
+ void setError(QPlaceReply::Error errorCode, const QString &errorString);
+ void replyFinished();
+
+private:
+ QPlaceResult parsePlaceResult(const QJsonObject &item) const;
+
+ QNetworkReply *m_reply;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPLACESEARCHREPLYIMPL_H