1113 lines
36 KiB
C++
Executable File
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
|