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,59 @@
/**
* @RDE_HEADER@
* Interface-Implementation of AbstractEffectSimulation
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "abstract_effect_simulation_impl.h"
#include "map_impl.h"
// ESRI
// Qt
namespace SituationMap
{
//-----------------------------------------------------------------------------
// constants
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Utils:
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// class AbstractEffectSimulationImpl
//-----------------------------------------------------------------------------
AbstractEffectSimulationImpl::AbstractEffectSimulationImpl()
: BaseClass() // Interface
, m_logger(getLogger("AbstractEffectSimulationImpl"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
AbstractEffectSimulationImpl::~AbstractEffectSimulationImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
QString AbstractEffectSimulationImpl::toString() const
{
return QString();
}
} // namespace SituationMap
@@ -0,0 +1,52 @@
/**
* @RDE_HEADER@
* Interface-Implementation of AbstractEffectSimulation
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_map_object_impl.h"
#include <Map/abstract_effect_simulation.h> // Interface
// ESRI
// Qt
//----------------------------------------------------------------------------
namespace SituationMap
{
// inherent 'public', to get that implementation
// inherent 'public virtual' to get that interface
class SITUATIONMAP_EXPORT AbstractEffectSimulationImpl : public virtual AbstractEffectSimulation
{
using BaseClass = SituationMap::AbstractEffectSimulation;
public:
AbstractEffectSimulationImpl();
virtual ~AbstractEffectSimulationImpl();
AbstractEffectSimulationImpl(const AbstractEffectSimulationImpl &other) = delete;
AbstractEffectSimulationImpl &operator=(AbstractEffectSimulationImpl &other) = delete;
virtual QString toString() const;
protected:
// logical
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
} // namespace SituationMap
File diff suppressed because it is too large. Load diff
+443
View File
@@ -0,0 +1,443 @@
/**
* @RDE_HEADER@
* Implementation of AbstractMapObject
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/abstract_map_object.h> // Interface
#include <Map/abstract_effect_simulation.h> // for AesEffectState
#include <Map/line_style.h>
#include <Map/fill_style.h>
#include <Common/simple_value.h>
#include <Common/situation_map_logging.h>
#include <Common/ref_pointer.h>
// Qt
#include <QString>
#include <QColor>
#include <limits>
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Graphic;
class GraphicsOverlay;
class Geometry;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
struct ExtraData;
class MapImpl;
class MapConnector;
class AbstractGraphicalObject;
class GraphicalObjectText;
class GraphicalObjectPath;
class TacticalAppearance;
class GraphicalObjectCube;
//----------------------------------------------------------------------------
class OriginPositionChangedCallback
{
public:
virtual ~OriginPositionChangedCallback() = default;
virtual void onOriginPositionChanged() = 0;
};
//----------------------------------------------------------------------------
/**
* @brief The AbstractMapObjectImpl class is the main base class of all MapObject instances.
* Therefore it supports typical parameters/functionality and avoids redundant code.
* As consequence AbstractMapObjectImpl supports functionality, which is not used in every subclass.
* The typical parameters/functionality is:
* - UniqueMapId
* - debugInfo
* - partyId, partyColor
* - aesState (aes = AreaEffectSimulation)
* - drawOrder
* - (tact.) symbol
* - visibility (major|techn|tact.)
* - pickability
* - selected
* - mainGraphicalObject (displaying the main techn. Appearance)
* - mainGraphicalObjectText (displaying the text/name)
* - tacticalAppearance (displaying the tact. Appearance)
* - m_graphicalObjectExtrusion (displaying the extrusion (3D))
* - mainGraphicalObjectPin (displaying a pin/needle to the ground)
*
* After an parameter changes, the method updateView() hat to be called.
*/
// inherent 'public virtual' to get that interface
class SITUATIONMAP_EXPORT AbstractMapObjectImpl : public virtual AbstractMapObject
{
public:
explicit AbstractMapObjectImpl(const QString &debugInfo = QString());
virtual ~AbstractMapObjectImpl();
void removeMySelf() override;
AbstractMapObjectImpl(const AbstractMapObjectImpl &other) = delete;
AbstractMapObjectImpl &operator=(AbstractMapObjectImpl &other) = delete;
// emulate QObject::property :
bool setProperty(const char *name, const QVariant &value) override;
QVariant property(const char *name) const override;
// : emulate QObject::property
UniqueMapId uniqueMapId() const override;
QString debugInfo() const override;
void setDebugInfo(const QString &debugInfo) override;
QString toString() const override;
// Data access:
void boundingBox(BoundingBox &bbox) const override;
QColor majorColor() const override;
QString identifier() const override;
void setIdentifier(const QString &identifier) override;
bool identifierVisible() const override;
void setIdentifierVisible(bool identifierVisible) override;
int partyId() const override;
bool isPartyIdValid() const override;
void setPartyId(int partyId) override;
bool setPartyIdChanged(int partyId = -1) override;
void forcePartyColor(const QColor &partyColorToForce) override;
bool useLightColor() const override; ///< Only for Participants
void setUseLightColor(bool useLightColor) override; ///< Only for Participants
virtual AesEffectState aesState() const;
virtual void setAesState(AesEffectState aesState);
TextStyle textStyle() const override;
TextStyle &rTextStyle() override;
void setTextStyle(const TextStyle &textStyle) override;
virtual LineStyle lineStyle() const;
virtual LineStyle &rLineStyle();
virtual void setLineStyle(const LineStyle &lineStyle);
virtual FillStyle fillStyle() const;
virtual FillStyle &rFillStyle();
virtual void setFillStyle(const FillStyle &fillStyle);
// Scale :
float scale() const override;
void setScale(float scale) override;
bool isScaleEnabled() const override;
void setScaleEnable(bool enabled) override;
// : Scale
// Layer
QString layer() const override;
int drawOrder() const override;
/**
* @brief Set drawOrder.
* Caused by internal dependencies, the value MUST within -3000...+3000. (INT_MAX=+32767 / 10 = 3276 -> ~3000).
* \n Otherwise the internal graphics will be disordered.
* \internal Background: Due to missing relative drawOrder an absolute drawOrder has to be used.\n
* To create an absolute drawOrder the specified drawOrder is multiplied with 10.
* The components (usually GraphicalObjects) receive that multiplied valued added by s small offset.
* drawOrder() is not influenced by this and will return the value you set before.
* @param drawOrder new value.
*/
void setDrawOrder(int drawOrder) override;
// Visibility :
// Prio 1:
bool visibility() const override;
void setVisibility(bool visibility) override;
// Prio 2
bool visibility(ObjectAppearance appearance) const override;
void setVisibility(ObjectAppearance appearance, bool visibility) override;
bool visibilityOfGeometry() const;
void setVisibilityOfGeometry(bool visiGeometry);
// : Visibility
// Selection, Picking :
bool selected() const override;
void setSelected(bool selected) override;
bool pickable() const override;
void setPickable(bool pickable) override;
// : Selection, Picking
// Symbol :
SymbolData symbol() const override;
void setSymbol(const SymbolData &symbol) override;
void reRenderSymbol() override;
SymbolDataToRender symbolDataToRender() const override;
SymbolImage symbolImage() const override;
QImage previewImage() const override;
QImage previewImageTypical() const override;
QImage previewImageSpecific() const override;
void setForceMajorColor(const QColor &majorColor) override;
// : Symbol
/*! \name Some methods regarding extrusion (renderer approach) */
//@{
double extrusionHeight() const override;
void setExtrusionHeight(double extrusionHeight = std::nan("")) override;
bool extrusionHeightValid() const;
const QString extrusionColorName() const override;
void setExtrusionColorName(const QString &extrusionColorName) override;
//@}
// Geometry :
bool geometryIntersect(const MathUtils::GeometryEngine::Geometry &intersector_polyline) const override;
bool geometryIsInHull(const MathUtils::GeometryEngine::Geometry &hull_polygon) const override;
Esri::ArcGISRuntime::Geometry getEsriGeometry() const;
// : Geometry
// Define default text style for all MapObjects:
static TextStyle defaultTextStyle();
/**
* @brief updateView
* transfer values to view
* @return true on success
*/
bool updateView() override
{
return updateViewImplPre() && updateViewImpl() && updateViewImplPost();
}
virtual MapImpl *mapImpl() { return m_mapImpl; }
int numberOfValuesChanged(); ///< For Unit/Module Test
protected:
virtual bool updateViewImplPre();
virtual bool updateViewImpl() = 0;
virtual bool updateViewImplPost();
friend class ObjectFactory;
// deriving classes MUST overload init and create an GraphicalObject!!!
// (to use whose identifier, textStyle)
virtual bool init(MapImpl *mapImpl, const QString &logicalLayerName = QStringLiteral("GraphicsLayer"));
virtual void setUniqueId(const UniqueMapId &uniqueId);
QString logicalLayerName() const;
bool setLogicalLayerName(const QString &logicalLayerName);
// internal/admin
void createTacticalAppearance();
void resetTacticalAppearanceDrawOrderOffset();
virtual bool updateTacticalAppearance();
// Extrusion
void createGraphicalObjectExtrusion();
void resetGraphicalObjectExtrusion();
bool updateGraphicalObjectExtrusion();
// pin/needle
void createPin();
void resetPin();
bool updatePin();
virtual void updateIdentifierPosition();
friend class MapConnector;
virtual bool onMapExtentChanged();
MapConnector *m_mapConnector = nullptr;
class VisibilityScale
{
public:
VisibilityScale()
{
resetToInitial();
}
void resetToInitial()
{
scale_zoomOutCurrent = scale_zoomOutInitialized;
}
void resetToMax()
{
scale_zoomOutCurrent = std::numeric_limits<double>::max();
}
void setInitial(double newValue)
{
scale_zoomOutInitialized = newValue;
resetToInitial();
}
bool scaleHasVisibility(double curScale) const
{
if (scale_zoomOutCurrent <= 0)
return true;
return (curScale < scale_zoomOutCurrent);
}
private:
double scale_zoomOutCurrent = 0;
double scale_zoomOutInitialized = 70000;
};
VisibilityScale m_visibilityScaleRange; // static value 's_scaleHasVisibilityValue' is located in the interface
UniqueMapId m_uniqueMapId = SituationMap::INVALID_MAP_ID; ///< for identification
QString m_debugInfo; ///< for debugging
MapImpl *m_mapImpl = nullptr;
QString m_logicalLayerName;
Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr;
// party :
struct party
{
SimpleValue<int> partyId = -1;
SimpleValue<bool> useLightColor = false; // useful for Participants
mutable QColor partyColor;
SimpleValue<QColor> forcedPartyColor;
bool changed() const { return partyId.changed() || useLightColor.changed() || forcedPartyColor.changed(); }
void reset() { partyId.reset(); useLightColor.reset(); forcedPartyColor.reset(); }
};
party m_party;
bool partyChanged() const { return m_party.changed(); }
void partyReset() { m_party.reset(); }
virtual const QColor &partyColor() const;
virtual QColor partyColor(int partyId, bool useLightColor) const;
virtual AdvancedSymbology::Role partyRole(bool roleIdentityAssumed = false) const;
// : party
// state :
SimpleValue<AesEffectState> m_aesState{AesEffectState::Unknown};
// : state
private:
int m_drawOrder{0}; // logical drawOrder
protected:
int m_internalDrawOrder{0};
// m_mainGraphicalObject will store LineStyle (color/width/pattern), FillStyle (color/pattern):
AbstractGraphicalObject *m_mainGraphicalObject{nullptr};
bool m_enableScale{true};
// m_mainGraphicalText will store TextStyle (color/width/size...)
GraphicalObjectText *m_mainGraphicalObjectText{nullptr};
Point3D m_identifierPositionCandidateTranslated;
SimpleValue<bool> m_identiferVisible{true};
TacticalAppearance *m_tacticalAppearance{nullptr};
int m_tacticalAppearanceDrawOrderOffset{2};
// for 3D Extrusion:
GraphicalObjectCube *m_graphicalObjectExtrusion{nullptr};
double m_extrusionHeight{std::nan("")};
QString m_extrusionColorName;
// For Pin
GraphicalObjectPath *m_graphicalObjectPin{nullptr};
double m_defaultZValue{0};
SimpleValue<SymbolData> m_symbol;
SymbolImage m_symbolImage;
QColor m_forcedMajorColor;
// Update ObjectIndicator :
protected:
bool m_updateObjectIndicator{false};
QVector<OriginPositionChangedCallback *> m_originPositionChangedCb;
public:
void addChangedCallback(OriginPositionChangedCallback *cb);
void removeChangedCallback(OriginPositionChangedCallback *cb);
// : Update ObjectIndicator
protected:
bool m_visibility = true;
bool m_visibilityTechnical = true;
bool m_visibilityTechnicalGeometry = true; // Special case
bool m_visibilityTactical = true;
bool m_selected = false;
bool m_pickable = true;
bool m_editorMode = false;
bool m_lineColorFromParty = true;
int m_fillColorAlpha = 255;
private:
ExtraData *m_extraData = nullptr;
void createGraphicalObjectText();
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
QString toString(const AesEffectState value);
std::ostream &operator<< (std::ostream &stream,
const AesEffectState &value);
} // namespace SituationMap
@@ -0,0 +1,206 @@
/**
* @RDE_HEADER@
* Interface-Implementation of Map
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/coord_translation_functor.h>
#include <Map/situation_map_utils.h>
#include "map_impl.h"
// ESRI
#include <arcgis/GeometryEngine.h>
//#include <arcgis/CoordinateConversion.h>
// Qt
#include <QPointF>
#include <QMutex>
#include <QMutexLocker>
#include <cmath>
namespace SituationMap
{
class CoordTranslationFunctorImpl final : public CoordTranslationFunctor
{
public:
explicit CoordTranslationFunctorImpl(const MapImpl *mapImpl)
: m_mapImpl(mapImpl)
, m_logger(getLogger("CoordTranslationFunctorImpl"))
{}
virtual ~CoordTranslationFunctorImpl() = default;
QPointF geographicToMap(const QPointF &coord) const override
{
return geographicToMap(coord.x(), coord.y());
}
QPointF mapToGeographic(const QPointF &coord) const override
{
return mapToGeographic(coord.x(), coord.y());
}
QPointF geographicToMap(const Esri::ArcGISRuntime::Point &coord) const override
{
return geographicToMap(coord.x(), coord.y());
}
QPointF mapToGeographic(const Esri::ArcGISRuntime::Point &coord) const override
{
return mapToGeographic(coord.x(), coord.y());
}
QPointF geographicToMap(const double lon, const double lat) const override
{
return convert(lon, lat, false);
}
QPointF mapToGeographic(const double x, const double y) const override
{
return convert(x, y, true);
}
Point3D geographicToMap(const Point3D &coord) const override
{
const QPointF &p = geographicToMap(coord.x(), coord.y());
return Point3D(p.x(), p.y(), coord.z());
}
Point3D mapToGeographic(const Point3D &coord) const override
{
const QPointF &p = mapToGeographic(coord.x(), coord.y());
return Point3D(p.x(), p.y(), coord.z());
}
bool mapIsGeographic() const override
{
return (m_mapImpl->getEsriSpatialReference().isGeographic());
} // bool mapIsGeographic() const override
void geographicToUtm(const double referenceLon, QList<QPointF> &points) const override
{
int wkid(buildWkid(referenceLon));
const Esri::ArcGISRuntime::SpatialReference utmSR(wkid);
QMutexLocker lock(&m_mutex);
for(QList<QPointF>::iterator it = points.begin(); it != points.end(); ++it)
{
QPointF &pt = *it;
const Esri::ArcGISRuntime::Point utmPoint( Esri::ArcGISRuntime::GeometryEngine::project( Esri::ArcGISRuntime::Point(pt.x(), pt.y(), 0.0, m_wkSR), utmSR ));
pt.setX(utmPoint.x());
pt.setY(utmPoint.y());
}
} // void geographicToUtm(const double referenceLon, QList<QPointF> &points) const override
void utmToGeographic(const double referenceLon, QList<QPointF> &points) const override
{
int wkid(buildWkid(referenceLon));
const Esri::ArcGISRuntime::SpatialReference utmSR(wkid);
QMutexLocker lock(&m_mutex);
for (QList<QPointF>::iterator it = points.begin(); it != points.end(); ++it)
{
QPointF &pt = *it;
const Esri::ArcGISRuntime::Point geoPoint(Esri::ArcGISRuntime::GeometryEngine::project(Esri::ArcGISRuntime::Point(pt.x(), pt.y(), 0.0, utmSR), m_wkSR));
pt.setX(geoPoint.x());
pt.setY(geoPoint.y());
}
} // void utmToGeographic(const double referenceLon, QList<QPointF> &points) const override
int projectionCounter() const override { return m_projectionCounter; }
void resetProjectionCounter() override { m_projectionCounter = 0; }
private:
static int buildWkid(const double referenceLon)
{
int utmZone = referenceLon / 6;
if (utmZone < 0) utmZone = 60 + utmZone;
utmZone += 31;
if (utmZone > 60) utmZone -= 60;
return 32600 + utmZone;
} // static int buildWkid(const double referenceLon)
QPointF convert(const double x, const double y, bool toGeographic) const
{
// if x,y are geographic (x=lon/y=lat), use radian
const char *directionString = toGeographic ? "mapToGeographic" : "geographicToMap";
if (!m_mapImpl)
{
ERROR_LOG(m_logger) << "Error converting " << directionString
<< " - no map!";
return QPointF();
}
const CoordTranslationFunctor *ctF = m_mapImpl->coordTranslationFunctor();
if (!ctF)
{
ERROR_LOG(m_logger) << "Error converting " << directionString
<< " - no coordinate translation function!";
return QPointF();
}
const Esri::ArcGISRuntime::SpatialReference sourceSR = toGeographic ? m_mapImpl->getEsriSpatialReference() : m_wkSR;
const Esri::ArcGISRuntime::SpatialReference destSR = toGeographic ? m_wkSR : m_mapImpl->getEsriSpatialReference();
if (sourceSR.isEmpty() || destSR.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " SpatialReference empty"
<< " sourceSR.isEmpty()=" << sourceSR.isEmpty()
<< " destSR.isEmpty()=" << destSR.isEmpty();
return QPointF();
}
// toGeographic: deg -> rad
// !toGeographic: rad -> deg (== from geographic)
const double factorSrc = toGeographic ? 1 : 180 / M_PI;
const double factorDst = toGeographic ? M_PI / 180 : 1;
QMutexLocker lock(&m_mutex);
// const double z = 12.34567;
const Esri::ArcGISRuntime::Point destPoint(Esri::ArcGISRuntime::GeometryEngine::project(Esri::ArcGISRuntime::Point(x * factorSrc,
y * factorSrc,
// z,
sourceSR),
destSR));
// qCritical() << " zOut= " << destPoint.z();
const QPointF ret(destPoint.x() * factorDst, destPoint.y() * factorDst);
++m_projectionCounter;
return ret;
}
const MapImpl *m_mapImpl = nullptr;
const Esri::ArcGISRuntime::SpatialReference m_wkSR = Esri::ArcGISRuntime::SpatialReference(4326); // EPSG:4236 = WGS84
mutable QMutex m_mutex;
mutable int m_projectionCounter = 0; // hmmm - really needed?
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
} // namespace SituationMap
+359
View File
@@ -0,0 +1,359 @@
/**
* @RDE_HEADER@
* CoordinateTranslation
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "coordinate_translation.h"
#include <Map/coord_translation_functor.h>
#include <MapImpl/map_impl.h>
#include <MapImpl/abstract_map_object_impl.h>
#include <MathUtils/simple_helper.h>
// ESRI
#include <arcgis/Point.h>
#include <arcgis/GeometryEngine.h>
// Qt
#include <QMutexLocker>
#include <iostream>
//----------------------------------------------------------------------------
namespace SituationMap
{
void TranslatedBase::setCoordTransFunctor(CoordTranslationFunctor *functor)
{
m_coordTransFunctor = functor;
}
void TranslatedBase::setMapObjectImpl(AbstractMapObjectImpl *mapObjectImpl)
{
m_mapObjectImpl = mapObjectImpl;
}
OriginTranslated::OriginTranslated()
#ifdef WITH_LOGGING
: m_logger(getLogger("OriginTranslated"))
#endif
{}
Point3D OriginTranslated::origin() const
{
return m_origin;
}
bool OriginTranslated::setOrigin(const Point3D &origin)
{
bool changed = m_origin != origin;
if (changed)
{
m_origin = origin;
m_translated = false;
}
return changed;
}
const Point3D &OriginTranslated::originTranslated() const
{
#ifdef WITH_LOGGING
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " m_origin=" << m_origin
;
#endif
if (!m_translated)
{
// m_coordTransFunctor has priority, otherwise use functor of mapImpl:
if (!m_coordTransFunctor)
{
if (m_mapObjectImpl && m_mapObjectImpl->mapImpl())
{
// if useful, a MapObjectType specific functor can be prefered here:
// like: if (m_mapObjectImpl->coordTranslationFunctor()) ctFunctor=m_mapObjectImpl->coordTranslationFunctor();
m_coordTransFunctor = m_mapObjectImpl->mapImpl()->coordTranslationFunctor();
}
}
if (m_coordTransFunctor)
m_originTranslated = m_coordTransFunctor->geographicToMap(m_origin);
else
m_originTranslated = Point3D();
m_translated = true;
}
#ifdef WITH_LOGGING
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " m_originTranslated=" << m_originTranslated
;
#endif
return m_originTranslated;
}
PositionsTranslated::PositionsTranslated()
: m_mutex(QMutex::Recursive)
#ifdef WITH_LOGGING
, m_logger(getLogger("PositionsTranslated"))
#endif
{}
PositionCont PositionsTranslated::positions() const
{
QMutexLocker lock(&m_mutex);
return m_positions + m_singlePositions;
}
bool PositionsTranslated::setPositions(const PositionCont &posCont)
{
QMutexLocker lock(&m_mutex);
bool changed = m_positions != posCont;
if (changed)
{
m_positions = posCont;
m_translated = false;
}
m_singlePositions.clear();
return changed;
}
void PositionsTranslated::addSinglePosition(const Point3D &singlePositions)
{
QMutexLocker lock(&m_mutex);
m_singlePositions.append(singlePositions);
}
bool PositionsTranslated::setPositionsTranslated(const PositionCont &posCont)
{
QMutexLocker lock(&m_mutex);
if ((m_translated) && (m_positionsTranslated == posCont))
return true;
m_positionsTranslated = posCont;
m_positions.clear();
if (!m_coordTransFunctor)
{
if (m_mapObjectImpl && m_mapObjectImpl->mapImpl())
{
// if useful, a MapObjectType specific functor can be prefered here:
// like: if (m_mapObjectImpl->coordTranslationFunctor()) ctFunctor=m_mapObjectImpl->coordTranslationFunctor();
m_coordTransFunctor = m_mapObjectImpl->mapImpl()->coordTranslationFunctor();
}
}
if (m_coordTransFunctor)
{
m_positions.reserve(m_positionsTranslated.size());
for (int i = 0; i < m_positionsTranslated.count(); ++i)
m_positions.push_back(m_coordTransFunctor->mapToGeographic(m_positionsTranslated.at(i))); // for
}
m_singlePositions.clear();
m_translated = true;
return false;
} // PositionsTranslated::setPositionsTranslated( const PositionCont & posCont )
int PositionsTranslated::size() const
{
QMutexLocker lock(&m_mutex);
return m_positions.size() + m_singlePositions.size();
}
bool PositionsTranslated::isEmpty() const
{
QMutexLocker lock(&m_mutex);
return m_positions.isEmpty() && m_singlePositions.isEmpty();
}
void PositionsTranslated::clear()
{
QMutexLocker lock(&m_mutex);
m_positions.clear();
m_positionsTranslated.clear();
m_singlePositions.clear();
m_translated = false;
}
Point3D PositionsTranslated::first()
{
if (isEmpty())
return Point3D();
if (! m_positions.isEmpty())
return m_positions.first();
return m_singlePositions.first();
}
Point3D PositionsTranslated::last()
{
if (isEmpty())
return Point3D();
if (! m_singlePositions.isEmpty())
return m_singlePositions.last();
return m_positions.last();
}
const PositionCont &PositionsTranslated::positionsTranslated() const
{
#ifdef WITH_LOGGING
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " m_positions=" << m_positions;
#endif
QMutexLocker lock(&m_mutex);
// !!! Important: consolidate !!!
consolidate();
if (!m_translated)
{
// translate:
m_positionsTranslated = geographicToMap(m_positions);
m_translated = true;
}
#ifdef WITH_LOGGING
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " m_positionsTranslated=" << m_positionsTranslated;
#endif
return m_positionsTranslated;
}
PositionCont PositionsTranslated::geographicToMap(const PositionCont &positions) const
{
PositionCont ret;
// m_coordTransFunctor has priority, otherwise use functor of mapImpl:
if (!m_coordTransFunctor)
{
if (m_mapObjectImpl && m_mapObjectImpl->mapImpl())
{
// if useful, a MapObjectType specific functor can be prefered here:
// like: if (m_mapObjectImpl->coordTranslationFunctor()) ctFunctor=m_mapObjectImpl->coordTranslationFunctor();
m_coordTransFunctor = m_mapObjectImpl->mapImpl()->coordTranslationFunctor();
}
}
if (!m_coordTransFunctor)
return ret;
ret.reserve(positions.size());
for (int i = 0; i < positions.count(); ++i)
{
const Point3D &p = positions.at(i);
ret.push_back(m_coordTransFunctor->geographicToMap(p));
} // for
return ret;
}
const Point3D &PositionsTranslated::startPointTranslated() const
{
QMutexLocker lock(&m_mutex);
// make sure that positions are translated:
positionsTranslated();
return m_positionsTranslated.first();
}
const Point3D &PositionsTranslated::endPointTranslated() const
{
QMutexLocker lock(&m_mutex);
// make sure that positions are translated:
positionsTranslated();
return m_positionsTranslated.back();
}
double PositionsTranslated::angleOfStartSegment() const
{
QMutexLocker lock(&m_mutex);
positionsTranslated();
const int size = m_positionsTranslated.size();
if (size < 2)
return 0;
// rechtwinkliges Koordinatensystem benutzen
return MathUtils::getAngleDegreeNorth(m_positionsTranslated.at(0).toPointF(), m_positionsTranslated.at(1).toPointF());
}
double PositionsTranslated::angleOfEndSegment() const
{
QMutexLocker lock(&m_mutex);
positionsTranslated();
const int size = m_positionsTranslated.size();
if (size < 2)
return 0;
// rechtwinkliges Koordinatensystem benutzen
return MathUtils::getAngleDegreeNorth(m_positionsTranslated.at(size - 2).toPointF(), m_positionsTranslated.at(size - 1).toPointF());
}
double PositionsTranslated::angleOfClosingSegment() const
{
QMutexLocker lock(&m_mutex);
positionsTranslated();
const int size = m_positionsTranslated.size();
if (size < 2)
return 0;
// rechtwinkliges Koordinatensystem benutzen
return MathUtils::getAngleDegreeNorth(m_positionsTranslated.last().toPointF(), m_positionsTranslated.first().toPointF());
}
void PositionsTranslated::consolidate() const
{
QMutexLocker lock(&m_mutex);
if (m_translated)
{
// ok, m_positions is already translated to m_positionsTranslated
// translate just the remaining m_singlePositions:
// To do so
// 1. add the m_singlePositions to m_positions
m_positions += m_singlePositions;
// 2. translate just the remaining m_singlePositions
// 3. add the now translated singlePositions to
m_positionsTranslated += geographicToMap(m_singlePositions);
// 4. cleanup m_singlePositions:
m_singlePositions.clear();
}
else
{
// move m_singlePositions to m_positions
m_positions += m_singlePositions;
m_singlePositions.clear();
}
}
} // namespace SituationMap
+190
View File
@@ -0,0 +1,190 @@
/**
* @RDE_HEADER@
* CoordinateTranslation
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/situation_map_types.h>
// #define WITH_LOGGING
#ifdef WITH_LOGGING
# include <Common/situation_map_logging.h>
#endif // WITH_LOGGING
// ESRI
// Qt
#include <QMutex>
//----------------------------------------------------------------------------
namespace SituationMap
{
//forward:
class CoordTranslationFunctor;
class AbstractMapObjectImpl;
class SITUATIONMAP_EXPORT TranslatedBase
{
public:
explicit TranslatedBase() = default;
virtual ~TranslatedBase() = default;
// use ONE of the following methods to set functor:
void setCoordTransFunctor(CoordTranslationFunctor *functor); // set functor direct
void setMapObjectImpl(AbstractMapObjectImpl *mapObjectImpl); // MapObject->Map->functor
bool isTranslated() const
{
return m_translated;
}
virtual bool isEmpty() const = 0;
protected:
mutable CoordTranslationFunctor *m_coordTransFunctor = nullptr;
AbstractMapObjectImpl *m_mapObjectImpl = nullptr; ///< parent / host
mutable bool m_translated = false;
};
class SITUATIONMAP_EXPORT OriginTranslated final : public TranslatedBase
{
public:
OriginTranslated();
Point3D origin() const;
bool setOrigin(const Point3D &origin);
const Point3D &originTranslated() const;
void clear()
{
m_origin = Point3D();
}
bool isEmpty() const
{
return m_origin.isNull();
}
/**
* @brief operator== Compare two objects
* @param other to be compared with
* @return True if identical, otherwise false.
*/
bool operator==(const OriginTranslated &other) const
{
// m_positionsTranslated must be skipped!
return ((this->m_origin == other.m_origin));
}
/**
* @brief operator!= Compare two objects
* @param other to be compared with
* @return True if NOT identical, otherwise false.
*/
bool operator!=(const OriginTranslated &other) const
{
return !(*this == other);
}
private:
#ifdef WITH_LOGGING
LOGGER_T m_logger; ///< Interface of the Logging-Facility
#endif
Point3D m_origin;
mutable Point3D m_originTranslated;
};
/**
* @brief The PositionsTranslated class to translate positions of simulation world to map coordinate.
* A the translation is done using the CoordTranslationFunctor.
* (A copy of this object is not allowed since inner mutex can´t be copied.)
*/
class SITUATIONMAP_EXPORT PositionsTranslated : public TranslatedBase
{
public:
PositionsTranslated();
// disable copy
PositionsTranslated(const PositionsTranslated &) = delete;
PositionsTranslated &operator=(const PositionsTranslated &) = delete;
PositionCont positions() const;
bool setPositions(const PositionCont &posCont);
void addSinglePosition(const Point3D &singlePositions);
bool setPositionsTranslated(const PositionCont &posCont);
int size() const;
bool isEmpty() const;
void clear();
Point3D first();
Point3D last();
bool isTranslated() const { return m_translated && m_singlePositions.isEmpty(); }
const PositionCont &positionsTranslated() const;
const Point3D &startPointTranslated() const;
const Point3D &endPointTranslated() const;
double angleOfStartSegment() const;
double angleOfEndSegment() const;
double angleOfClosingSegment() const; ///< Angle between end and start
/**
* @brief operator== Compare two objects
* @param other to be compared with
* @return True if identical, otherwise false.
*/
bool operator==(const PositionsTranslated &other) const
{
// m_positionsTranslated must be skipped!
return ((this->m_positions == other.m_positions));
}
/**
* @brief operator!= Compare two objects
* @param other to be compared with
* @return True if NOT identical, otherwise false.
*/
bool operator!=(const PositionsTranslated &other) const
{
return !(*this == other);
}
protected:
void consolidate() const; // method is const
private:
#ifdef WITH_LOGGING
LOGGER_T m_logger; ///< Interface of the Logging-Facility
#endif
PositionCont geographicToMap(const PositionCont &positions) const;
mutable PositionCont m_positions; // mutable to enable consolidate
mutable PositionCont m_singlePositions; // mutable to enable consolidate
mutable PositionCont m_positionsTranslated; // mutable to enable positionsTranslated
mutable QMutex m_mutex;
};
} // namespace SituationMap
@@ -0,0 +1,82 @@
/**
* @RDE_HEADER@
* CoordinateTranslation
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "coordinate_translation_thread.h"
#include <Map/coord_translation_functor.h>
#include <MapImpl/map_impl.h>
#include <MathUtils/simple_helper.h>
// ESRI
#include <arcgis/Point.h>
#include <arcgis/GeometryEngine.h>
// Qt
long SituationMap::CoordTranslationThread::m_counter = 0;
//----------------------------------------------------------------------------
namespace SituationMap
{
CoordTranslationThread::CoordTranslationThread(const QString &debugInfo)
: BaseClass()
, m_logger(getLogger("CoordTranslationThread"))
, m_debugInfo(debugInfo)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
" counter=" << m_counter;
++m_counter;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " counter=" << m_counter;
}
CoordTranslationThread::~CoordTranslationThread()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
" counter=" << m_counter;
--m_counter;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " counter=" << m_counter;
}
void CoordTranslationThread::run()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " userData=" << m_userData.toInt()
<< " counter=" << m_counter
<< " threadId: " << QThread::currentThreadId() ;
if (m_positions)
m_positions->positionsTranslated();
emit resultReady(m_userData);
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " userData=" << m_userData.toInt()
<< " counter=" << m_counter
<< " threadId: " << QThread::currentThreadId();
}
QVariant CoordTranslationThread::userData() const
{
return m_userData;
}
void CoordTranslationThread::setUserData(const QVariant &userData)
{
m_userData = userData;
}
} // namespace SituationMap
@@ -0,0 +1,79 @@
/**
* @RDE_HEADER@
* CoordinateTranslation
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "coordinate_translation.h"
#include <Common/situation_map_logging.h>
#include <situation_map_export.h>
// Qt
#include <QObject>
#include <QVariant>
#include <QThread>
// ESRI
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT CoordTranslationThread : public QThread
{
Q_OBJECT
using BaseClass = QThread;
public:
explicit CoordTranslationThread(const QString &debugInfo);
virtual ~CoordTranslationThread();
void setPositions(PositionsTranslated *positions)
{
m_positions = positions;
}
PositionsTranslated *positions() const
{
return m_positions;
}
QVariant userData() const;
void setUserData(const QVariant &userData);
signals:
void resultReady(const QVariant &userData);
protected:
void run() override;
private :
LOGGER_T m_logger; ///< Interface of the Logging-Facility
PositionsTranslated *m_positions = nullptr;
QString m_debugInfo;
QVariant m_userData;
static long m_counter;
};
} // namespace SituationMap
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,95 @@
/**
* @RDE_HEADER@
* GeometryModel - Renderer
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/geometry_model_renderer.h>
#include <Map/situation_map_types.h>
#include <Common/situation_map_logging.h>
#include "situation_map_export.h"
// Qt
#include <QtCore/qmap.h>
namespace SituationMap
{
class Map;
class AbstractMultiPointMapObject;
class RenderMapObjectsVisitor;
class SITUATIONMAP_EXPORT GeometryModelRendererEsri : public GeometryModelRenderer
{
Q_OBJECT
private:
friend class MapImpl;
explicit GeometryModelRendererEsri(Map *map, const QString &layerName, GeometryModel &geometryModel, QObject *parent = nullptr);
~GeometryModelRendererEsri() = default;
public:
bool render() override;
bool renderNode(const GeometryModel::Node &node) override;
bool render(QPixmap &drawable) override;
GeometryModel::Node getAssociatedNode(const AbstractMapObject *mapObject) override;
bool getNodesPickable() const override;
void setNodesPickable(bool nodesPickable) override;
bool editorMode() const override;
void setEditorMode(bool on) override;
UniqueMapId getIdFromNode(const GeometryModel::Node &node) const override;
protected:
friend class RenderMapObjectsVisitor;
bool drawNodesRecursive(const GeometryModel::Node &);
void drawPolyline(const GeometryModel::Node &, const QString &layer, const QList<QPointF> &points, SituationMap::AbstractMapObject *mapObj, bool geoCoordinates = true);
void drawParticipantSymbol( const GeometryModel::Node &, const QString &layer, SituationMap::AbstractMapObject *mapObj );
void drawDictionarySymbol ( const GeometryModel::Node &, const QString &layer, SituationMap::AbstractMapObject *mapObj );
void drawEllipse ( const GeometryModel::Node &, const QString &layer, SituationMap::AbstractMapObject *mapObj );
void drawLine ( const GeometryModel::Node &, const QString &layer, SituationMap::AbstractMapObject *mapObj );
void drawPolyline ( const GeometryModel::Node &, const QString &layer, SituationMap::AbstractMapObject *mapObj );
void drawRectangle ( const GeometryModel::Node &, const QString &layer, SituationMap::AbstractMapObject *mapObj );
void drawSpline ( const GeometryModel::Node &, const QString &layer, SituationMap::AbstractMapObject *mapObj );
void drawSymbol ( const GeometryModel::Node &, const QString &layer, SituationMap::AbstractMapObject *mapObj );
void drawText ( const GeometryModel::Node &, const QString &layer, SituationMap::AbstractMapObject *mapObj );
template<class MapObjectClass> uint setLineStyle(const GeometryModel::Node &p, MapObjectClass *objPtr) const;
template<class MapObjectClass> void setFillStyle(const GeometryModel::Node &p, MapObjectClass *objPtr) const;
bool m_editorMode;
QTimer *m_delayedRenderTimer;
QSet<UniqueMapId> m_delayedRenderIds;
protected slots:
void onDelayedRenderNodes();
void onCleanupMapObjects(const SituationMap::GeometryModel::Node &node);
void onRemoveMapObject(int uid);
void onUpdate(const SituationMap::GeometryModel::Node &node, SituationMap::GeometryModel::Attribute att, bool coordsChanged);
}; // GeometryModelRendererEsri
}
+220
View File
@@ -0,0 +1,220 @@
/**
* @RDE_HEADER@
* Interface-Implementation of Grid
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "grid_impl.h"
#include "map_impl.h"
// ESRI
#include <arcgis/arcgisversion.h> // This file is created by RME, not ESRI
#include <arcgis/Map.h>
#include <arcgis/MapGraphicsView.h>
#include <arcgis/LatitudeLongitudeGrid.h>
#include <arcgis/MGRSGrid.h>
#include <arcgis/UTMGrid.h>
#include <arcgis/USNGGrid.h>
#include <arcgis/SimpleLineSymbol.h>
#include <arcgis/TextSymbol.h>
// Qt
namespace SituationMap
{
class GridImpl::Private
{
public:
Private() = default;
~Private() = default;
// ESRI data:
Esri::ArcGISRuntime::Grid *m_esriGrid = nullptr;
// Own data:
SituationMap::MapImpl *m_mapImpl = nullptr;
}; // class GridImpl::Private
//-----------------------------------------------------------------------------
GridImpl::GridImpl(SituationMap::MapImpl *mapImpl)
: m_logger(getLogger("GridImpl"))
, d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
d->m_mapImpl = mapImpl;
if (d->m_mapImpl && d->m_mapImpl->getEsriMapGraphicsView())
d->m_esriGrid = d->m_mapImpl->getEsriMapGraphicsView()->grid();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
GridImpl::~GridImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
GridType GridImpl::gridType()
{
GridType curType = GridType::NoType;
if (d->m_esriGrid)
{
Esri::ArcGISRuntime::GridType esriGridType = d->m_esriGrid->gridType();
switch (esriGridType)
{
case Esri::ArcGISRuntime::GridType::Unknown:
curType = GridType::NoType;
break;
case Esri::ArcGISRuntime::GridType::LatitudeLongitudeGrid:
curType = GridType::LatLon;
break;
case Esri::ArcGISRuntime::GridType::MGRSGrid:
curType = GridType::MGRS;
break;
case Esri::ArcGISRuntime::GridType::UTMGrid:
curType = GridType::UTM;
break;
case Esri::ArcGISRuntime::GridType::USNGGrid:
curType = GridType::USGN;
break;
} // switch
}
else
DEBUG_LOG(m_logger) << LOGFUNCTION << " no object";
return curType;
}
void GridImpl::setGridType(GridType gridType)
{
// change?
if (this->gridType() == gridType)
return;
if (d->m_esriGrid)
d->m_esriGrid->deleteLater();
if (! d->m_mapImpl->getEsriMapGraphicsView())
return;
QObject *parent = d->m_mapImpl->getEsriMapGraphicsView();
switch (gridType)
{
case GridType::NoType:
d->m_esriGrid = nullptr;
break;
case GridType::LatLon:
d->m_esriGrid = new Esri::ArcGISRuntime::LatitudeLongitudeGrid(parent);
break;
case GridType::MGRS:
d->m_esriGrid = new Esri::ArcGISRuntime::MGRSGrid(parent);
break;
case GridType::UTM:
d->m_esriGrid = new Esri::ArcGISRuntime::UTMGrid(parent);
break;
case GridType::USGN:
d->m_esriGrid = new Esri::ArcGISRuntime::USNGGrid(parent);
break;
} // switch
if (d->m_esriGrid)
{
d->m_esriGrid->setLabelPosition(Esri::ArcGISRuntime::GridLabelPosition::AllSides);
int levelCount = d->m_esriGrid->levelCount();
for (int i = 0; i < levelCount; ++i)
{
Esri::ArcGISRuntime::TextSymbol *gridLabel = static_cast<Esri::ArcGISRuntime::TextSymbol *>(d->m_esriGrid->textSymbol(i));
if (gridLabel)
{
gridLabel->setSize(10);
gridLabel->setHaloWidth(1.0);
}
const QColor col(0, 0, 0, 200);
Esri::ArcGISRuntime::SimpleLineSymbol sls(Esri::ArcGISRuntime::SimpleLineSymbolStyle::Solid,
col,
1);
d->m_esriGrid->setLineSymbol(i, &sls);
} // for
}
// call setGrid even if d->m_esriGrid is nullptr !!!
d->m_mapImpl->getEsriMapGraphicsView()->setGrid(d->m_esriGrid);
d->m_mapImpl->emitVisualComponentChanged(VisualComponent::Grid);
}
void GridImpl::setVisible(bool visible)
{
if (d->m_esriGrid)
{
if (d->m_esriGrid->isVisible() != visible)
{
d->m_esriGrid->setVisible(visible);
d->m_mapImpl->emitVisualComponentChanged(VisualComponent::Grid);
}
}
else
ERROR_LOG(m_logger) << LOGFUNCTION << " no object";
}
bool GridImpl::isVisible()
{
if (d->m_esriGrid)
return d->m_esriGrid->isVisible();
else
DEBUG_LOG(m_logger) << LOGFUNCTION << " no object";
return false;
}
int GridImpl::levelCount()
{
if (d->m_esriGrid)
return d->m_esriGrid->levelCount();
else
ERROR_LOG(m_logger) << LOGFUNCTION << " no object";
return -1;
}
bool GridImpl::isLabelVisible()
{
if (d->m_esriGrid)
return d->m_esriGrid->isLabelsVisible();
else
ERROR_LOG(m_logger) << LOGFUNCTION << " no object";
return false;
}
void GridImpl::setLabelVisible(bool visible)
{
if (d->m_esriGrid)
{
if (d->m_esriGrid->isLabelsVisible() != visible)
{
d->m_esriGrid->setLabelsVisible(visible);
d->m_mapImpl->emitVisualComponentChanged(VisualComponent::Grid);
}
}
else
ERROR_LOG(m_logger) << LOGFUNCTION << " no object";
}
} // namespace SituationMap
+61
View File
@@ -0,0 +1,61 @@
/**
* @RDE_HEADER@
* Interface-Implementation of Grid
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/grid.h> // Interface
#include <Common/situation_map_logging.h>
//----------------------------------------------------------------------------
namespace SituationMap
{
class MapImpl;
class GridImpl : public SituationMap::Grid
{
using BaseClass = SituationMap::Grid;
public:
explicit GridImpl(SituationMap::MapImpl *mapImpl);
virtual ~GridImpl();
GridImpl(const GridImpl &other) = delete;
GridImpl &operator=(GridImpl &other) = delete;
virtual GridType gridType() override;
virtual void setGridType(GridType gridType) override;
virtual void setVisible(bool visible) override;
virtual bool isVisible() override;
virtual int levelCount() override;
virtual bool isLabelVisible() override;
virtual void setLabelVisible(bool visible) override;
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
//----------------------------------------------------------------------------
+76
View File
@@ -0,0 +1,76 @@
/**
* @RDE_HEADER@
* CustomView for MapObjects
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, SLSE, 2017-04-11
*/
// Own
#include "jump_point_custom_view.h"
#include "ui_jump_point_custom_view.h"
#include "jump_point_model.h"
#include <Map/map.h>
// Ot
#include <QMenu>
#include <QBuffer>
#include <QDataWidgetMapper>
namespace SituationMap
{
JumpPointCustomView::JumpPointCustomView(QWidget *parent)
: BaseClass(parent)
, m_ui(new Ui::JumpPointCustomView)
, m_mapper(new QDataWidgetMapper(this))
{
m_ui->setupUi(this);
m_ui->image->setMenuEnabled(false); // no menu !
// // to keep toolTip up-to-date:
// connect(m_ui->image, &LtsWidgets::LtsImageEditor::imageChanged,
// this, &JumpPointCustomView::initToolTip);
}
JumpPointCustomView::~JumpPointCustomView()
{
delete m_ui;
}
bool JumpPointCustomView::init(const QModelIndex &idx)
{
if (!idx.isValid())
return false;
if (idx.model() != m_mapper->model())
{
// create mapping;
m_mapper->setModel(const_cast<QAbstractItemModel *>(idx.model()));
m_mapper->addMapping(m_ui->lbDescription, JumpPointModel::Columns::NAME, "text");
// m_mapper->addMapping(m_ui->image, JumpPointModel::Columns::ICON, "pixmap");
}
setRow(idx.row());
return true;
}
void JumpPointCustomView::setRow(int row)
{
m_mapper->setCurrentIndex(row);
// assume for now, that the relation between row and uniqueMapId will not change:
if (m_mapper && m_mapper->model())
{
QAbstractItemModel *modelPtr = m_mapper->model();
m_name = modelPtr->data(modelPtr->index(row, JumpPointModel::Columns::NAME)).toString();
QImage img = modelPtr->data(modelPtr->index(row, JumpPointModel::Columns::ICON)).value<QImage>();
m_ui->image->setPixmap(QPixmap::fromImage(img));
}
}
} // namespace SituationMap
+48
View File
@@ -0,0 +1,48 @@
/**
* @RDE_HEADER@
* CustomView for MapObjects
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, SLSE, 2017-04-11
*/
#pragma once
// Own
#include <Map/situation_map_types.h>
#include <LtsDataView/abstract_display_widget.h>
class QDataWidgetMapper;
namespace Ui
{
class JumpPointCustomView;
}
namespace SituationMap
{
class Map;
class JumpPointCustomView final : public LtsDataView::AbstractDisplayWidget
{
Q_OBJECT
using BaseClass = AbstractDisplayWidget;
public:
explicit JumpPointCustomView(QWidget *parent = nullptr);
~JumpPointCustomView();
bool init(const QModelIndex &idx) override;
protected:
void setRow(int row);
private:
Ui::JumpPointCustomView *m_ui;
QDataWidgetMapper *m_mapper = nullptr;
QString m_name;
};
} // SituationMap
+80
View File
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>JumpPointCustomView</class>
<widget class="QWidget" name="JumpPointCustomView">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>62</width>
<height>82</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string notr="true"/>
</property>
<widget class="LtsWidgets::LtsImageEditor" name="image" native="true">
<property name="geometry">
<rect>
<x>6</x>
<y>6</y>
<width>50</width>
<height>50</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>50</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>50</height>
</size>
</property>
</widget>
<widget class="QLabel" name="lbDescription">
<property name="geometry">
<rect>
<x>6</x>
<y>57</y>
<width>50</width>
<height>19</height>
</rect>
</property>
<property name="text">
<string notr="true">&lt;Desc&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsImageEditor</class>
<extends>QWidget</extends>
<header>LtsWidgets/lts_image_editor.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
+166
View File
@@ -0,0 +1,166 @@
/**
* @RDE_HEADER@
* Interface for JumpPointWidget
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "jump_point_model.h"
#include <Map/map.h>
#include <Map/object_factory.h>
#include <Map/situation_map_utils.h>
#include <MapImpl/layer_factory_impl.h>
#include <MapImpl/coord_translation_functor_impl.h>
// Qt
#include <QBuffer>
namespace SituationMap
{
//----------------------------------------------------------------------------
// JumpPointModel
//----------------------------------------------------------------------------
JumpPointModel::JumpPointModel(QObject *parent)
: BaseClass( {tr("Name"), tr("OriginGeo"), tr("Origin"), tr("Scale"), tr("Icon")}, parent)
{
}
void JumpPointModel::setMap(Map *map)
{
m_map = map;
}
QVariant JumpPointModel::data(const QModelIndex &index, int role) const
{
if ((role != Qt::DisplayRole && role != Qt::ToolTipRole && role != Qt::EditRole) || !indexIsValid(index))
return QVariant();
const JPData &data = m_vecData.at(index.row());
if (role==Qt::ToolTipRole)
{
QString tooltip;
tooltip.append(QStringLiteral("<html>"));
const QImage &img = data.m_image;
if (! img.isNull())
{
// without a temporary file:
QByteArray baData;
QBuffer buffer(&baData);
img.save(&buffer, "PNG", 100);
tooltip.append(QStringLiteral("<center><img src='data:image/png;base64,%1'></center>").arg(QString::fromLatin1(baData.toBase64()))); // how can i center the image?
tooltip.append(QLatin1String("<br />"));
}
tooltip.append(QStringLiteral("<p>name: %1</p>").arg(data.m_name));
tooltip.append(QStringLiteral("<p>origin: %1</p>").arg(m_map->geoToMgrsString(data.m_originGeo.toPointF())));
tooltip.append(QStringLiteral("<p>scale: %1</p>").arg(data.m_mapScale));
tooltip.append(QStringLiteral("</html>"));
return tooltip;
}
switch (index.column())
{
case NAME:
return data.m_name;
case ORIGIN_GEO:
return data.m_originGeo.toPointF();
case ORIGIN_MGRS:
{
if (m_map)
return m_map->geoToMgrsString(data.m_originGeo.toPointF());
return QString();
}
case SCALE:
return data.m_mapScale;
case ICON:
return data.m_image;
}
return QVariant();
}
void JumpPointModel::addUpdateItem(const JPData &jpData)
{
int row = rowOfItem(jpData.m_name);
if (row >= 0)
setRowData(row, jpData); // UPDATE
else
insertRow(jpData); // APPEND
}
bool JumpPointModel::removeItem(const QString &name)
{
int row = rowOfItem(name);
if (row < 0)
return false;
removeRow(row);
return true;
}
bool JumpPointModel::contains(const QString &name)
{
return rowOfItem(name) >= 0;
}
int JumpPointModel::rowOfItem(const QString &name)
{
int row = -1;
for (const JPData &item : qAsConst(m_vecData))
{
++row;
if (item.m_name.compare(name) == 0)
return row;
}
return -1;
}
JPData *JumpPointModel::getItemPtr(const QModelIndex &index)
{
if (!indexIsValid(index))
return nullptr;
int row = index.row();
if (row < 0 || row > m_vecData.size())
return nullptr;
return &m_vecData[row];
}
JPData *JumpPointModel::getItemPtr(const QString &name)
{
int row = rowOfItem(name);
if (row < 0 || row > m_vecData.size())
return nullptr;
return &m_vecData[row];
}
QVector<JPData> JumpPointModel::jumpPoints()
{
return m_vecData;
}
QStringList JumpPointModel::names()
{
QStringList sl;
sl.reserve(m_vecData.size());
for (const JPData &item : qAsConst(m_vecData))
sl.append(item.m_name);
return sl;
}
} // namespace SituationMap
+105
View File
@@ -0,0 +1,105 @@
/**
* @RDE_HEADER@
* Interface
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "situation_map_export.h"
#include <MapObjects/geo_image.h>
#include <Common/ref_pointer.h>
#include <Common/situation_map_logging.h>
#include <LtsWidgets/lts_abstract_table_model.h>
// Qt
//----------------------------------------------------------------------------
namespace SituationMap
{
//----------------------------------------------------------------------------
// JPData
//----------------------------------------------------------------------------
class JPData
{
public:
JPData() = default;
explicit JPData(const QString &name, const Point3D &originGeo, double mapScale = 0.0)
: m_name(name), m_originGeo(originGeo), m_mapScale(mapScale)
{}
~JPData()
{
if (m_marker)
m_marker->removeMySelf();
}
bool hasMarker() const
{
return m_marker.valid();
}
QString m_name;
Point3D m_originGeo;
double m_mapScale = 0.0;
RefPointer<GeoImage> m_marker;
QImage m_image;
};
//----------------------------------------------------------------------------
// JumpPointModel
//----------------------------------------------------------------------------
class JumpPointModel: public LtsWidgets::LtsAbstractTableModel<JPData>
{
Q_OBJECT
using BaseClass = LtsWidgets::LtsAbstractTableModel<JPData>;
public:
enum Columns
{
NAME,
ORIGIN_GEO,
ORIGIN_MGRS,
SCALE,
ICON,
};
explicit JumpPointModel(QObject *parent = nullptr);
void setMap(Map *map);
QVariant data(const QModelIndex &index, int role) const override final;
void addUpdateItem(const JPData& jpData);
bool removeItem(const QString &name);
bool contains(const QString &name);
int rowOfItem(const QString &name);
JPData* getItemPtr(const QModelIndex &index);
JPData* getItemPtr(const QString &name);
QVector<JPData> jumpPoints();
QStringList names();
private:
Map *m_map = nullptr;
};
} // namespace SituationMap
+41
View File
@@ -0,0 +1,41 @@
/**
* @RDE_HEADER@
* Declaration/Definition
* $Id$
* $HeadURL$
* $Author$
*/
#include "jump_point_namer.h"
#include "ui_jump_point_namer.h"
namespace SituationMap
{
JumpPointNamer::JumpPointNamer(int count, const QPointF &coordGeo, QWidget *parent)
: LtsWidgets::LtsFramelessDialog(parent, LtsWidgets::Behaviour::Modal)
, m_ui(new Ui::JumpPointNamer)
, m_coord(coordGeo)
{
m_ui->setupUi(this);
m_ui->leName->setText(QStringLiteral("JP_%1").arg(count));
#ifdef TABLET_MODE
m_ui->leName->setFocusPolicy(Qt::ClickFocus);
#endif
connect(m_ui->ltsDialogButtonBox->getButton(LtsWidgets::DialogButton::Close), &QPushButton::clicked, this, &JumpPointNamer::close);
connect(m_ui->ltsDialogButtonBox->getButton(LtsWidgets::DialogButton::Ok), &QPushButton::clicked, this, &JumpPointNamer::onOk);
}
JumpPointNamer::~JumpPointNamer()
{
delete m_ui;
}
void JumpPointNamer::onOk()
{
if (m_ui->leName->text().size() > 0)
{
emit newName(m_ui->leName->text(), m_coord);
close();
}
}
}
+39
View File
@@ -0,0 +1,39 @@
/**
* @RDE_HEADER@
* Declaration/Definition
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <LtsWidgets/lts_frameless_dialog.h>
namespace Ui {
class JumpPointNamer;
}
namespace SituationMap
{
class JumpPointNamer : public LtsWidgets::LtsFramelessDialog
{
Q_OBJECT
public:
explicit JumpPointNamer(int count, const QPointF &coordGeo, QWidget *parent = nullptr);
~JumpPointNamer();
signals:
void newName(const QString &name, const QPointF &coordGeo);
private:
void onOk();
private:
Ui::JumpPointNamer *m_ui;
QPointF m_coord;
};
}
+46
View File
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>JumpPointNamer</class>
<widget class="QWidget" name="JumpPointNamer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>224</width>
<height>82</height>
</rect>
</property>
<property name="windowTitle">
<string>Set Jump Point Name</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="leName"/>
</item>
</layout>
</item>
<item>
<widget class="LtsWidgets::LtsDialogButtonBox" name="ltsDialogButtonBox"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LtsWidgets::LtsDialogButtonBox</class>
<extends>QWidget</extends>
<header>LtsWidgets/lts_dialog_button_box.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
+445
View File
@@ -0,0 +1,445 @@
/**
* @RDE_HEADER@
* Interface for JumpPointWidget
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "jump_point_widget.h"
#include "jump_point_custom_view.h"
#include <Map/map.h>
#include <Map/object_factory.h>
#include <Map/situation_map_utils.h>
#include <MapImpl/layer_factory_impl.h>
#include <MapImpl/jump_point_namer.h>
#include <MapImpl/coord_translation_functor_impl.h>
#include <GraphicalObjects/graphical_object_picture_marker.h>
#include <Rubberbands/rubberband_factory.h>
#include <MapObjects/geo_image.h>
#include <LtsDataView/lts_data_view.h>
// Qt
#include <QCoreApplication>
#include <QHBoxLayout>
#include <QPushButton>
#include <QMenu>
#include <QAction>
#include <QFocusEvent>
namespace SituationMap
{
//-----------------------------------------------------------------------------
// class JumpPointWidget::Private
//-----------------------------------------------------------------------------
class JumpPointWidget::Private
{
public:
Private() = default;
// OWN data:
SituationMap::Map *m_map = nullptr; // Use INTERFACE
QString m_layername;
QString m_debugInfo;
RefPointer<GeoImage> m_uniqueMarker;
QGridLayout *m_layout = nullptr;
JumpPointModel *m_jumpPointModel = nullptr;
LtsDataView::LtsDataView *m_jumpPointView = nullptr;
QMenu *m_actionMenu = nullptr;
QAction *m_removeAction = nullptr;
QModelIndex m_contextModelIndex;
}; // class JumpPointWidget::Private
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// class JumpPointWidget
//-----------------------------------------------------------------------------
JumpPointWidget::JumpPointWidget(const QString &layername,
Map *map,
const QString &debugInfo)
: BaseClass()
, d(new Private)
, m_logger(getLogger("JumpPointWidget"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " debugInfo='" << debugInfo << "'";
d->m_jumpPointModel = new JumpPointModel(this);
if (map)
{
d->m_map = map;
d->m_layername = layername;
d->m_debugInfo = debugInfo;
connect(d->m_map, &Map::beginDestroy, this, &JumpPointWidget::cleanup);
d->m_jumpPointModel->setMap(d->m_map);
QVBoxLayout *vBox = new QVBoxLayout();
this->setLayout(vBox);
QHBoxLayout *hBox = new QHBoxLayout();
vBox->addLayout(hBox);
hBox->addWidget(new QLabel(tr("Jump Points:")));
QPushButton *pbPosition = new QPushButton(QIcon(QLatin1String("icon:position.svg")), tr("Position"));
hBox->addWidget(pbPosition);
connect(pbPosition, &QPushButton::clicked, this, &JumpPointWidget::onGetPosition);
pbPosition->setEnabled(d->m_map->mode() == Mode::Mode_2D);
d->m_jumpPointView = new LtsDataView::LtsDataView(this);
vBox->addWidget(d->m_jumpPointView);
d->m_jumpPointView->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection);
d->m_jumpPointView->selectCustomView();
d->m_jumpPointView->seamless();
d->m_jumpPointView->setModel(d->m_jumpPointModel, [this]()
{
return new JumpPointCustomView(this);
});
// connect signals of TableView:
connect(d->m_jumpPointView, &LtsDataView::LtsDataView::signalEntered, this, &JumpPointWidget::onTableViewClicked);
connect(d->m_jumpPointView, &LtsDataView::LtsDataView::clicked, this, &JumpPointWidget::onTableViewSelectionChanged);
d->m_jumpPointView->hideColumn(JumpPointModel::Columns::ORIGIN_GEO); // do not "print"
d->m_jumpPointView->hideColumn(JumpPointModel::Columns::ICON); // do not "print"
}
else
ERROR_LOG(m_logger) << LOGFUNCTION << " no map";
// context menue:
d->m_jumpPointView->setContextMenuPolicyPerView(LtsDataView::LtsDataView::CUSTOM, Qt::CustomContextMenu);
connect(d->m_jumpPointView, &LtsDataView::LtsDataView::signalContextMenuRequested,
this, &JumpPointWidget::onCustomContextMenuRequested);
d->m_actionMenu = new QMenu(this);
d->m_removeAction = new QAction(tr("Remove Jump Point"), this);
d->m_actionMenu->addAction(d->m_removeAction);
connect(d->m_removeAction, &QAction::triggered, this, &JumpPointWidget::onRemoveAction);
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END";
}
JumpPointWidget::~JumpPointWidget()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START";
this->cleanup();
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END";
delete d;
}
QStringList JumpPointWidget::names()
{
if (!d->m_jumpPointModel)
return QStringList();
return d->m_jumpPointModel->names();
}
QVector<JPData> JumpPointWidget::jumpPoints()
{
if (!d->m_jumpPointModel)
return QVector<JPData>();
return d->m_jumpPointModel->jumpPoints();
}
int JumpPointWidget::count() const
{
if (!d->m_jumpPointModel)
return 0;
return d->m_jumpPointModel->rowCount();
}
bool JumpPointWidget::contains(const QString &name)
{
if (!d->m_jumpPointModel)
return false;
return d->m_jumpPointModel->contains(name);
}
bool JumpPointWidget::removeJumpPoint(const QString &name)
{
return d->m_jumpPointModel->removeItem(name);
}
void JumpPointWidget::onGetPosition()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START";
if (!d->m_map)
return;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Workaround to make sure that cursor setting of rubberband will work. Don't know why this
* is necessary. ToDo find the reason why changing cursor failed and fix the underlying cause.
*
* JT 2017/05/04
*/
d->m_map->sideBar()->hide();
// QCoreApplication::process Events(); // 2017.11.03, N.Thoden, not necessary
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
// remove any pending/active rubberbands of our map:
SituationMap::RB::Rubberband::deleteAllActiveRubberbands(d->m_map);
// create rb
RB::RubberbandPointFeature *rb = d->m_map->rubberbandFactory().create<RB::RubberbandPointFeature>(this);
if (!rb || !rb->rubberbandIsAlive())
{
if (rb)
rb->deleteMySelf();
FATAL_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " create Rubberband failed!";
return;
}
rb->setAutoDelete(true);
connect(rb, &RB::Rubberband::apply, this, &JumpPointWidget::onRubberbandApply);
}
void JumpPointWidget::onRubberbandApply(RB::RubberbandBase *rb)
{
QPolygonF coordGeo = rb->coordinateVector();
if (coordGeo.size() != 1)
return;
connect(this, &JumpPointWidget::newName, this, &JumpPointWidget::onName);
askUserForName(coordGeo.front());
}
void JumpPointWidget::askUserForName(const QPointF &coordGeo)
{
JumpPointNamer *namer = new JumpPointNamer(this->count(), coordGeo, this);
namer->setAttribute(Qt::WA_DeleteOnClose);
namer->connect(namer, &JumpPointNamer::newName, this, &JumpPointWidget::newName);
namer->show();
}
void JumpPointWidget::onName(const QString &name, const QPointF & coordGeo)
{
disconnect(this, &JumpPointWidget::newName, this, &JumpPointWidget::onName);
const double scale = d->m_map->scale();
addJumpPoint(name, coordGeo, scale);
}
void JumpPointWidget::onJumpPointHovered(const QString &name, bool enter)
{
setJumpPointSelected(name, enter);
}
void JumpPointWidget::onTableViewClicked(const QModelIndex &index)
{
if (!d->m_jumpPointModel)
return;
JPData *jumpPoint = d->m_jumpPointModel->getItemPtr(index);
if (!jumpPoint)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " not found";
return;
}
removeJumpPointSelections();
onJumpPointHovered(jumpPoint->m_name, true);
}
void JumpPointWidget::onTableViewSelectionChanged(const QModelIndex &index)
{
if (!d->m_jumpPointModel)
return;
JPData *jumpPoint = d->m_jumpPointModel->getItemPtr(index);
if (!jumpPoint)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " not found";
return;
}
centerMapToJumpPoint(jumpPoint->m_name);
}
bool JumpPointWidget::addJumpPoint(const QString &name, const QPointF &originGeo, double mapScale)
{
return addJumpPoint(name, Point3D::fromQPointF(originGeo), mapScale);
}
bool JumpPointWidget::addJumpPoint(const QString &name, const Point3D &originGeo, double mapScale)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"
<< " name='" << name << "'"
<< " originGeo=" << originGeo
<< " mapScale=" << mapScale;
if (d->m_layername.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no layername";
return false;
}
{
JPData jpData(name, originGeo, mapScale);
jpData.m_image = QImage(QLatin1String(":/situationMap/pushPinRedClipArt.png")).scaled(30, 30, Qt::KeepAspectRatio, Qt::SmoothTransformation);
d->m_jumpPointModel->addUpdateItem(jpData);
}
JPData *jumpPoint = d->m_jumpPointModel->getItemPtr(name);
if (!jumpPoint)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " not found";
return false;
}
if (! jumpPoint->hasMarker())
{
QString debugInfo = QStringLiteral("%1_JumpPoint(%2)").arg(d->m_debugInfo, name);
jumpPoint->m_marker = d->m_map->objectFactory().create<SituationMap::GeoImage>(d->m_layername, debugInfo);
if (! jumpPoint->m_marker.valid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " create GeoImage failed, debugInfo='" << d->m_debugInfo << "'";
return false;
}
jumpPoint->m_marker->setProxyMarker(MarkerSymbolType::PushPin2D);
jumpPoint->m_marker->setPickable(false);
}
jumpPoint->m_mapScale = mapScale;
jumpPoint->m_originGeo = originGeo;
jumpPoint->m_marker->setOrigin(originGeo);
jumpPoint->m_marker->setIdentifier(name);
this->setJumpPointSelected(name, false);
bool success = jumpPoint->m_marker->updateView();
if (! success)
ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed, debugInfo='" << d->m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'"
<< " END"
<< " name='" << name << "'"
<< " originGeo=" << originGeo
<< " mapScale=" << mapScale;
return success;
}
bool JumpPointWidget::centerMapToJumpPoint(const QString &name)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"
<< " name='" << name << "'";
JPData *jumpPoint = d->m_jumpPointModel->getItemPtr(name);
if (!jumpPoint)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " not found";
return false;
}
const Point3D &originGeo = jumpPoint->m_originGeo;
const double mapScale = jumpPoint->m_mapScale;
d->m_map->setCenterPoint(originGeo.toPointF(), mapScale);
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END";
return true;
}
void JumpPointWidget::removeJumpPointSelections()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START";
if (!d->m_jumpPointModel)
return;
const QStringList &sl = d->m_jumpPointModel->names();
for (const auto &name : sl)
setJumpPointSelected(name, false);
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END";
}
bool JumpPointWidget::setJumpPointSelected(const QString &name, bool selected)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"
<< " name='" << name << "'"
<< " selected='" << selected;
JPData *jumpPoint = d->m_jumpPointModel->getItemPtr(name);
if (!jumpPoint)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " not found";
return false;
}
if (! jumpPoint->hasMarker())
return false;
// jumpPoint->m_marker->setSelected(selected);
// jumpPoint->m_marker->setScale((selected) ? 1.5 : 1.0);
jumpPoint->m_marker->setProxyMarkerSize((selected) ? 30 : 20);
jumpPoint->m_marker->setDrawOrder((selected) ? 2 : 1); // selected should be above the other
jumpPoint->m_marker->rTextStyle().setSize((selected) ? 20 : 10);
bool success = jumpPoint->m_marker->updateView();
if (! success)
ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed, debugInfo='" << d->m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"
<< " name='" << name << "'"
<< " selected='" << selected;
return success;
}
void JumpPointWidget::onCustomContextMenuRequested(const QPoint &globalPos, const QModelIndex &indexSrc)
{
d->m_contextModelIndex = indexSrc;
if (!d->m_contextModelIndex.isValid())
return;
d->m_actionMenu->exec(globalPos);
}
void JumpPointWidget::onRemoveAction()
{
if (!d->m_contextModelIndex.isValid())
return;
d->m_jumpPointModel->removeRow(d->m_contextModelIndex.row());
}
void JumpPointWidget::cleanup()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START";
d->m_jumpPointModel->clear();
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END";
}
bool JumpPointWidget::event(QEvent *event)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"
<< " type " << event->type();
if (event->type() == QEvent::Type::Hide)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'"
<< " hide Event -> removeJumpPointSelections...";
this->removeJumpPointSelections();
}
return BaseClass::event(event);
}
} // namespace SituationMap
+103
View File
@@ -0,0 +1,103 @@
/**
* @RDE_HEADER@
* Interface
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "jump_point_model.h"
#include "situation_map_export.h"
#include <Common/ref_pointer.h>
#include <Common/situation_map_logging.h>
#include <Map/situation_map_types.h>
//
#include <MapObjects/geo_image.h>
#include <LtsWidgets/lts_abstract_table_model.h>
// Qt
#include <QWidget>
//----------------------------------------------------------------------------
namespace SituationMap
{
//Forward:
class Map;
namespace RB
{
class RubberbandBase;
}
//----------------------------------------------------------------------------
// JumpPointWidget
//----------------------------------------------------------------------------
class SITUATIONMAP_EXPORT JumpPointWidget : public QWidget
{
Q_OBJECT
using BaseClass = QWidget;
public:
explicit JumpPointWidget(const QString &layername,
Map *map,
const QString &debugInfo);
virtual ~JumpPointWidget();
QStringList names();
QVector<JPData> jumpPoints();
int count() const;
bool contains(const QString &name);
signals:
void newName(const QString &name, const QPointF &coordGeo);
public Q_SLOTS:
void onGetPosition();
void onRubberbandApply(RB::RubberbandBase *rb);
void askUserForName(const QPointF &coordGeo);
void onName(const QString &name, const QPointF & coordGeo);
void onJumpPointHovered(const QString &name, bool enter);
void onTableViewClicked(const QModelIndex &index);
void onTableViewSelectionChanged(const QModelIndex &index);
bool addJumpPoint(const QString &name, const Point3D &originGeo, double mapScale = -1);
bool addJumpPoint(const QString &name, const QPointF &originGeo, double mapScale = -1);
bool centerMapToJumpPoint(const QString &name);
bool removeJumpPoint(const QString &name);
void removeJumpPointSelections();
bool setJumpPointSelected(const QString &name, bool selected = true);
void onCustomContextMenuRequested(const QPoint &globalPos, const QModelIndex &indexSrc);
void onRemoveAction();
void cleanup();
protected:
bool event(QEvent *event) override;
private:
class Private;
/**
* The private d-pointer to not break BC when changing internals of this class
*/
Private *d = nullptr;
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
} // namespace SituationMap
File diff suppressed because it is too large. Load diff
+125
View File
@@ -0,0 +1,125 @@
/**
* @RDE_HEADER@
* Interface
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "situation_map_export.h"
#include <Common/situation_map_logging.h>
// Qt
#include <QSlider>
//----------------------------------------------------------------------------
namespace SituationMap
{
//Forward:
class Map;
class LayerParameter;
class mySlider : public QSlider
{
Q_OBJECT
public:
mySlider(const QString &layername, int min, int max)
: QSlider(), m_layername(layername)
{
this->setOrientation(Qt::Horizontal);
setMinimum(min);
setMaximum(max);
}
public:
QString m_layername;
};
class SITUATIONMAP_EXPORT LayerControlWidget : public QWidget
{
Q_OBJECT
public:
explicit LayerControlWidget(QWidget *parent,
Map *map,
bool enableLayer,
bool enableBasemap,
const QString &debugInfo);
~LayerControlWidget();
bool updatePanel();
private:
bool updatePanelZoomRotationScales();
bool updatePanelLayer();
bool updatePanelBasemap();
QString generateToolTip(const QString &layername, const LayerParameter &lp);
QSlider *generateZoomSlider();
QSlider *generateRotationSlider();
QSlider *generateSymbolSizeSlider();
private slots:
protected:
void dragMoveEvent(QDragMoveEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void dropEvent(QDropEvent *event) override;
private slots:
void expandCollapse(bool expanded);
void layerOnOff();
void opacity(int);
void brightness(int);
void contrast(int);
void gamma(int);
void onLayerParamterChanged(const QString &layername,
const LayerParameter &newLayerParameter);
void onZoomSliderInit(QSlider *slider);
void onZoomSliderValueChanged();
void onRotationSliderValueChanged();
void onRotationOfMapChanged(double rotation);
void onSymbolSizeSliderValueChanged();
void onSymbolSizeSliderValueChangedTimeout();
void onSymbolSizeOfMapChanged(double symbolSize);
void onBaseMapSelection();
// slots for external signals:
void onMapReady();
void onMapExtentChanged();
private:
QString layernameAt(const QPoint &pos) const;
QStringList newLayerOrder(const QString &draggedLayername,
const QString &childLayername);
class Private;
/**
* The private d-pointer to not break BC when changing internals of this class
*/
Private *d = nullptr;
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
} // namespace SituationMap
File diff suppressed because it is too large. Load diff
+324
View File
@@ -0,0 +1,324 @@
/**
* @RDE_HEADER@
* Interface-Implementation of LayerFactory
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "map_layer.h"
#include "logical_layer.h"
#include <Map/layer_factory.h>
#include <Map/situation_map_types.h>
#include <Map/party_id_to_color_functor.h>
#include <Common/situation_map_logging.h>
// ESRI
#include <arcgis/Error.h>
#include <arcgis/Surface.h>
// Qt
#include <QObject>
// Forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Layer;
class GraphicsOverlay;
class RasterElevationSource;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
class MapImpl;
/**
* @brief The LayerFactoryImpl
*
*
*/
// inherent 'public', to get that implementation
// inherent 'public virtual' to get that interface
class LayerFactoryImpl : public QObject, public virtual LayerFactory
{
Q_OBJECT
using BaseClass = QObject;
public:
/*! \name Construction, Destruction */
//@{
explicit LayerFactoryImpl(QObject *parent, SituationMap::MapImpl *mapImpl);
virtual ~LayerFactoryImpl();
LayerFactoryImpl(const LayerFactoryImpl &other) = delete;
LayerFactoryImpl &operator=(const LayerFactoryImpl &other) = delete;
//@}
/*! \name Basemap Layer */
//@{
bool initializeBasemapBuiltIn(const QString &tpkFile) override;
bool isUsingBaseMapBuiltIn() const override;
QString basemapLayernamePrefix() const override;
void setBasemapLayernamePrefix(const QString &basemapLayernamePrefix) override;
QString participantLayername() const override;
RenderingMode participantLayerRenderingMode() const override;
SurfacePlacement participantLayerSurfacePlacement() const override;
void setParticipantLayer(const QString &participantLayername,
RenderingMode renderingMode,
SurfacePlacement surfacePlacement) override;
bool addBasemap(const QString &layername,
const QUrl &mapServiceUrl,
const BasemapType basemapType,
const double minScale = -1,
const double maxScale = -1,
const QString &displayName = QString(),
const QString &description = QString(),
const QImage &previewImage = QImage()) override;
bool removeBasemap(const QString &layername) override;
Esri::ArcGISRuntime::Envelope fullExtentOfBasemaps(double margin_zeroToOne = 0.0) const;
//@}
/*! \name Scene Object Layer */
//@{
bool addSceneObjectLayer(const QString &layername,
const QUrl &sceneLayerServiceUrl,
const double altitudeOffset = 0,
const QString &displayName = QString(),
const QString &description = QString(),
const QImage &previewImage = QImage()) override;
bool isSceneObjectLayerVisible() const override;
void setSceneObjectLayerVisible(bool visible) override;
//@}
/*! \name Elevation */
//@{
bool initializeElevationSources(const QStringList &sceneElevationServiceUrls) override;
//@}
/*! \name Operational Layer */
//@{
static void setForceCreationOfGraphicsOverlay(bool onOff); // to deal with esri bug
bool addOperationalLayer(const QString &logicalLayerName,
const QString &displayName,
RenderingMode renderMode = RenderingMode::Dynamic,
SurfacePlacement surfacePlacement = SurfacePlacement::Relative) override;
bool removeOperationalLayer(const QString &logicalLayerName) override;
bool hasOperationalLayer(const QString &logicalLayerName) const override;
QString layernameForCubes() const override;
void setLayernameForCubes(const QString &logicalName) override;
bool hasLayernameForCubes() const override;
bool assignUniqueValueRendererForCubes() override;
bool assignUniqueValueRenderer(const QString &layername,
const QString &fieldName,
const QVector<UniqueValueData> &uvDataCont) override;
// For BoobyTraps
bool assignUniqueValueRendererAesCentral(const QString &layername,
const QStringList &fieldNames,
const QVector<UniqueValueDataAesCentral> &uvDataCont) override;
// For AES / 3D Extrusion
bool assignUniqueValueRendererAesCentral3DExtrusion(const QString &layername,
const QStringList &fieldNames,
const QVector<UniqueValueDataAesCentral> &uvDataCont) override;
bool initializeUnitTestLayer() override;
//@}
/*! \name Overlay Layer */
//@{
bool addOverlayLayer(const QString &logicalLayerName, const QString &displayName, bool isSnaphot = false) override;
QString technicalNameOfOverlayLayer(const QString &logicalLayerName);
bool removeOverlayLayer(const QString &logicalLayerName) override;
QString snapshotOverlayLayername() const override;
//@}
/*! \name Snapshot (overlay) Layer */
//@{
QString snapshotLayer();
bool snapshotLayerExists() const;
//@}
/*! \name JumpPoint Layer */
//@{
QString jumpPointLayer();
bool jumpPointLayerExists() const;
//@}
/*! \name Tool Layer */
//@{
QString toolLayer() override; // Tools: Viewshed etc.
bool toolLayerExists() const; // Tools: Viewshed etc.
//@}
/*! \name Rubberband Layer */
//@{
QString rubberbandLayer() const override;
bool rubberbandLayerExists() const;
//@}
/*! \name EmergencyCall Layer (at least relevant for GUEZ) */
//@{
QString emergencyCallLayer(bool getJustTheName = false) override;
bool emergencyCallLayerExists() const override;
bool switchEmergencyCallLayerOn() override;
bool switchEmergencyCallLayerOff() override;
void handleEmergencyCallSpecials();
//@}
/*! \name Common */
//@{
Map *map() const override;
int count() const override;
QStringList layernames() const override;
QStringList layernames(const LayerType &layerType) const override;
bool checkLayerOrder(const QStringList &orderedLayernames) override;
bool orderLayerTo(const QStringList &orderedLayernames) override;
LayerType layerType(const QString &layername) const override;
LayerParameter layerParameter(const QString &layername) const override;
bool setLayerParameter(const QString &layername, const LayerParameter &layerParameter) override;
bool setLayerOpacity(LayerType layerType, int opacity) override;
bool setLayerBrightness(LayerType layerType, int brightness) override;
bool setLayerContrast(LayerType layerType, int contrast) override;
bool setLayerGamma(LayerType layerType, int gamma) override;
bool setLayerParameter(LayerType layerType, int value, int LayerParameter::*memberPtr);
void setLayersVisible(const QVector<LayerType> &layerTypes, bool visible) override;
//@}
#if ACRGIS_VERSION < 0x640500 // 100.5.0 / Update 5
/*! \name Selection */
//@{
QColor selectionColor() const override;
void setSelectionColor(const QColor &selectionColor) override;
//@}
#endif // ACRGIS_VERSION
/*! \name Elevation */
//@{
Esri::ArcGISRuntime::Surface *baseSurface() const;
bool elevationAvailable() const override;
int elevationSourcesCount(bool checkIfLoaded = false) const;
bool elevationSourcesLoaded();
QPointer<LocationToElevationHelper>locationToElevation() const override;
//@}
// Not LayerIf, but for MapObject(Impl)
virtual Esri::ArcGISRuntime::GraphicsOverlay *getGraphicsLayer(const QString &logicalName);
virtual Esri::ArcGISRuntime::GraphicsOverlay *getGraphicsLayerDict(const QString &logicalName);
virtual Esri::ArcGISRuntime::GraphicsOverlay *getGraphicsLayer3DExtrusion(const QString &logicalName);
virtual Esri::ArcGISRuntime::GraphicsOverlay *getGraphicsLayer3D_SpAbsolute(const QString &logicalName);
protected slots:
void onLayerParameterChanged(const QString &layername,
const LayerParameter &newLayerParameter);
void onLayerCreatedOnDemand(const QString &logicalName);
void onLayerDoneLoading(const QString &logicalName);
void onElevationSourceDoneLoading();
void onElevationSourceErrorOccured(const Esri::ArcGISRuntime::Error &error);
void onSurfaceDoneLoading2D();
void onSurfaceErrorOccured2D(const Esri::ArcGISRuntime::Error &error);
void reCalculateMinMaxScaleBasedOnLayers();
protected:
bool initializeElevationSources2D(const QStringList &sceneElevationServiceUrls);
bool initializeElevationSources3D(const QStringList &sceneElevationServiceUrls);
QList<Esri::ArcGISRuntime::ElevationSource*> initializeElevationSourcesInternal(const QStringList &sceneElevationServiceUrls);
Esri::ArcGISRuntime::ElevationSource* initializeElevationSourceInternal(const QUrl &elevationServiceUrl, int layerCount);
Esri::ArcGISRuntime::RasterElevationSource *initializeRasterElevationSourceInternal(const QStringList &filePaths);
QSharedPointer<LogicalLayer> addLayerToMgnt(const QString &logicalLayerName, const LayerType &layerType);
bool layerExistsInMngt(const QString &logicalLayerName, const LayerType &layerType) const;
QString createInternalLayerName(const TechnicalLayerType &technicalLayerType,
const QString &logicalLayerName) const;
QSharedPointer<LogicalLayer> getLogicalLayer(const QString &logicalName) const;
QSharedPointer<LogicalLayer> getCreateLogicalLayer(const QString &logicalName);
virtual bool addBasemapInternal(const QString &layername,
const QUrl &mapServiceUrl,
const BasemapType basemapType,
const double minScale = -1,
const double maxScale = -1,
const QString &displayName = QString(),
const QString &description = QString(),
const QImage &previewImage = QImage());
bool removeLayerInternal(const LayerType &layerType, const QString &logicalLayerName);
virtual bool reOrderLayer();
virtual bool reOrderLayerBasemap();
virtual bool reOrderLayerGraphicOverlays();
int getEsriBasemapLayerIndex(const QString &esriLayername) const;
Esri::ArcGISRuntime::Layer *getEsriBasemapLayer(const QString &esriLayername) const;
QUrl completeUrl(const QString &context,
const QUrl &urlIn,
const QString &resourceSubdir);
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
//----------------------------------------------------------------------------
+246
View File
@@ -0,0 +1,246 @@
/**
* @RDE_HEADER@
* Interface-Implementation of LocationDisplay
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "location_display_impl.h"
#include "map_impl.h"
#include <Map/situation_map_utils.h>
#include <MapImpl/coordinate_translation.h>
// ESRI
#include <arcgis/arcgisversion.h> // This file is created by RME, not ESRI
#include <arcgis/Map.h>
#include <arcgis/MapGraphicsView.h>
#if ACRGIS_VERSION >= 0x640800 // 100.8.0 / Update 8
# define HAS_LOCATION_DISPLAY
#endif // ACRGIS_VERSION
#ifdef HAS_LOCATION_DISPLAY
# include <arcgis/LocationDisplay.h>
# include <arcgis/SimulatedLocationDataSource.h>
#endif // HAS_LOCATION_DISPLAY
// Qt
namespace SituationMap
{
#ifdef HAS_LOCATION_DISPLAY
class LocationDisplayImpl::Private
{
public:
Private() = default;
~Private() = default;
// ESRI data:
// Own data:
SituationMap::MapImpl *m_mapImpl{nullptr};
OriginTranslated m_originTranslated;
Esri::ArcGISRuntime::LocationDisplay *m_locationDisplay{nullptr};
Esri::ArcGISRuntime::SimulatedLocationDataSource *m_locationDataSource{nullptr};
}; // class LocationDisplayImpl::Private
//-----------------------------------------------------------------------------
LocationDisplayImpl::LocationDisplayImpl(SituationMap::MapImpl *mapImpl)
: m_logger(getLogger("LocationDisplayImpl"))
, d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
d->m_mapImpl = mapImpl;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
LocationDisplayImpl::~LocationDisplayImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
bool LocationDisplayImpl::init()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!d->m_mapImpl)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no map";
return false;
}
if (d->m_mapImpl->mode() != Mode::Mode_2D)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " LocationDisplay only supported at Mode2D";
return false;
}
// setup coordTranslationFunctor
d->m_originTranslated.setCoordTransFunctor(d->m_mapImpl->coordTranslationFunctor());
d->m_locationDisplay = d->m_mapImpl->getEsriMapGraphicsView()->locationDisplay();
if (!d->m_locationDisplay)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no locationDisplay";
return false;
}
// setup LocationDisplay:
if (d->m_locationDisplay->objectName().isEmpty())
d->m_locationDisplay->setObjectName(QStringLiteral("EsriLocationDisplay"));
QObject::connect(d->m_locationDisplay, &Esri::ArcGISRuntime::LocationDisplay::errorOccurred, d->m_locationDisplay, [this](const Esri::ArcGISRuntime::Error &error) {
ERROR_LOG(m_logger) << LOGFUNCTION << " error on LocationDisplay, error " << error;
});
if (m_logger.isDebugEnabled())
QObject::connect(d->m_locationDisplay, &Esri::ArcGISRuntime::LocationDisplay::locationChanged, d->m_locationDisplay, [this](const Esri::ArcGISRuntime::Location &location) {
DEBUG_LOG(m_logger) << LOGFUNCTION << " Location changed: " << location.position().toJson();
});
// setup DataSource:
d->m_locationDataSource = new Esri::ArcGISRuntime::SimulatedLocationDataSource(d->m_mapImpl);
d->m_locationDataSource->setObjectName(QStringLiteral("SimulatedLocationDataSource"));
QObject::connect(d->m_locationDataSource, &Esri::ArcGISRuntime::SimulatedLocationDataSource::errorOccurred, d->m_locationDisplay, [this](const Esri::ArcGISRuntime::Error &error) {
ERROR_LOG(m_logger) << LOGFUNCTION << " error on SimulatedLocationDataSource, error " << error;
});
// Maybe reduce animation rate:
// d->m_locationDataSource->setIterationRate(0.25);
d->m_locationDisplay->setDataSource(d->m_locationDataSource);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return (d->m_locationDisplay);
}
bool LocationDisplayImpl::setCurrentLocation(const Point3D & currentLocation,
double horizontalAccuracy,
double velocity,
double course,
bool lastKnown)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, currentLocation=" << currentLocation
<< ", horizontalAccuracy=" << horizontalAccuracy
<< ", velocity=" << velocity
<< ", course=" << course
<< ", lastKnown=" << lastKnown;
if (!d->m_mapImpl)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no map";
return false;
}
if (!d->m_locationDataSource)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no locationDataSource";
return false;
}
// convert using coordTranslationFunctor (wrapped by OriginTranslated)
d->m_originTranslated.setOrigin(currentLocation);
const Point3D &origionTranslated = d->m_originTranslated.originTranslated();
Esri::ArcGISRuntime::Point point(origionTranslated.x(), origionTranslated.y(), origionTranslated.z(), d->m_mapImpl->getEsriSpatialReference());
DEBUG_LOG(m_logger) << LOGFUNCTION << " point=" << point.toJson();
// const int velocity = 0; // 0 => kein Richtungsinformation in der Darstellung
Esri::ArcGISRuntime::Location location(point,
0, // The horizontal accuracy in meters. Positive values or NaN are supported
velocity, // The location's velocity in meters per second
course, // The location's course in degrees (clockwise), 0 being true North.
lastKnown // (false->blue ; true -> gray)
);
d->m_locationDataSource->setLocations({location});
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return true;
}
bool LocationDisplayImpl::setOutdated(bool lastKnown)
{
return setCurrentLocation(d->m_originTranslated.origin(),0.0 ,0.0 ,0.0 ,lastKnown);
}
bool LocationDisplayImpl::start()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
// 2023, N.Thoden: V100.15: use LocationDisplay instead of LocationDataSource
if (!d->m_locationDisplay)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no locationDisplay";
return false;
}
// d->m_locationDisplay->setAutoPanMode(Esri::ArcGISRuntime::LocationDisplayAutoPanMode::Recenter);
// d->m_locationDisplay->setInitialZoomScale(8123);
d->m_locationDisplay->start();
// d->m_locationDataSource->start();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return true;
}
void LocationDisplayImpl::stop()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
// 2023, N.Thoden: V100.15: use LocationDisplay instead of LocationDataSource
if (!d->m_locationDisplay)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no locationDisplay";
return;
}
d->m_locationDisplay->stop();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
bool LocationDisplayImpl::isStarted()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
// 2023, N.Thoden: V100.15: avoid deprecated, use LocationDisplay instead of LocationDataSource
if (!d->m_locationDisplay)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no locationDisplay";
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return d->m_locationDisplay->isStarted();
}
#else // HAS_LOCATION_DISPLAY
LocationDisplayImpl::LocationDisplayImpl(SituationMap::MapImpl *mapImpl)
: m_logger(getLogger("LocationDisplayImpl"))
, d(nullptr)
{ Q_UNUSED(mapImpl); }
LocationDisplayImpl::~LocationDisplayImpl() {}
bool LocationDisplayImpl::init() {return false; }
bool LocationDisplayImpl::setCurrentLocation(const Point3D & /*currentLocation*/,
double /*horizontalAccuracy*/,
double /*velocity*/,
double /*course*/,
bool /*lastKnown*/)
{ return false; }
bool LocationDisplayImpl::start() { return false; }
void LocationDisplayImpl::stop() { }
bool LocationDisplayImpl::isStarted() { return false; }
bool LocationDisplayImpl::setOutdated(bool lastKnown) { return lastKnown; }
#endif // HAS_LOCATION_DISPLAY
} // namespace SituationMap
+61
View File
@@ -0,0 +1,61 @@
/**
* @RDE_HEADER@
* Interface-Implementation of LocationDisplay
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/location_display.h> // Interface
#include <Common/situation_map_logging.h>
//----------------------------------------------------------------------------
namespace SituationMap
{
class MapImpl;
class LocationDisplayImpl : public SituationMap::LocationDisplay
{
using BaseClass = SituationMap::LocationDisplay;
public:
explicit LocationDisplayImpl(SituationMap::MapImpl *mapImpl);
virtual ~LocationDisplayImpl();
LocationDisplayImpl(const LocationDisplayImpl &other) = delete;
LocationDisplayImpl &operator=(LocationDisplayImpl &other) = delete;
bool init();
bool setCurrentLocation(const Point3D & currentLocation,
double horizontalAccuracy = 0,
double velocity = 0,
double course = 0,
bool lastKnown = false
) override;
bool setOutdated(bool lastKnown) override;
bool start() override;
void stop() override;
bool isStarted() override;
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,218 @@
/**
* @RDE_HEADER@
* LocationToElevationBulk
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "location_to_elevation_bulk.h"
#include <MapImpl/map_impl.h>
#include "Map/situation_map_utils.h"
#include "location_to_elevation_helper.h" // to get define USE_ELEVATION_MAP_AS_WORKAROUND
// ESRI
#include <arcgis/Surface.h>
#include <arcgis/Point.h>
// Qt
#include <QPointer>
#include <QVector>
//----------------------------------------------------------------------------
namespace SituationMap
{
//----------------------------------------------------------------------------
class LocationToElevationBulk::Private
{
public:
Private() = default;
~Private() = default;
// ESRI data:
QPointer<Esri::ArcGISRuntime::Surface> m_surface; // for ESRI-Surface-based calculation
QPointer<Map> m_map; // for ElevationMap-based calculation
Esri::ArcGISRuntime::Point m_location;
Esri::ArcGISRuntime::SpatialReference m_spatialReference;
QVector<QSharedPointer<LocationData>> m_locationDataCont;
int m_progressCount = 0;
};
//----------------------------------------------------------------------------
LocationToElevationBulk::LocationToElevationBulk(QObject *parent)
: BaseClass(parent)
, m_logger(getLogger("LocationToElevationBulk"))
, d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
reset();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
LocationToElevationBulk::~LocationToElevationBulk()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
this->stop();
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
bool LocationToElevationBulk::init(Map *map, Esri::ArcGISRuntime::Surface *surface)
{
this->reset();
if (!map)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no map";
return false;
}
d->m_map = map;
connect(d->m_map.data(), &Map::beginDestroy, this, &LocationToElevationBulk::stop);
MapImpl *mapImpl = static_cast<MapImpl*>(d->m_map.data());
d->m_spatialReference = mapImpl->getEsriSpatialReference();
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_spatialReference.wkid = " << d->m_spatialReference.wkid();
// use ESRI-Surface-based OR ElevationMap-based:
#ifdef USE_ELEVATION_MAP_AS_WORKAROUND
Q_UNUSED(surface);
d->m_surface.clear();
auto elevationMap = static_cast<MapImpl*>(d->m_map.data())->getElevationMapManager();
if (!elevationMap)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no ElevationMapManager";
return false;
}
#else
d->m_surface = surface;
if (!d->m_surface)
return false;
if (d->m_surface)
{
// be prepared in case of destruction:
connect(d->m_surface.data(), &Esri::ArcGISRuntime::Surface::destroyed, this, &LocationToElevationBulk::stop);
}
#endif // USE_ELEVATION_MAP_AS_WORKAROUND
return true;
}
void LocationToElevationBulk::addLocation(const Esri::ArcGISRuntime::Point &esriMapLocation)
{
d->m_locationDataCont.append(QSharedPointer<LocationData>(new LocationData(esriMapLocation)));
}
void LocationToElevationBulk::start()
{
d->m_progressCount = 0;
#ifndef USE_ELEVATION_MAP_AS_WORKAROUND
if (!d->m_surface || d->m_surface->loadStatus() != Esri::ArcGISRuntime::LoadStatus::Loaded)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no surface or surface not loaded yet"
<< " d->m_surface = " << d->m_surface.data();
return;
}
#endif // USE_ELEVATION_MAP_AS_WORKAROUND
QMutableVectorIterator<QSharedPointer<LocationData>> vecIt(d->m_locationDataCont);
while (vecIt.hasNext())
{
QSharedPointer<LocationData> ld = vecIt.next();
if(!ld)
continue;
#ifdef USE_ELEVATION_MAP_AS_WORKAROUND
ld->setMap(d->m_map);
#else
ld->setSurface(d->m_surface);
#endif // USE_ELEVATION_MAP_AS_WORKAROUND
connect(ld.data(), &LocationData::elevationCompleted,
this, &LocationToElevationBulk::onLocationDataCompleted, Qt::UniqueConnection);
if (!ld->startLocationToElevation())
ERROR_LOG(m_logger) << LOGFUNCTION << " startLocationToElevation failed";
} // while
}
void LocationToElevationBulk::stop()
{
QMutableVectorIterator<QSharedPointer<LocationData>> vecIt(d->m_locationDataCont);
while (vecIt.hasNext())
{
QSharedPointer<LocationData> ld = vecIt.next();
if(!ld)
continue;
if (!ld->cancel())
INFO_LOG(m_logger) << LOGFUNCTION << " LocationData.cancel failed";
} // while
}
void LocationToElevationBulk::reset()
{
stop();
d->m_locationDataCont.clear();
d->m_progressCount = 0;
}
int LocationToElevationBulk::size() const
{
return d->m_locationDataCont.size();
}
QVector<QPair<QPointF, double> > LocationToElevationBulk::results() const
{
QVector<QPair<QPointF, double> > results;
QVectorIterator<QSharedPointer<LocationData>> vecIt(d->m_locationDataCont);
while (vecIt.hasNext())
{
QSharedPointer<LocationData> ld = vecIt.next();
if(!ld)
continue;
const Esri::ArcGISRuntime::Point &location = ld->location();
QPair<QPointF, double> pair(QPointF(location.x(), location.y()), ld->elevation());
results.append(pair);
}
return results;
}
void LocationToElevationBulk::onLocationDataCompleted()
{
++d->m_progressCount;
if (d->m_progressCount >= d->m_locationDataCont.size())
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " emit locationsToElevationsCompleted";
emit locationsToElevationsCompleted();
}
}
} // namespace SituationMap
@@ -0,0 +1,218 @@
/**
* @RDE_HEADER@
* LocationToElevationBulk
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "screen_location_elevation_helper_base.h"
#include <Map/map.h>
#include <Common/situation_map_logging.h>
// for ElevationMap-based calculation
#include <MapImpl/map_impl.h>
#include <ElevationMap/elevation_map_manager.h>
#include <Map/coord_translation_functor.h>
//ESRI
#include <arcgis/Point.h>
#include <arcgis/Surface.h>
#include <arcgis/TaskWatcher.h>
// Qt
#include <QObject>
#include <QPointer>
#include <QList>
#include <QVector>
namespace Esri
{
namespace ArcGISRuntime
{
class Surface;
class Point;
class SpatialReference;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
class LocationData : public QObject
{
Q_OBJECT
public:
explicit LocationData(const Esri::ArcGISRuntime::Point &esriMapLocation)
: QObject()
, m_esriMapLocation(esriMapLocation)
{}
~LocationData()
{
cancel();
}
/*! \name Prepare surface (ESRI-based) OR map (own class ElevationMap */
//@{
void setSurface(Esri::ArcGISRuntime::Surface *surface)
{
m_map.clear();
m_surface = QPointer<Esri::ArcGISRuntime::Surface>(surface);
connect(m_surface.data(), &Esri::ArcGISRuntime::Surface::locationToElevationCompleted,
this, &LocationData::onLocationToElevationCompleted, Qt::UniqueConnection);
}
void setMap(Map *map)
{
m_surface.clear();
m_map = map;
}
//@}
bool startLocationToElevation()
{
// ESRI-Surface-based calculation :
if (m_surface)
{
m_taskWatcher = m_surface->locationToElevation(m_esriMapLocation);
if (!m_taskWatcher.isValid())
{
qCritical() << " locationToElevation failed, taskWatcher invalid";
return false;
}
return true;
}
// : ESRI-Surface-based calculation
// ElevationMap-based calculation :
if (m_map)
{
auto elevationMap = static_cast<MapImpl*>(m_map.data())->getElevationMapManager(); // ToDo: remove if Esri-Surface will work without crash
if (!elevationMap)
{
qCritical() << " no ElevationMapManager";
return false;
}
double lon = m_esriMapLocation.x();
double lat = m_esriMapLocation.y();
CoordTranslationFunctor *coordTranslationFunctor = m_map->coordTranslationFunctor();
if ((coordTranslationFunctor) && (!coordTranslationFunctor->mapIsGeographic()))
{
const QPointF &geoLoc = coordTranslationFunctor->mapToGeographic(lon, lat);
lon = geoLoc.x();
lat = geoLoc.y();
}
m_elevation = elevationMap->elevation(lon, lat);
emit elevationCompleted();
return true;
}
// : ElevationMap-based calculation
return false;
}
bool cancel()
{
return m_taskWatcher.cancel();
}
const Esri::ArcGISRuntime::Point &location() const { return m_esriMapLocation; }
bool hasElevation() const { return std::isnan(m_elevation); }
double elevation() const { return m_elevation; }
void setElevation(double elevation) { m_elevation = elevation; }
signals:
void elevationCompleted();
protected slots:
void onLocationToElevationCompleted(const QUuid &taskId, double elevation)
{
if (m_taskWatcher.taskId() != taskId)
return;
m_elevation = elevation;
emit elevationCompleted();
}
private:
// In:
Esri::ArcGISRuntime::Point m_esriMapLocation;
// processing:
QPointer<Esri::ArcGISRuntime::Surface> m_surface; // for ESRI-Surface-based calculation
QPointer<Map> m_map; // for ElevationMap-based calculation
Esri::ArcGISRuntime::TaskWatcher m_taskWatcher;
// out:
double m_elevation = std::nan("");
}; // LocationData
/**
* @brief The LocationToElevationBulk class helps calculate a couple of locations to their corresponding elevation
*/
class SITUATIONMAP_EXPORT LocationToElevationBulk : public QObject
{
Q_OBJECT
using BaseClass = QObject;
public:
/*! \name Construction, Destruction */
//@{
explicit LocationToElevationBulk(QObject *parent = nullptr);
~LocationToElevationBulk();
bool init(Map *map, Esri::ArcGISRuntime::Surface *surface);
LocationToElevationBulk(const LocationToElevationBulk &other) = delete;
LocationToElevationBulk &operator=(LocationToElevationBulk &other) = delete;
//@}
void addLocation(const Esri::ArcGISRuntime::Point &esriMapLocation);
void start();
void stop();
void reset();
int size() const;
QVector< QPair< QPointF, double > > results() const;
Q_SIGNALS:
void locationsToElevationsCompleted();
private Q_SLOTS:
void onLocationDataCompleted();
protected:
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,204 @@
/**
* @RDE_HEADER@
* LocationToElevationHelper
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "location_to_elevation_helper.h"
#include "Map/situation_map_utils.h"
//
// NOTE: define should be in the headerfile to switch location_to_elevation_bulk also
//
#ifdef USE_ELEVATION_MAP_AS_WORKAROUND
#include "Map/coord_translation_functor.h" // ToDo: remove if Esri-Surface will work under windows without crash
#include "MapImpl/map_impl.h"
#include <ElevationMap/elevation_map_manager.h>
#endif // USE_ELEVATION_MAP_AS_WORKAROUND
// ESRI
#include <arcgis/Surface.h>
#include <arcgis/Point.h>
// Qt
#include <QPointer>
//----------------------------------------------------------------------------
namespace SituationMap
{
//----------------------------------------------------------------------------
class LocationToElevationHelper::Private
{
public:
Private() = default;
~Private() = default;
// ESRI data:
QPointer<Esri::ArcGISRuntime::Surface> m_surface;
Esri::ArcGISRuntime::Point m_location;
Esri::ArcGISRuntime::SpatialReference m_spatialReference;
#ifdef USE_ELEVATION_MAP_AS_WORKAROUND
CoordTranslationFunctor *m_coordTransFunctor; // ToDo: remove once Esri-Surface will work under windows without crash
QPointer<ElevationMapManager> m_elevationMapManager;
#endif // USE_ELEVATION_MAP_AS_WORKAROUND
};
//----------------------------------------------------------------------------
LocationToElevationHelper::LocationToElevationHelper(QObject *parent)
: BaseClass(parent)
, m_logger(getLogger("LocationToElevationHelper"))
, d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
LocationToElevationHelper::~LocationToElevationHelper()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
this->stop();
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
bool LocationToElevationHelper::init(Map *map,
Esri::ArcGISRuntime::Surface *surface,
const Esri::ArcGISRuntime::SpatialReference &spatialReference)
{
this->stop();
connect(map, &Map::beginDestroy, this, &LocationToElevationHelper::stop);
d->m_surface = surface;
d->m_spatialReference = spatialReference;
#ifdef USE_ELEVATION_MAP_AS_WORKAROUND
d->m_elevationMapManager = static_cast<MapImpl*>(map)->getElevationMapManager().get(); // ToDo: remove (?) if Esri-Surface will work under windows without crash
d->m_coordTransFunctor = map->coordTranslationFunctor();
if (!d->m_elevationMapManager)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no ElevationMapManager";
return false;
}
#endif // USE_ELEVATION_MAP_AS_WORKAROUND
if (!d->m_surface)
return false;
INFO_LOG(m_logger) << LOGFUNCTION << " m_spatialReference.wkid = " << d->m_spatialReference.wkid();
// be prepared in case of destruction:
connect(d->m_surface.data(), &Esri::ArcGISRuntime::Surface::destroyed, this, &LocationToElevationHelper::stop);
// assume that m_surface is loaded:
QMetaObject::Connection con;
if (d->m_surface)
con = connect(d->m_surface.data(), &Esri::ArcGISRuntime::Surface::locationToElevationCompleted,
this, &LocationToElevationHelper::onLocationToElevationCompleted);
if (!con)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " connect failed";
return false;
}
return true;
}
void LocationToElevationHelper::stop()
{
if (d->m_surface)
d->m_surface->disconnect(this);
this->disconnect(this);
BaseClass::stop();
d->m_surface.clear();
}
void LocationToElevationHelper::locationToElevation(const QPointF &mapLocation)
{
locationToElevation(Esri::ArcGISRuntime::Point(mapLocation.x(), mapLocation.y(), d->m_spatialReference));
}
void LocationToElevationHelper::locationToElevation(const Esri::ArcGISRuntime::Point &esriMapLocation)
{
#ifdef USE_ELEVATION_MAP_AS_WORKAROUND
if (d->m_elevationMapManager) // ToDo: remove (?) if Esri-Surface will work under windows without crash
{
double lon = esriMapLocation.x();
double lat = esriMapLocation.y();
if ((d->m_coordTransFunctor) && (!d->m_coordTransFunctor->mapIsGeographic()))
{
const QPointF & geoLoc = d->m_coordTransFunctor->mapToGeographic(lon, lat);
lon = geoLoc.x();
lat = geoLoc.y();
}
const double ele = d->m_elevationMapManager->elevation(lon,lat);
emit elevationCompleted(ele);
return;
}
#endif // USE_ELEVATION_MAP_AS_WORKAROUND
d->m_location = esriMapLocation;
// this->cancel(); // cancel all former:
BaseClass::newComputationRequested();
}
void LocationToElevationHelper::mainComputation()
{
if (!d->m_surface)
return;
if (d->m_surface->loadStatus() != Esri::ArcGISRuntime::LoadStatus::Loaded)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " surface not loaded yet";
return;
}
const Esri::ArcGISRuntime::TaskWatcher &tw = d->m_surface->locationToElevation(Esri::ArcGISRuntime::Point(d->m_location));
if (!insertTask(tw))
ERROR_LOG(m_logger) << LOGFUNCTION << " locationToElevation failed";
}
void LocationToElevationHelper::onLocationToElevationCompleted(const QUuid &taskId, double elevation)
{
// INFO_LOG(m_logger) << LOGFUNCTION << " locationToElevation onLocationToElevationCompleted, elevation= " << elevation;
if (!this->removeTask(taskId))
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " not our task -> skip;";
return;
}
emit elevationCompleted(elevation);
}
} // namespace SituationMap
@@ -0,0 +1,83 @@
/**
* @RDE_HEADER@
* LocationToElevationHelper
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "screen_location_elevation_helper_base.h"
#include <Map/map.h>
#include <Common/situation_map_logging.h>
#define USE_ELEVATION_MAP_AS_WORKAROUND // ToDo: remove if Esri-Surface will work without crash
// Qt
#include <QObject>
namespace Esri {
namespace ArcGISRuntime
{
class Surface;
class Point;
class SpatialReference;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
/**
* @brief The ScreenToLocationHelper class helps to assign the (global) requests location_to_elevation to the right caller
*/
class SITUATIONMAP_EXPORT LocationToElevationHelper : public ScreenLocationElevationHelperBase
{
Q_OBJECT
using BaseClass = ScreenLocationElevationHelperBase;
public:
/*! \name Construction, Destruction */
//@{
explicit LocationToElevationHelper(QObject *parent = nullptr);
~LocationToElevationHelper();
bool init(Map *map,
Esri::ArcGISRuntime::Surface *surface,
const Esri::ArcGISRuntime::SpatialReference &spatialReference);
LocationToElevationHelper(const LocationToElevationHelper &other) = delete;
LocationToElevationHelper &operator=(LocationToElevationHelper &other) = delete;
//@}
void stop();
void locationToElevation(const QPointF &mapLocation);
void locationToElevation(const Esri::ArcGISRuntime::Point &esriMapLocation);
void mainComputation() override;
Q_SIGNALS:
void elevationCompleted(double elevation);
private Q_SLOTS:
void onLocationToElevationCompleted(const QUuid &taskId, double elevation);
protected:
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
+801
View File
@@ -0,0 +1,801 @@
/**
* @RDE_HEADER@
* MapLayer
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "logical_layer.h"
#include <Map/map.h>
#include <Symbology/symbol_dictionary.h>
#include <SymbologyImpl/symbol_dictionary_impl.h>
#include <Map/situation_map_utils.h>
// ESRI
#include <arcgis/Layer.h>
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/DictionaryRenderer.h>
#include <arcgis/ArcGISTiledLayer.h>
#include <arcgis/LayerSceneProperties.h>
// Qt
// #include <QDebug>
#include <QTimer>
static_assert((int)SituationMap::SurfacePlacement::Draped == (int)Esri::ArcGISRuntime::SurfacePlacement::Draped, "SurfacePlacement mismatch");
static_assert((int)SituationMap::SurfacePlacement::Absolute == (int)Esri::ArcGISRuntime::SurfacePlacement::Absolute, "SurfacePlacement mismatch");
static_assert((int)SituationMap::SurfacePlacement::Relative == (int)Esri::ArcGISRuntime::SurfacePlacement::Relative, "SurfacePlacement mismatch");
namespace SituationMap
{
LogicalLayer::LogicalLayer(const QString &logicalName, QObject *parent)
: BaseClass(parent),
m_logicalName(logicalName),
m_logger(getLogger("LogicalLayer"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " m_logicalName='" << m_logicalName << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " m_logicalName='" << m_logicalName << "'";
}
LogicalLayer::~LogicalLayer()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " m_logicalName='" << m_logicalName << "'";
fini();
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " m_logicalName='" << m_logicalName << "'";
}
void LogicalLayer::init(SituationMap::Map *map,
Esri::ArcGISRuntime::LayerListModel *layerListModel,
Esri::ArcGISRuntime::GraphicsOverlayListModel *graphicsOverlayListModel)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " START"
<< " m_logicalName='" << m_logicalName << "'"
<< " layerListModel=" << layerListModel
<< " graphicsOverlayListModel=" << graphicsOverlayListModel;
m_map = map;
m_layerListModel = layerListModel;
m_graphicsOverlayListModel = graphicsOverlayListModel;
#ifdef LTS_DEBUG
// sometimes getCreateGraphicalOverlay() has no m_graphicsOverlayListModel - hmm
// happens for layer 'MountingfPoints'
DEBUG_LOG(m_logger) << LOGFUNCTION << " START"
<< " m_logicalName='" << m_logicalName << "'"
<< " m_map=" << m_map
<< " m_map->isReady=" << m_map->isReady()
<< " layerListModel=" << layerListModel
<< " graphicsOverlayListModel=" << graphicsOverlayListModel;
Q_ASSERT(m_graphicsOverlayListModel);
#endif // LTS_DEBUG
// set default order of GraphicalOverlays: (from bottom to top)
setOrderOfGraphicalOverlays({ TechnicalLayerType::GraphicsLayer,
TechnicalLayerType::GraphicsLayer_Dict,
TechnicalLayerType::GraphicsLayer_3D_Extrusion,
TechnicalLayerType::GraphicsLayer_3D_Absolute });
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void LogicalLayer::fini()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " m_logicalName='" << m_logicalName << "'"
<< " layerListModel=" << m_layerListModel
<< " graphicsOverlayListModel=" << m_graphicsOverlayListModel;
// process Layer (e.g. Basemap)
// Esri::ArcGISRuntime::Layer
if (m_layer && m_layerListModel)
{
m_layerListModel->removeOne(m_layer);
delete m_layer;
m_layer = nullptr;
}
// process GraphicsOverlay
if (m_graphicsOverlayListModel)
{
for (Esri::ArcGISRuntime::GraphicsOverlay *graphicsOverlay : qAsConst(m_graphicalOverlays))
{
// do some checks :
{
const int numberOfCurrentObjects = graphicsOverlay->graphics()->size();
if (numberOfCurrentObjects != 0)
{
QString objectName;
Esri::ArcGISRuntime::Graphic* g = graphicsOverlay->graphics()->first();
if (g)
objectName = g->objectName();
ERROR_LOG(m_logger) << LOGFUNCTION
<< " Attention, removing GraphicsOverlay still having objects!!!"
<< ", graphicsOverlay='" << graphicsOverlay << "'"
<< ", numberOfCurrentObjects=" << numberOfCurrentObjects
<< ", m_logicalName='" << m_logicalName << "'"
<< ", objectName of first='" << objectName << "'";
}
TRACE_LOG(m_logger) << LOGFUNCTION << " m_logicalName='" << m_logicalName << "'"
<< " numberOfCurrentObjects=" << numberOfCurrentObjects;
}
// : do some checks
m_graphicsOverlayListModel->removeOne(graphicsOverlay);
delete graphicsOverlay;
}
}
// cleanup internal container:
m_graphicalOverlays.clear();
m_graphicOverlayContainer.clear();
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " m_logicalName='" << m_logicalName << "'"
<< " layerListModel=" << m_layerListModel
<< " graphicsOverlayListModel=" << m_graphicsOverlayListModel;
}
QString LogicalLayer::logicalName() const
{
return m_logicalName;
}
void LogicalLayer::setLogicalName(const QString &logicalName)
{
m_logicalName = logicalName;
}
LayerType LogicalLayer::layerType() const
{
return m_layerType;
}
void LogicalLayer::setLayerType(const LayerType &layerType)
{
m_layerType = layerType;
}
BasemapType LogicalLayer::basemapType() const
{
return m_basemapType;
}
void LogicalLayer::setBasemapType(const BasemapType &basemapType)
{
m_basemapType = basemapType;
}
RenderingMode LogicalLayer::renderingMode() const
{
return m_graphicsOverlayRenderingMode;
}
void LogicalLayer::setRenderingMode(RenderingMode renderingMode)
{
m_graphicsOverlayRenderingMode = renderingMode;
}
SurfacePlacement LogicalLayer::surfacePlacement() const
{
return m_surfacePlacement;
}
void LogicalLayer::setSurfacePlacement(SurfacePlacement surfacePlacement)
{
m_surfacePlacement = surfacePlacement;
}
const LayerParameter &LogicalLayer::layerParameter() const
{
return m_layerParameter;
}
void LogicalLayer::setLayerParameter(const LayerParameter &layerParameter)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " m_logicalName='" << m_logicalName << "'"
<< " layerParameter=" << layerParameter;
bool changed = (m_layerParameter != layerParameter);
m_layerParameter = layerParameter;
if (changed)
{
// apply LayerParameters (visible, opca, brightness....)
this->applyLayerParameterToEsri();
// trigger update of gui:
emit layerParameterChanged(m_logicalName, m_layerParameter);
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " m_logicalName='" << m_logicalName << "'"
<< " changed=" << changed;
}
Esri::ArcGISRuntime::Layer *LogicalLayer::layer() const
{
return m_layer;
}
void LogicalLayer::setLayer(Esri::ArcGISRuntime::Layer *layer)
{
m_layer = layer;
// Handle errors of layer:
connect(m_layer, &Esri::ArcGISRuntime::Layer::errorOccurred,
this, &LogicalLayer::onLayerError, Qt::UniqueConnection);
// Handle doneLoading:
connect(m_layer, &Esri::ArcGISRuntime::Layer::doneLoading,
this, &LogicalLayer::onLayerDoneLoading, Qt::UniqueConnection);
connect(m_layer, &Esri::ArcGISRuntime::Layer::loadStatusChanged,
this, &LogicalLayer::onLayerLoadStatusChanged, Qt::UniqueConnection);
if (!m_layerListModel->contains(m_layer))
m_layerListModel->append(m_layer);
}
QList<Esri::ArcGISRuntime::GraphicsOverlay *> LogicalLayer::getCurrentGraphicalOverlaysOrdered() const
{
QList<Esri::ArcGISRuntime::GraphicsOverlay *> orderedLayerList;
for (TechnicalLayerType techType : m_orderOfGraphicalOverlays)
{
const QString &esriLayerName = LogicalLayer::createInternalLayerName(m_logicalName, techType);
if (!m_graphicOverlayContainer.contains(esriLayerName))
continue;
Esri::ArcGISRuntime::GraphicsOverlay *layer = m_graphicOverlayContainer[esriLayerName];
if (layer)
orderedLayerList.append(layer);
} // for
return orderedLayerList;
}
QList<QPair<QString, Esri::ArcGISRuntime::GraphicsOverlay *> > LogicalLayer::getCurrentGraphicalOverlaysOrderedWithName() const
{
QList<QPair<QString, Esri::ArcGISRuntime::GraphicsOverlay *> > ret;
for (TechnicalLayerType techType : qAsConst(m_orderOfGraphicalOverlays))
{
const QString &esriLayerName = LogicalLayer::createInternalLayerName(m_logicalName, techType);
if (!m_graphicOverlayContainer.contains(esriLayerName))
continue;
Esri::ArcGISRuntime::GraphicsOverlay *layer = m_graphicOverlayContainer[esriLayerName];
if (!layer)
continue;
QPair<QString, Esri::ArcGISRuntime::GraphicsOverlay *> foundPair(esriLayerName, layer);
ret.append(foundPair);
} // for
return ret;
}
int LogicalLayer::layerIndex() const
{
if (! m_layerListModel)
return -1;
return m_layerListModel->indexOf(m_layer);
}
Esri::ArcGISRuntime::GraphicsOverlay *LogicalLayer::getCreateGraphicalOverlay(const TechnicalLayerType &technicalLayerType)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " m_logicalName='" << m_logicalName << "'"
<< " technicalLayerType=" << technicalLayerType;
// [GUEZE-496] RPL Nr 770: 3D Lagedarstellung Einfrieren der SW Appl.
// Layer renderer does not support multipoint graphics (V100.8)
// So turn back to 2 technical layers per logical layer. So The MineArea
// is on a TechnicalLayerType::GraphicsLayer and MineArea/IndicationContainer
// uses ONE GraphicalObjectPictureMarker/Esri::ArcGISRuntime::PictureMarkerSymbol
// with multiple coordinates (multipoint)
TechnicalLayerType technicalLayerTypeLocal = technicalLayerType;
if ((m_layerType == LayerType::Rubberband) ||
(m_layerType == LayerType::Overlay) ||
((m_layerType == LayerType::Operational) && (m_logicalName.compare(QStringLiteral("PlanEditorLayer")) == 0))
)
{
technicalLayerTypeLocal = TechnicalLayerType::GraphicsLayer_Dict;
}
if (! m_graphicsOverlayListModel)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " m_logicalName='" << m_logicalName << "'"
<< " no m_graphicsOverlayListModel";
#ifdef LTS_DEBUG
Q_ASSERT(m_graphicsOverlayListModel);
#endif // LTS_DEBUG
return nullptr;
}
if (! m_orderOfGraphicalOverlays.contains(technicalLayerTypeLocal))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " m_logicalName='" << m_logicalName << "'"
<< " not allowed: technicalLayerType=" << technicalLayerTypeLocal;
return nullptr;
}
Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = nullptr;
const QString &esriLayerName = LogicalLayer::createInternalLayerName(m_logicalName, technicalLayerTypeLocal);
// lookup in own container:
if (m_graphicOverlayContainer.contains(esriLayerName))
{
graphicsLayer = m_graphicOverlayContainer[esriLayerName];
// make sure, that layer is still valid (within esri/model):
if (graphicsLayer)
{
if (! m_graphicsOverlayListModel->contains(graphicsLayer))
{
m_graphicOverlayContainer.remove(esriLayerName);
graphicsLayer = nullptr;
}
}
}
// create if not found
if (! graphicsLayer)
{
INFO_LOG(m_logger) << LOGFUNCTION
<< " requested (logical) layername ='" << m_logicalName << "' not in esriMap"
<< ", create on demand..."
<< " (renderMode=" << m_graphicsOverlayRenderingMode << ")";
graphicsLayer = createGraphicLayer(technicalLayerTypeLocal, m_graphicsOverlayRenderingMode);
//
// post operations:
//
// apply LayerParameters (visible, opca, brightness....)
this->applyLayerParameterToEsri();
// trigger re-ordering
emit layerCreatedOnDemand(m_logicalName);
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " technicalLayerType=" << technicalLayerType;
return graphicsLayer;
}
void LogicalLayer::onLayerError(const Esri::ArcGISRuntime::Error &error)
{
if (error.code() == 404)
DEBUG_LOG(m_logger) << LOGFUNCTION << " EsriError occured: " << error
<< ", layerName=" << m_layerParameter.url.toString();
else
ERROR_LOG(m_logger) << LOGFUNCTION << " EsriError occured: " << error
<< ", layerName=" << m_layerParameter.url.toString();
}
void LogicalLayer::onGraphicsOverlayLayerError(const Esri::ArcGISRuntime::Error &error)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " EsriError occured: " << error
<< ", layerName=" << m_layerParameter.url.toString();
}
void LogicalLayer::onRendererError(const Esri::ArcGISRuntime::Error &error)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " EsriError occured: " << error
<< ", layerName=" << m_layerParameter.url.toString();
}
void LogicalLayer::onLayerDoneLoading(const Esri::ArcGISRuntime::Error &loadError)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " m_logicalName='" << m_logicalName << "'";
if (!loadError.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " m_logicalName='" << m_logicalName << "'"
<< ", layerName = '" << m_layerParameter.url.toString() << "'"
<< ", loadError: " << loadError;
Esri::ArcGISRuntime::Layer *layer = qobject_cast<Esri::ArcGISRuntime::Layer *>(sender());
if (layer)
{
const int maxRetry = 2;
if (m_retryCounter<=maxRetry)
{
++m_retryCounter;
QTimer::singleShot(1000 + (m_retryCounter+1000), this, std::bind(&Esri::ArcGISRuntime::Layer::retryLoad, layer));
INFO_LOG(m_logger) << LOGFUNCTION << " schedule retryLoad for '" << m_logicalName << "'" << " (m_retryCounter=" << m_retryCounter << ")";
}
else
{
ERROR_LOG(m_logger) << LOGFUNCTION << " max number of retries (" << maxRetry << ") exceeded, give up" << ", m_logicalName='" << m_logicalName << "'";
}
return;
}
else
ERROR_LOG(m_logger) << LOGFUNCTION
<< " qobject_cast failed m_logicalName='" << m_logicalName << "'";
return;
}
Esri::ArcGISRuntime::Layer *layer = qobject_cast<Esri::ArcGISRuntime::Layer *>(sender());
if (!layer)
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " qobject_cast failed m_logicalName='" << m_logicalName << "'";
return;
}
// Esri::ArcGISRuntime::Item *item = layer->item();
// if (!item)
// FATAL_LOG(m_logger) << LOGFUNCTION << " no item!!!! " << m_logicalName << "'";
const QString layername(layer->name());
INFO_LOG(m_logger) << LOGFUNCTION << " esriLayer of LogicalLayer: '" << layername << "' is loaded now #1: ESRI-Layer: "
<< " minScale()=" << layer->minScale()
<< ", maxScale()=" << layer->maxScale()
<< " (m_retryCounter=" << m_retryCounter << ")";
// minScale, maxScale at 3D returns always '0' for basemaps, so skip
if ((m_layerType == LayerType::Basemap) && (m_map->mode() == Mode::Mode_2D))
{
if (!m_layerParameter.minScaleValid())
m_layerParameter.minScale = layer->minScale();
if (!m_layerParameter.minMaxScaleValid())
m_layerParameter.maxScale = layer->maxScale();
}
INFO_LOG(m_logger) << LOGFUNCTION << " esriLayer of LogicalLayer: '" << layername << "' is loaded now #2: LogicalLayer"
<< ", minScale = " << m_layerParameter.minScale
<< ", maxScale = " << m_layerParameter.maxScale;
Esri::ArcGISRuntime::ArcGISTiledLayer *arcGISTiledLayer = qobject_cast<Esri::ArcGISRuntime::ArcGISTiledLayer *>(sender());
if (arcGISTiledLayer)
{
const Esri::ArcGISRuntime::ArcGISMapServiceInfo &mapSvcInfo = arcGISTiledLayer->mapServiceInfo();
if (m_layerParameter.displayName.isEmpty()) // if not already set use mapName of Service
m_layerParameter.displayName = mapSvcInfo.mapName();
m_layerParameter.description = mapSvcInfo.description();
m_layerParameter.svcDescription = mapSvcInfo.serviceDescription();
} // if (arcGISTiledLayer)
DEBUG_LOG(m_logger) << LOGFUNCTION << " doneLoading: '" << layername << "'"
<< " new m_layerParameter:" << m_layerParameter;
// maybe the para weren`t applied because not loaded..
// so do it here (again);
applyLayerParameterToEsri();
// trigger reCalulation of minMaxScale:
emit layerDoneLoading(m_logicalName);
// trigger update of gui:
emit layerParameterChanged(m_logicalName, m_layerParameter);
DEBUG_LOG(m_logger) << LOGFUNCTION << " doneLoading: '" << layername << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void LogicalLayer::onLayerLoadStatusChanged(const Esri::ArcGISRuntime::LoadStatus &loadStatus)
{
INFO_LOG(m_logger) << LOGFUNCTION << " START" << " m_logicalName='" << m_logicalName << "'"
<< " new loadStatus=" << loadStatus;
}
Esri::ArcGISRuntime::GraphicsOverlay *LogicalLayer::createGraphicLayer(const TechnicalLayerType &technicalLayerType,
RenderingMode renderingMode)
{
Esri::ArcGISRuntime::GraphicsOverlay *graphicalOverlay =
createGraphicLayerInternal(technicalLayerType, renderingMode);
if (technicalLayerType != TechnicalLayerType::GraphicsLayer_Dict)
return graphicalOverlay;
// get access to esri dictionary:
const AdvancedSymbology::SymbolDictionary &dict = m_map->symbolDictionary();
const AdvancedSymbology::SymbolDictionaryImpl &dictImpl = static_cast<const AdvancedSymbology::SymbolDictionaryImpl &>(dict);
Esri::ArcGISRuntime::DictionarySymbolStyle *esriDict = const_cast<Esri::ArcGISRuntime::DictionarySymbolStyle *>(dictImpl.getEsriSymbolDictionary());
if (!esriDict)
return graphicalOverlay;
// make sure, that dict is already loaded:
if (esriDict->loadStatus() == Esri::ArcGISRuntime::LoadStatus::Loaded)
{
// create renderer
Esri::ArcGISRuntime::DictionaryRenderer *dictRenderer =
new Esri::ArcGISRuntime::DictionaryRenderer(esriDict, this);
// Handle errors of DictionaryRenderer:
connect(dictRenderer, &Esri::ArcGISRuntime::DictionaryRenderer::errorOccurred,
this, &LogicalLayer::onRendererError);
dictRenderer->setTextVisible(true);
// set Renderer to layer:
// take care that the old one is deleted:
Esri::ArcGISRuntime::Renderer *oldRenderer = graphicalOverlay->renderer();
if (oldRenderer)
{
FATAL_LOG(m_logger) << LOGFUNCTION << " replace/destroy/remove old renderer, is this intended???";
oldRenderer->deleteLater(); // Qt-Doc: 'It is safe to call this function more than once;'
}
// assign new:
graphicalOverlay->setRenderer(dictRenderer);
INFO_LOG(m_logger) << LOGFUNCTION
<< " requested (logical) layername ='" << m_logicalName << "' -> with Renderer"
<< " of symbologyType=" << dict.symbologyType();
}
else
{
FATAL_LOG(m_logger) << LOGFUNCTION << " SymbolDictionary not loaded!!!";
FATAL_LOG(m_logger) << LOGFUNCTION << " SymbolDictionary not loaded!!!";
FATAL_LOG(m_logger) << LOGFUNCTION << " SymbolDictionary not loaded!!!";
// do not return 'nullptr' to avoid crash on further operations
}
return graphicalOverlay;
}
QString LogicalLayer::createInternalLayerName(const QString &logicalLayerName, const TechnicalLayerType &technicalLayerType)
{
return logicalLayerName + QLatin1Char('#') + toString(technicalLayerType);
}
Esri::ArcGISRuntime::GraphicsOverlay *LogicalLayer::createGraphicLayerInternal(const TechnicalLayerType &technicalLayerType,
RenderingMode renderingMode)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " m_logicalName='" << m_logicalName << "'"
<< " technicalLayerType=" << technicalLayerType
<< " m_graphicalOverlays.size()=" << m_graphicalOverlays.size()
<< " m_graphicsOverlayListModel.size()=" << m_graphicsOverlayListModel->size();
if (! m_graphicsOverlayListModel)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no m_graphicsOverlayListModel";
return nullptr;
}
const QString &esriLayername = createInternalLayerName(m_logicalName, technicalLayerType);
Esri::ArcGISRuntime::GraphicsOverlay *graphicalOverlay =
new Esri::ArcGISRuntime::GraphicsOverlay();
graphicalOverlay->setOverlayId(esriLayername);
static long s_counter = 0;
graphicalOverlay->setObjectName(QStringLiteral("GraphicsOverlay_%1_'%2'").arg(s_counter++).arg(esriLayername));
connect(graphicalOverlay, &Esri::ArcGISRuntime::GraphicsOverlay::destroyed, this, [this, graphicalOverlay] () {
INFO_LOG(m_logger) << " GraphicsOverlay is destroyed , objectName='" << graphicalOverlay->objectName() << "'";
});
// Extrusion requires
// a) GraphicsRenderingMode::Dynamic and
// b) SurfacePlacement::Relative
if (technicalLayerType == TechnicalLayerType::GraphicsLayer_3D_Extrusion)
{
graphicalOverlay->setRenderingMode(Esri::ArcGISRuntime::GraphicsRenderingMode::Dynamic);
} else {
switch (renderingMode)
{
case RenderingMode::Dynamic:
graphicalOverlay->setRenderingMode(Esri::ArcGISRuntime::GraphicsRenderingMode::Dynamic);
break;
case RenderingMode::Static:
graphicalOverlay->setRenderingMode(Esri::ArcGISRuntime::GraphicsRenderingMode::Static);
break;
default:
// empty
break;
}
}
#if ACRGIS_VERSION < 0x640500 // 100.5.0 / Update 5
// deprecated in V100.5, replaced by GeoView::setSelectionProperties()
graphicalOverlay->setSelectionColor(Qt::magenta);
#endif // ACRGIS_VERSION
m_graphicalOverlays.append(graphicalOverlay);
m_graphicsOverlayListModel->append(graphicalOverlay);
// store it for lookup in own container:
m_graphicOverlayContainer[esriLayername] = graphicalOverlay;
// set SurfacePlacement
Esri::ArcGISRuntime::LayerSceneProperties lsp = graphicalOverlay->sceneProperties();
// Extrusion requires
// a) GraphicsRenderingMode::Dynamic and
// b) SurfacePlacement::Relative
if (technicalLayerType == TechnicalLayerType::GraphicsLayer_3D_Extrusion)
lsp.setSurfacePlacement(Esri::ArcGISRuntime::SurfacePlacement::Relative);
else if (technicalLayerType == TechnicalLayerType::GraphicsLayer_3D_Absolute)
lsp.setSurfacePlacement(Esri::ArcGISRuntime::SurfacePlacement::Absolute);
else
lsp.setSurfacePlacement(static_cast<Esri::ArcGISRuntime::SurfacePlacement>(m_surfacePlacement)); // save due to static_assert
graphicalOverlay->setSceneProperties(lsp);
Esri::ArcGISRuntime::GraphicsRenderingMode esriRenderingMode = graphicalOverlay->renderingMode();
INFO_LOG(m_logger) << LOGFUNCTION << " created GraphicsOverlay: "
<< " logicalName='" << m_logicalName << "'"
<< " technicalLayerType=" << technicalLayerType
<< " esriLayerName='" << esriLayername << "'"
<< " esriRenderModeStr=" << esriRenderingMode // Check operator
<< " surfacePlacement=" << m_surfacePlacement
<< " curIdx=" << m_graphicsOverlayListModel->indexOf(graphicalOverlay)
<< " graphicalOverlay=" << graphicalOverlay;
// Handle errors of layer:
connect(graphicalOverlay, &Esri::ArcGISRuntime::GraphicsOverlay::errorOccurred,
this, &LogicalLayer::onGraphicsOverlayLayerError);
// GraphicsOverlay need no doneLoading :-)
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " m_logicalName='" << m_logicalName << "'"
<< " technicalLayerType=" << technicalLayerType
<< " m_graphicalOverlays.size()=" << m_graphicalOverlays.size()
<< " m_graphicsOverlayListModel.size()=" << m_graphicsOverlayListModel->size();
return graphicalOverlay;
}
void LogicalLayer::applyLayerParameterToEsri()
{
int graphicsOverlayListModelSize = -1;
if (m_graphicsOverlayListModel)
graphicsOverlayListModelSize = m_graphicsOverlayListModel->size();
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " m_logicalName='" << m_logicalName << "'"
<< " m_layer=" << m_layer
<< " m_graphicalOverlays.size()=" << m_graphicalOverlays.size()
<< " m_graphicsOverlayListModel.size()=" << graphicsOverlayListModelSize
<< " lp=" << m_layerParameter;
// process Layer (e.g. Basemap)
// Esri::ArcGISRuntime::Layer
Esri::ArcGISRuntime::Layer *esriLayer = m_layer;
if (esriLayer)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_logicalName='" << m_logicalName << "'"
<< " CURRENT loadStatus =" << esriLayer->loadStatus();
if (esriLayer->loadStatus() != Esri::ArcGISRuntime::LoadStatus::Loaded)
{
INFO_LOG(m_logger) << LOGFUNCTION << " not loaded yet, skip"
<< " m_logicalName='" << m_logicalName << "'"
<< " m_layer=" << m_layer
<< " lp=" << m_layerParameter;
return;
}
esriLayer->setVisible(m_layerParameter.visible);
esriLayer->setOpacity(m_layerParameter.opacity / 100.0f);
if (m_layerParameter.minScaleValid())
esriLayer->setMinScale(m_layerParameter.minScale);
else
{
ERROR_LOG(m_logger) << LOGFUNCTION << " minScale not valid, set to '0'"
<< " m_logicalName='" << m_logicalName << "'";
esriLayer->setMinScale(0);
}
// Sonderfall (leider)
// Für 3D wird maxScale hart auf '0' gesetzt, damit beim Neigen keine basemap
// (besser basemapkachel) ausgeblendet wird.
if ((m_layerType == LayerType::Basemap) &&
(m_map->mode() == Mode::Mode_3D)
)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " set maxScale to '0', because Basemap and Mode_3D for: "
<< "m_logicalName='" << m_logicalName << "'";
esriLayer->setMaxScale(0);
} else {
if (m_layerParameter.maxScaleValid())
esriLayer->setMaxScale(m_layerParameter.maxScale);
else
{
ERROR_LOG(m_logger) << LOGFUNCTION << " maxScale not valid, set to '0'"
<< " m_logicalName='" << m_logicalName << "'";
esriLayer->setMaxScale(0);
}
} // : Sonderfall
// for layer supporting bightness, contrast, gamma:
if (esriLayer->layerType() == Esri::ArcGISRuntime::LayerType::ArcGISTiledLayer)
{
Esri::ArcGISRuntime::ArcGISTiledLayer *tiledLayer =
static_cast<Esri::ArcGISRuntime::ArcGISTiledLayer *>(esriLayer);
tiledLayer->setBrightness(m_layerParameter.brightness);
tiledLayer->setContrast(m_layerParameter.contrast);
tiledLayer->setGamma(m_layerParameter.gamma);
}
} // if (esriLayer)
// process GraphicsOverlay
for (Esri::ArcGISRuntime::GraphicsOverlay *graphicsOverlay : qAsConst(m_graphicalOverlays))
{
graphicsOverlay->setVisible(m_layerParameter.visible);
graphicsOverlay->setOpacity(m_layerParameter.opacity / 100.0);
if (m_layerParameter.minScaleValid())
graphicsOverlay->setMinScale(m_layerParameter.minScale);
if (m_layerParameter.maxScaleValid())
graphicsOverlay->setMaxScale(m_layerParameter.maxScale);
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " m_logicalName='" << m_logicalName << "'";
}
void LogicalLayer::setOrderOfGraphicalOverlays(const QVector<TechnicalLayerType> &orderOfGraphicalOverlays)
{
m_orderOfGraphicalOverlays = orderOfGraphicalOverlays;
}
//------------------------------------------------------------------------------
QLatin1String toString(const TechnicalLayerType &value)
{
switch (value)
{
case TechnicalLayerType::GraphicsLayer:
return QLatin1String("GraphicsLayer");
case TechnicalLayerType::GraphicsLayer_Dict:
return QLatin1String("GraphicsLayer_Dict");
case TechnicalLayerType::GraphicsLayer_3D_Extrusion:
return QLatin1String("GraphicsLayer_3DExtrusion");
case TechnicalLayerType::GraphicsLayer_3D_Absolute:
return QLatin1String("GraphicsLayer_3D_Absolute");
}
return QLatin1String("TechnicalLayerTypeERROR");
}
std::ostream &operator<<(std::ostream &stream, const TechnicalLayerType &value)
{
stream << toString(value).latin1();
return stream;
}
} // namespace SituationMap
+190
View File
@@ -0,0 +1,190 @@
/**
* @RDE_HEADER@
* MapLayer
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/map.h>
#include <Map/layer_parameter.h>
#include <Common/situation_map_logging.h>
//ESRI
#include <arcgis/LayerListModel.h>
#include <arcgis/Layer.h>
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/GraphicsOverlayListModel.h>
// Qt
#include <QVector>
//----------------------------------------------------------------------------
namespace SituationMap
{
class MapImpl;
enum class TechnicalLayerType
{
GraphicsLayer,
GraphicsLayer_Dict,
GraphicsLayer_3D_Extrusion,
GraphicsLayer_3D_Absolute
};
SITUATIONMAP_EXPORT QLatin1String toString(const TechnicalLayerType &value);
class LogicalLayer final : public QObject
{
Q_OBJECT
using BaseClass = QObject;
public:
/*! \name Creation, Destruction */
//@{
explicit LogicalLayer(const QString &logicalName, QObject *parent = nullptr);
~LogicalLayer();
void init(SituationMap::Map *map,
Esri::ArcGISRuntime::LayerListModel *layerListModel,
Esri::ArcGISRuntime::GraphicsOverlayListModel *graphicsOverlayListModel);
void fini();
void setLayerType(const LayerType &layerType);
void setBasemapType(const BasemapType &basemapType);
RenderingMode renderingMode() const; ///< has to be set early
void setRenderingMode(RenderingMode renderingMode); ///< has to be set early
SurfacePlacement surfacePlacement() const; ///< has to be set early
void setSurfacePlacement(SurfacePlacement surfacePlacement); ///< has to be set early
void setOrderOfGraphicalOverlays(const QVector<TechnicalLayerType> &orderOfGraphicalOverlays);
//@}
/*! \name Some names */
//@{
QString logicalName() const;
void setLogicalName(const QString &logicalName);
static QString createInternalLayerName(const QString &logicalLayerName,
const TechnicalLayerType &technicalLayerType);
//@}
/*! \name Access to some types */
//@{
LayerType layerType() const;
BasemapType basemapType() const;
//@}
/*! \name Access to LayerParameter*/
//@{
const LayerParameter &layerParameter() const;
void setLayerParameter(const LayerParameter &layerParameter);
//@}
/*! \name Main Access without creation on demand*/
//@{
void setLayer(Esri::ArcGISRuntime::Layer *layer);
Esri::ArcGISRuntime::Layer *layer() const;
QList<Esri::ArcGISRuntime::GraphicsOverlay *> getCurrentGraphicalOverlaysOrdered() const;
QList<QPair<QString, Esri::ArcGISRuntime::GraphicsOverlay *>> getCurrentGraphicalOverlaysOrderedWithName() const;
//@}
/*! \name Main Access with creation on demand*/
//@{
Esri::ArcGISRuntime::GraphicsOverlay *getCreateGraphicalOverlay(const TechnicalLayerType &technicalLayerType);
//@}
/*! \name Access to LayerIndex*/
//@{
int layerIndex() const;
//@}
protected:
QString m_logicalName;
// Some enums:
LayerType m_layerType = LayerType::Undefined;
BasemapType m_basemapType = BasemapType::Undefined;
RenderingMode m_graphicsOverlayRenderingMode = RenderingMode::Dynamic;
SurfacePlacement m_surfacePlacement = SurfacePlacement::Draped;
// visi, opac, brigth, gamma, contrast...
LayerParameter m_layerParameter;
// to enable/allow GraphicalOverlays and set their order:
QVector<TechnicalLayerType> m_orderOfGraphicalOverlays;
// ESRI:
Esri::ArcGISRuntime::Layer *m_layer = nullptr; // store BasemapLayer
QVector<Esri::ArcGISRuntime::GraphicsOverlay *> m_graphicalOverlays; // store GraphicsOverlays (unordered)
int m_retryCounter = 0;
signals:
void layerCreatedOnDemand(const QString &logicalName);
void layerDoneLoading(const QString &logicalName);
void layerParameterChanged(const QString &logicalName, const LayerParameter &newLayerParameter);
protected slots:
void onLayerError(const Esri::ArcGISRuntime::Error &error);
void onGraphicsOverlayLayerError(const Esri::ArcGISRuntime::Error &error);
void onRendererError(const Esri::ArcGISRuntime::Error &error);
void onLayerDoneLoading(const Esri::ArcGISRuntime::Error &loadError); // not for GraphicOverlays
void onLayerLoadStatusChanged(const Esri::ArcGISRuntime::LoadStatus &loadStatus); // not for GraphicOverlays
private:
// to be able to handle with BaseLayers (got from Map)
Esri::ArcGISRuntime::LayerListModel *m_layerListModel = nullptr;
// to be able to handle with GraphicalOverlays (got from GeoView)
Esri::ArcGISRuntime::GraphicsOverlayListModel *m_graphicsOverlayListModel = nullptr;
// to be able to create DictionaryRenderer:
SituationMap::Map *m_map = nullptr;
// Since GraphicOverlays don`t support setName yet:
// (and objectname returns an empty string in any case):
mutable QMap<QString, Esri::ArcGISRuntime::GraphicsOverlay *> m_graphicOverlayContainer; // workaround
Esri::ArcGISRuntime::GraphicsOverlay *createGraphicLayer(const TechnicalLayerType &technicalLayerType,
RenderingMode renderingMode = RenderingMode::Dynamic);
Esri::ArcGISRuntime::GraphicsOverlay *createGraphicLayerInternal(const TechnicalLayerType &technicalLayerType,
RenderingMode renderingMode = RenderingMode::Dynamic);
void applyLayerParameterToEsri();
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
std::ostream &operator<< (std::ostream &stream,
const SituationMap::TechnicalLayerType &value);
} // namespace SituationMap
File diff suppressed because it is too large. Load diff
+520
View File
@@ -0,0 +1,520 @@
/**
* @RDE_HEADER@
* Interface-Implementation of Map
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/map.h> // Interface
#include <SideBar/side_bar.h> // Interface
#include <Common/situation_map_logging.h>
// ESRI
#include <arcgis/SpatialReference.h>
#include <arcgis/MapTypes.h>
#include <arcgis/MapViewTypes.h>
#include <arcgis/Error.h>
namespace Esri
{
namespace ArcGISRuntime
{
class Map;
class Basemap;
class GeoView;
class MapGraphicsView;
class Scene;
class SceneGraphicsView;
class GraphicsOverlay;
class Layer;
class Point;
}
}
class Grid;
//----------------------------------------------------------------------------
namespace SituationMap
{
class LayerFactoryImpl;
class ObjectIndicator;
class LocationDisplay;
class ElevationMap;
class ElevationMapManager;
namespace RB
{
class RubberbandBase;
}
/**
* @brief The MapImpl
*
*
*/
class SITUATIONMAP_EXPORT MapImpl : public SituationMap::Map
{
Q_OBJECT
using BaseClass = SituationMap::Map;
public:
explicit MapImpl(QWidget *parent = nullptr);
virtual ~MapImpl();
MapImpl(const MapImpl &other) = delete;
MapImpl &operator=(const MapImpl &other) = delete;
bool init(const Mode mapMode_2D3D) override;
bool load() override;
Mode mode() const override;
void setParent(QWidget *parent, bool bBeginDestroyFini) override;
double animationDuration() const override;
void setAnimationDuration(double durationSec) override;
void addMapWidget() override;
bool setTextBox(const PixelTextBoxData &ptbd, bool enableGeometryModifications = true) override;
bool removeTextBox() override;
protected:
bool init2D();
bool initFeature();
bool init3D();
bool fini();
void fini2D();
void fini3D();
QWidget *addMapWidget_AddToScene();
QWidget *addMapWidget_OwnWidget();
void setupLocationToElevationHelper();
static QImage renderCompass(const QColor &colorNorth,
const QColor &colorSouth,
const QColor &colorBackground,
int halfWidth, int halfHeight,
bool b_3D = false);
public:
bool isPositionStringVisible() const override;
void showPositionString(bool visible = true) override;
void setPositionStringColor(const QColor &foregroundColor, const QColor &backgroundColor) override;
void setPartitionTime(const qint64 &time) override;
void setReplay(bool b) override;
bool isReplay() const override;
bool isExerciseTimeStringVisible() const override;
void showExerciseTimeString(bool visible = false) override;
void setExerciseTimeStringColor(const QColor &foregroundColor, const QColor &backgroundColor) override;
bool isTimeStringVisible() const override;
void setTimeString(const QString &timeString) override;
void showTimeString(bool visible = true) override;
void setTimeStringColor(const QColor &foregroundColor, const QColor &backgroundColor) override;
bool isCompassVisible() const override;
void showCompass(bool visible = true) override;
void addSideBar() override;
void removeSideBar() override;
QPointer<SideBar> sideBar() override;
static bool applyLicense(const QString &licenseStr, const QStringList &extensions = QStringList());
static bool applyNetworkCacheSettings(const EsriNetworkCacheSettings &esriNetworkCacheSettings);
static EsriNetworkCacheSettings networkCacheSettings();
Esri::ArcGISRuntime::Map *getEsriMap() const;
Esri::ArcGISRuntime::Basemap *getEsriBasemap() const;
Esri::ArcGISRuntime::SpatialReference getEsriSpatialReference() const;
Esri::ArcGISRuntime::Scene *getEsriScene() const;
Esri::ArcGISRuntime::GeoView *getEsriGeoView() const;
Esri::ArcGISRuntime::MapGraphicsView *getEsriMapGraphicsView() const;
Esri::ArcGISRuntime::SceneGraphicsView *getEsriSceneGraphicsView() const;
QGraphicsView *getEsriQGraphicsView() const;
QString toDumpString() const;
bool isLoaded() const override;
QGraphicsView *getQGraphicsView() const override;
// bool readSightFanElevationMap(const QString &filePath);
// std::shared_ptr<ElevationMap> getSightFanElevationMap() const;
bool initElevationMapManager(const QStringList &filePaths_rough_to_fine);
std::shared_ptr<ElevationMapManager> getElevationMapManager() const;
double pixelSizeMeter() override;
double pixelSizeGeographic() override;
public:
bool isReady() const override;
DrawStatus drawStatus() const override;
int wkid() const override;
/*! \name Surface: Below Ground, introduced with V100.5.0 */
//@{
SituationMap::NavigationConstraint navigationConstraint() const override;
bool setNavigationConstraint(SituationMap::NavigationConstraint NavigationConstraint) override;
float baseSurfaceOpacity() const override;
bool setBaseSurfaceOpacity(float surfaceOpacity) override;
//@}
OverviewMapHandler &overviewMapHandler() const override;
OperatorMapSettings operatorMapSettings() const override;
bool restoreOperatorMapSettings(const OperatorMapSettings &oms) override;
// Layer :
LayerFactory &layerFactory() const override;
virtual LayerFactoryImpl &layerFactoryImpl() const;
int basemapLayerCount() const;
int graphicsOverlayCount() const;
int technicalLayerCount() const override;
// convenience :
virtual Esri::ArcGISRuntime::GraphicsOverlay *getGraphicsLayer(const QString &logicalName);
virtual Esri::ArcGISRuntime::GraphicsOverlay *getGraphicsLayerDict(const QString &logicalName);
virtual Esri::ArcGISRuntime::GraphicsOverlay *getGraphicsLayer3DExtrusion(const QString &logicalName);
virtual Esri::ArcGISRuntime::GraphicsOverlay *getGraphicsLayer3D_SpAbsolute(const QString &logicalName);
// : convenience
// : Layer
// Grid
Grid &grid() const override;
// Symbol Dictionary :
bool setSymbologyType(AdvancedSymbology::SymbologyType symbologyType) override;
AdvancedSymbology::SymbologyType symbologyType() override;
AdvancedSymbology::SymbolDictionary &symbolDictionary() const override;
bool isSymbolDictionaryLoaded() const override;
// : Symbol Dictionary
// Object Creation : (create MapObjects using a factory)
ObjectFactory &objectFactory() override;
// For object management (ObjectContainer)
virtual ObjectContainer &objectContainer();
void setObjectContainer(QSharedPointer<ObjectContainer> objectContainer) override;
PartyIdToColorFunctor &partyIdToColorFunctor() const override;
void setPartyIdToColorFunctor(QSharedPointer<PartyIdToColorFunctor> partyIdToColor) override;
OverlayHandler &overlayHandler() const override;
/*! \name JumpPoints... */
//@{
bool addJumpPoint(const QString &name, const Point3D &originGeo, double mapScale = -1) override;
bool removeJumpPoint(const QString &name) override;
QStringList jumpPointNames() const override;
//@}
void initCoordTranslationFunctor() override;
CoordTranslationFunctor *coordTranslationFunctor() const override;
// : Object Creation
// Map Extent :
Viewpoint2D initialViewpoint() const override;
void setInitialViewpoint(const Viewpoint2D &initialViewpoint) override;
Viewpoint2D viewpoint2D() const override;
void setViewpoint2D(const Viewpoint2D &viewpoint, float durationSeconds = -1.0) override;
void setViewpoint2DAndWait(const Viewpoint2D &viewpoint) override;
void setViewpoint2DInternal(const Viewpoint2D &viewpoint, bool andWait, float durationSeconds = -1.0);
QPointF centerPoint() const override;
void setCenterPoint(const QPointF &center, double scale = -1.0, float durationSeconds = -1.0) override;
void zoomToArea(const QVector<QPointF> &coords, int paddingInDIPS) override;
void setTrainingsAreaExtent(const QVector<QPointF> &trainingsAreaExtent) override;
QRectF extent() const override;
void setExtent(const QRectF &extent) override;
void setExtent(double xmin, double ymin, double xmax, double ymax) override;
/**
* @brief SetExtentMapWorld is for INTERNAL use only
* @param xmin
* @param ymin
* @param xmax
* @param ymax
*/
virtual void setExtentMapWorld(double xmin, double ymin, double xmax, double ymax);
// QRectF fullExtent() const override;
double scale() const override;
void setScale(double absoluteScale) override;
void setMinScale(double minScaleToLimitZoomOut) override;
double minScale() const override;
void setMaxScale(double maxScaleToLimitZoomIn) override;
double maxScale() const override;
bool scaleByArea() const override;
// Rotation
double rotation() const override;
void setRotation(double absoluteAngleDegrees) override;
// for 3D:
Camera camera() const override;
void setCamera(const Camera &camera, float durationSeconds = -1.0) override;
// coordinate conversion
QPointF mapCoordinateToViewPortWindowPoint(const QPointF &mapPos) const override;
QPointF mapCoordinateToViewPortWindowPoint3D(const Point3D &mapPos) const override;
QPointF viewPortWindowPointToMapCoordinate(const QPointF &pixelCoord) const override; // ret = Point3D
Point3D viewPortWindowPointToMapCoordinate3D(const QPointF &pixelCoord) const override;
QPointF geoCoordinateToViewPortWindowPoint(const QPointF &geoPos) const override;
QPointF geoCoordinateToViewPortWindowPoint3D(const Point3D &geoPos) const override;
QPointF viewPortWindowPointToGeoCoordinate(const QPointF &pixelCoord) const override; // ret = Point3D
Point3D viewPortWindowPointToGeoCoordinate3D(const QPointF &pixelCoord) const override;
QString geoToMgrsString(const QPointF &geoPos) const override;
QString mapToMgrsString(const QPointF &mapPos) const override;
GeometryModelRenderer *createGeometryModelRenderer(const QString &layerName,
GeometryModel *geometryModel,
QObject *parent = nullptr) override;
// Rubberbands
RB::RubberbandFactory &rubberbandFactory() const override;
// Pick objects by area
bool pickMapObjectsArea() const override;
// EventHandling
void installDropEventManager(DropEventManager *demgr) override;
void deinstallDropEventManager(DropEventManager *demgr) override;
// // MapObjectIndicator
// bool createMapObjectIndicator(UniqueMapId uniqueId) override;
// void removeMapObjectIndicator() override;
// UniqueMapId mapObjectIndicator() const override;
// ObjectIndicator
ObjectIndicator& objectIndicator() override;
// LocationDisplay
LocationDisplay &locationDisplay() override;
// PixelBasedTextBoxHandler
PixelBasedTextBoxHandler &pixelBasedTextBoxHandler() override;
// Tools (Measurement, Viewshed...)
Measurement &measurement() override;
TerrainProfile &terrainProfile() override;
Viewshed &viewshed() override;
// Rocket
double rocketDuration() const override;
void setRocketDuration(double durationSec) override;
void startRocket() override;
bool rocketIsRunning() const override;
// Symbol scale
double symbolScale() const override;
void setSymbolScale(double symbolScale) override;
void installViewPortEventFilter(QObject *eventFilter) override;
void removeViewPortEventFilter(QObject *eventFilter) override;
QPointer<MapNotifier> createMapNotifier() const override;
// Screenshot etc:
QImage toImageSync() override;
QUuid toImage() override;
virtual bool toPdf(const QString &outputFilename,
const QString &title,
const QString &footerLeft,
const QString &footerCenter,
const QString &footerRight,
const QColor &textColor = QColor()) override;
protected:
bool eventFilter(QObject *obj, QEvent *e) final;
public slots:
void onHandleMouseMove(int windowPosX, int windowPosY); // used by EventDispatcher (Rubberband)
protected slots:
void onErrorGeometryEngine(const Esri::ArcGISRuntime::Error &error);
void onErrorMap(const Esri::ArcGISRuntime::Error &error);
void onErrorScene(const Esri::ArcGISRuntime::Error &error);
void onErrorOperationalLayer(const Esri::ArcGISRuntime::Error &error);
void onErrorBasemap(const Esri::ArcGISRuntime::Error &error);
void onViewpointChanged();
void onRectChanged();
void onKeyPressEvent(const QKeyEvent& keyEvent);
void onLoadStatusChanged(Esri::ArcGISRuntime::LoadStatus loadStatus);
void onDoneLoading(const Esri::ArcGISRuntime::Error &loadError);
void onMouseScenePositionChanged(const Point3D &locationScene, bool isValid);
void onRubberbandPickMapObjectsApply(RB::RubberbandBase *rubberband);
void onExportImageCompleted(const QUuid &taskId, const QImage &image);
void finishToPdf(const QImage &image,
const QString &outputFilename,
const QString &title,
const QString &footerLeft,
const QString &footerCenter,
const QString &footerRight,
const QColor &textColor = QColor());
void onPartyChanged(int changedPartyId = -1);
void onAllPartysChanged();
void onCheckMapReadyAndSymbolDictionaryLoaded();
friend class LayerFactoryImpl;
friend class GridImpl;
friend class OverviewMapHandlerImpl;
void emitAllElevationSourcesLoaded()
{
emit allElevationSourcesLoaded();
onCheckMapReadyAndSymbolDictionaryLoaded();
}
void emitLayerCreateComplete(const QString &layername)
{
emit layerCreateComplete(layername);
}
void emitLayerCreateError(const QString &layername, const QString &error)
{
emit layerCreateError(layername, error);
}
void emitLayerOrderChanged()
{
emit layerOrderChanged();
}
void emitLayerParameterChanged(const QString &layername, const LayerParameter &newLayerParameter)
{
emit layerParameterChanged(layername, newLayerParameter);
}
void emitVisualComponentChanged(const VisualComponent &visualComponent);
void onClickMapObject(const QList<UniqueMapId> &uniqueMapIdList, const QPointF &pos);
void onContextClick(const QList<UniqueMapId> &uniqueMapIdList, const QPointF &pos);
void onWheelZoom(qreal factor, const QPointF &cursorPositionMap);
void onRocketTimerZoomOutFinished();
void onRocketTimerZoomInFinished();
void onName(const QString &name, const QPointF &coordGeo);
void onJumpPointRequest(const QPointF &viewPos);
void onEsriRotate();
// void showFunctionDialog();
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;
};
//----------------------------------------------------------------------------
// MapConnector (Map -> MapObjects)
//----------------------------------------------------------------------------
// forward:
class AbstractMapObjectImpl;
class MapConnector : public QObject
{
Q_OBJECT
public:
bool connectMapObject(MapImpl *mapImpl, AbstractMapObjectImpl *mapObjImpl);
public Q_SLOTS:
void onMapExtentChanged();
private:
MapImpl *m_mapImpl = nullptr; // map (emitting)
AbstractMapObjectImpl *m_mapObjectImpl = nullptr; // 'host' (to be updated)
};
} // namespace SituationMap
//----------------------------------------------------------------------------
+325
View File
@@ -0,0 +1,325 @@
/**
* @RDE_HEADER@
* MapLayer
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "map_layer.h"
#include <MapImpl/map_impl.h>
#include <MapImpl/layer_factory_impl.h>
#include <Map/situation_map_utils.h>
// ESRI
#include <arcgis/Layer.h>
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/ArcGISTiledLayer.h>
// Qt
// #include <QDebug>
namespace SituationMap
{
MapLayer::MapLayer(const QString &logicalName,
LayerType layerType)
: m_name(logicalName)
, m_layerType(layerType)
{
}
bool MapLayer::isValid() const
{
return ((m_layerType != LayerType::Undefined) && (!m_name.isEmpty()));
}
QString MapLayer::name() const
{
return m_name;
}
LayerType MapLayer::type() const
{
return m_layerType;
}
//------------------------------------------------------------------------------
bool MapLayerOrderedList::addLayer(const QString &name,
LayerType layerType)
{
if (name.isEmpty() || layerType == LayerType::Undefined)
return false;
// Do NOT allow same names for different LayerTypes:
if (contains(name, LayerType::Undefined))
return false;
MapLayer temp(name, layerType);
if (!temp.isValid())
return false;
if (layerType == LayerType::Basemap)
{
// Basemap -> bottom;
int cnt = this->count(LayerType::Basemap);
m_mapLayerList.insert(cnt, temp);
}
else if (layerType == LayerType::Overlay)
{
// Overlay -> directly above Basemap;
int cntBM = this->count(LayerType::Basemap);
int cntOVL = this->count(LayerType::Overlay);
m_mapLayerList.insert(cntBM + cntOVL, temp);
}
else
m_mapLayerList.append(temp);
return true;
}
bool MapLayerOrderedList::contains(const QString &name,
LayerType layerType) const
{
bool found = false;
for (const MapLayer &layer : m_mapLayerList)
{
if (layerType == LayerType::Undefined)
{
if (layer.name().compare(name) == 0)
{
found = true;
break;
}
}
else
{
if ((layer.type() == layerType) && (layer.name().compare(name) == 0))
{
found = true;
break;
}
}
}
return found;
}
MapLayer MapLayerOrderedList::getLayer(const QString &name) const
{
for (const MapLayer &layer : m_mapLayerList)
{
if (layer.name().compare(name) == 0)
return layer;
}
return MapLayer();
}
bool MapLayerOrderedList::removeLayer(const QString &name)
{
// since a layername has to be unique independent it´s layertype:
// QMutableListIterator does not realy work, Problems with MapLayer ???
// QMutableListIterator<MapLayer> contIt(m_mapLayerList);
// while (contIt.hasNext());
// {
// contIt.next();
// const MapLayer & layer = contIt.value();
// if (layer.name().compare(name) == 0)
// {
// contIt.remove();
// return true;
// }
// }
for (int i = 0; i < m_mapLayerList.size(); ++i)
{
const MapLayer &layer = m_mapLayerList[i];
if (layer.name().compare(name) == 0)
{
m_mapLayerList.removeAt(i);
return true;
}
}
return false;
}
int MapLayerOrderedList::count(LayerType layerType) const
{
if (layerType == LayerType::Undefined)
return m_mapLayerList.size();
int counter = 0;
for (const MapLayer &mapLayer : qAsConst(m_mapLayerList))
{
if (mapLayer.type() == layerType)
++counter;
}
return counter;
}
QStringList MapLayerOrderedList::layernamesOfType(const QStringList &layernames, LayerType layerType)
{
QStringList layerCont;
for (const QString &lName : layernames)
{
const MapLayer &ml = getLayer(lName);
if (!ml.isValid())
continue;
if (ml.type() == layerType)
layerCont.push_back(lName);
}
return layerCont;
}
QStringList MapLayerOrderedList::layernamesOfTypeOther(const QStringList &layernames)
{
QStringList layerCont;
for (const QString &lName : layernames)
{
const MapLayer &ml = getLayer(lName);
if (!ml.isValid())
continue;
if (
(ml.type() != LayerType::Basemap) &&
(ml.type() != LayerType::Overlay) &&
(ml.type() != LayerType::Operational) &&
(ml.type() != LayerType::EmergencyCall) &&
(ml.type() != LayerType::Undefined)
)
layerCont.push_back(lName);
}
return layerCont;
}
QStringList orderStringListPerType(const QStringList &slToOrder, const QStringList &slRequested)
{
//
// !!!do not lose one layer existing in slToOrder!!!
//
QStringList slNew = slToOrder;
int newIdx = 0;
for (const QString &name : slRequested)
{
int idx = slNew.indexOf(name);
if (idx >= 0)
{ // found
slNew.move(idx, newIdx++);
}
}
return slNew;
}
bool MapLayerOrderedList::orderLayerTo(const QStringList &orderedLayernamesReq, bool *bRequestedOrderIsValid)
{
// cur =ArcGISTiledLayer_URL_0;ArcGISTiledLayer_URL_1;ArcGISTiledLayer_URL_2;OverlayLayer_1001;OverlayLayer_1002;...;JumpPoints;SnaphotOverlay
// should=ArcGISTiledLayer_URL_0;ArcGISTiledLayer_URL_1;ArcGISTiledLayer_URL_2;OverlayLayer_1002;OverlayLayer_1001;...;JumpPoints
QStringList slMapLayerList;
slMapLayerList.reserve(m_mapLayerList.size());
for (const MapLayer &ml : qAsConst(m_mapLayerList))
slMapLayerList.push_back(ml.name());
//
// !!!do not lose one layer existing in the m_mapLayerList!!!
//
QStringList slBasemapNew;
{
QStringList slCur = layernamesOfType(slMapLayerList, LayerType::Basemap);
QStringList slReq = layernamesOfType(orderedLayernamesReq, LayerType::Basemap);
slBasemapNew = orderStringListPerType(slCur, slReq);
}
QStringList slOverlayNew;
{
QStringList slCur = layernamesOfType(slMapLayerList, LayerType::Overlay);
QStringList slReq = layernamesOfType(orderedLayernamesReq, LayerType::Overlay);
slOverlayNew = orderStringListPerType(slCur, slReq);
}
QStringList slOperationalNew;
{
QStringList slCur = layernamesOfType(slMapLayerList, LayerType::Operational);
QStringList slReq = layernamesOfType(orderedLayernamesReq, LayerType::Operational);
slOperationalNew = orderStringListPerType(slCur, slReq);
}
QStringList slOtherNew;
{
QStringList slCur = layernamesOfTypeOther(slMapLayerList);
QStringList slReq = layernamesOfTypeOther(orderedLayernamesReq);
slOtherNew = orderStringListPerType(slCur, slReq);
}
// Emergency should stay on top
QStringList slEmergencyCallNew;
{
QStringList slCur = layernamesOfType(slMapLayerList, LayerType::EmergencyCall);
QStringList slReq = layernamesOfType(orderedLayernamesReq, LayerType::EmergencyCall);
slEmergencyCallNew = orderStringListPerType(slCur, slReq);
}
// put all basemapLayer to front, then overlayLayers, then others and finally EmergencyCall on top
const QStringList &slMapLayerListNEW = slBasemapNew + slOverlayNew + slOperationalNew + slOtherNew + slEmergencyCallNew;
const bool localRequestedOrderIsValid = slMapLayerListNEW == orderedLayernamesReq;
if (bRequestedOrderIsValid)
{
// just request, do NOT modify !!!
*bRequestedOrderIsValid = localRequestedOrderIsValid;
} else {
QList<MapLayer> mlListNEW;
for(const QString &lName : slMapLayerListNEW)
{
const MapLayer &ml = getLayer(lName);
if (!ml.isValid())
continue;
mlListNEW.push_back(ml);
}
m_mapLayerList = mlListNEW;
}
return true;
}
int MapLayerOrderedList::count() const
{
return m_mapLayerList.count();
}
QStringList MapLayerOrderedList::layerNamesOrdered() const
{
QStringList ll;
ll.reserve(m_mapLayerList.size());
for (const MapLayer &layer : m_mapLayerList)
ll.append(layer.name());
return ll;
}
QStringList MapLayerOrderedList::layerNamesOrdered(LayerType layerType) const
{
QStringList ll;
for (const MapLayer &layer : m_mapLayerList)
{
if (layer.type() == layerType)
ll.append(layer.name());
}
return ll;
}
} // namespace SituationMap
+94
View File
@@ -0,0 +1,94 @@
/**
* @RDE_HEADER@
* MapLayer
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/situation_map_types.h>
#include <Common/situation_map_logging.h>
//----------------------------------------------------------------------------
namespace SituationMap
{
class MapImpl;
class MapLayer
{
public:
explicit MapLayer(const QString &logicalName = QString(),
LayerType layerType = LayerType::Undefined);
bool isValid() const;
// for some read-only values :
QString name() const;
LayerType type() const;
// : for some read-only values
protected:
QString m_name;
LayerType m_layerType = LayerType::Undefined;
};
/**
* @brief The MapLayerOrderedList class will store an ordered List of the MapLayer.
* The List can be used to get the orderedList of layernames as well as
* control the parameters like on/off, brightness etc.
*/
class MapLayerOrderedList final : public QObject
{
Q_OBJECT
using BaseClass = QObject;
public:
MapLayerOrderedList() = default;
~MapLayerOrderedList() = default;
/**
* @brief addLayer add Layername. Will NOT be added if name already exists (regardless layerType)
* !All LayerType::Basemap will below all other!
* @param name Layername to be added. Must be unique!
* @param layerType layerType to be added (Undefined is NOT allowed)
* @return true on success, otherwise false
*/
bool addLayer(const QString &name, LayerType layerType);
bool contains(const QString &name, LayerType layerType) const;
MapLayer getLayer(const QString &name) const;
bool removeLayer(const QString &name); // layername has to be unique
int count() const;
int count(LayerType layerType) const;
/**
* @brief Order layer according new list
* @param orderedLayernamesReq requested new order
* @param bRequestedOrderIsValid if set just a check will be performed. The result bool contains the check result.
* @return true on success, otherwise false
*/
bool orderLayerTo(const QStringList &orderedLayernamesReq, bool *bRequestedOrderIsValid = nullptr);
QStringList layerNamesOrdered() const;
QStringList layerNamesOrdered(LayerType layerType) const;
protected:
QStringList layernamesOfType(const QStringList &layernames, LayerType layerType);
QStringList layernamesOfTypeOther(const QStringList &layernames);
QList<MapLayer> m_mapLayerList; ///< using logical layernames
};
} // namespace SituationMap
+425
View File
@@ -0,0 +1,425 @@
/**
* @RDE_HEADER@
* Interface-Implementation of DangerArea
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "measurement_impl.h"
#include "layer_factory_impl.h"
#include "coord_translation_functor_impl.h"
#include "Rubberbands/rubberband_factory.h"
#include "Rubberbands/rubberband_multi_point_feature.h"
#include "Map/object_factory.h"
#include "MapObjects/polyline.h"
#include "MapObjects/geo_image.h"
#include "MapObjects/text.h"
#include "MapImpl/abstract_map_object_impl.h"
// Esri
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/GeometryEngine.h>
// Qt
namespace SituationMap
{
class MeasurementImpl::Private
{
public:
Private() = default;
~Private() = default;
// ESRI data:
// Own data:
SituationMap::MapImpl *m_mapImpl = nullptr;
QString m_toolsLayername;
QColor m_color = Qt::magenta;
Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr;
QPointer<RB::RubberbandMultiPointFeature> m_rubberband;
QVector<Point3D> m_coordsGeo;
QVector<Point3D> m_coordsMap;
QVector<double> m_lengthPerSegment;
double m_totalLength = 0;
RefPointer<Polyline> m_polyline;
QVector<RefPointer<GeoImage>> m_markerCont;
RefPointer<Text> m_textTotalLength;
void resizeMarkerCont(int newSize)
{
if (newSize < 1)
return;
// increase
if (m_markerCont.size() < newSize)
{
m_markerCont.resize(newSize);
return;
}
// decrease
while (m_markerCont.size() > newSize)
{
// do not call 'takeLast' on empty container. But since newSize is >= 0...
RefPointer<GeoImage> item = m_markerCont.takeLast();
item->removeMySelf();
}
}
}; // class MeasurementImpl::Private
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// class MeasurementImpl
//-----------------------------------------------------------------------------
MeasurementImpl::MeasurementImpl(SituationMap::MapImpl *mapImpl)
: Measurement()
, QObject()
, m_logger(getLogger("MeasurementImpl"))
, d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
d->m_mapImpl = mapImpl;
init();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
MeasurementImpl::~MeasurementImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
this->clear();
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
long MeasurementImpl::measureLength()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
// cleanup old
clear();
startRubberband();
return -1;
}
bool MeasurementImpl::startRubberband()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
// verify mapReady
if (!d->m_mapImpl || !d->m_mapImpl->isReady())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Failure - map is not ready yet!!!";
return false;
}
RB::RubberbandMultiPointFeature *rubberband = this->createRubberband();
if (!rubberband)
return false;
d->m_rubberband = rubberband;
connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &MeasurementImpl::onRbAbort, Qt::UniqueConnection);
connect(rubberband, &SituationMap::RB::Rubberband::interimResultsChanged, this, &MeasurementImpl::onRbInterimResultsChanged, Qt::UniqueConnection);
connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &MeasurementImpl::onRbApply, Qt::UniqueConnection);
connect(rubberband, &SituationMap::RB::RubberbandMultiPointFeature::currentCursorPositionChanged, this, &MeasurementImpl::onCurrentCursorPositionChanged, Qt::UniqueConnection);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return true;
}
void MeasurementImpl::clear()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
d->m_coordsGeo.clear();
d->m_coordsMap.clear();
d->m_lengthPerSegment.clear();
d->m_totalLength = 0;
if (d->m_polyline)
d->m_polyline->removeMySelf();
d->m_polyline.reset();
QMutableVectorIterator<RefPointer<GeoImage>> it(d->m_markerCont);
while (it.hasNext())
{
RefPointer<GeoImage> &marker = it.next();
if (marker)
marker->removeMySelf();
}
d->m_markerCont.clear();
if (d->m_textTotalLength)
d->m_textTotalLength->removeMySelf();
d->m_textTotalLength.reset();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
bool MeasurementImpl::displayPolyline()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (d->m_coordsGeo.size() < 2)
return false;
// create
if (!d->m_polyline)
{
d->m_polyline = d->m_mapImpl->objectFactory().create<Polyline>(d->m_toolsLayername,
QStringLiteral("MeasurementPolyline"));
d->m_polyline->setLineStyle(LineStyle(d->m_color, 3));
d->m_polyline->setScaleEnable(true);
d->m_polyline->setPickable(false);
}
// apply data
d->m_polyline->setPositions(d->m_coordsGeo);
for (int segment = 0; segment < d->m_lengthPerSegment.size(); ++segment)
{
const int dist = std::round(d->m_lengthPerSegment[segment]);
d->m_polyline->setTextOnSegment(segment, QStringLiteral("%1m").arg(dist));
// OutlineColor/Width is not supported yet
} // for
// update
if (!d->m_polyline->updateView())
ERROR_LOG(m_logger) << LOGFUNCTION << " polyline::updateView failed";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return true;
}
bool MeasurementImpl::displayVertices()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
const int size = d->m_coordsGeo.size();
if (size < 1)
return false;
d->resizeMarkerCont(size);
int idx = 0;
QMutableVectorIterator<RefPointer<GeoImage>> it(d->m_markerCont);
while (it.hasNext())
{
RefPointer<GeoImage> &marker = it.next();
if (!marker.valid())
{
marker = d->m_mapImpl->objectFactory().create<GeoImage>(d->m_toolsLayername,
QStringLiteral("MeasurementMarker%1").arg(idx));
marker->setProxyMarker(MarkerSymbolType::Circle);
marker->setProxyMarkerColor(d->m_color);
marker->setProxyMarkerSize(7);
marker->setPickable(false);
}
marker->setOrigin(d->m_coordsGeo[idx]);
// update
if (!marker->updateView())
ERROR_LOG(m_logger) << LOGFUNCTION << " marker::updateView failed";
++idx;
} // while
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return true;
}
bool MeasurementImpl::displayTotalLength()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (d->m_coordsGeo.size() < 2)
return false;
if (!d->m_textTotalLength.valid())
{
d->m_textTotalLength = d->m_mapImpl->objectFactory().create<Text>(d->m_toolsLayername,
QStringLiteral("MeasurementText"));
TextStyle ts = AbstractMapObjectImpl::defaultTextStyle();
ts.setTextColor(d->m_color);
ts.setBold(true);
ts.setSize(ts.size() * 1.5);
d->m_textTotalLength->setTextStyle(ts);
}
const int dist = std::round(d->m_totalLength);
d->m_textTotalLength->setIdentifier(QStringLiteral("%1m").arg(dist));
d->m_textTotalLength->setOrigin(d->m_coordsGeo.last());
// update
if (!d->m_textTotalLength->updateView())
ERROR_LOG(m_logger) << LOGFUNCTION << " textTotalLength::updateView failed";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return true;
}
bool MeasurementImpl::calculateLength(const QVector<Point3D> &mapCoords)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (mapCoords.size() < 2)
return false;
static Esri::ArcGISRuntime::GeometryEngine *gEngine = Esri::ArcGISRuntime::GeometryEngine::instance();
const Esri::ArcGISRuntime::SpatialReference &sr = d->m_mapImpl->getEsriSpatialReference();
d->m_totalLength = 0;
d->m_lengthPerSegment.clear();
d->m_lengthPerSegment.resize(mapCoords.size() - 1);
QVectorIterator<Point3D> it(mapCoords);
int segment = 0;
while (it.hasNext())
{
const Point3D &mapCoord1 = it.next();
if (!it.hasNext())
break;
const Point3D &mapCoord2 = it.peekNext();
DEBUG_LOG(m_logger) << LOGFUNCTION << " segment=" << segment;
Esri::ArcGISRuntime::Point geom1(mapCoord1.x(), mapCoord1.y(), mapCoord1.z(), sr);
Esri::ArcGISRuntime::Point geom2(mapCoord2.x(), mapCoord2.y(), mapCoord2.z(), sr);
// const double distancePlanar = gEngine->distance(geom1, geom2);
// gEngine->distance return wrong results, if Topographic.tpk is used. Most likly due to wkid.
// Use distanceGeodetic, that works (at least in 2D) for 32632, 3857
Esri::ArcGISRuntime::GeodeticDistanceResult geodeticDistanceResult = gEngine->distanceGeodetic(geom1, geom2,
Esri::ArcGISRuntime::LinearUnitId::Meters,
Esri::ArcGISRuntime::AngularUnitId::Degrees,
Esri::ArcGISRuntime::GeodeticCurveType::Geodesic);
d->m_lengthPerSegment[segment] = geodeticDistanceResult.distance();
d->m_totalLength += geodeticDistanceResult.distance();
DEBUG_LOG(m_logger) << LOGFUNCTION
<< " segment=" << segment
<< " -> m_totalLength = " << d->m_totalLength;
++segment;
} // while
DEBUG_LOG(m_logger) << LOGFUNCTION << " distancePlanar = " << d->m_totalLength;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return true;
}
void MeasurementImpl::onRbAbort(RB::RubberbandBase *rbnd)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
clear();
if (!rbnd)
return;
DEBUG_LOG(m_logger) << LOGFUNCTION << " rb abort";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void MeasurementImpl::onRbInterimResultsChanged(RB::RubberbandBase *rbnd)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!rbnd)
return;
d->m_coordsMap = SituationMap::convert2D3D(rbnd->coordinateVector(RB::CoordinateSystem::Map));
d->m_coordsGeo = SituationMap::convert2D3D(rbnd->coordinateVector(RB::CoordinateSystem::Geo));
calculateLength(d->m_coordsMap);
displayTotalLength();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void MeasurementImpl::onRbApply(RB::RubberbandBase *rbnd)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!rbnd)
return;
d->m_coordsMap = SituationMap::convert2D3D(rbnd->coordinateVector(RB::CoordinateSystem::Map));
d->m_coordsGeo = SituationMap::convert2D3D(rbnd->coordinateVector(RB::CoordinateSystem::Geo));
DEBUG_LOG(m_logger) << LOGFUNCTION << " rb apply, d->m_coordsMap.size()=" << d->m_coordsMap.size();
calculateLength(d->m_coordsMap);
displayPolyline();
displayVertices();
displayTotalLength();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void MeasurementImpl::onCurrentCursorPositionChanged(const Point3D &mapCoord)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " mapCoord = " << mapCoord;
d->m_coordsMap = SituationMap::convert2D3D(d->m_rubberband->coordinateVector(RB::CoordinateSystem::Map));
d->m_coordsGeo = SituationMap::convert2D3D(d->m_rubberband->coordinateVector(RB::CoordinateSystem::Geo));
// DEBUG_LOG(m_logger) << LOGFUNCTION << " rb apply, d->m_coordsMap.size()=" << d->m_coordsMap.size();
// append coord of current mouse position:
d->m_coordsMap += mapCoord;
const Point3D &geoCoord = d->m_mapImpl->coordTranslationFunctor()->mapToGeographic(mapCoord);
d->m_coordsGeo += geoCoord;
calculateLength(d->m_coordsMap);
displayPolyline();
displayVertices();
displayTotalLength();
}
void MeasurementImpl::init()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!d->m_graphicsLayer)
{
LayerFactoryImpl &layerFactory = d->m_mapImpl->layerFactoryImpl();
d->m_toolsLayername = layerFactory.toolLayer();
d->m_graphicsLayer = layerFactory.getGraphicsLayer(d->m_toolsLayername);
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
RB::RubberbandMultiPointFeature *MeasurementImpl::createRubberband()
{
// remove any pending/active rubberbands of our map:
RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl);
RB::RubberbandMultiPointFeature *rubberband =
d->m_mapImpl->rubberbandFactory().create<RB::RubberbandMultiPointFeature>(this);
if (!rubberband || !rubberband->rubberbandIsAlive())
return nullptr;
rubberband->setAutoDelete(true);
rubberband->enableClosed(false);
rubberband->minimumPointCount(2);
rubberband->maximumPointCount(11);
// rubberband->maximumLength(maxSegmentLength);
return rubberband;
}
} // namespace SituationMap
+66
View File
@@ -0,0 +1,66 @@
/**
* @RDE_HEADER@
* Interface-Implementation of DangerArea
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/measurement.h> // Interface
#include "map_impl.h"
#include "Rubberbands/rubberband_multi_point_feature.h"
// Qt
//----------------------------------------------------------------------------
namespace SituationMap
{
class MeasurementImpl : public QObject, public virtual Measurement
{
Q_OBJECT
public:
explicit MeasurementImpl(SituationMap::MapImpl *mapImpl);
virtual ~MeasurementImpl();
MeasurementImpl(const MeasurementImpl &other) = delete;
MeasurementImpl &operator=(MeasurementImpl &other) = delete;
long measureLength() override;
void clear() override;
bool displayPolyline();
bool displayVertices();
bool displayTotalLength();
bool calculateLength(const QVector<Point3D> &mapCoords);
protected Q_SLOTS:
void onRbAbort(RB::RubberbandBase *rbnd);
void onRbInterimResultsChanged(RB::RubberbandBase *rbnd);
void onRbApply(RB::RubberbandBase *rbnd);
void onCurrentCursorPositionChanged(const Point3D &mapCoord);
private:
void init();
bool startRubberband();
RB::RubberbandMultiPointFeature *createRubberband();
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
+134
View File
@@ -0,0 +1,134 @@
/**
* @RDE_HEADER@
* Interface-Implementation of ObjectContainer
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "object_container_impl.h"
namespace SituationMap
{
ObjectContainerCTC::ObjectContainerCTC(const QSharedPointer<IdGenerator> &idGenerator)
: BaseClass(idGenerator)
, m_logger(getLogger("ObjectContainerCTC"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
ObjectContainerCTC::~ObjectContainerCTC()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
ObjectContainer::UniqueObjectId ObjectContainerCTC::insertObject(ObjectContainer::Object *object)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
UniqueObjectId newId = m_idGenerator->getNextId();
m_map.insert(newId, RefPointer<Object>(object));
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " uniqueId = " << newId;
return newId;
}
ObjectContainer::UniqueObjectId ObjectContainerCTC::insertObject(ObjectContainer::Object *object, const ObjectContainer::UniqueObjectId &uid)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if ((uid >= m_idGenerator->nextFreeId()) || (INVALID_MAP_ID == uid) || containsObject(uid))
{
// uid is unvalid and can't be used. In this case insert the map object with a new id.
return insertObject(object);
}
m_map.insert(uid, RefPointer<Object>(object));
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " uniqueId = " << uid;
return uid;
}
bool ObjectContainerCTC::removeObject(const ObjectContainer::UniqueObjectId &id)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " uniqueId = " << id;
bool success = (m_map.remove(id) == 1);
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " uniqueId = " << id
<< " success = " << success;
return success;
}
void ObjectContainerCTC::clear()
{
m_map.clear();
}
ObjectContainer::Object *ObjectContainerCTC::getObject(const ObjectContainer::UniqueObjectId &id) const
{
auto it = m_map.constFind(id);
if (it == m_map.constEnd())
return nullptr;
RefPointer<Object> obj = it.value();
return obj.get();
}
QList<ObjectContainer::Object *> ObjectContainerCTC::getAllObjects() const
{
QList<ObjectContainer::Object *> list;
QMapIterator< UniqueObjectId, RefPointer<Object> > it(m_map);
while (it.hasNext())
{
it.next();
const RefPointer<Object> &obj = it.value();
if (obj)
list.append(const_cast<ObjectContainer::Object *>(obj.get()));
} // while
return list;
}
QList<ObjectContainer::Object *> ObjectContainerCTC::getAllObjects(const std::type_index &type) const
{
QList<ObjectContainer::Object *> list;
QMapIterator< UniqueObjectId, RefPointer<Object> > it(m_map);
while (it.hasNext())
{
it.next();
const RefPointer<Object> &obj = it.value();
// !!! get typ_index of object, not pointer to object: !!!
const Object *objGet = obj.get();
if (type == std::type_index(typeid(*objGet)))
list.append(const_cast<ObjectContainer::Object *>(objGet));
} // while
return list;
}
QList<ObjectContainer::UniqueObjectId> ObjectContainerCTC::getAllKeys() const
{
return m_map.keys();
}
bool ObjectContainerCTC::containsObject(const ObjectContainer::UniqueObjectId &id) const
{
return m_map.contains(id);
}
qint64 ObjectContainerCTC::countObjects() const
{
return m_map.size();
}
} // namespace SituationMap
+105
View File
@@ -0,0 +1,105 @@
/**
* @RDE_HEADER@
* Interface-Implementation of ObjectContainer
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/object_container.h>
#include <Common/ref_pointer.h>
#include <Common/situation_map_logging.h>
// Qt
#include <QMap>
#include <typeindex>
//----------------------------------------------------------------------------
namespace SituationMap
{
/**
* @brief The ObjectContainerCTC class implements the interface SituationMap::ObjectContainer.
* It is a map of UniqueMapId to MapObjects.
* It uses a external IdGenerator to generate a next UniqueMapId returned
* while inserting.
* For documentation of inherented methods, see interface class SituationMap::ObjectContainer.
*/
class SITUATIONMAP_EXPORT ObjectContainerCTC : public ObjectContainer
{
using BaseClass = ObjectContainer;
public:
/*! \name Construction, Destruction */
//@{
/**
* @brief ObjectContainerCTC.
* @param idGenerator to be used
* (can be shared among different ObjectContainer instances).
*/
explicit ObjectContainerCTC(const QSharedPointer<IdGenerator> &idGenerator);
virtual ~ObjectContainerCTC();
//@}
/*! \name Insert, Remove, Clear */
//@{
virtual UniqueObjectId insertObject( Object *object ) override;
virtual UniqueObjectId insertObject( ObjectContainer::Object *object
, const ObjectContainer::UniqueObjectId &uid ) override;
virtual bool removeObject( const UniqueObjectId &id ) override;
virtual void clear() override;
//@}
/*! \name Access */
//@{
virtual QList<UniqueObjectId> getAllKeys() const override;
virtual Object *getObject(const UniqueObjectId &id) const override; // Returns pointer to object
virtual QList<Object *> getAllObjects() const override;
virtual QList<Object *> getAllObjects(const std::type_index &type) const override; // without template
QMutableMapIterator<UniqueObjectId, RefPointer<Object>> mutableIterator() override
{
return QMutableMapIterator<UniqueObjectId, RefPointer<Object>> (m_map);
}
//@}
/*! \name Check */
//@{
virtual bool containsObject(const UniqueObjectId &id) const override;
//@}
/*! \name Count */
//@{
/**
* @brief Number of objects.
* @return Current number of objects.
*/
virtual qint64 countObjects() const override;
//@}
QMap<UniqueObjectId, RefPointer<Object> > m_map;
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
} // namespace SituationMap
+517
View File
@@ -0,0 +1,517 @@
/**
* @RDE_HEADER@
* Interface-Implementation of ObjectIndicator
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "object_indicator_impl.h"
#include <MapImpl/map_impl.h>
#include <Map/bounding_box.h>
#include <Map/object_factory.h>
#include <MapImpl/abstract_map_object_impl.h>
#include <ElevationMap/elevation_map_manager.h>
#include <MathUtils/simple_helper.h>
// ESRI
#include <arcgis/MapGraphicsView.h>
#include <arcgis/SceneGraphicsView.h>
// Qt
#include <QGraphicsView>
#include <cassert>
namespace SituationMap
{
//-----------------------------------------------------------------------------
// class ObjectIndicatorImpl
//-----------------------------------------------------------------------------
ObjectIndicatorImpl::ObjectIndicatorImpl()
: BaseClass()
, m_logger(getLogger("ObjectIndicatorImpl"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
m_markerSize = 15; // size of filled circle at the origin (indicating the center of the map)
m_lineWidth = 3; // lineWidth of (dashed) line from origin to target
// updateTimer:
m_updateTimer = new QTimer(this);
m_updateTimer->setSingleShot(true);
m_updateTimer->setInterval(100);
connect(m_updateTimer, &QTimer::timeout, this, &ObjectIndicatorImpl::updateImpl);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
bool ObjectIndicatorImpl::init(MapImpl *mapImpl)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
m_mapImpl = mapImpl;
if (!m_mapImpl)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " m_map_impl is nullptr, init failed";
return false;
}
// to ensure graceful shutdown: (remove QGraphicsItems BEFORE qGraphicsScene/qGraphicsView is destroyed:
connect(m_mapImpl, &Map::beginDestroy, this, &ObjectIndicatorImpl::onMapDestroyed, Qt::UniqueConnection);
m_qGraphicsView = static_cast<QGraphicsView *>(m_mapImpl->getEsriQGraphicsView());
if (m_qGraphicsView)
m_qGraphicsScene = m_qGraphicsView->scene();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return true;
}
ObjectIndicatorImpl::~ObjectIndicatorImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
remove();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
UniqueMapId ObjectIndicatorImpl::mapObjectToPointTo()
{
return m_uniqueIdToPointTo;
}
void ObjectIndicatorImpl::setMapObjectToPointTo(const UniqueMapId &newUniqueId, bool setSelected, int lifetimeMsec)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, newUniqueId = " << newUniqueId
<< ", (old m_uniqueIdToPointTo = " << m_uniqueIdToPointTo
<< ", lifetimeMsec=" << lifetimeMsec;
m_targetGeo2D = QPointF();
connect(m_mapImpl, &MapImpl::extentChangedDelayed,
this, &ObjectIndicatorImpl::update, Qt::UniqueConnection);
if (m_uniqueIdToPointTo != newUniqueId)
{
addRemoveCallback(m_uniqueIdToPointTo, AddRemove::Remove);
m_uniqueIdToPointTo = newUniqueId;
addRemoveCallback(m_uniqueIdToPointTo, AddRemove::Add);
}
update();
if (m_lifetimeTimer)
m_lifetimeTimer->stop();
if (lifetimeMsec > 1)
{
if (!m_lifetimeTimer)
{
m_lifetimeTimer = new QTimer(this);
m_lifetimeTimer->setSingleShot(true);
connect(m_lifetimeTimer.data(), &QTimer::timeout, this, &ObjectIndicatorImpl::remove); // lifetime expired
}
m_lifetimeTimer->setInterval(lifetimeMsec);
m_lifetimeTimer->start();
}
m_setSelected = setSelected;
if (m_setSelected)
setMapObjectSelected(m_setSelected);
TRACE_LOG(m_logger) << LOGFUNCTION << " END, newUniqueId = " << newUniqueId;
}
void ObjectIndicatorImpl::setPointToPointTo(const QPointF &targetGeo, int lifetimeMsec)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, targetGeo = " << targetGeo
<< ", lifetimeMsec=" << lifetimeMsec;
connect(m_mapImpl, &MapImpl::extentChangedDelayed,
this, &ObjectIndicatorImpl::update, Qt::UniqueConnection);
clearCurrentSelection();
addRemoveCallback(m_uniqueIdToPointTo, AddRemove::Remove);
m_uniqueIdToPointTo = INVALID_MAP_ID;
m_targetGeo2D = targetGeo;
update();
if (m_lifetimeTimer)
m_lifetimeTimer->stop();
if (lifetimeMsec > 1)
{
if (!m_lifetimeTimer)
{
m_lifetimeTimer = new QTimer(this);
m_lifetimeTimer->setSingleShot(true);
connect(m_lifetimeTimer.data(), &QTimer::timeout, this, &ObjectIndicatorImpl::remove); // lifetime expired
}
m_lifetimeTimer->setInterval(lifetimeMsec);
m_lifetimeTimer->start();
}
}
void ObjectIndicatorImpl::clearCurrentSelection()
{
ObjectContainer &objCont = m_mapImpl->objectContainer();
const QList<ObjectContainer::Object*> &allObj = objCont.getAllObjects();
for (ObjectContainer::Object* mapObj : allObj)
{
if (!mapObj)
continue;
if (!mapObj->selected())
continue;
mapObj->setSelected(false);
mapObj->updateView();
}
}
bool ObjectIndicatorImpl::update()
{
if (!m_updateTimer)
return false;
m_updateTimer->start(); // start is restart
return true;
}
bool ObjectIndicatorImpl::updateImpl()
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " START"
<< ", m_uniqueIdToPointTo = " << m_uniqueIdToPointTo;
if (!m_qGraphicsView)
return false;
if (!m_qGraphicsScene)
return false;
if (!SituationMap::isUniqueMapIdValid(m_uniqueIdToPointTo) && m_targetGeo2D.isNull())
return false;
//
// Line:
//
Point3D targetGeo;
QColor color = m_defaultColor;
// center:
bool targetIsValid = false;
if (!m_targetGeo2D.isNull())
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " target: m_targetGeo2D= " << m_targetGeo2D;
targetGeo = Point3D::fromQPointF(m_targetGeo2D);
targetIsValid = true;
} else {
DEBUG_LOG(m_logger) << LOGFUNCTION << " target: m_uniqueIdToPointTo= " << m_uniqueIdToPointTo;
if (!SituationMap::isUniqueMapIdValid(m_uniqueIdToPointTo))
return false;
targetIsValid = centerOfTarget(targetGeo, color);
}
QRectF rect = m_qGraphicsScene->sceneRect();
DEBUG_LOG(m_logger) << LOGFUNCTION << " rect: " << rect;
QBrush brush(color, Qt::SolidPattern);
QPen pen(brush, m_lineWidth, Qt::DashLine);
QPointF targetSceneCoordinate;
if (targetIsValid)
{
if (m_mapImpl->getElevationMapManager())
{
const double ele = m_mapImpl->getElevationMapManager()->elevation(targetGeo.x(), targetGeo.y());
targetGeo.rz() += ele; // targetGeo should contain an absolute z-value
}
QPointF targetWindowCoord = m_mapImpl->geoCoordinateToViewPortWindowPoint3D(targetGeo);
targetSceneCoordinate = m_qGraphicsView->mapToScene(targetWindowCoord.toPoint());
if (targetSceneCoordinate.isNull())
{
INFO_LOG(m_logger) << LOGFUNCTION << " targetSceneCoordinate invisible!!!?";
targetSceneCoordinate = rect.center();
}
QLineF lineData(rect.center(), targetSceneCoordinate);
if (!m_qGraphicsLine)
m_qGraphicsLine = m_qGraphicsScene->addLine(lineData, pen);
m_qGraphicsLine->setLine(lineData);
m_qGraphicsLine->setPen(pen);
DEBUG_LOG(m_logger) << LOGFUNCTION
<< ", rect.center() " << rect.center()
<< ", bCenterOfTarget " << targetIsValid
<< ", targetGeo:" << targetGeo
<< ", targetSceneCoordinate " << targetSceneCoordinate;
}
else
{
delete m_qGraphicsLine;
m_qGraphicsLine = nullptr;
targetSceneCoordinate = rect.center();
}
//
// ArrowTip:
//
m_arrowTip = QPixmap::fromImage(renderArrowHeadSymbol(color, 30, 10));
if (!m_qGraphicsArrowTip)
{
m_qGraphicsArrowTip = m_qGraphicsScene->addPixmap(m_arrowTip);
m_qGraphicsArrowTip->setOffset(m_arrowTip.width() / -2.0, 0);
}
m_qGraphicsArrowTip->setPixmap(m_arrowTip);
m_qGraphicsArrowTip->setPos(targetSceneCoordinate);
const double lineAngle = MathUtils::getAngleDegreeNorth(targetSceneCoordinate, rect.center());
const double angle = 360.0 - (lineAngle);
DEBUG_LOG(m_logger) << LOGFUNCTION << ", lineAngle " << lineAngle
<< ", angle " << angle;
// Sets the clockwise rotation angle, in degrees, around the Z axis.
m_qGraphicsArrowTip->setRotation(angle);
//
// centerMarker:
//
rect = QRect(rect.center().x() - m_markerSize * 0.5, rect.center().y() - m_markerSize * 0.5,
m_markerSize, m_markerSize);
if (!m_qGraphicsCenterMarker)
m_qGraphicsCenterMarker = m_qGraphicsScene->addEllipse(rect, pen, brush);
m_qGraphicsCenterMarker->setRect(rect);
m_qGraphicsCenterMarker->setPen(QPen(color, Qt::DashLine));
m_qGraphicsCenterMarker->setBrush(brush);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return true;
}
void ObjectIndicatorImpl::onMapDestroyed()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
remove();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
AbstractMapObjectImpl *ObjectIndicatorImpl::getObject(const UniqueMapId &uniqueId)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, uniqueId = " << uniqueId;
if (!SituationMap::isUniqueMapIdValid(uniqueId))
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueId invalid: " << uniqueId;
return nullptr;
}
if (!m_mapImpl->objectFactory().contains(uniqueId))
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_uniqueIdToPointTo not found: " << m_uniqueIdToPointTo;
return nullptr;
}
AbstractMapObjectImpl *impl = dynamic_cast<AbstractMapObjectImpl *>(m_mapImpl->objectFactory().idToMapObject(uniqueId));
TRACE_LOG(m_logger) << LOGFUNCTION << " END, uniqueId = " << uniqueId;
return impl;
}
bool ObjectIndicatorImpl::addRemoveCallback(const UniqueMapId &uniqueId, AddRemove addRemove)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, uniqueId = " << uniqueId
<< " addRemove = " << (int)addRemove;
if (!SituationMap::isUniqueMapIdValid(uniqueId))
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueId invalid: " << uniqueId;
return false;
}
AbstractMapObjectImpl *impl = getObject(uniqueId);
if (!impl)
return false;
const QString &objToPointToDebugInfo = impl->debugInfo();
DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueId is valid, uniqueId=" << uniqueId
<< ", (objToPointToDebugInfo=" << objToPointToDebugInfo << ")";
if (addRemove == AddRemove::Add)
impl->addChangedCallback(this);
else
impl->removeChangedCallback(this);
TRACE_LOG(m_logger) << LOGFUNCTION << " END, uniqueId = " << uniqueId
<< ", addRemove = " << (int)addRemove;
return true;
}
void ObjectIndicatorImpl::remove()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, m_uniqueIdToPointTo=" << m_uniqueIdToPointTo;
disconnect(this);
if (m_updateTimer)
m_updateTimer->stop();
addRemoveCallback(m_uniqueIdToPointTo, AddRemove::Remove);
// if we set the object selected, remove selection
if (m_setSelected)
setMapObjectSelected(false);
m_uniqueIdToPointTo = INVALID_MAP_ID;
m_targetGeo2D = QPointF();
delete m_qGraphicsCenterMarker;
delete m_qGraphicsLine;
delete m_qGraphicsArrowTip;
m_qGraphicsCenterMarker = nullptr;
m_qGraphicsLine = nullptr;
m_qGraphicsArrowTip = nullptr;
updateImpl();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, m_uniqueIdToPointTo=" << m_uniqueIdToPointTo;
}
void ObjectIndicatorImpl::onOriginPositionChanged()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " update...";
update();
}
bool ObjectIndicatorImpl::centerOfTarget(Point3D &objCenterPoint, QColor &objColor)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
//
// point to desired obj:
//
RefPointer<AbstractMapObject> objToPointTo;
if (!SituationMap::isUniqueMapIdValid(m_uniqueIdToPointTo))
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_uniqueIdToPointTo invalid: " << m_uniqueIdToPointTo;
return false;
}
if (!m_mapImpl->objectFactory().contains(m_uniqueIdToPointTo))
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_uniqueIdToPointTo not found: " << m_uniqueIdToPointTo;
return false;
}
objToPointTo = m_mapImpl->objectFactory().get(m_uniqueIdToPointTo);
// retrive values from objToPointTo:
if (!objToPointTo.valid())
{
WARNING_LOG(m_logger) << LOGFUNCTION << " objToPointTo not found";
return false;
}
AbstractMapObjectImpl *impl = dynamic_cast<AbstractMapObjectImpl *>(objToPointTo.get());
if (!impl)
{
WARNING_LOG(m_logger) << LOGFUNCTION << " dynamic_cast failed";
return false;
}
// get target position:
BoundingBox bbox;
objToPointTo->boundingBox(bbox);
if (!bbox.isValid())
WARNING_LOG(m_logger) << LOGFUNCTION << " boundingBox not valid: bbox = " << bbox;
const QString &objToPointToDebugInfo = objToPointTo->debugInfo();
DEBUG_LOG(m_logger) << LOGFUNCTION
<< ", objToPointTo is valid, uniqueIdToPointTo = " << m_uniqueIdToPointTo
<< ", (objToPointToDebugInfo = " << objToPointToDebugInfo << ")"
<< ", bbox= " << bbox;
objCenterPoint = bbox.center();
// objColor:
objColor = impl->majorColor();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return true;
}
QImage ObjectIndicatorImpl::renderArrowHeadSymbol(const QColor &color, int length, int halfWidth)
{
int width = halfWidth * 2;
QImage image(width, length, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&image);
painter.setRenderHints(QPainter::Antialiasing);
QPen pen(color);
pen.setWidth(2);
painter.setPen(pen);
painter.setBrush(QBrush(color, Qt::BrushStyle::SolidPattern));
// 2 ||
// | |
// 1 | | 3
QPolygon poly;
poly.reserve(3);
poly.append(QPoint(0, length));
poly.append(QPoint(halfWidth, 0));
poly.append(QPoint(width, length));
painter.drawPolygon(poly);
painter.end();
// image.save(QLatin1String("/tmp/Arrow.png"));
return image;
}
bool ObjectIndicatorImpl::setMapObjectSelected(bool selected)
{
// 'clear' current selection:
clearCurrentSelection();
if (!selected)
return true; // already done by clearCurrentSelection :-)
if (!SituationMap::isUniqueMapIdValid(m_uniqueIdToPointTo))
return false;
RefPointer<AbstractMapObject> objToPointTo = m_mapImpl->objectFactory().get(m_uniqueIdToPointTo);
// retrive values from objToPointTo:
if (!objToPointTo.valid())
{
WARNING_LOG(m_logger) << LOGFUNCTION << " objToPointTo not found";
return false;
}
AbstractMapObjectImpl *impl = dynamic_cast<AbstractMapObjectImpl *>(objToPointTo.get());
if (!impl)
{
WARNING_LOG(m_logger) << LOGFUNCTION << " dynamic_cast failed";
return false;
}
if (impl->selected() == selected)
return true;
impl->setSelected(selected);
return impl->updateView();
}
} // namespace SituationMap
+114
View File
@@ -0,0 +1,114 @@
/**
* @RDE_HEADER@
* Interface-Implementation of ObjectIndicator
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/object_indicator.h> // Interface
#include <MapImpl/map_impl.h>
#include <MapImpl/abstract_map_object_impl.h> // OriginPositionChangedCallback
// Qt
#include <QTimer>
#include <QString>
#include <QColor>
#include <QPointF>
#include <QGraphicsEllipseItem>
#include <QGraphicsLineItem>
//----------------------------------------------------------------------------
namespace SituationMap
{
/**
* @brief The ObjectIndicatorImpl can be used to ease the locating of an object.
* The ObjectIndicatorImpl can by parametrized with just the UniqueMapId to point to.
* update() will do the rest for you:
* - retrieve the object to point to,
* - get the bounding box of that object and
* - create an indicator from the current center of the map to the object.
* \note <b> If the object to point to disappears, you are responsible for removing the ObjectIndicatorImpl! </b>
*/
class SITUATIONMAP_EXPORT ObjectIndicatorImpl final : public QObject, public virtual ObjectIndicator, public virtual OriginPositionChangedCallback
{
Q_OBJECT
using BaseClass = QObject;
protected:
explicit ObjectIndicatorImpl();
virtual ~ObjectIndicatorImpl();
public:
ObjectIndicatorImpl(const ObjectIndicatorImpl &other) = delete;
ObjectIndicatorImpl &operator=(ObjectIndicatorImpl &other) = delete;
UniqueMapId mapObjectToPointTo() override;
void setMapObjectToPointTo(const UniqueMapId &newUniqueId, bool setSelected = false, int lifetimeMsec = -1) override;
void setPointToPointTo(const QPointF &targetGeo, int lifetimeMsec = -1) override;
void clearCurrentSelection();
protected Q_SLOTS:
void remove() override;
bool update();
bool updateImpl();
void onMapDestroyed();
protected:
enum class AddRemove
{
Add,
Remove
};
AbstractMapObjectImpl *getObject(const UniqueMapId &uniqueId);
bool addRemoveCallback(const UniqueMapId &uniqueId, AddRemove addRemove);
void onOriginPositionChanged() override;
bool centerOfTarget(Point3D &objCenterPoint, QColor &objColor);
static QImage renderArrowHeadSymbol(const QColor &color, int length, int halfWidth);
bool setMapObjectSelected(bool selected);
friend class MapImpl;
bool init(MapImpl *mapImpl);
// internal/admin
MapImpl *m_mapImpl{nullptr};
QGraphicsView *m_qGraphicsView{nullptr};
QGraphicsScene *m_qGraphicsScene{nullptr};
QGraphicsEllipseItem *m_qGraphicsCenterMarker{nullptr};
QGraphicsLineItem *m_qGraphicsLine{nullptr};
QPixmap m_arrowTip;
QGraphicsPixmapItem *m_qGraphicsArrowTip{nullptr};
QColor m_defaultColor{Qt::magenta};
int m_markerSize{1};
int m_lineWidth{1};
QTimer *m_updateTimer{nullptr};
QPointer<QTimer> m_lifetimeTimer;
// logical
UniqueMapId m_uniqueIdToPointTo{INVALID_MAP_ID}; // target to pointTo, #1
bool m_setSelected{false};
QPointF m_targetGeo2D; // target to pointTo, #2
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
} // namespace SituationMap
+644
View File
@@ -0,0 +1,644 @@
/**
* @RDE_HEADER@
* Interface of AbstractMapObject
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "overlay_handler_impl.h"
#include <Map/map.h>
#include <Map/layer_factory.h>
#include <Map/layer_parameter.h>
#include <Map/pixel_based_textbox_handler.h>
#include <Map/situation_map_utils.h>
#include <GeometryModel/geometry_model_factory.h>
#include <GeometryModel/geometry_pixelTextBox.h>
#include "geometry_model_renderer_esri.h"
#include <QFile>
//----------------------------------------------------------------------------
namespace SituationMap
{
OverlayHandler *OverlayHandler::create(Map *map)
{
return new OverlayHandlerImpl(map);
}
static const QLatin1String s_verlayLayerPrefix = QLatin1String("OverlayLayer_");
class OverlayData
{
public:
~OverlayData()
{
// graceful cleanup, destroy associated MapObject (adequate signal will be emitted)
if (m_geoModel)
m_geoModel->clearModel();
m_geoModelRenderer.clear();
m_geoModel.clear();
// : graceful cleanup
}
int m_key = -1;
QString m_logicalLayerName;
QString m_technicalLayerName; // just for debugging purpose
QSharedPointer<GeometryModel> m_geoModel;
QSharedPointer<GeometryModelRenderer> m_geoModelRenderer;
bool init(Map *map)
{
if (m_logicalLayerName.isEmpty())
return false;
m_geoModel = QSharedPointer<GeometryModel>(SituationMap::GeometryModelFactory::singleton()->create());
m_geoModelRenderer =
QSharedPointer<GeometryModelRenderer>(map->createGeometryModelRenderer(m_logicalLayerName, m_geoModel.data()));
m_geoModelRenderer->setNodesPickable(false); // we just want to display the overlay, no picking
return !m_geoModelRenderer.isNull();
}
QString toString() const
{
QString str;
str.append(QStringLiteral("m_key=%1").arg(m_key));
str.append(QStringLiteral(" m_logicalLayerName='%1'").arg(m_logicalLayerName));
str.append(QStringLiteral(" m_technicalLayerName='%1'").arg(m_technicalLayerName));
return str;
}
};
//-----------------------------------------------------------------------------
class OverlayHandlerImpl::Private
{
public:
Private() = default;
~Private() = default;
// ESRI data:
// Own data:
Map *m_map = nullptr;
private:
QMap<int, QSharedPointer<OverlayData>> m_overlayCont;
public:
QSharedPointer<OverlayData> &getOverlayData(int key)
{
if (m_overlayCont[key].isNull())
m_overlayCont[key] = QSharedPointer<OverlayData>(new OverlayData());
return m_overlayCont[key];
}
int remove(int key) { return m_overlayCont.remove(key); }
void clear() { m_overlayCont.clear(); }
int size() { return m_overlayCont.size(); }
QList<int> keys() { return m_overlayCont.keys(); }
bool contains(int key) { return m_overlayCont.contains(key); }
QMutableMapIterator<int, QSharedPointer<OverlayData>> mutableIterator()
{
return QMutableMapIterator<int, QSharedPointer<OverlayData>> (m_overlayCont);
}
}; // class OverlayHandlerImpl::Private
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
OverlayHandlerImpl::OverlayHandlerImpl(Map *map)
: BaseClass()
, m_logger(getLogger("OverlayHandlerImpl"))
, d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
d->m_map = map;
if (! d->m_map)
ERROR_LOG(m_logger) << LOGFUNCTION << " error no Map ";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
OverlayHandlerImpl::~OverlayHandlerImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
QString OverlayHandlerImpl::toString() const
{
QString str(QLatin1String("OverlayHandlerImpl"));
str.append(QStringLiteral(" m_overlayCont.size=%1").arg(d->size()));
return str;
}
bool OverlayHandlerImpl::setOverlay(int key, const QString &name, const QByteArray &overlayContent, bool isSnapshot)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " key=" << key
<< " name='" << name << "'"
<< " overlayContent.size()='" << overlayContent.size()
<< " isSnapshot=" << isSnapshot;
if (! d->m_map)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " error, no Map " << " key=" << key;
return false;
}
const bool contains = d->contains(key);
// data:
QSharedPointer<OverlayData> overlayData;
// create or update?
if (contains)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " Current11 SnaphotOverlay" << d->m_map->layerFactory().layerParameter(QStringLiteral("SnaphotOverlay"));
overlayData = d->getOverlayData(key);
// already created, but update displayname:
if (! d->m_map->layerFactory().addOverlayLayer(overlayData->m_logicalLayerName, name, isSnapshot))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " error on addOverlayLayer" << " key=" << key;
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " key=" << key;
return false;
}
}
else
{
DEBUG_LOG(m_logger) << " CurrentAA SnaphotOverlay" << d->m_map->layerFactory().layerParameter(QStringLiteral("SnaphotOverlay"));
overlayData = QSharedPointer<OverlayData>(new OverlayData());
// case 'create' -> create layer:
overlayData->m_key = key;
if (isSnapshot)
overlayData->m_logicalLayerName = d->m_map->layerFactory().snapshotOverlayLayername();
else
overlayData->m_logicalLayerName = QString(s_verlayLayerPrefix) + QString::number(key);
if (! d->m_map->layerFactory().addOverlayLayer(overlayData->m_logicalLayerName, name, isSnapshot))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " error on addOverlayLayer" << " key=" << key;
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " key=" << key;
return false;
}
// LayerFactory will make sure the technical layer is created.
DEBUG_LOG(m_logger) << LOGFUNCTION << " key=" << key << " overlayData init...";
if (! overlayData->init(d->m_map))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " error on init" << " key=" << key;
d->m_map->layerFactory().removeOverlayLayer(overlayData->m_logicalLayerName);
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " key=" << key;
return false;
}
} // if (contains)
bool success = false;
if (overlayContent.isEmpty())
{
// in case of restoring the OperatorMapSettings / toggle 2D<->3D)
// prepare layer WITHOUNT content
DEBUG_LOG(m_logger) << LOGFUNCTION << " END, overlayContent is empty, so just be prepared"
<< ", key=" << key
<< ", isSnapshot=" << isSnapshot;
success = true;
} else {
if (overlayData->m_geoModel && overlayData->m_geoModelRenderer)
{
if (!overlayData->m_geoModel->fromByteArray(overlayContent))
{
// overlayContent string is not a valid geoModel -> abort
ERROR_LOG(m_logger) << LOGFUNCTION << " error on fromString," << " key=" << key;
if (!d->m_map->layerFactory().removeOverlayLayer(overlayData->m_logicalLayerName))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " removeOverlayLayer failed," << " key=" << key;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " key=" << key;
return false;
}
// overlayData->m_geoModel->cleanModel(true); has been done in GeometryModel::fromString
DEBUG_LOG(m_logger) << LOGFUNCTION << " key=" << key << " render...";
if (overlayData->m_geoModelRenderer->render())
{
success = true;
}
else
{
ERROR_LOG(m_logger) << LOGFUNCTION << " error on render" << " key=" << key;
}
}
} // if (overlayContent.isEmpty())
if (success)
{
// add temporary data to internal map:
if (!contains)
d->getOverlayData(key) = overlayData;
//INFO_LOG(m_logger) << LOGFUNCTION << " olData=" << d->getOverlayData(key).data()->toString();
}
else
{
INFO_LOG(m_logger) << LOGFUNCTION << " removeOverlayLayer" << " key=" << key
<< ", m_logicalLayerName " << overlayData->m_logicalLayerName;
d->m_map->layerFactory().removeOverlayLayer(overlayData->m_logicalLayerName);
}
//
const QString &strRef =
overlayData->m_geoModel->getModelAttribute(SituationMap::GeometryModel::Attribute::MetaPixelBasedTextBoxes).toString();
const QVector<SituationMap::PixelTextBoxData> &ptbdCont =
SituationMap::PixelTextBoxData::pixelTextBoxContainerFromJsonStr(strRef);
PixelBasedTextBoxHandler &pixelBasedTextBoxHandler = d->m_map->pixelBasedTextBoxHandler();
// cleanup:
const int parentId = key; // key == fileId
pixelBasedTextBoxHandler.removeAllTextBoxesOfParent(parentId);
for (const SituationMap::PixelTextBoxData &ptbd : qAsConst(ptbdCont))
{
// create WITHIN loop, since it will be modified by createUpdateTextBox:
PBTB_ID pbtbId;
pbtbId.m_parentId = parentId;
QTextEdit *newTextEdit = pixelBasedTextBoxHandler.createTextBox(pbtbId, ptbd, false);
if (newTextEdit)
DEBUG_LOG(m_logger) << LOGFUNCTION << " new TextBox created: newTextEdit = " << newTextEdit
<< ", pbtbId=" << pbtbId
<< ", ptbd=" << ptbd;
else
ERROR_LOG(m_logger) << LOGFUNCTION << " createUpdateTextBox failed"
<< ", pbtbId=" << pbtbId
<< ", ptbd=" << ptbd;
} // for
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " key=" << key
<< ", size=" << d->size();
return success;
}
QByteArray OverlayHandlerImpl::content(int key) const
{
if (!d->contains(key))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " key=" << key << " unknown";
return QByteArray();
}
QSharedPointer<OverlayData> overlayData = d->getOverlayData(key);
return overlayData->m_geoModel->toByteArray();
}
bool OverlayHandlerImpl::reorderOverlays(const QVector<int> &orderOfIds)
{
QStringList layerOrder = d->m_map->layerFactory().layernames();
// Change the logical layer names in layerOrder list at all positions where a logical layer name has been map to an id ...
int idIndex = 0;
for (QStringList::iterator iter = layerOrder.begin(); layerOrder.end() != iter; ++iter)
{
QVector<int>::const_iterator orderIter = orderOfIds.cbegin();
// looking for the current logicalLayername in list of orderOfIds
while (orderOfIds.cend() != orderIter)
{
if (*iter == d->getOverlayData(*orderIter)->m_logicalLayerName)
{
// layer exists in orderOfIds lets change the order
*iter = d->getOverlayData(orderOfIds[idIndex])->m_logicalLayerName; // set new layername for this position
++idIndex;
break; // logical layer name is existing only once and we have changed the order hat this position, so we can leaf the while loop here
}
++orderIter;
}
}
// ready now give the new layer order to layer factory
if (!d->m_map->layerFactory().orderLayerTo(layerOrder))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " orderLayerTo failed.";
return false;
}
return true;
} // void OverlayHandlerImpl::reorderOverlays( const QVector<int> & orderOfIds )
double OverlayHandlerImpl::minScale(int key) const
{
if (! d->contains(key))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " key=" << key << " unknown";
return -1.0;
}
QSharedPointer<OverlayData> overlayData = d->getOverlayData(key);
const QString &logicalLayername = overlayData->m_logicalLayerName;
LayerParameter lp = d->m_map->layerFactory().layerParameter(logicalLayername);
return lp.minScale;
}
bool OverlayHandlerImpl::setMinScale(int key, double minScale)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " key=" << key << " minScale=" << minScale;
if (! d->contains(key))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " key=" << key << " unknown";
return false;
}
QSharedPointer<OverlayData> overlayData = d->getOverlayData(key);
const QString &logicalLayername = overlayData->m_logicalLayerName;
// get LayerParameter, update data and set:
LayerParameter lp = d->m_map->layerFactory().layerParameter(logicalLayername);
lp.minScale = minScale;
if (! d->m_map->layerFactory().setLayerParameter(logicalLayername, lp))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " error updating LayerParameter, key=" << key;
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " key=" << key << " minScale=" << minScale;
return true;
}
double OverlayHandlerImpl::maxScale(int key) const
{
if (! d->contains(key))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " key=" << key << " unknown";
return -1.0;
}
QSharedPointer<OverlayData> overlayData = d->getOverlayData(key);
const QString &logicalLayername = overlayData->m_logicalLayerName;
LayerParameter lp = d->m_map->layerFactory().layerParameter(logicalLayername);
return lp.maxScale;
}
bool OverlayHandlerImpl::setMaxScale(int key, double maxScale)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " key=" << key << " maxScale=" << maxScale;
if (! d->contains(key))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " key=" << key << " unknown";
return false;
}
QSharedPointer<OverlayData> overlayData = d->getOverlayData(key);
const QString &logicalLayername = overlayData->m_logicalLayerName;
// get LayerParameter, update data and set:
LayerParameter lp = d->m_map->layerFactory().layerParameter(logicalLayername);
lp.maxScale = maxScale;
if (! d->m_map->layerFactory().setLayerParameter(logicalLayername, lp))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " error updating LayerParameter, key=" << key;
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " key=" << key << " maxScale=" << maxScale;
return true;
}
double OverlayHandlerImpl::opacity(int key) const
{
if (! d->contains(key))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " key=" << key << " unknown";
return 0.0;
}
QSharedPointer<OverlayData> overlayData = d->getOverlayData(key);
const QString &logicalLayername = overlayData->m_logicalLayerName;
// get LayerParameter, update data and set:
LayerParameter lp = d->m_map->layerFactory().layerParameter(logicalLayername);
return double(lp.opacity) / 100.0;
}
bool OverlayHandlerImpl::setOpacity(int key, double opacity)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " key=" << key << " opacity=" << opacity;
if (! d->contains(key))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " key=" << key << " unknown";
return false;
}
opacity = (opacity < 0.0) ? 0.0 : ((opacity > 1.0) ? 1.0 : opacity);
QSharedPointer<OverlayData> overlayData = d->getOverlayData(key);
const QString &logicalLayername = overlayData->m_logicalLayerName;
// get LayerParameter, update data and set:
LayerParameter lp = d->m_map->layerFactory().layerParameter(logicalLayername);
lp.opacity = int(opacity * 100);
if (! d->m_map->layerFactory().setLayerParameter(logicalLayername, lp))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " error on setLayerParameter" << " key=" << key;
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " key=" << key << " opacity=" << opacity;
return true;
}
bool OverlayHandlerImpl::setOverlayVisibility(int key, bool visibility)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " key=" << key
<< " visibility=" << visibility;
// Lookup:
QSharedPointer<OverlayData> overlayData = d->getOverlayData(key);
const QString &logicalLayername = overlayData->m_logicalLayerName;
INFO_LOG(m_logger) << LOGFUNCTION << " visibility of layer='" << logicalLayername << "'"
<< " key=" << key
<< " -> " << visibility;
if (logicalLayername.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " layername is empty, key=" << key << " unknown";
return false;
}
// Technical/Esri:
// get LayerParameter, update data and set:
LayerParameter lp = d->m_map->layerFactory().layerParameter(logicalLayername);
lp.visible = visibility;
if (! d->m_map->layerFactory().setLayerParameter(logicalLayername, lp))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " error on setLayerParameter" << " key=" << key;
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " key=" << key
<< " visibility=" << visibility;
return true;
}
bool OverlayHandlerImpl::overlayVisibility(int key) const
{
QSharedPointer<OverlayData> overlayData = d->getOverlayData(key);
const QString &logicalLayername = overlayData->m_logicalLayerName;
if (logicalLayername.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " layername is empty, key=" << key << " unknown";
return false;
}
LayerParameter lp = d->m_map->layerFactory().layerParameter(logicalLayername);
return lp.visible;
} // bool OverlayHandlerImpl::overlayVisibility(const int &key)
bool OverlayHandlerImpl::removeOverlay(int key)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " key=" << key;
if (! d->m_map)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " error no Map " << " key=" << key;
return false;
}
if (! d->contains(key))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " key=" << key << " unknown";
return false;
}
// Lookup:
QSharedPointer<OverlayData> overlayData = d->getOverlayData(key);
QString logicalLayername = overlayData->m_logicalLayerName; // create a copy here
DEBUG_LOG(m_logger) << LOGFUNCTION << " remove layer='" << logicalLayername << "'"
<< " key=" << key;
if (logicalLayername.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " layername is empty, key=" << key;
return false;
}
// Own data management:
//
// FIRST:
//
// Remove own MapObject BEFORE the layer
d->remove(key);
overlayData.clear(); // to decrease QSharedPointer <----- important
//
// SECOND:
//
// Technical/Esri:
if (!d->m_map->layerFactory().removeOverlayLayer(logicalLayername))
{
WARNING_LOG(m_logger) << LOGFUNCTION << " remove layer='" << logicalLayername << "'"
<< " key=" << key;
return false;
}
//
// Pixel-based TextBoxes:
//
PixelBasedTextBoxHandler &pixelBasedTextBoxHandler = d->m_map->pixelBasedTextBoxHandler();
const int parentId = key; // key == fileId
pixelBasedTextBoxHandler.removeAllTextBoxesOfParent(parentId);
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " key=" << key;
return true;
}
int OverlayHandlerImpl::removeAllOverlays()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, current size = " << d->size();
int removed = 0;
const QList<int> &currentKeys = d->keys();
for (int key : currentKeys)
{
if (removeOverlay(key))
++removed;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " removed = " << removed
<< " size now = " << d->size();
return removed;
}
int OverlayHandlerImpl::overlayCount() const
{
return d->size();
}
QList<int> OverlayHandlerImpl::overlays() const
{
return d->keys();
}
bool OverlayHandlerImpl::containsOverlay(int key) const
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return d->contains(key);
}
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<< (std::ostream &stream,
const OverlayHandlerImpl &value)
{
stream << value.toString();
return stream;
}
} // namespace SituationMap
+108
View File
@@ -0,0 +1,108 @@
/**
* @RDE_HEADER@
* Interface of AbstractMapObject
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/overlay_handler.h>
#include <Common/situation_map_logging.h>
#include "situation_map_export.h"
// Qt
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT OverlayHandlerImpl : public OverlayHandler
{
using BaseClass = SituationMap::OverlayHandler;
public:
/*! \name Construction, Destruction */
//@{
explicit OverlayHandlerImpl(Map *map);
~OverlayHandlerImpl();
OverlayHandlerImpl(const OverlayHandlerImpl &other) = delete;
OverlayHandlerImpl &operator=(OverlayHandlerImpl &other) = delete;
//@}
QString toString() const;
/*! \name Adding / Updating */
//@{
bool setOverlay(int key, const QString &name, const QByteArray &overlayContent, bool isSnapshot = false) override;
QByteArray content(int key) const override;
double minScale(int key) const override;
bool setMinScale(int key, double minScale) override;
double maxScale(int key) const override;
bool setMaxScale(int key, double maxScale) override;
double opacity(int key) const override;
bool setOpacity(int key, double opacity) override;
//@}
bool reorderOverlays(const QVector<int> &orderOfIds) override;
/*! \name Visibility */
//@{
bool setOverlayVisibility(int key, bool visibility) override;
bool overlayVisibility(int key) const override;
//@}
/*! \name Removing */
//@{
bool removeOverlay(int key) override;
int removeAllOverlays() override;
//@}
/*! \name Retrieving */
//@{
int overlayCount() const override;
QList<int> overlays() const override;
bool containsOverlay(int key) const override;
// QString getOverlay(int key);
//@}
protected:
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;
};
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
/**
* @brief Output operator
* @param stream used to stream into.
* @param value to be streamed.
* @return appended stream.
*/
SITUATIONMAP_EXPORT std::ostream &operator<< (std::ostream &stream,
const OverlayHandlerImpl &value);
} // namespace SituationMap
@@ -0,0 +1,344 @@
/**
* @RDE_HEADER@
* Implementation of AbstractMapObject
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "overview_map_handler_impl.h"
#include <Common/resource_path.h>
#include <MapImpl/map_impl.h>
#include <SymbologyImpl/symbol_dictionary_impl.h>
#include <Rubberbands/rubberband_factory.h>
#include <Map/situation_map_utils.h>
#include <MapImpl/layer_factory_impl.h>
// ESRI
#include <arcgis/Map.h>
#include <arcgis/MapGraphicsView.h>
#include <arcgis/TileCache.h>
#include <arcgis/ArcGISTiledLayer.h>
#include <arcgis/Basemap.h>
#include <arcgis/SimpleRenderer.h>
#include <arcgis/GraphicsOverlayListModel.h>
#include <arcgis/SimpleLineSymbol.h>
#include <arcgis/SimpleFillSymbol.h>
//----------------------------------------------------------------------------
namespace SituationMap
{
//-----------------------------------------------------------------------------
class OverviewMapHandlerImpl::Private
{
public:
Private() = default;
~Private() = default;
// ESRI data:
// Own data:
QPointer<OverviewMap> m_builtInOverviewMap;
MapImpl *m_mainMap = nullptr;
QColor m_color = Qt::red;
}; // class OverviewMapHandlerImpl::Private
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
OverviewMapHandlerImpl::OverviewMapHandlerImpl(Map *map)
: BaseClass(map)
, m_logger(getLogger("OverviewMapHandlerImpl"))
, d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
d->m_mainMap = static_cast<MapImpl *>(map);
if (d->m_mainMap)
{
// d->m_color = d->m_mainMap->palette().color(QPalette::WindowText); // RDE_DARK_BLUE
// REGG-4125 - Karte: Bereichsauswahl verdrängt Übersichtskarte
// EventDispatcher und EventDelegater scheinen die OverviewMap zu verdrängen.
// OverviewMap per QGraphicsProxyWidget (QGraphicsView()->scene()->addWidget) schlägt auch fehl
// Daher dieser einfacher workaround:
// Raise nach dem Zerstören eines Rubberbands (und damit Entfernen von EventDispatcher und EventDelegater):
connect(&d->m_mainMap->rubberbandFactory(), &RB::RubberbandFactory::rubberbandDestroyed,
this, &OverviewMapHandlerImpl::onMainMapRectChanged);
}
else
ERROR_LOG(m_logger) << LOGFUNCTION << " error no mainMap ";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
OverviewMapHandlerImpl::~OverviewMapHandlerImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void OverviewMapHandlerImpl::createBuiltInOverviewMap()
{
if (d->m_builtInOverviewMap)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " already exists";
return;
}
d->m_builtInOverviewMap = QPointer<OverviewMap>(new OverviewMap(this));
d->m_builtInOverviewMap->init(d->m_mainMap, d->m_color);
if (! d->m_builtInOverviewMap->createBasicOverviewMap())
ERROR_LOG(m_logger) << LOGFUNCTION << " createBasicOverviewMap failed";
if (! d->m_builtInOverviewMap->widget())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " d->m_builtInOverviewMap->widget() failed";
return;
}
// confgure widget:
// disable mouse:
d->m_builtInOverviewMap->widget()->setAttribute(Qt::WA_TransparentForMouseEvents);
d->m_builtInOverviewMap->widget()->setFocusPolicy(Qt::NoFocus);
d->m_builtInOverviewMap->widget()->setMinimumSize(QSize(100, 100));
d->m_builtInOverviewMap->widget()->setStyleSheet(QLatin1String("border: 1px solid gray"));
d->m_builtInOverviewMap->widget()->show();
// take care of position:
connect(d->m_mainMap, &MapImpl::rectChanged,
this, &OverviewMapHandlerImpl::onMainMapRectChanged);
onMainMapRectChanged();
d->m_mainMap->emitVisualComponentChanged(VisualComponent::OverviewMap); // to update onOffCheckbox
}
void OverviewMapHandlerImpl::removeBuiltInOverviewMap()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (d->m_builtInOverviewMap)
{
QPointer<OverviewMap> localPtr = d->m_builtInOverviewMap;
d->m_builtInOverviewMap.clear();
localPtr->deleteLater();
d->m_mainMap->emitVisualComponentChanged(VisualComponent::OverviewMap);
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
QWidget *OverviewMapHandlerImpl::builtInOverviewMapWidget()
{
if (! d->m_builtInOverviewMap)
return nullptr;
return d->m_builtInOverviewMap->widget();
}
void OverviewMapHandlerImpl::removeAll()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
removeBuiltInOverviewMap();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void OverviewMapHandlerImpl::onMainMapRectChanged()
{
if (! d->m_builtInOverviewMap)
return;
if (! d->m_builtInOverviewMap->widget())
return;
const QSize &mainSize = d->m_mainMap->size();
const QSize &overviewSize = d->m_builtInOverviewMap->widget()->size();
QSize diff = mainSize - overviewSize - QSize(10, 10); // additional border
d->m_builtInOverviewMap->widget()->move(diff.width(), diff.height());
d->m_builtInOverviewMap->widget()->raise();
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
OverviewMap::OverviewMap(QObject *parent)
: BaseClass(parent)
, m_logger(getLogger("OverviewMap"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
OverviewMap::~OverviewMap()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (m_graphicsView)
m_graphicsView->deleteLater();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void OverviewMap::init(MapImpl *mainMap, const QColor &color)
{
m_mainMap = mainMap;
m_color = color;
}
QWidget *OverviewMap::widget()
{
return m_graphicsView.data();
}
bool OverviewMap::createBasicOverviewMap()
{
if (m_graphicsView)
return true;
// create required objects:
m_graphicsView = QPointer<Esri::ArcGISRuntime::MapGraphicsView>(new Esri::ArcGISRuntime::MapGraphicsView(m_mainMap));
m_graphicsView->setAttributionTextVisible(false);
QObject *parent = this;
QString tiledBaseMapLayer = SituationMap::getResourcePath() + QLatin1String("Topographic.tpk");
QFile f(tiledBaseMapLayer);
if (!f.exists())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " file does not exist: tiledBaseMapLayer='" << tiledBaseMapLayer << "'";
return false;
}
INFO_LOG(m_logger) << LOGFUNCTION << " tiledBaseMapLayer='" << tiledBaseMapLayer << "'";
Esri::ArcGISRuntime::TileCache *tpkCache = new Esri::ArcGISRuntime::TileCache(tiledBaseMapLayer, parent);
tpkCache->setObjectName(QLatin1String("OverviewMap-TPK-tpkCache"));
Esri::ArcGISRuntime::ArcGISTiledLayer *tiledLayer = new Esri::ArcGISRuntime::ArcGISTiledLayer(tpkCache, parent);
tiledLayer->setName(QLatin1String("OverviewMap-TPK-tiledLayer"));
Esri::ArcGISRuntime::Basemap *basemap = new Esri::ArcGISRuntime::Basemap(tiledLayer, parent);
basemap->setName(QLatin1String("TPK-Basemap"));
// connect signals
// loadStatusChanged ->
connect(tpkCache, &Esri::ArcGISRuntime::TileCache::loadStatusChanged, this, [this](Esri::ArcGISRuntime::LoadStatus loadStatus)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " OverviewMap-TileCache: " << "loadStatus changed to: " << loadStatus;
});
connect(tiledLayer, &Esri::ArcGISRuntime::ArcGISTiledLayer::loadStatusChanged, this, [this](Esri::ArcGISRuntime::LoadStatus loadStatus)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " OverviewMap-ArcGISTiledLayer: " << "loadStatus changed to: " << loadStatus;
if (loadStatus == Esri::ArcGISRuntime::LoadStatus::Loaded)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " loaded -> setToFullExtent";
setToFullExtent();
}
});
connect(basemap, &Esri::ArcGISRuntime::Basemap::loadStatusChanged, this, [this](Esri::ArcGISRuntime::LoadStatus loadStatus)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " OverviewMap-Basemap: " << "loadStatus changed to: " << loadStatus;
});
// Map:
Esri::ArcGISRuntime::Map *overviewMap = new Esri::ArcGISRuntime::Map(basemap, parent);
overviewMap->setObjectName(QLatin1String("OverviewMap-EsriMap"));
// will start esris cascading load dependecies...
m_graphicsView->setMap(overviewMap);
// GraphicsOverlay:
Esri::ArcGISRuntime::GraphicsOverlay *graphicalOverlay = new Esri::ArcGISRuntime::GraphicsOverlay(parent);
m_graphicsView->graphicsOverlays()->append(graphicalOverlay);
QColor color = m_color;
// create symbol
Esri::ArcGISRuntime::SimpleLineSymbol *sls =
new Esri::ArcGISRuntime::SimpleLineSymbol(Esri::ArcGISRuntime::SimpleLineSymbolStyle::Solid,
color, 1, parent);
color.setAlpha(100);
Esri::ArcGISRuntime::SimpleFillSymbol *sfs =
new Esri::ArcGISRuntime::SimpleFillSymbol(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid,
color, sls, parent);
// create renderer
Esri::ArcGISRuntime::SimpleRenderer *renderer =
new Esri::ArcGISRuntime::SimpleRenderer(sfs, parent);
// Handle errors of renderer:
connect(renderer, &Esri::ArcGISRuntime::SimpleRenderer::errorOccurred, this, [this](const Esri::ArcGISRuntime::Error & error)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " EsriError occured: " << error;
});
// set Renderer to layer:
graphicalOverlay->setRenderer(renderer);
// prepare graphic
m_graphic = new Esri::ArcGISRuntime::Graphic(graphicalOverlay);
graphicalOverlay->graphics()->append(m_graphic);
// update first time
onMainMapExtentChanged();
// execute on extentChanged
connect(m_mainMap, &MapImpl::extentChangedDelayed, this, &OverviewMap::onMainMapExtentChanged);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return true;
}
void OverviewMap::onMainMapExtentChanged()
{
if (! m_graphic)
return;
Esri::ArcGISRuntime::GeoView *geoView = m_mainMap->getEsriGeoView();
if (! geoView)
return;
setToFullExtent();
const Esri::ArcGISRuntime::Viewpoint curVp =
geoView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::BoundingGeometry);
m_graphic->setGeometry(curVp.targetGeometry()); // curVp.targetGeometry() should be a rectangle
}
void OverviewMap::setToFullExtent()
{
if (!m_graphicsView || !m_mainMap)
return;
// zoom to fullExtent:
Esri::ArcGISRuntime::Envelope fullExtent = m_mainMap->layerFactoryImpl().fullExtentOfBasemaps(0.2); // add margin
Esri::ArcGISRuntime::Viewpoint vp(fullExtent);
m_graphicsView->setViewpointAndWait(vp);
}
} // namespace SituationMap
+112
View File
@@ -0,0 +1,112 @@
/**
* @RDE_HEADER@
* Implementation of OverviewMap
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/overview_map_handler.h>
#include <Common/situation_map_logging.h>
#include "situation_map_export.h"
// ESRI
#include <arcgis/MapGraphicsView.h>
// Qt
// Forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Graphic;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
class MapImpl;
/**
* @brief The OverviewMap is a wrapper around OverviewMapWidget.
*/
class OverviewMap : public QObject
{
Q_OBJECT
using BaseClass = QObject;
public:
explicit OverviewMap(QObject *parent = nullptr);
~OverviewMap();
void init(MapImpl *mainMap, const QColor &color = Qt::red);
bool createBasicOverviewMap();
QWidget *widget();
protected slots:
void onMainMapExtentChanged();
void setToFullExtent();
protected:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
// Own data:
MapImpl *m_mainMap = nullptr;
QPointer<Esri::ArcGISRuntime::MapGraphicsView> m_graphicsView;
Esri::ArcGISRuntime::Graphic *m_graphic = nullptr;
QColor m_color;
};
// inherent 'public', to get that implementation
// inherent 'public virtual' to get that interface
class SITUATIONMAP_EXPORT OverviewMapHandlerImpl : public QObject, public virtual OverviewMapHandler
{
Q_OBJECT
using BaseClass = QObject;
public:
/*! \name Construction, Destruction */
//@{
explicit OverviewMapHandlerImpl(Map *map);
~OverviewMapHandlerImpl();
OverviewMapHandlerImpl(const OverviewMapHandlerImpl &other) = delete;
OverviewMapHandlerImpl &operator=(OverviewMapHandlerImpl &other) = delete;
void createBuiltInOverviewMap() override;
void removeBuiltInOverviewMap() override;
QWidget *builtInOverviewMapWidget() override;
void removeAll() override;
//@}
protected slots:
void onMainMapRectChanged();
protected:
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,563 @@
/**
* @RDE_HEADER@
* Interface-Implementation of LocationDisplay
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "pixel_based_textbox_handler_impl.h"
#include "map_impl.h"
#include <Map/situation_map_utils.h>
#include <MapImpl/coordinate_translation.h>
// ESRI
#include <arcgis/arcgisversion.h> // This file is created by RME, not ESRI
#include <arcgis/Map.h>
#include <arcgis/MapGraphicsView.h>
// Qt
#include <QHBoxLayout>
#include <QGraphicsScene>
#include <QGraphicsRectItem>
#include <QGraphicsProxyWidget>
#include <QPushButton>
#include <QDebug>
#include <QTimer>
#include <sstream>
namespace SituationMap
{
//-----------------------------------------------------------------------------
// PBTB_ID output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<< (std::ostream &stream, const PBTB_ID &value)
{
stream << value.toString();
return stream;
}
//-----------------------------------------------------------------------------
// class
//-----------------------------------------------------------------------------
class PixelBasedTextBoxHandlerImpl::Private
{
public:
Private() = default;
~Private() = default;
// ESRI data:
// Own data:
SituationMap::MapImpl *m_mapImpl{nullptr};
QVector<QSharedPointer<TB_Data>> m_tbDataCont;
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// class PixelBasedTextBoxHandlerImpl
//-----------------------------------------------------------------------------
PixelBasedTextBoxHandlerImpl::PixelBasedTextBoxHandlerImpl(SituationMap::MapImpl *mapImpl)
: m_logger(getLogger("PixelBasedTextBoxHandlerImpl"))
, d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
d->m_mapImpl = mapImpl;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
PixelBasedTextBoxHandlerImpl::~PixelBasedTextBoxHandlerImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
QTextEdit *PixelBasedTextBoxHandlerImpl::createTextBox(PBTB_ID &pbtbIdRef, const PixelTextBoxData &ptbd, bool enableGeometryModifications)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, (((pbtbId=" << pbtbIdRef.toString()
<< "))) , ptbd=" << ptbd.toDebugString()
<< ", enableGeometryModifications=" << enableGeometryModifications;
if (!d->m_mapImpl)
return nullptr;
QSharedPointer<TB_Data> tbData = getCreateTbData(pbtbIdRef);
// pbtbId may have changed, write back to caller:
pbtbIdRef = tbData->m_pbtbId;
// backgroundColor is special (not with QTextEdit), so update tbData here:
if (ptbd.m_backgroundColor.isValid())
tbData->m_ptbd.m_backgroundColor = ptbd.m_backgroundColor;
else
tbData->m_ptbd.m_backgroundColor = Qt::white;
TB_Data::TB_GUI_DATA &guiDataRef = tbData->m_guiData;
if (!guiDataRef.m_textEdit)
{
QTextEditWithDoubleClick *myTextEdit = new QTextEditWithDoubleClick();
guiDataRef.m_textEdit = myTextEdit;
guiDataRef.m_textEdit->setObjectName(QStringLiteral("WidgetOverlay_QTextEdit"));
guiDataRef.m_textEdit->setReadOnly(true);
guiDataRef.m_textEdit->setProperty(PBTB_ID_PROPERTY_NAME, tbData->m_pbtbId.toString());
if (ptbd.m_size.isValid())
guiDataRef.m_textEdit->setGeometry(0, 0, ptbd.m_size.width(), ptbd.m_size.height());
if (enableGeometryModifications)
{
connect(myTextEdit, &QTextEditWithDoubleClick::doubleClickEvent,
this, [this] (const PBTB_ID &pbtbId) {
emit pixelBasedTextBoxDoubleClickEvent(pbtbId);
});
}
// It looks like:
// GraphicsItem <-> ProxyWidget <-> Widget/TextEdit
// (QGraphicsItem is a baseclass of both GraphicsItem as well as ProxyWidget)
// GraphicsItem becomes parent of ProxyWidget
// Then TextEdit is added via setWidget to ProxyWidget
// Now both are connected, TextEdit in front of GraphicsItem which ist little bit bigger
// GraphicsItem is movable, GI an TE are connected to each other...
// 10.05.2023, Norbert Thoden
// (((Idea: Maybe ProxyWidget should be parent of GraphicsItem
// to put GraphicsItem on top of TextEdit???)))
QGraphicsRectItemWithEvents *grItem = new QGraphicsRectItemWithEvents(0, 0, 0, 0); // x,y == rel position, size will be set later
grItem->setProperty(PBTB_ID_PROPERTY_NAME, tbData->m_pbtbId.toString());
grItem->setCursor(Qt::SizeAllCursor);
if (enableGeometryModifications)
{
grItem->activatePositionChangedEvents();
connect(grItem, &QGraphicsRectItemWithEvents::positionChangedFinishedEvent,
this, &PixelBasedTextBoxHandler::pixelBasedTextBoxGeometryChanged, Qt::UniqueConnection);
}
guiDataRef.m_handle_or_proxyControl = grItem;
guiDataRef.m_proxy = new QGraphicsProxyWidget(guiDataRef.m_handle_or_proxyControl); // parent is important for cleanup!
guiDataRef.m_proxy->setObjectName(QStringLiteral("ProxyWidgetOverlay_QTextEdit"));
guiDataRef.m_proxy->setProperty(PBTB_ID_PROPERTY_NAME, tbData->m_pbtbId.toString()); // maybe helpful in future
guiDataRef.m_proxy->setWidget(guiDataRef.m_textEdit);
guiDataRef.m_proxy->setPos(QPoint(0, 0)); // relative position to GraphicsItem !?
guiDataRef.m_proxy->setOpacity(0.99); // avoid white on white
guiDataRef.m_handle_or_proxyControl->setPen(QPen(Qt::gray));
guiDataRef.m_handle_or_proxyControl->setBrush(QBrush(Qt::gray));
if (enableGeometryModifications)
guiDataRef.m_handle_or_proxyControl->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable);
d->m_mapImpl->getQGraphicsView()->scene()->addItem(guiDataRef.m_handle_or_proxyControl);
if (enableGeometryModifications)
{
// SizeGrip, removeBtn :
QSizeGripWithEvent *sizeGrip = new QSizeGripWithEvent(tbData->m_pbtbId, guiDataRef.m_textEdit);
QHBoxLayout *layout = new QHBoxLayout(guiDataRef.m_textEdit);
layout->setContentsMargins(0, 0, 0, 0);
QPushButton *btn = new QPushButton(guiDataRef.m_textEdit);
btn->setIcon(QIcon(QStringLiteral("icon:bgv_view_close.svg")));
connect(btn, &QPushButton::clicked, this, [this, pbtbIdRef](bool) {
emit pixelBasedTextBoxRemoveRequestedEvent(pbtbIdRef);
});
layout->addWidget(btn, 1, Qt::AlignRight | Qt::AlignBottom);
layout->addWidget(sizeGrip, 0, Qt::AlignRight | Qt::AlignBottom);
// [GUEZE-2947] Undo,Redo fuer PixelBasedTextBoxen
// pass geometryChanged via signal-signal-connection:
connect(sizeGrip, &QSizeGripWithEvent::geometryChangedFinishedEvent,
this, &PixelBasedTextBoxHandler::pixelBasedTextBoxGeometryChanged, Qt::UniqueConnection);
// : SizeGrip, removeBtn
}
// Geometry :
tbData->onGeometryChanged();
if (!tbData->connectGeometryChanges())
ERROR_LOG(m_logger) << LOGFUNCTION << " connectGeometryChanges failed!";
//
// [GUEZE-2947] Undo,Redo fuer PixelBasedTextBoxen
// // pass geometryChanged via signal-signal-connection:
// connect(tbData.data(), &TB_Data::geometryChanged,
// this, &PixelBasedTextBoxHandler::pixelBasedTextBoxGeometryChanged, Qt::UniqueConnection);
// : Geometry
}
//
// update GUI:
//
// update text
guiDataRef.m_textEdit->setText(ptbd.m_htmlText);
if (guiDataRef.m_textEdit->toPlainText().isEmpty())
{
const QString sansSerif = QLatin1String("Sans Serif");
QFont textFont = guiDataRef.m_textEdit->font();
textFont.setFamily(sansSerif);
textFont.setFamilies({sansSerif});
guiDataRef.m_textEdit->setFont(textFont);
}
// update backgroundColor
const QColor &bgCol = ptbd.m_backgroundColor;
if (bgCol.isValid())
{
QString styleSheet = QString(QStringLiteral("QTextEdit { background-color: rgb(%1, %2, %3) }")).arg(bgCol.red()).arg(bgCol.green()).arg(bgCol.blue());
guiDataRef.m_textEdit->setStyleSheet(styleSheet);
}
// update geometry and size:
guiDataRef.m_handle_or_proxyControl->setPos(ptbd.m_position);
if (ptbd.m_size.isValid())
guiDataRef.m_textEdit->setGeometry(0, 0, ptbd.m_size.width(), ptbd.m_size.height());
TRACE_LOG(m_logger) << LOGFUNCTION << " END, pbtbId=" << pbtbIdRef;
return guiDataRef.m_textEdit.data();
}
QTextEdit *PixelBasedTextBoxHandlerImpl::updateTextBox(const PBTB_ID &pbtbId, const PixelTextBoxData &ptbd)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, (((pbtbId=" << pbtbId.toString()
<< "))) , ptbd=" << ptbd.toDebugString();
QSharedPointer<TB_Data> tbDataToCheckIfItExists = getTbDataIfExists(pbtbId);
if(!tbDataToCheckIfItExists)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " TextBox does not exists, pbtbId=" << pbtbId.toString();
return nullptr;
}
PBTB_ID pbtbIdCopy = pbtbId;
QTextEdit *te = this->createTextBox(pbtbIdCopy, ptbd, false); // enableGeometryModifications==false - that part is already done
TRACE_LOG(m_logger) << LOGFUNCTION << " END, (((pbtbId=" << pbtbId.toString()
<< "))) , ptbd=" << ptbd.toDebugString();
return te;
};
QTextEdit *PixelBasedTextBoxHandlerImpl::getTextBox(const PBTB_ID &pbtbId)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, pbtbId=" << pbtbId.toString();
if (!d->m_mapImpl)
return nullptr;
QSharedPointer<TB_Data> tbData = getTbDataIfExists(pbtbId);
QTextEdit *te = nullptr;
if (tbData && tbData->hasValidId())
{
TB_Data::TB_GUI_DATA &guiDataRef = tbData->m_guiData;
te = guiDataRef.m_textEdit;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, pbtbId=" << pbtbId
<< ", te = " << te;
return te;
}
void PixelBasedTextBoxHandlerImpl::removeSelection()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
QMutableVectorIterator<QSharedPointer<TB_Data>> it(d->m_tbDataCont);
while (it.hasNext())
{
QSharedPointer<TB_Data> tbData = it.next();
if (tbData)
{
TB_Data::TB_GUI_DATA &guiDataRef = tbData->m_guiData;
guiDataRef.m_handle_or_proxyControl->setBrush(QBrush(Qt::gray));
}
} // while
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
bool PixelBasedTextBoxHandlerImpl::setSelected(const PBTB_ID &pbtbId)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, pbtbId=" << pbtbId;
// part 1
removeSelection();
// part 2
QSharedPointer<TB_Data> tbData = getCreateTbData(pbtbId);
if (tbData->m_pbtbId != pbtbId)
return false;
QColor col = Qt::magenta;
col.setAlpha(128);
TB_Data::TB_GUI_DATA &guiDataRef = tbData->m_guiData;
guiDataRef.m_handle_or_proxyControl->setBrush(QBrush(col));
TRACE_LOG(m_logger) << LOGFUNCTION << " END, pbtbId=" << pbtbId;
return true;
}
QVector<PBTB_ID> PixelBasedTextBoxHandlerImpl::getAllIdsOfParent(const PBTB_ID &pbtbId) const
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, pbtbId=" << pbtbId;
const int parentId = pbtbId.m_parentId;
QVector<PBTB_ID> retVec;
retVec.reserve(d->m_tbDataCont.size());
QVectorIterator<QSharedPointer<TB_Data>> it(d->m_tbDataCont);
while (it.hasNext())
{
QSharedPointer<TB_Data> item = it.next();
if (item->m_pbtbId.m_parentId == parentId)
retVec.append(item->m_pbtbId);
} // while
TRACE_LOG(m_logger) << LOGFUNCTION << " END, pbtbId=" << pbtbId
<< ", retVec.size()" << retVec.size();
return retVec;
}
PixelTextBoxData PixelBasedTextBoxHandlerImpl::getPixelTextBoxData(const PBTB_ID &pbtbId) const
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, pbtbId=" << pbtbId;
QSharedPointer<TB_Data> tbData = getCreateTbData(pbtbId);
if (tbData->m_pbtbId != pbtbId)
return {};
PixelTextBoxData &ptbdRef = tbData->m_ptbd;
TB_Data::TB_GUI_DATA &guiDataRef = tbData->m_guiData;
//
// current guiData to ptbd:
//
// text
ptbdRef.m_htmlText = guiDataRef.m_textEdit->document()->toHtml();
// ptbdRef.m_backgroundColor; // already updated, see createTextBox()
ptbdRef.m_position = guiDataRef.m_handle_or_proxyControl->pos().toPoint();
ptbdRef.m_size = guiDataRef.m_textEdit->geometry().size();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, pbtbId=" << pbtbId
<< ", ptbdRef = " << ptbdRef;
return ptbdRef;
}
bool PixelBasedTextBoxHandlerImpl::removeTextBox(const PBTB_ID &pbtbId)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, pbtbId=" << pbtbId;
if (!d->m_mapImpl)
return false;
if (d->m_tbDataCont.isEmpty())
return true;
QMutableVectorIterator<QSharedPointer<TB_Data>> it(d->m_tbDataCont);
while (it.hasNext())
{
QSharedPointer<TB_Data> item = it.next();
if (item->m_pbtbId == pbtbId)
{
it.remove(); // dtor will destroy guiElements
TRACE_LOG(m_logger) << LOGFUNCTION << " END, removed pbtbId=" << pbtbId;
return true;
}
} // while
TRACE_LOG(m_logger) << LOGFUNCTION << " END, pbtbId=" << pbtbId;
return false;
}
bool PixelBasedTextBoxHandlerImpl::removeAllTextBoxesOfParent(int parentId)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, parentId=" << parentId;
if (!d->m_mapImpl)
return false;
int cnt = 0;
QMutableVectorIterator<QSharedPointer<TB_Data>> it(d->m_tbDataCont);
while (it.hasNext())
{
QSharedPointer<TB_Data> item = it.next();
if (item->m_pbtbId.m_parentId == parentId)
{
it.remove(); // dtor will destroy guiElements
++cnt;
}
} // while
TRACE_LOG(m_logger) << LOGFUNCTION << " END, parentId=" << parentId
<< ", removed #items = " << cnt;
return true;
}
QSharedPointer<TB_Data> PixelBasedTextBoxHandlerImpl::getCreateTbData(const PBTB_ID &pbtbId) const
{
QVectorIterator<QSharedPointer<TB_Data>> it(d->m_tbDataCont);
while (it.hasNext())
{
QSharedPointer<TB_Data> item = it.next();
if (item->m_pbtbId == pbtbId)
return item;
} // while
// not found?
// insert and return;
QSharedPointer<TB_Data> initialData = QSharedPointer<TB_Data>(new TB_Data());
initialData->m_pbtbId.m_parentId = pbtbId.m_parentId; // !!! keep m_parentId !!!
initialData->m_pbtbId.m_uid = PBTB_ID::getNextId();
d->m_tbDataCont.append(initialData);
return d->m_tbDataCont.last();
}
QSharedPointer<TB_Data> PixelBasedTextBoxHandlerImpl::getTbDataIfExists(const PBTB_ID &pbtbId) const
{
QVectorIterator<QSharedPointer<TB_Data>> it(d->m_tbDataCont);
while (it.hasNext())
{
QSharedPointer<TB_Data> item = it.next();
if (item->m_pbtbId == pbtbId)
return item;
} // while
return {};
}
TB_Data::TB_Data()
{
}
TB_Data::~TB_Data()
{
m_guiData.resetGui();
}
bool TB_Data::hasValidId() const
{
return m_pbtbId.isValid();
}
bool TB_Data::connectGeometryChanges()
{
if (!m_guiData.m_proxy)
return false;
// QTextEdit has no geometryChanged or similar, but ProxyWidget has:
connect(m_guiData.m_proxy, &QGraphicsProxyWidget::geometryChanged,
this, &TB_Data::onGeometryChanged);
return true;
}
void TB_Data::onGeometryChanged()
{
// enableGeometryModifications could be 'read' by m_sizeGrip which is only created if modifications are enabled:
const bool enableGeometryModifications = m_guiData.m_handle_or_proxyControl->flags().testFlag(QGraphicsItem::ItemIsMovable);
if (enableGeometryModifications)
m_guiData.m_handle_or_proxyControl->setRect(m_guiData.m_textEdit->geometry().adjusted(-10, -10, 10, 10)); // bigger than textEdit
else
m_guiData.m_handle_or_proxyControl->setRect(m_guiData.m_textEdit->geometry().adjusted(1, 1, -1, -1)); // smaller than textEdit (invisible)
emit geometryChanged(m_pbtbId);
}
//-----------------------------------------------------------------------------
// class QTextEditWithDoubleClick
//-----------------------------------------------------------------------------
void QTextEditWithDoubleClick::mouseDoubleClickEvent(QMouseEvent */*event*/)
{
const QString &pbtbProp = this->property(PBTB_ID_PROPERTY_NAME).toString();
const PBTB_ID &pbtbId = SituationMap::PBTB_ID::fromString(pbtbProp);
emit doubleClickEvent(pbtbId);
// do NOT forward to baseClass. So we avoid the text being displayed xor/inverted
// BaseClass::mouseDoubleClickEvent(event);
// Maybe the selection coud be 'removed' in that way:
// QTextCursor tc = textCursor();
// tc.clearSelection();
// setTextCursor(tc);
}
//-----------------------------------------------------------------------------
// class QGraphicsRectItemWithMoveEvent
//-----------------------------------------------------------------------------
void QGraphicsRectItemWithEvents::activatePositionChangedEvents()
{
m_qtimer = new QTimer(this);
m_qtimer->setInterval(100);
m_qtimer->setSingleShot(true);
connect(m_qtimer, &QTimer::timeout,
this, &QGraphicsRectItemWithEvents::onTimeout);
}
void QGraphicsRectItemWithEvents::onTimeout()
{
const QString &pbtbProp = this->property(PBTB_ID_PROPERTY_NAME).toString();
const PBTB_ID &pbtbId = SituationMap::PBTB_ID::fromString(pbtbProp);
emit positionChangedEvent(pbtbId);
}
void QGraphicsRectItemWithEvents::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
BaseClass::mouseMoveEvent(event);
m_qtimer->start();
m_isMoving = true;
}
void QGraphicsRectItemWithEvents::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
BaseClass::mouseReleaseEvent(event);
if (m_isMoving)
{
const QString &pbtbProp = this->property(PBTB_ID_PROPERTY_NAME).toString();
const PBTB_ID &pbtbId = SituationMap::PBTB_ID::fromString(pbtbProp);
emit positionChangedFinishedEvent(pbtbId);
m_isMoving = false;
}
}
//-----------------------------------------------------------------------------
// class QSizeGripWithEvent
//-----------------------------------------------------------------------------
void QSizeGripWithEvent::mouseMoveEvent(QMouseEvent *event)
{
BaseClass::mouseMoveEvent(event);
m_isGeometryChanging = true;
}
void QSizeGripWithEvent::mouseReleaseEvent(QMouseEvent *mouseEvent)
{
BaseClass::mouseReleaseEvent(mouseEvent);
if (m_isGeometryChanging)
{
m_isGeometryChanging = false;
emit geometryChangedFinishedEvent(m_pbtbId);
}
}
} // namespace SituationMap
@@ -0,0 +1,221 @@
/**
* @RDE_HEADER@
* Interface-Implementation of LocationDisplay
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/pixel_based_textbox_handler.h>
#include <GeometryModel/geometry_pixelTextBox.h>
#include <Common/situation_map_logging.h>
// Qt
#include <QTextEdit>
#include <QPointer>
#include <QSizeGrip>
#include <QGraphicsRectItem>
#include <QGraphicsProxyWidget>
#include <QVariant>
#include <QTimer>
//----------------------------------------------------------------------------
namespace SituationMap
{
class MapImpl;
class TB_Data;
//-----------------------------------------------------------------------------
// class TB_Data
//-----------------------------------------------------------------------------
class TB_Data : public QObject
{
Q_OBJECT
public:
friend class PixelBasedTextBoxHandlerImpl;
TB_Data();
~TB_Data();
bool hasValidId() const;
bool connectGeometryChanges();
public Q_SLOTS:
void onGeometryChanged();
Q_SIGNALS:
void geometryChanged(SituationMap::PBTB_ID pbtbId);
void geometryChangedFinished(SituationMap::PBTB_ID pbtbId); // [GUEZE-2947] Undo,Redo fuer PixelBasedTextBoxen
public:
PBTB_ID m_pbtbId;
PixelTextBoxData m_ptbd;
struct TB_GUI_DATA
{
QPointer<QTextEdit> m_textEdit;
QGraphicsRectItem *m_handle_or_proxyControl{nullptr};
QGraphicsProxyWidget *m_proxy{nullptr};
void resetGui()
{
// ToDo: check
// delete without removing from scene/view?
delete m_handle_or_proxyControl;
m_handle_or_proxyControl = nullptr;
// since m_proxy has m_handle_or_proxyControl as parent...
m_proxy = nullptr;
delete m_textEdit; // QPointer
}
};
TB_GUI_DATA m_guiData;
private:
Q_DISABLE_COPY(TB_Data)
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// class QTextEditWithDoubleClick
//-----------------------------------------------------------------------------
class QTextEditWithDoubleClick : public QTextEdit
{
Q_OBJECT
public:
using BaseClass = QTextEdit;
QTextEditWithDoubleClick(QWidget *parent = nullptr)
:BaseClass(parent)
{}
virtual ~QTextEditWithDoubleClick() = default;
Q_SIGNALS:
void doubleClickEvent(PBTB_ID pbtbId);
protected:
void mouseDoubleClickEvent(QMouseEvent *) override;
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// class QGraphicsRectItemWithMoveEvent
//-----------------------------------------------------------------------------
class QGraphicsRectItemWithEvents : public QObject, public QGraphicsRectItem
{
Q_OBJECT
public:
using BaseClass = QGraphicsRectItem;
QGraphicsRectItemWithEvents(qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent = nullptr)
: QObject(), BaseClass(x, y, w, h, parent)
{}
virtual ~QGraphicsRectItemWithEvents() = default;
void activatePositionChangedEvents();
Q_SIGNALS:
void positionChangedEvent(PBTB_ID pbtbId);
void positionChangedFinishedEvent(PBTB_ID pbtbId); // [GUEZE-2947] Undo,Redo fuer PixelBasedTextBoxen
protected Q_SLOTS:
void onTimeout();
protected:
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
QTimer *m_qtimer{nullptr};
bool m_isMoving{false};
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// class QSizeGripWithEvent
//-----------------------------------------------------------------------------
// [GUEZE-2947] Undo,Redo fuer PixelBasedTextBoxen
// class QSizeGripWithEvent is introduced:
class QSizeGripWithEvent : public QSizeGrip
{
Q_OBJECT
public:
using BaseClass = QSizeGrip;
QSizeGripWithEvent(const PBTB_ID &pbtbId, QWidget *parent)
: BaseClass(parent), m_pbtbId(pbtbId)
{}
virtual ~QSizeGripWithEvent() = default;
Q_SIGNALS:
void geometryChangedFinishedEvent(SituationMap::PBTB_ID pbtbId); // [GUEZE-2947] Undo,Redo fuer PixelBasedTextBoxen
protected:
void mouseMoveEvent(QMouseEvent *) override;
void mouseReleaseEvent(QMouseEvent *mouseEvent) override;
PBTB_ID m_pbtbId;
bool m_isGeometryChanging{false};
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// class PixelBasedTextBoxHandlerImpl
//-----------------------------------------------------------------------------
class SITUATIONMAP_EXPORT PixelBasedTextBoxHandlerImpl : public SituationMap::PixelBasedTextBoxHandler
{
Q_OBJECT
using BaseClass = SituationMap::PixelBasedTextBoxHandler;
public:
explicit PixelBasedTextBoxHandlerImpl(SituationMap::MapImpl *mapImpl);
virtual ~PixelBasedTextBoxHandlerImpl();
PixelBasedTextBoxHandlerImpl(const PixelBasedTextBoxHandlerImpl &other) = delete;
PixelBasedTextBoxHandlerImpl &operator=(PixelBasedTextBoxHandlerImpl &other) = delete;
// create/get
QTextEdit *createTextBox(PBTB_ID &pbtbIdRef, const PixelTextBoxData &ptbd, bool enableGeometryModifications = true) override;
QTextEdit *updateTextBox(const PBTB_ID &pbtbId, const PixelTextBoxData &ptbd) override;
QTextEdit *getTextBox(const PBTB_ID &pbtbId) override;
void removeSelection() override;
bool setSelected(const PBTB_ID &pbtbId) override;
// get:
QVector<PBTB_ID> getAllIdsOfParent(const PBTB_ID &pbtbId) const override;
PixelTextBoxData getPixelTextBoxData(const PBTB_ID &pbtbId) const override;
// remove:
bool removeTextBox(const PBTB_ID &pbtbId) override;
bool removeAllTextBoxesOfParent(int parentId) override;
public Q_SLOTS:
private:
QSharedPointer<TB_Data> getCreateTbData(const PBTB_ID &pbtbId) const;
QSharedPointer<TB_Data> getTbDataIfExists(const PBTB_ID &pbtbId) const;
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,144 @@
/**
* @RDE_HEADER@
* Base class for ScreenToLocation and LocationToElevation
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "screen_location_elevation_helper_base.h"
#include "Map/situation_map_utils.h"
// ESRI
#include <arcgis/TaskWatcher.h>
//----------------------------------------------------------------------------
namespace SituationMap
{
//----------------------------------------------------------------------------
class ScreenLocationElevationHelperBase::Private
{
public:
Private() = default;
~Private() = default;
QMap<QUuid, Esri::ArcGISRuntime::TaskWatcher> m_taskWatcherCont;
QTimer *m_mainTimer = nullptr;
QTimer *m_killTimer = nullptr;
};
//----------------------------------------------------------------------------
ScreenLocationElevationHelperBase::ScreenLocationElevationHelperBase(QObject *parent)
: BaseClass(parent)
, m_logger(getLogger("ScreenLocationElevationHelperBase"))
, d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
const int mainTimerMsec = 250;
// mainTimer
d->m_mainTimer = new QTimer(this);
d->m_mainTimer->setInterval(mainTimerMsec);
connect(d->m_mainTimer, &QTimer::timeout,
this, &ScreenLocationElevationHelperBase::onMainTimoutInternal);
// killTimer
d->m_killTimer = new QTimer(this);
d->m_killTimer->setSingleShot(true);
d->m_killTimer->setInterval(mainTimerMsec + 50);
connect(d->m_killTimer, &QTimer::timeout,
this, &ScreenLocationElevationHelperBase::onKillTimout);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
ScreenLocationElevationHelperBase::~ScreenLocationElevationHelperBase()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
this->stop();
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
int ScreenLocationElevationHelperBase::pendingOperationsCount() const
{
return d->m_taskWatcherCont.size();
}
void ScreenLocationElevationHelperBase::cancel()
{
if (d->m_taskWatcherCont.isEmpty())
return;
QMutableMapIterator<QUuid, Esri::ArcGISRuntime::TaskWatcher> it(d->m_taskWatcherCont);
while (it.hasNext())
{
it.next();
if (it.value().cancel())
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " TaskWatcher canceled";
it.remove();
}
else
ERROR_LOG(m_logger) << LOGFUNCTION << " TaskWatcher.cancel() failed";
} // while
}
void ScreenLocationElevationHelperBase::stop()
{
this->cancel();
d->m_mainTimer->stop();
d->m_killTimer->stop();
}
void ScreenLocationElevationHelperBase::newComputationRequested()
{
// long absence?
if (!d->m_mainTimer->isActive())
d->m_mainTimer->start();
// restarting killTimer
d->m_killTimer->start();
}
void ScreenLocationElevationHelperBase::onMainTimoutInternal()
{
this->mainComputation();
}
void ScreenLocationElevationHelperBase::onKillTimout()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " killTimer stop mainTimer..."
<< " (size of m_taskIdSet=" << d->m_taskWatcherCont.size() << ")";
d->m_mainTimer->stop();
}
bool ScreenLocationElevationHelperBase::insertTask(const Esri::ArcGISRuntime::TaskWatcher &tw)
{
if (!tw.isValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " task not valid";
return false;
}
d->m_taskWatcherCont.insert(tw.taskId(), tw);
return true;
}
bool ScreenLocationElevationHelperBase::removeTask(const QUuid &taskId)
{
return (d->m_taskWatcherCont.remove(taskId) > 0);
}
} // namespace SituationMap
@@ -0,0 +1,84 @@
/**
* @RDE_HEADER@
* Base class for ScreenToLocation and LocationToElevation
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <situation_map_export.h>
#include <Common/situation_map_logging.h>
// ESRI
// Qt
#include <QUuid>
#include <QTimer>
namespace Esri {
namespace ArcGISRuntime
{
class TaskWatcher;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
/**
* @brief The ScreenLocationElevationHelperBase class limits the load of
* e.g. Esri::ArcGISRuntime::SceneGraphicsView::screenToLocation
* (cpu load of ~50% was recognized othjerwise)
* This class limits this to 10 computations per second.
*/
class SITUATIONMAP_EXPORT ScreenLocationElevationHelperBase : public QObject
{
Q_OBJECT
using BaseClass = QObject;
public:
/*! \name Construction, Destruction */
//@{
explicit ScreenLocationElevationHelperBase(QObject *parent = nullptr);
virtual ~ScreenLocationElevationHelperBase();
int pendingOperationsCount() const;
void cancel();
void stop();
void newComputationRequested();
ScreenLocationElevationHelperBase(const ScreenLocationElevationHelperBase &other) = delete;
ScreenLocationElevationHelperBase &operator=(ScreenLocationElevationHelperBase &other) = delete;
//@}
virtual void mainComputation() = 0;
private Q_SLOTS:
void onMainTimoutInternal();
void onKillTimout();
protected:
bool insertTask(const Esri::ArcGISRuntime::TaskWatcher &tw);
bool removeTask(const QUuid &taskId);
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,148 @@
/**
* @RDE_HEADER@
* ScreenToLocationHelper
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "screen_to_location_helper.h"
#include "Map/situation_map_utils.h"
// ESRI
#include <arcgis/TaskWatcher.h>
// Qt
#include <QPointer>
// V100.2.1: to guarante good accuracy of elevation we have to ignore 3d objects :-(
#define USE_SCREEN_TO_BASE_SURFACE
//----------------------------------------------------------------------------
namespace SituationMap
{
//----------------------------------------------------------------------------
class ScreenToLocationHelper::Private
{
public:
Private() = default;
~Private() = default;
// ESRI data:
QPointer<Esri::ArcGISRuntime::SceneGraphicsView> m_sceneGraphicsView;
int m_windowPosX = 0;
int m_windowPosY = 0;
};
//----------------------------------------------------------------------------
ScreenToLocationHelper::ScreenToLocationHelper(Esri::ArcGISRuntime::SceneGraphicsView *sceneGraphicsView)
: BaseClass(sceneGraphicsView)
, m_logger(getLogger("ScreenToLocationHelper"))
, d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
d->m_sceneGraphicsView = sceneGraphicsView;
if (d->m_sceneGraphicsView)
connect(d->m_sceneGraphicsView.data(), &Esri::ArcGISRuntime::SceneGraphicsView::screenToLocationCompleted,
this, &ScreenToLocationHelper::onScreenToLocationCompleted);
}
ScreenToLocationHelper::~ScreenToLocationHelper()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
this->stop();
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void ScreenToLocationHelper::stop()
{
if (d->m_sceneGraphicsView)
d->m_sceneGraphicsView->disconnect(this);
disconnect(this);
BaseClass::stop();
}
void ScreenToLocationHelper::screenToLocation(int windowPosX, int windowPosY)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " windowPosX = " << windowPosX << " windowPosY = " << windowPosY;
d->m_windowPosX = windowPosX;
d->m_windowPosY = windowPosY;
BaseClass::newComputationRequested();
}
void ScreenToLocationHelper::mainComputation()
{
if (!d->m_sceneGraphicsView)
return;
#ifdef USE_SCREEN_TO_BASE_SURFACE
// BUG-000109976 - Segmentation fault on Scene Viewer destruction (Esri Case #02020238)
// BUG on destroy of SceneView. So use 'screenToBaseSurface' instead.
// Difference: screenToLocations takes Building and Features into account.
// screenToBaseSurface uses baseSurface only.
const Esri::ArcGISRuntime::Point &locationMap = d->m_sceneGraphicsView->screenToBaseSurface(d->m_windowPosX, d->m_windowPosY);
emit mouseScenePositionChanged(Point3D(locationMap.x(), locationMap.y(), locationMap.z()), !locationMap.isEmpty());
#else
// qCritical() << " ScreenX=" << d->m_windowPosX << " ScreenY=" << d->m_windowPosY;
const Esri::ArcGISRuntime::TaskWatcher &tw = d->m_sceneGraphicsView->screenToLocation(d->m_windowPosX, d->m_windowPosY);
if (!insertTask(tw))
ERROR_LOG(m_logger) << LOGFUNCTION << " screenToLocation failed";
TRACE_LOG(m_logger) << LOGFUNCTION << " screenToLocation"
<< " taskId = " << tw.taskId()
<< " (m_windowPosX " << d->m_windowPosX << " m_windowPosY " << d->m_windowPosY << ")...";
#endif // USE_SCREEN_TO_BASE_SURFACE
}
void ScreenToLocationHelper::onScreenToLocationCompleted(const QUuid &taskId, const Esri::ArcGISRuntime::Point &locationMap)
{
if (!this->removeTask(taskId))
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " not our task -> skip;";
return;
}
// qCritical() << " sceneLocation= " << " x:" << locationMap.x() << " y:" << locationMap.y() << " z:" << locationMap.z();
const Point3D mapCoordinate(locationMap.x(), locationMap.y(), locationMap.z());
// ^^^^^^^^^^^^^^^^^ depends on current map scale!?
// Case 02063359 [BOS] wrong Elevation with ElevationSource
// BUG-000111521 The elevation (z) value returned by SceneGraphicsView::screenToLocationCompleted() function depends on the current map scale
TRACE_LOG(m_logger) << LOGFUNCTION << " screenToLocationCompl "
<< " taskId = " << taskId
<< " (m_windowPosX " << d->m_windowPosX << " m_windowPosY " << d->m_windowPosY << ")..."
<< " (mapCoordinate " << mapCoordinate << ", z=" << locationMap.z() << ")";
emit mouseScenePositionChanged(mapCoordinate, !locationMap.isEmpty());
}
} // namespace SituationMap
@@ -0,0 +1,73 @@
/**
* @RDE_HEADER@
* ScreenToLocationHelper
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "screen_location_elevation_helper_base.h"
#include <Map/map.h>
#include <Common/situation_map_logging.h>
// ESRI
#include <arcgis/SceneGraphicsView.h>
#include <arcgis/Point.h>
// Qt
#include <QObject>
#include <QTimer>
//----------------------------------------------------------------------------
namespace SituationMap
{
/**
* @brief The ScreenToLocationHelper class limits the load of
* Esri::ArcGISRuntime::SceneGraphicsView::screenToLocation
* (cpu load of ~50% was recognized othjerwise)
* This class limits this to 10 computation per second.
*/
class SITUATIONMAP_EXPORT ScreenToLocationHelper : public ScreenLocationElevationHelperBase
{
Q_OBJECT
using BaseClass = ScreenLocationElevationHelperBase;
public:
/*! \name Construction, Destruction */
//@{
explicit ScreenToLocationHelper(Esri::ArcGISRuntime::SceneGraphicsView *sceneGraphicsView);
~ScreenToLocationHelper();
void stop();
ScreenToLocationHelper(const ScreenToLocationHelper &other) = delete;
ScreenToLocationHelper &operator=(ScreenToLocationHelper &other) = delete;
//@}
void screenToLocation(int windowPosX, int windowPosY);
void mainComputation() override;
Q_SIGNALS:
void mouseScenePositionChanged(const Point3D &mapCoordinate, bool isValid);
private Q_SLOTS:
void onScreenToLocationCompleted(const QUuid &taskId, const Esri::ArcGISRuntime::Point &locationMap);
protected:
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
+226
View File
@@ -0,0 +1,226 @@
/**
**
** @RDE_HEADER@
** Definition of transient map objects for use while rubberbanding is done
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Joerg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 29.09.2015, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
// Own
#include "sidc_symbol_impl.h"
#include "map_impl.h"
#include <Map/bounding_box.h>
#include <Map/coord_translation_functor.h>
#include <Map/object_factory.h>
#include <GraphicalObjects/stub_graphical_object.h>
#include <GraphicalObjects/tactical_appearance.h>
#include <Symbology/sidc_codec.h>
// ESRI
#include <arcgis/Envelope.h>
#include <arcgis/Graphic.h>
//Qt
#include <QPointF>
namespace SituationMap
{
SidcSymbolImpl::SidcSymbolImpl(const QString &debugInfo) : SidcSymbolImpl(QString(), debugInfo) {;}
SidcSymbolImpl::SidcSymbolImpl(const QString &sidc, const QString &debugInfo)
: InterfaceClass() // Interface
, BaseClass(debugInfo) // Implementation
, m_sidc(sidc)
, m_color(0, 0, 0, 0)
, m_logger(getLogger("SIDC_SymbolImpl"))
{
m_visibility = true;
m_visibilityTactical = true;
m_visibilityTechnical = false;
m_selected = false;
m_pickable = false;
m_positions.setMapObjectImpl(this);
} /* SidcSymbolImpl::SidcSymbolImpl */
PositionCont SidcSymbolImpl::positions() const { return m_positions.positions(); }
void SidcSymbolImpl::setPositions(const PositionCont &posCont) { m_positions.setPositions(posCont); }
PositionCont SidcSymbolImpl::positionsTranslated() const { return m_positions.positionsTranslated(); }
void SidcSymbolImpl::setPositionsTranslated(const PositionCont &posCont) { m_positions.setPositionsTranslated(posCont); }
LineStyle SidcSymbolImpl::lineStyle() const { return BaseClass::lineStyle(); }
LineStyle &SidcSymbolImpl::rLineStyle() { return BaseClass::rLineStyle(); }
void SidcSymbolImpl::setLineStyle(const LineStyle &lineStyle) { BaseClass::setLineStyle(lineStyle); }
FillStyle SidcSymbolImpl::fillStyle() const { return BaseClass::fillStyle(); }
FillStyle &SidcSymbolImpl::rFillStyle() { return BaseClass::rFillStyle(); }
void SidcSymbolImpl::setFillStyle(const FillStyle &fillStyle) { BaseClass::setFillStyle(fillStyle); }
float SidcSymbolImpl::scale() const { return (m_tacticalAppearance) ? m_tacticalAppearance->scale() : 1; }
void SidcSymbolImpl::setScale(float scale) { if (m_tacticalAppearance) m_tacticalAppearance->setScale(scale); }
const QString &SidcSymbolImpl::sidc() const { return m_symbol.value().sidc; }
const QColor &SidcSymbolImpl::color() const { return m_color; }
void SidcSymbolImpl::boundingBox(BoundingBox &bbox) const
{
if (m_tacticalAppearance)
{
SituationMap::CoordTranslationFunctor *coordTransFunctor = nullptr;
if (const_cast<SidcSymbolImpl *>(this)->mapImpl())
{
// if useful, a MapObjectType specific functor can be prefered here:
// like: if (m_mapObjectImpl->coordTranslationFunctor()) ctFunctor=m_mapObjectImpl->coordTranslationFunctor();
coordTransFunctor = const_cast<SidcSymbolImpl *>(this)->mapImpl()->coordTranslationFunctor();
}
if (coordTransFunctor)
{
const Esri::ArcGISRuntime::Envelope &extent = m_tacticalAppearance->getGraphicGeometry().extent();
const QPointF &min = coordTransFunctor->mapToGeographic(extent.xMin(), extent.yMin());
const QPointF &max = coordTransFunctor->mapToGeographic(extent.xMax(), extent.yMax());
const QPointF &center = (min + max) * 0.5;
bbox = BoundingBox(BoundingBox::ExtentType::Sim, Point3D::fromQPointF(center), max.x() - min.x(), max.y() - min.y());
}
}
} // void SidcSymbolImpl::boundingBox(BoundingBox &bbox) const
void SidcSymbolImpl::setSidc(const QString &sidc)
{
SymbolData symbolData = symbol();
symbolData.sidc = sidc;
symbolData.useAlwaysDictionaryRenderer = (!AdvancedSymbology::SidcCodec::isCivil(sidc));
setSymbol(symbolData);
m_sidc = sidc;
} // void SidcSymbolImpl::setSidc( const QString & sidc )
void SidcSymbolImpl::setColor(const QColor &color)
{
m_color = color;
} // void SidcSymbolImpl::setColor(const QColor & color)
const QMap<QString, QVariant> &SidcSymbolImpl::attributeMap() const { return m_symbol.value().attributeMap; }
void SidcSymbolImpl::setAttributeMap(const QMap<QString, QVariant> &attrMap)
{
SymbolData symbolData = symbol();
symbolData.attributeMap = attrMap;
setSymbol(symbolData);
} // SidcSymbolImpl::setAttributeMap(const QMap<QString, QVariant> & attrMap)
void SidcSymbolImpl::defineSymbol(const QString &sidc, const PositionCont &posCont, const QMap<QString, QVariant> &attrMap, const QColor &color)
{
SymbolData symbolData = symbol();
symbolData.sidc = sidc;
symbolData.attributeMap = attrMap;
symbolData.color = (color.alpha() < 1) ? QColor(0, 0, 0, 0) : color;
symbolData.useAlwaysDictionaryRenderer = (!AdvancedSymbology::SidcCodec::isCivil(sidc));
setSymbol(symbolData);
m_sidc = sidc;
m_color = color;
m_positions.setPositions(posCont);
} // void SidcSymbolImpl::defineSymbol( const QString & sidc, const PositionCont & posCont, const QMap<QString, QVariant> & attrMap )
bool SidcSymbolImpl::updateViewImplPre()
{
return BaseClass::updateViewImplPre();
} // SidcSymbolImpl::updateViewImplPre
bool SidcSymbolImpl::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " visibility=" << m_visibility
<< " visiTechn=" << m_visibilityTechnical
<< " visiTact=" << m_visibilityTactical;
// REGG-5261 - Alle Wirkfelder blinken in 3D, wenn C-Kampf ausgelöst ist
// War mit 100.4 meist, aber nicht immer reproduzierbar.
// Wenn aber per FunctionsDialog die TacticalAppearance sichtbar wurde,
// so war ein massives Flackern erkennbar.
// Daher wird die Ausdehnung nur an SICHTBARE m_tacticalAppearance übergeben
// [basic_path_object, sidc_symbol_impl]
if (m_tacticalAppearance && m_tacticalAppearance->visibility())
{
m_tacticalAppearance->setSymbol(m_symbol.value());
m_tacticalAppearance->setPositions(m_positions.positionsTranslated());
this->resetTacticalAppearanceDrawOrderOffset(); // avoid tacticalAppearance getting drawOrderOffset (2)
}
bool success = BaseClass::updateViewImpl();
if (!success)
ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed";
else
TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success;
return success;
} /* SidcSymbolImpl::updateViewImpl */
bool SidcSymbolImpl::init(MapImpl *mapImpl, const QString &logicalLayerName)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!mapImpl) return false;
bool success = BaseClass::init(mapImpl, logicalLayerName);
if (success)
{
if (!m_mainGraphicalObject) m_mainGraphicalObject = new StubGraphicalObject();
m_symbol.refValue().sidc = m_sidc;
m_symbol.refValue().color = (m_color.alpha() < 255) ? QColor(0, 0, 0, 0) : m_color; // to avoid color replacement (use default color)
TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success;
}
else ERROR_LOG(m_logger) << LOGFUNCTION << " init failed";
return success;
} /* SidcSymbolImpl::init */
} // namespace SituationMap
+145
View File
@@ -0,0 +1,145 @@
/**
**
** @RDE_HEADER@
** Definition of transient map objects for use while rubberbanding is done
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 29.09.2015, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include "Map/sidc_symbol.h"
#include "abstract_map_object_impl.h"
#include "coordinate_translation.h"
#include <QString>
#include <QColor>
namespace SituationMap
{
/**
@class SIDC_SymbolImpl SIDC_SymbolImpl.h
The class \c SIDC_SymbolImpl creates a tactical symbol due a sidc code. It is used by class
TransientTacticalSymbol to display transient tactical symbols during rubberband input is done.
@see TransientTacticalSymbol
*/
class SITUATIONMAP_EXPORT SidcSymbolImpl : public virtual SidcSymbol, public virtual AbstractMapObjectImpl
{
using InterfaceClass = SituationMap::SidcSymbol;
using BaseClass = SituationMap::AbstractMapObjectImpl;
public:
/**
@name Construction & Destruction
@{
*/
explicit SidcSymbolImpl(const QString &debugInfo = QString());
explicit SidcSymbolImpl(const QString &sidc, const QString &debugInfo);
SidcSymbolImpl(const SidcSymbolImpl &other) = delete;
SidcSymbolImpl &operator=(SidcSymbolImpl &other) = delete;
/** @} */
/**
@name Overloaded functions from base class
@see AbstractMapObject
@{
*/
PositionCont positions() const override;
void setPositions(const PositionCont &posCont) override;
virtual PositionCont positionsTranslated() const;
virtual void setPositionsTranslated(const PositionCont &posCont);
LineStyle lineStyle() const override;
LineStyle &rLineStyle() override;
void setLineStyle(const LineStyle &lineStyle) override;
FillStyle fillStyle() const override;
FillStyle &rFillStyle() override;
void setFillStyle(const FillStyle &fillStyle) override;
float scale() const override;
void setScale(float scale) override;
/** @} */
const QString &sidc() const override;
void setSidc(const QString &) override;
const QColor &color() const override;
void setColor(const QColor &color = QColor(0, 0, 0, 0)) override;
const QMap<QString, QVariant> &attributeMap() const override;
void setAttributeMap(const QMap<QString, QVariant> &attrMap) override;
void defineSymbol(const QString &sidc,
const PositionCont &posCont,
const QMap<QString, QVariant> &attrMap,
const QColor &color = QColor(0, 0, 0, 0)) override;
/**
Overloaded from base class to implement the construction of GraphicalObjects.
*/
bool init(MapImpl *mapImpl, const QString &logicalLayerName = QStringLiteral("GraphicsLayer")) override;
void boundingBox(BoundingBox &bbox) const override;
protected:
/**
The function \c updateViewImplPre implementents how to begin transfer values to view. It is been called
after changing values from AbstractMapObject::updateView() before calling \c updateViewImplPre().
@see AbstractMapObject
*/
bool updateViewImplPre() override;
/**
The function \c updateViewImpl implementents how to transfer values to view. It is been called
after changing values from AbstractMapObject::updateView().
@see AbstractMapObject
*/
bool updateViewImpl() override;
friend class MapImpl;
QString m_sidc;
QColor m_color;
PositionsTranslated m_positions;
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
}; /* SidcSymbolImpl */
} // namespace SituationMap
+801
View File
@@ -0,0 +1,801 @@
/*
** -----------------------------------------------------------------------------------------------
**
** @RDE_HEADER@
** Implementation of fan of sight
** $Id$
** $HeadURL : svn ://192.168.133.21/TDS/branches/product/devel/trunk/Lts/Viewer/SituationMap/Rubberbands/rubberband.cpp $
** $Author : ehrlicher $
**
** -----------------------------------------------------------------------------------------------
**
** Hardware - Environment : Intel - PC
** Software - Environment : Windows 7, Qt 5.x.ArcGISRuntimeQt 100.2
**
** Language ........ : C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Autor ........... : Nikolaus Hanekamp, SEDG, Tel. 2906
** Jörg Turla, SPDG, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notices ......... :
**
** 01.02.2005, NH : Urversion
** 15.05.2018, JT : adapting GeISiR source to SituationMap
**
** -----------------------------------------------------------------------------------------------
*/
#include "sight_fan_impl.h"
#include <GraphicalObjects/graphical_object_path.h>
#include <Map/bounding_box.h>
#include <Map/coord_translation_functor.h>
#include <MapImpl/layer_factory_impl.h>
#include <MapImpl/map_impl.h>
#include <ElevationMap/elevation_map_manager.h>
#include <MathUtils/math_const.h>
#include <arcgis/Surface.h>
#include <QtCore/QSharedPointer>
#include <QtCore/QVector>
#include <QTimer>
#include <chrono>
using namespace std::chrono_literals;
namespace SituationMap
{
// class SightFanImpl::Internal;
enum SightMode { VISIBLE = 0, INVISIBLE, UNDEFINED, START };
class SightEntry
{
public:
SightEntry() : _dist(0.0), _mode(START) {;}
SightEntry(double dist, SightMode mode) : _dist(dist), _mode(mode) {;}
~SightEntry() = default;
double & dist() { return _dist; }
SightMode & mode() { return _mode; }
double dist() const { return _dist; }
SightMode mode() const { return _mode; }
private:
double _dist;
SightMode _mode;
}; // SightEntry
class SightBeam : public Referenced
{
public:
explicit SightBeam(int beamId, const QPointF &directionB, const QPointF &directionE)
: _beamId(beamId)
, _directionBeg(directionB)
, _directionEnd(directionE)
, _sightEntryVec()
, _graphicalQuadVec() {;}
SightBeam() = delete;
~SightBeam() = default;
QPointF direction() const
{
auto normalize = [](const QPointF &v) -> QPointF
{
double l = sqrt(v.x()*v.x() + v.y()*v.y());
return v / l;
};
return normalize(QPointF((_directionBeg + _directionEnd)*0.5));
} // QPointF direction()
void add(const SightEntry &sightE)
{
_sightEntryVec.push_back(sightE);
} // void add( const SightEntry &sightE)
void clear() { _sightEntryVec.clear(); }
// void draw(const QPointF &beamCenter, osg::Matrixd mat, QColor colors[]); // todo: Farbtabelle
void updateGraphic( MapImpl *mapImpl, AbstractMapObjectImpl *meMapObj, SightFanImpl::Internal * sightFanInternal );
int _beamId;
protected:
QPointF _directionBeg; // Einheitsvektoren die den Beam begrenzen
QPointF _directionEnd;
using SightEntryVec = QVector<SightEntry>;
SightEntryVec _sightEntryVec;
QVector<QSharedPointer<GraphicalObjectPath>> _graphicalQuadVec;
}; /* SightBeam */
class SightFanImpl::Internal
{
public:
using SightBeamVec = std::vector<RefPointer<SightBeam>> ;
QString m_layerName;
std::shared_ptr<ElevationMapManager> m_eMapManager;
QPointF m_center;
double m_centHat;
double m_objHat;
double m_radius;
double m_innerRadius;
double m_startAngle;
double m_dAngle;
unsigned int m_nrFans;
double m_alpha;
QRectF m_location; // Lage des SightFans in Kartenkoordinaten
SightBeamVec m_sightBeamVec;
QColor m_colors[3]; // 0 : visibleCol, 1 : invisibleCol, 2 : undefinedCol
QColor m_fanColorVec[2][4];
float m_fanMult; // Farbabsenkung jedes 2. Beams < 1.0 !
MapImpl *m_mapImpl;
Esri::ArcGISRuntime::Surface *m_surface;
LOGGER_T m_logger; ///< Interface of the Logging-Facility
Internal()
: m_layerName()
, m_center()
, m_centHat(1.8)
, m_objHat(0.5)
, m_radius(0.0)
, m_innerRadius(0.0)
, m_startAngle(0.0)
, m_dAngle(2.0*MathConst::pi/180)
, m_nrFans(180)
, m_alpha(0.8)
, m_fanMult(0.75)
, m_mapImpl(nullptr)
, m_surface(nullptr)
, m_logger(getLogger("SightFanImpl"))
{;}
void setFanColorMap( double brightness, double bias, double alpha)
{
for (int i = 0; i < 3; i++)
{
double r = (m_colors[i].redF() * brightness) + bias;
double g = (m_colors[i].greenF() * brightness) + bias;
double b = (m_colors[i].blueF() * brightness) + bias;
double a = m_colors[i].alphaF() * alpha;
m_fanColorVec[0][i] = QColor::fromRgbF(r,g,b,a);
m_fanColorVec[1][i] = QColor::fromRgbF(r*m_fanMult,g*m_fanMult,b*m_fanMult,a);
}
m_fanColorVec[0][3] = m_fanColorVec[0][2];
m_fanColorVec[1][3] = m_fanColorVec[1][2];
} // void setFanColorMap()
}; // SightFanImpl::Internal
SightFanImpl::SightFanImpl( const QString &debugInfo )
: SightFan()
, QObject()
, AbstractMapObjectImpl(debugInfo)
, m_internal(new Internal())
, m_moveDelayTimer(new QTimer(this))
{
initColors();
m_internal->setFanColorMap(1, 0, 1);
m_moveDelayTimer->setSingleShot(true);
m_moveDelayTimer->setInterval(250ms);
connect(m_moveDelayTimer, &QTimer::timeout, this, &SightFanImpl::delayedMove);
} // SightFanImpl::SightFanImpl
SightFanImpl::~SightFanImpl()
{
delete m_internal;
} // SightFanImpl::~SightFanImpl()
void SightFanImpl::initColors()
{
m_internal->m_fanMult = 0.75;
m_internal->m_colors[0] = QColor(0, 255, 0, 180);
m_internal->m_colors[1] = QColor(255, 0, 0, 180);
m_internal->m_colors[2] = QColor(0, 0, 255, 180);
} // void SightFanImpl::initColors()
void SightFanImpl::setFanColor( const QColor &visible, const QColor &invisible, const QColor &undefined)
{
m_internal->m_colors[0] = visible;
m_internal->m_colors[1] = invisible;
m_internal->m_colors[2] = undefined;
} // void SightFanImpl::setFanColor( const QColor &visible, const QColor &invisible, const QColor &undefined)
void SightFanImpl::setFanColorMark( float colMult)
{
if( colMult > 1.0)
colMult = 1.0;
if( colMult < 0.0)
colMult = 0.0;
m_internal->m_fanMult = colMult;
} // void SightFanImpl::setFanColorMark( float colMult)
void SightFanImpl::setCenter(const QPointF &center) { m_internal->m_center = center; }
void SightFanImpl::setCenter(const Point3D &center) { m_internal->m_center = center.toPointF();
m_internal->m_centHat = center.z(); }
void SightFanImpl::setCenter(const QPointF &center, double centerHat) { m_internal->m_center = center;
m_internal->m_centHat = centerHat; }
void SightFanImpl::setCenterHat(double centerHat) { m_internal->m_centHat = centerHat; }
void SightFanImpl::setObjectHat(double objHat) { m_internal->m_objHat = objHat; }
void SightFanImpl::setRadius(double radius) { m_internal->m_radius = radius; }
void SightFanImpl::setInnerRadius(double radius) { m_internal->m_innerRadius = radius; }
void SightFanImpl::setStartAngle(double startAngle) { m_internal->m_startAngle = startAngle; }
void SightFanImpl::setdeltaAngle(double dAngle) { m_internal->m_dAngle = dAngle; }
void SightFanImpl::setNoOfFans(double noFans) { m_internal->m_nrFans = noFans; }
void SightFanImpl::setAlpha(double alpha)
{
if (alpha < 0.0) alpha = 0.0;
if (alpha > 1.0) alpha = 1.0;
m_internal->m_alpha = alpha;
} // void SightFanImpl::setAlpha(double alpha)
QPointF SightFanImpl::center() const { return m_internal->m_center; }
double SightFanImpl::centerHat() const { return m_internal->m_centHat; }
double SightFanImpl::objectHat() const { return m_internal->m_objHat; }
double SightFanImpl::innerRadius() const { return m_internal->m_innerRadius; }
double SightFanImpl::radius() const { return m_internal->m_radius; }
double SightFanImpl::startAngle() const { return m_internal->m_startAngle; }
double SightFanImpl::dAngle() const { return m_internal->m_dAngle; }
double SightFanImpl::alpha() const { return m_internal->m_alpha; }
unsigned int SightFanImpl::noOfFans() const { return m_internal->m_nrFans; }
bool SightFanImpl::init(MapImpl *mapImpl, const QString &logicalLayerName)
{
if (mapImpl)
{
m_internal->m_mapImpl = mapImpl;
m_internal->m_layerName = logicalLayerName;
m_internal->m_eMapManager = mapImpl->getElevationMapManager();
if (!m_internal->m_eMapManager)
{
ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " failure, no ElevationMapManager";
return false;
}
if (!AbstractMapObjectImpl::init(mapImpl, logicalLayerName))
{
ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " failure, AbstractMapObjectImpl::init failed";
return false;
}
return true;
}
ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " init SightFanImpl failed";
return false;
} // bool SightFanImpl::init(MapImpl *mapImpl, const QString &logicalLayerName)
void SightFanImpl::boundingBox(BoundingBox &bbox) const
{
bbox.reset();
CoordTranslationFunctor * coordTransFunc = m_internal->m_mapImpl->coordTranslationFunctor();
if (coordTransFunc)
{
const QPointF &centerMap = coordTransFunc->geographicToMap(m_internal->m_center);
double diameter = 2 * m_internal->m_radius;
bbox = BoundingBox( BoundingBox::ExtentType::Sim, Point3D::fromQPointF(centerMap), diameter, diameter );
}
} // void SightFanImpl::boundingBox(BoundingBox &bbox) const
//---------------------------------------------------------------------------------------------------
void SightFanImpl::initFan( const Point3D &center
, double objHat
, double radius
, double innerRadius
, double startAngle
, double dAngle
, unsigned int nrFans
, double alpha )
{
initFan( center.toPointF(), center.z(), objHat, radius, innerRadius, startAngle, dAngle, nrFans, alpha );
} // void SightFanImpl::init(...)
void SightFanImpl::initFan( const QPointF &center
, double centHat
, double objHat
, double radius
, double innerRadius
, double startAngle
, double dAngle
, unsigned int nrFans
, double alpha )
{
if (!m_internal->m_mapImpl)
{
ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " no mapImpl";
return;
}
if (alpha < 0.0) alpha = 0.0;
if (alpha > 1.0) alpha = 1.0;
m_internal->m_center = center;
m_internal->m_centHat = centHat;
m_internal->m_objHat = objHat;
m_internal->m_radius = radius;
m_internal->m_innerRadius = innerRadius;
m_internal->m_startAngle = startAngle;
m_internal->m_dAngle = (dAngle < 0) ? -dAngle : dAngle;
m_internal->m_nrFans = nrFans;
m_internal->m_alpha = alpha;
QPointF offc(m_internal->m_radius, -m_internal->m_radius);
m_internal->m_location = QRectF(m_internal->m_center - offc, m_internal->m_center + offc);
if ((m_internal->m_nrFans*m_internal->m_dAngle) > (MathConst::pi*2.0))
{
m_internal->m_nrFans = floor((MathConst::pi*2.0) / m_internal->m_dAngle + 0.5); // To avoid error with floor; 30.3.2006 JT
}
calculateSightBeamVec();
} /* SightFan::SightFan */
void SightFanImpl::calculateSightBeamVec()
{
m_internal->m_sightBeamVec.clear();
double dAlfa = (m_internal->m_dAngle / 2.0) + 0.00025; // 0.00025 wegen Überlappung, auf antialiazed Systemen nicht nötig
for (unsigned int i = 0; i < m_internal->m_nrFans; i++)
{
double alfa = m_internal->m_startAngle + m_internal->m_dAngle * i;
double alfaBeg = alfa - dAlfa;
double alfaEnd = alfa + dAlfa;
RefPointer<SightBeam> sBeam = RefPointer<SightBeam>(new SightBeam(i, QPointF(cos(alfaBeg), sin(alfaBeg)), QPointF(cos(alfaEnd), sin(alfaEnd))));
calculateSightBeam(*(sBeam.get()));
m_internal->m_sightBeamVec.push_back(sBeam);
}
} // void SightFanImpl::calculateSightBeamVev()
void SightFanImpl::move(const QPointF &center, double centHat, double objHat)
{
m_internal->m_centHat = centHat;
m_internal->m_objHat = objHat;
move(center);
} // void SightFanImpl::move(ElevationMap &eMap, QPointF &center, double centHat, double objHat)
void SightFanImpl::move(const QPointF & center)
{
m_moveDelayedCenter = center;
m_moveDelayTimer->start();
}
void SightFanImpl::show()
{
move( m_internal->m_center );
} // void SightFanImpl::show
bool SightFanImpl::updateViewImplPre()
{
return true;
} // bool SightFanImpl::updateViewImplPre()
bool SightFanImpl::updateViewImpl()
{
show();
return true;
} // bool SightFanImpl::updateViewImpl()
bool SightFanImpl::updateViewImplPost()
{
return true;
} // bool SightFanImpl::updateViewImplPost()
inline QPointF mittelBreitenRechnung(const QPointF &start, const QPointF &direction, double distance)
{
// ---------------------------------------------------------------
// Vec2d mittelBreitenRechnung( Vec2d start, Vec2d direction, double distance )
// Berechnet näherungsweise die Koordinate der Endposition eines
// Beams gemäß der ersten Aufgabe der Besteckrechnung durch das
// Verfahren der Mittelbreite.
// Siehe http://astrosail.de/de/static/tutorial/best3.php?cat=42
// Start ist die Anfangskoordinate im Bogenmaß, direction der
// normierte Richtungsvektor, distance die Entfernung in Metern.
// ---------------------------------------------------------------
double cosKurs = direction.y();
double sinKurs = direction.x();
double distanceBogen = distance / MathConst::EarthRadius;
double lat = distanceBogen * cosKurs + start.y();
double mittelbreite = (start.y() + lat) * 0.5;
double lon = distanceBogen * sinKurs / cos(mittelbreite) + start.x();
return {lon, lat};
}
inline QPointF endOfBeam( const QPointF &start, const QPointF &direction, double distance )
{
return mittelBreitenRechnung(start,direction,distance);
}
void SightFanImpl::calculateSightBeam(SightBeam &sBeam)
{
const QPointF &direction = sBeam.direction();
const QPointF &end = endOfBeam(m_internal->m_center, direction, m_internal->m_radius);
LOSVector losProfile;
m_internal->m_eMapManager->LOSProfile(m_internal->m_center, end, losProfile);
calculateSightBeam(losProfile, sBeam);
sBeam.updateGraphic(m_internal->m_mapImpl, this, m_internal);
} // void SightFanImpl::calculateSightBeam(ElevationMap &eMap, SightBeam &sBeam)
void SightFanImpl::calculateSightBeam( const LOSVector &losProfile, SightBeam &sBeam )
{
sBeam.clear();
if (losProfile.empty()) // Startpunkt undefiniert oder ausserhalb der Karte
{
sBeam.add( SightEntry(m_internal->m_innerRadius, START) );
sBeam.add( SightEntry(m_internal->m_radius, UNDEFINED) );
return;
}
LOSVector::const_iterator itr = losProfile.begin();
double centerHeight = itr->height() + m_internal->m_centHat;
double d1 = 0.0;
double height1 = itr->height();
// überspringen bis _innerRadius erreicht ist !
while(++itr != losProfile.end())
{
double d2 = itr->dist(); // *EarthRadius;
double height2 = itr->height();
if (d2 >= m_internal->m_innerRadius)
{
height1 = height1 + (m_internal->m_innerRadius - d1) * ( height2 - height1)/(d2 - d1);
d1 = m_internal->m_innerRadius;
--itr;
break;
}
d1 = d2;
height1 = height2;
}
// bool visible = true;
SightMode mode = START;
double curM = (height1 - centerHeight ) / d1; // bestimmende Steigung
sBeam.add(SightEntry( d1, START));
if (itr != losProfile.end())
{
while (++itr != losProfile.end())
{
double d2 = itr->dist(); // *EarthRadius;
double height2 = itr->height();
if (std::isnan(height2)) // Undefined
{
if (mode != START)
{
sBeam.add(SightEntry(d1, mode));
}
mode = UNDEFINED;
break;
}
double hObj = (height2 + m_internal->m_objHat - centerHeight );
double objM = hObj / d2; // Steigung bezogen auf Objekte( Objekte verdecken nicht !)
SightMode newMode = ((objM >= curM)) ? VISIBLE : INVISIBLE;
if(newMode != mode)
{
if ((mode != START) && (d2 != d1))
{
// Korrekturwert für Distanz berechnen, unter Berücksichtigung der Objekthoehe !!
double localM = (height2 - height1) /(d2 -d1);
// localM == curM kann nicht auftreten, da newMode != mode
double dCorr = (hObj - (curM * d2)) / (localM - curM);
if (dCorr < 0.0)
dCorr = 0.0;
dCorr = d2 - dCorr;
if (dCorr < d1)
dCorr = d1;
sBeam.add( SightEntry( dCorr, mode) );
}
mode = newMode;
}
double terrM = (height2 - centerHeight) / d2; // Steigung bezogen auf Gelaende
if (terrM >= curM)
{
curM = terrM; // neue bestimmende Steigung
}
height1 = height2;
d1 = d2;
} /* while */
}
sBeam.add( SightEntry(m_internal->m_radius, mode) );
}
/*!
* \brief SightFanImpl::delayedMove
* \param center
* \details Delays the move call to prevent crash of map beacause of the heavy load while moving
*/
void SightFanImpl::delayedMove()
{
m_internal->m_center = m_moveDelayedCenter;
m_internal->setFanColorMap(1, 0, 1);
QPointF offc(m_internal->m_radius, -m_internal->m_radius);
m_internal->m_location = QRectF(m_internal->m_center - offc, m_internal->m_center + offc);
if (m_internal->m_nrFans == m_internal->m_sightBeamVec.size())
{
for (RefPointer<SightBeam> sBeam : qAsConst(m_internal->m_sightBeamVec))
{
calculateSightBeam(*sBeam.get());
}
}
else
{
calculateSightBeamVec();
}
}
void SightBeam::updateGraphic(MapImpl *mapImpl, AbstractMapObjectImpl *meMapObj, SightFanImpl::Internal *sightFanInternal)
{
if (!mapImpl || !meMapObj || !sightFanInternal)
return;
if (_sightEntryVec.size() <= 1)
return;
Esri::ArcGISRuntime::GraphicsOverlay *graphicLayer = sightFanInternal->m_mapImpl->getGraphicsLayer(sightFanInternal->m_layerName);
if (!graphicLayer)
{
ERROR_LOG(sightFanInternal->m_logger) << LOGFUNCTION << " Creating graphic overlayer \'" << sightFanInternal->m_layerName << "\' failed. Can't render sight fan";
return;
}
// _sightEntryVec[0] is SightMode::START
// resize _graphicalQuadVec according _sightEntryVec
// (_sightEntryVec.size() <= 1 is chexck above)
const int newGraphicalSize = _sightEntryVec.size() - 1;
_graphicalQuadVec.resize(newGraphicalSize);
CoordTranslationFunctor *geoToMap = sightFanInternal->m_mapImpl->coordTranslationFunctor();
int quadNo = 0;
SightEntryVec::iterator itr = _sightEntryVec.begin();
const QPointF &center = sightFanInternal->m_center;
QPointF v0 = endOfBeam(center, _directionBeg, itr->dist());
QPointF v1 = endOfBeam(center, _directionEnd, itr->dist());
if (geoToMap)
{
v0 = geoToMap->geographicToMap(v0);
v1 = geoToMap->geographicToMap(v1);
}
PositionCont locationVec;
// skip first, we know mode==SightMode::START
while (++itr != _sightEntryVec.end())
{
// GraphicalObjectPath already created :
QSharedPointer<GraphicalObjectPath> &spGopRef = _graphicalQuadVec[quadNo];
if (!spGopRef)
{
GraphicalObjectPath * grQuad = new GraphicalObjectPath( GraphicalObjectPathType::Polygon
, mapImpl->getEsriGeoView()
, graphicLayer
, QStringLiteral("FanOfSight-Beam-%1").arg(_beamId)
, meMapObj->uniqueMapId() );
grQuad->setPickable(false);
grQuad->rLineStyle().setLinePattern(LinePattern::Null);
grQuad->rFillStyle().setFillPattern(FillPattern::Solid);
// write to _graphicalQuadVec
spGopRef = QSharedPointer<GraphicalObjectPath>(grQuad);
}
// : GraphicalObjectPath already created
GraphicalObjectPath * grQuad = _graphicalQuadVec[quadNo].data();
Q_ASSERT_X(grQuad, "SightBeam", "GraphicalObjectPath invalid");
SightMode mode = itr->mode();
double dist = itr->dist();
QColor quadColor = sightFanInternal->m_fanColorVec[_beamId % 2][int(mode)];
quadColor.setAlphaF( quadColor.alphaF()*sightFanInternal->m_alpha );
grQuad->rFillStyle().setFillColor(quadColor);
QPointF v2 = endOfBeam(center, _directionBeg, dist);
QPointF v3 = endOfBeam(center, _directionEnd, dist);
if (geoToMap)
{
v2 = geoToMap->geographicToMap(v2);
v3 = geoToMap->geographicToMap(v3);
}
locationVec.clear();
locationVec << Point3D::fromQPointF(v0);
locationVec << Point3D::fromQPointF(v1);
locationVec << Point3D::fromQPointF(v2);
locationVec << Point3D::fromQPointF(v3);
grQuad->setPositions(locationVec);
grQuad->updateView();
v0 = v3;
v1 = v2;
++quadNo;
} /* while */
} // void SightBeam::updateGraphic(MapImpl *mapImpl, AbstractMapObjectImpl *meMapObj, SightFanImpl::Internal *sightFanInternal)
#ifdef NEVER
bool SightFan::draw( const AbstractView * const view, osg::Matrixd mat,
float alpha, float brightness, float bias, MapObject::RenderState rstate ) // todo: Farbtabelle
{
if ((rstate & _renderState) == 0) return false;
if( !( _location.intersects( view->rectBound()) )) return false; // isCulled
ColorVec colors[2][4];
for( int i = 0; i < 3; i++)
{
colors[0][i] = (_colors[i] * brightness) + bias;
colors[1][i] = colors[0][i] * _fanMult;
colors[0][i].alpha = alpha * _colors[i].alpha;
colors[1][i].alpha = alpha * _colors[i].alpha;
}
colors[0][3] = colors[0][2];
colors[1][3] = colors[1][2];
GLint shadeModel;
glGetIntegerv( GL_SHADE_MODEL, &shadeModel );
glShadeModel(GL_FLAT);
glEnable( GL_BLEND);
int n = 0;
for( SightBeamVec::iterator itr = _sightBeamVec.begin(); itr != _sightBeamVec.end(); ++itr)
{
(*itr).get()->draw( _center, mat, colors[(++n)%2]);
}
glDisable( GL_BLEND);
glShadeModel(shadeModel);
return true;
} /* SightFan::draw */
void SightBeam::draw( Vec2d center, osg::Matrixd mat, ColorVec colors[]) // todo: Farbtabelle !?
{
float z = 0;
if( _sightEntryVec.size() <= 1) return;
SightEntryVec::iterator itr = _sightEntryVec.begin();
Vec2d v0 = endOfBeam( center, _directionBeg, (*itr).dist() );
Vec2d v1 = endOfBeam( center, _directionEnd, (*itr).dist() );
osg::Vec3d _v0 = mat.preMult( osg::Vec3d( v0[0], v0[1], 0.0 ));
osg::Vec3d _v1 = mat.preMult( osg::Vec3d( v1[0], v1[1], 0.0 ));
glBegin( GL_QUAD_STRIP);
{
glVertex3f( _v0.x(), _v0.y(), z);
glVertex3f( _v1.x(), _v1.y(), z);
while( ++itr != _sightEntryVec.end() )
{
SightMode mode = (*itr).mode();
glColor4f( colors[mode].red, colors[mode].green, colors[mode].blue, colors[mode].alpha);
v0 = endOfBeam( center, _directionBeg, (*itr).dist() );
v1 = endOfBeam( center, _directionEnd, (*itr).dist() );
_v0 = mat.preMult( osg::Vec3d( v0[0], v0[1], 0.0 ));
_v1 = mat.preMult( osg::Vec3d( v1[0], v1[1], 0.0 ));
glVertex3f( _v0.x(), _v0.y(), z);
glVertex3f( _v1.x(), _v1.y(), z);
} /* while */
}
glEnd();
} /* SightBeam::draw */
#endif
} // SituationMap
+184
View File
@@ -0,0 +1,184 @@
/*
** -----------------------------------------------------------------------------------------------
**
** @RDE_HEADER@
** Implementation of fan of sight
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.x. ArcGISRuntimeQt 100.2
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Autor ...........: Nikolaus Hanekamp, SEDG, Tel. 2906
** Jörg Turla, SPDG, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notices .........:
**
** 01.02.2005, NH : Urversion
** 15.05.2018, JT : adapting GeISiR source to SituationMap
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include <Map/sight_fan.h>
#include <ElevationMap/elevation_map.h>
#include <MapImpl/abstract_map_object_impl.h>
namespace SituationMap
{
class MapImpl;
class SightBeam;
class SITUATIONMAP_EXPORT SightFanImpl : public QObject, public AbstractMapObjectImpl, public virtual SightFan
{
friend class SightBeam;
Q_OBJECT
public:
explicit SightFanImpl(const QString &debugInfo = QString());
virtual ~SightFanImpl();
void initColors();
void initFan( const QPointF &center
, double centHat
, double objHat
, double radius
, double innerRadius
, double startAngle
, double dAngle
, unsigned int nrFans
, double alpha ) override;
void initFan( const Point3D &center
, double objHat
, double radius
, double innerRadius
, double startAngle
, double dAngle
, unsigned int nrFans
, double alpha ) override;
void setCenter(const QPointF &center) override;
void setCenter(const Point3D &center) override;
void setCenter(const QPointF &center, double centerHat) override;
void setCenterHat(double centerHat) override;
void setObjectHat(double objHat) override;
void setRadius(double radius) override;
void setInnerRadius(double radius) override;
void setStartAngle(double startAngle) override;
void setdeltaAngle(double dAngle) override;
void setNoOfFans(double noFans) override;
void setAlpha(double alpha) override;
QPointF center() const override;
double centerHat() const override;
double objectHat() const override;
double innerRadius() const override;
double radius() const override;
double startAngle() const override;
double dAngle() const override;
double alpha() const override;
unsigned int noOfFans() const override;
void move(const QPointF &center) override;
void move(const QPointF &center, double centHat, double objHat) override;
void setFanColor(const QColor & visible, const QColor &invisible, const QColor &undefined) override;
void setFanColorMark(float colMult) override;
void show() override;
/**
Overloaded from base class to implement the construction of GraphicalObjects.
*/
bool init( MapImpl *mapImpl, const QString &logicalLayerName = QStringLiteral("SightFanLayer")) override;
void boundingBox(BoundingBox &bbox) const override;
protected:
bool updateViewImplPre() override;
bool updateViewImpl() override;
bool updateViewImplPost() override;
void calculateSightBeamVec();
void calculateSightBeam(SightBeam &sBeam);
void calculateSightBeam(const LOSVector &losProfile, SightBeam &sBeam);
class Internal;
Internal *m_internal;
private slots:
void delayedMove();
private:
QTimer *m_moveDelayTimer;
QPointF m_moveDelayedCenter;
}; // SightFanImpl
} // SituationMap
/* -----------------------------------------------------------------------------------------------
** CVS-Id/Release ...: $Header$
** -----------------------------------------------------------------------------------------------
**
** Historie:
**
** $Log: not supported by cvs2svn $
** Revision 1.9 2007/12/18 18:11:00 joerg
** draw() um bias erweitert und Rückgabe Boolean = true wenn wriklich gezeichnet wurde.
**
** Revision 1.8 2007/08/01 10:50:39 Jörg
** Neu: dirtyFlags und RenderState
**
** Revision 1.7 2007/07/23 14:04:09 Jörg
** AbstractView eingeführt
**
** Revision 1.6 2007/07/11 08:13:46 Jörg
** move jetzt auch nur mit Position
**
** Revision 1.5 2005/11/09 13:44:19 klaus
** Umsetzung auf Linux;
**
** Revision 1.4 2005/11/09 11:12:26 klaus
** Anpassung an Linux: Kommentar hinter #endif mit "//" versehen
**
** Revision 1.3 2005/04/15 13:05:18 joerg
** Resource Control
**
** Revision 1.2 2005/02/10 13:51:01 klaus
** Bias für SightFans eingeführt
**
** Revision 1.1 2005/02/09 17:08:11 klaus
** 1. Version der SightFans
**
**
** ----------------------------------------------------------------------------------------------- */
+414
View File
@@ -0,0 +1,414 @@
/**
* @RDE_HEADER@
* Interface-Implementation of TerrainProfile
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "terrain_profile_impl.h"
#include "terrain_profile_processor.h"
#include "layer_factory_impl.h"
#include "coord_translation_functor_impl.h"
#include <LtsCoordTrans/lts_coord_trans.h>
#include <LtsWidgets/lts_global_wait_dialog.h>
#include "Rubberbands/rubberband_factory.h"
//ESRI
#include <arcgis/Surface.h>
// Qt
#include <QList>
#include <QVector>
#include <QPair>
#include <functional>
namespace SituationMap
{
class TerrainProfileImpl::Private
{
public:
Private() = default;
~Private() = default;
// ESRI data:
// Own data:
SituationMap::MapImpl *m_mapImpl = nullptr;
Esri::ArcGISRuntime::Surface *m_surface = nullptr;
TerrainProfileParameter m_terrainProfileParameter;
QPointer<RB::RubberbandBase> m_rubbandBase = nullptr;
using COUNTER_T = long;
static COUNTER_T m_terrainProfileCounter;
QMap<COUNTER_T, QPointer<TerrainProfileProcessor>> m_terrainProfileMap;
std::function< bool(long id, const QVector<QPair<QPointF, double>> &)> m_finishedCallback;
}; // class TerrainProfileImpl::Private
//-----------------------------------------------------------------------------
TerrainProfileImpl::Private::COUNTER_T TerrainProfileImpl::Private::m_terrainProfileCounter = 0;
//-----------------------------------------------------------------------------
// class TerrainProfileImpl
//-----------------------------------------------------------------------------
TerrainProfileImpl::TerrainProfileImpl(SituationMap::MapImpl *mapImpl)
: BaseClass()
, QObject()
, m_logger(getLogger("TerrainProfileImpl"))
, d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
d->m_mapImpl = mapImpl;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
TerrainProfileImpl::~TerrainProfileImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
this->remove();
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
bool TerrainProfileImpl::isReady()
{
init();
bool isReady =
d->m_surface && d->m_surface->loadStatus() == Esri::ArcGISRuntime::LoadStatus::Loaded &&
d->m_mapImpl->isReady();
if (!isReady)
{
Esri::ArcGISRuntime::LoadStatus ls = Esri::ArcGISRuntime::LoadStatus::Unknown;
if (d->m_surface)
ls = d->m_surface->loadStatus();
DEBUG_LOG(m_logger) << LOGFUNCTION
<< " d->m_surface = " << d->m_surface
<< " d->m_surface->loadStatus = " << ls
<< " d->m_mapImpl->isReady() = " << d->m_mapImpl->isReady();
}
return isReady;
}
void TerrainProfileImpl::setFinishedCallback(const std::function< bool(long id, const QVector<QPair<QPointF, double> > &)> &finishedCallback)
{
d->m_finishedCallback = finishedCallback;
}
void TerrainProfileImpl::setTerrainProfileDialog(long id, QPointer<TerrainProfileDialog> &terrainProfileDialog)
{
if (!d->m_terrainProfileMap.contains(id))
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " no proc found for id = " << id;
return;
}
QPointer<TerrainProfileProcessor> proc = d->m_terrainProfileMap[id];
if (!proc)
return;
proc->setTerrainProfileDialog(terrainProfileDialog);
}
bool TerrainProfileImpl::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!isReady())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " failed, not ready";
return false;
}
// remove any pending/active rubberbands of our map:
RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl);
// d->m_rubbandBase; should be cleared now
const Mode mode = d->m_mapImpl->mode();
switch (mode)
{
case Mode::Mode_2D:
d->m_rubbandBase = createRubberband2D();
break;
case Mode::Mode_3D:
// d->m_rubbandBase = createRubberband3D();
break;
default:
ERROR_LOG(m_logger) << LOGFUNCTION << " mode (" << mode << ")" << " does not support viewshed";
return false;
break;
}
return true;
}
long TerrainProfileImpl::create(const TerrainProfileParameter &terrainProfileParameter)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " START" << " terrainProfileParameter: " << terrainProfileParameter;
if (!isReady())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " failed, not ready";
return -1;
}
// remove all former
this->remove();
// store current values:
d->m_terrainProfileParameter = terrainProfileParameter;
TerrainProfileProcessor *proc = new TerrainProfileProcessor();
if (!proc->init(d->m_mapImpl, d->m_terrainProfileCounter))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " init of TerrainProfileProcessor failed";
delete proc;
return -1;
}
d->m_terrainProfileMap[d->m_terrainProfileCounter++] = proc;
connect(proc, &TerrainProfileProcessor::locationsToElevationsCompleted,
this, &TerrainProfileImpl::onLocationsToElevationsCompleted, Qt::UniqueConnection);
// GlobalWaitDialog
LtsWidgets::LtsGlobalWaitDialog::getInstance()->wait();
proc->start(d->m_terrainProfileParameter);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return d->m_terrainProfileCounter;
}
void TerrainProfileImpl::remove(long terrainProfileId)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " terrainProfileId=" << terrainProfileId;
if (terrainProfileId < 0)
{
for (const QPointer<TerrainProfileProcessor> &proc : qAsConst(d->m_terrainProfileMap))
delete proc;
d->m_terrainProfileMap.clear();
return;
}
if (d->m_terrainProfileMap.contains(terrainProfileId))
{
QPointer<TerrainProfileProcessor> proc = d->m_terrainProfileMap[terrainProfileId];
if (proc)
delete d->m_terrainProfileMap[terrainProfileId];
d->m_terrainProfileMap.remove(terrainProfileId);
}
else
ERROR_LOG(m_logger) << LOGFUNCTION << " terrainProfileId not found, id=" << terrainProfileId;
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " terrainProfileId=" << terrainProfileId;
}
void TerrainProfileImpl::init()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!d->m_mapImpl)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no mapImpl";
return;
}
const LayerFactoryImpl &layerFactoryImpl = d->m_mapImpl->layerFactoryImpl();
if (!d->m_surface)
d->m_surface = layerFactoryImpl.baseSurface();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
RB::RubberbandMultiPointFeature *TerrainProfileImpl::createRubberband2D()
{
// remove any pending/active rubberbands of our map:
RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl);
RB::RubberbandMultiPointFeature *rubberband =
d->m_mapImpl->rubberbandFactory().create<SituationMap::RB::RubberbandMultiPointFeature>(this);
if (!rubberband || !rubberband->rubberbandIsAlive())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " create2D failed";
return nullptr;
}
rubberband->setAutoDelete(true);
connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &TerrainProfileImpl::onRbAbort, Qt::UniqueConnection);
connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &TerrainProfileImpl::onRb2DApply, Qt::UniqueConnection);
return rubberband;
}
/*
RB::RubberbandMultiPointFeature3D *TerrainProfileImpl::createRubberband3D()
{
// remove any pending/active rubberbands of our map:
RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl);
RB::RubberbandMultiPointFeature3D *rubberband =
d->m_mapImpl->rubberbandFactory().create3D<RB::RubberbandMultiPointFeature3D>(this);
if (!rubberband || !rubberband->rubberbandIsAlive())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " create3D failed";
return nullptr;
}
rubberband->setAutoDelete(true);
// rubberband->constrainQuadratic(true);
// rubberband->constrainOrthogonal(true);
connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &TerrainProfileImpl::onRbAbort, Qt::UniqueConnection);
connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &TerrainProfileImpl::onRb3DApply, Qt::UniqueConnection);
return rubberband;
}
*/
void TerrainProfileImpl::onRbAbort(RB::RubberbandBase *rbnd)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!rbnd)
return;
DEBUG_LOG(m_logger) << LOGFUNCTION << " rb abort";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void TerrainProfileImpl::onRb2DApply(RB::RubberbandBase *rbnd)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!rbnd)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no rubberband";
return;
}
RB::RubberbandMultiPointFeature *polyline = static_cast<RB::RubberbandMultiPointFeature *>(rbnd);
TerrainProfileParameter para;
para.m_coordinates = convert2D3D(polyline->coordinateVector(RB::CoordinateSystem::Geo));
long id = this->create(para);
if (id < 0)
ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed!?";
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " para.m_coordinates.size()= " << para.m_coordinates.size();
}
void TerrainProfileImpl::onLocationsToElevationsCompleted(long id)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << ", id=" << id;
// GlobalWaitDialog
LtsWidgets::LtsGlobalWaitDialog::getInstance()->done();
const auto it = d->m_terrainProfileMap.find(id);
// if (it == d->m_terrainProfileMap.constEnd())
// {
// ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed!?";
// return;
// }
QPointer<TerrainProfileProcessor> proc = it.value();
if (!proc)
return;
const QVector<QPair<QPointF, double>> &resultList = proc->results();
INFO_LOG(m_logger) << LOGFUNCTION << " START"
<< " id =" << id
<< " m_locationToElevationBulk.resultList().size()=" << resultList.size();
if (d->m_finishedCallback)
d->m_finishedCallback(id, resultList);
else
WARNING_LOG(m_logger) << LOGFUNCTION << " NO exe CallBack!?";
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << ", id=" << id;
}
/*
void TerrainProfileImpl::onRb3DApply(RB::RubberbandBase *rbnd)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!rbnd)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no rubberband";
return;
}
RB::RubberbandMultiPointFeature3D *rubberband = static_cast<RB::RubberbandMultiPointFeature3D *>(rbnd);
const QVector<QPointF> &data = rubberband->coordinateVector(RB::CoordinateSystem::Map); // 3D -> RB::CoordinateSystem::Map -> degree
if (data.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed!?";
return;
}
if (data.size() < 2)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " insufficiant number of coords";
return;
}
ViewshedParameter viewshedParameter;
// degree to rad
const double factor = M_PI / 180.0;
const QPointF &center = data.at(0) * factor;
viewshedParameter.viewshedOrigin_latLon = center;
if (data.size() >= 2)
{
static Esri::ArcGISRuntime::GeometryEngine *gEngine = Esri::ArcGISRuntime::GeometryEngine::instance();
const Esri::ArcGISRuntime::SpatialReference &sr = d->m_mapImpl->getEsriSpatialReference();
QPointF coord = data.at(0);
Esri::ArcGISRuntime::Point geom1(coord.x(), coord.y(), sr);
coord = data.at(1);
Esri::ArcGISRuntime::Point geom2(coord.x(), coord.y(), sr);
Esri::ArcGISRuntime::GeodeticDistanceResult geodeticDistanceResult = gEngine->distanceGeodetic(geom1, geom2,
Esri::ArcGISRuntime::LinearUnitId::Meters,
Esri::ArcGISRuntime::AngularUnitId::Degrees,
Esri::ArcGISRuntime::GeodeticCurveType::Geodesic);
viewshedParameter.outerRadius = geodeticDistanceResult.distance();
}
long id = this->create(viewshedParameter);
if (id < 0)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed!?";
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
*/
} // namespace SituationMap
+78
View File
@@ -0,0 +1,78 @@
/**
* @RDE_HEADER@
* Interface-Implementation of TerrainProfile
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/terrain_profile.h> // Interface
#include "map_impl.h"
#include "Rubberbands/rubberband_multi_point_feature.h"
#include <Utils/terrain_profile_dialog.h>
// Qt
#include <QPointF>
#include <QVector>
//----------------------------------------------------------------------------
namespace SituationMap
{
class TerrainProfileImpl : public QObject, public virtual TerrainProfile
{
Q_OBJECT
using BaseClass = TerrainProfile;
public:
explicit TerrainProfileImpl(SituationMap::MapImpl *mapImpl);
virtual ~TerrainProfileImpl();
TerrainProfileImpl(const TerrainProfileImpl &other) = delete;
TerrainProfileImpl &operator=(TerrainProfileImpl &other) = delete;
bool isReady();
void setFinishedCallback(const std::function< bool(long id, const QVector<QPair<QPointF, double>> &)> &finishedCallback) override;
void setTerrainProfileDialog(long id, QPointer<SituationMap::TerrainProfileDialog> &terrainProfileDialog) override;
bool create() override;
long create(const TerrainProfileParameter &terrainProfileParameter) override;
void remove(long terrainProfileId = -1) override;
private Q_SLOTS:
void onRbAbort(RB::RubberbandBase *rbnd);
void onRb2DApply(RB::RubberbandBase *rbnd);
void onLocationsToElevationsCompleted(long id);
private:
void init();
RB::RubberbandMultiPointFeature *createRubberband2D();
RB::RubberbandMultiPointFeature *createRubberband3D();
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;
}; // TerrainProfileImpl
} // namespace SituationMap
@@ -0,0 +1,365 @@
/**
* @RDE_HEADER@
* TerrainProfileProcessor
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "terrain_profile_processor.h"
#include "map_impl.h"
#include <GraphicalObjects/graphical_object_path.h>
#include <Map/object_factory.h>
#include <Map/situation_map_utils.h>
#include <Map/coord_translation_functor.h>
#include <MapImpl/layer_factory_impl.h>
// ESRI
#include <arcgis/GeometryEngine.h>
#include <arcgis/PolylineBuilder.h>
// Qt
namespace SituationMap
{
//-----------------------------------------------------------------------------
// class ViewshedBase
//-----------------------------------------------------------------------------
TerrainProfileProcessor::TerrainProfileProcessor(QObject *parent)
: QObject(parent)
, m_logger(getLogger("TerrainProfileProcessor"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
TerrainProfileProcessor::~TerrainProfileProcessor()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, id=" << m_id;
removeUserInput();
// maybe clear the view of m_terrainProfileDialog here?
TRACE_LOG(m_logger) << LOGFUNCTION << " END, id=" << m_id;
}
bool TerrainProfileProcessor::init(SituationMap::MapImpl *mapImpl, long id)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
m_id = id;
m_initialized = false;
m_mapImpl = mapImpl;
m_srMap = m_mapImpl->getEsriSpatialReference();
LayerFactoryImpl &layerFactoryImpl = m_mapImpl->layerFactoryImpl();
m_toolsLayername = layerFactoryImpl.toolLayer();
if (!m_surface)
m_surface = layerFactoryImpl.baseSurface();
if (m_surface && m_surface->loadStatus() == Esri::ArcGISRuntime::LoadStatus::Loaded)
m_initialized = true;
else
{
Esri::ArcGISRuntime::LoadStatus ls = Esri::ArcGISRuntime::LoadStatus::Unknown;
if (m_surface)
ls = m_surface->loadStatus();
ERROR_LOG(m_logger) << LOGFUNCTION
<< " d->m_surface = " << m_surface
<< " d->m_surface->loadStatus = " << ls;
}
if (!m_locationToElevationBulk.init(m_mapImpl, m_surface))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " locationToElevationBulk.init() failed";
return false;
}
connect(&m_locationToElevationBulk, &LocationToElevationBulk::locationsToElevationsCompleted,
this, &TerrainProfileProcessor::onLocationsToElevationsCompleted, Qt::UniqueConnection);
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " m_initialized=" << m_initialized;
return m_initialized;
}
bool TerrainProfileProcessor::start(const TerrainProfileParameter &terrainProfileParameter)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " terrainProfileParameter=" << terrainProfileParameter
<< " m_srMap.wkid=" << m_srMap.wkid();
// don´t allow more than one at once:
if (m_inProgress)
return false;
m_inProgress = true;
m_terrainProfileParameter = terrainProfileParameter;
removeUserInput();
displayUserInput();
// densify
const Esri::ArcGISRuntime::Polyline &polylineOut_Map = densifiy(m_terrainProfileParameter);
// calculate distance
// bulk location to elevation
const Esri::ArcGISRuntime::ImmutablePart &immutablePart = polylineOut_Map.parts().part(0);
const int size = immutablePart.pointCount();
for (int i = 0; i < size; ++i)
{
const Esri::ArcGISRuntime::Point &esriMapLocation = immutablePart.point(i);
// DEBUG_LOG(m_logger) << LOGFUNCTION << " esriMapLocation: " << QPointF(esriMapLocation.x(), esriMapLocation.y());
m_locationToElevationBulk.addLocation(esriMapLocation);
}
m_locationToElevationBulk.start();
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " m_viewshedInProgress=" << m_inProgress;
return true;
}
QVector<QPair<QPointF, double> > TerrainProfileProcessor::results() const
{
return m_locationToElevationBulk.results();
}
void TerrainProfileProcessor::setTerrainProfileDialog(QPointer<TerrainProfileDialog> &terrainProfileDialog)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
m_terrainProfileDialog = terrainProfileDialog;
if (m_terrainProfileDialog)
{
m_terrainProfileDialog->setMap(m_mapImpl);
// mousePositionChanged
connect(m_terrainProfileDialog.data(), &TerrainProfileDialog::mousePositionChanged,
this, &TerrainProfileProcessor::onDialogMousePositionChanged, Qt::UniqueConnection);
// handle destruction of according dialog:
connect(terrainProfileDialog.data(), &TerrainProfileDialog::destroyed,
this, &TerrainProfileProcessor::deleteLater);
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
Esri::ArcGISRuntime::Polyline TerrainProfileProcessor::densifiy(TerrainProfileParameter &terrainProfileParameter)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " terrainProfileParameter=" << terrainProfileParameter;
//
// PosCont to EsriPolyline:
//
Esri::ArcGISRuntime::PolylineBuilder polylineBuilder(m_srMap, this);
for (const Point3D &coord : qAsConst(terrainProfileParameter.m_coordinates))
{
// geo to map (polylineBuilder uses map also)
const Point3D &p3D = m_mapImpl->coordTranslationFunctor()->geographicToMap(coord);
polylineBuilder.addPoint(Esri::ArcGISRuntime::Point(p3D.x(), p3D.y(), p3D.z()));
}
const Esri::ArcGISRuntime::Polyline &polylineIn = polylineBuilder.toPolyline();
terrainProfileParameter.m_length =
Esri::ArcGISRuntime::GeometryEngine::lengthGeodetic(polylineIn,
Esri::ArcGISRuntime::LinearUnit::meters(),
Esri::ArcGISRuntime::GeodeticCurveType::Geodesic);
const int numberCoords = 50;
double maxSegmenthLength = terrainProfileParameter.m_length / numberCoords;
INFO_LOG(m_logger) << LOGFUNCTION << " START"
<< " lengthMeter=" << terrainProfileParameter.m_length
<< " numberCoords=" << numberCoords
<< " maxSegmenthLength=" << maxSegmenthLength;
const Esri::ArcGISRuntime::Geometry &geometryOut = Esri::ArcGISRuntime::GeometryEngine::densifyGeodetic(polylineIn,
maxSegmenthLength,
Esri::ArcGISRuntime::LinearUnit::meters(),
Esri::ArcGISRuntime::GeodeticCurveType::Geodesic);
const Esri::ArcGISRuntime::Polyline polylineOut(geometryOut);
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " lengthMeter=" << terrainProfileParameter.m_length
<< " numberCoords=" << numberCoords
<< " maxSegmenthLength=" << maxSegmenthLength
<< " polylineOut.parts().part(0).pointCount()=" << polylineOut.parts().part(0).pointCount();
return polylineOut;
}
void TerrainProfileProcessor::displayUserInput()
{
displayInputLine();
displayInputVertices();
}
void TerrainProfileProcessor::removeUserInput()
{
removeInputLine();
removeInputVertices();
removeCurrentMarker();
}
void TerrainProfileProcessor::displayInputLine()
{
// create
if (!m_polyline)
{
m_polyline = m_mapImpl->objectFactory().create<Polyline>(m_toolsLayername,
QStringLiteral("TerrainProfileLineId%1").arg(m_id));
m_polyline->setLineStyle(LineStyle(m_color, 3));
m_polyline->setScaleEnable(true);
m_polyline->setPickable(false);
}
// apply data
m_polyline->setPositions(m_terrainProfileParameter.m_coordinates);
// update
if (!m_polyline->updateView())
ERROR_LOG(m_logger) << LOGFUNCTION << " polyline::updateView failed";
}
void TerrainProfileProcessor::removeInputLine()
{
if (m_polyline)
m_polyline->removeMySelf();
m_polyline.reset();
}
void TerrainProfileProcessor::displayInputVertices()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
PositionCont coordinates = m_terrainProfileParameter.m_coordinates;
const int size = coordinates.size();
if (size < 1)
return;
m_markerCont.resize(size);
int idx = 0;
QMutableVectorIterator<RefPointer<GeoImage>> it(m_markerCont);
while (it.hasNext())
{
RefPointer<GeoImage> &marker = it.next();
if (!marker.valid())
{
marker = m_mapImpl->objectFactory().create<GeoImage>(m_toolsLayername,
QStringLiteral("TerrainProfileLineId%1_idx%2").arg(m_id).arg(idx));
marker->setProxyMarker(MarkerSymbolType::Circle);
marker->setProxyMarkerColor(m_color);
marker->setProxyMarkerSize(7);
marker->setPickable(false);
}
marker->setOrigin(coordinates[idx]);
// update
if (!marker->updateView())
ERROR_LOG(m_logger) << LOGFUNCTION << " marker::updateView failed";
++idx;
} // while
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void TerrainProfileProcessor::removeInputVertices()
{
QMutableVectorIterator<RefPointer<GeoImage>> it(m_markerCont);
while (it.hasNext())
{
RefPointer<GeoImage> &marker = it.next();
if (marker)
marker->removeMySelf();
}
m_markerCont.clear();
}
void TerrainProfileProcessor::displayCurrentMarker(const Point3D &pointGeo)
{
// create
if (!m_currentMarker)
{
m_currentMarker = m_mapImpl->objectFactory().create<GeoImage>(m_toolsLayername,
QStringLiteral("TerrainProfileLineId%1_currentMarker").arg(m_id));
m_currentMarker->setProxyMarker(MarkerSymbolType::Circle);
m_currentMarker->setProxyMarkerColor(Qt::blue);
m_currentMarker->setProxyMarkerSize(10);
m_currentMarker->setPickable(false);
}
// apply data
m_currentMarker->setOrigin(pointGeo);
// update
if (!m_currentMarker->updateView())
ERROR_LOG(m_logger) << LOGFUNCTION << " m_currentMarker::updateView failed";
}
void TerrainProfileProcessor::removeCurrentMarker()
{
if (m_currentMarker)
m_currentMarker->removeMySelf();
m_currentMarker.reset();
}
void TerrainProfileProcessor::onLocationsToElevationsCompleted()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " m_locationToElevationBulk.size()=" << m_locationToElevationBulk.size();
const QVector<QPair<QPointF, double>> &results = m_locationToElevationBulk.results();
if (m_logger.isDebugEnabled())
for (const QPair<QPointF, double> &item : qAsConst(results))
DEBUG_LOG(m_logger) << LOGFUNCTION << " location = " << item.first << " elevation = " << item.second;
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " results.size()=" << results.size();
// The caller should set TerrainProfileDialog on this signal:
emit locationsToElevationsCompleted(m_id);
// maybe (better hopefully) the caller (via finishedCallback of TerrainProfileImpl)
// has updated our m_terrainProfileParameter !?!
if (m_terrainProfileDialog)
{
m_terrainProfileDialog->setData(this->results());
QVector<QPointF> marks;
marks.reserve(m_terrainProfileParameter.m_coordinates.size());
for (const Point3D &coord : qAsConst(m_terrainProfileParameter.m_coordinates))
{
// geo to map
const Point3D &p3D = m_mapImpl->coordTranslationFunctor()->geographicToMap(coord);
marks.append(p3D.toPointF());
}
m_terrainProfileDialog->setMarks(marks);
} else {
ERROR_LOG(m_logger) << LOGFUNCTION << " no dialog";
}
m_inProgress = false;
}
void TerrainProfileProcessor::onDialogMousePositionChanged(const QPointF &curPos)
{
const Point3D &curPosGeo = Point3D::fromQPointF( m_mapImpl->coordTranslationFunctor()->mapToGeographic(curPos) );
displayCurrentMarker(curPosGeo);
}
} // namespace SituationMap
+125
View File
@@ -0,0 +1,125 @@
/**
* @RDE_HEADER@
* TerrainProfileProcessor
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "map_impl.h"
#include "location_to_elevation_bulk.h"
#include <Map/terrain_profile_parameter.h>
#include "MapObjects/polyline.h"
#include "MapObjects/geo_image.h"
#include <Utils/terrain_profile_dialog.h>
// ESRI
#include <arcgis/Map.h>
#include <arcgis/Surface.h>
#include <arcgis/SpatialReference.h>
// Qt
#include <QObject>
#include <QColor>
#include <QPointF>
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Map;
class Graphic;
class GraphicsOverlay;
class Polyline;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
class MapImpl;
class TerrainProfileProcessor : public QObject
{
Q_OBJECT
public:
explicit TerrainProfileProcessor(QObject *parent = nullptr);
virtual ~TerrainProfileProcessor();
long id() const { return m_id; }
bool init(SituationMap::MapImpl *mapImpl, long id);
bool start(const TerrainProfileParameter &terrainProfileParameter);
bool isRunning() const { return m_inProgress; }
QVector< QPair< QPointF, double > > results() const;
void setTerrainProfileDialog(QPointer<TerrainProfileDialog> &terrainProfileDialog);
signals:
void locationsToElevationsCompleted(long id);
protected slots:
void onLocationsToElevationsCompleted();
void onDialogMousePositionChanged(const QPointF &curPos);
protected:
Esri::ArcGISRuntime::Polyline densifiy(TerrainProfileParameter &terrainProfileParameter);
void displayUserInput();
void removeUserInput();
void displayInputLine();
void removeInputLine();
void displayInputVertices();
void removeInputVertices();
void displayCurrentMarker(const Point3D &pointGeo);
void removeCurrentMarker();
private:
SituationMap::MapImpl *m_mapImpl = nullptr;
Esri::ArcGISRuntime::SpatialReference m_srMap;
Esri::ArcGISRuntime::Surface *m_surface = nullptr;
long m_id = -1;
TerrainProfileParameter m_terrainProfileParameter;
// Calc
LocationToElevationBulk m_locationToElevationBulk;
bool m_initialized = false;
bool m_inProgress = false;
// Out
// TerrainProfileDialog (Displaying the results)
QPointer<SituationMap::TerrainProfileDialog> m_terrainProfileDialog;
QString m_toolsLayername;
QColor m_color = Qt::magenta;
RefPointer<Polyline> m_polyline;
QVector<RefPointer<GeoImage>> m_markerCont;
RefPointer<GeoImage> m_currentMarker;
LOGGER_T m_logger;
}; // class TerrainProfileProcessor
} // namespace SituationMap
+213
View File
@@ -0,0 +1,213 @@
/**
* @RDE_HEADER@
* Tile Map Service Layer for Esri
* $Id$
* $HeadURL$
* $Author$
* License: BSD 2-Clause
*/
#include "tms_layer.h"
#include <QCoreApplication>
#include <QDomDocument>
#include <QNetworkAccessManager>
namespace EsriExtension
{
TMSLayer::TMSLayer(const QString &url, QObject *parent)
: EsriRuntimeQt::TiledMapServiceLayer(url, parent)
, m_nam(nullptr)
{
connect(this, SIGNAL(requestTile(int,int,int)),
this, SLOT(onRequestTMSTile(int,int,int)));
}
TMSLayer::~TMSLayer()
{
delete m_nam;
}
void TMSLayer::initialize()
{
if (!m_nam)
{
m_nam = new QNetworkAccessManager(this);
connect(m_nam, SIGNAL(finished(QNetworkReply*)),
this, SLOT(replyBaseXmlFinished(QNetworkReply*)));
}
QNetworkReply *reply = m_nam->get(QNetworkRequest(QUrl(url())));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(replyBaseXmlError(QNetworkReply::NetworkError)));
}
void TMSLayer::replyBaseXmlFinished(QNetworkReply *reply)
{
if (!reply || reply->error() != QNetworkReply::NoError)
{
cleanupNetworkReply(reply);
return;
}
QDomDocument doc(QLatin1String("TMSLayerDoc"));
doc.setContent(reply);
const QDomElement domRoot = doc.documentElement();
const QDomElement domSRS = domRoot.firstChildElement(QLatin1String("SRS"));
const QDomElement domBoundingBox = domRoot.firstChildElement(QLatin1String("BoundingBox"));
const QDomElement domOrigin = domRoot.firstChildElement(QLatin1String("Origin"));
const QDomElement domTileFormat = domRoot.firstChildElement(QLatin1String("TileFormat"));
const QDomElement domTileSets = domRoot.firstChildElement(QLatin1String("TileSets"));
if (domSRS.isNull() ||
domBoundingBox.isNull() ||
domOrigin.isNull() ||
domTileFormat.isNull() ||
domTileSets.isNull())
{
cleanupNetworkReply(reply);
const QString errStr(QString(QLatin1String("Got invalid xml from tms server (url: %1)")).arg(url()));
layerCreateError(errStr);
qWarning("%s", qPrintable(errStr));
return;
}
// optional
if (domTileFormat.hasAttribute(QLatin1String("extension")))
m_extension = domTileFormat.attribute(QLatin1String("extension"));
//LODs:
const double dpi = 90.7; // according to spec
const double ipf = 12.0; // inch per foot
const double dpu = dpi * ipf; // dots per unit
// We expect a SRS string in the format 'EPSG:<number>' here ...
const EsriRuntimeQt::SpatialReference sr(domSRS.text().split(QLatin1Char(':')).at(1).toInt());
// Setting the image format is not needed (the image reader correctly guesses the format)
// Setting the spatial reference is also not needed - if not set, the default one
// from the layer is used.
EsriRuntimeQt::TileInfo tileInfo;
tileInfo.setCols(domTileFormat.attribute(QLatin1String("width")).toInt());
tileInfo.setRows(domTileFormat.attribute(QLatin1String("height")).toInt());
tileInfo.setDpi(dpi);
EsriRuntimeQt::LOD lod;
QList<EsriRuntimeQt::LOD> lods;
const double minX = domBoundingBox.attribute(QLatin1String("minx")).toDouble();
const double minY = domBoundingBox.attribute(QLatin1String("miny")).toDouble();
const double maxX = domBoundingBox.attribute(QLatin1String("maxx")).toDouble();
const double maxY = domBoundingBox.attribute(QLatin1String("maxy")).toDouble();
// Since ArcGIS calculates from upper left (instead lower left as tms defines),
// we need to make sure all our zoom levels have the same upper y value
// TODO: use from zoom level
const double utm32MaxYcalc = 16384 * 256 * 3; // 3 - Anzahl kacheln lowest zoom level
const double utm32MinX = 166021.44309607695; // min utm 32 boundary - calc from proj4 or smth. like this
const QString tsAttr(QLatin1String("TileSet"));
QDomElement tileSetSibling = domTileSets.firstChildElement(tsAttr);
for (; !tileSetSibling.isNull(); tileSetSibling = tileSetSibling.nextSiblingElement(tsAttr))
{
if (!tileSetSibling.hasAttribute(QLatin1String("order")))
{
qWarning("Could not find 'order' attribute for LOD - skipping lod");
continue;
}
const int lodLevel = tileSetSibling.attribute(QLatin1String("order")).toInt();
lod.setLevel(lodLevel);
lod.setResolution(tileSetSibling.attribute(QLatin1String("units-per-pixel")).toDouble());
lod.setScale(lod.resolution() / dpu);
if (!tileSetSibling.hasAttribute(QLatin1String("href")))
{
qWarning("Could not find 'href' attribute for LOD - skipping lod");
continue;
}
lods.append(lod);
m_tileUrls.insert(lodLevel, tileSetSibling.attribute(QLatin1String("href")));
// Since ArcGIS calculates from upper left (instead lower left as tms defines),
// we need to make sure all our zoom levels have the same upper y value
m_tilesYAxis.insert(lodLevel, qRound(utm32MaxYcalc / (lod.resolution() * tileInfo.cols())));
//qWarning("LOD: %d, %lf %lf, %lf", lodLevel, lod.resolution(), lod.scale(),
// lod.resolution() * 256 * m_tilesYAxis.value(lodLevel));
//qWarning("Y Tile count for level %d is %d - %lf", lodLevel, m_tilesYAxis.value(lodLevel),
// utm32MaxYcalc / (lod.resolution() * tileInfo.cols()));
}
if (lods.isEmpty())
{
const QString errStr(QLatin1String("Could not find any layer in the xml file!"));
layerCreateError(errStr);
qWarning("%s", qPrintable(errStr));
cleanupNetworkReply(reply);
return;
}
tileInfo.setLODs(lods);
const EsriRuntimeQt::Envelope envelope(minX, minY, maxX, maxY, sr);
// TMS service origin is bottom left, arcgis upper left
tileInfo.setOrigin({utm32MinX, utm32MaxYcalc, sr});
setDefaultSpatialReference(sr);
setFullExtent(envelope);
setInitialExtent(envelope);
setTileInfo(tileInfo);
// TODO: setMin/Max scale ?
EsriRuntimeQt::TiledMapServiceLayer::initialize();
// clean up
cleanupNetworkReply(reply);
}
void TMSLayer::replyBaseXmlError(QNetworkReply::NetworkError error)
{
const QString errStr(QString(QLatin1String("Error retrieving xml from %1, error: %2")).arg(url()).arg(error));
emit layerCreateError(errStr);
qWarning("%s", qPrintable(errStr));
}
void TMSLayer::onRequestTMSTile(int level, int col, int row)
{
if (!m_tilesYAxis.contains(level))
{
qWarning("Invalud zoom level (%d)!", level);
return;
}
// TMS starts counting from bottom left, ArcGIS from upper left
// -> we need to mirror the y-axis
int reqRow = m_tilesYAxis.value(level) - row - 1;
//qWarning("onRequestTMSTile: %d, %d, %d (%d)", level, col, reqRow, row);
QString requestUrl(m_tileUrls.value(level) +
QLatin1Char('/') + QString::number(col) +
QLatin1Char('/') + QString::number(reqRow));
if (!m_extension.isEmpty())
requestUrl += QLatin1Char('.') + m_extension;
//qWarning("RequestUrl: %s", qPrintable(requestUrl));
setTile(level, col, row, QUrl(requestUrl));
}
void TMSLayer::cleanupNetworkReply(QNetworkReply *reply)
{
if (reply)
{
reply->deleteLater();
reply->disconnect(this);
}
if (m_nam)
{
m_nam->disconnect(this);
m_nam->deleteLater();
m_nam = nullptr;
}
}
} // namespace EsriExtension
+50
View File
@@ -0,0 +1,50 @@
/**
* @RDE_HEADER@
* Tile Map Service Layer for Esri
* $Id$
* $HeadURL$
* $Author$
* License: BSD 2-Clause
*/
#pragma once
#include <arcgis/TiledMapServiceLayer.h>
#include <QNetworkReply>
class QNetworkAccessManager;
namespace EsriExtension
{
/** A small Wrapper around EsriRuntimeQt::TiledMapServiceLayer to
* be able to access a tile map service through Qt Runtime SDK.
* Sadly Esri does not provide this layer for Qt...
* so we have to write one on our own
* It's far from perfect but works for our needs.
* It was only tested with geoserver 2.8 and EPSG:23632 projection
*/
class TMSLayer : public EsriRuntimeQt::TiledMapServiceLayer
{
Q_OBJECT
public:
explicit TMSLayer(const QString &url, QObject *parent = nullptr);
~TMSLayer();
void initialize() override;
private Q_SLOTS:
void replyBaseXmlFinished(QNetworkReply *reply);
void replyBaseXmlError(QNetworkReply::NetworkError);
void onRequestTMSTile(int level, int col, int row);
private:
void cleanupNetworkReply(QNetworkReply *reply);
private:
QMap<int, QString> m_tileUrls;
QMap<int, int> m_tilesYAxis;
QString m_extension;
QNetworkAccessManager *m_nam;
};
}
+120
View File
@@ -0,0 +1,120 @@
/**
* @RDE_HEADER@
* ViewshedAnalysis
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include <Map/situation_map_utils.h>
#include "viewshed_analysis.h"
// ESRI
#include <arcgis/SceneGraphicsView.h>
#include <arcgis/AnalysisOverlay.h>
#include <arcgis/AnalysisListModel.h>
#include <arcgis/LocationViewshed.h>
// Qt
#include <QDebug>
namespace SituationMap
{
ViewshedAnalysis::ViewshedAnalysis(Esri::ArcGISRuntime::SceneGraphicsView *sceneGraphicsView, QObject *parent)
: ViewshedBase(parent)
, m_analysisOverlayModel(sceneGraphicsView->analysisOverlays())
, m_analysisOverlay(new Esri::ArcGISRuntime::AnalysisOverlay(this))
, m_viewsheds(3) // ESRI viewshed support 120 degree maximum, merge 3 of them
, m_logger(getLogger("ViewshedAnalysis")) // P.S.: from release 100.3 ESRI supports 360 degree viewshed
{
m_analysisOverlay->setVisible(true);
for (int i = 0; i < m_viewsheds.size(); ++i)
m_viewsheds[i] = nullptr;
QObject::connect(m_analysisOverlay->analyses(), &Esri::ArcGISRuntime::AnalysisListModel::errorOccurred, this, &ViewshedAnalysis::onErrorOccured);
m_analysisOverlayModel->append(m_analysisOverlay);
} // ViewshedAnalysis::ViewshedAnalysis
ViewshedAnalysis::~ViewshedAnalysis()
{
for (int i = 0; i < m_viewsheds.size(); ++i)
{
delete m_viewsheds[i];
m_viewsheds[i] = nullptr;
}
m_analysisOverlayModel->removeOne(m_analysisOverlay);
delete m_analysisOverlay;
} // ViewshedAnalysis::~ViewshedAnalysis()
bool ViewshedAnalysis::execute(const QPointF &viewshedPoint_map_degree, double outerRadius, double observerOffset)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " viewshedPoint_map_degree= " << viewshedPoint_map_degree
<< " outerRadius= " << outerRadius
<< " observerOffset= " << observerOffset;
if (!m_analysisOverlay)
return false;
Esri::ArcGISRuntime::Point viewshedCenter(viewshedPoint_map_degree.x(), viewshedPoint_map_degree.y(), observerOffset);
double heading(0.0);
double deltaHeading(360.0 / m_viewsheds.size());
// ToDo:
// Keep in mind that the number of viewsheds is restricted to 8 on mobile devices and 16 on desktop devices.
// The horizontal angle have to be less than 120 degree.
for (int i = 0; i < m_viewsheds.size(); ++i)
{
Esri::ArcGISRuntime::LocationViewshed *viewshed = m_viewsheds[i];
if (viewshed)
{
m_analysisOverlay->analyses()->removeOne(viewshed);
delete viewshed;
}
viewshed = new Esri::ArcGISRuntime::LocationViewshed(viewshedCenter
, heading
, 90.0 // pitch limited to 180
, deltaHeading // horizontalAngle limited to 120, from release 100.3 maximum is 360 here
, 90.0 // verticalAngle limited to 120
, 10.0 // minDistance
, outerRadius // maxDistance
, this);
viewshed->setVisible(true);
viewshed->setFrustumOutlineVisible(false);
viewshed->setFrustumOutlineColor(QColor(0, 0, 255, 255));
// Add the Viewshed to the Analysis Overlay
m_analysisOverlay->analyses()->append(viewshed);
m_viewsheds[i] = viewshed;
heading += deltaHeading;
} // for
return true;
} // bool ViewshedAnalysis::execute(const QPointF &viewshedPoint_map, double outerRadius, double observerOffset)
} // namespace SituationMap
namespace SituationMap
{
void ViewshedAnalysis::onErrorOccured(const Esri::ArcGISRuntime::Error &error)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " EsriError occured: " << error;
// qCritical() << "Error reported from ESRI Analyse Overlay:" << error.message() << " code:" << error.code();
} // void ViewshedAnalysis::onErrorOccured(const Esri::ArcGISRuntime::Error &error)
} // namespace SituationMap
+64
View File
@@ -0,0 +1,64 @@
/**
* @RDE_HEADER@
* ViewshedAnalysis
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "viewshed_base.h"
#include <QtCore/QVector>
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class AnalysisOverlay;
class AnalysisOverlayListModel;
class LocationViewshed;
class SceneGraphicsView;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
class ViewshedAnalysis : public ViewshedBase
{
Q_OBJECT
public:
explicit ViewshedAnalysis(Esri::ArcGISRuntime::SceneGraphicsView *sceneGraphicsView
, QObject *parent = nullptr);
virtual ~ViewshedAnalysis();
bool execute(const QPointF &viewshedPoint_map_degree, double outerRadius, double observerOffset) override;
protected slots:
void onErrorOccured(const Esri::ArcGISRuntime::Error &error);
private:
Esri::ArcGISRuntime::AnalysisOverlayListModel *m_analysisOverlayModel;
Esri::ArcGISRuntime::AnalysisOverlay *m_analysisOverlay;
QVector<Esri::ArcGISRuntime::LocationViewshed *> m_viewsheds;
LOGGER_T m_logger;
};
} // namespace SituationMap
+27
View File
@@ -0,0 +1,27 @@
/**
* @RDE_HEADER@
* Baseclass of Viewsheds
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "viewshed_base.h"
// Qt
namespace SituationMap
{
//-----------------------------------------------------------------------------
// class ViewshedBase
//-----------------------------------------------------------------------------
ViewshedBase::ViewshedBase(QObject *parent)
: QObject(parent)
{}
} // namespace SituationMap
+62
View File
@@ -0,0 +1,62 @@
/**
* @RDE_HEADER@
* Baseclass of Viewsheds
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Common/situation_map_logging.h>
// ESRI
#include <arcgis/Map.h>
#include <arcgis/SpatialReference.h>
// Qt
#include <QObject>
#include <QColor>
#include <QPointF>
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Map;
class Graphic;
class GraphicsOverlay;
class GeoprocessingTask;
class GeoprocessingResult;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
class ViewshedBase : public QObject
{
Q_OBJECT
public:
explicit ViewshedBase(QObject *parent = nullptr);
virtual ~ViewshedBase() = default;
ViewshedBase(const ViewshedBase &other) = delete;
ViewshedBase &operator=(ViewshedBase &other) = delete;
virtual bool execute(const QPointF &viewshedPoint_map, double outerRadius, double observerOffset) = 0;
};
} // namespace SituationMap
+569
View File
@@ -0,0 +1,569 @@
/**
* @RDE_HEADER@
* ViewshedGeoProcessing
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "viewshed_geo_processing.h"
#include "map_impl.h"
#include <GraphicalObjects/graphical_object_path.h>
#include <Map/situation_map_utils.h>
#include <MapImpl/layer_factory_impl.h>
#include <LtsWidgets/lts_global_wait_dialog.h>
// ESRI
#include <arcgis/Map.h>
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/Graphic.h>
#include <arcgis/GeoprocessingTask.h>
#include <arcgis/GeoprocessingJob.h>
#include <arcgis/GeoprocessingFeatures.h>
#include <arcgis/GeoprocessingRaster.h>
#include <arcgis/GeoprocessingParameter.h>
#include <arcgis/GeoprocessingParameters.h>
#include <arcgis/GeoprocessingResult.h>
#include <arcgis/FeatureCollectionTable.h>
#include <arcgis/Feature.h>
#include <arcgis/SimpleMarkerSymbol.h>
#include <arcgis/SimpleFillSymbol.h>
#include <arcgis/GeoprocessingLinearUnit.h>
#include <arcgis/ImageServiceRaster.h>
#include <arcgis/RasterLayer.h>
#include <arcgis/GeometryEngine.h>
// Qt
#include <QDebug>
#include <QMessageBox>
namespace SituationMap
{
//-----------------------------------------------------------------------------
// class ViewshedBase
//-----------------------------------------------------------------------------
ViewshedGeoProcessing::ViewshedGeoProcessing(const QString &url,
Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer,
const Esri::ArcGISRuntime::SpatialReference &spatialReference,
QObject *parent)
: ViewshedBase(parent)
, m_url(url)
, m_resultsOverlay(graphicsLayer)
, m_srMap(spatialReference)
, m_logger(getLogger("ViewshedGeoProcessing"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
ViewshedGeoProcessing::~ViewshedGeoProcessing()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
clearGraphics();
delete m_viewshedTask;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
bool ViewshedGeoProcessing::init(SituationMap::MapImpl *mapImpl)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
// check viewshedContraint (V100.2.1: disable usage of GPU to avoid failure:
/*
2018-03-21 13:06:15.911 ERROR ViewshedGeoProcessing |#| void SituationMap::ViewshedGeoProcessing::onErrorOccuredGeoprocessingJob(const Esri::ArcGISRuntime::Error &) GeoprocessingJob -> EsriError occured: sender= 'GeoprocessingJob', code=400, domain=0, extendedErrorType=0, message='Unable to complete operation.', additionalMessage='Error executing tool. Viewshed2Feature : ERROR 010151: No features found in C:\users\ctc\Temp\arcgisruntime_8\LegatusBremen201803151510.gpk\jobs\_\j5a4e467cb51047c28a463a703c2f2bcd\scratch\scratch.gdb\Viewshed_Polygon. Possible empty feature class.
Failed to execute (Raster to Polygon).
Failed to execute (Viewshed2Feature).
Failed to execute (Viewshed2Feature).' (pl/viewshed_geo_processing.cpp:529)
2018-03-21 13:06:15.911 ERROR Qt |#| Geoprocessing Task failed (ication/ngl_qt_msg_handler.inc:26)
2018-03-21 13:06:15.911 ERROR Qt |#| Geoprocessing Task failed message: code = 400 domain = 0 message = "Unable to complete operation." additionalMessage = "Error executing tool. Viewshed2Feature : ERROR 010151: No features found in C:\\users\\ctc\\Temp\\arcgisruntime_8\\LegatusBremen201803151510.gpk\\jobs\\_\\j5a4e467cb51047c28a463a703c2f2bcd\\scratch\\scratch.gdb\\Viewshed_Polygon. Possible empty feature class.\nFailed to execute (Raster to Polygon).\nFailed to execute (Viewshed2Feature).\nFailed to execute (Viewshed2Feature)." extendedErrorType = 0 (ication/ngl_qt_msg_handler.inc:26)
2018-03-21 13:06:15.911 INFO ViewshedGeoProcessing |#| void SituationMap::ViewshedGeoProcessing::onJobStatusChanged() jobStatusChanged is now (m_jobStatus): Job failed (pl/viewshed_geo_processing.cpp:256)
*/
{
const char cuda[] = "CUDA_VISIBLE_DEVICES";
bool ok = qEnvironmentVariableIsSet(cuda) &&
(qEnvironmentVariableIsEmpty(cuda) || qEnvironmentVariableIntValue(cuda) == -1);
if (ok)
DEBUG_LOG(m_logger) << LOGFUNCTION << " VIEWSHED_CONTRAINT fulfilled: "
<< cuda << " = '" << qgetenv(cuda) << "'";
else
{
FATAL_LOG(m_logger) << LOGFUNCTION << " VIEWSHED_CONTRAINT NOT fulfilled: " << cuda << " = '" << qgetenv(cuda) << "'";
QMessageBox::critical(nullptr,
tr("Configuration Error"),
tr("Environmentvariable '%1' is not set to deactivate GPU. Operations can fail").arg(QString::fromLatin1(cuda)),
QMessageBox::Ok);
}
}
if (m_url.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " url is empty";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return false;
}
m_mapImpl = mapImpl; // mapImpl is required only if processRasterResults is used
delete m_viewshedTask;
m_viewshedTask = new Esri::ArcGISRuntime::GeoprocessingTask(QUrl(m_url), this);
m_viewshedTask->setObjectName(QLatin1String("GeoprocessingTask"));
// Handle errors of GeoprocessingTask:
connect(m_viewshedTask, &Esri::ArcGISRuntime::GeoprocessingTask::errorOccurred,
this, &ViewshedGeoProcessing::onErrorOccuredGeoprocessingTask);
m_initialized = true;
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " m_initialized=" << m_initialized;
return m_initialized;
}
bool ViewshedGeoProcessing::execute(const QPointF &viewshedPoint_map, double outerRadius, double observerOffset)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " viewshedPoint_map=" << viewshedPoint_map
<< " m_srMap.wkid=" << m_srMap.wkid()
<< " outerRadius=" << outerRadius
<< " observerOffset=" << observerOffset
<< " m_viewshedInProgress=" << m_viewshedInProgress
<< " url='" << m_url << "'";
// check point is the viewshed point
Esri::ArcGISRuntime::Point esriViewshedPoint(viewshedPoint_map.x(), viewshedPoint_map.y(), m_srMap); // Esri::ArcGISRuntime::SpatialReference::webMercator());
DEBUG_LOG(m_logger) << LOGFUNCTION << " esriViewshedPoint.toJson: " << esriViewshedPoint.toJson();
m_viewshedInProgress = true;
createInputGraphic(esriViewshedPoint);
createExtentGraphic(esriViewshedPoint, outerRadius);
clearResultGraphics();
// Create a new feature collection table based upon point geometries using the current map view spatial reference
Esri::ArcGISRuntime::FeatureCollectionTable *inputFeatures =
new Esri::ArcGISRuntime::FeatureCollectionTable(QList<Esri::ArcGISRuntime::Field>()
, Esri::ArcGISRuntime::GeometryType::Point
, m_srMap
, this);
// Create a new feature from the feature collection table. It will not have a coordinate location (x,y) yet
Esri::ArcGISRuntime::Feature *inputFeature = inputFeatures->createFeature(this);
// Assign a physical location to the new point feature based upon where the user clicked on the map view
inputFeature->setGeometry(esriViewshedPoint);
// connect to addFeature status changed signal
connect(inputFeatures, &Esri::ArcGISRuntime::FeatureCollectionTable::addFeatureCompleted, this, [this, inputFeatures, outerRadius, observerOffset](QUuid, bool success)
{
INFO_LOG(m_logger) << LOGFUNCTION << " addFeatureCompleted...";
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no success";
return;
}
// Create the parameters that are passed to the used geoprocessing task
Esri::ArcGISRuntime::GeoprocessingParameters viewshedParameters =
Esri::ArcGISRuntime::GeoprocessingParameters(Esri::ArcGISRuntime::GeoprocessingExecutionType::SynchronousExecute); // AsynchronousSubmit | SynchronousExecute
// GeoprocessingExecutionType should match Esri::ArcGISRuntime::GeoprocessingServiceType of LocalGeoprocessingService
// Request the output features to use the same SpatialReference as the map view
viewshedParameters.setOutputSpatialReference(m_srMap);
// Add an input location to the geoprocessing parameters
QMap<QString, Esri::ArcGISRuntime::GeoprocessingParameter *> inputs;
inputs[QLatin1String("Input_point_or_polyline_observer_features")] = new Esri::ArcGISRuntime::GeoprocessingFeatures(inputFeatures, this);
// Radius
inputs[QLatin1String("Inner_radius")] = new Esri::ArcGISRuntime::GeoprocessingLinearUnit(55.0, Esri::ArcGISRuntime::GeoprocessingLinearUnits::Meter, this);
inputs[QLatin1String("Outer_radius")] = new Esri::ArcGISRuntime::GeoprocessingLinearUnit(outerRadius, Esri::ArcGISRuntime::GeoprocessingLinearUnits::Meter, this);
// Observer
// //!!! DO NOT DEFINE, keep elevation of ellipsoid in mind // inputs[QLatin1String("Observer_elevation")] = new Esri::ArcGISRuntime::GeoprocessingLinearUnit(0.0, Esri::ArcGISRuntime::GeoprocessingLinearUnits::Meter, this);
inputs[QLatin1String("Observer_offset")] = new Esri::ArcGISRuntime::GeoprocessingLinearUnit(observerOffset, Esri::ArcGISRuntime::GeoprocessingLinearUnits::Meter, this);
// Target
inputs[QLatin1String("Surface_offset")] = new Esri::ArcGISRuntime::GeoprocessingLinearUnit(0.0, Esri::ArcGISRuntime::GeoprocessingLinearUnits::Meter, this);
// setInputs
viewshedParameters.setInputs(inputs);
// Print geoprocessingParameters for debugging purpose:
QMapIterator<QString, Esri::ArcGISRuntime::GeoprocessingParameter *> inputsIt(inputs);
while (inputsIt.hasNext())
{
inputsIt.next();
QString value(QLatin1String("-"));
Esri::ArcGISRuntime::GeoprocessingParameter *gpPara = inputsIt.value();
Esri::ArcGISRuntime::GeoprocessingLinearUnit *gpParaLU = dynamic_cast<Esri::ArcGISRuntime::GeoprocessingLinearUnit *>(gpPara);
if (gpParaLU)
value = QStringLiteral("%1 %2").arg(gpParaLU->distance()).arg(gpParaLU->linearUnit().name());
DEBUG_LOG(m_logger) << LOGFUNCTION << " key= '" << inputsIt.key() << "'" << " value= '" << value << "'";
} // while
// Create the job that handles the communication between the application and the geoprocessing task
m_viewshedJob = m_viewshedTask->createJob(viewshedParameters);
m_viewshedJob->setObjectName(QLatin1String("GeoprocessingJob"));
// Handle errors of GeoprocessingJob:
connect(m_viewshedJob, &Esri::ArcGISRuntime::GeoprocessingJob::errorOccurred, this, &ViewshedGeoProcessing::onErrorOccuredGeoprocessingJob);
// Create signal handler for the job
#if ACRGIS_VERSION >= 0x640E00 // 100.14.0
// since V100.14
connect(m_viewshedJob, &Esri::ArcGISRuntime::GeoprocessingJob::statusChanged, this, &ViewshedGeoProcessing::onJobStatusChangedWithJsParameter);
#else
// until V100.14:
connect(m_viewshedJob, &Esri::ArcGISRuntime::GeoprocessingJob::jobStatusChanged, this, &ViewshedGeoProcessing::onJobStatusChanged);
#endif // ACRGIS_VERSION
// start the job
if (m_viewshedJob->start())
LtsWidgets::LtsGlobalWaitDialog::getInstance()->wait();
else
ERROR_LOG(m_logger) << LOGFUNCTION << " viewshedJob->start() failed";
});
// Add the new feature with (x,y) location to the feature collection table
inputFeatures->addFeature(inputFeature);
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " m_viewshedInProgress=" << m_viewshedInProgress;
return true;
}
void ViewshedGeoProcessing::onJobStatusChanged()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
// qCritical() << "jobStatusChanged... viewshedJob->jobStatus()=" << (int)viewshedJob->jobStatus();
bool viewshedInProgressOld = m_viewshedInProgress;
switch (m_viewshedJob->jobStatus())
{
case Esri::ArcGISRuntime::JobStatus::NotStarted:
ERROR_LOG(m_logger) << LOGFUNCTION << "Geoprocessing Task failed";
m_viewshedInProgress = false;
m_jobStatus = QLatin1String("Job NotStarted");
break;
case Esri::ArcGISRuntime::JobStatus::Failed:
{
ERROR_LOG(m_logger) << LOGFUNCTION << "Geoprocessing Task failed";
const Esri::ArcGISRuntime::Error &err = m_viewshedJob->error();
ERROR_LOG(m_logger) << LOGFUNCTION << "Geoprocessing Task failed message: " << err;
m_viewshedInProgress = false;
m_jobStatus = QLatin1String("Job failed");
}
break;
case Esri::ArcGISRuntime::JobStatus::Started:
m_viewshedInProgress = true;
m_jobStatus = QLatin1String("Job in progress...");
break;
case Esri::ArcGISRuntime::JobStatus::Paused:
m_viewshedInProgress = false;
m_jobStatus = QLatin1String("Job paused...");
break;
case Esri::ArcGISRuntime::JobStatus::Succeeded:
m_viewshedInProgress = false;
m_jobStatus = QLatin1String("Job succeeded");
// handle the results
processResults(m_viewshedJob->result());
break;
default:
break;
}
DEBUG_LOG(m_logger) << LOGFUNCTION << " jobStatusChanged is now (m_jobStatus): " << m_jobStatus;
if ((m_viewshedInProgress == false) && (viewshedInProgressOld != m_viewshedInProgress))
LtsWidgets::LtsGlobalWaitDialog::getInstance()->done();
// emit signals
// emit viewshedInProgressChanged();
// emit jobStatusChanged();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
#if ACRGIS_VERSION >= 0x640E00 // 100.14.0
void ViewshedGeoProcessing::onJobStatusChangedWithJsParameter(const Esri::ArcGISRuntime::JobStatus &/*newStatus*/)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
onJobStatusChanged();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
#endif // ACRGIS_VERSION
bool ViewshedGeoProcessing::processResults(Esri::ArcGISRuntime::GeoprocessingResult *results)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!results)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no results";
return false;
}
const QMap<QString, Esri::ArcGISRuntime::GeoprocessingParameter *> &outputsMap = results->outputs();
if (outputsMap.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no keys, outputsMap is empty ";
return false;
}
// clear graphics:
clearResultGraphics();
QString key;
key = QStringLiteral("Output_polygon_features");
if (outputsMap.contains(key))
{
// Get the results from the outputs as GeoprocessingFeatures
Esri::ArcGISRuntime::GeoprocessingFeatures *viewshedResultFeatures =
static_cast<Esri::ArcGISRuntime::GeoprocessingFeatures *>(outputsMap[key]);
return processFeatureResults(viewshedResultFeatures);
}
key = QStringLiteral("Output_raster");
if (outputsMap.contains(key))
{
Esri::ArcGISRuntime::GeoprocessingRaster *viewshedResultRaster =
static_cast<Esri::ArcGISRuntime::GeoprocessingRaster *>(outputsMap[key]);
return processRasterResults(viewshedResultRaster);
}
ERROR_LOG(m_logger) << LOGFUNCTION << " failure, no known keyword found in outputsMap"
<< ", outputsMap.size() " << outputsMap.size();
return false;
}
bool ViewshedGeoProcessing::processFeatureResults(Esri::ArcGISRuntime::GeoprocessingFeatures *viewshedResultFeatures)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " processFeatureResults";
if (!viewshedResultFeatures)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no viewshedResultFeatures";
return false;
}
Esri::ArcGISRuntime::SimpleFillSymbol sfsVisi (Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, QColor(0, 226, 0, 255), this);
Esri::ArcGISRuntime::SimpleFillSymbol sfsInVisi(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, QColor(226, 0, 0, 255), this);
// Add all the features from the result feature set as a graphics to the map
Esri::ArcGISRuntime::FeatureIterator features = viewshedResultFeatures->features()->iterator();
while (features.hasNext())
{
Esri::ArcGISRuntime::Feature *feat = features.next(this);
const QString &attKey(QLatin1String("gridcode"));
if (feat->attributes()->containsAttribute(attKey))
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " attributes()->attributeValue=== " << feat->attributes()->attributeValue(attKey).toString();
if (feat->attributes()->attributeValue(attKey).toInt())
m_resultGraphics.append(new Esri::ArcGISRuntime::Graphic(feat->geometry(), &sfsVisi));
else
m_resultGraphics.append(new Esri::ArcGISRuntime::Graphic(feat->geometry(), &sfsInVisi));
// ERROR_LOG(m_logger) << LOGFUNCTION << " attributeValue " << feat->attributes()->attributeValue(attKey).toInt();
} else
ERROR_LOG(m_logger) << LOGFUNCTION << " no key attributeValue ";
}
// transfer from own container to resultsOverlay:
m_resultsOverlay->graphics()->append(m_resultGraphics);
return true;
}
bool ViewshedGeoProcessing::processRasterResults(Esri::ArcGISRuntime::GeoprocessingRaster *viewshedResultRaster)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " viewshedResultRaster -> url = " << viewshedResultRaster->url().toString();
connect(viewshedResultRaster, &Esri::ArcGISRuntime::GeoprocessingRaster::fetchFileCompleted, this, [this] (QUuid, const QString& filePath) {
DEBUG_LOG(m_logger) << LOGFUNCTION << " filePath=" << filePath;
Esri::ArcGISRuntime::Raster *raster = new Esri::ArcGISRuntime::Raster(filePath, this);
if (!raster)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " raster failed";
return;
}
connect(raster, &Esri::ArcGISRuntime::Raster::errorOccurred, this, [this] (const Esri::ArcGISRuntime::Error &error) {
ERROR_LOG(m_logger) << LOGFUNCTION << " raster EsriError occured: " << error;
});
connect(raster, &Esri::ArcGISRuntime::Raster::loadStatusChanged, this, [this, raster] (Esri::ArcGISRuntime::LoadStatus loadStatus) {
ERROR_LOG(m_logger) << LOGFUNCTION << " raster loadStatus=" << loadStatus;
if (loadStatus == Esri::ArcGISRuntime::LoadStatus::Loaded)
{
Esri::ArcGISRuntime::RasterLayer *rl = new Esri::ArcGISRuntime::RasterLayer(raster, this);
if (!rl)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " rl failed";
return;
}
connect(rl, &Esri::ArcGISRuntime::RasterLayer::errorOccurred, this, [this] (const Esri::ArcGISRuntime::Error &error) {
ERROR_LOG(m_logger) << LOGFUNCTION << " RasterLayer EsriError occured: " << error;
});
connect(rl, &Esri::ArcGISRuntime::RasterLayer::loadStatusChanged, this, [this] (Esri::ArcGISRuntime::LoadStatus loadStatus) {
ERROR_LOG(m_logger) << LOGFUNCTION << " RasterLayer loadStatus=" << loadStatus;
});
DEBUG_LOG(m_logger) << LOGFUNCTION << " getEsriMap()->operationalLayers()->size" << m_mapImpl->getEsriMap()->operationalLayers()->size();
m_mapImpl->getEsriMap()->operationalLayers()->append(rl);
DEBUG_LOG(m_logger) << LOGFUNCTION << " getEsriMap()->operationalLayers()->size" << m_mapImpl->getEsriMap()->operationalLayers()->size();
DEBUG_LOG(m_logger) << LOGFUNCTION << " rasterLayer loading ...";
rl->load();
}
});
DEBUG_LOG(m_logger) << LOGFUNCTION << " raster loading ...";
raster->load();
});
Esri::ArcGISRuntime::TaskWatcher tw = viewshedResultRaster->fetchFile(QStringLiteral("/tmp/viewshedFetched.tif"));
if (!tw.isValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " m_mapImpl failed";
return false;
}
return true;
}
void ViewshedGeoProcessing::clearGraphics()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << this;
clearInputGraphics();
clearExtentGraphics();
clearResultGraphics();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void ViewshedGeoProcessing::createInputGraphic(const Esri::ArcGISRuntime::Point &origin)
{
if (!m_originGraphic)
{
m_originGraphic = new Esri::ArcGISRuntime::Graphic(this);
m_resultsOverlay->graphics()->append(m_originGraphic);
}
m_originGraphic->setGeometry(origin);
Esri::ArcGISRuntime::SimpleMarkerSymbol sms(Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle, QColor("red"), 12.0);
m_originGraphic->setSymbol(&sms);
m_originGraphic->setZIndex(std::numeric_limits<int>::max());
}
void ViewshedGeoProcessing::createExtentGraphic(const Esri::ArcGISRuntime::Point &origin, double radius)
{
if (!m_extentGraphic)
{
m_extentGraphic = new Esri::ArcGISRuntime::Graphic(this);
m_resultsOverlay->graphics()->append(m_extentGraphic);
}
const Esri::ArcGISRuntime::Polygon &circlePoly = Esri::ArcGISRuntime::GeometryEngine::buffer(origin, radius); // circlePoly.parts().part(0).pointCount(); -> 96
m_extentGraphic->setGeometry(circlePoly);
Esri::ArcGISRuntime::SimpleLineSymbol sls(Esri::ArcGISRuntime::SimpleLineSymbolStyle::Solid, QColor("red"), 3.0);
m_extentGraphic->setSymbol(&sls);
m_extentGraphic->setZIndex(std::numeric_limits<int>::max());
}
void ViewshedGeoProcessing::clearInputGraphics()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << this;
if (m_originGraphic)
{
m_resultsOverlay->graphics()->removeOne(m_originGraphic);
delete m_originGraphic;
m_originGraphic = nullptr;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void ViewshedGeoProcessing::clearExtentGraphics()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << this;
if (m_extentGraphic)
{
m_resultsOverlay->graphics()->removeOne(m_extentGraphic);
delete m_extentGraphic;
m_extentGraphic = nullptr;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void ViewshedGeoProcessing::clearResultGraphics()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << this;
for (auto graphic : qAsConst(m_resultGraphics))
{
m_resultsOverlay->graphics()->removeOne(graphic);
delete graphic;
}
m_resultGraphics.clear();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void ViewshedGeoProcessing::onErrorOccuredGeoprocessingTask(const Esri::ArcGISRuntime::Error &error)
{
QString senderName;
Esri::ArcGISRuntime::GeoprocessingTask *senderObj = qobject_cast<Esri::ArcGISRuntime::GeoprocessingTask *>(sender());
if (senderObj)
senderName = senderObj->objectName();
ERROR_LOG(m_logger) << LOGFUNCTION << " GeoprocessingTask -> EsriError occured: " << error
<< ", sender= '" << senderName << "'";
}
void ViewshedGeoProcessing::onErrorOccuredGeoprocessingJob(const Esri::ArcGISRuntime::Error &error)
{
QString senderName;
Esri::ArcGISRuntime::GeoprocessingJob *senderObj = qobject_cast<Esri::ArcGISRuntime::GeoprocessingJob *>(sender());
if (senderObj)
senderName = senderObj->objectName();
ERROR_LOG(m_logger) << LOGFUNCTION << " GeoprocessingJob -> EsriError occured: " << error
<< ", sender= '" << senderName << "'";
}
} // namespace SituationMap
@@ -0,0 +1,526 @@
/**
* @RDE_HEADER@
* ViewshedGeoProcessing
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "viewshed_geo_processing.h"
#include "map_impl.h"
#include <GraphicalObjects/graphical_object_path.h>
#include <Map/situation_map_utils.h>
#include <MapImpl/layer_factory_impl.h>
// ESRI
#include <arcgis/Map.h>
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/Graphic.h>
#include <arcgis/GeoprocessingTask.h>
#include <arcgis/GeoprocessingJob.h>
#include <arcgis/GeoprocessingFeatures.h>
#include <arcgis/GeoprocessingRaster.h>
#include <arcgis/GeoprocessingParameter.h>
#include <arcgis/GeoprocessingParameters.h>
#include <arcgis/GeoprocessingResult.h>
#include <arcgis/FeatureCollectionTable.h>
#include <arcgis/Feature.h>
#include <arcgis/SimpleMarkerSymbol.h>
#include <arcgis/SimpleFillSymbol.h>
#include <arcgis/GeoprocessingLinearUnit.h>
#include <arcgis/ImageServiceRaster.h>
#include <arcgis/RasterLayer.h>
// Qt
#include <QDebug>
namespace SituationMap
{
//-----------------------------------------------------------------------------
// class ViewshedBase
//-----------------------------------------------------------------------------
ViewshedGeoProcessing::ViewshedGeoProcessing(const QString &url,
Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer,
const Esri::ArcGISRuntime::SpatialReference &spatialReference,
QObject *parent)
: ViewshedBase(parent)
, m_url(url)
, m_resultsOverlay(graphicsLayer)
, m_srMap(spatialReference)
, m_logger(getLogger("ViewshedGeoProcessing"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
ViewshedGeoProcessing::~ViewshedGeoProcessing()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
clearGraphics();
delete m_viewshedTask;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
bool ViewshedGeoProcessing::init(SituationMap::MapImpl *mapImpl)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (m_url.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " url is empty";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return false;
}
m_mapImpl = mapImpl;
delete m_viewshedTask;
m_viewshedTask = new Esri::ArcGISRuntime::GeoprocessingTask(QUrl(m_url), this);
m_viewshedTask->setObjectName(QLatin1Literal("GeoprocessingTask"));
// Handle errors of GeoprocessingTask:
connect(m_viewshedTask, &Esri::ArcGISRuntime::GeoprocessingTask::errorOccurred,
this, &ViewshedGeoProcessing::onErrorOccuredGeoprocessingTask);
m_initialized = true;
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " m_initialized=" << m_initialized;
return m_initialized;
}
bool ViewshedGeoProcessing::execute(const QPointF &viewshedPoint_map, const double distance, double zOffset)
{
NOTICE_LOG(m_logger) << LOGFUNCTION << " START"
<< " viewshedPoint_map=" << viewshedPoint_map
<< " m_srMap.wkid=" << m_srMap.wkid()
<< " distance=" << distance
<< " zOffset=" << zOffset
<< " m_viewshedInProgress=" << m_viewshedInProgress
<< " url='" << m_url << "'";
// check point is the viewshed point
Esri::ArcGISRuntime::Point esriViewshedPoint(viewshedPoint_map.x(), viewshedPoint_map.y(), m_srMap); // Esri::ArcGISRuntime::SpatialReference::webMercator());
DEBUG_LOG(m_logger) << LOGFUNCTION << " esriViewshedPoint.toJson: " << esriViewshedPoint.toJson();
m_viewshedInProgress = true;
createInputGraphic(esriViewshedPoint);
clearResultGraphics();
// Create a new feature collection table based upon point geometries using the current map view spatial reference
Esri::ArcGISRuntime::FeatureCollectionTable *inputFeatures =
new Esri::ArcGISRuntime::FeatureCollectionTable(QList<Esri::ArcGISRuntime::Field>()
, Esri::ArcGISRuntime::GeometryType::Point
, m_srMap
, this);
// Create a new feature from the feature collection table. It will not have a coordinate location (x,y) yet
Esri::ArcGISRuntime::Feature *inputFeature = inputFeatures->createFeature(this);
// Assign a physical location to the new point feature based upon where the user clicked on the map view
// inputFeature->setGeometry(m_inputOverlay->graphics()->at(0)->geometry());
inputFeature->setGeometry(esriViewshedPoint);
// connect to addFeature status changed signal
connect(inputFeatures, &Esri::ArcGISRuntime::FeatureCollectionTable::addFeatureCompleted, this, [this, inputFeatures, distance, zOffset](QUuid, bool success)
{
INFO_LOG(m_logger) << LOGFUNCTION << " addFeatureCompleted...";
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no success";
return;
}
// Create the parameters that are passed to the used geoprocessing task
Esri::ArcGISRuntime::GeoprocessingParameters viewshedParameters =
Esri::ArcGISRuntime::GeoprocessingParameters(Esri::ArcGISRuntime::GeoprocessingExecutionType::SynchronousExecute); // AsynchronousSubmit | SynchronousExecute
// GeoprocessingExecutionType should match Esri::ArcGISRuntime::GeoprocessingServiceType of LocalGeoprocessingService
// Request the output features to use the same SpatialReference as the map view
viewshedParameters.setOutputSpatialReference(m_srMap);
// Add an input location to the geoprocessing parameters
QMap<QString, Esri::ArcGISRuntime::GeoprocessingParameter *> inputs;
inputs[QLatin1Literal("Input_point_or_polyline_observer_features")] = new Esri::ArcGISRuntime::GeoprocessingFeatures(inputFeatures, this); //QStringLiteral("ViewshedGeoProcessing")
// Radius
inputs[QLatin1Literal("Inner_radius")] = new Esri::ArcGISRuntime::GeoprocessingLinearUnit(100.0, Esri::ArcGISRuntime::GeoprocessingLinearUnits::Meter, this);
inputs[QLatin1Literal("Outer_radius")] = new Esri::ArcGISRuntime::GeoprocessingLinearUnit(distance, Esri::ArcGISRuntime::GeoprocessingLinearUnits::Meter, this);
// Observer
// //!!! inputs[QLatin1Literal("Observer_elevation")] = new Esri::ArcGISRuntime::GeoprocessingLinearUnit(0.0, Esri::ArcGISRuntime::GeoprocessingLinearUnits::Meter, this);
inputs[QLatin1Literal("Observer_offset")] = new Esri::ArcGISRuntime::GeoprocessingLinearUnit(zOffset, Esri::ArcGISRuntime::GeoprocessingLinearUnits::Meter, this);
// Target
inputs[QLatin1Literal("Surface_offset")] = new Esri::ArcGISRuntime::GeoprocessingLinearUnit(0.0, Esri::ArcGISRuntime::GeoprocessingLinearUnits::Meter, this);
// setInputs
viewshedParameters.setInputs(inputs);
// Print geoprocessingParameters for debugging purpose:
QMapIterator<QString, Esri::ArcGISRuntime::GeoprocessingParameter *> inputsIt(inputs);
while (inputsIt.hasNext())
{
inputsIt.next();
QString value(QLatin1Literal("-"));
Esri::ArcGISRuntime::GeoprocessingParameter *gpPara = inputsIt.value();
Esri::ArcGISRuntime::GeoprocessingLinearUnit *gpParaLU = dynamic_cast<Esri::ArcGISRuntime::GeoprocessingLinearUnit *>(gpPara);
if (gpParaLU)
value = QString(QLatin1Literal("%1 %2")).arg(gpParaLU->distance()).arg(gpParaLU->linearUnit().name());
NOTICE_LOG(m_logger) << LOGFUNCTION << " key= '" << inputsIt.key() << "'" << " value= '" << value << "'";
} // while
// Create the job that handles the communication between the application and the geoprocessing task
m_viewshedJob = m_viewshedTask->createJob(viewshedParameters);
m_viewshedJob->setObjectName(QLatin1Literal("GeoprocessingJob"));
// Handle errors of GeoprocessingJob:
connect(m_viewshedJob, &Esri::ArcGISRuntime::GeoprocessingJob::errorOccurred, this, &ViewshedGeoProcessing::onErrorOccuredGeoprocessingJob);
// Create signal handler for the job
connect(m_viewshedJob, &Esri::ArcGISRuntime::GeoprocessingJob::jobStatusChanged, this, &ViewshedGeoProcessing::onJobStatusChanged);
// start the job
m_viewshedJob->start();
});
// Add the new feature with (x,y) location to the feature collection table
inputFeatures->addFeature(inputFeature);
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " m_viewshedInProgress=" << m_viewshedInProgress;
return true;
}
void ViewshedGeoProcessing::onJobStatusChanged()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
// qCritical() << "jobStatusChanged... viewshedJob->jobStatus()=" << (int)viewshedJob->jobStatus();
switch (m_viewshedJob->jobStatus())
{
case Esri::ArcGISRuntime::JobStatus::NotStarted:
qCritical() << "Geoprocessing Task failed";
m_viewshedInProgress = false;
m_jobStatus = QLatin1Literal("Job NotStarted");
break;
case Esri::ArcGISRuntime::JobStatus::Failed:
{
qCritical() << "Geoprocessing Task failed";
const Esri::ArcGISRuntime::Error &err = m_viewshedJob->error();
qCritical() << "Geoprocessing Task failed message: "
<< " code = " << err.code()
<< " domain = " << (int)err.domain()
<< " message = " << err.message()
<< " additionalMessage = " << err.additionalMessage()
<< " extendedErrorType = " << (int)err.extendedErrorType();
m_viewshedInProgress = false;
m_jobStatus = QLatin1Literal("Job failed");
}
break;
case Esri::ArcGISRuntime::JobStatus::Started:
m_viewshedInProgress = true;
m_jobStatus = QLatin1Literal("Job in progress...");
break;
case Esri::ArcGISRuntime::JobStatus::Paused:
m_viewshedInProgress = false;
m_jobStatus = QLatin1Literal("Job paused...");
break;
case Esri::ArcGISRuntime::JobStatus::Succeeded:
m_viewshedInProgress = false;
m_jobStatus = QLatin1Literal("Job succeeded");
// handle the results
processResults(m_viewshedJob->result());
break;
default:
break;
}
NOTICE_LOG(m_logger) << LOGFUNCTION << " jobStatusChanged is now (m_jobStatus): " << m_jobStatus;
// emit signals
// emit viewshedInProgressChanged();
// emit jobStatusChanged();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
bool ViewshedGeoProcessing::processResults(Esri::ArcGISRuntime::GeoprocessingResult *results)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!results)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no results";
return false;
}
const QMap<QString, Esri::ArcGISRuntime::GeoprocessingParameter *> &outputsMap = results->outputs();
if (outputsMap.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no keys, outputsMap is empty ";
return false;
}
// clear graphics:
clearResultGraphics();
QString key;
key = QStringLiteral("Output_polygon_features");
if (outputsMap.contains(key))
{
// Get the results from the outputs as GeoprocessingFeatures
Esri::ArcGISRuntime::GeoprocessingFeatures *viewshedResultFeatures =
static_cast<Esri::ArcGISRuntime::GeoprocessingFeatures *>(outputsMap[key]);
return processFeatureResults(viewshedResultFeatures);
}
key = QStringLiteral("Output_raster");
if (outputsMap.contains(key))
{
Esri::ArcGISRuntime::GeoprocessingRaster *viewshedResultRaster =
static_cast<Esri::ArcGISRuntime::GeoprocessingRaster *>(outputsMap[key]);
return processRasterResults(viewshedResultRaster);
}
key = QStringLiteral("Output_polygon_features");
if (outputsMap.contains(key))
{
// Get the results from the outputs as GeoprocessingFeatures
Esri::ArcGISRuntime::GeoprocessingFeatures *viewshedResultFeatures =
static_cast<Esri::ArcGISRuntime::GeoprocessingFeatures *>(outputsMap[key]);
return processFeatureResults(viewshedResultFeatures);
}
ERROR_LOG(m_logger) << LOGFUNCTION << " no adeqaute keyword found in outputsMap";
return false;
}
bool ViewshedGeoProcessing::processFeatureResults(Esri::ArcGISRuntime::GeoprocessingFeatures *viewshedResultFeatures)
{
NOTICE_LOG(m_logger) << LOGFUNCTION << " processFeatureResults";
if (!viewshedResultFeatures)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no viewshedResultFeatures";
return false;
}
Esri::ArcGISRuntime::SimpleFillSymbol sfsVisi (Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, QColor(0, 226, 0, 255), this);
Esri::ArcGISRuntime::SimpleFillSymbol sfsInVisi(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, QColor(226, 0, 0, 255), this);
// Add all the features from the result feature set as a graphics to the map
Esri::ArcGISRuntime::FeatureIterator features = viewshedResultFeatures->features()->iterator();
while (features.hasNext())
{
Esri::ArcGISRuntime::Feature *feat = features.next(this);
const QString &attKey(QLatin1String("gridcode"));
if (feat->attributes()->containsAttribute(attKey))
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " attributes()->attributeValue=== " << feat->attributes()->attributeValue(attKey).toString();
if (feat->attributes()->attributeValue(attKey).toInt())
m_resultGraphics.append(new Esri::ArcGISRuntime::Graphic(feat->geometry(), &sfsVisi));
else
m_resultGraphics.append(new Esri::ArcGISRuntime::Graphic(feat->geometry(), &sfsInVisi));
// ERROR_LOG(m_logger) << LOGFUNCTION << " attributeValue " << feat->attributes()->attributeValue(attKey).toInt();
} else
ERROR_LOG(m_logger) << LOGFUNCTION << " no key attributeValue ";
}
// transfer from own container to resultsOverlay:
m_resultsOverlay->graphics()->append(m_resultGraphics);
return true;
}
bool ViewshedGeoProcessing::processRasterResults(Esri::ArcGISRuntime::GeoprocessingRaster *viewshedResultRaster)
{
NOTICE_LOG(m_logger) << LOGFUNCTION << " viewshedResultRaster -> url = " << viewshedResultRaster->url().toString();
connect(viewshedResultRaster, &Esri::ArcGISRuntime::GeoprocessingRaster::fetchFileCompleted, this, [this] (QUuid taskId, const QString& filePath) {
NOTICE_LOG(m_logger) << LOGFUNCTION << " filePath=" << filePath;
Esri::ArcGISRuntime::Raster *raster = new Esri::ArcGISRuntime::Raster(filePath, this);
if (!raster)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " raster failed";
return;
}
connect(raster, &Esri::ArcGISRuntime::Raster::errorOccurred, this, [this, raster] (const Esri::ArcGISRuntime::Error &error) {
ERROR_LOG(m_logger) << LOGFUNCTION << " raster EsriError occured: "
<< ", code=" << error.code()
<< ", domain=" << (int)error.domain()
<< ", extendedErrorType=" << (int)error.extendedErrorType()
<< ", message='" << error.message() << "'"
<< ", additionalMessage='" << error.additionalMessage() << "'";
});
connect(raster, &Esri::ArcGISRuntime::Raster::loadStatusChanged, this, [this, raster] (Esri::ArcGISRuntime::LoadStatus loadStatus) {
ERROR_LOG(m_logger) << LOGFUNCTION << " raster loadStatus=" << loadStatus;
if (loadStatus == Esri::ArcGISRuntime::LoadStatus::Loaded)
{
Esri::ArcGISRuntime::RasterLayer *rl = new Esri::ArcGISRuntime::RasterLayer(raster, this);
if (!rl)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " rl failed";
return;
}
connect(rl, &Esri::ArcGISRuntime::RasterLayer::errorOccurred, this, [this] (const Esri::ArcGISRuntime::Error &error) {
ERROR_LOG(m_logger) << LOGFUNCTION << " RasterLayer EsriError occured: "
<< ", code=" << error.code()
<< ", domain=" << (int)error.domain()
<< ", extendedErrorType=" << (int)error.extendedErrorType()
<< ", message='" << error.message() << "'"
<< ", additionalMessage='" << error.additionalMessage() << "'";
});
connect(rl, &Esri::ArcGISRuntime::RasterLayer::loadStatusChanged, this, [this] (Esri::ArcGISRuntime::LoadStatus loadStatus) {
ERROR_LOG(m_logger) << LOGFUNCTION << " RasterLayer loadStatus=" << loadStatus;
});
NOTICE_LOG(m_logger) << LOGFUNCTION << " getEsriMap()->operationalLayers()->size" << m_mapImpl->getEsriMap()->operationalLayers()->size();
m_mapImpl->getEsriMap()->operationalLayers()->append(rl);
NOTICE_LOG(m_logger) << LOGFUNCTION << " getEsriMap()->operationalLayers()->size" << m_mapImpl->getEsriMap()->operationalLayers()->size();
NOTICE_LOG(m_logger) << LOGFUNCTION << " rasterLayer loading ...";
rl->load();
}
});
NOTICE_LOG(m_logger) << LOGFUNCTION << " raster loading ...";
raster->load();
});
Esri::ArcGISRuntime::TaskWatcher tw = viewshedResultRaster->fetchFile(QString(QLatin1Literal("/tmp/viewshedFetched.tif")));
if (!tw.isValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " m_mapImpl failed";
return false;
}
return true;
}
void ViewshedGeoProcessing::clearGraphics()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << this;
clearInputGraphics();
clearResultGraphics();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void ViewshedGeoProcessing::createInputGraphic(const Esri::ArcGISRuntime::Point &origin)
{
if (!m_originGraphic)
{
m_originGraphic = new Esri::ArcGISRuntime::Graphic(this);
m_resultsOverlay->graphics()->append(m_originGraphic);
}
m_originGraphic->setGeometry(origin);
Esri::ArcGISRuntime::SimpleMarkerSymbol sms(Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle, QColor("red"), 12.0);
m_originGraphic->setSymbol(&sms);
m_originGraphic->setZIndex(32000);
m_resultsOverlay->graphics()->append(m_originGraphic);
}
void ViewshedGeoProcessing::clearInputGraphics()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << this;
if (m_originGraphic)
{
m_resultsOverlay->graphics()->removeOne(m_originGraphic);
delete m_originGraphic;
m_originGraphic = nullptr;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void ViewshedGeoProcessing::clearResultGraphics()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << this;
for (auto graphic : qAsConst(m_resultGraphics))
{
m_resultsOverlay->graphics()->removeOne(graphic);
delete graphic;
}
m_resultGraphics.clear();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void ViewshedGeoProcessing::onErrorOccuredGeoprocessingTask(const Esri::ArcGISRuntime::Error &error)
{
QString senderName;
Esri::ArcGISRuntime::GeoprocessingTask *senderObj = qobject_cast<Esri::ArcGISRuntime::GeoprocessingTask *>(sender());
if (senderObj)
senderName = senderObj->objectName();
ERROR_LOG(m_logger) << LOGFUNCTION << " GeoprocessingTask -> EsriError occured: "
<< " sender= '" << senderName << "'"
<< ", code=" << error.code()
<< ", domain=" << (int)error.domain()
<< ", extendedErrorType=" << (int)error.extendedErrorType()
<< ", message='" << error.message() << "'"
<< ", additionalMessage='" << error.additionalMessage() << "'";
}
void ViewshedGeoProcessing::onErrorOccuredGeoprocessingJob(const Esri::ArcGISRuntime::Error &error)
{
QString senderName;
Esri::ArcGISRuntime::GeoprocessingJob *senderObj = qobject_cast<Esri::ArcGISRuntime::GeoprocessingJob *>(sender());
if (senderObj)
senderName = senderObj->objectName();
ERROR_LOG(m_logger) << LOGFUNCTION << " GeoprocessingJob -> EsriError occured: "
<< " sender= '" << senderName << "'"
<< ", code=" << error.code()
<< ", domain=" << (int)error.domain()
<< ", extendedErrorType=" << (int)error.extendedErrorType()
<< ", message='" << error.message() << "'"
<< ", additionalMessage='" << error.additionalMessage() << "'";
}
} // namespace SituationMap
+124
View File
@@ -0,0 +1,124 @@
/**
* @RDE_HEADER@
* ViewshedGeoProcessing
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "viewshed_base.h"
// ESRI
#include <arcgis/arcgisversion.h> // This file is created by RME, not ESRI
#include <arcgis/Map.h>
#include <arcgis/SpatialReference.h>
// Qt
#include <QObject>
#include <QColor>
#include <QPointF>
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Map;
class Graphic;
class GraphicsOverlay;
class GeoprocessingTask;
class GeoprocessingJob;
class GeoprocessingResult;
class GeoprocessingFeatures;
class GeoprocessingRaster;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
class MapImpl;
class ViewshedGeoProcessing : public ViewshedBase
{
Q_OBJECT
public:
explicit ViewshedGeoProcessing(const QString &url,
Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer,
const Esri::ArcGISRuntime::SpatialReference &spatialReference,
QObject *parent = nullptr);
virtual ~ViewshedGeoProcessing();
bool init(SituationMap::MapImpl *mapImpl = nullptr); // mapImpl is required only if processRasterResults is used
bool initialized() const { return m_initialized; }
bool execute(const QPointF &viewshedPoint_map, double outerRadius, double observerOffset) override;
bool isRunning() const { return m_viewshedInProgress; }
bool processResults(Esri::ArcGISRuntime::GeoprocessingResult *results);
void clearGraphics();
void createInputGraphic(const Esri::ArcGISRuntime::Point &origin);
void clearInputGraphics();
void createExtentGraphic(const Esri::ArcGISRuntime::Point &origin, double radius);
void clearExtentGraphics();
void clearResultGraphics();
protected slots:
void onJobStatusChanged();
#if ACRGIS_VERSION >= 0x640E00 // 100.14.0
void onJobStatusChangedWithJsParameter(const Esri::ArcGISRuntime::JobStatus &);
#endif // ACRGIS_VERSION
void onErrorOccuredGeoprocessingTask(const Esri::ArcGISRuntime::Error &error);
void onErrorOccuredGeoprocessingJob(const Esri::ArcGISRuntime::Error &error);
protected:
bool processFeatureResults(Esri::ArcGISRuntime::GeoprocessingFeatures *viewshedResultFeatures);
bool processRasterResults(Esri::ArcGISRuntime::GeoprocessingRaster *viewshedResultRaster);
private:
QString m_url;
Esri::ArcGISRuntime::GraphicsOverlay *m_resultsOverlay;
SituationMap::MapImpl *m_mapImpl = nullptr;
Esri::ArcGISRuntime::SpatialReference m_srMap;
Esri::ArcGISRuntime::GeoprocessingTask *m_viewshedTask = nullptr;
QString m_jobStatus;
Esri::ArcGISRuntime::GeoprocessingJob *m_viewshedJob = nullptr;
bool m_initialized = false;
bool m_viewshedInProgress = false;
QList<Esri::ArcGISRuntime::Graphic *> m_resultGraphics;
Esri::ArcGISRuntime::Graphic *m_originGraphic = nullptr;
Esri::ArcGISRuntime::Graphic *m_extentGraphic = nullptr;
LOGGER_T m_logger;
}; // class ViewshedGeoProcessing
} // namespace SituationMap
@@ -0,0 +1,116 @@
/**
* @RDE_HEADER@
* ViewshedGeoProcessing
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "viewshed_base.h"
// ESRI
#include <arcgis/Map.h>
#include <arcgis/SpatialReference.h>
// Qt
#include <QObject>
#include <QColor>
#include <QPointF>
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Map;
class Graphic;
class GraphicsOverlay;
class GeoprocessingTask;
class GeoprocessingJob;
class GeoprocessingResult;
class GeoprocessingFeatures;
class GeoprocessingRaster;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
class MapImpl;
class ViewshedGeoProcessing : public ViewshedBase
{
Q_OBJECT
public:
explicit ViewshedGeoProcessing(const QString &url,
Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer,
const Esri::ArcGISRuntime::SpatialReference &spatialReference,
QObject *parent = nullptr);
virtual ~ViewshedGeoProcessing();
bool init(SituationMap::MapImpl *mapImpl = nullptr);
bool initialized() const { return m_initialized; }
bool execute(const QPointF &viewshedPoint_map, const double distance, double zOffset) override;
bool isRunning() const { return m_viewshedInProgress; }
bool processResults(Esri::ArcGISRuntime::GeoprocessingResult *results);
void clearGraphics();
void createInputGraphic(const Esri::ArcGISRuntime::Point &origin);
void clearInputGraphics();
void clearResultGraphics();
protected slots:
void onJobStatusChanged();
void onErrorOccuredGeoprocessingTask(const Esri::ArcGISRuntime::Error &error);
void onErrorOccuredGeoprocessingJob(const Esri::ArcGISRuntime::Error &error);
protected:
bool processFeatureResults(Esri::ArcGISRuntime::GeoprocessingFeatures *viewshedResultFeatures);
bool processRasterResults(Esri::ArcGISRuntime::GeoprocessingRaster *viewshedResultRaster);
private:
QString m_url;
Esri::ArcGISRuntime::GraphicsOverlay *m_resultsOverlay;
SituationMap::MapImpl *m_mapImpl = nullptr;
Esri::ArcGISRuntime::SpatialReference m_srMap;
Esri::ArcGISRuntime::GeoprocessingTask *m_viewshedTask = nullptr;
QString m_jobStatus;
Esri::ArcGISRuntime::GeoprocessingJob *m_viewshedJob = nullptr;
bool m_initialized = false;
bool m_viewshedInProgress = false;
QList<Esri::ArcGISRuntime::Graphic *> m_resultGraphics;
Esri::ArcGISRuntime::Graphic *m_originGraphic = nullptr;
LOGGER_T m_logger;
}; // class ViewshedGeoProcessing
} // namespace SituationMap
+880
View File
@@ -0,0 +1,880 @@
/**
* @RDE_HEADER@
* Interface-Implementation of Viewshed
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "viewshed_impl.h"
#include "viewshed_analysis.h"
#include "viewshed_geo_processing.h"
#include "layer_factory_impl.h"
#include "coord_translation_functor_impl.h"
#include <LtsCoordTrans/lts_coord_trans.h>
#include <Map/object_factory.h>
#include <Map/sight_fan.h>
#include <MathUtils/math_const.h>
#include <MathUtils/simple_helper.h>
#include "Rubberbands/rubberband_factory.h"
// Qt
namespace SituationMap
{
class ViewshedImpl::Private
{
public:
Private() = default;
~Private() = default;
// ESRI data:
// Own data:
SituationMap::MapImpl *m_mapImpl = nullptr;
QPointer<RB::RubberbandBase> m_rubbandBase = nullptr;
QString m_url;
Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr; // graphicsLayer used in 2D
ViewshedParameter m_viewshedParameter;
using COUNTER_T = long;
static COUNTER_T m_viewshedCounter;
QMap<COUNTER_T, ViewshedBase* > m_viewshedMap;
QMap<COUNTER_T, SightFan*> m_sightFanMap;
}; // class ViewshedImpl::Private
//-----------------------------------------------------------------------------
ViewshedImpl::Private::COUNTER_T ViewshedImpl::Private::m_viewshedCounter = 0;
//-----------------------------------------------------------------------------
// class ViewshedImpl
//-----------------------------------------------------------------------------
ViewshedImpl::ViewshedImpl(SituationMap::MapImpl *mapImpl)
: BaseClass()
, m_logger(getLogger("ViewshedImpl"))
, d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
d->m_mapImpl = mapImpl;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
ViewshedImpl::~ViewshedImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
this->remove();
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void ViewshedImpl::setViewshedUrl(const QString &viewshedServiceUrl)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, viewshedServiceUrl='" << viewshedServiceUrl << "'";
d->m_url = viewshedServiceUrl;
if (d->m_url.isEmpty())
{
// only demo!!! 10.2.6!? http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Elevation/ESRI_Elevation_World/GPServer/Viewshed
// only demo!!! V100.2 https://sampleserver6.arcgisonline.com/arcgis/rest/services/Elevation/ESRI_Elevation_World/GPServer/Viewshed
d->m_url = QLatin1String("https://sampleserver6.arcgisonline.com/arcgis/rest/services/Elevation/ESRI_Elevation_World/GPServer/Viewshed"); // only demo!!!
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, d->m_url'" << d->m_url << "'";
}
bool ViewshedImpl::isReady()
{
init();
bool isReady = (d->m_graphicsLayer) && d->m_mapImpl->isReady();
if (!isReady)
{
DEBUG_LOG(m_logger) << LOGFUNCTION
<< " d->m_url.isEmpty() = " << d->m_url.isEmpty()
<< " d->m_graphicsLayer = " << d->m_graphicsLayer
<< " d->m_mapImpl->isReady() = " << d->m_mapImpl->isReady();
}
return isReady;
}
bool ViewshedImpl::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!isReady())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " failed, not ready";
return false;
}
// remove all former
this->remove();
// remove any pending/active rubberbands of our map:
RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl);
// d->m_rubbandBase; should be cleared now
const Mode mode = d->m_mapImpl->mode();
switch (mode) {
case Mode::Mode_2D:
d->m_rubbandBase = createRubberband2D();
break;
case Mode::Mode_3D:
d->m_rubbandBase = createRubberband3D();
break;
default:
ERROR_LOG(m_logger) << LOGFUNCTION << " mode (" << mode << ")" << " does not support viewshed";
return false;
}
return true;
}
bool ViewshedImpl::create(double alpha, double innerRadius, double observerHeight, double targetHeight, double beamAngle)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!isReady())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " failed, not ready";
return false;
}
if (alpha < 0.0) alpha = 0.0;
if (alpha > 1.0) alpha = 1.0;
if (beamAngle < 0.5) beamAngle = 0.5;
// remove all former
this->remove();
// remove any pending/active rubberbands of our map:
RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl);
d->m_viewshedParameter.innerRadius = innerRadius;
d->m_viewshedParameter.observerHeight = observerHeight;
d->m_viewshedParameter.targetHeight = targetHeight;
d->m_viewshedParameter.dAngle = beamAngle*MathConst::pi/180.0; // grad to rad
d->m_viewshedParameter.alpha = alpha;
const Mode mode = d->m_mapImpl->mode();
switch (mode) {
case Mode::Mode_2D:
d->m_rubbandBase = createSightFanRubberband2D();
break;
case Mode::Mode_3D:
d->m_rubbandBase = createSightFanRubberband3D();
break;
default:
ERROR_LOG(m_logger) << LOGFUNCTION << " mode (" << mode << ")" << " does not support viewshed";
return false;
}
return true;
// d->m_rubbandBase; should be cleared now
}
long ViewshedImpl::create(const ViewshedParameter &viewshedParameter)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " viewshedParameter: " << viewshedParameter;
if (!isReady())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " failed, not ready";
return -1;
}
// store current values:
d->m_viewshedParameter = viewshedParameter;
ViewshedBase *viewshed = nullptr;
// GPU-based viewshed (viewshedAnalysis) is today supported for 3D only:
const Mode mode = d->m_mapImpl->mode();
switch (mode) {
case Mode::Mode_2D:
viewshed = create_viewshedGP(d->m_viewshedParameter);
break;
case Mode::Mode_3D:
viewshed = create_viewshedAnalysis(d->m_viewshedParameter);
break;
default:
ERROR_LOG(m_logger) << LOGFUNCTION << " mode (" << mode << ")" << " does not support viewshed";
return -1;
}
if (!viewshed)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " creation of viewshed failed" << " mode (" << mode << ")";
return -1;
}
d->m_viewshedMap[++d->m_viewshedCounter] = viewshed;
emit viewshedFinished(d->m_viewshedCounter);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return d->m_viewshedCounter;
} // long ViewshedImpl::create(const ViewshedParameter &viewshedParameter)
bool ViewshedImpl::modify(long viewshedId, double alpha, double innerRadius, double observerHeight, double targetHeight, double beamAngle)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!isReady())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " failed, not ready";
return false;
}
if (alpha < 0.0) alpha = 0.0;
if (alpha > 1.0) alpha = 1.0;
if (beamAngle < 0.5) beamAngle = 0.5;
if (viewshedId < 0) viewshedId = d->m_viewshedCounter;
if (d->m_sightFanMap.contains(viewshedId))
{
SightFan *sightFan = d->m_sightFanMap[viewshedId];
d->m_viewshedParameter.innerRadius = innerRadius;
d->m_viewshedParameter.observerHeight = observerHeight;
d->m_viewshedParameter.targetHeight = targetHeight;
d->m_viewshedParameter.dAngle = beamAngle * MathConst::pi / 180.0; // grad to rad
d->m_viewshedParameter.noBeams = MathConst::pi_two / d->m_viewshedParameter.dAngle;
d->m_viewshedParameter.alpha = alpha;
sightFan->setAlpha( d->m_viewshedParameter.alpha );
sightFan->setInnerRadius( d->m_viewshedParameter.innerRadius );
sightFan->setCenterHat( d->m_viewshedParameter.observerHeight );
sightFan->setObjectHat( d->m_viewshedParameter.targetHeight );
sightFan->setdeltaAngle( d->m_viewshedParameter.dAngle );
sightFan->setNoOfFans( d->m_viewshedParameter.noBeams );
sightFan->show();
return true;
}
return false;
} // bool ViewshedImpl::modify(long viewshedId, double alpha, double innerRadius, double observerHeight, double targetHeight, double beamAngle)
ViewshedBase *ViewshedImpl::create_viewshedGP(const ViewshedParameter &viewshedParameter)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " viewshedParameter: " << viewshedParameter;
if (!d->m_graphicsLayer)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no graphicsLayer for viewshed";
return nullptr;
}
if (d->m_url.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no url for viewshed";
return nullptr;
}
ViewshedGeoProcessing *viewshed = new ViewshedGeoProcessing(d->m_url, d->m_graphicsLayer, d->m_mapImpl->getEsriSpatialReference());
if (!viewshed->init(d->m_mapImpl))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " init failed";
delete viewshed;
return nullptr;
}
// coordTranslation:
CoordTranslationFunctor *coordTransFunctor = d->m_mapImpl->coordTranslationFunctor();
const QPointF &viewshedPointTranslated = coordTransFunctor ? coordTransFunctor->geographicToMap(viewshedParameter.viewshedOrigin_latLon) : QPointF();
DEBUG_LOG(m_logger) << LOGFUNCTION
<< " viewshedParameter= " << viewshedParameter;
if (!viewshed->execute(viewshedPointTranslated, // Map-Coord
viewshedParameter.outerRadius,
viewshedParameter.observerHeight))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " executing GeoProcessing viewshed failed.";
delete viewshed;
return nullptr;
}
return viewshed;
} // ViewshedBase *ViewshedImpl::create_viewshedGP(const ViewshedParameter &viewshedParameter)
ViewshedBase *ViewshedImpl::create_viewshedAnalysis(const ViewshedParameter &viewshedParameter)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " viewshedParameter: " << viewshedParameter;
if (!d->m_mapImpl->getEsriSceneGraphicsView())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " getEsriSceneGraphicsView failed for viewsheds";
return nullptr;
}
ViewshedAnalysis *viewshed = new ViewshedAnalysis(d->m_mapImpl->getEsriSceneGraphicsView());
// rad to degree
const double factor = 180 / MathConst::pi;
const QPointF &centerDegree = viewshedParameter.viewshedOrigin_latLon * factor;
if (!viewshed->execute(centerDegree, // degree
viewshedParameter.outerRadius,
viewshedParameter.observerHeight))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " executing analysis viewshed failed.";
delete viewshed;
return nullptr;
}
return viewshed;
} // ViewshedBase *ViewshedImpl::create_viewshedAnalysis(const ViewshedParameter &viewshedParameter)
void ViewshedImpl::remove(long viewshedId)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " viewshedId=" << viewshedId;
//if (viewshedId < 0)
{
// ok, not special viewshed is adressed --> remove all
for (ViewshedBase *viewshed : qAsConst(d->m_viewshedMap))
delete viewshed;
d->m_viewshedMap.clear();
for (SightFan *sightFan : qAsConst(d->m_sightFanMap))
{
if (sightFan)
sightFan->removeMySelf();
}
d->m_sightFanMap.clear();
return;
}
if (d->m_viewshedMap.contains(viewshedId))
{
delete d->m_viewshedMap[viewshedId];
d->m_viewshedMap.remove(viewshedId);
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " viewshedId=" << viewshedId;
return;
}
if (d->m_sightFanMap.contains(viewshedId))
{
d->m_sightFanMap[viewshedId]->removeMySelf();
d->m_sightFanMap[viewshedId] = nullptr;
d->m_sightFanMap.remove(viewshedId);
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " viewshedId=" << viewshedId;
return;
}
ERROR_LOG(m_logger) << LOGFUNCTION << " viewShedId not found, id=" << viewshedId;
}
ViewshedParameter ViewshedImpl::getViewshedParameter(long viewshedId, bool *ok) const
{
if (ok)
*ok = false;
if (viewshedId < 0)
viewshedId = d->m_viewshedCounter;
if (d->m_sightFanMap.contains(viewshedId))
{
const SightFan *sightFan = d->m_sightFanMap[viewshedId];
if (sightFan)
{
ViewshedParameter vp;
vp.viewshedOrigin_latLon = sightFan->center();
vp.observerHeight = sightFan->centerHat();
vp.targetHeight = sightFan->objectHat();
vp.innerRadius = sightFan->innerRadius();
vp.outerRadius = sightFan->radius();
vp.startAngle = sightFan->startAngle();
vp.dAngle = sightFan->dAngle();
vp.alpha = sightFan->alpha();
vp.noBeams = sightFan->noOfFans();
if (ok)
*ok = true;
return vp;
}
}
return d->m_viewshedParameter;
} // const ViewshedParameter & ViewshedImpl::getViewshedParameter(long viewshedId = -1, bool *ok = nullptr)
void ViewshedImpl::init()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!d->m_graphicsLayer)
{
LayerFactoryImpl &layerFactory = d->m_mapImpl->layerFactoryImpl();
const QString &toolsLayername = layerFactory.toolLayer();
d->m_graphicsLayer = layerFactory.getGraphicsLayer(toolsLayername);
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
RB::RubberbandEllipse *ViewshedImpl::createRubberband2D()
{
// remove any pending/active rubberbands of our map:
RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl);
RB::RubberbandEllipse *rubberband =
d->m_mapImpl->rubberbandFactory().create<SituationMap::RB::RubberbandEllipse>(this);
if (!rubberband || !rubberband->rubberbandIsAlive())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " create2D failed";
return nullptr;
}
rubberband->setAutoDelete(true);
rubberband->constrainQuadratic(true);
rubberband->constrainOrthogonal(true);
// rubberband->minWidth(radiusMin * 2); // Should be 2*radiusMin
// rubberband->maxWidth(radiusMax * 2); // Should be 2*radiusMax
connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &ViewshedImpl::onRbAbort, Qt::UniqueConnection);
connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &ViewshedImpl::onRb2DApply, Qt::UniqueConnection);
return rubberband;
}
RB::RubberbandEllipse *ViewshedImpl::createSightFanRubberband2D()
{
// remove any pending/active rubberbands of our map:
RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl);
RB::RubberbandEllipse *rubberband =
d->m_mapImpl->rubberbandFactory().create<SituationMap::RB::RubberbandEllipse>(this);
if (!rubberband || !rubberband->rubberbandIsAlive())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " create2D failed";
return nullptr;
}
rubberband->setAutoDelete(true);
rubberband->constrainQuadratic(true);
rubberband->constrainOrthogonal(true);
rubberband->enableSector(false); // set enableSector to true to allow sector sight fans instead of full circles
rubberband->minWidth(d->m_viewshedParameter.innerRadius * 3); // radius should be larger than innerRadius
// rubberband->maxWidth(radiusMax * 2); // Should be 2*radiusMax
connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &ViewshedImpl::onRbAbort, Qt::UniqueConnection);
connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &ViewshedImpl::onSightFanRb2DApply, Qt::UniqueConnection);
return rubberband;
} // RB::RubberbandEllipse *ViewshedImpl::createSightFanRubberband2D()
RB::RubberbandMultiPointFeature3D *ViewshedImpl::createSightFanRubberband3D()
{
// remove any pending/active rubberbands of our map:
RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl);
RB::RubberbandMultiPointFeature3D *rubberband =
d->m_mapImpl->rubberbandFactory().create3D<RB::RubberbandMultiPointFeature3D>(this);
if (!rubberband || !rubberband->rubberbandIsAlive())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " create3D failed";
return nullptr;
}
rubberband->setAutoDelete(true);
rubberband->enableCircular(true);
// rubberband->constrainQuadratic(true);
// rubberband->constrainOrthogonal(true);
connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &ViewshedImpl::onRbAbort, Qt::UniqueConnection);
connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &ViewshedImpl::onSightFanRb3DApply, Qt::UniqueConnection);
return rubberband;
} // RB::RubberbandMultiPointFeature3D *ViewshedImpl::createSightFanRubberband3D()
RB::RubberbandMultiPointFeature3D *ViewshedImpl::createRubberband3D()
{
// remove any pending/active rubberbands of our map:
RB::Rubberband::deleteAllActiveRubberbands(d->m_mapImpl);
RB::RubberbandMultiPointFeature3D *rubberband =
d->m_mapImpl->rubberbandFactory().create3D<RB::RubberbandMultiPointFeature3D>(this);
if (!rubberband || !rubberband->rubberbandIsAlive())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " create3D failed";
return nullptr;
}
rubberband->setAutoDelete(true);
rubberband->enableCircular(true);
// rubberband->constrainQuadratic(true);
// rubberband->constrainOrthogonal(true);
connect(rubberband, &SituationMap::RB::Rubberband::abort, this, &ViewshedImpl::onRbAbort, Qt::UniqueConnection);
connect(rubberband, &SituationMap::RB::Rubberband::apply, this, &ViewshedImpl::onSightFanRb3DApply, Qt::UniqueConnection);
return rubberband;
}
void ViewshedImpl::onRbAbort(RB::RubberbandBase *rbnd)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!rbnd)
return;
DEBUG_LOG(m_logger) << LOGFUNCTION << " rb abort";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void ViewshedImpl::onSightFanRb2DApply(RB::RubberbandBase *rbnd)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!rbnd)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no rubberband";
return;
}
RB::RubberbandEllipse *ellipse = static_cast<RB::RubberbandEllipse *>(rbnd);
const QString &toolLayerName = d->m_mapImpl->layerFactoryImpl().toolLayer();
double diameter;
double dummy;
QPointF direction;
ellipse->rectangleParameter( d->m_viewshedParameter.viewshedOrigin_latLon, diameter, dummy, direction );
if (diameter > 0.0)
{
d->m_viewshedParameter.outerRadius = diameter * 0.5;
if (ellipse->hasSector())
{
double startAngle = SituationMap::MathUtils::getMathAngle(ellipse->sectorStart());
double endAngle = SituationMap::MathUtils::getMathAngle(ellipse->sectorEnd());
if (startAngle > endAngle)
{
startAngle -= MathConst::pi_two;
}
d->m_viewshedParameter.noBeams = ceil( (endAngle - startAngle) / d->m_viewshedParameter.dAngle );
d->m_viewshedParameter.startAngle = startAngle;
}
else
{
d->m_viewshedParameter.startAngle = 0.0;
d->m_viewshedParameter.noBeams = MathConst::pi_two / d->m_viewshedParameter.dAngle;
}
ObjectFactory &objFac = d->m_mapImpl->objectFactory();
SightFan *sightFan = objFac.create<SituationMap::SightFan>(toolLayerName).take();
if (sightFan)
{
sightFan->initFan( d->m_viewshedParameter.viewshedOrigin_latLon
, d->m_viewshedParameter.observerHeight
, d->m_viewshedParameter.targetHeight
, d->m_viewshedParameter.outerRadius
, d->m_viewshedParameter.innerRadius
, d->m_viewshedParameter.startAngle
, d->m_viewshedParameter.dAngle
, d->m_viewshedParameter.noBeams
, d->m_viewshedParameter.alpha );
long sightFanId = ++d->m_viewshedCounter;
d->m_sightFanMap[sightFanId] = sightFan;
emit viewshedFinished(sightFanId);
// create a RubberbandPointFeature to let the user move around the sight fan
RB::RubberbandPointFeature *moveRB = d->m_mapImpl->rubberbandFactory().create<RB::RubberbandPointFeature>(this);
if (moveRB)
{
moveRB->setAutoDelete(true);
moveRB->enableModificationState();
moveRB->setCoordinateVector(QVector<QPointF>() << sightFan->center());
std::function<bool(SituationMap::RB::RubberbandBase*)> moveSightFan = [this, sightFanId](SituationMap::RB::RubberbandBase* rbBase) -> bool
{
SituationMap::RB::RubberbandPointFeature *rubberPFeat = dynamic_cast<SituationMap::RB::RubberbandPointFeature*>(rbBase);
SightFan * sightFan = d->m_sightFanMap[sightFanId];
if (nullptr == sightFan)
{
if (rubberPFeat) rubberPFeat->deleteMySelf();
return false;
}
if (rubberPFeat)
{
const QPointF & loc = rubberPFeat->singleCoordinate();
sightFan->move(loc);
}
return true;
};
moveRB->installExternalUpdate(moveSightFan);
}
} // if sightFan
} // if diameter
} // void ViewshedImpl::onSightFanRb2DApply(RB::RubberbandBase *rbnd)
void ViewshedImpl::onSightFanRb3DApply(RB::RubberbandBase *rbnd)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!rbnd)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no rubberband";
return;
}
RB::RubberbandMultiPointFeature3D *rubberband = static_cast<RB::RubberbandMultiPointFeature3D *>(rbnd);
const QString &toolLayerName = d->m_mapImpl->layerFactoryImpl().toolLayer();
const QVector<QPointF> &data = rubberband->coordinateVector(RB::CoordinateSystem::Map); // 3D -> RB::CoordinateSystem::Map -> degree
if (data.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed!?";
return;
}
if (data.size() < 2)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " insufficiant number of coords";
return;
}
ViewshedParameter viewshedParameter;
// degree to rad
const double factor = MathConst::pi / 180.0;
const QPointF &center = data.at(0) * factor;
// viewshedParameter.viewshedOrigin_latLon = center;
{
static Esri::ArcGISRuntime::GeometryEngine *gEngine = Esri::ArcGISRuntime::GeometryEngine::instance();
const Esri::ArcGISRuntime::SpatialReference &sr = d->m_mapImpl->getEsriSpatialReference();
QPointF coord = data.at(0);
Esri::ArcGISRuntime::Point geom1(coord.x(), coord.y(), sr);
coord = data.at(1);
Esri::ArcGISRuntime::Point geom2(coord.x(), coord.y(), sr);
Esri::ArcGISRuntime::GeodeticDistanceResult geodeticDistanceResult = gEngine->distanceGeodetic(geom1, geom2,
Esri::ArcGISRuntime::LinearUnitId::Meters,
Esri::ArcGISRuntime::AngularUnitId::Degrees,
Esri::ArcGISRuntime::GeodeticCurveType::Geodesic);
viewshedParameter.outerRadius = geodeticDistanceResult.distance();
}
unsigned int noBeams = floor((MathConst::pi_two) / d->m_viewshedParameter.dAngle);
ObjectFactory &objFac = d->m_mapImpl->objectFactory();
RefPointer<SightFan> objRef = objFac.create<SituationMap::SightFan>(toolLayerName);
SightFan *sightFan = objRef.get();
if (sightFan)
{
if (d->m_viewshedParameter.innerRadius*1.5 > viewshedParameter.outerRadius)
{
viewshedParameter.outerRadius = d->m_viewshedParameter.innerRadius*1.5;
}
sightFan->initFan(center
, d->m_viewshedParameter.observerHeight
, d->m_viewshedParameter.targetHeight
, viewshedParameter.outerRadius
, d->m_viewshedParameter.innerRadius
, 0.0
, d->m_viewshedParameter.dAngle
, noBeams
, d->m_viewshedParameter.alpha );
long sightFanId = ++d->m_viewshedCounter;
d->m_sightFanMap[sightFanId] = sightFan;
emit viewshedFinished(sightFanId);
// create a RubberbandPointFeature3D to let the user move around the sight fan
RB::RubberbandPointFeature3D *moveRB = d->m_mapImpl->rubberbandFactory().create3D<RB::RubberbandPointFeature3D>(this);
if (moveRB)
{
moveRB->setAutoDelete(true);
moveRB->enableModificationState();
moveRB->setCoordinateVector(QVector<QPointF>() << sightFan->center());
std::function<bool(SituationMap::RB::RubberbandBase*)> moveSightFan3D = [this, sightFanId](SituationMap::RB::RubberbandBase* rbBase) -> bool
{
SituationMap::RB::RubberbandPointFeature3D *rubberPFeat = dynamic_cast<SituationMap::RB::RubberbandPointFeature3D*>(rbBase);
SightFan * sightFan = d->m_sightFanMap[sightFanId];
if (nullptr == sightFan)
{
if (rubberPFeat) rubberPFeat->deleteMySelf();
return false;
}
if (rubberPFeat)
{
const QPointF & loc = rubberPFeat->singleCoordinate();
sightFan->move(loc);
}
return true;
};
moveRB->installExternalUpdate(moveSightFan3D);
}
}
} // void ViewshedImpl::onSightFanRb3DApply(RB::RubberbandBase *rbnd)
void ViewshedImpl::onRb2DApply(RB::RubberbandBase *rbnd)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!rbnd)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no rubberband";
return;
}
RB::RubberbandEllipse *ellipse = static_cast<RB::RubberbandEllipse *>(rbnd);
const QVector<QPointF> &data = ellipse->rectangleVerticies();
const LtsCoordTrans::UtmCoordinate p0(LtsCoordTrans::UtmCoordinate::fromGeographic(data.at(0)));
const LtsCoordTrans::UtmCoordinate p1(LtsCoordTrans::UtmCoordinate::fromGeographic(data.at(2), p0.zone));
const QPointF &center = ellipse->centerPoint();
double radius = fabs((p0.x - p1.x) / 2);
// 23.03.2018, N. Thoden
// LegatusBremen201803151510.gpk contains dem with cell size of ~ 50m
// So the inner_radius has to be >= 50m
// And of course: outer_radius > inner_radius
// So outerRadius > 60
if (radius < 100.0)
radius = 100.0;
ViewshedParameter viewshedParameter;
viewshedParameter.viewshedOrigin_latLon = center;
viewshedParameter.outerRadius = radius;
long id = this->create(viewshedParameter);
if (id < 0)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed!?";
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void ViewshedImpl::onRb3DApply(RB::RubberbandBase *rbnd)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!rbnd)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no rubberband";
return;
}
RB::RubberbandMultiPointFeature3D *rubberband = static_cast<RB::RubberbandMultiPointFeature3D *>(rbnd);
const QVector<QPointF> &data = rubberband->coordinateVector(RB::CoordinateSystem::Map); // 3D -> RB::CoordinateSystem::Map -> degree
if (data.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed!?";
return;
}
if (data.size() < 2)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " insufficiant number of coords";
return;
}
ViewshedParameter viewshedParameter;
// degree to rad
const double factor = MathConst::pi / 180.0;
const QPointF &center = data.at(0) * factor;
viewshedParameter.viewshedOrigin_latLon = center;
{
static Esri::ArcGISRuntime::GeometryEngine *gEngine = Esri::ArcGISRuntime::GeometryEngine::instance();
const Esri::ArcGISRuntime::SpatialReference &sr = d->m_mapImpl->getEsriSpatialReference();
QPointF coord = data.at(0);
Esri::ArcGISRuntime::Point geom1(coord.x(), coord.y(), sr);
coord = data.at(1);
Esri::ArcGISRuntime::Point geom2(coord.x(), coord.y(), sr);
Esri::ArcGISRuntime::GeodeticDistanceResult geodeticDistanceResult = gEngine->distanceGeodetic(geom1, geom2,
Esri::ArcGISRuntime::LinearUnitId::Meters,
Esri::ArcGISRuntime::AngularUnitId::Degrees,
Esri::ArcGISRuntime::GeodeticCurveType::Geodesic);
viewshedParameter.outerRadius = geodeticDistanceResult.distance();
}
long id = this->create(viewshedParameter);
if (id < 0)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed!?";
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
} // namespace SituationMap
+91
View File
@@ -0,0 +1,91 @@
/**
* @RDE_HEADER@
* Interface-Implementation of Viewshed
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/viewshed.h> // Interface
#include "map_impl.h"
#include "Rubberbands/rubberband_ellipse.h"
#include "Rubberbands/rubberband_multi_point_feature_3d.h"
// Qt
#include <QPointF>
//----------------------------------------------------------------------------
namespace SituationMap
{
class ViewshedBase;
class ViewshedImpl : public /*virtual*/ Viewshed
{
using BaseClass = Viewshed;
public:
explicit ViewshedImpl(SituationMap::MapImpl *mapImpl);
virtual ~ViewshedImpl();
ViewshedImpl(const ViewshedImpl &other) = delete;
ViewshedImpl &operator=(ViewshedImpl &other) = delete;
void setViewshedUrl(const QString &viewshedServiceUrl) override;
bool isReady();
bool create() override;
bool create(double alpha, double innerRadius, double observerHeight, double targetHeight, double beamAngle) override;
long create(const ViewshedParameter &viewshedParameter) override;
bool modify(long viewshedId, double alpha, double innerRadius, double observerHeight, double targetHeight, double beamAngle) override;
void remove(long viewshedId = -1) override;
ViewshedParameter getViewshedParameter(long viewshedId = -1, bool *ok = nullptr) const override;
private Q_SLOTS:
void onRbAbort(RB::RubberbandBase *rbnd);
void onRb2DApply(RB::RubberbandBase *rbnd);
void onRb3DApply(RB::RubberbandBase *rbnd);
void onSightFanRb2DApply(RB::RubberbandBase *rbnd);
void onSightFanRb3DApply(RB::RubberbandBase *rbnd);
private:
ViewshedBase *create_viewshedGP(const ViewshedParameter &viewshedParameter);
ViewshedBase *create_viewshedAnalysis(const ViewshedParameter &viewshedParameter);
void init();
RB::RubberbandEllipse *createRubberband2D();
RB::RubberbandMultiPointFeature3D *createRubberband3D();
RB::RubberbandEllipse *createSightFanRubberband2D();
RB::RubberbandMultiPointFeature3D *createSightFanRubberband3D();
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;
}; // ViewshedImpl
} // namespace SituationMap