summaryrefslogtreecommitdiff
path: root/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2020-03-31 03:03:41 +0200
committerAlex Blasche <alexander.blasche@qt.io>2020-04-02 08:20:37 +0200
commitb06a07cf9fd474e11fbe467047e5fe0322b677f0 (patch)
treeba8b1dd84c3d263b27a1865fff84a659b5273091 /src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
parent4cfed13377ababcfaa7dacb055bcd3dd0f2cf7d4 (diff)
parent29816a3aaa3f368422a3b19983add62673bb6960 (diff)
downloadqtlocation-b06a07cf9fd474e11fbe467047e5fe0322b677f0.tar.gz
Merge 5.15 to dev and fix resulting compile issues
Conflicts: src/imports/location/location.cpp The change fixes the bare minimum of what needs to be done to compile and run. This includes the following issues: 1. Fix build failures as a result of QMetaType changes in qtbase moc now stores the QMetaType of properties as a result of 46f407126ef3e94d59254012cdc34d6a4ad2faf2 in qtbase, which requires full type information about the property type inside the moc generated source file. Many of the property types were forward-declared, and this resulted in build errors like: "invalid application of 'sizeof' to an incomplete type 'QDeclarativeGeoMap'" 2. Adopts QtQML API changes. A private QJSValue ctor was removed. The "replacement" is QJSValuePrivate::fromReturnedValue(..). 3. The mapboxgl 3rdparty backend does not compile at this point in time and seems unmaintained. For the time being, the mapboxgl backend is disabled in the interest of keeping qtlocation closer to dev HEAD of other Qt modules. Change-Id: I756e1c2effb29eaaf96a61a28c1c17338774b77c
Diffstat (limited to 'src/location/declarativemaps/qdeclarativepolylinemapitem.cpp')
-rw-r--r--src/location/declarativemaps/qdeclarativepolylinemapitem.cpp1077
1 files changed, 952 insertions, 125 deletions
diff --git a/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp b/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
index 10f3f0c3..d59704dc 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,10 +60,32 @@
#include <QtPositioning/private/qclipperutils_p.h>
#include <QtPositioning/private/qgeopath_p.h>
+#include <QtQuick/private/qsgmaterialshader_p.h>
#include <array>
+#include <QThreadPool>
+#include <QRunnable>
+#include <QtLocation/private/qgeomapparameter_p.h>
+#include "qgeosimplify_p.h"
QT_BEGIN_NAMESPACE
+struct ThreadPool // to have a thread pool with max 1 thread for geometry processing
+{
+ ThreadPool ()
+ {
+ m_threadPool.setMaxThreadCount(1);
+ }
+
+ void start(QRunnable *runnable, int priority = 0)
+ {
+ m_threadPool.start(runnable, priority);
+ }
+
+ QThreadPool m_threadPool;
+};
+
+Q_GLOBAL_STATIC(ThreadPool, threadPool)
+
static const double kClipperScaleFactor = 281474976710656.0; // 48 bits of precision
@@ -125,7 +152,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 +382,6 @@ void QDeclarativeMapLineProperties::setWidth(qreal width)
emit widthChanged(width_);
}
-struct Vertex
-{
- QVector2D position;
-};
-
QGeoMapPolylineGeometry::QGeoMapPolylineGeometry()
{
}
@@ -661,7 +683,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 +741,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 +769,202 @@ 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);
+
+ // New pointers, some old LOD task might still be running and operating on the old pointers.
+ resetLOD();
+
+ for (const auto &v: qAsConst(wrappedPath)) m_screenVertices->append(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 +976,7 @@ QDeclarativePolylineMapItem::~QDeclarativePolylineMapItem()
*/
void QDeclarativePolylineMapItem::updateAfterLinePropertiesChanged()
{
- // mark dirty just in case we're a width change
- geometry_.markSourceDirty();
- polishAndUpdate();
+ m_d->onLinePropertiesChanged();
}
/*!
@@ -779,11 +985,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 +998,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)
@@ -807,7 +1010,7 @@ void QDeclarativePolylineMapItem::setPath(const QJSValue &value)
}
/*!
- \qmlmethod int MapPolyline::setPath(geopath path)
+ \qmlmethod void MapPolyline::setPath(geopath path)
Sets the \a path using a geopath type.
@@ -817,13 +1020,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 +1033,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 +1053,7 @@ void QDeclarativePolylineMapItem::setPathFromGeoList(const QList<QGeoCoordinate>
*/
int QDeclarativePolylineMapItem::pathLength() const
{
- return geopath_.path().length();
+ return m_geopath.path().length();
}
/*!
@@ -869,11 +1068,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 +1085,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 +1106,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 +1126,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 +1141,7 @@ QGeoCoordinate QDeclarativePolylineMapItem::coordinateAt(int index) const
*/
bool QDeclarativePolylineMapItem::containsCoordinate(const QGeoCoordinate &coordinate)
{
- return geopath_.containsCoordinate(coordinate);
+ return m_geopath.containsCoordinate(coordinate);
}
/*!
@@ -963,13 +1156,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 +1178,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 +1203,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 +1251,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 +1265,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 +1278,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 +1357,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 +1477,608 @@ 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()
+{
+
+}
+
+bool QGeoMapPolylineGeometryOpenGL::allocateAndFillEntries(QSGGeometry *geom,
+ bool closed,
+ unsigned int zoom) const
+{
+ // Select LOD. Generate if not present. Assign it to m_screenVertices;
+ if (m_dataChanged) {
+ // it means that the data really changed.
+ // So synchronously produce LOD 1, and enqueue the requested one if != 0 or 1.
+ // Select 0 if 0 is requested, or 1 in all other cases.
+ selectLODOnDataChanged(zoom, m_bboxLeftBoundWrapped.x());
+ } else {
+ // Data has not changed, but active LOD != requested LOD.
+ // So, if there are no active tasks, try to change to the correct one.
+ if (!selectLODOnLODMismatch(zoom, m_bboxLeftBoundWrapped.x(), closed))
+ return false;
+ }
+
+ const QVector<QDeclarativeGeoMapItemUtils::vec2> &v = *m_screenVertices;
+ if (v.size() < 2) {
+ geom->allocate(0, 0);
+ return true;
+ }
+ 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;
+ }
+ }
+ }
+ return true;
+}
+
+void QGeoMapPolylineGeometryOpenGL::allocateAndFillLineStrip(QSGGeometry *geom,
+ int lod) const
+{
+ // Select LOD. Generate if not present. Assign it to m_screenVertices;
+ Q_UNUSED(lod)
+
+ 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,
+ unsigned int zoom)
+{
+ // 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 || !shape->isLODActive(zoom) || !fill->vertexCount()) { // fill->vertexCount for when node gets destroyed by MapItemBase bcoz of opacity, then recreated.
+ if (shape->allocateAndFillEntries(fill, closed, zoom)) {
+ 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";
+}
+
+QVector<QDeclarativeGeoMapItemUtils::vec2> QGeoMapItemLODGeometry::getSimplified(
+ QVector<QDeclarativeGeoMapItemUtils::vec2> &wrappedPath, // reference as it gets copied in the nested call
+ double leftBoundWrapped,
+ unsigned int zoom)
+{
+ // Try a simplify step
+ QList<QDoubleVector2D> data;
+ for (auto e: wrappedPath)
+ data << e.toDoubleVector2D();
+ const QList<QDoubleVector2D> simplified = QGeoSimplify::geoSimplifyZL(data,
+ leftBoundWrapped,
+ zoom);
+
+ data.clear();
+ QVector<QDeclarativeGeoMapItemUtils::vec2> simple;
+ for (auto e: simplified)
+ simple << e;
+ return simple;
+}
+
+
+bool QGeoMapItemLODGeometry::isLODActive(unsigned int lod) const
+{
+ return m_screenVertices == m_verticesLOD[zoomToLOD(lod)].data();
+}
+
+class PolylineSimplifyTask : public QRunnable
+{
+public:
+ PolylineSimplifyTask(const QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &input, // reference as it gets copied in the nested call
+ const QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &output,
+ double leftBound,
+ unsigned int zoom,
+ QSharedPointer<unsigned int> &working)
+ : m_zoom(zoom)
+ , m_leftBound(leftBound)
+ , m_input(input)
+ , m_output(output)
+ , m_working(working)
+ {
+ Q_ASSERT(!input.isNull());
+ Q_ASSERT(!output.isNull());
+ }
+
+ ~PolylineSimplifyTask() override;
+
+ void run() override
+ {
+ // Skip sending notifications for now. Updated data will be picked up eventually.
+ // ToDo: figure out how to connect a signal from here to a slot in the item.
+ *m_working = QGeoMapPolylineGeometryOpenGL::zoomToLOD(m_zoom);
+ const QVector<QDeclarativeGeoMapItemUtils::vec2> res =
+ QGeoMapPolylineGeometryOpenGL::getSimplified( *m_input,
+ m_leftBound,
+ QGeoMapPolylineGeometryOpenGL::zoomForLOD(m_zoom));
+ *m_output = res;
+ *m_working = 0;
+ }
+
+ unsigned int m_zoom;
+ double m_leftBound;
+ QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > m_input, m_output;
+ QSharedPointer<unsigned int> m_working;
+};
+
+void QGeoMapItemLODGeometry::enqueueSimplificationTask(const QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &input,
+ const QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &output,
+ double leftBound,
+ unsigned int zoom,
+ QSharedPointer<unsigned int> &working)
+{
+ Q_ASSERT(!input.isNull());
+ Q_ASSERT(!output.isNull());
+ PolylineSimplifyTask *task = new PolylineSimplifyTask(input,
+ output,
+ leftBound,
+ zoom,
+ working);
+ threadPool->start(task);
+}
+
+PolylineSimplifyTask::~PolylineSimplifyTask() {}
+
+void QGeoMapItemLODGeometry::selectLOD(unsigned int zoom, double leftBound, bool /* closed */) // closed to tell if this is a polygon or a polyline.
+{
+ unsigned int requestedLod = zoomToLOD(zoom);
+ if (!m_verticesLOD[requestedLod].isNull()) {
+ m_screenVertices = m_verticesLOD[requestedLod].data();
+ } else if (!m_verticesLOD.at(0)->isEmpty()) {
+ // if here, zoomToLOD != 0 and no current working task.
+ // So select the last filled LOD != m_working (lower-bounded by 1,
+ // guaranteed to exist), and enqueue the right one
+ m_verticesLOD[requestedLod] = QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2>>(
+ new QVector<QDeclarativeGeoMapItemUtils::vec2>);
+
+ for (unsigned int i = requestedLod - 1; i >= 1; i--) {
+ if (*m_working != i && !m_verticesLOD[i].isNull()) {
+ m_screenVertices = m_verticesLOD[i].data();
+ break;
+ } else if (i == 1) {
+ // get 1 synchronously if not computed already
+ m_verticesLOD[1] = QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2>>(
+ new QVector<QDeclarativeGeoMapItemUtils::vec2>);
+ *m_verticesLOD[1] = getSimplified( *m_verticesLOD[0],
+ leftBound,
+ zoomForLOD(0));
+ if (requestedLod == 1)
+ return;
+ }
+ }
+
+ enqueueSimplificationTask( m_verticesLOD.at(0),
+ m_verticesLOD[requestedLod],
+ leftBound,
+ zoom,
+ m_working);
+
+ }
+}
+
+void QGeoMapItemLODGeometry::selectLODOnDataChanged(unsigned int zoom, double leftBound) const
+{
+ unsigned int lod = zoomToLOD(zoom);
+ if (lod > 0) {
+ // Generate ZL 1 as fallback for all cases != 0. Do not do if 0 is requested
+ // (= old behavior, LOD disabled)
+ m_verticesLOD[1] = QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2>>(
+ new QVector<QDeclarativeGeoMapItemUtils::vec2>);
+ *m_verticesLOD[1] = getSimplified( *m_verticesLOD[0],
+ leftBound,
+ zoomForLOD(0));
+ }
+ if (lod > 1) {
+ if (!m_verticesLOD[lod])
+ m_verticesLOD[lod] = QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2>>(
+ new QVector<QDeclarativeGeoMapItemUtils::vec2>);
+ enqueueSimplificationTask( m_verticesLOD.at(0),
+ m_verticesLOD[lod],
+ leftBound,
+ zoom,
+ m_working);
+ }
+ m_screenVertices = m_verticesLOD[qMin<unsigned int>(lod, 1)].data(); // return only 0,1 synchronously
+}
+
+unsigned int QGeoMapItemLODGeometry::zoomToLOD(unsigned int zoom)
+{
+ unsigned int res;
+ if (zoom > 20)
+ res = 0;
+ else
+ res = qBound<unsigned int>(3, zoom, 20) / 3; // bound LOD'ing between ZL 3 and 20. Every 3 ZoomLevels
+ return res;
+}
+
+unsigned int QGeoMapItemLODGeometry::zoomForLOD(unsigned int zoom)
+{
+ unsigned int res = (qBound<unsigned int>(3, zoom, 20) / 3) * 3;
+ if (zoom < 6)
+ return res;
+ return res + 1; // give more resolution when closing in
+}
+
QT_END_NAMESPACE