Files
qt5-legatus-legacy/Lts/Viewer/SituationMap/MapObjectsImpl/indication_container.cpp
T
2026-08-13 18:33:10 +02:00

576 lines
18 KiB
C++
Executable File

/**
* @RDE_HEADER@
* IndicationContainer
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "indication_container.h"
#include <MapImpl/coordinate_translation.h>
#include <GraphicalObjects/graphical_object_picture_marker.h>
#include <MapImpl/coordinate_translation_thread.h>
// ESRI
#include <arcgis/GeoView.h>
#include <arcgis/PictureMarkerSymbol.h>
// Qt
#include <QElapsedTimer>
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<CoordTranslationThread> m_coordTranslationThread;
QSharedPointer<GraphicalObjectPictureMarker> 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<int> m_drawOrder = 0;
private:
// TODO: use sorted vector - only max. 10 elements in there, so qhash/qmap is overkill
QHash<int, QSharedPointer<IndicationData>> m_map;
public:
QSharedPointer<IndicationData> getIndicationData(int key)
{
auto it = m_map.find(key);
if (it != m_map.end())
return it.value();
auto ret = QSharedPointer<IndicationData>(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> &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<int> keys() const { return m_map.keys(); }
bool contains(int key) const { return m_map.contains(key); }
const QHash<int, QSharedPointer<IndicationData>> &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<Esri::ArcGISRuntime::GeoView *>(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<IndicationData> &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<int> 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> &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<CoordTranslationThread> (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> 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> 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<GraphicalObjectPictureMarker> &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> indicationData = d->getIndicationData(idx);
QSharedPointer<GraphicalObjectPictureMarker> &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<GraphicalObjectPictureMarker>
(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