Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

@@ -0,0 +1,556 @@
/**
* @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
@@ -0,0 +1,213 @@
/**
* @RDE_HEADER@
* AbstractGraphicalObject
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/situation_map_types.h>
#include <Map/line_style.h>
#include <Map/fill_style.h>
#include <Common/simple_value.h>
#include <Common/situation_map_logging.h>
// ESRI
#include <arcgis/SimpleLineSymbol.h>
#include <arcgis/SimpleFillSymbol.h>
// Qt
#include <QPointer>
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class GeoView;
class Graphic;
class GraphicsOverlay;
class Geometry;
class Multipart;
class SpatialReference;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
//forward
class GraphicalObjectText;
class SITUATIONMAP_EXPORT AbstractGraphicalObject
{
public:
AbstractGraphicalObject(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
virtual ~AbstractGraphicalObject();
AbstractGraphicalObject(const AbstractGraphicalObject &other) = delete;
AbstractGraphicalObject &operator=(AbstractGraphicalObject &other) = delete;
virtual Esri::ArcGISRuntime::Graphic *graphic();
virtual Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer();
virtual QString toString() const;
virtual UniqueMapId uniqueMapId() const;
virtual void setUniqueMapId(UniqueMapId uniqueId);
virtual QString debugInfo() const;
virtual void setDebugInfo(const QString &debugInfo);
virtual int drawOrder() const;
virtual void setDrawOrder(int drawOrder);
// Data access:
// for SinglePoint:
virtual Point3D origin() const;
virtual void setOrigin(const Point3D &origin);
virtual void setOrigin(const QPointF &origin); // ToDO accept this??
// for MultiPoint:
virtual PositionCont positions() const;
virtual void setPositions(const PositionCont &posCont);
virtual bool isSinglePoint() const;
virtual bool isMultiPoint() const;
virtual bool isArea() const;
virtual LineStyle lineStyle() const;
virtual LineStyle &rLineStyle();
/**
* @brief setLineStyle (Font-)size is limited to 500.
* (High values can lead to coredump with 'SymbolDrawHelper::create_line_image FAILED, SkBitmap is NULL or empty')
* @param lineStyle to set.
*/
virtual void setLineStyle(const LineStyle &lineStyle);
virtual FillStyle fillStyle() const;
virtual FillStyle &rFillStyle();
virtual void setFillStyle(const FillStyle &fillStyle);
// Scale :
virtual float scale() const;
virtual void setScale(float scale);
// : Scale
// Visibility :
virtual bool visibility() const;
virtual void setVisibility(bool visibility);
// : Visibility
// Selection, Picking :
bool selected() const;
void setSelected(bool selected);
bool pickable() const;
/**
* @brief setPickable The method allows pickable=true only if uniqueMapIdis valid.
* @param pickable new value.
*/
void setPickable(bool pickable);
// : Selection, Picking
virtual Esri::ArcGISRuntime::Geometry getGraphicGeometry() const;
bool updateView();
bool addUpdateGraphic();
static Esri::ArcGISRuntime::SimpleLineSymbolStyle convert(const LinePattern linePattern);
static Esri::ArcGISRuntime::SimpleFillSymbolStyle convert(const FillPattern fillPattern);
bool assignLineStyleToGraphic();
bool assignFillStyleToGraphic();
static int countOfPolygonFirstPart(const Esri::ArcGISRuntime::Multipart &polylinePolygon);
protected:
// Internal/Admin
const Esri::ArcGISRuntime::SpatialReference spatialReference() const;
virtual bool changed()
{
return m_drawOrder.changed()
|| m_positions.changed()
|| m_visibility.changed()
|| m_selected.changed()
|| m_pickable.changed()
|| m_scale.changed()
|| m_lineStyle.changed()
|| m_fillStyle.changed();
}
virtual bool updateViewImpl() = 0;
virtual void resetChanged()
{
m_drawOrder.reset();
m_positions.reset();
m_visibility.reset();
m_selected.reset();
m_pickable.reset();
m_scale.reset();
m_lineStyle.reset();
m_fillStyle.reset();
}
virtual bool create() = 0;
const Esri::ArcGISRuntime::GeoView *m_geoView = nullptr;
Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr;
Esri::ArcGISRuntime::Graphic *m_graphic = nullptr;
SimpleValue<int> m_drawOrder = 0;
// Data
UniqueMapId m_uniqueMapId;
QString m_debugInfo; ///< for debugging
SimpleValue<PositionCont> m_positions;
SimpleValue<bool> m_visibility = true;
SimpleValue<bool> m_selected = false;
SimpleValue<bool> m_pickable = true;
SimpleValue<float> m_scale = 1.0;
SimpleValue<LineStyle> m_lineStyle;
SimpleValue<FillStyle> m_fillStyle;
QPointer<Esri::ArcGISRuntime::SimpleLineSymbol> m_esriSimpleLineSymbol = nullptr;
QPointer<Esri::ArcGISRuntime::SimpleFillSymbol> m_esriSimpleFillSymbol = nullptr;
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
} // namespace SituationMap
@@ -0,0 +1,96 @@
/**
* @RDE_HEADER@
* EllipseParameters
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "ellipse_parameters.h"
// ESRI
// Qt
#include <QString>
namespace SituationMap
{
float EllipseParameters::lengthX() const
{
return m_lengthX;
}
void EllipseParameters::setLengthX(float lengthX)
{
m_lengthX = lengthX;
}
float EllipseParameters::lengthY() const
{
return m_lengthY;
}
void EllipseParameters::setLengthY(float lengthY)
{
m_lengthY = lengthY;
}
float EllipseParameters::directionDegree() const
{
return m_directionDegree;
}
void EllipseParameters::setDirectionDegree(float directionDegree)
{
m_directionDegree = directionDegree;
}
int EllipseParameters::maxSegmentLength() const
{
return m_maxSegmentLength;
}
void EllipseParameters::setMaxSegmentLength(int value)
{
m_maxSegmentLength = value;
}
int EllipseParameters::maxPointCount() const
{
return m_maxPointCount;
}
void EllipseParameters::setMaxPointCount(int maxPointCount)
{
m_maxPointCount = maxPointCount;
}
bool EllipseParameters::isValid() const
{
return ((m_lengthX > 0) && (m_lengthY > 0)) ;
}
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<< (std::ostream &stream,
const EllipseParameters &value)
{
QString str;
str.append(QStringLiteral(" lengthX=%1").arg(value.lengthX()));
str.append(QStringLiteral(" lengthY=%1").arg(value.lengthY()));
str.append(QStringLiteral(" directionDegree=%1").arg(value.directionDegree()));
str.append(QStringLiteral(" maxSegmentLength=%1").arg(value.maxSegmentLength()));
stream << str.toStdString();
return stream;
}
} // namespace SituationMap
@@ -0,0 +1,95 @@
/**
* @RDE_HEADER@
* EllipseParameters
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <situation_map_export.h>
#include <ostream>
// Qt
//----------------------------------------------------------------------------
namespace SituationMap
{
//----------------------------------------------------------------------------
class SITUATIONMAP_EXPORT EllipseParameters
{
public:
static constexpr unsigned int MAX_POINT_COUNT_DEFAULT = 30;
EllipseParameters() = default;
float lengthX() const;
void setLengthX(float lengthX);
float lengthY() const;
void setLengthY(float lengthY);
float directionDegree() const;
void setDirectionDegree(float directionDegree);
int maxSegmentLength() const;
void setMaxSegmentLength(int value);
int maxPointCount() const;
void setMaxPointCount(int maxPointCount);
bool isValid() const;
/**
* @brief operator== Compare two objects
* @param other to be compared with
* @return true if identical, otherwise false.
*/
bool operator==(const EllipseParameters &other) const
{
return ((this->m_lengthX == other.m_lengthX) &&
(this->m_lengthY == other.m_lengthY) &&
(this->m_directionDegree == other.m_directionDegree) &&
(this->m_maxSegmentLength == other.m_maxSegmentLength) &&
(this->m_maxPointCount == other.m_maxPointCount)
);
}
/**
* @brief operator!= Compare two objects
* @param other to be compared with
* @return true if NOT identical, otherwise false.
*/
bool operator!=(const EllipseParameters &other) const
{
return !(*this == other);
}
private:
float m_lengthX = 0;
float m_lengthY = 0;
float m_directionDegree = 0;
int m_maxSegmentLength = 10; // meter
int m_maxPointCount = MAX_POINT_COUNT_DEFAULT;
};
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
SITUATIONMAP_EXPORT std::ostream &operator<< (std::ostream &stream,
const EllipseParameters &value);
} // namespace SituationMap
@@ -0,0 +1,190 @@
/**
* @RDE_HEADER@
* GraphicalObjectAttributes
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_attributes.h"
#include <MapImpl/map_impl.h>
#include <Map/situation_map_utils.h> // operator<<
#include <Common/simple_value.h>
// ESRI
#include <arcgis/Map.h>
#include <arcgis/GraphicsOverlay.h>
// Qt
namespace SituationMap
{
class GraphicalObjectAttributes::Private
{
public:
Private() = default;
~Private() = default;
// ESRI data:
MapImpl *m_mapImpl = nullptr;
QString m_logicalLayerName;
// OWN data:
SimpleValue<QVariantMap> m_attributes;
}; // class GraphicalObjectAttributes::Private
//-----------------------------------------------------------------------------
GraphicalObjectAttributes::GraphicalObjectAttributes(MapImpl *mapImpl,
Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer,
const QString &debugInfo,
const UniqueMapId &uniqueMapId)
: BaseClass(mapImpl->getEsriGeoView(), graphicsLayer, debugInfo, uniqueMapId),
m_logger(getLogger("GraphicalObjectAttributes")),
d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectAttributes::~GraphicalObjectAttributes()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
bool GraphicalObjectAttributes::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " m_visibility = " << m_visibility;
bool success = BaseClass::create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
bool successGraphic = addUpdateGraphic(); // CHECK
if (!successGraphic)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " addUpdateGraphic failed, debugInfo='" << m_debugInfo << "'" << " uniqueMapId=" << m_uniqueMapId;
return false;
}
// real work:
// empty
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
return success;
}
QVariantMap GraphicalObjectAttributes::attributes() const
{
return d->m_attributes.value();
}
void GraphicalObjectAttributes::setAttributes(const QVariantMap &attributes)
{
d->m_attributes = attributes;
}
bool GraphicalObjectAttributes::changed()
{
return BaseClass::changed()
|| d->m_attributes.changed();
}
void GraphicalObjectAttributes::resetChanged()
{
BaseClass::resetChanged();
d->m_attributes.reset();
}
Esri::ArcGISRuntime::Geometry GraphicalObjectAttributes::getGraphicGeometry() const
{
if (m_graphic) return m_graphic->geometry();
ERROR_LOG(m_logger) << LOGFUNCTION << "Geometry is undefined";
return Esri::ArcGISRuntime::Geometry();
} // Esri::ArcGISRuntime::Geometry GraphicalObjectAttributes::getGraphicGeometry() const
bool GraphicalObjectAttributes::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " visibility=" << m_visibility
<< " selected=" << m_selected
<< " origin=" << this->origin()
<< " scale=" << m_scale.value();
bool success = create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
if (!BaseClass::updateViewImpl())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
// real work:
if (m_positions.changed())
{
const PositionCont &posCont = m_positions.value();
if (posCont.size() == 1)
{
const Point3D &origin = this->origin();
Esri::ArcGISRuntime::Point originGeometry(origin.x(),
origin.y(),
origin.z(),
spatialReference());
m_graphic->setGeometry(originGeometry);
}
}
if (d->m_attributes.changed())
{
QVariantMap rawAttributes = d->m_attributes.value();
// add basics:
rawAttributes[QLatin1String(SituationMap::UniqueMapIdAttribute)] = m_uniqueMapId;
rawAttributes[QLatin1String(SituationMap::IsPickableAttribute)] = m_pickable.value();
m_graphic->attributes()->setAttributesMap(rawAttributes);
if (m_logger.isDebugEnabled())
qCritical() << " XXXXX set rawAttributes " << rawAttributes;
}
if (m_graphic)
{
m_graphic->setZIndex(m_drawOrder.value());
m_graphic->setVisible(m_visibility.value());
m_graphic->setSelected(m_selected.value());
}
if (!success)
ERROR_LOG(m_logger) << LOGFUNCTION << " failure, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success = " << success
<< " visibility=" << m_visibility;
return success;
}
} // namespace SituationMap
@@ -0,0 +1,88 @@
/**
* @RDE_HEADER@
* GraphicalObjectAttributes
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
// Esri
// Qt
// forward:
//----------------------------------------------------------------------------
namespace SituationMap
{
// forward:
class MapImpl;
/**
* @brief The GraphicalObjectAttributes class
*/
class SITUATIONMAP_EXPORT GraphicalObjectAttributes : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
/**
* @brief GraphicalObjectAttributes
* @param mapImpl map to be used.
* @param logicalLayerName logical layer name to be used.
* @param debugInfo debugInfo to be used
* @param uniqueMapId uniqueMapId to be used. (Optional to fulfill rubberband requirements)
*/
GraphicalObjectAttributes(MapImpl *mapImpl,
Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer,
const QString &debugInfo,
const UniqueMapId &uniqueMapId = INVALID_MAP_ID);
GraphicalObjectAttributes(const GraphicalObjectAttributes &other) = delete;
GraphicalObjectAttributes &operator=(GraphicalObjectAttributes &other) = delete;
virtual ~GraphicalObjectAttributes();
QVariantMap attributes() const;
void setAttributes(const QVariantMap &attributes);
Esri::ArcGISRuntime::Geometry getGraphicGeometry() const override;
protected:
// Internal/Admin
virtual bool create() override;
bool changed() override;
void resetChanged() override;
bool updateViewImpl() override;
// logical
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
class Private;
/**
* The private d-pointer to not break BC when changing internals of this class
*/
Private *d = nullptr;
};
} // namespace SituationMap
@@ -0,0 +1,162 @@
/**
* @RDE_HEADER@
* GraphicalObjectCube
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_cube.h"
#include <Map/situation_map_utils.h> // operator<<
// ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/PolygonBuilder.h>
// Qt
namespace SituationMap
{
GraphicalObjectCube::GraphicalObjectCube(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_logger(getLogger("GraphicalObjectCube"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectCube::~GraphicalObjectCube()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
bool GraphicalObjectCube::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
bool success = BaseClass::create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
}
// real work:
// empty
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
// << " drawOrder = " << m_graphic->drawOrder()
<< " success = " << success;
return success;
}
bool GraphicalObjectCube::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
// some checks:
if (!this->isArea())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
if (!create())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
if (!BaseClass::updateViewImpl())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
// real work:
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'";
// Attribute 'height':
QVariantMap attMap = m_graphic->attributes()->attributesMap();
attMap[QStringLiteral("height")] = QVariant(m_height.value());
//
// Attribute 'PartyName' is replaced by Attribute 'Extrusion_Color' to be more flexible
//
// Attribute 'Extrusion_Color':
if (m_extrusionColorName.value().isEmpty())
{
DEBUG_LOG(m_logger) << LOGFUNCTION
<< " debugInfo='" << m_debugInfo << "'"
<< " m_extrusionColorName is empty";
// -> time for the defaultSymbol of the Renderer
attMap[QStringLiteral("Extrusion_Color")] = QStringLiteral("DOES_NOT_EXIST");
}
else
attMap[QStringLiteral("Extrusion_Color")] = QVariant(m_extrusionColorName.value());
m_graphic->attributes()->setAttributesMap(attMap);
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " attributesMap[height] ='" << attMap[QStringLiteral("height")].toDouble() << "'";
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " attributesMap[Extrusion_Color] ='" << attMap[QStringLiteral("Extrusion_Color")].toString() << "'";
// geometry:
Esri::ArcGISRuntime::PolygonBuilder builder(spatialReference());
const PositionCont &posCont = m_positions.value();
for (const Point3D &pos : posCont)
{
builder.addPoint(pos.x(), pos.y());
}
if (! builder.isSketchValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " builder -> sketch invalid";
return false;
}
m_graphic->setGeometry(builder.toGeometry());
// add
bool successGraphic = addUpdateGraphic();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " drawOrder = " << m_graphic->zIndex();
return successGraphic;
}
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<<(std::ostream &stream,
const GraphicalObjectCube &value)
{
stream << value.toString();
return stream;
}
} // namespace SituationMap
@@ -0,0 +1,100 @@
/**
* @RDE_HEADER@
* GraphicalObjectCube
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
// Esri
// Qt
// forward:
//----------------------------------------------------------------------------
namespace SituationMap
{
/**
* @brief The GraphicalObjectCube class for 3D
*
*/
class SITUATIONMAP_EXPORT GraphicalObjectCube : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
explicit GraphicalObjectCube(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
~GraphicalObjectCube();
double height() const
{
return m_height.value();
}
void setHeight(double height)
{
m_height = height;
}
const QString &extrusionColorName() const
{
return m_extrusionColorName.value();
}
void setExtrusionColorName(const QString &extrusionColorName)
{
m_extrusionColorName = extrusionColorName;
}
protected:
// Internal/Admin
bool create() override;
bool changed() override
{
return BaseClass::changed()
|| m_height.changed()
|| m_extrusionColorName.changed();
}
void resetChanged() override
{
BaseClass::resetChanged();
m_height.reset();
m_extrusionColorName.reset();
}
bool updateViewImpl() override;
// Data
SimpleValue<double> m_height;
SimpleValue<QString> m_extrusionColorName;
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
SITUATIONMAP_EXPORT std::ostream &operator<< (std::ostream &stream,
const GraphicalObjectCube &value);
} // namespace SituationMap
@@ -0,0 +1,344 @@
/**
* @RDE_HEADER@
* GraphicalObjectEllipse
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_ellipse.h"
#include <Map/situation_map_utils.h> // operator<<
// ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/Polygon.h>
#include <arcgis/GeometryEngine.h>
#include <arcgis/SimpleLineSymbol.h>
#include <arcgis/SimpleFillSymbol.h>
// Qt
#include <QElapsedTimer>
namespace SituationMap
{
GraphicalObjectEllipse::GraphicalObjectEllipse(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_logger(getLogger("GraphicalObjectEllipse"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectEllipse::~GraphicalObjectEllipse()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
delete m_internalParameters;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
bool GraphicalObjectEllipse::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
bool success = BaseClass::create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
// real work:
// empty
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
// << " drawOrder = " << m_graphic->drawOrder()
<< " success = " << success;
return success;
}
bool GraphicalObjectEllipse::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
// some checks:
if (!this->isSinglePoint())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
if (!create())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
if (!BaseClass::updateViewImpl())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
if (!m_ellipseParameters.value().isValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " ellipseParameters not valid";
return false;
}
// real work:
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " update geometry";
bool successEllipse = true;
if (m_ellipseParameters.changed() || m_positions.changed())
{
// apply attributes:
parameterToInternal();
Esri::ArcGISRuntime::Polygon ellipse;
successEllipse = createEllipse(ellipse);
if (successEllipse)
m_graphic->setGeometry(ellipse);
else
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " createEllipse failed";
return false;
}
}
assignFillStyleToGraphic();
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'";
bool successGraphic = addUpdateGraphic();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " drawOrder = " << m_graphic->zIndex()
<< " successEllipse = " << successEllipse
<< " successGraphic = " << successGraphic;
return successEllipse && successGraphic;
}
void GraphicalObjectEllipse::parameterToInternal()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
if (!m_internalParameters)
{
m_internalParameters = new Esri::ArcGISRuntime::GeodesicEllipseParameters();
m_internalParameters->setGeometryType(Esri::ArcGISRuntime::GeometryType::Polygon);
}
// TODO !!??: set xy correct, calc maxSegLength...
const EllipseParameters &ellipseParameterRef = m_ellipseParameters.value();
// 1a, 1b
m_internalParameters->setMaxSegmentLength(ellipseParameterRef.maxSegmentLength());
m_internalParameters->setMaxPointCount(ellipseParameterRef.maxPointCount());
// 2
const Point3D &origin = this->origin();
Esri::ArcGISRuntime::Point mapPoint(origin.x(), origin.y(), origin.z(), spatialReference()); // center of ellipse
Esri::ArcGISRuntime::Point normalizedPt =
Esri::ArcGISRuntime::GeometryEngine::normalizeCentralMeridian(mapPoint);
m_internalParameters->setCenter(normalizedPt);
// m_internalParameters->setCenter(mapPoint);
// 3, 4
m_internalParameters->setSemiAxis1Length(ellipseParameterRef.lengthX());
m_internalParameters->setSemiAxis2Length(ellipseParameterRef.lengthY());
// 5
m_internalParameters->setAxisDirection(ellipseParameterRef.directionDegree());
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
bool GraphicalObjectEllipse::createEllipse(Esri::ArcGISRuntime::Polygon &ellipse)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
bool success = createEllipsePolygon(*m_internalParameters, ellipse);
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success = " << success
// << " pointCount = " << ellipse.pointCount()
<< " dimension = " << ellipse.dimension();
return success;
}
bool GraphicalObjectEllipse::createEllipsePolygon(const Esri::ArcGISRuntime::GeodesicEllipseParameters &ellipseParameters,
Esri::ArcGISRuntime::Polygon &polygon)
{
LOGGER_T m_logger = getLogger("GraphicalObjectEllipse");
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
QElapsedTimer timer;
if (m_logger.isInfoEnabled())
timer.start();
bool success = false;
Esri::ArcGISRuntime::Geometry ellipseGeometry =
Esri::ArcGISRuntime::GeometryEngine::ellipseGeodesic(ellipseParameters);
if (ellipseGeometry.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " There was an error computing the geodesic ellipse. Try a different segment length.";
}
else
{
polygon = Esri::ArcGISRuntime::Polygon(ellipseGeometry);
success = true;
}
int count = AbstractGraphicalObject::countOfPolygonFirstPart(polygon);
DEBUG_LOG(m_logger) << LOGFUNCTION
<< " count=" << count
<< " (semiAxis1Length=" << ellipseParameters.semiAxis1Length()
<< " , semiAxis2Length=" << ellipseParameters.semiAxis2Length()
<< " , maxPointCount=" << ellipseParameters.maxPointCount()
<< " , maxSegmentLength=" << ellipseParameters.maxSegmentLength()
<< " , dimension = " << polygon.dimension()
<< ")";
if (m_logger.isDebugEnabled())
{
DEBUG_LOG(m_logger) << LOGFUNCTION
<< " took " << timer.elapsed() << "ms"
<< " (semiAxis1Length=" << ellipseParameters.semiAxis1Length()
<< " , semiAxis2Length=" << ellipseParameters.semiAxis2Length()
<< " , maxPointCount=" << ellipseParameters.maxPointCount()
<< " , maxSegmentLength=" << ellipseParameters.maxSegmentLength()
<< " , dimension = " << polygon.dimension()
<< ")";
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " success = " << success;
return success;
}
bool GraphicalObjectEllipse::createCirclePolygon(const Esri::ArcGISRuntime::Point &originMap,
float radius,
unsigned int maxPointCount,
Esri::ArcGISRuntime::Polygon &polygon)
{
LOGGER_T m_logger = getLogger("GraphicalObjectEllipse");
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " origin=" << originMap.x() << " ; " << originMap.y() << " ; " << originMap.z()
<< " radius=" << radius;
Esri::ArcGISRuntime::GeodesicEllipseParameters ePara;
ePara.setGeometryType(Esri::ArcGISRuntime::GeometryType::Polygon);
// 1
ePara.setMaxSegmentLength(10);
if (maxPointCount == 0)
ePara.setMaxPointCount(EllipseParameters::MAX_POINT_COUNT_DEFAULT);
else
ePara.setMaxPointCount(maxPointCount);
// 2
const Esri::ArcGISRuntime::Point &mapPoint = originMap; // center of ellipse
Esri::ArcGISRuntime::Point normalizedPt =
Esri::ArcGISRuntime::GeometryEngine::normalizeCentralMeridian(mapPoint);
ePara.setCenter(normalizedPt);
// 3, 4
ePara.setSemiAxis1Length(radius);
ePara.setSemiAxis2Length(radius);
// 5
ePara.setAxisDirection(0);
return createEllipsePolygon(ePara, polygon);
}
bool GraphicalObjectEllipse::createCirclePolygon(const Esri::ArcGISRuntime::Point &originMap,
float radius,
unsigned int maxPointCount,
PositionCont &posCont)
{
Esri::ArcGISRuntime::Polygon circle;
bool success = GraphicalObjectEllipse::createCirclePolygon(originMap,
radius,
maxPointCount,
circle);
if (success && !circle.isEmpty())
{
posCont.clear();
const Esri::ArcGISRuntime::ImmutablePart &immutablePart = circle.parts().part(0);
const int size = immutablePart.pointCount();
for (int i = 0; i < size; ++i)
{
const Esri::ArcGISRuntime::Point &p = immutablePart.point(i);
posCont.append(Point3D(p.x(), p.y(), p.z()));
}
}
return success;
}
EllipseParameters GraphicalObjectEllipse::ellipseParameters() const
{
return m_ellipseParameters.value();
}
EllipseParameters &GraphicalObjectEllipse::rEllipseParameters()
{
return m_ellipseParameters.refValue();
}
void GraphicalObjectEllipse::setEllipseParameters(const EllipseParameters &ellipseParameters)
{
m_ellipseParameters = ellipseParameters;
}
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<<(std::ostream &stream,
const GraphicalObjectEllipse &value)
{
stream << value.toString();
return stream;
}
} // namespace SituationMap
@@ -0,0 +1,121 @@
/**
* @RDE_HEADER@
* GraphicalObjectEllipse
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
#include "ellipse_parameters.h"
// Esri
// Qt
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Polygon;
class Point;
class GeodesicEllipseParameters;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT GraphicalObjectEllipse : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
GraphicalObjectEllipse(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
~GraphicalObjectEllipse();
EllipseParameters ellipseParameters() const;
EllipseParameters &rEllipseParameters();
void setEllipseParameters(const EllipseParameters &ellipseParameters);
static bool createEllipsePolygon(const Esri::ArcGISRuntime::GeodesicEllipseParameters &ellipseParameters,
Esri::ArcGISRuntime::Polygon &polygon);
static bool createCirclePolygon(const Esri::ArcGISRuntime::Point &originMap,
float radius,
unsigned int maxPointCount,
Esri::ArcGISRuntime::Polygon &polygon);
static bool createCirclePolygon(const Esri::ArcGISRuntime::Point &originMap,
float radius,
unsigned int maxPointCount,
PositionCont &posCont);
protected:
// Internal/Admin
bool create() override;
bool changed() override
{
return BaseClass::changed()
|| m_ellipseParameters.changed();
}
void resetChanged() override
{
BaseClass::resetChanged();
m_ellipseParameters.reset();
}
bool updateViewImpl() override;
// Data
Esri::ArcGISRuntime::GeodesicEllipseParameters *m_internalParameters = nullptr;
SimpleValue<EllipseParameters> m_ellipseParameters;
void parameterToInternal();
virtual bool createEllipse(Esri::ArcGISRuntime::Polygon &ellipse);
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
// Forbid some overloaded methods without effects of this object;
virtual PositionCont positions() const override
{
return PositionCont();
}
virtual void setPositions(const PositionCont &posCont) override
{
Q_UNUSED(posCont);
}
};
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<< (std::ostream &stream,
const GraphicalObjectEllipse &value);
} // namespace SituationMap
@@ -0,0 +1,170 @@
/**
* @RDE_HEADER@
* GraphicalObjectModelSceneMarker
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_model_scene_marker.h"
#include <Map/situation_map_utils.h> // operator<<
#include <MathUtils/simple_helper.h>
// ESRI
#include <arcgis/arcgisversion.h> // This file is created by RME, not ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/ModelSceneSymbol.h>
// Qt
#include <QPainter>
#include <QFile>
namespace SituationMap
{
GraphicalObjectModelSceneMarker::GraphicalObjectModelSceneMarker(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_logger(getLogger("GraphicalObjectModelSceneMarker"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectModelSceneMarker::~GraphicalObjectModelSceneMarker()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
delete m_modelSceneSymbol;
m_modelSceneSymbol = nullptr;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectModelSceneMarker::setUrl(const QUrl &url)
{
m_url = url;
}
QUrl GraphicalObjectModelSceneMarker::url() const
{
return m_url.value();
}
void GraphicalObjectModelSceneMarker::setHeading(float heading)
{
m_heading = heading;
}
float GraphicalObjectModelSceneMarker::heading() const
{
return m_heading.value();
}
bool GraphicalObjectModelSceneMarker::create()
{
return BaseClass::create();
}
bool GraphicalObjectModelSceneMarker::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " origin =" << this->origin() // .x() << " , " << this->origin().y()
<< " visibility=" << m_visibility
<< " pickable=" << m_pickable
<< " pickable.changed=" << m_pickable.changed()
<< " selected=" << m_selected
<< " m_url = " << m_url.value().toString()
<< " m_drawOrder = " << m_drawOrder
<< " m_scale = " << m_scale.value()
<< " m_heading = " << m_heading.value();
// some checks:
if (!isSinglePoint())
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " debugInfo='" << m_debugInfo << "'"
<< " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
QFile fi(m_url.value().path());
if (!fi.exists())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " m_url does not exists: '" << fi.fileName() << "'";
m_url.reset();
}
if (m_url.value().isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " debugInfo='" << m_debugInfo << "'"
<< " no url provided (or does not exist)";
return false;
}
bool success = create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
const Point3D &origin = this->origin();
Esri::ArcGISRuntime::Point originGeometry(origin.x(),
origin.y(),
origin.z(),
spatialReference());
m_graphic->setGeometry(originGeometry);
// url changed?
if ( (m_modelSceneSymbol && (m_modelSceneSymbol->url() != m_url.value())) ||
m_scale.changed())
{
// force re-create ModelSceneSymbol:
delete m_modelSceneSymbol;
m_modelSceneSymbol = nullptr;
}
if (!m_modelSceneSymbol)
{
m_modelSceneSymbol = new Esri::ArcGISRuntime::ModelSceneSymbol(m_url.value(), m_scale.value()); // url, scale
}
m_modelSceneSymbol->setHeading(m_heading.value()); // degree, clockwise, 0 is north/12
// m_modelSceneSymbol->setAnchorPosition(Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Bottom);
m_graphic->setSymbol(m_modelSceneSymbol);
success = BaseClass::updateViewImpl();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
success = addUpdateGraphic();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success=" << success
<< " m_drawOrder = " << m_drawOrder
<< " m_graphic->drawOrder = " << m_graphic->zIndex();
return success;
}
} // namespace SituationMap
@@ -0,0 +1,111 @@
/**
* @RDE_HEADER@
* GraphicalObjectModelSceneMarker
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
#include <Map/text_style.h>
#include <Common/situation_map_logging.h>
// Esri
#include <arcgis/SymbolTypes.h>
// Qt
#include <QImage>
#include <QUrl>
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Graphic;
class GraphicsOverlay;
class ModelSceneSymbol;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT GraphicalObjectModelSceneMarker : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
GraphicalObjectModelSceneMarker(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
~GraphicalObjectModelSceneMarker();
// Data access:
// to be cont.
void setUrl(const QUrl &url);
QUrl url() const;
// degree ; 0 is north/12
void setHeading(float heading);
float heading() const;
protected:
// Internal/Admin
bool create() override;
bool changed() override
{
return BaseClass::changed()
|| m_url.changed()
|| m_heading.changed()
;
}
void resetChanged() override
{
BaseClass::resetChanged();
m_url.reset();
m_heading.reset();
}
bool updateViewImpl() override;
// used ModelSceneSymbol:
Esri::ArcGISRuntime::ModelSceneSymbol *m_modelSceneSymbol{nullptr};
SimpleValue<QUrl> m_url; // e.g. /shd/CTC/TOOLS/ArcGIS/Runtime/Data/3D/Bristol/Collada/Bristol.dae
SimpleValue<float> m_heading{0.0}; // degree, clockwise, 0 is north/12
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
// Forbid some overloaded methods without effects of this object;
PositionCont positions() const override
{
return PositionCont();
}
void setPositions(const PositionCont &posCont) override
{
Q_UNUSED(posCont);
Q_ASSERT(false);
}
};
} // namespace SituationMap
@@ -0,0 +1,323 @@
/**
* @RDE_HEADER@
* GraphicalObjectPath
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_multi_path.h"
#include <Map/situation_map_utils.h> // operator<<
// ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/PolygonBuilder.h>
#include <arcgis/PolylineBuilder.h>
#include <arcgis/SimpleLineSymbol.h>
#include <arcgis/SimpleFillSymbol.h>
#include <arcgis/GeoView.h>
#include <arcgis/GeometryEngine.h>
// Qt
namespace SituationMap
{
GraphicalObjectMultiPath::GraphicalObjectMultiPath(const GraphicalObjectPathType type
, const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, UniqueMapId uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_pathType(type)
, m_logger(getLogger("GraphicalObjectMultiPath"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " m_pathType=" << m_pathType;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " m_pathType=" << m_pathType;
}
GraphicalObjectMultiPath::~GraphicalObjectMultiPath()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " m_pathType=" << m_pathType;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " m_pathType=" << m_pathType;
}
PositionCont GraphicalObjectMultiPath::path(int pathIndex) const
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " pathIndex=" << pathIndex;
if (hasPosCont(pathIndex))
return getPosCont(pathIndex).value();
return {};
}
void GraphicalObjectMultiPath::setPath(int pathIndex, const PositionCont &positions)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " pathIndex=" << pathIndex
<< " positions=" << positions;
m_positionsCont[pathIndex] = positions;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectMultiPath::clearPath(int pathIndex)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " pathIndex=" << pathIndex;
if (pathIndex == -1)
{
m_positions.refValue().clear();
m_positionsCont.clear();
}
else
{
if (hasPosCont(pathIndex))
{
// Do not remove the whole entry to let changed() work
m_positionsCont[pathIndex].refValue().clear();
}
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectMultiPath::clearAllPaths()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
m_positions.refValue().clear();
m_positionsCont.clear();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectMultiPath::reduceTo(const QList<int> &remainingPathIndexes)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " remainingPathIndexes=" << remainingPathIndexes;
QList<int> keys = m_positionsCont.keys();
for (int key : qAsConst(keys))
{
if (!remainingPathIndexes.contains(key))
{
// Do not remove the whole entry to let chanded() work
m_positionsCont[key].refValue().clear();
}
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
QList<int> GraphicalObjectMultiPath::pathIndexInUse()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
QList<int> ret;
///// ret << 0; // pathIndex=0 -> m_positions
QMapIterator<int, SimpleValue<PositionCont> > mapIt(m_positionsCont);
while (mapIt.hasNext())
{
mapIt.next();
const SimpleValue<PositionCont> &posData = mapIt.value();
const PositionCont &posCont = posData.value();
if (! posCont.isEmpty())
ret += mapIt.key();
} // while
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
return ret;
}
bool GraphicalObjectMultiPath::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
bool success = BaseClass::create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
return success;
}
bool GraphicalObjectMultiPath::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
bool success = create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
success = BaseClass::updateViewImpl();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
success = updatePositions();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success = " << success
<< " drawOrder = " << m_graphic->zIndex();
return success;
}
bool GraphicalObjectMultiPath::updatePositions()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
bool success = true;
int pathIdxToInsert = 0;
const int minPoints = this->pathType() == GraphicalObjectPathType::Polyline ? 2 : 3;
QSharedPointer<Esri::ArcGISRuntime::MultipartBuilder> builder;
// some preparations: ( do no check isMultiPoint / isArea: m_positions should typically be empty!!!
switch (m_pathType)
{
case GraphicalObjectPathType::Polyline:
builder = QSharedPointer<Esri::ArcGISRuntime::PolylineBuilder>(new Esri::ArcGISRuntime::PolylineBuilder(spatialReference()));
break;
case GraphicalObjectPathType::Polygon:
builder = QSharedPointer<Esri::ArcGISRuntime::PolygonBuilder>(new Esri::ArcGISRuntime::PolygonBuilder(spatialReference()));
break;
} // switch
QSharedPointer<Esri::ArcGISRuntime::PartCollection> partCollection =
QSharedPointer<Esri::ArcGISRuntime::PartCollection>(new Esri::ArcGISRuntime::PartCollection(builder->spatialReference()));
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< ", m_positionsCont.size() /#rings=" << m_positionsCont.size();
QMutableMapIterator<int, SimpleValue<PositionCont> > mapIt(m_positionsCont);
//
// LOOP PER SINGLE POLY
//
while (mapIt.hasNext())
{
mapIt.next();
const int key = mapIt.key();
const SimpleValue<PositionCont> &posData = mapIt.value();
const PositionCont &posCont = posData.value();
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< ", key / #ring=" << key
<< ", rings.size()=" << posCont.size();
// maybe some pathIdx are cleared by clearPath() or similar:
if (posCont.isEmpty())
{
DEBUG_LOG(m_logger) << LOGFUNCTION
<< " posCont empty, cleared? -> remove/cleanup"
<< " pathIdxToInsert = " << pathIdxToInsert
<< " mapIt.key() = " << key;
mapIt.remove();
continue;
}
if (posCont.count() < minPoints)
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " incorrect number of positions"
<< " (" << posCont.count() << ")"
<< ", minPoints=" << minPoints
<< ", pathIdxToInsert = " << pathIdxToInsert
<< ", mapIt.key() = " << key
<< ", m_debugInfo = " << m_debugInfo;
success = false;
}
else
{
Esri::ArcGISRuntime::Part part(builder->spatialReference());
//
// LOOP PER POINT
//
for (auto pos : qAsConst(posCont))
{
part.addPoint(pos.x(), pos.y(), pos.z());
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< ", key / #ring=" << key
<< ", addPoint=" << pos;
}
partCollection->addPart(&part);
} // if (posCont.count() < minPoints)
} // while
builder->setParts(partCollection.get());
//
// builder to geometry;
//
switch (m_pathType)
{
case GraphicalObjectPathType::Polyline:
{
const Esri::ArcGISRuntime::Polyline &multipartPolyline =
static_cast<Esri::ArcGISRuntime::PolylineBuilder *>(builder.data())->toPolyline();
m_graphic->setGeometry(multipartPolyline);
assignLineStyleToGraphic();
}
break;
case GraphicalObjectPathType::Polygon:
{
const Esri::ArcGISRuntime::Polygon &multipartPolygon =
static_cast<Esri::ArcGISRuntime::PolygonBuilder *>(builder.data())->toPolygon();
m_graphic->setGeometry(multipartPolygon);
//assignLineStyleToGraphic();
assignFillStyleToGraphic();
}
break;
} // switch
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'";
success = addUpdateGraphic();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success = " << success
<< " drawOrder = " << m_graphic->zIndex();
return success;
}
bool GraphicalObjectMultiPath::hasPosCont(int idx) const
{
return m_positionsCont.contains(idx);
}
SimpleValue<PositionCont> &GraphicalObjectMultiPath::getPosCont(int idx)
{
return m_positionsCont[idx];
}
const SimpleValue<PositionCont> GraphicalObjectMultiPath::getPosCont(int idx) const
{
return m_positionsCont[idx];
}
} // namespace SituationMap
@@ -0,0 +1,105 @@
/**
* @RDE_HEADER@
* GraphicalObjectPath
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "graphical_object_path.h"
// Esri
// Qt
#include <QMap>
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT GraphicalObjectMultiPath : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
GraphicalObjectMultiPath(const GraphicalObjectPathType type
, const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, UniqueMapId uniqueMapId);
~GraphicalObjectMultiPath();
GraphicalObjectPathType pathType() const
{
return m_pathType;
}
virtual PositionCont path(int pathIndex) const;
virtual void setPath(int pathIndex, const PositionCont &positions);
/*! \name Subsidiary polyline/polygons */
//@{
virtual void clearPath(int pathIndex);
virtual void clearAllPaths();
virtual QList<int> pathIndexInUse();
virtual void reduceTo(const QList<int> &remainingPathIndexes);
//@}
protected:
// Internal/Admin
bool updatePositions();
bool changed() override
{
bool baseClassChanged = BaseClass::changed();
if (baseClassChanged)
return true;
for (const SimpleValue<PositionCont> &posData : qAsConst(m_positionsCont))
{
if (posData.changed())
return true;
}
return false;
}
void resetChanged() override
{
BaseClass::resetChanged();
for (SimpleValue<PositionCont> &posData : m_positionsCont)
posData.reset();
}
bool create() override;
bool updateViewImpl() override;
// logical
const GraphicalObjectPathType m_pathType;
//bool updatePositions();
private:
// Attention: will contain 'positions' of idx == 1 and higher :
bool hasPosCont(int idx) const;
SimpleValue<PositionCont> &getPosCont(int idx);
const SimpleValue<PositionCont> getPosCont(int idx) const;
QMap<int, SimpleValue<PositionCont> > m_positionsCont;
// : Attention: will contain 'positions' of idx == 1 and higher
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
} // namespace SituationMap
@@ -0,0 +1,260 @@
/**
* @RDE_HEADER@
* GraphicalObjectPath
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_path.h"
#include <Map/situation_map_utils.h> // operator<<
// ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/PolygonBuilder.h>
#include <arcgis/PolylineBuilder.h>
#include <arcgis/SimpleLineSymbol.h>
#include <arcgis/SimpleFillSymbol.h>
#include <arcgis/GeoView.h>
#include <arcgis/GeometryEngine.h>
// Qt
namespace SituationMap
{
QLatin1String toString(const GraphicalObjectPathType subType)
{
switch (subType)
{
case GraphicalObjectPathType::Polyline:
return QLatin1String("Polyline");
break;
case GraphicalObjectPathType::Polygon:
return QLatin1String("Polygon");
break;
} // switch
return QLatin1String("GraphicalObjectPathTypeERROR");
}
GraphicalObjectPath::GraphicalObjectPath(const GraphicalObjectPathType type
, const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_pathType(type)
, m_logger(getLogger("GraphicalObjectPath"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " m_pathType=" << m_pathType;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " m_pathType=" << m_pathType;
}
GraphicalObjectPath::~GraphicalObjectPath()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " m_pathType=" << m_pathType;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " m_pathType=" << m_pathType;
}
bool GraphicalObjectPath::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
bool success = BaseClass::create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success = " << success
// << " drawOrder = " << m_graphic->drawOrder()
;
return success;
}
bool GraphicalObjectPath::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " m_positions= " << m_positions.value()
<< ", m_maxSegmentLength=" << m_maxSegmentLength;
QSharedPointer<Esri::ArcGISRuntime::MultipartBuilder> builder;
// some checks:
switch (m_pathType)
{
case GraphicalObjectPathType::Polyline:
if (!this->isMultiPoint())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
builder = QSharedPointer<Esri::ArcGISRuntime::PolylineBuilder>(new Esri::ArcGISRuntime::PolylineBuilder(spatialReference()));
break;
case GraphicalObjectPathType::Polygon:
if (!this->isArea())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
builder = QSharedPointer<Esri::ArcGISRuntime::PolygonBuilder>(new Esri::ArcGISRuntime::PolygonBuilder(spatialReference()));
break;
} // switch
if (! builder)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " no builder";
return false;
}
bool success = create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
success = BaseClass::updateViewImpl();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
// real work:
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " update geometry";
const PositionCont &posCont = m_positions.value();
for (const Point3D &pos : posCont)
builder->addPoint(pos.x(), pos.y(), pos.z());
if (! builder->isSketchValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " builder -> sketch invalid";
return false;
}
//
// apply attributes:
//
// N. Thoden, 13.02.2018
// Case #02062713 [BOS] BasicSceneView_lineNotComplete
// If a line has a line of ~10km (Borderline)
// the line may 'move' on the ground (3D, RenderMode::Dynamic, SurfarcePlacement::Draped)
// To avoid these kind of effect, the advice is to densify the geometry
// [similar: Case #02054233 [BOS] LineArtefacts in 3D ->
// BUG-000111565 : Lines created on the GraphicsOverlay with "Dynamic" Rendering mode on 3D doesn't render completely when zooming in]
//
// N. Thoden, 29.03.2018
// Consulting with Luke Smallwood / using the V100.2.1. debug libs:
// densify leads to an assert, so avoid densify
// (until now densify does not lead to an improvement..)
//
// N. Thoden, 19.04.2021
// [GUEZE-495]: 3D Lagedarstellung Trefferlinien
// Linie fehlt teilweise
// jetzt (typeweise) per m_maxSegmentLength aktivierbar
switch (m_pathType)
{
case GraphicalObjectPathType::Polyline:
{
const Esri::ArcGISRuntime::Polyline &polyline =
static_cast<Esri::ArcGISRuntime::PolylineBuilder *>(builder.data())->toPolyline();
if (m_maxSegmentLength > 0)
{
m_graphic->setGeometry(Esri::ArcGISRuntime::GeometryEngine::densifyGeodetic(polyline,
m_maxSegmentLength,
Esri::ArcGISRuntime::LinearUnit::meters(),
Esri::ArcGISRuntime::GeodeticCurveType::Geodesic));
} else {
m_graphic->setGeometry(polyline);
} // if (densify)
assignLineStyleToGraphic();
}
break;
case GraphicalObjectPathType::Polygon:
{
const Esri::ArcGISRuntime::Polygon &polygon =
static_cast<Esri::ArcGISRuntime::PolygonBuilder *>(builder.data())->toPolygon();
if (m_maxSegmentLength > 0)
{
m_graphic->setGeometry(Esri::ArcGISRuntime::GeometryEngine::densifyGeodetic(polygon,
m_maxSegmentLength,
Esri::ArcGISRuntime::LinearUnit::meters(),
Esri::ArcGISRuntime::GeodeticCurveType::Geodesic));
} else {
m_graphic->setGeometry(polygon);
}
assignFillStyleToGraphic();
}
break;
} // switch
const int count = AbstractGraphicalObject::countOfPolygonFirstPart(m_graphic->geometry());
if (count > 1000)
WARNING_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< ", huge amount of vertices, countOfPolygonFirstPart = " << count;
else
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< ", amount of vertices, countOfPolygonFirstPart = " << count;
success = addUpdateGraphic();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success = " << success
<< " drawOrder = " << m_graphic->zIndex();
return success;
}
double GraphicalObjectPath::maxSegmentLength() const
{
return m_maxSegmentLength;
}
void GraphicalObjectPath::setMaxSegmentLength(double maxSegmentLength)
{
m_maxSegmentLength = maxSegmentLength;
}
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<<(std::ostream &stream,
const GraphicalObjectPathType &value)
{
stream << toString(value).latin1();
return stream;
}
} // namespace SituationMap
@@ -0,0 +1,78 @@
/**
* @RDE_HEADER@
* GraphicalObjectPath
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
// Esri
// Qt
// forward:
//----------------------------------------------------------------------------
namespace SituationMap
{
enum class GraphicalObjectPathType {Polyline, Polygon};
QLatin1String toString(const GraphicalObjectPathType subType);
class SITUATIONMAP_EXPORT GraphicalObjectPath : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
explicit GraphicalObjectPath(const GraphicalObjectPathType type
, const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
~GraphicalObjectPath();
GraphicalObjectPathType pathType() const
{
return m_pathType;
}
double maxSegmentLength() const;
void setMaxSegmentLength(double maxSegmentLength);
protected:
// Internal/Admin
bool create() override;
bool updateViewImpl() override;
// logical
const GraphicalObjectPathType m_pathType;
double m_maxSegmentLength{-1};
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
SITUATIONMAP_EXPORT std::ostream &operator<< (std::ostream &stream,
const GraphicalObjectPathType &value);
} // namespace SituationMap
@@ -0,0 +1,573 @@
/**
* @RDE_HEADER@
* GraphicalObjectPictureMarker
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_picture_marker.h"
#include <Map/situation_map_utils.h> // operator<<
#include <SymbologyImpl/symbol_extension_renderer.h>
#include <MathUtils/simple_helper.h>
// ESRI
#include <arcgis/arcgisversion.h> // This file is created by RME, not ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/PictureMarkerSymbol.h>
#include <arcgis/SimpleMarkerSymbol.h>
#include <arcgis/MultipointBuilder.h>
// Qt
#include <QPainter>
#include <QTimer>
namespace SituationMap
{
/**
* @brief getSimpleMarkerSymbolStyle
* @param markerSymbolType type to get
* @param filePath filePath of requested marker; if empty -> use return value, if !empty -> use filePath and get file out of qrc
* @param hotSpotPercent -0.5 to 0.5; e.g. x=center, y=bottom -> -0.5
* @return corrosponding Esri::ArcGISRuntime::SimpleMarkerSymbolStyle, see filePath
*/
Esri::ArcGISRuntime::SimpleMarkerSymbolStyle getSimpleMarkerSymbolStyle(MarkerSymbolType markerSymbolType,
QString &filePath,
QPointF *hotSpotPercent = nullptr)
{
if (hotSpotPercent)
*hotSpotPercent = QPointF();
filePath.clear();
Esri::ArcGISRuntime::SimpleMarkerSymbolStyle esriSMSS =
Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle;
switch (markerSymbolType)
{
case SituationMap::MarkerSymbolType::Undefined:
esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle;
break;
case SituationMap::MarkerSymbolType::Circle:
esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle;
break;
case SituationMap::MarkerSymbolType::Cross:
esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Cross;
break;
case SituationMap::MarkerSymbolType::Diamond:
esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Diamond;
break;
case SituationMap::MarkerSymbolType::Square:
esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Square;
break;
case SituationMap::MarkerSymbolType::X:
esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::X;
break;
case SituationMap::MarkerSymbolType::Triangle:
esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Triangle;
break;
case SituationMap::MarkerSymbolType::PushPin2D:
esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle;
filePath = QLatin1String(":/situationMap/pushPinRedClipArt.png");
if (hotSpotPercent)
*hotSpotPercent = QPointF(0, -0.5);
break;
} //switch
return esriSMSS;
}
GraphicalObjectPictureMarker::GraphicalObjectPictureMarker(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_logger(getLogger("GraphicalObjectPictureMarker"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectPictureMarker::~GraphicalObjectPictureMarker()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
resetPictureMarkerSymbol();
resetImage();
resetSimpleMarkerSymbol();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectPictureMarker::setImage(const QImage &image, const QSize &defaultSize)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " visibility=" << m_visibility
<< " image.size()=" << image.size()
<< " defaultSize=" << defaultSize
<< " m_imageDefaultSize=" << m_imageDefaultSize
<< " m_imageChanged=" << m_imageChanged
<< " m_imageDefaultSize.changed()=" << m_imageDefaultSize.changed();
resetSimpleMarkerSymbol();
if (m_image != image)
{
m_image = image;
m_imageChanged = true;
}
// store defautlSize (member shouldn´t be modified)
m_imageDefaultSize = defaultSize;
if (m_imageDefaultSize.changed())
m_imageChanged = true;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " visibility=" << m_visibility
<< " image.size()=" << image.size()
<< " defaultSize=" << defaultSize
<< " m_imageDefaultSize=" << m_imageDefaultSize
<< " m_imageChanged=" << m_imageChanged
<< " m_imageDefaultSize.changed()=" << m_imageDefaultSize.changed();
}
void GraphicalObjectPictureMarker::setPictureMarkerSymbol(Esri::ArcGISRuntime::PictureMarkerSymbol *pms, QSize defaultSize)
{
resetSimpleMarkerSymbol();
resetImage();
if (m_isOwnPms)
{
delete m_pictureMarkerSymbol;
m_pictureMarkerSymbol = nullptr;
}
if (m_pictureMarkerSymbol == pms &&
m_imageDefaultSize.value() == defaultSize)
return;
m_isOwnPms = false;
m_pictureMarkerSymbol = pms;
m_imageDefaultSize = defaultSize;
m_imageChanged = true;
}
void GraphicalObjectPictureMarker::resetImage()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
m_image = QImage();
m_imageChanged = true;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectPictureMarker::resetPictureMarkerSymbol()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
if (m_isOwnPms)
delete m_pictureMarkerSymbol;
m_pictureMarkerSymbol = nullptr;
if (m_workaorundTimer)
m_workaorundTimer->stop();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectPictureMarker::resetSimpleMarkerSymbol()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
delete m_simpleMarkerSymbol;
m_simpleMarkerSymbol = nullptr;
m_markerSymbolType = MarkerSymbolType::Undefined;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
MarkerSymbolType GraphicalObjectPictureMarker::markerSymbolType()
{
return m_markerSymbolType.value();
}
int GraphicalObjectPictureMarker::markerSymbolSize()
{
return m_markerSize.value();
}
void GraphicalObjectPictureMarker::setMarkerSymbol(MarkerSymbolType markerSymbolType,
int size, const QColor &color)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " color=" << color;
resetImage();
m_markerSymbolType = markerSymbolType;
m_markerSize = size;
m_markerColor = color;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
QSize GraphicalObjectPictureMarker::currentSize() const
{
return m_currentSize;
}
bool GraphicalObjectPictureMarker::create()
{
return BaseClass::create();
}
int GraphicalObjectPictureMarker::xOffset() const
{
return m_externalOffset.value().x();
}
void GraphicalObjectPictureMarker::setXOffset(int xOffset)
{
m_externalOffset.refValue().rx() = xOffset;
}
int GraphicalObjectPictureMarker::yOffset() const
{
return m_externalOffset.value().y();
}
void GraphicalObjectPictureMarker::setYOffset(int yOffset)
{
m_externalOffset.refValue().ry() = yOffset;
}
float GraphicalObjectPictureMarker::rotationAngle()
{
return m_angle.value();
}
void GraphicalObjectPictureMarker::setRotationAngle(float angle)
{
m_angle = angle;
}
bool GraphicalObjectPictureMarker::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " origin =" << this->origin() // .x() << " , " << this->origin().y()
<< " visibility=" << m_visibility
<< " pickable=" << m_pickable
<< " pickable.changed=" << m_pickable.changed()
<< " selected=" << m_selected
<< " m_angle = " << m_angle.value()
<< " m_drawOrder = " << m_drawOrder
<< " m_image.size() = " << m_image.size()
<< " m_imageChanged = " << m_imageChanged
<< " m_imageDefaultSize=" << m_imageDefaultSize
<< " m_imageDefaultSize.changed()=" << m_imageDefaultSize.changed()
<< " m_scale = " << m_scale.value();
// some checks:
if (m_positions.value().size() < 1)
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " debugInfo='" << m_debugInfo << "'"
<< " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
//
// Image or Marker ???
//
QString filePath;
QPointF hotSpotPercent;
Esri::ArcGISRuntime::SimpleMarkerSymbolStyle esriSMSS =
getSimpleMarkerSymbolStyle(m_markerSymbolType.value(),
filePath,
&hotSpotPercent);
m_markerHotSpotPercent = hotSpotPercent;
if (m_markerSymbolType.value() != MarkerSymbolType::Undefined)
{
if (m_markerSize.changed() ||
m_markerSymbolType.changed() ||
m_markerColor.changed() ||
m_markerSymbolType.changed() ||
m_angle.changed() ||
m_markerHotSpotPercent.changed())
{
if (!reCreateSymbol(filePath, esriSMSS))
return false;
}
}
else
{
if (m_image.isNull() && (!m_isOwnPms && m_pictureMarkerSymbol == nullptr))
{
// in context of class 'TacticalAppearance' the PictureMarker exist always...
DEBUG_LOG(m_logger) << LOGFUNCTION << " no image and m_markerSymbolType==Undefined -> nothing to do";
// -> nothing to do:
return true; // -> nothing to do:
}
if (m_scale.changed() ||
m_imageChanged ||
m_markerSymbolType.changed() ||
m_markerSize.changed() ||
m_angle.changed() ||
!m_pictureMarkerSymbol)
{
if (!reCreateSymbol(filePath, esriSMSS))
return false;
}
}
const PositionCont &posCont = m_positions.value();
if (posCont.size() == 1)
{
const Point3D &origin = this->origin();
Esri::ArcGISRuntime::Point originGeometry(origin.x(),
origin.y(),
origin.z(),
spatialReference());
m_graphic->setGeometry(originGeometry);
}
else
{
// Eception: Layer with name "AES2" should NOT have no renderer 8 to enable multipoint graphics
// [GUEZE-496] RPL Nr 770: 3D Lagedarstellung Einfrieren der SW Appl.
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " posCont.size() = " << posCont.size();
Esri::ArcGISRuntime::MultipointBuilder builder(spatialReference());
for (const Point3D &pos : posCont)
builder.points()->addPoint(pos.x(), pos.y(), pos.z());
if (! builder.isSketchValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " builder -> sketch invalid";
return false;
}
m_graphic->setGeometry(builder.toGeometry());
}
bool success = BaseClass::updateViewImpl();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
success = addUpdateGraphic();
QSize imgSize = m_image.size();
QSizeF pmSize;
if (m_pictureMarkerSymbol)
pmSize = QSizeF(m_pictureMarkerSymbol->width(), m_pictureMarkerSymbol->height());
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
// << " success=" << success
<< " m_drawOrder = " << m_drawOrder
<< " m_graphic->drawOrder = " << m_graphic->zIndex()
<< " m_image->size() = " << imgSize
<< " pictureMarkerSymbol->size() = " << pmSize;
return success;
}
bool GraphicalObjectPictureMarker::reCreateSymbol(const QString &filePath,
const Esri::ArcGISRuntime::SimpleMarkerSymbolStyle esriSMSS)
{
const bool imageRequired = !m_image.isNull() || !filePath.isEmpty();
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " filePath = '" << filePath << "'"
<< " m_image->size() = " << m_image.size()
<< " m_imageChanged = " << m_imageChanged
<< " m_angle = " << m_angle.value()
<< " m_currentSize = " << m_currentSize
<< " m_imageDefaultSize = " << m_imageDefaultSize.value()
<< " m_imageDefaultSize.changed() = " << m_imageDefaultSize.changed();
//
// Now we know what to use: Image or Marker
//
if (!create())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
// what is used currently?
// image or m_simpleMarkerSymbol(esri)?
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " imageRequired=" << imageRequired
<< " m_pictureMarkerSymbol=" << m_pictureMarkerSymbol
<< " m_simpleMarkerSymbol=" << m_simpleMarkerSymbol;
if (!filePath.isEmpty())
{
// e.g. case PushPin2D
QImage img;
if (img.load(filePath))
{
int iSize = qRound(m_markerSize.value() * m_scale.value());
m_currentSize = QSize(iSize, iSize);
img = img.scaledToHeight(iSize, Qt::SmoothTransformation);
QColor localColor = m_markerColor.value();
if (m_markerSymbolType.value() == MarkerSymbolType::PushPin2D && localColor.isValid())
{
QColor colorToBeReplaced(255, 0, 0);
AdvancedSymbology::ExtensionRenderer::replaceColor(img, colorToBeReplaced.rgba(), localColor.rgba());
}
setImage(img);
}
else
{
ERROR_LOG(m_logger) << LOGFUNCTION << " loading image failed, debugInfo='" << m_debugInfo << "'"
<< " path='" << filePath << "'";
return false;
}
}
//
// Transfer data to esriSymbols:
//
// Image:
if (!m_image.isNull() || (!m_isOwnPms && m_pictureMarkerSymbol))
{
resetSimpleMarkerSymbol();
QSize realImageSize;
if (!m_isOwnPms && m_pictureMarkerSymbol)
{
if (m_imageDefaultSize.value().isEmpty())
m_currentSize = m_image.size() * m_scale.value();
else
m_currentSize = m_imageDefaultSize.value() * m_scale.value();
realImageSize = m_currentSize;
}
else
{
// scale localImage by m_scale
QImage localImage;
if (!filePath.isEmpty())
{
// case PushPin2D -> already scaled by m_scale. So don´t scale again:
localImage = m_image;
m_currentSize = localImage.size();
}
else
{
if (m_imageDefaultSize.value().isEmpty())
m_currentSize = m_image.size() * m_scale.value();
else
m_currentSize = m_imageDefaultSize.value() * m_scale.value();
// scaled required?
if (m_image.size() != m_currentSize)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " SCALE FROM " << m_image.size() << " TO " << m_currentSize;
localImage = m_image.scaled(m_currentSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
++m_scaleCounter4Moduletest;
} else {
localImage = m_image;
m_currentSize = localImage.size();
}
}
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_scaleCounter4Moduletest = " << m_scaleCounter4Moduletest
<< ", localImage.size() = " << localImage.size();
delete m_pictureMarkerSymbol;
m_pictureMarkerSymbol = new Esri::ArcGISRuntime::PictureMarkerSymbol(localImage);
realImageSize = localImage.size();
// Workaround :
// Norbert Thoden, 11.06.2020:
// do some calls again to fix ESRI-Bug:
// Bug: new Image('localImage#) / pictureMarkerSymbol, but sometimes it will not be shown :-(
// Using try and error, we recognize that an additional call seems to fix that issue:
if (!m_workaorundTimer)
{
m_workaorundTimer = new QTimer(m_graphic);
m_workaorundTimer->setInterval(150);
m_workaorundTimer->setSingleShot(true);
QObject::connect(m_workaorundTimer, &QTimer::timeout, m_graphic, [this]() {
if (!m_graphic)
return;
m_graphic->setZIndex(m_drawOrder.value());
m_graphic->setVisible(m_visibility.value());
m_graphic->setSelected(m_selected.value());
});
}
m_workaorundTimer->start();
// : Workaround
}
m_imageChanged = false;
// set image to pms AND ALSO the Heigth and Width!!!
// // !!! Important !!!
// // Case #01713501 - PictureMarker-offset
// // Esri::PictureMarkerSymbol:: setHeight/setWidth -> typographic points.
// // 36pt -> 48pixel -> factor 0.75
// double pixelToPointFactor = 0.71;
// m_pictureMarkerSymbol->setHeight(m_image->size().height() * pixelToPointFactor);
// m_pictureMarkerSymbol->setWidth (m_image->size().width() * pixelToPointFactor);
// // !!! Important !!!
QPointF markerHotSpot = QPointF(realImageSize.width() / 2.0 * m_markerHotSpotPercent.value().x(),
realImageSize.height() / 2.0 * m_markerHotSpotPercent.value().y());
QPointF finalOffset = (markerHotSpot.toPoint() * -1) * 1.33 // 1.33; // try and error (48pixel / 36pt)
+
(m_externalOffset.value() * 0.75); // 0.75; // try and error (36pt / 48pixel)
m_pictureMarkerSymbol->setOffsetX(finalOffset.x());
m_pictureMarkerSymbol->setOffsetY(finalOffset.y());
m_pictureMarkerSymbol->setAngle(m_angle.value());
m_graphic->setSymbol(m_pictureMarkerSymbol);
}
else
{
resetPictureMarkerSymbol();
resetImage();
if (!m_simpleMarkerSymbol)
m_simpleMarkerSymbol = new Esri::ArcGISRuntime::SimpleMarkerSymbol();
m_simpleMarkerSymbol->setSize(m_markerSize.value() * m_scale.value());
m_simpleMarkerSymbol->setStyle(esriSMSS);
m_simpleMarkerSymbol->setAngle(m_angle.value());
m_simpleMarkerSymbol->setOffsetX(m_externalOffset.value().x());
m_simpleMarkerSymbol->setOffsetY(m_externalOffset.value().y());
m_simpleMarkerSymbol->setColor(m_markerColor.value());
m_graphic->setSymbol(m_simpleMarkerSymbol);
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " filePath = '" << filePath << "'"
<< " m_image->size() = " << m_image.size()
<< " m_imageChanged = " << m_imageChanged
<< " m_currentSize = " << m_currentSize
<< " m_imageDefaultSize = " << m_imageDefaultSize.value()
<< " m_imageDefaultSize.changed() = " << m_imageDefaultSize.changed();
return true;
}
} // namespace SituationMap
@@ -0,0 +1,153 @@
/**
* @RDE_HEADER@
* GraphicalObjectPictureMarker
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
#include <Map/text_style.h>
#include <Common/situation_map_logging.h>
// Esri
#include <arcgis/SymbolTypes.h>
#include <arcgis/DistanceCompositeSceneSymbol.h>
// Qt
#include <QImage>
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Graphic;
class GraphicsOverlay;
class PictureMarkerSymbol;
class SimpleMarkerSymbol;
}
}
class QTimer;
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT GraphicalObjectPictureMarker : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
GraphicalObjectPictureMarker(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
~GraphicalObjectPictureMarker();
// Data access:
const QImage &image() const { return m_image; }
QSize imageDefaultSize() const { return m_imageDefaultSize.value(); }
void setImage(const QImage &image, const QSize &defaultSize = QSize());
void setPictureMarkerSymbol(Esri::ArcGISRuntime::PictureMarkerSymbol *pms, QSize defaultSize);
virtual MarkerSymbolType markerSymbolType();
virtual int markerSymbolSize();
virtual void setMarkerSymbol(MarkerSymbolType markerSymbolType,
int size,
const QColor &color = QColor());
virtual QSize currentSize() const;
// Offset (x: to right ; y: to north)
virtual int xOffset() const;
virtual void setXOffset(int xOffset);
virtual int yOffset() const;
virtual void setYOffset(int yOffset);
// rotation / angle
virtual float rotationAngle();
virtual void setRotationAngle(float angle);
protected:
// Internal/Admin
bool create() override;
bool changed() override
{
return BaseClass::changed()
|| m_markerSymbolType.changed()
|| m_markerSize.changed()
|| m_markerColor.changed()
|| m_angle.changed()
|| m_markerHotSpotPercent.changed()
|| m_externalOffset.changed()
|| m_imageChanged
;
}
void resetChanged() override
{
BaseClass::resetChanged();
m_markerSymbolType.reset();
m_markerSize.reset();
m_markerColor.reset();
m_angle.reset();
m_markerHotSpotPercent.reset();
m_externalOffset.reset();
m_imageChanged = false;
m_imageDefaultSize.reset();
}
bool updateViewImpl() override;
void resetImage();
void resetPictureMarkerSymbol();
void resetSimpleMarkerSymbol();
bool reCreateSymbol(const QString &filePath,
const Esri::ArcGISRuntime::SimpleMarkerSymbolStyle esriSMSS);
// used for image:
QImage m_image;
SimpleValue<QSize> m_imageDefaultSize;
Esri::ArcGISRuntime::PictureMarkerSymbol *m_pictureMarkerSymbol = nullptr;
bool m_imageChanged = true;
bool m_isOwnPms = true;
// used marker:
Esri::ArcGISRuntime::SimpleMarkerSymbol *m_simpleMarkerSymbol = nullptr;
QTimer *m_workaorundTimer = nullptr; // for re-new of Esri::ArcGISRuntime::PictureMarkerSymbol
QSize m_currentSize;
int m_scaleCounter4Moduletest = 0;
SimpleValue<MarkerSymbolType> m_markerSymbolType = MarkerSymbolType::Undefined;
SimpleValue<int> m_markerSize = 10;
SimpleValue<QColor> m_markerColor = QColor(Qt::red);
SimpleValue<float> m_angle = 0; ///
SimpleValue<QPointF> m_markerHotSpotPercent; ///< Store marker-specific offset.
SimpleValue<QPoint> m_externalOffset; ///< Stores external offset.
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
} // namespace SituationMap
@@ -0,0 +1,163 @@
/**
* @RDE_HEADER@
* GraphicalObjectRange
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_range.h"
#include <Map/situation_map_utils.h> // operator<<
// ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/PolygonBuilder.h>
#include <arcgis/MultipartBuilder.h>
#include <arcgis/ImmutablePartCollection.h>
// Qt
namespace SituationMap
{
GraphicalObjectRange::GraphicalObjectRange(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_logger(getLogger("GraphicalObjectRange"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectRange::~GraphicalObjectRange()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
int GraphicalObjectRange::range() const
{
return m_range.value();
}
float GraphicalObjectRange::angle() const
{
return m_angleDegree.value();
}
void GraphicalObjectRange::setRange(int range, float angleDegree)
{
m_angleDegree = angleDegree;
m_range = range;
}
bool GraphicalObjectRange::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
EllipseParameters ellipsePara;
ellipsePara.setLengthX(m_range.value());
ellipsePara.setLengthY(m_range.value());
ellipsePara.setDirectionDegree(m_angleDegree.value()) ;
ellipsePara.setDirectionDegree((m_angleDegree.value() * -1) + 90);
ellipsePara.setMaxSegmentLength(10);
ellipsePara.setMaxPointCount(30); // limit to 30 to reduce the number of vertices. If to high, keep performance impact to 3D in mind!!!
this->setEllipseParameters(ellipsePara);
bool success = BaseClass::updateViewImpl();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " drawOrder = " << m_graphic->zIndex()
<< " success = " << success;
return success;
}
bool GraphicalObjectRange::createEllipse(Esri::ArcGISRuntime::Polygon &ellipse)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " range=" << m_range
<< " angle=" << m_angleDegree;
bool success = BaseClass::createEllipse(ellipse);
if (success &&
!ellipse.isEmpty() &&
!ellipse.parts().isEmpty() &&
!ellipse.parts().part(0).isEmpty())
{
const Point3D &center = this->origin();
// 'modify' ellipe (by copying, adding and re-assigning):
// part 0 - ellipse
// we have to copy the point since the ellipse does not contain the z value anymore :-(
Esri::ArcGISRuntime::Part partCircle(ellipse.spatialReference());
const Esri::ArcGISRuntime::ImmutablePart &im = ellipse.parts().part(0);
for (int i = 0; i < im.pointCount(); ++i)
{
const Esri::ArcGISRuntime::Point &p = im.point(i);
partCircle.addPoint(Esri::ArcGISRuntime::Point(p.x(),
p.y(),
center.z() // p.z()
));
}
// part 1 - line to center
Esri::ArcGISRuntime::Part partLine(ellipse.spatialReference());
partLine.addPoint(im.startPoint().x(),
im.startPoint().y(),
center.z());
partLine.addPoint(center.x(),
center.y(),
center.z());
// a polyGON requiree to have at least 3 points:
partLine.addPoint(center.x(),
center.y(),
center.z()+0.01);
// combine them to a PartCollection:
Esri::ArcGISRuntime::PartCollection pc(ellipse.spatialReference());
pc.addPart(&partCircle);
pc.addPart(&partLine);
Esri::ArcGISRuntime::PolygonBuilder builder(ellipse.spatialReference());
builder.setParts(&pc);
ellipse = builder.toPolygon();
success = true;
}
else
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'";
return success;
}
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<<(std::ostream &stream,
const GraphicalObjectRange &value)
{
stream << value.toString();
return stream;
}
} // namespace SituationMap
@@ -0,0 +1,87 @@
/**
* @RDE_HEADER@
* GraphicalObjectRange
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "graphical_object_ellipse.h"
// Esri
// Qt
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT GraphicalObjectRange : public GraphicalObjectEllipse
{
using BaseClass = GraphicalObjectEllipse;
public:
GraphicalObjectRange(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
~GraphicalObjectRange();
int range() const;
float angle() const;
void setRange(int range, float angleDegree);
protected:
// Internal/Admin
// bool create() override;
bool changed() override
{
return BaseClass::changed()
|| m_angleDegree.changed()
|| m_range.changed();
}
void resetChanged() override
{
BaseClass::resetChanged();
m_angleDegree.reset();
m_range.reset();
}
bool updateViewImpl() override;
bool createEllipse(Esri::ArcGISRuntime::Polygon &ellipse) override;
private:
SimpleValue<float> m_angleDegree = 0;
SimpleValue<int> m_range = 0;
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<< (std::ostream &stream,
const GraphicalObjectRange &value);
} // namespace SituationMap
@@ -0,0 +1,136 @@
/**
* @RDE_HEADER@
* GraphicalObjectRendererAttributes
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_renderer_attributes.h"
#include <Map/situation_map_utils.h> // operator<<
#include <MathUtils/simple_helper.h>
// ESRI
#include <arcgis/arcgisversion.h> // This file is created by RME, not ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/ModelSceneSymbol.h>
// Qt
#include <QPainter>
#include <QFile>
namespace SituationMap
{
GraphicalObjectRendererAttributes::GraphicalObjectRendererAttributes(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_logger(getLogger("GraphicalObjectRendererAttributes"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectRendererAttributes::~GraphicalObjectRendererAttributes()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectRendererAttributes::setAttributeMap(const QVariantMap &attribMap)
{
m_attributeMap = attribMap;
}
QVariantMap GraphicalObjectRendererAttributes::attributeMap() const
{
return m_attributeMap.value();
}
bool GraphicalObjectRendererAttributes::create()
{
return BaseClass::create();
}
bool GraphicalObjectRendererAttributes::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " origin =" << this->origin() // .x() << " , " << this->origin().y()
<< " visibility=" << m_visibility
<< " pickable=" << m_pickable
<< " pickable.changed=" << m_pickable.changed()
<< " selected=" << m_selected
<< " m_attributeMap = " << m_attributeMap.value().size()
<< " m_drawOrder = " << m_drawOrder;
// some checks:
if (!isSinglePoint())
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " debugInfo='" << m_debugInfo << "'"
<< " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
bool success = create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
const Point3D &origin = this->origin();
m_graphic->setGeometry(Esri::ArcGISRuntime::Point(origin.x(),
origin.y(),
origin.z(),
spatialReference()));
if (m_attributeMap.changed())
{
QVariantMap cur = m_graphic->attributes()->attributesMap();
if (m_logger.isDebugEnabled())
qCritical() << " debugInfo='" << m_debugInfo << "'" << ", QVariantMap before unite " << cur;
cur.unite(m_attributeMap.value());
if (m_logger.isDebugEnabled())
qCritical() << " debugInfo='" << m_debugInfo << "'" << ", QVariantMap after unite " << cur;
m_graphic->attributes()->setAttributesMap(cur);
}
// keep anchor like Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Bottom in mind.
// assigned during setup the Renderer (LayerFactory, AdapterDds...)
success = BaseClass::updateViewImpl();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
success = addUpdateGraphic();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success=" << success
<< " m_drawOrder = " << m_drawOrder
<< " m_graphic->drawOrder = " << m_graphic->zIndex();
return success;
}
} // namespace SituationMap
@@ -0,0 +1,102 @@
/**
* @RDE_HEADER@
* GraphicalObjectRendererAttributes
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
#include <Map/text_style.h>
#include <Common/situation_map_logging.h>
// Esri
#include <arcgis/SymbolTypes.h>
// Qt
#include <QImage>
#include <QVariantMap>
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Graphic;
class GraphicsOverlay;
class ModelSceneSymbol;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT GraphicalObjectRendererAttributes : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
GraphicalObjectRendererAttributes(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
~GraphicalObjectRendererAttributes();
// Data access:
// to be cont.
void setAttributeMap(const QVariantMap &attribMap);
QVariantMap attributeMap() const;
protected:
// Internal/Admin
bool create() override;
bool changed() override
{
return BaseClass::changed()
|| m_attributeMap.changed()
;
}
void resetChanged() override
{
BaseClass::resetChanged();
m_attributeMap.reset();
}
bool updateViewImpl() override;
// no ModelSceneSymbol since Renderer-based
SimpleValue<QVariantMap> m_attributeMap;
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
// Forbid some overloaded methods without effects of this object;
PositionCont positions() const override
{
return PositionCont();
}
void setPositions(const PositionCont &posCont) override
{
Q_UNUSED(posCont);
Q_ASSERT(false);
}
};
} // namespace SituationMap
@@ -0,0 +1,205 @@
/**
* @RDE_HEADER@
* GraphicalObjectSceneMarker
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_scene_marker.h"
#include <Map/situation_map_utils.h> // operator<<
#include <MathUtils/simple_helper.h>
#include <LtsCoordTrans/lts_coord_trans.h>
// ESRI
#include <arcgis/arcgisversion.h> // This file is created by RME, not ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/SimpleMarkerSceneSymbol.h>
// Qt
#include <QPainter>
#include <QTimer>
namespace SituationMap
{
GraphicalObjectSceneMarker::GraphicalObjectSceneMarker(SceneMarkerType type
, const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_type(type)
, m_logger(getLogger("GraphicalObjectSceneMarker"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectSceneMarker::~GraphicalObjectSceneMarker()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
delete m_simpleMarkerSceneSymbol;
m_simpleMarkerSceneSymbol = nullptr;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectSceneMarker::setDiameter(double diameter)
{
m_diameter = diameter;
}
double GraphicalObjectSceneMarker::diameter() const
{
return m_diameter.value();
}
bool GraphicalObjectSceneMarker::create()
{
return BaseClass::create();
}
bool GraphicalObjectSceneMarker::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " origin =" << this->origin() // .x() << " , " << this->origin().y()
<< " visibility=" << m_visibility
<< " pickable=" << m_pickable
<< " pickable.changed=" << m_pickable.changed()
<< " selected=" << m_selected
<< " m_diameter = " << m_diameter.value()
<< " m_heading = " << m_heading.value()
<< " m_pitch = " << m_pitch.value()
<< " m_roll = " << m_roll.value()
<< " m_drawOrder = " << m_drawOrder
<< " m_lineStyle = " << m_lineStyle.value()
<< " m_fillStyle = " << m_fillStyle.value()
<< " fillColor = " << m_fillStyle.value().fillColor()
<< " m_scale = " << m_scale.value();
if (m_graphicsLayer->renderingMode() != Esri::ArcGISRuntime::GraphicsRenderingMode::Dynamic)
for (int i=0; i<10; ++i)
ERROR_LOG(m_logger) << " RenderingMode::Dynamic required!!! , debugInfo='" << m_debugInfo << "'";
// some checks:
if (!isSinglePoint())
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " debugInfo='" << m_debugInfo << "'"
<< " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
bool success = create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
const double radiusHeight = m_diameter.value()/2.0;
Point3D origin = this->origin();
// Try to set AnchorPosition::Top, to let the cone rotate around top/tip.
// Using a cone, the anchor point does not work as expected, we have to move
if (m_type == SceneMarkerType::CONE)
{
//
// It´s a SCENE Marker. So we are per definition in 3D and origin is in degree.
//
// heading is in degree:m_heading.value()
// offset as vector:
QPointF offsetMeter(0, radiusHeight/2.0);
const int rotAngleDegree = (static_cast<int>(m_heading.value())-90)%360;
DEBUG_LOG(m_logger) << " offsetMeter = " << offsetMeter << ", m_heading = " << m_heading.value() << ", rotAngleDegree = " << rotAngleDegree;
MathUtils::rotatePoint(offsetMeter, QPointF(), rotAngleDegree);
// new temporary origin...
// -> rad
origin *= LtsCoordTrans::deg2rad();
LtsCoordTrans::UtmCoordinate newOrigin = LtsCoordTrans::UtmCoordinate::fromGeographic(origin.x(), origin.y());
DEBUG_LOG(m_logger) << " newOrigin = " << newOrigin.mgrs();
// add rotated offset to temporary origin:
const QPointF origin2D = newOrigin.geographic(offsetMeter) * LtsCoordTrans::rad2deg();
// -> deg
// write back; z-value stays untouched:
origin.rx() = origin2D.x();
origin.ry() = origin2D.y();
}
const Esri::ArcGISRuntime::Point originGeometry(origin.x(),
origin.y(),
origin.z(),
spatialReference());
m_graphic->setGeometry(originGeometry);
if (!m_simpleMarkerSceneSymbol)
{
switch (m_type)
{
case SceneMarkerType::CONE: // Kegel
m_simpleMarkerSceneSymbol =
Esri::ArcGISRuntime::SimpleMarkerSceneSymbol::cone(
m_fillStyle.value().fillColor(),
radiusHeight, radiusHeight, // V100.8: cone: 'diameter' is interpreted as radius
Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Center); // V100.8: cone: Top does not work as aspected - grrr
break;
case SceneMarkerType::SPHERE: // Kugel
m_simpleMarkerSceneSymbol =
Esri::ArcGISRuntime::SimpleMarkerSceneSymbol::sphere(
m_fillStyle.value().fillColor(),
radiusHeight*2.0,
Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Center);
break;
} // switch
}
m_simpleMarkerSceneSymbol->setColor(m_fillStyle.value().fillColor());
if (m_type == SceneMarkerType::CONE)
{
m_simpleMarkerSceneSymbol->setWidth(radiusHeight);
m_simpleMarkerSceneSymbol->setHeight(radiusHeight);
m_simpleMarkerSceneSymbol->setDepth(radiusHeight);
} else {
m_simpleMarkerSceneSymbol->setWidth(m_diameter.value());
m_simpleMarkerSceneSymbol->setHeight(m_diameter.value());
m_simpleMarkerSceneSymbol->setDepth(m_diameter.value());
}
m_simpleMarkerSceneSymbol->setHeading(m_heading.value());
m_simpleMarkerSceneSymbol->setPitch(m_pitch.value());
m_simpleMarkerSceneSymbol->setRoll(m_roll.value());
m_graphic->setSymbol(m_simpleMarkerSceneSymbol);
success = BaseClass::updateViewImpl();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
success = addUpdateGraphic();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success=" << success
<< " m_drawOrder = " << m_drawOrder
<< " m_graphic->drawOrder = " << m_graphic->zIndex();
return success;
}
} // namespace SituationMap
@@ -0,0 +1,137 @@
/**
* @RDE_HEADER@
* GraphicalObjectSceneMarker
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
#include <Map/text_style.h>
#include <Common/situation_map_logging.h>
// Esri
#include <arcgis/SymbolTypes.h>
// Qt
#include <QImage>
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Graphic;
class GraphicsOverlay;
class SimpleMarkerSceneSymbol;
}
}
class QTimer;
//----------------------------------------------------------------------------
namespace SituationMap
{
//
//
//
// !!! Layer with RenderingMode::Dynamic required !!!
// !!! Layer with RenderingMode::Dynamic required !!!
// !!! Layer with RenderingMode::Dynamic required !!!
//
//
//
enum class SceneMarkerType {CONE, SPHERE};
class SITUATIONMAP_EXPORT GraphicalObjectSceneMarker : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
GraphicalObjectSceneMarker(SceneMarkerType type
, const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
~GraphicalObjectSceneMarker();
// Data access:
// to be cont. (type is sphere, to support cube, cylinder etc)
void setDiameter(double diameter);
double diameter() const;
double heading() const { return m_heading.value(); }
void setHeading(double headingDegree) { m_heading = headingDegree; }
double pitch() const { return m_pitch.value(); }
void setPitch(double pitchDegree) { m_pitch = pitchDegree; }
double roll() const { return m_roll.value(); }
void setRoll(double rollDegree) { m_roll = rollDegree; }
protected:
// Internal/Admin
bool create() override;
bool changed() override
{
return BaseClass::changed()
|| m_diameter.changed()
|| m_heading.changed()
|| m_pitch.changed()
|| m_roll.changed()
;
}
void resetChanged() override
{
BaseClass::resetChanged();
m_diameter.reset();
m_heading.reset();
m_pitch.reset();
m_roll.reset();
}
bool updateViewImpl() override;
// used marker:
Esri::ArcGISRuntime::SimpleMarkerSceneSymbol *m_simpleMarkerSceneSymbol{nullptr};
SceneMarkerType m_type{SceneMarkerType::SPHERE};
SimpleValue<double> m_diameter{25};
SimpleValue<double> m_heading{0}; // degree
SimpleValue<double> m_pitch{0}; // degree
SimpleValue<double> m_roll{0}; // degree
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
// Forbid some overloaded methods without effects of this object;
PositionCont positions() const override
{
return PositionCont();
}
void setPositions(const PositionCont &posCont) override
{
Q_UNUSED(posCont);
Q_ASSERT(false);
}
};
} // namespace SituationMap
@@ -0,0 +1,340 @@
/**
* @RDE_HEADER@
* GraphicalObjectText
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_text.h"
#include <Map/situation_map_utils.h> // operator<<
// ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/TextSymbol.h>
#include <arcgis/PictureMarkerSymbol.h>
// Qt
#include <QString>
#include <iostream>
#include <algorithm>
namespace SituationMap
{
GraphicalObjectText::GraphicalObjectText(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_textSymbol(nullptr)
, m_logger(getLogger("GraphicalObjectText"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectText::~GraphicalObjectText()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " text='" << m_text << "'";
delete m_textSymbol;
m_textSymbol = nullptr;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " text='" << m_text << "'";
}
QString GraphicalObjectText::toString() const
{
QString str;
// str.append( BaseClass::toString() );
if (!m_debugInfo.isEmpty())
str.append(QStringLiteral("debugInfo=%1").arg(m_debugInfo));
if (m_graphicsLayer)
str.append(QStringLiteral(" layer='%1'").arg(m_graphicsLayer->objectName()));
if (m_graphic)
str.append(QStringLiteral(" zIndex='%1'").arg(m_graphic->zIndex()));
std::ostringstream stream;
stream << this->origin();
str.append(QLatin1String(stream.str().c_str()));
if (!m_text.value().isEmpty())
str.append(QStringLiteral("text=%1").arg(m_text.value()));
return str;
}
QString GraphicalObjectText::text() const
{
return m_text.value();
}
void GraphicalObjectText::setText(const QString &text)
{
m_text = text;
}
TextStyle GraphicalObjectText::textStyle() const
{
return m_textStyle.value();
}
TextStyle &GraphicalObjectText::rTextStyle()
{
return m_textStyle.refValue();
}
void GraphicalObjectText::setTextStyle(const TextStyle &textStyle)
{
m_textStyle = textStyle;
}
float GraphicalObjectText::xOffset() const
{
return m_offset.value().x();
}
void GraphicalObjectText::setXOffset(float xOffset)
{
QPointF temp = m_offset.value();
temp.rx() = xOffset;
m_offset = temp;
}
float GraphicalObjectText::yOffset() const
{
return m_offset.value().y();
}
void GraphicalObjectText::setYOffset(float yOffset)
{
QPointF temp = m_offset.value();
temp.ry() = yOffset;
m_offset = temp;
}
bool GraphicalObjectText::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " text='" << m_text << "'"
<< " origin =" << this->origin() // .x() << " , " << this->origin().y()
<< " visibility=" << m_visibility;
bool success = true;
// some checks:
if (!m_graphicsLayer)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " no graphicsLayer";
return false;
}
BaseClass::create();
if (!m_textSymbol)
m_textSymbol = new Esri::ArcGISRuntime::TextSymbol();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " text='" << m_text << "'"
<< " success=" << success
<< " drawOrder = " << m_graphic->zIndex();
return success;
}
bool GraphicalObjectText::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " text='" << m_text << "'"
<< " origin =" << this->origin() // .x() << " , " << this->origin().y()
<< " visibility=" << m_visibility
<< " m_textStyle=" << m_textStyle
<< " offset=" << m_offset;
// some checks:
if (!this->isSinglePoint())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
bool success = true;
if (m_visibility.value())
{
success = create(); // create only if visible
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
if (! BaseClass::updateViewImpl())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateViewImpl failed";
}
// FATAL_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
// << " text='" << m_text << "'"
// << " changed1=" << m_text.changed()
// << " changed2="<< m_textStyle.changed();
bool assignTextSymbol = false;
// || m_visibility.changed()) -> szenario: create invisible object and make it visible
if (m_text.changed() || m_visibility.changed())
{
m_textSymbol->setText(m_text.value());
assignTextSymbol = true;
}
// TextStyle to TextSymbol@ESRI :
if (m_textStyle.changed()
|| m_scale.changed()
|| m_visibility.changed() // -> szenario: create invisible object and make it visible
)
{
const TextStyle &textStyle = m_textStyle.value();
m_textSymbol->setColor(textStyle.textColor());
// High values can lead to coredump with 'SymbolDrawHelper::create_line_image FAILED, SkBitmap is NULL or empty' -> limit
m_textSymbol->setSize(std::min(500.0f, textStyle.size() * m_scale.value()));
m_textSymbol->setAngle(textStyle.angle());
m_textSymbol->setFontFamily(textStyle.fontFamily());
m_textSymbol->setFontWeight(textStyle.bold() ? Esri::ArcGISRuntime::FontWeight::Bold : Esri::ArcGISRuntime::FontWeight::Normal);
m_textSymbol->setFontStyle(textStyle.italic() ? Esri::ArcGISRuntime::FontStyle::Italic : Esri::ArcGISRuntime::FontStyle::Normal);
Esri::ArcGISRuntime::FontDecoration fd = Esri::ArcGISRuntime::FontDecoration::None;
if (textStyle.underline())
fd = Esri::ArcGISRuntime::FontDecoration::Underline;
if (textStyle.strikeThrough())
fd = Esri::ArcGISRuntime::FontDecoration::LineThrough;
m_textSymbol->setFontDecoration(fd);
float outlineWidth = 0;
if (textStyle.textOutlineWidth() > 0)
outlineWidth = textStyle.textOutlineWidth() * m_scale.value();
if (m_textSymbol->size() <= 15) // small size -> outline reduces readability :-(
outlineWidth = 0.0;
m_textSymbol->setHaloWidth(outlineWidth);
if (textStyle.textOutlineColor().isValid())
m_textSymbol->setHaloColor(textStyle.textOutlineColor());
// qWarning() << " haloWidth = " << m_textSymbol->haloWidth();
// m_textSymbol->setHaloColor(Qt::white);
// m_textSymbol->setHaloWidth(outlineWidth);
// qWarning() << " fontSize " << textStyle.size() * m_scale.value()
// <<" (textStyle.size() " << textStyle.size()
// << " m_scale.value() " << m_scale.value() << ")";
// qWarning() << " outlineWidth " << outlineWidth
// <<" (textStyle.textOutlineWidth() " << textStyle.textOutlineWidth()
// << " m_scale.value() " << m_scale.value() << ")";
// if (textStyle.backgroundColor().isValid())
m_textSymbol->setBackgroundColor(textStyle.backgroundColor());
// m_textSymbol->setAntiAliased(textStyle.antiAliased());
m_textSymbol->setHorizontalAlignment(convert(textStyle.horizontalAlignment()));
m_textSymbol->setVerticalAlignment(convert(textStyle.verticalAlignment()));
assignTextSymbol = true;
// : TextStyle to TextSymbol@ESRI
}
// || m_visibility.changed()) -> szenario: create invisible object and make it visible
if (m_offset.changed() || m_visibility.changed())
{
m_textSymbol->setOffsetX(m_offset.value().x());
m_textSymbol->setOffsetY(m_offset.value().y());
assignTextSymbol = true;
}
if (assignTextSymbol)
m_graphic->setSymbol(m_textSymbol);
// check ChangedFlags:
// Object is created and text is invisble due to scale (meterPerPixel)
// map is zoomed in -> text should arise, but on m_visibility is changed:
if (m_visibility.changed() || m_positions.changed())
{
const Point3D &origin = this->origin();
m_graphic->setGeometry(Esri::ArcGISRuntime::Point(origin.x(),
origin.y(),
origin.z(),
spatialReference()));
success = addUpdateGraphic();
} // m_visibility.changed() || m_positions.changed()
}
else // if visible
{
BaseClass::updateViewImpl();
success = addUpdateGraphic();
}
if (m_logger.isTraceEnabled())
{
int drawOrder = -1;
if (m_graphic)
drawOrder = m_graphic->zIndex();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " text='" << m_text << "'"
<< " success=" << success
<< " drawOrder = " << drawOrder;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
// << " fontFamily='" << m_textSymbol->fontFamily() << "'"
;
return success;
}
Esri::ArcGISRuntime::HorizontalAlignment GraphicalObjectText::convert(const HorizontalAlignment horizontalAlignment)
{
Esri::ArcGISRuntime::HorizontalAlignment esriAlignmenmt =
Esri::ArcGISRuntime::HorizontalAlignment::Left;
switch (horizontalAlignment)
{
case HorizontalAlignment::Left: esriAlignmenmt = Esri::ArcGISRuntime::HorizontalAlignment::Left; break;
case HorizontalAlignment::Center: esriAlignmenmt = Esri::ArcGISRuntime::HorizontalAlignment::Center; break;
case HorizontalAlignment::Right: esriAlignmenmt = Esri::ArcGISRuntime::HorizontalAlignment::Right; break;
case HorizontalAlignment::Justify: esriAlignmenmt = Esri::ArcGISRuntime::HorizontalAlignment::Justify; break;
} // switch
return esriAlignmenmt;
}
Esri::ArcGISRuntime::VerticalAlignment GraphicalObjectText::convert(const VerticalAlignment verticalAlignment)
{
Esri::ArcGISRuntime::VerticalAlignment esriAlignmenmt =
Esri::ArcGISRuntime::VerticalAlignment::Bottom;
switch (verticalAlignment)
{
case VerticalAlignment::Bottom: esriAlignmenmt = Esri::ArcGISRuntime::VerticalAlignment::Bottom; break;
case VerticalAlignment::Middle: esriAlignmenmt = Esri::ArcGISRuntime::VerticalAlignment::Middle; break;
case VerticalAlignment::Top: esriAlignmenmt = Esri::ArcGISRuntime::VerticalAlignment::Top; break;
case VerticalAlignment::BaseLine: esriAlignmenmt = Esri::ArcGISRuntime::VerticalAlignment::Baseline; break;
} // switch
return esriAlignmenmt;
}
} // namespace SituationMap
@@ -0,0 +1,125 @@
/**
* @RDE_HEADER@
* GraphicalObjectText
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
#include <Map/text_style.h>
#include <Map/situation_map_utils.h>
#include <Common/simple_value.h>
#include <Common/situation_map_logging.h>
// Esri
// Qt
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Graphic;
class GraphicsOverlay;
class PictureMarkerSymbol;
class TextSymbol;
enum class HorizontalAlignment;
enum class VerticalAlignment;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT GraphicalObjectText : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
GraphicalObjectText(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId
);
~GraphicalObjectText();
QString toString() const override; // specialized
// Data access:
QString text() const;
void setText(const QString &text);
TextStyle textStyle() const;
TextStyle &rTextStyle();
void setTextStyle(const TextStyle &textStyle);
// Offset
virtual float xOffset() const;
virtual void setXOffset(float xOffset);
virtual float yOffset() const;
virtual void setYOffset(float yOffset);
static Esri::ArcGISRuntime::HorizontalAlignment convert(const HorizontalAlignment horizontalAlignment);
static Esri::ArcGISRuntime::VerticalAlignment convert(const VerticalAlignment verticalAlignment);
protected:
// Internal/Admin
bool create() override;
bool changed() override
{
return BaseClass::changed()
|| m_text.changed()
|| m_textStyle.changed()
|| m_offset.changed();
}
void resetChanged() override
{
BaseClass::resetChanged();
m_text.reset();
m_textStyle.reset();
m_offset.reset();
}
bool updateViewImpl() override;
Esri::ArcGISRuntime::TextSymbol *m_textSymbol = nullptr;
SimpleValue<QString> m_text; ///< to be displayed on map
SimpleValue<TextStyle> m_textStyle;
SimpleValue<QPointF> m_offset; ///< Offset (pixel).
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
// Forbid some overloaded methods without effects of this object;
virtual PositionCont positions() const override
{
return PositionCont();
}
virtual void setPositions(const PositionCont &posCont) override
{
Q_UNUSED(posCont);
}
};
} // namespace SituationMap
@@ -0,0 +1,69 @@
/**
* @RDE_HEADER@
* StubGraphicalObject
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
#include <Map/situation_map_types.h>
// Esri
namespace SituationMap
{
/**
@class StubGraphicalObject stub_graphical_object.h
The class \c StubGraphicalObject is used as substitution for a graphical object. It does
nothing and the purpose is only to satisfy the interface of AbstractGraphicalObject.
@see AbstractGraphicalObject
*/
class StubGraphicalObject : public AbstractGraphicalObject
{
public:
explicit StubGraphicalObject()
: AbstractGraphicalObject(nullptr, nullptr, QString(), UniqueMapId())
{
}
~StubGraphicalObject() = default;
StubGraphicalObject(const StubGraphicalObject &other) = delete;
StubGraphicalObject &operator=(StubGraphicalObject &other) = delete;
protected:
bool changed() override
{
return false;
}
bool updateViewImpl() override
{
return true;
}
bool create() override
{
return true;
}
};
} // namespace SituationMap
@@ -0,0 +1,622 @@
/**
* @RDE_HEADER@
* TacticalAppearance
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "tactical_appearance.h"
#include <MapImpl/map_impl.h>
#include <Symbology/symbol_dictionary.h>
#include <Symbology/sidc_builder_base.h>
#include <Symbology/sidc_codec.h>
#include <GraphicalObjects/graphical_object_picture_marker.h>
#include <Map/situation_map_utils.h> // operator<<
#include <Common/simple_value.h>
// ESRI
#include <arcgis/Map.h>
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/PolygonBuilder.h>
#include <arcgis/PolylineBuilder.h>
#include <arcgis/MultipointBuilder.h>
// Qt
#include <QElapsedTimer>
namespace SituationMap
{
class TacticalAppearance::Private
{
public:
Private() = default;
~Private() = default;
// ESRI data:
MapImpl *m_mapImpl = nullptr;
QString m_logicalLayerName;
Esri::ArcGISRuntime::GraphicsOverlay *m_graphicOverlayTactical = nullptr;
AdvancedSymbology::SymbolDictionary *m_dictionary = nullptr;
AdvancedSymbology::SymbolResultWatcher m_symbolResultWatcher;
// OWN data:
SimpleValue<QString> m_uniqueDesignation;
SimpleValue<SymbolData> m_symbol;
AdvancedSymbology::SymbolDataToRender m_symDataToRender;
GraphicalObjectPictureMarker *m_pictureMarker = nullptr;
}; // class TacticalAppearance::Private
//-----------------------------------------------------------------------------
TacticalAppearance::TacticalAppearance(MapImpl *mapImpl,
const QString &logicalLayerName,
const QString &debugInfo,
const UniqueMapId &uniqueMapId)
: BaseClass(mapImpl->getEsriGeoView(), nullptr, debugInfo, uniqueMapId),
m_logger(getLogger("TacticalAppearance")),
d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< ", logicalLayerName='" << logicalLayerName << "'";
d->m_symDataToRender.sizeL = 100;
d->m_mapImpl = mapImpl;
d->m_logicalLayerName = logicalLayerName;
if (!m_graphicsLayer && d->m_mapImpl)
m_graphicsLayer = d->m_mapImpl->getGraphicsLayer(d->m_logicalLayerName);
if (!m_graphicsLayer)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " getGraphicsLayer failed";
}
if (d->m_mapImpl)
d->m_dictionary = &(d->m_mapImpl->symbolDictionary());
// lambda:
// m_symbolResultWatcher is canceled on destruction.
// So it`s safe that no signal will be fired afterwards.
// To avoid CLang warnings use m_symbolResultWatcher as context (3rd para), although it´s not intended.
// Better idea?
QObject::connect(&d->m_symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady, &d->m_symbolResultWatcher, [this]()
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " resultReady...";
QSharedPointer<AdvancedSymbology::SymbolResult> symResult = d->m_symbolResultWatcher.symbolResult();
if (! symResult)
return; // should NEVER happen! (Which object should have triggered the signal???)
if (d->m_pictureMarker)
{
d->m_pictureMarker->setImage(symResult->symbolImage().image());
d->m_pictureMarker->updateView();
}
});
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
TacticalAppearance::~TacticalAppearance()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
// abort pending call to SymbolDictionary::findSymbol:
d->m_symbolResultWatcher.cancel();
delete d->m_pictureMarker;
d->m_pictureMarker = nullptr;
removeGraphicFromGraphicOverlayTactical();
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void TacticalAppearance::removeGraphicFromGraphicOverlayTactical()
{
if (m_logger.isTraceEnabled())
{
if (m_graphic)
{
Esri::ArcGISRuntime::GraphicsOverlay *gOverlay = m_graphic->graphicsOverlay();
if (gOverlay)
TRACE_LOG(m_logger) << LOGFUNCTION << " START debugInfo='" << m_debugInfo << "'" << " name = '" << gOverlay->overlayId() << "'";
}
}
if (m_graphic && d->m_graphicOverlayTactical && d->m_graphicOverlayTactical->graphics())
d->m_graphicOverlayTactical->graphics()->removeOne(m_graphic);
delete m_graphic;
m_graphic = nullptr;
}
bool TacticalAppearance::useDictionaryRenderMode(const AdvancedSymbology::GeometryType &geomType) const
{
bool usePictureMarker = isSinglePoint() && (AdvancedSymbology::GeometryType::Point == geomType || AdvancedSymbology::GeometryType::Undefined == geomType);
return !usePictureMarker || symbol().useAlwaysDictionaryRenderer;
} // bool TacticalAppearance::useDictionaryRenderMode() const
bool TacticalAppearance::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " m_visibility = " << m_visibility;
bool success = true;
//
// Prepare / create :
//
// a) singlePoint / exactly one coordinate -> use pictureMarker
// or
// b) multiPoint / more than one coordinate -> m_graphic / DictionaryRenderer
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This assumption doesn't work any more because some polyline and polygon symbols will have only one
* anchor point and the real geometry is defined by additional attributes like radius, with, length ...
*
* JT 2016/12/20
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if (m_graphicsLayer && m_visibility.value())
{
const SymbolData &symbolData_ref = d->m_symbol.value();
const QString &baseSymbolCode = AdvancedSymbology::SidcBuilderBase::concreteSidcToBaseSymbolCode(symbolData_ref.sidc);
const AdvancedSymbology::SymbolProperties &symbolProperties = d->m_dictionary->symbolProperties(baseSymbolCode);
AdvancedSymbology::GeometryType geomType = symbolProperties.geometryType();
if (useDictionaryRenderMode(geomType))
{
if (!d->m_graphicOverlayTactical)
d->m_graphicOverlayTactical = d->m_mapImpl->getGraphicsLayerDict(d->m_logicalLayerName);
if (!d->m_graphicOverlayTactical)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " getGraphicsLayerDict failed";
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;
// Adding an empty graphic leads to Segmentation fault.
// (in Esri_runtimecore::Map_renderer::Dictionary_renderer::derive_geometry)
bool successGraphic = addUpdateGraphic(); // CHECK
if (!successGraphic)
ERROR_LOG(m_logger) << LOGFUNCTION << " addUpdateGraphic failed, debugInfo='" << m_debugInfo << "'" << " uniqueMapId=" << m_uniqueMapId;
} // if (!m_graphic)
}
else
{
// m_graphic is needless now:
removeGraphicFromGraphicOverlayTactical();
}
}
// : Prepare / create
bool positionsEmpty = m_positions.value().isEmpty();
if (positionsEmpty)
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " positions empty";
success = success && !positionsEmpty;
if (!success)
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " create failed";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
return success;
}
void TacticalAppearance::createPictureMarker()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
if (!d->m_pictureMarker && d->m_mapImpl)
{
d->m_pictureMarker = new GraphicalObjectPictureMarker(d->m_mapImpl->getEsriGeoView()
, m_graphicsLayer
, m_debugInfo + QLatin1String("#PicMarkTacticalAppearance")
, m_uniqueMapId);
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void TacticalAppearance::removePictureMarker()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
delete d->m_pictureMarker;
d->m_pictureMarker = nullptr;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
QString TacticalAppearance::uniqueDesignation() const
{
return d->m_uniqueDesignation.value();
}
void TacticalAppearance::setUniqueDesignation(const QString &uniqueDesignation)
{
d->m_uniqueDesignation = uniqueDesignation;
}
SymbolImage TacticalAppearance::symbolImage() const
{
SymbolImage symImage;
if (d->m_pictureMarker)
{
symImage.setImage(d->m_pictureMarker->image());
symImage.setValid(!symImage.image().isNull());
}
return symImage;
}
SymbolData TacticalAppearance::symbol() const
{
return d->m_symbol.value();
}
void TacticalAppearance::setSymbol(const SymbolData &symbol)
{
d->m_symbol = symbol;
}
bool TacticalAppearance::changed()
{
return BaseClass::changed()
|| d->m_uniqueDesignation.changed()
|| d->m_symbol.changed();
}
void TacticalAppearance::resetChanged()
{
BaseClass::resetChanged();
d->m_uniqueDesignation.reset();
d->m_symbol.reset();
}
Esri::ArcGISRuntime::Geometry TacticalAppearance::getGraphicGeometry() const
{
if (d->m_pictureMarker) return d->m_pictureMarker->getGraphicGeometry();
if (m_graphic) return m_graphic->geometry();
ERROR_LOG(m_logger) << LOGFUNCTION << "Geometry is undefined";
return Esri::ArcGISRuntime::Geometry();
} // Esri::ArcGISRuntime::Geometry TacticalAppearance::getGraphicGeometry() const
bool TacticalAppearance::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " symbol='" << d->m_symbol.value().sidc << "'"
<< " visibility=" << m_visibility
<< " selected=" << m_selected
<< " origin=" << this->origin()
<< " scale=" << m_scale.value();
const SymbolData &symbolData_ref = d->m_symbol.value();
const QString &baseSymbolCode = AdvancedSymbology::SidcBuilderBase::concreteSidcToBaseSymbolCode(symbolData_ref.sidc);
const AdvancedSymbology::SymbolProperties &symbolProperties = d->m_dictionary->symbolProperties(baseSymbolCode);
AdvancedSymbology::GeometryType geomType = symbolProperties.geometryType();
if (geomType == AdvancedSymbology::GeometryType::Undefined)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " GeometryType::Undefined, symbolProperties failed!?!";
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< ", geomType=" << geomType
<< ", useDictionaryRenderMode(geomType)=" << useDictionaryRenderMode(geomType);
bool success = create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
if (useDictionaryRenderMode(geomType))
{
// real work:
if (m_positions.changed() || d->m_symbol.changed() || m_visibility.changed())
{
auto buildPointClusterGeometry = [this]() -> Esri::ArcGISRuntime::Geometry
{
QSharedPointer<Esri::ArcGISRuntime::MultipointBuilder> builder(new Esri::ArcGISRuntime::MultipointBuilder(spatialReference()));
Esri::ArcGISRuntime::PointCollection points(spatialReference());
const auto posCont = positions();
for (const auto &pos : posCont)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " addPoint:" << pos.x() << " - " << pos.y();
points.addPoint(pos.x(), pos.y());
}
builder->setPoints(&points);
return builder->toGeometry();
}; // buildPointClusterGeometry
Esri::ArcGISRuntime::Geometry geom;
switch (geomType)
{
case AdvancedSymbology::GeometryType::Undefined : break;
case AdvancedSymbology::GeometryType::Point :
case AdvancedSymbology::GeometryType::Polyline :
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " m_positions.count()=" << m_positions.value().count();
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* If a PolylineBuilder is used here to create symbol geometry the construction appropriate symbol standard (MIL2525C / MIL2525D / App6)
* will not be done and the symbol is always a simple polyline. Using MultipointBuilder to build symbol geometry the result is based on
* underlying symbol standard.
* 2016/12/9 JT
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
geom = buildPointClusterGeometry();
} break;
case AdvancedSymbology::GeometryType::Polygon :
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " m_positions.count()=" << m_positions.value().count();
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Some symbols have polygon geometry and could have only one or two positions. In case of less than 3 positions do the same as for
* polylines and use MultipointBuilder to create the symbol geometry. In other case use PolygonBuilder.
* 2016/12/13 JT
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if (positions().size() > 2)
{
QSharedPointer<Esri::ArcGISRuntime::PolygonBuilder> builder(new Esri::ArcGISRuntime::PolygonBuilder(spatialReference()));
const auto posCont = positions();
for (const auto &pos : posCont)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " addPoint:" << pos.x() << " - " << pos.y();
builder->addPoint(pos.x(), pos.y());
}
geom = builder->toGeometry();
}
else
geom = buildPointClusterGeometry();
} break;
} // switch
if (geom.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Geometry NOT OK -> fallback symbol" << " (geometryType was " << geomType << ")";
createPictureMarker();
d->m_pictureMarker->setDebugInfo(m_debugInfo + QLatin1String("#fallback"));
int size = symbolData_ref.size;
if (size < 10) size = 10;
if (size > 100) size = 100;
d->m_pictureMarker->setMarkerSymbol(MarkerSymbolType::Diamond, size, symbolData_ref.color);
d->m_pictureMarker->setOrigin(this->origin());
d->m_pictureMarker->setDrawOrder(m_drawOrder.value());
d->m_pictureMarker->updateView();
success = false;
}
else // geom is not empty
{
removePictureMarker();
if (m_graphic)
{
success = true;
// move to graphic:
m_graphic->setGeometry(geom);
// prepare rawAttributes for DictionaryRenderer:
QVariantMap rawAttributes = d->m_symbol.value().attributeMap;
// build from scratch to honor removed attributes:
// (Otherwise the will remain wrongly)
rawAttributes[QLatin1String(SituationMap::UniqueMapIdAttribute)] = m_uniqueMapId;
rawAttributes[QLatin1String(SituationMap::IsPickableAttribute)] = m_pickable.value(); // set pickable attribute JT 2016/12/21
rawAttributes[QLatin1String("_type")] = QLatin1String("position_report");
rawAttributes[QLatin1String("_action")] = QLatin1String("update");
rawAttributes[QLatin1String("sidc")] = AdvancedSymbology::SidcCodec::decodeRawSidc(d->m_symbol.value().sidc); // in case of MIL2525
rawAttributes[QLatin1String("sic")] = AdvancedSymbology::SidcCodec::decodeRawSidc(d->m_symbol.value().sidc); // in case of APP6
Esri::ArcGISRuntime::AttributeListModel * attributeModel = m_graphic->attributes();
if (attributeModel)
attributeModel->setAttributesMap(rawAttributes);
else
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " no AttributeListModel";
// // prepare rawAttributes for DictionaryRenderer:
// QVariantMap rawAttributes = d->m_symbol.value().attributeMap;
// rawAttributes[QLatin1String(SituationMap::IsPickableAttribute)] = m_pickable.value(); // set pickable attribute JT 2016/12/21
// rawAttributes[QLatin1String("_type")] = QLatin1String("position_report");
// rawAttributes[QLatin1String("_action")] = QLatin1String("update");
// rawAttributes[QLatin1String("sidc")] = AdvancedSymbology::SidcCodec::decodeRawSidc(d->m_symbol.value().sidc); // in case of MIL2525
// rawAttributes[QLatin1String("sic")] = AdvancedSymbology::SidcCodec::decodeRawSidc(d->m_symbol.value().sidc); // in case of APP6
// // move to graphic:
// m_graphic->setGeometry(geom);
// /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// * Replace:
// * m_graphic->attributes()->setAttributesMap(rawAttributes);
// *
// * because it doesn't work anymore in ArcGISRuntime V100.2
// * JT, 2018/01/24
// * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
// Esri::ArcGISRuntime::AttributeListModel * attributeModel = m_graphic->attributes();
// if (attributeModel)
// {
// if (!attributeModel->isEmpty())
// {
// for (auto it = rawAttributes.cbegin(); it != rawAttributes.cend(); ++it)
// {
// const QString &attrKey = it.key();
// if (attributeModel->containsAttribute(attrKey))
// {
// attributeModel->replaceAttribute(attrKey, rawAttributes[attrKey]);
// continue;
// }
// attributeModel->insertAttribute(attrKey, rawAttributes[attrKey]);
// }
// }
// else attributeModel->setAttributesMap(rawAttributes);
// }
// 2021-08, N. Thoden
// While switching back to the behaviour that one logical layer has
// 1st getGraphicsLayer and 2nd getGraphicsLayerDict [GUEZE-496],
// the tactical appereance of DangerArea and similar disappears.
// Reason: m_graphicsLayer and m_graphicOverlayTactical are different now!
// It seems like that in former versions (< V100.8) the graphic could
// be added to more than one Esri layer.
// Now (V100.8) adding to a second layer fails.
// But we can remove the m_graphic from the default m_graphicsLayer
// before adding the the desired one: m_graphicOverlayTactical
if (m_graphicsLayer->graphics()->contains(m_graphic))
m_graphicsLayer->graphics()->removeOne(m_graphic);
// add to graphicOverlayTactical:
if (! d->m_graphicOverlayTactical->graphics()->contains(m_graphic))
d->m_graphicOverlayTactical->graphics()->append(m_graphic);
bool successGraphic = d->m_graphicOverlayTactical->graphics()->contains(m_graphic);
if (!successGraphic)
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " m_graphic not added to m_graphicOverlayTactical";
}
else
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " no m_graphic";
// success = false;
success = true; // if the DictionaryRenderer is used, m_graphic is null and that is ok.
}
} // if (geom.isEmpty())
}
}
else // !useDictionaryRenderMode(geomType)
{
createPictureMarker();
d->m_pictureMarker->setOrigin(this->origin());
// real work:
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " update geometry";
if (d->m_symbol.changed() || m_scale.changed())
{
if (d->m_dictionary)
{
int widthPixel = SituationMap::SymbolSizeLdefault;
widthPixel *= symbolData_ref.size * m_scale.value();
d->m_symDataToRender.sidc = symbolData_ref.sidc;
d->m_symDataToRender.fillColor = symbolData_ref.color;
d->m_symDataToRender.fillColorReplace = true;
d->m_symDataToRender.sizeL = widthPixel;
if (d->m_pictureMarker->image().isNull())
d->m_pictureMarker->setImage(d->m_dictionary->temporaryImage(d->m_symDataToRender.sizeL));
d->m_symbolResultWatcher.setSymbolResult(d->m_dictionary->fetchSymbol(d->m_symDataToRender));
success = true;
}
else
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " visibility=" << m_visibility
<< " no dictionary";
}
}
if (d->m_pictureMarker)
{
d->m_pictureMarker->setDrawOrder(m_drawOrder.value());
d->m_pictureMarker->setVisibility(m_visibility.value());
d->m_pictureMarker->setSelected(m_selected.value());
d->m_pictureMarker->setPickable(m_pickable.value());
success = success && d->m_pictureMarker->updateView();
}
} // else !useDictionaryRenderMode(geomType)
if (d->m_pictureMarker)
{
d->m_pictureMarker->setOrigin(this->origin()); // to avoid incorrect number of positions..
d->m_pictureMarker->setVisibility(m_visibility.value());
d->m_pictureMarker->setSelected(m_selected.value());
success = d->m_pictureMarker->updateView() && success;
}
// in case of no pictureMarker:
if (m_graphic)
{
m_graphic->setZIndex(m_drawOrder.value());
m_graphic->setVisible(m_visibility.value());
m_graphic->setSelected(m_selected.value());
}
// reset flags
d->m_symbol.reset();
if (!success)
ERROR_LOG(m_logger) << LOGFUNCTION << " failure, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success = " << success
<< " visibility=" << m_visibility;
return success;
}
} // namespace SituationMap
@@ -0,0 +1,117 @@
/**
* @RDE_HEADER@
* TacticalAppearance
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
// Esri
// Qt
// forward:
//----------------------------------------------------------------------------
namespace SituationMap
{
// forward:
class MapImpl;
class GraphicalObjectPictureMarker;
/**
* @brief The TacticalAppearance class displays sidcs.
* To do so, the number of coordinate lead to use
* - the GraphicalObjectPictureMarker (exactly one coordinate)
* or
* - the Message (more than one coordinate)
*
* Use cases:
* - Participant: sidc with #coords == 1, TacticalAppearance should get exactly one coordinate.
* (GraphicalObjectPictureMarker on ESRI::GraphicsLayer is used)
* - Tactical display of a Building: sidc #coords >= 2, TacticalAppearance should get list of positions.
* (Message on ESRI::MessageGroupLayer is used)
* - (Tactical) PreviewIcon of a Building: sidc #coords == 1, TacticalAppearance should get exactly one coordinate.
* (GraphicalObjectPictureMarker on ESRI::GraphicsLayer is used)
* - Tactical symbols of plan editor
*
* In case of GraphicalObjectPictureMarker the image creation is done by AdvancedSymbology::SymbolDictionary::symbolImageBySidcExtended().
*/
class SITUATIONMAP_EXPORT TacticalAppearance : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
/**
* @brief TacticalAppearance
* @param mapImpl map to be used.
* @param logicalLayerName logical layer name to be used.
* @param debugInfo debugInfo to be used
* @param uniqueMapId uniqueMapId to be used. (Optional to fulfill rubberband requirements)
*/
TacticalAppearance(MapImpl *mapImpl,
const QString &logicalLayerName,
const QString &debugInfo,
const UniqueMapId &uniqueMapId = INVALID_MAP_ID);
TacticalAppearance(const TacticalAppearance &other) = delete;
TacticalAppearance &operator=(TacticalAppearance &other) = delete;
virtual ~TacticalAppearance();
SymbolData symbol() const;
void setSymbol(const SymbolData &symbol);
QString uniqueDesignation() const;
void setUniqueDesignation(const QString &uniqueDesignation);
SymbolImage symbolImage() const;
Esri::ArcGISRuntime::Geometry getGraphicGeometry() const override;
protected:
// Internal/Admin
virtual bool create() override;
virtual void createPictureMarker();
virtual void removePictureMarker();
void removeGraphicFromGraphicOverlayTactical();
bool changed() override;
void resetChanged() override;
bool updateViewImpl() override;
bool useDictionaryRenderMode(const AdvancedSymbology::GeometryType &geomType) const;
// logical
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
class Private;
/**
* The private d-pointer to not break BC when changing internals of this class
*/
Private *d = nullptr;
};
} // namespace SituationMap