Files
qt5-legatus-legacy/Lts/Plugins/SituationMapPlugin/Gui/SituationMap/situation_map_init.cpp
T
2026-08-13 18:33:10 +02:00

1420 lines
43 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @RDE_HEADER@
* SituationMap - SituationMapInit
* $Id$
* $HeadURL$
* $Author$
* Created by Christian Ehrlicher, SLSE, 2016-02-29
*/
#include "situation_map_init.h"
#include "situation_map_init_p.h"
#include <QCoreApplication>
#include <QMenu>
#include <QFile>
#include <QDir>
#include <QJsonParseError>
#include <ngltools/ngltools_filesystem.h>
#include <Map/map_notifier.h>
#include <Map/grid.h>
#include <Map/object_factory.h>
#include <Map/layer_factory.h>
#include <Map/layer_parameter.h>
#include <Map/measurement.h>
#include <Map/viewshed.h>
#include <Map/bounding_box.h>
#include <Map/object_indicator.h>
#include <Map/measurement.h>
#include <Map/situation_map_utils.h>
#include <Utils/movement_track_manager.h>
#include <Symbology/symbol_dictionary.h>
#include <Symbology/symbol_data_to_render_role.h>
#include <ParticipantManagement/ppm_translation.h>
// OPM
#include <Plugins/OperatorManagement/opm_data_plugin_interface.h>
#include <Plugins/OperatorManagement/Data/opm_data_plugin.h>
#include <Plugins/ExerciseManagement/exm_data_plugin_interface.h>
#include <Services/OperatorManagement/opm.hpp>
#include <Plugins//AssetManagement/asm_data_plugin_interface.h>
#include <LtsUtilsSettings/settings.h>
// Contextmenue / Rights
#include <LtsSession/lts_session.h>
#include <LtsSession/lts_session_data.h>
#include <Services/SituationMap/map_operator_rights.h>
#include <LtsWidgets/lts_global_wait_dialog.h>
#include <functional>
#ifdef LTS_DEBUG
#define USE_FUNCTIONDIALOG
#endif
#ifdef USE_FUNCTIONDIALOG
// ndds/log/log_makeheader.h:21 ... -> create RTI bug !
#undef Public
#include <FunctionDialog/function_dialog.h>
#endif // USE_FUNCTIONDIALOG
namespace SituationMap
{
const char MapCacheState[] = "MapCacheState";
// CMake option at Lts/Plugins/SituationMapPlugin/Gui/SituationMap/CMakeLists.txt
#ifdef USE_MAP_CACHE
# pragma message("USE_MAP_CACHE is enabled")
class MapCache;
static MapCache *s_mapCache = nullptr;
// GUEZE-791 - RPL Nr 791: Arbeitsspeicherleck
/*!
* \brief The MapCache class reduces the number of loading processes
* We are caching here to avoid numerous construction and destruction of the map (3D/scene).
* At least at linux the scene with it´s ArcGISSceneLayerCache tends to alloc a
* hugh amount of data. Additionally allocation is influenced by the viewpoint
* choosen by the operator. And with other allocations (dds data, BGViewer or
* similar) at the same time the heap memory gets fragemented.
* So we try to reduce the number of loading processes.
* This should work with ONE TrainingArea.
* If we have more TA`s, the number of cached instances may increase...
*/
class MapCache
{
public:
static MapCache *getInstance()
{
if (!s_mapCache)
{
s_mapCache = new MapCache;
// qAddPostRoutine(&MapCache::cleanup); // since many of that objects/MapImpl are using log4cxx at dtor (which in turn leads to coredumps), we pass that to the os
}
return s_mapCache;
}
struct Item
{
QPointer<Map> m_mapWidget;
bool isItemFree() { return !m_mapWidget || m_mapWidget->property(MapCacheState).isNull(); }
};
Item& getFreeItemRef(const QString &itemName)
{
//Qt: If the map contains no item with key key, the function inserts a default-constructed value into the map with key key, and returns a reference to it.
QVector<Item> &vec = m_mapWidgetCache[itemName];
// search free item:
QMutableVectorIterator<Item> it(vec);
while (it.hasNext())
{
Item& itemRef = it.next();
if (itemRef.isItemFree())
return itemRef;
}
// Not found -> create:
vec.append(Item());
return vec.last();
}
static void cleanup()
{
delete s_mapCache;
}
~MapCache()
{
for (const auto &vec : qAsConst(m_mapWidgetCache))
{
for (const auto &item : vec)
{
std::cerr << " m_mapWidgetCache: delete " << item.m_mapWidget << std::endl;
delete item.m_mapWidget; // delete nullptr is ok
}
}
}
void dump()
{
std::cerr << " m_mapWidgetCache CacheSize = " << m_mapWidgetCache.size() << std::endl;
for (const auto &vec : qAsConst(m_mapWidgetCache))
{
std::cerr << " m_mapWidgetCache VectorSize: " << vec.size() << std::endl;
for (const auto &item : vec)
{
QString isVisiStr(QStringLiteral("unknown"));
if (item.m_mapWidget)
isVisiStr = item.m_mapWidget->isVisible() ? QStringLiteral("visible") : QStringLiteral("hidden");
QString updatesEnabledStr(QStringLiteral("unknown"));
if (item.m_mapWidget)
updatesEnabledStr = item.m_mapWidget->updatesEnabled() ? QStringLiteral("updatesEnabled") : QStringLiteral("updates_Not_Enabled");
std::cerr << " m_mapWidgetCache ITEM ="
<< " m_mapWidget=" << item.m_mapWidget
<< ", property (MapCacheState) ='" << (item.m_mapWidget ? item.m_mapWidget->property(MapCacheState).toString().toStdString() : "-") << "'"
<< ", mode=" << (item.m_mapWidget ? toString(item.m_mapWidget->mode()).toStdString().c_str() : "-") << "'"
<< ", parent()=" << (item.m_mapWidget ? item.m_mapWidget->parent() : nullptr) << "'"
<< ", isVisiStr=" << isVisiStr
<< ", updatesEnabledStr=" << updatesEnabledStr
<< std::endl;
} // for
} // for
}
private:
// map-name, ....
QMap<QString, QVector<Item>> m_mapWidgetCache;
}; // class ArcGISSceneLayerCache
#else
# pragma message("USE_MAP_CACHE is disabled")
#endif // USE_MAP_CACHE
SituationMapInit::SituationMapInit(QObject *parent)
: QObject(parent)
, m_logger(SituationMap::getLogger("SituationMapInit"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
SituationMapInit::~SituationMapInit()
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " START";
if (m_pluginProvider == nullptr)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " !m_pluginProvider";
m_adapterDds.clear();
return;
}
if (!m_pluginProvider->isSessionValid())
{
INFO_LOG(m_logger) << LOGFUNCTION << " no session anymore: return";
m_adapterDds.clear();
return;
}
if (m_adapterDds)
{
m_adapterDds->stop();
m_adapterDds.clear();
}
// remove some widgets (in context of toggle between 2D and 3D)
// SituationMap:
#ifdef USE_MAP_CACHE
if (m_situationMap)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " MapCache: DELETE ...., isNull = " << m_situationMap->property(MapCacheState).isNull();
DEBUG_LOG(m_logger) << LOGFUNCTION << " MapCache: DELETE ...., property = '" << (m_situationMap ? m_situationMap->property(MapCacheState).toString().toStdString() : "-") << "'";
if (m_situationMap->property(MapCacheState).isNull())
{
// no property -> 2D -> delete
DEBUG_LOG(m_logger) << LOGFUNCTION << " MapCache: DELETE YES";
delete m_situationMap;
m_situationMap.clear();
} else {
// has property: interrupt parent-child-relationship
DEBUG_LOG(m_logger) << LOGFUNCTION << " MapCache: DELETE NO, setParent->null";
m_situationMap->setParent(nullptr, true); // parent, beginDestroyFini
m_situationMap->setProperty(MapCacheState, QVariant()); // invalid QVariant -> property will be removed -> item is free for reUsing :-)
}
}
#else
DEBUG_LOG(m_logger) << LOGFUNCTION << " DELETE situationMap: YES";
delete m_situationMap;
m_situationMap.clear();
#endif // USE_MAP_CACHE
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
//-----------------------------------------------------------------------------
ThatPartyIdToColorFunctor::ThatPartyIdToColorFunctor(const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc)
: BaseClass()
, m_ppmDtc(ppmDtc)
, m_logger(SituationMap::getLogger("ThatPartyIdToColorFunctor"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
m_ppmDtc->waitForHistoricalDataParty(1000);
connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::partyChanged,
this, &ThatPartyIdToColorFunctor::onPartyChanged);
m_ppmDtc->partys([this](const ParticipantManagement::Party & data)
{
onPartyChanged(data);
});
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void ThatPartyIdToColorFunctor::onPartyChanged(const ParticipantManagement::Party &data, DataModification modType)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< ", modType= " << modType
<< " partyId=" << data.id()
<< " partyName='" << data.name() << "'"
<< " data.role().inner_enum=" << data.role();
const int partyId = data.id();
if (modType == DataModification::eRemoved
&& m_doRemovePartyFlag) // <--- pay attention (speed up replay)
removeParty(partyId);
else
{
const AdvancedSymbology::Role role = AdvancedSymbology::roleToRole<>(data.role());
const PartyData pData(ParticipantManagement::trPartyName(data.name()),
role,
QColor::fromRgb(QRgb(data.color())),
QColor::fromRgb(QRgb(data.fadingColor())));
setParty(partyId, pData);
}
}
//-----------------------------------------------------------------------------
bool SituationMapInit::initPluginProvider(SituationMap::PluginProvider *pluginProvider)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START " << " session=" << pluginProvider;
m_pluginProvider = pluginProvider;
if (!m_pluginProvider)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no plugin provider!";
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " START " << " session=" << pluginProvider;
return true;
}
//-----------------------------------------------------------------------------
bool SituationMapInit::initAdapter(QPointer<MovementTrackManager> &movementTrackMgr, bool resourceMode)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!m_pluginProvider)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no plugin provider!";
return false;
}
// create AdapterDds
m_adapterDds = QSharedPointer<SituationMap::AdapterDds>(new SituationMap::AdapterDds);
// signal-signal
connect(m_adapterDds.data(), &AdapterDds::resourceManagerModeChanged,
this, &SituationMapInit::resourceManagerModeChanged);
if (m_situationMap == nullptr)
return false;
// init AdapterDds
if (!m_adapterDds->init(m_situationMap, m_pluginProvider, movementTrackMgr, resourceMode)) // re-think: resourceMode
{
ERROR_LOG(m_logger) << LOGFUNCTION << " m_adapterDds->init failed";
return false;
}
// create ThatPartyIdToColorFunctor (m_ppmDtc should be available now)
QSharedPointer<ThatPartyIdToColorFunctor> partyIdToColorFunctor =
QSharedPointer<ThatPartyIdToColorFunctor>(new ThatPartyIdToColorFunctor(m_pluginProvider->ppmDtc()));
m_situationMap->setPartyIdToColorFunctor(partyIdToColorFunctor);
// in context replay, skip removing of parties.
// the topic is removed and added -> no real modification:
// if (m_pluginProvider->isReplayPartition())
// partyIdToColorFunctor->setDoRemovePartyFlag(false);
// [NLDEVCTRL-1364] Ausfallkreuz TLN-Symbol - war teilweise schwarz.
// Daher nun auch in LiveLage kein eRemoved bearbeiten und Party 'behalten'.
partyIdToColorFunctor->setDoRemovePartyFlag(false);
connect(m_pluginProvider, &PluginProvider::partitionTimeChanged, this, [this](qint64 partitionTime)
{
if (m_situationMap)
m_situationMap->setPartitionTime(partitionTime);
});
// not neccessary, done in MapImpl::onAllPartysChanged / MapImpl::onPartyChanged :
// connect(partyIdToColorFunctor.data(), &ThatPartyIdToColorFunctor::partyChanged,
// m_adapterDds.data(), &SituationMap::AdapterDds::restartIfRunning);
// : not neccessary, done in MapImpl::onAllPartysChanged / MapImpl::onPartyChanged
connect(m_adapterDds.data(), &AdapterDds::participantPositionChanged,
this, &SituationMapInit::participantPositionChanged);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return true;
}
void SituationMapInit::startDisplay()
{
if (!m_adapterDds)
return;
if (!m_situationMap)
return;
// run AdapterDds
QPointer<SituationMap::MapNotifier> mapNotifier = m_situationMap->createMapNotifier();
if (mapNotifier)
{
connect(mapNotifier.data(), &SituationMap::MapNotifier::mapReadyAndSymbolDictionaryLoaded, this, [this]()
{
// #define TRY_WORKAROUND_FOR_CRASH_ON_STARTUP
#ifdef TRY_WORKAROUND_FOR_CRASH_ON_STARTUP
QTimer::singleShot(500, std::bind(&SituationMap::AdapterDds::run, m_adapterDds));
#else
if (m_adapterDds) // might already be destroyed again (e.g. when closing the app before all was loaded)
m_adapterDds->run();
#endif // TRY_WORKAROUND_FOR_CRASH_ON_STARTUP
}, Qt::QueuedConnection);
mapNotifier->run();
}
}
void SituationMapInit::stopDisplay()
{
if (!m_adapterDds)
return;
m_adapterDds->stop();
}
bool SituationMapInit::clearObjects()
{
if (!m_adapterDds)
return false;
m_adapterDds->clearObjects();
if (!m_situationMap)
return false;
const int remainingObjectsCount = m_situationMap->objectFactory().count();
if (remainingObjectsCount != 0)
{
// maybe there are mapObjects of overlays...
WARNING_LOG(m_logger) << LOGFUNCTION << " remainingObjectsCount should be '0', is: " << remainingObjectsCount;
}
return true;
}
SituationMap::Map *SituationMapInit::getWidget()
{
return m_situationMap;
}
SituationMap::AdapterDds *SituationMapInit::adapterDds()
{
return m_adapterDds.data();
}
bool SituationMapInit::enableMovementTrack(int participantId, bool enable)
{
if (!m_adapterDds)
return false;
auto ptr = m_adapterDds->simId2Participant(participantId);
if (!ptr)
return false;
QPointer<SituationMap::MovementTrackManager> movementTrackManager = m_adapterDds->movementTrackManager();
if (movementTrackManager)
{
const int trackId = participantId;
if (enable)
movementTrackManager->activateTrack(trackId); // Maybe another is deactivated/removed?
else
movementTrackManager->deactivateTrack(trackId);
}
// ptr->setMovementTrackEnabled(enable);
// if (!ptr->updateView())
// ERROR_LOG (m_logger) << "Couldn't update view after setting setMovementTrackEnabled(" << enable << ") for SimId: " << participantId;
return true;
}
bool SituationMapInit::isMovementTrackEnabled(int participantId) const
{
if (!m_adapterDds)
return false;
auto ptr = m_adapterDds->simId2Participant(participantId);
if (!ptr)
return false;
return ptr->isMovementTrackEnabled();
}
bool SituationMapInit::enableGearRanges(int participantId, bool enable)
{
if (!m_adapterDds)
return false;
auto ptr = m_adapterDds->simId2Participant(participantId);
if (!ptr)
return false;
ptr->setRangesVisibility(enable);
if (!ptr->updateView())
ERROR_LOG(m_logger) << "Couldn't updateview after setting setRangesVisibility(" << enable << ") for SimId: " << participantId;
return true;
}
bool SituationMapInit::isGearRangesEnabled(int participantId) const
{
if (!m_adapterDds)
return false;
auto ptr = m_adapterDds->simId2Participant(participantId);
if (!ptr)
return false;
return ptr->isRangesVisible();
}
QPointF SituationMapInit::centerPoint() const
{
return m_situationMap ? m_situationMap->centerPoint() : QPointF();
}
void SituationMapInit::setCenterPoint(const QPointF &center, double scale, float durationSeconds)
{
if (m_situationMap)
m_situationMap->setCenterPoint(center, scale, durationSeconds);
}
bool SituationMapInit::centerMapToParticipant(int participantId)
{
if (!m_situationMap || !m_adapterDds)
return false;
auto ptr = m_adapterDds->simId2Participant(participantId);
if (!ptr)
return false;
m_situationMap->setCenterPoint(ptr->origin().toPointF());
return true;
}
bool SituationMapInit::centerMapToAesObject(int entityId)
{
if (!m_situationMap || !m_adapterDds)
return false;
auto ptr = m_adapterDds->simId2Aes(entityId);
if (!ptr)
return false;
SituationMap::BoundingBox bbox;
ptr->boundingBox(bbox);
if (!bbox.isValid())
{
WARNING_LOG(m_logger) << LOGFUNCTION
<< " boundingBox not valid: bbox=" << bbox;
return false;
}
m_situationMap->setCenterPoint(bbox.center().toPointF());
return true;
}
bool SituationMapInit::centerMapToBuildingObject(int entityId)
{
if (!m_situationMap || !m_adapterDds)
return false;
auto ptr = m_adapterDds->simId2Building(entityId);
if (!ptr)
return false;
SituationMap::BoundingBox bbox;
ptr->boundingBox(bbox);
if (!bbox.isValid())
{
WARNING_LOG(m_logger) << LOGFUNCTION
<< " boundingBox not valid: bbox=" << bbox;
return false;
}
m_situationMap->setCenterPoint(bbox.center().toPointF());
return true;
}
bool SituationMapInit::centerMapToGuezIedObject(int entityId)
{
if (!m_situationMap || !m_adapterDds)
return false;
auto ptr = m_adapterDds->simId2GuezIed(entityId);
if (!ptr)
return false;
m_situationMap->setCenterPoint(ptr->origin().toPointF());
return true;
}
bool SituationMapInit::centerMapToChargeEffectObject(int entityId)
{
if (!m_situationMap || !m_adapterDds)
return false;
auto ptr = m_adapterDds->simId2ChargeEffect(entityId);
if (!ptr)
return false;
m_situationMap->setCenterPoint(ptr->origin().toPointF());
return true;
}
bool SituationMapInit::centerMapToMapObject(MapObjectType mot, int entityId)
{
if (!m_adapterDds || !m_situationMap)
return false;
auto ptr = m_adapterDds->simId2MapObject(mot, entityId);
if (!ptr)
return false;
SituationMap::BoundingBox bbox;
ptr->boundingBox(bbox);
if (!bbox.isValid())
{
WARNING_LOG(m_logger) << LOGFUNCTION
<< " boundingBox not valid: bbox=" << bbox;
return false;
}
// should work for
// - PointSymbols/Particpant (ExtentType::Pixel) as well as
// - MultiPointSymbols/Areas (ExtentType::Sim)
m_situationMap->setCenterPoint(bbox.center().toPointF());
return true;
}
bool SituationMapInit::zoomMapTo(const QVector<QPair<MapObjectType, int> > &entities, int paddingInDIPS)
{
if (!m_adapterDds || !m_situationMap)
return false;
PositionCont posCont;
for (const QPair<MapObjectType, int> &entity : qAsConst(entities))
{
RefPointer<AbstractMapObject> mapObj = m_adapterDds->simId2MapObject(entity.first, entity.second);
if (!mapObj)
continue;
RefPointer<AbstractSinglePointMapObject> single(mapObj); // dynacast
if (single)
posCont.push_back(single->origin());
else
{
RefPointer<Participant> participant(mapObj); // dynacast
if (participant)
posCont.push_back(participant->origin());
else
{
RefPointer<AbstractMultiPointMapObject> multi(mapObj); // dynacast
if (multi)
posCont += multi->positions();
}
}
} // for
if (!posCont.isEmpty())
{
QVector<QPointF> coords;
coords.reserve(posCont.size());
for (const Point3D p : qAsConst(posCont))
coords.push_back(p.toPointF());
m_situationMap->zoomToArea(coords, paddingInDIPS);
}
return true;
}
//QSharedPointer<AdvancedSymbology::SymbolResult> SituationMapInit::findPreviewImage(const QString &objectType, int entityId)
//{
// return QSharedPointer<AdvancedSymbology::SymbolResult>(new AdvancedSymbology::SymbolResult());
//}
bool SituationMapInit::objectIndicatorToParticipant(int participantId, bool setSelected, int lifetimeMsec)
{
if (!m_adapterDds || !m_situationMap)
return false;
auto ptr = m_adapterDds->simId2Participant(participantId);
if (!ptr)
return false;
m_situationMap->objectIndicator().setMapObjectToPointTo(ptr->uniqueMapId(), setSelected, lifetimeMsec);
return true;
}
bool SituationMapInit::objectIndicatorToAesObject(int entityId)
{
if (!m_adapterDds || !m_situationMap)
return false;
auto ptr = m_adapterDds->simId2Aes(entityId);
if (!ptr)
return false;
m_situationMap->objectIndicator().setMapObjectToPointTo(ptr->uniqueMapId());
return true;
}
bool SituationMapInit::objectIndicatorToBuildingObject(int entityId)
{
if (!m_adapterDds || !m_situationMap)
return false;
auto ptr = m_adapterDds->simId2Building(entityId);
if (!ptr)
return false;
m_situationMap->objectIndicator().setMapObjectToPointTo(ptr->uniqueMapId());
return true;
}
bool SituationMapInit::objectIndicatorToGuezIedObject(int entityId)
{
if (!m_adapterDds || !m_situationMap)
return false;
auto ptr = m_adapterDds->simId2GuezIed(entityId);
if (!ptr)
return false;
m_situationMap->objectIndicator().setMapObjectToPointTo(ptr->uniqueMapId());
return true;
}
bool SituationMapInit::objectIndicatorToMapObject(MapObjectType mot, int entityId, bool setSelected, int lifetimeMsec)
{
if (!m_adapterDds || !m_situationMap)
return false;
const UniqueMapId uniqueMapId = m_adapterDds->simId2UniqueMapId(mot, entityId);
if (!isUniqueMapIdValid(uniqueMapId))
return false;
m_situationMap->objectIndicator().setMapObjectToPointTo(uniqueMapId, setSelected, lifetimeMsec);
return true;
}
bool SituationMapInit::objectIndicatorToPoint(const QPointF &targetGeo, int lifetimeMsec)
{
if (!m_adapterDds || !m_situationMap)
return false;
m_situationMap->objectIndicator().setPointToPointTo(targetGeo, lifetimeMsec);
return true;
}
bool SituationMapInit::removeObjectIndicator()
{
if (!m_situationMap)
return false;
m_situationMap->objectIndicator().remove();
return true;
}
bool SituationMapInit::setMapObjectSelected(MapObjectType mot, const QVector<int> &entityIds, bool selection)
{
if (!m_adapterDds)
return false;
m_adapterDds->setMapObjectSelected(mot, entityIds, selection);
return true;
}
bool SituationMapInit::clearMapObjectSelection(MapObjectType mot)
{
if (!m_adapterDds)
return false;
m_adapterDds->clearMapObjectSelection(mot);
return true;
}
bool SituationMapInit::skipMapObjectSelectionDialog() const
{
return m_skipMapObjectSelectionDialog;
}
void SituationMapInit::setSkipMapObjectSelectionDialog(bool asList)
{
m_skipMapObjectSelectionDialog = asList;
}
void SituationMapInit::startMeasureLength()
{
if (!m_situationMap)
return ;
m_situationMap->measurement().measureLength();
}
void SituationMapInit::clearMeasureLength()
{
if (!m_situationMap)
return ;
m_situationMap->measurement().clear();
}
void SituationMapInit::startViewshed()
{
if (!m_situationMap)
return ;
m_viewshedId = -1;
QObject::disconnect(m_viewshedFinishedConnection);
m_viewshedFinishedConnection = connect(&m_situationMap->viewshed(), &Viewshed::viewshedFinished,
this, &SituationMapInit::onViewshedFinished);
m_situationMap->viewshed().create(); // no para -> start rubberband
}
void SituationMapInit::startViewshed(double alpha, double innerRadius, double observerHeight, double targetHeight, double beamAngle)
{
if (!m_situationMap)
return ;
m_viewshedId = -1;
QObject::disconnect(m_viewshedFinishedConnection);
m_viewshedFinishedConnection = connect(&m_situationMap->viewshed(), &Viewshed::viewshedFinished,
this, &SituationMapInit::onViewshedFinished);
m_situationMap->viewshed().create(alpha, innerRadius, observerHeight, targetHeight, beamAngle); // not enough para -> start rubberband
}
void SituationMapInit::onViewshedFinished(long viewshedId)
{
m_viewshedId = viewshedId;
QObject::disconnect(m_viewshedFinishedConnection); // disconnect even if viewshedId is invalid
}
void SituationMapInit::modifyViewshed(double alpha, double innerRadius, double observerHeight, double targetHeight, double beamAngle)
{
if (m_situationMap)
{
if (m_viewshedId >= 0)
m_situationMap->viewshed().modify(m_viewshedId, alpha, innerRadius, observerHeight, targetHeight, beamAngle);
else
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_viewshedId invalid (" << m_viewshedId << ")";
}
}
void SituationMapInit::clearViewshed()
{
if (m_situationMap)
m_situationMap->viewshed().remove(); // no para -> remove all
m_viewshedId = -1;
}
bool SituationMapInit::showExpiredEmergencyCalls(bool showExpired)
{
if (!m_adapterDds)
return false;
return m_adapterDds->showExpiredEmergencyCalls(showExpired);
}
bool SituationMapInit::setOwnPositionVisible(bool enable)
{
return m_adapterDds ? m_adapterDds->setOwnPositionVisible(enable) : false;
}
bool SituationMapInit::isOwnPositionVisble() const
{
return m_adapterDds ? m_adapterDds->isOwnPositionVisible() : false;
}
bool SituationMapInit::isBuildingOwnershipSupported() const
{
return m_adapterDds ? m_adapterDds->isBuildingOwnershipSupported() : false;
}
bool SituationMapInit::isBuildingOwnershipVisible() const
{
return m_adapterDds ? m_adapterDds->isBuildingOwnershipVisible() : false;
}
bool SituationMapInit::setBuildingOwnershipVisible(bool visible)
{
return m_adapterDds ? m_adapterDds->setBuildingOwnershipVisible(visible) : false;
}
bool SituationMapInit::isBuildingContaminationSupported() const
{
return m_adapterDds ? m_adapterDds->isBuildingContaminationSupported() : false;
}
bool SituationMapInit::isBuildingContaminationVisible() const
{
return m_adapterDds ? m_adapterDds->isBuildingContaminationVisible() : false;
}
bool SituationMapInit::setBuildingContaminationVisible(bool visible)
{
return m_adapterDds ? m_adapterDds->setBuildingContaminationVisible(visible) : false;
}
bool SituationMapInit::enableFireEventFlashing(bool enable)
{
return m_adapterDds ? m_adapterDds->enableFireEventFlashing(enable) : false;
}
bool SituationMapInit::isFireEventFlashingEnabled() const
{
return m_adapterDds ? m_adapterDds->isFireEventFlashingEnabled() : false;
}
void SituationMapInit::setPlaybackTimeString(const QString &timeString)
{
if (m_situationMap == nullptr)
return;
m_situationMap->setTimeString(timeString);
m_situationMap->showTimeString(true);
}
SituationMap::RB::RubberbandFactory &SituationMapInit::rubberbandFactory()
{
if (m_situationMap != nullptr)
return m_situationMap->rubberbandFactory();
// TODO: fallback for very bad cases! workaround:
SituationMap::Map *temp = SituationMap::Map::create();
return temp->rubberbandFactory();
}
ResourceManagerMode SituationMapInit::resourceManagerMode() const
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!m_adapterDds)
return ResourceManagerMode::Undefined;
ResourceManagerMode rmm = m_adapterDds->resourceManagerMode();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return rmm;
}
bool SituationMapInit::setResourceManagerMode(ResourceManagerMode rmm)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!m_adapterDds)
return false;
bool success = m_adapterDds->setResourceManagerMode(rmm);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return success;
}
RefPointer<Participant> SituationMapInit::simId2Participant(int32_t simId)
{
if (!m_adapterDds)
return RefPointer<Participant>();
return m_adapterDds->simId2Participant(simId);
}
QVector<UniqueMapId> SituationMapInit::simIds2UniqueMapIds(MapObjectType mot, const QVector<int32_t> &simIds) const
{
if (!m_adapterDds)
return {};
return m_adapterDds->simIds2UniqueMapIds(mot, simIds);
}
static QVector<BaseUrl> convertMapDecription2BaseUrlVec(const std::vector<AssetManagement::MapDescription>& vecIn)
{
QVector<BaseUrl> vecOut;
vecOut.reserve(vecIn.size());
for (const AssetManagement::MapDescription &mapDescription : vecIn)
{
BaseUrl baseUrl(QString::fromStdString(mapDescription.url()),
mapDescription.minScale(), mapDescription.maxScale());
baseUrl.setName(QString::fromStdString(mapDescription.name()));
baseUrl.setDescription(QString::fromStdString(mapDescription.description()));
switch (mapDescription.mapTypeId().underlying()) {
case AssetManagement::MapType_def::Aerial:
baseUrl.setBasemapType(BasemapType::Aerial);
break;
case AssetManagement::MapType_def::Topographic:
baseUrl.setBasemapType(BasemapType::Topographic);
break;
case AssetManagement::MapType_def::Elevation:
case AssetManagement::MapType_def::Feature:
case AssetManagement::MapType_def::SceneObjects:
case AssetManagement::MapType_def::Unknown:
break;
} // switch
vecOut.append(baseUrl);
} // for
return vecOut;
}
bool SituationMapInit::trainingAreaExists() const
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!m_pluginProvider->exmDtc() || !m_pluginProvider->asmDtc())
return false;
const int exerciseId = m_pluginProvider->exerciseId();
if (exerciseId <= 0)
return false;
const int trainingAreaId = m_pluginProvider->exmDtc()->exercise(exerciseId).trainingAreaId();
if (trainingAreaId <= 0)
return false;
const AssetManagement::TrainingArea &ta = m_pluginProvider->asmDtc()->trainingArea(trainingAreaId);
TRACE_LOG(m_logger) << LOGFUNCTION << " END, trainingAreaExists = " << (ta.id() == trainingAreaId);
return (ta.id() == trainingAreaId);
}
MapSettingsJson SituationMapInit::getMapSettingsFromService() const
{
MapSettingsJson msj;
if (!m_pluginProvider->exmDtc() || !m_pluginProvider->asmDtc())
return msj;
const int exerciseId = m_pluginProvider->exerciseId();
const int trainingAreaId = m_pluginProvider->exmDtc()->exercise(exerciseId).trainingAreaId();
AssetManagement::TrainingArea ta = m_pluginProvider->asmDtc()->trainingArea(trainingAreaId);
if (ta.id() != trainingAreaId)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " TrainingArea does not exists (yet)";
return msj;
}
msj.setMapUrls (convertMapDecription2BaseUrlVec(ta.map2D()));
msj.setSceneUrls (convertMapDecription2BaseUrlVec(ta.scene3D()));
msj.setSceneObjectUrls(convertMapDecription2BaseUrlVec(ta.sceneObjects()));
msj.setElevationUrls (convertMapDecription2BaseUrlVec(ta.elevation()));
return msj;
}
//SLOTS
bool SituationMapInit::initMap(bool enableSelection, SituationMap::Mode mapMode, const SituationMap::Viewpoint2D &viewPoint)
{
INFO_LOG(m_logger) << LOGFUNCTION << " START, enableSelection=" << enableSelection
<< ", mapMode=" << mapMode;
#ifdef USE_MAP_CACHE
bool reUsing = false;
if (mapMode == Mode::Mode_3D)
{
// GUEZE-791 - RPL Nr 791: Arbeitsspeicherleck:
MapCache::Item& mapCacheItemRef = MapCache::getInstance()->getFreeItemRef(QStringLiteral("3dMapWidget"));
if (m_logger.isInfoEnabled())
MapCache::getInstance()->dump();
const bool reUse = mapCacheItemRef.m_mapWidget;
if (reUse)
{
m_situationMap = mapCacheItemRef.m_mapWidget;
m_situationMap->setProperty(MapCacheState, QVariant(QStringLiteral("3D/reUsed/taken")));
DEBUG_LOG(m_logger) << LOGFUNCTION << " MapCache: ReUse Mode_3D"
<< " (updatesEnabled()=" << m_situationMap->updatesEnabled() << ")";
reUsing = true;
} else {
DEBUG_LOG(m_logger) << LOGFUNCTION << " MapCache: CREATE Mode_3D";
m_situationMap = SituationMap::Map::create();
mapCacheItemRef.m_mapWidget = m_situationMap;
mapCacheItemRef.m_mapWidget->setProperty(MapCacheState, QVariant(QStringLiteral("3D/created")));
}
} // if (mapMode == Mode::Mode_3D)
else
{
m_situationMap = SituationMap::Map::create();
// no property at 2D: m_situationMap->setProperty(MapCacheState, QVariant(QStringLiteral("2D/created")));
}
if (m_logger.isInfoEnabled())
MapCache::getInstance()->dump();
#else
m_situationMap = SituationMap::Map::create();
#endif // USE_MAP_CACHE
if (enableSelection)
{
connect(m_situationMap.data(), &SituationMap::Map::mapObjectsClicked, this, &SituationMapInit::onMapObjectsClicked);
connect(m_situationMap.data(), &SituationMap::Map::mapObjectsPickedByArea, this, &SituationMapInit::onMapObjectClickedByArea);
connect(m_situationMap.data(), &SituationMap::Map::mapReadyAndSymbolDictionaryLoaded, this, [this]()
{
// just for 2D (mode is valid later)
if (m_situationMap->mode() == SituationMap::Mode::Mode_2D)
{
connect(m_situationMap.data()
, &SituationMap::Map::contextMenuClicked
, this
, &SituationMapInit::onContextMenuRequested); // siehe auch sit_map.cpp Zeile 421 ff
}
});
} // if (enableSelection)
// handle Destruction of MapWidget
// any partitionTimeChanged signals will cause problems:
connect(m_situationMap.data(), &SituationMap::Map::beginDestroy, this, &SituationMapInit::onMapbeginDestroy);
if (m_situationMap == nullptr)
{
FATAL_LOG(m_logger) << LOGFUNCTION << " map not available!";
return false;
}
#ifdef USE_MAP_CACHE
if (reUsing)
return true;
#endif // USE_MAP_CACHE
INFO_LOG(m_logger) << LOGFUNCTION << " ***** initMap:"
<< ", mapMode=" << mapMode
<< ", m_pluginProvider->toString=" << m_pluginProvider->toString()
<< ", exerciseId()= " << m_pluginProvider->exerciseId();
m_situationMap->setReplay(m_pluginProvider->isReplayPartition());
bool success = false;
if ( (m_pluginProvider->appId() == LtsAppDescriptor::App::OC) ||
(m_pluginProvider->appId() == LtsAppDescriptor::App::SituationTablet) ||
(m_pluginProvider->exerciseId() == 0) // e.g. in case of App::ResourceManager
)
{
// OC -> offline -> use local map urls (iniFile):
success = initMapPart2(mapMode, viewPoint);
} else {
// !OC -> online -> give map descriptions at asm_training_area a chance:
const bool bTrainingAreaExists = this->trainingAreaExists();
INFO_LOG(m_logger) << LOGFUNCTION << " bTrainingAreaExists=" << bTrainingAreaExists
<< ", m_pluginProvider->toString " << m_pluginProvider->toString();
bool bIsOfTypeOrganizer =
(m_pluginProvider->operatorId() == 99) || // 99 == Organisator
(m_pluginProvider->operatorId() == 98); // 98 == Organizer
if (bTrainingAreaExists || bIsOfTypeOrganizer) // 99 == Organisator
{
const MapSettingsJson &mapSettingsFromService = getMapSettingsFromService();
success = initMapPart2(mapMode, viewPoint, &mapSettingsFromService);
return success;
}
// if trainingAreaExists does not exists:
// like race condition publishing data at newly created replay environment and creating snapshotApp (this code)
LtsWidgets::LtsGlobalWaitDialog::getInstance()->wait(); // really visible???
LtsWidgets::LtsGlobalWaitDialog::getInstance()->setCustomText(tr("Situation Map is starting..."));
QTimer *waitForTa = new QTimer(this);
waitForTa->setInterval(250);
waitForTa->setProperty("tries", 0); // HACK
connect(waitForTa, &QTimer::timeout,
this, [this, waitForTa, mapMode, viewPoint]()
{
int tries = waitForTa->property("tries").toInt();
INFO_LOG(m_logger) << LOGFUNCTION << " waiting for trainingAreaExists " << trainingAreaExists() << ", tries = " << tries;
if (this->trainingAreaExists())
{
LtsWidgets::LtsGlobalWaitDialog::getInstance()->done();
waitForTa->stop();
const MapSettingsJson &mapSettingsFromService = getMapSettingsFromService();
if (!initMapPart2(mapMode, viewPoint, &mapSettingsFromService))
ERROR_LOG(m_logger) << LOGFUNCTION << " initMapPart2 failed";
}
if (tries > 30)
{
LtsWidgets::LtsGlobalWaitDialog::getInstance()->done();
waitForTa->stop();
// giving up: use values of ini file:
NOTICE_LOG(m_logger) << LOGFUNCTION << " waiting for trainingAreaExists " << trainingAreaExists() << ", tries = " << tries
<< " giving up";
if (!initMapPart2(mapMode, viewPoint))
ERROR_LOG(m_logger) << LOGFUNCTION << " initMapPart2 failed";
return;
}
waitForTa->setProperty("tries", ++tries);
});
waitForTa->start();
return true;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success;
return success;
}
bool SituationMapInit::initMapPart2(SituationMap::Mode mapMode,
const SituationMap::Viewpoint2D &viewPoint,
const MapSettingsJson* mapSettingJson)
{
INFO_LOG(m_logger) << LOGFUNCTION << " mapSettingJson=" << mapSettingJson;
bool success = true;
const QString initFilePath = NGL::findFile(NGL::FileType::ConfigFile, QStringLiteral("situation_map_defaults.ini"));
if (!QFile::exists(initFilePath))
{
FATAL_LOG(m_logger) << LOGFUNCTION << " MapIniFile location failed, used path: '" << initFilePath << "'";
assert(false);
return false;
}
else
DEBUG_LOG(m_logger) << LOGFUNCTION << " ***** MapIniFile found: " << initFilePath;
// ownership goes to m_situationMap
SituationMap::MapSettings *mapSettings =
new SituationMap::MapSettings(initFilePath, m_situationMap);
ThatMapInitializer *mapInitializer =
new ThatMapInitializer(*m_situationMap, *mapSettings, QLatin1String("SituationMapInterface"), m_situationMap);
if (mapSettingJson)
mapInitializer->setMapSettingsJson(*mapSettingJson);
mapInitializer->setViewPoint(viewPoint); // viewPoint is allowed to be null :-)
if (mapInitializer->hasErrors())
{
FATAL_LOG(m_logger) << LOGFUNCTION << " MapInitializer-filename='" << mapInitializer->filename()
<< "'' has following errors: "
<< " exists = " << mapInitializer->exists()
<< " accessError = " << mapInitializer->accessError()
<< " formatError = " << mapInitializer->formatError();
success = false;
}
else
success = mapInitializer->initMap(mapMode);
#ifdef USE_FUNCTIONDIALOG
// add FunctionDialog if needed:
if (m_situationMap)
SituationMap::FunctionDialog::addButtonToMap(m_situationMap);
#endif // USE_FUNCTIONDIALOG
TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success;
return success;
}
void SituationMapInit::myMapWidgetDialogIsAboutToBeClosed()
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " START";
// cleanup MapObjects and ESRI-Objects:
if (m_adapterDds)
{
m_adapterDds->stop();
m_adapterDds->clearObjects();
m_adapterDds.clear();
}
// If the user performs a logout, the MainDialog/LtsDialog/MyMapWidgetDialog
// (and children) is going to be destroyed.
//
if (m_situationMap && !m_situationMap->property(MapCacheState).isNull())
{
// m_situationMap has property (means 3D), so it should stay at the cache.
// But prevent destruction by interrupting parent-child-relationship
// and destroy some children like functionDialog, LayerControlDialog etc.
INFO_LOG(m_logger) << LOGFUNCTION << " MapCache: prevent destruction";
m_situationMap->setParent(nullptr, true); // parent, beginDestroyFini
// do not remove property !! //m_situationMap->setProperty(MapCacheState, QVariant());
}
}
QPointer<SituationMap::MovementTrackManager> SituationMapInit::movementTrackManager() const
{
if (!m_adapterDds)
return {};
return m_adapterDds->movementTrackManager();
}
bool SituationMapInit::updateAll()
{
if (m_situationMap == nullptr)
return false;
if (!m_situationMap->isReady())
{
INFO_LOG(m_logger) << LOGFUNCTION << " Map Not ready Yet: Skip ";
return false;
}
// m_situationMap->rubberbandFactory();
return true;
}
void SituationMapInit::onMapbeginDestroy()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (m_adapterDds)
{
m_adapterDds->stop();
m_adapterDds.clear();
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void SituationMapInit::onMapObjectsClicked(const SituationMap::Map::RefPointerMapObjectList &mapObjList,
const QPointF &viewportPosition)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " ViewPort X: " << viewportPosition.x() << " Y: " << viewportPosition.y();
onMapObjectClickedByArea(mapObjList);
}
void SituationMapInit::onMapObjectClickedByArea(const SituationMap::Map::RefPointerMapObjectList &mapObjList)
{
//
// In Case of assignment of MountingPoints at resourceApp of ISM :
//
if (m_skipMapObjectSelectionDialog)
{
QVector<SituationMap::UniqueMapId> uniqueMapIdVec;
uniqueMapIdVec.reserve(mapObjList.size());
for (const auto &mapObject : mapObjList)
uniqueMapIdVec.append(mapObject->uniqueMapId());
m_adapterDds->onMapObjectsClicked(uniqueMapIdVec);
return;
}
// : In Case of assignment of MountingPoints at resourceApp of ISM
//
// typical :
//
// size = 0 || 1 || > 1
// 0 -> nothing, stays as before
// 1 -> open InfoDialog direct
// >1 -> create, update content of mapObjectSelection
if (mapObjList.isEmpty())
return;
if (mapObjList.size() == 1)
{
//
// open InfoDialog for a single object immediately:
//
const auto &mapObj = mapObjList.front();
{
if (!m_adapterDds->onMapObjectClicked(mapObj))
WARNING_LOG(m_logger) << LOGFUNCTION << " Click on a map object but found no handler for it ... "
<< " mapObj: debugInfo='" << mapObj->debugInfo() << "'"
<< " uniqueMapId=" << mapObj->uniqueMapId();
}
return;
}
emit mapObjectsClicked(mapObjList);
}
void SituationMapInit::onProcessMapObjectClicked(const QVector<SituationMap::UniqueMapId> &uniqueMapIds)
{
if (!m_adapterDds)
return;
if (!m_adapterDds->onMapObjectsClicked(uniqueMapIds))
WARNING_LOG(m_logger) << LOGFUNCTION << " onMapObjectsClicked failed ... ";
}
void SituationMapInit::onContextMenuRequested(const SituationMap::Map::RefPointerMapObjectList &mapObjList,
const QPointF &viewportPosition)
{
Q_UNUSED(mapObjList);
if (!m_situationMap)
return;
QMenu *contextMenu = new QMenu(); // WA_DeleteOnClose -> no parent required
contextMenu->setAttribute(Qt::WA_DeleteOnClose);
{ // 1
QAction *act = contextMenu->addAction(tr("Area Selection"));
connect(act, &QAction::triggered, this, [this]()
{
if (m_situationMap)
m_situationMap->pickMapObjectsArea();
});
}
{ // 2
QAction *act = contextMenu->addAction(tr("Area Scale"));
connect(act, &QAction::triggered, this, [this]()
{
if (m_situationMap)
m_situationMap->scaleByArea();
});
}
if (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SituationMap::SituationMapMeasureDistance))
{
{ // 3
QAction *act = contextMenu->addAction(tr("Start Measure Length"));
connect(act, &QAction::triggered, this, [this]()
{
if (m_situationMap)
m_situationMap->measurement().measureLength();
});
}
{ // 4
QAction *act = contextMenu->addAction(tr("Clear Measure Length"));
connect(act, &QAction::triggered, this, [this]()
{
if (m_situationMap)
m_situationMap->measurement().clear();
});
}
}
// 5
QAction *saveAsImageAction = contextMenu->addAction(tr("Save as image"));
connect(saveAsImageAction, &QAction::triggered,
this, &SituationMapInit::saveAsImageRequested);
// QAction *terrainProfileStart = contextMenu.addAction(tr("Start Terrain Profile"));
// QAction *terrainProfileClear = contextMenu.addAction(tr("Terrain Profile"));
contextMenu->popup(m_situationMap->mapToGlobal(viewportPosition.toPoint()));
}
} // namespace SituationMap