/** * @RDE_HEADER@ * SituationMap - SituationMapInit * $Id$ * $HeadURL$ * $Author$ * Created by Christian Ehrlicher, SLSE, 2016-02-29 */ #include "situation_map_init.h" #include "situation_map_init_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // OPM #include #include #include #include #include #include // Contextmenue / Rights #include #include #include #include #include #ifdef LTS_DEBUG #define USE_FUNCTIONDIALOG #endif #ifdef USE_FUNCTIONDIALOG // ndds/log/log_makeheader.h:21 ... -> create RTI bug ! #undef Public #include #endif // USE_FUNCTIONDIALOG namespace SituationMap { const char MapCacheState[] = "MapCacheState"; // CMake option at Lts/Plugins/SituationMapPlugin/Gui/SituationMap/CMakeLists.txt #ifdef USE_MAP_CACHE # pragma message("USE_MAP_CACHE is enabled") class MapCache; static MapCache *s_mapCache = nullptr; // GUEZE-791 - RPL Nr 791: Arbeitsspeicherleck /*! * \brief The MapCache class reduces the number of loading processes * We are caching here to avoid numerous construction and destruction of the map (3D/scene). * At least at linux the scene with it´s ArcGISSceneLayerCache tends to alloc a * hugh amount of data. Additionally allocation is influenced by the viewpoint * choosen by the operator. And with other allocations (dds data, BGViewer or * similar) at the same time the heap memory gets fragemented. * So we try to reduce the number of loading processes. * This should work with ONE TrainingArea. * If we have more TA`s, the number of cached instances may increase... */ class MapCache { public: static MapCache *getInstance() { if (!s_mapCache) { s_mapCache = new MapCache; // qAddPostRoutine(&MapCache::cleanup); // since many of that objects/MapImpl are using log4cxx at dtor (which in turn leads to coredumps), we pass that to the os } return s_mapCache; } struct Item { QPointer m_mapWidget; bool isItemFree() { return !m_mapWidget || m_mapWidget->property(MapCacheState).isNull(); } }; Item& getFreeItemRef(const QString &itemName) { //Qt: If the map contains no item with key key, the function inserts a default-constructed value into the map with key key, and returns a reference to it. QVector &vec = m_mapWidgetCache[itemName]; // search free item: QMutableVectorIterator it(vec); while (it.hasNext()) { Item& itemRef = it.next(); if (itemRef.isItemFree()) return itemRef; } // Not found -> create: vec.append(Item()); return vec.last(); } static void cleanup() { delete s_mapCache; } ~MapCache() { for (const auto &vec : qAsConst(m_mapWidgetCache)) { for (const auto &item : vec) { std::cerr << " m_mapWidgetCache: delete " << item.m_mapWidget << std::endl; delete item.m_mapWidget; // delete nullptr is ok } } } void dump() { std::cerr << " m_mapWidgetCache CacheSize = " << m_mapWidgetCache.size() << std::endl; for (const auto &vec : qAsConst(m_mapWidgetCache)) { std::cerr << " m_mapWidgetCache VectorSize: " << vec.size() << std::endl; for (const auto &item : vec) { QString isVisiStr(QStringLiteral("unknown")); if (item.m_mapWidget) isVisiStr = item.m_mapWidget->isVisible() ? QStringLiteral("visible") : QStringLiteral("hidden"); QString updatesEnabledStr(QStringLiteral("unknown")); if (item.m_mapWidget) updatesEnabledStr = item.m_mapWidget->updatesEnabled() ? QStringLiteral("updatesEnabled") : QStringLiteral("updates_Not_Enabled"); std::cerr << " m_mapWidgetCache ITEM =" << " m_mapWidget=" << item.m_mapWidget << ", property (MapCacheState) ='" << (item.m_mapWidget ? item.m_mapWidget->property(MapCacheState).toString().toStdString() : "-") << "'" << ", mode=" << (item.m_mapWidget ? toString(item.m_mapWidget->mode()).toStdString().c_str() : "-") << "'" << ", parent()=" << (item.m_mapWidget ? item.m_mapWidget->parent() : nullptr) << "'" << ", isVisiStr=" << isVisiStr << ", updatesEnabledStr=" << updatesEnabledStr << std::endl; } // for } // for } private: // map-name, .... QMap> m_mapWidgetCache; }; // class ArcGISSceneLayerCache #else # pragma message("USE_MAP_CACHE is disabled") #endif // USE_MAP_CACHE SituationMapInit::SituationMapInit(QObject *parent) : QObject(parent) , m_logger(SituationMap::getLogger("SituationMapInit")) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } SituationMapInit::~SituationMapInit() { DEBUG_LOG(m_logger) << LOGFUNCTION << " START"; if (m_pluginProvider == nullptr) { ERROR_LOG(m_logger) << LOGFUNCTION << " !m_pluginProvider"; m_adapterDds.clear(); return; } if (!m_pluginProvider->isSessionValid()) { INFO_LOG(m_logger) << LOGFUNCTION << " no session anymore: return"; m_adapterDds.clear(); return; } if (m_adapterDds) { m_adapterDds->stop(); m_adapterDds.clear(); } // remove some widgets (in context of toggle between 2D and 3D) // SituationMap: #ifdef USE_MAP_CACHE if (m_situationMap) { DEBUG_LOG(m_logger) << LOGFUNCTION << " MapCache: DELETE ...., isNull = " << m_situationMap->property(MapCacheState).isNull(); DEBUG_LOG(m_logger) << LOGFUNCTION << " MapCache: DELETE ...., property = '" << (m_situationMap ? m_situationMap->property(MapCacheState).toString().toStdString() : "-") << "'"; if (m_situationMap->property(MapCacheState).isNull()) { // no property -> 2D -> delete DEBUG_LOG(m_logger) << LOGFUNCTION << " MapCache: DELETE YES"; delete m_situationMap; m_situationMap.clear(); } else { // has property: interrupt parent-child-relationship DEBUG_LOG(m_logger) << LOGFUNCTION << " MapCache: DELETE NO, setParent->null"; m_situationMap->setParent(nullptr, true); // parent, beginDestroyFini m_situationMap->setProperty(MapCacheState, QVariant()); // invalid QVariant -> property will be removed -> item is free for reUsing :-) } } #else DEBUG_LOG(m_logger) << LOGFUNCTION << " DELETE situationMap: YES"; delete m_situationMap; m_situationMap.clear(); #endif // USE_MAP_CACHE TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } //----------------------------------------------------------------------------- ThatPartyIdToColorFunctor::ThatPartyIdToColorFunctor(const std::shared_ptr &ppmDtc) : BaseClass() , m_ppmDtc(ppmDtc) , m_logger(SituationMap::getLogger("ThatPartyIdToColorFunctor")) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; m_ppmDtc->waitForHistoricalDataParty(1000); connect(m_ppmDtc.get(), &ParticipantManagement::Interface::Dtc::partyChanged, this, &ThatPartyIdToColorFunctor::onPartyChanged); m_ppmDtc->partys([this](const ParticipantManagement::Party & data) { onPartyChanged(data); }); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } void ThatPartyIdToColorFunctor::onPartyChanged(const ParticipantManagement::Party &data, DataModification modType) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << ", modType= " << modType << " partyId=" << data.id() << " partyName='" << data.name() << "'" << " data.role().inner_enum=" << data.role(); const int partyId = data.id(); if (modType == DataModification::eRemoved && m_doRemovePartyFlag) // <--- pay attention (speed up replay) removeParty(partyId); else { const AdvancedSymbology::Role role = AdvancedSymbology::roleToRole<>(data.role()); const PartyData pData(ParticipantManagement::trPartyName(data.name()), role, QColor::fromRgb(QRgb(data.color())), QColor::fromRgb(QRgb(data.fadingColor()))); setParty(partyId, pData); } } //----------------------------------------------------------------------------- bool SituationMapInit::initPluginProvider(SituationMap::PluginProvider *pluginProvider) { TRACE_LOG(m_logger) << LOGFUNCTION << " START " << " session=" << pluginProvider; m_pluginProvider = pluginProvider; if (!m_pluginProvider) { ERROR_LOG(m_logger) << LOGFUNCTION << " no plugin provider!"; return false; } TRACE_LOG(m_logger) << LOGFUNCTION << " START " << " session=" << pluginProvider; return true; } //----------------------------------------------------------------------------- bool SituationMapInit::initAdapter(QPointer &movementTrackMgr, bool resourceMode) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!m_pluginProvider) { ERROR_LOG(m_logger) << LOGFUNCTION << " no plugin provider!"; return false; } // create AdapterDds m_adapterDds = QSharedPointer(new SituationMap::AdapterDds); // signal-signal connect(m_adapterDds.data(), &AdapterDds::resourceManagerModeChanged, this, &SituationMapInit::resourceManagerModeChanged); if (m_situationMap == nullptr) return false; // init AdapterDds if (!m_adapterDds->init(m_situationMap, m_pluginProvider, movementTrackMgr, resourceMode)) // re-think: resourceMode { ERROR_LOG(m_logger) << LOGFUNCTION << " m_adapterDds->init failed"; return false; } // create ThatPartyIdToColorFunctor (m_ppmDtc should be available now) QSharedPointer partyIdToColorFunctor = QSharedPointer(new ThatPartyIdToColorFunctor(m_pluginProvider->ppmDtc())); m_situationMap->setPartyIdToColorFunctor(partyIdToColorFunctor); // in context replay, skip removing of parties. // the topic is removed and added -> no real modification: // if (m_pluginProvider->isReplayPartition()) // partyIdToColorFunctor->setDoRemovePartyFlag(false); // [NLDEVCTRL-1364] Ausfallkreuz TLN-Symbol - war teilweise schwarz. // Daher nun auch in LiveLage kein eRemoved bearbeiten und Party 'behalten'. partyIdToColorFunctor->setDoRemovePartyFlag(false); connect(m_pluginProvider, &PluginProvider::partitionTimeChanged, this, [this](qint64 partitionTime) { if (m_situationMap) m_situationMap->setPartitionTime(partitionTime); }); // not neccessary, done in MapImpl::onAllPartysChanged / MapImpl::onPartyChanged : // connect(partyIdToColorFunctor.data(), &ThatPartyIdToColorFunctor::partyChanged, // m_adapterDds.data(), &SituationMap::AdapterDds::restartIfRunning); // : not neccessary, done in MapImpl::onAllPartysChanged / MapImpl::onPartyChanged connect(m_adapterDds.data(), &AdapterDds::participantPositionChanged, this, &SituationMapInit::participantPositionChanged); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } void SituationMapInit::startDisplay() { if (!m_adapterDds) return; if (!m_situationMap) return; // run AdapterDds QPointer mapNotifier = m_situationMap->createMapNotifier(); if (mapNotifier) { connect(mapNotifier.data(), &SituationMap::MapNotifier::mapReadyAndSymbolDictionaryLoaded, this, [this]() { // #define TRY_WORKAROUND_FOR_CRASH_ON_STARTUP #ifdef TRY_WORKAROUND_FOR_CRASH_ON_STARTUP QTimer::singleShot(500, std::bind(&SituationMap::AdapterDds::run, m_adapterDds)); #else if (m_adapterDds) // might already be destroyed again (e.g. when closing the app before all was loaded) m_adapterDds->run(); #endif // TRY_WORKAROUND_FOR_CRASH_ON_STARTUP }, Qt::QueuedConnection); mapNotifier->run(); } } void SituationMapInit::stopDisplay() { if (!m_adapterDds) return; m_adapterDds->stop(); } bool SituationMapInit::clearObjects() { if (!m_adapterDds) return false; m_adapterDds->clearObjects(); if (!m_situationMap) return false; const int remainingObjectsCount = m_situationMap->objectFactory().count(); if (remainingObjectsCount != 0) { // maybe there are mapObjects of overlays... WARNING_LOG(m_logger) << LOGFUNCTION << " remainingObjectsCount should be '0', is: " << remainingObjectsCount; } return true; } SituationMap::Map *SituationMapInit::getWidget() { return m_situationMap; } SituationMap::AdapterDds *SituationMapInit::adapterDds() { return m_adapterDds.data(); } bool SituationMapInit::enableMovementTrack(int participantId, bool enable) { if (!m_adapterDds) return false; auto ptr = m_adapterDds->simId2Participant(participantId); if (!ptr) return false; QPointer movementTrackManager = m_adapterDds->movementTrackManager(); if (movementTrackManager) { const int trackId = participantId; if (enable) movementTrackManager->activateTrack(trackId); // Maybe another is deactivated/removed? else movementTrackManager->deactivateTrack(trackId); } // ptr->setMovementTrackEnabled(enable); // if (!ptr->updateView()) // ERROR_LOG (m_logger) << "Couldn't update view after setting setMovementTrackEnabled(" << enable << ") for SimId: " << participantId; return true; } bool SituationMapInit::isMovementTrackEnabled(int participantId) const { if (!m_adapterDds) return false; auto ptr = m_adapterDds->simId2Participant(participantId); if (!ptr) return false; return ptr->isMovementTrackEnabled(); } bool SituationMapInit::enableGearRanges(int participantId, bool enable) { if (!m_adapterDds) return false; auto ptr = m_adapterDds->simId2Participant(participantId); if (!ptr) return false; ptr->setRangesVisibility(enable); if (!ptr->updateView()) ERROR_LOG(m_logger) << "Couldn't updateview after setting setRangesVisibility(" << enable << ") for SimId: " << participantId; return true; } bool SituationMapInit::isGearRangesEnabled(int participantId) const { if (!m_adapterDds) return false; auto ptr = m_adapterDds->simId2Participant(participantId); if (!ptr) return false; return ptr->isRangesVisible(); } QPointF SituationMapInit::centerPoint() const { return m_situationMap ? m_situationMap->centerPoint() : QPointF(); } void SituationMapInit::setCenterPoint(const QPointF ¢er, double scale, float durationSeconds) { if (m_situationMap) m_situationMap->setCenterPoint(center, scale, durationSeconds); } bool SituationMapInit::centerMapToParticipant(int participantId) { if (!m_situationMap || !m_adapterDds) return false; auto ptr = m_adapterDds->simId2Participant(participantId); if (!ptr) return false; m_situationMap->setCenterPoint(ptr->origin().toPointF()); return true; } bool SituationMapInit::centerMapToAesObject(int entityId) { if (!m_situationMap || !m_adapterDds) return false; auto ptr = m_adapterDds->simId2Aes(entityId); if (!ptr) return false; SituationMap::BoundingBox bbox; ptr->boundingBox(bbox); if (!bbox.isValid()) { WARNING_LOG(m_logger) << LOGFUNCTION << " boundingBox not valid: bbox=" << bbox; return false; } m_situationMap->setCenterPoint(bbox.center().toPointF()); return true; } bool SituationMapInit::centerMapToBuildingObject(int entityId) { if (!m_situationMap || !m_adapterDds) return false; auto ptr = m_adapterDds->simId2Building(entityId); if (!ptr) return false; SituationMap::BoundingBox bbox; ptr->boundingBox(bbox); if (!bbox.isValid()) { WARNING_LOG(m_logger) << LOGFUNCTION << " boundingBox not valid: bbox=" << bbox; return false; } m_situationMap->setCenterPoint(bbox.center().toPointF()); return true; } bool SituationMapInit::centerMapToGuezIedObject(int entityId) { if (!m_situationMap || !m_adapterDds) return false; auto ptr = m_adapterDds->simId2GuezIed(entityId); if (!ptr) return false; m_situationMap->setCenterPoint(ptr->origin().toPointF()); return true; } bool SituationMapInit::centerMapToChargeEffectObject(int entityId) { if (!m_situationMap || !m_adapterDds) return false; auto ptr = m_adapterDds->simId2ChargeEffect(entityId); if (!ptr) return false; m_situationMap->setCenterPoint(ptr->origin().toPointF()); return true; } bool SituationMapInit::centerMapToMapObject(MapObjectType mot, int entityId) { if (!m_adapterDds || !m_situationMap) return false; auto ptr = m_adapterDds->simId2MapObject(mot, entityId); if (!ptr) return false; SituationMap::BoundingBox bbox; ptr->boundingBox(bbox); if (!bbox.isValid()) { WARNING_LOG(m_logger) << LOGFUNCTION << " boundingBox not valid: bbox=" << bbox; return false; } // should work for // - PointSymbols/Particpant (ExtentType::Pixel) as well as // - MultiPointSymbols/Areas (ExtentType::Sim) m_situationMap->setCenterPoint(bbox.center().toPointF()); return true; } bool SituationMapInit::zoomMapTo(const QVector > &entities, int paddingInDIPS) { if (!m_adapterDds || !m_situationMap) return false; PositionCont posCont; for (const QPair &entity : qAsConst(entities)) { RefPointer mapObj = m_adapterDds->simId2MapObject(entity.first, entity.second); if (!mapObj) continue; RefPointer single(mapObj); // dynacast if (single) posCont.push_back(single->origin()); else { RefPointer participant(mapObj); // dynacast if (participant) posCont.push_back(participant->origin()); else { RefPointer multi(mapObj); // dynacast if (multi) posCont += multi->positions(); } } } // for if (!posCont.isEmpty()) { QVector coords; coords.reserve(posCont.size()); for (const Point3D p : qAsConst(posCont)) coords.push_back(p.toPointF()); m_situationMap->zoomToArea(coords, paddingInDIPS); } return true; } //QSharedPointer SituationMapInit::findPreviewImage(const QString &objectType, int entityId) //{ // return QSharedPointer(new AdvancedSymbology::SymbolResult()); //} bool SituationMapInit::objectIndicatorToParticipant(int participantId, bool setSelected, int lifetimeMsec) { if (!m_adapterDds || !m_situationMap) return false; auto ptr = m_adapterDds->simId2Participant(participantId); if (!ptr) return false; m_situationMap->objectIndicator().setMapObjectToPointTo(ptr->uniqueMapId(), setSelected, lifetimeMsec); return true; } bool SituationMapInit::objectIndicatorToAesObject(int entityId) { if (!m_adapterDds || !m_situationMap) return false; auto ptr = m_adapterDds->simId2Aes(entityId); if (!ptr) return false; m_situationMap->objectIndicator().setMapObjectToPointTo(ptr->uniqueMapId()); return true; } bool SituationMapInit::objectIndicatorToBuildingObject(int entityId) { if (!m_adapterDds || !m_situationMap) return false; auto ptr = m_adapterDds->simId2Building(entityId); if (!ptr) return false; m_situationMap->objectIndicator().setMapObjectToPointTo(ptr->uniqueMapId()); return true; } bool SituationMapInit::objectIndicatorToGuezIedObject(int entityId) { if (!m_adapterDds || !m_situationMap) return false; auto ptr = m_adapterDds->simId2GuezIed(entityId); if (!ptr) return false; m_situationMap->objectIndicator().setMapObjectToPointTo(ptr->uniqueMapId()); return true; } bool SituationMapInit::objectIndicatorToMapObject(MapObjectType mot, int entityId, bool setSelected, int lifetimeMsec) { if (!m_adapterDds || !m_situationMap) return false; const UniqueMapId uniqueMapId = m_adapterDds->simId2UniqueMapId(mot, entityId); if (!isUniqueMapIdValid(uniqueMapId)) return false; m_situationMap->objectIndicator().setMapObjectToPointTo(uniqueMapId, setSelected, lifetimeMsec); return true; } bool SituationMapInit::objectIndicatorToPoint(const QPointF &targetGeo, int lifetimeMsec) { if (!m_adapterDds || !m_situationMap) return false; m_situationMap->objectIndicator().setPointToPointTo(targetGeo, lifetimeMsec); return true; } bool SituationMapInit::removeObjectIndicator() { if (!m_situationMap) return false; m_situationMap->objectIndicator().remove(); return true; } bool SituationMapInit::setMapObjectSelected(MapObjectType mot, const QVector &entityIds, bool selection) { if (!m_adapterDds) return false; m_adapterDds->setMapObjectSelected(mot, entityIds, selection); return true; } bool SituationMapInit::clearMapObjectSelection(MapObjectType mot) { if (!m_adapterDds) return false; m_adapterDds->clearMapObjectSelection(mot); return true; } bool SituationMapInit::skipMapObjectSelectionDialog() const { return m_skipMapObjectSelectionDialog; } void SituationMapInit::setSkipMapObjectSelectionDialog(bool asList) { m_skipMapObjectSelectionDialog = asList; } void SituationMapInit::startMeasureLength() { if (!m_situationMap) return ; m_situationMap->measurement().measureLength(); } void SituationMapInit::clearMeasureLength() { if (!m_situationMap) return ; m_situationMap->measurement().clear(); } void SituationMapInit::startViewshed() { if (!m_situationMap) return ; m_viewshedId = -1; QObject::disconnect(m_viewshedFinishedConnection); m_viewshedFinishedConnection = connect(&m_situationMap->viewshed(), &Viewshed::viewshedFinished, this, &SituationMapInit::onViewshedFinished); m_situationMap->viewshed().create(); // no para -> start rubberband } void SituationMapInit::startViewshed(double alpha, double innerRadius, double observerHeight, double targetHeight, double beamAngle) { if (!m_situationMap) return ; m_viewshedId = -1; QObject::disconnect(m_viewshedFinishedConnection); m_viewshedFinishedConnection = connect(&m_situationMap->viewshed(), &Viewshed::viewshedFinished, this, &SituationMapInit::onViewshedFinished); m_situationMap->viewshed().create(alpha, innerRadius, observerHeight, targetHeight, beamAngle); // not enough para -> start rubberband } void SituationMapInit::onViewshedFinished(long viewshedId) { m_viewshedId = viewshedId; QObject::disconnect(m_viewshedFinishedConnection); // disconnect even if viewshedId is invalid } void SituationMapInit::modifyViewshed(double alpha, double innerRadius, double observerHeight, double targetHeight, double beamAngle) { if (m_situationMap) { if (m_viewshedId >= 0) m_situationMap->viewshed().modify(m_viewshedId, alpha, innerRadius, observerHeight, targetHeight, beamAngle); else DEBUG_LOG(m_logger) << LOGFUNCTION << " m_viewshedId invalid (" << m_viewshedId << ")"; } } void SituationMapInit::clearViewshed() { if (m_situationMap) m_situationMap->viewshed().remove(); // no para -> remove all m_viewshedId = -1; } bool SituationMapInit::showExpiredEmergencyCalls(bool showExpired) { if (!m_adapterDds) return false; return m_adapterDds->showExpiredEmergencyCalls(showExpired); } bool SituationMapInit::setOwnPositionVisible(bool enable) { return m_adapterDds ? m_adapterDds->setOwnPositionVisible(enable) : false; } bool SituationMapInit::isOwnPositionVisble() const { return m_adapterDds ? m_adapterDds->isOwnPositionVisible() : false; } bool SituationMapInit::isBuildingOwnershipSupported() const { return m_adapterDds ? m_adapterDds->isBuildingOwnershipSupported() : false; } bool SituationMapInit::isBuildingOwnershipVisible() const { return m_adapterDds ? m_adapterDds->isBuildingOwnershipVisible() : false; } bool SituationMapInit::setBuildingOwnershipVisible(bool visible) { return m_adapterDds ? m_adapterDds->setBuildingOwnershipVisible(visible) : false; } bool SituationMapInit::isBuildingContaminationSupported() const { return m_adapterDds ? m_adapterDds->isBuildingContaminationSupported() : false; } bool SituationMapInit::isBuildingContaminationVisible() const { return m_adapterDds ? m_adapterDds->isBuildingContaminationVisible() : false; } bool SituationMapInit::setBuildingContaminationVisible(bool visible) { return m_adapterDds ? m_adapterDds->setBuildingContaminationVisible(visible) : false; } bool SituationMapInit::enableFireEventFlashing(bool enable) { return m_adapterDds ? m_adapterDds->enableFireEventFlashing(enable) : false; } bool SituationMapInit::isFireEventFlashingEnabled() const { return m_adapterDds ? m_adapterDds->isFireEventFlashingEnabled() : false; } void SituationMapInit::setPlaybackTimeString(const QString &timeString) { if (m_situationMap == nullptr) return; m_situationMap->setTimeString(timeString); m_situationMap->showTimeString(true); } SituationMap::RB::RubberbandFactory &SituationMapInit::rubberbandFactory() { if (m_situationMap != nullptr) return m_situationMap->rubberbandFactory(); // TODO: fallback for very bad cases! workaround: SituationMap::Map *temp = SituationMap::Map::create(); return temp->rubberbandFactory(); } ResourceManagerMode SituationMapInit::resourceManagerMode() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!m_adapterDds) return ResourceManagerMode::Undefined; ResourceManagerMode rmm = m_adapterDds->resourceManagerMode(); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return rmm; } bool SituationMapInit::setResourceManagerMode(ResourceManagerMode rmm) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!m_adapterDds) return false; bool success = m_adapterDds->setResourceManagerMode(rmm); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return success; } RefPointer SituationMapInit::simId2Participant(int32_t simId) { if (!m_adapterDds) return RefPointer(); return m_adapterDds->simId2Participant(simId); } QVector SituationMapInit::simIds2UniqueMapIds(MapObjectType mot, const QVector &simIds) const { if (!m_adapterDds) return {}; return m_adapterDds->simIds2UniqueMapIds(mot, simIds); } static QVector convertMapDecription2BaseUrlVec(const std::vector& vecIn) { QVector vecOut; vecOut.reserve(vecIn.size()); for (const AssetManagement::MapDescription &mapDescription : vecIn) { BaseUrl baseUrl(QString::fromStdString(mapDescription.url()), mapDescription.minScale(), mapDescription.maxScale()); baseUrl.setName(QString::fromStdString(mapDescription.name())); baseUrl.setDescription(QString::fromStdString(mapDescription.description())); switch (mapDescription.mapTypeId().underlying()) { case AssetManagement::MapType_def::Aerial: baseUrl.setBasemapType(BasemapType::Aerial); break; case AssetManagement::MapType_def::Topographic: baseUrl.setBasemapType(BasemapType::Topographic); break; case AssetManagement::MapType_def::Elevation: case AssetManagement::MapType_def::Feature: case AssetManagement::MapType_def::SceneObjects: case AssetManagement::MapType_def::Unknown: break; } // switch vecOut.append(baseUrl); } // for return vecOut; } bool SituationMapInit::trainingAreaExists() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!m_pluginProvider->exmDtc() || !m_pluginProvider->asmDtc()) return false; const int exerciseId = m_pluginProvider->exerciseId(); if (exerciseId <= 0) return false; const int trainingAreaId = m_pluginProvider->exmDtc()->exercise(exerciseId).trainingAreaId(); if (trainingAreaId <= 0) return false; const AssetManagement::TrainingArea &ta = m_pluginProvider->asmDtc()->trainingArea(trainingAreaId); TRACE_LOG(m_logger) << LOGFUNCTION << " END, trainingAreaExists = " << (ta.id() == trainingAreaId); return (ta.id() == trainingAreaId); } MapSettingsJson SituationMapInit::getMapSettingsFromService() const { MapSettingsJson msj; if (!m_pluginProvider->exmDtc() || !m_pluginProvider->asmDtc()) return msj; const int exerciseId = m_pluginProvider->exerciseId(); const int trainingAreaId = m_pluginProvider->exmDtc()->exercise(exerciseId).trainingAreaId(); AssetManagement::TrainingArea ta = m_pluginProvider->asmDtc()->trainingArea(trainingAreaId); if (ta.id() != trainingAreaId) { ERROR_LOG(m_logger) << LOGFUNCTION << " TrainingArea does not exists (yet)"; return msj; } msj.setMapUrls (convertMapDecription2BaseUrlVec(ta.map2D())); msj.setSceneUrls (convertMapDecription2BaseUrlVec(ta.scene3D())); msj.setSceneObjectUrls(convertMapDecription2BaseUrlVec(ta.sceneObjects())); msj.setElevationUrls (convertMapDecription2BaseUrlVec(ta.elevation())); return msj; } //SLOTS bool SituationMapInit::initMap(bool enableSelection, SituationMap::Mode mapMode, const SituationMap::Viewpoint2D &viewPoint) { INFO_LOG(m_logger) << LOGFUNCTION << " START, enableSelection=" << enableSelection << ", mapMode=" << mapMode; #ifdef USE_MAP_CACHE bool reUsing = false; if (mapMode == Mode::Mode_3D) { // GUEZE-791 - RPL Nr 791: Arbeitsspeicherleck: MapCache::Item& mapCacheItemRef = MapCache::getInstance()->getFreeItemRef(QStringLiteral("3dMapWidget")); if (m_logger.isInfoEnabled()) MapCache::getInstance()->dump(); const bool reUse = mapCacheItemRef.m_mapWidget; if (reUse) { m_situationMap = mapCacheItemRef.m_mapWidget; m_situationMap->setProperty(MapCacheState, QVariant(QStringLiteral("3D/reUsed/taken"))); DEBUG_LOG(m_logger) << LOGFUNCTION << " MapCache: ReUse Mode_3D" << " (updatesEnabled()=" << m_situationMap->updatesEnabled() << ")"; reUsing = true; } else { DEBUG_LOG(m_logger) << LOGFUNCTION << " MapCache: CREATE Mode_3D"; m_situationMap = SituationMap::Map::create(); mapCacheItemRef.m_mapWidget = m_situationMap; mapCacheItemRef.m_mapWidget->setProperty(MapCacheState, QVariant(QStringLiteral("3D/created"))); } } // if (mapMode == Mode::Mode_3D) else { m_situationMap = SituationMap::Map::create(); // no property at 2D: m_situationMap->setProperty(MapCacheState, QVariant(QStringLiteral("2D/created"))); } if (m_logger.isInfoEnabled()) MapCache::getInstance()->dump(); #else m_situationMap = SituationMap::Map::create(); #endif // USE_MAP_CACHE if (enableSelection) { connect(m_situationMap.data(), &SituationMap::Map::mapObjectsClicked, this, &SituationMapInit::onMapObjectsClicked); connect(m_situationMap.data(), &SituationMap::Map::mapObjectsPickedByArea, this, &SituationMapInit::onMapObjectClickedByArea); connect(m_situationMap.data(), &SituationMap::Map::mapReadyAndSymbolDictionaryLoaded, this, [this]() { // just for 2D (mode is valid later) if (m_situationMap->mode() == SituationMap::Mode::Mode_2D) { connect(m_situationMap.data() , &SituationMap::Map::contextMenuClicked , this , &SituationMapInit::onContextMenuRequested); // siehe auch sit_map.cpp Zeile 421 ff } }); } // if (enableSelection) // handle Destruction of MapWidget // any partitionTimeChanged signals will cause problems: connect(m_situationMap.data(), &SituationMap::Map::beginDestroy, this, &SituationMapInit::onMapbeginDestroy); if (m_situationMap == nullptr) { FATAL_LOG(m_logger) << LOGFUNCTION << " map not available!"; return false; } #ifdef USE_MAP_CACHE if (reUsing) return true; #endif // USE_MAP_CACHE INFO_LOG(m_logger) << LOGFUNCTION << " ***** initMap:" << ", mapMode=" << mapMode << ", m_pluginProvider->toString=" << m_pluginProvider->toString() << ", exerciseId()= " << m_pluginProvider->exerciseId(); m_situationMap->setReplay(m_pluginProvider->isReplayPartition()); bool success = false; if ( (m_pluginProvider->appId() == LtsAppDescriptor::App::OC) || (m_pluginProvider->appId() == LtsAppDescriptor::App::SituationTablet) || (m_pluginProvider->exerciseId() == 0) // e.g. in case of App::ResourceManager ) { // OC -> offline -> use local map urls (iniFile): success = initMapPart2(mapMode, viewPoint); } else { // !OC -> online -> give map descriptions at asm_training_area a chance: const bool bTrainingAreaExists = this->trainingAreaExists(); INFO_LOG(m_logger) << LOGFUNCTION << " bTrainingAreaExists=" << bTrainingAreaExists << ", m_pluginProvider->toString " << m_pluginProvider->toString(); bool bIsOfTypeOrganizer = (m_pluginProvider->operatorId() == 99) || // 99 == Organisator (m_pluginProvider->operatorId() == 98); // 98 == Organizer if (bTrainingAreaExists || bIsOfTypeOrganizer) // 99 == Organisator { const MapSettingsJson &mapSettingsFromService = getMapSettingsFromService(); success = initMapPart2(mapMode, viewPoint, &mapSettingsFromService); return success; } // if trainingAreaExists does not exists: // like race condition publishing data at newly created replay environment and creating snapshotApp (this code) LtsWidgets::LtsGlobalWaitDialog::getInstance()->wait(); // really visible??? LtsWidgets::LtsGlobalWaitDialog::getInstance()->setCustomText(tr("Situation Map is starting...")); QTimer *waitForTa = new QTimer(this); waitForTa->setInterval(250); waitForTa->setProperty("tries", 0); // HACK connect(waitForTa, &QTimer::timeout, this, [this, waitForTa, mapMode, viewPoint]() { int tries = waitForTa->property("tries").toInt(); INFO_LOG(m_logger) << LOGFUNCTION << " waiting for trainingAreaExists " << trainingAreaExists() << ", tries = " << tries; if (this->trainingAreaExists()) { LtsWidgets::LtsGlobalWaitDialog::getInstance()->done(); waitForTa->stop(); const MapSettingsJson &mapSettingsFromService = getMapSettingsFromService(); if (!initMapPart2(mapMode, viewPoint, &mapSettingsFromService)) ERROR_LOG(m_logger) << LOGFUNCTION << " initMapPart2 failed"; } if (tries > 30) { LtsWidgets::LtsGlobalWaitDialog::getInstance()->done(); waitForTa->stop(); // giving up: use values of ini file: NOTICE_LOG(m_logger) << LOGFUNCTION << " waiting for trainingAreaExists " << trainingAreaExists() << ", tries = " << tries << " giving up"; if (!initMapPart2(mapMode, viewPoint)) ERROR_LOG(m_logger) << LOGFUNCTION << " initMapPart2 failed"; return; } waitForTa->setProperty("tries", ++tries); }); waitForTa->start(); return true; } TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; return success; } bool SituationMapInit::initMapPart2(SituationMap::Mode mapMode, const SituationMap::Viewpoint2D &viewPoint, const MapSettingsJson* mapSettingJson) { INFO_LOG(m_logger) << LOGFUNCTION << " mapSettingJson=" << mapSettingJson; bool success = true; const QString initFilePath = NGL::findFile(NGL::FileType::ConfigFile, QStringLiteral("situation_map_defaults.ini")); if (!QFile::exists(initFilePath)) { FATAL_LOG(m_logger) << LOGFUNCTION << " MapIniFile location failed, used path: '" << initFilePath << "'"; assert(false); return false; } else DEBUG_LOG(m_logger) << LOGFUNCTION << " ***** MapIniFile found: " << initFilePath; // ownership goes to m_situationMap SituationMap::MapSettings *mapSettings = new SituationMap::MapSettings(initFilePath, m_situationMap); ThatMapInitializer *mapInitializer = new ThatMapInitializer(*m_situationMap, *mapSettings, QLatin1String("SituationMapInterface"), m_situationMap); if (mapSettingJson) mapInitializer->setMapSettingsJson(*mapSettingJson); mapInitializer->setViewPoint(viewPoint); // viewPoint is allowed to be null :-) if (mapInitializer->hasErrors()) { FATAL_LOG(m_logger) << LOGFUNCTION << " MapInitializer-filename='" << mapInitializer->filename() << "'' has following errors: " << " exists = " << mapInitializer->exists() << " accessError = " << mapInitializer->accessError() << " formatError = " << mapInitializer->formatError(); success = false; } else success = mapInitializer->initMap(mapMode); #ifdef USE_FUNCTIONDIALOG // add FunctionDialog if needed: if (m_situationMap) SituationMap::FunctionDialog::addButtonToMap(m_situationMap); #endif // USE_FUNCTIONDIALOG TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; return success; } void SituationMapInit::myMapWidgetDialogIsAboutToBeClosed() { DEBUG_LOG(m_logger) << LOGFUNCTION << " START"; // cleanup MapObjects and ESRI-Objects: if (m_adapterDds) { m_adapterDds->stop(); m_adapterDds->clearObjects(); m_adapterDds.clear(); } // If the user performs a logout, the MainDialog/LtsDialog/MyMapWidgetDialog // (and children) is going to be destroyed. // if (m_situationMap && !m_situationMap->property(MapCacheState).isNull()) { // m_situationMap has property (means 3D), so it should stay at the cache. // But prevent destruction by interrupting parent-child-relationship // and destroy some children like functionDialog, LayerControlDialog etc. INFO_LOG(m_logger) << LOGFUNCTION << " MapCache: prevent destruction"; m_situationMap->setParent(nullptr, true); // parent, beginDestroyFini // do not remove property !! //m_situationMap->setProperty(MapCacheState, QVariant()); } } QPointer SituationMapInit::movementTrackManager() const { if (!m_adapterDds) return {}; return m_adapterDds->movementTrackManager(); } bool SituationMapInit::updateAll() { if (m_situationMap == nullptr) return false; if (!m_situationMap->isReady()) { INFO_LOG(m_logger) << LOGFUNCTION << " Map Not ready Yet: Skip "; return false; } // m_situationMap->rubberbandFactory(); return true; } void SituationMapInit::onMapbeginDestroy() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (m_adapterDds) { m_adapterDds->stop(); m_adapterDds.clear(); } TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } void SituationMapInit::onMapObjectsClicked(const SituationMap::Map::RefPointerMapObjectList &mapObjList, const QPointF &viewportPosition) { TRACE_LOG(m_logger) << LOGFUNCTION << " ViewPort X: " << viewportPosition.x() << " Y: " << viewportPosition.y(); onMapObjectClickedByArea(mapObjList); } void SituationMapInit::onMapObjectClickedByArea(const SituationMap::Map::RefPointerMapObjectList &mapObjList) { // // In Case of assignment of MountingPoints at resourceApp of ISM : // if (m_skipMapObjectSelectionDialog) { QVector uniqueMapIdVec; uniqueMapIdVec.reserve(mapObjList.size()); for (const auto &mapObject : mapObjList) uniqueMapIdVec.append(mapObject->uniqueMapId()); m_adapterDds->onMapObjectsClicked(uniqueMapIdVec); return; } // : In Case of assignment of MountingPoints at resourceApp of ISM // // typical : // // size = 0 || 1 || > 1 // 0 -> nothing, stays as before // 1 -> open InfoDialog direct // >1 -> create, update content of mapObjectSelection if (mapObjList.isEmpty()) return; if (mapObjList.size() == 1) { // // open InfoDialog for a single object immediately: // const auto &mapObj = mapObjList.front(); { if (!m_adapterDds->onMapObjectClicked(mapObj)) WARNING_LOG(m_logger) << LOGFUNCTION << " Click on a map object but found no handler for it ... " << " mapObj: debugInfo='" << mapObj->debugInfo() << "'" << " uniqueMapId=" << mapObj->uniqueMapId(); } return; } emit mapObjectsClicked(mapObjList); } void SituationMapInit::onProcessMapObjectClicked(const QVector &uniqueMapIds) { if (!m_adapterDds) return; if (!m_adapterDds->onMapObjectsClicked(uniqueMapIds)) WARNING_LOG(m_logger) << LOGFUNCTION << " onMapObjectsClicked failed ... "; } void SituationMapInit::onContextMenuRequested(const SituationMap::Map::RefPointerMapObjectList &mapObjList, const QPointF &viewportPosition) { Q_UNUSED(mapObjList); if (!m_situationMap) return; QMenu *contextMenu = new QMenu(); // WA_DeleteOnClose -> no parent required contextMenu->setAttribute(Qt::WA_DeleteOnClose); { // 1 QAction *act = contextMenu->addAction(tr("Area Selection")); connect(act, &QAction::triggered, this, [this]() { if (m_situationMap) m_situationMap->pickMapObjectsArea(); }); } { // 2 QAction *act = contextMenu->addAction(tr("Area Scale")); connect(act, &QAction::triggered, this, [this]() { if (m_situationMap) m_situationMap->scaleByArea(); }); } if (LtsSessionData::LtsSessionData::getInstance()->hasOperatorRight(OperatorRights::SituationMap::SituationMapMeasureDistance)) { { // 3 QAction *act = contextMenu->addAction(tr("Start Measure Length")); connect(act, &QAction::triggered, this, [this]() { if (m_situationMap) m_situationMap->measurement().measureLength(); }); } { // 4 QAction *act = contextMenu->addAction(tr("Clear Measure Length")); connect(act, &QAction::triggered, this, [this]() { if (m_situationMap) m_situationMap->measurement().clear(); }); } } // 5 QAction *saveAsImageAction = contextMenu->addAction(tr("Save as image")); connect(saveAsImageAction, &QAction::triggered, this, &SituationMapInit::saveAsImageRequested); // QAction *terrainProfileStart = contextMenu.addAction(tr("Start Terrain Profile")); // QAction *terrainProfileClear = contextMenu.addAction(tr("Terrain Profile")); contextMenu->popup(m_situationMap->mapToGlobal(viewportPosition.toPoint())); } } // namespace SituationMap