/** * @RDE_HEADER@ * Interface-Implementation of LayerFactory * $Id$ * $HeadURL$ * $Author$ */ // Own #include "layer_factory_impl.h" #include "map_impl.h" #include "abstract_map_object_impl.h" #include #include "logical_layer.h" #include "location_to_elevation_helper.h" //#include "tms_layer.h" #include // for getResourcePath #include #include // ESRI #include // This file is created by RME, not ESRI #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Qt #include #include #include #include #include #include #include // FORCE_CREATION_GRAPHICSOVERLAY : // N.Thoden, 10.02.2017 // Esri Case #01887547 - [BOS] Dictionary Symbol crasches // deals with order of creation of GraphicalOverlay and adding them to MapView. // Although the reordering of the GraphicalOverlay is not included in the case, // it seems to be relevant in this application. // To find a quick solution, GraphicalOverlays are created as soon as possible // (EsriDict must be loaded). // static bool s_forceCreationOfGraphicsOverlay = true; // : FORCE_CREATION_GRAPHICSOVERLAY namespace SituationMap { static const QLatin1String LayerNameJumpPoints("JumpPoints"); static const QLatin1String LayerNameRubberband("RubberbandLayer"); static const QLatin1String LayerNameTools("Tools"); static const QLatin1String LayerNameSnapshotOverlay("SnaphotOverlay"); static const QLatin1String LayerNameEmergencyCall("EmergencyCall"); // #define USE_ARCGIS_SCENE_LAYER_CACHE #ifdef USE_ARCGIS_SCENE_LAYER_CACHE class ArcGISSceneLayerCache; static ArcGISSceneLayerCache *s_arcGisSceneLayerCache = nullptr; /*! * \brief The ArcGISSceneLayerCache class reduces the number of loading processes * We are caching here to avoid numerous loading and unloading. * At least at linux the ArcGISSceneLayerCache tends to alloc a hugh amount of data. * Additionally allocation is influenced by the viewpoint choosen by the operator. * And with other allocations (dds data, GBViewer of similar) at the same time * the heap memory gets fragemented. * So we try to reduce the number of loading processes. */ class ArcGISSceneLayerCache { public: static ArcGISSceneLayerCache *getInstance() { if (!s_arcGisSceneLayerCache) { s_arcGisSceneLayerCache = new ArcGISSceneLayerCache; qAddPostRoutine(&ArcGISSceneLayerCache::cleanup); } return s_arcGisSceneLayerCache; } struct Item { QPointer m_sceneLayer; QPointer m_hostingScene; }; Item& getFreeItemRef(const QString &itemName) { //Qt: If the map contains no item with key key, the function inserts a default-constructed value into the map with key key, and returns a reference to it. QVector &vec = m_arcGISSceneLayerCache[itemName]; // search free item: QMutableVectorIterator it(vec); while (it.hasNext()) { Item& itemRef = it.next(); if (itemRef.m_hostingScene.isNull()) return itemRef; } // Not found -> create: vec.append(Item()); return vec.last(); } static void cleanup() { delete s_arcGisSceneLayerCache; } ~ArcGISSceneLayerCache() { for (auto vec : m_arcGISSceneLayerCache) { for (auto item : vec) { std::cerr << " m_arcGISSceneLayerCache: delete " << item.m_sceneLayer << std::endl; delete item.m_sceneLayer; // delete nullptr is ok } } } void dump() { std::cerr << " ArcGISSceneLayer CacheSize = " << m_arcGISSceneLayerCache.size() << std::endl; for (auto vec : m_arcGISSceneLayerCache) { std::cerr << " ArcGISSceneLayer VectorSize: " << vec.size() << std::endl; for (auto item : vec) { std::cerr << " ArcGISSceneLayer ITEM = " << " m_hostingScene=" << item.m_hostingScene << " m_sceneLayer=" << item.m_sceneLayer << std::endl; } // for } // for } private: // slpk-name, .... QMap> m_arcGISSceneLayerCache; }; // class ArcGISSceneLayerCache #endif // USE_ARCGIS_SCENE_LAYER_CACHE class LayerFactoryImpl::Private { public: Private() = default; ~Private() = default; // ESRI data: Esri::ArcGISRuntime::Basemap *m_basemap = nullptr; Esri::ArcGISRuntime::GraphicsOverlayListModel *getEsriGraphicsOverlayListModel() { if (m_mapImpl && m_mapImpl->getEsriGeoView()) return m_mapImpl->getEsriGeoView()->graphicsOverlays(); else return nullptr; } Esri::ArcGISRuntime::GraphicsOverlay *m_functionDialogGraphicsLayer = nullptr; // Own data: SituationMap::MapImpl *m_mapImpl = nullptr; QStringList m_mapServiceUrlList; QVector m_sceneObjectUrlCont; Esri::ArcGISRuntime::Surface *m_baseSurface = nullptr; bool m_isSceneObjectLayerVisible = true; QTimer *reCalculateMinMaxScaleTimer = nullptr; MapLayerOrderedList m_orderedMapLayer; // store order of basemap and logical Layernames bool hasLogicalLayer(const QString &logicalName) { return m_logicalLayerCont.contains(logicalName); } // create and initalize LogicalLayer: void createLogicalLayer(const QString &logicalName) { // LayerListModel Esri::ArcGISRuntime::LayerListModel *layerListModel = nullptr; if (m_basemap) layerListModel = m_basemap->baseLayers(); // GraphicsOverlayListModel Esri::ArcGISRuntime::GraphicsOverlayListModel *graphicsOverlayListModel = this->getEsriGraphicsOverlayListModel(); #ifdef LTS_DEBUG if (!graphicsOverlayListModel) qCritical() << " graphicsOverlayListModel" << " logicalName='" << logicalName << "'" << " m_mapImpl=" << m_mapImpl << " m_mapImpl->getEsriGeoView()=" << m_mapImpl->getEsriGeoView() << " m_mapImpl->isReady=" << m_mapImpl->isReady() << " graphicsOverlayListModel=" << graphicsOverlayListModel; Q_ASSERT(graphicsOverlayListModel); #endif // LTS_DEBUG QSharedPointer ll = QSharedPointer(new LogicalLayer(logicalName)); ll->init(m_mapImpl, layerListModel, graphicsOverlayListModel); m_logicalLayerCont[logicalName] = ll; } QSharedPointer getLogicalLayer(const QString &logicalName) const { if (m_logicalLayerCont.contains(logicalName)) return m_logicalLayerCont[logicalName]; return QSharedPointer(); } void removeLogicalLayer(const QString &logicalName) { m_logicalLayerCont.remove(logicalName); } QMap> m_logicalLayerCont; // store logical layer without order QString m_basemapLayernamePrefix = QLatin1String("ArcGISTiledLayer_URL_"); QString m_participantLayername = QLatin1String("Participants"); RenderingMode m_participantLayerRenderingMode = RenderingMode::Dynamic; SurfacePlacement m_participantLayerSurfacePlacement = SurfacePlacement::Relative; QString m_logicalLayerNameForCubes; #if ACRGIS_VERSION < 0x640500 // 100.5.0 / Update 5 QColor m_selectionColor = Qt::magenta; #endif // ACRGIS_VERSION }; // class LayerFactoryImpl::Private //----------------------------------------------------------------------------- // // class LayerFactoryImpl; // LayerFactoryImpl::LayerFactoryImpl(QObject *parent, SituationMap::MapImpl *mapImpl) : LayerFactory() , BaseClass(parent) , m_logger(getLogger("LayerFactoryImpl")) , d(new Private) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; d->m_mapImpl = mapImpl; if (d->m_mapImpl) d->m_basemap = d->m_mapImpl->getEsriBasemap(); d->reCalculateMinMaxScaleTimer = new QTimer(this); d->reCalculateMinMaxScaleTimer->setInterval(500); d->reCalculateMinMaxScaleTimer->setSingleShot(true); connect(d->reCalculateMinMaxScaleTimer, &QTimer::timeout, this, &LayerFactoryImpl::reCalculateMinMaxScaleBasedOnLayers); INFO_LOG(m_logger) << LOGFUNCTION << " Resource Path = '" << SituationMap::getResourcePath() << "'"; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } LayerFactoryImpl::~LayerFactoryImpl() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; delete d; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } bool LayerFactoryImpl::initializeBasemapBuiltIn(const QString &tpkFile) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " tpkFile='" << tpkFile << "'"; QString tiledBaseMapLayer; if (tpkFile.isEmpty()) tiledBaseMapLayer = SituationMap::getResourcePath() + QLatin1String("Topographic.tpk"); else { QFileInfo fi(tpkFile); if (fi.isAbsolute()) tiledBaseMapLayer = tpkFile; else { tiledBaseMapLayer = SituationMap::getResourcePath() + tpkFile; if (fi.suffix().isEmpty()) tiledBaseMapLayer += QLatin1String(".tpk"); } } DEBUG_LOG(m_logger) << LOGFUNCTION << " tiledBaseMapLayer='" << tiledBaseMapLayer << "'"; //----------------------------------- // Local Tiled Basemap Layer using: sdk/samples/data/tpks/Topographic.tpk //----------------------------------- Esri::ArcGISRuntime::TileCache *tpkCache = new Esri::ArcGISRuntime::TileCache(tiledBaseMapLayer, this); tpkCache->setObjectName(QLatin1String("TPK-tpkCache")); Esri::ArcGISRuntime::ArcGISTiledLayer *tiledLayer = new Esri::ArcGISRuntime::ArcGISTiledLayer(tpkCache, this); tiledLayer->setName(QLatin1String("TPK-tiledLayer")); // loadStatusChanged -> connect(tpkCache, &Esri::ArcGISRuntime::TileCache::loadStatusChanged, this, [this](Esri::ArcGISRuntime::LoadStatus loadStatus) { DEBUG_LOG(m_logger) << LOGFUNCTION << " TileCache: " << "loadStatus changed to: " << loadStatus; }); connect(tiledLayer, &Esri::ArcGISRuntime::ArcGISTiledLayer::loadStatusChanged, this, [this](Esri::ArcGISRuntime::LoadStatus loadStatus) { DEBUG_LOG(m_logger) << LOGFUNCTION << " ArcGISTiledLayer: " << "loadStatus changed to: " << loadStatus; }); QString logicalLayerName = QStringLiteral("%1%2").arg(d->m_basemapLayernamePrefix).arg(0); tiledLayer->setName(logicalLayerName); tiledLayer->setObjectName(tiledLayer->name()); QSharedPointer logicalLayer = addLayerToMgnt(logicalLayerName, LayerType::Basemap); if (logicalLayer.isNull()) ERROR_LOG(m_logger) << LOGFUNCTION << " getLogicalLayer invalid'" << logicalLayerName << "'"; else { // update LayerParameter first: LayerParameter lp = logicalLayer->layerParameter(); lp.url = QUrl(tiledBaseMapLayer); lp.displayName = logicalLayerName; logicalLayer->setLayerParameter(lp); logicalLayer->setLayer(tiledLayer); logicalLayer->setLayerType(LayerType::Basemap); logicalLayer->setBasemapType(BasemapType::Topographic); // basemapType -> aerial, topo... } int basemapCount = -1; if (d->m_basemap->baseLayers()) basemapCount = d->m_basemap->baseLayers()->size(); INFO_LOG(m_logger) << LOGFUNCTION << " basemapCount = " << basemapCount; d->m_basemap->baseLayers()->append(tiledLayer); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } bool LayerFactoryImpl::isUsingBaseMapBuiltIn() const { return d->m_mapServiceUrlList.isEmpty(); } bool LayerFactoryImpl::addOperationalLayer(const QString &logicalLayerName, const QString &displayName, RenderingMode renderMode, SurfacePlacement surfacePlacement) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " logicalLayerName='" << logicalLayerName << "'" << " displayName='" << displayName << "'" << " renderMode=" << renderMode << " surfacePlacement=" << surfacePlacement; if (s_forceCreationOfGraphicsOverlay && !logicalLayerName.startsWith(QLatin1String("Participant"), Qt::CaseInsensitive)) { // First, wait until esriDict is load, second force creation of esriLayer: // wait until esriDict is load: AdvancedSymbology::SymbolDictionary &dict = d->m_mapImpl->symbolDictionary(); AdvancedSymbology::SymbolDictionaryImpl &dictImpl = static_cast(dict); // keep SymbologyType::None in mind: Esri::ArcGISRuntime::DictionarySymbolStyle *esriDict = const_cast(dictImpl.getEsriSymbolDictionary()); if (esriDict) { if (esriDict->loadStatus() != Esri::ArcGISRuntime::LoadStatus::Loaded) { QEventLoop loop; connect(esriDict, &Esri::ArcGISRuntime::DictionarySymbolStyle::doneLoading, &loop, &QEventLoop::quit); QTimer::singleShot(2000, &loop, &QEventLoop::quit); loop.exec(); } DEBUG_LOG(m_logger) << LOGFUNCTION << " FORCE_CREATION_GRAPHICSOVERLAY: " << " esriDict: loadStatus=" << esriDict->loadStatus(); } } // if (s_forceCreationOfGraphicsOverlay) const LayerType layerType = LayerType::Operational; if (! layerExistsInMngt(logicalLayerName, layerType)) { QSharedPointer logicalLayer = addLayerToMgnt(logicalLayerName, layerType); if (logicalLayer.isNull()) { ERROR_LOG(m_logger) << LOGFUNCTION << " addLayer failed: '" << logicalLayerName << "'"; return false; } LayerParameter lp = logicalLayer->layerParameter(); lp.displayName = displayName; logicalLayer->setLayerParameter(lp); logicalLayer->setRenderingMode(renderMode); logicalLayer->setSurfacePlacement(surfacePlacement); } // // the real Esri-Layers will be create later on demand :-) // //#ifdef FORCE_CREATION_GRAPHICSOVERLAY if (s_forceCreationOfGraphicsOverlay && !logicalLayerName.startsWith(QLatin1String("Participant"), Qt::CaseInsensitive)) { QSharedPointer logicalLayer = d->getLogicalLayer(logicalLayerName); if (logicalLayer) { logicalLayer->getCreateGraphicalOverlay(TechnicalLayerType::GraphicsLayer); if (d->m_mapImpl->symbologyType() != AdvancedSymbology::SymbologyType::None) logicalLayer->getCreateGraphicalOverlay(TechnicalLayerType::GraphicsLayer_Dict); DEBUG_LOG(m_logger) << LOGFUNCTION << " FORCE_CREATION_GRAPHICSOVERLAY"; } } // if (s_forceCreationOfGraphicsOverlay) //#endif // FORCE_CREATION_GRAPHICSOVERLAY TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " logicalLayerName='" << logicalLayerName << "'" << " success=" << true; return true; } bool LayerFactoryImpl::removeOperationalLayer(const QString &logicalLayerName) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " logicalLayerName='" << logicalLayerName << "'"; if (logicalLayerName.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName empty" << logicalLayerName; return false; } const LayerType layerType = LayerType::Operational; if (! d->m_orderedMapLayer.contains(logicalLayerName, layerType)) { ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName does not exist: '" << logicalLayerName << "'"; return false; } if (removeLayerInternal(layerType, logicalLayerName)) d->m_mapImpl->emitLayerOrderChanged(); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " logicalLayerName='" << logicalLayerName << "'"; return true; } bool LayerFactoryImpl::hasOperationalLayer(const QString &logicalLayerName) const { if (logicalLayerName.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName empty" << logicalLayerName; return false; } return layerExistsInMngt(logicalLayerName, LayerType::Operational); } bool LayerFactoryImpl::addOverlayLayer(const QString &logicalLayerName, const QString &displayName, bool isSnaphot) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " logicalLayerName='" << logicalLayerName << "'" << " displayName='" << displayName << "'" << " isSnaphot='" << isSnaphot; const LayerType layerType = (isSnaphot) ? LayerType::SnapshotOverlay : LayerType::Overlay; if (! layerExistsInMngt(logicalLayerName, layerType)) { QSharedPointer logicalLayer = addLayerToMgnt(logicalLayerName, layerType); if (logicalLayer.isNull()) { ERROR_LOG(m_logger) << LOGFUNCTION << " addLayer failed: '" << logicalLayerName << "'"; return false; } // logicalLayer->setRenderingMode(RenderingMode::Dynamic); } // update displayName: QSharedPointer logicalLayer = d->getLogicalLayer(logicalLayerName); if (logicalLayer.isNull()) { WARNING_LOG(m_logger) << LOGFUNCTION << " getLogicalLayer failed: '" << logicalLayerName << "'"; } else { LayerParameter lp = logicalLayer->layerParameter(); lp.displayName = displayName; logicalLayer->setLayerParameter(lp); } // // the real Esri-Layers will be create later on demand :-) // TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } QString LayerFactoryImpl::technicalNameOfOverlayLayer(const QString &logicalLayerName) { // By default, our overlays are displayed on just one GraphicsLayer: return createInternalLayerName(TechnicalLayerType::GraphicsLayer, logicalLayerName); } QSharedPointer LayerFactoryImpl::addLayerToMgnt(const QString &logicalLayerName, const LayerType &layerType) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " layerType=" << layerType << " logicalLayerName='" << logicalLayerName << "'"; QSharedPointer logicalLayer; if (logicalLayerName.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName empty" << logicalLayerName; return logicalLayer; } if (d->m_orderedMapLayer.contains(logicalLayerName, LayerType::Undefined)) { ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName already exists: '" << logicalLayerName << "'"; return logicalLayer; } // 1. add to orderedList: if (! d->m_orderedMapLayer.addLayer(logicalLayerName, layerType)) { ERROR_LOG(m_logger) << LOGFUNCTION << " addLayer failed: '" << logicalLayerName << "'" << " layerType=" << layerType; return logicalLayer; } // getLogicalLayer will // - initialize // - do errorHandling // - update LayerParameter on doneLoading // - and add layer to LayerlistModel of basemap: logicalLayer = this->getCreateLogicalLayer(logicalLayerName); if (logicalLayer.isNull()) { ERROR_LOG(m_logger) << LOGFUNCTION << " getLogicalLayer invalid'" << logicalLayerName << "'"; return logicalLayer; } logicalLayer->setLayerType(layerType); DEBUG_LOG(m_logger) << LOGFUNCTION << " handleEmergencySpecials ..."; handleEmergencyCallSpecials(); DEBUG_LOG(m_logger) << LOGFUNCTION << " handleEmergencySpecials ...DONE"; // trigger gui update: d->m_mapImpl->emitLayerOrderChanged(); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " layerType=" << layerType << " logicalLayerName='" << logicalLayerName << "'"; return logicalLayer; } bool LayerFactoryImpl::layerExistsInMngt(const QString &logicalLayerName, const LayerType &layerType) const { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " layerType=" << layerType << " logicalLayerName='" << logicalLayerName << "'"; bool exists = d->m_orderedMapLayer.contains(logicalLayerName, layerType); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " layerType=" << layerType << " logicalLayerName='" << logicalLayerName << "'" << " exists=" << exists; return exists; } bool LayerFactoryImpl::removeOverlayLayer(const QString &logicalLayerName) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " layername='" << logicalLayerName << "'"; if (logicalLayerName.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName empty" << logicalLayerName; return false; } const MapLayer &mapLayer = d->m_orderedMapLayer.getLayer(logicalLayerName); if (!mapLayer.isValid()) { ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName does not exist: '" << logicalLayerName << "'"; return false; } const LayerType type = mapLayer.type(); const bool isOverlayLayer = (type == LayerType::Overlay) || (type == LayerType::SnapshotOverlay); if (!isOverlayLayer) { ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName has wrong type: '" << logicalLayerName << "'" << " type = " << type; return false; } if (removeLayerInternal(type, logicalLayerName)) { DEBUG_LOG(m_logger) << LOGFUNCTION << " remove..." << " layername='" << logicalLayerName << "'"; d->m_mapImpl->emitLayerOrderChanged(); } TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " logicalLayerName='" << logicalLayerName << "'"; return true; } QString LayerFactoryImpl::snapshotOverlayLayername() const { return LayerNameSnapshotOverlay; } QString LayerFactoryImpl::layernameForCubes() const { return d->m_logicalLayerNameForCubes; } void LayerFactoryImpl::setLayernameForCubes(const QString &logicalName) { d->m_logicalLayerNameForCubes = logicalName; } bool LayerFactoryImpl::hasLayernameForCubes() const { return !d->m_logicalLayerNameForCubes.isEmpty(); } bool LayerFactoryImpl::assignUniqueValueRendererForCubes() { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " m_logicalLayerNameForCubes='" << d->m_logicalLayerNameForCubes << "'"; if (d->m_mapImpl->mode() != Mode::Mode_3D) { ERROR_LOG(m_logger) << LOGFUNCTION << " not in 3Dmode"; return false; } if (d->m_logicalLayerNameForCubes.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " no m_logicalLayerNameForCubes configured"; return false; } Esri::ArcGISRuntime::GraphicsOverlay *graphicsOverlay = this->getGraphicsLayer(d->m_logicalLayerNameForCubes); if (!graphicsOverlay) { ERROR_LOG(m_logger) << LOGFUNCTION << " graphicsOverlay not found, d->m_logicalLayerNameForCubes='" << d->m_logicalLayerNameForCubes << "'"; return false; } const PartyIdToColorFunctor::ColorMap &colorMap = d->m_mapImpl->partyIdToColorFunctor().colorMap(); if (colorMap.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " colorMap is empty"; return false; } // prepare UniqueValues Esri::ArcGISRuntime::SimpleFillSymbol *sls; QList uvList; QObject *parent = graphicsOverlay; for (const PartyIdToColorFunctor::PartyData &partyData : colorMap) { const QString &partyName = partyData.m_name; const QColor &partyColor = partyData.m_color; INFO_LOG(m_logger) << LOGFUNCTION << " partyName=" << partyName << "'" << " partyColor=" << partyColor.name() << "'"; sls = new Esri::ArcGISRuntime::SimpleFillSymbol(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, partyColor, parent); Esri::ArcGISRuntime::UniqueValue *uv = new Esri::ArcGISRuntime::UniqueValue(partyName, // more or less relevant QStringLiteral("Party '%1'").arg(partyName), QVariantList() << partyColor.name(), // (was in former times: partyName) sls, parent); uvList.append(uv); } // for //qt build in // static QVector addColors = {Qt::darkGray, Qt::gray, Qt::lightGray, // Qt::red, Qt::green, Qt::blue, // Qt::cyan, Qt::magenta, Qt::yellow, // Qt::darkRed, Qt::darkGreen, Qt::darkBlue, // Qt::darkCyan, Qt::darkMagenta, Qt::darkYellow, Qt::white}; static QVector addColors = { Qt::yellow }; uvList.reserve(uvList.size() + addColors.size()); for (const QColor &addColor : qAsConst(addColors)) { sls = new Esri::ArcGISRuntime::SimpleFillSymbol(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, addColor.name(), parent); Esri::ArcGISRuntime::UniqueValue *uv = new Esri::ArcGISRuntime::UniqueValue(addColor.name(), // more or less relevant QStringLiteral("Party '%1'").arg(addColor.name()), QVariantList() << addColor.name(), // (was in former times: partyName) sls, parent); uvList.append(uv); } //for // setup UniqueValueRenderer: // setup default: sls = new Esri::ArcGISRuntime::SimpleFillSymbol(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, Qt::white, this); Esri::ArcGISRuntime::UniqueValueRenderer *uniqueRenderer = new Esri::ArcGISRuntime::UniqueValueRenderer(QStringLiteral("UnknownParty"), sls, // default QStringList() << QStringLiteral("Extrusion_Color"), // fieldNames (was in former times: "PartyName") uvList, parent); static long s_counter = 0; uniqueRenderer->setObjectName(QStringLiteral("UniqueValueRenderer_ForCubes_%1").arg(s_counter++)); connect(uniqueRenderer, &Esri::ArcGISRuntime::UniqueValueRenderer::errorOccurred, this, [this] (const Esri::ArcGISRuntime::Error &error) { FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- assignUniqueValueRendererForCubes occured: "; FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; }); connect(uniqueRenderer, &Esri::ArcGISRuntime::UniqueValueRenderer::destroyed, this, [this, uniqueRenderer] () { INFO_LOG(m_logger) << " UniqueValueRenderer is destroyed , objectName='" << uniqueRenderer->objectName() << "'"; }); // set sceneProperties: Esri::ArcGISRuntime::RendererSceneProperties props = uniqueRenderer->sceneProperties(); props.setExtrusionMode(Esri::ArcGISRuntime::ExtrusionMode::AbsoluteHeight); // see BuildingOwnership::retrieveBuildingAbsHeight props.setExtrusionExpression(QStringLiteral("[height]")); // <-- keyword uniqueRenderer->setSceneProperties(props); // take care that the old one is deleted: Esri::ArcGISRuntime::Renderer *oldRenderer = graphicsOverlay->renderer(); delete oldRenderer; // delete nullptr is safe! // assign new: graphicsOverlay->setRenderer(uniqueRenderer); return true; } bool LayerFactoryImpl::assignUniqueValueRenderer(const QString &layername, const QString &fieldName, const QVector &uvDataCont) { TRACE_LOG(m_logger) << LOGFUNCTION << " START, layername='" << layername << "'" << ", fieldName='" << fieldName << "'" << ", uvDataCont.size()=" << uvDataCont.size(); // ensure existence: getCreateLogicalLayer(layername); Esri::ArcGISRuntime::GraphicsOverlay *graphicsOverlay = this->getGraphicsLayer(layername); if (!graphicsOverlay) { ERROR_LOG(m_logger) << LOGFUNCTION << " graphicsOverlay not found, layername='" << layername << "'"; return false; } // prepare UniqueValues Esri::ArcGISRuntime::PictureMarkerSymbol *pms; QList uvList; QObject *parent = graphicsOverlay; for (const UniqueValueData &uvData : qAsConst(uvDataCont)) { INFO_LOG(m_logger) << LOGFUNCTION << " uvData.value=" << uvData.value << "'"; pms = new Esri::ArcGISRuntime::PictureMarkerSymbol(uvData.img, parent); Esri::ArcGISRuntime::UniqueValue *uv = new Esri::ArcGISRuntime::UniqueValue(uvData.value, QStringLiteral("GenericUniqueValue '%1'").arg(uvData.value), QVariantList() << uvData.value, pms, parent); uvList.append(uv); } // for // setup default: Esri::ArcGISRuntime::SimpleMarkerSymbol *sms; sms = new Esri::ArcGISRuntime::SimpleMarkerSymbol(Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::X, Qt::white, 5, parent); // Finally, setup Renderer: Esri::ArcGISRuntime::UniqueValueRenderer* uniqueRenderer = new Esri::ArcGISRuntime::UniqueValueRenderer(QStringLiteral("GenericUniqueValueRenderer"), sms, // default QStringList() << fieldName, uvList, parent); static long s_counter = 0; uniqueRenderer->setObjectName(QStringLiteral("UniqueValueRenderer_assignUniqueValueRenderer_%1").arg(s_counter++)); connect(uniqueRenderer, &Esri::ArcGISRuntime::UniqueValueRenderer::errorOccurred, this, [this] (const Esri::ArcGISRuntime::Error &error) { FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- assignUniqueValueRenderer occured: "; FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; }); connect(uniqueRenderer, &Esri::ArcGISRuntime::UniqueValueRenderer::destroyed, this, [this, uniqueRenderer] () { INFO_LOG(m_logger) << " UniqueValueRenderer is destroyed , objectName='" << uniqueRenderer->objectName() << "'"; }); // take care that the old one is deleted: Esri::ArcGISRuntime::Renderer *oldRenderer = graphicsOverlay->renderer(); delete oldRenderer; // delete nullptr is safe! // assign new: graphicsOverlay->setRenderer(uniqueRenderer); TRACE_LOG(m_logger) << LOGFUNCTION << " END, layername='" << layername << "'" << ", fieldName='" << fieldName << "'" << ", uvDataCont.size()=" << uvDataCont.size(); return true; } bool LayerFactoryImpl::assignUniqueValueRendererAesCentral(const QString &layername, const QStringList &fieldNames, const QVector &uvDataCont) { TRACE_LOG(m_logger) << LOGFUNCTION << " START, layername='" << layername << "'" << ", fieldNames='" << fieldNames.join(QStringLiteral(";")) << "'" << ", uvDataCont.size()=" << uvDataCont.size(); // ensure existence: getCreateLogicalLayer(layername); Esri::ArcGISRuntime::GraphicsOverlay *graphicsOverlay = this->getGraphicsLayer(layername); if (!graphicsOverlay) { ERROR_LOG(m_logger) << LOGFUNCTION << " graphicsOverlay not found, layername='" << layername << "'"; return false; } // prepare UniqueValues QList uvList; QObject *parent = graphicsOverlay; for (const UniqueValueDataAesCentral &uvData : qAsConst(uvDataCont)) { INFO_LOG(m_logger) << LOGFUNCTION // << " uvData.value=" << uvData.values << "'" << ", type " << toString(uvData.type) << ", col " << uvData.col << ", col.name " << uvData.col.name() << ", uvData.radius " << uvData.radius; Esri::ArcGISRuntime::SimpleMarkerSceneSymbol *symbol = nullptr; switch (uvData.type) { case UniqueValueDataAesCentral::TYPE::CONE: symbol = Esri::ArcGISRuntime::SimpleMarkerSceneSymbol::cone(uvData.col, // extent should be similar to sphere uvData.radius, // V100.8: cone: diameter is interpreted as radius uvData.height, parent); symbol->setAnchorPosition(Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Top); break; case UniqueValueDataAesCentral::TYPE::SPHERE: symbol = Esri::ArcGISRuntime::SimpleMarkerSceneSymbol::sphere(uvData.col, uvData.radius*2.0, // diameter parent); symbol->setAnchorPosition(Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Center); default: break; } Esri::ArcGISRuntime::UniqueValue *uv = new Esri::ArcGISRuntime::UniqueValue(QStringLiteral("uvData.value"), QStringLiteral("AesCentralUniqueValue '%%%1'"), uvData.values, symbol, parent); uvList.append(uv); } // for // setup default: Esri::ArcGISRuntime::SimpleMarkerSymbol *sms; sms = new Esri::ArcGISRuntime::SimpleMarkerSymbol(Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::X, Qt::white, 25, parent); // Finally, setup Renderer: Esri::ArcGISRuntime::UniqueValueRenderer* uniqueRenderer = new Esri::ArcGISRuntime::UniqueValueRenderer(QStringLiteral("AesCentralUniqueValueRenderer"), sms, // default fieldNames, uvList, parent); static long s_counter = 0; uniqueRenderer->setObjectName(QStringLiteral("UniqueValueRenderer_assignUniqueValueRendererAesCentral_%1").arg(s_counter++)); connect(uniqueRenderer, &Esri::ArcGISRuntime::UniqueValueRenderer::errorOccurred, this, [this] (const Esri::ArcGISRuntime::Error &error) { FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- UniqueValueRendererAesCentral occured: "; FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; }); connect(uniqueRenderer, &Esri::ArcGISRuntime::UniqueValueRenderer::destroyed, this, [this, uniqueRenderer] () { INFO_LOG(m_logger) << " UniqueValueRenderer is destroyed , objectName='" << uniqueRenderer->objectName() << "'"; }); // setup SceneProperties Esri::ArcGISRuntime::RendererSceneProperties sceneProps; sceneProps.setHeadingExpression(QStringLiteral("[HEADING]")); // Expression with "[]", later on without "[]" sceneProps.setPitchExpression(QStringLiteral("[PITCH]")); // Expression with "[]", later on without "[]" sceneProps.setRollExpression(QStringLiteral("[ROLL]")); // Expression with "[]", later on without "[]" uniqueRenderer->setSceneProperties(sceneProps); // take care that the old one is deleted: Esri::ArcGISRuntime::Renderer *oldRenderer = graphicsOverlay->renderer(); delete oldRenderer; // delete nullptr is safe! // assign new: graphicsOverlay->setRenderer(uniqueRenderer); TRACE_LOG(m_logger) << LOGFUNCTION << " END, layername='" << layername << "'" << ", fieldNames='" << fieldNames.join(QStringLiteral(";")) << "'" << ", uvDataCont.size()=" << uvDataCont.size(); return true; } bool LayerFactoryImpl::assignUniqueValueRendererAesCentral3DExtrusion(const QString &layername, const QStringList &fieldNames, const QVector &uvDataCont) { TRACE_LOG(m_logger) << LOGFUNCTION << " START, layername='" << layername << "'" << ", fieldNames='" << fieldNames.join(QStringLiteral(";")) << "'" << ", uvDataCont.size()=" << uvDataCont.size(); // ensure existence: getCreateLogicalLayer(layername); Esri::ArcGISRuntime::GraphicsOverlay *graphicsOverlay = this->getGraphicsLayer3DExtrusion(layername); if (!graphicsOverlay) { ERROR_LOG(m_logger) << LOGFUNCTION << " graphicsOverlay not found, layername='" << layername << "'"; return false; } // prepare UniqueValues QList uvList; QObject *parent = graphicsOverlay; for (const UniqueValueDataAesCentral &uvData : qAsConst(uvDataCont)) { INFO_LOG(m_logger) << LOGFUNCTION // << " uvData.value=" << uvData.values << "'" << ", type " << toString(uvData.type) << ", col " << uvData.col << ", col.name " << uvData.col.name() << ", uvData.radius " << uvData.radius; Esri::ArcGISRuntime::SimpleFillSymbol *symbol = new Esri::ArcGISRuntime::SimpleFillSymbol(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, uvData.col, parent); Esri::ArcGISRuntime::UniqueValue *uv = new Esri::ArcGISRuntime::UniqueValue(uvData.label, QString(QStringLiteral("Description '%1'")).arg(uvData.col.name()), uvData.values, symbol, parent); uvList.append(uv); } // for // setup default: Esri::ArcGISRuntime::SimpleFillSymbol *defaultSymbol = new Esri::ArcGISRuntime::SimpleFillSymbol(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, Qt::white, parent); // Finally, setup Renderer: Esri::ArcGISRuntime::UniqueValueRenderer* uniqueRenderer = new Esri::ArcGISRuntime::UniqueValueRenderer(QStringLiteral("assignUniqueValueRendererAesCentral3DUNKNOWN"), defaultSymbol, // default fieldNames, uvList, parent); static long s_counter = 0; uniqueRenderer->setObjectName(QStringLiteral("UniqueValueRenderer_assignUniqueValueRendererAesCentral3D_%1").arg(s_counter++)); connect(uniqueRenderer, &Esri::ArcGISRuntime::UniqueValueRenderer::errorOccurred, this, [this] (const Esri::ArcGISRuntime::Error &error) { FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- UniqueValueRendererAesCentral3DExtrusion occured: "; FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; }); connect(uniqueRenderer, &Esri::ArcGISRuntime::UniqueValueRenderer::destroyed, this, [this, uniqueRenderer] () { INFO_LOG(m_logger) << " UniqueValueRenderer is destroyed , objectName='" << uniqueRenderer->objectName() << "'"; }); Esri::ArcGISRuntime::RendererSceneProperties props = uniqueRenderer->sceneProperties(); props.setExtrusionMode(Esri::ArcGISRuntime::ExtrusionMode::BaseHeight); props.setExtrusionExpression(QStringLiteral("[height]")); // <-- keyword uniqueRenderer->setSceneProperties(props); // take care that the old one is deleted: Esri::ArcGISRuntime::Renderer *oldRenderer = graphicsOverlay->renderer(); delete oldRenderer; // delete nullptr is safe! // assign new: graphicsOverlay->setRenderer(uniqueRenderer); TRACE_LOG(m_logger) << LOGFUNCTION << " END, layername='" << layername << "'" << ", fieldNames='" << fieldNames.join(QStringLiteral(";")) << "'" << ", uvDataCont.size()=" << uvDataCont.size(); return true; } bool LayerFactoryImpl::initializeUnitTestLayer() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; QUrl mapUrl(QLatin1String("http://DOES_NOT_EXISTS")); // wkid = 102100/3857 this->addBasemap(QLatin1String("DummyLayerName"), mapUrl, BasemapType::Aerial); //----------------------------------- // Layer: //----------------------------------- QStringList layerNames; layerNames << QLatin1String("AES"); layerNames << QLatin1String("Participants"); bool success = true; for (const QString &layername : qAsConst(layerNames)) if (!addOperationalLayer(layername, layername)) success = false; if (!success) ERROR_LOG(m_logger) << LOGFUNCTION << " initializeOperationalLayers failed"; // enableFunctionDialog(); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return success; } bool LayerFactoryImpl::addBasemap(const QString &layername, const QUrl &mapServiceUrl, const BasemapType basemapType, const double minScale, const double maxScale, const QString &displayName, const QString &description, const QImage &previewImage) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " layername='" << layername << "'" << " mapServiceUrl='" << mapServiceUrl.toString() << "'" << " basemapType='" << basemapType << "'" << " minScale='" << minScale << "'" << " maxScale='" << maxScale << "'" << " displayName='" << displayName << "'" << " description='" << description << "'" << " previewImage.isNull()=" << previewImage.isNull() << " wkid=" << d->m_mapImpl->getEsriSpatialReference().wkid(); bool success = addBasemapInternal(layername, mapServiceUrl, basemapType, minScale, maxScale, displayName, description); // reOrder success = success && reOrderLayer(); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " layername='" << layername << "'" << " mapServiceUrl='" << mapServiceUrl.toString() << "'" << " displayName='" << displayName << "'" << " success =" << success; return success; } bool LayerFactoryImpl::removeBasemap(const QString &layername) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " layername='" << layername << "'"; if (!d->m_orderedMapLayer.contains(layername, LayerType::Basemap)) { ERROR_LOG(m_logger) << LOGFUNCTION << " layername='" << layername << "' not found"; return false; } bool success = false; if (d->m_orderedMapLayer.removeLayer(layername) && d->m_basemap) { Esri::ArcGISRuntime::Layer *layer = this->getEsriBasemapLayer(layername); if (layer && d->m_basemap->baseLayers()) { d->m_basemap->baseLayers()->removeOne(layer); success = true; INFO_LOG(m_logger) << LOGFUNCTION << " layer" << " layername='" << layername << "' removed"; d->m_mapImpl->emitLayerOrderChanged(); } else ERROR_LOG(m_logger) << LOGFUNCTION << " layername='" << layername << "' can`t be found"; } else ERROR_LOG(m_logger) << LOGFUNCTION << " layername='" << layername << "' can`t be removed"; TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " layername='" << layername << "'" << " success='" << success << "'"; return success; } Esri::ArcGISRuntime::Envelope LayerFactoryImpl::fullExtentOfBasemaps(double margin_zeroToOne) const { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; Esri::ArcGISRuntime::LayerListModel *model = d->m_basemap->baseLayers(); if (!model || model->isEmpty()) return Esri::ArcGISRuntime::Envelope(); double xmin = std::numeric_limits::max(); double ymin = std::numeric_limits::max(); double xmax = std::numeric_limits::lowest(); double ymax = std::numeric_limits::lowest(); Esri::ArcGISRuntime::SpatialReference sr; for (int idx = 0; idx < model->size(); ++idx) { Esri::ArcGISRuntime::Layer *layer = model->at(idx); // 12.12.2018, N. Thoden, V100.4: // Do not call fullExtent if layer is not loaded. // Envelope contains nan and will not changed on further calls... if (!layer || (layer->loadStatus() != Esri::ArcGISRuntime::LoadStatus::Loaded)) continue; const Esri::ArcGISRuntime::Envelope singleEnv = layer->fullExtent(); DEBUG_LOG(m_logger) << LOGFUNCTION << " layer->loadStatus()" << layer->loadStatus() << ", singleEnv.toJson()" << singleEnv.toJson(); sr = singleEnv.spatialReference(); if (!std::isnan(singleEnv.xMin())) xmin = std::min(singleEnv.xMin(), xmin); if (!std::isnan(singleEnv.yMin())) ymin = std::min(singleEnv.yMin(), ymin); if (!std::isnan(singleEnv.xMax())) xmax = std::max(singleEnv.xMax(), xmax); if (!std::isnan(singleEnv.yMax())) ymax = std::max(singleEnv.yMax(), ymax); } // for if (qFuzzyCompare(xmin, std::numeric_limits::max()) || sr.isEmpty()) return Esri::ArcGISRuntime::Envelope(); if (margin_zeroToOne < 0.0) margin_zeroToOne = 0.0; if (margin_zeroToOne > 1.0) margin_zeroToOne = 1.0; if (margin_zeroToOne > 0) { double width = xmax - xmin; double height = ymax - ymin; xmin -= width * margin_zeroToOne; xmax += width * margin_zeroToOne; ymin -= height * margin_zeroToOne; ymax += height * margin_zeroToOne; } DEBUG_LOG(m_logger) << LOGFUNCTION << " return.x " << xmin << " - " << xmax; DEBUG_LOG(m_logger) << LOGFUNCTION << " return.y " << ymin << " - " << ymax; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return Esri::ArcGISRuntime::Envelope(xmin, ymin, xmax, ymax, sr); } bool LayerFactoryImpl::addSceneObjectLayer(const QString &layername, const QUrl &sceneLayerServiceUrl, const double altitudeOffset, const QString &displayName, const QString &description, const QImage &previewImage) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " layername='" << layername << "'" << " sceneLayerServiceUrl='" << sceneLayerServiceUrl.toString() << "'" << " altitudeOffset='" << altitudeOffset << "'" << " displayName='" << displayName << "'" << " description='" << description << "'" << " previewImage.isNull()=" << previewImage.isNull(); if (d->m_mapImpl->mode() != Mode::Mode_3D) { ERROR_LOG(m_logger) << LOGFUNCTION << " not allowd in Mode3D"; return false; } if (sceneLayerServiceUrl.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " sceneLayerServiceUrl empty, can not add layer"; return false; } if (d->m_orderedMapLayer.contains(layername, LayerType::Undefined)) { ERROR_LOG(m_logger) << LOGFUNCTION << " layername '" << layername << "' already in use"; return false; } //----------------------------------- // ArcGIS ArcGISSceneLayer //----------------------------------- //----------------------------------------------------------------- // Attention: // local URLS -> do NOT specify any userCredentials (main) // external URLS -> DO specify userCredentials (main) //----------------------------------------------------------------- QUrl finalUrl = this->completeUrl(QLatin1String("SCENE_OBJ"), sceneLayerServiceUrl, QLatin1String("slpk/")); INFO_LOG(m_logger) << LOGFUNCTION << " SCENE_OBJ finalUrl '" << finalUrl.toString() << "'"; Esri::ArcGISRuntime::Scene *esriScene = d->m_mapImpl->getEsriScene(); #ifdef USE_ARCGIS_SCENE_LAYER_CACHE ArcGISSceneLayerCache::Item& aslCacheItem = ArcGISSceneLayerCache::getInstance()->getFreeItemRef(sceneLayerServiceUrl.toString()); if (m_logger.isInfoEnabled()) ArcGISSceneLayerCache::getInstance()->dump(); // const bool reUse = aslCacheItem.m_hostingScene.isNull() && aslCacheItem.m_sceneLayer; // aslCacheItem.m_hostingScene is always null. See getItemRef const bool reUse = aslCacheItem.m_sceneLayer; ERROR_LOG(m_logger) << LOGFUNCTION << " ArcGISSceneLayer -> reUse = "<< reUse << " itemName = '" << sceneLayerServiceUrl.toString() << "'" << " layername='" << layername << "'" << " aslCacheItem.m_hostingScene.data() = " << aslCacheItem.m_hostingScene.data() << " aslCacheItem.m_sceneLayer.data() = " << aslCacheItem.m_sceneLayer.data(); Esri::ArcGISRuntime::ArcGISSceneLayer *sceneLayer = nullptr; if (!reUse) { INFO_LOG(m_logger) << LOGFUNCTION << " ArcGISSceneLayer -> CREATE" << " (itemName = '" << sceneLayerServiceUrl.toString() << "', layername='" << layername << "')"; sceneLayer = new Esri::ArcGISRuntime::ArcGISSceneLayer(finalUrl); // ArcGISSceneLayer should live longer than the scene: -> no parent here :-) sceneLayer->setName(layername); sceneLayer->setObjectName(sceneLayer->name()); connect(sceneLayer, &Esri::ArcGISRuntime::ArcGISSceneLayer::errorOccurred, this, [this](const Esri::ArcGISRuntime::Error &error) { ERROR_LOG(m_logger) << LOGFUNCTION << " ArcGISSceneLayer -> EsriError occured: " << error; }); connect(sceneLayer, &Esri::ArcGISRuntime::ArcGISSceneLayer::doneLoading, this, [this]() { Esri::ArcGISRuntime::ArcGISSceneLayer *sceneLayer = qobject_cast(sender()); if (!sceneLayer) { ERROR_LOG(m_logger) << LOGFUNCTION << " qobject_cast failed to get sender!?"; return; } QString senderName = sceneLayer->objectName(); INFO_LOG(m_logger) << LOGFUNCTION << " ArcGISSceneLayer -> doneLoading" << " ,sendername= '" << senderName << "'" << " ,URL= '" << sceneLayer->url().toString() << "'"; }); connect(sceneLayer, &Esri::ArcGISRuntime::ArcGISSceneLayer::destroyed, this, [this]() { ERROR_LOG(m_logger) << LOGFUNCTION << " ArcGISSceneLayer -> destroyed"; }); aslCacheItem.m_hostingScene = esriScene; // update cache aslCacheItem.m_sceneLayer = sceneLayer; // update cache } else { INFO_LOG(m_logger) << LOGFUNCTION << " ArcGISSceneLayer -> TAKE FROM STATIC" << " (itemName = '" << sceneLayerServiceUrl.toString() << "', layername='" << layername << "')"; aslCacheItem.m_hostingScene = esriScene; // update cache sceneLayer = aslCacheItem.m_sceneLayer; } #else Esri::ArcGISRuntime::ArcGISSceneLayer *sceneLayer = new Esri::ArcGISRuntime::ArcGISSceneLayer(finalUrl, d->m_mapImpl); sceneLayer->setName(layername); sceneLayer->setObjectName(sceneLayer->name()); sceneLayer->setAltitudeOffset(altitudeOffset); connect(sceneLayer, &Esri::ArcGISRuntime::ArcGISSceneLayer::errorOccurred, this, [this](const Esri::ArcGISRuntime::Error &error) { ERROR_LOG(m_logger) << LOGFUNCTION << " ArcGISSceneLayer -> EsriError occured: " << error; }); connect(sceneLayer, &Esri::ArcGISRuntime::ArcGISSceneLayer::doneLoading, this, [this]() { Esri::ArcGISRuntime::ArcGISSceneLayer *sceneLayer = qobject_cast(sender()); if (!sceneLayer) { ERROR_LOG(m_logger) << LOGFUNCTION << " qobject_cast failed to get sender!?"; return; } QString senderName = sceneLayer->objectName(); DEBUG_LOG(m_logger) << LOGFUNCTION << " ArcGISSceneLayer -> doneLoading" << " ,sendername= '" << senderName << "'" << " ,URL= '" << sceneLayer->url().toString() << "'"; }); #endif // USE_ARCGIS_SCENE_LAYER_CACHE if (esriScene) { esriScene->operationalLayers()->append(sceneLayer); } else { ERROR_LOG(m_logger) << LOGFUNCTION << " no scene"; return false; } #ifdef USE_ARCGIS_SCENE_LAYER_CACHE if (m_logger.isInfoEnabled()) ArcGISSceneLayerCache::getInstance()->dump(); #endif // USE_ARCGIS_SCENE_LAYER_CACHE d->m_sceneObjectUrlCont += sceneLayerServiceUrl; TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " sceneLayerServiceUrl='" << sceneLayerServiceUrl.toString() << "'"; return true; } bool LayerFactoryImpl::isSceneObjectLayerVisible() const { return d->m_isSceneObjectLayerVisible; } void LayerFactoryImpl::setSceneObjectLayerVisible(bool visible) { d->m_isSceneObjectLayerVisible = visible; if ( (!d->m_mapImpl) || d->m_mapImpl->mode() != Mode::Mode_3D) return; Esri::ArcGISRuntime::Scene *esriScene = d->m_mapImpl->getEsriScene(); if (!esriScene) { ERROR_LOG(m_logger) << LOGFUNCTION << " no scene"; return; } Esri::ArcGISRuntime::LayerListModel *layerListModel = esriScene->operationalLayers(); if (!layerListModel) return; const int size = layerListModel->size(); for (int idx=0; idxat(idx); if (!objLayer) continue; objLayer->setVisible(d->m_isSceneObjectLayerVisible); } // for } bool LayerFactoryImpl::initializeElevationSources(const QStringList &sceneElevationServiceUrls) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " sceneElevationServiceUrls='" << sceneElevationServiceUrls.join(QLatin1String(";")) << "'"; const Mode mode = d->m_mapImpl->mode(); switch (mode) { case Mode::Mode_2D: this->initializeElevationSources2D(sceneElevationServiceUrls); break; case Mode::Mode_3D: this->initializeElevationSources3D(sceneElevationServiceUrls); break; default: ERROR_LOG(m_logger) << LOGFUNCTION << " unknown mode"; return false; } // switch TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } bool LayerFactoryImpl::initializeElevationSources2D(const QStringList &sceneElevationServiceUrls) { if (sceneElevationServiceUrls.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " sceneObjectServiceUrls is Empty"; return false; } if (d->m_mapImpl->mode() != Mode::Mode_2D) { ERROR_LOG(m_logger) << LOGFUNCTION << " wrong mode"; return false; } // // append to 'own' esri (base) surface: // delete d->m_baseSurface; d->m_baseSurface = new Esri::ArcGISRuntime::Surface(this); d->m_baseSurface->setName(QLatin1String("BaseSurface2D")); d->m_baseSurface->setObjectName(d->m_baseSurface->name()); connect(d->m_baseSurface, &Esri::ArcGISRuntime::Surface::errorOccurred, this, &LayerFactoryImpl::onSurfaceErrorOccured2D, Qt::UniqueConnection); connect(d->m_baseSurface, &Esri::ArcGISRuntime::Surface::doneLoading, this, &LayerFactoryImpl::onSurfaceDoneLoading2D, Qt::UniqueConnection); const QList &elevationSources = initializeElevationSourcesInternal(sceneElevationServiceUrls); for (auto elevationSource : qAsConst(elevationSources)) d->m_baseSurface->elevationSources()->append(elevationSource); // trigger loading: // As Luke suggested: load surface if first elevation entry is loaded: // https://community.esri.com/thread/206757-elevation-in-2d if (!elevationSources.isEmpty()) { Esri::ArcGISRuntime::ElevationSource* elevationSourcesFirst = elevationSources.first(); if (elevationSourcesFirst) { connect(elevationSourcesFirst, &Esri::ArcGISRuntime::ElevationSource::doneLoading, this, [this](const Esri::ArcGISRuntime::Error&) { d->m_baseSurface->load(); }); elevationSourcesFirst->load(); } } return true; } bool LayerFactoryImpl::initializeElevationSources3D(const QStringList &sceneElevationServiceUrls) { if (sceneElevationServiceUrls.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " sceneObjectServiceUrls is Empty"; return false; } if (d->m_mapImpl->mode() != Mode::Mode_3D) { ERROR_LOG(m_logger) << LOGFUNCTION << " wrong mode"; return false; } Esri::ArcGISRuntime::Scene *esriScene = d->m_mapImpl->getEsriScene(); if (!esriScene) { ERROR_LOG(m_logger) << LOGFUNCTION << " no scene"; return false; } if (!esriScene->baseSurface() || !esriScene->baseSurface()->elevationSources()) { ERROR_LOG(m_logger) << LOGFUNCTION << " no ElevationSourceListModel"; return false; } // // append to esri scene: // const QList elevationSources = initializeElevationSourcesInternal(sceneElevationServiceUrls); for (auto item : elevationSources) esriScene->baseSurface()->elevationSources()->append(item); return true; } QList LayerFactoryImpl::initializeElevationSourcesInternal(const QStringList &sceneElevationServiceUrls) { if (sceneElevationServiceUrls.isEmpty()) return {}; Esri::ArcGISRuntime::RasterElevationSource* rasterElevationSource = initializeRasterElevationSourceInternal(sceneElevationServiceUrls); QList elevationSources; elevationSources.append(rasterElevationSource); return elevationSources; } Esri::ArcGISRuntime::RasterElevationSource *LayerFactoryImpl::initializeRasterElevationSourceInternal(const QStringList &filePaths) { DEBUG_LOG(m_logger) << LOGFUNCTION << " filePaths = " << filePaths.join(QStringLiteral(";")) << "'"; QStringList absFilePaths_rough_to_fine; absFilePaths_rough_to_fine.reserve(filePaths.size()); for (const QString &filePath : filePaths) { QUrl finalUrl = this->completeUrl(QLatin1String("ELEVATION"), QUrl(filePath), QLatin1String("elevation/")); INFO_LOG(m_logger) << LOGFUNCTION << " ELEVATION finalUrl '" << finalUrl.toString() << "'"; absFilePaths_rough_to_fine.append(finalUrl.toLocalFile()); } // for if (absFilePaths_rough_to_fine.isEmpty()) { WARNING_LOG(m_logger) << LOGFUNCTION << " filePaths are empty!!!"; return nullptr; } DEBUG_LOG(m_logger) << LOGFUNCTION << " absFilePaths_rough_to_fine = " << absFilePaths_rough_to_fine.join(QStringLiteral(";")) << "'"; // // init ElevationMapManager // if (!d->m_mapImpl->getElevationMapManager()) { if (!d->m_mapImpl->initElevationMapManager(absFilePaths_rough_to_fine)) ERROR_LOG(m_logger) << LOGFUNCTION << " initElevationMapManager failed"; } // // Esri::ArcGISRuntime::RasterElevationSource with several files // Esri::ArcGISRuntime::RasterElevationSource *rasterElevationSource = new Esri::ArcGISRuntime::RasterElevationSource(absFilePaths_rough_to_fine, this); const QString &elevationSourceName = QStringLiteral("RasterElevationSources_((%1))").arg(absFilePaths_rough_to_fine.size()); rasterElevationSource->setName(elevationSourceName); rasterElevationSource->setObjectName(rasterElevationSource->name()); // 2017-09-18; N.Thoden, V100.1: // Following message occures very often. Therefore don`t connect to errorOccurred // 'code=404 domain=0 extendedErrorType=0, message='Error transferring http://excgeo21.sl .. - server replied: Not Found' connect(rasterElevationSource, &Esri::ArcGISRuntime::ElevationSource::errorOccurred, this, &LayerFactoryImpl::onElevationSourceErrorOccured); connect(rasterElevationSource, &Esri::ArcGISRuntime::ElevationSource::doneLoading, this, &LayerFactoryImpl::onElevationSourceDoneLoading); return rasterElevationSource; } void LayerFactoryImpl::setForceCreationOfGraphicsOverlay(bool onOff) { s_forceCreationOfGraphicsOverlay = onOff; } QString LayerFactoryImpl::basemapLayernamePrefix() const { return d->m_basemapLayernamePrefix; } void LayerFactoryImpl::setBasemapLayernamePrefix(const QString &basemapLayernamePrefix) { d->m_basemapLayernamePrefix = basemapLayernamePrefix; } QString LayerFactoryImpl::participantLayername() const { return d->m_participantLayername; } RenderingMode LayerFactoryImpl::participantLayerRenderingMode() const { return d->m_participantLayerRenderingMode; } SurfacePlacement LayerFactoryImpl::participantLayerSurfacePlacement() const { return d->m_participantLayerSurfacePlacement; } void LayerFactoryImpl::setParticipantLayer(const QString &participantLayername, RenderingMode renderingMode, SurfacePlacement surfacePlacement) { d->m_participantLayername = participantLayername; d->m_participantLayerRenderingMode = renderingMode; d->m_participantLayerSurfacePlacement = surfacePlacement; } QString LayerFactoryImpl::jumpPointLayer() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; const QString &logicalLayerName = LayerNameJumpPoints; const LayerType layerType = LayerType::JumpPoint; if (! layerExistsInMngt(logicalLayerName, layerType)) { QSharedPointer logicalLayer = addLayerToMgnt(logicalLayerName, layerType); if (logicalLayer.isNull()) { ERROR_LOG(m_logger) << LOGFUNCTION << " addLayer failed: '" << logicalLayerName << "'"; return QString(); } LayerParameter lp = logicalLayer->layerParameter(); lp.displayName = tr("JumpPoints"); // dt. Sprungmarken logicalLayer->setLayerParameter(lp); } // // the real Esri-Layers will be create later on demand :-) // TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return logicalLayerName; // == LayerNameJumpPoints; } bool LayerFactoryImpl::jumpPointLayerExists() const { const QString &logicalLayerName = LayerNameJumpPoints; return (d->m_orderedMapLayer.contains(logicalLayerName, SituationMap::LayerType::JumpPoint)); } QString LayerFactoryImpl::toolLayer() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; const QString &logicalLayerName = LayerNameTools; const LayerType layerType = LayerType::Tools; if (! layerExistsInMngt(logicalLayerName, layerType)) { QSharedPointer logicalLayer = addLayerToMgnt(logicalLayerName, layerType); if (logicalLayer.isNull()) { ERROR_LOG(m_logger) << LOGFUNCTION << " addLayer failed: '" << logicalLayerName << "'"; return QString(); } LayerParameter lp = logicalLayer->layerParameter(); lp.displayName = tr("Tools"); // dt. Werkzeuge (?) logicalLayer->setLayerParameter(lp); } TRACE_LOG(m_logger) << LOGFUNCTION << " END"; // // the real Esri-Layers will be create later on demand :-) // TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return logicalLayerName; // == LayerNameTools; } bool LayerFactoryImpl::toolLayerExists() const { const QString &logicalLayerName = LayerNameTools; return (d->m_orderedMapLayer.contains(logicalLayerName, SituationMap::LayerType::Tools)); } QString LayerFactoryImpl::rubberbandLayer() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; const QString &logicalLayerName = LayerNameRubberband; const LayerType layerType = LayerType::Rubberband; if (! layerExistsInMngt(logicalLayerName, layerType)) { LayerFactoryImpl *THIS = const_cast(this); QSharedPointer logicalLayer = THIS->addLayerToMgnt(logicalLayerName, layerType); if (logicalLayer.isNull()) { ERROR_LOG(m_logger) << LOGFUNCTION << " addLayer failed: '" << logicalLayerName << "'"; return QString(); } } // // the real Esri-Layers will be create later on demand :-) // TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return logicalLayerName; // == LayerNameRubberband; } bool LayerFactoryImpl::rubberbandLayerExists() const { const QString &logicalLayerName = LayerNameRubberband; return (d->m_orderedMapLayer.contains(logicalLayerName, SituationMap::LayerType::Rubberband)); } QString LayerFactoryImpl::emergencyCallLayer(bool getJustTheName) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; const QString &logicalLayerName = LayerNameEmergencyCall; if (getJustTheName) { TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return logicalLayerName; } const LayerType layerType = LayerType::EmergencyCall; if (! layerExistsInMngt(logicalLayerName, layerType)) { QSharedPointer logicalLayer = addLayerToMgnt(logicalLayerName, layerType); if (logicalLayer.isNull()) { ERROR_LOG(m_logger) << LOGFUNCTION << " addLayer failed: '" << logicalLayerName << "'"; return QString(); } LayerParameter lp = logicalLayer->layerParameter(); lp.displayName = tr("EmergencyCalls"); // dt. Notrufe/SOS (?) logicalLayer->setLayerParameter(lp); } { Esri::ArcGISRuntime::GraphicsOverlay *graphicsOverlay = this->getGraphicsLayer(logicalLayerName); if (!graphicsOverlay) { FATAL_LOG(m_logger) << LOGFUNCTION << " graphicsOverlay not found, logicalLayerName'" << logicalLayerName<< "'"; return QString(); } // set SurfacePlacement // EmergencyCalls have a pin/needle Esri::ArcGISRuntime::LayerSceneProperties lsp = graphicsOverlay->sceneProperties(); lsp.setSurfacePlacement(static_cast(Esri::ArcGISRuntime::SurfacePlacement::Relative)); graphicsOverlay->setSceneProperties(lsp); } TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return logicalLayerName; // == LayerNameEmergencyCall; } bool LayerFactoryImpl::emergencyCallLayerExists() const { const QString &emergencyCallLayername = LayerNameEmergencyCall; return (d->m_orderedMapLayer.contains(emergencyCallLayername, SituationMap::LayerType::EmergencyCall)); } bool LayerFactoryImpl::switchEmergencyCallLayerOn() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; const QString &emergencyCallLayername = LayerNameEmergencyCall; if (!(d->m_orderedMapLayer.contains(emergencyCallLayername, SituationMap::LayerType::EmergencyCall))) { ERROR_LOG(m_logger) << LOGFUNCTION << " EmergencyCallLayer does not exists, can´t be switched on."; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return false; } LayerParameter lp = this->layerParameter(emergencyCallLayername); lp.visible = true; lp.opacity = 100; lp.minScale = -1; lp.maxScale = -1; const bool success = this->setLayerParameter(emergencyCallLayername, lp); if (!success) { ERROR_LOG(m_logger) << LOGFUNCTION << " setLayerParameter failed for '" << emergencyCallLayername << "'"; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return false; } TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } bool LayerFactoryImpl::switchEmergencyCallLayerOff() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; const QString &emergencyCallLayername = LayerNameEmergencyCall; if (!(d->m_orderedMapLayer.contains(emergencyCallLayername, SituationMap::LayerType::EmergencyCall))) { ERROR_LOG(m_logger) << LOGFUNCTION << " EmergencyCallLayer does not exists, can´t be switched off."; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return false; } LayerParameter lp = this->layerParameter(emergencyCallLayername); lp.visible = false; lp.opacity = 100; lp.minScale = -1; lp.maxScale = -1; const bool success = this->setLayerParameter(emergencyCallLayername, lp); if (!success) { ERROR_LOG(m_logger) << LOGFUNCTION << " setLayerParameter failed for '" << emergencyCallLayername << "'"; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return false; } TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } void LayerFactoryImpl::handleEmergencyCallSpecials() { const QString &emergencyCallLayername = LayerNameEmergencyCall; const bool bEmergencyCallLayerExists = emergencyCallLayerExists(); TRACE_LOG(m_logger) << LOGFUNCTION << " START, emergencyCallLayerExists() = " << bEmergencyCallLayerExists; if (!bEmergencyCallLayerExists) return; // ensure to be on top: QStringList slLayerNamesOrdered = d->m_orderedMapLayer.layerNamesOrdered(); if (slLayerNamesOrdered.isEmpty()) return; const QString &lastNameTOP = slLayerNamesOrdered.last(); if (lastNameTOP == emergencyCallLayer()) { DEBUG_LOG(m_logger) << LOGFUNCTION << " emergencyCallLayer already on top"; return; } slLayerNamesOrdered.removeAll(emergencyCallLayername); slLayerNamesOrdered.push_back(emergencyCallLayername); orderLayerTo(slLayerNamesOrdered); { QStringList slLayerNamesOrderedLocal = d->m_orderedMapLayer.layerNamesOrdered(); if (slLayerNamesOrderedLocal.isEmpty()) return; const QString &lastNameTOPLocal = slLayerNamesOrderedLocal.last(); if (lastNameTOPLocal == emergencyCallLayer()) DEBUG_LOG(m_logger) << LOGFUNCTION << " emergencyCallLayer is now on top"; } TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } Map *LayerFactoryImpl::map() const { return d->m_mapImpl; } int LayerFactoryImpl::count() const { return d->m_orderedMapLayer.count(); } QStringList LayerFactoryImpl::layernames() const { return d->m_orderedMapLayer.layerNamesOrdered(); } QStringList LayerFactoryImpl::layernames(const LayerType &layerType) const { return d->m_orderedMapLayer.layerNamesOrdered(layerType); } bool SituationMap::LayerFactoryImpl::checkLayerOrder(const QStringList &orderedLayernames) { bool requestedOrderIsValid = false; bool success = d->m_orderedMapLayer.orderLayerTo(orderedLayernames, &requestedOrderIsValid); if (success) return requestedOrderIsValid; else return false; } bool LayerFactoryImpl::orderLayerTo(const QStringList &orderedLayernames) { DEBUG_LOG(m_logger) << LOGFUNCTION << " START" << ", orderedLayername= " << orderedLayernames.join(QLatin1String(";")); if (orderedLayernames.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " orderedLayername is empty"; return false; } bool success = true; const QStringList &orderedLayernamesCurrent = d->m_orderedMapLayer.layerNamesOrdered(); DEBUG_LOG(m_logger) << LOGFUNCTION << " START" << ", orderedLayernamesCurrent=" << orderedLayernamesCurrent.join(QLatin1String(";")); if (orderedLayernamesCurrent != orderedLayernames) { if ((success = d->m_orderedMapLayer.orderLayerTo(orderedLayernames))) reOrderLayer(); else { ERROR_LOG(m_logger) << LOGFUNCTION << " orderLayerTo failed"; ERROR_LOG(m_logger) << LOGFUNCTION << " orderLayerTo failed, cur =" << d->m_orderedMapLayer.layerNamesOrdered().join(QLatin1String(";")); ERROR_LOG(m_logger) << LOGFUNCTION << " orderLayerTo failed, should=" << orderedLayernames.join(QLatin1String(";")); } d->m_mapImpl->emitLayerOrderChanged(); } DEBUG_LOG(m_logger) << LOGFUNCTION << " END, success=" << success << ", orderedLayernamesCurrent=" << orderedLayernamesCurrent.join(QLatin1String(";")); return success; } LayerType LayerFactoryImpl::layerType(const QString &layername) const { LayerType layerType = LayerType::Undefined; MapLayer mapLayer = d->m_orderedMapLayer.getLayer(layername); if (mapLayer.isValid()) layerType = mapLayer.type(); else { ERROR_LOG(m_logger) << LOGFUNCTION << " layer not found" << " layername='" << layername << "'"; } return layerType; } LayerParameter LayerFactoryImpl::layerParameter(const QString &layername) const { LayerParameter lp; QSharedPointer logicalLayer = this->getLogicalLayer(layername); if (logicalLayer) lp = logicalLayer->layerParameter(); else { ERROR_LOG(m_logger) << LOGFUNCTION << " not found" << " layername=" << layername; } return lp; } bool LayerFactoryImpl::setLayerParameter(const QString &layername, const LayerParameter &layerParameter) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " layername=" << layername; bool success = false; QSharedPointer logicalLayer = this->getLogicalLayer(layername); if (logicalLayer) { logicalLayer->setLayerParameter(layerParameter); success = true; } else { ERROR_LOG(m_logger) << LOGFUNCTION << " not found" << " layername=" << layername; } TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " success=" << success; return success; } bool LayerFactoryImpl::setLayerParameter(LayerType layerType, int value, int LayerParameter::*memberPtr) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " layer type=" << int(layerType) << " layer parameter member=" << memberPtr << " value=" << value; const QStringList &layerNameList = d->m_orderedMapLayer.layerNamesOrdered(layerType); bool success = true; for (const QString &layername : layerNameList) { QSharedPointer logicalLayer = this->getLogicalLayer(layername); if (logicalLayer) { LayerParameter layerParamTmp = logicalLayer->layerParameter(); layerParamTmp.*memberPtr = value; logicalLayer->setLayerParameter(layerParamTmp); } else { success = false; ERROR_LOG(m_logger) << LOGFUNCTION << " not found" << ", layername='" << layername << "'"; } } TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " success=" << success; return success; } bool LayerFactoryImpl::setLayerOpacity(LayerType layerType, int opacity) { return setLayerParameter(layerType, opacity, &LayerParameter::opacity); } bool LayerFactoryImpl::setLayerBrightness(LayerType layerType, int brightness) { return setLayerParameter(layerType, brightness, &LayerParameter::brightness); } bool LayerFactoryImpl::setLayerContrast(LayerType layerType, int contrast) { return setLayerParameter(layerType, contrast, &LayerParameter::contrast); } bool LayerFactoryImpl::setLayerGamma(LayerType layerType, int gamma) { return setLayerParameter(layerType, gamma, &LayerParameter::gamma); } void LayerFactoryImpl::setLayersVisible(const QVector &layerTypes, bool visible) { for (auto &logicalLayer : d->m_logicalLayerCont) { if (!layerTypes.contains(logicalLayer->layerType())) continue; LayerParameter layerParamTmp = logicalLayer->layerParameter(); layerParamTmp.visible = visible; logicalLayer->setLayerParameter(layerParamTmp); } } #if ACRGIS_VERSION < 0x640500 // 100.5.0 / Update 5 // deprecated in V100.5, replaced by GeoView::setSelectionProperties() QColor LayerFactoryImpl::selectionColor() const { return d->m_selectionColor; } void LayerFactoryImpl::setSelectionColor(const QColor &selectionColor) { d->m_selectionColor = selectionColor; } #endif // ACRGIS_VERSION bool LayerFactoryImpl::elevationAvailable() const { if (!d->m_mapImpl) return false; return this->elevationSourcesCount() > 0; } Esri::ArcGISRuntime::Surface *LayerFactoryImpl::baseSurface() const { if (!elevationAvailable()) return nullptr; Esri::ArcGISRuntime::Surface *baseSurface = nullptr; switch (d->m_mapImpl->mode()) { case Mode::Mode_2D: baseSurface = d->m_baseSurface; break; case Mode::Mode_3D: { Esri::ArcGISRuntime::Scene *esriScene = d->m_mapImpl->getEsriScene(); if (esriScene && esriScene->baseSurface() ) baseSurface = esriScene->baseSurface(); } break; default: break; } // switch // Verify: if (!baseSurface) { ERROR_LOG(m_logger) << LOGFUNCTION << " no baseSurface"; return nullptr; } if (baseSurface->loadStatus() != Esri::ArcGISRuntime::LoadStatus::Loaded) { ERROR_LOG(m_logger) << LOGFUNCTION << " baseSurface not loaded yet"; return nullptr; } int elevationSourcesCount = -1; Esri::ArcGISRuntime::ElevationSourceListModel* eslm = baseSurface->elevationSources(); if (eslm) elevationSourcesCount = eslm->size(); if (elevationSourcesCount <= 0) { ERROR_LOG(m_logger) << LOGFUNCTION << " no elevationSourcesCount"; return nullptr; } return baseSurface; } int LayerFactoryImpl::elevationSourcesCount(bool checkIfLoaded) const { if (!d->m_mapImpl) return -1; Esri::ArcGISRuntime::ElevationSourceListModel* elevationSourceListModel = nullptr; switch (d->m_mapImpl->mode()) { case Mode::Mode_2D: { if (d->m_baseSurface && d->m_baseSurface->elevationSources()) elevationSourceListModel = d->m_baseSurface->elevationSources(); } break; case Mode::Mode_3D: { if (d->m_mapImpl->getEsriScene() && d->m_mapImpl->getEsriScene()->baseSurface() && d->m_mapImpl->getEsriScene()->baseSurface()->elevationSources() ) elevationSourceListModel = d->m_mapImpl->getEsriScene()->baseSurface()->elevationSources(); } break; default: return false; } // switch if (!elevationSourceListModel) return -1; int elevationSourcesCount = -1; if (checkIfLoaded) { elevationSourcesCount = 0; int idx = 0; const int esmSize = elevationSourceListModel->size(); for (; idx < esmSize; ++idx) { Esri::ArcGISRuntime::ElevationSource *es = elevationSourceListModel->at(idx); if (es && (es->loadStatus() == Esri::ArcGISRuntime::LoadStatus::Loaded)) ++elevationSourcesCount; } // for INFO_LOG(m_logger) << LOGFUNCTION << " elevationSourcesCount = " << esmSize << ", loaded = " << elevationSourcesCount; } else { elevationSourcesCount = elevationSourceListModel->size(); } return elevationSourcesCount; } bool LayerFactoryImpl::elevationSourcesLoaded() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; const int elevationSourcesCount = this->elevationSourcesCount(); const int elevationSourcesCountLoaded = this->elevationSourcesCount(true); return (elevationSourcesCount == elevationSourcesCountLoaded); } QPointer LayerFactoryImpl::locationToElevation() const { QPointer locationToElevationHelper; if (!elevationAvailable()) { ERROR_LOG(m_logger) << LOGFUNCTION << " no elevation available"; return locationToElevationHelper; } Esri::ArcGISRuntime::Surface *baseSurface = this->baseSurface(); if (!baseSurface) { ERROR_LOG(m_logger) << LOGFUNCTION << " no baseSurface"; return locationToElevationHelper; } LayerFactoryImpl *THIS = const_cast(this); locationToElevationHelper = new LocationToElevationHelper(THIS); // QPointer -> parent is ok! if (!locationToElevationHelper->init(d->m_mapImpl, baseSurface, d->m_mapImpl->getEsriSpatialReference())) { ERROR_LOG(m_logger) << LOGFUNCTION << " locationToElevationHelper->init failed"; delete locationToElevationHelper; return nullptr; } return locationToElevationHelper; } Esri::ArcGISRuntime::GraphicsOverlay *LayerFactoryImpl::getGraphicsLayer(const QString &logicalName) { TRACE_LOG(m_logger) << LOGFUNCTION << " START, logicalName='" << logicalName << "'"; Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = nullptr; QSharedPointer logicalLayer = d->getLogicalLayer(logicalName); if (d->m_orderedMapLayer.contains(logicalName, LayerType::Undefined) && logicalLayer) graphicsLayer = logicalLayer->getCreateGraphicalOverlay(TechnicalLayerType::GraphicsLayer); TRACE_LOG(m_logger) << LOGFUNCTION << " END, logicalName='" << logicalName << "'" << " graphicsLayer=" << graphicsLayer; return graphicsLayer; } Esri::ArcGISRuntime::GraphicsOverlay *LayerFactoryImpl::getGraphicsLayerDict(const QString &logicalName) { TRACE_LOG(m_logger) << LOGFUNCTION << " START, logicalName='" << logicalName << "'"; Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = nullptr; QSharedPointer logicalLayer = d->getLogicalLayer(logicalName); if (d->m_orderedMapLayer.contains(logicalName, LayerType::Undefined) && logicalLayer) graphicsLayer = logicalLayer->getCreateGraphicalOverlay(TechnicalLayerType::GraphicsLayer_Dict); TRACE_LOG(m_logger) << LOGFUNCTION << " END, logicalName='" << logicalName << "'" << " graphicsLayer=" << graphicsLayer; return graphicsLayer; } Esri::ArcGISRuntime::GraphicsOverlay *LayerFactoryImpl::getGraphicsLayer3DExtrusion(const QString &logicalName) { TRACE_LOG(m_logger) << LOGFUNCTION << " START, logicalName='" << logicalName << "'"; Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = nullptr; // getGraphicsLayer3DExtrusion -> SurfacePlacement::Relative QSharedPointer logicalLayer = d->getLogicalLayer(logicalName); if (d->m_orderedMapLayer.contains(logicalName, LayerType::Undefined) && logicalLayer) graphicsLayer = logicalLayer->getCreateGraphicalOverlay(TechnicalLayerType::GraphicsLayer_3D_Extrusion); TRACE_LOG(m_logger) << LOGFUNCTION << " END, logicalName='" << logicalName << "'" << " graphicsLayer=" << graphicsLayer; return graphicsLayer; } Esri::ArcGISRuntime::GraphicsOverlay *LayerFactoryImpl::getGraphicsLayer3D_SpAbsolute(const QString &logicalName) { TRACE_LOG(m_logger) << LOGFUNCTION << " START, logicalName='" << logicalName << "'"; Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = nullptr; // getGraphicsLayer3D_SpAbsolute -> SurfacePlacement::Absolute QSharedPointer logicalLayer = d->getLogicalLayer(logicalName); if (d->m_orderedMapLayer.contains(logicalName, LayerType::Undefined) && logicalLayer) graphicsLayer = logicalLayer->getCreateGraphicalOverlay(TechnicalLayerType::GraphicsLayer_3D_Absolute); TRACE_LOG(m_logger) << LOGFUNCTION << " END, logicalName='" << logicalName << "'" << " graphicsLayer=" << graphicsLayer; return graphicsLayer; } QString LayerFactoryImpl::createInternalLayerName(const TechnicalLayerType &technicalLayerType, const QString &logicalLayerName) const { return LogicalLayer::createInternalLayerName(logicalLayerName, technicalLayerType); } QSharedPointer LayerFactoryImpl::getLogicalLayer(const QString &logicalName) const { return d->getLogicalLayer(logicalName); } QSharedPointer LayerFactoryImpl::getCreateLogicalLayer(const QString &logicalName) { if (! d->hasLogicalLayer(logicalName)) d->createLogicalLayer(logicalName); QSharedPointer logicalLayer = d->getLogicalLayer(logicalName); if (logicalLayer) { connect(logicalLayer.data(), &LogicalLayer::layerParameterChanged, this, &LayerFactoryImpl::onLayerParameterChanged); connect(logicalLayer.data(), &LogicalLayer::layerCreatedOnDemand, this, &LayerFactoryImpl::onLayerCreatedOnDemand); connect(logicalLayer.data(), &LogicalLayer::layerDoneLoading, this, &LayerFactoryImpl::onLayerDoneLoading); } else ERROR_LOG(m_logger) << LOGFUNCTION << " getLogicalLayer failed"; return logicalLayer; } bool LayerFactoryImpl::addBasemapInternal(const QString &layername, const QUrl &mapServiceUrl, const BasemapType basemapType, const double minScale, const double maxScale, const QString &displayName, const QString &description, const QImage &previewImage) { if (mapServiceUrl.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " mapServiceUrl empty, can not add layer " << layername << " basemap type: " << basemapType; return false; } if (d->m_orderedMapLayer.contains(layername, LayerType::Undefined)) { ERROR_LOG(m_logger) << LOGFUNCTION << " layername '" << layername << "' already in use"; return false; } //----------------------------------- // ArcGIS Tiled Basemap Layer //----------------------------------- //----------------------------------------------------------------- // Attention: // local URLS -> do NOT specify any userCredentials (main) // external URLS -> DO specify userCredentials (main) //----------------------------------------------------------------- Esri::ArcGISRuntime::Layer *layer = nullptr; if (mapServiceUrl.isLocalFile()) { QUrl finalUrl = this->completeUrl(QLatin1String("BASEMAP"), mapServiceUrl, QLatin1String("/")); INFO_LOG(m_logger) << LOGFUNCTION << " TileCache: finalUrl '" << finalUrl.toString() << "'" << ", finalUrltoLocalFile '" << finalUrl.toLocalFile() << "'"; QString tcFileAbs = finalUrl.toLocalFile(); QFileInfo fi(tcFileAbs); if (!fi.exists(tcFileAbs)) { ERROR_LOG(m_logger) << LOGFUNCTION << " file '" << tcFileAbs << "' doesn't exists"; return false; } if (fi.isDir() || // TileCacheFolder fi.suffix().compare(QLatin1String("tpk"))==0) // TilePackage { Esri::ArcGISRuntime::TileCache *tileCache = new Esri::ArcGISRuntime::TileCache(tcFileAbs, d->m_mapImpl); tileCache->setObjectName(QLatin1String("BasemapTileCache-")+layername); layer = new Esri::ArcGISRuntime::ArcGISTiledLayer(tileCache, d->m_mapImpl); } else if (fi.suffix().compare(QLatin1String("vtpk"))==0) // VectorTilePackage { Esri::ArcGISRuntime::VectorTileCache *vectorTileCache = new Esri::ArcGISRuntime::VectorTileCache(tcFileAbs, d->m_mapImpl); vectorTileCache->setObjectName(QLatin1String("BasemapVectorTileCache-")+layername); layer = new Esri::ArcGISRuntime::ArcGISVectorTiledLayer(vectorTileCache, d->m_mapImpl); } else { // geotiff (often '.tif') Esri::ArcGISRuntime::Raster* raster = new Esri::ArcGISRuntime::Raster(tcFileAbs, d->m_mapImpl); layer = new Esri::ArcGISRuntime::RasterLayer(raster, d->m_mapImpl); // loading of the geotiff may take a while (tile by tile) } } else { INFO_LOG(m_logger) << LOGFUNCTION << " loading url '" << mapServiceUrl.toString() << "'"; layer = new Esri::ArcGISRuntime::ArcGISTiledLayer(mapServiceUrl, d->m_mapImpl); } layer->setName(layername); layer->setObjectName(layer->name()); if (!d->m_orderedMapLayer.addLayer(layer->name(), SituationMap::LayerType::Basemap)) { ERROR_LOG(m_logger) << LOGFUNCTION << " addLayer failed'" << layer->name() << "'"; } QImage previewImageFinal = previewImage; if (previewImageFinal.isNull()) { switch (basemapType) { case BasemapType::Undefined: case BasemapType::Aerial: previewImageFinal = QImage(QLatin1String(":/situationMap/preview_aerial.png")); break; case BasemapType::Topographic: previewImageFinal = QImage(QLatin1String(":/situationMap/preview_topographic.png")); break; } // switch } if (! d->m_basemap) { ERROR_LOG(m_logger) << LOGFUNCTION << " no basemap!!!"; return false; } int basemapCount = -1; if (d->m_basemap->baseLayers()) basemapCount = d->m_basemap->baseLayers()->size(); INFO_LOG(m_logger) << LOGFUNCTION << " basemapCount = " << basemapCount; d->m_basemap->baseLayers()->append(layer); // getLogicalLayer will // - initialize // - do errorHandling // - update Layerparamter on doneLoading // - and add layer to LayerListModel of basemap: QSharedPointer logicalLayer = this->getCreateLogicalLayer(layername); if (logicalLayer.isNull()) { ERROR_LOG(m_logger) << LOGFUNCTION << " getLogicalLayer invalid'" << layername << "'"; } else { // update LayerParameter first: LayerParameter lp = logicalLayer->layerParameter(); lp.url = mapServiceUrl; lp.minScale = minScale; lp.maxScale = maxScale; lp.basemapType = basemapType; lp.displayName = displayName; lp.description = description; lp.previewImage = previewImageFinal; logicalLayer->setLayerParameter(lp); logicalLayer->setLayer(layer); logicalLayer->setLayerType(LayerType::Basemap); logicalLayer->setBasemapType(basemapType); // basemapType -> aerial, topo... } // // load / retryLoad: // if (d->m_esriMap->loadStatus() == Esri::ArcGISRuntime::LoadStatus::FailedToLoad) // d->m_esriMap->retryLoad(); // else // d->m_esriMap->load(); return true; } bool LayerFactoryImpl::removeLayerInternal(const LayerType &layerType, const QString &logicalLayerName) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " layerType=" << layerType << " logicalLayerName=" << logicalLayerName; if (logicalLayerName.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName empty" << logicalLayerName; return false; } if (! d->m_orderedMapLayer.contains(logicalLayerName, layerType)) { ERROR_LOG(m_logger) << LOGFUNCTION << " does not exist: '" << " layerType=" << layerType << " logicalLayerName='" << logicalLayerName << "'"; return false; } if (! d->hasLogicalLayer(logicalLayerName)) { ERROR_LOG(m_logger) << LOGFUNCTION << " does not exist: '" << " layerType=" << layerType << " logicalLayerName='" << logicalLayerName << "'"; } if (! d->m_orderedMapLayer.removeLayer(logicalLayerName)) { ERROR_LOG(m_logger) << LOGFUNCTION << " remove failed: '" << " layerType=" << layerType; return false; } d->removeLogicalLayer(logicalLayerName); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " logicalLayerName='" << logicalLayerName << "'"; return true; } bool LayerFactoryImpl::reOrderLayer() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; DEBUG_LOG(m_logger) << LOGFUNCTION << " orderedList 1: " << d->m_orderedMapLayer.layerNamesOrdered().join(QLatin1String(";")); reOrderLayerBasemap(); reOrderLayerGraphicOverlays(); DEBUG_LOG(m_logger) << LOGFUNCTION << " orderedList 2: " << d->m_orderedMapLayer.layerNamesOrdered().join(QLatin1String(";")); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } bool LayerFactoryImpl::reOrderLayerBasemap() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!(d->m_basemap && d->m_basemap->baseLayers() && ! d->m_basemap->baseLayers()->isEmpty()) ) return false; const QStringList &allLogicalLayerNames = d->m_orderedMapLayer.layerNamesOrdered(); DEBUG_LOG(m_logger) << LOGFUNCTION << " orderedListAll: " << allLogicalLayerNames.join(QLatin1String(";")); const QStringList &basemapLogicalNames = d->m_orderedMapLayer.layerNamesOrdered(LayerType::Basemap); DEBUG_LOG(m_logger) << LOGFUNCTION << " orderedListBasemap: " << basemapLogicalNames.join(QLatin1String(";")); int newIdx = 0; Esri::ArcGISRuntime::LayerListModel *model = d->m_basemap->baseLayers(); for (const QString &basemapName : basemapLogicalNames) { int curIdx = getEsriBasemapLayerIndex(basemapName); if (curIdx != newIdx) { DEBUG_LOG(m_logger) << LOGFUNCTION << " move" << " basemapName ='" << basemapName << "'" << " from " << curIdx << " to " << newIdx << "..."; model->move(curIdx, newIdx); } ++newIdx; } // for TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " orderedList: " << d->m_orderedMapLayer.layerNamesOrdered().join(QLatin1String(";")); return true; } bool LayerFactoryImpl::reOrderLayerGraphicOverlays() { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " orderedList: " << d->m_orderedMapLayer.layerNamesOrdered().join(QLatin1String(";")); if (! d->getEsriGraphicsOverlayListModel()) return false; if (d->getEsriGraphicsOverlayListModel()->isEmpty()) return false; const QStringList &layernamesOrdered = d->m_orderedMapLayer.layerNamesOrdered(); // // reOrder all GraphicOverlays: // DEBUG_LOG(m_logger) << LOGFUNCTION << " layernamesOrdered: " << layernamesOrdered.join(QLatin1String(";")); Esri::ArcGISRuntime::GraphicsOverlayListModel *model = d->getEsriGraphicsOverlayListModel(); DEBUG_LOG(m_logger) << LOGFUNCTION << " model.size() 1= " << model->size(); // N. Thoden, 28.03.2018, V100.2.1 - Consulting with Luke Smallwood: // Using the testFix for the crashOnStartup, the approach of // model->clear(); // // re-append in required new order: // foreach (auto l, newOrderedGraphicsOverlayList) // model->append(l) // fails. -> go back to the initial (move) approach // ==> use MOVE_APPROACH #define MOVE_APPROACH // N. Thoden, 06.04.2018, V100.2.1 - AFTER Consulting with Luke Smallwood: // Scenario: // - start opApp, // - add simple plan/overlay (one ETS point symbol) via Overlay Dialag // (participants can be picked and MapObjectSelection is opened) // - remove plan/overlay via Overlay Dialag // (participants can no longer be picked and MapObjectSelection is opened) // -> EventMouseNavigator::onGetPickingResult gets empty list from ESRI... // ==> toogle back to NOT using MOVE_APPROACH #undef MOVE_APPROACH // N. Thoden, 30.08.2018, V100.3.0 - behaviour is now: // Using the MOVE_APPROACH the picking is working #if ACRGIS_VERSION >= 0x640300 // 100.3.0 / Update 3 # define MOVE_APPROACH #endif #ifdef MOVE_APPROACH // // model->move(curIdx, newIdx); seems to be buggy // (N. Thoden, 29.09.2016, QuartzBeta1) // int newIdx = 0; for (const QString &logicalname : layernamesOrdered) { QSharedPointer ll = d->getLogicalLayer(logicalname); if (ll && ll->basemapType() == BasemapType::Undefined) //BasemapType::Undefined -> GraphicsOverlay { QList > pairs = ll->getCurrentGraphicalOverlaysOrderedWithName(); QListIterator< QPair > it(pairs); while (it.hasNext()) { const QPair &pair = it.next(); const QString &esriLayername = pair.first; Esri::ArcGISRuntime::GraphicsOverlay *graphicalOverlay = pair.second; DEBUG_LOG(m_logger) << LOGFUNCTION << " handle" << " esriLayername='" << esriLayername << "':"; int curIdx = model->indexOf(graphicalOverlay); DEBUG_LOG(m_logger) << LOGFUNCTION << " handle" << " esriLayername='" << esriLayername << "':" << " curIdx=" << curIdx << " -> newIdx="<< newIdx; if (curIdx < 0) { FATAL_LOG(m_logger) << LOGFUNCTION << " esriLayername='" << esriLayername << "':" << " not found"; abort(); // continue; } if (curIdx != newIdx) { DEBUG_LOG(m_logger) << LOGFUNCTION << " move" << " GraphicOverlays =" << " from " << curIdx << " to " << newIdx << "..." ; DEBUG_LOG(m_logger) << LOGFUNCTION << " model.size() 2= " << model->size(); model->move(curIdx, newIdx); DEBUG_LOG(m_logger) << LOGFUNCTION << " model.size() 3= " << model->size(); int retIdx = model->indexOf(graphicalOverlay); if (retIdx != newIdx) { FATAL_LOG(m_logger) << LOGFUNCTION << " esriLayername='" << esriLayername << "':" << " move failed" << " curIdx='" << curIdx << " newIdx='" << newIdx << " retIdx='" << retIdx; abort(); } } ++newIdx; } // while } // if } // for #else // build new order of GraphicsOverlays : QList newOrderedGraphicsOverlayList; foreach (const QString &logicalname, layernamesOrdered) { QSharedPointer ll = d->getLogicalLayer(logicalname); if (ll && ll->basemapType() == BasemapType::Undefined) //BasemapType::Undefined -> GraphicsOverlay { QList > pairs = ll->getCurrentGraphicalOverlaysOrderedWithName(); QListIterator< QPair > it(pairs); while (it.hasNext()) { const QPair &pair = it.next(); QString esriLayername = pair.first; Esri::ArcGISRuntime::GraphicsOverlay *graphicalOverlay = pair.second; DEBUG_LOG(m_logger) << LOGFUNCTION << " handle" << " esriLayername='" << esriLayername << "'"; newOrderedGraphicsOverlayList.append(graphicalOverlay); } // while } // if } // foreach // : build new order of GraphicsOverlays // clear model: model->clear(); // re-append in required new order: foreach (auto l, newOrderedGraphicsOverlayList) model->append(l); #endif // MOVE_APPROACH DEBUG_LOG(m_logger) << LOGFUNCTION << " model.size() 4= " << model->size(); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " orderedList: " << d->m_orderedMapLayer.layerNamesOrdered().join(QLatin1String(";")); return true; } int LayerFactoryImpl::getEsriBasemapLayerIndex(const QString &esriLayername) const { if (!(d->m_basemap && d->m_basemap->baseLayers())) return -1; const Esri::ArcGISRuntime::LayerListModel *model = d->m_basemap->baseLayers(); for (int i = 0; i < model->size(); ++i) { const Esri::ArcGISRuntime::Layer *curLayer = model->at(i); const QString &curName = curLayer->name(); if (QString::compare(esriLayername, curName) == 0) return i; } return -1; } Esri::ArcGISRuntime::Layer *LayerFactoryImpl::getEsriBasemapLayer(const QString &esriLayername) const { int idx = getEsriBasemapLayerIndex(esriLayername); if (idx < 0) return nullptr; // d->m_basemap already checked within 'getEsriBasemapLayerIndex' Esri::ArcGISRuntime::Layer *layerFound = d->m_basemap->baseLayers()->at(idx); return layerFound; } QUrl LayerFactoryImpl::completeUrl(const QString &context, const QUrl &urlIn, const QString &resourceSubdir) { DEBUG_LOG(m_logger) << LOGFUNCTION << " START" << " context='" << context << "'" << ", urlIn='" << urlIn.toString() << " resourceSubdir='" << resourceSubdir << "'"; if (!urlIn.isLocalFile()) { DEBUG_LOG(m_logger) << LOGFUNCTION << " http -> return" << ", url ='" << urlIn.toString(); return urlIn; } // assume it´s a file url: // make absolute path according resourcePath: QString filePathAbs = urlIn.toLocalFile(); if (QDir::isRelativePath(filePathAbs)) filePathAbs = SituationMap::getResourcePath() + resourceSubdir + filePathAbs; if (!QFileInfo::exists(filePathAbs)) { ERROR_LOG(m_logger) << LOGFUNCTION << " context='" << context << "'" << " file '" << filePathAbs << "' doesn't exists"; return urlIn; } DEBUG_LOG(m_logger) << LOGFUNCTION << " END" << " context='" << context << "'" << ", filePathAbs='" << filePathAbs << "'"; return QUrl::fromLocalFile(filePathAbs); } void LayerFactoryImpl::onLayerParameterChanged(const QString &layername, const LayerParameter &newLayerParameter) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " layername='" << layername << "'" << ", newLayerParameter='" << newLayerParameter; d->m_mapImpl->emitLayerParameterChanged(layername, newLayerParameter); } void LayerFactoryImpl::onLayerCreatedOnDemand(const QString &logicalName) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " logicalName='" << logicalName << "'"; this->reOrderLayer(); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " logicalName='" << logicalName << "'"; } void LayerFactoryImpl::onLayerDoneLoading(const QString &logicalName) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " logicalName='" << logicalName << "'"; MapLayer mapLayer = d->m_orderedMapLayer.getLayer(logicalName); if (!mapLayer.isValid()) { ERROR_LOG(m_logger) << LOGFUNCTION << " mapLayer not found" << " layername='" << logicalName << "'"; } if (mapLayer.type() != LayerType::Basemap) return; QSharedPointer layer = d->getLogicalLayer(logicalName); if (!layer) { ERROR_LOG(m_logger) << LOGFUNCTION << " no layer, " << " layername='" << logicalName << "'"; return; } // Bugfix : // REGG-5350 - mit dem normalen Zoom ( Mausrad ) kann man 6 Stufen ansteuern. // Erst nach der Ums... // REGG-5338 - In der Live-Lage ist die Zoomstufe nicht // vollständig nutzbar, das letzte Viertel... // REGG-5317 - Die Lagekarte der 1. Lageanwendung lässt sich nicht bis // zur untersten Begrenzung... // (Hinein)Zoomen nur bis Scale 4000 moeglich... // Tritt mit TA Letzlingen und User OHNE Settings sgelgentlich auf. // Loesungsansatz: Slot-Methoden von synchronen Aufruf zu // reCalculateMinMaxScaleBasedOnLayers befreien und per Timer aufrufen. // // reCalculate minMaxScale even if there is just one Basemap: // // calulcate and set values direct: // reCalculateMinMaxScaleBasedOnLayers(); // : Bugfix // do via timer: d->reCalculateMinMaxScaleTimer->start(); // start == restart TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " logicalName='" << logicalName << "'"; } void LayerFactoryImpl::onElevationSourceDoneLoading() { Esri::ArcGISRuntime::ElevationSource *elevationSource = qobject_cast(sender()); if (elevationSource) { INFO_LOG(m_logger) << LOGFUNCTION << " ElevationSource -> doneLoading: " << " sender= '" << elevationSource->objectName() << "'" << " loadStatus= '" << elevationSource->loadStatus() << "'" << " elevationSourceType= " << elevationSource->elevationSourceType(); INFO_LOG(m_logger) << LOGFUNCTION << " ElevationSources " << elevationSourcesLoaded(); if (elevationSourcesLoaded()) { INFO_LOG(m_logger) << LOGFUNCTION << " ElevationSources completed"; d->m_mapImpl->emitAllElevationSourcesLoaded(); } } else INFO_LOG(m_logger) << LOGFUNCTION << " ElevationSource -> doneLoading"; } void LayerFactoryImpl::onElevationSourceErrorOccured(const Esri::ArcGISRuntime::Error &error) { QString senderName; Esri::ArcGISRuntime::ElevationSource *elevationSource = qobject_cast(sender()); if (elevationSource) senderName = elevationSource->objectName(); ERROR_LOG(m_logger) << LOGFUNCTION << " ElevationSource -> EsriError occured: " << error << ", sender= '" << senderName << "'"; } void LayerFactoryImpl::onSurfaceDoneLoading2D() { QString senderName; Esri::ArcGISRuntime::Surface *surface = qobject_cast(sender()); if (surface) senderName = surface->objectName(); INFO_LOG(m_logger) << LOGFUNCTION << " Surface -> doneLoading: " << " sender= '" << senderName << "'"; } void LayerFactoryImpl::onSurfaceErrorOccured2D(const Esri::ArcGISRuntime::Error &error) { QString senderName; Esri::ArcGISRuntime::Surface *surface = qobject_cast(sender()); if (surface) senderName = surface->objectName(); ERROR_LOG(m_logger) << LOGFUNCTION << " Surface -> EsriError occured: " << error << ", sender= '" << senderName << "'"; } void LayerFactoryImpl::reCalculateMinMaxScaleBasedOnLayers() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; const QStringList &layernames = d->m_orderedMapLayer.layerNamesOrdered(LayerType::Basemap); double minScale = 0; double maxScale = 0; QList minScales; QList maxScales; bool initial = true; for (const QString &layername : layernames) { DEBUG_LOG(m_logger) << LOGFUNCTION << " layername" << layername; QSharedPointer layer = d->getLogicalLayer(layername); if (!layer) { ERROR_LOG(m_logger) << LOGFUNCTION << " no layer, " << " layername='" << layername << "'"; continue; } const LayerParameter &layerParameter = layer->layerParameter(); DEBUG_LOG(m_logger) << LOGFUNCTION << " layername='" << layername << "'" << " minScale=" << layerParameter.minScale << " maxScale=" << layerParameter.maxScale; // already loaded? ( if (!layerParameter.minMaxScaleValid()) { DEBUG_LOG(m_logger) << LOGFUNCTION << " layername" << layername << " not valid"; continue; } double localMinScale = layerParameter.minScale; double localMaxScale = layerParameter.maxScale; minScales.append(static_cast(localMinScale)); maxScales.append(static_cast(localMaxScale)); // first time: set initial values: if (initial) { initial = false; minScale = localMinScale; maxScale = localMaxScale; } else { minScale = std::max(minScale, localMinScale); // use std::max for minScale!!! maxScale = std::min(maxScale, localMaxScale); // use std::min for maxScale!!! } } // for DEBUG_LOG(m_logger) << LOGFUNCTION << " a minScale=" << minScale << " maxScale=" << maxScale; // OGC: scales are optional. In this case use '0': // For MinScale: if (minScales.contains(0)) minScale = 0; // For MaxScale: if (maxScales.contains(0)) maxScale = 0; DEBUG_LOG(m_logger) << LOGFUNCTION << " apply " << " minScale=" << minScale << " maxScale=" << maxScale; // d->m_mapImpl->setMinScale(minScale+1); // why did i wrote this? // d->m_mapImpl->setMaxScale(maxScale+1); // why did i wrote this? d->m_mapImpl->setMinScale(minScale); d->m_mapImpl->setMaxScale(maxScale); } } // namespace SituationMap