diff options
Diffstat (limited to 'src')
88 files changed, 5869 insertions, 1100 deletions
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 917ad25a..d4606066 100644 --- a/src/imports/location/location.cpp +++ b/src/imports/location/location.cpp @@ -206,6 +206,14 @@ public: qmlRegisterUncreatableType<QDeclarativeGeoMapItemBase, 14>(uri, major, minor, "GeoMapItemBase", QStringLiteral("GeoMapItemBase is not intended instantiable by developer.")); + minor = 15; + qmlRegisterType<QDeclarativePolylineMapItem, 15>(uri, major, minor, "MapPolyline"); + qmlRegisterType<QDeclarativePolygonMapItem, 15>(uri, major, minor, "MapPolygon"); + qmlRegisterType<QDeclarativeRectangleMapItem, 15>(uri, major, minor, "MapRectangle"); + qmlRegisterType<QDeclarativeCircleMapItem, 15>(uri, major, minor, "MapCircle"); + qmlRegisterUncreatableType<QDeclarativeGeoMapItemBase, 15>(uri, major, minor, "GeoMapItemBase", + QStringLiteral("GeoMapItemBase is not intended instantiable by developer.")); + // The minor version used to be the current Qt 5 minor. For compatibility it is the last // Qt 5 release. qmlRegisterModule(uri, 5, 15); diff --git a/src/location/declarativemaps/declarativemaps.pri b/src/location/declarativemaps/declarativemaps.pri index 54a0824b..e2a922f4 100644 --- a/src/location/declarativemaps/declarativemaps.pri +++ b/src/location/declarativemaps/declarativemaps.pri @@ -29,12 +29,19 @@ PRIVATE_HEADERS += \ declarativemaps/qgeomapobject_p.h \ declarativemaps/qgeomapobject_p_p.h \ declarativemaps/qparameterizableobject_p.h \ + declarativemaps/qdeclarativegeomapitemutils_p.h \ + declarativemaps/qdeclarativepolylinemapitem_p_p.h \ + declarativemaps/qdeclarativepolygonmapitem_p_p.h \ + declarativemaps/qdeclarativerectanglemapitem_p_p.h \ + declarativemaps/qdeclarativecirclemapitem_p_p.h \ + declarativemaps/qgeosimplify_p.h \ declarativemaps/qquickgeomapgesturearea_p.h SOURCES += \ declarativemaps/error_messages.cpp \ declarativemaps/locationvaluetypehelper.cpp \ - declarativemaps/qdeclarativecirclemapitem.cpp \ + declarativemaps/qdeclarativepolylinemapitem.cpp \ + declarativemaps/qdeclarativepolygonmapitem.cpp \ declarativemaps/qdeclarativegeocodemodel.cpp \ declarativemaps/qdeclarativegeomaneuver.cpp \ declarativemaps/qdeclarativegeomapcopyrightsnotice.cpp \ @@ -50,14 +57,16 @@ SOURCES += \ declarativemaps/qdeclarativegeoroutemodel.cpp \ declarativemaps/qdeclarativegeoroutesegment.cpp \ declarativemaps/qdeclarativegeoserviceprovider.cpp \ - declarativemaps/qdeclarativepolygonmapitem.cpp \ - declarativemaps/qdeclarativepolylinemapitem.cpp \ + declarativemaps/qdeclarativecirclemapitem.cpp \ declarativemaps/qdeclarativerectanglemapitem.cpp \ declarativemaps/qdeclarativeroutemapitem.cpp \ declarativemaps/qgeomapitemgeometry.cpp \ declarativemaps/qgeomapobject.cpp \ + declarativemaps/qdeclarativegeomapitemutils.cpp \ declarativemaps/qparameterizableobject.cpp \ + declarativemaps/qgeosimplify.cpp \ declarativemaps/qquickgeomapgesturearea.cpp load(qt_build_paths) LIBS_PRIVATE += -L$$MODULE_BASE_OUTDIR/lib -lpoly2tri$$qtPlatformTargetSuffix() -lclip2tri$$qtPlatformTargetSuffix() + diff --git a/src/location/declarativemaps/locationvaluetypehelper.cpp b/src/location/declarativemaps/locationvaluetypehelper.cpp index 3e2f3658..e1ef7f59 100644 --- a/src/location/declarativemaps/locationvaluetypehelper.cpp +++ b/src/location/declarativemaps/locationvaluetypehelper.cpp @@ -164,7 +164,7 @@ QJSValue fromList(const QObject *object, const QList<QGeoCoordinate> &list) pathArray->put(i++, cv); } - return QJSValue(v4, pathArray.asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(pathArray.asReturnedValue()); } QList<QGeoCoordinate> toList(const QObject *object, const QJSValue &value) diff --git a/src/location/declarativemaps/qdeclarativecirclemapitem.cpp b/src/location/declarativemaps/qdeclarativecirclemapitem.cpp index 846fccbf..f0ba122c 100644 --- a/src/location/declarativemaps/qdeclarativecirclemapitem.cpp +++ b/src/location/declarativemaps/qdeclarativecirclemapitem.cpp @@ -57,6 +57,7 @@ #include <sweep/cdt.h> #include <QtPositioning/private/qclipperutils_p.h> +#include "qdeclarativecirclemapitem_p_p.h" QT_BEGIN_NAMESPACE @@ -274,83 +275,36 @@ void QGeoMapCircleGeometry::updateScreenPointsInvert(const QList<QDoubleVector2D sourceBounds_ = screenBounds_; } -bool QDeclarativeCircleMapItem::crossEarthPole(const QGeoCoordinate ¢er, qreal distance) +struct CircleBackendSelector { - qreal poleLat = 90; - QGeoCoordinate northPole = QGeoCoordinate(poleLat, center.longitude()); - QGeoCoordinate southPole = QGeoCoordinate(-poleLat, center.longitude()); - // approximate using great circle distance - qreal distanceToNorthPole = center.distanceTo(northPole); - qreal distanceToSouthPole = center.distanceTo(southPole); - if (distanceToNorthPole < distance || distanceToSouthPole < distance) - return true; - return false; -} - -void QDeclarativeCircleMapItem::calculatePeripheralPoints(QList<QGeoCoordinate> &path, - const QGeoCoordinate ¢er, - qreal distance, - int steps, - QGeoCoordinate &leftBound) -{ - // Calculate points based on great-circle distance - // Calculation is the same as GeoCoordinate's atDistanceAndAzimuth function - // but tweaked here for computing multiple points - - // pre-calculations - steps = qMax(steps, 3); - qreal centerLon = center.longitude(); - qreal minLon = centerLon; - qreal latRad = QLocationUtils::radians(center.latitude()); - qreal lonRad = QLocationUtils::radians(centerLon); - qreal cosLatRad = std::cos(latRad); - qreal sinLatRad = std::sin(latRad); - qreal ratio = (distance / QLocationUtils::earthMeanRadius()); - qreal cosRatio = std::cos(ratio); - qreal sinRatio = std::sin(ratio); - qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio; - qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio; - int idx = 0; - for (int i = 0; i < steps; ++i) { - qreal azimuthRad = 2 * M_PI * i / steps; - qreal resultLatRad = std::asin(sinLatRad_x_cosRatio - + cosLatRad_x_sinRatio * std::cos(azimuthRad)); - qreal resultLonRad = lonRad + std::atan2(std::sin(azimuthRad) * cosLatRad_x_sinRatio, - cosRatio - sinLatRad * std::sin(resultLatRad)); - qreal lat2 = QLocationUtils::degrees(resultLatRad); - qreal lon2 = QLocationUtils::wrapLong(QLocationUtils::degrees(resultLonRad)); - - path << QGeoCoordinate(lat2, lon2, center.altitude()); - // Consider only points in the left half of the circle for the left bound. - if (azimuthRad > M_PI) { - if (lon2 > centerLon) // if point and center are on different hemispheres - lon2 -= 360; - if (lon2 < minLon) { - minLon = lon2; - idx = i; - } - } + CircleBackendSelector() + { + backend = (qgetenv("QTLOCATION_OPENGL_ITEMS").toInt()) ? QDeclarativeCircleMapItem::OpenGL : QDeclarativeCircleMapItem::Software; } - leftBound = path.at(idx); -} + QDeclarativeCircleMapItem::Backend backend = QDeclarativeCircleMapItem::Software; +}; + +Q_GLOBAL_STATIC(CircleBackendSelector, mapCircleBackendSelector) QDeclarativeCircleMapItem::QDeclarativeCircleMapItem(QQuickItem *parent) -: QDeclarativeGeoMapItemBase(parent), border_(this), color_(Qt::transparent), dirtyMaterial_(true), - updatingGeometry_(false) +: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent), m_dirtyMaterial(true), + m_updatingGeometry(false) + , m_d(new QDeclarativeCircleMapItemPrivateCPU(*this)) { + // ToDo: handle envvar, and switch implementation. m_itemType = QGeoMap::MapCircle; setFlag(ItemHasContents, true); - QObject::connect(&border_, SIGNAL(colorChanged(QColor)), - this, SLOT(markSourceDirtyAndUpdate())); - QObject::connect(&border_, SIGNAL(widthChanged(qreal)), - this, SLOT(markSourceDirtyAndUpdate())); + QObject::connect(&m_border, SIGNAL(colorChanged(QColor)), + this, SLOT(onLinePropertiesChanged())); + QObject::connect(&m_border, SIGNAL(widthChanged(qreal)), + this, SLOT(onLinePropertiesChanged())); // assume that circles are not self-intersecting // to speed up processing // FIXME: unfortunately they self-intersect at the poles due to current drawing method // so the line is commented out until fixed //geometry_.setAssumeSimple(true); - + setBackend(mapCircleBackendSelector->backend); } QDeclarativeCircleMapItem::~QDeclarativeCircleMapItem() @@ -371,23 +325,24 @@ QDeclarativeCircleMapItem::~QDeclarativeCircleMapItem() */ QDeclarativeMapLineProperties *QDeclarativeCircleMapItem::border() { - return &border_; + return &m_border; } void QDeclarativeCircleMapItem::markSourceDirtyAndUpdate() { - geometry_.markSourceDirty(); - borderGeometry_.markSourceDirty(); - polishAndUpdate(); + m_d->markSourceDirtyAndUpdate(); +} + +void QDeclarativeCircleMapItem::onLinePropertiesChanged() +{ + m_d->onLinePropertiesChanged(); } void QDeclarativeCircleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) { QDeclarativeGeoMapItemBase::setMap(quickMap,map); - if (!map) - return; - updateCirclePath(); - markSourceDirtyAndUpdate(); + if (map) + m_d->onMapSet(); } /*! @@ -399,18 +354,18 @@ void QDeclarativeCircleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *ma */ void QDeclarativeCircleMapItem::setCenter(const QGeoCoordinate ¢er) { - if (circle_.center() == center) + if (m_circle.center() == center) return; - circle_.setCenter(center); - updateCirclePath(); - markSourceDirtyAndUpdate(); + possiblySwitchBackend(m_circle.center(), m_circle.radius(), center, m_circle.radius()); + m_circle.setCenter(center); + m_d->onGeoGeometryChanged(); emit centerChanged(center); } QGeoCoordinate QDeclarativeCircleMapItem::center() { - return circle_.center(); + return m_circle.center(); } /*! @@ -421,17 +376,17 @@ QGeoCoordinate QDeclarativeCircleMapItem::center() */ void QDeclarativeCircleMapItem::setColor(const QColor &color) { - if (color_ == color) + if (m_color == color) return; - color_ = color; - dirtyMaterial_ = true; + m_color = color; + m_dirtyMaterial = true; update(); - emit colorChanged(color_); + emit colorChanged(m_color); } QColor QDeclarativeCircleMapItem::color() const { - return color_; + return m_color; } /*! @@ -443,18 +398,18 @@ QColor QDeclarativeCircleMapItem::color() const */ void QDeclarativeCircleMapItem::setRadius(qreal radius) { - if (circle_.radius() == radius) + if (m_circle.radius() == radius) return; - circle_.setRadius(radius); - updateCirclePath(); - markSourceDirtyAndUpdate(); + possiblySwitchBackend(m_circle.center(), m_circle.radius(), m_circle.center(), radius); + m_circle.setRadius(radius); + m_d->onGeoGeometryChanged(); emit radiusChanged(radius); } qreal QDeclarativeCircleMapItem::radius() const { - return circle_.radius(); + return m_circle.radius(); } /*! @@ -472,23 +427,7 @@ qreal QDeclarativeCircleMapItem::radius() const */ QSGNode *QDeclarativeCircleMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) { - Q_UNUSED(data); - - MapPolygonNode *node = static_cast<MapPolygonNode *>(oldNode); - - if (!node) - node = new MapPolygonNode(); - - //TODO: update only material - if (geometry_.isScreenDirty() || borderGeometry_.isScreenDirty() || dirtyMaterial_) { - node->update(color_, border_.color(), &geometry_, &borderGeometry_); - geometry_.setPreserveGeometry(false); - borderGeometry_.setPreserveGeometry(false); - geometry_.markClean(); - borderGeometry_.markClean(); - dirtyMaterial_ = false; - } - return node; + return m_d->updateMapItemPaintNode(oldNode, data); } /*! @@ -498,111 +437,41 @@ void QDeclarativeCircleMapItem::updatePolish() { if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) return; - if (!circle_.isValid()) { - geometry_.clear(); - borderGeometry_.clear(); - setWidth(0); - setHeight(0); - return; - } - - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection()); - QScopedValueRollback<bool> rollback(updatingGeometry_); - updatingGeometry_ = true; - - QList<QDoubleVector2D> circlePath = circlePath_; - - int pathCount = circlePath.size(); - bool preserve = preserveCircleGeometry(circlePath, circle_.center(), circle_.radius(), p); - // using leftBound_ instead of the analytically calculated circle_.boundingGeoRectangle().topLeft()); - // to fix QTBUG-62154 - geometry_.setPreserveGeometry(true, leftBound_); // to set the geoLeftBound_ - geometry_.setPreserveGeometry(preserve, leftBound_); - - bool invertedCircle = false; - if (crossEarthPole(circle_.center(), circle_.radius()) && circlePath.size() == pathCount) { - geometry_.updateScreenPointsInvert(circlePath, *map()); // invert fill area for really huge circles - invertedCircle = true; - } else { - geometry_.updateSourcePoints(*map(), circlePath); - geometry_.updateScreenPoints(*map(), border_.width()); - } - - borderGeometry_.clear(); - QList<QGeoMapItemGeometry *> geoms; - geoms << &geometry_; - - if (border_.color() != Qt::transparent && border_.width() > 0) { - QList<QDoubleVector2D> closedPath = circlePath; - closedPath << closedPath.first(); - - if (invertedCircle) { - closedPath = circlePath_; - closedPath << closedPath.first(); - std::reverse(closedPath.begin(), closedPath.end()); - } - - borderGeometry_.setPreserveGeometry(true, leftBound_); - borderGeometry_.setPreserveGeometry(preserve, leftBound_); - - // Use srcOrigin_ from fill geometry after clipping to ensure that translateToCommonOrigin won't fail. - const QGeoCoordinate &geometryOrigin = geometry_.origin(); - - borderGeometry_.srcPoints_.clear(); - borderGeometry_.srcPointTypes_.clear(); - - QDoubleVector2D borderLeftBoundWrapped; - QList<QList<QDoubleVector2D > > clippedPaths = borderGeometry_.clipPath(*map(), closedPath, borderLeftBoundWrapped); - if (clippedPaths.size()) { - borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); - borderGeometry_.pathToScreen(*map(), clippedPaths, borderLeftBoundWrapped); - borderGeometry_.updateScreenPoints(*map(), border_.width()); - geoms << &borderGeometry_; - } else { - borderGeometry_.clear(); - } - } - - QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); - - if (invertedCircle || !preserve) { - setWidth(combined.width()); - setHeight(combined.height()); - setPositionOnMap(geometry_.origin(), geometry_.firstPointOffset()); - } else { - setWidth(combined.width() + 2 * border_.width()); - setHeight(combined.height() + 2 * border_.width()); - } - - // No offsetting here, even in normal case, because first point offset is already translated - setPositionOnMap(geometry_.origin(), geometry_.firstPointOffset()); + m_d->updatePolish(); } /*! \internal + + The OpenGL backend doesn't do circles crossing poles yet. + So if that backend is selected and the circle crosses the poles, use the CPU backend instead. */ -void QDeclarativeCircleMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) +void QDeclarativeCircleMapItem::possiblySwitchBackend(const QGeoCoordinate &oldCenter, qreal oldRadius, const QGeoCoordinate &newCenter, qreal newRadius) { - if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) + if (m_backend != QDeclarativeCircleMapItem::OpenGL) return; - markSourceDirtyAndUpdate(); + // if old does not cross and new crosses, move to CPU. + if (!QDeclarativeCircleMapItemPrivate::crossEarthPole(oldCenter, oldRadius) + && !QDeclarativeCircleMapItemPrivate::crossEarthPole(newCenter, newRadius)) { + QScopedPointer<QDeclarativeCircleMapItemPrivate> d(static_cast<QDeclarativeCircleMapItemPrivate *>(new QDeclarativeCircleMapItemPrivateCPU(*this))); + m_d.swap(d); + } else if (QDeclarativeCircleMapItemPrivate::crossEarthPole(oldCenter, oldRadius) + && !QDeclarativeCircleMapItemPrivate::crossEarthPole(newCenter, newRadius)) { // else if old crosses and new does not cross, move back to OpenGL + QScopedPointer<QDeclarativeCircleMapItemPrivate> d(static_cast<QDeclarativeCircleMapItemPrivate *>(new QDeclarativeCircleMapItemPrivateOpenGL(*this))); + m_d.swap(d); + } } /*! \internal */ -void QDeclarativeCircleMapItem::updateCirclePath() +void QDeclarativeCircleMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) { - if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + if (event.mapSize.isEmpty()) return; - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection()); - QList<QGeoCoordinate> path; - calculatePeripheralPoints(path, circle_.center(), circle_.radius(), CircleSamples, leftBound_); - circlePath_.clear(); - for (const QGeoCoordinate &c : path) - circlePath_ << p.geoToMapProjection(c); + m_d->afterViewportChanged(); } /*! @@ -610,30 +479,69 @@ void QDeclarativeCircleMapItem::updateCirclePath() */ bool QDeclarativeCircleMapItem::contains(const QPointF &point) const { - return (geometry_.contains(point) || borderGeometry_.contains(point)); + return m_d->contains(point); + // } const QGeoShape &QDeclarativeCircleMapItem::geoShape() const { - return circle_; + return m_circle; } void QDeclarativeCircleMapItem::setGeoShape(const QGeoShape &shape) { - if (shape == circle_) + if (shape == m_circle) return; const QGeoCircle circle(shape); // if shape isn't a circle, circle will be created as a default-constructed circle - const bool centerHasChanged = circle.center() != circle_.center(); - const bool radiusHasChanged = circle.radius() != circle_.radius(); - circle_ = circle; + const bool centerHasChanged = circle.center() != m_circle.center(); + const bool radiusHasChanged = circle.radius() != m_circle.radius(); + possiblySwitchBackend(m_circle.center(), m_circle.radius(), circle.center(), circle.radius()); + m_circle = circle; - updateCirclePath(); - markSourceDirtyAndUpdate(); + m_d->onGeoGeometryChanged(); if (centerHasChanged) - emit centerChanged(circle_.center()); + emit centerChanged(m_circle.center()); if (radiusHasChanged) - emit radiusChanged(circle_.radius()); + emit radiusChanged(m_circle.radius()); +} + +/*! + \qmlproperty MapCircle.Backend QtLocation::MapCircle::backend + + This property holds which backend is in use to render the map item. + Valid values are \b MapCircle.Software and \b{MapCircle.OpenGL}. + The default value is \b{MapCircle.Software}. + + \note \b{The release of this API with Qt 5.15 is a Technology Preview}. + Ideally, as the OpenGL backends for map items mature, there will be + no more need to also offer the legacy software-projection backend. + So this property will likely disappear at some later point. + To select OpenGL-accelerated item backends without using this property, + it is also possible to set the environment variable \b QTLOCATION_OPENGL_ITEMS + to \b{1}. + Also note that all current OpenGL backends won't work as expected when enabling + layers on the individual item, or when running on OpenGL core profiles greater than 2.x. + + \since 5.15 +*/ + +QDeclarativeCircleMapItem::Backend QDeclarativeCircleMapItem::backend() const +{ + return m_backend; +} + +void QDeclarativeCircleMapItem::setBackend(QDeclarativeCircleMapItem::Backend b) +{ + if (b == m_backend) + return; + m_backend = b; + QScopedPointer<QDeclarativeCircleMapItemPrivate> d((m_backend == Software) + ? static_cast<QDeclarativeCircleMapItemPrivate *>(new QDeclarativeCircleMapItemPrivateCPU(*this)) + : static_cast<QDeclarativeCircleMapItemPrivate * >(new QDeclarativeCircleMapItemPrivateOpenGL(*this))); + m_d.swap(d); + m_d->onGeoGeometryChanged(); + emit backendChanged(); } /*! @@ -641,21 +549,27 @@ void QDeclarativeCircleMapItem::setGeoShape(const QGeoShape &shape) */ void QDeclarativeCircleMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { - if (!map() || !circle_.isValid() || updatingGeometry_ || newGeometry == oldGeometry) { + if (!map() || !m_circle.isValid() || m_updatingGeometry || newGeometry == oldGeometry) { QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); return; } - QDoubleVector2D newPoint = QDoubleVector2D(x(),y()) + QDoubleVector2D(width(), height()) / 2; + QDoubleVector2D newPoint = QDoubleVector2D(x(),y()) + QDoubleVector2D(width(), height()) * 0.5; QGeoCoordinate newCoordinate = map()->geoProjection().itemPositionToCoordinate(newPoint, false); if (newCoordinate.isValid()) - setCenter(newCoordinate); + setCenter(newCoordinate); // ToDo: this is incorrect. setting such center might yield to another geometry changed. // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested // call to this function. } -bool QDeclarativeCircleMapItem::preserveCircleGeometry (QList<QDoubleVector2D> &path, +QDeclarativeCircleMapItemPrivate::~QDeclarativeCircleMapItemPrivate() {} + +QDeclarativeCircleMapItemPrivateCPU::~QDeclarativeCircleMapItemPrivateCPU() {} + +QDeclarativeCircleMapItemPrivateOpenGL::~QDeclarativeCircleMapItemPrivateOpenGL() {} + +bool QDeclarativeCircleMapItemPrivate::preserveCircleGeometry (QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, qreal distance, const QGeoProjectionWebMercator &p) { // if circle crosses north/south pole, then don't preserve circular shape, @@ -664,7 +578,6 @@ bool QDeclarativeCircleMapItem::preserveCircleGeometry (QList<QDoubleVector2D> & return false; } return true; - } /* @@ -684,7 +597,7 @@ bool QDeclarativeCircleMapItem::preserveCircleGeometry (QList<QDoubleVector2D> & * | ____ | * \__/ \__/ */ -void QDeclarativeCircleMapItem::updateCirclePathForRendering(QList<QDoubleVector2D> &path, +void QDeclarativeCircleMapItemPrivate::updateCirclePathForRendering(QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, qreal distance, const QGeoProjectionWebMercator &p) { @@ -743,6 +656,66 @@ void QDeclarativeCircleMapItem::updateCirclePathForRendering(QList<QDoubleVector } } +bool QDeclarativeCircleMapItemPrivate::crossEarthPole(const QGeoCoordinate ¢er, qreal distance) +{ + qreal poleLat = 90; + QGeoCoordinate northPole = QGeoCoordinate(poleLat, center.longitude()); + QGeoCoordinate southPole = QGeoCoordinate(-poleLat, center.longitude()); + // approximate using great circle distance + qreal distanceToNorthPole = center.distanceTo(northPole); + qreal distanceToSouthPole = center.distanceTo(southPole); + if (distanceToNorthPole < distance || distanceToSouthPole < distance) + return true; + return false; +} + +void QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(QList<QGeoCoordinate> &path, + const QGeoCoordinate ¢er, + qreal distance, + int steps, + QGeoCoordinate &leftBound) +{ + // Calculate points based on great-circle distance + // Calculation is the same as GeoCoordinate's atDistanceAndAzimuth function + // but tweaked here for computing multiple points + + // pre-calculations + steps = qMax(steps, 3); + qreal centerLon = center.longitude(); + qreal minLon = centerLon; + qreal latRad = QLocationUtils::radians(center.latitude()); + qreal lonRad = QLocationUtils::radians(centerLon); + qreal cosLatRad = std::cos(latRad); + qreal sinLatRad = std::sin(latRad); + qreal ratio = (distance / QLocationUtils::earthMeanRadius()); + qreal cosRatio = std::cos(ratio); + qreal sinRatio = std::sin(ratio); + qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio; + qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio; + int idx = 0; + for (int i = 0; i < steps; ++i) { + qreal azimuthRad = 2 * M_PI * i / steps; + qreal resultLatRad = std::asin(sinLatRad_x_cosRatio + + cosLatRad_x_sinRatio * std::cos(azimuthRad)); + qreal resultLonRad = lonRad + std::atan2(std::sin(azimuthRad) * cosLatRad_x_sinRatio, + cosRatio - sinLatRad * std::sin(resultLatRad)); + qreal lat2 = QLocationUtils::degrees(resultLatRad); + qreal lon2 = QLocationUtils::wrapLong(QLocationUtils::degrees(resultLonRad)); + + path << QGeoCoordinate(lat2, lon2, center.altitude()); + // Consider only points in the left half of the circle for the left bound. + if (azimuthRad > M_PI) { + if (lon2 > centerLon) // if point and center are on different hemispheres + lon2 -= 360; + if (lon2 < minLon) { + minLon = lon2; + idx = i; + } + } + } + leftBound = path.at(idx); +} + ////////////////////////////////////////////////////////////////////// QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativecirclemapitem_p.h b/src/location/declarativemaps/qdeclarativecirclemapitem_p.h index 4b3f81c0..a6715e30 100644 --- a/src/location/declarativemaps/qdeclarativecirclemapitem_p.h +++ b/src/location/declarativemaps/qdeclarativecirclemapitem_p.h @@ -50,33 +50,33 @@ #include <QtLocation/private/qlocationglobal_p.h> #include <QtLocation/private/qdeclarativegeomapitembase_p.h> -#include <QtLocation/private/qdeclarativepolylinemapitem_p.h> -#include <QtLocation/private/qdeclarativepolygonmapitem_p.h> +#include <QtLocation/private/qdeclarativepolylinemapitem_p_p.h> #include <QSGGeometryNode> #include <QSGFlatColorMaterial> #include <QtPositioning/QGeoCircle> QT_BEGIN_NAMESPACE -class Q_LOCATION_PRIVATE_EXPORT QGeoMapCircleGeometry : public QGeoMapPolygonGeometry -{ -public: - QGeoMapCircleGeometry(); - - void updateScreenPointsInvert(const QList<QDoubleVector2D> &circlePath, const QGeoMap &map); -}; - +class QDeclarativeCircleMapItemPrivate; class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCircleMapItem : public QDeclarativeGeoMapItemBase { Q_OBJECT + Q_ENUMS(Backend) + Q_PROPERTY(QGeoCoordinate center READ center WRITE setCenter NOTIFY centerChanged) Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(QDeclarativeMapLineProperties *border READ border CONSTANT) + Q_PROPERTY(Backend backend READ backend WRITE setBackend NOTIFY backendChanged REVISION 15) public: - explicit QDeclarativeCircleMapItem(QQuickItem *parent = 0); - ~QDeclarativeCircleMapItem(); + enum Backend { + Software = 0, + OpenGL = 1 + }; + + explicit QDeclarativeCircleMapItem(QQuickItem *parent = nullptr); + ~QDeclarativeCircleMapItem() override; virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) override; virtual QSGNode *updateMapItemPaintNode(QSGNode *, UpdatePaintNodeData *) override; @@ -96,40 +96,41 @@ public: const QGeoShape &geoShape() const override; void setGeoShape(const QGeoShape &shape) override; - static bool crossEarthPole(const QGeoCoordinate ¢er, qreal distance); - static void calculatePeripheralPoints(QList<QGeoCoordinate> &path, const QGeoCoordinate ¢er, - qreal distance, int steps, QGeoCoordinate &leftBound); - static bool preserveCircleGeometry(QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, - qreal distance, const QGeoProjectionWebMercator &p); - static void updateCirclePathForRendering(QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, - qreal distance, const QGeoProjectionWebMercator &p); + Backend backend() const; + void setBackend(Backend b); Q_SIGNALS: void centerChanged(const QGeoCoordinate ¢er); void radiusChanged(qreal radius); void colorChanged(const QColor &color); + void backendChanged(); protected: void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; void updatePolish() override; + void possiblySwitchBackend(const QGeoCoordinate &oldCenter, qreal oldRadius, const QGeoCoordinate &newCenter, qreal newRadius); protected Q_SLOTS: void markSourceDirtyAndUpdate(); + void onLinePropertiesChanged(); virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) override; private: void updateCirclePath(); private: - QGeoCircle circle_; - QDeclarativeMapLineProperties border_; - QColor color_; - QList<QDoubleVector2D> circlePath_; - QGeoCoordinate leftBound_; - bool dirtyMaterial_; - QGeoMapCircleGeometry geometry_; - QGeoMapPolylineGeometry borderGeometry_; - bool updatingGeometry_; + QGeoCircle m_circle; + QDeclarativeMapLineProperties m_border; + QColor m_color; + bool m_dirtyMaterial; + bool m_updatingGeometry; + Backend m_backend = Software; + + QScopedPointer<QDeclarativeCircleMapItemPrivate> m_d; + + friend class QDeclarativeCircleMapItemPrivate; + friend class QDeclarativeCircleMapItemPrivateCPU; + friend class QDeclarativeCircleMapItemPrivateOpenGL; }; ////////////////////////////////////////////////////////////////////// diff --git a/src/location/declarativemaps/qdeclarativecirclemapitem_p_p.h b/src/location/declarativemaps/qdeclarativecirclemapitem_p_p.h new file mode 100644 index 00000000..4cf42173 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativecirclemapitem_p_p.h @@ -0,0 +1,449 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Paolo Angelelli <paolo.angelelli@gmail.com> +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECIRCLEMAPITEM_P_P_H +#define QDECLARATIVECIRCLEMAPITEM_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativepolygonmapitem_p_p.h> +#include <QtLocation/private/qdeclarativecirclemapitem_p.h> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapCircleGeometry : public QGeoMapPolygonGeometry +{ +public: + QGeoMapCircleGeometry(); + + void updateScreenPointsInvert(const QList<QDoubleVector2D> &circlePath, const QGeoMap &map); +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCircleMapItemPrivate +{ +public: + static const int CircleSamples = 128; // ToDo: make this radius && ZL dependent? + + QDeclarativeCircleMapItemPrivate(QDeclarativeCircleMapItem &circle) : m_circle(circle) + { + + } + QDeclarativeCircleMapItemPrivate(QDeclarativeCircleMapItemPrivate &other) : m_circle(other.m_circle) + { + } + + virtual ~QDeclarativeCircleMapItemPrivate(); + virtual void onLinePropertiesChanged() = 0; + virtual void markSourceDirtyAndUpdate() = 0; + virtual void onMapSet() = 0; + virtual void onGeoGeometryChanged() = 0; + virtual void onItemGeometryChanged() = 0; + virtual void updatePolish() = 0; + virtual void afterViewportChanged() = 0; + virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) = 0; + virtual bool contains(const QPointF &point) const = 0; + + void updateCirclePath() + { + if (!m_circle.map() || m_circle.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_circle.map()->geoProjection()); + QList<QGeoCoordinate> path; + calculatePeripheralPoints(path, m_circle.center(), m_circle.radius(), CircleSamples, m_leftBound); + m_circlePath.clear(); + for (const QGeoCoordinate &c : path) + m_circlePath << p.geoToMapProjection(c); + } + + static bool crossEarthPole(const QGeoCoordinate ¢er, qreal distance); + + static bool preserveCircleGeometry(QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, + qreal distance, const QGeoProjectionWebMercator &p); + static void updateCirclePathForRendering(QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, + qreal distance, const QGeoProjectionWebMercator &p); + + static void calculatePeripheralPoints(QList<QGeoCoordinate> &path, const QGeoCoordinate ¢er, + qreal distance, int steps, QGeoCoordinate &leftBound); + + QDeclarativeCircleMapItem &m_circle; + QList<QDoubleVector2D> m_circlePath; + QGeoCoordinate m_leftBound; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCircleMapItemPrivateCPU: public QDeclarativeCircleMapItemPrivate +{ +public: + + QDeclarativeCircleMapItemPrivateCPU(QDeclarativeCircleMapItem &circle) : QDeclarativeCircleMapItemPrivate(circle) + { + } + + QDeclarativeCircleMapItemPrivateCPU(QDeclarativeCircleMapItemPrivate &other) + : QDeclarativeCircleMapItemPrivate(other) + { + } + + ~QDeclarativeCircleMapItemPrivateCPU() override; + + void onLinePropertiesChanged() override + { + // mark dirty just in case we're a width change + markSourceDirtyAndUpdate(); + } + void markSourceDirtyAndUpdate() override + { + // preserveGeometry is cleared in updateMapItemPaintNode + m_geometry.markSourceDirty(); + m_borderGeometry.markSourceDirty(); + m_circle.polishAndUpdate(); + } + void onMapSet() override + { + updateCirclePath(); + markSourceDirtyAndUpdate(); + } + void onGeoGeometryChanged() override + { + updateCirclePath(); + markSourceDirtyAndUpdate(); + } + void onItemGeometryChanged() override + { + onGeoGeometryChanged(); + } + void afterViewportChanged() override + { + markSourceDirtyAndUpdate(); + } + void updatePolish() override + { + if (!m_circle.m_circle.isValid()) { + m_geometry.clear(); + m_borderGeometry.clear(); + m_circle.setWidth(0); + m_circle.setHeight(0); + return; + } + + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_circle.map()->geoProjection()); + QScopedValueRollback<bool> rollback(m_circle.m_updatingGeometry); + m_circle.m_updatingGeometry = true; + + QList<QDoubleVector2D> circlePath = m_circlePath; + + int pathCount = circlePath.size(); + bool preserve = preserveCircleGeometry(circlePath, m_circle.m_circle.center(), m_circle.m_circle.radius(), p); + // using leftBound_ instead of the analytically calculated circle_.boundingGeoRectangle().topLeft()); + // to fix QTBUG-62154 + m_geometry.setPreserveGeometry(true, m_leftBound); // to set the geoLeftBound_ + m_geometry.setPreserveGeometry(preserve, m_leftBound); + + bool invertedCircle = false; + if (crossEarthPole(m_circle.m_circle.center(), m_circle.m_circle.radius()) && circlePath.size() == pathCount) { + m_geometry.updateScreenPointsInvert(circlePath, *m_circle.map()); // invert fill area for really huge circles + invertedCircle = true; + } else { + m_geometry.updateSourcePoints(*m_circle.map(), circlePath); + m_geometry.updateScreenPoints(*m_circle.map(), m_circle.m_border.width()); + } + + m_borderGeometry.clear(); + QList<QGeoMapItemGeometry *> geoms; + geoms << &m_geometry; + + if (m_circle.m_border.color() != Qt::transparent && m_circle.m_border.width() > 0) { + QList<QDoubleVector2D> closedPath = circlePath; + closedPath << closedPath.first(); + + if (invertedCircle) { + closedPath = m_circlePath; + closedPath << closedPath.first(); + std::reverse(closedPath.begin(), closedPath.end()); + } + + m_borderGeometry.setPreserveGeometry(true, m_leftBound); + m_borderGeometry.setPreserveGeometry(preserve, m_leftBound); + + // Use srcOrigin_ from fill geometry after clipping to ensure that translateToCommonOrigin won't fail. + const QGeoCoordinate &geometryOrigin = m_geometry.origin(); + + m_borderGeometry.srcPoints_.clear(); + m_borderGeometry.srcPointTypes_.clear(); + + QDoubleVector2D borderLeftBoundWrapped; + QList<QList<QDoubleVector2D > > clippedPaths = m_borderGeometry.clipPath(*m_circle.map(), closedPath, borderLeftBoundWrapped); + if (clippedPaths.size()) { + borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); + m_borderGeometry.pathToScreen(*m_circle.map(), clippedPaths, borderLeftBoundWrapped); + m_borderGeometry.updateScreenPoints(*m_circle.map(), m_circle.m_border.width()); + geoms << &m_borderGeometry; + } else { + m_borderGeometry.clear(); + } + } + + QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); + + if (invertedCircle || !preserve) { + m_circle.setWidth(combined.width()); + m_circle.setHeight(combined.height()); + } else { + m_circle.setWidth(combined.width() + 2 * m_circle.m_border.width()); // ToDo: Fix this! + m_circle.setHeight(combined.height() + 2 * m_circle.m_border.width()); + } + + // No offsetting here, even in normal case, because first point offset is already translated + m_circle.setPositionOnMap(m_geometry.origin(), m_geometry.firstPointOffset()); + } + + QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override + { + Q_UNUSED(data); + if (!m_node || !oldNode) { // Apparently the QSG might delete the nodes if they become invisible + m_node = new MapPolygonNode(); + if (oldNode) { + delete oldNode; + oldNode = nullptr; + } + } else { + m_node = static_cast<MapPolygonNode *>(oldNode); + } + + //TODO: update only material + if (m_geometry.isScreenDirty() || m_borderGeometry.isScreenDirty() || m_circle.m_dirtyMaterial) { + m_node->update(m_circle.m_color, m_circle.m_border.color(), &m_geometry, &m_borderGeometry); + m_geometry.setPreserveGeometry(false); + m_borderGeometry.setPreserveGeometry(false); + m_geometry.markClean(); + m_borderGeometry.markClean(); + m_circle.m_dirtyMaterial = false; + } + return m_node; + } + bool contains(const QPointF &point) const override + { + return (m_geometry.contains(point) || m_borderGeometry.contains(point)); + } + + QGeoMapCircleGeometry m_geometry; + QGeoMapPolylineGeometry m_borderGeometry; + MapPolygonNode *m_node = nullptr; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCircleMapItemPrivateOpenGL: public QDeclarativeCircleMapItemPrivate +{ +public: + QDeclarativeCircleMapItemPrivateOpenGL(QDeclarativeCircleMapItem &circle) : QDeclarativeCircleMapItemPrivate(circle) + { + } + + QDeclarativeCircleMapItemPrivateOpenGL(QDeclarativeCircleMapItemPrivate &other) + : QDeclarativeCircleMapItemPrivate(other) + { + } + + ~QDeclarativeCircleMapItemPrivateOpenGL() override; + + void onLinePropertiesChanged() override + { + m_circle.m_dirtyMaterial = true; + afterViewportChanged(); + } + void markScreenDirtyAndUpdate() + { + // preserveGeometry is cleared in updateMapItemPaintNode + m_geometry.markScreenDirty(); + m_borderGeometry.markScreenDirty(); + m_circle.polishAndUpdate(); + } + virtual void markSourceDirtyAndUpdate() override + { + updateCirclePath(); + preserveGeometry(); + m_geometry.markSourceDirty(); + m_borderGeometry.markSourceDirty(); + m_circle.polishAndUpdate(); + } + void preserveGeometry() + { + m_geometry.setPreserveGeometry(true, m_leftBound); + m_borderGeometry.setPreserveGeometry(true, m_leftBound); + } + virtual void onMapSet() override + { + markSourceDirtyAndUpdate(); + } + virtual void onGeoGeometryChanged() override + { + + markSourceDirtyAndUpdate(); + } + virtual void onItemGeometryChanged() override + { + onGeoGeometryChanged(); + } + virtual void afterViewportChanged() override + { + preserveGeometry(); + markScreenDirtyAndUpdate(); + } + virtual void updatePolish() override + { + if (m_circle.m_circle.isEmpty()) { + m_geometry.clear(); + m_borderGeometry.clear(); + m_circle.setWidth(0); + m_circle.setHeight(0); + return; + } + + QScopedValueRollback<bool> rollback(m_circle.m_updatingGeometry); + m_circle.m_updatingGeometry = true; + const qreal lineWidth = m_circle.m_border.width(); + const QColor &lineColor = m_circle.m_border.color(); + const QColor &fillColor = m_circle.color(); + if (fillColor.alpha() != 0) { + m_geometry.updateSourcePoints(*m_circle.map(), m_circlePath); + m_geometry.markScreenDirty(); + m_geometry.updateScreenPoints(*m_circle.map(), lineWidth, lineColor); + } else { + m_geometry.clearBounds(); + } + + QGeoMapItemGeometry * geom = &m_geometry; + m_borderGeometry.clearScreen(); + if (lineColor.alpha() != 0 && lineWidth > 0) { + m_borderGeometry.updateSourcePoints(*m_circle.map(), m_circle.m_circle); + m_borderGeometry.markScreenDirty(); + m_borderGeometry.updateScreenPoints(*m_circle.map(), lineWidth); + geom = &m_borderGeometry; + } + m_circle.setWidth(geom->sourceBoundingBox().width()); + m_circle.setHeight(geom->sourceBoundingBox().height()); + m_circle.setPosition(1.0 * geom->firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5)); + } + + virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override + { + Q_UNUSED(data); + + if (!m_rootNode || !oldNode) { + m_rootNode = new QDeclarativePolygonMapItemPrivateOpenGL::RootNode(); + m_node = new MapPolygonNodeGL(); + m_rootNode->appendChildNode(m_node); + m_polylinenode = new MapPolylineNodeOpenGLExtruded(); + m_rootNode->appendChildNode(m_polylinenode); + m_rootNode->markDirty(QSGNode::DirtyNodeAdded); + if (oldNode) + delete oldNode; + } else { + m_rootNode = static_cast<QDeclarativePolygonMapItemPrivateOpenGL::RootNode *>(oldNode); + } + + const QGeoMap *map = m_circle.map(); + const QMatrix4x4 &combinedMatrix = map->geoProjection().qsgTransform(); + const QDoubleVector3D &cameraCenter = map->geoProjection().centerMercator(); + + if (m_borderGeometry.isScreenDirty()) { + /* Do the border update first */ + m_polylinenode->update(m_circle.m_border.color(), + float(m_circle.m_border.width()), + &m_borderGeometry, + combinedMatrix, + cameraCenter, + Qt::SquareCap, + true, + 30); // No LOD for circles + m_borderGeometry.setPreserveGeometry(false); + m_borderGeometry.markClean(); + } else { + m_polylinenode->setSubtreeBlocked(true); + } + if (m_geometry.isScreenDirty()) { + m_node->update(m_circle.m_color, + &m_geometry, + combinedMatrix, + cameraCenter); + m_geometry.setPreserveGeometry(false); + m_geometry.markClean(); + } else { + m_node->setSubtreeBlocked(true); + } + + m_rootNode->setSubtreeBlocked(false); + return m_rootNode; + } + virtual bool contains(const QPointF &point) const override + { + const qreal lineWidth = m_circle.m_border.width(); + const QColor &lineColor = m_circle.m_border.color(); + const QRectF &bounds = (lineColor.alpha() != 0 && lineWidth > 0) ? m_borderGeometry.sourceBoundingBox() : m_geometry.sourceBoundingBox(); + if (bounds.contains(point)) { + QDeclarativeGeoMap *m = m_circle.quickMap(); + if (m) { + const QGeoCoordinate crd = m->toCoordinate(m->mapFromItem(&m_circle, point)); + return m_circle.m_circle.contains(crd) || m_borderGeometry.contains(m_circle.mapToItem(m_circle.quickMap(), point), + m_circle.border()->width(), + static_cast<const QGeoProjectionWebMercator&>(m_circle.map()->geoProjection())); + } else { + return true; + } + } + return false; + } + + QGeoMapPolygonGeometryOpenGL m_geometry; + QGeoMapPolylineGeometryOpenGL m_borderGeometry; + QDeclarativePolygonMapItemPrivateOpenGL::RootNode *m_rootNode = nullptr; + MapPolygonNodeGL *m_node = nullptr; + MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVECIRCLEMAPITEM_P_P_H diff --git a/src/location/declarativemaps/qdeclarativegeomap.cpp b/src/location/declarativemaps/qdeclarativegeomap.cpp index 167f1d5f..17baf811 100644 --- a/src/location/declarativemaps/qdeclarativegeomap.cpp +++ b/src/location/declarativemaps/qdeclarativegeomap.cpp @@ -198,7 +198,7 @@ QDeclarativeGeoMap::QDeclarativeGeoMap(QQuickItem *parent) setAcceptHoverEvents(false); setAcceptedMouseButtons(Qt::LeftButton); setFlags(QQuickItem::ItemHasContents | QQuickItem::ItemClipsChildrenToShape); - setFiltersChildMouseEvents(true); + setFiltersChildMouseEvents(true); // needed for childMouseEventFilter to work. m_activeMapType = new QDeclarativeGeoMapType(QGeoMapType(QGeoMapType::NoMap, tr("No Map"), @@ -707,7 +707,8 @@ void QDeclarativeGeoMap::mappingManagerInitialized() emit m_map->copyrightsChanged(copyrightImage); - connect(m_map.data(), &QGeoMap::sgNodeChanged, this, &QQuickItem::update); + connect(window(), &QQuickWindow::beforeSynchronizing, this, &QDeclarativeGeoMap::updateItemToWindowTransform, Qt::DirectConnection); + connect(m_map.data(), &QGeoMap::sgNodeChanged, this, &QDeclarativeGeoMap::onSGNodeChanged); connect(m_map.data(), &QGeoMap::cameraCapabilitiesChanged, this, &QDeclarativeGeoMap::onCameraCapabilitiesChanged); // This prefetches a buffer around the map @@ -2179,6 +2180,38 @@ bool QDeclarativeGeoMap::removeMapItemView_real(QDeclarativeGeoMapItemView *item return removeMapItemGroup_real(itemView); // at this point, all delegate instances have been removed. } +void QDeclarativeGeoMap::updateItemToWindowTransform() +{ + if (!m_initialized) + return; + + // Update itemToWindowTransform into QGeoProjection + const QTransform item2WindowOld = m_map->geoProjection().itemToWindowTransform(); + QTransform item2Window = QQuickItemPrivate::get(this)->itemToWindowTransform(); + if (!property("layer").isNull() && property("layer").value<QObject *>()->property("enabled").toBool()) + item2Window.reset(); // When layer is enabled, the item is rendered offscreen with no transformation, then the layer is applied + + m_map->setItemToWindowTransform(item2Window); + + // This method is called at every redraw, including those redraws not generated by + // sgNodeChanged. + // In these cases, *if* the item2windowTransform has changed (e.g., if transformation of + // the item or one of its ancestors changed), a forced update of the map items using accelerated + // GL implementation has to be performed in order to have them pulling the updated itemToWindowTransform. + if (!m_sgNodeHasChanged && item2WindowOld != item2Window) { + for (auto i: qAsConst(m_mapItems)) + i->setMaterialDirty(); + } + + m_sgNodeHasChanged = false; +} + +void QDeclarativeGeoMap::onSGNodeChanged() +{ + m_sgNodeHasChanged = true; + update(); +} + /*! \qmlmethod void QtLocation::Map::addMapItemView(MapItemView itemView) diff --git a/src/location/declarativemaps/qdeclarativegeomap_p.h b/src/location/declarativemaps/qdeclarativegeomap_p.h index ee9f8ec2..2a7ea6b1 100644 --- a/src/location/declarativemaps/qdeclarativegeomap_p.h +++ b/src/location/declarativemaps/qdeclarativegeomap_p.h @@ -63,6 +63,9 @@ #include <QtLocation/private/qgeomap_p.h> #include <QtQuick/private/qquickitemchangelistener_p.h> +Q_MOC_INCLUDE(<QtLocation/private/qdeclarativegeomaptype_p.h>) +Q_MOC_INCLUDE(<QtLocation/private/qdeclarativegeoserviceprovider_p.h>) + QT_BEGIN_NAMESPACE class QDeclarativeGeoServiceProvider; @@ -182,7 +185,7 @@ public: Q_INVOKABLE void clearMapParameters(); QList<QObject *> mapParameters(); - void addMapObject(QGeoMapObject *object); + void addMapObject(QGeoMapObject *object); // Not invokable as currently meant to be used through a main MapObjectView void removeMapObject(QGeoMapObject *object); void clearMapObjects(); QList<QGeoMapObject *> mapObjects(); @@ -267,6 +270,8 @@ protected: bool removeMapItemGroup_real(QDeclarativeGeoMapItemGroup *itemGroup); bool addMapItemView_real(QDeclarativeGeoMapItemView *itemView); bool removeMapItemView_real(QDeclarativeGeoMapItemView *itemView); + void updateItemToWindowTransform(); + void onSGNodeChanged(); private Q_SLOTS: void mappingManagerInitialized(); @@ -308,6 +313,7 @@ private: double m_maximumViewportLatitude; double m_minimumViewportLatitude = 0.0; bool m_initialized; + bool m_sgNodeHasChanged = false; QList<QDeclarativeGeoMapParameter *> m_mapParameters; QList<QGeoMapObject*> m_pendingMapObjects; // Used only in the initialization phase QGeoCameraCapabilities m_cameraCapabilities; diff --git a/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h b/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h index 0cf06d12..ca345743 100644 --- a/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h +++ b/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h @@ -55,6 +55,9 @@ #include <QPointer> #include <QtQuick/QQuickPaintedItem> +Q_MOC_INCLUDE(<QtLocation/private/qdeclarativegeomap_p.h>) + + QT_BEGIN_NAMESPACE class QTextDocument; diff --git a/src/location/declarativemaps/qdeclarativegeomapitembase.cpp b/src/location/declarativemaps/qdeclarativegeomapitembase.cpp index 23993faf..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 */ @@ -323,6 +350,8 @@ bool QDeclarativeGeoMapItemBase::isPolishScheduled() const return QQuickItemPrivate::get(this)->polishScheduled; } +void QDeclarativeGeoMapItemBase::setMaterialDirty() {} + void QDeclarativeGeoMapItemBase::polishAndUpdate() { polish(); diff --git a/src/location/declarativemaps/qdeclarativegeomapitembase_p.h b/src/location/declarativemaps/qdeclarativegeomapitembase_p.h index 38a118e5..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(); @@ -92,14 +94,18 @@ public: virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map); virtual void setPositionOnMap(const QGeoCoordinate &coordinate, const QPointF &offset); - QDeclarativeGeoMap *quickMap() { return quickMap_; } - QGeoMap *map() { return map_; } + QDeclarativeGeoMap *quickMap() const { return quickMap_; } + QGeoMap *map() const { return map_; } virtual const QGeoShape &geoShape() const = 0; virtual void setGeoShape(const QGeoShape &shape) = 0; 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 *); @@ -108,10 +114,28 @@ public: void setParentGroup(QDeclarativeGeoMapItemGroup &parentGroup); + template <typename T = QObject> + + QList<T*> quickChildren() const + { + QList<T*> res; + QObjectList kids = children(); + QList<QQuickItem *> quickKids = childItems(); + for (int i = 0; i < quickKids.count(); ++i) + kids.append(quickKids.at(i)); + for (auto kid : qAsConst(kids)) { + auto val = qobject_cast<T*>(kid); + if (val) + res.push_back(val); + } + return res; + } + Q_SIGNALS: void mapItemOpacityChanged(); Q_REVISION(12) void addTransitionFinished(); Q_REVISION(12) void removeTransitionFinished(); + void lodThresholdChanged(); protected Q_SLOTS: virtual void afterChildrenChanged(); @@ -122,6 +146,7 @@ protected: float zoomLevelOpacity() const; bool childMouseEventFilter(QQuickItem *item, QEvent *event); bool isPolishScheduled() const; + virtual void setMaterialDirty(); QGeoMap::ItemType m_itemType = QGeoMap::NoItem; @@ -140,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/qdeclarativegeomapitemutils.cpp b/src/location/declarativemaps/qdeclarativegeomapitemutils.cpp new file mode 100644 index 00000000..e2774559 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapitemutils.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Paolo Angelelli <paolo.angelelli@gmail.com> +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeomapitemutils_p.h" +#include <QtPositioning/private/qdoublevector3d_p.h> +#include <QtPositioning/private/qdoublematrix4x4_p.h> +#include <QtPositioning/QGeoCoordinate> +#include <QPointF> +#include <QMatrix4x4> +#include <QPainterPath> +#include <QPainterPathStroker> +#include <QtGui/private/qvectorpath_p.h> +#include <QtGui/private/qtriangulatingstroker_p.h> +#include <QtGui/private/qtriangulator_p.h> +#include <QtPositioning/private/qclipperutils_p.h> + +QT_BEGIN_NAMESPACE + +void QDeclarativeGeoMapItemUtils::wrapPath(const QList<QGeoCoordinate> &perimeter, + const QGeoCoordinate &geoLeftBound, + const QGeoProjectionWebMercator &p, + QList<QDoubleVector2D> &wrappedPath, + QList<QDoubleVector2D> &wrappedPathMinus1, + QList<QDoubleVector2D> &wrappedPathPlus1, + QDoubleVector2D *leftBoundWrapped) +{ + QList<QDoubleVector2D> path; + for (const QGeoCoordinate &c : perimeter) + path << p.geoToMapProjection(c); + const QDoubleVector2D leftBound = p.geoToMapProjection(geoLeftBound); + wrappedPath.clear(); + wrappedPathPlus1.clear(); + wrappedPathMinus1.clear(); + // compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 + for (int i = 0; i < path.size(); ++i) { + QDoubleVector2D coord = path.at(i); + + // We can get NaN if the map isn't set up correctly, or the projection + // is faulty -- probably best thing to do is abort + if (!qIsFinite(coord.x()) || !qIsFinite(coord.y())) + return; + + const bool isPointLessThanUnwrapBelowX = (coord.x() < leftBound.x()); + // unwrap x to preserve geometry if moved to border of map + if (isPointLessThanUnwrapBelowX) + coord.setX(coord.x() + 1.0); + + QDoubleVector2D coordP1(coord.x() + 1.0, coord.y()); + QDoubleVector2D coordM1(coord.x() - 1.0, coord.y()); + + wrappedPath.append(coord); + wrappedPathPlus1.append(coordP1); + wrappedPathMinus1.append(coordM1); + } + if (leftBoundWrapped) + *leftBoundWrapped = leftBound; +} + +void QDeclarativeGeoMapItemUtils::wrapPath(const QList<QGeoCoordinate> &perimeter, + const QGeoCoordinate &geoLeftBound, + const QGeoProjectionWebMercator &p, + QList<QDoubleVector2D> &wrappedPath, + QDoubleVector2D *leftBoundWrapped) +{ + QList<QDoubleVector2D> path; + for (const QGeoCoordinate &c : perimeter) + path << p.geoToMapProjection(c); + const QDoubleVector2D leftBound = p.geoToMapProjection(geoLeftBound); + wrapPath(path, leftBound,wrappedPath); + if (leftBoundWrapped) + *leftBoundWrapped = leftBound; +} + +void QDeclarativeGeoMapItemUtils::wrapPath(const QList<QDoubleVector2D> &path, + const QDoubleVector2D &geoLeftBound, + QList<QDoubleVector2D> &wrappedPath) +{ + wrappedPath.clear(); + // compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 + for (int i = 0; i < path.size(); ++i) { + QDoubleVector2D coord = path.at(i); + + // We can get NaN if the map isn't set up correctly, or the projection + // is faulty -- probably best thing to do is abort + if (!qIsFinite(coord.x()) || !qIsFinite(coord.y())) + return; + + const bool isPointLessThanUnwrapBelowX = (coord.x() < geoLeftBound.x()); + // unwrap x to preserve geometry if moved to border of map + if (isPointLessThanUnwrapBelowX) + coord.setX(coord.x() + 1.0); + + wrappedPath.append(coord); + } +} + +void QDeclarativeGeoMapItemUtils::clipPolygon(const QList<QDoubleVector2D> &wrappedPath, const QGeoProjectionWebMercator &p, QList<QList<QDoubleVector2D> > &clippedPaths, QDoubleVector2D *leftBoundWrapped, const bool closed) +{ + // 2) Clip bounding box + clippedPaths.clear(); + const QList<QDoubleVector2D> &visibleRegion = p.projectableGeometry(); + if (visibleRegion.size()) { + c2t::clip2tri clipper; + clipper.addSubjectPath(QClipperUtils::qListToPath(wrappedPath), closed); + clipper.addClipPolygon(QClipperUtils::qListToPath(visibleRegion)); + Paths res = clipper.execute(c2t::clip2tri::Intersection, QtClipperLib::pftEvenOdd, QtClipperLib::pftEvenOdd); + clippedPaths = QClipperUtils::pathsToQList(res); + + if (leftBoundWrapped) { + // 2.1) update srcOrigin_ and leftBoundWrapped with the point with minimum X + QDoubleVector2D lb(qInf(), qInf()); + for (const QList<QDoubleVector2D> &path: clippedPaths) + for (const QDoubleVector2D &p: path) + if (p.x() < lb.x() || (p.x() == lb.x() && p.y() < lb.y())) + // y-minimization needed to find the same point on polygon and border + lb = p; + + if (qIsInf(lb.x())) // e.g., when the polygon is clipped entirely + return; + + // 2.2) Prevent the conversion to and from clipper from introducing tiny negative offsets which, + // in turn will make the geometry wrap around. + lb.setX(qMax(leftBoundWrapped->x(), lb.x())); + + *leftBoundWrapped = lb; + // srcOrigin_ = p.mapProjectionToGeo(p.unwrapMapProjection(lb)); + } + } else { + clippedPaths.append(wrappedPath); + } +} + +void QDeclarativeGeoMapItemUtils::projectBbox(const QList<QDoubleVector2D> &clippedBbox, const QGeoProjectionWebMercator &p, QPainterPath &projectedBbox) +{ + projectedBbox = QPainterPath(); // clear() is added in 5.13.. + for (int i = 0; i < clippedBbox.size(); ++i) { + QDoubleVector2D point = p.wrappedMapProjectionToItemPosition(clippedBbox.at(i)); + if (i == 0) + projectedBbox.moveTo(point.toPointF()); + else + projectedBbox.lineTo(point.toPointF()); + } + projectedBbox.closeSubpath(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomapitemutils_p.h b/src/location/declarativemaps/qdeclarativegeomapitemutils_p.h new file mode 100644 index 00000000..48ece6b2 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapitemutils_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Paolo Angelelli <paolo.angelelli@gmail.com> +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAPITEMUTILS_P_H +#define QDECLARATIVEGEOMAPITEMUTILS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qgeoprojection_p.h> +#include <QtPositioning/private/qdoublevector2d_p.h> + + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapItemUtils +{ +public: + struct vec2 { + float x; + float y; + vec2(const QDoubleVector2D &p) + { + x = float(p.x()); + y = float(p.y()); + } + vec2() = default; + vec2 &operator=(const QPointF &p) + { + x = float(p.x()); + y = float(p.y()); + return *this; + } + vec2 &operator=(const QDoubleVector2D &p) + { + x = float(p.x()); + y = float(p.y()); + return *this; + } + QDoubleVector2D toDoubleVector2D() const + { + return QDoubleVector2D(double(x), double(y)); + } + }; + + static void wrapPath(const QList<QGeoCoordinate> &perimeter + ,const QGeoCoordinate &geoLeftBound + ,const QGeoProjectionWebMercator &p + ,QList<QDoubleVector2D> &wrappedPath + ,QList<QDoubleVector2D> &wrappedPathMinus1 + ,QList<QDoubleVector2D> &wrappedPathPlus1 + ,QDoubleVector2D *leftBoundWrapped = nullptr); + + static void wrapPath(const QList<QGeoCoordinate> &perimeter + ,const QGeoCoordinate &geoLeftBound + ,const QGeoProjectionWebMercator &p + ,QList<QDoubleVector2D> &wrappedPath + ,QDoubleVector2D *leftBoundWrapped = nullptr); + + static void wrapPath(const QList<QDoubleVector2D> &path + , const QDoubleVector2D &geoLeftBound + , QList<QDoubleVector2D> &wrappedPath); + + + static void clipPolygon(const QList<QDoubleVector2D> &wrappedPath + ,const QGeoProjectionWebMercator &p + ,QList<QList<QDoubleVector2D> > &clippedPaths + ,QDoubleVector2D *leftBoundWrapped = nullptr + ,const bool closed = true); + + static void projectBbox(const QList<QDoubleVector2D> &clippedBbox + ,const QGeoProjectionWebMercator &p + ,QPainterPath &projectedBbox); + +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEGEOMAPITEMUTILS_P_H diff --git a/src/location/declarativemaps/qdeclarativegeoroute.cpp b/src/location/declarativemaps/qdeclarativegeoroute.cpp index c1f5264b..0144ade9 100644 --- a/src/location/declarativemaps/qdeclarativegeoroute.cpp +++ b/src/location/declarativemaps/qdeclarativegeoroute.cpp @@ -192,7 +192,7 @@ QJSValue QDeclarativeGeoRoute::path() const pathArray->put(i, cv); } - return QJSValue(v4, pathArray.asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(pathArray.asReturnedValue()); } void QDeclarativeGeoRoute::setPath(const QJSValue &value) diff --git a/src/location/declarativemaps/qdeclarativegeoroute_p.h b/src/location/declarativemaps/qdeclarativegeoroute_p.h index f455d027..9d89760a 100644 --- a/src/location/declarativemaps/qdeclarativegeoroute_p.h +++ b/src/location/declarativemaps/qdeclarativegeoroute_p.h @@ -55,6 +55,8 @@ #include <QtQml/QQmlListProperty> #include <QtLocation/QGeoRoute> +Q_MOC_INCLUDE(<QtLocation/private/qdeclarativegeoroutemodel_p.h>) + QT_BEGIN_NAMESPACE class QDeclarativeGeoRouteQuery; diff --git a/src/location/declarativemaps/qdeclarativegeoroutemodel.cpp b/src/location/declarativemaps/qdeclarativegeoroutemodel.cpp index 910e7026..a3a7fe85 100644 --- a/src/location/declarativemaps/qdeclarativegeoroutemodel.cpp +++ b/src/location/declarativemaps/qdeclarativegeoroutemodel.cpp @@ -959,7 +959,7 @@ QJSValue QDeclarativeGeoRouteQuery::excludedAreas() const excludedAreasArray->put(i, cv); } - return QJSValue(v4, excludedAreasArray.asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(excludedAreasArray.asReturnedValue()); } void QDeclarativeGeoRouteQuery::setExcludedAreas(const QJSValue &value) diff --git a/src/location/declarativemaps/qdeclarativegeoroutesegment.cpp b/src/location/declarativemaps/qdeclarativegeoroutesegment.cpp index 6751a47b..713d434b 100644 --- a/src/location/declarativemaps/qdeclarativegeoroutesegment.cpp +++ b/src/location/declarativemaps/qdeclarativegeoroutesegment.cpp @@ -158,7 +158,7 @@ QJSValue QDeclarativeGeoRouteSegment::path() const pathArray->put(i, cv); } - return QJSValue(v4, pathArray.asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(pathArray.asReturnedValue()); } QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp b/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp index 86ff04af..fa6ee174 100644 --- a/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp +++ b/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp @@ -34,7 +34,11 @@ ** ****************************************************************************/ +#include "qdeclarativegeomapitemutils_p.h" #include "qdeclarativepolygonmapitem_p.h" +#include "qdeclarativepolylinemapitem_p_p.h" +#include "qdeclarativepolygonmapitem_p_p.h" +#include "qdeclarativerectanglemapitem_p_p.h" #include "qlocationutils_p.h" #include "error_messages_p.h" #include "locationvaluetypehelper_p.h" @@ -51,6 +55,10 @@ #include <QtPositioning/private/qdoublevector2d_p.h> #include <QtPositioning/private/qclipperutils_p.h> #include <QtPositioning/private/qgeopolygon_p.h> +#include <QtPositioning/private/qwebmercator_p.h> +#include <QtQuick/private/qsgmaterialshader_p.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/qsgnode.h> /* poly2tri triangulator includes */ #include <clip2tri.h> @@ -326,17 +334,307 @@ void QGeoMapPolygonGeometry::updateScreenPoints(const QGeoMap &map, qreal stroke this->translate(QPointF(strokeWidth, strokeWidth)); } +QGeoMapPolygonGeometryOpenGL::QGeoMapPolygonGeometryOpenGL(){ +} + +void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QList<QDoubleVector2D> &path) +{ + QList<QGeoCoordinate> geopath; + for (const auto &c: path) + geopath.append(QWebMercator::mercatorToCoord(c)); + updateSourcePoints(map, geopath); +} + +// wrapPath always preserves the geometry +// This one handles holes +static void wrapPath(const QGeoPolygon &poly + ,const QGeoCoordinate &geoLeftBound + ,const QGeoProjectionWebMercator &p + ,QList<QList<QDoubleVector2D> > &wrappedPaths + ,QDoubleVector2D *leftBoundWrapped = nullptr) +{ + QList<QList<QDoubleVector2D> > paths; + for (int i = 0; i < 1+poly.holesCount(); ++i) { + QList<QDoubleVector2D> path; + if (!i) { + for (const QGeoCoordinate &c : poly.path()) + path << p.geoToMapProjection(c); + } else { + for (const QGeoCoordinate &c : poly.holePath(i-1)) + path << p.geoToMapProjection(c); + } + paths.append(path); + } + + const QDoubleVector2D leftBound = p.geoToMapProjection(geoLeftBound); + wrappedPaths.clear(); + + QList<QDoubleVector2D> wrappedPath; + // compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 + for (int j = 0; j < paths.size(); ++j) { + const QList<QDoubleVector2D> &path = paths.at(j); + wrappedPath.clear(); + for (int i = 0; i < path.size(); ++i) { + QDoubleVector2D coord = path.at(i); + + // We can get NaN if the map isn't set up correctly, or the projection + // is faulty -- probably best thing to do is abort + if (!qIsFinite(coord.x()) || !qIsFinite(coord.y())) { + wrappedPaths.clear(); + return; + } + + const bool isPointLessThanUnwrapBelowX = (coord.x() < leftBound.x()); + // unwrap x to preserve geometry if moved to border of map + if (isPointLessThanUnwrapBelowX) + coord.setX(coord.x() + 1.0); + wrappedPath.append(coord); + } + wrappedPaths.append(wrappedPath); + } + + if (leftBoundWrapped) + *leftBoundWrapped = leftBound; +} + +static void cutPathEars(const QList<QList<QDoubleVector2D>> &wrappedPaths, + QVector<QDeclarativeGeoMapItemUtils::vec2> &screenVertices, + QVector<quint32> &screenIndices) +{ + using Coord = double; + using N = uint32_t; + using Point = std::array<Coord, 2>; + screenVertices.clear(); + screenIndices.clear(); + + std::vector<std::vector<Point>> polygon; + std::vector<Point> poly; + + for (const QList<QDoubleVector2D> &wrappedPath: wrappedPaths) { + poly.clear(); + for (const QDoubleVector2D &v: wrappedPath) { + screenVertices << v; + Point pt = {{ v.x(), v.y() }}; + poly.push_back( pt ); + } + polygon.push_back(poly); + } + + std::vector<N> indices = qt_mapbox::earcut<N>(polygon); + + for (const auto &i: indices) + screenIndices << quint32(i); +} + +static void cutPathEars(const QList<QDoubleVector2D> &wrappedPath, + QVector<QDeclarativeGeoMapItemUtils::vec2> &screenVertices, + QVector<quint32> &screenIndices) +{ + using Coord = double; + using N = uint32_t; + using Point = std::array<Coord, 2>; + screenVertices.clear(); + screenIndices.clear(); + + std::vector<std::vector<Point>> polygon; + std::vector<Point> poly; + + for (const QDoubleVector2D &v: wrappedPath) { + screenVertices << v; + Point pt = {{ v.x(), v.y() }}; + poly.push_back( pt ); + } + polygon.push_back(poly); + + std::vector<N> indices = qt_mapbox::earcut<N>(polygon); + + for (const auto &i: indices) + screenIndices << quint32(i); +} + +/*! + \internal +*/ +// This one does only a perimeter +void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map, + const QList<QGeoCoordinate> &perimeter) +{ + if (!sourceDirty_) + return; + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection()); + + // build the actual path + // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints + srcOrigin_ = geoLeftBound_; + + QDoubleVector2D leftBoundWrapped; + // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 + QList<QDoubleVector2D> wrappedPath; + QDeclarativeGeoMapItemUtils::wrapPath(perimeter, geoLeftBound_, p, + wrappedPath, &leftBoundWrapped); + + // 1.1) do the same for the bbox + QList<QDoubleVector2D> wrappedBbox, wrappedBboxPlus1, wrappedBboxMinus1; + QGeoPolygon bbox(QGeoPath(perimeter).boundingGeoRectangle()); + QDeclarativeGeoMapItemUtils::wrapPath(bbox.path(), bbox.boundingGeoRectangle().topLeft(), p, + wrappedBbox, wrappedBboxMinus1, wrappedBboxPlus1, &m_bboxLeftBoundWrapped); + + // 2) Store the triangulated polygon, and the wrapped bbox paths. + // the triangulations can be used as they are, as they "bypass" the QtQuick display chain + // the bbox wraps have to be however clipped, and then projected, in order to figure out the geometry. + // Note that this might still cause the geometryChanged method to fail under some extreme conditions. + cutPathEars(wrappedPath, m_screenVertices, m_screenIndices); + + m_wrappedPolygons.resize(3); + m_wrappedPolygons[0].wrappedBboxes = wrappedBboxMinus1; + m_wrappedPolygons[1].wrappedBboxes = wrappedBbox; + m_wrappedPolygons[2].wrappedBboxes = wrappedBboxPlus1; +} + +// This one handles whole QGeoPolygon w. holes +void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoPolygon &poly) +{ + if (!sourceDirty_) + return; + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection()); + + // build the actual path + // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints + srcOrigin_ = geoLeftBound_; + + QDoubleVector2D leftBoundWrapped; + QList<QList<QDoubleVector2D>> wrappedPath; + // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 + wrapPath(poly, geoLeftBound_, p, + wrappedPath, &leftBoundWrapped); + + // 1.1) do the same for the bbox + QList<QDoubleVector2D> wrappedBbox, wrappedBboxPlus1, wrappedBboxMinus1; + QGeoPolygon bbox(poly.boundingGeoRectangle()); + QDeclarativeGeoMapItemUtils::wrapPath(bbox.path(), bbox.boundingGeoRectangle().topLeft(), p, + wrappedBbox, wrappedBboxMinus1, wrappedBboxPlus1, &m_bboxLeftBoundWrapped); + + // 2) Store the triangulated polygon, and the wrapped bbox paths. + // the triangulations can be used as they are, as they "bypass" the QtQuick display chain + // the bbox wraps have to be however clipped, and then projected, in order to figure out the geometry. + // Note that this might still cause the geometryChanged method to fail under some extreme conditions. + cutPathEars(wrappedPath, m_screenVertices, m_screenIndices); + m_wrappedPolygons.resize(3); + m_wrappedPolygons[0].wrappedBboxes = wrappedBboxMinus1; + m_wrappedPolygons[1].wrappedBboxes = wrappedBbox; + m_wrappedPolygons[2].wrappedBboxes = wrappedBboxPlus1; +} + +void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoRectangle &rect) +{ + if (!sourceDirty_) + return; + const QList<QGeoCoordinate> perimeter = QDeclarativeRectangleMapItemPrivateCPU::path(rect); + updateSourcePoints(map, perimeter); +} + +/*! + \internal +*/ +void QGeoMapPolygonGeometryOpenGL::updateScreenPoints(const QGeoMap &map, qreal strokeWidth , const QColor &strokeColor) +{ + if (map.viewportWidth() == 0 || map.viewportHeight() == 0) { + clear(); + return; + } + + // 1) identify which set to use: std, +1 or -1 + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection()); + const QDoubleVector2D leftBoundMercator = p.geoToMapProjection(srcOrigin_); + m_wrapOffset = p.projectionWrapFactor(leftBoundMercator) + 1; // +1 to get the offset into QLists + + // 1.1) select geometry set + // This could theoretically be skipped for those polygons whose bbox is not even projectable. + // However, such optimization could only be introduced if not calculating bboxes lazily. + // Hence not doing it. + if (sourceDirty_) { + m_dataChanged = true; + } + + if (strokeWidth == 0.0 || strokeColor.alpha() == 0) // or else the geometry of the border is used, so no point in calculating 2 of them + updateQuickGeometry(p, strokeWidth); +} + +void QGeoMapPolygonGeometryOpenGL::updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal /*strokeWidth*/) +{ + // 2) clip bbox + // BBox handling -- this is related to the bounding box geometry + // that has to inevitably follow the old projection codepath + // As it needs to provide projected coordinates for QtQuick interaction. + // This could be futher optimized to be updated in a lazy fashion. + const QList<QDoubleVector2D> &wrappedBbox = m_wrappedPolygons.at(m_wrapOffset).wrappedBboxes; + QList<QList<QDoubleVector2D> > clippedBbox; + QDoubleVector2D bboxLeftBoundWrapped = m_bboxLeftBoundWrapped; + bboxLeftBoundWrapped.setX(bboxLeftBoundWrapped.x() + double(m_wrapOffset - 1)); + QDeclarativeGeoMapItemUtils::clipPolygon(wrappedBbox, p, clippedBbox, &bboxLeftBoundWrapped); + + // 3) project bbox + QPainterPath ppi; + if (!clippedBbox.size() || clippedBbox.first().size() < 3) { + sourceBounds_ = screenBounds_ = QRectF(); + firstPointOffset_ = QPointF(); + screenOutline_ = ppi; + return; + } + + QDeclarativeGeoMapItemUtils::projectBbox(clippedBbox.first(), p, ppi); // Using first because a clipped box should always result in one polygon + const QRectF brect = ppi.boundingRect(); + firstPointOffset_ = QPointF(brect.topLeft()); + screenOutline_ = ppi; + + // 4) Set Screen bbox + screenBounds_ = brect; + sourceBounds_.setX(0); + sourceBounds_.setY(0); + sourceBounds_.setWidth(brect.width()); + sourceBounds_.setHeight(brect.height()); +} + +/* + * QDeclarativePolygonMapItem Private Implementations + */ + +QDeclarativePolygonMapItemPrivate::~QDeclarativePolygonMapItemPrivate() {} + +QDeclarativePolygonMapItemPrivateCPU::~QDeclarativePolygonMapItemPrivateCPU() {} + +QDeclarativePolygonMapItemPrivateOpenGL::~QDeclarativePolygonMapItemPrivateOpenGL() {} + +/* + * QDeclarativePolygonMapItem Implementation + */ + +struct PolygonBackendSelector +{ + PolygonBackendSelector() + { + backend = (qgetenv("QTLOCATION_OPENGL_ITEMS").toInt()) ? QDeclarativePolygonMapItem::OpenGL : QDeclarativePolygonMapItem::Software; + } + QDeclarativePolygonMapItem::Backend backend = QDeclarativePolygonMapItem::Software; +}; + +Q_GLOBAL_STATIC(PolygonBackendSelector, mapPolygonBackendSelector) + QDeclarativePolygonMapItem::QDeclarativePolygonMapItem(QQuickItem *parent) -: QDeclarativeGeoMapItemBase(parent), border_(this), color_(Qt::transparent), dirtyMaterial_(true), - updatingGeometry_(false) +: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent), m_dirtyMaterial(true), + m_updatingGeometry(false) + , m_d(new QDeclarativePolygonMapItemPrivateCPU(*this)) + { + // ToDo: handle envvar, and switch implementation. m_itemType = QGeoMap::MapPolygon; - geopath_ = QGeoPolygonEager(); + m_geopoly = QGeoPolygonEager(); setFlag(ItemHasContents, true); - QObject::connect(&border_, SIGNAL(colorChanged(QColor)), - this, SLOT(markSourceDirtyAndUpdate())); - QObject::connect(&border_, SIGNAL(widthChanged(qreal)), - this, SLOT(markSourceDirtyAndUpdate())); + QObject::connect(&m_border, SIGNAL(colorChanged(QColor)), + this, SLOT(onLinePropertiesChanged())); // ToDo: fix this, only flag material? + QObject::connect(&m_border, SIGNAL(widthChanged(qreal)), + this, SLOT(onLinePropertiesChanged())); + setBackend(mapPolygonBackendSelector->backend); } QDeclarativePolygonMapItem::~QDeclarativePolygonMapItem() @@ -359,7 +657,44 @@ QDeclarativePolygonMapItem::~QDeclarativePolygonMapItem() QDeclarativeMapLineProperties *QDeclarativePolygonMapItem::border() { - return &border_; + return &m_border; +} + +/*! + \qmlproperty MapPolygon.Backend QtLocation::MapPolygon::backend + + This property holds which backend is in use to render the map item. + Valid values are \b MapPolygon.Software and \b{MapPolygon.OpenGL}. + The default value is \b{MapPolygon.Software}. + + \note \b{The release of this API with Qt 5.15 is a Technology Preview}. + Ideally, as the OpenGL backends for map items mature, there will be + no more need to also offer the legacy software-projection backend. + So this property will likely disappear at some later point. + To select OpenGL-accelerated item backends without using this property, + it is also possible to set the environment variable \b QTLOCATION_OPENGL_ITEMS + to \b{1}. + Also note that all current OpenGL backends won't work as expected when enabling + layers on the individual item, or when running on OpenGL core profiles greater than 2.x. + + \since 5.15 +*/ +QDeclarativePolygonMapItem::Backend QDeclarativePolygonMapItem::backend() const +{ + return m_backend; +} + +void QDeclarativePolygonMapItem::setBackend(QDeclarativePolygonMapItem::Backend b) +{ + if (b == m_backend) + return; + m_backend = b; + QScopedPointer<QDeclarativePolygonMapItemPrivate> d((m_backend == Software) + ? static_cast<QDeclarativePolygonMapItemPrivate *>(new QDeclarativePolygonMapItemPrivateCPU(*this)) + : static_cast<QDeclarativePolygonMapItemPrivate * >(new QDeclarativePolygonMapItemPrivateOpenGL(*this))); + m_d.swap(d); + m_d->onGeoGeometryChanged(); + emit backendChanged(); } /*! @@ -368,12 +703,8 @@ QDeclarativeMapLineProperties *QDeclarativePolygonMapItem::border() void QDeclarativePolygonMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) { QDeclarativeGeoMapItemBase::setMap(quickMap,map); - if (map) { - regenerateCache(); - geometry_.markSourceDirty(); - borderGeometry_.markSourceDirty(); - polishAndUpdate(); - } + if (map) + m_d->onMapSet(); } /*! @@ -387,7 +718,7 @@ void QDeclarativePolygonMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *m */ QJSValue QDeclarativePolygonMapItem::path() const { - return fromList(this, geopath_.path()); + return fromList(this, m_geopoly.path()); } void QDeclarativePolygonMapItem::setPath(const QJSValue &value) @@ -398,15 +729,12 @@ void QDeclarativePolygonMapItem::setPath(const QJSValue &value) QList<QGeoCoordinate> pathList = toList(this, value); // Equivalent to QDeclarativePolylineMapItem::setPathFromGeoList - if (geopath_.path() == pathList) + if (m_geopoly.path() == pathList) return; - geopath_.setPath(pathList); + m_geopoly.setPath(pathList); - regenerateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_d->onGeoGeometryChanged(); emit pathChanged(); } @@ -423,11 +751,8 @@ void QDeclarativePolygonMapItem::addCoordinate(const QGeoCoordinate &coordinate) if (!coordinate.isValid()) return; - geopath_.addCoordinate(coordinate); - updateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_geopoly.addCoordinate(coordinate); + m_d->onGeoGeometryUpdated(); emit pathChanged(); } @@ -443,15 +768,12 @@ void QDeclarativePolygonMapItem::addCoordinate(const QGeoCoordinate &coordinate) */ void QDeclarativePolygonMapItem::removeCoordinate(const QGeoCoordinate &coordinate) { - int length = geopath_.path().length(); - geopath_.removeCoordinate(coordinate); - if (geopath_.path().length() == length) + int length = m_geopoly.path().length(); + m_geopoly.removeCoordinate(coordinate); + if (m_geopoly.path().length() == length) return; - regenerateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_d->onGeoGeometryChanged(); emit pathChanged(); } @@ -465,18 +787,18 @@ void QDeclarativePolygonMapItem::removeCoordinate(const QGeoCoordinate &coordina QColor QDeclarativePolygonMapItem::color() const { - return color_; + return m_color; } void QDeclarativePolygonMapItem::setColor(const QColor &color) { - if (color_ == color) + if (m_color == color) return; - color_ = color; - dirtyMaterial_ = true; - update(); - emit colorChanged(color_); + m_color = color; + m_dirtyMaterial = true; + polishAndUpdate(); // in case color was transparent and now is not or vice versa + emit colorChanged(m_color); } /*! @@ -484,22 +806,7 @@ void QDeclarativePolygonMapItem::setColor(const QColor &color) */ QSGNode *QDeclarativePolygonMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) { - Q_UNUSED(data); - MapPolygonNode *node = static_cast<MapPolygonNode *>(oldNode); - - if (!node) - node = new MapPolygonNode(); - - //TODO: update only material - if (geometry_.isScreenDirty() || borderGeometry_.isScreenDirty() || dirtyMaterial_) { - node->update(color_, border_.color(), &geometry_, &borderGeometry_); - geometry_.setPreserveGeometry(false); - borderGeometry_.setPreserveGeometry(false); - geometry_.markClean(); - borderGeometry_.markClean(); - dirtyMaterial_ = false; - } - return node; + return m_d->updateMapItemPaintNode(oldNode, data); } /*! @@ -509,102 +816,34 @@ void QDeclarativePolygonMapItem::updatePolish() { if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) return; - if (geopath_.path().length() == 0) { // Possibly cleared - geometry_.clear(); - borderGeometry_.clear(); - setWidth(0); - setHeight(0); - return; - } - - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection()); - QScopedValueRollback<bool> rollback(updatingGeometry_); - updatingGeometry_ = true; - - geometry_.updateSourcePoints(*map(), geopathProjected_); - geometry_.updateScreenPoints(*map(), border_.width()); - - QList<QGeoMapItemGeometry *> geoms; - geoms << &geometry_; - borderGeometry_.clear(); - - if (border_.color() != Qt::transparent && border_.width() > 0) { - QList<QDoubleVector2D> closedPath = geopathProjected_; - closedPath << closedPath.first(); - - borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - - const QGeoCoordinate &geometryOrigin = geometry_.origin(); - - borderGeometry_.srcPoints_.clear(); - borderGeometry_.srcPointTypes_.clear(); - - QDoubleVector2D borderLeftBoundWrapped; - QList<QList<QDoubleVector2D > > clippedPaths = borderGeometry_.clipPath(*map(), closedPath, borderLeftBoundWrapped); - if (clippedPaths.size()) { - borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); - borderGeometry_.pathToScreen(*map(), clippedPaths, borderLeftBoundWrapped); - borderGeometry_.updateScreenPoints(*map(), border_.width()); - - geoms << &borderGeometry_; - } else { - borderGeometry_.clear(); - } - } - - QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); - setWidth(combined.width() + 2 * border_.width()); - setHeight(combined.height() + 2 * border_.width()); - - setPositionOnMap(geometry_.origin(), -1 * geometry_.sourceBoundingBox().topLeft() - + QPointF(border_.width(), border_.width())); + m_d->updatePolish(); } -void QDeclarativePolygonMapItem::markSourceDirtyAndUpdate() +void QDeclarativePolygonMapItem::setMaterialDirty() { - geometry_.markSourceDirty(); - borderGeometry_.markSourceDirty(); - polishAndUpdate(); + m_dirtyMaterial = true; + update(); } -/*! - \internal -*/ -void QDeclarativePolygonMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) +void QDeclarativePolygonMapItem::markSourceDirtyAndUpdate() { - if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) - return; - - geometry_.setPreserveGeometry(true, geometry_.geoLeftBound()); - borderGeometry_.setPreserveGeometry(true, borderGeometry_.geoLeftBound()); - geometry_.markSourceDirty(); - borderGeometry_.markSourceDirty(); - polishAndUpdate(); + m_d->markSourceDirtyAndUpdate(); } -/*! - \internal -*/ -void QDeclarativePolygonMapItem::regenerateCache() +void QDeclarativePolygonMapItem::onLinePropertiesChanged() { - if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) - return; - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection()); - geopathProjected_.clear(); - geopathProjected_.reserve(geopath_.path().size()); - for (const QGeoCoordinate &c : geopath_.path()) - geopathProjected_ << p.geoToMapProjection(c); + m_d->onLinePropertiesChanged(); } /*! \internal */ -void QDeclarativePolygonMapItem::updateCache() +void QDeclarativePolygonMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) { - if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + if (event.mapSize.isEmpty()) return; - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection()); - geopathProjected_ << p.geoToMapProjection(geopath_.path().last()); + + m_d->afterViewportChanged(); } /*! @@ -612,24 +851,21 @@ void QDeclarativePolygonMapItem::updateCache() */ bool QDeclarativePolygonMapItem::contains(const QPointF &point) const { - return (geometry_.contains(point) || borderGeometry_.contains(point)); + return m_d->contains(point); } const QGeoShape &QDeclarativePolygonMapItem::geoShape() const { - return geopath_; + return m_geopoly; } void QDeclarativePolygonMapItem::setGeoShape(const QGeoShape &shape) { - if (shape == geopath_) + if (shape == m_geopoly) return; - geopath_ = QGeoPolygonEager(shape); - regenerateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_geopoly = QGeoPolygonEager(shape); + m_d->onGeoGeometryChanged(); emit pathChanged(); } @@ -638,7 +874,7 @@ void QDeclarativePolygonMapItem::setGeoShape(const QGeoShape &shape) */ void QDeclarativePolygonMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { - if (!map() || !geopath_.isValid() || updatingGeometry_ || newGeometry.topLeft() == oldGeometry.topLeft()) { + if (newGeometry.topLeft() == oldGeometry.topLeft() || !map() || !m_geopoly.isValid() || m_updatingGeometry) { QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); return; } @@ -652,11 +888,8 @@ void QDeclarativePolygonMapItem::geometryChanged(const QRectF &newGeometry, cons if (offsetLati == 0.0 && offsetLongi == 0.0) return; - geopath_.translate(offsetLati, offsetLongi); - regenerateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_geopoly.translate(offsetLati, offsetLongi); + m_d->onGeoGeometryChanged(); emit pathChanged(); // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested @@ -665,6 +898,25 @@ void QDeclarativePolygonMapItem::geometryChanged(const QRectF &newGeometry, cons ////////////////////////////////////////////////////////////////////// +QSGMaterialShader *MapPolygonMaterial::createShader() const +{ + return new MapPolygonShader(); +} + +int MapPolygonMaterial::compare(const QSGMaterial *other) const +{ + const MapPolygonMaterial &o = *static_cast<const MapPolygonMaterial *>(other); + if (o.m_center == m_center && o.m_geoProjection == m_geoProjection && o.m_wrapOffset == m_wrapOffset) + return QSGFlatColorMaterial::compare(other); + return -1; +} + +QSGMaterialType *MapPolygonMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + MapPolygonNode::MapPolygonNode() : border_(new MapPolylineNode()), geometry_(QSGGeometry::defaultAttributes_Point2D(), 0) @@ -701,6 +953,9 @@ void MapPolygonNode::update(const QColor &fillColor, const QColor &borderColor, setSubtreeBlocked(false); + // TODO: do this only if the geometry has changed!! + // No need to do this every frame. + // Then benchmark the difference! QSGGeometry *fill = QSGGeometryNode::geometry(); fillShape->allocateAndFill(fill); markDirty(DirtyGeometry); @@ -712,4 +967,90 @@ void MapPolygonNode::update(const QColor &fillColor, const QColor &borderColor, } } +MapPolygonNodeGL::MapPolygonNodeGL() : + //fill_material_(this), + fill_material_(), + geometry_(QSGGeometry::defaultAttributes_Point2D(), 0) +{ + geometry_.setDrawingMode(QSGGeometry::DrawTriangles); + QSGGeometryNode::setMaterial(&fill_material_); + QSGGeometryNode::setGeometry(&geometry_); +} + +MapPolygonNodeGL::~MapPolygonNodeGL() +{ +} + +/*! + \internal +*/ +void MapPolygonNodeGL::update(const QColor &fillColor, + const QGeoMapPolygonGeometryOpenGL *fillShape, + const QMatrix4x4 &geoProjection, + const QDoubleVector3D ¢er) +{ + if (fillShape->m_screenIndices.size() < 3 || fillColor.alpha() == 0) { + setSubtreeBlocked(true); + return; + } + setSubtreeBlocked(false); + + QSGGeometry *fill = QSGGeometryNode::geometry(); + if (fillShape->m_dataChanged || !fill->vertexCount()) { + fillShape->allocateAndFillPolygon(fill); + markDirty(DirtyGeometry); + fillShape->m_dataChanged = false; + } + + //if (fillColor != fill_material_.color()) // Any point in optimizing this? + { + fill_material_.setColor(fillColor); + fill_material_.setGeoProjection(geoProjection); + fill_material_.setCenter(center); + fill_material_.setWrapOffset(fillShape->m_wrapOffset - 1); + setMaterial(&fill_material_); + markDirty(DirtyMaterial); + } +} + +MapPolygonShader::MapPolygonShader() : QSGMaterialShader(*new QSGMaterialShaderPrivate) +{ + +} + +void MapPolygonShader::updateState(const QSGMaterialShader::RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); + MapPolygonMaterial *oldMaterial = static_cast<MapPolygonMaterial *>(oldEffect); + MapPolygonMaterial *newMaterial = static_cast<MapPolygonMaterial *>(newEffect); + + const QColor &c = newMaterial->color(); + const QMatrix4x4 &geoProjection = newMaterial->geoProjection(); + const QDoubleVector3D ¢er = newMaterial->center(); + + QVector3D vecCenter, vecCenter_lowpart; + for (int i = 0; i < 3; i++) + QLocationUtils::split_double(center.get(i), &vecCenter[i], &vecCenter_lowpart[i]); + + if (oldMaterial == nullptr || c != oldMaterial->color() || state.isOpacityDirty()) { + float opacity = state.opacity() * c.alphaF(); + QVector4D v(c.redF() * opacity, + c.greenF() * opacity, + c.blueF() * opacity, + opacity); + program()->setUniformValue(m_color_id, v); + } + + if (state.isMatrixDirty()) + { + program()->setUniformValue(m_matrix_id, state.projectionMatrix()); + } + + program()->setUniformValue(m_mapProjection_id, geoProjection); + + program()->setUniformValue(m_center_id, vecCenter); + program()->setUniformValue(m_center_lowpart_id, vecCenter_lowpart); + program()->setUniformValue(m_wrapOffset_id, float(newMaterial->wrapOffset())); +} + QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativepolygonmapitem_p.h b/src/location/declarativemaps/qdeclarativepolygonmapitem_p.h index 810a8d35..efc1a137 100644 --- a/src/location/declarativemaps/qdeclarativepolygonmapitem_p.h +++ b/src/location/declarativemaps/qdeclarativepolygonmapitem_p.h @@ -51,44 +51,29 @@ #include <QtLocation/private/qlocationglobal_p.h> #include <QtLocation/private/qdeclarativegeomapitembase_p.h> #include <QtLocation/private/qdeclarativepolylinemapitem_p.h> -#include <QtLocation/private/qgeomapitemgeometry_p.h> #include <QtPositioning/qgeopolygon.h> -#include <QSGGeometryNode> -#include <QSGFlatColorMaterial> - QT_BEGIN_NAMESPACE -class MapPolygonNode; - -class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometry : public QGeoMapItemGeometry -{ -public: - QGeoMapPolygonGeometry(); - - inline void setAssumeSimple(bool value) { assumeSimple_ = value; } - - void updateSourcePoints(const QGeoMap &map, - const QList<QDoubleVector2D> &path); - - void updateScreenPoints(const QGeoMap &map, qreal strokeWidth = 0.0); - -protected: - QPainterPath srcPath_; - bool assumeSimple_; -}; - +class QDeclarativePolygonMapItemPrivate; class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItem : public QDeclarativeGeoMapItemBase { Q_OBJECT + Q_ENUMS(Backend) Q_PROPERTY(QJSValue path READ path WRITE setPath NOTIFY pathChanged) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(QDeclarativeMapLineProperties *border READ border CONSTANT) + Q_PROPERTY(Backend backend READ backend WRITE setBackend NOTIFY backendChanged REVISION 15) public: - explicit QDeclarativePolygonMapItem(QQuickItem *parent = 0); - ~QDeclarativePolygonMapItem(); + enum Backend { + Software = 0, + OpenGL = 1 + }; + + explicit QDeclarativePolygonMapItem(QQuickItem *parent = nullptr); + ~QDeclarativePolygonMapItem() override; virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) override; //from QuickItem @@ -105,6 +90,9 @@ public: QDeclarativeMapLineProperties *border(); + Backend backend() const; + void setBackend(Backend b); + bool contains(const QPointF &point) const override; const QGeoShape &geoShape() const override; void setGeoShape(const QGeoShape &shape) override; @@ -112,45 +100,34 @@ public: Q_SIGNALS: void pathChanged(); void colorChanged(const QColor &color); - -protected: - void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; - void updatePolish() override; + void backendChanged(); protected Q_SLOTS: void markSourceDirtyAndUpdate(); + void onLinePropertiesChanged(); virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) override; -private: - void regenerateCache(); - void updateCache(); - - QGeoPolygon geopath_; - QList<QDoubleVector2D> geopathProjected_; - QDeclarativeMapLineProperties border_; - QColor color_; - bool dirtyMaterial_; - QGeoMapPolygonGeometry geometry_; - QGeoMapPolylineGeometry borderGeometry_; - bool updatingGeometry_; -}; - -////////////////////////////////////////////////////////////////////// - -class Q_LOCATION_PRIVATE_EXPORT MapPolygonNode : public MapItemGeometryNode -{ +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void updatePolish() override; + void setMaterialDirty() override; +#ifdef QT_LOCATION_DEBUG public: - MapPolygonNode(); - ~MapPolygonNode() override; - - void update(const QColor &fillColor, const QColor &borderColor, - const QGeoMapItemGeometry *fillShape, - const QGeoMapItemGeometry *borderShape); -private: - QSGFlatColorMaterial fill_material_; - MapPolylineNode *border_; - QSGGeometry geometry_; +#endif + QGeoPolygon m_geopoly; + QDeclarativeMapLineProperties m_border; + QColor m_color; + Backend m_backend = Software; + bool m_dirtyMaterial; +// bool m_dirtyGeometry = false; + bool m_updatingGeometry; + + QScopedPointer<QDeclarativePolygonMapItemPrivate> m_d; + + friend class QDeclarativePolygonMapItemPrivate; + friend class QDeclarativePolygonMapItemPrivateCPU; + friend class QDeclarativePolygonMapItemPrivateOpenGL; }; QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h b/src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h new file mode 100644 index 00000000..8d566e69 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h @@ -0,0 +1,668 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Paolo Angelelli <paolo.angelelli@gmail.com> +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPOLYGONMAPITEM_P_P_H +#define QDECLARATIVEPOLYGONMAPITEM_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qgeomapitemgeometry_p.h> +#include <QtLocation/private/qdeclarativegeomapitembase_p.h> +#include <QtLocation/private/qdeclarativepolylinemapitem_p.h> +#include <QtLocation/private/qdeclarativegeomapitemutils_p.h> +#include <QtLocation/private/qdeclarativepolygonmapitem_p.h> +#include <QtLocation/private/qdeclarativepolylinemapitem_p_p.h> +#include <QSGGeometryNode> +#include <QSGFlatColorMaterial> +#include <QtPositioning/QGeoPath> +#include <QtPositioning/QGeoRectangle> +#include <QtPositioning/QGeoPolygon> +#include <QtPositioning/private/qdoublevector2d_p.h> +#include <QSGFlatColorMaterial> +#include <QSGSimpleMaterial> +#include <QtGui/QMatrix4x4> +#include <QColor> +#include <QList> +#include <QVector> +#include <QtCore/QScopedValueRollback> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometry : public QGeoMapItemGeometry +{ +public: + QGeoMapPolygonGeometry(); + + inline void setAssumeSimple(bool value) { assumeSimple_ = value; } + + void updateSourcePoints(const QGeoMap &map, + const QList<QDoubleVector2D> &path); + + void updateScreenPoints(const QGeoMap &map, qreal strokeWidth = 0.0); + +protected: + QPainterPath srcPath_; + bool assumeSimple_; +}; + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometryOpenGL : public QGeoMapItemGeometry +{ +public: + typedef struct { + QList<QDoubleVector2D> wrappedBboxes; + } WrappedPolygon; + QGeoMapPolygonGeometryOpenGL(); + ~QGeoMapPolygonGeometryOpenGL() override {} + + // Temporary method for compatibility in MapCircleObject. Remove when MapObjects are ported. + void updateSourcePoints(const QGeoMap &map, + const QList<QDoubleVector2D> &path); + + void updateSourcePoints(const QGeoMap &map, + const QList<QGeoCoordinate> &perimeter); + + void updateSourcePoints(const QGeoMap &map, + const QGeoPolygon &poly); + + void updateSourcePoints(const QGeoMap &map, + const QGeoRectangle &rect); + + void updateScreenPoints(const QGeoMap &map, qreal strokeWidth = 0.0, const QColor &strokeColor = Qt::transparent); + void updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth = 0.0); + + void allocateAndFillPolygon(QSGGeometry *geom) const + { + + + const QVector<QDeclarativeGeoMapItemUtils::vec2> &vx = m_screenVertices; + const QVector<quint32> &ix = m_screenIndices; + + geom->allocate(vx.size(), ix.size()); + if (geom->indexType() == QSGGeometry::UnsignedShortType) { + quint16 *its = geom->indexDataAsUShort(); + for (int i = 0; i < ix.size(); ++i) + its[i] = ix[i]; + } else if (geom->indexType() == QSGGeometry::UnsignedIntType) { + quint32 *its = geom->indexDataAsUInt(); + for (int i = 0; i < ix.size(); ++i) + its[i] = ix[i]; + } + + QSGGeometry::Point2D *pts = geom->vertexDataAsPoint2D(); + for (int i = 0; i < vx.size(); ++i) + pts[i].set(vx[i].x, vx[i].y); + } + + QVector<QDeclarativeGeoMapItemUtils::vec2> m_screenVertices; + QVector<quint32> m_screenIndices; + QDoubleVector2D m_bboxLeftBoundWrapped; + QVector<WrappedPolygon> m_wrappedPolygons; + int m_wrapOffset; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapPolygonShader : public QSGMaterialShader +{ +public: + MapPolygonShader(); + + const char *vertexShader() const override { + return + "attribute highp vec4 vertex; \n" + "uniform highp mat4 qt_Matrix; \n" + "uniform highp mat4 mapProjection; \n" + "uniform highp vec3 center; \n" + "uniform highp vec3 center_lowpart; \n" + "uniform lowp float wrapOffset; \n" + "vec4 wrapped(in vec4 v) { return vec4(v.x + wrapOffset, v.y, 0.0, 1.0); }\n" + "void main() { \n" + " vec4 vtx = wrapped(vertex) - vec4(center, 0.0); \n" + " vtx = vtx - vec4(center_lowpart, 0.0); \n" + " gl_Position = qt_Matrix * mapProjection * vtx; \n" + "}"; + } + + const char *fragmentShader() const override { + return + "uniform lowp vec4 color; \n" + "void main() { \n" + " gl_FragColor = color; \n" + "}"; + } + + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override + { + static char const *const attr[] = { "vertex", nullptr }; + return attr; + } + +private: + void initialize() override + { + m_matrix_id = program()->uniformLocation("qt_Matrix"); + m_color_id = program()->uniformLocation("color"); + m_mapProjection_id = program()->uniformLocation("mapProjection"); + m_center_id = program()->uniformLocation("center"); + m_center_lowpart_id = program()->uniformLocation("center_lowpart"); + m_wrapOffset_id = program()->uniformLocation("wrapOffset"); + } + int m_center_id; + int m_center_lowpart_id; + int m_mapProjection_id; + int m_matrix_id; + int m_color_id; + int m_wrapOffset_id; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapPolygonMaterial : public QSGFlatColorMaterial +{ +public: + MapPolygonMaterial() + : QSGFlatColorMaterial() + { + // Passing RequiresFullMatrix is essential in order to prevent the + // batch renderer from baking in simple, translate-only transforms into + // the vertex data. The shader will rely on the fact that + // vertexCoord.xy is the Shape-space coordinate and so no modifications + // are welcome. + setFlag(Blending | RequiresFullMatrix | CustomCompileStep); + } + + QSGMaterialShader *createShader() const override; + + void setGeoProjection(const QMatrix4x4 &p) + { + m_geoProjection = p; + } + + QMatrix4x4 geoProjection() const + { + return m_geoProjection; + } + + void setCenter(const QDoubleVector3D &c) + { + m_center = c; + } + + QDoubleVector3D center() const + { + return m_center; + } + + int wrapOffset() const + { + return m_wrapOffset; + } + + void setWrapOffset(int wrapOffset) + { + m_wrapOffset = wrapOffset; + } + + int compare(const QSGMaterial *other) const override; + QSGMaterialType *type() const override; + +protected: + QMatrix4x4 m_geoProjection; + QDoubleVector3D m_center; + int m_wrapOffset = 0; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapPolygonNode : public MapItemGeometryNode +{ + +public: + MapPolygonNode(); + ~MapPolygonNode() override; + + void update(const QColor &fillColor, const QColor &borderColor, + const QGeoMapItemGeometry *fillShape, + const QGeoMapItemGeometry *borderShape); +private: + QSGFlatColorMaterial fill_material_; + MapPolylineNode *border_; + QSGGeometry geometry_; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapPolygonNodeGL : public MapItemGeometryNode +{ + +public: + MapPolygonNodeGL(); + ~MapPolygonNodeGL() override; + + void update(const QColor &fillColor, + const QGeoMapPolygonGeometryOpenGL *fillShape, + const QMatrix4x4 &geoProjection, + const QDoubleVector3D ¢er); + + MapPolygonMaterial fill_material_; + QSGGeometry geometry_; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItemPrivate +{ +public: + QDeclarativePolygonMapItemPrivate(QDeclarativePolygonMapItem &polygon) : m_poly(polygon) + { + + } + QDeclarativePolygonMapItemPrivate(QDeclarativePolygonMapItemPrivate &other) : m_poly(other.m_poly) + { + } + + virtual ~QDeclarativePolygonMapItemPrivate(); + virtual void onLinePropertiesChanged() = 0; + virtual void markSourceDirtyAndUpdate() = 0; + virtual void onMapSet() = 0; + virtual void onGeoGeometryChanged() = 0; + virtual void onGeoGeometryUpdated() = 0; + virtual void onItemGeometryChanged() = 0; + virtual void updatePolish() = 0; + virtual void afterViewportChanged() = 0; + virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) = 0; + virtual bool contains(const QPointF &point) const = 0; + + QDeclarativePolygonMapItem &m_poly; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItemPrivateCPU: public QDeclarativePolygonMapItemPrivate +{ +public: + QDeclarativePolygonMapItemPrivateCPU(QDeclarativePolygonMapItem &polygon) : QDeclarativePolygonMapItemPrivate(polygon) + { + } + + QDeclarativePolygonMapItemPrivateCPU(QDeclarativePolygonMapItemPrivate &other) + : QDeclarativePolygonMapItemPrivate(other) + { + } + + ~QDeclarativePolygonMapItemPrivateCPU() override; + void onLinePropertiesChanged() override + { + // mark dirty just in case we're a width change + markSourceDirtyAndUpdate(); + } + void markSourceDirtyAndUpdate() override + { + // preserveGeometry is cleared in updateMapItemPaintNode + m_geometry.markSourceDirty(); + m_borderGeometry.markSourceDirty(); + m_poly.polishAndUpdate(); + } + void regenerateCache() + { + if (!m_poly.map() || m_poly.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection()); + m_geopathProjected.clear(); + m_geopathProjected.reserve(m_poly.m_geopoly.size()); + for (const QGeoCoordinate &c : m_poly.m_geopoly.path()) + m_geopathProjected << p.geoToMapProjection(c); + } + void updateCache() + { + if (!m_poly.map() || m_poly.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection()); + m_geopathProjected << p.geoToMapProjection(m_poly.m_geopoly.path().last()); + } + void preserveGeometry() + { + m_geometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft()); + m_borderGeometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft()); + } + void afterViewportChanged() override + { + // preserveGeometry is cleared in updateMapItemPaintNode + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + void onMapSet() override + { + regenerateCache(); + markSourceDirtyAndUpdate(); + } + void onGeoGeometryChanged() override + { + regenerateCache(); + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + void onGeoGeometryUpdated() override + { + updateCache(); + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + void onItemGeometryChanged() override + { + onGeoGeometryChanged(); + } + void updatePolish() override + { + if (m_poly.m_geopoly.path().length() == 0) { // Possibly cleared + m_geometry.clear(); + m_borderGeometry.clear(); + m_poly.setWidth(0); + m_poly.setHeight(0); + return; + } + const QGeoMap *map = m_poly.map(); + const qreal borderWidth = m_poly.m_border.width(); + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map->geoProjection()); + QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry); + m_poly.m_updatingGeometry = true; + + m_geometry.updateSourcePoints(*map, m_geopathProjected); + m_geometry.updateScreenPoints(*map, borderWidth); + + QList<QGeoMapItemGeometry *> geoms; + geoms << &m_geometry; + m_borderGeometry.clear(); + + if (m_poly.m_border.color().alpha() != 0 && borderWidth > 0) { + QList<QDoubleVector2D> closedPath = m_geopathProjected; + closedPath << closedPath.first(); + + m_borderGeometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft()); + + const QGeoCoordinate &geometryOrigin = m_geometry.origin(); + + m_borderGeometry.srcPoints_.clear(); + m_borderGeometry.srcPointTypes_.clear(); + + QDoubleVector2D borderLeftBoundWrapped; + QList<QList<QDoubleVector2D > > clippedPaths = m_borderGeometry.clipPath(*map, closedPath, borderLeftBoundWrapped); + if (clippedPaths.size()) { + borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); + m_borderGeometry.pathToScreen(*map, clippedPaths, borderLeftBoundWrapped); + m_borderGeometry.updateScreenPoints(*map, borderWidth); + + geoms << &m_borderGeometry; + } else { + m_borderGeometry.clear(); + } + } + + QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); + m_poly.setWidth(combined.width() + 2 * borderWidth); + m_poly.setHeight(combined.height() + 2 * borderWidth); + + m_poly.setPositionOnMap(m_geometry.origin(), -1 * m_geometry.sourceBoundingBox().topLeft() + + QPointF(borderWidth, borderWidth)); + } + QSGNode *updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override + { + Q_UNUSED(data); + if (!m_node || !oldNode) { + m_node = new MapPolygonNode(); + if (oldNode) { + delete oldNode; + oldNode = nullptr; + } + } else { + m_node = static_cast<MapPolygonNode *>(oldNode); + } + + //TODO: update only material + if (m_geometry.isScreenDirty() + || m_borderGeometry.isScreenDirty() + || m_poly.m_dirtyMaterial + || !oldNode) { + m_node->update(m_poly.m_color, + m_poly.m_border.color(), + &m_geometry, + &m_borderGeometry); + m_geometry.setPreserveGeometry(false); + m_borderGeometry.setPreserveGeometry(false); + m_geometry.markClean(); + m_borderGeometry.markClean(); + m_poly.m_dirtyMaterial = false; + } + return m_node; + } + bool contains(const QPointF &point) const override + { + return (m_geometry.contains(point) || m_borderGeometry.contains(point)); + } + + QList<QDoubleVector2D> m_geopathProjected; + QGeoMapPolygonGeometry m_geometry; + QGeoMapPolylineGeometry m_borderGeometry; + MapPolygonNode *m_node = nullptr; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItemPrivateOpenGL: public QDeclarativePolygonMapItemPrivate +{ +public: + struct RootNode : public QSGNode /*QSGTransformNode*/, public VisibleNode + { + RootNode() { } + + bool isSubtreeBlocked() const override + { + return subtreeBlocked(); + } + }; + + QDeclarativePolygonMapItemPrivateOpenGL(QDeclarativePolygonMapItem &polygon) : QDeclarativePolygonMapItemPrivate(polygon) + { + } + + QDeclarativePolygonMapItemPrivateOpenGL(QDeclarativePolygonMapItemPrivate &other) + : QDeclarativePolygonMapItemPrivate(other) + { + } + + ~QDeclarativePolygonMapItemPrivateOpenGL() override; + + void markScreenDirtyAndUpdate() + { + // preserveGeometry is cleared in updateMapItemPaintNode + m_geometry.markScreenDirty(); + m_borderGeometry.markScreenDirty(); + m_poly.polishAndUpdate(); + } + void onLinePropertiesChanged() override + { + m_poly.m_dirtyMaterial = true; + afterViewportChanged(); + } + void markSourceDirtyAndUpdate() override + { + // preserveGeometry is cleared in updateMapItemPaintNode + m_geometry.markSourceDirty(); + m_borderGeometry.markSourceDirty(); + m_poly.polishAndUpdate(); + } + void preserveGeometry() + { + m_geometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft()); + m_borderGeometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft()); + } + void afterViewportChanged() override // This is called when the camera changes, or visibleArea changes. + { + // preserveGeometry is cleared in updateMapItemPaintNode + preserveGeometry(); + markScreenDirtyAndUpdate(); + } + void onMapSet() override + { + markSourceDirtyAndUpdate(); + } + void onGeoGeometryChanged() override + { + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + void onGeoGeometryUpdated() override + { + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + void onItemGeometryChanged() override + { + onGeoGeometryChanged(); + } + void updatePolish() override + { + if (m_poly.m_geopoly.path().length() == 0) { // Possibly cleared + m_geometry.clear(); + m_borderGeometry.clear(); + m_poly.setWidth(0); + m_poly.setHeight(0); + return; + } + + QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry); + m_poly.m_updatingGeometry = true; + const qreal lineWidth = m_poly.m_border.width(); + const QColor &lineColor = m_poly.m_border.color(); + const QColor &fillColor = m_poly.color(); + if (fillColor.alpha() != 0) { + m_geometry.updateSourcePoints(*m_poly.map(), m_poly.m_geopoly); + m_geometry.markScreenDirty(); + m_geometry.updateScreenPoints(*m_poly.map(), lineWidth, lineColor); + } else { + m_geometry.clearBounds(); + } + + QGeoMapItemGeometry * geom = &m_geometry; + m_borderGeometry.clearScreen(); + if (lineColor.alpha() != 0 && lineWidth > 0) { + m_borderGeometry.updateSourcePoints(*m_poly.map(), m_poly.m_geopoly); + m_borderGeometry.markScreenDirty(); + m_borderGeometry.updateScreenPoints(*m_poly.map(), lineWidth); + geom = &m_borderGeometry; + } + m_poly.setWidth(geom->sourceBoundingBox().width()); + m_poly.setHeight(geom->sourceBoundingBox().height()); + m_poly.setPosition(1.0 * geom->firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5)); + } + QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override + { + Q_UNUSED(data); + + if (!m_rootNode || !oldNode) { + m_rootNode = new RootNode(); + m_node = new MapPolygonNodeGL(); + m_rootNode->appendChildNode(m_node); + m_polylinenode = new MapPolylineNodeOpenGLExtruded(); + m_rootNode->appendChildNode(m_polylinenode); + m_rootNode->markDirty(QSGNode::DirtyNodeAdded); + if (oldNode) + delete oldNode; + } else { + m_rootNode = static_cast<RootNode *>(oldNode); + } + + const QGeoMap *map = m_poly.map(); + const QMatrix4x4 &combinedMatrix = map->geoProjection().qsgTransform(); + const QDoubleVector3D &cameraCenter = map->geoProjection().centerMercator(); + + if (m_borderGeometry.isScreenDirty()) { + /* Do the border update first */ + m_polylinenode->update(m_poly.m_border.color(), + float(m_poly.m_border.width()), + &m_borderGeometry, + combinedMatrix, + cameraCenter, + Qt::SquareCap, + true, + 30); // No LOD for polygons just yet. + // First figure out what to do with holes. + m_borderGeometry.setPreserveGeometry(false); + m_borderGeometry.markClean(); + } else { + m_polylinenode->setSubtreeBlocked(true); + } + if (m_geometry.isScreenDirty()) { + m_node->update(m_poly.m_color, + &m_geometry, + combinedMatrix, + cameraCenter); + m_geometry.setPreserveGeometry(false); + m_geometry.markClean(); + } else { + m_node->setSubtreeBlocked(true); + } + + m_rootNode->setSubtreeBlocked(false); + return m_rootNode; + } + bool contains(const QPointF &point) const override + { + const qreal lineWidth = m_poly.m_border.width(); + const QColor &lineColor = m_poly.m_border.color(); + const QRectF &bounds = (lineColor.alpha() != 0 && lineWidth > 0) ? m_borderGeometry.sourceBoundingBox() : m_geometry.sourceBoundingBox(); + if (bounds.contains(point)) { + QDeclarativeGeoMap *m = m_poly.quickMap(); + if (m) { + const QGeoCoordinate crd = m->toCoordinate(m->mapFromItem(&m_poly, point)); + return m_poly.m_geopoly.contains(crd) || m_borderGeometry.contains(m_poly.mapToItem(m_poly.quickMap(), point), + m_poly.border()->width(), + static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection())); + } else { + return true; + } + } + return false; + } + + QGeoMapPolygonGeometryOpenGL m_geometry; + QGeoMapPolylineGeometryOpenGL m_borderGeometry; + RootNode *m_rootNode = nullptr; + MapPolygonNodeGL *m_node = nullptr; + MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEPOLYGONMAPITEM_P_P_H diff --git a/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp b/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp index 10f3f0c3..d59704dc 100644 --- a/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp +++ b/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp @@ -35,11 +35,16 @@ ****************************************************************************/ #include "qdeclarativepolylinemapitem_p.h" +#include "qdeclarativepolylinemapitem_p_p.h" +#include "qdeclarativerectanglemapitem_p_p.h" +#include "qdeclarativecirclemapitem_p_p.h" #include "qlocationutils_p.h" +#include "qdeclarativegeomapitemutils_p.h" #include "error_messages_p.h" #include "locationvaluetypehelper_p.h" #include "qdoublevector2d_p.h" #include <QtLocation/private/qgeomap_p.h> +#include <QtPositioning/private/qwebmercator_p.h> #include <QtCore/QScopedValueRollback> #include <QtQml/QQmlInfo> @@ -55,10 +60,32 @@ #include <QtPositioning/private/qclipperutils_p.h> #include <QtPositioning/private/qgeopath_p.h> +#include <QtQuick/private/qsgmaterialshader_p.h> #include <array> +#include <QThreadPool> +#include <QRunnable> +#include <QtLocation/private/qgeomapparameter_p.h> +#include "qgeosimplify_p.h" QT_BEGIN_NAMESPACE +struct ThreadPool // to have a thread pool with max 1 thread for geometry processing +{ + ThreadPool () + { + m_threadPool.setMaxThreadCount(1); + } + + void start(QRunnable *runnable, int priority = 0) + { + m_threadPool.start(runnable, priority); + } + + QThreadPool m_threadPool; +}; + +Q_GLOBAL_STATIC(ThreadPool, threadPool) + static const double kClipperScaleFactor = 281474976710656.0; // 48 bits of precision @@ -125,7 +152,7 @@ static QList<QList<QDoubleVector2D> > clipLine( const QList<QDoubleVector2D> &poly) { QList<QList<QDoubleVector2D> > res; - if (poly.size() < 3 || l.size() < 2) + if (poly.size() < 2 || l.size() < 2) return res; // Step 1: build edges @@ -355,11 +382,6 @@ void QDeclarativeMapLineProperties::setWidth(qreal width) emit widthChanged(width_); } -struct Vertex -{ - QVector2D position; -}; - QGeoMapPolylineGeometry::QGeoMapPolylineGeometry() { } @@ -661,7 +683,7 @@ void QGeoMapPolylineGeometry::updateScreenPoints(const QGeoMap &map, // Create the viewport rect in the same coordinate system // as the actual points QRectF viewport(0, 0, map.viewportWidth(), map.viewportHeight()); - viewport.adjust(-strokeWidth, -strokeWidth, strokeWidth, strokeWidth); + viewport.adjust(-strokeWidth, -strokeWidth, strokeWidth * 2, strokeWidth * 2); viewport.translate(-1 * origin); QVector<qreal> points; @@ -719,7 +741,7 @@ void QGeoMapPolylineGeometry::updateScreenPoints(const QGeoMap &map, } screenBounds_ = bb; - const QPointF strokeOffset = (adjustTranslation) ? QPointF(strokeWidth, strokeWidth) : QPointF(); + const QPointF strokeOffset = (adjustTranslation) ? QPointF(strokeWidth, strokeWidth) * 0.5: QPointF(); this->translate( -1 * sourceBounds_.topLeft() + strokeOffset); } @@ -747,16 +769,202 @@ bool QGeoMapPolylineGeometry::contains(const QPointF &point) const return false; } +void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoPolygon &poly) +{ + if (!sourceDirty_) + return; + QGeoPath p(poly.path()); + if (poly.path().size() && poly.path().last() != poly.path().first()) + p.addCoordinate(poly.path().first()); + updateSourcePoints(map, p); +} + +void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoPath &poly) +{ + if (!sourceDirty_) + return; + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection()); + + // build the actual path + // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints + + + QDoubleVector2D leftBoundWrapped; + // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 + QList<QDoubleVector2D> wrappedPath; + QDeclarativeGeoMapItemUtils::wrapPath(poly.path(), geoLeftBound_, p, + wrappedPath, &leftBoundWrapped); + + const QGeoRectangle &boundingRectangle = poly.boundingGeoRectangle(); + updateSourcePoints(p, wrappedPath, boundingRectangle); +} + +void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoProjectionWebMercator &p, + const QList<QDoubleVector2D> &wrappedPath, + const QGeoRectangle &boundingRectangle) { + if (!sourceDirty_) + return; + // 1.1) do the same for the bbox + // Beware: vertical lines (or horizontal lines) might have an "empty" bbox. Check for that + + QGeoCoordinate topLeft = boundingRectangle.topLeft(); + QGeoCoordinate bottomRight = boundingRectangle.bottomRight(); + const qreal epsilon = 0.000001; + if (qFuzzyCompare(topLeft.latitude(), bottomRight.latitude())) { + topLeft.setLatitude(qBound(-90.0, topLeft.latitude() + epsilon ,90.0)); + bottomRight.setLatitude(qBound(-90.0, bottomRight.latitude() - epsilon ,90.0)); + } + if (qFuzzyCompare(topLeft.longitude(), bottomRight.longitude())) { + topLeft.setLongitude(QLocationUtils::wrapLong(topLeft.longitude() - epsilon)); + bottomRight.setLongitude(QLocationUtils::wrapLong(bottomRight.longitude() + epsilon)); + } + QGeoPolygon bbox(QGeoRectangle(topLeft, bottomRight)); + QList<QDoubleVector2D> wrappedBbox, wrappedBboxPlus1, wrappedBboxMinus1; + QDeclarativeGeoMapItemUtils::wrapPath(bbox.path(), bbox.boundingGeoRectangle().topLeft(), p, + wrappedBbox, wrappedBboxMinus1, wrappedBboxPlus1, &m_bboxLeftBoundWrapped); + + // New pointers, some old LOD task might still be running and operating on the old pointers. + resetLOD(); + + for (const auto &v: qAsConst(wrappedPath)) m_screenVertices->append(v); + + m_wrappedPolygons.resize(3); + m_wrappedPolygons[0].wrappedBboxes = wrappedBboxMinus1; + m_wrappedPolygons[1].wrappedBboxes = wrappedBbox; + m_wrappedPolygons[2].wrappedBboxes = wrappedBboxPlus1; + srcOrigin_ = geoLeftBound_; +} + +void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoRectangle &rect) +{ + const QGeoPath path(QDeclarativeRectangleMapItemPrivateCPU::perimeter(rect)); + updateSourcePoints(map, path); +} + +void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoCircle &circle) +{ + if (!sourceDirty_) + return; + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection()); + + QDoubleVector2D leftBoundWrapped; + // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 + QList<QGeoCoordinate> path; + QGeoCoordinate leftBound; + QList<QDoubleVector2D> wrappedPath; + QDeclarativeCircleMapItemPrivateCPU::calculatePeripheralPoints(path, circle.center(), circle.radius(), QDeclarativeCircleMapItemPrivateCPU::CircleSamples, leftBound); + path << path.first(); + geoLeftBound_ = leftBound; + QDeclarativeGeoMapItemUtils::wrapPath(path, leftBound, p, wrappedPath, &leftBoundWrapped); + const QGeoRectangle &boundingRectangle = circle.boundingGeoRectangle(); + updateSourcePoints(p, wrappedPath, boundingRectangle); +} + +void QGeoMapPolylineGeometryOpenGL::updateScreenPoints(const QGeoMap &map, qreal strokeWidth, bool /*adjustTranslation*/) +{ + if (map.viewportWidth() == 0 || map.viewportHeight() == 0) { + clear(); + return; + } + + // 1) identify which set to use: std, +1 or -1 + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection()); + const QDoubleVector2D leftBoundMercator = p.geoToMapProjection(srcOrigin_); + m_wrapOffset = p.projectionWrapFactor(leftBoundMercator) + 1; // +1 to get the offset into QLists + + if (sourceDirty_) { + // 1.1) select geometry set + // This could theoretically be skipped for those polylines whose bbox is not even projectable. + // However, such optimization could only be introduced if not calculating bboxes lazily. + // Hence not doing it. +// if (m_screenVertices.size() > 1) + m_dataChanged = true; + } + + updateQuickGeometry(p, strokeWidth); +} + +void QGeoMapPolylineGeometryOpenGL::updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth) +{ + // 2) clip bbox + // BBox handling -- this is related to the bounding box geometry + // that has to inevitably follow the old projection codepath + // As it needs to provide projected coordinates for QtQuick interaction. + // This could be futher optimized to be updated in a lazy fashion. + const QList<QDoubleVector2D> &wrappedBbox = m_wrappedPolygons.at(m_wrapOffset).wrappedBboxes; + QList<QList<QDoubleVector2D> > clippedBbox; + QDoubleVector2D bboxLeftBoundWrapped = m_bboxLeftBoundWrapped; + bboxLeftBoundWrapped.setX(bboxLeftBoundWrapped.x() + double(m_wrapOffset - 1)); + QDeclarativeGeoMapItemUtils::clipPolygon(wrappedBbox, p, clippedBbox, &bboxLeftBoundWrapped, false); + + // 3) project bbox + QPainterPath ppi; + + if ( !clippedBbox.size() || + clippedBbox.first().size() < 3) { + sourceBounds_ = screenBounds_ = QRectF(); + firstPointOffset_ = QPointF(); + screenOutline_ = ppi; + return; + } + + QDeclarativeGeoMapItemUtils::projectBbox(clippedBbox.first(), p, ppi); // Using first because a clipped box should always result in one polygon + const QRectF brect = ppi.boundingRect(); + firstPointOffset_ = QPointF(brect.topLeft()); + sourceBounds_ = brect; + screenOutline_ = ppi; + + // 4) Set Screen bbox + screenBounds_ = brect; + sourceBounds_.setX(0); + sourceBounds_.setY(0); + sourceBounds_.setWidth(brect.width() + strokeWidth); + sourceBounds_.setHeight(brect.height() + strokeWidth); +} + +/* + * QDeclarativePolygonMapItem Private Implementations + */ + +QDeclarativePolylineMapItemPrivate::~QDeclarativePolylineMapItemPrivate() {} + + +QDeclarativePolylineMapItemPrivateCPU::~QDeclarativePolylineMapItemPrivateCPU() {} + +QDeclarativePolylineMapItemPrivateOpenGLLineStrip::~QDeclarativePolylineMapItemPrivateOpenGLLineStrip() {} + +QDeclarativePolylineMapItemPrivateOpenGLExtruded::~QDeclarativePolylineMapItemPrivateOpenGLExtruded() {} + +/* + * QDeclarativePolygonMapItem Implementation + */ + +struct PolylineBackendSelector +{ + PolylineBackendSelector() + { + backend = (qgetenv("QTLOCATION_OPENGL_ITEMS").toInt()) ? QDeclarativePolylineMapItem::OpenGLExtruded : QDeclarativePolylineMapItem::Software; + } + QDeclarativePolylineMapItem::Backend backend = QDeclarativePolylineMapItem::Software; +}; + +Q_GLOBAL_STATIC(PolylineBackendSelector, mapPolylineBackendSelector) + QDeclarativePolylineMapItem::QDeclarativePolylineMapItem(QQuickItem *parent) -: QDeclarativeGeoMapItemBase(parent), line_(this), dirtyMaterial_(true), updatingGeometry_(false) +: QDeclarativeGeoMapItemBase(parent), + m_line(this), + m_dirtyMaterial(true), + m_updatingGeometry(false), + m_d(new QDeclarativePolylineMapItemPrivateCPU(*this)) { m_itemType = QGeoMap::MapPolyline; - geopath_ = QGeoPathEager(); + m_geopath = QGeoPathEager(); setFlag(ItemHasContents, true); - QObject::connect(&line_, SIGNAL(colorChanged(QColor)), + QObject::connect(&m_line, SIGNAL(colorChanged(QColor)), this, SLOT(updateAfterLinePropertiesChanged())); - QObject::connect(&line_, SIGNAL(widthChanged(qreal)), + QObject::connect(&m_line, SIGNAL(widthChanged(qreal)), this, SLOT(updateAfterLinePropertiesChanged())); + setBackend(mapPolylineBackendSelector->backend); } QDeclarativePolylineMapItem::~QDeclarativePolylineMapItem() @@ -768,9 +976,7 @@ QDeclarativePolylineMapItem::~QDeclarativePolylineMapItem() */ void QDeclarativePolylineMapItem::updateAfterLinePropertiesChanged() { - // mark dirty just in case we're a width change - geometry_.markSourceDirty(); - polishAndUpdate(); + m_d->onLinePropertiesChanged(); } /*! @@ -779,11 +985,8 @@ void QDeclarativePolylineMapItem::updateAfterLinePropertiesChanged() void QDeclarativePolylineMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) { QDeclarativeGeoMapItemBase::setMap(quickMap,map); - if (map) { - regenerateCache(); - geometry_.markSourceDirty(); - polishAndUpdate(); - } + if (map) + m_d->onMapSet(); } /*! @@ -795,7 +998,7 @@ void QDeclarativePolylineMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap * QJSValue QDeclarativePolylineMapItem::path() const { - return fromList(this, geopath_.path()); + return fromList(this, m_geopath.path()); } void QDeclarativePolylineMapItem::setPath(const QJSValue &value) @@ -807,7 +1010,7 @@ void QDeclarativePolylineMapItem::setPath(const QJSValue &value) } /*! - \qmlmethod int MapPolyline::setPath(geopath path) + \qmlmethod void MapPolyline::setPath(geopath path) Sets the \a path using a geopath type. @@ -817,13 +1020,11 @@ void QDeclarativePolylineMapItem::setPath(const QJSValue &value) */ void QDeclarativePolylineMapItem::setPath(const QGeoPath &path) { - if (geopath_.path() == path.path()) + if (m_geopath.path() == path.path()) return; - geopath_ = QGeoPathEager(path); - regenerateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_geopath = QGeoPathEager(path); + m_d->onGeoGeometryChanged(); emit pathChanged(); } @@ -832,14 +1033,12 @@ void QDeclarativePolylineMapItem::setPath(const QGeoPath &path) */ void QDeclarativePolylineMapItem::setPathFromGeoList(const QList<QGeoCoordinate> &path) { - if (geopath_.path() == path) + if (m_geopath.path() == path) return; - geopath_.setPath(path); + m_geopath.setPath(path); - regenerateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_d->onGeoGeometryChanged(); emit pathChanged(); } @@ -854,7 +1053,7 @@ void QDeclarativePolylineMapItem::setPathFromGeoList(const QList<QGeoCoordinate> */ int QDeclarativePolylineMapItem::pathLength() const { - return geopath_.path().length(); + return m_geopath.path().length(); } /*! @@ -869,11 +1068,9 @@ void QDeclarativePolylineMapItem::addCoordinate(const QGeoCoordinate &coordinate if (!coordinate.isValid()) return; - geopath_.addCoordinate(coordinate); + m_geopath.addCoordinate(coordinate); - updateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_d->onGeoGeometryUpdated(); emit pathChanged(); } @@ -888,14 +1085,12 @@ void QDeclarativePolylineMapItem::addCoordinate(const QGeoCoordinate &coordinate */ void QDeclarativePolylineMapItem::insertCoordinate(int index, const QGeoCoordinate &coordinate) { - if (index < 0 || index > geopath_.path().length()) + if (index < 0 || index > m_geopath.path().length()) return; - geopath_.insertCoordinate(index, coordinate); + m_geopath.insertCoordinate(index, coordinate); - regenerateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_d->onGeoGeometryChanged(); emit pathChanged(); } @@ -911,14 +1106,12 @@ void QDeclarativePolylineMapItem::insertCoordinate(int index, const QGeoCoordina */ void QDeclarativePolylineMapItem::replaceCoordinate(int index, const QGeoCoordinate &coordinate) { - if (index < 0 || index >= geopath_.path().length()) + if (index < 0 || index >= m_geopath.path().length()) return; - geopath_.replaceCoordinate(index, coordinate); + m_geopath.replaceCoordinate(index, coordinate); - regenerateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_d->onGeoGeometryChanged(); emit pathChanged(); } @@ -933,10 +1126,10 @@ void QDeclarativePolylineMapItem::replaceCoordinate(int index, const QGeoCoordin */ QGeoCoordinate QDeclarativePolylineMapItem::coordinateAt(int index) const { - if (index < 0 || index >= geopath_.path().length()) + if (index < 0 || index >= m_geopath.path().length()) return QGeoCoordinate(); - return geopath_.coordinateAt(index); + return m_geopath.coordinateAt(index); } /*! @@ -948,7 +1141,7 @@ QGeoCoordinate QDeclarativePolylineMapItem::coordinateAt(int index) const */ bool QDeclarativePolylineMapItem::containsCoordinate(const QGeoCoordinate &coordinate) { - return geopath_.containsCoordinate(coordinate); + return m_geopath.containsCoordinate(coordinate); } /*! @@ -963,13 +1156,12 @@ bool QDeclarativePolylineMapItem::containsCoordinate(const QGeoCoordinate &coord */ void QDeclarativePolylineMapItem::removeCoordinate(const QGeoCoordinate &coordinate) { - int length = geopath_.path().length(); - geopath_.removeCoordinate(coordinate); - if (geopath_.path().length() == length) + int length = m_geopath.path().length(); + m_geopath.removeCoordinate(coordinate); + if (m_geopath.path().length() == length) return; - regenerateCache(); - markSourceDirtyAndUpdate(); + m_d->onGeoGeometryChanged(); emit pathChanged(); } @@ -986,14 +1178,12 @@ void QDeclarativePolylineMapItem::removeCoordinate(const QGeoCoordinate &coordin */ void QDeclarativePolylineMapItem::removeCoordinate(int index) { - if (index < 0 || index >= geopath_.path().length()) + if (index < 0 || index >= m_geopath.path().length()) return; - geopath_.removeCoordinate(index); + m_geopath.removeCoordinate(index); - regenerateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_d->onGeoGeometryChanged(); emit pathChanged(); } @@ -1013,7 +1203,47 @@ void QDeclarativePolylineMapItem::removeCoordinate(int index) QDeclarativeMapLineProperties *QDeclarativePolylineMapItem::line() { - return &line_; + return &m_line; +} + +/*! + \qmlproperty MapPolyline.Backend QtLocation::MapPolyline::backend + + This property holds which backend is in use to render the map item. + Valid values are \b MapPolyline.Software and \b{MapPolyline.OpenGLLineStrip} + and \b{MapPolyline.OpenGLExtruded}. + The default value is \b{MapPolyline.Software}. + + \note \b{The release of this API with Qt 5.15 is a Technology Preview}. + Ideally, as the OpenGL backends for map items mature, there will be + no more need to also offer the legacy software-projection backend. + So this property will likely disappear at some later point. + To select OpenGL-accelerated item backends without using this property, + it is also possible to set the environment variable \b QTLOCATION_OPENGL_ITEMS + to \b{1}. + Also note that all current OpenGL backends won't work as expected when enabling + layers on the individual item, or when running on OpenGL core profiles greater than 2.x. + + \since 5.15 +*/ +QDeclarativePolylineMapItem::Backend QDeclarativePolylineMapItem::backend() const +{ + return m_backend; +} + +void QDeclarativePolylineMapItem::setBackend(QDeclarativePolylineMapItem::Backend b) +{ + if (b == m_backend) + return; + m_backend = b; + QScopedPointer<QDeclarativePolylineMapItemPrivate> d((m_backend == Software) + ? static_cast<QDeclarativePolylineMapItemPrivate *>(new QDeclarativePolylineMapItemPrivateCPU(*this)) + : ((m_backend == OpenGLExtruded) + ? static_cast<QDeclarativePolylineMapItemPrivate * >(new QDeclarativePolylineMapItemPrivateOpenGLExtruded(*this)) + : static_cast<QDeclarativePolylineMapItemPrivate * >(new QDeclarativePolylineMapItemPrivateOpenGLLineStrip(*this)))); + m_d.swap(d); + m_d->onGeoGeometryChanged(); + emit backendChanged(); } /*! @@ -1021,7 +1251,7 @@ QDeclarativeMapLineProperties *QDeclarativePolylineMapItem::line() */ void QDeclarativePolylineMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { - if (!map() || !geopath_.isValid() || updatingGeometry_ || newGeometry.topLeft() == oldGeometry.topLeft()) { + if (newGeometry.topLeft() == oldGeometry.topLeft() || !map() || !m_geopath.isValid() || m_updatingGeometry) { QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); return; } @@ -1035,10 +1265,8 @@ void QDeclarativePolylineMapItem::geometryChanged(const QRectF &newGeometry, con if (offsetLati == 0.0 && offsetLongi == 0.0) return; - geopath_.translate(offsetLati, offsetLongi); - regenerateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_geopath.translate(offsetLati, offsetLongi); + m_d->onGeoGeometryChanged(); emit pathChanged(); // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested @@ -1050,68 +1278,78 @@ void QDeclarativePolylineMapItem::geometryChanged(const QRectF &newGeometry, con */ void QDeclarativePolylineMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) { - if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) + if (event.mapSize.isEmpty()) return; - geometry_.setPreserveGeometry(true, geometry_.geoLeftBound()); - markSourceDirtyAndUpdate(); + m_d->afterViewportChanged(); } /*! \internal */ -void QDeclarativePolylineMapItem::regenerateCache() +void QDeclarativePolylineMapItem::updatePolish() { if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) return; - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection()); - geopathProjected_.clear(); - geopathProjected_.reserve(geopath_.path().size()); - for (const QGeoCoordinate &c : geopath_.path()) - geopathProjected_ << p.geoToMapProjection(c); + m_d->updatePolish(); } -/*! - \internal -*/ -void QDeclarativePolylineMapItem::updateCache() +void QDeclarativePolylineMapItem::updateLineStyleParameter(QGeoMapParameter *p, + const char *propertyName, + bool update) { - if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) - return; - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection()); - geopathProjected_ << p.geoToMapProjection(geopath_.path().last()); + static const QByteArrayList acceptedParameterTypes = QByteArrayList() + << QByteArrayLiteral("lineCap") + << QByteArrayLiteral("pen"); + switch (acceptedParameterTypes.indexOf(QByteArray(propertyName))) { + case -1: + qWarning() << "Invalid property " << QLatin1String(propertyName) << " for parameter lineStyle"; + break; + case 0: // lineCap + { + const QVariant lineCap = p->property("lineCap"); + m_d->m_penCapStyle = lineCap.value<Qt::PenCapStyle>(); // if invalid, will return 0 == FlatCap + if (update) + markSourceDirtyAndUpdate(); + break; + } + case 1: // penStyle + { + const QVariant penStyle = p->property("pen"); + m_d->m_penStyle = penStyle.value<Qt::PenStyle>(); + if (m_d->m_penStyle == Qt::NoPen) + m_d->m_penStyle = Qt::SolidLine; + if (update) + markSourceDirtyAndUpdate(); + break; + } + } } -/*! - \internal -*/ -void QDeclarativePolylineMapItem::updatePolish() +void QDeclarativePolylineMapItem::updateLineStyleParameter(QGeoMapParameter *p, const char *propertyName) { - if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) - return; - if (geopath_.path().length() == 0) { // Possibly cleared - geometry_.clear(); - setWidth(0); - setHeight(0); - return; - } - - QScopedValueRollback<bool> rollback(updatingGeometry_); - updatingGeometry_ = true; - - geometry_.updateSourcePoints(*map(), geopathProjected_, geopath_.boundingGeoRectangle().topLeft()); - geometry_.updateScreenPoints(*map(), line_.width()); - - setWidth(geometry_.sourceBoundingBox().width() + 2 * line_.width()); - setHeight(geometry_.sourceBoundingBox().height() + 2 * line_.width()); + updateLineStyleParameter(p, propertyName, true); +} - setPositionOnMap(geometry_.origin(), -1 * geometry_.sourceBoundingBox().topLeft() + QPointF(line_.width(), line_.width())); +void QDeclarativePolylineMapItem::componentComplete() +{ + QQuickItem::componentComplete(); + // Set up Dynamic Parameters + QList<QGeoMapParameter *> dynamicParameters = quickChildren<QGeoMapParameter>(); + for (QGeoMapParameter *p : qAsConst(dynamicParameters)) { + if (p->type() == QLatin1String("lineStyle")) { + updateLineStyleParameter(p, "lineCap", false); + updateLineStyleParameter(p, "pen", false); + connect(p, &QGeoMapParameter::propertyUpdated, + this, static_cast<void (QDeclarativePolylineMapItem::*)(QGeoMapParameter *, const char *)>(&QDeclarativePolylineMapItem::updateLineStyleParameter)); + markSourceDirtyAndUpdate(); + } + } } void QDeclarativePolylineMapItem::markSourceDirtyAndUpdate() { - geometry_.markSourceDirty(); - polishAndUpdate(); + m_d->markSourceDirtyAndUpdate(); } /*! @@ -1119,32 +1357,17 @@ void QDeclarativePolylineMapItem::markSourceDirtyAndUpdate() */ QSGNode *QDeclarativePolylineMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) { - Q_UNUSED(data); - - MapPolylineNode *node = static_cast<MapPolylineNode *>(oldNode); - - if (!node) { - node = new MapPolylineNode(); - } - - //TODO: update only material - if (geometry_.isScreenDirty() || dirtyMaterial_ || !oldNode) { - node->update(line_.color(), &geometry_); - geometry_.setPreserveGeometry(false); - geometry_.markClean(); - dirtyMaterial_ = false; - } - return node; + return m_d->updateMapItemPaintNode(oldNode, data); } bool QDeclarativePolylineMapItem::contains(const QPointF &point) const { - return geometry_.contains(point); + return m_d->contains(point); } const QGeoShape &QDeclarativePolylineMapItem::geoShape() const { - return geopath_; + return m_geopath; } void QDeclarativePolylineMapItem::setGeoShape(const QGeoShape &shape) @@ -1254,4 +1477,608 @@ void MapPolylineNode::update(const QColor &fillColor, } } +MapPolylineNodeOpenGLLineStrip::MapPolylineNodeOpenGLLineStrip() +: geometry_(QSGGeometry::defaultAttributes_Point2D(), 0) +{ + geometry_.setDrawingMode(QSGGeometry::DrawLineStrip); + QSGGeometryNode::setMaterial(&fill_material_); + QSGGeometryNode::setGeometry(&geometry_); +} + +MapPolylineNodeOpenGLLineStrip::~MapPolylineNodeOpenGLLineStrip() +{ + +} + +void MapPolylineNodeOpenGLLineStrip::update(const QColor &fillColor, + const qreal lineWidth, + const QGeoMapPolylineGeometryOpenGL *shape, + const QMatrix4x4 &geoProjection, + const QDoubleVector3D ¢er, + const Qt::PenCapStyle /*capStyle*/) +{ + if (shape->m_screenVertices->size() < 2) { + setSubtreeBlocked(true); + return; + } else { + setSubtreeBlocked(false); + } + + QSGGeometry *fill = QSGGeometryNode::geometry(); + if (shape->m_dataChanged) { + shape->allocateAndFillLineStrip(fill); + markDirty(DirtyGeometry); + shape->m_dataChanged = false; + } + fill->setLineWidth(lineWidth); + fill_material_.setLineWidth(lineWidth); // to make the material not compare equal if linewidth changes + +// if (fillColor != fill_material_.color()) + { + fill_material_.setWrapOffset(shape->m_wrapOffset - 1); + fill_material_.setColor(fillColor); + fill_material_.setGeoProjection(geoProjection); + fill_material_.setCenter(center); + setMaterial(&fill_material_); + markDirty(DirtyMaterial); + } +} + +MapPolylineShaderLineStrip::MapPolylineShaderLineStrip() : QSGMaterialShader(*new QSGMaterialShaderPrivate) +{ + +} + +void MapPolylineShaderLineStrip::updateState(const QSGMaterialShader::RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); + MapPolylineMaterial *oldMaterial = static_cast<MapPolylineMaterial *>(oldEffect); + MapPolylineMaterial *newMaterial = static_cast<MapPolylineMaterial *>(newEffect); + + const QColor &c = newMaterial->color(); + const QMatrix4x4 &geoProjection = newMaterial->geoProjection(); + const QDoubleVector3D ¢er = newMaterial->center(); + + QVector3D vecCenter, vecCenter_lowpart; + for (int i = 0; i < 3; i++) + QLocationUtils::split_double(center.get(i), &vecCenter[i], &vecCenter_lowpart[i]); + + if (oldMaterial == nullptr || c != oldMaterial->color() || state.isOpacityDirty()) { + float opacity = state.opacity() * c.alphaF(); + QVector4D v(c.redF() * opacity, + c.greenF() * opacity, + c.blueF() * opacity, + opacity); + program()->setUniformValue(m_color_id, v); + } + + if (state.isMatrixDirty()) + { + program()->setUniformValue(m_matrix_id, state.projectionMatrix()); + } + + program()->setUniformValue(m_mapProjection_id, geoProjection); + + program()->setUniformValue(m_center_id, vecCenter); + program()->setUniformValue(m_center_lowpart_id, vecCenter_lowpart); + program()->setUniformValue(m_wrapOffset_id, float(newMaterial->wrapOffset())); +} + +const char * const *MapPolylineShaderLineStrip::attributeNames() const +{ + static char const *const attr[] = { "vertex", nullptr }; + return attr; +} + +QSGMaterialShader *MapPolylineMaterial::createShader() const +{ + return new MapPolylineShaderLineStrip(); +} + +QSGMaterialType *MapPolylineMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + +int MapPolylineMaterial::compare(const QSGMaterial *other) const +{ + const MapPolylineMaterial &o = *static_cast<const MapPolylineMaterial *>(other); + if (o.m_center == m_center && o.m_geoProjection == m_geoProjection && o.m_wrapOffset == m_wrapOffset && o.m_lineWidth == m_lineWidth) + return QSGFlatColorMaterial::compare(other); + return -1; +} + +const QSGGeometry::AttributeSet &MapPolylineNodeOpenGLExtruded::attributesMapPolylineTriangulated() +{ + return MapPolylineEntry::attributes(); +} + +MapPolylineNodeOpenGLExtruded::MapPolylineNodeOpenGLExtruded() +: m_geometryTriangulating(MapPolylineNodeOpenGLExtruded::attributesMapPolylineTriangulated(), + 0 /* vtx cnt */, 0 /* index cnt */, QSGGeometry::UnsignedIntType /* index type */) +{ + m_geometryTriangulating.setDrawingMode(QSGGeometry::DrawTriangles); + QSGGeometryNode::setMaterial(&fill_material_); + QSGGeometryNode::setGeometry(&m_geometryTriangulating); +} + +MapPolylineNodeOpenGLExtruded::~MapPolylineNodeOpenGLExtruded() +{ + +} + +bool QGeoMapPolylineGeometryOpenGL::allocateAndFillEntries(QSGGeometry *geom, + bool closed, + unsigned int zoom) const +{ + // Select LOD. Generate if not present. Assign it to m_screenVertices; + if (m_dataChanged) { + // it means that the data really changed. + // So synchronously produce LOD 1, and enqueue the requested one if != 0 or 1. + // Select 0 if 0 is requested, or 1 in all other cases. + selectLODOnDataChanged(zoom, m_bboxLeftBoundWrapped.x()); + } else { + // Data has not changed, but active LOD != requested LOD. + // So, if there are no active tasks, try to change to the correct one. + if (!selectLODOnLODMismatch(zoom, m_bboxLeftBoundWrapped.x(), closed)) + return false; + } + + const QVector<QDeclarativeGeoMapItemUtils::vec2> &v = *m_screenVertices; + if (v.size() < 2) { + geom->allocate(0, 0); + return true; + } + const int numSegments = (v.size() - 1); + + const int numIndices = numSegments * 6; // six vertices per line segment + geom->allocate(numIndices); + MapPolylineNodeOpenGLExtruded::MapPolylineEntry *vertices = + static_cast<MapPolylineNodeOpenGLExtruded::MapPolylineEntry *>(geom->vertexData()); + + for (int i = 0; i < numSegments; ++i) { + MapPolylineNodeOpenGLExtruded::MapPolylineEntry e; + const QDeclarativeGeoMapItemUtils::vec2 &cur = v[i]; + const QDeclarativeGeoMapItemUtils::vec2 &next = v[i+1]; + e.triangletype = 1.0; + e.next = next; + e.prev = cur; + e.pos = cur; + e.direction = 1.0; + e.vertextype = -1.0; + vertices[i*6] = e; + e.direction = -1.0; + vertices[i*6+1] = e; + e.pos = next; + e.vertextype = 1.0; + vertices[i*6+2] = e; + + // Second tri + e.triangletype = -1.0; + e.direction = -1.0; + vertices[i*6+3] = e; + e.direction = 1.0; + vertices[i*6+4] = e; + e.pos = cur; + e.vertextype = -1.0; + vertices[i*6+5] = e; + + if (i != 0) { + vertices[i*6].prev = vertices[i*6+1].prev = vertices[i*6+5].prev = v[i-1]; + } else { + if (closed) { + vertices[i*6].prev = vertices[i*6+1].prev = vertices[i*6+5].prev = v[numSegments - 1]; + } else { + vertices[i*6].triangletype = vertices[i*6+1].triangletype = vertices[i*6+5].triangletype = 2.0; + } + } + if (i != numSegments - 1) { + vertices[i*6+2].next = vertices[i*6+3].next = vertices[i*6+4].next = v[i+2]; + } else { + if (closed) { + vertices[i*6+2].next = vertices[i*6+3].next = vertices[i*6+4].next = v[1]; + } else { + vertices[i*6+2].triangletype = vertices[i*6+3].triangletype = vertices[i*6+4].triangletype = 3.0; + } + } + } + return true; +} + +void QGeoMapPolylineGeometryOpenGL::allocateAndFillLineStrip(QSGGeometry *geom, + int lod) const +{ + // Select LOD. Generate if not present. Assign it to m_screenVertices; + Q_UNUSED(lod) + + const QVector<QDeclarativeGeoMapItemUtils::vec2> &vx = *m_screenVertices; + geom->allocate(vx.size()); + + QSGGeometry::Point2D *pts = geom->vertexDataAsPoint2D(); + for (int i = 0; i < vx.size(); ++i) + pts[i].set(vx[i].x, vx[i].y); +} + +void MapPolylineNodeOpenGLExtruded::update(const QColor &fillColor, + const float lineWidth, + const QGeoMapPolylineGeometryOpenGL *shape, + const QMatrix4x4 geoProjection, + const QDoubleVector3D center, + const Qt::PenCapStyle capStyle, + bool closed, + unsigned int zoom) +{ + // shape->size() == number of triangles + if (shape->m_screenVertices->size() < 2 + || lineWidth < 0.5 || fillColor.alpha() == 0) { // number of points + setSubtreeBlocked(true); + return; + } else { + setSubtreeBlocked(false); + } + + QSGGeometry *fill = QSGGeometryNode::geometry(); + if (shape->m_dataChanged || !shape->isLODActive(zoom) || !fill->vertexCount()) { // fill->vertexCount for when node gets destroyed by MapItemBase bcoz of opacity, then recreated. + if (shape->allocateAndFillEntries(fill, closed, zoom)) { + markDirty(DirtyGeometry); + shape->m_dataChanged = false; + } + } + + // Update this +// if (fillColor != fill_material_.color()) + { + fill_material_.setWrapOffset(shape->m_wrapOffset - 1); + fill_material_.setColor(fillColor); + fill_material_.setGeoProjection(geoProjection); + fill_material_.setCenter(center); + fill_material_.setLineWidth(lineWidth); + fill_material_.setMiter(capStyle != Qt::FlatCap); + setMaterial(&fill_material_); + markDirty(DirtyMaterial); + } +} + +MapPolylineShaderExtruded::MapPolylineShaderExtruded() : QSGMaterialShader(*new QSGMaterialShaderPrivate) +{ + +} + +void MapPolylineShaderExtruded::updateState(const QSGMaterialShader::RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); + MapPolylineMaterialExtruded *oldMaterial = static_cast<MapPolylineMaterialExtruded *>(oldEffect); + MapPolylineMaterialExtruded *newMaterial = static_cast<MapPolylineMaterialExtruded *>(newEffect); + + const QColor &c = newMaterial->color(); + const QMatrix4x4 &geoProjection = newMaterial->geoProjection(); + const QDoubleVector3D ¢er = newMaterial->center(); + + QVector3D vecCenter, vecCenter_lowpart; + for (int i = 0; i < 3; i++) + QLocationUtils::split_double(center.get(i), &vecCenter[i], &vecCenter_lowpart[i]); + + if (oldMaterial == nullptr || c != oldMaterial->color() || state.isOpacityDirty()) { + float opacity = state.opacity() * c.alphaF(); + QVector4D v(c.redF() * opacity, + c.greenF() * opacity, + c.blueF() * opacity, + opacity); + program()->setUniformValue(m_color_id, v); + } + + if (state.isMatrixDirty()) + { + program()->setUniformValue(m_matrix_id, state.projectionMatrix()); + } + + // ToDo: dirty-flag all this + program()->setUniformValue(m_mapProjection_id, geoProjection); + + program()->setUniformValue(m_center_id, vecCenter); + program()->setUniformValue(m_center_lowpart_id, vecCenter_lowpart); + program()->setUniformValue(m_miter_id, newMaterial->miter()); + program()->setUniformValue(m_lineWidth_id, newMaterial->lineWidth()); + program()->setUniformValue(m_wrapOffset_id, float(newMaterial->wrapOffset())); + + const QRectF viewportRect = state.viewportRect(); + const float aspect = float(viewportRect.width() / viewportRect.height()); + program()->setUniformValue(m_aspect_id, aspect); +} + +const char * const *MapPolylineShaderExtruded::attributeNames() const +{ + return MapPolylineNodeOpenGLExtruded::MapPolylineEntry::attributeNames(); +} + +QSGMaterialShader *MapPolylineMaterialExtruded::createShader() const +{ + return new MapPolylineShaderExtruded(); +} + +QSGMaterialType *MapPolylineMaterialExtruded::type() const +{ + static QSGMaterialType type; + return &type; +} + +int MapPolylineMaterialExtruded::compare(const QSGMaterial *other) const +{ + const MapPolylineMaterialExtruded &o = *static_cast<const MapPolylineMaterialExtruded *>(other); + if (o.m_miter == m_miter) + return MapPolylineMaterial::compare(other); + return -1; +} + +const char *MapPolylineShaderExtruded::vertexShaderMiteredSegments() const +{ + return + "attribute highp vec4 vertex;\n" + "attribute highp vec4 previous;\n" + "attribute highp vec4 next;\n" + "attribute lowp float direction;\n" + "attribute lowp float triangletype;\n" + "attribute lowp float vertextype;\n" // -1.0 if it is the "left" end of the segment, 1.0 if it is the "right" end. + "\n" + "uniform highp mat4 qt_Matrix;\n" + "uniform highp mat4 mapProjection;\n" + "uniform highp vec3 center;\n" + "uniform highp vec3 center_lowpart;\n" + "uniform lowp float lineWidth;\n" + "uniform lowp float aspect;\n" + "uniform lowp int miter;\n" // currently unused + "uniform lowp vec4 color;\n" + "uniform lowp float wrapOffset;\n" + "\n" + "varying vec4 primitivecolor;\n" + "\n" + " \n" + "vec4 wrapped(in vec4 v) { return vec4(v.x + wrapOffset, v.y, 0.0, 1.0); }\n" + "void main() {\n" // ln 22 + " primitivecolor = color;\n" + " vec2 aspectVec = vec2(aspect, 1.0);\n" + " mat4 projViewModel = qt_Matrix * mapProjection;\n" + " vec4 cur = wrapped(vertex) - vec4(center, 0.0);\n" + " cur = cur - vec4(center_lowpart, 0.0);\n" + " vec4 prev = wrapped(previous) - vec4(center, 0.0);\n" + " prev = prev - vec4(center_lowpart, 0.0);\n" + " vec4 nex = wrapped(next) - vec4(center, 0.0);\n" + " nex = nex - vec4(center_lowpart, 0.0);\n" + "\n" + " vec4 centerProjected = projViewModel * vec4(center, 1.0);\n" + " vec4 previousProjected = projViewModel * prev;\n" + " vec4 currentProjected = projViewModel * cur;\n" + " vec4 nextProjected = projViewModel * nex;\n" + "\n" + " //get 2D screen space with W divide and aspect correction\n" + " vec2 currentScreen = (currentProjected.xy / currentProjected.w) * aspectVec;\n" + " vec2 previousScreen = (previousProjected.xy / previousProjected.w) * aspectVec;\n" + " vec2 nextScreen = (nextProjected.xy / nextProjected.w) * aspectVec;\n" + " float len = (lineWidth);\n" + " float orientation = direction;\n" + " bool clipped = false;\n" + " bool otherEndBelowFrustum = false;\n" + " //starting point uses (next - current)\n" + " vec2 dir = vec2(0.0);\n" + " if (vertextype < 0.0) {\n" + " dir = normalize(nextScreen - currentScreen);\n" + " if (nextProjected.z < 0.0) dir = -dir;\n" + " } else { \n" + " dir = normalize(currentScreen - previousScreen);\n" + " if (previousProjected.z < 0.0) dir = -dir;\n" + " }\n" + // first, clip current, and make sure currentProjected.z is > 0 + " if (currentProjected.z < 0.0) {\n" + " if ((nextProjected.z > 0.0 && vertextype < 0.0) || (vertextype > 0.0 && previousProjected.z > 0.0)) {\n" + " dir = -dir;\n" + " clipped = true;\n" + " if (vertextype < 0.0 && nextProjected.y / nextProjected.w < -1.0) otherEndBelowFrustum = true;\n" + " else if (vertextype > 0.0 && previousProjected.y / previousProjected.w < -1.0) otherEndBelowFrustum = true;\n" + " } else {\n" + " primitivecolor = vec4(0.0,0.0,0.0,0.0);\n" + " gl_Position = vec4(-10000000.0, -1000000000.0, -1000000000.0, 1);\n" // get the vertex out of the way if the segment is fully invisible + " return;\n" + " }\n" + " } else if (triangletype < 2.0) {\n" // vertex in the view, try to miter + " //get directions from (C - B) and (B - A)\n" + " vec2 dirA = normalize((currentScreen - previousScreen));\n" + " if (previousProjected.z < 0.0) dirA = -dirA;\n" + " vec2 dirB = normalize((nextScreen - currentScreen));\n" + " //now compute the miter join normal and length\n" + " if (nextProjected.z < 0.0) dirB = -dirB;\n" + " vec2 tangent = normalize(dirA + dirB);\n" + " vec2 perp = vec2(-dirA.y, dirA.x);\n" + " vec2 vmiter = vec2(-tangent.y, tangent.x);\n" + " len = lineWidth / dot(vmiter, perp);\n" + // The following is an attempt to have a segment-length based miter threshold. + // A mediocre workaround until better mitering will be added. + " float lenTreshold = clamp( min(length((currentProjected.xy - previousProjected.xy) / aspectVec)," + " length((nextProjected.xy - currentProjected.xy) / aspectVec)), 3.0, 6.0 ) * 0.5;\n" + " if (len < lineWidth * lenTreshold && len > -lineWidth * lenTreshold \n" + " ) {\n" + " dir = tangent;\n" + " } else {\n" + " len = lineWidth;\n" + " }\n" + " }\n" + " vec4 offset;\n" + " if (!clipped) {\n" + " vec2 normal = normalize(vec2(-dir.y, dir.x));\n" + " normal *= len;\n" // fracZL apparently was needed before the (-2.0 / qt_Matrix[1][1]) factor was introduced + " normal /= aspectVec;\n" // straighten the normal up again + " float scaleFactor = currentProjected.w / centerProjected.w;\n" + " offset = vec4(normal * orientation * scaleFactor * (centerProjected.w / (-2.0 / qt_Matrix[1][1])), 0.0, 0.0);\n" // ToDo: figure out why (-2.0 / qt_Matrix[1][1]), that is empirically what works + " gl_Position = currentProjected + offset;\n" + " } else {\n" + " if (otherEndBelowFrustum) offset = vec4((dir * 1.0) / aspectVec, 0.0, 0.0);\n" // the if is necessary otherwise it seems the direction vector still flips in some obscure cases. + " else offset = vec4((dir * 500000000000.0) / aspectVec, 0.0, 0.0);\n" // Hack alert: just 1 triangle, long enough to look like a rectangle. + " if (vertextype < 0.0) gl_Position = nextProjected - offset; else gl_Position = previousProjected + offset;\n" + " }\n" + "}\n"; +} + +QVector<QDeclarativeGeoMapItemUtils::vec2> QGeoMapItemLODGeometry::getSimplified( + QVector<QDeclarativeGeoMapItemUtils::vec2> &wrappedPath, // reference as it gets copied in the nested call + double leftBoundWrapped, + unsigned int zoom) +{ + // Try a simplify step + QList<QDoubleVector2D> data; + for (auto e: wrappedPath) + data << e.toDoubleVector2D(); + const QList<QDoubleVector2D> simplified = QGeoSimplify::geoSimplifyZL(data, + leftBoundWrapped, + zoom); + + data.clear(); + QVector<QDeclarativeGeoMapItemUtils::vec2> simple; + for (auto e: simplified) + simple << e; + return simple; +} + + +bool QGeoMapItemLODGeometry::isLODActive(unsigned int lod) const +{ + return m_screenVertices == m_verticesLOD[zoomToLOD(lod)].data(); +} + +class PolylineSimplifyTask : public QRunnable +{ +public: + PolylineSimplifyTask(const QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &input, // reference as it gets copied in the nested call + const QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &output, + double leftBound, + unsigned int zoom, + QSharedPointer<unsigned int> &working) + : m_zoom(zoom) + , m_leftBound(leftBound) + , m_input(input) + , m_output(output) + , m_working(working) + { + Q_ASSERT(!input.isNull()); + Q_ASSERT(!output.isNull()); + } + + ~PolylineSimplifyTask() override; + + void run() override + { + // Skip sending notifications for now. Updated data will be picked up eventually. + // ToDo: figure out how to connect a signal from here to a slot in the item. + *m_working = QGeoMapPolylineGeometryOpenGL::zoomToLOD(m_zoom); + const QVector<QDeclarativeGeoMapItemUtils::vec2> res = + QGeoMapPolylineGeometryOpenGL::getSimplified( *m_input, + m_leftBound, + QGeoMapPolylineGeometryOpenGL::zoomForLOD(m_zoom)); + *m_output = res; + *m_working = 0; + } + + unsigned int m_zoom; + double m_leftBound; + QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > m_input, m_output; + QSharedPointer<unsigned int> m_working; +}; + +void QGeoMapItemLODGeometry::enqueueSimplificationTask(const QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &input, + const QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &output, + double leftBound, + unsigned int zoom, + QSharedPointer<unsigned int> &working) +{ + Q_ASSERT(!input.isNull()); + Q_ASSERT(!output.isNull()); + PolylineSimplifyTask *task = new PolylineSimplifyTask(input, + output, + leftBound, + zoom, + working); + threadPool->start(task); +} + +PolylineSimplifyTask::~PolylineSimplifyTask() {} + +void QGeoMapItemLODGeometry::selectLOD(unsigned int zoom, double leftBound, bool /* closed */) // closed to tell if this is a polygon or a polyline. +{ + unsigned int requestedLod = zoomToLOD(zoom); + if (!m_verticesLOD[requestedLod].isNull()) { + m_screenVertices = m_verticesLOD[requestedLod].data(); + } else if (!m_verticesLOD.at(0)->isEmpty()) { + // if here, zoomToLOD != 0 and no current working task. + // So select the last filled LOD != m_working (lower-bounded by 1, + // guaranteed to exist), and enqueue the right one + m_verticesLOD[requestedLod] = QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2>>( + new QVector<QDeclarativeGeoMapItemUtils::vec2>); + + for (unsigned int i = requestedLod - 1; i >= 1; i--) { + if (*m_working != i && !m_verticesLOD[i].isNull()) { + m_screenVertices = m_verticesLOD[i].data(); + break; + } else if (i == 1) { + // get 1 synchronously if not computed already + m_verticesLOD[1] = QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2>>( + new QVector<QDeclarativeGeoMapItemUtils::vec2>); + *m_verticesLOD[1] = getSimplified( *m_verticesLOD[0], + leftBound, + zoomForLOD(0)); + if (requestedLod == 1) + return; + } + } + + enqueueSimplificationTask( m_verticesLOD.at(0), + m_verticesLOD[requestedLod], + leftBound, + zoom, + m_working); + + } +} + +void QGeoMapItemLODGeometry::selectLODOnDataChanged(unsigned int zoom, double leftBound) const +{ + unsigned int lod = zoomToLOD(zoom); + if (lod > 0) { + // Generate ZL 1 as fallback for all cases != 0. Do not do if 0 is requested + // (= old behavior, LOD disabled) + m_verticesLOD[1] = QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2>>( + new QVector<QDeclarativeGeoMapItemUtils::vec2>); + *m_verticesLOD[1] = getSimplified( *m_verticesLOD[0], + leftBound, + zoomForLOD(0)); + } + if (lod > 1) { + if (!m_verticesLOD[lod]) + m_verticesLOD[lod] = QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2>>( + new QVector<QDeclarativeGeoMapItemUtils::vec2>); + enqueueSimplificationTask( m_verticesLOD.at(0), + m_verticesLOD[lod], + leftBound, + zoom, + m_working); + } + m_screenVertices = m_verticesLOD[qMin<unsigned int>(lod, 1)].data(); // return only 0,1 synchronously +} + +unsigned int QGeoMapItemLODGeometry::zoomToLOD(unsigned int zoom) +{ + unsigned int res; + if (zoom > 20) + res = 0; + else + res = qBound<unsigned int>(3, zoom, 20) / 3; // bound LOD'ing between ZL 3 and 20. Every 3 ZoomLevels + return res; +} + +unsigned int QGeoMapItemLODGeometry::zoomForLOD(unsigned int zoom) +{ + unsigned int res = (qBound<unsigned int>(3, zoom, 20) / 3) * 3; + if (zoom < 6) + return res; + return res + 1; // give more resolution when closing in +} + QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativepolylinemapitem_p.h b/src/location/declarativemaps/qdeclarativepolylinemapitem_p.h index 3aa0f96b..9cd20ea5 100644 --- a/src/location/declarativemaps/qdeclarativepolylinemapitem_p.h +++ b/src/location/declarativemaps/qdeclarativepolylinemapitem_p.h @@ -59,8 +59,6 @@ QT_BEGIN_NAMESPACE -class MapPolylineNode; - class Q_LOCATION_PRIVATE_EXPORT QDeclarativeMapLineProperties : public QObject { Q_OBJECT @@ -86,53 +84,23 @@ private: QColor color_; }; -class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometry : public QGeoMapItemGeometry -{ -public: - QGeoMapPolylineGeometry(); - - void updateSourcePoints(const QGeoMap &map, - const QList<QDoubleVector2D> &path, - const QGeoCoordinate geoLeftBound); - - void updateScreenPoints(const QGeoMap &map, - qreal strokeWidth, - bool adjustTranslation = true); - - void clearSource(); - - bool contains(const QPointF &point) const override; - - QList<QList<QDoubleVector2D> > clipPath(const QGeoMap &map, - const QList<QDoubleVector2D> &path, - QDoubleVector2D &leftBoundWrapped); - - void pathToScreen(const QGeoMap &map, - const QList<QList<QDoubleVector2D> > &clippedPaths, - const QDoubleVector2D &leftBoundWrapped); - -public: - QVector<qreal> srcPoints_; - QVector<QPainterPath::ElementType> srcPointTypes_; - -#ifdef QT_LOCATION_DEBUG - QList<QDoubleVector2D> m_wrappedPath; - QList<QList<QDoubleVector2D>> m_clippedPaths; -#endif - - friend class QDeclarativeCircleMapItem; - friend class QDeclarativePolygonMapItem; - friend class QDeclarativeRectangleMapItem; -}; - +class QDeclarativePolylineMapItemPrivate; class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItem : public QDeclarativeGeoMapItemBase { Q_OBJECT + Q_ENUMS(Backend) Q_PROPERTY(QJSValue path READ path WRITE setPath NOTIFY pathChanged) Q_PROPERTY(QDeclarativeMapLineProperties *line READ line CONSTANT) + Q_PROPERTY(Backend backend READ backend WRITE setBackend NOTIFY backendChanged REVISION 15) public: + enum Backend { + Software = 0, + OpenGLLineStrip = 1, + OpenGLExtruded = 2, + }; + explicit QDeclarativePolylineMapItem(QQuickItem *parent = 0); ~QDeclarativePolylineMapItem(); @@ -159,70 +127,42 @@ public: QDeclarativeMapLineProperties *line(); + Backend backend() const; + void setBackend(Backend b); + Q_SIGNALS: void pathChanged(); - -protected: - void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; - void setPathFromGeoList(const QList<QGeoCoordinate> &path); - void updatePolish() override; + void backendChanged(); protected Q_SLOTS: void markSourceDirtyAndUpdate(); void updateAfterLinePropertiesChanged(); virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) override; -private: - void regenerateCache(); - void updateCache(); +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void setPathFromGeoList(const QList<QGeoCoordinate> &path); + void updatePolish() override; + void componentComplete() override; + void updateLineStyleParameter(QGeoMapParameter *p, const char *propertyName); + void updateLineStyleParameter(QGeoMapParameter *p, const char *propertyName, bool update); #ifdef QT_LOCATION_DEBUG public: #endif - QGeoPath geopath_; - QList<QDoubleVector2D> geopathProjected_; - QDeclarativeMapLineProperties line_; - QColor color_; - bool dirtyMaterial_; - QGeoMapPolylineGeometry geometry_; - bool updatingGeometry_; -}; + QGeoPath m_geopath; + QDeclarativeMapLineProperties m_line; -////////////////////////////////////////////////////////////////////// - -class Q_LOCATION_PRIVATE_EXPORT VisibleNode -{ -public: - VisibleNode(); - virtual ~VisibleNode(); + Backend m_backend = Software; + bool m_dirtyMaterial; + bool m_updatingGeometry; - bool subtreeBlocked() const; - void setSubtreeBlocked(bool blocked); - bool visible() const; - void setVisible(bool visible); + QScopedPointer<QDeclarativePolylineMapItemPrivate> m_d; - bool m_blocked : 1; - bool m_visible : 1; -}; - -class Q_LOCATION_PRIVATE_EXPORT MapItemGeometryNode : public QSGGeometryNode, public VisibleNode -{ -public: - ~MapItemGeometryNode() override; - bool isSubtreeBlocked() const override; -}; - -class Q_LOCATION_PRIVATE_EXPORT MapPolylineNode : public MapItemGeometryNode -{ -public: - MapPolylineNode(); - ~MapPolylineNode() override; - - void update(const QColor &fillColor, const QGeoMapItemGeometry *shape); - -private: - QSGFlatColorMaterial fill_material_; - QSGGeometry geometry_; + friend class QDeclarativePolylineMapItemPrivate; + friend class QDeclarativePolylineMapItemPrivateCPU; + friend class QDeclarativePolylineMapItemPrivateOpenGLLineStrip; + friend class QDeclarativePolylineMapItemPrivateOpenGLExtruded; }; QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativepolylinemapitem_p_p.h b/src/location/declarativemaps/qdeclarativepolylinemapitem_p_p.h new file mode 100644 index 00000000..2a588222 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativepolylinemapitem_p_p.h @@ -0,0 +1,890 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Paolo Angelelli <paolo.angelelli@gmail.com> +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPOLYLINEMAPITEM_P_P_H +#define QDECLARATIVEPOLYLINEMAPITEM_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativepolylinemapitem_p.h> +#include <QtLocation/private/qdeclarativegeomapitemutils_p.h> +#include <QtLocation/private/qdeclarativepolylinemapitem_p.h> +#include <QtLocation/private/qgeomapitemgeometry_p.h> +#include <QSGGeometryNode> +#include <QSGFlatColorMaterial> +#include <QtPositioning/QGeoPath> +#include <QtPositioning/QGeoPolygon> +#include <QtPositioning/QGeoRectangle> +#include <QtPositioning/QGeoCircle> +#include <QtPositioning/private/qdoublevector2d_p.h> +#include <QtCore/QScopedValueRollback> +#include <QSharedPointer> +#include <array> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometry : public QGeoMapItemGeometry +{ +public: + QGeoMapPolylineGeometry(); + + void updateSourcePoints(const QGeoMap &map, + const QList<QDoubleVector2D> &path, + const QGeoCoordinate geoLeftBound); + + void updateScreenPoints(const QGeoMap &map, + qreal strokeWidth, + bool adjustTranslation = true); + + void clearSource(); + + bool contains(const QPointF &point) const override; + + QList<QList<QDoubleVector2D> > clipPath(const QGeoMap &map, + const QList<QDoubleVector2D> &path, + QDoubleVector2D &leftBoundWrapped); + + void pathToScreen(const QGeoMap &map, + const QList<QList<QDoubleVector2D> > &clippedPaths, + const QDoubleVector2D &leftBoundWrapped); + +public: + QVector<qreal> srcPoints_; + QVector<QPainterPath::ElementType> srcPointTypes_; + +#ifdef QT_LOCATION_DEBUG + QList<QDoubleVector2D> m_wrappedPath; + QList<QList<QDoubleVector2D>> m_clippedPaths; +#endif + + friend class QDeclarativeCircleMapItem; + friend class QDeclarativePolygonMapItem; + friend class QDeclarativeRectangleMapItem; +}; + +class Q_LOCATION_PRIVATE_EXPORT VisibleNode +{ +public: + VisibleNode(); + virtual ~VisibleNode(); + + bool subtreeBlocked() const; + void setSubtreeBlocked(bool blocked); + bool visible() const; + void setVisible(bool visible); + + bool m_blocked : 1; + bool m_visible : 1; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapItemGeometryNode : public QSGGeometryNode, public VisibleNode +{ +public: + ~MapItemGeometryNode() override; + bool isSubtreeBlocked() const override; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapPolylineMaterial : public QSGFlatColorMaterial +{ +public: + MapPolylineMaterial() + : QSGFlatColorMaterial() + { + // Passing RequiresFullMatrix is essential in order to prevent the + // batch renderer from baking in simple, translate-only transforms into + // the vertex data. The shader will rely on the fact that + // vertexCoord.xy is the Shape-space coordinate and so no modifications + // are welcome. + setFlag(Blending | RequiresFullMatrix | CustomCompileStep); + } + + QSGMaterialShader *createShader() const override; + + void setGeoProjection(const QMatrix4x4 &p) + { + m_geoProjection = p; + } + + QMatrix4x4 geoProjection() const + { + return m_geoProjection; + } + + void setCenter(const QDoubleVector3D &c) + { + m_center = c; + } + + QDoubleVector3D center() const + { + return m_center; + } + + void setColor(const QColor &color) + { + QSGFlatColorMaterial::setColor(color); + setFlag(Blending, true); // ToDo: Needed only temporarily, can be removed after debugging + } + + int wrapOffset() const + { + return m_wrapOffset; + } + + void setWrapOffset(int wrapOffset) + { + m_wrapOffset = wrapOffset; + } + + void setLineWidth(const float lw) + { + m_lineWidth = lw; + } + + float lineWidth() const + { + return m_lineWidth; + } + + QSGMaterialType *type() const override; + int compare(const QSGMaterial *other) const override; + +protected: + QMatrix4x4 m_geoProjection; + QDoubleVector3D m_center; + int m_wrapOffset = 0; + float m_lineWidth = 1.0; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapPolylineNode : public MapItemGeometryNode +{ +public: + MapPolylineNode(); + ~MapPolylineNode() override; + + void update(const QColor &fillColor, const QGeoMapItemGeometry *shape); + +protected: + QSGFlatColorMaterial fill_material_; + QSGGeometry geometry_; +}; + +class Q_LOCATION_PRIVATE_EXPORT 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(const QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &input, // reference as it gets copied in the nested call + const QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &output, + double leftBound, + unsigned int zoom, + QSharedPointer<unsigned int> &working); + + void selectLODOnDataChanged(unsigned int zoom, double leftBound) const; + + 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() + { + m_working = QSharedPointer<unsigned int>(new unsigned int(0)); + } + + void updateSourcePoints(const QGeoMap &map, + const QGeoPolygon &poly); + + void updateSourcePoints(const QGeoMap &map, + const QGeoPath &poly); + + void updateSourcePoints(const QGeoProjectionWebMercator &p, + const QList<QDoubleVector2D> &wrappedPath, + const QGeoRectangle &boundingRectangle); + + void updateSourcePoints(const QGeoMap &map, + const QGeoRectangle &rect); + + void updateSourcePoints(const QGeoMap &map, + const QGeoCircle &circle); + + void updateScreenPoints(const QGeoMap &map, + qreal strokeWidth, + bool adjustTranslation = true); + + void updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth = 0.0); + + 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 + { + Q_UNUSED(point) + return false; + } + + static double distanceTo(const QDoubleVector2D &a, const QDoubleVector2D &b, const QDoubleVector2D &p) + { + double u = ((p.x() - a.x()) * (b.x() - a.x()) + (p.y() - a.y()) * (b.y() - a.y()) ) / (b - a).lengthSquared(); + QDoubleVector2D intersection(a.x() + u * (b.x() - a.x()) , a.y() + u * (b.y() - a.y()) ); + + QDoubleVector2D candidate = ( (p-a).length() < (p-b).length() ) ? a : b; + + if (u > 0 && u < 1 + && (p-intersection).length() < (p-candidate).length() ) // And it falls in the segment + candidate = intersection; + + return qAbs((candidate - p).length()); + } + // Note: this is also slightly incorrect on joins and in the beginning/end of the line + bool contains(const QPointF &point, qreal lineWidth, const QGeoProjectionWebMercator &p) const + { + const double lineHalfWidth = lineWidth * 0.5; + const QDoubleVector2D pt(point); + QDoubleVector2D a; + if (m_screenVertices->size()) + a = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(m_screenVertices->first().toDoubleVector2D())); + QDoubleVector2D b; + for (int i = 1; i < m_screenVertices->size(); ++i) + { + if (!a.isFinite()) { + a = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(m_screenVertices->at(i).toDoubleVector2D())); + continue; + } + + b = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(m_screenVertices->at(i).toDoubleVector2D())); + if (!b.isFinite()) { + a = b; + continue; + } + + if (b == a) + continue; + + // Heavily simplifying it here: if a point is not projectable, skip the segment. + // For a correct solution, the segment should be clipped instead. + if (distanceTo(a, b, pt) <= lineHalfWidth) + return true; + + a = b; + } + return false; + } + +public: + QDoubleVector2D m_bboxLeftBoundWrapped; + QVector<WrappedPolyline> m_wrappedPolygons; + int m_wrapOffset; + + friend class QDeclarativeCircleMapItem; + friend class QDeclarativePolygonMapItem; + friend class QDeclarativeRectangleMapItem; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapPolylineShaderLineStrip : public QSGMaterialShader +{ +public: + MapPolylineShaderLineStrip(); + + const char *vertexShader() const override { + return + "attribute highp vec4 vertex; \n" + "uniform highp mat4 qt_Matrix; \n" + "uniform highp mat4 mapProjection; \n" + "uniform highp vec3 center; \n" + "uniform highp vec3 center_lowpart; \n" + "uniform lowp float wrapOffset; \n" + "vec4 wrapped(in vec4 v) { return vec4(v.x + wrapOffset, v.y, 0.0, 1.0); }\n" + "void main() { \n" + " vec4 vtx = wrapped(vertex) - vec4(center, 0.0); \n" + " vtx = vtx - vec4(center_lowpart, 0.0); \n" + " gl_Position = qt_Matrix * mapProjection * vtx; \n" + "}"; + } + + const char *fragmentShader() const { + return + "uniform lowp vec4 color; \n" + "void main() { \n" + " gl_FragColor = color; \n" + "}"; + } + + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; + +protected: + void initialize() override + { + m_matrix_id = program()->uniformLocation("qt_Matrix"); + m_color_id = program()->uniformLocation("color"); + m_mapProjection_id = program()->uniformLocation("mapProjection"); + m_center_id = program()->uniformLocation("center"); + m_center_lowpart_id = program()->uniformLocation("center_lowpart"); + m_wrapOffset_id = program()->uniformLocation("wrapOffset"); + } + int m_center_id; + int m_center_lowpart_id; + int m_mapProjection_id; + int m_matrix_id; + int m_color_id; + int m_wrapOffset_id; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapPolylineShaderExtruded : public QSGMaterialShader +{ +public: + MapPolylineShaderExtruded(); + + // Heavily adapted from https://github.com/mattdesl/webgl-lines/blob/master/projected/vert.glsl, + // that is (c) Matt DesLauriers, and released under the MIT license. + const char *vertexShaderMiteredSegments() const; + + const char *vertexShader() const override + { + return vertexShaderMiteredSegments(); + } + + const char *fragmentShader() const override + { + return + "varying vec4 primitivecolor; \n" + "void main() { \n" + " gl_FragColor = primitivecolor; \n" + "}"; + } + + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; + +protected: + void initialize() override + { + m_matrix_id = program()->uniformLocation("qt_Matrix"); + m_color_id = program()->uniformLocation("color"); + m_mapProjection_id = program()->uniformLocation("mapProjection"); + m_center_id = program()->uniformLocation("center"); + m_center_lowpart_id = program()->uniformLocation("center_lowpart"); + m_lineWidth_id = program()->uniformLocation("lineWidth"); + m_aspect_id = program()->uniformLocation("aspect"); + m_miter_id = program()->uniformLocation("miter"); + m_wrapOffset_id = program()->uniformLocation("wrapOffset"); + } + int m_center_id; + int m_center_lowpart_id; + int m_mapProjection_id; + int m_matrix_id; + int m_color_id; + int m_lineWidth_id; + int m_aspect_id; + int m_miter_id; + int m_wrapOffset_id; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapPolylineNodeOpenGLLineStrip : public MapItemGeometryNode +{ +public: + MapPolylineNodeOpenGLLineStrip(); + ~MapPolylineNodeOpenGLLineStrip() override; + + void update(const QColor &fillColor, + const qreal lineWidth, + const QGeoMapPolylineGeometryOpenGL *shape, + const QMatrix4x4 &geoProjection, + const QDoubleVector3D ¢er, + const Qt::PenCapStyle capStyle = Qt::SquareCap); + +protected: + MapPolylineMaterial fill_material_; + QSGGeometry geometry_; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapPolylineMaterialExtruded : public MapPolylineMaterial +{ +public: + MapPolylineMaterialExtruded() : MapPolylineMaterial() + { + + } + QSGMaterialShader *createShader() const override; + + void setMiter(const int m) + { + m_miter = m; + } + + int miter() const + { + return m_miter; + } + + QSGMaterialType *type() const override; + int compare(const QSGMaterial *other) const override; + + int m_miter = 0; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapPolylineNodeOpenGLExtruded : public MapItemGeometryNode +{ +public: + + typedef struct { + QDeclarativeGeoMapItemUtils::vec2 pos; + QDeclarativeGeoMapItemUtils::vec2 prev; + QDeclarativeGeoMapItemUtils::vec2 next; + float direction; + float triangletype; // es2 does not support int attribs + float vertextype; + + static const char * const *attributeNames() + { + static char const *const attr[] = { "vertex", "previous", "next", "direction", "triangletype", "vertextype", nullptr }; + return attr; + } + static const QSGGeometry::AttributeSet &attributes() + { + static const QSGGeometry::Attribute dataTri[] = { + QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute) // pos + ,QSGGeometry::Attribute::createWithAttributeType(1, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute) // next + ,QSGGeometry::Attribute::createWithAttributeType(2, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute) // previous + ,QSGGeometry::Attribute::createWithAttributeType(3, 1, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute) // direction + ,QSGGeometry::Attribute::createWithAttributeType(4, 1, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute) // triangletype + ,QSGGeometry::Attribute::createWithAttributeType(5, 1, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute) // vertextype + }; + static const QSGGeometry::AttributeSet attrsTri = { 6, sizeof(MapPolylineNodeOpenGLExtruded::MapPolylineEntry), dataTri }; + return attrsTri; + } + } MapPolylineEntry; + + MapPolylineNodeOpenGLExtruded(); + ~MapPolylineNodeOpenGLExtruded() override; + + void update(const QColor &fillColor, + const float lineWidth, + const QGeoMapPolylineGeometryOpenGL *shape, + const QMatrix4x4 geoProjection, + const QDoubleVector3D center, + const Qt::PenCapStyle capStyle = Qt::FlatCap, + bool closed = false, + unsigned int zoom = 30); + + static const QSGGeometry::AttributeSet &attributesMapPolylineTriangulated(); + +protected: + MapPolylineMaterialExtruded fill_material_; + QSGGeometry m_geometryTriangulating; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivate +{ +public: + QDeclarativePolylineMapItemPrivate(QDeclarativePolylineMapItem &poly) : m_poly(poly) + { + + } + QDeclarativePolylineMapItemPrivate(QDeclarativePolylineMapItemPrivate &other) : m_poly(other.m_poly) + { + } + + virtual ~QDeclarativePolylineMapItemPrivate(); + virtual void markSourceDirtyAndUpdate() = 0; + virtual void onMapSet() = 0; + virtual void onLinePropertiesChanged() = 0; + virtual void onGeoGeometryChanged() = 0; + virtual void onGeoGeometryUpdated() = 0; + virtual void onItemGeometryChanged() = 0; + virtual void updatePolish() = 0; + virtual void afterViewportChanged() = 0; + virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) = 0; + virtual bool contains(const QPointF &point) const = 0; + + QDeclarativePolylineMapItem &m_poly; + Qt::PenStyle m_penStyle = Qt::SolidLine; + Qt::PenCapStyle m_penCapStyle = Qt::SquareCap; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivateCPU: public QDeclarativePolylineMapItemPrivate +{ +public: + QDeclarativePolylineMapItemPrivateCPU(QDeclarativePolylineMapItem &poly) : QDeclarativePolylineMapItemPrivate(poly) + { + } + + QDeclarativePolylineMapItemPrivateCPU(QDeclarativePolylineMapItemPrivate &other) + : QDeclarativePolylineMapItemPrivate(other) + { + } + + ~QDeclarativePolylineMapItemPrivateCPU() override; + void onLinePropertiesChanged() override + { + // mark dirty just in case we're a width change + markSourceDirtyAndUpdate(); + } + void markSourceDirtyAndUpdate() override + { + m_geometry.markSourceDirty(); + m_poly.polishAndUpdate(); + } + void regenerateCache() + { + if (!m_poly.map() || m_poly.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection()); + m_geopathProjected.clear(); + m_geopathProjected.reserve(m_poly.m_geopath.size()); + for (const QGeoCoordinate &c : m_poly.m_geopath.path()) + m_geopathProjected << p.geoToMapProjection(c); + } + void updateCache() + { + if (!m_poly.map() || m_poly.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection()); + m_geopathProjected << p.geoToMapProjection(m_poly.m_geopath.path().last()); + } + void preserveGeometry() + { + m_geometry.setPreserveGeometry(true, m_poly.m_geopath.boundingGeoRectangle().topLeft()); + } + void afterViewportChanged() override + { + // preserveGeometry is cleared in updateMapItemPaintNode + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + void onMapSet() override + { + regenerateCache(); + markSourceDirtyAndUpdate(); + } + void onGeoGeometryChanged() override + { + regenerateCache(); + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + void onGeoGeometryUpdated() override + { + updateCache(); + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + void onItemGeometryChanged() override + { + onGeoGeometryChanged(); + } + void updatePolish() override + { + if (m_poly.m_geopath.path().length() < 2) { // Possibly cleared + m_geometry.clear(); + m_poly.setWidth(0); + m_poly.setHeight(0); + return; + } + QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry); + m_poly.m_updatingGeometry = true; + + const QGeoMap *map = m_poly.map(); + const qreal borderWidth = m_poly.m_line.width(); + + m_geometry.updateSourcePoints(*map, m_geopathProjected, m_poly.m_geopath.boundingGeoRectangle().topLeft()); + m_geometry.updateScreenPoints(*map, borderWidth); + + m_poly.setWidth(m_geometry.sourceBoundingBox().width() + borderWidth); + m_poly.setHeight(m_geometry.sourceBoundingBox().height() + borderWidth); + + m_poly.setPositionOnMap(m_geometry.origin(), -1 * m_geometry.sourceBoundingBox().topLeft() + + QPointF(borderWidth, borderWidth) * 0.5 ); // it has to be shifted so that the center of the line is on the correct geocoord + } + QSGNode *updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData * /*data*/) override + { + if (!m_node || !oldNode) { + m_node = new MapPolylineNode(); + if (oldNode) { + delete oldNode; + oldNode = nullptr; + } + } else { + m_node = static_cast<MapPolylineNode *>(oldNode); + } + + //TODO: update only material + if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial || !oldNode) { + m_node->update(m_poly.m_line.color(), &m_geometry); + m_geometry.setPreserveGeometry(false); + m_geometry.markClean(); + m_poly.m_dirtyMaterial = false; + } + return m_node; + } + bool contains(const QPointF &point) const override + { + return m_geometry.contains(point); + } + + QList<QDoubleVector2D> m_geopathProjected; + QGeoMapPolylineGeometry m_geometry; + MapPolylineNode *m_node = nullptr; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivateOpenGLLineStrip: public QDeclarativePolylineMapItemPrivate +{ +public: + + QDeclarativePolylineMapItemPrivateOpenGLLineStrip(QDeclarativePolylineMapItem &poly) : QDeclarativePolylineMapItemPrivate(poly) + { + } + + QDeclarativePolylineMapItemPrivateOpenGLLineStrip(QDeclarativePolylineMapItemPrivate &other) + : QDeclarativePolylineMapItemPrivate(other) + { + } + + ~QDeclarativePolylineMapItemPrivateOpenGLLineStrip() override; + void onLinePropertiesChanged() override + { + afterViewportChanged(); + } + void markSourceDirtyAndUpdate() override + { + m_geometry.markSourceDirty(); + m_poly.polishAndUpdate(); + } + void preserveGeometry() + { + m_geometry.setPreserveGeometry(true, m_poly.m_geopath.boundingGeoRectangle().topLeft()); + } + void onMapSet() override + { + markSourceDirtyAndUpdate(); + } + void onGeoGeometryChanged() override + { + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + void onGeoGeometryUpdated() override + { + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + void onItemGeometryChanged() override + { + onGeoGeometryChanged(); + } + void afterViewportChanged() override + { + preserveGeometry(); + m_poly.polishAndUpdate(); + } + bool contains(const QPointF &point) const override + { + return m_geometry.contains(m_poly.mapToItem(m_poly.quickMap(), point), + m_poly.line()->width(), + static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection())); + } + void updatePolish() override + { + if (m_poly.m_geopath.path().length() == 0) { // Possibly cleared + m_geometry.clear(); + m_geometry.clear(); + m_poly.setWidth(0); + m_poly.setHeight(0); + return; + } + + QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry); + m_poly.m_updatingGeometry = true; + const qreal lineWidth = m_poly.m_line.width(); + m_geometry.updateSourcePoints(*m_poly.map(), m_poly.m_geopath); + m_geometry.markScreenDirty(); + m_geometry.updateScreenPoints(*m_poly.map(), lineWidth); + + m_poly.setWidth(m_geometry.sourceBoundingBox().width()); + m_poly.setHeight(m_geometry.sourceBoundingBox().height()); + m_poly.setPosition(1.0 * m_geometry.firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5)); + } + QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override + { + Q_UNUSED(data); + + if (!m_node || !oldNode) { + m_node = new MapPolylineNodeOpenGLLineStrip(); + if (oldNode) + delete oldNode; + } else { + m_node = static_cast<MapPolylineNodeOpenGLLineStrip *>(oldNode); + } + + if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial) { + const QGeoMap *map = m_poly.map(); + const QMatrix4x4 &combinedMatrix = map->geoProjection().qsgTransform(); + const QDoubleVector3D &cameraCenter = map->geoProjection().centerMercator(); + m_node->update(m_poly.m_line.color(), // This updates only the material if the geometry is unchanged + m_poly.m_line.width(), + &m_geometry, + combinedMatrix, + cameraCenter); + m_geometry.setPreserveGeometry(false); + m_geometry.markClean(); + m_poly.m_dirtyMaterial = false; + } + return m_node; + } + + QGeoMapPolylineGeometryOpenGL m_geometry; + MapPolylineNodeOpenGLLineStrip *m_node = nullptr; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivateOpenGLExtruded: public QDeclarativePolylineMapItemPrivateOpenGLLineStrip +{ +public: + + QDeclarativePolylineMapItemPrivateOpenGLExtruded(QDeclarativePolylineMapItem &poly) + : QDeclarativePolylineMapItemPrivateOpenGLLineStrip(poly) + { + } + + QDeclarativePolylineMapItemPrivateOpenGLExtruded(QDeclarativePolylineMapItemPrivate &other) + : QDeclarativePolylineMapItemPrivateOpenGLLineStrip(other) + { + } + + ~QDeclarativePolylineMapItemPrivateOpenGLExtruded() override; + + QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override + { + Q_UNUSED(data); + const QGeoMap *map = m_poly.map(); + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map->geoProjection()); + const QMatrix4x4 &combinedMatrix = p.qsgTransform(); + const QDoubleVector3D &cameraCenter = p.centerMercator(); + const QColor &color = m_poly.m_line.color(); + const float lineWidth = m_poly.m_line.width(); + + MapPolylineNodeOpenGLExtruded *nodeTri = nullptr; + if (!m_nodeTri || !oldNode) { + if (oldNode) + delete oldNode; + nodeTri = new MapPolylineNodeOpenGLExtruded(); + } else { + nodeTri = static_cast<MapPolylineNodeOpenGLExtruded *>(oldNode); + } + + //TODO: update only material + if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial) { + nodeTri->update(color, + lineWidth , + &m_geometry, + combinedMatrix, + cameraCenter, + m_penCapStyle, + false, + m_poly.zoomForLOD(int(map->cameraData().zoomLevel()))); + m_geometry.setPreserveGeometry(false); + m_geometry.markClean(); + m_poly.m_dirtyMaterial = false; + } + m_nodeTri = nodeTri; + return nodeTri; + } + + MapPolylineNodeOpenGLExtruded *m_nodeTri = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEPOLYLINEMAPITEM_P_P_H diff --git a/src/location/declarativemaps/qdeclarativerectanglemapitem.cpp b/src/location/declarativemaps/qdeclarativerectanglemapitem.cpp index fd4109a7..74d2cc13 100644 --- a/src/location/declarativemaps/qdeclarativerectanglemapitem.cpp +++ b/src/location/declarativemaps/qdeclarativerectanglemapitem.cpp @@ -35,6 +35,7 @@ ****************************************************************************/ #include "qdeclarativerectanglemapitem_p.h" +#include "qdeclarativerectanglemapitem_p_p.h" #include "qdeclarativepolygonmapitem_p.h" #include "qlocationutils_p.h" #include <QPainterPath> @@ -125,16 +126,30 @@ QT_BEGIN_NAMESPACE \since 5.14 */ +struct RectangleBackendSelector +{ + RectangleBackendSelector() + { + backend = (qgetenv("QTLOCATION_OPENGL_ITEMS").toInt()) ? QDeclarativeRectangleMapItem::OpenGL : QDeclarativeRectangleMapItem::Software; + } + QDeclarativeRectangleMapItem::Backend backend = QDeclarativeRectangleMapItem::Software; +}; + +Q_GLOBAL_STATIC(RectangleBackendSelector, mapRectangleBackendSelector) + QDeclarativeRectangleMapItem::QDeclarativeRectangleMapItem(QQuickItem *parent) -: QDeclarativeGeoMapItemBase(parent), border_(this), color_(Qt::transparent), dirtyMaterial_(true), - updatingGeometry_(false) +: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent), m_dirtyMaterial(true), + m_updatingGeometry(false) + , m_d(new QDeclarativeRectangleMapItemPrivateCPU(*this)) { + // ToDo: handle envvar, and switch implementation. m_itemType = QGeoMap::MapRectangle; setFlag(ItemHasContents, true); - QObject::connect(&border_, SIGNAL(colorChanged(QColor)), - this, SLOT(markSourceDirtyAndUpdate())); - QObject::connect(&border_, SIGNAL(widthChanged(qreal)), - this, SLOT(markSourceDirtyAndUpdate())); + QObject::connect(&m_border, SIGNAL(colorChanged(QColor)), + this, SLOT(onLinePropertiesChanged())); + QObject::connect(&m_border, SIGNAL(widthChanged(qreal)), + this, SLOT(onLinePropertiesChanged())); + setBackend(mapRectangleBackendSelector->backend); } QDeclarativeRectangleMapItem::~QDeclarativeRectangleMapItem() @@ -142,6 +157,43 @@ QDeclarativeRectangleMapItem::~QDeclarativeRectangleMapItem() } /*! + \qmlproperty MapRectangle.Backend QtLocation::MapRectangle::backend + + This property holds which backend is in use to render the map item. + Valid values are \b MapRectangle.Software and \b{MapRectangle.OpenGL}. + The default value is \b{MapRectangle.Software}. + + \note \b{The release of this API with Qt 5.15 is a Technology Preview}. + Ideally, as the OpenGL backends for map items mature, there will be + no more need to also offer the legacy software-projection backend. + So this property will likely disappear at some later point. + To select OpenGL-accelerated item backends without using this property, + it is also possible to set the environment variable \b QTLOCATION_OPENGL_ITEMS + to \b{1}. + Also note that all current OpenGL backends won't work as expected when enabling + layers on the individual item, or when running on OpenGL core profiles greater than 2.x. + + \since 5.15 +*/ +QDeclarativeRectangleMapItem::Backend QDeclarativeRectangleMapItem::backend() const +{ + return m_backend; +} + +void QDeclarativeRectangleMapItem::setBackend(QDeclarativeRectangleMapItem::Backend b) +{ + if (b == m_backend) + return; + m_backend = b; + QScopedPointer<QDeclarativeRectangleMapItemPrivate> d((m_backend == Software) + ? static_cast<QDeclarativeRectangleMapItemPrivate *>(new QDeclarativeRectangleMapItemPrivateCPU(*this)) + : static_cast<QDeclarativeRectangleMapItemPrivate * >(new QDeclarativeRectangleMapItemPrivateOpenGL(*this))); + m_d.swap(d); + m_d->onGeoGeometryChanged(); + emit backendChanged(); +} + +/*! \internal */ void QDeclarativeRectangleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) @@ -149,8 +201,7 @@ void QDeclarativeRectangleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap QDeclarativeGeoMapItemBase::setMap(quickMap,map); if (!map) return; - updatePath(); - markSourceDirtyAndUpdate(); + m_d->onMapSet(); } /*! @@ -167,7 +218,7 @@ void QDeclarativeRectangleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap */ QDeclarativeMapLineProperties *QDeclarativeRectangleMapItem::border() { - return &border_; + return &m_border; } /*! @@ -178,18 +229,17 @@ QDeclarativeMapLineProperties *QDeclarativeRectangleMapItem::border() */ void QDeclarativeRectangleMapItem::setTopLeft(const QGeoCoordinate &topLeft) { - if (rectangle_.topLeft() == topLeft) + if (m_rectangle.topLeft() == topLeft) return; - rectangle_.setTopLeft(topLeft); - updatePath(); - markSourceDirtyAndUpdate(); + m_rectangle.setTopLeft(topLeft); + m_d->onGeoGeometryChanged(); emit topLeftChanged(topLeft); } QGeoCoordinate QDeclarativeRectangleMapItem::topLeft() { - return rectangle_.topLeft(); + return m_rectangle.topLeft(); } /*! @@ -197,9 +247,12 @@ QGeoCoordinate QDeclarativeRectangleMapItem::topLeft() */ void QDeclarativeRectangleMapItem::markSourceDirtyAndUpdate() { - geometry_.markSourceDirty(); - borderGeometry_.markSourceDirty(); - polishAndUpdate(); + m_d->markSourceDirtyAndUpdate(); +} + +void QDeclarativeRectangleMapItem::onLinePropertiesChanged() +{ + m_d->onLinePropertiesChanged(); } /*! @@ -210,18 +263,17 @@ void QDeclarativeRectangleMapItem::markSourceDirtyAndUpdate() */ void QDeclarativeRectangleMapItem::setBottomRight(const QGeoCoordinate &bottomRight) { - if (rectangle_.bottomRight() == bottomRight) + if (m_rectangle.bottomRight() == bottomRight) return; - rectangle_.setBottomRight(bottomRight); - updatePath(); - markSourceDirtyAndUpdate(); + m_rectangle.setBottomRight(bottomRight); + m_d->onGeoGeometryChanged(); emit bottomRightChanged(bottomRight); } QGeoCoordinate QDeclarativeRectangleMapItem::bottomRight() { - return rectangle_.bottomRight(); + return m_rectangle.bottomRight(); } /*! @@ -232,17 +284,17 @@ QGeoCoordinate QDeclarativeRectangleMapItem::bottomRight() */ QColor QDeclarativeRectangleMapItem::color() const { - return color_; + return m_color; } void QDeclarativeRectangleMapItem::setColor(const QColor &color) { - if (color_ == color) + if (m_color == color) return; - color_ = color; - dirtyMaterial_ = true; + m_color = color; + m_dirtyMaterial = true; polishAndUpdate(); - emit colorChanged(color_); + emit colorChanged(m_color); } /*! @@ -260,24 +312,7 @@ void QDeclarativeRectangleMapItem::setColor(const QColor &color) */ QSGNode *QDeclarativeRectangleMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) { - Q_UNUSED(data); - - MapPolygonNode *node = static_cast<MapPolygonNode *>(oldNode); - - if (!node) { - node = new MapPolygonNode(); - } - - //TODO: update only material - if (geometry_.isScreenDirty() || borderGeometry_.isScreenDirty() || dirtyMaterial_) { - node->update(color_, border_.color(), &geometry_, &borderGeometry_); - geometry_.setPreserveGeometry(false); - borderGeometry_.setPreserveGeometry(false); - geometry_.markClean(); - borderGeometry_.markClean(); - dirtyMaterial_ = false; - } - return node; + return m_d->updateMapItemPaintNode(oldNode, data); } /*! @@ -287,55 +322,7 @@ void QDeclarativeRectangleMapItem::updatePolish() { if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) return; - if (!topLeft().isValid() || !bottomRight().isValid()) { - geometry_.clear(); - borderGeometry_.clear(); - setWidth(0); - setHeight(0); - return; - } - - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection()); - - QScopedValueRollback<bool> rollback(updatingGeometry_); - updatingGeometry_ = true; - - geometry_.setPreserveGeometry(true, rectangle_.topLeft()); - geometry_.updateSourcePoints(*map(), pathMercator_); - geometry_.updateScreenPoints(*map(), border_.width()); - - QList<QGeoMapItemGeometry *> geoms; - geoms << &geometry_; - borderGeometry_.clear(); - - if (border_.color() != Qt::transparent && border_.width() > 0) { - QList<QDoubleVector2D> closedPath = pathMercator_; - closedPath << closedPath.first(); - - borderGeometry_.setPreserveGeometry(true, rectangle_.topLeft()); - const QGeoCoordinate &geometryOrigin = geometry_.origin(); - - borderGeometry_.srcPoints_.clear(); - borderGeometry_.srcPointTypes_.clear(); - - QDoubleVector2D borderLeftBoundWrapped; - QList<QList<QDoubleVector2D > > clippedPaths = borderGeometry_.clipPath(*map(), closedPath, borderLeftBoundWrapped); - if (clippedPaths.size()) { - borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); - borderGeometry_.pathToScreen(*map(), clippedPaths, borderLeftBoundWrapped); - borderGeometry_.updateScreenPoints(*map(), border_.width()); - - geoms << &borderGeometry_; - } else { - borderGeometry_.clear(); - } - } - - QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); - setWidth(combined.width() + 2 * border_.width()); - setHeight(combined.height() + 2 * border_.width()); - - setPositionOnMap(geometry_.origin(), geometry_.firstPointOffset()); + m_d->updatePolish(); } /*! @@ -345,10 +332,7 @@ void QDeclarativeRectangleMapItem::afterViewportChanged(const QGeoMapViewportCha { if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) return; - - geometry_.setPreserveGeometry(true, rectangle_.topLeft()); - borderGeometry_.setPreserveGeometry(true, rectangle_.topLeft()); - markSourceDirtyAndUpdate(); + m_d->afterViewportChanged(); } /*! @@ -356,46 +340,29 @@ void QDeclarativeRectangleMapItem::afterViewportChanged(const QGeoMapViewportCha */ bool QDeclarativeRectangleMapItem::contains(const QPointF &point) const { - return (geometry_.contains(point) || borderGeometry_.contains(point)); + return m_d->contains(point); } const QGeoShape &QDeclarativeRectangleMapItem::geoShape() const { - return rectangle_; + return m_rectangle; } void QDeclarativeRectangleMapItem::setGeoShape(const QGeoShape &shape) { - if (shape == rectangle_) + if (shape == m_rectangle) return; - const QGeoRectangle rectangle = rectangle_.boundingGeoRectangle(); - const bool tlHasChanged = rectangle.topLeft() != rectangle_.topLeft(); - const bool brHasChanged = rectangle.bottomRight() != rectangle_.bottomRight(); - rectangle_ = rectangle; + const QGeoRectangle rectangle = m_rectangle.boundingGeoRectangle(); + const bool tlHasChanged = rectangle.topLeft() != m_rectangle.topLeft(); + const bool brHasChanged = rectangle.bottomRight() != m_rectangle.bottomRight(); + m_rectangle = rectangle; - updatePath(); - markSourceDirtyAndUpdate(); + m_d->onGeoGeometryChanged(); if (tlHasChanged) - emit topLeftChanged(rectangle_.topLeft()); + emit topLeftChanged(m_rectangle.topLeft()); if (brHasChanged) - emit bottomRightChanged(rectangle_.bottomRight()); -} - -/*! - \internal -*/ -void QDeclarativeRectangleMapItem::updatePath() -{ - if (!map()) - return; - pathMercator_.clear(); - pathMercator_ << QWebMercator::coordToMercator(rectangle_.topLeft()); - pathMercator_ << QWebMercator::coordToMercator( - QGeoCoordinate(rectangle_.topLeft().latitude(), rectangle_.bottomRight().longitude())); - pathMercator_ << QWebMercator::coordToMercator(rectangle_.bottomRight()); - pathMercator_ << QWebMercator::coordToMercator( - QGeoCoordinate(rectangle_.bottomRight().latitude(), rectangle_.topLeft().longitude())); + emit bottomRightChanged(m_rectangle.bottomRight()); } /*! @@ -403,7 +370,7 @@ void QDeclarativeRectangleMapItem::updatePath() */ void QDeclarativeRectangleMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { - if (!map() || !rectangle_.isValid() || updatingGeometry_ || newGeometry.topLeft() == oldGeometry.topLeft()) { + if (!map() || !m_rectangle.isValid() || m_updatingGeometry || newGeometry.topLeft() == oldGeometry.topLeft()) { QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); return; } @@ -417,16 +384,19 @@ void QDeclarativeRectangleMapItem::geometryChanged(const QRectF &newGeometry, co if (offsetLati == 0.0 && offsetLongi == 0.0) return; - rectangle_.translate(offsetLati, offsetLongi); - updatePath(); - geometry_.setPreserveGeometry(true, rectangle_.topLeft()); - borderGeometry_.setPreserveGeometry(true, rectangle_.topLeft()); - markSourceDirtyAndUpdate(); - emit topLeftChanged(rectangle_.topLeft()); - emit bottomRightChanged(rectangle_.bottomRight()); + m_rectangle.translate(offsetLati, offsetLongi); + m_d->onItemGeometryChanged(); + emit topLeftChanged(m_rectangle.topLeft()); + emit bottomRightChanged(m_rectangle.bottomRight()); // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested // call to this function. } +QDeclarativeRectangleMapItemPrivate::~QDeclarativeRectangleMapItemPrivate() {} + +QDeclarativeRectangleMapItemPrivateCPU::~QDeclarativeRectangleMapItemPrivateCPU() {} + +QDeclarativeRectangleMapItemPrivateOpenGL::~QDeclarativeRectangleMapItemPrivateOpenGL() {} + QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativerectanglemapitem_p.h b/src/location/declarativemaps/qdeclarativerectanglemapitem_p.h index 790b99d9..a9ce2f4c 100644 --- a/src/location/declarativemaps/qdeclarativerectanglemapitem_p.h +++ b/src/location/declarativemaps/qdeclarativerectanglemapitem_p.h @@ -52,7 +52,7 @@ #include <QtLocation/private/qdeclarativegeomapitembase_p.h> #include <QtLocation/private/qgeomapitemgeometry_p.h> #include <QtLocation/private/qdeclarativepolylinemapitem_p.h> -#include <QtLocation/private/qdeclarativepolygonmapitem_p.h> +#include <QtLocation/private/qdeclarativepolygonmapitem_p_p.h> #include <QtPositioning/private/qdoublevector2d_p.h> #include <QSGGeometryNode> @@ -60,18 +60,26 @@ QT_BEGIN_NAMESPACE +class QDeclarativeRectangleMapItemPrivate; class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItem: public QDeclarativeGeoMapItemBase { Q_OBJECT + Q_ENUMS(Backend) Q_PROPERTY(QGeoCoordinate topLeft READ topLeft WRITE setTopLeft NOTIFY topLeftChanged) Q_PROPERTY(QGeoCoordinate bottomRight READ bottomRight WRITE setBottomRight NOTIFY bottomRightChanged) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(QDeclarativeMapLineProperties *border READ border CONSTANT) + Q_PROPERTY(Backend backend READ backend WRITE setBackend NOTIFY backendChanged REVISION 15) public: - explicit QDeclarativeRectangleMapItem(QQuickItem *parent = 0); - ~QDeclarativeRectangleMapItem(); + enum Backend { + Software = 0, + OpenGL = 1 + }; + + explicit QDeclarativeRectangleMapItem(QQuickItem *parent = nullptr); + ~QDeclarativeRectangleMapItem() override; virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) override; //from QuickItem @@ -92,29 +100,38 @@ public: const QGeoShape &geoShape() const override; void setGeoShape(const QGeoShape &shape) override; + Backend backend() const; + void setBackend(Backend b); + Q_SIGNALS: void topLeftChanged(const QGeoCoordinate &topLeft); void bottomRightChanged(const QGeoCoordinate &bottomRight); void colorChanged(const QColor &color); + void backendChanged(); protected: - void updatePath(); void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; void updatePolish() override; protected Q_SLOTS: void markSourceDirtyAndUpdate(); + void onLinePropertiesChanged(); virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) override; private: - QGeoRectangle rectangle_; - QDeclarativeMapLineProperties border_; - QColor color_; - bool dirtyMaterial_; - QGeoMapPolygonGeometry geometry_; - QGeoMapPolylineGeometry borderGeometry_; - bool updatingGeometry_; - QList<QDoubleVector2D> pathMercator_; + QGeoRectangle m_rectangle; + QDeclarativeMapLineProperties m_border; + QColor m_color; + bool m_dirtyMaterial; + + bool m_updatingGeometry; + Backend m_backend = Software; + + QScopedPointer<QDeclarativeRectangleMapItemPrivate> m_d; + + friend class QDeclarativeRectangleMapItemPrivate; + friend class QDeclarativeRectangleMapItemPrivateCPU; + friend class QDeclarativeRectangleMapItemPrivateOpenGL; }; ////////////////////////////////////////////////////////////////////// diff --git a/src/location/declarativemaps/qdeclarativerectanglemapitem_p_p.h b/src/location/declarativemaps/qdeclarativerectanglemapitem_p_p.h new file mode 100644 index 00000000..65d2f618 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativerectanglemapitem_p_p.h @@ -0,0 +1,417 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Paolo Angelelli <paolo.angelelli@gmail.com> +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVERECTANGLEMAPITEM_P_P_H +#define QDECLARATIVERECTANGLEMAPITEM_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativepolygonmapitem_p_p.h> +#include <QtLocation/private/qdeclarativerectanglemapitem_p.h> +#include <QtPositioning/private/qwebmercator_p.h> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItemPrivate +{ +public: + QDeclarativeRectangleMapItemPrivate(QDeclarativeRectangleMapItem &rect) : m_rect(rect) + { + + } + QDeclarativeRectangleMapItemPrivate(QDeclarativeRectangleMapItemPrivate &other) : m_rect(other.m_rect) + { + } + + virtual ~QDeclarativeRectangleMapItemPrivate(); + virtual void onLinePropertiesChanged() = 0; + virtual void markSourceDirtyAndUpdate() = 0; + virtual void onMapSet() = 0; + virtual void onGeoGeometryChanged() = 0; + virtual void onItemGeometryChanged() = 0; + virtual void updatePolish() = 0; + virtual void afterViewportChanged() = 0; + virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) = 0; + virtual bool contains(const QPointF &point) const = 0; + + QDeclarativeRectangleMapItem &m_rect; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItemPrivateCPU: public QDeclarativeRectangleMapItemPrivate +{ +public: + QDeclarativeRectangleMapItemPrivateCPU(QDeclarativeRectangleMapItem &rect) : QDeclarativeRectangleMapItemPrivate(rect) + { + } + + QDeclarativeRectangleMapItemPrivateCPU(QDeclarativeRectangleMapItemPrivate &other) + : QDeclarativeRectangleMapItemPrivate(other) + { + } + + ~QDeclarativeRectangleMapItemPrivateCPU() override; + + void onLinePropertiesChanged() override + { + // mark dirty just in case we're a width change + markSourceDirtyAndUpdate(); + } + virtual void markSourceDirtyAndUpdate() override + { + m_geometry.markSourceDirty(); + m_borderGeometry.markSourceDirty(); + m_rect.polishAndUpdate(); + } + virtual void onMapSet() override + { + markSourceDirtyAndUpdate(); + } + virtual void onGeoGeometryChanged() override + { + markSourceDirtyAndUpdate(); + } + virtual void onItemGeometryChanged() override + { + m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); + m_borderGeometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); + markSourceDirtyAndUpdate(); + } + virtual void afterViewportChanged() override + { + m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); + m_borderGeometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); + markSourceDirtyAndUpdate(); + } + virtual void updatePolish() override + { + if (!m_rect.topLeft().isValid() || !m_rect.bottomRight().isValid()) { + m_geometry.clear(); + m_borderGeometry.clear(); + m_rect.setWidth(0); + m_rect.setHeight(0); + return; + } + + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_rect.map()->geoProjection()); + + QScopedValueRollback<bool> rollback(m_rect.m_updatingGeometry); + m_rect.m_updatingGeometry = true; + + const QList<QGeoCoordinate> perimeter = path(m_rect.m_rectangle); + const QList<QDoubleVector2D> pathMercator_ = pathMercator(perimeter); + m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); + m_geometry.updateSourcePoints(*m_rect.map(), pathMercator_); + m_geometry.updateScreenPoints(*m_rect.map(), m_rect.m_border.width()); + + QList<QGeoMapItemGeometry *> geoms; + geoms << &m_geometry; + m_borderGeometry.clear(); + + if (m_rect.m_border.color().alpha() != 0 && m_rect.m_border.width() > 0) { + QList<QDoubleVector2D> closedPath = pathMercator_; + closedPath << closedPath.first(); + + m_borderGeometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); + const QGeoCoordinate &geometryOrigin = m_geometry.origin(); + + m_borderGeometry.srcPoints_.clear(); + m_borderGeometry.srcPointTypes_.clear(); + + QDoubleVector2D borderLeftBoundWrapped; + QList<QList<QDoubleVector2D > > clippedPaths = m_borderGeometry.clipPath(*m_rect.map(), closedPath, borderLeftBoundWrapped); + if (clippedPaths.size()) { + borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); + m_borderGeometry.pathToScreen(*m_rect.map(), clippedPaths, borderLeftBoundWrapped); + m_borderGeometry.updateScreenPoints(*m_rect.map(), m_rect.m_border.width()); + + geoms << &m_borderGeometry; + } else { + m_borderGeometry.clear(); + } + } + + QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); + m_rect.setWidth(combined.width() + 2 * m_rect.m_border.width()); // ToDo: fix this! 2 is incorrect + m_rect.setHeight(combined.height() + 2 * m_rect.m_border.width()); + + m_rect.setPositionOnMap(m_geometry.origin(), m_geometry.firstPointOffset()); + } + + virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override + { + Q_UNUSED(data); + if (!m_node || !oldNode) { + m_node = new MapPolygonNode(); + if (oldNode) { + delete oldNode; + oldNode = nullptr; + } + } else { + m_node = static_cast<MapPolygonNode *>(oldNode); + } + + //TODO: update only material + if (m_geometry.isScreenDirty() || m_borderGeometry.isScreenDirty() || m_rect.m_dirtyMaterial) { + m_node->update(m_rect.m_color, m_rect.m_border.color(), &m_geometry, &m_borderGeometry); + m_geometry.setPreserveGeometry(false); + m_borderGeometry.setPreserveGeometry(false); + m_geometry.markClean(); + m_borderGeometry.markClean(); + m_rect.m_dirtyMaterial = false; + } + return m_node; + } + virtual bool contains(const QPointF &point) const override + { + return (m_geometry.contains(point) || m_borderGeometry.contains(point)); + } + + static QList<QGeoCoordinate> path(const QGeoRectangle &rect) + { + QList<QGeoCoordinate> res; + res << rect.topLeft(); + res << QGeoCoordinate(rect.topLeft().latitude(), rect.bottomRight().longitude()); + res << rect.bottomRight(); + res << QGeoCoordinate(rect.bottomRight().latitude(), rect.topLeft().longitude()); + return res; + } + + static QList<QGeoCoordinate> perimeter(const QGeoRectangle &rect) + { + QList<QGeoCoordinate> res; + res << rect.topLeft(); + res << QGeoCoordinate(rect.topLeft().latitude(), rect.bottomRight().longitude()); + res << rect.bottomRight(); + res << QGeoCoordinate(rect.bottomRight().latitude(), rect.topLeft().longitude()); + res << res.first(); + return res; + } + + static QList<QDoubleVector2D> pathMercator(const QList<QGeoCoordinate> &p) + { + QList<QDoubleVector2D> res; + for (const auto &c: p) + res << QWebMercator::coordToMercator(c); + return res; + } + + QGeoMapPolygonGeometry m_geometry; + QGeoMapPolylineGeometry m_borderGeometry; + MapPolygonNode *m_node = nullptr; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItemPrivateOpenGL: public QDeclarativeRectangleMapItemPrivate +{ +public: + QDeclarativeRectangleMapItemPrivateOpenGL(QDeclarativeRectangleMapItem &rect) : QDeclarativeRectangleMapItemPrivate(rect) + { + } + + QDeclarativeRectangleMapItemPrivateOpenGL(QDeclarativeRectangleMapItemPrivate &other) + : QDeclarativeRectangleMapItemPrivate(other) + { + } + + ~QDeclarativeRectangleMapItemPrivateOpenGL() override; + + void markScreenDirtyAndUpdate() + { + // preserveGeometry is cleared in updateMapItemPaintNode + m_geometry.markScreenDirty(); + m_borderGeometry.markScreenDirty(); + m_rect.polishAndUpdate(); + } + void onLinePropertiesChanged() override + { + m_rect.m_dirtyMaterial = true; + afterViewportChanged(); + } + virtual void markSourceDirtyAndUpdate() override + { + m_geometry.markSourceDirty(); + m_borderGeometry.markSourceDirty(); + m_rect.polishAndUpdate(); + } + void preserveGeometry() + { + m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); + m_borderGeometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); + } + virtual void onMapSet() override + { + markSourceDirtyAndUpdate(); + } + virtual void onGeoGeometryChanged() override + { + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + virtual void onItemGeometryChanged() override + { + onGeoGeometryChanged(); + } + virtual void afterViewportChanged() override + { + preserveGeometry(); + markScreenDirtyAndUpdate(); + } + virtual void updatePolish() override + { + if (!m_rect.topLeft().isValid() || !m_rect.bottomRight().isValid()) { + m_geometry.clear(); + m_borderGeometry.clear(); + m_rect.setWidth(0); + m_rect.setHeight(0); + return; + } + + QScopedValueRollback<bool> rollback(m_rect.m_updatingGeometry); + m_rect.m_updatingGeometry = true; + const qreal lineWidth = m_rect.m_border.width(); + const QColor &lineColor = m_rect.m_border.color(); + const QColor &fillColor = m_rect.color(); + if (fillColor.alpha() != 0) { + m_geometry.updateSourcePoints(*m_rect.map(), m_rect.m_rectangle); + m_geometry.markScreenDirty(); + m_geometry.updateScreenPoints(*m_rect.map(), lineWidth, lineColor); + } else { + m_geometry.clearBounds(); + } + + QGeoMapItemGeometry * geom = &m_geometry; + m_borderGeometry.clearScreen(); + if (lineColor.alpha() != 0 && lineWidth > 0) { + m_borderGeometry.updateSourcePoints(*m_rect.map(), m_rect.m_rectangle); + m_borderGeometry.markScreenDirty(); + m_borderGeometry.updateScreenPoints(*m_rect.map(), lineWidth); + geom = &m_borderGeometry; + } + m_rect.setWidth(geom->sourceBoundingBox().width()); + m_rect.setHeight(geom->sourceBoundingBox().height()); + m_rect.setPosition(1.0 * geom->firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5)); + } + + virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override + { + Q_UNUSED(data); + + if (!m_rootNode || !oldNode) { + m_rootNode = new QDeclarativePolygonMapItemPrivateOpenGL::RootNode(); + m_node = new MapPolygonNodeGL(); + m_rootNode->appendChildNode(m_node); + m_polylinenode = new MapPolylineNodeOpenGLExtruded(); + m_rootNode->appendChildNode(m_polylinenode); + m_rootNode->markDirty(QSGNode::DirtyNodeAdded); + if (oldNode) + delete oldNode; + } else { + m_rootNode = static_cast<QDeclarativePolygonMapItemPrivateOpenGL::RootNode *>(oldNode); + } + + const QGeoMap *map = m_rect.map(); + const QMatrix4x4 &combinedMatrix = map->geoProjection().qsgTransform(); + const QDoubleVector3D &cameraCenter = map->geoProjection().centerMercator(); + + if (m_borderGeometry.isScreenDirty()) { + /* Do the border update first */ + m_polylinenode->update(m_rect.m_border.color(), + float(m_rect.m_border.width()), + &m_borderGeometry, + combinedMatrix, + cameraCenter, + Qt::SquareCap, + true, + 30); // No LOD for rectangles + m_borderGeometry.setPreserveGeometry(false); + m_borderGeometry.markClean(); + } else { + m_polylinenode->setSubtreeBlocked(true); + } + if (m_geometry.isScreenDirty()) { + m_node->update(m_rect.m_color, + &m_geometry, + combinedMatrix, + cameraCenter); + m_geometry.setPreserveGeometry(false); + m_geometry.markClean(); + } else { + m_node->setSubtreeBlocked(true); + } + + m_rootNode->setSubtreeBlocked(false); + return m_rootNode; + } + virtual bool contains(const QPointF &point) const override + { + const qreal lineWidth = m_rect.m_border.width(); + const QColor &lineColor = m_rect.m_border.color(); + const QRectF &bounds = (lineColor.alpha() != 0 && lineWidth > 0) ? m_borderGeometry.sourceBoundingBox() : m_geometry.sourceBoundingBox(); + if (bounds.contains(point)) { + QDeclarativeGeoMap *m = m_rect.quickMap(); + if (m) { + const QGeoCoordinate crd = m->toCoordinate(m->mapFromItem(&m_rect, point)); + return m_rect.m_rectangle.contains(crd) || m_borderGeometry.contains(m_rect.mapToItem(m_rect.quickMap(), point), + m_rect.border()->width(), + static_cast<const QGeoProjectionWebMercator&>(m_rect.map()->geoProjection())); + } else { + return true; + } + } + return false; + } + + QGeoMapPolygonGeometryOpenGL m_geometry; + QGeoMapPolylineGeometryOpenGL m_borderGeometry; + QDeclarativePolygonMapItemPrivateOpenGL::RootNode *m_rootNode = nullptr; + MapPolygonNodeGL *m_node = nullptr; + MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVERECTANGLEMAPITEM_P_P_H + diff --git a/src/location/declarativemaps/qdeclarativeroutemapitem_p.h b/src/location/declarativemaps/qdeclarativeroutemapitem_p.h index a0b171b0..f0759b66 100644 --- a/src/location/declarativemaps/qdeclarativeroutemapitem_p.h +++ b/src/location/declarativemaps/qdeclarativeroutemapitem_p.h @@ -55,6 +55,8 @@ #include <QPen> #include <QBrush> +Q_MOC_INCLUDE(<QtLocation/private/qdeclarativegeoroute_p.h>) + QT_BEGIN_NAMESPACE class QDeclarativeGeoRoute; diff --git a/src/location/declarativemaps/qgeomapitemgeometry.cpp b/src/location/declarativemaps/qgeomapitemgeometry.cpp index 2883c2bb..28e9fa67 100644 --- a/src/location/declarativemaps/qgeomapitemgeometry.cpp +++ b/src/location/declarativemaps/qgeomapitemgeometry.cpp @@ -48,6 +48,11 @@ QGeoMapItemGeometry::QGeoMapItemGeometry() { } +QGeoMapItemGeometry::~QGeoMapItemGeometry() +{ + +} + /*! \internal */ diff --git a/src/location/declarativemaps/qgeomapitemgeometry_p.h b/src/location/declarativemaps/qgeomapitemgeometry_p.h index ab81ff0b..aa5ea4ac 100644 --- a/src/location/declarativemaps/qgeomapitemgeometry_p.h +++ b/src/location/declarativemaps/qgeomapitemgeometry_p.h @@ -67,6 +67,7 @@ class Q_LOCATION_PRIVATE_EXPORT QGeoMapItemGeometry { public: QGeoMapItemGeometry(); + virtual ~QGeoMapItemGeometry(); inline bool isSourceDirty() const { return sourceDirty_; } inline bool isScreenDirty() const { return screenDirty_; } @@ -74,6 +75,7 @@ public: inline void markScreenDirty() { screenDirty_ = true; clipToViewport_ = true; } inline void markFullScreenDirty() { screenDirty_ = true; clipToViewport_ = false;} inline void markClean() { screenDirty_ = (sourceDirty_ = false); clipToViewport_ = true;} + inline void clearScreen() { screenDirty_ = false; } inline void setPreserveGeometry(bool value, const QGeoCoordinate &geoLeftBound = QGeoCoordinate()) { @@ -85,6 +87,7 @@ public: inline QRectF sourceBoundingBox() const { return sourceBounds_; } inline QRectF screenBoundingBox() const { return screenBounds_; } + inline void clearBounds() { sourceBounds_ = screenBounds_ = QRectF(); firstPointOffset_ = QPointF(); } inline QPointF firstPointOffset() const { return firstPointOffset_; } void translate(const QPointF &offset); @@ -128,6 +131,8 @@ public: static QRectF translateToCommonOrigin(const QList<QGeoMapItemGeometry *> &geoms); + mutable bool m_dataChanged = false; + private: QGeoMapItemGeometry(const QGeoMapItemGeometry &other); // Or else it may crash on copy QGeoMapItemGeometry &operator= (const QGeoMapItemGeometry & other); // Or else it may crash on copy diff --git a/src/location/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/declarativeplaces/qdeclarativecategory_p.h b/src/location/declarativeplaces/qdeclarativecategory_p.h index c32072f4..96983c1f 100644 --- a/src/location/declarativeplaces/qdeclarativecategory_p.h +++ b/src/location/declarativeplaces/qdeclarativecategory_p.h @@ -57,6 +57,8 @@ #include <QtLocation/private/qdeclarativegeoserviceprovider_p.h> +Q_MOC_INCLUDE(<QtLocation/private/qdeclarativeplaceicon_p.h>) + QT_BEGIN_NAMESPACE class QDeclarativePlaceIcon; diff --git a/src/location/declarativeplaces/qdeclarativeplacecontentmodel_p.h b/src/location/declarativeplaces/qdeclarativeplacecontentmodel_p.h index 05b559ae..53e24f42 100644 --- a/src/location/declarativeplaces/qdeclarativeplacecontentmodel_p.h +++ b/src/location/declarativeplaces/qdeclarativeplacecontentmodel_p.h @@ -54,6 +54,8 @@ #include <QtLocation/QPlaceContent> #include <QtLocation/QPlaceContentReply> +Q_MOC_INCLUDE(<QtLocation/private/qdeclarativeplace_p.h>) + QT_BEGIN_NAMESPACE class QDeclarativePlace; diff --git a/src/location/labs/qdeclarativenavigator.cpp b/src/location/labs/qdeclarativenavigator.cpp index 69f450f3..546d0b01 100644 --- a/src/location/labs/qdeclarativenavigator.cpp +++ b/src/location/labs/qdeclarativenavigator.cpp @@ -52,9 +52,9 @@ QT_BEGIN_NAMESPACE To use this module, import the module with the following line: - \code + \qml import Qt.labs.location 1.0 - \endcode + \endqml \note These types are experimental and subject to source-incompatible changes from one Qt minor release to the next, until they are ready to be moved to the stable QtLocation QML diff --git a/src/location/labs/qdeclarativenavigator_p.h b/src/location/labs/qdeclarativenavigator_p.h index 24ff798f..30dd7f11 100644 --- a/src/location/labs/qdeclarativenavigator_p.h +++ b/src/location/labs/qdeclarativenavigator_p.h @@ -54,6 +54,13 @@ #include <QtLocation/private/qparameterizableobject_p.h> #include <QtLocation/qgeoserviceprovider.h> +Q_MOC_INCLUDE(<QtLocation/private/qdeclarativenavigator_p_p.h>) +Q_MOC_INCLUDE(<QtLocation/private/qdeclarativegeomap_p.h>) +Q_MOC_INCLUDE(<QtLocation/private/qdeclarativegeoroute_p.h>) +Q_MOC_INCLUDE(<QtLocation/private/qdeclarativegeoserviceprovider_p.h>) +Q_MOC_INCLUDE(<QtLocation/private/qnavigationmanagerengine_p.h>) +Q_MOC_INCLUDE(<QtPositioningQuick/private/qdeclarativepositionsource_p.h>) + QT_BEGIN_NAMESPACE class QDeclarativeGeoServiceProvider; diff --git a/src/location/labs/qmappolygonobject.cpp b/src/location/labs/qmappolygonobject.cpp index 5a8ce228..9d679f31 100644 --- a/src/location/labs/qmappolygonobject.cpp +++ b/src/location/labs/qmappolygonobject.cpp @@ -145,10 +145,9 @@ void QMapPolygonObjectPrivateDefault::setGeoShape(const QGeoShape &shape) return; const QGeoPolygon poly(shape); - setPath(poly.path()); // to handle overrides for (int i = 0; i < poly.holesCount(); i++) m_path.addHole(poly.holePath(i)); - emit static_cast<QMapPolygonObject *>(q)->pathChanged(); + setPath(poly.path()); // to handle overrides. Last as it normally emits static_cast<QMapPolygonObject *>(q)->pathChanged(); } bool QMapPolygonObjectPrivate::equals(const QGeoMapObjectPrivate &other) const diff --git a/src/location/labs/qmappolylineobject.cpp b/src/location/labs/qmappolylineobject.cpp index 81390d7c..acfe6609 100644 --- a/src/location/labs/qmappolylineobject.cpp +++ b/src/location/labs/qmappolylineobject.cpp @@ -76,7 +76,7 @@ QMapPolylineObjectPrivateDefault::QMapPolylineObjectPrivateDefault(QGeoMapObject QMapPolylineObjectPrivateDefault::QMapPolylineObjectPrivateDefault(const QMapPolylineObjectPrivate &other) : QMapPolylineObjectPrivate(other.q) { - m_path = other.path(); + m_path.setPath(other.path()); m_color = other.color(); m_width = other.width(); } @@ -88,12 +88,12 @@ QMapPolylineObjectPrivateDefault::~QMapPolylineObjectPrivateDefault() QList<QGeoCoordinate> QMapPolylineObjectPrivateDefault::path() const { - return m_path; + return m_path.path(); } void QMapPolylineObjectPrivateDefault::setPath(const QList<QGeoCoordinate> &path) { - m_path = path; + m_path.setPath(path); } QColor QMapPolylineObjectPrivateDefault::color() const diff --git a/src/location/labs/qmappolylineobject_p_p.h b/src/location/labs/qmappolylineobject_p_p.h index a0eb3711..3fdf1a1f 100644 --- a/src/location/labs/qmappolylineobject_p_p.h +++ b/src/location/labs/qmappolylineobject_p_p.h @@ -51,6 +51,7 @@ #include <QtLocation/private/qlocationglobal_p.h> #include <QtLocation/private/qgeomapobject_p_p.h> #include <QGeoCoordinate> +#include <QGeoPath> #include <QColor> QT_BEGIN_NAMESPACE @@ -95,7 +96,7 @@ public: QGeoMapObjectPrivate *clone() override; public: - QList<QGeoCoordinate> m_path; + QGeoPath m_path; // small overhead compared to plain QList<QGeoCoordinate> QColor m_color; qreal m_width = 0; diff --git a/src/location/labs/qmaprouteobject_p.h b/src/location/labs/qmaprouteobject_p.h index dcc35807..05354544 100644 --- a/src/location/labs/qmaprouteobject_p.h +++ b/src/location/labs/qmaprouteobject_p.h @@ -54,6 +54,8 @@ #include <QtLocation/private/qgeomapobject_p.h> #include <QtLocation/private/qparameterizableobject_p.h> +Q_MOC_INCLUDE(<QtLocation/private/qdeclarativegeoroute_p.h>) + QT_BEGIN_NAMESPACE class QDeclarativeGeoRoute; diff --git a/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp b/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp index 1a1b102b..0e1df8f6 100644 --- a/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp +++ b/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp @@ -152,12 +152,20 @@ void QGeoMapObjectQSGSupport::removeMapObject(QGeoMapObject *obj) } } +// called in the render thread void QGeoMapObjectQSGSupport::updateMapObjects(QSGNode *root, QQuickWindow *window) { + if (!root) + return; + if (!m_mapObjectsRootNode) { + m_mapObjectsRootNode = new QDeclarativePolygonMapItemPrivateOpenGL::RootNode(); + root->appendChildNode(m_mapObjectsRootNode); + } + + m_mapObjectsRootNode->removeAllChildNodes(); for (int i = 0; i < m_removedMapObjects.size(); ++i) { MapObject mo = m_removedMapObjects[i]; if (mo.qsgNode) { - root->removeChildNode(mo.qsgNode); delete mo.qsgNode; mo.qsgNode = nullptr; // mo.sgObject is now invalid as it is destroyed right after appending @@ -176,7 +184,7 @@ void QGeoMapObjectQSGSupport::updateMapObjects(QSGNode *root, QQuickWindow *wind MapObject &mo = m_mapObjects[i]; QQSGMapObject *sgo = mo.sgObject; QSGNode *oldNode = mo.qsgNode; - mo.qsgNode = sgo->updateMapObjectNode(oldNode, &mo.visibleNode, root, window); + mo.qsgNode = sgo->updateMapObjectNode(oldNode, &mo.visibleNode, m_mapObjectsRootNode, window); if (Q_UNLIKELY(!mo.qsgNode)) { qWarning() << "updateMapObjectNode for "<<mo.object->type() << " returned NULL"; } else if (mo.visibleNode && (mo.visibleNode->visible() != mo.object->visible())) { @@ -192,7 +200,7 @@ void QGeoMapObjectQSGSupport::updateMapObjects(QSGNode *root, QQuickWindow *wind QQSGMapObject *sgo = mo.sgObject; QSGNode *oldNode = mo.qsgNode; sgo->updateGeometry(); // or subtree will be blocked - mo.qsgNode = sgo->updateMapObjectNode(oldNode, &mo.visibleNode, root, window); + mo.qsgNode = sgo->updateMapObjectNode(oldNode, &mo.visibleNode, m_mapObjectsRootNode, window); if (mo.qsgNode) { if (mo.visibleNode && (mo.visibleNode->visible() != mo.object->visible())) { mo.visibleNode->setVisible(mo.object->visible()); @@ -208,8 +216,10 @@ void QGeoMapObjectQSGSupport::updateMapObjects(QSGNode *root, QQuickWindow *wind for (int i: qAsConst(toRemove)) m_pendingMapObjects.removeAt(i); + m_mapObjectsRootNode->setSubtreeBlocked(false); } +// called in GUI thread void QGeoMapObjectQSGSupport::updateObjectsGeometry() { for (int i = 0; i < m_mapObjects.size(); ++i) { diff --git a/src/location/labs/qsg/qgeomapobjectqsgsupport_p.h b/src/location/labs/qsg/qgeomapobjectqsgsupport_p.h index bb0477c5..1ec966fa 100644 --- a/src/location/labs/qsg/qgeomapobjectqsgsupport_p.h +++ b/src/location/labs/qsg/qgeomapobjectqsgsupport_p.h @@ -57,10 +57,10 @@ #include <QtLocation/private/qmaprouteobjectqsg_p_p.h> #include <QtLocation/private/qmapiconobjectqsg_p_p.h> #include <QtLocation/private/qdeclarativepolylinemapitem_p.h> +#include <QtLocation/private/qdeclarativepolygonmapitem_p_p.h> #include <QtCore/qpointer.h> QT_BEGIN_NAMESPACE - struct Q_LOCATION_PRIVATE_EXPORT MapObject { MapObject(QPointer<QGeoMapObject> &o, QQSGMapObject *sgo) : object(o), sgObject(sgo) {} @@ -85,6 +85,7 @@ public: QList<MapObject> m_pendingMapObjects; QList<MapObject> m_removedMapObjects; QGeoMap *m_map = nullptr; + QDeclarativePolygonMapItemPrivateOpenGL::RootNode *m_mapObjectsRootNode = nullptr; }; QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qmapcircleobjectqsg.cpp b/src/location/labs/qsg/qmapcircleobjectqsg.cpp index 32f3030b..750b20f2 100644 --- a/src/location/labs/qsg/qmapcircleobjectqsg.cpp +++ b/src/location/labs/qsg/qmapcircleobjectqsg.cpp @@ -41,16 +41,19 @@ QT_BEGIN_NAMESPACE static const int CircleSamples = 128; QMapCircleObjectPrivateQSG::QMapCircleObjectPrivateQSG(QGeoMapObject *q) - : QMapCircleObjectPrivateDefault(q) + : QMapCircleObjectPrivateDefault(q), m_dataCPU(new CircleDataCPU) { } QMapCircleObjectPrivateQSG::QMapCircleObjectPrivateQSG(const QMapCircleObjectPrivate &other) - : QMapCircleObjectPrivateDefault(other) + : QMapCircleObjectPrivateDefault(other), m_dataCPU(new CircleDataCPU) { // Data already cloned by the *Default copy constructor, but necessary // update operations triggered only by setters overrides + if (!QDeclarativeCircleMapItemPrivateCPU::crossEarthPole(center(), radius())) + switchToGL(); // this marks source dirty + updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -62,47 +65,54 @@ QMapCircleObjectPrivateQSG::~QMapCircleObjectPrivateQSG() m_map->removeMapObject(q); } -void QMapCircleObjectPrivateQSG::updateCirclePath() +void QMapCircleObjectPrivateQSG::updateGeometry() +{ + if (!m_dataGL.isNull()) + updateGeometryGL(); + else + updateGeometryCPU(); +} + +void QMapCircleObjectPrivateQSG::CircleDataCPU::updateCirclePath(const QGeoCoordinate ¢er, qreal radius, const QGeoProjectionWebMercator &p) { - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_map->geoProjection()); QList<QGeoCoordinate> path; - QDeclarativeCircleMapItem::calculatePeripheralPoints(path, center(), radius(), CircleSamples, m_leftBound); + QDeclarativeCircleMapItemPrivateCPU::calculatePeripheralPoints(path, center, radius, CircleSamples, m_leftBound); m_circlePath.clear(); for (const QGeoCoordinate &c : path) m_circlePath << p.geoToMapProjection(c); } -void QMapCircleObjectPrivateQSG::updateGeometry() +void QMapCircleObjectPrivateQSG::updateGeometryCPU() { if (!m_map || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator || !qIsFinite(radius()) || !center().isValid()) return; const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_map->geoProjection()); - QScopedValueRollback<bool> rollback(m_updatingGeometry); - m_updatingGeometry = true; + QScopedValueRollback<bool> rollback(m_dataCPU->m_updatingGeometry); + m_dataCPU->m_updatingGeometry = true; - updateCirclePath(); - QList<QDoubleVector2D> circlePath = m_circlePath; + m_dataCPU->updateCirclePath(center(), radius(), p); + QList<QDoubleVector2D> circlePath = m_dataCPU->m_circlePath; int pathCount = circlePath.size(); - bool preserve = QDeclarativeCircleMapItem::preserveCircleGeometry(circlePath, center(), radius(), p); + bool preserve = QDeclarativeCircleMapItemPrivateCPU::preserveCircleGeometry(circlePath, center(), radius(), p); // using leftBound_ instead of the analytically calculated circle_.boundingGeoRectangle().topLeft()); // to fix QTBUG-62154 - m_geometry.markSourceDirty(); - m_geometry.setPreserveGeometry(true, m_leftBound); // to set the geoLeftBound_ - m_geometry.setPreserveGeometry(preserve, m_leftBound); + m_dataCPU->m_geometry.markSourceDirty(); + m_dataCPU->m_geometry.setPreserveGeometry(true, m_dataCPU->m_leftBound); // to set the geoLeftBound_ + m_dataCPU->m_geometry.setPreserveGeometry(preserve, m_dataCPU->m_leftBound); bool invertedCircle = false; - if (QDeclarativeCircleMapItem::crossEarthPole(center(), radius()) && circlePath.size() == pathCount) { - m_geometry.updateScreenPointsInvert(circlePath, *m_map); // invert fill area for really huge circles + if (QDeclarativeCircleMapItemPrivateCPU::crossEarthPole(center(), radius()) && circlePath.size() == pathCount) { + m_dataCPU->m_geometry.updateScreenPointsInvert(circlePath, *m_map); // invert fill area for really huge circles invertedCircle = true; } else { - m_geometry.updateSourcePoints(*m_map, circlePath); - m_geometry.updateScreenPoints(*m_map); + m_dataCPU->m_geometry.updateSourcePoints(*m_map, circlePath); + m_dataCPU->m_geometry.updateScreenPoints(*m_map); } - m_borderGeometry.clear(); + m_dataCPU->m_borderGeometry.clear(); //if (borderColor() != Qt::transparent && borderWidth() > 0) { @@ -110,35 +120,76 @@ void QMapCircleObjectPrivateQSG::updateGeometry() closedPath << closedPath.first(); if (invertedCircle) { - closedPath = m_circlePath; + closedPath = m_dataCPU->m_circlePath; closedPath << closedPath.first(); std::reverse(closedPath.begin(), closedPath.end()); } - m_borderGeometry.markSourceDirty(); - m_borderGeometry.setPreserveGeometry(true, m_leftBound); - m_borderGeometry.setPreserveGeometry(preserve, m_leftBound); + m_dataCPU->m_borderGeometry.markSourceDirty(); + m_dataCPU->m_borderGeometry.setPreserveGeometry(true, m_dataCPU->m_leftBound); + m_dataCPU->m_borderGeometry.setPreserveGeometry(preserve, m_dataCPU->m_leftBound); // Use srcOrigin_ from fill geometry after clipping to ensure that translateToCommonOrigin won't fail. - const QGeoCoordinate &geometryOrigin = m_geometry.origin(); + const QGeoCoordinate &geometryOrigin = m_dataCPU->m_geometry.origin(); - m_borderGeometry.clearSource(); + m_dataCPU->m_borderGeometry.clearSource(); QDoubleVector2D borderLeftBoundWrapped; QList<QList<QDoubleVector2D > > clippedPaths = - m_borderGeometry.clipPath(*m_map, closedPath, borderLeftBoundWrapped); + m_dataCPU->m_borderGeometry.clipPath(*m_map, closedPath, borderLeftBoundWrapped); if (clippedPaths.size()) { borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); - m_borderGeometry.pathToScreen(*m_map, clippedPaths, borderLeftBoundWrapped); - m_borderGeometry.updateScreenPoints(*m_map, borderWidth(), false); + m_dataCPU->m_borderGeometry.pathToScreen(*m_map, clippedPaths, borderLeftBoundWrapped); + m_dataCPU->m_borderGeometry.updateScreenPoints(*m_map, borderWidth(), false); } else { - m_borderGeometry.clear(); + m_dataCPU->m_borderGeometry.clear(); } } - QPointF origin = m_map->geoProjection().coordinateToItemPosition(m_geometry.origin(), false).toPointF(); - m_geometry.translate(origin - m_geometry.firstPointOffset()); - m_borderGeometry.translate(origin - m_borderGeometry.firstPointOffset()); + QPointF origin = m_map->geoProjection().coordinateToItemPosition(m_dataCPU->m_geometry.origin(), false).toPointF(); + m_dataCPU->m_geometry.translate(origin - m_dataCPU->m_geometry.firstPointOffset()); + m_dataCPU->m_borderGeometry.translate(origin - m_dataCPU->m_borderGeometry.firstPointOffset()); +} + +void QMapCircleObjectPrivateQSG::CircleDataGL::updateCirclePath(const QGeoCoordinate ¢er, qreal radius, const QGeoProjectionWebMercator &p) +{ + m_circlePath.clear(); + if (radius < 0.001) // 1mm is small enough, probably already way too small. + return; + QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(m_circlePath, + center, + radius, + CircleSamples, + m_leftBound); + + m_leftBoundMercator = p.geoToMapProjection(m_leftBound); + m_geometry.setPreserveGeometry(true, m_leftBound); + m_borderGeometry.setPreserveGeometry(true, m_leftBound); +} + +void QMapCircleObjectPrivateQSG::updateGeometryGL() +{ + if (!m_map || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_map->geoProjection()); + if (m_dataGL->m_geometry.isSourceDirty() + || m_dataGL->m_borderGeometry.isSourceDirty()) { + m_dataGL->updateCirclePath(center(), radius(), p); + + if (m_dataGL->m_circlePath.length() == 0) { // Possibly cleared + m_dataGL->m_geometry.clear(); + m_dataGL->m_borderGeometry.clear(); + return; + } + m_dataGL->m_geometry.m_dataChanged = m_dataGL->m_borderGeometry.m_dataChanged = true; + m_dataGL->m_geometry.updateSourcePoints(*m_map, m_dataGL->m_circlePath); + m_dataGL->m_borderGeometry.updateSourcePoints(*m_map, QGeoCircle(center(), radius())); + m_dataGL->m_circlePath.clear(); // not needed anymore + } + m_dataGL->m_geometry.markScreenDirty(); // ToDo: this needs refactor. It's useless, remove screenDirty_ altogether. + m_dataGL->m_borderGeometry.markScreenDirty(); + m_dataGL->m_borderGeometry.m_wrapOffset = m_dataGL->m_geometry.m_wrapOffset = p.projectionWrapFactor(m_dataGL->m_leftBoundMercator) + 1; } QGeoMapObjectPrivate *QMapCircleObjectPrivateQSG::clone() @@ -146,41 +197,144 @@ QGeoMapObjectPrivate *QMapCircleObjectPrivateQSG::clone() return new QMapCircleObjectPrivateQSG(static_cast<QMapCircleObjectPrivate &>(*this)); } +void QMapCircleObjectPrivateQSG::switchToGL() +{ + if (!m_dataGL.isNull()) + return; + QScopedPointer<CircleDataGL> data(new CircleDataGL); + m_dataGL.swap(data); + m_dataGL->markSourceDirty(); + m_dataCPU.reset(nullptr); +} + +void QMapCircleObjectPrivateQSG::switchToCPU() +{ + if (!m_dataCPU.isNull()) + return; + QScopedPointer<CircleDataCPU> data(new CircleDataCPU); + m_dataCPU.swap(data); + m_dataGL.reset(nullptr); +} + QSGNode *QMapCircleObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, VisibleNode **visibleNode, QSGNode *root, - QQuickWindow * /*window*/) + QQuickWindow * window) { -// Q_UNUSED(visibleNode); // coz of -Werror=unused-but-set-parameter - MapPolygonNode *node = static_cast<MapPolygonNode *>(oldNode); + if (!m_dataGL.isNull()) + return updateMapObjectNodeGL(oldNode, visibleNode, root, window); + else + return updateMapObjectNodeCPU(oldNode, visibleNode, root, window); +} - bool created = false; - if (!node) { - node = new MapPolygonNode(); - *visibleNode = static_cast<VisibleNode *>(node); - created = true; +QSGNode *QMapCircleObjectPrivateQSG::updateMapObjectNodeCPU(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow */*window*/) +{ + if (!m_dataCPU->m_node || !oldNode) { + m_dataCPU->m_node = new MapPolygonNode(); + *visibleNode = static_cast<VisibleNode *>(m_dataCPU->m_node); + if (oldNode) + delete oldNode; + } else { + m_dataCPU->m_node = static_cast<MapPolygonNode *>(oldNode); + } + + if (!m_dataCPU->m_geometry.size() && !m_dataCPU->m_borderGeometry.size()) { + visibleNode = nullptr; + return nullptr; } //TODO: update only material - if (m_geometry.isScreenDirty() || !m_borderGeometry.isScreenDirty() || !oldNode || created) { + if (m_dataCPU->m_geometry.isScreenDirty() || m_dataCPU->m_borderGeometry.isScreenDirty() || oldNode != m_dataCPU->m_node) { //QMapPolygonObject *p = static_cast<QMapPolygonObject *>(q); - node->update(color(), borderColor(), &m_geometry, &m_borderGeometry); - m_geometry.setPreserveGeometry(false); - m_borderGeometry.setPreserveGeometry(false); - m_geometry.markClean(); - m_borderGeometry.markClean(); + m_dataCPU->m_node->update(color(), borderColor(), &m_dataCPU->m_geometry, &m_dataCPU->m_borderGeometry); + m_dataCPU->m_geometry.setPreserveGeometry(false); + m_dataCPU->m_borderGeometry.setPreserveGeometry(false); + m_dataCPU->m_geometry.markClean(); + m_dataCPU->m_borderGeometry.markClean(); } - if (created) - root->appendChildNode(node); - - return node; + if (m_dataCPU->m_geometry.size() || m_dataCPU->m_borderGeometry.size()) { + root->appendChildNode(m_dataCPU->m_node); + } else { + delete m_dataCPU->m_node; + m_dataCPU->m_node = nullptr; + visibleNode = nullptr; + return nullptr; + } + return m_dataCPU->m_node; } +QSGNode *QMapCircleObjectPrivateQSG::updateMapObjectNodeGL(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow */*window*/) +{ + if (!m_dataGL->m_rootNode || !oldNode) { + m_dataGL->m_rootNode = new QDeclarativePolygonMapItemPrivateOpenGL::RootNode(); + m_dataGL->m_node = new MapPolygonNodeGL(); + m_dataGL->m_rootNode->appendChildNode(m_dataGL->m_node); + m_dataGL->m_polylinenode = new MapPolylineNodeOpenGLExtruded(); + m_dataGL->m_rootNode->appendChildNode(m_dataGL->m_polylinenode); + m_dataGL->m_rootNode->markDirty(QSGNode::DirtyNodeAdded); + *visibleNode = static_cast<VisibleNode *>(m_dataGL->m_rootNode); + if (oldNode) + delete oldNode; + } else { + m_dataGL->m_rootNode = static_cast<QDeclarativePolygonMapItemPrivateOpenGL::RootNode *>(oldNode); + } + + const QMatrix4x4 &combinedMatrix = m_map->geoProjection().qsgTransform(); + const QDoubleVector3D &cameraCenter = m_map->geoProjection().centerMercator(); + + if (m_dataGL->m_borderGeometry.isScreenDirty()) { + /* Do the border update first */ + m_dataGL->m_polylinenode->update(borderColor(), + float(borderWidth()), + &m_dataGL->m_borderGeometry, + combinedMatrix, + cameraCenter, + Qt::SquareCap, + true); // No LOD for circles ATM + m_dataGL->m_borderGeometry.setPreserveGeometry(false); + m_dataGL->m_borderGeometry.markClean(); + } + if (m_dataGL->m_geometry.isScreenDirty()) { + m_dataGL->m_node->update(color(), + &m_dataGL->m_geometry, + combinedMatrix, + cameraCenter); + m_dataGL->m_geometry.setPreserveGeometry(false); + m_dataGL->m_geometry.markClean(); + } + + if (!m_dataGL->m_polylinenode->isSubtreeBlocked() || !m_dataGL->m_node->isSubtreeBlocked()) { + m_dataGL->m_rootNode->setSubtreeBlocked(false); + root->appendChildNode(m_dataGL->m_rootNode); + return m_dataGL->m_rootNode; + } else { + delete m_dataGL->m_rootNode; + m_dataGL->m_rootNode = nullptr; + m_dataGL->m_node = nullptr; + m_dataGL->m_polylinenode = nullptr; + *visibleNode = nullptr; + return nullptr; + } +} void QMapCircleObjectPrivateQSG::setCenter(const QGeoCoordinate ¢er) { QMapCircleObjectPrivateDefault::setCenter(center); + if (!QDeclarativeCircleMapItemPrivate::crossEarthPole(this->center(), this->radius())) // Switching implementation for circles crossing/not crossing poles + switchToGL(); + else + switchToCPU(); + + if (!m_dataGL.isNull()) + m_dataGL->markSourceDirty(); + updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -189,6 +343,14 @@ void QMapCircleObjectPrivateQSG::setCenter(const QGeoCoordinate ¢er) void QMapCircleObjectPrivateQSG::setRadius(qreal radius) { QMapCircleObjectPrivateDefault::setRadius(radius); + if (!QDeclarativeCircleMapItemPrivate::crossEarthPole(this->center(), this->radius())) // Switching implementation for circles crossing/not crossing poles + switchToGL(); + else + switchToCPU(); + + if (!m_dataGL.isNull()) + m_dataGL->markSourceDirty(); + updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -197,7 +359,8 @@ void QMapCircleObjectPrivateQSG::setRadius(qreal radius) void QMapCircleObjectPrivateQSG::setColor(const QColor &color) { QMapCircleObjectPrivateDefault::setColor(color); - updateGeometry(); + if (!m_dataCPU.isNull()) + updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); } @@ -205,7 +368,8 @@ void QMapCircleObjectPrivateQSG::setColor(const QColor &color) void QMapCircleObjectPrivateQSG::setBorderColor(const QColor &color) { QMapCircleObjectPrivateDefault::setBorderColor(color); - updateGeometry(); + if (!m_dataCPU.isNull()) + updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); } @@ -213,10 +377,10 @@ void QMapCircleObjectPrivateQSG::setBorderColor(const QColor &color) void QMapCircleObjectPrivateQSG::setBorderWidth(qreal width) { QMapCircleObjectPrivateDefault::setBorderWidth(width); - updateGeometry(); + if (!m_dataCPU.isNull()) + updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); } - QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h b/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h index 4e8162fa..dc057c0b 100644 --- a/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h +++ b/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h @@ -50,7 +50,7 @@ #include <QtLocation/private/qlocationglobal_p.h> #include <QtLocation/private/qgeomapobject_p_p.h> -#include <QtLocation/private/qdeclarativecirclemapitem_p.h> +#include <QtLocation/private/qdeclarativecirclemapitem_p_p.h> #include <QtLocation/private/qdeclarativepolygonmapitem_p.h> #include <QtLocation/private/qmapcircleobject_p.h> #include <QtLocation/private/qmapcircleobject_p_p.h> @@ -68,14 +68,22 @@ public: QMapCircleObjectPrivateQSG(const QMapCircleObjectPrivate &other); ~QMapCircleObjectPrivateQSG() override; - void updateCirclePath(); - // QQSGMapObject void updateGeometry() override; + void updateGeometryCPU(); + void updateGeometryGL(); QSGNode *updateMapObjectNode(QSGNode *oldNode, VisibleNode **visibleNode, QSGNode *root, QQuickWindow *window) override; + QSGNode *updateMapObjectNodeCPU(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow *window); + QSGNode *updateMapObjectNodeGL(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow *window); // QGeoMapCirclePrivate interface void setCenter(const QGeoCoordinate ¢er) override; @@ -87,15 +95,43 @@ public: // QGeoMapObjectPrivate QGeoMapObjectPrivate *clone() override; + void switchToGL(); + void switchToCPU(); + public: // Data Members +struct CircleDataCPU { + MapPolygonNode *m_node = nullptr; QList<QDoubleVector2D> m_circlePath; QGeoCoordinate m_leftBound; QGeoMapCircleGeometry m_geometry; QGeoMapPolylineGeometry m_borderGeometry; bool m_updatingGeometry = false; + + void updateCirclePath(const QGeoCoordinate ¢er, qreal radius, const QGeoProjectionWebMercator &p); +}; +struct CircleDataGL { + QList<QGeoCoordinate> m_circlePath; + QGeoCoordinate m_leftBound; + QDoubleVector2D m_leftBoundMercator; + QGeoMapPolygonGeometryOpenGL m_geometry; + QGeoMapPolylineGeometryOpenGL m_borderGeometry; + QDeclarativePolygonMapItemPrivateOpenGL::RootNode *m_rootNode = nullptr; + MapPolygonNodeGL *m_node = nullptr; + MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; + + void updateCirclePath(const QGeoCoordinate ¢er, qreal radius, const QGeoProjectionWebMercator &p); + void markSourceDirty() + { + m_geometry.markSourceDirty(); + m_borderGeometry.markSourceDirty(); + } +}; + QScopedPointer<CircleDataCPU> m_dataCPU; + QScopedPointer<CircleDataGL> m_dataGL; }; QT_END_NAMESPACE #endif // QMAPCIRCLEOBJECT_P_P_H + diff --git a/src/location/labs/qsg/qmapiconobjectqsg.cpp b/src/location/labs/qsg/qmapiconobjectqsg.cpp index d9a80c91..10948d82 100644 --- a/src/location/labs/qsg/qmapiconobjectqsg.cpp +++ b/src/location/labs/qsg/qmapiconobjectqsg.cpp @@ -102,7 +102,6 @@ QSGNode *QMapIconObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, QQuickWindow *window) { Q_UNUSED(visibleNode); - bool created = false; RootNode *node = static_cast<RootNode *>(oldNode); if (!node) { node = new RootNode(); @@ -110,7 +109,6 @@ QSGNode *QMapIconObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, m_imageNode->setOwnsTexture(true); node->appendChildNode(m_imageNode); *visibleNode = static_cast<VisibleNode *>(node); - created = true; } if (m_imageDirty) { @@ -131,8 +129,7 @@ QSGNode *QMapIconObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, } } - if (created) - root->appendChildNode(node); + root->appendChildNode(node); return node; } diff --git a/src/location/labs/qsg/qmappolygonobjectqsg.cpp b/src/location/labs/qsg/qmappolygonobjectqsg.cpp index 9963cac9..a8a1cb4b 100644 --- a/src/location/labs/qsg/qmappolygonobjectqsg.cpp +++ b/src/location/labs/qsg/qmappolygonobjectqsg.cpp @@ -52,6 +52,7 @@ QMapPolygonObjectPrivateQSG::QMapPolygonObjectPrivateQSG(const QMapPolygonObject { // Data already cloned by the *Default copy constructor, but necessary // update operations triggered only by setters overrides + markSourceDirty(); updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -63,65 +64,22 @@ QMapPolygonObjectPrivateQSG::~QMapPolygonObjectPrivateQSG() m_map->removeMapObject(q); } -QList<QDoubleVector2D> QMapPolygonObjectPrivateQSG::projectPath() +void QMapPolygonObjectPrivateQSG::setPath(const QList<QGeoCoordinate> &p) { - QList<QDoubleVector2D> geopathProjected_; - if (!m_map || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) - return geopathProjected_; - - const QGeoProjectionWebMercator &p = - static_cast<const QGeoProjectionWebMercator&>(m_map->geoProjection()); - geopathProjected_.reserve(m_path.path().size()); - for (const QGeoCoordinate &c : m_path.path()) - geopathProjected_ << p.geoToMapProjection(c); - return geopathProjected_; -} - -QSGNode *QMapPolygonObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, - VisibleNode **visibleNode, - QSGNode *root, - QQuickWindow * /*window*/) -{ - Q_UNUSED(visibleNode); - MapPolygonNode *node = static_cast<MapPolygonNode *>(oldNode); - - bool created = false; - if (!node) { - if (!m_geometry.size() && !m_borderGeometry.size()) - return nullptr; - node = new MapPolygonNode(); - *visibleNode = static_cast<VisibleNode *>(node); - created = true; - } - - //TODO: update only material - if (m_geometry.isScreenDirty() || !m_borderGeometry.isScreenDirty() || !oldNode || created) { - node->update(fillColor(), borderColor(), &m_geometry, &m_borderGeometry); - m_geometry.setPreserveGeometry(false); - m_borderGeometry.setPreserveGeometry(false); - m_geometry.markClean(); - m_borderGeometry.markClean(); - } - - if (created) - root->appendChildNode(node); - - return node; -} - -void QMapPolygonObjectPrivateQSG::setPath(const QList<QGeoCoordinate> &path) -{ - QMapPolygonObjectPrivateDefault::setPath(path); + if (p == path()) + return; + QMapPolygonObjectPrivateDefault::setPath(p); + markSourceDirty(); updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); + emit static_cast<QMapPolygonObject *>(q)->pathChanged(); } void QMapPolygonObjectPrivateQSG::setFillColor(const QColor &color) { QMapPolygonObjectPrivateDefault::setFillColor(color); - updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -130,7 +88,6 @@ void QMapPolygonObjectPrivateQSG::setFillColor(const QColor &color) void QMapPolygonObjectPrivateQSG::setBorderColor(const QColor &color) { QMapPolygonObjectPrivateDefault::setBorderColor(color); - updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -139,7 +96,6 @@ void QMapPolygonObjectPrivateQSG::setBorderColor(const QColor &color) void QMapPolygonObjectPrivateQSG::setBorderWidth(qreal width) { QMapPolygonObjectPrivateDefault::setBorderWidth(width); - updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -156,59 +112,111 @@ void QMapPolygonObjectPrivateQSG::setGeoShape(const QGeoShape &shape) return; m_path = QGeoPathEager(shape); + markSourceDirty(); updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); emit static_cast<QMapPolygonObject *>(q)->pathChanged(); } +// This is called both when data changes and when viewport changes. +// so handle both cases (sourceDirty, !sourceDirty) void QMapPolygonObjectPrivateQSG::updateGeometry() { - if (!m_map || m_path.path().length() == 0 - || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + if (!m_map || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) return; - QScopedValueRollback<bool> rollback(m_updatingGeometry); - m_updatingGeometry = true; - - const QList<QDoubleVector2D> &geopathProjected = projectPath(); - - m_geometry.markSourceDirty(); - m_geometry.setPreserveGeometry(true, m_path.boundingGeoRectangle().topLeft()); - m_geometry.updateSourcePoints(*m_map, geopathProjected); - m_geometry.updateScreenPoints(*m_map); - - m_borderGeometry.clear(); - - //if (border_.color() != Qt::transparent && border_.width() > 0) - { - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_map->geoProjection()); - QList<QDoubleVector2D> closedPath = geopathProjected; - closedPath << closedPath.first(); - - m_borderGeometry.markSourceDirty(); + if (m_path.path().length() == 0) { // Possibly cleared + m_geometry.clear(); + m_borderGeometry.clear(); + return; + } + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_map->geoProjection()); + if (m_geometry.isSourceDirty() || m_borderGeometry.isSourceDirty()) { + // This works a bit differently than MapPolygon: + // the "screen bounds" aren't needed, so update only sources, + // regardless of the color, as color changes won't trigger polish(), + // and remember to flag m_dataChanged, that is in principle the same as + // sourceDirty_, but in practice is cleared in two different codepaths. + // sourceDirty_ is cleared in any case, dataChanged only if the primitive + // is effectively visible (e.g., not transparent or border not null) + m_geometry.setPreserveGeometry(true, m_path.boundingGeoRectangle().topLeft()); m_borderGeometry.setPreserveGeometry(true, m_path.boundingGeoRectangle().topLeft()); + m_geometry.m_dataChanged = m_borderGeometry.m_dataChanged = true; + m_geometry.updateSourcePoints(*m_map, m_path); + m_borderGeometry.updateSourcePoints(*m_map, m_path); + m_leftBoundMercator = p.geoToMapProjection(m_geometry.origin()); + } + m_geometry.markScreenDirty(); // ToDo: this needs refactor. It's useless, remove screenDirty_ altogether. + m_borderGeometry.markScreenDirty(); + m_borderGeometry.m_wrapOffset = m_geometry.m_wrapOffset = p.projectionWrapFactor(m_leftBoundMercator) + 1; +} - const QGeoCoordinate &geometryOrigin = m_geometry.origin(); - - m_borderGeometry.clearSource(); +QSGNode *QMapPolygonObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow * /*window*/) +{ + if (!m_rootNode || !oldNode) { + m_rootNode = new QDeclarativePolygonMapItemPrivateOpenGL::RootNode(); + m_node = new MapPolygonNodeGL(); + m_rootNode->appendChildNode(m_node); + m_polylinenode = new MapPolylineNodeOpenGLExtruded(); + m_rootNode->appendChildNode(m_polylinenode); + m_rootNode->markDirty(QSGNode::DirtyNodeAdded); + *visibleNode = static_cast<VisibleNode *>(m_rootNode); + if (oldNode) + delete oldNode; + } else { + m_rootNode = static_cast<QDeclarativePolygonMapItemPrivateOpenGL::RootNode *>(oldNode); + } - QDoubleVector2D borderLeftBoundWrapped; - QList<QList<QDoubleVector2D > > clippedPaths = - m_borderGeometry.clipPath(*m_map.data(), closedPath, borderLeftBoundWrapped); + const QMatrix4x4 &combinedMatrix = m_map->geoProjection().qsgTransform(); + const QDoubleVector3D &cameraCenter = m_map->geoProjection().centerMercator(); + + if (m_borderGeometry.isScreenDirty()) { + /* Do the border update first */ + m_polylinenode->update(borderColor(), + float(borderWidth()), + &m_borderGeometry, + combinedMatrix, + cameraCenter, + Qt::SquareCap, + true); + m_borderGeometry.setPreserveGeometry(false); + m_borderGeometry.markClean(); + } + if (m_geometry.isScreenDirty()) { + m_node->update(fillColor(), + &m_geometry, + combinedMatrix, + cameraCenter); + m_geometry.setPreserveGeometry(false); + m_geometry.markClean(); + } - if (clippedPaths.size()) { - borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); - m_borderGeometry.pathToScreen(*m_map.data(), clippedPaths, borderLeftBoundWrapped); - m_borderGeometry.updateScreenPoints(*m_map.data(), borderWidth(), false); - } else { - m_borderGeometry.clear(); - } + if (!m_polylinenode->isSubtreeBlocked() || !m_node->isSubtreeBlocked()) { + m_rootNode->setSubtreeBlocked(false); + root->appendChildNode(m_rootNode); + return m_rootNode; + } else { + m_rootNode->setSubtreeBlocked(true); + // If the object is currently invisible, but not gone, + // it is reasonable to assume it will become visible again. + // However, better not to retain unused data. + delete m_rootNode; + m_rootNode = nullptr; + m_node = nullptr; + m_polylinenode = nullptr; + *visibleNode = nullptr; + return nullptr; } +} - QPointF origin = m_map->geoProjection().coordinateToItemPosition(m_geometry.origin(), false).toPointF(); - m_geometry.translate(origin - m_geometry.firstPointOffset()); - m_borderGeometry.translate(origin - m_borderGeometry.firstPointOffset()); +void QMapPolygonObjectPrivateQSG::markSourceDirty() +{ + m_geometry.markSourceDirty(); + m_borderGeometry.markSourceDirty(); } QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h b/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h index 0f42a92e..9dcece74 100644 --- a/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h +++ b/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h @@ -52,7 +52,7 @@ #include <QtLocation/private/qmappolygonobject_p.h> #include <QtLocation/private/qmappolygonobject_p_p.h> #include <QtLocation/private/qqsgmapobject_p.h> -#include <QtLocation/private/qdeclarativepolygonmapitem_p.h> +#include <QtLocation/private/qdeclarativepolygonmapitem_p_p.h> #include <QtCore/qscopedvaluerollback.h> QT_BEGIN_NAMESPACE @@ -67,6 +67,7 @@ public: QList<QDoubleVector2D> projectPath(); // QQSGMapObject + void markSourceDirty(); void updateGeometry() override; QSGNode *updateMapObjectNode(QSGNode *oldNode, VisibleNode **visibleNode, @@ -84,10 +85,12 @@ public: virtual void setGeoShape(const QGeoShape &shape) override; // Data Members - QGeoMapPolygonGeometry m_geometry; - QGeoMapPolylineGeometry m_borderGeometry; - - bool m_updatingGeometry = false; + QDoubleVector2D m_leftBoundMercator; + QGeoMapPolygonGeometryOpenGL m_geometry; + QGeoMapPolylineGeometryOpenGL m_borderGeometry; + QDeclarativePolygonMapItemPrivateOpenGL::RootNode *m_rootNode = nullptr; + MapPolygonNodeGL *m_node = nullptr; + MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; }; QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qmappolylineobjectqsg.cpp b/src/location/labs/qsg/qmappolylineobjectqsg.cpp index 2bf5b287..8efbfc2f 100644 --- a/src/location/labs/qsg/qmappolylineobjectqsg.cpp +++ b/src/location/labs/qsg/qmappolylineobjectqsg.cpp @@ -53,9 +53,9 @@ QMapPolylineObjectPrivateQSG::QMapPolylineObjectPrivateQSG(QGeoMapObject *q) QMapPolylineObjectPrivateQSG::QMapPolylineObjectPrivateQSG(const QMapPolylineObjectPrivate &other) : QMapPolylineObjectPrivateDefault(other) { - m_geoPath.setPath(m_path); // rest of the data already cloned by the *Default copy constructor, but necessary // update operations triggered only by setters overrides + markSourceDirty(); updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -75,28 +75,43 @@ QList<QDoubleVector2D> QMapPolylineObjectPrivateQSG::projectPath() const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_map->geoProjection()); - geopathProjected_.reserve(m_geoPath.path().size()); - for (const QGeoCoordinate &c : m_geoPath.path()) + geopathProjected_.reserve(m_path.path().size()); + for (const QGeoCoordinate &c : m_path.path()) geopathProjected_ << p.geoToMapProjection(c); return geopathProjected_; } void QMapPolylineObjectPrivateQSG::updateGeometry() { - if (!m_map || m_geoPath.path().length() == 0 - || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + if (!m_map || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + + if (m_path.path().length() == 0) { // Possibly cleared + m_borderGeometry.clear(); return; + } - QScopedValueRollback<bool> rollback(m_updatingGeometry); - m_updatingGeometry = true; - m_geometry.markSourceDirty(); - const QList<QDoubleVector2D> &geopathProjected = projectPath(); - m_geometry.setPreserveGeometry(true, m_geoPath.boundingGeoRectangle().topLeft()); - m_geometry.updateSourcePoints(*m_map.data(), geopathProjected, m_geoPath.boundingGeoRectangle().topLeft()); - m_geometry.updateScreenPoints(*m_map.data(), width(), false); + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_map->geoProjection()); + if (m_borderGeometry.isSourceDirty()) { + m_borderGeometry.setPreserveGeometry(true, m_path.boundingGeoRectangle().topLeft()); + m_borderGeometry.m_dataChanged = true; + m_borderGeometry.updateSourcePoints(*m_map, m_path); + m_leftBoundMercator = p.geoToMapProjection(m_borderGeometry.origin()); + } + m_borderGeometry.markScreenDirty(); + m_borderGeometry.m_wrapOffset = p.projectionWrapFactor(m_leftBoundMercator) + 1; +} - QPointF origin = m_map->geoProjection().coordinateToItemPosition(m_geometry.origin(), false).toPointF(); - m_geometry.translate(origin - m_geometry.firstPointOffset()); +/*! + \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, @@ -104,40 +119,53 @@ QSGNode *QMapPolylineObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, QSGNode *root, QQuickWindow * /*window*/) { - Q_UNUSED(visibleNode); - MapPolylineNode *node = static_cast<MapPolylineNode *>(oldNode); - - bool created = false; - if (!node) { - if (!m_geometry.size()) // condition to block the subtree - return nullptr; - node = new MapPolylineNode(); - *visibleNode = static_cast<VisibleNode *>(node); - created = true; + if (!m_polylinenode || !oldNode) { + m_polylinenode = new MapPolylineNodeOpenGLExtruded(); + *visibleNode = static_cast<VisibleNode *>(m_polylinenode); + if (oldNode) + delete oldNode; + } else { + m_polylinenode = static_cast<MapPolylineNodeOpenGLExtruded *>(oldNode); } - //TODO: update only material - if (m_geometry.isScreenDirty() || !oldNode || created) { - node->update(color(), &m_geometry); - m_geometry.setPreserveGeometry(false); - m_geometry.markClean(); + const QMatrix4x4 &combinedMatrix = m_map->geoProjection().qsgTransform(); + const QDoubleVector3D &cameraCenter = m_map->geoProjection().centerMercator(); + + if (m_borderGeometry.isScreenDirty()) { + /* Do the border update first */ + m_polylinenode->update(color(), + float(width()), + &m_borderGeometry, + combinedMatrix, + cameraCenter, + Qt::SquareCap, + true, + zoomForLOD(int(m_map->cameraData().zoomLevel()))); + m_borderGeometry.setPreserveGeometry(false); + m_borderGeometry.markClean(); } - if (created) - root->appendChildNode(node); - - return node; + if (!m_polylinenode->isSubtreeBlocked() ) { + m_polylinenode->setSubtreeBlocked(false); + root->appendChildNode(m_polylinenode); + return m_polylinenode; + } else { + delete m_polylinenode; + m_polylinenode = nullptr; + *visibleNode = nullptr; + return nullptr; + } } QList<QGeoCoordinate> QMapPolylineObjectPrivateQSG::path() const { - return m_geoPath.path(); + return m_path.path(); } void QMapPolylineObjectPrivateQSG::setPath(const QList<QGeoCoordinate> &path) { - m_path = path; - m_geoPath.setPath(path); + m_path.setPath(path); + markSourceDirty(); updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -146,7 +174,6 @@ void QMapPolylineObjectPrivateQSG::setPath(const QList<QGeoCoordinate> &path) void QMapPolylineObjectPrivateQSG::setColor(const QColor &color) { QMapPolylineObjectPrivateDefault::setColor(color); - updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -155,7 +182,6 @@ void QMapPolylineObjectPrivateQSG::setColor(const QColor &color) void QMapPolylineObjectPrivateQSG::setWidth(qreal width) { QMapPolylineObjectPrivateDefault::setWidth(width); - updateGeometry(); if (m_map) emit m_map->sgNodeChanged(); @@ -168,7 +194,12 @@ QGeoMapObjectPrivate *QMapPolylineObjectPrivateQSG::clone() QGeoShape QMapPolylineObjectPrivateQSG::geoShape() const { - return m_geoPath; + return m_path; +} + +void QMapPolylineObjectPrivateQSG::markSourceDirty() +{ + m_borderGeometry.markSourceDirty(); } QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h b/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h index e8eb5839..8bba2703 100644 --- a/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h +++ b/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h @@ -68,6 +68,7 @@ public: QList<QDoubleVector2D> projectPath(); // QQSGMapObject + void markSourceDirty(); void updateGeometry() override; QSGNode *updateMapObjectNode(QSGNode *oldNode, VisibleNode **visibleNode, @@ -84,10 +85,12 @@ public: QGeoMapObjectPrivate *clone() override; virtual QGeoShape geoShape() const override; + unsigned int zoomForLOD(int zoom) const; + // Data Members - QGeoPathEager m_geoPath; - QGeoMapPolylineGeometry m_geometry; - bool m_updatingGeometry = false; + QDoubleVector2D m_leftBoundMercator; + QGeoMapPolylineGeometryOpenGL m_borderGeometry; + MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; }; QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qqsgmapobject_p.h b/src/location/labs/qsg/qqsgmapobject_p.h index 52036805..0a717e62 100644 --- a/src/location/labs/qsg/qqsgmapobject_p.h +++ b/src/location/labs/qsg/qqsgmapobject_p.h @@ -51,7 +51,7 @@ #include <QtLocation/private/qlocationglobal_p.h> #include <QtQuick/QSGOpacityNode> #include <QtLocation/private/qgeomapobject_p.h> -#include <QtLocation/private/qdeclarativepolylinemapitem_p.h> +#include <QtLocation/private/qdeclarativepolylinemapitem_p_p.h> QT_BEGIN_NAMESPACE diff --git a/src/location/maps/qgeomap.cpp b/src/location/maps/qgeomap.cpp index dc8aa2c8..51af9199 100644 --- a/src/location/maps/qgeomap.cpp +++ b/src/location/maps/qgeomap.cpp @@ -41,6 +41,7 @@ #include "qdeclarativegeomapitembase_p.h" #include "qgeomapobject_p.h" #include "qgeomapobject_p_p.h" +#include <QtQuick/private/qquickitem_p.h> #include <QDebug> #include <QRectF> @@ -310,6 +311,12 @@ QList<QObject *> QGeoMap::mapObjectsAt(const QGeoCoordinate &/*coordinate*/) con return QList<QObject *>(); } +void QGeoMap::setItemToWindowTransform(const QTransform &itemToWindowTransform) +{ + Q_D(QGeoMap); + d->m_geoProjection->setItemToWindowTransform(itemToWindowTransform); +} + void QGeoMap::setVisibleArea(const QRectF &visibleArea) { Q_D(QGeoMap); diff --git a/src/location/maps/qgeomap_p.h b/src/location/maps/qgeomap_p.h index 216c8b64..e955b513 100644 --- a/src/location/maps/qgeomap_p.h +++ b/src/location/maps/qgeomap_p.h @@ -55,6 +55,7 @@ #include <QtPositioning/private/qdoublevector2d_p.h> #include <QtLocation/private/qgeoprojection_p.h> #include <QtLocation/qgeoroute.h> +#include <QTransform> QT_BEGIN_NAMESPACE @@ -67,6 +68,7 @@ class QQuickWindow; class QGeoMapParameter; class QDeclarativeGeoMapItemBase; class QGeoMapObject; +class QDeclarativeGeoMap; class Q_LOCATION_PRIVATE_EXPORT QGeoMap : public QObject { @@ -155,6 +157,7 @@ public: virtual void setCopyrightVisible(bool visible); virtual void removeMapObject(QGeoMapObject *obj); virtual QList<QObject *> mapObjectsAt(const QGeoCoordinate &coordinate) const; + virtual void setItemToWindowTransform(const QTransform &itemToWindowTransform); void setVisibleArea(const QRectF &visibleArea); QRectF visibleArea() const; diff --git a/src/location/maps/qgeoprojection.cpp b/src/location/maps/qgeoprojection.cpp index f64060e2..ff6a0b77 100644 --- a/src/location/maps/qgeoprojection.cpp +++ b/src/location/maps/qgeoprojection.cpp @@ -118,6 +118,19 @@ bool QGeoProjection::setBearing(qreal bearing, const QGeoCoordinate &coordinate) return false; } +void QGeoProjection::setItemToWindowTransform(const QTransform &itemToWindowTransform) +{ + if (m_itemToWindowTransform == itemToWindowTransform) + return; + m_qsgTransformDirty = true; + m_itemToWindowTransform = itemToWindowTransform; +} + +QTransform QGeoProjection::itemToWindowTransform() const +{ + return m_itemToWindowTransform; +} + /* * QGeoProjectionWebMercator implementation @@ -188,6 +201,31 @@ double QGeoProjectionWebMercator::minimumZoom() const return m_minimumZoom; } +QMatrix4x4 QGeoProjectionWebMercator::projectionTransformation() const +{ + return toMatrix4x4(m_transformation); +} + +QMatrix4x4 QGeoProjectionWebMercator::projectionTransformation_centered() const +{ + return toMatrix4x4(m_transformation0); +} + +const QMatrix4x4 &QGeoProjectionWebMercator::qsgTransform() const +{ + if (m_qsgTransformDirty) { + m_qsgTransformDirty = false; + m_qsgTransform = QMatrix4x4(m_itemToWindowTransform) * toMatrix4x4(m_transformation0); +// qDebug() << "QGeoProjectionWebMercator::qsgTransform" << m_itemToWindowTransform << toMatrix4x4(m_transformation0); + } + return m_qsgTransform; +} + +QDoubleVector3D QGeoProjectionWebMercator::centerMercator() const +{ + return geoToMapProjection(m_cameraData.center()).toVector3D(); +} + // This method recalculates the "no-trespassing" limits for the map center. // This has to be used when: // 1) the map is resized, because the meters per pixel remain the same, but @@ -273,19 +311,23 @@ QGeoCoordinate QGeoProjectionWebMercator::mapProjectionToGeo(const QDoubleVector return QWebMercator::mercatorToCoord(projection); } -//wraps around center -QDoubleVector2D QGeoProjectionWebMercator::wrapMapProjection(const QDoubleVector2D &projection) const +int QGeoProjectionWebMercator::projectionWrapFactor(const QDoubleVector2D &projection) const { - double x = projection.x(); + const double &x = projection.x(); if (m_cameraCenterXMercator < 0.5) { if (x - m_cameraCenterXMercator > 0.5 ) - x -= 1.0; + return -1; } else if (m_cameraCenterXMercator > 0.5) { if (x - m_cameraCenterXMercator < -0.5 ) - x += 1.0; + return 1; } + return 0; +} - return QDoubleVector2D(x, projection.y()); +//wraps around center +QDoubleVector2D QGeoProjectionWebMercator::wrapMapProjection(const QDoubleVector2D &projection) const +{ + return QDoubleVector2D(projection.x() + double(projectionWrapFactor(projection)), projection.y()); } QDoubleVector2D QGeoProjectionWebMercator::unwrapMapProjection(const QDoubleVector2D &wrappedProjection) const @@ -547,6 +589,7 @@ QGeoProjection::ProjectionType QGeoProjectionWebMercator::projectionType() const void QGeoProjectionWebMercator::setupCamera() { + m_qsgTransformDirty = true; m_centerMercator = geoToMapProjection(m_cameraData.center()); m_cameraCenterXMercator = m_centerMercator.x(); m_cameraCenterYMercator = m_centerMercator.y(); @@ -571,6 +614,10 @@ void QGeoProjectionWebMercator::setupCamera() // And in mercator space m_eyeMercator = m_centerMercator; m_eyeMercator.setZ(altitude_mercator / m_aperture); + m_eyeMercator0 = QDoubleVector3D(0,0,0); + m_eyeMercator0.setZ(altitude_mercator / m_aperture); + QDoubleVector3D eye0(0,0,0); + eye0.setZ(altitude * defaultTileSize / m_aperture); m_view = m_eye - m_center; QDoubleVector3D side = QDoubleVector3D::normal(m_view, QDoubleVector3D(0.0, 1.0, 0.0)); @@ -599,11 +646,13 @@ void QGeoProjectionWebMercator::setupCamera() QDoubleMatrix4x4 mTilt; mTilt.rotate(-m_cameraData.tilt(), m_side); m_eye = mTilt * m_view + m_center; + eye0 = mTilt * m_view; // In mercator space too QDoubleMatrix4x4 mTiltMercator; mTiltMercator.rotate(-m_cameraData.tilt(), m_sideMercator); m_eyeMercator = mTiltMercator * m_viewMercator + m_centerMercator; + m_eyeMercator0 = mTiltMercator * m_viewMercator; } m_view = m_eye - m_center; // ToDo: this should be inverted (center - eye), and the rest should follow @@ -634,8 +683,10 @@ void QGeoProjectionWebMercator::setupCamera() double verticalHalfFOV = QLocationUtils::degrees(atan(m_aperture)); - QDoubleMatrix4x4 cameraMatrix; - cameraMatrix.lookAt(m_eye, m_center, m_up); + m_cameraMatrix.setToIdentity(); + m_cameraMatrix.lookAt(m_eye, m_center, m_up); + m_cameraMatrix0.setToIdentity(); + m_cameraMatrix0.lookAt(eye0, QDoubleVector3D(0,0,0), m_up); QDoubleMatrix4x4 projectionMatrix; projectionMatrix.frustum(-m_halfWidth, m_halfWidth, -m_halfHeight, m_halfHeight, m_nearPlane, m_farPlane); @@ -656,10 +707,13 @@ void QGeoProjectionWebMercator::setupCamera() matScreenTransformation(0,3) = (0.5 + offsetPct.x()) * m_viewportWidth; matScreenTransformation(1,3) = (0.5 + offsetPct.y()) * m_viewportHeight; - m_transformation = matScreenTransformation * projectionMatrix * cameraMatrix; + m_transformation = matScreenTransformation * projectionMatrix * m_cameraMatrix; m_quickItemTransformation = m_transformation; m_transformation.scale(m_sideLengthPixels, m_sideLengthPixels, 1.0); + m_transformation0 = matScreenTransformation * projectionMatrix * m_cameraMatrix0; + m_transformation0.scale(m_sideLengthPixels, m_sideLengthPixels, 1.0); + m_centerNearPlane = m_eye - m_viewNormalized; m_centerNearPlaneMercator = m_eyeMercator - m_viewNormalized * m_nearPlaneMercator; diff --git a/src/location/maps/qgeoprojection_p.h b/src/location/maps/qgeoprojection_p.h index 2e1af8c5..9a75246a 100644 --- a/src/location/maps/qgeoprojection_p.h +++ b/src/location/maps/qgeoprojection_p.h @@ -52,6 +52,8 @@ #include <QtLocation/private/qgeocameradata_p.h> #include <QtPositioning/private/qdoublematrix4x4_p.h> #include <QtPositioning/QGeoShape> +#include <QMatrix4x4> +#include <QTransform> QT_BEGIN_NAMESPACE @@ -107,6 +109,17 @@ public: virtual QGeoShape visibleRegion() const; virtual bool setBearing(qreal bearing, const QGeoCoordinate &coordinate); + virtual QMatrix4x4 projectionTransformation() const = 0; // This brings a mercator coord into the correct viewport coordinate. + virtual QMatrix4x4 projectionTransformation_centered() const = 0; // Same as projectionTransformation, but the center of the camera is around 0,0. + // Requires subsequent shifting of the geometry to fit such camera. + virtual const QMatrix4x4 &qsgTransform() const = 0; + virtual QDoubleVector3D centerMercator() const = 0; + + void setItemToWindowTransform(const QTransform &itemToWindowTransform); + virtual QTransform itemToWindowTransform() const; + + QTransform m_itemToWindowTransform; + mutable bool m_qsgTransformDirty = true; }; class Q_LOCATION_PRIVATE_EXPORT QGeoProjectionWebMercator : public QGeoProjection @@ -117,6 +130,11 @@ public: // From QGeoProjection double minimumZoom() const override; + QMatrix4x4 projectionTransformation() const override; + QMatrix4x4 projectionTransformation_centered() const override; + const QMatrix4x4 &qsgTransform() const override; + QDoubleVector3D centerMercator() const override; + double maximumCenterLatitudeAtZoom(const QGeoCameraData &cameraData) const override; double minimumCenterLatitudeAtZoom(const QGeoCameraData &cameraData) const override; @@ -144,6 +162,7 @@ public: QDoubleVector2D geoToMapProjection(const QGeoCoordinate &coordinate) const; QGeoCoordinate mapProjectionToGeo(const QDoubleVector2D &projection) const; + int projectionWrapFactor(const QDoubleVector2D &projection) const; QDoubleVector2D wrapMapProjection(const QDoubleVector2D &projection) const; QDoubleVector2D unwrapMapProjection(const QDoubleVector2D &wrappedProjection) const; @@ -213,7 +232,10 @@ protected: double m_1_viewportWidth; double m_1_viewportHeight; + QDoubleMatrix4x4 m_cameraMatrix; + QDoubleMatrix4x4 m_cameraMatrix0; QDoubleMatrix4x4 m_transformation; + QDoubleMatrix4x4 m_transformation0; QDoubleMatrix4x4 m_quickItemTransformation; QDoubleVector3D m_eye; QDoubleVector3D m_up; @@ -234,6 +256,7 @@ protected: // For the clipping region QDoubleVector3D m_centerMercator; QDoubleVector3D m_eyeMercator; + QDoubleVector3D m_eyeMercator0; QDoubleVector3D m_viewMercator; QDoubleVector3D m_upMercator; QDoubleVector3D m_sideMercator; @@ -245,6 +268,8 @@ protected: QList<QDoubleVector2D> m_visibleRegionExpanded; QList<QDoubleVector2D> m_projectableRegion; bool m_visibleRegionDirty; + + mutable QMatrix4x4 m_qsgTransform; QRectF m_visibleArea; Q_DISABLE_COPY(QGeoProjectionWebMercator) diff --git a/src/location/maps/qgeotiledmap.cpp b/src/location/maps/qgeotiledmap.cpp index 74346fdb..e6c91042 100644 --- a/src/location/maps/qgeotiledmap.cpp +++ b/src/location/maps/qgeotiledmap.cpp @@ -329,7 +329,7 @@ void QGeoTiledMapPrivate::changeCameraData(const QGeoCameraData &cameraData) m_mapScene->setCameraData(cam); updateScene(); - q->sgNodeChanged(); + q->sgNodeChanged(); // ToDo: explain why emitting twice } void QGeoTiledMapPrivate::updateScene() @@ -371,7 +371,7 @@ void QGeoTiledMapPrivate::setVisibleArea(const QRectF &visibleArea) if (m_copyrightVisible) q->evaluateCopyrights(m_mapScene->visibleTiles()); updateScene(); - q->sgNodeChanged(); + q->sgNodeChanged(); // ToDo: explain why emitting twice } QRectF QGeoTiledMapPrivate::visibleArea() const diff --git a/src/plugins/geoservices/esri/geocodereply_esri.cpp b/src/plugins/geoservices/esri/geocodereply_esri.cpp index 05f941d1..51d97882 100644 --- a/src/plugins/geoservices/esri/geocodereply_esri.cpp +++ b/src/plugins/geoservices/esri/geocodereply_esri.cpp @@ -58,7 +58,7 @@ GeoCodeReplyEsri::GeoCodeReplyEsri(QNetworkReply *reply, OperationType operation return; } connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(networkReplyError(QNetworkReply::NetworkError))); connect(this, &QGeoCodeReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); @@ -84,7 +84,7 @@ void GeoCodeReplyEsri::networkReplyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); diff --git a/src/plugins/geoservices/esri/georoutereply_esri.cpp b/src/plugins/geoservices/esri/georoutereply_esri.cpp index 78e14a9b..8cadfb29 100644 --- a/src/plugins/geoservices/esri/georoutereply_esri.cpp +++ b/src/plugins/geoservices/esri/georoutereply_esri.cpp @@ -55,7 +55,7 @@ GeoRouteReplyEsri::GeoRouteReplyEsri(QNetworkReply *reply, const QGeoRouteReques return; } connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(networkReplyError(QNetworkReply::NetworkError))); connect(this, &QGeoRouteReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); @@ -70,7 +70,7 @@ void GeoRouteReplyEsri::networkReplyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); diff --git a/src/plugins/geoservices/esri/geotiledmapreply_esri.cpp b/src/plugins/geoservices/esri/geotiledmapreply_esri.cpp index 2f4016a9..1f72b1ea 100644 --- a/src/plugins/geoservices/esri/geotiledmapreply_esri.cpp +++ b/src/plugins/geoservices/esri/geotiledmapreply_esri.cpp @@ -56,7 +56,7 @@ GeoTiledMapReplyEsri::GeoTiledMapReplyEsri(QNetworkReply *reply, const QGeoTileS return; } connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(networkReplyError(QNetworkReply::NetworkError))); connect(this, &QGeoTiledMapReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); @@ -71,7 +71,7 @@ void GeoTiledMapReplyEsri::networkReplyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; QByteArray const& imageData = reply->readAll(); diff --git a/src/plugins/geoservices/esri/placemanagerengine_esri.cpp b/src/plugins/geoservices/esri/placemanagerengine_esri.cpp index 8f973ff3..525ec785 100644 --- a/src/plugins/geoservices/esri/placemanagerengine_esri.cpp +++ b/src/plugins/geoservices/esri/placemanagerengine_esri.cpp @@ -261,7 +261,7 @@ void PlaceManagerEngineEsri::initializeGeocodeServer() { m_geocodeServerReply = m_networkManager->get(QNetworkRequest(kUrlGeocodeServer)); connect(m_geocodeServerReply, SIGNAL(finished()), this, SLOT(geocodeServerReplyFinished())); - connect(m_geocodeServerReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(geocodeServerReplyError())); + connect(m_geocodeServerReply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(geocodeServerReplyError())); } } diff --git a/src/plugins/geoservices/esri/placesearchreply_esri.cpp b/src/plugins/geoservices/esri/placesearchreply_esri.cpp index b674c136..72c080f5 100644 --- a/src/plugins/geoservices/esri/placesearchreply_esri.cpp +++ b/src/plugins/geoservices/esri/placesearchreply_esri.cpp @@ -87,7 +87,7 @@ PlaceSearchReplyEsri::PlaceSearchReplyEsri(const QPlaceSearchRequest &request, Q setRequest(request); connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkError(QNetworkReply::NetworkError))); + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(networkError(QNetworkReply::NetworkError))); connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); } @@ -109,7 +109,7 @@ void PlaceSearchReplyEsri::replyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); diff --git a/src/plugins/geoservices/geoservices.pro b/src/plugins/geoservices/geoservices.pro index b81ad34a..c67b5f5b 100644 --- a/src/plugins/geoservices/geoservices.pro +++ b/src/plugins/geoservices/geoservices.pro @@ -8,7 +8,8 @@ qtConfig(geoservices_esri): SUBDIRS += esri qtConfig(geoservices_itemsoverlay): SUBDIRS += itemsoverlay qtConfig(geoservices_osm): SUBDIRS += osm -qtConfig(geoservices_mapboxgl) { +# for now turned off in Qt 6 +false:qtConfig(geoservices_mapboxgl) { !exists(../../3rdparty/mapbox-gl-native/mapbox-gl-native.pro) { warning("Submodule mapbox-gl-native does not exist. Run 'git submodule update --init' on qtlocation.") } else { diff --git a/src/plugins/geoservices/mapbox/qgeocodereplymapbox.cpp b/src/plugins/geoservices/mapbox/qgeocodereplymapbox.cpp index 7e94e6e0..aabe82a4 100644 --- a/src/plugins/geoservices/mapbox/qgeocodereplymapbox.cpp +++ b/src/plugins/geoservices/mapbox/qgeocodereplymapbox.cpp @@ -60,8 +60,7 @@ QGeoCodeReplyMapbox::QGeoCodeReplyMapbox(QNetworkReply *reply, QObject *parent) } connect(reply, &QNetworkReply::finished, this, &QGeoCodeReplyMapbox::onNetworkReplyFinished); - connect(reply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), - this, &QGeoCodeReplyMapbox::onNetworkReplyError); + connect(reply, &QNetworkReply::errorOccurred, this, &QGeoCodeReplyMapbox::onNetworkReplyError); connect(this, &QGeoCodeReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); @@ -76,7 +75,7 @@ void QGeoCodeReplyMapbox::onNetworkReplyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; QList<QGeoLocation> locations; diff --git a/src/plugins/geoservices/mapbox/qgeomapreplymapbox.cpp b/src/plugins/geoservices/mapbox/qgeomapreplymapbox.cpp index c10aba19..4472fdc0 100644 --- a/src/plugins/geoservices/mapbox/qgeomapreplymapbox.cpp +++ b/src/plugins/geoservices/mapbox/qgeomapreplymapbox.cpp @@ -46,7 +46,7 @@ QGeoMapReplyMapbox::QGeoMapReplyMapbox(QNetworkReply *reply, const QGeoTileSpec return; } connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(networkReplyError(QNetworkReply::NetworkError))); connect(this, &QGeoTiledMapReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); @@ -61,7 +61,7 @@ void QGeoMapReplyMapbox::networkReplyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; setMapImageData(reply->readAll()); diff --git a/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp b/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp index f10b0086..cc270181 100644 --- a/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp +++ b/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp @@ -101,7 +101,7 @@ QGeoRouteReplyMapbox::QGeoRouteReplyMapbox(QNetworkReply *reply, const QGeoRoute return; } connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(networkReplyError(QNetworkReply::NetworkError))); connect(this, &QGeoRouteReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); @@ -116,7 +116,7 @@ void QGeoRouteReplyMapbox::networkReplyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; QGeoRoutingManagerEngineMapbox *engine = qobject_cast<QGeoRoutingManagerEngineMapbox *>(parent()); diff --git a/src/plugins/geoservices/mapbox/qplacesearchreplymapbox.cpp b/src/plugins/geoservices/mapbox/qplacesearchreplymapbox.cpp index 7284b67a..6d37d803 100644 --- a/src/plugins/geoservices/mapbox/qplacesearchreplymapbox.cpp +++ b/src/plugins/geoservices/mapbox/qplacesearchreplymapbox.cpp @@ -142,8 +142,7 @@ QPlaceSearchReplyMapbox::QPlaceSearchReplyMapbox(const QPlaceSearchRequest &requ setRequest(request); connect(reply, &QNetworkReply::finished, this, &QPlaceSearchReplyMapbox::onReplyFinished); - connect(reply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), - this, &QPlaceSearchReplyMapbox::onNetworkError); + connect(reply, &QNetworkReply::errorOccurred, this, &QPlaceSearchReplyMapbox::onNetworkError); connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); @@ -167,7 +166,7 @@ void QPlaceSearchReplyMapbox::onReplyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); diff --git a/src/plugins/geoservices/mapbox/qplacesearchsuggestionreplymapbox.cpp b/src/plugins/geoservices/mapbox/qplacesearchsuggestionreplymapbox.cpp index 95296172..b8fc315f 100644 --- a/src/plugins/geoservices/mapbox/qplacesearchsuggestionreplymapbox.cpp +++ b/src/plugins/geoservices/mapbox/qplacesearchsuggestionreplymapbox.cpp @@ -61,8 +61,7 @@ QPlaceSearchSuggestionReplyMapbox::QPlaceSearchSuggestionReplyMapbox(QNetworkRep } connect(reply, &QNetworkReply::finished, this, &QPlaceSearchSuggestionReplyMapbox::onReplyFinished); - connect(reply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), - this, &QPlaceSearchSuggestionReplyMapbox::onNetworkError); + connect(reply, &QNetworkReply::errorOccurred, this, &QPlaceSearchSuggestionReplyMapbox::onNetworkError); connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); @@ -86,7 +85,7 @@ void QPlaceSearchSuggestionReplyMapbox::onReplyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); diff --git a/src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp b/src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp index cb8dc2be..6dc8ea70 100644 --- a/src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp +++ b/src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp @@ -43,6 +43,7 @@ #include <QtPositioning/QGeoPath> #include <QtPositioning/QGeoPolygon> #include <QtQml/QJSValue> +#include <QtLocation/private/qdeclarativecirclemapitem_p_p.h> namespace { @@ -93,12 +94,12 @@ QMapbox::Feature featureFromMapCircle(QDeclarativeCircleMapItem *mapItem) const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(mapItem->map()->geoProjection()); QList<QGeoCoordinate> path; QGeoCoordinate leftBound; - QDeclarativeCircleMapItem::calculatePeripheralPoints(path, mapItem->center(), mapItem->radius(), circleSamples, leftBound); + QDeclarativeCircleMapItemPrivateCPU::calculatePeripheralPoints(path, mapItem->center(), mapItem->radius(), circleSamples, leftBound); QList<QDoubleVector2D> pathProjected; for (const QGeoCoordinate &c : qAsConst(path)) pathProjected << p.geoToMapProjection(c); - if (QDeclarativeCircleMapItem::crossEarthPole(mapItem->center(), mapItem->radius())) - mapItem->preserveCircleGeometry(pathProjected, mapItem->center(), mapItem->radius(), p); + if (QDeclarativeCircleMapItemPrivateCPU::crossEarthPole(mapItem->center(), mapItem->radius())) + QDeclarativeCircleMapItemPrivateCPU::preserveCircleGeometry(pathProjected, mapItem->center(), mapItem->radius(), p); path.clear(); for (const QDoubleVector2D &c : qAsConst(pathProjected)) path << p.mapProjectionToGeo(c); diff --git a/src/plugins/geoservices/nokia/placesv2/qplacecontentreplyimpl.cpp b/src/plugins/geoservices/nokia/placesv2/qplacecontentreplyimpl.cpp index 196b68d3..7569fbb8 100644 --- a/src/plugins/geoservices/nokia/placesv2/qplacecontentreplyimpl.cpp +++ b/src/plugins/geoservices/nokia/placesv2/qplacecontentreplyimpl.cpp @@ -58,7 +58,7 @@ QPlaceContentReplyImpl::QPlaceContentReplyImpl(const QPlaceContentRequest &reque setRequest(request); connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError))); connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); @@ -81,7 +81,7 @@ void QPlaceContentReplyImpl::replyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); diff --git a/src/plugins/geoservices/nokia/placesv2/qplacedetailsreplyimpl.cpp b/src/plugins/geoservices/nokia/placesv2/qplacedetailsreplyimpl.cpp index 677efddc..f89a033c 100644 --- a/src/plugins/geoservices/nokia/placesv2/qplacedetailsreplyimpl.cpp +++ b/src/plugins/geoservices/nokia/placesv2/qplacedetailsreplyimpl.cpp @@ -95,7 +95,7 @@ QPlaceDetailsReplyImpl::QPlaceDetailsReplyImpl(QNetworkReply *reply, return; } connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError))); connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); @@ -118,7 +118,7 @@ void QPlaceDetailsReplyImpl::replyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); diff --git a/src/plugins/geoservices/nokia/placesv2/qplacesearchreplyhere.cpp b/src/plugins/geoservices/nokia/placesv2/qplacesearchreplyhere.cpp index 49574084..ce5c2ac6 100644 --- a/src/plugins/geoservices/nokia/placesv2/qplacesearchreplyhere.cpp +++ b/src/plugins/geoservices/nokia/placesv2/qplacesearchreplyhere.cpp @@ -64,7 +64,7 @@ QPlaceSearchReplyHere::QPlaceSearchReplyHere(const QPlaceSearchRequest &request, setRequest(request); connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError))); connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); @@ -87,7 +87,7 @@ void QPlaceSearchReplyHere::replyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); diff --git a/src/plugins/geoservices/nokia/placesv2/qplacesearchsuggestionreplyimpl.cpp b/src/plugins/geoservices/nokia/placesv2/qplacesearchsuggestionreplyimpl.cpp index 236af184..c654a4e1 100644 --- a/src/plugins/geoservices/nokia/placesv2/qplacesearchsuggestionreplyimpl.cpp +++ b/src/plugins/geoservices/nokia/placesv2/qplacesearchsuggestionreplyimpl.cpp @@ -53,7 +53,7 @@ QPlaceSearchSuggestionReplyImpl::QPlaceSearchSuggestionReplyImpl(QNetworkReply * return; } connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError))); connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); @@ -77,7 +77,7 @@ void QPlaceSearchSuggestionReplyImpl::replyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); diff --git a/src/plugins/geoservices/nokia/qgeocodereply_nokia.cpp b/src/plugins/geoservices/nokia/qgeocodereply_nokia.cpp index 2b302a76..6e1d1270 100644 --- a/src/plugins/geoservices/nokia/qgeocodereply_nokia.cpp +++ b/src/plugins/geoservices/nokia/qgeocodereply_nokia.cpp @@ -62,7 +62,7 @@ QGeoCodeReplyNokia::QGeoCodeReplyNokia(QNetworkReply *reply, int limit, int offs qRegisterMetaType<QList<QGeoLocation> >(); connect(reply, SIGNAL(finished()), this, SLOT(networkFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(networkError(QNetworkReply::NetworkError))); connect(this, &QGeoCodeReply::aborted, reply, &QNetworkReply::abort); connect(this, &QGeoCodeReply::aborted, [this](){ m_parsing = false; }); @@ -83,7 +83,7 @@ void QGeoCodeReplyNokia::networkFinished() QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; QGeoCodeJsonParser *parser = new QGeoCodeJsonParser; // QRunnable, autoDelete = true. diff --git a/src/plugins/geoservices/nokia/qgeomapreply_nokia.cpp b/src/plugins/geoservices/nokia/qgeomapreply_nokia.cpp index 1ae32004..20ee9b76 100644 --- a/src/plugins/geoservices/nokia/qgeomapreply_nokia.cpp +++ b/src/plugins/geoservices/nokia/qgeomapreply_nokia.cpp @@ -54,7 +54,7 @@ QGeoMapReplyNokia::QGeoMapReplyNokia(QNetworkReply *reply, const QGeoTileSpec &s SLOT(networkFinished())); connect(reply, - SIGNAL(error(QNetworkReply::NetworkError)), + SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(networkError(QNetworkReply::NetworkError))); connect(this, &QGeoTiledMapReply::aborted, reply, &QNetworkReply::abort); @@ -70,7 +70,7 @@ void QGeoMapReplyNokia::networkFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; setMapImageData(reply->readAll()); diff --git a/src/plugins/geoservices/nokia/qgeoroutereply_nokia.cpp b/src/plugins/geoservices/nokia/qgeoroutereply_nokia.cpp index 6aed85cc..ba64d8ae 100644 --- a/src/plugins/geoservices/nokia/qgeoroutereply_nokia.cpp +++ b/src/plugins/geoservices/nokia/qgeoroutereply_nokia.cpp @@ -60,7 +60,7 @@ QGeoRouteReplyNokia::QGeoRouteReplyNokia(const QGeoRouteRequest &request, continue; } connect(reply, SIGNAL(finished()), this, SLOT(networkFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(networkError(QNetworkReply::NetworkError))); connect(this, &QGeoRouteReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); @@ -80,8 +80,8 @@ void QGeoRouteReplyNokia::networkFinished() QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError - && reply->networkError() != QNetworkReply::UnknownContentError) { + if (reply->error() != QNetworkReply::NoError + && reply->error() != QNetworkReply::UnknownContentError) { return; } diff --git a/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp b/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp index 2e16a5d8..d07a93ba 100644 --- a/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp +++ b/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp @@ -247,7 +247,7 @@ QString QGeoTileFetcherNokia::applicationId() const void QGeoTileFetcherNokia::copyrightsFetched() { - if (m_engineNokia && m_copyrightsReply->networkError() == QNetworkReply::NoError) { + if (m_engineNokia && m_copyrightsReply->error() == QNetworkReply::NoError) { QMetaObject::invokeMethod(m_engineNokia.data(), "loadCopyrightsDescriptorsFromJson", Qt::QueuedConnection, @@ -259,7 +259,7 @@ void QGeoTileFetcherNokia::copyrightsFetched() void QGeoTileFetcherNokia::versionFetched() { - if (m_engineNokia && m_versionReply->networkError() == QNetworkReply::NoError) { + if (m_engineNokia && m_versionReply->error() == QNetworkReply::NoError) { QMetaObject::invokeMethod(m_engineNokia.data(), "parseNewVersionInfo", Qt::QueuedConnection, @@ -288,7 +288,7 @@ void QGeoTileFetcherNokia::fetchCopyrightsData() QNetworkRequest netRequest((QUrl(copyrightUrl))); m_copyrightsReply = m_networkManager->get(netRequest); - if (m_copyrightsReply->networkError() != QNetworkReply::NoError) { + if (m_copyrightsReply->error() != QNetworkReply::NoError) { qWarning() << __FUNCTION__ << m_copyrightsReply->errorString(); m_copyrightsReply->deleteLater(); return; @@ -321,7 +321,7 @@ void QGeoTileFetcherNokia::fetchVersionData() QNetworkRequest netRequest((QUrl(versionUrl))); m_versionReply = m_networkManager->get(netRequest); - if (m_versionReply->networkError() != QNetworkReply::NoError) { + if (m_versionReply->error() != QNetworkReply::NoError) { qWarning() << __FUNCTION__ << m_versionReply->errorString(); m_versionReply->deleteLater(); return; diff --git a/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp b/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp index acf43a01..c5c05a2e 100644 --- a/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp +++ b/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp @@ -637,7 +637,7 @@ QPlaceReply *QPlaceManagerEngineNokiaV2::initializeCategories() QStringLiteral("/places/v1/categories/places/") + *it); QNetworkReply *networkReply = sendRequest(requestUrl); connect(networkReply, SIGNAL(finished()), this, SLOT(categoryReplyFinished())); - connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(networkReply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(categoryReplyError())); m_categoryRequests.insert(*it, networkReply); @@ -777,7 +777,7 @@ void QPlaceManagerEngineNokiaV2::categoryReplyFinished() QString categoryId; - if (reply->networkError() == QNetworkReply::NoError) { + if (reply->error() == QNetworkReply::NoError) { QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); if (!document.isObject()) { if (m_categoryReply) { diff --git a/src/plugins/geoservices/osm/qgeocodereplyosm.cpp b/src/plugins/geoservices/osm/qgeocodereplyosm.cpp index 145a26ca..e309c87f 100644 --- a/src/plugins/geoservices/osm/qgeocodereplyosm.cpp +++ b/src/plugins/geoservices/osm/qgeocodereplyosm.cpp @@ -58,7 +58,7 @@ QGeoCodeReplyOsm::QGeoCodeReplyOsm(QNetworkReply *reply, bool includeExtraData, return; } connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(networkReplyError(QNetworkReply::NetworkError))); connect(this, &QGeoCodeReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); @@ -129,7 +129,7 @@ void QGeoCodeReplyOsm::networkReplyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; QList<QGeoLocation> locations; diff --git a/src/plugins/geoservices/osm/qgeomapreplyosm.cpp b/src/plugins/geoservices/osm/qgeomapreplyosm.cpp index b83d9015..1d693455 100644 --- a/src/plugins/geoservices/osm/qgeomapreplyosm.cpp +++ b/src/plugins/geoservices/osm/qgeomapreplyosm.cpp @@ -52,7 +52,7 @@ QGeoMapReplyOsm::QGeoMapReplyOsm(QNetworkReply *reply, return; } connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(networkReplyError(QNetworkReply::NetworkError))); connect(this, &QGeoTiledMapReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); @@ -68,7 +68,7 @@ void QGeoMapReplyOsm::networkReplyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) // Already handled in networkReplyError + if (reply->error() != QNetworkReply::NoError) // Already handled in networkReplyError return; QByteArray a = reply->readAll(); diff --git a/src/plugins/geoservices/osm/qgeoroutereplyosm.cpp b/src/plugins/geoservices/osm/qgeoroutereplyosm.cpp index 45e6b2c3..255ccfe5 100644 --- a/src/plugins/geoservices/osm/qgeoroutereplyosm.cpp +++ b/src/plugins/geoservices/osm/qgeoroutereplyosm.cpp @@ -51,7 +51,7 @@ QGeoRouteReplyOsm::QGeoRouteReplyOsm(QNetworkReply *reply, const QGeoRouteReques return; } connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(networkReplyError(QNetworkReply::NetworkError))); connect(this, &QGeoRouteReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); @@ -66,7 +66,7 @@ void QGeoRouteReplyOsm::networkReplyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; QGeoRoutingManagerEngineOsm *engine = qobject_cast<QGeoRoutingManagerEngineOsm *>(parent()); diff --git a/src/plugins/geoservices/osm/qgeotileproviderosm.cpp b/src/plugins/geoservices/osm/qgeotileproviderosm.cpp index 13b9f47e..dfe3d8e9 100644 --- a/src/plugins/geoservices/osm/qgeotileproviderosm.cpp +++ b/src/plugins/geoservices/osm/qgeotileproviderosm.cpp @@ -339,7 +339,7 @@ void TileProvider::resolveProvider() request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork); QNetworkReply *reply = m_nm->get(request); connect(reply, SIGNAL(finished()), this, SLOT(onNetworkReplyFinished()) ); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onNetworkReplyError(QNetworkReply::NetworkError))); + connect(reply, SIGNAL(errorOccured(QNetworkReply::NetworkError)), this, SLOT(onNetworkReplyError(QNetworkReply::NetworkError))); } void TileProvider::handleError(QNetworkReply::NetworkError error) @@ -383,8 +383,8 @@ void TileProvider::onNetworkReplyFinished() QObject errorEmitter; QMetaObject::Connection errorEmitterConnection = connect(&errorEmitter, &QObject::destroyed, [this](){ this->resolutionError(this); }); - if (reply->networkError() != QNetworkReply::NoError) { - handleError(reply->networkError()); + if (reply->error() != QNetworkReply::NoError) { + handleError(reply->error()); return; } m_status = Invalid; diff --git a/src/plugins/geoservices/osm/qplacemanagerengineosm.cpp b/src/plugins/geoservices/osm/qplacemanagerengineosm.cpp index 80964d35..d6344989 100644 --- a/src/plugins/geoservices/osm/qplacemanagerengineosm.cpp +++ b/src/plugins/geoservices/osm/qplacemanagerengineosm.cpp @@ -354,6 +354,6 @@ void QPlaceManagerEngineOsm::fetchNextCategoryLocale() m_categoriesReply = m_networkManager->get(QNetworkRequest(requestUrl)); connect(m_categoriesReply, SIGNAL(finished()), this, SLOT(categoryReplyFinished())); - connect(m_categoriesReply, SIGNAL(error(QNetworkReply::NetworkError)), + connect(m_categoriesReply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), this, SLOT(categoryReplyError())); } diff --git a/src/plugins/geoservices/osm/qplacesearchreplyosm.cpp b/src/plugins/geoservices/osm/qplacesearchreplyosm.cpp index ba6eb81c..593f476a 100644 --- a/src/plugins/geoservices/osm/qplacesearchreplyosm.cpp +++ b/src/plugins/geoservices/osm/qplacesearchreplyosm.cpp @@ -64,7 +64,8 @@ QPlaceSearchReplyOsm::QPlaceSearchReplyOsm(const QPlaceSearchRequest &request, setRequest(request); connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkError(QNetworkReply::NetworkError))); + connect(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)), + this, SLOT(networkError(QNetworkReply::NetworkError))); connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort); connect(this, &QObject::destroyed, reply, &QObject::deleteLater); } @@ -99,7 +100,7 @@ void QPlaceSearchReplyOsm::replyFinished() QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); reply->deleteLater(); - if (reply->networkError() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) return; QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); diff --git a/src/plugins/position/serialnmea/qiopipe.cpp b/src/plugins/position/serialnmea/qiopipe.cpp index ce908d4d..fab0b56b 100644 --- a/src/plugins/position/serialnmea/qiopipe.cpp +++ b/src/plugins/position/serialnmea/qiopipe.cpp @@ -50,7 +50,15 @@ QT_BEGIN_NAMESPACE QIOPipePrivate::QIOPipePrivate(QIODevice *iodevice, bool proxying) : m_proxying(proxying), source(iodevice) { - const QIOPipe *parentPipe = qobject_cast<QIOPipe *>(iodevice); +} + +QIOPipePrivate::~QIOPipePrivate() +{ +} + +void QIOPipePrivate::initialize() +{ + const QIOPipe *parentPipe = qobject_cast<QIOPipe *>(source); if (parentPipe && parentPipe->d_func()->m_proxying) // with proxying parent, return; // don't do anything @@ -60,10 +68,6 @@ QIOPipePrivate::QIOPipePrivate(QIODevice *iodevice, bool proxying) QObjectPrivate::connect(source, &QIODevice::readyRead, this, &QIOPipePrivate::_q_onReadyRead); } -QIOPipePrivate::~QIOPipePrivate() -{ -} - bool QIOPipePrivate::readAvailableData() { if (!source) return false; @@ -130,6 +134,7 @@ void QIOPipePrivate::removeChildPipe(QIOPipe *childPipe) QIOPipe::QIOPipe(QIODevice *parent, Mode mode) : QIODevice(*new QIOPipePrivate(parent, mode == ProxyPipe), parent) { + this->d_func()->initialize(); if (!parent->isOpen() && !parent->open(QIODevice::ReadOnly)) { qWarning() << "QIOPipe: Failed to open " << parent; return; diff --git a/src/plugins/position/serialnmea/qiopipe_p.h b/src/plugins/position/serialnmea/qiopipe_p.h index 25758fcb..472ab102 100644 --- a/src/plugins/position/serialnmea/qiopipe_p.h +++ b/src/plugins/position/serialnmea/qiopipe_p.h @@ -95,6 +95,7 @@ public: explicit QIOPipePrivate(QIODevice *iodevice, bool proxying); ~QIOPipePrivate() override; + void initialize(); bool readAvailableData(); void pumpData(const QByteArray &ba); void pushData(const QByteArray &ba); diff --git a/src/positioning/qgeopolygon.cpp b/src/positioning/qgeopolygon.cpp index 301759df..7d8a0ce0 100644 --- a/src/positioning/qgeopolygon.cpp +++ b/src/positioning/qgeopolygon.cpp @@ -673,6 +673,7 @@ QGeoShapePrivate *QGeoPolygonPrivateEager::clone() const void QGeoPolygonPrivateEager::translate(double degreesLatitude, double degreesLongitude) { translatePoly(m_path, m_holesList, m_bbox, degreesLatitude, degreesLongitude, m_maxLati, m_minLati); + m_leftBoundWrapped = QWebMercator::coordToMercator(m_bbox.topLeft()).x(); m_clipperDirty = true; } diff --git a/src/positioning/qlocationutils_p.h b/src/positioning/qlocationutils_p.h index e2d739e7..563db200 100644 --- a/src/positioning/qlocationutils_p.h +++ b/src/positioning/qlocationutils_p.h @@ -258,6 +258,13 @@ public: return wrapLong(centerLongitude - leftOffset); } + inline static void split_double(double input, float *hipart, float *lopart) + { + *hipart = (float) input; + double delta = input - ((double) *hipart); + *lopart = (float) delta; + } + static qreal metersPerPixel(qreal zoomLevel, const QGeoCoordinate &coordinate) { const qreal metersPerTile = earthMeanCircumference() * std::cos(radians(coordinate.latitude())) / std::pow(2, zoomLevel); diff --git a/src/positioningquick/qdeclarativepositionsource.cpp b/src/positioningquick/qdeclarativepositionsource.cpp index d6c62147..cf160541 100644 --- a/src/positioningquick/qdeclarativepositionsource.cpp +++ b/src/positioningquick/qdeclarativepositionsource.cpp @@ -280,7 +280,7 @@ void QDeclarativePositionSource::setNmeaSource(const QUrl &nmeaSource) delete m_nmeaSocket; m_nmeaSocket = new QTcpSocket(); - connect(m_nmeaSocket, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)> (&QAbstractSocket::error), + connect(m_nmeaSocket, &QAbstractSocket::errorOccurred, this, &QDeclarativePositionSource::socketError); connect(m_nmeaSocket, &QTcpSocket::connected, this, &QDeclarativePositionSource::socketConnected); |