/** * @RDE_HEADER@ * Interface-Implementation of Map * $Id$ * $HeadURL$ * $Author$ */ // Own #include "map_impl.h" #include "grid_impl.h" #include "screen_to_location_helper.h" #include "location_to_elevation_helper.h" #include "layer_factory_impl.h" #include "layer_control_widget.h" #include "coord_translation_functor_impl.h" #include "object_indicator_impl.h" #include #include "jump_point_widget.h" #include "overview_map_handler_impl.h" #include "measurement_impl.h" #include "terrain_profile_impl.h" #include "viewshed_impl.h" #include "location_display_impl.h" #include "pixel_based_textbox_handler_impl.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "overlay_handler_impl.h" #include #include // NGL #include // Lts #include #include // ESRI #include #include #include #include #include #include #include // enum NavigationConstraint //#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // This file is created by RME, not ESRI #if ACRGIS_VERSION >= 0x640500 // 100.5.0 # define HAS_BELOW_GROUND #endif #if ACRGIS_VERSION >= 0x640f00 // 100.15.0 # define HAS_NETWORK_CACHE_CONFIGURATION #endif #ifdef HAS_NETWORK_CACHE_CONFIGURATION #include #include #endif // HAS_NETWORK_CACHE_CONFIGURATION #include #include #include #include #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USE_OWN_EVENTHANDLER static_assert((int)SituationMap::DrawStatus::InProgress == (int)Esri::ArcGISRuntime::DrawStatus::InProgress, "DrawStatus mismatch"); static_assert((int)SituationMap::DrawStatus::Completed == (int)Esri::ArcGISRuntime::DrawStatus::Completed, "DrawStatus mismatch"); #ifdef HAS_BELOW_GROUND static_assert((int)SituationMap::NavigationConstraint::None == (int)Esri::ArcGISRuntime::NavigationConstraint::None, "NavigationConstraint mismatch"); static_assert((int)SituationMap::NavigationConstraint::StayAbove == (int)Esri::ArcGISRuntime::NavigationConstraint::StayAbove, "NavigationConstraint mismatch"); #endif // HAS_BELOW_GROUND namespace SituationMap { class MyGraphicsPixmapItem final : public QObject, public QGraphicsPixmapItem { Q_OBJECT public: explicit MyGraphicsPixmapItem(QGraphicsItem *parent = nullptr) : QObject(), QGraphicsPixmapItem(parent) {} signals: void doubleClicked(); protected: void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) override { emit doubleClicked(); } }; // class MyGraphicsPixmapItem //----------------------------------------------------------------------------- class MyPushButton : public QPushButton { Q_OBJECT public: MyPushButton() : QPushButton() {} void setIconPale(const QIcon &icon) { m_iconPale = icon; setIcon(m_iconPale); } void setIconOnHover(const QIcon &icon) { m_iconOnHover = icon; } void enterEvent(QEvent *e) override { setIcon(m_iconOnHover); QPushButton::enterEvent(e); } void leaveEvent(QEvent *e) override { setIcon(m_iconPale); QPushButton::leaveEvent(e); } protected: QIcon m_iconPale; QIcon m_iconOnHover; }; // class MyPushButton //----------------------------------------------------------------------------- class MapImpl::Private { public: Private() = default; static int s_counter; // ESRI data: Esri::ArcGISRuntime::Map *m_esriMap = nullptr; Esri::ArcGISRuntime::Scene *m_esriScene = nullptr; Esri::ArcGISRuntime::Basemap *m_basemap = nullptr; std::shared_ptr m_elevationMap; std::shared_ptr m_elevationMapManager; Esri::ArcGISRuntime::MapGraphicsView *m_mapGraphicsView = nullptr; Esri::ArcGISRuntime::SceneGraphicsView *m_sceneGraphicsView = nullptr; ScreenToLocationHelper *m_screenToLocationHelper = nullptr; bool m_destroying = false; bool m_mapReadyFlag = false; bool m_mapReadyAndSymbolDictionaryEmitted = false; Viewpoint2D m_initialViewpoint; double m_animationDuration = 0.5; double m_rocketDuration = 2.0; QTimer *m_rocketTimer = nullptr; bool m_rocketRunning = false; QPointF m_currentCenterPointBeforeRocket; double m_currentScaleBeforeRocket = 0.0; Camera m_currentCameraBeforRocket; // PositionLabel QLabel *m_positionLabel = nullptr; QString m_positionStr; QString m_positionElevationStr = QLatin1String("0"); QColor m_positionLabelForegroundColor; QColor m_positionLabelBackgroundColor; QPointer m_locationToElevationHelper; void updatePositionText() { if (!m_positionLabel) return; m_positionLabel->setText(QStringLiteral("%1 %2"). arg(m_positionStr). arg(m_positionElevationStr, 3)); } // ExerciseTimeLabel QLabel *m_exerciseTimeLabel = nullptr; QColor m_exerciseTimeLabelForegroundColor; QColor m_exerciseTimeLabelBackgroundColor; bool m_isReplay = false; // TimeLabel QLabel *m_timeLabel = nullptr; QColor m_timeLabelForegroundColor; QColor m_timeLabelBackgroundColor; // Compass MyGraphicsPixmapItem *m_compass = nullptr; // scene will take ownership. // OWN data: QVector m_trainingsAreaExtent; // Layout, SideBar etc QStackedLayout *m_stackedLayout = nullptr; QPointer m_sideBar = nullptr; // optional QPushButton *m_pbSideBar = nullptr; // optional JumpPointWidget *m_jumpPointWidget = nullptr; // optional // QTextEdit / TextBoxPixel struct { QPointer m_label; QPointer m_textEdit; QGraphicsRectItem *m_handle_or_proxyControl{nullptr}; QGraphicsProxyWidget *m_proxy{nullptr}; QPointer m_sizeGrip; void reset() { // ToDo: check // delete without removing from scene/view? // qCritical() << " 11"; delete m_handle_or_proxyControl; m_handle_or_proxyControl = nullptr; // since m_proxy has m_handle_or_proxyControl as parent... m_proxy = nullptr; // qCritical() << " 33"; delete m_textEdit; // QPointer delete m_sizeGrip; // QPointer // qCritical() << " fini"; // qCritical() << " m_handle_or_proxyControl= " << m_handle_or_proxyControl // << " m_proxy= " << m_proxy // << " m_textEdit= " << m_textEdit // << " m_sizeGrip= " << m_sizeGrip; } } m_textBoxPixelAndCo; LayerFactoryImpl *m_layerFactoryImpl = nullptr; ObjectFactory *m_objectFactory = nullptr; QSharedPointer m_objectContainer; CoordTranslationFunctor *m_coordTranslationFunctorInitialize = nullptr; CoordTranslationFunctor *m_coordTranslationFunctor = nullptr; QSharedPointer m_partyIdToColorFunctor; QTimer *m_partychangedDelayTimer = nullptr; QSharedPointer m_overlayHandler; SituationMap::GridImpl *m_gridImpl = nullptr; bool m_suppressFirstViewpointChange = true; double m_pixelsize = -1.0; // meterPerPixel double m_absoluteScale = -1.0; double m_rotationAbsoluteAngleDegrees = 0; QTimer *m_maxExtentTimer = nullptr; RB::RubberbandFactory *m_rubberbandFactory = nullptr; OverviewMapHandlerImpl *m_overviewMapHandler = nullptr; ObjectIndicatorImpl *m_objectIndicatorImpl = nullptr; LocationDisplayImpl *m_locationDisplayImpl = nullptr; QPointer m_pixelBasedTextBoxHandlerImpl; MeasurementImpl *m_measurementImpl = nullptr; TerrainProfileImpl *m_terrainProfileImpl = nullptr; ViewshedImpl *m_viewshedImpl = nullptr; AdvancedSymbology::SymbologyType m_symbologyType = AdvancedSymbology::SymbologyType::None; double m_symbolScale = 1.0; // // 'Optional' / Internal : // replaced by EventMoseNavigator RB::EventMouseNavigator *m_mouseEventHandler = nullptr; QList> m_dropEventManagerList; // : 'Optional' / Internal }; // class MapImpl::Private //----------------------------------------------------------------------------- int MapImpl::Private::s_counter = 0; MapImpl::MapImpl(QWidget *parent) : BaseClass(parent) , m_logger(getLogger("MapImpl")) , d(new Private()) { INFO_LOG(m_logger) << LOGFUNCTION << " START, s_counter=" << ++d->s_counter; // connect errorHandling: connect(Esri::ArcGISRuntime::GeometryEngine::instance(), &Esri::ArcGISRuntime::GeometryEngine::errorOccurred, this, &MapImpl::onErrorGeometryEngine); d->m_basemap = new Esri::ArcGISRuntime::Basemap(this); d->m_basemap->setObjectName(QLatin1String("Basemap-Basemap")); //---------------------------------------------------------------------------- // connect errorHandling: //---------------------------------------------------------------------------- connect(d->m_basemap, &Esri::ArcGISRuntime::Map::errorOccurred, this, &MapImpl::onErrorBasemap); d->m_maxExtentTimer = new QTimer(this); d->m_maxExtentTimer->setSingleShot(true); d->m_maxExtentTimer->setInterval(200); connect(d->m_maxExtentTimer, &QTimer::timeout, this, [this]() { DEBUG_LOG(m_logger) << LOGFUNCTION << " ************** extentChangedDELAYED="; if (d->m_destroying) return; emit extentChangedDelayed(); // will be sent if zooming/scaling/moving/resizing has finished for a while }); //---------------------------------------------------------------------------- // add view to widget: // (dependency to plugin???) //---------------------------------------------------------------------------- // QGridLayout* layout = new QGridLayout(this); // layout->setObjectName(QLatin1String("MapImpl-layout")); // layout->setMargin(0); // layout->addWidget(d->m_mapGraphicsView, 0, 0); // this->setLayout(layout); // StackedLayout: d->m_stackedLayout = new QStackedLayout(); d->m_stackedLayout->setStackingMode(QStackedLayout::StackAll); this->setLayout(d->m_stackedLayout); //---------------------------------------------------------------------------- // set default IdGenerator / IdContainer: // can be replaced using 'setObjectContainer' //---------------------------------------------------------------------------- QSharedPointer idGen(new SituationMap::IdGenerator); QSharedPointer objectCont(new SituationMap::ObjectContainerCTC(idGen)); setObjectContainer(objectCont); //---------------------------------------------------------------------------- // set default partyIdToColorFunctor: // can be replaced using '' //---------------------------------------------------------------------------- d->m_partyIdToColorFunctor = QSharedPointer(new PartyIdToColorFunctor()); d->m_partychangedDelayTimer = new QTimer(this); d->m_partychangedDelayTimer->setSingleShot(true); d->m_partychangedDelayTimer->setInterval(1000); connect(d->m_partychangedDelayTimer, &QTimer::timeout, this, &MapImpl::onAllPartysChanged); //---------------------------------------------------------------------------- // setting of further details are continued within 'init D' and 'onMapReady'... //---------------------------------------------------------------------------- TRACE_LOG(m_logger) << LOGFUNCTION << " END " << this; } bool MapImpl::init(const Mode mapMode_2D3D) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " mode=" << mapMode_2D3D; bool success = false; switch (mapMode_2D3D) { case Mode::Mode_2D: success = init2D(); break; case Mode::Mode_3D: success = init3D(); break; case Mode::Undefined: { ERROR_LOG(m_logger) << LOGFUNCTION << " undefined 2D/3D mode"; return false; } } //switch this->getEsriGeoView()->setAttributionTextVisible(false); // ToDo Check #if ACRGIS_VERSION >= 0x640500 // 100.5.0 / Update 5 this->getEsriGeoView()->setSelectionProperties(Esri::ArcGISRuntime::SelectionProperties(Qt::magenta)); #endif // ACRGIS_VERSION TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " mode=" << mapMode_2D3D << " success=" << success; return success; } // for debugging : class MyMapGraphicsView : public Esri::ArcGISRuntime::MapGraphicsView { using BaseClass = Esri::ArcGISRuntime::MapGraphicsView; public: MyMapGraphicsView(QWidget* parent = nullptr) : BaseClass(parent) {} void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE { BaseClass::paintEvent(event); qCritical() << " MyMapGraphicsView::paintEvent called" << " type=" << event->type() << " rect=" << event->rect() << " region=" << event->region(); } }; // class MyMapGraphicsView class MySceneGraphicsView : public Esri::ArcGISRuntime::SceneGraphicsView { using BaseClass = Esri::ArcGISRuntime::SceneGraphicsView; public: MySceneGraphicsView(QWidget* parent = nullptr) : BaseClass(parent) {} void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE { BaseClass::paintEvent(event); qCritical() << " MySceneGraphicsView::paintEvent called" << " type=" << event->type() << " rect=" << event->rect() << " region=" << event->region(); } }; // class MySceneGraphicsView // : for debugging bool MapImpl::init2D() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; d->m_mapReadyFlag = false; #ifdef LTS_DEBUG //---------------------------------------------------------------------------- // QNetworkProxy //---------------------------------------------------------------------------- { QNetworkProxy proxy = QNetworkProxy::applicationProxy(); INFO_LOG(m_logger) << LOGFUNCTION << " QNetworkProxy is NOT Empty" << ", hostname: '" << proxy.hostName() << "'" << ", port: '" << proxy.port() << "'" << ", user: '" << proxy.user() << "'" // << ", password: '" << proxy.password() << "'" ; } #endif //---------------------------------------------------------------------------- // Map //---------------------------------------------------------------------------- d->m_esriMap = new Esri::ArcGISRuntime::Map(d->m_basemap, this); d->m_esriMap->setObjectName(QLatin1String("EsriMap")); // the fetch infos (like minMaxScale) of layers: d->m_esriMap->setAutoFetchLegendInfos(true); //---------------------------------------------------------------------------- // connect some signals: //---------------------------------------------------------------------------- connect(d->m_esriMap, &Esri::ArcGISRuntime::Map::errorOccurred, this, &MapImpl::onErrorMap); connect(d->m_esriMap, &Esri::ArcGISRuntime::Map::doneLoading, this, &MapImpl::onDoneLoading); connect(d->m_esriMap->operationalLayers(), &Esri::ArcGISRuntime::LayerListModel::errorOccurred, this, &MapImpl::onErrorOperationalLayer); //---------------------------------------------------------------------------- // MapGraphicsView //---------------------------------------------------------------------------- try { // d->m_mapGraphicsView = new Esri::ArcGISRuntime::MapGraphicsView(d->m_esriMap, this); d->m_mapGraphicsView = new Esri::ArcGISRuntime::MapGraphicsView(this); d->m_mapGraphicsView->setObjectName(QLatin1String("EsriMapGraphicsView")); d->m_mapGraphicsView->setFrameShape(QFrame::NoFrame); // frameless #ifdef USE_OWN_EVENTHANDLER d->m_mapGraphicsView->viewport()->installEventFilter(this); // filter drag and drop events #endif // USE_OWN_EVENTHANDLER d->m_mapGraphicsView->installEventFilter(this); // filter keyboard events // filter keyboard events new, for 2D and 3D connect(d->m_mapGraphicsView, &Esri::ArcGISRuntime::MapGraphicsView::keyPressed, this, &MapImpl::onKeyPressEvent); } catch (std::exception &stdEx) { const char *what = stdEx.what(); if (what) { FATAL_LOG(m_logger) << LOGFUNCTION << "std Exception " << " what: '" << what << "'"; } else FATAL_LOG(m_logger) << LOGFUNCTION << "std Exception"; throw; // re-throw } catch (...) { FATAL_LOG(m_logger) << LOGFUNCTION << "unknown Exception"; throw; // re-throw } //---------------------------------------------------------------------------- // Add to StackedLayout: //---------------------------------------------------------------------------- d->m_stackedLayout->addWidget(d->m_mapGraphicsView); //---------------------------------------------------------------------------- // some details: //---------------------------------------------------------------------------- d->m_mapGraphicsView->setWrapAroundMode(Esri::ArcGISRuntime::WrapAroundMode::Disabled); // initFeature(); // handle rotations controlled by ESRI View connect( d->m_mapGraphicsView, &Esri::ArcGISRuntime::MapGraphicsView::mapRotationChanged, this, &MapImpl::onEsriRotate ); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } bool MapImpl::initFeature() { QString urlStr(QLatin1String("http://sampleserver6.arcgisonline.com/arcgis/rest/services/Energy/Geology/FeatureServer/9")); urlStr = QLatin1String("http://10.86.144.36/arcgis/rest/services/Bremen/MountingPoints/FeatureServer/0"); QUrl featureUrl(urlStr); // create the feature table Esri::ArcGISRuntime::ServiceFeatureTable *featureTable = new Esri::ArcGISRuntime::ServiceFeatureTable(featureUrl, this); // create the feature layer using the feature table Esri::ArcGISRuntime::FeatureLayer *featureLayer = new Esri::ArcGISRuntime::FeatureLayer(featureTable, this); // add the feature layer to the map d->m_esriMap->operationalLayers()->append(featureLayer); return true; } bool MapImpl::init3D() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; d->m_mapReadyFlag = false; //---------------------------------------------------------------------------- // Map //---------------------------------------------------------------------------- d->m_esriScene = new Esri::ArcGISRuntime::Scene(d->m_basemap, this); d->m_esriScene->setObjectName(QLatin1String("EsriScene")); //---------------------------------------------------------------------------- // connect some signals: //---------------------------------------------------------------------------- connect(d->m_esriScene, &Esri::ArcGISRuntime::Scene::errorOccurred, this, &MapImpl::onErrorScene); connect(d->m_esriScene, &Esri::ArcGISRuntime::Scene::doneLoading, this, &MapImpl::onDoneLoading); connect(d->m_esriScene->operationalLayers(), &Esri::ArcGISRuntime::LayerListModel::errorOccurred, this, &MapImpl::onErrorOperationalLayer); // the fetch infos (like minMaxScale) of layers: d->m_esriScene->setAutoFetchLegendInfos(true); //---------------------------------------------------------------------------- // SceneGraphicsView //---------------------------------------------------------------------------- try { // 2017-11-02, V100.1 // restoreOperatorMapSettings (restoreViewpoint will not really work, if the parent is set. // wierd, but true... d->m_sceneGraphicsView = new Esri::ArcGISRuntime::SceneGraphicsView(); // no parent, d->m_sceneGraphicsView->setObjectName(QLatin1String("EsriSceneView")); d->m_sceneGraphicsView->setFrameShape(QFrame::NoFrame); // frameless // filter keyboard events new, for 2D and 3D connect(d->m_sceneGraphicsView, &Esri::ArcGISRuntime::SceneGraphicsView::keyPressed, this, &MapImpl::onKeyPressEvent); } catch (std::exception &stdEx) { const char *what = stdEx.what(); if (what) { FATAL_LOG(m_logger) << LOGFUNCTION << "std Exception " << " what: '" << what << "'"; } else FATAL_LOG(m_logger) << LOGFUNCTION << "std Exception"; throw; // re-throw } catch (...) { FATAL_LOG(m_logger) << LOGFUNCTION << "unknown Exception"; throw; // re-throw } //---------------------------------------------------------------------------- // ScreenToLocationHelper // to reduce the load of screenToLocation, limit the number of calculation to 10/sec //---------------------------------------------------------------------------- d->m_screenToLocationHelper = new ScreenToLocationHelper(d->m_sceneGraphicsView); connect(d->m_screenToLocationHelper, &ScreenToLocationHelper::mouseScenePositionChanged, this, &MapImpl::onMouseScenePositionChanged, Qt::UniqueConnection); //---------------------------------------------------------------------------- // Add to StackedLayout: //---------------------------------------------------------------------------- d->m_stackedLayout->addWidget(d->m_sceneGraphicsView); //---------------------------------------------------------------------------- // some details: //---------------------------------------------------------------------------- // SceneView has no member 'setWrapAroundMode' // d->m_sceneGraphicsView->setWrapAroundMode(Esri::ArcGISRuntime::WrapAroundMode::Disabled); // handle rotations controlled by ESRI View connect(d->m_sceneGraphicsView, &Esri::ArcGISRuntime::SceneGraphicsView::viewpointChanged, this, &MapImpl::onEsriRotate); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } void MapImpl::onPartyChanged(int changedPartyId) { INFO_LOG(m_logger) << LOGFUNCTION << " START, changedPartyId=" << changedPartyId << ", this->objectContainer().countObjects()=" << this->objectContainer().countObjects() << ", isReady = " << this->isReady(); // partyIdToColorFunctor signals that colorTable has changed. // // Update UniqueValueRenderer: (for cubes displaying the buildingOwnership // if ((this->mode() == Mode::Mode_3D) && this->layerFactory().hasLayernameForCubes()) this->layerFactory().assignUniqueValueRendererForCubes(); if (this->isReady()) INFO_LOG(m_logger) << LOGFUNCTION << " do party-update."; else { INFO_LOG(m_logger) << LOGFUNCTION << " skip party-update, because map is not ready yet."; emit partysChangedDelayed(); return; } // // clearXmlSymbolCache // // maybe on particpant has an XML-Symbol with NBC-Protection=1; // to force re-creation of that addonSymbol: symbolDictionary().clearXmlSymbolCache(); // // Update all MapObjects // long objectsToUpdate = 0; QMutableMapIterator> mapObjIt = this->objectContainer().mutableIterator(); while (mapObjIt.hasNext()) { mapObjIt.next(); RefPointer mapObj = mapObjIt.value(); if (mapObj) { bool changed = mapObj->setPartyIdChanged(changedPartyId); if (changed) { ++objectsToUpdate; if (! mapObj->updateView()) ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed, debugInfo='" << mapObj->debugInfo() << "'"; } // if (changedPartyId >= 0) // if (mapObj->partyId() != changedPartyId) // ToDo: honor injuryPartyId also // continue; // mapObj->setPartyIdChanged(changedPartyId); // if (! mapObj->updateView()) // ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed, debugInfo='" << mapObj->debugInfo() << "'"; } } // while INFO_LOG(m_logger) << LOGFUNCTION << " START, partysChangedDelayed " ; emit partysChangedDelayed(); INFO_LOG(m_logger) << LOGFUNCTION << objectsToUpdate << " of " << this->objectContainer().countObjects() << " were updated"; } void MapImpl::onAllPartysChanged() { DEBUG_LOG(m_logger) << LOGFUNCTION << " START"; onPartyChanged(-1); } void MapImpl::onCheckMapReadyAndSymbolDictionaryLoaded() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (d->m_mapReadyFlag && (AdvancedSymbology::SymbolDictionary::alreadyExists(d->m_symbologyType) && symbolDictionary().isLoaded() && (symbolDictionary().symbologySpecificCount()>0)) && d->m_layerFactoryImpl->elevationSourcesLoaded() ) { if (!d->m_mapReadyAndSymbolDictionaryEmitted) { DEBUG_LOG(m_logger) << LOGFUNCTION << " ***** emitting mapReadyAndSymbolDictionaryLoaded" << ": '" << mode() << "'" << ": '" << symbolDictionary().symbologyType() << "'" << " (" << symbolDictionary().symbologySpecificCount() << " + " << symbolDictionary().etsSymbolCount() << " + " << symbolDictionary().xmlSymbolCount() << " = " << symbolDictionary().count() << ")"; emit mapReadyAndSymbolDictionaryLoaded(); d->m_mapReadyAndSymbolDictionaryEmitted = true; } } } bool MapImpl::fini() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; // Eventhandling: delete d->m_mouseEventHandler; delete d->m_measurementImpl; // ToDo: check delete d->m_terrainProfileImpl; delete d->m_viewshedImpl; if (d->m_partychangedDelayTimer) d->m_partychangedDelayTimer->stop(); emit beginDestroy(); // to enable destruction of FunctionDialog and its object first // cleanup overlays: if (d->m_overlayHandler) { INFO_LOG(m_logger) << LOGFUNCTION << " cleanup " << d->m_overlayHandler->overlayCount() << " overlays..."; d->m_overlayHandler->removeAllOverlays(); } else { INFO_LOG(m_logger) << LOGFUNCTION << " no overlays to cleanup -> skip"; } // cleanup overviewMap: if (d->m_overviewMapHandler) { INFO_LOG(m_logger) << LOGFUNCTION << " cleanup overviewMap"; d->m_overviewMapHandler->removeAll(); } // SideBar: // (remove embedded widgets like JumpPointWidget and is JumpPoints from the map) removeSideBar(); // ObjectFactory: int numberOfObjects = d->m_objectFactory->count(); INFO_LOG(m_logger) << LOGFUNCTION << " numberOfObjects at the moment = " << numberOfObjects; if (numberOfObjects > 0) d->m_objectFactory->clearObjects(); // Verify: numberOfObjects = d->m_objectFactory->count(); INFO_LOG(m_logger) << LOGFUNCTION << " numberOfObjects remaining = " << numberOfObjects; // deleting the LayerFactory may cause changing to DratStatus::InProgress: delete d->m_layerFactoryImpl; delete d->m_gridImpl; delete d->m_rubberbandFactory; // The static MapSingleton::getFactoryMap() should be cleaned up // via 'qAddPostRoutine(cleanupFactoryMap);' delete d->m_objectFactory; delete d->m_coordTranslationFunctorInitialize; // cleanup/detach ElevationMap d->m_elevationMap = nullptr; d->m_elevationMapManager = nullptr; INFO_LOG(m_logger) << LOGFUNCTION << " drawStatus = " << this->drawStatus(); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } void MapImpl::fini2D() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; fini(); delete d->m_esriMap; d->m_esriMap = nullptr; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } void MapImpl::fini3D() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; fini(); INFO_LOG(m_logger) << LOGFUNCTION << " drawStatus = " << this->drawStatus(); delete d->m_sceneGraphicsView; d->m_sceneGraphicsView = nullptr; // delete d->m_esriScene; INFO_LOG(m_logger) << LOGFUNCTION << " END"; } bool MapImpl::load() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (d->m_mapGraphicsView && d->m_sceneGraphicsView) { FATAL_LOG(m_logger) << LOGFUNCTION << " Upps, 2D(Map) and also 3D (Scene) -> error!!! "; FATAL_LOG(m_logger) << LOGFUNCTION << " Upps, 2D(Map) and also 3D (Scene) -> error!!! "; FATAL_LOG(m_logger) << LOGFUNCTION << " Upps, 2D(Map) and also 3D (Scene) -> error!!! "; } // // setMap // // will start esris cascading load dependecies... if (d->m_mapGraphicsView) d->m_mapGraphicsView->setMap(d->m_esriMap); // or if (d->m_sceneGraphicsView) d->m_sceneGraphicsView->setArcGISScene(d->m_esriScene); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } Mode MapImpl::mode() const { Mode mode = Mode::Undefined; if (d->m_esriMap && (!d->m_esriScene)) mode = Mode::Mode_2D; else if (!d->m_esriMap && d->m_esriScene) mode = Mode::Mode_3D; return mode; } void MapImpl::setParent(QWidget *parent, bool bBeginDestroyFini) { QWidget::setParent(parent); if (bBeginDestroyFini) { // GUEZE-791 - RPL Nr 791: Arbeitsspeicherleck // If map at 3D is cached and going to be unused, destroy // some high level objects: // see ::fini() delete d->m_measurementImpl; d->m_measurementImpl = nullptr; delete d->m_terrainProfileImpl; d->m_terrainProfileImpl = nullptr; delete d->m_viewshedImpl; d->m_viewshedImpl = nullptr; delete d->m_objectIndicatorImpl; d->m_objectIndicatorImpl = nullptr; emit beginDestroy(); // cleanup overlays: if (d->m_overlayHandler) { INFO_LOG(m_logger) << LOGFUNCTION << " cleanup " << d->m_overlayHandler->overlayCount() << " overlays..."; d->m_overlayHandler->removeAllOverlays(); } else { INFO_LOG(m_logger) << LOGFUNCTION << " no overlays to cleanup -> skip"; } int numberOfObjects = d->m_objectFactory->count(); INFO_LOG(m_logger) << LOGFUNCTION << " numberOfObjects remaining = " << numberOfObjects; } } void MapImpl::installViewPortEventFilter(QObject *eventFilter) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (! eventFilter) return; d->m_mapGraphicsView->viewport()->installEventFilter(eventFilter); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } // void MapImpl::installViewPortEventFilter( QObject * eventFilter ) void MapImpl::removeViewPortEventFilter(QObject *eventFilter) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (! eventFilter) return; d->m_mapGraphicsView->viewport()->removeEventFilter(eventFilter); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } QPointer MapImpl::createMapNotifier() const { MapImpl *THIS = const_cast(this); return MapNotifier::createMapNotifier(THIS, THIS); } void MapImpl::onLoadStatusChanged(Esri::ArcGISRuntime::LoadStatus loadStatus) { DEBUG_LOG(m_logger) << LOGFUNCTION << " loadStatus changes to '" << loadStatus << "'"; } void MapImpl::onDoneLoading(const Esri::ArcGISRuntime::Error &loadError) { TRACE_LOG(m_logger) << LOGFUNCTION << " START " << this << " d->m_mapGraphicsView=" << d->m_mapGraphicsView << " d->m_sceneGraphicsView=" << d->m_sceneGraphicsView; if (! loadError.isEmpty()) ERROR_LOG(m_logger) << LOGFUNCTION << " loadError: " << loadError; if (d->m_mouseEventHandler) { ERROR_LOG(m_logger) << LOGFUNCTION << " already loaded, return"; return; } // d->m_absoluteScale = d->m_mapGraphicsView->mapScale(); // update d->m_absoluteScale // filter mouse events to navigate the map #ifdef USE_OWN_EVENTHANDLER { switch (this->mode()) { case Mode::Mode_2D: d->m_mouseEventHandler = new RB::EventMouseNavigator(d->m_mapGraphicsView->viewport(), this); break; case Mode::Mode_3D: d->m_mouseEventHandler = new RB::EventMouseNavigator(d->m_sceneGraphicsView->viewport(), this); break; default: // empty break; } // switch if (d->m_mouseEventHandler) { // handle mouse move events connect(d->m_mouseEventHandler , &RB::EventMouseNavigator::mouseMovedEvent , this , &MapImpl::onHandleMouseMove); connect(d->m_mouseEventHandler , &RB::EventMouseNavigator::selectionRequest , this , &MapImpl::onClickMapObject); connect(d->m_mouseEventHandler , &RB::EventMouseNavigator::contextMenuRequest , this , &MapImpl::onContextClick); #ifdef SITUATIONMAP_OWN_WHEEL_HANDLING connect(d->m_mouseEventHandler , &RB::EventMouseNavigator::mouseWheelEvent , this , &MapImpl::onWheelZoom); #endif // SITUATIONMAP_OWN_WHEEL_HANDLING if (d->m_sideBar && d->m_pbSideBar) { connect(d->m_mouseEventHandler, &RB::EventMouseNavigator::selectionRequest, this, [this] (const QList< SituationMap::UniqueMapId > & //idList , const QPointF & viewPortPos) { // ignore event, if viewPortPos is at m_pbSideBar // (In that case the SideBar is currently opening...) QRect rect = d->m_pbSideBar->rect(); rect.moveTo(d->m_pbSideBar->pos()); if (! rect.contains(viewPortPos.toPoint())) d->m_sideBar->hideAnimated(); }); } // if (d->m_sideBar && d->m_pbSideBar) } // if (d->m_mouseEventHandler) } #endif // USE_OWN_EVENTHANDLER //---------------------------------------------------------------------------- // set CoordTranslationFunctor to default within Combat Training Centers (CTC) // (SLSE) //---------------------------------------------------------------------------- if (! d->m_coordTranslationFunctorInitialize) d->m_coordTranslationFunctorInitialize = new CoordTranslationFunctorImpl(this); if (! d->m_coordTranslationFunctor) d->m_coordTranslationFunctor = d->m_coordTranslationFunctorInitialize; INFO_LOG(m_logger) << LOGFUNCTION << " ***** CoordTranslationFunctor set" << " (" << d->m_coordTranslationFunctor << ")"; INFO_LOG(m_logger) << LOGFUNCTION << " ***** wkid=" << this->getEsriSpatialReference().wkid() << " *****"; INFO_LOG(m_logger) << LOGFUNCTION << " ***** wkt='" << this->getEsriSpatialReference().wkText() << "' *****"; if (this->getEsriSpatialReference().wkid() >= 0) { // // set map ready: // INFO_LOG(m_logger) << LOGFUNCTION << " ***** emitting mapReady" << " (wkid = " << this->getEsriSpatialReference().wkid() << ", mode = " << this->mode() << ", minScale = " << this->minScale() << ", maxScale = " << this->maxScale() << ", this=" << this << ", ThreadId=" << QThread::currentThreadId() << ")"; d->m_mapReadyFlag = true; emit mapReady(); // maybe SymbolDictionary was loaded earlier than map: onCheckMapReadyAndSymbolDictionaryLoaded(); } // if (this->getEsriSpatialReference().wkid() >= 0) else ERROR_LOG(m_logger) << LOGFUNCTION << " wkid failed, wkid=" << this->getEsriSpatialReference().wkid() << " *****"; // Problems with order: // mapReady and "connect viewpointChanged" -> ok // "connect viewpointChanged" and mapReady -> NOT ok // connect some signals: if (d->m_mapGraphicsView) { connect(d->m_mapGraphicsView, &Esri::ArcGISRuntime::MapGraphicsView::viewpointChanged, this, &MapImpl::onViewpointChanged); connect(d->m_mapGraphicsView, &Esri::ArcGISRuntime::MapGraphicsView::rectChanged, this, &MapImpl::onRectChanged); } if (d->m_sceneGraphicsView) { connect(d->m_sceneGraphicsView, &Esri::ArcGISRuntime::SceneGraphicsView::viewpointChanged, this, &MapImpl::onViewpointChanged); connect(d->m_sceneGraphicsView, &Esri::ArcGISRuntime::SceneGraphicsView::rectChanged, this, &MapImpl::onRectChanged); } // check mapReady and SymbolDictionary::loaded onCheckMapReadyAndSymbolDictionaryLoaded(); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } void MapImpl::onMouseScenePositionChanged(const Point3D &locationScene, bool isValid) { if (d->m_destroying) return; // 1 emit mouseMapPositionChanged(locationScene, isValid); SituationMap::CoordTranslationFunctor *mapCtFunctor = coordTranslationFunctor(); if (mapCtFunctor) { if (isSignalConnected(QMetaMethod::fromSignal(&Map::mouseGeoPositionChanged))) { // 2 emit mouseGeoPositionChanged(mapCtFunctor->mapToGeographic(locationScene), isValid); } } } void MapImpl::onRubberbandPickMapObjectsApply(RB::RubberbandBase *rubberband) { if (d->m_destroying) return; TRACE_LOG(m_logger) << LOGFUNCTION << " START"; bool mapObjectIdsPicked = isSignalConnected(QMetaMethod::fromSignal(&Map::mapObjectIdsPickedByArea)); bool mapObjectsPicked = isSignalConnected(QMetaMethod::fromSignal(&Map::mapObjectsPickedByArea)); // // cehck if any signal is connected: if (!(mapObjectIdsPicked || mapObjectsPicked)) return; QList pickedMapObjectIdList; RefPointerMapObjectList pickedMapObjects; const QVector &mapCoordVec = rubberband->coordinateVector(RB::CoordinateSystem::Map); if (mapCoordVec.size() != 4) { ERROR_LOG(m_logger) << LOGFUNCTION << " failure, coordVec.size()=" << mapCoordVec.size(); return; } // create esriHull for geometryIsInHull ; create esriLine for geometryIntersect // // if required : // Esri::ArcGISRuntime::PolylineBuilder polylineBuilder(this->getEsriSpatialReference()); // polylineBuilder.addPoint(mapCoord.x(), mapCoord.y()); // mapObj->geometryIntersect(esriLine) // : if required Esri::ArcGISRuntime::PolygonBuilder polygonBuilder(this->getEsriSpatialReference()); for (const QPointF &mapCoord : mapCoordVec) polygonBuilder.addPoint(mapCoord.x(), mapCoord.y()); const Esri::ArcGISRuntime::Geometry &esriHull = polygonBuilder.toGeometry(); // check any MapObject const QList allMapObj = this->objectContainer().getAllObjects(); pickedMapObjectIdList.reserve(allMapObj.size()); pickedMapObjects.reserve(allMapObj.size()); for (AbstractMapObject *mapObj : allMapObj) { if (!mapObj) continue; if (!mapObj->pickable()) continue; if (mapObj->geometryIsInHull(esriHull)) { pickedMapObjectIdList.append(mapObj->uniqueMapId()); pickedMapObjects.append(RefPointer(mapObj)); } } // emit if (mapObjectIdsPicked) emit mapObjectIdsPickedByArea(pickedMapObjectIdList); if (mapObjectsPicked) emit mapObjectsPickedByArea(pickedMapObjects); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } MapImpl::~MapImpl() { INFO_LOG(m_logger) << LOGFUNCTION << " START, s_counter=" << d->s_counter--; d->m_destroying = true; if (d->m_maxExtentTimer) d->m_maxExtentTimer->stop(); delete d->m_maxExtentTimer; d->m_maxExtentTimer = nullptr; delete d->m_rocketTimer; d->m_rocketTimer = nullptr; if (d->m_screenToLocationHelper) { d->m_screenToLocationHelper->disconnect(this); INFO_LOG(m_logger) << LOGFUNCTION << " Helper pendingOperationsCount = " << d->m_screenToLocationHelper->pendingOperationsCount(); delete d->m_screenToLocationHelper; d->m_screenToLocationHelper = nullptr; } if (d->m_locationToElevationHelper) { d->m_locationToElevationHelper->disconnect(this); INFO_LOG(m_logger) << LOGFUNCTION << " Helper pendingOperationsCount = " << d->m_locationToElevationHelper->pendingOperationsCount(); delete d->m_locationToElevationHelper; d->m_locationToElevationHelper = nullptr; } delete d->m_objectIndicatorImpl; d->m_objectIndicatorImpl = nullptr; delete d->m_locationDisplayImpl; d->m_locationDisplayImpl = nullptr; switch(mode()) { case Mode::Mode_2D: fini2D(); break; case Mode::Mode_3D: fini3D(); break; case Mode::Undefined: fini(); break; } delete d->m_basemap; d->m_basemap = nullptr; delete d; INFO_LOG(m_logger) << LOGFUNCTION << " END " << this; } double MapImpl::animationDuration() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return d->m_animationDuration; } void SituationMap::MapImpl::setAnimationDuration(double durationSec) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " durationSec=" << durationSec; d->m_animationDuration = durationSec; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } double SituationMap::MapImpl::rocketDuration() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return d->m_rocketDuration; } void SituationMap::MapImpl::setRocketDuration(double durationSec) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " durationSec=" << durationSec; d->m_rocketDuration = durationSec; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } //class MyLabel : public QLabel //{ //public: // void hideEvent(QHideEvent* event) override // { // qWarning() << LOGFUNCTION << " type=" << event->type(); // QLabel::hideEvent(event); //// abort(); // } // bool event(QEvent *e) // { // qWarning() << LOGFUNCTION << " type=" << e->type(); // return QLabel::event(e); // } // void updateGeometry() // { // qWarning() << LOGFUNCTION; // QLabel::updateGeometry(); // } //}; void MapImpl::addMapWidget() { // return addMapWidget_OwnWidget(); // return this->addMapWidget_AddToScene(); if (this->isReady()) this->addMapWidget_AddToScene(); else { connect(this, &Map::mapReady, this, [this]() { this->addMapWidget_AddToScene(); }); } } bool MapImpl::setTextBox(const PixelTextBoxData &ptbd, bool enableGeometryModifications) { FATAL_LOG(m_logger) << LOGFUNCTION << " XXXXXXXXXXXXXXXXXX START" << " enableGeomtryUpdate " << enableGeometryModifications << ", ptbd " << ptbd.toDebugString(); if (ptbd.m_htmlText.isEmpty()) return false; QGraphicsView *gView = this->getQGraphicsView(); if (!gView) return false; d->m_textBoxPixelAndCo.reset(); if (!d->m_textBoxPixelAndCo.m_textEdit) { d->m_textBoxPixelAndCo.m_textEdit = new QTextEdit(); d->m_textBoxPixelAndCo.m_textEdit->setObjectName(QStringLiteral("WidgetOverlay_QTextEdit")); d->m_textBoxPixelAndCo.m_textEdit->setReadOnly(false); d->m_textBoxPixelAndCo.m_label = new QLabel(QStringLiteral("new QLabel")); // Make it 'modifiable' if (enableGeometryModifications) { // It looks like: // GraphicsItem <-> ProxyWidget <-> Widget/TextEdit // (QGraphicsItem is a baseclass of both GraphicsItem as well as ProxyWidget) // GraphicsItem becomes parent of ProxyWidget // Then TextEdit is added via setWidget to ProxyWidget // Now both are connected, TextEdit in front of GraphicsItem which ist little bit bigger // GraphicsItem is movable, GI an TE are connected to each other... // 10.05.2023, Norbert Thoden // (((Idea: Maybe ProxyWidget should be parent of GraphicsItem // to put GraphicsItem on top of TextEdit???))) d->m_textBoxPixelAndCo.m_handle_or_proxyControl = new QGraphicsRectItem(0, 0, 0, 0); // x,y == rel position, size will be set later d->m_textBoxPixelAndCo.m_handle_or_proxyControl->setCursor(Qt::SizeAllCursor); d->m_textBoxPixelAndCo.m_proxy = new QGraphicsProxyWidget(d->m_textBoxPixelAndCo.m_handle_or_proxyControl); d->m_textBoxPixelAndCo.m_proxy->setObjectName(QStringLiteral("ProxyWidgetOverlay_QTextEdit")); d->m_textBoxPixelAndCo.m_proxy->setWidget(d->m_textBoxPixelAndCo.m_textEdit); // d->m_textBoxPixelAndCo.m_proxy->setWidget(d->m_textBoxPixelAndCo.m_label); d->m_textBoxPixelAndCo.m_proxy->setPos(QPoint(0, 0)); // relative position to GraphicsItem !? d->m_textBoxPixelAndCo.m_proxy->setOpacity(0.99); // avoid white on white // d->m_textBoxPixelAndCo.m_proxy->setZValue(std::numeric_limits::max()); d->m_textBoxPixelAndCo.m_handle_or_proxyControl->setPen(QPen(Qt::gray)); d->m_textBoxPixelAndCo.m_handle_or_proxyControl->setBrush(QBrush(Qt::gray)); d->m_textBoxPixelAndCo.m_handle_or_proxyControl->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable); this->getQGraphicsView()->scene()->addItem(d->m_textBoxPixelAndCo.m_handle_or_proxyControl); // restore geometry: d->m_textBoxPixelAndCo.m_handle_or_proxyControl->setPos(ptbd.m_position); // SizeGrip : QSizeGrip *sizeGrip = new QSizeGrip(d->m_textBoxPixelAndCo.m_textEdit); QHBoxLayout *layout = new QHBoxLayout(d->m_textBoxPixelAndCo.m_textEdit); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(sizeGrip, 0, Qt::AlignRight | Qt::AlignBottom); // : SizeGrip { // Sync geometry of TextEdit and GraphicsItem/ProxyWidget: // create fn... std::function fnUpdateGraphicsItemGeometry = [this]() { d->m_textBoxPixelAndCo.m_handle_or_proxyControl->setRect( d->m_textBoxPixelAndCo.m_textEdit->geometry().adjusted(-10, -10, 10, 10) ); // qCritical() << " handle_or_proxyControl = " << d->m_textBoxPixelAndCo.m_handle_or_proxyControl->pos(); // qCritical() << " d->m_textEdit->geometry() = " << d->m_textBoxPixelAndCo.m_textEdit->geometry(); }; // ... to be executed initially fnUpdateGraphicsItemGeometry(); // ... and on geometry changes // QTextEdit has no geometryChanged or similar, but ProxyWidget has: connect(d->m_textBoxPixelAndCo.m_proxy, &QGraphicsProxyWidget::geometryChanged, fnUpdateGraphicsItemGeometry); } { // get pos and geometry; std::function fnGetPosAndGeometry = [this]() { // qCritical() << " 222 handle_or_proxyControl pos = " << handle_or_proxyControl->pos(); // qCritical() << " 222 handle_or_proxyControl rect= " << handle_or_proxyControl->rect(); // const QPoint &pos = d->m_textBoxPixelAndCo.m_handle_or_proxyControl->pos().toPoint(); const QSize size(d->m_textBoxPixelAndCo.m_handle_or_proxyControl->rect().width(), d->m_textBoxPixelAndCo.m_handle_or_proxyControl->rect().height()); //maybe take border of 10 pixel into account!? // qCritical() << " 222 handle_or_proxyControl pos= " << pos // << ", size= " << size; // ptbd.m_position = pos; // ptbd.m_size = size; }; auto *timer =new QTimer(this); connect(timer, &QTimer::timeout, fnGetPosAndGeometry); // timer->start(5000); } } else { // d->m_textBoxPixelAndCo.m_proxy = this->getQGraphicsView()->scene()->addWidget(d->m_textBoxPixelAndCo.m_textEdit); // d->m_textBoxPixelAndCo.m_proxy->setOpacity(0.99); // d->m_textBoxPixelAndCo.m_proxy->setZValue(std::numeric_limits::max()); // d->m_textBoxPixelAndCo.m_proxy->setPos(QPoint(75, 75)); // d->m_textBoxPixelAndCo.m_proxy->setObjectName(QStringLiteral("ProxyWidgetOverlay_QTextEdit")); } } // qCritical() << " setText= " << ptbd.m_htmlText; d->m_textBoxPixelAndCo.m_textEdit->setText(ptbd.m_htmlText); d->m_textBoxPixelAndCo.m_proxy->setOpacity(0.99); // QTimer::singleShot(5000, this, [this] () { // FATAL_LOG(m_logger) << LOGFUNCTION << " XXXX timer: reset..."; // d->m_textBoxPixelAndCo.reset(); // FATAL_LOG(m_logger) << LOGFUNCTION << " XXXX timer: reset... Done"; // }); // QSizeF size = te->document()->size(); // te->setFixedWidth(te->document()->idealWidth() + 100); // te->setFixedSize(size.toSize()); // QGraphicsProxyWidget *proxy = this->getQGraphicsView()->scene()->addWidget(d->m_textEdit); // proxy->setOpacity(0.99); // proxy->setZValue(std::numeric_limits::max()); // proxy->setPos(QPoint(75, 75)); // proxy->setObjectName(QStringLiteral("ProxyWidgetOverlay_QTextEdit")); /* QTimer::singleShot(10000000, this, [this] () { FATAL_LOG(m_logger) << LOGFUNCTION << " XXXX timer:"; qCritical() << " size " << d->m_textEdit->size(); auto *handle_or_proxyControl = new QGraphicsRectItem(0, 0, 0, 0); // x,y == rel position handle_or_proxyControl->setCursor(Qt::SizeAllCursor); auto *localProxy = new QGraphicsProxyWidget(handle_or_proxyControl); localProxy->setWidget(d->m_textEdit); localProxy->setPos(QPoint(75, 75)); // SizeGrip : QSizeGrip *sizeGrip = new QSizeGrip(d->m_textEdit); QHBoxLayout *layout = new QHBoxLayout(d->m_textEdit); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(sizeGrip, 0, Qt::AlignRight | Qt::AlignBottom); // : SizeGrip handle_or_proxyControl->setPen(QPen(Qt::yellow)); handle_or_proxyControl->setBrush(QBrush(Qt::gray)); handle_or_proxyControl->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable ); this->getQGraphicsView()->scene()->addItem(handle_or_proxyControl); handle_or_proxyControl->setRect(d->m_textEdit->geometry().adjusted(-10, -10, 10, 10) ); connect(d->m_textEdit.data(), &QTextEdit::cursorPositionChanged, [this, handle_or_proxyControl] () { handle_or_proxyControl->setRect( d->m_textEdit->geometry().adjusted(-10, -10, 10, 10) ); }); // QGraphicsWidget* parentWidget = new QGraphicsWidget(handle);//make parent of widget // parentWidget->setCursor(Qt::SizeAllCursor); // qCritical() << " size " << d->m_textEdit; // parentWidget->setGeometry(100, 100, 500, 500); // parentWidget->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable ); // parentWidget->setSelected(true); // this->getQGraphicsView()->scene()->addItem(handle); // localProxy->setParentItem(parentWidget); }); */ /* // Moving: QTimer::singleShot(1000, this, [this, &te] () { FATAL_LOG(m_logger) << LOGFUNCTION << " timer:"; QGraphicsWidget* parentWidget = new QGraphicsWidget();//make parent of widget parentWidget->setCursor(Qt::SizeAllCursor); qCritical() << " size " << te->size(); // parentWidget->setGeometry(75, 75, te->size().width(), te->size().height()); parentWidget->setGeometry(75, 75, 200, 200); parentWidget->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable ); // parentWidget->setSelected(true); this->getQGraphicsView()->scene()->addItem(parentWidget); QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(); proxy->setPos(QPoint(75, 75)); // QGraphicsSimpleTextItem *gitem = new QGraphicsSimpleTextItem(); // gitem->setText(QStringLiteral("MyMovableText")); proxy->setWidget(te); proxy->setParentItem(parentWidget); }); */ FATAL_LOG(m_logger) << LOGFUNCTION << " XXXXXXXXXXXXXXXXXX Done"; return true; } bool MapImpl::removeTextBox() { FATAL_LOG(m_logger) << LOGFUNCTION << "START"; d->m_textBoxPixelAndCo.reset(); FATAL_LOG(m_logger) << LOGFUNCTION << "START"; return true; } void moveLabelTo(QLabel *label, int windowWidth, int borderX, int y) { QPoint newPos; newPos.rx() = windowWidth - label->width() - borderX; newPos.ry() = y; label->move(newPos); } QWidget *MapImpl::addMapWidget_AddToScene() { const QFont font(QLatin1String("Courier New"), 13, QFont::DemiBold); const QSize labelBorderPixel(5, 2); const QPoint distanceToFrame(25, 25); QFontMetrics fm(font); QSize labelMinSize; if (true) // (mode() == Mode::Mode_3D) && layerFactory().elevationAvailable()) labelMinSize = fm.size(0, QLatin1String("32U MD 12345 12345 - 123")); else labelMinSize = fm.size(0, QLatin1String("32U MD 12345 12345")); QPoint labelPos = distanceToFrame; // PositionLabel if (1) // Todo: unnecessary if ? { if (! d->m_positionLabel) { d->m_positionLabel = new QLabel(); d->m_positionLabel->setObjectName(QLatin1String("WidgetOverlayLayoutPositionLabel")); d->m_positionLabel->setText(QLatin1String("-")); d->m_positionLabel->setFont(font); d->m_positionLabel->setAlignment(Qt::AlignBottom | Qt::AlignCenter); d->m_positionLabel->setMinimumSize(labelMinSize + labelBorderPixel); // Bug iOSQz-000005 'QGraphicsProxyWidget will crash' // Adding a widget will let the application crash // Workaround: 'proxy->setOpacity(0.99);' QGraphicsProxyWidget *proxy = this->getQGraphicsView()->scene()->addWidget(d->m_positionLabel); proxy->setOpacity(0.99); proxy->setZValue(std::numeric_limits::max()); // lambda :-) connect(this, &SituationMap::Map::rectChanged, this, [this, distanceToFrame]() { moveLabelTo(d->m_positionLabel, getQGraphicsView()->width(), distanceToFrame.x(), d->m_positionLabel->pos().y()); }); // lambda :-) connect(this, &SituationMap::Map::mouseMapPositionChanged, this, [this](const Point3D &mapPos, bool isValid) { if (!this->isReady()) return; if (!isValid) { d->m_positionLabel->setText(QStringLiteral("-")); return; } // Shift + Strg -> lonLat in DEG // Shift + Alt -> lonLat in RAD // default -> MGRS if (d->m_positionLabel->isVisible()) { QString posStr; const Qt::KeyboardModifiers &curModifier = QGuiApplication::queryKeyboardModifiers(); SituationMap::CoordTranslationFunctor *mapCtFunctor = coordTranslationFunctor(); if (mapCtFunctor && curModifier.testFlag(Qt::KeyboardModifier::ShiftModifier)) { Point3D geoPos3D = mapCtFunctor->mapToGeographic(mapPos); QPointF geoPos2D = geoPos3D.toPointF(); if (curModifier.testFlag(Qt::ControlModifier)) { posStr = QStringLiteral("%1 - %2").arg(geoPos2D.x()).arg(geoPos2D.y()); d->m_positionLabel->setToolTip(QLatin1String("lon/x - lat/y ")); } else if (curModifier.testFlag(Qt::AltModifier)) { geoPos2D *= LtsCoordTrans::rad2deg(); // RAD to DEG posStr = QStringLiteral("%1 - %2").arg(geoPos2D.x()).arg(geoPos2D.y()); d->m_positionLabel->setToolTip(QLatin1String("lon/x - lat/y")); } } else { d->m_positionLabel->setToolTip(QString()); } if (posStr.isEmpty()) posStr = this->mapToMgrsString(mapPos.toPointF()); d->m_positionStr = posStr; switch(this->mode()) { case Mode::Mode_3D: { // 3D: simple -> use z value: // based on Surface::screenToBaseSurface / Surface::screenToLocation // if (qIsNaN(mapPos.z())) d->m_positionElevationStr = QLatin1String("INV"); else { if (std::lround(mapPos.z()) < 10.0) d->m_positionElevationStr.setNum(mapPos.z(), 'f', 1); else d->m_positionElevationStr.setNum(std::lround(mapPos.z())); } d->updatePositionText(); } break; case Mode::Mode_2D: { // if elevation is available if (this->layerFactory().elevationAvailable() && this->isReady()) { // 2D: use LocationToElevationHelper: // 1. do setup: setupLocationToElevationHelper(); // 2. trigger computation: if (d->m_locationToElevationHelper) d->m_locationToElevationHelper->locationToElevation(mapPos.toPointF()); } // if (this->layerFactory().elevationAvailable() && this->isReady()) else d->updatePositionText(); // update if elevation is NOT available } break; case Mode::Undefined: // empty break; } //switch } // if (d->m_positionLabel->isVisible()) }); // lambda } moveLabelTo(d->m_positionLabel, getQGraphicsView()->width(), distanceToFrame.x(), labelPos.y()); // Prepare next label: if (d->m_positionLabel->isVisible()) labelPos.ry() += d->m_positionLabel->minimumHeight(); } // ExerciseTimeLabel // if (isReplay()) { if (! d->m_exerciseTimeLabel) { d->m_exerciseTimeLabel = new QLabel(); d->m_exerciseTimeLabel->setVisible(false); d->m_exerciseTimeLabel->setObjectName(QLatin1String("WidgetOverlayLayoutExerciseTimeLabel")); d->m_exerciseTimeLabel->setText(QLatin1String("-")); d->m_exerciseTimeLabel->setFont(font); d->m_exerciseTimeLabel->setAlignment(Qt::AlignBottom | Qt::AlignCenter); d->m_exerciseTimeLabel->setMinimumSize(labelMinSize + labelBorderPixel); // Bug iOSQz-000005 'QGraphicsProxyWidget will crash' // Adding a widget will let the application crash // Workaround: 'proxy->setOpacity(0.99);' QGraphicsProxyWidget *proxy = this->getQGraphicsView()->scene()->addWidget(d->m_exerciseTimeLabel); proxy->setOpacity(0.99); proxy->setZValue(std::numeric_limits::max()); connect(this, &SituationMap::Map::rectChanged, this, [this, distanceToFrame]() { moveLabelTo(d->m_exerciseTimeLabel, getQGraphicsView()->width(), distanceToFrame.x(), d->m_exerciseTimeLabel->pos().y()); }); } moveLabelTo(d->m_exerciseTimeLabel, getQGraphicsView()->width(), distanceToFrame.x(), labelPos.y()); // Prepare next label: if (d->m_exerciseTimeLabel->isVisible()) labelPos.ry() += d->m_exerciseTimeLabel->minimumHeight(); } // TimeLabel: if (0) // Todo: unnecessary if ? { if (! d->m_timeLabel) { d->m_timeLabel = new QLabel(); d->m_timeLabel->setObjectName(QLatin1String("WidgetOverlayLayoutTimeStringLabel")); d->m_timeLabel->setText(QLatin1String("#")); d->m_timeLabel->setFont(font); d->m_timeLabel->setAlignment(Qt::AlignBottom | Qt::AlignCenter); d->m_timeLabel->setMinimumSize(labelMinSize + labelBorderPixel); // Bug iOSQz-000005 'QGraphicsProxyWidget will crash' // Adding a widget will let the application crash // Workaround: 'proxy->setOpacity(0.99);' QGraphicsProxyWidget *proxy = this->getQGraphicsView()->scene()->addWidget(d->m_timeLabel); proxy->setOpacity(0.99); proxy->setZValue(std::numeric_limits::max()); // lambda :-) connect(this, &SituationMap::Map::rectChanged, this, [this, distanceToFrame]() { moveLabelTo(d->m_timeLabel, getQGraphicsView()->width(), distanceToFrame.x(), d->m_timeLabel->pos().y()); }); // by default -> not visible: d->m_timeLabel->setVisible(false); } moveLabelTo(d->m_timeLabel, getQGraphicsView()->width(), distanceToFrame.x(), labelPos.y()); // Prepare next label: if (d->m_timeLabel->isVisible()) labelPos.ry() += d->m_timeLabel->minimumHeight(); } // compass if (this->mode() != Mode::Undefined) { if (! d->m_compass) { d->m_compass = new MyGraphicsPixmapItem(); // QGraphicsPixmapItem // get rde blue: QColor rdeColor = this->palette().color(QPalette::WindowText); // RDE_DARK_BLUE QColor bgColor;// = this->palette().color(QPalette::Shadow); // RDE_LIGHTER_GRAY is too pale if (! bgColor.isValid()) bgColor = Qt::gray; bgColor.setAlphaF(0.5); QImage image = renderCompass(rdeColor, Qt::white, bgColor, 8, 30, this->mode()==Mode::Mode_3D); d->m_compass->setPixmap(QPixmap::fromImage(image)); // QPixmap pixmap(QLatin1String(":/compass.png")); // d->m_compass->setPixmap(pixmap.scaledToHeight(height)); qreal xOffset = d->m_compass->pixmap().width() * 0.5; qreal yOffset = d->m_compass->pixmap().height() * 0.5; d->m_compass->setOffset(-xOffset, -yOffset); d->m_compass->setZValue(std::numeric_limits::max()); d->m_compass->setVisible(true); d->m_compass->setFlags(QGraphicsItem::ItemIgnoresTransformations); d->m_compass->setShapeMode(QGraphicsPixmapItem::BoundingRectShape); this->getQGraphicsView()->scene()->addItem(d->m_compass); // lambda :-) connect(this, &SituationMap::Map::rectChanged, this, [this, distanceToFrame]() { QPoint newPos = d->m_compass->pos().toPoint(); int maxDimHalf = std::max(d->m_compass->pixmap().width(), d->m_compass->pixmap().height()) * 0.5; newPos.rx() = this->getQGraphicsView()->width() - maxDimHalf - distanceToFrame.x(); d->m_compass->setPos(newPos); }); if (this->mode() == Mode::Mode_2D) { // lambda :-) connect(this, &Map::rotationChanged, this, [this](double absoluteAngleDegrees) { // QGraphicsItem: // Sets the clockwise rotation angle, in degrees, around the Z axis. // The default value is 0 (i.e., the item is not rotated). // Assigning a negative value will rotate the item counter-clockwise. // Normally the rotation angle is in the range (-360, 360), // but it's also possible to assign values outside of this range // (e.g., a rotation of 370 degrees is the same as a rotation of 10 degrees). // -> use values directly: d->m_compass->setRotation(absoluteAngleDegrees); }); } else if (this->mode() == Mode::Mode_3D) { // lambda :-) connect(this, &Map::extentChanged, this, [this]() { const Esri::ArcGISRuntime::Camera &cam = d->m_sceneGraphicsView->currentViewpointCamera(); d->m_compass->setRotation(360.0 - cam.heading()); }); } // lambda :-) connect(d->m_compass, &MyGraphicsPixmapItem::doubleClicked, this, [this]() { if (mode() == Mode::Mode_2D) { if (qApp->keyboardModifiers().testFlag(Qt::ShiftModifier)) { const Esri::ArcGISRuntime::Envelope &fullExtent = this->layerFactoryImpl().fullExtentOfBasemaps(); d->m_mapGraphicsView->setViewpoint(Esri::ArcGISRuntime::Viewpoint(fullExtent)); } else if (qApp->keyboardModifiers().testFlag(Qt::ControlModifier)) { this->zoomToArea(d->m_trainingsAreaExtent, 50); } else { Viewpoint2D vp = this->viewpoint2D(); vp.setRotation(0.0); this->setViewpoint2D(vp); } } else if(mode() == Mode::Mode_3D) { if (qApp->keyboardModifiers().testFlag(Qt::ShiftModifier)) { const Esri::ArcGISRuntime::Envelope &fullExtent = this->layerFactoryImpl().fullExtentOfBasemaps(); d->m_sceneGraphicsView->setViewpoint(Esri::ArcGISRuntime::Viewpoint(fullExtent)); } else if (qApp->keyboardModifiers().testFlag(Qt::ControlModifier)) { this->zoomToArea(d->m_trainingsAreaExtent, 50); } else { Camera cam = this->camera(); cam.heading = 0.0; cam.pitch = 0.0; cam.roll = 0.0; this->setCamera(cam); } } }); } // if (! d->m_compass) QPoint newPos = labelPos; newPos.ry() += 10; // small distance int maxDimHalf = std::max(d->m_compass->pixmap().width(), d->m_compass->pixmap().height()) * 0.5; newPos.ry() += maxDimHalf; newPos.rx() = this->getQGraphicsView()->width() - maxDimHalf - distanceToFrame.x(); d->m_compass->setPos(newPos); } else DEBUG_LOG(m_logger) << LOGFUNCTION << " compass not supported for 3D yet"; // to trigger all 'consumer' emitVisualComponentChanged(VisualComponent::PosititionString); emitVisualComponentChanged(VisualComponent::ExerciseTimeString); emitVisualComponentChanged(VisualComponent::TimeString); emitVisualComponentChanged(VisualComponent::CompassRose); return nullptr; } void MapImpl::setupLocationToElevationHelper() { if (d->m_locationToElevationHelper) return; auto elevationMap = this->getElevationMapManager(); if (!elevationMap) { DEBUG_LOG(m_logger) << LOGFUNCTION << " no ElevationMapManager"; return; } d->m_locationToElevationHelper = this->layerFactory().locationToElevation(); // layerFactory has ownership if (d->m_locationToElevationHelper) { // lambda :-) connect(d->m_locationToElevationHelper.data(), &SituationMap::LocationToElevationHelper::elevationCompleted, this, [this](double elevation) { // ElevationMap return std::numeric_limits::min() or similar if location is not covered: if (qIsNaN(elevation) || elevation < -30000) d->m_positionElevationStr = QLatin1String("-"); else if (std::lround(elevation) < 10.0) d->m_positionElevationStr.setNum(elevation, 'f', 1); else d->m_positionElevationStr.setNum(std::lround(elevation)); d->updatePositionText(); }); } } #ifdef NEVER class MyWidget : public QWidget { Q_OBJECT public: MyWidget() : QWidget() , m_targetWidget(nullptr) , m_enable(false) {} void setTargetWidget(QWidget *targetWidget) { m_targetWidget = targetWidget; } void enable() { qWarning() << " target is enabled now"; m_enable = true; } bool event(QEvent *e) override { qWarning() << " e=" << e; if (!m_enable) return QWidget::event(e); if (e->type() == QEvent::MouseButtonDblClick || e->type() == QEvent::MouseMove || e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonRelease) { // Mouse events are forwarded to the underlying widget QMouseEvent *event = static_cast(e); qWarning() << " event.pos=" << event->pos(); QWidget *child = childAt(event->pos()); qWarning() << " child=" << child; // no own child widgets found: if (! child) { QMouseEvent shiftedEvent(event->type(), event->pos(), event->globalPos(), event->button(), event->buttons(), event->modifiers()); qWarning() << " forwarding event=" << e; // -> send event to 'underlying' widget: QApplication::sendEvent(m_targetWidget, &shiftedEvent); } } // own child widgets found -> handle event normally: return QWidget::event(e); } private: QWidget *m_targetWidget; bool m_enable; }; #endif QWidget *MapImpl::addMapWidget_OwnWidget() { #ifdef NEVER const QFont font(QLatin1String("Courier New"), 13, QFont::DemiBold); const QSize labelBorderPixel(5, 2); // const QPoint distanceToFrame (25,25); QFontMetrics fm(font); //const QString defaultPostitionLabelStr(QLatin1String("32 U MD 12345 12345")); const QString defaultTimeStr(QLatin1String("01234567890123456789")); QSize labelMinSize = fm.size(0, defaultTimeStr); MyWidget *widget = new MyWidget(); // widget->setStyleSheet(QLatin1String("* {background-color: yellow;}")); QHBoxLayout *layout = new QHBoxLayout(); widget->setLayout(layout); widget->setTargetWidget(d->m_stackedLayout->currentWidget()); QTimer *timer = new QTimer(); timer->setSingleShot(true); timer->setInterval(2000); connect(timer, &QTimer::timeout, widget, [widget]() { widget->enable(); }); timer->start(); // add WidgetOverlayLayout and make it current: int idx = d->m_stackedLayout->addWidget(widget); d->m_stackedLayout->setCurrentIndex(idx); d->m_positionLabel = new QLabel(); d->m_positionLabel->setObjectName(QLatin1String("WidgetOverlayLayoutPositionLabel")); d->m_positionLabel->setStyleSheet(QLatin1String("QLabel {background-color: gray;}")); d->m_positionLabel->setText(QLatin1String("-")); d->m_positionLabel->setFont(font); d->m_positionLabel->setAlignment(Qt::AlignBottom | Qt::AlignCenter); d->m_positionLabel->setMinimumSize(labelMinSize + labelBorderPixel); d->m_positionLabel->setMaximumSize(this->minimumSize()); // QSpacerItem *spacer = new QSpacerItem(10, 1, QSizePolicy::Maximum, QSizePolicy::Maximum); // layout->addItem(spacer, 0,0); layout->addStretch(2); layout->addWidget(d->m_positionLabel); #endif return nullptr; } QImage SituationMap::MapImpl::renderCompass(const QColor &colorNorth, const QColor &colorSouth, const QColor &colorBackground, int halfWidth, int halfHeight, bool b_3D) { int width = halfWidth * 2; int height = halfHeight * 2; QImage image(width, height, QImage::Format_ARGB32); image.fill(Qt::transparent); QPainter painter(&image); painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); { // QPen pen(Qt::black); // pen.setWidth(1); painter.setPen(Qt::NoPen); painter.setBrush(QBrush(colorNorth, Qt::BrushStyle::SolidPattern)); // 2 || // | | // 1 | | 3 // | | // || QPolygon poly; poly.reserve(3); poly.append(QPoint( 0, halfHeight)); poly.append(QPoint(halfWidth, 0)); poly.append(QPoint( width, halfHeight)); painter.drawPolygon(poly); } { // QPen pen(Qt::black); // pen.setWidth(1); painter.setPen(Qt::NoPen); painter.setBrush(QBrush(colorSouth, Qt::BrushStyle::SolidPattern)); // || // | | // 1 | | 3 // | | // 2 || QPolygon poly; poly.reserve(3); poly.append(QPoint( 0, halfHeight)); poly.append(QPoint(halfWidth, height)); poly.append(QPoint( width, halfHeight)); painter.drawPolygon(poly); } painter.end(); // background: int maxDim = std::max(width, height); int border = 5; QImage backgroundImage(maxDim + (2 * border), maxDim + (2 * border), QImage::Format_ARGB32); { backgroundImage.fill(Qt::transparent); painter.begin(&backgroundImage); painter.setBrush(QBrush(colorBackground)); painter.setPen(Qt::NoPen); painter.drawEllipse(backgroundImage.rect()); painter.end(); } // merge: { painter.begin(&backgroundImage); int offsetX = (backgroundImage.width() - image.width()) / 2; int offsetY = (backgroundImage.height() - image.height()) / 2; QRect r = image.rect(); r.moveTo(offsetX, offsetY); painter.drawImage(r, image, image.rect()); painter.end(); } { painter.begin(&backgroundImage); QPointF pos(backgroundImage.width() / 2.0 * 1.1, backgroundImage.height() / 2.0 * 0.4); // 'N' QFont font = painter.font(); font.setBold(true); painter.setFont(font); painter.setPen(colorNorth); painter.drawText(pos, QLatin1String("N")); pos.rx() = backgroundImage.width() / 2.0 * 1.1; pos.ry() = backgroundImage.height() / 2.0 * 1.9; if (b_3D) painter.drawText(pos, QLatin1String("3D")); else painter.drawText(pos, QLatin1String("2D")); } return backgroundImage; } bool SituationMap::MapImpl::isPositionStringVisible() const { if (! d->m_positionLabel) return false; d->updatePositionText(); return d->m_positionLabel->isVisible(); } void MapImpl::showPositionString(bool visible) { if (! d->m_positionLabel) return; bool changed = d->m_positionLabel->isVisible() != visible; d->m_positionLabel->setVisible(visible); if (changed) { addMapWidget(); emitVisualComponentChanged(VisualComponent::PosititionString); } } void SituationMap::MapImpl::setPositionStringColor(const QColor &foregroundColor, const QColor &backgroundColor) { d->m_positionLabelForegroundColor = foregroundColor; d->m_positionLabelBackgroundColor = backgroundColor; if (! d->m_positionLabel) return; QLabel *label = d->m_positionLabel; if (label) { QPalette palette = label->palette(); if (d->m_positionLabelForegroundColor.isValid()) palette.setColor(label->foregroundRole(), d->m_positionLabelForegroundColor); if (d->m_positionLabelBackgroundColor.isValid()) palette.setColor(label->backgroundRole(), d->m_positionLabelBackgroundColor); label->setPalette(palette); } } void MapImpl::setPartitionTime(const qint64 &time) { if(!d->m_exerciseTimeLabel) return; d->m_exerciseTimeLabel->setText(DTG::toStringMSec(time)); } void MapImpl::setReplay(bool b) { d->m_isReplay = b; } bool MapImpl::isReplay() const { return d->m_isReplay; } bool MapImpl::isExerciseTimeStringVisible() const { if (! d->m_exerciseTimeLabel) return false; return d->m_exerciseTimeLabel->isVisible(); } void MapImpl::showExerciseTimeString(bool visible) { if (! d->m_exerciseTimeLabel) return; bool changed = d->m_exerciseTimeLabel->isVisible() != visible; d->m_exerciseTimeLabel->setVisible(visible); if (changed) { addMapWidget(); emitVisualComponentChanged(VisualComponent::ExerciseTimeString); } } void MapImpl::setExerciseTimeStringColor(const QColor &foregroundColor, const QColor &backgroundColor) { d->m_exerciseTimeLabelForegroundColor = foregroundColor; d->m_exerciseTimeLabelBackgroundColor = backgroundColor; if (! d->m_exerciseTimeLabel) return; QLabel *label = d->m_exerciseTimeLabel; if (label) { QPalette palette = label->palette(); if (d->m_exerciseTimeLabelForegroundColor.isValid()) palette.setColor(label->foregroundRole(), d->m_exerciseTimeLabelForegroundColor); if (d->m_exerciseTimeLabelBackgroundColor.isValid()) palette.setColor(label->backgroundRole(), d->m_exerciseTimeLabelBackgroundColor); label->setPalette(palette); } } bool SituationMap::MapImpl::isTimeStringVisible() const { if (! d->m_timeLabel) return false; return d->m_timeLabel->isVisible(); } void MapImpl::setTimeString(const QString &timeString) { if (! d->m_timeLabel) return; d->m_timeLabel->setText(timeString); } void SituationMap::MapImpl::showTimeString(bool visible) { if (d->m_destroying) return; if (!d->m_timeLabel) return; bool changed = d->m_timeLabel->isVisible() != visible; d->m_timeLabel->setVisible(visible); if (changed) { addMapWidget(); emitVisualComponentChanged(VisualComponent::TimeString); } } void SituationMap::MapImpl::setTimeStringColor(const QColor &foregroundColor, const QColor &backgroundColor) { if (d->m_destroying) return; d->m_timeLabelForegroundColor = foregroundColor; d->m_timeLabelBackgroundColor = backgroundColor; if (! d->m_timeLabel) return; QLabel *label = d->m_timeLabel; if (label) { QPalette palette = label->palette(); if (d->m_timeLabelForegroundColor.isValid()) palette.setColor(label->foregroundRole(), d->m_timeLabelForegroundColor); if (d->m_timeLabelBackgroundColor.isValid()) palette.setColor(label->backgroundRole(), d->m_timeLabelBackgroundColor); label->setPalette(palette); } } bool SituationMap::MapImpl::isCompassVisible() const { if (! d->m_compass) return false; return d->m_compass->isVisible(); } void MapImpl::showCompass(bool visible) { if (d->m_destroying) return; if (!d->m_compass) return; bool changed = d->m_compass->isVisible() != visible; d->m_compass->setVisible(visible); if (changed) emitVisualComponentChanged(VisualComponent::CompassRose); } void MapImpl::addSideBar() { if (d->m_destroying) return; TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (d->m_sideBar) return; // Create SideBar: d->m_sideBar = QPointer(new SideBar()); // no parent, will be embedded to StackedItem taking ownership d->m_sideBar->setAnimationDuration(500); d->m_sideBar->setMinimumWidth(350); d->m_sideBar->setMaximumWidth(d->m_sideBar->minimumWidth()); d->m_sideBar->setMinimumHeight(300); int width = d->m_sideBar->minimumWidth(); d->m_sideBar->setVisiblePosition(QPoint(0, 0)); d->m_sideBar->setInvisiblePosition(QPoint(-width, 0)); // Embed (and take ownership) if (d->m_stackedLayout) { int idx = d->m_stackedLayout->addWidget(d->m_sideBar); // takes ownership d->m_stackedLayout->setCurrentIndex(idx); } // Visibility: d->m_sideBar->hide(); //---------------------------------------------- // add Entries: //---------------------------------------------- SituationMap::SideBarItem *item; // Show Grid item = new SituationMap::SideBarItem(tr("Show Grid")); item->setEnabled(this->mode() == Mode::Mode_2D); item->setCheckState((this->grid().isVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); d->m_sideBar->addCheckableItem(item); connect(item, &SituationMap::SideBarItem::changed, this, [this](bool onOff) { this->grid().setVisible(onOff); }); connect(this, &SituationMap::Map::visualComponentChanged, this, [this, item]() { item->setCheckState((this->grid().isVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); }); // Show Position String item = new SituationMap::SideBarItem(tr("Show Position String")); item->setCheckState((this->isPositionStringVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); d->m_sideBar->addCheckableItem(item); connect(item, &SituationMap::SideBarItem::changed, this, [this](bool onOff) { this->showPositionString(onOff); }); connect(this, &SituationMap::Map::visualComponentChanged, this, [this, item]() { item->setCheckState((this->isPositionStringVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); }); // Show Exercise Time String // // if(isReplay()) // GUEZE-2921 'Anpassen der Uhrzeit auf Nutzerbasis (Checkbox)' // Mit GUEZE-2921 ist es immer verfügbar. // Muss es vielleicht über ein projectSetting gesteuert werden? // Gilt es wirklich für alle Apps? { item = new SituationMap::SideBarItem(tr("Show Exercise Time String")); item->setCheckState((this->isExerciseTimeStringVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); d->m_sideBar->addCheckableItem(item); connect(item, &SituationMap::SideBarItem::changed, this, [this](bool onOff) { this->showExerciseTimeString(onOff); }); connect(this, &SituationMap::Map::visualComponentChanged, this, [this, item]() { item->setCheckState((this->isExerciseTimeStringVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); }); } // Show Time String // item = new SituationMap::SideBarItem(tr("Show Time String")); // item->setCheckState((this->isTimeStringVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); // d->m_sideBar->addCheckableItem(item); // connect(item, &SituationMap::SideBarItem::changed, this, [this](bool onOff) // { // this->showTimeString(onOff); // }); // connect(this, &SituationMap::Map::visualComponentChanged, this, [this, item]() // { // item->setCheckState((this->isTimeStringVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); // }); // Show Compass Rose item = new SituationMap::SideBarItem(tr("Show Compass Rose")); item->setCheckState((this->isCompassVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); d->m_sideBar->addCheckableItem(item); connect(item, &SituationMap::SideBarItem::changed, this, [this](bool onOff) { this->showCompass(onOff); }); connect(this, &SituationMap::Map::visualComponentChanged, this, [this, item]() { item->setCheckState((this->isCompassVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); }); // Show Overview Map item = new SituationMap::SideBarItem(tr("Show Overview Map")); OverviewMapHandler &omHdl = overviewMapHandler(); item->setCheckState((omHdl.builtInOverviewMapWidget()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); d->m_sideBar->addCheckableItem(item); connect(item, &SituationMap::SideBarItem::changed, this, [this](bool onOff) { OverviewMapHandler &omHdl = overviewMapHandler(); if (onOff) omHdl.createBuiltInOverviewMap(); else omHdl.removeBuiltInOverviewMap(); }); connect(this, &SituationMap::Map::visualComponentChanged, this, [this, item]() { OverviewMapHandler &omHdl = overviewMapHandler(); item->setCheckState((omHdl.builtInOverviewMapWidget()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); }); // could be continue like: // item = new SituationMap::SideBarItem(tr("Scale Bar (tbd)")); // item->setCheckState(Qt::CheckState::Checked); // d->m_sideBar->addCheckableItem(item); // item = new SituationMap::SideBarItem(tr("Distance Measurement (tbd)")); // item->setCheckState(Qt::CheckState::Checked); // d->m_sideBar->addCheckableItem(item); // item = new SituationMap::SideBarItem(tr("Terrain Profile (tbd)")); // item->setCheckState(Qt::CheckState::Checked); // d->m_sideBar->addCheckableItem(item); // item = new SituationMap::SideBarItem(tr("Viewshed (tbd)")); // item->setCheckState(Qt::CheckState::Checked); // d->m_sideBar->addCheckableItem(item); // Rocket QPushButton *pbHome = new QPushButton(tr("Home")); pbHome->setIcon(QIcon(QLatin1String("icon:go-home.svg"))); d->m_sideBar->addWidget(pbHome); connect(pbHome, &QPushButton::clicked, this, [this]() { this->zoomToArea(d->m_trainingsAreaExtent, 50); }); // Rocket QPushButton *pbRocket = new QPushButton(tr("Rocket")); pbRocket->setIcon(QIcon(QLatin1String("icon:rocket.svg"))); d->m_sideBar->addWidget(pbRocket); connect(pbRocket, &QPushButton::clicked, this, &MapImpl::startRocket); // JumpPointWidget: const QString jpLayername = this->layerFactoryImpl().jumpPointLayer(); d->m_jumpPointWidget = new JumpPointWidget(jpLayername, this, QLatin1String("myJumpPointWidget")); d->m_sideBar->addWidget(d->m_jumpPointWidget); // temporaery : if (0) { if (this->isReady()) { d->m_jumpPointWidget->addJumpPoint(QLatin1String("JP_BuildingDemo111"), QPointF(0.155275, 0.925975), 1000); } else { connect(this, &Map::mapReady, this, [this]() { d->m_jumpPointWidget->addJumpPoint(QLatin1String("JP_BuildingDemo222"), QPointF(0.155275, 0.925975), 1000); }); } } // : temporaery // Add Button for SideBar/BurgerMenue { if (!d->m_pbSideBar) { MyPushButton *pb = new MyPushButton(); // parent = 0 -> toplevelWidget //const QColor &base = this->palette().color(QPalette::Base); // qt::white QColor rdeColor = palette().color(QPalette::WindowText); // RDE_DARK_BLUE pb->setFlat(true); pb->setIconSize(QSize(36, 36)); pb->setFixedSize(pb->iconSize() * 1.2); pb->setIconOnHover(SideBar::createIcon(rdeColor, QColor(), 100)); rdeColor.setAlphaF(0.7); pb->setIconPale(SideBar::createIcon(rdeColor, QColor(), 100)); d->m_pbSideBar = pb; } QPushButton *pbSideBar = d->m_pbSideBar; // pbSideBar->setText(QLatin1String("MainMenue")); pbSideBar->setObjectName(QLatin1String("QPushButton-MainMenue")); // add button // Bug iOSQz-000005 'QGraphicsProxyWidget will crash' // Adding a widget will let the application crash // Workaround: 'proxy->setOpacity(0.99);' switch (mode()) { case Mode::Mode_2D: { QGraphicsProxyWidget *proxy = d->m_mapGraphicsView->scene()->addWidget(pbSideBar); proxy->setOpacity(0.99); } break; case Mode::Mode_3D: { QGraphicsProxyWidget *proxy = d->m_sceneGraphicsView->QGraphicsView::scene()->addWidget(pbSideBar); proxy->setOpacity(0.99); } break; case Mode::Undefined: // empty break; } // switch pbSideBar->move(25, 25); // connect(pbMainMenue, &QPushButton::clicked, this, &SitMap::onShowSideBar); connect(pbSideBar, &QPushButton::clicked, this, [&]() { if (QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier) && QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) && QGuiApplication::keyboardModifiers().testFlag(Qt::AltModifier)) { // 'debug' behaviour: toDumpString std::string localLoggerName = m_logger.getLoggerName() + ".dump"; LOGGER_T logger = m_logger.getNGLLogger(localLoggerName); NOTICE_LOG(logger) << "START DUMP --- objectName: " << this->objectName(); const QString &dumpStr = this->toDumpString(); NOTICE_LOG(logger) << dumpStr; NOTICE_LOG(logger) << "START END --- objectName: " << this->objectName(); } else { // typical behaviour: d->m_sideBar->showAnimated(); } }); // QDialog * d = new QDialog; // d->setLayout(new QHBoxLayout); // d->layout()->addWidget(pbSideBar); // d->setMinimumSize(QSize(500, 500)); // d->exec(); // enable/disable button depending on mapReady if (this->isReady()) { pbSideBar->setStyleSheet(QLatin1String("QPushButton {background-color: transparent; border: 1px solid transparent;}")); pbSideBar->setEnabled(true); } else { pbSideBar->setStyleSheet(QLatin1String("QPushButton {background-color: red; border: 1px solid transparent;}")); pbSideBar->setEnabled(false); connect(this, &Map::mapReady, this, [pbSideBar]() { pbSideBar->setStyleSheet(QLatin1String("QPushButton {background-color: transparent; border: 1px solid transparent;}")); pbSideBar->setEnabled(true); // pbSideBar->setStyleSheet(QLatin1String("QPushButton {background-color: transparent; border: 1px solid transparent;} QPushButton:hover {background-color: white; border: 1px solid transparent;}")); // setStyleSheet(QLatin1String("QTabBar::close-button{ image: url(\"icon:dialog-close.svg\"); }")); }); } } TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } void MapImpl::removeSideBar() { delete d->m_sideBar; } QPointer MapImpl::sideBar() { return d->m_sideBar; } bool MapImpl::applyLicense(const QString &licenseStr, const QStringList &extensions) { LOGGER_T logger = getLogger("MapImpl"); static int licensed = -1; INFO_LOG(logger) << LOGFUNCTION << " licenseStr = '" << licenseStr << "'"; for (const auto &ex : extensions) INFO_LOG(logger) << LOGFUNCTION << " extension = '" << ex << "'"; if (licensed != -1) { INFO_LOG(logger) << LOGFUNCTION << " already licensed" << " (licensed=" << licensed << ")"; return licensed == 1; } connect(Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::instance(), &Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::errorOccurred, [](const Esri::ArcGISRuntime::Error & error) { std::cerr << " EsriError occured: " << error << std::endl; LOGGER_T logger = getLogger("MapImpl"); ERROR_LOG(logger) << LOGFUNCTION << " EsriError occured: " << error; }); // 1. setLicense Esri::ArcGISRuntime::LicenseResult licResult; if (extensions.isEmpty()) licResult = Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setLicense(licenseStr); else licResult = Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setLicense(licenseStr, extensions); // 2. initialize if (! Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::initialize()) FATAL_LOG(logger) << LOGFUNCTION << " ArcGISRuntimeEnvironment::initialize failed"; DEBUG_LOG(logger) << LOGFUNCTION << " licResult=" << (int)licResult.licenseStatus(); // Invalid = 0, Expired = 2, LoginRequired = 3, Valid = 4 std::ostringstream msg; msg << " licenseStatus = " << licResult.licenseStatus() << " licenseStr='" << licenseStr.toStdString() << "'"; for (const QString &extStr : qAsConst(extensions)) msg << " extensionsStr= '" << extStr << "'"; if (licResult.licenseStatus() == Esri::ArcGISRuntime::LicenseStatus::Valid) { licensed = 1; INFO_LOG(logger) << LOGFUNCTION << " success: " << msg.str(); } else { licensed = 0; FATAL_LOG(logger) << LOGFUNCTION << " Error: " << msg.str(); } return licResult.licenseStatus() == Esri::ArcGISRuntime::LicenseStatus::Valid; } bool MapImpl::applyNetworkCacheSettings(const EsriNetworkCacheSettings &esriNetworkCacheSettings) { LOGGER_T logger = getLogger("MapImpl"); INFO_LOG(logger) << LOGFUNCTION << " EsriNetworkCacheSettings = " << esriNetworkCacheSettings; #if ACRGIS_VERSION < 0x640700 // 100.7.0 / Update 7 WARNING_LOG(logger) << LOGFUNCTION << " EsriNetworkCacheSettings not supported yet"; #else # ifdef HAS_NETWORK_CACHE_CONFIGURATION // 100.15 Esri::ArcGISRuntime::NetworkCacheConfiguration *globNetCacheConfig = Esri::ArcGISRuntime::RequestConfiguration::globalNetworkCacheConfiguration(); if (!globNetCacheConfig) { WARNING_LOG(logger) << LOGFUNCTION << " globalNetworkCacheConfiguration failed"; return false; } // V100.8: // setNetworkCachingEnabled -> NetworkCacheConfiguration // V100.15: default CachePolicy is 'PreferNetwork' INFO_LOG(logger) << LOGFUNCTION << " globalNetworkCacheConfiguration: clearCache... '" << globNetCacheConfig->cacheDirectory() << "'"; globNetCacheConfig->clearCacheAsync(); // clearCacheAsync - since 100.15.0 INFO_LOG(logger) << LOGFUNCTION << " globalNetworkCacheConfiguration: setNetworkCachingEnabledAsync... to " << globNetCacheConfig->networkCachingEnabled(); globNetCacheConfig->setNetworkCachingEnabledAsync(esriNetworkCacheSettings.enabled); // setNetworkCachingEnabledAsync - since 100.15.0 NOTICE_LOG(logger) << LOGFUNCTION << " globalNetworkCacheConfiguration: " << globNetCacheConfig->cachePolicy() << ", cacheDir='" << globNetCacheConfig->cacheDirectory() << "'" << ", cacheSize=" << globNetCacheConfig->cacheSize(); # else Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setNetworkCachingEnabled(esriNetworkCacheSettings.enabled); # endif // HAS_NETWORK_CACHE_CONFIGURATION #endif // ACRGIS_VERSION return true; } EsriNetworkCacheSettings MapImpl::networkCacheSettings() { EsriNetworkCacheSettings encs; #if ACRGIS_VERSION < 0x640700 // 100.7.0 / Update 7 LOGGER_T logger = getLogger("MapImpl"); WARNING_LOG(logger) << LOGFUNCTION << " EsriNetworkCacheSettings not supported yet"; #else # ifdef HAS_NETWORK_CACHE_CONFIGURATION // 100.15 LOGGER_T logger = getLogger("MapImpl"); Esri::ArcGISRuntime::NetworkCacheConfiguration *globNetCacheConfig = Esri::ArcGISRuntime::RequestConfiguration::globalNetworkCacheConfiguration(); if (globNetCacheConfig) { encs.enabled = globNetCacheConfig->networkCachingEnabled(); } else { WARNING_LOG(logger) << LOGFUNCTION << " globalNetworkCacheConfiguration failed"; } # else encs.enabled = Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::networkCachingEnabled(); # endif // HAS_NETWORK_CACHE_CONFIGURATION #endif // #if ACRGIS_VERSION < 0x640700 return encs; } Esri::ArcGISRuntime::Map *MapImpl::getEsriMap() const { return d->m_esriMap; } Esri::ArcGISRuntime::Basemap *MapImpl::getEsriBasemap() const { return d->m_basemap; } bool MapImpl::initElevationMapManager(const QStringList &filePaths_rough_to_fine) { INFO_LOG(m_logger) << LOGFUNCTION << " read elevation data file \'" << filePaths_rough_to_fine.join(QStringLiteral(" ; ")) << "\' ..."; QString fileCombinationKey; // name of TrainingArea would be helpful :-) for (const QString &filePath : filePaths_rough_to_fine) fileCombinationKey.append(QFileInfo(filePath).fileName() + QStringLiteral("#")); static QHash> s_elevationMapManagers; auto it = s_elevationMapManagers.find(fileCombinationKey); if (it != s_elevationMapManagers.end()) { d->m_elevationMapManager = it.value().lock(); if (d->m_elevationMapManager) return true; } d->m_elevationMapManager = std::make_shared(); d->m_elevationMapManager->init(filePaths_rough_to_fine); INFO_LOG(m_logger) << LOGFUNCTION << " read elevation data file \'" << filePaths_rough_to_fine.join(QStringLiteral(" ; ")) << "\' ... done"; return true; } std::shared_ptr MapImpl::getElevationMapManager() const { if (d->m_elevationMapManager && !d->m_elevationMapManager->isValid()) return nullptr; return d->m_elevationMapManager; } Esri::ArcGISRuntime::SpatialReference MapImpl::getEsriSpatialReference() const { if (d->m_esriMap) return d->m_esriMap->spatialReference(); if (d->m_esriScene) return d->m_esriScene->spatialReference(); return Esri::ArcGISRuntime::SpatialReference(); } Esri::ArcGISRuntime::Scene *MapImpl::getEsriScene() const { if (!d->m_esriScene) return nullptr; return d->m_esriScene; } Esri::ArcGISRuntime::MapGraphicsView *MapImpl::getEsriMapGraphicsView() const { return d->m_mapGraphicsView; } Esri::ArcGISRuntime::SceneGraphicsView *MapImpl::getEsriSceneGraphicsView() const { return d->m_sceneGraphicsView; } QGraphicsView *MapImpl::getEsriQGraphicsView() const { if (d->m_mapGraphicsView) return d->m_mapGraphicsView; if (d->m_sceneGraphicsView) return d->m_sceneGraphicsView; return nullptr; } Esri::ArcGISRuntime::GeoView *MapImpl::getEsriGeoView() const { if (d->m_mapGraphicsView && d->m_sceneGraphicsView) { FATAL_LOG(m_logger) << LOGFUNCTION << " Upps, 2D(Map) and also 3D (Scene) -> error!!! "; FATAL_LOG(m_logger) << LOGFUNCTION << " Upps, 2D(Map) and also 3D (Scene) -> error!!! "; FATAL_LOG(m_logger) << LOGFUNCTION << " Upps, 2D(Map) and also 3D (Scene) -> error!!! "; } if (d->m_mapGraphicsView) return d->m_mapGraphicsView; if (d->m_sceneGraphicsView) return d->m_sceneGraphicsView; return nullptr; } QString MapImpl::toDumpString() const { std::stringstream myLine; Esri::ArcGISRuntime::Map *esriMap = d->m_esriMap; Esri::ArcGISRuntime::MapGraphicsView *esriGraphicsMapView = d->m_mapGraphicsView; Esri::ArcGISRuntime::Scene *esriScene = d->m_esriScene; // Esri::ArcGISRuntime::SceneGraphicsView *esriGraphicsSceneView = d->m_sceneGraphicsView; Esri::ArcGISRuntime::GeoView *geoView = this->getEsriGeoView(); if (! geoView) return QString(); int graphicsOverlayCount = -1; if (geoView->graphicsOverlays()) graphicsOverlayCount = geoView->graphicsOverlays()->size(); int basemapCount = -1; if (this->getEsriBasemap() && this->getEsriBasemap()->baseLayers()) basemapCount = this->getEsriBasemap()->baseLayers()->size(); QRectF curExtent = this->extent(); QRectF fullExtent; // = this->fullExtent(); // Esri::ArcGISRuntime::Envelope env = esriMap->fullExtent(); // QPointF fullExtentMin(env.xMin(), env.yMin()); // QPointF fullExtentMax(env.xMax(), env.yMax()); // QPointF fullExtentCenter(env.center().x(), env.center().y()); // header { myLine << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX" << std::endl; // myLine << " animationMode \t\t= " << static_cast(esriMap->animationMode()) << std::endl; // myLine << " animationDuration \t= " << esriMap->animationDuration() << std::endl; myLine << " current wkid \t\t= " << this->getEsriSpatialReference().wkid() << std::endl; myLine << " wkText \t='" << this->getEsriSpatialReference().wkText() << "'" << std::endl; myLine << " toJson='" << this->getEsriSpatialReference().toJson() << "'" << std::endl; if (esriMap) myLine << " Map::isAutoFetchLegendInfos='" << esriMap->isAutoFetchLegendInfos() << std::endl; if (esriScene) myLine << " Scene::isAutoFetchLegendInfos='" << esriScene->isAutoFetchLegendInfos() << std::endl; myLine << " dict isLoaded \t\t= " << this->symbolDictionary().isLoaded() << std::endl; myLine << " dict extraSymbolCount \t= " << this->symbolDictionary().etsSymbolCount() << std::endl; myLine << " graphicsOverlayCount \t= " << graphicsOverlayCount << std::endl; myLine << " basemapCount \t\t= " << basemapCount << std::endl; myLine << " curExtent \t\t= " << "center=" << curExtent.center() << " ; w=" << curExtent.width() << " h=" << curExtent.height() << std::endl; myLine << " fullExtent \t\t= " << "center=" << fullExtent.center() << " ; w=" << fullExtent.width() << " h=" << fullExtent.height() << std::endl; // myLine << " (fullExtent[esri] \t= " << "min=" << fullExtentMin << " ; max=" << fullExtentMax << ")" << std::endl; // myLine << " (fullExtent[esri] \t= " << "center=" << fullExtentCenter // << " ; w=" << env.width() << " h=" << env.height() << ")" << std::endl; if (esriMap) { myLine << " minScale \t\t\t= " << esriMap->minScale() << std::endl; myLine << " maxScale \t\t\t= " << esriMap->maxScale() << std::endl; } myLine << " scale(SitMap) \t\t= " << this->scale() << std::endl; myLine << " rotation(SitMap) \t\t= " << this->rotation() << " [deg]" << std::endl; if (esriMap) myLine << " MapView::rotation \t\t= " << esriGraphicsMapView->mapRotation() << " [deg]" << std::endl; if (esriScene) myLine << " SceneView::rotation \t\t= " << "-" << std::endl; myLine << " viewpoint \t\t\t= " << this->viewpoint2D() << std::endl; if (d->m_mapGraphicsView) myLine << " MapView::viewpoint \t\t= " << d->m_mapGraphicsView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale) << std::endl; if (d->m_sceneGraphicsView) { myLine << " Camera(SitMap) \t\t= " << this->camera() << std::endl; myLine << " SceneView::viewpoint \t\t= " << d->m_sceneGraphicsView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale) << std::endl; myLine << " SceneView::camera \t\t= " << d->m_sceneGraphicsView->currentViewpointCamera() << std::endl; myLine << "\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" << std::endl; myLine << " OperationalLayers:"; Esri::ArcGISRuntime::LayerListModel *llm = nullptr; if (esriScene) llm = esriScene->operationalLayers(); if (llm) { myLine << " - count=" << llm->size(); myLine << "\nLayer \tVisi \tOpac \tSts \t\tType \t\t\t\tName\n"; for (int i = 0; i < llm->size(); ++i) { Esri::ArcGISRuntime::Layer *layer = llm->at(i); if (!layer) continue; myLine << " " << i << "\t" << layer->isVisible() << "\t" << layer->opacity() << "\t" << layer->loadStatus() << "\t" << layer->layerType() << "\t" << layer->name() << " (minScale=" << layer->minScale() << " ; maxScale=" << layer->maxScale() << ")" << " (attribution='" << layer->attribution() << "'')" << "\t description=" << layer->description() << "\t isShowInLegend=" << layer->isShowInLegend(); { Esri::ArcGISRuntime::ArcGISSceneLayer *sceneLayer = dynamic_cast(layer); if (sceneLayer) myLine << "\n\t\t\t\t\t url:" << sceneLayer->url().toString(); } myLine << std::endl; } // for } } } myLine << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX" << std::endl; AdvancedSymbology::SymbolDictionary &dict = this->symbolDictionary(); AdvancedSymbology::SymbolDictionaryImpl &dictImpl = static_cast(dict); myLine << " symbologyFieldNames: " << dictImpl.symbologyFieldNamesInternal().join(QLatin1String(" ; ")) << std::endl; myLine << " textFieldNames: " << dictImpl.textFieldNamesInternal().join(QLatin1String(" ; ")) << std::endl; myLine << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX" << std::endl; if (basemapCount > 0) { myLine << "basemapCount=" << basemapCount << std::endl; myLine << "\nLayer \tVisi \tOpac \tSts \t\tType \t\t\t\tName" << std::endl; const Esri::ArcGISRuntime::LayerListModel *model = this->getEsriBasemap()->baseLayers(); for (int i = 0; i < model->size(); ++i) { /*const*/ Esri::ArcGISRuntime::Layer *layer = model->at(i); myLine << " " << i << "\t" << layer->isVisible() << "\t" << layer->opacity() << "\t" << layer->loadStatus() << "\t" << layer->layerType() << "\t" << layer->name() << " (minScale=" << layer->minScale() << " ; maxScale=" << layer->maxScale() << ")" << " (attribution='" << layer->attribution() << "'')" << "\t description=" << layer->description() << "\t isShowInLegend=" << layer->isShowInLegend(); { Esri::ArcGISRuntime::ImageAdjustmentLayer *iaLayer = dynamic_cast(layer); if (iaLayer) { myLine << "\t b:" << iaLayer->brightness() << "\t g:" << iaLayer->gamma() << "\t c:" << iaLayer->contrast(); } } myLine << std::endl; QString url(QLatin1String("no url availble")); Esri::ArcGISRuntime::ArcGISTiledLayer *tiledLayer = dynamic_cast(layer); if (tiledLayer) url = tiledLayer->mapServiceInfo().url().toString(); myLine << "url= '" << url << "'"; { Esri::ArcGISRuntime::LegendInfoListModel *lilm = layer->legendInfos(); int lSize = -1; if (lilm) lSize = lilm->size(); myLine << std::endl; myLine << "LegendInfoListModelCount=" << lSize << std::endl; for (int j = 0; j < lSize; ++j) { Esri::ArcGISRuntime::LegendInfo *li = lilm->at(j); myLine << " " << j << "\t" << li->name() << "\t" << (int)li->symbol()->symbolType(); } } myLine << std::endl; } // for } // if (basemapCount > 0) // // GraphicsOverlays // myLine << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; int objCounter = 0; QVector> gOverlayCounter; if (graphicsOverlayCount) { myLine << std::endl; myLine << "graphicsOverlayCount=" << graphicsOverlayCount << std::endl; myLine << "i \tVisi \tOpac \tGRM \t\t\tSP \t\t\t#Obj \tName" << std::endl; Esri::ArcGISRuntime::GraphicsOverlayListModel *gOverlayListModel = getEsriGeoView()->graphicsOverlays(); const int gOverlayCount = gOverlayListModel->size(); for (int esriIdx=0; esriIdxat(esriIdx); if (!gOverlay) continue; const QString &name = gOverlay->overlayId(); // since 100.2.0 if (gOverlay) { int i = 0; int objCountPerLayer = -1; if (gOverlay->graphics()) objCountPerLayer = gOverlay->graphics()->size(); objCounter += objCountPerLayer; gOverlayCounter.append(QPair(name, objCountPerLayer)); int esriIdx = gOverlayListModel ? gOverlayListModel->indexOf(gOverlay) : -1; Esri::ArcGISRuntime::LayerSceneProperties sceneProperties = gOverlay->sceneProperties(); std::string typeName; Esri::ArcGISRuntime::Renderer *renderer = gOverlay->renderer(); if (renderer) typeName = typeid(*renderer).name(); QString objName; if (renderer) objName = renderer->objectName(); myLine << " " << i++ << "\t" << gOverlay->isVisible() << "\t" << gOverlay->opacity() << "\t" << gOverlay->renderingMode() << " " << "\t" << sceneProperties.surfacePlacement() << " " << "\t" << objCountPerLayer << "\t (" << esriIdx << ")" << "\t" << name << " (minScale=" << gOverlay->minScale() << " ; maxScale=" << gOverlay->maxScale() << ")" << " (Renderer=" << gOverlay->renderer() << ", type_info=" << typeName << ", objName=" << objName << ")"; Esri::ArcGISRuntime::GraphicListModel *gListModel = gOverlay->graphics(); if (gListModel && !gListModel->isEmpty()) { myLine << std::endl; myLine << "objIdx" << "\tUMapId \tVisi \tPick \tSel \tzIdx \tobjectName" << std::endl; for (int idx = 0; idx < gListModel->size(); ++idx) { Esri::ArcGISRuntime::Graphic *graphic = gListModel->at(idx); if (graphic) { UniqueMapId uniqueMapId = INVALID_MAP_ID; bool isPickable = false; Esri::ArcGISRuntime::AttributeListModel *attLisModel = graphic->attributes(); if (attLisModel) { bool ok; uniqueMapId = attLisModel->attributeValue(QLatin1String(SituationMap::UniqueMapIdAttribute)).toUInt(&ok); isPickable = attLisModel->attributeValue(QLatin1String(SituationMap::IsPickableAttribute)).toBool(); } myLine << " " << idx << "\t" << uniqueMapId << "\t" << graphic->isVisible() << "\t" << isPickable << "\t" << graphic->isSelected() << "\t" << graphic->zIndex() << "\t objName " << graphic->objectName() << "\t geom " << graphic->geometry().toJson().left(100) << std::endl; } } // for } myLine << std::endl; } } // while } // if (graphicsOverlayCount) myLine << "PartyIdToColorFunctor: " << std::endl; this->partyIdToColorFunctor().toString(myLine); myLine << std::endl; myLine << "Obj per GraphicalOverlay:" << std::endl; for (const QPair &item : gOverlayCounter) myLine << " GraphicalOverlay: " << item.second << "\t" << " obj on '" << item.first << "'" << std::endl; myLine << "-------" << std::endl; myLine << "#Total: " << objCounter << std::endl; myLine << std::endl; myLine << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX" << std::endl; myLine << "count() of objectFactory/Container: " << d->m_objectFactory->count() << std::endl; myLine << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX" << std::endl; myLine << std::endl; return QString::fromStdString(myLine.str()); } bool MapImpl::isLoaded() const { if (d->m_esriMap) return d->m_esriMap->loadStatus() == Esri::ArcGISRuntime::LoadStatus::Loaded; if (d->m_esriScene) return d->m_esriScene->loadStatus() == Esri::ArcGISRuntime::LoadStatus::Loaded; return false; } QGraphicsView *MapImpl::getQGraphicsView() const { if (d->m_mapGraphicsView) return d->m_mapGraphicsView; if (d->m_sceneGraphicsView) return d->m_sceneGraphicsView; return nullptr; } double MapImpl::pixelSizeMeter() { if (d->m_pixelsize <= 0) { if (d->m_mapGraphicsView) { Esri::ArcGISRuntime::Point p1_mapCoord = d->m_mapGraphicsView->screenToLocation(0, 0); Esri::ArcGISRuntime::Point p2_mapCoord = d->m_mapGraphicsView->screenToLocation(0, 1); if (d->m_coordTranslationFunctor) { // const QPointF &dist = d->m_coordTranslationFunctor->mapToGeographic(p1_mapCoord) - d->m_coordTranslationFunctor->mapToGeographic(p2_mapCoord); const QPointF &dist = QPointF(p1_mapCoord.x(), p1_mapCoord.y()) - QPointF(p2_mapCoord.x(), p2_mapCoord.y()); d->m_pixelsize = sqrt(dist.x() * dist.x() + dist.y() * dist.y()); } else { d->m_pixelsize = Esri::ArcGISRuntime::GeometryEngine::distance(d->m_mapGraphicsView->screenToLocation(0, 0), d->m_mapGraphicsView->screenToLocation(0, 1)); } // 2019-11-13, Norbert Thoden // Sometimes (most of the time when starting a new map) the drawing of a // spline results in a simple line (vertexCount=2 assumed) // Maybe m_pixelsize is NaN? To avoid this: if (std::isnan(d->m_pixelsize)) { FATAL_LOG(m_logger) << LOGFUNCTION << " d->m_pixelsize is Nan, set to default"; #ifdef LTS_DEBUG QMessageBox::critical(nullptr, QLatin1String("pixelSizeMeter"), QLatin1String("pixelSizeMeter is NaN - abort.")); abort(); #endif // LTS_DEBUG d->m_pixelsize = 1.234567; } if (d->m_sceneGraphicsView) d->m_pixelsize = 1.2345; } } return d->m_pixelsize; } double MapImpl::pixelSizeGeographic() { if (d->m_mapGraphicsView) { Esri::ArcGISRuntime::Point p1_mapCoord = d->m_mapGraphicsView->screenToLocation(0, 0); Esri::ArcGISRuntime::Point p2_mapCoord = d->m_mapGraphicsView->screenToLocation(0, 1); if (d->m_coordTranslationFunctor) { const QPointF &dist = d->m_coordTranslationFunctor->mapToGeographic(p1_mapCoord) - d->m_coordTranslationFunctor->mapToGeographic(p2_mapCoord); return sqrt(dist.x() * dist.x() + dist.y() * dist.y()); } } return -1; } //void MapImpl::moveEvent(QMoveEvent *e) //{ // qWarning() << LOGFUNCTION << "moveEvent" << e->type(); // QWidget::moveEvent(e); //} //bool MapImpl::event(QEvent *e) //{ // qWarning() << LOGFUNCTION << "EVENT" << e->type(); // if (e->type() == QEvent::Move) // qWarning() << "MOVEE"; // return QWidget::event(e); //} //Esri::ArcGISRuntime::Layer* MapImpl::getEsriLayerByName(const QString &layerName) //{ // Esri::ArcGISRuntime::Layer* foundLayer = nullptr; // Esri::ArcGISRuntime::Map* esriMap = this->getEsriMap(); // QList layerList = esriMap->layers(); // foreach (Esri::ArcGISRuntime::Layer* layer, layerList) { // if (layer) // { // if ( layer->name() == layerName) // { // foundLayer = layer; // break; // } // } // } // return foundLayer; //} QPointF MapImpl::mapCoordinateToViewPortWindowPoint(const QPointF &mapPos) const { // don NOT delegate to mapCoordinateToViewPortWindowPoint3D(Point3D::fromQPointF(mapPos)); // In that case, the z-value is set to zero. That is different to this method. // Here the z-value is not taken into account: if (!isReady()) return QPointF(); if (d->m_mapGraphicsView) { Esri::ArcGISRuntime::Point esriCoordinate(mapPos.x(), mapPos.y(), d->m_mapGraphicsView->spatialReference()); const QPointF &screenCoordinate = d->m_mapGraphicsView->locationToScreen(esriCoordinate); return screenCoordinate; } else if (d->m_sceneGraphicsView) { Esri::ArcGISRuntime::Point esriCoordinate(mapPos.x(), mapPos.y(), d->m_sceneGraphicsView->spatialReference()); const Esri::ArcGISRuntime::LocationToScreenResult &locToSrnRes = d->m_sceneGraphicsView->locationToScreen(esriCoordinate); switch (locToSrnRes.visibility()) { case Esri::ArcGISRuntime::SceneLocationVisibility::Visible: case Esri::ArcGISRuntime::SceneLocationVisibility::HiddenByBaseSurface: case Esri::ArcGISRuntime::SceneLocationVisibility::HiddenByEarth: case Esri::ArcGISRuntime::SceneLocationVisibility::HiddenByElevation: { const QPointF &screenCoordinate = locToSrnRes.screenPoint(); DEBUG_LOG(m_logger) << LOGFUNCTION << " screenCoordinate=" << screenCoordinate << " locToSrnRes.visibility()=" << locToSrnRes.visibility(); return screenCoordinate; } break; case Esri::ArcGISRuntime::SceneLocationVisibility::NotOnScreen: DEBUG_LOG(m_logger) << LOGFUNCTION << " locToSrnRes.visibility()=" << locToSrnRes.visibility() << ", locToSrnRes.screenPoint()=" << locToSrnRes.screenPoint(); } // switch } return QPointF(); } QPointF MapImpl::mapCoordinateToViewPortWindowPoint3D(const Point3D &mapPos) const { if (!isReady()) return QPointF(); if (d->m_mapGraphicsView) { Esri::ArcGISRuntime::Point esriCoordinate(mapPos.x(), mapPos.y(), mapPos.z(), d->m_mapGraphicsView->spatialReference()); const QPointF &screenCoordinate = d->m_mapGraphicsView->locationToScreen(esriCoordinate); return screenCoordinate; } else if (d->m_sceneGraphicsView) { Esri::ArcGISRuntime::Point esriCoordinate(mapPos.x(), mapPos.y(), mapPos.z(), d->m_sceneGraphicsView->spatialReference()); const Esri::ArcGISRuntime::LocationToScreenResult &locToSrnRes = d->m_sceneGraphicsView->locationToScreen(esriCoordinate); switch (locToSrnRes.visibility()) { case Esri::ArcGISRuntime::SceneLocationVisibility::Visible: case Esri::ArcGISRuntime::SceneLocationVisibility::HiddenByBaseSurface: case Esri::ArcGISRuntime::SceneLocationVisibility::HiddenByEarth: case Esri::ArcGISRuntime::SceneLocationVisibility::HiddenByElevation: { const QPointF &screenCoordinate = locToSrnRes.screenPoint(); DEBUG_LOG(m_logger) << LOGFUNCTION << " screenCoordinate=" << screenCoordinate << " locToSrnRes.visibility()=" << locToSrnRes.visibility(); return screenCoordinate; } break; case Esri::ArcGISRuntime::SceneLocationVisibility::NotOnScreen: DEBUG_LOG(m_logger) << LOGFUNCTION << " locToSrnRes.visibility()=" << locToSrnRes.visibility() << ", locToSrnRes.screenPoint()=" << locToSrnRes.screenPoint(); break; } // switch } return QPointF(); } QPointF MapImpl::viewPortWindowPointToMapCoordinate(const QPointF &pixelCoord) const { if (!isReady()) return QPointF(); if (d->m_mapGraphicsView) { const Esri::ArcGISRuntime::Point &mapCoordinate = d->m_mapGraphicsView->screenToLocation(pixelCoord.x(), pixelCoord.y()); return QPointF(mapCoordinate.x(), mapCoordinate.y()); } else if (d->m_sceneGraphicsView) { const Esri::ArcGISRuntime::Point &baseCoordinate = d->m_sceneGraphicsView->screenToBaseSurface(pixelCoord.x(), pixelCoord.y()); return QPointF(baseCoordinate.x(), baseCoordinate.y()); } return QPointF(); } Point3D MapImpl::viewPortWindowPointToMapCoordinate3D(const QPointF &pixelCoord) const { if (!isReady()) return Point3D(); if (d->m_mapGraphicsView) { const Esri::ArcGISRuntime::Point &mapCoordinate = d->m_mapGraphicsView->screenToLocation(pixelCoord.x(), pixelCoord.y()); return Point3D(mapCoordinate.x(), mapCoordinate.y(), mapCoordinate.z()); } else if (d->m_sceneGraphicsView) { const Esri::ArcGISRuntime::Point &baseCoordinate = d->m_sceneGraphicsView->screenToBaseSurface(pixelCoord.x(), pixelCoord.y()); return Point3D(baseCoordinate.x(), baseCoordinate.y(), baseCoordinate.z()); } return Point3D(); } QPointF MapImpl::geoCoordinateToViewPortWindowPoint(const QPointF &geoPos) const { if (!isReady()) return QPointF(); const SituationMap::CoordTranslationFunctor *mapCtFunctor = coordTranslationFunctor(); if (!mapCtFunctor) return QPointF(); return mapCoordinateToViewPortWindowPoint(mapCtFunctor->geographicToMap(geoPos)); } QPointF MapImpl::geoCoordinateToViewPortWindowPoint3D(const Point3D &geoPos) const { if (!isReady()) return QPointF(); const SituationMap::CoordTranslationFunctor *mapCtFunctor = coordTranslationFunctor(); if (!mapCtFunctor) return QPointF(); Point3D mapPos = mapCtFunctor->geographicToMap(geoPos); return mapCoordinateToViewPortWindowPoint3D(mapPos); } QPointF MapImpl::viewPortWindowPointToGeoCoordinate(const QPointF &pixelCoord) const { if (!isReady()) return QPointF(); const SituationMap::CoordTranslationFunctor *mapCtFunctor = coordTranslationFunctor(); if (!mapCtFunctor) return QPointF(); const QPointF mapCoordinate = viewPortWindowPointToMapCoordinate(pixelCoord); return mapCtFunctor->mapToGeographic(mapCoordinate); } Point3D MapImpl::viewPortWindowPointToGeoCoordinate3D(const QPointF &pixelCoord) const { if (!isReady()) return Point3D(); const SituationMap::CoordTranslationFunctor *mapCtFunctor = coordTranslationFunctor(); if (!mapCtFunctor) return Point3D(); const Point3D mapCoordinate = viewPortWindowPointToMapCoordinate3D(pixelCoord); return mapCtFunctor->mapToGeographic(mapCoordinate); } QString MapImpl::geoToMgrsString(const QPointF &geoPos) const { SituationMap::CoordTranslationFunctor *mapCtFunctor = coordTranslationFunctor(); if (!mapCtFunctor) return QString(); return mapToMgrsString(mapCtFunctor->geographicToMap(geoPos)); } QString MapImpl::mapToMgrsString(const QPointF &mapPos) const { // DEBUG_LOG(m_logger) << LOGFUNCTION << "mapPos=" << mapPos; const CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor(); if (! ctFunctor) return QString(); const QPointF &pointGeo = ctFunctor->mapToGeographic(mapPos); // DEBUG_LOG(m_logger) << LOGFUNCTION << " mapPos= " << mapPos << " pointGeo= " << pointGeo; return LtsCoordTrans::mgrsFromGeographic(pointGeo.x(), pointGeo.y()); } bool MapImpl::eventFilter(QObject *obj, QEvent *e) { if (!e) return false; bool filterDragAndDrop = static_cast(d->m_mapGraphicsView) != obj; if (filterDragAndDrop) { switch (e->type()) { case QEvent::DragEnter: case QEvent::DragMove: { INFO_LOG(m_logger) << LOGFUNCTION << "Drag enter event reached."; const QMimeData *mimeData = ((QDropEvent *)e)->mimeData(); if (mimeData) { for (auto &mgrPtr : qAsConst(d->m_dropEventManagerList)) { if (mimeData->hasFormat(mgrPtr->mimeFormatString())) e->accept(); } } return true; } break; case QEvent::Drop: { INFO_LOG(m_logger) << LOGFUNCTION << "Drop event reached."; const QMimeData *mimeData = ((QDropEvent *)e)->mimeData(); if (mimeData) { for (auto mgrPtr : qAsConst(d->m_dropEventManagerList)) { if (mimeData->hasFormat(mgrPtr->mimeFormatString())) mgrPtr->manageDrop(((QDropEvent *)e)->pos(), mimeData); } e->accept(); } return true; } break; default : break; } // switch } if (QEvent::KeyPress == e->type()) { QKeyEvent *keyEvent = static_cast(e); int key = keyEvent->key(); Qt::KeyboardModifiers mod = keyEvent->modifiers(); if ((Qt::Key_J == key) && (mod.testFlag(Qt::KeyboardModifier::ControlModifier))) { INFO_LOG(m_logger) << LOGFUNCTION << " rethink jumpPointRequest Modifier"; onJumpPointRequest(static_cast(obj)->mapFromGlobal(QCursor::pos())); e->accept(); return true; } } return false; } void MapImpl::onKeyPressEvent(const QKeyEvent &keyEvent) { const int key = keyEvent.key(); if (Qt::Key_Escape == key) { emit escapePressed(); } } void MapImpl::installDropEventManager(DropEventManager *demgr) { if (demgr == nullptr) return; RefPointer refDemgr(demgr); if (d->m_dropEventManagerList.contains(refDemgr)) return; d->m_dropEventManagerList += refDemgr; } // MapImpl::installDropEventManager void MapImpl::deinstallDropEventManager(DropEventManager *demgr) { if (demgr == nullptr) return; RefPointer refDemgr(demgr); if (d->m_dropEventManagerList.contains(refDemgr)) d->m_dropEventManagerList.removeAll(refDemgr); } void MapImpl::onHandleMouseMove(int windowPosX, int windowPosY) { if (d->m_destroying) return; if (!d->m_mapReadyFlag) return; if (windowPosX < 0 || windowPosY < 0) return; // 1 emit mouseWindowPositionChanged(windowPosX, windowPosY); if (!isReady()) { return; } if (mode() == Mode::Mode_2D) { // Mode_2D: const QPointF pixelCoord(windowPosX, windowPosY); const Point3D &mapCoordinate = viewPortWindowPointToMapCoordinate3D(pixelCoord); // 2 emit mouseMapPositionChanged(mapCoordinate, true); SituationMap::CoordTranslationFunctor *mapCtFunctor = coordTranslationFunctor(); if (mapCtFunctor) { if (isSignalConnected(QMetaMethod::fromSignal(&Map::mouseGeoPositionChanged))) { // 3 emit mouseGeoPositionChanged(mapCtFunctor->mapToGeographic(mapCoordinate), true); } } } else { // Mode_3D: // 'screenToLocation' is expensive. Use screenToLocationHelper: if (d->m_screenToLocationHelper) d->m_screenToLocationHelper->screenToLocation(windowPosX, windowPosY); } } // MapImpl::onHandleMouseMove bool MapImpl::isReady() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; TRACE_LOG(m_logger) << LOGFUNCTION << " END, m_mapReadyFlag=" << d->m_mapReadyFlag; return d->m_mapReadyFlag; } DrawStatus MapImpl::drawStatus() const { switch (this->mode()) { case Mode::Mode_2D: return DrawStatus::Completed; break; case Mode::Mode_3D: { if (d->m_sceneGraphicsView) return static_cast(d->m_sceneGraphicsView->drawStatus()); // save due to static_assert } break; default: // empty break; } // switch return DrawStatus::Completed; } int MapImpl::wkid() const { return this->getEsriSpatialReference().wkid(); } SituationMap::NavigationConstraint MapImpl::navigationConstraint() const { #ifdef HAS_BELOW_GROUND Esri::ArcGISRuntime::Surface* baseSurface = nullptr; if (!d->m_esriScene || !(baseSurface = d->m_esriScene->baseSurface())) return SituationMap::NavigationConstraint::Undefined; return static_cast(baseSurface->navigationConstraint()); #else return SituationMap::NavigationConstraint::Undefined; #endif // HAS_BELOW_GROUND } bool MapImpl::setNavigationConstraint(SituationMap::NavigationConstraint navigationConstraint) { if (navigationConstraint == SituationMap::NavigationConstraint::Undefined) return false; #ifdef HAS_BELOW_GROUND Esri::ArcGISRuntime::Surface* baseSurface = nullptr; if (!d->m_esriScene || !(baseSurface = d->m_esriScene->baseSurface())) return false; const SituationMap::NavigationConstraint currentNavConstraint = this->navigationConstraint(); if (currentNavConstraint != navigationConstraint) { baseSurface->setNavigationConstraint(static_cast(navigationConstraint)); // save due to static_assert emit navigationConstraintChanged(navigationConstraint); } return true; #else return true; // handle as successful operation #endif // HAS_BELOW_GROUND } float MapImpl::baseSurfaceOpacity() const { #ifdef HAS_BELOW_GROUND Esri::ArcGISRuntime::Surface* baseSurface = nullptr; if (!d->m_esriScene || !(baseSurface = d->m_esriScene->baseSurface())) return 1.0; // opac, no transparency return baseSurface->opacity(); #else return 1.0; // opac, no transparency #endif // HAS_BELOW_GROUND } bool MapImpl::setBaseSurfaceOpacity(float surfaceOpacity) { #ifdef HAS_BELOW_GROUND Esri::ArcGISRuntime::Surface* baseSurface = nullptr; if (!d->m_esriScene || !(baseSurface = d->m_esriScene->baseSurface())) return false; if (!qFuzzyCompare(baseSurface->opacity(), surfaceOpacity)) { baseSurface->setOpacity(surfaceOpacity); emit baseSurfaceOpacityChanged(baseSurface->opacity()); } return true; #else Q_UNUSED(surfaceOpacity); return true; // handle as successful operation #endif // HAS_BELOW_GROUND } OverviewMapHandler &MapImpl::overviewMapHandler() const { if (! d->m_overviewMapHandler) { MapImpl *THIS = const_cast(this); d->m_overviewMapHandler = new OverviewMapHandlerImpl(THIS); } return *d->m_overviewMapHandler; } OperatorMapSettings MapImpl::operatorMapSettings() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START" ; OperatorMapSettings oms; if (!isReady()) { ERROR_LOG(m_logger) << LOGFUNCTION << " map not ready"; return oms; } oms.mode = this->mode(); // // 1. viewpoint/camera : // switch (oms.mode) { case Mode::Mode_2D: oms.viewpoint = this->viewpoint2D(); break; case Mode::Mode_3D: oms.camera = this->camera(); // oms.viewpoint.setScale(this->scale()); // additionally save current scale (which is not in camera) oms.viewpoint = this->viewpoint2D(); break; default: // empty break; } // : viewpoint/camera // // 1a. below ground : // oms.navigationConstraint = this->navigationConstraint(); oms.baseSurfaceOpacity = this->baseSurfaceOpacity(); // : 1a. below ground // // 2. orderedLayerList : // const LayerFactory &layerFactory = this->layerFactory(); const QList &layernames = layerFactory.layernames(); for (const auto &layername : layernames) { QPair pair; pair.first = layername; pair.second = layerFactory.layerParameter(layername); oms.orderedLayers.append(pair); } // : orderedLayerList // // 3. orderedOverlayList : // const OverlayHandler &overlayHndl = this->overlayHandler(); oms.orderedOverlays = overlayHndl.overlays(); // : orderedOverlayList // // 4. some visiblities // oms.mapWidgetSettings.gridVisibility = this->grid().isVisible(); oms.mapWidgetSettings.positionStringVisibility = this->isPositionStringVisible(); oms.mapWidgetSettings.exerciseTimeStringVisibility = this->isExerciseTimeStringVisible(); oms.mapWidgetSettings.timeStringVisibility = this->isTimeStringVisible(); oms.mapWidgetSettings.compassVisibility = this->isCompassVisible(); oms.mapWidgetSettings.overviewMapVisibility = this->overviewMapHandler().builtInOverviewMapWidget() != nullptr; oms.symbolScale = this->symbolScale(); // oms.symbolOpacity = this->symbol // // 5. JumpPointWidget : // if (d->m_jumpPointWidget) { const QVector &jumpPoints = d->m_jumpPointWidget->jumpPoints(); JumpPointData jpd; for (const JPData &jpKey : jumpPoints) { jpd.key = jpKey.m_name; jpd.originGeo = jpKey.m_originGeo; jpd.mapScale = jpKey.m_mapScale; oms.jumpPoints.append(jpd); } } // : JumpPointWidget TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return oms; } bool MapImpl::restoreOperatorMapSettings(const OperatorMapSettings &oms) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!isReady()) { INFO_LOG(m_logger) << LOGFUNCTION << " map not ready"; return false; } if (!isValid(oms.mode)) { INFO_LOG(m_logger) << LOGFUNCTION << " mode no valid"; return false; } bool success = true; const bool switchingBetweenModes = isValid(this->mode()) && isValid(oms.mode) && (this->mode() != oms.mode); DEBUG_LOG(m_logger) << LOGFUNCTION << " switchingBetweenModes = " /*<< std::boolalpha*/ << switchingBetweenModes << " (this->mode()=" << this->mode() << " oms.mode=" << oms.mode << ")"; // conflict with FilterManagement::SymbolAppearanceSettings::Settings::symbolScale // so skip oms.symbolScale // this->setSymbolScale(oms.symbolScale); // // 1. viewpoint/camera : // // Possible cases: // F1 - 2D -> 2D : setViewpoint2D // F2 - 3D -> 3D : setCamera // F3 - 2D -> 3D : setViewpoint2D // F4 - 3D -> 2D : setViewpoint2D ( finalViewpoint ) switch (oms.mode) { case Mode::Mode_2D: this->setViewpoint2D(oms.viewpoint); // F1, F3 break; case Mode::Mode_3D: if (this->mode() == Mode::Mode_3D && !oms.camera.isNull()) this->setCamera(oms.camera); // F2 else this->setViewpoint2D(oms.viewpoint); // F4 break; default: // empty break; } // switch // // below ground : // this->setNavigationConstraint(oms.navigationConstraint); this->setBaseSurfaceOpacity(oms.baseSurfaceOpacity); // : below ground // // process orderedOVERLAYList *before* orderedLAYERList // // 3. orderedOverlayList : // OverlayHandler &overlayHndl = this->overlayHandler(); for (auto ovlKey : oms.orderedOverlays) { QByteArray overlayContent; QString displaynameEmpty; // empty -> logicalLayername will we shown. This name will be overwritten if overlay is loaded and updated. // GUEZE-811 3D Lage Filter werde nicht korrekt abgebaut... wenn 2D-Snap // mit SnapshotPlan auf 3D-Topic folgt... // Szenario: 2D-Snapshuss wird wiedergegeben (App Snapshot|Script) // und SitMap muss dafür von 3D auf 2D wechseln. // Dabei werden die Overlays via MapOperatorSettings zunächst vorbereitet. // Dabei wurde aber in MapImpl::restoreOperatorMapSettings der SNAPSHOT-Overlay // als herkömmlicher Overlay vorbereitet. // Beim nachfolgendem OverlayHandlerImpl::setOverlay usw. ergaben sich Fehler // wg. LayerType::SnapshotOverlay vs. LayerType::Overlay. // Mit dem FixPart1 wird in restoreOperatorMapSettings der snapShotOverlayId == -4711 // als isSnapshot==true eintragen. // Mit FixPart2 bei SnapshotDialog::showSnapshotInternal ein // setMapReadyCallback genutzt, weil die SitMap noch nicht mapReady ist. // Mit FixPart3 bei InterfaceImpl::snapshotAdjustSitMap ebenfalls ein // setMapReadyCallback genutzt, weil die SitMap noch nicht mapReady ist. // Zu FixPart1 / GUEZE-1011 // Mit GUEZE-1011 'Absturz Planeditor' hat sich ein Problem gezeigt: // Snap aufrufen, PlanEditor (auf dem Snap starten) ein TextObj anlegen... PlanEditor beenden -> Absturz // Tritt auf, wenn -4711 als isSnapshot = true eingetragen wird. Daher lassen wir dass... // const int snapShotOverlayId = -4711; // TODO: ??? // const bool isSnapshot = false; //(ovlKey == snapShotOverlayId); // GUEZE-1723 (swp_2023) // Der SnapshotOverlay wurde (teilweise, ?3D -> 2D?) bei ersten Mal nicht geladen. // Daher isSnapshot wieder auf true, // Dafür den 'Absturz Planeditor' behoben (dtor geomodel at PlanEditor::onSignalClose) // ---> Nun wird also der der SnapshotOverlay auch als Layer mit isSnapshot==true erstellt, // korrekt in der layerOrder eingetragen. Displayname wird mit dem Laden "nachgeschoben. const int snapShotOverlayId = -4711; const bool isSnapshot = (ovlKey == snapShotOverlayId); if (!overlayHndl.setOverlay(ovlKey, displaynameEmpty, overlayContent, isSnapshot)) { ERROR_LOG(m_logger) << LOGFUNCTION << " addOverlay failed, key=" << ovlKey; success = false; } } // foreach // : orderedOverlayList // // 2. orderedLayerList : // LayerFactory &layerFactory = this->layerFactory(); const QStringList &curLayernames = layerFactory.layernames(); DEBUG_LOG(m_logger) << LOGFUNCTION << " restore curLayernames = " << curLayernames.join(QStringLiteral(";")); QStringList newOrderedLayerAll; newOrderedLayerAll.reserve(oms.orderedLayers.size()); for (const auto &item : oms.orderedLayers) { const QString &layername = item.first; LayerParameter layerParameter = item.second; newOrderedLayerAll.append(layername); DEBUG_LOG(m_logger) << LOGFUNCTION << " restore orderedLayerList = " << " layername='" << layername << "'" << " layerParameter=" << layerParameter; // restore Parameter: if (curLayernames.contains(layername)) { // maybe keep previewImage, displayName, description const LayerParameter ¤tLp = layerFactory.layerParameter(layername); if (layerParameter.previewImage.isNull()) layerParameter.previewImage = currentLp.previewImage; if (layerParameter.displayName.isEmpty()) layerParameter.displayName = currentLp.displayName; if (layerParameter.description.isEmpty()) layerParameter.description = currentLp.description; // // GUEZE-2928 // Filtervoreinstellungen werden nicht übernommen // GUEZE-2928 steht aber auch im Widerspruch zu GUEZE-1711 // if (!isReplay() && (layername == layerFactory.emergencyCallLayer(true))) // { // // for Live: // layerParameter.visible = true; // layerParameter.opacity = 100; // layerParameter.minScale = -1; // layerParameter.maxScale = -1; // DEBUG_LOG(m_logger) << LOGFUNCTION << " 'fix' layerParameter due to emergencyCallLayer"; // } if (! layerFactory.setLayerParameter(layername, layerParameter)) { ERROR_LOG(m_logger) << LOGFUNCTION << " setLayerParameter failed: layername='" << layername << "'"; success = false; } } else WARNING_LOG(m_logger) << LOGFUNCTION << " layer not found: layername='" << layername << "'"; } // for // restore order : if (!newOrderedLayerAll.isEmpty()) // in case of initial/empty oms, newOrderedLayerAll is empty { if (! layerFactory.orderLayerTo(newOrderedLayerAll)) { ERROR_LOG(m_logger) << LOGFUNCTION << " orderLayerTo failed"; success = false; } } // : restore order // : orderedLayerList // // 4. some flags, visiblities etc: // this->grid().setVisible(oms.mapWidgetSettings.gridVisibility); this->showPositionString(oms.mapWidgetSettings.positionStringVisibility); this->showExerciseTimeString(oms.mapWidgetSettings.exerciseTimeStringVisibility); #ifndef TABLET_MODE this->showTimeString(oms.mapWidgetSettings.timeStringVisibility); #endif this->showCompass(oms.mapWidgetSettings.compassVisibility); if (oms.mapWidgetSettings.overviewMapVisibility) this->overviewMapHandler().createBuiltInOverviewMap(); else this->overviewMapHandler().removeBuiltInOverviewMap(); // // 5. JumpPointWidget : // if (d->m_jumpPointWidget) { d->m_jumpPointWidget->cleanup(); for (const auto &jpd : oms.jumpPoints) { d->m_jumpPointWidget->addJumpPoint(jpd.key, jpd.originGeo, jpd.mapScale); } // foreach } TRACE_LOG(m_logger) << LOGFUNCTION << " END, success = " << success; return success; } Esri::ArcGISRuntime::GraphicsOverlay *MapImpl::getGraphicsLayer(const QString &logicalName) { return this->layerFactoryImpl().getGraphicsLayer(logicalName); } Esri::ArcGISRuntime::GraphicsOverlay *MapImpl::getGraphicsLayerDict(const QString &logicalName) { return this->layerFactoryImpl().getGraphicsLayerDict(logicalName); } Esri::ArcGISRuntime::GraphicsOverlay *MapImpl::getGraphicsLayer3DExtrusion(const QString &logicalName) { return this->layerFactoryImpl().getGraphicsLayer3DExtrusion(logicalName); } Esri::ArcGISRuntime::GraphicsOverlay *MapImpl::getGraphicsLayer3D_SpAbsolute(const QString &logicalName) { return this->layerFactoryImpl().getGraphicsLayer3D_SpAbsolute(logicalName); } CoordTranslationFunctor *MapImpl::coordTranslationFunctor() const { return d->m_coordTranslationFunctor; } ObjectContainer &MapImpl::objectContainer() { return *(d->m_objectContainer.data()); } void MapImpl::setObjectContainer(QSharedPointer objectContainer) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; d->m_objectContainer = objectContainer; this->objectFactory().setObjectContainer(d->m_objectContainer); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } PartyIdToColorFunctor &MapImpl::partyIdToColorFunctor() const { return *(d->m_partyIdToColorFunctor.data()); } void MapImpl::setPartyIdToColorFunctor(QSharedPointer partyIdToColor) { d->m_partyIdToColorFunctor = partyIdToColor; // delay that signal, in rpelay it occurs more often: connect(d->m_partyIdToColorFunctor.data(), &PartyIdToColorFunctor::partyChanged, this, [this] () { d->m_partychangedDelayTimer->start(); // is restart timer if already running }); // to create UniqueValueRenderer if required: this->onPartyChanged(); } SituationMap::OverlayHandler &SituationMap::MapImpl::overlayHandler() const { if (!d->m_overlayHandler) { MapImpl *map = const_cast(this); d->m_overlayHandler = QSharedPointer (new OverlayHandlerImpl(map)); } return *(d->m_overlayHandler); } bool MapImpl::addJumpPoint(const QString &name, const Point3D &originGeo, double mapScale) { if (!d->m_jumpPointWidget) return false; return d->m_jumpPointWidget->addJumpPoint(name, originGeo, mapScale); } bool MapImpl::removeJumpPoint(const QString &name) { if (!d->m_jumpPointWidget) return false; return d->m_jumpPointWidget->removeJumpPoint(name); } QStringList MapImpl::jumpPointNames() const { if (!d->m_jumpPointWidget) return {}; return d->m_jumpPointWidget->names(); } void MapImpl::initCoordTranslationFunctor() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (! d->m_coordTranslationFunctorInitialize) d->m_coordTranslationFunctorInitialize = new CoordTranslationFunctorImpl(this); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } AdvancedSymbology::SymbolDictionary &MapImpl::symbolDictionary() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; AdvancedSymbology::SymbolDictionary &dict = AdvancedSymbology::SymbolDictionary::dictionary(d->m_symbologyType); // check mapReady and SymbolDictionary::loaded connect(&dict, &AdvancedSymbology::SymbolDictionary::loaded, this, &MapImpl::onCheckMapReadyAndSymbolDictionaryLoaded, Qt::UniqueConnection); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return dict; } bool MapImpl::isSymbolDictionaryLoaded() const { if (d->m_symbologyType == AdvancedSymbology::SymbologyType::None) return true; bool isExisting = AdvancedSymbology::SymbolDictionary::alreadyExists(d->m_symbologyType) && symbolDictionary().isLoaded(); return isExisting; } bool MapImpl::setSymbologyType(AdvancedSymbology::SymbologyType symbologyType) { DEBUG_LOG(m_logger) << LOGFUNCTION << " set SymbologyType to " << d->m_symbologyType; if (d->m_symbologyType == AdvancedSymbology::SymbologyType::None) { d->m_symbologyType = symbologyType; AdvancedSymbology::SymbolDictionary &dict = this->symbolDictionary(); dict.load(); return dict.isOk(); } else ERROR_LOG(m_logger) << LOGFUNCTION << " modification of symbologyType is not allowed"; return false; } AdvancedSymbology::SymbologyType MapImpl::symbologyType() { return d->m_symbologyType; } Viewpoint2D MapImpl::initialViewpoint() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; TRACE_LOG(m_logger) << LOGFUNCTION << " END, initialVp=" << d->m_initialViewpoint; return d->m_initialViewpoint; } void MapImpl::setInitialViewpoint(const Viewpoint2D &initialViewpoint) { TRACE_LOG(m_logger) << LOGFUNCTION << " START, initialViewpoint=" << initialViewpoint; d->m_initialViewpoint = initialViewpoint; if (d->m_initialViewpoint.scale() <= 0) { ERROR_LOG(m_logger) << LOGFUNCTION << " initialViewpoint.scale less or equal zero!!! set to 1"; d->m_initialViewpoint.setScale(1.0); } // rad to deg: QPointF centerGeo = d->m_initialViewpoint.centerGeo(); centerGeo.rx() *= LtsCoordTrans::rad2deg(); // x = lon centerGeo.ry() *= LtsCoordTrans::rad2deg(); // y = lat // lat, lon const Esri::ArcGISRuntime::Viewpoint initialVp(centerGeo.y(), centerGeo.x(), d->m_initialViewpoint.scale()); // skip rotation here switch (mode()) { case Mode::Mode_2D: d->m_esriMap->setInitialViewpoint(initialVp); break; case Mode::Mode_3D: d->m_esriScene->setInitialViewpoint(initialVp); break; default: FATAL_LOG(m_logger) << LOGFUNCTION << " mode is undefined!!!"; FATAL_LOG(m_logger) << LOGFUNCTION << " mode is undefined!!!"; FATAL_LOG(m_logger) << LOGFUNCTION << " mode is undefined!!!"; FATAL_LOG(m_logger) << LOGFUNCTION << " mode is undefined!!!"; FATAL_LOG(m_logger) << LOGFUNCTION << " mode is undefined!!!"; break; } } Viewpoint2D MapImpl::viewpoint2D() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; Viewpoint2D vp; vp.setCenterGeo(this->centerPoint()); vp.setScale(this->scale()); vp.setRotation(this->rotation()); TRACE_LOG(m_logger) << LOGFUNCTION << " END, vp=" << vp; return vp; } void MapImpl::setViewpoint2D(const Viewpoint2D &viewpoint, float durationSeconds) { this->setViewpoint2DInternal(viewpoint, false, durationSeconds); } void MapImpl::setViewpoint2DAndWait(const Viewpoint2D &viewpoint) { this->setViewpoint2DInternal(viewpoint, true, -1.0); } void MapImpl::setViewpoint2DInternal(const Viewpoint2D &viewpoint, bool andWait, float durationSeconds) { INFO_LOG(m_logger) << LOGFUNCTION << " START, vp=" << viewpoint << " andWait=" << andWait << " durationSeconds=" << durationSeconds; Esri::ArcGISRuntime::GeoView *geoview = this->getEsriGeoView(); const CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor(); if (!geoview || !ctFunctor) { ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; return; } const QPointF ¢erPointMap = ctFunctor->geographicToMap(viewpoint.centerGeo()); const Esri::ArcGISRuntime::Point esriPoint(centerPointMap.x(), centerPointMap.y(), this->getEsriSpatialReference()); if (!esriPoint.isValid()) { ERROR_LOG(m_logger) << LOGFUNCTION << " esriPoint not valid!!!" << " (viewpoint=" << viewpoint << ")"; return; } if (viewpoint.isNull()) { WARNING_LOG(m_logger) << LOGFUNCTION << " viewpoint is null -> skip!!!" << " (viewpoint=" << viewpoint << ")"; return; } // scale in Viewport: value of 0 and below may raise an exception. Try to avoid: double scale = viewpoint.scale(); if (scale <= 0) { scale = Viewpoint2D::defaultScale; ERROR_LOG(m_logger) << LOGFUNCTION << " viewpoint.scale less or equal zero!!! set to default (" << scale << ")" << " (viewpoint=" << viewpoint << ")"; } const Esri::ArcGISRuntime::Viewpoint targetVp(esriPoint, scale, viewpoint.rotation() * -1.0); // clock vs. counterClock if (andWait) geoview->setViewpointAndWait(targetVp); else { // use default (d->m_animationDuration) if not specified: if (durationSeconds < 0) durationSeconds = d->m_animationDuration; if (durationSeconds < 0) geoview->setViewpoint(targetVp); else geoview->setViewpoint(targetVp, durationSeconds); } d->m_rotationAbsoluteAngleDegrees = viewpoint.rotation(); emit rotationChanged(d->m_rotationAbsoluteAngleDegrees); TRACE_LOG(m_logger) << LOGFUNCTION << " END, vp=" << viewpoint; } QPointF MapImpl::centerPoint() const { Esri::ArcGISRuntime::GeoView *geoview = this->getEsriGeoView(); const CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor(); if (!geoview || !ctFunctor) { ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; return QPointF(); } const Esri::ArcGISRuntime::Viewpoint &curVp = geoview->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); Esri::ArcGISRuntime::Point mapCenter = static_cast(curVp.targetGeometry()); const QPointF center(mapCenter.x(), mapCenter.y()); return ctFunctor->mapToGeographic(center); } void MapImpl::setCenterPoint(const QPointF ¢er, double scale, float durationSeconds) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " center=" << center << " scale=" << scale << " durationSeconds=" << durationSeconds << " sp=" << this->getEsriSpatialReference().wkid(); Esri::ArcGISRuntime::GeoView *geoview = this->getEsriGeoView(); const CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor(); if (!geoview || !ctFunctor) { ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; return; } const QPointF ¢erPointMap = ctFunctor->geographicToMap(center); const Esri::ArcGISRuntime::Point esriPoint(centerPointMap.x(), centerPointMap.y(), this->getEsriSpatialReference()); const Esri::ArcGISRuntime::Viewpoint &curVp = geoview->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); Esri::ArcGISRuntime::Viewpoint targetVp(esriPoint, (scale < 0) ? curVp.targetScale() : scale ); // use default (d->m_animationDuration) if not specified: if (durationSeconds < 0) durationSeconds = d->m_animationDuration; if (durationSeconds < 0) geoview->setViewpoint(targetVp); else geoview->setViewpoint(targetVp, durationSeconds); if (isPositionStringVisible()) { d->m_positionStr = this->mapToMgrsString(centerPointMap); d->updatePositionText(); } } void MapImpl::zoomToArea(const QVector &coords, int paddingInDIPS) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " coords=" << coords.size(); Esri::ArcGISRuntime::GeoView *geoview = this->getEsriGeoView(); const CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor(); if (!geoview || !ctFunctor) { ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; return; } if (coords.size() < 2) { ERROR_LOG(m_logger) << LOGFUNCTION << " not enough coords (" << coords.size() << ")"; return; } Esri::ArcGISRuntime::PolygonBuilder pBuilder(geoview->spatialReference()); for (const auto & coord : coords) { QPointF mapCoord = ctFunctor->geographicToMap(coord); pBuilder.addPoint(mapCoord.x(), mapCoord.y()); } // for switch (this->mode()) { case Mode::Mode_2D: { Esri::ArcGISRuntime::MapGraphicsView *mapView = static_cast(geoview); mapView->setViewpointGeometry(pBuilder.toGeometry(), paddingInDIPS); } break; case Mode::Mode_3D: { Esri::ArcGISRuntime::SceneGraphicsView *sceneView = static_cast(geoview); Esri::ArcGISRuntime::Viewpoint esriVp(pBuilder.toGeometry()); // unfortunately padding is not supported here sceneView->setViewpoint(esriVp); } break; case Mode::Undefined: {} } // switch } void MapImpl::setTrainingsAreaExtent(const QVector &trainingsAreaExtent) { d->m_trainingsAreaExtent = trainingsAreaExtent; } QRectF MapImpl::extent() const { if (d->m_mapGraphicsView) { const Esri::ArcGISRuntime::Viewpoint &vp = d->m_mapGraphicsView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::BoundingGeometry); if (vp.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " Viewpoint is empty"; return QRect(); } const Esri::ArcGISRuntime::Envelope &env = static_cast(vp.targetGeometry()); if (std::isnan(env.width()) || std::isnan(env.height())) ERROR_LOG(m_logger) << LOGFUNCTION << " Envelope: width|height isnan"; // DEBUG_LOG(m_logger) << LOGFUNCTION // << " xMin =" << env.xMin() // << " yMin =" << env.yMin() // << " xMax =" << env.xMax() // << " yMax =" << env.yMax() // << " width =" << env.width() << " height =" << env.height(); CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor(); if (ctFunctor) { const QPointF upperLeft(ctFunctor->mapToGeographic(env.xMax(), env.yMin())); const QPointF lowerRight(ctFunctor->mapToGeographic(env.xMin(), env.yMax())); return QRectF(upperLeft, lowerRight); } else { // QRect -> left, top, width, height return QRectF(env.xMin(), env.yMin(), // use yMin, not yMax env.width(), env.height()); } } else { DEBUG_LOG(m_logger) << LOGFUNCTION << " no object"; return QRectF(); } } void MapImpl::setExtent(const QRectF &extent) { // !!! extent.bottom__.y() > extent.bottom.top__.y() !!! setExtent(extent.topLeft() .x(), extent.topLeft(). y(), extent.bottomRight().x(), extent.bottomRight().y()); } void MapImpl::setExtent(double xmin, double ymin, double xmax, double ymax) { INFO_LOG(m_logger) << LOGFUNCTION << " xmin=" << xmin << " ymin=" << ymin << " xmax=" << xmax << " ymax=" << ymax << " w=" << xmax - xmin << " h= " << ymax - ymin; if (d->m_mapGraphicsView) { Esri::ArcGISRuntime::Envelope env; CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor(); if (ctFunctor) { const QPointF bottomLeft(ctFunctor->geographicToMap(xmin, ymin)); const QPointF topRight(ctFunctor->geographicToMap(xmax, ymax)); // xmin,ymin,xmax,ymax env = Esri::ArcGISRuntime::Envelope(bottomLeft.x(), bottomLeft.y(), topRight.x(), topRight.y(), d->m_esriMap->spatialReference()); } else { ERROR_LOG(m_logger) << LOGFUNCTION << " !!! no CoordTranslationFunctor!!!" " interpret values as absolute values"; // setCoords -> xmin,ymin,xmax,ymax env = Esri::ArcGISRuntime::Envelope(xmin, ymin, xmax, ymax, d->m_esriMap->spatialReference()); } d->m_mapGraphicsView->setViewpoint(env, d->m_animationDuration); //d->m_mapGraphicsView->setViewpointGeometry(env); ///// d->m_absoluteScale = d->m_esriMap->scale(); // update d->m_absoluteScale } else ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; } void MapImpl::setExtentMapWorld(double xmin, double ymin, double xmax, double ymax) { if (d->m_mapGraphicsView) { Esri::ArcGISRuntime::Envelope env(xmin, ymin, xmax, ymax, d->m_esriMap->spatialReference()); d->m_mapGraphicsView->setViewpoint(Esri::ArcGISRuntime::Viewpoint(env)); d->m_absoluteScale = this->scale(); // update d->m_absoluteScale } else ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; } /* QRectF MapImpl::fullExtent() const { if (d->m_esriMap) { const EsriRuntimeQt::Envelope &env = d->m_esriMap->fullExtent(); CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor(); if (ctFunctor) { const QPointF upperLeft(ctFunctor->mapToGeographic(env.upperLeft())); const QPointF lowerRight(ctFunctor->mapToGeographic(env.lowerRight())); return QRectF(upperLeft, lowerRight); } else { // QRect -> left, top, width, height return QRectF(env.xMin(), env.yMin(), // use yMin, not yMax env.width(), env.height()); } } else { ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; return QRectF(); } } */ double MapImpl::scale() const { if (!this->isReady()) { ERROR_LOG(m_logger) << LOGFUNCTION << " not ready yet!!!"; return 0.0; } if (d->m_destroying) { ERROR_LOG(m_logger) << LOGFUNCTION << " is going to be destroyed!!!"; return 0.0; } // Keep in mind: while animation, esriMap delivers to old value. #ifdef SITUATIONMAP_OWN_WHEEL_HANDLING return d->m_absoluteScale; #else if (this->getEsriGeoView()) { const Esri::ArcGISRuntime::Viewpoint &vp = this->getEsriGeoView()->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); if (vp.isEmpty()) return 0.0; double scale = vp.targetScale(); return scale; } else ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; return 0.0; #endif // SITUATIONMAP_OWN_WHEEL_HANDLING } void MapImpl::setScale(double absoluteScale) { TRACE_LOG(m_logger) << LOGFUNCTION << " absoluteScale = " << absoluteScale; if (d->m_mapGraphicsView) { d->m_mapGraphicsView->setViewpointScale(absoluteScale); d->m_absoluteScale = absoluteScale; } else if (d->m_sceneGraphicsView) { FATAL_LOG(m_logger) << LOGFUNCTION << " Does this work with V100 ???"; FATAL_LOG(m_logger) << LOGFUNCTION << " Does this work with V100 ???"; FATAL_LOG(m_logger) << LOGFUNCTION << " Does this work with V100 ???"; FATAL_LOG(m_logger) << LOGFUNCTION << " Does this work with V100 ???"; FATAL_LOG(m_logger) << LOGFUNCTION << " Does this work with V100 ???"; // Check: Does this work with QuartzFinal? // Or Camera::moveForward ?? const Esri::ArcGISRuntime::Viewpoint &curVp = d->m_sceneGraphicsView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); Esri::ArcGISRuntime::Viewpoint targetVp(curVp.targetGeometry(), (absoluteScale < 0) ? curVp.targetScale() : absoluteScale); d->m_sceneGraphicsView->setViewpoint(targetVp); } else ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; } void MapImpl::setMinScale(double minScaleToLimitZoomOut) { DEBUG_LOG(m_logger) << LOGFUNCTION << " minScaleToLimitZoomOut = " << minScaleToLimitZoomOut; if (d->m_esriMap) d->m_esriMap->setMinScale(minScaleToLimitZoomOut); else ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; } double MapImpl::minScale() const { if (d->m_esriMap) return d->m_esriMap->minScale(); ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; return 0; } void MapImpl::setMaxScale(double maxScaleToLimitZoomIn) { DEBUG_LOG(m_logger) << LOGFUNCTION << " maxScaleToLimitZoomIn = " << maxScaleToLimitZoomIn; if (d->m_esriMap) d->m_esriMap->setMaxScale(maxScaleToLimitZoomIn); else ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; } double MapImpl::maxScale() const { if (d->m_esriMap) return d->m_esriMap->maxScale(); ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; return 0; } double MapImpl::rotation() const { // return d->m_rotationAbsoluteAngleDegrees; if (this->getEsriGeoView()) { const Esri::ArcGISRuntime::Viewpoint &vp = this->getEsriGeoView()->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); double rot = vp.rotation(); if (std::isnan(rot)) { DEBUG_LOG(m_logger) << LOGFUNCTION << " nan detected..."; return 0; } rot *= -1.0; // clock vs. counterClock if (rot < 0) rot += 360.0; return rot; } else { ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; return 0.0; } } void MapImpl::setRotation(double absoluteAngleDegrees) { Esri::ArcGISRuntime::GeoView *geoview = this->getEsriGeoView(); if (mode() == Mode::Mode_3D) { ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; return; } if (geoview) { d->m_rotationAbsoluteAngleDegrees = absoluteAngleDegrees; const Esri::ArcGISRuntime::Viewpoint &curVp = geoview->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); Esri::ArcGISRuntime::Viewpoint targetVp(curVp.targetGeometry(), curVp.targetScale(), d->m_rotationAbsoluteAngleDegrees * -1.0); // clock vs. counterClock geoview->setViewpoint(targetVp); emit rotationChanged(d->m_rotationAbsoluteAngleDegrees); } // if (d->m_mapGraphicsView) // { // d->m_rotationAbsoluteAngleDegrees = absoluteAngleDegrees; // d->m_mapGraphicsView->setViewpointRotation( d->m_rotationAbsoluteAngleDegrees * -1.0 ); // clock vs. counterClock // emit rotationChanged(d->m_rotationAbsoluteAngleDegrees); // } } void MapImpl::onEsriRotate() { double rot = rotation(); if (!qFuzzyCompare(rot, d->m_rotationAbsoluteAngleDegrees)) { d->m_rotationAbsoluteAngleDegrees = rot; emit rotationChanged(d->m_rotationAbsoluteAngleDegrees); } } Esri::ArcGISRuntime::Camera cameraToEsriCamera(const Camera &camIn) { // rad to degree const double factor = 180 / M_PI; return Esri::ArcGISRuntime::Camera(camIn.loc_y_lat * factor, // latitude = y camIn.loc_x_lon * factor, // longitude = x camIn.loc_z, camIn.heading * factor, camIn.pitch * factor, camIn.roll * factor ); } Camera esriCameraToCamera(const Esri::ArcGISRuntime::Camera &camIn) { Camera cam; const Esri::ArcGISRuntime::Point &loc = camIn.location(); const double factor = M_PI / 180; cam.loc_x_lon = loc.x() * factor; cam.loc_y_lat = loc.y() * factor; cam.loc_z = loc.z(); cam.heading = camIn.heading() * factor; cam.roll = camIn.roll() * factor; cam.pitch = camIn.pitch() * factor; return cam; } Camera MapImpl::camera() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!d->m_sceneGraphicsView) { ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; return Camera(); } const Esri::ArcGISRuntime::Camera &curCam = d->m_sceneGraphicsView->currentViewpointCamera(); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return esriCameraToCamera(curCam); } void MapImpl::setCamera(const Camera &camera, float durationSeconds) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" #ifndef WIN32 << " camera=" << camera #endif // WIN32 << ", durationSeconds=" << durationSeconds; if (!d->m_sceneGraphicsView) { ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; return; } const Esri::ArcGISRuntime::Camera &tarCam = cameraToEsriCamera(camera); // avoid neg durationSeconds if (durationSeconds < 0) d->m_sceneGraphicsView->setViewpointCamera(tarCam); else d->m_sceneGraphicsView->setViewpointCamera(tarCam, durationSeconds); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } Grid &MapImpl::grid() const { if (!d->m_gridImpl) { MapImpl *THIS = const_cast(this); d->m_gridImpl = new SituationMap::GridImpl(THIS); } return *(d->m_gridImpl); } ObjectFactory &MapImpl::objectFactory() { if (!d->m_objectFactory) d->m_objectFactory = new ObjectFactory(this); return *(d->m_objectFactory); } LayerFactory &MapImpl::layerFactory() const { return layerFactoryImpl(); } LayerFactoryImpl &MapImpl::layerFactoryImpl() const { if (!d->m_layerFactoryImpl) { MapImpl *THIS = const_cast(this); d->m_layerFactoryImpl = new LayerFactoryImpl(THIS, THIS); } return *(d->m_layerFactoryImpl); } int MapImpl::basemapLayerCount() const { int counter = -1; if (d->m_basemap && d->m_basemap->baseLayers()) counter = d->m_basemap->baseLayers()->size(); return counter; } int MapImpl::graphicsOverlayCount() const { int counter = -1; if (d->m_mapGraphicsView && d->m_mapGraphicsView->graphicsOverlays()) counter = d->m_mapGraphicsView->graphicsOverlays()->size(); return counter; } int MapImpl::technicalLayerCount() const { int counter = 0; int cTemp = basemapLayerCount(); if (cTemp >= 0) counter += cTemp; cTemp = graphicsOverlayCount(); if (cTemp >= 0) counter += cTemp; return counter; } void MapImpl::emitVisualComponentChanged(const VisualComponent &visualComponent) { if (d->m_destroying) return; emit visualComponentChanged(visualComponent); } void MapImpl::onClickMapObject(const QList &uniqueMapIdList, const QPointF &pos) { if (d->m_destroying) return; DEBUG_LOG(m_logger) << LOGFUNCTION << " START onClickMapObject clicked ids at viewport position x = " << pos.x() << " ; y = " << pos.y() << " uniqueMapIdList.size() = " << uniqueMapIdList.size(); RefPointerMapObjectList mapObjList; QList mapObjIdList; bool makeObjList = isSignalConnected(QMetaMethod::fromSignal(&Map::mapObjectsClicked)); bool makeObjIdList = isSignalConnected(QMetaMethod::fromSignal(&Map::mapObjectIdsClicked)); for (const UniqueMapId &uniqueMapId : uniqueMapIdList) { AbstractMapObject *ifMapObjPtr = d->m_objectFactory->idToMapObject(uniqueMapId); if (ifMapObjPtr) { if (ifMapObjPtr->pickable()) { DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueMapId=" << uniqueMapId << QStringLiteral(" Address=%1").arg((qint64)ifMapObjPtr, 0, 16) << " object-debugInfo=" << ifMapObjPtr->debugInfo() << " object-identifier=" << ifMapObjPtr->identifier(); if (makeObjList) mapObjList += RefPointer(ifMapObjPtr); if (makeObjIdList) mapObjIdList += uniqueMapId; } else { DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueMapId=" << uniqueMapId << QStringLiteral(" Address=%1").arg((qint64)ifMapObjPtr, 0, 16) << " object-debugInfo=" << ifMapObjPtr->debugInfo() << " object-identifier=" << ifMapObjPtr->identifier() << " object is NOT pickable!"; } } else ERROR_LOG(m_logger) << LOGFUNCTION << " Unknown map object with uniqueMapId=" << uniqueMapId << " found."; } // for DEBUG_LOG(m_logger) << LOGFUNCTION << " END onClickMapObject clicked ids" << ", mapObjList=" << mapObjList.size() << ", makeObjIdList=" << mapObjIdList.size(); if (makeObjList) emit mapObjectsClicked(mapObjList, pos); if (makeObjIdList) emit mapObjectIdsClicked(mapObjIdList, pos); } // MapImpl::onClickMapObject void MapImpl::onContextClick(const QList &uniqueMapIdList, const QPointF &pos) { if (d->m_destroying) return; DEBUG_LOG(m_logger) << LOGFUNCTION << " START onContextClick clicked ids at viewport position x = " << pos.x() << " ; y = " << pos.y(); RefPointerMapObjectList mapObjList; bool makeObjList = isSignalConnected(QMetaMethod::fromSignal((void (Map::*)(const RefPointerMapObjectList &, const QPointF &))(&Map::contextMenuClicked))); if (!makeObjList) return; for (UniqueMapId uniqueMapId : uniqueMapIdList) { AbstractMapObject *ifMapObjPtr = d->m_objectFactory->idToMapObject(uniqueMapId); if (ifMapObjPtr) { DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueMapId=" << uniqueMapId << QStringLiteral(" Address=%1").arg((qint64)ifMapObjPtr, 0, 16) << " object-debugInfo=" << ifMapObjPtr->debugInfo() << " object-identifier=" << ifMapObjPtr->identifier(); if (makeObjList) mapObjList += RefPointer(ifMapObjPtr); } else ERROR_LOG(m_logger) << LOGFUNCTION << "Unknown map object with uniqueMapId=" << uniqueMapId << " found."; } // for DEBUG_LOG(m_logger) << LOGFUNCTION << " END onContextClick clicked ids"; if (makeObjList) emit contextMenuClicked(mapObjList, pos); } // MapImpl::onContextClick() void MapImpl::onWheelZoom(qreal factor, const QPointF &cursorPositionMap) { if (d->m_destroying) return; TRACE_LOG(m_logger) << LOGFUNCTION << " factor = " << factor << " cursorPositionMap = " << cursorPositionMap; // scale -> non-animated ; zoomTo -> animated // zoomToScale( d->m_absoluteScale *= factor ); ///// zoomToScaleMapCoordinate(d->m_absoluteScale *= factor, cursorPositionMap); } // MapImpl::onWheelZoom( qreal factor ) void MapImpl::startRocket() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; DEBUG_LOG(m_logger) << LOGFUNCTION << " current Scale = " << d->m_mapGraphicsView->mapScale(); if (rocketIsRunning()) { DEBUG_LOG(m_logger) << LOGFUNCTION << " rocket is running -> return"; return; } // Save value // for 2D d->m_currentCenterPointBeforeRocket = centerPoint(); d->m_currentScaleBeforeRocket = scale(); d->m_currentCameraBeforRocket = camera(); // Zoom out switch (this->mode()) { case Mode::Mode_2D: setCenterPoint(d->m_currentCenterPointBeforeRocket, d->m_currentScaleBeforeRocket * 10, d->m_rocketDuration); break; case Mode::Mode_3D: { Esri::ArcGISRuntime::Camera curCam = cameraToEsriCamera(d->m_currentCameraBeforRocket); const double curHeight = d->m_currentCameraBeforRocket.loc_z; const Esri::ArcGISRuntime::Camera &newCam = curCam.moveForward(curHeight * 10.0 * -1); setCamera(esriCameraToCamera(newCam), d->m_rocketDuration); break; } case Mode::Undefined: // empty; break; } // switch delete d->m_rocketTimer; d->m_rocketTimer = new QTimer(this); d->m_rocketTimer->setSingleShot(true); d->m_rocketTimer->setInterval((d->m_rocketDuration * 1000) + 100); // add small amount to let zoomSlider react :-) connect(d->m_rocketTimer, &QTimer::timeout, this, &MapImpl::onRocketTimerZoomOutFinished); d->m_rocketTimer->start(); // to zoom out TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } bool SituationMap::MapImpl::rocketIsRunning() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return d->m_currentScaleBeforeRocket != 0.0; } double MapImpl::symbolScale() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; TRACE_LOG(m_logger) << LOGFUNCTION << " END, symbolScale = " << d->m_symbolScale; return d->m_symbolScale; } void MapImpl::setSymbolScale(double symbolScale) { TRACE_LOG(m_logger) << LOGFUNCTION << " START, new symbolScale = " << symbolScale; // 0 check range: if (symbolScale < SituationMap::symbolScaleMinimum) symbolScale = SituationMap::symbolScaleMinimum; if (symbolScale > SituationMap::symbolScaleMaximum) symbolScale = SituationMap::symbolScaleMaximum; // 1 d->m_symbolScale = symbolScale; // 2 apply QMutableMapIterator> mapObjIt = this->objectContainer().mutableIterator(); while (mapObjIt.hasNext()) { mapObjIt.next(); RefPointer mapObj = mapObjIt.value(); if (mapObj) { mapObj->setScale(symbolScale); if (! mapObj->updateView()) ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed, debugInfo='" << mapObj->debugInfo() << "'"; } } // while // 3 emit emit symbolScaleChanged(d->m_symbolScale); TRACE_LOG(m_logger) << LOGFUNCTION << " END, new symbolScale = " << symbolScale; } void MapImpl::onRocketTimerZoomOutFinished() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; // Zoom in / restore initial values: switch (this->mode()) { case Mode::Mode_2D: setCenterPoint(d->m_currentCenterPointBeforeRocket, d->m_currentScaleBeforeRocket, d->m_rocketDuration); break; case Mode::Mode_3D: setCamera(d->m_currentCameraBeforRocket, d->m_rocketDuration); break; case Mode::Undefined: // empty; break; } // switch delete d->m_rocketTimer; d->m_rocketTimer = new QTimer(this); d->m_rocketTimer->setSingleShot(true); d->m_rocketTimer->setInterval((d->m_rocketDuration * 1000) + 100); // add small amount to let zoomSlider react :-) connect(d->m_rocketTimer, &QTimer::timeout, this, &MapImpl::onRocketTimerZoomInFinished); d->m_rocketTimer->start(); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } void MapImpl::onRocketTimerZoomInFinished() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; delete d->m_rocketTimer; d->m_rocketTimer = nullptr; d->m_currentCenterPointBeforeRocket = QPointF(); d->m_currentScaleBeforeRocket = 0.0; d->m_currentCameraBeforRocket.resetCamera(); emit rocketFinished(); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } void SituationMap::MapImpl::onJumpPointRequest(const QPointF &viewPortPos) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!d->m_jumpPointWidget) return; const QPointF &coordGeo = this->viewPortWindowPointToGeoCoordinate(viewPortPos); connect(d->m_jumpPointWidget, &JumpPointWidget::newName, this, &MapImpl::onName); d->m_jumpPointWidget->askUserForName(coordGeo); } void SituationMap::MapImpl::onName(const QString &name, const QPointF &coordGeo) { if (name.isEmpty()) return; const double scale = this->scale(); d->m_jumpPointWidget->addJumpPoint(name, coordGeo, scale); disconnect(d->m_jumpPointWidget, &JumpPointWidget::newName, this, &MapImpl::onName); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } GeometryModelRenderer *MapImpl::createGeometryModelRenderer(const QString &layerName, GeometryModel *geometryModel, QObject *parent) { return new SituationMap::GeometryModelRendererEsri(this, layerName, *geometryModel, parent); } RB::RubberbandFactory &MapImpl::rubberbandFactory() const { if (!d->m_rubberbandFactory) { MapImpl *THIS = const_cast(this); d->m_rubberbandFactory = new RB::RubberbandFactory(THIS); } return *(d->m_rubberbandFactory); } bool MapImpl::pickMapObjectsArea() const { // assume that we always use a rectangle (orientation north) RB::RubberbandRectangle *rubberband = rubberbandFactory().create(); if (!rubberband) { ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed"; return false; } rubberband->constrainOrthogonal(true); rubberband->setAutoDelete(true); connect(rubberband, &RB::Rubberband::apply, this, &MapImpl::onRubberbandPickMapObjectsApply); return true; } bool MapImpl::scaleByArea() const { SituationMap::RB::RubberbandRectangle *rubberband = rubberbandFactory().create(); if (!rubberband) { ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed"; return false; } rubberband->constrainOrthogonal(true); rubberband->setAutoDelete(true); connect(rubberband, &SituationMap::RB::Rubberband::apply, this, [this](SituationMap::RB::RubberbandBase *rubberband) { if (rubberband) { const QVector &points = rubberband->coordinateVector(); if (points.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed, no coords"; return; } double xmin = points[0].x(); double ymin = points[0].y(); double xmax = xmin; double ymax = ymin; for (auto pt : points) { if (pt.x() < xmin) xmin = pt.x(); if (pt.y() < ymin) ymin = pt.y(); if (pt.x() > xmax) xmax = pt.x(); if (pt.y() > ymax) ymax = pt.y(); } if ((!qFuzzyCompare(xmin, xmax)) && (!qFuzzyCompare(ymin, ymax))) const_cast(this)->setExtent(xmin, ymin, xmax, ymax); } }); return true; } // bool MapImpl::scaleByArea() const ObjectIndicator &MapImpl::objectIndicator() { if (!d->m_objectIndicatorImpl) { d->m_objectIndicatorImpl = new ObjectIndicatorImpl(); d->m_objectIndicatorImpl->init(this); } return *(d->m_objectIndicatorImpl); } LocationDisplay &MapImpl::locationDisplay() { if (!d->m_locationDisplayImpl) { d->m_locationDisplayImpl = new LocationDisplayImpl(this); d->m_locationDisplayImpl->init(); } return *(d->m_locationDisplayImpl); } PixelBasedTextBoxHandler &MapImpl::pixelBasedTextBoxHandler() { if (!d->m_pixelBasedTextBoxHandlerImpl) { d->m_pixelBasedTextBoxHandlerImpl = new PixelBasedTextBoxHandlerImpl(this); } return *(d->m_pixelBasedTextBoxHandlerImpl); } Measurement &MapImpl::measurement() { if (!d->m_measurementImpl) d->m_measurementImpl = new MeasurementImpl(this); return *(d->m_measurementImpl); } TerrainProfile &MapImpl::terrainProfile() { if (!d->m_terrainProfileImpl) d->m_terrainProfileImpl = new TerrainProfileImpl(this); return *(d->m_terrainProfileImpl); } Viewshed &MapImpl::viewshed() { if (!d->m_viewshedImpl) d->m_viewshedImpl = new ViewshedImpl(this); return *(d->m_viewshedImpl); } void MapImpl::onErrorGeometryEngine(const Esri::ArcGISRuntime::Error &error) { FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; } void MapImpl::onErrorMap(const Esri::ArcGISRuntime::Error &error) { FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; } void MapImpl::onErrorScene(const Esri::ArcGISRuntime::Error &error) { FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; } void MapImpl::onErrorOperationalLayer(const Esri::ArcGISRuntime::Error &error) { FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; } void MapImpl::onErrorBasemap(const Esri::ArcGISRuntime::Error &error) { FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; } void MapImpl::onViewpointChanged() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (d->m_destroying) return; if (!d->m_mapReadyFlag) return; // to ensure recalculation: d->m_pixelsize = -1.0; // The very first time, the extent is nan (not a number) // Therefore suppress this: // 23.11.16, N.Thoden, V100.0 if (d->m_suppressFirstViewpointChange) { DEBUG_LOG(m_logger) << LOGFUNCTION << " suppress firstViewpointChange"; d->m_suppressFirstViewpointChange = false; return; } DEBUG_LOG(m_logger) << LOGFUNCTION << " ************** viewpointChanged=" << toString(this->extent()); emit extentChanged(); d->m_maxExtentTimer->start(); // start -> restart // TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } void MapImpl::onRectChanged() { if (d->m_destroying) return; DEBUG_LOG(m_logger) << LOGFUNCTION << " ************** signal emitted: rectChanged ************** "; DEBUG_LOG(m_logger) << LOGFUNCTION << " ************** signal emitted: rectChanged ************** " << " w=" << this->width() << " - h=" << this->height(); emit rectChanged(); } QImage MapImpl::toImageSync() { QScreen *screen = this->screen(); if (!screen) return {}; WId winId = this->winId(); QSize size = this->size(); QPixmap pix = screen->grabWindow(winId, 0, 0, size.width(), size.height()); return pix.toImage(); } QUuid MapImpl::toImage() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; Esri::ArcGISRuntime::GeoView *geoview = this->getEsriGeoView(); if (! geoview) { ERROR_LOG(m_logger) << LOGFUNCTION << " no view"; return QUuid(); } switch (mode()) { case Mode::Mode_2D: connect(d->m_mapGraphicsView, &Esri::ArcGISRuntime::MapGraphicsView::exportImageCompleted, this, &MapImpl::onExportImageCompleted, Qt::UniqueConnection); break; case Mode::Mode_3D: connect(d->m_sceneGraphicsView, &Esri::ArcGISRuntime::SceneGraphicsView::exportImageCompleted, this, &MapImpl::onExportImageCompleted, Qt::UniqueConnection); break; case Mode::Undefined: // empty break; } // switch Esri::ArcGISRuntime::TaskWatcher tw = geoview->exportImage(); if (! tw.isValid()) { ERROR_LOG(m_logger) << LOGFUNCTION << " TaskWatcher invalid"; return QUuid(); } const QUuid &taskId = tw.taskId(); TRACE_LOG(m_logger) << LOGFUNCTION << " END, taskId=" << taskId; return taskId; } void MapImpl::onExportImageCompleted(const QUuid &taskId, const QImage &image) { TRACE_LOG(m_logger) << LOGFUNCTION << " START, taskId=" << taskId; emit imageCompleted(taskId, image); // image.save(QLatin1String("./ExportImageCompleted.png"), "PNG"); TRACE_LOG(m_logger) << LOGFUNCTION << " END, taskId=" << taskId; } bool MapImpl::toPdf(const QString &outputFilename, const QString &title, const QString &footerLeft, const QString &footerCenter, const QString &footerRight, const QColor &textColor) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " outputFilename='" << outputFilename << "'"; connect(this, &MapImpl::imageCompleted, this, [this, outputFilename, title, footerLeft, footerCenter, footerRight, textColor](const QUuid &, const QImage & image) { finishToPdf(image, outputFilename, title, footerLeft, footerCenter, footerRight, textColor); }); if (this->toImage().isNull()) { ERROR_LOG(m_logger) << LOGFUNCTION << " taskId invalid"; return false; } TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " outputFilename='" << outputFilename << "'"; return true; } void MapImpl::finishToPdf(const QImage &image, const QString &outputFilename, const QString &title, const QString &footerLeft, const QString &footerCenter, const QString &footerRight, const QColor &textColor) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " outputFilename='" << outputFilename << "'"; QColor textColorInternal; if (textColor.isValid()) textColorInternal = textColor; else textColorInternal = this->palette().color(QPalette::WindowText); // RDE_DARK_BLUE QPrinter printer(QPrinter::PrinterMode::HighResolution); // QPrinter::PrinterMode::ScreenResolution ; ScreenResolution is Qt-default printer.setOutputFormat(QPrinter::PdfFormat); printer.setOutputFileName(outputFilename); printer.setOrientation(QPrinter::Orientation::Landscape); int marginInt = 20; QMarginsF margins; margins.setLeft(marginInt); margins.setTop(marginInt); printer.setPageMargins(margins); printer.setCreator(QStringLiteral("Rde-") + qAppName()); printer.setDocName(title); const QSizeF pageSize = printer.paperRect().size(); DEBUG_LOG(m_logger) << LOGFUNCTION << " pageSize=" << pageSize; const QSizeF drawingSize = pageSize - QSizeF(2 * marginInt, 2 * marginInt) * 1.5; QPainter painter(&printer); QTextDocument document; QTextCursor cursor(&document); QTextTableFormat tableFormat; // tableFormat.setBackground(QColor(QLatin1String("lightGray"))); tableFormat.setBorder(0); tableFormat.setCellPadding(10); QTextCharFormat defaultFormat; defaultFormat.setForeground(QBrush(textColorInternal)); // // Header: // { QTextCharFormat format = defaultFormat; format.setFontWeight(QFont::Bold); format.setFontPointSize(20); QTextTable *headerTable = cursor.insertTable(1, 1, tableFormat); QTextTableCell cell = headerTable->cellAt(0, 0); QTextCursor cellCursor = cell.firstCursorPosition(); cellCursor.mergeCharFormat(format); cellCursor.insertText(title); // create small distance: format.setFontPointSize(5); cellCursor.mergeCharFormat(format); cellCursor.insertBlock(); } cursor.insertBlock(); // // Image: // const QUrl imageUrl(QLatin1String("situationMap://sitMapScreenshot.png")); document.addResource(QTextDocument::ImageResource, imageUrl, QVariant(image)); QTextImageFormat imageFormat; imageFormat.setName(imageUrl.toString()); imageFormat.setWidth(drawingSize.width() * 0.95); cursor.insertImage(imageFormat); // // Footer // { QVector constraints; constraints << QTextLength(QTextLength::FixedLength, imageFormat.width() / 3); constraints << QTextLength(QTextLength::FixedLength, imageFormat.width() / 3); constraints << QTextLength(QTextLength::FixedLength, imageFormat.width() / 3); tableFormat.setColumnWidthConstraints(constraints); QTextTable *footerTable = cursor.insertTable(1, 3, tableFormat); QStringList sl; sl << footerLeft << footerCenter << footerRight; // Footer -> centered: QTextBlockFormat blockFormat = cursor.blockFormat(); Qt::Alignment alignment = Qt::AlignHCenter; // Read vertical part of current alignment flags Qt::Alignment vertAlign = blockFormat.alignment() & Qt::AlignVertical_Mask; // Mask out vertical part of specified alignment flags Qt::Alignment horzAlign = alignment & Qt::AlignHorizontal_Mask; // Combine current vertical and specified horizontal alignment Qt::Alignment combAlign = horzAlign | vertAlign; // Apply and write blockFormat.setAlignment(combAlign); for (int i = 0; i < 3; ++i) { QTextTableCell cell = footerTable->cellAt(0, i); QTextCursor cellCursor = cell.firstCursorPosition(); cellCursor.setBlockFormat(blockFormat); cellCursor.mergeCharFormat(defaultFormat); cellCursor.insertText(sl.at(i)); } // finally: document.drawContents(&painter); } emit pdfCompleted(outputFilename); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " outputFilename='" << outputFilename << "'"; } //---------------------------------------------------------------------------- // MapConnector (Map -> MapObjects) //---------------------------------------------------------------------------- bool MapConnector::connectMapObject(MapImpl *mapImpl, AbstractMapObjectImpl *mapObjImpl) { m_mapImpl = mapImpl; m_mapObjectImpl = mapObjImpl; if (m_mapImpl && m_mapObjectImpl) { connect(m_mapImpl, &MapImpl::extentChangedDelayed, this, &MapConnector::onMapExtentChanged); return true; } return false; } void MapConnector::onMapExtentChanged() { // if (m_mapImpl && m_mapObjectImpl) // checked in connectObject m_mapObjectImpl->onMapExtentChanged(); } } // namespace SituationMap #include "map_impl.moc"