summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/location/quickmapitems/qdeclarativecirclemapitem.cpp343
-rw-r--r--src/location/quickmapitems/qdeclarativecirclemapitem_p.h1
-rw-r--r--src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h19
-rw-r--r--src/location/quickmapitems/qdeclarativegeomap.cpp20
-rw-r--r--src/location/quickmapitems/qdeclarativegeomapitembase.cpp8
-rw-r--r--src/location/quickmapitems/qdeclarativegeomapitembase_p.h3
-rw-r--r--src/location/quickmapitems/qdeclarativegeomapitemutils.cpp41
-rw-r--r--src/location/quickmapitems/qdeclarativegeomapitemutils_p.h10
-rw-r--r--src/location/quickmapitems/qdeclarativepolygonmapitem.cpp168
-rw-r--r--src/location/quickmapitems/qdeclarativepolygonmapitem_p.h3
-rw-r--r--src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h35
-rw-r--r--src/location/quickmapitems/qdeclarativepolylinemapitem.cpp490
-rw-r--r--src/location/quickmapitems/qdeclarativepolylinemapitem_p.h1
-rw-r--r--src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h43
-rw-r--r--src/location/quickmapitems/qdeclarativerectanglemapitem.cpp8
-rw-r--r--src/location/quickmapitems/qdeclarativerectanglemapitem_p.h1
-rw-r--r--src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h2
-rw-r--r--src/location/quickmapitems/qgeomapitemgeometry_p.h6
18 files changed, 353 insertions, 849 deletions
diff --git a/src/location/quickmapitems/qdeclarativecirclemapitem.cpp b/src/location/quickmapitems/qdeclarativecirclemapitem.cpp
index 0e45d439..34aa4fb5 100644
--- a/src/location/quickmapitems/qdeclarativecirclemapitem.cpp
+++ b/src/location/quickmapitems/qdeclarativecirclemapitem.cpp
@@ -11,7 +11,6 @@
#include <QtGui/private/qtriangulator_p.h>
#include <QtLocation/private/qgeomap_p.h>
#include <QtPositioning/private/qlocationutils_p.h>
-#include <QtPositioning/private/qclipperutils_p.h>
#include <qmath.h>
#include <algorithm>
@@ -108,136 +107,8 @@ QGeoMapCircleGeometry::QGeoMapCircleGeometry()
{
}
-/*!
- \internal
-*/
-void QGeoMapCircleGeometry::updateSourceAndScreenPointsInvert(const QList<QDoubleVector2D> &circlePath, const QGeoMap &map)
-{
- const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection());
- // Not checking for !screenDirty anymore, as everything is now recalculated.
- clear();
- srcPath_ = QPainterPath();
- if (map.viewportWidth() == 0 || map.viewportHeight() == 0 || circlePath.size() < 3) // a circle requires at least 3 points;
- return;
-
- /*
- * No special case for no tilting as these items are very rare, and usually at most one per map.
- *
- * Approach:
- * 1) subtract the circle from a rectangle filling the whole map, *in wrapped mercator space*
- * 2) clip the resulting geometries against the visible region, *in wrapped mercator space*
- * 3) create a QPainterPath with each of the resulting polygons projected to screen
- * 4) use qTriangulate() to triangulate the painter path
- */
-
- // 1)
- const double topLati = QLocationUtils::mercatorMaxLatitude();
- const double bottomLati = -(QLocationUtils::mercatorMaxLatitude());
- const double leftLongi = QLocationUtils::mapLeftLongitude(map.cameraData().center().longitude());
- const double rightLongi = QLocationUtils::mapRightLongitude(map.cameraData().center().longitude());
-
- srcOrigin_ = QGeoCoordinate(topLati,leftLongi);
- const QDoubleVector2D tl = p.geoToWrappedMapProjection(QGeoCoordinate(topLati,leftLongi));
- const QDoubleVector2D tr = p.geoToWrappedMapProjection(QGeoCoordinate(topLati,rightLongi));
- const QDoubleVector2D br = p.geoToWrappedMapProjection(QGeoCoordinate(bottomLati,rightLongi));
- const QDoubleVector2D bl = p.geoToWrappedMapProjection(QGeoCoordinate(bottomLati,leftLongi));
-
- QList<QDoubleVector2D> fill;
- fill << tl << tr << br << bl;
-
- QList<QDoubleVector2D> hole;
- for (const QDoubleVector2D &c: circlePath)
- hole << p.wrapMapProjection(c);
-
- QClipperUtils clipper;
- clipper.addSubjectPath(fill, true);
- clipper.addClipPolygon(hole);
- auto difference = clipper.execute(QClipperUtils::Difference, QClipperUtils::pftEvenOdd,
- QClipperUtils::pftEvenOdd);
-
- // 2)
- QDoubleVector2D lb = p.geoToWrappedMapProjection(srcOrigin_);
- QList<QList<QDoubleVector2D> > clippedPaths;
- const QList<QDoubleVector2D> &visibleRegion = p.visibleGeometry();
- if (visibleRegion.size()) {
- clipper.clearClipper();
- for (const auto &p: difference)
- clipper.addSubjectPath(p, true);
- clipper.addClipPolygon(visibleRegion);
- clippedPaths = clipper.execute(QClipperUtils::Intersection, QClipperUtils::pftEvenOdd,
- QClipperUtils::pftEvenOdd);
-
- // 2.1) update srcOrigin_ with the point with minimum X/Y
- lb = QDoubleVector2D(qInf(), qInf());
- for (const QList<QDoubleVector2D> &path: clippedPaths) {
- for (const QDoubleVector2D &p: path) {
- if (p.x() < lb.x() || (p.x() == lb.x() && p.y() < lb.y())) {
- lb = p;
- }
- }
- }
- if (qIsInf(lb.x()))
- return;
-
- // Prevent the conversion to and from clipper from introducing negative offsets which
- // in turn will make the geometry wrap around.
- lb.setX(qMax(tl.x(), lb.x()));
- srcOrigin_ = p.mapProjectionToGeo(p.unwrapMapProjection(lb));
- } else {
- clippedPaths = difference;
- }
-
- //3)
- const QDoubleVector2D origin = p.wrappedMapProjectionToItemPosition(lb);
-
- for (const QList<QDoubleVector2D> &path: clippedPaths) {
- QDoubleVector2D lastAddedPoint;
- for (qsizetype i = 0; i < path.size(); ++i) {
- QDoubleVector2D point = p.wrappedMapProjectionToItemPosition(path.at(i));
- //point = point - origin; // Do this using ppi.translate()
-
- const QDoubleVector2D pt = point - origin;
- if (qMax(pt.x(), pt.y()) > maxCoord_)
- maxCoord_ = qMax(pt.x(), pt.y());
-
- if (i == 0) {
- srcPath_.moveTo(point.toPointF());
- lastAddedPoint = point;
- } else if ((point - lastAddedPoint).manhattanLength() > 3 || i == path.size() - 1) {
- srcPath_.lineTo(point.toPointF());
- lastAddedPoint = point;
- }
- }
- srcPath_.closeSubpath();
- }
-
- QPainterPath ppi = srcPath_;
- ppi.translate(-1 * origin.toPointF());
-
- QTriangleSet ts = qTriangulate(ppi);
- qreal *vx = ts.vertices.data();
-
- screenIndices_.reserve(ts.indices.size());
- screenVertices_.reserve(ts.vertices.size());
-
- if (ts.indices.type() == QVertexIndexVector::UnsignedInt) {
- const quint32 *ix = reinterpret_cast<const quint32 *>(ts.indices.data());
- for (qsizetype i = 0; i < (ts.indices.size()/3*3); ++i)
- screenIndices_ << ix[i];
- } else {
- const quint16 *ix = reinterpret_cast<const quint16 *>(ts.indices.data());
- for (qsizetype i = 0; i < (ts.indices.size()/3*3); ++i)
- screenIndices_ << ix[i];
- }
- for (qsizetype i = 0; i < (ts.vertices.size()/2*2); i += 2)
- screenVertices_ << QPointF(vx[i], vx[i + 1]);
-
- screenBounds_ = ppi.boundingRect();
- sourceBounds_ = srcPath_.boundingRect();
-}
-
QDeclarativeCircleMapItem::QDeclarativeCircleMapItem(QQuickItem *parent)
-: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent), m_dirtyMaterial(true),
+: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent),
m_updatingGeometry(false)
, m_d(new QDeclarativeCircleMapItemPrivateCPU(*this))
{
@@ -248,12 +119,6 @@ QDeclarativeCircleMapItem::QDeclarativeCircleMapItem(QQuickItem *parent)
this, &QDeclarativeCircleMapItem::onLinePropertiesChanged);
QObject::connect(&m_border, &QDeclarativeMapLineProperties::widthChanged,
this, &QDeclarativeCircleMapItem::onLinePropertiesChanged);
-
- // assume that circles are not self-intersecting
- // to speed up processing
- // FIXME: unfortunately they self-intersect at the poles due to current drawing method
- // so the line is commented out until fixed
- //geometry_.setAssumeSimple(true);
}
QDeclarativeCircleMapItem::~QDeclarativeCircleMapItem()
@@ -326,9 +191,9 @@ void QDeclarativeCircleMapItem::setColor(const QColor &color)
{
if (m_color == color)
return;
+
m_color = color;
- m_dirtyMaterial = true;
- update();
+ polishAndUpdate(); // in case color was transparent and now is not or vice versa
emit colorChanged(m_color);
}
@@ -404,7 +269,6 @@ void QDeclarativeCircleMapItem::afterViewportChanged(const QGeoMapViewportChange
bool QDeclarativeCircleMapItem::contains(const QPointF &point) const
{
return m_d->contains(point);
- //
}
const QGeoShape &QDeclarativeCircleMapItem::geoShape() const
@@ -475,94 +339,59 @@ QDeclarativeCircleMapItemPrivateCPU::~QDeclarativeCircleMapItemPrivateCPU()
delete m_shape;
}
-bool QDeclarativeCircleMapItemPrivate::preserveCircleGeometry (QList<QDoubleVector2D> &path,
- const QGeoCoordinate &center, qreal distance, const QGeoProjectionWebMercator &p)
-{
- // if circle crosses north/south pole, then don't preserve circular shape,
- if ( crossEarthPole(center, distance)) {
- updateCirclePathForRendering(path, center, distance, p);
- return false;
- }
- return true;
-}
-
/*
* A workaround for circle path to be drawn correctly using a polygon geometry
* This method generates a polygon like
- * _____________
- * | |
- * \ /
- * | |
- * / \
- * | |
- * -------------
- *
- * or a polygon like
- *
* ______________
* | ____ |
* \__/ \__/
*/
-void QDeclarativeCircleMapItemPrivate::updateCirclePathForRendering(QList<QDoubleVector2D> &path,
+void QDeclarativeCircleMapItemPrivate::includeOnePoleInPath(QList<QDoubleVector2D> &path,
const QGeoCoordinate &center,
qreal distance, const QGeoProjectionWebMercator &p)
{
const qreal poleLat = 90;
const qreal distanceToNorthPole = center.distanceTo(QGeoCoordinate(poleLat, 0));
const qreal distanceToSouthPole = center.distanceTo(QGeoCoordinate(-poleLat, 0));
- bool crossNorthPole = distanceToNorthPole < distance;
- bool crossSouthPole = distanceToSouthPole < distance;
-
- QList<qsizetype> wrapPathIndex;
- QDoubleVector2D prev = p.wrapMapProjection(path.at(0));
-
- for (qsizetype i = 1; i <= path.count(); ++i) {
- const auto index = i % path.count();
- const QDoubleVector2D point = p.wrapMapProjection(path.at(index));
- double diff = qAbs(point.x() - prev.x());
- if (diff > 0.5) {
- continue;
- }
- }
+ const bool crossNorthPole = distanceToNorthPole < distance;
+ const bool crossSouthPole = distanceToSouthPole < distance;
- // find the points in path where wrapping occurs
- for (qsizetype i = 1; i <= path.count(); ++i) {
- const auto index = i % path.count();
- const QDoubleVector2D point = p.wrapMapProjection(path.at(index));
- if ((qAbs(point.x() - prev.x())) >= 0.5) {
- wrapPathIndex << index;
- if (wrapPathIndex.size() == 2 || !(crossNorthPole && crossSouthPole))
- break;
- }
- prev = point;
- }
- // insert two additional coords at top/bottom map corners of the map for shape
- // to be drawn correctly
- if (wrapPathIndex.size() > 0) {
- qreal newPoleLat = 0; // 90 latitude
- QDoubleVector2D wrapCoord = path.at(wrapPathIndex[0]);
- if (wrapPathIndex.size() == 2) {
- QDoubleVector2D wrapCoord2 = path.at(wrapPathIndex[1]);
- if (wrapCoord2.y() < wrapCoord.y())
- newPoleLat = 1; // -90 latitude
- } else if (center.latitude() < 0) {
- newPoleLat = 1; // -90 latitude
- }
- for (qsizetype i = 0; i < wrapPathIndex.size(); ++i) {
- const qsizetype index = wrapPathIndex[i] == 0 ? 0 : wrapPathIndex[i] + i*2;
- const qsizetype prevIndex = (index - 1) < 0 ? (path.count() - 1) : index - 1;
- QDoubleVector2D coord0 = path.at(prevIndex);
- QDoubleVector2D coord1 = path.at(index);
- coord0.setY(newPoleLat);
- coord1.setY(newPoleLat);
- path.insert(index ,coord1);
- path.insert(index, coord0);
- newPoleLat = 1.0 - newPoleLat;
- }
+ if (!crossNorthPole && !crossSouthPole)
+ return;
+
+ if (crossNorthPole && crossSouthPole)
+ return;
+
+ const QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry());
+ const qreal xAtBorder = cameraRect.left();
+
+ // The strategy is to order the points from left to right as they appear on the screen.
+ // Then add the 3 missing sides that form the box for painting at the front and at the end of the list.
+ // We ensure that the box aligns with the cameraRect in order to avoid rendering it twice (wrap around).
+ // Notably, this leads to outlines at the right side of the map.
+ // Set xAtBorder to 0.0 to avoid this, however, for an increased rendering cost.
+ for (auto &c : path) {
+ c.setX(c.x());
+ while (c.x() - xAtBorder > 1.0)
+ c.setX(c.x() - 1.0);
+ while (c.x() - xAtBorder < 0.0)
+ c.setX(c.x() + 1.0);
}
+
+ std::sort(path.begin(), path.end(),
+ [](const QDoubleVector2D &a, const QDoubleVector2D &b) -> bool
+ {return a.x() < b.x();});
+
+ const qreal newPoleLat = crossNorthPole ? 0.0 : 1.0;
+ const QDoubleVector2D P1 = path.first() + QDoubleVector2D(1.0, 0.0);
+ const QDoubleVector2D P2 = path.last() - QDoubleVector2D(1.0, 0.0);
+ path.push_front(P2);
+ path.push_front(QDoubleVector2D(P2.x(), newPoleLat));
+ path.append(P1);
+ path.append(QDoubleVector2D(P1.x(), newPoleLat));
}
-bool QDeclarativeCircleMapItemPrivate::crossEarthPole(const QGeoCoordinate &center, qreal distance)
+int QDeclarativeCircleMapItemPrivate::crossEarthPole(const QGeoCoordinate &center, qreal distance)
{
qreal poleLat = 90;
QGeoCoordinate northPole = QGeoCoordinate(poleLat, center.longitude());
@@ -570,16 +399,15 @@ bool QDeclarativeCircleMapItemPrivate::crossEarthPole(const QGeoCoordinate &cent
// approximate using great circle distance
qreal distanceToNorthPole = center.distanceTo(northPole);
qreal distanceToSouthPole = center.distanceTo(southPole);
- if (distanceToNorthPole < distance || distanceToSouthPole < distance)
- return true;
- return false;
+ return (distanceToNorthPole < distance? 1 : 0) +
+ (distanceToSouthPole < distance? 1 : 0);
}
-void QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(QList<QGeoCoordinate> &path,
+void QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(QList<QDoubleVector2D> &path,
const QGeoCoordinate &center,
qreal distance,
- int steps,
- QGeoCoordinate &leftBound)
+ const QGeoProjectionWebMercator &p,
+ int steps)
{
// Calculate points based on great-circle distance
// Calculation is the same as GeoCoordinate's atDistanceAndAzimuth function
@@ -588,7 +416,6 @@ void QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(QList<QGeoCoord
// pre-calculations
steps = qMax(steps, 3);
qreal centerLon = center.longitude();
- qreal minLon = centerLon;
qreal latRad = QLocationUtils::radians(center.latitude());
qreal lonRad = QLocationUtils::radians(centerLon);
qreal cosLatRad = std::cos(latRad);
@@ -598,7 +425,6 @@ void QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(QList<QGeoCoord
qreal sinRatio = std::sin(ratio);
qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio;
qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio;
- int idx = 0;
for (int i = 0; i < steps; ++i) {
const qreal azimuthRad = 2 * M_PI * i / steps;
const qreal resultLatRad = std::asin(sinLatRad_x_cosRatio
@@ -606,20 +432,20 @@ void QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(QList<QGeoCoord
const qreal resultLonRad = lonRad + std::atan2(std::sin(azimuthRad) * cosLatRad_x_sinRatio,
cosRatio - sinLatRad * std::sin(resultLatRad));
const qreal lat2 = QLocationUtils::degrees(resultLatRad);
- qreal lon2 = QLocationUtils::wrapLong(QLocationUtils::degrees(resultLonRad));
-
- path << QGeoCoordinate(lat2, lon2, center.altitude());
- // Consider only points in the left half of the circle for the left bound.
- if (azimuthRad > M_PI) {
- if (lon2 > centerLon) // if point and center are on different hemispheres
- lon2 -= 360;
- if (lon2 < minLon) {
- minLon = lon2;
- idx = i;
- }
+ qreal lon2 = QLocationUtils::degrees(resultLonRad);
+
+ //Workaround as QGeoCoordinate does not take Longitudes outside [-180,180]
+ qreal offset = 0.0;
+ while (lon2 > 180.0) {
+ offset += 1.0;
+ lon2 -= 360.0;
}
+ while (lon2 < -180.0) {
+ offset -= 1.0;
+ lon2 += 360.0;
+ }
+ path << p.geoToMapProjection(QGeoCoordinate(lat2, lon2, center.altitude())) + QDoubleVector2D(offset, 0.0);
}
- leftBound = path.at(idx);
}
//////////////////////////////////////////////////////////////////////
@@ -640,22 +466,41 @@ void QDeclarativeCircleMapItemPrivateCPU::updatePolish()
QList<QDoubleVector2D> circlePath = m_circlePath;
- int pathCount = circlePath.size();
- bool preserve = preserveCircleGeometry(circlePath, m_circle.m_circle.center(),
- m_circle.m_circle.radius(), p);
- // using leftBound_ instead of the analytically calculated
- // circle_.boundingGeoRectangle().topLeft());
- // to fix QTBUG-62154
- m_geometry.setPreserveGeometry(true, m_leftBound); // to set the geoLeftBound_
- m_geometry.setPreserveGeometry(preserve, m_leftBound);
-
- bool invertedCircle = false;
- if (crossEarthPole(m_circle.m_circle.center(), m_circle.m_circle.radius()) && circlePath.size() == pathCount) {
- // invert fill area for really huge circles
- m_geometry.updateSourceAndScreenPointsInvert(circlePath, *m_circle.map());
- invertedCircle = true;
+ const QGeoCoordinate &center = m_circle.m_circle.center();
+ const qreal &radius = m_circle.m_circle.radius();
+
+ // if circle crosses north/south pole, then don't preserve circular shape,
+ int crossingPoles = crossEarthPole(center, radius);
+ if (crossingPoles == 1) { // If the circle crosses both poles, we will remove it from a rectangle
+ includeOnePoleInPath(circlePath, center, radius, p);
+ m_geometry.updateSourcePoints(*m_circle.map(), QList<QList<QDoubleVector2D>>{circlePath}, QGeoMapPolygonGeometry::DrawOnce);
+ }
+ else if (crossingPoles == 2) { // If the circle crosses both poles, we will remove it from a rectangle
+ // The circle covers both poles. This appears on the map as a total fill with a hole on the opposite side of the planet
+ // This can be represented by a rectangle that spans the entire planet with a hole defined by the calculated points.
+ // The points on one side have to be wraped around the globe
+ const qreal centerX = p.geoToMapProjection(center).x();
+ for (int i = 0; i < circlePath.count(); i++) {
+ if (circlePath.at(i).x() > centerX)
+ circlePath[i].setX(circlePath.at(i).x() - 1.0);
+ }
+ QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry());
+ const QRectF circleRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(circlePath);
+ QGeoMapPolygonGeometry::MapBorderBehaviour wrappingMode = QGeoMapPolygonGeometry::DrawOnce;
+ QList<QDoubleVector2D> surroundingRect;
+ if (cameraRect.contains(circleRect)){
+ cameraRect = cameraRect.adjusted(-0.1, 0.0, 0.2, 0.0);
+ surroundingRect = {{cameraRect.left(), cameraRect.top()}, {cameraRect.right(), cameraRect.top()},
+ {cameraRect.right(), cameraRect.bottom()}, {cameraRect.left() , cameraRect.bottom()}};
+ } else {
+ const qreal anchorRect = centerX;
+ surroundingRect = {{anchorRect, 0.0}, {anchorRect + 1.0, 0.0},
+ {anchorRect + 1.0, 1.0}, {anchorRect, 1.0}};
+ wrappingMode = QGeoMapPolygonGeometry::WrapAround;
+ }
+ m_geometry.updateSourcePoints(*m_circle.map(), {surroundingRect, circlePath}, wrappingMode);
} else {
- m_geometry.updateSourcePoints(*m_circle.map(), circlePath);
+ m_geometry.updateSourcePoints(*m_circle.map(), QList<QList<QDoubleVector2D>>{circlePath});
}
m_circle.setShapeTriangulationScale(m_shape, m_geometry.maxCoord());
@@ -672,9 +517,7 @@ void QDeclarativeCircleMapItemPrivateCPU::updatePolish()
path.closeSubpath();
m_painterPath->setPath(path);
- m_circle.setSize(invertedCircle || !preserve
- ? bb.size()
- : bb.size() + QSize(2 * borderWidth, 2 * borderWidth));
+ m_circle.setSize(bb.size());
m_shape->setSize(m_circle.size());
m_shape->setOpacity(m_circle.zoomLevelOpacity());
m_shape->setVisible(true);
@@ -687,10 +530,8 @@ QSGNode *QDeclarativeCircleMapItemPrivateCPU::updateMapItemPaintNode(QSGNode *ol
{
Q_UNUSED(data);
delete oldNode;
- if (m_geometry.isScreenDirty() || m_circle.m_dirtyMaterial) {
- m_geometry.setPreserveGeometry(false);
+ if (m_geometry.isScreenDirty()) {
m_geometry.markClean();
- m_circle.m_dirtyMaterial = false;
}
return nullptr;
}
diff --git a/src/location/quickmapitems/qdeclarativecirclemapitem_p.h b/src/location/quickmapitems/qdeclarativecirclemapitem_p.h
index 211cd914..305fd719 100644
--- a/src/location/quickmapitems/qdeclarativecirclemapitem_p.h
+++ b/src/location/quickmapitems/qdeclarativecirclemapitem_p.h
@@ -79,7 +79,6 @@ private:
QGeoCircle m_circle;
QDeclarativeMapLineProperties m_border;
QColor m_color;
- bool m_dirtyMaterial;
bool m_updatingGeometry;
std::unique_ptr<QDeclarativeCircleMapItemPrivate> m_d;
diff --git a/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h b/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h
index 03b4f1b7..117dcb63 100644
--- a/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h
+++ b/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h
@@ -60,27 +60,21 @@ public:
if (!m_circle.map() || m_circle.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
return;
- const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_circle.map()->geoProjection());
- QList<QGeoCoordinate> path;
- calculatePeripheralPoints(path, m_circle.center(), m_circle.radius(), CircleSamples, m_leftBound);
m_circlePath.clear();
- for (const QGeoCoordinate &c : path)
- m_circlePath << p.geoToMapProjection(c);
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_circle.map()->geoProjection());
+ calculatePeripheralPoints(m_circlePath, m_circle.center(), m_circle.radius(), p, CircleSamples);
}
- static bool crossEarthPole(const QGeoCoordinate &center, qreal distance);
+ static int crossEarthPole(const QGeoCoordinate &center, qreal distance);
- static bool preserveCircleGeometry(QList<QDoubleVector2D> &path, const QGeoCoordinate &center,
- qreal distance, const QGeoProjectionWebMercator &p);
- static void updateCirclePathForRendering(QList<QDoubleVector2D> &path, const QGeoCoordinate &center,
+ static void includeOnePoleInPath(QList<QDoubleVector2D> &path, const QGeoCoordinate &center,
qreal distance, const QGeoProjectionWebMercator &p);
- static void calculatePeripheralPoints(QList<QGeoCoordinate> &path, const QGeoCoordinate &center,
- qreal distance, int steps, QGeoCoordinate &leftBound);
+ static void calculatePeripheralPoints(QList<QDoubleVector2D> &path, const QGeoCoordinate &center,
+ qreal distance, const QGeoProjectionWebMercator &p, int steps);
QDeclarativeCircleMapItem &m_circle;
QList<QDoubleVector2D> m_circlePath;
- QGeoCoordinate m_leftBound;
};
class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCircleMapItemPrivateCPU: public QDeclarativeCircleMapItemPrivate
@@ -96,7 +90,6 @@ public:
}
void markSourceDirtyAndUpdate() override
{
- // preserveGeometry is cleared in updateMapItemPaintNode
m_geometry.markSourceDirty();
m_circle.polishAndUpdate();
}
diff --git a/src/location/quickmapitems/qdeclarativegeomap.cpp b/src/location/quickmapitems/qdeclarativegeomap.cpp
index 0b4fcde9..c232010b 100644
--- a/src/location/quickmapitems/qdeclarativegeomap.cpp
+++ b/src/location/quickmapitems/qdeclarativegeomap.cpp
@@ -1868,23 +1868,12 @@ void QDeclarativeGeoMap::updateItemToWindowTransform()
return;
// Update itemToWindowTransform into QGeoProjection
- const QTransform item2WindowOld = m_map->geoProjection().itemToWindowTransform();
QTransform item2Window = QQuickItemPrivate::get(this)->itemToWindowTransform();
if (!property("layer").isNull() && property("layer").value<QObject *>()->property("enabled").toBool())
item2Window.reset(); // When layer is enabled, the item is rendered offscreen with no transformation, then the layer is applied
m_map->setItemToWindowTransform(item2Window);
- // This method is called at every redraw, including those redraws not generated by
- // sgNodeChanged.
- // In these cases, *if* the item2windowTransform has changed (e.g., if transformation of
- // the item or one of its ancestors changed), a forced update of the map items using accelerated
- // GL implementation has to be performed in order to have them pulling the updated itemToWindowTransform.
- if (!m_sgNodeHasChanged && item2WindowOld != item2Window) {
- for (auto i: std::as_const(m_mapItems))
- i->setMaterialDirty();
- }
-
m_sgNodeHasChanged = false;
}
@@ -2112,10 +2101,11 @@ void QDeclarativeGeoMap::fitViewportToMapItemsRefine(const QList<QPointer<QDecla
bottomRightX = topLeftX + brect.width();
bottomRightY = topLeftY + brect.height();
} else {
- topLeftX = item->position().x();
- topLeftY = item->position().y();
- bottomRightX = topLeftX + item->width();
- bottomRightY = topLeftY + item->height();
+ QGeoRectangle brect = item->geoShape().boundingGeoRectangle();
+ topLeftX = fromCoordinate(brect.topLeft(), false).x();
+ topLeftY = fromCoordinate(brect.topLeft(), false).y();
+ bottomRightX = fromCoordinate(brect.bottomRight(), false).x();
+ bottomRightY = fromCoordinate(brect.bottomRight(), false).y();
}
minX = qMin(minX, topLeftX);
diff --git a/src/location/quickmapitems/qdeclarativegeomapitembase.cpp b/src/location/quickmapitems/qdeclarativegeomapitembase.cpp
index c5b1cc8c..233ea82d 100644
--- a/src/location/quickmapitems/qdeclarativegeomapitembase.cpp
+++ b/src/location/quickmapitems/qdeclarativegeomapitembase.cpp
@@ -77,7 +77,7 @@ void QDeclarativeGeoMapItemBase::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *m
// For performance reasons we're not connecting map_'s and quickMap_'s signals to this.
// Rather, the handling of cameraDataChanged, visibleAreaChanged, heightChanged and widthChanged is done explicitly in QDeclarativeGeoMap by directly calling methods on the items.
// See QTBUG-76950
- lastSize_ = QSizeF(quickMap_->width(), quickMap_->height());
+ lastMapSize_ = QSizeF(quickMap_->width(), quickMap_->height());
lastCameraData_ = map_->cameraData();
}
}
@@ -91,7 +91,7 @@ void QDeclarativeGeoMapItemBase::baseCameraDataChanged(const QGeoCameraData &cam
evt.cameraData = cameraData;
evt.mapSize = QSizeF(quickMap_->width(), quickMap_->height());
- if (evt.mapSize != lastSize_)
+ if (evt.mapSize != lastMapSize_)
evt.mapSizeChanged = true;
if (cameraData.bearing() != lastCameraData_.bearing())
@@ -105,7 +105,7 @@ void QDeclarativeGeoMapItemBase::baseCameraDataChanged(const QGeoCameraData &cam
if (cameraData.zoomLevel() != lastCameraData_.zoomLevel())
evt.zoomLevelChanged = true;
- lastSize_ = evt.mapSize;
+ lastMapSize_ = evt.mapSize;
lastCameraData_ = cameraData;
afterViewportChanged(evt);
@@ -286,8 +286,6 @@ bool QDeclarativeGeoMapItemBase::isPolishScheduled() const
return QQuickItemPrivate::get(this)->polishScheduled;
}
-void QDeclarativeGeoMapItemBase::setMaterialDirty() {}
-
void QDeclarativeGeoMapItemBase::polishAndUpdate()
{
polish();
diff --git a/src/location/quickmapitems/qdeclarativegeomapitembase_p.h b/src/location/quickmapitems/qdeclarativegeomapitembase_p.h
index 6888596a..38eef2db 100644
--- a/src/location/quickmapitems/qdeclarativegeomapitembase_p.h
+++ b/src/location/quickmapitems/qdeclarativegeomapitembase_p.h
@@ -111,7 +111,6 @@ protected Q_SLOTS:
protected:
float zoomLevelOpacity() const;
bool isPolishScheduled() const;
- virtual void setMaterialDirty();
QGeoMap::ItemType m_itemType = QGeoMap::NoItem;
@@ -123,7 +122,7 @@ private:
QPointer<QGeoMap> map_;
QDeclarativeGeoMap *quickMap_ = nullptr;
- QSizeF lastSize_;
+ QSizeF lastMapSize_;
QGeoCameraData lastCameraData_;
QDeclarativeGeoMapItemGroup *parentGroup_ = nullptr;
diff --git a/src/location/quickmapitems/qdeclarativegeomapitemutils.cpp b/src/location/quickmapitems/qdeclarativegeomapitemutils.cpp
index 6a79b33b..a1501dd6 100644
--- a/src/location/quickmapitems/qdeclarativegeomapitemutils.cpp
+++ b/src/location/quickmapitems/qdeclarativegeomapitemutils.cpp
@@ -16,6 +16,31 @@ QT_BEGIN_NAMESPACE
namespace QDeclarativeGeoMapItemUtils {
+double distanceSqrPointLine(double p0_x
+ , double p0_y
+ , double p1_x
+ , double p1_y
+ , double p2_x
+ , double p2_y)
+{
+ const double t_x = p2_x - p1_x;
+ const double t_y = p2_y - p1_y;
+ const double p_x = p0_x - p1_x;
+ const double p_y = p0_y - p1_y;
+ const double tsqr = t_x * t_x + t_y * t_y;
+
+ if (tsqr == 0)
+ return qInf();
+
+ double alpha = (p_x * t_x + p_y * t_y) / tsqr;
+ alpha = qBound<double>(0, alpha, 1);
+
+ const double dx = p_x - t_x * alpha;
+ const double dy = p_y - t_y * alpha;
+
+ return dx * dx + dy * dy;
+}
+
void wrapPath(const QList<QGeoCoordinate> &perimeter,
const QGeoCoordinate &geoLeftBound,
const QGeoProjectionWebMercator &p,
@@ -150,6 +175,22 @@ void projectBbox(const QList<QDoubleVector2D> &clippedBbox,
projectedBbox.closeSubpath();
}
+
+QRectF boundingRectangleFromList(const QList<QDoubleVector2D> &list)
+{
+ double xMin = qInf();
+ double xMax = -qInf();
+ double yMin = qInf();
+ double yMax = -qInf();
+ for (const auto &coord : list) {
+ xMin = qMin(xMin, coord.x());
+ xMax = qMax(xMax, coord.x());
+ yMin = qMin(yMin, coord.y());
+ yMax = qMax(yMax, coord.y());
+ }
+ return QRectF(xMin, yMin, xMax - xMin, yMax - yMin);
+}
+
} // namespace QDeclarativeGeoMapItemUtils
QT_END_NAMESPACE
diff --git a/src/location/quickmapitems/qdeclarativegeomapitemutils_p.h b/src/location/quickmapitems/qdeclarativegeomapitemutils_p.h
index 2f117520..50ece528 100644
--- a/src/location/quickmapitems/qdeclarativegeomapitemutils_p.h
+++ b/src/location/quickmapitems/qdeclarativegeomapitemutils_p.h
@@ -52,6 +52,14 @@ namespace QDeclarativeGeoMapItemUtils
}
};
+
+ double distanceSqrPointLine(double p0_x
+ , double p0_y
+ , double p1_x
+ , double p1_y
+ , double p2_x
+ , double p2_y);
+
void wrapPath(const QList<QGeoCoordinate> &perimeter
, const QGeoCoordinate &geoLeftBound
, const QGeoProjectionWebMercator &p
@@ -80,6 +88,8 @@ namespace QDeclarativeGeoMapItemUtils
, const QGeoProjectionWebMercator &p
, QPainterPath &projectedBbox);
+ QRectF boundingRectangleFromList(const QList<QDoubleVector2D> &list);
+
};
QT_END_NAMESPACE
diff --git a/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp b/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp
index 1f34d0c1..1eee3eb0 100644
--- a/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp
+++ b/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp
@@ -115,98 +115,105 @@ QGeoMapPolygonGeometry::QGeoMapPolygonGeometry() = default;
\internal
*/
void QGeoMapPolygonGeometry::updateSourcePoints(const QGeoMap &map,
- const QList<QDoubleVector2D> &path)
+ const QList<QList <QDoubleVector2D>> &paths,
+ MapBorderBehaviour wrapping)
{
+ // A polygon consists of mutliple paths. This is usually a perimeter and multiple holes
+ // We move all paths into a single QPainterPath. The filling rule EvenOdd will then ensure that the paths are shown correctly
if (!sourceDirty_)
return;
const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection());
srcPath_ = QPainterPath();
+ srcOrigin_ = p.mapProjectionToGeo(QDoubleVector2D(0.0, 0.0)); //avoid warning of NaN values if function is returned early
+
+ //1 The bounding rectangle of the polygon and camera view are compared to determine if the polygon is visible
+ // The viewport is periodic in x-direction in the interval [-1; 1].
+ // The polygon (maybe) has to be ploted periodically too by shifting it by -1 or +1;
+ const QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry());
+ QRectF itemRect;
+ for (const auto &path : paths)
+ itemRect |= QDeclarativeGeoMapItemUtils::boundingRectangleFromList(path);
+ QList<QList<QDoubleVector2D>> wrappedPaths;
+
+ if (wrapping == WrapAround) {
+ for (double xoffset : {-1.0, 0.0, 1.0}) {
+ if (!cameraRect.intersects(itemRect.translated(QPointF(xoffset, 0.0))))
+ continue;
+ for (const auto &path : paths) {
+ wrappedPaths.append(QList<QDoubleVector2D>());
+ QList<QDoubleVector2D> &wP = wrappedPaths.last();
+ wP.reserve(path.size());
+ for (const QDoubleVector2D &coord : path)
+ wP.append(coord+QDoubleVector2D(xoffset, 0.0));
+ }
+ }
+ } else
+ wrappedPaths = paths;
+
+ if (wrappedPaths.isEmpty()) // the polygon boundary rectangle does not overlap with the viewport rectangle
+ return;
- // build the actual path
- // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints
- srcOrigin_ = geoLeftBound_;
- double unwrapBelowX = 0;
- QDoubleVector2D leftBoundWrapped = p.wrapMapProjection(p.geoToMapProjection(geoLeftBound_));
- if (preserveGeometry_)
- unwrapBelowX = leftBoundWrapped.x();
-
- QList<QDoubleVector2D> wrappedPath;
- wrappedPath.reserve(path.size());
- QDoubleVector2D wrappedLeftBound(qInf(), qInf());
- // 1)
- for (const auto &coord : path) {
- QDoubleVector2D wrappedProjection = p.wrapMapProjection(coord);
-
- // We can get NaN if the map isn't set up correctly, or the projection
- // is faulty -- probably best thing to do is abort
- if (!qIsFinite(wrappedProjection.x()) || !qIsFinite(wrappedProjection.y()))
- return;
-
- const bool isPointLessThanUnwrapBelowX = (wrappedProjection.x() < leftBoundWrapped.x());
- // unwrap x to preserve geometry if moved to border of map
- if (preserveGeometry_ && isPointLessThanUnwrapBelowX) {
- double distance = wrappedProjection.x() - unwrapBelowX;
- if (distance < 0.0)
- distance += 1.0;
- wrappedProjection.setX(unwrapBelowX + distance);
+
+ //2 The polygons that are at least partially in the viewport are cliped to reduce their size
+ QList<QList<QDoubleVector2D>> clippedPaths;
+ const QList<QDoubleVector2D> &visibleRegion = p.visibleGeometryExpanded();
+ for (const auto &path : wrappedPaths) {
+ if (visibleRegion.size()) {
+ QClipperUtils clipper;
+ clipper.addSubjectPath(path, true);
+ clipper.addClipPolygon(visibleRegion);
+ clippedPaths << clipper.execute(QClipperUtils::Intersection, QClipperUtils::pftEvenOdd,
+ QClipperUtils::pftEvenOdd);
}
- if (wrappedProjection.x() < wrappedLeftBound.x() || (wrappedProjection.x() == wrappedLeftBound.x() && wrappedProjection.y() < wrappedLeftBound.y())) {
- wrappedLeftBound = wrappedProjection;
+ else {
+ clippedPaths.append(path); //Do we really need this if there are no visible regions??
}
- wrappedPath.append(wrappedProjection);
- }
-
- // 2)
- QList<QList<QDoubleVector2D> > clippedPaths;
- const QList<QDoubleVector2D> &visibleRegion = p.projectableGeometry();
- if (visibleRegion.size()) {
- QClipperUtils clipper;
- clipper.addSubjectPath(wrappedPath, true);
- clipper.addClipPolygon(visibleRegion);
- clippedPaths = clipper.execute(QClipperUtils::Intersection, QClipperUtils::pftEvenOdd,
- QClipperUtils::pftEvenOdd);
-
- // 2.1) update srcOrigin_ and leftBoundWrapped with the point with minimum X
- QDoubleVector2D lb(qInf(), qInf());
- for (const QList<QDoubleVector2D> &path: clippedPaths)
- for (const QDoubleVector2D &p: path)
- if (p.x() < lb.x() || (p.x() == lb.x() && p.y() < lb.y()))
- // y-minimization needed to find the same point on polygon and border
- lb = p;
-
- if (qIsInf(lb.x())) // e.g., when the polygon is clipped entirely
- return;
-
- // 2.2) Prevent the conversion to and from clipper from introducing negative offsets which
- // in turn will make the geometry wrap around.
- lb.setX(qMax(wrappedLeftBound.x(), lb.x()));
- leftBoundWrapped = lb;
- srcOrigin_ = p.mapProjectionToGeo(p.unwrapMapProjection(lb));
- } else {
- clippedPaths.append(wrappedPath);
}
+ if (clippedPaths.isEmpty()) //the polygon is entirely outside visibleRegion
+ return;
- // 3)
+ QRectF bb;
+ for (const auto &path: clippedPaths)
+ bb |= QDeclarativeGeoMapItemUtils::boundingRectangleFromList(path);
+ //Offset by origin, find the maximum coordinate
+ srcOrigin_ = p.mapProjectionToGeo(QDoubleVector2D(bb.left(), bb.top()));
+ QDoubleVector2D origin = p.wrappedMapProjectionToItemPosition(p.geoToWrappedMapProjection(srcOrigin_)); //save way: redo all projections
maxCoord_ = 0.0;
- QDoubleVector2D origin = p.wrappedMapProjectionToItemPosition(leftBoundWrapped);
- for (const QList<QDoubleVector2D> &path: clippedPaths) {
- QDoubleVector2D lastAddedPoint;
- for (qsizetype i = 0; i < path.size(); ++i) {
- QDoubleVector2D point = p.wrappedMapProjectionToItemPosition(path.at(i));
- point = point - origin; // (0,0) if point == geoLeftBound_
+ for (const auto &path: clippedPaths) {
+ QDoubleVector2D prevPoint = p.wrappedMapProjectionToItemPosition(path.at(0)) - origin;
+ QDoubleVector2D nextPoint = p.wrappedMapProjectionToItemPosition(path.at(1)) - origin;
+ srcPath_.moveTo(prevPoint.toPointF());
+ maxCoord_ = qMax(maxCoord_, qMax(prevPoint.x(), prevPoint.y()));
+ qsizetype pointsAdded = 1;
+ for (qsizetype i = 1; i < path.size(); ++i) {
+ const QDoubleVector2D point = nextPoint;
if (qMax(point.x(), point.y()) > maxCoord_)
maxCoord_ = qMax(point.x(), point.y());
- if (i == 0) {
- srcPath_.moveTo(point.toPointF());
- lastAddedPoint = point;
+ if (i == path.size() - 1) {
+ srcPath_.lineTo(point.toPointF()); //close the path
} else {
- if ((point - lastAddedPoint).manhattanLength() > 3 ||
- i == path.size() - 1) {
+ nextPoint = p.wrappedMapProjectionToItemPosition(path.at(i+1)) - origin;
+
+ bool addPoint = ( i > pointsAdded * 10 || //make sure that at least every 10th point is drawn
+ path.size() < 10 ); //draw small paths completely
+
+ const double tolerance = 0.1;
+ if (!addPoint) { //add the point to the shape if it deflects the boundary by more than the tolerance
+ const double dsqr = QDeclarativeGeoMapItemUtils::distanceSqrPointLine(
+ point.x(), point.y(),
+ nextPoint.x(), nextPoint.y(),
+ prevPoint.x(), prevPoint.y());
+ addPoint = addPoint || (dsqr > (tolerance*tolerance));
+ }
+
+ if (addPoint) {
srcPath_.lineTo(point.toPointF());
- lastAddedPoint = point;
+ pointsAdded++;
+ prevPoint = point;
}
+
}
}
srcPath_.closeSubpath();
@@ -292,10 +299,8 @@ QSGNode *QDeclarativePolygonMapItemPrivateCPU::updateMapItemPaintNode(QSGNode *o
{
Q_UNUSED(data);
delete oldNode;
- if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial) {
- m_geometry.setPreserveGeometry(false);
+ if (m_geometry.isScreenDirty()) {
m_geometry.markClean();
- m_poly.m_dirtyMaterial = false;
}
return nullptr;
}
@@ -310,7 +315,7 @@ bool QDeclarativePolygonMapItemPrivateCPU::contains(const QPointF &point) const
*/
QDeclarativePolygonMapItem::QDeclarativePolygonMapItem(QQuickItem *parent)
-: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent), m_dirtyMaterial(true),
+: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent),
m_updatingGeometry(false)
, m_d(new QDeclarativePolygonMapItemPrivateCPU(*this))
@@ -443,7 +448,6 @@ void QDeclarativePolygonMapItem::setColor(const QColor &color)
return;
m_color = color;
- m_dirtyMaterial = true;
polishAndUpdate(); // in case color was transparent and now is not or vice versa
emit colorChanged(m_color);
}
@@ -466,12 +470,6 @@ void QDeclarativePolygonMapItem::updatePolish()
m_d->updatePolish();
}
-void QDeclarativePolygonMapItem::setMaterialDirty()
-{
- m_dirtyMaterial = true;
- update();
-}
-
void QDeclarativePolygonMapItem::markSourceDirtyAndUpdate()
{
m_d->markSourceDirtyAndUpdate();
diff --git a/src/location/quickmapitems/qdeclarativepolygonmapitem_p.h b/src/location/quickmapitems/qdeclarativepolygonmapitem_p.h
index 1462bca4..1a99983d 100644
--- a/src/location/quickmapitems/qdeclarativepolygonmapitem_p.h
+++ b/src/location/quickmapitems/qdeclarativepolygonmapitem_p.h
@@ -68,7 +68,6 @@ protected Q_SLOTS:
protected:
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
void updatePolish() override;
- void setMaterialDirty() override;
#ifdef QT_LOCATION_DEBUG
public:
@@ -76,8 +75,6 @@ public:
QGeoPolygon m_geopoly;
QDeclarativeMapLineProperties m_border;
QColor m_color;
- bool m_dirtyMaterial;
-// bool m_dirtyGeometry = false;
bool m_updatingGeometry;
std::unique_ptr<QDeclarativePolygonMapItemPrivate> m_d;
diff --git a/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h b/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h
index 449a1a1d..ce0ed18e 100644
--- a/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h
+++ b/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h
@@ -38,12 +38,18 @@ class QQuickShapePath;
class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometry : public QGeoMapItemGeometry
{
public:
+ enum MapBorderBehaviour {
+ DrawOnce,
+ WrapAround
+ };
+
QGeoMapPolygonGeometry();
inline void setAssumeSimple(bool value) { assumeSimple_ = value; }
void updateSourcePoints(const QGeoMap &map,
- const QList<QDoubleVector2D> &path);
+ const QList<QList<QDoubleVector2D>> &path,
+ MapBorderBehaviour wrapping = WrapAround);
QPainterPath srcPath() const { return srcPath_; }
qreal maxCoord() const { return maxCoord_; }
@@ -91,7 +97,6 @@ public:
}
void markSourceDirtyAndUpdate() override
{
- // preserveGeometry is cleared in updateMapItemPaintNode
m_geometry.markSourceDirty();
m_poly.polishAndUpdate();
}
@@ -101,25 +106,29 @@ public:
return;
const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection());
m_geopathProjected.clear();
- m_geopathProjected.reserve(m_poly.m_geopoly.size());
+ m_geopathProjected << QList<QDoubleVector2D>();
+ QList<QDoubleVector2D> &pP = m_geopathProjected.last();
+ pP.reserve(m_poly.m_geopoly.perimeter().size());
for (const QGeoCoordinate &c : m_poly.m_geopoly.perimeter())
- m_geopathProjected << p.geoToMapProjection(c);
+ pP << p.geoToMapProjection(c);
+ for (int i = 0; i < m_poly.m_geopoly.holesCount(); i++) {
+ m_geopathProjected << QList<QDoubleVector2D>();
+ QList<QDoubleVector2D> &pH = m_geopathProjected.last();
+ pH.reserve(m_poly.m_geopoly.holePath(i).size());
+ for (const QGeoCoordinate &c : m_poly.m_geopoly.holePath(i))
+ pH << p.geoToMapProjection(c);
+ }
}
void updateCache()
{
if (!m_poly.map() || m_poly.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
return;
const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection());
- m_geopathProjected << p.geoToMapProjection(m_poly.m_geopoly.perimeter().last());
- }
- void preserveGeometry()
- {
- m_geometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft());
+ QList<QDoubleVector2D> &pP = m_geopathProjected.first();
+ pP << p.geoToMapProjection(m_poly.m_geopoly.perimeter().last());
}
void afterViewportChanged() override
{
- // preserveGeometry is cleared in updateMapItemPaintNode
- preserveGeometry();
markSourceDirtyAndUpdate();
}
void onMapSet() override
@@ -130,13 +139,11 @@ public:
void onGeoGeometryChanged() override
{
regenerateCache();
- preserveGeometry();
markSourceDirtyAndUpdate();
}
void onGeoGeometryUpdated() override
{
updateCache();
- preserveGeometry();
markSourceDirtyAndUpdate();
}
void onItemGeometryChanged() override
@@ -147,7 +154,7 @@ public:
QSGNode *updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override;
bool contains(const QPointF &point) const override;
- QList<QDoubleVector2D> m_geopathProjected;
+ QList<QList<QDoubleVector2D>> m_geopathProjected;
QGeoMapPolygonGeometry m_geometry;
QQuickShape *m_shape = nullptr;
QQuickShapePath *m_shapePath = nullptr;
diff --git a/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp b/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp
index 2477a4d8..e80afbee 100644
--- a/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp
+++ b/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp
@@ -3,7 +3,6 @@
#include "qdeclarativepolylinemapitem_p.h"
#include "qdeclarativepolylinemapitem_p_p.h"
-#include "qdeclarativegeomapitemutils_p.h"
#include <QtCore/QScopedValueRollback>
#include <qnumeric.h>
@@ -296,421 +295,97 @@ void QDeclarativeMapLineProperties::setWidth(qreal width)
emit widthChanged(width_);
}
-QGeoMapPolylineGeometry::QGeoMapPolylineGeometry()
-{
-}
-
-QList<QList<QDoubleVector2D> > QGeoMapPolylineGeometry::clipPath(const QGeoMap &map,
- const QList<QDoubleVector2D> &path,
- QDoubleVector2D &leftBoundWrapped)
-{
- /*
- * Approach:
- * 1) project coordinates to wrapped web mercator, and do unwrapBelowX
- * 2) if the scene is tilted, clip the geometry against the visible region (this may generate multiple polygons)
- * 2.1) recalculate the origin and geoLeftBound to prevent these parameters from ending in unprojectable areas
- * 2.2) ensure the left bound does not wrap around due to QGeoCoordinate <-> clipper conversions
- */
- const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection());
- srcOrigin_ = geoLeftBound_;
-
- double unwrapBelowX = 0;
- leftBoundWrapped = p.wrapMapProjection(p.geoToMapProjection(geoLeftBound_));
- if (preserveGeometry_)
- unwrapBelowX = leftBoundWrapped.x();
-
- QList<QDoubleVector2D> wrappedPath;
- wrappedPath.reserve(path.size());
- QDoubleVector2D wrappedLeftBound(qInf(), qInf());
- // 1)
- for (const auto &coord : path) {
- QDoubleVector2D wrappedProjection = p.wrapMapProjection(coord);
-
- // We can get NaN if the map isn't set up correctly, or the projection
- // is faulty -- probably best thing to do is abort
- if (!qIsFinite(wrappedProjection.x()) || !qIsFinite(wrappedProjection.y()))
- return QList<QList<QDoubleVector2D> >();
-
- const bool isPointLessThanUnwrapBelowX = (wrappedProjection.x() < leftBoundWrapped.x());
- // unwrap x to preserve geometry if moved to border of map
- if (preserveGeometry_ && isPointLessThanUnwrapBelowX) {
- double distance = wrappedProjection.x() - unwrapBelowX;
- if (distance < 0.0)
- distance += 1.0;
- wrappedProjection.setX(unwrapBelowX + distance);
- }
- if (wrappedProjection.x() < wrappedLeftBound.x() || (wrappedProjection.x() == wrappedLeftBound.x() && wrappedProjection.y() < wrappedLeftBound.y())) {
- wrappedLeftBound = wrappedProjection;
- }
- wrappedPath.append(wrappedProjection);
- }
-
-#ifdef QT_LOCATION_DEBUG
- m_wrappedPath = wrappedPath;
-#endif
-
- // 2)
- QList<QList<QDoubleVector2D> > clippedPaths;
- const QList<QDoubleVector2D> &visibleRegion = p.projectableGeometry();
- if (visibleRegion.size()) {
- clippedPaths = clipLine(wrappedPath, visibleRegion);
-
- // 2.1) update srcOrigin_ and leftBoundWrapped with the point with minimum X
- QDoubleVector2D lb(qInf(), qInf());
- for (const QList<QDoubleVector2D> &path: clippedPaths) {
- for (const QDoubleVector2D &p: path) {
- if (p == leftBoundWrapped) {
- lb = p;
- break;
- } else if (p.x() < lb.x() || (p.x() == lb.x() && p.y() < lb.y())) {
- // y-minimization needed to find the same point on polygon and border
- lb = p;
- }
- }
- }
- if (qIsInf(lb.x()))
- return QList<QList<QDoubleVector2D> >();
-
- // 2.2) Prevent the conversion to and from clipper from introducing negative offsets which
- // in turn will make the geometry wrap around.
- lb.setX(qMax(wrappedLeftBound.x(), lb.x()));
- leftBoundWrapped = lb;
- } else {
- clippedPaths.append(wrappedPath);
- }
-
-#ifdef QT_LOCATION_DEBUG
- m_clippedPaths = clippedPaths;
-#endif
-
- return clippedPaths;
-}
-
-void QGeoMapPolylineGeometry::pathToScreen(const QGeoMap &map,
- const QList<QList<QDoubleVector2D> > &clippedPaths,
- const QDoubleVector2D &leftBoundWrapped)
-{
- const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection());
- // 3) project the resulting geometry to screen position and calculate screen bounds
- double minX = qInf();
- double minY = qInf();
- double maxX = -qInf();
- double maxY = -qInf();
- srcOrigin_ = p.mapProjectionToGeo(p.unwrapMapProjection(leftBoundWrapped));
- QDoubleVector2D origin = p.wrappedMapProjectionToItemPosition(leftBoundWrapped);
- for (const QList<QDoubleVector2D> &path: clippedPaths) {
- QDoubleVector2D lastAddedPoint;
- for (qsizetype i = 0; i < path.size(); ++i) {
- QDoubleVector2D point = p.wrappedMapProjectionToItemPosition(path.at(i));
- point = point - origin; // (0,0) if point == geoLeftBound_
-
- minX = qMin(point.x(), minX);
- minY = qMin(point.y(), minY);
- maxX = qMax(point.x(), maxX);
- maxY = qMax(point.y(), maxY);
-
- if (i == 0) {
- srcPoints_ << point.x() << point.y();
- srcPointTypes_ << QPainterPath::MoveToElement;
- lastAddedPoint = point;
- } else {
- if ((point - lastAddedPoint).manhattanLength() > 3 ||
- i == path.size() - 1) {
- srcPoints_ << point.x() << point.y();
- srcPointTypes_ << QPainterPath::LineToElement;
- lastAddedPoint = point;
- }
- }
- }
- }
-
- sourceBounds_ = QRectF(QPointF(minX, minY), QPointF(maxX, maxY));
-}
-
/*!
\internal
*/
void QGeoMapPolylineGeometry::updateSourcePoints(const QGeoMap &map,
- const QList<QDoubleVector2D> &path,
- const QGeoCoordinate geoLeftBound)
+ const QList<QDoubleVector2D> &basePath)
{
+ // A polygon consists of mutliple paths. This is usually a perimeter and multiple holes
+ // We move all paths into a single QPainterPath. The filling rule EvenOdd will then ensure that the paths are shown correctly
if (!sourceDirty_)
return;
-
- geoLeftBound_ = geoLeftBound;
-
- // clear the old data and reserve enough memory
- srcPoints_.clear();
- srcPoints_.reserve(path.size() * 2);
- srcPointTypes_.clear();
- srcPointTypes_.reserve(path.size());
-
- /*
- * Approach:
- * 1) project coordinates to wrapped web mercator, and do unwrapBelowX
- * 2) if the scene is tilted, clip the geometry against the visible region (this may generate multiple polygons)
- * 3) project the resulting geometry to screen position and calculate screen bounds
- */
-
- QDoubleVector2D leftBoundWrapped;
- // 1, 2)
- const QList<QList<QDoubleVector2D> > &clippedPaths = clipPath(map, path, leftBoundWrapped);
-
- // 3)
- pathToScreen(map, clippedPaths, leftBoundWrapped);
-
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection());
srcPath_ = QPainterPath();
- maxCoord_ = 0.0;
- const int elemCount = srcPointTypes_.count();
- for (int i = 0; i < elemCount; ++i) {
- switch (srcPointTypes_[i]) {
- case QPainterPath::MoveToElement:
- {
- const qreal x = srcPoints_[2 * i];
- const qreal y = srcPoints_[2 * i + 1];
- if (qMax(x, y) > maxCoord_)
- maxCoord_ = qMax(x, y);
- srcPath_.moveTo(x, y);
- }
- break;
- case QPainterPath::LineToElement:
- {
- const qreal x = srcPoints_[2 * i];
- const qreal y = srcPoints_[2 * i + 1];
- if (qMax(x, y) > maxCoord_)
- maxCoord_ = qMax(x, y);
- srcPath_.lineTo(x, y);
- }
- break;
- default:
- break;
- }
+ srcOrigin_ = p.mapProjectionToGeo(QDoubleVector2D(0, 0)); //avoid warning of NaN values if function is returned early
+
+ //0 Wrap the points around the globe if the path makes more sense that way.
+ // Ultimately, this is done if it is closer to walk around the day-border than the other direction
+ QVarLengthArray<QList<QDoubleVector2D>, 3> wrappedPaths;
+ wrappedPaths << QList<QDoubleVector2D>({basePath[0]});
+ wrappedPaths.last().reserve(basePath.size());
+ for (int i = 1; i < basePath.size(); i++) {
+ if (basePath[i].x() > wrappedPaths.last().last().x() + 0.5)
+ wrappedPaths.last() << basePath[i] - QDoubleVector2D(1.0, 0.0);
+ else if (basePath[i].x() < wrappedPaths.last().last().x() - 0.5)
+ wrappedPaths.last() << basePath[i] + QDoubleVector2D(1.0, 0.0);
+ else
+ wrappedPaths.last() << basePath[i];
}
-}
-
-// *** SCREEN CLIPPING *** //
-
-enum ClipPointType {
- InsidePoint = 0x00,
- LeftPoint = 0x01,
- RightPoint = 0x02,
- BottomPoint = 0x04,
- TopPoint = 0x08
-};
-static inline int clipPointType(qreal x, qreal y, const QRectF &rect)
-{
- int type = InsidePoint;
- if (x < rect.left())
- type |= LeftPoint;
- else if (x > rect.right())
- type |= RightPoint;
- if (y < rect.top())
- type |= TopPoint;
- else if (y > rect.bottom())
- type |= BottomPoint;
- return type;
-}
-
-static void clipSegmentToRect(qreal x0, qreal y0, qreal x1, qreal y1, const QRectF &clipRect,
- QList<qreal> &outPoints, QList<QPainterPath::ElementType> &outTypes)
-{
- int type0 = clipPointType(x0, y0, clipRect);
- int type1 = clipPointType(x1, y1, clipRect);
- bool accept = false;
-
- while (true) {
- if (!(type0 | type1)) {
- accept = true;
- break;
- } else if (type0 & type1) {
- break;
- } else {
- qreal x = 0.0;
- qreal y = 0.0;
- int outsideType = type0 ? type0 : type1;
-
- if (outsideType & BottomPoint) {
- x = x0 + (x1 - x0) * (clipRect.bottom() - y0) / (y1 - y0);
- y = clipRect.bottom() - 0.1;
- } else if (outsideType & TopPoint) {
- x = x0 + (x1 - x0) * (clipRect.top() - y0) / (y1 - y0);
- y = clipRect.top() + 0.1;
- } else if (outsideType & RightPoint) {
- y = y0 + (y1 - y0) * (clipRect.right() - x0) / (x1 - x0);
- x = clipRect.right() - 0.1;
- } else if (outsideType & LeftPoint) {
- y = y0 + (y1 - y0) * (clipRect.left() - x0) / (x1 - x0);
- x = clipRect.left() + 0.1;
- }
-
- if (outsideType == type0) {
- x0 = x;
- y0 = y;
- type0 = clipPointType(x0, y0, clipRect);
- } else {
- x1 = x;
- y1 = y;
- type1 = clipPointType(x1, y1, clipRect);
- }
- }
+ //1 The bounding rectangle of the polygon and camera view are compared to determine if the polygon is visible
+ // The viewport is periodic in x-direction in the interval [-1; 1].
+ // The polygon (maybe) has to be ploted periodically too by shifting it by -1 or +1;
+ const QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry());
+ QRectF itemRect;
+ for (const auto &path : wrappedPaths)
+ itemRect |= QDeclarativeGeoMapItemUtils::boundingRectangleFromList(path).adjusted(-1e-6, -1e-6, 2e-6, 2e-6); //TODO: Maybe use linewidth?
+ for (double xoffset : {-1.0, 1.0}) {
+ if (!cameraRect.intersects(itemRect.translated(QPointF(xoffset,0))))
+ continue;
+ wrappedPaths.append(QList<QDoubleVector2D>());
+ QList<QDoubleVector2D> &wP = wrappedPaths.last();
+ wP.reserve(wrappedPaths.first().size());
+ for (const QDoubleVector2D &coord : wrappedPaths.first())
+ wP.append(coord + QDoubleVector2D(xoffset, 0.0));
}
+ if (wrappedPaths.isEmpty()) // the polygon boundary rectangle does not overlap with the viewport rectangle
+ return;
- if (accept) {
- if (outPoints.size() >= 2) {
- qreal lastX, lastY;
- lastY = outPoints.at(outPoints.size() - 1);
- lastX = outPoints.at(outPoints.size() - 2);
-
- if (!qFuzzyCompare(lastY, y0) || !qFuzzyCompare(lastX, x0)) {
- outTypes << QPainterPath::MoveToElement;
- outPoints << x0 << y0;
- }
+ //2 The polygons that are at least partially in the viewport are cliped to reduce their size
+ QList<QList<QDoubleVector2D>> clippedPaths;
+ const QList<QDoubleVector2D> &visibleRegion = p.visibleGeometryExpanded();
+ for (const auto &path : wrappedPaths) {
+ if (visibleRegion.size()) {
+ clippedPaths << clipLine(path, visibleRegion);
+ //TODO: Replace clipping with Clipper lib, similar to QPolygonMapItem
} else {
- outTypes << QPainterPath::MoveToElement;
- outPoints << x0 << y0;
- }
-
- outTypes << QPainterPath::LineToElement;
- outPoints << x1 << y1;
- }
-}
-
-static void clipPathToRect(const QList<qreal> &points,
- const QList<QPainterPath::ElementType> &types, const QRectF &clipRect,
- QList<qreal> &outPoints, QList<QPainterPath::ElementType> &outTypes)
-{
- outPoints.clear();
- outPoints.reserve(points.size());
- outTypes.clear();
- outTypes.reserve(types.size());
-
- qreal lastX = 0;
- qreal lastY = 0; // or else used uninitialized
- for (qsizetype i = 0; i < types.size(); ++i) {
- if (i > 0 && types[i] != QPainterPath::MoveToElement) {
- qreal x = points[i * 2], y = points[i * 2 + 1];
- clipSegmentToRect(lastX, lastY, x, y, clipRect, outPoints, outTypes);
+ clippedPaths.append(path); //Do we really need this if there are no visible regions??
}
-
- lastX = points[i * 2];
- lastY = points[i * 2 + 1];
- }
-}
-
-////////////////////////////////////////////////////////////////////////////
-
-/*!
- \internal
-*/
-void QGeoMapPolylineGeometry::updateScreenPoints(const QGeoMap &map,
- qreal strokeWidth,
- bool adjustTranslation)
-{
- if (!screenDirty_)
- return;
-
- QPointF origin = map.geoProjection().coordinateToItemPosition(srcOrigin_, false).toPointF();
-
- if (!qIsFinite(origin.x()) || !qIsFinite(origin.y()) || srcPointTypes_.size() < 2) { // the line might have been clipped away.
- clear();
- return;
- }
-
- // 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 * 2, strokeWidth * 2);
- viewport.translate(-1 * origin);
-
- QList<qreal> points;
- QList<QPainterPath::ElementType> types;
-
- if (clipToViewport_) {
- // Although the geometry has already been clipped against the visible region in wrapped mercator space.
- // This is currently still needed to prevent a number of artifacts deriving from QTriangulatingStroker processing
- // very large lines (that is, polylines that span many pixels in screen space)
- clipPathToRect(srcPoints_, srcPointTypes_, viewport, points, types);
- } else {
- points = srcPoints_;
- types = srcPointTypes_;
}
-
- QVectorPath vp(points.data(), types.size(), types.data());
- QTriangulatingStroker ts;
- // As of Qt5.11, the clip argument is not actually used, in the call below.
- ts.process(vp, QPen(QBrush(Qt::black), strokeWidth), QRectF(), QPainter::Antialiasing);
-
- clear();
-
- // Nothing is on the screen
- if (ts.vertexCount() == 0)
+ if (clippedPaths.isEmpty()) //the polygon is entirely outside visibleRegion
return;
- // QTriangulatingStroker#vertexCount is actually the length of the array,
- // not the number of vertices
- screenVertices_.reserve(ts.vertexCount());
-
QRectF bb;
+ for (const auto &path: clippedPaths)
+ bb |= QDeclarativeGeoMapItemUtils::boundingRectangleFromList(path);
+ //Offset by origin, find the maximum coordinate
+ maxCoord_ = 0.0;
+ srcOrigin_ = p.mapProjectionToGeo(QDoubleVector2D(bb.left(), bb.top()));
+ QDoubleVector2D origin = p.wrappedMapProjectionToItemPosition(p.geoToWrappedMapProjection(srcOrigin_)); //save way: redo all projections
+ for (const auto &path: clippedPaths) {
+ QDoubleVector2D lastAddedPoint;
+ for (qsizetype i = 0; i < path.size(); ++i) {
+ QDoubleVector2D point = p.wrappedMapProjectionToItemPosition(path.at(i));
+ point = point - origin; // (0,0) if point == origin
- QPointF pt;
- const float *vs = ts.vertices();
- for (int i = 0; i < (ts.vertexCount()/2*2); i += 2) {
- pt = QPointF(vs[i], vs[i + 1]);
- screenVertices_ << pt;
-
- if (!qIsFinite(pt.x()) || !qIsFinite(pt.y()))
- break;
-
- if (!bb.contains(pt)) {
- if (pt.x() < bb.left())
- bb.setLeft(pt.x());
-
- if (pt.x() > bb.right())
- bb.setRight(pt.x());
-
- if (pt.y() < bb.top())
- bb.setTop(pt.y());
-
- if (pt.y() > bb.bottom())
- bb.setBottom(pt.y());
- }
- }
-
- screenBounds_ = bb;
-
- const QPointF strokeOffset = (adjustTranslation) ? QPointF(strokeWidth, strokeWidth) * 0.5: QPointF();
- const QPointF offset = -1 * sourceBounds_.topLeft() + strokeOffset;
- for (qsizetype i = 0; i < screenVertices_.size(); ++i)
- screenVertices_[i] += offset;
-
- firstPointOffset_ += offset;
- screenOutline_.translate(offset);
- screenBounds_.translate(offset);
-}
-
-void QGeoMapPolylineGeometry::clearSource()
-{
- srcPoints_.clear();
- srcPointTypes_.clear();
-}
+ if (qMax(point.x(), point.y()) > maxCoord_)
+ maxCoord_ = qMax(point.x(), point.y());
-bool QGeoMapPolylineGeometry::contains(const QPointF &point) const
-{
- // screenOutline_.contains(screenPoint) doesn't work, as, it appears, that
- // screenOutline_ for QGeoMapPolylineGeometry is empty (QRectF(0,0 0x0))
- const QList<QPointF> &verts = vertices();
- QPolygonF tri;
- for (const auto &v : verts) {
- tri << v;
- if (tri.size() == 3) {
- if (tri.containsPoint(point, Qt::OddEvenFill))
- return true;
- tri.remove(0);
+ if (i == 0) {
+ srcPath_.moveTo(point.toPointF());
+ lastAddedPoint = point;
+ } else {
+ if ((point - lastAddedPoint).manhattanLength() > 3 ||
+ i == path.size() - 1) {
+ srcPath_.lineTo(point.toPointF());
+ lastAddedPoint = point;
+ }
+ }
}
}
- return false;
+ sourceBounds_ = srcPath_.boundingRect();
}
/*
@@ -750,7 +425,7 @@ void QDeclarativePolylineMapItemPrivateCPU::regenerateCache()
return;
const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection());
m_geopathProjected.clear();
- m_geopathProjected.reserve(m_poly.m_geopath.size());
+ m_geopathProjected.reserve(m_poly.m_geopath.path().size());
for (const QGeoCoordinate &c : m_poly.m_geopath.path())
m_geopathProjected << p.geoToMapProjection(c);
}
@@ -778,16 +453,14 @@ void QDeclarativePolylineMapItemPrivateCPU::updatePolish()
const QGeoMap *map = m_poly.map();
const qreal borderWidth = m_poly.m_line.width();
- m_geometry.updateSourcePoints(*map, m_geopathProjected, m_poly.m_geopath.boundingGeoRectangle().topLeft());
-
- // still needed even with Shapes, due to contains()
- m_geometry.updateScreenPoints(*map, borderWidth);
+ m_geometry.updateSourcePoints(*map, m_geopathProjected);
const QRectF bb = m_geometry.sourceBoundingBox();
m_poly.setSize(bb.size() + QSizeF(borderWidth, borderWidth));
// it has to be shifted so that the center of the line is on the correct geocoord
m_poly.setPositionOnMap(m_geometry.origin(), -1 * bb.topLeft() + QPointF(borderWidth, borderWidth) * 0.5);
+
m_poly.setShapeTriangulationScale(m_shape, m_geometry.maxCoord_);
m_shapePath->setStrokeColor(m_poly.m_line.color());
@@ -808,11 +481,8 @@ QSGNode *QDeclarativePolylineMapItemPrivateCPU::updateMapItemPaintNode(QSGNode *
{
delete oldNode;
- //TODO: update only material
- if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial || !oldNode) {
- m_geometry.setPreserveGeometry(false);
+ if (m_geometry.isScreenDirty() || !oldNode) {
m_geometry.markClean();
- m_poly.m_dirtyMaterial = false;
}
return nullptr;
}
@@ -822,7 +492,21 @@ bool QDeclarativePolylineMapItemPrivateCPU::contains(const QPointF &point) const
// With Shapes, do not just call
// m_shape->contains(m_poly.mapToItem(m_shape, point)) because that can
// only do FillContains at best, whereas the polyline relies on stroking.
- return m_geometry.contains(point);
+
+ const QPainterPath &path = m_geometry.srcPath_;
+ const double &lineWidth = m_poly.m_line.width();
+ const QPointF p = m_poly.mapToItem(m_shape, point) - QPointF(lineWidth, lineWidth) * 0.5;
+
+ for (int i = 1; i < path.elementCount(); i++) {
+ if (path.elementAt(i).type == QPainterPath::MoveToElement)
+ continue;
+ const double dsqr = QDeclarativeGeoMapItemUtils::distanceSqrPointLine(p.x(), p.y(),
+ path.elementAt(i - 1).x, path.elementAt(i - 1).y,
+ path.elementAt(i).x, path.elementAt(i).y);
+ if (dsqr < 0.25 * lineWidth * lineWidth)
+ return true;
+ }
+ return false;
}
/*
diff --git a/src/location/quickmapitems/qdeclarativepolylinemapitem_p.h b/src/location/quickmapitems/qdeclarativepolylinemapitem_p.h
index 377fb239..ce5d29cc 100644
--- a/src/location/quickmapitems/qdeclarativepolylinemapitem_p.h
+++ b/src/location/quickmapitems/qdeclarativepolylinemapitem_p.h
@@ -109,7 +109,6 @@ public:
QGeoPath m_geopath;
QDeclarativeMapLineProperties m_line;
- bool m_dirtyMaterial = true;
bool m_updatingGeometry = false;
std::unique_ptr<QDeclarativePolylineMapItemPrivate> m_d;
diff --git a/src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h b/src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h
index 4ade0fba..ef482f3c 100644
--- a/src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h
+++ b/src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h
@@ -31,47 +31,15 @@ QT_BEGIN_NAMESPACE
class QQuickShape;
class QQuickShapePath;
-class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometry : public QGeoMapItemGeometry
+struct QGeoMapPolylineGeometry : QGeoMapItemGeometry
{
-public:
- QGeoMapPolylineGeometry();
-
void updateSourcePoints(const QGeoMap &map,
- const QList<QDoubleVector2D> &path,
- const QGeoCoordinate geoLeftBound);
-
- void updateScreenPoints(const QGeoMap &map,
- qreal strokeWidth,
- bool adjustTranslation = true);
-
- void clearSource();
-
- bool contains(const QPointF &point) const override;
-
- QList<QList<QDoubleVector2D> > clipPath(const QGeoMap &map,
- const QList<QDoubleVector2D> &path,
- QDoubleVector2D &leftBoundWrapped);
-
- void pathToScreen(const QGeoMap &map,
- const QList<QList<QDoubleVector2D> > &clippedPaths,
- const QDoubleVector2D &leftBoundWrapped);
+ const QList<QDoubleVector2D> &basePath);
QPainterPath srcPath() const { return srcPath_; }
-public:
- QList<qreal> srcPoints_;
- QList<QPainterPath::ElementType> srcPointTypes_;
QPainterPath srcPath_;
qreal maxCoord_ = 0.0;
-
-#ifdef QT_LOCATION_DEBUG
- QList<QDoubleVector2D> m_wrappedPath;
- QList<QList<QDoubleVector2D>> m_clippedPaths;
-#endif
-
- friend class QDeclarativeCircleMapItem;
- friend class QDeclarativePolygonMapItem;
- friend class QDeclarativeRectangleMapItem;
};
class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivate
@@ -116,14 +84,9 @@ public:
}
void regenerateCache();
void updateCache();
- void preserveGeometry()
- {
- m_geometry.setPreserveGeometry(true, m_poly.m_geopath.boundingGeoRectangle().topLeft());
- }
void afterViewportChanged() override
{
// preserveGeometry is cleared in updateMapItemPaintNode
- preserveGeometry();
markSourceDirtyAndUpdate();
}
void onMapSet() override
@@ -134,13 +97,11 @@ public:
void onGeoGeometryChanged() override
{
regenerateCache();
- preserveGeometry();
markSourceDirtyAndUpdate();
}
void onGeoGeometryUpdated() override
{
updateCache();
- preserveGeometry();
markSourceDirtyAndUpdate();
}
void onItemGeometryChanged() override
diff --git a/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp b/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp
index 1fbc9215..0e4e9e02 100644
--- a/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp
+++ b/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp
@@ -211,7 +211,6 @@ void QDeclarativeRectangleMapItem::setColor(const QColor &color)
if (m_color == color)
return;
m_color = color;
- m_dirtyMaterial = true;
polishAndUpdate();
emit colorChanged(m_color);
}
@@ -359,8 +358,7 @@ void QDeclarativeRectangleMapItemPrivateCPU::updatePolish()
const QList<QGeoCoordinate> perimeter = QGeoMapItemGeometry::path(m_rect.m_rectangle);
const QList<QDoubleVector2D> pathMercator_ = QGeoMapItemGeometry::pathMercator(perimeter);
- m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft());
- m_geometry.updateSourcePoints(*m_rect.map(), pathMercator_);
+ m_geometry.updateSourcePoints(*m_rect.map(), QList<QList<QDoubleVector2D>>{pathMercator_});
m_rect.setShapeTriangulationScale(m_shape, m_geometry.maxCoord());
@@ -389,10 +387,8 @@ QSGNode *QDeclarativeRectangleMapItemPrivateCPU::updateMapItemPaintNode(QSGNode
{
Q_UNUSED(data);
delete oldNode;
- if (m_geometry.isScreenDirty() || m_rect.m_dirtyMaterial) {
- m_geometry.setPreserveGeometry(false);
+ if (m_geometry.isScreenDirty()) {
m_geometry.markClean();
- m_rect.m_dirtyMaterial = false;
}
return nullptr;
}
diff --git a/src/location/quickmapitems/qdeclarativerectanglemapitem_p.h b/src/location/quickmapitems/qdeclarativerectanglemapitem_p.h
index 883b261e..467857fe 100644
--- a/src/location/quickmapitems/qdeclarativerectanglemapitem_p.h
+++ b/src/location/quickmapitems/qdeclarativerectanglemapitem_p.h
@@ -80,7 +80,6 @@ private:
QGeoRectangle m_rectangle;
QDeclarativeMapLineProperties m_border;
QColor m_color = Qt::transparent;
- bool m_dirtyMaterial = true;
bool m_updatingGeometry = false;
diff --git a/src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h b/src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h
index 3f914dd0..2df6767d 100644
--- a/src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h
+++ b/src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h
@@ -72,12 +72,10 @@ public:
}
void onItemGeometryChanged() override
{
- m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft());
markSourceDirtyAndUpdate();
}
void afterViewportChanged() override
{
- m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft());
markSourceDirtyAndUpdate();
}
void updatePolish() override;
diff --git a/src/location/quickmapitems/qgeomapitemgeometry_p.h b/src/location/quickmapitems/qgeomapitemgeometry_p.h
index c7fd2f57..0fa90ae4 100644
--- a/src/location/quickmapitems/qgeomapitemgeometry_p.h
+++ b/src/location/quickmapitems/qgeomapitemgeometry_p.h
@@ -74,12 +74,6 @@ public:
inline void markClean() { screenDirty_ = (sourceDirty_ = false); clipToViewport_ = true;}
inline void clearScreen() { screenDirty_ = false; }
- inline void setPreserveGeometry(bool value, const QGeoCoordinate &geoLeftBound = QGeoCoordinate())
- {
- preserveGeometry_ = value;
- if (preserveGeometry_)
- geoLeftBound_ = geoLeftBound;
- }
inline QGeoCoordinate geoLeftBound() { return geoLeftBound_; }
inline QRectF sourceBoundingBox() const { return sourceBounds_; }