// Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qdeclarativegeomapitembase_p.h" #include "qgeocameradata_p.h" #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE QDeclarativeGeoMapItemBase::QDeclarativeGeoMapItemBase(QQuickItem *parent) : QQuickItem(parent) { connect(this, &QDeclarativeGeoMapItemBase::childrenChanged, this, &QDeclarativeGeoMapItemBase::afterChildrenChanged); // Changing opacity on a mapItemGroup should affect also the opacity on the children. // This must be notified to plugins, if they are to render the item. connect(this, &QQuickItem::opacityChanged, this, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged); } QDeclarativeGeoMapItemBase::~QDeclarativeGeoMapItemBase() { disconnect(this, &QDeclarativeGeoMapItemBase::childrenChanged, this, &QDeclarativeGeoMapItemBase::afterChildrenChanged); if (quickMap_) quickMap_->removeMapItem(this); } /*! \internal */ void QDeclarativeGeoMapItemBase::afterChildrenChanged() { const QList kids = childItems(); if (kids.size() > 0) { bool printedWarning = false; for (auto *i : kids) { if (i->flags() & QQuickItem::ItemHasContents && !qobject_cast(i) && i->objectName() != QStringLiteral("_qt_map_item_shape")) { if (!printedWarning) { qmlWarning(this) << "Geographic map items do not support child items"; printedWarning = true; } qmlWarning(i) << "deleting this child"; i->deleteLater(); } } } } /*! \internal */ void QDeclarativeGeoMapItemBase::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) { if (quickMap == quickMap_) return; if (quickMap && quickMap_) return; // don't allow association to more than one map quickMap_ = quickMap; map_ = map; if (map_ && quickMap_) { // For performance reasons we're not connecting map_'s and quickMap_'s signals to this. // Rather, the handling of cameraDataChanged, visibleAreaChanged, heightChanged and widthChanged is done explicitly in QDeclarativeGeoMap by directly calling methods on the items. // See QTBUG-76950 lastMapSize_ = QSizeF(quickMap_->width(), quickMap_->height()); lastCameraData_ = map_->cameraData(); } } /*! \internal */ void QDeclarativeGeoMapItemBase::baseCameraDataChanged(const QGeoCameraData &cameraData) { QGeoMapViewportChangeEvent evt; evt.cameraData = cameraData; evt.mapSize = QSizeF(quickMap_->width(), quickMap_->height()); if (evt.mapSize != lastMapSize_) evt.mapSizeChanged = true; if (cameraData.bearing() != lastCameraData_.bearing()) evt.bearingChanged = true; if (cameraData.center() != lastCameraData_.center()) evt.centerChanged = true; if (cameraData.roll() != lastCameraData_.roll()) evt.rollChanged = true; if (cameraData.tilt() != lastCameraData_.tilt()) evt.tiltChanged = true; if (cameraData.zoomLevel() != lastCameraData_.zoomLevel()) evt.zoomLevelChanged = true; lastMapSize_ = evt.mapSize; lastCameraData_ = cameraData; afterViewportChanged(evt); } void QDeclarativeGeoMapItemBase::visibleAreaChanged() { QGeoMapViewportChangeEvent evt; evt.mapSize = QSizeF(quickMap_->width(), quickMap_->height()); afterViewportChanged(evt); } /*! \internal */ void QDeclarativeGeoMapItemBase::setPositionOnMap(const QGeoCoordinate &coordinate, const QPointF &offset) { if (!map_ || !quickMap_) return; QDoubleVector2D pos; if (map()->geoProjection().projectionType() == QGeoProjection::ProjectionWebMercator) { const QGeoProjectionWebMercator &p = static_cast(map()->geoProjection()); QDoubleVector2D wrappedProjection = p.geoToWrappedMapProjection(coordinate); if (!p.isProjectable(wrappedProjection)) return; pos = p.wrappedMapProjectionToItemPosition(wrappedProjection); } else { pos = map()->geoProjection().coordinateToItemPosition(coordinate, false); if (qIsNaN(pos.x())) return; } QPointF topLeft = pos.toPointF() - offset; setPosition(topLeft); } bool QDeclarativeGeoMapItemBase::autoFadeIn() const { return m_autoFadeIn; } static const double opacityRampMin = 1.5; static const double opacityRampMax = 2.5; void QDeclarativeGeoMapItemBase::setAutoFadeIn(bool fadeIn) { if (fadeIn == m_autoFadeIn) return; m_autoFadeIn = fadeIn; if (quickMap_ && quickMap_->zoomLevel() < opacityRampMax) polishAndUpdate(); } QLocation::ReferenceSurface QDeclarativeGeoMapItemBase::referenceSurface() const { return m_referenceSurface; } void QDeclarativeGeoMapItemBase::setReferenceSurface(QLocation::ReferenceSurface referenceSurface) { if (referenceSurface == m_referenceSurface) return; m_referenceSurface = referenceSurface; emit referenceSurfaceChanged(); updatePolish(); } 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 */ float QDeclarativeGeoMapItemBase::zoomLevelOpacity() const { if (!m_autoFadeIn) // Consider skipping the opacity node instead. return 1.0; else if (quickMap_->zoomLevel() > opacityRampMax) return 1.0; else if (quickMap_->zoomLevel() > opacityRampMin) return quickMap_->zoomLevel() - opacityRampMin; else return 0.0; } void QDeclarativeGeoMapItemBase::setShapeTriangulationScale(QQuickShape *shape, qreal maxCoord) const { const qreal zoom = qMax(0.01, quickMap_->zoomLevel()); qreal scale = 1 / zoom; // cater also for QTriangulator's 65536 coordinate limit due to the fixed point math qint64 coord = qint64(maxCoord); const qint64 COORD_LIMIT = (1 << 21) / 32; // 65536 (where 32 is Q_FIXED_POINT_SCALE) while (coord > COORD_LIMIT) { coord /= COORD_LIMIT; scale /= COORD_LIMIT; } QQuickShapePrivate::get(shape)->triangulationScale = scale; } /*! \internal */ QSGNode *QDeclarativeGeoMapItemBase::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *pd) { if (!map_ || !quickMap_ || map_->supportedMapItemTypes() & itemType()) { if (oldNode) delete oldNode; oldNode = nullptr; return nullptr; } QSGOpacityNode *opn = static_cast(oldNode); if (!opn) opn = new QSGOpacityNode(); opn->setOpacity(zoomLevelOpacity()); QSGNode *oldN = opn->childCount() ? opn->firstChild() : 0; opn->removeAllChildNodes(); if (opn->opacity() > 0.0) { QSGNode *n = this->updateMapItemPaintNode(oldN, pd); if (n) opn->appendChildNode(n); } else { delete oldN; } return opn; } /*! \internal */ QSGNode *QDeclarativeGeoMapItemBase::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { delete oldNode; return nullptr; } /*! \internal The actual combined opacity of the item. Needed by custom renderer to look like the scene-graph one. */ qreal QDeclarativeGeoMapItemBase::mapItemOpacity() const { if (parentGroup_) return parentGroup_->mapItemOpacity() * opacity(); return opacity(); } void QDeclarativeGeoMapItemBase::setParentGroup(QDeclarativeGeoMapItemGroup &parentGroup) { parentGroup_ = &parentGroup; if (parentGroup_) { connect(parentGroup_, &QDeclarativeGeoMapItemGroup::mapItemOpacityChanged, this, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged); } } bool QDeclarativeGeoMapItemBase::isPolishScheduled() const { return QQuickItemPrivate::get(this)->polishScheduled; } void QDeclarativeGeoMapItemBase::polishAndUpdate() { polish(); update(); } QT_END_NAMESPACE