Files
qt5-legatus-legacy/Lts/Viewer/SituationMap/MapObjectsImpl/basic_path_object_impl.cpp
T
2026-08-13 18:33:10 +02:00

1113 lines
36 KiB
C++
Executable File

/**
* @RDE_HEADER@
* Interface-Implementation of BasicPathObjectImpl
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "basic_path_object_impl.h"
#include "indication_container.h"
#include <MapImpl/map_impl.h>
#include <MapImpl/coord_translation_functor_impl.h>
#include <MapObjects/geometry.h>
#include <GraphicalObjects/graphical_object_path.h>
//#include <GraphicalObjects/graphical_object_multi_path.h>
#include <GraphicalObjects/graphical_object_ellipse.h>
#include <GraphicalObjects/graphical_object_scene_marker.h>
#include <GraphicalObjects/graphical_object_text.h>
#include <GraphicalObjects/tactical_appearance.h>
#include <MathUtils/simple_helper.h>
// ESRI
#include <arcgis/GeoView.h>
#include <arcgis/SceneGraphicsView.h>
// Qt
#include <QElapsedTimer>
#include <QPainter>
#include <QPen>
#include <QPoint>
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<int> 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<GraphicalObjectPath *>(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<QPointF> 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<GraphicalObjectMultiPath *>(m_mainGraphicalObject);
QList<int> pathIdxInUse;
int pathIdx = 0;
QMutableMapIterator<int, PositionsTranslated> 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<GraphicalObjectSceneMarker>(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<GraphicalObjectPictureMarker>
(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<uint>(this->segmentCount()))
{
// see QTBUG-59373
const auto key = m_textDataCont.lastKey();
m_textDataCont.remove(key);
}
// ok, 'resized' :-)
// go on ->
QMutableMapIterator<uint, TextData> 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<GraphicalObjectText>
(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<GraphicalObjectPath*>(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