/** * @RDE_HEADER@ * Interface-Implementation of Viewshed * $Id$ * $HeadURL$ * $Author$ */ // Own #include "viewshed_impl.h" #include "viewshed_analysis.h" #include "viewshed_geo_processing.h" #include "layer_factory_impl.h" #include "coord_translation_functor_impl.h" #include #include #include #include #include #include "Rubberbands/rubberband_factory.h" // Qt namespace SituationMap { class ViewshedImpl::Private { public: Private() = default; ~Private() = default; // ESRI data: // Own data: SituationMap::MapImpl *m_mapImpl = nullptr; QPointer m_rubbandBase = nullptr; QString m_url; Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr; // graphicsLayer used in 2D ViewshedParameter m_viewshedParameter; using COUNTER_T = long; static COUNTER_T m_viewshedCounter; QMap m_viewshedMap; QMap m_sightFanMap; }; // class ViewshedImpl::Private //----------------------------------------------------------------------------- ViewshedImpl::Private::COUNTER_T ViewshedImpl::Private::m_viewshedCounter = 0; //----------------------------------------------------------------------------- // class ViewshedImpl //----------------------------------------------------------------------------- ViewshedImpl::ViewshedImpl(SituationMap::MapImpl *mapImpl) : BaseClass() , m_logger(getLogger("ViewshedImpl")) , d(new Private) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; d->m_mapImpl = mapImpl; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } ViewshedImpl::~ViewshedImpl() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; this->remove(); delete d; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } void ViewshedImpl::setViewshedUrl(const QString &viewshedServiceUrl) { TRACE_LOG(m_logger) << LOGFUNCTION << " START, viewshedServiceUrl='" << viewshedServiceUrl << "'"; d->m_url = viewshedServiceUrl; if (d->m_url.isEmpty()) { // only demo!!! 10.2.6!? http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Elevation/ESRI_Elevation_World/GPServer/Viewshed // only demo!!! V100.2 https://sampleserver6.arcgisonline.com/arcgis/rest/services/Elevation/ESRI_Elevation_World/GPServer/Viewshed d->m_url = QLatin1String("https://sampleserver6.arcgisonline.com/arcgis/rest/services/Elevation/ESRI_Elevation_World/GPServer/Viewshed"); // only demo!!! } TRACE_LOG(m_logger) << LOGFUNCTION << " END, d->m_url'" << d->m_url << "'"; } bool ViewshedImpl::isReady() { init(); bool isReady = (d->m_graphicsLayer) && d->m_mapImpl->isReady(); if (!isReady) { DEBUG_LOG(m_logger) << LOGFUNCTION << " d->m_url.isEmpty() = " << d->m_url.isEmpty() << " d->m_graphicsLayer = " << d->m_graphicsLayer << " d->m_mapImpl->isReady() = " << d->m_mapImpl->isReady(); } return isReady; } bool ViewshedImpl::create() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!isReady()) { ERROR_LOG(m_logger) << LOGFUNCTION << " failed, not ready"; return false; } // remove all former this->remove(); // remove any pending/active rubberbands of our map: RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl); // d->m_rubbandBase; should be cleared now const Mode mode = d->m_mapImpl->mode(); switch (mode) { case Mode::Mode_2D: d->m_rubbandBase = createRubberband2D(); break; case Mode::Mode_3D: d->m_rubbandBase = createRubberband3D(); break; default: ERROR_LOG(m_logger) << LOGFUNCTION << " mode (" << mode << ")" << " does not support viewshed"; return false; } return true; } bool ViewshedImpl::create(double alpha, double innerRadius, double observerHeight, double targetHeight, double beamAngle) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!isReady()) { ERROR_LOG(m_logger) << LOGFUNCTION << " failed, not ready"; return false; } if (alpha < 0.0) alpha = 0.0; if (alpha > 1.0) alpha = 1.0; if (beamAngle < 0.5) beamAngle = 0.5; // remove all former this->remove(); // remove any pending/active rubberbands of our map: RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl); d->m_viewshedParameter.innerRadius = innerRadius; d->m_viewshedParameter.observerHeight = observerHeight; d->m_viewshedParameter.targetHeight = targetHeight; d->m_viewshedParameter.dAngle = beamAngle*MathConst::pi/180.0; // grad to rad d->m_viewshedParameter.alpha = alpha; const Mode mode = d->m_mapImpl->mode(); switch (mode) { case Mode::Mode_2D: d->m_rubbandBase = createSightFanRubberband2D(); break; case Mode::Mode_3D: d->m_rubbandBase = createSightFanRubberband3D(); break; default: ERROR_LOG(m_logger) << LOGFUNCTION << " mode (" << mode << ")" << " does not support viewshed"; return false; } return true; // d->m_rubbandBase; should be cleared now } long ViewshedImpl::create(const ViewshedParameter &viewshedParameter) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " viewshedParameter: " << viewshedParameter; if (!isReady()) { ERROR_LOG(m_logger) << LOGFUNCTION << " failed, not ready"; return -1; } // store current values: d->m_viewshedParameter = viewshedParameter; ViewshedBase *viewshed = nullptr; // GPU-based viewshed (viewshedAnalysis) is today supported for 3D only: const Mode mode = d->m_mapImpl->mode(); switch (mode) { case Mode::Mode_2D: viewshed = create_viewshedGP(d->m_viewshedParameter); break; case Mode::Mode_3D: viewshed = create_viewshedAnalysis(d->m_viewshedParameter); break; default: ERROR_LOG(m_logger) << LOGFUNCTION << " mode (" << mode << ")" << " does not support viewshed"; return -1; } if (!viewshed) { ERROR_LOG(m_logger) << LOGFUNCTION << " creation of viewshed failed" << " mode (" << mode << ")"; return -1; } d->m_viewshedMap[++d->m_viewshedCounter] = viewshed; emit viewshedFinished(d->m_viewshedCounter); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return d->m_viewshedCounter; } // long ViewshedImpl::create(const ViewshedParameter &viewshedParameter) bool ViewshedImpl::modify(long viewshedId, double alpha, double innerRadius, double observerHeight, double targetHeight, double beamAngle) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!isReady()) { ERROR_LOG(m_logger) << LOGFUNCTION << " failed, not ready"; return false; } if (alpha < 0.0) alpha = 0.0; if (alpha > 1.0) alpha = 1.0; if (beamAngle < 0.5) beamAngle = 0.5; if (viewshedId < 0) viewshedId = d->m_viewshedCounter; if (d->m_sightFanMap.contains(viewshedId)) { SightFan *sightFan = d->m_sightFanMap[viewshedId]; d->m_viewshedParameter.innerRadius = innerRadius; d->m_viewshedParameter.observerHeight = observerHeight; d->m_viewshedParameter.targetHeight = targetHeight; d->m_viewshedParameter.dAngle = beamAngle * MathConst::pi / 180.0; // grad to rad d->m_viewshedParameter.noBeams = MathConst::pi_two / d->m_viewshedParameter.dAngle; d->m_viewshedParameter.alpha = alpha; sightFan->setAlpha( d->m_viewshedParameter.alpha ); sightFan->setInnerRadius( d->m_viewshedParameter.innerRadius ); sightFan->setCenterHat( d->m_viewshedParameter.observerHeight ); sightFan->setObjectHat( d->m_viewshedParameter.targetHeight ); sightFan->setdeltaAngle( d->m_viewshedParameter.dAngle ); sightFan->setNoOfFans( d->m_viewshedParameter.noBeams ); sightFan->show(); return true; } return false; } // bool ViewshedImpl::modify(long viewshedId, double alpha, double innerRadius, double observerHeight, double targetHeight, double beamAngle) ViewshedBase *ViewshedImpl::create_viewshedGP(const ViewshedParameter &viewshedParameter) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " viewshedParameter: " << viewshedParameter; if (!d->m_graphicsLayer) { ERROR_LOG(m_logger) << LOGFUNCTION << " no graphicsLayer for viewshed"; return nullptr; } if (d->m_url.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " no url for viewshed"; return nullptr; } ViewshedGeoProcessing *viewshed = new ViewshedGeoProcessing(d->m_url, d->m_graphicsLayer, d->m_mapImpl->getEsriSpatialReference()); if (!viewshed->init(d->m_mapImpl)) { ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; delete viewshed; return nullptr; } // coordTranslation: CoordTranslationFunctor *coordTransFunctor = d->m_mapImpl->coordTranslationFunctor(); const QPointF &viewshedPointTranslated = coordTransFunctor ? coordTransFunctor->geographicToMap(viewshedParameter.viewshedOrigin_latLon) : QPointF(); DEBUG_LOG(m_logger) << LOGFUNCTION << " viewshedParameter= " << viewshedParameter; if (!viewshed->execute(viewshedPointTranslated, // Map-Coord viewshedParameter.outerRadius, viewshedParameter.observerHeight)) { ERROR_LOG(m_logger) << LOGFUNCTION << " executing GeoProcessing viewshed failed."; delete viewshed; return nullptr; } return viewshed; } // ViewshedBase *ViewshedImpl::create_viewshedGP(const ViewshedParameter &viewshedParameter) ViewshedBase *ViewshedImpl::create_viewshedAnalysis(const ViewshedParameter &viewshedParameter) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " viewshedParameter: " << viewshedParameter; if (!d->m_mapImpl->getEsriSceneGraphicsView()) { ERROR_LOG(m_logger) << LOGFUNCTION << " getEsriSceneGraphicsView failed for viewsheds"; return nullptr; } ViewshedAnalysis *viewshed = new ViewshedAnalysis(d->m_mapImpl->getEsriSceneGraphicsView()); // rad to degree const double factor = 180 / MathConst::pi; const QPointF ¢erDegree = viewshedParameter.viewshedOrigin_latLon * factor; if (!viewshed->execute(centerDegree, // degree viewshedParameter.outerRadius, viewshedParameter.observerHeight)) { ERROR_LOG(m_logger) << LOGFUNCTION << " executing analysis viewshed failed."; delete viewshed; return nullptr; } return viewshed; } // ViewshedBase *ViewshedImpl::create_viewshedAnalysis(const ViewshedParameter &viewshedParameter) void ViewshedImpl::remove(long viewshedId) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " viewshedId=" << viewshedId; //if (viewshedId < 0) { // ok, not special viewshed is adressed --> remove all for (ViewshedBase *viewshed : qAsConst(d->m_viewshedMap)) delete viewshed; d->m_viewshedMap.clear(); for (SightFan *sightFan : qAsConst(d->m_sightFanMap)) { if (sightFan) sightFan->removeMySelf(); } d->m_sightFanMap.clear(); return; } if (d->m_viewshedMap.contains(viewshedId)) { delete d->m_viewshedMap[viewshedId]; d->m_viewshedMap.remove(viewshedId); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " viewshedId=" << viewshedId; return; } if (d->m_sightFanMap.contains(viewshedId)) { d->m_sightFanMap[viewshedId]->removeMySelf(); d->m_sightFanMap[viewshedId] = nullptr; d->m_sightFanMap.remove(viewshedId); TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " viewshedId=" << viewshedId; return; } ERROR_LOG(m_logger) << LOGFUNCTION << " viewShedId not found, id=" << viewshedId; } ViewshedParameter ViewshedImpl::getViewshedParameter(long viewshedId, bool *ok) const { if (ok) *ok = false; if (viewshedId < 0) viewshedId = d->m_viewshedCounter; if (d->m_sightFanMap.contains(viewshedId)) { const SightFan *sightFan = d->m_sightFanMap[viewshedId]; if (sightFan) { ViewshedParameter vp; vp.viewshedOrigin_latLon = sightFan->center(); vp.observerHeight = sightFan->centerHat(); vp.targetHeight = sightFan->objectHat(); vp.innerRadius = sightFan->innerRadius(); vp.outerRadius = sightFan->radius(); vp.startAngle = sightFan->startAngle(); vp.dAngle = sightFan->dAngle(); vp.alpha = sightFan->alpha(); vp.noBeams = sightFan->noOfFans(); if (ok) *ok = true; return vp; } } return d->m_viewshedParameter; } // const ViewshedParameter & ViewshedImpl::getViewshedParameter(long viewshedId = -1, bool *ok = nullptr) void ViewshedImpl::init() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!d->m_graphicsLayer) { LayerFactoryImpl &layerFactory = d->m_mapImpl->layerFactoryImpl(); const QString &toolsLayername = layerFactory.toolLayer(); d->m_graphicsLayer = layerFactory.getGraphicsLayer(toolsLayername); } TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } RB::RubberbandEllipse *ViewshedImpl::createRubberband2D() { // remove any pending/active rubberbands of our map: RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl); RB::RubberbandEllipse *rubberband = d->m_mapImpl->rubberbandFactory().create(this); if (!rubberband || !rubberband->rubberbandIsAlive()) { ERROR_LOG(m_logger) << LOGFUNCTION << " create2D failed"; return nullptr; } rubberband->setAutoDelete(true); rubberband->constrainQuadratic(true); rubberband->constrainOrthogonal(true); // rubberband->minWidth(radiusMin * 2); // Should be 2*radiusMin // rubberband->maxWidth(radiusMax * 2); // Should be 2*radiusMax connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &ViewshedImpl::onRbAbort, Qt::UniqueConnection); connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &ViewshedImpl::onRb2DApply, Qt::UniqueConnection); return rubberband; } RB::RubberbandEllipse *ViewshedImpl::createSightFanRubberband2D() { // remove any pending/active rubberbands of our map: RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl); RB::RubberbandEllipse *rubberband = d->m_mapImpl->rubberbandFactory().create(this); if (!rubberband || !rubberband->rubberbandIsAlive()) { ERROR_LOG(m_logger) << LOGFUNCTION << " create2D failed"; return nullptr; } rubberband->setAutoDelete(true); rubberband->constrainQuadratic(true); rubberband->constrainOrthogonal(true); rubberband->enableSector(false); // set enableSector to true to allow sector sight fans instead of full circles rubberband->minWidth(d->m_viewshedParameter.innerRadius * 3); // radius should be larger than innerRadius // rubberband->maxWidth(radiusMax * 2); // Should be 2*radiusMax connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &ViewshedImpl::onRbAbort, Qt::UniqueConnection); connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &ViewshedImpl::onSightFanRb2DApply, Qt::UniqueConnection); return rubberband; } // RB::RubberbandEllipse *ViewshedImpl::createSightFanRubberband2D() RB::RubberbandMultiPointFeature3D *ViewshedImpl::createSightFanRubberband3D() { // remove any pending/active rubberbands of our map: RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl); RB::RubberbandMultiPointFeature3D *rubberband = d->m_mapImpl->rubberbandFactory().create3D(this); if (!rubberband || !rubberband->rubberbandIsAlive()) { ERROR_LOG(m_logger) << LOGFUNCTION << " create3D failed"; return nullptr; } rubberband->setAutoDelete(true); rubberband->enableCircular(true); // rubberband->constrainQuadratic(true); // rubberband->constrainOrthogonal(true); connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &ViewshedImpl::onRbAbort, Qt::UniqueConnection); connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &ViewshedImpl::onSightFanRb3DApply, Qt::UniqueConnection); return rubberband; } // RB::RubberbandMultiPointFeature3D *ViewshedImpl::createSightFanRubberband3D() RB::RubberbandMultiPointFeature3D *ViewshedImpl::createRubberband3D() { // remove any pending/active rubberbands of our map: RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl); RB::RubberbandMultiPointFeature3D *rubberband = d->m_mapImpl->rubberbandFactory().create3D(this); if (!rubberband || !rubberband->rubberbandIsAlive()) { ERROR_LOG(m_logger) << LOGFUNCTION << " create3D failed"; return nullptr; } rubberband->setAutoDelete(true); rubberband->enableCircular(true); // rubberband->constrainQuadratic(true); // rubberband->constrainOrthogonal(true); connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &ViewshedImpl::onRbAbort, Qt::UniqueConnection); connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &ViewshedImpl::onSightFanRb3DApply, Qt::UniqueConnection); return rubberband; } void ViewshedImpl::onRbAbort(RB::RubberbandBase *rbnd) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!rbnd) return; DEBUG_LOG(m_logger) << LOGFUNCTION << " rb abort"; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } void ViewshedImpl::onSightFanRb2DApply(RB::RubberbandBase *rbnd) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!rbnd) { ERROR_LOG(m_logger) << LOGFUNCTION << " no rubberband"; return; } RB::RubberbandEllipse *ellipse = static_cast(rbnd); const QString &toolLayerName = d->m_mapImpl->layerFactoryImpl().toolLayer(); double diameter; double dummy; QPointF direction; ellipse->rectangleParameter( d->m_viewshedParameter.viewshedOrigin_latLon, diameter, dummy, direction ); if (diameter > 0.0) { d->m_viewshedParameter.outerRadius = diameter * 0.5; if (ellipse->hasSector()) { double startAngle = SituationMap::MathUtils::getMathAngle(ellipse->sectorStart()); double endAngle = SituationMap::MathUtils::getMathAngle(ellipse->sectorEnd()); if (startAngle > endAngle) { startAngle -= MathConst::pi_two; } d->m_viewshedParameter.noBeams = ceil( (endAngle - startAngle) / d->m_viewshedParameter.dAngle ); d->m_viewshedParameter.startAngle = startAngle; } else { d->m_viewshedParameter.startAngle = 0.0; d->m_viewshedParameter.noBeams = MathConst::pi_two / d->m_viewshedParameter.dAngle; } ObjectFactory &objFac = d->m_mapImpl->objectFactory(); SightFan *sightFan = objFac.create(toolLayerName).take(); if (sightFan) { sightFan->initFan( d->m_viewshedParameter.viewshedOrigin_latLon , d->m_viewshedParameter.observerHeight , d->m_viewshedParameter.targetHeight , d->m_viewshedParameter.outerRadius , d->m_viewshedParameter.innerRadius , d->m_viewshedParameter.startAngle , d->m_viewshedParameter.dAngle , d->m_viewshedParameter.noBeams , d->m_viewshedParameter.alpha ); long sightFanId = ++d->m_viewshedCounter; d->m_sightFanMap[sightFanId] = sightFan; emit viewshedFinished(sightFanId); // create a RubberbandPointFeature to let the user move around the sight fan RB::RubberbandPointFeature *moveRB = d->m_mapImpl->rubberbandFactory().create(this); if (moveRB) { moveRB->setAutoDelete(true); moveRB->enableModificationState(); moveRB->setCoordinateVector(QVector() << sightFan->center()); std::function moveSightFan = [this, sightFanId](SituationMap::RB::RubberbandBase* rbBase) -> bool { SituationMap::RB::RubberbandPointFeature *rubberPFeat = dynamic_cast(rbBase); SightFan * sightFan = d->m_sightFanMap[sightFanId]; if (nullptr == sightFan) { if (rubberPFeat) rubberPFeat->deleteMySelf(); return false; } if (rubberPFeat) { const QPointF & loc = rubberPFeat->singleCoordinate(); sightFan->move(loc); } return true; }; moveRB->installExternalUpdate(moveSightFan); } } // if sightFan } // if diameter } // void ViewshedImpl::onSightFanRb2DApply(RB::RubberbandBase *rbnd) void ViewshedImpl::onSightFanRb3DApply(RB::RubberbandBase *rbnd) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!rbnd) { ERROR_LOG(m_logger) << LOGFUNCTION << " no rubberband"; return; } RB::RubberbandMultiPointFeature3D *rubberband = static_cast(rbnd); const QString &toolLayerName = d->m_mapImpl->layerFactoryImpl().toolLayer(); const QVector &data = rubberband->coordinateVector(RB::CoordinateSystem::Map); // 3D -> RB::CoordinateSystem::Map -> degree if (data.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed!?"; return; } if (data.size() < 2) { ERROR_LOG(m_logger) << LOGFUNCTION << " insufficiant number of coords"; return; } ViewshedParameter viewshedParameter; // degree to rad const double factor = MathConst::pi / 180.0; const QPointF ¢er = data.at(0) * factor; // viewshedParameter.viewshedOrigin_latLon = center; { static Esri::ArcGISRuntime::GeometryEngine *gEngine = Esri::ArcGISRuntime::GeometryEngine::instance(); const Esri::ArcGISRuntime::SpatialReference &sr = d->m_mapImpl->getEsriSpatialReference(); QPointF coord = data.at(0); Esri::ArcGISRuntime::Point geom1(coord.x(), coord.y(), sr); coord = data.at(1); Esri::ArcGISRuntime::Point geom2(coord.x(), coord.y(), sr); Esri::ArcGISRuntime::GeodeticDistanceResult geodeticDistanceResult = gEngine->distanceGeodetic(geom1, geom2, Esri::ArcGISRuntime::LinearUnitId::Meters, Esri::ArcGISRuntime::AngularUnitId::Degrees, Esri::ArcGISRuntime::GeodeticCurveType::Geodesic); viewshedParameter.outerRadius = geodeticDistanceResult.distance(); } unsigned int noBeams = floor((MathConst::pi_two) / d->m_viewshedParameter.dAngle); ObjectFactory &objFac = d->m_mapImpl->objectFactory(); RefPointer objRef = objFac.create(toolLayerName); SightFan *sightFan = objRef.get(); if (sightFan) { if (d->m_viewshedParameter.innerRadius*1.5 > viewshedParameter.outerRadius) { viewshedParameter.outerRadius = d->m_viewshedParameter.innerRadius*1.5; } sightFan->initFan(center , d->m_viewshedParameter.observerHeight , d->m_viewshedParameter.targetHeight , viewshedParameter.outerRadius , d->m_viewshedParameter.innerRadius , 0.0 , d->m_viewshedParameter.dAngle , noBeams , d->m_viewshedParameter.alpha ); long sightFanId = ++d->m_viewshedCounter; d->m_sightFanMap[sightFanId] = sightFan; emit viewshedFinished(sightFanId); // create a RubberbandPointFeature3D to let the user move around the sight fan RB::RubberbandPointFeature3D *moveRB = d->m_mapImpl->rubberbandFactory().create3D(this); if (moveRB) { moveRB->setAutoDelete(true); moveRB->enableModificationState(); moveRB->setCoordinateVector(QVector() << sightFan->center()); std::function moveSightFan3D = [this, sightFanId](SituationMap::RB::RubberbandBase* rbBase) -> bool { SituationMap::RB::RubberbandPointFeature3D *rubberPFeat = dynamic_cast(rbBase); SightFan * sightFan = d->m_sightFanMap[sightFanId]; if (nullptr == sightFan) { if (rubberPFeat) rubberPFeat->deleteMySelf(); return false; } if (rubberPFeat) { const QPointF & loc = rubberPFeat->singleCoordinate(); sightFan->move(loc); } return true; }; moveRB->installExternalUpdate(moveSightFan3D); } } } // void ViewshedImpl::onSightFanRb3DApply(RB::RubberbandBase *rbnd) void ViewshedImpl::onRb2DApply(RB::RubberbandBase *rbnd) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!rbnd) { ERROR_LOG(m_logger) << LOGFUNCTION << " no rubberband"; return; } RB::RubberbandEllipse *ellipse = static_cast(rbnd); const QVector &data = ellipse->rectangleVerticies(); const LtsCoordTrans::UtmCoordinate p0(LtsCoordTrans::UtmCoordinate::fromGeographic(data.at(0))); const LtsCoordTrans::UtmCoordinate p1(LtsCoordTrans::UtmCoordinate::fromGeographic(data.at(2), p0.zone)); const QPointF ¢er = ellipse->centerPoint(); double radius = fabs((p0.x - p1.x) / 2); // 23.03.2018, N. Thoden // LegatusBremen201803151510.gpk contains dem with cell size of ~ 50m // So the inner_radius has to be >= 50m // And of course: outer_radius > inner_radius // So outerRadius > 60 if (radius < 100.0) radius = 100.0; ViewshedParameter viewshedParameter; viewshedParameter.viewshedOrigin_latLon = center; viewshedParameter.outerRadius = radius; long id = this->create(viewshedParameter); if (id < 0) { ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed!?"; } TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } void ViewshedImpl::onRb3DApply(RB::RubberbandBase *rbnd) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (!rbnd) { ERROR_LOG(m_logger) << LOGFUNCTION << " no rubberband"; return; } RB::RubberbandMultiPointFeature3D *rubberband = static_cast(rbnd); const QVector &data = rubberband->coordinateVector(RB::CoordinateSystem::Map); // 3D -> RB::CoordinateSystem::Map -> degree if (data.isEmpty()) { ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed!?"; return; } if (data.size() < 2) { ERROR_LOG(m_logger) << LOGFUNCTION << " insufficiant number of coords"; return; } ViewshedParameter viewshedParameter; // degree to rad const double factor = MathConst::pi / 180.0; const QPointF ¢er = data.at(0) * factor; viewshedParameter.viewshedOrigin_latLon = center; { static Esri::ArcGISRuntime::GeometryEngine *gEngine = Esri::ArcGISRuntime::GeometryEngine::instance(); const Esri::ArcGISRuntime::SpatialReference &sr = d->m_mapImpl->getEsriSpatialReference(); QPointF coord = data.at(0); Esri::ArcGISRuntime::Point geom1(coord.x(), coord.y(), sr); coord = data.at(1); Esri::ArcGISRuntime::Point geom2(coord.x(), coord.y(), sr); Esri::ArcGISRuntime::GeodeticDistanceResult geodeticDistanceResult = gEngine->distanceGeodetic(geom1, geom2, Esri::ArcGISRuntime::LinearUnitId::Meters, Esri::ArcGISRuntime::AngularUnitId::Degrees, Esri::ArcGISRuntime::GeodeticCurveType::Geodesic); viewshedParameter.outerRadius = geodeticDistanceResult.distance(); } long id = this->create(viewshedParameter); if (id < 0) { ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed!?"; } TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } } // namespace SituationMap