Files
qt-migration-legatus/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/abstract_graphical_object.cpp
T

563 lines
17 KiB
C++
Executable File

/**
* @RDE_HEADER@
* AbstractGraphicalObject
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "abstract_graphical_object.h"
#include <Map/situation_map_utils.h> // operator<<
// ESRI
#include <GeoView.h>
#include <Graphic.h>
#include <GraphicsOverlay.h>
#include <Multipart.h>
#include <GraphicListModel.h>
#include <AttributeListModel.h>
#include <ImmutablePartCollection.h>
#include <ImmutablePart.h>
#include <SpatialReference.h>
#include <SymbolTypes.h>
// Qt
#include <QDebug>
#include <QSharedPointer>
#include <QApplication>
#include <QThread>
#include <QObject>
namespace SituationMap
{
AbstractGraphicalObject::AbstractGraphicalObject(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: m_geoView(geoView)
, m_graphicsLayer(graphicsLayer)
, m_uniqueMapId(uniqueMapId)
, m_debugInfo(debugInfo)
, m_logger(getLogger("AbstractGraphicalObject"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
m_pickable = (m_uniqueMapId != INVALID_MAP_ID);
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
AbstractGraphicalObject::~AbstractGraphicalObject()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< ", m_graphicsLayer-overlayId=" << (m_graphicsLayer ? m_graphicsLayer->overlayId() : QString(QStringLiteral("unknown")));
#if ACRGIS_VERSION >= 0x640F00 // 100.15.0
// removeOne ist helpful to let ModuleTest succeed: 'situationMapModuleTest checkOverlayHandler3 none'
if (m_graphic && m_graphicsLayer && m_graphicsLayer->graphics())
m_graphicsLayer->graphics()->removeOne(m_graphic);
delete m_graphic;
m_graphic = nullptr;
#else
/*
// cleanup
// to make sure, the the graphic is removed from internal container BEFORE
// graphic is deleted, follow the documentation: use Layer::removeGraphic
// (instead 'delete m_graphic')
if (m_graphic && m_graphicsLayer && m_graphicsLayer->graphics())
m_graphicsLayer->graphics()->removeOne(m_graphic);
if (m_graphic)
{
delete m_graphic;
m_graphic = nullptr;
}
*/
// Race condition between removeOne() and delete m_graphic :
// BUG-000116867 is for the deadlock
// BUG-000117464 is for the crash
// Use following workaround until ESRI will release the fix (with 100.5!?)
if (m_graphic && m_graphicsLayer && m_graphicsLayer->graphics())
{
const int idx = m_graphicsLayer->graphics()->indexOf(m_graphic);
if (idx < 0)
ERROR_LOG(m_logger) << LOGFUNCTION << " indexOf failed, debugInfo='" << m_debugInfo << "'";
auto graphicCopy = m_graphic;
QObject::connect (m_graphicsLayer->graphics(), &Esri::ArcGISRuntime::GraphicListModel::itemRemoved,
m_graphic, [idx, graphicCopy](int index)
{
if (idx == index)
delete graphicCopy;
});
m_graphicsLayer->graphics()->removeOne(m_graphic);
}
// : Race condition between removeOne() and delete m_graphic
// m_esriSimpleLineSymbol AND m_esriSimpleFillSymbol will be destroyed m_graphic
#endif // ACRGIS_VERSION >= 0x640F00
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
Esri::ArcGISRuntime::Graphic *AbstractGraphicalObject::graphic()
{
return m_graphic;
}
Esri::ArcGISRuntime::GraphicsOverlay *AbstractGraphicalObject::graphicsLayer()
{
return m_graphicsLayer;
}
Esri::ArcGISRuntime::Geometry AbstractGraphicalObject::getGraphicGeometry() const
{
if (! m_graphic)
return Esri::ArcGISRuntime::Geometry();
return m_graphic->geometry();
}
bool AbstractGraphicalObject::updateView()
{
if (!changed())
return true;
bool success = updateViewImpl();
resetChanged();
return success;
}
QString AbstractGraphicalObject::debugInfo() const
{
return m_debugInfo;
}
void AbstractGraphicalObject::setDebugInfo(const QString &debugInfo)
{
m_debugInfo = debugInfo;
}
int AbstractGraphicalObject::drawOrder() const
{
return m_drawOrder.value();
}
void AbstractGraphicalObject::setDrawOrder(int drawOrder)
{
m_drawOrder = drawOrder;
// if (m_graphic)
// m_graphic->setDrawOrder(m_drawOrder.value());
}
QString AbstractGraphicalObject::toString() const
{
QString str;
if (!m_debugInfo.isEmpty())
str.append(QStringLiteral("debugInfo=%1").arg(m_debugInfo));
if (m_graphicsLayer)
str.append(QStringLiteral(" layer='%1'").arg(m_graphicsLayer->objectName()));
str.append(QStringLiteral(" m_drawOrder='%1'").arg(m_drawOrder.value()));
if (m_graphic)
str.append(QStringLiteral(" zIndex='%1'").arg(m_graphic->zIndex()));
str.append(QStringLiteral(" visibility='%1'").arg(m_visibility.value()));
str.append(QStringLiteral(" selected='%1'").arg(m_selected.value()));
str.append(QStringLiteral(" scale='%1'").arg(m_scale.value()));
str.append(QStringLiteral(" positions.count='%1'").arg(m_positions.value().count()));
return str;
}
UniqueMapId AbstractGraphicalObject::uniqueMapId() const
{
return m_uniqueMapId;
}
void AbstractGraphicalObject::setUniqueMapId(UniqueMapId uniqueId)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId=" << uniqueId;
m_uniqueMapId = uniqueId;
// use attribute to store application obj id (SimWorld) at graphic
if (m_graphic)
{
// use attributeModel to store application obj id (SimWorld) at graphic
Esri::ArcGISRuntime::AttributeListModel *attListModel = m_graphic->attributes();
if (attListModel)
attListModel->insertAttribute(QLatin1String(SituationMap::UniqueMapIdAttribute), m_uniqueMapId);
else
ERROR_LOG(m_logger) << LOGFUNCTION << " no AttributeListModel, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId=" << m_uniqueMapId;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId=" << uniqueId;
}
Point3D AbstractGraphicalObject::origin() const
{
return m_positions.value().value(0); // value => if index is out of range -> default object
}
void AbstractGraphicalObject::setOrigin(const Point3D &origin)
{
// TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
// << " origin=" << origin;
if (m_positions.value().isEmpty())
m_positions.refValue().append(origin);
else
{
// https://stackoverflow.com/a/1573715/2352507
// Any comparison(except with "!=") with NaN returns false.
// NaN != NaN -> true!!!
if (m_positions.value().value(0) != origin) // NaN != NaN -> true!!!
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " value(0) = " << m_positions.value().value(0) << " origin = " << origin;
m_positions.refValue().clear();
m_positions.refValue().append(origin);
}
}
// TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
// << " origin=" << origin
// << " changed=" << m_positions.changed()
// << " SIZE=" << m_positions.value().size();
}
void AbstractGraphicalObject::setOrigin(const QPointF &origin)
{
setOrigin(Point3D(origin.x(), origin.y(), .0));
}
PositionCont AbstractGraphicalObject::positions() const
{
return m_positions.value();
}
void AbstractGraphicalObject::setPositions(const PositionCont &posCont)
{
m_positions = posCont;
// TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
// << " changed=" << m_positions.changed();
}
bool AbstractGraphicalObject::isSinglePoint() const
{
return m_positions.value().count() == 1;
}
bool AbstractGraphicalObject::isMultiPoint() const
{
return m_positions.value().count() > 1;
}
bool AbstractGraphicalObject::isArea() const
{
return m_positions.value().count() > 2;
}
LineStyle AbstractGraphicalObject::lineStyle() const
{
return m_lineStyle.value();
}
LineStyle &AbstractGraphicalObject::rLineStyle()
{
return m_lineStyle.refValue();
}
void AbstractGraphicalObject::setLineStyle(const LineStyle &lineStyle)
{
m_lineStyle = lineStyle;
}
FillStyle AbstractGraphicalObject::fillStyle() const
{
return m_fillStyle.value();
}
FillStyle &AbstractGraphicalObject::rFillStyle()
{
return m_fillStyle.refValue();
}
void AbstractGraphicalObject::setFillStyle(const FillStyle &fillStyle)
{
m_fillStyle = fillStyle;
}
bool AbstractGraphicalObject::visibility() const
{
return m_visibility.value();
}
void AbstractGraphicalObject::setVisibility(bool visibility)
{
m_visibility = visibility;
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " visibility=" << m_visibility;
}
bool AbstractGraphicalObject::selected() const
{
return m_selected.value();
}
void AbstractGraphicalObject::setSelected(bool selected)
{
m_selected = selected;
}
bool AbstractGraphicalObject::pickable() const
{
return m_pickable.value();
}
void AbstractGraphicalObject::setPickable(bool pickable)
{
// allow pickable only if m_uniqueMapId is valid:
m_pickable = pickable && (m_uniqueMapId != INVALID_MAP_ID);
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " pickable=" << m_pickable;
}
float AbstractGraphicalObject::scale() const
{
return m_scale.value();
}
void AbstractGraphicalObject::setScale(float scale)
{
m_scale = scale;
}
bool AbstractGraphicalObject::addUpdateGraphic()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " updateGraphic";
bool successGraphic = true;
if (! m_graphic)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " no m_graphic -> return true";
return true;
}
// append just once :-)
if (! m_graphicsLayer->graphics()->contains(m_graphic))
{
m_graphicsLayer->graphics()->append(m_graphic);
successGraphic = m_graphicsLayer->graphics()->contains(m_graphic);
}
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " successGraphic=" << successGraphic;
return successGraphic;
}
bool AbstractGraphicalObject::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId =" << m_uniqueMapId
<< " origin =" << this->origin()
<< " visibility=" << m_visibility;
bool success = true;
// some checks:
if (!m_graphicsLayer)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " no graphicsLayer";
return false;
}
if (!m_graphic)
{
m_graphic = new Esri::ArcGISRuntime::Graphic();
m_graphic->setObjectName(m_debugInfo);
m_graphic->setZIndex(m_drawOrder.value());
// use attributeModel to store application obj id (SimWorld) at graphic
Esri::ArcGISRuntime::AttributeListModel *attLisModel = m_graphic->attributes();
if (attLisModel)
attLisModel->insertAttribute(QLatin1String(SituationMap::UniqueMapIdAttribute), m_uniqueMapId);
else
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no AttributeListModel, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId=" << m_uniqueMapId;
}
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success=" << success;
return success;
}
bool AbstractGraphicalObject::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId=" << m_uniqueMapId
<< " origin=" << this->origin()
<< " visibility=" << m_visibility
<< " selected=" << m_selected
<< " drawOrder=" << m_drawOrder
<< " pickable=" << m_pickable
<< " pickableCHG=" << m_pickable.changed();
if (QApplication::instance()->thread()->currentThreadId() != QThread::currentThreadId())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " wrong Thread, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId=" << m_uniqueMapId;
abort();
}
if (m_graphic)
{
m_graphic->setZIndex(m_drawOrder.value());
m_graphic->setVisible(m_visibility.value());
m_graphic->setSelected(m_selected.value());
// Picking:
if (m_pickable.changed())
{
// use attributeModel to store m_pickable at graphic
Esri::ArcGISRuntime::AttributeListModel *attLisModel = m_graphic->attributes();
if (attLisModel)
attLisModel->insertAttribute(QLatin1String(SituationMap::IsPickableAttribute), m_pickable.value());
else
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no AttributeListModel, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId=" << m_uniqueMapId;
}
}
m_pickable.reset();
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId =" << m_uniqueMapId;
return true;
}
bool AbstractGraphicalObject::assignLineStyleToGraphic()
{
if (!m_graphic)
return false;
const LineStyle &ls = m_lineStyle.value();
if (!m_esriSimpleLineSymbol)
{
m_esriSimpleLineSymbol =
new Esri::ArcGISRuntime::SimpleLineSymbol(convert(ls.linePattern()), ls.lineColor(), ls.lineWidth() * m_scale.value(), m_graphic);
} else {
m_esriSimpleLineSymbol->setStyle(convert(ls.linePattern()));
m_esriSimpleLineSymbol->setColor(ls.lineColor());
m_esriSimpleLineSymbol->setWidth(ls.lineWidth() * m_scale.value());
}
m_graphic->setSymbol(m_esriSimpleLineSymbol.data());
return true;
}
bool AbstractGraphicalObject::assignFillStyleToGraphic()
{
if (!m_graphic)
return false;
const LineStyle &ls = m_lineStyle.value();
const FillStyle &fs = m_fillStyle.value();
// 1. the Line
if (!m_esriSimpleLineSymbol)
{
m_esriSimpleLineSymbol =
new Esri::ArcGISRuntime::SimpleLineSymbol(convert(ls.linePattern()), ls.lineColor(), ls.lineWidth() * m_scale.value(), m_graphic);
} else {
m_esriSimpleLineSymbol->setStyle(convert(ls.linePattern()));
m_esriSimpleLineSymbol->setColor(ls.lineColor());
m_esriSimpleLineSymbol->setWidth(ls.lineWidth() * m_scale.value());
}
// 1. the Fill
if (!m_esriSimpleFillSymbol)
{
m_esriSimpleFillSymbol =
new Esri::ArcGISRuntime::SimpleFillSymbol(convert(fs.fillPattern()), fs.fillColor(), m_esriSimpleLineSymbol, m_graphic);
} else {
m_esriSimpleFillSymbol->setStyle(convert(fs.fillPattern()));
m_esriSimpleFillSymbol->setColor(fs.fillColor());
m_esriSimpleFillSymbol->setOutline(m_esriSimpleLineSymbol);
}
m_graphic->setSymbol(m_esriSimpleFillSymbol.data());
return true;
}
Esri::ArcGISRuntime::SimpleLineSymbolStyle AbstractGraphicalObject::convert(const LinePattern linePattern)
{
switch (linePattern)
{
case LinePattern::Dash: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::Dash;
case LinePattern::DashDot: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::DashDot;
case LinePattern::DashDotDot: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::DashDotDot;
case LinePattern::Dot: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::Dot;
case LinePattern::Null: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::Null;
case LinePattern::Solid: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::Solid;
} // switch
return Esri::ArcGISRuntime::SimpleLineSymbolStyle::Solid;
}
Esri::ArcGISRuntime::SimpleFillSymbolStyle AbstractGraphicalObject::convert(const FillPattern fillPattern)
{
switch (fillPattern)
{
case FillPattern::BackwardDiagonal: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::BackwardDiagonal;
case FillPattern::Cross: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::Cross;
case FillPattern::DiagonalCross: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::DiagonalCross;
case FillPattern::ForwardDiagonal: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::ForwardDiagonal;
case FillPattern::Horizontal: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::Horizontal;
case FillPattern::Null: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::Null;
case FillPattern::Solid: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid;
case FillPattern::Vertical: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::Vertical;
} // switch
return Esri::ArcGISRuntime::SimpleFillSymbolStyle::BackwardDiagonal;
}
int AbstractGraphicalObject::countOfPolygonFirstPart(const Esri::ArcGISRuntime::Multipart &polylinePolygon)
{
if (polylinePolygon.isEmpty() || polylinePolygon.parts().isEmpty())
return -1;
return polylinePolygon.parts().part(0).pointCount();
}
const Esri::ArcGISRuntime::SpatialReference AbstractGraphicalObject::spatialReference() const
{
return m_geoView->spatialReference();
}
} // namespace SituationMap