diff options
Diffstat (limited to 'platform/qt/src')
-rw-r--r-- | platform/qt/src/http_file_source.cpp | 2 | ||||
-rw-r--r-- | platform/qt/src/http_request.cpp | 2 | ||||
-rw-r--r-- | platform/qt/src/qmapbox.cpp | 237 | ||||
-rw-r--r-- | platform/qt/src/qmapboxgl.cpp | 163 | ||||
-rw-r--r-- | platform/qt/src/qmapboxgl_p.hpp | 6 | ||||
-rw-r--r-- | platform/qt/src/qquickmapboxgl.hpp | 176 | ||||
-rw-r--r-- | platform/qt/src/qquickmapboxglmapparameter.hpp | 34 | ||||
-rw-r--r-- | platform/qt/src/qquickmapboxglrenderer.cpp | 4 | ||||
-rw-r--r-- | platform/qt/src/qt_conversion.hpp | 10 | ||||
-rw-r--r-- | platform/qt/src/qt_geojson.hpp | 180 | ||||
-rw-r--r-- | platform/qt/src/sqlite3.cpp | 436 |
11 files changed, 955 insertions, 295 deletions
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/qquickmapboxgl.hpp b/platform/qt/src/qquickmapboxgl.hpp deleted file mode 100644 index 39b4395bd6..0000000000 --- a/platform/qt/src/qquickmapboxgl.hpp +++ /dev/null @@ -1,176 +0,0 @@ -#pragma once - -#include "qmapbox.hpp" -#include "qmapboxgl.hpp" -#include "qquickmapboxglmapparameter.hpp" - -#include <QColor> -#include <QGeoCoordinate> -#include <QGeoServiceProvider> -#include <QGeoShape> -#include <QImage> -#include <QPointF> -#include <QQmlListProperty> -#include <QQuickFramebufferObject> -#include <QQuickItem> - -class QDeclarativeGeoServiceProvider; -class QQuickMapboxGLRenderer; - -class Q_DECL_EXPORT QQuickMapboxGL : public QQuickFramebufferObject -{ - Q_OBJECT - - // Map QML Type interface. - Q_ENUMS(QGeoServiceProvider::Error) - Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) - Q_PROPERTY(qreal minimumZoomLevel READ minimumZoomLevel WRITE setMinimumZoomLevel NOTIFY minimumZoomLevelChanged) - Q_PROPERTY(qreal maximumZoomLevel READ maximumZoomLevel WRITE setMaximumZoomLevel NOTIFY maximumZoomLevelChanged) - Q_PROPERTY(qreal zoomLevel READ zoomLevel WRITE setZoomLevel NOTIFY zoomLevelChanged) - Q_PROPERTY(QGeoCoordinate center READ center WRITE setCenter NOTIFY centerChanged) - Q_PROPERTY(QGeoServiceProvider::Error error READ error NOTIFY errorChanged) - Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged) - Q_PROPERTY(QGeoShape visibleRegion READ visibleRegion WRITE setVisibleRegion) - Q_PROPERTY(bool copyrightsVisible READ copyrightsVisible WRITE setCopyrightsVisible NOTIFY copyrightsVisibleChanged) - Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) - - // Proposed Qt interface - based on the example documentation below: - // http://doc.qt.io/qt-5/qtqml-referenceexamples-properties-example.html - Q_PROPERTY(QQmlListProperty<QQuickMapboxGLMapParameter> parameters READ parameters) - -public: - QQuickMapboxGL(QQuickItem *parent = 0); - virtual ~QQuickMapboxGL(); - - // QQuickFramebufferObject implementation. - virtual Renderer *createRenderer() const Q_DECL_FINAL; - - // Map QML Type interface implementation. - void setPlugin(QDeclarativeGeoServiceProvider *plugin); - QDeclarativeGeoServiceProvider *plugin() const; - - void setMinimumZoomLevel(qreal minimumZoomLevel); - qreal minimumZoomLevel() const; - - void setMaximumZoomLevel(qreal maximumZoomLevel); - qreal maximumZoomLevel() const; - - void setZoomLevel(qreal zoomLevel); - qreal zoomLevel() const; - - QGeoCoordinate center() const; - - QGeoServiceProvider::Error error() const; - QString errorString() const; - - void setVisibleRegion(const QGeoShape &shape); - QGeoShape visibleRegion() const; - - void setCopyrightsVisible(bool visible); - bool copyrightsVisible() const; - - void setColor(const QColor &color); - QColor color() const; - - Q_INVOKABLE void pan(int dx, int dy); - - // Proposed Qt interface implementation. - QQmlListProperty<QQuickMapboxGLMapParameter> parameters(); - -protected: - // QQmlParserStatus implementation - void componentComplete() override; - -signals: - // Map QML Type signals. - void minimumZoomLevelChanged(); - void maximumZoomLevelChanged(); - void zoomLevelChanged(qreal zoomLevel); - void centerChanged(const QGeoCoordinate &coordinate); - void colorChanged(const QColor &color); - void errorChanged(); - - // Compatibility with Map QML Type, but no-op. - void pluginChanged(QDeclarativeGeoServiceProvider *plugin); - void copyrightLinkActivated(const QString &link); - void copyrightsVisibleChanged(bool visible); - -public slots: - void setCenter(const QGeoCoordinate ¢er); - -private slots: - void onMapChanged(QMapboxGL::MapChange); - void onParameterPropertyUpdated(const QString &name); - -private: - static void appendParameter(QQmlListProperty<QQuickMapboxGLMapParameter> *prop, QQuickMapboxGLMapParameter *mapObject); - static int countParameters(QQmlListProperty<QQuickMapboxGLMapParameter> *prop); - static QQuickMapboxGLMapParameter *parameterAt(QQmlListProperty<QQuickMapboxGLMapParameter> *prop, int index); - static void clearParameter(QQmlListProperty<QQuickMapboxGLMapParameter> *prop); - - enum SyncState { - NothingNeedsSync = 0, - ZoomNeedsSync = 1 << 0, - CenterNeedsSync = 1 << 1, - StyleNeedsSync = 1 << 2, - PanNeedsSync = 1 << 3, - BearingNeedsSync = 1 << 4, - PitchNeedsSync = 1 << 5, - }; - - struct Image { - QString name; - QImage sprite; - }; - - struct StyleProperty { - enum Type { - Paint = 0, - Layout - }; - - Type type; - QString layer; - QString property; - QVariant value; - QString klass; - }; - - void processMapParameter(QQuickMapboxGLMapParameter *); - bool parseImage(QQuickMapboxGLMapParameter *); - bool parseStyle(QQuickMapboxGLMapParameter *); - bool parseStyleProperty(QQuickMapboxGLMapParameter *, const QString &name); - bool parseStyleLayer(QQuickMapboxGLMapParameter *); - bool parseStyleSource(QQuickMapboxGLMapParameter *); - bool parseStyleFilter(QQuickMapboxGLMapParameter *); - bool parseBearing(QQuickMapboxGLMapParameter *); - bool parsePitch(QQuickMapboxGLMapParameter *); - - qreal m_minimumZoomLevel = 0; - qreal m_maximumZoomLevel = 20; - qreal m_zoomLevel = 20; - - QPointF m_pan; - - QGeoCoordinate m_center; - QGeoShape m_visibleRegion; - QColor m_color; - QString m_styleUrl; - QList<Image> m_imageChanges; - QList<StyleProperty> m_stylePropertyChanges; - QList<QVariantMap> m_layerChanges; - QList<QVariantMap> m_sourceChanges; - QList<QVariantMap> m_filterChanges; - QList<QQuickMapboxGLMapParameter*> m_parameters; - - QGeoServiceProvider::Error m_error = QGeoServiceProvider::NoError; - QString m_errorString; - - qreal m_bearing = 0; - qreal m_pitch = 0; - - int m_syncState = NothingNeedsSync; - bool m_styleLoaded = false; - - friend class QQuickMapboxGLRenderer; -}; diff --git a/platform/qt/src/qquickmapboxglmapparameter.hpp b/platform/qt/src/qquickmapboxglmapparameter.hpp deleted file mode 100644 index 1dca0cf55d..0000000000 --- a/platform/qt/src/qquickmapboxglmapparameter.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include <QObject> -#include <QQmlParserStatus> -#include <QString> -#include <qqml.h> - -class Q_DECL_EXPORT QQuickMapboxGLMapParameter : public QObject, public QQmlParserStatus -{ - Q_OBJECT - Q_INTERFACES(QQmlParserStatus) - -public: - QQuickMapboxGLMapParameter(QObject *parent = 0); - virtual ~QQuickMapboxGLMapParameter() {}; - - int propertyOffset() const { return m_metaPropertyOffset; } - -signals: - void propertyUpdated(const QString &name); - -protected: - // QQmlParserStatus implementation - void classBegin() override {} - void componentComplete() override; - -private slots: - void onPropertyUpdated(int index); - -private: - int m_metaPropertyOffset; -}; - -QML_DECLARE_TYPE(QQuickMapboxGLMapParameter) 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 |