Files
qt5-legatus-legacy/Lts/Viewer/SituationMap/MapImpl/layer_factory_impl.cpp
T
2026-08-13 18:33:10 +02:00

3040 lines
106 KiB
C++
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @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 <Map/situation_map_utils.h>
#include "logical_layer.h"
#include "location_to_elevation_helper.h"
//#include "tms_layer.h"
#include <Common/resource_path.h> // for getResourcePath
#include <SymbologyImpl/symbol_dictionary_impl.h>
#include <ElevationMap/elevation_map_manager.h>
// ESRI
#include <arcgis/arcgisversion.h> // This file is created by RME, not ESRI
#include <arcgis/Map.h>
#include <arcgis/Layer.h>
#include <arcgis/TileCache.h>
#include <arcgis/ArcGISTiledLayer.h>
#include <arcgis/Raster.h>
#include <arcgis/RasterLayer.h>
#include <arcgis/Scene.h>
#include <arcgis/ArcGISSceneLayer.h>
#include <arcgis/RasterElevationSource.h>
#include <arcgis/ArcGISTiledElevationSource.h>
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/MapGraphicsView.h>
#include <arcgis/SimpleFillSymbol.h>
#include <arcgis/SimpleMarkerSymbol.h>
#include <arcgis/PictureMarkerSymbol.h>
#include <arcgis/SimpleMarkerSceneSymbol.h>
#include <arcgis/UniqueValueRenderer.h>
#include <arcgis/ArcGISVectorTiledLayer.h>
#include <arcgis/ArcGISMapImageLayer.h>
// Qt
#include <QDir>
#include <QEventLoop>
#include <QFileInfo>
#include <QNetworkProxy>
#include <QVector>
#include <QCoreApplication>
#include <iostream>
// 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<Esri::ArcGISRuntime::ArcGISSceneLayer> m_sceneLayer;
QPointer<Esri::ArcGISRuntime::Scene> 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<Item> &vec = m_arcGISSceneLayerCache[itemName];
// search free item:
QMutableVectorIterator<Item> 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<QString, QVector<Item>> 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<QUrl> 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<LogicalLayer> ll = QSharedPointer<LogicalLayer>(new LogicalLayer(logicalName));
ll->init(m_mapImpl, layerListModel, graphicsOverlayListModel);
m_logicalLayerCont[logicalName] = ll;
}
QSharedPointer<LogicalLayer> getLogicalLayer(const QString &logicalName) const
{
if (m_logicalLayerCont.contains(logicalName))
return m_logicalLayerCont[logicalName];
return QSharedPointer<LogicalLayer>();
}
void removeLogicalLayer(const QString &logicalName)
{
m_logicalLayerCont.remove(logicalName);
}
QMap<QString, QSharedPointer<LogicalLayer>> 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> 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<AdvancedSymbology::SymbolDictionaryImpl &>(dict);
// keep SymbologyType::None in mind:
Esri::ArcGISRuntime::DictionarySymbolStyle *esriDict = const_cast<Esri::ArcGISRuntime::DictionarySymbolStyle *>(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> 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> 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> 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> 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<LogicalLayer> LayerFactoryImpl::addLayerToMgnt(const QString &logicalLayerName, const LayerType &layerType)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " layerType=" << layerType
<< " logicalLayerName='" << logicalLayerName << "'";
QSharedPointer<LogicalLayer> 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<Esri::ArcGISRuntime::UniqueValue *> 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<QColor> 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<QColor> 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<UniqueValueData> &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<Esri::ArcGISRuntime::UniqueValue *> 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<UniqueValueDataAesCentral> &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<Esri::ArcGISRuntime::UniqueValue *> 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<LayerFactory::UniqueValueDataAesCentral> &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<Esri::ArcGISRuntime::UniqueValue *> 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<double>::max();
double ymin = std::numeric_limits<double>::max();
double xmax = std::numeric_limits<double>::lowest();
double ymax = std::numeric_limits<double>::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<double>::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<Esri::ArcGISRuntime::ArcGISSceneLayer *>(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<Esri::ArcGISRuntime::ArcGISSceneLayer *>(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; idx<size; ++idx)
{
Esri::ArcGISRuntime::Layer *objLayer = layerListModel->at(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<Esri::ArcGISRuntime::ElevationSource*> &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<Esri::ArcGISRuntime::ElevationSource*> elevationSources =
initializeElevationSourcesInternal(sceneElevationServiceUrls);
for (auto item : elevationSources)
esriScene->baseSurface()->elevationSources()->append(item);
return true;
}
QList<Esri::ArcGISRuntime::ElevationSource*> LayerFactoryImpl::initializeElevationSourcesInternal(const QStringList &sceneElevationServiceUrls)
{
if (sceneElevationServiceUrls.isEmpty())
return {};
Esri::ArcGISRuntime::RasterElevationSource* rasterElevationSource =
initializeRasterElevationSourceInternal(sceneElevationServiceUrls);
QList<Esri::ArcGISRuntime::ElevationSource*> 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> 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> 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<LayerFactoryImpl *>(this);
QSharedPointer<LogicalLayer> 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> 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>(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> 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> 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> 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<LayerType> &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<LocationToElevationHelper> LayerFactoryImpl::locationToElevation() const
{
QPointer<LocationToElevationHelper> 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<LayerFactoryImpl*>(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> 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> 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> 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> 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<LogicalLayer> LayerFactoryImpl::getLogicalLayer(const QString &logicalName) const
{
return d->getLogicalLayer(logicalName);
}
QSharedPointer<LogicalLayer> LayerFactoryImpl::getCreateLogicalLayer(const QString &logicalName)
{
if (! d->hasLogicalLayer(logicalName))
d->createLogicalLayer(logicalName);
QSharedPointer<LogicalLayer> 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> 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<LogicalLayer> ll = d->getLogicalLayer(logicalname);
if (ll && ll->basemapType() == BasemapType::Undefined) //BasemapType::Undefined -> GraphicsOverlay
{
QList<QPair<QString, Esri::ArcGISRuntime::GraphicsOverlay*> > pairs =
ll->getCurrentGraphicalOverlaysOrderedWithName();
QListIterator< QPair<QString, Esri::ArcGISRuntime::GraphicsOverlay*> > it(pairs);
while (it.hasNext())
{
const QPair<QString, Esri::ArcGISRuntime::GraphicsOverlay*> &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<Esri::ArcGISRuntime::GraphicsOverlay *> newOrderedGraphicsOverlayList;
foreach (const QString &logicalname, layernamesOrdered)
{
QSharedPointer<LogicalLayer> ll = d->getLogicalLayer(logicalname);
if (ll && ll->basemapType() == BasemapType::Undefined) //BasemapType::Undefined -> GraphicsOverlay
{
QList<QPair<QString, Esri::ArcGISRuntime::GraphicsOverlay *> > pairs =
ll->getCurrentGraphicalOverlaysOrderedWithName();
QListIterator< QPair<QString, Esri::ArcGISRuntime::GraphicsOverlay *> > it(pairs);
while (it.hasNext())
{
const QPair<QString, Esri::ArcGISRuntime::GraphicsOverlay *> &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<LogicalLayer> 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<Esri::ArcGISRuntime::ElevationSource *>(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<Esri::ArcGISRuntime::ElevationSource *>(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<Esri::ArcGISRuntime::Surface *>(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<Esri::ArcGISRuntime::Surface *>(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<int> minScales;
QList<int> maxScales;
bool initial = true;
for (const QString &layername : layernames)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " layername" << layername;
QSharedPointer<LogicalLayer> 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<int>(localMinScale));
maxScales.append(static_cast<int>(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