summaryrefslogtreecommitdiff
path: root/platform/qt
diff options
context:
space:
mode:
Diffstat (limited to 'platform/qt')
-rw-r--r--platform/qt/app/mapwindow.cpp79
-rw-r--r--platform/qt/app/mapwindow.hpp5
-rw-r--r--platform/qt/config.cmake4
-rw-r--r--platform/qt/config.qdocconf3
-rw-r--r--platform/qt/include/QQuickMapboxGL1
-rw-r--r--platform/qt/include/QQuickMapboxGLMapParameter1
-rw-r--r--platform/qt/include/qmapbox.hpp74
-rw-r--r--platform/qt/include/qmapboxgl.hpp20
-rw-r--r--platform/qt/include/qquickmapboxgl.hpp (renamed from platform/qt/src/qquickmapboxgl.hpp)0
-rw-r--r--platform/qt/include/qquickmapboxglmapparameter.hpp (renamed from platform/qt/src/qquickmapboxglmapparameter.hpp)0
-rw-r--r--platform/qt/qmlapp/main.cpp8
-rw-r--r--platform/qt/qt.cmake6
-rw-r--r--platform/qt/qt4.cmake1
-rw-r--r--platform/qt/qt5.cmake9
-rw-r--r--platform/qt/resources/common.qrc1
-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/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
24 files changed, 1132 insertions, 120 deletions
diff --git a/platform/qt/app/mapwindow.cpp b/platform/qt/app/mapwindow.cpp
index 0b453406f3..e29e62f157 100644
--- a/platform/qt/app/mapwindow.cpp
+++ b/platform/qt/app/mapwindow.cpp
@@ -203,6 +203,85 @@ void MapWindow::keyPressEvent(QKeyEvent *ev)
m_map->setLayoutProperty("road-label-small", "text-size", 30.0);
}
break;
+ case Qt::Key_1: {
+ if (m_symbolAnnotationId.isNull()) {
+ QMapbox::Coordinate coordinate = m_map->coordinate();
+ QMapbox::SymbolAnnotation symbol { coordinate, "default_marker" };
+ m_map->addAnnotationIcon("default_marker", QImage(":default_marker.svg"));
+ m_symbolAnnotationId = m_map->addAnnotation(QVariant::fromValue<QMapbox::SymbolAnnotation>(symbol));
+ } else {
+ m_map->removeAnnotation(m_symbolAnnotationId.toUInt());
+ m_symbolAnnotationId.clear();
+ }
+ }
+ break;
+ case Qt::Key_2: {
+ if (m_lineAnnotationId.isNull()) {
+ QMapbox::Coordinate topLeft = m_map->coordinateForPixel({ 0, 0 });
+ QMapbox::Coordinate bottomRight = m_map->coordinateForPixel({ qreal(size().width()), qreal(size().height()) });
+ QMapbox::CoordinatesCollections geometry { { { topLeft, bottomRight } } };
+ QMapbox::LineAnnotation line { { QMapbox::ShapeAnnotationGeometry::LineStringType, geometry }, 0.5f, 1.0f, Qt::red };
+ m_lineAnnotationId = m_map->addAnnotation(QVariant::fromValue<QMapbox::LineAnnotation>(line));
+ } else {
+ m_map->removeAnnotation(m_lineAnnotationId.toUInt());
+ m_lineAnnotationId.clear();
+ }
+ }
+ break;
+ case Qt::Key_3: {
+ if (m_fillAnnotationId.isNull()) {
+ QMapbox::Coordinate topLeft = m_map->coordinateForPixel({ 0, 0 });
+ QMapbox::Coordinate topRight = m_map->coordinateForPixel({ 0, qreal(size().height()) });
+ QMapbox::Coordinate bottomLeft = m_map->coordinateForPixel({ qreal(size().width()), 0 });
+ QMapbox::Coordinate bottomRight = m_map->coordinateForPixel({ qreal(size().width()), qreal(size().height()) });
+ QMapbox::CoordinatesCollections geometry { { { bottomLeft, bottomRight, topRight, topLeft, bottomLeft } } };
+ QMapbox::FillAnnotation fill { { QMapbox::ShapeAnnotationGeometry::PolygonType, geometry }, 0.5f, Qt::green, QVariant::fromValue<QColor>(QColor(Qt::black)) };
+ m_fillAnnotationId = m_map->addAnnotation(QVariant::fromValue<QMapbox::FillAnnotation>(fill));
+ } else {
+ m_map->removeAnnotation(m_fillAnnotationId.toUInt());
+ m_fillAnnotationId.clear();
+ }
+ }
+ break;
+ case Qt::Key_4: {
+ if (m_styleSourcedAnnotationId.isNull()) {
+ QMapbox::Coordinate topLeft = m_map->coordinateForPixel({ 0, 0 });
+ QMapbox::Coordinate topRight = m_map->coordinateForPixel({ 0, qreal(size().height()) });
+ QMapbox::Coordinate bottomLeft = m_map->coordinateForPixel({ qreal(size().width()), 0 });
+ QMapbox::Coordinate bottomRight = m_map->coordinateForPixel({ qreal(size().width()), qreal(size().height()) });
+ QMapbox::CoordinatesCollections geometry { { { bottomLeft, bottomRight, topRight, topLeft, bottomLeft } } };
+ QMapbox::StyleSourcedAnnotation styleSourced { { QMapbox::ShapeAnnotationGeometry::PolygonType, geometry }, "water" };
+ m_styleSourcedAnnotationId = m_map->addAnnotation(QVariant::fromValue<QMapbox::StyleSourcedAnnotation>(styleSourced));
+ } else {
+ m_map->removeAnnotation(m_styleSourcedAnnotationId.toUInt());
+ m_styleSourcedAnnotationId.clear();
+ }
+ }
+ break;
+ case Qt::Key_5: {
+ if (m_map->layerExists("circleLayer")) {
+ m_map->removeLayer("circleLayer");
+ m_map->removeSource("circleSource");
+ } else {
+ QMapbox::CoordinatesCollections geometry { { { m_map->coordinate() } } };
+ QMapbox::Feature feature { QMapbox::Feature::PointType, geometry, {}, {} };
+
+ QVariantMap circleSource;
+ circleSource["type"] = "geojson";
+ circleSource["data"] = QVariant::fromValue<QMapbox::Feature>(feature);
+ m_map->addSource("circleSource", circleSource);
+
+ QVariantMap circle;
+ circle["id"] = "circleLayer";
+ circle["type"] = "circle";
+ circle["source"] = "circleSource";
+ m_map->addLayer(circle);
+
+ m_map->setPaintProperty("circleLayer", "circle-radius", 10.0);
+ m_map->setPaintProperty("circleLayer", "circle-color", QColor("black"));
+ }
+ }
+ break;
case Qt::Key_Tab:
m_map->cycleDebugOptions();
break;
diff --git a/platform/qt/app/mapwindow.hpp b/platform/qt/app/mapwindow.hpp
index 23880902d3..6b4b7fd1cc 100644
--- a/platform/qt/app/mapwindow.hpp
+++ b/platform/qt/app/mapwindow.hpp
@@ -61,6 +61,11 @@ private:
unsigned m_animationTicks = 0;
unsigned m_frameDraws = 0;
+ QVariant m_symbolAnnotationId;
+ QVariant m_lineAnnotationId;
+ QVariant m_fillAnnotationId;
+ QVariant m_styleSourcedAnnotationId;
+
bool m_sourceAdded = false;
};
diff --git a/platform/qt/config.cmake b/platform/qt/config.cmake
index e1c454c757..e97c6b340a 100644
--- a/platform/qt/config.cmake
+++ b/platform/qt/config.cmake
@@ -1,7 +1,7 @@
include(platform/qt/qt.cmake)
mason_use(sqlite VERSION 3.14.2)
-mason_use(gtest VERSION 1.7.0${MASON_CXXABI_SUFFIX})
+mason_use(gtest VERSION 1.8.0${MASON_CXXABI_SUFFIX})
if(NOT WITH_QT_DECODERS)
mason_use(libjpeg-turbo VERSION 1.5.0)
@@ -23,8 +23,6 @@ macro(mbgl_platform_core)
PRIVATE platform/qt/include
)
- target_add_mason_package(mbgl-core PRIVATE sqlite)
-
target_link_libraries(mbgl-core
${MBGL_QT_LIBRARIES}
)
diff --git a/platform/qt/config.qdocconf b/platform/qt/config.qdocconf
index 7dbe6423eb..b8ade81bb2 100644
--- a/platform/qt/config.qdocconf
+++ b/platform/qt/config.qdocconf
@@ -4,10 +4,13 @@ include($QT_INSTALL_DOCS/global/compat.qdocconf)
include($QT_INSTALL_DOCS/global/fileextensions.qdocconf)
include($QT_INSTALL_DOCS/global/qt-html-templates-online.qdocconf)
+Cpp.ignoretokens = Q_DECL_EXPORT
+
project = QMapboxGL
description = Mapbox Qt SDK
language = Cpp
+outputdir = docs
headerdirs += include
sourcedirs += src
diff --git a/platform/qt/include/QQuickMapboxGL b/platform/qt/include/QQuickMapboxGL
new file mode 100644
index 0000000000..db109a1d3a
--- /dev/null
+++ b/platform/qt/include/QQuickMapboxGL
@@ -0,0 +1 @@
+#include "qquickmapboxgl.hpp"
diff --git a/platform/qt/include/QQuickMapboxGLMapParameter b/platform/qt/include/QQuickMapboxGLMapParameter
new file mode 100644
index 0000000000..603fb2bd51
--- /dev/null
+++ b/platform/qt/include/QQuickMapboxGLMapParameter
@@ -0,0 +1 @@
+#include "qquickmapboxglmapparameter.hpp"
diff --git a/platform/qt/include/qmapbox.hpp b/platform/qt/include/qmapbox.hpp
index b2e3b521d0..f2d96412a9 100644
--- a/platform/qt/include/qmapbox.hpp
+++ b/platform/qt/include/qmapbox.hpp
@@ -1,6 +1,7 @@
#ifndef QMAPBOX_H
#define QMAPBOX_H
+#include <QColor>
#include <QList>
#include <QPair>
#include <QVariant>
@@ -11,19 +12,65 @@
namespace QMapbox {
typedef QPair<double, double> Coordinate;
-typedef QList<Coordinate> LineString;
-
typedef QPair<Coordinate, double> CoordinateZoom;
+typedef QPair<double, double> ProjectedMeters;
+
+typedef QList<Coordinate> Coordinates;
+typedef QList<Coordinates> CoordinatesCollection;
+
+typedef QList<CoordinatesCollection> CoordinatesCollections;
+
+struct Q_DECL_EXPORT Feature {
+ enum Type {
+ PointType = 1,
+ LineStringType,
+ PolygonType
+ };
+ Type type;
+ CoordinatesCollections geometry;
+ QVariantMap properties;
+ QVariant id;
+};
-typedef quint32 AnnotationID;
-typedef QList<AnnotationID> AnnotationIDs;
+struct Q_DECL_EXPORT ShapeAnnotationGeometry {
+ enum Type {
+ LineStringType,
+ PolygonType,
+ MultiLineStringType,
+ MultiPolygonType
+ };
+ Type type;
+ CoordinatesCollections geometry;
+};
+
+struct Q_DECL_EXPORT SymbolAnnotation {
+ Coordinate geometry;
+ QString icon;
+};
-typedef QPair<Coordinate, QString> PointAnnotation;
+struct Q_DECL_EXPORT LineAnnotation {
+ ShapeAnnotationGeometry geometry;
+ float opacity = 1.0f;
+ float width = 1.0f;
+ QColor color = Qt::black;
+};
+
+struct Q_DECL_EXPORT FillAnnotation {
+ ShapeAnnotationGeometry geometry;
+ float opacity = 1.0f;
+ QColor color = Qt::black;
+ QVariant outlineColor;
+};
-// FIXME: We need to add support for custom style properties
-typedef QPair<LineString, QString> ShapeAnnotation;
+struct Q_DECL_EXPORT StyleSourcedAnnotation {
+ ShapeAnnotationGeometry geometry;
+ QString layerID;
+};
+
+typedef QVariant Annotation;
+typedef quint32 AnnotationID;
+typedef QList<AnnotationID> AnnotationIDs;
-// Reflects mbgl::NetworkStatus::Status.
enum NetworkMode {
Online, // Default
Offline,
@@ -51,10 +98,19 @@ typedef void (*CustomLayerRenderFunction)(void* context, const CustomLayerRender
typedef void (*CustomLayerDeinitializeFunction)(void* context);
Q_DECL_EXPORT void initializeGLExtensions();
-Q_DECL_EXPORT void registerTypes();
} // namespace QMapbox
Q_DECLARE_METATYPE(QMapbox::Coordinate);
+Q_DECLARE_METATYPE(QMapbox::Coordinates);
+Q_DECLARE_METATYPE(QMapbox::CoordinatesCollection);
+Q_DECLARE_METATYPE(QMapbox::CoordinatesCollections);
+Q_DECLARE_METATYPE(QMapbox::Feature);
+
+Q_DECLARE_METATYPE(QMapbox::SymbolAnnotation);
+Q_DECLARE_METATYPE(QMapbox::ShapeAnnotationGeometry);
+Q_DECLARE_METATYPE(QMapbox::LineAnnotation);
+Q_DECLARE_METATYPE(QMapbox::FillAnnotation);
+Q_DECLARE_METATYPE(QMapbox::StyleSourcedAnnotation);
#endif // QMAPBOX_H
diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp
index be2d60608b..af7ed6275f 100644
--- a/platform/qt/include/qmapboxgl.hpp
+++ b/platform/qt/include/qmapboxgl.hpp
@@ -10,10 +10,6 @@
#include <QString>
#include <QStringList>
-#if QT_VERSION >= 0x050000
-#include <QOpenGLFramebufferObject>
-#endif
-
class QMapboxGLPrivate;
// This header follows the Qt coding style: https://wiki.qt.io/Qt_Coding_Style
@@ -183,11 +179,8 @@ public:
void addAnnotationIcon(const QString &name, const QImage &sprite);
- QMapbox::AnnotationID addPointAnnotation(const QMapbox::PointAnnotation &);
- QMapbox::AnnotationID addShapeAnnotation(const QMapbox::ShapeAnnotation &);
-
- void updatePointAnnotation(QMapbox::AnnotationID, const QMapbox::PointAnnotation &);
-
+ QMapbox::AnnotationID addAnnotation(const QMapbox::Annotation &);
+ void updateAnnotation(QMapbox::AnnotationID, const QMapbox::Annotation &);
void removeAnnotation(QMapbox::AnnotationID);
void setLayoutProperty(const QString &layer, const QString &property, const QVariant &value);
@@ -201,6 +194,9 @@ public:
void resize(const QSize &size, const QSize &framebufferSize);
+ double metersPerPixelAtLatitude(double latitude, double zoom) const;
+ QMapbox::ProjectedMeters projectedMetersForCoordinate(const QMapbox::Coordinate &) const;
+ QMapbox::Coordinate coordinateForProjectedMeters(const QMapbox::ProjectedMeters &) const;
QPointF pixelForCoordinate(const QMapbox::Coordinate &) const;
QMapbox::Coordinate coordinateForPixel(const QPointF &) const;
@@ -211,6 +207,7 @@ public:
QMargins margins() const;
void addSource(const QString &sourceID, const QVariantMap& params);
+ bool sourceExists(const QString &sourceID);
void updateSource(const QString &sourceID, const QVariantMap& params);
void removeSource(const QString &sourceID);
@@ -224,16 +221,13 @@ public:
void* context,
char* before = NULL);
void addLayer(const QVariantMap &params);
+ bool layerExists(const QString &id);
void removeLayer(const QString &id);
void setFilter(const QString &layer, const QVariant &filter);
public slots:
-#if QT_VERSION >= 0x050000
- void render(QOpenGLFramebufferObject *fbo = NULL);
-#else
void render();
-#endif
void connectionEstablished();
signals:
diff --git a/platform/qt/src/qquickmapboxgl.hpp b/platform/qt/include/qquickmapboxgl.hpp
index 39b4395bd6..39b4395bd6 100644
--- a/platform/qt/src/qquickmapboxgl.hpp
+++ b/platform/qt/include/qquickmapboxgl.hpp
diff --git a/platform/qt/src/qquickmapboxglmapparameter.hpp b/platform/qt/include/qquickmapboxglmapparameter.hpp
index 1dca0cf55d..1dca0cf55d 100644
--- a/platform/qt/src/qquickmapboxglmapparameter.hpp
+++ b/platform/qt/include/qquickmapboxglmapparameter.hpp
diff --git a/platform/qt/qmlapp/main.cpp b/platform/qt/qmlapp/main.cpp
index 0dd8c96b7d..8606704002 100644
--- a/platform/qt/qmlapp/main.cpp
+++ b/platform/qt/qmlapp/main.cpp
@@ -1,4 +1,5 @@
-#include <QMapbox>
+#include <QQuickMapboxGL>
+#include <QQuickMapboxGLMapParameter>
#include <QGuiApplication>
#include <QIcon>
@@ -13,9 +14,8 @@ int main(int argc, char *argv[])
app.setWindowIcon(QIcon(":icon.png"));
#endif
- // Exposes the QQuickMapboxGL module so we
- // can do `import QQuickMapboxGL 1.0`.
- QMapbox::registerTypes();
+ qmlRegisterType<QQuickMapboxGL>("QQuickMapboxGL", 1, 0, "MapboxMap");
+ qmlRegisterType<QQuickMapboxGLMapParameter>("QQuickMapboxGL", 1, 0, "MapParameter");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
diff --git a/platform/qt/qt.cmake b/platform/qt/qt.cmake
index bd7c863f98..cee0d1080c 100644
--- a/platform/qt/qt.cmake
+++ b/platform/qt/qt.cmake
@@ -25,15 +25,16 @@ set(MBGL_QT_FILES
PRIVATE platform/default/mbgl/storage/offline_database.hpp
PRIVATE platform/default/mbgl/storage/offline_download.cpp
PRIVATE platform/default/mbgl/storage/offline_download.hpp
- PRIVATE platform/default/sqlite3.cpp
PRIVATE platform/default/sqlite3.hpp
# Misc
PRIVATE platform/default/logging_stderr.cpp
# Thread pool
+ PRIVATE platform/default/mbgl/util/shared_thread_pool.cpp
+ PRIVATE platform/default/mbgl/util/shared_thread_pool.hpp
PRIVATE platform/default/mbgl/util/default_thread_pool.cpp
- PRIVATE platform/default/mbgl/util/default_thread_pool.cpp
+ PRIVATE platform/default/mbgl/util/default_thread_pool.hpp
# Platform integration
PRIVATE platform/qt/src/async_task.cpp
@@ -45,6 +46,7 @@ set(MBGL_QT_FILES
PRIVATE platform/qt/src/image.cpp
PRIVATE platform/qt/src/run_loop.cpp
PRIVATE platform/qt/src/run_loop_impl.hpp
+ PRIVATE platform/qt/src/sqlite3.cpp
PRIVATE platform/qt/src/string_stdlib.cpp
PRIVATE platform/qt/src/timer.cpp
PRIVATE platform/qt/src/timer_impl.hpp
diff --git a/platform/qt/qt4.cmake b/platform/qt/qt4.cmake
index 45c299c8a8..d6d7d89417 100644
--- a/platform/qt/qt4.cmake
+++ b/platform/qt/qt4.cmake
@@ -5,6 +5,7 @@ set(MBGL_QT_LIBRARIES
PRIVATE Qt4::QtGui
PRIVATE Qt4::QtNetwork
PRIVATE Qt4::QtOpenGL
+ PRIVATE Qt4::QtSql
)
target_link_libraries(qmapboxgl
diff --git a/platform/qt/qt5.cmake b/platform/qt/qt5.cmake
index 47e178d132..7210a3d5f5 100644
--- a/platform/qt/qt5.cmake
+++ b/platform/qt/qt5.cmake
@@ -5,21 +5,21 @@ find_package(Qt5Network REQUIRED)
find_package(Qt5OpenGL REQUIRED)
find_package(Qt5Quick REQUIRED)
find_package(Qt5Widgets REQUIRED)
+find_package(Qt5Sql REQUIRED)
set(MBGL_QT_LIBRARIES
PRIVATE Qt5::Core
PRIVATE Qt5::Gui
- PRIVATE Qt5::Location
PRIVATE Qt5::Network
PRIVATE Qt5::OpenGL
- PRIVATE Qt5::Quick
+ PRIVATE Qt5::Sql
)
target_sources(qmapboxgl
+ PRIVATE platform/qt/include/qquickmapboxgl.hpp
+ PRIVATE platform/qt/include/qquickmapboxglmapparameter.hpp
PRIVATE platform/qt/src/qquickmapboxgl.cpp
- PRIVATE platform/qt/src/qquickmapboxgl.hpp
PRIVATE platform/qt/src/qquickmapboxglmapparameter.cpp
- PRIVATE platform/qt/src/qquickmapboxglmapparameter.hpp
PRIVATE platform/qt/src/qquickmapboxglrenderer.cpp
PRIVATE platform/qt/src/qquickmapboxglrenderer.hpp
)
@@ -31,6 +31,7 @@ target_link_libraries(qmapboxgl
PRIVATE Qt5::Location
PRIVATE Qt5::OpenGL
PRIVATE Qt5::Quick
+ PRIVATE Qt5::Sql
)
target_link_libraries(mbgl-qt
diff --git a/platform/qt/resources/common.qrc b/platform/qt/resources/common.qrc
index d02c04a3c2..4c792b057c 100644
--- a/platform/qt/resources/common.qrc
+++ b/platform/qt/resources/common.qrc
@@ -1,6 +1,7 @@
<RCC>
<qresource prefix="/">
<file alias="icon.png">../../../common/icon.png</file>
+ <file alias="default_marker.svg">../../../platform/default/resources/default_marker.svg</file>
<file>source1.geojson</file>
<file>source2.geojson</file>
<file>label-arrow.svg</file>
diff --git a/platform/qt/src/http_file_source.cpp b/platform/qt/src/http_file_source.cpp
index 6831c040b0..573b707c27 100644
--- a/platform/qt/src/http_file_source.cpp
+++ b/platform/qt/src/http_file_source.cpp
@@ -108,4 +108,4 @@ uint32_t HTTPFileSource::maximumConcurrentRequests() {
#endif
}
-} // mbgl
+} // namespace mbgl
diff --git a/platform/qt/src/http_request.cpp b/platform/qt/src/http_request.cpp
index e19316fe2f..6141216c65 100644
--- a/platform/qt/src/http_request.cpp
+++ b/platform/qt/src/http_request.cpp
@@ -118,7 +118,7 @@ void HTTPRequest::handleNetworkReply(QNetworkReply *reply)
}
case 429:
response.error = std::make_unique<Error>(
- Error::Reason::RateLimit, "HTTP status code 429",
+ Error::Reason::RateLimit, "HTTP status code 429",
http::parseRetryHeaders(retryAfter, xRateLimitReset));
break;
default:
diff --git a/platform/qt/src/qmapbox.cpp b/platform/qt/src/qmapbox.cpp
index 379b0cdd57..62bfde4fa8 100644
--- a/platform/qt/src/qmapbox.cpp
+++ b/platform/qt/src/qmapbox.cpp
@@ -4,11 +4,10 @@
#include <mbgl/map/change.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/util/default_styles.hpp>
+#include <mbgl/util/geometry.hpp>
#include <mbgl/util/traits.hpp>
#if QT_VERSION >= 0x050000
-#include "qquickmapboxgl.hpp"
-#include "qquickmapboxglmapparameter.hpp"
#include <QOpenGLContext>
#else
#include <QGLContext>
@@ -18,19 +17,232 @@
static_assert(mbgl::underlying_type(QMapbox::Online) == mbgl::underlying_type(mbgl::NetworkStatus::Status::Online), "error");
static_assert(mbgl::underlying_type(QMapbox::Offline) == mbgl::underlying_type(mbgl::NetworkStatus::Status::Offline), "error");
+// mbgl::FeatureType
+static_assert(mbgl::underlying_type(QMapbox::Feature::PointType) == mbgl::underlying_type(mbgl::FeatureType::Point), "error");
+static_assert(mbgl::underlying_type(QMapbox::Feature::LineStringType) == mbgl::underlying_type(mbgl::FeatureType::LineString), "error");
+static_assert(mbgl::underlying_type(QMapbox::Feature::PolygonType) == mbgl::underlying_type(mbgl::FeatureType::Polygon), "error");
+
namespace QMapbox {
+/*!
+ \namespace QMapbox
+ \inmodule Mapbox Qt SDK
+
+ Contains miscellaneous Mapbox bindings used throughout QMapboxGL.
+*/
+
+/*!
+ \typedef QMapbox::Coordinate
+
+ Alias for QPair<double, double>.
+ Representation for geographical coordinates - latitude and longitude, respectively.
+*/
+
+/*!
+ \typedef QMapbox::CoordinateZoom
+
+ Alias for QPair<Coordinate, double>.
+ Used as return value in QMapboxGL::coordinateZoomForBounds.
+*/
+
+/*!
+ \typedef QMapbox::ProjectedMeters
+
+ Alias for QPair<double, double>.
+ Representation for projected meters - northing and easting, respectively.
+*/
+
+/*!
+ \typedef QMapbox::Coordinates
+
+ Alias for QList<QMapbox::Coordinate>.
+ A list of QMapbox::Coordinate objects.
+*/
+
+/*!
+ \typedef QMapbox::CoordinatesCollection
+
+ Alias for QList<QMapbox::Coordinates>.
+ A list of QMapbox::Coordinates objects.
+*/
+
+/*!
+ \typedef QMapbox::CoordinatesCollections
+
+ Alias for QList<QMapbox::CoordinatesCollection>.
+ A list of QMapbox::CoordinatesCollection objects.
+*/
+
+/*!
+ \class QMapbox::Feature
+
+ \inmodule Mapbox Qt SDK
+
+ Represents \l {https://www.mapbox.com/help/define-features/}{map features}
+ via its \a type (PointType, LineStringType or PolygonType), \a geometry, \a
+ properties map and \a id (optional).
+*/
+
+/*!
+ \enum QMapbox::Feature::Type
+
+ This enum is used as basis for geometry disambiguation in QMapbox::Feature.
+
+ \value PointType A point geometry type. Means a single or a collection of points.
+ \value LineStringType A line string geometry type. Means a single or a collection of line strings.
+ \value PolygonType A polygon geometry type. Means a single or a collection of polygons.
+*/
+
+/*!
+ \class QMapbox::ShapeAnnotationGeometry
+
+ \inmodule Mapbox Qt SDK
+
+ Represents a shape annotation geometry.
+*/
+
+/*!
+ \enum QMapbox::ShapeAnnotationGeometry::Type
+
+ This enum is used as basis for shape annotation geometry disambiguation.
+
+ \value PolygonType A polygon geometry type.
+ \value LineStringType A line string geometry type.
+ \value MultiPolygonType A polygon geometry collection type.
+ \value MultiLineStringType A line string geometry collection type.
+*/
+
+/*!
+ \class QMapbox::SymbolAnnotation
+
+ \inmodule Mapbox Qt SDK
+
+ A symbol annotation comprises of its geometry and an icon identifier.
+*/
+
+/*!
+ \class QMapbox::LineAnnotation
+
+ \inmodule Mapbox Qt SDK
+
+ Represents a line annotation object, along with its properties.
+
+ A line annotation comprises of its geometry and line properties such as opacity, width and color.
+*/
+
+/*!
+ \class QMapbox::FillAnnotation
+
+ \inmodule Mapbox Qt SDK
+
+ Represents a fill annotation object, along with its properties.
+
+ A fill annotation comprises of its geometry and fill properties such as opacity, color and outline color.
+*/
+
+/*!
+ \class QMapbox::StyleSourcedAnnotation
+
+ \inmodule Mapbox Qt SDK
+
+ Represents a style sourced annotation object, along with its properties.
+
+ A style sourced annotation comprises of its geometry and a layer identifier.
+*/
+
+/*!
+ \typedef QMapbox::Annotation
+
+ Alias for QVariant.
+ Container that encapsulates either a symbol, a line, a fill or a style sourced annotation.
+*/
+
+/*!
+ \typedef QMapbox::AnnotationID
+
+ Alias for quint32 representing an annotation identifier.
+*/
+
+/*!
+ \typedef QMapbox::AnnotationIDs
+
+ Alias for QList<quint32> representing a container of annotation identifiers.
+*/
+
+/*!
+ \typedef QMapbox::CustomLayerDeinitializeFunction
+
+ Represents a callback to be called when destroying a custom layer.
+
+ \warning This is used for delegating the rendering of a layer to the user of
+ this API and is not officially supported. Use at your own risk.
+*/
+
+/*!
+ \typedef QMapbox::CustomLayerInitializeFunction
+
+ Represents a callback to be called when initializing a custom layer.
+
+ \warning This is used for delegating the rendering of a layer to the user of
+ this API and is not officially supported. Use at your own risk.
+*/
+
+/*!
+ \typedef QMapbox::CustomLayerRenderFunction
+
+ Represents a callback to be called on each render pass for a custom layer.
+
+ \warning This is used for delegating the rendering of a layer to the user of
+ this API and is not officially supported. Use at your own risk.
+*/
+
+/*!
+ \enum QMapbox::NetworkMode
+
+ This enum represents whether server requests can be performed via network.
+
+ \value Online Server network requests are accessible.
+ \value Offline Only requests to the local cache are accessible.
+*/
+
+/*!
+ \class QMapbox::CustomLayerRenderParameters
+ \inmodule Mapbox Qt SDK
+
+ QMapbox::CustomLayerRenderParameters provides the data passed on each render
+ pass for a custom layer.
+*/
+
+/*!
+ \fn QMapbox::NetworkMode QMapbox::networkMode()
+
+ Returns the current QMapbox::NetworkMode.
+*/
Q_DECL_EXPORT NetworkMode networkMode()
{
return static_cast<NetworkMode>(mbgl::NetworkStatus::Get());
}
+/*!
+ \fn void QMapbox::setNetworkMode(QMapbox::NetworkMode mode)
+
+ Forwards the network status \a mode to Mapbox GL Native engine.
+
+ File source requests uses the available network when \a mode is set to \a
+ Online, otherwise scoped to the local cache.
+*/
Q_DECL_EXPORT void setNetworkMode(NetworkMode mode)
{
mbgl::NetworkStatus::Set(static_cast<mbgl::NetworkStatus::Status>(mode));
}
-Q_DECL_EXPORT QList<QPair<QString, QString>>& defaultStyles()
+/*!
+ \fn QList<QPair<QString, QString> >& QMapbox::defaultStyles()
+
+ Returns a list containing a pair of string objects, representing the style
+ URL and name, respectively.
+*/
+Q_DECL_EXPORT QList<QPair<QString, QString> >& defaultStyles()
{
static QList<QPair<QString, QString>> styles;
@@ -44,6 +256,15 @@ Q_DECL_EXPORT QList<QPair<QString, QString>>& defaultStyles()
return styles;
}
+/*!
+ \fn void QMapbox::initializeGLExtensions()
+
+ Initializes the OpenGL extensions such as Vertex Array Objects (VAOs),
+ required by Mapbox GL Native engine.
+
+ Should be called only once, after an OpenGL context is available.
+ Consecutive calls are ignored.
+*/
Q_DECL_EXPORT void initializeGLExtensions()
{
mbgl::gl::InitializeExtensions([](const char* name) {
@@ -57,12 +278,4 @@ Q_DECL_EXPORT void initializeGLExtensions()
});
}
-Q_DECL_EXPORT void registerTypes()
-{
-#if QT_VERSION >= 0x050000
- qmlRegisterType<QQuickMapboxGL>("QQuickMapboxGL", 1, 0, "MapboxMap");
- qmlRegisterType<QQuickMapboxGLMapParameter>("QQuickMapboxGL", 1, 0, "MapParameter");
-#endif
-}
-
-}
+} // namespace QMapbox
diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp
index 86604a945f..55470a65e1 100644
--- a/platform/qt/src/qmapboxgl.cpp
+++ b/platform/qt/src/qmapboxgl.cpp
@@ -17,15 +17,17 @@
#include <mbgl/style/transition_options.hpp>
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/storage/network_status.hpp>
+#include <mbgl/util/color.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/geo.hpp>
+#include <mbgl/util/geometry.hpp>
#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/shared_thread_pool.hpp>
#include <mbgl/util/traits.hpp>
#if QT_VERSION >= 0x050000
#include <QGuiApplication>
#include <QWindow>
-#include <QOpenGLFramebufferObject>
#include <QOpenGLContext>
#else
#include <QCoreApplication>
@@ -85,20 +87,6 @@ QThreadStorage<std::shared_ptr<mbgl::util::RunLoop>> loop;
// Conversion helper functions.
-auto fromQMapboxGLShapeAnnotation(const ShapeAnnotation &shapeAnnotation) {
- const LineString &lineString = shapeAnnotation.first;
- const QString &styleLayer = shapeAnnotation.second;
-
- mbgl::LineString<double> mbglLineString;
- mbglLineString.reserve(lineString.size());
-
- for (const Coordinate &coordinate : lineString) {
- mbglLineString.emplace_back(mbgl::Point<double> { coordinate.first, coordinate.second });
- }
-
- return mbgl::StyleSourcedAnnotation { std::move(mbglLineString), styleLayer.toStdString() };
-}
-
auto fromQStringList(const QStringList &list)
{
std::vector<std::string> strings;
@@ -848,42 +836,72 @@ void QMapboxGL::setTransitionOptions(qint64 duration, qint64 delay) {
d_ptr->mapObj->setTransitionOptions(mbgl::style::TransitionOptions{ convert(duration), convert(delay) });
}
-mbgl::Annotation fromPointAnnotation(const PointAnnotation &pointAnnotation) {
- const Coordinate &coordinate = pointAnnotation.first;
- const QString &icon = pointAnnotation.second;
- return mbgl::SymbolAnnotation { mbgl::Point<double> { coordinate.second, coordinate.first }, icon.toStdString() };
+mbgl::Annotation asMapboxGLAnnotation(const QMapbox::Annotation & annotation) {
+ auto asMapboxGLGeometry = [](const QMapbox::ShapeAnnotationGeometry &geometry) {
+ mbgl::ShapeAnnotationGeometry result;
+ switch (geometry.type) {
+ case QMapbox::ShapeAnnotationGeometry::LineStringType:
+ result = { asMapboxGLLineString(geometry.geometry.first().first()) };
+ break;
+ case QMapbox::ShapeAnnotationGeometry::PolygonType:
+ result = { asMapboxGLPolygon(geometry.geometry.first()) };
+ break;
+ case QMapbox::ShapeAnnotationGeometry::MultiLineStringType:
+ result = { asMapboxGLMultiLineString(geometry.geometry.first()) };
+ break;
+ case QMapbox::ShapeAnnotationGeometry::MultiPolygonType:
+ result = { asMapboxGLMultiPolygon(geometry.geometry) };
+ break;
+ }
+ return result;
+ };
+
+ if (annotation.canConvert<QMapbox::SymbolAnnotation>()) {
+ QMapbox::SymbolAnnotation symbolAnnotation = annotation.value<QMapbox::SymbolAnnotation>();
+ QMapbox::Coordinate& pair = symbolAnnotation.geometry;
+ return mbgl::SymbolAnnotation { mbgl::Point<double> { pair.second, pair.first }, symbolAnnotation.icon.toStdString() };
+ } else if (annotation.canConvert<QMapbox::LineAnnotation>()) {
+ QMapbox::LineAnnotation lineAnnotation = annotation.value<QMapbox::LineAnnotation>();
+ auto color = mbgl::Color::parse(lineAnnotation.color.name().toStdString());
+ return mbgl::LineAnnotation { asMapboxGLGeometry(lineAnnotation.geometry), lineAnnotation.opacity, lineAnnotation.width, { *color } };
+ } else if (annotation.canConvert<QMapbox::FillAnnotation>()) {
+ QMapbox::FillAnnotation fillAnnotation = annotation.value<QMapbox::FillAnnotation>();
+ auto color = mbgl::Color::parse(fillAnnotation.color.name().toStdString());
+ if (fillAnnotation.outlineColor.canConvert<QColor>()) {
+ auto outlineColor = mbgl::Color::parse(fillAnnotation.outlineColor.value<QColor>().name().toStdString());
+ return mbgl::FillAnnotation { asMapboxGLGeometry(fillAnnotation.geometry), fillAnnotation.opacity, { *color }, { *outlineColor } };
+ } else {
+ return mbgl::FillAnnotation { asMapboxGLGeometry(fillAnnotation.geometry), fillAnnotation.opacity, { *color }, {} };
+ }
+ } else if (annotation.canConvert<QMapbox::StyleSourcedAnnotation>()) {
+ QMapbox::StyleSourcedAnnotation styleSourcedAnnotation = annotation.value<QMapbox::StyleSourcedAnnotation>();
+ return mbgl::StyleSourcedAnnotation { asMapboxGLGeometry(styleSourcedAnnotation.geometry), styleSourcedAnnotation.layerID.toStdString() };
+ }
+
+ qWarning() << "Unable to convert annotation:" << annotation;
+ return {};
}
/*!
- Adds a \a point annotation to the map.
+ Adds an \a annotation to the map.
Returns the unique identifier for the new annotation.
\sa addAnnotationIcon()
*/
-QMapbox::AnnotationID QMapboxGL::addPointAnnotation(const QMapbox::PointAnnotation &point)
+QMapbox::AnnotationID QMapboxGL::addAnnotation(const QMapbox::Annotation &annotation)
{
- return d_ptr->mapObj->addAnnotation(fromPointAnnotation(point));
+ return d_ptr->mapObj->addAnnotation(asMapboxGLAnnotation(annotation));
}
/*!
- Updates an existing \a point annotation referred by \a id.
+ Updates an existing \a annotation referred by \a id.
\sa addAnnotationIcon()
*/
-void QMapboxGL::updatePointAnnotation(QMapbox::AnnotationID id, const QMapbox::PointAnnotation &point)
+void QMapboxGL::updateAnnotation(QMapbox::AnnotationID id, const QMapbox::Annotation &annotation)
{
- d_ptr->mapObj->updateAnnotation(id, fromPointAnnotation(point));
-}
-
-/*!
- Adds a \a shape annotation to the map.
-
- Returns the unique identifier for the new annotation.
-*/
-QMapbox::AnnotationID QMapboxGL::addShapeAnnotation(const QMapbox::ShapeAnnotation &shape)
-{
- return d_ptr->mapObj->addAnnotation(fromQMapboxGLShapeAnnotation(shape));
+ d_ptr->mapObj->updateAnnotation(id, asMapboxGLAnnotation(annotation));
}
/*!
@@ -1087,10 +1105,10 @@ void QMapboxGL::resize(const QSize& size, const QSize& framebufferSize)
Adds an \a icon to the annotation icon pool. This can be later used by the annotation
functions to shown any drawing on the map by referencing its \a name.
- Unlike using addIcon() for runtime styling, annotations added with addPointAnnotation()
+ Unlike using addIcon() for runtime styling, annotations added with addAnnotation()
will survive style changes.
- \sa addPointAnnotation()
+ \sa addAnnotation()
*/
void QMapboxGL::addAnnotationIcon(const QString &name, const QImage &icon)
{
@@ -1100,6 +1118,32 @@ void QMapboxGL::addAnnotationIcon(const QString &name, const QImage &icon)
}
/*!
+ Returns the amount of meters per pixel from a given \a latitude and \a zoom.
+*/
+double QMapboxGL::metersPerPixelAtLatitude(double latitude, double zoom) const
+{
+ return d_ptr->mapObj->getMetersPerPixelAtLatitude(latitude, zoom);
+}
+
+/*!
+ Return the projected meters for a given \a coordinate_ object.
+*/
+QMapbox::ProjectedMeters QMapboxGL::projectedMetersForCoordinate(const QMapbox::Coordinate &coordinate_) const
+{
+ auto projectedMeters = d_ptr->mapObj->projectedMetersForLatLng(mbgl::LatLng { coordinate_.first, coordinate_.second });
+ return QMapbox::ProjectedMeters(projectedMeters.northing, projectedMeters.easting);
+}
+
+/*!
+ Returns the coordinate for a given \a projectedMeters object.
+*/
+QMapbox::Coordinate QMapboxGL::coordinateForProjectedMeters(const QMapbox::ProjectedMeters &projectedMeters) const
+{
+ auto latLng = d_ptr->mapObj->latLngForProjectedMeters(mbgl::ProjectedMeters { projectedMeters.first, projectedMeters.second });
+ return QMapbox::Coordinate(latLng.latitude, latLng.longitude);
+}
+
+/*!
\fn QMapboxGL::pixelForCoordinate(const QMapbox::Coordinate &coordinate) const
Returns the offset in pixels for \a coordinate. The origin pixel coordinate is
@@ -1226,6 +1270,14 @@ void QMapboxGL::addSource(const QString &id, const QVariantMap &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/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