426 lines
13 KiB
C++
Executable File
426 lines
13 KiB
C++
Executable File
/**
|
|
* @RDE_HEADER@
|
|
* Interface-Implementation of DangerArea
|
|
* $Id$
|
|
* $HeadURL$
|
|
* $Author$
|
|
*/
|
|
|
|
// Own
|
|
#include "measurement_impl.h"
|
|
#include "layer_factory_impl.h"
|
|
#include "coord_translation_functor_impl.h"
|
|
#include "Rubberbands/rubberband_factory.h"
|
|
#include "Rubberbands/rubberband_multi_point_feature.h"
|
|
|
|
#include "Map/object_factory.h"
|
|
#include "MapObjects/polyline.h"
|
|
#include "MapObjects/geo_image.h"
|
|
#include "MapObjects/text.h"
|
|
#include "MapImpl/abstract_map_object_impl.h"
|
|
|
|
|
|
// Esri
|
|
#include <arcgis/GraphicsOverlay.h>
|
|
#include <arcgis/GeometryEngine.h>
|
|
|
|
|
|
// Qt
|
|
|
|
|
|
namespace SituationMap
|
|
{
|
|
|
|
|
|
class MeasurementImpl::Private
|
|
{
|
|
public:
|
|
Private() = default;
|
|
~Private() = default;
|
|
|
|
// ESRI data:
|
|
|
|
// Own data:
|
|
SituationMap::MapImpl *m_mapImpl = nullptr;
|
|
QString m_toolsLayername;
|
|
QColor m_color = Qt::magenta;
|
|
Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr;
|
|
|
|
QPointer<RB::RubberbandMultiPointFeature> m_rubberband;
|
|
|
|
QVector<Point3D> m_coordsGeo;
|
|
QVector<Point3D> m_coordsMap;
|
|
|
|
QVector<double> m_lengthPerSegment;
|
|
double m_totalLength = 0;
|
|
|
|
RefPointer<Polyline> m_polyline;
|
|
QVector<RefPointer<GeoImage>> m_markerCont;
|
|
RefPointer<Text> m_textTotalLength;
|
|
|
|
void resizeMarkerCont(int newSize)
|
|
{
|
|
if (newSize < 1)
|
|
return;
|
|
|
|
// increase
|
|
if (m_markerCont.size() < newSize)
|
|
{
|
|
m_markerCont.resize(newSize);
|
|
return;
|
|
}
|
|
|
|
// decrease
|
|
while (m_markerCont.size() > newSize)
|
|
{
|
|
// do not call 'takeLast' on empty container. But since newSize is >= 0...
|
|
RefPointer<GeoImage> item = m_markerCont.takeLast();
|
|
item->removeMySelf();
|
|
}
|
|
}
|
|
|
|
|
|
}; // class MeasurementImpl::Private
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// class MeasurementImpl
|
|
//-----------------------------------------------------------------------------
|
|
MeasurementImpl::MeasurementImpl(SituationMap::MapImpl *mapImpl)
|
|
: Measurement()
|
|
, QObject()
|
|
, m_logger(getLogger("MeasurementImpl"))
|
|
, d(new Private)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
d->m_mapImpl = mapImpl;
|
|
init();
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
}
|
|
|
|
MeasurementImpl::~MeasurementImpl()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
this->clear();
|
|
delete d;
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
}
|
|
|
|
|
|
long MeasurementImpl::measureLength()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
|
|
// cleanup old
|
|
clear();
|
|
|
|
startRubberband();
|
|
|
|
return -1;
|
|
}
|
|
|
|
bool MeasurementImpl::startRubberband()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
|
|
// verify mapReady
|
|
if (!d->m_mapImpl || !d->m_mapImpl->isReady())
|
|
{
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " Failure - map is not ready yet!!!";
|
|
return false;
|
|
}
|
|
|
|
RB::RubberbandMultiPointFeature *rubberband = this->createRubberband();
|
|
if (!rubberband)
|
|
return false;
|
|
|
|
d->m_rubberband = rubberband;
|
|
|
|
connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &MeasurementImpl::onRbAbort, Qt::UniqueConnection);
|
|
connect(rubberband, &SituationMap::RB::Rubberband::interimResultsChanged, this, &MeasurementImpl::onRbInterimResultsChanged, Qt::UniqueConnection);
|
|
connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &MeasurementImpl::onRbApply, Qt::UniqueConnection);
|
|
connect(rubberband, &SituationMap::RB::RubberbandMultiPointFeature::currentCursorPositionChanged, this, &MeasurementImpl::onCurrentCursorPositionChanged, Qt::UniqueConnection);
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
return true;
|
|
}
|
|
|
|
|
|
void MeasurementImpl::clear()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
d->m_coordsGeo.clear();
|
|
d->m_coordsMap.clear();
|
|
d->m_lengthPerSegment.clear();
|
|
d->m_totalLength = 0;
|
|
|
|
if (d->m_polyline)
|
|
d->m_polyline->removeMySelf();
|
|
d->m_polyline.reset();
|
|
|
|
QMutableVectorIterator<RefPointer<GeoImage>> it(d->m_markerCont);
|
|
while (it.hasNext())
|
|
{
|
|
RefPointer<GeoImage> &marker = it.next();
|
|
if (marker)
|
|
marker->removeMySelf();
|
|
}
|
|
d->m_markerCont.clear();
|
|
|
|
if (d->m_textTotalLength)
|
|
d->m_textTotalLength->removeMySelf();
|
|
d->m_textTotalLength.reset();
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
}
|
|
|
|
bool MeasurementImpl::displayPolyline()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
|
|
if (d->m_coordsGeo.size() < 2)
|
|
return false;
|
|
|
|
// create
|
|
if (!d->m_polyline)
|
|
{
|
|
d->m_polyline = d->m_mapImpl->objectFactory().create<Polyline>(d->m_toolsLayername,
|
|
QStringLiteral("MeasurementPolyline"));
|
|
d->m_polyline->setLineStyle(LineStyle(d->m_color, 3));
|
|
d->m_polyline->setScaleEnable(true);
|
|
d->m_polyline->setPickable(false);
|
|
}
|
|
|
|
// apply data
|
|
d->m_polyline->setPositions(d->m_coordsGeo);
|
|
for (int segment = 0; segment < d->m_lengthPerSegment.size(); ++segment)
|
|
{
|
|
const int dist = std::round(d->m_lengthPerSegment[segment]);
|
|
d->m_polyline->setTextOnSegment(segment, QStringLiteral("%1m").arg(dist));
|
|
// OutlineColor/Width is not supported yet
|
|
} // for
|
|
|
|
// update
|
|
if (!d->m_polyline->updateView())
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " polyline::updateView failed";
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
return true;
|
|
}
|
|
|
|
bool MeasurementImpl::displayVertices()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
|
|
const int size = d->m_coordsGeo.size();
|
|
if (size < 1)
|
|
return false;
|
|
|
|
d->resizeMarkerCont(size);
|
|
|
|
int idx = 0;
|
|
QMutableVectorIterator<RefPointer<GeoImage>> it(d->m_markerCont);
|
|
|
|
while (it.hasNext())
|
|
{
|
|
RefPointer<GeoImage> &marker = it.next();
|
|
if (!marker.valid())
|
|
{
|
|
marker = d->m_mapImpl->objectFactory().create<GeoImage>(d->m_toolsLayername,
|
|
QStringLiteral("MeasurementMarker%1").arg(idx));
|
|
marker->setProxyMarker(MarkerSymbolType::Circle);
|
|
marker->setProxyMarkerColor(d->m_color);
|
|
marker->setProxyMarkerSize(7);
|
|
marker->setPickable(false);
|
|
}
|
|
marker->setOrigin(d->m_coordsGeo[idx]);
|
|
|
|
// update
|
|
if (!marker->updateView())
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " marker::updateView failed";
|
|
|
|
++idx;
|
|
} // while
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
return true;
|
|
}
|
|
|
|
bool MeasurementImpl::displayTotalLength()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
|
|
if (d->m_coordsGeo.size() < 2)
|
|
return false;
|
|
|
|
if (!d->m_textTotalLength.valid())
|
|
{
|
|
d->m_textTotalLength = d->m_mapImpl->objectFactory().create<Text>(d->m_toolsLayername,
|
|
QStringLiteral("MeasurementText"));
|
|
TextStyle ts = AbstractMapObjectImpl::defaultTextStyle();
|
|
ts.setTextColor(d->m_color);
|
|
ts.setBold(true);
|
|
ts.setSize(ts.size() * 1.5);
|
|
d->m_textTotalLength->setTextStyle(ts);
|
|
}
|
|
const int dist = std::round(d->m_totalLength);
|
|
d->m_textTotalLength->setIdentifier(QStringLiteral("%1m").arg(dist));
|
|
d->m_textTotalLength->setOrigin(d->m_coordsGeo.last());
|
|
|
|
// update
|
|
if (!d->m_textTotalLength->updateView())
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " textTotalLength::updateView failed";
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
return true;
|
|
}
|
|
|
|
bool MeasurementImpl::calculateLength(const QVector<Point3D> &mapCoords)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
if (mapCoords.size() < 2)
|
|
return false;
|
|
|
|
static Esri::ArcGISRuntime::GeometryEngine *gEngine = Esri::ArcGISRuntime::GeometryEngine::instance();
|
|
const Esri::ArcGISRuntime::SpatialReference &sr = d->m_mapImpl->getEsriSpatialReference();
|
|
|
|
d->m_totalLength = 0;
|
|
d->m_lengthPerSegment.clear();
|
|
d->m_lengthPerSegment.resize(mapCoords.size() - 1);
|
|
|
|
QVectorIterator<Point3D> it(mapCoords);
|
|
int segment = 0;
|
|
while (it.hasNext())
|
|
{
|
|
const Point3D &mapCoord1 = it.next();
|
|
if (!it.hasNext())
|
|
break;
|
|
const Point3D &mapCoord2 = it.peekNext();
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " segment=" << segment;
|
|
|
|
Esri::ArcGISRuntime::Point geom1(mapCoord1.x(), mapCoord1.y(), mapCoord1.z(), sr);
|
|
Esri::ArcGISRuntime::Point geom2(mapCoord2.x(), mapCoord2.y(), mapCoord2.z(), sr);
|
|
|
|
// const double distancePlanar = gEngine->distance(geom1, geom2);
|
|
// gEngine->distance return wrong results, if Topographic.tpk is used. Most likly due to wkid.
|
|
// Use distanceGeodetic, that works (at least in 2D) for 32632, 3857
|
|
Esri::ArcGISRuntime::GeodeticDistanceResult geodeticDistanceResult = gEngine->distanceGeodetic(geom1, geom2,
|
|
Esri::ArcGISRuntime::LinearUnitId::Meters,
|
|
Esri::ArcGISRuntime::AngularUnitId::Degrees,
|
|
Esri::ArcGISRuntime::GeodeticCurveType::Geodesic);
|
|
|
|
|
|
d->m_lengthPerSegment[segment] = geodeticDistanceResult.distance();
|
|
d->m_totalLength += geodeticDistanceResult.distance();
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION
|
|
<< " segment=" << segment
|
|
<< " -> m_totalLength = " << d->m_totalLength;
|
|
++segment;
|
|
} // while
|
|
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " distancePlanar = " << d->m_totalLength;
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
return true;
|
|
}
|
|
|
|
void MeasurementImpl::onRbAbort(RB::RubberbandBase *rbnd)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
clear();
|
|
if (!rbnd)
|
|
return;
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " rb abort";
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
}
|
|
|
|
|
|
void MeasurementImpl::onRbInterimResultsChanged(RB::RubberbandBase *rbnd)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
if (!rbnd)
|
|
return;
|
|
|
|
d->m_coordsMap = SituationMap::convert2D3D(rbnd->coordinateVector(RB::CoordinateSystem::Map));
|
|
d->m_coordsGeo = SituationMap::convert2D3D(rbnd->coordinateVector(RB::CoordinateSystem::Geo));
|
|
|
|
calculateLength(d->m_coordsMap);
|
|
displayTotalLength();
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
}
|
|
|
|
|
|
void MeasurementImpl::onRbApply(RB::RubberbandBase *rbnd)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
if (!rbnd)
|
|
return;
|
|
|
|
d->m_coordsMap = SituationMap::convert2D3D(rbnd->coordinateVector(RB::CoordinateSystem::Map));
|
|
d->m_coordsGeo = SituationMap::convert2D3D(rbnd->coordinateVector(RB::CoordinateSystem::Geo));
|
|
DEBUG_LOG(m_logger) << LOGFUNCTION << " rb apply, d->m_coordsMap.size()=" << d->m_coordsMap.size();
|
|
|
|
calculateLength(d->m_coordsMap);
|
|
displayPolyline();
|
|
displayVertices();
|
|
displayTotalLength();
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
}
|
|
|
|
void MeasurementImpl::onCurrentCursorPositionChanged(const Point3D &mapCoord)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
|
|
<< " mapCoord = " << mapCoord;
|
|
|
|
d->m_coordsMap = SituationMap::convert2D3D(d->m_rubberband->coordinateVector(RB::CoordinateSystem::Map));
|
|
d->m_coordsGeo = SituationMap::convert2D3D(d->m_rubberband->coordinateVector(RB::CoordinateSystem::Geo));
|
|
// DEBUG_LOG(m_logger) << LOGFUNCTION << " rb apply, d->m_coordsMap.size()=" << d->m_coordsMap.size();
|
|
|
|
// append coord of current mouse position:
|
|
d->m_coordsMap += mapCoord;
|
|
const Point3D &geoCoord = d->m_mapImpl->coordTranslationFunctor()->mapToGeographic(mapCoord);
|
|
d->m_coordsGeo += geoCoord;
|
|
|
|
calculateLength(d->m_coordsMap);
|
|
displayPolyline();
|
|
displayVertices();
|
|
displayTotalLength();
|
|
}
|
|
|
|
void MeasurementImpl::init()
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
|
|
|
|
if (!d->m_graphicsLayer)
|
|
{
|
|
LayerFactoryImpl &layerFactory = d->m_mapImpl->layerFactoryImpl();
|
|
|
|
d->m_toolsLayername = layerFactory.toolLayer();
|
|
d->m_graphicsLayer = layerFactory.getGraphicsLayer(d->m_toolsLayername);
|
|
}
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
|
|
}
|
|
|
|
RB::RubberbandMultiPointFeature *MeasurementImpl::createRubberband()
|
|
{
|
|
// remove any pending/active rubberbands of our map:
|
|
RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl);
|
|
|
|
RB::RubberbandMultiPointFeature *rubberband =
|
|
d->m_mapImpl->rubberbandFactory().create<RB::RubberbandMultiPointFeature>(this);
|
|
if (!rubberband || !rubberband->rubberbandIsAlive())
|
|
return nullptr;
|
|
rubberband->setAutoDelete(true);
|
|
rubberband->enableClosed(false);
|
|
rubberband->minimumPointCount(2);
|
|
rubberband->maximumPointCount(11);
|
|
// rubberband->maximumLength(maxSegmentLength);
|
|
return rubberband;
|
|
}
|
|
|
|
|
|
} // namespace SituationMap
|