summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/location/geojson_viewer/GeoJsonDelegateMapObject.qml152
-rw-r--r--examples/location/geojson_viewer/main.qml33
-rw-r--r--examples/location/geojson_viewer/qml.qrc1
-rw-r--r--src/3rdparty/geosimplify.js/LICENSE27
-rw-r--r--src/3rdparty/geosimplify.js/qt_attribution.json13
-rw-r--r--src/imports/location/location.cpp2
-rw-r--r--src/location/declarativemaps/declarativemaps.pri2
-rw-r--r--src/location/declarativemaps/qdeclarativecirclemapitem_p_p.h3
-rw-r--r--src/location/declarativemaps/qdeclarativegeomapitembase.cpp27
-rw-r--r--src/location/declarativemaps/qdeclarativegeomapitembase_p.h8
-rw-r--r--src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h6
-rw-r--r--src/location/declarativemaps/qdeclarativepolylinemapitem.cpp207
-rw-r--r--src/location/declarativemaps/qdeclarativepolylinemapitem_p_p.h109
-rw-r--r--src/location/declarativemaps/qdeclarativerectanglemapitem_p_p.h3
-rw-r--r--src/location/declarativemaps/qgeosimplify.cpp313
-rw-r--r--src/location/declarativemaps/qgeosimplify_p.h147
-rw-r--r--src/location/labs/qsg/qmapcircleobjectqsg.cpp2
-rw-r--r--src/location/labs/qsg/qmappolylineobjectqsg.cpp15
-rw-r--r--src/location/labs/qsg/qmappolylineobjectqsg_p_p.h2
-rw-r--r--tests/manual/mapobjects_tester/main.qml38
-rw-r--r--tests/manual/mapobjects_tester/qml.qrc1
21 files changed, 1067 insertions, 44 deletions
diff --git a/examples/location/geojson_viewer/GeoJsonDelegateMapObject.qml b/examples/location/geojson_viewer/GeoJsonDelegateMapObject.qml
new file mode 100644
index 00000000..d1a144c8
--- /dev/null
+++ b/examples/location/geojson_viewer/GeoJsonDelegateMapObject.qml
@@ -0,0 +1,152 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Paolo Angelelli <paolo.angelelli@gmail.com>
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+import QtPositioning 5.12
+import Qt.labs.location 1.0
+import Qt.labs.qmlmodels 1.0
+
+DelegateChooser {
+ id: dc
+ role: "type"
+ property color defaultColor: "grey"
+
+ DelegateChoice {
+ roleValue: "Point"
+ delegate: MapCircleObject {
+ property string geojsonType: "Point"
+ property var props: modelData.properties
+ geoShape: modelData.data
+ radius: 20*1000
+ border.width: 3
+ /* The expression below is equivalent to:
+ ((props !== undefined && props["color"] !== undefined) ? props["color"] :
+ ((parent && parent.props !== undefined && parent.props["color"] !== undefined) ? parent.props["color"] : dc.defaultColor))
+ */
+ color: (props && props.color) || (parent && parent.props && parent.props.color) || dc.defaultColor
+ }
+ }
+
+ DelegateChoice {
+ roleValue: "LineString"
+ delegate: MapPolylineObject {
+ property string geojsonType: "LineString"
+ property var props: modelData.properties
+ geoShape: modelData.data
+ line.width: 4
+ line.color: (props && props.color) || (parent && parent.props && parent.props.color) || dc.defaultColor
+ }
+ }
+
+ DelegateChoice {
+ roleValue: "Polygon"
+ delegate: MapPolygonObject {
+ property string geojsonType: "Polygon"
+ property var props: modelData.properties
+ geoShape: modelData.data
+ color: (props && props.color) || (parent && parent.props && parent.props.color) || dc.defaultColor
+ border.width: 4
+ border.color: 'black'
+ }
+ }
+
+ DelegateChoice {
+ roleValue: "MultiPoint"
+ delegate: MapObjectView {
+ property string geojsonType: "MultiPoint"
+ property var props: modelData.properties
+ model: modelData.data
+ delegate: dc
+ }
+ }
+
+ DelegateChoice {
+ roleValue: "MultiLineString"
+ delegate: MapObjectView {
+ property string geojsonType: "MultiLineString"
+ property var props: modelData.properties
+ model: modelData.data
+ delegate: dc
+ }
+ }
+
+ DelegateChoice {
+ roleValue: "MultiPolygon"
+ delegate: MapObjectView {
+ property string geojsonType: "MultiPolygon"
+ property var props: modelData.properties
+ model: modelData.data
+ delegate: dc
+ }
+ }
+
+ DelegateChoice {
+ roleValue: "GeometryCollection"
+ delegate: MapObjectView {
+ property string geojsonType: "GeometryCollection"
+ property var props: modelData.properties
+ model: modelData.data
+ delegate: dc
+ }
+ }
+
+ // Features are explicitly not generated by the parser, but converted straight to their content + the properties.
+
+ DelegateChoice {
+ roleValue: "FeatureCollection"
+ delegate: MapObjectView {
+ property string geojsonType: "FeatureCollection"
+ property var props: modelData.properties
+ model: modelData.data
+ delegate: dc
+ }
+ }
+}
diff --git a/examples/location/geojson_viewer/main.qml b/examples/location/geojson_viewer/main.qml
index b2fc3524..1b55ef8b 100644
--- a/examples/location/geojson_viewer/main.qml
+++ b/examples/location/geojson_viewer/main.qml
@@ -58,6 +58,7 @@ import QtQuick.Window 2.11
import QtPositioning 5.12
import QtLocation 5.12
import Qt.labs.qmlmodels 1.0
+import Qt.labs.location 1.0
import Qt.GeoJson 1.0
C1.ApplicationWindow {
@@ -144,11 +145,32 @@ C1.ApplicationWindow {
}
}
C1.MenuItem {
- text: "&OpenGL Item backends"
+ text: "OpenGL Item backends"
id: glBackendSelector
checkable: true
checked: false
}
+
+ C1.MenuItem {
+ text: "Map Object Delegates"
+ id: mapObjectsSelector
+ checkable: true
+ checked: false
+
+ onCheckedChanged: {
+ if (checked) {
+ miv.model = undefined
+ map.removeMapItemView(miv)
+ rootMoV.addMapObject(mov)
+ mov.model = geoJsoner.model
+ } else {
+ mov.model = undefined
+ rootMoV.removeMapObject(mov)
+ map.addMapItemView(miv)
+ miv.model = geoJsoner.model
+ }
+ }
+ }
}
}
@@ -166,6 +188,11 @@ C1.ApplicationWindow {
}
}
+ MapObjectView {
+ id: mov
+ delegate: GeoJsonDelegateMapObject {}
+ }
+
Map {
id: map
anchors.fill: parent
@@ -173,6 +200,10 @@ C1.ApplicationWindow {
plugin: Plugin { name: "osm" }
zoomLevel: 4
+ MapObjectView {
+ id: rootMoV
+ }
+
MapItemView {
id: miv
model: geoJsoner.model
diff --git a/examples/location/geojson_viewer/qml.qrc b/examples/location/geojson_viewer/qml.qrc
index ea443bb0..794a2093 100644
--- a/examples/location/geojson_viewer/qml.qrc
+++ b/examples/location/geojson_viewer/qml.qrc
@@ -2,5 +2,6 @@
<qresource prefix="/">
<file>main.qml</file>
<file>GeoJsonDelegate.qml</file>
+ <file>GeoJsonDelegateMapObject.qml</file>
</qresource>
</RCC>
diff --git a/src/3rdparty/geosimplify.js/LICENSE b/src/3rdparty/geosimplify.js/LICENSE
new file mode 100644
index 00000000..bd04cc24
--- /dev/null
+++ b/src/3rdparty/geosimplify.js/LICENSE
@@ -0,0 +1,27 @@
+Qt port of geosimplify.js, https://github.com/mapbox/geosimplify-js
+
+Copyright (c) 2017, Daniel Patterson
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---------------------------------
+Based on simplify-js by Vladimir Agafonkin - http://mourner.github.io/simplify-js/
diff --git a/src/3rdparty/geosimplify.js/qt_attribution.json b/src/3rdparty/geosimplify.js/qt_attribution.json
new file mode 100644
index 00000000..bc8046e0
--- /dev/null
+++ b/src/3rdparty/geosimplify.js/qt_attribution.json
@@ -0,0 +1,13 @@
+{
+ "Id": "geosimplify-js",
+ "Name": "geosimplify-js polyline simplification library",
+ "QDocModule": "qtlocation",
+ "QtUsage": "Used in the QML plugin of Qt Location.",
+
+ "Description": "Based on https://github.com/mourner/simplify-js, geosimplify-js fixes the problem that the simple pythagorean measure used in simplify-js changes size if you simply give it longitude/latitude sequences to simplify.",
+ "Homepage": "https://github.com/mapbox/geosimplify-js",
+ "LicenseId": "geosimplify-js",
+ "License": "geosimplify-js License",
+ "LicenseFile": "LICENSE",
+ "Copyright": "Copyright (c) 2017 Daniel Patterson"
+}
diff --git a/src/imports/location/location.cpp b/src/imports/location/location.cpp
index 7876e45a..01b6fb62 100644
--- a/src/imports/location/location.cpp
+++ b/src/imports/location/location.cpp
@@ -211,6 +211,8 @@ public:
qmlRegisterType<QDeclarativePolygonMapItem, 15>(uri, major, minor, "MapPolygon");
qmlRegisterType<QDeclarativeRectangleMapItem, 15>(uri, major, minor, "MapRectangle");
qmlRegisterType<QDeclarativeCircleMapItem, 15>(uri, major, minor, "MapCircle");
+ qmlRegisterUncreatableType<QDeclarativeGeoMapItemBase, 15>(uri, major, minor, "GeoMapItemBase",
+ QStringLiteral("GeoMapItemBase is not intended instantiable by developer."));
// 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 c2afe9a4..e2a922f4 100644
--- a/src/location/declarativemaps/declarativemaps.pri
+++ b/src/location/declarativemaps/declarativemaps.pri
@@ -34,6 +34,7 @@ PRIVATE_HEADERS += \
declarativemaps/qdeclarativepolygonmapitem_p_p.h \
declarativemaps/qdeclarativerectanglemapitem_p_p.h \
declarativemaps/qdeclarativecirclemapitem_p_p.h \
+ declarativemaps/qgeosimplify_p.h \
declarativemaps/qquickgeomapgesturearea_p.h
SOURCES += \
@@ -63,6 +64,7 @@ SOURCES += \
declarativemaps/qgeomapobject.cpp \
declarativemaps/qdeclarativegeomapitemutils.cpp \
declarativemaps/qparameterizableobject.cpp \
+ declarativemaps/qgeosimplify.cpp \
declarativemaps/qquickgeomapgesturearea.cpp
load(qt_build_paths)
diff --git a/src/location/declarativemaps/qdeclarativecirclemapitem_p_p.h b/src/location/declarativemaps/qdeclarativecirclemapitem_p_p.h
index 71a6b6a4..4cf42173 100644
--- a/src/location/declarativemaps/qdeclarativecirclemapitem_p_p.h
+++ b/src/location/declarativemaps/qdeclarativecirclemapitem_p_p.h
@@ -397,7 +397,8 @@ public:
combinedMatrix,
cameraCenter,
Qt::SquareCap,
- true);
+ true,
+ 30); // No LOD for circles
m_borderGeometry.setPreserveGeometry(false);
m_borderGeometry.markClean();
} else {
diff --git a/src/location/declarativemaps/qdeclarativegeomapitembase.cpp b/src/location/declarativemaps/qdeclarativegeomapitembase.cpp
index d1a34f1e..70b67828 100644
--- a/src/location/declarativemaps/qdeclarativegeomapitembase.cpp
+++ b/src/location/declarativemaps/qdeclarativegeomapitembase.cpp
@@ -218,6 +218,33 @@ void QDeclarativeGeoMapItemBase::setAutoFadeIn(bool fadeIn)
polishAndUpdate();
}
+int QDeclarativeGeoMapItemBase::lodThreshold() const
+{
+ return m_lodThreshold;
+}
+
+void QDeclarativeGeoMapItemBase::setLodThreshold(int lt)
+{
+ if (lt == m_lodThreshold)
+ return;
+ m_lodThreshold = lt;
+ update();
+}
+
+/*!
+ \internal
+
+ This returns the zoom level to be used when requesting the LOD.
+ Essentially it clamps to m_lodThreshold, and if above, it selects
+ a ZL higher than the maximum LODable level.
+*/
+unsigned int QDeclarativeGeoMapItemBase::zoomForLOD(int zoom) const
+{
+ if (zoom >= m_lodThreshold)
+ return 30; // some arbitrarily large zoom
+ return uint(zoom);
+}
+
/*!
\internal
*/
diff --git a/src/location/declarativemaps/qdeclarativegeomapitembase_p.h b/src/location/declarativemaps/qdeclarativegeomapitembase_p.h
index 65f111f6..61a67f59 100644
--- a/src/location/declarativemaps/qdeclarativegeomapitembase_p.h
+++ b/src/location/declarativemaps/qdeclarativegeomapitembase_p.h
@@ -85,6 +85,8 @@ class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapItemBase : public QQuickItem
Q_PROPERTY(QGeoShape geoShape READ geoShape WRITE setGeoShape STORED false )
Q_PROPERTY(bool autoFadeIn READ autoFadeIn WRITE setAutoFadeIn REVISION 14)
+ Q_PROPERTY(int lodThreshold READ lodThreshold WRITE setLodThreshold NOTIFY lodThresholdChanged REVISION 15)
+
public:
explicit QDeclarativeGeoMapItemBase(QQuickItem *parent = 0);
virtual ~QDeclarativeGeoMapItemBase();
@@ -100,6 +102,10 @@ public:
bool autoFadeIn() const;
void setAutoFadeIn(bool fadeIn);
+ int lodThreshold() const;
+ void setLodThreshold(int lt);
+ unsigned int zoomForLOD(int zoom) const;
+
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
virtual QSGNode *updateMapItemPaintNode(QSGNode *, UpdatePaintNodeData *);
@@ -129,6 +135,7 @@ Q_SIGNALS:
void mapItemOpacityChanged();
Q_REVISION(12) void addTransitionFinished();
Q_REVISION(12) void removeTransitionFinished();
+ void lodThresholdChanged();
protected Q_SLOTS:
virtual void afterChildrenChanged();
@@ -158,6 +165,7 @@ private:
QScopedPointer<QDeclarativeGeoMapItemTransitionManager> m_transitionManager;
bool m_autoFadeIn = true;
+ int m_lodThreshold = 0;
friend class QDeclarativeGeoMap;
friend class QDeclarativeGeoMapItemView;
diff --git a/src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h b/src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h
index 83f1100e..8d566e69 100644
--- a/src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h
+++ b/src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h
@@ -116,6 +116,8 @@ public:
void allocateAndFillPolygon(QSGGeometry *geom) const
{
+
+
const QVector<QDeclarativeGeoMapItemUtils::vec2> &vx = m_screenVertices;
const QVector<quint32> &ix = m_screenIndices;
@@ -613,7 +615,9 @@ public:
combinedMatrix,
cameraCenter,
Qt::SquareCap,
- true);
+ true,
+ 30); // No LOD for polygons just yet.
+ // First figure out what to do with holes.
m_borderGeometry.setPreserveGeometry(false);
m_borderGeometry.markClean();
} else {
diff --git a/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp b/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
index 7e484122..f914b36d 100644
--- a/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
+++ b/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
@@ -62,10 +62,30 @@
#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
@@ -774,6 +794,7 @@ void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const
QList<QDoubleVector2D> wrappedPath;
QDeclarativeGeoMapItemUtils::wrapPath(poly.path(), geoLeftBound_, p,
wrappedPath, &leftBoundWrapped);
+
const QGeoRectangle &boundingRectangle = poly.boundingGeoRectangle();
updateSourcePoints(p, wrappedPath, boundingRectangle);
}
@@ -802,8 +823,10 @@ void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoProjectionWebMe
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;
+ // 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;
@@ -1474,7 +1497,7 @@ void MapPolylineNodeOpenGLLineStrip::update(const QColor &fillColor,
const QDoubleVector3D &center,
const Qt::PenCapStyle /*capStyle*/)
{
- if (shape->m_screenVertices.size() < 2) {
+ if (shape->m_screenVertices->size() < 2) {
setSubtreeBlocked(true);
return;
} else {
@@ -1585,13 +1608,27 @@ MapPolylineNodeOpenGLExtruded::~MapPolylineNodeOpenGLExtruded()
}
-void QGeoMapPolylineGeometryOpenGL::allocateAndFillEntries(QSGGeometry *geom, bool closed) const
+bool QGeoMapPolylineGeometryOpenGL::allocateAndFillEntries(QSGGeometry *geom,
+ bool closed,
+ unsigned int zoom) const
{
- // ToDo: add dirty flag.
- const QVector<QDeclarativeGeoMapItemUtils::vec2> &v = m_screenVertices;
+ // 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;
+ return true;
}
const int numSegments = (v.size() - 1);
@@ -1646,11 +1683,16 @@ void QGeoMapPolylineGeometryOpenGL::allocateAndFillEntries(QSGGeometry *geom, bo
}
}
}
+ return true;
}
-void QGeoMapPolylineGeometryOpenGL::allocateAndFillLineStrip(QSGGeometry *geom) const
+void QGeoMapPolylineGeometryOpenGL::allocateAndFillLineStrip(QSGGeometry *geom,
+ int lod) const
{
- const QVector<QDeclarativeGeoMapItemUtils::vec2> &vx = m_screenVertices;
+ // 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();
@@ -1664,10 +1706,11 @@ void MapPolylineNodeOpenGLExtruded::update(const QColor &fillColor,
const QMatrix4x4 geoProjection,
const QDoubleVector3D center,
const Qt::PenCapStyle capStyle,
- bool closed)
+ bool closed,
+ unsigned int zoom)
{
// shape->size() == number of triangles
- if (shape->m_screenVertices.size() < 2
+ if (shape->m_screenVertices->size() < 2
|| lineWidth < 0.5 || fillColor.alpha() == 0) { // number of points
setSubtreeBlocked(true);
return;
@@ -1676,10 +1719,11 @@ void MapPolylineNodeOpenGLExtruded::update(const QColor &fillColor,
}
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;
+ 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
@@ -1874,5 +1918,136 @@ const char *MapPolylineShaderExtruded::vertexShaderMiteredSegments() const
"}\n";
}
-QT_END_NAMESPACE
+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(QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &input, // reference as it gets copied in the nested call
+ 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)
+ {
+ }
+
+ ~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);
+ *m_output = QGeoMapPolylineGeometryOpenGL::getSimplified( *m_input,
+ m_leftBound,
+ QGeoMapPolylineGeometryOpenGL::zoomForLOD(m_zoom));
+ *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(QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &input,
+ QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &output,
+ double leftBound,
+ unsigned int zoom,
+ QSharedPointer<unsigned int> &working)
+{
+ 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);
+
+ }
+}
+
+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
diff --git a/src/location/declarativemaps/qdeclarativepolylinemapitem_p_p.h b/src/location/declarativemaps/qdeclarativepolylinemapitem_p_p.h
index d37f77ad..35d52790 100644
--- a/src/location/declarativemaps/qdeclarativepolylinemapitem_p_p.h
+++ b/src/location/declarativemaps/qdeclarativepolylinemapitem_p_p.h
@@ -62,6 +62,8 @@
#include <QtPositioning/QGeoCircle>
#include <QtPositioning/private/qdoublevector2d_p.h>
#include <QtCore/QScopedValueRollback>
+#include <QSharedPointer>
+#include <array>
QT_BEGIN_NAMESPACE
@@ -211,14 +213,93 @@ protected:
QSGGeometry geometry_;
};
-class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometryOpenGL : public QGeoMapItemGeometry
+class Q_LOCATION_PRIVATE_EXPORT QGeoMapItemLODGeometry
+{
+public:
+ mutable std::array<QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2>>, 7> m_verticesLOD; // fix it to 7,
+ // do not allow simplifications beyond ZL 20. This could actually be limited even further
+ mutable QVector<QDeclarativeGeoMapItemUtils::vec2> *m_screenVertices;
+ mutable QSharedPointer<unsigned int> m_working;
+
+ QGeoMapItemLODGeometry()
+ {
+ resetLOD();
+ }
+
+ void resetLOD()
+ {
+ // New pointer, some old LOD task might still be running and operating on the old pointers.
+ m_verticesLOD[0] = QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2>>(
+ new QVector<QDeclarativeGeoMapItemUtils::vec2>);
+ for (unsigned int i = 1; i < m_verticesLOD.size(); ++i)
+ m_verticesLOD[i] = nullptr; // allocate on first use
+ m_screenVertices = m_verticesLOD.front().data(); // resetting pointer to data to be LOD 0
+ }
+
+ static unsigned int zoomToLOD(unsigned int zoom);
+
+ static unsigned int zoomForLOD(unsigned int zoom);
+
+ bool isLODActive(unsigned int lod) const;
+
+ void selectLOD(unsigned int zoom, double leftBound, bool /*closed*/);
+
+ static QVector<QDeclarativeGeoMapItemUtils::vec2> getSimplified (
+ QVector<QDeclarativeGeoMapItemUtils::vec2> &wrappedPath,
+ double leftBoundWrapped,
+ unsigned int zoom);
+
+ static void enqueueSimplificationTask(QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &input, // reference as it gets copied in the nested call
+ QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &output,
+ double leftBound,
+ unsigned int zoom,
+ QSharedPointer<unsigned int> &working);
+
+ void 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) {
+ enqueueSimplificationTask( m_verticesLOD.at(0),
+ m_verticesLOD[zoomToLOD(zoom)],
+ leftBound,
+ zoom,
+ m_working);
+ }
+ m_screenVertices = m_verticesLOD[qMin<unsigned int>(lod, 1)].data();
+ }
+
+ bool selectLODOnLODMismatch(unsigned int zoom, double leftBound, bool closed) const
+ {
+ if (*m_working > 0) {
+ return false;
+ }
+ const_cast<QGeoMapItemLODGeometry *>(this)->selectLOD(zoom,
+ leftBound,
+ closed);
+ return true;
+ }
+};
+
+class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometryOpenGL : public QGeoMapItemGeometry, public QGeoMapItemLODGeometry
{
public:
typedef struct {
QList<QDoubleVector2D> wrappedBboxes;
} WrappedPolyline;
- QGeoMapPolylineGeometryOpenGL() {}
+ QGeoMapPolylineGeometryOpenGL()
+ {
+ m_working = QSharedPointer<unsigned int>(new unsigned int(0));
+ }
void updateSourcePoints(const QGeoMap &map,
const QGeoPolygon &poly);
@@ -242,8 +323,11 @@ public:
void updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth = 0.0);
- void allocateAndFillEntries(QSGGeometry *geom, bool closed = false) const;
- void allocateAndFillLineStrip(QSGGeometry *geom) const;
+ bool allocateAndFillEntries(QSGGeometry *geom,
+ bool closed = false,
+ unsigned int zoom = 0) const;
+ void allocateAndFillLineStrip(QSGGeometry *geom,
+ int lod = 0) const;
bool contains(const QPointF &point) const override
{
@@ -270,16 +354,17 @@ public:
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()));
+ if (m_screenVertices->size())
+ a = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(m_screenVertices->first().toDoubleVector2D()));
QDoubleVector2D b;
- for (int i = 1; i < m_screenVertices.size(); ++i)
+ for (int i = 1; i < m_screenVertices->size(); ++i)
{
if (!a.isFinite()) {
- a = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(m_screenVertices.at(i).toDoubleVector2D()));
+ a = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(m_screenVertices->at(i).toDoubleVector2D()));
continue;
}
- b = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(m_screenVertices.at(i).toDoubleVector2D()));
+ b = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(m_screenVertices->at(i).toDoubleVector2D()));
if (!b.isFinite()) {
a = b;
continue;
@@ -299,7 +384,6 @@ public:
}
public:
- QVector<QDeclarativeGeoMapItemUtils::vec2> m_screenVertices;
QDoubleVector2D m_bboxLeftBoundWrapped;
QVector<WrappedPolyline> m_wrappedPolygons;
int m_wrapOffset;
@@ -493,7 +577,8 @@ public:
const QMatrix4x4 geoProjection,
const QDoubleVector3D center,
const Qt::PenCapStyle capStyle = Qt::FlatCap,
- bool closed = false);
+ bool closed = false,
+ unsigned int zoom = 30);
static const QSGGeometry::AttributeSet &attributesMapPolylineTriangulated();
@@ -806,7 +891,9 @@ public:
&m_geometry,
combinedMatrix,
cameraCenter,
- m_penCapStyle);
+ m_penCapStyle,
+ false,
+ m_poly.zoomForLOD(int(map->cameraData().zoomLevel())));
m_geometry.setPreserveGeometry(false);
m_geometry.markClean();
m_poly.m_dirtyMaterial = false;
diff --git a/src/location/declarativemaps/qdeclarativerectanglemapitem_p_p.h b/src/location/declarativemaps/qdeclarativerectanglemapitem_p_p.h
index 520abdf3..65d2f618 100644
--- a/src/location/declarativemaps/qdeclarativerectanglemapitem_p_p.h
+++ b/src/location/declarativemaps/qdeclarativerectanglemapitem_p_p.h
@@ -364,7 +364,8 @@ public:
combinedMatrix,
cameraCenter,
Qt::SquareCap,
- true);
+ true,
+ 30); // No LOD for rectangles
m_borderGeometry.setPreserveGeometry(false);
m_borderGeometry.markClean();
} else {
diff --git a/src/location/declarativemaps/qgeosimplify.cpp b/src/location/declarativemaps/qgeosimplify.cpp
new file mode 100644
index 00000000..9414a1cf
--- /dev/null
+++ b/src/location/declarativemaps/qgeosimplify.cpp
@@ -0,0 +1,313 @@
+/****************************************************************************
+**
+** Qt adaptation of geosimplify-js
+** Copyright (C) 2017 Daniel Patterson
+** See 3rdParty/geosimplify.js for the original license.
+**
+** 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 "qgeosimplify_p.h"
+#include <QtPositioning/private/qlocationutils_p.h>
+
+QT_BEGIN_NAMESPACE
+
+double QGeoSimplify::getDist(const QGeoCoordinate &p1, const QGeoCoordinate &p2)
+{
+ return p1.distanceTo(p2);
+}
+
+QDoubleVector2D QGeoSimplify::closestPoint(const QDoubleVector2D &p, const QDoubleVector2D &a, const QDoubleVector2D &b)
+{
+ if (a == b)
+ return a;
+
+ const double u = ((p.x() - a.x()) * (b.x() - a.x()) + (p.y() - a.y()) * (b.y() - a.y()) ) / (b - a).lengthSquared();
+ const 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 candidate;
+}
+
+QGeoCoordinate QGeoSimplify::closestPoint(const QGeoCoordinate &pc, const QGeoCoordinate &ac, const QGeoCoordinate &bc, const double &leftBound)
+{
+ QDoubleVector2D p = QWebMercator::coordToMercator(pc);
+ if (p.x() < leftBound)
+ p.setX(p.x() + leftBound); // unwrap X
+
+ QDoubleVector2D a = QWebMercator::coordToMercator(ac);
+ if (a.x() < leftBound)
+ a.setX(a.x() + leftBound); // unwrap X
+
+ QDoubleVector2D b = QWebMercator::coordToMercator(bc);
+ if (b.x() < leftBound)
+ b.setX(b.x() + leftBound); // unwrap X
+
+ QDoubleVector2D intersection = closestPoint(p, a, b);
+ if (intersection.x() > 1.0)
+ intersection.setX(intersection.x() - leftBound); // wrap X
+
+ const QGeoCoordinate closest = QWebMercator::mercatorToCoord(intersection);
+ return closest;
+}
+
+double QGeoSimplify::getSegDist(const QGeoCoordinate &pc, const QGeoCoordinate &ac, const QGeoCoordinate &bc, const double &leftBound)
+{
+ const QGeoCoordinate closest = closestPoint(pc, ac, bc, leftBound);
+ const double distanceMeters = pc.distanceTo(closest);
+ return distanceMeters;
+}
+
+double QGeoSimplify::getSegDist(const QDoubleVector2D &p, const QDoubleVector2D &a, const QDoubleVector2D &b, const double &leftBound)
+{
+ QDoubleVector2D intersection = closestPoint(p, a, b);
+ return getDist(intersection, p, leftBound);
+}
+
+void QGeoSimplify::simplifyDPStep(const QList<QGeoCoordinate> &points, const double &leftBound, int first, int last, double offsetTolerance, QList<QGeoCoordinate> &simplified)
+{
+ double maxDistanceFound = offsetTolerance;
+ int index = 0;
+
+ for (int i = first + 1; i < last; i++) {
+ const double distance = getSegDist(points.at(i),
+ points.at(first),
+ points.at(last),
+ leftBound);
+
+ if (distance > maxDistanceFound) {
+ index = i;
+ maxDistanceFound = distance;
+ }
+ }
+
+ if (index > 0) {
+ if (index - first > 1)
+ simplifyDPStep(points,
+ leftBound,
+ first,
+ index,
+ offsetTolerance,
+ simplified);
+ simplified.append(points.at(index));
+ if (last - index > 1)
+ simplifyDPStep(points,
+ leftBound,
+ index,
+ last,
+ offsetTolerance,
+ simplified);
+ }
+}
+
+double QGeoSimplify::getDist(QDoubleVector2D a, QDoubleVector2D b, const double &leftBound)
+{
+ if (a.x() > 1.0)
+ a.setX(a.x() - leftBound); // wrap X
+ if (b.x() > 1.0)
+ b.setX(b.x() - leftBound); // wrap X
+ return QWebMercator::mercatorToCoord(a).distanceTo(
+ QWebMercator::mercatorToCoord(b));
+}
+
+void QGeoSimplify::simplifyDPStep(const QList<QDoubleVector2D> &points,
+ const double &leftBound,
+ int first,
+ int last,
+ double offsetTolerance,
+ QList<QDoubleVector2D> &simplified)
+{
+ double maxDistanceFound = offsetTolerance;
+ int index = 0;
+
+ for (int i = first + 1; i < last; i++) {
+ const double distance = getSegDist(points.at(i),
+ points.at(first),
+ points.at(last),
+ leftBound);
+
+ if (distance > maxDistanceFound) {
+ index = i;
+ maxDistanceFound = distance;
+ }
+ }
+
+ if (index > 0) {
+ if (index - first > 1)
+ simplifyDPStep(points,
+ leftBound,
+ first,
+ index,
+ offsetTolerance,
+ simplified);
+ simplified.append(points.at(index));
+ if (last - index > 1)
+ simplifyDPStep(points,
+ leftBound,
+ index,
+ last,
+ offsetTolerance,
+ simplified);
+ }
+}
+
+static double pixelDistanceAtZoomAndLatitude(int zoom, double latitude)
+{
+ const double den = double((1 << (zoom + 8)));
+ const double pixelDist = (QLocationUtils::earthMeanCircumference() *
+ std::cos(QLocationUtils::radians(latitude))) / den;
+ return pixelDist;
+}
+
+static QGeoCoordinate unwrappedToGeo(QDoubleVector2D p, double leftBound)
+{
+ if (p.x() > 1.0)
+ p.setX(p.x() - leftBound);
+ return QWebMercator::mercatorToCoord(p);
+}
+
+void QGeoSimplify::simplifyDPStepZL(const QList<QDoubleVector2D> &points,
+ const double &leftBound,
+ int first,
+ int last,
+ int zoomLevel,
+ QList<QDoubleVector2D> &simplified)
+{
+ const QGeoCoordinate firstC = unwrappedToGeo(points.at(first), leftBound);
+ const QGeoCoordinate lastC = unwrappedToGeo(points.at(last), leftBound);
+ double maxDistanceFound = (pixelDistanceAtZoomAndLatitude(zoomLevel, firstC.latitude())
+ + pixelDistanceAtZoomAndLatitude(zoomLevel, lastC.latitude())) * 0.5;
+ int index = 0;
+
+ for (int i = first + 1; i < last; i++) {
+ const double distance = getSegDist(points.at(i),
+ points.at(first),
+ points.at(last),
+ leftBound);
+
+ if (distance > maxDistanceFound) {
+ index = i;
+ maxDistanceFound = distance;
+ }
+ }
+
+ if (index > 0) {
+ if (index - first > 1)
+ simplifyDPStepZL(points,
+ leftBound,
+ first,
+ index,
+ zoomLevel,
+ simplified);
+ simplified.append(points.at(index));
+ if (last - index > 1)
+ simplifyDPStepZL(points,
+ leftBound,
+ index,
+ last,
+ zoomLevel,
+ simplified);
+ }
+}
+
+QList<QGeoCoordinate> QGeoSimplify::simplifyDouglasPeucker(const QList<QGeoCoordinate> &points,
+ const double &leftBound,
+ double offsetTolerance) {
+ const int last = points.size() - 1;
+ QList<QGeoCoordinate> simplified { points.first() };
+ simplifyDPStep(points, leftBound, 0, last, offsetTolerance, simplified);
+ simplified.append(points.at(last));
+ return simplified;
+}
+
+QList<QDoubleVector2D> QGeoSimplify::simplifyDouglasPeucker(const QList<QDoubleVector2D> &points,
+ const double &leftBound,
+ double offsetTolerance) {
+ const int last = points.size() - 1;
+ QList<QDoubleVector2D> simplified { points.first() };
+ simplifyDPStep(points, leftBound, 0, last, offsetTolerance, simplified);
+ simplified.append(points.at(last));
+ return simplified;
+}
+
+QList<QDoubleVector2D> QGeoSimplify::simplifyDouglasPeuckerZL(const QList<QDoubleVector2D> &points,
+ const double &leftBound,
+ int zoomLevel)
+{
+ const int last = points.size() - 1;
+ QList<QDoubleVector2D> simplified { points.first() };
+ simplifyDPStepZL(points, leftBound, 0, last, zoomLevel, simplified);
+ simplified.append(points.at(last));
+ return simplified;
+}
+
+QList<QGeoCoordinate> QGeoSimplify::geoSimplify(const QList<QGeoCoordinate> &points,
+ const double &leftBound,
+ double offsetTolerance) // also in meters
+{
+ if (points.size() <= 2)
+ return points;
+ return simplifyDouglasPeucker(points,
+ leftBound,
+ offsetTolerance);
+}
+
+QList<QDoubleVector2D> QGeoSimplify::geoSimplify(const QList<QDoubleVector2D> &points,
+ const double &leftBound,
+ double offsetTolerance) // also in meters
+{
+ if (points.size() <= 2)
+ return points;
+ return simplifyDouglasPeucker(points,
+ leftBound,
+ offsetTolerance);
+}
+
+QList<QDoubleVector2D> QGeoSimplify::geoSimplifyZL(const QList<QDoubleVector2D> &points,
+ const double &leftBound,
+ int zoomLevel)
+{
+ if (points.size() <= 2)
+ return points;
+ return simplifyDouglasPeuckerZL(points,
+ leftBound,
+ zoomLevel);
+}
+
+
+QT_END_NAMESPACE
+
diff --git a/src/location/declarativemaps/qgeosimplify_p.h b/src/location/declarativemaps/qgeosimplify_p.h
new file mode 100644
index 00000000..19445552
--- /dev/null
+++ b/src/location/declarativemaps/qgeosimplify_p.h
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Qt adaptation of geosimplify.js, https://github.com/mapbox/geosimplify-js, (c) 2017, Mapbox
+**
+** 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 QGEOSIMPLIFY_P_H
+#define QGEOSIMPLIFY_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 <QtPositioning/QGeoCoordinate>
+#include <QtPositioning/private/qdoublevector2d_p.h>
+#include <QtPositioning/private/qwebmercator_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QGeoSimplify {
+protected:
+ // Distance between two points in metres
+ static double getDist(const QGeoCoordinate &p1, const QGeoCoordinate &p2);
+
+ // p, a and b are intended as "unwrapped" around the left bound
+ static QDoubleVector2D closestPoint( const QDoubleVector2D &p,
+ const QDoubleVector2D &a,
+ const QDoubleVector2D &b);
+
+ static QGeoCoordinate closestPoint( const QGeoCoordinate &pc,
+ const QGeoCoordinate &ac,
+ const QGeoCoordinate &bc,
+ const double &leftBound);
+
+ // Distance from a point to a segment (line between two points) in metres
+ static double getSegDist(const QGeoCoordinate &pc,
+ const QGeoCoordinate &ac,
+ const QGeoCoordinate &bc,
+ const double &leftBound);
+
+ // doublevectors Intended as wrapped
+ static double getSegDist(const QDoubleVector2D &p,
+ const QDoubleVector2D &a,
+ const QDoubleVector2D &b,
+ const double &leftBound);
+
+ static void simplifyDPStep(const QList<QGeoCoordinate> &points,
+ const double &leftBound,
+ int first,
+ int last,
+ double offsetTolerance,
+ QList<QGeoCoordinate> &simplified);
+
+ static double getDist(QDoubleVector2D a,
+ QDoubleVector2D b,
+ const double &leftBound);
+
+ static void simplifyDPStep(const QList<QDoubleVector2D> &points,
+ const double &leftBound,
+ int first,
+ int last,
+ double offsetTolerance,
+ QList<QDoubleVector2D> &simplified);
+
+ static void simplifyDPStepZL(const QList<QDoubleVector2D> &points,
+ const double &leftBound,
+ int first,
+ int last,
+ int zoomLevel,
+ QList<QDoubleVector2D> &simplified);
+
+ // simplification using Ramer-Douglas-Peucker algorithm
+ static QList<QGeoCoordinate> simplifyDouglasPeucker(const QList<QGeoCoordinate> &points,
+ const double &leftBound,
+ double offsetTolerance);
+
+ static QList<QDoubleVector2D> simplifyDouglasPeucker(const QList<QDoubleVector2D> &points,
+ const double &leftBound,
+ double offsetTolerance);
+
+ static QList<QDoubleVector2D> simplifyDouglasPeuckerZL(const QList<QDoubleVector2D> &points,
+ const double &leftBound,
+ int zoomLevel);
+
+public:
+ /*
+ offsetTolerance - how far outside the straight line a point
+ needs to be for it to be "kept"
+ */
+ static QList<QGeoCoordinate> geoSimplify(const QList<QGeoCoordinate> &points,
+ const double &leftBound,
+ double offsetTolerance); // in meters
+
+ static QList<QDoubleVector2D> geoSimplify(const QList<QDoubleVector2D> &points,
+ const double &leftBound,
+ double offsetTolerance); // in meters
+
+ // This overload tries to be adaptive in the offsetTolerance across latitudes,
+ // and return a simplification adequate for the given zoomLevel.
+ static QList<QDoubleVector2D> geoSimplifyZL(const QList<QDoubleVector2D> &points,
+ const double &leftBound,
+ int zoomLevel); // in meters
+};
+
+QT_END_NAMESPACE
+
+#endif // QGEOSIMPLIFY_P_H
diff --git a/src/location/labs/qsg/qmapcircleobjectqsg.cpp b/src/location/labs/qsg/qmapcircleobjectqsg.cpp
index 5b1f5361..750b20f2 100644
--- a/src/location/labs/qsg/qmapcircleobjectqsg.cpp
+++ b/src/location/labs/qsg/qmapcircleobjectqsg.cpp
@@ -297,7 +297,7 @@ QSGNode *QMapCircleObjectPrivateQSG::updateMapObjectNodeGL(QSGNode *oldNode,
combinedMatrix,
cameraCenter,
Qt::SquareCap,
- true);
+ true); // No LOD for circles ATM
m_dataGL->m_borderGeometry.setPreserveGeometry(false);
m_dataGL->m_borderGeometry.markClean();
}
diff --git a/src/location/labs/qsg/qmappolylineobjectqsg.cpp b/src/location/labs/qsg/qmappolylineobjectqsg.cpp
index 817d4df5..8efbfc2f 100644
--- a/src/location/labs/qsg/qmappolylineobjectqsg.cpp
+++ b/src/location/labs/qsg/qmappolylineobjectqsg.cpp
@@ -102,6 +102,18 @@ void QMapPolylineObjectPrivateQSG::updateGeometry()
m_borderGeometry.m_wrapOffset = p.projectionWrapFactor(m_leftBoundMercator) + 1;
}
+/*!
+ \internal
+*/
+unsigned int QMapPolylineObjectPrivateQSG::zoomForLOD(int zoom) const
+{
+ // LOD Threshold currently fixed to 12 for MapPolylineObject(QSG).
+ // ToDo: Consider allowing to change this via DynamicParameter.
+ if (zoom >= 12)
+ return 30;
+ return uint(zoom);
+}
+
QSGNode *QMapPolylineObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode,
VisibleNode **visibleNode,
QSGNode *root,
@@ -127,7 +139,8 @@ QSGNode *QMapPolylineObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode,
combinedMatrix,
cameraCenter,
Qt::SquareCap,
- true);
+ true,
+ zoomForLOD(int(m_map->cameraData().zoomLevel())));
m_borderGeometry.setPreserveGeometry(false);
m_borderGeometry.markClean();
}
diff --git a/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h b/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h
index 63ebcde9..8bba2703 100644
--- a/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h
+++ b/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h
@@ -85,6 +85,8 @@ public:
QGeoMapObjectPrivate *clone() override;
virtual QGeoShape geoShape() const override;
+ unsigned int zoomForLOD(int zoom) const;
+
// Data Members
QDoubleVector2D m_leftBoundMercator;
QGeoMapPolylineGeometryOpenGL m_borderGeometry;
diff --git a/tests/manual/mapobjects_tester/main.qml b/tests/manual/mapobjects_tester/main.qml
index 4695df3a..1e758ace 100644
--- a/tests/manual/mapobjects_tester/main.qml
+++ b/tests/manual/mapobjects_tester/main.qml
@@ -39,7 +39,7 @@ Window {
visible: true
width: 1440
height: 720
- title: qsTr("MapItems backends")
+ title: qsTr("MapObjects tester")
property real initialZL: 5
@@ -365,6 +365,12 @@ Window {
}
}
+ LongPolyline {
+ id: longPoly
+ backend: polylineBackend()
+ Component.onCompleted: longPolyPath = path
+ }
+
MapCircle {
center: QtPositioning.coordinate(52, 0)
radius: sliRadius.value
@@ -391,8 +397,8 @@ Window {
center: QtPositioning.coordinate(17, 44);
radius: 200*1000
color: "firebrick"
- layer.enabled: (backend == MapCircle.Software)
- layer.samples: 4
+// layer.enabled: (backend == MapCircle.Software)
+// layer.samples: 4
}
}
}
@@ -486,17 +492,27 @@ Window {
border.color: 'firebrick'
}
-// MapCircleObject {
-// id: circle1
-// border.color: 'deepskyblue'
-// border.width: 26
-// center: QtPositioning.coordinate(17, 44);
-// radius: 200*1000
-// color: "firebrick"
-// }
+ MapPolylineObject {
+ id: longPolyline
+ line.color: "firebrick"
+ objectName: parent.objectName + "longPolyline"
+ line.width: 10
+ path: longPolyPath
+ }
+
+ MapCircleObject {
+ id: circle1
+ border.color: 'deepskyblue'
+ border.width: 26
+ center: QtPositioning.coordinate(17, 44);
+ radius: 200*1000
+ color: "firebrick"
+ }
}
}
+ property var longPolyPath
+
C2.Slider {
id: sliRadius
orientation: Qt.Vertical
diff --git a/tests/manual/mapobjects_tester/qml.qrc b/tests/manual/mapobjects_tester/qml.qrc
index 5f6483ac..3a8a697d 100644
--- a/tests/manual/mapobjects_tester/qml.qrc
+++ b/tests/manual/mapobjects_tester/qml.qrc
@@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/">
<file>main.qml</file>
+ <file alias="LongPolyline.qml">../mappolyline_tester/LongPolyline.qml</file>
</qresource>
</RCC>