Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+425
View File
@@ -0,0 +1,425 @@
/**
* @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