summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPaolo Angelelli <paolo.angelelli.qt@gmail.com>2019-11-27 15:37:07 +0100
committerpaolo <paolo.angelelli.qt@gmail.com>2020-02-11 11:46:08 +0100
commitd055098540df99a5d426360e9322c659e678e5ee (patch)
treee7e43a057b7e06814e2ef11eeddc52a4aac06cdb /src
parent614d67be158e3ef8443c1b7f3126303cfcf4becc (diff)
Enable mercator-to-screen projection in GLSL
With this change, all the geo-to-screen conversion, and the triangulation operations for geo polylines and geo polygon are performed either at set time or in the shader. A separate bounding box geometry is processed in the old way to provide a correct QtQuick Item geometry, that can be used for nesting mouse areas, performing translations, input event delivery, etc. With this approach, performance are improved by more than one order of magnitude in average, but complex geometries will of course benefit more. It also adds correct rendering support for polygons with holes, previously only rendered correctly by the MapboxGL plugin. The polyline shader has basic miter joins. The miter is skipped if the angle is too sharp to avoid complicating the implementation. This shader introduces some glitches when the polyline is minified, for which the real fix is to have LOD for the geometry, and render simplified geometries at low zoom levels (added in a subsequent patch). Note: this approach, at least in its current implementation, does not support enabling layers on individual items, only on the Map element. Task-number: QTBUG-49303 Task-number: QTBUG-38459 Change-Id: I0c2dc0bf364d32f74ca7c4014f6d66e6219c8ae4 Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/imports/location/location.cpp6
-rw-r--r--src/location/declarativemaps/declarativemaps.pri13
-rw-r--r--src/location/declarativemaps/qdeclarativecirclemapitem.cpp391
-rw-r--r--src/location/declarativemaps/qdeclarativecirclemapitem_p.h57
-rw-r--r--src/location/declarativemaps/qdeclarativecirclemapitem_p_p.h448
-rw-r--r--src/location/declarativemaps/qdeclarativegeomap.cpp37
-rw-r--r--src/location/declarativemaps/qdeclarativegeomap_p.h3
-rw-r--r--src/location/declarativemaps/qdeclarativegeomapitembase.cpp2
-rw-r--r--src/location/declarativemaps/qdeclarativegeomapitembase_p.h22
-rw-r--r--src/location/declarativemaps/qdeclarativegeomapitemutils.cpp180
-rw-r--r--src/location/declarativemaps/qdeclarativegeomapitemutils_p.h122
-rw-r--r--src/location/declarativemaps/qdeclarativepolygonmapitem.cpp639
-rw-r--r--src/location/declarativemaps/qdeclarativepolygonmapitem_p.h91
-rw-r--r--src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h664
-rw-r--r--src/location/declarativemaps/qdeclarativepolylinemapitem.cpp869
-rw-r--r--src/location/declarativemaps/qdeclarativepolylinemapitem_p.h120
-rw-r--r--src/location/declarativemaps/qdeclarativepolylinemapitem_p_p.h823
-rw-r--r--src/location/declarativemaps/qdeclarativerectanglemapitem.cpp236
-rw-r--r--src/location/declarativemaps/qdeclarativerectanglemapitem_p.h41
-rw-r--r--src/location/declarativemaps/qdeclarativerectanglemapitem_p_p.h416
-rw-r--r--src/location/declarativemaps/qgeomapitemgeometry.cpp5
-rw-r--r--src/location/declarativemaps/qgeomapitemgeometry_p.h5
-rw-r--r--src/location/labs/qsg/qmapcircleobjectqsg.cpp6
-rw-r--r--src/location/labs/qsg/qmapcircleobjectqsg_p_p.h2
-rw-r--r--src/location/labs/qsg/qmappolygonobjectqsg_p_p.h2
-rw-r--r--src/location/labs/qsg/qqsgmapobject_p.h2
-rw-r--r--src/location/maps/qgeomap.cpp7
-rw-r--r--src/location/maps/qgeomap_p.h3
-rw-r--r--src/location/maps/qgeoprojection.cpp72
-rw-r--r--src/location/maps/qgeoprojection_p.h25
-rw-r--r--src/location/maps/qgeotiledmap.cpp4
-rw-r--r--src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp7
-rw-r--r--src/positioning/qgeopolygon.cpp1
-rw-r--r--src/positioning/qlocationutils_p.h7
34 files changed, 4499 insertions, 829 deletions
diff --git a/src/imports/location/location.cpp b/src/imports/location/location.cpp
index fd53ad7a..7876e45a 100644
--- a/src/imports/location/location.cpp
+++ b/src/imports/location/location.cpp
@@ -206,6 +206,12 @@ public:
qmlRegisterUncreatableType<QDeclarativeGeoMapItemBase, 14>(uri, major, minor, "GeoMapItemBase",
QStringLiteral("GeoMapItemBase is not intended instantiable by developer."));
+ minor = 15;
+ qmlRegisterType<QDeclarativePolylineMapItem, 15>(uri, major, minor, "MapPolyline");
+ qmlRegisterType<QDeclarativePolygonMapItem, 15>(uri, major, minor, "MapPolygon");
+ qmlRegisterType<QDeclarativeRectangleMapItem, 15>(uri, major, minor, "MapRectangle");
+ qmlRegisterType<QDeclarativeCircleMapItem, 15>(uri, major, minor, "MapCircle");
+
// Register the latest Qt version as QML type version
qmlRegisterModule(uri, QT_VERSION_MAJOR, QT_VERSION_MINOR);
diff --git a/src/location/declarativemaps/declarativemaps.pri b/src/location/declarativemaps/declarativemaps.pri
index 54a0824b..c2afe9a4 100644
--- a/src/location/declarativemaps/declarativemaps.pri
+++ b/src/location/declarativemaps/declarativemaps.pri
@@ -29,12 +29,18 @@ PRIVATE_HEADERS += \
declarativemaps/qgeomapobject_p.h \
declarativemaps/qgeomapobject_p_p.h \
declarativemaps/qparameterizableobject_p.h \
+ declarativemaps/qdeclarativegeomapitemutils_p.h \
+ declarativemaps/qdeclarativepolylinemapitem_p_p.h \
+ declarativemaps/qdeclarativepolygonmapitem_p_p.h \
+ declarativemaps/qdeclarativerectanglemapitem_p_p.h \
+ declarativemaps/qdeclarativecirclemapitem_p_p.h \
declarativemaps/qquickgeomapgesturearea_p.h
SOURCES += \
declarativemaps/error_messages.cpp \
declarativemaps/locationvaluetypehelper.cpp \
- declarativemaps/qdeclarativecirclemapitem.cpp \
+ declarativemaps/qdeclarativepolylinemapitem.cpp \
+ declarativemaps/qdeclarativepolygonmapitem.cpp \
declarativemaps/qdeclarativegeocodemodel.cpp \
declarativemaps/qdeclarativegeomaneuver.cpp \
declarativemaps/qdeclarativegeomapcopyrightsnotice.cpp \
@@ -50,14 +56,15 @@ SOURCES += \
declarativemaps/qdeclarativegeoroutemodel.cpp \
declarativemaps/qdeclarativegeoroutesegment.cpp \
declarativemaps/qdeclarativegeoserviceprovider.cpp \
- declarativemaps/qdeclarativepolygonmapitem.cpp \
- declarativemaps/qdeclarativepolylinemapitem.cpp \
+ declarativemaps/qdeclarativecirclemapitem.cpp \
declarativemaps/qdeclarativerectanglemapitem.cpp \
declarativemaps/qdeclarativeroutemapitem.cpp \
declarativemaps/qgeomapitemgeometry.cpp \
declarativemaps/qgeomapobject.cpp \
+ declarativemaps/qdeclarativegeomapitemutils.cpp \
declarativemaps/qparameterizableobject.cpp \
declarativemaps/qquickgeomapgesturearea.cpp
load(qt_build_paths)
LIBS_PRIVATE += -L$$MODULE_BASE_OUTDIR/lib -lpoly2tri$$qtPlatformTargetSuffix() -lclip2tri$$qtPlatformTargetSuffix()
+
diff --git a/src/location/declarativemaps/qdeclarativecirclemapitem.cpp b/src/location/declarativemaps/qdeclarativecirclemapitem.cpp
index 846fccbf..f0ba122c 100644
--- a/src/location/declarativemaps/qdeclarativecirclemapitem.cpp
+++ b/src/location/declarativemaps/qdeclarativecirclemapitem.cpp
@@ -57,6 +57,7 @@
#include <sweep/cdt.h>
#include <QtPositioning/private/qclipperutils_p.h>
+#include "qdeclarativecirclemapitem_p_p.h"
QT_BEGIN_NAMESPACE
@@ -274,83 +275,36 @@ void QGeoMapCircleGeometry::updateScreenPointsInvert(const QList<QDoubleVector2D
sourceBounds_ = screenBounds_;
}
-bool QDeclarativeCircleMapItem::crossEarthPole(const QGeoCoordinate &center, qreal distance)
+struct CircleBackendSelector
{
- qreal poleLat = 90;
- QGeoCoordinate northPole = QGeoCoordinate(poleLat, center.longitude());
- QGeoCoordinate southPole = QGeoCoordinate(-poleLat, center.longitude());
- // approximate using great circle distance
- qreal distanceToNorthPole = center.distanceTo(northPole);
- qreal distanceToSouthPole = center.distanceTo(southPole);
- if (distanceToNorthPole < distance || distanceToSouthPole < distance)
- return true;
- return false;
-}
-
-void QDeclarativeCircleMapItem::calculatePeripheralPoints(QList<QGeoCoordinate> &path,
- const QGeoCoordinate &center,
- qreal distance,
- int steps,
- QGeoCoordinate &leftBound)
-{
- // Calculate points based on great-circle distance
- // Calculation is the same as GeoCoordinate's atDistanceAndAzimuth function
- // but tweaked here for computing multiple points
-
- // pre-calculations
- steps = qMax(steps, 3);
- qreal centerLon = center.longitude();
- qreal minLon = centerLon;
- qreal latRad = QLocationUtils::radians(center.latitude());
- qreal lonRad = QLocationUtils::radians(centerLon);
- qreal cosLatRad = std::cos(latRad);
- qreal sinLatRad = std::sin(latRad);
- qreal ratio = (distance / QLocationUtils::earthMeanRadius());
- qreal cosRatio = std::cos(ratio);
- qreal sinRatio = std::sin(ratio);
- qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio;
- qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio;
- int idx = 0;
- for (int i = 0; i < steps; ++i) {
- qreal azimuthRad = 2 * M_PI * i / steps;
- qreal resultLatRad = std::asin(sinLatRad_x_cosRatio
- + cosLatRad_x_sinRatio * std::cos(azimuthRad));
- qreal resultLonRad = lonRad + std::atan2(std::sin(azimuthRad) * cosLatRad_x_sinRatio,
- cosRatio - sinLatRad * std::sin(resultLatRad));
- qreal lat2 = QLocationUtils::degrees(resultLatRad);
- qreal lon2 = QLocationUtils::wrapLong(QLocationUtils::degrees(resultLonRad));
-
- path << QGeoCoordinate(lat2, lon2, center.altitude());
- // Consider only points in the left half of the circle for the left bound.
- if (azimuthRad > M_PI) {
- if (lon2 > centerLon) // if point and center are on different hemispheres
- lon2 -= 360;
- if (lon2 < minLon) {
- minLon = lon2;
- idx = i;
- }
- }
+ CircleBackendSelector()
+ {
+ backend = (qgetenv("QTLOCATION_OPENGL_ITEMS").toInt()) ? QDeclarativeCircleMapItem::OpenGL : QDeclarativeCircleMapItem::Software;
}
- leftBound = path.at(idx);
-}
+ QDeclarativeCircleMapItem::Backend backend = QDeclarativeCircleMapItem::Software;
+};
+
+Q_GLOBAL_STATIC(CircleBackendSelector, mapCircleBackendSelector)
QDeclarativeCircleMapItem::QDeclarativeCircleMapItem(QQuickItem *parent)
-: QDeclarativeGeoMapItemBase(parent), border_(this), color_(Qt::transparent), dirtyMaterial_(true),
- updatingGeometry_(false)
+: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent), m_dirtyMaterial(true),
+ m_updatingGeometry(false)
+ , m_d(new QDeclarativeCircleMapItemPrivateCPU(*this))
{
+ // ToDo: handle envvar, and switch implementation.
m_itemType = QGeoMap::MapCircle;
setFlag(ItemHasContents, true);
- QObject::connect(&border_, SIGNAL(colorChanged(QColor)),
- this, SLOT(markSourceDirtyAndUpdate()));
- QObject::connect(&border_, SIGNAL(widthChanged(qreal)),
- this, SLOT(markSourceDirtyAndUpdate()));
+ QObject::connect(&m_border, SIGNAL(colorChanged(QColor)),
+ this, SLOT(onLinePropertiesChanged()));
+ QObject::connect(&m_border, SIGNAL(widthChanged(qreal)),
+ this, SLOT(onLinePropertiesChanged()));
// assume that circles are not self-intersecting
// to speed up processing
// FIXME: unfortunately they self-intersect at the poles due to current drawing method
// so the line is commented out until fixed
//geometry_.setAssumeSimple(true);
-
+ setBackend(mapCircleBackendSelector->backend);
}
QDeclarativeCircleMapItem::~QDeclarativeCircleMapItem()
@@ -371,23 +325,24 @@ QDeclarativeCircleMapItem::~QDeclarativeCircleMapItem()
*/
QDeclarativeMapLineProperties *QDeclarativeCircleMapItem::border()
{
- return &border_;
+ return &m_border;
}
void QDeclarativeCircleMapItem::markSourceDirtyAndUpdate()
{
- geometry_.markSourceDirty();
- borderGeometry_.markSourceDirty();
- polishAndUpdate();
+ m_d->markSourceDirtyAndUpdate();
+}
+
+void QDeclarativeCircleMapItem::onLinePropertiesChanged()
+{
+ m_d->onLinePropertiesChanged();
}
void QDeclarativeCircleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map)
{
QDeclarativeGeoMapItemBase::setMap(quickMap,map);
- if (!map)
- return;
- updateCirclePath();
- markSourceDirtyAndUpdate();
+ if (map)
+ m_d->onMapSet();
}
/*!
@@ -399,18 +354,18 @@ void QDeclarativeCircleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *ma
*/
void QDeclarativeCircleMapItem::setCenter(const QGeoCoordinate &center)
{
- if (circle_.center() == center)
+ if (m_circle.center() == center)
return;
- circle_.setCenter(center);
- updateCirclePath();
- markSourceDirtyAndUpdate();
+ possiblySwitchBackend(m_circle.center(), m_circle.radius(), center, m_circle.radius());
+ m_circle.setCenter(center);
+ m_d->onGeoGeometryChanged();
emit centerChanged(center);
}
QGeoCoordinate QDeclarativeCircleMapItem::center()
{
- return circle_.center();
+ return m_circle.center();
}
/*!
@@ -421,17 +376,17 @@ QGeoCoordinate QDeclarativeCircleMapItem::center()
*/
void QDeclarativeCircleMapItem::setColor(const QColor &color)
{
- if (color_ == color)
+ if (m_color == color)
return;
- color_ = color;
- dirtyMaterial_ = true;
+ m_color = color;
+ m_dirtyMaterial = true;
update();
- emit colorChanged(color_);
+ emit colorChanged(m_color);
}
QColor QDeclarativeCircleMapItem::color() const
{
- return color_;
+ return m_color;
}
/*!
@@ -443,18 +398,18 @@ QColor QDeclarativeCircleMapItem::color() const
*/
void QDeclarativeCircleMapItem::setRadius(qreal radius)
{
- if (circle_.radius() == radius)
+ if (m_circle.radius() == radius)
return;
- circle_.setRadius(radius);
- updateCirclePath();
- markSourceDirtyAndUpdate();
+ possiblySwitchBackend(m_circle.center(), m_circle.radius(), m_circle.center(), radius);
+ m_circle.setRadius(radius);
+ m_d->onGeoGeometryChanged();
emit radiusChanged(radius);
}
qreal QDeclarativeCircleMapItem::radius() const
{
- return circle_.radius();
+ return m_circle.radius();
}
/*!
@@ -472,23 +427,7 @@ qreal QDeclarativeCircleMapItem::radius() const
*/
QSGNode *QDeclarativeCircleMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
{
- Q_UNUSED(data);
-
- MapPolygonNode *node = static_cast<MapPolygonNode *>(oldNode);
-
- if (!node)
- node = new MapPolygonNode();
-
- //TODO: update only material
- if (geometry_.isScreenDirty() || borderGeometry_.isScreenDirty() || dirtyMaterial_) {
- node->update(color_, border_.color(), &geometry_, &borderGeometry_);
- geometry_.setPreserveGeometry(false);
- borderGeometry_.setPreserveGeometry(false);
- geometry_.markClean();
- borderGeometry_.markClean();
- dirtyMaterial_ = false;
- }
- return node;
+ return m_d->updateMapItemPaintNode(oldNode, data);
}
/*!
@@ -498,111 +437,41 @@ void QDeclarativeCircleMapItem::updatePolish()
{
if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
return;
- if (!circle_.isValid()) {
- geometry_.clear();
- borderGeometry_.clear();
- setWidth(0);
- setHeight(0);
- return;
- }
-
- const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection());
- QScopedValueRollback<bool> rollback(updatingGeometry_);
- updatingGeometry_ = true;
-
- QList<QDoubleVector2D> circlePath = circlePath_;
-
- int pathCount = circlePath.size();
- bool preserve = preserveCircleGeometry(circlePath, circle_.center(), circle_.radius(), p);
- // using leftBound_ instead of the analytically calculated circle_.boundingGeoRectangle().topLeft());
- // to fix QTBUG-62154
- geometry_.setPreserveGeometry(true, leftBound_); // to set the geoLeftBound_
- geometry_.setPreserveGeometry(preserve, leftBound_);
-
- bool invertedCircle = false;
- if (crossEarthPole(circle_.center(), circle_.radius()) && circlePath.size() == pathCount) {
- geometry_.updateScreenPointsInvert(circlePath, *map()); // invert fill area for really huge circles
- invertedCircle = true;
- } else {
- geometry_.updateSourcePoints(*map(), circlePath);
- geometry_.updateScreenPoints(*map(), border_.width());
- }
-
- borderGeometry_.clear();
- QList<QGeoMapItemGeometry *> geoms;
- geoms << &geometry_;
-
- if (border_.color() != Qt::transparent && border_.width() > 0) {
- QList<QDoubleVector2D> closedPath = circlePath;
- closedPath << closedPath.first();
-
- if (invertedCircle) {
- closedPath = circlePath_;
- closedPath << closedPath.first();
- std::reverse(closedPath.begin(), closedPath.end());
- }
-
- borderGeometry_.setPreserveGeometry(true, leftBound_);
- borderGeometry_.setPreserveGeometry(preserve, leftBound_);
-
- // Use srcOrigin_ from fill geometry after clipping to ensure that translateToCommonOrigin won't fail.
- const QGeoCoordinate &geometryOrigin = geometry_.origin();
-
- borderGeometry_.srcPoints_.clear();
- borderGeometry_.srcPointTypes_.clear();
-
- QDoubleVector2D borderLeftBoundWrapped;
- QList<QList<QDoubleVector2D > > clippedPaths = borderGeometry_.clipPath(*map(), closedPath, borderLeftBoundWrapped);
- if (clippedPaths.size()) {
- borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin);
- borderGeometry_.pathToScreen(*map(), clippedPaths, borderLeftBoundWrapped);
- borderGeometry_.updateScreenPoints(*map(), border_.width());
- geoms << &borderGeometry_;
- } else {
- borderGeometry_.clear();
- }
- }
-
- QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms);
-
- if (invertedCircle || !preserve) {
- setWidth(combined.width());
- setHeight(combined.height());
- setPositionOnMap(geometry_.origin(), geometry_.firstPointOffset());
- } else {
- setWidth(combined.width() + 2 * border_.width());
- setHeight(combined.height() + 2 * border_.width());
- }
-
- // No offsetting here, even in normal case, because first point offset is already translated
- setPositionOnMap(geometry_.origin(), geometry_.firstPointOffset());
+ m_d->updatePolish();
}
/*!
\internal
+
+ The OpenGL backend doesn't do circles crossing poles yet.
+ So if that backend is selected and the circle crosses the poles, use the CPU backend instead.
*/
-void QDeclarativeCircleMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event)
+void QDeclarativeCircleMapItem::possiblySwitchBackend(const QGeoCoordinate &oldCenter, qreal oldRadius, const QGeoCoordinate &newCenter, qreal newRadius)
{
- if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0)
+ if (m_backend != QDeclarativeCircleMapItem::OpenGL)
return;
- markSourceDirtyAndUpdate();
+ // if old does not cross and new crosses, move to CPU.
+ if (!QDeclarativeCircleMapItemPrivate::crossEarthPole(oldCenter, oldRadius)
+ && !QDeclarativeCircleMapItemPrivate::crossEarthPole(newCenter, newRadius)) {
+ QScopedPointer<QDeclarativeCircleMapItemPrivate> d(static_cast<QDeclarativeCircleMapItemPrivate *>(new QDeclarativeCircleMapItemPrivateCPU(*this)));
+ m_d.swap(d);
+ } else if (QDeclarativeCircleMapItemPrivate::crossEarthPole(oldCenter, oldRadius)
+ && !QDeclarativeCircleMapItemPrivate::crossEarthPole(newCenter, newRadius)) { // else if old crosses and new does not cross, move back to OpenGL
+ QScopedPointer<QDeclarativeCircleMapItemPrivate> d(static_cast<QDeclarativeCircleMapItemPrivate *>(new QDeclarativeCircleMapItemPrivateOpenGL(*this)));
+ m_d.swap(d);
+ }
}
/*!
\internal
*/
-void QDeclarativeCircleMapItem::updateCirclePath()
+void QDeclarativeCircleMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event)
{
- if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
+ if (event.mapSize.isEmpty())
return;
- const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection());
- QList<QGeoCoordinate> path;
- calculatePeripheralPoints(path, circle_.center(), circle_.radius(), CircleSamples, leftBound_);
- circlePath_.clear();
- for (const QGeoCoordinate &c : path)
- circlePath_ << p.geoToMapProjection(c);
+ m_d->afterViewportChanged();
}
/*!
@@ -610,30 +479,69 @@ void QDeclarativeCircleMapItem::updateCirclePath()
*/
bool QDeclarativeCircleMapItem::contains(const QPointF &point) const
{
- return (geometry_.contains(point) || borderGeometry_.contains(point));
+ return m_d->contains(point);
+ //
}
const QGeoShape &QDeclarativeCircleMapItem::geoShape() const
{
- return circle_;
+ return m_circle;
}
void QDeclarativeCircleMapItem::setGeoShape(const QGeoShape &shape)
{
- if (shape == circle_)
+ if (shape == m_circle)
return;
const QGeoCircle circle(shape); // if shape isn't a circle, circle will be created as a default-constructed circle
- const bool centerHasChanged = circle.center() != circle_.center();
- const bool radiusHasChanged = circle.radius() != circle_.radius();
- circle_ = circle;
+ const bool centerHasChanged = circle.center() != m_circle.center();
+ const bool radiusHasChanged = circle.radius() != m_circle.radius();
+ possiblySwitchBackend(m_circle.center(), m_circle.radius(), circle.center(), circle.radius());
+ m_circle = circle;
- updateCirclePath();
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryChanged();
if (centerHasChanged)
- emit centerChanged(circle_.center());
+ emit centerChanged(m_circle.center());
if (radiusHasChanged)
- emit radiusChanged(circle_.radius());
+ emit radiusChanged(m_circle.radius());
+}
+
+/*!
+ \qmlproperty MapCircle.Backend QtLocation::MapCircle::backend
+
+ This property holds which backend is in use to render the map item.
+ Valid values are \b MapCircle.Software and \b{MapCircle.OpenGL}.
+ The default value is \b{MapCircle.Software}.
+
+ \note \b{The release of this API with Qt 5.15 is a Technology Preview}.
+ Ideally, as the OpenGL backends for map items mature, there will be
+ no more need to also offer the legacy software-projection backend.
+ So this property will likely disappear at some later point.
+ To select OpenGL-accelerated item backends without using this property,
+ it is also possible to set the environment variable \b QTLOCATION_OPENGL_ITEMS
+ to \b{1}.
+ Also note that all current OpenGL backends won't work as expected when enabling
+ layers on the individual item, or when running on OpenGL core profiles greater than 2.x.
+
+ \since 5.15
+*/
+
+QDeclarativeCircleMapItem::Backend QDeclarativeCircleMapItem::backend() const
+{
+ return m_backend;
+}
+
+void QDeclarativeCircleMapItem::setBackend(QDeclarativeCircleMapItem::Backend b)
+{
+ if (b == m_backend)
+ return;
+ m_backend = b;
+ QScopedPointer<QDeclarativeCircleMapItemPrivate> d((m_backend == Software)
+ ? static_cast<QDeclarativeCircleMapItemPrivate *>(new QDeclarativeCircleMapItemPrivateCPU(*this))
+ : static_cast<QDeclarativeCircleMapItemPrivate * >(new QDeclarativeCircleMapItemPrivateOpenGL(*this)));
+ m_d.swap(d);
+ m_d->onGeoGeometryChanged();
+ emit backendChanged();
}
/*!
@@ -641,21 +549,27 @@ void QDeclarativeCircleMapItem::setGeoShape(const QGeoShape &shape)
*/
void QDeclarativeCircleMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
- if (!map() || !circle_.isValid() || updatingGeometry_ || newGeometry == oldGeometry) {
+ if (!map() || !m_circle.isValid() || m_updatingGeometry || newGeometry == oldGeometry) {
QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry);
return;
}
- QDoubleVector2D newPoint = QDoubleVector2D(x(),y()) + QDoubleVector2D(width(), height()) / 2;
+ QDoubleVector2D newPoint = QDoubleVector2D(x(),y()) + QDoubleVector2D(width(), height()) * 0.5;
QGeoCoordinate newCoordinate = map()->geoProjection().itemPositionToCoordinate(newPoint, false);
if (newCoordinate.isValid())
- setCenter(newCoordinate);
+ setCenter(newCoordinate); // ToDo: this is incorrect. setting such center might yield to another geometry changed.
// Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested
// call to this function.
}
-bool QDeclarativeCircleMapItem::preserveCircleGeometry (QList<QDoubleVector2D> &path,
+QDeclarativeCircleMapItemPrivate::~QDeclarativeCircleMapItemPrivate() {}
+
+QDeclarativeCircleMapItemPrivateCPU::~QDeclarativeCircleMapItemPrivateCPU() {}
+
+QDeclarativeCircleMapItemPrivateOpenGL::~QDeclarativeCircleMapItemPrivateOpenGL() {}
+
+bool QDeclarativeCircleMapItemPrivate::preserveCircleGeometry (QList<QDoubleVector2D> &path,
const QGeoCoordinate &center, qreal distance, const QGeoProjectionWebMercator &p)
{
// if circle crosses north/south pole, then don't preserve circular shape,
@@ -664,7 +578,6 @@ bool QDeclarativeCircleMapItem::preserveCircleGeometry (QList<QDoubleVector2D> &
return false;
}
return true;
-
}
/*
@@ -684,7 +597,7 @@ bool QDeclarativeCircleMapItem::preserveCircleGeometry (QList<QDoubleVector2D> &
* | ____ |
* \__/ \__/
*/
-void QDeclarativeCircleMapItem::updateCirclePathForRendering(QList<QDoubleVector2D> &path,
+void QDeclarativeCircleMapItemPrivate::updateCirclePathForRendering(QList<QDoubleVector2D> &path,
const QGeoCoordinate &center,
qreal distance, const QGeoProjectionWebMercator &p)
{
@@ -743,6 +656,66 @@ void QDeclarativeCircleMapItem::updateCirclePathForRendering(QList<QDoubleVector
}
}
+bool QDeclarativeCircleMapItemPrivate::crossEarthPole(const QGeoCoordinate &center, qreal distance)
+{
+ qreal poleLat = 90;
+ QGeoCoordinate northPole = QGeoCoordinate(poleLat, center.longitude());
+ QGeoCoordinate southPole = QGeoCoordinate(-poleLat, center.longitude());
+ // approximate using great circle distance
+ qreal distanceToNorthPole = center.distanceTo(northPole);
+ qreal distanceToSouthPole = center.distanceTo(southPole);
+ if (distanceToNorthPole < distance || distanceToSouthPole < distance)
+ return true;
+ return false;
+}
+
+void QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(QList<QGeoCoordinate> &path,
+ const QGeoCoordinate &center,
+ qreal distance,
+ int steps,
+ QGeoCoordinate &leftBound)
+{
+ // Calculate points based on great-circle distance
+ // Calculation is the same as GeoCoordinate's atDistanceAndAzimuth function
+ // but tweaked here for computing multiple points
+
+ // pre-calculations
+ steps = qMax(steps, 3);
+ qreal centerLon = center.longitude();
+ qreal minLon = centerLon;
+ qreal latRad = QLocationUtils::radians(center.latitude());
+ qreal lonRad = QLocationUtils::radians(centerLon);
+ qreal cosLatRad = std::cos(latRad);
+ qreal sinLatRad = std::sin(latRad);
+ qreal ratio = (distance / QLocationUtils::earthMeanRadius());
+ qreal cosRatio = std::cos(ratio);
+ qreal sinRatio = std::sin(ratio);
+ qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio;
+ qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio;
+ int idx = 0;
+ for (int i = 0; i < steps; ++i) {
+ qreal azimuthRad = 2 * M_PI * i / steps;
+ qreal resultLatRad = std::asin(sinLatRad_x_cosRatio
+ + cosLatRad_x_sinRatio * std::cos(azimuthRad));
+ qreal resultLonRad = lonRad + std::atan2(std::sin(azimuthRad) * cosLatRad_x_sinRatio,
+ cosRatio - sinLatRad * std::sin(resultLatRad));
+ qreal lat2 = QLocationUtils::degrees(resultLatRad);
+ qreal lon2 = QLocationUtils::wrapLong(QLocationUtils::degrees(resultLonRad));
+
+ path << QGeoCoordinate(lat2, lon2, center.altitude());
+ // Consider only points in the left half of the circle for the left bound.
+ if (azimuthRad > M_PI) {
+ if (lon2 > centerLon) // if point and center are on different hemispheres
+ lon2 -= 360;
+ if (lon2 < minLon) {
+ minLon = lon2;
+ idx = i;
+ }
+ }
+ }
+ leftBound = path.at(idx);
+}
+
//////////////////////////////////////////////////////////////////////
QT_END_NAMESPACE
diff --git a/src/location/declarativemaps/qdeclarativecirclemapitem_p.h b/src/location/declarativemaps/qdeclarativecirclemapitem_p.h
index 4b3f81c0..a6715e30 100644
--- a/src/location/declarativemaps/qdeclarativecirclemapitem_p.h
+++ b/src/location/declarativemaps/qdeclarativecirclemapitem_p.h
@@ -50,33 +50,33 @@
#include <QtLocation/private/qlocationglobal_p.h>
#include <QtLocation/private/qdeclarativegeomapitembase_p.h>
-#include <QtLocation/private/qdeclarativepolylinemapitem_p.h>
-#include <QtLocation/private/qdeclarativepolygonmapitem_p.h>
+#include <QtLocation/private/qdeclarativepolylinemapitem_p_p.h>
#include <QSGGeometryNode>
#include <QSGFlatColorMaterial>
#include <QtPositioning/QGeoCircle>
QT_BEGIN_NAMESPACE
-class Q_LOCATION_PRIVATE_EXPORT QGeoMapCircleGeometry : public QGeoMapPolygonGeometry
-{
-public:
- QGeoMapCircleGeometry();
-
- void updateScreenPointsInvert(const QList<QDoubleVector2D> &circlePath, const QGeoMap &map);
-};
-
+class QDeclarativeCircleMapItemPrivate;
class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCircleMapItem : public QDeclarativeGeoMapItemBase
{
Q_OBJECT
+ Q_ENUMS(Backend)
+
Q_PROPERTY(QGeoCoordinate center READ center WRITE setCenter NOTIFY centerChanged)
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(QDeclarativeMapLineProperties *border READ border CONSTANT)
+ Q_PROPERTY(Backend backend READ backend WRITE setBackend NOTIFY backendChanged REVISION 15)
public:
- explicit QDeclarativeCircleMapItem(QQuickItem *parent = 0);
- ~QDeclarativeCircleMapItem();
+ enum Backend {
+ Software = 0,
+ OpenGL = 1
+ };
+
+ explicit QDeclarativeCircleMapItem(QQuickItem *parent = nullptr);
+ ~QDeclarativeCircleMapItem() override;
virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) override;
virtual QSGNode *updateMapItemPaintNode(QSGNode *, UpdatePaintNodeData *) override;
@@ -96,40 +96,41 @@ public:
const QGeoShape &geoShape() const override;
void setGeoShape(const QGeoShape &shape) override;
- static bool crossEarthPole(const QGeoCoordinate &center, qreal distance);
- static void calculatePeripheralPoints(QList<QGeoCoordinate> &path, const QGeoCoordinate &center,
- qreal distance, int steps, QGeoCoordinate &leftBound);
- static bool preserveCircleGeometry(QList<QDoubleVector2D> &path, const QGeoCoordinate &center,
- qreal distance, const QGeoProjectionWebMercator &p);
- static void updateCirclePathForRendering(QList<QDoubleVector2D> &path, const QGeoCoordinate &center,
- qreal distance, const QGeoProjectionWebMercator &p);
+ Backend backend() const;
+ void setBackend(Backend b);
Q_SIGNALS:
void centerChanged(const QGeoCoordinate &center);
void radiusChanged(qreal radius);
void colorChanged(const QColor &color);
+ void backendChanged();
protected:
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
void updatePolish() override;
+ void possiblySwitchBackend(const QGeoCoordinate &oldCenter, qreal oldRadius, const QGeoCoordinate &newCenter, qreal newRadius);
protected Q_SLOTS:
void markSourceDirtyAndUpdate();
+ void onLinePropertiesChanged();
virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) override;
private:
void updateCirclePath();
private:
- QGeoCircle circle_;
- QDeclarativeMapLineProperties border_;
- QColor color_;
- QList<QDoubleVector2D> circlePath_;
- QGeoCoordinate leftBound_;
- bool dirtyMaterial_;
- QGeoMapCircleGeometry geometry_;
- QGeoMapPolylineGeometry borderGeometry_;
- bool updatingGeometry_;
+ QGeoCircle m_circle;
+ QDeclarativeMapLineProperties m_border;
+ QColor m_color;
+ bool m_dirtyMaterial;
+ bool m_updatingGeometry;
+ Backend m_backend = Software;
+
+ QScopedPointer<QDeclarativeCircleMapItemPrivate> m_d;
+
+ friend class QDeclarativeCircleMapItemPrivate;
+ friend class QDeclarativeCircleMapItemPrivateCPU;
+ friend class QDeclarativeCircleMapItemPrivateOpenGL;
};
//////////////////////////////////////////////////////////////////////
diff --git a/src/location/declarativemaps/qdeclarativecirclemapitem_p_p.h b/src/location/declarativemaps/qdeclarativecirclemapitem_p_p.h
new file mode 100644
index 00000000..71a6b6a4
--- /dev/null
+++ b/src/location/declarativemaps/qdeclarativecirclemapitem_p_p.h
@@ -0,0 +1,448 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Paolo Angelelli <paolo.angelelli@gmail.com>
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtLocation module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVECIRCLEMAPITEM_P_P_H
+#define QDECLARATIVECIRCLEMAPITEM_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtLocation/private/qlocationglobal_p.h>
+#include <QtLocation/private/qdeclarativepolygonmapitem_p_p.h>
+#include <QtLocation/private/qdeclarativecirclemapitem_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_LOCATION_PRIVATE_EXPORT QGeoMapCircleGeometry : public QGeoMapPolygonGeometry
+{
+public:
+ QGeoMapCircleGeometry();
+
+ void updateScreenPointsInvert(const QList<QDoubleVector2D> &circlePath, const QGeoMap &map);
+};
+
+class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCircleMapItemPrivate
+{
+public:
+ static const int CircleSamples = 128; // ToDo: make this radius && ZL dependent?
+
+ QDeclarativeCircleMapItemPrivate(QDeclarativeCircleMapItem &circle) : m_circle(circle)
+ {
+
+ }
+ QDeclarativeCircleMapItemPrivate(QDeclarativeCircleMapItemPrivate &other) : m_circle(other.m_circle)
+ {
+ }
+
+ virtual ~QDeclarativeCircleMapItemPrivate();
+ virtual void onLinePropertiesChanged() = 0;
+ virtual void markSourceDirtyAndUpdate() = 0;
+ virtual void onMapSet() = 0;
+ virtual void onGeoGeometryChanged() = 0;
+ virtual void onItemGeometryChanged() = 0;
+ virtual void updatePolish() = 0;
+ virtual void afterViewportChanged() = 0;
+ virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) = 0;
+ virtual bool contains(const QPointF &point) const = 0;
+
+ void updateCirclePath()
+ {
+ if (!m_circle.map() || m_circle.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
+ return;
+
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_circle.map()->geoProjection());
+ QList<QGeoCoordinate> path;
+ calculatePeripheralPoints(path, m_circle.center(), m_circle.radius(), CircleSamples, m_leftBound);
+ m_circlePath.clear();
+ for (const QGeoCoordinate &c : path)
+ m_circlePath << p.geoToMapProjection(c);
+ }
+
+ static bool crossEarthPole(const QGeoCoordinate &center, qreal distance);
+
+ static bool preserveCircleGeometry(QList<QDoubleVector2D> &path, const QGeoCoordinate &center,
+ qreal distance, const QGeoProjectionWebMercator &p);
+ static void updateCirclePathForRendering(QList<QDoubleVector2D> &path, const QGeoCoordinate &center,
+ qreal distance, const QGeoProjectionWebMercator &p);
+
+ static void calculatePeripheralPoints(QList<QGeoCoordinate> &path, const QGeoCoordinate &center,
+ qreal distance, int steps, QGeoCoordinate &leftBound);
+
+ QDeclarativeCircleMapItem &m_circle;
+ QList<QDoubleVector2D> m_circlePath;
+ QGeoCoordinate m_leftBound;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCircleMapItemPrivateCPU: public QDeclarativeCircleMapItemPrivate
+{
+public:
+
+ QDeclarativeCircleMapItemPrivateCPU(QDeclarativeCircleMapItem &circle) : QDeclarativeCircleMapItemPrivate(circle)
+ {
+ }
+
+ QDeclarativeCircleMapItemPrivateCPU(QDeclarativeCircleMapItemPrivate &other)
+ : QDeclarativeCircleMapItemPrivate(other)
+ {
+ }
+
+ ~QDeclarativeCircleMapItemPrivateCPU() override;
+
+ void onLinePropertiesChanged() override
+ {
+ // mark dirty just in case we're a width change
+ markSourceDirtyAndUpdate();
+ }
+ void markSourceDirtyAndUpdate() override
+ {
+ // preserveGeometry is cleared in updateMapItemPaintNode
+ m_geometry.markSourceDirty();
+ m_borderGeometry.markSourceDirty();
+ m_circle.polishAndUpdate();
+ }
+ void onMapSet() override
+ {
+ updateCirclePath();
+ markSourceDirtyAndUpdate();
+ }
+ void onGeoGeometryChanged() override
+ {
+ updateCirclePath();
+ markSourceDirtyAndUpdate();
+ }
+ void onItemGeometryChanged() override
+ {
+ onGeoGeometryChanged();
+ }
+ void afterViewportChanged() override
+ {
+ markSourceDirtyAndUpdate();
+ }
+ void updatePolish() override
+ {
+ if (!m_circle.m_circle.isValid()) {
+ m_geometry.clear();
+ m_borderGeometry.clear();
+ m_circle.setWidth(0);
+ m_circle.setHeight(0);
+ return;
+ }
+
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_circle.map()->geoProjection());
+ QScopedValueRollback<bool> rollback(m_circle.m_updatingGeometry);
+ m_circle.m_updatingGeometry = true;
+
+ QList<QDoubleVector2D> circlePath = m_circlePath;
+
+ int pathCount = circlePath.size();
+ bool preserve = preserveCircleGeometry(circlePath, m_circle.m_circle.center(), m_circle.m_circle.radius(), p);
+ // using leftBound_ instead of the analytically calculated circle_.boundingGeoRectangle().topLeft());
+ // to fix QTBUG-62154
+ m_geometry.setPreserveGeometry(true, m_leftBound); // to set the geoLeftBound_
+ m_geometry.setPreserveGeometry(preserve, m_leftBound);
+
+ bool invertedCircle = false;
+ if (crossEarthPole(m_circle.m_circle.center(), m_circle.m_circle.radius()) && circlePath.size() == pathCount) {
+ m_geometry.updateScreenPointsInvert(circlePath, *m_circle.map()); // invert fill area for really huge circles
+ invertedCircle = true;
+ } else {
+ m_geometry.updateSourcePoints(*m_circle.map(), circlePath);
+ m_geometry.updateScreenPoints(*m_circle.map(), m_circle.m_border.width());
+ }
+
+ m_borderGeometry.clear();
+ QList<QGeoMapItemGeometry *> geoms;
+ geoms << &m_geometry;
+
+ if (m_circle.m_border.color() != Qt::transparent && m_circle.m_border.width() > 0) {
+ QList<QDoubleVector2D> closedPath = circlePath;
+ closedPath << closedPath.first();
+
+ if (invertedCircle) {
+ closedPath = m_circlePath;
+ closedPath << closedPath.first();
+ std::reverse(closedPath.begin(), closedPath.end());
+ }
+
+ m_borderGeometry.setPreserveGeometry(true, m_leftBound);
+ m_borderGeometry.setPreserveGeometry(preserve, m_leftBound);
+
+ // Use srcOrigin_ from fill geometry after clipping to ensure that translateToCommonOrigin won't fail.
+ const QGeoCoordinate &geometryOrigin = m_geometry.origin();
+
+ m_borderGeometry.srcPoints_.clear();
+ m_borderGeometry.srcPointTypes_.clear();
+
+ QDoubleVector2D borderLeftBoundWrapped;
+ QList<QList<QDoubleVector2D > > clippedPaths = m_borderGeometry.clipPath(*m_circle.map(), closedPath, borderLeftBoundWrapped);
+ if (clippedPaths.size()) {
+ borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin);
+ m_borderGeometry.pathToScreen(*m_circle.map(), clippedPaths, borderLeftBoundWrapped);
+ m_borderGeometry.updateScreenPoints(*m_circle.map(), m_circle.m_border.width());
+ geoms << &m_borderGeometry;
+ } else {
+ m_borderGeometry.clear();
+ }
+ }
+
+ QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms);
+
+ if (invertedCircle || !preserve) {
+ m_circle.setWidth(combined.width());
+ m_circle.setHeight(combined.height());
+ } else {
+ m_circle.setWidth(combined.width() + 2 * m_circle.m_border.width()); // ToDo: Fix this!
+ m_circle.setHeight(combined.height() + 2 * m_circle.m_border.width());
+ }
+
+ // No offsetting here, even in normal case, because first point offset is already translated
+ m_circle.setPositionOnMap(m_geometry.origin(), m_geometry.firstPointOffset());
+ }
+
+ QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override
+ {
+ Q_UNUSED(data);
+ if (!m_node || !oldNode) { // Apparently the QSG might delete the nodes if they become invisible
+ m_node = new MapPolygonNode();
+ if (oldNode) {
+ delete oldNode;
+ oldNode = nullptr;
+ }
+ } else {
+ m_node = static_cast<MapPolygonNode *>(oldNode);
+ }
+
+ //TODO: update only material
+ if (m_geometry.isScreenDirty() || m_borderGeometry.isScreenDirty() || m_circle.m_dirtyMaterial) {
+ m_node->update(m_circle.m_color, m_circle.m_border.color(), &m_geometry, &m_borderGeometry);
+ m_geometry.setPreserveGeometry(false);
+ m_borderGeometry.setPreserveGeometry(false);
+ m_geometry.markClean();
+ m_borderGeometry.markClean();
+ m_circle.m_dirtyMaterial = false;
+ }
+ return m_node;
+ }
+ bool contains(const QPointF &point) const override
+ {
+ return (m_geometry.contains(point) || m_borderGeometry.contains(point));
+ }
+
+ QGeoMapCircleGeometry m_geometry;
+ QGeoMapPolylineGeometry m_borderGeometry;
+ MapPolygonNode *m_node = nullptr;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCircleMapItemPrivateOpenGL: public QDeclarativeCircleMapItemPrivate
+{
+public:
+ QDeclarativeCircleMapItemPrivateOpenGL(QDeclarativeCircleMapItem &circle) : QDeclarativeCircleMapItemPrivate(circle)
+ {
+ }
+
+ QDeclarativeCircleMapItemPrivateOpenGL(QDeclarativeCircleMapItemPrivate &other)
+ : QDeclarativeCircleMapItemPrivate(other)
+ {
+ }
+
+ ~QDeclarativeCircleMapItemPrivateOpenGL() override;
+
+ void onLinePropertiesChanged() override
+ {
+ m_circle.m_dirtyMaterial = true;
+ afterViewportChanged();
+ }
+ void markScreenDirtyAndUpdate()
+ {
+ // preserveGeometry is cleared in updateMapItemPaintNode
+ m_geometry.markScreenDirty();
+ m_borderGeometry.markScreenDirty();
+ m_circle.polishAndUpdate();
+ }
+ virtual void markSourceDirtyAndUpdate() override
+ {
+ updateCirclePath();
+ preserveGeometry();
+ m_geometry.markSourceDirty();
+ m_borderGeometry.markSourceDirty();
+ m_circle.polishAndUpdate();
+ }
+ void preserveGeometry()
+ {
+ m_geometry.setPreserveGeometry(true, m_leftBound);
+ m_borderGeometry.setPreserveGeometry(true, m_leftBound);
+ }
+ virtual void onMapSet() override
+ {
+ markSourceDirtyAndUpdate();
+ }
+ virtual void onGeoGeometryChanged() override
+ {
+
+ markSourceDirtyAndUpdate();
+ }
+ virtual void onItemGeometryChanged() override
+ {
+ onGeoGeometryChanged();
+ }
+ virtual void afterViewportChanged() override
+ {
+ preserveGeometry();
+ markScreenDirtyAndUpdate();
+ }
+ virtual void updatePolish() override
+ {
+ if (m_circle.m_circle.isEmpty()) {
+ m_geometry.clear();
+ m_borderGeometry.clear();
+ m_circle.setWidth(0);
+ m_circle.setHeight(0);
+ return;
+ }
+
+ QScopedValueRollback<bool> rollback(m_circle.m_updatingGeometry);
+ m_circle.m_updatingGeometry = true;
+ const qreal lineWidth = m_circle.m_border.width();
+ const QColor &lineColor = m_circle.m_border.color();
+ const QColor &fillColor = m_circle.color();
+ if (fillColor.alpha() != 0) {
+ m_geometry.updateSourcePoints(*m_circle.map(), m_circlePath);
+ m_geometry.markScreenDirty();
+ m_geometry.updateScreenPoints(*m_circle.map(), lineWidth, lineColor);
+ } else {
+ m_geometry.clearBounds();
+ }
+
+ QGeoMapItemGeometry * geom = &m_geometry;
+ m_borderGeometry.clearScreen();
+ if (lineColor.alpha() != 0 && lineWidth > 0) {
+ m_borderGeometry.updateSourcePoints(*m_circle.map(), m_circle.m_circle);
+ m_borderGeometry.markScreenDirty();
+ m_borderGeometry.updateScreenPoints(*m_circle.map(), lineWidth);
+ geom = &m_borderGeometry;
+ }
+ m_circle.setWidth(geom->sourceBoundingBox().width());
+ m_circle.setHeight(geom->sourceBoundingBox().height());
+ m_circle.setPosition(1.0 * geom->firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5));
+ }
+
+ virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override
+ {
+ Q_UNUSED(data);
+
+ if (!m_rootNode || !oldNode) {
+ m_rootNode = new QDeclarativePolygonMapItemPrivateOpenGL::RootNode();
+ m_node = new MapPolygonNodeGL();
+ m_rootNode->appendChildNode(m_node);
+ m_polylinenode = new MapPolylineNodeOpenGLExtruded();
+ m_rootNode->appendChildNode(m_polylinenode);
+ m_rootNode->markDirty(QSGNode::DirtyNodeAdded);
+ if (oldNode)
+ delete oldNode;
+ } else {
+ m_rootNode = static_cast<QDeclarativePolygonMapItemPrivateOpenGL::RootNode *>(oldNode);
+ }
+
+ const QGeoMap *map = m_circle.map();
+ const QMatrix4x4 &combinedMatrix = map->geoProjection().qsgTransform();
+ const QDoubleVector3D &cameraCenter = map->geoProjection().centerMercator();
+
+ if (m_borderGeometry.isScreenDirty()) {
+ /* Do the border update first */
+ m_polylinenode->update(m_circle.m_border.color(),
+ float(m_circle.m_border.width()),
+ &m_borderGeometry,
+ combinedMatrix,
+ cameraCenter,
+ Qt::SquareCap,
+ true);
+ m_borderGeometry.setPreserveGeometry(false);
+ m_borderGeometry.markClean();
+ } else {
+ m_polylinenode->setSubtreeBlocked(true);
+ }
+ if (m_geometry.isScreenDirty()) {
+ m_node->update(m_circle.m_color,
+ &m_geometry,
+ combinedMatrix,
+ cameraCenter);
+ m_geometry.setPreserveGeometry(false);
+ m_geometry.markClean();
+ } else {
+ m_node->setSubtreeBlocked(true);
+ }
+
+ m_rootNode->setSubtreeBlocked(false);
+ return m_rootNode;
+ }
+ virtual bool contains(const QPointF &point) const override
+ {
+ const qreal lineWidth = m_circle.m_border.width();
+ const QColor &lineColor = m_circle.m_border.color();
+ const QRectF &bounds = (lineColor.alpha() != 0 && lineWidth > 0) ? m_borderGeometry.sourceBoundingBox() : m_geometry.sourceBoundingBox();
+ if (bounds.contains(point)) {
+ QDeclarativeGeoMap *m = m_circle.quickMap();
+ if (m) {
+ const QGeoCoordinate crd = m->toCoordinate(m->mapFromItem(&m_circle, point));
+ return m_circle.m_circle.contains(crd) || m_borderGeometry.contains(m_circle.mapToItem(m_circle.quickMap(), point),
+ m_circle.border()->width(),
+ static_cast<const QGeoProjectionWebMercator&>(m_circle.map()->geoProjection()));
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ QGeoMapPolygonGeometryOpenGL m_geometry;
+ QGeoMapPolylineGeometryOpenGL m_borderGeometry;
+ QDeclarativePolygonMapItemPrivateOpenGL::RootNode *m_rootNode = nullptr;
+ MapPolygonNodeGL *m_node = nullptr;
+ MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QDECLARATIVECIRCLEMAPITEM_P_P_H
diff --git a/src/location/declarativemaps/qdeclarativegeomap.cpp b/src/location/declarativemaps/qdeclarativegeomap.cpp
index 6a2d82e8..fc282829 100644
--- a/src/location/declarativemaps/qdeclarativegeomap.cpp
+++ b/src/location/declarativemaps/qdeclarativegeomap.cpp
@@ -198,7 +198,7 @@ QDeclarativeGeoMap::QDeclarativeGeoMap(QQuickItem *parent)
setAcceptHoverEvents(false);
setAcceptedMouseButtons(Qt::LeftButton);
setFlags(QQuickItem::ItemHasContents | QQuickItem::ItemClipsChildrenToShape);
- setFiltersChildMouseEvents(true);
+ setFiltersChildMouseEvents(true); // needed for childMouseEventFilter to work.
m_activeMapType = new QDeclarativeGeoMapType(QGeoMapType(QGeoMapType::NoMap,
tr("No Map"),
@@ -707,7 +707,8 @@ void QDeclarativeGeoMap::mappingManagerInitialized()
emit m_map->copyrightsChanged(copyrightImage);
- connect(m_map.data(), &QGeoMap::sgNodeChanged, this, &QQuickItem::update);
+ connect(window(), &QQuickWindow::beforeSynchronizing, this, &QDeclarativeGeoMap::updateItemToWindowTransform, Qt::DirectConnection);
+ connect(m_map.data(), &QGeoMap::sgNodeChanged, this, &QDeclarativeGeoMap::onSGNodeChanged);
connect(m_map.data(), &QGeoMap::cameraCapabilitiesChanged, this, &QDeclarativeGeoMap::onCameraCapabilitiesChanged);
// This prefetches a buffer around the map
@@ -2175,6 +2176,38 @@ bool QDeclarativeGeoMap::removeMapItemView_real(QDeclarativeGeoMapItemView *item
return removeMapItemGroup_real(itemView); // at this point, all delegate instances have been removed.
}
+void QDeclarativeGeoMap::updateItemToWindowTransform()
+{
+ if (!m_initialized)
+ return;
+
+ // Update itemToWindowTransform into QGeoProjection
+ const QTransform item2WindowOld = m_map->geoProjection().itemToWindowTransform();
+ QTransform item2Window = QQuickItemPrivate::get(this)->itemToWindowTransform();
+ if (!property("layer").isNull() && property("layer").value<QObject *>()->property("enabled").toBool())
+ item2Window.reset(); // When layer is enabled, the item is rendered offscreen with no transformation, then the layer is applied
+
+ m_map->setItemToWindowTransform(item2Window);
+
+ // This method is called at every redraw, including those redraws not generated by
+ // sgNodeChanged.
+ // In these cases, *if* the item2windowTransform has changed (e.g., if transformation of
+ // the item or one of its ancestors changed), a forced update of the map items using accelerated
+ // GL implementation has to be performed in order to have them pulling the updated itemToWindowTransform.
+ if (!m_sgNodeHasChanged && item2WindowOld != item2Window) {
+ for (auto i: qAsConst(m_mapItems))
+ i->setMaterialDirty();
+ }
+
+ m_sgNodeHasChanged = false;
+}
+
+void QDeclarativeGeoMap::onSGNodeChanged()
+{
+ m_sgNodeHasChanged = true;
+ update();
+}
+
/*!
\qmlmethod void QtLocation::Map::addMapItemView(MapItemView itemView)
diff --git a/src/location/declarativemaps/qdeclarativegeomap_p.h b/src/location/declarativemaps/qdeclarativegeomap_p.h
index ee9f8ec2..7dcb60aa 100644
--- a/src/location/declarativemaps/qdeclarativegeomap_p.h
+++ b/src/location/declarativemaps/qdeclarativegeomap_p.h
@@ -267,6 +267,8 @@ protected:
bool removeMapItemGroup_real(QDeclarativeGeoMapItemGroup *itemGroup);
bool addMapItemView_real(QDeclarativeGeoMapItemView *itemView);
bool removeMapItemView_real(QDeclarativeGeoMapItemView *itemView);
+ void updateItemToWindowTransform();
+ void onSGNodeChanged();
private Q_SLOTS:
void mappingManagerInitialized();
@@ -308,6 +310,7 @@ private:
double m_maximumViewportLatitude;
double m_minimumViewportLatitude = 0.0;
bool m_initialized;
+ bool m_sgNodeHasChanged = false;
QList<QDeclarativeGeoMapParameter *> m_mapParameters;
QList<QGeoMapObject*> m_pendingMapObjects; // Used only in the initialization phase
QGeoCameraCapabilities m_cameraCapabilities;
diff --git a/src/location/declarativemaps/qdeclarativegeomapitembase.cpp b/src/location/declarativemaps/qdeclarativegeomapitembase.cpp
index 23993faf..d1a34f1e 100644
--- a/src/location/declarativemaps/qdeclarativegeomapitembase.cpp
+++ b/src/location/declarativemaps/qdeclarativegeomapitembase.cpp
@@ -323,6 +323,8 @@ bool QDeclarativeGeoMapItemBase::isPolishScheduled() const
return QQuickItemPrivate::get(this)->polishScheduled;
}
+void QDeclarativeGeoMapItemBase::setMaterialDirty() {}
+
void QDeclarativeGeoMapItemBase::polishAndUpdate()
{
polish();
diff --git a/src/location/declarativemaps/qdeclarativegeomapitembase_p.h b/src/location/declarativemaps/qdeclarativegeomapitembase_p.h
index 38a118e5..65f111f6 100644
--- a/src/location/declarativemaps/qdeclarativegeomapitembase_p.h
+++ b/src/location/declarativemaps/qdeclarativegeomapitembase_p.h
@@ -92,8 +92,8 @@ public:
virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map);
virtual void setPositionOnMap(const QGeoCoordinate &coordinate, const QPointF &offset);
- QDeclarativeGeoMap *quickMap() { return quickMap_; }
- QGeoMap *map() { return map_; }
+ QDeclarativeGeoMap *quickMap() const { return quickMap_; }
+ QGeoMap *map() const { return map_; }
virtual const QGeoShape &geoShape() const = 0;
virtual void setGeoShape(const QGeoShape &shape) = 0;
@@ -108,6 +108,23 @@ public:
void setParentGroup(QDeclarativeGeoMapItemGroup &parentGroup);
+ template <typename T = QObject>
+
+ QList<T*> quickChildren() const
+ {
+ QList<T*> res;
+ QObjectList kids = children();
+ QList<QQuickItem *> quickKids = childItems();
+ for (int i = 0; i < quickKids.count(); ++i)
+ kids.append(quickKids.at(i));
+ for (auto kid : qAsConst(kids)) {
+ auto val = qobject_cast<T*>(kid);
+ if (val)
+ res.push_back(val);
+ }
+ return res;
+ }
+
Q_SIGNALS:
void mapItemOpacityChanged();
Q_REVISION(12) void addTransitionFinished();
@@ -122,6 +139,7 @@ protected:
float zoomLevelOpacity() const;
bool childMouseEventFilter(QQuickItem *item, QEvent *event);
bool isPolishScheduled() const;
+ virtual void setMaterialDirty();
QGeoMap::ItemType m_itemType = QGeoMap::NoItem;
diff --git a/src/location/declarativemaps/qdeclarativegeomapitemutils.cpp b/src/location/declarativemaps/qdeclarativegeomapitemutils.cpp
new file mode 100644
index 00000000..e2774559
--- /dev/null
+++ b/src/location/declarativemaps/qdeclarativegeomapitemutils.cpp
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Paolo Angelelli <paolo.angelelli@gmail.com>
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtLocation module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdeclarativegeomapitemutils_p.h"
+#include <QtPositioning/private/qdoublevector3d_p.h>
+#include <QtPositioning/private/qdoublematrix4x4_p.h>
+#include <QtPositioning/QGeoCoordinate>
+#include <QPointF>
+#include <QMatrix4x4>
+#include <QPainterPath>
+#include <QPainterPathStroker>
+#include <QtGui/private/qvectorpath_p.h>
+#include <QtGui/private/qtriangulatingstroker_p.h>
+#include <QtGui/private/qtriangulator_p.h>
+#include <QtPositioning/private/qclipperutils_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void QDeclarativeGeoMapItemUtils::wrapPath(const QList<QGeoCoordinate> &perimeter,
+ const QGeoCoordinate &geoLeftBound,
+ const QGeoProjectionWebMercator &p,
+ QList<QDoubleVector2D> &wrappedPath,
+ QList<QDoubleVector2D> &wrappedPathMinus1,
+ QList<QDoubleVector2D> &wrappedPathPlus1,
+ QDoubleVector2D *leftBoundWrapped)
+{
+ QList<QDoubleVector2D> path;
+ for (const QGeoCoordinate &c : perimeter)
+ path << p.geoToMapProjection(c);
+ const QDoubleVector2D leftBound = p.geoToMapProjection(geoLeftBound);
+ wrappedPath.clear();
+ wrappedPathPlus1.clear();
+ wrappedPathMinus1.clear();
+ // compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0
+ for (int i = 0; i < path.size(); ++i) {
+ QDoubleVector2D coord = path.at(i);
+
+ // We can get NaN if the map isn't set up correctly, or the projection
+ // is faulty -- probably best thing to do is abort
+ if (!qIsFinite(coord.x()) || !qIsFinite(coord.y()))
+ return;
+
+ const bool isPointLessThanUnwrapBelowX = (coord.x() < leftBound.x());
+ // unwrap x to preserve geometry if moved to border of map
+ if (isPointLessThanUnwrapBelowX)
+ coord.setX(coord.x() + 1.0);
+
+ QDoubleVector2D coordP1(coord.x() + 1.0, coord.y());
+ QDoubleVector2D coordM1(coord.x() - 1.0, coord.y());
+
+ wrappedPath.append(coord);
+ wrappedPathPlus1.append(coordP1);
+ wrappedPathMinus1.append(coordM1);
+ }
+ if (leftBoundWrapped)
+ *leftBoundWrapped = leftBound;
+}
+
+void QDeclarativeGeoMapItemUtils::wrapPath(const QList<QGeoCoordinate> &perimeter,
+ const QGeoCoordinate &geoLeftBound,
+ const QGeoProjectionWebMercator &p,
+ QList<QDoubleVector2D> &wrappedPath,
+ QDoubleVector2D *leftBoundWrapped)
+{
+ QList<QDoubleVector2D> path;
+ for (const QGeoCoordinate &c : perimeter)
+ path << p.geoToMapProjection(c);
+ const QDoubleVector2D leftBound = p.geoToMapProjection(geoLeftBound);
+ wrapPath(path, leftBound,wrappedPath);
+ if (leftBoundWrapped)
+ *leftBoundWrapped = leftBound;
+}
+
+void QDeclarativeGeoMapItemUtils::wrapPath(const QList<QDoubleVector2D> &path,
+ const QDoubleVector2D &geoLeftBound,
+ QList<QDoubleVector2D> &wrappedPath)
+{
+ wrappedPath.clear();
+ // compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0
+ for (int i = 0; i < path.size(); ++i) {
+ QDoubleVector2D coord = path.at(i);
+
+ // We can get NaN if the map isn't set up correctly, or the projection
+ // is faulty -- probably best thing to do is abort
+ if (!qIsFinite(coord.x()) || !qIsFinite(coord.y()))
+ return;
+
+ const bool isPointLessThanUnwrapBelowX = (coord.x() < geoLeftBound.x());
+ // unwrap x to preserve geometry if moved to border of map
+ if (isPointLessThanUnwrapBelowX)
+ coord.setX(coord.x() + 1.0);
+
+ wrappedPath.append(coord);
+ }
+}
+
+void QDeclarativeGeoMapItemUtils::clipPolygon(const QList<QDoubleVector2D> &wrappedPath, const QGeoProjectionWebMercator &p, QList<QList<QDoubleVector2D> > &clippedPaths, QDoubleVector2D *leftBoundWrapped, const bool closed)
+{
+ // 2) Clip bounding box
+ clippedPaths.clear();
+ const QList<QDoubleVector2D> &visibleRegion = p.projectableGeometry();
+ if (visibleRegion.size()) {
+ c2t::clip2tri clipper;
+ clipper.addSubjectPath(QClipperUtils::qListToPath(wrappedPath), closed);
+ clipper.addClipPolygon(QClipperUtils::qListToPath(visibleRegion));
+ Paths res = clipper.execute(c2t::clip2tri::Intersection, QtClipperLib::pftEvenOdd, QtClipperLib::pftEvenOdd);
+ clippedPaths = QClipperUtils::pathsToQList(res);
+
+ if (leftBoundWrapped) {
+ // 2.1) update srcOrigin_ and leftBoundWrapped with the point with minimum X
+ QDoubleVector2D lb(qInf(), qInf());
+ for (const QList<QDoubleVector2D> &path: clippedPaths)
+ for (const QDoubleVector2D &p: path)
+ if (p.x() < lb.x() || (p.x() == lb.x() && p.y() < lb.y()))
+ // y-minimization needed to find the same point on polygon and border
+ lb = p;
+
+ if (qIsInf(lb.x())) // e.g., when the polygon is clipped entirely
+ return;
+
+ // 2.2) Prevent the conversion to and from clipper from introducing tiny negative offsets which,
+ // in turn will make the geometry wrap around.
+ lb.setX(qMax(leftBoundWrapped->x(), lb.x()));
+
+ *leftBoundWrapped = lb;
+ // srcOrigin_ = p.mapProjectionToGeo(p.unwrapMapProjection(lb));
+ }
+ } else {
+ clippedPaths.append(wrappedPath);
+ }
+}
+
+void QDeclarativeGeoMapItemUtils::projectBbox(const QList<QDoubleVector2D> &clippedBbox, const QGeoProjectionWebMercator &p, QPainterPath &projectedBbox)
+{
+ projectedBbox = QPainterPath(); // clear() is added in 5.13..
+ for (int i = 0; i < clippedBbox.size(); ++i) {
+ QDoubleVector2D point = p.wrappedMapProjectionToItemPosition(clippedBbox.at(i));
+ if (i == 0)
+ projectedBbox.moveTo(point.toPointF());
+ else
+ projectedBbox.lineTo(point.toPointF());
+ }
+ projectedBbox.closeSubpath();
+}
+
+QT_END_NAMESPACE
diff --git a/src/location/declarativemaps/qdeclarativegeomapitemutils_p.h b/src/location/declarativemaps/qdeclarativegeomapitemutils_p.h
new file mode 100644
index 00000000..48ece6b2
--- /dev/null
+++ b/src/location/declarativemaps/qdeclarativegeomapitemutils_p.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Paolo Angelelli <paolo.angelelli@gmail.com>
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtLocation module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVEGEOMAPITEMUTILS_P_H
+#define QDECLARATIVEGEOMAPITEMUTILS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtLocation/private/qlocationglobal_p.h>
+#include <QtLocation/private/qgeoprojection_p.h>
+#include <QtPositioning/private/qdoublevector2d_p.h>
+
+
+QT_BEGIN_NAMESPACE
+
+class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapItemUtils
+{
+public:
+ struct vec2 {
+ float x;
+ float y;
+ vec2(const QDoubleVector2D &p)
+ {
+ x = float(p.x());
+ y = float(p.y());
+ }
+ vec2() = default;
+ vec2 &operator=(const QPointF &p)
+ {
+ x = float(p.x());
+ y = float(p.y());
+ return *this;
+ }
+ vec2 &operator=(const QDoubleVector2D &p)
+ {
+ x = float(p.x());
+ y = float(p.y());
+ return *this;
+ }
+ QDoubleVector2D toDoubleVector2D() const
+ {
+ return QDoubleVector2D(double(x), double(y));
+ }
+ };
+
+ static void wrapPath(const QList<QGeoCoordinate> &perimeter
+ ,const QGeoCoordinate &geoLeftBound
+ ,const QGeoProjectionWebMercator &p
+ ,QList<QDoubleVector2D> &wrappedPath
+ ,QList<QDoubleVector2D> &wrappedPathMinus1
+ ,QList<QDoubleVector2D> &wrappedPathPlus1
+ ,QDoubleVector2D *leftBoundWrapped = nullptr);
+
+ static void wrapPath(const QList<QGeoCoordinate> &perimeter
+ ,const QGeoCoordinate &geoLeftBound
+ ,const QGeoProjectionWebMercator &p
+ ,QList<QDoubleVector2D> &wrappedPath
+ ,QDoubleVector2D *leftBoundWrapped = nullptr);
+
+ static void wrapPath(const QList<QDoubleVector2D> &path
+ , const QDoubleVector2D &geoLeftBound
+ , QList<QDoubleVector2D> &wrappedPath);
+
+
+ static void clipPolygon(const QList<QDoubleVector2D> &wrappedPath
+ ,const QGeoProjectionWebMercator &p
+ ,QList<QList<QDoubleVector2D> > &clippedPaths
+ ,QDoubleVector2D *leftBoundWrapped = nullptr
+ ,const bool closed = true);
+
+ static void projectBbox(const QList<QDoubleVector2D> &clippedBbox
+ ,const QGeoProjectionWebMercator &p
+ ,QPainterPath &projectedBbox);
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QDECLARATIVEGEOMAPITEMUTILS_P_H
diff --git a/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp b/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp
index 86ff04af..fa6ee174 100644
--- a/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp
+++ b/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp
@@ -34,7 +34,11 @@
**
****************************************************************************/
+#include "qdeclarativegeomapitemutils_p.h"
#include "qdeclarativepolygonmapitem_p.h"
+#include "qdeclarativepolylinemapitem_p_p.h"
+#include "qdeclarativepolygonmapitem_p_p.h"
+#include "qdeclarativerectanglemapitem_p_p.h"
#include "qlocationutils_p.h"
#include "error_messages_p.h"
#include "locationvaluetypehelper_p.h"
@@ -51,6 +55,10 @@
#include <QtPositioning/private/qdoublevector2d_p.h>
#include <QtPositioning/private/qclipperutils_p.h>
#include <QtPositioning/private/qgeopolygon_p.h>
+#include <QtPositioning/private/qwebmercator_p.h>
+#include <QtQuick/private/qsgmaterialshader_p.h>
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/qsgnode.h>
/* poly2tri triangulator includes */
#include <clip2tri.h>
@@ -326,17 +334,307 @@ void QGeoMapPolygonGeometry::updateScreenPoints(const QGeoMap &map, qreal stroke
this->translate(QPointF(strokeWidth, strokeWidth));
}
+QGeoMapPolygonGeometryOpenGL::QGeoMapPolygonGeometryOpenGL(){
+}
+
+void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QList<QDoubleVector2D> &path)
+{
+ QList<QGeoCoordinate> geopath;
+ for (const auto &c: path)
+ geopath.append(QWebMercator::mercatorToCoord(c));
+ updateSourcePoints(map, geopath);
+}
+
+// wrapPath always preserves the geometry
+// This one handles holes
+static void wrapPath(const QGeoPolygon &poly
+ ,const QGeoCoordinate &geoLeftBound
+ ,const QGeoProjectionWebMercator &p
+ ,QList<QList<QDoubleVector2D> > &wrappedPaths
+ ,QDoubleVector2D *leftBoundWrapped = nullptr)
+{
+ QList<QList<QDoubleVector2D> > paths;
+ for (int i = 0; i < 1+poly.holesCount(); ++i) {
+ QList<QDoubleVector2D> path;
+ if (!i) {
+ for (const QGeoCoordinate &c : poly.path())
+ path << p.geoToMapProjection(c);
+ } else {
+ for (const QGeoCoordinate &c : poly.holePath(i-1))
+ path << p.geoToMapProjection(c);
+ }
+ paths.append(path);
+ }
+
+ const QDoubleVector2D leftBound = p.geoToMapProjection(geoLeftBound);
+ wrappedPaths.clear();
+
+ QList<QDoubleVector2D> wrappedPath;
+ // compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0
+ for (int j = 0; j < paths.size(); ++j) {
+ const QList<QDoubleVector2D> &path = paths.at(j);
+ wrappedPath.clear();
+ for (int i = 0; i < path.size(); ++i) {
+ QDoubleVector2D coord = path.at(i);
+
+ // We can get NaN if the map isn't set up correctly, or the projection
+ // is faulty -- probably best thing to do is abort
+ if (!qIsFinite(coord.x()) || !qIsFinite(coord.y())) {
+ wrappedPaths.clear();
+ return;
+ }
+
+ const bool isPointLessThanUnwrapBelowX = (coord.x() < leftBound.x());
+ // unwrap x to preserve geometry if moved to border of map
+ if (isPointLessThanUnwrapBelowX)
+ coord.setX(coord.x() + 1.0);
+ wrappedPath.append(coord);
+ }
+ wrappedPaths.append(wrappedPath);
+ }
+
+ if (leftBoundWrapped)
+ *leftBoundWrapped = leftBound;
+}
+
+static void cutPathEars(const QList<QList<QDoubleVector2D>> &wrappedPaths,
+ QVector<QDeclarativeGeoMapItemUtils::vec2> &screenVertices,
+ QVector<quint32> &screenIndices)
+{
+ using Coord = double;
+ using N = uint32_t;
+ using Point = std::array<Coord, 2>;
+ screenVertices.clear();
+ screenIndices.clear();
+
+ std::vector<std::vector<Point>> polygon;
+ std::vector<Point> poly;
+
+ for (const QList<QDoubleVector2D> &wrappedPath: wrappedPaths) {
+ poly.clear();
+ for (const QDoubleVector2D &v: wrappedPath) {
+ screenVertices << v;
+ Point pt = {{ v.x(), v.y() }};
+ poly.push_back( pt );
+ }
+ polygon.push_back(poly);
+ }
+
+ std::vector<N> indices = qt_mapbox::earcut<N>(polygon);
+
+ for (const auto &i: indices)
+ screenIndices << quint32(i);
+}
+
+static void cutPathEars(const QList<QDoubleVector2D> &wrappedPath,
+ QVector<QDeclarativeGeoMapItemUtils::vec2> &screenVertices,
+ QVector<quint32> &screenIndices)
+{
+ using Coord = double;
+ using N = uint32_t;
+ using Point = std::array<Coord, 2>;
+ screenVertices.clear();
+ screenIndices.clear();
+
+ std::vector<std::vector<Point>> polygon;
+ std::vector<Point> poly;
+
+ for (const QDoubleVector2D &v: wrappedPath) {
+ screenVertices << v;
+ Point pt = {{ v.x(), v.y() }};
+ poly.push_back( pt );
+ }
+ polygon.push_back(poly);
+
+ std::vector<N> indices = qt_mapbox::earcut<N>(polygon);
+
+ for (const auto &i: indices)
+ screenIndices << quint32(i);
+}
+
+/*!
+ \internal
+*/
+// This one does only a perimeter
+void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map,
+ const QList<QGeoCoordinate> &perimeter)
+{
+ if (!sourceDirty_)
+ return;
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection());
+
+ // build the actual path
+ // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints
+ srcOrigin_ = geoLeftBound_;
+
+ QDoubleVector2D leftBoundWrapped;
+ // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0
+ QList<QDoubleVector2D> wrappedPath;
+ QDeclarativeGeoMapItemUtils::wrapPath(perimeter, geoLeftBound_, p,
+ wrappedPath, &leftBoundWrapped);
+
+ // 1.1) do the same for the bbox
+ QList<QDoubleVector2D> wrappedBbox, wrappedBboxPlus1, wrappedBboxMinus1;
+ QGeoPolygon bbox(QGeoPath(perimeter).boundingGeoRectangle());
+ QDeclarativeGeoMapItemUtils::wrapPath(bbox.path(), bbox.boundingGeoRectangle().topLeft(), p,
+ wrappedBbox, wrappedBboxMinus1, wrappedBboxPlus1, &m_bboxLeftBoundWrapped);
+
+ // 2) Store the triangulated polygon, and the wrapped bbox paths.
+ // the triangulations can be used as they are, as they "bypass" the QtQuick display chain
+ // the bbox wraps have to be however clipped, and then projected, in order to figure out the geometry.
+ // Note that this might still cause the geometryChanged method to fail under some extreme conditions.
+ cutPathEars(wrappedPath, m_screenVertices, m_screenIndices);
+
+ m_wrappedPolygons.resize(3);
+ m_wrappedPolygons[0].wrappedBboxes = wrappedBboxMinus1;
+ m_wrappedPolygons[1].wrappedBboxes = wrappedBbox;
+ m_wrappedPolygons[2].wrappedBboxes = wrappedBboxPlus1;
+}
+
+// This one handles whole QGeoPolygon w. holes
+void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoPolygon &poly)
+{
+ if (!sourceDirty_)
+ return;
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection());
+
+ // build the actual path
+ // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints
+ srcOrigin_ = geoLeftBound_;
+
+ QDoubleVector2D leftBoundWrapped;
+ QList<QList<QDoubleVector2D>> wrappedPath;
+ // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0
+ wrapPath(poly, geoLeftBound_, p,
+ wrappedPath, &leftBoundWrapped);
+
+ // 1.1) do the same for the bbox
+ QList<QDoubleVector2D> wrappedBbox, wrappedBboxPlus1, wrappedBboxMinus1;
+ QGeoPolygon bbox(poly.boundingGeoRectangle());
+ QDeclarativeGeoMapItemUtils::wrapPath(bbox.path(), bbox.boundingGeoRectangle().topLeft(), p,
+ wrappedBbox, wrappedBboxMinus1, wrappedBboxPlus1, &m_bboxLeftBoundWrapped);
+
+ // 2) Store the triangulated polygon, and the wrapped bbox paths.
+ // the triangulations can be used as they are, as they "bypass" the QtQuick display chain
+ // the bbox wraps have to be however clipped, and then projected, in order to figure out the geometry.
+ // Note that this might still cause the geometryChanged method to fail under some extreme conditions.
+ cutPathEars(wrappedPath, m_screenVertices, m_screenIndices);
+ m_wrappedPolygons.resize(3);
+ m_wrappedPolygons[0].wrappedBboxes = wrappedBboxMinus1;
+ m_wrappedPolygons[1].wrappedBboxes = wrappedBbox;
+ m_wrappedPolygons[2].wrappedBboxes = wrappedBboxPlus1;
+}
+
+void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoRectangle &rect)
+{
+ if (!sourceDirty_)
+ return;
+ const QList<QGeoCoordinate> perimeter = QDeclarativeRectangleMapItemPrivateCPU::path(rect);
+ updateSourcePoints(map, perimeter);
+}
+
+/*!
+ \internal
+*/
+void QGeoMapPolygonGeometryOpenGL::updateScreenPoints(const QGeoMap &map, qreal strokeWidth , const QColor &strokeColor)
+{
+ if (map.viewportWidth() == 0 || map.viewportHeight() == 0) {
+ clear();
+ return;
+ }
+
+ // 1) identify which set to use: std, +1 or -1
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection());
+ const QDoubleVector2D leftBoundMercator = p.geoToMapProjection(srcOrigin_);
+ m_wrapOffset = p.projectionWrapFactor(leftBoundMercator) + 1; // +1 to get the offset into QLists
+
+ // 1.1) select geometry set
+ // This could theoretically be skipped for those polygons whose bbox is not even projectable.
+ // However, such optimization could only be introduced if not calculating bboxes lazily.
+ // Hence not doing it.
+ if (sourceDirty_) {
+ m_dataChanged = true;
+ }
+
+ if (strokeWidth == 0.0 || strokeColor.alpha() == 0) // or else the geometry of the border is used, so no point in calculating 2 of them
+ updateQuickGeometry(p, strokeWidth);
+}
+
+void QGeoMapPolygonGeometryOpenGL::updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal /*strokeWidth*/)
+{
+ // 2) clip bbox
+ // BBox handling -- this is related to the bounding box geometry
+ // that has to inevitably follow the old projection codepath
+ // As it needs to provide projected coordinates for QtQuick interaction.
+ // This could be futher optimized to be updated in a lazy fashion.
+ const QList<QDoubleVector2D> &wrappedBbox = m_wrappedPolygons.at(m_wrapOffset).wrappedBboxes;
+ QList<QList<QDoubleVector2D> > clippedBbox;
+ QDoubleVector2D bboxLeftBoundWrapped = m_bboxLeftBoundWrapped;
+ bboxLeftBoundWrapped.setX(bboxLeftBoundWrapped.x() + double(m_wrapOffset - 1));
+ QDeclarativeGeoMapItemUtils::clipPolygon(wrappedBbox, p, clippedBbox, &bboxLeftBoundWrapped);
+
+ // 3) project bbox
+ QPainterPath ppi;
+ if (!clippedBbox.size() || clippedBbox.first().size() < 3) {
+ sourceBounds_ = screenBounds_ = QRectF();
+ firstPointOffset_ = QPointF();
+ screenOutline_ = ppi;
+ return;
+ }
+
+ QDeclarativeGeoMapItemUtils::projectBbox(clippedBbox.first(), p, ppi); // Using first because a clipped box should always result in one polygon
+ const QRectF brect = ppi.boundingRect();
+ firstPointOffset_ = QPointF(brect.topLeft());
+ screenOutline_ = ppi;
+
+ // 4) Set Screen bbox
+ screenBounds_ = brect;
+ sourceBounds_.setX(0);
+ sourceBounds_.setY(0);
+ sourceBounds_.setWidth(brect.width());
+ sourceBounds_.setHeight(brect.height());
+}
+
+/*
+ * QDeclarativePolygonMapItem Private Implementations
+ */
+
+QDeclarativePolygonMapItemPrivate::~QDeclarativePolygonMapItemPrivate() {}
+
+QDeclarativePolygonMapItemPrivateCPU::~QDeclarativePolygonMapItemPrivateCPU() {}
+
+QDeclarativePolygonMapItemPrivateOpenGL::~QDeclarativePolygonMapItemPrivateOpenGL() {}
+
+/*
+ * QDeclarativePolygonMapItem Implementation
+ */
+
+struct PolygonBackendSelector
+{
+ PolygonBackendSelector()
+ {
+ backend = (qgetenv("QTLOCATION_OPENGL_ITEMS").toInt()) ? QDeclarativePolygonMapItem::OpenGL : QDeclarativePolygonMapItem::Software;
+ }
+ QDeclarativePolygonMapItem::Backend backend = QDeclarativePolygonMapItem::Software;
+};
+
+Q_GLOBAL_STATIC(PolygonBackendSelector, mapPolygonBackendSelector)
+
QDeclarativePolygonMapItem::QDeclarativePolygonMapItem(QQuickItem *parent)
-: QDeclarativeGeoMapItemBase(parent), border_(this), color_(Qt::transparent), dirtyMaterial_(true),
- updatingGeometry_(false)
+: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent), m_dirtyMaterial(true),
+ m_updatingGeometry(false)
+ , m_d(new QDeclarativePolygonMapItemPrivateCPU(*this))
+
{
+ // ToDo: handle envvar, and switch implementation.
m_itemType = QGeoMap::MapPolygon;
- geopath_ = QGeoPolygonEager();
+ m_geopoly = QGeoPolygonEager();
setFlag(ItemHasContents, true);
- QObject::connect(&border_, SIGNAL(colorChanged(QColor)),
- this, SLOT(markSourceDirtyAndUpdate()));
- QObject::connect(&border_, SIGNAL(widthChanged(qreal)),
- this, SLOT(markSourceDirtyAndUpdate()));
+ QObject::connect(&m_border, SIGNAL(colorChanged(QColor)),
+ this, SLOT(onLinePropertiesChanged())); // ToDo: fix this, only flag material?
+ QObject::connect(&m_border, SIGNAL(widthChanged(qreal)),
+ this, SLOT(onLinePropertiesChanged()));
+ setBackend(mapPolygonBackendSelector->backend);
}
QDeclarativePolygonMapItem::~QDeclarativePolygonMapItem()
@@ -359,7 +657,44 @@ QDeclarativePolygonMapItem::~QDeclarativePolygonMapItem()
QDeclarativeMapLineProperties *QDeclarativePolygonMapItem::border()
{
- return &border_;
+ return &m_border;
+}
+
+/*!
+ \qmlproperty MapPolygon.Backend QtLocation::MapPolygon::backend
+
+ This property holds which backend is in use to render the map item.
+ Valid values are \b MapPolygon.Software and \b{MapPolygon.OpenGL}.
+ The default value is \b{MapPolygon.Software}.
+
+ \note \b{The release of this API with Qt 5.15 is a Technology Preview}.
+ Ideally, as the OpenGL backends for map items mature, there will be
+ no more need to also offer the legacy software-projection backend.
+ So this property will likely disappear at some later point.
+ To select OpenGL-accelerated item backends without using this property,
+ it is also possible to set the environment variable \b QTLOCATION_OPENGL_ITEMS
+ to \b{1}.
+ Also note that all current OpenGL backends won't work as expected when enabling
+ layers on the individual item, or when running on OpenGL core profiles greater than 2.x.
+
+ \since 5.15
+*/
+QDeclarativePolygonMapItem::Backend QDeclarativePolygonMapItem::backend() const
+{
+ return m_backend;
+}
+
+void QDeclarativePolygonMapItem::setBackend(QDeclarativePolygonMapItem::Backend b)
+{
+ if (b == m_backend)
+ return;
+ m_backend = b;
+ QScopedPointer<QDeclarativePolygonMapItemPrivate> d((m_backend == Software)
+ ? static_cast<QDeclarativePolygonMapItemPrivate *>(new QDeclarativePolygonMapItemPrivateCPU(*this))
+ : static_cast<QDeclarativePolygonMapItemPrivate * >(new QDeclarativePolygonMapItemPrivateOpenGL(*this)));
+ m_d.swap(d);
+ m_d->onGeoGeometryChanged();
+ emit backendChanged();
}
/*!
@@ -368,12 +703,8 @@ QDeclarativeMapLineProperties *QDeclarativePolygonMapItem::border()
void QDeclarativePolygonMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map)
{
QDeclarativeGeoMapItemBase::setMap(quickMap,map);
- if (map) {
- regenerateCache();
- geometry_.markSourceDirty();
- borderGeometry_.markSourceDirty();
- polishAndUpdate();
- }
+ if (map)
+ m_d->onMapSet();
}
/*!
@@ -387,7 +718,7 @@ void QDeclarativePolygonMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *m
*/
QJSValue QDeclarativePolygonMapItem::path() const
{
- return fromList(this, geopath_.path());
+ return fromList(this, m_geopoly.path());
}
void QDeclarativePolygonMapItem::setPath(const QJSValue &value)
@@ -398,15 +729,12 @@ void QDeclarativePolygonMapItem::setPath(const QJSValue &value)
QList<QGeoCoordinate> pathList = toList(this, value);
// Equivalent to QDeclarativePolylineMapItem::setPathFromGeoList
- if (geopath_.path() == pathList)
+ if (m_geopoly.path() == pathList)
return;
- geopath_.setPath(pathList);
+ m_geopoly.setPath(pathList);
- regenerateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryChanged();
emit pathChanged();
}
@@ -423,11 +751,8 @@ void QDeclarativePolygonMapItem::addCoordinate(const QGeoCoordinate &coordinate)
if (!coordinate.isValid())
return;
- geopath_.addCoordinate(coordinate);
- updateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_geopoly.addCoordinate(coordinate);
+ m_d->onGeoGeometryUpdated();
emit pathChanged();
}
@@ -443,15 +768,12 @@ void QDeclarativePolygonMapItem::addCoordinate(const QGeoCoordinate &coordinate)
*/
void QDeclarativePolygonMapItem::removeCoordinate(const QGeoCoordinate &coordinate)
{
- int length = geopath_.path().length();
- geopath_.removeCoordinate(coordinate);
- if (geopath_.path().length() == length)
+ int length = m_geopoly.path().length();
+ m_geopoly.removeCoordinate(coordinate);
+ if (m_geopoly.path().length() == length)
return;
- regenerateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryChanged();
emit pathChanged();
}
@@ -465,18 +787,18 @@ void QDeclarativePolygonMapItem::removeCoordinate(const QGeoCoordinate &coordina
QColor QDeclarativePolygonMapItem::color() const
{
- return color_;
+ return m_color;
}
void QDeclarativePolygonMapItem::setColor(const QColor &color)
{
- if (color_ == color)
+ if (m_color == color)
return;
- color_ = color;
- dirtyMaterial_ = true;
- update();
- emit colorChanged(color_);
+ m_color = color;
+ m_dirtyMaterial = true;
+ polishAndUpdate(); // in case color was transparent and now is not or vice versa
+ emit colorChanged(m_color);
}
/*!
@@ -484,22 +806,7 @@ void QDeclarativePolygonMapItem::setColor(const QColor &color)
*/
QSGNode *QDeclarativePolygonMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
{
- Q_UNUSED(data);
- MapPolygonNode *node = static_cast<MapPolygonNode *>(oldNode);
-
- if (!node)
- node = new MapPolygonNode();
-
- //TODO: update only material
- if (geometry_.isScreenDirty() || borderGeometry_.isScreenDirty() || dirtyMaterial_) {
- node->update(color_, border_.color(), &geometry_, &borderGeometry_);
- geometry_.setPreserveGeometry(false);
- borderGeometry_.setPreserveGeometry(false);
- geometry_.markClean();
- borderGeometry_.markClean();
- dirtyMaterial_ = false;
- }
- return node;
+ return m_d->updateMapItemPaintNode(oldNode, data);
}
/*!
@@ -509,102 +816,34 @@ void QDeclarativePolygonMapItem::updatePolish()
{
if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
return;
- if (geopath_.path().length() == 0) { // Possibly cleared
- geometry_.clear();
- borderGeometry_.clear();
- setWidth(0);
- setHeight(0);
- return;
- }
-
- const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection());
- QScopedValueRollback<bool> rollback(updatingGeometry_);
- updatingGeometry_ = true;
-
- geometry_.updateSourcePoints(*map(), geopathProjected_);
- geometry_.updateScreenPoints(*map(), border_.width());
-
- QList<QGeoMapItemGeometry *> geoms;
- geoms << &geometry_;
- borderGeometry_.clear();
-
- if (border_.color() != Qt::transparent && border_.width() > 0) {
- QList<QDoubleVector2D> closedPath = geopathProjected_;
- closedPath << closedPath.first();
-
- borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
-
- const QGeoCoordinate &geometryOrigin = geometry_.origin();
-
- borderGeometry_.srcPoints_.clear();
- borderGeometry_.srcPointTypes_.clear();
-
- QDoubleVector2D borderLeftBoundWrapped;
- QList<QList<QDoubleVector2D > > clippedPaths = borderGeometry_.clipPath(*map(), closedPath, borderLeftBoundWrapped);
- if (clippedPaths.size()) {
- borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin);
- borderGeometry_.pathToScreen(*map(), clippedPaths, borderLeftBoundWrapped);
- borderGeometry_.updateScreenPoints(*map(), border_.width());
-
- geoms << &borderGeometry_;
- } else {
- borderGeometry_.clear();
- }
- }
-
- QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms);
- setWidth(combined.width() + 2 * border_.width());
- setHeight(combined.height() + 2 * border_.width());
-
- setPositionOnMap(geometry_.origin(), -1 * geometry_.sourceBoundingBox().topLeft()
- + QPointF(border_.width(), border_.width()));
+ m_d->updatePolish();
}
-void QDeclarativePolygonMapItem::markSourceDirtyAndUpdate()
+void QDeclarativePolygonMapItem::setMaterialDirty()
{
- geometry_.markSourceDirty();
- borderGeometry_.markSourceDirty();
- polishAndUpdate();
+ m_dirtyMaterial = true;
+ update();
}
-/*!
- \internal
-*/
-void QDeclarativePolygonMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event)
+void QDeclarativePolygonMapItem::markSourceDirtyAndUpdate()
{
- if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0)
- return;
-
- geometry_.setPreserveGeometry(true, geometry_.geoLeftBound());
- borderGeometry_.setPreserveGeometry(true, borderGeometry_.geoLeftBound());
- geometry_.markSourceDirty();
- borderGeometry_.markSourceDirty();
- polishAndUpdate();
+ m_d->markSourceDirtyAndUpdate();
}
-/*!
- \internal
-*/
-void QDeclarativePolygonMapItem::regenerateCache()
+void QDeclarativePolygonMapItem::onLinePropertiesChanged()
{
- if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
- return;
- const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection());
- geopathProjected_.clear();
- geopathProjected_.reserve(geopath_.path().size());
- for (const QGeoCoordinate &c : geopath_.path())
- geopathProjected_ << p.geoToMapProjection(c);
+ m_d->onLinePropertiesChanged();
}
/*!
\internal
*/
-void QDeclarativePolygonMapItem::updateCache()
+void QDeclarativePolygonMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event)
{
- if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
+ if (event.mapSize.isEmpty())
return;
- const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection());
- geopathProjected_ << p.geoToMapProjection(geopath_.path().last());
+
+ m_d->afterViewportChanged();
}
/*!
@@ -612,24 +851,21 @@ void QDeclarativePolygonMapItem::updateCache()
*/
bool QDeclarativePolygonMapItem::contains(const QPointF &point) const
{
- return (geometry_.contains(point) || borderGeometry_.contains(point));
+ return m_d->contains(point);
}
const QGeoShape &QDeclarativePolygonMapItem::geoShape() const
{
- return geopath_;
+ return m_geopoly;
}
void QDeclarativePolygonMapItem::setGeoShape(const QGeoShape &shape)
{
- if (shape == geopath_)
+ if (shape == m_geopoly)
return;
- geopath_ = QGeoPolygonEager(shape);
- regenerateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_geopoly = QGeoPolygonEager(shape);
+ m_d->onGeoGeometryChanged();
emit pathChanged();
}
@@ -638,7 +874,7 @@ void QDeclarativePolygonMapItem::setGeoShape(const QGeoShape &shape)
*/
void QDeclarativePolygonMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
- if (!map() || !geopath_.isValid() || updatingGeometry_ || newGeometry.topLeft() == oldGeometry.topLeft()) {
+ if (newGeometry.topLeft() == oldGeometry.topLeft() || !map() || !m_geopoly.isValid() || m_updatingGeometry) {
QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry);
return;
}
@@ -652,11 +888,8 @@ void QDeclarativePolygonMapItem::geometryChanged(const QRectF &newGeometry, cons
if (offsetLati == 0.0 && offsetLongi == 0.0)
return;
- geopath_.translate(offsetLati, offsetLongi);
- regenerateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_geopoly.translate(offsetLati, offsetLongi);
+ m_d->onGeoGeometryChanged();
emit pathChanged();
// Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested
@@ -665,6 +898,25 @@ void QDeclarativePolygonMapItem::geometryChanged(const QRectF &newGeometry, cons
//////////////////////////////////////////////////////////////////////
+QSGMaterialShader *MapPolygonMaterial::createShader() const
+{
+ return new MapPolygonShader();
+}
+
+int MapPolygonMaterial::compare(const QSGMaterial *other) const
+{
+ const MapPolygonMaterial &o = *static_cast<const MapPolygonMaterial *>(other);
+ if (o.m_center == m_center && o.m_geoProjection == m_geoProjection && o.m_wrapOffset == m_wrapOffset)
+ return QSGFlatColorMaterial::compare(other);
+ return -1;
+}
+
+QSGMaterialType *MapPolygonMaterial::type() const
+{
+ static QSGMaterialType type;
+ return &type;
+}
+
MapPolygonNode::MapPolygonNode() :
border_(new MapPolylineNode()),
geometry_(QSGGeometry::defaultAttributes_Point2D(), 0)
@@ -701,6 +953,9 @@ void MapPolygonNode::update(const QColor &fillColor, const QColor &borderColor,
setSubtreeBlocked(false);
+ // TODO: do this only if the geometry has changed!!
+ // No need to do this every frame.
+ // Then benchmark the difference!
QSGGeometry *fill = QSGGeometryNode::geometry();
fillShape->allocateAndFill(fill);
markDirty(DirtyGeometry);
@@ -712,4 +967,90 @@ void MapPolygonNode::update(const QColor &fillColor, const QColor &borderColor,
}
}
+MapPolygonNodeGL::MapPolygonNodeGL() :
+ //fill_material_(this),
+ fill_material_(),
+ geometry_(QSGGeometry::defaultAttributes_Point2D(), 0)
+{
+ geometry_.setDrawingMode(QSGGeometry::DrawTriangles);
+ QSGGeometryNode::setMaterial(&fill_material_);
+ QSGGeometryNode::setGeometry(&geometry_);
+}
+
+MapPolygonNodeGL::~MapPolygonNodeGL()
+{
+}
+
+/*!
+ \internal
+*/
+void MapPolygonNodeGL::update(const QColor &fillColor,
+ const QGeoMapPolygonGeometryOpenGL *fillShape,
+ const QMatrix4x4 &geoProjection,
+ const QDoubleVector3D &center)
+{
+ if (fillShape->m_screenIndices.size() < 3 || fillColor.alpha() == 0) {
+ setSubtreeBlocked(true);
+ return;
+ }
+ setSubtreeBlocked(false);
+
+ QSGGeometry *fill = QSGGeometryNode::geometry();
+ if (fillShape->m_dataChanged || !fill->vertexCount()) {
+ fillShape->allocateAndFillPolygon(fill);
+ markDirty(DirtyGeometry);
+ fillShape->m_dataChanged = false;
+ }
+
+ //if (fillColor != fill_material_.color()) // Any point in optimizing this?
+ {
+ fill_material_.setColor(fillColor);
+ fill_material_.setGeoProjection(geoProjection);
+ fill_material_.setCenter(center);
+ fill_material_.setWrapOffset(fillShape->m_wrapOffset - 1);
+ setMaterial(&fill_material_);
+ markDirty(DirtyMaterial);
+ }
+}
+
+MapPolygonShader::MapPolygonShader() : QSGMaterialShader(*new QSGMaterialShaderPrivate)
+{
+
+}
+
+void MapPolygonShader::updateState(const QSGMaterialShader::RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
+{
+ Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type());
+ MapPolygonMaterial *oldMaterial = static_cast<MapPolygonMaterial *>(oldEffect);
+ MapPolygonMaterial *newMaterial = static_cast<MapPolygonMaterial *>(newEffect);
+
+ const QColor &c = newMaterial->color();
+ const QMatrix4x4 &geoProjection = newMaterial->geoProjection();
+ const QDoubleVector3D &center = newMaterial->center();
+
+ QVector3D vecCenter, vecCenter_lowpart;
+ for (int i = 0; i < 3; i++)
+ QLocationUtils::split_double(center.get(i), &vecCenter[i], &vecCenter_lowpart[i]);
+
+ if (oldMaterial == nullptr || c != oldMaterial->color() || state.isOpacityDirty()) {
+ float opacity = state.opacity() * c.alphaF();
+ QVector4D v(c.redF() * opacity,
+ c.greenF() * opacity,
+ c.blueF() * opacity,
+ opacity);
+ program()->setUniformValue(m_color_id, v);
+ }
+
+ if (state.isMatrixDirty())
+ {
+ program()->setUniformValue(m_matrix_id, state.projectionMatrix());
+ }
+
+ program()->setUniformValue(m_mapProjection_id, geoProjection);
+
+ program()->setUniformValue(m_center_id, vecCenter);
+ program()->setUniformValue(m_center_lowpart_id, vecCenter_lowpart);
+ program()->setUniformValue(m_wrapOffset_id, float(newMaterial->wrapOffset()));
+}
+
QT_END_NAMESPACE
diff --git a/src/location/declarativemaps/qdeclarativepolygonmapitem_p.h b/src/location/declarativemaps/qdeclarativepolygonmapitem_p.h
index 810a8d35..efc1a137 100644
--- a/src/location/declarativemaps/qdeclarativepolygonmapitem_p.h
+++ b/src/location/declarativemaps/qdeclarativepolygonmapitem_p.h
@@ -51,44 +51,29 @@
#include <QtLocation/private/qlocationglobal_p.h>
#include <QtLocation/private/qdeclarativegeomapitembase_p.h>
#include <QtLocation/private/qdeclarativepolylinemapitem_p.h>
-#include <QtLocation/private/qgeomapitemgeometry_p.h>
#include <QtPositioning/qgeopolygon.h>
-#include <QSGGeometryNode>
-#include <QSGFlatColorMaterial>
-
QT_BEGIN_NAMESPACE
-class MapPolygonNode;
-
-class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometry : public QGeoMapItemGeometry
-{
-public:
- QGeoMapPolygonGeometry();
-
- inline void setAssumeSimple(bool value) { assumeSimple_ = value; }
-
- void updateSourcePoints(const QGeoMap &map,
- const QList<QDoubleVector2D> &path);
-
- void updateScreenPoints(const QGeoMap &map, qreal strokeWidth = 0.0);
-
-protected:
- QPainterPath srcPath_;
- bool assumeSimple_;
-};
-
+class QDeclarativePolygonMapItemPrivate;
class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItem : public QDeclarativeGeoMapItemBase
{
Q_OBJECT
+ Q_ENUMS(Backend)
Q_PROPERTY(QJSValue path READ path WRITE setPath NOTIFY pathChanged)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(QDeclarativeMapLineProperties *border READ border CONSTANT)
+ Q_PROPERTY(Backend backend READ backend WRITE setBackend NOTIFY backendChanged REVISION 15)
public:
- explicit QDeclarativePolygonMapItem(QQuickItem *parent = 0);
- ~QDeclarativePolygonMapItem();
+ enum Backend {
+ Software = 0,
+ OpenGL = 1
+ };
+
+ explicit QDeclarativePolygonMapItem(QQuickItem *parent = nullptr);
+ ~QDeclarativePolygonMapItem() override;
virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) override;
//from QuickItem
@@ -105,6 +90,9 @@ public:
QDeclarativeMapLineProperties *border();
+ Backend backend() const;
+ void setBackend(Backend b);
+
bool contains(const QPointF &point) const override;
const QGeoShape &geoShape() const override;
void setGeoShape(const QGeoShape &shape) override;
@@ -112,45 +100,34 @@ public:
Q_SIGNALS:
void pathChanged();
void colorChanged(const QColor &color);
-
-protected:
- void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
- void updatePolish() override;
+ void backendChanged();
protected Q_SLOTS:
void markSourceDirtyAndUpdate();
+ void onLinePropertiesChanged();
virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) override;
-private:
- void regenerateCache();
- void updateCache();
-
- QGeoPolygon geopath_;
- QList<QDoubleVector2D> geopathProjected_;
- QDeclarativeMapLineProperties border_;
- QColor color_;
- bool dirtyMaterial_;
- QGeoMapPolygonGeometry geometry_;
- QGeoMapPolylineGeometry borderGeometry_;
- bool updatingGeometry_;
-};
-
-//////////////////////////////////////////////////////////////////////
-
-class Q_LOCATION_PRIVATE_EXPORT MapPolygonNode : public MapItemGeometryNode
-{
+protected:
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ void updatePolish() override;
+ void setMaterialDirty() override;
+#ifdef QT_LOCATION_DEBUG
public:
- MapPolygonNode();
- ~MapPolygonNode() override;
-
- void update(const QColor &fillColor, const QColor &borderColor,
- const QGeoMapItemGeometry *fillShape,
- const QGeoMapItemGeometry *borderShape);
-private:
- QSGFlatColorMaterial fill_material_;
- MapPolylineNode *border_;
- QSGGeometry geometry_;
+#endif
+ QGeoPolygon m_geopoly;
+ QDeclarativeMapLineProperties m_border;
+ QColor m_color;
+ Backend m_backend = Software;
+ bool m_dirtyMaterial;
+// bool m_dirtyGeometry = false;
+ bool m_updatingGeometry;
+
+ QScopedPointer<QDeclarativePolygonMapItemPrivate> m_d;
+
+ friend class QDeclarativePolygonMapItemPrivate;
+ friend class QDeclarativePolygonMapItemPrivateCPU;
+ friend class QDeclarativePolygonMapItemPrivateOpenGL;
};
QT_END_NAMESPACE
diff --git a/src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h b/src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h
new file mode 100644
index 00000000..83f1100e
--- /dev/null
+++ b/src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h
@@ -0,0 +1,664 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Paolo Angelelli <paolo.angelelli@gmail.com>
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtLocation module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVEPOLYGONMAPITEM_P_P_H
+#define QDECLARATIVEPOLYGONMAPITEM_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtLocation/private/qlocationglobal_p.h>
+#include <QtLocation/private/qgeomapitemgeometry_p.h>
+#include <QtLocation/private/qdeclarativegeomapitembase_p.h>
+#include <QtLocation/private/qdeclarativepolylinemapitem_p.h>
+#include <QtLocation/private/qdeclarativegeomapitemutils_p.h>
+#include <QtLocation/private/qdeclarativepolygonmapitem_p.h>
+#include <QtLocation/private/qdeclarativepolylinemapitem_p_p.h>
+#include <QSGGeometryNode>
+#include <QSGFlatColorMaterial>
+#include <QtPositioning/QGeoPath>
+#include <QtPositioning/QGeoRectangle>
+#include <QtPositioning/QGeoPolygon>
+#include <QtPositioning/private/qdoublevector2d_p.h>
+#include <QSGFlatColorMaterial>
+#include <QSGSimpleMaterial>
+#include <QtGui/QMatrix4x4>
+#include <QColor>
+#include <QList>
+#include <QVector>
+#include <QtCore/QScopedValueRollback>
+
+QT_BEGIN_NAMESPACE
+
+class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometry : public QGeoMapItemGeometry
+{
+public:
+ QGeoMapPolygonGeometry();
+
+ inline void setAssumeSimple(bool value) { assumeSimple_ = value; }
+
+ void updateSourcePoints(const QGeoMap &map,
+ const QList<QDoubleVector2D> &path);
+
+ void updateScreenPoints(const QGeoMap &map, qreal strokeWidth = 0.0);
+
+protected:
+ QPainterPath srcPath_;
+ bool assumeSimple_;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometryOpenGL : public QGeoMapItemGeometry
+{
+public:
+ typedef struct {
+ QList<QDoubleVector2D> wrappedBboxes;
+ } WrappedPolygon;
+ QGeoMapPolygonGeometryOpenGL();
+ ~QGeoMapPolygonGeometryOpenGL() override {}
+
+ // Temporary method for compatibility in MapCircleObject. Remove when MapObjects are ported.
+ void updateSourcePoints(const QGeoMap &map,
+ const QList<QDoubleVector2D> &path);
+
+ void updateSourcePoints(const QGeoMap &map,
+ const QList<QGeoCoordinate> &perimeter);
+
+ void updateSourcePoints(const QGeoMap &map,
+ const QGeoPolygon &poly);
+
+ void updateSourcePoints(const QGeoMap &map,
+ const QGeoRectangle &rect);
+
+ void updateScreenPoints(const QGeoMap &map, qreal strokeWidth = 0.0, const QColor &strokeColor = Qt::transparent);
+ void updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth = 0.0);
+
+ void allocateAndFillPolygon(QSGGeometry *geom) const
+ {
+ const QVector<QDeclarativeGeoMapItemUtils::vec2> &vx = m_screenVertices;
+ const QVector<quint32> &ix = m_screenIndices;
+
+ geom->allocate(vx.size(), ix.size());
+ if (geom->indexType() == QSGGeometry::UnsignedShortType) {
+ quint16 *its = geom->indexDataAsUShort();
+ for (int i = 0; i < ix.size(); ++i)
+ its[i] = ix[i];
+ } else if (geom->indexType() == QSGGeometry::UnsignedIntType) {
+ quint32 *its = geom->indexDataAsUInt();
+ for (int i = 0; i < ix.size(); ++i)
+ its[i] = ix[i];
+ }
+
+ QSGGeometry::Point2D *pts = geom->vertexDataAsPoint2D();
+ for (int i = 0; i < vx.size(); ++i)
+ pts[i].set(vx[i].x, vx[i].y);
+ }
+
+ QVector<QDeclarativeGeoMapItemUtils::vec2> m_screenVertices;
+ QVector<quint32> m_screenIndices;
+ QDoubleVector2D m_bboxLeftBoundWrapped;
+ QVector<WrappedPolygon> m_wrappedPolygons;
+ int m_wrapOffset;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT MapPolygonShader : public QSGMaterialShader
+{
+public:
+ MapPolygonShader();
+
+ const char *vertexShader() const override {
+ return
+ "attribute highp vec4 vertex; \n"
+ "uniform highp mat4 qt_Matrix; \n"
+ "uniform highp mat4 mapProjection; \n"
+ "uniform highp vec3 center; \n"
+ "uniform highp vec3 center_lowpart; \n"
+ "uniform lowp float wrapOffset; \n"
+ "vec4 wrapped(in vec4 v) { return vec4(v.x + wrapOffset, v.y, 0.0, 1.0); }\n"
+ "void main() { \n"
+ " vec4 vtx = wrapped(vertex) - vec4(center, 0.0); \n"
+ " vtx = vtx - vec4(center_lowpart, 0.0); \n"
+ " gl_Position = qt_Matrix * mapProjection * vtx; \n"
+ "}";
+ }
+
+ const char *fragmentShader() const override {
+ return
+ "uniform lowp vec4 color; \n"
+ "void main() { \n"
+ " gl_FragColor = color; \n"
+ "}";
+ }
+
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+ char const *const *attributeNames() const override
+ {
+ static char const *const attr[] = { "vertex", nullptr };
+ return attr;
+ }
+
+private:
+ void initialize() override
+ {
+ m_matrix_id = program()->uniformLocation("qt_Matrix");
+ m_color_id = program()->uniformLocation("color");
+ m_mapProjection_id = program()->uniformLocation("mapProjection");
+ m_center_id = program()->uniformLocation("center");
+ m_center_lowpart_id = program()->uniformLocation("center_lowpart");
+ m_wrapOffset_id = program()->uniformLocation("wrapOffset");
+ }
+ int m_center_id;
+ int m_center_lowpart_id;
+ int m_mapProjection_id;
+ int m_matrix_id;
+ int m_color_id;
+ int m_wrapOffset_id;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT MapPolygonMaterial : public QSGFlatColorMaterial
+{
+public:
+ MapPolygonMaterial()
+ : QSGFlatColorMaterial()
+ {
+ // Passing RequiresFullMatrix is essential in order to prevent the
+ // batch renderer from baking in simple, translate-only transforms into
+ // the vertex data. The shader will rely on the fact that
+ // vertexCoord.xy is the Shape-space coordinate and so no modifications
+ // are welcome.
+ setFlag(Blending | RequiresFullMatrix | CustomCompileStep);
+ }
+
+ QSGMaterialShader *createShader() const override;
+
+ void setGeoProjection(const QMatrix4x4 &p)
+ {
+ m_geoProjection = p;
+ }
+
+ QMatrix4x4 geoProjection() const
+ {
+ return m_geoProjection;
+ }
+
+ void setCenter(const QDoubleVector3D &c)
+ {
+ m_center = c;
+ }
+
+ QDoubleVector3D center() const
+ {
+ return m_center;
+ }
+
+ int wrapOffset() const
+ {
+ return m_wrapOffset;
+ }
+
+ void setWrapOffset(int wrapOffset)
+ {
+ m_wrapOffset = wrapOffset;
+ }
+
+ int compare(const QSGMaterial *other) const override;
+ QSGMaterialType *type() const override;
+
+protected:
+ QMatrix4x4 m_geoProjection;
+ QDoubleVector3D m_center;
+ int m_wrapOffset = 0;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT MapPolygonNode : public MapItemGeometryNode
+{
+
+public:
+ MapPolygonNode();
+ ~MapPolygonNode() override;
+
+ void update(const QColor &fillColor, const QColor &borderColor,
+ const QGeoMapItemGeometry *fillShape,
+ const QGeoMapItemGeometry *borderShape);
+private:
+ QSGFlatColorMaterial fill_material_;
+ MapPolylineNode *border_;
+ QSGGeometry geometry_;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT MapPolygonNodeGL : public MapItemGeometryNode
+{
+
+public:
+ MapPolygonNodeGL();
+ ~MapPolygonNodeGL() override;
+
+ void update(const QColor &fillColor,
+ const QGeoMapPolygonGeometryOpenGL *fillShape,
+ const QMatrix4x4 &geoProjection,
+ const QDoubleVector3D &center);
+
+ MapPolygonMaterial fill_material_;
+ QSGGeometry geometry_;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItemPrivate
+{
+public:
+ QDeclarativePolygonMapItemPrivate(QDeclarativePolygonMapItem &polygon) : m_poly(polygon)
+ {
+
+ }
+ QDeclarativePolygonMapItemPrivate(QDeclarativePolygonMapItemPrivate &other) : m_poly(other.m_poly)
+ {
+ }
+
+ virtual ~QDeclarativePolygonMapItemPrivate();
+ virtual void onLinePropertiesChanged() = 0;
+ virtual void markSourceDirtyAndUpdate() = 0;
+ virtual void onMapSet() = 0;
+ virtual void onGeoGeometryChanged() = 0;
+ virtual void onGeoGeometryUpdated() = 0;
+ virtual void onItemGeometryChanged() = 0;
+ virtual void updatePolish() = 0;
+ virtual void afterViewportChanged() = 0;
+ virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) = 0;
+ virtual bool contains(const QPointF &point) const = 0;
+
+ QDeclarativePolygonMapItem &m_poly;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItemPrivateCPU: public QDeclarativePolygonMapItemPrivate
+{
+public:
+ QDeclarativePolygonMapItemPrivateCPU(QDeclarativePolygonMapItem &polygon) : QDeclarativePolygonMapItemPrivate(polygon)
+ {
+ }
+
+ QDeclarativePolygonMapItemPrivateCPU(QDeclarativePolygonMapItemPrivate &other)
+ : QDeclarativePolygonMapItemPrivate(other)
+ {
+ }
+
+ ~QDeclarativePolygonMapItemPrivateCPU() override;
+ void onLinePropertiesChanged() override
+ {
+ // mark dirty just in case we're a width change
+ markSourceDirtyAndUpdate();
+ }
+ void markSourceDirtyAndUpdate() override
+ {
+ // preserveGeometry is cleared in updateMapItemPaintNode
+ m_geometry.markSourceDirty();
+ m_borderGeometry.markSourceDirty();
+ m_poly.polishAndUpdate();
+ }
+ void regenerateCache()
+ {
+ if (!m_poly.map() || m_poly.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
+ return;
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection());
+ m_geopathProjected.clear();
+ m_geopathProjected.reserve(m_poly.m_geopoly.size());
+ for (const QGeoCoordinate &c : m_poly.m_geopoly.path())
+ m_geopathProjected << p.geoToMapProjection(c);
+ }
+ void updateCache()
+ {
+ if (!m_poly.map() || m_poly.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
+ return;
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection());
+ m_geopathProjected << p.geoToMapProjection(m_poly.m_geopoly.path().last());
+ }
+ void preserveGeometry()
+ {
+ m_geometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft());
+ m_borderGeometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft());
+ }
+ void afterViewportChanged() override
+ {
+ // preserveGeometry is cleared in updateMapItemPaintNode
+ preserveGeometry();
+ markSourceDirtyAndUpdate();
+ }
+ void onMapSet() override
+ {
+ regenerateCache();
+ markSourceDirtyAndUpdate();
+ }
+ void onGeoGeometryChanged() override
+ {
+ regenerateCache();
+ preserveGeometry();
+ markSourceDirtyAndUpdate();
+ }
+ void onGeoGeometryUpdated() override
+ {
+ updateCache();
+ preserveGeometry();
+ markSourceDirtyAndUpdate();
+ }
+ void onItemGeometryChanged() override
+ {
+ onGeoGeometryChanged();
+ }
+ void updatePolish() override
+ {
+ if (m_poly.m_geopoly.path().length() == 0) { // Possibly cleared
+ m_geometry.clear();
+ m_borderGeometry.clear();
+ m_poly.setWidth(0);
+ m_poly.setHeight(0);
+ return;
+ }
+ const QGeoMap *map = m_poly.map();
+ const qreal borderWidth = m_poly.m_border.width();
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map->geoProjection());
+ QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry);
+ m_poly.m_updatingGeometry = true;
+
+ m_geometry.updateSourcePoints(*map, m_geopathProjected);
+ m_geometry.updateScreenPoints(*map, borderWidth);
+
+ QList<QGeoMapItemGeometry *> geoms;
+ geoms << &m_geometry;
+ m_borderGeometry.clear();
+
+ if (m_poly.m_border.color().alpha() != 0 && borderWidth > 0) {
+ QList<QDoubleVector2D> closedPath = m_geopathProjected;
+ closedPath << closedPath.first();
+
+ m_borderGeometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft());
+
+ const QGeoCoordinate &geometryOrigin = m_geometry.origin();
+
+ m_borderGeometry.srcPoints_.clear();
+ m_borderGeometry.srcPointTypes_.clear();
+
+ QDoubleVector2D borderLeftBoundWrapped;
+ QList<QList<QDoubleVector2D > > clippedPaths = m_borderGeometry.clipPath(*map, closedPath, borderLeftBoundWrapped);
+ if (clippedPaths.size()) {
+ borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin);
+ m_borderGeometry.pathToScreen(*map, clippedPaths, borderLeftBoundWrapped);
+ m_borderGeometry.updateScreenPoints(*map, borderWidth);
+
+ geoms << &m_borderGeometry;
+ } else {
+ m_borderGeometry.clear();
+ }
+ }
+
+ QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms);
+ m_poly.setWidth(combined.width() + 2 * borderWidth);
+ m_poly.setHeight(combined.height() + 2 * borderWidth);
+
+ m_poly.setPositionOnMap(m_geometry.origin(), -1 * m_geometry.sourceBoundingBox().topLeft()
+ + QPointF(borderWidth, borderWidth));
+ }
+ QSGNode *updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override
+ {
+ Q_UNUSED(data);
+ if (!m_node || !oldNode) {
+ m_node = new MapPolygonNode();
+ if (oldNode) {
+ delete oldNode;
+ oldNode = nullptr;
+ }
+ } else {
+ m_node = static_cast<MapPolygonNode *>(oldNode);
+ }
+
+ //TODO: update only material
+ if (m_geometry.isScreenDirty()
+ || m_borderGeometry.isScreenDirty()
+ || m_poly.m_dirtyMaterial
+ || !oldNode) {
+ m_node->update(m_poly.m_color,
+ m_poly.m_border.color(),
+ &m_geometry,
+ &m_borderGeometry);
+ m_geometry.setPreserveGeometry(false);
+ m_borderGeometry.setPreserveGeometry(false);
+ m_geometry.markClean();
+ m_borderGeometry.markClean();
+ m_poly.m_dirtyMaterial = false;
+ }
+ return m_node;
+ }
+ bool contains(const QPointF &point) const override
+ {
+ return (m_geometry.contains(point) || m_borderGeometry.contains(point));
+ }
+
+ QList<QDoubleVector2D> m_geopathProjected;
+ QGeoMapPolygonGeometry m_geometry;
+ QGeoMapPolylineGeometry m_borderGeometry;
+ MapPolygonNode *m_node = nullptr;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItemPrivateOpenGL: public QDeclarativePolygonMapItemPrivate
+{
+public:
+ struct RootNode : public QSGNode /*QSGTransformNode*/, public VisibleNode
+ {
+ RootNode() { }
+
+ bool isSubtreeBlocked() const override
+ {
+ return subtreeBlocked();
+ }
+ };
+
+ QDeclarativePolygonMapItemPrivateOpenGL(QDeclarativePolygonMapItem &polygon) : QDeclarativePolygonMapItemPrivate(polygon)
+ {
+ }
+
+ QDeclarativePolygonMapItemPrivateOpenGL(QDeclarativePolygonMapItemPrivate &other)
+ : QDeclarativePolygonMapItemPrivate(other)
+ {
+ }
+
+ ~QDeclarativePolygonMapItemPrivateOpenGL() override;
+
+ void markScreenDirtyAndUpdate()
+ {
+ // preserveGeometry is cleared in updateMapItemPaintNode
+ m_geometry.markScreenDirty();
+ m_borderGeometry.markScreenDirty();
+ m_poly.polishAndUpdate();
+ }
+ void onLinePropertiesChanged() override
+ {
+ m_poly.m_dirtyMaterial = true;
+ afterViewportChanged();
+ }
+ void markSourceDirtyAndUpdate() override
+ {
+ // preserveGeometry is cleared in updateMapItemPaintNode
+ m_geometry.markSourceDirty();
+ m_borderGeometry.markSourceDirty();
+ m_poly.polishAndUpdate();
+ }
+ void preserveGeometry()
+ {
+ m_geometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft());
+ m_borderGeometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft());
+ }
+ void afterViewportChanged() override // This is called when the camera changes, or visibleArea changes.
+ {
+ // preserveGeometry is cleared in updateMapItemPaintNode
+ preserveGeometry();
+ markScreenDirtyAndUpdate();
+ }
+ void onMapSet() override
+ {
+ markSourceDirtyAndUpdate();
+ }
+ void onGeoGeometryChanged() override
+ {
+ preserveGeometry();
+ markSourceDirtyAndUpdate();
+ }
+ void onGeoGeometryUpdated() override
+ {
+ preserveGeometry();
+ markSourceDirtyAndUpdate();
+ }
+ void onItemGeometryChanged() override
+ {
+ onGeoGeometryChanged();
+ }
+ void updatePolish() override
+ {
+ if (m_poly.m_geopoly.path().length() == 0) { // Possibly cleared
+ m_geometry.clear();
+ m_borderGeometry.clear();
+ m_poly.setWidth(0);
+ m_poly.setHeight(0);
+ return;
+ }
+
+ QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry);
+ m_poly.m_updatingGeometry = true;
+ const qreal lineWidth = m_poly.m_border.width();
+ const QColor &lineColor = m_poly.m_border.color();
+ const QColor &fillColor = m_poly.color();
+ if (fillColor.alpha() != 0) {
+ m_geometry.updateSourcePoints(*m_poly.map(), m_poly.m_geopoly);
+ m_geometry.markScreenDirty();
+ m_geometry.updateScreenPoints(*m_poly.map(), lineWidth, lineColor);
+ } else {
+ m_geometry.clearBounds();
+ }
+
+ QGeoMapItemGeometry * geom = &m_geometry;
+ m_borderGeometry.clearScreen();
+ if (lineColor.alpha() != 0 && lineWidth > 0) {
+ m_borderGeometry.updateSourcePoints(*m_poly.map(), m_poly.m_geopoly);
+ m_borderGeometry.markScreenDirty();
+ m_borderGeometry.updateScreenPoints(*m_poly.map(), lineWidth);
+ geom = &m_borderGeometry;
+ }
+ m_poly.setWidth(geom->sourceBoundingBox().width());
+ m_poly.setHeight(geom->sourceBoundingBox().height());
+ m_poly.setPosition(1.0 * geom->firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5));
+ }
+ QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override
+ {
+ Q_UNUSED(data);
+
+ if (!m_rootNode || !oldNode) {
+ m_rootNode = new RootNode();
+ m_node = new MapPolygonNodeGL();
+ m_rootNode->appendChildNode(m_node);
+ m_polylinenode = new MapPolylineNodeOpenGLExtruded();
+ m_rootNode->appendChildNode(m_polylinenode);
+ m_rootNode->markDirty(QSGNode::DirtyNodeAdded);
+ if (oldNode)
+ delete oldNode;
+ } else {
+ m_rootNode = static_cast<RootNode *>(oldNode);
+ }
+
+ const QGeoMap *map = m_poly.map();
+ const QMatrix4x4 &combinedMatrix = map->geoProjection().qsgTransform();
+ const QDoubleVector3D &cameraCenter = map->geoProjection().centerMercator();
+
+ if (m_borderGeometry.isScreenDirty()) {
+ /* Do the border update first */
+ m_polylinenode->update(m_poly.m_border.color(),
+ float(m_poly.m_border.width()),
+ &m_borderGeometry,
+ combinedMatrix,
+ cameraCenter,
+ Qt::SquareCap,
+ true);
+ m_borderGeometry.setPreserveGeometry(false);
+ m_borderGeometry.markClean();
+ } else {
+ m_polylinenode->setSubtreeBlocked(true);
+ }
+ if (m_geometry.isScreenDirty()) {
+ m_node->update(m_poly.m_color,
+ &m_geometry,
+ combinedMatrix,
+ cameraCenter);
+ m_geometry.setPreserveGeometry(false);
+ m_geometry.markClean();
+ } else {
+ m_node->setSubtreeBlocked(true);
+ }
+
+ m_rootNode->setSubtreeBlocked(false);
+ return m_rootNode;
+ }
+ bool contains(const QPointF &point) const override
+ {
+ const qreal lineWidth = m_poly.m_border.width();
+ const QColor &lineColor = m_poly.m_border.color();
+ const QRectF &bounds = (lineColor.alpha() != 0 && lineWidth > 0) ? m_borderGeometry.sourceBoundingBox() : m_geometry.sourceBoundingBox();
+ if (bounds.contains(point)) {
+ QDeclarativeGeoMap *m = m_poly.quickMap();
+ if (m) {
+ const QGeoCoordinate crd = m->toCoordinate(m->mapFromItem(&m_poly, point));
+ return m_poly.m_geopoly.contains(crd) || m_borderGeometry.contains(m_poly.mapToItem(m_poly.quickMap(), point),
+ m_poly.border()->width(),
+ static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection()));
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ QGeoMapPolygonGeometryOpenGL m_geometry;
+ QGeoMapPolylineGeometryOpenGL m_borderGeometry;
+ RootNode *m_rootNode = nullptr;
+ MapPolygonNodeGL *m_node = nullptr;
+ MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QDECLARATIVEPOLYGONMAPITEM_P_P_H
diff --git a/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp b/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
index 10f3f0c3..7e484122 100644
--- a/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
+++ b/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
@@ -35,11 +35,16 @@
****************************************************************************/
#include "qdeclarativepolylinemapitem_p.h"
+#include "qdeclarativepolylinemapitem_p_p.h"
+#include "qdeclarativerectanglemapitem_p_p.h"
+#include "qdeclarativecirclemapitem_p_p.h"
#include "qlocationutils_p.h"
+#include "qdeclarativegeomapitemutils_p.h"
#include "error_messages_p.h"
#include "locationvaluetypehelper_p.h"
#include "qdoublevector2d_p.h"
#include <QtLocation/private/qgeomap_p.h>
+#include <QtPositioning/private/qwebmercator_p.h>
#include <QtCore/QScopedValueRollback>
#include <QtQml/QQmlInfo>
@@ -55,7 +60,9 @@
#include <QtPositioning/private/qclipperutils_p.h>
#include <QtPositioning/private/qgeopath_p.h>
+#include <QtQuick/private/qsgmaterialshader_p.h>
#include <array>
+#include <QtLocation/private/qgeomapparameter_p.h>
QT_BEGIN_NAMESPACE
@@ -125,7 +132,7 @@ static QList<QList<QDoubleVector2D> > clipLine(
const QList<QDoubleVector2D> &poly)
{
QList<QList<QDoubleVector2D> > res;
- if (poly.size() < 3 || l.size() < 2)
+ if (poly.size() < 2 || l.size() < 2)
return res;
// Step 1: build edges
@@ -355,11 +362,6 @@ void QDeclarativeMapLineProperties::setWidth(qreal width)
emit widthChanged(width_);
}
-struct Vertex
-{
- QVector2D position;
-};
-
QGeoMapPolylineGeometry::QGeoMapPolylineGeometry()
{
}
@@ -661,7 +663,7 @@ void QGeoMapPolylineGeometry::updateScreenPoints(const QGeoMap &map,
// Create the viewport rect in the same coordinate system
// as the actual points
QRectF viewport(0, 0, map.viewportWidth(), map.viewportHeight());
- viewport.adjust(-strokeWidth, -strokeWidth, strokeWidth, strokeWidth);
+ viewport.adjust(-strokeWidth, -strokeWidth, strokeWidth * 2, strokeWidth * 2);
viewport.translate(-1 * origin);
QVector<qreal> points;
@@ -719,7 +721,7 @@ void QGeoMapPolylineGeometry::updateScreenPoints(const QGeoMap &map,
}
screenBounds_ = bb;
- const QPointF strokeOffset = (adjustTranslation) ? QPointF(strokeWidth, strokeWidth) : QPointF();
+ const QPointF strokeOffset = (adjustTranslation) ? QPointF(strokeWidth, strokeWidth) * 0.5: QPointF();
this->translate( -1 * sourceBounds_.topLeft() + strokeOffset);
}
@@ -747,16 +749,199 @@ bool QGeoMapPolylineGeometry::contains(const QPointF &point) const
return false;
}
+void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoPolygon &poly)
+{
+ if (!sourceDirty_)
+ return;
+ QGeoPath p(poly.path());
+ if (poly.path().size() && poly.path().last() != poly.path().first())
+ p.addCoordinate(poly.path().first());
+ updateSourcePoints(map, p);
+}
+
+void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoPath &poly)
+{
+ if (!sourceDirty_)
+ return;
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection());
+
+ // build the actual path
+ // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints
+
+
+ QDoubleVector2D leftBoundWrapped;
+ // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0
+ QList<QDoubleVector2D> wrappedPath;
+ QDeclarativeGeoMapItemUtils::wrapPath(poly.path(), geoLeftBound_, p,
+ wrappedPath, &leftBoundWrapped);
+ const QGeoRectangle &boundingRectangle = poly.boundingGeoRectangle();
+ updateSourcePoints(p, wrappedPath, boundingRectangle);
+}
+
+void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoProjectionWebMercator &p,
+ const QList<QDoubleVector2D> &wrappedPath,
+ const QGeoRectangle &boundingRectangle) {
+ if (!sourceDirty_)
+ return;
+ // 1.1) do the same for the bbox
+ // Beware: vertical lines (or horizontal lines) might have an "empty" bbox. Check for that
+
+ QGeoCoordinate topLeft = boundingRectangle.topLeft();
+ QGeoCoordinate bottomRight = boundingRectangle.bottomRight();
+ const qreal epsilon = 0.000001;
+ if (qFuzzyCompare(topLeft.latitude(), bottomRight.latitude())) {
+ topLeft.setLatitude(qBound(-90.0, topLeft.latitude() + epsilon ,90.0));
+ bottomRight.setLatitude(qBound(-90.0, bottomRight.latitude() - epsilon ,90.0));
+ }
+ if (qFuzzyCompare(topLeft.longitude(), bottomRight.longitude())) {
+ topLeft.setLongitude(QLocationUtils::wrapLong(topLeft.longitude() - epsilon));
+ bottomRight.setLongitude(QLocationUtils::wrapLong(bottomRight.longitude() + epsilon));
+ }
+ QGeoPolygon bbox(QGeoRectangle(topLeft, bottomRight));
+ QList<QDoubleVector2D> wrappedBbox, wrappedBboxPlus1, wrappedBboxMinus1;
+ QDeclarativeGeoMapItemUtils::wrapPath(bbox.path(), bbox.boundingGeoRectangle().topLeft(), p,
+ wrappedBbox, wrappedBboxMinus1, wrappedBboxPlus1, &m_bboxLeftBoundWrapped);
+
+ m_screenVertices.clear();
+ for (const auto &v: qAsConst(wrappedPath)) m_screenVertices << v;
+
+ m_wrappedPolygons.resize(3);
+ m_wrappedPolygons[0].wrappedBboxes = wrappedBboxMinus1;
+ m_wrappedPolygons[1].wrappedBboxes = wrappedBbox;
+ m_wrappedPolygons[2].wrappedBboxes = wrappedBboxPlus1;
+ srcOrigin_ = geoLeftBound_;
+}
+
+void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoRectangle &rect)
+{
+ const QGeoPath path(QDeclarativeRectangleMapItemPrivateCPU::perimeter(rect));
+ updateSourcePoints(map, path);
+}
+
+void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoCircle &circle)
+{
+ if (!sourceDirty_)
+ return;
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection());
+
+ QDoubleVector2D leftBoundWrapped;
+ // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0
+ QList<QGeoCoordinate> path;
+ QGeoCoordinate leftBound;
+ QList<QDoubleVector2D> wrappedPath;
+ QDeclarativeCircleMapItemPrivateCPU::calculatePeripheralPoints(path, circle.center(), circle.radius(), QDeclarativeCircleMapItemPrivateCPU::CircleSamples, leftBound);
+ path << path.first();
+ geoLeftBound_ = leftBound;
+ QDeclarativeGeoMapItemUtils::wrapPath(path, leftBound, p, wrappedPath, &leftBoundWrapped);
+ const QGeoRectangle &boundingRectangle = circle.boundingGeoRectangle();
+ updateSourcePoints(p, wrappedPath, boundingRectangle);
+}
+
+void QGeoMapPolylineGeometryOpenGL::updateScreenPoints(const QGeoMap &map, qreal strokeWidth, bool /*adjustTranslation*/)
+{
+ if (map.viewportWidth() == 0 || map.viewportHeight() == 0) {
+ clear();
+ return;
+ }
+
+ // 1) identify which set to use: std, +1 or -1
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection());
+ const QDoubleVector2D leftBoundMercator = p.geoToMapProjection(srcOrigin_);
+ m_wrapOffset = p.projectionWrapFactor(leftBoundMercator) + 1; // +1 to get the offset into QLists
+
+ if (sourceDirty_) {
+ // 1.1) select geometry set
+ // This could theoretically be skipped for those polylines whose bbox is not even projectable.
+ // However, such optimization could only be introduced if not calculating bboxes lazily.
+ // Hence not doing it.
+// if (m_screenVertices.size() > 1)
+ m_dataChanged = true;
+ }
+
+ updateQuickGeometry(p, strokeWidth);
+}
+
+void QGeoMapPolylineGeometryOpenGL::updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth)
+{
+ // 2) clip bbox
+ // BBox handling -- this is related to the bounding box geometry
+ // that has to inevitably follow the old projection codepath
+ // As it needs to provide projected coordinates for QtQuick interaction.
+ // This could be futher optimized to be updated in a lazy fashion.
+ const QList<QDoubleVector2D> &wrappedBbox = m_wrappedPolygons.at(m_wrapOffset).wrappedBboxes;
+ QList<QList<QDoubleVector2D> > clippedBbox;
+ QDoubleVector2D bboxLeftBoundWrapped = m_bboxLeftBoundWrapped;
+ bboxLeftBoundWrapped.setX(bboxLeftBoundWrapped.x() + double(m_wrapOffset - 1));
+ QDeclarativeGeoMapItemUtils::clipPolygon(wrappedBbox, p, clippedBbox, &bboxLeftBoundWrapped, false);
+
+ // 3) project bbox
+ QPainterPath ppi;
+
+ if ( !clippedBbox.size() ||
+ clippedBbox.first().size() < 3) {
+ sourceBounds_ = screenBounds_ = QRectF();
+ firstPointOffset_ = QPointF();
+ screenOutline_ = ppi;
+ return;
+ }
+
+ QDeclarativeGeoMapItemUtils::projectBbox(clippedBbox.first(), p, ppi); // Using first because a clipped box should always result in one polygon
+ const QRectF brect = ppi.boundingRect();
+ firstPointOffset_ = QPointF(brect.topLeft());
+ sourceBounds_ = brect;
+ screenOutline_ = ppi;
+
+ // 4) Set Screen bbox
+ screenBounds_ = brect;
+ sourceBounds_.setX(0);
+ sourceBounds_.setY(0);
+ sourceBounds_.setWidth(brect.width() + strokeWidth);
+ sourceBounds_.setHeight(brect.height() + strokeWidth);
+}
+
+/*
+ * QDeclarativePolygonMapItem Private Implementations
+ */
+
+QDeclarativePolylineMapItemPrivate::~QDeclarativePolylineMapItemPrivate() {}
+
+
+QDeclarativePolylineMapItemPrivateCPU::~QDeclarativePolylineMapItemPrivateCPU() {}
+
+QDeclarativePolylineMapItemPrivateOpenGLLineStrip::~QDeclarativePolylineMapItemPrivateOpenGLLineStrip() {}
+
+QDeclarativePolylineMapItemPrivateOpenGLExtruded::~QDeclarativePolylineMapItemPrivateOpenGLExtruded() {}
+
+/*
+ * QDeclarativePolygonMapItem Implementation
+ */
+
+struct PolylineBackendSelector
+{
+ PolylineBackendSelector()
+ {
+ backend = (qgetenv("QTLOCATION_OPENGL_ITEMS").toInt()) ? QDeclarativePolylineMapItem::OpenGLExtruded : QDeclarativePolylineMapItem::Software;
+ }
+ QDeclarativePolylineMapItem::Backend backend = QDeclarativePolylineMapItem::Software;
+};
+
+Q_GLOBAL_STATIC(PolylineBackendSelector, mapPolylineBackendSelector)
+
QDeclarativePolylineMapItem::QDeclarativePolylineMapItem(QQuickItem *parent)
-: QDeclarativeGeoMapItemBase(parent), line_(this), dirtyMaterial_(true), updatingGeometry_(false)
+: QDeclarativeGeoMapItemBase(parent),
+ m_line(this),
+ m_dirtyMaterial(true),
+ m_updatingGeometry(false),
+ m_d(new QDeclarativePolylineMapItemPrivateCPU(*this))
{
m_itemType = QGeoMap::MapPolyline;
- geopath_ = QGeoPathEager();
+ m_geopath = QGeoPathEager();
setFlag(ItemHasContents, true);
- QObject::connect(&line_, SIGNAL(colorChanged(QColor)),
+ QObject::connect(&m_line, SIGNAL(colorChanged(QColor)),
this, SLOT(updateAfterLinePropertiesChanged()));
- QObject::connect(&line_, SIGNAL(widthChanged(qreal)),
+ QObject::connect(&m_line, SIGNAL(widthChanged(qreal)),
this, SLOT(updateAfterLinePropertiesChanged()));
+ setBackend(mapPolylineBackendSelector->backend);
}
QDeclarativePolylineMapItem::~QDeclarativePolylineMapItem()
@@ -768,9 +953,7 @@ QDeclarativePolylineMapItem::~QDeclarativePolylineMapItem()
*/
void QDeclarativePolylineMapItem::updateAfterLinePropertiesChanged()
{
- // mark dirty just in case we're a width change
- geometry_.markSourceDirty();
- polishAndUpdate();
+ m_d->onLinePropertiesChanged();
}
/*!
@@ -779,11 +962,8 @@ void QDeclarativePolylineMapItem::updateAfterLinePropertiesChanged()
void QDeclarativePolylineMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map)
{
QDeclarativeGeoMapItemBase::setMap(quickMap,map);
- if (map) {
- regenerateCache();
- geometry_.markSourceDirty();
- polishAndUpdate();
- }
+ if (map)
+ m_d->onMapSet();
}
/*!
@@ -795,7 +975,7 @@ void QDeclarativePolylineMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *
QJSValue QDeclarativePolylineMapItem::path() const
{
- return fromList(this, geopath_.path());
+ return fromList(this, m_geopath.path());
}
void QDeclarativePolylineMapItem::setPath(const QJSValue &value)
@@ -817,13 +997,11 @@ void QDeclarativePolylineMapItem::setPath(const QJSValue &value)
*/
void QDeclarativePolylineMapItem::setPath(const QGeoPath &path)
{
- if (geopath_.path() == path.path())
+ if (m_geopath.path() == path.path())
return;
- geopath_ = QGeoPathEager(path);
- regenerateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_geopath = QGeoPathEager(path);
+ m_d->onGeoGeometryChanged();
emit pathChanged();
}
@@ -832,14 +1010,12 @@ void QDeclarativePolylineMapItem::setPath(const QGeoPath &path)
*/
void QDeclarativePolylineMapItem::setPathFromGeoList(const QList<QGeoCoordinate> &path)
{
- if (geopath_.path() == path)
+ if (m_geopath.path() == path)
return;
- geopath_.setPath(path);
+ m_geopath.setPath(path);
- regenerateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryChanged();
emit pathChanged();
}
@@ -854,7 +1030,7 @@ void QDeclarativePolylineMapItem::setPathFromGeoList(const QList<QGeoCoordinate>
*/
int QDeclarativePolylineMapItem::pathLength() const
{
- return geopath_.path().length();
+ return m_geopath.path().length();
}
/*!
@@ -869,11 +1045,9 @@ void QDeclarativePolylineMapItem::addCoordinate(const QGeoCoordinate &coordinate
if (!coordinate.isValid())
return;
- geopath_.addCoordinate(coordinate);
+ m_geopath.addCoordinate(coordinate);
- updateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryUpdated();
emit pathChanged();
}
@@ -888,14 +1062,12 @@ void QDeclarativePolylineMapItem::addCoordinate(const QGeoCoordinate &coordinate
*/
void QDeclarativePolylineMapItem::insertCoordinate(int index, const QGeoCoordinate &coordinate)
{
- if (index < 0 || index > geopath_.path().length())
+ if (index < 0 || index > m_geopath.path().length())
return;
- geopath_.insertCoordinate(index, coordinate);
+ m_geopath.insertCoordinate(index, coordinate);
- regenerateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryChanged();
emit pathChanged();
}
@@ -911,14 +1083,12 @@ void QDeclarativePolylineMapItem::insertCoordinate(int index, const QGeoCoordina
*/
void QDeclarativePolylineMapItem::replaceCoordinate(int index, const QGeoCoordinate &coordinate)
{
- if (index < 0 || index >= geopath_.path().length())
+ if (index < 0 || index >= m_geopath.path().length())
return;
- geopath_.replaceCoordinate(index, coordinate);
+ m_geopath.replaceCoordinate(index, coordinate);
- regenerateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryChanged();
emit pathChanged();
}
@@ -933,10 +1103,10 @@ void QDeclarativePolylineMapItem::replaceCoordinate(int index, const QGeoCoordin
*/
QGeoCoordinate QDeclarativePolylineMapItem::coordinateAt(int index) const
{
- if (index < 0 || index >= geopath_.path().length())
+ if (index < 0 || index >= m_geopath.path().length())
return QGeoCoordinate();
- return geopath_.coordinateAt(index);
+ return m_geopath.coordinateAt(index);
}
/*!
@@ -948,7 +1118,7 @@ QGeoCoordinate QDeclarativePolylineMapItem::coordinateAt(int index) const
*/
bool QDeclarativePolylineMapItem::containsCoordinate(const QGeoCoordinate &coordinate)
{
- return geopath_.containsCoordinate(coordinate);
+ return m_geopath.containsCoordinate(coordinate);
}
/*!
@@ -963,13 +1133,12 @@ bool QDeclarativePolylineMapItem::containsCoordinate(const QGeoCoordinate &coord
*/
void QDeclarativePolylineMapItem::removeCoordinate(const QGeoCoordinate &coordinate)
{
- int length = geopath_.path().length();
- geopath_.removeCoordinate(coordinate);
- if (geopath_.path().length() == length)
+ int length = m_geopath.path().length();
+ m_geopath.removeCoordinate(coordinate);
+ if (m_geopath.path().length() == length)
return;
- regenerateCache();
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryChanged();
emit pathChanged();
}
@@ -986,14 +1155,12 @@ void QDeclarativePolylineMapItem::removeCoordinate(const QGeoCoordinate &coordin
*/
void QDeclarativePolylineMapItem::removeCoordinate(int index)
{
- if (index < 0 || index >= geopath_.path().length())
+ if (index < 0 || index >= m_geopath.path().length())
return;
- geopath_.removeCoordinate(index);
+ m_geopath.removeCoordinate(index);
- regenerateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryChanged();
emit pathChanged();
}
@@ -1013,7 +1180,47 @@ void QDeclarativePolylineMapItem::removeCoordinate(int index)
QDeclarativeMapLineProperties *QDeclarativePolylineMapItem::line()
{
- return &line_;
+ return &m_line;
+}
+
+/*!
+ \qmlproperty MapPolyline.Backend QtLocation::MapPolyline::backend
+
+ This property holds which backend is in use to render the map item.
+ Valid values are \b MapPolyline.Software and \b{MapPolyline.OpenGLLineStrip}
+ and \b{MapPolyline.OpenGLExtruded}.
+ The default value is \b{MapPolyline.Software}.
+
+ \note \b{The release of this API with Qt 5.15 is a Technology Preview}.
+ Ideally, as the OpenGL backends for map items mature, there will be
+ no more need to also offer the legacy software-projection backend.
+ So this property will likely disappear at some later point.
+ To select OpenGL-accelerated item backends without using this property,
+ it is also possible to set the environment variable \b QTLOCATION_OPENGL_ITEMS
+ to \b{1}.
+ Also note that all current OpenGL backends won't work as expected when enabling
+ layers on the individual item, or when running on OpenGL core profiles greater than 2.x.
+
+ \since 5.15
+*/
+QDeclarativePolylineMapItem::Backend QDeclarativePolylineMapItem::backend() const
+{
+ return m_backend;
+}
+
+void QDeclarativePolylineMapItem::setBackend(QDeclarativePolylineMapItem::Backend b)
+{
+ if (b == m_backend)
+ return;
+ m_backend = b;
+ QScopedPointer<QDeclarativePolylineMapItemPrivate> d((m_backend == Software)
+ ? static_cast<QDeclarativePolylineMapItemPrivate *>(new QDeclarativePolylineMapItemPrivateCPU(*this))
+ : ((m_backend == OpenGLExtruded)
+ ? static_cast<QDeclarativePolylineMapItemPrivate * >(new QDeclarativePolylineMapItemPrivateOpenGLExtruded(*this))
+ : static_cast<QDeclarativePolylineMapItemPrivate * >(new QDeclarativePolylineMapItemPrivateOpenGLLineStrip(*this))));
+ m_d.swap(d);
+ m_d->onGeoGeometryChanged();
+ emit backendChanged();
}
/*!
@@ -1021,7 +1228,7 @@ QDeclarativeMapLineProperties *QDeclarativePolylineMapItem::line()
*/
void QDeclarativePolylineMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
- if (!map() || !geopath_.isValid() || updatingGeometry_ || newGeometry.topLeft() == oldGeometry.topLeft()) {
+ if (newGeometry.topLeft() == oldGeometry.topLeft() || !map() || !m_geopath.isValid() || m_updatingGeometry) {
QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry);
return;
}
@@ -1035,10 +1242,8 @@ void QDeclarativePolylineMapItem::geometryChanged(const QRectF &newGeometry, con
if (offsetLati == 0.0 && offsetLongi == 0.0)
return;
- geopath_.translate(offsetLati, offsetLongi);
- regenerateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_geopath.translate(offsetLati, offsetLongi);
+ m_d->onGeoGeometryChanged();
emit pathChanged();
// Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested
@@ -1050,68 +1255,78 @@ void QDeclarativePolylineMapItem::geometryChanged(const QRectF &newGeometry, con
*/
void QDeclarativePolylineMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event)
{
- if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0)
+ if (event.mapSize.isEmpty())
return;
- geometry_.setPreserveGeometry(true, geometry_.geoLeftBound());
- markSourceDirtyAndUpdate();
+ m_d->afterViewportChanged();
}
/*!
\internal
*/
-void QDeclarativePolylineMapItem::regenerateCache()
+void QDeclarativePolylineMapItem::updatePolish()
{
if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
return;
- const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection());
- geopathProjected_.clear();
- geopathProjected_.reserve(geopath_.path().size());
- for (const QGeoCoordinate &c : geopath_.path())
- geopathProjected_ << p.geoToMapProjection(c);
+ m_d->updatePolish();
}
-/*!
- \internal
-*/
-void QDeclarativePolylineMapItem::updateCache()
+void QDeclarativePolylineMapItem::updateLineStyleParameter(QGeoMapParameter *p,
+ const char *propertyName,
+ bool update)
{
- if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
- return;
- const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection());
- geopathProjected_ << p.geoToMapProjection(geopath_.path().last());
+ static const QByteArrayList acceptedParameterTypes = QByteArrayList()
+ << QByteArrayLiteral("lineCap")
+ << QByteArrayLiteral("pen");
+ switch (acceptedParameterTypes.indexOf(QByteArray(propertyName))) {
+ case -1:
+ qWarning() << "Invalid property " << QLatin1String(propertyName) << " for parameter lineStyle";
+ break;
+ case 0: // lineCap
+ {
+ const QVariant lineCap = p->property("lineCap");
+ m_d->m_penCapStyle = lineCap.value<Qt::PenCapStyle>(); // if invalid, will return 0 == FlatCap
+ if (update)
+ markSourceDirtyAndUpdate();
+ break;
+ }
+ case 1: // penStyle
+ {
+ const QVariant penStyle = p->property("pen");
+ m_d->m_penStyle = penStyle.value<Qt::PenStyle>();
+ if (m_d->m_penStyle == Qt::NoPen)
+ m_d->m_penStyle = Qt::SolidLine;
+ if (update)
+ markSourceDirtyAndUpdate();
+ break;
+ }
+ }
}
-/*!
- \internal
-*/
-void QDeclarativePolylineMapItem::updatePolish()
+void QDeclarativePolylineMapItem::updateLineStyleParameter(QGeoMapParameter *p, const char *propertyName)
{
- if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
- return;
- if (geopath_.path().length() == 0) { // Possibly cleared
- geometry_.clear();
- setWidth(0);
- setHeight(0);
- return;
- }
-
- QScopedValueRollback<bool> rollback(updatingGeometry_);
- updatingGeometry_ = true;
-
- geometry_.updateSourcePoints(*map(), geopathProjected_, geopath_.boundingGeoRectangle().topLeft());
- geometry_.updateScreenPoints(*map(), line_.width());
-
- setWidth(geometry_.sourceBoundingBox().width() + 2 * line_.width());
- setHeight(geometry_.sourceBoundingBox().height() + 2 * line_.width());
+ updateLineStyleParameter(p, propertyName, true);
+}
- setPositionOnMap(geometry_.origin(), -1 * geometry_.sourceBoundingBox().topLeft() + QPointF(line_.width(), line_.width()));
+void QDeclarativePolylineMapItem::componentComplete()
+{
+ QQuickItem::componentComplete();
+ // Set up Dynamic Parameters
+ QList<QGeoMapParameter *> dynamicParameters = quickChildren<QGeoMapParameter>();
+ for (QGeoMapParameter *p : qAsConst(dynamicParameters)) {
+ if (p->type() == QLatin1String("lineStyle")) {
+ updateLineStyleParameter(p, "lineCap", false);
+ updateLineStyleParameter(p, "pen", false);
+ connect(p, &QGeoMapParameter::propertyUpdated,
+ this, static_cast<void (QDeclarativePolylineMapItem::*)(QGeoMapParameter *, const char *)>(&QDeclarativePolylineMapItem::updateLineStyleParameter));
+ markSourceDirtyAndUpdate();
+ }
+ }
}
void QDeclarativePolylineMapItem::markSourceDirtyAndUpdate()
{
- geometry_.markSourceDirty();
- polishAndUpdate();
+ m_d->markSourceDirtyAndUpdate();
}
/*!
@@ -1119,32 +1334,17 @@ void QDeclarativePolylineMapItem::markSourceDirtyAndUpdate()
*/
QSGNode *QDeclarativePolylineMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
{
- Q_UNUSED(data);
-
- MapPolylineNode *node = static_cast<MapPolylineNode *>(oldNode);
-
- if (!node) {
- node = new MapPolylineNode();
- }
-
- //TODO: update only material
- if (geometry_.isScreenDirty() || dirtyMaterial_ || !oldNode) {
- node->update(line_.color(), &geometry_);
- geometry_.setPreserveGeometry(false);
- geometry_.markClean();
- dirtyMaterial_ = false;
- }
- return node;
+ return m_d->updateMapItemPaintNode(oldNode, data);
}
bool QDeclarativePolylineMapItem::contains(const QPointF &point) const
{
- return geometry_.contains(point);
+ return m_d->contains(point);
}
const QGeoShape &QDeclarativePolylineMapItem::geoShape() const
{
- return geopath_;
+ return m_geopath;
}
void QDeclarativePolylineMapItem::setGeoShape(const QGeoShape &shape)
@@ -1254,4 +1454,425 @@ void MapPolylineNode::update(const QColor &fillColor,
}
}
+MapPolylineNodeOpenGLLineStrip::MapPolylineNodeOpenGLLineStrip()
+: geometry_(QSGGeometry::defaultAttributes_Point2D(), 0)
+{
+ geometry_.setDrawingMode(QSGGeometry::DrawLineStrip);
+ QSGGeometryNode::setMaterial(&fill_material_);
+ QSGGeometryNode::setGeometry(&geometry_);
+}
+
+MapPolylineNodeOpenGLLineStrip::~MapPolylineNodeOpenGLLineStrip()
+{
+
+}
+
+void MapPolylineNodeOpenGLLineStrip::update(const QColor &fillColor,
+ const qreal lineWidth,
+ const QGeoMapPolylineGeometryOpenGL *shape,
+ const QMatrix4x4 &geoProjection,
+ const QDoubleVector3D &center,
+ const Qt::PenCapStyle /*capStyle*/)
+{
+ if (shape->m_screenVertices.size() < 2) {
+ setSubtreeBlocked(true);
+ return;
+ } else {
+ setSubtreeBlocked(false);
+ }
+
+ QSGGeometry *fill = QSGGeometryNode::geometry();
+ if (shape->m_dataChanged) {
+ shape->allocateAndFillLineStrip(fill);
+ markDirty(DirtyGeometry);
+ shape->m_dataChanged = false;
+ }
+ fill->setLineWidth(lineWidth);
+ fill_material_.setLineWidth(lineWidth); // to make the material not compare equal if linewidth changes
+
+// if (fillColor != fill_material_.color())
+ {
+ fill_material_.setWrapOffset(shape->m_wrapOffset - 1);
+ fill_material_.setColor(fillColor);
+ fill_material_.setGeoProjection(geoProjection);
+ fill_material_.setCenter(center);
+ setMaterial(&fill_material_);
+ markDirty(DirtyMaterial);
+ }
+}
+
+MapPolylineShaderLineStrip::MapPolylineShaderLineStrip() : QSGMaterialShader(*new QSGMaterialShaderPrivate)
+{
+
+}
+
+void MapPolylineShaderLineStrip::updateState(const QSGMaterialShader::RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
+{
+ Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type());
+ MapPolylineMaterial *oldMaterial = static_cast<MapPolylineMaterial *>(oldEffect);
+ MapPolylineMaterial *newMaterial = static_cast<MapPolylineMaterial *>(newEffect);
+
+ const QColor &c = newMaterial->color();
+ const QMatrix4x4 &geoProjection = newMaterial->geoProjection();
+ const QDoubleVector3D &center = newMaterial->center();
+
+ QVector3D vecCenter, vecCenter_lowpart;
+ for (int i = 0; i < 3; i++)
+ QLocationUtils::split_double(center.get(i), &vecCenter[i], &vecCenter_lowpart[i]);
+
+ if (oldMaterial == nullptr || c != oldMaterial->color() || state.isOpacityDirty()) {
+ float opacity = state.opacity() * c.alphaF();
+ QVector4D v(c.redF() * opacity,
+ c.greenF() * opacity,
+ c.blueF() * opacity,
+ opacity);
+ program()->setUniformValue(m_color_id, v);
+ }
+
+ if (state.isMatrixDirty())
+ {
+ program()->setUniformValue(m_matrix_id, state.projectionMatrix());
+ }
+
+ program()->setUniformValue(m_mapProjection_id, geoProjection);
+
+ program()->setUniformValue(m_center_id, vecCenter);
+ program()->setUniformValue(m_center_lowpart_id, vecCenter_lowpart);
+ program()->setUniformValue(m_wrapOffset_id, float(newMaterial->wrapOffset()));
+}
+
+const char * const *MapPolylineShaderLineStrip::attributeNames() const
+{
+ static char const *const attr[] = { "vertex", nullptr };
+ return attr;
+}
+
+QSGMaterialShader *MapPolylineMaterial::createShader() const
+{
+ return new MapPolylineShaderLineStrip();
+}
+
+QSGMaterialType *MapPolylineMaterial::type() const
+{
+ static QSGMaterialType type;
+ return &type;
+}
+
+int MapPolylineMaterial::compare(const QSGMaterial *other) const
+{
+ const MapPolylineMaterial &o = *static_cast<const MapPolylineMaterial *>(other);
+ if (o.m_center == m_center && o.m_geoProjection == m_geoProjection && o.m_wrapOffset == m_wrapOffset && o.m_lineWidth == m_lineWidth)
+ return QSGFlatColorMaterial::compare(other);
+ return -1;
+}
+
+const QSGGeometry::AttributeSet &MapPolylineNodeOpenGLExtruded::attributesMapPolylineTriangulated()
+{
+ return MapPolylineEntry::attributes();
+}
+
+MapPolylineNodeOpenGLExtruded::MapPolylineNodeOpenGLExtruded()
+: m_geometryTriangulating(MapPolylineNodeOpenGLExtruded::attributesMapPolylineTriangulated(),
+ 0 /* vtx cnt */, 0 /* index cnt */, QSGGeometry::UnsignedIntType /* index type */)
+{
+ m_geometryTriangulating.setDrawingMode(QSGGeometry::DrawTriangles);
+ QSGGeometryNode::setMaterial(&fill_material_);
+ QSGGeometryNode::setGeometry(&m_geometryTriangulating);
+}
+
+MapPolylineNodeOpenGLExtruded::~MapPolylineNodeOpenGLExtruded()
+{
+
+}
+
+void QGeoMapPolylineGeometryOpenGL::allocateAndFillEntries(QSGGeometry *geom, bool closed) const
+{
+ // ToDo: add dirty flag.
+ const QVector<QDeclarativeGeoMapItemUtils::vec2> &v = m_screenVertices;
+ if (v.size() < 2) {
+ geom->allocate(0, 0);
+ return;
+ }
+ const int numSegments = (v.size() - 1);
+
+ const int numIndices = numSegments * 6; // six vertices per line segment
+ geom->allocate(numIndices);
+ MapPolylineNodeOpenGLExtruded::MapPolylineEntry *vertices =
+ static_cast<MapPolylineNodeOpenGLExtruded::MapPolylineEntry *>(geom->vertexData());
+
+ for (int i = 0; i < numSegments; ++i) {
+ MapPolylineNodeOpenGLExtruded::MapPolylineEntry e;
+ const QDeclarativeGeoMapItemUtils::vec2 &cur = v[i];
+ const QDeclarativeGeoMapItemUtils::vec2 &next = v[i+1];
+ e.triangletype = 1.0;
+ e.next = next;
+ e.prev = cur;
+ e.pos = cur;
+ e.direction = 1.0;
+ e.vertextype = -1.0;
+ vertices[i*6] = e;
+ e.direction = -1.0;
+ vertices[i*6+1] = e;
+ e.pos = next;
+ e.vertextype = 1.0;
+ vertices[i*6+2] = e;
+
+ // Second tri
+ e.triangletype = -1.0;
+ e.direction = -1.0;
+ vertices[i*6+3] = e;
+ e.direction = 1.0;
+ vertices[i*6+4] = e;
+ e.pos = cur;
+ e.vertextype = -1.0;
+ vertices[i*6+5] = e;
+
+ if (i != 0) {
+ vertices[i*6].prev = vertices[i*6+1].prev = vertices[i*6+5].prev = v[i-1];
+ } else {
+ if (closed) {
+ vertices[i*6].prev = vertices[i*6+1].prev = vertices[i*6+5].prev = v[numSegments - 1];
+ } else {
+ vertices[i*6].triangletype = vertices[i*6+1].triangletype = vertices[i*6+5].triangletype = 2.0;
+ }
+ }
+ if (i != numSegments - 1) {
+ vertices[i*6+2].next = vertices[i*6+3].next = vertices[i*6+4].next = v[i+2];
+ } else {
+ if (closed) {
+ vertices[i*6+2].next = vertices[i*6+3].next = vertices[i*6+4].next = v[1];
+ } else {
+ vertices[i*6+2].triangletype = vertices[i*6+3].triangletype = vertices[i*6+4].triangletype = 3.0;
+ }
+ }
+ }
+}
+
+void QGeoMapPolylineGeometryOpenGL::allocateAndFillLineStrip(QSGGeometry *geom) const
+{
+ const QVector<QDeclarativeGeoMapItemUtils::vec2> &vx = m_screenVertices;
+ geom->allocate(vx.size());
+
+ QSGGeometry::Point2D *pts = geom->vertexDataAsPoint2D();
+ for (int i = 0; i < vx.size(); ++i)
+ pts[i].set(vx[i].x, vx[i].y);
+}
+
+void MapPolylineNodeOpenGLExtruded::update(const QColor &fillColor,
+ const float lineWidth,
+ const QGeoMapPolylineGeometryOpenGL *shape,
+ const QMatrix4x4 geoProjection,
+ const QDoubleVector3D center,
+ const Qt::PenCapStyle capStyle,
+ bool closed)
+{
+ // shape->size() == number of triangles
+ if (shape->m_screenVertices.size() < 2
+ || lineWidth < 0.5 || fillColor.alpha() == 0) { // number of points
+ setSubtreeBlocked(true);
+ return;
+ } else {
+ setSubtreeBlocked(false);
+ }
+
+ QSGGeometry *fill = QSGGeometryNode::geometry();
+ if (shape->m_dataChanged || !fill->vertexCount()) { // fill->vertexCount for when node gets destroyed by MapItemBase bcoz of opacity, then recreated.
+ shape->allocateAndFillEntries(fill, closed);
+ markDirty(DirtyGeometry);
+ shape->m_dataChanged = false;
+ }
+
+ // Update this
+// if (fillColor != fill_material_.color())
+ {
+ fill_material_.setWrapOffset(shape->m_wrapOffset - 1);
+ fill_material_.setColor(fillColor);
+ fill_material_.setGeoProjection(geoProjection);
+ fill_material_.setCenter(center);
+ fill_material_.setLineWidth(lineWidth);
+ fill_material_.setMiter(capStyle != Qt::FlatCap);
+ setMaterial(&fill_material_);
+ markDirty(DirtyMaterial);
+ }
+}
+
+MapPolylineShaderExtruded::MapPolylineShaderExtruded() : QSGMaterialShader(*new QSGMaterialShaderPrivate)
+{
+
+}
+
+void MapPolylineShaderExtruded::updateState(const QSGMaterialShader::RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
+{
+ Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type());
+ MapPolylineMaterialExtruded *oldMaterial = static_cast<MapPolylineMaterialExtruded *>(oldEffect);
+ MapPolylineMaterialExtruded *newMaterial = static_cast<MapPolylineMaterialExtruded *>(newEffect);
+
+ const QColor &c = newMaterial->color();
+ const QMatrix4x4 &geoProjection = newMaterial->geoProjection();
+ const QDoubleVector3D &center = newMaterial->center();
+
+ QVector3D vecCenter, vecCenter_lowpart;
+ for (int i = 0; i < 3; i++)
+ QLocationUtils::split_double(center.get(i), &vecCenter[i], &vecCenter_lowpart[i]);
+
+ if (oldMaterial == nullptr || c != oldMaterial->color() || state.isOpacityDirty()) {
+ float opacity = state.opacity() * c.alphaF();
+ QVector4D v(c.redF() * opacity,
+ c.greenF() * opacity,
+ c.blueF() * opacity,
+ opacity);
+ program()->setUniformValue(m_color_id, v);
+ }
+
+ if (state.isMatrixDirty())
+ {
+ program()->setUniformValue(m_matrix_id, state.projectionMatrix());
+ }
+
+ // ToDo: dirty-flag all this
+ program()->setUniformValue(m_mapProjection_id, geoProjection);
+
+ program()->setUniformValue(m_center_id, vecCenter);
+ program()->setUniformValue(m_center_lowpart_id, vecCenter_lowpart);
+ program()->setUniformValue(m_miter_id, newMaterial->miter());
+ program()->setUniformValue(m_lineWidth_id, newMaterial->lineWidth());
+ program()->setUniformValue(m_wrapOffset_id, float(newMaterial->wrapOffset()));
+
+ const QRectF viewportRect = state.viewportRect();
+ const float aspect = float(viewportRect.width() / viewportRect.height());
+ program()->setUniformValue(m_aspect_id, aspect);
+}
+
+const char * const *MapPolylineShaderExtruded::attributeNames() const
+{
+ return MapPolylineNodeOpenGLExtruded::MapPolylineEntry::attributeNames();
+}
+
+QSGMaterialShader *MapPolylineMaterialExtruded::createShader() const
+{
+ return new MapPolylineShaderExtruded();
+}
+
+QSGMaterialType *MapPolylineMaterialExtruded::type() const
+{
+ static QSGMaterialType type;
+ return &type;
+}
+
+int MapPolylineMaterialExtruded::compare(const QSGMaterial *other) const
+{
+ const MapPolylineMaterialExtruded &o = *static_cast<const MapPolylineMaterialExtruded *>(other);
+ if (o.m_miter == m_miter)
+ return MapPolylineMaterial::compare(other);
+ return -1;
+}
+
+const char *MapPolylineShaderExtruded::vertexShaderMiteredSegments() const
+{
+ return
+ "attribute highp vec4 vertex;\n"
+ "attribute highp vec4 previous;\n"
+ "attribute highp vec4 next;\n"
+ "attribute lowp float direction;\n"
+ "attribute lowp float triangletype;\n"
+ "attribute lowp float vertextype;\n" // -1.0 if it is the "left" end of the segment, 1.0 if it is the "right" end.
+ "\n"
+ "uniform highp mat4 qt_Matrix;\n"
+ "uniform highp mat4 mapProjection;\n"
+ "uniform highp vec3 center;\n"
+ "uniform highp vec3 center_lowpart;\n"
+ "uniform lowp float lineWidth;\n"
+ "uniform lowp float aspect;\n"
+ "uniform lowp int miter;\n" // currently unused
+ "uniform lowp vec4 color;\n"
+ "uniform lowp float wrapOffset;\n"
+ "\n"
+ "varying vec4 primitivecolor;\n"
+ "\n"
+ " \n"
+ "vec4 wrapped(in vec4 v) { return vec4(v.x + wrapOffset, v.y, 0.0, 1.0); }\n"
+ "void main() {\n" // ln 22
+ " primitivecolor = color;\n"
+ " vec2 aspectVec = vec2(aspect, 1.0);\n"
+ " mat4 projViewModel = qt_Matrix * mapProjection;\n"
+ " vec4 cur = wrapped(vertex) - vec4(center, 0.0);\n"
+ " cur = cur - vec4(center_lowpart, 0.0);\n"
+ " vec4 prev = wrapped(previous) - vec4(center, 0.0);\n"
+ " prev = prev - vec4(center_lowpart, 0.0);\n"
+ " vec4 nex = wrapped(next) - vec4(center, 0.0);\n"
+ " nex = nex - vec4(center_lowpart, 0.0);\n"
+ "\n"
+ " vec4 centerProjected = projViewModel * vec4(center, 1.0);\n"
+ " vec4 previousProjected = projViewModel * prev;\n"
+ " vec4 currentProjected = projViewModel * cur;\n"
+ " vec4 nextProjected = projViewModel * nex;\n"
+ "\n"
+ " //get 2D screen space with W divide and aspect correction\n"
+ " vec2 currentScreen = (currentProjected.xy / currentProjected.w) * aspectVec;\n"
+ " vec2 previousScreen = (previousProjected.xy / previousProjected.w) * aspectVec;\n"
+ " vec2 nextScreen = (nextProjected.xy / nextProjected.w) * aspectVec;\n"
+ " float len = (lineWidth);\n"
+ " float orientation = direction;\n"
+ " bool clipped = false;\n"
+ " bool otherEndBelowFrustum = false;\n"
+ " //starting point uses (next - current)\n"
+ " vec2 dir = vec2(0.0);\n"
+ " if (vertextype < 0.0) {\n"
+ " dir = normalize(nextScreen - currentScreen);\n"
+ " if (nextProjected.z < 0.0) dir = -dir;\n"
+ " } else { \n"
+ " dir = normalize(currentScreen - previousScreen);\n"
+ " if (previousProjected.z < 0.0) dir = -dir;\n"
+ " }\n"
+ // first, clip current, and make sure currentProjected.z is > 0
+ " if (currentProjected.z < 0.0) {\n"
+ " if ((nextProjected.z > 0.0 && vertextype < 0.0) || (vertextype > 0.0 && previousProjected.z > 0.0)) {\n"
+ " dir = -dir;\n"
+ " clipped = true;\n"
+ " if (vertextype < 0.0 && nextProjected.y / nextProjected.w < -1.0) otherEndBelowFrustum = true;\n"
+ " else if (vertextype > 0.0 && previousProjected.y / previousProjected.w < -1.0) otherEndBelowFrustum = true;\n"
+ " } else {\n"
+ " primitivecolor = vec4(0.0,0.0,0.0,0.0);\n"
+ " gl_Position = vec4(-10000000.0, -1000000000.0, -1000000000.0, 1);\n" // get the vertex out of the way if the segment is fully invisible
+ " return;\n"
+ " }\n"
+ " } else if (triangletype < 2.0) {\n" // vertex in the view, try to miter
+ " //get directions from (C - B) and (B - A)\n"
+ " vec2 dirA = normalize((currentScreen - previousScreen));\n"
+ " if (previousProjected.z < 0.0) dirA = -dirA;\n"
+ " vec2 dirB = normalize((nextScreen - currentScreen));\n"
+ " //now compute the miter join normal and length\n"
+ " if (nextProjected.z < 0.0) dirB = -dirB;\n"
+ " vec2 tangent = normalize(dirA + dirB);\n"
+ " vec2 perp = vec2(-dirA.y, dirA.x);\n"
+ " vec2 vmiter = vec2(-tangent.y, tangent.x);\n"
+ " len = lineWidth / dot(vmiter, perp);\n"
+ // The following is an attempt to have a segment-length based miter threshold.
+ // A mediocre workaround until better mitering will be added.
+ " float lenTreshold = clamp( min(length((currentProjected.xy - previousProjected.xy) / aspectVec),"
+ " length((nextProjected.xy - currentProjected.xy) / aspectVec)), 3.0, 6.0 ) * 0.5;\n"
+ " if (len < lineWidth * lenTreshold && len > -lineWidth * lenTreshold \n"
+ " ) {\n"
+ " dir = tangent;\n"
+ " } else {\n"
+ " len = lineWidth;\n"
+ " }\n"
+ " }\n"
+ " vec4 offset;\n"
+ " if (!clipped) {\n"
+ " vec2 normal = normalize(vec2(-dir.y, dir.x));\n"
+ " normal *= len;\n" // fracZL apparently was needed before the (-2.0 / qt_Matrix[1][1]) factor was introduced
+ " normal /= aspectVec;\n" // straighten the normal up again
+ " float scaleFactor = currentProjected.w / centerProjected.w;\n"
+ " offset = vec4(normal * orientation * scaleFactor * (centerProjected.w / (-2.0 / qt_Matrix[1][1])), 0.0, 0.0);\n" // ToDo: figure out why (-2.0 / qt_Matrix[1][1]), that is empirically what works
+ " gl_Position = currentProjected + offset;\n"
+ " } else {\n"
+ " if (otherEndBelowFrustum) offset = vec4((dir * 1.0) / aspectVec, 0.0, 0.0);\n" // the if is necessary otherwise it seems the direction vector still flips in some obscure cases.
+ " else offset = vec4((dir * 500000000000.0) / aspectVec, 0.0, 0.0);\n" // Hack alert: just 1 triangle, long enough to look like a rectangle.
+ " if (vertextype < 0.0) gl_Position = nextProjected - offset; else gl_Position = previousProjected + offset;\n"
+ " }\n"
+ "}\n";
+}
+
QT_END_NAMESPACE
+
diff --git a/src/location/declarativemaps/qdeclarativepolylinemapitem_p.h b/src/location/declarativemaps/qdeclarativepolylinemapitem_p.h
index 3aa0f96b..9cd20ea5 100644
--- a/src/location/declarativemaps/qdeclarativepolylinemapitem_p.h
+++ b/src/location/declarativemaps/qdeclarativepolylinemapitem_p.h
@@ -59,8 +59,6 @@
QT_BEGIN_NAMESPACE
-class MapPolylineNode;
-
class Q_LOCATION_PRIVATE_EXPORT QDeclarativeMapLineProperties : public QObject
{
Q_OBJECT
@@ -86,53 +84,23 @@ private:
QColor color_;
};
-class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometry : public QGeoMapItemGeometry
-{
-public:
- QGeoMapPolylineGeometry();
-
- void updateSourcePoints(const QGeoMap &map,
- const QList<QDoubleVector2D> &path,
- const QGeoCoordinate geoLeftBound);
-
- void updateScreenPoints(const QGeoMap &map,
- qreal strokeWidth,
- bool adjustTranslation = true);
-
- void clearSource();
-
- bool contains(const QPointF &point) const override;
-
- QList<QList<QDoubleVector2D> > clipPath(const QGeoMap &map,
- const QList<QDoubleVector2D> &path,
- QDoubleVector2D &leftBoundWrapped);
-
- void pathToScreen(const QGeoMap &map,
- const QList<QList<QDoubleVector2D> > &clippedPaths,
- const QDoubleVector2D &leftBoundWrapped);
-
-public:
- QVector<qreal> srcPoints_;
- QVector<QPainterPath::ElementType> srcPointTypes_;
-
-#ifdef QT_LOCATION_DEBUG
- QList<QDoubleVector2D> m_wrappedPath;
- QList<QList<QDoubleVector2D>> m_clippedPaths;
-#endif
-
- friend class QDeclarativeCircleMapItem;
- friend class QDeclarativePolygonMapItem;
- friend class QDeclarativeRectangleMapItem;
-};
-
+class QDeclarativePolylineMapItemPrivate;
class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItem : public QDeclarativeGeoMapItemBase
{
Q_OBJECT
+ Q_ENUMS(Backend)
Q_PROPERTY(QJSValue path READ path WRITE setPath NOTIFY pathChanged)
Q_PROPERTY(QDeclarativeMapLineProperties *line READ line CONSTANT)
+ Q_PROPERTY(Backend backend READ backend WRITE setBackend NOTIFY backendChanged REVISION 15)
public:
+ enum Backend {
+ Software = 0,
+ OpenGLLineStrip = 1,
+ OpenGLExtruded = 2,
+ };
+
explicit QDeclarativePolylineMapItem(QQuickItem *parent = 0);
~QDeclarativePolylineMapItem();
@@ -159,70 +127,42 @@ public:
QDeclarativeMapLineProperties *line();
+ Backend backend() const;
+ void setBackend(Backend b);
+
Q_SIGNALS:
void pathChanged();
-
-protected:
- void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
- void setPathFromGeoList(const QList<QGeoCoordinate> &path);
- void updatePolish() override;
+ void backendChanged();
protected Q_SLOTS:
void markSourceDirtyAndUpdate();
void updateAfterLinePropertiesChanged();
virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) override;
-private:
- void regenerateCache();
- void updateCache();
+protected:
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ void setPathFromGeoList(const QList<QGeoCoordinate> &path);
+ void updatePolish() override;
+ void componentComplete() override;
+ void updateLineStyleParameter(QGeoMapParameter *p, const char *propertyName);
+ void updateLineStyleParameter(QGeoMapParameter *p, const char *propertyName, bool update);
#ifdef QT_LOCATION_DEBUG
public:
#endif
- QGeoPath geopath_;
- QList<QDoubleVector2D> geopathProjected_;
- QDeclarativeMapLineProperties line_;
- QColor color_;
- bool dirtyMaterial_;
- QGeoMapPolylineGeometry geometry_;
- bool updatingGeometry_;
-};
+ QGeoPath m_geopath;
+ QDeclarativeMapLineProperties m_line;
-//////////////////////////////////////////////////////////////////////
-
-class Q_LOCATION_PRIVATE_EXPORT VisibleNode
-{
-public:
- VisibleNode();
- virtual ~VisibleNode();
+ Backend m_backend = Software;
+ bool m_dirtyMaterial;
+ bool m_updatingGeometry;
- bool subtreeBlocked() const;
- void setSubtreeBlocked(bool blocked);
- bool visible() const;
- void setVisible(bool visible);
+ QScopedPointer<QDeclarativePolylineMapItemPrivate> m_d;
- bool m_blocked : 1;
- bool m_visible : 1;
-};
-
-class Q_LOCATION_PRIVATE_EXPORT MapItemGeometryNode : public QSGGeometryNode, public VisibleNode
-{
-public:
- ~MapItemGeometryNode() override;
- bool isSubtreeBlocked() const override;
-};
-
-class Q_LOCATION_PRIVATE_EXPORT MapPolylineNode : public MapItemGeometryNode
-{
-public:
- MapPolylineNode();
- ~MapPolylineNode() override;
-
- void update(const QColor &fillColor, const QGeoMapItemGeometry *shape);
-
-private:
- QSGFlatColorMaterial fill_material_;
- QSGGeometry geometry_;
+ friend class QDeclarativePolylineMapItemPrivate;
+ friend class QDeclarativePolylineMapItemPrivateCPU;
+ friend class QDeclarativePolylineMapItemPrivateOpenGLLineStrip;
+ friend class QDeclarativePolylineMapItemPrivateOpenGLExtruded;
};
QT_END_NAMESPACE
diff --git a/src/location/declarativemaps/qdeclarativepolylinemapitem_p_p.h b/src/location/declarativemaps/qdeclarativepolylinemapitem_p_p.h
new file mode 100644
index 00000000..d37f77ad
--- /dev/null
+++ b/src/location/declarativemaps/qdeclarativepolylinemapitem_p_p.h
@@ -0,0 +1,823 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Paolo Angelelli <paolo.angelelli@gmail.com>
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtLocation module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVEPOLYLINEMAPITEM_P_P_H
+#define QDECLARATIVEPOLYLINEMAPITEM_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtLocation/private/qlocationglobal_p.h>
+#include <QtLocation/private/qdeclarativepolylinemapitem_p.h>
+#include <QtLocation/private/qdeclarativegeomapitemutils_p.h>
+#include <QtLocation/private/qdeclarativepolylinemapitem_p.h>
+#include <QtLocation/private/qgeomapitemgeometry_p.h>
+#include <QSGGeometryNode>
+#include <QSGFlatColorMaterial>
+#include <QtPositioning/QGeoPath>
+#include <QtPositioning/QGeoPolygon>
+#include <QtPositioning/QGeoRectangle>
+#include <QtPositioning/QGeoCircle>
+#include <QtPositioning/private/qdoublevector2d_p.h>
+#include <QtCore/QScopedValueRollback>
+
+QT_BEGIN_NAMESPACE
+
+class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometry : public QGeoMapItemGeometry
+{
+public:
+ QGeoMapPolylineGeometry();
+
+ void updateSourcePoints(const QGeoMap &map,
+ const QList<QDoubleVector2D> &path,
+ const QGeoCoordinate geoLeftBound);
+
+ void updateScreenPoints(const QGeoMap &map,
+ qreal strokeWidth,
+ bool adjustTranslation = true);
+
+ void clearSource();
+
+ bool contains(const QPointF &point) const override;
+
+ QList<QList<QDoubleVector2D> > clipPath(const QGeoMap &map,
+ const QList<QDoubleVector2D> &path,
+ QDoubleVector2D &leftBoundWrapped);
+
+ void pathToScreen(const QGeoMap &map,
+ const QList<QList<QDoubleVector2D> > &clippedPaths,
+ const QDoubleVector2D &leftBoundWrapped);
+
+public:
+ QVector<qreal> srcPoints_;
+ QVector<QPainterPath::ElementType> srcPointTypes_;
+
+#ifdef QT_LOCATION_DEBUG
+ QList<QDoubleVector2D> m_wrappedPath;
+ QList<QList<QDoubleVector2D>> m_clippedPaths;
+#endif
+
+ friend class QDeclarativeCircleMapItem;
+ friend class QDeclarativePolygonMapItem;
+ friend class QDeclarativeRectangleMapItem;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT VisibleNode
+{
+public:
+ VisibleNode();
+ virtual ~VisibleNode();
+
+ bool subtreeBlocked() const;
+ void setSubtreeBlocked(bool blocked);
+ bool visible() const;
+ void setVisible(bool visible);
+
+ bool m_blocked : 1;
+ bool m_visible : 1;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT MapItemGeometryNode : public QSGGeometryNode, public VisibleNode
+{
+public:
+ ~MapItemGeometryNode() override;
+ bool isSubtreeBlocked() const override;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT MapPolylineMaterial : public QSGFlatColorMaterial
+{
+public:
+ MapPolylineMaterial()
+ : QSGFlatColorMaterial()
+ {
+ // Passing RequiresFullMatrix is essential in order to prevent the
+ // batch renderer from baking in simple, translate-only transforms into
+ // the vertex data. The shader will rely on the fact that
+ // vertexCoord.xy is the Shape-space coordinate and so no modifications
+ // are welcome.
+ setFlag(Blending | RequiresFullMatrix | CustomCompileStep);
+ }
+
+ QSGMaterialShader *createShader() const override;
+
+ void setGeoProjection(const QMatrix4x4 &p)
+ {
+ m_geoProjection = p;
+ }
+
+ QMatrix4x4 geoProjection() const
+ {
+ return m_geoProjection;
+ }
+
+ void setCenter(const QDoubleVector3D &c)
+ {
+ m_center = c;
+ }
+
+ QDoubleVector3D center() const
+ {
+ return m_center;
+ }
+
+ void setColor(const QColor &color)
+ {
+ QSGFlatColorMaterial::setColor(color);
+ setFlag(Blending, true); // ToDo: Needed only temporarily, can be removed after debugging
+ }
+
+ int wrapOffset() const
+ {
+ return m_wrapOffset;
+ }
+
+ void setWrapOffset(int wrapOffset)
+ {
+ m_wrapOffset = wrapOffset;
+ }
+
+ void setLineWidth(const float lw)
+ {
+ m_lineWidth = lw;
+ }
+
+ float lineWidth() const
+ {
+ return m_lineWidth;
+ }
+
+ QSGMaterialType *type() const override;
+ int compare(const QSGMaterial *other) const override;
+
+protected:
+ QMatrix4x4 m_geoProjection;
+ QDoubleVector3D m_center;
+ int m_wrapOffset = 0;
+ float m_lineWidth = 1.0;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT MapPolylineNode : public MapItemGeometryNode
+{
+public:
+ MapPolylineNode();
+ ~MapPolylineNode() override;
+
+ void update(const QColor &fillColor, const QGeoMapItemGeometry *shape);
+
+protected:
+ QSGFlatColorMaterial fill_material_;
+ QSGGeometry geometry_;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometryOpenGL : public QGeoMapItemGeometry
+{
+public:
+ typedef struct {
+ QList<QDoubleVector2D> wrappedBboxes;
+ } WrappedPolyline;
+
+ QGeoMapPolylineGeometryOpenGL() {}
+
+ void updateSourcePoints(const QGeoMap &map,
+ const QGeoPolygon &poly);
+
+ void updateSourcePoints(const QGeoMap &map,
+ const QGeoPath &poly);
+
+ void updateSourcePoints(const QGeoProjectionWebMercator &p,
+ const QList<QDoubleVector2D> &wrappedPath,
+ const QGeoRectangle &boundingRectangle);
+
+ void updateSourcePoints(const QGeoMap &map,
+ const QGeoRectangle &rect);
+
+ void updateSourcePoints(const QGeoMap &map,
+ const QGeoCircle &circle);
+
+ void updateScreenPoints(const QGeoMap &map,
+ qreal strokeWidth,
+ bool adjustTranslation = true);
+
+ void updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth = 0.0);
+
+ void allocateAndFillEntries(QSGGeometry *geom, bool closed = false) const;
+ void allocateAndFillLineStrip(QSGGeometry *geom) const;
+
+ bool contains(const QPointF &point) const override
+ {
+ Q_UNUSED(point)
+ return false;
+ }
+
+ static double distanceTo(const QDoubleVector2D &a, const QDoubleVector2D &b, const QDoubleVector2D &p)
+ {
+ double u = ((p.x() - a.x()) * (b.x() - a.x()) + (p.y() - a.y()) * (b.y() - a.y()) ) / (b - a).lengthSquared();
+ QDoubleVector2D intersection(a.x() + u * (b.x() - a.x()) , a.y() + u * (b.y() - a.y()) );
+
+ QDoubleVector2D candidate = ( (p-a).length() < (p-b).length() ) ? a : b;
+
+ if (u > 0 && u < 1
+ && (p-intersection).length() < (p-candidate).length() ) // And it falls in the segment
+ candidate = intersection;
+
+ return qAbs((candidate - p).length());
+ }
+ // Note: this is also slightly incorrect on joins and in the beginning/end of the line
+ bool contains(const QPointF &point, qreal lineWidth, const QGeoProjectionWebMercator &p) const
+ {
+ const double lineHalfWidth = lineWidth * 0.5;
+ const QDoubleVector2D pt(point);
+ QDoubleVector2D a;
+ if (m_screenVertices.size()) a = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(m_screenVertices.first().toDoubleVector2D()));
+ QDoubleVector2D b;
+ for (int i = 1; i < m_screenVertices.size(); ++i)
+ {
+ if (!a.isFinite()) {
+ a = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(m_screenVertices.at(i).toDoubleVector2D()));
+ continue;
+ }
+
+ b = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(m_screenVertices.at(i).toDoubleVector2D()));
+ if (!b.isFinite()) {
+ a = b;
+ continue;
+ }
+
+ if (b == a)
+ continue;
+
+ // Heavily simplifying it here: if a point is not projectable, skip the segment.
+ // For a correct solution, the segment should be clipped instead.
+ if (distanceTo(a, b, pt) <= lineHalfWidth)
+ return true;
+
+ a = b;
+ }
+ return false;
+ }
+
+public:
+ QVector<QDeclarativeGeoMapItemUtils::vec2> m_screenVertices;
+ QDoubleVector2D m_bboxLeftBoundWrapped;
+ QVector<WrappedPolyline> m_wrappedPolygons;
+ int m_wrapOffset;
+
+ friend class QDeclarativeCircleMapItem;
+ friend class QDeclarativePolygonMapItem;
+ friend class QDeclarativeRectangleMapItem;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT MapPolylineShaderLineStrip : public QSGMaterialShader
+{
+public:
+ MapPolylineShaderLineStrip();
+
+ const char *vertexShader() const override {
+ return
+ "attribute highp vec4 vertex; \n"
+ "uniform highp mat4 qt_Matrix; \n"
+ "uniform highp mat4 mapProjection; \n"
+ "uniform highp vec3 center; \n"
+ "uniform highp vec3 center_lowpart; \n"
+ "uniform lowp float wrapOffset; \n"
+ "vec4 wrapped(in vec4 v) { return vec4(v.x + wrapOffset, v.y, 0.0, 1.0); }\n"
+ "void main() { \n"
+ " vec4 vtx = wrapped(vertex) - vec4(center, 0.0); \n"
+ " vtx = vtx - vec4(center_lowpart, 0.0); \n"
+ " gl_Position = qt_Matrix * mapProjection * vtx; \n"
+ "}";
+ }
+
+ const char *fragmentShader() const {
+ return
+ "uniform lowp vec4 color; \n"
+ "void main() { \n"
+ " gl_FragColor = color; \n"
+ "}";
+ }
+
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+ char const *const *attributeNames() const override;
+
+protected:
+ void initialize() override
+ {
+ m_matrix_id = program()->uniformLocation("qt_Matrix");
+ m_color_id = program()->uniformLocation("color");
+ m_mapProjection_id = program()->uniformLocation("mapProjection");
+ m_center_id = program()->uniformLocation("center");
+ m_center_lowpart_id = program()->uniformLocation("center_lowpart");
+ m_wrapOffset_id = program()->uniformLocation("wrapOffset");
+ }
+ int m_center_id;
+ int m_center_lowpart_id;
+ int m_mapProjection_id;
+ int m_matrix_id;
+ int m_color_id;
+ int m_wrapOffset_id;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT MapPolylineShaderExtruded : public QSGMaterialShader
+{
+public:
+ MapPolylineShaderExtruded();
+
+ // Heavily adapted from https://github.com/mattdesl/webgl-lines/blob/master/projected/vert.glsl,
+ // that is (c) Matt DesLauriers, and released under the MIT license.
+ const char *vertexShaderMiteredSegments() const;
+
+ const char *vertexShader() const override
+ {
+ return vertexShaderMiteredSegments();
+ }
+
+ const char *fragmentShader() const override
+ {
+ return
+ "varying vec4 primitivecolor; \n"
+ "void main() { \n"
+ " gl_FragColor = primitivecolor; \n"
+ "}";
+ }
+
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+ char const *const *attributeNames() const override;
+
+protected:
+ void initialize() override
+ {
+ m_matrix_id = program()->uniformLocation("qt_Matrix");
+ m_color_id = program()->uniformLocation("color");
+ m_mapProjection_id = program()->uniformLocation("mapProjection");
+ m_center_id = program()->uniformLocation("center");
+ m_center_lowpart_id = program()->uniformLocation("center_lowpart");
+ m_lineWidth_id = program()->uniformLocation("lineWidth");
+ m_aspect_id = program()->uniformLocation("aspect");
+ m_miter_id = program()->uniformLocation("miter");
+ m_wrapOffset_id = program()->uniformLocation("wrapOffset");
+ }
+ int m_center_id;
+ int m_center_lowpart_id;
+ int m_mapProjection_id;
+ int m_matrix_id;
+ int m_color_id;
+ int m_lineWidth_id;
+ int m_aspect_id;
+ int m_miter_id;
+ int m_wrapOffset_id;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT MapPolylineNodeOpenGLLineStrip : public MapItemGeometryNode
+{
+public:
+ MapPolylineNodeOpenGLLineStrip();
+ ~MapPolylineNodeOpenGLLineStrip() override;
+
+ void update(const QColor &fillColor,
+ const qreal lineWidth,
+ const QGeoMapPolylineGeometryOpenGL *shape,
+ const QMatrix4x4 &geoProjection,
+ const QDoubleVector3D &center,
+ const Qt::PenCapStyle capStyle = Qt::SquareCap);
+
+protected:
+ MapPolylineMaterial fill_material_;
+ QSGGeometry geometry_;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT MapPolylineMaterialExtruded : public MapPolylineMaterial
+{
+public:
+ MapPolylineMaterialExtruded() : MapPolylineMaterial()
+ {
+
+ }
+ QSGMaterialShader *createShader() const override;
+
+ void setMiter(const int m)
+ {
+ m_miter = m;
+ }
+
+ int miter() const
+ {
+ return m_miter;
+ }
+
+ QSGMaterialType *type() const override;
+ int compare(const QSGMaterial *other) const override;
+
+ int m_miter = 0;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT MapPolylineNodeOpenGLExtruded : public MapItemGeometryNode
+{
+public:
+
+ typedef struct {
+ QDeclarativeGeoMapItemUtils::vec2 pos;
+ QDeclarativeGeoMapItemUtils::vec2 prev;
+ QDeclarativeGeoMapItemUtils::vec2 next;
+ float direction;
+ float triangletype; // es2 does not support int attribs
+ float vertextype;
+
+ static const char * const *attributeNames()
+ {
+ static char const *const attr[] = { "vertex", "previous", "next", "direction", "triangletype", "vertextype", nullptr };
+ return attr;
+ }
+ static const QSGGeometry::AttributeSet &attributes()
+ {
+ static const QSGGeometry::Attribute dataTri[] = {
+ QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute) // pos
+ ,QSGGeometry::Attribute::createWithAttributeType(1, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute) // next
+ ,QSGGeometry::Attribute::createWithAttributeType(2, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute) // previous
+ ,QSGGeometry::Attribute::createWithAttributeType(3, 1, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute) // direction
+ ,QSGGeometry::Attribute::createWithAttributeType(4, 1, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute) // triangletype
+ ,QSGGeometry::Attribute::createWithAttributeType(5, 1, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute) // vertextype
+ };
+ static const QSGGeometry::AttributeSet attrsTri = { 6, sizeof(MapPolylineNodeOpenGLExtruded::MapPolylineEntry), dataTri };
+ return attrsTri;
+ }
+ } MapPolylineEntry;
+
+ MapPolylineNodeOpenGLExtruded();
+ ~MapPolylineNodeOpenGLExtruded() override;
+
+ void update(const QColor &fillColor,
+ const float lineWidth,
+ const QGeoMapPolylineGeometryOpenGL *shape,
+ const QMatrix4x4 geoProjection,
+ const QDoubleVector3D center,
+ const Qt::PenCapStyle capStyle = Qt::FlatCap,
+ bool closed = false);
+
+ static const QSGGeometry::AttributeSet &attributesMapPolylineTriangulated();
+
+protected:
+ MapPolylineMaterialExtruded fill_material_;
+ QSGGeometry m_geometryTriangulating;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivate
+{
+public:
+ QDeclarativePolylineMapItemPrivate(QDeclarativePolylineMapItem &poly) : m_poly(poly)
+ {
+
+ }
+ QDeclarativePolylineMapItemPrivate(QDeclarativePolylineMapItemPrivate &other) : m_poly(other.m_poly)
+ {
+ }
+
+ virtual ~QDeclarativePolylineMapItemPrivate();
+ virtual void markSourceDirtyAndUpdate() = 0;
+ virtual void onMapSet() = 0;
+ virtual void onLinePropertiesChanged() = 0;
+ virtual void onGeoGeometryChanged() = 0;
+ virtual void onGeoGeometryUpdated() = 0;
+ virtual void onItemGeometryChanged() = 0;
+ virtual void updatePolish() = 0;
+ virtual void afterViewportChanged() = 0;
+ virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) = 0;
+ virtual bool contains(const QPointF &point) const = 0;
+
+ QDeclarativePolylineMapItem &m_poly;
+ Qt::PenStyle m_penStyle = Qt::SolidLine;
+ Qt::PenCapStyle m_penCapStyle = Qt::SquareCap;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivateCPU: public QDeclarativePolylineMapItemPrivate
+{
+public:
+ QDeclarativePolylineMapItemPrivateCPU(QDeclarativePolylineMapItem &poly) : QDeclarativePolylineMapItemPrivate(poly)
+ {
+ }
+
+ QDeclarativePolylineMapItemPrivateCPU(QDeclarativePolylineMapItemPrivate &other)
+ : QDeclarativePolylineMapItemPrivate(other)
+ {
+ }
+
+ ~QDeclarativePolylineMapItemPrivateCPU() override;
+ void onLinePropertiesChanged() override
+ {
+ // mark dirty just in case we're a width change
+ markSourceDirtyAndUpdate();
+ }
+ void markSourceDirtyAndUpdate() override
+ {
+ m_geometry.markSourceDirty();
+ m_poly.polishAndUpdate();
+ }
+ void regenerateCache()
+ {
+ if (!m_poly.map() || m_poly.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
+ return;
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection());
+ m_geopathProjected.clear();
+ m_geopathProjected.reserve(m_poly.m_geopath.size());
+ for (const QGeoCoordinate &c : m_poly.m_geopath.path())
+ m_geopathProjected << p.geoToMapProjection(c);
+ }
+ void updateCache()
+ {
+ if (!m_poly.map() || m_poly.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
+ return;
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection());
+ m_geopathProjected << p.geoToMapProjection(m_poly.m_geopath.path().last());
+ }
+ void preserveGeometry()
+ {
+ m_geometry.setPreserveGeometry(true, m_poly.m_geopath.boundingGeoRectangle().topLeft());
+ }
+ void afterViewportChanged() override
+ {
+ // preserveGeometry is cleared in updateMapItemPaintNode
+ preserveGeometry();
+ markSourceDirtyAndUpdate();
+ }
+ void onMapSet() override
+ {
+ regenerateCache();
+ markSourceDirtyAndUpdate();
+ }
+ void onGeoGeometryChanged() override
+ {
+ regenerateCache();
+ preserveGeometry();
+ markSourceDirtyAndUpdate();
+ }
+ void onGeoGeometryUpdated() override
+ {
+ updateCache();
+ preserveGeometry();
+ markSourceDirtyAndUpdate();
+ }
+ void onItemGeometryChanged() override
+ {
+ onGeoGeometryChanged();
+ }
+ void updatePolish() override
+ {
+ if (m_poly.m_geopath.path().length() < 2) { // Possibly cleared
+ m_geometry.clear();
+ m_poly.setWidth(0);
+ m_poly.setHeight(0);
+ return;
+ }
+ QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry);
+ m_poly.m_updatingGeometry = true;
+
+ const QGeoMap *map = m_poly.map();
+ const qreal borderWidth = m_poly.m_line.width();
+
+ m_geometry.updateSourcePoints(*map, m_geopathProjected, m_poly.m_geopath.boundingGeoRectangle().topLeft());
+ m_geometry.updateScreenPoints(*map, borderWidth);
+
+ m_poly.setWidth(m_geometry.sourceBoundingBox().width() + borderWidth);
+ m_poly.setHeight(m_geometry.sourceBoundingBox().height() + borderWidth);
+
+ m_poly.setPositionOnMap(m_geometry.origin(), -1 * m_geometry.sourceBoundingBox().topLeft()
+ + QPointF(borderWidth, borderWidth) * 0.5 ); // it has to be shifted so that the center of the line is on the correct geocoord
+ }
+ QSGNode *updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData * /*data*/) override
+ {
+ if (!m_node || !oldNode) {
+ m_node = new MapPolylineNode();
+ if (oldNode) {
+ delete oldNode;
+ oldNode = nullptr;
+ }
+ } else {
+ m_node = static_cast<MapPolylineNode *>(oldNode);
+ }
+
+ //TODO: update only material
+ if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial || !oldNode) {
+ m_node->update(m_poly.m_line.color(), &m_geometry);
+ m_geometry.setPreserveGeometry(false);
+ m_geometry.markClean();
+ m_poly.m_dirtyMaterial = false;
+ }
+ return m_node;
+ }
+ bool contains(const QPointF &point) const override
+ {
+ return m_geometry.contains(point);
+ }
+
+ QList<QDoubleVector2D> m_geopathProjected;
+ QGeoMapPolylineGeometry m_geometry;
+ MapPolylineNode *m_node = nullptr;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivateOpenGLLineStrip: public QDeclarativePolylineMapItemPrivate
+{
+public:
+
+ QDeclarativePolylineMapItemPrivateOpenGLLineStrip(QDeclarativePolylineMapItem &poly) : QDeclarativePolylineMapItemPrivate(poly)
+ {
+ }
+
+ QDeclarativePolylineMapItemPrivateOpenGLLineStrip(QDeclarativePolylineMapItemPrivate &other)
+ : QDeclarativePolylineMapItemPrivate(other)
+ {
+ }
+
+ ~QDeclarativePolylineMapItemPrivateOpenGLLineStrip() override;
+ void onLinePropertiesChanged() override
+ {
+ afterViewportChanged();
+ }
+ void markSourceDirtyAndUpdate() override
+ {
+ m_geometry.markSourceDirty();
+ m_poly.polishAndUpdate();
+ }
+ void preserveGeometry()
+ {
+ m_geometry.setPreserveGeometry(true, m_poly.m_geopath.boundingGeoRectangle().topLeft());
+ }
+ void onMapSet() override
+ {
+ markSourceDirtyAndUpdate();
+ }
+ void onGeoGeometryChanged() override
+ {
+ preserveGeometry();
+ markSourceDirtyAndUpdate();
+ }
+ void onGeoGeometryUpdated() override
+ {
+ preserveGeometry();
+ markSourceDirtyAndUpdate();
+ }
+ void onItemGeometryChanged() override
+ {
+ onGeoGeometryChanged();
+ }
+ void afterViewportChanged() override
+ {
+ preserveGeometry();
+ m_poly.polishAndUpdate();
+ }
+ bool contains(const QPointF &point) const override
+ {
+ return m_geometry.contains(m_poly.mapToItem(m_poly.quickMap(), point),
+ m_poly.line()->width(),
+ static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection()));
+ }
+ void updatePolish() override
+ {
+ if (m_poly.m_geopath.path().length() == 0) { // Possibly cleared
+ m_geometry.clear();
+ m_geometry.clear();
+ m_poly.setWidth(0);
+ m_poly.setHeight(0);
+ return;
+ }
+
+ QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry);
+ m_poly.m_updatingGeometry = true;
+ const qreal lineWidth = m_poly.m_line.width();
+ m_geometry.updateSourcePoints(*m_poly.map(), m_poly.m_geopath);
+ m_geometry.markScreenDirty();
+ m_geometry.updateScreenPoints(*m_poly.map(), lineWidth);
+
+ m_poly.setWidth(m_geometry.sourceBoundingBox().width());
+ m_poly.setHeight(m_geometry.sourceBoundingBox().height());
+ m_poly.setPosition(1.0 * m_geometry.firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5));
+ }
+ QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override
+ {
+ Q_UNUSED(data);
+
+ if (!m_node || !oldNode) {
+ m_node = new MapPolylineNodeOpenGLLineStrip();
+ if (oldNode)
+ delete oldNode;
+ } else {
+ m_node = static_cast<MapPolylineNodeOpenGLLineStrip *>(oldNode);
+ }
+
+ if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial) {
+ const QGeoMap *map = m_poly.map();
+ const QMatrix4x4 &combinedMatrix = map->geoProjection().qsgTransform();
+ const QDoubleVector3D &cameraCenter = map->geoProjection().centerMercator();
+ m_node->update(m_poly.m_line.color(), // This updates only the material if the geometry is unchanged
+ m_poly.m_line.width(),
+ &m_geometry,
+ combinedMatrix,
+ cameraCenter);
+ m_geometry.setPreserveGeometry(false);
+ m_geometry.markClean();
+ m_poly.m_dirtyMaterial = false;
+ }
+ return m_node;
+ }
+
+ QGeoMapPolylineGeometryOpenGL m_geometry;
+ MapPolylineNodeOpenGLLineStrip *m_node = nullptr;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivateOpenGLExtruded: public QDeclarativePolylineMapItemPrivateOpenGLLineStrip
+{
+public:
+
+ QDeclarativePolylineMapItemPrivateOpenGLExtruded(QDeclarativePolylineMapItem &poly)
+ : QDeclarativePolylineMapItemPrivateOpenGLLineStrip(poly)
+ {
+ }
+
+ QDeclarativePolylineMapItemPrivateOpenGLExtruded(QDeclarativePolylineMapItemPrivate &other)
+ : QDeclarativePolylineMapItemPrivateOpenGLLineStrip(other)
+ {
+ }
+
+ ~QDeclarativePolylineMapItemPrivateOpenGLExtruded() override;
+
+ QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override
+ {
+ Q_UNUSED(data);
+ const QGeoMap *map = m_poly.map();
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map->geoProjection());
+ const QMatrix4x4 &combinedMatrix = p.qsgTransform();
+ const QDoubleVector3D &cameraCenter = p.centerMercator();
+ const QColor &color = m_poly.m_line.color();
+ const float lineWidth = m_poly.m_line.width();
+
+ MapPolylineNodeOpenGLExtruded *nodeTri = nullptr;
+ if (!m_nodeTri || !oldNode) {
+ if (oldNode)
+ delete oldNode;
+ nodeTri = new MapPolylineNodeOpenGLExtruded();
+ } else {
+ nodeTri = static_cast<MapPolylineNodeOpenGLExtruded *>(oldNode);
+ }
+
+ //TODO: update only material
+ if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial) {
+ nodeTri->update(color,
+ lineWidth ,
+ &m_geometry,
+ combinedMatrix,
+ cameraCenter,
+ m_penCapStyle);
+ m_geometry.setPreserveGeometry(false);
+ m_geometry.markClean();
+ m_poly.m_dirtyMaterial = false;
+ }
+ m_nodeTri = nodeTri;
+ return nodeTri;
+ }
+
+ MapPolylineNodeOpenGLExtruded *m_nodeTri = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QDECLARATIVEPOLYLINEMAPITEM_P_P_H
diff --git a/src/location/declarativemaps/qdeclarativerectanglemapitem.cpp b/src/location/declarativemaps/qdeclarativerectanglemapitem.cpp
index fd4109a7..74d2cc13 100644
--- a/src/location/declarativemaps/qdeclarativerectanglemapitem.cpp
+++ b/src/location/declarativemaps/qdeclarativerectanglemapitem.cpp
@@ -35,6 +35,7 @@
****************************************************************************/
#include "qdeclarativerectanglemapitem_p.h"
+#include "qdeclarativerectanglemapitem_p_p.h"
#include "qdeclarativepolygonmapitem_p.h"
#include "qlocationutils_p.h"
#include <QPainterPath>
@@ -125,16 +126,30 @@ QT_BEGIN_NAMESPACE
\since 5.14
*/
+struct RectangleBackendSelector
+{
+ RectangleBackendSelector()
+ {
+ backend = (qgetenv("QTLOCATION_OPENGL_ITEMS").toInt()) ? QDeclarativeRectangleMapItem::OpenGL : QDeclarativeRectangleMapItem::Software;
+ }
+ QDeclarativeRectangleMapItem::Backend backend = QDeclarativeRectangleMapItem::Software;
+};
+
+Q_GLOBAL_STATIC(RectangleBackendSelector, mapRectangleBackendSelector)
+
QDeclarativeRectangleMapItem::QDeclarativeRectangleMapItem(QQuickItem *parent)
-: QDeclarativeGeoMapItemBase(parent), border_(this), color_(Qt::transparent), dirtyMaterial_(true),
- updatingGeometry_(false)
+: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent), m_dirtyMaterial(true),
+ m_updatingGeometry(false)
+ , m_d(new QDeclarativeRectangleMapItemPrivateCPU(*this))
{
+ // ToDo: handle envvar, and switch implementation.
m_itemType = QGeoMap::MapRectangle;
setFlag(ItemHasContents, true);
- QObject::connect(&border_, SIGNAL(colorChanged(QColor)),
- this, SLOT(markSourceDirtyAndUpdate()));
- QObject::connect(&border_, SIGNAL(widthChanged(qreal)),
- this, SLOT(markSourceDirtyAndUpdate()));
+ QObject::connect(&m_border, SIGNAL(colorChanged(QColor)),
+ this, SLOT(onLinePropertiesChanged()));
+ QObject::connect(&m_border, SIGNAL(widthChanged(qreal)),
+ this, SLOT(onLinePropertiesChanged()));
+ setBackend(mapRectangleBackendSelector->backend);
}
QDeclarativeRectangleMapItem::~QDeclarativeRectangleMapItem()
@@ -142,6 +157,43 @@ QDeclarativeRectangleMapItem::~QDeclarativeRectangleMapItem()
}
/*!
+ \qmlproperty MapRectangle.Backend QtLocation::MapRectangle::backend
+
+ This property holds which backend is in use to render the map item.
+ Valid values are \b MapRectangle.Software and \b{MapRectangle.OpenGL}.
+ The default value is \b{MapRectangle.Software}.
+
+ \note \b{The release of this API with Qt 5.15 is a Technology Preview}.
+ Ideally, as the OpenGL backends for map items mature, there will be
+ no more need to also offer the legacy software-projection backend.
+ So this property will likely disappear at some later point.
+ To select OpenGL-accelerated item backends without using this property,
+ it is also possible to set the environment variable \b QTLOCATION_OPENGL_ITEMS
+ to \b{1}.
+ Also note that all current OpenGL backends won't work as expected when enabling
+ layers on the individual item, or when running on OpenGL core profiles greater than 2.x.
+
+ \since 5.15
+*/
+QDeclarativeRectangleMapItem::Backend QDeclarativeRectangleMapItem::backend() const
+{
+ return m_backend;
+}
+
+void QDeclarativeRectangleMapItem::setBackend(QDeclarativeRectangleMapItem::Backend b)
+{
+ if (b == m_backend)
+ return;
+ m_backend = b;
+ QScopedPointer<QDeclarativeRectangleMapItemPrivate> d((m_backend == Software)
+ ? static_cast<QDeclarativeRectangleMapItemPrivate *>(new QDeclarativeRectangleMapItemPrivateCPU(*this))
+ : static_cast<QDeclarativeRectangleMapItemPrivate * >(new QDeclarativeRectangleMapItemPrivateOpenGL(*this)));
+ m_d.swap(d);
+ m_d->onGeoGeometryChanged();
+ emit backendChanged();
+}
+
+/*!
\internal
*/
void QDeclarativeRectangleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map)
@@ -149,8 +201,7 @@ void QDeclarativeRectangleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap
QDeclarativeGeoMapItemBase::setMap(quickMap,map);
if (!map)
return;
- updatePath();
- markSourceDirtyAndUpdate();
+ m_d->onMapSet();
}
/*!
@@ -167,7 +218,7 @@ void QDeclarativeRectangleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap
*/
QDeclarativeMapLineProperties *QDeclarativeRectangleMapItem::border()
{
- return &border_;
+ return &m_border;
}
/*!
@@ -178,18 +229,17 @@ QDeclarativeMapLineProperties *QDeclarativeRectangleMapItem::border()
*/
void QDeclarativeRectangleMapItem::setTopLeft(const QGeoCoordinate &topLeft)
{
- if (rectangle_.topLeft() == topLeft)
+ if (m_rectangle.topLeft() == topLeft)
return;
- rectangle_.setTopLeft(topLeft);
- updatePath();
- markSourceDirtyAndUpdate();
+ m_rectangle.setTopLeft(topLeft);
+ m_d->onGeoGeometryChanged();
emit topLeftChanged(topLeft);
}
QGeoCoordinate QDeclarativeRectangleMapItem::topLeft()
{
- return rectangle_.topLeft();
+ return m_rectangle.topLeft();
}
/*!
@@ -197,9 +247,12 @@ QGeoCoordinate QDeclarativeRectangleMapItem::topLeft()
*/
void QDeclarativeRectangleMapItem::markSourceDirtyAndUpdate()
{
- geometry_.markSourceDirty();
- borderGeometry_.markSourceDirty();
- polishAndUpdate();
+ m_d->markSourceDirtyAndUpdate();
+}
+
+void QDeclarativeRectangleMapItem::onLinePropertiesChanged()
+{
+ m_d->onLinePropertiesChanged();
}
/*!
@@ -210,18 +263,17 @@ void QDeclarativeRectangleMapItem::markSourceDirtyAndUpdate()
*/
void QDeclarativeRectangleMapItem::setBottomRight(const QGeoCoordinate &bottomRight)
{
- if (rectangle_.bottomRight() == bottomRight)
+ if (m_rectangle.bottomRight() == bottomRight)
return;
- rectangle_.setBottomRight(bottomRight);
- updatePath();
- markSourceDirtyAndUpdate();
+ m_rectangle.setBottomRight(bottomRight);
+ m_d->onGeoGeometryChanged();
emit bottomRightChanged(bottomRight);
}
QGeoCoordinate QDeclarativeRectangleMapItem::bottomRight()
{
- return rectangle_.bottomRight();
+ return m_rectangle.bottomRight();
}
/*!
@@ -232,17 +284,17 @@ QGeoCoordinate QDeclarativeRectangleMapItem::bottomRight()
*/
QColor QDeclarativeRectangleMapItem::color() const
{
- return color_;
+ return m_color;
}
void QDeclarativeRectangleMapItem::setColor(const QColor &color)
{
- if (color_ == color)
+ if (m_color == color)
return;
- color_ = color;
- dirtyMaterial_ = true;
+ m_color = color;
+ m_dirtyMaterial = true;
polishAndUpdate();
- emit colorChanged(color_);
+ emit colorChanged(m_color);
}
/*!
@@ -260,24 +312,7 @@ void QDeclarativeRectangleMapItem::setColor(const QColor &color)
*/
QSGNode *QDeclarativeRectangleMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
{
- Q_UNUSED(data);
-
- MapPolygonNode *node = static_cast<MapPolygonNode *>(oldNode);
-
- if (!node) {
- node = new MapPolygonNode();
- }
-
- //TODO: update only material
- if (geometry_.isScreenDirty() || borderGeometry_.isScreenDirty() || dirtyMaterial_) {
- node->update(color_, border_.color(), &geometry_, &borderGeometry_);
- geometry_.setPreserveGeometry(false);
- borderGeometry_.setPreserveGeometry(false);
- geometry_.markClean();
- borderGeometry_.markClean();
- dirtyMaterial_ = false;
- }
- return node;
+ return m_d->updateMapItemPaintNode(oldNode, data);
}
/*!
@@ -287,55 +322,7 @@ void QDeclarativeRectangleMapItem::updatePolish()
{
if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
return;
- if (!topLeft().isValid() || !bottomRight().isValid()) {
- geometry_.clear();
- borderGeometry_.clear();
- setWidth(0);
- setHeight(0);
- return;
- }
-
- const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection());
-
- QScopedValueRollback<bool> rollback(updatingGeometry_);
- updatingGeometry_ = true;
-
- geometry_.setPreserveGeometry(true, rectangle_.topLeft());
- geometry_.updateSourcePoints(*map(), pathMercator_);
- geometry_.updateScreenPoints(*map(), border_.width());
-
- QList<QGeoMapItemGeometry *> geoms;
- geoms << &geometry_;
- borderGeometry_.clear();
-
- if (border_.color() != Qt::transparent && border_.width() > 0) {
- QList<QDoubleVector2D> closedPath = pathMercator_;
- closedPath << closedPath.first();
-
- borderGeometry_.setPreserveGeometry(true, rectangle_.topLeft());
- const QGeoCoordinate &geometryOrigin = geometry_.origin();
-
- borderGeometry_.srcPoints_.clear();
- borderGeometry_.srcPointTypes_.clear();
-
- QDoubleVector2D borderLeftBoundWrapped;
- QList<QList<QDoubleVector2D > > clippedPaths = borderGeometry_.clipPath(*map(), closedPath, borderLeftBoundWrapped);
- if (clippedPaths.size()) {
- borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin);
- borderGeometry_.pathToScreen(*map(), clippedPaths, borderLeftBoundWrapped);
- borderGeometry_.updateScreenPoints(*map(), border_.width());
-
- geoms << &borderGeometry_;
- } else {
- borderGeometry_.clear();
- }
- }
-
- QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms);
- setWidth(combined.width() + 2 * border_.width());
- setHeight(combined.height() + 2 * border_.width());
-
- setPositionOnMap(geometry_.origin(), geometry_.firstPointOffset());
+ m_d->updatePolish();
}
/*!
@@ -345,10 +332,7 @@ void QDeclarativeRectangleMapItem::afterViewportChanged(const QGeoMapViewportCha
{
if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0)
return;
-
- geometry_.setPreserveGeometry(true, rectangle_.topLeft());
- borderGeometry_.setPreserveGeometry(true, rectangle_.topLeft());
- markSourceDirtyAndUpdate();
+ m_d->afterViewportChanged();
}
/*!
@@ -356,46 +340,29 @@ void QDeclarativeRectangleMapItem::afterViewportChanged(const QGeoMapViewportCha
*/
bool QDeclarativeRectangleMapItem::contains(const QPointF &point) const
{
- return (geometry_.contains(point) || borderGeometry_.contains(point));
+ return m_d->contains(point);
}
const QGeoShape &QDeclarativeRectangleMapItem::geoShape() const
{
- return rectangle_;
+ return m_rectangle;
}
void QDeclarativeRectangleMapItem::setGeoShape(const QGeoShape &shape)
{
- if (shape == rectangle_)
+ if (shape == m_rectangle)
return;
- const QGeoRectangle rectangle = rectangle_.boundingGeoRectangle();
- const bool tlHasChanged = rectangle.topLeft() != rectangle_.topLeft();
- const bool brHasChanged = rectangle.bottomRight() != rectangle_.bottomRight();
- rectangle_ = rectangle;
+ const QGeoRectangle rectangle = m_rectangle.boundingGeoRectangle();
+ const bool tlHasChanged = rectangle.topLeft() != m_rectangle.topLeft();
+ const bool brHasChanged = rectangle.bottomRight() != m_rectangle.bottomRight();
+ m_rectangle = rectangle;
- updatePath();
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryChanged();
if (tlHasChanged)
- emit topLeftChanged(rectangle_.topLeft());
+ emit topLeftChanged(m_rectangle.topLeft());
if (brHasChanged)
- emit bottomRightChanged(rectangle_.bottomRight());
-}
-
-/*!
- \internal
-*/
-void QDeclarativeRectangleMapItem::updatePath()
-{
- if (!map())
- return;
- pathMercator_.clear();
- pathMercator_ << QWebMercator::coordToMercator(rectangle_.topLeft());
- pathMercator_ << QWebMercator::coordToMercator(
- QGeoCoordinate(rectangle_.topLeft().latitude(), rectangle_.bottomRight().longitude()));
- pathMercator_ << QWebMercator::coordToMercator(rectangle_.bottomRight());
- pathMercator_ << QWebMercator::coordToMercator(
- QGeoCoordinate(rectangle_.bottomRight().latitude(), rectangle_.topLeft().longitude()));
+ emit bottomRightChanged(m_rectangle.bottomRight());
}
/*!
@@ -403,7 +370,7 @@ void QDeclarativeRectangleMapItem::updatePath()
*/
void QDeclarativeRectangleMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
- if (!map() || !rectangle_.isValid() || updatingGeometry_ || newGeometry.topLeft() == oldGeometry.topLeft()) {
+ if (!map() || !m_rectangle.isValid() || m_updatingGeometry || newGeometry.topLeft() == oldGeometry.topLeft()) {
QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry);
return;
}
@@ -417,16 +384,19 @@ void QDeclarativeRectangleMapItem::geometryChanged(const QRectF &newGeometry, co
if (offsetLati == 0.0 && offsetLongi == 0.0)
return;
- rectangle_.translate(offsetLati, offsetLongi);
- updatePath();
- geometry_.setPreserveGeometry(true, rectangle_.topLeft());
- borderGeometry_.setPreserveGeometry(true, rectangle_.topLeft());
- markSourceDirtyAndUpdate();
- emit topLeftChanged(rectangle_.topLeft());
- emit bottomRightChanged(rectangle_.bottomRight());
+ m_rectangle.translate(offsetLati, offsetLongi);
+ m_d->onItemGeometryChanged();
+ emit topLeftChanged(m_rectangle.topLeft());
+ emit bottomRightChanged(m_rectangle.bottomRight());
// Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested
// call to this function.
}
+QDeclarativeRectangleMapItemPrivate::~QDeclarativeRectangleMapItemPrivate() {}
+
+QDeclarativeRectangleMapItemPrivateCPU::~QDeclarativeRectangleMapItemPrivateCPU() {}
+
+QDeclarativeRectangleMapItemPrivateOpenGL::~QDeclarativeRectangleMapItemPrivateOpenGL() {}
+
QT_END_NAMESPACE
diff --git a/src/location/declarativemaps/qdeclarativerectanglemapitem_p.h b/src/location/declarativemaps/qdeclarativerectanglemapitem_p.h
index 790b99d9..a9ce2f4c 100644
--- a/src/location/declarativemaps/qdeclarativerectanglemapitem_p.h
+++ b/src/location/declarativemaps/qdeclarativerectanglemapitem_p.h
@@ -52,7 +52,7 @@
#include <QtLocation/private/qdeclarativegeomapitembase_p.h>
#include <QtLocation/private/qgeomapitemgeometry_p.h>
#include <QtLocation/private/qdeclarativepolylinemapitem_p.h>
-#include <QtLocation/private/qdeclarativepolygonmapitem_p.h>
+#include <QtLocation/private/qdeclarativepolygonmapitem_p_p.h>
#include <QtPositioning/private/qdoublevector2d_p.h>
#include <QSGGeometryNode>
@@ -60,18 +60,26 @@
QT_BEGIN_NAMESPACE
+class QDeclarativeRectangleMapItemPrivate;
class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItem: public QDeclarativeGeoMapItemBase
{
Q_OBJECT
+ Q_ENUMS(Backend)
Q_PROPERTY(QGeoCoordinate topLeft READ topLeft WRITE setTopLeft NOTIFY topLeftChanged)
Q_PROPERTY(QGeoCoordinate bottomRight READ bottomRight WRITE setBottomRight NOTIFY bottomRightChanged)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
Q_PROPERTY(QDeclarativeMapLineProperties *border READ border CONSTANT)
+ Q_PROPERTY(Backend backend READ backend WRITE setBackend NOTIFY backendChanged REVISION 15)
public:
- explicit QDeclarativeRectangleMapItem(QQuickItem *parent = 0);
- ~QDeclarativeRectangleMapItem();
+ enum Backend {
+ Software = 0,
+ OpenGL = 1
+ };
+
+ explicit QDeclarativeRectangleMapItem(QQuickItem *parent = nullptr);
+ ~QDeclarativeRectangleMapItem() override;
virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) override;
//from QuickItem
@@ -92,29 +100,38 @@ public:
const QGeoShape &geoShape() const override;
void setGeoShape(const QGeoShape &shape) override;
+ Backend backend() const;
+ void setBackend(Backend b);
+
Q_SIGNALS:
void topLeftChanged(const QGeoCoordinate &topLeft);
void bottomRightChanged(const QGeoCoordinate &bottomRight);
void colorChanged(const QColor &color);
+ void backendChanged();
protected:
- void updatePath();
void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
void updatePolish() override;
protected Q_SLOTS:
void markSourceDirtyAndUpdate();
+ void onLinePropertiesChanged();
virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) override;
private:
- QGeoRectangle rectangle_;
- QDeclarativeMapLineProperties border_;
- QColor color_;
- bool dirtyMaterial_;
- QGeoMapPolygonGeometry geometry_;
- QGeoMapPolylineGeometry borderGeometry_;
- bool updatingGeometry_;
- QList<QDoubleVector2D> pathMercator_;
+ QGeoRectangle m_rectangle;
+ QDeclarativeMapLineProperties m_border;
+ QColor m_color;
+ bool m_dirtyMaterial;
+
+ bool m_updatingGeometry;
+ Backend m_backend = Software;
+
+ QScopedPointer<QDeclarativeRectangleMapItemPrivate> m_d;
+
+ friend class QDeclarativeRectangleMapItemPrivate;
+ friend class QDeclarativeRectangleMapItemPrivateCPU;
+ friend class QDeclarativeRectangleMapItemPrivateOpenGL;
};
//////////////////////////////////////////////////////////////////////
diff --git a/src/location/declarativemaps/qdeclarativerectanglemapitem_p_p.h b/src/location/declarativemaps/qdeclarativerectanglemapitem_p_p.h
new file mode 100644
index 00000000..520abdf3
--- /dev/null
+++ b/src/location/declarativemaps/qdeclarativerectanglemapitem_p_p.h
@@ -0,0 +1,416 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Paolo Angelelli <paolo.angelelli@gmail.com>
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtLocation module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPLv3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVERECTANGLEMAPITEM_P_P_H
+#define QDECLARATIVERECTANGLEMAPITEM_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtLocation/private/qlocationglobal_p.h>
+#include <QtLocation/private/qdeclarativepolygonmapitem_p_p.h>
+#include <QtLocation/private/qdeclarativerectanglemapitem_p.h>
+#include <QtPositioning/private/qwebmercator_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItemPrivate
+{
+public:
+ QDeclarativeRectangleMapItemPrivate(QDeclarativeRectangleMapItem &rect) : m_rect(rect)
+ {
+
+ }
+ QDeclarativeRectangleMapItemPrivate(QDeclarativeRectangleMapItemPrivate &other) : m_rect(other.m_rect)
+ {
+ }
+
+ virtual ~QDeclarativeRectangleMapItemPrivate();
+ virtual void onLinePropertiesChanged() = 0;
+ virtual void markSourceDirtyAndUpdate() = 0;
+ virtual void onMapSet() = 0;
+ virtual void onGeoGeometryChanged() = 0;
+ virtual void onItemGeometryChanged() = 0;
+ virtual void updatePolish() = 0;
+ virtual void afterViewportChanged() = 0;
+ virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) = 0;
+ virtual bool contains(const QPointF &point) const = 0;
+
+ QDeclarativeRectangleMapItem &m_rect;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItemPrivateCPU: public QDeclarativeRectangleMapItemPrivate
+{
+public:
+ QDeclarativeRectangleMapItemPrivateCPU(QDeclarativeRectangleMapItem &rect) : QDeclarativeRectangleMapItemPrivate(rect)
+ {
+ }
+
+ QDeclarativeRectangleMapItemPrivateCPU(QDeclarativeRectangleMapItemPrivate &other)
+ : QDeclarativeRectangleMapItemPrivate(other)
+ {
+ }
+
+ ~QDeclarativeRectangleMapItemPrivateCPU() override;
+
+ void onLinePropertiesChanged() override
+ {
+ // mark dirty just in case we're a width change
+ markSourceDirtyAndUpdate();
+ }
+ virtual void markSourceDirtyAndUpdate() override
+ {
+ m_geometry.markSourceDirty();
+ m_borderGeometry.markSourceDirty();
+ m_rect.polishAndUpdate();
+ }
+ virtual void onMapSet() override
+ {
+ markSourceDirtyAndUpdate();
+ }
+ virtual void onGeoGeometryChanged() override
+ {
+ markSourceDirtyAndUpdate();
+ }
+ virtual void onItemGeometryChanged() override
+ {
+ m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft());
+ m_borderGeometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft());
+ markSourceDirtyAndUpdate();
+ }
+ virtual void afterViewportChanged() override
+ {
+ m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft());
+ m_borderGeometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft());
+ markSourceDirtyAndUpdate();
+ }
+ virtual void updatePolish() override
+ {
+ if (!m_rect.topLeft().isValid() || !m_rect.bottomRight().isValid()) {
+ m_geometry.clear();
+ m_borderGeometry.clear();
+ m_rect.setWidth(0);
+ m_rect.setHeight(0);
+ return;
+ }
+
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_rect.map()->geoProjection());
+
+ QScopedValueRollback<bool> rollback(m_rect.m_updatingGeometry);
+ m_rect.m_updatingGeometry = true;
+
+ const QList<QGeoCoordinate> perimeter = path(m_rect.m_rectangle);
+ const QList<QDoubleVector2D> pathMercator_ = pathMercator(perimeter);
+ m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft());
+ m_geometry.updateSourcePoints(*m_rect.map(), pathMercator_);
+ m_geometry.updateScreenPoints(*m_rect.map(), m_rect.m_border.width());
+
+ QList<QGeoMapItemGeometry *> geoms;
+ geoms << &m_geometry;
+ m_borderGeometry.clear();
+
+ if (m_rect.m_border.color().alpha() != 0 && m_rect.m_border.width() > 0) {
+ QList<QDoubleVector2D> closedPath = pathMercator_;
+ closedPath << closedPath.first();
+
+ m_borderGeometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft());
+ const QGeoCoordinate &geometryOrigin = m_geometry.origin();
+
+ m_borderGeometry.srcPoints_.clear();
+ m_borderGeometry.srcPointTypes_.clear();
+
+ QDoubleVector2D borderLeftBoundWrapped;
+ QList<QList<QDoubleVector2D > > clippedPaths = m_borderGeometry.clipPath(*m_rect.map(), closedPath, borderLeftBoundWrapped);
+ if (clippedPaths.size()) {
+ borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin);
+ m_borderGeometry.pathToScreen(*m_rect.map(), clippedPaths, borderLeftBoundWrapped);
+ m_borderGeometry.updateScreenPoints(*m_rect.map(), m_rect.m_border.width());
+
+ geoms << &m_borderGeometry;
+ } else {
+ m_borderGeometry.clear();
+ }
+ }
+
+ QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms);
+ m_rect.setWidth(combined.width() + 2 * m_rect.m_border.width()); // ToDo: fix this! 2 is incorrect
+ m_rect.setHeight(combined.height() + 2 * m_rect.m_border.width());
+
+ m_rect.setPositionOnMap(m_geometry.origin(), m_geometry.firstPointOffset());
+ }
+
+ virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override
+ {
+ Q_UNUSED(data);
+ if (!m_node || !oldNode) {
+ m_node = new MapPolygonNode();
+ if (oldNode) {
+ delete oldNode;
+ oldNode = nullptr;
+ }
+ } else {
+ m_node = static_cast<MapPolygonNode *>(oldNode);
+ }
+
+ //TODO: update only material
+ if (m_geometry.isScreenDirty() || m_borderGeometry.isScreenDirty() || m_rect.m_dirtyMaterial) {
+ m_node->update(m_rect.m_color, m_rect.m_border.color(), &m_geometry, &m_borderGeometry);
+ m_geometry.setPreserveGeometry(false);
+ m_borderGeometry.setPreserveGeometry(false);
+ m_geometry.markClean();
+ m_borderGeometry.markClean();
+ m_rect.m_dirtyMaterial = false;
+ }
+ return m_node;
+ }
+ virtual bool contains(const QPointF &point) const override
+ {
+ return (m_geometry.contains(point) || m_borderGeometry.contains(point));
+ }
+
+ static QList<QGeoCoordinate> path(const QGeoRectangle &rect)
+ {
+ QList<QGeoCoordinate> res;
+ res << rect.topLeft();
+ res << QGeoCoordinate(rect.topLeft().latitude(), rect.bottomRight().longitude());
+ res << rect.bottomRight();
+ res << QGeoCoordinate(rect.bottomRight().latitude(), rect.topLeft().longitude());
+ return res;
+ }
+
+ static QList<QGeoCoordinate> perimeter(const QGeoRectangle &rect)
+ {
+ QList<QGeoCoordinate> res;
+ res << rect.topLeft();
+ res << QGeoCoordinate(rect.topLeft().latitude(), rect.bottomRight().longitude());
+ res << rect.bottomRight();
+ res << QGeoCoordinate(rect.bottomRight().latitude(), rect.topLeft().longitude());
+ res << res.first();
+ return res;
+ }
+
+ static QList<QDoubleVector2D> pathMercator(const QList<QGeoCoordinate> &p)
+ {
+ QList<QDoubleVector2D> res;
+ for (const auto &c: p)
+ res << QWebMercator::coordToMercator(c);
+ return res;
+ }
+
+ QGeoMapPolygonGeometry m_geometry;
+ QGeoMapPolylineGeometry m_borderGeometry;
+ MapPolygonNode *m_node = nullptr;
+};
+
+class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItemPrivateOpenGL: public QDeclarativeRectangleMapItemPrivate
+{
+public:
+ QDeclarativeRectangleMapItemPrivateOpenGL(QDeclarativeRectangleMapItem &rect) : QDeclarativeRectangleMapItemPrivate(rect)
+ {
+ }
+
+ QDeclarativeRectangleMapItemPrivateOpenGL(QDeclarativeRectangleMapItemPrivate &other)
+ : QDeclarativeRectangleMapItemPrivate(other)
+ {
+ }
+
+ ~QDeclarativeRectangleMapItemPrivateOpenGL() override;
+
+ void markScreenDirtyAndUpdate()
+ {
+ // preserveGeometry is cleared in updateMapItemPaintNode
+ m_geometry.markScreenDirty();
+ m_borderGeometry.markScreenDirty();
+ m_rect.polishAndUpdate();
+ }
+ void onLinePropertiesChanged() override
+ {
+ m_rect.m_dirtyMaterial = true;
+ afterViewportChanged();
+ }
+ virtual void markSourceDirtyAndUpdate() override
+ {
+ m_geometry.markSourceDirty();
+ m_borderGeometry.markSourceDirty();
+ m_rect.polishAndUpdate();
+ }
+ void preserveGeometry()
+ {
+ m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft());
+ m_borderGeometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft());
+ }
+ virtual void onMapSet() override
+ {
+ markSourceDirtyAndUpdate();
+ }
+ virtual void onGeoGeometryChanged() override
+ {
+ preserveGeometry();
+ markSourceDirtyAndUpdate();
+ }
+ virtual void onItemGeometryChanged() override
+ {
+ onGeoGeometryChanged();
+ }
+ virtual void afterViewportChanged() override
+ {
+ preserveGeometry();
+ markScreenDirtyAndUpdate();
+ }
+ virtual void updatePolish() override
+ {
+ if (!m_rect.topLeft().isValid() || !m_rect.bottomRight().isValid()) {
+ m_geometry.clear();
+ m_borderGeometry.clear();
+ m_rect.setWidth(0);
+ m_rect.setHeight(0);
+ return;
+ }
+
+ QScopedValueRollback<bool> rollback(m_rect.m_updatingGeometry);
+ m_rect.m_updatingGeometry = true;
+ const qreal lineWidth = m_rect.m_border.width();
+ const QColor &lineColor = m_rect.m_border.color();
+ const QColor &fillColor = m_rect.color();
+ if (fillColor.alpha() != 0) {
+ m_geometry.updateSourcePoints(*m_rect.map(), m_rect.m_rectangle);
+ m_geometry.markScreenDirty();
+ m_geometry.updateScreenPoints(*m_rect.map(), lineWidth, lineColor);
+ } else {
+ m_geometry.clearBounds();
+ }
+
+ QGeoMapItemGeometry * geom = &m_geometry;
+ m_borderGeometry.clearScreen();
+ if (lineColor.alpha() != 0 && lineWidth > 0) {
+ m_borderGeometry.updateSourcePoints(*m_rect.map(), m_rect.m_rectangle);
+ m_borderGeometry.markScreenDirty();
+ m_borderGeometry.updateScreenPoints(*m_rect.map(), lineWidth);
+ geom = &m_borderGeometry;
+ }
+ m_rect.setWidth(geom->sourceBoundingBox().width());
+ m_rect.setHeight(geom->sourceBoundingBox().height());
+ m_rect.setPosition(1.0 * geom->firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5));
+ }
+
+ virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override
+ {
+ Q_UNUSED(data);
+
+ if (!m_rootNode || !oldNode) {
+ m_rootNode = new QDeclarativePolygonMapItemPrivateOpenGL::RootNode();
+ m_node = new MapPolygonNodeGL();
+ m_rootNode->appendChildNode(m_node);
+ m_polylinenode = new MapPolylineNodeOpenGLExtruded();
+ m_rootNode->appendChildNode(m_polylinenode);
+ m_rootNode->markDirty(QSGNode::DirtyNodeAdded);
+ if (oldNode)
+ delete oldNode;
+ } else {
+ m_rootNode = static_cast<QDeclarativePolygonMapItemPrivateOpenGL::RootNode *>(oldNode);
+ }
+
+ const QGeoMap *map = m_rect.map();
+ const QMatrix4x4 &combinedMatrix = map->geoProjection().qsgTransform();
+ const QDoubleVector3D &cameraCenter = map->geoProjection().centerMercator();
+
+ if (m_borderGeometry.isScreenDirty()) {
+ /* Do the border update first */
+ m_polylinenode->update(m_rect.m_border.color(),
+ float(m_rect.m_border.width()),
+ &m_borderGeometry,
+ combinedMatrix,
+ cameraCenter,
+ Qt::SquareCap,
+ true);
+ m_borderGeometry.setPreserveGeometry(false);
+ m_borderGeometry.markClean();
+ } else {
+ m_polylinenode->setSubtreeBlocked(true);
+ }
+ if (m_geometry.isScreenDirty()) {
+ m_node->update(m_rect.m_color,
+ &m_geometry,
+ combinedMatrix,
+ cameraCenter);
+ m_geometry.setPreserveGeometry(false);
+ m_geometry.markClean();
+ } else {
+ m_node->setSubtreeBlocked(true);
+ }
+
+ m_rootNode->setSubtreeBlocked(false);
+ return m_rootNode;
+ }
+ virtual bool contains(const QPointF &point) const override
+ {
+ const qreal lineWidth = m_rect.m_border.width();
+ const QColor &lineColor = m_rect.m_border.color();
+ const QRectF &bounds = (lineColor.alpha() != 0 && lineWidth > 0) ? m_borderGeometry.sourceBoundingBox() : m_geometry.sourceBoundingBox();
+ if (bounds.contains(point)) {
+ QDeclarativeGeoMap *m = m_rect.quickMap();
+ if (m) {
+ const QGeoCoordinate crd = m->toCoordinate(m->mapFromItem(&m_rect, point));
+ return m_rect.m_rectangle.contains(crd) || m_borderGeometry.contains(m_rect.mapToItem(m_rect.quickMap(), point),
+ m_rect.border()->width(),
+ static_cast<const QGeoProjectionWebMercator&>(m_rect.map()->geoProjection()));
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ QGeoMapPolygonGeometryOpenGL m_geometry;
+ QGeoMapPolylineGeometryOpenGL m_borderGeometry;
+ QDeclarativePolygonMapItemPrivateOpenGL::RootNode *m_rootNode = nullptr;
+ MapPolygonNodeGL *m_node = nullptr;
+ MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QDECLARATIVERECTANGLEMAPITEM_P_P_H
+
diff --git a/src/location/declarativemaps/qgeomapitemgeometry.cpp b/src/location/declarativemaps/qgeomapitemgeometry.cpp
index 2883c2bb..28e9fa67 100644
--- a/src/location/declarativemaps/qgeomapitemgeometry.cpp
+++ b/src/location/declarativemaps/qgeomapitemgeometry.cpp
@@ -48,6 +48,11 @@ QGeoMapItemGeometry::QGeoMapItemGeometry()
{
}
+QGeoMapItemGeometry::~QGeoMapItemGeometry()
+{
+
+}
+
/*!
\internal
*/
diff --git a/src/location/declarativemaps/qgeomapitemgeometry_p.h b/src/location/declarativemaps/qgeomapitemgeometry_p.h
index ab81ff0b..aa5ea4ac 100644
--- a/src/location/declarativemaps/qgeomapitemgeometry_p.h
+++ b/src/location/declarativemaps/qgeomapitemgeometry_p.h
@@ -67,6 +67,7 @@ class Q_LOCATION_PRIVATE_EXPORT QGeoMapItemGeometry
{
public:
QGeoMapItemGeometry();
+ virtual ~QGeoMapItemGeometry();
inline bool isSourceDirty() const { return sourceDirty_; }
inline bool isScreenDirty() const { return screenDirty_; }
@@ -74,6 +75,7 @@ public:
inline void markScreenDirty() { screenDirty_ = true; clipToViewport_ = true; }
inline void markFullScreenDirty() { screenDirty_ = true; clipToViewport_ = false;}
inline void markClean() { screenDirty_ = (sourceDirty_ = false); clipToViewport_ = true;}
+ inline void clearScreen() { screenDirty_ = false; }
inline void setPreserveGeometry(bool value, const QGeoCoordinate &geoLeftBound = QGeoCoordinate())
{
@@ -85,6 +87,7 @@ public:
inline QRectF sourceBoundingBox() const { return sourceBounds_; }
inline QRectF screenBoundingBox() const { return screenBounds_; }
+ inline void clearBounds() { sourceBounds_ = screenBounds_ = QRectF(); firstPointOffset_ = QPointF(); }
inline QPointF firstPointOffset() const { return firstPointOffset_; }
void translate(const QPointF &offset);
@@ -128,6 +131,8 @@ public:
static QRectF translateToCommonOrigin(const QList<QGeoMapItemGeometry *> &geoms);
+ mutable bool m_dataChanged = false;
+
private:
QGeoMapItemGeometry(const QGeoMapItemGeometry &other); // Or else it may crash on copy
QGeoMapItemGeometry &operator= (const QGeoMapItemGeometry & other); // Or else it may crash on copy
diff --git a/src/location/labs/qsg/qmapcircleobjectqsg.cpp b/src/location/labs/qsg/qmapcircleobjectqsg.cpp
index 32f3030b..f79be136 100644
--- a/src/location/labs/qsg/qmapcircleobjectqsg.cpp
+++ b/src/location/labs/qsg/qmapcircleobjectqsg.cpp
@@ -66,7 +66,7 @@ void QMapCircleObjectPrivateQSG::updateCirclePath()
{
const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_map->geoProjection());
QList<QGeoCoordinate> path;
- QDeclarativeCircleMapItem::calculatePeripheralPoints(path, center(), radius(), CircleSamples, m_leftBound);
+ QDeclarativeCircleMapItemPrivateCPU::calculatePeripheralPoints(path, center(), radius(), CircleSamples, m_leftBound);
m_circlePath.clear();
for (const QGeoCoordinate &c : path)
m_circlePath << p.geoToMapProjection(c);
@@ -86,7 +86,7 @@ void QMapCircleObjectPrivateQSG::updateGeometry()
QList<QDoubleVector2D> circlePath = m_circlePath;
int pathCount = circlePath.size();
- bool preserve = QDeclarativeCircleMapItem::preserveCircleGeometry(circlePath, center(), radius(), p);
+ bool preserve = QDeclarativeCircleMapItemPrivateCPU::preserveCircleGeometry(circlePath, center(), radius(), p);
// using leftBound_ instead of the analytically calculated circle_.boundingGeoRectangle().topLeft());
// to fix QTBUG-62154
m_geometry.markSourceDirty();
@@ -94,7 +94,7 @@ void QMapCircleObjectPrivateQSG::updateGeometry()
m_geometry.setPreserveGeometry(preserve, m_leftBound);
bool invertedCircle = false;
- if (QDeclarativeCircleMapItem::crossEarthPole(center(), radius()) && circlePath.size() == pathCount) {
+ if (QDeclarativeCircleMapItemPrivateCPU::crossEarthPole(center(), radius()) && circlePath.size() == pathCount) {
m_geometry.updateScreenPointsInvert(circlePath, *m_map); // invert fill area for really huge circles
invertedCircle = true;
} else {
diff --git a/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h b/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h
index 4e8162fa..6bbeb397 100644
--- a/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h
+++ b/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h
@@ -50,7 +50,7 @@
#include <QtLocation/private/qlocationglobal_p.h>
#include <QtLocation/private/qgeomapobject_p_p.h>
-#include <QtLocation/private/qdeclarativecirclemapitem_p.h>
+#include <QtLocation/private/qdeclarativecirclemapitem_p_p.h>
#include <QtLocation/private/qdeclarativepolygonmapitem_p.h>
#include <QtLocation/private/qmapcircleobject_p.h>
#include <QtLocation/private/qmapcircleobject_p_p.h>
diff --git a/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h b/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h
index 0f42a92e..8e6ae8a8 100644
--- a/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h
+++ b/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h
@@ -52,7 +52,7 @@
#include <QtLocation/private/qmappolygonobject_p.h>
#include <QtLocation/private/qmappolygonobject_p_p.h>
#include <QtLocation/private/qqsgmapobject_p.h>
-#include <QtLocation/private/qdeclarativepolygonmapitem_p.h>
+#include <QtLocation/private/qdeclarativepolygonmapitem_p_p.h>
#include <QtCore/qscopedvaluerollback.h>
QT_BEGIN_NAMESPACE
diff --git a/src/location/labs/qsg/qqsgmapobject_p.h b/src/location/labs/qsg/qqsgmapobject_p.h
index 52036805..0a717e62 100644
--- a/src/location/labs/qsg/qqsgmapobject_p.h
+++ b/src/location/labs/qsg/qqsgmapobject_p.h
@@ -51,7 +51,7 @@
#include <QtLocation/private/qlocationglobal_p.h>
#include <QtQuick/QSGOpacityNode>
#include <QtLocation/private/qgeomapobject_p.h>
-#include <QtLocation/private/qdeclarativepolylinemapitem_p.h>
+#include <QtLocation/private/qdeclarativepolylinemapitem_p_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/location/maps/qgeomap.cpp b/src/location/maps/qgeomap.cpp
index dc8aa2c8..51af9199 100644
--- a/src/location/maps/qgeomap.cpp
+++ b/src/location/maps/qgeomap.cpp
@@ -41,6 +41,7 @@
#include "qdeclarativegeomapitembase_p.h"
#include "qgeomapobject_p.h"
#include "qgeomapobject_p_p.h"
+#include <QtQuick/private/qquickitem_p.h>
#include <QDebug>
#include <QRectF>
@@ -310,6 +311,12 @@ QList<QObject *> QGeoMap::mapObjectsAt(const QGeoCoordinate &/*coordinate*/) con
return QList<QObject *>();
}
+void QGeoMap::setItemToWindowTransform(const QTransform &itemToWindowTransform)
+{
+ Q_D(QGeoMap);
+ d->m_geoProjection->setItemToWindowTransform(itemToWindowTransform);
+}
+
void QGeoMap::setVisibleArea(const QRectF &visibleArea)
{
Q_D(QGeoMap);
diff --git a/src/location/maps/qgeomap_p.h b/src/location/maps/qgeomap_p.h
index 216c8b64..e955b513 100644
--- a/src/location/maps/qgeomap_p.h
+++ b/src/location/maps/qgeomap_p.h
@@ -55,6 +55,7 @@
#include <QtPositioning/private/qdoublevector2d_p.h>
#include <QtLocation/private/qgeoprojection_p.h>
#include <QtLocation/qgeoroute.h>
+#include <QTransform>
QT_BEGIN_NAMESPACE
@@ -67,6 +68,7 @@ class QQuickWindow;
class QGeoMapParameter;
class QDeclarativeGeoMapItemBase;
class QGeoMapObject;
+class QDeclarativeGeoMap;
class Q_LOCATION_PRIVATE_EXPORT QGeoMap : public QObject
{
@@ -155,6 +157,7 @@ public:
virtual void setCopyrightVisible(bool visible);
virtual void removeMapObject(QGeoMapObject *obj);
virtual QList<QObject *> mapObjectsAt(const QGeoCoordinate &coordinate) const;
+ virtual void setItemToWindowTransform(const QTransform &itemToWindowTransform);
void setVisibleArea(const QRectF &visibleArea);
QRectF visibleArea() const;
diff --git a/src/location/maps/qgeoprojection.cpp b/src/location/maps/qgeoprojection.cpp
index f64060e2..ff6a0b77 100644
--- a/src/location/maps/qgeoprojection.cpp
+++ b/src/location/maps/qgeoprojection.cpp
@@ -118,6 +118,19 @@ bool QGeoProjection::setBearing(qreal bearing, const QGeoCoordinate &coordinate)
return false;
}
+void QGeoProjection::setItemToWindowTransform(const QTransform &itemToWindowTransform)
+{
+ if (m_itemToWindowTransform == itemToWindowTransform)
+ return;
+ m_qsgTransformDirty = true;
+ m_itemToWindowTransform = itemToWindowTransform;
+}
+
+QTransform QGeoProjection::itemToWindowTransform() const
+{
+ return m_itemToWindowTransform;
+}
+
/*
* QGeoProjectionWebMercator implementation
@@ -188,6 +201,31 @@ double QGeoProjectionWebMercator::minimumZoom() const
return m_minimumZoom;
}
+QMatrix4x4 QGeoProjectionWebMercator::projectionTransformation() const
+{
+ return toMatrix4x4(m_transformation);
+}
+
+QMatrix4x4 QGeoProjectionWebMercator::projectionTransformation_centered() const
+{
+ return toMatrix4x4(m_transformation0);
+}
+
+const QMatrix4x4 &QGeoProjectionWebMercator::qsgTransform() const
+{
+ if (m_qsgTransformDirty) {
+ m_qsgTransformDirty = false;
+ m_qsgTransform = QMatrix4x4(m_itemToWindowTransform) * toMatrix4x4(m_transformation0);
+// qDebug() << "QGeoProjectionWebMercator::qsgTransform" << m_itemToWindowTransform << toMatrix4x4(m_transformation0);
+ }
+ return m_qsgTransform;
+}
+
+QDoubleVector3D QGeoProjectionWebMercator::centerMercator() const
+{
+ return geoToMapProjection(m_cameraData.center()).toVector3D();
+}
+
// This method recalculates the "no-trespassing" limits for the map center.
// This has to be used when:
// 1) the map is resized, because the meters per pixel remain the same, but
@@ -273,19 +311,23 @@ QGeoCoordinate QGeoProjectionWebMercator::mapProjectionToGeo(const QDoubleVector
return QWebMercator::mercatorToCoord(projection);
}
-//wraps around center
-QDoubleVector2D QGeoProjectionWebMercator::wrapMapProjection(const QDoubleVector2D &projection) const
+int QGeoProjectionWebMercator::projectionWrapFactor(const QDoubleVector2D &projection) const
{
- double x = projection.x();
+ const double &x = projection.x();
if (m_cameraCenterXMercator < 0.5) {
if (x - m_cameraCenterXMercator > 0.5 )
- x -= 1.0;
+ return -1;
} else if (m_cameraCenterXMercator > 0.5) {
if (x - m_cameraCenterXMercator < -0.5 )
- x += 1.0;
+ return 1;
}
+ return 0;
+}
- return QDoubleVector2D(x, projection.y());
+//wraps around center
+QDoubleVector2D QGeoProjectionWebMercator::wrapMapProjection(const QDoubleVector2D &projection) const
+{
+ return QDoubleVector2D(projection.x() + double(projectionWrapFactor(projection)), projection.y());
}
QDoubleVector2D QGeoProjectionWebMercator::unwrapMapProjection(const QDoubleVector2D &wrappedProjection) const
@@ -547,6 +589,7 @@ QGeoProjection::ProjectionType QGeoProjectionWebMercator::projectionType() const
void QGeoProjectionWebMercator::setupCamera()
{
+ m_qsgTransformDirty = true;
m_centerMercator = geoToMapProjection(m_cameraData.center());
m_cameraCenterXMercator = m_centerMercator.x();
m_cameraCenterYMercator = m_centerMercator.y();
@@ -571,6 +614,10 @@ void QGeoProjectionWebMercator::setupCamera()
// And in mercator space
m_eyeMercator = m_centerMercator;
m_eyeMercator.setZ(altitude_mercator / m_aperture);
+ m_eyeMercator0 = QDoubleVector3D(0,0,0);
+ m_eyeMercator0.setZ(altitude_mercator / m_aperture);
+ QDoubleVector3D eye0(0,0,0);
+ eye0.setZ(altitude * defaultTileSize / m_aperture);
m_view = m_eye - m_center;
QDoubleVector3D side = QDoubleVector3D::normal(m_view, QDoubleVector3D(0.0, 1.0, 0.0));
@@ -599,11 +646,13 @@ void QGeoProjectionWebMercator::setupCamera()
QDoubleMatrix4x4 mTilt;
mTilt.rotate(-m_cameraData.tilt(), m_side);
m_eye = mTilt * m_view + m_center;
+ eye0 = mTilt * m_view;
// In mercator space too
QDoubleMatrix4x4 mTiltMercator;
mTiltMercator.rotate(-m_cameraData.tilt(), m_sideMercator);
m_eyeMercator = mTiltMercator * m_viewMercator + m_centerMercator;
+ m_eyeMercator0 = mTiltMercator * m_viewMercator;
}
m_view = m_eye - m_center; // ToDo: this should be inverted (center - eye), and the rest should follow
@@ -634,8 +683,10 @@ void QGeoProjectionWebMercator::setupCamera()
double verticalHalfFOV = QLocationUtils::degrees(atan(m_aperture));
- QDoubleMatrix4x4 cameraMatrix;
- cameraMatrix.lookAt(m_eye, m_center, m_up);
+ m_cameraMatrix.setToIdentity();
+ m_cameraMatrix.lookAt(m_eye, m_center, m_up);
+ m_cameraMatrix0.setToIdentity();
+ m_cameraMatrix0.lookAt(eye0, QDoubleVector3D(0,0,0), m_up);
QDoubleMatrix4x4 projectionMatrix;
projectionMatrix.frustum(-m_halfWidth, m_halfWidth, -m_halfHeight, m_halfHeight, m_nearPlane, m_farPlane);
@@ -656,10 +707,13 @@ void QGeoProjectionWebMercator::setupCamera()
matScreenTransformation(0,3) = (0.5 + offsetPct.x()) * m_viewportWidth;
matScreenTransformation(1,3) = (0.5 + offsetPct.y()) * m_viewportHeight;
- m_transformation = matScreenTransformation * projectionMatrix * cameraMatrix;
+ m_transformation = matScreenTransformation * projectionMatrix * m_cameraMatrix;
m_quickItemTransformation = m_transformation;
m_transformation.scale(m_sideLengthPixels, m_sideLengthPixels, 1.0);
+ m_transformation0 = matScreenTransformation * projectionMatrix * m_cameraMatrix0;
+ m_transformation0.scale(m_sideLengthPixels, m_sideLengthPixels, 1.0);
+
m_centerNearPlane = m_eye - m_viewNormalized;
m_centerNearPlaneMercator = m_eyeMercator - m_viewNormalized * m_nearPlaneMercator;
diff --git a/src/location/maps/qgeoprojection_p.h b/src/location/maps/qgeoprojection_p.h
index 2e1af8c5..9a75246a 100644
--- a/src/location/maps/qgeoprojection_p.h
+++ b/src/location/maps/qgeoprojection_p.h
@@ -52,6 +52,8 @@
#include <QtLocation/private/qgeocameradata_p.h>
#include <QtPositioning/private/qdoublematrix4x4_p.h>
#include <QtPositioning/QGeoShape>
+#include <QMatrix4x4>
+#include <QTransform>
QT_BEGIN_NAMESPACE
@@ -107,6 +109,17 @@ public:
virtual QGeoShape visibleRegion() const;
virtual bool setBearing(qreal bearing, const QGeoCoordinate &coordinate);
+ virtual QMatrix4x4 projectionTransformation() const = 0; // This brings a mercator coord into the correct viewport coordinate.
+ virtual QMatrix4x4 projectionTransformation_centered() const = 0; // Same as projectionTransformation, but the center of the camera is around 0,0.
+ // Requires subsequent shifting of the geometry to fit such camera.
+ virtual const QMatrix4x4 &qsgTransform() const = 0;
+ virtual QDoubleVector3D centerMercator() const = 0;
+
+ void setItemToWindowTransform(const QTransform &itemToWindowTransform);
+ virtual QTransform itemToWindowTransform() const;
+
+ QTransform m_itemToWindowTransform;
+ mutable bool m_qsgTransformDirty = true;
};
class Q_LOCATION_PRIVATE_EXPORT QGeoProjectionWebMercator : public QGeoProjection
@@ -117,6 +130,11 @@ public:
// From QGeoProjection
double minimumZoom() const override;
+ QMatrix4x4 projectionTransformation() const override;
+ QMatrix4x4 projectionTransformation_centered() const override;
+ const QMatrix4x4 &qsgTransform() const override;
+ QDoubleVector3D centerMercator() const override;
+
double maximumCenterLatitudeAtZoom(const QGeoCameraData &cameraData) const override;
double minimumCenterLatitudeAtZoom(const QGeoCameraData &cameraData) const override;
@@ -144,6 +162,7 @@ public:
QDoubleVector2D geoToMapProjection(const QGeoCoordinate &coordinate) const;
QGeoCoordinate mapProjectionToGeo(const QDoubleVector2D &projection) const;
+ int projectionWrapFactor(const QDoubleVector2D &projection) const;
QDoubleVector2D wrapMapProjection(const QDoubleVector2D &projection) const;
QDoubleVector2D unwrapMapProjection(const QDoubleVector2D &wrappedProjection) const;
@@ -213,7 +232,10 @@ protected:
double m_1_viewportWidth;
double m_1_viewportHeight;
+ QDoubleMatrix4x4 m_cameraMatrix;
+ QDoubleMatrix4x4 m_cameraMatrix0;
QDoubleMatrix4x4 m_transformation;
+ QDoubleMatrix4x4 m_transformation0;
QDoubleMatrix4x4 m_quickItemTransformation;
QDoubleVector3D m_eye;
QDoubleVector3D m_up;
@@ -234,6 +256,7 @@ protected:
// For the clipping region
QDoubleVector3D m_centerMercator;
QDoubleVector3D m_eyeMercator;
+ QDoubleVector3D m_eyeMercator0;
QDoubleVector3D m_viewMercator;
QDoubleVector3D m_upMercator;
QDoubleVector3D m_sideMercator;
@@ -245,6 +268,8 @@ protected:
QList<QDoubleVector2D> m_visibleRegionExpanded;
QList<QDoubleVector2D> m_projectableRegion;
bool m_visibleRegionDirty;
+
+ mutable QMatrix4x4 m_qsgTransform;
QRectF m_visibleArea;
Q_DISABLE_COPY(QGeoProjectionWebMercator)
diff --git a/src/location/maps/qgeotiledmap.cpp b/src/location/maps/qgeotiledmap.cpp
index 74346fdb..e6c91042 100644
--- a/src/location/maps/qgeotiledmap.cpp
+++ b/src/location/maps/qgeotiledmap.cpp
@@ -329,7 +329,7 @@ void QGeoTiledMapPrivate::changeCameraData(const QGeoCameraData &cameraData)
m_mapScene->setCameraData(cam);
updateScene();
- q->sgNodeChanged();
+ q->sgNodeChanged(); // ToDo: explain why emitting twice
}
void QGeoTiledMapPrivate::updateScene()
@@ -371,7 +371,7 @@ void QGeoTiledMapPrivate::setVisibleArea(const QRectF &visibleArea)
if (m_copyrightVisible)
q->evaluateCopyrights(m_mapScene->visibleTiles());
updateScene();
- q->sgNodeChanged();
+ q->sgNodeChanged(); // ToDo: explain why emitting twice
}
QRectF QGeoTiledMapPrivate::visibleArea() const
diff --git a/src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp b/src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp
index cb8dc2be..6dc8ea70 100644
--- a/src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp
+++ b/src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp
@@ -43,6 +43,7 @@
#include <QtPositioning/QGeoPath>
#include <QtPositioning/QGeoPolygon>
#include <QtQml/QJSValue>
+#include <QtLocation/private/qdeclarativecirclemapitem_p_p.h>
namespace {
@@ -93,12 +94,12 @@ QMapbox::Feature featureFromMapCircle(QDeclarativeCircleMapItem *mapItem)
const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(mapItem->map()->geoProjection());
QList<QGeoCoordinate> path;
QGeoCoordinate leftBound;
- QDeclarativeCircleMapItem::calculatePeripheralPoints(path, mapItem->center(), mapItem->radius(), circleSamples, leftBound);
+ QDeclarativeCircleMapItemPrivateCPU::calculatePeripheralPoints(path, mapItem->center(), mapItem->radius(), circleSamples, leftBound);
QList<QDoubleVector2D> pathProjected;
for (const QGeoCoordinate &c : qAsConst(path))
pathProjected << p.geoToMapProjection(c);
- if (QDeclarativeCircleMapItem::crossEarthPole(mapItem->center(), mapItem->radius()))
- mapItem->preserveCircleGeometry(pathProjected, mapItem->center(), mapItem->radius(), p);
+ if (QDeclarativeCircleMapItemPrivateCPU::crossEarthPole(mapItem->center(), mapItem->radius()))
+ QDeclarativeCircleMapItemPrivateCPU::preserveCircleGeometry(pathProjected, mapItem->center(), mapItem->radius(), p);
path.clear();
for (const QDoubleVector2D &c : qAsConst(pathProjected))
path << p.mapProjectionToGeo(c);
diff --git a/src/positioning/qgeopolygon.cpp b/src/positioning/qgeopolygon.cpp
index 301759df..7d8a0ce0 100644
--- a/src/positioning/qgeopolygon.cpp
+++ b/src/positioning/qgeopolygon.cpp
@@ -673,6 +673,7 @@ QGeoShapePrivate *QGeoPolygonPrivateEager::clone() const
void QGeoPolygonPrivateEager::translate(double degreesLatitude, double degreesLongitude)
{
translatePoly(m_path, m_holesList, m_bbox, degreesLatitude, degreesLongitude, m_maxLati, m_minLati);
+ m_leftBoundWrapped = QWebMercator::coordToMercator(m_bbox.topLeft()).x();
m_clipperDirty = true;
}
diff --git a/src/positioning/qlocationutils_p.h b/src/positioning/qlocationutils_p.h
index e2d739e7..563db200 100644
--- a/src/positioning/qlocationutils_p.h
+++ b/src/positioning/qlocationutils_p.h
@@ -258,6 +258,13 @@ public:
return wrapLong(centerLongitude - leftOffset);
}
+ inline static void split_double(double input, float *hipart, float *lopart)
+ {
+ *hipart = (float) input;
+ double delta = input - ((double) *hipart);
+ *lopart = (float) delta;
+ }
+
static qreal metersPerPixel(qreal zoomLevel, const QGeoCoordinate &coordinate)
{
const qreal metersPerTile = earthMeanCircumference() * std::cos(radians(coordinate.latitude())) / std::pow(2, zoomLevel);