557 lines
17 KiB
C++
Executable File
557 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 <arcgis/GeoView.h>
|
|
#include <arcgis/Graphic.h>
|
|
#include <arcgis/GraphicsOverlay.h>
|
|
#include <arcgis/Multipart.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::graphicRemoved,
|
|
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
|