366 lines
12 KiB
C++
Executable File
366 lines
12 KiB
C++
Executable File
/**
|
||
* @RDE_HEADER@
|
||
* TerrainProfileProcessor
|
||
* $Id$
|
||
* $HeadURL$
|
||
* $Author$
|
||
*/
|
||
|
||
// Own
|
||
#include "terrain_profile_processor.h"
|
||
|
||
#include "map_impl.h"
|
||
#include <GraphicalObjects/graphical_object_path.h>
|
||
#include <Map/object_factory.h>
|
||
#include <Map/situation_map_utils.h>
|
||
#include <Map/coord_translation_functor.h>
|
||
#include <MapImpl/layer_factory_impl.h>
|
||
|
||
|
||
|
||
// ESRI
|
||
#include <arcgis/GeometryEngine.h>
|
||
#include <arcgis/PolylineBuilder.h>
|
||
|
||
|
||
// Qt
|
||
|
||
|
||
namespace SituationMap
|
||
{
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// class ViewshedBase
|
||
//-----------------------------------------------------------------------------
|
||
TerrainProfileProcessor::TerrainProfileProcessor(QObject *parent)
|
||
: QObject(parent)
|
||
, m_logger(getLogger("TerrainProfileProcessor"))
|
||
{
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
||
}
|
||
|
||
TerrainProfileProcessor::~TerrainProfileProcessor()
|
||
{
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " START, id=" << m_id;
|
||
removeUserInput();
|
||
|
||
// maybe clear the view of m_terrainProfileDialog here?
|
||
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " END, id=" << m_id;
|
||
}
|
||
|
||
bool TerrainProfileProcessor::init(SituationMap::MapImpl *mapImpl, long id)
|
||
{
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
||
|
||
m_id = id;
|
||
|
||
m_initialized = false;
|
||
|
||
m_mapImpl = mapImpl;
|
||
m_srMap = m_mapImpl->getEsriSpatialReference();
|
||
|
||
LayerFactoryImpl &layerFactoryImpl = m_mapImpl->layerFactoryImpl();
|
||
|
||
m_toolsLayername = layerFactoryImpl.toolLayer();
|
||
|
||
if (!m_surface)
|
||
m_surface = layerFactoryImpl.baseSurface();
|
||
|
||
if (m_surface && m_surface->loadStatus() == Esri::ArcGISRuntime::LoadStatus::Loaded)
|
||
m_initialized = true;
|
||
else
|
||
{
|
||
Esri::ArcGISRuntime::LoadStatus ls = Esri::ArcGISRuntime::LoadStatus::Unknown;
|
||
if (m_surface)
|
||
ls = m_surface->loadStatus();
|
||
ERROR_LOG(m_logger) << LOGFUNCTION
|
||
<< " d->m_surface = " << m_surface
|
||
<< " d->m_surface->loadStatus = " << ls;
|
||
}
|
||
|
||
if (!m_locationToElevationBulk.init(m_mapImpl, m_surface))
|
||
{
|
||
ERROR_LOG(m_logger) << LOGFUNCTION << " locationToElevationBulk.init() failed";
|
||
return false;
|
||
}
|
||
|
||
connect(&m_locationToElevationBulk, &LocationToElevationBulk::locationsToElevationsCompleted,
|
||
this, &TerrainProfileProcessor::onLocationsToElevationsCompleted, Qt::UniqueConnection);
|
||
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " m_initialized=" << m_initialized;
|
||
return m_initialized;
|
||
}
|
||
|
||
bool TerrainProfileProcessor::start(const TerrainProfileParameter &terrainProfileParameter)
|
||
{
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
|
||
<< " terrainProfileParameter=" << terrainProfileParameter
|
||
<< " m_srMap.wkid=" << m_srMap.wkid();
|
||
|
||
// don´t allow more than one at once:
|
||
if (m_inProgress)
|
||
return false;
|
||
|
||
m_inProgress = true;
|
||
|
||
m_terrainProfileParameter = terrainProfileParameter;
|
||
|
||
removeUserInput();
|
||
displayUserInput();
|
||
|
||
// densify
|
||
const Esri::ArcGISRuntime::Polyline &polylineOut_Map = densifiy(m_terrainProfileParameter);
|
||
|
||
// calculate distance
|
||
|
||
// bulk location to elevation
|
||
const Esri::ArcGISRuntime::ImmutablePart &immutablePart = polylineOut_Map.parts().part(0);
|
||
const int size = immutablePart.pointCount();
|
||
for (int i = 0; i < size; ++i)
|
||
{
|
||
const Esri::ArcGISRuntime::Point &esriMapLocation = immutablePart.point(i);
|
||
// DEBUG_LOG(m_logger) << LOGFUNCTION << " esriMapLocation: " << QPointF(esriMapLocation.x(), esriMapLocation.y());
|
||
m_locationToElevationBulk.addLocation(esriMapLocation);
|
||
}
|
||
m_locationToElevationBulk.start();
|
||
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " m_viewshedInProgress=" << m_inProgress;
|
||
return true;
|
||
}
|
||
|
||
QVector<QPair<QPointF, double> > TerrainProfileProcessor::results() const
|
||
{
|
||
return m_locationToElevationBulk.results();
|
||
}
|
||
|
||
void TerrainProfileProcessor::setTerrainProfileDialog(QPointer<TerrainProfileDialog> &terrainProfileDialog)
|
||
{
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
||
m_terrainProfileDialog = terrainProfileDialog;
|
||
|
||
if (m_terrainProfileDialog)
|
||
{
|
||
m_terrainProfileDialog->setMap(m_mapImpl);
|
||
|
||
// mousePositionChanged
|
||
connect(m_terrainProfileDialog.data(), &TerrainProfileDialog::mousePositionChanged,
|
||
this, &TerrainProfileProcessor::onDialogMousePositionChanged, Qt::UniqueConnection);
|
||
|
||
// handle destruction of according dialog:
|
||
connect(terrainProfileDialog.data(), &TerrainProfileDialog::destroyed,
|
||
this, &TerrainProfileProcessor::deleteLater);
|
||
}
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
||
}
|
||
|
||
Esri::ArcGISRuntime::Polyline TerrainProfileProcessor::densifiy(TerrainProfileParameter &terrainProfileParameter)
|
||
{
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
|
||
<< " terrainProfileParameter=" << terrainProfileParameter;
|
||
|
||
//
|
||
// PosCont to EsriPolyline:
|
||
//
|
||
Esri::ArcGISRuntime::PolylineBuilder polylineBuilder(m_srMap, this);
|
||
|
||
for (const Point3D &coord : qAsConst(terrainProfileParameter.m_coordinates))
|
||
{
|
||
// geo to map (polylineBuilder uses map also)
|
||
const Point3D &p3D = m_mapImpl->coordTranslationFunctor()->geographicToMap(coord);
|
||
polylineBuilder.addPoint(Esri::ArcGISRuntime::Point(p3D.x(), p3D.y(), p3D.z()));
|
||
}
|
||
|
||
const Esri::ArcGISRuntime::Polyline &polylineIn = polylineBuilder.toPolyline();
|
||
|
||
terrainProfileParameter.m_length =
|
||
Esri::ArcGISRuntime::GeometryEngine::lengthGeodetic(polylineIn,
|
||
Esri::ArcGISRuntime::LinearUnit::meters(),
|
||
Esri::ArcGISRuntime::GeodeticCurveType::Geodesic);
|
||
const int numberCoords = 50;
|
||
|
||
double maxSegmenthLength = terrainProfileParameter.m_length / numberCoords;
|
||
|
||
INFO_LOG(m_logger) << LOGFUNCTION << " START"
|
||
<< " lengthMeter=" << terrainProfileParameter.m_length
|
||
<< " numberCoords=" << numberCoords
|
||
<< " maxSegmenthLength=" << maxSegmenthLength;
|
||
|
||
const Esri::ArcGISRuntime::Geometry &geometryOut = Esri::ArcGISRuntime::GeometryEngine::densifyGeodetic(polylineIn,
|
||
maxSegmenthLength,
|
||
Esri::ArcGISRuntime::LinearUnit::meters(),
|
||
Esri::ArcGISRuntime::GeodeticCurveType::Geodesic);
|
||
const Esri::ArcGISRuntime::Polyline polylineOut(geometryOut);
|
||
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
|
||
<< " lengthMeter=" << terrainProfileParameter.m_length
|
||
<< " numberCoords=" << numberCoords
|
||
<< " maxSegmenthLength=" << maxSegmenthLength
|
||
<< " polylineOut.parts().part(0).pointCount()=" << polylineOut.parts().part(0).pointCount();
|
||
return polylineOut;
|
||
}
|
||
|
||
void TerrainProfileProcessor::displayUserInput()
|
||
{
|
||
displayInputLine();
|
||
displayInputVertices();
|
||
}
|
||
|
||
void TerrainProfileProcessor::removeUserInput()
|
||
{
|
||
removeInputLine();
|
||
removeInputVertices();
|
||
removeCurrentMarker();
|
||
}
|
||
|
||
void TerrainProfileProcessor::displayInputLine()
|
||
{
|
||
// create
|
||
if (!m_polyline)
|
||
{
|
||
m_polyline = m_mapImpl->objectFactory().create<Polyline>(m_toolsLayername,
|
||
QStringLiteral("TerrainProfileLineId%1").arg(m_id));
|
||
m_polyline->setLineStyle(LineStyle(m_color, 3));
|
||
m_polyline->setScaleEnable(true);
|
||
m_polyline->setPickable(false);
|
||
}
|
||
|
||
// apply data
|
||
m_polyline->setPositions(m_terrainProfileParameter.m_coordinates);
|
||
|
||
// update
|
||
if (!m_polyline->updateView())
|
||
ERROR_LOG(m_logger) << LOGFUNCTION << " polyline::updateView failed";
|
||
}
|
||
void TerrainProfileProcessor::removeInputLine()
|
||
{
|
||
if (m_polyline)
|
||
m_polyline->removeMySelf();
|
||
m_polyline.reset();
|
||
}
|
||
|
||
void TerrainProfileProcessor::displayInputVertices()
|
||
{
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
||
|
||
PositionCont coordinates = m_terrainProfileParameter.m_coordinates;
|
||
const int size = coordinates.size();
|
||
if (size < 1)
|
||
return;
|
||
|
||
m_markerCont.resize(size);
|
||
|
||
int idx = 0;
|
||
QMutableVectorIterator<RefPointer<GeoImage>> it(m_markerCont);
|
||
|
||
while (it.hasNext())
|
||
{
|
||
RefPointer<GeoImage> &marker = it.next();
|
||
if (!marker.valid())
|
||
{
|
||
marker = m_mapImpl->objectFactory().create<GeoImage>(m_toolsLayername,
|
||
QStringLiteral("TerrainProfileLineId%1_idx%2").arg(m_id).arg(idx));
|
||
marker->setProxyMarker(MarkerSymbolType::Circle);
|
||
marker->setProxyMarkerColor(m_color);
|
||
marker->setProxyMarkerSize(7);
|
||
marker->setPickable(false);
|
||
}
|
||
marker->setOrigin(coordinates[idx]);
|
||
|
||
// update
|
||
if (!marker->updateView())
|
||
ERROR_LOG(m_logger) << LOGFUNCTION << " marker::updateView failed";
|
||
|
||
++idx;
|
||
} // while
|
||
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
||
}
|
||
void TerrainProfileProcessor::removeInputVertices()
|
||
{
|
||
QMutableVectorIterator<RefPointer<GeoImage>> it(m_markerCont);
|
||
while (it.hasNext())
|
||
{
|
||
RefPointer<GeoImage> &marker = it.next();
|
||
if (marker)
|
||
marker->removeMySelf();
|
||
}
|
||
m_markerCont.clear();
|
||
}
|
||
|
||
void TerrainProfileProcessor::displayCurrentMarker(const Point3D &pointGeo)
|
||
{
|
||
// create
|
||
if (!m_currentMarker)
|
||
{
|
||
m_currentMarker = m_mapImpl->objectFactory().create<GeoImage>(m_toolsLayername,
|
||
QStringLiteral("TerrainProfileLineId%1_currentMarker").arg(m_id));
|
||
m_currentMarker->setProxyMarker(MarkerSymbolType::Circle);
|
||
m_currentMarker->setProxyMarkerColor(Qt::blue);
|
||
m_currentMarker->setProxyMarkerSize(10);
|
||
m_currentMarker->setPickable(false);
|
||
}
|
||
|
||
// apply data
|
||
m_currentMarker->setOrigin(pointGeo);
|
||
|
||
// update
|
||
if (!m_currentMarker->updateView())
|
||
ERROR_LOG(m_logger) << LOGFUNCTION << " m_currentMarker::updateView failed";
|
||
}
|
||
|
||
void TerrainProfileProcessor::removeCurrentMarker()
|
||
{
|
||
if (m_currentMarker)
|
||
m_currentMarker->removeMySelf();
|
||
m_currentMarker.reset();
|
||
}
|
||
|
||
void TerrainProfileProcessor::onLocationsToElevationsCompleted()
|
||
{
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " m_locationToElevationBulk.size()=" << m_locationToElevationBulk.size();
|
||
|
||
const QVector<QPair<QPointF, double>> &results = m_locationToElevationBulk.results();
|
||
|
||
if (m_logger.isDebugEnabled())
|
||
for (const QPair<QPointF, double> &item : qAsConst(results))
|
||
DEBUG_LOG(m_logger) << LOGFUNCTION << " location = " << item.first << " elevation = " << item.second;
|
||
|
||
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " results.size()=" << results.size();
|
||
|
||
|
||
// The caller should set TerrainProfileDialog on this signal:
|
||
emit locationsToElevationsCompleted(m_id);
|
||
|
||
// maybe (better hopefully) the caller (via finishedCallback of TerrainProfileImpl)
|
||
// has updated our m_terrainProfileParameter !?!
|
||
if (m_terrainProfileDialog)
|
||
{
|
||
m_terrainProfileDialog->setData(this->results());
|
||
|
||
QVector<QPointF> marks;
|
||
marks.reserve(m_terrainProfileParameter.m_coordinates.size());
|
||
for (const Point3D &coord : qAsConst(m_terrainProfileParameter.m_coordinates))
|
||
{
|
||
// geo to map
|
||
const Point3D &p3D = m_mapImpl->coordTranslationFunctor()->geographicToMap(coord);
|
||
marks.append(p3D.toPointF());
|
||
}
|
||
m_terrainProfileDialog->setMarks(marks);
|
||
} else {
|
||
ERROR_LOG(m_logger) << LOGFUNCTION << " no dialog";
|
||
}
|
||
|
||
m_inProgress = false;
|
||
}
|
||
|
||
void TerrainProfileProcessor::onDialogMousePositionChanged(const QPointF &curPos)
|
||
{
|
||
const Point3D &curPosGeo = Point3D::fromQPointF( m_mapImpl->coordTranslationFunctor()->mapToGeographic(curPos) );
|
||
displayCurrentMarker(curPosGeo);
|
||
}
|
||
|
||
|
||
} // namespace SituationMap
|