/** * @RDE_HEADER@ * IndicationContainer * $Id$ * $HeadURL$ * $Author$ */ // Own #include "indication_container.h" #include #include #include // ESRI #include #include // Qt #include namespace SituationMap { //----------------------------------------------------------------------------- // class IndicationData //----------------------------------------------------------------------------- class IndicationData { public: const QImage &image() const { return m_image;} void setImage(const QImage &image) { if (m_image != image) m_changed = true; m_image = image; } MarkerSymbolType markerSymbolType() const { return m_markerSymbolType; } void setMarkerSymbolType(const MarkerSymbolType &markerSymbolType) { if (m_markerSymbolType != markerSymbolType) m_changed = true; m_markerSymbolType = markerSymbolType; } const QColor &markerColor() const { return m_markerColor; } void setMarkerColor(const QColor &markerColor) { if (m_markerColor != markerColor) m_changed = true; m_markerColor = markerColor; } int markerSize() const { return m_markerSize; } void setMarkerSize(int markerSize) { if (m_markerSize != markerSize) m_changed = true; m_markerSize = markerSize; } bool markerSelected() const { return m_markerSelected; } void setMarkerSelected(bool markerSelected) { if (m_markerSelected != markerSelected) m_changed = true; m_markerSelected = markerSelected; } bool markerPickable() const { return m_markerPickable; } void setMarkerPickable(bool markerPickable) { if (m_markerPickable != markerPickable) m_changed = true; m_markerPickable = markerPickable; } PositionsTranslated &positionsTrans() { return m_positions; } void setPositions(const PositionCont &positions) { if (m_positions.positions() != positions) m_changed = true; m_positions.setPositions(positions); } bool markerVisible() const { return m_markerVisible; } void setMarkerVisible(bool markerVisible) { if (m_markerVisible != markerVisible) m_changed = true; m_markerVisible = markerVisible; } bool changed() const { return m_changed; } void resetChanged() { m_changed = false; } IndicationData() {} ~IndicationData() { m_pictureMarker.clear(); } public: QSharedPointer m_coordTranslationThread; QSharedPointer m_pictureMarker; private: QImage m_image; PositionsTranslated m_positions; MarkerSymbolType m_markerSymbolType = MarkerSymbolType::Undefined; int m_markerSize = 20; QColor m_markerColor = Qt::black; bool m_markerVisible = true; bool m_markerSelected = false; bool m_markerPickable = true; bool m_changed = true; }; class IndicationContainer::Private { public: Private() = default; ~Private() = default; // ESRI data: Esri::ArcGISRuntime::GeoView *m_geoView = nullptr; Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr; // Own data: CoordTranslationFunctor *m_coordTransFunctor = nullptr; QString m_debugInfo; // will be appended by consecutive number UniqueMapId m_uniqueMapId; SimpleValue m_drawOrder = 0; private: // TODO: use sorted vector - only max. 10 elements in there, so qhash/qmap is overkill QHash> m_map; public: QSharedPointer getIndicationData(int key) { auto it = m_map.find(key); if (it != m_map.end()) return it.value(); auto ret = QSharedPointer(new IndicationData()); m_map[key] = ret; return ret; } void remove(int key) // attention, will remove the provides image/marker... { auto it = m_map.find(key); if (it == m_map.end()) return; const QSharedPointer &indicationData = it.value(); if (indicationData && indicationData->m_coordTranslationThread) indicationData->m_coordTranslationThread->wait(); m_map.erase(it); } void clear() { m_map.clear(); } int size() const { return m_map.size(); } QList keys() const { return m_map.keys(); } bool contains(int key) const { return m_map.contains(key); } const QHash> &container() const { return m_map; } }; // class IndicationContainer::Private //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // class IndicationContainer //----------------------------------------------------------------------------- IndicationContainer::IndicationContainer(const Esri::ArcGISRuntime::GeoView *geoView, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer, CoordTranslationFunctor *coordTransFunctor, const UniqueMapId &uniqueMapId) : QObject() , m_logger(getLogger("IndicationContainer")) , d(new Private) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; d->m_geoView = const_cast(geoView); d->m_graphicsLayer = graphicsLayer; d->m_coordTransFunctor = coordTransFunctor; d->m_uniqueMapId = uniqueMapId; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } IndicationContainer::~IndicationContainer() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; // Thread::wait(): for (const QSharedPointer &id : d->container()) { if (id && id->m_coordTranslationThread) { id->m_coordTranslationThread->quit(); id->m_coordTranslationThread->wait(); } } // for d->clear(); delete d; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } QString IndicationContainer::debugInfo() const { return d->m_debugInfo; } void IndicationContainer::setDebugInfo(const QString &debugInfo) { d->m_debugInfo = debugInfo; } PositionCont IndicationContainer::positions(int idx) const { return d->getIndicationData(idx)->positionsTrans().positions(); } void IndicationContainer::setPositions(int idx, const PositionCont &positions) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << ", debugInfo='" << d->m_debugInfo << "'" << ", m_uniqueMapId = " << d->m_uniqueMapId << ", idx=" << idx << ", positions.size=" << positions.size(); d->getIndicationData(idx)->setPositions(positions); // // d->remove(idx); // attention: remove will implicit rmove the provided image... TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } void IndicationContainer::setMarker(int idx, MarkerSymbolType mst, int size, const QColor &color) { d->getIndicationData(idx)->setMarkerSymbolType(mst); d->getIndicationData(idx)->setMarkerSize(size); d->getIndicationData(idx)->setMarkerColor(color); } void IndicationContainer::setMarker(int idx, int size) { d->getIndicationData(idx)->setMarkerSize(size); } void IndicationContainer::setMarker(int idx, const QColor &color) { d->getIndicationData(idx)->setMarkerColor(color); } const QImage &IndicationContainer::image(int idx) const { return d->getIndicationData(idx)->image(); } void IndicationContainer::setImage(int idx, const QImage &image) { auto id = d->getIndicationData(idx); if (id->image() == image) return; id->setImage(image); } void IndicationContainer::clear(int idx) { d->remove(idx); } void IndicationContainer::clearAll() { d->clear(); } int IndicationContainer::drawOrder() const { return d->m_drawOrder.value(); } void IndicationContainer::setDrawOrder(int drawOrder) { d->m_drawOrder = drawOrder; } bool IndicationContainer::visible(int idx) const { return d->getIndicationData(idx)->markerVisible(); } void IndicationContainer::setVisible(int idx, bool visible) { d->getIndicationData(idx)->setMarkerVisible(visible); } bool IndicationContainer::selected(int idx) const { return d->getIndicationData(idx)->markerSelected(); } void IndicationContainer::setSelected(int idx, bool selected) { d->getIndicationData(idx)->setMarkerSelected(selected); } bool IndicationContainer::pickable(int idx) const { return d->getIndicationData(idx)->markerPickable(); } void IndicationContainer::setPickable(int idx, bool pickable) { d->getIndicationData(idx)->setMarkerPickable(pickable); } int IndicationContainer::getIndicationCount() const { return d->size(); } QList IndicationContainer::getIndicationIndexList() const { return d->keys(); } bool IndicationContainer::updateView() { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << ", debugInfo='" << d->m_debugInfo << "'" << ", m_uniqueMapId = " << d->m_uniqueMapId << " , d->m_map.size()=" << d->size(); bool success = true; // int outterSize = d->m_map.size(); for (auto it = d->container().begin(); it != d->container().end(); ++it) { const QSharedPointer &indicationData = it.value(); // per single dimension: // nothing changed? if ((!indicationData->changed()) && (!d->m_drawOrder.changed())) { DEBUG_LOG(m_logger) << LOGFUNCTION << " no change -> continue... " << ", debugInfo='" << d->m_debugInfo << "'"; continue; } // set indicationData->positionsTrans().setCoordTransFunctor(d->m_coordTransFunctor); int idx = it.key(); #define SYNC #ifdef SYNC bool updated = updateIndicationContainer(idx); success = success && updated; #else // skip thread and do call sync if: if (indicationData->positionsTrans().isTranslated() || ! indicationData->markerVisible() ) { // call sync: updateIndicationContainer(idx); // update of invisible marker is cheap :-) } else { // ok, thread is requested: // another thread prepared/running or finished/deleted? if (indicationData->m_coordTranslationThread.isNull()) { // move time consuming work to worker thread: // apply Deleter to SharedPointer (to avoid simple 'delete') indicationData->m_coordTranslationThread = QSharedPointer (new CoordTranslationThread(d->m_debugInfo), &QObject::deleteLater); indicationData->m_coordTranslationThread->setUserData(idx); indicationData->m_coordTranslationThread->setPositions(& (indicationData->positionsTrans())); // QObject::connect(indicationData.m_coordTranslationThread.data(), &CoordTranslationThread::finished, indicationData.m_coordTranslationThread.data(), &QObject::deleteLater); QObject::connect(indicationData->m_coordTranslationThread.data(), &CoordTranslationThread::finished, this, &IndicationContainer::onThreadFinished); QObject::connect(indicationData->m_coordTranslationThread.data(), &CoordTranslationThread::resultReady, this, &IndicationContainer::onResultReady); qWarning() << " start workerThread, idx=" << idx; indicationData->m_coordTranslationThread->start(); } else { FATAL_LOG(m_logger) << LOGFUNCTION << " new Translation via Thread requested, but still exists..."; } } #endif // SYNC indicationData->resetChanged(); } // while d->m_drawOrder.reset(); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " debugInfo='" << d->m_debugInfo << "'" << " success=" << success; return success; } bool IndicationContainer::onResultReady(const QVariant &userData) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " debugInfo='" << d->m_debugInfo << "'" << "threadId: " << QThread::currentThreadId(); bool success = false; bool bOk = false; int idx = userData.toInt(&bOk); if (bOk) { if (d->contains(idx)) { QSharedPointer indicationData = d->getIndicationData(idx); if (! indicationData->positionsTrans().isTranslated()) { ERROR_LOG(m_logger) << LOGFUNCTION << " NOT TRANSLATED, should not happen!!!" << " debugInfo='" << d->m_debugInfo << "'" << " idx=" << idx; } } success = updateIndicationContainer(idx); } else ERROR_LOG(m_logger) << LOGFUNCTION << " QVariant toInt failed!!!"; TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " debugInfo='" << d->m_debugInfo << "'" << " success = " << success; return success; } void IndicationContainer::onThreadFinished(const QVariant &userData) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " debugInfo='" << d->m_debugInfo << "'"; bool bOk = false; int idx = userData.toInt(&bOk); if (bOk) { if (d->contains(idx)) d->getIndicationData(idx)->m_coordTranslationThread.clear(); // will call the Deleter which is 'deleteLater' } else qWarning() << "resultReady: QVariant toInt failed!!!"; TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " debugInfo='" << d->m_debugInfo << "'"; } bool IndicationContainer::updateIndicationContainer(int idx) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " debugInfo='" << d->m_debugInfo << "'" << " idx=" << idx; QElapsedTimer timer; if (m_logger.isInfoEnabled()) timer.start(); if (! d->contains(idx)) { ERROR_LOG(m_logger) << LOGFUNCTION << " idx does not exist: idx=" << idx; return false; } bool successPM = true; QSharedPointer indicationData = d->getIndicationData(idx); // do we have positions or is PositionCont empty? const PositionsTranslated &posTransRef = indicationData->positionsTrans(); int numberOfPositions = posTransRef.size(); if (posTransRef.isEmpty()) { QSharedPointer &picMarker = indicationData->m_pictureMarker; picMarker.clear(); } else { successPM = updateIndicationContainerPictureMarker(idx); } bool success = successPM; INFO_LOG(m_logger) << LOGFUNCTION << " END" << " debugInfo='" << d->m_debugInfo << "'" << " success=" << success << " successPM=" << successPM << " numberOfPositions = " << numberOfPositions << " took[ms] " << timer.elapsed(); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " debugInfo='" << d->m_debugInfo << "'" << " numberOfPositions = " << numberOfPositions << " successPM=" << successPM << " success=" << success; return success; } bool IndicationContainer::updateIndicationContainerPictureMarker(int idx) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " debugInfo='" << d->m_debugInfo << "'" << " idx=" << idx; QElapsedTimer timer; if (m_logger.isInfoEnabled()) timer.start(); bool success = true; int numberOfPositions = -1; QSharedPointer indicationData = d->getIndicationData(idx); QSharedPointer &picMarker = indicationData->m_pictureMarker; const MarkerSymbolType &mst = indicationData->markerSymbolType(); const bool bPicMarkerNeeded = (mst != MarkerSymbolType::Undefined) || !indicationData->image().isNull(); if (bPicMarkerNeeded) { if (picMarker.isNull()) { QString debugInfo = QStringLiteral("%1#Indication_idx(%2)_PM").arg(d->m_debugInfo).arg(idx); DEBUG_LOG(m_logger) << LOGFUNCTION << " create new Object: " << debugInfo; picMarker = QSharedPointer (new GraphicalObjectPictureMarker(d->m_geoView, d->m_graphicsLayer, debugInfo, d->m_uniqueMapId)); } // set parameter: // [GUEZE-496] RPL Nr 770: 3D Lagedarstellung Einfrieren der SW Appl. // GraphicalObjectPictureMarker supports multipoint geometry now // do we have an mst or image? if (mst != MarkerSymbolType::Undefined) { // Marker: picMarker->setMarkerSymbol(mst, indicationData->markerSize(), indicationData->markerColor()); } else { // Image: picMarker->setImage(indicationData->image()); } bool visible = indicationData->markerVisible(); picMarker->setDrawOrder(d->m_drawOrder.value()); picMarker->setVisibility(visible); picMarker->setSelected(indicationData->markerSelected()); picMarker->setPickable(indicationData->markerPickable()); // Geometry/Positions const PositionCont &positionsTrans = indicationData->positionsTrans().positionsTranslated(); picMarker->setPositions(positionsTrans); numberOfPositions = positionsTrans.size(); success = picMarker->updateView(); } else { picMarker.clear(); } INFO_LOG(m_logger) << LOGFUNCTION << " END" << " debugInfo='" << d->m_debugInfo << "'" << " success=" << success << " numberOfPositions = " << numberOfPositions << " took[ms] " << timer.elapsed(); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " debugInfo='" << d->m_debugInfo << "'" << " numberOfPositions = " << numberOfPositions << " success=" << success; return success; } } // namespace SituationMap