/** * @RDE_HEADER@ * ViewshedGeoProcessing * $Id$ * $HeadURL$ * $Author$ */ // Own #include "viewshed_geo_processing.h" #include "map_impl.h" #include #include #include // ESRI #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Qt #include 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::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 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 inputsIt(inputs); while (inputsIt.hasNext()) { inputsIt.next(); QString value(QLatin1Literal("-")); Esri::ArcGISRuntime::GeoprocessingParameter *gpPara = inputsIt.value(); Esri::ArcGISRuntime::GeoprocessingLinearUnit *gpParaLU = dynamic_cast(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 &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(outputsMap[key]); return processFeatureResults(viewshedResultFeatures); } key = QStringLiteral("Output_raster"); if (outputsMap.contains(key)) { Esri::ArcGISRuntime::GeoprocessingRaster *viewshedResultRaster = static_cast(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(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(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(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