From c062403a894a270101ae821fd73fa848f918ac54 Mon Sep 17 00:00:00 2001 From: Paolo Angelelli Date: Sat, 6 Aug 2016 21:26:10 +0200 Subject: Use geoshapes as geo-model in qdeclarativegeomapitems This patch uses QGeoShapes as data model inside the QDeclarativeMapItemBase subclasses. Advantages: - avoids duplication of implementation for geo-related methods (now only inside QGeoShapes). - provides map items a bounding box, usable for area based item searching ,or simple viewport fitting - allows to follow the graphics-view pattern, and have the same items visualized in different Maps (not yet supported). Change-Id: I6eec738fef5d753d90bdeeb4b109be89155b25f8 Reviewed-by: Alex Blasche --- src/imports/location/qdeclarativecirclemapitem.cpp | 48 ++--- src/imports/location/qdeclarativecirclemapitem_p.h | 6 +- .../location/qdeclarativegeomapitembase_p.h | 4 + .../location/qdeclarativegeomapquickitem.cpp | 11 + .../location/qdeclarativegeomapquickitem_p.h | 4 + .../location/qdeclarativepolygonmapitem.cpp | 131 +++++------- .../location/qdeclarativepolygonmapitem_p.h | 8 +- .../location/qdeclarativepolylinemapitem.cpp | 235 ++++++--------------- .../location/qdeclarativepolylinemapitem_p.h | 14 +- .../location/qdeclarativerectanglemapitem.cpp | 84 ++++---- .../location/qdeclarativerectanglemapitem_p.h | 4 +- src/imports/location/qgeomapitemgeometry_p.h | 2 +- 12 files changed, 213 insertions(+), 338 deletions(-) (limited to 'src/imports') diff --git a/src/imports/location/qdeclarativecirclemapitem.cpp b/src/imports/location/qdeclarativecirclemapitem.cpp index f5520e4b..8b1c0258 100644 --- a/src/imports/location/qdeclarativecirclemapitem.cpp +++ b/src/imports/location/qdeclarativecirclemapitem.cpp @@ -351,7 +351,6 @@ static void calculatePeripheralPoints(QList &path, qreal sinRatio = std::sin(ratio); qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio; qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio; - for (int i = 0; i < steps; ++i) { qreal azimuthRad = 2 * M_PI * i / steps; qreal resultLatRad = std::asin(sinLatRad_x_cosRatio @@ -366,7 +365,7 @@ static void calculatePeripheralPoints(QList &path, } QDeclarativeCircleMapItem::QDeclarativeCircleMapItem(QQuickItem *parent) -: QDeclarativeGeoMapItemBase(parent), color_(Qt::transparent), radius_(0), dirtyMaterial_(true), +: QDeclarativeGeoMapItemBase(parent), color_(Qt::transparent), dirtyMaterial_(true), updatingGeometry_(false) { setFlag(ItemHasContents, true); @@ -427,17 +426,17 @@ void QDeclarativeCircleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *ma */ void QDeclarativeCircleMapItem::setCenter(const QGeoCoordinate ¢er) { - if (center_ == center) + if (circle_.center() == center) return; - center_ = center; + circle_.setCenter(center); markSourceDirtyAndUpdate(); - emit centerChanged(center_); + emit centerChanged(center); } QGeoCoordinate QDeclarativeCircleMapItem::center() { - return center_; + return circle_.center(); } /*! @@ -470,17 +469,17 @@ QColor QDeclarativeCircleMapItem::color() const */ void QDeclarativeCircleMapItem::setRadius(qreal radius) { - if (radius_ == radius) + if (circle_.radius() == radius) return; - radius_ = radius; + circle_.setRadius(radius); markSourceDirtyAndUpdate(); emit radiusChanged(radius); } qreal QDeclarativeCircleMapItem::radius() const { - return radius_; + return circle_.radius(); } /*! @@ -522,7 +521,7 @@ QSGNode *QDeclarativeCircleMapItem::updateMapItemPaintNode(QSGNode *oldNode, Upd */ void QDeclarativeCircleMapItem::updatePolish() { - if (!map() || !center().isValid() || qIsNaN(radius_) || radius_ <= 0.0) + if (!map() || !circle_.isValid()) return; QScopedValueRollback rollback(updatingGeometry_); @@ -530,19 +529,18 @@ void QDeclarativeCircleMapItem::updatePolish() if (geometry_.isSourceDirty()) { circlePath_.clear(); - calculatePeripheralPoints(circlePath_, center_, radius_, CircleSamples); - geoLeftBound_ = QGeoCircle(center(), radius()).boundingGeoRectangle().topLeft(); + calculatePeripheralPoints(circlePath_, circle_.center(), circle_.radius(), CircleSamples); } QList originalCirclePath = circlePath_; int pathCount = circlePath_.size(); - bool preserve = preserveCircleGeometry(circlePath_, center_, radius_); - geometry_.setPreserveGeometry(true, geoLeftBound_); // to set the geoLeftBound_ - geometry_.setPreserveGeometry(preserve, geoLeftBound_); + bool preserve = preserveCircleGeometry(circlePath_, circle_.center(), circle_.radius()); + geometry_.setPreserveGeometry(true, circle_.boundingGeoRectangle().topLeft()); // to set the geoLeftBound_ + geometry_.setPreserveGeometry(preserve, circle_.boundingGeoRectangle().topLeft()); bool invertedCircle = false; - if (crossEarthPole(center_, radius_) && circlePath_.size() == pathCount) { + if (crossEarthPole(circle_.center(), circle_.radius()) && circlePath_.size() == pathCount) { geometry_.updateScreenPointsInvert(circlePath_, *map()); // invert fill area for really huge circles invertedCircle = true; } else { @@ -558,19 +556,14 @@ void QDeclarativeCircleMapItem::updatePolish() QList closedPath = circlePath_; closedPath << closedPath.first(); - QGeoCoordinate lb = geoLeftBound_; if (invertedCircle) { closedPath = originalCirclePath; closedPath << closedPath.first(); std::reverse(closedPath.begin(), closedPath.end()); - - double circumferenceRadius = QLocationUtils::earthMeanDiameter() * 0.5 - radius(); - QGeoCoordinate circumferenceCenter = QLocationUtils::antipodalPoint(center()); - lb = QGeoCircle(circumferenceCenter, circumferenceRadius).boundingGeoRectangle().topLeft(); } - borderGeometry_.setPreserveGeometry(true, geoLeftBound_); // to set the geoLeftBound_ - borderGeometry_.setPreserveGeometry(preserve, geoLeftBound_); + borderGeometry_.setPreserveGeometry(true, circle_.boundingGeoRectangle().topLeft()); // to set the geoLeftBound_ + borderGeometry_.setPreserveGeometry(preserve, circle_.boundingGeoRectangle().topLeft()); // Use srcOrigin_ from fill geometry after clipping to ensure that translateToCommonOrigin won't fail. const QGeoCoordinate &geometryOrigin = geometry_.origin(); @@ -605,9 +598,7 @@ void QDeclarativeCircleMapItem::afterViewportChanged(const QGeoMapViewportChange if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) return; - geometry_.markSourceDirty(); - borderGeometry_.markSourceDirty(); - polishAndUpdate(); + markSourceDirtyAndUpdate(); } /*! @@ -618,6 +609,11 @@ bool QDeclarativeCircleMapItem::contains(const QPointF &point) const return (geometry_.contains(point) || borderGeometry_.contains(point)); } +const QGeoShape &QDeclarativeCircleMapItem::geoShape() const +{ + return circle_; +} + /*! \internal */ diff --git a/src/imports/location/qdeclarativecirclemapitem_p.h b/src/imports/location/qdeclarativecirclemapitem_p.h index 0305000d..08e5e21b 100644 --- a/src/imports/location/qdeclarativecirclemapitem_p.h +++ b/src/imports/location/qdeclarativecirclemapitem_p.h @@ -53,6 +53,7 @@ #include "qdeclarativepolygonmapitem_p.h" #include #include +#include QT_BEGIN_NAMESPACE @@ -91,6 +92,7 @@ public: QDeclarativeMapLineProperties *border(); bool contains(const QPointF &point) const Q_DECL_OVERRIDE; + const QGeoShape &geoShape() const Q_DECL_OVERRIDE; Q_SIGNALS: void centerChanged(const QGeoCoordinate ¢er); @@ -112,11 +114,9 @@ private: qreal distance); private: - QGeoCoordinate center_; + QGeoCircle circle_; QDeclarativeMapLineProperties border_; QColor color_; - qreal radius_; - QGeoCoordinate geoLeftBound_; QList circlePath_; bool dirtyMaterial_; QGeoMapCircleGeometry geometry_; diff --git a/src/imports/location/qdeclarativegeomapitembase_p.h b/src/imports/location/qdeclarativegeomapitembase_p.h index c7793fbd..3a1728a2 100644 --- a/src/imports/location/qdeclarativegeomapitembase_p.h +++ b/src/imports/location/qdeclarativegeomapitembase_p.h @@ -49,6 +49,7 @@ // #include +#include #include "qdeclarativegeomap_p.h" @@ -75,6 +76,8 @@ public: class QDeclarativeGeoMapItemBase : public QQuickItem { Q_OBJECT + + Q_PROPERTY(QGeoShape geoShape READ geoShape STORED false ) public: explicit QDeclarativeGeoMapItemBase(QQuickItem *parent = 0); virtual ~QDeclarativeGeoMapItemBase(); @@ -84,6 +87,7 @@ public: QDeclarativeGeoMap *quickMap() { return quickMap_; } QGeoMap *map() { return map_; } + virtual const QGeoShape &geoShape() const = 0; QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); virtual QSGNode *updateMapItemPaintNode(QSGNode *, UpdatePaintNodeData *); diff --git a/src/imports/location/qdeclarativegeomapquickitem.cpp b/src/imports/location/qdeclarativegeomapquickitem.cpp index 78fd4f8f..fc710188 100644 --- a/src/imports/location/qdeclarativegeomapquickitem.cpp +++ b/src/imports/location/qdeclarativegeomapquickitem.cpp @@ -145,6 +145,9 @@ void QDeclarativeGeoMapQuickItem::setCoordinate(const QGeoCoordinate &coordinate return; coordinate_ = coordinate; + geoshape_.setTopLeft(coordinate_); + geoshape_.setBottomRight(coordinate_); + // TODO: Handle zoomLevel != 0.0 polishAndUpdate(); emit coordinateChanged(); @@ -278,6 +281,7 @@ void QDeclarativeGeoMapQuickItem::setZoomLevel(qreal zoomLevel) if (zoomLevel == zoomLevel_) return; zoomLevel_ = zoomLevel; + // TODO: update geoshape_! polishAndUpdate(); emit zoomLevelChanged(); } @@ -287,6 +291,13 @@ qreal QDeclarativeGeoMapQuickItem::zoomLevel() const return zoomLevel_; } +const QGeoShape &QDeclarativeGeoMapQuickItem::geoShape() const +{ + // TODO: return a QGeoRectangle representing the bounding geo rectangle of the quick item + // when zoomLevel_ is != 0.0 + return geoshape_; +} + /*! \internal */ diff --git a/src/imports/location/qdeclarativegeomapquickitem_p.h b/src/imports/location/qdeclarativegeomapquickitem_p.h index 0410f060..39d246df 100644 --- a/src/imports/location/qdeclarativegeomapquickitem_p.h +++ b/src/imports/location/qdeclarativegeomapquickitem_p.h @@ -53,6 +53,7 @@ #include "qdeclarativegeomap_p.h" #include "qdeclarativegeomapitembase_p.h" +#include QT_BEGIN_NAMESPACE @@ -82,6 +83,8 @@ public: void setZoomLevel(qreal zoomLevel); qreal zoomLevel() const; + const QGeoShape &geoShape() const Q_DECL_OVERRIDE; + Q_SIGNALS: void coordinateChanged(); void sourceItemChanged(); @@ -99,6 +102,7 @@ protected Q_SLOTS: private: qreal scaleFactor(); QGeoCoordinate coordinate_; + QGeoRectangle geoshape_; QPointer sourceItem_; QQuickItem *opacityContainer_; QPointF anchorPoint_; diff --git a/src/imports/location/qdeclarativepolygonmapitem.cpp b/src/imports/location/qdeclarativepolygonmapitem.cpp index bfd57e99..a966e165 100644 --- a/src/imports/location/qdeclarativepolygonmapitem.cpp +++ b/src/imports/location/qdeclarativepolygonmapitem.cpp @@ -198,14 +198,12 @@ void QGeoMapPolygonGeometry::updateSourcePoints(const QGeoMap &map, // 2.1) update srcOrigin_ and leftBoundWrapped with the point with minimum X QDoubleVector2D lb(qInf(), qInf()); - for (const QList &path: clippedPaths) { - for (const QDoubleVector2D &p: path) { - if (p.x() < lb.x() || (p.x() == lb.x() && p.y() < lb.y())) { + for (const QList &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; @@ -431,14 +429,14 @@ void QDeclarativePolygonMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *m */ QJSValue QDeclarativePolygonMapItem::path() const { - QQmlContext *context = QQmlEngine::contextForObject(parent()); + QQmlContext *context = QQmlEngine::contextForObject(this); QQmlEngine *engine = context->engine(); QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine); QV4::Scope scope(v4); - QV4::Scoped pathArray(scope, v4->newArrayObject(path_.length())); - for (int i = 0; i < path_.length(); ++i) { - const QGeoCoordinate &c = path_.at(i); + QV4::Scoped pathArray(scope, v4->newArrayObject(geopath_.path().length())); + for (int i = 0; i < geopath_.path().length(); ++i) { + const QGeoCoordinate &c = geopath_.coordinateAt(i); QV4::ScopedValue cv(scope, v4->fromVariant(QVariant::fromValue(c))); pathArray->putIndexed(i, cv); @@ -466,16 +464,15 @@ void QDeclarativePolygonMapItem::setPath(const QJSValue &value) pathList.append(c); } - if (path_ == pathList) + // Equivalent to QDeclarativePolylineMapItem::setPathFromGeoList + if (geopath_.path() == pathList) return; - path_ = pathList; - geoLeftBound_ = QDeclarativePolylineMapItem::getLeftBound(path_, deltaXs_, minX_); - geometry_.setPreserveGeometry(true, geoLeftBound_); - borderGeometry_.setPreserveGeometry(true, geoLeftBound_); - geometry_.markSourceDirty(); - borderGeometry_.markSourceDirty(); - polishAndUpdate(); + geopath_.setPath(pathList); + + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); emit pathChanged(); } @@ -489,13 +486,11 @@ void QDeclarativePolygonMapItem::setPath(const QJSValue &value) void QDeclarativePolygonMapItem::addCoordinate(const QGeoCoordinate &coordinate) { - path_.append(coordinate); - geoLeftBound_ = QDeclarativePolylineMapItem::getLeftBound(path_, deltaXs_, minX_, geoLeftBound_); - geometry_.setPreserveGeometry(true, geoLeftBound_); - borderGeometry_.setPreserveGeometry(true, geoLeftBound_); - geometry_.markSourceDirty(); - borderGeometry_.markSourceDirty(); - polishAndUpdate(); + geopath_.addCoordinate(coordinate); + + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); emit pathChanged(); } @@ -511,17 +506,14 @@ void QDeclarativePolygonMapItem::addCoordinate(const QGeoCoordinate &coordinate) */ void QDeclarativePolygonMapItem::removeCoordinate(const QGeoCoordinate &coordinate) { - int index = path_.lastIndexOf(coordinate); - if (index == -1) + int length = geopath_.path().length(); + geopath_.removeCoordinate(coordinate); + if (geopath_.path().length() == length) return; - path_.removeAt(index); - geoLeftBound_ = QDeclarativePolylineMapItem::getLeftBound(path_, deltaXs_, minX_); - geometry_.setPreserveGeometry(true, geoLeftBound_); - borderGeometry_.setPreserveGeometry(true, geoLeftBound_); - geometry_.markSourceDirty(); - borderGeometry_.markSourceDirty(); - polishAndUpdate(); + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); emit pathChanged(); } @@ -577,13 +569,13 @@ QSGNode *QDeclarativePolygonMapItem::updateMapItemPaintNode(QSGNode *oldNode, Up */ void QDeclarativePolygonMapItem::updatePolish() { - if (!map() || path_.count() == 0) + if (!map() || geopath_.path().length() == 0) return; QScopedValueRollback rollback(updatingGeometry_); updatingGeometry_ = true; - geometry_.updateSourcePoints(*map(), path_); + geometry_.updateSourcePoints(*map(), geopath_.path()); geometry_.updateScreenPoints(*map()); QList geoms; @@ -591,10 +583,10 @@ void QDeclarativePolygonMapItem::updatePolish() borderGeometry_.clear(); if (border_.color() != Qt::transparent && border_.width() > 0) { - QList closedPath = path_; + QList closedPath = geopath_.path(); closedPath << closedPath.first(); - borderGeometry_.setPreserveGeometry(true, geoLeftBound_); + borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); const QGeoCoordinate &geometryOrigin = geometry_.origin(); @@ -621,6 +613,13 @@ void QDeclarativePolygonMapItem::updatePolish() setPositionOnMap(geometry_.origin(), -1 * geometry_.sourceBoundingBox().topLeft()); } +void QDeclarativePolygonMapItem::markSourceDirtyAndUpdate() +{ + geometry_.markSourceDirty(); + borderGeometry_.markSourceDirty(); + polishAndUpdate(); +} + /*! \internal */ @@ -644,6 +643,11 @@ bool QDeclarativePolygonMapItem::contains(const QPointF &point) const return (geometry_.contains(point) || borderGeometry_.contains(point)); } +const QGeoShape &QDeclarativePolygonMapItem::geoShape() const +{ + return geopath_; +} + /*! \internal */ @@ -653,44 +657,21 @@ void QDeclarativePolygonMapItem::geometryChanged(const QRectF &newGeometry, cons QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); return; } + // TODO: change the algorithm to preserve the distances and size! + QGeoCoordinate newCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(newGeometry.center()), false); + QGeoCoordinate oldCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(oldGeometry.center()), false); + if (!newCenter.isValid() || !oldCenter.isValid()) + return; + double offsetLongi = newCenter.longitude() - oldCenter.longitude(); + double offsetLati = newCenter.latitude() - oldCenter.latitude(); + if (offsetLati == 0.0 && offsetLongi == 0.0) + return; - QDoubleVector2D newPoint = QDoubleVector2D(x(),y()) + QDoubleVector2D(geometry_.firstPointOffset()); - QGeoCoordinate newCoordinate = map()->geoProjection().itemPositionToCoordinate(newPoint, false); - if (newCoordinate.isValid()) { - double firstLongitude = geoLeftBound_.longitude(); - double firstLatitude = geoLeftBound_.latitude(); - double minMaxLatitude = firstLatitude; - // prevent dragging over valid min and max latitudes - for (int i = 0; i < path_.count(); ++i) { - double newLatitude = path_.at(i).latitude() - + newCoordinate.latitude() - firstLatitude; - if (!QLocationUtils::isValidLat(newLatitude)) { - if (qAbs(newLatitude) > qAbs(minMaxLatitude)) { - minMaxLatitude = newLatitude; - } - } - } - // calculate offset needed to re-position the item within map border - double offsetLatitude = minMaxLatitude - QLocationUtils::clipLat(minMaxLatitude); - for (int i = 0; i < path_.count(); ++i) { - QGeoCoordinate coord = path_.at(i); - // handle dateline crossing - coord.setLongitude(QLocationUtils::wrapLong(coord.longitude() - + newCoordinate.longitude() - firstLongitude)); - coord.setLatitude(coord.latitude() - + newCoordinate.latitude() - firstLatitude - offsetLatitude); - - path_.replace(i, coord); - } - geoLeftBound_.setLongitude(QLocationUtils::wrapLong(geoLeftBound_.longitude() - + newCoordinate.longitude() - firstLongitude)); - geometry_.setPreserveGeometry(true, geoLeftBound_); - borderGeometry_.setPreserveGeometry(true, geoLeftBound_); - geometry_.markSourceDirty(); - borderGeometry_.markSourceDirty(); - polishAndUpdate(); - emit pathChanged(); - } + geopath_.translate(offsetLati, offsetLongi); + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested // call to this function. diff --git a/src/imports/location/qdeclarativepolygonmapitem_p.h b/src/imports/location/qdeclarativepolygonmapitem_p.h index 19ac5081..09c46af2 100644 --- a/src/imports/location/qdeclarativepolygonmapitem_p.h +++ b/src/imports/location/qdeclarativepolygonmapitem_p.h @@ -104,6 +104,7 @@ public: QDeclarativeMapLineProperties *border(); bool contains(const QPointF &point) const Q_DECL_OVERRIDE; + const QGeoShape &geoShape() const Q_DECL_OVERRIDE; Q_SIGNALS: void pathChanged(); @@ -114,23 +115,20 @@ protected: void updatePolish() Q_DECL_OVERRIDE; protected Q_SLOTS: + void markSourceDirtyAndUpdate(); void handleBorderUpdated(); virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) Q_DECL_OVERRIDE; private: void pathPropertyChanged(); + QGeoPath geopath_; QDeclarativeMapLineProperties border_; - QList path_; - QGeoCoordinate geoLeftBound_; QColor color_; bool dirtyMaterial_; QGeoMapPolygonGeometry geometry_; QGeoMapPolylineGeometry borderGeometry_; bool updatingGeometry_; - // for the left bound calculation - QVector deltaXs_; // longitude deltas from path_[0] - double minX_; // minimum value inside deltaXs_ }; ////////////////////////////////////////////////////////////////////// diff --git a/src/imports/location/qdeclarativepolylinemapitem.cpp b/src/imports/location/qdeclarativepolylinemapitem.cpp index 2dea1464..bc388e1c 100644 --- a/src/imports/location/qdeclarativepolylinemapitem.cpp +++ b/src/imports/location/qdeclarativepolylinemapitem.cpp @@ -191,6 +191,7 @@ QList > QGeoMapPolylineGeometry::clipPath(const QGeoMap & */ srcOrigin_ = geoLeftBound_; + double unwrapBelowX = 0; leftBoundWrapped = map.geoProjection().wrapMapProjection(map.geoProjection().geoToMapProjection(geoLeftBound_)); if (preserveGeometry_) @@ -570,15 +571,13 @@ void QDeclarativePolylineMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap * QJSValue QDeclarativePolylineMapItem::path() const { QQmlContext *context = QQmlEngine::contextForObject(this); - if (!context) - return QJSValue(); QQmlEngine *engine = context->engine(); QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine); QV4::Scope scope(v4); - QV4::Scoped pathArray(scope, v4->newArrayObject(path_.length())); - for (int i = 0; i < path_.length(); ++i) { - const QGeoCoordinate &c = path_.at(i); + QV4::Scoped pathArray(scope, v4->newArrayObject(geopath_.path().length())); + for (int i = 0; i < geopath_.path().length(); ++i) { + const QGeoCoordinate &c = geopath_.coordinateAt(i); QV4::ScopedValue cv(scope, v4->fromVariant(QVariant::fromValue(c))); pathArray->putIndexed(i, cv); @@ -614,14 +613,13 @@ void QDeclarativePolylineMapItem::setPath(const QJSValue &value) */ void QDeclarativePolylineMapItem::setPathFromGeoList(const QList &path) { - if (path_ == path) + if (geopath_.path() == path) return; - path_ = path; - geoLeftBound_ = getLeftBound(path_, deltaXs_, minX_); - geometry_.setPreserveGeometry(true, geoLeftBound_); - geometry_.markSourceDirty(); - polishAndUpdate(); + geopath_.setPath(path); + + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); emit pathChanged(); } @@ -636,7 +634,7 @@ void QDeclarativePolylineMapItem::setPathFromGeoList(const QList */ int QDeclarativePolylineMapItem::pathLength() const { - return path_.size(); + return geopath_.path().length(); } /*! @@ -648,11 +646,10 @@ int QDeclarativePolylineMapItem::pathLength() const */ void QDeclarativePolylineMapItem::addCoordinate(const QGeoCoordinate &coordinate) { - path_.append(coordinate); - geoLeftBound_ = getLeftBound(path_, deltaXs_, minX_, geoLeftBound_); - geometry_.setPreserveGeometry(true, geoLeftBound_); - geometry_.markSourceDirty(); - polishAndUpdate(); + geopath_.addCoordinate(coordinate); + + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); emit pathChanged(); } @@ -667,14 +664,13 @@ void QDeclarativePolylineMapItem::addCoordinate(const QGeoCoordinate &coordinate */ void QDeclarativePolylineMapItem::insertCoordinate(int index, const QGeoCoordinate &coordinate) { - if (index < 0 || index > path_.size()) + if (index < 0 || index > geopath_.path().length()) return; - path_.insert(index, coordinate); - geoLeftBound_ = getLeftBound(path_, deltaXs_, minX_); - geometry_.setPreserveGeometry(true, geoLeftBound_); - geometry_.markSourceDirty(); - polishAndUpdate(); + geopath_.insertCoordinate(index, coordinate); + + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); emit pathChanged(); } @@ -690,14 +686,13 @@ void QDeclarativePolylineMapItem::insertCoordinate(int index, const QGeoCoordina */ void QDeclarativePolylineMapItem::replaceCoordinate(int index, const QGeoCoordinate &coordinate) { - if (index < 0 || index >= path_.size()) + if (index < 0 || index >= geopath_.path().length()) return; - path_[index] = coordinate; - geoLeftBound_ = getLeftBound(path_, deltaXs_, minX_); - geometry_.setPreserveGeometry(true, geoLeftBound_); - geometry_.markSourceDirty(); - polishAndUpdate(); + geopath_.replaceCoordinate(index, coordinate); + + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); emit pathChanged(); } @@ -712,10 +707,10 @@ void QDeclarativePolylineMapItem::replaceCoordinate(int index, const QGeoCoordin */ QGeoCoordinate QDeclarativePolylineMapItem::coordinateAt(int index) const { - if (index < 0 || index >= path_.size()) + if (index < 0 || index >= geopath_.path().length()) return QGeoCoordinate(); - return path_.at(index); + return geopath_.coordinateAt(index); } /*! @@ -727,7 +722,7 @@ QGeoCoordinate QDeclarativePolylineMapItem::coordinateAt(int index) const */ bool QDeclarativePolylineMapItem::containsCoordinate(const QGeoCoordinate &coordinate) { - return path_.indexOf(coordinate) > -1; + return geopath_.containsCoordinate(coordinate); } /*! @@ -742,8 +737,13 @@ bool QDeclarativePolylineMapItem::containsCoordinate(const QGeoCoordinate &coord */ void QDeclarativePolylineMapItem::removeCoordinate(const QGeoCoordinate &coordinate) { - int index = path_.lastIndexOf(coordinate); - removeCoordinate(index); + int length = geopath_.path().length(); + geopath_.removeCoordinate(coordinate); + if (geopath_.path().length() == length) + return; + geometry_.markSourceDirty(); + polishAndUpdate(); + emit pathChanged(); } /*! @@ -759,14 +759,13 @@ void QDeclarativePolylineMapItem::removeCoordinate(const QGeoCoordinate &coordin */ void QDeclarativePolylineMapItem::removeCoordinate(int index) { - if (index < 0 || index >= path_.size()) + if (index < 0 || index >= geopath_.path().length()) return; - path_.removeAt(index); - geoLeftBound_ = getLeftBound(path_, deltaXs_, minX_); - geometry_.setPreserveGeometry(true, geoLeftBound_); - geometry_.markSourceDirty(); - polishAndUpdate(); + geopath_.removeCoordinate(index); + + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); emit pathChanged(); } @@ -789,96 +788,6 @@ QDeclarativeMapLineProperties *QDeclarativePolylineMapItem::line() return &line_; } -QGeoCoordinate QDeclarativePolylineMapItem::computeLeftBound(const QList &path, - QVector &deltaXs, - double &minX) -{ - if (path.isEmpty()) { - minX = qInf(); - return QGeoCoordinate(); - } - deltaXs.resize(path.size()); - - deltaXs[0] = minX = 0.0; - int minId = 0; - - for (int i = 1; i < path.size(); i++) { - const QGeoCoordinate &geoFrom = path.at(i-1); - const QGeoCoordinate &geoTo = path.at(i); - double longiFrom = geoFrom.longitude(); - double longiTo = geoTo.longitude(); - double deltaLongi = longiTo - longiFrom; - if (qAbs(deltaLongi) > 180.0) { - if (longiTo > 0.0) - longiTo -= 360.0; - else - longiTo += 360.0; - deltaLongi = longiTo - longiFrom; - } - deltaXs[i] = deltaXs[i-1] + deltaLongi; - if (deltaXs[i] < minX) { - minX = deltaXs[i]; - minId = i; - } - } - return path.at(minId); -} - -QGeoCoordinate QDeclarativePolylineMapItem::updateLeftBound(const QList &path, - QVector &deltaXs, - double &minX, - QGeoCoordinate currentLeftBound) -{ - if (path.isEmpty()) { - deltaXs.clear(); - minX = qInf(); - return QGeoCoordinate(); - } else if (path.size() == 1) { - deltaXs.resize(1); - deltaXs[0] = minX = 0.0; - return path.last(); - } else if ( path.size() != deltaXs.size() + 1 ) { // something went wrong. This case should not happen - return computeLeftBound(path, deltaXs, minX); - } - - const QGeoCoordinate &geoFrom = path.at(path.size()-2); - const QGeoCoordinate &geoTo = path.last(); - double longiFrom = geoFrom.longitude(); - double longiTo = geoTo.longitude(); - double deltaLongi = longiTo - longiFrom; - if (qAbs(deltaLongi) > 180.0) { - if (longiTo > 0.0) - longiTo -= 360.0; - else - longiTo += 360.0; - deltaLongi = longiTo - longiFrom; - } - - deltaXs.push_back(deltaXs.last() + deltaLongi); - if (deltaXs.last() < minX) { - minX = deltaXs.last(); - return path.last(); - } else { - return currentLeftBound; - } -} - -QGeoCoordinate QDeclarativePolylineMapItem::getLeftBound(const QList &path, - QVector &deltaXs, - double &minX) -{ - return QDeclarativePolylineMapItem::computeLeftBound(path, deltaXs, minX); -} - -// Optimizing the common addCoordinate() path -QGeoCoordinate QDeclarativePolylineMapItem::getLeftBound(const QList &path, - QVector &deltaXs, - double &minX, - QGeoCoordinate currentLeftBound) -{ - return QDeclarativePolylineMapItem::updateLeftBound(path, deltaXs, minX, currentLeftBound); -} - /*! \internal */ @@ -888,42 +797,20 @@ void QDeclarativePolylineMapItem::geometryChanged(const QRectF &newGeometry, con QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); return; } + // TODO: change the algorithm to preserve the distances and size! + QGeoCoordinate newCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(newGeometry.center()), false); + QGeoCoordinate oldCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(oldGeometry.center()), false); + if (!newCenter.isValid() || !oldCenter.isValid()) + return; + double offsetLongi = newCenter.longitude() - oldCenter.longitude(); + double offsetLati = newCenter.latitude() - oldCenter.latitude(); + if (offsetLati == 0.0 && offsetLongi == 0.0) + return; - QDoubleVector2D newPoint = QDoubleVector2D(x(),y()) + QDoubleVector2D(geometry_.firstPointOffset()); - QGeoCoordinate newCoordinate = map()->geoProjection().itemPositionToCoordinate(newPoint, false); - if (newCoordinate.isValid()) { - double firstLongitude = geoLeftBound_.longitude(); - double firstLatitude = geoLeftBound_.latitude(); - double minMaxLatitude = firstLatitude; - // prevent dragging over valid min and max latitudes - for (int i = 0; i < path_.count(); ++i) { - double newLatitude = path_.at(i).latitude() - + newCoordinate.latitude() - firstLatitude; - if (!QLocationUtils::isValidLat(newLatitude)) { - if (qAbs(newLatitude) > qAbs(minMaxLatitude)) { - minMaxLatitude = newLatitude; - } - } - } - // calculate offset needed to re-position the item within map border - double offsetLatitude = minMaxLatitude - QLocationUtils::clipLat(minMaxLatitude); - for (int i = 0; i < path_.count(); ++i) { - QGeoCoordinate coord = path_.at(i); - // handle dateline crossing - coord.setLongitude(QLocationUtils::wrapLong(coord.longitude() - + newCoordinate.longitude() - firstLongitude)); - coord.setLatitude(coord.latitude() - + newCoordinate.latitude() - firstLatitude - offsetLatitude); - path_.replace(i, coord); - } - - geoLeftBound_.setLongitude(QLocationUtils::wrapLong(geoLeftBound_.longitude() - + newCoordinate.longitude() - firstLongitude)); - geometry_.setPreserveGeometry(true, geoLeftBound_); - geometry_.markSourceDirty(); - polishAndUpdate(); - emit pathChanged(); - } + geopath_.translate(offsetLati, offsetLongi); + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested // call to this function. @@ -938,8 +825,7 @@ void QDeclarativePolylineMapItem::afterViewportChanged(const QGeoMapViewportChan return; geometry_.setPreserveGeometry(true, geometry_.geoLeftBound()); - geometry_.markSourceDirty(); - polishAndUpdate(); + markSourceDirtyAndUpdate(); } /*! @@ -947,13 +833,13 @@ void QDeclarativePolylineMapItem::afterViewportChanged(const QGeoMapViewportChan */ void QDeclarativePolylineMapItem::updatePolish() { - if (!map() || path_.count() == 0) + if (!map() || geopath_.path().length() == 0) return; QScopedValueRollback rollback(updatingGeometry_); updatingGeometry_ = true; - geometry_.updateSourcePoints(*map(), path_, geoLeftBound_); + geometry_.updateSourcePoints(*map(), geopath_.path(), geopath_.boundingGeoRectangle().topLeft()); geometry_.updateScreenPoints(*map(), line_.width()); setWidth(geometry_.sourceBoundingBox().width()); @@ -962,6 +848,12 @@ void QDeclarativePolylineMapItem::updatePolish() setPositionOnMap(geometry_.origin(), -1 * geometry_.sourceBoundingBox().topLeft()); } +void QDeclarativePolylineMapItem::markSourceDirtyAndUpdate() +{ + geometry_.markSourceDirty(); + polishAndUpdate(); +} + /*! \internal */ @@ -1001,6 +893,11 @@ bool QDeclarativePolylineMapItem::contains(const QPointF &point) const return false; } +const QGeoShape &QDeclarativePolylineMapItem::geoShape() const +{ + return geopath_; +} + ////////////////////////////////////////////////////////////////////// /*! diff --git a/src/imports/location/qdeclarativepolylinemapitem_p.h b/src/imports/location/qdeclarativepolylinemapitem_p.h index c744bf7f..17d5ac34 100644 --- a/src/imports/location/qdeclarativepolylinemapitem_p.h +++ b/src/imports/location/qdeclarativepolylinemapitem_p.h @@ -51,6 +51,7 @@ #include "qdeclarativegeomapitembase_p.h" #include "qgeomapitemgeometry_p.h" +#include #include #include @@ -141,14 +142,10 @@ public: virtual void setPath(const QJSValue &value); bool contains(const QPointF &point) const Q_DECL_OVERRIDE; + const QGeoShape &geoShape() const Q_DECL_OVERRIDE; QDeclarativeMapLineProperties *line(); - static QGeoCoordinate computeLeftBound(const QList &path, QVector &deltaXs, double &minX); - static QGeoCoordinate updateLeftBound(const QList &path, QVector &deltaXs, double &minX, QGeoCoordinate currentLeftBound); - static QGeoCoordinate getLeftBound(const QList &path, QVector &deltaXs, double &minX); - static QGeoCoordinate getLeftBound(const QList &path, QVector &deltaXs, double &minX, QGeoCoordinate currentLeftBound); - Q_SIGNALS: void pathChanged(); @@ -158,22 +155,19 @@ protected: void updatePolish() Q_DECL_OVERRIDE; protected Q_SLOTS: + void markSourceDirtyAndUpdate(); void updateAfterLinePropertiesChanged(); virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) Q_DECL_OVERRIDE; private: void pathPropertyChanged(); + QGeoPath geopath_; QDeclarativeMapLineProperties line_; - QList path_; - QGeoCoordinate geoLeftBound_; QColor color_; bool dirtyMaterial_; QGeoMapPolylineGeometry geometry_; bool updatingGeometry_; - // for the left bound calculation - QVector deltaXs_; // longitude deltas from path_[0] - double minX_; // minimum value inside deltaXs_ }; ////////////////////////////////////////////////////////////////////// diff --git a/src/imports/location/qdeclarativerectanglemapitem.cpp b/src/imports/location/qdeclarativerectanglemapitem.cpp index 5b6a8914..a467f4c6 100644 --- a/src/imports/location/qdeclarativerectanglemapitem.cpp +++ b/src/imports/location/qdeclarativerectanglemapitem.cpp @@ -162,18 +162,18 @@ QDeclarativeMapLineProperties *QDeclarativeRectangleMapItem::border() */ void QDeclarativeRectangleMapItem::setTopLeft(const QGeoCoordinate &topLeft) { - if (topLeft_ == topLeft) + if (rectangle_.topLeft() == topLeft) return; - topLeft_ = topLeft; + rectangle_.setTopLeft(topLeft); markSourceDirtyAndUpdate(); - emit topLeftChanged(topLeft_); + emit topLeftChanged(topLeft); } QGeoCoordinate QDeclarativeRectangleMapItem::topLeft() { - return topLeft_; + return rectangle_.topLeft(); } /*! @@ -194,18 +194,18 @@ void QDeclarativeRectangleMapItem::markSourceDirtyAndUpdate() */ void QDeclarativeRectangleMapItem::setBottomRight(const QGeoCoordinate &bottomRight) { - if (bottomRight_ == bottomRight) + if (rectangle_.bottomRight() == bottomRight) return; - bottomRight_ = bottomRight; + rectangle_.setBottomRight(bottomRight); markSourceDirtyAndUpdate(); - emit bottomRightChanged(bottomRight_); + emit bottomRightChanged(bottomRight); } QGeoCoordinate QDeclarativeRectangleMapItem::bottomRight() { - return bottomRight_; + return rectangle_.bottomRight(); } /*! @@ -276,12 +276,12 @@ void QDeclarativeRectangleMapItem::updatePolish() updatingGeometry_ = true; QList path; - path << topLeft_; - path << QGeoCoordinate(topLeft_.latitude(), bottomRight_.longitude()); - path << bottomRight_; - path << QGeoCoordinate(bottomRight_.latitude(), topLeft_.longitude()); + path << rectangle_.topLeft(); + path << QGeoCoordinate(rectangle_.topLeft().latitude(), rectangle_.bottomRight().longitude()); + path << rectangle_.bottomRight(); + path << QGeoCoordinate(rectangle_.bottomRight().latitude(), rectangle_.topLeft().longitude()); - geometry_.setPreserveGeometry(true, topLeft_); + geometry_.setPreserveGeometry(true, rectangle_.topLeft()); geometry_.updateSourcePoints(*map(), path); geometry_.updateScreenPoints(*map()); @@ -293,8 +293,7 @@ void QDeclarativeRectangleMapItem::updatePolish() QList closedPath = path; closedPath << closedPath.first(); - borderGeometry_.setPreserveGeometry(true, topLeft_); - + borderGeometry_.setPreserveGeometry(true, rectangle_.topLeft()); const QGeoCoordinate &geometryOrigin = geometry_.origin(); borderGeometry_.srcPoints_.clear(); @@ -328,11 +327,9 @@ void QDeclarativeRectangleMapItem::afterViewportChanged(const QGeoMapViewportCha if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) return; - geometry_.setPreserveGeometry(true, topLeft_); - borderGeometry_.setPreserveGeometry(true, topLeft_); - geometry_.markSourceDirty(); - borderGeometry_.markSourceDirty(); - polishAndUpdate(); + geometry_.setPreserveGeometry(true, rectangle_.topLeft()); + borderGeometry_.setPreserveGeometry(true, rectangle_.topLeft()); + markSourceDirtyAndUpdate(); } /*! @@ -343,6 +340,11 @@ bool QDeclarativeRectangleMapItem::contains(const QPointF &point) const return (geometry_.contains(point) || borderGeometry_.contains(point)); } +const QGeoShape &QDeclarativeRectangleMapItem::geoShape() const +{ + return rectangle_; +} + /*! \internal */ @@ -352,34 +354,22 @@ void QDeclarativeRectangleMapItem::geometryChanged(const QRectF &newGeometry, co QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); return; } + // TODO: change the algorithm to preserve the distances and size + QGeoCoordinate newCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(newGeometry.center()), false); + QGeoCoordinate oldCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(oldGeometry.center()), false); + if (!newCenter.isValid() || !oldCenter.isValid()) + return; + double offsetLongi = newCenter.longitude() - oldCenter.longitude(); + double offsetLati = newCenter.latitude() - oldCenter.latitude(); + if (offsetLati == 0.0 && offsetLongi == 0.0) + return; - QDoubleVector2D newTopLeftPoint = QDoubleVector2D(x(),y()); - QGeoCoordinate newTopLeft = map()->geoProjection().itemPositionToCoordinate(newTopLeftPoint, false); - if (newTopLeft.isValid()) { - // calculate new geo width while checking for dateline crossing - const double lonW = bottomRight_.longitude() > topLeft_.longitude() ? - bottomRight_.longitude() - topLeft_.longitude() : - bottomRight_.longitude() + 360 - topLeft_.longitude(); - const double latH = qAbs(bottomRight_.latitude() - topLeft_.latitude()); - QGeoCoordinate newBottomRight; - // prevent dragging over valid min and max latitudes - if (QLocationUtils::isValidLat(newTopLeft.latitude() - latH)) { - newBottomRight.setLatitude(newTopLeft.latitude() - latH); - } else { - newBottomRight.setLatitude(QLocationUtils::clipLat(newTopLeft.latitude() - latH)); - newTopLeft.setLatitude(newBottomRight.latitude() + latH); - } - // handle dateline crossing - newBottomRight.setLongitude(QLocationUtils::wrapLong(newTopLeft.longitude() + lonW)); - newBottomRight.setAltitude(newTopLeft.altitude()); - topLeft_ = newTopLeft; - bottomRight_ = newBottomRight; - geometry_.setPreserveGeometry(true, newTopLeft); - borderGeometry_.setPreserveGeometry(true, newTopLeft); - markSourceDirtyAndUpdate(); - emit topLeftChanged(topLeft_); - emit bottomRightChanged(bottomRight_); - } + rectangle_.translate(offsetLati, offsetLongi); + geometry_.setPreserveGeometry(true, rectangle_.topLeft()); + borderGeometry_.setPreserveGeometry(true, rectangle_.topLeft()); + markSourceDirtyAndUpdate(); + emit topLeftChanged(rectangle_.topLeft()); + emit bottomRightChanged(rectangle_.bottomRight()); // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested // call to this function. diff --git a/src/imports/location/qdeclarativerectanglemapitem_p.h b/src/imports/location/qdeclarativerectanglemapitem_p.h index 3c55b7ba..97e92fa4 100644 --- a/src/imports/location/qdeclarativerectanglemapitem_p.h +++ b/src/imports/location/qdeclarativerectanglemapitem_p.h @@ -86,6 +86,7 @@ public: QDeclarativeMapLineProperties *border(); bool contains(const QPointF &point) const Q_DECL_OVERRIDE; + const QGeoShape &geoShape() const Q_DECL_OVERRIDE; Q_SIGNALS: void topLeftChanged(const QGeoCoordinate &topLeft); @@ -101,8 +102,7 @@ protected Q_SLOTS: virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) Q_DECL_OVERRIDE; private: - QGeoCoordinate topLeft_; - QGeoCoordinate bottomRight_; + QGeoRectangle rectangle_; QDeclarativeMapLineProperties border_; QColor color_; bool dirtyMaterial_; diff --git a/src/imports/location/qgeomapitemgeometry_p.h b/src/imports/location/qgeomapitemgeometry_p.h index 36b670f9..5ea658de 100644 --- a/src/imports/location/qgeomapitemgeometry_p.h +++ b/src/imports/location/qgeomapitemgeometry_p.h @@ -73,7 +73,7 @@ public: inline void markFullScreenDirty() { screenDirty_ = true; clipToViewport_ = false;} inline void markClean() { screenDirty_ = (sourceDirty_ = false); clipToViewport_ = true;} - inline void setPreserveGeometry(bool value, QGeoCoordinate geoLeftBound = QGeoCoordinate()) + inline void setPreserveGeometry(bool value, const QGeoCoordinate &geoLeftBound = QGeoCoordinate()) { preserveGeometry_ = value; if (preserveGeometry_) -- cgit v1.2.1