/** * Handler for building models. * $Id$ * $HeadURL$ * $Author$ * Created by biedziak_f, SLTE, 2021-03-23 */ #include "handler_building_model.h" #include #include #include #include #include // 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 &ddsHandler, const std::shared_ptr &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 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 &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 &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 buildingIds = m_ddsHandler->dtcIsm()->getBuildingIdsByExercise(exerciseId); // const QMap vulnerabilityModelData = m_ddsHandler->dtcIsm()->vulnerabilityModelDatas(); // QMap 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 &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 &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& 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 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 &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&, 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 &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