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

@@ -0,0 +1,56 @@
#
# $Id: CMakeLists.txt 46327 2019-03-28 16:06:35Z hjl $
# $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/ParticipantManagement/Gui/MSDL/CMakeLists.txt $
# $Author: hjl $
# Created by Hannes Fritsche, ESZS, 2024-08-05
#
FIND_PACKAGE(KDReports REQUIRED)
SET(cds_gui_SRCS
cds_building_model.cpp
cds_building_model.h
cds_calculations.cpp
cds_calculations.h
cds_create_simulation_dialog.cpp
cds_create_simulation_dialog.h
cds_create_simulation_gui_plugin.cpp
cds_create_simulation_gui_plugin.h
cds_plan_creator.cpp
cds_plan_creator.h
cds_participant_model.cpp
cds_participant_model.h
cds_pdf_creator.cpp
cds_pdf_creator.h
cds_simulation_dialog.cpp
cds_simulation_dialog.h
)
NGL_WRAP_UI(cds_gui_SRCS
cds_create_simulation_dialog.ui
cds_simulation_dialog.ui
)
NGL_ADD_LIBRARY(WS CdsSimulationGuiPlugin MODULE ${cds_gui_SRCS})
NGL_TARGET_LINK_LIBRARIES(CdsSimulationGuiPlugin
CdsGuiPluginInterface
IsmDataPluginInterface
FlmDataPluginInterface
KDReports::KDReports
ltsCoordWidget
LtsCoordTrans
LtsSession
LtsSessionData
LtsWidgets
NGLLogger
ngltools
PdmDataPluginInterface
PpmDataPluginInterface
Qt5::Widgets
situationMap
situationMapGeometryModel
SituationMapGuiPluginInterface
svcAesCommon
)
NGL_INSTALL_TARGET(CdsSimulationGuiPlugin)
@@ -0,0 +1,63 @@
/**
* @RDE_HEADER@
* The BuildingModel provices building data for SimulationDialog
* $Id: ism_asset_list_dialog_model.h 58852 2023-02-01 11:28:56Z fritsche_h $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/InfrastructureManagement/Gui/ResourceManagement/AssetList/ism_asset_list_dialog_model.h $
* $Author: fritsche_h $
* Created by Hannes Fritsche, ESZS, 2024-09-20
*/
#include "cds_building_model.h"
#include <InfrastructureManagement/ism_dtc.h>
namespace CollateralDamageSimulation
{
BuildingModel::BuildingModel(QObject *parent)
: LtsWidgets::LtsAbstractTableModel<QString>(
{
tr("Name")
},
parent)
{
}
QVariant BuildingModel::data(const QModelIndex &index, int role) const
{
if (role != Qt::DisplayRole || !indexIsValid(index))
return QVariant();
if (role == Qt::DisplayRole)
{
switch (index.column())
{
case NAME:
return m_vecData.at(index.row());
default:
return QVariant();
}
}
return QVariant();
}
bool BuildingModel::initModel(const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc, const QVector<int> &buildingIds)
{
m_vecData.clear();
m_vecData.reserve(buildingIds.size());
if (!ismDtc->isMainStructureElementEnabled())
{
ismDtc->enableMainStructureElement();
ismDtc->waitForHistoricalDataMainStructureElement(3000);
}
for (int currentBuildingId : buildingIds)
m_vecData.append(QString::fromStdString(ismDtc->mainStructureElement(currentBuildingId).name()));
return true;
}
} // namespace CollateralDamageSimulation
@@ -0,0 +1,42 @@
/**
* @RDE_HEADER@
* The BuildingModel provices building data for SimulationDialog
* $Id: ism_asset_list_dialog_model.h 58852 2023-02-01 11:28:56Z fritsche_h $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/InfrastructureManagement/Gui/ResourceManagement/AssetList/ism_asset_list_dialog_model.h $
* $Author: fritsche_h $
* Created by Hannes Fritsche, ESZS, 2024-09-20
*/
#pragma once
#include <LtsWidgets/lts_abstract_table_model.h>
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace CollateralDamageSimulation
{
class BuildingModel final : public LtsWidgets::LtsAbstractTableModel<QString>
{
Q_OBJECT
public:
explicit BuildingModel(QObject *parent = nullptr);
enum Columns
{
NAME = 0
};
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool initModel(const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc, const QVector<int> &buildingIds);
}; // class BuildingModel
} // namespace CollateralDamageSimulation
@@ -0,0 +1,557 @@
/**
* Namespace for calculations, needed by CollateralDamageSimulation
* @RDE_HEADER@
* $Id: ppm_msdl_edit.h 52239 2020-10-05 11:33:12Z thoden $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/ParticipantManagement/Gui/MSDL/ppm_msdl_edit.h $
* $Author: thoden $
* Created by Hannes Fritsche, ESZS, 2024-09-03
*/
#include "cds_calculations.h"
#include <InfrastructureManagement/ism_dtc.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <QPolygonF>
#include <QLineF>
#include <MathUtils/simple_helper.h>
#include <AreaEffectSimulation/Utils/polygons.h>
namespace CollateralDamageSimulation
{
namespace Calculations
{
QHash<int, QPolygonF> getBuildingFootprints(const QVector<int> &buildingIds, const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc, const QPointF &impactPosition)
{
QHash<int, QPolygonF> footprints;
if (buildingIds.isEmpty())
return footprints;
footprints.reserve(buildingIds.size());
for (int buildingId : buildingIds)
{
const QPolygonF &buildingFootprint = getBuildingFootprint(buildingId, ismDtc);
// if the impact position is within a building footprint, the building has to be ignored
if (buildingFootprint.containsPoint(impactPosition, Qt::OddEvenFill))
continue;
footprints.insert(buildingId, buildingFootprint);
}
return footprints;
}
QPolygonF getBuildingFootprint(int buildingId, const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc)
{
if (!ismDtc->isMainFootprintEnabled())
{
ismDtc->enableMainFootprint();
ismDtc->waitForHistoricalDataMainFootprint(3000);
}
if (!ismDtc->isMainStructureElementEnabled())
{
ismDtc->enableMainStructureElement();
ismDtc->waitForHistoricalDataMainStructureElement(3000);
}
QPolygonF footprint; // = absolute positions
QVector<QPointF> relPositions;
const InfrastructureManagement::MainFootprint &mainFootprint = ismDtc->mainFootprint(buildingId);
relPositions.reserve(mainFootprint.elements().size());
for (const InfrastructureManagement::FootprintElement &element : mainFootprint.elements())
{
const Core::RelativePosition &relPos = element.relPosition();
relPositions.append(QPointF(relPos.offsetX(), relPos.offsetY()));
}
const InfrastructureManagement::MainStructureElement &mainStructureElement = ismDtc->mainStructureElement(buildingId);
const Core::GeoPosition &geoPosition = mainStructureElement.geoPosition();
const double angleRad = mainStructureElement.angle() * LtsCoordTrans::deg2rad();
const QPointF referencePointGeo(geoPosition.lon(), geoPosition.lat());
const LtsCoordTrans::UtmCoordinate referencePointUTM = LtsCoordTrans::UtmCoordinate::fromGeographic(referencePointGeo);
const QPointF center;
footprint.reserve(relPositions.size());
for (const auto &relPosition : qAsConst(relPositions))
{
const QPointF relPositionRotate = SituationMap::MathUtils::rotate(relPosition, center, angleRad);
const QPointF geoPositionRotate = referencePointUTM.geographic(relPositionRotate);
footprint.append(geoPositionRotate);
}
return footprint;
}
QVector<QPolygonF> getProtectionAreas(const QHash<int, QPolygonF> &buildingFootprints, double radius, const QPointF &impactPosition)
{
if (isPointInAnyPolygon(buildingFootprints, impactPosition))
return {AreaEffectSimulation::Polygons::circle(impactPosition, radius)};
QPolygonF currentProtectionArea;
QVector<QPolygonF> protectionAreas;
protectionAreas.reserve(buildingFootprints.size());
for (const QPolygonF &currentFootprint : buildingFootprints)
{
currentProtectionArea.clear();
currentProtectionArea.reserve(currentFootprint.size() + 360);
// take the visible points from the center
for (const QPointF currentPoint : currentFootprint)
{
if (isPointVisible(impactPosition, currentPoint, currentFootprint))
currentProtectionArea.append(currentPoint);
}
// order the points by angle
// each of the two sorting algorithms has a weakness if a point is present which is either directly left or right
// of the impact position, so we have to choose one
if (currentProtectionArea.boundingRect().center().x() < impactPosition.x())
{
// does not sort properly if there is a point directly right to center point
std::sort(currentProtectionArea.begin(), currentProtectionArea.end(),
[&impactPosition](const QPointF &a, const QPointF &b)
{
double angleA = QLineF(impactPosition, a).angle();
double angleB = QLineF(impactPosition, b).angle();
return angleA > angleB;
});
}
else
{
// does not sort properly if there is a point directly left to center point
std::sort(currentProtectionArea.begin(), currentProtectionArea.end(), [impactPosition](const QPointF &a, const QPointF &b)
{
double angleA = std::atan2(a.y() - impactPosition.y(), a.x() - impactPosition.x());
double angleB = std::atan2(b.y() - impactPosition.y(), b.x() - impactPosition.x());
return angleA < angleB;
});
}
addOuterBounds(currentProtectionArea, impactPosition, radius);
protectionAreas.append(currentProtectionArea);
}
return mergePolygons(protectionAreas);
}
bool isPointVisible(const QPointF &center, const QPointF &point, const QPolygonF &polygon)
{
for (int i = 0; i < polygon.size(); ++i)
{
const QPointF &currentPoint = polygon.at(i);
const QPointF &nextPoint = polygon.at((i + 1) % polygon.size());
if (point == currentPoint || point == nextPoint)
continue;
if (doLinesIntersect(center, point, currentPoint, nextPoint))
return false;
}
return true;
}
bool doLinesIntersect(const QPointF &a1, const QPointF &a2, const QPointF &b1, const QPointF &b2)
{
auto ccw = [](const QPointF &A, const QPointF &B, const QPointF &C)
{
return ((C.y() - A.y()) * (B.x() - A.x())) > ((B.y() - A.y()) * (C.x() - A.x()));
};
return ccw(a1, b1, b2) != ccw(a2, b1, b2) && ccw(a1, a2, b1) != ccw(a1, a2, b2);
}
void addOuterBounds(QPolygonF &polygon, const QPointF &center, double radius)
{
QPolygonF circle = AreaEffectSimulation::Polygons::circle(center, radius);
LtsCoordTrans::UtmCoordinate startUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(getStartPosForOuterBounds(polygon, circle));
LtsCoordTrans::UtmCoordinate endUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(getEndPosForOuterBounds(polygon, circle));
LtsCoordTrans::UtmCoordinate centerUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(center);
double startRad = std::atan2(startUtm.y - centerUtm.y, startUtm.x - centerUtm.x);
double endRad = std::atan2(endUtm.y - centerUtm.y, endUtm.x - centerUtm.x);
double angleStep = 5.0 * M_PI / 180.0;
if (startRad > endRad)
endRad += 2 * M_PI;
for (double angle = startRad; angle < endRad; angle += angleStep)
{
double x = centerUtm.x + radius * cos(angle);
double y = centerUtm.y + radius * sin(angle);
polygon.prepend(LtsCoordTrans::UtmCoordinate(centerUtm.zone, x, y).geographic());
}
if (endRad > startRad && fmod(endRad - startRad, angleStep) != 0)
{
double x = centerUtm.x + radius * cos(endRad);
double y = centerUtm.y + radius * sin(endRad);
polygon.prepend(LtsCoordTrans::UtmCoordinate(centerUtm.zone, x, y).geographic());
}
}
ParticipantProtectionInfo getParticipantProtectionInfo(const QVector<int> &participantIds, const QVector<QPolygonF> &protectionAreas, const QVector<int> &undergroundBuildingIds, const QVector<int> &directlyHitBuildingIds, const QHash<int, QPolygonF> &buildingFootprints, const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc, const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc)
{
ParticipantProtectionInfo participantProtectionInfo;
QHash<int, QPolygonF> directlyHitBuildingFootprints;
directlyHitBuildingFootprints.reserve(directlyHitBuildingIds.size());
for (int buildingId : directlyHitBuildingIds)
directlyHitBuildingFootprints.insert(buildingId, buildingFootprints.value(buildingId));
participantProtectionInfo.withoutProtection.reserve(participantIds.size());
participantProtectionInfo.withProtection.reserve(participantIds.size());
participantProtectionInfo.participantsInUnderground.reserve(participantIds.size());
if (!ppmDtc->isParticipantPositionEnabled())
{
ppmDtc->enableParticipantPosition();
ppmDtc->waitForHistoricalDataParticipantPosition(3000);
}
if (!ismDtc->isMainStructureElementEnabled())
{
ismDtc->enableMainStructureElement();
ismDtc->waitForHistoricalDataMainStructureElement(3000);
}
for (int currentParticipantId : participantIds)
{
const ParticipantManagement::ParticipantPosition &currentPosition = ppmDtc->participantPosition(currentParticipantId);
if (undergroundBuildingIds.contains(currentPosition.buildingId()))
{
participantProtectionInfo.participantsInUnderground.append(currentParticipantId);
continue;
}
if (directlyHitBuildingIds.contains(currentPosition.buildingId()))
{
participantProtectionInfo.withoutProtection.append(currentParticipantId);
continue;
}
const QPointF geoPosition(currentPosition.pos().lon(), currentPosition.pos().lat());
if (isPointInAnyPolygon(directlyHitBuildingFootprints, geoPosition))
{
participantProtectionInfo.withoutProtection.append(currentParticipantId);
continue;
}
// participants in buildings are always protected
if (currentPosition.roomId() != 0)
{
participantProtectionInfo.withProtection.append(currentParticipantId);
continue;
}
// check if a participant is in a protection area
for (const QPolygonF &currentProtectionArea : protectionAreas)
{
if (currentProtectionArea.containsPoint(geoPosition, Qt::OddEvenFill))
{
participantProtectionInfo.withProtection.append(currentParticipantId);
break;
}
}
if (participantProtectionInfo.withProtection.contains(currentParticipantId))
continue;
// no protection
participantProtectionInfo.withoutProtection.append(currentParticipantId);
}
participantProtectionInfo.withoutProtection.squeeze();
participantProtectionInfo.withProtection.squeeze();
participantProtectionInfo.participantsInUnderground.squeeze();
return participantProtectionInfo;
}
QPointF getStartPosForOuterBounds(const QPolygonF &polygon, const QPolygonF &circle)
{
if (circle.containsPoint(polygon.first(), Qt::OddEvenFill))
return polygon.first();
QPointF firstInnerPoint = polygon.first();
int index = 0;
while (!circle.containsPoint(firstInnerPoint, Qt::OddEvenFill))
{
if (index >= polygon.size() - 1)
return QPointF();
firstInnerPoint = polygon.at(++index);
}
QPointF lastOuterPoint = polygon.at(index - 1);
QLineF intersectionLine(firstInnerPoint, lastOuterPoint);
return firstIntersection(intersectionLine, circle);
}
QPointF getEndPosForOuterBounds(const QPolygonF &polygon, const QPolygonF &circle)
{
if (circle.containsPoint(polygon.last(), Qt::OddEvenFill))
return polygon.last();
QPointF firstInnerPoint = polygon.last();
int index = polygon.size();
while (!circle.containsPoint(firstInnerPoint, Qt::OddEvenFill))
{
if (index <= 0)
return QPointF();
firstInnerPoint = polygon.at(--index);
}
QPointF lastOuterPoint = polygon.at(index + 1);
QLineF intersectionLine(firstInnerPoint, lastOuterPoint);
return firstIntersection(intersectionLine, circle);
}
QPointF firstIntersection(const QLineF &line, const QPolygonF &circle)
{
QPointF prevPoint = circle.last();
QPointF intersectionPoint;
for (int index = 0; index < circle.size(); ++index)
{
const QLineF edge(prevPoint, circle.at(index));
if (edge.intersects(line, &intersectionPoint) == QLineF::BoundedIntersection)
return intersectionPoint;
prevPoint = circle.at(index);
}
return QPointF();
}
bool isPointInAnyPolygon(const QHash<int, QPolygonF> &polygons, const QPointF &point)
{
for (auto it = polygons.constBegin(); it != polygons.constEnd(); ++it)
{
const QPolygonF &currentPolygon = it.value();
if (currentPolygon.containsPoint(point, Qt::OddEvenFill))
return true;
}
return false;
}
QVector<QPolygonF> mergePolygons(const QVector<QPolygonF> &geoPolygons)
{
if (geoPolygons.size() <= 1)
return geoPolygons;
const double scaleFactor = 100000.0;
// since geocoordinates are very small the result of QPolygonF::united is very imprecise.
// the polygons therefore must be scaled up and later be scaled down again
QVector<QPolygonF> mergedPolygons = getScaledPolygons(geoPolygons,scaleFactor);
bool hasIntersection = true;
while (hasIntersection)
{
hasIntersection = false;
for (int outerIndex = 0; outerIndex < mergedPolygons.size(); ++outerIndex)
{
for (int innerIndex = outerIndex + 1; innerIndex < mergedPolygons.size(); ++innerIndex)
{
if (mergedPolygons.at(outerIndex).intersects(mergedPolygons.at(innerIndex)))
{
const QPolygonF mergedPolygon = mergedPolygons.at(outerIndex).united(mergedPolygons.at(innerIndex));
mergedPolygons[outerIndex] = mergedPolygon;
mergedPolygons.removeAt(innerIndex);
hasIntersection = true;
break;
}
if (hasIntersection)
break;
}
}
}
return getScaledPolygons(mergedPolygons, 1.0 / scaleFactor);
}
QVector<QPolygonF> getScaledPolygons(const QVector<QPolygonF> &polygons, double factor)
{
QVector<QPolygonF> scaledPolygons;
QPolygonF scaledPolygon;
scaledPolygons.reserve(polygons.size());
for (const QPolygonF &currentPolygon : polygons)
{
scaledPolygon.reserve(currentPolygon.size());
for (const QPointF &currentPoint : currentPolygon)
scaledPolygon.append(currentPoint * factor);
scaledPolygons.append(scaledPolygon);
scaledPolygon.clear();
}
return scaledPolygons;
}
QVector<int> getUndergroundBuildingIds(const QVector<int> &buildingIds, const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc)
{
if (!ismDtc->isMainStructureElementEnabled())
{
ismDtc->enableMainStructureElement();
ismDtc->waitForHistoricalDataMainStructureElement(3000);
}
QVector<int> undergroundBuildingIds;
undergroundBuildingIds.reserve(buildingIds.size());
// since there is no indicator for underground buildings, we have to parse the buildings name and description
// --> known underground buildings area Kanalisation and U-Bahn Tunnel
const QString kanal = QStringLiteral("kanaele");
const QString uBahn = QStringLiteral("bahn");
for (int currentBuildingId : buildingIds)
{
const InfrastructureManagement::MainStructureElement &building = ismDtc->mainStructureElement(currentBuildingId);
const QString description = QString::fromStdString(building.description());
const QString name = QString::fromStdString(building.name());
if (!(description.contains(kanal, Qt::CaseInsensitive) ||
description.contains(uBahn, Qt::CaseInsensitive) ||
name.contains(kanal, Qt::CaseInsensitive) ||
name.contains(uBahn, Qt::CaseInsensitive)))
continue;
undergroundBuildingIds.append(currentBuildingId);
}
return undergroundBuildingIds;
}
QList<QPointF> getGeoRect(const QPointF &center, double edgeLength)
{
QList<QPointF> geoRect;
const LtsCoordTrans::UtmCoordinate utmCenter = LtsCoordTrans::UtmCoordinate::fromGeographic(center.x(), center.y());
const QPointF topLeft(utmCenter.geographic(-(edgeLength / 2.0), edgeLength / 2.0));
const QPointF topRight(utmCenter.geographic(edgeLength / 2.0, edgeLength / 2.0));
const QPointF bottomRight(utmCenter.geographic(edgeLength / 2.0, -(edgeLength / 2.0)));
const QPointF bottomLeft(utmCenter.geographic(-(edgeLength / 2.0), -(edgeLength / 2.0)));
geoRect.reserve(4);
geoRect.append(topLeft);
geoRect.append(topRight);
geoRect.append(bottomRight);
geoRect.append(bottomLeft);
return geoRect;
}
QVector<int> getDirectlyHitBuildings(const QHash<int, QPolygonF> &footprints, const QPointF &impactPosition)
{
const double angleStep = 0.2;
const int minimumHitCount = 12;
QVector<int> directlyHitBuildingIds;
QHash<int, int> hitBuildingsCount; // <buildingId, hit count>
directlyHitBuildingIds.reserve(footprints.size());
QLineF ray(impactPosition, {impactPosition.x() + 1.0, impactPosition.y()});
for (double currentAngle = 0.0; currentAngle < 360; currentAngle += angleStep)
{
ray.setAngle(currentAngle);
int buildingId = getNearestBlockedBuildingId(ray, footprints);
if (buildingId == -1)
continue;
if (!hitBuildingsCount.contains(buildingId))
hitBuildingsCount.insert(buildingId, 0);
++hitBuildingsCount[buildingId];
}
for (auto it = hitBuildingsCount.constBegin(); it != hitBuildingsCount.constEnd(); ++it)
{
if (it.value() >= minimumHitCount)
directlyHitBuildingIds.append(it.key());
}
directlyHitBuildingIds.squeeze();
return directlyHitBuildingIds;
}
int getNearestBlockedBuildingId(const QLineF &ray, const QHash<int, QPolygonF> &buildings)
{
QMap<double, int> distances; // <distance, buildingId>
for (auto it = buildings.constBegin(); it != buildings.constEnd(); ++it)
{
const QPolygonF &currentPolygon = it.value();
for (int i = 0; i < currentPolygon.count(); ++i)
{
const QPointF p1 = currentPolygon.at(i);
const QPointF p2 = currentPolygon.at((i + 1) % currentPolygon.count());
const QLineF currentEdge(p1, p2);
QPointF intersectionPoint;
if (ray.intersects(currentEdge, &intersectionPoint) != QLineF::BoundedIntersection)
continue;
QLineF distanceLine(ray.p1(), intersectionPoint);
distances.insert(distanceLine.length(), it.key());
}
}
if (distances.isEmpty())
return -1;
return distances.first();
}
} // namespace Calculations
} // namespace CollateralDamageSimulation
@@ -0,0 +1,73 @@
/**
* Namespace for calculations, needed by CollateralDamageSimulation
* @RDE_HEADER@
* $Id: ppm_msdl_edit.h 52239 2020-10-05 11:33:12Z thoden $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/ParticipantManagement/Gui/MSDL/ppm_msdl_edit.h $
* $Author: thoden $
* Created by Hannes Fritsche, ESZS, 2024-09-03
*/
#pragma once
#include <QVector>
#include <QRectF>
#include <memory>
namespace InfrastructureManagement
{
class MainStructureElement;
namespace Interface
{
class Dtc;
}
}
namespace ParticipantManagement
{
namespace Interface
{
class Dtc;
}
}
class QLineF;
class QPointF;
class QPolygonF;
namespace CollateralDamageSimulation
{
namespace Calculations
{
struct ParticipantProtectionInfo
{
QVector<int> withProtection;
QVector<int> withoutProtection;
QVector<int> participantsInUnderground;
};
QHash<int, QPolygonF> getBuildingFootprints(const QVector<int> &buildingIds, const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc, const QPointF &impactPosition);
QVector<int> getDirectlyHitBuildings(const QHash<int, QPolygonF> &footprints, const QPointF &impactPosition);
int getNearestBlockedBuildingId(const QLineF &ray, const QHash<int, QPolygonF> &buildings);
QPolygonF getBuildingFootprint(int buildingId, const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc);
QVector<QPolygonF> getProtectionAreas(const QHash<int, QPolygonF> &buildingFootprints, double radius, const QPointF &impactPosition);
bool isPointVisible(const QPointF &center, const QPointF &point, const QPolygonF &polygon);
bool doLinesIntersect(const QPointF &a1, const QPointF &a2, const QPointF &b1, const QPointF &b2);
void addOuterBounds(QPolygonF &polygon, const QPointF &center, double radius);
QPointF getStartPosForOuterBounds(const QPolygonF &polygon, const QPolygonF &circle);
QPointF getEndPosForOuterBounds(const QPolygonF &polygon, const QPolygonF &circle);
QPointF firstIntersection(const QLineF &line, const QPolygonF &circle);
ParticipantProtectionInfo getParticipantProtectionInfo(const QVector<int> &participantIds,
const QVector<QPolygonF> &protectionAreas,
const QVector<int> &undergroundBuildingIds,
const QVector<int> &directlyHitBuildingIds,
const QHash<int, QPolygonF> &buildingFootprints,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc);
bool isPointInAnyPolygon(const QHash<int, QPolygonF> &polygons, const QPointF &point);
QVector<QPolygonF> mergePolygons(const QVector<QPolygonF> &geoPolygons);
QVector<QPolygonF> getScaledPolygons(const QVector<QPolygonF> &polygons, double factor);
QVector<int> getUndergroundBuildingIds(const QVector<int> &buildingIds, const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc);
QList<QPointF> getGeoRect(const QPointF &center, double edgeLength);
} // namespace Calculations
} // namespace CollateralDamageSimulation
@@ -0,0 +1,451 @@
/**
* @RDE_HEADER@
* $Id: ppm_msdl_edit.h 52239 2020-10-05 11:33:12Z thoden $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/ParticipantManagement/Gui/MSDL/ppm_msdl_edit.h $
* $Author: thoden $
* Created by Hannes Fritsche, ESZS, 2024-08-05
*/
#include "cds_create_simulation_dialog.h"
#include "ui_cds_create_simulation_dialog.h"
#include <Plugins/SituationMapPlugin/situation_map_gui_plugin_interface.h>
#include <Plugins/ProjectDataManagement/pdm_gui_plugin_interface.h>
#include <LtsSession/lts_session.h>
#include <LtsCoordTrans/lts_coord_trans.h>
#include <PluginInterfaces/gui_plugin_interface.h>
#include <PluginInterfaces/data_plugin_interface.h>
#include <FileManagement/Data/flm_data_plugin.h>
#include <InfrastructureManagement/Data/ism_data_plugin.h>
#include <ParticipantManagement/Data/ppm_data_plugin.h>
#include <ProjectDataManagement/Data/pdm_data_plugin.h>
#include <ProjectDataManagement/pdm_translation.h>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
#include <AreaEffectSimulation/Utils/polygons.h>
#include "cds_simulation_dialog.h"
#include "cds_plan_creator.h"
#include "cds_pdf_creator.h"
#include <LtsCoordTrans/lts_coord_trans.h>
#include <float.h>
#include "cds_calculations.h"
namespace CollateralDamageSimulation
{
CreateSimulationDialog::CreateSimulationDialog(QWidget *parent)
: LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel)
, m_ui(new Ui::CreateSimulationDialog)
{
m_ui->setupUi(this);
m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->hide();
m_buttonBox->getButton(LtsWidgets::DialogButton::Apply)->setText(tr("Create"));
setOnlineHelpFilename(QStringLiteral("CDS_Create_Simulation_Dialog"));
}
CreateSimulationDialog::~CreateSimulationDialog()
{
if (m_simulationDialog)
{
m_simulationDialog->closeRequest();
}
}
bool CreateSimulationDialog::init(LtsSession::LtsSession *session )
{
if (session == nullptr)
return false;
m_session = session;
if (!m_session->getDtc<InfrastructureManagement::DataInterface>(m_ismDtc))
{
ERROR_LOG(m_logger) << " failed to get ismDtc";
return false;
}
if (!m_session->getDtc<FileManagement::DataInterface>(m_flmDtc))
{
ERROR_LOG(m_logger) << " failed to get flmDtc";
return false;
}
if (!m_session->getDtc<ParticipantManagement::DataInterface>(m_ppmDtc))
{
ERROR_LOG(m_logger) << " failed to get ppmDtc";
return false;
}
if (!m_session->getDtc<ProjectDataManagement::DataInterface>(m_pdmDtc))
{
ERROR_LOG(m_logger) << " failed to get pdmDtc";
return false;
}
if (!m_session->getForeignGuiInterface<SituationMap::ForeignGuiInterface>(m_sitMapGuiIfc))
return false;
if (!m_session->getForeignGuiInterface<ProjectDataManagement::ForeignGuiInterface>(m_pdmCbx))
return false;
if (!m_planCreator)
m_planCreator = new PlanCreator(this);
std::shared_ptr<FileManagement::Interface::Ftc> flmFtc;
if (!m_session->getFtc<FileManagement::DataInterface>(flmFtc))
{
ERROR_LOG(m_logger) << " failed to get flmFtc";
return false;
}
m_planCreator->init(m_ismDtc, m_ppmDtc, flmFtc);
if (!m_pdfCreator)
m_pdfCreator = new PdfCreator(this);
m_pdfCreator->init(m_ismDtc, m_ppmDtc, flmFtc);
m_session = session;
fillCharges();
connect(m_ui->pbPosition, &QPushButton::clicked,
this, &CreateSimulationDialog::onPositionClicked, Qt::UniqueConnection);
connect(m_ui->pbClear, &QPushButton::clicked,
m_ui->positionEdit, &LtsCoordWidget::LtsCoordWidget::clear, Qt::UniqueConnection);
connect(m_ui->cbChargeTypes, qOverload<int>(&LtsWidgets::LtsComboBox::currentIndexChanged),
this, &CreateSimulationDialog::onChargeTypeChanged, Qt::UniqueConnection);
connect(m_ui->cbIgniterType, qOverload<int>(&LtsWidgets::LtsComboBox::currentIndexChanged),
this, &CreateSimulationDialog::onIgniterTypeChanged, Qt::UniqueConnection);
return true;
}
void CreateSimulationDialog::retranslate()
{
translate(m_ui, tr("Create Simulation"));
m_uiRequirements->clear();
m_uiRequirements->addRequirement( { m_ui->lblName, [this]() { return isNameValid(m_ui->leName->text()); } } );
m_uiRequirements->addRequirement( { m_ui->lblPosition, [this]() { return !m_ui->positionEdit->coordinate().isNull(); } } );
}
void CreateSimulationDialog::onChargeTypeChanged(int index)
{
Q_UNUSED(index);
// update comboBox IgniterType:
m_pdmCbx->fillIgniterComboBox(m_ui->cbIgniterType, m_ui->cbChargeTypes->currentData().toInt());
drawDetonationArea();
}
void CreateSimulationDialog::onIgniterTypeChanged(int index)
{
Q_UNUSED(index);
drawDetonationArea();
}
void CreateSimulationDialog::onPositionClicked()
{
if (!has2DMap())
{
LtsWidgets::LtsMessageDialog::showMessage(this, tr("no"));
return;
}
m_sitMapGuiIfc->getCoordinate(this,
SLOT(onCoordinate(const QPointF &)),
nullptr);
}
void CreateSimulationDialog::onCoordinate(const QPointF &coordinate)
{
m_ui->positionEdit->setCoordinate(coordinate);
drawDetonationArea();
}
bool CreateSimulationDialog::apply()
{
const int folderId = getFolderId();
const QPointF impactPosition = m_ui->positionEdit->coordinate();
const QPolygonF detonationCircle = AreaEffectSimulation::Polygons::circle(impactPosition, getCurrentImpactRadius());
const QVector<int> participantIdsInRange = m_ppmDtc->getParticipantsByPolygon(detonationCircle, false, false);
QVector<int> buildingIdsInRange = m_ismDtc->getBuildingByPolygon(m_session->exerciseId(), detonationCircle, false, true);
const QVector<int> undergroundBuildingIds = Calculations::getUndergroundBuildingIds(buildingIdsInRange, m_ismDtc);
// remove the underground buildings
for (int undergroundBuildingId : undergroundBuildingIds)
buildingIdsInRange.removeAll(undergroundBuildingId);
buildingIdsInRange.squeeze();
const double lethalRadius = getLethalRadius();
const double nonLethalRadius = getNonLethalRadius();
const QHash<int, QPolygonF> buildingFootprints = Calculations::getBuildingFootprints(buildingIdsInRange, m_ismDtc, impactPosition);
const QVector<int> directlyHitBuildingIds = Calculations::getDirectlyHitBuildings(buildingFootprints, impactPosition);
const QVector<QPolygonF> protectionAreas = Calculations::getProtectionAreas(buildingFootprints,
nonLethalRadius > lethalRadius ? nonLethalRadius : lethalRadius,
impactPosition);
Calculations::ParticipantProtectionInfo participantProtectionInfo = Calculations::getParticipantProtectionInfo(participantIdsInRange, protectionAreas, undergroundBuildingIds, directlyHitBuildingIds, buildingFootprints, m_ppmDtc, m_ismDtc);
if (!m_simulationDialog)
{
m_simulationDialog = new SimulationDialog(this);
m_simulationDialog->init(directlyHitBuildingIds,
participantProtectionInfo.withoutProtection,
participantProtectionInfo.withProtection + participantProtectionInfo.participantsInUnderground,
m_ismDtc,
m_ppmDtc);
addDialog(m_simulationDialog, menuGroup(), QLatin1String("Collateral Damage Simulation"), LtsWidgets::LayoutArea::eDialogTab);
connect(m_simulationDialog, &SimulationDialog::destroyed,
this, [this]() {m_simulationDialog = nullptr; });
}
else
{
m_simulationDialog->init(directlyHitBuildingIds,
participantProtectionInfo.withoutProtection,
participantProtectionInfo.withProtection + participantProtectionInfo.participantsInUnderground,
m_ismDtc,
m_ppmDtc);
focusDialog(menuGroup(), QLatin1String("Collateral Damage Simulation"), LtsWidgets::LayoutArea::eDialogTab);
}
if (!m_planCreator)
WARNING_LOG(m_logger) << "Plan Creator not present --> not possible to create a tactical plan.";
else
{
m_planCreator->clear();
m_planCreator->addProtectionAreas(protectionAreas);
m_planCreator->addBuildings(buildingFootprints, directlyHitBuildingIds);
m_planCreator->addDetonationArea(impactPosition, lethalRadius, nonLethalRadius);
m_planCreator->addParticipantsWithoutProtection(participantProtectionInfo.withoutProtection);
m_planCreator->addParticipantsWithProtection(participantProtectionInfo.withProtection);
m_planCreator->addParticipantsInUnderground(participantProtectionInfo.participantsInUnderground);
m_planCreator->save(m_ui->leName->text(), folderId, m_session->exerciseId());
}
if (!m_pdfCreator)
WARNING_LOG(m_logger) << "PDF Creator not present --> not possible to create PDF file.";
else
{
QString positionText = LtsCoordTrans::mgrsFromGeographic(impactPosition.x(), impactPosition.y());
m_pdfCreator->clear();
m_pdfCreator->addInformation(m_ui->leName->text(), m_ui->cbChargeTypes->currentText(), m_ui->cbIgniterType->currentText(), positionText);
m_pdfCreator->addParticipantsWithoutProtection(participantProtectionInfo.withoutProtection);
m_pdfCreator->addParticipantsWithProtection(participantProtectionInfo.withProtection);
m_pdfCreator->addBuildings(directlyHitBuildingIds);
m_pdfCreator->save(m_ui->leName->text(), folderId, m_session->exerciseId());
}
return true;
}
bool CreateSimulationDialog::has2DMap()
{
if (!m_sitMapGuiIfc || !m_session)
{
ERROR_LOG(m_logger) << "Not possible to draw on map without sitMapGuiIfc or session.";
return false;
}
if (m_sitMapGuiIfc->mapMode() != SituationMap::MapMode::MapMode_2D)
{
LtsWidgets::LtsMessageDialog::showMessage(this, tr("Only available in 2D mode"));
return false;
}
return true;
}
void CreateSimulationDialog::drawDetonationArea()
{
if (!has2DMap())
return;
const QPointF centerCoordinate = m_ui->positionEdit->coordinate();
if (centerCoordinate == QPointF())
{
DEBUG_LOG(m_logger) << "Not possible to draw detonation area. No position given.";
return;
}
QVector<SituationMap::ForeignGuiInterface::PreviewCircleData> previewCircleDataVec;
SituationMap::ForeignGuiInterface::PreviewCircleData circleData;
previewCircleDataVec.reserve(3);
circleData.centerGeo = centerCoordinate;
// detonation point
circleData.radius = 1.0;
circleData.fillColor = Qt::black;
circleData.lineWidth = 1;
previewCircleDataVec.append(circleData);
// lethal radius
circleData.radius = getLethalRadius();
circleData.fillPattern = SituationMap::MapFillPattern::Null;
circleData.lineWidth = 3;
previewCircleDataVec.append(circleData);
// nonlethal radius
const double nonLethalRadius = getNonLethalRadius();
if (nonLethalRadius > 0.0)
{
circleData.radius = nonLethalRadius;
circleData.linePattern = SituationMap::MapLinePattern::Dash;
previewCircleDataVec.append(circleData);
}
else
previewCircleDataVec.squeeze();
m_sitMapGuiIfc->displayPreviewObjects(this, previewCircleDataVec);
}
void CreateSimulationDialog::fillCharges()
{
m_ui->cbChargeTypes->clear();
if (!m_pdmDtc)
{
ERROR_LOG(m_logger) << "no m_pdmDtc";
return;
}
using namespace ProjectDataManagement;
const QVector<GearClass_def::type> gearClasses = { GearClass_def::AirSupport,
GearClass_def::Artillery,
GearClass_def::Mortar };
for (GearClass_def::type currentGearClass : gearClasses)
{
const QVector<ChargeType> &chargeTypes = m_pdmDtc->chargesForGearClass(currentGearClass);
for (const ChargeType &currentChargeType : chargeTypes)
{
const QJsonDocument doc(QJsonDocument::fromJson(QByteArray::fromRawData(currentChargeType.properties().c_str(), currentChargeType.properties().size())));
const QJsonObject obj(doc.object());
if (!obj.contains(QLatin1String("nasatId")))
continue;
m_ui->cbChargeTypes->addItem(QString::fromStdString(currentChargeType.name()), currentChargeType.id());
}
}
// update comboBox IgniterType:
m_pdmCbx->fillIgniterComboBox(m_ui->cbIgniterType, m_ui->cbChargeTypes->currentData().toInt());
}
double CreateSimulationDialog::getCurrentImpactRadius() const
{
const int chargeTypeId = m_ui->cbChargeTypes->currentData().toInt();
const int igniterTypeId = m_ui->cbIgniterType->currentData().toInt();
double impactRadius = m_pdmDtc->getChargeTypeIgniterTypeIgniterFunction(chargeTypeId, igniterTypeId, 0).impactRadius();
DEBUG_LOG(m_logger) << "got impact radius " << impactRadius << " for current charge type " << chargeTypeId << " and igniterTypeId " << igniterTypeId;
return impactRadius;
}
double CreateSimulationDialog::getLethalRadius() const
{
const int currentChargeTypeId = m_ui->cbChargeTypes->currentData().toInt();
const ProjectDataManagement::ChargeType &currentChargeType = m_pdmDtc->chargeType(currentChargeTypeId);
const QJsonObject properties = QJsonDocument::fromJson(QByteArray::fromStdString(currentChargeType.properties())).object();
const QString keyImpactRadius = QStringLiteral("impactRadius");
const QString keyCasEffects = QStringLiteral("casEffects");
double lethalRadius = DBL_MAX;
if (properties.contains(keyCasEffects))
{
const QJsonArray casEffects = properties.value(keyCasEffects).toArray();
for (const QJsonValue &currentCasEffect : casEffects)
{
QJsonObject currentCasEffectObj = currentCasEffect.toObject();
double tmpRadius = currentCasEffectObj.value(keyImpactRadius).toDouble();
if (tmpRadius < lethalRadius)
lethalRadius = tmpRadius;
}
}
else
lethalRadius = properties.value(keyImpactRadius).toDouble();
return lethalRadius;
}
double CreateSimulationDialog::getNonLethalRadius() const
{
const int currentChargeTypeId = m_ui->cbChargeTypes->currentData().toInt();
const ProjectDataManagement::ChargeType &currentChargeType = m_pdmDtc->chargeType(currentChargeTypeId);
const QJsonObject properties = QJsonDocument::fromJson(QByteArray::fromStdString(currentChargeType.properties())).object();
const QString keyImpactRadius = QStringLiteral("impactRadius");
const QString keyCasEffects = QStringLiteral("casEffects");
double nonLethalRadius = 0.0;
if (!properties.contains(keyCasEffects))
return 0.0;
const QJsonArray casEffects = properties.value(keyCasEffects).toArray();
if (casEffects.size() < 2)
return 0.0;
for (const QJsonValue &currentCasEffect : casEffects)
{
QJsonObject currentCasEffectObj = currentCasEffect.toObject();
double tmpRadius = currentCasEffectObj.value(keyImpactRadius).toDouble();
if (tmpRadius > nonLethalRadius)
nonLethalRadius = tmpRadius;
}
return nonLethalRadius;
}
int CreateSimulationDialog::getFolderId() const
{
int exeFolderId = m_flmDtc->getExerciseFolderId(m_session->exerciseId());
return m_flmDtc->findFolder(exeFolderId, QStringLiteral("BDA"));
}
bool CreateSimulationDialog::isNameValid(const QString &name) const
{
if (name.isEmpty())
return false;
QStringList fileNames;
const int folderId = getFolderId();
m_flmDtc->getAllFiles(FileManagement::FileType::Pdf, folderId, fileNames);
if (fileNames.contains(name))
return false;
fileNames.clear();
m_flmDtc->getAllFiles(FileManagement::FileType::TacticalPlan, folderId, fileNames);
if (fileNames.contains(name))
return false;
return true;
}
} //namespace CollateralDamageSimulation
@@ -0,0 +1,122 @@
/**
* @RDE_HEADER@
* $Id: ppm_msdl_edit.h 52239 2020-10-05 11:33:12Z thoden $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/ParticipantManagement/Gui/MSDL/ppm_msdl_edit.h $
* $Author: thoden $
* Created by Hannes Fritsche, ESZS, 2024-08-05
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <NGLLogger/INGLLogger.h>
namespace FileManagement
{
namespace Interface
{
class Dtc;
class Ftc;
}
}
namespace InfrastructureManagement
{
class FootprintElement;
namespace Interface
{
class Dtc;
}
}
namespace LtsSession
{
class LtsSession;
}
namespace ParticipantManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ProjectDataManagement
{
class ForeignGuiInterface;
namespace Interface
{
class Dtc;
}
}
namespace SituationMap
{
class ForeignGuiInterface;
}
namespace Ui
{
class CreateSimulationDialog;
}
namespace CollateralDamageSimulation
{
class PdfCreator;
class PlanCreator;
class SimulationDialog;
class CreateSimulationDialog : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
explicit CreateSimulationDialog(QWidget *parent = nullptr);
~CreateSimulationDialog();
bool init(LtsSession::LtsSession *session);
void retranslate() override final;
private slots:
void onChargeTypeChanged(int index);
void onIgniterTypeChanged(int index);
void onPositionClicked();
void onCoordinate(const QPointF &coordinate);
private: // methods
bool apply() override;
bool has2DMap();
void drawDetonationArea();
void fillCharges();
double getCurrentImpactRadius() const;
double getLethalRadius() const;
double getNonLethalRadius() const;
int getFolderId() const;
bool isNameValid(const QString &name) const;
private: // attributes
Ui::CreateSimulationDialog *m_ui;
LtsSession::LtsSession *m_session { nullptr };
SimulationDialog *m_simulationDialog { nullptr };
PlanCreator *m_planCreator { nullptr };
PdfCreator *m_pdfCreator { nullptr };
std::shared_ptr<SituationMap::ForeignGuiInterface> m_sitMapGuiIfc;
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
std::shared_ptr<FileManagement::Interface::Dtc> m_flmDtc;
std::shared_ptr<ProjectDataManagement::ForeignGuiInterface> m_pdmCbx;
};
} // namespace CollateralDamageSimulation
@@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CreateSimulationDialog</class>
<widget class="QWidget" name="CreateSimulationDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>482</width>
<height>238</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">setwindowtitle in source</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QLabel" name="lblPosition">
<property name="text">
<string>Position:</string>
</property>
</widget>
</item>
<item row="0" column="1" rowspan="2">
<widget class="QLineEdit" name="leName"/>
</item>
<item row="5" column="0" colspan="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Charge Type:</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="lblIgniterType">
<property name="text">
<string>Igniter Type:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="LtsCoordWidget::LtsCoordWidget" name="positionEdit" native="true"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="pbClear">
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/remove.svg</normaloff>:/style/remove.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pbPosition">
<property name="text">
<string notr="true"/>
</property>
<property name="icon">
<iconset>
<normaloff>:/style/position.svg</normaloff>:/style/position.svg</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lblName">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="LtsWidgets::LtsComboBox" name="cbChargeTypes"/>
</item>
<item row="4" column="1">
<widget class="LtsWidgets::LtsComboBox" name="cbIgniterType"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsCoordWidget::LtsCoordWidget</class>
<extends>QWidget</extends>
<header>LtsCoordTrans/lts_coord_widget.h</header>
</customwidget>
<customwidget>
<class>LtsWidgets::LtsComboBox</class>
<extends>QComboBox</extends>
<header>LtsWidgets/lts_combo_box.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,114 @@
/**
* @RDE_HEADER@
* Gui Plugin for Collateral Damage Simulation
* $Id: bat_gui_plugin.h 47123 2019-06-07 09:30:13Z ehrlicher $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/Battery/Gui/bat_gui_plugin.h $
* $Author: ehrlicher $
* Created by Hannes Fritsche, ESZS, 2024-08-05
*/
#include "cds_create_simulation_gui_plugin.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
#include "cds_create_simulation_dialog.h"
#include <FileManagement/flm_data_plugin_interface.h>
#include <InfrastructureManagement/Data/ism_data_plugin.h>
#include <ParticipantManagement/Data/ppm_data_plugin.h>
#include <ProjectDataManagement/Data/pdm_data_plugin.h>
#include <SituationMapPlugin/situation_map_gui_plugin_interface.h>
#include <LtsSession/lts_session.h>
namespace CollateralDamageSimulation
{
std::shared_ptr<GuiPluginBase::Interface> GuiPlugin::createGuiInterface(LtsSession::LtsSession *session)
{
return std::make_shared<InterfaceImpl>(session);
}
bool GuiPlugin::availableInPartition(IDL::PartitionType partitionType) const
{
return partitionType == IDL::PartitionType::Exercise;
}
InterfaceImpl::InterfaceImpl(LtsSession::LtsSession *session)
: GuiPluginBase::Interface(session, NGL::NGLLoggerBootstrap::getNGLRootLogger().getNGLLogger("CDS.SimulationGuiPlugin"))
{}
QVector<LtsWidgets::PluginData> InterfaceImpl::getActions()
{
QVector<LtsWidgets::PluginData> ret;
LtsWidgets::PluginData pluginData;
pluginData.menuGroup = tr("Collateral Damage Simulation");
pluginData.layoutArea = LtsWidgets::LayoutArea::eDialogTab;
pluginData.menuText = tr("Create Simulation");
pluginData.identifier = QLatin1String("CdsCreateSimulationDialog");
pluginData.menuIcon = QIcon(QLatin1String("icon:aes_impacts_sim.svg"));
pluginData.createWidgetFunc = [this](QWidget * parent) -> LtsWidgets::LtsDialog *
{
CreateSimulationDialog *createSimulationDialog = new CreateSimulationDialog(parent);
createSimulationDialog->init(m_session);
return createSimulationDialog;
};
ret.append(pluginData);
return ret;
}
bool InterfaceImpl::fetchInterfaces()
{
// map
if (!m_session->getForeignGuiInterface<SituationMap::ForeignGuiInterface>(m_sitMapGuiIfc))
return false;
// --------------------------------------------------------------------------------
// Project Data Management
if (!m_session->getDtc<ProjectDataManagement::DataInterface>(m_pdmDtc))
return false;
if (!m_pdmDtc->enableChargeType() ||
!m_pdmDtc->enableGearType())
return false;
// --------------------------------------------------------------------------------
// Participant Management
if (!m_session->getDtc<ParticipantManagement::DataInterface>(m_ppmDtc))
return false;
if (!m_ppmDtc->enableParticipant() ||
!m_ppmDtc->enableParticipantPosition())
return false;
// --------------------------------------------------------------------------------
// Infrastructure Management
if (!m_session->getDtc<InfrastructureManagement::DataInterface>(m_ismDtc))
return false;
if (!m_ismDtc->enableMainFootprint() ||
!m_ismDtc->enableMainStructureElement())
return false;
// --------------------------------------------------------------------------------
// File Management
if (!m_session->getDtc<FileManagement::DataInterface>(m_flmDtc) ||
!m_session->getFtc<FileManagement::DataInterface>(m_flmFtc))
return false;
if (!m_flmDtc->enableFolder() ||
!m_flmFtc->enableFileContentModify())
return false;
// --------------------------------------------------------------------------------
return true;
}
} // namespace CollateralDamageSimulation
@@ -0,0 +1,90 @@
/**
* @RDE_HEADER@
* Gui Plugin for Collateral Damage Simulation
* $Id: bat_gui_plugin.h 47123 2019-06-07 09:30:13Z ehrlicher $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/Battery/Gui/bat_gui_plugin.h $
* $Author: ehrlicher $
* Created by Hannes Fritsche, ESZS, 2024-08-05
*/
#pragma once
#include <PluginInterfaces/gui_plugin_interface.h>
#include <Plugins/CollateralDamageSimulation/cds_gui_plugin_interface.h>
namespace FileManagement
{
namespace Interface
{
class Dtc;
class Ftc;
}
}
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ParticipantManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ProjectDataManagement
{
namespace Interface
{
class Dtc;
}
}
namespace SituationMap
{
class ForeignGuiInterface;
}
namespace CollateralDamageSimulation
{
class GuiPlugin final : public QObject, public GuiPluginBase::Plugin
{
Q_PLUGIN_METADATA(IID "gui:3:cds.sim")
Q_INTERFACES(GuiPluginBase::Plugin)
Q_OBJECT
public:
explicit GuiPlugin() = default;
~GuiPlugin() = default;
QString pluginName() override { return QLatin1String("CdsCreateSimulationGui"); }
std::shared_ptr<GuiPluginBase::Interface> createGuiInterface(LtsSession::LtsSession *session) override;
bool availableInPartition(IDL::PartitionType partitionType) const override;
};
class InterfaceImpl final : public QObject, public GuiPluginBase::Interface
{
Q_OBJECT
public:
explicit InterfaceImpl(LtsSession::LtsSession *session);
QVector<LtsWidgets::PluginData> getActions() override;
bool fetchInterfaces() override;
bool availableInPartition(IDL::PartitionType ) const override { return true; }
private:
std::shared_ptr<FileManagement::Interface::Dtc> m_flmDtc;
std::shared_ptr<FileManagement::Interface::Ftc> m_flmFtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
std::shared_ptr<ProjectDataManagement::Interface::Dtc> m_pdmDtc;
std::shared_ptr<SituationMap::ForeignGuiInterface> m_sitMapGuiIfc;
};
} // namespace CollateralDamageSimulation
@@ -0,0 +1,93 @@
/**
* @RDE_HEADER@
* The ParticipantModel provices participant data for SimulationDialog
* $Id: ism_asset_list_dialog_model.h 58852 2023-02-01 11:28:56Z fritsche_h $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/InfrastructureManagement/Gui/ResourceManagement/AssetList/ism_asset_list_dialog_model.h $
* $Author: fritsche_h $
* Created by Hannes Fritsche, ESZS, 2024-09-19
*/
#include "cds_participant_model.h"
#include <ParticipantManagement/ppm_dtc.h>
namespace CollateralDamageSimulation
{
ParticipantModel::ParticipantModel(QObject *parent)
: LtsWidgets::LtsAbstractTableModel<ParticipantManagement::Participant>(
{
tr("Party"),
tr("Equipment Number"),
tr("Designator"),
tr("Tactical Identifier")
},
parent)
{
}
QVariant ParticipantModel::data(const QModelIndex &index, int role) const
{
if (role != Qt::DisplayRole || !indexIsValid(index))
return QVariant();
if (role == Qt::DisplayRole)
{
const ParticipantManagement::Participant &participant = m_vecData.at(index.row());
switch (index.column())
{
case PARTY:
{
const int partyId = participant.partyId();
if (!m_partyCache.contains(partyId))
m_partyCache.insert(partyId, m_ppmDtc->party(partyId));
const ParticipantManagement::Party &party = m_partyCache.value(partyId);
return QString::fromStdString(party.name());
}
case EQUIPMENT_NUMBER:
return participant.equipmentNumber().is_set() ? participant.equipmentNumber().get() : 0;
case DESIGNATOR:
return QString::fromStdString(participant.designator());
case TACTICAL_IDENTIFIER:
return QString::fromStdString(participant.tacticalIdentifier());
default:
return QVariant();
}
}
return QVariant();
}
bool ParticipantModel::initModel(const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc, const QVector<int> &participantIds)
{
m_partyCache.clear();
m_vecData.clear();
m_vecData.reserve(participantIds.size());
m_ppmDtc = ppmDtc;
if (!m_ppmDtc->isParticipantEnabled())
{
m_ppmDtc->enableParticipant();
m_ppmDtc->waitForHistoricalDataParticipant(3000);
}
if (!m_ppmDtc->isPartyEnabled())
{
m_ppmDtc->enableParty();
m_ppmDtc->waitForHistoricalDataParty(3000);
}
for (int currentParticipantId : participantIds)
m_vecData.append(m_ppmDtc->participant(currentParticipantId));
return true;
}
} // namespace CollateralDamageSimulation
@@ -0,0 +1,51 @@
/**
* @RDE_HEADER@
* The ParticipantModel provices participant data for SimulationDialog
* $Id: ism_asset_list_dialog_model.h 58852 2023-02-01 11:28:56Z fritsche_h $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/InfrastructureManagement/Gui/ResourceManagement/AssetList/ism_asset_list_dialog_model.h $
* $Author: fritsche_h $
* Created by Hannes Fritsche, ESZS, 2024-09-19
*/
#pragma once
#include <LtsWidgets/lts_abstract_table_model.h>
namespace ParticipantManagement
{
class Participant;
class Party;
namespace Interface
{
class Dtc;
}
}
namespace CollateralDamageSimulation
{
class ParticipantModel final : public LtsWidgets::LtsAbstractTableModel<ParticipantManagement::Participant>
{
Q_OBJECT
public:
explicit ParticipantModel(QObject *parent = nullptr);
enum Columns
{
PARTY = 0,
EQUIPMENT_NUMBER = 1,
DESIGNATOR = 2,
TACTICAL_IDENTIFIER = 3
};
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool initModel(const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc, const QVector<int> &participantIds);
private: // attributes
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
mutable QHash<int, ParticipantManagement::Party> m_partyCache;
}; // class ParticipantModel
} // namespace CollateralDamageSimulation
@@ -0,0 +1,235 @@
/**
The PdfCreator class creates a PDF file with the information given by a collateral damage simulation
* @RDE_HEADER@
* $Id: ppm_msdl_edit.h 52239 2020-10-05 11:33:12Z thoden $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/ParticipantManagement/Gui/MSDL/ppm_msdl_edit.h $
* $Author: thoden $
* Created by Hannes Fritsche, ESZS, 2024-08-26
*/
#include "cds_pdf_creator.h"
#include <InfrastructureManagement/ism_dtc.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <FileManagement/flm_ftc.h>
#include <QDateTime>
#include <QTemporaryFile>
#include <QDateTime>
#include <LtsSession/lts_session_data.h>
#include <LtsUtilsCore/dds_vector.h>
#include <LtsWidgets/lts_message_dialog.h>
#include <KDReports/KDReports.h>
#include <ngltools/ngltools_dtg.h>
namespace CollateralDamageSimulation
{
CollateralDamageSimulation::PdfCreator::PdfCreator(QWidget *parent)
: QObject(parent)
{}
PdfCreator::~PdfCreator()
{
if (m_report)
{
delete m_report;
m_report = nullptr;
}
}
void PdfCreator::init(const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc, const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc, const std::shared_ptr<FileManagement::Interface::Ftc> &flmFtc)
{
if (m_report != nullptr)
{
delete m_report;
m_report = nullptr;
}
m_report = new KDReports::Report();
m_ismDtc = ismDtc;
m_ppmDtc = ppmDtc;
m_flmFtc = flmFtc;
}
void PdfCreator::addInformation(const QString &name, const QString &charge, const QString &igniterType, const QString &position)
{
if (!m_report)
return;
KDReports::TextElement heading(tr("Collateral Damage Report"));
heading.setBold(true);
heading.setPointSize(24);
KDReports::TextElement nameText(tr("Name: %1").arg(name));
KDReports::TextElement chargeText(tr("Charge: %1").arg(charge));
KDReports::TextElement fuseText(tr("Igniter type: %1").arg(igniterType));
KDReports::TextElement positionText(tr("Position: %1").arg(position));
KDReports::TextElement time(tr("Time: %1").arg(DTG::toStringMSec(QDateTime::currentMSecsSinceEpoch())));
m_report->addElement(heading, Qt::AlignmentFlag::AlignCenter);
m_report->addElement(nameText);
m_report->addElement(chargeText);
m_report->addElement(fuseText);
m_report->addElement(positionText);
m_report->addElement(time);
}
void PdfCreator::addBuildings(const QVector<int> &buildingIds)
{
if (!m_report)
return;
KDReports::TextElement tableHeading(tr("Buildings"));
tableHeading.setBold(true);
tableHeading.setPointSize(16.0);
m_report->addElement(tableHeading);
KDReports::TableElement table;
table.setWidth(100, KDReports::Percent);
table.setHeaderRowCount(1);
KDReports::TextElement nameHeader(tr("Name"));
table.cell(0, 0).addElement(nameHeader);
int row = 1;
for (int currentBuildingId : buildingIds)
{
const InfrastructureManagement::MainStructureElement &currentBuilding = m_ismDtc->mainStructureElement(currentBuildingId);
KDReports::TextElement name(QString::fromStdString(currentBuilding.name()));
table.cell(row, 0).addElement(name);
++row;
}
m_report->addElement(table);
}
void PdfCreator::addParticipantsWithProtection(const QVector<int> &participantIds)
{
addParticipantTable(participantIds, tr("Participants with protection"));
}
void PdfCreator::addParticipantsWithoutProtection(const QVector<int> &participantIds)
{
addParticipantTable(participantIds, tr("Participants without protection"));
}
void PdfCreator::save(const QString &name, int folderId, int exerciseId)
{
QTemporaryFile saveFile;
saveFile.rename(QString(QStringLiteral("%1.pdf")).arg(saveFile.fileName()));
if (!saveFile.open())
return;
m_report->exportToFile(saveFile.fileName());
FileManagement::FileContentModifyRequest data;
data.name(name.toStdString());
data.isTemplate(false);
data.isHidden(false);
data.folderId(folderId);
data.fileStatus(FileManagement::FileStatus::Valid);
data.fileCategory(FileManagement::FileCategory::Exercise);
data.fileType(FileManagement::FileType::Pdf);
data.operatorId(LtsSessionData::LtsSessionData::getInstance()->getOperatorId());
data.lastModified(QDateTime::currentMSecsSinceEpoch());
data.content(LtsUtils::convert<std::vector<uint8_t>>(saveFile.readAll()));
data.exerciseId(exerciseId);
bool isModifyRequestOk = m_flmFtc->fileContentModifyRequest(data, [this](const Core::FunctionReplyObject &reply)
{
if (reply.error())
warnSafeDidNotWork(QLatin1String("Could not save plan: %1").arg(QString::fromStdString(reply.str())));
});
if (!isModifyRequestOk)
warnSafeDidNotWork(QLatin1String("Could not save plan."));
}
void PdfCreator::clear()
{
if (m_report != nullptr)
{
delete m_report;
m_report = nullptr;
}
m_report = new KDReports::Report();
}
void PdfCreator::warnSafeDidNotWork(const QString &message) const
{
new LtsWidgets::LtsMessageDialogNew(static_cast<QWidget*>(parent()), message, tr("Save failed"));
}
void PdfCreator::addParticipantTable(const QVector<int> &participantIds, const QString &tableName)
{
if (!m_report)
return;
KDReports::TextElement tableHeading(tableName);
tableHeading.setBold(true);
tableHeading.setPointSize(16.0);
m_report->addElement(tableHeading);
KDReports::TableElement table;
table.setWidth(100, KDReports::Percent);
table.setHeaderRowCount(1);
KDReports::TextElement partyHeader(tr("Party"));
KDReports::TextElement equipmentNumberHeader(tr("Equipment No."));
KDReports::TextElement designationHeader(tr("Designator"));
KDReports::TextElement tacticalIdentifierHeader(tr("Tactical Identifier"));
table.cell(0, 0).addElement(partyHeader);
table.cell(0, 1).addElement(equipmentNumberHeader);
table.cell(0, 2).addElement(designationHeader);
table.cell(0, 3).addElement(tacticalIdentifierHeader);
QHash<int, QString> partyNames;
int row = 1;
for (int currentParticipantId : participantIds)
{
int column = 0;
const ParticipantManagement::Participant &currentParticipant = m_ppmDtc->participant(currentParticipantId);
if (!partyNames.contains(currentParticipant.partyId()))
{
const ParticipantManagement::Party &party = m_ppmDtc->party(currentParticipant.partyId());
partyNames.insert(party.id(), QString::fromStdString(party.name()));
}
KDReports::TextElement party(partyNames.value(currentParticipant.partyId()));
KDReports::TextElement equipmentNumber(QString::number(currentParticipant.equipmentNumber().is_set() ? currentParticipant.equipmentNumber().get() : 0));
KDReports::TextElement designation(QString::fromStdString(currentParticipant.designator()));
KDReports::TextElement tacticalIdentifier(QString::fromStdString(currentParticipant.tacticalIdentifier()));
table.cell(row, column++).addElement(party);
table.cell(row, column++).addElement(equipmentNumber);
table.cell(row, column++).addElement(designation);
table.cell(row, column++).addElement(tacticalIdentifier);
++row;
}
m_report->addElement(table);
}
} //namespace CollateralDamageSimulation
@@ -0,0 +1,80 @@
/**
The PdfCreator class creates a PDF file with the information given by a collateral damage simulation
* @RDE_HEADER@
* $Id: ppm_msdl_edit.h 52239 2020-10-05 11:33:12Z thoden $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/ParticipantManagement/Gui/MSDL/ppm_msdl_edit.h $
* $Author: thoden $
* Created by Hannes Fritsche, ESZS, 2024-08-26
*/
#pragma once
#include <QObject>
#include <memory>
namespace KDReports
{
class Report;
}
namespace FileManagement
{
namespace Interface
{
class Ftc;
}
}
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ParticipantManagement
{
namespace Interface
{
class Dtc;
}
}
namespace CollateralDamageSimulation
{
class PdfCreator : public QObject
{
Q_OBJECT
public:
explicit PdfCreator(QWidget *parent = nullptr);
~PdfCreator();
void init(const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<FileManagement::Interface::Ftc> &flmFtc);
void addInformation(const QString &name, const QString &charge, const QString &igniterType, const QString &position);
void addBuildings(const QVector<int> &buildingIds);
void addParticipantsWithProtection(const QVector<int> &participantIds);
void addParticipantsWithoutProtection(const QVector<int> &participantIds);
void save(const QString &name, int folderId, int exerciseId);
void clear();
private: // methods
void warnSafeDidNotWork(const QString &message) const;
void addParticipantTable(const QVector<int> &participantIds, const QString &tableName);
private: // attributes
std::shared_ptr<FileManagement::Interface::Ftc> m_flmFtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
KDReports::Report *m_report { nullptr };
};
} // namespace CollateralDamageSimulation
@@ -0,0 +1,305 @@
/**
The PlanCreator class creates a tactical plan with the information given by a collateral damage simulation
* @RDE_HEADER@
* $Id: ppm_msdl_edit.h 52239 2020-10-05 11:33:12Z thoden $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/ParticipantManagement/Gui/MSDL/ppm_msdl_edit.h $
* $Author: thoden $
* Created by Hannes Fritsche, ESZS, 2024-08-20
*/
#include "cds_plan_creator.h"
#include <GeometryModel/geometry_model.h>
#include <GeometryModel/geometry_model_factory.h>
#include <LtsUtilsCore/dds_vector.h>
#include <InfrastructureManagement/ism_dtc.h>
#include <ParticipantManagement/ppm_dtc.h>
#include <FileManagement/flm_ftc.h>
#include <QPolygonF>
#include <QDateTime>
#include <QGuiApplication>
#include <LtsSession/lts_session_data.h>
#include <LtsWidgets/lts_message_dialog.h>
#include <AreaEffectSimulation/Utils/polygons.h>
#include "cds_calculations.h"
namespace CollateralDamageSimulation
{
CollateralDamageSimulation::PlanCreator::PlanCreator(QWidget *parent)
: QObject(parent)
{}
PlanCreator::~PlanCreator()
{
if (m_geometryModel != nullptr)
{
delete m_geometryModel;
m_geometryModel = nullptr;
}
}
void PlanCreator::init(const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc, const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc, const std::shared_ptr<FileManagement::Interface::Ftc> &flmFtc)
{
if (m_geometryModel != nullptr)
{
delete m_geometryModel;
m_geometryModel = nullptr;
}
m_geometryModel = SituationMap::GeometryModelFactory::singleton()->create();
clear();
m_ismDtc = ismDtc;
m_ppmDtc = ppmDtc;
m_flmFtc = flmFtc;
if (!m_ppmDtc->isPartyEnabled())
{
m_ppmDtc->enableParty();
m_ppmDtc->waitForHistoricalDataParty(3000);
}
if (!m_ppmDtc->isParticipantPositionEnabled())
{
m_ppmDtc->enableParticipantPosition();
m_ppmDtc->waitForHistoricalDataParticipantPosition(3000);
}
if (!m_ppmDtc->isParticipantEnabled())
{
m_ppmDtc->enableParticipant();
m_ppmDtc->waitForHistoricalDataParticipant(3000);
}
}
void PlanCreator::addBuildings(const QHash<int, QPolygonF> &buildingFootprints, const QVector<int> &directlyHitBuildings)
{
m_directlyHitBuildings.reserve(directlyHitBuildings.size());
for (auto it = buildingFootprints.constBegin(); it != buildingFootprints.constEnd(); ++it)
addBuilding(it.value(), directlyHitBuildings.contains(it.key()));
}
void PlanCreator::addProtectionAreas(const QVector<QPolygonF> &protectionAreas)
{
m_protectionAreas.reserve(protectionAreas.size());
for (const QPolygonF &protectionArea : protectionAreas)
addProtectionArea(protectionArea);
}
void PlanCreator::addParticipantsWithProtection(const QVector<int> &participantIds)
{
addParticipants(participantIds, ProtectionState::WithProtection);
}
void PlanCreator::addParticipantsWithoutProtection(const QVector<int> &participantIds)
{
addParticipants(participantIds, ProtectionState::WithoutProtection);
}
void PlanCreator::addParticipantsInUnderground(const QVector<int> &participantIds)
{
addParticipants(participantIds, ProtectionState::Underground);
}
void PlanCreator::save(const QString &name, int folderId, int exerciseId)
{
if (!m_protectionAreas.isEmpty())
m_geometryModel->reorderPrimitives(m_protectionAreas, SituationMap::GeometryModel::PrimitivOrder::Raise);
if (!m_directlyHitBuildings.isEmpty())
m_geometryModel->reorderPrimitives(m_directlyHitBuildings, SituationMap::GeometryModel::PrimitivOrder::Raise);
FileManagement::FileContentModifyRequest data;
data.name(name.toStdString());
data.isTemplate(false);
data.isHidden(false);
data.folderId(folderId);
data.fileStatus(FileManagement::FileStatus::Valid);
data.fileCategory(FileManagement::FileCategory::Exercise);
data.fileType(FileManagement::FileType::TacticalPlan);
data.operatorId(LtsSessionData::LtsSessionData::getInstance()->getOperatorId());
data.lastModified(QDateTime::currentMSecsSinceEpoch());
data.content(LtsUtils::convert<std::vector<uint8_t>>(m_geometryModel->toByteArray()));
data.exerciseId(exerciseId);
bool isModifyRequestOk = m_flmFtc->fileContentModifyRequest(data, [this](const Core::FunctionReplyObject &reply)
{
if (reply.error())
warnSafeDidNotWork(tr("Could not save plan: %1").arg(QString::fromStdString(reply.str())));
});
if (!isModifyRequestOk)
warnSafeDidNotWork(tr("Could not save plan."));
}
void PlanCreator::clear()
{
if (m_geometryModel == nullptr)
return;
m_protectionAreas.clear();
m_directlyHitBuildings.clear();
m_geometryModel->clearModel();
m_partyCache.clear();
}
void PlanCreator::warnSafeDidNotWork(const QString &message)
{
new LtsWidgets::LtsMessageDialogNew(static_cast<QWidget*>(parent()), message, tr("Save failed"));
}
void PlanCreator::addDetonationArea(const QPointF &impactPosition, double lethalRadius, double nonLethalRadius)
{
if (!m_geometryModel)
return;
using namespace SituationMap;
QList<QPair<GeometryModel::Attribute, QVariant>> symbolAttributes;
// impact position
symbolAttributes.reserve(4);
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::Fill, QVariant(true)));
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::Close, QVariant(true)));
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::FillColor, QVariant(QColor(Qt::black))));
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::FillPattern, QVariant(static_cast<int>(GeometryModel::FillPattern::Solid))));
m_geometryModel->addPrimitive(GeometryModel::PrimitiveType::Polyline, AreaEffectSimulation::Polygons::circle(impactPosition, 1.0).toList(), symbolAttributes);
// lethal radius
symbolAttributes.clear();
symbolAttributes.reserve(2);
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::Close, QVariant(true)));
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::LineWidth, QVariant(3)));
m_geometryModel->addPrimitive(GeometryModel::PrimitiveType::Polyline, AreaEffectSimulation::Polygons::circle(impactPosition, lethalRadius).toList(), symbolAttributes);
// nonlethal radius
if (nonLethalRadius == 0.0)
return;
symbolAttributes.clear();
symbolAttributes.reserve(3);
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::Close, QVariant(true)));
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::LineWidth, QVariant(3)));
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::LineStyle, QVariant(static_cast<int>(GeometryModel::LineStyle::Dashed))));
m_geometryModel->addPrimitive(GeometryModel::PrimitiveType::Polyline, AreaEffectSimulation::Polygons::circle(impactPosition, nonLethalRadius).toList(), symbolAttributes);
}
void PlanCreator::addBuilding(const QPolygonF &footprint, bool isDirectlyHit)
{
if (!m_geometryModel)
return;
using namespace SituationMap;
QList<QPair<GeometryModel::Attribute, QVariant>> symbolAttributes;
symbolAttributes.reserve(4);
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::Fill, QVariant(true)));
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::Close, QVariant(true)));
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::FillColor, QVariant(QColor(isDirectlyHit ? Qt::red : Qt::lightGray))));
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::FillPattern, QVariant(static_cast<int>(GeometryModel::FillPattern::Solid))));
GeometryModel::Node node = m_geometryModel->addPrimitive(GeometryModel::PrimitiveType::Polyline, footprint.toList(), symbolAttributes);
if (isDirectlyHit)
m_directlyHitBuildings.append(node);
}
void PlanCreator::addParticipants(const QVector<int> &participantIds, ProtectionState protectionState)
{
for (int participantId: participantIds)
addParticipant(participantId, protectionState);
}
void PlanCreator::addParticipant(int participantId, ProtectionState protectionState)
{
if (!m_geometryModel)
return;
using namespace SituationMap;
const ParticipantManagement::Participant &participant = m_ppmDtc->participant(participantId);
const ParticipantManagement::ParticipantPosition &position = m_ppmDtc->participantPosition(participantId);
if (!m_partyCache.contains(participant.partyId()))
m_partyCache.insert(participant.partyId(), m_ppmDtc->party(participant.partyId()));
QList<QPair<GeometryModel::Attribute, QVariant>> symbolAttributes;
QPointF positionPoint(position.pos().lon(), position.pos().lat());
const QList<QPointF> positionList = {positionPoint};
// participant
symbolAttributes.reserve(2);
QString sidc = QString::fromStdString(participant.symbol());
if (sidc.size() > 16)
sidc.replace(16, 1, QString::number(static_cast<int>(protectionState)));
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::SIDC, QVariant(sidc)));
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::ForceId, QVariant(participant.partyId())));
m_geometryModel->addPrimitive(GeometryModel::PrimitiveType::ParticipantSymbol, positionList, symbolAttributes);
}
void PlanCreator::addProtectionArea(const QPolygonF &areaPolygon)
{
if (!m_geometryModel)
return;
using namespace SituationMap;
QList<QPair<GeometryModel::Attribute, QVariant>> symbolAttributes;
symbolAttributes.reserve(4);
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::Fill, QVariant(true)));
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::Close, QVariant(true)));
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::FillColor, QVariant(QGuiApplication::palette().color(QPalette::Link))));
symbolAttributes.append(QPair<GeometryModel::Attribute, QVariant>(GeometryModel::Attribute::FillPattern, QVariant(static_cast<int>(GeometryModel::FillPattern::Diag2))));
m_protectionAreas.append(m_geometryModel->addPrimitive(GeometryModel::PrimitiveType::Polyline, areaPolygon.toList(), symbolAttributes));
}
SymbologyXML::SymbolTypes::t_affil PlanCreator::partyRoleToSymbolAffiliation(const ParticipantManagement::Role &partyRole) const
{
switch (partyRole.underlying())
{
case ParticipantManagement::Role_def::Unknown:
return SymbologyXML::SymbolTypes::UNKNOWN;
case ParticipantManagement::Role_def::Friend:
return SymbologyXML::SymbolTypes::FRIEND;
case ParticipantManagement::Role_def::Neutral:
return SymbologyXML::SymbolTypes::NEUTRAL;
case ParticipantManagement::Role_def::Enemy:
return SymbologyXML::SymbolTypes::ENEMY;
case ParticipantManagement::Role_def::Pending:
return SymbologyXML::SymbolTypes::PENDING;
case ParticipantManagement::Role_def::AssumedFriend:
return SymbologyXML::SymbolTypes::ASSUMED_FRIEND;
case ParticipantManagement::Role_def::Suspect:
return SymbologyXML::SymbolTypes::SUSPECT;
default:
return SymbologyXML::SymbolTypes::AFFIL_UNDEFINED;
}
}
} //namespace CollateralDamageSimulation
@@ -0,0 +1,92 @@
/**
The PlanCreator class creates a tactical plan with the information given by a collateral damage simulation
* @RDE_HEADER@
* $Id: ppm_msdl_edit.h 52239 2020-10-05 11:33:12Z thoden $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/ParticipantManagement/Gui/MSDL/ppm_msdl_edit.h $
* $Author: thoden $
* Created by Hannes Fritsche, ESZS, 2024-08-20
*/
#pragma once
#include <QObject>
#include <QVector>
#include <QMap>
#include <memory>
#include <SymbologyXML/symbol_types.h>
#include <ParticipantManagement/ppm_enums.hpp>
#include <ParticipantManagement/ppm_dtc.h>
#include <SituationMap/GeometryModel/geometry_model.h>
namespace FileManagement
{
namespace Interface
{
class Ftc;
}
}
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace CollateralDamageSimulation
{
enum class ProtectionState
{
WithProtection = 2,
WithoutProtection = 4,
Underground = 5,
};
class PlanCreator : public QObject
{
Q_OBJECT
public:
explicit PlanCreator(QWidget *parent = nullptr);
~PlanCreator();
void init(const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc,
const std::shared_ptr<FileManagement::Interface::Ftc> &flmFtc);
void addBuildings(const QHash<int, QPolygonF> &buildingFootprints, const QVector<int> &directlyHitBuildings);
void addProtectionAreas(const QVector<QPolygonF> &protectionAreas);
void addDetonationArea(const QPointF &impactPosition, double lethalRadius, double nonLethalRadius = 0.0);
void addParticipantsWithProtection(const QVector<int> &participantIds);
void addParticipantsWithoutProtection(const QVector<int> &participantIds);
void addParticipantsInUnderground(const QVector<int> &participantIds);
void save(const QString &name, int folderId, int exerciseId);
void clear();
private: // methods
void warnSafeDidNotWork(const QString &message);
void addBuilding(const QPolygonF &footprint, bool isDirectlyHit);
void addParticipants(const QVector<int> &participantIds, ProtectionState protectionState);
void addParticipant(int participantId, ProtectionState protectionState);
void addProtectionArea(const QPolygonF &areaPolygon);
SymbologyXML::SymbolTypes::t_affil partyRoleToSymbolAffiliation(const ParticipantManagement::Role &partyRole) const;
private: // attributes
QList<SituationMap::GeometryModel::Node> m_protectionAreas;
QList<SituationMap::GeometryModel::Node> m_directlyHitBuildings;
SituationMap::GeometryModel *m_geometryModel { nullptr };
std::shared_ptr<FileManagement::Interface::Ftc> m_flmFtc;
std::shared_ptr<InfrastructureManagement::Interface::Dtc> m_ismDtc;
std::shared_ptr<ParticipantManagement::Interface::Dtc> m_ppmDtc;
QMap<int, ParticipantManagement::Party> m_partyCache;
};
} // namespace CollateralDamageSimulation
@@ -0,0 +1,97 @@
/**
* Dialog to show affected participants and builsings by an impact
* @RDE_HEADER@
* $Id: ppm_msdl_edit.h 52239 2020-10-05 11:33:12Z thoden $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/ParticipantManagement/Gui/MSDL/ppm_msdl_edit.h $
* $Author: thoden $
* Created by Hannes Fritsche, ESZS, 2024-08-05
*/
#include "cds_simulation_dialog.h"
#include "ui_cds_simulation_dialog.h"
#include <InfrastructureManagement/ism_dtc.h>
#include <ParticipantManagement/ppm_dtc.h>
#include "cds_building_model.h"
#include "cds_participant_model.h"
namespace CollateralDamageSimulation
{
SimulationDialog::SimulationDialog(QWidget *parent)
: LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly)
, m_ui(new Ui::SimulationDialog)
{
m_ui->setupUi(this);
setOnlineHelpFilename(QStringLiteral("Situation_Kollateralschadensimulator"));
}
void SimulationDialog::init(const QVector<int> &buildingsInRange, const QVector<int> &participantsWithoutProtection, const QVector<int> &participantsWithProtection, const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc, const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc)
{
// dds stuff
if (!ismDtc->isMainStructureElementEnabled())
{
ismDtc->enableMainStructureElement();
ismDtc->waitForHistoricalDataMainStructureElement(3000);
}
if (!ppmDtc->isParticipantEnabled())
{
ppmDtc->enableParticipant();
ppmDtc->waitForHistoricalDataParticipant(3000);
}
if (!ppmDtc->isPartyEnabled())
{
ppmDtc->enableParty();
ppmDtc->waitForHistoricalDataParty(3000);
}
// participants without protection
if (m_participantsWithoutProtectionModel != nullptr)
{
delete m_participantsWithoutProtectionModel;
m_participantsWithoutProtectionModel = nullptr;
}
m_participantsWithoutProtectionModel = new ParticipantModel(this);
m_participantsWithoutProtectionModel->initModel(ppmDtc, participantsWithoutProtection);
m_ui->ldvParticipantsWithoutProtection->setModel(m_participantsWithoutProtectionModel);
// participants with protection
if (m_participantsWithProtectionModel != nullptr)
{
delete m_participantsWithProtectionModel;
m_participantsWithProtectionModel = nullptr;
}
m_participantsWithProtectionModel = new ParticipantModel(this);
m_participantsWithProtectionModel->initModel(ppmDtc, participantsWithProtection);
m_ui->ldvParticipantsWithProtection->setModel(m_participantsWithProtectionModel);
// buildings
if (m_buildingModel != nullptr)
{
delete m_buildingModel;
m_buildingModel = nullptr;
}
m_buildingModel = new BuildingModel(this);
m_buildingModel->initModel(ismDtc, buildingsInRange);
m_ui->ldvBuildings->setModel(m_buildingModel);
}
void SimulationDialog::retranslate()
{
translate(m_ui, tr("Simulation"));
}
} //namespace ParticipantManagement
@@ -0,0 +1,66 @@
/**
* Dialog to show affected participants and builsings by an impact
* @RDE_HEADER@
* $Id: ppm_msdl_edit.h 52239 2020-10-05 11:33:12Z thoden $
* $HeadURL: svn://192.168.133.21/TDS/branches/product/features/3dws_devel/Lts/Plugins/ParticipantManagement/Gui/MSDL/ppm_msdl_edit.h $
* $Author: thoden $
* Created by Hannes Fritsche, ESZS, 2024-08-05
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <NGLLogger/INGLLogger.h>
class QListWidget;
namespace Ui
{
class SimulationDialog;
}
namespace InfrastructureManagement
{
namespace Interface
{
class Dtc;
}
}
namespace ParticipantManagement
{
namespace Interface
{
class Dtc;
}
}
namespace CollateralDamageSimulation
{
class BuildingModel;
class ParticipantModel;
class SimulationDialog : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
explicit SimulationDialog(QWidget *parent = nullptr);
void init(const QVector<int> &buildingsInRange,
const QVector<int> &participantsWithoutProtection,
const QVector<int> &participantsWithProtection,
const std::shared_ptr<InfrastructureManagement::Interface::Dtc> &ismDtc,
const std::shared_ptr<ParticipantManagement::Interface::Dtc> &ppmDtc);
void retranslate() override final;
private: // attributes
ParticipantModel *m_participantsWithoutProtectionModel { nullptr };
ParticipantModel *m_participantsWithProtectionModel { nullptr };
BuildingModel *m_buildingModel { nullptr };
Ui::SimulationDialog *m_ui;
};
} // namespace CollateralDamageSimulation
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SimulationDialog</class>
<widget class="QWidget" name="SimulationDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>816</width>
<height>280</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true">setwindowtitle in source</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Affected Participants (protection):</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Affected Participants (no protection):</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Affected buildings:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="LtsDataView::LtsDataView" name="ldvParticipantsWithoutProtection" native="true"/>
</item>
<item row="1" column="2">
<widget class="LtsDataView::LtsDataView" name="ldvParticipantsWithProtection" native="true"/>
</item>
<item row="1" column="4">
<widget class="LtsDataView::LtsDataView" name="ldvBuildings" native="true"/>
</item>
<item row="0" column="1" rowspan="2">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item row="0" column="3" rowspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsDataView::LtsDataView</class>
<extends>QWidget</extends>
<header>LtsDataView/lts_data_view.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>