881 lines
27 KiB
C++
Executable File
881 lines
27 KiB
C++
Executable File
/**
|
|
* @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 <LtsCoordTrans/lts_coord_trans.h>
|
|
|
|
#include <Map/object_factory.h>
|
|
#include <Map/sight_fan.h>
|
|
|
|
#include <MathUtils/math_const.h>
|
|
#include <MathUtils/simple_helper.h>
|
|
|
|
#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<RB::RubberbandBase> 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<COUNTER_T, ViewshedBase* > m_viewshedMap;
|
|
QMap<COUNTER_T, SightFan*> 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<SituationMap::RB::RubberbandEllipse>(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<SituationMap::RB::RubberbandEllipse>(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<RB::RubberbandMultiPointFeature3D>(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<RB::RubberbandMultiPointFeature3D>(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<RB::RubberbandEllipse *>(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<SituationMap::SightFan>(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<RB::RubberbandPointFeature>(this);
|
|
if (moveRB)
|
|
{
|
|
moveRB->setAutoDelete(true);
|
|
moveRB->enableModificationState();
|
|
moveRB->setCoordinateVector(QVector<QPointF>() << sightFan->center());
|
|
|
|
std::function<bool(SituationMap::RB::RubberbandBase*)> moveSightFan = [this, sightFanId](SituationMap::RB::RubberbandBase* rbBase) -> bool
|
|
{
|
|
SituationMap::RB::RubberbandPointFeature *rubberPFeat = dynamic_cast<SituationMap::RB::RubberbandPointFeature*>(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<RB::RubberbandMultiPointFeature3D *>(rbnd);
|
|
const QString &toolLayerName = d->m_mapImpl->layerFactoryImpl().toolLayer();
|
|
const QVector<QPointF> &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<SightFan> objRef = objFac.create<SituationMap::SightFan>(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<RB::RubberbandPointFeature3D>(this);
|
|
if (moveRB)
|
|
{
|
|
moveRB->setAutoDelete(true);
|
|
moveRB->enableModificationState();
|
|
moveRB->setCoordinateVector(QVector<QPointF>() << sightFan->center());
|
|
|
|
std::function<bool(SituationMap::RB::RubberbandBase*)> moveSightFan3D = [this, sightFanId](SituationMap::RB::RubberbandBase* rbBase) -> bool
|
|
{
|
|
SituationMap::RB::RubberbandPointFeature3D *rubberPFeat = dynamic_cast<SituationMap::RB::RubberbandPointFeature3D*>(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<RB::RubberbandEllipse *>(rbnd);
|
|
|
|
const QVector<QPointF> &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<RB::RubberbandMultiPointFeature3D *>(rbnd);
|
|
|
|
const QVector<QPointF> &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
|