diff options
Diffstat (limited to 'platform/qt')
24 files changed, 1132 insertions, 120 deletions
diff --git a/platform/qt/app/mapwindow.cpp b/platform/qt/app/mapwindow.cpp index 0b453406f3..e29e62f157 100644 --- a/platform/qt/app/mapwindow.cpp +++ b/platform/qt/app/mapwindow.cpp @@ -203,6 +203,85 @@ void MapWindow::keyPressEvent(QKeyEvent *ev) m_map->setLayoutProperty("road-label-small", "text-size", 30.0); } break; + case Qt::Key_1: { + if (m_symbolAnnotationId.isNull()) { + QMapbox::Coordinate coordinate = m_map->coordinate(); + QMapbox::SymbolAnnotation symbol { coordinate, "default_marker" }; + m_map->addAnnotationIcon("default_marker", QImage(":default_marker.svg")); + m_symbolAnnotationId = m_map->addAnnotation(QVariant::fromValue<QMapbox::SymbolAnnotation>(symbol)); + } else { + m_map->removeAnnotation(m_symbolAnnotationId.toUInt()); + m_symbolAnnotationId.clear(); + } + } + break; + case Qt::Key_2: { + if (m_lineAnnotationId.isNull()) { + QMapbox::Coordinate topLeft = m_map->coordinateForPixel({ 0, 0 }); + QMapbox::Coordinate bottomRight = m_map->coordinateForPixel({ qreal(size().width()), qreal(size().height()) }); + QMapbox::CoordinatesCollections geometry { { { topLeft, bottomRight } } }; + QMapbox::LineAnnotation line { { QMapbox::ShapeAnnotationGeometry::LineStringType, geometry }, 0.5f, 1.0f, Qt::red }; + m_lineAnnotationId = m_map->addAnnotation(QVariant::fromValue<QMapbox::LineAnnotation>(line)); + } else { + m_map->removeAnnotation(m_lineAnnotationId.toUInt()); + m_lineAnnotationId.clear(); + } + } + break; + case Qt::Key_3: { + if (m_fillAnnotationId.isNull()) { + QMapbox::Coordinate topLeft = m_map->coordinateForPixel({ 0, 0 }); + QMapbox::Coordinate topRight = m_map->coordinateForPixel({ 0, qreal(size().height()) }); + QMapbox::Coordinate bottomLeft = m_map->coordinateForPixel({ qreal(size().width()), 0 }); + QMapbox::Coordinate bottomRight = m_map->coordinateForPixel({ qreal(size().width()), qreal(size().height()) }); + QMapbox::CoordinatesCollections geometry { { { bottomLeft, bottomRight, topRight, topLeft, bottomLeft } } }; + QMapbox::FillAnnotation fill { { QMapbox::ShapeAnnotationGeometry::PolygonType, geometry }, 0.5f, Qt::green, QVariant::fromValue<QColor>(QColor(Qt::black)) }; + m_fillAnnotationId = m_map->addAnnotation(QVariant::fromValue<QMapbox::FillAnnotation>(fill)); + } else { + m_map->removeAnnotation(m_fillAnnotationId.toUInt()); + m_fillAnnotationId.clear(); + } + } + break; + case Qt::Key_4: { + if (m_styleSourcedAnnotationId.isNull()) { + QMapbox::Coordinate topLeft = m_map->coordinateForPixel({ 0, 0 }); + QMapbox::Coordinate topRight = m_map->coordinateForPixel({ 0, qreal(size().height()) }); + QMapbox::Coordinate bottomLeft = m_map->coordinateForPixel({ qreal(size().width()), 0 }); + QMapbox::Coordinate bottomRight = m_map->coordinateForPixel({ qreal(size().width()), qreal(size().height()) }); + QMapbox::CoordinatesCollections geometry { { { bottomLeft, bottomRight, topRight, topLeft, bottomLeft } } }; + QMapbox::StyleSourcedAnnotation styleSourced { { QMapbox::ShapeAnnotationGeometry::PolygonType, geometry }, "water" }; + m_styleSourcedAnnotationId = m_map->addAnnotation(QVariant::fromValue<QMapbox::StyleSourcedAnnotation>(styleSourced)); + } else { + m_map->removeAnnotation(m_styleSourcedAnnotationId.toUInt()); + m_styleSourcedAnnotationId.clear(); + } + } + break; + case Qt::Key_5: { + if (m_map->layerExists("circleLayer")) { + m_map->removeLayer("circleLayer"); + m_map->removeSource("circleSource"); + } else { + QMapbox::CoordinatesCollections geometry { { { m_map->coordinate() } } }; + QMapbox::Feature feature { QMapbox::Feature::PointType, geometry, {}, {} }; + + QVariantMap circleSource; + circleSource["type"] = "geojson"; + circleSource["data"] = QVariant::fromValue<QMapbox::Feature>(feature); + m_map->addSource("circleSource", circleSource); + + QVariantMap circle; + circle["id"] = "circleLayer"; + circle["type"] = "circle"; + circle["source"] = "circleSource"; + m_map->addLayer(circle); + + m_map->setPaintProperty("circleLayer", "circle-radius", 10.0); + m_map->setPaintProperty("circleLayer", "circle-color", QColor("black")); + } + } + break; case Qt::Key_Tab: m_map->cycleDebugOptions(); break; diff --git a/platform/qt/app/mapwindow.hpp b/platform/qt/app/mapwindow.hpp index 23880902d3..6b4b7fd1cc 100644 --- a/platform/qt/app/mapwindow.hpp +++ b/platform/qt/app/mapwindow.hpp @@ -61,6 +61,11 @@ private: unsigned m_animationTicks = 0; unsigned m_frameDraws = 0; + QVariant m_symbolAnnotationId; + QVariant m_lineAnnotationId; + QVariant m_fillAnnotationId; + QVariant m_styleSourcedAnnotationId; + bool m_sourceAdded = false; }; diff --git a/platform/qt/config.cmake b/platform/qt/config.cmake index e1c454c757..e97c6b340a 100644 --- a/platform/qt/config.cmake +++ b/platform/qt/config.cmake @@ -1,7 +1,7 @@ include(platform/qt/qt.cmake) mason_use(sqlite VERSION 3.14.2) -mason_use(gtest VERSION 1.7.0${MASON_CXXABI_SUFFIX}) +mason_use(gtest VERSION 1.8.0${MASON_CXXABI_SUFFIX}) if(NOT WITH_QT_DECODERS) mason_use(libjpeg-turbo VERSION 1.5.0) @@ -23,8 +23,6 @@ macro(mbgl_platform_core) PRIVATE platform/qt/include ) - target_add_mason_package(mbgl-core PRIVATE sqlite) - target_link_libraries(mbgl-core ${MBGL_QT_LIBRARIES} ) diff --git a/platform/qt/config.qdocconf b/platform/qt/config.qdocconf index 7dbe6423eb..b8ade81bb2 100644 --- a/platform/qt/config.qdocconf +++ b/platform/qt/config.qdocconf @@ -4,10 +4,13 @@ include($QT_INSTALL_DOCS/global/compat.qdocconf) include($QT_INSTALL_DOCS/global/fileextensions.qdocconf) include($QT_INSTALL_DOCS/global/qt-html-templates-online.qdocconf) +Cpp.ignoretokens = Q_DECL_EXPORT + project = QMapboxGL description = Mapbox Qt SDK language = Cpp +outputdir = docs headerdirs += include sourcedirs += src diff --git a/platform/qt/include/QQuickMapboxGL b/platform/qt/include/QQuickMapboxGL new file mode 100644 index 0000000000..db109a1d3a --- /dev/null +++ b/platform/qt/include/QQuickMapboxGL @@ -0,0 +1 @@ +#include "qquickmapboxgl.hpp" diff --git a/platform/qt/include/QQuickMapboxGLMapParameter b/platform/qt/include/QQuickMapboxGLMapParameter new file mode 100644 index 0000000000..603fb2bd51 --- /dev/null +++ b/platform/qt/include/QQuickMapboxGLMapParameter @@ -0,0 +1 @@ +#include "qquickmapboxglmapparameter.hpp" diff --git a/platform/qt/include/qmapbox.hpp b/platform/qt/include/qmapbox.hpp index b2e3b521d0..f2d96412a9 100644 --- a/platform/qt/include/qmapbox.hpp +++ b/platform/qt/include/qmapbox.hpp @@ -1,6 +1,7 @@ #ifndef QMAPBOX_H #define QMAPBOX_H +#include <QColor> #include <QList> #include <QPair> #include <QVariant> @@ -11,19 +12,65 @@ namespace QMapbox { typedef QPair<double, double> Coordinate; -typedef QList<Coordinate> LineString; - typedef QPair<Coordinate, double> CoordinateZoom; +typedef QPair<double, double> ProjectedMeters; + +typedef QList<Coordinate> Coordinates; +typedef QList<Coordinates> CoordinatesCollection; + +typedef QList<CoordinatesCollection> CoordinatesCollections; + +struct Q_DECL_EXPORT Feature { + enum Type { + PointType = 1, + LineStringType, + PolygonType + }; + Type type; + CoordinatesCollections geometry; + QVariantMap properties; + QVariant id; +}; -typedef quint32 AnnotationID; -typedef QList<AnnotationID> AnnotationIDs; +struct Q_DECL_EXPORT ShapeAnnotationGeometry { + enum Type { + LineStringType, + PolygonType, + MultiLineStringType, + MultiPolygonType + }; + Type type; + CoordinatesCollections geometry; +}; + +struct Q_DECL_EXPORT SymbolAnnotation { + Coordinate geometry; + QString icon; +}; -typedef QPair<Coordinate, QString> PointAnnotation; +struct Q_DECL_EXPORT LineAnnotation { + ShapeAnnotationGeometry geometry; + float opacity = 1.0f; + float width = 1.0f; + QColor color = Qt::black; +}; + +struct Q_DECL_EXPORT FillAnnotation { + ShapeAnnotationGeometry geometry; + float opacity = 1.0f; + QColor color = Qt::black; + QVariant outlineColor; +}; -// FIXME: We need to add support for custom style properties -typedef QPair<LineString, QString> ShapeAnnotation; +struct Q_DECL_EXPORT StyleSourcedAnnotation { + ShapeAnnotationGeometry geometry; + QString layerID; +}; + +typedef QVariant Annotation; +typedef quint32 AnnotationID; +typedef QList<AnnotationID> AnnotationIDs; -// Reflects mbgl::NetworkStatus::Status. enum NetworkMode { Online, // Default Offline, @@ -51,10 +98,19 @@ typedef void (*CustomLayerRenderFunction)(void* context, const CustomLayerRender typedef void (*CustomLayerDeinitializeFunction)(void* context); Q_DECL_EXPORT void initializeGLExtensions(); -Q_DECL_EXPORT void registerTypes(); } // namespace QMapbox Q_DECLARE_METATYPE(QMapbox::Coordinate); +Q_DECLARE_METATYPE(QMapbox::Coordinates); +Q_DECLARE_METATYPE(QMapbox::CoordinatesCollection); +Q_DECLARE_METATYPE(QMapbox::CoordinatesCollections); +Q_DECLARE_METATYPE(QMapbox::Feature); + +Q_DECLARE_METATYPE(QMapbox::SymbolAnnotation); +Q_DECLARE_METATYPE(QMapbox::ShapeAnnotationGeometry); +Q_DECLARE_METATYPE(QMapbox::LineAnnotation); +Q_DECLARE_METATYPE(QMapbox::FillAnnotation); +Q_DECLARE_METATYPE(QMapbox::StyleSourcedAnnotation); #endif // QMAPBOX_H diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp index be2d60608b..af7ed6275f 100644 --- a/platform/qt/include/qmapboxgl.hpp +++ b/platform/qt/include/qmapboxgl.hpp @@ -10,10 +10,6 @@ #include <QString> #include <QStringList> -#if QT_VERSION >= 0x050000 -#include <QOpenGLFramebufferObject> -#endif - class QMapboxGLPrivate; // This header follows the Qt coding style: https://wiki.qt.io/Qt_Coding_Style @@ -183,11 +179,8 @@ public: void addAnnotationIcon(const QString &name, const QImage &sprite); - QMapbox::AnnotationID addPointAnnotation(const QMapbox::PointAnnotation &); - QMapbox::AnnotationID addShapeAnnotation(const QMapbox::ShapeAnnotation &); - - void updatePointAnnotation(QMapbox::AnnotationID, const QMapbox::PointAnnotation &); - + QMapbox::AnnotationID addAnnotation(const QMapbox::Annotation &); + void updateAnnotation(QMapbox::AnnotationID, const QMapbox::Annotation &); void removeAnnotation(QMapbox::AnnotationID); void setLayoutProperty(const QString &layer, const QString &property, const QVariant &value); @@ -201,6 +194,9 @@ public: void resize(const QSize &size, const QSize &framebufferSize); + double metersPerPixelAtLatitude(double latitude, double zoom) const; + QMapbox::ProjectedMeters projectedMetersForCoordinate(const QMapbox::Coordinate &) const; + QMapbox::Coordinate coordinateForProjectedMeters(const QMapbox::ProjectedMeters &) const; QPointF pixelForCoordinate(const QMapbox::Coordinate &) const; QMapbox::Coordinate coordinateForPixel(const QPointF &) const; @@ -211,6 +207,7 @@ public: QMargins margins() const; void addSource(const QString &sourceID, const QVariantMap& params); + bool sourceExists(const QString &sourceID); void updateSource(const QString &sourceID, const QVariantMap& params); void removeSource(const QString &sourceID); @@ -224,16 +221,13 @@ public: void* context, char* before = NULL); void addLayer(const QVariantMap ¶ms); + bool layerExists(const QString &id); void removeLayer(const QString &id); void setFilter(const QString &layer, const QVariant &filter); public slots: -#if QT_VERSION >= 0x050000 - void render(QOpenGLFramebufferObject *fbo = NULL); -#else void render(); -#endif void connectionEstablished(); signals: diff --git a/platform/qt/src/qquickmapboxgl.hpp b/platform/qt/include/qquickmapboxgl.hpp index 39b4395bd6..39b4395bd6 100644 --- a/platform/qt/src/qquickmapboxgl.hpp +++ b/platform/qt/include/qquickmapboxgl.hpp diff --git a/platform/qt/src/qquickmapboxglmapparameter.hpp b/platform/qt/include/qquickmapboxglmapparameter.hpp index 1dca0cf55d..1dca0cf55d 100644 --- a/platform/qt/src/qquickmapboxglmapparameter.hpp +++ b/platform/qt/include/qquickmapboxglmapparameter.hpp diff --git a/platform/qt/qmlapp/main.cpp b/platform/qt/qmlapp/main.cpp index 0dd8c96b7d..8606704002 100644 --- a/platform/qt/qmlapp/main.cpp +++ b/platform/qt/qmlapp/main.cpp @@ -1,4 +1,5 @@ -#include <QMapbox> +#include <QQuickMapboxGL> +#include <QQuickMapboxGLMapParameter> #include <QGuiApplication> #include <QIcon> @@ -13,9 +14,8 @@ int main(int argc, char *argv[]) app.setWindowIcon(QIcon(":icon.png")); #endif - // Exposes the QQuickMapboxGL module so we - // can do `import QQuickMapboxGL 1.0`. - QMapbox::registerTypes(); + qmlRegisterType<QQuickMapboxGL>("QQuickMapboxGL", 1, 0, "MapboxMap"); + qmlRegisterType<QQuickMapboxGLMapParameter>("QQuickMapboxGL", 1, 0, "MapParameter"); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); diff --git a/platform/qt/qt.cmake b/platform/qt/qt.cmake index bd7c863f98..cee0d1080c 100644 --- a/platform/qt/qt.cmake +++ b/platform/qt/qt.cmake @@ -25,15 +25,16 @@ set(MBGL_QT_FILES PRIVATE platform/default/mbgl/storage/offline_database.hpp PRIVATE platform/default/mbgl/storage/offline_download.cpp PRIVATE platform/default/mbgl/storage/offline_download.hpp - PRIVATE platform/default/sqlite3.cpp PRIVATE platform/default/sqlite3.hpp # Misc PRIVATE platform/default/logging_stderr.cpp # Thread pool + PRIVATE platform/default/mbgl/util/shared_thread_pool.cpp + PRIVATE platform/default/mbgl/util/shared_thread_pool.hpp PRIVATE platform/default/mbgl/util/default_thread_pool.cpp - PRIVATE platform/default/mbgl/util/default_thread_pool.cpp + PRIVATE platform/default/mbgl/util/default_thread_pool.hpp # Platform integration PRIVATE platform/qt/src/async_task.cpp @@ -45,6 +46,7 @@ set(MBGL_QT_FILES PRIVATE platform/qt/src/image.cpp PRIVATE platform/qt/src/run_loop.cpp PRIVATE platform/qt/src/run_loop_impl.hpp + PRIVATE platform/qt/src/sqlite3.cpp PRIVATE platform/qt/src/string_stdlib.cpp PRIVATE platform/qt/src/timer.cpp PRIVATE platform/qt/src/timer_impl.hpp diff --git a/platform/qt/qt4.cmake b/platform/qt/qt4.cmake index 45c299c8a8..d6d7d89417 100644 --- a/platform/qt/qt4.cmake +++ b/platform/qt/qt4.cmake @@ -5,6 +5,7 @@ set(MBGL_QT_LIBRARIES PRIVATE Qt4::QtGui PRIVATE Qt4::QtNetwork PRIVATE Qt4::QtOpenGL + PRIVATE Qt4::QtSql ) target_link_libraries(qmapboxgl diff --git a/platform/qt/qt5.cmake b/platform/qt/qt5.cmake index 47e178d132..7210a3d5f5 100644 --- a/platform/qt/qt5.cmake +++ b/platform/qt/qt5.cmake @@ -5,21 +5,21 @@ find_package(Qt5Network REQUIRED) find_package(Qt5OpenGL REQUIRED) find_package(Qt5Quick REQUIRED) find_package(Qt5Widgets REQUIRED) +find_package(Qt5Sql REQUIRED) set(MBGL_QT_LIBRARIES PRIVATE Qt5::Core PRIVATE Qt5::Gui - PRIVATE Qt5::Location PRIVATE Qt5::Network PRIVATE Qt5::OpenGL - PRIVATE Qt5::Quick + PRIVATE Qt5::Sql ) target_sources(qmapboxgl + PRIVATE platform/qt/include/qquickmapboxgl.hpp + PRIVATE platform/qt/include/qquickmapboxglmapparameter.hpp PRIVATE platform/qt/src/qquickmapboxgl.cpp - PRIVATE platform/qt/src/qquickmapboxgl.hpp PRIVATE platform/qt/src/qquickmapboxglmapparameter.cpp - PRIVATE platform/qt/src/qquickmapboxglmapparameter.hpp PRIVATE platform/qt/src/qquickmapboxglrenderer.cpp PRIVATE platform/qt/src/qquickmapboxglrenderer.hpp ) @@ -31,6 +31,7 @@ target_link_libraries(qmapboxgl PRIVATE Qt5::Location PRIVATE Qt5::OpenGL PRIVATE Qt5::Quick + PRIVATE Qt5::Sql ) target_link_libraries(mbgl-qt diff --git a/platform/qt/resources/common.qrc b/platform/qt/resources/common.qrc index d02c04a3c2..4c792b057c 100644 --- a/platform/qt/resources/common.qrc +++ b/platform/qt/resources/common.qrc @@ -1,6 +1,7 @@ <RCC> <qresource prefix="/"> <file alias="icon.png">../../../common/icon.png</file> + <file alias="default_marker.svg">../../../platform/default/resources/default_marker.svg</file> <file>source1.geojson</file> <file>source2.geojson</file> <file>label-arrow.svg</file> diff --git a/platform/qt/src/http_file_source.cpp b/platform/qt/src/http_file_source.cpp index 6831c040b0..573b707c27 100644 --- a/platform/qt/src/http_file_source.cpp +++ b/platform/qt/src/http_file_source.cpp @@ -108,4 +108,4 @@ uint32_t HTTPFileSource::maximumConcurrentRequests() { #endif } -} // mbgl +} // namespace mbgl diff --git a/platform/qt/src/http_request.cpp b/platform/qt/src/http_request.cpp index e19316fe2f..6141216c65 100644 --- a/platform/qt/src/http_request.cpp +++ b/platform/qt/src/http_request.cpp @@ -118,7 +118,7 @@ void HTTPRequest::handleNetworkReply(QNetworkReply *reply) } case 429: response.error = std::make_unique<Error>( - Error::Reason::RateLimit, "HTTP status code 429", + Error::Reason::RateLimit, "HTTP status code 429", http::parseRetryHeaders(retryAfter, xRateLimitReset)); break; default: diff --git a/platform/qt/src/qmapbox.cpp b/platform/qt/src/qmapbox.cpp index 379b0cdd57..62bfde4fa8 100644 --- a/platform/qt/src/qmapbox.cpp +++ b/platform/qt/src/qmapbox.cpp @@ -4,11 +4,10 @@ #include <mbgl/map/change.hpp> #include <mbgl/storage/network_status.hpp> #include <mbgl/util/default_styles.hpp> +#include <mbgl/util/geometry.hpp> #include <mbgl/util/traits.hpp> #if QT_VERSION >= 0x050000 -#include "qquickmapboxgl.hpp" -#include "qquickmapboxglmapparameter.hpp" #include <QOpenGLContext> #else #include <QGLContext> @@ -18,19 +17,232 @@ static_assert(mbgl::underlying_type(QMapbox::Online) == mbgl::underlying_type(mbgl::NetworkStatus::Status::Online), "error"); static_assert(mbgl::underlying_type(QMapbox::Offline) == mbgl::underlying_type(mbgl::NetworkStatus::Status::Offline), "error"); +// mbgl::FeatureType +static_assert(mbgl::underlying_type(QMapbox::Feature::PointType) == mbgl::underlying_type(mbgl::FeatureType::Point), "error"); +static_assert(mbgl::underlying_type(QMapbox::Feature::LineStringType) == mbgl::underlying_type(mbgl::FeatureType::LineString), "error"); +static_assert(mbgl::underlying_type(QMapbox::Feature::PolygonType) == mbgl::underlying_type(mbgl::FeatureType::Polygon), "error"); + namespace QMapbox { +/*! + \namespace QMapbox + \inmodule Mapbox Qt SDK + + Contains miscellaneous Mapbox bindings used throughout QMapboxGL. +*/ + +/*! + \typedef QMapbox::Coordinate + + Alias for QPair<double, double>. + Representation for geographical coordinates - latitude and longitude, respectively. +*/ + +/*! + \typedef QMapbox::CoordinateZoom + + Alias for QPair<Coordinate, double>. + Used as return value in QMapboxGL::coordinateZoomForBounds. +*/ + +/*! + \typedef QMapbox::ProjectedMeters + + Alias for QPair<double, double>. + Representation for projected meters - northing and easting, respectively. +*/ + +/*! + \typedef QMapbox::Coordinates + + Alias for QList<QMapbox::Coordinate>. + A list of QMapbox::Coordinate objects. +*/ + +/*! + \typedef QMapbox::CoordinatesCollection + + Alias for QList<QMapbox::Coordinates>. + A list of QMapbox::Coordinates objects. +*/ + +/*! + \typedef QMapbox::CoordinatesCollections + + Alias for QList<QMapbox::CoordinatesCollection>. + A list of QMapbox::CoordinatesCollection objects. +*/ + +/*! + \class QMapbox::Feature + + \inmodule Mapbox Qt SDK + + Represents \l {https://www.mapbox.com/help/define-features/}{map features} + via its \a type (PointType, LineStringType or PolygonType), \a geometry, \a + properties map and \a id (optional). +*/ + +/*! + \enum QMapbox::Feature::Type + + This enum is used as basis for geometry disambiguation in QMapbox::Feature. + + \value PointType A point geometry type. Means a single or a collection of points. + \value LineStringType A line string geometry type. Means a single or a collection of line strings. + \value PolygonType A polygon geometry type. Means a single or a collection of polygons. +*/ + +/*! + \class QMapbox::ShapeAnnotationGeometry + + \inmodule Mapbox Qt SDK + + Represents a shape annotation geometry. +*/ + +/*! + \enum QMapbox::ShapeAnnotationGeometry::Type + + This enum is used as basis for shape annotation geometry disambiguation. + + \value PolygonType A polygon geometry type. + \value LineStringType A line string geometry type. + \value MultiPolygonType A polygon geometry collection type. + \value MultiLineStringType A line string geometry collection type. +*/ + +/*! + \class QMapbox::SymbolAnnotation + + \inmodule Mapbox Qt SDK + + A symbol annotation comprises of its geometry and an icon identifier. +*/ + +/*! + \class QMapbox::LineAnnotation + + \inmodule Mapbox Qt SDK + + Represents a line annotation object, along with its properties. + + A line annotation comprises of its geometry and line properties such as opacity, width and color. +*/ + +/*! + \class QMapbox::FillAnnotation + + \inmodule Mapbox Qt SDK + + Represents a fill annotation object, along with its properties. + + A fill annotation comprises of its geometry and fill properties such as opacity, color and outline color. +*/ + +/*! + \class QMapbox::StyleSourcedAnnotation + + \inmodule Mapbox Qt SDK + + Represents a style sourced annotation object, along with its properties. + + A style sourced annotation comprises of its geometry and a layer identifier. +*/ + +/*! + \typedef QMapbox::Annotation + + Alias for QVariant. + Container that encapsulates either a symbol, a line, a fill or a style sourced annotation. +*/ + +/*! + \typedef QMapbox::AnnotationID + + Alias for quint32 representing an annotation identifier. +*/ + +/*! + \typedef QMapbox::AnnotationIDs + + Alias for QList<quint32> representing a container of annotation identifiers. +*/ + +/*! + \typedef QMapbox::CustomLayerDeinitializeFunction + + Represents a callback to be called when destroying a custom layer. + + \warning This is used for delegating the rendering of a layer to the user of + this API and is not officially supported. Use at your own risk. +*/ + +/*! + \typedef QMapbox::CustomLayerInitializeFunction + + Represents a callback to be called when initializing a custom layer. + + \warning This is used for delegating the rendering of a layer to the user of + this API and is not officially supported. Use at your own risk. +*/ + +/*! + \typedef QMapbox::CustomLayerRenderFunction + + Represents a callback to be called on each render pass for a custom layer. + + \warning This is used for delegating the rendering of a layer to the user of + this API and is not officially supported. Use at your own risk. +*/ + +/*! + \enum QMapbox::NetworkMode + + This enum represents whether server requests can be performed via network. + + \value Online Server network requests are accessible. + \value Offline Only requests to the local cache are accessible. +*/ + +/*! + \class QMapbox::CustomLayerRenderParameters + \inmodule Mapbox Qt SDK + + QMapbox::CustomLayerRenderParameters provides the data passed on each render + pass for a custom layer. +*/ + +/*! + \fn QMapbox::NetworkMode QMapbox::networkMode() + + Returns the current QMapbox::NetworkMode. +*/ Q_DECL_EXPORT NetworkMode networkMode() { return static_cast<NetworkMode>(mbgl::NetworkStatus::Get()); } +/*! + \fn void QMapbox::setNetworkMode(QMapbox::NetworkMode mode) + + Forwards the network status \a mode to Mapbox GL Native engine. + + File source requests uses the available network when \a mode is set to \a + Online, otherwise scoped to the local cache. +*/ Q_DECL_EXPORT void setNetworkMode(NetworkMode mode) { mbgl::NetworkStatus::Set(static_cast<mbgl::NetworkStatus::Status>(mode)); } -Q_DECL_EXPORT QList<QPair<QString, QString>>& defaultStyles() +/*! + \fn QList<QPair<QString, QString> >& QMapbox::defaultStyles() + + Returns a list containing a pair of string objects, representing the style + URL and name, respectively. +*/ +Q_DECL_EXPORT QList<QPair<QString, QString> >& defaultStyles() { static QList<QPair<QString, QString>> styles; @@ -44,6 +256,15 @@ Q_DECL_EXPORT QList<QPair<QString, QString>>& defaultStyles() return styles; } +/*! + \fn void QMapbox::initializeGLExtensions() + + Initializes the OpenGL extensions such as Vertex Array Objects (VAOs), + required by Mapbox GL Native engine. + + Should be called only once, after an OpenGL context is available. + Consecutive calls are ignored. +*/ Q_DECL_EXPORT void initializeGLExtensions() { mbgl::gl::InitializeExtensions([](const char* name) { @@ -57,12 +278,4 @@ Q_DECL_EXPORT void initializeGLExtensions() }); } -Q_DECL_EXPORT void registerTypes() -{ -#if QT_VERSION >= 0x050000 - qmlRegisterType<QQuickMapboxGL>("QQuickMapboxGL", 1, 0, "MapboxMap"); - qmlRegisterType<QQuickMapboxGLMapParameter>("QQuickMapboxGL", 1, 0, "MapParameter"); -#endif -} - -} +} // namespace QMapbox diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index 86604a945f..55470a65e1 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -17,15 +17,17 @@ #include <mbgl/style/transition_options.hpp> #include <mbgl/sprite/sprite_image.hpp> #include <mbgl/storage/network_status.hpp> +#include <mbgl/util/color.hpp> #include <mbgl/util/constants.hpp> #include <mbgl/util/geo.hpp> +#include <mbgl/util/geometry.hpp> #include <mbgl/util/run_loop.hpp> +#include <mbgl/util/shared_thread_pool.hpp> #include <mbgl/util/traits.hpp> #if QT_VERSION >= 0x050000 #include <QGuiApplication> #include <QWindow> -#include <QOpenGLFramebufferObject> #include <QOpenGLContext> #else #include <QCoreApplication> @@ -85,20 +87,6 @@ QThreadStorage<std::shared_ptr<mbgl::util::RunLoop>> loop; // Conversion helper functions. -auto fromQMapboxGLShapeAnnotation(const ShapeAnnotation &shapeAnnotation) { - const LineString &lineString = shapeAnnotation.first; - const QString &styleLayer = shapeAnnotation.second; - - mbgl::LineString<double> mbglLineString; - mbglLineString.reserve(lineString.size()); - - for (const Coordinate &coordinate : lineString) { - mbglLineString.emplace_back(mbgl::Point<double> { coordinate.first, coordinate.second }); - } - - return mbgl::StyleSourcedAnnotation { std::move(mbglLineString), styleLayer.toStdString() }; -} - auto fromQStringList(const QStringList &list) { std::vector<std::string> strings; @@ -848,42 +836,72 @@ void QMapboxGL::setTransitionOptions(qint64 duration, qint64 delay) { d_ptr->mapObj->setTransitionOptions(mbgl::style::TransitionOptions{ convert(duration), convert(delay) }); } -mbgl::Annotation fromPointAnnotation(const PointAnnotation &pointAnnotation) { - const Coordinate &coordinate = pointAnnotation.first; - const QString &icon = pointAnnotation.second; - return mbgl::SymbolAnnotation { mbgl::Point<double> { coordinate.second, coordinate.first }, icon.toStdString() }; +mbgl::Annotation asMapboxGLAnnotation(const QMapbox::Annotation & annotation) { + auto asMapboxGLGeometry = [](const QMapbox::ShapeAnnotationGeometry &geometry) { + mbgl::ShapeAnnotationGeometry result; + switch (geometry.type) { + case QMapbox::ShapeAnnotationGeometry::LineStringType: + result = { asMapboxGLLineString(geometry.geometry.first().first()) }; + break; + case QMapbox::ShapeAnnotationGeometry::PolygonType: + result = { asMapboxGLPolygon(geometry.geometry.first()) }; + break; + case QMapbox::ShapeAnnotationGeometry::MultiLineStringType: + result = { asMapboxGLMultiLineString(geometry.geometry.first()) }; + break; + case QMapbox::ShapeAnnotationGeometry::MultiPolygonType: + result = { asMapboxGLMultiPolygon(geometry.geometry) }; + break; + } + return result; + }; + + if (annotation.canConvert<QMapbox::SymbolAnnotation>()) { + QMapbox::SymbolAnnotation symbolAnnotation = annotation.value<QMapbox::SymbolAnnotation>(); + QMapbox::Coordinate& pair = symbolAnnotation.geometry; + return mbgl::SymbolAnnotation { mbgl::Point<double> { pair.second, pair.first }, symbolAnnotation.icon.toStdString() }; + } else if (annotation.canConvert<QMapbox::LineAnnotation>()) { + QMapbox::LineAnnotation lineAnnotation = annotation.value<QMapbox::LineAnnotation>(); + auto color = mbgl::Color::parse(lineAnnotation.color.name().toStdString()); + return mbgl::LineAnnotation { asMapboxGLGeometry(lineAnnotation.geometry), lineAnnotation.opacity, lineAnnotation.width, { *color } }; + } else if (annotation.canConvert<QMapbox::FillAnnotation>()) { + QMapbox::FillAnnotation fillAnnotation = annotation.value<QMapbox::FillAnnotation>(); + auto color = mbgl::Color::parse(fillAnnotation.color.name().toStdString()); + if (fillAnnotation.outlineColor.canConvert<QColor>()) { + auto outlineColor = mbgl::Color::parse(fillAnnotation.outlineColor.value<QColor>().name().toStdString()); + return mbgl::FillAnnotation { asMapboxGLGeometry(fillAnnotation.geometry), fillAnnotation.opacity, { *color }, { *outlineColor } }; + } else { + return mbgl::FillAnnotation { asMapboxGLGeometry(fillAnnotation.geometry), fillAnnotation.opacity, { *color }, {} }; + } + } else if (annotation.canConvert<QMapbox::StyleSourcedAnnotation>()) { + QMapbox::StyleSourcedAnnotation styleSourcedAnnotation = annotation.value<QMapbox::StyleSourcedAnnotation>(); + return mbgl::StyleSourcedAnnotation { asMapboxGLGeometry(styleSourcedAnnotation.geometry), styleSourcedAnnotation.layerID.toStdString() }; + } + + qWarning() << "Unable to convert annotation:" << annotation; + return {}; } /*! - Adds a \a point annotation to the map. + Adds an \a annotation to the map. Returns the unique identifier for the new annotation. \sa addAnnotationIcon() */ -QMapbox::AnnotationID QMapboxGL::addPointAnnotation(const QMapbox::PointAnnotation &point) +QMapbox::AnnotationID QMapboxGL::addAnnotation(const QMapbox::Annotation &annotation) { - return d_ptr->mapObj->addAnnotation(fromPointAnnotation(point)); + return d_ptr->mapObj->addAnnotation(asMapboxGLAnnotation(annotation)); } /*! - Updates an existing \a point annotation referred by \a id. + Updates an existing \a annotation referred by \a id. \sa addAnnotationIcon() */ -void QMapboxGL::updatePointAnnotation(QMapbox::AnnotationID id, const QMapbox::PointAnnotation &point) +void QMapboxGL::updateAnnotation(QMapbox::AnnotationID id, const QMapbox::Annotation &annotation) { - d_ptr->mapObj->updateAnnotation(id, fromPointAnnotation(point)); -} - -/*! - Adds a \a shape annotation to the map. - - Returns the unique identifier for the new annotation. -*/ -QMapbox::AnnotationID QMapboxGL::addShapeAnnotation(const QMapbox::ShapeAnnotation &shape) -{ - return d_ptr->mapObj->addAnnotation(fromQMapboxGLShapeAnnotation(shape)); + d_ptr->mapObj->updateAnnotation(id, asMapboxGLAnnotation(annotation)); } /*! @@ -1087,10 +1105,10 @@ void QMapboxGL::resize(const QSize& size, const QSize& framebufferSize) Adds an \a icon to the annotation icon pool. This can be later used by the annotation functions to shown any drawing on the map by referencing its \a name. - Unlike using addIcon() for runtime styling, annotations added with addPointAnnotation() + Unlike using addIcon() for runtime styling, annotations added with addAnnotation() will survive style changes. - \sa addPointAnnotation() + \sa addAnnotation() */ void QMapboxGL::addAnnotationIcon(const QString &name, const QImage &icon) { @@ -1100,6 +1118,32 @@ void QMapboxGL::addAnnotationIcon(const QString &name, const QImage &icon) } /*! + Returns the amount of meters per pixel from a given \a latitude and \a zoom. +*/ +double QMapboxGL::metersPerPixelAtLatitude(double latitude, double zoom) const +{ + return d_ptr->mapObj->getMetersPerPixelAtLatitude(latitude, zoom); +} + +/*! + Return the projected meters for a given \a coordinate_ object. +*/ +QMapbox::ProjectedMeters QMapboxGL::projectedMetersForCoordinate(const QMapbox::Coordinate &coordinate_) const +{ + auto projectedMeters = d_ptr->mapObj->projectedMetersForLatLng(mbgl::LatLng { coordinate_.first, coordinate_.second }); + return QMapbox::ProjectedMeters(projectedMeters.northing, projectedMeters.easting); +} + +/*! + Returns the coordinate for a given \a projectedMeters object. +*/ +QMapbox::Coordinate QMapboxGL::coordinateForProjectedMeters(const QMapbox::ProjectedMeters &projectedMeters) const +{ + auto latLng = d_ptr->mapObj->latLngForProjectedMeters(mbgl::ProjectedMeters { projectedMeters.first, projectedMeters.second }); + return QMapbox::Coordinate(latLng.latitude, latLng.longitude); +} + +/*! \fn QMapboxGL::pixelForCoordinate(const QMapbox::Coordinate &coordinate) const Returns the offset in pixels for \a coordinate. The origin pixel coordinate is @@ -1226,6 +1270,14 @@ void QMapboxGL::addSource(const QString &id, const QVariantMap ¶ms) } /*! + Returns true if the layer with given \a sourceID exists, false otherwise. +*/ +bool QMapboxGL::sourceExists(const QString& sourceID) +{ + return !!d_ptr->mapObj->getSource(sourceID.toStdString()); +} + +/*! Updates the source \a id with new \a params. If the source does not exist, it will be added like in addSource(). Only @@ -1330,7 +1382,15 @@ void QMapboxGL::addLayer(const QVariantMap ¶ms) } /*! - Removes the layer \a id. + Returns true if the layer with given \a id exists, false otherwise. +*/ +bool QMapboxGL::layerExists(const QString& id) +{ + return !!d_ptr->mapObj->getLayer(id.toStdString()); +} + +/*! + Removes the layer with given \a id. */ void QMapboxGL::removeLayer(const QString& id) { @@ -1431,17 +1491,9 @@ void QMapboxGL::setFilter(const QString& layer, const QVariant& filter) This function should be called only after the signal needsRendering() is emitted at least once. */ -#if QT_VERSION >= 0x050000 -void QMapboxGL::render(QOpenGLFramebufferObject *fbo) -{ - d_ptr->dirty = false; - d_ptr->fbo = fbo; - d_ptr->mapObj->render(*d_ptr); -} -#else void QMapboxGL::render() { -#if defined(__APPLE__) +#if defined(__APPLE__) && QT_VERSION < 0x050000 // FIXME Qt 4.x provides an incomplete FBO at start. // See https://bugreports.qt.io/browse/QTBUG-36802 for details. if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { @@ -1452,7 +1504,6 @@ void QMapboxGL::render() d_ptr->dirty = false; d_ptr->mapObj->render(*d_ptr); } -#endif /*! Informs the map that the network connection has been established, causing @@ -1498,10 +1549,10 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin settings.cacheDatabasePath().toStdString(), settings.assetPath().toStdString(), settings.cacheDatabaseMaximumSize())) - , threadPool(4) + , threadPool(mbgl::sharedThreadPool()) , mapObj(std::make_unique<mbgl::Map>( *this, mbgl::Size{ static_cast<uint32_t>(size.width()), static_cast<uint32_t>(size.height()) }, - pixelRatio, *fileSourceObj, threadPool, + pixelRatio, *fileSourceObj, *threadPool, mbgl::MapMode::Continuous, static_cast<mbgl::GLContextMode>(settings.contextMode()), static_cast<mbgl::ConstrainMode>(settings.constrainMode()), @@ -1521,24 +1572,12 @@ QMapboxGLPrivate::~QMapboxGLPrivate() { } -#if QT_VERSION >= 0x050000 -void QMapboxGLPrivate::bind() { - if (fbo) { - fbo->bind(); - getContext().bindFramebuffer.setDirty(); - getContext().viewport = { - 0, 0, { static_cast<uint32_t>(fbo->width()), static_cast<uint32_t>(fbo->height()) } - }; - } -} -#else void QMapboxGLPrivate::bind() { getContext().bindFramebuffer = 0; getContext().viewport = { 0, 0, { static_cast<uint32_t>(fbSize.width()), static_cast<uint32_t>(fbSize.height()) } }; } -#endif void QMapboxGLPrivate::invalidate() { diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp index e2edf3f96c..ee7bff0872 100644 --- a/platform/qt/src/qmapboxgl_p.hpp +++ b/platform/qt/src/qmapboxgl_p.hpp @@ -38,15 +38,11 @@ public: QMapboxGL *q_ptr { nullptr }; std::unique_ptr<mbgl::DefaultFileSource> fileSourceObj; - mbgl::ThreadPool threadPool; + std::shared_ptr<mbgl::ThreadPool> threadPool; std::unique_ptr<mbgl::Map> mapObj; bool dirty { false }; -#if QT_VERSION >= 0x050000 - QOpenGLFramebufferObject *fbo { nullptr }; -#endif - public slots: void connectionEstablished(); diff --git a/platform/qt/src/qquickmapboxglrenderer.cpp b/platform/qt/src/qquickmapboxglrenderer.cpp index 69c22d35d7..133bed40e2 100644 --- a/platform/qt/src/qquickmapboxglrenderer.cpp +++ b/platform/qt/src/qquickmapboxglrenderer.cpp @@ -38,7 +38,9 @@ QOpenGLFramebufferObject* QQuickMapboxGLRenderer::createFramebufferObject(const void QQuickMapboxGLRenderer::render() { - m_map->render(framebufferObject()); + framebufferObject()->bind(); + m_map->render(); + framebufferObject()->release(); } void QQuickMapboxGLRenderer::synchronize(QQuickFramebufferObject *item) diff --git a/platform/qt/src/qt_conversion.hpp b/platform/qt/src/qt_conversion.hpp index e49b6dbe3e..4b93ca7423 100644 --- a/platform/qt/src/qt_conversion.hpp +++ b/platform/qt/src/qt_conversion.hpp @@ -4,6 +4,8 @@ #include <mbgl/util/feature.hpp> #include <mbgl/util/optional.hpp> +#include <QMapbox> + #include <QColor> #include <QVariant> @@ -28,7 +30,13 @@ inline QVariant arrayMember(const QVariant& value, std::size_t i) { } inline bool isObject(const QVariant& value) { - return value.canConvert(QVariant::Map) || value.type() == QVariant::ByteArray; + return value.canConvert(QVariant::Map) + || value.type() == QVariant::ByteArray +#if QT_VERSION >= 0x050000 + || QString(value.typeName()) == QStringLiteral("QMapbox::Feature"); +#else + || QString(value.typeName()) == QString("QMapbox::Feature"); +#endif } inline optional<QVariant> objectMember(const QVariant& value, const char* key) { diff --git a/platform/qt/src/qt_geojson.hpp b/platform/qt/src/qt_geojson.hpp index 07813623fd..038b051941 100644 --- a/platform/qt/src/qt_geojson.hpp +++ b/platform/qt/src/qt_geojson.hpp @@ -4,19 +4,195 @@ #include <mbgl/style/conversion/geojson.hpp> #include <mbgl/util/rapidjson.hpp> +#include <QMapbox> + #include <QByteArray> +#include <QDebug> #include <QVariant> #include <sstream> #include <string> +namespace QMapbox { + +mbgl::Point<double> asMapboxGLPoint(const QMapbox::Coordinate &coordinate) { + return mbgl::Point<double> { coordinate.second, coordinate.first }; +} + +mbgl::MultiPoint<double> asMapboxGLMultiPoint(const QMapbox::Coordinates &multiPoint) { + mbgl::MultiPoint<double> mbglMultiPoint; + mbglMultiPoint.reserve(multiPoint.size()); + for (const auto &point: multiPoint) { + mbglMultiPoint.emplace_back(asMapboxGLPoint(point)); + } + return mbglMultiPoint; +}; + +mbgl::LineString<double> asMapboxGLLineString(const QMapbox::Coordinates &lineString) { + mbgl::LineString<double> mbglLineString; + mbglLineString.reserve(lineString.size()); + for (const auto &coordinate : lineString) { + mbglLineString.emplace_back(asMapboxGLPoint(coordinate)); + } + return mbglLineString; +}; + +mbgl::MultiLineString<double> asMapboxGLMultiLineString(const QMapbox::CoordinatesCollection &multiLineString) { + mbgl::MultiLineString<double> mbglMultiLineString; + mbglMultiLineString.reserve(multiLineString.size()); + for (const auto &lineString : multiLineString) { + mbglMultiLineString.emplace_back(std::forward<mbgl::LineString<double>>(asMapboxGLLineString(lineString))); + } + return mbglMultiLineString; +}; + +mbgl::Polygon<double> asMapboxGLPolygon(const QMapbox::CoordinatesCollection &polygon) { + mbgl::Polygon<double> mbglPolygon; + mbglPolygon.reserve(polygon.size()); + for (const auto &linearRing : polygon) { + mbgl::LinearRing<double> mbglLinearRing; + mbglLinearRing.reserve(linearRing.size()); + for (const auto &coordinate: linearRing) { + mbglLinearRing.emplace_back(asMapboxGLPoint(coordinate)); + } + mbglPolygon.emplace_back(std::move(mbglLinearRing)); + } + return mbglPolygon; +}; + +mbgl::MultiPolygon<double> asMapboxGLMultiPolygon(const QMapbox::CoordinatesCollections &multiPolygon) { + mbgl::MultiPolygon<double> mbglMultiPolygon; + mbglMultiPolygon.reserve(multiPolygon.size()); + for (const auto &polygon : multiPolygon) { + mbglMultiPolygon.emplace_back(std::forward<mbgl::Polygon<double>>(asMapboxGLPolygon(polygon))); + } + return mbglMultiPolygon; +}; + +mbgl::Value asMapboxGLPropertyValue(const QVariant &value) { + auto valueList = [](const QVariantList &list) { + std::vector<mbgl::Value> mbglList; + mbglList.reserve(list.size()); + for (const auto& listValue : list) { + mbglList.emplace_back(asMapboxGLPropertyValue(listValue)); + } + return mbglList; + }; + + auto valueMap = [](const QVariantMap &map) { + std::unordered_map<std::string, mbgl::Value> mbglMap; + mbglMap.reserve(map.size()); + auto it = map.constBegin(); + while (it != map.constEnd()) { + mbglMap.emplace(std::make_pair(it.key().toStdString(), asMapboxGLPropertyValue(it.value()))); + ++it; + } + return mbglMap; + }; + + switch (value.type()) { +#if QT_VERSION >= 0x050000 + case QMetaType::UnknownType: +#else + case QVariant::Invalid: +#endif + return mbgl::NullValue {}; + case QMetaType::Bool: + return { value.toBool() }; + case QMetaType::ULongLong: + return { uint64_t(value.toULongLong()) }; + case QMetaType::LongLong: + return { int64_t(value.toLongLong()) }; + case QMetaType::Double: + return { value.toDouble() }; + case QMetaType::QString: + return { value.toString().toStdString() }; + case QMetaType::QVariantList: + return valueList(value.toList()); + case QMetaType::QVariantMap: + return valueMap(value.toMap()); + default: + qWarning() << "Unsupported feature property value:" << value; + return {}; + } +} + +mbgl::FeatureIdentifier asMapboxGLFeatureIdentifier(const QVariant &id) { + switch (id.type()) { +#if QT_VERSION >= 0x050000 + case QMetaType::UnknownType: +#else + case QVariant::Invalid: +#endif + return {}; + case QMetaType::ULongLong: + return { uint64_t(id.toULongLong()) }; + case QMetaType::LongLong: + return { int64_t(id.toLongLong()) }; + case QMetaType::Double: + return { id.toDouble() }; + case QMetaType::QString: + return { id.toString().toStdString() }; + default: + qWarning() << "Unsupported feature identifier:" << id; + return {}; + } +} + +mbgl::Feature asMapboxGLFeature(const QMapbox::Feature &feature) { + mbgl::PropertyMap properties; + properties.reserve(feature.properties.size()); + auto it = feature.properties.constBegin(); + while (it != feature.properties.constEnd()) { + properties.emplace(std::make_pair(it.key().toStdString(), asMapboxGLPropertyValue(it.value()))); + } + + mbgl::FeatureIdentifier id = asMapboxGLFeatureIdentifier(feature.id); + + if (feature.type == QMapbox::Feature::PointType) { + const QMapbox::Coordinates &points = feature.geometry.first().first(); + if (points.size() == 1) { + return { asMapboxGLPoint(points.first()), std::move(properties), std::move(id) }; + } else { + return { asMapboxGLMultiPoint(points), std::move(properties), std::move(id) }; + } + } else if (feature.type == QMapbox::Feature::LineStringType) { + const QMapbox::CoordinatesCollection &lineStrings = feature.geometry.first(); + if (lineStrings.size() == 1) { + return { asMapboxGLLineString(lineStrings.first()), std::move(properties), std::move(id) }; + } else { + return { asMapboxGLMultiLineString(lineStrings), std::move(properties), std::move(id) }; + } + } else { // PolygonType + const QMapbox::CoordinatesCollections &polygons = feature.geometry; + if (polygons.size() == 1) { + return { asMapboxGLPolygon(polygons.first()), std::move(properties), std::move(id) }; + } else { + return { asMapboxGLMultiPolygon(polygons), std::move(properties), std::move(id) }; + } + } +}; + +} // namespace QMapbox + namespace mbgl { namespace style { namespace conversion { template <> +Result<GeoJSON> convertGeoJSON(const QMapbox::Feature& feature) { + return Result<GeoJSON> { GeoJSON { asMapboxGLFeature(feature) } }; +} + +template <> Result<GeoJSON> convertGeoJSON(const QVariant& value) { - if (value.type() != QVariant::ByteArray) { +#if QT_VERSION >= 0x050000 + if (value.typeName() == QStringLiteral("QMapbox::Feature")) { +#else + if (value.typeName() == QString("QMapbox::Feature")) { +#endif + return convertGeoJSON(value.value<QMapbox::Feature>()); + } else if (value.type() != QVariant::ByteArray) { return Error { "JSON data must be in QByteArray" }; } @@ -36,7 +212,7 @@ Result<GeoJSON> convertGeoJSON(const QVariant& value) { return Error { message.str() }; } - conversion::Result<GeoJSON> geoJSON = conversion::convertGeoJSON<JSValue>(d); + Result<GeoJSON> geoJSON = convertGeoJSON<JSValue>(d); if (!geoJSON) { return Error { geoJSON.error().message }; } diff --git a/platform/qt/src/sqlite3.cpp b/platform/qt/src/sqlite3.cpp new file mode 100644 index 0000000000..3470c36910 --- /dev/null +++ b/platform/qt/src/sqlite3.cpp @@ -0,0 +1,436 @@ +#include "sqlite3.hpp" + +#include <QSqlDatabase> +#include <QSqlError> +#include <QSqlQuery> +#include <QStringList> +#include <QThread> +#include <QVariant> + +#include <cassert> +#include <cstring> +#include <cstdio> +#include <chrono> + +#include <mbgl/util/chrono.hpp> +#include <mbgl/util/logging.hpp> +#include <mbgl/util/optional.hpp> +#include <mbgl/util/string.hpp> +#include <mbgl/util/traits.hpp> + +namespace mapbox { +namespace sqlite { + +// https://www.sqlite.org/rescode.html#ok +static_assert(mbgl::underlying_type(Exception::OK) == 0, "error"); +// https://www.sqlite.org/rescode.html#cantopen +static_assert(mbgl::underlying_type(Exception::CANTOPEN) == 14, "error"); +// https://www.sqlite.org/rescode.html#notadb +static_assert(mbgl::underlying_type(Exception::NOTADB) == 26, "error"); + +void checkQueryError(const QSqlQuery& query) { + QSqlError lastError = query.lastError(); + if (lastError.type() != QSqlError::NoError) { +#if QT_VERSION >= 0x050300 + throw Exception { lastError.nativeErrorCode().toInt(), lastError.text().toStdString() }; +#else + throw Exception { lastError.number(), lastError.text().toStdString() }; +#endif + } +} + +void checkDatabaseError(const QSqlDatabase &db) { + QSqlError lastError = db.lastError(); + if (lastError.type() != QSqlError::NoError) { +#if QT_VERSION >= 0x050300 + throw Exception { lastError.nativeErrorCode().toInt(), lastError.text().toStdString() }; +#else + throw Exception { lastError.number(), lastError.text().toStdString() }; +#endif + } +} + +class DatabaseImpl { +public: + DatabaseImpl(const char* filename, int flags) { + static uint64_t count = 0; + const QString connectionName = QString::number(uint64_t(QThread::currentThread())) + QString::number(count++); + + if (!QSqlDatabase::drivers().contains("QSQLITE")) { + throw Exception { Exception::Code::CANTOPEN, "SQLite driver not found." }; + } + + assert(!QSqlDatabase::contains(connectionName)); + db.reset(new QSqlDatabase(QSqlDatabase::addDatabase("QSQLITE", connectionName))); + + QString connectOptions = db->connectOptions(); + if (flags & OpenFlag::ReadOnly) { + if (!connectOptions.isEmpty()) connectOptions.append(';'); + connectOptions.append("QSQLITE_OPEN_READONLY"); + } + if (flags & OpenFlag::SharedCache) { + if (!connectOptions.isEmpty()) connectOptions.append(';'); + connectOptions.append("QSQLITE_ENABLE_SHARED_CACHE"); + } + + db->setConnectOptions(connectOptions); + db->setDatabaseName(QString(filename)); + + if (!db->open()) { + checkDatabaseError(*db); + } + } + + ~DatabaseImpl() { + db->close(); + checkDatabaseError(*db); + } + + QScopedPointer<QSqlDatabase> db; +}; + +class StatementImpl { +public: + StatementImpl(const QString& sql, const QSqlDatabase& db) : query(db) { + query.setForwardOnly(true); + if (!query.prepare(sql)) { + checkQueryError(query); + } + } + + ~StatementImpl() { + query.clear(); + } + + QSqlQuery query; + int64_t lastInsertRowId = 0; + int64_t changes = 0; +}; + +template <typename T> +using optional = std::experimental::optional<T>; + + +Database::Database(const std::string& file, int flags) + : impl(std::make_unique<DatabaseImpl>(file.c_str(), flags)) { + assert(impl); +} + +Database::Database(Database &&other) + : impl(std::move(other.impl)) { + assert(impl); +} + +Database &Database::operator=(Database &&other) { + std::swap(impl, other.impl); + assert(impl); + return *this; +} + +Database::~Database() { +} + +Database::operator bool() const { + return impl.operator bool(); +} + +void Database::setBusyTimeout(std::chrono::milliseconds timeout) { + assert(impl); + std::string timeoutStr = mbgl::util::toString(timeout.count()); + QString connectOptions = impl->db->connectOptions(); + if (connectOptions.isEmpty()) { + if (!connectOptions.isEmpty()) connectOptions.append(';'); + connectOptions.append("QSQLITE_BUSY_TIMEOUT=").append(QString::fromStdString(timeoutStr)); + } + if (impl->db->isOpen()) { + impl->db->close(); + } + impl->db->setConnectOptions(connectOptions); + if (!impl->db->open()) { + checkDatabaseError(*impl->db); + } +} + +void Database::exec(const std::string &sql) { + assert(impl); + QStringList statements = QString::fromStdString(sql).split(';', QString::SkipEmptyParts); + statements.removeAll("\n"); + for (QString statement : statements) { + if (!statement.endsWith(';')) { + statement.append(';'); + } + QSqlQuery query(*impl->db); + query.setForwardOnly(true); + query.prepare(statement); + if (!query.exec()) { + checkQueryError(query); + } + } +} + +Statement Database::prepare(const char *query) { + return Statement(this, query); +} + +Statement::Statement(Database *db, const char *sql) + : impl(std::make_unique<StatementImpl>(QString(sql), *db->impl->db)) { + assert(impl); +} + +Statement::Statement(Statement &&other) + : impl(std::move(other.impl)) { + assert(impl); +} + +Statement &Statement::operator=(Statement &&other) { + assert(impl); + std::swap(impl, other.impl); + return *this; +} + +Statement::~Statement() { +} + +Statement::operator bool() const { + assert(impl); + return impl.operator bool(); +} + +template void Statement::bind(int, int64_t); + +template <typename T> +void Statement::bind(int offset, T value) { + assert(impl); + // Field numbering starts at 0. + impl->query.bindValue(offset - 1, QVariant::fromValue<T>(value), QSql::In); + checkQueryError(impl->query); +} + +template <> +void Statement::bind(int offset, std::nullptr_t) { + assert(impl); + // Field numbering starts at 0. + impl->query.bindValue(offset - 1, QVariant(QVariant::Invalid), QSql::In); + checkQueryError(impl->query); +} + +template <> +void Statement::bind(int offset, int32_t value) { + bind(offset, static_cast<int64_t>(value)); +} + +template <> +void Statement::bind(int offset, bool value) { + bind(offset, static_cast<int>(value)); +} + +template <> +void Statement::bind(int offset, int8_t value) { + bind(offset, static_cast<int64_t>(value)); +} + +template <> +void Statement::bind(int offset, uint8_t value) { + bind(offset, static_cast<int64_t>(value)); +} + +template <> +void Statement::bind(int offset, mbgl::Timestamp value) { + bind(offset, std::chrono::system_clock::to_time_t(value)); +} + +template <> +void Statement::bind(int offset, optional<std::string> value) { + if (value) { + bind(offset, *value); + } else { + bind(offset, nullptr); + } +} + +template <> +void Statement::bind(int offset, optional<mbgl::Timestamp> value) { + if (value) { + bind(offset, *value); + } else { + bind(offset, nullptr); + } +} + +void Statement::bind(int offset, const char* value, std::size_t length, bool retain) { + assert(impl); + if (length > std::numeric_limits<int>::max()) { + // Kept for consistence with the default implementation. + throw std::range_error("value too long"); + } + + // Field numbering starts at 0. + impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) : + QByteArray::fromRawData(value, length), QSql::In); + + checkQueryError(impl->query); +} + +void Statement::bind(int offset, const std::string& value, bool retain) { + bind(offset, value.data(), value.size(), retain); +} + +void Statement::bindBlob(int offset, const void* value_, std::size_t length, bool retain) { + const char* value = reinterpret_cast<const char*>(value_); + + // Field numbering starts at 0. + impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) : + QByteArray::fromRawData(value, length), QSql::In | QSql::Binary); + + checkQueryError(impl->query); +} + +void Statement::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) { + bindBlob(offset, value.data(), value.size(), retain); +} + +bool Statement::run() { + assert(impl); + if (impl->query.isValid()) { + return impl->query.next(); + } + + assert(!impl->query.isActive()); + impl->query.setForwardOnly(true); + if (!impl->query.exec()) { + checkQueryError(impl->query); + } + + impl->lastInsertRowId = impl->query.lastInsertId().value<int64_t>(); + impl->changes = impl->query.numRowsAffected(); + + return impl->query.next(); +} + +template int Statement::get(int); +template int64_t Statement::get(int); +template double Statement::get(int); + +template <typename T> T Statement::get(int offset) { + assert(impl && impl->query.isValid()); + QVariant value = impl->query.value(offset); + checkQueryError(impl->query); + return value.value<T>(); +} + +template <> std::vector<uint8_t> Statement::get(int offset) { + assert(impl && impl->query.isValid()); + QByteArray byteArray = impl->query.value(offset).toByteArray(); + checkQueryError(impl->query); + std::vector<uint8_t> blob(byteArray.begin(), byteArray.end()); + return blob; +} + +template <> mbgl::Timestamp Statement::get(int offset) { + assert(impl && impl->query.isValid()); + QVariant value = impl->query.value(offset); + checkQueryError(impl->query); + return std::chrono::time_point_cast<std::chrono::seconds>( + std::chrono::system_clock::from_time_t(value.value<std::time_t>())); +} + +template <> optional<int64_t> Statement::get(int offset) { + assert(impl && impl->query.isValid()); + QVariant value = impl->query.value(offset); + checkQueryError(impl->query); + if (value.isNull()) + return {}; + return { value.value<int64_t>() }; +} + +template <> optional<double> Statement::get(int offset) { + assert(impl && impl->query.isValid()); + QVariant value = impl->query.value(offset); + checkQueryError(impl->query); + if (value.isNull()) + return {}; + return { value.value<double>() }; +} + +template <> std::string Statement::get(int offset) { + assert(impl && impl->query.isValid()); + QByteArray value = impl->query.value(offset).toByteArray(); + checkQueryError(impl->query); + return std::string(value.constData(), value.size()); +} + +template <> optional<std::string> Statement::get(int offset) { + assert(impl && impl->query.isValid()); + QByteArray value = impl->query.value(offset).toByteArray(); + checkQueryError(impl->query); + if (value.isNull()) + return {}; + return { std::string(value.constData(), value.size()) }; +} + +template <> optional<mbgl::Timestamp> Statement::get(int offset) { + assert(impl && impl->query.isValid()); + QVariant value = impl->query.value(offset); + checkQueryError(impl->query); + if (value.isNull()) + return {}; + return { std::chrono::time_point_cast<mbgl::Seconds>( + std::chrono::system_clock::from_time_t(value.value<std::time_t>())) }; +} + +void Statement::reset() { + assert(impl); + impl->query.finish(); +} + +void Statement::clearBindings() { + // no-op +} + +int64_t Statement::lastInsertRowId() const { + assert(impl); + return impl->lastInsertRowId; +} + +uint64_t Statement::changes() const { + assert(impl); + return (impl->changes < 0 ? 0 : impl->changes); +} + +Transaction::Transaction(Database& db_, Mode mode) + : db(db_) { + switch (mode) { + case Deferred: + db.exec("BEGIN DEFERRED TRANSACTION"); + break; + case Immediate: + db.exec("BEGIN IMMEDIATE TRANSACTION"); + break; + case Exclusive: + db.exec("BEGIN EXCLUSIVE TRANSACTION"); + break; + } +} + +Transaction::~Transaction() { + if (needRollback) { + try { + rollback(); + } catch (...) { + // Ignore failed rollbacks in destructor. + } + } +} + +void Transaction::commit() { + needRollback = false; + db.exec("COMMIT TRANSACTION"); +} + +void Transaction::rollback() { + needRollback = false; + db.exec("ROLLBACK TRANSACTION"); +} + +} // namespace sqlite +} // namespace mapbox |