Files
qt-migration-legatus/qt6-after/qt6-migrated-legatus/Lts/Application/THP/thpwidgets/thp_situation_widget.cpp
T
2026-10-03 09:20:13 +02:00

652 lines
22 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@
* Handle situation display in thp
* $Id$
* $HeadURL$
* $Author$
*/
#include "thp_situation_widget.h"
#include <utility>
#include "ui_thp_situation_building_base.h"
#include "thp_situation_filter_aes.h"
#include "thp_situation_filter_symbol_appearance.h"
#include "thp_situation_filter_collapse_expand.h"
#include "thp_situation_filter_types.h"
#include "thp_situation_filter_building_components.h"
#include "thp_plugin_provider.h"
#include <QDir>
#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QKeyEvent>
#include <QMenu>
#include <QMessageBox>
#include <QPainter>
#include <QSvgRenderer>
#include <QTimer>
#include <QThread>
#include <FileManagement/flm_db_structs.h>
#include <FilterManagement/flt_participant_plugin_interface.h>
#include <FilterManagement/flt_aes_plugin_interface.h>
#include <FilterManagement/flt_building_cmpnt_plugin_interface.h>
#include <FilterManagement/flt_building_cmpnt_settings.h>
#include <FilterManagement/Gui/Aes/Common/aes_display_filter_impl.h>
#include <FilterManagement/Gui/Aes/Common/symbol_appearance_settings_impl.h>
#include <SituationReplay/handler_situation_replay_sqlite.h>
#include <SituationMap/situation_map_init.h>
#include <SituationMap/Utils/movement_track_manager.h>
#include <SituationMap/Map/grid.h>
#include <SituationMap/Map/point3D.h>
#include <SituationMap/Map/map_initializer.h>
#include <SituationMap/Map/map_notifier.h>
#include <SituationMap/Map/operator_map_settings.h>
#include <SituationMap/Map/overlay_handler.h>
#include <SituationMap/Symbology/sidc_codec.h>
#include <PlanOverlays/Gui/plan_overlay_model.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <NGLLogger/INGLLogger.h>
#include <ngltools/ngltools_dtg.h>
namespace THP
{
class MySitMapIinit final : public SituationMap::SituationMapInit
{
Q_OBJECT
public:
using SituationMap::SituationMapInit::SituationMapInit;
bool trainingAreaExists() const override { return true; }
SituationMap::MapSettingsJson getMapSettingsFromService() const override { return m_mapSettingsJson; }
void setMapSettingsJson(const SituationMap::MapSettingsJson &settings) { m_mapSettingsJson = settings; }
const SituationMap::MapSettingsJson & getMapSettingsJson() const { return m_mapSettingsJson; }
private:
SituationMap::MapSettingsJson m_mapSettingsJson;
};
using namespace SituationReplay;
SituationWidget::SituationWidget(QWidget *parent)
: SituationBuildingBase("THP.SituationWidget", parent)
, m_pluginProvider(new SitMapPluginProvider)
, m_situationMapInit(new MySitMapIinit(nullptr))
{
QMenu *m = new QMenu(m_ui->btnFilter);
m->addAction(tr("AES Filter"), this, &SituationWidget::onFilterAes);
// 29.09.2022, Norbert Thoden
// SWPÄ 2:22 GUEZE-1477 Kartenfilter für Komponenten:
// Es wurde der Komponenten Filter eingeführt.
// in der LiveLage werden allerdings nur MountingPoints angezeigt, die auch eine
// zugeordnetes Asset haben. Die Zuordnug befindet sich in der Tabelle ism.sim_mounting_point_asset.
// ism.sim_mounting_point_asset wird derzeit nicht in die sitreplay.sqlite
// überführt. Es koennen also keine Komponenten angezeigt / gefiltert werden...
// FilterDialog nicht anzeigen:
// m->addAction(tr("Building / Component"), this, &SituationWidget::onFilterBuildingComponent);
m->addAction(tr("Symbol Appearance"), this, &SituationWidget::onFilterSymbolAppearance);
m->addAction(tr("Collpase/Expand"), this, &SituationWidget::onFilterCollapseExpand);
m->addAction(tr("Type filter"), this, &SituationWidget::onFilterTypes);
m_ui->btnFilter->setMenu(m);
}
SituationWidget::~SituationWidget()
{
if (m_replayHandler->handlerSrp())
m_replayHandler->handlerSrp()->onStopReplayRequest();
delete m_situationMapInit;
delete m_pluginProvider;
delete m_replayHandler;
}
bool SituationWidget::init(const QString &homePath, int exerciseId)
{
m_pluginProvider->setExerciseId(exerciseId);
delete m_replayHandler;
m_replayHandler = new SituationReplay::MainHandlerSqlite;
if (!SituationBuildingBase::init(m_replayHandler))
return false;
loadLayerMappings(homePath);
SituationReplay::ConnectionParams connParams;
connParams.domainId = 1;
connParams.partitionId = 1;
if (!m_replayHandler->initDds(connParams))
return false;
if (!m_pluginProvider->init(m_replayHandler->ddsReplay()))
return false;
if (!m_situationMapInit->initPluginProvider(m_pluginProvider))
return false;
QPointer<SituationMap::MovementTrackManager> movementTrackMgr =
QPointer<SituationMap::MovementTrackManager>(new SituationMap::MovementTrackManager(m_situationMapInit));
FATAL_LOG(m_logger) << LOGFUNCTION << " XXXX initMap....";
if (!m_situationMapInit->initMap(false, SituationMap::Mode::Mode_2D))
return false;
if (!m_situationMapInit->initAdapter(movementTrackMgr, false))
return false;
SituationMap::Map *map = m_situationMapInit->getWidget();
if (!map)
return false;
connect(map, &SituationMap::Map::mouseGeoPositionChanged,
this, &SituationWidget::updatePositionLabel,
Qt::UniqueConnection);
connect(map, &SituationMap::Map::extentChanged,
this, &SituationWidget::updateZoomSlider);
map->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_ui->vLayout->insertWidget(0, map);
m_aesDisplayFilter = m_pluginProvider->fltAesIfc()->aesFilterForOperator(0, QString());
m_symbolAppaearanceSettings = m_pluginProvider->fltParticipantIfc()->symbolAppearanceForOperator(0, QString());
m_buildingComponentSettings = m_pluginProvider->fltBuildingComponentIfc()->buildingComponentSettingsForOperator(0, QString());
return true;
}
bool SituationWidget::showSituationInternal()
{
// cleanup 'old' map:
// remove old overlays from former session:
{
SituationMap::Map *map = m_situationMapInit->getWidget();
if (map)
map->overlayHandler().removeAllOverlays();
}
m_ui->cbPlaybackParts->setVisible(m_replayInformation.size() > 1);
// MAYBE there are filter to be applied:
applyFilterFromReplayConfigFile();
if (m_replayInformation.isEmpty())
{
WARNING_LOG(m_logger) << "No replay information given...";
return false;
}
const QString sqliteDb = m_replayInformation.first().databaseFilePath;
if (sqliteDb.isEmpty())
{
WARNING_LOG(m_logger) << "No database given...";
return false;
}
QFileInfo fi(sqliteDb);
if (!fi.exists() || !fi.isFile() || !fi.isReadable())
{
Q_ASSERT(false);
WARNING_LOG(m_logger) << "Can not access sqlite database '" << fi.absoluteFilePath() << "'";
return false;
}
m_ui->cbPlaybackParts->clear();
m_ui->cbPlaybackParts->blockSignals(true);
for (int i = 0; i < m_replayInformation.size(); ++i)
{
const ReplayInformation &info = m_replayInformation.at(i);
m_ui->cbPlaybackParts->addItem(QStringLiteral("%1: %2 - %3").arg(i+1).arg(DTG::toStringMSec(info.timeSpan.begin, DTG::Long),
DTG::toStringMSec(info.timeSpan.end, DTG::Long)), i);
}
m_ui->cbPlaybackParts->setCurrentIndex(0);
m_ui->cbPlaybackParts->blockSignals(false);
onCbPlaybackPartsChanged(); // creates replayHandler for correct db etc.
SituationMap::Map *map = m_situationMapInit->getWidget();
if (map->isReady())
QTimer::singleShot(0, this, &SituationWidget::onInitDone);
else
connect(map, &SituationMap::Map::mapReady,
this, &SituationWidget::onInitDone, Qt::QueuedConnection);
return true;
}
bool SituationWidget::applyFilterFromReplayConfigFile()
{
// Maybe the Situation topic has own Filter (AesFilter, SymbolAppearance; OperatorMapSettings)
// Read and apply them:
if (m_replayInformation.isEmpty())
{
WARNING_LOG(m_logger) << "No replay information given...";
return false;
}
const QString replayConfigFile = m_replayInformation.first().replayConfigPath; // sitreplay.json
if (replayConfigFile.isEmpty())
{
WARNING_LOG(m_logger) << "No replayConfigFile given...";
return false;
}
QFileInfo fi(replayConfigFile);
if (!fi.exists() || !fi.isFile() || !fi.isReadable())
{
WARNING_LOG(m_logger) << "Can not access replayConfigFile '" << fi.absoluteFilePath() << "'";
return false;
}
QFile f(replayConfigFile);
if (!f.open(QIODevice::ReadOnly))
{
WARNING_LOG(m_logger) << "Can not open replayConfigFile '" << fi.absoluteFilePath() << "': " << f.errorString();
return false;
}
QJsonParseError jError;
const QByteArray ba = f.readAll();
QJsonDocument doc(QJsonDocument::fromJson(ba, &jError));
if (jError.error)
{
WARNING_LOG(m_logger) << "Can not parse json document: "
<< ", replayConfigFile '" << fi.absoluteFilePath() << "'"
<< ", errorString= " << jError.errorString();
return false;
}
const QJsonObject &rootObj = doc.object();
const QJsonObject &filters = rootObj[QStringLiteral("filters")].toObject(); // optional!!!
if (filters.isEmpty())
{
WARNING_LOG(m_logger) << "no 'filters' provided at "
<< " replayConfigFile '" << fi.absoluteFilePath() << "'";
return false;
}
DEBUG_LOG(m_logger) << LOGFUNCTION << " XXXX replayConfigFile='" << fi.absoluteFilePath() << "'"
<< " doc.toJson = " << doc.toJson();
// fine, put together
FilterInformation filterInfo;
filterInfo.aesFilters = filters[QStringLiteral("AesFilterJson")].toObject();
filterInfo.symbolSettings = filters[QStringLiteral("SymbolAppearanceJson")].toObject();
filterInfo.buildingComponentSettings = filters[QStringLiteral("BuildingComponentSettingsJson")].toObject();
filterInfo.mapSettings = rootObj[QStringLiteral("settings")].toObject();
applyCurentFilter(filterInfo);
return true;
}
void SituationWidget::applyCurentFilter(const SituationReplay::FilterInformation &filter)
{
m_aesDisplayFilter->setFilterFromData(filter.aesFilters);
m_symbolAppaearanceSettings->setSettings(filter.symbolSettings, FilterManagement::SymbolAppearanceSettings::Part::All);
m_buildingComponentSettings->setBldCmpSettings(filter.buildingComponentSettings);
SituationMap::OperatorMapSettings oms;
oms.fromJsonObject(filter.mapSettings);
if (oms.isEmpty() || oms.mode == SituationMap::Mode::Undefined)
return;
if (oms.mode != m_situationMapInit->getWidget()->mode())
{
INFO_LOG(m_logger) << " Switching to new mode : " << oms.mode;
SituationMap::MapSettingsJson savedMapSettingsJson;
if (m_situationMapInit)
{
savedMapSettingsJson = m_situationMapInit->getMapSettingsJson(); // containing path from layerMappping.json
m_situationMapInit->stopDisplay();
m_situationMapInit->clearObjects();
}
delete m_situationMapInit;
m_situationMapInit = new MySitMapIinit(nullptr);
m_situationMapInit->setMapSettingsJson(savedMapSettingsJson);
m_situationMapInit->initPluginProvider(m_pluginProvider);
m_situationMapInit->initMap(false, oms.mode);
QPointer<SituationMap::MovementTrackManager> movementTrackMgr =
QPointer<SituationMap::MovementTrackManager>(new SituationMap::MovementTrackManager(m_situationMapInit));
if (!m_situationMapInit->initAdapter(movementTrackMgr, false))
return;
SituationMap::Map *map = m_situationMapInit->getWidget();
if (map)
{
connect(map, &SituationMap::Map::mouseGeoPositionChanged,
this, &SituationWidget::updatePositionLabel,
Qt::UniqueConnection);
connect(map, &SituationMap::Map::extentChanged,
this, &SituationWidget::updateZoomSlider);
map->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_ui->vLayout->insertWidget(0, map);
static bool s_waitFlag = true;
QPointer<SituationMap::MapNotifier> mapNotifier = map->createMapNotifier();
if (mapNotifier)
{
connect(mapNotifier.data(), &SituationMap::MapNotifier::mapReadyAndSymbolDictionaryLoaded, this, [this, oms]()
{
s_waitFlag = false;
if (!restoreOperatorMapSettingsOverlays(oms))
WARNING_LOG(m_logger) << "restoreOperatorMapSettingsOverlays failed";
});
mapNotifier->run();
// snyc wait :-(
{
DEBUG_LOG(m_logger) << " WAIT_SYNC BEGIN";
int i=0;
while(i<100 && s_waitFlag) // 100 * 50ms = 5000ms
{
qApp->processEvents(QEventLoop::ExcludeUserInputEvents|QEventLoop::ExcludeSocketNotifiers, 50);
QThread::msleep(50);
++i;
DEBUG_LOG(m_logger) << " WAIT_SYNC ..., i=" << i;
}
DEBUG_LOG(m_logger) << " WAIT_SYNC ...DONE, i=" << i;
DEBUG_LOG(m_logger) << " WAIT_SYNC, m_situationMapInit->getWidget()->mode()" << m_situationMapInit->getWidget()->mode();
m_situationMapInit->startDisplay();
}
} // if (mapNotifier)
}
} else {
if (!restoreOperatorMapSettingsOverlays(oms))
WARNING_LOG(m_logger) << "restoreOperatorMapSettingsOverlays failed";
}
}
bool SituationWidget::restoreOperatorMapSettingsOverlays(const SituationMap::OperatorMapSettings &oms)
{
if (!m_situationMapInit || !m_situationMapInit->getWidget() || oms.isEmpty())
return false;
if (!m_situationMapInit->getWidget()->restoreOperatorMapSettings(oms))
WARNING_LOG(m_logger) << LOGFUNCTION << " restoreOperatorMapSettings failed";
// continue!!! maybe just LayerOrder failed.... ToDo
// 2. handle Overlays :
// SituationMap can`t restore the overlays on it´s own:
// So we have to do it here:
const QList<int> &orderedOverlayIds = oms.orderedOverlays;
DEBUG_LOG(m_logger) << LOGFUNCTION << " restore OverlayIds...";
for (auto overlayId : std::as_const(orderedOverlayIds))
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " applyNewOverlay , overlayId=" << overlayId;
applyNewOverlay(overlayId);
}
return true;
}
void SituationWidget::applyNewOverlay(int overlayId)
{
const QString filename = QStringLiteral("%1/%2.TacticalPlan").arg(m_scriptPath).arg(overlayId);
QFile f(filename);
if (!f.open(QIODevice::ReadOnly))
{
WARNING_LOG(m_logger) << "Can not open tactical plan '" << filename << "': " << f.errorString();
return;
}
SituationMap::OverlayHandler &overlayHandler = m_situationMapInit->getWidget()->overlayHandler();
if (!overlayHandler.setOverlay(overlayId, tr("Plan '%1'").arg(overlayId), f.readAll(), false))
WARNING_LOG(m_logger) << "can not display overlay " << filename;
}
void SituationWidget::removeOverlay(int overlayId)
{
SituationMap::OverlayHandler &overlayHandler = m_situationMapInit->getWidget()->overlayHandler();
overlayHandler.removeOverlay(overlayId);
}
void SituationWidget::onFilterAes()
{
if (!m_filterAesDialog)
m_filterAesDialog = new SituatationFilterAes(m_aesDisplayFilter,
m_pluginProvider, this);
m_filterAesDialog->show();
}
void SituationWidget::onFilterSymbolAppearance()
{
if (!m_filterSymbolAppearanceDialog)
m_filterSymbolAppearanceDialog = new SituatationFilterSymbolAppearance(m_symbolAppaearanceSettings,
m_pluginProvider, this);
m_filterSymbolAppearanceDialog->show();
}
void SituationWidget::onFilterCollapseExpand()
{
if (!m_filterCollapseExpandDialog)
m_filterCollapseExpandDialog = new SituatationFilterCollapseExpand(m_symbolAppaearanceSettings,
m_pluginProvider, this);
m_filterCollapseExpandDialog->show();
}
void SituationWidget::onFilterTypes()
{
if (!m_filterTypesDialog)
m_filterTypesDialog = new SituatationFilterTypes(m_symbolAppaearanceSettings,
m_pluginProvider, this);
m_filterTypesDialog->show();
}
void SituationWidget::onFilterBuildingComponent()
{
if (!m_filterBuildingComponentDialog)
m_filterBuildingComponentDialog = new SituationFilterBuildingComponent(m_buildingComponentSettings,
m_pluginProvider, this);
m_filterBuildingComponentDialog->show();
}
void SituationWidget::emitPartitionTimeChanged(qint64 curTime)
{
m_pluginProvider->emitPartitionTimeChanged(curTime);
}
void SituationWidget::updateZoomSlider()
{
// this is not really what we want to display... but currently we hide the slider anyway
m_ui->zoomSlider->setZoomValue(m_situationMapInit->getWidget()->scale() / 1000);
}
QString SituationWidget::centerPointString() const
{
const QPointF center = m_situationMapInit->getWidget()->centerPoint();
LtsCoordTrans::UtmCoordinate coord(LtsCoordTrans::UtmCoordinate::fromGeographic(center.x(), center.y()));
return coord.mgrs();
}
QRectF SituationWidget::extentMgrs() const
{
const QRectF r = m_situationMapInit->getWidget()->extent();
LtsCoordTrans::UtmCoordinate topLeft(LtsCoordTrans::UtmCoordinate::fromGeographic(r.left(), r.top()));
LtsCoordTrans::UtmCoordinate bottomRight(LtsCoordTrans::UtmCoordinate::fromGeographic(r.right(), r.bottom(), topLeft.zone));
return QRectF(QPointF(topLeft.x, topLeft.y), QPointF(bottomRight.x, bottomRight.y));
}
QImage SituationWidget::grabFrameBuffer() const
{
return m_situationMapInit->getWidget()->grab().toImage();
}
void SituationWidget::onInitDone()
{
SituationBuildingBase::onInitDone();
m_situationMapInit->startDisplay();
}
void SituationWidget::keyPressEvent(QKeyEvent *event)
{
switch (event->key())
{
case Qt::Key_F10:
onMgrsGrid();
break;
case Qt::Key_PageUp:
onRotate(10);
break;
case Qt::Key_PageDown:
onRotate(-10);
break;
default:
QFrame::keyPressEvent(event);
break;
}
}
void SituationWidget::onMgrsGrid()
{
SituationMap::Map *m = m_situationMapInit->getWidget();
m->grid().setVisible(!m->grid().isVisible());
}
void SituationWidget::onRotate(int degrees)
{
SituationMap::Map *m = m_situationMapInit->getWidget();
m->setRotation(m->rotation() + degrees);
}
void SituationWidget::onFileContentAvailable(const FileManagement::Database::FlmFileContent &data)
{
if (data.fileTypeId != FileManagement::FileType::OperatorDefinedSymbol ||
data.fileStatusId != FileManagement::FileStatus::Valid ||
data.content.size() == 0)
return;
QColor colorToBeReplaced;
bool bIsXml = false;
if (!data.properties.isEmpty())
{
QJsonParseError parseError;
const QJsonDocument jDoc(QJsonDocument::fromJson(data.properties.toUtf8(), &parseError));
const QJsonObject obj(jDoc.object());
const QLatin1String imageFormatKey("imageFormat");
bIsXml = obj.value(imageFormatKey).toString() == QLatin1String("xml");
if (!bIsXml)
{
// partyColor
const QLatin1String partyColorStr("partyColor");
if (obj.contains(partyColorStr))
{
const QJsonObject partyColor = obj[partyColorStr].toObject();
if (partyColor.size() == 3) // r, g, b
{
// update map:
colorToBeReplaced.setRgb(
partyColor[QLatin1String("r")].toInt(),
partyColor[QLatin1String("g")].toInt(),
partyColor[QLatin1String("b")].toInt());
} // if (partyColor.size() == 3)
} // if (obj.contains(partyColorStr))
}
}
AdvancedSymbology::SymbologyType symType = AdvancedSymbology::SymbolDictionary::defaultSymbologyType();
AdvancedSymbology::SymbolDictionary &dict =
AdvancedSymbology::SymbolDictionary::dictionary(symType);
if (bIsXml)
{
dict.setXmlSymbol(data.id, data.name, QStringLiteral("OperatorName"), 0, QString::fromUtf8(data.content), AdvancedSymbology::SymbolProperties());
}
else
{
QImage image;
QSvgRenderer svgRenderer;
if (!svgRenderer.load(data.content))
return;
// prepare Image:
constexpr int size = 200; // pixel
QImage imageOut(size, size, QImage::Format_ARGB32);
imageOut.fill(Qt::transparent);
// render
QPainter painter(&imageOut);
svgRenderer.render(&painter);
painter.end();
AdvancedSymbology::SymbolProperties symProps;
symProps.setKey(data.name);
symProps.setSidc(AdvancedSymbology::SidcCodec::encodeCivilEts(data.name));
symProps.setName(data.name);
symProps.setSymbolClass(QLatin1String("3")); // 18.10.2016, N.Thoden: 3 == PointSymbol (mil2525d.stylx - QuartzRelease)
symProps.setGeometryType(AdvancedSymbology::GeometryType::Point); // ETS-Symbols are always Points
symProps.setTags({data.name});
symProps.setColorToBeReplaced(colorToBeReplaced);
dict.setEtsSymbol(data.id, data.name, symProps, image);
}
}
void SituationWidget::loadLayerMappings(const QString &homePath)
{
const QString mappingsFile = homePath + QStringLiteral("/layerMapping.json");
QFile f(mappingsFile);
if (!f.open(QIODevice::ReadOnly))
{
fprintf(stderr, "Could not load layer mappings from '%s' - %s\n", mappingsFile.toStdString().c_str(), f.errorString().toStdString().c_str());
WARNING_LOG(m_logger) << "Could not load layer mappings from " << mappingsFile << ": " << f.errorString();
return;
}
m_layerMapping.clear();
const QJsonObject obj = QJsonDocument::fromJson(f.readAll()).object();
SituationMap::MapSettingsJson settings;
if (!settings.fromJsonObject(obj))
{
WARNING_LOG(m_logger) << "Could not parse layer mappings from " << mappingsFile;
return;
}
settings.setMapUrls(fixupUrls(settings.getMapUrls(), homePath));
settings.setSceneUrls(fixupUrls(settings.getSceneUrls(), homePath));
settings.setSceneObjectUrls(fixupUrls(settings.getSceneObjectUrls(), homePath));
settings.setElevationUrls(fixupUrls(settings.getElevationUrls(), homePath));
m_situationMapInit->setMapSettingsJson(settings);
}
QVector<SituationMap::BaseUrl> SituationWidget::fixupUrls(const QVector<SituationMap::BaseUrl> &in,
const QString &homePath)
{
QVector<SituationMap::BaseUrl> out;
for (auto url : in)
{
const QString fn = QStringLiteral("%1/Maps/%2").arg(homePath, url.url());
if (!QFile::exists(fn))
{
fprintf(stderr, "Can not find map data file '%s'\n", fn.toStdString().c_str());
QMessageBox::warning(this, tr("Can not find map data"),
tr("Can not find map data file '%1' - can't start application").arg(fn));
QCoreApplication::quit();
return out;
}
url.setUrl(QUrl::fromLocalFile(fn).toString());
out.push_back(std::move(url));
}
return out;
}
} // namespace THP
#include "thp_situation_widget.moc"