/** * @RDE_HEADER@ * Interface-Implementation of ObjectIndicator * $Id$ * $HeadURL$ * $Author$ */ // Own #include "object_indicator_impl.h" #include #include #include #include #include #include // ESRI #include #include // Qt #include #include namespace SituationMap { //----------------------------------------------------------------------------- // class ObjectIndicatorImpl //----------------------------------------------------------------------------- ObjectIndicatorImpl::ObjectIndicatorImpl() : BaseClass() , m_logger(getLogger("ObjectIndicatorImpl")) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; m_markerSize = 15; // size of filled circle at the origin (indicating the center of the map) m_lineWidth = 3; // lineWidth of (dashed) line from origin to target // updateTimer: m_updateTimer = new QTimer(this); m_updateTimer->setSingleShot(true); m_updateTimer->setInterval(100); connect(m_updateTimer, &QTimer::timeout, this, &ObjectIndicatorImpl::updateImpl); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } bool ObjectIndicatorImpl::init(MapImpl *mapImpl) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; m_mapImpl = mapImpl; if (!m_mapImpl) { ERROR_LOG(m_logger) << LOGFUNCTION << " m_map_impl is nullptr, init failed"; return false; } // to ensure graceful shutdown: (remove QGraphicsItems BEFORE qGraphicsScene/qGraphicsView is destroyed: connect(m_mapImpl, &Map::beginDestroy, this, &ObjectIndicatorImpl::onMapDestroyed, Qt::UniqueConnection); m_qGraphicsView = static_cast(m_mapImpl->getEsriQGraphicsView()); if (m_qGraphicsView) m_qGraphicsScene = m_qGraphicsView->scene(); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } ObjectIndicatorImpl::~ObjectIndicatorImpl() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; remove(); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } UniqueMapId ObjectIndicatorImpl::mapObjectToPointTo() { return m_uniqueIdToPointTo; } void ObjectIndicatorImpl::setMapObjectToPointTo(const UniqueMapId &newUniqueId, bool setSelected, int lifetimeMsec) { TRACE_LOG(m_logger) << LOGFUNCTION << " START, newUniqueId = " << newUniqueId << ", (old m_uniqueIdToPointTo = " << m_uniqueIdToPointTo << ", lifetimeMsec=" << lifetimeMsec; m_targetGeo2D = QPointF(); connect(m_mapImpl, &MapImpl::extentChangedDelayed, this, &ObjectIndicatorImpl::update, Qt::UniqueConnection); if (m_uniqueIdToPointTo != newUniqueId) { addRemoveCallback(m_uniqueIdToPointTo, AddRemove::Remove); m_uniqueIdToPointTo = newUniqueId; addRemoveCallback(m_uniqueIdToPointTo, AddRemove::Add); } update(); if (m_lifetimeTimer) m_lifetimeTimer->stop(); if (lifetimeMsec > 1) { if (!m_lifetimeTimer) { m_lifetimeTimer = new QTimer(this); m_lifetimeTimer->setSingleShot(true); connect(m_lifetimeTimer.data(), &QTimer::timeout, this, &ObjectIndicatorImpl::remove); // lifetime expired } m_lifetimeTimer->setInterval(lifetimeMsec); m_lifetimeTimer->start(); } m_setSelected = setSelected; if (m_setSelected) setMapObjectSelected(m_setSelected); TRACE_LOG(m_logger) << LOGFUNCTION << " END, newUniqueId = " << newUniqueId; } void ObjectIndicatorImpl::setPointToPointTo(const QPointF &targetGeo, int lifetimeMsec) { TRACE_LOG(m_logger) << LOGFUNCTION << " START, targetGeo = " << targetGeo << ", lifetimeMsec=" << lifetimeMsec; connect(m_mapImpl, &MapImpl::extentChangedDelayed, this, &ObjectIndicatorImpl::update, Qt::UniqueConnection); clearCurrentSelection(); addRemoveCallback(m_uniqueIdToPointTo, AddRemove::Remove); m_uniqueIdToPointTo = INVALID_MAP_ID; m_targetGeo2D = targetGeo; update(); if (m_lifetimeTimer) m_lifetimeTimer->stop(); if (lifetimeMsec > 1) { if (!m_lifetimeTimer) { m_lifetimeTimer = new QTimer(this); m_lifetimeTimer->setSingleShot(true); connect(m_lifetimeTimer.data(), &QTimer::timeout, this, &ObjectIndicatorImpl::remove); // lifetime expired } m_lifetimeTimer->setInterval(lifetimeMsec); m_lifetimeTimer->start(); } } void ObjectIndicatorImpl::clearCurrentSelection() { ObjectContainer &objCont = m_mapImpl->objectContainer(); const QList &allObj = objCont.getAllObjects(); for (ObjectContainer::Object* mapObj : allObj) { if (!mapObj) continue; if (!mapObj->selected()) continue; mapObj->setSelected(false); mapObj->updateView(); } } bool ObjectIndicatorImpl::update() { if (!m_updateTimer) return false; m_updateTimer->start(); // start is restart return true; } bool ObjectIndicatorImpl::updateImpl() { DEBUG_LOG(m_logger) << LOGFUNCTION << " START" << ", m_uniqueIdToPointTo = " << m_uniqueIdToPointTo; if (!m_qGraphicsView) return false; if (!m_qGraphicsScene) return false; if (!SituationMap::isUniqueMapIdValid(m_uniqueIdToPointTo) && m_targetGeo2D.isNull()) return false; // // Line: // Point3D targetGeo; QColor color = m_defaultColor; // center: bool targetIsValid = false; if (!m_targetGeo2D.isNull()) { DEBUG_LOG(m_logger) << LOGFUNCTION << " target: m_targetGeo2D= " << m_targetGeo2D; targetGeo = Point3D::fromQPointF(m_targetGeo2D); targetIsValid = true; } else { DEBUG_LOG(m_logger) << LOGFUNCTION << " target: m_uniqueIdToPointTo= " << m_uniqueIdToPointTo; if (!SituationMap::isUniqueMapIdValid(m_uniqueIdToPointTo)) return false; targetIsValid = centerOfTarget(targetGeo, color); } QRectF rect = m_qGraphicsScene->sceneRect(); DEBUG_LOG(m_logger) << LOGFUNCTION << " rect: " << rect; QBrush brush(color, Qt::SolidPattern); QPen pen(brush, m_lineWidth, Qt::DashLine); QPointF targetSceneCoordinate; if (targetIsValid) { if (m_mapImpl->getElevationMapManager()) { const double ele = m_mapImpl->getElevationMapManager()->elevation(targetGeo.x(), targetGeo.y()); targetGeo.rz() += ele; // targetGeo should contain an absolute z-value } QPointF targetWindowCoord = m_mapImpl->geoCoordinateToViewPortWindowPoint3D(targetGeo); targetSceneCoordinate = m_qGraphicsView->mapToScene(targetWindowCoord.toPoint()); if (targetSceneCoordinate.isNull()) { INFO_LOG(m_logger) << LOGFUNCTION << " targetSceneCoordinate invisible!!!?"; targetSceneCoordinate = rect.center(); } QLineF lineData(rect.center(), targetSceneCoordinate); if (!m_qGraphicsLine) m_qGraphicsLine = m_qGraphicsScene->addLine(lineData, pen); m_qGraphicsLine->setLine(lineData); m_qGraphicsLine->setPen(pen); DEBUG_LOG(m_logger) << LOGFUNCTION << ", rect.center() " << rect.center() << ", bCenterOfTarget " << targetIsValid << ", targetGeo:" << targetGeo << ", targetSceneCoordinate " << targetSceneCoordinate; } else { delete m_qGraphicsLine; m_qGraphicsLine = nullptr; targetSceneCoordinate = rect.center(); } // // ArrowTip: // m_arrowTip = QPixmap::fromImage(renderArrowHeadSymbol(color, 30, 10)); if (!m_qGraphicsArrowTip) { m_qGraphicsArrowTip = m_qGraphicsScene->addPixmap(m_arrowTip); m_qGraphicsArrowTip->setOffset(m_arrowTip.width() / -2.0, 0); } m_qGraphicsArrowTip->setPixmap(m_arrowTip); m_qGraphicsArrowTip->setPos(targetSceneCoordinate); const double lineAngle = MathUtils::getAngleDegreeNorth(targetSceneCoordinate, rect.center()); const double angle = 360.0 - (lineAngle); DEBUG_LOG(m_logger) << LOGFUNCTION << ", lineAngle " << lineAngle << ", angle " << angle; // Sets the clockwise rotation angle, in degrees, around the Z axis. m_qGraphicsArrowTip->setRotation(angle); // // centerMarker: // rect = QRect(rect.center().x() - m_markerSize * 0.5, rect.center().y() - m_markerSize * 0.5, m_markerSize, m_markerSize); if (!m_qGraphicsCenterMarker) m_qGraphicsCenterMarker = m_qGraphicsScene->addEllipse(rect, pen, brush); m_qGraphicsCenterMarker->setRect(rect); m_qGraphicsCenterMarker->setPen(QPen(color, Qt::DashLine)); m_qGraphicsCenterMarker->setBrush(brush); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } void ObjectIndicatorImpl::onMapDestroyed() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; remove(); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } AbstractMapObjectImpl *ObjectIndicatorImpl::getObject(const UniqueMapId &uniqueId) { TRACE_LOG(m_logger) << LOGFUNCTION << " START, uniqueId = " << uniqueId; if (!SituationMap::isUniqueMapIdValid(uniqueId)) { DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueId invalid: " << uniqueId; return nullptr; } if (!m_mapImpl->objectFactory().contains(uniqueId)) { DEBUG_LOG(m_logger) << LOGFUNCTION << " m_uniqueIdToPointTo not found: " << m_uniqueIdToPointTo; return nullptr; } AbstractMapObjectImpl *impl = dynamic_cast(m_mapImpl->objectFactory().idToMapObject(uniqueId)); TRACE_LOG(m_logger) << LOGFUNCTION << " END, uniqueId = " << uniqueId; return impl; } bool ObjectIndicatorImpl::addRemoveCallback(const UniqueMapId &uniqueId, AddRemove addRemove) { TRACE_LOG(m_logger) << LOGFUNCTION << " START, uniqueId = " << uniqueId << " addRemove = " << (int)addRemove; if (!SituationMap::isUniqueMapIdValid(uniqueId)) { DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueId invalid: " << uniqueId; return false; } AbstractMapObjectImpl *impl = getObject(uniqueId); if (!impl) return false; const QString &objToPointToDebugInfo = impl->debugInfo(); DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueId is valid, uniqueId=" << uniqueId << ", (objToPointToDebugInfo=" << objToPointToDebugInfo << ")"; if (addRemove == AddRemove::Add) impl->addChangedCallback(this); else impl->removeChangedCallback(this); TRACE_LOG(m_logger) << LOGFUNCTION << " END, uniqueId = " << uniqueId << ", addRemove = " << (int)addRemove; return true; } void ObjectIndicatorImpl::remove() { TRACE_LOG(m_logger) << LOGFUNCTION << " START, m_uniqueIdToPointTo=" << m_uniqueIdToPointTo; disconnect(this); if (m_updateTimer) m_updateTimer->stop(); addRemoveCallback(m_uniqueIdToPointTo, AddRemove::Remove); // if we set the object selected, remove selection if (m_setSelected) setMapObjectSelected(false); m_uniqueIdToPointTo = INVALID_MAP_ID; m_targetGeo2D = QPointF(); delete m_qGraphicsCenterMarker; delete m_qGraphicsLine; delete m_qGraphicsArrowTip; m_qGraphicsCenterMarker = nullptr; m_qGraphicsLine = nullptr; m_qGraphicsArrowTip = nullptr; updateImpl(); TRACE_LOG(m_logger) << LOGFUNCTION << " END, m_uniqueIdToPointTo=" << m_uniqueIdToPointTo; } void ObjectIndicatorImpl::onOriginPositionChanged() { TRACE_LOG(m_logger) << LOGFUNCTION << " update..."; update(); } bool ObjectIndicatorImpl::centerOfTarget(Point3D &objCenterPoint, QColor &objColor) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; // // point to desired obj: // RefPointer objToPointTo; if (!SituationMap::isUniqueMapIdValid(m_uniqueIdToPointTo)) { DEBUG_LOG(m_logger) << LOGFUNCTION << " m_uniqueIdToPointTo invalid: " << m_uniqueIdToPointTo; return false; } if (!m_mapImpl->objectFactory().contains(m_uniqueIdToPointTo)) { DEBUG_LOG(m_logger) << LOGFUNCTION << " m_uniqueIdToPointTo not found: " << m_uniqueIdToPointTo; return false; } objToPointTo = m_mapImpl->objectFactory().get(m_uniqueIdToPointTo); // retrive values from objToPointTo: if (!objToPointTo.valid()) { WARNING_LOG(m_logger) << LOGFUNCTION << " objToPointTo not found"; return false; } AbstractMapObjectImpl *impl = dynamic_cast(objToPointTo.get()); if (!impl) { WARNING_LOG(m_logger) << LOGFUNCTION << " dynamic_cast failed"; return false; } // get target position: BoundingBox bbox; objToPointTo->boundingBox(bbox); if (!bbox.isValid()) WARNING_LOG(m_logger) << LOGFUNCTION << " boundingBox not valid: bbox = " << bbox; const QString &objToPointToDebugInfo = objToPointTo->debugInfo(); DEBUG_LOG(m_logger) << LOGFUNCTION << ", objToPointTo is valid, uniqueIdToPointTo = " << m_uniqueIdToPointTo << ", (objToPointToDebugInfo = " << objToPointToDebugInfo << ")" << ", bbox= " << bbox; objCenterPoint = bbox.center(); // objColor: objColor = impl->majorColor(); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } QImage ObjectIndicatorImpl::renderArrowHeadSymbol(const QColor &color, int length, int halfWidth) { int width = halfWidth * 2; QImage image(width, length, QImage::Format_ARGB32); image.fill(Qt::transparent); QPainter painter(&image); painter.setRenderHints(QPainter::Antialiasing); QPen pen(color); pen.setWidth(2); painter.setPen(pen); painter.setBrush(QBrush(color, Qt::BrushStyle::SolidPattern)); // 2 || // | | // 1 | | 3 QPolygon poly; poly.reserve(3); poly.append(QPoint(0, length)); poly.append(QPoint(halfWidth, 0)); poly.append(QPoint(width, length)); painter.drawPolygon(poly); painter.end(); // image.save(QLatin1String("/tmp/Arrow.png")); return image; } bool ObjectIndicatorImpl::setMapObjectSelected(bool selected) { // 'clear' current selection: clearCurrentSelection(); if (!selected) return true; // already done by clearCurrentSelection :-) if (!SituationMap::isUniqueMapIdValid(m_uniqueIdToPointTo)) return false; RefPointer objToPointTo = m_mapImpl->objectFactory().get(m_uniqueIdToPointTo); // retrive values from objToPointTo: if (!objToPointTo.valid()) { WARNING_LOG(m_logger) << LOGFUNCTION << " objToPointTo not found"; return false; } AbstractMapObjectImpl *impl = dynamic_cast(objToPointTo.get()); if (!impl) { WARNING_LOG(m_logger) << LOGFUNCTION << " dynamic_cast failed"; return false; } if (impl->selected() == selected) return true; impl->setSelected(selected); return impl->updateView(); } } // namespace SituationMap