Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+650
View File
@@ -0,0 +1,650 @@
/**
* @RDE_HEADER@
* Handle situation display in thp
* $Id$
* $HeadURL$
* $Author$
*/
#include "thp_situation_widget.h"
#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 : qAsConst(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"