/** * @RDE_HEADER@ * Interface * $Id$ * $HeadURL$ * $Author$ */ // Own #include "buildinghitlineinfo_to_map.h" #include "pdm_handler.h" #include // Qt #include #include #include namespace SituationMap { BuildingHitlineInfo2SituationMap::BuildingHitlineInfo2SituationMap(PdmHandler *pdmHandler, Participant2SituationMap *participant2SituationMap, ObjectFactory &objFac, const QString &layername, int drawOrder, const char *loggerName) : BaseClass(objFac, layername, drawOrder, loggerName) , FilterClass(loggerName, this) , m_pdmHandler(pdmHandler) , m_participant2SituationMap(participant2SituationMap) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } BuildingHitlineInfo2SituationMap::~BuildingHitlineInfo2SituationMap() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } int32_t BuildingHitlineInfo2SituationMap::getSimId(const Sim2SituationMap::DDS_T &ddsData) const { return ddsData.id(); } bool BuildingHitlineInfo2SituationMap::init(PluginProvider *pluginProvider) { if (!pluginProvider || !pluginProvider->bldDtc()) return false; m_pluginProvider = pluginProvider; m_bldDtc = pluginProvider->bldDtc(); m_bldBuildingInfoIfc = pluginProvider->bldInfoIfc(); m_ismDtc = pluginProvider->ismDtc(); if (!m_bldDtc) return false; if (!m_ismDtc) return false; // FilterManagement: FilterClass::init(m_pluginProvider); // PartitionTime connect(m_pluginProvider, &PluginProvider::partitionTimeChanged, this, &BuildingHitlineInfo2SituationMap::onPartitionTimeChanged); // Get initial DDS data // building to Height m_exerciseId = m_pluginProvider->exerciseId(); m_buildingHeight.clear(); // try to fill m_buildingHeight BEFORE the buildingHitLineInfos are processed m_ismDtc->waitForHistoricalDataMainStructureElement(5000); connect(m_ismDtc.get(), &InfrastructureManagement::Interface::Dtc::mainStructureElementChanged, this, &BuildingHitlineInfo2SituationMap::onMainStructureElementChanged); m_ismDtc->mainStructureElements([this](const InfrastructureManagement::MainStructureElement & data) { onMainStructureElementChanged(data, DataModification::eAdded); }); return true; } bool BuildingHitlineInfo2SituationMap::run() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!m_bldDtc) { ERROR_LOG(m_logger) << LOGFUNCTION << " no dtc"; return false; } connect(m_bldDtc.get(), &Buildings::Interface::Dtc::buildingHitLineInfoChanged, this, &BuildingHitlineInfo2SituationMap::onBuildingHitLineInfoChanged); // get BuildingHitlines: m_bldDtc->buildingHitLineInfos([this](const Sim2SituationMap::DDS_T & ddsData) { onBuildingHitLineInfoChanged(ddsData, DataModification::eAdded); }); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } void BuildingHitlineInfo2SituationMap::stop() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (m_bldDtc) m_bldDtc->disconnect(this); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } bool BuildingHitlineInfo2SituationMap::mapObjectsClicked(const QVector &uniqueMapIds) { if (!m_bldBuildingInfoIfc) return false; const QVector &hitLineIds = uniqueMapIds2SimIds(uniqueMapIds); if (hitLineIds.isEmpty()) return true; m_bldBuildingInfoIfc->showHitLineInfoWindow(hitLineIds); // HitlineIds != buildingIds return true; } QString BuildingHitlineInfo2SituationMap::chargeTypeIdentifier(const int32_t originatorChargeTypeId) const { if (!m_pdmHandler) return QStringLiteral("ctId_%1").arg(originatorChargeTypeId); QString chargeTypeString = m_pdmHandler->trChargeTypeShortName(originatorChargeTypeId); if (chargeTypeString.isEmpty()) return QStringLiteral("ctId_%1").arg(originatorChargeTypeId); return chargeTypeString; } bool BuildingHitlineInfo2SituationMap::unifiedMapObjectData(UniqueMapId uniqueMapId, UnifiedMapObjectData &umodOut) const { if (!m_bldDtc) return false; int32_t simIdOut = -1; if (!this->uniqueMapId2SimId(uniqueMapId, simIdOut)) return false; umodOut = UnifiedMapObjectData(); RefPointer mapObj = this->getMapObjectOfUniqueMapId(uniqueMapId); if (mapObj.valid()) umodOut.m_image = mapObj->previewImage(); const auto cb = [this, simIdOut, &umodOut](const Sim2SituationMap::DDS_T & ddsData) -> bool { if (ddsData.id() == simIdOut) { umodOut.m_partyId = ddsData.originatorPartyId(); const int32_t originatorChargeTypeId = ddsData.chargeTypeId(); umodOut.m_description1 = chargeTypeIdentifier(originatorChargeTypeId); return true; // break loop } return false; }; // sonst kann sich der compiler nicht zwischen bool und void entscheiden... !? std::function cb1 = cb; return m_bldDtc->buildingHitLineInfos(cb1); } bool BuildingHitlineInfo2SituationMap::onBuildingHitLineInfoChanged(const Sim2SituationMap::DDS_T &ddsData, DataModification modType) { const bool bIsValid = !isElapsed(ddsData); if (bIsValid) updateDdsDataContainer(ddsData, modType); // helpfull for FilterManagement/onSymbolAppearanceSettingsChanged return handleChanged(ddsData, modType); } void BuildingHitlineInfo2SituationMap::onMainStructureElementChanged(const InfrastructureManagement::MainStructureElement &mse, DataModification modType) { switch (modType) { case DataModification::eAdded: case DataModification::eModified: { if (mse.modelId() != 0 && m_ismDtc->isElementAssignedToExercise(mse, m_exerciseId)) { DEBUG_LOG(m_logger) << "m_buildingHeight.insert id=" << mse.id() << " z=" << mse.geoPosition().z(); m_buildingHeight.insert(mse.id(), mse.geoPosition().z()); } break; } case DataModification::eRemoved: m_buildingHeight.remove(mse.id()); break; } } bool isValidPos(const Core::GeoPosition &pos) { if (qFuzzyIsNull(pos.lat()) && qFuzzyIsNull(pos.lon())) return false; return !std::isnan(pos.lat()) && !std::isnan(pos.lon()); } bool BuildingHitlineInfo2SituationMap::isElapsed(const Sim2SituationMap::DDS_T &data) const { // true -> hide // false -> show DEBUG_LOG(m_logger) << " isElapsed"; // 2024-09: N.Thoden // Some hitlines were starting at 0.0. // After densify the line hat ~500000 vertices. // Try to avoid them: { const Core::GeoPosition &startGeoPos = data.originatorPos(); if (!isValidPos(startGeoPos)) return true; const Core::GeoPosition &endGeoPos = data.buildingPos(); if (!isValidPos(endGeoPos)) return true; } // GUEZE-2861 says we have to create LTD this way again //######################################################### // GUEZE-2550: // No LTD is created this way... // if (m_pdmHandler->isLTDChargeType(data.chargeTypeId())) // return true; //######################################################### return false; } bool BuildingHitlineInfo2SituationMap::updateElementImpl(const Sim2SituationMap::DDS_T &ddsData, RefPointer mapObj) { const qint32 hitlineId = ddsData.id(); TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " id=" << hitlineId; // mapObj->setPickable(true); // default: pickable == true mapObj->setVisibility(SituationMap::ObjectAppearance::Tactical, false); mapObj->rLineStyle().setLineWidth(3); // will influence LineWith and ArrowSize //Line Style LineStyle ls = mapObj->lineStyle(); const SpecialChargeType sct = m_pdmHandler->isSpecialChargeType(ddsData.chargeTypeId()); switch (sct) { case SpecialChargeType::LRF: ls.setLinePattern(LinePattern::Dot); break; case SpecialChargeType::LTD: ls.setLinePattern(LinePattern::Solid); break; case SpecialChargeType::NoneSpecial: default: ls.setLinePattern(LinePattern::Dash); } // switch mapObj->setLineStyle(ls); // PartyId: mapObj->setPartyId(ddsData.originatorPartyId()); // Identifier const int32_t originatorChargeTypeId = ddsData.chargeTypeId(); mapObj->setIdentifier(chargeTypeIdentifier(originatorChargeTypeId)); // setStartAndEndPoint const Core::GeoPosition &startGeoPos = ddsData.originatorPos(); const Core::GeoPosition &endGeoPos = ddsData.buildingPos(); int bldgId = ddsData.buildingId(); // bldgId == mseId double buildingHeight = m_buildingHeight.value(bldgId, 0); // default == 0; mapObj->setStartAndEndPoint(Point3D(startGeoPos.lon(), startGeoPos.lat(), startGeoPos.z()), Point3D(endGeoPos.lon(), endGeoPos.lat(), endGeoPos.z()-buildingHeight)); // FilterManagement: handleSymbolAppearanceSettings(mapObj, ddsData); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " id=" << hitlineId; return true; } void BuildingHitlineInfo2SituationMap::onSymbolAppearanceSettingsChanged(FilterManagement::SymbolAppearanceSettings::Part part) { if ((part != FilterManagement::SymbolAppearanceSettings::Part::All) && (part != FilterManagement::SymbolAppearanceSettings::Part::Symbol)) return; // Part::All || Part::Symbol const bool relevant = symbolAppearanceSettingsChangesAreRelevant(m_symbolAppearanceSettings->settings()); m_currentSymbolSettings = m_symbolAppearanceSettings->settings(); if (!relevant) return; handleSymbolAppearanceSettings(); } bool BuildingHitlineInfo2SituationMap::symbolAppearanceSettingsChangesAreRelevant(const FilterManagement::SymbolAppearanceSettings::Settings &newSas) const { return qFuzzyCompare(m_currentSymbolSettings.symbolSettings.symbolScale, newSas.symbolSettings.symbolScale) || (m_currentSymbolSettings.symbolSettings.symbolPartyAppearance != newSas.symbolSettings.symbolPartyAppearance) || (m_currentSymbolSettings.symbolSettings.hitLineDisplayLengthMSec != newSas.symbolSettings.hitLineDisplayLengthMSec) || (m_currentSymbolSettings.symbolSettings.lrfLineDisplayLengthMSec != newSas.symbolSettings.lrfLineDisplayLengthMSec) || (m_currentSymbolSettings.symbolSettings.ltdLineDisplayLengthMSec != newSas.symbolSettings.ltdLineDisplayLengthMSec); } void BuildingHitlineInfo2SituationMap::handleSymbolAppearanceSettings() { for (const Buildings::BuildingHitLineInfo &buildingHitline : qAsConst(m_ddsDataContainer)) { const UniqueMapId uniqueMapId = m_simId2UniqueMapId.value(buildingHitline.id(), INVALID_MAP_ID); if (uniqueMapId == INVALID_MAP_ID) { WARNING_LOG(m_logger) << LOGFUNCTION << "Found a buildingHitline which is in internal map but has no map object...: " << buildingHitline.id(); continue; } RefPointer mapObj = m_objFac.get(uniqueMapId); if (!mapObj.valid()) continue; handleSymbolAppearanceSettings(mapObj, buildingHitline); if (!mapObj->updateView()) WARNING_LOG(m_logger) << LOGFUNCTION << " updateView failed," << " mapObj = " << mapObj->debugInfo(); } // for } void BuildingHitlineInfo2SituationMap::handleSymbolAppearanceSettings(RefPointer mapObj, const Sim2SituationMap::DDS_T &ddsData) { const FilterManagement::SymbolAppearanceSettings::Settings &filterSettings = m_symbolAppearanceSettings->settings(); mapObj->setScale(filterSettings.symbolSettings.symbolScale); bool bUncertain = false; const bool isVisi = isVisible(ddsData, &bUncertain); if (bUncertain) { // N. Thoden, 25.09.2020 // Do NOT store mapObj at lambda! The lambda will be destroyed AFTER // HitlineInfo2SituationMap and the ESRI map. So the RefPointer is still // holding a MapObject. The destruction will fail due to non-existing ESRI map... QTimer::singleShot(200, this, [this, ddsData]() { RefPointer mapObj = getMapObjectOfSimId(ddsData.id()); if (!mapObj.valid()) return; handleSymbolAppearanceSettings(mapObj, ddsData); }); } // add missing updateView() if (mapObj->visibility() != isVisi) { mapObj->setVisibility(isVisi); if (!mapObj->updateView()) WARNING_LOG(m_logger) << LOGFUNCTION << " updateView failed," << " mapObj = " << mapObj->debugInfo(); } } bool BuildingHitlineInfo2SituationMap::isVisible(const Sim2SituationMap::DDS_T &ddsData, bool *bUncertain) const { return visibleBySymbolPartyAppearance(ddsData) && visibleByHitLineDisplayLength(m_pluginProvider->partitionTime(), ddsData) && // partitionTime() -> msec visibleByParticipants(ddsData, bUncertain); } bool BuildingHitlineInfo2SituationMap::visibleBySymbolPartyAppearance(const Sim2SituationMap::DDS_T &ddsData) const { const FilterManagement::SymbolAppearanceSettings::Settings &filterSettings = m_symbolAppearanceSettings->settings(); const auto spa = filterSettings.symbolSettings.symbolPartyAppearance.value(ddsData.originatorPartyId()); return spa.isVisible; } bool BuildingHitlineInfo2SituationMap::visibleByParticipants(const Sim2SituationMap::DDS_T &ddsData, bool *bUncertain) const { // show hitline only if // ddsData.originatorParticipantId() && ddsData.injuryParticipantId() are visible: if (!m_participant2SituationMap) return true; bool bParticipantExists = true; bool bRet = m_participant2SituationMap->isParticipantVisible(ddsData.originatorParticipantId(), &bParticipantExists); if (!bParticipantExists && bUncertain) *bUncertain = true; return bRet; } bool BuildingHitlineInfo2SituationMap::visibleByHitLineDisplayLength(const qint64 newPartitionTime, const DDS_T &ddsData) const { qint64 displayLength = m_currentSymbolSettings.symbolSettings.hitLineDisplayLengthMSec; const SpecialChargeType sct = m_pdmHandler->isSpecialChargeType(ddsData.chargeTypeId()); switch (sct) { case SpecialChargeType::LRF: displayLength = m_currentSymbolSettings.symbolSettings.lrfLineDisplayLengthMSec; break; case SpecialChargeType::LTD: displayLength = m_currentSymbolSettings.symbolSettings.ltdLineDisplayLengthMSec; break; case SpecialChargeType::NoneSpecial: default: break; } // switch // DEBUG_LOG(m_logger) << LOGFUNCTION // << ", newPartitionTime = " << newPartitionTime // << ", stateTime = " << ddsData.sampleTime() // << ", age = " << (newPartitionTime - stateTime) // << ", sct = " << sct // << ", displayLength = " << displayLength; if (displayLength < 0) return true; return (newPartitionTime - ddsData.sampleTime()) <= displayLength; // all msec } void BuildingHitlineInfo2SituationMap::onPartitionTimeChanged() { handleSymbolAppearanceSettings(); } }