Initial commit
This commit is contained in:
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
|
||||
|
||||
+1205
File diff suppressed because it is too large.
Load diff
+443
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
Executable
+61
@@ -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
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"><Desc></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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
+1134
File diff suppressed because it is too large.
Load diff
+125
@@ -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
|
||||
|
||||
|
||||
+3039
File diff suppressed because it is too large.
Load diff
+324
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
Executable
+5805
File diff suppressed because it is too large.
Load diff
Executable
+520
@@ -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 ¢er, 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
@@ -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
|
||||
Executable
+94
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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> ¤tKeys = 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
@@ -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
@@ -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
@@ -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 ¢er = (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
@@ -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
@@ -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 ¢er) { m_internal->m_center = center; }
|
||||
void SightFanImpl::setCenter(const Point3D ¢er) { m_internal->m_center = center.toPointF();
|
||||
m_internal->m_centHat = center.z(); }
|
||||
|
||||
void SightFanImpl::setCenter(const QPointF ¢er, 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 ¢erMap = 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 ¢er
|
||||
, 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 ¢er
|
||||
, 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 ¢er, double centHat, double objHat)
|
||||
{
|
||||
|
||||
m_internal->m_centHat = centHat;
|
||||
m_internal->m_objHat = objHat;
|
||||
move(center);
|
||||
} // void SightFanImpl::move(ElevationMap &eMap, QPointF ¢er, 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 ¢er = 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
@@ -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 ¢er
|
||||
, double centHat
|
||||
, double objHat
|
||||
, double radius
|
||||
, double innerRadius
|
||||
, double startAngle
|
||||
, double dAngle
|
||||
, unsigned int nrFans
|
||||
, double alpha ) override;
|
||||
|
||||
void initFan( const Point3D ¢er
|
||||
, double objHat
|
||||
, double radius
|
||||
, double innerRadius
|
||||
, double startAngle
|
||||
, double dAngle
|
||||
, unsigned int nrFans
|
||||
, double alpha ) override;
|
||||
|
||||
void setCenter(const QPointF ¢er) override;
|
||||
void setCenter(const Point3D ¢er) override;
|
||||
void setCenter(const QPointF ¢er, 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 ¢er) override;
|
||||
|
||||
void move(const QPointF ¢er, 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
@@ -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 ¢er = 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
@@ -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
@@ -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
@@ -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
|
||||
Executable
+50
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
+526
@@ -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
@@ -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
|
||||
|
||||
+116
@@ -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
@@ -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 ¢erDegree = 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 ¢er = 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 ¢er = 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 ¢er = 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
@@ -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
|
||||
|
||||
Reference in new issue
Block a user