summaryrefslogtreecommitdiff
path: root/platform/qt/src
diff options
context:
space:
mode:
Diffstat (limited to 'platform/qt/src')
-rw-r--r--platform/qt/src/http_file_source.cpp2
-rw-r--r--platform/qt/src/http_request.cpp2
-rw-r--r--platform/qt/src/qmapbox.cpp237
-rw-r--r--platform/qt/src/qmapboxgl.cpp163
-rw-r--r--platform/qt/src/qmapboxgl_p.hpp6
-rw-r--r--platform/qt/src/qquickmapboxgl.hpp176
-rw-r--r--platform/qt/src/qquickmapboxglmapparameter.hpp34
-rw-r--r--platform/qt/src/qquickmapboxglrenderer.cpp4
-rw-r--r--platform/qt/src/qt_conversion.hpp10
-rw-r--r--platform/qt/src/qt_geojson.hpp180
-rw-r--r--platform/qt/src/sqlite3.cpp436
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 &params)
}
/*!
+ 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 &params)
}
/*!
- 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 &center);
-
-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