/** * @RDE_HEADER@ * Interface-Implementation of AirSupport * $Id$ * $HeadURL$ * $Author$ */ // Own #include "air_support_impl.h" #include #include #include #include #include #include // ESRI #include #include // Qt namespace SituationMap { const int AIR_SUPPORT_LINE_WIDTH = 2; class AirSupportImpl::Private { public: Private() = default; ~Private() { removeAesBuffer(); removeAFP(); removeALP(); m_pm_AFP = nullptr; m_pm_ALP = nullptr; m_aesBuffer = nullptr; } // ESRI data: Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr; // Own data: MapImpl *m_mapImpl = nullptr; UniqueMapId m_referencedObject = INVALID_MAP_ID; QString m_debugInfo; // will be appended by consecutive number UniqueMapId m_uniqueMapId; QColor m_color = Qt::black; GraphicalObjectPictureMarker *m_pm_AFP = nullptr; GraphicalObjectPictureMarker *m_pm_ALP = nullptr; GraphicalObjectPath *m_aesBuffer = nullptr; QMap > m_cont; /// one polyline per subsidiary line typedef QMapIterator> MAP_IT_T; // int m_drawOrder = 0; GraphicalObjectPictureMarker *getAFP() { if (!m_pm_AFP) { QString debugInfo = QStringLiteral("%1#Marker(AFP)").arg(m_debugInfo); m_pm_AFP = new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView(), m_graphicsLayer, debugInfo, m_uniqueMapId); m_pm_AFP->setMarkerSymbol(MarkerSymbolType::Circle, 10, Qt::black); } return m_pm_AFP; } void removeAFP() { delete m_pm_AFP; m_pm_AFP = nullptr; } GraphicalObjectPictureMarker *getALP() { if (!m_pm_ALP) { QString debugInfo = QStringLiteral("%1#Marker(ALP)").arg(m_debugInfo); m_pm_ALP = new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView(), m_graphicsLayer, debugInfo, m_uniqueMapId); m_pm_ALP->setMarkerSymbol(MarkerSymbolType::Circle, 10, Qt::black); } return m_pm_ALP; } void removeALP() { delete m_pm_ALP; m_pm_ALP = nullptr; } GraphicalObjectPath *getAesBuffer() { if (!m_aesBuffer) { QString debugInfo = QStringLiteral("%1#AesBuffer").arg(m_debugInfo); m_aesBuffer = new GraphicalObjectPath(GraphicalObjectPathType::Polygon, m_mapImpl->getEsriGeoView(), m_graphicsLayer, debugInfo, m_uniqueMapId); m_aesBuffer->setFillStyle(FillStyle(FillPattern::Null)); } return m_aesBuffer; } void removeAesBuffer() { delete m_aesBuffer; m_aesBuffer = nullptr; } bool hasPathObject(int key) { return m_cont.contains(key); } QList keysInUse() { return m_cont.keys(); } void removePathObject(int key) { m_cont.remove(key); } void clearPathObjects() { m_cont.clear(); } QSharedPointer getPathObject(int key) { if (m_cont[key].isNull()) { QString debugInfo = QStringLiteral("%1#SubsidiaryLine(%2)").arg(m_debugInfo).arg(key); GraphicalObjectPath *temp = new GraphicalObjectPath(GraphicalObjectPathType::Polyline, m_mapImpl->getEsriGeoView(), m_graphicsLayer, debugInfo, m_uniqueMapId); if (temp) m_cont[key] = QSharedPointer(temp); } return m_cont[key]; } }; // class AirSupportImpl::Private //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // class AirSupportImpl //----------------------------------------------------------------------------- AirSupportImpl::AirSupportImpl(const QString &debugInfo) : AirSupport() // Interface , BaseClass(debugInfo) // Implementation , d(new Private) , m_logger(getLogger("AirSupportImpl")) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } AirSupportImpl::~AirSupportImpl() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; delete d; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } void AirSupportImpl::setUniqueId(const UniqueMapId &uniqueId) { BaseClass::setUniqueId(uniqueId); d->m_uniqueMapId = m_uniqueMapId; } void AirSupportImpl::setReferencedObject(const UniqueMapId &referencedObject) { d->m_referencedObject = referencedObject; } bool AirSupportImpl::updateViewImpl() { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " m_debugInfo=" << m_debugInfo << " symbol=" << m_symbol.value().sidc << " visibility=" << m_visibility << " visiTechn=" << m_visibilityTechnical << " visiTact=" << m_visibilityTactical << " this->drawOrder=" << this->drawOrder() << " this->positions()=" << this->positions(); // m_mainGraphicalObject stores the outline/'bounding box' (techn and tact) // Those geometry should never be visible. // The text of should be visible. this->setVisibilityOfGeometry(false); if (this->positions().size() < 2) { ERROR_LOG(m_logger) << LOGFUNCTION << " number of coordinates is not sufficiant" << " (#coord=" << this->positions().size() << ")"; return false; } if (m_mainGraphicalObject) d->m_color = m_mainGraphicalObject->lineStyle().lineColor(); bool success = BaseClass::updateViewImpl(); if (!success) ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; // real work: // // First, draw AFP and ALP // // AFP { Point3D coordTranslated = getCoordinateTranslated(COORD_INDEX_AFP); if (coordTranslated.isNull()) { d->removeAFP(); } else { GraphicalObjectPictureMarker *pm = d->getAFP(); pm->setMarkerSymbol(MarkerSymbolType::Circle, 10, d->m_color); pm->setOrigin(coordTranslated); pm->setVisibility(m_visibility && m_visibilityTechnical); pm->setScale(m_mainGraphicalObject->scale()); pm->setDrawOrder(m_mainGraphicalObject->drawOrder()); if (!pm->updateView()) ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed"; } } // ALP { Point3D coordTranslated = getCoordinateTranslated(COORD_INDEX_ALP); if (coordTranslated.isNull()) { d->removeALP(); } else { GraphicalObjectPictureMarker *pm = d->getALP(); pm->setMarkerSymbol(MarkerSymbolType::Circle, 10, d->m_color); pm->setOrigin(coordTranslated); pm->setVisibility(m_visibility && m_visibilityTechnical); pm->setScale(m_mainGraphicalObject->scale()); pm->setDrawOrder(m_mainGraphicalObject->drawOrder()); if (!pm->updateView()) ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed"; } } // // delegate remaining to onMapExtentChanged // TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; return success; } bool AirSupportImpl::onMapExtentChanged() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; bool success = BaseClass::onMapExtentChanged(); if (!m_mainGraphicalObject) { ERROR_LOG(m_logger) << LOGFUNCTION << " no m_mainGraphicalObject yet"; return false; } // 1. create aesBuffer; updateViewImplBuffer(); // 2. and see, if Alp is within: bool contains = bufferContainsAlp(); // buffer and alp must exist!!! DEBUG_LOG(m_logger) << LOGFUNCTION << " buffer contains Alp: " << contains; // Line Afp Alp Point3D start = this->getCoordinateTranslated(COORD_INDEX_AFP); Point3D end = this->getCoordinateTranslated(COORD_INDEX_ALP); if (end.isNull()) updateViewImplLineAfpAlp(false, Point3D(), Point3D()); else updateViewImplLineAfpAlp(true, start, end); // Line Alp Aes start = this->getCoordinateTranslated(COORD_INDEX_ALP); end = this->getCoordinateTranslated(COORD_INDEX_AES); // try to replace end by intersection with Aes: Point3D intersection = this->intersectionAesAndLineAlpAes(); DEBUG_LOG(m_logger) << " intersectionAes " << intersection; if (!intersection.isNull()) end = intersection; // try to replace end by intersection with buffer of Aes: if (d->m_aesBuffer) { intersection = this->intersectionBufferAndLineAlpAes(); DEBUG_LOG(m_logger) << " intersectionBuffer " << intersection; if (!intersection.isNull()) end = intersection; } // Maybe 3rd coordinate is not provided? if (start.isNull() || end.isNull()) updateViewImplLineAlpAes(false, Point3D(), Point3D()); else updateViewImplLineAlpAes(!contains, start, end); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return success; } bool AirSupportImpl::updateViewImplBuffer() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; DEBUG_LOG(m_logger) << LOGFUNCTION << " pixelSize= " << m_mapImpl->pixelSizeMeter(); if (m_mapImpl->pixelSizeMeter() < 1) { d->removeAesBuffer(); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } double bufferDistance = m_mapImpl->pixelSizeMeter() * 100; DEBUG_LOG(m_logger) << LOGFUNCTION << " bufferDistance= " << bufferDistance; if (bufferDistance > 2000) bufferDistance = 2000; // AesBuffer of referenced object if (!isUniqueMapIdValid(d->m_referencedObject)) { // This can happen, if the object is just created: DEBUG_LOG(m_logger) << LOGFUNCTION << " m_referencedObject is invalid" << " (m_referencedObject=" << d->m_referencedObject << ")"; return false; } AbstractMapObject *amo = d->m_mapImpl->objectContainer().getObject(d->m_referencedObject); if (!amo) { ERROR_LOG(m_logger) << LOGFUNCTION << " m_referencedObject not found" << " (m_referencedObject=" << d->m_referencedObject << ")"; return false; } QVector resultVec = MathUtils::GeometryEngine::buffer(amo, bufferDistance); GraphicalObjectPath *buffer = d->getAesBuffer(); if (buffer) { buffer->setVisibility(m_visibility && m_visibilityTechnical); buffer->setScale(m_mainGraphicalObject->scale()); buffer->setLineStyle(LineStyle(d->m_color, AIR_SUPPORT_LINE_WIDTH, LinePattern::Solid)); buffer->setPositions(resultVec); buffer->setDrawOrder(m_mainGraphicalObject->drawOrder()); if (!buffer->updateView()) ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed"; } TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } void AirSupportImpl::updateViewImplLineAfpAlp(bool show, const Point3D &start, const Point3D &end) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (show) { QSharedPointer line = d->getPathObject(LINE_INDEX_AFP_ALP); if (line) { line->setVisibility(m_visibility && m_visibilityTechnical); line->setScale(m_mainGraphicalObject->scale()); line->setDrawOrder(m_mainGraphicalObject->drawOrder()); line->setLineStyle(LineStyle(d->m_color, AIR_SUPPORT_LINE_WIDTH, LinePattern::Dash)); PositionCont posCont; posCont.reserve(2); posCont.append(start); posCont.append(end); line->setPositions(posCont); if (!line->updateView()) ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed"; } TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } else d->removePathObject(LINE_INDEX_AFP_ALP); } void AirSupportImpl::updateViewImplLineAlpAes(bool show, const Point3D &start, const Point3D &end) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (show) { QSharedPointer line = d->getPathObject(LINE_INDEX_ALP_AES); if (line) { line->setVisibility(m_visibility && m_visibilityTechnical); line->setScale(m_mainGraphicalObject->scale()); line->setDrawOrder(m_mainGraphicalObject->drawOrder()); line->setLineStyle(LineStyle(d->m_color, AIR_SUPPORT_LINE_WIDTH, LinePattern::Solid)); PositionCont posCont; posCont.reserve(2); posCont.append(start); posCont.append(end); line->setPositions(posCont); if (!line->updateView()) ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed"; } } else d->removePathObject(LINE_INDEX_ALP_AES); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } bool AirSupportImpl::bufferContainsAlp() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; Esri::ArcGISRuntime::Geometry geoHull; if (d->m_aesBuffer) geoHull = d->m_aesBuffer->getGraphicGeometry(); Esri::ArcGISRuntime::Geometry pntToCheck; if (d->getALP()) pntToCheck = d->getALP()->getGraphicGeometry(); bool contains = false; if (!geoHull.isEmpty() && !pntToCheck.isEmpty()) contains = Esri::ArcGISRuntime::GeometryEngine::contains(geoHull, pntToCheck); TRACE_LOG(m_logger) << LOGFUNCTION << " END, contains" << contains; return contains; } Point3D AirSupportImpl::intersectionBufferAndLineAlpAes() { Esri::ArcGISRuntime::Geometry geo1; if (d->m_aesBuffer) geo1 = d->m_aesBuffer->getGraphicGeometry(); if (geo1.isEmpty()) return Point3D(); Esri::ArcGISRuntime::Geometry geo2 = geometryOfLine(getCoordinateTranslated(COORD_INDEX_ALP), getCoordinateTranslated(COORD_INDEX_AES)); if (geo2.isEmpty()) return Point3D(); return intersection(geo1, geo2); } Point3D AirSupportImpl::intersectionAesAndLineAlpAes() { AbstractMapObject *amo = d->m_mapImpl->objectContainer().getObject(d->m_referencedObject); AbstractMapObjectImpl *mapObjImpl = dynamic_cast(amo); if (!mapObjImpl) { DEBUG_LOG(m_logger) << LOGFUNCTION << " m_referencedObject not found/dynamic_cast failed" << " (m_referencedObject=" << d->m_referencedObject << ")"; return Point3D(); } Esri::ArcGISRuntime::Geometry geo1; if (mapObjImpl) geo1 = mapObjImpl->getEsriGeometry(); if (geo1.isEmpty()) return Point3D(); Esri::ArcGISRuntime::Geometry geo2 = geometryOfLine(getCoordinateTranslated(COORD_INDEX_ALP), getCoordinateTranslated(COORD_INDEX_AES)); if (geo2.isEmpty()) return Point3D(); return intersection(geo1, geo2); } Point3D AirSupportImpl::intersection(const Esri::ArcGISRuntime::Geometry &geo1, const Esri::ArcGISRuntime::Geometry &geo2) { if (geo1.isEmpty() || geo2.isEmpty()) return Point3D(); Esri::ArcGISRuntime::Geometry geoResult = Esri::ArcGISRuntime::GeometryEngine::intersection(geo1, geo2); DEBUG_LOG(m_logger) << LOGFUNCTION << " geoInter = " << geoResult.toJson(); DEBUG_LOG(m_logger) << LOGFUNCTION << " geoInter dimension = " << geoResult.dimension(); Esri::ArcGISRuntime::Point intersectPoint; if (geoResult.dimension() == 1) // dim 1 -> Lines and Polyline { Esri::ArcGISRuntime::Polyline polylineResult(geoResult); if (polylineResult.parts().part(0).pointCount() > 0) intersectPoint = polylineResult.parts().part(0).startPoint(); } else if (geoResult.dimension() == 0) { intersectPoint = Esri::ArcGISRuntime::Point(geoResult); // This is not tested yet } DEBUG_LOG(m_logger) << LOGFUNCTION << " intersectPoint = " << intersectPoint.toJson(); if (std::isnan(intersectPoint.x()) || std::isnan(intersectPoint.y()) || std::isnan(intersectPoint.z())) return Point3D(); Point3D intersection(intersectPoint.x(), intersectPoint.y(), intersectPoint.z()); return intersection; } Esri::ArcGISRuntime::Geometry AirSupportImpl::geometryOfLine(const Point3D &start, const Point3D &end) { Esri::ArcGISRuntime::PolylineBuilder polylineBuilder(m_mapImpl->getEsriSpatialReference()); polylineBuilder.addPoint(Esri::ArcGISRuntime::Point(start.x(), start.y())); polylineBuilder.addPoint(Esri::ArcGISRuntime::Point(end.x(), end.y())); const Esri::ArcGISRuntime::Geometry &geo = polylineBuilder.toGeometry(); return geo; } bool AirSupportImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; // no symbol required bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polyline); setLineStyle(LineStyle(Qt::black, 1)); if (!success) ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; // set some values to Private: d->m_mapImpl = m_mapImpl; d->m_debugInfo = m_debugInfo; d->m_uniqueMapId = m_uniqueMapId; d->m_graphicsLayer = m_graphicsLayer; TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; return success; } Point3D AirSupportImpl::getCoordinateTranslated(int idx) const { if (idx < 0 || idx > 2) return Point3D(); const PositionCont &posContTranslated = m_positions.positionsTranslated(); if (idx >= posContTranslated.size()) return Point3D(); return posContTranslated[idx]; } } // namespace SituationMap