/** * @RDE_HEADER@ * Interface-Implementation of BasicPathObjectImpl * $Id$ * $HeadURL$ * $Author$ */ // Own #include "basic_path_object_impl.h" #include "indication_container.h" #include #include #include #include //#include #include #include #include #include #include // ESRI #include #include // Qt #include #include #include #include namespace SituationMap { //----------------------------------------------------------------------------- // class BasicPathObjectImpl //----------------------------------------------------------------------------- BasicPathObjectImpl::BasicPathObjectImpl(const QString &debugInfo) : BaseClass(debugInfo) // Implementation , AbstractEffectSimulationImpl() // Secondary implementation , m_logger(getLogger("BasicPathObjectImpl")) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " debugInfo=" << m_debugInfo << "'"; TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " debugInfo=" << m_debugInfo << "'"; } BasicPathObjectImpl::~BasicPathObjectImpl() { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " debugInfo=" << m_debugInfo << "'"; delete m_indicationCont; delete m_indicationContSpAbsolute; delete m_pathCont_SpDraped; delete m_pathCont_SpDrapedPolygon; delete m_pathCont_SpAbsolute1; delete m_pathCont_SpAbsolute2; TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " debugInfo=" << m_debugInfo << "'"; } QString BasicPathObjectImpl::toString() const { QString str; str.append(BaseClass::toString()); str.append(AbstractEffectSimulationImpl::toString()); return str; } Point3D BasicPathObjectImpl::tacticalOrigin() const { return m_tacticalOrigin.value().origin(); } void BasicPathObjectImpl::setTacticalOrigin(const Point3D &tacticalOrigin) { if (m_tacticalOrigin.value().origin() != tacticalOrigin) m_tacticalOrigin.refValue().setOrigin(tacticalOrigin); } Point3D BasicPathObjectImpl::origin() const { return m_origin.value(); } void BasicPathObjectImpl::setOrigin(const Point3D &origin) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " debugInfo=" << m_debugInfo << "'" << " origin=" << origin; m_origin = origin; TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " debugInfo=" << m_debugInfo << "'"; } float BasicPathObjectImpl::radius() const { return m_radius.value(); } void BasicPathObjectImpl::setRadius(float radius, unsigned int maxPointCount) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " debugInfo=" << m_debugInfo << "'" << " radius=" << radius << " maxPointCount=" << maxPointCount; m_radius = radius; m_maxPointCount = maxPointCount; TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " debugInfo=" << m_debugInfo << "'"; } #ifdef SUPPORT_SUBSIDIARY_LINES PositionCont BasicPathObjectImpl::subsidiaryPositions(int pathIndex) const { if (m_subsidiaryPositionsCont.contains(pathIndex)) return m_subsidiaryPositionsCont[pathIndex].positions(); return PositionCont(); } void BasicPathObjectImpl::setSubsidiaryPositions(int pathIndex, const PositionCont &positions) { m_subsidiaryPositionsCont[pathIndex].setPositions(positions); } QList BasicPathObjectImpl::subsidiaryPathIndexInUse() { return m_subsidiaryPositionsCont.keys(); } void BasicPathObjectImpl::clearSubsidiaryPositions(int pathIndex) { m_subsidiaryPositionsCont.remove(pathIndex); } void BasicPathObjectImpl::clearSubsidiaryPositions() { m_subsidiaryPositionsCont.clear(); } #endif // SUPPORT_SUBSIDIARY_LINES GraphicalObjectPathType BasicPathObjectImpl::pathType() const { GraphicalObjectPath *path = static_cast(m_mainGraphicalObject); return (path) ? path->pathType() : GraphicalObjectPathType::Polyline; } int BasicPathObjectImpl::segmentCount() const { if (m_positions.size() >= 2) return m_positions.size() - 1; return 0; } AesEffectState BasicPathObjectImpl::aesState() const { return BaseClass::aesState(); } void BasicPathObjectImpl::setAesState(AesEffectState aesState) { BaseClass::setAesState(aesState); } IndicationContainer &BasicPathObjectImpl::rIndicationContainer() const { if (!m_indicationCont) { m_indicationCont = new IndicationContainer(m_mapImpl->getEsriGeoView(), m_graphicsLayer, m_mapImpl->coordTranslationFunctor(), m_uniqueMapId); } return *m_indicationCont; } IndicationContainer &BasicPathObjectImpl::rIndicationContainerAbsolute() const { if (!m_indicationContSpAbsolute) { Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = m_mapImpl->getGraphicsLayer3D_SpAbsolute(m_logicalLayerName); if (!graphicsLayer) { ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " getGraphicsLayer3D_SpAbsolute failed"; } m_indicationContSpAbsolute = new IndicationContainer(m_mapImpl->getEsriGeoView(), graphicsLayer, m_mapImpl->coordTranslationFunctor(), m_uniqueMapId); } return *m_indicationContSpAbsolute; } PathContainer &BasicPathObjectImpl::rPathContainer() const { if (!m_pathCont_SpDraped) { m_pathCont_SpDraped = new PathContainer(m_mapImpl->getEsriGeoView(), m_graphicsLayer, m_mapImpl->coordTranslationFunctor(), m_uniqueMapId); } return *m_pathCont_SpDraped; } PathContainer &BasicPathObjectImpl::rPathContainerSpDrapedPolygon() const { if (!m_pathCont_SpDrapedPolygon) { m_pathCont_SpDrapedPolygon = new PathContainer(GraphicalObjectPathType::Polygon, m_mapImpl->getEsriGeoView(), m_graphicsLayer, m_mapImpl->coordTranslationFunctor(), m_uniqueMapId); } return *m_pathCont_SpDrapedPolygon; } void BasicPathObjectImpl::clearPathContainerSpDrapedPolygon(int idx) { if (!m_pathCont_SpDrapedPolygon) return; rPathContainerSpDrapedPolygon().clear(idx); } PathContainer &BasicPathObjectImpl::rPathContainerSpAbsolute1() const { if (!m_pathCont_SpAbsolute1) { Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = m_mapImpl->getGraphicsLayer3D_SpAbsolute(m_logicalLayerName); if (!graphicsLayer) { ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " getGraphicsLayer3D_SpAbsolute failed"; } m_pathCont_SpAbsolute1 = new PathContainer(m_mapImpl->getEsriGeoView(), graphicsLayer, m_mapImpl->coordTranslationFunctor(), m_uniqueMapId); } return *m_pathCont_SpAbsolute1; } PathContainer &BasicPathObjectImpl::rPathContainerSpAbsolute2() const { if (!m_pathCont_SpAbsolute2) { Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = m_mapImpl->getGraphicsLayer3D_SpAbsolute(m_logicalLayerName); if (!graphicsLayer) { ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " getGraphicsLayer3D_SpAbsolute failed"; } m_pathCont_SpAbsolute2 = new PathContainer(m_mapImpl->getEsriGeoView(), graphicsLayer, m_mapImpl->coordTranslationFunctor(), m_uniqueMapId); } return *m_pathCont_SpAbsolute2; } bool BasicPathObjectImpl::arrowHeadStart() const { return m_arrowHeadStart.enable; } void BasicPathObjectImpl::setArrowHeadStart(bool enable) { m_arrowHeadStart.enable = enable; } bool BasicPathObjectImpl::arrowHeadEnd() const { return m_arrowHeadEnd.enable; } void BasicPathObjectImpl::setArrowHeadEnd(bool enable) { m_arrowHeadEnd.enable = enable; } void BasicPathObjectImpl::setArrowHeadsSelected(bool select) { if (m_arrowHeadStart.pmArrowHead) m_arrowHeadStart.pmArrowHead->setSelected(select); if (m_arrowHeadEnd.pmArrowHead) m_arrowHeadEnd.pmArrowHead->setSelected(select); } void BasicPathObjectImpl::setDetonation(const Point3D &origin, double radius) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; if (m_detonationOrigin.value().origin() != origin) m_detonationOrigin.refValue().setOrigin(origin); m_detonationRadius = radius; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } bool BasicPathObjectImpl::hasDetonation() const { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return m_detonationRadius.value() > 0; } void BasicPathObjectImpl::resetDetonation() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; setDetonation({}, 0.0); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } QString BasicPathObjectImpl::textOnSegment(uint segment) const { return m_textDataCont[segment].text; } void BasicPathObjectImpl::setTextOnSegment(uint segment, const QString &segmentText) { m_textDataCont[segment].text = segmentText; } void BasicPathObjectImpl::clearTextOnSegment(uint segment) { m_textDataCont.remove(segment); } QImage BasicPathObjectImpl::renderArrowHeadSymbol(const QColor &color, int length, int halfWidth) { int width = halfWidth * 2; QImage image(width, length, QImage::Format_ARGB32); image.fill(Qt::transparent); QPainter painter(&image); painter.setRenderHints(QPainter::Antialiasing); QPen pen(color); pen.setWidth(2); painter.setPen(pen); painter.setBrush(QBrush(color, Qt::BrushStyle::SolidPattern)); // 2 || // | | // 1 | | 3 QPolygon poly; poly.reserve(3); poly.append(QPoint(0 , length)); poly.append(QPoint(halfWidth, 0)); poly.append(QPoint(width , length)); painter.drawPolygon(poly); painter.end(); // image.save(QLatin1String("/tmp/Arrow.png")); return image; } bool BasicPathObjectImpl::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; if (!m_graphicsLayer) { ERROR_LOG(m_logger) << LOGFUNCTION << " no graphicsLayer"; return false; } if (!m_mainGraphicalObject) { ERROR_LOG(m_logger) << LOGFUNCTION << "no m_mainGraphicalObject"; return false; } // // Use positions or origin/radius= // if (m_radius.value() > 0) { // origin/radius : if (m_origin.changed() || m_radius.changed() || m_maxPointCount.changed()) { // clear/disable some other things: m_positions.clear(); m_arrowHeadStart.enable = false; m_arrowHeadEnd.enable = false; m_textDataCont.clear(); DEBUG_LOG(m_logger) << LOGFUNCTION << " positions,arrowHeads,textDataCont cleared"; } OriginTranslated orgTemp; orgTemp.setMapObjectImpl(this); orgTemp.setOrigin(m_origin.value()); const Point3D &orgMapPoint = orgTemp.originTranslated(); // for 2D // QList posContTrans = Geometry::createCircle(orgMapPoint, m_radius.value()); // Q_FOREACH(const QPointF &pp, posContTrans) // qWarning() << "posContTrans" << pp; // m_positions.setPositionsTranslated( posContTrans ); // m_mainGraphicalObject->setPositions(this->positionsTranslated()); // for 2D and 3D: Esri::ArcGISRuntime::Point centerP(orgMapPoint.x(), orgMapPoint.y(), orgMapPoint.z(), this->mapImpl()->getEsriSpatialReference()); PositionCont posCont; if (! GraphicalObjectEllipse::createCirclePolygon(centerP, m_radius.value(), m_maxPointCount.value(), posCont)) ERROR_LOG(m_logger) << LOGFUNCTION << " createCirclePolygon failed"; else { m_positions.setPositionsTranslated(posCont); m_mainGraphicalObject->setPositions(this->positionsTranslated()); } // : origin/radius } else { if (!m_positions.isEmpty()) m_mainGraphicalObject->setPositions(this->positionsTranslated()); } // verify: const int minPoints = this->pathType() == GraphicalObjectPathType::Polyline ? 2 : 3; if (m_mainGraphicalObject->positions().count() < minPoints) { ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo1='" << m_debugInfo << "'" << " incorrect number of positions" << " (" << this->positions().count() << ")"; return false; } // REGG-5261 - Alle Wirkfelder blinken in 3D, wenn C-Kampf ausgelöst ist // War mit 100.4 meist, aber nicht immer reproduzierbar. // Wenn aber per FunctionsDialog die TacticalAppearance sichtbar wurde, // so war ein massives Flackern erkennbar. // Daher wird die Ausdehnung nur an SICHTBARE m_tacticalAppearance übergeben // [basic_path_object, sidc_symbol_impl] if (m_tacticalAppearance && m_tacticalAppearance->visibility()) { // m_tacticalOrigin has priority (SingleFire) if (!m_tacticalOrigin.value().origin().toPointF().isNull()) // check if x,y are defined, just z is not sufficiant m_tacticalAppearance->setOrigin(m_tacticalOrigin.value().originTranslated()); else m_tacticalAppearance->setPositions(m_mainGraphicalObject->positions()); } // Indication: if (m_indicationCont) { // The drawOrder should be above the m_mainGraphicalObject (==Path) // (case: path is closed and filled -> guarantee that indication is visible.) m_indicationCont->setDrawOrder(m_internalDrawOrder + 1); } if (m_indicationContSpAbsolute) { m_indicationContSpAbsolute->setDrawOrder(m_internalDrawOrder + 1); } // Paths: if (m_pathCont_SpDraped) { // The drawOrder should be above the m_mainGraphicalObject (==Path) // (case: path is closed and filled -> guarantee that indication is visible.) m_pathCont_SpDraped->setDrawOrder(m_internalDrawOrder + 1); // lineStyle has to be set explicit // m_pathCont->setLineStyle(m_mainGraphicalObject->lineStyle()); } // Paths: if (m_pathCont_SpDrapedPolygon) { // The drawOrder should be above the m_mainGraphicalObject (==Path) // (case: path is closed and filled -> guarantee that indication is visible.) m_pathCont_SpDrapedPolygon->setDrawOrder(m_internalDrawOrder + 1); } // if (m_pathCont_SpAbsolute) // { // // DrawOrder for SpAbsolute not really helpful: // // m_pathCont_SpAbsolute->setDrawOrder(m_internalDrawOrder + 1); // // m_pathCont_SpAbsolute->setLineStyle(m_mainGraphicalObject->lineStyle()); // } if (m_pathCont_SpDraped) if (!m_pathCont_SpDraped->updateView()) WARNING_LOG(m_logger) << LOGFUNCTION << " m_pathCont_SpDraped updateView failed"; if (m_pathCont_SpDrapedPolygon) if (!m_pathCont_SpDrapedPolygon->updateView()) WARNING_LOG(m_logger) << LOGFUNCTION << " m_pathCont_SpDrapedPolygon updateView failed"; if (m_pathCont_SpAbsolute1) if (!m_pathCont_SpAbsolute1->updateView()) WARNING_LOG(m_logger) << LOGFUNCTION << " m_pathCont_SpAbsolute1 updateView failed"; if (m_pathCont_SpAbsolute2) if (!m_pathCont_SpAbsolute2->updateView()) WARNING_LOG(m_logger) << LOGFUNCTION << " m_pathCont_SpAbsolute2 updateView failed"; // Single Scene Marker // has to be executed in final class // updateSingleSceneMarker(); // updateSingleSceneMarker(true); // Arrowhead: updateArrowHead(m_arrowHeadStart, true); updateArrowHead(m_arrowHeadEnd, false); // SegmentText: bool successSegText = updateSegmentText(); if (!successSegText) ERROR_LOG(m_logger) << LOGFUNCTION << " successSegText failed"; #ifdef SUPPORT_SUBSIDIARY_LINES // subsidiaryPositionsCont : GraphicalObjectMultiPath *multiPath = static_cast(m_mainGraphicalObject); QList pathIdxInUse; int pathIdx = 0; QMutableMapIterator mapIt(m_subsidiaryPositionsCont); while (mapIt.hasNext()) { mapIt.next(); PositionsTranslated &posTranslated = mapIt.value(); posTranslated.setMapObjectImpl(this); // let functor work :-) // PRE-increment! (pathIdx=0 is the main path in GraphicalObjectMultiPath multiPath->setPath(++pathIdx, posTranslated.positionsTranslated()); pathIdxInUse.append(pathIdx); } // while // remove non-existing subsidiary idx from multiPath: multiPath->reduceTo(pathIdxInUse); // : subsidiaryPositionsCont #endif // SUPPORT_SUBSIDIARY_LINES // BaseClass: bool success = BaseClass::updateViewImpl() && successSegText; if (!success) ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; return success; } bool BasicPathObjectImpl::updateSingleSceneMarker(bool newVisibility) { DEBUG_LOG(m_logger) << " START, newVisibility = " << newVisibility << ", m_debugInfo" << m_debugInfo; // do we have a detonationPoint / impactOrigin? if (m_detonationOrigin.value().origin().isNull() && (m_detonationRadius.value() < 1.0)) { m_detonationSceneMarker.clear(); } else { if (!m_detonationSceneMarker) { m_detonationSceneMarker = QSharedPointer(new GraphicalObjectSceneMarker(SituationMap::SceneMarkerType::SPHERE, m_mapImpl->getEsriGeoView(), m_graphicsLayer, m_debugInfo + QLatin1String("#DetonationSceneMarker"), m_uniqueMapId)); } m_detonationSceneMarker->setOrigin(m_detonationOrigin.value().originTranslated()); m_detonationSceneMarker->setDiameter(m_detonationRadius.value()*2.0); m_detonationSceneMarker->setPickable(m_pickable); // path == 0; indicationCont == 1; Marker == 2 m_detonationSceneMarker->setDrawOrder(m_internalDrawOrder+2); QColor col = fillStyle().fillColor(); //partyColor(); partyColor could be invalid, e.g. in case of NContamination! col.setAlphaF(col.alphaF() / 2.0); m_detonationSceneMarker->setFillStyle(FillStyle(col, FillPattern::Solid)); m_detonationSceneMarker->setVisibility(newVisibility); if (!m_detonationSceneMarker->updateView()) ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed for" << " debugInfo='" << m_detonationSceneMarker->debugInfo() << "'"; } TRACE_LOG(m_logger) << LOGFUNCTION << " END"; return true; } double viewAngleOfMapCoordinates3D(MapImpl * mapImpl, const Point3D &mapPos0, const Point3D &mapPos1, bool &bOkOut) { bOkOut = false; if (!mapImpl) return 0.0; QPointF p0 = mapImpl->mapCoordinateToViewPortWindowPoint3D(mapPos0); if (p0.isNull()) return 0.0; QPointF p1 = mapImpl->mapCoordinateToViewPortWindowPoint3D(mapPos1); if (p1.isNull()) return 0.0; p0.ry() = p0.ry() * -1; p1.ry() = p1.ry() * -1; double angle = MathUtils::getAngleDegreeNorth(p0, p1); // qCritical() << " mapPos0 = " << mapPos0 << " - mapPos1 = " << mapPos1; // qCritical() << " p0 = " << p0 << " - p1 = " << p1; // qCritical() << " angle = " << angle; bOkOut = true; return angle; } bool BasicPathObjectImpl::updateArrowHead(BasicPathObjectImpl::ArrowHead &arrowHead, bool start) { // pre-condition: if (m_positions.size() < 2) return false; if (!m_mainGraphicalObject) return false; bool realEnable = arrowHead.enable; if (arrowHead.enable && m_visibility && m_visibilityTechnical && (m_mapImpl->mode() == Mode::Mode_3D) ) { // 3D -> take distance into account: Point3D pt = m_positions.startPointTranslated(); Esri::ArcGISRuntime::Point esriEnd3D(pt.x(), pt.y(), pt.z(), m_mapImpl->getEsriSpatialReference()); const Esri::ArcGISRuntime::Point &camPoint = m_mapImpl->getEsriSceneGraphicsView()->currentViewpointCamera().location(); Esri::ArcGISRuntime::GeodeticDistanceResult distRes = Esri::ArcGISRuntime::GeometryEngine::distanceGeodetic(esriEnd3D, camPoint, Esri::ArcGISRuntime::LinearUnitId::Meters, Esri::ArcGISRuntime::AngularUnitId::Degrees, Esri::ArcGISRuntime::GeodeticCurveType::Geodesic); realEnable = distRes.distance() < 20000; } if (realEnable) { // delay creation if not visible: if (arrowHead.pmArrowHead.isNull() && m_visibility && m_visibilityTechnical) { arrowHead.pmArrowHead = QSharedPointer (new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView() , m_graphicsLayer , m_debugInfo + QLatin1String("#ArrowHead") , m_uniqueMapId) ); } if (arrowHead.pmArrowHead) { // maybe only if partyId changed or scale changed or selectionChanged { // colorChanged -> must be rendered again: const float scale = m_mainGraphicalObject->scale() * m_mainGraphicalObject->lineStyle().lineWidth() * 0.5; int lengthHeight = m_arrowHeadLengthHeight * scale; int halfWidth = m_arrowHeadHalfWidth * scale; const QImage arrowImg = renderArrowHeadSymbol(m_mainGraphicalObject->lineStyle().lineColor(), lengthHeight, halfWidth); arrowHead.pmArrowHead->setImage(arrowImg); arrowHead.pmArrowHead->setYOffset(lengthHeight / -2.0); } // take mapRotation into account, if necessary double mapRotation = 0; if (this->m_graphicsLayer->renderingMode() == Esri::ArcGISRuntime::GraphicsRenderingMode::Dynamic) mapRotation = m_mapImpl->rotation(); if (start) { arrowHead.pmArrowHead->setOrigin(m_positions.startPointTranslated()); if (m_mapImpl->mode() == Mode::Mode_2D) { arrowHead.pmArrowHead->setRotationAngle(m_positions.angleOfStartSegment() + 180 + mapRotation); } else { // Mode::Mode_3D: const PositionCont &posTranslated = m_positions.positionsTranslated(); if (m_mapImpl && posTranslated.size() >= 2) { bool bOkOut = false; double viewAngle = viewAngleOfMapCoordinates3D(m_mapImpl, posTranslated[1], posTranslated[0], bOkOut); if (bOkOut) arrowHead.pmArrowHead->setRotationAngle(viewAngle); else arrowHead.pmArrowHead->setRotationAngle(90); } } } else { switch (pathType()) { case GraphicalObjectPathType::Polyline: arrowHead.pmArrowHead->setOrigin(m_positions.endPointTranslated()); if (m_mapImpl->mode() == Mode::Mode_2D) arrowHead.pmArrowHead->setRotationAngle(m_positions.angleOfEndSegment() + mapRotation); else { // Mode::Mode_3D: const PositionCont &posTranslated = m_positions.positionsTranslated(); const int size = posTranslated.size(); if (m_mapImpl && size >= 2) { bool bOkOut = false; double viewAngle = viewAngleOfMapCoordinates3D(m_mapImpl, posTranslated.at(size - 2), posTranslated.at(size - 1), bOkOut); if (bOkOut) arrowHead.pmArrowHead->setRotationAngle(viewAngle); else arrowHead.pmArrowHead->setRotationAngle(-90); } } break; case GraphicalObjectPathType::Polygon: arrowHead.pmArrowHead->setOrigin(m_positions.startPointTranslated()); if (m_mapImpl->mode() == Mode::Mode_2D) arrowHead.pmArrowHead->setRotationAngle(m_positions.angleOfClosingSegment() + mapRotation); else { // Mode::Mode_3D: const PositionCont &posTranslated = m_positions.positionsTranslated(); const int size = posTranslated.size(); if (m_mapImpl && size >= 2) { bool bOkOut = false; double viewAngle = viewAngleOfMapCoordinates3D(m_mapImpl, posTranslated.first(), posTranslated.last(), bOkOut); if (bOkOut) arrowHead.pmArrowHead->setRotationAngle(viewAngle); else arrowHead.pmArrowHead->setRotationAngle(90); } } break; } // switch } arrowHead.pmArrowHead->setDrawOrder(m_internalDrawOrder + 1); arrowHead.pmArrowHead->setVisibility(m_visibility && m_visibilityTechnical); arrowHead.pmArrowHead->setPickable(m_pickable); arrowHead.pmArrowHead->setSelected(m_selected); arrowHead.pmArrowHead->updateView(); } // if (arrowHead.pmArrowHead) } else arrowHead.pmArrowHead.clear(); return true; } bool BasicPathObjectImpl::updateSegmentText() { // Qt5.4: QMap::lastKey(): "This function assumes that the map is not empty." if (m_textDataCont.isEmpty()) return true; if (!m_mapImpl) return false; bool success = true; // Maybe the number of coordinates has been decreased and therefore the number of segments: // They have to be removed to delete the TextData.goText (should be removed from screen.) while (!m_textDataCont.isEmpty() && m_textDataCont.lastKey() >= static_cast(this->segmentCount())) { // see QTBUG-59373 const auto key = m_textDataCont.lastKey(); m_textDataCont.remove(key); } // ok, 'resized' :-) // go on -> QMutableMapIterator it(m_textDataCont); int i = -1; while (it.hasNext()) { ++i; // current number of lineSegment exists? if (i >= segmentCount()) break; it.next(); TextData &textData = it.value(); if (textData.goText.isNull()) { QString debugInfo = QStringLiteral("%1#Text_seg(%2)").arg(m_debugInfo).arg(i); DEBUG_LOG(m_logger) << LOGFUNCTION << " create new Object " << " " << debugInfo; textData.goText = QSharedPointer (new GraphicalObjectText(m_mapImpl->getEsriGeoView() , m_graphicsLayer , debugInfo , m_uniqueMapId) ); } // calculate textOrigin on this segment: const Point3D &mapCoord1 = m_positions.positionsTranslated()[i]; const Point3D &mapCoord2 = m_positions.positionsTranslated()[i + 1]; const Point3D &textOrigin = MathUtils::getPointOnLine(mapCoord1, mapCoord2, 0.5); double angle = 0.0; if (m_mapImpl->mode() == Mode::Mode_2D) { angle = MathUtils::getAngleDegreeNorth(mapCoord1.toPointF(), mapCoord2.toPointF()); } else { // Mode::Mode_3D: const PositionCont &posTranslated = m_positions.positionsTranslated(); if (m_mapImpl && posTranslated.size() >= 2) { bool bOkOut = false; angle = viewAngleOfMapCoordinates3D(m_mapImpl, mapCoord1, mapCoord2, bOkOut); if (!bOkOut) angle = 90; // degree, 3 o'clock } } // take mapRotation into account, if necessary if ((this->m_graphicsLayer->renderingMode() == Esri::ArcGISRuntime::GraphicsRenderingMode::Dynamic) && (m_mapImpl->mode() == Mode::Mode_2D)) angle += m_mapImpl->rotation(); if (angle > 360.0) angle -= 360; else if (angle < -360.0) angle += 360; if ((angle >= 0) && (angle < 180)) { // Q1 & Q2 angle -= 90; } else { // Q3 & Q4 angle += 90; } textData.goText->setOrigin(textOrigin); textData.goText->setVisibility(m_visibility && m_visibilityTechnical); textData.goText->setDrawOrder(m_internalDrawOrder + 1); textData.goText->setText(textData.text); if (m_mainGraphicalObject) { textData.goText->setScale(m_mainGraphicalObject->scale()); textData.goText->setPickable(m_mainGraphicalObject->pickable()); } TextStyle ts = defaultTextStyle(); if (m_mainGraphicalObject) ts.setTextColor(m_mainGraphicalObject->lineStyle().lineColor()); // ts.setTextOutlineColor(Qt::white); // ts.setTextOutlineWidth(2); ts.setHorizontalAlignment(HorizontalAlignment::Center); ts.setVerticalAlignment(VerticalAlignment::Bottom); ts.setAngle(angle); // int outlineWith = 2 * scale(); // if (outlineWith > 4) // outlineWith = 4; // ts.setTextOutlineWidth( outlineWith ); textData.goText->setTextStyle(ts); // TextStyle ts; // ts.setTextOutlineColor(Qt::white); // ts.setTextOutlineWidth(2); // ts.setHorizontalAlignment(HorizontalAlignment::Center); // ts.setVerticalAlignment(VerticalAlignment::Middle); // ts.setAngle(angle); // ts.setSize(ts.size() * scale()); // int outlineWith = 2 * scale(); // if (outlineWith > 4) // outlineWith = 4; // ts.setTextOutlineWidth( outlineWith ); // textData.goText->setTextStyle(ts); if (! textData.goText->updateView()) { success = false; ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed" << " debugInfo1='" << m_debugInfo << "'" << " debugInfo2='" << textData.goText->debugInfo() << "'"; } } // while return success; } bool BasicPathObjectImpl::init(MapImpl *mapImpl, const QString &logicalLayerName, GraphicalObjectPathType pathType, double maxSegmentLength) { TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " logicalLayerName='" << logicalLayerName << "'" << " pathType=" << pathType; // to create adequate preview: m_isClosed = (pathType == GraphicalObjectPathType::Polygon); bool success = BaseClass::init(mapImpl, logicalLayerName); if (success) { // create underlying object (to save most attributes only there) if (!m_mainGraphicalObject) { // m_mainGraphicalObject = new GraphicalObjectMultiPath(pathType, GraphicalObjectPath *goPath = new GraphicalObjectPath(pathType, m_mapImpl->getEsriGeoView(), m_graphicsLayer, m_debugInfo + QLatin1String("#GOPath"), m_uniqueMapId); goPath->setMaxSegmentLength(maxSegmentLength); m_mainGraphicalObject = goPath; } } else ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; m_tacticalOrigin.refValue().setMapObjectImpl(this); m_detonationOrigin.refValue().setMapObjectImpl(this); TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; return success; } double BasicPathObjectImpl::maxSegmentLength() const { if (!m_mainGraphicalObject) return -1; GraphicalObjectPath *goPath = dynamic_cast(m_mainGraphicalObject); if (!goPath) return -1; return goPath->maxSegmentLength(); } bool BasicPathObjectImpl::onMapExtentChanged() { TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo=" << debugInfo(); bool success = BaseClass::onMapExtentChanged(); bool successIndication = true; if (m_indicationCont) { QElapsedTimer timer; if (m_logger.isDebugEnabled()) timer.start(); successIndication = m_indicationCont->updateView(); DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo=" << m_debugInfo << " Indication.updateView took[ms] " << timer.elapsed(); } bool successIndicationAbs = true; if (m_indicationContSpAbsolute) { QElapsedTimer timer; if (m_logger.isDebugEnabled()) timer.start(); successIndicationAbs = m_indicationContSpAbsolute->updateView(); DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo=" << m_debugInfo << " IndicationSpAbsolute.updateView took[ms] " << timer.elapsed(); } bool successPath1 = true; if (m_pathCont_SpDraped) { QElapsedTimer timer; if (m_logger.isDebugEnabled()) timer.start(); successPath1 = m_pathCont_SpDraped->updateView(); DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo=" << m_debugInfo << " m_pathCont_SpDraped.updateView took[ms] " << timer.elapsed(); } bool successPath2 = true; if (m_pathCont_SpDrapedPolygon) { QElapsedTimer timer; if (m_logger.isDebugEnabled()) timer.start(); successPath2 = m_pathCont_SpDrapedPolygon->updateView(); DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo=" << m_debugInfo << " m_pathCont_SpDrapedPolygon.updateView took[ms] " << timer.elapsed(); } bool successPathSpA1 = true; if (m_pathCont_SpAbsolute1) { QElapsedTimer timer; if (m_logger.isDebugEnabled()) timer.start(); successPathSpA1 = m_pathCont_SpAbsolute1->updateView(); DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo=" << m_debugInfo << " m_pathCont_SpAbsolute1.updateView took[ms] " << timer.elapsed(); } bool successPathSpA2 = true; if (m_pathCont_SpAbsolute2) { QElapsedTimer timer; if (m_logger.isDebugEnabled()) timer.start(); successPathSpA2 = m_pathCont_SpAbsolute2->updateView(); DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo=" << m_debugInfo << " m_pathCont_SpAbsolute2.updateView took[ms] " << timer.elapsed(); } // to honor mapRotation: updateArrowHead(m_arrowHeadStart, true); updateArrowHead(m_arrowHeadEnd, false); updateSegmentText(); success = success && successIndication && successIndicationAbs && successPath1 && successPath2 && successPathSpA1 && successPathSpA2; if (!success) ERROR_LOG(m_logger) << " successIndication=" << successIndication << " successIndicationAbs=" << successIndicationAbs << " successPath1=" << successPath1 << " successPath2=" << successPath2 << " successPathSpA1=" << successPathSpA1 << " successPathSpA2=" << successPathSpA2; TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; return success; } } // namespace SituationMap