Files
qt5-legatus-legacy/Lts/Services/CentralEffectSimulation/Plugins/Buildings/handler_building_model.cpp
T
2026-08-13 18:33:10 +02:00

783 lines
33 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Handler for building models.
* $Id$
* $HeadURL$
* $Author$
* Created by biedziak_f, SLTE, 2021-03-23
*/
#include "handler_building_model.h"
#include <CentralEffectSimulation/logger.h>
#include <float.h>
#include <QJsonDocument>
#include <QJsonObject>
#include <QVector3D>
// operator<<
std::ostream &operator<< (std::ostream &stream, const QVector3D &value)
{
stream << "QPointF("
<< QString::number(value.x(), 'f', 6) << "#"
<< QString::number(value.y(), 'f', 6) << "#"
<< QString::number(value.z(), 'f', 6) << "#lenght="
<< QString::number(value.length(), 'f', 6)
<< ")";
return stream;
}
namespace CesBuildings
{
///////////////////////////////////////////////////////////////////////////////////////////////////
// HandlerBuildingModel
//
/*************************************************************************************************
* Ctor, dtor and init *
*************************************************************************************************/
HandlerBuildingModel::HandlerBuildingModel(
const std::shared_ptr<CentralEffectSimulation::DdsHandler> &ddsHandler, const std::shared_ptr<HandlerAesData> &aesDataHandler, QObject *parent
)
: QObject(parent)
, CentralEffectSimulation::Interface::Fts::GetLineBuildingIntersectionRequestHandler(ddsHandler->ftsCes())
, CentralEffectSimulation::Interface::Fts::GetLineBuildingIntersectsRequestHandler(ddsHandler->ftsCes())
, CentralEffectSimulation::Interface::Fts::DetonationLocationRequestHandler(ddsHandler->ftsCes())
, m_ddsHandler(ddsHandler)
, m_aesDataHandler(aesDataHandler)
, m_logger(CentralEffectSimulation::getLogger("Plugin.Buildings.HandlerBuildingModel"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
HandlerBuildingModel::~HandlerBuildingModel()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
bool HandlerBuildingModel::init()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
std::shared_ptr<InfrastructureManagement::Interface::Dtc> dtcIsm = m_ddsHandler->dtcIsm();
dtcIsm->waitForHistoricalDataMainStructureElement(1000);
connect(dtcIsm.get(), &InfrastructureManagement::Interface::Dtc::mainStructureElementChanged,
this, &HandlerBuildingModel::onMainStructureElementChanged);
// get:
dtcIsm->mainStructureElements([this](const InfrastructureManagement::MainStructureElement & data)
{
onMainStructureElementChanged(data, DataModification::eAdded);
});
INFO_LOG(m_logger) << "HandlerBuildingModel is running."
<< ", m_buildingModels.size() = " << m_buildingModels.size();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return true;
}
HandlerBuildingModelInterface::LineBuildingIntersectionResult HandlerBuildingModel::getLineBuildingIntersection(
const std::vector<Core::GeoPosition> &line, const std::string &requestIdOpt)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
CentralEffectSimulation::GetLineBuildingIntersectionRequest newRequest;
newRequest.line(line);
newRequest.requestId(requestIdOpt);
const bool justForLineOfSight = false;
const CentralEffectSimulation::GetLineBuildingIntersectionReply &reply =
getLineBuildingIntersectionInternal(newRequest, justForLineOfSight);
// convert reply to result:
// compare HandlerBuildingModel::getLineBuildingIntersection()
LineBuildingIntersectionResult result;
result.requestId = requestIdOpt;
result.errorString = reply.errorString();
result.errorParams = reply.errorParams();
result.intersection = reply.intersection();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return result;
}
HandlerBuildingModelInterface::LineBuildingIntersectsResult HandlerBuildingModel::getLineBuildingIntersects(const std::vector<Core::GeoPosition> &line, const std::string &requestIdOpt)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
CentralEffectSimulation::GetLineBuildingIntersectionRequest newRequest;
newRequest.line(line);
newRequest.requestId(requestIdOpt);
const bool justForLineOfSight = true;
const CentralEffectSimulation::GetLineBuildingIntersectionReply &reply =
getLineBuildingIntersectionInternal(newRequest, justForLineOfSight);
// convert reply to result:
// compare HandlerBuildingModel::onGetLineBuildingIntersectsRequest()
LineBuildingIntersectsResult result;
result.requestId = requestIdOpt;
result.errorString = reply.errorString();
result.errorParams = reply.errorParams();
result.intersects = ! std::isnan(reply.intersection().lat()); // ! std::isnan
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return result;
}
void HandlerBuildingModel::onMainStructureElementChanged(const InfrastructureManagement::MainStructureElement &data, DataModification modType)
{
const int mseBldgId = data.id();
TRACE_LOG(m_logger) << "MainStructureElement === " << data;
// Always delete m_buildingModels-entry
BuildingModel *buildingModelPtr = m_buildingModels.value((mseBldgId));
delete buildingModelPtr;
m_buildingModels.remove(mseBldgId);
if (modType == DataModification::eRemoved)
{
DEBUG_LOG(m_logger) << "Building not added or modified id=" << mseBldgId;
return;
}
// skip "Outdoor Cameras / Announcement Units"
if (data.structureElementTypeId() != InfrastructureManagement::StructureElementType_def::Building)
return;
// Vor allem in der GUEZ-DB sind ca 23 Gebäude per Hand eingefügt. (Sakralbau, Bahnhof, Wasserwerk etc.)
// Diese tragen die Description "Import von Hand".
// Für diese steht natürlich kein Verwundungsmodell zur Verfügung.
// Wie können wir diese erkennen und hier überspringen?
// Ich habe keine bessere Möglichkeit gefunden als diese:
const Core::GeoPosition &geoPos = data.geoPosition();
if (qFuzzyIsNull(geoPos.z()))
{
INFO_LOG(m_logger) << "BuildingId " << mseBldgId
<< " kann übersprungen werden, da von Hand importiert."
<< ", name: '" << data.name() << "'"
<< ", name: '" << data.description() << "'";
return;
}
const int exerciseId = m_ddsHandler->getExerciseId();
if (m_ddsHandler->dtcIsm()->isElementAssignedToExercise(data, exerciseId))
{
DEBUG_LOG(m_logger) << "BuildingId " << mseBldgId
<< ", in Exercise";
}
else
{
DEBUG_LOG(m_logger) << "BuildingId " << mseBldgId
<< ", not in Exercise";
return;
}
DEBUG_LOG(m_logger) << "HandlerBuildingData::onMainStructureElementChanged() id=" << mseBldgId
<< " properties=" << data.properties();
// const QVector<int> buildingIds = m_ddsHandler->dtcIsm()->getBuildingIdsByExercise(exerciseId);
// const QMap<int, InfrastructureManagement::VulnerabilityModelData> vulnerabilityModelData = m_ddsHandler->dtcIsm()->vulnerabilityModelDatas();
// QMap<int, InfrastructureManagement::VulnerabilityModelData> vulnerabilityModelData;
// InfrastructureManagement::VulnerabilityModelData vulnerabilityModelDataEle;
QByteArray vulnerabilityModelData = m_ddsHandler->dtcIsm()->getVulnerabilityModelData(data.id());
TRACE_LOG(m_logger) << "vulnerabilityModelData=" << vulnerabilityModelData
<< " id=" << data.id();
// int id = -1;
// for (auto vmdIt = vulnerabilityModelData.constBegin(); vmdIt != vulnerabilityModelData.constEnd(); ++vmdIt)
// {
// if (vmdIt.value().mainStructureElementId() == mseBldgId)
// {
// id = vmdIt.key();
// vulnerabilityModelDataEle = m_ddsHandler->dtcIsm()->vulnerabilityModelDatas().value(id);
// break;
// } // if
// } // for
// if (id == -1)
// {
// WARNING_LOG(m_logger) << "Missing VulnerabilityModelData for building " << mseBldgId << ". Skip building.";
// return;
// } // if
const InfrastructureManagement::MainFootprint &mainFootprint = m_ddsHandler->dtcIsm()->getFootprintsByExerciseId(exerciseId).value(mseBldgId);
DEBUG_LOG(m_logger) << "BuildingId " << mseBldgId;
BuildingModel *buildingModel = new BuildingModel(this);
const InfrastructureManagement::MainStructureElement &mse = data;
if (!buildingModel->init(mainFootprint, mse, vulnerabilityModelData, mseBldgId))
{
WARNING_LOG(m_logger) << "Initializing BuildingModel failed for building " << mseBldgId << ". Skipping building.";
return;
}
// if
INFO_LOG(m_logger) << "Inserting BuildingModel for building " << mseBldgId;
m_buildingModels.insert(mseBldgId, buildingModel);
INFO_LOG(m_logger) << "onMainStructureElementChanged: "
<< ", m_buildingModels.size() = " << m_buildingModels.size();
}
//--------------------------------------------------------------------------------------------------
// Intersection handling *
//--------------------------------------------------------------------------------------------------
void HandlerBuildingModel::onGetLineBuildingIntersectionRequest(const rti::core::SampleIdentity &cookie,
const CentralEffectSimulation::GetLineBuildingIntersectionRequest &request)
{
const int exerciseId = m_ddsHandler->getExerciseId();
DEBUG_LOG(m_logger) << "START onGetLineBuildingIntersectionRequest" << ", exerciseId = " << exerciseId << ", request '" << request.requestId() << "'";
const bool justForLineOfSight = false;
const CentralEffectSimulation::GetLineBuildingIntersectionReply &reply = getLineBuildingIntersectionInternal(request, justForLineOfSight);
// sendReply
m_ddsHandler->ftsCes()->getLineBuildingIntersectionReply(cookie, reply);
DEBUG_LOG(m_logger) << "END onGetLineBuildingIntersectionRequest, request '" << request.requestId() << "'";
}
void HandlerBuildingModel::onGetLineBuildingIntersectsRequest(const rti::core::SampleIdentity &cookie,
const CentralEffectSimulation::GetLineBuildingIntersectsRequest &request)
{
const int exerciseId = m_ddsHandler->getExerciseId();
DEBUG_LOG(m_logger) << "START onGetLineBuildingIntersectsRequest" << ", exerciseId = " << exerciseId << ", request '" << request.requestId() << "'";
CentralEffectSimulation::GetLineBuildingIntersectionRequest newRequest;
newRequest.line(request.line());
newRequest.requestId(request.requestId());
const bool justForLineOfSight = true;
const CentralEffectSimulation::GetLineBuildingIntersectionReply &reply = getLineBuildingIntersectionInternal(newRequest, justForLineOfSight);
// sendReply
CentralEffectSimulation::GetLineBuildingIntersectsReply newReply;
newReply.id(reply.id());
newReply.errorString(reply.errorString());
newReply.errorParams(reply.errorParams());
newReply.requestId(reply.requestId());
newReply.intersects(! std::isnan(reply.intersection().lat())); // ! std::isnan
m_ddsHandler->ftsCes()->getLineBuildingIntersectsReply(cookie, newReply);
DEBUG_LOG(m_logger) << "END onGetLineBuildingIntersectsRequest, request '" << request.requestId() << "'";
}
//LtsUtils::QPoint3D set_zValueToIfNegative(const LtsUtils::QPoint3D &pnt, double newZValue = 0.1)
//{
// return LtsUtils::QPoint3D(pnt.x(),
// pnt.y(),
// (pnt.z() > 0) ? pnt.z() : newZValue);
//}
CentralEffectSimulation::GetLineBuildingIntersectionReply HandlerBuildingModel::getLineBuildingIntersectionInternal(const CentralEffectSimulation::GetLineBuildingIntersectionRequest &request,
bool justForLineOfSight,
int *buildingIdOut,
int *segmentIdxOfIntersectionPtrOut) const
{
static Core::GeoPosition s_invalidPos(std::nan(""), std::nan(""), std::nan(""));
const int exerciseId = m_ddsHandler->getExerciseId();
DEBUG_LOG(m_logger) << "START getLineBuildingIntersectionInternal" << ", exerciseId = " << exerciseId << ", request '" << request.requestId() << "'"
<< ", request.line().size() = " << request.line().size();
CentralEffectSimulation::GetLineBuildingIntersectionReply reply;
reply.requestId(request.requestId());
// prepare invalid 'answer'
reply.intersection(s_invalidPos);
// empty (poly-)line
// (think of a trajectory)
const std::vector<Core::GeoPosition> &polylineRef = request.line();
if (polylineRef.size() < 2)
{
reply.errorString("insufficiant number of points");
WARNING_LOG(m_logger) << "getLineBuildingIntersectionInternal" << ", exerciseId = " << exerciseId << ", request '" << request.requestId() << "'"
<< ", error: '" << reply.errorString() << "'";
return reply;
}
QPolygonF polygon2D;
polygon2D.reserve(polylineRef.size());
for (const Core::GeoPosition &geoPos : qAsConst(polylineRef))
polygon2D << QPointF(geoPos.lon(), geoPos.lat());
// Only the buildings where the line/trajectory flies over the building footprint are taken into account.
bool completeEnclosed = false;
bool isGroundLevel = false;
const QVector<int> &relevantBuildings = m_ddsHandler->dtcIsm()->getBuildingByPolygon(exerciseId, polygon2D, completeEnclosed, isGroundLevel);
if (m_logger.isDebugEnabled())
{
DEBUG_LOG(m_logger) << "relevantBuildings.size=" << relevantBuildings.size();
for (const int bldgId : qAsConst(relevantBuildings))
DEBUG_LOG(m_logger) << "relevantBuilding: bldgId = " << bldgId;
}
double distance = DBL_MAX;
uint lineSegmentIdx = 0;
//---------------------
// We need to iterate over all items.
// But we need access to current and also successor item!
// If we like to avoid index operator, we can use following approach:
//
// iterate over all items, !!! BUT ONE !!!
// for (lineIt = polylineRef.begin(); lineIt != polylineRef.end()-1; )
// {
// // Copy...
// const Core::GeoPosition segStart = *lineIt;
// // ...increment and ref
// const Core::GeoPosition &segEnd = *(++lineIt);
// ...
//---------------------
// but we prefer index operator here, because in total is better:
// less copying of GeoPosition, iterator etc.
//---------------------
for (lineSegmentIdx = 0; lineSegmentIdx < polylineRef.size() - 1; ++lineSegmentIdx)
{
const Core::GeoPosition &segStart = polylineRef[lineSegmentIdx];
const Core::GeoPosition &segEnd = polylineRef[lineSegmentIdx + 1];
DEBUG_LOG(m_logger) << " lineSegmentIdx=" << lineSegmentIdx
<< ", start=" << segStart
<< " , end=" << segEnd;
int buildingId = -1; // for debugging purposes
//for (BuildingModel *buildingModel : qAsConst(m_buildingModels))
for (const int bldgId : qAsConst(relevantBuildings))
{
BuildingModel *buildingModel = m_buildingModels.value(bldgId, nullptr);
if (!buildingModel)
{
WARNING_LOG(m_logger) << " buildingModel not found, bldgId=" << bldgId;
continue;
}
DEBUG_LOG(m_logger) << " buildingModel->buildingId=" << buildingModel->buildingId()
<< " , HEIGHT = " << buildingModel-> height();
// skip buildingModel, if damaged (>80%)
auto dtcBld = m_ddsHandler->dtcBld();
dtcBld->enableBuildingState();
const Buildings::BuildingState &buildingState = dtcBld->buildingState(bldgId);
// if buildingState is not found, assume intact:
if (/*(buildingState.id() == bldgId) && */(buildingState.damagePercent() >= 80))
{
WARNING_LOG(m_logger) << " buildingModel->buildingId=" << buildingModel->buildingId()
<< ", lineSegmentIdx=" << lineSegmentIdx
<< ", damagePercent > 80% (" << buildingState.damagePercent() << ")";
continue;
}
// Do NOT
// Do NOT
// Do NOT
// Note:
// Imagine following situation:
// ______
// | |
// | Bldg |
// | |
// ----------
// * ---/ ------ o
// ---/ --------- Surface / BaseSurface / Soil
// * = Impact / Detonation
// o = Player
// In that case, no intersection will re recognized :-(
// To avoid this, lift zValue on building level by using set_zValueToIfNegative
// positions in the building's coordinate system in [m] (distance from reference position)
// const LtsUtils::QPoint3D &relStart = set_zValueToIfNegative(buildingModel->fromGeographic(segStart), 0.1); // lift / lift_zValueToIfNegative
// const LtsUtils::QPoint3D &relEnd = set_zValueToIfNegative(buildingModel->fromGeographic(segEnd), 0.1); // lift / lift_zValueToIfNegative
// Do NOT
// GUEZE-3166:
// in case of U_BAHN, segStart and segEnd really have negative values.
// It will lead to false results, if set_zValueToIfNegative is executed.
// In the meantime, lineOfSightElevation is introduced.
// Aceept the situation drawn above and assume, that lineOfSightElevation will do it.
// Do NOT
// Do NOT
const LtsUtils::QPoint3D &relStart = buildingModel->fromGeographic(segStart); // lift / lift_zValueToIfNegative
const LtsUtils::QPoint3D &relEnd = buildingModel->fromGeographic(segEnd); // lift / lift_zValueToIfNegative
DEBUG_LOG(m_logger) << " buildingModel->buildingId=" << buildingModel->buildingId()
<< ", lineSegmentIdx=" << lineSegmentIdx;
DEBUG_LOG(m_logger) << " segStart=" << segStart << " relStart=" << relStart;
DEBUG_LOG(m_logger) << " segEnd =" << segEnd << " relEnd=" << relEnd;
if (!buildingModel->hasIntersectionSimplified(relStart, relEnd)) // check height and footprint
continue;
DEBUG_LOG(m_logger) << " buildingModel->buildingId=" << buildingModel->buildingId()
<< ", lineSegmentIdx=" << lineSegmentIdx
<< ", hasIntersectionSimplified was true";
const Core::GeoPosition &intersection = buildingModel->intersection(relStart, relEnd); // assume intersection and calculate
DEBUG_LOG(m_logger) << " buildingModel->buildingId=" << buildingModel->buildingId()
<< ", lineSegmentIdx=" << lineSegmentIdx
<< ", intersection = " << intersection;
if (!BuildingModel::isValid(intersection)) // no intersection
continue;
DEBUG_LOG(m_logger) << " buildingModel->buildingId=" << buildingModel->buildingId()
<< ", lineSegmentIdx=" << lineSegmentIdx
<< ", has valid intersection = " << intersection;
if (buildingIdOut)
*buildingIdOut = buildingModel->buildingId();
// justForLineOfSight :
if (justForLineOfSight)
{
reply.intersection(intersection);
DEBUG_LOG(m_logger) << "END getLineBuildingIntersectionInternal, return due to justForLineOfSight " << request.requestId() << "'";
return reply;
}
// : justForLineOfSight
const double dist = (relStart - buildingModel->fromGeographic(intersection)).length(); // distance to start of segment
if (dist < distance) // new intersection is closer than the last one
{
if (buildingId == -1) // first intersection found
DEBUG_LOG(m_logger) << "Building " << buildingModel->buildingId() << " was hit.";
else // this building "covers" the last one found
DEBUG_LOG(m_logger) << "Building " << buildingModel->buildingId() << " was hit prior to building " << buildingId << ".";
buildingId = buildingModel->buildingId();
distance = dist;
reply.intersection(intersection);
if (segmentIdxOfIntersectionPtrOut)
*segmentIdxOfIntersectionPtrOut = lineSegmentIdx;
} // if
} // for (const int bldgId : qAsConst(relevantBuildings))
} // for(lineIt = polylineRef.begin(); lineIt != polylineRef.end()-1; ++lineIt)
DEBUG_LOG(m_logger) << "END getLineBuildingIntersectionInternal, request '" << request.requestId() << "'"
<< " buildingIdOut=" << *buildingIdOut;
return reply;
}
//--------------------------------------------------------------------------------------------------
// DetonationLocationRequest handling *
//--------------------------------------------------------------------------------------------------
void HandlerBuildingModel::onDetonationLocationRequest(
const rti::core::SampleIdentity &cookie, const CentralEffectSimulation::DetonationLocationRequest &request
)
{
DEBUG_LOG(m_logger) << "Received DetonationLocationRequest for aesId: " << request.id();
const CentralEffectSimulation::DetonationLocationReply reply = handleDetonationLocationRequest(request);
if (!reply.errorString().empty())
WARNING_LOG(m_logger) << "DetonationLocationRequest for aesId " << request.id() << " failed: " << reply.errorString();
// if
m_ddsHandler->ftsCes()->detonationLocationReply(cookie, reply);
}
CentralEffectSimulation::DetonationLocationReply HandlerBuildingModel::handleDetonationLocationRequest(const CentralEffectSimulation::DetonationLocationRequest &request) const
{
const int exerciseId = m_ddsHandler->getExerciseId();
DEBUG_LOG(m_logger) << " handleDetonationLocationRequest request=" << request
<< ", m_buildingModels.size() = " << m_buildingModels.size();
// AES::
// ExplosionCluster most likely has serveral impacts and therefore several trajectories.
// To differentiate between them, 'explosionIndex' is used (starting with 0)
// CES:
// To differentiate between a) explosionIndex and b) trajectoryIdx (segmentIdx)
// const int explosionIndex = request.index();
// prepare reply and store some values from request:
CentralEffectSimulation::DetonationLocationReply reply;
reply.id(request.id()); // AES: aesItemId
reply.index(request.index()); // AES: explosionIndex
if ( m_buildingModels.size() <= 0)
return reply;
CentralEffectSimulation::GetLineBuildingIntersectionRequest lbRequest;
lbRequest.requestId("handleDetonationLocationRequest");
lbRequest.line(request.trajectory());
const std::vector<Core::GeoPosition>& reqTrajRef = request.trajectory();
if (reqTrajRef.size() < 2)
{
ERROR_LOG(m_logger) << " handleDetonationLocationRequest request.trajectory() invalid, size =" << reqTrajRef.size();
return reply;
}
// next step: determine detonationDistance according fuseType:
double detonationDistance = 0;
ProjectDataManagement::IgniterFunctionBase ignFuncBase = m_ddsHandler->getIgniterFunctionBaseFromChargeTypeId(request.chargeTypeId(),
request.igniterTypeId(),
request.igniterFunctionId());
switch (ignFuncBase.underlying())
{
case ProjectDataManagement::IgniterFunctionBase::IfbProximity: detonationDistance = -5; break;
case ProjectDataManagement::IgniterFunctionBase::IfbImpact: detonationDistance = 0; break;
case ProjectDataManagement::IgniterFunctionBase::IfbDelay: detonationDistance = 5; break;
default:
// empty
break;
} // switch
// next step: find bldgIntersection
const bool justForLineOfSight = false;
int validSizeOfOriginalTraj = -1;
int buildingId = -1;
const CentralEffectSimulation::GetLineBuildingIntersectionReply &lbiReply =
getLineBuildingIntersectionInternal(lbRequest, justForLineOfSight, &buildingId, &validSizeOfOriginalTraj);
const LtsCoordTrans::LtsCoordinate bldgIntersectionCoord(lbiReply.intersection());
const bool bHasBuildingIntersection = BuildingModel::isValid(bldgIntersectionCoord.geoPosition());
if (bHasBuildingIntersection)
{
DEBUG_LOG(m_logger) << "RESULT: Trajectory: hasBuildingIntersection YES, at: " << bldgIntersectionCoord.mgrs()
<< " buildingId=" << buildingId;
}
else
{
DEBUG_LOG(m_logger) << "RESULT: Trajectory: hasBuildingIntersection NO";
const bool bIsInTrainingOrCentralArea = true;
if (bIsInTrainingOrCentralArea)
{
DEBUG_LOG(m_logger) << "RESULT: integrate free area...";
// free area and FuseType_def::IfbDelay:
// We can´t move the detonation point _below_ the surface.
// That would be identical to a neagative 'startHat'
// In other words: lineOfSightElevation would deliver LineOfSight::LOS_ERROR
// so:
if (detonationDistance > 0) // detonationDistance > 0 === FuseType_def::IfbDelay
detonationDistance = 0;
}
} // if (bHasBuildingIntersection)
// next step: integrate detonationDistance into trajectory to find the final detonationLocation:
// !!! assume that the requested trajectory ends directly on the ground !!!
LtsCoordTrans::LtsCoordinate detonationLocation;
if (bHasBuildingIntersection)
detonationLocation = bldgIntersectionCoord;
else
detonationLocation = LtsCoordTrans::LtsCoordinate(reqTrajRef.back());
// do we really have to do any calculations?
if (qFuzzyIsNull(detonationDistance)) // depends on 'FuseType'
{
if (!bHasBuildingIntersection)
{
DEBUG_LOG(m_logger) << "detonationLocation on ground" << detonationLocation.geoPosition()
<< std::fixed << std::setprecision(3)
<< ", heightOverElevation = " << detonationLocation.heightOverElevation();
Q_ASSERT_X(std::fabs(detonationLocation.heightOverElevation()) < 1.0, "handleDetonationLocationRequest", "heightOverElevation is to high");
}
DEBUG_LOG(m_logger) << "Trajectory bAdjusted/bHasBuildingIntersection: " << bHasBuildingIntersection
<< ", igniterFunctionBase: " << ignFuncBase
<< ", detonationDistance: " << detonationDistance
<< ", reqTrajRef.size():" << reqTrajRef.size() << " back: " << reqTrajRef.back()
<< ", detonationLocation: " << detonationLocation.toString(true)
<< ", buildingId: " << buildingId;
reply.position(detonationLocation.geoPosition());
reply.buildingId(buildingId);
}
else
{
// DetonationLocation
// instead of backtrackTrajectory(), use simply resize and push_back(bldgIntersectionCoord)
std::vector<Core::GeoPosition> trajectoryCopy = reqTrajRef;
DEBUG_LOG(m_logger) << "validSizeOfOriginalTraj is below zero: " << validSizeOfOriginalTraj << ", exerciseId " << exerciseId << ", buildingId=" << buildingId;
// in case of hasBuildingIntersection
if (validSizeOfOriginalTraj > 0)
{
trajectoryCopy.resize(validSizeOfOriginalTraj);
trajectoryCopy.push_back(bldgIntersectionCoord.geoPosition());
}
// adjust detonationLocation by taking detonationDistance (!= 0) into account:
detonationLocation = LtsCoordTrans::LtsCoordinate(calculateDetonationLocation(trajectoryCopy, detonationDistance));
DEBUG_LOG(m_logger) << "Trajectory bAdjusted/bHasBuildingIntersection: " << bHasBuildingIntersection
<< ", igniterFunctionBase: " << ignFuncBase
<< ", detonationDistance: " << detonationDistance
<< ", trajectoryCopy.size()=" << trajectoryCopy.size() << " back: " << trajectoryCopy.back()
<< ", detonationLocation: " << detonationLocation.toString(true)
<< ", buildingId: " << buildingId;
reply.buildingId(buildingId);
reply.position(detonationLocation.geoPosition());
}
// otherwise let reply.position be untouched.
return reply;
}
Core::GeoPosition HandlerBuildingModel::calculateDetonationLocation(const std::vector<Core::GeoPosition> &trajectoryGeometry, double detonationDistance) const
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " START";
const int geometrySize = trajectoryGeometry.size();
if (geometrySize < 3)
{
ERROR_LOG(m_logger) << LOGFUNCTION << ", trajectoryGeometry not sufficiant";
return {};
}
// Z-Werte - absolute
// geometrySize-3]; - see comment below
// ^^^
auto p1 = trajectoryGeometry[geometrySize - 3];
LtsCoordTrans::UtmCoordinate utm1 = LtsCoordTrans::UtmCoordinate::fromGeographic(p1.lon(), p1.lat()); // ToDo: Zone des Bldgs?
QVector3D segStart(utm1.x, utm1.y, p1.z());
auto p2 = trajectoryGeometry[geometrySize - 1];
LtsCoordTrans::UtmCoordinate utm2 = LtsCoordTrans::UtmCoordinate::fromGeographic(p2.lon(), p2.lat()); // ToDo: Zone des Bldgs?
QVector3D segEnd(utm2.x, utm2.y, p2.z());
QVector3D flightVec = segEnd - segStart;
INFO_LOG(m_logger) << "flightVec1 "
<< ", utm1 " << utm1 << " , z " << p1.z()
<< ", utm2 " << utm2 << " , z " << p2.z()
<< ", flightVec " << flightVec;
// 'auto p1 = trajectoryGeometry[geometrySize-3];'
// ^^^
// Sometime the flightVec has a very small component, so it becomes zero and scaling to 5m will fail:
// 2023-12-15 16:01:59.879 INFO CES::HandlerBuildingModel |#| Core::GeoPosition CesBuildings::HandlerBuildingModel::calculateDetonationLocation(const std::vector<Core::GeoPosition>&, double) const START (ation/Plugins/Buildings/handler_building_model.cpp:279)
// 2023-12-15 16:01:59.879 ERROR CES::HandlerBuildingModel |#|
// flightVec1 ,
// utm1 UtmCoordinate(676943.27#5818641.5 zone=32) , z 98.19423,
// utm2 UtmCoordinate(676943.29#5818641.7 zone=32) , z 98.072554,
// flightVec QPointF(0.062500#0.000000#-0.121674#lenght=0.136787) (ation/Plugins/Buildings/handler_building_model.cpp:299)
// ^^^^^^^^
// 2023-12-15 16:01:59.879 ERROR CES::HandlerBuildingModel |#| flightVec2 ,
// utm1 UtmCoordinate(676943.27#5818641.5 zone=32) , z 98.19423,
// utm2 UtmCoordinate(676943.29#5818641.7 zone=32) , z 98.072554,
// flightVec QPointF(2.284572#0.000000#-4.447553#lenght=5.000000), detonationLoc
// ^^^^^^^^
// scale:
const int distanceMeter = std::fabs(detonationDistance); // meter
flightVec *= distanceMeter / flightVec.length();
// now put on impactPositon;
QVector3D detonationLoc;
if (detonationDistance > 0) // Delay
detonationLoc = segEnd + flightVec; // 'extend'
else
detonationLoc = segEnd - flightVec; // 'reduce'
LtsCoordTrans::UtmCoordinate utmDeto(utm1.zone, detonationLoc.x(), detonationLoc.y());
const QPointF &pointDeto = utmDeto.geographic();
Core::GeoPosition geoPosDeto(pointDeto.x(), pointDeto.y(), detonationLoc.z());
DEBUG_LOG(m_logger) << "flightVec2 "
<< ", utm1 " << utm1 << " , z " << p1.z()
<< ", utm2 " << utm2 << " , z " << p2.z()
<< ", flightVec " << flightVec
<< ", detonationLoc " << detonationLoc
<< ", utmDeto " << utmDeto;
return geoPosDeto;
}
/*
Core::GeoPosition HandlerBuildingModel::backtrackTrajectory(const std::vector<Core::GeoPosition> &trajectory,
const Core::GeoPosition &position,
double distancePerSegment) const
{
Core::GeoPosition startGeo = position;
LtsCoordTrans::UtmCoordinate startUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(position.lon(), position.lat());
LtsUtils::QPoint3D start(startUtm.x, startUtm.y, position.z());
double res = distancePerSegment;
for (int i = trajectory.size() - 1; i > 0; --i)
{
const Core::GeoPosition &segmentStartGeo = trajectory[i];
const Core::GeoPosition &segmentEndGeo = trajectory[i - 1];
const LtsCoordTrans::UtmCoordinate segmentStartUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(segmentStartGeo.lon(), segmentStartGeo.lat());
const LtsCoordTrans::UtmCoordinate segmentEndUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(segmentEndGeo.lon(), segmentEndGeo.lat());
const LtsUtils::QPoint3D segmentStart(segmentStartUtm.x, segmentStartUtm.y, segmentStartGeo.z());
const LtsUtils::QPoint3D segmentEnd(segmentEndUtm.x, segmentEndUtm.y, segmentEndGeo.z());
const LtsUtils::QPoint3D direction = (segmentEnd - segmentStart).normalized();
if (LtsUtils::QPoint3D::dotProduct(start - segmentEnd, direction) > (segmentEnd - segmentStart).length()) // start is in a different trajectory segment
continue;
const double length = (start - segmentEnd).length();
if (length < res) // continue to next segment
{
res -= length;
start = segmentEnd;
continue;
} // if
start += res * direction;
startUtm.x = start.x();
startUtm.y = start.y();
const QPointF lonLat = startUtm.geographic();
startGeo.lon(lonLat.x());
startGeo.lat(lonLat.y());
startGeo.z(start.z());
return startGeo;
} // for
WARNING_LOG(m_logger) << "Backtracing trajectory for proximity fuse type failed.";
return startGeo;
}
*/
} // namespace CesBuildings