/** * @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 #include // 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 m_rubberband; QVector m_coordsGeo; QVector m_coordsMap; QVector m_lengthPerSegment; double m_totalLength = 0; RefPointer m_polyline; QVector> m_markerCont; RefPointer 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 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> it(d->m_markerCont); while (it.hasNext()) { RefPointer &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(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> it(d->m_markerCont); while (it.hasNext()) { RefPointer &marker = it.next(); if (!marker.valid()) { marker = d->m_mapImpl->objectFactory().create(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(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 &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 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(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