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

527 lines
20 KiB
Plaintext
Executable File

/**
* @RDE_HEADER@
* ViewshedGeoProcessing
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "viewshed_geo_processing.h"
#include "map_impl.h"
#include <GraphicalObjects/graphical_object_path.h>
#include <Map/situation_map_utils.h>
#include <MapImpl/layer_factory_impl.h>
// ESRI
#include <arcgis/Map.h>
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/Graphic.h>
#include <arcgis/GeoprocessingTask.h>
#include <arcgis/GeoprocessingJob.h>
#include <arcgis/GeoprocessingFeatures.h>
#include <arcgis/GeoprocessingRaster.h>
#include <arcgis/GeoprocessingParameter.h>
#include <arcgis/GeoprocessingParameters.h>
#include <arcgis/GeoprocessingResult.h>
#include <arcgis/FeatureCollectionTable.h>
#include <arcgis/Feature.h>
#include <arcgis/SimpleMarkerSymbol.h>
#include <arcgis/SimpleFillSymbol.h>
#include <arcgis/GeoprocessingLinearUnit.h>
#include <arcgis/ImageServiceRaster.h>
#include <arcgis/RasterLayer.h>
// Qt
#include <QDebug>
namespace SituationMap
{
//-----------------------------------------------------------------------------
// class ViewshedBase
//-----------------------------------------------------------------------------
ViewshedGeoProcessing::ViewshedGeoProcessing(const QString &url,
Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer,
const Esri::ArcGISRuntime::SpatialReference &spatialReference,
QObject *parent)
: ViewshedBase(parent)
, m_url(url)
, m_resultsOverlay(graphicsLayer)
, m_srMap(spatialReference)
, m_logger(getLogger("ViewshedGeoProcessing"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
ViewshedGeoProcessing::~ViewshedGeoProcessing()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
clearGraphics();
delete m_viewshedTask;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
bool ViewshedGeoProcessing::init(SituationMap::MapImpl *mapImpl)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (m_url.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " url is empty";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return false;
}
m_mapImpl = mapImpl;
delete m_viewshedTask;
m_viewshedTask = new Esri::ArcGISRuntime::GeoprocessingTask(QUrl(m_url), this);
m_viewshedTask->setObjectName(QLatin1Literal("GeoprocessingTask"));
// Handle errors of GeoprocessingTask:
connect(m_viewshedTask, &Esri::ArcGISRuntime::GeoprocessingTask::errorOccurred,
this, &ViewshedGeoProcessing::onErrorOccuredGeoprocessingTask);
m_initialized = true;
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " m_initialized=" << m_initialized;
return m_initialized;
}
bool ViewshedGeoProcessing::execute(const QPointF &viewshedPoint_map, const double distance, double zOffset)
{
NOTICE_LOG(m_logger) << LOGFUNCTION << " START"
<< " viewshedPoint_map=" << viewshedPoint_map
<< " m_srMap.wkid=" << m_srMap.wkid()
<< " distance=" << distance
<< " zOffset=" << zOffset
<< " m_viewshedInProgress=" << m_viewshedInProgress
<< " url='" << m_url << "'";
// check point is the viewshed point
Esri::ArcGISRuntime::Point esriViewshedPoint(viewshedPoint_map.x(), viewshedPoint_map.y(), m_srMap); // Esri::ArcGISRuntime::SpatialReference::webMercator());
DEBUG_LOG(m_logger) << LOGFUNCTION << " esriViewshedPoint.toJson: " << esriViewshedPoint.toJson();
m_viewshedInProgress = true;
createInputGraphic(esriViewshedPoint);
clearResultGraphics();
// Create a new feature collection table based upon point geometries using the current map view spatial reference
Esri::ArcGISRuntime::FeatureCollectionTable *inputFeatures =
new Esri::ArcGISRuntime::FeatureCollectionTable(QList<Esri::ArcGISRuntime::Field>()
, Esri::ArcGISRuntime::GeometryType::Point
, m_srMap
, this);
// Create a new feature from the feature collection table. It will not have a coordinate location (x,y) yet
Esri::ArcGISRuntime::Feature *inputFeature = inputFeatures->createFeature(this);
// Assign a physical location to the new point feature based upon where the user clicked on the map view
// inputFeature->setGeometry(m_inputOverlay->graphics()->at(0)->geometry());
inputFeature->setGeometry(esriViewshedPoint);
// connect to addFeature status changed signal
connect(inputFeatures, &Esri::ArcGISRuntime::FeatureCollectionTable::addFeatureCompleted, this, [this, inputFeatures, distance, zOffset](QUuid, bool success)
{
INFO_LOG(m_logger) << LOGFUNCTION << " addFeatureCompleted...";
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no success";
return;
}
// Create the parameters that are passed to the used geoprocessing task
Esri::ArcGISRuntime::GeoprocessingParameters viewshedParameters =
Esri::ArcGISRuntime::GeoprocessingParameters(Esri::ArcGISRuntime::GeoprocessingExecutionType::SynchronousExecute); // AsynchronousSubmit | SynchronousExecute
// GeoprocessingExecutionType should match Esri::ArcGISRuntime::GeoprocessingServiceType of LocalGeoprocessingService
// Request the output features to use the same SpatialReference as the map view
viewshedParameters.setOutputSpatialReference(m_srMap);
// Add an input location to the geoprocessing parameters
QMap<QString, Esri::ArcGISRuntime::GeoprocessingParameter *> inputs;
inputs[QLatin1Literal("Input_point_or_polyline_observer_features")] = new Esri::ArcGISRuntime::GeoprocessingFeatures(inputFeatures, this); //QStringLiteral("ViewshedGeoProcessing")
// Radius
inputs[QLatin1Literal("Inner_radius")] = new Esri::ArcGISRuntime::GeoprocessingLinearUnit(100.0, Esri::ArcGISRuntime::GeoprocessingLinearUnits::Meter, this);
inputs[QLatin1Literal("Outer_radius")] = new Esri::ArcGISRuntime::GeoprocessingLinearUnit(distance, Esri::ArcGISRuntime::GeoprocessingLinearUnits::Meter, this);
// Observer
// //!!! inputs[QLatin1Literal("Observer_elevation")] = new Esri::ArcGISRuntime::GeoprocessingLinearUnit(0.0, Esri::ArcGISRuntime::GeoprocessingLinearUnits::Meter, this);
inputs[QLatin1Literal("Observer_offset")] = new Esri::ArcGISRuntime::GeoprocessingLinearUnit(zOffset, Esri::ArcGISRuntime::GeoprocessingLinearUnits::Meter, this);
// Target
inputs[QLatin1Literal("Surface_offset")] = new Esri::ArcGISRuntime::GeoprocessingLinearUnit(0.0, Esri::ArcGISRuntime::GeoprocessingLinearUnits::Meter, this);
// setInputs
viewshedParameters.setInputs(inputs);
// Print geoprocessingParameters for debugging purpose:
QMapIterator<QString, Esri::ArcGISRuntime::GeoprocessingParameter *> inputsIt(inputs);
while (inputsIt.hasNext())
{
inputsIt.next();
QString value(QLatin1Literal("-"));
Esri::ArcGISRuntime::GeoprocessingParameter *gpPara = inputsIt.value();
Esri::ArcGISRuntime::GeoprocessingLinearUnit *gpParaLU = dynamic_cast<Esri::ArcGISRuntime::GeoprocessingLinearUnit *>(gpPara);
if (gpParaLU)
value = QString(QLatin1Literal("%1 %2")).arg(gpParaLU->distance()).arg(gpParaLU->linearUnit().name());
NOTICE_LOG(m_logger) << LOGFUNCTION << " key= '" << inputsIt.key() << "'" << " value= '" << value << "'";
} // while
// Create the job that handles the communication between the application and the geoprocessing task
m_viewshedJob = m_viewshedTask->createJob(viewshedParameters);
m_viewshedJob->setObjectName(QLatin1Literal("GeoprocessingJob"));
// Handle errors of GeoprocessingJob:
connect(m_viewshedJob, &Esri::ArcGISRuntime::GeoprocessingJob::errorOccurred, this, &ViewshedGeoProcessing::onErrorOccuredGeoprocessingJob);
// Create signal handler for the job
connect(m_viewshedJob, &Esri::ArcGISRuntime::GeoprocessingJob::jobStatusChanged, this, &ViewshedGeoProcessing::onJobStatusChanged);
// start the job
m_viewshedJob->start();
});
// Add the new feature with (x,y) location to the feature collection table
inputFeatures->addFeature(inputFeature);
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " m_viewshedInProgress=" << m_viewshedInProgress;
return true;
}
void ViewshedGeoProcessing::onJobStatusChanged()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
// qCritical() << "jobStatusChanged... viewshedJob->jobStatus()=" << (int)viewshedJob->jobStatus();
switch (m_viewshedJob->jobStatus())
{
case Esri::ArcGISRuntime::JobStatus::NotStarted:
qCritical() << "Geoprocessing Task failed";
m_viewshedInProgress = false;
m_jobStatus = QLatin1Literal("Job NotStarted");
break;
case Esri::ArcGISRuntime::JobStatus::Failed:
{
qCritical() << "Geoprocessing Task failed";
const Esri::ArcGISRuntime::Error &err = m_viewshedJob->error();
qCritical() << "Geoprocessing Task failed message: "
<< " code = " << err.code()
<< " domain = " << (int)err.domain()
<< " message = " << err.message()
<< " additionalMessage = " << err.additionalMessage()
<< " extendedErrorType = " << (int)err.extendedErrorType();
m_viewshedInProgress = false;
m_jobStatus = QLatin1Literal("Job failed");
}
break;
case Esri::ArcGISRuntime::JobStatus::Started:
m_viewshedInProgress = true;
m_jobStatus = QLatin1Literal("Job in progress...");
break;
case Esri::ArcGISRuntime::JobStatus::Paused:
m_viewshedInProgress = false;
m_jobStatus = QLatin1Literal("Job paused...");
break;
case Esri::ArcGISRuntime::JobStatus::Succeeded:
m_viewshedInProgress = false;
m_jobStatus = QLatin1Literal("Job succeeded");
// handle the results
processResults(m_viewshedJob->result());
break;
default:
break;
}
NOTICE_LOG(m_logger) << LOGFUNCTION << " jobStatusChanged is now (m_jobStatus): " << m_jobStatus;
// emit signals
// emit viewshedInProgressChanged();
// emit jobStatusChanged();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
bool ViewshedGeoProcessing::processResults(Esri::ArcGISRuntime::GeoprocessingResult *results)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!results)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no results";
return false;
}
const QMap<QString, Esri::ArcGISRuntime::GeoprocessingParameter *> &outputsMap = results->outputs();
if (outputsMap.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no keys, outputsMap is empty ";
return false;
}
// clear graphics:
clearResultGraphics();
QString key;
key = QStringLiteral("Output_polygon_features");
if (outputsMap.contains(key))
{
// Get the results from the outputs as GeoprocessingFeatures
Esri::ArcGISRuntime::GeoprocessingFeatures *viewshedResultFeatures =
static_cast<Esri::ArcGISRuntime::GeoprocessingFeatures *>(outputsMap[key]);
return processFeatureResults(viewshedResultFeatures);
}
key = QStringLiteral("Output_raster");
if (outputsMap.contains(key))
{
Esri::ArcGISRuntime::GeoprocessingRaster *viewshedResultRaster =
static_cast<Esri::ArcGISRuntime::GeoprocessingRaster *>(outputsMap[key]);
return processRasterResults(viewshedResultRaster);
}
key = QStringLiteral("Output_polygon_features");
if (outputsMap.contains(key))
{
// Get the results from the outputs as GeoprocessingFeatures
Esri::ArcGISRuntime::GeoprocessingFeatures *viewshedResultFeatures =
static_cast<Esri::ArcGISRuntime::GeoprocessingFeatures *>(outputsMap[key]);
return processFeatureResults(viewshedResultFeatures);
}
ERROR_LOG(m_logger) << LOGFUNCTION << " no adeqaute keyword found in outputsMap";
return false;
}
bool ViewshedGeoProcessing::processFeatureResults(Esri::ArcGISRuntime::GeoprocessingFeatures *viewshedResultFeatures)
{
NOTICE_LOG(m_logger) << LOGFUNCTION << " processFeatureResults";
if (!viewshedResultFeatures)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no viewshedResultFeatures";
return false;
}
Esri::ArcGISRuntime::SimpleFillSymbol sfsVisi (Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, QColor(0, 226, 0, 255), this);
Esri::ArcGISRuntime::SimpleFillSymbol sfsInVisi(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, QColor(226, 0, 0, 255), this);
// Add all the features from the result feature set as a graphics to the map
Esri::ArcGISRuntime::FeatureIterator features = viewshedResultFeatures->features()->iterator();
while (features.hasNext())
{
Esri::ArcGISRuntime::Feature *feat = features.next(this);
const QString &attKey(QLatin1String("gridcode"));
if (feat->attributes()->containsAttribute(attKey))
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " attributes()->attributeValue=== " << feat->attributes()->attributeValue(attKey).toString();
if (feat->attributes()->attributeValue(attKey).toInt())
m_resultGraphics.append(new Esri::ArcGISRuntime::Graphic(feat->geometry(), &sfsVisi));
else
m_resultGraphics.append(new Esri::ArcGISRuntime::Graphic(feat->geometry(), &sfsInVisi));
// ERROR_LOG(m_logger) << LOGFUNCTION << " attributeValue " << feat->attributes()->attributeValue(attKey).toInt();
} else
ERROR_LOG(m_logger) << LOGFUNCTION << " no key attributeValue ";
}
// transfer from own container to resultsOverlay:
m_resultsOverlay->graphics()->append(m_resultGraphics);
return true;
}
bool ViewshedGeoProcessing::processRasterResults(Esri::ArcGISRuntime::GeoprocessingRaster *viewshedResultRaster)
{
NOTICE_LOG(m_logger) << LOGFUNCTION << " viewshedResultRaster -> url = " << viewshedResultRaster->url().toString();
connect(viewshedResultRaster, &Esri::ArcGISRuntime::GeoprocessingRaster::fetchFileCompleted, this, [this] (QUuid taskId, const QString& filePath) {
NOTICE_LOG(m_logger) << LOGFUNCTION << " filePath=" << filePath;
Esri::ArcGISRuntime::Raster *raster = new Esri::ArcGISRuntime::Raster(filePath, this);
if (!raster)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " raster failed";
return;
}
connect(raster, &Esri::ArcGISRuntime::Raster::errorOccurred, this, [this, raster] (const Esri::ArcGISRuntime::Error &error) {
ERROR_LOG(m_logger) << LOGFUNCTION << " raster EsriError occured: "
<< ", code=" << error.code()
<< ", domain=" << (int)error.domain()
<< ", extendedErrorType=" << (int)error.extendedErrorType()
<< ", message='" << error.message() << "'"
<< ", additionalMessage='" << error.additionalMessage() << "'";
});
connect(raster, &Esri::ArcGISRuntime::Raster::loadStatusChanged, this, [this, raster] (Esri::ArcGISRuntime::LoadStatus loadStatus) {
ERROR_LOG(m_logger) << LOGFUNCTION << " raster loadStatus=" << loadStatus;
if (loadStatus == Esri::ArcGISRuntime::LoadStatus::Loaded)
{
Esri::ArcGISRuntime::RasterLayer *rl = new Esri::ArcGISRuntime::RasterLayer(raster, this);
if (!rl)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " rl failed";
return;
}
connect(rl, &Esri::ArcGISRuntime::RasterLayer::errorOccurred, this, [this] (const Esri::ArcGISRuntime::Error &error) {
ERROR_LOG(m_logger) << LOGFUNCTION << " RasterLayer EsriError occured: "
<< ", code=" << error.code()
<< ", domain=" << (int)error.domain()
<< ", extendedErrorType=" << (int)error.extendedErrorType()
<< ", message='" << error.message() << "'"
<< ", additionalMessage='" << error.additionalMessage() << "'";
});
connect(rl, &Esri::ArcGISRuntime::RasterLayer::loadStatusChanged, this, [this] (Esri::ArcGISRuntime::LoadStatus loadStatus) {
ERROR_LOG(m_logger) << LOGFUNCTION << " RasterLayer loadStatus=" << loadStatus;
});
NOTICE_LOG(m_logger) << LOGFUNCTION << " getEsriMap()->operationalLayers()->size" << m_mapImpl->getEsriMap()->operationalLayers()->size();
m_mapImpl->getEsriMap()->operationalLayers()->append(rl);
NOTICE_LOG(m_logger) << LOGFUNCTION << " getEsriMap()->operationalLayers()->size" << m_mapImpl->getEsriMap()->operationalLayers()->size();
NOTICE_LOG(m_logger) << LOGFUNCTION << " rasterLayer loading ...";
rl->load();
}
});
NOTICE_LOG(m_logger) << LOGFUNCTION << " raster loading ...";
raster->load();
});
Esri::ArcGISRuntime::TaskWatcher tw = viewshedResultRaster->fetchFile(QString(QLatin1Literal("/tmp/viewshedFetched.tif")));
if (!tw.isValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " m_mapImpl failed";
return false;
}
return true;
}
void ViewshedGeoProcessing::clearGraphics()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << this;
clearInputGraphics();
clearResultGraphics();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void ViewshedGeoProcessing::createInputGraphic(const Esri::ArcGISRuntime::Point &origin)
{
if (!m_originGraphic)
{
m_originGraphic = new Esri::ArcGISRuntime::Graphic(this);
m_resultsOverlay->graphics()->append(m_originGraphic);
}
m_originGraphic->setGeometry(origin);
Esri::ArcGISRuntime::SimpleMarkerSymbol sms(Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle, QColor("red"), 12.0);
m_originGraphic->setSymbol(&sms);
m_originGraphic->setZIndex(32000);
m_resultsOverlay->graphics()->append(m_originGraphic);
}
void ViewshedGeoProcessing::clearInputGraphics()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << this;
if (m_originGraphic)
{
m_resultsOverlay->graphics()->removeOne(m_originGraphic);
delete m_originGraphic;
m_originGraphic = nullptr;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void ViewshedGeoProcessing::clearResultGraphics()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << this;
for (auto graphic : qAsConst(m_resultGraphics))
{
m_resultsOverlay->graphics()->removeOne(graphic);
delete graphic;
}
m_resultGraphics.clear();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void ViewshedGeoProcessing::onErrorOccuredGeoprocessingTask(const Esri::ArcGISRuntime::Error &error)
{
QString senderName;
Esri::ArcGISRuntime::GeoprocessingTask *senderObj = qobject_cast<Esri::ArcGISRuntime::GeoprocessingTask *>(sender());
if (senderObj)
senderName = senderObj->objectName();
ERROR_LOG(m_logger) << LOGFUNCTION << " GeoprocessingTask -> EsriError occured: "
<< " sender= '" << senderName << "'"
<< ", code=" << error.code()
<< ", domain=" << (int)error.domain()
<< ", extendedErrorType=" << (int)error.extendedErrorType()
<< ", message='" << error.message() << "'"
<< ", additionalMessage='" << error.additionalMessage() << "'";
}
void ViewshedGeoProcessing::onErrorOccuredGeoprocessingJob(const Esri::ArcGISRuntime::Error &error)
{
QString senderName;
Esri::ArcGISRuntime::GeoprocessingJob *senderObj = qobject_cast<Esri::ArcGISRuntime::GeoprocessingJob *>(sender());
if (senderObj)
senderName = senderObj->objectName();
ERROR_LOG(m_logger) << LOGFUNCTION << " GeoprocessingJob -> EsriError occured: "
<< " sender= '" << senderName << "'"
<< ", code=" << error.code()
<< ", domain=" << (int)error.domain()
<< ", extendedErrorType=" << (int)error.extendedErrorType()
<< ", message='" << error.message() << "'"
<< ", additionalMessage='" << error.additionalMessage() << "'";
}
} // namespace SituationMap