diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ArcGISQt5Hack/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ArcGISQt5Hack/CMakeLists.txt new file mode 100755 index 000000000..d856f41bb --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ArcGISQt5Hack/CMakeLists.txt @@ -0,0 +1,15 @@ +# +# @RDE_HEADER@ +# Small hack to provide the missing smybol to 'typeinfo for std::bad_array_new_length@Qt_5' +# $Id$ +# $HeadURL$ +# $Author$ +# + +IF(UNIX) + NGL_ADD_LIBRARY(WS SV AAR ArcGISQt5Hack SHARED arcgisqt5hack.cpp arcgisqt5hack.version) # SV,AAR for CES - expand polygon + NGL_TARGET_LINK_LIBRARIES(ArcGISQt5Hack PRIVATE "-Wl,--version-script,${CMAKE_CURRENT_SOURCE_DIR}/arcgisqt5hack.version") +ELSE() + NGL_ADD_LIBRARY(WS SV AAR ArcGISQt5Hack STATIC arcgisqt5hack.cpp) # SV,AAR for CES - expand polygon +ENDIF() +NGL_INSTALL_TARGET(ArcGISQt5Hack) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ArcGISQt5Hack/arcgisqt5hack.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ArcGISQt5Hack/arcgisqt5hack.cpp new file mode 100755 index 000000000..5d0c7516e --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ArcGISQt5Hack/arcgisqt5hack.cpp @@ -0,0 +1,21 @@ +/** + * @RDE_HEADER@ + * Small hack to provide the missing smybol to 'typeinfo for std::bad_array_new_length@Qt_5' + * $Id$ + * $HeadURL$ + * $Author$ + */ +#ifdef WIN32 +__declspec(dllexport) int foo42(int) { return 42;} // just a dummy to get windows build running +#else +#include + +extern "C++" +{ +__attribute__((visibility("default"))) void operator delete(void* ptr, std::size_t) noexcept +{ + ::operator delete(ptr); +} +} + +#endif diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ArcGISQt5Hack/arcgisqt5hack.version b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ArcGISQt5Hack/arcgisqt5hack.version new file mode 100755 index 000000000..069dfeeca --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ArcGISQt5Hack/arcgisqt5hack.version @@ -0,0 +1,3 @@ +Qt_5 { + *; +}; diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/elevation_map.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/elevation_map.cpp new file mode 100755 index 000000000..be3be3aed --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/elevation_map.cpp @@ -0,0 +1,165 @@ +// Stub implementation without GDAL dependency for Qt6 migration +#include "elevation_map.h" +#include "elevation_map_helper.h" + +#include +#include +#include +#include +#include + +#include + +using namespace SituationMap; + +class ElevationMap::Internal +{ +public: + Internal() = default; + ~Internal() = default; +}; + +ElevationMap::ElevationMap(const QFileInfo &fileInfo) + : m_internal(new Internal()) + , m_aproximation(true) + , m_valid(false) + , m_logger(getLogger("ElevationMap")) +{ + m_fileName = fileInfo.absoluteFilePath(); + + INFO_LOG(m_logger) << " ctor ElevationMap: map of file '" << m_fileName << "'" + << ", this=" << this; + + if (!fileInfo.exists()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " elevation data file \'" << m_fileName << "\' doesn't exists."; + return; + } + + if (!fileInfo.isReadable()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " elevation data file \'" << m_fileName << "\' isn't readable."; + return; + } + + auto cb = [this]() + { + return readFromFile(); + }; + auto cbFinished = [this]() + { + NOTICE_LOG(m_logger) << " Finished reading elevation map of file '" << m_fileName << "'" + << ", this=" << this; + }; + QObject::connect(&m_gdalReadWatcher, &QFutureWatcher::finished, + &m_gdalReadWatcher, cbFinished); + m_gdalReadWatcher.setFuture(QtConcurrent::run(cb)); +} + +ElevationMap::~ElevationMap() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + m_gdalReadWatcher.cancel(); + m_gdalReadWatcher.waitForFinished(); + + delete m_internal; + m_internal = nullptr; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +bool ElevationMap::waitForInitialization() +{ + m_gdalReadWatcher.waitForFinished(); + return m_valid; +} + +bool ElevationMap::isValid() const +{ + return m_valid; +} + +QString ElevationMap::filename() const +{ + return m_fileName; +} + +QRectF ElevationMap::extent() const +{ + return QRectF(); +} + +QPair ElevationMap::elexelSize() const +{ + return qMakePair(0.0, 0.0); +} + +double ElevationMap::elevation(double lon, double lat) const +{ + return 0.0; +} + +double ElevationMap::distance(const QPointF &start, const QPointF &end) const +{ + return 0.0; +} + +double ElevationMap::profileDistance(const QPointF &start, const QPointF &end) const +{ + return 0.0; +} + +unsigned int ElevationMap::elevationProfile(const QPointF &start, const QPointF &end, Vec3dVector &elevProfile) const +{ + elevProfile.clear(); + return 0; +} + +bool ElevationMap::contains(const QPointF &point) const +{ + return false; +} + +bool ElevationMap::intersects(const QPolygonF &polygon) const +{ + return false; +} + +void ElevationMap::LOSProfile(const QPointF &start, const QPointF &end, LOSVector &losProfile) +{ + losProfile.clear(); +} + +int ElevationMap::getElevation(int column, int row) const +{ + return 0; +} + +double ElevationMap::getElevation(double lon, double lat) const +{ + return 0.0; +} + +double ElevationMap::getOrSubstituteElevation(int column, int row, double substitute) const +{ + return substitute; +} + +void ElevationMap::getSamplePoints(const QPointF &start, const QPointF &end, Vec3dVector &sPnts) const +{ + sPnts.clear(); +} + +int ElevationMap::clipp2Map(const QPointF &start, const QPointF &end, QPointF &cStart, QPointF &cEnd) const +{ + cStart = start; + cEnd = end; + return 0; +} + +bool ElevationMap::readFromFile() +{ + // Stub implementation - GDAL removed for Qt6 migration + ERROR_LOG(m_logger) << LOGFUNCTION << " GDAL support not available in Qt6 build"; + m_valid = false; + return false; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/elevation_map.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/elevation_map.h new file mode 100755 index 000000000..fe3212be3 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/elevation_map.h @@ -0,0 +1,168 @@ +/** + * @RDE_HEADER@ + * adapting GeISiR source to SituationMap + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "elevation_map_helper.h" +#include "situationmapelevationmap_export.h" + +#include + +#include + +#include +#include +#include +#include + +class QFileInfo; +namespace SituationMap +{ + + +//------------------------------------------------------------------------------------------------------------ +// ElevationMap +//------------------------------------------------------------------------------------------------------------ + +class SITUATIONMAPELEVATIONMAP_EXPORT ElevationMap final +{ + + friend class ElevationMapManager; + +public: + ElevationMap() = delete; + ElevationMap(const ElevationMap &) = delete; + ElevationMap &operator = (const ElevationMap &) = delete; + + explicit ElevationMap(const QFileInfo &fileInfo); + + ~ElevationMap(); + + bool waitForInitialization(); + + bool isValid() const; + + QString filename() const; + + QRectF extent() const; + + QPair elexelSize() const; + + double elevation(double lon, double lat) const; // rad + + double distance(const QPointF &start, const QPointF &end) const; + + double profileDistance(const QPointF &start, const QPointF &end) const; + + unsigned int elevationProfile(const QPointF &start, const QPointF &end, Vec3dVector &elevProfile) const; + + void interpolation(bool on) { m_aproximation = on; } + bool interpolation() const { return m_aproximation; } + + bool contains(const QPointF &point) const; + bool intersects(const QPolygonF &polygon) const; + + // Line of Sight Profile: berücksichtigt Erdkrümmung und WGS84 Erdellipsoid + void LOSProfile(const QPointF &start, const QPointF &end, LOSVector &losProfile); + +protected: + + int getElevation(int column, int row) const; + double getElevation(double lon, double lat) const; + + double getOrSubstituteElevation(int column, int row, double substitute) const; + + // getSamplePoints rastert den Vector( start, end) in Lat/Lon entsprechend dem Höhenraster + void getSamplePoints(const QPointF &start, const QPointF &end, Vec3dVector &sPnts) const; + + int clipp2Map(const QPointF &start, const QPointF &end, QPointF &cStart, QPointF &cEnd) const; + + bool readFromFile(); + + struct Internal; + + Internal *m_internal; + QString m_fileName; + bool m_aproximation; + bool m_valid; + QFutureWatcher m_gdalReadWatcher; + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; // ElevationMap + + +} // SituationMap + + + +/* ----------------------------------------------------------------------------------------------- +** CVS-Id/Release ...: $Header$ +** ----------------------------------------------------------------------------------------------- +** +** Historie: +** +** $Log: not supported by cvs2svn $ +** Revision 1.20 2006/09/26 07:51:12 Jörg +** Syntax Fehler unter Suse Linux 10.1 behoben. +** +** Revision 1.19 2006/03/24 09:05:24 klaus +** Elevationprofile: Begrenzung auf Kartengröße aufgehoben ( Clipping) +** +** Revision 1.18 2005/11/09 11:12:27 klaus +** Anpassung an Linux: Kommentar hinter #endif mit "//" versehen +** +** Revision 1.17 2005/08/22 14:15:02 joerg +** typeId() +** +** Revision 1.16 2005/08/12 09:37:44 joerg +** Koordinaten-Umprojezieren bei Wechsel der Projektion +** +** Revision 1.15 2005/04/15 13:04:35 joerg +** Resource Control +** +** Revision 1.14 2005/03/04 14:29:13 klaus +** Elevationmap mit CachedTileList versehen und automatischem Entladen von Tiles +** +** Revision 1.13 2005/03/03 12:37:14 klaus +** dynamischen Laden und Entladen der ElevationTiles für Reliefmap +** +** Revision 1.12 2005/03/01 12:33:12 klaus +** Reliefmap eingeführt, aus Elevationmap entfernt +** +** Revision 1.11 2005/02/25 15:26:41 klaus +** Ergänzung um Reliefkarte +** +** Revision 1.10 2005/02/10 14:13:21 klaus +** Bias in Basisklasse MapObject überführt +** +** Revision 1.9 2005/02/10 13:51:01 klaus +** Bias für SightFans eingeführt +** +** Revision 1.8 2005/02/09 16:53:06 klaus +** 1. Version der SightFans +** +** Revision 1.7 2005/01/26 12:25:41 klaus +** todos bearbeitet, Fehlerrückmeldnugen +** +** Revision 1.6 2004/12/14 09:43:25 klaus +** 7. Erweiterung für Höhenkarten +** +** Revision 1.5 2004/12/07 11:07:20 klaus +** 5. Erweiterung für Höhenkarten +** +** Revision 1.4 2004/12/03 16:24:12 klaus +** 4. Erweiterung für Höhenkarten +** +** Revision 1.3 2004/11/30 13:15:28 klaus +** 3. Erweiterung für Höhenkarten +** +** Revision 1.2 2004/11/23 14:36:14 klaus +** 2. Erweiterung für Höhenkarten +** +** +** ----------------------------------------------------------------------------------------------- */ + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/elevation_map_helper.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/elevation_map_helper.h new file mode 100755 index 000000000..75655f15b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/elevation_map_helper.h @@ -0,0 +1,88 @@ +/** + * @RDE_HEADER@ + * Some functions and struct for ElevationMap/LineOfSight (LOS) + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include + +#include +#include + + +#include +#include + +namespace SituationMap +{ + +inline bool isInIntervall(double x, double x1, double x2) +{ + return (((x > x1) && (x < x2)) || (((x > x2) && (x < x1)))); +} // inline bool isInIntervall(double x, double x1, double x2) + + +inline double radiusWGS84(double lat) +{ + double sinLAT = sin(lat); + return (MathConst::WGS84_Major_a * sqrt(1.0 - MathConst::WGS84_SQR_EXC * sinLAT * sinLAT)); +} // inline double radiusWGS84(double lat) + + +inline QPointF distance_in_meter_WGS84(const QPointF &start, const QPointF &end) // start, end in LonLat +{ + // todo: prüfen ob Distanz zu groß, dann verbesserte Berechnung !! + double lat = (end.y() + start.y()) / 2.0; + double faktorLon = MathConst::WGS84_Major_a * cos(lat); // radiusWGS84(lat) * 2.0 * PI; + double faktorLat = MathConst::WGS84_Minor_b; + + return QPointF(faktorLon * (end.x() - start.x()), faktorLat * (end.y() - start.y())); + +} // QPointF distance_in_meter_WGS84(const QPointF &start, const QPointF &end) + + +inline double squareLength(const QPointF &v) +{ + return v.x() * v.x() + v.y() * v.y(); +} // inline double squareLength(const QPoint & v) + + +inline double length(const QPointF &v) +{ + return sqrt(squareLength(v)); +} // inline double length(const QPoint & v) + + +struct LOSentry // Defines 1 Entry for Line of Sight +{ + LOSentry() = default; + LOSentry(double dist, double height) : _dist(dist), _height(height) { ; } + + double &dist() { return _dist; } + double &height() { return _height; } + + double dist() const { return _dist; } + double height() const { return _height; } + + bool isUndefined() const { return std::isnan(_height); } + +private: + + double _dist = 0; // Distance from Start + double _height = -NAN; + +}; /* LOSentry */ + + +using Point3D = LtsUtils::QPoint3D; ///< x = lon ; y = lat , z = elevation/altitude in meters +using LOSVector = QVector; +using Vec2dVector = QVector; +using Vec3dVector = QVector; + + +} // SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/elevation_map_manager.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/elevation_map_manager.cpp new file mode 100755 index 000000000..0394f5c1e --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/elevation_map_manager.cpp @@ -0,0 +1,531 @@ +/** + * @RDE_HEADER@ + * Manage and combine several ElevationMap + * $Id$ + * $HeadURL$ + * $Author$ + */ + + +#include "elevation_map_manager.h" +#include "elevation_map.h" + +// Qt +#include +#include +#include +#include + +// system +#include + +namespace SituationMap +{ + +static long s_counter{0}; + +//------------------------------------------------------------------------------------------------------------ +// ElevationMapManager::Private +//------------------------------------------------------------------------------------------------------------ + +class ElevationMapManager::Private +{ +public: + // fine to rough + std::vector m_elevationMapCont; + QRectF m_simpleExtent; +}; + + +//------------------------------------------------------------------------------------------------------------ +// ElevationMapManager +//------------------------------------------------------------------------------------------------------------ +ElevationMapManager::ElevationMapManager() + : BaseClass() + , d(new Private()) + , m_logger(getLogger("ElevationMapManager")) +{ + ++s_counter; + TRACE_LOG(m_logger) << LOGFUNCTION << " START, s_counter=" << s_counter << " this=" << this; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, s_counter=" << s_counter << " this=" << this; +} + +ElevationMapManager::~ElevationMapManager() +{ + --s_counter; + TRACE_LOG(m_logger) << LOGFUNCTION << " START, s_counter=" << s_counter << " this=" << this; + cleanupCurrentContent(); + delete d; + d = nullptr; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, s_counter=" << s_counter << " this=" << this; +} +int ElevationMapManager::init(const QStringList &filePaths_rough_to_fine) +{ + NOTICE_LOG(m_logger) << LOGFUNCTION << " START" << " filePaths_rough_to_fine=" << filePaths_rough_to_fine.join(QStringLiteral(" ; ")); + + cleanupCurrentContent(); + + d->m_elevationMapCont.reserve(filePaths_rough_to_fine.size()); + int cnt = 0; + for (const QString &filePath : filePaths_rough_to_fine) + { + QFileInfo fileInfo(filePath); + + const QString &fileName = fileInfo.absoluteFilePath(); + + if (!fileInfo.exists()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " elevation data file \'" << fileName << "\' doesn't exists."; + continue; + } + + if (!fileInfo.isReadable()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " elevation data file \'" << fileName << "\' isn't readable."; + continue; + } + + // ctor will spawn thread via QtConcurrent and read file async: + d->m_elevationMapCont.insert(d->m_elevationMapCont.begin(), new ElevationMap(fileInfo)); + ++cnt; + } // for + + NOTICE_LOG(m_logger) << LOGFUNCTION << " END" << " filePaths_rough_to_fine=" << filePaths_rough_to_fine.join(QStringLiteral(" ; ")) + << ", following number of files are going to be loaded: " << cnt; + return cnt; +} + +void ElevationMapManager::cleanupCurrentContent() +{ + qDeleteAll(d->m_elevationMapCont); + d->m_elevationMapCont.clear(); +} + + + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + + + +bool ElevationMapManager::waitForInitialization() +{ + for (ElevationMap *elevationMap : d->m_elevationMapCont) + elevationMap->waitForInitialization(); + return isValid(); +} + +bool ElevationMapManager::isValid() const +{ + bool allAreValid = true; + for (const ElevationMap *elevationMap : d->m_elevationMapCont) + allAreValid = allAreValid && elevationMap->isValid(); + +// FATAL_LOG(m_logger) << LOGFUNCTION << " allAreValid = " << allAreValid; + return allAreValid; +} + +double ElevationMapManager::elevation(double lon_rad, double lat_rad, bool *bIsCovered) const +{ + // assume, that all children are ready + for (const ElevationMap *elevationMap : d->m_elevationMapCont) + { +// qCritical() << " EMM::elevation: " +// << " lon_rad=" << lon_rad +// << " lat_rad=" << lat_rad +// << " (try: " << elevationMap->filename() << ")..."; + + double singleResult = elevationMap->elevation(lon_rad, lat_rad); + + if (singleResult > -300) // -300 -> ausserhalb der Höhenkarte !! + { + // qCritical() << " EMM::elevation: elevation= " << singleResult << " (taken from: " << elevationMap->filename() << ")"; + if (bIsCovered) + *bIsCovered = true; + return singleResult; + } + } + +// DEBUG_LOG(m_logger) << LOGFUNCTION << " invalid= " +// << " lon_rad=" << lon_rad +// << " lat_rad=" << lat_rad; + if (bIsCovered) + *bIsCovered = false; + return std::numeric_limits::min(); +} + +double ElevationMapManager::elevation(double lon_rad, double lat_rad, double defaultValue, bool *bIsCovered) const +{ + bool bIsCoveredLocal = false; + const double elevationValue = elevation(lon_rad, lat_rad, &bIsCoveredLocal); + + // in any case: if provided, transfer isCovered back to caller + if (bIsCovered) + *bIsCovered = bIsCoveredLocal; + + // which value to be returned? + if (!bIsCoveredLocal) + return defaultValue; + + return elevationValue; +} + + +QRectF ElevationMapManager::simpleUnitedExtent() const +{ + if (d->m_simpleExtent.isNull()) + { + bool bFirst = true; + for (const ElevationMap *elevationMap : d->m_elevationMapCont) + { + const QRectF &singleExtent = elevationMap->extent(); + if (bFirst) + { + bFirst = false; + d->m_simpleExtent = singleExtent; +// qCritical() << " bFirst.left " << singleExtent.left() << " bFirst.right " << singleExtent.right(); +// qCritical() << " bFirst.top " << singleExtent.top() << " bFirst.bottom " << singleExtent.bottom(); + + } else { + // prefer following instead QRectF::united (top vs. bottom): +// qCritical() << " next.left " << singleExtent.left() << " secnextright " << singleExtent.right(); +// qCritical() << " next.top " << singleExtent.top() << " next.bottom " << singleExtent.bottom(); + d->m_simpleExtent.setLeft (std::min(d->m_simpleExtent.left(), singleExtent.left())); + d->m_simpleExtent.setTop (std::max(d->m_simpleExtent.top(), singleExtent.top())); // top - >use max !!! + d->m_simpleExtent.setBottom(std::min(d->m_simpleExtent.bottom(), singleExtent.bottom())); // bottom -> use min + d->m_simpleExtent.setRight (std::max(d->m_simpleExtent.right(), singleExtent.right())); + } + } // for + } + + return d->m_simpleExtent; +} + +bool ElevationMapManager::contains(const QPointF &point) const +{ + for (const ElevationMap *elevationMap : d->m_elevationMapCont) + { + if (elevationMap->contains(point)) + return true; + } + return false; +} + +void ElevationMapManager::LOSProfile(const QPointF &start, const QPointF &end, LOSVector &losProfile) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (d->m_elevationMapCont.empty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no elevationMap"; + return; + } + + waitForInitialization(); + + QRectF simpleExtentLocal = simpleUnitedExtent(); + +// qCritical() << " simpleExtent.left " << simpleExtentLocal.left() << " simpleExtent.right " << simpleExtentLocal.right(); +// qCritical() << " simpleExtent.top " << simpleExtentLocal.top() << " simpleExtent.bottom " << simpleExtentLocal.bottom(); + + if (!simpleExtentLocal.contains(start)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " start coordinate not within area" + << ", start: x=" << start.x() << " ; y=" << start.y() + << ", area: " + << " left " << simpleExtentLocal.left() << ", right " << simpleExtentLocal.right() + << " top " << simpleExtentLocal.top() << ", bottom " << simpleExtentLocal.bottom() + ; + return; // ausserhalb der Höhenkarte !! + } + + bool clipped = false; + double totalLng; + QPointF startG = start; + QPointF endG = end; + + if (!simpleExtentLocal.contains(end)) // Clippen + { + clipped = true; + totalLng = length(endG - startG); + if (endG.x() < simpleExtentLocal.left()) + { + double m = (endG.y() - startG.y()) / (endG.x() - startG.x()); + double x = simpleExtentLocal.left(); + endG.setX(x); + endG.setY(startG.y() + m * (x - startG.x())); + } + else if (endG.x() > simpleExtentLocal.right()) + { + double m = (endG.y() - startG.y()) / (endG.x() - startG.x()); + double x = simpleExtentLocal.right(); + endG.setX(x); + endG.setY(startG.y() + m * (x - startG.x())); + } + if (endG.y() < simpleExtentLocal.bottom()) + { + double m = (endG.x() - startG.x()) / (endG.y() - startG.y()); + double y = simpleExtentLocal.bottom(); + endG.setY(y); + endG.setX(startG.x() + m * (y - startG.y())); + } + else if (endG.y() > simpleExtentLocal.top()) + { + double m = (endG.x() - startG.x()) / (endG.y() - startG.y()); + double y = simpleExtentLocal.top(); + endG.setY(y); + endG.setX(startG.x() + m * (y - startG.y())); + } + } + + + // Korrektursteigung aufgrund WGS84 Erdellipsoid, Lineare Approximation, Fehler bis 200 km: < 1.0m + double startRadius = radiusWGS84(startG.y()); + double corrM = (radiusWGS84(endG.y()) - startRadius) / length(endG - startG); + + // + // getSamplePoints(start, end, elevProfile); + // find best elevationMap for equested path {startG, endG} + // + + // 1st find maps covering the desired pathG: + const QPolygonF pathG( QVector{startG, endG} ); + + std::vector coveredElevationMaps; + coveredElevationMaps.reserve(d->m_elevationMapCont.size()); + for (auto curItem : std::as_const(d->m_elevationMapCont)) + { + if (curItem && curItem->intersects(pathG)) + coveredElevationMaps.push_back(curItem); + } // for + + if (m_logger.isDebugEnabled()) + { + DEBUG_LOG(m_logger) << " coveredElevationMap.size() = " << coveredElevationMaps.size(); + for (auto curItem : std::as_const(coveredElevationMaps)) + DEBUG_LOG(m_logger) << " coveredElevationMap = " << curItem->filename(); + } + + // if no maps covers the desired path, stay on old approach: use most bottom elevation map: + if (coveredElevationMaps.empty()) + coveredElevationMaps = d->m_elevationMapCont; + + // 2nd: find elevation map with highest resolution to achieve best results: + using ELE_VEC_IT = std::vector::iterator; + ELE_VEC_IT eleIt = coveredElevationMaps.begin(); + for (ELE_VEC_IT curItem = coveredElevationMaps.begin()+1; curItem != coveredElevationMaps.end(); ++curItem) + { + ElevationMap *elePtr = *curItem; + if (!elePtr) + continue; + + if (elePtr->elexelSize().first < (*eleIt)->elexelSize().first) + eleIt = curItem; + } // for + + Q_ASSERT_X((*eleIt), "ElevationMapManager::LOSProfile", "eleIt failed"); + + Vec3dVector elevProfile; + (*eleIt)->getSamplePoints(start, end, elevProfile); + + INFO_LOG(m_logger) << "Using ElevationMap '" << (*eleIt)->filename() << "'" + << " (of size = " << d->m_elevationMapCont.size() << ")" + << " with_elexelSize " << (*eleIt)->elexelSize().first + << " -> # SamplePoints = " << elevProfile.size(); + + for (Vec3dVector::iterator itr = elevProfile.begin(); itr != elevProfile.end(); ++itr) + { + double dist = length(distance_in_meter_WGS84(start, itr->toPointF())); + double height = elevation(itr->x(), itr->y(), nullptr); // use elevation over all seperate elevationMaps + height += corrM * dist; // WGS84 Erdellipsoid Korrektur + height -= (startRadius * (1 - cos(atan(dist / startRadius)))); // Einbeziehen der Erdkruemmung + // alternativ als Naeherung: height -= ( dist * dist) / (startRadius * 2.0); + losProfile.push_back(LOSentry(dist, height)); + } + + if (clipped) + losProfile.push_back(LOSentry(totalLng, -NAN)); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +ElevationMapManager::LineOfSight ElevationMapManager::lineOfSightCalculation(const LOSVector &losProfile, + double centerHat, + double objHat) const +{ + // Analog zu SightFanImpl::calculateSightBeam + + + if (losProfile.size() < 2) // Startpunkt undefiniert oder ausserhalb der Karte + { + return LineOfSight::LOS_ERROR; + } + + LOSVector::const_iterator itr = losProfile.begin(); + const double centerHeight = itr->height() + centerHat; + + 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; + + d1 = itr->dist(); + double curM = (height1 - centerHeight ) / d1; // bestimmende Steigung + +// sBeam.add( SightEntry( d1, START) ); + + while (++itr != losProfile.end()) + { + double d2 = itr->dist(); // *EarthRadius; + double height2 = itr->height(); + + if (std::isnan(height2)) // Undefined + { + return LineOfSight::LOS_UNDEFINED; + } + + double hObj = (height2 + objHat - centerHeight ); + double objM = hObj / d2; // Steigung bezogen auf Objekte( Objekte verdecken nicht !) + + LineOfSight newMode = ((objM >= curM)) ? LineOfSight::LOS_VISIBLE : LineOfSight::LOS_INVISIBLE; + + if (newMode == LineOfSight::LOS_INVISIBLE) + return LineOfSight::LOS_INVISIBLE; + + +// if (newMode != mode) +// { +// if ( (mode != LineOfSight::LOS_UNDEFINED) && (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 + + return LineOfSight::LOS_VISIBLE; +} + +ElevationMapManager::LineOfSight ElevationMapManager::lineOfSightCalculation(const LtsUtils::QPoint3D &startPointGeo_absZ, + const LtsUtils::QPoint3D &endPointGeo_absZ) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << ", startPointGeo_absZ = " << startPointGeo_absZ << ", endPointGeo_absZ = " << endPointGeo_absZ; + + if (!isValid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Error, LtsElevationManager not valid"; + return LineOfSight::LOS_ERROR; + } + + // 1st: a) centerHat / startHat: + const double startHat = startPointGeo_absZ.z() - this->elevation(startPointGeo_absZ.x(), startPointGeo_absZ.y(), -10000); + if (startHat < 0) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Error, startHat is negativ"; + return LineOfSight::LOS_ERROR; + } + // 1st: b) endHat / objHat: + const double endHat = endPointGeo_absZ.z() - this->elevation(endPointGeo_absZ.x(), endPointGeo_absZ.y(), -10000); + if (endHat < 0) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Error, objHat is negativ" + << ", startPointGeo_absZ=" << startPointGeo_absZ + << ", endPointGeo_absZ=" << endPointGeo_absZ; + return LineOfSight::LOS_ERROR; + } + + SituationMap::ElevationMapManager::LineOfSight eleMapMgrResult = lineOfSightCalculation(startPointGeo_absZ.toPointF(), startHat, + endPointGeo_absZ.toPointF(), endHat); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" << ", startPointGeo = " << startPointGeo_absZ << ", endPointGeo = " << endPointGeo_absZ + << " -> LineOfSight " << static_cast(eleMapMgrResult); + return eleMapMgrResult; +} + +ElevationMapManager::LineOfSight ElevationMapManager::lineOfSightCalculation(const QPointF &startPointGeo, double startHat, + const QPointF &endPointGeo, double endHat) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << ", startPointGeo = " << startPointGeo << ", startHat = " << startHat + << ", endPointGeo = " << endPointGeo << ", endHat = " << endHat ; + if (!isValid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Error, LtsElevationManager not valid"; + return LineOfSight::LOS_ERROR; + } + + if (startHat < 0) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Error, startHat is negativ"; + return LineOfSight::LOS_ERROR; + } + if (endHat < 0) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Error, endHat is negativ"; + return LineOfSight::LOS_ERROR; + } + + // 1st: create LOSVector: + SituationMap::LOSVector losProfile; + LOSProfile(startPointGeo, endPointGeo, losProfile); + if (losProfile.size() < 2) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Error, losProfile not valid" + << ", losProfile.size = " << losProfile.size(); + return LineOfSight::LOS_ERROR; + } + + // 2nd: lineOfSightCalculation + SituationMap::ElevationMapManager::LineOfSight eleMapMgrResult = this->lineOfSightCalculation(losProfile, startHat, endHat); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << ", startPointGeo = " << startPointGeo << ", startHat = " << startHat + << ", endPointGeo = " << endPointGeo << ", endPointHat = " << endHat + << " -> LineOfSight " << static_cast(eleMapMgrResult); + return eleMapMgrResult; +} + + + + +} // SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/elevation_map_manager.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/elevation_map_manager.h new file mode 100755 index 000000000..b77ddf77c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/elevation_map_manager.h @@ -0,0 +1,84 @@ +/** + * @RDE_HEADER@ + * Manage and combine several ElevationMap + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "elevation_map_helper.h" +#include "situationmapelevationmap_export.h" +#include + +#include + +namespace SituationMap +{ + +// forward: +class ElevationMap; + +//------------------------------------------------------------------------------------------------------------ +// ElevationMapManager +//------------------------------------------------------------------------------------------------------------ +class SITUATIONMAPELEVATIONMAP_EXPORT ElevationMapManager final : public QObject +{ + using BaseClass = QObject; + +public: + ElevationMapManager(); + ElevationMapManager(const ElevationMapManager &) = delete; + ElevationMapManager &operator = (const ElevationMapManager &) = delete; + ~ElevationMapManager(); + + int init(const QStringList &filePaths_rough_to_fine); + void cleanupCurrentContent(); + + bool waitForInitialization(); + bool isValid() const; + + + double elevation(double lon_rad, double lat_rad, bool *bIsCovered = nullptr) const; + double elevation(double lon_rad, double lat_rad, double defaultValue, bool *bIsCovered = nullptr) const; + + // united extent of seperate files: + QRectF simpleUnitedExtent() const; + + bool contains(const QPointF &point) const; + + // Line of Sight Profile: berücksichtigt Erdkrümmung und WGS84 Erdellipsoid + void LOSProfile(const QPointF &start, const QPointF &end, LOSVector &losProfile); + + /*! \name Line of Sight */ + //@{ + enum class LineOfSight { LOS_UNDEFINED, + LOS_ERROR, + LOS_VISIBLE, + LOS_INVISIBLE }; + + LineOfSight lineOfSightCalculation(const LOSVector &losProfile, + double centerHat, + double objHat) const; + + LineOfSight lineOfSightCalculation(const LtsUtils::QPoint3D &startPointGeo_absZ, + const LtsUtils::QPoint3D &endPointGeo_absZ); + + LineOfSight lineOfSightCalculation(const QPointF &startPointGeo, double startHat, + const QPointF &endPointGeo, double endHat); + + //@} + +protected: + + class Private; + + Private *d; + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; // ElevationMapManager + + +} // SituationMap + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/line_of_sight_calculation.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/line_of_sight_calculation.cpp new file mode 100755 index 000000000..65a6a5805 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/line_of_sight_calculation.cpp @@ -0,0 +1,229 @@ +/** + * @RDE_HEADER@ + * LineOfSightCalculation (L400) + * $Id$ + * $HeadURL$ + * $Author$ + * Created by N. Thoden, NLE, 06.04.2020 + */ + +#include "line_of_sight_calculation.h" +#include "elevation_map.h" + +#include + +// +// ESRI / ARCGIS +// if you link libEsriCommonQt, you propably have to add ArcGISQt5Hack AND: # make sure it's loaded *before* libEsriCommonQt.so +// this may effect all executables linking situationMapElevationMap. CHECK! +// + + +// Qt +#include +#include + + +// some helper functions +inline Q_CONSTEXPR double pi() { return M_PI; } +inline Q_CONSTEXPR double deg2rad() { return pi() / 180.; } +inline Q_CONSTEXPR double rad2deg() { return 1. / deg2rad(); } + + +namespace SituationMap +{ + +LineOfSightCalculation::LineOfSightCalculation(ElevationMap *elevationMap) + : m_logger(getLogger("LineOfSightCalculation")) + , m_elevationMap(elevationMap) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +LineOfSightCalculation::~LineOfSightCalculation() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +bool LineOfSightCalculation::calculate(ObserverTargetData &observerTarget, unsigned int sampleIntervallMeter) +{ + // same checks: + if (!m_elevationMap || !m_elevationMap->isValid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no elevationMap configured or not valid"; + observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Undefined); + return false; + } + + if (!observerTarget.isObserverAndTargetValid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " observer and/or target not valid"; + observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Undefined); + return false; + } + + if (observerTarget.observer().toPointF() == observerTarget.target().toPointF()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " observer and target are identical regarding x and y"; + observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Undefined); + return false; + } + + bool success = true; + + QElapsedTimer timer; + if (m_logger.isInfoEnabled()) + timer.start(); + + // + // + // consider using ElevationMap::LOSProfile + // + // + + // determine how many iterations we need: + double distanceInMeter = distanceMeter(observerTarget.observer(), + observerTarget.target()); + //ERROR_LOG(m_logger) << LOGFUNCTION << " distanceMeter = " << distanceInMeter; + + int maxNumberOfIterations = distanceInMeter / sampleIntervallMeter; // check visibility every ~5m + // maxNumberOfIterations = 1000000; + + if (maxNumberOfIterations<=0) + maxNumberOfIterations = 5; + + + double distanceFromObserverToTargetAsFraction = 0; + int i = 0; + while (idirectLineElevation) -> obstructed/break + */ + + distanceFromObserverToTargetAsFraction = (double)i/maxNumberOfIterations; + + + const Point3D &pCandidate = movePoint(observerTarget.observer(), + observerTarget.target(), + distanceFromObserverToTargetAsFraction); + + // { + // static Point3D oldCandidate; + // double distanceInMeter = distanceMeter(pCandidate, oldCandidate); + // ERROR_LOG(m_logger) << LOGFUNCTION << " distanceMeter = " << distanceInMeter; + // oldCandidate = pCandidate; + // } + + + // z is the elevation of a *direct* line: + const double directLineElevation = pCandidate.z(); + const double elevationMapElevation = m_elevationMap->elevation(pCandidate.x(), pCandidate.y()); + + // Coord covered by elevationMap? + if (elevationMapElevation <= std::numeric_limits::min()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Error, coordinate not covered be elevationData. Finish this LineOfSightCalculation." + << " i=" << i + << ", pCandidate = " << pCandidate; + + // write back data + observerTarget.setObstacle(pCandidate); + observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Undefined); + + // finish the loop: + success = false; + break; + } + + // analyze / check visibility: + if (elevationMapElevation>directLineElevation) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " i=" << i + << " break - pCandidate = " << pCandidate + << " -> directLineElevation = " << directLineElevation + << " -> elevationMapElevation = " << elevationMapElevation; + + // write result + observerTarget.setObstacle(pCandidate); + observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Obstructed); + + // finish the loop: + break; + } else { + observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Visible); + } + + } // while + + + if (m_logger.isInfoEnabled()) + { + INFO_LOG(m_logger) << LOGFUNCTION << " END" + << ", success = " << success + << ", " << i << " of "<< maxNumberOfIterations << " iterations" + << ", distanceFromObserverToTargetAsFraction = " << distanceFromObserverToTargetAsFraction + << ", pCandidate=" << observerTarget.obstacle() + << ", lineOfSightVisibility=" << static_cast(observerTarget.lineOfSightVisibility()) + << ", took [ms] " << timer.elapsed(); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" << ", success = " << success; + return success; +} + +Point3D LineOfSightCalculation::movePoint(const Point3D &oberserver, const Point3D &target, double distanceFromObserverToTargetAsFraction) // 0.0 ... 1.0 +{ + // positions are lon/lat @ rad. + // for evaluation we do that very simple: + + // strange, but: using Point3D seems to be faster then using QPointF + const Point3D &dist = target - oberserver; + const Point3D &point = oberserver + (dist * distanceFromObserverToTargetAsFraction); + + return point; +} + +double LineOfSightCalculation::distanceMeter(const Point3D &oberserver, const Point3D &target) +{ + // Try to avoid the dependency to ESRI (keep ArcGISQt5Hack in mind) + // And good accuracy is not required here + // use LtsCoordTrans::distance + +// Esri::ArcGISRuntime::Point geom1(oberserver.x()*rad2deg(), oberserver.y()*rad2deg(), oberserver.z(), Esri::ArcGISRuntime::SpatialReference::wgs84()); +// Esri::ArcGISRuntime::Point geom2(target.x() *rad2deg(), target.y() *rad2deg(), target.z() , Esri::ArcGISRuntime::SpatialReference::wgs84()); + +// Esri::ArcGISRuntime::GeometryEngine *gEngine = Esri::ArcGISRuntime::GeometryEngine::instance(); +// Esri::ArcGISRuntime::GeodeticDistanceResult geodeticDistanceResult = gEngine->distanceGeodetic(geom1, geom2, +// Esri::ArcGISRuntime::LinearUnitId::Meters, +// Esri::ArcGISRuntime::AngularUnitId::Degrees, +// Esri::ArcGISRuntime::GeodeticCurveType::Geodesic); +// double distance = geodeticDistanceResult.distance(); + + + double distance = LtsCoordTrans::distance(oberserver.toPointF(), target.toPointF()); + + return distance; +} + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/line_of_sight_calculation.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/line_of_sight_calculation.h new file mode 100755 index 000000000..75f83426e --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/line_of_sight_calculation.h @@ -0,0 +1,92 @@ +/** + * @RDE_HEADER@ + * LineOfSightCalculation (L400) + * $Id$ + * $HeadURL$ + * $Author$ + * Created by N. Thoden, NLE, 06.04.2020 + */ + +#pragma once + +#include +#include + +#include +using Point3D = LtsUtils::QPoint3D; ///< x = lon ; y = lat , z = elevation/altitude in meters + + +namespace SituationMap +{ + +// forward +class ElevationMap; + +//---------------------------------------------------------------------------- +/*! + \brief Define visibility. + */ +enum class LineOfSightVisibility +{ + Undefined, /*!< no data at that location? */ + Visible, + Obstructed +}; +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- +class SITUATIONMAPELEVATIONMAP_EXPORT ObserverTargetData +{ +public: + explicit ObserverTargetData(const Point3D &oberserver = Point3D(), // z-value: incl. observerHeight + const Point3D &target = Point3D()) // z-value: incl. targetHeight + : m_oberserver(oberserver) + , m_target(target) + {} + + const Point3D &observer() const { return m_oberserver; } + const Point3D &target() const { return m_target; } + + const Point3D &obstacle() const { return m_obstacle; } + void setObstacle(const Point3D &obstacle) { m_obstacle = obstacle; } + + LineOfSightVisibility lineOfSightVisibility() const { return m_visible; } + void setLineOfSightVisibility(const LineOfSightVisibility &visible) { m_visible = visible; } + + bool isObserverAndTargetValid() const { return (!m_oberserver.isNull()) && (!m_target.isNull()); } + +private: + Point3D m_oberserver; + Point3D m_target; + + Point3D m_obstacle; + LineOfSightVisibility m_visible = LineOfSightVisibility::Undefined; +}; +//---------------------------------------------------------------------------- + +class SITUATIONMAPELEVATIONMAP_EXPORT LineOfSightCalculation +{ +public: + explicit LineOfSightCalculation(ElevationMap *elevationMap); + ~LineOfSightCalculation(); + + bool calculate(ObserverTargetData &observerTarget, unsigned int sampleIntervallMeter = 5); + +protected: + static Point3D movePoint(const Point3D &oberserver, + const Point3D &target, + double distanceFromObserverToTargetAsFraction); + + static double distanceMeter(const Point3D &oberserver, + const Point3D &target); + + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility + ElevationMap *m_elevationMap = nullptr; + + ObserverTargetData m_observerTarget; +}; + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/situationmapelevationmap_export.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/situationmapelevationmap_export.h new file mode 100644 index 000000000..2f350131e --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ElevationMap/situationmapelevationmap_export.h @@ -0,0 +1,21 @@ +/** + * Export definitions + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include + +#ifndef SITUATIONMAPELEVATIONMAP_EXPORT +# ifdef BUILD_STATIC_LIBRARIES +# define SITUATIONMAPELEVATIONMAP_EXPORT +# else +# if defined(MAKE_SITUATIONMAPELEVATIONMAP_LIB) +# define SITUATIONMAPELEVATIONMAP_EXPORT Q_DECL_EXPORT +# else +# define SITUATIONMAPELEVATIONMAP_EXPORT Q_DECL_IMPORT +# endif +# endif +#endif \ No newline at end of file diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/CMakeLists.txt new file mode 100755 index 000000000..d7609782d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/CMakeLists.txt @@ -0,0 +1,9 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +# ADD_SUBDIRECTORY(Viewshed3D) + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/Viewshed3D/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/Viewshed3D/CMakeLists.txt new file mode 100755 index 000000000..397d49bd4 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/Viewshed3D/CMakeLists.txt @@ -0,0 +1,43 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +# Evaluation application for 3D viewshed + +FIND_PACKAGE(ArcGIS REQUIRED) + +INCLUDE_DIRECTORIES(${ARCGIS_INCLUDE_DIR}/arcgis) + +SET(EXE_SRCS + main.cpp + ViewshedLocation.cpp +) + +SET(EXE_HDRS + ViewshedLocation.h +) + +SOURCE_GROUP(EvalViewshed3DApp FILES ${EXE_SRCS} ${EXE_HDRS}) + +NGL_ADD_EXECUTABLE(WS NO_TRANSLATION EvalViewshed3DApp ${EXE_SRCS} ${EXE_HDRS}) + +NGL_TARGET_LINK_LIBRARIES( + EvalViewshed3DApp + ArcGIS::ArcGIS + Qt6::Core + Qt6::Gui + Qt6::Widgets + Qt6::Network +) + + +IF(MSVC) + # Configure the template file + SET(VS_USER_FILE EvalViewshed3DApp.vcxproj.user) + configure_file( ../../SituationMap.vcxproj.user.in ${CMAKE_CURRENT_BINARY_DIR}/${VS_USER_FILE} @ONLY) +ENDIF(MSVC) + +NGL_INSTALL_TARGET(EvalViewshed3DApp) + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/Viewshed3D/LocationViewshed.pro b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/Viewshed3D/LocationViewshed.pro new file mode 100755 index 000000000..374b1a7f8 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/Viewshed3D/LocationViewshed.pro @@ -0,0 +1,58 @@ +#------------------------------------------------- +# Copyright 2015 Esri. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------- + +mac { + cache() +} + +CONFIG += c++11 esri_runtime_qt100_0_0 debug + +QT += core gui opengl xml network positioning sensors + +win32:CONFIG += \ + embed_manifest_exe + +TARGET = LocationViewshed +TEMPLATE = app + +SOURCES += \ + main.cpp \ + ViewshedLocation.cpp + +HEADERS += \ + ViewshedLocation.h + + +win32 { + INCLUDEPATH += "C:/Program Files (x86)/ArcGIS SDKs/Qt100.0/sdk/include" + debug:LIBPATH += "C:/Program Files (x86)/ArcGIS SDKs/Qt100.0/sdk/windows/x64/lib/Debug" + release:LIBPATH += "C:/Program Files (x86)/ArcGIS SDKs/Qt100.0/sdk/windows/x64/lib/Release" + debug:LIBS += -lEsriCommonQtd + release:LIBS += -lEsriCommonQt + debug:LIBS += -lEsriRuntimeQtd + release:LIBS += -lEsriRuntimeQt +} + +linux { + INCLUDEPATH += /usr/include/arcgis + LIBS += -lEsriCommonQt + LIBS += -lEsriRuntimeQt + LIBS += -lc++ +} + + +macx { + QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.11 +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/Viewshed3D/ViewshedLocation.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/Viewshed3D/ViewshedLocation.cpp new file mode 100755 index 000000000..8f0758190 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/Viewshed3D/ViewshedLocation.cpp @@ -0,0 +1,274 @@ +/** + * @RDE_HEADER@ + * Declaration/Definition + * $Id$ + * $HeadURL$ + * $Author$ + */ +// [WriteFile Name=ViewshedLocation, Category=Analysis] +// [Legal] +// Copyright 2017 Esri. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// [Legal] + +#include "ViewshedLocation.h" + +#include "ArcGISTiledElevationSource.h" +#include "Scene.h" +#include "SceneGraphicsView.h" +#include "LocationViewshed.h" +#include "Camera.h" +#include "Point.h" +#include "AnalysisOverlay.h" + +#include "QColor" + + +using namespace Esri::ArcGISRuntime; + +ViewshedLocation::ViewshedLocation(QObject* parent /* = nullptr */): + QObject(parent) +{ +} + +void ViewshedLocation::init(Esri::ArcGISRuntime::SceneGraphicsView* sceneView) +{ + m_sceneView = sceneView; + + // Register classes for QML +// qmlRegisterType("Esri.Samples", 1, 0, "SceneView"); +// qmlRegisterType("Esri.Samples", 1, 0, "ViewshedLocationSample"); + + + Surface* surface = new Surface(this); + surface->elevationSources()->append( + new ArcGISTiledElevationSource( + QUrl(QStringLiteral("http://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer")), + this)); + m_sceneView->arcGISScene()->setBaseSurface(surface); + + // set initial viewpoint + setInitialViewpoint(); + + // connect signals + connectSignals(); +} + + +void ViewshedLocation::setInitialViewpoint() +{ + // Set a viewpoint + double x = 6.86088; + double y = 45.3604; + double z = 3582.55; + Point point(x, y, z, SpatialReference(4326)); + double heading = 345; + double pitch = 70; + double roll = 0; + Camera camera(point, heading, pitch, roll); + m_sceneView->setViewpointCamera(camera); +} + +void ViewshedLocation::connectSignals() +{ + // on mouse click perform the location viewshed + connect(m_sceneView, &SceneGraphicsView::mouseClicked, this, [this](QMouseEvent& event) + { + if (!m_locationViewshed[0]) + createViewshed(event.x(), event.y()); + else + { + for (int i = 0; i < 3; ++i) + { + const Point pt = m_sceneView->screenToBaseSurface(event.x(), event.y()); + m_locationViewshed[i]->setLocation(pt); + } + } + }); + + connect(m_sceneView, &SceneGraphicsView::mousePressedAndHeld, this, [this](QMouseEvent& event) + { + if (!m_locationViewshed[0]) + createViewshed(event.x(), event.y()); + + m_calculating = true; + }); + + connect(m_sceneView, &SceneGraphicsView::mouseMoved, this, [this](QMouseEvent& event) + { + if (m_calculating) + { + for (int i = 0; i < 3; ++i) + { + const Point pt = m_sceneView->screenToBaseSurface(event.x(), event.y()); + m_locationViewshed[i]->setLocation(pt); + } + } + }); + + connect(m_sceneView, &SceneGraphicsView::mouseReleased, this, [this](QMouseEvent) + { + m_calculating = false; + }); +} + +void ViewshedLocation::createViewshed(double x, double y) +{ + + m_analysisOverlay = new AnalysisOverlay(this); + m_sceneView->analysisOverlays()->append(m_analysisOverlay); + + + const Point pt = m_sceneView->screenToBaseSurface(x, y); + + qCritical() << " pt="; + qCritical() << " pt=" << pt.x(); + qCritical() << " pt=" << pt.y(); + qCritical() << " pt=" << pt.z(); + + for (int i = 0; i < 3; ++i) + { + double heading = i * 120.0; + m_locationViewshed[i] = new LocationViewshed(pt + , heading + , 90.0 // pitch + , 120.0 // horizontalAngle + , 90.0 // verticalAngle + , 10.0 // minDistance + , 20000 + , this); + + + m_locationViewshed[i]->setVisible(m_viewshedVisible); + + // Add the Viewshed to the Analysis Overlay + m_analysisOverlay->analyses()->append(m_locationViewshed[i]); + + m_locationViewshed[i]->setFrustumOutlineVisible(true); + + // QColor frustumColor(0, 0, 0, 255); + // m_locationViewshed->setFrustumOutlineColor(frustumColor); + } + return; +} + +// Getters/Setters for each Q_PROPERTY +bool ViewshedLocation::isViewshedVisible() const +{ + return m_viewshedVisible; +} + +void ViewshedLocation::setViewshedVisible(bool viewshedVisible) +{ + for (int i = 0; i < 3; ++i) + { + if (m_locationViewshed[i]) + { + if (m_locationViewshed[i]->isVisible() == viewshedVisible) + { + continue; + } + + m_locationViewshed[i]->setVisible(viewshedVisible); + } + } + + if (m_viewshedVisible != viewshedVisible) + { + m_viewshedVisible = viewshedVisible; + + emit viewshedVisibleChanged(); + } +} + +bool ViewshedLocation::isFrustumOutlineVisible() const +{ + return m_locationViewshed[0] ? m_locationViewshed[0]->isFrustumOutlineVisible() : m_frustumVisible; +} + +void ViewshedLocation::setFrustumOutlineVisible(bool frustumVisible) +{ + for (int i = 0; i < 3; ++i) + { + if (m_locationViewshed[i]) + { + if (m_locationViewshed[i]->isFrustumOutlineVisible() == frustumVisible) + { + continue; + } + + m_frustumVisible = frustumVisible; + m_locationViewshed[i]->setFrustumOutlineVisible(frustumVisible); + } + } + + if (m_frustumVisible != frustumVisible) + { + m_frustumVisible = frustumVisible; + emit frustumVisibleChanged(); + } +} + +double ViewshedLocation::minDistance() const +{ + return m_locationViewshed[0] ? m_locationViewshed[0]->minDistance() : m_minDistance; +} + +void ViewshedLocation::setMinDistance(double minDistance) +{ + for (int i = 0; i < 3; ++i) + { + if (m_locationViewshed[i]) + { + if (m_locationViewshed[i]->minDistance() == minDistance) + { + continue; + } + m_locationViewshed[i]->setMinDistance(minDistance); + } + } + + if (m_minDistance != minDistance) + { + m_minDistance = minDistance; + emit minDistanceChanged(); + } +} + +double ViewshedLocation::maxDistance() const +{ + return m_locationViewshed[0] ? m_locationViewshed[0]->maxDistance() : m_maxDistance; +} + +void ViewshedLocation::setMaxDistance(double maxDistance) +{ + for (int i = 0; i < 3; ++i) + { + if (m_locationViewshed[i]) + { + if (m_locationViewshed[i]->maxDistance() == maxDistance) + { + continue; + } + m_locationViewshed[i]->setMaxDistance(maxDistance); + } + } + + if (m_maxDistance != maxDistance) + { + m_maxDistance = maxDistance; + emit maxDistanceChanged(); + } +} + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/Viewshed3D/ViewshedLocation.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/Viewshed3D/ViewshedLocation.h new file mode 100755 index 000000000..5cd4cb31a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/Viewshed3D/ViewshedLocation.h @@ -0,0 +1,99 @@ +/** + * @RDE_HEADER@ + * Declaration/Definition + * $Id$ + * $HeadURL$ + * $Author$ + */ +// [WriteFile Name=ViewshedLocation, Category=Analysis] +// [Legal] +// Copyright 2017 Esri. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// [Legal] + +#ifndef VIEWSHEDLOCATION_H +#define VIEWSHEDLOCATION_H + +#include +#include + + +namespace Esri +{ +namespace ArcGISRuntime +{ +class SceneGraphicsView; +class LocationViewshed; +class AnalysisOverlay; +} +} + + +class ViewshedLocation : public QObject +{ + Q_OBJECT + +public: + explicit ViewshedLocation(QObject* parent = nullptr); + ~ViewshedLocation() = default; + +// void componentComplete() Q_DECL_OVERRIDE; + void init(Esri::ArcGISRuntime::SceneGraphicsView* sceneView); + + Q_PROPERTY(bool viewshedVisible READ isViewshedVisible WRITE setViewshedVisible NOTIFY viewshedVisibleChanged) + Q_PROPERTY(bool frustumVisible READ isFrustumOutlineVisible WRITE setFrustumOutlineVisible NOTIFY frustumVisibleChanged) + Q_PROPERTY(double minDistance READ minDistance WRITE setMinDistance NOTIFY minDistanceChanged) + Q_PROPERTY(double maxDistance READ maxDistance WRITE setMaxDistance NOTIFY maxDistanceChanged) + + + bool isViewshedVisible() const; + void setViewshedVisible(bool viewshedVisible); + + bool isFrustumOutlineVisible() const; + void setFrustumOutlineVisible(bool frustumVisible); + + double minDistance() const; + void setMinDistance(double minDistance); + + double maxDistance() const; + void setMaxDistance(double maxDistance); + + +signals: + void viewshedVisibleChanged(); + void frustumVisibleChanged(); + void minDistanceChanged(); + void maxDistanceChanged(); + + +private: + void connectSignals(); + void setInitialViewpoint(); + void createViewshed(double x, double y); + + Esri::ArcGISRuntime::SceneGraphicsView *m_sceneView = nullptr; + Esri::ArcGISRuntime::LocationViewshed *m_locationViewshed[3] = { nullptr, nullptr, nullptr }; + Esri::ArcGISRuntime::AnalysisOverlay *m_analysisOverlay = nullptr; + + bool m_viewshedVisible = true; + bool m_frustumVisible = false; + double m_minDistance = 50; + double m_maxDistance = 1000; + double m_horizontalAngle = 45; + double m_veriticalAngle = 90; + double m_heading = 0; + double m_pitch = 90; + bool m_calculating = false; +}; + +#endif // VIEWSHEDLOCATION_H diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/Viewshed3D/main.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/Viewshed3D/main.cpp new file mode 100755 index 000000000..d4abac4af --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/EvalApps/Viewshed3D/main.cpp @@ -0,0 +1,244 @@ +/* +** +** @RDE_HEADER@ +** Showing artefacts when drawing 3D map. +** $Id$ +** $HeadURL$ +** $Author$ +** +** ----------------------------------------------------------------------------------------------- +** +** Hardware-Environment: Intel-PC +** Software-Environment: Windows 7, Linux, Qt 5.9. ArcGISRuntimeQt 100.1 +** +** Language ........: C++ 11 +** +** ----------------------------------------------------------------------------------------------- +** +** Author ...........: Jörg Turla, SPD G, Tel. 3132 +** +** ----------------------------------------------------------------------------------------------- +** +** Notes .........: +** +** 08.11.2017, JT : Initial +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ViewshedLocation.h" + + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +class MapWindow : public QWidget +{ + + Esri::ArcGISRuntime::Basemap * m_basemap; + Esri::ArcGISRuntime::Scene * m_scene; + Esri::ArcGISRuntime::SceneGraphicsView * m_sceneGraphicsView; + Esri::ArcGISRuntime::GeoView * m_mapView; + Esri::ArcGISRuntime::GraphicsOverlay * m_graphicsOverlay; + + Qt::MouseButton m_pressedMouseButton; + QPoint m_mouseMoveRefPos; + QFuture m_pickingTask; + bool m_pendingRequest; + + public: + + MapWindow() : QWidget(), m_graphicsOverlay(nullptr), m_pickingTask(), m_pendingRequest(false) + { + m_pressedMouseButton = Qt::MouseButton::NoButton; + m_mouseMoveRefPos = QPoint(); + + m_basemap = new Esri::ArcGISRuntime::Basemap(this); + m_scene = new Esri::ArcGISRuntime::Scene(m_basemap,this); + + connect( m_basemap, &Esri::ArcGISRuntime::Map::errorOccurred , this, &MapWindow::onErrorBasemap ); + connect( m_scene , &Esri::ArcGISRuntime::Scene::errorOccurred, this, &MapWindow::onErrorScene ); + connect( m_scene , &Esri::ArcGISRuntime::Scene::doneLoading , this, &MapWindow::onDoneLoading ); + + m_sceneGraphicsView = nullptr; + try + { + m_sceneGraphicsView = new Esri::ArcGISRuntime::SceneGraphicsView(); // no parent, + m_sceneGraphicsView->setObjectName(QLatin1String("EsriSceneView")); + m_sceneGraphicsView->setFrameShape(QFrame::NoFrame); // frameless + } + catch (std::exception &stdEx) + { + const char *what = stdEx.what(); + if (what) + qCritical()<< "MapWindow init SceneGraphicsView: std Exception " << " what: '" << what << "'"; + else + qCritical() << "MapWindow init SceneGraphicsView: std Exception"; + + throw; // re-throw + } + catch (...) + { + qCritical() << "MapWindow init SceneGraphicsView: unknown Exception"; + throw; // re-throw + } + + if (!m_sceneGraphicsView) + return; + + m_sceneGraphicsView->setArcGISScene(m_scene); + + QHBoxLayout *layout = new QHBoxLayout(this); + layout->addWidget(m_sceneGraphicsView); + + initMapLayer(QLatin1String("http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer")); + initMapLayer(QLatin1String("http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer")); + + Esri::ArcGISRuntime::Camera camera(28.4, 83.9, 10010.0, 10.0, 80.0, 300.0); + m_sceneGraphicsView->setViewpointCameraAndWait(camera);// set the viewpoint + + /* + QPushButton * aButton = new QPushButton(tr("Click me")); // toplevel widget + QGraphicsProxyWidget *proxy = m_sceneGraphicsView->QGraphicsView::scene()->addWidget(aButton); + proxy->setOpacity(0.99); + + connect( aButton, &QPushButton::clicked, this, []() {qCritical() << "A Button has been clicked!";} ); + */ + + QTimer *timer = new QTimer(this); + timer->setSingleShot(true); + timer->setInterval(5000); + connect(timer, &QTimer::timeout, this, [this]() { + + qCritical() << "Create viewshed ..."; + ViewshedLocation * vLoc = new ViewshedLocation(this); + vLoc->init(m_sceneGraphicsView); + }); + timer->start(); + + + } // MapWindow() + + + ~MapWindow() = default; + + + void initMapLayer( const QString &mapServiceUrl ) + { + Esri::ArcGISRuntime::ArcGISTiledLayer *tiledLayer = new Esri::ArcGISRuntime::ArcGISTiledLayer(QUrl(mapServiceUrl), this); + tiledLayer->setName(mapServiceUrl); + tiledLayer->setObjectName(tiledLayer->name()); + + m_basemap->baseLayers()->append(tiledLayer); + + } // void initMapLayer() + + private: + + void onDoneLoading(const Esri::ArcGISRuntime::Error &loadError) + { + + } // void onDoneLoading(const Esri::ArcGISRuntime::Error &loadError) + + + void onErrorBasemap(const Esri::ArcGISRuntime::Error &error) + { + qCritical() << " ---EsriError,Basemap --- occured: " + << " code=" << error.code() + << " domain=" << (int)error.domain() + << " extendedErrorType=" << (int)error.extendedErrorType() + << ", message='" << error.message() << "'"; + } // void onErrorBasemap(const Esri::ArcGISRuntime::Error &error) + + + void onErrorScene(const Esri::ArcGISRuntime::Error &error) + { + qCritical() << " ---EsriError, Scene --- occured: " + << " code=" << error.code() + << " domain=" << (int)error.domain() + << " extendedErrorType=" << (int)error.extendedErrorType() + << ", message='" << error.message() << "'"; + } // void onErrorScene(const Esri::ArcGISRuntime::Error &error) + + +}; // MapWindow + + +int main(int argc, char* argv[]) +{ + QApplication app(argc,argv); + + // ESRI : +#ifdef Q_OS_WIN + // Force usage of OpenGL ES through ANGLE on Windows + QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); +#endif + // : ESRI + +#define PROXY +#ifdef PROXY + QNetworkProxy proxy; + proxy.setType(QNetworkProxy::HttpProxy); + proxy.setHostName(QStringLiteral("proxy-rde")); + proxy.setPort(8080); + proxy.setUser(QStringLiteral("norbert.thoden@rheinmetall.com")); + proxy.setPassword(QStringLiteral("RDEWWW11")); + QNetworkProxy::setApplicationProxy(proxy); +#endif + + MapWindow * mapWin = new MapWindow(); + + int width = mapWin->frameGeometry().width(); + int height = mapWin->frameGeometry().height(); + + QScreen *screen = QGuiApplication::primaryScreen(); + int screenWidth = screen ? screen->size().width() : 1920; + int screenHeight = screen ? screen->size().height() : 1080; + + mapWin->setGeometry((screenWidth / 2) - (width / 2), (screenHeight / 2) - (height / 2), width, height); + + mapWin->show(); + + app.setQuitOnLastWindowClosed(true); + + int retval = app.exec(); + + delete mapWin; + + return retval; + +} // main diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/CMakeLists.txt new file mode 100755 index 000000000..6822589b3 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/CMakeLists.txt @@ -0,0 +1,46 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# +FIND_PACKAGE(ArcGIS REQUIRED) + + +# LIBRARY +SET(FunctionDialog_SRCS + function_dialog.cpp + simple_esri_layer.cpp +) + +SET(FunctionDialog_HDRS + function_dialog_export.h + function_dialog.h + simple_esri_layer.h +) + +SET(FunctionDialog_ICONS + state_icon_b.xpm + state_icon_a.xpm + state_icon_c.xpm +) + +SOURCE_GROUP(FunctionDialog FILES ${FunctionDialog_SRCS} ${FunctionDialog_HDRS}) +NGL_ADD_LIBRARY(WS situationMapFunctionDialog SHARED ${FunctionDialog_SRCS} ${FunctionDialog_HDRS}) + +NGL_TARGET_LINK_LIBRARIES(situationMapFunctionDialog + PRIVATE + NGLLogger + LtsCoordTrans + LtsWidgets + advancedSymbologyGui + PUBLIC + ArcGIS::ArcGIS + situationMap + situationMapObjects + situationMapPortal + Qt6::Core + Qt6::Widgets +) + +NGL_INSTALL_TARGET(situationMapFunctionDialog) + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/function_dialog.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/function_dialog.cpp new file mode 100755 index 000000000..bb4e3cdbd --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/function_dialog.cpp @@ -0,0 +1,8906 @@ +/** + * @RDE_HEADER@ + * FunctionDialog + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "function_dialog.h" +#include "simple_esri_layer.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +// Portal +#include + +#include +#include +#include + +// RME LocalServer +#ifdef USE_LOCAL_SERVER +# include +#endif // USE_LOCAL_SERVER + + +// ESRI +#include +//#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include + +#include +#include +#include +#include + +// 3D +#include +#include + + + +// Qt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +void appendQPointF(SituationMap::PositionCont &posCont, const QPointF &point) +{ + posCont.append(SituationMap::Point3D::fromQPointF(point)); +} + + +void drawEllipse(Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer, + const Esri::ArcGISRuntime::Point &mapPoint, + const double &semiAxis1Length, + const double &semiAxis2Length, + const double &maxSegmentLength, + const QColor &color) +{ + Esri::ArcGISRuntime::GeodesicEllipseParameters ePara; + ePara.setGeometryType(Esri::ArcGISRuntime::GeometryType::Polygon); + + // 1 + ePara.setMaxSegmentLength(maxSegmentLength); + + // 2 + Esri::ArcGISRuntime::Point normalizedPt = + Esri::ArcGISRuntime::GeometryEngine::normalizeCentralMeridian(mapPoint); + ePara.setCenter(normalizedPt); + + // 3, 4 + ePara.setSemiAxis1Length(semiAxis1Length); + ePara.setSemiAxis2Length(semiAxis2Length); + + // 5 + ePara.setAxisDirection(/*axisDirection*/90); + + // Unit??????? + + Esri::ArcGISRuntime::Geometry ellipseGeometry = Esri::ArcGISRuntime::GeometryEngine::ellipseGeodesic(ePara); + + if (ellipseGeometry.isEmpty()) + qWarning() << " There was an error computing the geodesic ellipse. Try a different segment length."; + else + { + Esri::ArcGISRuntime::Polygon polygon(ellipseGeometry); + + QColor fillColor = color; + fillColor.setAlpha(150); + + Esri::ArcGISRuntime::Graphic *ellipseGraphic = new Esri::ArcGISRuntime::Graphic(); + ellipseGraphic->setGeometry(polygon); + Esri::ArcGISRuntime::SimpleFillSymbol ellipseSymbol(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, fillColor); + ellipseGraphic->setSymbol(&ellipseSymbol); + graphicsLayer->graphics()->append(ellipseGraphic); + +// for (int count = 0; count < ellipse.pointCount(); count++) +// { +// EsriRuntimeQt::Graphic *pointGraphic = +// new EsriRuntimeQt::Graphic(ellipse.point(count), EsriRuntimeQt::SimpleMarkerSymbol(color, 3)); +// graphicsLayer->graphics()->append(pointGraphic); +// } + } + +} + + +namespace SituationMap +{ +static LtsCoordTrans::UtmCoordinate s_basePoint; + +static OperatorMapSettings & s_operatorMapSettings() +{ + static OperatorMapSettings operatorMapSettings; + return operatorMapSettings; +} + + +class FunctionDialog::Private +{ +public: + static QHash s_activeFunctionDialogMap; + static void cleanupActiveFunctionDialogMap() + { + s_activeFunctionDialogMap.clear(); + } + + Private() = default; + ~Private() = default; + + // ESRI data: + Esri::ArcGISRuntime::Map *m_esriMap = nullptr; + Esri::ArcGISRuntime::GeoView *m_mapGeoView = nullptr; + Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr; + Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayerDict = nullptr; + + // OWN data: + RenderingMode m_renderMode = RenderingMode::Dynamic; + SituationMap::Map *m_mapInterface = nullptr; + SituationMap::MapImpl *m_mapImpl = nullptr; + QLabel *m_lbMapPosition = nullptr; + QLabel *m_lbPixelSize = nullptr; + + QPushButton *m_pbDump = nullptr; + QPushButton *m_pbGrid = nullptr; + QPushButton *m_pbVisibility = nullptr; + QPushButton *m_pbVisibilityTechn = nullptr; + QPushButton *m_pbVisibilityTact = nullptr; + + QPushButton *m_pbSelected = nullptr; + + QPushButton *m_pbPicking = nullptr; + QList m_objectsWithBoundingBox; + + QPushButton *m_pbBoundingBox = nullptr; + + QPushButton *m_pbBB2 = nullptr; + + QPushButton *m_pbPickByArea = nullptr; + + QDial *m_dialMapRotation = nullptr; + float getMapRotation() + { + float rotationAngle = 1.0; + if (m_dialMapRotation) + rotationAngle = m_dialMapRotation->value(); + return rotationAngle; + } + + + QSlider *m_slSymbolSize = nullptr; + float getScale() + { + float scale = 1.0f; + if (m_slSymbolSize) + scale = m_slSymbolSize->value() / 100.0f; + return scale; + } + + QSlider *m_slSymbolOpacity = nullptr; + float getSymbolOpacity() + { + float opac = 1.0f; + if (m_slSymbolOpacity) + opac = m_slSymbolOpacity->value() / 100.0f; + return opac; + } + + + QSlider *m_slDrawOrder = nullptr; + int getDrawOrder() + { + int drawOrder = 1; + if (m_slDrawOrder) + drawOrder = m_slDrawOrder->value(); + return drawOrder; + } + + QPushButton *m_pbOverviewMap = nullptr; + QPushButton *m_pbBackupOms = nullptr; + QPushButton *m_pbRestoreOms = nullptr; + + QPushButton *m_pbRemove = nullptr; + QPushButton *m_pbRemoveAll = nullptr; + QPushButton *m_pbClose = nullptr; + + QTabWidget *m_tabwidget = nullptr; + + SimpleEsriLayer *m_simpleEsriLayer = nullptr; + int m_simpleEsriLayerIndex = -1; + + LayerControlWidget *m_layerControlWidget = nullptr; + int m_layerControlIndex = -1; + + + // Performance: + QLineEdit *m_layerPerf_leLayer = nullptr; + QLineEdit *m_layerPerf_leObject = nullptr; + QLineEdit *m_layerPerf_leRadius = nullptr; + QCheckBox *m_layerPerf_cbText = nullptr; + + + // Tools/Viewshed + QLineEdit *m_viewshedDistance = nullptr; + + QLineEdit *m_sightFanObserverHeight = nullptr; + QLineEdit *m_sightFanTargetHeight = nullptr; + + + bool m_visibility = true; + bool m_visibilityTechn = true; + bool m_visibilityTact = true; + + bool m_selected = false; + + bool m_pickable = true; + + QLineEdit *m_leSidc = nullptr; + + + QComboBox *m_cbMultiPointSidc = nullptr; + Esri::ArcGISRuntime::TextSymbol *m_textSymbolMultiPointSidc = nullptr; + Esri::ArcGISRuntime::Graphic *m_tsGraphicMultiPointSidc = nullptr; + + + QComboBox *m_cbRubSidc = nullptr; + + QPointF m_objectBasePoint = QPointF(0, 0); + + // Graphics3D + struct object3D_data + { + std::shared_ptr m_graphic; + Esri::ArcGISRuntime::Point m_position; + }; + QVector m_impactCircleSphere; + QVector m_impactCone; + QVector m_3dModel; + QVector m_impactConeViaUniqueValueRenderer; + + + + // + static void clearMapObjectPtr(RefPointer mapObj) + { + if (mapObj) + mapObj->removeMySelf(); + } + + RefPointer m_polygon; + RefPointer m_polyline; + RefPointer m_text; + + // + RefPointer m_participant; + + RefPointer m_areaFire; + RefPointer m_building; + RefPointer m_bAgent; + RefPointer m_cAgent; + RefPointer m_cube; + RefPointer m_dangerArea; + RefPointer m_functionalArea; + RefPointer m_geoImage; + RefPointer m_soundArea; + RefPointer m_hitLine; + RefPointer m_lineOfSight; + RefPointer m_mine; + RefPointer m_mineField; + RefPointer m_nuclearAgent; + RefPointer m_virtualSingleMineAgent; + RefPointer m_singleFire; + + RefPointer m_basePoint; + + const QList< RefPointer > getObjList() + { + QList > objList = + m_mapInterface->objectFactory().getAllObjects(); + return objList; + } + + +}; // class FunctionDialog::Private +QHash FunctionDialog::Private::s_activeFunctionDialogMap; +//----------------------------------------------------------------------------- + +FunctionDialog::FunctionDialog(Esri::ArcGISRuntime::Map *esriMap, + Esri::ArcGISRuntime::MapGraphicsView *esriView, + QWidget *parent) + : QWidget(parent) + , d(new Private) +{ + qDebug() << LOGFUNCTION << " BEGIN"; + this->setObjectName(QLatin1String("SituationMapFunctionDialog")); + d->m_esriMap = esriMap; + d->m_mapGeoView = esriView; + + qDebug() << LOGFUNCTION << " BEGIN"; + + init(); +} + +FunctionDialog::FunctionDialog(Map *map) + : QWidget(nullptr) + , d(new Private) +{ + qDebug() << LOGFUNCTION << " BEGIN"; + this->setObjectName(QLatin1String("SituationMapFunctionDialog")); + d->m_mapInterface = map; + d->m_mapImpl = dynamic_cast(d->m_mapInterface); + + if (d->m_mapImpl) + { + d->m_esriMap = d->m_mapImpl->getEsriMap(); + d->m_mapGeoView = d->m_mapImpl->getEsriGeoView(); + + // define + QLatin1String layerName("FunctionDialog"); + d->m_renderMode = RenderingMode::Dynamic; + + // prepare + LayerFactoryImpl &layerFactory = d->m_mapImpl->layerFactoryImpl(); + layerFactory.addOperationalLayer(layerName, layerName, d->m_renderMode); + + // create corresponding layer: + d->m_graphicsLayer = layerFactory.getGraphicsLayer(layerName); + d->m_graphicsLayerDict = layerFactory.getGraphicsLayerDict(layerName); + } + + init(); + + connect(map, &Map::beginDestroy, this, &FunctionDialog::onDestroyed); + + FunctionDialog::Private::s_activeFunctionDialogMap[d->m_mapInterface] = this; + + qAddPostRoutine(FunctionDialog::Private::cleanupActiveFunctionDialogMap); + +} + +FunctionDialog::~FunctionDialog() +{ + qDebug() << LOGFUNCTION << " END"; + FunctionDialog::Private::s_activeFunctionDialogMap.remove(d->m_mapInterface); + delete d; +} + + +void FunctionDialog::onDestroyed() +{ + qDebug() << LOGFUNCTION << " commit suicide"; + delete this; +} + + +void FunctionDialog::addButtonToMap(Map *map, bool isVisible) +{ + if (!map || !map->getQGraphicsView()) + { + qWarning() << LOGFUNCTION << " no map!!!"; + std::cerr << LOGFUNCTION << " no map!!!" << std::endl; + return; + } + + // create button: + QPushButton *pbFunctionDialog = new QPushButton(QLatin1String("FunctionDialog")); + pbFunctionDialog->setObjectName(QLatin1String("QPushButton-FunctionDialog")); + pbFunctionDialog->setAttribute(Qt::WA_AcceptTouchEvents); + // add button +// MapImpl *mapImpl = static_cast(); +// EsriRuntimeQt::MapGraphicsView *mapGraphicsView = mapImpl->getEsriMapGraphicsView(); + + // Bug iOSQz-000005 'QGraphicsProxyWidget will crash' + // Adding a widget will let the application crash + // Workaround: 'proxy->setOpacity(0.99);' + QGraphicsProxyWidget *proxy = map->getQGraphicsView()->scene()->addWidget(pbFunctionDialog); + proxy->setOpacity(0.99); + + // enable/disable button + pbFunctionDialog->setStyleSheet(QLatin1String("QPushButton {background-color: red;}")); + pbFunctionDialog->setEnabled(false); + + QPointer mapNotifier = map->createMapNotifier(); + if (mapNotifier) + { + connect(mapNotifier.data(), &MapNotifier::mapReadyAndSymbolDictionaryLoaded, map, [pbFunctionDialog] () + { + pbFunctionDialog->setStyleSheet(QLatin1String("QPushButton {background-color: gray;}")); + pbFunctionDialog->setEnabled(true); + }); + mapNotifier->run(); + } + + pbFunctionDialog->setVisible(isVisible); + + + // add action to button + // c++11 -> lambda :-) + connect(pbFunctionDialog, &QPushButton::clicked, map, [map]() + { + + // Keep multiple MapWidget in mind! + if (!FunctionDialog::Private::s_activeFunctionDialogMap.contains(map)) + FunctionDialog::Private::s_activeFunctionDialogMap[map] = new SituationMap::FunctionDialog(map); + + SituationMap::FunctionDialog *functionDialog = FunctionDialog::Private::s_activeFunctionDialogMap[map]; + +// // map->getQGraphicsView()->scene()->addWidget(functionDialog); // just for testing + + functionDialog->raise(); + functionDialog->show(); + functionDialog->move(100, 100); + }, Qt::QueuedConnection); + +} + +void FunctionDialog::init() +{ + setAttribute(Qt::WA_DeleteOnClose); + + s_basePoint = LtsCoordTrans::UtmCoordinate::fromMgrs(QLatin1String("32UMD000000000000")); // Bremen 32UMD000000000000 +// s_basePoint = LtsCoordTrans::UtmCoordinate::fromMgrs(QLatin1String("32TNT000000000000")); // +// s_basePoint = LtsCoordTrans::UtmCoordinate::fromMgrs(QLatin1String("32UPD000000000000")); // Salchau 32UPD000000000000 + qWarning() << " s_basePoint: " + << " x=" << s_basePoint.x + << " y=" << s_basePoint.y; + + qint64 myPid = QCoreApplication::applicationPid(); + QString symType(QLatin1String("noSymType")); + if (d->m_mapImpl) + symType = toString(d->m_mapImpl->symbologyType()); + + QString title = QStringLiteral("FunctionDialog (symType=%1) (Rendering=%2) (pid=%3)") + .arg(symType, toString(d->m_renderMode)) + .arg(myPid); + + setWindowTitle(title); + + QHBoxLayout *layout = new QHBoxLayout(this); + + QVBoxLayout *leftLayout = new QVBoxLayout(); + layout->addLayout(leftLayout); + + + QPushButton *pbToBremen = new QPushButton(QLatin1String("Center to Bremen"), this); + leftLayout->addWidget(pbToBremen); + connect(pbToBremen, &QPushButton::clicked, this, [this]() + { + if (d->m_mapImpl->isReady()) + d->m_mapImpl->setCenterPoint(QPointF(0.155, 0.925)); + }); + + + QComboBox *cbCenterTo = new QComboBox(this); + leftLayout->addWidget(cbCenterTo); + + QVariant data; + data.setValue(QPointF(0.155, 0.925)); + cbCenterTo->addItem(QStringLiteral("Bremen"), data); + + data.setValue(QPointF(0.158, 0.928)); + cbCenterTo->addItem(QStringLiteral("Schnoeggersburg"), data); + + data.setValue(QPointF(0.155, 0.925)); + cbCenterTo->addItem(QStringLiteral("AHTC"), data); + + connect(cbCenterTo, static_cast(&QComboBox::currentIndexChanged), this, [this, cbCenterTo](int) + { + if (!d->m_mapImpl->isReady()) + return; + QPointF p = cbCenterTo->currentData().toPointF(); + d->m_mapImpl->setCenterPoint(p); + }); + + + + d->m_lbMapPosition = new QLabel; + leftLayout->addWidget(d->m_lbMapPosition); + + d->m_lbPixelSize = new QLabel; + leftLayout->addWidget(d->m_lbPixelSize); + + + d->m_tabwidget = new QTabWidget(); + leftLayout->addWidget(d->m_tabwidget); + + // make connection to receive mouse position signals + + connect(d->m_mapInterface, &Map::mouseWindowPositionChanged, this, &FunctionDialog::onMouseWindowPositionChanged); + connect(d->m_mapInterface, &Map::mouseMapPositionChanged , this, &FunctionDialog::onMouseMapPositionChanged); + connect(d->m_mapInterface, &Map::mouseGeoPositionChanged , this, &FunctionDialog::onMouseGeoPositionChanged); + + connect(d->m_mapInterface, &Map::extentChanged, this, &FunctionDialog::onExtendChanged, Qt::QueuedConnection); + + //----------------------------------- + // Visibility + //----------------------------------- + QVBoxLayout *vBoxLayout = new QVBoxLayout(); + layout->addLayout(vBoxLayout); + + if (d->m_mapImpl) + { + d->m_pbGrid = new QPushButton(QLatin1String("Grid")); + if (d->m_mapImpl->grid().isVisible()) + d->m_pbGrid->setText(QLatin1String("Grid is ON ")); + else + d->m_pbGrid->setText(QLatin1String("Grid is OFF")); + vBoxLayout->addWidget(d->m_pbGrid); + connect(d->m_pbGrid, &QPushButton::clicked, this, &FunctionDialog::onToggleGrid); + } + + d->m_pbVisibility = new QPushButton(QLatin1String("Visibility")); + if (d->m_visibility) + d->m_pbVisibility->setText(QLatin1String("Visibility is ON ")); + else + d->m_pbVisibility->setText(QLatin1String("Visibility is OFF")); + vBoxLayout->addWidget(d->m_pbVisibility); + connect(d->m_pbVisibility, &QPushButton::clicked, this, &FunctionDialog::onToggleVisibility); + + d->m_pbVisibilityTechn = new QPushButton(QLatin1String("VisiTechn.")); + if (d->m_visibilityTechn) + d->m_pbVisibilityTechn->setText(QLatin1String("VisiTechn is ON ")); + else + d->m_pbVisibilityTechn->setText(QLatin1String("Visibility is OFF")); + vBoxLayout->addWidget(d->m_pbVisibilityTechn); + connect(d->m_pbVisibilityTechn, &QPushButton::clicked, this, &FunctionDialog::onToggleVisibilityTechn); + + d->m_pbVisibilityTact = new QPushButton(QLatin1String("VisiTact.")); + if (d->m_visibilityTact) + d->m_pbVisibilityTact->setText(QLatin1String("VisiTact is ON ")); + else + d->m_pbVisibilityTact->setText(QLatin1String("VisiTact is OFF")); + vBoxLayout->addWidget(d->m_pbVisibilityTact); + connect(d->m_pbVisibilityTact, &QPushButton::clicked, this, &FunctionDialog::onToggleVisibilityTact); + + + d->m_pbSelected = new QPushButton(QLatin1String("Selection")); + if (d->m_selected) + d->m_pbSelected->setText(QLatin1String("Selection is ON ")); + else + d->m_pbSelected->setText(QLatin1String("Selection is OFF")); + vBoxLayout->addWidget(d->m_pbSelected); + connect(d->m_pbSelected, &QPushButton::clicked, this, &FunctionDialog::onToggleSelection); + + d->m_pbPicking = new QPushButton(QLatin1String("Picking")); + if (d->m_pickable) + d->m_pbPicking->setText(QLatin1String("Picking is ON ")); + else + d->m_pbPicking->setText(QLatin1String("Picking is OFF")); + vBoxLayout->addWidget(d->m_pbPicking); + connect(d->m_pbPicking, &QPushButton::clicked, this, &FunctionDialog::onTogglePicking); + + + d->m_pbBoundingBox = new QPushButton(QLatin1String("BoundingBox is ON ")); + if (d->m_objectsWithBoundingBox.isEmpty()) + d->m_pbBoundingBox->setText(QLatin1String("BoundingBox is OFF")); + else + d->m_pbBoundingBox->setText(QLatin1String("BoundingBox is ON")); + vBoxLayout->addWidget(d->m_pbBoundingBox); + connect(d->m_pbBoundingBox, &QPushButton::clicked, this, &FunctionDialog::onToggleBoundingBox); + + + d->m_pbBB2 = new QPushButton(QLatin1String("BB2")); + vBoxLayout->addWidget(d->m_pbBB2); + connect(d->m_pbBB2, &QPushButton::clicked, this, &FunctionDialog::onToggleBB2); + + + d->m_pbPickByArea = new QPushButton(QLatin1String("pick by area")); + vBoxLayout->addWidget(d->m_pbPickByArea); + connect(d->m_pbPickByArea, &QPushButton::clicked, this, [this]() + { + d->m_mapInterface->pickMapObjectsArea(); + }); + + + // picking: + connect(d->m_mapInterface, &Map::mapObjectsPickedByArea, this, [](const SituationMap::Map::RefPointerMapObjectList & mapObjList) + { + qCritical() << " GUI is not available anymore, moved to AdapterDds."; + + QList uniqueMapIds; + uniqueMapIds.reserve(mapObjList.size()); + + for (RefPointer mapObj : mapObjList) + { + uniqueMapIds.append(mapObj->uniqueMapId()); + + qCritical() << " mapObjectsPickedByArea: " << mapObj->uniqueMapId() + << " , mapObj->debugInfo: " << mapObj->debugInfo(); + } + + }); + + + vBoxLayout->addWidget(new QLabel(QLatin1String("MapRotation:"))); + + QPushButton *pbMapRotation = new QPushButton(QLatin1String("MapRotation to 0")); + pbMapRotation->setToolTip(QLatin1String("reset rotation")); + vBoxLayout->addWidget(pbMapRotation); + + d->m_dialMapRotation = new QDial(); + d->m_dialMapRotation->setMinimum(-180); + d->m_dialMapRotation->setMaximum(180); + d->m_dialMapRotation->setValue(0); + +// d->m_dialMapRotation->setTickInterval(10); + d->m_dialMapRotation->setSingleStep(10); + + vBoxLayout->addWidget(d->m_dialMapRotation); + connect(d->m_dialMapRotation, &QSlider::valueChanged, this, &FunctionDialog::onMapRotationChanged); + + // enable 'reset' + connect(pbMapRotation, &QPushButton::pressed, this, [this]() + { + d->m_dialMapRotation->setValue(0); // reset + }); + + + + QLabel *lbSymScale = new QLabel(QLatin1String("SymSize:")); + vBoxLayout->addWidget(lbSymScale); + + d->m_slSymbolSize = new QSlider(Qt::Horizontal); + d->m_slSymbolSize->setMinimum(5); + d->m_slSymbolSize->setMaximum(500); + d->m_slSymbolSize->setValue(100); + + d->m_slSymbolSize->setTickInterval(50); + d->m_slSymbolSize->setSingleStep(1); + + vBoxLayout->addWidget(d->m_slSymbolSize); + connect(d->m_slSymbolSize, &QSlider::valueChanged, this, &FunctionDialog::onSymbolSizeChanged); + + + QLabel *lbSymOpacity = new QLabel(QLatin1String("SymOpacity:")); + vBoxLayout->addWidget(lbSymOpacity); + + d->m_slSymbolOpacity = new QSlider(Qt::Horizontal); + d->m_slSymbolOpacity->setMinimum(10); + d->m_slSymbolOpacity->setMaximum(90); + d->m_slSymbolOpacity->setValue(50); + + d->m_slSymbolOpacity->setTickInterval(10); + d->m_slSymbolOpacity->setSingleStep(10); + + vBoxLayout->addWidget(d->m_slSymbolOpacity); + connect(d->m_slSymbolOpacity, &QSlider::valueChanged, this, &FunctionDialog::onSymbolOpacityChanged); + + + QLabel *lbDrawOrder = new QLabel(QLatin1String("DrawOrder:")); + vBoxLayout->addWidget(lbDrawOrder); + + d->m_slDrawOrder = new QSlider(Qt::Horizontal); + d->m_slDrawOrder->setMinimum(-100); + d->m_slDrawOrder->setMaximum(100); + d->m_slDrawOrder->setValue(0); + + d->m_slDrawOrder->setTickInterval(1); + d->m_slDrawOrder->setSingleStep(1); + + vBoxLayout->addWidget(d->m_slDrawOrder); + connect(d->m_slDrawOrder, &QSlider::valueChanged, this, &FunctionDialog::onDrawOrderChanged); + + + //----------------------------------- + vBoxLayout->addStretch(); + + d->m_pbOverviewMap = new QPushButton(QLatin1String("OverviewMap on/off")); + vBoxLayout->addWidget(d->m_pbOverviewMap); + connect(d->m_pbOverviewMap, &QPushButton::clicked, this, &FunctionDialog::onOverviewMap); + + + //----------------------------------- + // Operator Map Settings + //----------------------------------- + d->m_pbBackupOms = new QPushButton(QLatin1String("backup Operator Map Settings")); + vBoxLayout->addWidget(d->m_pbBackupOms); + connect(d->m_pbBackupOms, &QPushButton::clicked, this, &FunctionDialog::onBackupOms); + + d->m_pbRestoreOms = new QPushButton(QLatin1String("restore Operator Map Settings")); + vBoxLayout->addWidget(d->m_pbRestoreOms); + connect(d->m_pbRestoreOms, &QPushButton::clicked, this, &FunctionDialog::onRestoreOms); + + + //----------------------------------- + // Remove + //----------------------------------- + d->m_pbRemove = new QPushButton(QLatin1String("Remove couple")); + vBoxLayout->addWidget(d->m_pbRemove); + connect(d->m_pbRemove, &QPushButton::clicked, this, &FunctionDialog::onRemove); + + d->m_pbRemoveAll = new QPushButton(QLatin1String("Remove ALL")); + vBoxLayout->addWidget(d->m_pbRemoveAll); + connect(d->m_pbRemoveAll, &QPushButton::clicked, this, &FunctionDialog::onRemoveAll); + + + //----------------------------------- + // Dump + //----------------------------------- + d->m_pbDump = new QPushButton(QLatin1String("Dump (ESRI)")); + vBoxLayout->addWidget(d->m_pbDump); + connect(d->m_pbDump, &QPushButton::clicked, this, &FunctionDialog::onDump); + + // Screenshot + QPushButton *pbScreenshot = new QPushButton(QLatin1String("Screenshot - grab()")); + vBoxLayout->addWidget(pbScreenshot); + connect(pbScreenshot, &QPushButton::clicked, this, []() + { + QString imageName = QDir::tempPath() + QLatin1String("/sitMapScreenshot.png"); + qWarning() << " save to '" << imageName << "'... ToDo."; + }); + + + // toPdf + QPushButton *pbToPdf = new QPushButton(QLatin1String("toPdf")); + vBoxLayout->addWidget(pbToPdf); + connect(pbToPdf, &QPushButton::clicked, this, &FunctionDialog::onToPdf); + + + + //----------------------------------- + // Quit + //----------------------------------- + d->m_pbClose = new QPushButton(QLatin1String("Close")); + vBoxLayout->addWidget(d->m_pbClose, 2); + // c++11 -> lambda :-) + connect(d->m_pbClose, &QPushButton::clicked, this, &FunctionDialog::close); + + + //----------------------------------- + // Tabs + //----------------------------------- + this->addSimpleLayerTab(); + this->addLogicalLayerTab(); + this->addLayerPerformanceTab(); + this->addGraphicsTab(); + this->addGraphics3DTab(); + this->addMessageTab(); + this->addMapObjectTab(); + this->addRubberbandTab(); + this->addRubberband3DTab(); + this->addToolsTab(); + this->addViewshedTab(); + this->addOverlayPlanTab(); + this->addIconRenderTab(); + this->addPortalTab(); + this->addMovePointLuxemburgTab(); + // this->addYourTab(); + + // c++11 -> lambda :-) + connect(d->m_tabwidget, &QTabWidget::currentChanged, this, [this]() + { + int curIdx = d->m_tabwidget->currentIndex(); + if (curIdx == d->m_simpleEsriLayerIndex) + d->m_simpleEsriLayer->updatePanel(); + else if (curIdx == d->m_layerControlIndex) + d->m_layerControlWidget->updatePanel(); + }); + + d->m_tabwidget->setCurrentIndex(7); + + + // Access symbolDictionary to create the dictionary at beginning + if (d->m_mapImpl) + d->m_mapImpl->symbolDictionary(); +} + + +void FunctionDialog::setGraphicsLayer(Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer) +{ + d->m_graphicsLayer = graphicsLayer; +} + + +void FunctionDialog::setMapPositionString(const QString &mapPosition) +{ + if (d->m_lbMapPosition) + d->m_lbMapPosition->setText(mapPosition); +} + +void FunctionDialog::setPixelSizeString(const QString &text) +{ + if (d->m_lbPixelSize) + d->m_lbPixelSize->setText(text); +} + +void FunctionDialog::addMapPositionString(const QString &mapPosition) +{ + if (d->m_lbMapPosition) + d->m_lbMapPosition->setText(d->m_lbMapPosition->text() + mapPosition); +} + +void FunctionDialog::addSimpleLayerTab() +{ + QWidget *w = new QWidget(); + + QVBoxLayout *mainLayout = new QVBoxLayout(w); + if (!d->m_simpleEsriLayer) + { + Esri::ArcGISRuntime::MapGraphicsView *mapGraphicsView = + dynamic_cast(d->m_mapGeoView); + if (mapGraphicsView) + d->m_simpleEsriLayer = new SimpleEsriLayer(this, mapGraphicsView); + } + mainLayout->addWidget(d->m_simpleEsriLayer); + mainLayout->addStretch(); + + d->m_simpleEsriLayerIndex = d->m_tabwidget->addTab(w, QLatin1String("SimpleLayer")); + + +} + +void FunctionDialog::addLogicalLayerTab() +{ + QWidget *w = new QWidget(); + + QVBoxLayout *mainLayout = new QVBoxLayout(w); + if (!d->m_layerControlWidget) + { + d->m_layerControlWidget = new LayerControlWidget(this, d->m_mapInterface, + true, // enableLayer + true, // enableBaseMap + QLatin1String("LC-FunctionDialog")); + } + mainLayout->addWidget(d->m_layerControlWidget); + mainLayout->addStretch(); + + d->m_layerControlIndex = d->m_tabwidget->addTab(w, QLatin1String("LogicalLayer")); +} + +void FunctionDialog::addLayerPerformanceTab() +{ + QWidget *w = new QWidget(); + + QVBoxLayout *mainLayout = new QVBoxLayout(w); + QGridLayout *layout = new QGridLayout(); + mainLayout->addLayout(layout); + mainLayout->addStretch(); + + + d->m_tabwidget->addTab(w, QLatin1String("LayerPerf.")); + + QLabel *lb; + + // + lb = new QLabel(QLatin1String("# of Layer")); + layout->addWidget(lb, 0, 0); + + d->m_layerPerf_leLayer = new QLineEdit(QLatin1String("1")); + d->m_layerPerf_leLayer->setPlaceholderText(QLatin1String("# of Layer")); + d->m_layerPerf_leLayer->setToolTip(d->m_layerPerf_leLayer->placeholderText()); + layout->addWidget(d->m_layerPerf_leLayer, 1, 0); + + // + lb = new QLabel(QLatin1String("# of Objects (total)")); + layout->addWidget(lb, 0, 1); + + d->m_layerPerf_leObject = new QLineEdit(QLatin1String("1000")); + d->m_layerPerf_leObject->setPlaceholderText(QLatin1String("# of Objects")); + d->m_layerPerf_leObject->setToolTip(d->m_layerPerf_leObject->placeholderText()); + layout->addWidget(d->m_layerPerf_leObject, 1, 1); + + + // + lb = new QLabel(QLatin1String("radius")); + layout->addWidget(lb, 0, 2); + + d->m_layerPerf_leRadius = new QLineEdit(QLatin1String("100")); + d->m_layerPerf_leRadius->setPlaceholderText(QLatin1String("radius")); + d->m_layerPerf_leRadius->setToolTip(d->m_layerPerf_leRadius->placeholderText()); + layout->addWidget(d->m_layerPerf_leRadius, 1, 2); + + // + lb = new QLabel(QLatin1String("with Text")); + layout->addWidget(lb, 0, 3); + + d->m_layerPerf_cbText = new QCheckBox; + d->m_layerPerf_cbText->setToolTip(QLatin1String("with Text")); + layout->addWidget(d->m_layerPerf_cbText, 1, 3); + + + QPushButton *pbStart = new QPushButton(QLatin1String("generate Layer")); + layout->addWidget(pbStart); + connect(pbStart, &QPushButton::clicked, this, &FunctionDialog::onLayerPerfCreateLayer); + +} + + +void FunctionDialog::addGraphicsTab() +{ + QWidget *w = new QWidget(); + + QVBoxLayout *mainLayout = new QVBoxLayout(w); + QGridLayout *layout = new QGridLayout(); + mainLayout->addLayout(layout); + mainLayout->addStretch(); + + d->m_tabwidget->addTab(w, QLatin1String("Graphics")); + + QPushButton *pbGraphics = new QPushButton(QLatin1String("generate Graphics")); + layout->addWidget(pbGraphics); + connect(pbGraphics, &QPushButton::clicked, this, &FunctionDialog::onGenerateGraphics); + + QPushButton *pbGenerateCube = new QPushButton(QLatin1String("generate/destroy Cube")); + layout->addWidget(pbGenerateCube); + connect(pbGenerateCube, &QPushButton::clicked, this, &FunctionDialog::onGenerateCube); + + QPushButton *pbGenerateCubeArray = new QPushButton(QLatin1String("generate/destroy CubeArray")); + layout->addWidget(pbGenerateCubeArray); + connect(pbGenerateCubeArray, &QPushButton::clicked, this, &FunctionDialog::onGenerateCubeArray); + + QPushButton *pbGenerateStackedCubes = new QPushButton(QLatin1String("generate stacked Cubes")); + layout->addWidget(pbGenerateStackedCubes); + connect(pbGenerateStackedCubes, &QPushButton::clicked, this, &FunctionDialog::onGenerateStackedCubes); + + QPushButton *pbGraphicsKristenStrandford = new QPushButton(QLatin1String("generate Graphics - KristenStrandford")); + layout->addWidget(pbGraphicsKristenStrandford); + connect(pbGraphicsKristenStrandford, &QPushButton::clicked, this, &FunctionDialog::onGenerateGraphicsKristenStrandford); +} + +void FunctionDialog::addGraphics3DTab() +{ + QWidget *w = new QWidget(); + + QVBoxLayout *mainLayout = new QVBoxLayout(w); + QGridLayout *layout = new QGridLayout(); + mainLayout->addLayout(layout); + mainLayout->addStretch(); + + d->m_tabwidget->addTab(w, QLatin1String("Graphics 3D")); + + // + // Sphere (Kugel) - RENDERER + { + QPushButton *pbGraphics = new QPushButton(QLatin1String("generate 3D-Graphic Sphere RENDERER")); + layout->addWidget(pbGraphics); + connect(pbGraphics, &QPushButton::clicked, this, &FunctionDialog::onGenerate3DGraphicSphereRENDERER); + + QSlider *slOpcacity = new QSlider(Qt::Horizontal); + slOpcacity->setMinimum(0); + slOpcacity->setMaximum(100); + layout->addWidget(slOpcacity); + connect(slOpcacity, &QSlider::valueChanged, this, &FunctionDialog::onSphereOpacityChangedRENDERER); + + QSlider *slZValue = new QSlider(Qt::Horizontal); + slZValue->setMinimum(-150); + slZValue->setMaximum(150); + layout->addWidget(slZValue); + connect(slZValue, &QSlider::valueChanged, this, &FunctionDialog::onSphereZValueChanged); + } + + + // + // Sphere (Kugel) + { + QPushButton *pbGraphics = new QPushButton(QLatin1String("generate 3D-Graphic Sphere")); + layout->addWidget(pbGraphics); + connect(pbGraphics, &QPushButton::clicked, this, &FunctionDialog::onGenerate3DGraphicSphere); + + QSlider *slOpcacity = new QSlider(Qt::Horizontal); + slOpcacity->setMinimum(0); + slOpcacity->setMaximum(100); + layout->addWidget(slOpcacity); + connect(slOpcacity, &QSlider::valueChanged, this, &FunctionDialog::onSphereOpacityChanged); + + QSlider *slZValue = new QSlider(Qt::Horizontal); + slZValue->setMinimum(-150); + slZValue->setMaximum(150); + layout->addWidget(slZValue); + connect(slZValue, &QSlider::valueChanged, this, &FunctionDialog::onSphereZValueChanged); + } + + + // + // Cone (Kegel) + { + QPushButton *pbCone = new QPushButton(QLatin1String("generate 3D-Graphic Cone")); + layout->addWidget(pbCone); + connect(pbCone, &QPushButton::clicked, this, &FunctionDialog::onGenerate3DGraphicCone); + + QSlider *slOpcacity = new QSlider(Qt::Horizontal); + slOpcacity->setMinimum(0); + slOpcacity->setMaximum(100); + layout->addWidget(slOpcacity); + connect(slOpcacity, &QSlider::valueChanged, this, &FunctionDialog::onConeOpacityChanged); + + QSlider *slZValue = new QSlider(Qt::Horizontal); + slZValue->setMinimum(-150); + slZValue->setMaximum(150); + layout->addWidget(slZValue); + connect(slZValue, &QSlider::valueChanged, this, &FunctionDialog::onConeZValueChanged); + + QSlider *slHeading = new QSlider(Qt::Horizontal); + slHeading->setMinimum(-180); + slHeading->setMaximum(180); + layout->addWidget(slHeading); + connect(slHeading, &QSlider::valueChanged, this, &FunctionDialog::onConeHeadingChanged); + + QSlider *slRoll = new QSlider(Qt::Horizontal); + slRoll->setMinimum(-180); + slRoll->setMaximum(180); + layout->addWidget(slRoll); + connect(slRoll, &QSlider::valueChanged, this, &FunctionDialog::onConeRollChanged); + + QSlider *slPitch = new QSlider(Qt::Horizontal); + slPitch->setMinimum(-180); + slPitch->setMaximum(180); + layout->addWidget(slPitch); + connect(slPitch, &QSlider::valueChanged, this, &FunctionDialog::onConePitchChanged); + } + + // + // 3D Model + { + QPushButton *pbGraphics = new QPushButton(QLatin1String("place 3D model (symbol and graphic)")); + layout->addWidget(pbGraphics); + connect(pbGraphics, &QPushButton::clicked, this, &FunctionDialog::on3dModel); + +// QSlider *slOpcacity = new QSlider(Qt::Horizontal); +// slOpcacity->setMinimum(0); +// slOpcacity->setMaximum(100); +// layout->addWidget(slOpcacity); +// connect(slOpcacity, &QSlider::valueChanged, this, &FunctionDialog::onSphereOpacityChangedRENDERER); + + QSlider *slZValue = new QSlider(Qt::Horizontal); + slZValue->setMinimum(-150); + slZValue->setMaximum(150); + layout->addWidget(slZValue); + connect(slZValue, &QSlider::valueChanged, this, &FunctionDialog::on3dModelZValueChanged); + } + + // + // Cones via UniqueValueRenderer + { + QPushButton *pbGraphics = new QPushButton(QStringLiteral("Cones via UniqueValueRenderer")); + layout->addWidget(pbGraphics); + connect(pbGraphics, &QPushButton::clicked, this, &FunctionDialog::onGenerate3DGraphicConeUniqueValueRenderer); + +// QSlider *slOpcacity = new QSlider(Qt::Horizontal); +// slOpcacity->setMinimum(0); +// slOpcacity->setMaximum(100); +// layout->addWidget(slOpcacity); +// connect(slOpcacity, &QSlider::valueChanged, this, &FunctionDialog::onSphereOpacityChangedRENDERER); + +// QSlider *slZValue = new QSlider(Qt::Horizontal); +// slZValue->setMinimum(-150); +// slZValue->setMaximum(150); +// layout->addWidget(slZValue); +// connect(slZValue, &QSlider::valueChanged, this, &FunctionDialog::on3dModelZValueChanged); + } + + +} + + +QSet listOfPointSidcs_Internal() +{ + QSet sidcs; + + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10011500002102000000"); // mine_impl + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10012500002108000000"); // single_fire_impl + + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10012500002707050000"); // 25270705 -> Protection Areas : Minefield : Dummy + + + // SymbolSet 01 == air + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10030100001101000000"); // differs in APP6(D) + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10030100001201000000"); + + // SymbolSet 10 == Land Unit + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10031000001100000000"); + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10031000001101000000"); + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10031000001102000000"); + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10031000001103000000"); + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10031000001104000000"); + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10031000001105000000"); + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10031000001106000000"); + + // SymbolSet 15 == Land Equipment + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10031500001110000000"); // baseSymbolCode = 15 111000 Missle Launcher + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10031500001100000000"); + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10031500001101000000"); + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10031500001101010000"); + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10031500001101020000"); + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10031500001101030000"); + + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10011000001213030000"); // Movement and Maneuver : Reconnaissance 10 121303 (FULL_FRAME, but no real MainIcon) + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10031500001202000000"); // Vehicle : Tank 15 120200 + + + // Guez-IEDs + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10011500002104000000"); // LAND MINE - IED + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10013500001202000000"); // Weapon - IED + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10014000001103000000"); // IED SUICIDE BOMBER (OHNE ZickZack) + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10014000001103010000"); // IED SUICIDE BOMBER (MIT ZickZack) + // ^ + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10014000001103040000"); // IED suicide bomber + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10014000001101110000"); // BOOBY TRAP (replaced by 'IED suicide bomber') + + return sidcs; +} + +QSet listOfLineSidcs_Internal() +{ + QSet sidcs; + + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10032500001406030000"); // hit_line_impl + + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10012500001402000000"); // 25140200_6 == "Maneuver Lines : Line of Contact" (no key8-Entry) + +#ifdef SYM_PROTECTION_AREAS_BECOMES_LINE + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10012500002711000000"); // Control Measure: Protection Areas : + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10012500002713000000"); // Control Measure: Protection Areas : Assult Crossing (former GeometryType::area, now GeometryType::Line) + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10012500002714000000"); // Control Measure: Protection Areas : Bridge +#endif // SYM_PROTECTION_AREAS_BECOMES_LINE + + return sidcs; +} + +QSet listOfAreaSidcs_Internal() +{ + QSet sidcs; + + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10012500002408010000"); // area_fire_impl + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10012500002717000000"); // biological_agent_impl + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10042500002701000000"); // building_outline_impl + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10012500002718000000"); // chemical_agent_impl + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10012500001511000000"); // danger_area_impl +// sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D,"10012500002707010000"); // MineField + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10012500002707060000"); // mine_field_impl + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10012500002719000000"); // nuclear_agent_impl + +#ifndef SYM_PROTECTION_AREAS_BECOMES_LINE + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10012500002711000000"); // Control Measure: Protection Areas : + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10012500002713000000"); // Control Measure: Protection Areas : Assult Crossing (former GeometryType::area, now GeometryType::Line) + sidcs += AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6D, "10012500002714000000"); // Control Measure: Protection Areas : Bridge +#endif // SYM_PROTECTION_AREAS_BECOMES_LINE + + return sidcs; +} + +QSet listOfPlanEditorSidcs_Internal() +{ + QSet sidcs; + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25342300"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25140500"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25152100"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25141400"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25141700"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25290500"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25270503"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25341900"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25270504"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25230100"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25271100"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25341500"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25241202"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25241602"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25340800"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25270502"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25342201"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25271204"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25240103"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25240802"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25270501"); + sidcs += AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, "25271600"); + + return sidcs; +} + + + +void FunctionDialog::addMessageTab() +{ + QWidget *w = new QWidget(); + + QVBoxLayout *mainLayout = new QVBoxLayout(w); + QGridLayout *layout = new QGridLayout(); + mainLayout->addLayout(layout); + mainLayout->addStretch(); + + d->m_tabwidget->addTab(w, QLatin1String("Message")); + + //----------------------------------- + QPushButton *pbAddCivilSymbols = new QPushButton(QLatin1String("add civil symbols")); + layout->addWidget(pbAddCivilSymbols); + connect(pbAddCivilSymbols, &QPushButton::clicked, this, &FunctionDialog::onAddCivilSymbols); + + //----------------------------------- + QPushButton *pbMessage = new QPushButton(QLatin1String("generate Message")); + layout->addWidget(pbMessage); + connect(pbMessage, &QPushButton::clicked, this, &FunctionDialog::onMessage); + + //----------------------------------- + QPushButton *pbSelectMessage = new QPushButton(QLatin1String("select Message")); + layout->addWidget(pbSelectMessage); + connect(pbSelectMessage, &QPushButton::clicked, this, &FunctionDialog::onMessageSelect); + + //----------------------------------- + QPushButton *pbSymbolOverview = new QPushButton(QLatin1String("overviewESRI - wait!!!")); + layout->addWidget(pbSymbolOverview); + + // c++11 -> lambda :-) + connect(pbSymbolOverview, &QPushButton::clicked, this, []() + { + AdvancedSymbology::Gui::BaseSymbolLookupDialog *bslDlg = + AdvancedSymbology::Gui::BaseSymbolLookupDialog::showInstance(); + + QSet sidcs; + sidcs += listOfPointSidcs_Internal(); + sidcs += listOfLineSidcs_Internal(); + sidcs += listOfAreaSidcs_Internal(); + sidcs += listOfPlanEditorSidcs_Internal(); + + QSet baseSidcs; + baseSidcs.reserve(sidcs.size()); + for(const auto &item : std::as_const(sidcs)) + { + QString res = AdvancedSymbology::SidcBuilderBase::concreteSidcToBaseSymbolCode(item); + if (res.isEmpty()) + baseSidcs.insert(item); + else + baseSidcs.insert(res); + } // for + bslDlg->setValidBaseSymbolCodes(baseSidcs); + + bslDlg->show(); + }); + + //----------------------------------- + QPushButton *pbAddSymbol = new QPushButton(QLatin1String("add symbol from File")); + layout->addWidget(pbAddSymbol); + connect(pbAddSymbol, &QPushButton::clicked, this, &FunctionDialog::onAddSymbol); + + + //----------------------------------- + QGridLayout *someParticipantLayout = new QGridLayout(); + someParticipantLayout->setObjectName(QLatin1String("SomeParticipantsLayout")); + layout->addLayout(someParticipantLayout, layout->rowCount(), 0); + + QPushButton *pbSomeParticipants = new QPushButton(QLatin1String("Aggregation-Participants")); + someParticipantLayout->addWidget(pbSomeParticipants, 0, 0); + connect(pbSomeParticipants, &QPushButton::clicked, this, &FunctionDialog::onSomeParticipants); + + QSlider *slAggregationModifier = new QSlider(Qt::Horizontal); + + slAggregationModifier->setTickInterval(10); + slAggregationModifier->setSingleStep(10); + + someParticipantLayout->addWidget(slAggregationModifier, 0, 1); + + connect(slAggregationModifier, &QSlider::valueChanged, this, [this, slAggregationModifier]() + { + const int counter = 6; + int scaleMin = 80000; + int scaleMax = 500; + + int interval = (scaleMin - scaleMax) / counter; + + + LayerFactory &layerFac = d->m_mapInterface->layerFactory(); + int newVal = 0; + for (int i = 1; i <= counter; ++i) + { + const QString &layername = QStringLiteral("Participants_%1").arg(i); + LayerParameter layerPara = layerFac.layerParameter(layername); + + // double minScale = 0; ///< big value (e.g. 10000000) limiting zoomOut 1:10000000 -> 0.0000001 ; '0' without function + // double maxScale = 0; ///< small value (e.g. 1000) limiting zoomIn 1:1000 -> 0.001 ; '0' without function + + layerPara.maxScale = newVal; + + newVal = scaleMax + interval * i; + double userVal = slAggregationModifier->value() / 100.0; + newVal += interval * userVal; + newVal = std::min(newVal, scaleMin); + newVal = std::max(newVal, scaleMax); + + layerPara.minScale = newVal; + + if (i == counter) + layerPara.minScale = 0; + + qWarning() << LOGFUNCTION << " layer: " << layername << " -> " + << " userVal=" << userVal + << " max=" << layerPara.maxScale + << " min=" << layerPara.minScale; + + layerFac.setLayerParameter(layername, layerPara); + + + } // for + + }); + + slAggregationModifier->setMinimum(-200); + slAggregationModifier->setMaximum(200); + slAggregationModifier->setValue(0); + + + //----------------------------------- + // createParticipantButton + //----------------------------------- + QGridLayout *participantLayout = new QGridLayout(); + participantLayout->setObjectName(QLatin1String("participantLayout")); + layout->addLayout(participantLayout, layout->rowCount(), 0); + + d->m_leSidc = new QLineEdit(); + + switch (d->m_mapInterface->symbologyType()) + { + case AdvancedSymbology::SymbologyType::None: + d->m_leSidc->setText(AdvancedSymbology::SidcCodec::encodeCivilEts("StelleBerRaum")); + d->m_leSidc->setToolTip(QStringLiteral("

symbols added \?\?

")); + d->m_leSidc->setToolTipDuration(1000); + break; + case AdvancedSymbology::SymbologyType::APP6B: + case AdvancedSymbology::SymbologyType::MIL2525C: + d->m_leSidc->setText( AdvancedSymbology::SidcCodec::encodeComplete( d->m_mapInterface->symbologyType(), "SFGPUCD---D-") ); + break; + case AdvancedSymbology::SymbologyType::APP6D: + case AdvancedSymbology::SymbologyType::MIL2525D: + d->m_leSidc->setText(AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::MIL2525D, "10011501001202000000")); // 10015000001202000000 -> 15 120200 LandEquip, Tank + d->m_leSidc->setText(AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::MIL2525D, "10011001001110000000")); // 10031000001110000000 -> 10 111000 LandUnits, CommandAndControl Signal + d->m_leSidc->setText(AdvancedSymbology::SidcCodec::encodeCivilEts("Pers_FW_BerFue")); +// d->m_leSidc->setText(AdvancedSymbology::SidcCodec::encodeCivilEts("ETS_PersFueAllg")); + break; + } + + d->m_leSidc->setObjectName(QLatin1String("SIDC")); + QFont f = d->m_leSidc->font(); + f.setPixelSize(20); + d->m_leSidc->setFont(f); + participantLayout->addWidget(d->m_leSidc, 0, 0); + connect(d->m_leSidc, &QLineEdit::textChanged, this, [this]() + { + if (d->m_leSidc->text().length() > 20) + d->m_leSidc->setStyleSheet(QLatin1String("QLineEdit#SIDC { background-color: red; color: #000; }")); + else + d->m_leSidc->setStyleSheet(QLatin1String("QLineEdit#SIDC { background-color: white; color: #000; }")); + }); + + QPushButton *pbCreatePart = new QPushButton(QLatin1String("createParticipant!! \n symbols added?")); + pbCreatePart->setToolTip(d->m_leSidc->toolTip()); + participantLayout->addWidget(pbCreatePart, 0, 1); + + connect(pbCreatePart, &QPushButton::clicked, this, &FunctionDialog::onParticipantCreate); +} /* FunctionDialog::addMessageTab */ + + +void FunctionDialog::addMapObjectTab() +{ + QWidget *w = new QWidget(); + + QVBoxLayout *mainLayout = new QVBoxLayout(w); + QGridLayout *layout = new QGridLayout(); + mainLayout->addLayout(layout); + mainLayout->addStretch(); + + d->m_tabwidget->addTab(w, QLatin1String("Map Objects (Factory)")); + + //----------------------------------- + // createPolygonButton + //----------------------------------- + QPushButton *pbPolygon = new QPushButton(QLatin1String("create Polygon")); + layout->addWidget(pbPolygon); + connect(pbPolygon, &QPushButton::clicked, + this, &FunctionDialog::onPolygonCreate); + + + //----------------------------------- + // createPolylineButton + //----------------------------------- + QPushButton *pbPolyline = new QPushButton(QLatin1String("create Polyline")); + layout->addWidget(pbPolyline); + connect(pbPolyline, &QPushButton::clicked, + this, &FunctionDialog::onPolylineCreate); + + //----------------------------------- + // createTextButton + //----------------------------------- + QPushButton *pbText = new QPushButton(QLatin1String("create Text")); + layout->addWidget(pbText); + connect(pbText, &QPushButton::clicked, + this, &FunctionDialog::onTextCreate); + + + //----------------------------------- + // createMapObjectIndicatorButton + //----------------------------------- + QPushButton *pbMapObjectIndicator = new QPushButton(QLatin1String("create MapObjectIndicator")); + layout->addWidget(pbMapObjectIndicator); + connect(pbMapObjectIndicator, &QPushButton::clicked, + this, &FunctionDialog::onMapObjectIndicatorCreate); + + + //----------------------------------- + // createBuildingOutlineButton + //----------------------------------- + QPushButton *pbCreateBuilding = new QPushButton(QLatin1String("create Building")); + layout->addWidget(pbCreateBuilding); + connect(pbCreateBuilding, &QPushButton::clicked, + this, &FunctionDialog::onBuildingCreate); + + //----------------------------------- + // createBAgentButton + //----------------------------------- + QPushButton *pbCreateBAgent = new QPushButton(QLatin1String("create B-Agent")); + layout->addWidget(pbCreateBAgent); + connect(pbCreateBAgent, &QPushButton::clicked, + this, &FunctionDialog::onBiologicalAgentCreate); + + //----------------------------------- + // createCAgentButton + //----------------------------------- + QPushButton *pbCreateCAgent = new QPushButton(QLatin1String("create C-Agent")); + layout->addWidget(pbCreateCAgent); + connect(pbCreateCAgent, &QPushButton::clicked, + this, &FunctionDialog::onChemicalAgentCreate); + + + //----------------------------------- + // createDangerAreaButton + //----------------------------------- + QPushButton *createDangerAreaButton = new QPushButton(QLatin1String("create DangerArea")); + layout->addWidget(createDangerAreaButton); + connect(createDangerAreaButton, &QPushButton::clicked, + this, &FunctionDialog::onDangerAreaCreate); + + //----------------------------------- + // createDangerAreaButton + //----------------------------------- + QPushButton *createFunctionalAreaButton = new QPushButton(QLatin1String("create FunctionalArea")); + layout->addWidget(createFunctionalAreaButton); + connect(createFunctionalAreaButton, &QPushButton::clicked, + this, &FunctionDialog::onFunctionalAreaCreate); + + //----------------------------------- + // createAreaFireButton + //----------------------------------- + QPushButton *createAreaFireButton = new QPushButton(QLatin1String("create AreaFire")); + layout->addWidget(createAreaFireButton); + connect(createAreaFireButton, &QPushButton::clicked, + this, &FunctionDialog::onAreaFireCreate); + + //----------------------------------- + // createSingleFireButton + //----------------------------------- + QPushButton *createSingleFireButton = new QPushButton(QLatin1String("create SingleFire")); + layout->addWidget(createSingleFireButton); + connect(createSingleFireButton, &QPushButton::clicked, + this, &FunctionDialog::onSingleFireCreate); + + //----------------------------------- + // createChargeEffectButton + //----------------------------------- + QPushButton *createChargeEffectButton = new QPushButton(QLatin1String("create Charge Effect")); + layout->addWidget(createChargeEffectButton); + connect(createChargeEffectButton, &QPushButton::clicked, + this, &FunctionDialog::onChargeEffectCreate); + + //----------------------------------- + // createMineButton + //----------------------------------- + QPushButton *createMineButton = new QPushButton(QLatin1String("create Mine")); + layout->addWidget(createMineButton); + connect(createMineButton, &QPushButton::clicked, + this, &FunctionDialog::onMineCreate); + + //----------------------------------- + // createMineButton + //----------------------------------- + QPushButton *createMineFieldButton = new QPushButton(QLatin1String("create MineField")); + layout->addWidget(createMineFieldButton); + connect(createMineFieldButton, &QPushButton::clicked, + this, &FunctionDialog::onMineFieldCreate); + + //----------------------------------- + // createNuclearButton + //----------------------------------- + QPushButton *createNuclearButton = new QPushButton(QLatin1String("create Nuclear")); + layout->addWidget(createNuclearButton); + connect(createNuclearButton, &QPushButton::clicked, + this, &FunctionDialog::onNuclearCreate); + + //----------------------------------- + // createVirtualSingleMineButton + //----------------------------------- + QPushButton *createVirtualSingleMineButton = new QPushButton(QLatin1String("create Virtual Single Mine")); + layout->addWidget(createVirtualSingleMineButton); + connect(createVirtualSingleMineButton, &QPushButton::clicked, + this, &FunctionDialog::onBoobyTrapCreate); + + + //----------------------------------- + // createHitLineButton + //----------------------------------- + QHBoxLayout *hBox = new QHBoxLayout(); + layout->addLayout(hBox, 99, 0); + + QPushButton *createHitLineButton = new QPushButton(QLatin1String("create HitLine")); + hBox->addWidget(createHitLineButton); + connect(createHitLineButton, &QPushButton::clicked, + this, &FunctionDialog::onHitLineCreate); + + QPushButton *hitLineRotate = new QPushButton(QLatin1String("rotate HitLine")); + hBox->addWidget(hitLineRotate); + connect(hitLineRotate, &QPushButton::clicked, this, [this]() + { + if (d->m_hitLine) + { + static int i = 0; + const QPointF &start = d->m_hitLine->startPoint().toPointF(); + QPointF end = d->m_hitLine->endPoint().toPointF(); + MathUtils::rotatePoint(end, start, 10); + + Point3D start3D = Point3D::fromQPointF(start); + Point3D end3D = Point3D::fromQPointF(end); + end3D.rz() += i*10; + + i++; + qCritical() << " Hitline: z = " << end3D.z(); + + d->m_hitLine->setStartAndEndPoint(start3D, + end3D); + d->m_hitLine->updateView(); + } + }); + + //----------------------------------- + // createLineOfSightLine + //----------------------------------- + QPushButton *createLosLineButton = new QPushButton(QLatin1String("create LineOfSightLinge")); + layout->addWidget(createLosLineButton); + connect(createLosLineButton, &QPushButton::clicked, + this, &FunctionDialog::onLineOfSightLineCreate); + + + //----------------------------------- + // createGeoImage + //----------------------------------- + QPushButton *createGeoImageButton = new QPushButton(QLatin1String("create GeoImage \n(handled on signal 'mapObjectsClicked'?)")); + layout->addWidget(createGeoImageButton); + connect(createGeoImageButton, &QPushButton::clicked, + this, &FunctionDialog::onGeoImageCreate); + + // similar to the application: + connect(d->m_mapInterface, &SituationMap::Map::mapObjectsClicked, + this, &FunctionDialog::onMapObjectClicked); + + + //----------------------------------- + // createSoundArea + //----------------------------------- + QPushButton *createSoundAreaButton = new QPushButton(QLatin1String("create SoundArea")); + layout->addWidget(createSoundAreaButton); + connect(createSoundAreaButton, &QPushButton::clicked, + this, &FunctionDialog::onSoundAreaCreate); + + //----------------------------------- + // createMapObjectIndicatorButton + //----------------------------------- + QPushButton *pbCube = new QPushButton(QLatin1String("create cube")); + layout->addWidget(pbCube); + connect(pbCube, &QPushButton::clicked, + this, &FunctionDialog::onCubeCreate); + + //----------------------------------- + // ??? + //----------------------------------- + + + //----------------------------------- + // createShowBasePoint + //----------------------------------- + QPushButton *createShowBasePointButton = new QPushButton(QLatin1String("ShowBasePoint")); + layout->addWidget(createShowBasePointButton); + connect(createShowBasePointButton, &QPushButton::clicked, + this, &FunctionDialog::onShowBasePoint); + + + //----------------------------------- + // createPolizeiButton + //----------------------------------- + QPushButton *createPolizeiButton = new QPushButton(QLatin1String("create Polizei")); + layout->addWidget(createPolizeiButton); + connect(createPolizeiButton, &QPushButton::clicked, + this, &FunctionDialog::onShowPolizei); + +} + + +void FunctionDialog::addRubberband3DTab() +{ + QWidget *w = new QWidget(); + QGridLayout *layout = new QGridLayout(w); + + d->m_tabwidget->addTab(w, QLatin1String("Rubberband3D")); + + + QPushButton *pbPointFtRubberband = new QPushButton(QLatin1String("Point Feature Rubberband"), w); + pbPointFtRubberband->setToolTip(QString()); + + layout->addWidget(pbPointFtRubberband, 0, 0); + + connect(pbPointFtRubberband, &QPushButton::clicked, this, [this]() + { + RB::RubberbandPointFeature3D *rubberband = d->m_mapInterface->rubberbandFactory().create3D(); + + if (nullptr != rubberband) + { + rubberband->setAutoDelete(true); + + QObject::connect(rubberband, &RB::RubberbandBase::apply, this, &FunctionDialog::onRubberband3DApply); + QObject::connect(rubberband, &RB::RubberbandBase::abort, this, &FunctionDialog::onRubberband3DAbort); + } + else + qWarning() << "Creating rubberband failed."; + + }); + + QPushButton *pbMultiPointFtRubberband = new QPushButton(QLatin1String("Multi Point Feature Rubberband - Polyline"), w); + pbMultiPointFtRubberband->setToolTip(QStringLiteral("Create 3D polyline rubberband")); + + layout->addWidget(pbMultiPointFtRubberband, 1, 0); + + connect(pbMultiPointFtRubberband, &QPushButton::clicked, this, [this]() + { + RB::RubberbandMultiPointFeature3D *rubberband = d->m_mapInterface->rubberbandFactory().create3D(); + + if (nullptr != rubberband) + { + rubberband->setAutoDelete(true); + + QObject::connect(rubberband, &RB::RubberbandBase::apply, this, &FunctionDialog::onRubberband3DApply); + QObject::connect(rubberband, &RB::RubberbandBase::abort, this, &FunctionDialog::onRubberband3DAbort); + } + else qWarning() << "Creating rubberband failed."; + + }); + + QPushButton *pbMultiPointFtClosedRubberband = new QPushButton(QLatin1String("Multi Point Feature Rubberband - Polygon"), w); + pbMultiPointFtClosedRubberband->setToolTip(QStringLiteral("Create 3D polygon rubberband")); + + layout->addWidget(pbMultiPointFtClosedRubberband, 2, 0); + + connect(pbMultiPointFtClosedRubberband, &QPushButton::clicked, this, [this]() + { + RB::RubberbandMultiPointFeature3D *rubberband = d->m_mapInterface->rubberbandFactory().create3D(); + + if (nullptr != rubberband) + { + rubberband->setAutoDelete(true); + rubberband->enableClosed(true); + + QObject::connect(rubberband, &RB::RubberbandBase::apply, this, &FunctionDialog::onRubberband3DApply); + QObject::connect(rubberband, &RB::RubberbandBase::abort, this, &FunctionDialog::onRubberband3DAbort); + } + else qWarning() << "Creating rubberband failed."; + + }); + + QPushButton *pbMultiPointFtCircleRubberband = new QPushButton(QLatin1String("Multi Point Feature Rubberband - Circle"), w); + pbMultiPointFtCircleRubberband->setToolTip(QStringLiteral("Create 3D circle rubberband")); + + layout->addWidget(pbMultiPointFtCircleRubberband, 3, 0); + + connect(pbMultiPointFtCircleRubberband, &QPushButton::clicked, this, [this]() + { + RB::RubberbandMultiPointFeature3D *rubberband = d->m_mapInterface->rubberbandFactory().create3D(); + + if (nullptr != rubberband) + { + rubberband->setAutoDelete(true); + rubberband->enableCircular(true); + + QObject::connect(rubberband, &RB::RubberbandBase::apply, this, &FunctionDialog::onRubberband3DApply); + QObject::connect(rubberband, &RB::RubberbandBase::abort, this, &FunctionDialog::onRubberband3DAbort); + } + else qWarning() << "Creating rubberband failed."; + + }); +} // void FunctionDialog::addRubberband3DTab() + + +void FunctionDialog::onRubberband3DApply(RB::RubberbandBase *) +{ + qDebug() << LOGFUNCTION; +} // void FunctionDialog::onRubberband3DApply(RB::RubberbandBase *) + + +void FunctionDialog::onRubberband3DAbort(RB::RubberbandBase *) +{ + qDebug() << LOGFUNCTION; +} // void FunctionDialog::onRubberband3DAbort(RB::RubberbandBase *) + + +void inline getSectorStartEnd(double angle, double rotation, double &aStartRad, double &aEndRad) +{ + const double rotationMath = SituationMap::MathUtils::getAngleDegree2Math(rotation);//rotation < 90. ? std::fabs(rotation - 90) : std::fabs(360 - rotation + 90) ; // 12uhr ist 0° | rotation Math counter clockwise + // angle sectorStart + { + double val = rotationMath - angle / 2.; + aStartRad = val > 0 ? val : val + 360; // return the positive angle of the value + aStartRad = (aStartRad > 360. ? std::fmod(aStartRad, 360) : aStartRad) * LtsCoordTrans::deg2rad(); // convert to rad + } + // angle sectorEnd + { + aEndRad = rotationMath + angle / 2.; + aEndRad = (aEndRad > 360. ? std::fmod(aEndRad, 360) : aEndRad) * LtsCoordTrans::deg2rad(); + } +} + +void FunctionDialog::addRubberbandTab() +{ + QWidget *w = new QWidget(); + QGridLayout *layout = new QGridLayout(w); + + d->m_tabwidget->addTab(w, QLatin1String("Rubberband")); + + //----------------------------------- + // create symbol type edit line + //----------------------------------- + QGridLayout *rubberbandLayout = new QGridLayout(); + rubberbandLayout->setObjectName(QLatin1String("rubberbandLayout")); + layout->addLayout(rubberbandLayout, layout->rowCount(), 0); + + d->m_cbRubSidc = new QComboBox(w); + d->m_cbRubSidc -> setObjectName(QLatin1String("SIDC Rubberband")); + + AdvancedSymbology::SymbolDictionary &dict = d->m_mapImpl->symbolDictionary(); + + + switch (d->m_mapImpl->symbologyType()) + { + case AdvancedSymbology::SymbologyType::MIL2525D: + { + + // following works, but uses lambda. + // Therefore switch to SymbolResultWatcherComboxBox... + // QString sidc = QLatin1String("10051000001213030000"); // 01011000001213030000 -> LandUnits, Observer, Motorized + // AdvancedSymbology::SymbolResultWatcher *watcher = new AdvancedSymbology::SymbolResultWatcher(d->m_cbRubSidc); + // connect(watcher, &AdvancedSymbology::SymbolResultWatcher::resultReady, [this, watcher]() + // { + // qWarning() << " lamdbadYYYYYYY" << watcher; + // QSharedPointer symResult = watcher->symbolResult(); + // if (! symResult) + // return; // should NEVER happen! (Which object should have triggered the signal???) + // int idx = d->m_cbRubSidc->findData( QVariant::fromValue(watcher) ); + // const SymbolImage &symImage = symResult->symbolImage(); + // d->m_cbRubSidc->setItemIcon(idx, QPixmap::fromImage(symImage.image()) ); + // }); + // d->m_cbRubSidc->addItem(sidc, QVariant::fromValue(watcher) ); + // watcher->setResult(dict.findSymbol(sidc)); + + + QStringList sidcs; + sidcs += AdvancedSymbology::SidcCodec::encodeCivilEts("Pers_FW_BerFue"); // ETS + + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10051000001213030000"); // 01011000001213030000 -> LandUnits, Observer, Motorized + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10031500001202000000"); // 01015000001202000000 -> 15 120200 LandEquip, Tank + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10031500001110000000"); // 10031500001110000000 -> 15 111000 Missle Launcher + + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10032500002701000000"); // 25270100 + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10032500002718000000"); // 25271800 + + // For Hanna : + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10011500001101000000"); // 15 110100 Soldier (rifle) + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10011500001102000000"); // Soldier MG + + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10011500001202000000"); // Tank + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10011500131202000000"); // Tank BT + + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10011500001408000000"); // Utility Vehicle CROSS-COUNTRY + + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10011000131211000000"); // Infantry + + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10011000131215000000"); // Sniper + + + + // : For Hanna + + + // FLOT + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10012500001401010000"); // 10032500001401030000 -> 24 140103 Maneuver Lines Direction of Attack + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10012500001401020000"); // 10032500001401030000 -> 24 140103 Maneuver Lines Direction of Attack + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10012500001401030000"); // 10032500001401030000 -> 24 140103 Maneuver Lines Direction of Attack + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10012500001401040000"); // 10032500001401030000 -> 24 140103 Maneuver Lines Direction of Attack + + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10032500001401010000"); // 10032500001401030000 -> 24 140103 Maneuver Lines Direction of Attack + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10032500001401020000"); // 10032500001401030000 -> 24 140103 Maneuver Lines Direction of Attack + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10032500001401030000"); // 10032500001401030000 -> 24 140103 Maneuver Lines Direction of Attack + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10032500001401040000"); // 10032500001401030000 -> 24 140103 Maneuver Lines Direction of Attack + + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10042500001401010000"); // 10032500001401030000 -> 24 140103 Maneuver Lines Direction of Attack + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10042500001401020000"); // 10032500001401030000 -> 24 140103 Maneuver Lines Direction of Attack + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10042500001401030000"); // 10032500001401030000 -> 24 140103 Maneuver Lines Direction of Attack + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10042500001401040000"); // 10032500001401030000 -> 24 140103 Maneuver Lines Direction of Attack + + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10062500001401010000"); // 10032500001401030000 -> 24 140103 Maneuver Lines Direction of Attack + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10062500001401020000"); // 10032500001401030000 -> 24 140103 Maneuver Lines Direction of Attack + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10042500001401030000"); // 10032500001401030000 -> 24 140103 Maneuver Lines Direction of Attack + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10042500001401040000"); // 10032500001401030000 -> 24 140103 Maneuver Lines Direction of Attack + + for (const auto &sidc : std::as_const(sidcs)) + { + QSharedPointer srwCb(new AdvancedSymbology::SymbolResultWatcherComboxBox(d->m_cbRubSidc)); + d->m_cbRubSidc->addItem(sidc, QVariant::fromValue(srwCb)); + //AdvancedSymbology::SymbolResultWatcherComboxBox *srwCb; + //d->m_cbRubSidc->addItem(sidc, QVariant::fromValue(srwCb = new AdvancedSymbology::SymbolResultWatcherComboxBox(d->m_cbRubSidc))); + srwCb->setSymbolResult(dict.fetchSymbol(sidc)); + } + } + break; + + case AdvancedSymbology::SymbologyType::MIL2525C: + { + // Check Quartz + // Point Feature +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("SFGPUCD---"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("SUGPEWSM--"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFGPGPWA--"), 64)), sidc); + + d->m_cbRubSidc->insertSeparator(d->m_cbRubSidc->maxCount()); + + // Line Feature +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFGPOLAA--"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFMPOGL---"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFMPOWCT--"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFMPOWU---"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFMPOADC--"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFMPOEB---"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFGPSLA---"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFGPOLKGM-"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFMPOFG---"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GHGPOLAGM-"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GHMPBDI---"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GHMPBCR---"), 64)), sidc); + + d->m_cbRubSidc->insertSeparator(d->m_cbRubSidc->maxCount()); + + // Area Feature +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFGPPM----"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GHMPOFA---"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GHMPOGF---"), 64)), sidc); + } break; + + case AdvancedSymbology::SymbologyType::APP6B: + { + // Point Feature +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("SFGPUCD---"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("SUGPEWSM--"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFOPID----"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("SNGPUCRH--"), 64)), sidc); + + d->m_cbRubSidc->insertSeparator(d->m_cbRubSidc->maxCount()); + + // Line Feature +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFCPMOLAM-"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFCPMOLDS-"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFCPMOLDM-"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GHCPMOLI--"), 64)), sidc); + + d->m_cbRubSidc->insertSeparator(d->m_cbRubSidc->maxCount()); + + // Area Feature +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFCPMGASF-"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFCPMDM---"), 64)), sidc); +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("GFCPMDN---"), 64)), sidc); + + } break; + + case AdvancedSymbology::SymbologyType::APP6D: + // empty + break; + + + case AdvancedSymbology::SymbologyType::None: + // try civil (ETS_xxx +// d->m_cbRubSidc -> addItem(QPixmap::fromImage(dict.symbolImageBySidc(sidc = QLatin1String("ETS_StelleBerRaum"), 64)), sidc); + break; + + } // switch (d->m_map->symbologyType()) + + QFont f = d->m_cbRubSidc->font(); + f.setPixelSize(16); + d->m_cbRubSidc->setFont(f); + d->m_cbRubSidc->setIconSize(QSize(32, 32)); + d->m_cbRubSidc->setEditable(true); + +// connect(d->m_cbRubSidc, &QComboBox::editTextChanged, [=](const QString & text) +// { +// QVariant decoration = this->d->m_cbRubSidc->currentData( Qt::DecorationRole ); + +// AdvancedSymbology::SymbolDictionary & dict = this->d->m_mapImpl->symbolDictionary(); +// if (dict.contains(text)) +// { +// const QPixmap & icon = QPixmap::fromImage(dict.symbolImageBySidc(text,64)); +// // why isn't there write excess to currentData?? +// } +//// else; // why isn't there write excess to currentData?? + +// } ); + + rubberbandLayout->addWidget(d->m_cbRubSidc, 0, 0, 2, 2); + connect(d->m_cbRubSidc, &QComboBox::currentTextChanged, this, [this]() + { + if (d->m_cbRubSidc->currentText().length() > 15) + d->m_cbRubSidc->setStyleSheet(QLatin1String("QLineEdit#SIDC { background-color: red; color: #000; }")); + else + d->m_cbRubSidc->setStyleSheet(QLatin1String("QLineEdit#SIDC { background-color: white; color: #000; }")); + }); /* connect */ + + + QPushButton *pbCreatePointSymbol = new QPushButton(QLatin1String("Create Point Symbol"), w); + pbCreatePointSymbol->setToolTip(d->m_leSidc->toolTip()); + + rubberbandLayout->addWidget(pbCreatePointSymbol, 0, 2); + + connect(pbCreatePointSymbol, &QPushButton::clicked, + this , &FunctionDialog::onParticipantGetPosition); + + QPushButton *pbCreateRubberband = new QPushButton(QLatin1String("Create Rubberband"), w); + pbCreateRubberband->setToolTip(d->m_leSidc->toolTip()); + + rubberbandLayout->addWidget(pbCreateRubberband, 1, 2); + + connect(pbCreateRubberband, &QPushButton::clicked, this, [this]() + { + RB::Rubberband *rubberband = d->m_mapInterface->rubberbandFactory().createSymbol(d->m_cbRubSidc->currentText()); + + if (rubberband == nullptr) + qWarning() << "Creating rubberband failed, may be wrong sidc (" << d->m_cbRubSidc->currentText() << ")"; + else + { + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + }); + + + unsigned int rubberbandTests = 2; + + QPushButton *pbTestPointClusterRubber = new QPushButton(QLatin1String("Test PointCluster Rubberband"), w); + rubberbandLayout->addWidget(pbTestPointClusterRubber, rubberbandTests++, 0); + + connect(pbTestPointClusterRubber, &QPushButton::clicked, this, [this]() + { + RB::RubberbandPointCluster *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + if (rubberband == nullptr) + qWarning() << "Creating rubberband point cluster failed"; + else + { + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + }); + + //-------------------------------------- + QPushButton *pbTestMultiPointRubber = new QPushButton(QLatin1String("Multipoint RB"), w); + rubberbandLayout->addWidget(pbTestMultiPointRubber, rubberbandTests++, 0); + + connect(pbTestMultiPointRubber, &QPushButton::clicked, this, [this]() + { + RB::RubberbandMultiPointFeature *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + if (rubberband == nullptr) + qWarning() << "Creating rubberband multi points feature failed"; + else + { + rubberband -> enableClosed(false); + rubberband -> minimumPointCount(3); + rubberband -> maximumPointCount(6); + rubberband -> maximumLength(20000); // will not be checked for closed lines + rubberband -> maximumSegmentLength(5000); + + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + }); + + //-------------------------------------- + QPushButton *pbTestMultiEllipseRubber = new QPushButton(QLatin1String("MultiEllipse RB (BoobyTrap) from Scratch"), w); + rubberbandLayout->addWidget(pbTestMultiEllipseRubber, rubberbandTests++, 0); + connect(pbTestMultiEllipseRubber, &QPushButton::clicked, this, [this]() + { + + // FIRST RubberbandMultiEllipseFeature + RB::RubberbandMultiEllipseFeature *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + if (rubberband == nullptr) + qWarning() << "Creating rubberband multi CIRCLE feature failed"; + else + { + rubberband->minimumRadiusCount(2); + rubberband->maximumRadiusCount(2); + + using RWC = RB::RubberbandMultiEllipseFeature::RadiusWithContraints; + RWC radiusWithContraints1(0, 750, 1250); + RWC radiusWithContraints2(0, 1750, 2250); + rubberband->prepareFixedNumberOfRadiiWithConstraints({radiusWithContraints1, radiusWithContraints2}); + +#define CIRCLE_CONTRAINT +#ifdef CIRCLE_CONTRAINT + rubberband->constrainQuadratic(true); +#endif + //rubberband -> maximumLength(20000); // will not be checked for closed lines + //rubberband -> maximumSegmentLength(5000); + + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + }); + + //-------------------------------------- + QPushButton *pbTestMultiEllipseRubberPreDefined = new QPushButton(QLatin1String("MultiEllipse RB (BoobyTrap) predefined Start"), w); + rubberbandLayout->addWidget(pbTestMultiEllipseRubberPreDefined, rubberbandTests++, 0); + connect(pbTestMultiEllipseRubberPreDefined, &QPushButton::clicked, this, [this]() + { + RB::RubberbandMultiEllipseFeature *rubberbandForPreperation = d->m_mapInterface->rubberbandFactory().create(); + + if (rubberbandForPreperation == nullptr) + qWarning() << "Creating rubberband multi CIRCLE feature failed"; + else + { + // start with GUEZ-typical BoobyTrap: + + QPointF centerOfMapView = d->m_mapInterface->centerPoint(); + Q_UNUSED(centerOfMapView); + + //const QVector radii( { {1000, 1000}, {1500, 1500} } ); +// rubberband->setCenterAndRadii(centerOfMapView, RB::CoordinateSystem::Geo, radii); +// rubberbandForPreperation->prepareFixedNumberOfRadii(radii); + + + RB::RubberbandMultiEllipseFeature::RadiusWithContraints radiusWithContraints1(1000, 1000, 1000); + RB::RubberbandMultiEllipseFeature::RadiusWithContraints radiusWithContraints2(3000, 2750, 3250); + + rubberbandForPreperation->prepareFixedNumberOfRadiiWithConstraints({radiusWithContraints1, radiusWithContraints2}); + + + + + rubberbandForPreperation->constrainQuadratic(true); + + rubberbandForPreperation->enableSector(true); + double aStartRad=0, aEndRad=0; + double angle = 90; + double rot = 0; + getSectorStartEnd(angle, rot, aStartRad, aEndRad); + + rubberbandForPreperation->setSectorStart(std::sin(aStartRad), std::cos(aStartRad)); + rubberbandForPreperation->setSectorEnd(std::sin(aEndRad), std::cos(aEndRad)); + rubberbandForPreperation->setSectorAngleFixed(true); + + QObject::connect(rubberbandForPreperation, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberbandForPreperation, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + }); + + + + QPushButton *pbTestMultiPointFillRubber = new QPushButton(QLatin1String("Filled Multipoint RB"), w); + rubberbandLayout->addWidget(pbTestMultiPointFillRubber, rubberbandTests++, 0); + + connect(pbTestMultiPointFillRubber, &QPushButton::clicked, this, [this]() + { + RB::RubberbandMultiPointFeature *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + if (rubberband == nullptr) + qWarning() << "Creating filled rubberband multi points feature failed"; + else + { + rubberband -> enableClosed(true); + rubberband -> setFillColor(QColor(QLatin1String("teal"))); + rubberband -> maximumLength(20000); // will not be checked for closed lines + rubberband -> maximumSegmentLength(5000); + + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + }); + + QPushButton *pbTestSmoothLineRubber = new QPushButton(QLatin1String("Smooth Line RB"), w); + rubberbandLayout->addWidget(pbTestSmoothLineRubber, rubberbandTests++, 0); + + connect(pbTestSmoothLineRubber, &QPushButton::clicked, this, [this]() + { + RB::RubberbandSmoothLine *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + if (rubberband == nullptr) + qWarning() << "Creating rubberband smooth line failed"; + else + { + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + }); + + + QPushButton *pbTestSmoothFillRubber = new QPushButton(QLatin1String("Filled Smooth Line RB"), w); + rubberbandLayout->addWidget(pbTestSmoothFillRubber, rubberbandTests++, 0); + + connect(pbTestSmoothFillRubber, &QPushButton::clicked, this, [this]() + { + RB::RubberbandSmoothLine *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + if (rubberband == nullptr) + qWarning() << "Creating filled rubberband smooth line failed"; + else + { + rubberband->enableClosed(true); + rubberband->setFillColor(QColor(QLatin1String("olive"))); + + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + }); + + QPushButton *pbTestCorridorRubber = new QPushButton(QLatin1String("Corridor RB(Boundary)"), w); + rubberbandLayout->addWidget(pbTestCorridorRubber, rubberbandTests++, 0); + + connect(pbTestCorridorRubber, &QPushButton::clicked, this, [this]() + { + RB::RubberbandCorridor *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + if (rubberband == nullptr) + qWarning() << "Creating rubberband corridors failed"; + else + { + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + }); + + pbTestCorridorRubber = new QPushButton(QLatin1String("Corridor RB (Centerl., W = 50000)"), w); + rubberbandLayout->addWidget(pbTestCorridorRubber, rubberbandTests++, 0); + + connect(pbTestCorridorRubber, &QPushButton::clicked, this, [this]() + { + RB::RubberbandCorridor *rubberband = d->m_mapInterface->rubberbandFactory().create(RB::RubberbandCorridor::InputMode::InputCenterLine, 50000); + + if (rubberband == nullptr) + qWarning() << "Creating rubberband corridors failed"; + else + { + rubberband->setCorridorFillColor(QColor(QLatin1String("orange"))); + + rubberband -> minimumPointCount(3); + rubberband -> maximumPointCount(6); + rubberband -> minimumCorridorWidth(400); + rubberband -> maximumCorridorWidth(1000); + rubberband -> maximumLength(7000); + rubberband -> maximumSegmentLength(2000); + + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + }); + + QPushButton *pbTestSquareRubber = new QPushButton(QLatin1String("Square RB"), w); + rubberbandLayout->addWidget(pbTestSquareRubber, rubberbandTests++, 0); + + connect(pbTestSquareRubber, &QPushButton::clicked, this, [this]() + { + RB::RubberbandRectangle *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + if (rubberband == nullptr) + qWarning() << "Creating rubberband rectangle failed"; + else + { + rubberband->constrainQuadratic(true); + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + }); + + QPushButton *pbTestRectangleRubber = new QPushButton(QLatin1String("Rectangle RB"), w); + rubberbandLayout->addWidget(pbTestRectangleRubber, rubberbandTests++, 0); + + connect(pbTestRectangleRubber, &QPushButton::clicked, this, [this]() + { + RB::RubberbandRectangle *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + if (rubberband == nullptr) + qWarning() << "Creating rubberband rectangle failed"; + else + { + rubberband->minWidth(5000); + rubberband->maxWidth(2000000); + rubberband->minHeight(5000); + rubberband->maxHeight(1000000); + + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + }); + + QPushButton *pbTestRectangleOrthoRubber = new QPushButton(QLatin1String("Rectangle Ortho RB"), w); + rubberbandLayout->addWidget(pbTestRectangleOrthoRubber, rubberbandTests++, 0); + + connect(pbTestRectangleOrthoRubber, &QPushButton::clicked, this, [this]() + { + RB::RubberbandRectangle *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + if (rubberband == nullptr) + qWarning() << "Creating rubberband rectangle failed"; + else + { + rubberband->constrainOrthogonal(true); + + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + }); + + + QPushButton *pbTestRectangleParameter = new QPushButton(QLatin1String("Rectangle Parameter"), w); + rubberbandLayout->addWidget(pbTestRectangleParameter, rubberbandTests++, 0); + + connect(pbTestRectangleParameter, &QPushButton::clicked, this, [this]() + { + RB::RubberbandRectangle *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + if (rubberband == nullptr) + qWarning() << "Creating rubberband rectangle failed"; + else + { + SituationMap::CoordTranslationFunctor *cf = d->m_mapInterface->coordTranslationFunctor(); + + + QPointF bottomLeft = d->m_mapInterface->centerPoint(); + if (cf) bottomLeft = cf->geographicToMap(bottomLeft); + rubberband->buildRectangleRelativeToBottomLeftCorner(bottomLeft, 3000, 1500, QPoint(-1, 0), SituationMap::RB::CoordinateSystem::Map); + + + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + }); + + QPushButton *pbTestEllipseRubber = new QPushButton(QLatin1String("Ellipse RB"), w); + rubberbandLayout->addWidget(pbTestEllipseRubber, rubberbandTests++, 0); + + connect(pbTestEllipseRubber, &QPushButton::clicked, this, [this]() + { + d->m_mapInterface->rubberbandFactory().allowMultipleRubberbands(true); + + RB::RubberbandEllipse *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + d->m_mapInterface->rubberbandFactory().allowMultipleRubberbands(false); + + if (rubberband == nullptr) + qWarning() << "Creating rubberband ellipse failed"; + else + { + if (rubberband->rubberbandIsAlive()) + { + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + else + { + qWarning() << "Creating rubberband ellipse is not alive"; + rubberband->deleteMySelf(); + } + } + }); + + QPushButton *pbTestArrowRubber = new QPushButton(QLatin1String("Arrow RB"), w); + rubberbandLayout->addWidget(pbTestArrowRubber, rubberbandTests++, 0); + + connect(pbTestArrowRubber, &QPushButton::clicked, this, [this]() + { + + d->m_mapInterface->rubberbandFactory().allowMultipleRubberbands(true); + + RB::RubberbandArrow *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + d->m_mapInterface->rubberbandFactory().allowMultipleRubberbands(false); + + if (rubberband == nullptr) + qWarning() << "Creating rubberband arrow failed"; + else + { + if (rubberband->rubberbandIsAlive()) + { + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + else + { + qWarning() << "Creating rubberband arrow is not alive"; + rubberband->deleteMySelf(); + } + } + }); + + QPushButton *pbTestFreeHdRubber = new QPushButton(QLatin1String("Freehand RB"), w); + rubberbandLayout->addWidget(pbTestFreeHdRubber, rubberbandTests++, 0); + + connect(pbTestFreeHdRubber, &QPushButton::clicked, this, [this]() + { + RB::RubberbandFreeHand *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + if (rubberband == nullptr) + qWarning() << "Creating rubberband freehand failed"; + else + { + rubberband->setFillColor(QColor(0, 0, 0, 0)); + if (rubberband->rubberbandIsAlive()) + { + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + else + { + qWarning() << "Creating rubberband freehand is not alive"; + rubberband->deleteMySelf(); + } + } + }); + + rubberbandTests = 2; + QPushButton *pbTestRubberDefineArrow = new QPushButton(QLatin1String("Define Arrow"), w); + rubberbandLayout->addWidget(pbTestRubberDefineArrow, rubberbandTests++, 1); + + connect(pbTestRubberDefineArrow, &QPushButton::clicked, this, &FunctionDialog::onRubberbandDefineArrow); + + QPushButton *pbTestRubberDefineEllipse = new QPushButton(QLatin1String("Define Ellipse"), w); + rubberbandLayout->addWidget(pbTestRubberDefineEllipse, rubberbandTests++, 1); + + connect(pbTestRubberDefineEllipse, &QPushButton::clicked, this, &FunctionDialog::onRubberbandDefineEllipse); + + QPushButton *pbTestRubberCoordinates = new QPushButton(QLatin1String("RB Coordinates"), w); + rubberbandLayout->addWidget(pbTestRubberCoordinates, rubberbandTests++, 1); + + connect(pbTestRubberCoordinates, &QPushButton::clicked, this, &FunctionDialog::onRubberbandCheckCoordinates); + + QPushButton *pbTestRectCoordinates = new QPushButton(QLatin1String("Rectangle Coord."), w); + rubberbandLayout->addWidget(pbTestRectCoordinates, rubberbandTests++, 1); + + connect(pbTestRectCoordinates, &QPushButton::clicked, this, &FunctionDialog::onRectCheckCoordinates); + + QPushButton *pbTestRubberStateSelect = new QPushButton(QLatin1String("State Select RB"), w); + rubberbandLayout->addWidget(pbTestRubberStateSelect, rubberbandTests++, 1); + + connect(pbTestRubberStateSelect, &QPushButton::clicked, this, &FunctionDialog::onRubberbandStateSelect); + + QPushButton *pbTestRubberDefineBoobyTrap = new QPushButton(QLatin1String("BoobyTrap Sector RB"), w); + rubberbandLayout->addWidget(pbTestRubberDefineBoobyTrap, rubberbandTests++, 1); + + connect(pbTestRubberDefineBoobyTrap, &QPushButton::clicked, this, &FunctionDialog::onRubberbandBoobyTrapSector); + + + + rubberbandTests = 2; + QPushButton *pbTestTowInOneRubberband = new QPushButton(QLatin1String("TwoInOne"), w); + rubberbandLayout->addWidget(pbTestTowInOneRubberband, rubberbandTests++, 3); + connect(pbTestTowInOneRubberband, &QPushButton::clicked, this, &FunctionDialog::onRubberbandTwoInOne); + +} /* FunctionDialog::addRubberbandTab */ + +void FunctionDialog::onRubberbandApply(RB::RubberbandBase *rubberband) +{ + qCritical() << LOGFUNCTION << " rubberband = " << rubberband; + + RB::RubberbandPointFeature *rubberbandPoint = dynamic_cast(rubberband); + RB::RubberbandPointCluster *rubberbandClust = dynamic_cast(rubberband); + RB::RubberbandRectangle *rubberbandRect = dynamic_cast(rubberband); + RB::RubberbandEllipse *rubberbandElli = dynamic_cast(rubberband); + RB::RubberbandMultiPointFeature *rubberbandMFeat = dynamic_cast(rubberband); + RB::RubberbandMultiEllipseFeature *rubberbandMEllipseFeat = dynamic_cast(rubberband); + RB::RubberbandCorridor *rubberbandCorr = dynamic_cast(rubberband); + RB::RubberbandSmoothLine *rubberbandSmooth = dynamic_cast(rubberband); + RB::RubberbandArrow *rubberbandArrow = dynamic_cast(rubberband); + RB::RubberbandFreeHand *rubberbandFreehand = dynamic_cast(rubberband); + + if (rubberbandPoint) qCritical() << LOGFUNCTION << "point feature rubberband"; + if (rubberbandClust) qCritical() << LOGFUNCTION << "point cluster rubberband"; + if (rubberbandRect) qCritical() << LOGFUNCTION << "rectangle rubberband"; + if (rubberbandElli) qCritical() << LOGFUNCTION << "ellipse rubberband"; + if (rubberbandFreehand) qCritical() << LOGFUNCTION << "freehand rubberband"; + if (rubberbandMEllipseFeat) qCritical() << LOGFUNCTION << "multiCircle rubberband"; + + if (rubberbandMFeat) + { + if (rubberbandCorr) qDebug() << LOGFUNCTION << "corridor rubberband"; + else if (rubberbandSmooth) qDebug() << LOGFUNCTION << "smooth line rubberband"; + else qDebug() << LOGFUNCTION << "multi point feature rubberband"; + } + + qCritical() << LOGFUNCTION << " Count of Coordinates " << rubberband->coordinateCount(); + + if (rubberbandMEllipseFeat) + { + QPointF centerCoord = rubberbandMEllipseFeat->centerCoordinate(RB::CoordinateSystem::Map); + const QVector &radii = rubberbandMEllipseFeat->radii(); + + qCritical() << LOGFUNCTION << " FIRST centerCoord " << centerCoord << ", radii = " << radii; + qCritical() << LOGFUNCTION << " FIRST Sector sectorStart " << rubberbandMEllipseFeat->sectorStart(); + qCritical() << LOGFUNCTION << " FIRST Sector sectorEnd " << rubberbandMEllipseFeat->sectorEnd(); + + qCritical() << LOGFUNCTION << " FIRST Sector sectorStartDegMath " << rubberbandMEllipseFeat->sectorStartDegMath(); + qCritical() << LOGFUNCTION << " FIRST Sector sectorEndDegMath " << rubberbandMEllipseFeat->sectorEndDegMath(); + + + + RB::RubberbandMultiEllipseFeature *rubberbandReCreate = d->m_mapInterface->rubberbandFactory().create(); + + if (!rubberbandReCreate) + qWarning() << "Creating rubberband multi CIRCLE feature failed"; + else + { + rubberbandReCreate->setAutoDelete(true); +#ifdef CIRCLE_CONTRAINT + rubberbandReCreate->constrainQuadratic(true); +#endif + QVector radiiWithConstraints; + radiiWithConstraints.reserve(2); + for (const QPointF &item: std::as_const(radii)) + { + RB::RubberbandMultiEllipseFeature::RadiusWithContraints radiusWC; + radiusWC.m_radius = item.x(); + radiusWC.m_minRadius = radiusWC.m_radius-250; + radiusWC.m_maxRadius = radiusWC.m_radius+250; + + radiiWithConstraints.append(radiusWC); + } + rubberbandReCreate->setCenterAndRadii2(centerCoord, RB::CoordinateSystem::Map, + radiiWithConstraints); + + + QList rotation = {0, 45, 90, -45, 180, 270, 315, 360}; + double angle = 90; // oeffnungsWinkel + double rot = rotation.at(0); // clockwise + double aStartRad=0, aEndRad=0; + getSectorStartEnd(angle, rot, aStartRad, aEndRad); +// qCritical()<< "[FORWARD] rotation: " << rot +// << ", angle start: "<< aStartRad << "[rad] (" << (aStartRad*LtsCoordTrans::rad2deg()) << "[deg])" +// << ", angle end: "<< aEndRad << "[rad] (" << (aEndRad*LtsCoordTrans::rad2deg()) << "[deg])"; + + rubberbandReCreate->setSectorStart(std::sin(aStartRad), std::cos(aStartRad)); + rubberbandReCreate->setSectorEnd(std::sin(aEndRad), std::cos(aEndRad)); + + rubberbandReCreate->setSectorAngleFixed(true); + + QObject::connect(rubberbandReCreate, &RB::Rubberband::apply, this, [](RB::RubberbandBase *rubberband) + { + RB::RubberbandMultiEllipseFeature *rubberbandMCircleFeat = dynamic_cast(rubberband); + if (!rubberbandMCircleFeat) + return; + QPointF centerCoord = rubberbandMCircleFeat->centerCoordinate(RB::CoordinateSystem::Map); + const QVector &radii = rubberbandMCircleFeat->radii(); + + qCritical() << LOGFUNCTION << " SECOND centerCoord " << centerCoord << ", radii = " << radii; + qCritical() << LOGFUNCTION << " SECOND Sector sectorStart " << rubberbandMCircleFeat->sectorStart(); + qCritical() << LOGFUNCTION << " SECOND Sector sectorEnd " << rubberbandMCircleFeat->sectorEnd(); + + }); + +//// QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); +//// QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + + } + + + QVector vertexList = rubberband->coordinateVector(); + bool constrainQuad = false; + bool constrainOrtho = false; + if (rubberbandRect) + { + constrainQuad = rubberbandRect->constrainQuadratic(); + constrainOrtho = rubberbandRect->constrainOrthogonal(); + QPointF geoPoint = rubberband->singleCoordinate(0, RB::CoordinateSystem::Geo); + qDebug() << LOGFUNCTION << "Rectangle center : geo coordinate " << geoPoint; + qDebug() << LOGFUNCTION << "Vertexlist sim coordinates " << vertexList; + d->m_mapInterface->geoCoordinateToViewPortWindowPoint(geoPoint); + } + else + { + for (unsigned int i = 0; i < rubberband->coordinateCount(); ++i) + { + QPointF geoPoint = rubberband->singleCoordinate(i, RB::CoordinateSystem::Geo); + qDebug() << LOGFUNCTION << " " << i << " geo coordinate " << geoPoint; + } + } + + if (rubberbandFreehand) + { + rubberband->deleteMySelf(); + return; + } + + if (rubberbandCorr) + { + QVector bufferVertexList; + + if (rubberbandCorr->bufferPolygon(bufferVertexList, RB::CoordinateSystem::Map)) + { + static SituationMap::RefPointer bufferPolygon; + if (bufferPolygon.valid()) bufferPolygon->removeMySelf(); + + bufferPolygon = d->m_mapInterface->objectFactory().create(QStringLiteral("FunctionDialog"), QStringLiteral("BufferPolygon")); + + if (bufferPolygon.valid()) + { + + bufferPolygon->setIdentifier(QLatin1String("BufferPolygon")); + bufferPolygon->setVisibility(true); + + QColor magenta(Qt::magenta); + QColor magentaAlpha(Qt::magenta); + + magentaAlpha.setAlpha(100); + + bufferPolygon->setLineStyle(LineStyle(magenta, 2, LinePattern::Solid)); + bufferPolygon->setFillStyle(FillStyle(magentaAlpha, FillPattern::Solid)); + + SituationMap::PositionCont posCont; + posCont.reserve(bufferVertexList.size()); + for (const auto &bufferPoint : std::as_const(bufferVertexList)) + { + posCont << Point3D::fromQPointF(bufferPoint); + qDebug() << fixed << qSetRealNumberPrecision(2) << LOGFUNCTION << "buffer coordinate : " << bufferPoint ; + } + + bufferPolygon->setPositionsTranslated(posCont); // use map coordinates to print the coordinate values + bufferPolygon->updateView(); + + + QTimer::singleShot(10000, this, [this]() + { + if (bufferPolygon.valid()) bufferPolygon->removeMySelf(); + bufferPolygon = nullptr; + + this->update(); + }); + } + else qDebug() << LOGFUNCTION << " creating polygon to visualize buffer polygon failed"; + } + else qWarning() << LOGFUNCTION << "RubberbandCorridor::bufferPolygon failed"; + } + + static QString rubberTypeLabel; + + auto slotFunc = [ = ](RB::RubberbandBase * rubberband) + { + qDebug() << LOGFUNCTION << "end of " << rubberTypeLabel << " rubberband"; + + qDebug() << LOGFUNCTION << "Vertexlist sim coordinates " << rubberband->coordinateVector(); + + for (unsigned int i = 0; i < rubberband->coordinateCount(); ++i) + { + QPointF simPoint = rubberband->singleCoordinate(i, RB::CoordinateSystem::Geo); + qDebug() << LOGFUNCTION << " " << i << " sim coordinate " << simPoint; + } + rubberband->deleteMySelf(); + }; + + if (rubberbandRect) + { + if (rubberbandElli) + { + rubberTypeLabel = QLatin1String("ellpise"); + if (rubberbandElli->hasSector()) + { + double rotation=0; + double openAngle = 0; + getRotationAndAngle(rubberbandElli->sectorStart(),rubberbandElli->sectorEnd(), rotation, openAngle); + } + + rubberbandRect = d->m_mapInterface->rubberbandFactory().create(); + } + else + { + rubberTypeLabel = QLatin1String("rectangle"); + rubberbandRect = d->m_mapInterface->rubberbandFactory().create(); + } + if (rubberbandRect == nullptr) + qWarning() << "Creating rubberband " << rubberTypeLabel << " failed"; + else + { + rubberbandRect -> setCoordinateVector(vertexList); + rubberbandRect -> constrainQuadratic(constrainQuad); + rubberbandRect -> constrainOrthogonal(constrainOrtho); + rubberbandRect -> setDrawColor(QColor(QLatin1String("blue"))); + + connect(rubberbandRect, &RB::Rubberband::apply, slotFunc); + connect(rubberbandRect, &RB::Rubberband::abort, slotFunc); + } + } + + bool mRbands = d->m_mapInterface->rubberbandFactory().allowMultipleRubberbands(); + d->m_mapInterface->rubberbandFactory().allowMultipleRubberbands(true); + { + if (rubberbandMFeat) + { + RB::RubberbandMultiPointFeature *rbandMPF = nullptr; + if (rubberbandCorr) + { + RB::RubberbandCorridor *rbandCorr = d->m_mapInterface->rubberbandFactory().create(rubberbandCorr->isEnabledInputBoundary() ? RB::RubberbandCorridor::InputMode::InputBoundary + : RB::RubberbandCorridor::InputMode::InputCenterLine); + if (rbandCorr != nullptr) + { + if (rubberbandCorr->isEnabledInputBoundary()) + { + rbandCorr->setCoordinateVector(vertexList); +// rbandCorr->setDrawColor(QColor(QLatin1String("blue"))); +// rbandCorr->setCorridorFillColor(QColor(QLatin1String("aqua"))); + } + else + { + rbandCorr->setCoordinateVectorWidth(vertexList, rubberbandCorr->corridorWidth()); +// rbandCorr->setDrawColor(QColor(QLatin1String("blue"))); +// rbandCorr->setCorridorFillColor(QColor(QLatin1String("red"))); + } + + rubberTypeLabel = QLatin1String("corridor"); + + connect(rbandCorr, &RB::Rubberband::apply, slotFunc); + connect(rbandCorr, &RB::Rubberband::abort, slotFunc); + } + else qWarning() << "Creating rubberband corridor failed"; + } + else if (rubberbandSmooth) + { + RB::RubberbandSmoothLine *rbandSmooth = d->m_mapInterface->rubberbandFactory().create(); + + if (rbandSmooth != nullptr) + { + rbandMPF = rbandSmooth; + rubberTypeLabel = QLatin1String("smooth line"); + } + else qWarning() << "Creating rubberband smooth line failed"; + } + else if (rubberbandArrow) + { + RB::RubberbandArrow *rbandArrow = d->m_mapInterface->rubberbandFactory().create(); + + if (rbandArrow != nullptr) + { + rbandArrow->setCoordinateVectorArrow(vertexList, rubberbandArrow->arrowHalfWidth(), rubberbandArrow->arrowLength()); + rbandArrow->setDrawColor(QColor(QLatin1String("purple"))); + + rubberTypeLabel = QLatin1String("arrow"); + + connect(rbandArrow, &RB::Rubberband::apply, slotFunc); + connect(rbandArrow, &RB::Rubberband::abort, slotFunc); + } + else qWarning() << "Creating rubberband arrow failed"; + } + else + { + rbandMPF = d->m_mapInterface->rubberbandFactory().create(); + + rubberTypeLabel = QLatin1String("multi point feature"); + if (rbandMPF == nullptr) + qWarning() << "Creating rubberband multi point feature failed"; + } + + if (rbandMPF != nullptr) + { + rbandMPF -> setCoordinateVector(vertexList); + rbandMPF -> enableClosed(rubberbandMFeat->isClosedEnabled()); + rbandMPF -> setDrawColor(QColor(QLatin1String("maroon"))); + rbandMPF -> setFillColor(QColor(QLatin1String("lime"))); + rbandMPF -> setPreviewSidc(rubberbandMFeat->previewSidc()); + + connect(rbandMPF, &RB::Rubberband::apply, slotFunc); + connect(rbandMPF, &RB::Rubberband::abort, slotFunc); + } + } + if (rubberbandClust) + { + RB::RubberbandPointCluster *rbandClust = d->m_mapInterface->rubberbandFactory().create(); + + if (rbandClust == nullptr) + qWarning() << "Creating rubberband point cluster failed"; + else + { + rbandClust -> setCoordinateVector(vertexList); + rbandClust -> setPreviewSidc(rubberbandClust->previewSidc()); + + rubberTypeLabel = QLatin1String("point cluster"); + + connect(rbandClust, &RB::Rubberband::apply, slotFunc); + connect(rbandClust, &RB::Rubberband::abort, slotFunc); + } + } + } + d->m_mapInterface->rubberbandFactory().allowMultipleRubberbands(mRbands); + + rubberband->deleteMySelf(); +} // FunctionDialog::onRubberbandApply + + +void FunctionDialog::onRubberbandAbort(RB::RubberbandBase *rubberband) +{ + qDebug() << LOGFUNCTION; + rubberband->deleteMySelf(); +} // FunctionDialog::onRubberbandAbort + + +void FunctionDialog::onRubberbandDefineEllipse() +{ + static QVector vertexList; + + if (vertexList.isEmpty()) + { + vertexList << s_basePoint.geographic(d->m_objectBasePoint + QPointF(36042.4, 38162.5)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(302120, -46643.1)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(244876, -224735)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(-21201.4, -139929)); + } + + SituationMap::RB::RubberbandEllipse *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + if (rubberband == nullptr) + qWarning() << LOGFUNCTION << "Creating rubberband failed"; + else + { + rubberband->setCoordinateVector(vertexList); + + auto slotFunc = [ = ](RB::RubberbandBase * rubberband) + { + rubberband->deleteMySelf(); + }; + + connect(rubberband, &RB::Rubberband::apply, slotFunc); + connect(rubberband, &RB::Rubberband::abort, slotFunc); + } +} + +void FunctionDialog::getRotationAndAngle(const QPointF §Start, const QPointF §End, double &rotationDegOut, double &openAngleDegOut) +{ + double startAngle = SituationMap::MathUtils::getMathAngle(sectStart) * LtsCoordTrans::rad2deg(); + double endAngle = SituationMap::MathUtils::getMathAngle(sectEnd) * LtsCoordTrans::rad2deg(); + // get start angle value in deg + { + startAngle = startAngle > 0 ? startAngle : (360 - startAngle); + startAngle = startAngle < 360 ? startAngle : std::fmod(startAngle,360.); + } + + // get end angle value in deg + { + endAngle = endAngle > 0 ? endAngle : (360 - endAngle); + } + + // get angle value in deg + { + if ( startAngle > endAngle) + openAngleDegOut = 360 + (endAngle - startAngle); + else + openAngleDegOut = endAngle - startAngle; + } + + // get rotation angle north (in deg) orientated 0° North(12h) + { + rotationDegOut = SituationMap::MathUtils::getAngleDegree2North((startAngle + (openAngleDegOut/2.))); + } + + //qCritical()<<" sDeg:"<m_mapInterface->rubberbandFactory().allowMultipleRubberbands(true); + + RB::RubberbandEllipse *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + d->m_mapInterface->rubberbandFactory().allowMultipleRubberbands(false); + + if (rubberband == nullptr) + qWarning() << "Creating rubberband BoobyTrap failed"; + else + { + if (rubberband->rubberbandIsAlive()) + { + rubberband->constrainQuadratic(true); + rubberband->enableSector(true); + + QList rotation = QList()<<0<<45<<90<<-45<<180<<270<<315<<360; + double angle = 90; + double rot = rotation.at(0); + double aStartRad=0, aEndRad=0; + getSectorStartEnd(angle, rot, aStartRad, aEndRad); +// qCritical()<<"[FORWARD] rotation: "<setSectorStart(std::sin(aStartRad), std::cos(aStartRad)); + rubberband->setSectorEnd(std::sin(aEndRad), std::cos(aEndRad)); + + QObject::connect(rubberband, &RB::Rubberband::apply, this, &FunctionDialog::onRubberbandApply); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + else + { + qWarning() << "Creating rubberband BoobyTrap is not alive"; + rubberband->deleteMySelf(); + } + } + + +} /* FunctionDialog::onRubberbandDefineEllipse */ + + +void FunctionDialog::onRubberbandDefineArrow() +{ + static QVector vertexList; + + if (vertexList.isEmpty()) + { + vertexList << s_basePoint.geographic(d->m_objectBasePoint + QPointF(36042.4, 38162.5)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(302120, -46643.1)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(244876, -224735)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(-21201.4, -139929)); + } + + SituationMap::RB::RubberbandArrow *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + if (rubberband == nullptr) + qWarning() << LOGFUNCTION << "Creating rubberband failed"; + else + { + rubberband->setCoordinateVector(vertexList); + + auto slotFunc = [ = ](RB::RubberbandBase * rubberband) + { + rubberband->deleteMySelf(); + }; + + connect(rubberband, &RB::Rubberband::apply, slotFunc); + connect(rubberband, &RB::Rubberband::abort, slotFunc); + } +} /* FunctionDialog::onRubberbandDefineEllipse */ + + +void FunctionDialog::onRubberbandCheckCoordinates() +{ + static QVector vertexList; + + if (vertexList.isEmpty()) + { + vertexList << s_basePoint.geographic(d->m_objectBasePoint + QPointF(-338163, 168551)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(154770, 168551)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(154770, -71025)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(-338163, -71025)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(36042.4, 38162.5)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(302120, -46643.1)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(244876, -224735)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(-21201.4, -139929)); + } + + if (d->m_mapImpl->coordTranslationFunctor()) + { + bool ok = true; + for (const QPointF &p : std::as_const(vertexList)) + { + const QPointF p1(d->m_mapImpl->coordTranslationFunctor()->geographicToMap(p)); + const QPointF p2(d->m_mapImpl->coordTranslationFunctor()->mapToGeographic(p1)); + if ((fabs(p2.x() - p.x()) > 1) || (fabs(p2.y() - p.y()) > 1)) + { + ok = false; +#ifdef LTS_DEBUG + assert(false); +#endif + } + } + if (!ok) + qWarning() << LOGFUNCTION << "CoordTranslationFunctor is to inaccurate."; + } + + + auto slotFunc = [ = ](RB::RubberbandBase * rubberband) + { + bool ok = true; + + if (((unsigned int)vertexList.size()) == rubberband->coordinateCount()) + { + for (unsigned int i = 0; i < rubberband->coordinateCount(); ++i) + { + QPointF simPoint = rubberband->singleCoordinate(i); + QPointF p = vertexList[i]; + + if ((fabs(simPoint.x() - p.x()) > d->m_mapImpl->pixelSizeMeter()) || (fabs(simPoint.y() - p.y()) > d->m_mapImpl->pixelSizeMeter())) + { + ok = false; +#ifdef LTS_DEBUG + assert(false); +#endif + } + } + } + else qWarning() << LOGFUNCTION << "Rubberband coordinates are modified by user, skip coordinate test."; + rubberband->deleteMySelf(); + + if (!ok) + qWarning() << LOGFUNCTION << "Rubberband coordinates may be changed without editing."; + }; + + RB::RubberbandMultiPointFeature *rband = d->m_mapInterface->rubberbandFactory().create(); + + if (rband == nullptr) + qWarning() << "Creating rubberband multi point failed"; + else + { + rband->setCoordinateVector(vertexList); + + connect(rband, &RB::Rubberband::apply, slotFunc); + connect(rband, &RB::Rubberband::abort, slotFunc); + } +} /* FunctionDialog::onRubberbandCheckCoordinates */ + + + + +#include "state_icon_a.xpm" +#include "state_icon_b.xpm" +#include "state_icon_c.xpm" + +void FunctionDialog::onRubberbandStateSelect() +{ + static QVector vertexList; + + if (vertexList.isEmpty()) + { + vertexList << s_basePoint.geographic(d->m_objectBasePoint + QPointF(-338163, 168551)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(154770, 168551)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(154770, -71025)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(-338163, -71025)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(36042.4, 38162.5)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(302120, -46643.1)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(244876, -224735)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(-21201.4, -139929)); + } + + RB::RubberbandStateSelect *rband = d->m_mapInterface->rubberbandFactory().create(); + + if (rband) + { + rband -> setHandleFillColor(QColor(0, 0, 0, 0)); + rband -> setHandleSelectionColor(QColor(255, 0, 0, 100)); + + QVector stateVector; + QVector itemVector; + + stateVector << RB::RubberbandStateSelect::State('A', QLatin1String("Test State A"), QPixmap(stateIconA_xpm)) + << RB::RubberbandStateSelect::State('B', QLatin1String("Test State B"), QPixmap(stateIconB_xpm)) + << RB::RubberbandStateSelect::State('C', QLatin1String("Test State C"), QPixmap(stateIconC_xpm)); + + quint32 id = 815; + itemVector.reserve(vertexList.size()); + for (const QPointF &p : std::as_const(vertexList)) + { + itemVector << RB::RubberbandStateSelect::StateItem(id, QStringLiteral("Item %1").arg(id), p, 'A'); + ++id; + } + + if (rband->setStateItems(stateVector, itemVector)) + { + auto slotFunc = [ = ](RB::RubberbandBase * rubberband) + { + rubberband->deleteMySelf(); + }; + connect(rband, &RB::Rubberband::apply, slotFunc); + connect(rband, &RB::Rubberband::abort, slotFunc); + } + else + { + rband->deleteMySelf(); + qWarning() << "Setting item states to rubberband state select failed."; + } + } + else + qWarning() << "Creating rubberband state select"; + +} /* FunctionDialog::onRubberbandStateSelect */ + + +void FunctionDialog::onRubberbandTwoInOne() +{ + qCritical() << " onRubberbandTwoInOne..."; + + d->m_mapInterface->rubberbandFactory().allowMultipleRubberbands(false); + + static QVector s_vertexList; + s_vertexList.clear(); + + + const auto onApply2 = [](RB::RubberbandBase *rubberband) -> void + { + qCritical() << " onRubberbandTwoInOne onApply 2 "; + + RB::RubberbandEllipse *rubberbandElli = dynamic_cast(rubberband); + if (!rubberband) + { + qCritical() << " onRubberbandTwoInOne dynamic_cast failed"; + return; + } + + if (!rubberbandElli) + { + qCritical() << " onRubberbandTwoInOne no Rubberband!"; + return; + } + + QVector coordinateVector = rubberbandElli->coordinateVector(); + qCritical() << " onRubberbandTwoInOne coordinateVector = " << coordinateVector; + + QVector data = rubberbandElli->rectangleVerticies(); + const LtsCoordTrans::UtmCoordinate p0(LtsCoordTrans::UtmCoordinate::fromGeographic(data.at(0))); + const LtsCoordTrans::UtmCoordinate p1(LtsCoordTrans::UtmCoordinate::fromGeographic(data.at(2), p0.zone)); + const double radius = fabs((p0.x - p1.x) / 2); + qCritical() << " onRubberbandTwoInOne radius 2 = " << radius; + + }; // onApply2 + + + + const auto onApply1 = [this, onApply2](RB::RubberbandBase *rubberband) -> void + { + qCritical() << " onRubberbandTwoInOne onApply 1 "; + + RB::RubberbandMultiEllipseFeature *rubberbandElli = dynamic_cast(rubberband); + if (!rubberband) + { + qCritical() << " onRubberbandTwoInOne dynamic_cast failed"; + return; + } + + QPointF centerCoord = rubberbandElli->centerCoordinate(RB::CoordinateSystem::Map); + const QVector &radii = rubberbandElli->radii(); + + qCritical() << LOGFUNCTION << " FRIST centerCoord " << centerCoord + << " - radii = " << radii; + + double radiusMax = radii.last().x(); + + // 'save' for following rubberband: + s_vertexList = rubberbandElli->coordinateVector(); + + + // + // create SECOND RB + + // + QTimer::singleShot(250, this, [this, onApply2, radiusMax] + { + RB::RubberbandEllipse *rubberband2 = d->m_mapInterface->rubberbandFactory().create(); + if (rubberband2->rubberbandIsAlive()) + { + QObject::connect(rubberband2, &RB::Rubberband::apply, onApply2); + QObject::connect(rubberband2, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + else + { + qWarning() << "Creating rubberband 2 ellipse is not alive"; + rubberband2->deleteMySelf(); + } + + rubberband2->setAutoDelete(true); + + rubberband2->buildFromRectangleParamter(s_vertexList.first(), + radiusMax * 2, radiusMax * 2, + QPointF(1, 0)); +// rubberband2->setCoordinateVector(s_vertexList); + + rubberband2->minWidth(2*radiusMax); + rubberband2->maxWidth(2*radiusMax); + rubberband2->minHeight(2*radiusMax); + rubberband2->maxHeight(2*radiusMax); + + rubberband2->enableSector(true); + rubberband2->setSectorAngleFixed(true); + rubberband2->setSectorStart(0.75,0.75); + rubberband2->setSectorEnd(0.75,-0.75); + rubberband2->constrainQuadratic(true); +// rubberband2->constrainOrthogonal(true); + + rubberband2->setLineHandleWidth(2); + + }); + }; // onApply1 + + // + // 'main' + // + RB::RubberbandMultiEllipseFeature *rubberband = d->m_mapInterface->rubberbandFactory().create(); + + d->m_mapInterface->rubberbandFactory().allowMultipleRubberbands(true); + + if (!rubberband) + qWarning() << "Creating rubberband ellipse failed"; + else + { + rubberband->setAutoDelete(true); + rubberband->minimumRadiusCount(2); + rubberband->maximumRadiusCount(2); + rubberband->constrainQuadratic(true); + + if (rubberband->rubberbandIsAlive()) + { + QObject::connect(rubberband, &RB::Rubberband::apply, onApply1); + QObject::connect(rubberband, &RB::Rubberband::abort, this, &FunctionDialog::onRubberbandAbort); + } + else + { + qWarning() << "Creating rubberband 1 ellipse is not alive"; + rubberband->deleteMySelf(); + } + } + + +} + + +void FunctionDialog::onRectCheckCoordinates() +{ + static QVector vertexList; + + if (vertexList.isEmpty()) + { + vertexList << s_basePoint.geographic(d->m_objectBasePoint + QPointF(-338163, 168551)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(154770, 168551)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(154770, -71025)) + << s_basePoint.geographic(d->m_objectBasePoint + QPointF(-338163, -71025)); + } + + auto slotFunc = [ = ](RB::RubberbandBase * rubberband) + { + bool ok = true; + RB::RubberbandRectangle *rband = dynamic_cast(rubberband); + + if (rband == nullptr) return; + + QVector rectangleCoords = rband->rectangleVerticies(); + for (int i = 0; i < rectangleCoords.size(); ++i) + { + const QPointF &simPoint = rectangleCoords[i]; + const QPointF &p = vertexList[i]; + + if ((fabs(simPoint.x() - p.x()) > d->m_mapImpl->pixelSizeMeter()) || (fabs(simPoint.y() - p.y()) > d->m_mapImpl->pixelSizeMeter())) + ok = false; + } + + if (!rband->rectangleIsValid()) + qWarning() << LOGFUNCTION << "Rectangle rubberband has been initialized with invalid coordinates"; + else + { + if (!ok) qWarning() << LOGFUNCTION << "Rubberband coordinates changed without editing."; + } + + rubberband->deleteMySelf(); + }; // slotFunc + + RB::RubberbandRectangle *rband = d->m_mapInterface->rubberbandFactory().create(); + + if (rband == nullptr) + qWarning() << "Creating rubberband rectangle failed"; + else + { + rband->setCoordinateVector(vertexList); + + connect(rband, &RB::Rubberband::apply, slotFunc); + connect(rband, &RB::Rubberband::abort, slotFunc); + } +} /* FunctionDialog::onRectCheckCoordinates */ + + + + +void FunctionDialog::addToolsTab() +{ + QWidget *w = new QWidget(); + + QVBoxLayout *mainLayout = new QVBoxLayout(w); + QGridLayout *layout = new QGridLayout(); + mainLayout->addLayout(layout); + mainLayout->addStretch(); + + d->m_tabwidget->addTab(w, QLatin1String("Tools - ObjIndicator, measure, Location")); + + //----------------------------------- + // createObjectIndicator + //----------------------------------- + QPushButton *pbObjectIndicator = new QPushButton(QLatin1String("Object Indicator")); + layout->addWidget(pbObjectIndicator); + connect(pbObjectIndicator, &QPushButton::clicked, + this, &FunctionDialog::onObjectIndicator); + + + //----------------------------------- + // createMeasurementButton + //----------------------------------- + QPushButton *pbCreateMeasurement = new QPushButton(QLatin1String("measure length")); + layout->addWidget(pbCreateMeasurement); + connect(pbCreateMeasurement, &QPushButton::clicked, + this, &FunctionDialog::onMeasureLength); + + + //----------------------------------- + // LocationDisplay + //----------------------------------- + QGridLayout *locationDisplayLayout = new QGridLayout(); + locationDisplayLayout->setObjectName(QLatin1String("LocationDisplayLayout")); + layout->addLayout(locationDisplayLayout, layout->rowCount(), 0); + + //----------------------------------- + // startLocationDisplay + //----------------------------------- + QPushButton *pbstartLocationDisplay = new QPushButton(QLatin1String("start LocationDisplay")); + locationDisplayLayout->addWidget(pbstartLocationDisplay, 0, 0); + connect(pbstartLocationDisplay, &QPushButton::clicked, + this, &FunctionDialog::onStartLocationDisplay); + //----------------------------------- + // stopLocationDisplay + //----------------------------------- + QPushButton *pbstopLocationDisplay = new QPushButton(QLatin1String("stop LocationDisplay")); + locationDisplayLayout->addWidget(pbstopLocationDisplay, 0, 1); + connect(pbstopLocationDisplay, &QPushButton::clicked, + this, &FunctionDialog::onStopLocationDisplay); +} + +void FunctionDialog::addViewshedTab() +{ + QWidget *w = new QWidget(); + + QVBoxLayout *mainLayout = new QVBoxLayout(w); + QGridLayout *layout = new QGridLayout(); + mainLayout->addLayout(layout); + mainLayout->addStretch(); + + d->m_tabwidget->addTab(w, QLatin1String("Tool Viewshed")); + + //----------------------------------- + // createViewshedButtons + //----------------------------------- + QPushButton *pbLoadFile = new QPushButton(QLatin1String(" pbLoadFile")); + layout->addWidget(pbLoadFile); + connect(pbLoadFile, &QPushButton::clicked, + this, &FunctionDialog::onLoadFile); + +#ifdef USE_LOCAL_SERVER + { + QPushButton *pbStartLocalService = new QPushButton(QLatin1String(" Start LocalService")); + layout->addWidget(pbStartLocalService); + connect(pbStartLocalService, &QPushButton::clicked, + this, &FunctionDialog::onStartLocalService); + pbStartLocalService->setStyleSheet(QLatin1String("QPushButton {background-color: gray;}")); + + QPushButton *pbStopLocalService = new QPushButton(QLatin1String(" Stop LocalService")); + layout->addWidget(pbStopLocalService); + connect(pbStopLocalService, &QPushButton::clicked, + this, &FunctionDialog::onStopLocalService); + + QPushButton *pbStartViewshedSvc = new QPushButton(QLatin1String(" Start ViewshedSvc")); + layout->addWidget(pbStartViewshedSvc); + connect(pbStartViewshedSvc, &QPushButton::clicked, + this, &FunctionDialog::onStartViewshedSvc); + pbStartViewshedSvc->setStyleSheet(QLatin1String("QPushButton {background-color: gray;}")); + + QPushButton *pbStopViewshedSvc = new QPushButton(QLatin1String(" Stop ViewshedSvc")); + layout->addWidget(pbStopViewshedSvc); + connect(pbStopViewshedSvc, &QPushButton::clicked, + this, &FunctionDialog::onStopViewshedSvc); + } + + + if (!d->m_viewshedDistance) + { + d->m_viewshedDistance = new QLineEdit(this); + layout->addWidget(d->m_viewshedDistance); + d->m_viewshedDistance->setText(QLatin1String("5123")); + } + + QPushButton *pbCreateViewshedIF = new QPushButton(QLatin1String("create viewshed interactive - at Interface")); + layout->addWidget(pbCreateViewshedIF); + connect(pbCreateViewshedIF, &QPushButton::clicked, + this, &FunctionDialog::onViewshedInteractive); + + QPushButton *pbCreateViewshedEsri = new QPushButton(QLatin1String("create viewshed - ESRI")); + layout->addWidget(pbCreateViewshedEsri); + connect(pbCreateViewshedEsri, &QPushButton::clicked, + this, &FunctionDialog::onViewshed); +#endif // USE_LOCAL_SERVER + + QPushButton *pbCreateViewshed = new QPushButton(QLatin1String("create viewshed - SightFan")); + layout->addWidget(pbCreateViewshed); + connect(pbCreateViewshed, &QPushButton::clicked, + this, &FunctionDialog::onViewshedSightFan); + + QFormLayout * sightFanLayout = new QFormLayout(); + + mainLayout->addLayout(sightFanLayout); + if (!d->m_sightFanObserverHeight) + { + d->m_sightFanObserverHeight = new QLineEdit(this); + d->m_sightFanTargetHeight = new QLineEdit(this); + + d->m_sightFanObserverHeight->setValidator(new QDoubleValidator(d->m_sightFanObserverHeight)); + d->m_sightFanTargetHeight->setValidator(new QDoubleValidator(d->m_sightFanTargetHeight)); + + d->m_sightFanObserverHeight->setText(QStringLiteral("2.0")); + d->m_sightFanTargetHeight->setText(QStringLiteral("2.0")); + + QPushButton *pbCreateSightFan = new QPushButton(QStringLiteral("Create Sight Fan"), this); + + connect(pbCreateSightFan, &QPushButton::clicked, this, &FunctionDialog::onSightFan ); + + sightFanLayout->addRow(QStringLiteral("Target Height"), d->m_sightFanObserverHeight); + sightFanLayout->addRow(QStringLiteral("Observer Height"), d->m_sightFanTargetHeight); + sightFanLayout->addWidget(pbCreateSightFan); + + } +} + +void SituationMap::FunctionDialog::addOverlayPlanTab() +{ + QWidget *w = new QWidget(); + + QVBoxLayout *mainLayout = new QVBoxLayout(w); + QGridLayout *layout = new QGridLayout(); + mainLayout->addLayout(layout); + mainLayout->addStretch(); + + d->m_tabwidget->addTab(w, QLatin1String("Overlay/Plan")); + + + const auto slOverlays = { + QLatin1String("myplan"), + QLatin1String("myplan2") + }; + + int row = 0; + int key = 4711; +// layout->addWidget(new QLabel(QLatin1String("onOff")), row, 0); +// layout->addWidget(new QLabel(QLatin1String("load plan")), row, 1); +// layout->addWidget(new QLabel(QLatin1String("remove plan")), row, 2); + for (const auto &overlayName : slOverlays) + { + ++key; + ++row; + int col = 0; + QCheckBox *cbPlan = new QCheckBox(overlayName); + cbPlan->setProperty("key", key); + layout->addWidget(cbPlan, row, col); + connect(cbPlan, &QPushButton::clicked, + this, &FunctionDialog::onOverlayVisiChanged); + + ++col; + QPushButton *pbLoadPlan = new QPushButton(QLatin1String("load ") + overlayName); + pbLoadPlan->setProperty("name", overlayName); + pbLoadPlan->setProperty("key", key); + layout->addWidget(pbLoadPlan, row, 1); + connect(pbLoadPlan, &QPushButton::clicked, + this, &FunctionDialog::onLoadPlan); + + ++col; + QPushButton *pbRemovePlan = new QPushButton(QLatin1String("remove ") + overlayName); + pbRemovePlan->setProperty("key", key); + layout->addWidget(pbRemovePlan, row, 2); + connect(pbRemovePlan, &QPushButton::clicked, + this, &FunctionDialog::onRemovePlan); + } // Q_FOREACH + + // Remove all + ++row; + QPushButton *pbRemoveAll = new QPushButton(QLatin1String("Remove all overlays")); + layout->addWidget(pbRemoveAll, row, 0); + connect(pbRemoveAll, &QPushButton::clicked, + this, &FunctionDialog::onRemoveAllPlans); +} + +void FunctionDialog::addIconRenderTab() +{ + QWidget *w = new QWidget(); + + QVBoxLayout *mainLayout = new QVBoxLayout(w); + QGridLayout *layout = new QGridLayout(); + mainLayout->addLayout(layout); + mainLayout->addStretch(); + + d->m_tabwidget->addTab(w, QLatin1String("Icon Demo")); + + //----------------------------------- + // create button + //----------------------------------- + LtsWidgets::LtsDragIcon *dummyIcon = new LtsWidgets::LtsDragIcon(this); + layout->addWidget(dummyIcon); + + dummyIcon->setText(QLatin1String("dummyIcon")); + + QImage dummyImage(100, 100, QImage::Format_ARGB32); + { + dummyImage.fill(Qt::transparent); + QPainter painter(&dummyImage); + painter.setBrush(QBrush(Qt::gray)); + painter.setPen(Qt::NoPen); + painter.drawEllipse(dummyImage.rect()); + painter.setPen(QPen(Qt::red)); + painter.drawText(dummyImage.rect(), Qt::AlignCenter, QLatin1String("dummyIcon")); + painter.end(); + } + + dummyIcon->setIcon(QIcon(QPixmap::fromImage(dummyImage))); + + // show template-based approach : + // (is executed by FunctionDialog::addIconRenderTab() ) + { +// AdvancedSymbology::SymbolResultWatcherQWidgetBase *dummyIconSibling = +// new AdvancedSymbology::SymbolResultWatcherQWidget(dummyIcon); + QSharedPointer dummyIconSibling (new AdvancedSymbology::SymbolResultWatcherQWidget(dummyIcon)); + + AdvancedSymbology::SymbolDictionary &dict = d->m_mapImpl->symbolDictionary(); + const QString sidc(AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10051000001213030000")); + + // option a + // dummyIconSibling->setSymbolResult(dict.findPreview(sidc)); + + // option b + AdvancedSymbology::SymbolDataToRender symDataToRender(sidc); + symDataToRender.injury = AdvancedSymbology::Injury::TotalKill; + symDataToRender.injuryColor = Qt::blue; + dummyIconSibling->setSymbolResult(dict.findImage(symDataToRender)); + + // option c + // dummyIconSibling->setSymbolResult(dict.findBaseSymbol(sidc)); + } + // : show template-based approach +} + +void FunctionDialog::addPortalTab() +{ + qDebug() << LOGFUNCTION ; + + QWidget *w = new QWidget(); + + QVBoxLayout *mainLayout = new QVBoxLayout(w); + QGridLayout *layout = new QGridLayout(); + mainLayout->addLayout(layout); + mainLayout->addStretch(); + + d->m_tabwidget->addTab(w, QLatin1String("Portal")); + + //----------------------------------- + // create button + //----------------------------------- + QPushButton *pbLoadPortal = new QPushButton(QLatin1String("load Portal")); + layout->addWidget(pbLoadPortal); + connect(pbLoadPortal, &QPushButton::clicked, this, &FunctionDialog::onPortal); + +} + +void FunctionDialog::addMovePointLuxemburgTab() +{ + qDebug() << LOGFUNCTION ; + + QWidget *w = new QWidget(); + + QVBoxLayout *mainLayout = new QVBoxLayout(w); + QGridLayout *layout = new QGridLayout(); + mainLayout->addLayout(layout); + mainLayout->addStretch(); + + d->m_tabwidget->addTab(w, QLatin1String("MovePoint")); + + //----------------------------------- + // create button + //----------------------------------- + QPushButton *pbMovePoint = new QPushButton(QLatin1String("MovePoint")); + layout->addWidget(pbMovePoint); + connect(pbMovePoint, &QPushButton::clicked, this, &FunctionDialog::onMovePoint); +} + +void FunctionDialog::onPortal() +{ + Portal *portal = new Portal(this); + + bool init = portal->init(); + qCritical() << " portal.init = " << init; + + // bool fetchData = portal->fetchData(); + // qCritical() << " portal.fetchData = " << fetchData; +} + +void FunctionDialog::onMovePoint() +{ + const int incrementX = 1000; + qCritical() << " move marker from Center to left (arcording UTM), incrementX [m]:" << incrementX; + + const QPointF centerGeo = d->m_mapImpl->centerPoint(); + const LtsCoordTrans::UtmCoordinate centerUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(centerGeo); + + qCritical() << "center " << centerGeo + << " # utm " << centerUtm.x << " ; " << centerUtm.y<< " # utm " << centerUtm.mgrs(); + + + PositionCont posContIncrement; + posContIncrement.reserve(10); + + for (int i=0; i< 10; ++i) + { + LtsCoordTrans::UtmCoordinate movedCoord = centerUtm; + movedCoord.x -= i*incrementX; + + qCritical() << qSetRealNumberPrecision(8) << " # UTM " << movedCoord.x << " ; " << movedCoord.y << " # utm " << movedCoord.mgrs(); + + { + LtsCoordTrans::UtmCoordinate reverse = LtsCoordTrans::UtmCoordinate::fromGeographic(movedCoord.geographic()); + qCritical() << qSetRealNumberPrecision(8) << " # rev " << reverse.x << " ; " << reverse.y << " # utm " << reverse.mgrs(); + + // diff zw. movedCoord & reverse: + QPointF diff = movedCoord.geographic() - reverse.geographic(); + qCritical() << qSetRealNumberPrecision(8) << " diff " << diff; + } + + appendQPointF(posContIncrement, movedCoord.geographic()); + } + + qCritical() << " reCreate Coord every step **********+"; + + // reCreate Coord every step + PositionCont posContRelative; + posContRelative.reserve(10); + + LtsCoordTrans::UtmCoordinate incrementedCoord = centerUtm; + + QPointF geo = incrementedCoord.geographic(); + + for (int i=0; i< 10; ++i) + { + LtsCoordTrans::UtmCoordinate coord = LtsCoordTrans::UtmCoordinate::fromGeographic(geo); + + qCritical() << qSetRealNumberPrecision(8) << " # UTM " << coord.x << " ; " << coord.y << " # utm " << coord.mgrs(); + appendQPointF(posContRelative, geo); + + coord.x -= incrementX; + geo = coord.geographic(); + } + + { + RefPointer polyline; + + polyline = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("PolyLINE_Lux1Increment")); + if (!polyline) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + polyline->setIdentifier(QLatin1String("Polyline-Luxemburg")); + LineStyle lineStyle(Qt::red, 5, LinePattern::Solid); + polyline->setLineStyle(lineStyle); + polyline->setPositions(posContIncrement); + bool success = polyline->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + } + + { + RefPointer polyline; + + polyline = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("PolyLINE_Lux2Relative")); + if (!polyline) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + polyline->setIdentifier(QLatin1String("Polyline-Luxemburg")); + LineStyle lineStyle(Qt::blue, 2, LinePattern::Dash); + polyline->setLineStyle(lineStyle); + polyline->setPositions(posContRelative); + bool success = polyline->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + } +} + + +void FunctionDialog::onPolygonCreate() +{ + qDebug() << LOGFUNCTION ; + + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_polygon)); + + d->m_polygon = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("PolyGON_Id")); + + if (!d->m_polygon) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_polygon->setIdentifier(QLatin1String("Polygon-NotOutLined")); + + d->m_polygon->setVisibility(d->m_visibility); + d->m_polygon->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_polygon->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_polygon->setSelected(d->m_selected); + + TextStyle textStyle(Qt::magenta); + LineStyle lineStyle(Qt::blue, 2, LinePattern::Solid); + FillStyle fillStyle(Qt::red, FillPattern::ForwardDiagonal); + + d->m_polygon->setTextStyle(textStyle); + d->m_polygon->setLineStyle(lineStyle); + d->m_polygon->setFillStyle(fillStyle); + + + // set additional values: + SituationMap::PositionCont posCont; + QPointF offset = d->m_objectBasePoint + QPointF(90000, 150000); + + int extent = 100000; + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(0, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(0, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(-extent / 2, extent / 2))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(0, extent))); + + std::cout << " posCont=" << posCont.size() << std::endl; + + d->m_polygon->setPositions(posCont); + + bool success = d->m_polygon->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_polygon->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " Polygon success = " << success << std::endl; + std::cout << " Polygon dump: " << (*d->m_polygon) << std::endl; + + +// QTimer* timer = new QTimer(this); +// connect(timer, &QTimer::timeout, this, &FunctionDialog::onDangerAreaUpdate); +// timer->setSingleShot(true); +// timer->start(2000); + +} + +void FunctionDialog::onPolylineCreate() +{ + qDebug() << LOGFUNCTION ; + + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_polyline)); + + d->m_polyline = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("PolyLINE_Id")); + + if (!d->m_polyline) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_polyline->setIdentifier(QLatin1String("Polyline-NotOutLined")); + + d->m_polyline->setVisibility(d->m_visibility); + d->m_polyline->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_polyline->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_polyline->setSelected(d->m_selected); + + TextStyle textStyle(Qt::darkGreen); + LineStyle lineStyle(Qt::red, 5, LinePattern::Solid); + FillStyle fillStyle(Qt::blue, FillPattern::DiagonalCross); + + d->m_polyline->setTextStyle(textStyle); + d->m_polyline->setLineStyle(lineStyle); + + + // set additional values: + SituationMap::PositionCont posCont; + QPointF offset = d->m_objectBasePoint + QPointF(100000, 150000); + + int extent = 100000; + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(0, extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent / 2, extent / 2))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(0, 0))); + + std::cout << " posCont=" << posCont.size() << std::endl; + + d->m_polyline->setPositions(posCont); + + bool success = d->m_polyline->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_polyline->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " Polyline success = " << success << std::endl; + std::cout << " Polyline dump: " << (*d->m_polyline) << std::endl; + + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &FunctionDialog::onPolylineUpdate); + timer->setSingleShot(true); + timer->start(2000); +} + +void FunctionDialog::onPolylineUpdate() +{ + qDebug() << LOGFUNCTION ; + + if (!d->m_polyline) + { + qDebug() << LOGFUNCTION << " no object anymore"; + return; + } + + d->m_polyline->setArrowHeadStart(true); + d->m_polyline->setArrowHeadEnd(true); + + bool success = d->m_polyline->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; +} + +void FunctionDialog::onTextCreate() +{ + qDebug() << LOGFUNCTION ; + + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_text)); + + d->m_text = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("TextId")); + + if (!d->m_text) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_text->setIdentifier(QLatin1String("Text-NotOutLined\nNewLineForPatrick")); + + d->m_text->setVisibility(d->m_visibility); +// d->m_polyline->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); +// d->m_polyline->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_text->setSelected(d->m_selected); + + TextStyle textStyle(Qt::darkRed); + + d->m_text->setTextStyle(textStyle); + + + // set additional values: + QPointF origin = d->m_objectBasePoint + QPointF(90000, 120000); + + std::cout << " origin=" << origin << std::endl; + + origin = s_basePoint.geographic(origin); + d->m_text->setOrigin(Point3D::fromQPointF(origin)); + + bool success = d->m_text->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_text->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " Text success = " << success << std::endl; + std::cout << " Text dump: " << (*d->m_text) << std::endl; + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &FunctionDialog::onTextUpdate); + timer->setSingleShot(true); + timer->start(2000); + +} + +void FunctionDialog::onTextUpdate() +{ + qDebug() << LOGFUNCTION ; + + if (!d->m_text) + { + qDebug() << LOGFUNCTION << " no object anymore"; + return; + } + + d->m_text->rTextStyle().setFontFamily(QLatin1String("Palatino")); // which values are valid????????? + d->m_text->rTextStyle().setHorizontalAlignment(HorizontalAlignment::Left); + std::cout << " Text dump: " << (*d->m_text) << std::endl; + + bool success = d->m_text->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; +} + +void FunctionDialog::onMapObjectIndicatorCreate() +{ + qDebug() << LOGFUNCTION ; + + static SituationMap::UniqueMapId uniqueId = SituationMap::INVALID_MAP_ID; + + if (uniqueId == SituationMap::INVALID_MAP_ID) + { + qDebug() << LOGFUNCTION << " create......"; + + // create and set debugInfo for debugging: + RefPointer moIndi + = d->m_mapInterface->objectFactory(). + create(QLatin1String("Participants"), QLatin1String("MapObjectIndicatorId")); + + if (!moIndi) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + uniqueId = moIndi->uniqueMapId(); + + moIndi->setIdentifier(QLatin1String("MapObjectIndicator")); + + moIndi->setVisibility(true); // was maybe invisible... + moIndi->setMapObjectToPointTo(1); + + bool success = moIndi->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = moIndi->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + } + else + { + qDebug() << LOGFUNCTION << " remove...... (uId=" << uniqueId << ")"; + + RefPointer moIndi = + d->m_mapInterface->objectFactory().get(uniqueId); + if (moIndi.valid()) + { + qDebug() << LOGFUNCTION << " removeMySelf: " << uniqueId; + moIndi->removeMySelf(); + } + } + +} + + +void FunctionDialog::onLayerPerfCreateLayer() +{ + qDebug() << LOGFUNCTION ; + + int layerCount = d->m_layerPerf_leLayer->text().toInt(); + int objCount = d->m_layerPerf_leObject->text().toInt(); + int objPerLayer = objCount / layerCount; + int radius = d->m_layerPerf_leRadius->text().toInt(); + bool withText = d->m_layerPerf_cbText->isChecked(); + + qDebug() << LOGFUNCTION << " LayerCount =" << layerCount; + qDebug() << LOGFUNCTION << " objCount =" << objCount; + qDebug() << LOGFUNCTION << " -> ObjPerLayer =" << objPerLayer; + qDebug() << LOGFUNCTION << " radius =" << radius; + qDebug() << LOGFUNCTION << " withText =" << withText; + + + QStringList layerNames; + LayerFactory &layerFac = d->m_mapImpl->layerFactory(); + + // + // create operational layer: + // + layerNames.reserve(layerCount - 1); + for (int i = 1; i <= layerCount; ++i) + layerNames << QStringLiteral("LayerPerformance_%1").arg(i); + + for (const auto &layerName : std::as_const(layerNames)) + layerFac.addOperationalLayer(layerName, layerName); + + // + // force creation of techn. Layer: + // create on object per layer: + // + + qDebug() << LOGFUNCTION << " Create ..."; + + // B-Agent -> GraphicsLayer only + for (int i = 0; i < layerCount; ++i) + { + QString layername = layerNames[i]; + + for (int j = 0; j < objPerLayer; ++j) + { + QString debugIdStr = QStringLiteral("DebugId_L%1_Obj%2").arg(i).arg(j); + + if (j % 100 == 0) + std::cerr << LOGFUNCTION + << " Layer = " << i + << " Obj = " << j << std::endl; + + + +#define BAGENT +#ifdef BAGENT + RefPointer bAgent + = d->m_mapInterface->objectFactory(). + create(layername, debugIdStr); + + if (!bAgent) + { + qDebug() << LOGFUNCTION << " no object"; + abort(); + // return; + } +// QPointF center = d->m_objectBasePoint + QPointF(10 * j, 10 * i); + QPointF center = d->m_mapImpl->centerPoint(); + + bAgent->setOrigin(Point3D::fromQPointF(center)); + bAgent->setRadius(radius); + + bAgent->setVisibility(d->m_visibility); + bAgent->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + bAgent->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + // if ( ( j%10 == 0) || ( (i==(layerCount-1)) && (j==(objPerLayer-1)) ) ) + { + if (withText) + bAgent->setIdentifier(debugIdStr); + } + + bAgent->updateView(); +#else + RefPointer part + = d->m_mapInterface->objectFactory(). + create(layername, debugIdStr); + + if (!part) + { + qDebug() << LOGFUNCTION << " no object"; + abort(); + return; + } + QPointF center = d->m_objectBasePoint + QPointF(1000 * j, 1000 * i); + + SituationMap::SymbolData symData; + symData.m_sidc = "SFGPUCD---"; + part->setSymbol(symData); + + part->setOrigin(center); + +// if ( ( j%10 == 0) || ( (i==(layerCount-1)) && (j==(objPerLayer-1)) ) ) + { + if (withText) + part->setIdentifier(debugIdStr); + } + + part->updateView(); + +#endif + + + } + } // for + + std::cerr << LOGFUNCTION << " Create ... DONE" << std::endl; + + std::cerr << LOGFUNCTION << " LayerCount =" << layerCount << std::endl; + std::cerr << LOGFUNCTION << " objCount =" << objCount << std::endl; + std::cerr << LOGFUNCTION << " -> ObjPerLayer =" << objPerLayer << std::endl; + std::cerr << LOGFUNCTION << " radius =" << radius << std::endl; + std::cerr << LOGFUNCTION << " withText =" << withText << std::endl; + + + +} /* FunctionDialog::onLayerPerfCreateLayer */ + + + +void FunctionDialog::onGenerateGraphics() +{ + + // + // + // Pay Attention to the current RenderingMode!!!!!!!!!!!!!!!!!! + // + // + static bool done = false; + if (done) + { + qWarning() << "already done"; + return; + } + else + done = true; + + + if (d->m_graphicsLayer) + { + qWarning() << "generate graphics"; + + // SurfacePlacement::Draped is default on V100.0 + //d->m_graphicsLayer->setSceneProperties(Esri::ArcGISRuntime::LayerSceneProperties(Esri::ArcGISRuntime::SurfacePlacement::Draped)); + + const Esri::ArcGISRuntime::SpatialReference &sr = d->m_mapImpl->getEsriSpatialReference(); + +#define TEXT_WKT +#ifdef TEXT_WKT + { + double i = 1000000; // Meter + + QString wkText = QLatin1String("GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]]"); + /* + "PROJCS[\"WGS_1984_Web_Mercator_Auxiliary_Sphere\",\ + GEOGCS[\"GCS_WGS_1984\",\ + DATUM[\"D_WGS_1984\",\ + SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],\ + PRIMEM[\"Greenwich\",0.0],\ + UNIT[\"Degree\",0.0174532925199433]],\ + PROJECTION[\"Mercator_Auxiliary_Sphere\"],\ + PARAMETER[\"False_Easting\",0.0],\ + PARAMETER[\"False_Northing\",0.0],\ + PARAMETER[\"Central_Meridian\",0.0],\ + PARAMETER[\"Standard_Parallel_1\",0.0],\ + PARAMETER[\"Auxiliary_Sphere_Type\",0.0],\ + UNIT[\"Meter\",1.0]]\""); + + */ + + const Esri::ArcGISRuntime::SpatialReference wgs84(wkText); + + +// \brief Projects the given point from EPSG:4326 (WGS-84) to the output spatial reference. +// \param[in] x The x coordinate of the point. +// \param[in] y The y coordinate of the point. +// \param[in] sr The spatial reference to which to project the point. +// \return The projected point. + const Esri::ArcGISRuntime::Viewpoint &vp = + d->m_mapGeoView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + const Esri::ArcGISRuntime::Point &mapCenter = static_cast(vp.targetGeometry()); + + const Esri::ArcGISRuntime::Point &bremenPoint = mapCenter; +// bremenPoint = Esri::ArcGISRuntime::GeometryEngine::project( Esri::ArcGISRuntime::Point(8.897803, 53.054957, Esri::ArcGISRuntime::SpatialReference(4326)), sr); + + // myWKT : + QLatin1String myWkt( + "PROJCS[\"WGS_1984_Web_Mercator_Auxiliary_Sphere\",\ + GEOGCS[\"GCS_WGS_1984\",\ + DATUM[\"D_WGS_1984\",\ + SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],\ + PRIMEM[\"Greenwich\",0.0],\ + UNIT[\"Degree\",0.0174532925199433]],\ + PROJECTION[\"Mercator_Auxiliary_Sphere\"],\ + PARAMETER[\"False_Easting\",1000000.0],\ + PARAMETER[\"False_Northing\",1000000.0],\ + PARAMETER[\"Central_Meridian\",0.0],\ + PARAMETER[\"Standard_Parallel_1\",0.0],\ + PARAMETER[\"Auxiliary_Sphere_Type\",0.0],\ + UNIT[\"Meter\",1.0]]\""); + + Esri::ArcGISRuntime::Point myPoint(i, i, Esri::ArcGISRuntime::SpatialReference(myWkt)); +// bremenPoint = EsriRuntimeQt::GeometryEngine::project(myPoint, sr); + // : myWKT + + // Esri::ArcGISRuntime::Point(lon, lat) + Esri::ArcGISRuntime::Point marseille(5.371113, 43.283954, 0, sr); + Esri::ArcGISRuntime::Point nullPoint(0, 0, 0, sr); + +// nullPoint = marseille; + + + Esri::ArcGISRuntime::SimpleMarkerSymbol redCircle(Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle, Qt::red, 20); + Esri::ArcGISRuntime::Graphic *graphic1 = new Esri::ArcGISRuntime::Graphic(nullPoint, &redCircle); + d->m_graphicsLayer->graphics()->append(graphic1); + + Esri::ArcGISRuntime::SimpleMarkerSymbol blueCircle(Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle, Qt::blue, 10); + Esri::ArcGISRuntime::Graphic *myGraphic = new Esri::ArcGISRuntime::Graphic(bremenPoint, &blueCircle); + d->m_graphicsLayer->graphics()->append(myGraphic); + + Esri::ArcGISRuntime::PolylineBuilder builder(sr); + builder.addPoint(nullPoint); + builder.addPoint(Esri::ArcGISRuntime::Point(bremenPoint.x(), bremenPoint.y(), 100, bremenPoint.spatialReference())); + + Esri::ArcGISRuntime::SimpleLineSymbol lineSym(Esri::ArcGISRuntime::SimpleLineSymbolStyle::Solid, QColor("green"), 5); + + Esri::ArcGISRuntime::Graphic *graphicPolyline = new Esri::ArcGISRuntime::Graphic(builder.toGeometry(), &lineSym); + d->m_graphicsLayer->graphics()->append(graphicPolyline); + + } +#else // WKT + { + //----------------------------------- + // LINE + //----------------------------------- + EsriRuntimeQt::Point point11(0, 0, sr); + EsriRuntimeQt::Point point12(i, i, sr); + + EsriRuntimeQt::SimpleMarkerSymbol blueCircle(Qt::blue, 8, EsriRuntimeQt::SimpleMarkerSymbolStyle::Circle); + EsriRuntimeQt::Graphic *graphic1 = new EsriRuntimeQt::Graphic(point12, blueCircle); + d->m_graphicsLayer->addGraphic(graphic1); + + EsriRuntimeQt::LIST_OF_POINTS lop; + lop.append(point11); + lop.append(point12); + EsriRuntimeQt::Polyline polyline(QList() << lop); + + EsriRuntimeQt::SimpleLineSymbol lineSym; + + EsriRuntimeQt::Graphic *graphicPolyline = new EsriRuntimeQt::Graphic(polyline, lineSym); + d->m_graphicsLayer->addGraphic(graphicPolyline); + + //----------------------------------- + // CIRCLE + //----------------------------------- + int radius = sqrt(2.0 * pow(i, 2)); // Meter + drawEllipse(d->m_graphicsLayer, point11, radius, radius, 5000.0, QColor(230, 139, 200)); + + + // MOVE (GeometryEngine::geodesicMovePoints) + QList plIN, plOUT; + plIN << point11; + int wkid = sr.wkid(); + EsriRuntimeQt::Unit *unit = EsriRuntimeQt::Unit::create(wkid); + qWarning() << "geodesicMovePoints" + << " wkid=" << wkid + // << " unit = " << unit->name() + ; + + plOUT = EsriRuntimeQt::GeometryEngine::geodesicMovePoints(plIN, + sqrt(2.0 * pow(i, 2)), + 45.0, + // nullptr + unit + ); + EsriRuntimeQt::SimpleMarkerSymbol greenDot(Qt::green, 20, EsriRuntimeQt::SimpleMarkerSymbolStyle::Triangle); + greenDot.setAngle(10); + EsriRuntimeQt::Graphic *graphicMoved = new EsriRuntimeQt::Graphic(plOUT.at(0), greenDot); + d->m_graphicsLayer->addGraphic(graphicMoved); + + //----------------------------------- + // CIRCLE II + //----------------------------------- + { + int radius = 10400; // Meter + drawEllipse(d->m_graphicsLayer, point12, radius, radius, radius / 20, Qt::yellow); + } + + + //----------------------------------- + // IMAGE + //----------------------------------- + int widthPixel = 100; + QImage image(widthPixel, widthPixel, QImage::Format_ARGB32); + image.fill(qRgba(0, 0, 0, 0)); + + QPainter p; + QPen pen(Qt::yellow); + pen.setWidth(5); + + p.begin(&image); + p.setPen(pen); + p.drawLine(QPointF(0, 0), QPointF(100, 100)); + p.end(); + + bool success = image.save(QLatin1String("/tmp/dummy.png")); + qWarning() << " success = " << success; + + EsriRuntimeQt::PictureMarkerSymbol pms(image); + EsriRuntimeQt::Point origin(20000, 20000, d->m_graphicsLayer->spatialReference()); + EsriRuntimeQt::Graphic *graphic = new EsriRuntimeQt::Graphic(origin, pms); + + qint64 gid = d->m_graphicsLayer->addGraphic(graphic); + qWarning() << " gid = " << gid; + + + + //----------------------------------- + // TEXT + //----------------------------------- + EsriRuntimeQt::TextSymbol textSymbol; + + textSymbol.setText(QLatin1String("Mein Langer Text")); + + textSymbol.setTextColor(QColor(255, 0, 0)); + textSymbol.setSize(20.0); + textSymbol.setTextOutlineWidth(1.0); + textSymbol.setTextOutlineColor(QColor(255, 0, 0)); + textSymbol.setHorizontalAlignment(EsriRuntimeQt::HorizontalAlignment::Left); + textSymbol.setVerticalAlignment(EsriRuntimeQt::VerticalAlignment::Middle); + + // 2. Graphic + EsriRuntimeQt::Geometry originGeometry(point11); + EsriRuntimeQt::Graphic *textGraphic = new EsriRuntimeQt::Graphic(originGeometry, + textSymbol); + // 3. add graphic + qint64 gidText = d->m_graphicsLayer->addGraphic(textGraphic); + qWarning() << " gidText = " << gidText; + } +#endif // WKT + } + else + { + qWarning() << "no m_graphicsLayer"; + QMessageBox msgBox; + msgBox.setText(QLatin1String("no m_graphicsLayer")); + msgBox.exec(); + } +} + +Esri::ArcGISRuntime::Graphic *FunctionDialog::createCube(const PositionCont &mapPosCont, + int baseHeight, + double objHeight, + const QString &partyNameValue) +{ + if (!d->m_graphicsLayer) + { + qCritical() << " no m_graphicsLayer"; + return nullptr; + } + + const Esri::ArcGISRuntime::SpatialReference &sr = d->m_mapImpl->getEsriSpatialReference(); + Esri::ArcGISRuntime::PolygonBuilder builder(sr, this); + + for (const auto &coord : mapPosCont) + { + Esri::ArcGISRuntime::Point pnt (coord.x(), coord.y(), baseHeight); + builder.addPoint(pnt); + qCritical() << " coord=" << pnt; + } + + Esri::ArcGISRuntime::Polygon poly(builder.toGeometry()); + + const int maxSegmentLength = 100; + Esri::ArcGISRuntime::Geometry geometry = Esri::ArcGISRuntime::GeometryEngine::densifyGeodetic(poly, + maxSegmentLength, + Esri::ArcGISRuntime::LinearUnit::meters(), + Esri::ArcGISRuntime::GeodeticCurveType::Geodesic); + + + Esri::ArcGISRuntime::Graphic *graphic = new Esri::ArcGISRuntime::Graphic(this); + graphic->setGeometry(geometry); + QVariantMap att = graphic->attributes()->attributesMap(); + att[QLatin1String("height")] = QString::number(objHeight); + qCritical() << " height=" << att[QLatin1String("height")]; + if (!partyNameValue.isEmpty()) + att[QLatin1String("PartyName")] = partyNameValue; + graphic->attributes()->setAttributesMap(att); + + return graphic; +} + + +void FunctionDialog::onGenerateCube() +{ + static Esri::ArcGISRuntime::Graphic *s_graphic = nullptr; + if (s_graphic) + { + qWarning() << "removing..."; + if (d->m_graphicsLayer && d->m_graphicsLayer->graphics()) + d->m_graphicsLayer->graphics()->removeOne(s_graphic); + s_graphic->deleteLater(); + s_graphic = nullptr; + return; + } + + d->m_graphicsLayer->setSceneProperties(Esri::ArcGISRuntime::LayerSceneProperties(Esri::ArcGISRuntime::SurfacePlacement::Relative)); + + // Renderer + QColor col = Qt::red; + col.setAlphaF(0.5); + Esri::ArcGISRuntime::SimpleFillSymbol *sls = + new Esri::ArcGISRuntime::SimpleFillSymbol(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, col, this); + + qCritical() << " USE RENDERER"; + Esri::ArcGISRuntime::SimpleRenderer *renderer = new Esri::ArcGISRuntime::SimpleRenderer(sls, this); + Esri::ArcGISRuntime::RendererSceneProperties props = renderer->sceneProperties(); + props.setExtrusionMode(Esri::ArcGISRuntime::ExtrusionMode::BaseHeight); + props.setExtrusionExpression(QLatin1String("[height]")); + renderer->setSceneProperties(props); + + d->m_graphicsLayer->setRenderer(renderer); + +// Esri::ArcGISRuntime::GraphicsOverlay *gOverlay = d->m_mapImpl->getGraphicsLayer3DExtrusion(QLatin1String("AES")); +// qCritical() << " USE RENDERER gOverlay " << gOverlay; +// gOverlay->setRenderer(renderer); + + qCritical() << " USE RENDERER" << (int) props.extrusionMode(); + + // Geometry + int sizeX = 1500; + int sizeY = 50; + + SituationMap::PositionCont posCont; + + LtsCoordTrans::UtmCoordinate local_basePoint; + + // local_basePoint = LtsCoordTrans::UtmCoordinate::fromMgrs(QLatin1String("32UNE000000000000")); // Bremen 32UMD000000000000 + local_basePoint = s_basePoint; + + QPointF offset = d->m_objectBasePoint + QPointF(91000, 77000); + appendQPointF(posCont, local_basePoint.geographic(offset + QPointF(-sizeX, -sizeY))); // ul + appendQPointF(posCont, local_basePoint.geographic(offset + QPointF(-sizeX, sizeY))); + appendQPointF(posCont, local_basePoint.geographic(offset + QPointF( sizeX, sizeY))); + appendQPointF(posCont, local_basePoint.geographic(offset + QPointF( sizeX, -sizeY))); + + SituationMap::PositionsTranslated posTrans; + posTrans.setCoordTransFunctor(d->m_mapImpl->coordTranslationFunctor()); + posTrans.setPositions(posCont); + SituationMap::PositionCont posContTrans = posTrans.positionsTranslated(); + + s_graphic = createCube(posContTrans, 10, 50); + + d->m_graphicsLayer->graphics()->append(s_graphic); +} + +void FunctionDialog::onGenerateCubeArray() +{ + if (!d->m_graphicsLayer) + { + qCritical() << " no m_graphicsLayer"; + return; + } + if (d->m_mapImpl->mode() != Mode::Mode_3D) + return; + + d->m_graphicsLayer->setSceneProperties(Esri::ArcGISRuntime::LayerSceneProperties(Esri::ArcGISRuntime::SurfacePlacement::Relative)); + + // Renderer + { + QColor col = Qt::red; + col.setAlphaF(0.25); + Esri::ArcGISRuntime::SimpleFillSymbol *sls = + new Esri::ArcGISRuntime::SimpleFillSymbol(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, col, this); + + qCritical() << " USE RENDERER"; + Esri::ArcGISRuntime::SimpleRenderer *renderer = new Esri::ArcGISRuntime::SimpleRenderer(sls, this); + Esri::ArcGISRuntime::RendererSceneProperties props = renderer->sceneProperties(); + props.setExtrusionMode(Esri::ArcGISRuntime::ExtrusionMode::BaseHeight); + props.setExtrusionExpression(QLatin1String("[height]")); + renderer->setSceneProperties(props); + + d->m_graphicsLayer->setRenderer(renderer); + } + + const Esri::ArcGISRuntime::SpatialReference &sr = d->m_mapImpl->getEsriSpatialReference(); + + const Esri::ArcGISRuntime::Viewpoint &vp = + d->m_mapGeoView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + const Esri::ArcGISRuntime::Point &mapCenter = static_cast(vp.targetGeometry()); + + + Esri::ArcGISRuntime::Graphic *clipGraphic = nullptr; + { + const double clipOffset = 0.0085; + Esri::ArcGISRuntime::PolygonBuilder builder(sr, this); + builder.addPoint(mapCenter.x(), mapCenter.y()); + builder.addPoint(mapCenter.x()-clipOffset, mapCenter.y() + clipOffset); + builder.addPoint(mapCenter.x()+clipOffset, mapCenter.y() + clipOffset); + clipGraphic = new Esri::ArcGISRuntime::Graphic(builder.toGeometry(), this); +// Esri::ArcGISRuntime::SimpleLineSymbol *sls = +// new Esri::ArcGISRuntime::SimpleLineSymbol(Esri::ArcGISRuntime::SimpleLineSymbolStyle::Solid, Qt::yellow, 5, this); +// clipGraphic->setSymbol(sls); + + + QVariantMap att = clipGraphic->attributes()->attributesMap(); + att[QLatin1String("height")] = QString::number(2); + clipGraphic->attributes()->setAttributesMap(att); + + + d->m_graphicsLayer->graphics()->append(clipGraphic); + } + + + QElapsedTimer elapsedTimer; + elapsedTimer.start(); + + + const int maxRow = 10; + const int maxColumn = 10; + const double offsetSize = 0.001; + + Esri::ArcGISRuntime::PartCollection partCollection(sr); + + for (int row=0; rowattributes()->attributesMap(); +// att[QLatin1String("height")] = QString::number(10); +// graphic->attributes()->setAttributesMap(att); + +// d->m_graphicsLayer->graphics()->append(graphic); + + Esri::ArcGISRuntime::Geometry clippedGeometry = Esri::ArcGISRuntime::GeometryEngine::intersection(polylineBuilder.toPolygon(), + clipGraphic->geometry()); +// qCritical() << " cg" << clippedGeometry.toJson(); + + if (!clippedGeometry.isEmpty()) + { + Esri::ArcGISRuntime::Polygon poly(clippedGeometry); + const Esri::ArcGISRuntime::ImmutablePointCollection pc = poly.parts().part(0).points(); + + Esri::ArcGISRuntime::Part part(sr); + for (const Esri::ArcGISRuntime::Point &pnt : pc) + part.addPoint(pnt); + + // part.addPoint(bottomLeft.x() + 0, bottomLeft.y() + 0); + // part.addPoint(bottomLeft.x() + offsetSize, bottomLeft.y() + 0); + // part.addPoint(bottomLeft.x() + offsetSize, bottomLeft.y() + offsetSize); + // part.addPoint(bottomLeft.x() + 0, bottomLeft.y() + offsetSize); + + partCollection.addPart(&part); + } + + } // for + } // for + + Esri::ArcGISRuntime::Graphic *multiPartGraphic = new Esri::ArcGISRuntime::Graphic(this); + + Esri::ArcGISRuntime::PolygonBuilder builder(sr, this); + builder.setParts(&partCollection); + + const Esri::ArcGISRuntime::Polygon &multipartPolygon = builder.toPolygon(); + multiPartGraphic->setGeometry(multipartPolygon); + + int msec = elapsedTimer.elapsed(); + qCritical() << " XYZ took [msec]: " << msec; + + + QVariantMap att = multiPartGraphic->attributes()->attributesMap(); + att[QLatin1String("height")] = QString::number(10); + multiPartGraphic->attributes()->setAttributesMap(att); + d->m_graphicsLayer->graphics()->append(multiPartGraphic); + +// Esri::ArcGISRuntime::SimpleLineSymbol *sls = +// new Esri::ArcGISRuntime::SimpleLineSymbol(Esri::ArcGISRuntime::SimpleLineSymbolStyle::Solid, Qt::green, 5, this); +// graphic->setSymbol(sls); +} + +void FunctionDialog::onGenerateStackedCubes() +{ + if (!d->m_graphicsLayer) + { + qCritical() << " no m_graphicsLayer"; + return; + } + + d->m_graphicsLayer->setSceneProperties(Esri::ArcGISRuntime::LayerSceneProperties(Esri::ArcGISRuntime::SurfacePlacement::DrapedFlat)); + + // RenderingMode::Dynamic: ok, for V100.0 ; V100.1 + // RenderingMode::Static: no Height in V100.1 + + // Renderer + qCritical() << " USE UniqueValueRenderer"; + Esri::ArcGISRuntime::SimpleFillSymbol *sls; + + const QVector partyList = { + QLatin1String("red"), + QLatin1String("blue"), + QLatin1String("green") + }; + QList uvList; + + for (const auto &party : partyList) + { + QColor col(party); + // col.setAlphaF(0.3); // ---> set opacity of complete layer!!!???!!! + sls = new Esri::ArcGISRuntime::SimpleFillSymbol(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, col, this); + + Esri::ArcGISRuntime::UniqueValue *uv = + new Esri::ArcGISRuntime::UniqueValue(party, + QStringLiteral("Party '%1'").arg(party), + QVariantList() << party, sls, this); + uvList.append(uv); + } // Q_FOREACH + + sls = new Esri::ArcGISRuntime::SimpleFillSymbol(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, Qt::white, this); + + Esri::ArcGISRuntime::UniqueValueRenderer* uniqueRenderer = + new Esri::ArcGISRuntime::UniqueValueRenderer(QLatin1String("UnknownParty"), + sls, // default + QStringList() << QLatin1String("PartyName"), + uvList, this); + + Esri::ArcGISRuntime::RendererSceneProperties props = uniqueRenderer->sceneProperties(); + props.setExtrusionMode(Esri::ArcGISRuntime::ExtrusionMode::BaseHeight); + props.setExtrusionExpression(QLatin1String("[height]")); + uniqueRenderer->setSceneProperties(props); + d->m_graphicsLayer->setRenderer(uniqueRenderer); + + + // Geometry + const auto centerPoints = { + d->m_objectBasePoint + QPointF(92400, 78600), + d->m_objectBasePoint + QPointF(92450, 78600), + d->m_objectBasePoint + QPointF(92450, 78675), + d->m_objectBasePoint + QPointF(93069, 78331) + }; + + SituationMap::PositionCont posCont; + for (const QPointF ¢erPoint : centerPoints) + { + double sizeX = 10; + double sizeY = sizeX; + + for (int i=0; i<3; ++i) + { + sizeX -= 0.01; + sizeY -= 0.01; + + posCont.clear(); + posCont.reserve(4); + appendQPointF(posCont, s_basePoint.geographic(centerPoint + QPointF(-sizeX, -sizeY))); // ul + appendQPointF(posCont, s_basePoint.geographic(centerPoint + QPointF(-sizeX, sizeY))); + appendQPointF(posCont, s_basePoint.geographic(centerPoint + QPointF( sizeX, sizeY))); + appendQPointF(posCont, s_basePoint.geographic(centerPoint + QPointF( sizeX, -sizeY))); + + SituationMap::PositionsTranslated posTrans; + posTrans.setCoordTransFunctor(d->m_mapImpl->coordTranslationFunctor()); + posTrans.setPositions(posCont); + SituationMap::PositionCont posContTrans = posTrans.positionsTranslated(); + const QString &partyNameValue = partyList[i]; + Esri::ArcGISRuntime::Graphic *graphic = createCube(posContTrans, 1000, 1+i, partyNameValue); + + d->m_graphicsLayer->graphics()->append(graphic); + } // for + } // Q_FOREACH +} + +void FunctionDialog::onGenerateGraphicsKristenStrandford() +{ + static bool done = false; + if (done) + { + qWarning() << "already done"; + return; + } + else + done = true; + + if (d->m_graphicsLayer) + { + qWarning() << "onGenerateGraphicsKristenStrandford"; + + // SurfacePlacement::Draped is default on V100.0 + d->m_graphicsLayer->setSceneProperties(Esri::ArcGISRuntime::LayerSceneProperties(Esri::ArcGISRuntime::SurfacePlacement::Absolute)); + + const Esri::ArcGISRuntime::SpatialReference &sr = d->m_mapImpl->getEsriSpatialReference(); + + const Esri::ArcGISRuntime::Viewpoint &vp = + d->m_mapGeoView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + const Esri::ArcGISRuntime::Point &mapCenter = static_cast(vp.targetGeometry()); + + Esri::ArcGISRuntime::Point myLocation = Esri::ArcGISRuntime::Point(mapCenter.x(), mapCenter.y(), 1000, sr); + Esri::ArcGISRuntime::Point skyLocation = Esri::ArcGISRuntime::Point(mapCenter.x(), mapCenter.y(), 99999, sr); + + qCritical() << " myLocation=" << myLocation.toJson() << " z=" << myLocation.z();; + qCritical() << " skyLocation=" << skyLocation.toJson() << " z=" << skyLocation.z(); + + Esri::ArcGISRuntime::PolygonBuilder polylineBuilder(sr, this); + polylineBuilder.addPoint(myLocation); + polylineBuilder.addPoint(skyLocation); + + Esri::ArcGISRuntime::SimpleLineSymbol *sls = + new Esri::ArcGISRuntime::SimpleLineSymbol(Esri::ArcGISRuntime::SimpleLineSymbolStyle::Solid, Qt::red, 5, this); + + Esri::ArcGISRuntime::Graphic *lineGraphic = new Esri::ArcGISRuntime::Graphic(polylineBuilder.toGeometry(), this); + +//#define USE_RENDERER +#ifdef USE_RENDERER + qCritical() << " USE RENDERER"; + d->m_graphicsLayer->setRenderer(new Esri::ArcGISRuntime::SimpleRenderer(sls, this)); +#else + qCritical() << " USE no RENDERER"; + lineGraphic->setSymbol(sls); +#endif // USE_RENDERER + + d->m_graphicsLayer->graphics()->append(lineGraphic); + } +} + +void FunctionDialog::onGenerateUniqueVaueRendererMountingPoints() +{ + qCritical() << " onGenerateUniqueVaueRendererMountingPoints"; + + static QString s_prefix(QStringLiteral("MyXXXXXXXXXXXXXXXXXXXX_VeryLongPrefix_")); + + qCritical() << " USE UniqueValueRenderer"; + Esri::ArcGISRuntime::SimpleMarkerSymbol *sms; + + const QVector partyList = { + QStringLiteral("red"), + QStringLiteral("blue"), + QStringLiteral("blue"), + QStringLiteral("green") + }; + QList uvList; + + for (const auto &party : partyList) + { + QColor col(party); + sms = new Esri::ArcGISRuntime::SimpleMarkerSymbol(Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Diamond, col, 20, this); + +// QString value = party; + QString value = s_prefix+party; + + qCritical() << " XXXXXX value '" << value << "'"; + + Esri::ArcGISRuntime::UniqueValue *uv = + new Esri::ArcGISRuntime::UniqueValue(party, + QStringLiteral("Party '%1'").arg(party), + QVariantList() << value, sms, this); + uvList.append(uv); + } // Q_FOREACH + + // default: + sms = new Esri::ArcGISRuntime::SimpleMarkerSymbol(Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::X, Qt::white, 15, this); + + Esri::ArcGISRuntime::UniqueValueRenderer* uniqueRenderer = + new Esri::ArcGISRuntime::UniqueValueRenderer(QStringLiteral("UnknownParty"), + sms, // default + QStringList() << QStringLiteral("MountingPointFieldName"), + uvList, this); + uniqueRenderer->setObjectName(QStringLiteral("UniqueValueRenderer_onGenerateUniqueVaueRendererMountingPoints")); + d->m_graphicsLayer->setRenderer(uniqueRenderer); + + const auto centerPoints = { + d->m_objectBasePoint + QPointF(91400, 78600), + d->m_objectBasePoint + QPointF(92450, 78600), + d->m_objectBasePoint + QPointF(92450, 78675), + d->m_objectBasePoint + QPointF(93069, 78331) + }; + + for (const QPointF &pppp : centerPoints) + { + QPointF originSim = d->m_objectBasePoint + pppp; + QPointF originGeo = s_basePoint.geographic(originSim); + + SituationMap::PositionCont posCont; + posCont.append(Point3D::fromQPointF(originGeo)); + + SituationMap::PositionsTranslated posTrans; + posTrans.setCoordTransFunctor(d->m_mapImpl->coordTranslationFunctor()); + posTrans.setPositions(posCont); + SituationMap::PositionCont posContTrans = posTrans.positionsTranslated(); + + Point3D p3D = posContTrans.first(); + + Esri::ArcGISRuntime::Point newPnt(p3D.x(), + p3D.y(), + p3D.z()); + + Esri::ArcGISRuntime::Graphic *graphic = new Esri::ArcGISRuntime::Graphic(); + graphic->setGeometry(newPnt); + d->m_graphicsLayer->graphics()->append(graphic); + + QVariantMap rawAttributes = graphic->attributes()->attributesMap(); + rawAttributes[QStringLiteral("MountingPointFieldName")] = QString(s_prefix+QStringLiteral("blue")); + graphic->attributes()->setAttributesMap(rawAttributes); + + qCritical() << " XXXXX set rawAttributes " << rawAttributes; + + } + return; +} + +void FunctionDialog::onGenerate3DGraphicSphereRENDERER() +{ + int instances = 1000; + qCritical() << " onGenerate3DGraphicSphereRENDERER, instances=" << instances; + + // setup Renderer + // Renderer + QColor col = Qt::green; + col.setAlphaF(0.5); + Esri::ArcGISRuntime::SimpleMarkerSceneSymbol *sym = + Esri::ArcGISRuntime::SimpleMarkerSceneSymbol::sphere(col, 25, Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Center); + Q_UNUSED(sym); + + // symbol + static QString resourcePath = SituationMap::getResourcePath() + QDir::separator() + QStringLiteral("3Dmodels") + QDir::separator(); + QUrl url = QUrl(resourcePath + QString(QStringLiteral("SPD/buildings/Bruecke_20m/Bruecke_20m.dae"))); // modelled horizontal + +// QUrl url(QStringLiteral("/tmp/Halbkugel.fbx")); + + const float scale = 4.0; + Esri::ArcGISRuntime::ModelSceneSymbol *modelSceneSymbol = + new Esri::ArcGISRuntime::ModelSceneSymbol(url, scale); // url, scale + + modelSceneSymbol->setHeading(45); + modelSceneSymbol->setRoll(115); + modelSceneSymbol->setPitch(75); + + modelSceneSymbol->setColor(Qt::red); + + + +// Esri::ArcGISRuntime::Symbol* symbol = sym; + Esri::ArcGISRuntime::Symbol* symbol = modelSceneSymbol; + + qCritical() << " USE RENDERER"; + Esri::ArcGISRuntime::SimpleRenderer *renderer = new Esri::ArcGISRuntime::SimpleRenderer(symbol, this); + d->m_graphicsLayer->setRenderer(renderer); + + //----------------------------------------------------- + //----------------------------------------------------- + //----------------------------------------------------- + + const Esri::ArcGISRuntime::Viewpoint &vp = + d->m_mapGeoView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + const Esri::ArcGISRuntime::Point &mapCenter = static_cast(vp.targetGeometry()); + + MEMORY_LOG() << " START"; + + if (!d->m_impactCircleSphere.isEmpty()) + { + qCritical() << " onGenerate3DGraphicSphereRENDERER DESTROY " << instances; + for (Private::object3D_data &data : d->m_impactCircleSphere) + d->m_graphicsLayer->graphics()->removeOne(data.m_graphic.get()); + d->m_impactCircleSphere.clear(); + } + else + { + if (d->m_mapImpl->mode() != Mode::Mode_3D) + return; + + int i = 0; + d->m_impactCircleSphere.resize(instances); + for (Private::object3D_data &data : d->m_impactCircleSphere) + { + data.m_position = Esri::ArcGISRuntime::Point(mapCenter.x(), mapCenter.y()+ (0.001 * i)); + ++i; + + data.m_graphic = std::shared_ptr(new Esri::ArcGISRuntime::Graphic(data.m_position)); +// static int myGraphicCnt = 0; connect(data.m_graphic.get(), &QObject::destroyed, [] () { qCritical() << " myGraphic destroyed cnt=" << ++myGraphicCnt; }); + + d->m_graphicsLayer->graphics()->append(data.m_graphic.get()); + } // for + } + MEMORY_LOG() << " END"; + +} + +void FunctionDialog::onSphereOpacityChangedRENDERER(int opacityValue) +{ + // update Renderer: + Esri::ArcGISRuntime::Renderer* myRenderer = d->m_graphicsLayer->renderer(); + if (!myRenderer) + return; + + Esri::ArcGISRuntime::SimpleRenderer *simpleRenderer = dynamic_cast(myRenderer); + if (!simpleRenderer) + return; + + // Symbol + Esri::ArcGISRuntime::Symbol* mySymbol = simpleRenderer->symbol(); + if (!mySymbol) + return; + + Esri::ArcGISRuntime::SimpleMarkerSceneSymbol *sym = dynamic_cast(mySymbol); + if (!sym) + return; + + QColor col = Qt::green; + col.setAlphaF(opacityValue/100.0); + sym->setColor(col); + +// Esri::ArcGISRuntime::SimpleMarkerSceneSymbol *sym = +// Esri::ArcGISRuntime::SimpleMarkerSceneSymbol::sphere(col, 10, Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Center); + +// simpleRenderer->setSymbol(sym); +} + +void FunctionDialog::onGenerate3DGraphicSphere() +{ + int instances = 1000; + qCritical() << " onGenerate3DGraphicSphere, instances=" << instances; + + const Esri::ArcGISRuntime::Viewpoint &vp = + d->m_mapGeoView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + const Esri::ArcGISRuntime::Point &mapCenter = static_cast(vp.targetGeometry()); + + MEMORY_LOG() << " START"; + + if (!d->m_impactCircleSphere.isEmpty()) + { + qCritical() << " onGenerate3DGraphicSphere DESTROY " << instances; + for (Private::object3D_data &data : d->m_impactCircleSphere) + d->m_graphicsLayer->graphics()->removeOne(data.m_graphic.get()); + d->m_impactCircleSphere.clear(); + return; + } + else + { + d->m_impactCircleSphere.resize(instances); + + int i = 0; + for (Private::object3D_data &data : d->m_impactCircleSphere) + { + data.m_position = Esri::ArcGISRuntime::Point(mapCenter.x(), mapCenter.y()+ (0.00001 * i)); + ++i; + + if (d->m_mapImpl->mode() == Mode::Mode_2D) + { + data.m_graphic = std::shared_ptr(new Esri::ArcGISRuntime::Graphic(data.m_position)); + Esri::ArcGISRuntime::SimpleMarkerSymbol redCircle(Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle, Qt::red, 10); + data.m_graphic->setSymbol(&redCircle); + d->m_graphicsLayer->graphics()->append(data.m_graphic.get()); + } + else if (d->m_mapImpl->mode() == Mode::Mode_3D) + { + // SimpleMarkerSceneSymbol(SimpleMarkerSceneSymbolStyle style, const QColor& color, double height, double width, double depth, SceneSymbolAnchorPosition anchorPosition, QObject* parent = nullptr); + + QColor col = Qt::red; + col.setAlphaF(0.5); + + data.m_graphic = std::shared_ptr(new Esri::ArcGISRuntime::Graphic(data.m_position)); +// static int myGraphicCnt = 0; connect(data.m_graphic.get(), &QObject::destroyed, [] () { qCritical() << " myGraphic destroyed cnt=" << ++myGraphicCnt; }); + + Esri::ArcGISRuntime::SimpleMarkerSceneSymbol *sym = + Esri::ArcGISRuntime::SimpleMarkerSceneSymbol::sphere(col, 25, Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Center, data.m_graphic.get()); +// static int mySymbolCnt = 0; connect(sym, &QObject::destroyed, [] () { qCritical() << " sphere destroyed cnt=" << ++mySymbolCnt; }); + + data.m_graphic->setSymbol(sym); + + d->m_graphicsLayer->graphics()->append(data.m_graphic.get()); + } + } // for + } + MEMORY_LOG() << " END"; +} + +void FunctionDialog::onSphereOpacityChanged(int opacityValue) +{ + for (Private::object3D_data &data : d->m_impactCircleSphere) + { + Esri::ArcGISRuntime::SimpleMarkerSceneSymbol *sym = dynamic_cast(data.m_graphic->symbol()); + if (!sym) + return; + + QColor col = sym->color(); + col.setAlphaF(opacityValue/100.0); + sym->setColor(col); + + data.m_graphic->setSymbol(sym); + } // for +} + +void FunctionDialog::onSphereZValueChanged(int ZValue) +{ + qCritical() << " onSphereZValueChanged -> " << ZValue; + + for (Private::object3D_data &data : d->m_impactCircleSphere) + { + Esri::ArcGISRuntime::Point newPnt(data.m_position.x(), + data.m_position.y(), + data.m_position.z()+ZValue); + data.m_graphic->setGeometry(newPnt); + } // for +} + +// +// 3DGraphic Cone +// +void FunctionDialog::onGenerate3DGraphicCone() +{ + int instances = 1000; + qCritical() << " onGenerate3DGraphicCone, instances=" << instances; + + const Esri::ArcGISRuntime::Viewpoint &vp = + d->m_mapGeoView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + const Esri::ArcGISRuntime::Point &mapCenter = static_cast(vp.targetGeometry()); + + MEMORY_LOG() << " START"; + + if (!d->m_impactCone.isEmpty()) + { + qCritical() << " onGenerate3DGraphicCone DESTROY " << instances; + for (Private::object3D_data &data : d->m_impactCone) + d->m_graphicsLayer->graphics()->removeOne(data.m_graphic.get()); + d->m_impactCone.clear(); + } + else + { + d->m_impactCone.resize(instances); + + int i = 0; + for (Private::object3D_data &data : d->m_impactCone) + { + data.m_position = Esri::ArcGISRuntime::Point(mapCenter.x(), mapCenter.y()+ (0.001 * i)); + ++i; + + if (d->m_mapImpl->mode() == Mode::Mode_2D) + { + Esri::ArcGISRuntime::SimpleMarkerSymbol redCircle(Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle, Qt::red, 10); + Esri::ArcGISRuntime::Graphic *myGraphic = new Esri::ArcGISRuntime::Graphic(data.m_position, &redCircle); + d->m_graphicsLayer->graphics()->append(myGraphic); + } + else if (d->m_mapImpl->mode() == Mode::Mode_3D) + { + // SimpleMarkerSceneSymbol(SimpleMarkerSceneSymbolStyle style, const QColor& color, double height, double width, double depth, SceneSymbolAnchorPosition anchorPosition, QObject* parent = nullptr); + + QColor col = Qt::red; + col.setAlphaF(0.5); + + if (!data.m_graphic) + { + data.m_graphic = std::shared_ptr(new Esri::ArcGISRuntime::Graphic(data.m_position)); + + Esri::ArcGISRuntime::SimpleMarkerSceneSymbol *sym = + Esri::ArcGISRuntime::SimpleMarkerSceneSymbol::cone(col, 2, 1.0, data.m_graphic.get()); // color, diameter, height + + data.m_graphic->setSymbol(sym); + + d->m_graphicsLayer->graphics()->append(data.m_graphic.get()); + } + // update values: + data.m_graphic->setGeometry(data.m_position); + } + } // for + } + + MEMORY_LOG() << " END"; +} + +void FunctionDialog::onConeOpacityChanged(int opacityValue) +{ + for (Private::object3D_data &data : d->m_impactCone) + { + if (!data.m_graphic) + return; + + Esri::ArcGISRuntime::SimpleMarkerSceneSymbol *sym = dynamic_cast(data.m_graphic->symbol()); + if (!sym) + return; + + QColor col = sym->color(); + col.setAlphaF(opacityValue/100.0); + sym->setColor(col); + + data.m_graphic->setSymbol(sym); + } // for +} + +void FunctionDialog::onConeZValueChanged(int ZValue) +{ + qCritical() << " onConeZValueChanged -> " << ZValue; + + for (Private::object3D_data &data : d->m_impactCone) + { + if (!data.m_graphic) + return; + + Esri::ArcGISRuntime::Point newPnt(data.m_position.x(), + data.m_position.y(), + data.m_position.z()+ZValue); + data.m_graphic->setGeometry(newPnt); + } +} + +void FunctionDialog::onConeHeadingChanged(float angle) +{ + qCritical() << " onConeHeadingChanged -> " << angle; + + for (Private::object3D_data &data : d->m_impactCone) + { + if (!data.m_graphic) + return; + + Esri::ArcGISRuntime::SimpleMarkerSceneSymbol *sym = dynamic_cast(data.m_graphic->symbol()); + if (!sym) + return; + + sym->setHeading(angle); + data.m_graphic->setSymbol(sym); + } // for +} + +void FunctionDialog::onConeRollChanged(float angle) +{ + qCritical() << " onConeRollChanged -> " << angle; + + for (Private::object3D_data &data : d->m_impactCone) + { + if (!data.m_graphic) + return; + + Esri::ArcGISRuntime::SimpleMarkerSceneSymbol *sym = dynamic_cast(data.m_graphic->symbol()); + if (!sym) + return; + + sym->setRoll(angle); + data.m_graphic->setSymbol(sym); + } // for +} + +void FunctionDialog::onConePitchChanged(float angle) +{ + qCritical() << " onConePitchChanged -> " << angle; + + for (Private::object3D_data &data : d->m_impactCone) + { + + Esri::ArcGISRuntime::SimpleMarkerSceneSymbol *sym = dynamic_cast(data.m_graphic->symbol()); + if (!sym) + return; + + sym->setPitch(angle); + data.m_graphic->setSymbol(sym); + } // for +} + +void FunctionDialog::on3dModel() +{ + int instances = 100; + + // Graphic: + const Esri::ArcGISRuntime::Viewpoint &vp = + d->m_mapGeoView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + const Esri::ArcGISRuntime::Point &mapCenter = static_cast(vp.targetGeometry()); + + MEMORY_LOG() << " START"; + + // symbol + static QString resourcePath = SituationMap::getResourcePath() + QDir::separator() + QStringLiteral("3Dmodels") + QDir::separator(); + QUrl url = QUrl(resourcePath + QString(QStringLiteral("SPD/buildings/Bruecke_20m/Bruecke_20m.dae"))); // modelled horizontal + + const float scale = 2.0; + Esri::ArcGISRuntime::ModelSceneSymbol *modelSceneSymbol = + new Esri::ArcGISRuntime::ModelSceneSymbol(url, scale); // url, scale + + + if (!d->m_3dModel.isEmpty()) + { + qCritical() << " on3dModel DESTROY " << instances; + for (Private::object3D_data &data : d->m_3dModel) + d->m_graphicsLayer->graphics()->removeOne(data.m_graphic.get()); + d->m_3dModel.clear(); + } + else + { + d->m_3dModel.resize(instances); + + int i = 0; + for (Private::object3D_data &data : d->m_3dModel) + { + if (d->m_mapImpl->mode() != Mode::Mode_3D) + return; + + data.m_position = Esri::ArcGISRuntime::Point(mapCenter.x(), mapCenter.y()+ (0.001 * i)); + ++i; + + if (!data.m_graphic) + { + data.m_graphic = std::shared_ptr(new Esri::ArcGISRuntime::Graphic(data.m_position)); + data.m_graphic->setSymbol(modelSceneSymbol); + d->m_graphicsLayer->graphics()->append(data.m_graphic.get()); + } + // update values: + data.m_graphic->setGeometry(data.m_position); + } // for + } + MEMORY_LOG() << " END"; +} + +void FunctionDialog::on3dModelZValueChanged(int ZValue) +{ + qCritical() << " on3dModelZValueChanged -> " << ZValue; + + for (Private::object3D_data &data : d->m_3dModel) + { + if (!data.m_graphic) + return; + + Esri::ArcGISRuntime::Point newPnt(data.m_position.x(), + data.m_position.y(), + data.m_position.z()+ZValue); + data.m_graphic->setGeometry(newPnt); + } +} + +void FunctionDialog::onGenerate3DGraphicConeUniqueValueRenderer() +{ + qCritical() << " onGenerate3DGraphicConeUniqueValueRenderer"; + + MEMORY_LOG() << " START"; + + const QString layernameAES(QStringLiteral("AES")); + + LayerFactoryImpl &layerFactory = d->m_mapImpl->layerFactoryImpl(); + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = layerFactory.getGraphicsLayer(layernameAES); + + if (!d->m_impactConeViaUniqueValueRenderer.isEmpty()) + { + qCritical() << " onGenerate3DGraphicConeUniqueValueRenderer DESTROY " << d->m_impactConeViaUniqueValueRenderer.size(); + for (Private::object3D_data &data : d->m_impactConeViaUniqueValueRenderer) + graphicsLayer->graphics()->removeOne(data.m_graphic.get()); + d->m_impactConeViaUniqueValueRenderer.clear(); + MEMORY_LOG() << " START"; + return; + } + + QVector typeVec; + typeVec << LayerFactory::UniqueValueDataAesCentral::TYPE::CONE; + typeVec << LayerFactory::UniqueValueDataAesCentral::TYPE::SPHERE; + + QVector colVec; + colVec << Qt::darkMagenta << Qt::darkGreen; + + QVector radiuss; + radiuss << 10 << 1 << 2.5 << 5 << 10 << 20; + + colVec.first().setAlpha(128); + + // init + // Assume that typically the layer does not have a renderer: + + + if (graphicsLayer->renderer()) + { + qCritical() << " onGenerate3DGraphicConeUniqueValueRenderer, there is already a renderer assigned: " << graphicsLayer->renderer()->objectName(); + } else { + QVector uvCont; + + for (const LayerFactory::UniqueValueDataAesCentral::TYPE &type : std::as_const(typeVec)) + { + for (const QColor &col : std::as_const(colVec)) + { + for (float radius : std::as_const(radiuss)) + { + LayerFactory::UniqueValueDataAesCentral uvd; + QString singleVal; + + // V100.8: order seems to be relevant. Keep alphabetical; + singleVal = QString(QStringLiteral("Impact_Color_%1")).arg(col.name()); + uvd.values.append(singleVal); + singleVal = QString(QStringLiteral("Impact_Radius_%1")).arg(radius); + uvd.values.append(singleVal); + singleVal = QString(QStringLiteral("Impact_Type_%1")).arg(LayerFactory::toString(type)); + uvd.values.append(singleVal); + + uvd.type = type; + uvd.col = col; + uvd.radius = radius; + uvd.height = uvd.radius; + + uvCont.append(uvd); + } // for + } // for + } // for + + + QStringList fieldnames; + // V100.8: order seems to be relevant. keep alphabetical + fieldnames.append(QString(QStringLiteral("IMPACT_COLOR"))); + fieldnames.append(QString(QStringLiteral("IMPACT_RADIUS"))); + fieldnames.append(QString(QStringLiteral("IMPACT_TYPE"))); + + bool success = + d->m_mapInterface->layerFactory().assignUniqueValueRendererAesCentral(layernameAES, + fieldnames, + uvCont); + qCritical() << " onGenerate3DGraphicConeUniqueValueRenderer, success=" << success; + } + + // create graphics: + { + qCritical() << " onGenerate3DGraphicConeUniqueValueRenderer, setup Instances..."; + + // Graphic: + const Esri::ArcGISRuntime::Viewpoint &vp = + d->m_mapGeoView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + const Esri::ArcGISRuntime::Point &mapCenter = static_cast(vp.targetGeometry()); + + d->m_impactConeViaUniqueValueRenderer.clear(); + + + int n = 0; + while (++n < 500) + radiuss.append(20); + + int k = 0; + for (const QColor &col : std::as_const(colVec)) + { + int i = 0; + for (float radius : std::as_const(radiuss)) + { + Private::object3D_data data; + data.m_position = Esri::ArcGISRuntime::Point(mapCenter.x() + (0.001 * k), mapCenter.y() + (0.001 * i)); + data.m_graphic = std::shared_ptr(new Esri::ArcGISRuntime::Graphic(data.m_position)); + + QVariantMap attribMap; + QString attribValue; + + attribValue = QString(QStringLiteral("Impact_Color_%1")).arg(col.name()); + attribMap.insert(QStringLiteral("IMPACT_COLOR"), attribValue); + + attribValue = QString(QStringLiteral("Impact_Radius_%1")).arg(radius); + attribMap.insert(QStringLiteral("IMPACT_RADIUS"), attribValue); + + LayerFactory::UniqueValueDataAesCentral::TYPE type{LayerFactory::UniqueValueDataAesCentral::TYPE::CONE}; + if (i%2) + type = LayerFactory::UniqueValueDataAesCentral::TYPE::SPHERE; + + attribValue = QString(QStringLiteral("Impact_Type_%1")).arg(LayerFactory::toString(type)); + attribMap.insert(QStringLiteral("IMPACT_TYPE"), attribValue); + + // SceneProperties: HEADING, PITCH, ROLL + attribMap.insert(QStringLiteral("HEADING"), ((10*i)%360) ); + attribMap.insert(QStringLiteral("PITCH"), ((30*i)%360)); + attribMap.insert(QStringLiteral("ROLL"), ((30*i)%360)); + + // qCritical() << " attribMap " << attribMap; + data.m_graphic->attributes()->setAttributesMap(attribMap); + + graphicsLayer->graphics()->append(data.m_graphic.get()); + d->m_impactConeViaUniqueValueRenderer.append(data); + + ++i; + } // for + + ++k; + } // for + + qCritical() << " onGenerate3DGraphicConeUniqueValueRenderer, setup Instances..." << d->m_impactConeViaUniqueValueRenderer.size(); + + } + + MEMORY_LOG() << " END"; +} + +void FunctionDialog::onAddCivilSymbols() +{ + AdvancedSymbology::SymbolDictionary &dict = d->m_mapImpl->symbolDictionary(); + int files = dict.addEtsSymbols(QLatin1String(":/advancedSymbology/CivilSymbols")); + qWarning() << LOGFUNCTION << " dict.addCivilSymbols() = " << files; + + AdvancedSymbology::SymbolProperties symProps; + dict.insertXmlTestSymbol(symProps); + qWarning() << LOGFUNCTION << " dict.insertXmlTestSymbol (sidc=" << symProps.sidc() << ")"; +} + + + +void FunctionDialog::onMessage() +{ + /* + QString messageId = QLatin1String("mySpecialId"); + + if (d->m_messageGroupLayer) + { + EsriRuntimeQt::SpatialReference sr = d->m_esriMap->spatialReference(); + + //create and process message, adding it to the map + EsriRuntimeQt::Message message; + message.setProperty(QLatin1String("_id"), messageId); + + message.setProperty(QLatin1String("_type"), QLatin1String("position_report")); + message.setProperty(QLatin1String("_action"), QLatin1String("update")); + + EsriRuntimeQt::Point mapPoint(0, 0, sr); + QString ctrlPoints = QString::number(mapPoint.x(), 'f') + QLatin1Char(',') + QString::number(mapPoint.y(), 'f'); + message.setProperty(QLatin1String("_control_points"), ctrlPoints); + + // 123456789012345 + // message.setProperty(QLatin1String("sic"), "SFGPUSAC-------"); + message.setProperty(QLatin1String("sic"), QLatin1String("GFGPOPP-------X")); + + message.setProperty(QLatin1String("_wkid"), QString::number(d->m_esriMap->spatialReference().wkid())); + message.setProperty(QLatin1String("uniquedesignation"), QLatin1String("MySymbolName")); + + qDebug() << "message = " << message.toString(); + + if (d->m_messageGroupLayer->messageProcessor()->processMessage(message)) + { + qDebug() << "message processed OK" ; + qDebug() << "message = '" << message.toString() << "'"; + messageId = message.messageId(); + } + else + qWarning() << "message processed NOT OK" ; + + QList layerList = d->m_messageGroupLayer->getLayers(); + + for ( auto layer, layerList) + qDebug() << "MessageGroupLayer consists of layer " << static_cast(layer->type()); + + } + else + { + qWarning() << "no messageGroupLayer"; + QMessageBox msgBox; + msgBox.setText(QLatin1String("no messageGroupLayer")); + msgBox.exec(); + } + */ +} + +void FunctionDialog::onMessageSelect() +{ + /* + QString messageId = QLatin1String("mySpecialId"); + + if (d->m_messageGroupLayer) + { + static bool select = true; + + EsriRuntimeQt::Message message; + + // arcGIS 10.2.6: createSelectMessage generates a wrong message + // -> wrong : .. {_action,highlight}... + // -> correct: .. {_action,select}... + // message = EsriRuntimeQt::MessageHelper::createSelectMessage(EsriRuntimeQt::SymbolDictionaryType::Mil2525C, + // messageId, + // QLatin1String("position_report"), + // true); + message.setMessageId(messageId); + message.setProperty(QLatin1String("_type"), QLatin1String("position_report")); + if (select) + message.setProperty(QLatin1String("_action"), QLatin1String("select")); + else + message.setProperty(QLatin1String("_action"), QLatin1String("un-select")); + + // toggle + select = ! select; + + qDebug() << "message = " << message.toString(); + + if (d->m_messageGroupLayer->messageProcessor()->processMessage(message)) + qDebug() << "createSelectMessage processed OK" ; + else + qWarning() << "createSelectMessage processed NOT OK" ; + + + } + else + { + qWarning() << "no messageGroupLayer or messageId.isEmpty"; + QMessageBox msgBox; + msgBox.setText(QLatin1String("no messageGroupLayer")); + msgBox.exec(); + } + */ +} + +void FunctionDialog::onAddSymbol() +{ + QString fileNameAndPath = QFileDialog::getOpenFileName(this, tr("Add Symbol"), + QLatin1String("/tmp"), + tr("Images (*.png)")); + + QImage symImg(fileNameAndPath); + + QFileInfo fi(fileNameAndPath); + + const QString &baseName = fi.baseName(); + + AdvancedSymbology::SymbolProperties symProps; + symProps.setName(baseName); + symProps.setKey(baseName); + + if ( d->m_mapImpl->symbolDictionary().setEtsSymbol(4711, baseName, symProps, symImg) ) + qDebug() << " insertExtraSymbol ok"; + else + qCritical() << " insertExtraSymbol failed"; + +} + +void FunctionDialog::onSomeParticipants() +{ + + // + // place participants + // + + QStringList sidcs; + // 1=Team/Crew, 2=Squad, 3=Section, 4=Platoon, 5=Company, 6=Battalion + // 12345678|| + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10031500111202000000"); // 01015000001202000000 -> 15 120200 LandEquip, Tank + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10031500121202000000"); // 01015000001202000000 -> 15 120200 LandEquip, Tank + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10031500131202000000"); // 01015000001202000000 -> 15 120200 LandEquip, Tank + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10031500141202000000"); // 01015000001202000000 -> 15 120200 LandEquip, Tank + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10031500151202000000"); // 01015000001202000000 -> 15 120200 LandEquip, Tank + sidcs += AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10031500161202000000"); // 01015000001202000000 -> 15 120200 LandEquip, Tank + + int counter = 0; + for (const QString &sidc : std::as_const(sidcs)) + { + ++counter; + const QString &layername = QStringLiteral("Participants_%1").arg(counter); + + // set additional values: + SituationMap::SymbolData symData; + symData.sidc = sidc; + + // create and set debugInfo for debugging: + const QString &debugInfo = layername; + RefPointer participant = + d->m_mapInterface->objectFactory(). + create(layername, debugInfo); + + if (!participant) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + symData.color = Qt::red; // Will be overwritten via partyId. + symData.size = 2; + + participant->setSymbol(symData); + participant->setTextModifier(Participant::TextModifier::T_UniqueDesignation, layername.right(1)); + + // QPointF(92750, 80525) -> Rennplatz - Bremen + + const QPointF &origin = s_basePoint.geographic(QPointF(90000 + counter * 100, 80000 + counter * 100)); + participant->setOrigin(Point3D::fromQPointF(origin)); + + participant->updateView(); + } + +} + +void FunctionDialog::onParticipantGetPosition() +{ + qWarning() << LOGFUNCTION << "XXXXXXXXXX"; + + if (d->m_participant) + { + d->m_participant->removeMySelf(); + d->m_participant.reset(); + } + +// if (d->m_mapGeoView) + { +// AdvancedSymbology::SymbolDictionary &dict = d->m_mapImpl->symbolDictionary(); + QString sidc(AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525C, "SFGPUCD---MR")); + + if ((d->m_cbRubSidc) +// && (d->m_cbRubSidc->currentText().length() <= 15) // civil symbols: + ) + sidc = d->m_cbRubSidc->currentText(); + +// if (!dict.contains(sidc)) +// { +// qWarning() << "sidc not found"; +// return; +// } + +// const QImage &symbolImage = dict.symbolImageBySidc(sidc, 32); +// if (symbolImage.isNull()) +// { +// qWarning() << "symbolImageBySidc failed"; +// return; +// } + + RB::RubberbandPointFeature *rubberband = d->m_mapInterface->rubberbandFactory().create(); + if (!rubberband) + { + qWarning() << "Creating rubberband to get participant position failed"; + return; + } + rubberband->maximumPointCount(1); // rubberband will finish after first mouse click + // rubberband->connectPreviewPixmap(QPixmap::fromImage(symbolImage)); + + rubberband->setUserData(new QString(sidc)); + rubberband->setPreviewSidc(sidc); + + connect(rubberband, &RB::RubberbandPointFeature::apply, this, &FunctionDialog::onParticipantGetPositionApplied); + connect(rubberband, &RB::RubberbandPointFeature::abort, rubberband, &RB::Rubberband::deleteMySelf); + } +} /* FunctionDialog::onParticipantGetPosition */ + + +void FunctionDialog::onParticipantGetPositionApplied(RB::RubberbandBase *rubberband) +{ + qDebug() << LOGFUNCTION; + + if (! rubberband) + { + qWarning() << LOGFUNCTION << " no rubberband anymore!!!"; + return; + } + + const QPointF &rubberCoord = rubberband->singleCoordinate(); + + + QString *p_sidc = reinterpret_cast(rubberband->userData()); + if (! p_sidc) + { + qWarning() << LOGFUNCTION << " no sidc anymore!!!"; + return; + } + + if (! this->createParticipant(Point3D::fromQPointF(rubberCoord), + QLatin1String("ParticipantRubberbandTab")) + ) + qWarning() << LOGFUNCTION << " createParticipant (ParticipantRubberbandTab) failed"; + + + SymbolData symData; + symData.sidc = *p_sidc; + d->m_participant->setSymbol(symData); + bool success = d->m_participant->updateView(); + qWarning() << LOGFUNCTION << " success (setMovementTrack) =" << success; + + // cleanup + delete p_sidc; + + rubberband->setUserData(nullptr); + rubberband -> deleteMySelf(); +} + + +bool FunctionDialog::createParticipant(const Point3D &origin, + const QString &debugInfo) +{ + qDebug() << LOGFUNCTION << " debugInfo='" << debugInfo << "'"; + + if (d->m_participant) + { + d->m_participant->removeMySelf(); + d->m_participant.reset(); + } + + // create and set debugInfo for debugging: + d->m_participant = d->m_mapInterface->objectFactory(). + create(QLatin1String("Participants"), debugInfo); + + if (!d->m_participant) + { + qDebug() << LOGFUNCTION << " no object"; + return false; + } + + d->m_participant->setOrigin(origin); + + return true; +} + + +void FunctionDialog::onParticipantCreate() +{ + qDebug() << LOGFUNCTION ; + + if (d->m_participant) + { + d->m_participant->removeMySelf(); + d->m_participant.reset(); + } + +// QPointF origin = d->m_objectBasePoint + QPointF( -150000, -150000 ); + QPointF originSim = d->m_objectBasePoint + QPointF(88000, 76000); + QPointF originGeo = s_basePoint.geographic(originSim); + + QString sidc; + + if (d->m_leSidc) + { + QString text = d->m_leSidc->text(); + // if (text.length() <= 15) // do not check regarding civil symbols + sidc = text; + } + + qWarning() << LOGFUNCTION << " sidc=" << sidc; + + + + bool success = this->createParticipant(Point3D::fromQPointF(originGeo), + QLatin1String("ParticipantMessageTab")); + + d->m_participant->setTextModifier(Participant::TextModifier::EF_ReinforcedReduced, QLatin1String("EF_ReinforcedReduced")); + d->m_participant->setTextModifier(Participant::TextModifier::M_HigherFormation, QLatin1String("M_HigherFormation")); + d->m_participant->setTextModifier(Participant::TextModifier::T_UniqueDesignation, QLatin1String("T_UniqueDesignation")); + d->m_participant->setTextModifier(Participant::TextModifier::W_DateTimeGroup, QLatin1String("W_DateTimeGroup")); + + d->m_participant->setVisibility(d->m_visibility); +// d->m_participant->setVisibility(SituationMap::ObjectAppearance::Technical, false); // no sense + + + SymbolData symData; + symData.sidc = sidc; + symData.color = Qt::cyan; // Will be overwritten via partyId. +// symData.size = 0.5; + + d->m_participant->setSymbol(symData); + d->m_participant->setPartyId(4); + d->m_participant->setScale(d->getScale()); + +// d->m_participant->setInjury(Injury::MobilityKill, QColor(Qt::red) ); // QColor() to use the correct signature! +// d->m_participant->setInjury(Injury::TotalKill, 3); + +// d->m_participant->setManipulation(true); +// d->m_participant->setBiteError(true); +// d->m_participant->setUseLightColor(true); +// d->m_participant->setNbcProtection(true); + +// d->m_participant->setMarked(true); + +// d->m_participant->setRanges(QVector() << 8000 << 10000); + + + if (! success) + { + qWarning() << LOGFUNCTION << " createParticipant (ParticipantMessageTab) failed"; + return; + } + + // MovementTrack: + d->m_participant->setMovementTrackEnabled(true); +// d->m_participant->setMovementTrackVisibility(true); // visible = true = default + + PositionCont intialMovementTrack; + Point3D ppp; + + ppp = Point3D::fromQPointF(s_basePoint.geographic(originSim + QPointF(-500, -500))); + ppp.setZ(100); + qCritical() << " ppp=" << ppp.x() << " ; " << ppp.y() << " ; "<< ppp.z(); + intialMovementTrack << ppp; + + ppp = Point3D::fromQPointF(s_basePoint.geographic(originSim + QPointF(-500, 500))); + ppp.setZ(50); + qCritical() << " ppp=" << ppp.x() << " ; " << ppp.y() << " ; "<< ppp.z(); + intialMovementTrack << ppp; + + + d->m_participant->setMovementTrack(intialMovementTrack); + + + + // DirectionIndicator: + { + Participant::DirectionIndicatorData directionIndicatorData ; + directionIndicatorData.color = Qt::black; + + directionIndicatorData.directionDegree = 22; + directionIndicatorData.lineStyle = Qt::SolidLine; + directionIndicatorData.lineWidth = 1; + d->m_participant->setDirectionIndicator(0, directionIndicatorData); + + directionIndicatorData.directionDegree = 45; + directionIndicatorData.lineStyle = Qt::DashLine; + directionIndicatorData.lineWidth = 2; + d->m_participant->setDirectionIndicator(1, directionIndicatorData); + + directionIndicatorData.directionDegree = 90; + directionIndicatorData.lineStyle = Qt::DashDotDotLine; + directionIndicatorData.lineWidth = 3; + d->m_participant->setDirectionIndicator(2, directionIndicatorData); + } + + + // Designation: + { + Participant::DesignationData designationData; + designationData.spotLocation.rx() = 0.155; //spotLocation.lon(); + designationData.spotLocation.ry() = 0.926; //spotLocation.lat(); + designationData.code = 123; + + d->m_participant->setDesignations({designationData}); + } + + + success = d->m_participant->updateView(); + + qWarning() << LOGFUNCTION << " success =" << success; + + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &FunctionDialog::onParticipantUpdate); + timer->setSingleShot(true); + timer->start(2000); +} + +void FunctionDialog::onParticipantUpdate() +{ + qDebug() << LOGFUNCTION ; + + if (!d->m_participant) + { + qDebug() << LOGFUNCTION << " no object anymore"; + return; + } + + // move + QPointF origin = s_basePoint.geographic(QPointF(93064, 78331)); + d->m_participant->setOrigin(Point3D(origin.x(), origin.y(), 100.2345)); + + d->m_participant->setRanges(QVector() << 50 << 150 << 350 << 500 << 1000 << 1800); + d->m_participant->setRangesVisibility(true); + + + d->m_participant->setInjury(Injury::TotalKill, QColor(Qt::blue)); + + d->m_participant->setAdditionalSymbol(SituationMap::AdditionalSymbolType::NbcProtection, true); + d->m_participant->setAdditionalSymbol(SituationMap::AdditionalSymbolType::Manipulation, true); + d->m_participant->setAdditionalSymbol(SituationMap::AdditionalSymbolType::NoPosition, true); + d->m_participant->setAdditionalSymbol(SituationMap::AdditionalSymbolType::BiteError, true); + + d->m_participant->setTextModifier(Participant::TextModifier::EF_ReinforcedReduced, QLatin1String("+ ")); + + SymbolData symData = d->m_participant->symbol(); + symData.color = Qt::magenta; // Will be overwritten via partyId (if partyId != -1) + symData.size = 1.0; + d->m_participant->setSymbol(symData); + + d->m_participant->setPartyId(4); + + d->m_participant->setMarked(true); + + // Designation: + { + Participant::DesignationData designationData; + designationData.spotLocation.rx() = 0; //spotLocation.lon(); + designationData.spotLocation.ry() = 0; //spotLocation.lat(); + designationData.code = 123; + d->m_participant->setDesignations({designationData}); + } + + + bool success = d->m_participant->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_participant->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + +// //----------------------------------- +// // second participant near to the first one: +// //----------------------------------- +// Participant* secondPart = d->m_mapImpl->createParticipant(); +// +// if (!secondPart) +// { +// qDebug() << LOGFUNCTION << " no object"; +// return; +// } +// +// // Set objId for debugging: +// secondPart->setObjId("secondParticipant"); +// +// secondPart->setSymbol(d->m_participant->symbol()); +// secondPart->setOrigin( d->m_participant->origin() + QPointF(1000, 1000) ); +// +// // update view +// success = secondPart->updateView() && success; +// +// if (success) +// qDebug() << LOGFUNCTION << " secondParticipant updateView OK" ; +// else +// qDebug() << LOGFUNCTION << " secondParticipant updateView NOT OK" ; + + +} + +void FunctionDialog::onBuildingCreate() +{ + qDebug() << LOGFUNCTION ; + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_building)); + + d->m_building = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("BuildingOutlineId2")); + + if (!d->m_building) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_building->setIdentifier(QLatin1String("Building")); + + d->m_building->setVisibility(d->m_visibility); + d->m_building->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_building->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_building->setSelected(d->m_selected); + + d->m_building->rTextStyle().setTextColor(Qt::magenta); + d->m_building->rTextStyle().setSize(30); +// d->m_building->rTextStyle().setAngle(-90); // -90 -> north + d->m_building->rTextStyle().setAntiAliased(true); + + + d->m_building->rTextStyle().setItalic(true); + d->m_building->rTextStyle().setHorizontalAlignment(HorizontalAlignment::Justify); + d->m_building->rTextStyle().setVerticalAlignment(VerticalAlignment::BaseLine); + + d->m_building->rFillStyle().setFillColor(QColor(Qt::darkGray)); + + // set additional values: + SituationMap::PositionCont posCont; +// QPointF offset = d->m_objectBasePoint + QPointF(50000, 50000); + +// int extent = 30000; +// posCont << offset - QPointF(0, 0); +// posCont << offset - QPointF(extent, 0); +// posCont << offset - QPointF(extent, extent); +// posCont << offset - QPointF(0, extent); + + if (true) + { + // Bt56 + appendQPointF(posCont, s_basePoint.geographic(QPointF(93064 , 78331))); + appendQPointF(posCont, s_basePoint.geographic(QPointF(93069.9, 78337.8))); + appendQPointF(posCont, s_basePoint.geographic(QPointF(93076.8, 78331.8))); + appendQPointF(posCont, s_basePoint.geographic(QPointF(93070.9, 78325))); + } + else + { + // Salchau + appendQPointF(posCont, s_basePoint.geographic(QPointF(75000.0 , 13000))); + appendQPointF(posCont, s_basePoint.geographic(QPointF(75000.0 , 13100))); + appendQPointF(posCont, s_basePoint.geographic(QPointF(75100.0 , 13100))); + appendQPointF(posCont, s_basePoint.geographic(QPointF(75100.0 , 13000))); + } + + std::cout << " posCont=" << posCont << std::endl; + + d->m_building->setPositions(posCont); + + + d->m_building->setMarker(posCont[0]); + + + // update view + bool success = d->m_building->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_building->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " building dump: " << (*d->m_building) << std::endl; + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &FunctionDialog::onBuildingUpdate); + timer->setSingleShot(true); + timer->start(2000); +} + +void FunctionDialog::onBuildingUpdate() +{ + + if (!d->m_building) + { + qDebug() << LOGFUNCTION << " no object anymore"; + return; + } + + qDebug() << LOGFUNCTION << " update Values.." ; + + d->m_building->setIdentifier(d->m_building->identifier() + QLatin1String("Update")); + d->m_building->rTextStyle().setAngle(-45); // -45 -> north/east + + d->m_building->setMarker(Point3D()); + + + // Bt56 + SituationMap::PositionCont posCont; + appendQPointF(posCont, s_basePoint.geographic(QPointF(93064 , 78331))); + appendQPointF(posCont, s_basePoint.geographic(QPointF(93069.9, 78337.8))); + appendQPointF(posCont, s_basePoint.geographic(QPointF(93076.8, 78331.8))); + + appendQPointF(posCont, s_basePoint.geographic(QPointF(93100.9, 78300))); + + appendQPointF(posCont, s_basePoint.geographic(QPointF(93070.9, 78325))); + + d->m_building->setPositions(posCont); + + d->m_building->rLineStyle().setLineColor(Qt::red); + d->m_building->rLineStyle().setLineWidth(6); + d->m_building->rLineStyle().setLinePattern(LinePattern::DashDotDot); + + d->m_building->rFillStyle().setFillColor(Qt::yellow); + d->m_building->rFillStyle().setFillPattern(FillPattern::DiagonalCross); + + bool success = d->m_building->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_building->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; +} + +void FunctionDialog::onBiologicalAgentCreate() +{ + qDebug() << LOGFUNCTION ; + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_bAgent)); + + d->m_bAgent = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("B-AgentId")); + + if (!d->m_bAgent) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_bAgent->setIdentifier(QLatin1String("My BiologicalAgent\nNext Line-OutLined")); + + d->m_bAgent->setVisibility(d->m_visibility); + d->m_bAgent->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_bAgent->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_bAgent->setSelected(d->m_selected); + + d->m_bAgent->rTextStyle().setTextColor(Qt::magenta); + d->m_bAgent->rTextStyle().setSize(30); + d->m_bAgent->rTextStyle().setBold(true); + d->m_bAgent->rTextStyle().setHorizontalAlignment(HorizontalAlignment::Left); + d->m_bAgent->rTextStyle().setHorizontalAlignment(HorizontalAlignment::Justify); + d->m_bAgent->rTextStyle().setVerticalAlignment(VerticalAlignment::Top); + + + LineStyle lineStyle(Qt::cyan, 5, LinePattern::Solid); + FillStyle fillStyle(Qt::blue, FillPattern::Solid); + + d->m_bAgent->setLineStyle(lineStyle); + d->m_bAgent->setFillStyle(fillStyle); + + d->m_bAgent->setPartyId(3); // Will overwrite line/fillcolor + d->m_bAgent->setAesState(AesEffectState::Unknown); // != Unknown will overwrite fillPattern + + +// QPointF center = d->m_objectBasePoint + QPointF(100000, 100000); + QPointF center = d->m_mapImpl->centerPoint(); + qWarning() << LOGFUNCTION << " xxxxxxxxxx center=" << center; + +// center = s_basePoint.geographic(center); + d->m_bAgent->setOrigin(Point3D::fromQPointF(center)); + + d->m_bAgent->setRadius(10000); + + + // Extrusion + d->m_bAgent->setExtrusionHeight(5); + + // if setExtrusionColorName is not set, the partyId is used as fallback + // see -> AbstractMapObjectImpl::updateGraphicalObjectExtrusion() + // + + // d->m_bAgent->setExtrusionColorName( QColor(Qt::green).name() ); + + + bool success = d->m_bAgent->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_bAgent->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " B-Agent dump: " << (*d->m_bAgent) << std::endl; + + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &FunctionDialog::onBiologicalAgentUpdate); + timer->setSingleShot(true); + timer->start(2000); +} + +void FunctionDialog::onBiologicalAgentUpdate() +{ + if (!d->m_bAgent) + { + qDebug() << LOGFUNCTION << " no object anymore"; + return; + } + + if (d->m_mapImpl->mode() == Mode::Mode_2D) + d->m_bAgent->setIdentifier(d->m_bAgent->identifier() + QLatin1String("Upd")); + + // modify geometry: + // d->m_bAgent->setOrigin(d->m_bAgent->origin() + Point3D(500, 500, .0)); + d->m_bAgent->setRadius(d->m_bAgent->radius() + 50); + + LineStyle lineStyle(Qt::red, 6, LinePattern::DashDotDot); + FillStyle fillStyle(Qt::magenta, FillPattern::DiagonalCross); + d->m_bAgent->setLineStyle(lineStyle); + d->m_bAgent->setFillStyle(fillStyle); + + d->m_bAgent->setPartyId(4); // Will overwrite line/fillcolor + d->m_bAgent->setAesState(AesEffectState::Prepared); // != Unknown will overwrite fillPattern + + + SymbolData symData = d->m_bAgent->symbol(); + + switch (d->m_mapImpl->symbologyType()) + { + case AdvancedSymbology::SymbologyType::APP6D: + case AdvancedSymbology::SymbologyType::MIL2525D: + break; + case AdvancedSymbology::SymbologyType::MIL2525C: + symData.sidc = QLatin1String("GUMPNB----"); // only to test modify of sidc + break; + case AdvancedSymbology::SymbologyType::APP6B: + symData.sidc = QLatin1String("GUCPBWC---"); // only to test modify of sidc + break; + case AdvancedSymbology::SymbologyType::None: + // empty + break; + } // switch + + d->m_bAgent->setSymbol(symData); + + bool success = d->m_bAgent->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_bAgent->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; +} + +void FunctionDialog::onChemicalAgentCreate() +{ + qDebug() << LOGFUNCTION ; + + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_cAgent)); + + d->m_cAgent = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("C-AgentId")); + + if (!d->m_cAgent) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_cAgent->setIdentifier(QLatin1String("My ChemicalAgent-OutLined")); + + d->m_cAgent->setVisibility(d->m_visibility); + d->m_cAgent->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_cAgent->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_cAgent->setSelected(d->m_selected); + + LineStyle lineStyle(Qt::cyan, 5, LinePattern::Solid); + FillStyle fillStyle(Qt::blue, FillPattern::Null); + d->m_cAgent->setLineStyle(lineStyle); + d->m_cAgent->setFillStyle(fillStyle); + + d->m_cAgent->setPartyId(4); // Will overwrite line/fillcolor + d->m_cAgent->setAesState(AesEffectState::Unknown); // != Unknown will overwrite fillPattern + + QPointF center = d->m_objectBasePoint + QPointF(90000, 100100); + + center = s_basePoint.geographic(center); + d->m_cAgent->setOrigin(Point3D::fromQPointF(center)); + d->m_cAgent->setRadius(1000); + + bool success = d->m_cAgent->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_cAgent->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " C-Agent dump: " << (*d->m_cAgent) << std::endl; +} + +void FunctionDialog::onCubeCreate() +{ + qDebug() << LOGFUNCTION ; + + d->clearMapObjectPtr(RefPointer(d->m_cube)); + + const QString &layernameForCubes = d->m_mapInterface->layerFactory().layernameForCubes(); + + d->m_cube = d->m_mapInterface->objectFactory(). + create(layernameForCubes, QLatin1String("CubeID")); + + if (!d->m_cube) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + // set additional values: + SituationMap::PositionCont posCont; + QPointF offset = d->m_objectBasePoint + QPointF(92500, 79000); + + int extent = 1000; + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent, extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, extent))); + + std::cout << " posCont=" << posCont.size() << std::endl; + + d->m_cube->setPositions(posCont); + + d->m_cube->setHeight(123456); + d->m_cube->setPartyId(1); + + d->m_cube->setDrawOrder(234); + + bool success = d->m_cube->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_cube->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; +} + + +void FunctionDialog::onDangerAreaCreate() +{ + qDebug() << LOGFUNCTION ; + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_dangerArea)); + + d->m_dangerArea = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("DangerAreaId")); + + if (!d->m_dangerArea) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_dangerArea->setIdentifier(QLatin1String("DangerArea-NotOutLined")); + + d->m_dangerArea->setVisibility(d->m_visibility); + d->m_dangerArea->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_dangerArea->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_dangerArea->setSelected(d->m_selected); + + TextStyle textStyle(Qt::yellow); + textStyle.setTextOutlineColor(Qt::white); + textStyle.setTextOutlineWidth(0.5); + LineStyle lineStyle(Qt::yellow, 5, LinePattern::Solid); + FillStyle fillStyle(Qt::blue, FillPattern::DiagonalCross); + + d->m_dangerArea->setTextStyle(textStyle); + d->m_dangerArea->setLineStyle(lineStyle); + d->m_dangerArea->setFillStyle(fillStyle); + + + // set additional values: + SituationMap::PositionCont posCont; + QPointF offset = d->m_objectBasePoint + QPointF(150000, 150000); + + int extent = 100000; + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent, extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent / 2, extent / 2))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, extent))); + + std::cout << " posCont=" << posCont.size() << std::endl; + + d->m_dangerArea->setPositions(posCont); + + bool success = d->m_dangerArea->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_dangerArea->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " DangerArea success = " << success << std::endl; + std::cout << " DangerArea dump: " << (*d->m_dangerArea) << std::endl; + + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &FunctionDialog::onDangerAreaUpdate); + timer->setSingleShot(true); + timer->start(2000); +} + +void FunctionDialog::onDangerAreaUpdate() +{ + if (!d->m_dangerArea) + { + qDebug() << LOGFUNCTION << " no object anymore"; + return; + } + + d->m_dangerArea->setIdentifier(d->m_dangerArea->identifier() + QLatin1String("Update")); + + d->m_dangerArea->rLineStyle().setLineWidth(4); + d->m_dangerArea->rFillStyle().setFillPattern(FillPattern::Null); + + d->m_dangerArea->setPartyId(2); // Will overwrite line/fillcolor + + // move + QPointF offset(d->m_dangerArea->positions().value(0).toPointF() + QPointF(10000, 10000)); + + qDebug() << LOGFUNCTION << " update Values.." ; + + SituationMap::PositionCont posCont; + + int extent = 150000; + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent * 2, extent))); +// appendQPointF(posCont, s_basePoint.geographic( offset + QPointF(extent/2, extent/2)) ; + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, extent))); + + d->m_dangerArea->setPositions(posCont); + + bool success = d->m_dangerArea->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_dangerArea->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; +} + +void FunctionDialog::onFunctionalAreaCreate() +{ + qDebug() << LOGFUNCTION ; + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_functionalArea)); + + d->m_functionalArea = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("FunctionalAreaId")); + + if (!d->m_functionalArea) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_functionalArea->setIdentifier(QLatin1String("FunctionalArea-OutLined")); + + d->m_functionalArea->setVisibility(d->m_visibility); + d->m_functionalArea->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_functionalArea->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_functionalArea->setSelected(d->m_selected); + + LineStyle lineStyle(Qt::red, 5, LinePattern::Solid); + FillStyle fillStyle(Qt::blue, FillPattern::ForwardDiagonal); + + d->m_functionalArea->setLineStyle(lineStyle); + d->m_functionalArea->setFillStyle(fillStyle); + + + // set additional values: + SituationMap::PositionCont posCont; + QPointF offset = QPointF(93550, 78550); + + int extent = 1000; + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent, extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent / 2, extent / 2))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, extent))); + + std::cout << " posCont=" << posCont.size() << std::endl; + + d->m_functionalArea->setPositions(posCont); + + + // pointSymbol : + QPointF psOrg = s_basePoint.geographic(offset + QPointF(extent / 2, extent / 2)); + d->m_functionalArea->setPointSymbolOrigin(Point3D::fromQPointF(psOrg)); + + // !!! use only POINT symbols !!! + // DoDo: find adequate sidc + d->m_functionalArea->setPointSymbolSidc(AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10012500002108000000")); // Maritime Points : Impact Point + d->m_functionalArea->setPointSymbolSidc(AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10031500001102030000")); // Weapon/Weapon System + d->m_functionalArea->setPointSymbolSidc(AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D, "10031500001202000000")); // Vehicle : Tank +// d->m_functionalArea->setPointSymbolSidc(AdvancedSymbology::SidcCodec::encodeCivil("ETS_Pers_FW_BerFue")); // Color Replacement not working yet + + d->m_functionalArea->setPartyId(2); + // : pointSymbol + + bool success = d->m_functionalArea->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_functionalArea->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " FunctionalArea dump: " << (*d->m_functionalArea) << std::endl; + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &FunctionDialog::onFunctionalAreaUpdate); + timer->setSingleShot(true); + timer->start(2000); +} + +void FunctionDialog::onFunctionalAreaUpdate() +{ + if (!d->m_functionalArea) + { + qDebug() << LOGFUNCTION << " no object anymore"; + return; + } + + d->m_functionalArea->rLineStyle().setLineWidth(2); + d->m_functionalArea->rFillStyle().setFillPattern(FillPattern::Null); + + d->m_functionalArea->setIdentifier(d->m_functionalArea->identifier() + QLatin1String("Update")); + + // move + QPointF offset = QPointF(93550, 78550) + QPointF(100, 100); + + qDebug() << LOGFUNCTION << " update Values.."; + + SituationMap::PositionCont posCont; + + int extent = 1000; + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent * 2, extent))); +// appendQPointF(posCont, s_basePoint.geographic( offset + QPointF(extent/2, extent/2) )); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, extent))); + + d->m_functionalArea->setPositions(posCont); + + // pointSymbol : + QPointF pSymOrg = s_basePoint.geographic(offset + QPointF(extent / 2, extent / 2)); + d->m_functionalArea->setPointSymbolOrigin(Point3D::fromQPointF(pSymOrg)); + d->m_functionalArea->setPartyId(3); + // : pointSymbol + + bool success = d->m_functionalArea->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_functionalArea->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; +} + +void FunctionDialog::onGeoImageCreate() +{ + qDebug() << LOGFUNCTION ; + + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_geoImage)); + + d->m_geoImage = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("GeoImageId")); + + if (!d->m_geoImage) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_geoImage->setIdentifier(QLatin1String("GeoImageIdent")); // maybe empty!? + + { + // d->m_geoImage->setProxyImage(); // no proxyImage -> use defaultMarker + QImage mainImage(100, 100, QImage::Format_ARGB32); + mainImage.fill(qRgba(0, 0, 0, 0)); + + QPainter painter(&mainImage); + painter.setBrush(QBrush(Qt::gray)); + painter.setPen(Qt::NoPen); + painter.drawEllipse(mainImage.rect()); + + painter.setPen(QPen(Qt::red)); + painter.drawText(mainImage.rect(), Qt::AlignCenter, QLatin1String("GeoImg")); + + painter.end(); + d->m_geoImage->setMainImage(mainImage); + d->m_geoImage->setDescription(QLatin1String("desc")); + +// d->m_geoImage->setProxyImage(mainImage, QSize(20, 20)); + + } + + + d->m_geoImage->setVisibility(d->m_visibility); + d->m_geoImage->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_geoImage->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_geoImage->setSelected(d->m_selected); + + QPointF origin = s_basePoint.geographic(d->m_objectBasePoint + QPointF(90500, 80500)); + d->m_geoImage->setOrigin(Point3D::fromQPointF(origin)); + + bool success = d->m_geoImage->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_geoImage->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " GeoImage dump: " << (*d->m_geoImage) << std::endl; +} + +void FunctionDialog::onMapObjectClicked(const SituationMap::Map::RefPointerMapObjectList &mapObjList, + const QPointF &viewportPosition) +{ + qDebug() << LOGFUNCTION ; + + // Keep in mind: It´s likely that the application has also such a slot. + + for (auto mapObj : mapObjList) + { + qDebug() << LOGFUNCTION << " id=" << mapObj->uniqueMapId() + << "object-debugInfo=" << mapObj->debugInfo() + << "object-identifier=" << mapObj->identifier(); + + // GeoImage: + SituationMap::RefPointer geoImage(mapObj); + if (geoImage.valid()) + { + qDebug() << LOGFUNCTION << " geoImage found, id=" << mapObj->uniqueMapId() + << "object-debugInfo=" << mapObj->debugInfo() + << "object-identifier=" << mapObj->identifier(); + + QWidget *geoImageWidget = geoImage->createWidget(); + + if (!geoImageWidget) + { + qDebug() << LOGFUNCTION << " geoImage found, id=" << mapObj->uniqueMapId() + << "object-debugInfo=" << mapObj->debugInfo() + << "object-identifier=" << mapObj->identifier() + << " no mainImage provided!?"; + return; + } + + // Bug iOSQz-000005 'QGraphicsProxyWidget will crash' + // Adding a widget will let the application crash + // Workaround: 'proxy->setOpacity(0.99);' + QGraphicsProxyWidget *proxyWidget = d->m_mapInterface->getQGraphicsView()->scene()->addWidget(geoImageWidget); + proxyWidget->setOpacity(0.99); + proxyWidget->setPos(viewportPosition + QPointF(10, 10)); + + return; // to do it for just one GeoImage + } + } // for +} + +void FunctionDialog::onSoundAreaCreate() +{ + qDebug() << LOGFUNCTION ; + + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_soundArea)); + + d->m_soundArea = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("SoundAreaId")); + + if (!d->m_soundArea) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_soundArea->setIdentifier(QLatin1String("SoundAreaIdent")); // maybe empty!? + + SituationMap::PositionCont posCont; + QPointF offset = QPointF(93000, 78000); + + int extent = 1123; + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent, extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent / 2, extent / 2))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, extent))); + + std::cout << " posCont=" << posCont.size() << std::endl; + + d->m_soundArea->setPositions(posCont); + + { + QImage img(100, 100, QImage::Format_ARGB32); + img.fill(qRgba(0, 0, 0, 0)); + + QPainter painter(&img); + painter.setBrush(QBrush(Qt::red)); + painter.setPen(Qt::NoPen); + painter.drawEllipse(img.rect()); + painter.setPen(QPen(Qt::black)); + painter.drawText(img.rect(), Qt::AlignCenter, QLatin1String("Img")); + painter.end(); + + d->m_soundArea->setMarkerData(MarkerData(img, QSize(40, 40))); // image in high resolution and small (default) size + d->m_soundArea->setMarkerOrigin(posCont[3]); + } + + + d->m_soundArea->setVisibility(d->m_visibility); + d->m_soundArea->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_soundArea->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_soundArea->setSelected(d->m_selected); + + + bool success = d->m_soundArea->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_soundArea->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + + std::cout << " GeoImage dump: " << (*d->m_soundArea) << std::endl; + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &FunctionDialog::onSoundAreaUpdate); + timer->setSingleShot(true); + timer->start(2000); +} + +void FunctionDialog::onSoundAreaUpdate() +{ + if (!d->m_soundArea) + { + qDebug() << LOGFUNCTION << " no object anymore"; + return; + } + + { + // changed pos + SituationMap::PositionCont posCont = d->m_soundArea->positions(); + d->m_soundArea->setMarkerOrigin(posCont[2]); + } + + { + // changed img to marker + d->m_soundArea->setMarkerData(MarkerData(MarkerSymbolType::Cross, 20, Qt::cyan)); + } + + bool success = d->m_soundArea->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; +} + + +void FunctionDialog::onHitLineCreate() +{ + qDebug() << LOGFUNCTION ; + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_hitLine)); + + d->m_hitLine = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("HitLineId")); + + if (!d->m_hitLine) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_hitLine->setIdentifier(QLatin1String("HitLine-OutLined")); + + d->m_hitLine->setVisibility(d->m_visibility); + d->m_hitLine->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_hitLine->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_hitLine->setSelected(d->m_selected); + + d->m_hitLine->rLineStyle().setLineColor(Qt::blue); + d->m_hitLine->rLineStyle().setLineWidth(3); + + + // set additional values: + QPointF offset = QPointF(80000, 80000); + + Point3D start = Point3D::fromQPointF(s_basePoint.geographic(offset + QPointF())); + Point3D end = Point3D::fromQPointF(s_basePoint.geographic(offset + QPointF(1230, 1560))); + + d->m_hitLine->setStartAndEndPoint(start, end); + + bool success = d->m_hitLine->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_hitLine->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " HitLine dump: " << (*d->m_hitLine) << std::endl; + + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &FunctionDialog::onHitLineUpdate); + timer->setSingleShot(true); +// timer->start(2000); +} + +void FunctionDialog::onHitLineUpdate() +{ + if (!d->m_hitLine) + { + qDebug() << LOGFUNCTION << " no object anymore"; + return; + } + + d->m_hitLine->setIdentifier(d->m_hitLine->identifier() + QLatin1String("Update")); + + d->m_hitLine->setPartyId(4); // Will overwrite line/fillcolor + + // set additional values: + Point3D start = d->m_hitLine->startPoint(); + Point3D end = d->m_hitLine->endPoint(); + + // modify: + end.rx() += 0.001; + + d->m_hitLine->setStartAndEndPoint(start, end); + + bool success = d->m_hitLine->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_hitLine->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; +} + +void FunctionDialog::onLineOfSightLineCreate() +{ + qDebug() << LOGFUNCTION ; + + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_lineOfSight)); + + d->m_lineOfSight = d->m_mapInterface->objectFactory(). + create(QLatin1String("MountingPoints"), QLatin1String("LineOfSight")); // SurcfacePlacementMode () + + d->m_lineOfSight->setPoints(Point3D(0.155, 0.926, 100) // observer + , Point3D(0.156, 0.926, 50) // obstacle + , Point3D(0.157, 0.926, 0) // target +// , Qt::cyan, // visibleColor +// , Qt::magenta + ); // obstructedColor + + d->m_lineOfSight->setVisibility(d->m_visibility); + d->m_lineOfSight->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_lineOfSight->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_lineOfSight->setSelected(d->m_selected); + + + bool success = d->m_lineOfSight->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_lineOfSight->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " LineOfSight dump: " << (*d->m_lineOfSight) << std::endl; +} + + +void FunctionDialog::onMineCreate() +{ + qDebug() << LOGFUNCTION ; + + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_mine)); + + d->m_mine = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("MineId")); + + if (!d->m_mine) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_mine->setIdentifier(QLatin1String("Mine-OutLined")); + + d->m_mine->setVisibility(d->m_visibility); + d->m_mine->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_mine->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_mine->setSelected(d->m_selected); + + d->m_mine->setPartyId(3); // Will overwrite line/fillcolor + + + // set additional values: + QPointF origin = s_basePoint.geographic(d->m_objectBasePoint + QPointF(91500, 81500)); + d->m_mine->setOrigin(Point3D::fromQPointF(origin)); + + bool success = d->m_mine->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_mine->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " Mine dump: " << (*d->m_mine) << std::endl; + + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &FunctionDialog::onMineUpdate); + timer->setSingleShot(true); + timer->start(2000); +} + +void FunctionDialog::onMineUpdate() +{ + if (!d->m_mine) + { + qDebug() << LOGFUNCTION << " no object anymore"; + return; + } + + d->m_mine->setState(true); + + bool success = d->m_mine->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_mine->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; +} + +void FunctionDialog::onMineFieldCreate() +{ + qDebug() << LOGFUNCTION ; + + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_mineField)); + + d->m_mineField = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("MineFieldId")); + + if (!d->m_mineField) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_mineField->setIdentifier(QLatin1String("MineField-OutLined")); + + d->m_mineField->setVisibility(d->m_visibility); + d->m_mineField->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_mineField->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_mineField->setSelected(d->m_selected); + + d->m_mineField->setAesState(AesEffectState::Unknown); // != Unknown will overwrite fillPattern + + d->m_mineField->rLineStyle().setLineColor(Qt::green); + d->m_mineField->rLineStyle().setLineWidth(2); + d->m_mineField->rFillStyle().setFillColor(Qt::blue); + d->m_mineField->rFillStyle().setFillPattern(FillPattern::DiagonalCross); + +// d->m_mineField->setPartyId(5); // Will overwrite line/fillcolor + + + + // set paths: + SituationMap::PositionCont posContSeg1; + QPointF offsetSeg1 = d->m_objectBasePoint + QPointF(150000, 150000); + + int extent = 40000; + appendQPointF(posContSeg1, s_basePoint.geographic(offsetSeg1 + QPointF( 0, 0))); + appendQPointF(posContSeg1, s_basePoint.geographic(offsetSeg1 + QPointF(extent, 0))); + appendQPointF(posContSeg1, s_basePoint.geographic(offsetSeg1 + QPointF(extent, 2 * extent))); + appendQPointF(posContSeg1, s_basePoint.geographic(offsetSeg1 + QPointF( 0, 2 * extent))); + d->m_mineField->setSegment(1, posContSeg1); + + PositionCont posContSeg2; + QPointF offsetSeg2 = d->m_objectBasePoint + QPointF(200000, 150000); + appendQPointF(posContSeg2, s_basePoint.geographic(offsetSeg2 + QPointF( 0, 0))); + appendQPointF(posContSeg2, s_basePoint.geographic(offsetSeg2 + QPointF(2 * extent, 0))); + appendQPointF(posContSeg2, s_basePoint.geographic(offsetSeg2 + QPointF(2 * extent, extent))); + appendQPointF(posContSeg2, s_basePoint.geographic(offsetSeg2 + QPointF( 0, extent))); + d->m_mineField->setSegment(2, posContSeg2); + +// PositionCont posContSeg3; +// QPointF offsetSeg3 = d->m_objectBasePoint + QPointF(300000, 150000); +// posContSeg3 << offsetSeg3 + QPointF(0, 0); +// posContSeg3 << offsetSeg3 + QPointF(2 * extent, 0); +// posContSeg3 << offsetSeg3 + QPointF(2 * extent, extent); +// posContSeg3 << offsetSeg3 + QPointF(0, extent); +// d->m_mineField->setSegment(3, posContSeg3); + PositionCont posContSeg3; + QPointF offsetSeg3 = d->m_objectBasePoint + QPointF(300000, 150000); + appendQPointF(posContSeg3, s_basePoint.geographic(offsetSeg3 + QPointF( 0, 0))); + appendQPointF(posContSeg3, s_basePoint.geographic(offsetSeg3 + QPointF(2 * extent, 0))); + appendQPointF(posContSeg3, s_basePoint.geographic(offsetSeg3 + QPointF(2 * extent, extent))); + appendQPointF(posContSeg3, s_basePoint.geographic(offsetSeg3 + QPointF( 0, extent))); + d->m_mineField->setSegment(3, posContSeg3); + + +// PositionCont posContComplete; +// QPolygonF polyComplete; +// QPolygonF polySeg1; +// Q_FOREACH(const QPointF &item, posContSeg1) +// polySeg1.append(item); + +// QPolygonF polySeg2; +// Q_FOREACH(const QPointF &item, posContSeg2) +// polySeg2.append(item); +// polyComplete = polySeg1.united(polySeg2); + +// qWarning() << "polyComplete.size()=" << polyComplete.size(); +// qWarning() << "polyComplete=" << polyComplete; + +// Q_FOREACH(const QPointF &item, polyComplete) +// posContComplete.append(item); + +// posContComplete +// << posContSeg1[0] +// << posContSeg1[3] +// << posContSeg1[2] +// << posContSeg3[2] +// << posContSeg3[1]; + + + d->m_mineField->setPositions(posContSeg1); + + PositionCont minePosCont; + appendQPointF(minePosCont, s_basePoint.geographic(offsetSeg1)); + appendQPointF(minePosCont, s_basePoint.geographic(offsetSeg1 + QPointF(1000, 100))); + appendQPointF(minePosCont, s_basePoint.geographic(offsetSeg1 + QPointF(12000, 12000))); + appendQPointF(minePosCont, s_basePoint.geographic(offsetSeg1 + QPointF(12000, 13000))); + + d->m_mineField->setPositionsMine(MineField::MineState::Activated, minePosCont); + + PositionCont minePosContDeActive; + appendQPointF(minePosContDeActive, s_basePoint.geographic(offsetSeg1 + QPointF(0, 100))); + d->m_mineField->setPositionsMine(MineField::MineState::DeActivated, minePosContDeActive); + + bool success = d->m_mineField->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_mineField->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " MineField dump: " << (*d->m_mineField) << std::endl; + + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &FunctionDialog::onMineFieldUpdate); + timer->setSingleShot(true); + timer->start(5000); +} + +void FunctionDialog::onMineFieldUpdate() +{ + qDebug() << LOGFUNCTION ; + + if (!d->m_mineField) + { + qDebug() << LOGFUNCTION << " no object anymore"; + return; + } + + d->m_mineField->setDrawOrder(10); + + d->m_mineField->setMineVisibility(MineField::MineState::Activated, false); + + d->m_mineField->updateView(); + + // remove some mines: + PositionCont empty; + d->m_mineField->setPositionsMine(MineField::MineState::DeActivated, empty); + + // remove segment: + d->m_mineField->removeSegment(2); + + d->m_mineField->setAesState(AesEffectState::Created); // != Unknown will overwrite fillPattern + + d->m_mineField->setPartyId(5); // Will overwrite line/fillcolor + + + bool success = d->m_mineField->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_mineField->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; +} + +void FunctionDialog::onNuclearCreate() +{ + qDebug() << LOGFUNCTION ; + + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_nuclearAgent)); + + d->m_nuclearAgent = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("NuclearAgentId")); + + if (!d->m_nuclearAgent) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_nuclearAgent->setIdentifier(QLatin1String("NuclearAgent-OutLined")); + + d->m_nuclearAgent->setVisibility(d->m_visibility); + d->m_nuclearAgent->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_nuclearAgent->setVisibility(SituationMap::ObjectAppearance::Tactical, false); + + d->m_nuclearAgent->setSelected(d->m_selected); + + d->m_nuclearAgent->setPartyId(4); // Will overwrite line/fillcolor + + + // set additional values: + SituationMap::PositionCont posCont; + QPointF offset = d->m_objectBasePoint + QPointF(90000, 80000); + + int extent = 1000; + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(-1 * extent, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(-1 * extent, 1 * extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(-2 * extent, 3 * extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(2 * extent, 3 * extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(1 * extent, 1 * extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(1 * extent, 0))); + + std::cout << " posCont=" << posCont.size() << std::endl; + + d->m_nuclearAgent->setPositions(posCont); + + d->m_nuclearAgent->setAesState(AesEffectState::Paused); + + + SituationMap::PositionCont subLine1; + appendQPointF(subLine1, s_basePoint.geographic(offset + QPointF(-1 * extent, 1 * extent))); + appendQPointF(subLine1, s_basePoint.geographic(offset + QPointF(+1 * extent, 1 * extent))); + + d->m_nuclearAgent->setSubsidiaryLine(0, subLine1); + + SituationMap::PositionCont subLine2; + appendQPointF(subLine2, s_basePoint.geographic(offset + QPointF(-1.7 * extent, 2 * extent))); + appendQPointF(subLine2, s_basePoint.geographic(offset + QPointF(+1.7 * extent, 2 * extent))); + + d->m_nuclearAgent-> + setSubsidiaryLine(1, subLine2); + + + bool success = d->m_nuclearAgent->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_nuclearAgent->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " NuclearAgent dump: " << (*d->m_nuclearAgent) << std::endl; + + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &FunctionDialog::onNuclearUpdate); + timer->setSingleShot(true); +// timer->start(2000); +} + +void FunctionDialog::onNuclearUpdate() +{ + qDebug() << LOGFUNCTION ; + + if (!d->m_nuclearAgent) + { + qDebug() << LOGFUNCTION << " no object anymore"; + return; + } + + // move + QPointF offset(d->m_nuclearAgent->positions().value(0).toPointF() + QPointF(10000, 10000)); + + qDebug() << LOGFUNCTION << " update values.." ; + + SituationMap::PositionCont posCont; + + int extent = 10000; + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(-1 * extent, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(-1 * extent, 1 * extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(-2 * extent, 4 * extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(+2 * extent, 4 * extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(+1 * extent, 1 * extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(+1 * extent, 0))); + + d->m_nuclearAgent->setPositions(posCont); + + d->m_nuclearAgent->removeSubsidiaryLines(); + + d->m_nuclearAgent->setAesState(AesEffectState::Activated); + + d->m_nuclearAgent->setPartyId(5); // Will overwrite line/fillcolor + + + bool success = d->m_nuclearAgent->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_nuclearAgent->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + +} + +void FunctionDialog::onChargeEffectCreate() +{ + qDebug() << LOGFUNCTION ; + + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_singleFire)); + + d->m_singleFire = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("SingleFireId")); + + if (!d->m_singleFire) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_singleFire->setIdentifier(QLatin1String("ChargeEffect-OutLined")); + + d->m_singleFire->setVisibility(d->m_visibility); + d->m_singleFire->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_singleFire->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_singleFire->setSelected(d->m_selected); + + d->m_singleFire->setPartyId(3); // Will overwrite line/fillcolor + + + // set additional values: + + QPointF offset = QPointF(84000, 77000); + Point3D origin = Point3D::fromQPointF(s_basePoint.geographic(offset + QPointF())); + qWarning() << LOGFUNCTION <<" origin "<< origin.toPointF(); + //QPointF origin = s_basePoint.geographic(d->m_objectBasePoint + QPointF(155000, 155000)); + + d->m_singleFire->setOrigin(/*Point3D::fromQPointF*/(origin)); + d->m_singleFire->setRadius(300); + + d->m_singleFire->setImpactOrigin(d->m_singleFire->origin()); + + d->m_singleFire->setAesState(AesEffectState::Created); + + bool success = d->m_singleFire->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_singleFire->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " SingleFire dump: " << (*d->m_singleFire) << std::endl; + + + // QTimer *timer = new QTimer(this); + // connect(timer, &QTimer::timeout, this, &FunctionDialog::onChargeEffectUpdate); + // timer->setSingleShot(true); + // timer->start(2000); +} + +void FunctionDialog::onChargeEffectUpdate() +{ + qDebug() << LOGFUNCTION ; + + if (!d->m_singleFire) + { + qDebug() << LOGFUNCTION << " no object anymore"; + return; + } + + // move + //QPointF offset = QPointF(80000, 80000); + //Point3D origin = Point3D::fromQPointF(s_basePoint.geographic(offset + QPointF())); + //QPointF origin = s_basePoint.geographic(d->m_singleFire->origin().toPointF() + QPointF(50, 50)); + + //d->m_singleFire->setOrigin(origin); + d->m_singleFire->setRadius(500); + d->m_singleFire->setImpactOrigin(d->m_singleFire->origin()); + + d->m_singleFire->setPartyId(4); // Will overwrite line/fillcolor + d->m_singleFire->setAesState(AesEffectState::Activated); // created, Prepared, Activated + + bool success = d->m_singleFire->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + +} + +void FunctionDialog::onBoobyTrapCreate() +{ + qDebug() << LOGFUNCTION ; + + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_virtualSingleMineAgent)); + + d->m_virtualSingleMineAgent = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("NuclearAgentId")); + + if (!d->m_virtualSingleMineAgent) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_virtualSingleMineAgent->setIdentifier(QLatin1String("virtualSingleMine-OutLined")); + + d->m_virtualSingleMineAgent->setVisibility(d->m_visibility); + d->m_virtualSingleMineAgent->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_virtualSingleMineAgent->setVisibility(SituationMap::ObjectAppearance::Tactical, false); + + d->m_virtualSingleMineAgent->setSelected(d->m_selected); + + + // set additional values: + SituationMap::PositionCont posCont; + QPointF offset = d->m_objectBasePoint + QPointF(90000, 80000); + + int extent = 1000; + + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(-1 * extent, 3 * extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(3 * extent, 3 * extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(3 * extent, -3 * extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(-1 * extent, -3 * extent))); + + std::cout << " posCont=" << posCont.size() << std::endl; + + d->m_virtualSingleMineAgent->setPositions(posCont); + + d->m_virtualSingleMineAgent->setAesState(AesEffectState::Paused); + + const SituationMap::PartyIdToColorFunctor::PartyData pData1(QStringLiteral("P1"), + AdvancedSymbology::Role::HostileFaker, + Qt::blue, Qt::blue); + d->m_mapInterface->partyIdToColorFunctor().setParty(1, pData1); + + SituationMap::PositionCont subLine1; + appendQPointF(subLine1, s_basePoint.geographic(offset + QPointF(1 * extent, -1 * extent))); + appendQPointF(subLine1, s_basePoint.geographic(offset + QPointF(1 * extent, -3 * extent))); + appendQPointF(subLine1, s_basePoint.geographic(offset + QPointF(+5 * extent, -3 * extent))); + appendQPointF(subLine1, s_basePoint.geographic(offset + QPointF(+5 * extent, -1 * extent))); + + + NuclearAgent::OptionalData optData; + optData.closed = true; + optData.lineStyle = QSharedPointer(new LineStyle(Qt::red, 5, LinePattern::DashDot)); + optData.fillStyle = QSharedPointer(new FillStyle(Qt::red, FillPattern::DiagonalCross)); + d->m_virtualSingleMineAgent->setSubsidiaryLine(0, subLine1, optData); + +// SituationMap::PositionCont subLine2; +// appendQPointF(subLine2, s_basePoint.geographic(offset + QPointF(-1.7 * extent, 2 * extent))); +// appendQPointF(subLine2, s_basePoint.geographic(offset + QPointF(+1.7 * extent, 2 * extent))); +// d->m_virtualSingleMineAgent->setSubsidiaryLine(1, subLine2, false); + + + bool success = d->m_virtualSingleMineAgent->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_virtualSingleMineAgent->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " Virtual Single Mine dump: " << (*d->m_virtualSingleMineAgent) << std::endl; + + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &FunctionDialog::onBoobyTrapUpdate); + timer->setSingleShot(true); + timer->start(2000); + +} + +void FunctionDialog::onBoobyTrapUpdate() +{ + qDebug() << LOGFUNCTION ; + + if (!d->m_virtualSingleMineAgent) + { + qDebug() << LOGFUNCTION << " no object anymore"; + return; + } + + // move + //QPointF offset(d->m_virtualSingleMineAgent->positions().value(0).toPointF() + QPointF(1000, 1000)); + + QPointF offset = d->m_objectBasePoint + QPointF(90000, 80000); + qDebug() << LOGFUNCTION << " update values.." ; + + SituationMap::PositionCont posCont; + + int extent = 1000; + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(-1 * extent, 3 * extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(3 * extent, 3 * extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(3 * extent, -3 * extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(-1 * extent, -3 * extent))); + + + d->m_virtualSingleMineAgent->setPositions(posCont); + + d->m_virtualSingleMineAgent->removeSubsidiaryLines(); + + d->m_virtualSingleMineAgent->setAesState(AesEffectState::Activated); + + //d->m_virtualSingleMineAgent->setPartyId(5); // Will overwrite line/fillcolor + + const SituationMap::PartyIdToColorFunctor::PartyData pData1(QStringLiteral("P1"), + AdvancedSymbology::Role::HostileFaker, + Qt::blue, Qt::blue); + d->m_mapInterface->partyIdToColorFunctor().setParty(3, pData1); + + SituationMap::PositionCont subLine1; + appendQPointF(subLine1, s_basePoint.geographic(offset + QPointF(1 * extent, -1 * extent))); + appendQPointF(subLine1, s_basePoint.geographic(offset + QPointF(1 * extent, -3 * extent))); + appendQPointF(subLine1, s_basePoint.geographic(offset + QPointF(+5 * extent, -3 * extent))); + appendQPointF(subLine1, s_basePoint.geographic(offset + QPointF(+5 * extent, -1 * extent))); + + + NuclearAgent::OptionalData optData; + optData.closed = true; + d->m_virtualSingleMineAgent->setSubsidiaryLine(0, subLine1, optData); + + bool success = d->m_virtualSingleMineAgent->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_virtualSingleMineAgent->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; +} + + +void FunctionDialog::onSingleFireCreate() +{ + qDebug() << LOGFUNCTION ; + + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_singleFire)); + + d->m_singleFire = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("SingleFireId")); + + if (!d->m_singleFire) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_singleFire->setIdentifier(QLatin1String("SingleFire-OutLined")); + + d->m_singleFire->setVisibility(d->m_visibility); + d->m_singleFire->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_singleFire->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_singleFire->setSelected(d->m_selected); + + d->m_singleFire->setPartyId(3); // Will overwrite line/fillcolor + + + // set additional values: + QPointF origin = s_basePoint.geographic(d->m_objectBasePoint + QPointF(155000, 155000)); + + d->m_singleFire->setOrigin(Point3D::fromQPointF(origin)); + d->m_singleFire->setRadius(100); + + d->m_singleFire->setImpactOrigin(d->m_singleFire->origin()); + + d->m_singleFire->setAesState(AesEffectState::Paused); + + bool success = d->m_singleFire->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_singleFire->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " SingleFire dump: " << (*d->m_singleFire) << std::endl; + + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &FunctionDialog::onSingleFireUpdate); + timer->setSingleShot(true); + timer->start(2000); +} + +void FunctionDialog::onSingleFireUpdate() +{ + qDebug() << LOGFUNCTION ; + + if (!d->m_singleFire) + { + qDebug() << LOGFUNCTION << " no object anymore"; + return; + } + + // move + QPointF origin = s_basePoint.geographic(d->m_singleFire->origin().toPointF() + QPointF(50, 50)); + + d->m_singleFire->setOrigin(Point3D::fromQPointF(origin)); + d->m_singleFire->setRadius(250); + d->m_singleFire->setImpactOrigin(d->m_singleFire->origin()); + +// d->m_singleFire->setPartyId(4); // Will overwrite line/fillcolor + d->m_singleFire->setAesState(AesEffectState::Activated); // created, Prepared, Activated + + bool success = d->m_singleFire->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_singleFire->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; +} + +void FunctionDialog::onShowBasePoint() +{ + qDebug() << LOGFUNCTION ; + + if (d->m_basePoint) + { + qDebug() << LOGFUNCTION << " already exists"; + return; + } + + // create and set debugInfo for debugging: + d->m_basePoint = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("BasePointPolygon")); + + if (!d->m_basePoint) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + + d->m_basePoint->setIdentifier(QLatin1String("BasePoint\n SimCoord = 0,0")); + + d->m_basePoint->rLineStyle().setLineColor(Qt::red); + d->m_basePoint->rLineStyle().setLineWidth(8); + + PositionCont posCont; + appendQPointF(posCont, QPointF(0, 0)); + appendQPointF(posCont, d->m_mapImpl->centerPoint()); + + d->m_basePoint->setPositions(posCont); + + bool success = d->m_basePoint->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + + std::cout << " m_basePoint dump: " << (*d->m_basePoint) << std::endl; +} + +void FunctionDialog::onShowPolizei() +{ + onRemoveAll(); + + QString layername = d->m_mapInterface->layerFactory().basemapLayernamePrefix(); + layername += QLatin1String("0"); + + LayerParameter lp = d->m_mapInterface->layerFactory().layerParameter(layername); + lp.opacity = 20; + d->m_mapInterface->layerFactory().setLayerParameter(layername, lp); + + + // Bld + { + RefPointer building; + + building = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("BuildingOutlineId2")); + + if (!building) { qDebug() << LOGFUNCTION << " no object"; return; } + + building->setIdentifier(QLatin1String("Building")); + + building->setVisibility(true); + building->setVisibility(SituationMap::ObjectAppearance::Technical, true); + building->setVisibility(SituationMap::ObjectAppearance::Tactical, true); + + + building->rTextStyle().setTextColor(Qt::magenta); + building->rTextStyle().setSize(30); + building->rTextStyle().setAntiAliased(true); + building->rTextStyle().setHorizontalAlignment(HorizontalAlignment::Left); + building->rTextStyle().setVerticalAlignment(VerticalAlignment::BaseLine); + building->rTextStyle().setTextOutlineWidth(0); + + + // set additional values: + SituationMap::PositionCont posCont; + appendQPointF(posCont, s_basePoint.geographic(QPointF(93076.8, 78331.8))); + appendQPointF(posCont, s_basePoint.geographic(QPointF(93070.9, 78325.0))); + appendQPointF(posCont, s_basePoint.geographic(QPointF(93064.0, 78331.0))); + appendQPointF(posCont, s_basePoint.geographic(QPointF(93069.9, 78337.8))); + + building->setPositions(posCont); + + bool success = building->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + } + + + static bool single = true; + single = !single; + + const QColor participantColor(41, 76, 56); + + QPointF shooterOrigin = s_basePoint.geographic(QPointF(93072, 78344)); + QPointF targetOrigin = s_basePoint.geographic(QPointF(92927, 78333)); + + // part0 - shooter + { + RefPointer participant = d->m_mapInterface->objectFactory(). + create(QLatin1String("Participants"), QLatin1String("ParticipantShooter")); + + participant->setOrigin(Point3D::fromQPointF(shooterOrigin)); + + participant->setTextModifier(Participant::TextModifier::M_HigherFormation, QLatin1String("Amok")); + participant->setTextModifier(Participant::TextModifier::T_UniqueDesignation, QLatin1String("")); + + SymbolData symData; + symData.sidc = QStringLiteral("ETS_Zivil_Person"); + symData.color = Qt::cyan; // Will be overwritten via partyId. + + participant->setSymbol(symData); + participant->setPartyId(3); + participant->forcePartyColor(Qt::gray); + participant->setScale(1.0); + + bool success = participant->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + } + + // part1 - target + { + RefPointer participant = d->m_mapInterface->objectFactory(). + create(QLatin1String("Participants"), QLatin1String("ParticipantPolizei111")); + + participant->setOrigin(Point3D::fromQPointF(targetOrigin)); + + participant->setTextModifier(Participant::TextModifier::M_HigherFormation, QLatin1String("Schupo")); + participant->setTextModifier(Participant::TextModifier::T_UniqueDesignation, QLatin1String("FuStw.1")); + + SymbolData symData; + if (single) + symData.sidc = QStringLiteral("ETS_Polizei_PKW"); + else + symData.sidc = QStringLiteral("ETS_Polizei_FueKraft"); + symData.color = Qt::cyan; // Will be overwritten via partyId. + + participant->setSymbol(symData); + participant->setPartyId(3); + participant->forcePartyColor(participantColor); + if (single) + participant->setScale(1.0); + else + participant->setScale(2.0); + + bool success = participant->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + } + + if (single) + { + // part2 + { + RefPointer participant = d->m_mapInterface->objectFactory(). + create(QLatin1String("Participants"), QLatin1String("ParticipantPolizei222")); + + QPointF origin = s_basePoint.geographic(QPointF(92936, 78368)); + participant->setOrigin(Point3D::fromQPointF(origin)); + + participant->setTextModifier(Participant::TextModifier::M_HigherFormation, QLatin1String("Schupo")); + participant->setTextModifier(Participant::TextModifier::T_UniqueDesignation, QLatin1String("Wm.1")); + + SymbolData symData; + symData.sidc = QStringLiteral("ETS_Polizei_Person"); + symData.color = Qt::cyan; // Will be overwritten via partyId. + symData.size = 1.0; + + participant->setSymbol(symData); + participant->setPartyId(3); + participant->forcePartyColor(participantColor); + participant->setScale(1.0); + + bool success = participant->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + } + + // part3 + { + RefPointer participant = d->m_mapInterface->objectFactory(). + create(QLatin1String("Participants"), QLatin1String("ParticipantPolizei222")); + + QPointF origin = s_basePoint.geographic(QPointF(92944, 78393)); + participant->setOrigin(Point3D::fromQPointF(origin)); + + participant->setTextModifier(Participant::TextModifier::M_HigherFormation, QLatin1String("Schupo")); + participant->setTextModifier(Participant::TextModifier::T_UniqueDesignation, QLatin1String("Wm.2")); + + SymbolData symData; + symData.sidc = QStringLiteral("ETS_Polizei_Person"); + symData.color = Qt::cyan; // Will be overwritten via partyId. + symData.size = 1.0; + + participant->setSymbol(symData); + participant->setPartyId(3); + participant->forcePartyColor(participantColor); + participant->setScale(1.0); + + bool success = participant->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + } + } + + // hitline + { + RefPointer hitLine; + hitLine = d->m_mapInterface->objectFactory(). + create(QLatin1String("Participants"), QLatin1String("HitLineId")); + + if (!hitLine) { qDebug() << LOGFUNCTION << " no object"; return; } + + hitLine->setDrawOrder(11); + hitLine->setVisibility(SituationMap::ObjectAppearance::Technical, true); + hitLine->setVisibility(SituationMap::ObjectAppearance::Tactical, false); + + hitLine->setIdentifier(QLatin1String("M16")); + + hitLine->rLineStyle().setLineColor(Qt::green); + hitLine->rLineStyle().setLineWidth(3); + + hitLine->setStartAndEndPoint(Point3D::fromQPointF(shooterOrigin), + Point3D::fromQPointF(targetOrigin)); + + bool success = hitLine->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + } + + // Area + { + RefPointer functionalArea = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("FunctionalAreaId")); + + if (!functionalArea) { qDebug() << LOGFUNCTION << " no object"; return; } + + functionalArea->setIdentifier(QLatin1String("Medical")); + functionalArea->rTextStyle().setTextColor(Qt::red); + functionalArea->rTextStyle().setTextOutlineWidth(0); + + functionalArea->setVisibility(d->m_visibility); + functionalArea->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + functionalArea->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + functionalArea->setLineStyle(LineStyle(Qt::red, 2, LinePattern::Solid)); + functionalArea->setFillStyle(FillStyle(Qt::red, FillPattern::ForwardDiagonal)); + + SituationMap::PositionCont posCont; + QPointF offset(92850, 78250); + + int extent = 25; + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent, extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(0, extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(0, 0))); + + functionalArea->setPositions(posCont); + + bool success = functionalArea->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + } + +} + +void FunctionDialog::onObjectIndicator() +{ + qCritical() << LOGFUNCTION << " START"; + + d->m_mapInterface->objectIndicator().setMapObjectToPointTo(0); +} + +void FunctionDialog::onMeasureLength() +{ + qDebug() << LOGFUNCTION ; + d->m_mapInterface->measurement().measureLength(); +} + +void FunctionDialog::onStartLocationDisplay() +{ + qCritical() << LOGFUNCTION ; + QPointF center = d->m_mapInterface->centerPoint(); + d->m_mapInterface->locationDisplay().setCurrentLocation(Point3D::fromQPointF(center)); + d->m_mapInterface->locationDisplay().start(); +} + +void FunctionDialog::onStopLocationDisplay() +{ + d->m_mapInterface->locationDisplay().stop(); +} + +void FunctionDialog::onLoadFile() +{ + qCritical() << LOGFUNCTION << " AAAAAAAAAA"; + + { + QString filePath(QLatin1String("/tmp/Observer1Reference.tif")); + + Esri::ArcGISRuntime::Raster *isr = new Esri::ArcGISRuntime::Raster(filePath, this); + if (!isr) + { + qCritical() << LOGFUNCTION << " isr failed"; + return; + } + + Esri::ArcGISRuntime::RasterLayer *rl = new Esri::ArcGISRuntime::RasterLayer(isr, this); + if (!rl) + { + qCritical() << LOGFUNCTION << " rl failed"; + return; + } + + + connect(rl, &Esri::ArcGISRuntime::RasterLayer::errorOccurred, this, [] (const Esri::ArcGISRuntime::Error &error) { + qCritical() << LOGFUNCTION << " EsriError occured: " << toString(error); + }); + + qCritical() << LOGFUNCTION << " getEsriMap()->operationalLayers()->size" << d->m_mapImpl->getEsriMap()->operationalLayers()->size(); + d->m_mapImpl->getEsriMap()->operationalLayers()->append(rl); + qCritical() << LOGFUNCTION << " getEsriMap()->operationalLayers()->size" << d->m_mapImpl->getEsriMap()->operationalLayers()->size(); + + + connect(rl, &Esri::ArcGISRuntime::RasterLayer::loadStatusChanged, this, [] (Esri::ArcGISRuntime::LoadStatus loadStatus) { + qCritical() << LOGFUNCTION << " loadStatus=" << (int)loadStatus; + }); + + + rl->load(); + } +} + +#ifdef USE_LOCAL_SERVER +void FunctionDialog::onStartLocalService() +{ + QPushButton *pb = qobject_cast(sender()); + if (!pb) + return; + + + + SituationMap::LocalServer *localServer = SituationMap::LocalServer::instance(); + + connect(localServer, &LocalServer::localServerStatusChanged, pb, [localServer, pb] () { + if (localServer->isLocalServerStarted()) + pb->setStyleSheet(QLatin1String("QPushButton {background-color: green;}")); + else if (localServer->isLocalServerStopped()) + pb->setStyleSheet(QLatin1String("QPushButton {background-color: gray;}")); + }); + + connect(localServer, &LocalServer::localGeoprocessingServiceStarted, this, [this] (const QString &serviceName, const LocalServiceData &svcData) { + qCritical() << LOGFUNCTION + << " :-) service started: '" << serviceName << "'" + << " url: '" << svcData.svcUrl().toString() << "'" + << " sync: " << svcData.isSync(); + d->m_mapImpl->viewshed().setViewshedUrl(svcData.svcUrl().toString()); + }); + + + // starts LocalServer (and pending GPSsvc, which are none yet.) + localServer->start(); +} + +void FunctionDialog::onStopLocalService() +{ + SituationMap::LocalServer *localServer = SituationMap::LocalServer::instance(); + localServer->stop(); +} + +void FunctionDialog::onStartViewshedSvc() +{ + QPushButton *pb = qobject_cast(sender()); + if (!pb) + return; + + SituationMap::LocalServer *localServer = SituationMap::LocalServer::instance(); + const QString viewshedSvcName(QLatin1String("Viewshed")); + localServer->setLocalGeoprocessingService(viewshedSvcName, + QLatin1String("/shd/CTC/TOOLS/arcgis/EsriJebu/LegatusBremen201803151510.gpk"), + QLatin1String("Viewshed2Feature"), + true); + localServer->startLocalGeoprocessingService(viewshedSvcName); + + connect(localServer, &LocalServer::localGeoprocessingServiceStatusChanged, this, [localServer, pb] (const QString &serviceName) { + if (localServer->isLocalGeoprocessingServiceStarted(serviceName)) + pb->setStyleSheet(QLatin1String("QPushButton {background-color: green;}")); + else if (localServer->isLocalGeoprocessingServiceStopped(serviceName)) + pb->setStyleSheet(QLatin1String("QPushButton {background-color: gray;}")); + }); +} + +void FunctionDialog::onStopViewshedSvc() +{ + SituationMap::LocalServer *localServer = SituationMap::LocalServer::instance(); + const QString viewshedSvcName(QLatin1String("Viewshed")); + localServer->stopLocalGeoprocessingService(viewshedSvcName); +} + + +void FunctionDialog::onViewshedInteractive() +{ + d->m_mapInterface->viewshed().create(); +} + +void FunctionDialog::onViewshed() +{ + static long viewshedId = -1; + + if (viewshedId >= 0) + d->m_mapInterface->viewshed().remove(viewshedId); + + // GP-based: + QPointF viewshedPoint = d->m_mapInterface->centerPoint(); + int distance = 523; //meter + + + if (d->m_viewshedDistance) + { + bool ok = false; + int localDis = d->m_viewshedDistance->text().toInt(&ok); + if (ok) + distance= localDis; + } + + ViewshedParameter viewshedParameter; + viewshedParameter.viewshedOrigin_latLon = viewshedPoint; + viewshedParameter.outerRadius = distance; + viewshedParameter.observerHeight = 15; + + viewshedId = d->m_mapInterface->viewshed().create(viewshedParameter); + qDebug() << LOGFUNCTION +// << " viewshedParameter =" << viewshedParameter; + << " center =" << viewshedPoint + << " distance =" << distance; +} + +#endif // USE_LOCAL_SERVER + +void FunctionDialog::onSightFan() +{ + RB::Rubberband::deleteAllActiveRubberbands(d->m_mapInterface); + + SituationMap::RB::RubberbandEllipse *rubberband = d->m_mapInterface->rubberbandFactory().create(this); + if (rubberband) + { + rubberband->setAutoDelete(true); + rubberband->constrainQuadratic(true); + rubberband->constrainOrthogonal(true); + rubberband->enableSector(true); + + static SituationMap::RefPointer sightFan; + static QString toolLayerName; + + if (toolLayerName.isEmpty()) + { + toolLayerName = d->m_mapImpl->layerFactory().toolLayer(); + } + + if (nullptr == sightFan.get()) + { + connect( d->m_sightFanObserverHeight, &QLineEdit::textEdited, this, [this](const QString&) + { + if (nullptr == sightFan.get()) return; + sightFan->setCenterHat(d->m_sightFanObserverHeight->text().toDouble()); + sightFan->show(); + }); + + connect(d->m_sightFanTargetHeight, &QLineEdit::textEdited, this, [this](const QString&) + { + if (nullptr == sightFan.get()) return; + sightFan->setObjectHat(d->m_sightFanTargetHeight->text().toDouble()); + sightFan->show(); + }); + } + + + std::function updateSightFan = [this](SituationMap::RB::RubberbandBase* rbBase) -> bool + { + SituationMap::RB::RubberbandEllipse *rubberElli = dynamic_cast(rbBase); + if (rubberElli) + { + QPointF center; + double diameter; + double dummy; + QPointF direction; + double observerHeight = d->m_sightFanObserverHeight->text().toDouble(); + double targetHeight = d->m_sightFanTargetHeight->text().toDouble(); + + rubberElli->rectangleParameter(center, diameter, dummy, direction); + + if (diameter > 0.0) + { + double startAngle = 0.0; + double dAngle = 2.0 / 180.0*MathConst::pi; + unsigned int noBeams = 180; + double radius = diameter * 0.5; + + if (rubberElli->hasSector()) + { + startAngle = SituationMap::MathUtils::getMathAngle(rubberElli->sectorStart()); + double endAngle = SituationMap::MathUtils::getMathAngle(rubberElli->sectorEnd()); + + noBeams = ceil(((startAngle > endAngle) ? (endAngle - startAngle + MathConst::pi_two) :(endAngle - startAngle)) / dAngle); + } + + if (nullptr == sightFan.get()) + { + SituationMap::ObjectFactory &objFac = d->m_mapInterface->objectFactory(); + + sightFan = objFac.create(toolLayerName); + if (sightFan) + { + sightFan->initFan(center, observerHeight, targetHeight, radius, radius / 1000.0, startAngle, dAngle, noBeams, 1.0); + } + } + else + { + sightFan->setInnerRadius(radius / 1000.0); + sightFan->setRadius(radius); + sightFan->setStartAngle(startAngle); + sightFan->setNoOfFans(noBeams); + sightFan->move(center); + } + } + } + return true; + }; // std::function + + rubberband->installExternalUpdate(updateSightFan); + + QObject::connect(rubberband, &SituationMap::RB::Rubberband::apply, this, [this](SituationMap::RB::RubberbandBase *rubberband) + { + if (nullptr != sightFan.get()) + { + sightFan->removeMySelf(); + sightFan = nullptr; + } + + SituationMap::RB::RubberbandEllipse *rubberElli = dynamic_cast(rubberband); + if (rubberElli) + { + SituationMap::ObjectFactory &objFac = d->m_mapInterface->objectFactory(); + + sightFan = objFac.create(toolLayerName); + + if (sightFan) + { + QPointF center = rubberElli->centerPoint(); + double diameter; + double dummy; + QPointF direction; + double startAngle = 0.0; + double dAngle = 2.0 / 180.0*MathConst::pi; + unsigned int noBeams = 180; + + double observerHeight = d->m_sightFanObserverHeight->text().toDouble(); + double targetHeight = d->m_sightFanTargetHeight->text().toDouble(); + + rubberElli->rectangleParameter(center, diameter, dummy, direction); + + if (rubberElli->hasSector()) + { + startAngle = SituationMap::MathUtils::getMathAngle(rubberElli->sectorStart()); + double endAngle = SituationMap::MathUtils::getMathAngle(rubberElli->sectorEnd()); + + noBeams = floor((endAngle - startAngle) / dAngle); + } + + double radius = diameter * 0.5; + + sightFan->initFan(center, observerHeight, targetHeight, radius, radius / 1000.0, startAngle, dAngle, noBeams, 1.0); + + SituationMap::RB::RubberbandPointFeature *moveRB = d->m_mapInterface->rubberbandFactory().create(this); + if (moveRB) + { + moveRB->setAutoDelete(true); + moveRB->enableModificationState(); + moveRB->setCoordinateVector( QVector() << sightFan->center() ); + + std::function moveSightFan = [](SituationMap::RB::RubberbandBase* rbBase) -> bool + { + SituationMap::RB::RubberbandPointFeature *rubberPFeat = dynamic_cast(rbBase); + if (rubberPFeat && sightFan) + { + const QPointF & loc = rubberPFeat->singleCoordinate(); + sightFan->move(loc); + } + return true; + }; + + moveRB->installExternalUpdate(moveSightFan); + } + } + } + }); + + QObject::connect(rubberband, &SituationMap::RB::Rubberband::abort, this, [this](SituationMap::RB::RubberbandBase *rubberband) + { + if (nullptr != sightFan.get()) + { + sightFan->removeMySelf(); + sightFan = nullptr; + } + + onRubberbandAbort(rubberband); + }); + } +} // void FunctionDialog::onSightFan() + + +void FunctionDialog::onViewshedSightFan() +{ + static long sightFanId = -1; + + if (sightFanId >= 0) + { + d->m_mapInterface->viewshed().remove(sightFanId); + } + + Viewshed &viewshed = d->m_mapInterface->viewshed(); + sightFanId = viewshed.create(100.0, 1.8, 1.8, 2.5, 1.0); + +} // void FunctionDialog::onViewshedSightFan() + + +void FunctionDialog::onLoadPlan() +{ + qDebug() << LOGFUNCTION ; + + OverlayHandler &overlayHandler = d->m_mapInterface->overlayHandler(); + + QPushButton *pb = qobject_cast(sender()); + if (! pb) + return; + + QString name = pb->property("name").toString(); + + if (name.isEmpty()) + { + qWarning() << LOGFUNCTION << " no name"; + return; + } + + QFile file(QStringLiteral("/tmp/") + name); + if (!file.open((QIODevice::ReadOnly | QIODevice::Text))) + { + qWarning() << LOGFUNCTION << " file open failed"; + return; + } + const QByteArray overlayContent = file.readAll(); + + qWarning() << LOGFUNCTION << " overlayContent.size()='" << overlayContent.size() << "'"; + qWarning() << LOGFUNCTION << " overlayContent='" << overlayContent << "'"; + + int key = pb->property("key").toInt(); + overlayHandler.setOverlay(key, QStringLiteral("OverlayDisplayname"), overlayContent); + overlayHandler.setMinScale(key, 500000); // limiting zoomOut + overlayHandler.setMaxScale(key, 20000); // limiting zoomIn + +} + +void FunctionDialog::onOverlayVisiChanged(bool checked) +{ + qDebug() << LOGFUNCTION; + + QCheckBox *cb = qobject_cast(sender()); + if (! cb) + { + qWarning() << LOGFUNCTION << " no object"; + return; + } + + int key = cb->property("key").toInt(); + OverlayHandler &overlayHandler = d->m_mapInterface->overlayHandler(); + overlayHandler.setOverlayVisibility(key, checked); +} + +void FunctionDialog::onRemovePlan() +{ + qDebug() << LOGFUNCTION; + + OverlayHandler &overlayHandler = d->m_mapInterface->overlayHandler(); + + QString filename; + QPushButton *pb = qobject_cast(sender()); + if (! pb) + return; + + filename = pb->text(); + + if (filename.isEmpty()) + { + qWarning() << LOGFUNCTION << " no filename"; + return; + } + + int key = pb->property("key").toInt(); + overlayHandler.removeOverlay(key); +} + +void FunctionDialog::onRemoveAllPlans() +{ + qDebug() << LOGFUNCTION; + + OverlayHandler &overlayHandler = d->m_mapInterface->overlayHandler(); + + qWarning() << LOGFUNCTION << " overlayCount =" << overlayHandler.overlayCount(); + int nummberRemoved = overlayHandler.removeAllOverlays(); + qWarning() << LOGFUNCTION << " removed =" << nummberRemoved; + qWarning() << LOGFUNCTION << " overlayCount =" << overlayHandler.overlayCount(); +} + +void FunctionDialog::onToPdf() +{ + qDebug() << LOGFUNCTION; + + QString fileName = QDir::tempPath() + QLatin1String("/sitMap.pdf"); + + d->m_mapInterface->toPdf(fileName, + QLatin1String("ScreenshotTitle"), + QLatin1String("Uebung: Test"), + QLatin1String("Benutzer: Norbert"), + QLatin1String("2016-01-01 - 10:11:12")); + + qWarning() << LOGFUNCTION << " pdf written to '" << fileName << "'"; + +} + + +void FunctionDialog::onAreaFireCreate() +{ + qDebug() << LOGFUNCTION ; + // create and set debugInfo for debugging: + d->clearMapObjectPtr(RefPointer(d->m_areaFire)); + + d->m_areaFire = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), QLatin1String("AreaFireId")); + + if (!d->m_areaFire) + { + qDebug() << LOGFUNCTION << " no object"; + return; + } + + d->m_areaFire->setIdentifier(QLatin1String("AreaFire-OutLined")); + + d->m_areaFire->setVisibility(d->m_visibility); + d->m_areaFire->setVisibility(SituationMap::ObjectAppearance::Technical, d->m_visibilityTechn); + d->m_areaFire->setVisibility(SituationMap::ObjectAppearance::Tactical, d->m_visibilityTact); + + d->m_areaFire->setSelected(d->m_selected); + + LineStyle lineStyle(Qt::red, 5, LinePattern::Solid); + FillStyle fillStyle(Qt::blue, FillPattern::Null); + + d->m_areaFire->setLineStyle(lineStyle); + d->m_areaFire->setFillStyle(fillStyle); + + d->m_areaFire->setPartyId(3); // Will overwrite line/fillcolor + d->m_areaFire->setAesState(AesEffectState::Unknown); // != Unknown will overwrite fillPattern + + + // set additional values: + SituationMap::PositionCont posCont; + QPointF offset = d->m_objectBasePoint + QPointF(170000, 170000); + + int extent = 100000; + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent, extent))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent / 2, extent / 2))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, extent))); + + std::cout << " posCont=" << posCont.size() << std::endl; + + d->m_areaFire->setPositions(posCont); + + + posCont.removeAt(3); + d->m_areaFire->setImpacts(posCont); + + bool success = d->m_areaFire->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_areaFire->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + std::cout << " AreaFire dump: " << (*d->m_areaFire) << std::endl; + + + QTimer *timer = new QTimer(this); + connect(timer, &QTimer::timeout, this, &FunctionDialog::onAreaFireUpdate); + timer->setSingleShot(true); + timer->start(2000); + +} + +void FunctionDialog::onAreaFireUpdate() +{ + if (!d->m_areaFire) + { + qDebug() << LOGFUNCTION << " no object anymore"; + return; + } + + d->m_areaFire->setIdentifier(d->m_areaFire->identifier() + QLatin1String("Update")); + + d->m_areaFire->setPartyId(4); // Will overwrite line/fillcolor + d->m_areaFire->setAesState(AesEffectState::Activated); // != Unknown will overwrite fillPattern + + + SituationMap::PositionCont posCont; + QPointF offset = d->m_objectBasePoint + QPointF(170000, 170000); + + int extent = 100000; + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent, 0))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF(extent, extent * 2))); + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, extent))); + + appendQPointF(posCont, s_basePoint.geographic(offset + QPointF( 0, 0))); + + d->m_areaFire->setPositions(posCont); + + posCont.removeAt(1); + posCont.removeAt(3); + d->m_areaFire->setImpacts(posCont); + + d->m_areaFire->setDrawOrder(10); + + bool success = d->m_areaFire->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + + success = d->m_areaFire->updateView(); + qWarning() << LOGFUNCTION << " success=" << success; + +} + + +void FunctionDialog::onDump() +{ + const QString &dump = d->m_mapImpl->toDumpString(); + std::cout << dump << std::endl; + + LtsCoordTrans::dumpConversionCounter(); + +// std::stringstream myLines; +// myLines.str(dump.toStdString()); + +// std::string line; +// { +// while(std::getline(myLines,line,'\n')){ +// qCritical() << QString::fromStdString(line); +// } +// } +} + +void FunctionDialog::onOverviewMap() +{ + OverviewMapHandler &omHdl = d->m_mapInterface->overviewMapHandler(); + + if (omHdl.builtInOverviewMapWidget()) + omHdl.removeBuiltInOverviewMap(); + else + omHdl.createBuiltInOverviewMap(); +} + +void FunctionDialog::onBackupOms() +{ + qDebug() << LOGFUNCTION << " START"; + s_operatorMapSettings() = d->m_mapInterface->operatorMapSettings(); + + // json + QFile saveFile(QStringLiteral("/tmp/oms.json")); + if (!saveFile.open(QIODevice::WriteOnly)) { + qWarning("Couldn't open oms file."); + return; + } + + saveFile.write(s_operatorMapSettings().toJson()); + qCritical() << "wrote " << QFileInfo(saveFile).absoluteFilePath(); + qCritical() << "oms.toJson()=" << s_operatorMapSettings().toJson(); +} + +void FunctionDialog::onRestoreOms() +{ + qDebug() << LOGFUNCTION << " START"; + +// bool success = d->m_mapInterface->restoreOperatorMapSettings(s_operatorMapSettings); +// qDebug() << LOGFUNCTION << " success=" << success; + + + // json + QFile loadFile(QStringLiteral("/tmp/oms.json")); + if (!loadFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open oms file."); + return; + } + + const QByteArray &ba = loadFile.readAll(); + SituationMap::OperatorMapSettings oms; + if (oms.fromJson(ba)) + qWarning("fromJson ok"); + else + qWarning("fromJson failed"); + + + bool success = d->m_mapInterface->restoreOperatorMapSettings(oms); + qDebug() << LOGFUNCTION << " success=" << success; + +} + +void FunctionDialog::onRemove() +{ + qDebug() << LOGFUNCTION << " START XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; + + if (d->m_polygon) + { + qDebug() << LOGFUNCTION << " " << d->m_polygon->debugInfo(); + d->m_polygon->removeMySelf(); + d->m_polygon.reset(); + } + if (d->m_polyline) + { + qDebug() << LOGFUNCTION << " " << d->m_polyline->debugInfo(); + d->m_polyline->removeMySelf(); + d->m_polyline.reset(); + } + if (d->m_text) + { + qDebug() << LOGFUNCTION << " " << d->m_text->debugInfo(); + d->m_text->removeMySelf(); + d->m_text.reset(); + } +// if (d->m_mapObjectIndicator) +// { +// qDebug() << LOGFUNCTION << " " << d->m_mapObjectIndicator->debugInfo(); +// d->m_mapObjectIndicator->removeMySelf(); +// d->m_mapObjectIndicator.reset(); +// } + + + if (d->m_participant) + { + qDebug() << LOGFUNCTION << " " << d->m_participant->debugInfo(); + d->m_participant->removeMySelf(); + d->m_participant.reset(); + } + + if (d->m_building) + { + qDebug() << LOGFUNCTION << " " << d->m_building->debugInfo(); + d->m_building->removeMySelf(); + d->m_building.reset(); + } + + if (d->m_bAgent) + { + qDebug() << LOGFUNCTION << " " << d->m_bAgent->debugInfo(); + d->m_bAgent->removeMySelf(); + d->m_bAgent.reset(); + } + + if (d->m_cAgent) + { + qDebug() << LOGFUNCTION << " " << d->m_cAgent->debugInfo(); + d->m_cAgent->removeMySelf(); + d->m_cAgent.reset(); + } + + if (d->m_dangerArea) + { + qDebug() << LOGFUNCTION << " " << d->m_dangerArea->debugInfo(); + d->m_dangerArea->removeMySelf(); + d->m_dangerArea.reset(); + } + + if (d->m_functionalArea) + { + qDebug() << LOGFUNCTION << " " << d->m_functionalArea->debugInfo(); + d->m_functionalArea->removeMySelf(); + d->m_functionalArea.reset(); + } + + if (d->m_geoImage) + { + qDebug() << LOGFUNCTION << " " << d->m_geoImage->debugInfo(); + d->m_geoImage->removeMySelf(); + d->m_geoImage.reset(); + } + + if (d->m_areaFire) + { + qDebug() << LOGFUNCTION << " " << d->m_areaFire->debugInfo(); + d->m_areaFire->removeMySelf(); + d->m_areaFire.reset(); + } + + if (d->m_soundArea) + { + qDebug() << LOGFUNCTION << " " << d->m_soundArea->debugInfo(); + d->m_soundArea->removeMySelf(); + d->m_soundArea.reset(); + } + + if (d->m_hitLine) + { + qDebug() << LOGFUNCTION << " " << d->m_hitLine->debugInfo(); + d->m_hitLine->removeMySelf(); + d->m_hitLine.reset(); + } + + if (d->m_mine) + { + qDebug() << LOGFUNCTION << " " << d->m_mine->debugInfo(); + d->m_mine->removeMySelf(); + d->m_mine.reset(); + } + + if (d->m_mineField) + { + qDebug() << LOGFUNCTION << " " << d->m_mineField->debugInfo(); + d->m_mineField->removeMySelf(); + d->m_mineField.reset(); + } + + if (d->m_nuclearAgent) + { + qDebug() << LOGFUNCTION << " " << d->m_nuclearAgent->debugInfo(); + d->m_nuclearAgent->removeMySelf(); + d->m_nuclearAgent.reset(); + } + + if (d->m_singleFire) + { + qDebug() << LOGFUNCTION << " " << d->m_singleFire->debugInfo(); + d->m_singleFire->removeMySelf(); + d->m_singleFire.reset(); + } + + if (d->m_cube) + { + qDebug() << LOGFUNCTION << " " << d->m_cube->debugInfo(); + d->m_cube->removeMySelf(); + d->m_cube.reset(); + } + + qDebug() << LOGFUNCTION << " END XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; +} + +void FunctionDialog::onRemoveAll() +{ + qDebug() << LOGFUNCTION << " START XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; + + onRemove(); + + qDebug() << LOGFUNCTION << " clear ObjectFactory"; + d->m_mapImpl->objectFactory().clearObjects(); + + qDebug() << LOGFUNCTION << " END XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; +} + +void FunctionDialog::onToggleGrid() +{ + bool gridOnOff = d->m_mapImpl->grid().isVisible(); + d->m_mapImpl->grid().setVisible(!gridOnOff); + + if (d->m_mapImpl->grid().isVisible()) + d->m_pbGrid->setText(QLatin1String("Grid is ON ")); + else + d->m_pbGrid->setText(QLatin1String("Grid is OFF")); +} + +void FunctionDialog::onToggleVisibility() +{ + d->m_visibility = !d->m_visibility; + qDebug() << LOGFUNCTION << " visibility -> " << d->m_visibility; + + QPushButton *pb = qobject_cast(sender()); + if (! pb) + return; + + if (d->m_visibility) + pb->setText(QLatin1String("Visibility is ON ")); + else + pb->setText(QLatin1String("Visibility is OFF")); + + + const auto objList = d->getObjList(); + for (RefPointer mObj : objList) + { + if (mObj) + { + qDebug() << LOGFUNCTION << " " << mObj->debugInfo(); + mObj->setVisibility(d->m_visibility); + mObj->updateView(); + } + } + +} + +void FunctionDialog::onToggleVisibilityTechn() +{ + d->m_visibilityTechn = !d->m_visibilityTechn; + qDebug() << LOGFUNCTION << " m_visibilityTechn -> " << d->m_visibilityTechn; + + QPushButton *pb = qobject_cast(sender()); + if (! pb) + return; + + if (d->m_visibilityTechn) + pb->setText(QLatin1String("VisiTechn is ON ")); + else + pb->setText(QLatin1String("VisiTechn is OFF")); + + ObjectAppearance oa = SituationMap::ObjectAppearance::Technical; + bool localVisi = d->m_visibilityTechn; + + const auto objList = d->getObjList(); + for (RefPointer mObj : objList) + { + if (mObj) + { + qDebug() << LOGFUNCTION << " " << mObj->debugInfo(); + mObj->setVisibility(oa, localVisi); + mObj->updateView(); + } + } + +} + +void FunctionDialog::onToggleVisibilityTact() +{ + d->m_visibilityTact = !d->m_visibilityTact; + qDebug() << LOGFUNCTION << " m_visibilityTact -> " << d->m_visibilityTact; + + QPushButton *pb = qobject_cast(sender()); + if (! pb) + return; + + if (d->m_visibilityTact) + pb->setText(QLatin1String("VisiTact is ON ")); + else + pb->setText(QLatin1String("VisiTact is OFF")); + + ObjectAppearance oa = SituationMap::ObjectAppearance::Tactical; + bool localVisi = d->m_visibilityTact; + + const auto objList = d->getObjList(); + for (RefPointer mObj : objList) + { + if (mObj) + { + qDebug() << LOGFUNCTION << " " << mObj->debugInfo(); + mObj->setVisibility(oa, localVisi); + mObj->updateView(); + } + } + +} + +void FunctionDialog::onToggleSelection() +{ + d->m_selected = !d->m_selected; + qDebug() << LOGFUNCTION << " m_selected -> " << d->m_selected; + + QPushButton *pb = qobject_cast(sender()); + if (! pb) + return; + + if (d->m_selected) + pb->setText(QLatin1String("Selection is ON ")); + else + pb->setText(QLatin1String("Selection is OFF")); + + const auto objList = d->getObjList(); + for (RefPointer mObj : objList) + { + if (mObj) + { + qDebug() << LOGFUNCTION << " " << mObj->debugInfo(); + mObj->setSelected(d->m_selected); + mObj->updateView(); + } + } + +} + +void FunctionDialog::onTogglePicking() +{ + d->m_pickable = !d->m_pickable; + qDebug() << LOGFUNCTION << " m_pickable -> " << d->m_pickable; + + QPushButton *pb = qobject_cast(sender()); + if (! pb) + return; + + if (d->m_pickable) + pb->setText(QLatin1String("Picking is ON ")); + else + pb->setText(QLatin1String("Picking is OFF")); + + const auto objList = d->getObjList(); + for (RefPointer mObj : objList) + { + if (mObj) + { + qDebug() << LOGFUNCTION << " " << mObj->debugInfo(); + mObj->setPickable(d->m_pickable); + mObj->updateView(); + } + } + +} + +void FunctionDialog::onToggleBoundingBox() +{ + qDebug() << LOGFUNCTION << " m_objectsWithBoundingBox=" << d->m_objectsWithBoundingBox; + + QPushButton *pb = qobject_cast(sender()); + if (! pb) + return; + + if (d->m_objectsWithBoundingBox.isEmpty()) + pb->setText(QLatin1String("BoundingBox is ON ")); + else + pb->setText(QLatin1String("BoundingBox is OFF")); + + // create or destroy? + if (d->m_objectsWithBoundingBox.isEmpty()) + { + // create: + + const auto objList = d->getObjList(); + for (RefPointer mObj : objList) + { + if (mObj) + { + qDebug() << LOGFUNCTION << " " << mObj->debugInfo(); + SituationMap::BoundingBox bbox; + mObj->boundingBox(bbox); + + if (!bbox.isValid()) + { + qWarning() << LOGFUNCTION << " bbox invalid, debugInfo=" << mObj->debugInfo() + << " bbox=" << bbox.toString(); + continue; + } + + qDebug() << LOGFUNCTION + << " debugInfo =" << mObj->debugInfo() + << " bbox =" << bbox.toString(); + + RefPointer moPoly + = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), + QStringLiteral("BoundingBoxPrimitiveOf(%1)").arg(mObj->debugInfo())); + + d->m_objectsWithBoundingBox.append(moPoly->uniqueMapId()); + + switch (bbox.extentType()) + { + case SituationMap::BoundingBox::ExtentType::Sim: + moPoly->setPositions(bbox.positions()); + break; + case SituationMap::BoundingBox::ExtentType::Pixel: + { + const QPointF pCenter = d->m_mapImpl->geoCoordinateToViewPortWindowPoint(bbox.center().toPointF()); // ToDo Check + SituationMap::BoundingBox pixelBox(SituationMap::BoundingBox::ExtentType::Pixel, + Point3D::fromQPointF(pCenter), bbox.width(), bbox.height()); + + const QList points({d->m_mapImpl->viewPortWindowPointToGeoCoordinate(pixelBox.topLeft().toPointF()), // ToDo Check + d->m_mapImpl->viewPortWindowPointToGeoCoordinate(pixelBox.topRight().toPointF()), // ToDo Check + d->m_mapImpl->viewPortWindowPointToGeoCoordinate(pixelBox.bottomRight().toPointF()), // ToDo Check + d->m_mapImpl->viewPortWindowPointToGeoCoordinate(pixelBox.bottomLeft().toPointF()) // ToDo Check + }); + SituationMap::PositionCont posContForCurrentScale = convert2D3D(points); + + moPoly->setPositions(posContForCurrentScale); + } + break; + case SituationMap::BoundingBox::ExtentType::Undefined: + { + qWarning() << LOGFUNCTION << " bbox has no ExtentType, bbox=" << bbox.toString(); + } + + } //switch + + moPoly->rLineStyle().setLineColor(Qt::magenta); + moPoly->updateView(); + } + } + + } + else + { + // destroy: + + qDebug() << LOGFUNCTION << " m_objectsWithBoundingBox=" << d->m_objectsWithBoundingBox; + for (UniqueMapId id : std::as_const(d->m_objectsWithBoundingBox)) + { + RefPointer moPoly + = d->m_mapInterface->objectFactory().get(id); + moPoly->removeMySelf(); + } + d->m_objectsWithBoundingBox.clear(); + } + + +} + +void FunctionDialog::onToggleBB2() +{ + SituationMap::PositionCont posCont; + for (RefPointer mObj : d->getObjList()) + { + if (!mObj) + continue; + qCritical() << LOGFUNCTION << " debugInfo" << mObj->debugInfo(); + + const QVector resultVec = MathUtils::GeometryEngine::buffer(mObj.get(), 100); + + + posCont.clear(); + posCont.reserve(resultVec.size()); + for (const Point3D &p : resultVec) + posCont.append(d->m_mapImpl->coordTranslationFunctor()->mapToGeographic(p)); + + RefPointer bb2Poly + = d->m_mapInterface->objectFactory(). + create(QLatin1String("AES"), + QStringLiteral("BB2PrimitiveOf(%1)").arg(mObj->debugInfo())); + + bb2Poly->setPositions(posCont); + + bb2Poly->rLineStyle().setLineColor(Qt::magenta); + bb2Poly->updateView(); + } +} + +void FunctionDialog::onMapRotationChanged() +{ + float mapRotationDegree = d->getMapRotation(); + + double currrentRotation = d->m_mapImpl->rotation(); + + qDebug() << LOGFUNCTION + << " change from " << currrentRotation + << " to new " << mapRotationDegree; + + d->m_mapImpl->setRotation(mapRotationDegree); +} + +void FunctionDialog::onSymbolSizeChanged() +{ + float newScale = d->getScale(); + qDebug() << LOGFUNCTION << " newScale " << newScale << " START"; + + QSlider *sl = qobject_cast(sender()); + if (! sl) + return; + + sl->setToolTip(QStringLiteral("%1%").arg(newScale * 100)); + + + d->m_mapInterface->setSymbolScale(newScale); + +// Q_FOREACH (RefPointer mObj, d->getObjList()) +// { +// if (mObj) +// { +// qDebug() << LOGFUNCTION << " " << mObj->debugInfo(); +// mObj->setScale(newScale); +// mObj->updateView(); +// } +// } + + qDebug() << LOGFUNCTION << " newScale " << newScale << " END"; + +} + +void FunctionDialog::onSymbolOpacityChanged() +{ + float newOpacity = d->getSymbolOpacity(); + qDebug() << LOGFUNCTION << " newOpacity " << newOpacity << " START"; + + const auto objList = d->getObjList(); + for (RefPointer mObj : objList) + { + if (mObj) + { + qDebug() << LOGFUNCTION << " " << mObj->debugInfo(); + + Participant *part = dynamic_cast(mObj.get()); + + if (part) + { + part->setUserSymbolOpacity(newOpacity); + part->updateView(); + } + } + } + +} + +void FunctionDialog::onDrawOrderChanged() +{ + int drawOrder = d->getDrawOrder(); + qDebug() << LOGFUNCTION << " newDrawOrder " << drawOrder << " START"; + + std::cerr << LOGFUNCTION << " drawOrder = " << drawOrder << std::endl; + + const auto objList = d->getObjList(); + for (RefPointer mObj : objList) + { + if (mObj) + { + qDebug() << LOGFUNCTION << " " << mObj->debugInfo(); + mObj->setDrawOrder(drawOrder); + mObj->updateView(); + } + } + + +} + + +void FunctionDialog::onMouseWindowPositionChanged(int x, int y) +{ + QString strWindow(QLatin1String("Screen: X = ")); + strWindow.append(QString::number(x)); + strWindow.append(QLatin1String(", Y = ")); + strWindow.append(QString::number(y)); + + setMapPositionString(strWindow); + +} /* FunctionDialog::onMouseWindowPositionChanged */ + + +void FunctionDialog::onMouseMapPositionChanged(const Point3D &mapPos, bool isValid) +{ + QString strMap(QLatin1String("\nMap Coord: X = ")); + + strMap.append(QString::number(mapPos.x(), 'f', -1)); + strMap.append(QLatin1String(", Y = ")); + strMap.append(QString::number(mapPos.y(), 'f', -1)); + strMap.append(QLatin1String(", z = ")); + strMap.append(QString::number(mapPos.z(), 'f', -1)); + if (isValid) + strMap.append(QLatin1String(" (valid)")); + else + strMap.append(QLatin1String(" (invalid)")); + + QString strLatLon(QLatin1String("\nLon(x) - Lat(y) Coord: ")); + +// Esri::ArcGISRuntime::Point normalPt = +// Esri::ArcGISRuntime::GeometryEngine::normalizeCentralMeridian(Esri::ArcGISRuntime::Point(mapPos.x(), mapPos.y(), d->m_mapImpl->getEsriSpatialReference())); + + const CoordTranslationFunctor *ctFunctor = d->m_mapImpl->coordTranslationFunctor(); + if (ctFunctor) + { + Point3D geoPos = ctFunctor->mapToGeographic(mapPos); + strLatLon.append(QString::number(geoPos.x(), 'f', 10)); + strLatLon.append(QLatin1String(" - ")); + strLatLon.append(QString::number(geoPos.y(), 'f', 10)); + strLatLon.append(QLatin1String(" - ")); + strLatLon.append(QString::number(geoPos.z(), 'f', -1)); + } + + QString strUTM(QLatin1String("\nUTM Mgrs Coord: ")); +// strUTM.append(EsriRuntimeQt::CoordinateConversion::PointToMgrs(normalPt +// , EsriRuntimeQt::MgrsConversionMode::Automatic +// , 5 +// , true +// , true)); + strUTM.append(d->m_mapImpl->mapToMgrsString(mapPos.toPointF())); + addMapPositionString(strMap); + addMapPositionString(strLatLon); + addMapPositionString(strUTM); + + +} /* FunctionDialog::onMouseMapPositionChanged */ + + +void FunctionDialog::onMouseGeoPositionChanged(const Point3D &geoPos, bool isValid) +{ + QString strSim(QLatin1String("\nGeo Coord: X = ")); + + strSim.append(QString::number(geoPos.x(), 'f', -1)); + strSim.append(QLatin1String(", Y = ")); + strSim.append(QString::number(geoPos.y(), 'f', -1)); + strSim.append(QLatin1String(", z = ")); + strSim.append(QString::number(geoPos.z(), 'f', -1)); + if (isValid) + strSim.append(QLatin1String(" (valid)")); + else + strSim.append(QLatin1String(" (invalid)")); + + addMapPositionString(strSim); + + onExtendChanged(); +} + +void FunctionDialog::onExtendChanged() +{ + double pixelSize = d->m_mapInterface->pixelSizeMeter(); + double scale = d->m_mapInterface->scale(); + setPixelSizeString(QStringLiteral("PixelSize=%1 - Scale=%2").arg(pixelSize).arg(scale)); +} + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/function_dialog.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/function_dialog.h new file mode 100755 index 000000000..f6de06288 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/function_dialog.h @@ -0,0 +1,289 @@ +/** + * @RDE_HEADER@ + * FunctionDialog + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "function_dialog_export.h" +#include +#include +#include + + +// ESRI +#include +#include +#include + +// #define USE_LOCAL_SERVER + + +class QStandardItemModel; + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +namespace RB +{ +class RubberbandBase; +} + +class Map; + +// TODO: nur fuer Tests exportiert +class FUNCTIONDIALOG_EXPORT FunctionDialog : public QWidget +{ + Q_OBJECT + +public: + explicit FunctionDialog(Esri::ArcGISRuntime::Map *esriMap, + Esri::ArcGISRuntime::MapGraphicsView *esriView, + QWidget *parent); + explicit FunctionDialog(SituationMap::Map *map); + virtual ~FunctionDialog(); + + static void addButtonToMap(SituationMap::Map *map, bool isVisible = true); + + + void init(); + + void setGraphicsLayer(Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer); + + void setMapPositionString(const QString &mapPosition); + void setPixelSizeString(const QString &text); + void addMapPositionString(const QString &mapPosition); + + FunctionDialog(const FunctionDialog &other) = delete; // No copy + FunctionDialog &operator=(const FunctionDialog &other) = delete; // No copy + + + +protected: + void addSimpleLayerTab(); + void addLogicalLayerTab(); + void addLayerPerformanceTab(); + void addGraphicsTab(); + void addGraphics3DTab(); + void addMessageTab(); + void addMapObjectTab(); + void addRubberbandTab(); + void addRubberband3DTab(); + void addToolsTab(); + void addViewshedTab(); + void addOverlayPlanTab(); + void addIconRenderTab(); + void addPortalTab(); + void addMovePointLuxemburgTab(); + + + void getRotationAndAngle(const QPointF §Start, const QPointF §End, double &rotationDegOut, double &openAngleDegOut); + // void addYourTab(); + + + Esri::ArcGISRuntime::Graphic *createCube(const SituationMap::PositionCont &mapPosCont, + int baseHeight, + double objHeight, + const QString &partyNameValue = QString()); + + +public Q_SLOTS: + void onDestroyed(); + + void onLayerPerfCreateLayer(); + + + void onGenerateGraphics(); + void onGenerateCube(); + void onGenerateCubeArray(); + void onGenerateStackedCubes(); + void onGenerateGraphicsKristenStrandford(); + void onGenerateUniqueVaueRendererMountingPoints(); + + void onGenerate3DGraphicSphereRENDERER(); + void onSphereOpacityChangedRENDERER(int opacityValue); + + void onGenerate3DGraphicSphere(); + void onSphereOpacityChanged(int opacityValue); + void onSphereZValueChanged(int ZValue); + + void onGenerate3DGraphicCone(); + void onConeOpacityChanged(int opacityValue); + void onConeZValueChanged(int ZValue); + void onConeHeadingChanged(float angle); + void onConeRollChanged(float angle); + void onConePitchChanged(float angle); + + void on3dModel(); + void on3dModelZValueChanged(int ZValue); + + void onGenerate3DGraphicConeUniqueValueRenderer(); + + + + void onAddCivilSymbols(); + void onMessage(); + void onMessageSelect(); + void onAddSymbol(); + void onSomeParticipants(); + + void onRubberbandApply(RB::RubberbandBase *); + void onRubberbandAbort(RB::RubberbandBase *); + void onRubberband3DApply(RB::RubberbandBase *); + void onRubberband3DAbort(RB::RubberbandBase *); + void onRubberbandDefineArrow(); + void onRubberbandDefineEllipse(); + void onRubberbandBoobyTrapSector(); + void onRubberbandCheckCoordinates(); + void onRubberbandStateSelect(); + void onRubberbandTwoInOne(); + void onRectCheckCoordinates(); + + void onPolygonCreate(); + void onPolylineCreate(); + void onPolylineUpdate(); + void onTextCreate(); + void onTextUpdate(); + void onMapObjectIndicatorCreate(); + + + void onParticipantGetPosition(); + void onParticipantGetPositionApplied(RB::RubberbandBase *rubberband = nullptr); + bool createParticipant(const Point3D &origin, + const QString &debugInfo); + void onParticipantCreate(); + void onParticipantUpdate(); + + void onAreaFireCreate(); + void onAreaFireUpdate(); + + void onBuildingCreate(); + void onBuildingUpdate(); + + void onBiologicalAgentCreate(); + void onBiologicalAgentUpdate(); + + void onChemicalAgentCreate(); + + void onCubeCreate(); + + void onDangerAreaCreate(); + void onDangerAreaUpdate(); + + void onFunctionalAreaCreate(); + void onFunctionalAreaUpdate(); + + void onGeoImageCreate(); + void onMapObjectClicked(const SituationMap::Map::RefPointerMapObjectList &mapObjList, + const QPointF &viewportPosition); + + void onSoundAreaCreate(); + void onSoundAreaUpdate(); + + void onHitLineCreate(); + void onHitLineUpdate(); + + void onLineOfSightLineCreate(); + + + void onMineCreate(); + void onMineUpdate(); + + void onMineFieldCreate(); + void onMineFieldUpdate(); + + void onNuclearCreate(); + void onNuclearUpdate(); + + void onChargeEffectCreate(); + void onChargeEffectUpdate(); + + void onBoobyTrapCreate(); + void onBoobyTrapUpdate(); + + void onSingleFireCreate(); + void onSingleFireUpdate(); + + void onShowBasePoint(); + + void onShowPolizei(); + + void onObjectIndicator(); + + void onMeasureLength(); + + void onStartLocationDisplay(); + void onStopLocationDisplay(); + + void onLoadFile(); + + void onPortal(); + + void onMovePoint(); + +#ifdef USE_LOCAL_SERVER + void onStartLocalService(); + void onStopLocalService(); + void onStartViewshedSvc(); + void onStopViewshedSvc(); + + void onViewshedInteractive(); + void onViewshed(); +#endif // USE_LOCAL_SERVER + + void onSightFan(); + void onViewshedSightFan(); + + void onLoadPlan(); + void onOverlayVisiChanged(bool checked); + void onRemovePlan(); + void onRemoveAllPlans(); + + void onToPdf(); + + void onDump(); + + void onOverviewMap(); + + void onBackupOms(); + void onRestoreOms(); + + void onRemove(); + void onRemoveAll(); + void onToggleGrid(); + void onToggleVisibility(); + void onToggleVisibilityTechn(); + void onToggleVisibilityTact(); + void onToggleSelection(); + void onTogglePicking(); + void onToggleBoundingBox(); + void onToggleBB2(); + void onMapRotationChanged(); + void onSymbolSizeChanged(); + void onSymbolOpacityChanged(); + void onDrawOrderChanged(); + + + //void onBuildingPERFORMANCE(); + + void onMouseWindowPositionChanged(int x, int y); + void onMouseMapPositionChanged(const Point3D &mapPos, bool isValid); + void onMouseGeoPositionChanged(const Point3D &geoPos, bool isValid); + + void onExtendChanged(); + +private: + class Private; + /** + * The private d-pointer to not break BC when changing internals of this class + */ + Private *d = nullptr; +}; + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/function_dialog_export.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/function_dialog_export.h new file mode 100755 index 000000000..c4d4f2535 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/function_dialog_export.h @@ -0,0 +1,21 @@ +/** + * Export definitions + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include + +#ifndef FUNCTIONDIALOG_EXPORT +# ifdef BUILD_STATIC_LIBRARIES +# define FUNCTIONDIALOG_EXPORT +# else +# if defined(MAKE_SITUATIONMAPFUNCTIONDIALOG_LIB) +# define FUNCTIONDIALOG_EXPORT Q_DECL_EXPORT +# else +# define FUNCTIONDIALOG_EXPORT Q_DECL_IMPORT +# endif +# endif +#endif diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/simple_esri_layer.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/simple_esri_layer.cpp new file mode 100755 index 000000000..29ecf4ce1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/simple_esri_layer.cpp @@ -0,0 +1,378 @@ +/** + * @RDE_HEADER@ + * FunctionDialog + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "simple_esri_layer.h" + +#include + +#include +#include +#include +#include +#include + + +// for overview +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace SituationMap +{ + +Esri::ArcGISRuntime::Layer *getEsriBasemap_Layer(Esri::ArcGISRuntime::Map *map, + const QString &layername) +{ + if (!(map && + map->basemap() && + map->basemap()->baseLayers()) + ) + return nullptr; + + Esri::ArcGISRuntime::Layer *layerFound = nullptr; + + const Esri::ArcGISRuntime::LayerListModel *model = map->basemap()->baseLayers(); + for (int i = 0; i < model->size(); ++i) + { + const Esri::ArcGISRuntime::Layer *curLayer = model->at(i); + const QString &curName = curLayer->name(); + if (QString::compare(layername, curName) == 0) + { + layerFound = model->at(i); + break; + } + } // for + return layerFound; +} + + +Esri::ArcGISRuntime::ArcGISTiledLayer *getEsriBasemap_ArcGISTiledLayer(Esri::ArcGISRuntime::Map *map, + const QString &layername) +{ + Esri::ArcGISRuntime::Layer *layerFound = getEsriBasemap_Layer(map, layername); + if (! layerFound) + return nullptr; + + if (layerFound->layerType() == Esri::ArcGISRuntime::LayerType::ArcGISTiledLayer) + { + Esri::ArcGISRuntime::ArcGISTiledLayer *layer2 = + static_cast(layerFound); + return layer2; + } + return nullptr; +} + + + +class SimpleEsriLayer::Private +{ + +public: + Private() + { + } + + ~Private() + { + } + + // ESRI data: + Esri::ArcGISRuntime::Map *m_map = nullptr; + Esri::ArcGISRuntime::MapGraphicsView *m_mapView = nullptr; + + // OWN data: + QGridLayout *m_layout = nullptr; + + +}; // class SimpleEsriLayer::Private +//----------------------------------------------------------------------------- + + + +SimpleEsriLayer::SimpleEsriLayer(QWidget *parent, Esri::ArcGISRuntime::MapGraphicsView *mapView) + : QWidget(parent) + , d(new Private) +{ + d->m_mapView = mapView; + if (d->m_mapView) + d->m_map = d->m_mapView->map(); + + d->m_layout = new QGridLayout(this); + + updatePanel(); +} + +SimpleEsriLayer::~SimpleEsriLayer() +{ + delete d; +} + +bool SimpleEsriLayer::updatePanel() +{ + + for (int row = 0; row < d->m_layout->rowCount(); ++row) + { + for (int col = 0; col < d->m_layout->columnCount(); ++col) + { + QLayoutItem *item = d->m_layout->itemAtPosition(row, col); + if (item) + { + QWidget *w = item->widget(); + delete w; + } + } + } + + delete d->m_layout; + d->m_layout = new QGridLayout(this); + + + if (d->m_map) + { + // Header + int row = 0; + int col = 0; + for (const char *name : { "OnOff", "Opac", "Brightness", "Contrast", "Gamma" }) + d->m_layout->addWidget(new QLabel(QString::fromLatin1(name)), row, col++); + + + row = d->m_layout->rowCount(); + + // GraphicalOverlay + Esri::ArcGISRuntime::GraphicsOverlayListModel *graphicsOverlayListModel = d->m_mapView->graphicsOverlays(); + if (graphicsOverlayListModel) + { + int graphicsOverlaySize = graphicsOverlayListModel->size(); +// qDebug() << LOGFUNCTION << " graphicsOverlayListModel -> size=" << graphicsOverlaySize; + + row += graphicsOverlaySize; + + for (int i = 0; i < graphicsOverlaySize; ++i) + { + --row; + col = 0; +// qDebug() << LOGFUNCTION << " graphicsOverlayListModel handle layer i='" << i; + + Esri::ArcGISRuntime::GraphicsOverlay *gOverlay = graphicsOverlayListModel->at(i); + if (! gOverlay) + { + qWarning() << LOGFUNCTION << " gOverlay is nil"; + return false; + } + + const QString &layername = gOverlay->objectName(); + + QCheckBox *cbOnOff = new QCheckBox(layername); + cbOnOff->setChecked(gOverlay->isVisible()); + + connect(cbOnOff, &QCheckBox::stateChanged, this, &SimpleEsriLayer::layerOnOff); + d->m_layout->addWidget(cbOnOff, row, col++); + + SimpleEsriLayerSlider *sl_opac = new SimpleEsriLayerSlider(layername, 0, 100); + sl_opac->setValue(gOverlay->opacity() * 100); + connect(sl_opac, &SimpleEsriLayerSlider::valueChanged, this, &SimpleEsriLayer::opacity); + d->m_layout->addWidget(sl_opac, row, col++); + + } // for + } // if (graphicsOverlayListModel) + + + + // BasemapLayer + row = d->m_layout->rowCount(); + + Esri::ArcGISRuntime::LayerListModel *baseLayerModel = d->m_map->basemap()->baseLayers(); + if (baseLayerModel) + { + int baseLayerModelSize = baseLayerModel->size(); + qWarning() << LOGFUNCTION << " baseLayerModel -> size=" << baseLayerModelSize; + + row += baseLayerModelSize; + + for (int i = 0; i < baseLayerModelSize; ++i) + { + col = 0; + --row; + qWarning() << LOGFUNCTION << " baseLayerModel handle layer i=" << i; + Esri::ArcGISRuntime::Layer *layer = baseLayerModel->at(i); + if (! layer) + { + qWarning() << LOGFUNCTION << " layer is nil"; + return false; + } + + const QString &layername = layer->name(); + + QCheckBox *cbOnOff = new QCheckBox(layername); + cbOnOff->setChecked(layer->isVisible()); + + // QString tooltip = layer->url(); + // if (!l->serviceDescription().isEmpty()) + // tooltip += QLatin1String(" ; ") % l->serviceDescription(); + // if (!tooltip.isEmpty()) + // cbOnOff->setToolTip(tooltip); + + connect(cbOnOff, &QCheckBox::stateChanged, this, &SimpleEsriLayer::layerOnOff); + d->m_layout->addWidget(cbOnOff, row, col++); + + SimpleEsriLayerSlider *sl_opac = new SimpleEsriLayerSlider(layername, 0, 100); + sl_opac->setValue(layer->opacity() * 100); + connect(sl_opac, &SimpleEsriLayerSlider::valueChanged, this, &SimpleEsriLayer::opacity); + d->m_layout->addWidget(sl_opac, row, col++); + + + if (layer->layerType() == Esri::ArcGISRuntime::LayerType::ArcGISTiledLayer + // || layer->type() == EsriRuntimeQt::LayerType::ArcGISTiledMapService + ) + { + + Esri::ArcGISRuntime::ArcGISTiledLayer *realLayer = + dynamic_cast(layer); + if (realLayer) + { + float brightness = realLayer->brightness(); + float contrast = realLayer->contrast(); + float gamma = realLayer->gamma(); + + qWarning() << " brightness=" << brightness; // QuartzBeta1 -> not supported + qWarning() << " contrast=" << contrast; // QuartzBeta1 -> not supported + qWarning() << " gamma=" << gamma; // QuartzBeta1 -> not supported + + SimpleEsriLayerSlider *sl_brightness = new SimpleEsriLayerSlider(layername, -100, 100); + sl_brightness->setValue(brightness * 100); + connect(sl_brightness, &SimpleEsriLayerSlider::valueChanged, this, &SimpleEsriLayer::brightness); + d->m_layout->addWidget(sl_brightness, row, col++); + + SimpleEsriLayerSlider *sl_contrast = new SimpleEsriLayerSlider(layername, -100, 100); + sl_contrast->setValue(contrast * 100); + connect(sl_contrast, &SimpleEsriLayerSlider::valueChanged, this, &SimpleEsriLayer::contrast); + d->m_layout->addWidget(sl_contrast, row, col++); + + SimpleEsriLayerSlider *sl_gamma = new SimpleEsriLayerSlider(layername, -100, 100); + sl_gamma->setValue(gamma * 100); + connect(sl_gamma, &SimpleEsriLayerSlider::valueChanged, this, &SimpleEsriLayer::gamma); + d->m_layout->addWidget(sl_gamma, row, col++); + } // ir (realLayer) + + } // if (layer->type() == Es + } // for + } // if (baseLayerModel) + + + QPushButton *pbMaximizeExtent = new QPushButton(QLatin1String("maximize scale of 1st basemap")); + d->m_layout->addWidget(pbMaximizeExtent, 9999, 0); + connect(pbMaximizeExtent, &QPushButton::clicked, this, [this]() + { + if (d->m_map) + { + d->m_map->setMinScale(500000000); // 5e+08 + d->m_map->setMaxScale(100); + } + }); + + } + else + qWarning() << LOGFUNCTION << " no map !!! "; + + return true; +} + + +void SimpleEsriLayer::layerOnOff() +{ + QCheckBox *cb = qobject_cast(sender()); + if (!cb) + return; + + QString layername = cb->text(); + qDebug() << LOGFUNCTION << " layername=" << layername; + + Esri::ArcGISRuntime::Layer *l = getEsriBasemap_Layer(d->m_map, layername); + if (l) + l->setVisible(cb->isChecked()); + else + qDebug() << LOGFUNCTION << " no layer!!!, layername=" << layername; +} + +void SimpleEsriLayer::opacity(int) +{ + SimpleEsriLayerSlider *sl = qobject_cast(sender()); + if (!sl) + return; + + QString layername = sl->m_layername; + double value = sl->value() / 100.0; + + Esri::ArcGISRuntime::Layer *l = getEsriBasemap_Layer(d->m_map, layername); + if (l) + { + qDebug() << LOGFUNCTION << " layername=" << layername << " set to val=" << value; + l->setOpacity(value); + } + else + qWarning() << LOGFUNCTION << " no layer!!!, layername=" << layername; +} + +void SimpleEsriLayer::brightness(int) +{ + SimpleEsriLayerSlider *sl = qobject_cast(sender()); + if (!sl) + return; + + QString layername = sl->m_layername; + Esri::ArcGISRuntime::ArcGISTiledLayer *l = getEsriBasemap_ArcGISTiledLayer(d->m_map, layername); + + double value = sl->value() / 100.0; +// double value = sl->value(); + + qDebug() << LOGFUNCTION << " layername=" << layername << " set to val=" << value; + + l->setBrightness(value); +} + +void SimpleEsriLayer::contrast(int) +{ + SimpleEsriLayerSlider *sl = qobject_cast(sender()); + if (!sl) + return; + + QString layername = sl->m_layername; + Esri::ArcGISRuntime::ArcGISTiledLayer *l = getEsriBasemap_ArcGISTiledLayer(d->m_map, layername); + + double value = sl->value() / 100.0; +// double value = sl->value(); + + qDebug() << LOGFUNCTION << " layername=" << layername << " set to val=" << value; + + l->setContrast(value); +} + +void SimpleEsriLayer::gamma(int) +{ + SimpleEsriLayerSlider *sl = qobject_cast(sender()); + if (!sl) + return; + + QString layername = sl->m_layername; + Esri::ArcGISRuntime::ArcGISTiledLayer *l = getEsriBasemap_ArcGISTiledLayer(d->m_map, layername); + + double value = sl->value() / 100.0; +// double value = sl->value(); + + qDebug() << LOGFUNCTION << " layername=" << layername << " set to val=" << value; + + l->setGamma(value); +} + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/simple_esri_layer.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/simple_esri_layer.h new file mode 100755 index 000000000..82ec611cb --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/simple_esri_layer.h @@ -0,0 +1,79 @@ +/** + * @RDE_HEADER@ + * FunctionDialog + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include +#include + + +// ESRI +#include +#include +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +class SimpleEsriLayerSlider : public QSlider +{ + Q_OBJECT + +public: + SimpleEsriLayerSlider(const QString &layername, int min, int max) + : QSlider(), m_layername(layername) + { + this->setOrientation(Qt::Horizontal); + setMinimum(min); + setMaximum(max); + } + +public: + QString m_layername; +}; + + +class SimpleEsriLayer : public QWidget +{ + Q_OBJECT + +public: + + SimpleEsriLayer(QWidget *parent, Esri::ArcGISRuntime::MapGraphicsView *mapView); + virtual ~SimpleEsriLayer(); + + SimpleEsriLayer(const SimpleEsriLayer &other) = delete; // No copy + SimpleEsriLayer &operator=(const SimpleEsriLayer &other) = delete; // No copy + + bool updatePanel(); +private slots: + void layerOnOff(); + void opacity(int); + void brightness(int); + void contrast(int); + void gamma(int); + +private : + + class Private; + /** + * The private d-pointer to not break BC when changing internals of this class + */ + Private *d = nullptr; + +}; + + +} // namespace SituationMap + +//---------------------------------------------------------------------------- +// Q_DECLARE_METATYPE(SituationMap::GeometryType); + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/state_icon_a.xpm b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/state_icon_a.xpm new file mode 100755 index 000000000..f56427fa1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/state_icon_a.xpm @@ -0,0 +1,156 @@ +/* XPM */ +static const char * stateIconA_xpm[] = { +"24 24 129 2", +" c None", +". c #4CE146", +"+ c #9E8E3A", +"@ c #4CE045", +"# c #BDAB46", +"$ c #A3912F", +"% c #4BDF44", +"& c #32942E", +"* c #B8C14A", +"= c #CABF35", +"- c #4ADE43", +"; c #F2DA55", +"> c #A7973C", +", c #CDC74E", +"' c #E1D132", +") c #347D25", +"! c #48DD42", +"~ c #E7CF4C", +"{ c #F7D519", +"] c #F8DB3E", +"^ c #9D8E38", +"/ c #E3CC50", +"( c #F6D629", +"_ c #56821F", +": c #37AA32", +"< c #47DB41", +"[ c #DAC54B", +"} c #F6D51F", +"| c #F5D210", +"1 c #F5D211", +"2 c #F5DA3F", +"3 c #819D38", +"4 c #E8D14F", +"5 c #F6D522", +"6 c #A48D0C", +"7 c #46DA40", +"8 c #C3B445", +"9 c #F4D425", +"0 c #F3D00E", +"a c #C9BF13", +"b c #F3D00F", +"c c #F2D840", +"d c #8FAA3D", +"e c #E7D54D", +"f c #F4D31B", +"g c #D4B50C", +"h c #45D93E", +"i c #AC9B3C", +"j c #F3D52C", +"k c #EBCC0D", +"l c #7D9516", +"m c #CCAE0A", +"n c #F1CE0C", +"o c #F1CE0E", +"p c #F4D738", +"q c #F1D015", +"r c #DFC70E", +"s c #43D73D", +"t c #8E7F31", +"u c #F0D434", +"v c #DBC50D", +"w c #588019", +"x c #D6B709", +"y c #EFCC0A", +"z c #EDCB0A", +"A c #5B8F1F", +"B c #42D63B", +"C c #ECD23C", +"D c #C9BA0D", +"E c #3A7F1F", +"F c #DEBD07", +"G c #EDCA08", +"H c #A39309", +"I c #2E972A", +"J c #40D43A", +"K c #E7CF41", +"L c #C2A60A", +"M c #27701E", +"N c #33A92E", +"O c #C1A405", +"P c #38BB32", +"Q c #DCC543", +"R c #9D8709", +"S c #37B931", +"T c #3FD338", +"U c #24711E", +"V c #C2B33F", +"W c #3DD137", +"X c #C8B54A", +"Y c #83A134", +"Z c #3D6517", +"` c #3CD036", +" . c #E4CA37", +".. c #EDD035", +"+. c #29731E", +"@. c #F6D726", +"#. c #F9D821", +"$. c #3ACF34", +"%. c #39CD33", +"&. c #8F8132", +"*. c #F6D414", +"=. c #F6D311", +"-. c #AD9B39", +";. c #38CC32", +">. c #C9B337", +",. c #D9BF33", +"'. c #37CB30", +"). c #E9CB29", +"!. c #F1CD0C", +"~. c #EFD022", +"{. c #36CA2F", +"]. c #EFCD12", +"^. c #EECB09", +"/. c #EECC0C", +"(. c #8C7E2F", +"_. c #35C92E", +":. c #B29F34", +"<. c #EBC806", +"[. c #C4AD31", +"}. c #D7BD29", +"|. c #E8C503", +"1. c #E4C722", +"2. c #E8C714", +"3. c #E7C402", +"4. c #E8C508", +"5. c #97872E", +"6. c #E5C201", +"7. c #E5C200", +"8. c #B8A330", +" ", +" . . . . . . . . + ", +" @ @ @ @ # $ ", +" % & * = ", +" - ; > , ' ) ", +" ! ~ { ] ^ / ( _ : ", +" < [ } | 1 2 3 4 5 6 < ", +" 7 8 9 0 a 0 b c d e f g 7 ", +" h i j k l m n o p q r h ", +" s t u v w x y y z A s ", +" B C D E F G H I B ", +" J K L M N O J ", +" P Q R S T ", +" U V W # X W W ", +" Y Z ` ... ` ` ", +" +. @.#. $. $. ", +" %. &.*.=.-. %. ", +" ;. >.0 0 ,. ;. ", +" '. ).!.!.~. '. ", +" {. ].^.^./.(. {. ", +" _. :.<.<.<.<.[. _. ", +" }.|.|.|.|.1. ", +" 2.3.3.3.3.4. ", +" 5.6.7.7.7.7.7.8. "}; diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/state_icon_b.xpm b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/state_icon_b.xpm new file mode 100755 index 000000000..52c1f0db9 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/state_icon_b.xpm @@ -0,0 +1,216 @@ +/* XPM */ +static const char * stateIconB_xpm[] = { +"24 24 189 2", +" c None", +". c #4A8448", +"+ c #69BC66", +"@ c #75D272", +"# c #7ADA76", +"$ c #77D673", +"% c #6CC269", +"& c #508E4D", +"* c #61AE5E", +"= c #7EE57A", +"- c #73E76E", +"; c #64E55F", +"> c #5BE355", +", c #57E351", +"' c #59E353", +") c #62E55D", +"! c #71E76C", +"~ c #7EE779", +"{ c #68BB65", +"] c #7BE076", +"^ c #69E563", +"/ c #50E149", +"( c #46CE40", +"_ c #34982E", +": c #44C93E", +"< c #4EE047", +"[ c #65E45F", +"} c #7CE377", +"| c #4A8447", +"1 c #51934E", +"2 c #75E56F", +"3 c #51E04B", +"4 c #48D541", +"5 c #46D140", +"6 c #4FE048", +"7 c #71E56B", +"8 c #59A056", +"9 c #6EE368", +"0 c #4BDE44", +"a c #42C73C", +"b c #40BF3A", +"c c #6AE364", +"d c #4C894A", +"e c #6EDF69", +"f c #49DD43", +"g c #40C63B", +"h c #3FC13A", +"i c #6CE068", +"j c #66BD62", +"k c #4BDC45", +"l c #44D03E", +"m c #5EAB5B", +"n c #75D571", +"o c #42CD3D", +"p c #65C062", +"q c #5CDE57", +"r c #46DA40", +"s c #74D670", +"t c #64E05F", +"u c #52DC4C", +"v c #44CF3E", +"w c #5BDD56", +"x c #5AA956", +"y c #46D93F", +"z c #3EC438", +"A c #6DD168", +"B c #4FDB49", +"C c #3CBC36", +"D c #6DD268", +"E c #58A954", +"F c #57C352", +"G c #43D73D", +"H c #389234", +"I c #569F53", +"J c #50DA4A", +"K c #3ABA35", +"L c #3ABB35", +"M c #4FDA49", +"N c #5BA758", +"O c #58C553", +"P c #50D04B", +"Q c #42D63B", +"R c #5CCA57", +"S c #5BCE56", +"T c #50D049", +"U c #46D240", +"V c #40D43A", +"W c #4CD146", +"X c #3ECE38", +"Y c #3DCB38", +"Z c #4AD545", +"` c #41CF3A", +" . c #3FD338", +".. c #42CF3B", +"+. c #43D03C", +"@. c #45CF3E", +"#. c #40D139", +"$. c #41CE3A", +"%. c #3EC738", +"&. c #3DD137", +"*. c #3ABC35", +"=. c #41D23B", +"-. c #BDAB46", +";. c #C8B54A", +">. c #42D23C", +",. c #3ABF35", +"'. c #3BB136", +"). c #3CD036", +"!. c #53A74F", +"~. c #2F812B", +"{. c #64CF60", +"]. c #E4CA37", +"^. c #EDD035", +"/. c #66CF62", +"(. c #30852C", +"_. c #51A44D", +":. c #3DB637", +"<. c #338530", +"[. c #3ACF34", +"}. c #55CF51", +"|. c #33B72E", +"1. c #42CF3D", +"2. c #F6D726", +"3. c #F9D821", +"4. c #3A6E37", +"5. c #44D13E", +"6. c #35BD2F", +"7. c #55CC51", +"8. c #348930", +"9. c #38CA32", +"0. c #3FCF39", +"a. c #57A654", +"b. c #278723", +"c. c #8F8132", +"d. c #F6D414", +"e. c #F6D311", +"f. c #AD9B39", +"g. c #2EA72A", +"h. c #519A4E", +"i. c #42CF3C", +"j. c #2C9928", +"k. c #38CC32", +"l. c #56D351", +"m. c #C9B337", +"n. c #F3D00E", +"o. c #D9BF33", +"p. c #5AD256", +"q. c #2E9F2A", +"r. c #34C02D", +"s. c #38CB31", +"t. c #62D25D", +"u. c #E9CB29", +"v. c #F1CD0C", +"w. c #EFD022", +"x. c #64CD5F", +"y. c #3ACC33", +"z. c #34BF2D", +"A. c #35C62E", +"B. c #3BCB34", +"C. c #67D261", +"D. c #EFCD12", +"E. c #EECB09", +"F. c #EECC0C", +"G. c #8C7E2F", +"H. c #6ACF65", +"I. c #3FCC39", +"J. c #34C42D", +"K. c #39CA32", +"L. c #5BC656", +"M. c #B29F34", +"N. c #EBC806", +"O. c #C4AD31", +"P. c #63CD5E", +"Q. c #3ECB37", +"R. c #34C52D", +"S. c #30B82A", +"T. c #2B8327", +"U. c #D7BD29", +"V. c #E8C503", +"W. c #E4C722", +"X. c #2D9428", +"Y. c #2FB72A", +"Z. c #E8C714", +"`. c #E7C402", +" + c #E8C508", +".+ c #97872E", +"++ c #E5C201", +"@+ c #E5C200", +"#+ c #B8A330", +" . + @ # # $ % & ", +" * = - ; > , , ' ) ! ~ { ", +" ] ^ / ( _ : < [ } | ", +" 1 2 3 4 5 6 7 8 ", +" 9 0 a b 0 c d ", +" e f g h f i ", +" j k l m n = = n m o k p ", +" q r s t u v v u t s r w ", +"x y z A B C C B D z y E ", +"F G H I J K L M N H G O ", +"P Q R Q Q S Q T ", +"U V W X Y Z V U ", +"` . ..+. @.#. .$.", +"%.&. *.=. -.;. >.,. &.%.", +"'.).!. ~.).{. ].^. /.).(. _.).:.", +"<.[.}. |.1. 2.3. 4.5.6. 7.[.8.", +" 9.0.a. b. c.d.e.f. g. h.i.9. ", +" j.k.l. m.n.n.o. p.k.q. ", +" r.s.t. u.v.v.w. x.y.z. ", +" A.B.C. D.E.E.F.G. H.I.A. ", +" J.K.L. M.N.N.N.N.O. P.Q.R. ", +" S.T. U.V.V.V.V.W. X.Y. ", +" Z.`.`.`.`. + ", +" .+++@+@+@+@+@+#+ "}; diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/state_icon_c.xpm b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/state_icon_c.xpm new file mode 100755 index 000000000..edbc31ba5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/state_icon_c.xpm @@ -0,0 +1,110 @@ +/* XPM */ +static const char * stateIconC_xpm[] = { +"24 24 83 1", +" c None", +". c #7FBD7C", +"+ c #8BCF88", +"@ c #84C582", +"# c #588456", +"$ c #63BE5F", +"% c #57B953", +"& c #73BF70", +"* c #56B752", +"= c #499B45", +"- c #699F66", +"; c #59B755", +"> c #54B550", +", c #52B14E", +"' c #6ABC67", +") c #52B34E", +"! c #377334", +"~ c #6FAF6C", +"{ c #50B24C", +"] c #4FB14B", +"^ c #48A144", +"/ c #4C734A", +"( c #5DB559", +"_ c #4DAE49", +": c #4CAD48", +"< c #6BB569", +"[ c #4AAC46", +"} c #377F35", +"| c #5E915C", +"1 c #50AD4C", +"2 c #48A944", +"3 c #44A040", +"4 c #61B05E", +"5 c #45A641", +"6 c #527F50", +"7 c #7BBF78", +"8 c #78BA75", +"9 c #639C60", +"0 c #46A542", +"a c #42A33E", +"b c #368633", +"c c #389035", +"d c #3FA13B", +"e c #43A33F", +"f c #619B5E", +"g c #56AC53", +"h c #3D9D39", +"i c #3B9C37", +"j c #3C9E38", +"k c #54A951", +"l c #64A761", +"m c #3D9E39", +"n c #2A6B26", +"o c #3A9B35", +"p c #3B9B36", +"q c #62A55F", +"r c #4B7749", +"s c #48A243", +"t c #33892F", +"u c #328B2F", +"v c #379833", +"w c #459F41", +"x c #4F7E4D", +"y c #5CA659", +"z c #369632", +"A c #349530", +"B c #349630", +"C c #59A955", +"D c #3A9936", +"E c #276C25", +"F c #256C22", +"G c #32932E", +"H c #2F892B", +"I c #2C8728", +"J c #2F912B", +"K c #2F902B", +"L c #2D8E29", +"M c #2D8F29", +"N c #257322", +"O c #226E1F", +"P c #2B8D27", +"Q c #298826", +"R c #288424", +" ", +" ", +" .+++@ ", +" #$%%% ", +" &***= ", +" -;>>, ", +" ')))! ", +" ~{]]^ ", +" /(__: ", +" <[[[} ", +" |1223 ", +" 4555 ", +" 67778 90aab ", +" cddef gddh ", +" ijjk lmjj ", +" noopq rsoot ", +" uvvwxyvvz ", +" ABBCDBBE ", +" FGGGGGH ", +" IJJJK ", +" LMMN ", +" OPQ ", +" R ", +" "}; diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/symbole2.csv b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/symbole2.csv new file mode 100755 index 000000000..8f85c38d7 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/FunctionDialog/symbole2.csv @@ -0,0 +1,445 @@ +"id";"name";"sym_klassifiz_id";"name";"symbol_id_code";"id";"name";"def_symbol_id" +"611";"ADMINISTRATIVE FINANCE";"331";"FINANCE";"S*G*USAF--*****";"(null)";"(null)";"(null)" +"609";"ADMINISTRATIVE JUDGE ADVOCATE GENERAL (JAG)";"325";"JUDGEADVOCATEGENERAL(JAG)";"S*G*USAJ--*****";"(null)";"(null)";"(null)" +"606";"ADMINISTRATIVE LABOUR";"355";"LABOUR";"S*G*USAL--*****";"(null)";"(null)";"(null)" +"607";"ADMINISTRATIVE MORAL, WELFARE, RECREATION (MWR)";"358";"MORAL,WELFARE,RECREATION(MWR)";"S*G*USAW--*****";"(null)";"(null)";"(null)" +"613";"ADMINISTRATIVE MORTUARY/GRAVE REGISTRY";"337";"MORTUARY/GRAVESREGISTRY";"S*G*USAM--*****";"(null)";"(null)";"(null)" +"612";"ADMINISTRATIVE PERSONNEL SERVICES";"334";"PERSONNELSERVICES";"S*G*USAS--*****";"(null)";"(null)";"(null)" +"610";"ADMINISTRATIVE POSTAL";"328";"POSTAL";"S*G*USAO--*****";"(null)";"(null)";"(null)" +"615";"ADMINISTRATIVE PUPLIC AFFAIRS";"343";"PUBLICAFFAIRS";"S*G*USAP--*****";"(null)";"(null)";"(null)" +"608";"ADMINISTRATIVE QUARTERMASTER (";"361";"QUARTERMASTER(SUPPLY)";"S*G*USAQ--*****";"(null)";"(null)";"(null)" +"614";"ADMINISTRATIVE RELIGIOUS/CHAPLAIN";"340";"RELIGIOUS/CHAPLAIN";"S*G*USAR--*****";"(null)";"(null)";"(null)" +"605";"ADMINISTRATIVE REPLACEMENT HOLDING UNIT (RHV)";"352";"REPLACEMENTHOLDINGUNIT(RHU)";"S*G*USAX--*****";"(null)";"(null)";"(null)" +"364";"AIR DEFENCE";"79";"AIRDEFENCE";"S*G*UCD---*****";"(null)";"(null)";"(null)" +"664";"AIR DEFENCE (AD) MISSILE LAUNC";"471";"AIRDEFENCE(AD)MISSILELAUNCH";"S*G*EWMA--*****";"(null)";"(null)";"(null)" +"379";"AIR DEFENCE COMPOSITE";"90";"COMPOSITE";"S*G*UCDC--*****";"(null)";"(null)";"(null)" +"367";"Air Defence COY";"79";"AIRDEFENCE";"S*G*UCD---*****";"(null)";"(null)";"(null)" +"683";"AIR DEFENCE GUN";"527";"AIRDEFENCEGUN";"S*G*EWA---*****";"(null)";"(null)";"(null)" +"686";"AIR DEFENCE GUN HEAVY";"530";"AIRDEFENCEGUNHEAVY";"S*G*EWAH--*****";"(null)";"(null)";"(null)" +"684";"AIR DEFENCE GUN LIGHT";"528";"AIRDEFENCEGUNLIGHT";"S*G*EWAL--*****";"(null)";"(null)";"(null)" +"685";"AIR DEFENCE GUN MEDIUM";"529";"AIRDEFENCEGUNMEDIUM";"S*G*EWAM--*****";"44";"Soldier Air Defence";"685" +"378";"AIR DEFENCE GUN UNIT";"89";"GUNUNIT";"S*G*UCDG--*****";"(null)";"(null)";"(null)" +"377";"AIR DEFENCE H/MAD";"88";"H/MAD";"S*G*UCDH--*****";"(null)";"(null)";"(null)" +"372";"AIR DEFENCE MISSILE";"83";"AIRDEFENCEMISSILE";"S*G*UCDM--*****";"(null)";"(null)";"(null)" +"376";"AIR DEFENCE MISSILE HEAVY";"87";"AIRDEFENCEMISSILEHEAVY";"S*G*UCDMH-*****";"(null)";"(null)";"(null)" +"373";"AIR DEFENCE MISSILE LIGHT";"84";"AIRDEFENCEMISSILELIGHT";"S*G*UCDML-*****";"229";"Legatus Wiesel";"373" +"375";"AIR DEFENCE MISSILE MEDIUM";"86";"AIRDEFENCEMISSILEMEDIUM";"S*G*UCDMM-*****";"(null)";"(null)";"(null)" +"374";"AIR DEFENCE MISSILE MOTORIZED";"85";"AIRDEFENCEMISSILEMOTORIZED";"S*G*UCDMLA*****";"(null)";"(null)";"(null)" +"369";"AIR DEFENCE SHORT RANGE";"80";"SHORTRANGE";"S*G*UCDS--*****";"(null)";"(null)";"(null)" +"371";"AIR DEFENCE SHORT RANGE GUN";"82";"GUN";"S*G*UCDS--*****";"(null)";"(null)";"(null)" +"370";"AIR DEFENCE SHORT RANGE MISSILE";"81";"MISSILE";"S*G*UCDS--*****";"(null)";"(null)";"(null)" +"380";"AIR DEFENCE TARGETING UNIT";"91";"TARGETINGUNIT";"S*G*UCDT--*****";"(null)";"(null)";"(null)" +"381";"AIR DEFENCE THEATRE MISSILE DEFENCE UNIT";"92";"THEATREMISSILEDEFENCEUNIT";"S*G*UCDO--*****";"(null)";"(null)";"(null)" +"781";"AIRPORT/AIRBASE";"605";"AIRPORT/AIRBASE";"S*G*IBA---H****";"(null)";"(null)";"(null)" +"342";"AIR TRACK";"6";"AIRTRACK";"S*A*------*****";"(null)";"(null)";"(null)" +"777";"AMMUNITION AND EXPLOSIVES PRODUCTION";"595";"AMMUNITIONANDEXPLOSIVESPRODUCTION";"S*G*IME---H****";"(null)";"(null)";"(null)" +"788";"AMPHIBIOUS WARFARE SHIP";"620";"AMPHIBIOUSWARFARESHIP";"S*S*CA----*****";"(null)";"(null)";"(null)" +"414";"ANTI ARMOUR";"110";"ANTIARMOUR";"S*G*UCAA--*****";"(null)";"(null)";"(null)" +"422";"ANTI ARMOUR AIR ASSAULT";"114";"ANTIARMOURAIRASSAULT";"S*G*UCAAS-*****";"(null)";"(null)";"(null)" +"421";"ANTI ARMOUR AIRBORNE";"113";"ANTIARMOURAIRBORNE";"S*G*UCAAM-*****";"(null)";"(null)";"(null)" +"424";"ANTI ARMOUR ARMOURED";"117";"ANTIARMOURARMOURED";"S*G*UCAAA-*****";"(null)";"(null)";"(null)" +"427";"ANTI ARMOUR ARMOURED AIR ASSAU";"120";"ANTIARMOURARMOUREDAIRASSAULT";"S*G*UCAAAS*****";"(null)";"(null)";"(null)" +"425";"ANTI ARMOUR ARMOURED TRACKED";"118";"ANTIARMOURARMOUREDTRACKED";"S*G*UCAAAT*****";"(null)";"(null)";"(null)" +"426";"ANTI ARMOUR ARMOURED WHEELED";"119";"ANTIARMOURARMOUREDWHEELED";"S*G*UCAAAW*****";"(null)";"(null)";"(null)" +"419";"ANTI ARMOUR DISMOUNTED";"111";"ANTIARMOURDISMOUNTED";"S*G*UCAAD-*****";"(null)";"(null)";"(null)" +"420";"ANTI ARMOUR LIGHT";"112";"ANTIARMOURLIGHT";"S*G*UCAAL-*****";"(null)";"(null)";"(null)" +"428";"ANTI ARMOUR MOTORIZED";"121";"ANTIARMOURMOTORIZED";"S*G*UCAAO-*****";"(null)";"(null)";"(null)" +"429";"ANTI ARMOUR MOTORIZED AIR ASSA";"122";"ANTIARMOURMOTORIZEDAIRASSAULT";"S*G*UCAAOS*****";"(null)";"(null)";"(null)" +"423";"ANTI ARMOUR MOUNTAIN";"115";"ANTIARMOURMOUNTAIN";"S*G*UCAAU-*****";"(null)";"(null)";"(null)" +"718";"ANTITANK GUN";"515";"ANTITANKGUN";"S*G*EWG---*****";"130";"Soldier AT (RPG7)";"718" +"722";"ANTITANK GUN HEAVY";"519";"ANTITANKGUNHEAVY";"S*G*EWGH--*****";"(null)";"(null)";"(null)" +"720";"ANTITANK GUN LIGHT";"517";"ANTITANKGUNLIGHT";"S*G*EWGL--*****";"(null)";"(null)";"(null)" +"721";"ANTITANK GUN MEDIUM";"518";"ANTITANKGUNMEDIUM";"S*G*EWGM--*****";"(null)";"(null)";"(null)" +"719";"ANTITANK GUN RECOILLESS";"516";"ANTITANKGUNRECOILLESS";"S*G*EWGR--*****";"(null)";"(null)";"(null)" +"695";"ANTITANK ROCKET LAUNCHER";"492";"ANTITANKROCKETLAUNCHER";"S*G*EWT---*****";"(null)";"(null)";"(null)" +"698";"ANTITANK ROCKET LAUNCHER HEAVY";"495";"ANTITANKROCKETLAUNCHERHEAVY";"S*G*EWTH--*****";"(null)";"(null)";"(null)" +"696";"ANTITANK ROCKET LAUNCHER LIGHT";"493";"ANTITANKROCKETLAUNCHERLIGHT";"S*G*EWTL--*****";"(null)";"(null)";"(null)" +"697";"ANTITANK ROCKET LAUNCHER MEDIU";"494";"ANTITANKROCKETLAUNCHERMEDIUM";"S*G*EWTM--*****";"(null)";"(null)";"(null)" +"392";"ARMOUR";"93";"ARMOUR";"S*G*UCA---*****";"(null)";"(null)";"(null)" +"752";"ARMOURED CARRIER WITH SCATTERA";"561";"ARMOUREDCARRIERWITHSCATTERABLEMINES";"S*G*EVEMSM*****";"(null)";"(null)";"(null)" +"734";"ARMOURED INFANTRY";"542";"ARMOUREDINFANTRY";"S*G*EVAI--*****";"(null)";"(null)";"(null)" +"732";"ARMOURED PERSONNEL CARRIER";"540";"ARMOUREDPERSONNELCARRIER";"S*G*EVAA--*****";"(null)";"(null)";"(null)" +"733";"ARMOURED PERSONNEL CARRIER REC";"541";"ARMOUREDPERSONNELCARRIERRECOVERY";"S*G*EVAAR-*****";"(null)";"(null)";"(null)" +"724";"ARMOURED VEHICLE";"532";"ARMOUREDVEHICLE";"S*G*EVA---*****";"231";"Marder";"724" +"750";"ARMOURED VEHICLE MOUNTED";"558";"ARMOUREDVEHICLEMOUNTED";"S*G*EVEMA-*****";"(null)";"(null)";"(null)" +"397";"ARMOUR TRACK";"94";"ARMOURTRACK";"S*G*UCAT--*****";"(null)";"(null)";"(null)" +"398";"ARMOUR TRACK AIRBORNE";"95";"ARMOURTRACKAIRBORNE";"S*G*UCATA-*****";"(null)";"(null)";"(null)" +"399";"ARMOUR TRACK AMPHIBIOUS";"96";"ARMOURTRACKAMPHIBIOUS";"S*G*UCATW-*****";"(null)";"(null)";"(null)" +"400";"ARMOUR TRACK AMPHIBIOUS RECOVE";"97";"ARMOURTRACKAMPHIBIOUSRECOVERY";"S*G*UCATWR*****";"(null)";"(null)";"(null)" +"403";"ARMOUR TRACK HEAVY";"100";"ARMOURTRACK,HEAVY";"S*G*UCATH-*****";"(null)";"(null)";"(null)" +"401";"ARMOUR TRACK LIGHT";"98";"ARMOURTRACK,LIGHT";"S*G*UCATL-*****";"(null)";"(null)";"(null)" +"402";"ARMOUR TRACK MEDIUM";"99";"ARMOURTRACK,MEDIUM";"S*G*UCATM-*****";"(null)";"(null)";"(null)" +"404";"ARMOUR TRACK RECOVERY";"101";"ARMOURTRACK,RECOVERY";"S*G*UCATR-*****";"(null)";"(null)";"(null)" +"405";"ARMOUR WHEELED";"102";"ARMOUR,WHEELED";"S*G*UCAW--*****";"(null)";"(null)";"(null)" +"406";"ARMOUR WHEELED AIR ASSAULT";"103";"ARMOUR,WHEELEDAIRASSAULT";"S*G*UCAWS-*****";"(null)";"(null)";"(null)" +"407";"ARMOUR WHEELED AIRBORNE";"104";"ARMOUR,WHEELEDAIRBORNE";"S*G*UCAWA-*****";"227";"BMP3 IFV";"407" +"408";"ARMOUR WHEELED AMPHIBIOUS";"105";"ARMOUR,WHEELEDAMPHIBIOUS";"S*G*UCAWA-*****";"(null)";"(null)";"(null)" +"409";"ARMOUR WHEELED AMPHIBIOUS RECO";"1325";"ARMOURWHEELEDAMPHIBIOUSRECOVERY";"S*G*UCAWWR*****";"(null)";"(null)";"(null)" +"412";"ARMOUR WHEELED HEAVY";"108";"ARMOUR,WHEELEDHEAVY";"S*G*UCAWH-*****";"(null)";"(null)";"(null)" +"410";"ARMOUR WHEELED LIGHT";"106";"ARMOUR,WHEELEDLIGHT";"S*G*UCAWL-*****";"(null)";"(null)";"(null)" +"411";"ARMOUR WHEELED MEDIUM";"107";"ARMOUR,WHEELEDMEDIUM";"S*G*UCAWM-*****";"(null)";"(null)";"(null)" +"413";"ARMOUR WHEELED RECOVERY";"109";"ARMOUR,WHEELEDRECOVERY";"S*G*UCAWR-*****";"10";"Armored Recovery Vehicle";"413" +"789";"ASSAULT VESSEL";"621";"ASSAULTVESSEL";"S*S*CALA--*****";"(null)";"(null)";"(null)" +"776";"ATOMIC ENERGY PRODUCTION";"593";"ATOMICENERGYPRODUCTION";"S*G*IMF---H****";"(null)";"(null)";"(null)" +"430";"AVIATION";"123";"AVIATION";"S*G*UCV---*****";"(null)";"(null)";"(null)" +"431";"AVIATION FIXED WING";"124";"FIXEDWING";"S*G*UCVF--*****";"(null)";"(null)";"(null)" +"433";"AVIATION FIXED WING ATTACK";"126";"ATTACKFIXEDWING";"S*G*UCVFA-*****";"(null)";"(null)";"(null)" +"436";"AVIATION FIXED WING RECON";"129";"RECONFIXEDWING";"S*G*UCVFR-*****";"(null)";"(null)";"(null)" +"432";"AVIATION FIXED WING UTILITY";"125";"UTILITYFIXEDWING";"S*G*UCVFU-*****";"(null)";"(null)";"(null)" +"435";"AVIATION FORWARD AIR CONTROLLER (FAC)";"128";"FORWARDAIRCONTROLLER(FAC)";"S*G*UCVUFC*****";"(null)";"(null)";"(null)" +"437";"AVIATION ROTARY WING";"130";"ROTARYWING";"S*G*UCVR--*****";"(null)";"(null)";"(null)" +"440";"AVIATION ROTARY WING C2";"138";"C2ROTARYWING";"S*G*UCVRUC*****";"(null)";"(null)";"(null)" +"441";"AVIATION ROTARY WING MEDEVAC";"139";"MEDEVACROTARYWING";"S*G*UCVRUE*****";"(null)";"(null)";"(null)" +"439";"AVIATION ROTARY WING UTILITY";"134";"UTILITYROTARYWING";"S*G*UCVRU-*****";"(null)";"(null)";"(null)" +"434";"AVIATION TACTICAL AIR CONTROL";"127";"TACTICALAIRCONTROLPARTY(TACP)";"S*G*UCVUTP*****";"(null)";"(null)";"(null)" +"445";"AVIATION UAV CONTROL";"1326";"AVIATIONUAVCONTROL";"S*G*UCVU--*****";"(null)";"(null)";"(null)" +"446";"AVIATION UAV LAUNCHER";"1327";"AVIATIONUAVLAUNCHER";"S*G*UCVU--*****";"(null)";"(null)";"(null)" +"447";"AVIATION UAV RECOVERY";"1337";"AVIATIONUAVRECOVERY";"S*G*UCVU--*****";"(null)";"(null)";"(null)" +"444";"AVIATION UNMANNED AERIAL VEHIC";"145";"UNMANNEDAERIALVEHICLEFIXEDWING";"S*G*UCVUF-*****";"(null)";"(null)";"(null)" +"448";"AVIATION UNMANNED AERIAL VEHICLE ROTARY WING";"146";"UNMANNEDAERIALVEHICLEROTARYWING";"S*G*UCVUR-*****";"(null)";"(null)";"(null)" +"443";"AVIATION UNMANNED AVIATION VEH";"144";"UNMANNEDAERIALVEHICLE";"S*G*UCVU--*****";"(null)";"(null)";"(null)" +"442";"AVIATION VERTICAL/SHORT TAKEOF";"143";"VERTICAL/SHORTTAKEOFFANDLANDING(V/STOL)";"S*G*UCVV--*****";"(null)";"(null)";"(null)" +"769";"BIOLOGICAL";"581";"BIOLOGICAL";"S*G*IRNB--H****";"(null)";"(null)";"(null)" +"745";"BRIDGE";"553";"BRIDGE";"S*G*EVEB--*****";"(null)";"(null)";"(null)" +"778";"BRIDGE";"599";"BRIDGE";"S*G*IMNB--H****";"(null)";"(null)";"(null)" +"739";"BUS";"547";"BUS";"S*G*EVUB--*****";"(null)";"(null)";"(null)" +"735";"C2V/ACV";"543";"C2V/ACV";"S*G*EVAC--*****";"(null)";"(null)";"(null)" +"548";"CBRN";"256";"COMBATSUPPORTCBRN";"S*G*UUA---*****";"(null)";"(null)";"(null)" +"768";"CBRN";"580";"CBRN";"S*G*IRN---H****";"(null)";"(null)";"(null)" +"560";"CBRN BIOLOGICAL";"268";"BIOLOGICAL";"S*G*UUAB--*****";"(null)";"(null)";"(null)" +"561";"CBRN BIOLOGICAL RECON EQUIPPED";"269";"RECONEQUIPPED";"S*G*UUABR-*****";"(null)";"(null)";"(null)" +"549";"CBRN CHEMICAL";"257";"CHEMICAL";"S*G*UUAC--*****";"(null)";"(null)";"(null)" +"556";"CBRN CHEMICAL RECON";"264";"CHEMICALRECON";"S*G*UUACR-*****";"(null)";"(null)";"(null)" +"553";"CBRN CHEMICAL SMOKE";"261";"SMOKE";"S*G*UUACS-*****";"(null)";"(null)";"(null)" +"555";"CBRN CHEMICAL SMOKE ARMOUR";"263";"ARMOURSMOKE";"S*G*UUACSA*****";"(null)";"(null)";"(null)" +"550";"CBRN CHEMICAL SMOKE/DECON";"258";"SMOKE/DECON";"S*G*UUACC-*****";"(null)";"(null)";"(null)" +"551";"CBRN CHEMICAL SMOKE/DECON MECHANIZED";"259";"MECHANIZEDSMOKE/DECON";"S*G*UUACCK*****";"(null)";"(null)";"(null)" +"552";"CBRN CHEMICAL SMOKE/DECON MOTO";"260";"MOTORIZEDSMOKE/DECON";"S*G*UUACCM*****";"(null)";"(null)";"(null)" +"554";"CBRN CHEMICAL SMOKE MOTORIZED";"262";"MOTORIZEDSMOKE";"S*G*UUACSM*****";"(null)";"(null)";"(null)" +"557";"CBRN CHEMICAL WHEELED ARMOURED VEHICLE";"265";"CHEMICALWHEELEDARMOUREDVEHICLE";"S*G*UUACRW*****";"(null)";"(null)";"(null)" +"558";"CBRN CHEMICAL WHEELED ARMOURED VEHICLE RECONNAISAN";"266";"CHEMICALWHEELEDARMOUREDVEHICLERECONNAISSANCESURVEI";"S*G*UUACRS*****";"(null)";"(null)";"(null)" +"562";"CBRN DECON";"270";"DECONTAMINATION";"S*G*UUAD--*****";"(null)";"(null)";"(null)" +"564";"CBRN DECON EQUIPMENT";"1330";"CBRNDECONEQUIPMENT";"S*G*UUADE-*****";"(null)";"(null)";"(null)" +"565";"CBRN DECON EQUIPMENT/TROOP";"1331";"CBRNDECONEQUIPMENT/TROOP";"S*G*UUADET*****";"(null)";"(null)";"(null)" +"563";"CBRN DECON TROOP";"1329";"CBRNDECONTROOP";"S*G*UUADT-*****";"(null)";"(null)";"(null)" +"760";"CBRN EQUIPMENT";"571";"CBRNEQUIPMENT";"S*G*EXN---*****";"(null)";"(null)";"(null)" +"566";"CBRN LABORATORY";"1332";"CBRNLABORATORY";"S*G*UUAL--*****";"(null)";"(null)";"(null)" +"559";"CBRN NUCLEAR";"267";"NUCLEAR";"S*G*UUAN--*****";"(null)";"(null)";"(null)" +"770";"CHEMICAL";"582";"CHEMICAL";"S*G*IRNC--H****";"(null)";"(null)";"(null)" +"356";"CIVIL FIXED WING";"73";"FIXEDWING";"S*A*CF----*****";"(null)";"(null)";"(null)" +"755";"CIVILIAN VEHICLE";"565";"CIVILIANVEHICLE";"S*G*EVC---*****";"(null)";"(null)";"(null)" +"357";"CIVIL ROTARY WING";"74";"ROTARYWING";"S*A*CH----*****";"(null)";"(null)";"(null)" +"604";"COMBAT SERVICE SUPPORT";"321";"COMBATSERVICESUPPORT";"S*G*US----*****";"(null)";"(null)";"(null)" +"736";"COMBAT SERVICE SUPPORT VEHICLE";"544";"COMBATSERVICESUPPORTVEHICLE";"S*G*EVAS--*****";"(null)";"(null)";"(null)" +"547";"COMBAT SUPPORT";"255";"COMBATSUPPORT";"S*G*UU----*****";"(null)";"(null)";"(null)" +"748";"CONSTRUCTION VEHICLE";"556";"CONSTRUCTIONVEHICLE";"S*G*EVEC--*****";"(null)";"(null)";"(null)" +"742";"CROSS-COUNTRY TRUCK";"550";"CROSS-COUNTRYTRUCK";"S*G*EVUX--*****";"212";"TATRA Tanker Fuel";"742" +"742";"CROSS-COUNTRY TRUCK";"550";"CROSS-COUNTRYTRUCK";"S*G*EVUX--*****";"50";"Truck 2t off-road";"742" +"772";"DECON";"585";"DECON";"S*G*IPD---H****";"(null)";"(null)";"(null)" +"676";"DIRECT FIRE GUN";"520";"DIRECTFIREGUN";"S*G*EWD---*****";"(null)";"(null)";"(null)" +"681";"DIRECT FIRE GUN HEAVY";"525";"DIRECTFIREGUNHEAVY";"S*G*EWDH--*****";"(null)";"(null)";"(null)" +"682";"DIRECT FIRE GUN HEAVY SELF-PRO";"526";"DIRECTFIREGUNHEAVYSELF-PROPELLED";"S*G*EWDHS-*****";"(null)";"(null)";"(null)" +"677";"DIRECT FIRE GUN LIGHT";"521";"DIRECTFIREGUNLIGHT";"S*G*EWDL--*****";"(null)";"(null)";"(null)" +"678";"DIRECT FIRE GUN LIGHT SELF PRO";"522";"DIRECTFIREGUNLIGHTSELF-PROPELLED";"S*G*EWDLS-*****";"(null)";"(null)";"(null)" +"679";"DIRECT FIRE GUN MEDIUM";"523";"DIRECTFIREGUNMEDIUM";"S*G*EWDM--*****";"(null)";"(null)";"(null)" +"680";"DIRECT FIRE GUN MEDIUM SELF-PR";"524";"DIRECTFIREGUNMEDIUMSELF-PROPELLED";"S*G*EWDMS-*****";"(null)";"(null)";"(null)" +"753";"DOZER";"562";"DOZER";"S*G*EVED--*****";"215";"CAT Excavator";"753" +"746";"EARTHMOVER";"554";"EARTHMOVER";"S*G*EVEE--*****";"(null)";"(null)";"(null)" +"774";"ELCECTRIC POWER FACILITY";"590";"ELECTRICPOWERFACILITY";"S*G*IUE---H****";"(null)";"(null)";"(null)" +"758";"EMPLACED SENSOR";"568";"EMPLACEDSENSOR";"S*G*ESE---*****";"(null)";"(null)";"(null)" +"467";"ENGINEER";"159";"ENGINEER";"S*G*UCE---*****";"(null)";"(null)";"(null)" +"472";"ENGINEER COMBAT";"160";"ENGINEERCOMBAT";"S*G*UCEC--*****";"(null)";"(null)";"(null)" +"473";"ENGINEER COMBAT AIR ASSAULT";"161";"ENGINEERCOMBATAIRASSAULT";"S*G*UCECS-*****";"(null)";"(null)";"(null)" +"478";"ENGINEER COMBAT AIRBORNE";"162";"ENGINEERCOMBATAIRBORNE";"S*G*UCECA-*****";"(null)";"(null)";"(null)" +"475";"ENGINEER COMBAT BRIDGING";"171";"ENGINEERCOMBATBRIDGING";"S*G*UCEC--*****";"(null)";"(null)";"(null)" +"481";"ENGINEER COMBAT HEAVY";"166";"ENGINEERCOMBATHEAVY";"S*G*UCECH-*****";"(null)";"(null)";"(null)" +"479";"ENGINEER COMBAT LIGHT";"164";"ENGINEERCOMBATLIGHT";"S*G*UCECL-*****";"(null)";"(null)";"(null)" +"482";"ENGINEER COMBAT MECH (TRACK)";"167";"ENGINEERCOMBATMECHANIZED(TRACK)";"S*G*UCECT-*****";"(null)";"(null)";"(null)" +"480";"ENGINEER COMBAT MEDIUM";"165";"ENGINEERCOMBATMEDIUM";"S*G*UCECM-*****";"(null)";"(null)";"(null)" +"476";"ENGINEER COMBAT MINE CLEARING";"172";"ENGINEERCOMBATMINECLEARING";"S*G*UCEC--*****";"(null)";"(null)";"(null)" +"477";"ENGINEER COMBAT MINE LAYING";"173";"ENGINEERCOMBATMINELAYING";"S*G*UCEC--*****";"(null)";"(null)";"(null)" +"483";"ENGINEER COMBAT MOTORIZED";"168";"ENGINEERCOMBATMOTORIZED";"S*G*UCECW-*****";"(null)";"(null)";"(null)" +"484";"ENGINEER COMBAT MOUNTAIN";"169";"ENGINEERCOMBATMOUNTAIN";"S*G*UCECO-*****";"(null)";"(null)";"(null)" +"474";"ENGINEER COMBAT RECON";"170";"ENGINEERCOMBATRECON";"S*G*UCECR-*****";"(null)";"(null)";"(null)" +"485";"ENGINEER CONSTRUCTION";"175";"ENGINEERCONSTRUCTION";"S*G*UCEN--*****";"(null)";"(null)";"(null)" +"744";"ENGINEER VEHICLE";"552";"ENGINEERVEHICLE";"S*G*EVE---*****";"(null)";"(null)";"(null)" +"657";"ENVIRONMENTAL PROTECTION";"463";"ENVIRONMENTALPROTECTION";"S*G*USXEP-*****";"(null)";"(null)";"(null)" +"576";"EW CORPS";"280";"CORPS";"S*G*UUMSEC*****";"(null)";"(null)";"(null)" +"575";"EW THEATRE";"279";"THEATRE";"S*G*UUMSET*****";"(null)";"(null)";"(null)" +"602";"EXPLOSIVE ORDNANCE DISPOSAL";"317";"EXPLOSIVEORDINANCEDISPOSAL";"S*G*UUE---*****";"(null)";"(null)";"(null)" +"490";"FIELD ARTELLERY HOWITZER/GUN AIRBORNE";"181";"AIRBORNE";"S*G*UCFHA-*****";"(null)";"(null)";"(null)" +"495";"FIELD ARTELLERY HOWITZER/GUN AMPHIBIOUS";"187";"AMPHIBIOUS";"S*G*UCFHX-*****";"(null)";"(null)";"(null)" +"494";"FIELD ARTELLERY HOWITZER/GUN HEAVY";"186";"HEAVY";"S*G*UCFHH-*****";"(null)";"(null)";"(null)" +"492";"FIELD ARTELLERY HOWITZER/GUN LIGHT";"184";"LIGHT";"S*G*UCFHL-*****";"(null)";"(null)";"(null)" +"493";"FIELD ARTELLERY HOWITZER/GUN MEDIUM";"185";"MEDIUM";"S*G*UCFHM-*****";"(null)";"(null)";"(null)" +"491";"FIELD ARTELLERY HOWITZER/GUN MOUNTAIN";"183";"MOUNTAIN";"S*G*UCFHO-*****";"(null)";"(null)";"(null)" +"496";"FIELD ARTELLERY ROCKET";"188";"ROCKET";"S*G*UCFR--*****";"(null)";"(null)";"(null)" +"498";"FIELD ARTILEERY ROCKET SINGLE";"190";"SINGLEROCKETSELFPROPELLED";"S*G*UCFRSS*****";"(null)";"(null)";"(null)" +"486";"FIELD ARTILLERY";"177";"FIELDARTILLERY";"S*G*UCF---*****";"(null)";"(null)";"(null)" +"521";"FIELD ARTILLERY ARTILLERY SURV";"214";"ARTILLERYSURVEY";"S*G*UCFS--*****";"(null)";"(null)";"(null)" +"523";"FIELD ARTILLERY FIRE DIRECTION CENTRE";"224";"FIREDIRECTIONCENTRE";"S*G*UCF---*****";"(null)";"(null)";"(null)" +"487";"FIELD ARTILLERY HOWITZER/GUN";"178";"HOWITZER/GUN";"S*G*UCFH--*****";"(null)";"(null)";"(null)" +"489";"FIELD ARTILLERY HOWITZER/GUN AIR ASSAULT";"180";"AIRASSAULT";"S*G*UCFHS-*****";"(null)";"(null)";"(null)" +"488";"FIELD ARTILLERY HOWITZER/GUN SELF-PROPELLED";"179";"SELFPROPELLED";"S*G*UCFHE-*****";"(null)";"(null)";"(null)" +"522";"FIELD ARTILLERY METEOROLOGICAL";"219";"METEOROLOGICAL";"S*G*UCFO--*****";"(null)";"(null)";"(null)" +"513";"FIELD ARTILLERY MORTAR";"205";"MORTAR";"S*G*UCFM--*****";"(null)";"(null)";"(null)" +"514";"FIELD ARTILLERY MORTAR SP TRAC";"206";"SELFPROPELLED(SP)TRACKEDMORTAR";"S*G*UCFMS-*****";"(null)";"(null)";"(null)" +"515";"FIELD ARTILLERY MORTAR SP WHEE";"207";"SPWHEELEDMORTAR";"S*G*UCFMSW*****";"(null)";"(null)";"(null)" +"516";"FIELD ARTILLERY MORTAR TOWED";"208";"TOWEDMORTAR";"S*G*UCFMT-*****";"(null)";"(null)";"(null)" +"518";"FIELD ARTILLERY MORTAR TOWED A";"210";"TOWEDAIRASSAULTMORTAR";"S*G*UCFMTS*****";"(null)";"(null)";"(null)" +"517";"FIELD ARTILLERY MORTAR TOWED A";"209";"TOWEDAIRBORNEMORTAR";"S*G*UCFMTA*****";"(null)";"(null)";"(null)" +"519";"FIELD ARTILLERY MORTAR TOWED M";"212";"TOWEDMOUNTAINMORTAR";"S*G*UCFMTO*****";"(null)";"(null)";"(null)" +"524";"FIELD ARTILLERY OBERSERVER";"225";"OBERSERVER";"S*G*UCF---*****";"(null)";"(null)";"(null)" +"502";"FIELD ARTILLERY ROCKET MULTI R";"194";"MULTIROCKETSELFPROPELLED";"S*G*UCFRMS*****";"(null)";"(null)";"(null)" +"504";"FIELD ARTILLERY ROCKET MULTI ROCKET LAUNCER MULTI";"196";"MULTIROCKETTOWED";"S*G*UCFRMT*****";"(null)";"(null)";"(null)" +"503";"FIELD ARTILLERY ROCKET MULTI ROCKET LAUNCER MULTI";"195";"MULTIROCKETTRUCK";"S*G*UCFRMR*****";"(null)";"(null)";"(null)" +"501";"FIELD ARTILLERY ROCKET MULTI ROCKET LAUNCHER";"193";"MULTIROCKETLAUNCHER";"S*G*UCFRM-*****";"(null)";"(null)";"(null)" +"497";"FIELD ARTILLERY ROCKET SINGLE";"189";"SINGLEROCKETLAUNCHER";"S*G*UCFRS-*****";"(null)";"(null)";"(null)" +"499";"FIELD ARTILLERY ROCKET SINGLE ROCKET LAUNCHER SING";"191";"SINGLEROCKETTRUCK";"S*G*UCFRSR*****";"(null)";"(null)";"(null)" +"500";"FIELD ARTILLERY ROCKET SINGLE ROCKET LAUNCHER SING";"192";"SINGLEROCKETTOWED";"S*G*UCFRST*****";"(null)";"(null)";"(null)" +"506";"FIELD ARTILLERY TARGET ACQUISI";"198";"RADAR";"S*G*UCFTR-*****";"(null)";"(null)";"(null)" +"505";"FIELD ARTILLERY TARGET ACQUISITION";"197";"TARGETACQUISITION";"S*G*UCFT--*****";"(null)";"(null)";"(null)" +"510";"FIELD ARTILLERY TARGET ACQUISITION DISMOUNTED";"202";"TARGETACQUISITIONDISMOUNTED";"S*G*UCFTCD*****";"(null)";"(null)";"(null)" +"508";"FIELD ARTILLERY TARGET ACQUISITION FLASH (OPTICAL)";"200";"FLASH(OPTICAL)";"S*G*UCFTF-*****";"(null)";"(null)";"(null)" +"509";"FIELD ARTILLERY TARGET ACQUISITION MOUNTED";"201";"TARGETACQUISITIONMOUNTED";"S*G*UCFTC-*****";"(null)";"(null)";"(null)" +"512";"FIELD ARTILLERY TARGET ACQUISITION NAVAL GUNFIRE";"204";"TARGETACQUISITIONNAVALGUNFIRE";"S*G*UCFTA-*****";"(null)";"(null)";"(null)" +"507";"FIELD ARTILLERY TARGET ACQUISITION SOUND";"199";"SOUND";"S*G*UCFTS-*****";"(null)";"(null)";"(null)" +"511";"FIELD ARTILLERY TARGET ACQUISITION TRACKED";"203";"TARGETACQUISITIONTRACKED";"S*G*UCFTCM*****";"(null)";"(null)";"(null)" +"345";"FIXED WING DRONE (RPV/UAE)";"36";"DRONE(RPV/UAV)";"S*A*MFQ---*****";"(null)";"(null)";"(null)" +"761";"FLAME THROWER";"572";"FLAMETHROWER";"S*G*EXF---*****";"(null)";"(null)";"(null)" +"660";"GENERAL HEADQUATERS AND SERVICE";"466";"GENERALHEADQUARTERSANDSERVICE";"S*G*UH----*****";"(null)";"(null)";"(null)" +"779";"GOVERNMENT LEADERSHIP";"603";"GOVERNMENTLEADERSHIP";"S*G*IG----H****";"(null)";"(null)";"(null)" +"703";"GRENADE LAUNCHER";"500";"GRENADELAUNCHER";"S*G*EWZ---*****";"(null)";"(null)";"(null)" +"706";"GRENADE LAUNCHER HEAVY";"503";"GRENADELAUNCHERHEAVY";"S*G*EWZH--*****";"(null)";"(null)";"(null)" +"704";"GRENADE LAUNCHER LIGHT";"501";"GRENADELAUNCHERLIGHT";"S*G*EWZL--*****";"(null)";"(null)";"(null)" +"705";"GRENADE LAUNCHER MEDIUM";"502";"GRENADELAUNCHERMEDIUM";"S*G*EWZM--*****";"(null)";"(null)";"(null)" +"662";"GROUND TRACK EQUIPMENT";"468";"GROUNDTRACKEQUIPMENT";"S*G*E-----*****";"(null)";"(null)";"(null)" +"358";"GROUND TRACK UNIT";"77";"UNIT";"S*G*U-----*****";"217";"Soldier (Role Player)";"358" +"358";"GROUND TRACK UNIT";"77";"UNIT";"S*G*U-----*****";"106";"Soldier";"358" +"363";"GROUND TRACK UNIT COMBAT";"78";"COMBAT";"S*G*UC----*****";"(null)";"(null)";"(null)" +"520";"GROUND TRACK UNIT COMBAT FIELD ARTILLERY MORTAR AM";"213";"AMPHIBIOUSMORTAR";"S*G*UCFML-*****";"(null)";"(null)";"(null)" +"659";"GROUND TRACK UNIT GENERAL HEADQUATERS";"465";"UNITGENERALHEADQUARTERS";"S*G*UH----*****";"(null)";"(null)";"(null)" +"723";"GROUND VEHICLE";"531";"GROUNDVEHICLE";"S*G*EV----*****";"(null)";"(null)";"(null)" +"702";"HEAVY MACHINE GUN";"499";"HEAVYMACHINEGUN";"S*G*EWRH--*****";"129";"Soldier HMG";"702" +"785";"HOSPITAL";"609";"HOSPITAL";"S*G*IXH---H****";"(null)";"(null)";"(null)" +"711";"HOWITZER";"508";"HOWITZER";"S*G*EWH---*****";"(null)";"(null)";"(null)" +"716";"HOWITZER HEAVY";"513";"HOWITZERHEAVY";"S*G*EWHH--*****";"(null)";"(null)";"(null)" +"717";"HOWITZER HEAVY SELF-PROPELLED";"514";"HOWITZERHEAVYSELF-PROPELLED";"S*G*EWHHS-*****";"(null)";"(null)";"(null)" +"712";"HOWITZER LIGHT";"509";"HOWITZERLIGHT";"S*G*EWHL--*****";"(null)";"(null)";"(null)" +"713";"HOWITZER LIGHT SELF-PROPELLED";"510";"HOWITZERLIGHTSELF-PROPELLED";"S*G*EWHLS-*****";"(null)";"(null)";"(null)" +"714";"HOWITZER MEDIUM";"511";"HOWITZERMEDIUM";"S*G*EWHM--*****";"(null)";"(null)";"(null)" +"715";"HOWITZER MEDIUM SELF-PROPELLED";"512";"HOWITZERMEDIUMSELF-PROPELLED";"S*G*EWHMS-*****";"(null)";"(null)";"(null)" +"449";"INFANTRY";"149";"INFANTRY";"S*G*UCI---*****";"161";"TM";"449" +"449";"INFANTRY";"149";"INFANTRY";"S*G*UCI---*****";"226";"TEST PLAYER";"449" +"449";"INFANTRY";"149";"INFANTRY";"S*G*UCI---*****";"232";"0Soldier";"449" +"449";"INFANTRY";"149";"INFANTRY";"S*G*UCI---*****";"128";"TM Leader";"449" +"463";"INFANTRY AIR ASSAULT";"154";"INFANTRYAIRASSAULT";"S*G*UCIS--*****";"(null)";"(null)";"(null)" +"462";"INFANTRY AIRBORNE";"153";"INFANTRYAIRBORNE";"S*G*UCIA--*****";"(null)";"(null)";"(null)" +"466";"INFANTRY FIGHTING VEHICLE";"157";"INFANTRYFIGHTINGVEHICLE";"S*G*UCII--*****";"(null)";"(null)";"(null)" +"454";"INFANTRY LIGHT";"150";"INFANTRYLIGHT";"S*G*UCIL--*****";"(null)";"(null)";"(null)" +"464";"INFANTRY MECHANIZED";"155";"INFANTRYMECHANIZED";"S*G*UCIZ--*****";"(null)";"(null)";"(null)" +"456";"INFANTRY MOTORIZED";"151";"INFANTRYMOTORIZED";"S*G*UCIM--*****";"(null)";"(null)";"(null)" +"461";"INFANTRY MOUNTAIN";"152";"INFANTRYMOUNTAIN";"S*G*UCIO--*****";"(null)";"(null)";"(null)" +"465";"INFANTRY NAVAL";"156";"INFANTRYNAVAL";"S*G*UCIN--*****";"(null)";"(null)";"(null)" +"600";"INFORMATION OPERATIONS";"315";"INFORMATIONWARFAREUNIT";"S*G*UUI---*****";"(null)";"(null)";"(null)" +"765";"INSTALLATION";"576";"INSTALLATION";"S*G*I-----H****";"225";"MEU";"765" +"765";"INSTALLATION";"576";"INSTALLATION";"S*G*I-----H****";"224";"Building";"765" +"666";"INTERMEDIATE RANGE AD MISSILE";"473";"INTERMEDIATERANGEADMISSILELAUNCH";"S*G*EWMAI-*****";"(null)";"(null)";"(null)" +"670";"INTERMEDIATE RANGE SS MISSILE";"478";"INTERMEDIATERANGESSMISSILELAUNCH";"S*G*EWMSI-*****";"(null)";"(null)";"(null)" +"382";"INTERNAL SECURITY FORCES";"246";"INTERNALSECURITYFORCES";"S*G*UCS---*****";"(null)";"(null)";"(null)" +"390";"INTERNAL SECURITY FORCES AVIAT";"254";"AVIATION";"S*G*UCSA--*****";"(null)";"(null)";"(null)" +"387";"INTERNAL SECURITY FORCES GROUN";"251";"MECHANIZEDGROUND";"S*G*UCSGA-*****";"(null)";"(null)";"(null)" +"384";"INTERNAL SECURITY FORCES GROUND";"248";"GROUND";"S*G*UCSG--*****";"(null)";"(null)";"(null)" +"385";"INTERNAL SECURITY FORCES GROUND DISMOUNTED";"249";"DISMOUNTEDGROUND";"S*G*UCSGD-*****";"(null)";"(null)";"(null)" +"386";"INTERNAL SECURITY FORCES GROUND MOTORIZED";"250";"MOTORIZEDGROUND";"S*G*UCSGM-*****";"(null)";"(null)";"(null)" +"389";"INTERNAL SECURITY FORCES RAILROAD";"253";"RAILROAD";"S*G*UCSR--*****";"(null)";"(null)";"(null)" +"383";"INTERNAL SECURITY FORCES RIVERINE";"247";"RIVERINE";"S*G*UCSW--*****";"(null)";"(null)";"(null)" +"388";"INTERNAL SECURITY FORCES WHEEL";"252";"WHEELEDMECHANIZED";"S*G*UCSM--*****";"(null)";"(null)";"(null)" +"791";"LANDING CRAFT";"623";"LANDINGCRAFT";"S*S*CALC--*****";"(null)";"(null)";"(null)" +"790";"LANDING SHIP";"622";"LANDINGSHIP";"S*S*CALS--*****";"(null)";"(null)";"(null)" +"601";"LANDING SUPPORT";"316";"LANDINGSUPPORT";"S*G*UUX---*****";"(null)";"(null)";"(null)" +"762";"LAND MINES";"573";"LANDMINES";"S*G*EXM---*****";"(null)";"(null)";"(null)" +"763";"LAND MINES, LETHAL";"574";"LANDMINES,LETHAL";"S*G*EXMC--*****";"(null)";"(null)";"(null)" +"759";"LASER";"570";"LASER";"S*G*EXL---*****";"(null)";"(null)";"(null)" +"584";"LAW ENFORCEMENT UNIT";"292";"LAWENFORCEMENTUNIT";"S*G*UUL---*****";"(null)";"(null)";"(null)" +"587";"LAW ENFORCEMENT UNIT CIVILIAN LAW ENFORCEMENT";"295";"CIVILIANLAWENFORCEMENT";"S*G*UULC--*****";"(null)";"(null)";"(null)" +"588";"LAW ENFORCEMENT UNIT CRIMINAL INVESTIGATION DIVISI";"297";"CENTRALINTELLIGENCEDIVISION(CID)";"S*G*UULD--*****";"(null)";"(null)";"(null)" +"586";"LAW ENFORCEMENT UNIT MILITARY POLICE";"294";"MILITARYPOLICE";"S*G*UULM--*****";"(null)";"(null)";"(null)" +"585";"LAW ENFORCEMENT UNIT SHORE PATROL";"293";"SHOREPATROL";"S*G*UULS--*****";"(null)";"(null)";"(null)" +"794";"LEISURE CRAFT";"665";"LEISURECRAFT";"S*S*XR----*****";"(null)";"(null)";"(null)" +"764";"LESS THAN LETHAL";"575";"LESSTHANLETHAL";"S*G*EXML--*****";"(null)";"(null)";"(null)" +"737";"LIGHT ARMOURED VEHICLE";"545";"LIGHTARMOUREDVEHICLE";"S*G*EVAL--*****";"(null)";"(null)";"(null)" +"701";"LIGHT MACHINE GUN";"498";"LIGHTMACHINEGUN";"S*G*EWRL--*****";"131";"Soldier LMG";"701" +"741";"LIMITED CROSS-COUNTRY TRUCK";"549";"LIMITEDCROSS-COUNTRYTRUCK";"S*G*EVUL--*****";"(null)";"(null)";"(null)" +"667";"LONG RANGE AD MISSILE LAUNCH";"474";"LONGRANGEADMISSILELAUNCH";"S*G*EWMAL-*****";"(null)";"(null)";"(null)" +"671";"LONG RANGE SS MISSILE LAUNCH";"479";"LONGRANGESSMISSILELAUNCH";"S*G*EWMSL-*****";"(null)";"(null)";"(null)" +"648";"MAINTENANCE";"442";"MAINTENANCE";"S*G*USX---*****";"(null)";"(null)";"(null)" +"654";"MAINTENANCE BATTLE DAMAGE REPA";"460";"BATTLEDAMAGEREPAIR";"S*G*USXBDR*****";"(null)";"(null)";"(null)" +"653";"MAINTENANCE ELECTRO-OPTICAL";"457";"ELECTRO-OPTICAL";"S*G*USXE--*****";"(null)";"(null)";"(null)" +"649";"MAINTENANCE HEAVY";"445";"MAINTENANCEHEAVY";"S*G*USXH--*****";"(null)";"(null)";"(null)" +"651";"MAINTENANCE ORDINANCE";"451";"ORDINANCE";"S*G*USXO--*****";"(null)";"(null)";"(null)" +"652";"MAINTENANCE ORDINANCE MISSILE";"454";"ORDINANCEMISSILE";"S*G*USXOM-*****";"(null)";"(null)";"(null)" +"655";"MAINTENANCE PREVENTIVE MAINTEN";"461";"PREVENTIVEMAINTANANCE";"S*G*USXPM-*****";"(null)";"(null)";"(null)" +"650";"MAINTENANCE RECOVERY";"448";"MAINTENANCERECOVERY";"S*G*USXR--*****";"(null)";"(null)";"(null)" +"623";"MEDICAL DENTAL";"373";"MEDICALDENTAL";"S*G*USMD--*****";"(null)";"(null)";"(null)" +"784";"MEDICAL FACILITY";"608";"MEDICALFACILITY";"S*G*IX----H****";"(null)";"(null)";"(null)" +"624";"MEDICAL PSYCHOLOGICAL";"376";"MEDICALPSYCHOLOGICAL";"S*G*USMP--*****";"(null)";"(null)";"(null)" +"621";"MEDICAL TREATMENT FACILITY";"367";"MEDICALTREATMENTFACILITY";"S*G*USMM--*****";"(null)";"(null)";"(null)" +"622";"MEDICAL VETERINARY";"370";"MEDICALVETERINARY";"S*G*USMV--*****";"(null)";"(null)";"(null)" +"343";"MILITARY";"7";"MILITARY";"S*A*M-----*****";"(null)";"(null)";"(null)" +"780";"MILITARY BASE/FACILITY";"604";"MILITARYBASE/FACILITY";"S*G*IB----H****";"(null)";"(null)";"(null)" +"344";"MILITARY FIXED WING";"8";"FIXEDWING";"S*A*MF----*****";"(null)";"(null)";"(null)" +"567";"MILITARY INTELLIGENCE";"271";"MILITARYINTELLIGENCE";"S*G*UUM---*****";"(null)";"(null)";"(null)" +"568";"MILITARY INTELLIGENCE AERIAL EXPLOITATION";"272";"AERIALEXPLOITATION";"S*G*UUMA--*****";"(null)";"(null)";"(null)" +"577";"MILITARY INTELLIGENCE COUNTER INTELLIGENCE";"281";"COUNTERINTELLIGENCE";"S*G*UUMC--*****";"(null)";"(null)";"(null)" +"573";"MILITARY INTELLIGENCE ELECTRON";"277";"INTERCEPT";"S*G*UUMSEI*****";"(null)";"(null)";"(null)" +"572";"MILITARY INTELLIGENCE ELECTRON";"276";"DIRECTIONFINDING";"S*G*UUMSED*****";"(null)";"(null)";"(null)" +"571";"MILITARY INTELLIGENCE ELECTRON";"275";"ARMOUREDWHEELEDVEHICLE";"S*G*UUMSEA*****";"(null)";"(null)";"(null)" +"570";"MILITARY INTELLIGENCE ELECTRONIC WARFARE";"274";"ELECTRONICWARFARE";"S*G*UUMSE-*****";"(null)";"(null)";"(null)" +"574";"MILITARY INTELLIGENCE ELECTRONIC WARFARE JAMMING";"278";"JAMMING";"S*G*UUMSEJ*****";"(null)";"(null)";"(null)" +"582";"MILITARY INTELLIGENCE INTERROGATION";"290";"INTERROGATION";"S*G*UUMQ--*****";"(null)";"(null)";"(null)" +"583";"MILITARY INTELLIGENCE JOINT INTELLIGENCE CENTRE";"291";"JOINTINTELLIGENCECENTRE";"S*G*UUMJ--*****";"(null)";"(null)";"(null)" +"581";"MILITARY INTELLIGENCE OPERATIONS";"288";"OPERATIONS";"S*G*UUMO--*****";"(null)";"(null)";"(null)" +"569";"MILITARY INTELLIGENCE SIGNALS";"273";"SIGNALINTELLIGENCE(SIGINT)";"S*G*UUMS--*****";"(null)";"(null)";"(null)" +"579";"MILITARY INTELLIGENCE SURVEILL";"283";"GROUNDSURVEILLANCERADAR";"S*G*UUMRG-*****";"(null)";"(null)";"(null)" +"578";"MILITARY INTELLIGENCE SURVEILLANCE";"282";"SURVEILLANCE";"S*G*UUMR--*****";"(null)";"(null)";"(null)" +"580";"MILITARY INTELLIGENCE SURVEILLANCE SENSOR";"284";"SENSOR";"S*G*UUMRS-*****";"(null)";"(null)";"(null)" +"346";"MILITARY ROTARY WING";"39";"ROTARYWING";"S*A*MH----*****";"195";"Apache";"346" +"766";"MINE";"578";"MINE";"S*G*IRM---H****";"(null)";"(null)";"(null)" +"749";"MINE LAYING VEHICLE";"557";"MINELAYINGVEHICLE";"S*G*EVEM--*****";"4";"Trailer, Minelaying";"749" +"675";"MISSILE LAUNCHER AT HEAVY";"483";"MISSILELAUNCHERATHEAVY";"S*G*EWMTH-*****";"(null)";"(null)";"(null)" +"674";"MISSILE LAUNCHER AT MEDIUM";"482";"MISSILELAUNCHERATMEDIUM";"S*G*EWMTM-*****";"(null)";"(null)";"(null)" +"663";"MISSILE LAUNCHERS";"470";"MISSILELAUNCHERS";"S*G*EWM---*****";"(null)";"(null)";"(null)" +"672";"MISSILE LAUNCHERS ANTI TANK (A";"480";"MISSILELAUNCHERSANTITANK(AT)";"S*G*EWMT--*****";"(null)";"(null)";"(null)" +"673";"MISSILE LAUNCHERS AT LIGHT";"481";"MISSILELAUNCHERSATLIGHT";"S*G*EWMTL-*****";"(null)";"(null)";"(null)" +"544";"MISSILE (SURF-SURF)";"243";"MISSILE(SURFACE-SURFACE)";"S*G*UCM---*****";"(null)";"(null)";"(null)" +"546";"MISSILE (SURF-SURF) STRATEGIC";"245";"MISSILE(SURFACE-SURFACE)STRATEGIC";"S*G*UCMS--*****";"(null)";"(null)";"(null)" +"545";"MISSILE (SURF-SURF) TACTICAL";"244";"MISSILE(SURFACE-SURFACE)TACTICAL";"S*G*UCMT--*****";"(null)";"(null)";"(null)" +"707";"MORTAR";"504";"MORTAR";"S*G*EWO---*****";"209";"Soldier Mortar";"707" +"710";"MORTAR HEAVY";"507";"MORTARHEAVY";"S*G*EWOH--*****";"(null)";"(null)";"(null)" +"708";"MORTAR LIGHT";"505";"MORTARLIGHT";"S*G*EWOL--*****";"(null)";"(null)";"(null)" +"709";"MORTAR MEDIUM";"506";"MORTARMEDIUM";"S*G*EWOM--*****";"(null)";"(null)";"(null)" +"747";"MULTIFUNCTIONAL EARTHMOVER/DIG";"555";"MULTIFUNCTIONALEARTHMOVER/DIGGER";"S*G*EVEE--*****";"(null)";"(null)";"(null)" +"691";"MULTIPLE ROCKET LAUNCHER";"488";"MULTIPLEROCKETLAUNCHER";"S*G*EWX---*****";"(null)";"(null)";"(null)" +"694";"MULTIPLE ROCKET LAUNCHER HEAVY";"491";"MULTIPLEROCKETLAUNCHERHEAVY";"S*G*EWXH--*****";"(null)";"(null)";"(null)" +"692";"MULTIPLE ROCKET LAUNCHER LIGHT";"489";"MULTIPLEROCKETLAUNCHERLIGHT";"S*G*EWXL--*****";"(null)";"(null)";"(null)" +"693";"MULTIPLE ROCKET LAUNCHER MEDIU";"490";"MULTIPLEROCKETLAUNCHERMEDIUM";"S*G*EWXM--*****";"(null)";"(null)";"(null)" +"771";"NUCLEAR";"583";"NUCLEAR";"S*G*IRNN--H****";"(null)";"(null)";"(null)" +"188";"O/C Force";"5";"SPACESTATION";"S*P*T-----*****";"113";"O/C Staff";"188" +"188";"O/C Force";"5";"SPACESTATION";"S*P*T-----*****";"180";"O/C TM";"188" +"188";"O/C Force";"5";"SPACESTATION";"S*P*T-----*****";"114";"O/C Force";"188" +"807";"O/C Force CE";"5";"SPACESTATION";"S*P*T-----*****";"(null)";"(null)";"(null)" +"793";"OILER/TANKER";"655";"OILER/TANKER";"S*S*XMO---*****";"(null)";"(null)";"(null)" +"792";"PATROL";"630";"PATROL";"S*S*CP----*****";"(null)";"(null)";"(null)" +"767";"PETROLEUM/GAS/OIL";"579";"PETROLEUM/GAS/OIL";"S*G*IRP---H****";"(null)";"(null)";"(null)" +"656";"PIPELINE";"462";"PIPELINE";"S*G*USXP--*****";"(null)";"(null)";"(null)" +"775";"PUBLIC WATER SERVICES";"591";"PUBLICWATERSERVICES";"S*G*IUP---H****";"(null)";"(null)";"(null)" +"757";"RADAR";"567";"RADAR";"S*G*ESR---*****";"(null)";"(null)";"(null)" +"525";"RECONNAISSANCE";"226";"RECONNAISSANCE";"S*G*UCR---*****";"(null)";"(null)";"(null)" +"535";"RECONNAISSANCE AIR ASSAULT";"234";"RECONNAISSANCEAIRASSAULT";"S*G*UCRS--*****";"(null)";"(null)";"(null)" +"536";"RECONNAISSANCE AIRBORNE";"235";"RECONNAISSANCEAIRBORNE";"S*G*UCRA--*****";"(null)";"(null)";"(null)" +"539";"RECONNAISSANCE AMPHIBIOUS";"238";"RECONNAISSANCEMARINE";"S*G*UCRR--*****";"(null)";"(null)";"(null)" +"540";"RECONNAISSANCE AMPHIBIOUS DIVI";"239";"RECONNAISSANCEMARINEDIVISION";"S*G*UCRRD-*****";"(null)";"(null)";"(null)" +"541";"RECONNAISSANCE AMPHIBIOUS FORC";"240";"RECONNAISSANCEMARINEFORCE";"S*G*UCRRF-*****";"(null)";"(null)";"(null)" +"530";"RECONNAISSANCE CAVALRY";"228";"RECONNAISSANCECAVALRY";"S*G*UCRV--*****";"(null)";"(null)";"(null)" +"534";"RECONNAISSANCE CAVALRY AIR";"232";"RECONNAISSANCECAVALRYAIR";"S*G*UCRVO-*****";"(null)";"(null)";"(null)" +"531";"RECONNAISSANCE CAVALRY ARMOURE";"229";"RECONNAISSANCECAVALRYARMOURED";"S*G*UCRVA-*****";"(null)";"(null)";"(null)" +"533";"RECONNAISSANCE CAVALRY GROUND";"231";"RECONNAISSANCECAVALRYGROUND";"S*G*UCRVG-*****";"(null)";"(null)";"(null)" +"532";"RECONNAISSANCE CAVALRY MOTORIZ";"230";"RECONNAISSANCECAVALRYMOTORIZED";"S*G*UCRVM-*****";"(null)";"(null)";"(null)" +"538";"RECONNAISSANCE LIGHT";"237";"RECONNAISSANCELIGHT";"S*G*UCRLL--*****";"(null)";"(null)";"(null)" +"542";"RECONNAISSANCE MARINE LIGHT AR";"241";"RECONNAISSANCEMARINELIGHTARMOUREDRECONNAISSANCE(LA";"S*G*UCRRL-*****";"(null)";"(null)";"(null)" +"537";"RECONNAISSANCE MOUNTAIN";"236";"RECONNAISSANCEMOUNTAIN";"S*G*UCRO--*****";"(null)";"(null)";"(null)" +"543";"RECONNAISSANCE SURVEILLANCE LO";"242";"RECONNAISSANCELONGRANGESURVEILLANCE(LRS)";"S*G*UCRX--*****";"(null)";"(null)";"(null)" +"700";"RIFLE";"497";"RIFLE";"S*G*EWRR--*****";"210";"Soldier Assault Rifle";"700" +"699";"RIFLE/AUTOMATIC WEAPON";"496";"RIFLE/AUTOMATICWEAPON";"S*G*EWR---*****";"205";"Soldier Tgt";"699" +"699";"RIFLE/AUTOMATIC WEAPON";"496";"RIFLE/AUTOMATICWEAPON";"S*G*EWR---*****";"216";"Soldier M203";"699" +"699";"RIFLE/AUTOMATIC WEAPON";"496";"RIFLE/AUTOMATICWEAPON";"S*G*EWR---*****";"153";"Soldier MP";"699" +"438";"ROTARY WING ATTACK";"131";"ATTACKROTARYWING";"S*G*UCVRA-*****";"(null)";"(null)";"(null)" +"347";"ROTARY WING ATTACK";"40";"ATTACK";"S*A*MHA---*****";"(null)";"(null)";"(null)" +"352";"ROTARY WING DRONE (RPV/UAV)";"49";"DRONE(RPV/UAV)";"S*A*MHQ---*****";"(null)";"(null)";"(null)" +"348";"ROTARY WING MEDEVAC";"55";"MEDEVAC";"S*A*MHO---*****";"(null)";"(null)";"(null)" +"351";"ROTARY WING RECCE";"48";"RECONNAISSANCE";"S*A*MHR---*****";"(null)";"(null)";"(null)" +"349";"ROTARY WING SOF";"56";"SPECIALOPERATIONSFORCE(SOF)";"S*A*MHM---*****";"(null)";"(null)";"(null)" +"350";"ROTARY WING UTILITY";"42";"UTILITY";"S*A*MHU---*****";"(null)";"(null)";"(null)" +"782";"SEAPORT/NAVAL BASE";"606";"SEAPORT/NAVALBASE";"S*G*IBN---H****";"(null)";"(null)";"(null)" +"786";"SEA SURFACE INSTALLATION, OIL RIG / PLATFORM";"611";"SEASURFACEINSTALLATION,OILRIG/PLATFORM";"S*G*IRR---H****";"(null)";"(null)";"(null)" +"787";"SEA SURFACE TRACK";"612";"SEASURFACETRACK";"S*S*------*****";"(null)";"(null)";"(null)" +"740";"SEMI";"548";"SEMI";"S*G*EVUS--*****";"(null)";"(null)";"(null)" +"756";"SENSOR";"566";"SENSOR";"S*G*ES----*****";"(null)";"(null)";"(null)" +"616";"SERVICE SUPPORT MEDICAL";"364";"MEDICAL";"S*G*USM---*****";"(null)";"(null)";"(null)" +"641";"SERVICE SUPPORT TRANSPORTATION";"424";"TRANSPORTATION";"S*G*UST---*****";"53";"TATRA Logistics Carrier";"641" +"665";"SHORT RANGE AD MISSILE LAUNCHE";"472";"SHORTRANGEADMISSILELAUNCHERS";"S*G*EWMAS-*****";"(null)";"(null)";"(null)" +"669";"SHORT RANGE SS MISSILE LAUNCH";"477";"SHORTRANGESSMISSILELAUNCH";"S*G*EWMSS-*****";"(null)";"(null)";"(null)" +"593";"SIGNALS UNIT FORWARD COMMUNICATIONS";"303";"FORWARDCOMMUNICATIONS";"S*G*UUSF--*****";"(null)";"(null)";"(null)" +"597";"SIGNALS UNIT SIGNAL SUPPORT";"312";"SIGNALSUPPORT";"S*G*UUSS--*****";"(null)";"(null)";"(null)" +"589";"SIGNAL UNIT";"298";"SIGNALUNIT";"S*G*UUS---*****";"(null)";"(null)";"(null)" +"590";"SIGNAL UNIT AREA";"299";"AREA";"S*G*UUSA--*****";"(null)";"(null)";"(null)" +"592";"SIGNAL UNIT COMMAND OPERATIONS";"302";"COMMANDOPERATIONS";"S*G*UUSO--*****";"(null)";"(null)";"(null)" +"591";"SIGNAL UNIT COMMUNICATION CONF";"300";"COMMUNICATIONCONFIGUREDPACKAGE";"S*G*UUSC--*****";"(null)";"(null)";"(null)" +"599";"SIGNAL UNIT ELECTRONIC RANGING";"314";"ELECTRONICRANGING";"S*G*UUSX--*****";"(null)";"(null)";"(null)" +"594";"SIGNAL UNIT RADIO UNIT";"308";"RADIOUNIT";"S*G*UUSR--*****";"(null)";"(null)";"(null)" +"596";"SIGNAL UNIT RADIO UNIT RELAY";"311";"RELAY";"S*G*UUSRW-*****";"(null)";"(null)";"(null)" +"595";"SIGNAL UNIT RADIO UNIT TACTICA";"309";"TACTICALSATELLITE";"S*G*UUSRS-*****";"(null)";"(null)";"(null)" +"598";"SIGNAL UNIT TELEPHONE SWITCH";"313";"TELEPHONESWITCH";"S*G*UUSW--*****";"(null)";"(null)";"(null)" +"687";"SINGLE ROCKET LAUNCHER";"484";"SINGLEROCKETLAUNCHER";"S*G*EWS---*****";"(null)";"(null)";"(null)" +"690";"SINGLE ROCKET LAUNCHER HEAVY";"487";"SINGLEROCKETLAUNCHERHEAVY";"S*G*EWSH--*****";"(null)";"(null)";"(null)" +"688";"SINGLE ROCKET LAUNCHER LIGHT";"485";"SINGLEROCKETLAUNCHERLIGHT";"S*G*EWSL--*****";"(null)";"(null)";"(null)" +"689";"SINGLE ROCKET LAUNCHER MEDIUM";"486";"SINGLEROCKETLAUNCHERMEDIUM";"S*G*EWSM--*****";"(null)";"(null)";"(null)" +"796";"SOF AVIATION FIXED WING";"707";"SOFUNITFIXEDWING";"S*F*AF----*****";"(null)";"(null)";"(null)" +"797";"SOF AVIATION FIXED WING ATTACK";"708";"SOFUNITATTACK";"S*F*AFA---*****";"(null)";"(null)";"(null)" +"798";"SOF AVIATION FIXED WING UTILIT";"710";"SOFUNITUTILITY";"S*F*AFU---*****";"(null)";"(null)";"(null)" +"799";"SOF AVIATION FIXED WING UTILIT";"711";"SOFUNITUTILITY(LIGHT)";"S*F*AFUL--*****";"(null)";"(null)";"(null)" +"800";"SOF AVIATION ROTARY WING";"715";"SOFUNITROTARYWING";"S*F*AH----*****";"(null)";"(null)";"(null)" +"803";"SOF UNIT GROUND";"727";"SOFUNITGROUND";"S*F*G-----*****";"(null)";"(null)";"(null)" +"805";"SOF UNIT GROUND RANGER";"729";"SOFUNITRANGER";"S*F*GSR---*****";"(null)";"(null)";"(null)" +"804";"SOF UNIT GROUND SPECIAL FORCES";"728";"SOFUNITSPECIALFORCES";"S*F*GS----*****";"(null)";"(null)";"(null)" +"801";"SOF UNIT NAVAL";"722";"SOFUNITSOFUNITNAVAL";"S*F*SN----*****";"(null)";"(null)";"(null)" +"802";"SOF UNIT NAVAL SPECIAL BOAT";"725";"SOFUNITSPECIALBOAT";"S*F*SNB---*****";"(null)";"(null)";"(null)" +"806";"SOF UNIT PSYCHOLOGICAL OPERATI";"730";"SOFUNITPSYCHOLOGICALOPERATIONS(PSYOPS)";"S*F*GSP---*****";"(null)";"(null)";"(null)" +"658";"SPECIAL C2 HEADQUATERS COMPONE";"464";"SPECIALC2HEADQUARTERSCOMPONENT";"S*G*UH----*****";"(null)";"(null)";"(null)" +"795";"SPECIAL OPERATIONS FORCE (SOF)";"705";"SPECIALOPERATIONSFORCE(SOF)UNIT";"S*F*------*****";"(null)";"(null)";"(null)" +"625";"SUPPLY";"379";"SUPPLY";"S*G*USS---*****";"(null)";"(null)";"(null)" +"633";"SUPPLY CLASS I";"382";"SUPPLYCLASSI";"S*G*USS1--*****";"(null)";"(null)";"(null)" +"634";"SUPPLY CLASS II";"385";"SUPPLYCLASSII";"S*G*USS2--*****";"(null)";"(null)";"(null)" +"635";"SUPPLY CLASS III";"388";"SUPPLYCLASSIII";"S*G*USS3--*****";"(null)";"(null)";"(null)" +"636";"SUPPLY CLASS III AVIATION";"391";"SUPPLYCLASSIIIAVIATION";"S*G*USS3A-*****";"(null)";"(null)";"(null)" +"637";"SUPPLY CLASS IV";"394";"SUPPLYCLASSIV";"S*G*USS4--*****";"(null)";"(null)";"(null)" +"627";"SUPPLY CLASS IX";"409";"SUPPLYCLASSIX";"S*G*USS9--*****";"(null)";"(null)";"(null)" +"638";"SUPPLY CLASS V";"397";"SUPPLYCLASSV";"S*G*USS5--*****";"(null)";"(null)";"(null)" +"639";"SUPPLY CLASS VI";"400";"SUPPLYCLASSVI";"S*G*USS6--*****";"(null)";"(null)";"(null)" +"640";"SUPPLY CLASS VII";"403";"SUPPLYCLASSVII";"S*G*USS7--*****";"(null)";"(null)";"(null)" +"626";"SUPPLY CLASS VIII";"406";"SUPPLYCLASSVIII";"S*G*USS8--*****";"(null)";"(null)";"(null)" +"628";"SUPPLY CLASS X";"412";"SUPPLYCLASSX";"S*G*USSX--*****";"(null)";"(null)";"(null)" +"629";"SUPPLY LAUNDRY/BATH";"415";"SUPPLYLAUNDRY/BATH";"S*G*USSL--*****";"(null)";"(null)";"(null)" +"632";"SUPPLY MATERIEL (STOCK)";"1335";"SUPPLYMATERIEL(STOCK)";"S*G*US**--*****";"(null)";"(null)";"(null)" +"630";"SUPPLY WATER";"418";"SUPPLYWATER";"S*G*USSW--*****";"(null)";"(null)";"(null)" +"631";"SUPPLY WATER PURIFICATION";"421";"SUPPLYWATERPURIFICATION";"S*G*USSWP-*****";"(null)";"(null)";"(null)" +"668";"SURFACE- SURFACE (SS) MISSILE";"476";"SURFACE-SURFACE(SS)MISSILELAUNCHER";"S*G*EWMS--*****";"(null)";"(null)";"(null)" +"355";"SURFACE TO AIR MISSILE";"65";"SURFACETOAIRMISSILE(SAM)";"S*A*WMSA--*****";"(null)";"(null)";"(null)" +"354";"SURFACE TO SURFACE MISSILE";"64";"SURFACETOSURFACEMISSILE(SSM)";"S*A*WMSS--*****";"(null)";"(null)";"(null)" +"391";"SURVEILLANCE";"1334";"SURVEILLANCE";"S*G*F-S---*****";"(null)";"(null)";"(null)" +"725";"TANK";"533";"TANK";"S*G*EVAT--*****";"233";"0Vehicle";"725" +"725";"TANK";"533";"TANK";"S*G*EVAT--*****";"43";"MBT";"725" +"725";"TANK";"533";"TANK";"S*G*EVAT--*****";"230";"LEOPARD 2A5";"725" +"730";"TANK HEAVY";"538";"TANKHEAVY";"S*G*EVATH-*****";"(null)";"(null)";"(null)" +"731";"TANK HEAVY RECOVERY";"539";"TANKHEAVYRECOVERY";"S*G*EVATY-*****";"(null)";"(null)";"(null)" +"726";"TANK LIGHT";"534";"TANKLIGHT";"S*G*EVATL-*****";"(null)";"(null)";"(null)" +"727";"TANK LIGHT RECOVERY";"535";"TANKLIGHTRECOVERY";"S*G*EVATW-*****";"110";"BMP3 Recovery";"727" +"728";"TANK MEDIUM";"536";"TANKMEDIUM";"S*G*EVATM-*****";"(null)";"(null)";"(null)" +"729";"TANK MEDIUM RECOVERY";"537";"TANKMEDIUMRECOVERY";"S*G*EVATX-*****";"(null)";"(null)";"(null)" +"773";"TELECOMMUNICATIONS FACILITY";"589";"TELECOMMUNICATIONSFACILITY";"S*G*IUT---H****";"(null)";"(null)";"(null)" +"603";"TOPOGRAPHIC";"318";"TOPOGRAPHIC";"S*G*UUT---*****";"(null)";"(null)";"(null)" +"751";"TRAILER MOUNTED";"559";"TRAILERMOUNTED";"S*G*EVEMT-*****";"(null)";"(null)";"(null)" +"754";"TRAIN LOCOMOTIVE";"564";"TRAINLOCOMOTIVE";"S*G*EVST--*****";"(null)";"(null)";"(null)" +"645";"TRANSPORTATION APOD/APOE";"436";"APOD/APOE";"S*G*USTA--*****";"(null)";"(null)";"(null)" +"647";"TRANSPORTATION INTERMODAL";"1328";"TRANSPORTATIONINTERMODAL";"S*G*UST---*****";"(null)";"(null)";"(null)" +"646";"TRANSPORTATION MISSILE";"439";"MISSILE";"S*G*USTI--*****";"(null)";"(null)";"(null)" +"642";"TRANSPORTATION MOVEMENT CONTRO";"427";"MOVEMENTCONTROLCENTRE(MCC)";"S*G*USTM--*****";"(null)";"(null)";"(null)" +"643";"TRANSPORTATION RAILHEAD";"430";"RAILHEAD";"S*G*USTR--*****";"(null)";"(null)";"(null)" +"644";"TRANSPORTATION SPOD/SPOE";"433";"SPOD/SPOE";"S*G*USTS--*****";"(null)";"(null)";"(null)" +"783";"TRANSPORT FACILITY";"607";"TRANSPORTFACILITY";"S*G*IT----H****";"(null)";"(null)";"(null)" +"661";"UNITS GENERAL LIAISON";"467";"UNITGENERALLIAISON";"S*G*UHGL--*****";"(null)";"(null)";"(null)" +"738";"UTILITY VEHICLE";"546";"UTILITYVEHICLE";"S*G*EVU---*****";"54";"HMMWV Utility";"738" +"738";"UTILITY VEHICLE";"546";"UTILITYVEHICLE";"S*G*EVU---*****";"221";"Vehicle Tgt";"738" +"743";"WATER CRAFT";"551";"WATERCRAFT";"S*G*EVUR--*****";"213";"TATRA Tanker Water";"743" +"353";"WEAPON";"61";"WEAPON";"S*A*W-----*****";"(null)";"(null)";"(null)" diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/CMakeLists.txt new file mode 100755 index 000000000..0113ced47 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/CMakeLists.txt @@ -0,0 +1,42 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +# PlanModel +SET(GeometryModel_SRCS + geometry_model.dtd + geometry_model.cpp + geometry_model.h + geometry_model_export.h + geometry_model_factory.cpp + geometry_model_factory.h + geometry_model_q_dom.cpp + geometry_model_q_dom.h + geometry_node_visitor.cpp + geometry_node_visitor.h + geometry_pixelTextBox.cpp +) + +SOURCE_GROUP(GeometryModel FILES ${GeometryModel_SRCS}) + +NGL_ADD_LIBRARY(WS situationMapGeometryModel SHARED ${GeometryModel_SRCS}) + +NGL_TARGET_INCLUDE_DIRECTORIES( situationMapGeometryModel + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +NGL_TARGET_LINK_LIBRARIES(situationMapGeometryModel + PRIVATE + NGLLogger + PUBLIC + situationMapCommon + Qt6::Core + Qt6::Gui + Qt6::Xml +) + +NGL_INSTALL_TARGET(situationMapGeometryModel) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model.cpp new file mode 100755 index 000000000..eb4c80970 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model.cpp @@ -0,0 +1,38 @@ +/** +** +** @RDE_HEADER@ +** GeometryModel to write and read PlanEditor plans. +** $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 .........: +** +** 11.11.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "geometry_model.h" + +namespace SituationMap +{ + + GeometryModel::GeometryModel(QObject *parent) : QObject(parent) {;} + + GeometryModel::GeometryModel(const GeometryModel &) : QObject() {;} + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model.dtd b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model.dtd new file mode 100755 index 000000000..6ae4ce58b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model.dtd @@ -0,0 +1,400 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model.h new file mode 100755 index 000000000..250a8db31 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model.h @@ -0,0 +1,1028 @@ +/** +** +** @RDE_HEADER@ +** GeometryModel to write and read PlanEditor plans. +** $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 .........: +** +** 11.11.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +// clazy:excludeall=const-signal-or-slot + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "geometry_model_export.h" + + +class QIODevice; +class QTransform; + +namespace SituationMap +{ + +class Referenced; + +/** + Interface to Read/Write plan(s) +*/ +class GEOMETRYMODEL_EXPORT GeometryModel : public QObject +{ + Q_OBJECT + +public: + + class GEOMETRYMODEL_EXPORT Node + { + public: + + class Implementation; + + Node(); + Node(const Implementation &other); // do not use explicit here to avoid a lot of writing stuff to explicit casts + + Node(const Node &other); + + virtual ~Node(); + + Node &operator = (const Node &other); + + Node &operator = (Node &&other); + + const Implementation &get() const; + Implementation &get(); + + bool isValid() const; + bool isRootNode() const; + + Node parentNode() const; + + bool hasChildNodes() const; + + Node next() const; + Node firstChildNode() const; + Node firstChildNode(const QString &nodeName) const; + + bool operator ==(const Node &) const; + bool operator !=(const Node &) const; + + + uint makeConnection(Referenced *) const; + bool unlinkConnection() const; + uint connectedId() const; + + Referenced *connected() const; + + private: + + Implementation *_d; + + }; // Node + + + explicit GeometryModel(QObject *parent = nullptr); + + GeometryModel(const GeometryModel &); //!< Copy-Constructor + GeometryModel& operator = (const GeometryModel &) = delete; // copy allowed but no assigment? + + /** + Converts the geometry model to its textual representation. + @{ + */ + virtual QByteArray toByteArray() const = 0; + + /** + Converts the geometry model to QString by retaining userData. + */ + virtual QByteArray toByteArrayDebug() const = 0; + /** @} */ + + /** + Converts textual representation of geometry model to binary representation. + @{ + */ + virtual bool fromByteArray(const QByteArray &content) = 0; + /** @} */ + + /** + Reads textual representation of geometry model from device and converts to binary representation. + */ + virtual bool fromDevice(QIODevice *device) = 0; + + /** + Converts textual representation of geometry model to binary representation by retaining userData. + */ + virtual bool fromByteArrayDebug(const QByteArray &content) = 0; + + /** + Check for existens of primitives. + */ + virtual bool isEmpty() const = 0; + + /** + Retrieve the root node to traverse the model. + */ + virtual Node getRootNode() const = 0; + + /** + Retrieve the node to store default map settings. + */ + virtual Node getMapDefaultSettingsNode() const = 0; + + + using NodeList = QList; + + + //! Types of Primitives for \sa { addPrimitive(), getType() } + enum class PrimitiveType + { + DictionarySymbol + , ParticipantSymbol + , Ellipse + , Line + , Polyline + , Rectangle + , Spline + , Symbol + , Text + , Node + , RootNode + , MapDefaults + , Unknown + }; // PrimitiveType + + //! Horizontal Alignment-Enum for Text + enum class HorizontalAlign + { + Left = 0, + HCenter = 1, + Right = 2 + }; + + //! Vertical Alignment-Enum for Text + enum class VerticalAlign + { + Bottom = 0, + VCenter = 1, + Top = 2 + }; + + //! Enum for positioning Arrows at (Poly)Lines + enum class Arrows {NoArrows, Start, End, Both}; + + //! Enum for Fill-Patterns + enum class FillPattern {None, Dots, Hor1, Hor2, Ver1, Ver2, Diag1, Diag2, Solid}; + + //! Enum for Fill-Style of Ellipsis + enum class FillStyle {Chord, Pieslice, Open}; + + //! Enum for Line-Styles + enum class LineStyle {LineSolid, Dashed, Dotted, DashedDotted}; + + //! Enum for Text-Fonts + enum class Font {SansSerif, Serif, Fixed, Decorative}; + + //! Enum for different types of maps - extends the types from GDW::MapType in + //! GDW/src/GDWUtils/LoadMapControl.h + enum class MapType {Raster, Vector, Contour, Elevation, RasterElevation, MapUnknown}; + + //! Enum for tactical state of a symbol: ts_* = tactical state... + + enum class vbg_et + { + EINSATZBEREIT = 0, + LEICHTER_TREFFER = 1, + MITTLERER_TREFFER = 2, + SCHWERER_TREFFER = 3, + BEWEGUNGSUNFAEHIG = 4, + BEWEGUNGSUNFAEHIG_SICHTB = 5, + AUSFALL_WAFFENANLAGE = 6, + AUSFALL_WAFFENANLAGE_SICHTB = 7, + TOTALAUSFALL = 8, + ZEITLICHER_AUSFALL = 9, + ANNAEHENDER_TREFFER = 10, + TREFFER_OHNE_WIRKUNG = 11, + }; + + using t_vbg_et = vbg_et; + /* + enum t_w_vbg_et { + ts_einsatzbereit, ts_treffer_ohne_wirkung, ts_annaehender_treffer, + ts_zeitlicher_ausfall, ts_leichter_treffer, ts_mittlerer_treffer, + ts_schwerer_treffer, ts_bewegungsunfaehig, ts_bewegungsunfaehig_sichtb, + ts_ausfall_waffenanlage, ts_ausfall_waffenanlage_sichtb, ts_totalausfall + }; + */ + + //! Re-order Primitives + enum class PrimitivOrder { Lower, Raise }; + + //! Enum for all known attributes + enum class Attribute + { + None, // Only for changed signal, can not be set + AbcProtection, // Available for: symbol - Type: bool + Affiliation, // Available for: symbol - Type: Symbol::t_affil + Alignment, // Available for: text_data - Type: HorizontalAlign + AnchorHorizontal, // Available for: text - Type: HorizontalAlign + AnchorVertical, // Available for: text - Type: t_valign + ArrowClampHeight, // Available for: line polyline spline - Type: double + ArrowHeight, // Available for: line polyline spline - Type: double + ArrowLineWidth, // Available for: line polyline spline - Type: double + Arrows, // Available for: line polyline spline - Type: t_arrows + ArrowWidth, // Available for: line polyline spline - Type: double + Box, // Available for: text - Type: bool + Center, // Available for: ellpise rectangle - Type QPointF + Close, // Available for: polyline spline - Type: bool + Color, // Available for: ellipse line polyline rectangle spline text - Type: QColor + Extent, // Available for: ellipse - Type: double + Fill, // Available for: ellipse polyline rectangle spline text - Type: bool + FillColor, // Available for: ellipse polyline rectangle spline text - Type: QColor + FillPattern, // Available for: ellipse polyline rectangle spline text - Type: t_fillPattern + FillStyle, // Available for: ellipse - Type: t_fillStyle + Font, // Available for: text - Type: t_font + FontBold, // Available for: text - Type: bool + FontItalic, // Available for: text - Type: bool + FontSize, // Available for: text - Type: int + Height, // Available for: ellipse rectangle - Type: double + Injury, // Available for: ParticipantSymbol - Type int + LineColor, // Available for: text - Type: QColor + LineStyle, // Available for: ellipse line polyline rectangle spline text - Type: t_lineStyle + LineWidth, // Available for: ellipse line polyline rectangle spline text - Type: double + MarginHorizontal, // Available for: text - Type: int + MarginVertical, // Available for: text - Type: int + Mobility, // Available for: symbol - Type: Symbol::t_mob + SIDC, // Available for: dictionary symbol - Type: QString + SIDC_Color, // Available for: dictionary symbol - Type: QColor + SIDC_Attributes, // Available for: dictionary symbol - Type: QString AttributeName=AttributeValue;AttributeName.... + SymbolId, // Available for: symbol - Type: int + SymbolOffsetX, // Available for: symbol - Type: int + SymbolOffsetY, // Available for: symbol - Type: int + SymbolDirection, // Available for: symbol - Type: double + ForceId, // Available for: symbol - Type: int + SymbolState, // Available for: - Type: enum t_state (aus symbol_types.h) + SymbolUncertainty, // Available for: - Type: enum t_uncertainty (aus symbol_types.h) + SymbolExplicitScale, // Available for: symbol + DamageForceId, // Available for: symbol - Type: int + PeMapBrightness, // Available for: plan - Type: double + PeMapCenterX, // Available for: plan - Type: int + PeMapCenterY, // Available for: plan - Type: int + PeMapSaturation, // Available for: plan - Type: double + PeMapRotation, // Available for: plan - Type: double + PeMapType, // Available for: plan - Type: t_mapType + PeMapZoom, // Available for: plan - Type: double + PeShowUtm, // Available for: plan - Type: bool + + PeSymbolOpacity, // Available for: plan - Type: double 0..1.0 (0=keine Deckkraft, 1.0 deckend) + PeSymbolFillType, // Available for: plan - Type: Type: enum t_fillType (aus symbol_types.h) + PeSymbolScale, // Available for: plan - Type: double 0..2.0 + + MetaDataTitle, + MetaDataAuthor, + MetaDataDate, + MetaDataDesciption, + MetaDataMinScale, + MetaDataMaxScale, + + MetaPixelBasedTextBoxes, + + Rotate, // Available for: ellipse rectangle text - Type: double + Rounding, // Available for: rectangle - Type: double + Start, // Available for: ellipse - Type: double + Text_EF, // Available for: symbol - Type: QString + Text_G, // Available for: symbol - Type: QString + Text_H, // Available for: symbol - Type: QString + Text_KLNPJ, // Available for: symbol - Type: QString + Text_M, // Available for: symbol - Type: QString + Text_T, // Available for: symbol - Type: QString + Text_V, // Available for: symbol - Type: QString + Text_W, // Available for: symbol - Type: QString + Text_XY, // Available for: symbol - Type: QString + Text_Z, // Available for: symbol - Type: QString + UtmEast, // Available for: plan - Type: int + UtmGrid, // Available for: plan - Type: QString + UtmNorth, // Available for: plan - Type: int + UtmZone, // Available for: plan - Type: QString + Width, // Available for: ellipse rectangle - Type: double + att_vbg_et, // Available for: symbol - Type: t_w_vbg_et + MaxAttributes + }; // enum class Attribute + + + /** + Set a value for a global model-attribute. If the attribute does not exist in the + model or the given value is invalid, a setAttributeError()-signal is emitted. + Signal changed() ist emitted. + \sa { getModelAttribute(), setAttributeError(), hasAttribute(), attributeType() } + \param attribute: the attribute to set + \param value: the value of the attribute + */ + virtual void setModelAttribute(Attribute attribute, const QVariant &value) = 0; + + + /** + Set a value for a plan-editor-attribute. If the attribute does not exist in + the plan-editor or the given value is invalid, a setAttributeError()-signal + is emitted. + Signal planEditorChanged() ist emitted. + \sa { getAttribute(), setAttributeError(), hasAttribute(), attributeType() } + \param attribute: the attribute to set + \param value: the value of the attribute + */ + virtual void setModelEditorAttribute(Attribute attribute, const QVariant &value) = 0; + + + /** + Set a value for a primitive-attribute. If the attribute does not exist in + the primitive or the given value is invalid, a setAttributeError()-signal + is emitted. + Signal changed() ist emitted. + \sa { getAttribute(), setAttributeError(), hasAttribute(), attributeType() } + \param node: reference to the primitive-node + \param attribute: the attribute to set + \param value: the value of the attribute + */ + virtual void setPrimitiveAttribute(const Node &node, Attribute attribute, const QVariant &value) = 0; + + + /** + Move a primitive or a group-node into another group-node. If the moved + primitive is already child of a group-node, it ist moved to the group + and removed from the previous one. If the target is not a group-node, + a typeError()-signal is emitted. + Signal changed() ist emitted. + \param primitive: the primitive or group-node to move into a group + \param target: the target group-node + */ + virtual void movePrimitiveIntoGroup(const Node &primitive, const Node &target) = 0; + + + /** + Remove a group, but not its children. This means, that all children of the + given group are moved one level up the tree and then the group is deleted. + If group is not a group-node, a typeError()-signal is emitted. + Signal changed() ist emitted. + \param group: the group + */ + virtual void releaseGroup(const Node &group) = 0; + + + /** + Remove a group AND all its children. + If group is not a group-node, a typeError()-signal is emitted. + Signal changed() ist emitted. + \param group: the group + */ + virtual void deleteGroup(const Node &group) = 0; + + + /** + Remove and delete the given primitive from the plan (and possibly all of + its children). + Signal changed() ist emitted. + \param primitive: reference to the primitive-node + */ + virtual void removePrimitive(const Node &primitive) = 0; + + + /** + Remove and delete all primitives from the plan (and possibly all of + their children), that match the userData in the given list. + Signal changed() ist emitted, after all primitves have been removed. + \param primitive: reference to the primitive-node + */ + virtual void removePrimitivesByUserData(const QList &userDataList) = 0; + + + /** + Remove Text from a primitive, if any. + Signal changed() ist emitted. + + \param parent: The node where to remove the text from + */ + virtual void removeText(Node &parent) = 0; + + + /** + Set the text-data of a text-node. If the given node is not a text-node, + a typeError()-signal is emitted. + Signal changed() ist emitted. + \sa{ addText(), getText(), getTextData() } + + \param textNode: the text-node + \param data: the text-data + \param textAlign: the alignment of the text-data + */ + virtual void setTextData(const Node &textNode, const QString &data, HorizontalAlign textAlign) = 0; + + + /** + Translate a group or primitive. + Signal changed() ist emitted. + \param node: reference to the primitive-node + \param x: translate horizontally by this value + \param y: translate vertically by this value + */ + virtual void translateNode(const Node &node, double x, double y) = 0; + + + /** + Move a group or primitive to the given coordinates. + Signal changed() ist emitted. + \param node: reference to the primitive-node + \param x: move horizontally to this value + \param y: move vertically to this value + */ + virtual void moveNode(const Node &node, double x, double y) = 0; + + + /** + Scale the group or primitive. A primitive is scaled around the center of + its bounding box. If a group is scaled, all children are scaled around + the center of the groups bounding box. + Signal changed() ist emitted. + \param node: reference to the primitive-node + \param scaleX: the x-scale-factor + \param scaleY: the y-scale-factor + */ + virtual void scaleNode(const Node &node, double scaleX, double scaleY) = 0; + + + /** + Rotate a group or primitive. A primitive is rotated around the center of + its bounding box. If a group is rotated, all children are rotated around + the center of the groups bounding box. + Signal changed() ist emitted. + \param node: reference to the primitive or group + \param angle: the rotation angle (ccw, 360 degree) + */ + virtual void rotateNode(const Node &node, double angle) = 0; + + + /** + Reorder Primitives in the plan. + Signal changed() ist emitted. + \param nodes: The primitives to reorder + \param order: Move primitives up (Raise) or down (Lower) + */ + virtual void reorderPrimitives(const NodeList &nodes, PrimitivOrder order) = 0; + + + /** + Merge another Node into this one. + All Primitives and GroupNodes of other are copied and added (appended) to + this. + Signal changed() ist emitted. + + \param other: the other plan or Node + \param skipUndo: if true, the undo-stack is not updated + \param intoNewGroup: If true, all merged primitives go into a new Group, + while groups from the merged plan are removed. + */ + + virtual void merge(const Node &other, bool skipUndo = false, bool intoNewGroup = false) = 0; + + /** + Merge another plan into this one. + All Primitives and GroupNodes of other are copied and added (appended) to + this. + Signal changed() ist emitted. + + \param other: the other plan or Node + \param skipUndo: if true, the undo-stack is not updated + \param intoNewGroup: If true, all merged primitives go into a new Group, + while groups from the merged plan are removed. + */ + virtual void merge(GeometryModel *other, bool skipUndo = false, bool intoNewGroup = false) = 0; + + + /** + Undo the last change on the undo()-stack. To check, if there are + undo()-steps available, check the size of the undo-stack with undoSteps() + If inside of an undo-block, the block is closed, even if nested. + Signal changed() ist emitted. + \param steps: Go this many steps back in undo-history. If less than 1, undo + does nothing, if greater than undo-steps available, undo goes + only back that many steps. + */ + virtual void undo(unsigned int steps = 1) = 0; + + /** + Redo the last change on redo-stack. To check, if there are + redo-steps available, check the size of the redo-stack with redoSteps() + \param steps: Go this many steps back in undo-history. If less than 1, redo + does nothing, if greater than redo-steps available, redo goes + only back that many steps. + */ + virtual void redo(unsigned int steps = 1) = 0; + + + /** + Enable or disable undo(). While undo() is disabled, no new actions will be + recorded in the undo-stack. + \param enabled: Bool to enable/disable undo() + */ + virtual void enableUndo(bool enable) = 0; + + + /** + Start a block of collected undo-steps. All modifications until endUndoBlock() + are collected into one undo-Step. Nested undoBlocks are not possible. If + already inside an undo-block, this method does nothing. + undo() ends an undo-block. + */ + virtual void startUndoBlock() = 0; + + + /** + End a block of collected undo-steps. All collected modifications since the + startUndoBlock() are now pushed into one undo-Step. Nested undoBlocks are + not possible. If not inside an undo-block, this method does nothing. + undo() also ends an undo-block. + \param eventDescription: describe the last change. If == "", use last + automatic entry. + */ + virtual void endUndoBlock(const QString &eventDescription = QString()) = 0; + + /** + Abort recording a block of collected undo-steps. + */ + virtual void abortUndoBlock() = 0; + + /** + Set the maximum number of undo-Steps. Default is 100. + \param max: number of undo-Steps + */ + virtual void setMaxUndoSteps(unsigned int max) = 0; + + /** + Return maximum number of undo-steps. + */ + virtual unsigned int maxUndoSteps() const = 0; + + + /** + Counting modifications of the model. + */ + virtual unsigned int modificationCounter() const = 0; + + + /** + Reset modification counter. + */ + virtual void resetModificationCounter() = 0; + + + /** + Remove all User-Data-Values-Attributes and empty groups from the model. + ATTENTION: After this, many methods may return unexpected results. + This slot/method should only be used before the model is written to a file + or database. + For each cleaned Attribute the signal userDataCleanup() is emitted. + */ + virtual void cleanModel() = 0; + + + /** + Clear the model. This deletes all primitives and groups. + Signal changed() is emitted. + */ + virtual void clearModel() = 0; + + + /** + Add a primitive to the plan. If the given primitive-type does not exist, + a typeError()-signal is emitted. + Signal changed() ist emitted. + + \sa { typeError() } + \param type: the type of a primitive + \param points: a list of points to define the position/geometry of the primitive + \param attributes: a list of attribute/value-pairs. If the list contains + attributes, that are not supported by the primitive, or + values, that do not fit into the attributes, a + setAttributeError()-signal is emitted. + \param userData: An int for creating simIDs + \param textData: Add this text to the Primitive, if this is a text-Primitive + \param textAlign: the alignment of the text-data, only used for text-primitive + \return a reference to the new tree-element + */ + virtual Node addPrimitive(PrimitiveType type + , const QList &points + , const QList > &attributes + , int userData = SituationMap::IdHandler::BadId + , const QString &textData = QString() + , HorizontalAlign textAlign = HorizontalAlign::Left) = 0; + + + /** + Set the defining points of a primitive + If the number of points does not match the Primitives-definition, an + attributeError() is emitted. + \param node: reference to the primitive-node + \param points: a list of points to define the position/geometry of the primitive + \return true on success, false otherwise (i.e. wrong number of points) + */ + virtual bool setPrimitivePoints(const Node &node, const QList &points) = 0; + + + /** + Add a text-primitive to another primitive. A typeError()-signal is emitted, + if the given parent-node can not have text. The returned node is a text-node. + Signal changed() ist emitted. + + \sa { typeError(), addPrimitive(), setPrimitiveAttribute(), setTextData() } + + \param parent: The node where to insert text + \param textData: Initial string for the text-element. + \param textAlign: Initial aligment of the text-data. + \param attributes: a list of attribute/value-pairs. If the list contains + attributes, that are not supported by text-primitives, or + values, that do not fit into the attributes, a + setAttributeError()-signal is emitted. + \param userData: An int for creating simIDs + \return a reference to the new tree-element + */ + virtual Node addText(const Node &parent + , const QString &textData + , HorizontalAlign textAlign + , const QList > &attributes + , int userData = SituationMap::IdHandler::BadId) = 0; + + + /** + Retrieve the text-element from a primitive, if any. + \param parent: the primitive where to get the text-node from + \return the text-node, or a null-node, if parent has no text. + */ + virtual Node getText(const Node &parent) const = 0; + + + /** + Retrieve the text-data from a text-node. If the given node is not a + text-node, a typeError() is emitted and an empty string returned. + \sa{ getText() } + + \param textNode: the text-node + \return a string with the text-data + */ + virtual QString getTextData(const Node &textNode) const = 0; + + + /** + Retrieve the defining points of a primitive + If the primitive has no points, a signal planModelError() is emitted, + since the model does not contain points where the user believes it should. + + \param node: reference to the primitive-node + \return QList containing the QPointF of the primitive + */ + virtual QList getPrimitivePoints(const Node &node) const = 0; + + + /** + Return the bounding box of a primitive or group of primitives or the + whole plan. If the given node has no bounding box (i.e. not a primitive) + the resulting rect is not valid. + \sa {translateNode(), moveNode(), rotateNode(), scaleNode() QRectF::isValid()} + \param node: The primitive or group. + */ + virtual QRectF getBoundingBox(const Node &node) const = 0; + + /** + Set user defined name of node, that will be used in GUI to label nodes. + \param node The primitive or group + \param name The name string + */ + virtual void setNodeDisplayName(const Node &node, const QString &name) = 0; + + /** + Set user defined name of node, that will be used in GUI to label nodes. + \param node The primitive or group + */ + virtual QString getNodeDisplayName(const Node &node) const = 0; + + + /** + Get a value for a global model-attribute. If the attribute does not exist in the + model a setAttributeError()-signal is emitted. + \sa { setModelAttribute(), setAttributeError(), hasAttribute(), attributeType() } + \param attribute: the attribute to get + */ + virtual QVariant getModelAttribute(Attribute attribute) const = 0; + + + /** + Get the value of an attribute. If the attribute does not exist in the given + node, a setAttributeError()-signal is emitted. + \sa { setAttributeError(), hasAttribute(), attributeType() } + \param node: reference to the primitive-node + \param attribute: the attribute to get + */ + virtual QVariant getAttribute(const Node &node, Attribute attribute) const = 0; + + /** + Get the value of an attribute of class type T from geometry model of type GeometryModelType. + \see getAttribute + */ + template T getAttribute(const Node &node, Attribute attribute) const + { + const GeometryModelType *geoModelPtr = dynamic_cast(this); + if (geoModelPtr) + return geoModelPtr->template getAttribute(node, attribute); + return T(); + } // template T getAttribute( const Node & node, Attribute attribute ) const + + + /** + Check, if a given node has a specific attribute. + \sa { setPlanAttribute(), getAttribute(), setAttributeError(), attributeType() } + \param node: reference to the primitive-node + \param attribute: the attribute to check + */ + virtual bool hasAttribute(const Node &node, Attribute attribute) const = 0; + + + /** + Get the type a an attribute. The type returned is a QVariant-type. + \sa { setPlanAttribute(), getAttribute(), setAttributeError(), hasAttribute() } + \param node: reference to the primitive-node + \param attribute: the attribute to check + */ + virtual QMetaType::Type attributeType(const Attribute &attribute) const = 0; + + + /** + Get a list of supported attributes from a primitive/node-type. If the given + type does not exist, a typeError()-signal is emitted. + \sa { setPlanAttribute(), getAttribute(), setAttributeError(), hasAttribute(), attributeType() } + \param type: the type of a primitive + */ + virtual QList supportedAttributes(PrimitiveType type) const = 0; + + + /** + Create a group from a list of primitives. + Signal changed() ist emitted. + \sa{ addPrimitive() } + \param primitives: A list of Elements which build the new group. May be + empty. Primitives can be added later to the group with + movePrimitiveIntoGroup() + \return a reference to the new group + */ + virtual Node createGroup(const NodeList &primitives) = 0; + + + /** + If the given Primitive is in a group, return the Group-Node, otherwise + return a Null-Node. + \param primitive: The primitive to look up. + \return A group-node or a Null-Node + */ + virtual Node isInGroup(const Node &primitive) const = 0; + + + /** + Return all primitives inside this group. If the given Node isn't a group, a + typeError() is emitted. + \param node: A group-node + \return: List of primitives in this group, possibly empty. + */ + virtual NodeList getGroupPrimitives(const Node &node) = 0; + + + /** + Find a primitive by its corresponding userData-int. If there is no + Primitive that corresponds to the userData, a Null-Node is returned. + \sa{ QDomNode::isNull() } + \param userData: An int for creating simIDs + \return pointer to primitive-node or NULL + */ + virtual Node findPrimitive(int userData) const = 0; + + + /** + Return \c true in case of existing user data. Otherwise the return value is false. + */ + virtual bool hasUserData(const Node &node) const = 0; + + /** + Get the userData from its corresponding primitive if any. + If no userData is associated with this Primitive, 0 is + returned and a typeError()-signal is sent and the bool opinted to by + ok is set to false. Otherwise it is set to true. + \param node: the primitive-node + \return userData-int + */ + virtual int getUserData(const Node &node, bool *ok = nullptr) const = 0; + + + /** + Set the userData of a primitive. + If this primitive can not have userData, a typeError()-signal is sent. + \param node: pointer to the primitive-node + \param userData: an int + */ + virtual void setUserData(const Node &node, int userData) = 0; + + + /** + Create and return a MultiMap with all UserData as Keys and the Primitives, + that contain that data as values. Used for searching Primitives by their + UserData (for example Selection-Check in GDW) + \param node: Start search for UserData at this node. This is normally a + Null-Node to search the whole tree. + \return QMultiMap with UserData and corresponding Primitives + */ + virtual QMultiMap userDataMap(const Node &node = Node()) = 0; + + + /** + Create a copy of a group or primitive and insert it into the tree. If a + group is copied, a deep copy including all its children is created. + Signal changed() ist emitted. + \param node: the group or primitive to copy. + \param userData: An int for creating simIDs + \return a reference to the new group (or NULL) + */ + virtual Node copy(const Node &node, int userData = SituationMap::IdHandler::BadId) = 0; + + + /** + Return the type of the given primitive. + \param node: the primitive or group + \return the type + */ + virtual PrimitiveType getType(const Node &node) const = 0; + + + /** + Return the number of undo-steps available. + \return number of undo-steps on the undo-stack + */ + virtual unsigned int undoSteps() const = 0; + + + /** + Return the number of redo-steps available. + \return number of redo-steps on the redo-stack + */ + virtual unsigned int redoSteps() const = 0; + + + /** + Return the undo history containing strings that describe the available + undo-steps + \return A list of strings + */ + virtual QStringList undoHistory() const = 0; + + + /** + Return the redo history containing strings that describe the available + redo-steps + \return A list of strings + */ + virtual QStringList redoHistory() const = 0; + + /** + Clear the undo-stack + */ + virtual void clearUndoStack() = 0; + + /** + Clear the redo-stack + */ + virtual void clearRedoStack() = 0; + + /** + Return a attribute-name from the enum for signals and logging + \param attribute: enum-value of attribute + \return name of attribute as string + */ + virtual QString attributeToString(const Attribute &attribute) const = 0; + + + /** + Parsing the attribute string of a dictionary symbol. The result is written to \c attrMap + \param sidcAttributes Attribute string + \param attrMap Result of parsing process + \return true in case of none errors, else false + */ + + virtual bool parseSymbolAttributeString(const QString &sidcAttributes, QMap &attrMap) const = 0; + + /** + Build attribute string to store at dictionary symbol. + */ + virtual QString buildSymbolAttributeString(const QMap &attrMap) const = 0; + +signals: + /** + A signal, that is emitted, when the plan changed. Only real changes are + signaled to reduce unnecessary updates. + + \param node: the changed node (if this is a Null-Node, the Root-Node of + the plan has changed) + \param att: The attribute, that changed. If att==MaxAttributes, then + multiple attributes are changed + \param coordsChanged: if true, the coordinated of the primitive are changed + */ + void changed(const Node &node, GeometryModel::Attribute att, bool coordsChanged); + + + /** + A signal, that is emitted, when the settings of the plan-editor have + changed. + \param what: Short description of the change for logging + */ + void modelChanged(const QString &what); + + + /** + A signal, that is emitted, when an invalid type-parameter was given + as argument. + \param error: Short description of the error for logging + */ + void typeError(const QString &error); + + + /** + A signal, that is emitted, when an invalid attribute-parameter was given + as argument. + \param error: Short description of the error for logging + */ + void attributeError(const QString &error); + + + /** + A signal, that is emitted, when a method detects an error or inconsistency + in the model. + \param error: Short description of the error for logging + */ + void modelError(const QString &error); + + + /** + A signal, that is emitted, when a methode detects an error. + */ + void criticalError(const QString &error); + + + /** + This signal is emitted for each userData, when cleanModel() is called. + \param userData: an int + */ + void userDataCleanup(int userData); + + /** + The signal is emitted when undo has been called. + */ + void nodeCleanup(const Node &node); + + /** + This signal is emitted for each connection created with \c Node::makeConnection() function, when cleanModel() is called. + \param connectedPtr: an IdHandler::AccessableId * + */ + void connectionCleanup(Referenced *connectedPtr); + + /** + This signal is emitted when the modification counter has been changed + */ + void modificationCount(unsigned int); +}; + +} // namespace SituationMap + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model_export.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model_export.h new file mode 100755 index 000000000..2f951b621 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model_export.h @@ -0,0 +1,21 @@ +/** + * Export definitions + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include + +#ifndef GEOMETRYMODEL_EXPORT +# ifdef BUILD_STATIC_LIBRARIES +# define GEOMETRYMODEL_EXPORT +# else +# if defined(MAKE_SITUATIONMAPGEOMETRYMODEL_LIB) +# define GEOMETRYMODEL_EXPORT Q_DECL_EXPORT +# else +# define GEOMETRYMODEL_EXPORT Q_DECL_IMPORT +# endif +# endif +#endif diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model_factory.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model_factory.cpp new file mode 100755 index 000000000..18de4ac9f --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model_factory.cpp @@ -0,0 +1,58 @@ +/** +** +** @RDE_HEADER@ +** GeometryModelFactory to create geometry model +** $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 .........: +** +** 20.11.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + + +#include "geometry_model_factory.h" +#include "geometry_model_q_dom.h" + +namespace SituationMap +{ + +GeometryModelFactory *GeometryModelFactory::singleton() +{ + static GeometryModelFactory s_factory; + return &s_factory; +} /* GeometryModelFactory::singleton() */ + + +GeometryModel *GeometryModelFactory::create() +{ + return new GeometryModelQDom(); +} // GeometryModelFactory::create() + + +GeometryModel *GeometryModelFactory::clone(GeometryModel *geoModel) +{ + GeometryModelQDom * qdomModel = dynamic_cast(geoModel); + if (qdomModel) + return new GeometryModelQDom(*qdomModel); + + return nullptr; +} // GeometryModelFactory::clone() + +} /* namespace SituationMap */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model_factory.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model_factory.h new file mode 100755 index 000000000..7a66aaa4b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model_factory.h @@ -0,0 +1,59 @@ +/** +** +** @RDE_HEADER@ +** GeometryModelFactory to create geometry model +** $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 .........: +** +** 20.11.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "geometry_model_q_dom.h" + + +namespace SituationMap +{ + +/** + @brief Factory class to create GeometryModels + + @see GeometryModel GeometryModelQDom +*/ + +class GEOMETRYMODEL_EXPORT GeometryModelFactory +{ +public: + + static GeometryModelFactory *singleton(); + + GeometryModelFactory() = default; + ~GeometryModelFactory() = default; + GeometryModelFactory(const GeometryModelFactory &) = delete; + GeometryModelFactory operator =(const GeometryModelFactory &) = delete; + + GeometryModel *create(); + GeometryModel *clone( GeometryModel * ); + +}; /* GeometryModelFactory */ + +} /* namespace SituationMap */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model_q_dom.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model_q_dom.cpp new file mode 100755 index 000000000..4908635d7 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model_q_dom.cpp @@ -0,0 +1,2711 @@ +/** +** +** @RDE_HEADER@ +** GeometryModel implementation to write and read PlanEditor plans from/to XML and manipulate DOM-tree. +** $Id$ +** $HeadURL$ +** $Author$ +** +** ----------------------------------------------------------------------------------------------- +** +** Hardware-Environment: Intel-PC +** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5 +** +** Language ........: C++ 11 +** +** ----------------------------------------------------------------------------------------------- +** +** Author ...........: Stephan Kuhagen +** Jörg Turla, SPD G, Tel. 3132 +** +** ----------------------------------------------------------------------------------------------- +** +** Notes .........: +** +** ??.??.200?, SK : First edition for use with Qt4 +** 11.11.2015, JT : Splitting interface and implementation +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "geometry_model_q_dom.h" + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + + +template inline _Ty doExchange(_Ty &_Val, _Other && _New_val) +{ + // assign _New_val to _Val, return previous _Val + _Ty _Old_val = std::move(_Val); + _Val = std::forward<_Other>(_New_val); + return (_Old_val); +} + +namespace SituationMap +{ + +const int CoordinateDoublePrecision = 20; + +bool GeometryModelQDom::_staticInitDone = false; + +QMultiMap< GeometryModel::PrimitiveType , GeometryModel::Attribute > GeometryModelQDom::_primitiveSupportedAttributes; +QMap < QString , GeometryModel::PrimitiveType> GeometryModelQDom::_primitiveTagsTypes; +QMap < GeometryModel::PrimitiveType , QString > GeometryModelQDom::_primitiveTagsStrings; +QMap < QPair, QString > GeometryModelQDom::_attributeDefaults; +QMap < GeometryModel::Attribute , QMetaType::Type > GeometryModelQDom::_attributeTypes; + + +static const QLatin1String mapSettingsName("MapSettings"); +static const QLatin1String rootNodeName("GeometryModel"); +static const QLatin1String connectionIdName("connectionId"); +static const QLatin1String userDataName("userData"); + + +// convert to QString (with non locale-aware conversions) +QString GeometryModelQDom::variant2XmlString(const QVariant &v) +{ + switch (v.type()) + { + case QVariant::Int : return QString::fromLatin1(QByteArray::number(v.toInt())); break; + case QVariant::UInt : return QString::fromLatin1(QByteArray::number(v.toUInt())); break; + case QVariant::LongLong : return QString::fromLatin1(QByteArray::number(v.toLongLong())); break; + case QVariant::ULongLong : return QString::fromLatin1(QByteArray::number(v.toULongLong())); break; + case QVariant::Double : return QString::fromLatin1(QByteArray::number(v.toDouble(), 'f', -1)); break; + case QVariant::PointF : + { + const QPointF &point = v.toPointF(); + return QString::number(point.x(), 'f', CoordinateDoublePrecision) + QLatin1Char(' ') + QString::number(point.y(), 'f', CoordinateDoublePrecision); + } break; + + case QVariant::Color : + { + const QColor &color = v.value(); + + return QStringLiteral("#%1%2%3%4") + .arg(color.red() , 2, 16, QLatin1Char('0')) + .arg(color.green(), 2, 16, QLatin1Char('0')) + .arg(color.blue() , 2, 16, QLatin1Char('0')) + .arg(color.alpha(), 2, 16, QLatin1Char('0')); + } break; + + default: break; + } + + return v.toString(); +} // QString GeometryModelQDom::variant2XmlString( const QVariant & v ) + + + +QColor GeometryModelQDom::colorFromString(const QString &attColor) +{ + QColor result = Qt::black; // in case of wrong attColor the default is black. + + if (attColor.startsWith(QLatin1Char('#')) && attColor.length() == 9) + { + result = QColor::fromString(attColor.left(7)); + // Get Alpha-Channel + bool ok; + int alpha = attColor.right(2).toInt(&ok, 16); + if (ok) result.setAlpha(alpha); + } + else result = QColor::fromString(attColor); + + return result; +} // QColor GeometryModelQDom::colorFromString( const QString & attColor ) + + +void GeometryModelQDom::valueFromVariant(const QVariant &v, QPointF &result) { result = GeometryModelQDom::pointFromString(v.toString()); } +void GeometryModelQDom::valueFromVariant(const QVariant &v, QColor &result) { result = GeometryModelQDom::colorFromString(v.toString()); } + + +QPointF GeometryModelQDom::pointFromString(const QString &attPoint) +{ + QPointF result; + const auto xyStrList = attPoint.split(QLatin1Char(' ')); + + if (xyStrList.size() == 2) + { + bool okX = false; + bool okY = false; + + const double &x = xyStrList[0].toDouble(&okX); + const double &y = xyStrList[1].toDouble(&okY); + + if (okX && okY) + { + result.setX(x); + result.setY(y); + } + } + return result; +} // QPointF GeometryModelQDom::pointFromString( const QString & attPoint ) + + +#ifdef _WINDOWS +#pragma warning ( disable : 4172 ) +#endif + +class GeometryModel::Node::Implementation +{ + mutable QDomNode _domNode; + + static SituationMap::IdHandler *_idHandler; + + static SituationMap::IdHandler &idHandler() + { + if (!_idHandler) + _idHandler = new SituationMap::IdHandler(); + return *_idHandler; + } // idHandler() + + +public: + + Implementation() : _domNode() {;} + + Implementation(const QDomNode &domNode) : _domNode(domNode) + { + // The QDomNode is simply a smart pointer to internal QDom objects. For this the + // QDomNode handles the derived classes itself, so we don't habe to distinguish + // the different QDomNode classes, like QDomElement, QDomAttr ... Just use the copy + // ctor of QDomNode. + } // Implementation(const QDomNode & domNode) + + Implementation(const QDomElement &domElement) : _domNode(domElement) {;} + + Implementation(const Implementation &other) : Implementation(other._domNode) {;} + + ~Implementation() = default; + + Implementation &operator = (const QDomNode &domNode) + { + _domNode = domNode; + return *this; + } // operator = + + Implementation &operator = (const Implementation &other) + { + if (this != &other) _domNode = other._domNode; + return *this; + } // operator = + + + QDomNode *operator ->() const { return &_domNode; } + + const QDomNode &node() const { return _domNode; } + QDomNode &node() { return _domNode; } + + bool isValid() const { return !_domNode.isNull(); } + bool isRootNode() const { return (_domNode.nodeName() == rootNodeName); } + bool hasChildNodes() const { return _domNode.hasChildNodes(); } + + Node parentNode() const { return Node(Implementation(_domNode.parentNode())); } + Node next() const { return Node(Implementation(_domNode.nextSiblingElement())); } + Node firstChildNode() const { return Node(Implementation(_domNode.firstChildElement())); } + Node firstChildNode(const QString &nodeName) const { return Node(Implementation(_domNode.firstChildElement(nodeName))); } + + bool operator == (const Implementation &other) const { return (_domNode == other._domNode); } + bool operator != (const Implementation &other) const { return (_domNode != other._domNode); } + + + Referenced *connected() const + { + uint id = connectedId(); + if (id != SituationMap::IdHandler::BadId) + return static_cast(idHandler().accessId(id)); + return nullptr; + } // connected() const + + + uint connectedId() const + { + if (isValid()) + { + QDomElement e = _domNode.toElement(); + if (e.hasAttribute(connectionIdName)) + { + QString value = e.attribute(connectionIdName); + bool ok = false; + uint id = value.toUInt(&ok, 10); + + if ((ok) && (idHandler().isIdInUse(id))) + return id; + e.removeAttribute(connectionIdName); + } + } + return SituationMap::IdHandler::BadId; + } // connectedId() const + + + uint makeConnection(Referenced *objPtr) + { + if (objPtr != nullptr) + { + uint id = idHandler().createId(static_cast(objPtr)); + if (id != SituationMap::IdHandler::BadId) + { + QDomElement e = _domNode.toElement(); + e.setAttribute(connectionIdName, id); + return id; + } + } + return SituationMap::IdHandler::BadId; + } // makeConnection(SituationMap::IdHandler::AccessableId * objPtr) + + + bool unlinkConnection() const + { + uint id = connectedId(); + if (id != SituationMap::IdHandler::BadId) + { + idHandler().freeId(id); + QDomElement e = _domNode.toElement(); + e.removeAttribute(connectionIdName); + return true; + } + return false; + } // unlinkConnection() + +}; // GeometryModel::Node::Implementation + + +SituationMap::IdHandler *GeometryModel::Node::Implementation::_idHandler = nullptr; + + +#ifdef _WINDOWS +#pragma warning ( default : 4172 ) +#endif + + +GeometryModel::Node::Node() : _d(new Implementation()) {;} +GeometryModel::Node::Node(const Node &other) : _d(new Implementation(*(other._d))) {;} +GeometryModel::Node::Node(const Implementation &other) : _d(new Implementation(other)) {;} + +GeometryModel::Node::~Node() { delete _d; } + +GeometryModel::Node &GeometryModel::Node::operator= (const Node &other) +{ + if (this != &other) // self-assignment check + { + delete _d; + _d = new Implementation(*(other._d)); + } + return *this; +} + +GeometryModel::Node &GeometryModel::Node::operator = (Node &&other) +{ + if (this != &other) // self-assignment check + { + delete _d; + _d = doExchange(other._d, nullptr); + } + return *this; +} + + +const GeometryModel::Node::Implementation &GeometryModel::Node::get() const { return *_d; } +GeometryModel::Node::Implementation &GeometryModel::Node::get() { return *_d; } +bool GeometryModel::Node::isValid() const { return _d->isValid(); } +bool GeometryModel::Node::isRootNode() const { return _d->isRootNode(); } +GeometryModel::Node GeometryModel::Node::parentNode() const { return _d->parentNode(); } +bool GeometryModel::Node::hasChildNodes() const { return _d->hasChildNodes(); } +GeometryModel::Node GeometryModel::Node::next() const { return _d->next(); } +GeometryModel::Node GeometryModel::Node::firstChildNode() const { return _d->firstChildNode(); } +GeometryModel::Node GeometryModel::Node::firstChildNode(const QString &nodeName) const { return _d->firstChildNode(nodeName); } +bool GeometryModel::Node::operator ==(const Node &other) const { return ((*_d) == (*(other._d))); } +bool GeometryModel::Node::operator !=(const Node &other) const { return ((*_d) != (*(other._d))); } + +uint GeometryModel::Node::makeConnection(Referenced *objPtr) const { return _d->makeConnection(objPtr); } +bool GeometryModel::Node::unlinkConnection() const { return _d->unlinkConnection(); } +uint GeometryModel::Node::connectedId() const { return _d->connectedId(); } +Referenced *GeometryModel::Node::connected() const { return _d->connected(); } + + +GeometryModelQDom::GeometryModelQDom(QObject *parent) + : GeometryModel(parent) + , QDomDocument(QLatin1String(rootNodeName) + QLatin1String("Document")) + , _skipChangedSignal(false) + , _undoEnabled(true) + , _maxUndoSteps(100) + , _undoStack() + , _undoHistory() + , _redoStack() + , _redoHistory() + , _undoBlock() + , _undoHistoryBlock() + , _insideUndoBlock(false) + , _modificationCounter(0) +{ + init(); + + // OpenSuse13.2: + // Don´t really know why, but ModuleTest::checkOverlayHandler2/3 (dealing with GeometryModel) + // lead to SegFault at end of program, if 'cleanupStaticMember' is not added: + qAddPostRoutine(&GeometryModelQDom::cleanupStaticMember); + +} // GeometryModelQDom::GeometryModelQDom(QObject *parent) + + +GeometryModelQDom::GeometryModelQDom(const GeometryModelQDom &other) + : GeometryModel(other) + , QDomDocument(other.cloneNode().toDocument()) + , _skipChangedSignal(false) + , _undoEnabled(other._undoEnabled) + , _maxUndoSteps(other._maxUndoSteps) + , _undoStack() + , _undoHistory() + , _redoStack() + , _redoHistory() + , _undoBlock() + , _undoHistoryBlock() + , _insideUndoBlock(false) + , _modificationCounter(0) +{ +} // GeometryModelQDom::GeometryModelQDom(const GeometryModelQDom &other) + + +GeometryModelQDom::~GeometryModelQDom() +{ + cleanModel(); +} + + + +GeometryModelQDom & GeometryModelQDom::operator = (const GeometryModelQDom &other) +{ + fromByteArray(other.toByteArray()); + _undoEnabled = other._undoEnabled; + _maxUndoSteps = other._maxUndoSteps; + return *this; +} // GeometryModelQDom & GeometryModelQDom::operator = (const GeometryModelQDom &other) + + +/* ~~~~~~~~~~~~~~~~ create XML representation of GeometryModel ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +static void xmlContent( const QDomDocument & domDocument, QByteArray & xmlContent ) +{ + xmlContent = domDocument.toByteArray(3); +} + +template MemoryClass xmlContent(GeometryModelQDom localCopy) +{ + MemoryClass xmlContentMem; + + // 'clean' just localCopy, not the original + // cleanModel removes userData; in other words: unlink; remove uniqueMapIds + localCopy.cleanModel(); + + QDomElement rootNode = localCopy.firstChildElement(rootNodeName); + + qDebug() << " xmlContent rootNode.isNull " << rootNode.isNull() + << ", rootNode.hasChildNodes " << rootNode.hasChildNodes() + << ", rootNode.hasAttributes " << rootNode.hasAttributes(); + + // rootNode may have ChildNodes and/or Attribute (like metaData) + // pay attention also to attributes + if ((rootNode.isNull()) || !(rootNode.hasChildNodes() || rootNode.hasAttributes()) ) + { + qDebug() << " empty -> use constant default string"; + // Don't return an empty string because file mamagement service will get an error when loading + // the empty file content. + xmlContentMem = MemoryClass::fromStdString(std::string("\n\n")); + } + else + xmlContent( localCopy, xmlContentMem ); + + qDebug() << " xmlContentMem " << xmlContentMem; + return xmlContentMem; +} // template MemoryClass xmlString(GeometryModelQDom localCopy) + + +QByteArray GeometryModelQDom::toByteArray() const +{ + return xmlContent(*this); +} + +//QString GeometryModelQDom::toString() const +//{ +// return xmlContent(*this); +//} +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + +QByteArray GeometryModelQDom::toByteArrayDebug() const { return QDomDocument::toByteArray(3); } // complete XML content for debug purposes + + +/* ~~~~~~~~~~~~~~~~ restore GeometryModel from XML representation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +template bool parseXmlString(ContentClass content, GeometryModelQDom &geoModel) +{ + qDebug() << " parseXmlString 1"; + geoModel.clearUndoStack(); + geoModel.clearRedoStack(); + geoModel.cleanModel(); + bool ok = static_cast(geoModel.QDomDocument::setContent(content)); + + qDebug() << " parseXmlString geoModel.cleanModel() " << geoModel.toString(); + + geoModel.blockSignals(true); + { + geoModel.cleanModel(); + + qDebug() << " parseXmlString geoModel.cleanModel() " << geoModel.toString();; + + QDomElement rootNode = geoModel.firstChildElement(rootNodeName); + if (rootNode.isNull()) + { + QDomElement root = geoModel.createElement(rootNodeName); + geoModel.appendChild(root); + } + + } + geoModel.blockSignals(false); + qDebug() << " parseXmlString ok = " << ok; + return ok; +} // template bool parseXmlString(MemoryClass memory, GeometryModelQDom &geoModel) + + +bool GeometryModelQDom::fromByteArray(const QByteArray &content) +{ + if (content.isEmpty()) // allow empty content + { + blockSignals(true); + clearModel(); + blockSignals(false); + clearUndoStack(); + clearRedoStack(); + return true; + } + return parseXmlString(content,*this); +} // bool GeometryModelQDom::fromByteArray(const QByteArray &content) + + +bool GeometryModelQDom::fromDevice(QIODevice *device) +{ + if (device->size() < 1) // allow empty content + { + blockSignals(true); + clearModel(); + blockSignals(false); + clearUndoStack(); + clearRedoStack(); + return true; + } + return parseXmlString(device,*this); +} // bool GeometryModelQDom::fromDevice (QIODevice *device) + + +bool GeometryModelQDom::fromByteArrayDebug(const QByteArray &content) +{ + clearUndoStack(); + clearRedoStack(); + cleanModel(); + auto result = QDomDocument::setContent(content); + return static_cast(result); +} // bool GeometryModelQDom::fromStringDebug(const QByteArray &content) +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + +void GeometryModelQDom::setModelAttribute(Attribute attribute, const QVariant &value) +{ + QDomElement root = firstChildElement(rootNodeName); + qDebug() << "GeometryModelQDom isNull = " << root.isNull(); + + //const bool bEnableUndoRedo = true; //attribute != Attribute::MetaPixelBasedTextBoxes; + + pushUndo(QLatin1String("setModelAttribute")); + + if (supportedAttributes(PrimitiveType::RootNode).contains(attribute)) + root.setAttribute(attributeToString(attribute), variant2XmlString(value)); + else + { + emit attributeError(tr("Unknown or unsupported attribute for model root node: %1").arg(attributeToString(attribute))); + popUndo(); + return; + } + emitChangedSignal(Node(), attribute, false); +} // void GeometryModelQDom::setModelAttribute(Attribute attribute, const QVariant &value) + + +QVariant GeometryModelQDom::getModelAttribute(Attribute attribute) const +{ + QDomElement root = firstChildElement(rootNodeName); + + if (supportedAttributes(PrimitiveType::RootNode).contains(attribute)) + return getAttribute(root, attribute); + else + emit const_cast(this)->attributeError(tr("Unknown or unsupported attribute for model root node: %1").arg(attributeToString(attribute))); + return QVariant(); +} // QVariant GeometryModelQDom::getModelAttribute(Attribute attribute) const + + +void GeometryModelQDom::setModelEditorAttribute(Attribute attribute, const QVariant &value) +{ + QDomElement root = firstChildElement(mapSettingsName); + + switch (attribute) + { + case Attribute::PeMapBrightness: + case Attribute::PeMapCenterX: + case Attribute::PeMapCenterY: + case Attribute::PeMapSaturation: + case Attribute::PeMapZoom: + case Attribute::PeShowUtm: + case Attribute::PeSymbolOpacity: + case Attribute::PeSymbolFillType: + case Attribute::PeSymbolScale: + case Attribute::PeMapRotation: + case Attribute::PeMapType: + root.setAttribute(attributeToString(attribute), variant2XmlString(value)); + break; + + default: + emit attributeError(tr("Unknown or unsupported attribute for model-editor: %1").arg(attributeToString(attribute))); + return; + } + emitChangedSignal(Node(), attribute, false); +} // void GeometryModelQDom::setModelEditorAttribute(Attribute attribute, const QVariant &value) + + +void GeometryModelQDom::setPrimitiveAttribute(const Node &node, Attribute attribute, const QVariant &value) +{ + pushUndo(QLatin1String("setPrimitiveAttribute")); + // Check, if Attribute is supported by Primitive; if so, set it + if (hasAttribute(node, attribute)) + node.get()->toElement().setAttribute(attributeToString(attribute), variant2XmlString(value)); + else + { + emit attributeError(tr("Unknown or unsupported attribute for primitive: %1, Attribute: %2").arg(node.get()->nodeName(), attributeToString(attribute))); + + popUndo(); + return; + } + emitChangedSignal(node, attribute, false); +} // void GeometryModelQDom::setPrimitiveAttribute(QDomNode node...) + + +GeometryModel::Node GeometryModelQDom::addPrimitive( PrimitiveType type + , const QList &points + , const QList > &attributes + , int userData + , const QString &textData + , HorizontalAlign textAlign) +{ + QDomElement root = firstChildElement(rootNodeName); + bool undoEnabledRestore = _undoEnabled; + pushUndo(QLatin1String("addPrimitive")); + _undoEnabled = false; + // Create new node and set required attributes + + QDomElement primitiveDom; + + if (_primitiveTagsStrings.contains(type)) + primitiveDom = createElement(_primitiveTagsStrings[type]); + else + { + emit typeError(tr("Unknown Primitive-Type %1").arg(static_cast(type))); + _undoEnabled = undoEnabledRestore; + popUndo(); + return Node(); + } + + Node primitiveNode(primitiveDom); + + // No changed() inside this block + _skipChangedSignal = true; + + if (setPrimitivePoints(Node(primitiveDom), points)) + { + // Init Primitive with default-Values + using index = QPair; + QList primAttributes = supportedAttributes(type); + for (int i = 0; i < primAttributes.size(); ++i) + { + Attribute ap = primAttributes.at(i); + setPrimitiveAttribute(primitiveNode, ap, _attributeDefaults[index(type, ap)]); + } + + // Set Attributes given as parameter + for (int i = 0; i < attributes.size(); ++i) + setPrimitiveAttribute(primitiveNode, (attributes.at(i)).first, (attributes.at(i)).second); + + // Add textData, if any + if (! textData.isNull()) + setTextData(primitiveNode, textData, textAlign); + + // Insert userData as Attribute + if ((type != PrimitiveType::Node) && (type != PrimitiveType::RootNode)) + { + if (SituationMap::IdHandler::isIdValid(userData)) + { + QString userDataStr = QString::number(userData, 10); + primitiveDom.setAttribute(userDataName, userDataStr); + } + } + + Node result(root.appendChild(primitiveDom)); + _skipChangedSignal = false; + + emitChangedSignal(result, Attribute::MaxAttributes, true); + + _undoEnabled = undoEnabledRestore; + return result; + } + else + { + _undoEnabled = undoEnabledRestore; + popUndo(); + return Node(); + } +} // QDomNode GeometryModelQDom::addPrimitive(PrimitiveType type, ...) + + +GeometryModel::Node GeometryModelQDom::addText(const Node &parent + , const QString &textData + , HorizontalAlign textAlign + , const QList > &attributes + , int userData) +{ + pushUndo(QLatin1String("addText")); + + // Check, if parent is of correct type + QString tag = parent.get()->nodeName(); + PrimitiveType type = getType(parent); + + // Get Position (center of bounding box) + // TODO: Position of text depends on Parent-Type + // TODO: on moveNode, translateNode,... transform text also + QRectF parentBox = getBoundingBox(parent); + QList pointList; + + switch (type) + { + case PrimitiveType::Ellipse: + case PrimitiveType::Line: + case PrimitiveType::Polyline: + case PrimitiveType::Rectangle: + case PrimitiveType::Spline: + pointList.append(parentBox.topLeft()); + break; + + default: + emit typeError(tr("Can not add text to type %1").arg(tag)); + popUndo(); + return Node(); + } + + // Create Text-Node and reparent to parent + Node textNode(addPrimitive(PrimitiveType::Text, pointList, attributes, userData)); + parent.get()->appendChild(textNode.get().node()); + + // Set textData and alignment + QDomElement textDataNode = (textNode.get()->appendChild(createElement(QLatin1String("text_data")))).toElement(); + textDataNode.setAttribute(QLatin1String("alignment"), QString::number((int)textAlign, 10)); + QDomCDATASection cdata = createCDATASection(textData); + emitChangedSignal(parent, Attribute::MaxAttributes, false); + return Node(Node::Implementation(textDataNode.appendChild(cdata))); +} // QDomNode GeometryModelQDom::addText(QDomNode parent, const QString &textData,... + + +GeometryModel::Node GeometryModelQDom::getRootNode() const { return Node(Node::Implementation(firstChildElement(rootNodeName))); } + + +bool GeometryModelQDom::isEmpty() const +{ + QDomElement rootNode = firstChildElement(rootNodeName); + return (!rootNode.hasChildNodes()); +} // bool GeometryModelQDom::isEmpty() const + + +GeometryModel::Node GeometryModelQDom::getText(const Node &parent) const +{ + // Check, if parent is of correct type + PrimitiveType type = getType(parent); + + switch (type) + { + case PrimitiveType::Ellipse: + case PrimitiveType::Line: + case PrimitiveType::Polyline: + case PrimitiveType::Rectangle: + case PrimitiveType::Spline: + return Node(Node::Implementation(parent.get()->firstChildElement(QLatin1String("text")))); + + default: + return Node(); + } +} // QDomNode GeometryModelQDom::getText(QDomNode parent) + + +QString GeometryModelQDom::getTextData(const Node &node) const +{ + QString result; + // Check, if parent is of correct type + QString tag = node.get()->nodeName(); + if (tag != QLatin1String("text")) + emit const_cast(this)->typeError(tr("Can not get text-data from %1").arg(tag)); + else + { + QDomNode textData = node.get()->firstChildElement(QLatin1String("text_data")); + QDomNode data = textData.firstChild(); + if (! data.isNull()) + result = data.nodeValue(); + } + return result; +} // QString GeometryModelQDom::getTextData(QDomNode textNode) + + +QList GeometryModelQDom::getPrimitivePoints(const Node &node) const { return getPrimitivePoints(node.get().node()); } + + +QList GeometryModelQDom::getPrimitivePoints(const QDomNode &node) const +{ + const QString &tag = node.nodeName(); + PrimitiveType type = getType(node); + + // set the number of required points for this primitive... + QList result; + int minPoints = 0; + int maxPoints = 0; + + switch (type) + { + + case PrimitiveType::DictionarySymbol: + { + minPoints = 1; + maxPoints = INT_MAX; + } break; + + case PrimitiveType::Ellipse: + case PrimitiveType::Rectangle: + { + minPoints = 4; + maxPoints = 4; + } break; + + case PrimitiveType::ParticipantSymbol: + case PrimitiveType::Symbol: + case PrimitiveType::Text: + { + minPoints = 1; + maxPoints = 1; + } break; + + case PrimitiveType::Line: + { + minPoints = 2; + maxPoints = 2; + } break; + + case PrimitiveType::Polyline: + case PrimitiveType::Spline: + { + minPoints = 3; + maxPoints = INT_MAX; + } break; + + default: + { + emit const_cast(this)->modelError(tr("No points in primitive of type %1").arg(tag)); + return result; + } + } // switch + + // ... and get the points + QDomNodeList nodes = node.childNodes(); + if (nodes.isEmpty()) + emit const_cast(this)->modelError(tr("No points found in primitive of type %1").arg(tag)); + else + { + for (int i = 0; i < nodes.size(); ++i) + { + QDomElement p = nodes.at(i).toElement(); + if (p.nodeName() != QLatin1String("point")) continue; + if (!p.hasAttribute(QLatin1String("x")) || !p.hasAttribute(QLatin1String("y"))) + emit const_cast(this)->modelError(tr("Invalid point in primitive of type %1").arg(tag)); + else + { + bool okx, oky; + double x = p.toElement().attribute(QLatin1String("x")).toDouble(&okx); + double y = p.toElement().attribute(QLatin1String("y")).toDouble(&oky); + if (!(okx && oky)) + emit const_cast(this)->modelError(tr("Invalid point in primitive of type %1").arg(tag)); + else + result.append(QPointF(x, y)); + } + } + } + + // Check points + if ((result.size() < minPoints) || (result.size() > maxPoints)) + emit const_cast(this)->attributeError(tr("Wrong number of points for new primitive %1").arg(_primitiveTagsStrings[type])); + + return result; +} // QList GeometryModelQDom::getPrimitivePoints(QDomNode node) const + + +QRectF GeometryModelQDom::getBoundingBox(const Node &node) const { return getBoundingBox(node.get().node()); } + + +QRectF GeometryModelQDom::getBoundingBox(const QDomNode &node) const +{ + PrimitiveType type = getType(node); + + QRectF result; + if ((type == PrimitiveType::Node) || (type == PrimitiveType::RootNode) || (node == *this)) + { + // Recursive over all children + QRectF merge; + QDomNodeList children; + if ((type == PrimitiveType::Node) || (type == PrimitiveType::RootNode)) + { + // Children of other Node + children = node.childNodes(); + } + else + children = node.firstChildElement(rootNodeName).childNodes(); + for (int i = 0; i < children.size(); ++i) + { + merge = getBoundingBox(children.at(i)); + if (! merge.isNull()) + result = result.isNull() ? merge : result.united(merge); + } + + } + else + { + // TODO Add calculation for rotated Primitives + + bool ok; + QList points = getPrimitivePoints(node); + double width, height; + + switch (type) + { + case PrimitiveType::Ellipse: + case PrimitiveType::Rectangle: + width = getAttribute(node, Attribute::Width).toDouble(&ok); + height = getAttribute(node, Attribute::Height).toDouble(&ok); + if (ok && (points.size() == 1)) + { + result.setRect(points[0].x(), points[0].y(), width, height); + result.moveCenter(points[0]); + } + break; + + case PrimitiveType::Line: + if (points.size() == 2) + result = QRectF(points[0], points[1]).normalized(); + break; + + case PrimitiveType::DictionarySymbol: + if (points.size() > 2) + { + result = QRectF(points.at(0), points.at(1)); + for (int i = 2; i < points.count(); ++i) + result = result.united(QRectF(points.at(0), points.at(i))); + } + else + { + result = QRectF(points[0].x(), points[0].y(), 1.0, 1.0); + result.moveCenter(points[0]); + } + break; + + case PrimitiveType::Polyline: + case PrimitiveType::Spline: + if (points.size() > 2) + { + result = QRectF(points.at(0), points.at(1)); + for (int i = 2; i < points.count(); ++i) + result = result.united(QRectF(points.at(0), points.at(i))); + } + break; + + case PrimitiveType::ParticipantSymbol: + case PrimitiveType::Symbol: + case PrimitiveType::Text: + if (points.size() == 1) + { + // TODO Calculate real dimensions of Textbox and Symbol? + // Only possible with more information from GDW implementation, Map Scale etc. + result = QRectF(0, 0, 0, 0); + result.moveCenter(points[0]); + } + break; + + default: + break; + } + } // if ( type == Node ) {...} else... + + return result; +} // QRectF GeometryModelQDom::getBoundingBox(QDomNode node) + + + +void GeometryModelQDom::movePrimitiveIntoGroup(const Node &primitive, const Node &target) +{ + pushUndo(QLatin1String("movePrimitiveIntoGroup")); + + QString tag = primitive.get()->nodeName(); + if (target.get()->nodeName() != QLatin1String("node")) + { + emit typeError(tr("Can not move primitive into %1").arg(target.get()->nodeName())); + popUndo(); + return; + } + if (_primitiveTagsTypes.contains(tag)) + { + target.get()->appendChild(primitive.get().node()); + emitChangedSignal(primitive, Attribute::MaxAttributes, false); + } +} // void GeometryModelQDom::movePrimitiveIntoGroup(QDomNode primitive, QDomNode target) + + +void GeometryModelQDom::releaseGroup(const Node &group) +{ + pushUndo(QLatin1String("releaseGroup")); + + if (group.get()->nodeName() != QLatin1String("node")) + { + emit typeError(tr("Given node is not a group: %1").arg(group.get()->nodeName())); + popUndo(); + return; + } + + QDomNode parent = group.get()->parentNode(); + while (group.get()->hasChildNodes()) + parent.appendChild(group.get()->firstChild()); + parent.removeChild(group.get().node()); + + emitChangedSignal(Node(), Attribute::MaxAttributes, false); + +} // void GeometryModelQDom::releaseGroup(QDomNode group) + + +void GeometryModelQDom::deleteGroup(const Node &group) +{ + if (!group.isValid()) return; + + pushUndo(QLatin1String("deleteGroup")); + + if (group.get()->nodeName() != QLatin1String("node")) + { + emit typeError(tr("Given node is not a group: %1").arg(group.get()->nodeName())); + popUndo(); + return; + } + + QDomNode parent = group.get()->parentNode(); + parent.removeChild(group.get().node()); + + emitChangedSignal(Node(), Attribute::MaxAttributes, false); +} // void GeometryModelQDom::deleteGroup(QDomNode group) + + +void GeometryModelQDom::removePrimitive(const Node &primitive) +{ + _skipChangedSignal = true; + removePrimitive(primitive.get().node()); + _skipChangedSignal = false; +} + + +void GeometryModelQDom::removePrimitive(const QDomNode &primitive) +{ + // could be a group node + while (primitive.hasChildNodes()) + { + removePrimitive(primitive.firstChild()); + } + + // Remove userData + bool ok; + int userData = getUserData(primitive, &ok); + if (ok) + { + primitive.toElement().removeAttribute(userDataName); + emit userDataCleanup(userData); + } + + // pushUndo() here, because we must not save the primitive with userData, since + // the GeISiR2-Primitive is removed from the Map + pushUndo(QLatin1String("removePrimitive")); + QDomNode parent = primitive.parentNode(); + QDomNode oldChild = parent.removeChild(primitive); + + if (! oldChild.isNull()) + emitChangedSignal(Node(), Attribute::MaxAttributes, false); + else popUndo(); +} // void GeometryModelQDom::removePrimitive(QDomNode primitive) + + +void GeometryModelQDom::removePrimitivesByUserData(const QList &userDataList) +{ + _skipChangedSignal = true; + int removed = 0; + // First delete userData, then make the undo-backup + QList nodeList; + + nodeList.reserve(userDataList.size()); + for (int i = 0; i < userDataList.size(); ++i) + { + Node node = findPrimitive(userDataList.at(i)); + node.get()->toElement().removeAttribute(userDataName); + nodeList.append(node.get().node()); + } + startUndoBlock(); + for (int i = 0; i < nodeList.size(); ++i) + { + removePrimitive(nodeList.at(i)); + ++removed; + } + endUndoBlock(QLatin1String("removePrimitivesByUserData")); + _skipChangedSignal = false; + + if (removed > 0) + emitChangedSignal(Node(), Attribute::MaxAttributes, false); +} // void GeometryModelQDom::removePrimitivesByUserData(QList userDataList) + + +void GeometryModelQDom::removeText(Node &parent) +{ + pushUndo(QLatin1String("removeText")); + + // Check, if parent is of correct type + PrimitiveType type = getType(parent); + + switch (type) + { + case PrimitiveType::Ellipse: + case PrimitiveType::Line: + case PrimitiveType::Polyline: + case PrimitiveType::Rectangle: + case PrimitiveType::Spline: + parent.get()->removeChild(parent.get()->firstChildElement(QLatin1String("text"))); + emitChangedSignal(parent, Attribute::MaxAttributes, false); + break; + + default: + popUndo(); + return; + } +} // void GeometryModelQDom::removeText(QDomNode parent) + + +void GeometryModelQDom::setTextData(const Node &textNode, const QString &data, HorizontalAlign textAlign) +{ + pushUndo(QLatin1String("setTextData")); + + // Check, if parent is of correct type + const QString &tag = textNode.get()->nodeName(); + if (tag != QLatin1String("text")) + { + emit typeError(tr("Can not set text-data for %1").arg(tag)); + popUndo(); + } + else + { + QDomNode textData = textNode.get()->firstChildElement(QLatin1String("text_data")); + if (textData.isNull()) + textData = (textNode.get()->appendChild(createElement(QLatin1String("text_data")))).toElement(); + textData.toElement().setAttribute(QLatin1String("alignment"), QString::number((int)textAlign, 10)); + textData.removeChild(textData.firstChild()); + textData.appendChild(createCDATASection(data)); + + emitChangedSignal(textNode, Attribute::MaxAttributes, false); + } +} // void GeometryModelQDom::setTextData(QDomNode textNode, const QString &data, HorizontalAlign textAlign) + + +void GeometryModelQDom::clearModel() +{ + qDebug() << " GeometryModelQDom::clearModel()"; + + QDomElement root = firstChildElement(rootNodeName); + pushUndo(QLatin1String("clearPlan")); + + bool oldSCS = _skipChangedSignal; + bool oldUE = _undoEnabled; + _skipChangedSignal = true; + _undoEnabled = false; + while (root.hasChildNodes()) + removePrimitive(root.firstChild()); + _skipChangedSignal = oldSCS; + _undoEnabled = oldUE; + + const auto supportedAttribs = supportedAttributes(GeometryModel::PrimitiveType::RootNode); + for (const GeometryModel::Attribute &attr : supportedAttribs) + { + const QString & attrName = attributeToString(attr); + if (root.hasAttribute(attrName)) + { + root.removeAttribute(attrName); + } + } + emitChangedSignal(Node(), Attribute::MaxAttributes, false); +} // void GeometryModelQDom::clearModel() + + +bool GeometryModelQDom::setPrimitivePoints(const Node &node, const QList &points) +{ + PrimitiveType type = getType(node); + + // Check number of points and insert them + int minPoints = 0; + int maxPoints = 0; + switch (type) + { + case PrimitiveType::DictionarySymbol: + { + minPoints = 1; + maxPoints = INT_MAX; + } break; + + case PrimitiveType::Ellipse: + case PrimitiveType::Rectangle: + { + minPoints = 4; + maxPoints = 4; + } break; + + case PrimitiveType::ParticipantSymbol: + case PrimitiveType::Symbol: + case PrimitiveType::Text: + { + minPoints = 1; + maxPoints = 1; + } break; + + case PrimitiveType::Line: + { + minPoints = 2; + maxPoints = 2; + } break; + + case PrimitiveType::Polyline: + case PrimitiveType::Spline: + { + minPoints = 3; + maxPoints = INT_MAX; + } break; + + case PrimitiveType::RootNode: + case PrimitiveType::Node: + { + if (points.isEmpty()) return true; + } break; + + default: break; + } + + if ((points.size() < minPoints) || (points.size() > maxPoints)) + { + emit attributeError(tr("Wrong number of points for primitive %1").arg(_primitiveTagsStrings[type])); + return false; + } + else + { + + pushUndo(QLatin1String("setPrimitivePoints")); + + // Remove old Points before Adding + QDomElement p = node.get()->firstChildElement(QLatin1String("point")); + while (! p.isNull()) + { + node.get()->removeChild(p); + p = node.get()->firstChildElement(QLatin1String("point")); + } + + for (int i = 0; i < points.size(); ++i) + { + QDomElement point = (node.get()->appendChild(createElement(QLatin1String("point")))).toElement(); + // QString::number() does NOT honor locale settings: + point.setAttribute(QLatin1String("x"), QString::number(points.at(i).x(), 'f', CoordinateDoublePrecision)); // to enforce "1.23" instead of "1,23" + point.setAttribute(QLatin1String("y"), QString::number(points.at(i).y(), 'f', CoordinateDoublePrecision)); + } + emitChangedSignal(node, Attribute::None, true); + } + return true; +} // bool GeometryModelQDom::setPrimitivePoints(QDomNode node, QList points) + + +void GeometryModelQDom::translateNode(const Node &node, double x, double y) { translateNode(node.get().node(), x, y); } + +void GeometryModelQDom::translateNode(const QDomNode &node, double x, double y) +{ + pushUndo(QLatin1String("translateNode")); + + QTransform t; + t = t.translate(x, y); + + if (node == *this) + { + // translate complete plan + QDomElement root = node.firstChildElement(rootNodeName); + QDomNodeList children = root.childNodes(); + // No changed() inside this block + _skipChangedSignal = true; + for (int i = 0; i < children.size(); ++i) + translateNode(children.at(i), x, y); + _skipChangedSignal = false; + } + else + { + QString tag = node.nodeName(); + if (tag == QLatin1String("node")) + { + QDomNodeList children = node.childNodes(); + for (int i = 0; i < children.size(); ++i) + translateNode(children.at(i), x, y); + } + else + transformNode(node, t); + } + + emitChangedSignal(Node(Node::Implementation(node)), Attribute::None, true); +} // void GeometryModelQDom::translateNode(QDomNode node, double x, double y) + + +void GeometryModelQDom::moveNode(const Node &node, double x, double y) { moveNode(node.get().node(), x, y); } + + +void GeometryModelQDom::moveNode(const QDomNode &node, double x, double y) +{ + pushUndo(QLatin1String("moveNode")); + + // Move to (x,y) + QRectF bb = getBoundingBox(node); + QPointF center = bb.center(); + + translateNode(node, -center.x() + x, -center.y() + y); + + emitChangedSignal(Node(Node::Implementation(node)), Attribute::None, true); +} // void GeometryModelQDom::moveNode(QDomNode node, double x, double y) + + +void GeometryModelQDom::scaleNode(const Node &node, double scaleX, double scaleY) { scaleNode(node.get().node(), scaleX, scaleY); } + + +void GeometryModelQDom::scaleNode(const QDomNode &node, double scaleX, double scaleY) +{ + pushUndo(QLatin1String("scaleNode")); + + // Translate to (0.0, 0.0) + QRectF bb = getBoundingBox(node); + QPointF center = bb.center(); + translateNode(node, -center.x(), -center.y()); + + // Scale + QTransform t; + t = t.scale(scaleX, scaleY); + QString tag = node.nodeName(); + if (tag == QLatin1String("node")) + { + QDomNodeList children = node.childNodes(); + for (int i = 0; i < children.size(); ++i) + transformNode(children.at(i), t); + } + else transformNode(node, t); + + + // Translate back + translateNode(node, center.x(), center.y()); + + emitChangedSignal(Node(Node::Implementation(node)), Attribute::None, true); +} // void GeometryModelQDom::scaleNode(QDomNode node, double scaleX, double scaleY) + + +void GeometryModelQDom::rotateNode(const Node &node, double angle) { rotateNode(node.get().node(), angle); } + +void GeometryModelQDom::rotateNode(const QDomNode &node, double angle) +{ + pushUndo(QLatin1String("rotateNode")); + + // Translate to (0.0, 0.0) + QRectF bb = getBoundingBox(node); + QPointF center = bb.center(); + translateNode(node, -center.x(), -center.y()); + + // Rotate + QTransform t; + t = t.rotate(angle); + QString tag = node.nodeName(); + if (tag == QLatin1String("node")) + { + QDomNodeList children = node.childNodes(); + for (int i = 0; i < children.size(); ++i) + transformNode(children.at(i), t); + } + else transformNode(node, t); + + + // Translate back + translateNode(node, center.x(), center.y()); + + emitChangedSignal(Node(Node::Implementation(node)), Attribute::Rotate, true); + +} // void GeometryModelQDom::rotateNode(QDomNode node, double angle) + + +void GeometryModelQDom::reorderPrimitives(const NodeList &nodes, PrimitivOrder order) +{ + QDomElement root = firstChildElement(rootNodeName); + if (nodes.length() > 0) + { + pushUndo(QLatin1String("reorderPrimitives")); + + if (order == GeometryModelQDom::PrimitivOrder::Lower) + { + QDomNode before; + for (int i = nodes.length() - 1; i >= 0; --i) + before = root.insertBefore(nodes.at(i).get().node(), before); + } + else if (order == GeometryModelQDom::PrimitivOrder::Raise) + { + for (int i = 0; i < nodes.length(); ++i) + root.insertAfter(nodes.at(i).get().node(), QDomNode()); + } + + emitChangedSignal(Node(), Attribute::MaxAttributes, false); + } +} // void GeometryModelQDom::reorderPrimitives(const QList &nodes, t_PrimOrder order) + + +void GeometryModelQDom::merge(const Node &other, bool skipUndo, bool intoNewGroup) { merge(other.get().node(), skipUndo, intoNewGroup); } + + +void GeometryModelQDom::merge(GeometryModel *other, bool skipUndo, bool intoNewGroup) +{ + const GeometryModelQDom *otherQDom = dynamic_cast(other); + merge(*otherQDom, skipUndo, intoNewGroup); + +} // void GeometryModelQDom::merge( GeometryModel * other, bool skipUndo, bool intoNewGroup ) + + +void GeometryModelQDom::merge(const QDomNode &other, bool skipUndo, bool intoNewGroup) +{ + if ((other == *this) || (other.ownerDocument() == *this)) + { + // Do not merge with itself or own child + return; + } + + if (! skipUndo) + pushUndo(QLatin1String("merge")); + + QString tag = other.nodeName(); + if (other.nodeType() == QDomNode::DocumentNode) + { + // Its a complete plan, add recursive + QDomElement root = other.firstChildElement(rootNodeName); + // No changed() inside this block + _skipChangedSignal = true; + if (intoNewGroup) + { + QList nodeList; + QDomNodeList domNodeList = root.childNodes(); + nodeList.reserve(domNodeList.size()); + for (int i = 0; i < domNodeList.size(); ++i) + nodeList.append(domNodeList.at(i)); + const QDomNode &group = createGroup(nodeList); + setNodeDisplayName(Node(group), tr("Imported Group")); + } + else + { + while (root.hasChildNodes()) + merge(root.firstChild(), true); + } + _skipChangedSignal = false; + } + else if ((getType(other) == PrimitiveType::Unknown) || (getType(other) == PrimitiveType::MapDefaults)) + { + // Unknown type error + emit typeError(tr("Can not merge with %1").arg(tag)); + if (! skipUndo) + popUndo(); + return; + } + else + { + // Insert + QDomElement docRoot = firstChildElement(rootNodeName); + docRoot.appendChild(other); + } + + emitChangedSignal(Node(), Attribute::MaxAttributes, false); +} // void PlanModel::merge(QDomNode other, bool skipUndo, bool intoNewGroup) + + +unsigned int GeometryModelQDom::modificationCounter() const { return _modificationCounter; } + + +void GeometryModelQDom::resetModificationCounter() +{ + _modificationCounter = 0; + emit modificationCount(_modificationCounter); +} // void GeometryModelQDom::resetModificationCounter() + + +void GeometryModelQDom::undo(unsigned int steps) +{ + if (undoSteps() < 1) return; + if (steps < 1) return; + + if (_insideUndoBlock) + endUndoBlock(); + + emit nodeCleanup(Node(firstChildElement(rootNodeName))); + while ((steps > 0) && (undoSteps() > 0)) + { + QDomNode root = firstChildElement(rootNodeName); + QDomNode undoNode = _undoStack.pop(); + + _redoStack.push(root.cloneNode()); + _redoHistory.push(_undoHistory.pop()); + + root = replaceChild(undoNode, root); + + if (root.isNull()) // was replaceChild successful? + { + emit criticalError(tr("Undo function failed.")); + break; + } + else + { + --steps; + } + --_modificationCounter; + emit modificationCount(_modificationCounter); + } + + emitChangedSignal(Node(), Attribute::MaxAttributes, true); + +} // void GeometryModelQDom::undo(unsigned int steps) + + +void GeometryModelQDom::redo(unsigned int steps) +{ + if (redoSteps() < 1) return; + if (steps < 1) return; + + emit nodeCleanup(Node(firstChildElement(rootNodeName))); + + while ((steps > 0) && (redoSteps() > 0)) + { + QDomNode root = firstChildElement(rootNodeName); + QDomNode redoNode = _redoStack.pop(); + + _undoStack.push(root.cloneNode()); + _undoHistory.push(_redoHistory.pop()); + + root = replaceChild(redoNode, root); + + if (root.isNull()) // was replaceChild successful? + { + emit criticalError(tr("Redo function failed.")); + break; + } + --steps; + ++_modificationCounter; + emit modificationCount(_modificationCounter); + } + emitChangedSignal(Node(), Attribute::MaxAttributes, true); + +} // void GeometryModelQDom::redo(unsigned int steps) + + +void GeometryModelQDom::enableUndo(bool enable) +{ + _undoEnabled = enable; +} // void GeometryModelQDom::enableUndo(bool enable) + + +void GeometryModelQDom::startUndoBlock() +{ + if (! _insideUndoBlock) + { + QDomElement root = firstChildElement(rootNodeName); + _undoBlock = root.cloneNode(); + } + _insideUndoBlock = true; +} // void GeometryModelQDom::startUndoBlock() + + +void GeometryModelQDom::abortUndoBlock() +{ + if (_insideUndoBlock) + _undoBlock.clear(); +} // void GeometryModelQDom::abortUndoBlock() + + +void GeometryModelQDom::endUndoBlock(const QString &eventDescription) +{ + if (_insideUndoBlock) + { + if (_undoEnabled && (! _undoBlock.isNull())) + { + while (undoSteps() >= _maxUndoSteps) + { + // Size-limit reached, remove first element + _undoStack.erase(_undoStack.begin()); + _undoHistory.erase(_undoHistory.begin()); + } + _undoStack.push(_undoBlock); + _undoHistory.push((eventDescription.isEmpty() ? _undoHistoryBlock : eventDescription)); + clearRedoStack(); + ++_modificationCounter; + emit modificationCount(_modificationCounter); + } + } + _insideUndoBlock = false; +} // void GeometryModelQDom::endUndoBlock(const QString &eventDescription) + + +void GeometryModelQDom::setMaxUndoSteps(unsigned int max) +{ + _maxUndoSteps = (max < 1) ? 1 : max; + + while (undoSteps() > _maxUndoSteps) + { + // shrink to max size, if needed. + _undoStack.erase(_undoStack.begin()); + _undoHistory.erase(_undoHistory.begin()); + } +} // void GeometryModelQDom::setMaxUndoSteps(int max) + + +unsigned int GeometryModelQDom::maxUndoSteps() const +{ + return _maxUndoSteps; +} + + +void GeometryModelQDom::cleanModel() +{ + qDebug() << " GeometryModelQDom::cleanModel()"; + + QMap::iterator it = _primitiveTagsTypes.begin(); + while (it != _primitiveTagsTypes.end()) + { + QDomNodeList nodes = elementsByTagName(it.key()); + for (int i = 0; i < nodes.size(); ++i) + { + if ((_primitiveTagsTypes[it.key()] == PrimitiveType::Node) || (_primitiveTagsTypes[it.key()] == PrimitiveType::RootNode)) + { + // Remove empty groups + QDomNode group = nodes.at(i); + + if ( ! (group.hasChildNodes() || group.hasAttributes()) ) + { + group.parentNode().removeChild(group); + } + } + else + { + // Remove userData + const QDomNode &node = nodes.at(i); + if (hasUserData(node)) + { + bool ok; + int userData = getUserData(node, &ok); + if (ok) + { + nodes.at(i).toElement().removeAttribute(userDataName); + emit userDataCleanup(userData); + } + } + } + + GeometryModel::Node::Implementation nodeImpl(nodes.at(i)); + emit connectionCleanup(nodeImpl.connected()); + nodeImpl.unlinkConnection(); + } + ++it; + } + + resetModificationCounter(); +} // void GeometryModelQDom::cleanModel() + + +void GeometryModelQDom::setNodeDisplayName(const Node &node, const QString &name) +{ + const QString attName = QLatin1String("GuiDisplayName"); + QDomElement e = node.get().node().toElement(); + + if (e.isNull()) + { + qCritical() << " node not found"; + return; + } + + e.setAttribute(attName, name); + + emitChangedSignal(node, Attribute::MaxAttributes, false); + +} // void GeometryModelQDom::setNodeDisplayName(const Node & node, const QString & name) + + +QString GeometryModelQDom::getNodeDisplayName(const Node &node) const +{ + const QString attName = QLatin1String("GuiDisplayName"); + QDomElement e = node.get().node().toElement(); + + return (e.hasAttribute(attName)) ? e.attribute(attName) : QString(); + +} // QString GeometryModelQDom::getNodeDisplayName(const Node & node) const + + +QVariant GeometryModelQDom::getAttribute(const Node &node, Attribute attribute) const { return getAttribute(node.get().node(), attribute); } + +QVariant GeometryModelQDom::getAttribute(const QDomNode &node, Attribute attribute) const +{ + QString att = attributeToString(attribute); + QDomElement e = node.toElement(); + + if (e.hasAttribute(att)) + { + QString value = e.attribute(att); + + if (_attributeTypes[attribute] == QMetaType::QColor) + { + // Return Colors as QString and let the user convert it with static method + // PlanModel::colorFromString() - QVariant ignores alpha in Colors + return QVariant(colorFromString(value)); + } + else if (_attributeTypes[attribute] == QMetaType::QPointF) + return QVariant(pointFromString(value)); + else + { + if (_attributeTypes[attribute] == QMetaType::User) + { + // Return PlanModel-Enums as String-Value + // TODO: Implement UserTypes? + return QVariant(value); + } + else + { + // Convert other values to their native type + QVariant result(value); + return result; + } + } + } + else + { + // Check for default + if (hasAttribute(node, attribute)) + { + using index = QPair; + QVariant result = QVariant::fromValue(_attributeDefaults[index(PrimitiveType::Unknown, attribute)]); + return result; + } + else + { + emit const_cast(this)->attributeError(att + tr(": Attribute unknown in primitive %1").arg(node.nodeName())); + return QVariant(); + } + } + + return QVariant(); +} // QVariant GeometryModelQDom::getAttribute(QDomNode node, Attribute attribute) + + +bool GeometryModelQDom::hasAttribute(const Node &node, Attribute attribute) const { return hasAttribute(node.get().node(), attribute); } + + +bool GeometryModelQDom::hasAttribute(const QDomNode &node, Attribute attribute) const +{ + QString tag = node.nodeName(); + if (_primitiveTagsTypes.contains(tag)) + { + PrimitiveType t = _primitiveTagsTypes[tag]; + if (_primitiveSupportedAttributes.contains(t)) + return supportedAttributes(t).contains(attribute); + } + return false; +} // bool GeometryModelQDom::hasAttribute(QDomNode node, Attribute attribute) const + + +QMetaType::Type GeometryModelQDom::attributeType(const Attribute &attribute) const +{ + if (_attributeTypes.contains(attribute)) return _attributeTypes[attribute]; + else return QMetaType::UnknownType; +} // QMetaType::Type GeometryModelQDom::attributeType(Attribute attribute) const + + +QList GeometryModelQDom::supportedAttributes(PrimitiveType type) const +{ + if (_primitiveSupportedAttributes.contains(type)) return _primitiveSupportedAttributes.values(type); + else return QList(); +} // QList GeometryModelQDom::supportedAttributes(PrimitiveType type) const + + +GeometryModel::Node GeometryModelQDom::createGroup(const NodeList &primitives) +{ + pushUndo(QLatin1String("createGroup")); + + QDomElement root = firstChildElement(rootNodeName); + QDomElement node = createElement(_primitiveTagsStrings[PrimitiveType::Node]); + QDomNode group = root.appendChild(node); + if (! group.isNull()) + { + for (int i = 0; i < primitives.size(); ++i) + { + // For performance-reasons we use direct appendChild(), but + // normally you should use movePrimitiveIntoGroup(primitives.at(i), group); + group.appendChild(primitives.at(i).get().node()); + } + } + + emitChangedSignal(Node(), Attribute::MaxAttributes, false); + + return Node(Node::Implementation(group)); +} // QDomNode PlanModel::createGroup(QList primitives) + + +QDomNode GeometryModelQDom::createGroup(const QList &primitives) +{ + pushUndo(QLatin1String("createGroup")); + + QDomElement root = firstChildElement(rootNodeName); + QDomElement node = createElement(_primitiveTagsStrings[PrimitiveType::Node]); + QDomNode group = root.appendChild(node); + if (! group.isNull()) + { + for (int i = 0; i < primitives.size(); ++i) + { + // For performance-reasons we use direct appendChild(), but + // normally you should use movePrimitiveIntoGroup(primitives.at(i), group); + group.appendChild(primitives.at(i)); + } + } + + emitChangedSignal(Node(), Attribute::MaxAttributes, false); + + return group; +} // QDomNode PlanModel::createGroup(QList primitives) + + +GeometryModel::Node GeometryModelQDom::isInGroup(const Node &node) const +{ + QDomNode parent = node.get()->parentNode(); + return Node(Node::Implementation((getType(parent) == PrimitiveType::Node) ? parent : QDomNode())); +} // QDomNode GeometryModelQDom::isInGroup(QDomNode primitive) const + + +GeometryModel::NodeList GeometryModelQDom::getGroupPrimitives(const Node &node) +{ + PrimitiveType myNodeType = getType(node); + if ((myNodeType != PrimitiveType::Node) && (myNodeType != PrimitiveType::RootNode)) + { + emit typeError(tr("Given node is not a group: %1").arg(node.get()->nodeName())); + return NodeList(); + } + + const QDomNodeList &domPrimitiveList = node.get()->childNodes(); + GeometryModel::NodeList primitiveList; + + primitiveList.reserve(domPrimitiveList.size()); + for (int i = 0; i < domPrimitiveList.size(); ++i) + primitiveList += Node(Node::Implementation(domPrimitiveList.at(i))); + + return primitiveList; + +} // QDomNodeList GeometryModelQDom::getGroupPrimitives(QDomNode node) const + + +GeometryModel::Node GeometryModelQDom::findPrimitive(int userData) const +{ + // TODO: This is inefficient!!! Search only for Att userData! + QString userDataString = QString::number(userData, 10); + QMap::iterator it = _primitiveTagsTypes.begin(); + while (it != _primitiveTagsTypes.end()) + { + QDomNodeList nodes = elementsByTagName(it.key()); + for (int i = 0; i < nodes.size(); ++i) + { + QDomElement e = nodes.at(i).toElement(); + if (e.hasAttribute(userDataName) && (e.attribute(userDataName) == userDataString)) + return Node(Node::Implementation(nodes.at(i))); + } + ++it; + } + + return Node(); +} // QDomNode GeometryModelQDom::findPrimitive(int userData) const + + +bool GeometryModelQDom::hasUserData(const Node &node) const { return hasUserData(node.get().node()); } +int GeometryModelQDom::getUserData(const Node &node, bool *ok) const { return getUserData(node.get().node(), ok); } + + +bool GeometryModelQDom::hasUserData(const QDomNode &node) const { return node.toElement().hasAttribute(userDataName); } + + +int GeometryModelQDom::getUserData(const QDomNode &node, bool *okPtr) const +{ + if (okPtr) *okPtr = false; // if provided, initialize + + int result = 0; + bool ok = false; + + QDomElement e = node.toElement(); + if (e.hasAttribute(userDataName)) + { + QString value = e.attribute(userDataName); + result = value.toInt(&ok, 10); + if (!(ok)) + emit const_cast(this)->modelError(tr("Can not convert value to int: %1").arg(value)); + } + else + { + PrimitiveType type = getType(node); + + if ((type != PrimitiveType::Node) && (type != PrimitiveType::RootNode) && (type != PrimitiveType::Unknown)) + { + qWarning() << "Missing user data at node " << node.nodeName() << " connectedId=" << Node::Implementation(node).connectedId() + << " [maybe m_internal->m_modelClipboard->clearModel()]"; +// #ifdef LTS_DEBUG +// // *** To locate a bug stop program here and start debugger, should be removed for final release +// assert(false); +// // *** +// #endif + emit const_cast(this)->typeError(tr("No UserData in %1").arg(node.nodeName())); + } + return SituationMap::IdHandler::BadId; + } + if (okPtr) *okPtr = ok; + return result; +} // void* GeometryModelQDom::getUserData(QDomNode node, bool *ok) + + +void GeometryModelQDom::setUserData(const Node &node, int userData) +{ + PrimitiveType type = getType(node); + + if ((type == PrimitiveType::Node) || (type == PrimitiveType::RootNode) || (type == PrimitiveType::Unknown)) + emit typeError(tr("Can not set UserData in %1").arg(node.get()->nodeName())); + else + { + if (!SituationMap::IdHandler::isIdValid(userData)) + { + QDomElement e = node.get()->toElement(); + e.removeAttribute(userDataName); + } + else + { + QString userDataString = QString::number(userData, 10); + QDomElement e = node.get()->toElement(); + e.setAttribute(userDataName, userDataString); + } + emitChangedSignal(node, Attribute::MaxAttributes, false); + } +} // void GeometryModelQDom::setUserData(QDomNode node, int userData) + + +QMultiMap GeometryModelQDom::userDataMap(const Node &node) +{ + QMultiMap domResult = userDataMap(node.get().node()); + QMultiMap result; + + QMultiMap::const_iterator iter = domResult.constBegin(); + + for (; iter != domResult.constEnd(); ++iter) + result.insert(iter.key(), Node(Node::Implementation(iter.value()))); + + return result; +} // QMultiMap GeometryModelQDom::userDataMap(const Node & node) + + +QMultiMap GeometryModelQDom::userDataMap(const QDomNode &node) const +{ + QMultiMap result; + if ((node.ownerDocument() != *this) && (! node.isNull())) + { + // Not a Node of this Document, ignore + return result; + } + + QDomNodeList children; + if ((node == *this) || node.isNull()) + children = firstChildElement(rootNodeName).childNodes(); + else if ((getType(node) == PrimitiveType::Node) || (getType(node) == PrimitiveType::RootNode)) + children = node.childNodes(); + + for (int i = 0; i < children.size(); ++i) + { + QDomNode curNode = children.at(i); + if (getType(curNode) == PrimitiveType::Node) + { + // If its a node, search deep and merge results... + QMultiMap deepResult = userDataMap(curNode); + QMultiMap::const_iterator it = deepResult.constBegin(); + while (it != deepResult.constEnd()) + { + int key = it.key(); + QList values = deepResult.values(key); + for (int k = 0; k < values.size(); ++k) + result.insert(key, values.at(k)); + ++it; + } + + } + else + { + // ...get UserData and append to result + bool ok; + int userData = getUserData(curNode, &ok); + if (ok) + result.insert(userData, curNode); + } + } + return result; +} // QMultiMap GeometryModelQDom::userDataMap(QDomNode node) + + +static void resetUserData(const QDomNode &node) +{ + const QDomNodeList &childNodes = node.childNodes(); + for (int i = 0; i < childNodes.size(); ++i) + resetUserData(childNodes.at(i)); + node.toElement().removeAttribute(userDataName); + node.toElement().removeAttribute(connectionIdName); + +} // resetUserData (QDomNode & node) + + +GeometryModel::Node GeometryModelQDom::copy(const Node &node, int) +{ + QDomElement root = firstChildElement(rootNodeName); + pushUndo(QLatin1String("copy")); + + QDomNode clone = node.get()->cloneNode(true); + + resetUserData(clone); + + QDomNode result = root.appendChild(clone); + emitChangedSignal(Node(), Attribute::MaxAttributes, false); + return Node(Node::Implementation(result)); +} // GeometryModel::Node GeometryModelQDom::copy(Node node, int userData) + + +GeometryModelQDom::PrimitiveType GeometryModelQDom::getType(const Node &node) const { return getType(node.get().node()); } + + +GeometryModelQDom::PrimitiveType GeometryModelQDom::getType(const QDomNode &node) const +{ + QString tag = node.nodeName(); + if (_primitiveTagsTypes.contains(tag)) return _primitiveTagsTypes[tag]; + else return PrimitiveType::Unknown; +} // PrimitiveType GeometryModelQDom::getType(QDomNode node) const + + +unsigned int GeometryModelQDom::redoSteps() const { return _redoStack.size(); } +unsigned int GeometryModelQDom::undoSteps() const { return _undoStack.size(); } + + +QString GeometryModelQDom::attributeToString(const Attribute &attribute) const +{ + switch (attribute) + { + case Attribute::AbcProtection: return QLatin1String("abc_protection"); + case Attribute::Affiliation: return QLatin1String("affil"); + case Attribute::Alignment: return QLatin1String("alignment"); + case Attribute::AnchorHorizontal: return QLatin1String("anchor_horizontal"); + case Attribute::AnchorVertical: return QLatin1String("anchor_vertical"); + case Attribute::ArrowClampHeight: return QLatin1String("arrow_clamp_height"); + case Attribute::ArrowHeight: return QLatin1String("arrow_height"); + case Attribute::ArrowLineWidth: return QLatin1String("arrow_line_width"); + case Attribute::Arrows: return QLatin1String("arrows"); + case Attribute::ArrowWidth: return QLatin1String("arrow_width"); + case Attribute::Box: return QLatin1String("box"); + case Attribute::Center: return QLatin1String("center_point"); + case Attribute::Close: return QLatin1String("close"); + case Attribute::Color: return QLatin1String("color"); + case Attribute::Extent: return QLatin1String("extent"); + case Attribute::Fill: return QLatin1String("fill"); + case Attribute::FillColor: return QLatin1String("FillColor"); + case Attribute::FillPattern: return QLatin1String("FillPattern"); + case Attribute::FillStyle: return QLatin1String("FillStyle"); + case Attribute::Font: return QLatin1String("font"); + case Attribute::FontBold: return QLatin1String("FontBold"); + case Attribute::FontItalic: return QLatin1String("FontItalic"); + case Attribute::FontSize: return QLatin1String("FontSize"); + case Attribute::Height: return QLatin1String("height"); + case Attribute::LineColor: return QLatin1String("linecolor"); + case Attribute::LineStyle: return QLatin1String("linestyle"); + case Attribute::LineWidth: return QLatin1String("linewidth"); + case Attribute::MarginHorizontal: return QLatin1String("margin_horizontal"); + case Attribute::MarginVertical: return QLatin1String("margin_vertical"); + case Attribute::Mobility: return QLatin1String("mobility"); + case Attribute::SIDC: return QLatin1String("sidc"); + case Attribute::SIDC_Color: return QLatin1String("sidc_color"); + case Attribute::SIDC_Attributes: return QLatin1String("sidc_attributes"); + case Attribute::SymbolId: return QLatin1String("symbol_id"); + case Attribute::SymbolOffsetX: return QLatin1String("symbol_offsetX"); + case Attribute::SymbolOffsetY: return QLatin1String("symbol_offsetY"); + case Attribute::SymbolDirection: return QLatin1String("symbol_direction"); + case Attribute::ForceId: return QLatin1String("force_id"); + case Attribute::SymbolState: return QLatin1String("symbolState"); + case Attribute::SymbolUncertainty: return QLatin1String("symbolUncertainty"); + case Attribute::SymbolExplicitScale: return QLatin1String("explicitScale"); + case Attribute::DamageForceId: return QLatin1String("damage_force_id"); + case Attribute::Injury: return QLatin1String("injury"); + case Attribute::PeMapBrightness: return QLatin1String("pe_map_brightness"); + case Attribute::PeMapCenterX: return QLatin1String("pe_map_centerx"); + case Attribute::PeMapCenterY: return QLatin1String("pe_map_centery"); + case Attribute::PeMapSaturation: return QLatin1String("pe_map_saturation"); + case Attribute::PeMapZoom: return QLatin1String("pe_map_zoom"); + case Attribute::PeMapRotation: return QLatin1String("pe_map_rotation"); + case Attribute::PeMapType: return QLatin1String("pe_map_type"); + case Attribute::PeShowUtm: return QLatin1String("pe_show_utm"); + + case Attribute::PeSymbolOpacity: return QLatin1String("symbol_opacity"); + case Attribute::PeSymbolFillType: return QLatin1String("pe_symbol_fill_type"); + case Attribute::PeSymbolScale: return QLatin1String("pe_symbol_scale"); + + case Attribute::MetaDataTitle: return QLatin1String("meta_data_title"); + case Attribute::MetaDataAuthor: return QLatin1String("meta_data_author"); + case Attribute::MetaDataDate: return QLatin1String("meta_data_date"); + case Attribute::MetaDataDesciption: return QLatin1String("meta_data_description"); + case Attribute::MetaDataMinScale: return QLatin1String("meta_data_min_scale"); + case Attribute::MetaDataMaxScale: return QLatin1String("meta_data_max_scale"); + case Attribute::MetaPixelBasedTextBoxes: return QLatin1String("meta_pixel_based_text_boxes"); + + case Attribute::Rotate: return QLatin1String("rotate"); + case Attribute::Rounding: return QLatin1String("rounding"); + case Attribute::Start: return QLatin1String("start"); + case Attribute::Text_EF: return QLatin1String("text_ef"); + case Attribute::Text_G: return QLatin1String("text_g"); + case Attribute::Text_H: return QLatin1String("text_h"); + case Attribute::Text_KLNPJ: return QLatin1String("text_jklnp"); + case Attribute::Text_M: return QLatin1String("text_m"); + case Attribute::Text_T: return QLatin1String("text_t"); + case Attribute::Text_V: return QLatin1String("text_v"); + case Attribute::Text_W: return QLatin1String("text_w"); + case Attribute::Text_XY: return QLatin1String("text_xy"); + case Attribute::Text_Z: return QLatin1String("text_z"); + case Attribute::UtmEast: return QLatin1String("utm_east"); + case Attribute::UtmGrid: return QLatin1String("utm_grid"); + case Attribute::UtmNorth: return QLatin1String("utm_north"); + case Attribute::UtmZone: return QLatin1String("utm_zone"); + case Attribute::Width: return QLatin1String("width"); + case Attribute::att_vbg_et: return QLatin1String("w_vbg_et"); + default: return QLatin1String("Unknown Attribute"); + } +} // QString GeometryModelQDom::attributeToString(Attribute attribute) const + + + +void GeometryModelQDom::init() +{ + using index = QPair; + + // Only once + if (! _staticInitDone) + { + // Create mapping between primitive-types and their string/tag-name + + _primitiveTagsTypes[QLatin1String("participantSymbol")] = PrimitiveType::ParticipantSymbol; + _primitiveTagsTypes[QLatin1String("dictionarySymbol")] = PrimitiveType::DictionarySymbol; + _primitiveTagsTypes[QLatin1String("ellipse")] = PrimitiveType::Ellipse; + _primitiveTagsTypes[QLatin1String("line")] = PrimitiveType::Line; + _primitiveTagsTypes[QLatin1String("polyline")] = PrimitiveType::Polyline; + _primitiveTagsTypes[QLatin1String("rectangle")] = PrimitiveType::Rectangle; + _primitiveTagsTypes[QLatin1String("spline")] = PrimitiveType::Spline; + _primitiveTagsTypes[QLatin1String("symbol")] = PrimitiveType::Symbol; + _primitiveTagsTypes[QLatin1String("text")] = PrimitiveType::Text; + _primitiveTagsTypes[QLatin1String("node")] = PrimitiveType::Node; + _primitiveTagsTypes[rootNodeName] = PrimitiveType::RootNode; + + _primitiveTagsStrings[PrimitiveType::ParticipantSymbol] = QLatin1String("participantSymbol"); + _primitiveTagsStrings[PrimitiveType::DictionarySymbol] = QLatin1String("dictionarySymbol"); + _primitiveTagsStrings[PrimitiveType::Ellipse] = QLatin1String("ellipse"); + _primitiveTagsStrings[PrimitiveType::Line] = QLatin1String("line"); + _primitiveTagsStrings[PrimitiveType::Polyline] = QLatin1String("polyline"); + _primitiveTagsStrings[PrimitiveType::Rectangle] = QLatin1String("rectangle"); + _primitiveTagsStrings[PrimitiveType::Spline] = QLatin1String("spline"); + _primitiveTagsStrings[PrimitiveType::Symbol] = QLatin1String("symbol"); + _primitiveTagsStrings[PrimitiveType::Text] = QLatin1String("text"); + _primitiveTagsStrings[PrimitiveType::Node] = QLatin1String("node"); + + + // Don't set + // _primitiveTagsStrings[PrimitiveType::RootNode] = rootNodeName; + // because the root node can not be created from interface. + + // Create mapping between node-types and supported attributes + // "Unknown" is used for plan-attributes + + _primitiveSupportedAttributes.insert(PrimitiveType::RootNode, Attribute::MetaDataTitle); + _primitiveSupportedAttributes.insert(PrimitiveType::RootNode, Attribute::MetaDataAuthor); + _primitiveSupportedAttributes.insert(PrimitiveType::RootNode, Attribute::MetaDataDate); + _primitiveSupportedAttributes.insert(PrimitiveType::RootNode, Attribute::MetaDataDesciption); + _primitiveSupportedAttributes.insert(PrimitiveType::RootNode, Attribute::MetaDataMinScale); + _primitiveSupportedAttributes.insert(PrimitiveType::RootNode, Attribute::MetaDataMaxScale); + _primitiveSupportedAttributes.insert(PrimitiveType::RootNode, Attribute::MetaPixelBasedTextBoxes); + + _primitiveSupportedAttributes.insert(PrimitiveType::MapDefaults, Attribute::PeMapBrightness); + _primitiveSupportedAttributes.insert(PrimitiveType::MapDefaults, Attribute::PeMapCenterX); + _primitiveSupportedAttributes.insert(PrimitiveType::MapDefaults, Attribute::PeMapCenterY); + _primitiveSupportedAttributes.insert(PrimitiveType::MapDefaults, Attribute::PeMapSaturation); + _primitiveSupportedAttributes.insert(PrimitiveType::MapDefaults, Attribute::PeMapZoom); + _primitiveSupportedAttributes.insert(PrimitiveType::MapDefaults, Attribute::PeMapRotation); + _primitiveSupportedAttributes.insert(PrimitiveType::MapDefaults, Attribute::PeMapType); + _primitiveSupportedAttributes.insert(PrimitiveType::MapDefaults, Attribute::PeShowUtm); + + _primitiveSupportedAttributes.insert(PrimitiveType::MapDefaults, Attribute::PeSymbolOpacity); + _primitiveSupportedAttributes.insert(PrimitiveType::MapDefaults, Attribute::PeSymbolFillType); + _primitiveSupportedAttributes.insert(PrimitiveType::MapDefaults, Attribute::PeSymbolScale); + + _primitiveSupportedAttributes.insert(PrimitiveType::MapDefaults, Attribute::UtmEast); + _primitiveSupportedAttributes.insert(PrimitiveType::MapDefaults, Attribute::UtmGrid); + _primitiveSupportedAttributes.insert(PrimitiveType::MapDefaults, Attribute::UtmNorth); + _primitiveSupportedAttributes.insert(PrimitiveType::MapDefaults, Attribute::UtmZone); + + _primitiveSupportedAttributes.insert(PrimitiveType::DictionarySymbol, Attribute::SIDC); + _primitiveSupportedAttributes.insert(PrimitiveType::DictionarySymbol, Attribute::SIDC_Attributes); + + _primitiveSupportedAttributes.insert(PrimitiveType::ParticipantSymbol, Attribute::SIDC); + _primitiveSupportedAttributes.insert(PrimitiveType::ParticipantSymbol, Attribute::SIDC_Attributes); + _primitiveSupportedAttributes.insert(PrimitiveType::ParticipantSymbol, Attribute::ForceId); + _primitiveSupportedAttributes.insert(PrimitiveType::ParticipantSymbol, Attribute::SymbolState); + _primitiveSupportedAttributes.insert(PrimitiveType::ParticipantSymbol, Attribute::DamageForceId); + _primitiveSupportedAttributes.insert(PrimitiveType::ParticipantSymbol, Attribute::Injury); + _primitiveSupportedAttributes.insert(PrimitiveType::ParticipantSymbol, Attribute::Text_EF); + _primitiveSupportedAttributes.insert(PrimitiveType::ParticipantSymbol, Attribute::Text_G); + _primitiveSupportedAttributes.insert(PrimitiveType::ParticipantSymbol, Attribute::Text_H); + _primitiveSupportedAttributes.insert(PrimitiveType::ParticipantSymbol, Attribute::Text_KLNPJ); + _primitiveSupportedAttributes.insert(PrimitiveType::ParticipantSymbol, Attribute::Text_M); + _primitiveSupportedAttributes.insert(PrimitiveType::ParticipantSymbol, Attribute::Text_T); + _primitiveSupportedAttributes.insert(PrimitiveType::ParticipantSymbol, Attribute::Text_V); + _primitiveSupportedAttributes.insert(PrimitiveType::ParticipantSymbol, Attribute::Text_W); + _primitiveSupportedAttributes.insert(PrimitiveType::ParticipantSymbol, Attribute::Text_XY); + _primitiveSupportedAttributes.insert(PrimitiveType::ParticipantSymbol, Attribute::Text_Z); + + _primitiveSupportedAttributes.insert(PrimitiveType::Ellipse, Attribute::Center); + _primitiveSupportedAttributes.insert(PrimitiveType::Ellipse, Attribute::Color); + _primitiveSupportedAttributes.insert(PrimitiveType::Ellipse, Attribute::Height); + _primitiveSupportedAttributes.insert(PrimitiveType::Ellipse, Attribute::Width); + _primitiveSupportedAttributes.insert(PrimitiveType::Ellipse, Attribute::Extent); + _primitiveSupportedAttributes.insert(PrimitiveType::Ellipse, Attribute::Fill); + _primitiveSupportedAttributes.insert(PrimitiveType::Ellipse, Attribute::FillColor); + _primitiveSupportedAttributes.insert(PrimitiveType::Ellipse, Attribute::FillPattern); + _primitiveSupportedAttributes.insert(PrimitiveType::Ellipse, Attribute::FillStyle); + _primitiveSupportedAttributes.insert(PrimitiveType::Ellipse, Attribute::LineStyle); + _primitiveSupportedAttributes.insert(PrimitiveType::Ellipse, Attribute::LineWidth); + _primitiveSupportedAttributes.insert(PrimitiveType::Ellipse, Attribute::Rotate); + _primitiveSupportedAttributes.insert(PrimitiveType::Ellipse, Attribute::Start); + + _primitiveSupportedAttributes.insert(PrimitiveType::Line, Attribute::ArrowClampHeight); + _primitiveSupportedAttributes.insert(PrimitiveType::Line, Attribute::ArrowHeight); + _primitiveSupportedAttributes.insert(PrimitiveType::Line, Attribute::ArrowLineWidth); + _primitiveSupportedAttributes.insert(PrimitiveType::Line, Attribute::Arrows); + _primitiveSupportedAttributes.insert(PrimitiveType::Line, Attribute::ArrowWidth); + _primitiveSupportedAttributes.insert(PrimitiveType::Line, Attribute::Color); + _primitiveSupportedAttributes.insert(PrimitiveType::Line, Attribute::LineStyle); + _primitiveSupportedAttributes.insert(PrimitiveType::Line, Attribute::LineWidth); + + _primitiveSupportedAttributes.insert(PrimitiveType::Polyline, Attribute::ArrowClampHeight); + _primitiveSupportedAttributes.insert(PrimitiveType::Polyline, Attribute::ArrowHeight); + _primitiveSupportedAttributes.insert(PrimitiveType::Polyline, Attribute::ArrowLineWidth); + _primitiveSupportedAttributes.insert(PrimitiveType::Polyline, Attribute::Arrows); + _primitiveSupportedAttributes.insert(PrimitiveType::Polyline, Attribute::ArrowWidth); + _primitiveSupportedAttributes.insert(PrimitiveType::Polyline, Attribute::Close); + _primitiveSupportedAttributes.insert(PrimitiveType::Polyline, Attribute::Color); + _primitiveSupportedAttributes.insert(PrimitiveType::Polyline, Attribute::Fill); + _primitiveSupportedAttributes.insert(PrimitiveType::Polyline, Attribute::FillColor); + _primitiveSupportedAttributes.insert(PrimitiveType::Polyline, Attribute::FillPattern); + _primitiveSupportedAttributes.insert(PrimitiveType::Polyline, Attribute::LineStyle); + _primitiveSupportedAttributes.insert(PrimitiveType::Polyline, Attribute::LineWidth); + + _primitiveSupportedAttributes.insert(PrimitiveType::Rectangle, Attribute::Center); + _primitiveSupportedAttributes.insert(PrimitiveType::Rectangle, Attribute::Color); + _primitiveSupportedAttributes.insert(PrimitiveType::Rectangle, Attribute::Height); + _primitiveSupportedAttributes.insert(PrimitiveType::Rectangle, Attribute::Width); + _primitiveSupportedAttributes.insert(PrimitiveType::Rectangle, Attribute::Fill); + _primitiveSupportedAttributes.insert(PrimitiveType::Rectangle, Attribute::FillColor); + _primitiveSupportedAttributes.insert(PrimitiveType::Rectangle, Attribute::FillPattern); + _primitiveSupportedAttributes.insert(PrimitiveType::Rectangle, Attribute::LineStyle); + _primitiveSupportedAttributes.insert(PrimitiveType::Rectangle, Attribute::LineWidth); + _primitiveSupportedAttributes.insert(PrimitiveType::Rectangle, Attribute::Rotate); + _primitiveSupportedAttributes.insert(PrimitiveType::Rectangle, Attribute::Rounding); + + _primitiveSupportedAttributes.insert(PrimitiveType::Spline, Attribute::ArrowClampHeight); + _primitiveSupportedAttributes.insert(PrimitiveType::Spline, Attribute::ArrowHeight); + _primitiveSupportedAttributes.insert(PrimitiveType::Spline, Attribute::ArrowLineWidth); + _primitiveSupportedAttributes.insert(PrimitiveType::Spline, Attribute::Arrows); + _primitiveSupportedAttributes.insert(PrimitiveType::Spline, Attribute::ArrowWidth); + _primitiveSupportedAttributes.insert(PrimitiveType::Spline, Attribute::Close); + _primitiveSupportedAttributes.insert(PrimitiveType::Spline, Attribute::Color); + _primitiveSupportedAttributes.insert(PrimitiveType::Spline, Attribute::Fill); + _primitiveSupportedAttributes.insert(PrimitiveType::Spline, Attribute::FillColor); + _primitiveSupportedAttributes.insert(PrimitiveType::Spline, Attribute::FillPattern); + _primitiveSupportedAttributes.insert(PrimitiveType::Spline, Attribute::LineStyle); + _primitiveSupportedAttributes.insert(PrimitiveType::Spline, Attribute::LineWidth); + + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::AbcProtection); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::Affiliation); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::Mobility); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::SymbolId); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::SymbolOffsetX); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::SymbolOffsetY); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::SymbolDirection); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::ForceId); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::SymbolState); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::SymbolUncertainty); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::SymbolExplicitScale); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::DamageForceId); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::Text_EF); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::Text_G); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::Text_H); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::Text_KLNPJ); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::Text_M); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::Text_T); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::Text_V); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::Text_W); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::Text_XY); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::Text_Z); + _primitiveSupportedAttributes.insert(PrimitiveType::Symbol, Attribute::att_vbg_et); + + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::AnchorHorizontal); + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::AnchorVertical); + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::Box); + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::Color); + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::Fill); + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::FillColor); + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::FillPattern); + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::Font); + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::FontBold); + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::FontItalic); + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::FontSize); + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::LineColor); + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::LineStyle); + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::LineWidth); + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::MarginHorizontal); + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::MarginVertical); + _primitiveSupportedAttributes.insert(PrimitiveType::Text, Attribute::Rotate); + + // Init all default-values for all required attributes + QFont defaultFont; + int fontSize = defaultFont.pixelSize(); + + // TODO: Find better defaults + + // "Unknown" is used for plan-attributes + _attributeDefaults.insert(index(PrimitiveType::DictionarySymbol, Attribute::SIDC), QLatin1String("------------")); + + _attributeDefaults.insert(index(PrimitiveType::ParticipantSymbol, Attribute::SIDC), QLatin1String("------------")); + _attributeDefaults.insert(index(PrimitiveType::ParticipantSymbol, Attribute::SIDC_Color), variant2XmlString(QColor(0, 0, 0, 0))); + + _attributeDefaults.insert(index(PrimitiveType::RootNode, Attribute::MetaDataTitle), QString()); + _attributeDefaults.insert(index(PrimitiveType::RootNode, Attribute::MetaDataAuthor), QString()); + _attributeDefaults.insert(index(PrimitiveType::RootNode, Attribute::MetaDataDate), QString()); + _attributeDefaults.insert(index(PrimitiveType::RootNode, Attribute::MetaDataDesciption), QString()); + _attributeDefaults.insert(index(PrimitiveType::RootNode, Attribute::MetaDataMinScale), QString()); + _attributeDefaults.insert(index(PrimitiveType::RootNode, Attribute::MetaDataMaxScale), QString()); + + _attributeDefaults.insert(index(PrimitiveType::Ellipse, Attribute::Center), QString()); + _attributeDefaults.insert(index(PrimitiveType::Ellipse, Attribute::Color), QColor().name()); + _attributeDefaults.insert(index(PrimitiveType::Ellipse, Attribute::Height), QLatin1String("0.0")); + _attributeDefaults.insert(index(PrimitiveType::Ellipse, Attribute::Width), QLatin1String("0.0")); + _attributeDefaults.insert(index(PrimitiveType::Ellipse, Attribute::Extent), QLatin1String("360")); + _attributeDefaults.insert(index(PrimitiveType::Ellipse, Attribute::Fill), QLatin1String("0")); + _attributeDefaults.insert(index(PrimitiveType::Ellipse, Attribute::FillColor), QColor().name()); + _attributeDefaults.insert(index(PrimitiveType::Ellipse, Attribute::FillPattern), QLatin1String("none")); + _attributeDefaults.insert(index(PrimitiveType::Ellipse, Attribute::FillStyle), QLatin1String("pieslice")); + _attributeDefaults.insert(index(PrimitiveType::Ellipse, Attribute::LineStyle), QLatin1String("solid")); + _attributeDefaults.insert(index(PrimitiveType::Ellipse, Attribute::LineWidth), QLatin1String("0.1")); + _attributeDefaults.insert(index(PrimitiveType::Ellipse, Attribute::Rotate), QLatin1String("0")); + _attributeDefaults.insert(index(PrimitiveType::Ellipse, Attribute::Start), QLatin1String("0")); + + _attributeDefaults.insert(index(PrimitiveType::Line, Attribute::ArrowClampHeight), QLatin1String("1.0")); + _attributeDefaults.insert(index(PrimitiveType::Line, Attribute::ArrowHeight), QLatin1String("1.0")); + _attributeDefaults.insert(index(PrimitiveType::Line, Attribute::ArrowLineWidth), QLatin1String("1.0")); + _attributeDefaults.insert(index(PrimitiveType::Line, Attribute::Arrows), QLatin1String("0")); + _attributeDefaults.insert(index(PrimitiveType::Line, Attribute::ArrowWidth), QLatin1String("1.0")); + _attributeDefaults.insert(index(PrimitiveType::Line, Attribute::Color), QColor().name()); + _attributeDefaults.insert(index(PrimitiveType::Line, Attribute::LineStyle), QLatin1String("solid")); + _attributeDefaults.insert(index(PrimitiveType::Line, Attribute::LineWidth), QLatin1String("0.1")); + + _attributeDefaults.insert(index(PrimitiveType::Polyline, Attribute::ArrowClampHeight), QLatin1String("1.0")); + _attributeDefaults.insert(index(PrimitiveType::Polyline, Attribute::ArrowHeight), QLatin1String("1.0")); + _attributeDefaults.insert(index(PrimitiveType::Polyline, Attribute::ArrowLineWidth), QLatin1String("1.0")); + _attributeDefaults.insert(index(PrimitiveType::Polyline, Attribute::Arrows), QLatin1String("0")); + _attributeDefaults.insert(index(PrimitiveType::Polyline, Attribute::ArrowWidth), QLatin1String("1.0")); + _attributeDefaults.insert(index(PrimitiveType::Polyline, Attribute::Close), QLatin1String("1")); + _attributeDefaults.insert(index(PrimitiveType::Polyline, Attribute::Color), QColor().name()); + _attributeDefaults.insert(index(PrimitiveType::Polyline, Attribute::Fill), QLatin1String("0")); + _attributeDefaults.insert(index(PrimitiveType::Polyline, Attribute::FillColor), QColor().name()); + _attributeDefaults.insert(index(PrimitiveType::Polyline, Attribute::FillPattern), QLatin1String("none")); + _attributeDefaults.insert(index(PrimitiveType::Polyline, Attribute::LineStyle), QLatin1String("solid")); + _attributeDefaults.insert(index(PrimitiveType::Polyline, Attribute::LineWidth), QLatin1String("0.1")); + + _attributeDefaults.insert(index(PrimitiveType::Rectangle, Attribute::Center), QString()); + _attributeDefaults.insert(index(PrimitiveType::Rectangle, Attribute::Color), QColor().name()); + _attributeDefaults.insert(index(PrimitiveType::Rectangle, Attribute::Height), QLatin1String("0.0")); + _attributeDefaults.insert(index(PrimitiveType::Rectangle, Attribute::Width), QLatin1String("0.0")); + _attributeDefaults.insert(index(PrimitiveType::Rectangle, Attribute::Fill), QLatin1String("0")); + _attributeDefaults.insert(index(PrimitiveType::Rectangle, Attribute::FillColor), QColor().name()); + _attributeDefaults.insert(index(PrimitiveType::Rectangle, Attribute::FillPattern), QLatin1String("none")); + _attributeDefaults.insert(index(PrimitiveType::Rectangle, Attribute::LineStyle), QLatin1String("solid")); + _attributeDefaults.insert(index(PrimitiveType::Rectangle, Attribute::LineWidth), QLatin1String("0.1")); + _attributeDefaults.insert(index(PrimitiveType::Rectangle, Attribute::Rotate), QLatin1String("0")); + _attributeDefaults.insert(index(PrimitiveType::Rectangle, Attribute::Rounding), QLatin1String("0.0")); + + _attributeDefaults.insert(index(PrimitiveType::Spline, Attribute::ArrowClampHeight), QLatin1String("1.0")); + _attributeDefaults.insert(index(PrimitiveType::Spline, Attribute::ArrowHeight), QLatin1String("1.0")); + _attributeDefaults.insert(index(PrimitiveType::Spline, Attribute::ArrowLineWidth), QLatin1String("1.0")); + _attributeDefaults.insert(index(PrimitiveType::Spline, Attribute::Arrows), QLatin1String("0")); + _attributeDefaults.insert(index(PrimitiveType::Spline, Attribute::ArrowWidth), QLatin1String("1.0")); + _attributeDefaults.insert(index(PrimitiveType::Spline, Attribute::Close), QLatin1String("1")); + _attributeDefaults.insert(index(PrimitiveType::Spline, Attribute::Color), QColor().name()); + _attributeDefaults.insert(index(PrimitiveType::Spline, Attribute::Fill), QLatin1String("0")); + _attributeDefaults.insert(index(PrimitiveType::Spline, Attribute::FillColor), QColor().name()); + _attributeDefaults.insert(index(PrimitiveType::Spline, Attribute::FillPattern), QLatin1String("none")); + _attributeDefaults.insert(index(PrimitiveType::Spline, Attribute::LineStyle), QLatin1String("solid")); + _attributeDefaults.insert(index(PrimitiveType::Spline, Attribute::LineWidth), QLatin1String("0.1")); + + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::AbcProtection), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::Affiliation), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::Mobility), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::SymbolId), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::SymbolOffsetX), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::SymbolOffsetY), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::SymbolDirection), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::ForceId), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::SymbolState), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::SymbolUncertainty), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::SymbolExplicitScale), QLatin1String("1.0")); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::DamageForceId), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::Text_EF), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::Text_G), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::Text_H), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::Text_KLNPJ), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::Text_M), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::Text_T), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::Text_V), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::Text_W), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::Text_XY), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::Text_Z), QString()); + _attributeDefaults.insert(index(PrimitiveType::Symbol, Attribute::att_vbg_et), QString()); + + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::AnchorHorizontal), QLatin1String("center")); + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::AnchorVertical), QLatin1String("center")); + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::Box), QLatin1String("0")); + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::Color), QColor().name()); + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::Fill), QLatin1String("0")); + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::FillColor), QColor().name()); + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::FillPattern), QLatin1String("none")); + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::Font), defaultFont.key()); + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::FontBold), QLatin1String("0")); + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::FontItalic), QLatin1String("0")); + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::FontSize), QString::number(fontSize)); + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::LineColor), QColor().name()); + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::LineStyle), QLatin1String("solid")); + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::LineWidth), QLatin1String("0.1")); + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::MarginHorizontal), QLatin1String("1")); + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::MarginVertical), QLatin1String("1")); + _attributeDefaults.insert(index(PrimitiveType::Text, Attribute::Rotate), QLatin1String("0")); + + _attributeTypes[Attribute::AbcProtection] = QMetaType::Bool; // Type: bool + _attributeTypes[Attribute::Affiliation] = QMetaType::User; // Type: Symbol::t_affil + _attributeTypes[Attribute::Alignment] = QMetaType::User; // Type: HorizontalAlign + _attributeTypes[Attribute::AnchorHorizontal] = QMetaType::User; // Type: HorizontalAlign + _attributeTypes[Attribute::AnchorVertical] = QMetaType::User; // Type: t_valign + _attributeTypes[Attribute::ArrowClampHeight] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::ArrowHeight] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::ArrowLineWidth] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::Arrows] = QMetaType::User; // Type: t_arrows + _attributeTypes[Attribute::ArrowWidth] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::Box] = QMetaType::Bool; // Type: bool + _attributeTypes[Attribute::Center] = QMetaType::QPointF; // Type: PointF + _attributeTypes[Attribute::Close] = QMetaType::Bool; // Type: bool + _attributeTypes[Attribute::Color] = QMetaType::QColor; // Type: QColor + _attributeTypes[Attribute::Extent] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::Fill] = QMetaType::Bool; // Type: bool + _attributeTypes[Attribute::FillColor] = QMetaType::QColor; // Type: QColor + _attributeTypes[Attribute::FillPattern] = QMetaType::User; // Type: t_fillPattern + _attributeTypes[Attribute::FillStyle] = QMetaType::User; // Type: t_fillStyle + _attributeTypes[Attribute::Font] = QMetaType::User; // Type: t_font + _attributeTypes[Attribute::FontBold] = QMetaType::Bool; // Type: bool + _attributeTypes[Attribute::FontItalic] = QMetaType::Bool; // Type: bool + _attributeTypes[Attribute::FontSize] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::LineColor] = QMetaType::QColor; // Type: QColor + _attributeTypes[Attribute::LineStyle] = QMetaType::User; // Type: t_lineStyle + _attributeTypes[Attribute::LineWidth] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::MarginHorizontal] = QMetaType::User; // Type: int + _attributeTypes[Attribute::MarginVertical] = QMetaType::User; // Type: int + _attributeTypes[Attribute::Mobility] = QMetaType::User; // Type: Symbol::t_mob + _attributeTypes[Attribute::SIDC] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::SIDC_Color] = QMetaType::QColor; // Type: QColor + _attributeTypes[Attribute::SIDC_Attributes] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::SymbolId] = QMetaType::Int; // Type: int + _attributeTypes[Attribute::SymbolOffsetX] = QMetaType::Int; // Type: int + _attributeTypes[Attribute::SymbolOffsetY] = QMetaType::Int; // Type: int + _attributeTypes[Attribute::SymbolDirection] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::ForceId] = QMetaType::Int; // Type: int + _attributeTypes[Attribute::SymbolState] = QMetaType::User; // Type: SymbolTypes::t_state + _attributeTypes[Attribute::SymbolUncertainty] = QMetaType::User; // Type: SymbolTypes::t_uncertainty + _attributeTypes[Attribute::SymbolExplicitScale] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::DamageForceId] = QMetaType::Int; // Type: int + _attributeTypes[Attribute::Injury] = QMetaType::Int; // Type: int + _attributeTypes[Attribute::PeMapBrightness] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::PeMapCenterX] = QMetaType::Int; // Type: int + _attributeTypes[Attribute::PeMapCenterY] = QMetaType::Int; // Type: int + _attributeTypes[Attribute::PeMapSaturation] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::PeMapZoom] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::PeMapRotation] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::PeMapType] = QMetaType::User; // Type: t_mapType + _attributeTypes[Attribute::PeShowUtm] = QMetaType::Bool; // Type: bool + + _attributeTypes[Attribute::PeSymbolOpacity] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::PeSymbolFillType] = QMetaType::User; // Type: SymbolTypes::t_filltype + _attributeTypes[Attribute::PeSymbolScale] = QMetaType::Bool; // Type: bool + + _attributeTypes[Attribute::MetaDataTitle] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::MetaDataAuthor] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::MetaDataDate] = QMetaType::QDateTime; + _attributeTypes[Attribute::MetaDataDesciption] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::MetaDataMinScale] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::MetaDataMaxScale] = QMetaType::Double; // Type: double + + _attributeTypes[Attribute::Rotate] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::Rounding] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::Start] = QMetaType::Double; // Type: double + _attributeTypes[Attribute::Text_EF] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::Text_G] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::Text_H] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::Text_KLNPJ] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::Text_M] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::Text_T] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::Text_V] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::Text_W] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::Text_XY] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::Text_Z] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::UtmEast] = QMetaType::Int; // Type: int + _attributeTypes[Attribute::UtmGrid] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::UtmNorth] = QMetaType::Int; // Type: int + _attributeTypes[Attribute::UtmZone] = QMetaType::QString; // Type: QString + _attributeTypes[Attribute::att_vbg_et] = QMetaType::User; // Type: t_w_vbg_et + + _staticInitDone = true; + } + + QDomElement root = createElement(rootNodeName); + appendChild(root); + + clearUndoStack(); +} // void GeometryModelQDom::init() + +void GeometryModelQDom::cleanupStaticMember() +{ + _primitiveTagsTypes.clear(); + _primitiveTagsStrings.clear(); + _primitiveSupportedAttributes.clear(); + _attributeDefaults.clear(); + _attributeTypes.clear(); +} + + +GeometryModel::Node GeometryModelQDom::getMapDefaultSettingsNode() const +{ + QDomElement mapSettings = firstChildElement(mapSettingsName); + + if (mapSettings.isNull()) + { + GeometryModelQDom *THIS = const_cast(this); // cast const away + + mapSettings = THIS->createElement(mapSettingsName); + + THIS->appendChild(mapSettings); + + using index = QPair; + + _attributeDefaults.insert(index(PrimitiveType::MapDefaults, Attribute::PeMapBrightness), QLatin1String("100")); + _attributeDefaults.insert(index(PrimitiveType::MapDefaults, Attribute::PeMapCenterX), QLatin1String("0.0")); + _attributeDefaults.insert(index(PrimitiveType::MapDefaults, Attribute::PeMapCenterY), QLatin1String("0.0")); + _attributeDefaults.insert(index(PrimitiveType::MapDefaults, Attribute::PeMapSaturation), QLatin1String("100")); + _attributeDefaults.insert(index(PrimitiveType::MapDefaults, Attribute::PeMapZoom), QLatin1String("1.0")); + _attributeDefaults.insert(index(PrimitiveType::MapDefaults, Attribute::PeMapRotation), QLatin1String("0.0")); + _attributeDefaults.insert(index(PrimitiveType::MapDefaults, Attribute::PeMapType), QLatin1String("MapPrimitiveType::Unknown")); + _attributeDefaults.insert(index(PrimitiveType::MapDefaults, Attribute::PeShowUtm), QLatin1String("1")); + _attributeDefaults.insert(index(PrimitiveType::MapDefaults, Attribute::UtmEast), QLatin1String("10000")); + _attributeDefaults.insert(index(PrimitiveType::MapDefaults, Attribute::UtmGrid), QLatin1String("XY")); + _attributeDefaults.insert(index(PrimitiveType::MapDefaults, Attribute::UtmNorth), QLatin1String("10000")); + _attributeDefaults.insert(index(PrimitiveType::MapDefaults, Attribute::UtmZone), QLatin1String("00Z")); + + _attributeDefaults.insert(index(PrimitiveType::MapDefaults, Attribute::PeSymbolOpacity), QLatin1String("1.0")); + _attributeDefaults.insert(index(PrimitiveType::MapDefaults, Attribute::PeSymbolFillType), QLatin1String("filled")); + _attributeDefaults.insert(index(PrimitiveType::MapDefaults, Attribute::PeSymbolScale), QLatin1String("1.0")); + + // Create root-node and set required attributes + + + QList modelAttributes = supportedAttributes(PrimitiveType::MapDefaults); + for (int i = 0; i < modelAttributes.size(); ++i) + { + Attribute at = modelAttributes.at(i); + THIS->setModelEditorAttribute(at, _attributeDefaults[index(PrimitiveType::MapDefaults, at)]); + } + } + + return Node(Node::Implementation(mapSettings)); + +} // GeometryModelQDom::getMapDefaultSettingsNode + + +void GeometryModelQDom::transformNode(const QDomNode &node, const QTransform &t) +{ + QString tag = node.nodeName(); + if (!_primitiveTagsTypes.contains(tag)) + { + emit modelError(tr("Can not translate node of type %1").arg(tag)); + return; + } + + QDomNodeList nodes = node.childNodes(); + for (int i = 0; i < nodes.size(); ++i) + { + QDomElement p = nodes.at(i).toElement(); + if (p.nodeName() != QLatin1String("point")) + continue; + if (!p.hasAttribute(QLatin1String("x")) || !p.hasAttribute(QLatin1String("y"))) + emit modelError(tr("Invalid point in primitive of type %1").arg(tag)); + else + { + bool okx, oky; + double oldx = p.toElement().attribute(QLatin1String("x")).toDouble(&okx); + double oldy = p.toElement().attribute(QLatin1String("y")).toDouble(&oky); + if (!(okx && oky)) + emit modelError(tr("Invalid point in primitive of type %1").arg(tag)); + else + { + QPointF point = t.map(QPointF(oldx, oldy)); + p.setAttribute(QLatin1String("x"), QString::number(point.x(), 'f', CoordinateDoublePrecision)); + p.setAttribute(QLatin1String("y"), QString::number(point.y(), 'f', CoordinateDoublePrecision)); + } + } + } +} // void GeometryModelQDom::transformNode(QDomNode node, QTransform t) + + +void GeometryModelQDom::emitChangedSignal(const Node &node, Attribute att, bool coordsChanged) +{ + if (!_skipChangedSignal) + emit changed(node, att, coordsChanged); +} // void GeometryModelQDom::emitChangedSignal(QDomNode node, Attribute att, bool coordsChanged) + + +void GeometryModelQDom::pushUndo(const QString &eventDescription) +{ + if (_undoEnabled && (! _insideUndoBlock)) + { + QDomElement root = firstChildElement(rootNodeName); + while (undoSteps() >= _maxUndoSteps) + { + // Size-limit reached, remove first element + _undoStack.erase(_undoStack.begin()); + _undoHistory.erase(_undoHistory.begin()); + } + _undoStack.push(root.cloneNode()); + _undoHistory.push(eventDescription); + } + if (_insideUndoBlock) + _undoHistoryBlock = eventDescription; +} // void GeometryModelQDom::pushUndo(const QString &eventDescription) + + +GeometryModel::Node GeometryModelQDom::popUndo() +{ + if (_undoEnabled && (! _insideUndoBlock)) + { + _undoHistory.pop(); + return Node(Node::Implementation(_undoStack.pop())); + } + return Node(); +} // void GeometryModelQDom::pushUndo() + + + +bool GeometryModelQDom::parseSymbolAttributeString(const QString &sidcAttributes, QMap &attrMap) const +{ + attrMap.clear(); + + if (sidcAttributes.isEmpty()) return true; + + bool ok = true; + const QStringList sidcAttrList = sidcAttributes.split(QLatin1Char(';')); + + for (const QString &attr : sidcAttrList) + { + const QStringList &attrPair = attr.split(QLatin1Char('=')); + if (attrPair.size() != 2) + { + ok = false; + QString errorString = tr("%1 Parsing symbol attribute list failed. %2").arg(QString::fromStdString(LOGFUNCTION), attr); + emit const_cast(this)->attributeError(errorString); + continue; + } + attrMap[attrPair.first()] = attrPair.last(); + } + return ok; +} // bool GeometryModelQDom::parseSymbolAttributeString( const QString & sidcAttributes, QMap & attrMap ) const + + +QString GeometryModelQDom::buildSymbolAttributeString(const QMap &attrMap) const +{ + QString attributeString; + + QMap::const_iterator iter = attrMap.cbegin(); + while (attrMap.cend() != iter) + { + const QString &key = iter.key(); + const QString &val = iter.value().toString(); + + if (!val.isEmpty()) + { + if (key.isEmpty()) + { + QString errorString = tr("%1 Empty key found").arg(QString::fromStdString(LOGFUNCTION)); + emit const_cast(this)->attributeError(errorString); + ++iter; + continue; + } + + if (val.contains(QLatin1Char('=')) || val.contains(QLatin1Char(';'))) + { + QString errorString = tr("%1 Attribute value has illegal characters <;=> : %2").arg(QString::fromStdString(LOGFUNCTION), val); + emit const_cast(this)->attributeError(errorString); + ++iter; + continue; + } + + if (!attributeString.isEmpty()) attributeString += QLatin1Char(';'); + attributeString += QStringLiteral("%1=%2").arg(key, val); + } + ++iter; + } + return attributeString; +} // QString GeometryModelQDom::buildSymbolAttributeString(const QMap & attrMap) const + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model_q_dom.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model_q_dom.h new file mode 100755 index 000000000..71a73140a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_model_q_dom.h @@ -0,0 +1,374 @@ +/** +** +** @RDE_HEADER@ +** GeometryModel implementation to write and read PlanEditor plans from/to XML and manipulate DOM-tree. +** $Id$ +** $HeadURL$ +** $Author$ +** +** ----------------------------------------------------------------------------------------------- +** +** Hardware-Environment: Intel-PC +** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5 +** +** Language ........: C++ 11 +** +** ----------------------------------------------------------------------------------------------- +** +** Author ...........: Stephan Kuhagen +** Jörg Turla, SPD G, Tel. 3132 +** +** ----------------------------------------------------------------------------------------------- +** +** Notes .........: +** +** ??.??.200?, SK : First edition for use with Qt4 +** 11.11.2015, JT : Splitting interface and implementation +** +** ----------------------------------------------------------------------------------------------- +*/ + +// clazy:excludeall=const-signal-or-slot + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "geometry_model.h" +class QTransform; + +namespace SituationMap +{ + +// exported for unit test only +class GEOMETRYMODEL_EXPORT GeometryModelQDom : public GeometryModel, public QDomDocument +{ + + Q_OBJECT + +public: + + explicit GeometryModelQDom(QObject *parent = nullptr); + + //! Copy-Constructor + GeometryModelQDom(const GeometryModelQDom &other); + + ~GeometryModelQDom(); + + GeometryModelQDom & operator = (const GeometryModelQDom &other); + + QByteArray toByteArray() const override; + QByteArray toByteArrayDebug() const override; + + bool fromByteArray(const QByteArray &content) override; + bool fromDevice(QIODevice *device) override; + bool fromByteArrayDebug(const QByteArray &content) override; + + bool isEmpty() const override; + + Node getRootNode() const override; + + Node getMapDefaultSettingsNode() const override; + + void setModelAttribute(GeometryModel::Attribute attribute, const QVariant &value) override; + + void setModelEditorAttribute(GeometryModel::Attribute attribute, const QVariant &value) override; + + void setPrimitiveAttribute(const GeometryModel::Node &node, GeometryModel::Attribute attribute, const QVariant &value) override; + + void movePrimitiveIntoGroup(const GeometryModel::Node &primitive, const GeometryModel::Node &target) override; + + void releaseGroup(const GeometryModel::Node &group) override; + + void deleteGroup(const GeometryModel::Node &group) override; + + void removePrimitive(const GeometryModel::Node &primitive) override; + + void removePrimitivesByUserData(const QList &userDataList) override; + + void removeText(GeometryModel::Node &parent) override; + + void setTextData(const GeometryModel::Node &textNode, const QString &data, GeometryModel::HorizontalAlign textAlign) override; + + void clearModel() override; + + void translateNode(const GeometryModel::Node &node, double x, double y) override; + + void moveNode(const GeometryModel::Node &node, double x, double y) override; + + void scaleNode(const GeometryModel::Node &node, double scaleX, double scaleY) override; + + + void rotateNode(const GeometryModel::Node &node, double angle) override; + + void reorderPrimitives(const QList &nodes, GeometryModel::PrimitivOrder order) override; + + void merge(const GeometryModel::Node &other, bool skipUndo = false, bool intoNewGroup = false) override; + + void merge(GeometryModel *other, bool skipUndo = false, bool intoNewGroup = false) override; + + unsigned int modificationCounter() const override; + + void resetModificationCounter() override; + + void undo(unsigned int steps = 1) override; + + void redo(unsigned int steps = 1) override; + + void enableUndo(bool enable) override; + + void startUndoBlock() override; + + void endUndoBlock(const QString &eventDescription = QString()) override; + + void abortUndoBlock() override; + + void setMaxUndoSteps(unsigned int max) override; + + unsigned int maxUndoSteps() const override; + + void cleanModel() override; + + + GeometryModel::Node addPrimitive(GeometryModel::PrimitiveType type + , const QList &points + , const QList > &attributes + , int userData = -1 + , const QString &textData = QString() + , GeometryModel::HorizontalAlign textAlign = HorizontalAlign::Left) override; + + + bool setPrimitivePoints(const GeometryModel::Node &node, const QList &points) override; + + GeometryModel::Node addText(const GeometryModel::Node &parent + , const QString &textData + , GeometryModel::HorizontalAlign textAlign + , const QList > &attributes + , int userData) override; + + GeometryModel::Node getText(const GeometryModel::Node &parent) const override; + + QString getTextData(const GeometryModel::Node &textNode) const override; + + QList getPrimitivePoints(const GeometryModel::Node &node) const override; + + QRectF getBoundingBox(const GeometryModel::Node &node) const override; + + void setNodeDisplayName(const Node &node, const QString &name) override; + + QString getNodeDisplayName(const Node &node) const override; + + QVariant getModelAttribute(Attribute attribute) const override; + + QVariant getAttribute(const GeometryModel::Node &node, GeometryModel::Attribute attribute) const override; + + template T getAttribute(const Node &node, Attribute attribute) const; + + bool hasAttribute(const GeometryModel::Node &node, GeometryModel::Attribute attribute) const override; + + QMetaType::Type attributeType(const GeometryModel::Attribute &attribute) const override; + + QList supportedAttributes(GeometryModel::PrimitiveType type) const override; + + GeometryModel::Node createGroup(const QList &primitives) override; + + GeometryModel::Node isInGroup(const GeometryModel::Node &primitive) const override; + + GeometryModel::NodeList getGroupPrimitives(const GeometryModel::Node &node) override; + + GeometryModel::Node findPrimitive(int userData) const override; + + bool hasUserData(const Node &node) const override; + + int getUserData(const GeometryModel::Node &node, bool *ok = nullptr) const override; + + void setUserData(const GeometryModel::Node &node, int userData) override; + + QMultiMap userDataMap(const GeometryModel::Node &node = GeometryModel::Node()) override; + + GeometryModel::Node copy(const GeometryModel::Node &node, int userData = 0) override; + + GeometryModel::PrimitiveType getType(const GeometryModel::Node &node) const override; + + unsigned int undoSteps() const override; + + unsigned int redoSteps() const override; + + QStringList undoHistory() const override { return _undoHistory.toList(); } + + QStringList redoHistory() const override { return _redoHistory.toList(); } + + void clearUndoStack() override + { + _undoStack.clear(); + _undoHistory.clear(); + } // clearUndoStack + + void clearRedoStack() override + { + _redoStack.clear(); + _redoHistory.clear(); + } // clearRedoStack + + QString attributeToString(const GeometryModel::Attribute &attribute) const override; + + bool parseSymbolAttributeString(const QString &sidcAttributes, QMap &attrMap) const override; + + QString buildSymbolAttributeString(const QMap &attrMap) const override; + + static void cleanupStaticMember(); + +private: + + static void valueFromVariant(const QVariant &v, QPointF &result); + static void valueFromVariant(const QVariant &v, QColor &result); + + /** + convert to QString (with non locale-aware conversions) + */ + static QString variant2XmlString(const QVariant &v); + + /** + Create a QColor from an attribute-value with alpha-channel (QVariant + ignores Alpha) + \param attColor: The QString-QVariant from an attribute of Type QColor + \return QColor with alpha-channel or invalid QColor, if the value could be parsed. + */ + static QColor colorFromString(const QString &attColor); + + /** + Create a QPoint from an attribute-value + \param attPoint: The QString-QVariant from an attribute of Type QPointF + \return QPointF + */ + static QPointF pointFromString(const QString &attPoint); + + //! A mapping between all primitives and the attributes they support + static QMultiMap _primitiveSupportedAttributes; + //! Mappings between all primitive-types and their string/tag-name + static QMap _primitiveTagsTypes; + static QMap _primitiveTagsStrings; + + //! Default-Values for all supported attributes + static QMap, QString> _attributeDefaults; + + //! Mapping between Attributes and their QVariant-Type. + static QMap _attributeTypes; + + //! Bool to check, if static class variables have been initialized + static bool _staticInitDone; + + /** + Init data of the object + */ + void init(); + + + /** + Transform the points of a primitive. + \sa {translateNode(), moveNode(), rotateNode(), scaleNode()} + \param node: The primitive to transform. + \param t: The transformation-matrix. + */ + void transformNode(const QDomNode &node, const QTransform &t); + + + GeometryModel::PrimitiveType getType(const QDomNode &node) const; + + bool hasUserData(const QDomNode &node) const; + + int getUserData(const QDomNode &node, bool *okPtr) const; + + QRectF getBoundingBox(const QDomNode &node) const; + + QList getPrimitivePoints(const QDomNode &node) const; + + bool hasAttribute(const QDomNode &node, GeometryModel::Attribute attribute) const; + + QVariant getAttribute(const QDomNode &node, GeometryModel::Attribute attribute) const; + + void removePrimitive(const QDomNode &primitive); + + void translateNode(const QDomNode &node, double x, double y); + + void moveNode(const QDomNode &node, double x, double y); + + void scaleNode(const QDomNode &node, double scaleX, double scaleY); + + void rotateNode(const QDomNode &node, double angle); + + void merge(const QDomNode &other, bool skipUndo = false, bool intoNewGroup = false); + + QDomNode createGroup(const QList &primitives); + + QMultiMap userDataMap(const QDomNode &node) const; + + + /** + Wrapper for signal changed(), checks _skipChangedSignal + + !!! ATTENTION: Do not use the signal changed directly. + Use emitChangedSignal() instead !!! + + \param what: Short description of the change for logging + \param node: the changed node (if this is a Null-Node, the Root-Node of + the plan has changed) + \param att: The attribute, that changed. If att==MaxAttributes, then + multiple attributes are changed + \param coordsChanged: if true, the coordinated of the primitive are changed + */ + void emitChangedSignal(const Node &node, Attribute att, bool coordsChanged); + + + //! Lock for temporarily blocking changed()-Signals + bool _skipChangedSignal; + + //! Undo-Stack + bool _undoEnabled; + unsigned int _maxUndoSteps; + QStack _undoStack; + QStack _undoHistory; + QStack _redoStack; + QStack _redoHistory; + + //! Undo-blocks + QDomNode _undoBlock; + QString _undoHistoryBlock; + bool _insideUndoBlock; + unsigned int _modificationCounter; + + //! Push/pop current state to undo-stack, if not locked + void pushUndo(const QString &eventDescription = QString()); + GeometryModel::Node popUndo(); +}; // GeometryModelQDom + + +template T GeometryModelQDom::getAttribute(const Node &node, Attribute attribute) const +{ + const QVariant &v = getAttribute(node, attribute); + + switch (qMetaTypeId()) + { + case QMetaType::QPointF: + case QMetaType::QColor: + { + T result; valueFromVariant(v, result); return result; + } break; + + default: break; + } + return v.value(); +} // template T GeometryModelQDom::getAttribute( const Node & node, Attribute attribute ) const + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_node_visitor.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_node_visitor.cpp new file mode 100755 index 000000000..f5b0d0944 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_node_visitor.cpp @@ -0,0 +1,75 @@ +/** +** +** @RDE_HEADER@ +** Node visitor for GeometryModel. +** $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 .........: +** +** 07.07.2016, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "geometry_node_visitor.h" + +namespace SituationMap +{ +GeometryNodeVisitor::GeometryNodeVisitor(GeometryModel *geoModel) : m_geometryModel(geoModel) {;} + +bool GeometryNodeVisitor::traverse() +{ + if (!m_geometryModel) + return true; + return traverse(m_geometryModel->getRootNode()); +} // bool GeometryNodeVisitor::traverse() + + +bool GeometryNodeVisitor::traverse(GeometryModel::Node node) +{ + if (!m_geometryModel) + return true; + + if (node.isValid()) + { + const GeometryModel::PrimitiveType primitiveType = m_geometryModel->getType(node); + + if ((GeometryModel::PrimitiveType::RootNode == primitiveType) || (GeometryModel::PrimitiveType::Node == primitiveType)) + { + if (node.hasChildNodes()) + { + node = node.firstChildNode(); + while (node.isValid()) + { + if (!traverse(node)) + return false; + node = node.next(); + } + } + } + + if (!apply(m_geometryModel, node, primitiveType)) + return false; + + } // if + + return true; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_node_visitor.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_node_visitor.h new file mode 100755 index 000000000..9dbcebbca --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_node_visitor.h @@ -0,0 +1,75 @@ +/** +** +** @RDE_HEADER@ +** Node visitor for GeometryModel. +** $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 .........: +** +** 07.07.2016, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "geometry_model.h" + +namespace SituationMap +{ + +/** + GeometryNopdeVisitor implements visitor pattern to traverse geoemtry model tree. The apply function will be + called for each node in geometry model. +*/ +class GEOMETRYMODEL_EXPORT GeometryNodeVisitor +{ +public: + + explicit GeometryNodeVisitor(GeometryModel *geoModel); + virtual ~GeometryNodeVisitor() = default; + GeometryNodeVisitor(const GeometryNodeVisitor &) = delete; + + /** + Traversing the complete geometry model + */ + bool traverse(); + + /** + Traversing the sub-tree of node. Coming in deepth first. + */ + bool traverse(GeometryModel::Node node); + +protected: + + /** + For each node the apply function will be called. If apply returns false the traversal is aborted. In + other case if return value is true the traversel is continued. + + @param geoModel Pointer to geometry model + @param node Node of geometry model + @param nodeType Type of node + @return false to abort traversael otherwise true + */ + virtual bool apply(GeometryModel *geoModel, const GeometryModel::Node &node, GeometryModel::PrimitiveType nodeType) = 0; + + GeometryModel *m_geometryModel; + +}; // GeometryNodeVisitor + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_pixelTextBox.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_pixelTextBox.cpp new file mode 100755 index 000000000..15ee116f7 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_pixelTextBox.cpp @@ -0,0 +1,216 @@ +/** + * @RDE_HEADER@ + * PixelTextBoxData as Addon to GeometryModel + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Norbert Thoden, SLSE, 2023-04-27 + */ + +#include "geometry_pixelTextBox.h" +#include +#include +#include +#include + +#include +#include + +namespace SituationMap +{ + +PixelTextBoxData::PixelTextBoxData(const QString &htmlText, const QColor &backgroundColor) + : m_htmlText(htmlText), m_backgroundColor(backgroundColor) +{ +} + +void PixelTextBoxData::reset() +{ + m_htmlText.clear(); + m_backgroundColor = QColor(); + m_position = QPoint(); + m_size = QSize(); +} + +bool PixelTextBoxData::isEmpty() const +{ + return m_htmlText.isEmpty(); +} + +QString PixelTextBoxData::toDebugString() const +{ + QJsonDocument jDoc(toJson()); + return QString::fromStdString(jDoc.toJson(QJsonDocument::JsonFormat::Compact).toStdString()); +} + +bool PixelTextBoxData::operator==(const PixelTextBoxData &o) const +{ + return m_htmlText == o.m_htmlText && + m_backgroundColor == o.m_backgroundColor && + m_position == o.m_position && + m_size == o.m_size; +} + + +QJsonObject PixelTextBoxData::toJson() const +{ + QJsonObject obj; + obj[QStringLiteral("text")] = m_htmlText; + obj[QStringLiteral("backgroundColor")] = m_backgroundColor.name(); + + obj[QStringLiteral("position")] = QJsonObject({{QStringLiteral("x"), m_position.x()}, + {QStringLiteral("y"), m_position.y()}, + }); + + obj[QStringLiteral("size")] = QJsonObject({{QStringLiteral("width"), m_size.width()}, + {QStringLiteral("height"), m_size.height()}, + }); + return obj; +} + + +bool get(const QJsonObject &obj, const QString &key, int &val) +{ + const auto jVal = obj[key]; + if (!jVal.isDouble()) + { + qCritical() << " isDouble failed"; + } + val = jVal.toInt(); + return true; +} + + +PixelTextBoxData PixelTextBoxData::fromJson(const QJsonValue &json) +{ + if (json.isUndefined()) + return {}; + if (!json.isObject()) + return {}; + + PixelTextBoxData ret; + + // text + ret.m_htmlText = json[QStringLiteral("text")].toString(); + + // backgroundColor + QString temp = json[QStringLiteral("backgroundColor")].toString(); + ret.m_backgroundColor = QColor::fromString(temp); + + // position: + { + const auto jVal = json[QStringLiteral("position")]; + get(jVal.toObject(), QStringLiteral("x"), ret.m_position.rx()); + get(jVal.toObject(), QStringLiteral("y"), ret.m_position.ry()); + } + + // size: + { + const auto jVal = json[QStringLiteral("size")]; + get(jVal.toObject(), QStringLiteral("width"), ret.m_size.rwidth()); + get(jVal.toObject(), QStringLiteral("height"), ret.m_size.rheight()); + } + + return ret; +} + +QString PixelTextBoxData::toJsonStr(QJsonDocument::JsonFormat format) const +{ + QJsonObject obj = toJson(); + QJsonDocument jDoc(obj); + return QString::fromStdString(jDoc.toJson(format).toStdString()); +} + +PixelTextBoxData PixelTextBoxData::fromJsonStr(const QString &jsonStr) +{ + if (jsonStr.isEmpty()) + return {}; + + QJsonParseError parseError; + QJsonDocument jDoc(QJsonDocument::fromJson(jsonStr.toStdString().c_str(), &parseError)); + + if (parseError.error != QJsonParseError::NoError) + { + qCritical() << " QJsonParseError->errorString: '" << parseError.errorString() << "'" + << ", offset: " << parseError.offset; + return {}; + } + + QJsonObject jObj = jDoc.object(); + if (jObj.isEmpty()) + return {}; + + return PixelTextBoxData::fromJson(jObj); +} + +// +// Container +// + +QJsonObject PixelTextBoxData::pixelTextBoxContainerToJson(const QVector &ptbdCont) +{ + QJsonArray arr; + for (const PixelTextBoxData &item : std::as_const(ptbdCont)) + arr.push_back(item.toJson()); + + QJsonObject jObj; + jObj[QStringLiteral("PixelTextBoxData")] = arr; + return jObj; +} +QVector PixelTextBoxData::pixelTextBoxContainerFromJson(const QJsonValue &json) +{ + QVector ret; + + if (!json.isObject()) + return ret; + + const QJsonObject jObj = json.toObject(); + if (!jObj.contains(QStringLiteral("PixelTextBoxData"))) + return ret; + + const QJsonArray &array = jObj[QStringLiteral("PixelTextBoxData")].toArray(); + ret.reserve(array.size()); + for(int i = 0; i < array.size(); ++i) + ret.push_back(PixelTextBoxData::fromJson(array.at(i))); + return ret; +} + +QString PixelTextBoxData::pixelTextBoxContainerToJsonStr(const QVector &ptbdCont, QJsonDocument::JsonFormat format) +{ + QJsonObject obj = pixelTextBoxContainerToJson(ptbdCont); + QJsonDocument jDoc(obj); + return QString::fromStdString(jDoc.toJson(format).toStdString()); +} +QVector PixelTextBoxData::pixelTextBoxContainerFromJsonStr(const QString &jsonStr) +{ + if (jsonStr.isEmpty()) + return {}; + + QJsonParseError parseError; + QJsonDocument jDoc(QJsonDocument::fromJson(jsonStr.toStdString().c_str(), &parseError)); + + if (parseError.error != QJsonParseError::NoError) + { + qCritical() << " QJsonParseError->errorString: '" << parseError.errorString() << "'" + << ", offset: " << parseError.offset; + return {}; + } + + QJsonObject jObj = jDoc.object(); + if (jObj.isEmpty()) + return {}; + + return PixelTextBoxData::pixelTextBoxContainerFromJson(jObj); +} + +//----------------------------------------------------------------------------- +// output operator: +//----------------------------------------------------------------------------- +GEOMETRYMODEL_EXPORT std::ostream &operator<<(std::ostream &stream, const PixelTextBoxData &value) +{ + stream << value.toDebugString().toStdString().c_str(); + return stream; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_pixelTextBox.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_pixelTextBox.h new file mode 100755 index 000000000..a1484a6ce --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GeometryModel/geometry_pixelTextBox.h @@ -0,0 +1,69 @@ +/** + * @RDE_HEADER@ + * PixelTextBoxData as Addon to GeometryModel + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Norbert Thoden, SLSE, 2023-04-27 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "geometry_model_export.h" + +namespace SituationMap +{ + +class GEOMETRYMODEL_EXPORT PixelTextBoxData +{ +public: + PixelTextBoxData() = default; + explicit PixelTextBoxData(const QString &htmlText, const QColor &backgroundColor); + + void reset(); + bool isEmpty() const; + + QString toDebugString() const; + + QString m_htmlText; + QColor m_backgroundColor; + QPoint m_position; + QSize m_size; + + // operator + bool operator==(const PixelTextBoxData &o) const; + + + // Json: + QJsonObject toJson() const; + static PixelTextBoxData fromJson(const QJsonValue &json); + + QString toJsonStr(QJsonDocument::JsonFormat format = QJsonDocument::JsonFormat::Indented) const; + static PixelTextBoxData fromJsonStr(const QString &jsonStr); + + static QJsonObject pixelTextBoxContainerToJson(const QVector &ptbdCont); + static QVector pixelTextBoxContainerFromJson(const QJsonValue &json); + + static QString pixelTextBoxContainerToJsonStr(const QVector &ptbdCont, QJsonDocument::JsonFormat format = QJsonDocument::JsonFormat::Indented); + static QVector pixelTextBoxContainerFromJsonStr(const QString &jsonStr); + +}; + +//----------------------------------------------------------------------------- +// output operator: +//----------------------------------------------------------------------------- +GEOMETRYMODEL_EXPORT std::ostream &operator<< (std::ostream &stream, + const PixelTextBoxData &value); + + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/abstract_graphical_object.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/abstract_graphical_object.cpp new file mode 100755 index 000000000..d2ebd3d64 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/abstract_graphical_object.cpp @@ -0,0 +1,556 @@ +/** + * @RDE_HEADER@ + * AbstractGraphicalObject + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "abstract_graphical_object.h" +#include // operator<< + + +// ESRI +#include +#include +#include +#include + + +// Qt +#include +#include +#include +#include +#include + + +namespace SituationMap +{ + +AbstractGraphicalObject::AbstractGraphicalObject(const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId) + : m_geoView(geoView) + , m_graphicsLayer(graphicsLayer) + , m_uniqueMapId(uniqueMapId) + , m_debugInfo(debugInfo) + , m_logger(getLogger("AbstractGraphicalObject")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + + m_pickable = (m_uniqueMapId != INVALID_MAP_ID); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +AbstractGraphicalObject::~AbstractGraphicalObject() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << ", m_graphicsLayer-overlayId=" << (m_graphicsLayer ? m_graphicsLayer->overlayId() : QString(QStringLiteral("unknown"))); + +#if ACRGIS_VERSION >= 0x640F00 // 100.15.0 + // removeOne ist helpful to let ModuleTest succeed: 'situationMapModuleTest checkOverlayHandler3 none' + if (m_graphic && m_graphicsLayer && m_graphicsLayer->graphics()) + m_graphicsLayer->graphics()->removeOne(m_graphic); + delete m_graphic; + m_graphic = nullptr; +#else + +/* + // cleanup + // to make sure, the the graphic is removed from internal container BEFORE + // graphic is deleted, follow the documentation: use Layer::removeGraphic + // (instead 'delete m_graphic') + if (m_graphic && m_graphicsLayer && m_graphicsLayer->graphics()) + m_graphicsLayer->graphics()->removeOne(m_graphic); + + if (m_graphic) + { + delete m_graphic; + m_graphic = nullptr; + } +*/ + + // Race condition between removeOne() and delete m_graphic : + // BUG-000116867 is for the deadlock + // BUG-000117464 is for the crash + // Use following workaround until ESRI will release the fix (with 100.5!?) + if (m_graphic && m_graphicsLayer && m_graphicsLayer->graphics()) + { + const int idx = m_graphicsLayer->graphics()->indexOf(m_graphic); + + if (idx < 0) + ERROR_LOG(m_logger) << LOGFUNCTION << " indexOf failed, debugInfo='" << m_debugInfo << "'"; + + auto graphicCopy = m_graphic; + + QObject::connect (m_graphicsLayer->graphics(), &Esri::ArcGISRuntime::GraphicListModel::graphicRemoved, + m_graphic, [idx, graphicCopy](int index) + { + if (idx == index) + delete graphicCopy; + }); + m_graphicsLayer->graphics()->removeOne(m_graphic); + } + // : Race condition between removeOne() and delete m_graphic + + // m_esriSimpleLineSymbol AND m_esriSimpleFillSymbol will be destroyed m_graphic + +#endif // ACRGIS_VERSION >= 0x640F00 + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +Esri::ArcGISRuntime::Graphic *AbstractGraphicalObject::graphic() +{ + return m_graphic; +} + +Esri::ArcGISRuntime::GraphicsOverlay *AbstractGraphicalObject::graphicsLayer() +{ + return m_graphicsLayer; +} + +Esri::ArcGISRuntime::Geometry AbstractGraphicalObject::getGraphicGeometry() const +{ + if (! m_graphic) + return Esri::ArcGISRuntime::Geometry(); + return m_graphic->geometry(); +} + +bool AbstractGraphicalObject::updateView() +{ + if (!changed()) + return true; + + bool success = updateViewImpl(); + resetChanged(); + return success; +} + + + +QString AbstractGraphicalObject::debugInfo() const +{ + return m_debugInfo; +} +void AbstractGraphicalObject::setDebugInfo(const QString &debugInfo) +{ + m_debugInfo = debugInfo; +} + +int AbstractGraphicalObject::drawOrder() const +{ + return m_drawOrder.value(); +} +void AbstractGraphicalObject::setDrawOrder(int drawOrder) +{ + m_drawOrder = drawOrder; + // if (m_graphic) + // m_graphic->setDrawOrder(m_drawOrder.value()); +} + + +QString AbstractGraphicalObject::toString() const +{ + QString str; + + if (!m_debugInfo.isEmpty()) + str.append(QStringLiteral("debugInfo=%1").arg(m_debugInfo)); + if (m_graphicsLayer) + str.append(QStringLiteral(" layer='%1'").arg(m_graphicsLayer->objectName())); + + str.append(QStringLiteral(" m_drawOrder='%1'").arg(m_drawOrder.value())); + + if (m_graphic) + str.append(QStringLiteral(" zIndex='%1'").arg(m_graphic->zIndex())); + + str.append(QStringLiteral(" visibility='%1'").arg(m_visibility.value())); + str.append(QStringLiteral(" selected='%1'").arg(m_selected.value())); + + str.append(QStringLiteral(" scale='%1'").arg(m_scale.value())); + str.append(QStringLiteral(" positions.count='%1'").arg(m_positions.value().count())); + + return str; +} + +UniqueMapId AbstractGraphicalObject::uniqueMapId() const +{ + return m_uniqueMapId; +} + +void AbstractGraphicalObject::setUniqueMapId(UniqueMapId uniqueId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " uniqueMapId=" << uniqueId; + + m_uniqueMapId = uniqueId; + + // use attribute to store application obj id (SimWorld) at graphic + if (m_graphic) + { + // use attributeModel to store application obj id (SimWorld) at graphic + Esri::ArcGISRuntime::AttributeListModel *attListModel = m_graphic->attributes(); + if (attListModel) + attListModel->insertAttribute(QLatin1String(SituationMap::UniqueMapIdAttribute), m_uniqueMapId); + else + ERROR_LOG(m_logger) << LOGFUNCTION << " no AttributeListModel, debugInfo='" << m_debugInfo << "'" + << " uniqueMapId=" << m_uniqueMapId; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " uniqueMapId=" << uniqueId; +} + +Point3D AbstractGraphicalObject::origin() const +{ + return m_positions.value().value(0); // value => if index is out of range -> default object +} +void AbstractGraphicalObject::setOrigin(const Point3D &origin) +{ + // TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + // << " origin=" << origin; + + if (m_positions.value().isEmpty()) + m_positions.refValue().append(origin); + else + { + // https://stackoverflow.com/a/1573715/2352507 + // Any comparison(except with "!=") with NaN returns false. + // NaN != NaN -> true!!! + if (m_positions.value().value(0) != origin) // NaN != NaN -> true!!! + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " value(0) = " << m_positions.value().value(0) << " origin = " << origin; + m_positions.refValue().clear(); + m_positions.refValue().append(origin); + } + } + + // TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + // << " origin=" << origin + // << " changed=" << m_positions.changed() + // << " SIZE=" << m_positions.value().size(); +} + +void AbstractGraphicalObject::setOrigin(const QPointF &origin) +{ + setOrigin(Point3D(origin.x(), origin.y(), .0)); +} + +PositionCont AbstractGraphicalObject::positions() const +{ + return m_positions.value(); +} +void AbstractGraphicalObject::setPositions(const PositionCont &posCont) +{ + m_positions = posCont; + + // TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + // << " changed=" << m_positions.changed(); +} + + +bool AbstractGraphicalObject::isSinglePoint() const +{ + return m_positions.value().count() == 1; +} +bool AbstractGraphicalObject::isMultiPoint() const +{ + return m_positions.value().count() > 1; +} +bool AbstractGraphicalObject::isArea() const +{ + return m_positions.value().count() > 2; +} + +LineStyle AbstractGraphicalObject::lineStyle() const +{ + return m_lineStyle.value(); +} +LineStyle &AbstractGraphicalObject::rLineStyle() +{ + return m_lineStyle.refValue(); +} +void AbstractGraphicalObject::setLineStyle(const LineStyle &lineStyle) +{ + m_lineStyle = lineStyle; +} + +FillStyle AbstractGraphicalObject::fillStyle() const +{ + return m_fillStyle.value(); +} +FillStyle &AbstractGraphicalObject::rFillStyle() +{ + return m_fillStyle.refValue(); +} +void AbstractGraphicalObject::setFillStyle(const FillStyle &fillStyle) +{ + m_fillStyle = fillStyle; +} + + +bool AbstractGraphicalObject::visibility() const +{ + return m_visibility.value(); +} +void AbstractGraphicalObject::setVisibility(bool visibility) +{ + m_visibility = visibility; + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " visibility=" << m_visibility; +} + +bool AbstractGraphicalObject::selected() const +{ + return m_selected.value(); +} +void AbstractGraphicalObject::setSelected(bool selected) +{ + m_selected = selected; +} + + +bool AbstractGraphicalObject::pickable() const +{ + return m_pickable.value(); +} +void AbstractGraphicalObject::setPickable(bool pickable) +{ + // allow pickable only if m_uniqueMapId is valid: + m_pickable = pickable && (m_uniqueMapId != INVALID_MAP_ID); + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " pickable=" << m_pickable; +} + + +float AbstractGraphicalObject::scale() const +{ + return m_scale.value(); +} +void AbstractGraphicalObject::setScale(float scale) +{ + m_scale = scale; +} + + +bool AbstractGraphicalObject::addUpdateGraphic() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " updateGraphic"; + + bool successGraphic = true; + + if (! m_graphic) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " no m_graphic -> return true"; + return true; + } + + // append just once :-) + if (! m_graphicsLayer->graphics()->contains(m_graphic)) + { + m_graphicsLayer->graphics()->append(m_graphic); + successGraphic = m_graphicsLayer->graphics()->contains(m_graphic); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " successGraphic=" << successGraphic; + return successGraphic; +} + + +bool AbstractGraphicalObject::create() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " uniqueMapId =" << m_uniqueMapId + << " origin =" << this->origin() + << " visibility=" << m_visibility; + bool success = true; + + // some checks: + if (!m_graphicsLayer) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " no graphicsLayer"; + return false; + } + + if (!m_graphic) + { + m_graphic = new Esri::ArcGISRuntime::Graphic(); + m_graphic->setObjectName(m_debugInfo); + m_graphic->setZIndex(m_drawOrder.value()); + + // use attributeModel to store application obj id (SimWorld) at graphic + Esri::ArcGISRuntime::AttributeListModel *attLisModel = m_graphic->attributes(); + if (attLisModel) + attLisModel->insertAttribute(QLatin1String(SituationMap::UniqueMapIdAttribute), m_uniqueMapId); + else + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no AttributeListModel, debugInfo='" << m_debugInfo << "'" + << " uniqueMapId=" << m_uniqueMapId; + } + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " success=" << success; + return success; +} + + +bool AbstractGraphicalObject::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " uniqueMapId=" << m_uniqueMapId + << " origin=" << this->origin() + << " visibility=" << m_visibility + << " selected=" << m_selected + << " drawOrder=" << m_drawOrder + << " pickable=" << m_pickable + << " pickableCHG=" << m_pickable.changed(); + + if (QApplication::instance()->thread()->currentThreadId() != QThread::currentThreadId()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " wrong Thread, debugInfo='" << m_debugInfo << "'" + << " uniqueMapId=" << m_uniqueMapId; + abort(); + } + + + if (m_graphic) + { + m_graphic->setZIndex(m_drawOrder.value()); + m_graphic->setVisible(m_visibility.value()); + m_graphic->setSelected(m_selected.value()); + + // Picking: + if (m_pickable.changed()) + { + // use attributeModel to store m_pickable at graphic + Esri::ArcGISRuntime::AttributeListModel *attLisModel = m_graphic->attributes(); + if (attLisModel) + attLisModel->insertAttribute(QLatin1String(SituationMap::IsPickableAttribute), m_pickable.value()); + else + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no AttributeListModel, debugInfo='" << m_debugInfo << "'" + << " uniqueMapId=" << m_uniqueMapId; + } + } + m_pickable.reset(); + + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " uniqueMapId =" << m_uniqueMapId; + return true; +} + +bool AbstractGraphicalObject::assignLineStyleToGraphic() +{ + if (!m_graphic) + return false; + + const LineStyle &ls = m_lineStyle.value(); + + if (!m_esriSimpleLineSymbol) + { + m_esriSimpleLineSymbol = + new Esri::ArcGISRuntime::SimpleLineSymbol(convert(ls.linePattern()), ls.lineColor(), ls.lineWidth() * m_scale.value(), m_graphic); + } else { + m_esriSimpleLineSymbol->setStyle(convert(ls.linePattern())); + m_esriSimpleLineSymbol->setColor(ls.lineColor()); + m_esriSimpleLineSymbol->setWidth(ls.lineWidth() * m_scale.value()); + } + m_graphic->setSymbol(m_esriSimpleLineSymbol.data()); + + return true; +} + +bool AbstractGraphicalObject::assignFillStyleToGraphic() +{ + if (!m_graphic) + return false; + + const LineStyle &ls = m_lineStyle.value(); + const FillStyle &fs = m_fillStyle.value(); + + // 1. the Line + if (!m_esriSimpleLineSymbol) + { + m_esriSimpleLineSymbol = + new Esri::ArcGISRuntime::SimpleLineSymbol(convert(ls.linePattern()), ls.lineColor(), ls.lineWidth() * m_scale.value(), m_graphic); + } else { + m_esriSimpleLineSymbol->setStyle(convert(ls.linePattern())); + m_esriSimpleLineSymbol->setColor(ls.lineColor()); + m_esriSimpleLineSymbol->setWidth(ls.lineWidth() * m_scale.value()); + } + + // 1. the Fill + if (!m_esriSimpleFillSymbol) + { + m_esriSimpleFillSymbol = + new Esri::ArcGISRuntime::SimpleFillSymbol(convert(fs.fillPattern()), fs.fillColor(), m_esriSimpleLineSymbol, m_graphic); + } else { + m_esriSimpleFillSymbol->setStyle(convert(fs.fillPattern())); + m_esriSimpleFillSymbol->setColor(fs.fillColor()); + m_esriSimpleFillSymbol->setOutline(m_esriSimpleLineSymbol); + } + m_graphic->setSymbol(m_esriSimpleFillSymbol.data()); + + return true; +} + +Esri::ArcGISRuntime::SimpleLineSymbolStyle AbstractGraphicalObject::convert(const LinePattern linePattern) +{ + switch (linePattern) + { + case LinePattern::Dash: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::Dash; + case LinePattern::DashDot: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::DashDot; + case LinePattern::DashDotDot: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::DashDotDot; + case LinePattern::Dot: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::Dot; + case LinePattern::Null: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::Null; + case LinePattern::Solid: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::Solid; + } // switch + + return Esri::ArcGISRuntime::SimpleLineSymbolStyle::Solid; +} + + +Esri::ArcGISRuntime::SimpleFillSymbolStyle AbstractGraphicalObject::convert(const FillPattern fillPattern) +{ + switch (fillPattern) + { + case FillPattern::BackwardDiagonal: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::BackwardDiagonal; + case FillPattern::Cross: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::Cross; + case FillPattern::DiagonalCross: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::DiagonalCross; + case FillPattern::ForwardDiagonal: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::ForwardDiagonal; + case FillPattern::Horizontal: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::Horizontal; + case FillPattern::Null: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::Null; + case FillPattern::Solid: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid; + case FillPattern::Vertical: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::Vertical; + } // switch + + return Esri::ArcGISRuntime::SimpleFillSymbolStyle::BackwardDiagonal; +} + +int AbstractGraphicalObject::countOfPolygonFirstPart(const Esri::ArcGISRuntime::Multipart &polylinePolygon) +{ + if (polylinePolygon.isEmpty() || polylinePolygon.parts().isEmpty()) + return -1; + + return polylinePolygon.parts().part(0).pointCount(); +} + + +const Esri::ArcGISRuntime::SpatialReference AbstractGraphicalObject::spatialReference() const +{ + return m_geoView->spatialReference(); +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/abstract_graphical_object.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/abstract_graphical_object.h new file mode 100755 index 000000000..749776324 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/abstract_graphical_object.h @@ -0,0 +1,213 @@ +/** + * @RDE_HEADER@ + * AbstractGraphicalObject + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include +#include +#include +#include + +#include + +// ESRI +#include +#include + +// Qt +#include + + +// forward: +namespace Esri +{ +namespace ArcGISRuntime +{ +class GeoView; +class Graphic; +class GraphicsOverlay; +class Geometry; +class Multipart; +class SpatialReference; +} +} + + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +//forward +class GraphicalObjectText; + + +class SITUATIONMAP_EXPORT AbstractGraphicalObject +{ + +public: + AbstractGraphicalObject(const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId); + + virtual ~AbstractGraphicalObject(); + + AbstractGraphicalObject(const AbstractGraphicalObject &other) = delete; + AbstractGraphicalObject &operator=(AbstractGraphicalObject &other) = delete; + + virtual Esri::ArcGISRuntime::Graphic *graphic(); + virtual Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer(); + + virtual QString toString() const; + + virtual UniqueMapId uniqueMapId() const; + virtual void setUniqueMapId(UniqueMapId uniqueId); + + virtual QString debugInfo() const; + virtual void setDebugInfo(const QString &debugInfo); + + virtual int drawOrder() const; + virtual void setDrawOrder(int drawOrder); + + // Data access: + + // for SinglePoint: + virtual Point3D origin() const; + virtual void setOrigin(const Point3D &origin); + virtual void setOrigin(const QPointF &origin); // ToDO accept this?? + + // for MultiPoint: + virtual PositionCont positions() const; + virtual void setPositions(const PositionCont &posCont); + + virtual bool isSinglePoint() const; + virtual bool isMultiPoint() const; + virtual bool isArea() const; + + virtual LineStyle lineStyle() const; + virtual LineStyle &rLineStyle(); + /** + * @brief setLineStyle (Font-)size is limited to 500. + * (High values can lead to coredump with 'SymbolDrawHelper::create_line_image FAILED, SkBitmap is NULL or empty') + * @param lineStyle to set. + */ + virtual void setLineStyle(const LineStyle &lineStyle); + + virtual FillStyle fillStyle() const; + virtual FillStyle &rFillStyle(); + virtual void setFillStyle(const FillStyle &fillStyle); + + // Scale : + virtual float scale() const; + virtual void setScale(float scale); + // : Scale + + + // Visibility : + virtual bool visibility() const; + virtual void setVisibility(bool visibility); + // : Visibility + + // Selection, Picking : + bool selected() const; + void setSelected(bool selected); + + bool pickable() const; + /** + * @brief setPickable The method allows pickable=true only if uniqueMapIdis valid. + * @param pickable new value. + */ + void setPickable(bool pickable); + // : Selection, Picking + + + virtual Esri::ArcGISRuntime::Geometry getGraphicGeometry() const; + + bool updateView(); + + bool addUpdateGraphic(); + + static Esri::ArcGISRuntime::SimpleLineSymbolStyle convert(const LinePattern linePattern); + static Esri::ArcGISRuntime::SimpleFillSymbolStyle convert(const FillPattern fillPattern); + + bool assignLineStyleToGraphic(); + bool assignFillStyleToGraphic(); + + + static int countOfPolygonFirstPart(const Esri::ArcGISRuntime::Multipart &polylinePolygon); + + +protected: + // Internal/Admin + const Esri::ArcGISRuntime::SpatialReference spatialReference() const; + + virtual bool changed() + { + return m_drawOrder.changed() + || m_positions.changed() + || m_visibility.changed() + || m_selected.changed() + || m_pickable.changed() + || m_scale.changed() + || m_lineStyle.changed() + || m_fillStyle.changed(); + } + + virtual bool updateViewImpl() = 0; + + virtual void resetChanged() + { + m_drawOrder.reset(); + m_positions.reset(); + m_visibility.reset(); + m_selected.reset(); + m_pickable.reset(); + m_scale.reset(); + m_lineStyle.reset(); + m_fillStyle.reset(); + } + + + virtual bool create() = 0; + + + const Esri::ArcGISRuntime::GeoView *m_geoView = nullptr; + Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr; + Esri::ArcGISRuntime::Graphic *m_graphic = nullptr; + SimpleValue m_drawOrder = 0; + + // Data + UniqueMapId m_uniqueMapId; + + + QString m_debugInfo; ///< for debugging + + SimpleValue m_positions; + + SimpleValue m_visibility = true; + + SimpleValue m_selected = false; + SimpleValue m_pickable = true; + + SimpleValue m_scale = 1.0; + + SimpleValue m_lineStyle; + SimpleValue m_fillStyle; + QPointer m_esriSimpleLineSymbol = nullptr; + QPointer m_esriSimpleFillSymbol = nullptr; + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/ellipse_parameters.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/ellipse_parameters.cpp new file mode 100755 index 000000000..9b60d8462 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/ellipse_parameters.cpp @@ -0,0 +1,96 @@ +/** + * @RDE_HEADER@ + * EllipseParameters + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "ellipse_parameters.h" + + +// ESRI + + +// Qt +#include + + +namespace SituationMap +{ + +float EllipseParameters::lengthX() const +{ + return m_lengthX; +} + +void EllipseParameters::setLengthX(float lengthX) +{ + m_lengthX = lengthX; +} +float EllipseParameters::lengthY() const +{ + return m_lengthY; +} + +void EllipseParameters::setLengthY(float lengthY) +{ + m_lengthY = lengthY; +} +float EllipseParameters::directionDegree() const +{ + return m_directionDegree; +} + +void EllipseParameters::setDirectionDegree(float directionDegree) +{ + m_directionDegree = directionDegree; +} +int EllipseParameters::maxSegmentLength() const +{ + return m_maxSegmentLength; +} + +void EllipseParameters::setMaxSegmentLength(int value) +{ + m_maxSegmentLength = value; +} + +int EllipseParameters::maxPointCount() const +{ + return m_maxPointCount; +} + +void EllipseParameters::setMaxPointCount(int maxPointCount) +{ + m_maxPointCount = maxPointCount; +} + + +bool EllipseParameters::isValid() const +{ + return ((m_lengthX > 0) && (m_lengthY > 0)) ; +} + + + +//----------------------------------------------------------------------------- +// output operator: +//----------------------------------------------------------------------------- +std::ostream &operator<< (std::ostream &stream, + const EllipseParameters &value) +{ + QString str; + str.append(QStringLiteral(" lengthX=%1").arg(value.lengthX())); + str.append(QStringLiteral(" lengthY=%1").arg(value.lengthY())); + str.append(QStringLiteral(" directionDegree=%1").arg(value.directionDegree())); + str.append(QStringLiteral(" maxSegmentLength=%1").arg(value.maxSegmentLength())); + + stream << str.toStdString(); + return stream; +} + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/ellipse_parameters.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/ellipse_parameters.h new file mode 100755 index 000000000..a8609a3a0 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/ellipse_parameters.h @@ -0,0 +1,95 @@ +/** + * @RDE_HEADER@ + * EllipseParameters + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include + +#include + +// Qt + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + + + +//---------------------------------------------------------------------------- +class SITUATIONMAP_EXPORT EllipseParameters +{ +public: + + static constexpr unsigned int MAX_POINT_COUNT_DEFAULT = 30; + + EllipseParameters() = default; + + float lengthX() const; + void setLengthX(float lengthX); + + float lengthY() const; + void setLengthY(float lengthY); + + float directionDegree() const; + void setDirectionDegree(float directionDegree); + + int maxSegmentLength() const; + void setMaxSegmentLength(int value); + + int maxPointCount() const; + void setMaxPointCount(int maxPointCount); + + bool isValid() const; + + /** + * @brief operator== Compare two objects + * @param other to be compared with + * @return true if identical, otherwise false. + */ + bool operator==(const EllipseParameters &other) const + { + return ((this->m_lengthX == other.m_lengthX) && + (this->m_lengthY == other.m_lengthY) && + (this->m_directionDegree == other.m_directionDegree) && + (this->m_maxSegmentLength == other.m_maxSegmentLength) && + (this->m_maxPointCount == other.m_maxPointCount) + ); + } + + /** + * @brief operator!= Compare two objects + * @param other to be compared with + * @return true if NOT identical, otherwise false. + */ + bool operator!=(const EllipseParameters &other) const + { + return !(*this == other); + } + + + +private: + float m_lengthX = 0; + float m_lengthY = 0; + float m_directionDegree = 0; + int m_maxSegmentLength = 10; // meter + int m_maxPointCount = MAX_POINT_COUNT_DEFAULT; +}; + +//----------------------------------------------------------------------------- +// output operator: +//----------------------------------------------------------------------------- +SITUATIONMAP_EXPORT std::ostream &operator<< (std::ostream &stream, + const EllipseParameters &value); + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_attributes.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_attributes.cpp new file mode 100755 index 000000000..589367333 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_attributes.cpp @@ -0,0 +1,190 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectAttributes + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "graphical_object_attributes.h" +#include +#include // operator<< +#include + + +// ESRI +#include +#include + +// Qt + + +namespace SituationMap +{ + + +class GraphicalObjectAttributes::Private +{ +public: + Private() = default; + ~Private() = default; + + // ESRI data: + MapImpl *m_mapImpl = nullptr; + QString m_logicalLayerName; + + // OWN data: + SimpleValue m_attributes; + +}; // class GraphicalObjectAttributes::Private +//----------------------------------------------------------------------------- + + + +GraphicalObjectAttributes::GraphicalObjectAttributes(MapImpl *mapImpl, + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer, + const QString &debugInfo, + const UniqueMapId &uniqueMapId) + : BaseClass(mapImpl->getEsriGeoView(), graphicsLayer, debugInfo, uniqueMapId), + m_logger(getLogger("GraphicalObjectAttributes")), + d(new Private) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +GraphicalObjectAttributes::~GraphicalObjectAttributes() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + delete d; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +bool GraphicalObjectAttributes::create() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " m_visibility = " << m_visibility; + + bool success = BaseClass::create(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " create failed"; + return false; + } + bool successGraphic = addUpdateGraphic(); // CHECK + if (!successGraphic) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " addUpdateGraphic failed, debugInfo='" << m_debugInfo << "'" << " uniqueMapId=" << m_uniqueMapId; + return false; + } + + // real work: + // empty + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; + return success; +} + +QVariantMap GraphicalObjectAttributes::attributes() const +{ + return d->m_attributes.value(); +} + +void GraphicalObjectAttributes::setAttributes(const QVariantMap &attributes) +{ + d->m_attributes = attributes; +} + +bool GraphicalObjectAttributes::changed() +{ + return BaseClass::changed() + || d->m_attributes.changed(); +} +void GraphicalObjectAttributes::resetChanged() +{ + BaseClass::resetChanged(); + d->m_attributes.reset(); +} + +Esri::ArcGISRuntime::Geometry GraphicalObjectAttributes::getGraphicGeometry() const +{ + if (m_graphic) return m_graphic->geometry(); + + ERROR_LOG(m_logger) << LOGFUNCTION << "Geometry is undefined"; + + return Esri::ArcGISRuntime::Geometry(); +} // Esri::ArcGISRuntime::Geometry GraphicalObjectAttributes::getGraphicGeometry() const + + +bool GraphicalObjectAttributes::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " visibility=" << m_visibility + << " selected=" << m_selected + << " origin=" << this->origin() + << " scale=" << m_scale.value(); + + bool success = create(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " create failed"; + return false; + } + + if (!BaseClass::updateViewImpl()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " updateView failed"; + return false; + } + + + // real work: + if (m_positions.changed()) + { + const PositionCont &posCont = m_positions.value(); + if (posCont.size() == 1) + { + const Point3D &origin = this->origin(); + Esri::ArcGISRuntime::Point originGeometry(origin.x(), + origin.y(), + origin.z(), + spatialReference()); + m_graphic->setGeometry(originGeometry); + } + } + + if (d->m_attributes.changed()) + { + QVariantMap rawAttributes = d->m_attributes.value(); + + // add basics: + rawAttributes[QLatin1String(SituationMap::UniqueMapIdAttribute)] = m_uniqueMapId; + rawAttributes[QLatin1String(SituationMap::IsPickableAttribute)] = m_pickable.value(); + m_graphic->attributes()->setAttributesMap(rawAttributes); + + if (m_logger.isDebugEnabled()) + qCritical() << " XXXXX set rawAttributes " << rawAttributes; + } + + if (m_graphic) + { + m_graphic->setZIndex(m_drawOrder.value()); + m_graphic->setVisible(m_visibility.value()); + m_graphic->setSelected(m_selected.value()); + } + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " failure, debugInfo='" << m_debugInfo << "'"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " success = " << success + << " visibility=" << m_visibility; + return success; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_attributes.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_attributes.h new file mode 100755 index 000000000..56d84c23a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_attributes.h @@ -0,0 +1,88 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectAttributes + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_graphical_object.h" + +// Esri + + +// Qt + + +// forward: + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +// forward: +class MapImpl; + + +/** + * @brief The GraphicalObjectAttributes class + */ +class SITUATIONMAP_EXPORT GraphicalObjectAttributes : public AbstractGraphicalObject +{ + + using BaseClass = AbstractGraphicalObject; + +public: + + /** + * @brief GraphicalObjectAttributes + * @param mapImpl map to be used. + * @param logicalLayerName logical layer name to be used. + * @param debugInfo debugInfo to be used + * @param uniqueMapId uniqueMapId to be used. (Optional to fulfill rubberband requirements) + */ + GraphicalObjectAttributes(MapImpl *mapImpl, + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer, + const QString &debugInfo, + const UniqueMapId &uniqueMapId = INVALID_MAP_ID); + + GraphicalObjectAttributes(const GraphicalObjectAttributes &other) = delete; + GraphicalObjectAttributes &operator=(GraphicalObjectAttributes &other) = delete; + + virtual ~GraphicalObjectAttributes(); + + + QVariantMap attributes() const; + void setAttributes(const QVariantMap &attributes); + + Esri::ArcGISRuntime::Geometry getGraphicGeometry() const override; + +protected: + // Internal/Admin + virtual bool create() override; + + bool changed() override; + void resetChanged() override; + + bool updateViewImpl() override; + + // logical + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility + + class Private; + /** + * The private d-pointer to not break BC when changing internals of this class + */ + Private *d = nullptr; + + +}; + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_cube.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_cube.cpp new file mode 100755 index 000000000..78ce4bb26 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_cube.cpp @@ -0,0 +1,162 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectCube + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "graphical_object_cube.h" +#include // operator<< + + +// ESRI +#include +#include + + +// Qt + + +namespace SituationMap +{ + + +GraphicalObjectCube::GraphicalObjectCube(const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId) + : BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId) + , m_logger(getLogger("GraphicalObjectCube")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +GraphicalObjectCube::~GraphicalObjectCube() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +bool GraphicalObjectCube::create() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + + bool success = BaseClass::create(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " create failed"; + } + + // real work: + // empty + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + // << " drawOrder = " << m_graphic->drawOrder() + << " success = " << success; + return success; +} + + +bool GraphicalObjectCube::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + + // some checks: + if (!this->isArea()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " incorrect number of positions" + << " (" << this->positions().count() << ")"; + return false; + } + + if (!create()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " create failed"; + return false; + } + + if (!BaseClass::updateViewImpl()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " updateView failed"; + return false; + } + + // real work: + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"; + + + // Attribute 'height': + QVariantMap attMap = m_graphic->attributes()->attributesMap(); + + attMap[QStringLiteral("height")] = QVariant(m_height.value()); + // + // Attribute 'PartyName' is replaced by Attribute 'Extrusion_Color' to be more flexible + // + + + // Attribute 'Extrusion_Color': + if (m_extrusionColorName.value().isEmpty()) + { + DEBUG_LOG(m_logger) << LOGFUNCTION + << " debugInfo='" << m_debugInfo << "'" + << " m_extrusionColorName is empty"; + // -> time for the defaultSymbol of the Renderer + attMap[QStringLiteral("Extrusion_Color")] = QStringLiteral("DOES_NOT_EXIST"); + } + else + attMap[QStringLiteral("Extrusion_Color")] = QVariant(m_extrusionColorName.value()); + + m_graphic->attributes()->setAttributesMap(attMap); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " attributesMap[height] ='" << attMap[QStringLiteral("height")].toDouble() << "'"; + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " attributesMap[Extrusion_Color] ='" << attMap[QStringLiteral("Extrusion_Color")].toString() << "'"; + + // geometry: + Esri::ArcGISRuntime::PolygonBuilder builder(spatialReference()); + + const PositionCont &posCont = m_positions.value(); + for (const Point3D &pos : posCont) + { + builder.addPoint(pos.x(), pos.y()); + } + + if (! builder.isSketchValid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " builder -> sketch invalid"; + return false; + } + m_graphic->setGeometry(builder.toGeometry()); + + + // add + bool successGraphic = addUpdateGraphic(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " drawOrder = " << m_graphic->zIndex(); + return successGraphic; +} + + + + +//----------------------------------------------------------------------------- +// output operator: +//----------------------------------------------------------------------------- +std::ostream &operator<<(std::ostream &stream, + const GraphicalObjectCube &value) +{ + stream << value.toString(); + return stream; +} + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_cube.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_cube.h new file mode 100755 index 000000000..15866adb9 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_cube.h @@ -0,0 +1,100 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectCube + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_graphical_object.h" + + +// Esri + + +// Qt + + +// forward: + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The GraphicalObjectCube class for 3D + * + */ +class SITUATIONMAP_EXPORT GraphicalObjectCube : public AbstractGraphicalObject +{ + + using BaseClass = AbstractGraphicalObject; + +public: + explicit GraphicalObjectCube(const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId); + + ~GraphicalObjectCube(); + + double height() const + { + return m_height.value(); + } + void setHeight(double height) + { + m_height = height; + } + + const QString &extrusionColorName() const + { + return m_extrusionColorName.value(); + } + void setExtrusionColorName(const QString &extrusionColorName) + { + m_extrusionColorName = extrusionColorName; + } + +protected: + // Internal/Admin + bool create() override; + + bool changed() override + { + return BaseClass::changed() + || m_height.changed() + || m_extrusionColorName.changed(); + } + + void resetChanged() override + { + BaseClass::resetChanged(); + m_height.reset(); + m_extrusionColorName.reset(); + } + + bool updateViewImpl() override; + + + // Data + SimpleValue m_height; + SimpleValue m_extrusionColorName; + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + +//----------------------------------------------------------------------------- +// output operator: +//----------------------------------------------------------------------------- +SITUATIONMAP_EXPORT std::ostream &operator<< (std::ostream &stream, + const GraphicalObjectCube &value); + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_ellipse.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_ellipse.cpp new file mode 100755 index 000000000..578dc1010 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_ellipse.cpp @@ -0,0 +1,344 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectEllipse + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "graphical_object_ellipse.h" +#include // operator<< + + +// ESRI +#include +#include +#include +#include +#include + + +// Qt +#include + + +namespace SituationMap +{ + + +GraphicalObjectEllipse::GraphicalObjectEllipse(const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId) + : BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId) + , m_logger(getLogger("GraphicalObjectEllipse")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +GraphicalObjectEllipse::~GraphicalObjectEllipse() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + + delete m_internalParameters; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +bool GraphicalObjectEllipse::create() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + + bool success = BaseClass::create(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " create failed"; + return false; + } + + // real work: + // empty + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + // << " drawOrder = " << m_graphic->drawOrder() + << " success = " << success; + return success; +} + + +bool GraphicalObjectEllipse::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + + // some checks: + if (!this->isSinglePoint()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " incorrect number of positions" + << " (" << this->positions().count() << ")"; + return false; + } + + if (!create()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " create failed"; + return false; + } + + if (!BaseClass::updateViewImpl()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " updateView failed"; + return false; + } + + if (!m_ellipseParameters.value().isValid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " ellipseParameters not valid"; + return false; + } + + + // real work: + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " update geometry"; + + bool successEllipse = true; + if (m_ellipseParameters.changed() || m_positions.changed()) + { + // apply attributes: + parameterToInternal(); + + Esri::ArcGISRuntime::Polygon ellipse; + successEllipse = createEllipse(ellipse); + if (successEllipse) + m_graphic->setGeometry(ellipse); + else + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " createEllipse failed"; + return false; + } + } + + assignFillStyleToGraphic(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"; + + bool successGraphic = addUpdateGraphic(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " drawOrder = " << m_graphic->zIndex() + << " successEllipse = " << successEllipse + << " successGraphic = " << successGraphic; + return successEllipse && successGraphic; +} + +void GraphicalObjectEllipse::parameterToInternal() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + if (!m_internalParameters) + { + m_internalParameters = new Esri::ArcGISRuntime::GeodesicEllipseParameters(); + m_internalParameters->setGeometryType(Esri::ArcGISRuntime::GeometryType::Polygon); + } + + // TODO !!??: set xy correct, calc maxSegLength... + + + const EllipseParameters &ellipseParameterRef = m_ellipseParameters.value(); + + // 1a, 1b + m_internalParameters->setMaxSegmentLength(ellipseParameterRef.maxSegmentLength()); + m_internalParameters->setMaxPointCount(ellipseParameterRef.maxPointCount()); + + // 2 + const Point3D &origin = this->origin(); + Esri::ArcGISRuntime::Point mapPoint(origin.x(), origin.y(), origin.z(), spatialReference()); // center of ellipse + + Esri::ArcGISRuntime::Point normalizedPt = + Esri::ArcGISRuntime::GeometryEngine::normalizeCentralMeridian(mapPoint); + m_internalParameters->setCenter(normalizedPt); + // m_internalParameters->setCenter(mapPoint); + + // 3, 4 + m_internalParameters->setSemiAxis1Length(ellipseParameterRef.lengthX()); + m_internalParameters->setSemiAxis2Length(ellipseParameterRef.lengthY()); + + // 5 + m_internalParameters->setAxisDirection(ellipseParameterRef.directionDegree()); + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +bool GraphicalObjectEllipse::createEllipse(Esri::ArcGISRuntime::Polygon &ellipse) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + + bool success = createEllipsePolygon(*m_internalParameters, ellipse); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " success = " << success +// << " pointCount = " << ellipse.pointCount() + << " dimension = " << ellipse.dimension(); + return success; +} + +bool GraphicalObjectEllipse::createEllipsePolygon(const Esri::ArcGISRuntime::GeodesicEllipseParameters &ellipseParameters, + Esri::ArcGISRuntime::Polygon &polygon) +{ + LOGGER_T m_logger = getLogger("GraphicalObjectEllipse"); + + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + QElapsedTimer timer; + + if (m_logger.isInfoEnabled()) + timer.start(); + + + bool success = false; + + Esri::ArcGISRuntime::Geometry ellipseGeometry = + Esri::ArcGISRuntime::GeometryEngine::ellipseGeodesic(ellipseParameters); + if (ellipseGeometry.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " There was an error computing the geodesic ellipse. Try a different segment length."; + } + else + { + polygon = Esri::ArcGISRuntime::Polygon(ellipseGeometry); + success = true; + } + int count = AbstractGraphicalObject::countOfPolygonFirstPart(polygon); + DEBUG_LOG(m_logger) << LOGFUNCTION + << " count=" << count + << " (semiAxis1Length=" << ellipseParameters.semiAxis1Length() + << " , semiAxis2Length=" << ellipseParameters.semiAxis2Length() + << " , maxPointCount=" << ellipseParameters.maxPointCount() + << " , maxSegmentLength=" << ellipseParameters.maxSegmentLength() + << " , dimension = " << polygon.dimension() + << ")"; + + + + if (m_logger.isDebugEnabled()) + { + DEBUG_LOG(m_logger) << LOGFUNCTION + << " took " << timer.elapsed() << "ms" + << " (semiAxis1Length=" << ellipseParameters.semiAxis1Length() + << " , semiAxis2Length=" << ellipseParameters.semiAxis2Length() + << " , maxPointCount=" << ellipseParameters.maxPointCount() + << " , maxSegmentLength=" << ellipseParameters.maxSegmentLength() + << " , dimension = " << polygon.dimension() + << ")"; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " success = " << success; + return success; +} + +bool GraphicalObjectEllipse::createCirclePolygon(const Esri::ArcGISRuntime::Point &originMap, + float radius, + unsigned int maxPointCount, + Esri::ArcGISRuntime::Polygon &polygon) +{ + LOGGER_T m_logger = getLogger("GraphicalObjectEllipse"); + + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " origin=" << originMap.x() << " ; " << originMap.y() << " ; " << originMap.z() + << " radius=" << radius; + + Esri::ArcGISRuntime::GeodesicEllipseParameters ePara; + ePara.setGeometryType(Esri::ArcGISRuntime::GeometryType::Polygon); + + // 1 + ePara.setMaxSegmentLength(10); + if (maxPointCount == 0) + ePara.setMaxPointCount(EllipseParameters::MAX_POINT_COUNT_DEFAULT); + else + ePara.setMaxPointCount(maxPointCount); + + // 2 + const Esri::ArcGISRuntime::Point &mapPoint = originMap; // center of ellipse + + Esri::ArcGISRuntime::Point normalizedPt = + Esri::ArcGISRuntime::GeometryEngine::normalizeCentralMeridian(mapPoint); + ePara.setCenter(normalizedPt); + + // 3, 4 + ePara.setSemiAxis1Length(radius); + ePara.setSemiAxis2Length(radius); + + // 5 + ePara.setAxisDirection(0); + + return createEllipsePolygon(ePara, polygon); +} + +bool GraphicalObjectEllipse::createCirclePolygon(const Esri::ArcGISRuntime::Point &originMap, + float radius, + unsigned int maxPointCount, + PositionCont &posCont) +{ + Esri::ArcGISRuntime::Polygon circle; + bool success = GraphicalObjectEllipse::createCirclePolygon(originMap, + radius, + maxPointCount, + circle); + + if (success && !circle.isEmpty()) + { + posCont.clear(); + + const Esri::ArcGISRuntime::ImmutablePart &immutablePart = circle.parts().part(0); + const int size = immutablePart.pointCount(); + + for (int i = 0; i < size; ++i) + { + const Esri::ArcGISRuntime::Point &p = immutablePart.point(i); + posCont.append(Point3D(p.x(), p.y(), p.z())); + } + } + return success; +} + + + +EllipseParameters GraphicalObjectEllipse::ellipseParameters() const +{ + return m_ellipseParameters.value(); +} + +EllipseParameters &GraphicalObjectEllipse::rEllipseParameters() +{ + return m_ellipseParameters.refValue(); +} + +void GraphicalObjectEllipse::setEllipseParameters(const EllipseParameters &ellipseParameters) +{ + m_ellipseParameters = ellipseParameters; +} + + + + +//----------------------------------------------------------------------------- +// output operator: +//----------------------------------------------------------------------------- +std::ostream &operator<<(std::ostream &stream, + const GraphicalObjectEllipse &value) +{ + stream << value.toString(); + return stream; +} + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_ellipse.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_ellipse.h new file mode 100755 index 000000000..19b34ccf1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_ellipse.h @@ -0,0 +1,121 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectEllipse + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_graphical_object.h" +#include "ellipse_parameters.h" + + +// Esri + + +// Qt + + +// forward: +namespace Esri +{ +namespace ArcGISRuntime +{ +class Polygon; +class Point; +class GeodesicEllipseParameters; +} +} + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +class SITUATIONMAP_EXPORT GraphicalObjectEllipse : public AbstractGraphicalObject +{ + + using BaseClass = AbstractGraphicalObject; + +public: + GraphicalObjectEllipse(const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId); + + ~GraphicalObjectEllipse(); + + EllipseParameters ellipseParameters() const; + EllipseParameters &rEllipseParameters(); + void setEllipseParameters(const EllipseParameters &ellipseParameters); + + + static bool createEllipsePolygon(const Esri::ArcGISRuntime::GeodesicEllipseParameters &ellipseParameters, + Esri::ArcGISRuntime::Polygon &polygon); + + static bool createCirclePolygon(const Esri::ArcGISRuntime::Point &originMap, + float radius, + unsigned int maxPointCount, + Esri::ArcGISRuntime::Polygon &polygon); + + static bool createCirclePolygon(const Esri::ArcGISRuntime::Point &originMap, + float radius, + unsigned int maxPointCount, + PositionCont &posCont); + + +protected: + // Internal/Admin + bool create() override; + + bool changed() override + { + return BaseClass::changed() + || m_ellipseParameters.changed(); + } + + void resetChanged() override + { + BaseClass::resetChanged(); + m_ellipseParameters.reset(); + } + + + bool updateViewImpl() override; + + + // Data + Esri::ArcGISRuntime::GeodesicEllipseParameters *m_internalParameters = nullptr; + SimpleValue m_ellipseParameters; + + void parameterToInternal(); + virtual bool createEllipse(Esri::ArcGISRuntime::Polygon &ellipse); + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility + + // Forbid some overloaded methods without effects of this object; + virtual PositionCont positions() const override + { + return PositionCont(); + } + virtual void setPositions(const PositionCont &posCont) override + { + Q_UNUSED(posCont); + } + +}; + +//----------------------------------------------------------------------------- +// output operator: +//----------------------------------------------------------------------------- +std::ostream &operator<< (std::ostream &stream, + const GraphicalObjectEllipse &value); + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_model_scene_marker.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_model_scene_marker.cpp new file mode 100755 index 000000000..52786aeed --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_model_scene_marker.cpp @@ -0,0 +1,169 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectModelSceneMarker + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "graphical_object_model_scene_marker.h" +#include // operator<< +#include + + +// ESRI +#include +#include + + +// Qt +#include +#include + +namespace SituationMap +{ + +GraphicalObjectModelSceneMarker::GraphicalObjectModelSceneMarker(const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId) + : BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId) + , m_logger(getLogger("GraphicalObjectModelSceneMarker")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +GraphicalObjectModelSceneMarker::~GraphicalObjectModelSceneMarker() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + delete m_modelSceneSymbol; + m_modelSceneSymbol = nullptr; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +void GraphicalObjectModelSceneMarker::setUrl(const QUrl &url) +{ + m_url = url; +} + +QUrl GraphicalObjectModelSceneMarker::url() const +{ + return m_url.value(); +} + +void GraphicalObjectModelSceneMarker::setHeading(float heading) +{ + m_heading = heading; +} + +float GraphicalObjectModelSceneMarker::heading() const +{ + return m_heading.value(); +} + + +bool GraphicalObjectModelSceneMarker::create() +{ + return BaseClass::create(); +} + +bool GraphicalObjectModelSceneMarker::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " origin =" << this->origin() // .x() << " , " << this->origin().y() + << " visibility=" << m_visibility + << " pickable=" << m_pickable + << " pickable.changed=" << m_pickable.changed() + << " selected=" << m_selected + << " m_url = " << m_url.value().toString() + << " m_drawOrder = " << m_drawOrder + << " m_scale = " << m_scale.value() + << " m_heading = " << m_heading.value(); + + // some checks: + if (!isSinglePoint()) + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " debugInfo='" << m_debugInfo << "'" + << " incorrect number of positions" + << " (" << this->positions().count() << ")"; + return false; + } + + QFile fi(m_url.value().path()); + if (!fi.exists()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " m_url does not exists: '" << fi.fileName() << "'"; + m_url.reset(); + } + + if (m_url.value().isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " debugInfo='" << m_debugInfo << "'" + << " no url provided (or does not exist)"; + return false; + } + + bool success = create(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " create failed"; + return false; + } + + const Point3D &origin = this->origin(); + Esri::ArcGISRuntime::Point originGeometry(origin.x(), + origin.y(), + origin.z(), + spatialReference()); + m_graphic->setGeometry(originGeometry); + + // url changed? + if ( (m_modelSceneSymbol && (m_modelSceneSymbol->url() != m_url.value())) || + m_scale.changed()) + { + // force re-create ModelSceneSymbol: + delete m_modelSceneSymbol; + m_modelSceneSymbol = nullptr; + } + + if (!m_modelSceneSymbol) + { + m_modelSceneSymbol = new Esri::ArcGISRuntime::ModelSceneSymbol(m_url.value(), m_scale.value()); // url, scale + } + + m_modelSceneSymbol->setHeading(m_heading.value()); // degree, clockwise, 0 is north/12 +// m_modelSceneSymbol->setAnchorPosition(Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Bottom); + m_graphic->setSymbol(m_modelSceneSymbol); + + + + success = BaseClass::updateViewImpl(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " updateView failed"; + return false; + } + + success = addUpdateGraphic(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " updateView failed"; + return false; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " success=" << success + << " m_drawOrder = " << m_drawOrder + << " m_graphic->drawOrder = " << m_graphic->zIndex(); + return success; +} + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_model_scene_marker.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_model_scene_marker.h new file mode 100755 index 000000000..e58fa84ee --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_model_scene_marker.h @@ -0,0 +1,111 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectModelSceneMarker + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_graphical_object.h" +#include +#include + + +// Esri +#include + + +// Qt +#include +#include + + +// forward: +namespace Esri +{ +namespace ArcGISRuntime +{ +class Graphic; +class GraphicsOverlay; +class ModelSceneSymbol; +} +} + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +class SITUATIONMAP_EXPORT GraphicalObjectModelSceneMarker : public AbstractGraphicalObject +{ + + using BaseClass = AbstractGraphicalObject; + +public: + GraphicalObjectModelSceneMarker(const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId); + + ~GraphicalObjectModelSceneMarker(); + + // Data access: + // to be cont. + void setUrl(const QUrl &url); + QUrl url() const; + + // degree ; 0 is north/12 + void setHeading(float heading); + float heading() const; + + +protected: + + // Internal/Admin + bool create() override; + + bool changed() override + { + return BaseClass::changed() + || m_url.changed() + || m_heading.changed() + ; + } + + void resetChanged() override + { + BaseClass::resetChanged(); + m_url.reset(); + m_heading.reset(); + } + + bool updateViewImpl() override; + + // used ModelSceneSymbol: + Esri::ArcGISRuntime::ModelSceneSymbol *m_modelSceneSymbol{nullptr}; + + SimpleValue m_url; // e.g. /shd/CTC/TOOLS/ArcGIS/Runtime/Data/3D/Bristol/Collada/Bristol.dae + SimpleValue m_heading{0.0}; // degree, clockwise, 0 is north/12 + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility + + // Forbid some overloaded methods without effects of this object; + PositionCont positions() const override + { + return PositionCont(); + } + void setPositions(const PositionCont &posCont) override + { + Q_UNUSED(posCont); + Q_ASSERT(false); + } +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_multi_path.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_multi_path.cpp new file mode 100755 index 000000000..0d7974431 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_multi_path.cpp @@ -0,0 +1,323 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectPath + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "graphical_object_multi_path.h" +#include // operator<< + + +// ESRI +#include +#include +#include +#include +#include +#include +#include + + +// Qt + + +namespace SituationMap +{ + + +GraphicalObjectMultiPath::GraphicalObjectMultiPath(const GraphicalObjectPathType type + , const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , UniqueMapId uniqueMapId) + : BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId) + , m_pathType(type) + , m_logger(getLogger("GraphicalObjectMultiPath")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " m_pathType=" << m_pathType; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " m_pathType=" << m_pathType; +} + +GraphicalObjectMultiPath::~GraphicalObjectMultiPath() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " m_pathType=" << m_pathType; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " m_pathType=" << m_pathType; +} + +PositionCont GraphicalObjectMultiPath::path(int pathIndex) const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " pathIndex=" << pathIndex; + if (hasPosCont(pathIndex)) + return getPosCont(pathIndex).value(); + return {}; +} +void GraphicalObjectMultiPath::setPath(int pathIndex, const PositionCont &positions) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " pathIndex=" << pathIndex + << " positions=" << positions; + m_positionsCont[pathIndex] = positions; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +void GraphicalObjectMultiPath::clearPath(int pathIndex) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " pathIndex=" << pathIndex; + if (pathIndex == -1) + { + m_positions.refValue().clear(); + m_positionsCont.clear(); + } + else + { + if (hasPosCont(pathIndex)) + { + // Do not remove the whole entry to let changed() work + m_positionsCont[pathIndex].refValue().clear(); + } + } + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +void GraphicalObjectMultiPath::clearAllPaths() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + m_positions.refValue().clear(); + m_positionsCont.clear(); + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +void GraphicalObjectMultiPath::reduceTo(const QList &remainingPathIndexes) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " remainingPathIndexes=" << remainingPathIndexes; + QList keys = m_positionsCont.keys(); + for (int key : std::as_const(keys)) + { + if (!remainingPathIndexes.contains(key)) + { + // Do not remove the whole entry to let chanded() work + m_positionsCont[key].refValue().clear(); + } + } + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +QList GraphicalObjectMultiPath::pathIndexInUse() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + QList ret; +///// ret << 0; // pathIndex=0 -> m_positions + + QMapIterator > mapIt(m_positionsCont); + while (mapIt.hasNext()) + { + mapIt.next(); + const SimpleValue &posData = mapIt.value(); + const PositionCont &posCont = posData.value(); + if (! posCont.isEmpty()) + ret += mapIt.key(); + } // while + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; + return ret; +} + + +bool GraphicalObjectMultiPath::create() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + + bool success = BaseClass::create(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " create failed"; + return false; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; + return success; +} + + +bool GraphicalObjectMultiPath::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + + bool success = create(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " create failed"; + return false; + } + + success = BaseClass::updateViewImpl(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " updateView failed"; + return false; + } + + success = updatePositions(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " success = " << success + << " drawOrder = " << m_graphic->zIndex(); + return success; +} + +bool GraphicalObjectMultiPath::updatePositions() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + + bool success = true; + + int pathIdxToInsert = 0; + const int minPoints = this->pathType() == GraphicalObjectPathType::Polyline ? 2 : 3; + + QSharedPointer builder; + + // some preparations: ( do no check isMultiPoint / isArea: m_positions should typically be empty!!! + switch (m_pathType) + { + case GraphicalObjectPathType::Polyline: + builder = QSharedPointer(new Esri::ArcGISRuntime::PolylineBuilder(spatialReference())); + + break; + case GraphicalObjectPathType::Polygon: + builder = QSharedPointer(new Esri::ArcGISRuntime::PolygonBuilder(spatialReference())); + break; + } // switch + + QSharedPointer partCollection = + QSharedPointer(new Esri::ArcGISRuntime::PartCollection(builder->spatialReference())); + + + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << ", m_positionsCont.size() /#rings=" << m_positionsCont.size(); + + QMutableMapIterator > mapIt(m_positionsCont); + // + // LOOP PER SINGLE POLY + // + while (mapIt.hasNext()) + { + mapIt.next(); + const int key = mapIt.key(); + + const SimpleValue &posData = mapIt.value(); + const PositionCont &posCont = posData.value(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << ", key / #ring=" << key + << ", rings.size()=" << posCont.size(); + + // maybe some pathIdx are cleared by clearPath() or similar: + if (posCont.isEmpty()) + { + DEBUG_LOG(m_logger) << LOGFUNCTION + << " posCont empty, cleared? -> remove/cleanup" + << " pathIdxToInsert = " << pathIdxToInsert + << " mapIt.key() = " << key; + mapIt.remove(); + continue; + } + + if (posCont.count() < minPoints) + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " incorrect number of positions" + << " (" << posCont.count() << ")" + << ", minPoints=" << minPoints + << ", pathIdxToInsert = " << pathIdxToInsert + << ", mapIt.key() = " << key + << ", m_debugInfo = " << m_debugInfo; + + success = false; + } + else + { + Esri::ArcGISRuntime::Part part(builder->spatialReference()); + + // + // LOOP PER POINT + // + for (auto pos : std::as_const(posCont)) + { + part.addPoint(pos.x(), pos.y(), pos.z()); + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << ", key / #ring=" << key + << ", addPoint=" << pos; + + } + + partCollection->addPart(&part); + } // if (posCont.count() < minPoints) + + } // while + + builder->setParts(partCollection.get()); + + // + // builder to geometry; + // + switch (m_pathType) + { + case GraphicalObjectPathType::Polyline: + { + const Esri::ArcGISRuntime::Polyline &multipartPolyline = + static_cast(builder.data())->toPolyline(); + m_graphic->setGeometry(multipartPolyline); + assignLineStyleToGraphic(); + } + break; + case GraphicalObjectPathType::Polygon: + { + const Esri::ArcGISRuntime::Polygon &multipartPolygon = + static_cast(builder.data())->toPolygon(); + m_graphic->setGeometry(multipartPolygon); + //assignLineStyleToGraphic(); + assignFillStyleToGraphic(); + } + break; + } // switch + + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"; + success = addUpdateGraphic(); + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " success = " << success + << " drawOrder = " << m_graphic->zIndex(); + return success; +} + +bool GraphicalObjectMultiPath::hasPosCont(int idx) const +{ + return m_positionsCont.contains(idx); +} + +SimpleValue &GraphicalObjectMultiPath::getPosCont(int idx) +{ + return m_positionsCont[idx]; +} + +const SimpleValue GraphicalObjectMultiPath::getPosCont(int idx) const +{ + return m_positionsCont[idx]; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_multi_path.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_multi_path.h new file mode 100755 index 000000000..5ca871e57 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_multi_path.h @@ -0,0 +1,105 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectPath + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "graphical_object_path.h" + + +// Esri + + +// Qt +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + + +class SITUATIONMAP_EXPORT GraphicalObjectMultiPath : public AbstractGraphicalObject +{ + + using BaseClass = AbstractGraphicalObject; + +public: + + GraphicalObjectMultiPath(const GraphicalObjectPathType type + , const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , UniqueMapId uniqueMapId); + + ~GraphicalObjectMultiPath(); + + GraphicalObjectPathType pathType() const + { + return m_pathType; + } + + virtual PositionCont path(int pathIndex) const; + virtual void setPath(int pathIndex, const PositionCont &positions); + + /*! \name Subsidiary polyline/polygons */ + //@{ + virtual void clearPath(int pathIndex); + virtual void clearAllPaths(); + virtual QList pathIndexInUse(); + virtual void reduceTo(const QList &remainingPathIndexes); + //@} + +protected: + // Internal/Admin + bool updatePositions(); + bool changed() override + { + bool baseClassChanged = BaseClass::changed(); + if (baseClassChanged) + return true; + + for (const SimpleValue &posData : std::as_const(m_positionsCont)) + { + if (posData.changed()) + return true; + } + return false; + } + + void resetChanged() override + { + BaseClass::resetChanged(); + + for (SimpleValue &posData : m_positionsCont) + posData.reset(); + } + + bool create() override; + bool updateViewImpl() override; + + // logical + const GraphicalObjectPathType m_pathType; + //bool updatePositions(); + +private: + // Attention: will contain 'positions' of idx == 1 and higher : + bool hasPosCont(int idx) const; + SimpleValue &getPosCont(int idx); + const SimpleValue getPosCont(int idx) const; + + QMap > m_positionsCont; + // : Attention: will contain 'positions' of idx == 1 and higher + + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_path.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_path.cpp new file mode 100755 index 000000000..729152d5c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_path.cpp @@ -0,0 +1,260 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectPath + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "graphical_object_path.h" +#include // operator<< + + +// ESRI +#include +#include +#include +#include +#include +#include +#include + +// Qt + + +namespace SituationMap +{ + +QLatin1String toString(const GraphicalObjectPathType subType) +{ + switch (subType) + { + case GraphicalObjectPathType::Polyline: + return QLatin1String("Polyline"); + break; + case GraphicalObjectPathType::Polygon: + return QLatin1String("Polygon"); + break; + } // switch + return QLatin1String("GraphicalObjectPathTypeERROR"); +} + + +GraphicalObjectPath::GraphicalObjectPath(const GraphicalObjectPathType type + , const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId) + : BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId) + , m_pathType(type) + , m_logger(getLogger("GraphicalObjectPath")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " m_pathType=" << m_pathType; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " m_pathType=" << m_pathType; +} + +GraphicalObjectPath::~GraphicalObjectPath() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " m_pathType=" << m_pathType; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " m_pathType=" << m_pathType; +} + +bool GraphicalObjectPath::create() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + + bool success = BaseClass::create(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " create failed"; + return false; + } + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " success = " << success + // << " drawOrder = " << m_graphic->drawOrder() + ; + return success; +} + + +bool GraphicalObjectPath::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " m_positions= " << m_positions.value() + << ", m_maxSegmentLength=" << m_maxSegmentLength; + + QSharedPointer builder; + + // some checks: + switch (m_pathType) + { + case GraphicalObjectPathType::Polyline: + if (!this->isMultiPoint()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " incorrect number of positions" + << " (" << this->positions().count() << ")"; + return false; + } + builder = QSharedPointer(new Esri::ArcGISRuntime::PolylineBuilder(spatialReference())); + + break; + case GraphicalObjectPathType::Polygon: + if (!this->isArea()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " incorrect number of positions" + << " (" << this->positions().count() << ")"; + return false; + } + builder = QSharedPointer(new Esri::ArcGISRuntime::PolygonBuilder(spatialReference())); + break; + } // switch + + + if (! builder) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " no builder"; + return false; + } + + bool success = create(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " create failed"; + return false; + } + + success = BaseClass::updateViewImpl(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " updateView failed"; + return false; + } + + + // real work: + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " update geometry"; + + + const PositionCont &posCont = m_positions.value(); + for (const Point3D &pos : posCont) + builder->addPoint(pos.x(), pos.y(), pos.z()); + + if (! builder->isSketchValid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " builder -> sketch invalid"; + return false; + } + + // + // apply attributes: + // + + // N. Thoden, 13.02.2018 + // Case #02062713 [BOS] BasicSceneView_lineNotComplete + // If a line has a line of ~10km (Borderline) + // the line may 'move' on the ground (3D, RenderMode::Dynamic, SurfarcePlacement::Draped) + // To avoid these kind of effect, the advice is to densify the geometry + // [similar: Case #02054233 [BOS] LineArtefacts in 3D -> + // BUG-000111565 : Lines created on the GraphicsOverlay with "Dynamic" Rendering mode on 3D doesn't render completely when zooming in] + + // + // N. Thoden, 29.03.2018 + // Consulting with Luke Smallwood / using the V100.2.1. debug libs: + // densify leads to an assert, so avoid densify + // (until now densify does not lead to an improvement..) + + // + // N. Thoden, 19.04.2021 + // [GUEZE-495]: 3D Lagedarstellung Trefferlinien + // Linie fehlt teilweise + // jetzt (typeweise) per m_maxSegmentLength aktivierbar + + switch (m_pathType) + { + case GraphicalObjectPathType::Polyline: + { + const Esri::ArcGISRuntime::Polyline &polyline = + static_cast(builder.data())->toPolyline(); + if (m_maxSegmentLength > 0) + { + m_graphic->setGeometry(Esri::ArcGISRuntime::GeometryEngine::densifyGeodetic(polyline, + m_maxSegmentLength, + Esri::ArcGISRuntime::LinearUnit::meters(), + Esri::ArcGISRuntime::GeodeticCurveType::Geodesic)); + } else { + m_graphic->setGeometry(polyline); + } // if (densify) + assignLineStyleToGraphic(); + } + break; + case GraphicalObjectPathType::Polygon: + { + const Esri::ArcGISRuntime::Polygon &polygon = + static_cast(builder.data())->toPolygon(); + + if (m_maxSegmentLength > 0) + { + m_graphic->setGeometry(Esri::ArcGISRuntime::GeometryEngine::densifyGeodetic(polygon, + m_maxSegmentLength, + Esri::ArcGISRuntime::LinearUnit::meters(), + Esri::ArcGISRuntime::GeodeticCurveType::Geodesic)); + } else { + m_graphic->setGeometry(polygon); + } + assignFillStyleToGraphic(); + } + break; + } // switch + + const int count = AbstractGraphicalObject::countOfPolygonFirstPart(m_graphic->geometry()); + if (count > 1000) + WARNING_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << ", huge amount of vertices, countOfPolygonFirstPart = " << count; + else + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << ", amount of vertices, countOfPolygonFirstPart = " << count; + + success = addUpdateGraphic(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " success = " << success + << " drawOrder = " << m_graphic->zIndex(); + return success; +} + +double GraphicalObjectPath::maxSegmentLength() const +{ + return m_maxSegmentLength; +} + +void GraphicalObjectPath::setMaxSegmentLength(double maxSegmentLength) +{ + m_maxSegmentLength = maxSegmentLength; +} + + +//----------------------------------------------------------------------------- +// output operator: +//----------------------------------------------------------------------------- +std::ostream &operator<<(std::ostream &stream, + const GraphicalObjectPathType &value) +{ + stream << toString(value).latin1(); + return stream; +} + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_path.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_path.h new file mode 100755 index 000000000..5bf117ffc --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_path.h @@ -0,0 +1,78 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectPath + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_graphical_object.h" + +// Esri + + +// Qt + + +// forward: + + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +enum class GraphicalObjectPathType {Polyline, Polygon}; +QLatin1String toString(const GraphicalObjectPathType subType); + + +class SITUATIONMAP_EXPORT GraphicalObjectPath : public AbstractGraphicalObject +{ + + using BaseClass = AbstractGraphicalObject; + +public: + + explicit GraphicalObjectPath(const GraphicalObjectPathType type + , const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId); + + ~GraphicalObjectPath(); + + GraphicalObjectPathType pathType() const + { + return m_pathType; + } + + + double maxSegmentLength() const; + void setMaxSegmentLength(double maxSegmentLength); + +protected: + // Internal/Admin + bool create() override; + bool updateViewImpl() override; + + // logical + const GraphicalObjectPathType m_pathType; + double m_maxSegmentLength{-1}; + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + +//----------------------------------------------------------------------------- +// output operator: +//----------------------------------------------------------------------------- +SITUATIONMAP_EXPORT std::ostream &operator<< (std::ostream &stream, + const GraphicalObjectPathType &value); + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_picture_marker.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_picture_marker.cpp new file mode 100755 index 000000000..f6c85d5ad --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_picture_marker.cpp @@ -0,0 +1,572 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectPictureMarker + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "graphical_object_picture_marker.h" +#include // operator<< +#include +#include + + +// ESRI +#include +#include +#include +#include + + +// Qt +#include +#include + + +namespace SituationMap +{ +/** + * @brief getSimpleMarkerSymbolStyle + * @param markerSymbolType type to get + * @param filePath filePath of requested marker; if empty -> use return value, if !empty -> use filePath and get file out of qrc + * @param hotSpotPercent -0.5 to 0.5; e.g. x=center, y=bottom -> -0.5 + * @return corrosponding Esri::ArcGISRuntime::SimpleMarkerSymbolStyle, see filePath + */ +Esri::ArcGISRuntime::SimpleMarkerSymbolStyle getSimpleMarkerSymbolStyle(MarkerSymbolType markerSymbolType, + QString &filePath, + QPointF *hotSpotPercent = nullptr) +{ + if (hotSpotPercent) + *hotSpotPercent = QPointF(); + + filePath.clear(); + + Esri::ArcGISRuntime::SimpleMarkerSymbolStyle esriSMSS = + Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle; + + switch (markerSymbolType) + { + case SituationMap::MarkerSymbolType::Undefined: + esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle; + break; + + case SituationMap::MarkerSymbolType::Circle: + esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle; + break; + case SituationMap::MarkerSymbolType::Cross: + esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Cross; + break; + case SituationMap::MarkerSymbolType::Diamond: + esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Diamond; + break; + case SituationMap::MarkerSymbolType::Square: + esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Square; + break; + case SituationMap::MarkerSymbolType::X: + esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::X; + break; + case SituationMap::MarkerSymbolType::Triangle: + esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Triangle; + break; + + case SituationMap::MarkerSymbolType::PushPin2D: + esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle; + filePath = QLatin1String(":/situationMap/pushPinRedClipArt.png"); + if (hotSpotPercent) + *hotSpotPercent = QPointF(0, -0.5); + + break; + } //switch + + return esriSMSS; +} + +GraphicalObjectPictureMarker::GraphicalObjectPictureMarker(const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId) + : BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId) + , m_logger(getLogger("GraphicalObjectPictureMarker")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +GraphicalObjectPictureMarker::~GraphicalObjectPictureMarker() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + + resetPictureMarkerSymbol(); + resetImage(); + resetSimpleMarkerSymbol(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +void GraphicalObjectPictureMarker::setImage(const QImage &image, const QSize &defaultSize) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " visibility=" << m_visibility + << " image.size()=" << image.size() + << " defaultSize=" << defaultSize + << " m_imageDefaultSize=" << m_imageDefaultSize + << " m_imageChanged=" << m_imageChanged + << " m_imageDefaultSize.changed()=" << m_imageDefaultSize.changed(); + resetSimpleMarkerSymbol(); + + if (m_image != image) + { + m_image = image; + m_imageChanged = true; + } + + // store defautlSize (member shouldn´t be modified) + m_imageDefaultSize = defaultSize; + if (m_imageDefaultSize.changed()) + m_imageChanged = true; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " visibility=" << m_visibility + << " image.size()=" << image.size() + << " defaultSize=" << defaultSize + << " m_imageDefaultSize=" << m_imageDefaultSize + << " m_imageChanged=" << m_imageChanged + << " m_imageDefaultSize.changed()=" << m_imageDefaultSize.changed(); +} + +void GraphicalObjectPictureMarker::setPictureMarkerSymbol(Esri::ArcGISRuntime::PictureMarkerSymbol *pms, QSize defaultSize) +{ + resetSimpleMarkerSymbol(); + resetImage(); + + if (m_isOwnPms) + { + delete m_pictureMarkerSymbol; + m_pictureMarkerSymbol = nullptr; + } + + if (m_pictureMarkerSymbol == pms && + m_imageDefaultSize.value() == defaultSize) + return; + m_isOwnPms = false; + m_pictureMarkerSymbol = pms; + m_imageDefaultSize = defaultSize; + m_imageChanged = true; +} + +void GraphicalObjectPictureMarker::resetImage() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + m_image = QImage(); + m_imageChanged = true; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +void GraphicalObjectPictureMarker::resetPictureMarkerSymbol() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + if (m_isOwnPms) + delete m_pictureMarkerSymbol; + m_pictureMarkerSymbol = nullptr; + if (m_workaorundTimer) + m_workaorundTimer->stop(); + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +void GraphicalObjectPictureMarker::resetSimpleMarkerSymbol() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + delete m_simpleMarkerSymbol; + m_simpleMarkerSymbol = nullptr; + m_markerSymbolType = MarkerSymbolType::Undefined; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + + +MarkerSymbolType GraphicalObjectPictureMarker::markerSymbolType() +{ + return m_markerSymbolType.value(); +} + +int GraphicalObjectPictureMarker::markerSymbolSize() +{ + return m_markerSize.value(); +} + +void GraphicalObjectPictureMarker::setMarkerSymbol(MarkerSymbolType markerSymbolType, + int size, const QColor &color) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " color=" << color; + + resetImage(); + + m_markerSymbolType = markerSymbolType; + m_markerSize = size; + m_markerColor = color; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +QSize GraphicalObjectPictureMarker::currentSize() const +{ + return m_currentSize; +} + +bool GraphicalObjectPictureMarker::create() +{ + return BaseClass::create(); +} + +int GraphicalObjectPictureMarker::xOffset() const +{ + return m_externalOffset.value().x(); +} +void GraphicalObjectPictureMarker::setXOffset(int xOffset) +{ + m_externalOffset.refValue().rx() = xOffset; +} +int GraphicalObjectPictureMarker::yOffset() const +{ + return m_externalOffset.value().y(); +} +void GraphicalObjectPictureMarker::setYOffset(int yOffset) +{ + m_externalOffset.refValue().ry() = yOffset; +} + +float GraphicalObjectPictureMarker::rotationAngle() +{ + return m_angle.value(); +} +void GraphicalObjectPictureMarker::setRotationAngle(float angle) +{ + m_angle = angle; +} + + +bool GraphicalObjectPictureMarker::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " origin =" << this->origin() // .x() << " , " << this->origin().y() + << " visibility=" << m_visibility + << " pickable=" << m_pickable + << " pickable.changed=" << m_pickable.changed() + << " selected=" << m_selected + << " m_angle = " << m_angle.value() + << " m_drawOrder = " << m_drawOrder + << " m_image.size() = " << m_image.size() + << " m_imageChanged = " << m_imageChanged + << " m_imageDefaultSize=" << m_imageDefaultSize + << " m_imageDefaultSize.changed()=" << m_imageDefaultSize.changed() + << " m_scale = " << m_scale.value(); + + // some checks: + if (m_positions.value().size() < 1) + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " debugInfo='" << m_debugInfo << "'" + << " incorrect number of positions" + << " (" << this->positions().count() << ")"; + return false; + } + + // + // Image or Marker ??? + // + QString filePath; + QPointF hotSpotPercent; + Esri::ArcGISRuntime::SimpleMarkerSymbolStyle esriSMSS = + getSimpleMarkerSymbolStyle(m_markerSymbolType.value(), + filePath, + &hotSpotPercent); + m_markerHotSpotPercent = hotSpotPercent; + + if (m_markerSymbolType.value() != MarkerSymbolType::Undefined) + { + if (m_markerSize.changed() || + m_markerSymbolType.changed() || + m_markerColor.changed() || + m_markerSymbolType.changed() || + m_angle.changed() || + m_markerHotSpotPercent.changed()) + { + if (!reCreateSymbol(filePath, esriSMSS)) + return false; + } + } + else + { + if (m_image.isNull() && (!m_isOwnPms && m_pictureMarkerSymbol == nullptr)) + { + // in context of class 'TacticalAppearance' the PictureMarker exist always... + DEBUG_LOG(m_logger) << LOGFUNCTION << " no image and m_markerSymbolType==Undefined -> nothing to do"; + // -> nothing to do: + return true; // -> nothing to do: + } + + if (m_scale.changed() || + m_imageChanged || + m_markerSymbolType.changed() || + m_markerSize.changed() || + m_angle.changed() || + !m_pictureMarkerSymbol) + { + if (!reCreateSymbol(filePath, esriSMSS)) + return false; + } + } + + const PositionCont &posCont = m_positions.value(); + if (posCont.size() == 1) + { + const Point3D &origin = this->origin(); + Esri::ArcGISRuntime::Point originGeometry(origin.x(), + origin.y(), + origin.z(), + spatialReference()); + m_graphic->setGeometry(originGeometry); + } + else + { + // Eception: Layer with name "AES2" should NOT have no renderer 8 to enable multipoint graphics + // [GUEZE-496] RPL Nr 770: 3D Lagedarstellung Einfrieren der SW Appl. + + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " posCont.size() = " << posCont.size(); + Esri::ArcGISRuntime::MultipointBuilder builder(spatialReference()); + for (const Point3D &pos : posCont) + builder.points()->addPoint(pos.x(), pos.y(), pos.z()); + + if (! builder.isSketchValid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " builder -> sketch invalid"; + return false; + } + m_graphic->setGeometry(builder.toGeometry()); + } + + bool success = BaseClass::updateViewImpl(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " updateView failed"; + return false; + } + + + success = addUpdateGraphic(); + + + QSize imgSize = m_image.size(); + QSizeF pmSize; + if (m_pictureMarkerSymbol) + pmSize = QSizeF(m_pictureMarkerSymbol->width(), m_pictureMarkerSymbol->height()); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" +// << " success=" << success + << " m_drawOrder = " << m_drawOrder + << " m_graphic->drawOrder = " << m_graphic->zIndex() + << " m_image->size() = " << imgSize + << " pictureMarkerSymbol->size() = " << pmSize; + return success; +} + +bool GraphicalObjectPictureMarker::reCreateSymbol(const QString &filePath, + const Esri::ArcGISRuntime::SimpleMarkerSymbolStyle esriSMSS) +{ + const bool imageRequired = !m_image.isNull() || !filePath.isEmpty(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " filePath = '" << filePath << "'" + << " m_image->size() = " << m_image.size() + << " m_imageChanged = " << m_imageChanged + << " m_angle = " << m_angle.value() + << " m_currentSize = " << m_currentSize + << " m_imageDefaultSize = " << m_imageDefaultSize.value() + << " m_imageDefaultSize.changed() = " << m_imageDefaultSize.changed(); + + // + // Now we know what to use: Image or Marker + // + + if (!create()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " create failed"; + return false; + } + + // what is used currently? + // image or m_simpleMarkerSymbol(esri)? + + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " imageRequired=" << imageRequired + << " m_pictureMarkerSymbol=" << m_pictureMarkerSymbol + << " m_simpleMarkerSymbol=" << m_simpleMarkerSymbol; + + if (!filePath.isEmpty()) + { + // e.g. case PushPin2D + QImage img; + if (img.load(filePath)) + { + int iSize = qRound(m_markerSize.value() * m_scale.value()); + m_currentSize = QSize(iSize, iSize); + img = img.scaledToHeight(iSize, Qt::SmoothTransformation); + + QColor localColor = m_markerColor.value(); + + if (m_markerSymbolType.value() == MarkerSymbolType::PushPin2D && localColor.isValid()) + { + QColor colorToBeReplaced(255, 0, 0); + AdvancedSymbology::ExtensionRenderer::replaceColor(img, colorToBeReplaced.rgba(), localColor.rgba()); + } + setImage(img); + } + else + { + ERROR_LOG(m_logger) << LOGFUNCTION << " loading image failed, debugInfo='" << m_debugInfo << "'" + << " path='" << filePath << "'"; + return false; + } + } + + + // + // Transfer data to esriSymbols: + // + + // Image: + if (!m_image.isNull() || (!m_isOwnPms && m_pictureMarkerSymbol)) + { + resetSimpleMarkerSymbol(); + + QSize realImageSize; + if (!m_isOwnPms && m_pictureMarkerSymbol) + { + if (m_imageDefaultSize.value().isEmpty()) + m_currentSize = m_image.size() * m_scale.value(); + else + m_currentSize = m_imageDefaultSize.value() * m_scale.value(); + realImageSize = m_currentSize; + } + else + { + // scale localImage by m_scale + QImage localImage; + if (!filePath.isEmpty()) + { + // case PushPin2D -> already scaled by m_scale. So don´t scale again: + localImage = m_image; + m_currentSize = localImage.size(); + } + else + { + if (m_imageDefaultSize.value().isEmpty()) + m_currentSize = m_image.size() * m_scale.value(); + else + m_currentSize = m_imageDefaultSize.value() * m_scale.value(); + + // scaled required? + if (m_image.size() != m_currentSize) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " SCALE FROM " << m_image.size() << " TO " << m_currentSize; + localImage = m_image.scaled(m_currentSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); + ++m_scaleCounter4Moduletest; + } else { + localImage = m_image; + m_currentSize = localImage.size(); + } + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " m_scaleCounter4Moduletest = " << m_scaleCounter4Moduletest + << ", localImage.size() = " << localImage.size(); + + delete m_pictureMarkerSymbol; + m_pictureMarkerSymbol = new Esri::ArcGISRuntime::PictureMarkerSymbol(localImage); + realImageSize = localImage.size(); + + + // Workaround : + // Norbert Thoden, 11.06.2020: + // do some calls again to fix ESRI-Bug: + // Bug: new Image('localImage#) / pictureMarkerSymbol, but sometimes it will not be shown :-( + // Using try and error, we recognize that an additional call seems to fix that issue: + if (!m_workaorundTimer) + { + m_workaorundTimer = new QTimer(m_graphic); + m_workaorundTimer->setInterval(150); + m_workaorundTimer->setSingleShot(true); + QObject::connect(m_workaorundTimer, &QTimer::timeout, m_graphic, [this]() { + if (!m_graphic) + return; + m_graphic->setZIndex(m_drawOrder.value()); + m_graphic->setVisible(m_visibility.value()); + m_graphic->setSelected(m_selected.value()); + }); + } + m_workaorundTimer->start(); + // : Workaround + + } + m_imageChanged = false; + + // set image to pms AND ALSO the Heigth and Width!!! + + // // !!! Important !!! + // // Case #01713501 - PictureMarker-offset + // // Esri::PictureMarkerSymbol:: setHeight/setWidth -> typographic points. + // // 36pt -> 48pixel -> factor 0.75 + // double pixelToPointFactor = 0.71; + // m_pictureMarkerSymbol->setHeight(m_image->size().height() * pixelToPointFactor); + // m_pictureMarkerSymbol->setWidth (m_image->size().width() * pixelToPointFactor); + // // !!! Important !!! + + QPointF markerHotSpot = QPointF(realImageSize.width() / 2.0 * m_markerHotSpotPercent.value().x(), + realImageSize.height() / 2.0 * m_markerHotSpotPercent.value().y()); + + QPointF finalOffset = (markerHotSpot.toPoint() * -1) * 1.33 // 1.33; // try and error (48pixel / 36pt) + + + (m_externalOffset.value() * 0.75); // 0.75; // try and error (36pt / 48pixel) + + m_pictureMarkerSymbol->setOffsetX(finalOffset.x()); + m_pictureMarkerSymbol->setOffsetY(finalOffset.y()); + + m_pictureMarkerSymbol->setAngle(m_angle.value()); + + m_graphic->setSymbol(m_pictureMarkerSymbol); + } + else + { + resetPictureMarkerSymbol(); + resetImage(); + if (!m_simpleMarkerSymbol) + m_simpleMarkerSymbol = new Esri::ArcGISRuntime::SimpleMarkerSymbol(); + + m_simpleMarkerSymbol->setSize(m_markerSize.value() * m_scale.value()); + m_simpleMarkerSymbol->setStyle(esriSMSS); + m_simpleMarkerSymbol->setAngle(m_angle.value()); + + m_simpleMarkerSymbol->setOffsetX(m_externalOffset.value().x()); + m_simpleMarkerSymbol->setOffsetY(m_externalOffset.value().y()); + + m_simpleMarkerSymbol->setColor(m_markerColor.value()); + + m_graphic->setSymbol(m_simpleMarkerSymbol); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " filePath = '" << filePath << "'" + << " m_image->size() = " << m_image.size() + << " m_imageChanged = " << m_imageChanged + << " m_currentSize = " << m_currentSize + << " m_imageDefaultSize = " << m_imageDefaultSize.value() + << " m_imageDefaultSize.changed() = " << m_imageDefaultSize.changed(); + + return true; +} + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_picture_marker.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_picture_marker.h new file mode 100755 index 000000000..fc66b4f50 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_picture_marker.h @@ -0,0 +1,153 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectPictureMarker + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_graphical_object.h" +#include +#include + + +// Esri +#include +#include + + +// Qt +#include + + +// forward: +namespace Esri +{ +namespace ArcGISRuntime +{ +class Graphic; +class GraphicsOverlay; +class PictureMarkerSymbol; +class SimpleMarkerSymbol; +} +} + +class QTimer; + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +class SITUATIONMAP_EXPORT GraphicalObjectPictureMarker : public AbstractGraphicalObject +{ + + using BaseClass = AbstractGraphicalObject; + +public: + GraphicalObjectPictureMarker(const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId); + + ~GraphicalObjectPictureMarker(); + + // Data access: + const QImage &image() const { return m_image; } + QSize imageDefaultSize() const { return m_imageDefaultSize.value(); } + void setImage(const QImage &image, const QSize &defaultSize = QSize()); + + void setPictureMarkerSymbol(Esri::ArcGISRuntime::PictureMarkerSymbol *pms, QSize defaultSize); + + virtual MarkerSymbolType markerSymbolType(); + virtual int markerSymbolSize(); + virtual void setMarkerSymbol(MarkerSymbolType markerSymbolType, + int size, + const QColor &color = QColor()); + + virtual QSize currentSize() const; + + // Offset (x: to right ; y: to north) + virtual int xOffset() const; + virtual void setXOffset(int xOffset); + virtual int yOffset() const; + virtual void setYOffset(int yOffset); + + // rotation / angle + virtual float rotationAngle(); + virtual void setRotationAngle(float angle); + +protected: + + // Internal/Admin + bool create() override; + + bool changed() override + { + return BaseClass::changed() + || m_markerSymbolType.changed() + || m_markerSize.changed() + || m_markerColor.changed() + || m_angle.changed() + || m_markerHotSpotPercent.changed() + || m_externalOffset.changed() + || m_imageChanged + ; + } + + void resetChanged() override + { + BaseClass::resetChanged(); + m_markerSymbolType.reset(); + m_markerSize.reset(); + m_markerColor.reset(); + m_angle.reset(); + m_markerHotSpotPercent.reset(); + m_externalOffset.reset(); + m_imageChanged = false; + + m_imageDefaultSize.reset(); + } + + bool updateViewImpl() override; + + void resetImage(); + void resetPictureMarkerSymbol(); + void resetSimpleMarkerSymbol(); + bool reCreateSymbol(const QString &filePath, + const Esri::ArcGISRuntime::SimpleMarkerSymbolStyle esriSMSS); + + // used for image: + QImage m_image; + SimpleValue m_imageDefaultSize; + Esri::ArcGISRuntime::PictureMarkerSymbol *m_pictureMarkerSymbol = nullptr; + bool m_imageChanged = true; + bool m_isOwnPms = true; + + // used marker: + Esri::ArcGISRuntime::SimpleMarkerSymbol *m_simpleMarkerSymbol = nullptr; + QTimer *m_workaorundTimer = nullptr; // for re-new of Esri::ArcGISRuntime::PictureMarkerSymbol + + QSize m_currentSize; + int m_scaleCounter4Moduletest = 0; + + + SimpleValue m_markerSymbolType = MarkerSymbolType::Undefined; + SimpleValue m_markerSize = 10; + SimpleValue m_markerColor = QColor(Qt::red); + + SimpleValue m_angle = 0; /// + SimpleValue m_markerHotSpotPercent; ///< Store marker-specific offset. + SimpleValue m_externalOffset; ///< Stores external offset. + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_range.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_range.cpp new file mode 100755 index 000000000..2f6725743 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_range.cpp @@ -0,0 +1,163 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectRange + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "graphical_object_range.h" +#include // operator<< + + +// ESRI +#include +#include +#include +#include + + +// Qt + + +namespace SituationMap +{ + + +GraphicalObjectRange::GraphicalObjectRange(const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId) + : BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId) + , m_logger(getLogger("GraphicalObjectRange")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +GraphicalObjectRange::~GraphicalObjectRange() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + + +int GraphicalObjectRange::range() const +{ + return m_range.value(); +} + +float GraphicalObjectRange::angle() const +{ + return m_angleDegree.value(); +} + +void GraphicalObjectRange::setRange(int range, float angleDegree) +{ + m_angleDegree = angleDegree; + m_range = range; +} + + + +bool GraphicalObjectRange::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + + EllipseParameters ellipsePara; + ellipsePara.setLengthX(m_range.value()); + ellipsePara.setLengthY(m_range.value()); + ellipsePara.setDirectionDegree(m_angleDegree.value()) ; + ellipsePara.setDirectionDegree((m_angleDegree.value() * -1) + 90); + ellipsePara.setMaxSegmentLength(10); + ellipsePara.setMaxPointCount(30); // limit to 30 to reduce the number of vertices. If to high, keep performance impact to 3D in mind!!! + + this->setEllipseParameters(ellipsePara); + + bool success = BaseClass::updateViewImpl(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " drawOrder = " << m_graphic->zIndex() + << " success = " << success; + return success; +} + + +bool GraphicalObjectRange::createEllipse(Esri::ArcGISRuntime::Polygon &ellipse) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " range=" << m_range + << " angle=" << m_angleDegree; + + bool success = BaseClass::createEllipse(ellipse); + + if (success && + !ellipse.isEmpty() && + !ellipse.parts().isEmpty() && + !ellipse.parts().part(0).isEmpty()) + { + const Point3D ¢er = this->origin(); + + // 'modify' ellipe (by copying, adding and re-assigning): + + // part 0 - ellipse + // we have to copy the point since the ellipse does not contain the z value anymore :-( + Esri::ArcGISRuntime::Part partCircle(ellipse.spatialReference()); + const Esri::ArcGISRuntime::ImmutablePart &im = ellipse.parts().part(0); + + for (int i = 0; i < im.pointCount(); ++i) + { + const Esri::ArcGISRuntime::Point &p = im.point(i); + partCircle.addPoint(Esri::ArcGISRuntime::Point(p.x(), + p.y(), + center.z() // p.z() + )); + } + + // part 1 - line to center + Esri::ArcGISRuntime::Part partLine(ellipse.spatialReference()); + partLine.addPoint(im.startPoint().x(), + im.startPoint().y(), + center.z()); + partLine.addPoint(center.x(), + center.y(), + center.z()); + // a polyGON requiree to have at least 3 points: + partLine.addPoint(center.x(), + center.y(), + center.z()+0.01); + + // combine them to a PartCollection: + Esri::ArcGISRuntime::PartCollection pc(ellipse.spatialReference()); + pc.addPart(&partCircle); + pc.addPart(&partLine); + + Esri::ArcGISRuntime::PolygonBuilder builder(ellipse.spatialReference()); + builder.setParts(&pc); + + ellipse = builder.toPolygon(); + + success = true; + + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"; + return success; +} + + + +//----------------------------------------------------------------------------- +// output operator: +//----------------------------------------------------------------------------- +std::ostream &operator<<(std::ostream &stream, + const GraphicalObjectRange &value) +{ + stream << value.toString(); + return stream; +} + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_range.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_range.h new file mode 100755 index 000000000..5151ea224 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_range.h @@ -0,0 +1,87 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectRange + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "graphical_object_ellipse.h" + + +// Esri + + +// Qt + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +class SITUATIONMAP_EXPORT GraphicalObjectRange : public GraphicalObjectEllipse +{ + + using BaseClass = GraphicalObjectEllipse; + +public: + GraphicalObjectRange(const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId); + + ~GraphicalObjectRange(); + + int range() const; + float angle() const; + + void setRange(int range, float angleDegree); + + + +protected: + // Internal/Admin + // bool create() override; + + bool changed() override + { + return BaseClass::changed() + || m_angleDegree.changed() + || m_range.changed(); + } + + void resetChanged() override + { + BaseClass::resetChanged(); + m_angleDegree.reset(); + m_range.reset(); + } + + + bool updateViewImpl() override; + + + bool createEllipse(Esri::ArcGISRuntime::Polygon &ellipse) override; + + +private: + SimpleValue m_angleDegree = 0; + SimpleValue m_range = 0; + LOGGER_T m_logger; ///< Interface of the Logging-Facility + + +}; + +//----------------------------------------------------------------------------- +// output operator: +//----------------------------------------------------------------------------- +std::ostream &operator<< (std::ostream &stream, + const GraphicalObjectRange &value); + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_renderer_attributes.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_renderer_attributes.cpp new file mode 100755 index 000000000..cf24f1add --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_renderer_attributes.cpp @@ -0,0 +1,135 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectRendererAttributes + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "graphical_object_renderer_attributes.h" +#include // operator<< +#include + + +// ESRI +#include +#include + + +// Qt +#include +#include + +namespace SituationMap +{ + +GraphicalObjectRendererAttributes::GraphicalObjectRendererAttributes(const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId) + : BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId) + , m_logger(getLogger("GraphicalObjectRendererAttributes")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +GraphicalObjectRendererAttributes::~GraphicalObjectRendererAttributes() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +void GraphicalObjectRendererAttributes::setAttributeMap(const QVariantMap &attribMap) +{ + m_attributeMap = attribMap; +} + +QVariantMap GraphicalObjectRendererAttributes::attributeMap() const +{ + return m_attributeMap.value(); +} + +bool GraphicalObjectRendererAttributes::create() +{ + return BaseClass::create(); +} + +bool GraphicalObjectRendererAttributes::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " origin =" << this->origin() // .x() << " , " << this->origin().y() + << " visibility=" << m_visibility + << " pickable=" << m_pickable + << " pickable.changed=" << m_pickable.changed() + << " selected=" << m_selected + << " m_attributeMap = " << m_attributeMap.value().size() + << " m_drawOrder = " << m_drawOrder; + + // some checks: + if (!isSinglePoint()) + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " debugInfo='" << m_debugInfo << "'" + << " incorrect number of positions" + << " (" << this->positions().count() << ")"; + return false; + } + + bool success = create(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " create failed"; + return false; + } + + const Point3D &origin = this->origin(); + m_graphic->setGeometry(Esri::ArcGISRuntime::Point(origin.x(), + origin.y(), + origin.z(), + spatialReference())); + + if (m_attributeMap.changed()) + { + QVariantMap cur = m_graphic->attributes()->attributesMap(); + + if (m_logger.isDebugEnabled()) + qCritical() << " debugInfo='" << m_debugInfo << "'" << ", QVariantMap before unite " << cur; + + cur.unite(m_attributeMap.value()); + + if (m_logger.isDebugEnabled()) + qCritical() << " debugInfo='" << m_debugInfo << "'" << ", QVariantMap after unite " << cur; + + m_graphic->attributes()->setAttributesMap(cur); + } + + // keep anchor like Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Bottom in mind. + // assigned during setup the Renderer (LayerFactory, AdapterDds...) + + success = BaseClass::updateViewImpl(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " updateView failed"; + return false; + } + + success = addUpdateGraphic(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " updateView failed"; + return false; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " success=" << success + << " m_drawOrder = " << m_drawOrder + << " m_graphic->drawOrder = " << m_graphic->zIndex(); + return success; +} + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_renderer_attributes.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_renderer_attributes.h new file mode 100755 index 000000000..47e07fcda --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_renderer_attributes.h @@ -0,0 +1,102 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectRendererAttributes + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_graphical_object.h" +#include +#include + + +// Esri +#include + + +// Qt +#include +#include + + +// forward: +namespace Esri +{ +namespace ArcGISRuntime +{ +class Graphic; +class GraphicsOverlay; +class ModelSceneSymbol; +} +} + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +class SITUATIONMAP_EXPORT GraphicalObjectRendererAttributes : public AbstractGraphicalObject +{ + + using BaseClass = AbstractGraphicalObject; + +public: + GraphicalObjectRendererAttributes(const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId); + + ~GraphicalObjectRendererAttributes(); + + // Data access: + // to be cont. + void setAttributeMap(const QVariantMap &attribMap); + QVariantMap attributeMap() const; + +protected: + + // Internal/Admin + bool create() override; + + bool changed() override + { + return BaseClass::changed() + || m_attributeMap.changed() + ; + } + + void resetChanged() override + { + BaseClass::resetChanged(); + m_attributeMap.reset(); + } + + bool updateViewImpl() override; + + // no ModelSceneSymbol since Renderer-based + + SimpleValue m_attributeMap; + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility + + // Forbid some overloaded methods without effects of this object; + PositionCont positions() const override + { + return PositionCont(); + } + void setPositions(const PositionCont &posCont) override + { + Q_UNUSED(posCont); + Q_ASSERT(false); + } +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_scene_marker.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_scene_marker.cpp new file mode 100755 index 000000000..18946498d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_scene_marker.cpp @@ -0,0 +1,204 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectSceneMarker + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "graphical_object_scene_marker.h" +#include // operator<< +#include +#include + + +// ESRI +#include +#include + + +// Qt +#include +#include + + +namespace SituationMap +{ + +GraphicalObjectSceneMarker::GraphicalObjectSceneMarker(SceneMarkerType type + , const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId) + : BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId) + , m_type(type) + , m_logger(getLogger("GraphicalObjectSceneMarker")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +GraphicalObjectSceneMarker::~GraphicalObjectSceneMarker() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + delete m_simpleMarkerSceneSymbol; + m_simpleMarkerSceneSymbol = nullptr; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +void GraphicalObjectSceneMarker::setDiameter(double diameter) +{ + m_diameter = diameter; +} + +double GraphicalObjectSceneMarker::diameter() const +{ + return m_diameter.value(); +} + +bool GraphicalObjectSceneMarker::create() +{ + return BaseClass::create(); +} + +bool GraphicalObjectSceneMarker::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " origin =" << this->origin() // .x() << " , " << this->origin().y() + << " visibility=" << m_visibility + << " pickable=" << m_pickable + << " pickable.changed=" << m_pickable.changed() + << " selected=" << m_selected + << " m_diameter = " << m_diameter.value() + << " m_heading = " << m_heading.value() + << " m_pitch = " << m_pitch.value() + << " m_roll = " << m_roll.value() + << " m_drawOrder = " << m_drawOrder + << " m_lineStyle = " << m_lineStyle.value() + << " m_fillStyle = " << m_fillStyle.value() + << " fillColor = " << m_fillStyle.value().fillColor() + << " m_scale = " << m_scale.value(); + + if (m_graphicsLayer->renderingMode() != Esri::ArcGISRuntime::GraphicsRenderingMode::Dynamic) + for (int i=0; i<10; ++i) + ERROR_LOG(m_logger) << " RenderingMode::Dynamic required!!! , debugInfo='" << m_debugInfo << "'"; + + // some checks: + if (!isSinglePoint()) + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " debugInfo='" << m_debugInfo << "'" + << " incorrect number of positions" + << " (" << this->positions().count() << ")"; + return false; + } + + bool success = create(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " create failed"; + return false; + } + + const double radiusHeight = m_diameter.value()/2.0; + + Point3D origin = this->origin(); + + // Try to set AnchorPosition::Top, to let the cone rotate around top/tip. + // Using a cone, the anchor point does not work as expected, we have to move + if (m_type == SceneMarkerType::CONE) + { + // + // It´s a SCENE Marker. So we are per definition in 3D and origin is in degree. + // + // heading is in degree:m_heading.value() + // offset as vector: + QPointF offsetMeter(0, radiusHeight/2.0); + const int rotAngleDegree = (static_cast(m_heading.value())-90)%360; + DEBUG_LOG(m_logger) << " offsetMeter = " << offsetMeter << ", m_heading = " << m_heading.value() << ", rotAngleDegree = " << rotAngleDegree; + MathUtils::rotatePoint(offsetMeter, QPointF(), rotAngleDegree); + // new temporary origin... + // -> rad + origin *= LtsCoordTrans::deg2rad(); + LtsCoordTrans::UtmCoordinate newOrigin = LtsCoordTrans::UtmCoordinate::fromGeographic(origin.x(), origin.y()); + DEBUG_LOG(m_logger) << " newOrigin = " << newOrigin.mgrs(); + + // add rotated offset to temporary origin: + const QPointF origin2D = newOrigin.geographic(offsetMeter) * LtsCoordTrans::rad2deg(); + // -> deg + // write back; z-value stays untouched: + origin.rx() = origin2D.x(); + origin.ry() = origin2D.y(); + } + + const Esri::ArcGISRuntime::Point originGeometry(origin.x(), + origin.y(), + origin.z(), + spatialReference()); + m_graphic->setGeometry(originGeometry); + + if (!m_simpleMarkerSceneSymbol) + { + switch (m_type) + { + case SceneMarkerType::CONE: // Kegel + m_simpleMarkerSceneSymbol = + Esri::ArcGISRuntime::SimpleMarkerSceneSymbol::cone( + m_fillStyle.value().fillColor(), + radiusHeight, radiusHeight, // V100.8: cone: 'diameter' is interpreted as radius + Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Center); // V100.8: cone: Top does not work as aspected - grrr + break; + case SceneMarkerType::SPHERE: // Kugel + m_simpleMarkerSceneSymbol = + Esri::ArcGISRuntime::SimpleMarkerSceneSymbol::sphere( + m_fillStyle.value().fillColor(), + radiusHeight*2.0, + Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Center); + break; + } // switch + } + m_simpleMarkerSceneSymbol->setColor(m_fillStyle.value().fillColor()); + + if (m_type == SceneMarkerType::CONE) + { + m_simpleMarkerSceneSymbol->setWidth(radiusHeight); + m_simpleMarkerSceneSymbol->setHeight(radiusHeight); + m_simpleMarkerSceneSymbol->setDepth(radiusHeight); + } else { + m_simpleMarkerSceneSymbol->setWidth(m_diameter.value()); + m_simpleMarkerSceneSymbol->setHeight(m_diameter.value()); + m_simpleMarkerSceneSymbol->setDepth(m_diameter.value()); + } + + m_simpleMarkerSceneSymbol->setHeading(m_heading.value()); + m_simpleMarkerSceneSymbol->setPitch(m_pitch.value()); + m_simpleMarkerSceneSymbol->setRoll(m_roll.value()); + + m_graphic->setSymbol(m_simpleMarkerSceneSymbol); + + success = BaseClass::updateViewImpl(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " updateView failed"; + return false; + } + + success = addUpdateGraphic(); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " updateView failed"; + return false; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " success=" << success + << " m_drawOrder = " << m_drawOrder + << " m_graphic->drawOrder = " << m_graphic->zIndex(); + return success; +} + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_scene_marker.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_scene_marker.h new file mode 100755 index 000000000..f42aebc71 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_scene_marker.h @@ -0,0 +1,137 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectSceneMarker + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_graphical_object.h" +#include +#include + + +// Esri +#include + + +// Qt +#include + + +// forward: +namespace Esri +{ +namespace ArcGISRuntime +{ +class Graphic; +class GraphicsOverlay; +class SimpleMarkerSceneSymbol; +} +} + +class QTimer; + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +// +// +// +// !!! Layer with RenderingMode::Dynamic required !!! +// !!! Layer with RenderingMode::Dynamic required !!! +// !!! Layer with RenderingMode::Dynamic required !!! +// +// +// + +enum class SceneMarkerType {CONE, SPHERE}; + + +class SITUATIONMAP_EXPORT GraphicalObjectSceneMarker : public AbstractGraphicalObject +{ + + using BaseClass = AbstractGraphicalObject; + + +public: + GraphicalObjectSceneMarker(SceneMarkerType type + , const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId); + + ~GraphicalObjectSceneMarker(); + + // Data access: + // to be cont. (type is sphere, to support cube, cylinder etc) + void setDiameter(double diameter); + double diameter() const; + + double heading() const { return m_heading.value(); } + void setHeading(double headingDegree) { m_heading = headingDegree; } + double pitch() const { return m_pitch.value(); } + void setPitch(double pitchDegree) { m_pitch = pitchDegree; } + double roll() const { return m_roll.value(); } + void setRoll(double rollDegree) { m_roll = rollDegree; } + +protected: + + // Internal/Admin + bool create() override; + + bool changed() override + { + return BaseClass::changed() + || m_diameter.changed() + || m_heading.changed() + || m_pitch.changed() + || m_roll.changed() + ; + } + + void resetChanged() override + { + BaseClass::resetChanged(); + m_diameter.reset(); + m_heading.reset(); + m_pitch.reset(); + m_roll.reset(); + } + + bool updateViewImpl() override; + + // used marker: + Esri::ArcGISRuntime::SimpleMarkerSceneSymbol *m_simpleMarkerSceneSymbol{nullptr}; + + SceneMarkerType m_type{SceneMarkerType::SPHERE}; + SimpleValue m_diameter{25}; + + SimpleValue m_heading{0}; // degree + SimpleValue m_pitch{0}; // degree + SimpleValue m_roll{0}; // degree + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility + + // Forbid some overloaded methods without effects of this object; + PositionCont positions() const override + { + return PositionCont(); + } + void setPositions(const PositionCont &posCont) override + { + Q_UNUSED(posCont); + Q_ASSERT(false); + } +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_text.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_text.cpp new file mode 100755 index 000000000..6673cfa41 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_text.cpp @@ -0,0 +1,340 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectText + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "graphical_object_text.h" +#include // operator<< + + +// ESRI +#include +#include +#include + +// Qt +#include + +#include +#include + +namespace SituationMap +{ + +GraphicalObjectText::GraphicalObjectText(const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId) + : BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId) + , m_textSymbol(nullptr) + , m_logger(getLogger("GraphicalObjectText")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +GraphicalObjectText::~GraphicalObjectText() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " text='" << m_text << "'"; + + delete m_textSymbol; + m_textSymbol = nullptr; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " text='" << m_text << "'"; +} + +QString GraphicalObjectText::toString() const +{ + QString str; + // str.append( BaseClass::toString() ); + + if (!m_debugInfo.isEmpty()) + str.append(QStringLiteral("debugInfo=%1").arg(m_debugInfo)); + + if (m_graphicsLayer) + str.append(QStringLiteral(" layer='%1'").arg(m_graphicsLayer->objectName())); + + if (m_graphic) + str.append(QStringLiteral(" zIndex='%1'").arg(m_graphic->zIndex())); + + std::ostringstream stream; + stream << this->origin(); + str.append(QLatin1String(stream.str().c_str())); + + if (!m_text.value().isEmpty()) + str.append(QStringLiteral("text=%1").arg(m_text.value())); + + return str; +} + +QString GraphicalObjectText::text() const +{ + return m_text.value(); +} + +void GraphicalObjectText::setText(const QString &text) +{ + m_text = text; +} + +TextStyle GraphicalObjectText::textStyle() const +{ + return m_textStyle.value(); +} +TextStyle &GraphicalObjectText::rTextStyle() +{ + return m_textStyle.refValue(); +} +void GraphicalObjectText::setTextStyle(const TextStyle &textStyle) +{ + m_textStyle = textStyle; +} + +float GraphicalObjectText::xOffset() const +{ + return m_offset.value().x(); +} +void GraphicalObjectText::setXOffset(float xOffset) +{ + QPointF temp = m_offset.value(); + temp.rx() = xOffset; + m_offset = temp; +} +float GraphicalObjectText::yOffset() const +{ + return m_offset.value().y(); +} +void GraphicalObjectText::setYOffset(float yOffset) +{ + QPointF temp = m_offset.value(); + temp.ry() = yOffset; + m_offset = temp; +} + + +bool GraphicalObjectText::create() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " text='" << m_text << "'" + << " origin =" << this->origin() // .x() << " , " << this->origin().y() + << " visibility=" << m_visibility; + bool success = true; + + // some checks: + if (!m_graphicsLayer) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " no graphicsLayer"; + return false; + } + + BaseClass::create(); + + if (!m_textSymbol) + m_textSymbol = new Esri::ArcGISRuntime::TextSymbol(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " text='" << m_text << "'" + << " success=" << success + << " drawOrder = " << m_graphic->zIndex(); + return success; +} + +bool GraphicalObjectText::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " text='" << m_text << "'" + << " origin =" << this->origin() // .x() << " , " << this->origin().y() + << " visibility=" << m_visibility + << " m_textStyle=" << m_textStyle + << " offset=" << m_offset; + + // some checks: + if (!this->isSinglePoint()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " incorrect number of positions" + << " (" << this->positions().count() << ")"; + return false; + } + + bool success = true; + if (m_visibility.value()) + { + success = create(); // create only if visible + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " create failed"; + return false; + } + + if (! BaseClass::updateViewImpl()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " updateViewImpl failed"; + } + + // FATAL_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + // << " text='" << m_text << "'" + // << " changed1=" << m_text.changed() + // << " changed2="<< m_textStyle.changed(); + + bool assignTextSymbol = false; + + // || m_visibility.changed()) -> szenario: create invisible object and make it visible + if (m_text.changed() || m_visibility.changed()) + { + m_textSymbol->setText(m_text.value()); + assignTextSymbol = true; + } + + // TextStyle to TextSymbol@ESRI : + if (m_textStyle.changed() + || m_scale.changed() + || m_visibility.changed() // -> szenario: create invisible object and make it visible + ) + { + const TextStyle &textStyle = m_textStyle.value(); + m_textSymbol->setColor(textStyle.textColor()); + + // High values can lead to coredump with 'SymbolDrawHelper::create_line_image FAILED, SkBitmap is NULL or empty' -> limit + m_textSymbol->setSize(std::min(500.0f, textStyle.size() * m_scale.value())); + m_textSymbol->setAngle(textStyle.angle()); + + m_textSymbol->setFontFamily(textStyle.fontFamily()); + + m_textSymbol->setFontWeight(textStyle.bold() ? Esri::ArcGISRuntime::FontWeight::Bold : Esri::ArcGISRuntime::FontWeight::Normal); + m_textSymbol->setFontStyle(textStyle.italic() ? Esri::ArcGISRuntime::FontStyle::Italic : Esri::ArcGISRuntime::FontStyle::Normal); + + Esri::ArcGISRuntime::FontDecoration fd = Esri::ArcGISRuntime::FontDecoration::None; + if (textStyle.underline()) + fd = Esri::ArcGISRuntime::FontDecoration::Underline; + if (textStyle.strikeThrough()) + fd = Esri::ArcGISRuntime::FontDecoration::LineThrough; + m_textSymbol->setFontDecoration(fd); + + float outlineWidth = 0; + if (textStyle.textOutlineWidth() > 0) + outlineWidth = textStyle.textOutlineWidth() * m_scale.value(); + if (m_textSymbol->size() <= 15) // small size -> outline reduces readability :-( + outlineWidth = 0.0; + m_textSymbol->setHaloWidth(outlineWidth); + + if (textStyle.textOutlineColor().isValid()) + m_textSymbol->setHaloColor(textStyle.textOutlineColor()); + +// qWarning() << " haloWidth = " << m_textSymbol->haloWidth(); + +// m_textSymbol->setHaloColor(Qt::white); +// m_textSymbol->setHaloWidth(outlineWidth); + +// qWarning() << " fontSize " << textStyle.size() * m_scale.value() +// <<" (textStyle.size() " << textStyle.size() +// << " m_scale.value() " << m_scale.value() << ")"; + +// qWarning() << " outlineWidth " << outlineWidth +// <<" (textStyle.textOutlineWidth() " << textStyle.textOutlineWidth() +// << " m_scale.value() " << m_scale.value() << ")"; + +// if (textStyle.backgroundColor().isValid()) + m_textSymbol->setBackgroundColor(textStyle.backgroundColor()); + +// m_textSymbol->setAntiAliased(textStyle.antiAliased()); + + m_textSymbol->setHorizontalAlignment(convert(textStyle.horizontalAlignment())); + m_textSymbol->setVerticalAlignment(convert(textStyle.verticalAlignment())); + + assignTextSymbol = true; + // : TextStyle to TextSymbol@ESRI + } + + // || m_visibility.changed()) -> szenario: create invisible object and make it visible + if (m_offset.changed() || m_visibility.changed()) + { + m_textSymbol->setOffsetX(m_offset.value().x()); + m_textSymbol->setOffsetY(m_offset.value().y()); + assignTextSymbol = true; + } + + if (assignTextSymbol) + m_graphic->setSymbol(m_textSymbol); + + + // check ChangedFlags: + // Object is created and text is invisble due to scale (meterPerPixel) + // map is zoomed in -> text should arise, but on m_visibility is changed: + if (m_visibility.changed() || m_positions.changed()) + { + const Point3D &origin = this->origin(); + m_graphic->setGeometry(Esri::ArcGISRuntime::Point(origin.x(), + origin.y(), + origin.z(), + spatialReference())); + success = addUpdateGraphic(); + } // m_visibility.changed() || m_positions.changed() + + } + else // if visible + { + BaseClass::updateViewImpl(); + success = addUpdateGraphic(); + } + + if (m_logger.isTraceEnabled()) + { + int drawOrder = -1; + if (m_graphic) + drawOrder = m_graphic->zIndex(); + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " text='" << m_text << "'" + << " success=" << success + << " drawOrder = " << drawOrder; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + // << " fontFamily='" << m_textSymbol->fontFamily() << "'" + ; + return success; +} + + + +Esri::ArcGISRuntime::HorizontalAlignment GraphicalObjectText::convert(const HorizontalAlignment horizontalAlignment) +{ + Esri::ArcGISRuntime::HorizontalAlignment esriAlignmenmt = + Esri::ArcGISRuntime::HorizontalAlignment::Left; + switch (horizontalAlignment) + { + case HorizontalAlignment::Left: esriAlignmenmt = Esri::ArcGISRuntime::HorizontalAlignment::Left; break; + case HorizontalAlignment::Center: esriAlignmenmt = Esri::ArcGISRuntime::HorizontalAlignment::Center; break; + case HorizontalAlignment::Right: esriAlignmenmt = Esri::ArcGISRuntime::HorizontalAlignment::Right; break; + case HorizontalAlignment::Justify: esriAlignmenmt = Esri::ArcGISRuntime::HorizontalAlignment::Justify; break; + } // switch + + return esriAlignmenmt; +} + +Esri::ArcGISRuntime::VerticalAlignment GraphicalObjectText::convert(const VerticalAlignment verticalAlignment) +{ + Esri::ArcGISRuntime::VerticalAlignment esriAlignmenmt = + Esri::ArcGISRuntime::VerticalAlignment::Bottom; + switch (verticalAlignment) + { + case VerticalAlignment::Bottom: esriAlignmenmt = Esri::ArcGISRuntime::VerticalAlignment::Bottom; break; + case VerticalAlignment::Middle: esriAlignmenmt = Esri::ArcGISRuntime::VerticalAlignment::Middle; break; + case VerticalAlignment::Top: esriAlignmenmt = Esri::ArcGISRuntime::VerticalAlignment::Top; break; + case VerticalAlignment::BaseLine: esriAlignmenmt = Esri::ArcGISRuntime::VerticalAlignment::Baseline; break; + } // switch + return esriAlignmenmt; +} + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_text.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_text.h new file mode 100755 index 000000000..f649c2917 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/graphical_object_text.h @@ -0,0 +1,125 @@ +/** + * @RDE_HEADER@ + * GraphicalObjectText + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_graphical_object.h" +#include +#include +#include +#include + + +// Esri + + +// Qt + + +// forward: +namespace Esri +{ +namespace ArcGISRuntime +{ +class Graphic; +class GraphicsOverlay; +class PictureMarkerSymbol; +class TextSymbol; + +enum class HorizontalAlignment; +enum class VerticalAlignment; +} +} + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +class SITUATIONMAP_EXPORT GraphicalObjectText : public AbstractGraphicalObject +{ + + using BaseClass = AbstractGraphicalObject; + +public: + GraphicalObjectText(const Esri::ArcGISRuntime::GeoView *geoView + , Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer + , const QString &debugInfo + , const UniqueMapId &uniqueMapId + ); + + ~GraphicalObjectText(); + + QString toString() const override; // specialized + + // Data access: + QString text() const; + void setText(const QString &text); + + TextStyle textStyle() const; + TextStyle &rTextStyle(); + void setTextStyle(const TextStyle &textStyle); + + // Offset + virtual float xOffset() const; + virtual void setXOffset(float xOffset); + virtual float yOffset() const; + virtual void setYOffset(float yOffset); + + static Esri::ArcGISRuntime::HorizontalAlignment convert(const HorizontalAlignment horizontalAlignment); + static Esri::ArcGISRuntime::VerticalAlignment convert(const VerticalAlignment verticalAlignment); + + +protected: + // Internal/Admin + bool create() override; + + bool changed() override + { + return BaseClass::changed() + || m_text.changed() + || m_textStyle.changed() + || m_offset.changed(); + } + + void resetChanged() override + { + BaseClass::resetChanged(); + m_text.reset(); + m_textStyle.reset(); + m_offset.reset(); + } + + bool updateViewImpl() override; + + Esri::ArcGISRuntime::TextSymbol *m_textSymbol = nullptr; + + SimpleValue m_text; ///< to be displayed on map + SimpleValue m_textStyle; + SimpleValue m_offset; ///< Offset (pixel). + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility + + // Forbid some overloaded methods without effects of this object; + virtual PositionCont positions() const override + { + return PositionCont(); + } + virtual void setPositions(const PositionCont &posCont) override + { + Q_UNUSED(posCont); + } +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/stub_graphical_object.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/stub_graphical_object.h new file mode 100755 index 000000000..d19e3f43d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/stub_graphical_object.h @@ -0,0 +1,69 @@ +/** + * @RDE_HEADER@ + * StubGraphicalObject + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_graphical_object.h" + +#include + + +// Esri + + + +namespace SituationMap +{ + + + +/** + @class StubGraphicalObject stub_graphical_object.h + The class \c StubGraphicalObject is used as substitution for a graphical object. It does + nothing and the purpose is only to satisfy the interface of AbstractGraphicalObject. + @see AbstractGraphicalObject + */ +class StubGraphicalObject : public AbstractGraphicalObject +{ + +public: + + explicit StubGraphicalObject() + : AbstractGraphicalObject(nullptr, nullptr, QString(), UniqueMapId()) + { + } + + ~StubGraphicalObject() = default; + + StubGraphicalObject(const StubGraphicalObject &other) = delete; + StubGraphicalObject &operator=(StubGraphicalObject &other) = delete; + +protected: + + bool changed() override + { + return false; + } + + bool updateViewImpl() override + { + return true; + } + + bool create() override + { + return true; + } + +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/tactical_appearance.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/tactical_appearance.cpp new file mode 100755 index 000000000..796b0739b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/tactical_appearance.cpp @@ -0,0 +1,622 @@ +/** + * @RDE_HEADER@ + * TacticalAppearance + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "tactical_appearance.h" +#include +#include +#include +#include +#include +#include // operator<< +#include + + +// ESRI +#include +#include +#include +#include +#include + +// Qt +#include + + +namespace SituationMap +{ + + +class TacticalAppearance::Private +{ +public: + Private() = default; + ~Private() = default; + + // ESRI data: + MapImpl *m_mapImpl = nullptr; + QString m_logicalLayerName; + + Esri::ArcGISRuntime::GraphicsOverlay *m_graphicOverlayTactical = nullptr; + AdvancedSymbology::SymbolDictionary *m_dictionary = nullptr; + AdvancedSymbology::SymbolResultWatcher m_symbolResultWatcher; + + // OWN data: + SimpleValue m_uniqueDesignation; + SimpleValue m_symbol; + + + AdvancedSymbology::SymbolDataToRender m_symDataToRender; + GraphicalObjectPictureMarker *m_pictureMarker = nullptr; + +}; // class TacticalAppearance::Private +//----------------------------------------------------------------------------- + + + +TacticalAppearance::TacticalAppearance(MapImpl *mapImpl, + const QString &logicalLayerName, + const QString &debugInfo, + const UniqueMapId &uniqueMapId) + : BaseClass(mapImpl->getEsriGeoView(), nullptr, debugInfo, uniqueMapId), + m_logger(getLogger("TacticalAppearance")), + d(new Private) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << ", logicalLayerName='" << logicalLayerName << "'"; + + d->m_symDataToRender.sizeL = 100; + d->m_mapImpl = mapImpl; + d->m_logicalLayerName = logicalLayerName; + + if (!m_graphicsLayer && d->m_mapImpl) + m_graphicsLayer = d->m_mapImpl->getGraphicsLayer(d->m_logicalLayerName); + + if (!m_graphicsLayer) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " getGraphicsLayer failed"; + } + + if (d->m_mapImpl) + d->m_dictionary = &(d->m_mapImpl->symbolDictionary()); + + + // lambda: + // m_symbolResultWatcher is canceled on destruction. + // So it`s safe that no signal will be fired afterwards. + // To avoid CLang warnings use m_symbolResultWatcher as context (3rd para), although it´s not intended. + // Better idea? + QObject::connect(&d->m_symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady, &d->m_symbolResultWatcher, [this]() + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " resultReady..."; + + QSharedPointer symResult = d->m_symbolResultWatcher.symbolResult(); + if (! symResult) + return; // should NEVER happen! (Which object should have triggered the signal???) + + if (d->m_pictureMarker) + { + d->m_pictureMarker->setImage(symResult->symbolImage().image()); + d->m_pictureMarker->updateView(); + } + }); + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +TacticalAppearance::~TacticalAppearance() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + + // abort pending call to SymbolDictionary::findSymbol: + d->m_symbolResultWatcher.cancel(); + + delete d->m_pictureMarker; + d->m_pictureMarker = nullptr; + + removeGraphicFromGraphicOverlayTactical(); + + delete d; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +void TacticalAppearance::removeGraphicFromGraphicOverlayTactical() +{ + if (m_logger.isTraceEnabled()) + { + if (m_graphic) + { + Esri::ArcGISRuntime::GraphicsOverlay *gOverlay = m_graphic->graphicsOverlay(); + if (gOverlay) + TRACE_LOG(m_logger) << LOGFUNCTION << " START debugInfo='" << m_debugInfo << "'" << " name = '" << gOverlay->overlayId() << "'"; + } + } + + if (m_graphic && d->m_graphicOverlayTactical && d->m_graphicOverlayTactical->graphics()) + d->m_graphicOverlayTactical->graphics()->removeOne(m_graphic); + + delete m_graphic; + m_graphic = nullptr; +} + + + +bool TacticalAppearance::useDictionaryRenderMode(const AdvancedSymbology::GeometryType &geomType) const +{ + bool usePictureMarker = isSinglePoint() && (AdvancedSymbology::GeometryType::Point == geomType || AdvancedSymbology::GeometryType::Undefined == geomType); + return !usePictureMarker || symbol().useAlwaysDictionaryRenderer; +} // bool TacticalAppearance::useDictionaryRenderMode() const + + +bool TacticalAppearance::create() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " m_visibility = " << m_visibility; + + bool success = true; + + // + // Prepare / create : + // + // a) singlePoint / exactly one coordinate -> use pictureMarker + // or + // b) multiPoint / more than one coordinate -> m_graphic / DictionaryRenderer + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This assumption doesn't work any more because some polyline and polygon symbols will have only one + * anchor point and the real geometry is defined by additional attributes like radius, with, length ... + * + * JT 2016/12/20 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + if (m_graphicsLayer && m_visibility.value()) + { + const SymbolData &symbolData_ref = d->m_symbol.value(); + const QString &baseSymbolCode = AdvancedSymbology::SidcBuilderBase::concreteSidcToBaseSymbolCode(symbolData_ref.sidc); + const AdvancedSymbology::SymbolProperties &symbolProperties = d->m_dictionary->symbolProperties(baseSymbolCode); + AdvancedSymbology::GeometryType geomType = symbolProperties.geometryType(); + + if (useDictionaryRenderMode(geomType)) + { + if (!d->m_graphicOverlayTactical) + d->m_graphicOverlayTactical = d->m_mapImpl->getGraphicsLayerDict(d->m_logicalLayerName); + + if (!d->m_graphicOverlayTactical) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " getGraphicsLayerDict failed"; + return false; + } + + if (!m_graphic) + { + m_graphic = new Esri::ArcGISRuntime::Graphic(); + m_graphic->setObjectName(m_debugInfo); + m_graphic->setZIndex(m_drawOrder.value()); + + // use attributeModel to store application obj id (SimWorld) at graphic + Esri::ArcGISRuntime::AttributeListModel *attLisModel = m_graphic->attributes(); + if (attLisModel) + attLisModel->insertAttribute(QLatin1String(SituationMap::UniqueMapIdAttribute), m_uniqueMapId); + else + ERROR_LOG(m_logger) << LOGFUNCTION << " no AttributeListModel, debugInfo='" << m_debugInfo << "'" << " uniqueMapId=" << m_uniqueMapId; + + // Adding an empty graphic leads to Segmentation fault. + // (in Esri_runtimecore::Map_renderer::Dictionary_renderer::derive_geometry) + + bool successGraphic = addUpdateGraphic(); // CHECK + if (!successGraphic) + ERROR_LOG(m_logger) << LOGFUNCTION << " addUpdateGraphic failed, debugInfo='" << m_debugInfo << "'" << " uniqueMapId=" << m_uniqueMapId; + + } // if (!m_graphic) + } + else + { + // m_graphic is needless now: + removeGraphicFromGraphicOverlayTactical(); + } + + } + + // : Prepare / create + + bool positionsEmpty = m_positions.value().isEmpty(); + if (positionsEmpty) + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " positions empty"; + + success = success && !positionsEmpty; + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " create failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; + return success; +} + +void TacticalAppearance::createPictureMarker() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + if (!d->m_pictureMarker && d->m_mapImpl) + { + d->m_pictureMarker = new GraphicalObjectPictureMarker(d->m_mapImpl->getEsriGeoView() + , m_graphicsLayer + , m_debugInfo + QLatin1String("#PicMarkTacticalAppearance") + , m_uniqueMapId); + } + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +void TacticalAppearance::removePictureMarker() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"; + delete d->m_pictureMarker; + d->m_pictureMarker = nullptr; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"; +} + +QString TacticalAppearance::uniqueDesignation() const +{ + return d->m_uniqueDesignation.value(); +} +void TacticalAppearance::setUniqueDesignation(const QString &uniqueDesignation) +{ + d->m_uniqueDesignation = uniqueDesignation; +} + +SymbolImage TacticalAppearance::symbolImage() const +{ + SymbolImage symImage; + if (d->m_pictureMarker) + { + symImage.setImage(d->m_pictureMarker->image()); + symImage.setValid(!symImage.image().isNull()); + } + return symImage; +} + + +SymbolData TacticalAppearance::symbol() const +{ + return d->m_symbol.value(); +} + +void TacticalAppearance::setSymbol(const SymbolData &symbol) +{ + d->m_symbol = symbol; +} + +bool TacticalAppearance::changed() +{ + return BaseClass::changed() + || d->m_uniqueDesignation.changed() + || d->m_symbol.changed(); +} +void TacticalAppearance::resetChanged() +{ + BaseClass::resetChanged(); + d->m_uniqueDesignation.reset(); + d->m_symbol.reset(); +} + +Esri::ArcGISRuntime::Geometry TacticalAppearance::getGraphicGeometry() const +{ + if (d->m_pictureMarker) return d->m_pictureMarker->getGraphicGeometry(); + if (m_graphic) return m_graphic->geometry(); + + ERROR_LOG(m_logger) << LOGFUNCTION << "Geometry is undefined"; + + return Esri::ArcGISRuntime::Geometry(); +} // Esri::ArcGISRuntime::Geometry TacticalAppearance::getGraphicGeometry() const + + +bool TacticalAppearance::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << " symbol='" << d->m_symbol.value().sidc << "'" + << " visibility=" << m_visibility + << " selected=" << m_selected + << " origin=" << this->origin() + << " scale=" << m_scale.value(); + + const SymbolData &symbolData_ref = d->m_symbol.value(); + const QString &baseSymbolCode = AdvancedSymbology::SidcBuilderBase::concreteSidcToBaseSymbolCode(symbolData_ref.sidc); + const AdvancedSymbology::SymbolProperties &symbolProperties = d->m_dictionary->symbolProperties(baseSymbolCode); + AdvancedSymbology::GeometryType geomType = symbolProperties.geometryType(); + + if (geomType == AdvancedSymbology::GeometryType::Undefined) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " GeometryType::Undefined, symbolProperties failed!?!"; + return false; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'" + << ", geomType=" << geomType + << ", useDictionaryRenderMode(geomType)=" << useDictionaryRenderMode(geomType); + + bool success = create(); + + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " create failed"; + return false; + } + + if (useDictionaryRenderMode(geomType)) + { + // real work: + if (m_positions.changed() || d->m_symbol.changed() || m_visibility.changed()) + { + auto buildPointClusterGeometry = [this]() -> Esri::ArcGISRuntime::Geometry + { + QSharedPointer builder(new Esri::ArcGISRuntime::MultipointBuilder(spatialReference())); + Esri::ArcGISRuntime::PointCollection points(spatialReference()); + const auto posCont = positions(); + for (const auto &pos : posCont) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " addPoint:" << pos.x() << " - " << pos.y(); + points.addPoint(pos.x(), pos.y()); + } + builder->setPoints(&points); + return builder->toGeometry(); + }; // buildPointClusterGeometry + + Esri::ArcGISRuntime::Geometry geom; + + switch (geomType) + { + case AdvancedSymbology::GeometryType::Undefined : break; + + case AdvancedSymbology::GeometryType::Point : + case AdvancedSymbology::GeometryType::Polyline : + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " m_positions.count()=" << m_positions.value().count(); + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * If a PolylineBuilder is used here to create symbol geometry the construction appropriate symbol standard (MIL2525C / MIL2525D / App6) + * will not be done and the symbol is always a simple polyline. Using MultipointBuilder to build symbol geometry the result is based on + * underlying symbol standard. + * 2016/12/9 JT + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + geom = buildPointClusterGeometry(); + + } break; + + case AdvancedSymbology::GeometryType::Polygon : + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " m_positions.count()=" << m_positions.value().count(); + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Some symbols have polygon geometry and could have only one or two positions. In case of less than 3 positions do the same as for + * polylines and use MultipointBuilder to create the symbol geometry. In other case use PolygonBuilder. + * 2016/12/13 JT + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + if (positions().size() > 2) + { + QSharedPointer builder(new Esri::ArcGISRuntime::PolygonBuilder(spatialReference())); + const auto posCont = positions(); + for (const auto &pos : posCont) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " addPoint:" << pos.x() << " - " << pos.y(); + builder->addPoint(pos.x(), pos.y()); + } + geom = builder->toGeometry(); + } + else + geom = buildPointClusterGeometry(); + + } break; + + } // switch + + if (geom.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Geometry NOT OK -> fallback symbol" << " (geometryType was " << geomType << ")"; + createPictureMarker(); + d->m_pictureMarker->setDebugInfo(m_debugInfo + QLatin1String("#fallback")); + + int size = symbolData_ref.size; + if (size < 10) size = 10; + if (size > 100) size = 100; + + d->m_pictureMarker->setMarkerSymbol(MarkerSymbolType::Diamond, size, symbolData_ref.color); + d->m_pictureMarker->setOrigin(this->origin()); + d->m_pictureMarker->setDrawOrder(m_drawOrder.value()); + d->m_pictureMarker->updateView(); + success = false; + } + else // geom is not empty + { + removePictureMarker(); + + if (m_graphic) + { + success = true; + + // move to graphic: + m_graphic->setGeometry(geom); + + // prepare rawAttributes for DictionaryRenderer: + + QVariantMap rawAttributes = d->m_symbol.value().attributeMap; + + // build from scratch to honor removed attributes: + // (Otherwise the will remain wrongly) + + rawAttributes[QLatin1String(SituationMap::UniqueMapIdAttribute)] = m_uniqueMapId; + rawAttributes[QLatin1String(SituationMap::IsPickableAttribute)] = m_pickable.value(); // set pickable attribute JT 2016/12/21 + + rawAttributes[QLatin1String("_type")] = QLatin1String("position_report"); + rawAttributes[QLatin1String("_action")] = QLatin1String("update"); + rawAttributes[QLatin1String("sidc")] = AdvancedSymbology::SidcCodec::decodeRawSidc(d->m_symbol.value().sidc); // in case of MIL2525 + rawAttributes[QLatin1String("sic")] = AdvancedSymbology::SidcCodec::decodeRawSidc(d->m_symbol.value().sidc); // in case of APP6 + + Esri::ArcGISRuntime::AttributeListModel * attributeModel = m_graphic->attributes(); + if (attributeModel) + attributeModel->setAttributesMap(rawAttributes); + else + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " no AttributeListModel"; + + + +// // prepare rawAttributes for DictionaryRenderer: +// QVariantMap rawAttributes = d->m_symbol.value().attributeMap; + +// rawAttributes[QLatin1String(SituationMap::IsPickableAttribute)] = m_pickable.value(); // set pickable attribute JT 2016/12/21 + +// rawAttributes[QLatin1String("_type")] = QLatin1String("position_report"); +// rawAttributes[QLatin1String("_action")] = QLatin1String("update"); +// rawAttributes[QLatin1String("sidc")] = AdvancedSymbology::SidcCodec::decodeRawSidc(d->m_symbol.value().sidc); // in case of MIL2525 +// rawAttributes[QLatin1String("sic")] = AdvancedSymbology::SidcCodec::decodeRawSidc(d->m_symbol.value().sidc); // in case of APP6 +// // move to graphic: +// m_graphic->setGeometry(geom); + +// /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// * Replace: +// * m_graphic->attributes()->setAttributesMap(rawAttributes); +// * +// * because it doesn't work anymore in ArcGISRuntime V100.2 +// * JT, 2018/01/24 +// * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +// Esri::ArcGISRuntime::AttributeListModel * attributeModel = m_graphic->attributes(); +// if (attributeModel) +// { +// if (!attributeModel->isEmpty()) +// { +// for (auto it = rawAttributes.cbegin(); it != rawAttributes.cend(); ++it) +// { +// const QString &attrKey = it.key(); +// if (attributeModel->containsAttribute(attrKey)) +// { +// attributeModel->replaceAttribute(attrKey, rawAttributes[attrKey]); +// continue; +// } +// attributeModel->insertAttribute(attrKey, rawAttributes[attrKey]); +// } +// } +// else attributeModel->setAttributesMap(rawAttributes); +// } + + // 2021-08, N. Thoden + // While switching back to the behaviour that one logical layer has + // 1st getGraphicsLayer and 2nd getGraphicsLayerDict [GUEZE-496], + // the tactical appereance of DangerArea and similar disappears. + // Reason: m_graphicsLayer and m_graphicOverlayTactical are different now! + // It seems like that in former versions (< V100.8) the graphic could + // be added to more than one Esri layer. + // Now (V100.8) adding to a second layer fails. + // But we can remove the m_graphic from the default m_graphicsLayer + // before adding the the desired one: m_graphicOverlayTactical + if (m_graphicsLayer->graphics()->contains(m_graphic)) + m_graphicsLayer->graphics()->removeOne(m_graphic); + + // add to graphicOverlayTactical: + if (! d->m_graphicOverlayTactical->graphics()->contains(m_graphic)) + d->m_graphicOverlayTactical->graphics()->append(m_graphic); + + bool successGraphic = d->m_graphicOverlayTactical->graphics()->contains(m_graphic); + if (!successGraphic) + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " m_graphic not added to m_graphicOverlayTactical"; + } + else + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " no m_graphic"; + // success = false; + success = true; // if the DictionaryRenderer is used, m_graphic is null and that is ok. + } + + } // if (geom.isEmpty()) + } + + } + else // !useDictionaryRenderMode(geomType) + { + createPictureMarker(); + d->m_pictureMarker->setOrigin(this->origin()); + + // real work: + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " update geometry"; + + + if (d->m_symbol.changed() || m_scale.changed()) + { + if (d->m_dictionary) + { + int widthPixel = SituationMap::SymbolSizeLdefault; + widthPixel *= symbolData_ref.size * m_scale.value(); + + d->m_symDataToRender.sidc = symbolData_ref.sidc; + d->m_symDataToRender.fillColor = symbolData_ref.color; + d->m_symDataToRender.fillColorReplace = true; + d->m_symDataToRender.sizeL = widthPixel; + + if (d->m_pictureMarker->image().isNull()) + d->m_pictureMarker->setImage(d->m_dictionary->temporaryImage(d->m_symDataToRender.sizeL)); + d->m_symbolResultWatcher.setSymbolResult(d->m_dictionary->fetchSymbol(d->m_symDataToRender)); + success = true; + } + else + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " visibility=" << m_visibility + << " no dictionary"; + } + + } + + if (d->m_pictureMarker) + { + d->m_pictureMarker->setDrawOrder(m_drawOrder.value()); + d->m_pictureMarker->setVisibility(m_visibility.value()); + d->m_pictureMarker->setSelected(m_selected.value()); + d->m_pictureMarker->setPickable(m_pickable.value()); + + success = success && d->m_pictureMarker->updateView(); + } + } // else !useDictionaryRenderMode(geomType) + + + if (d->m_pictureMarker) + { + d->m_pictureMarker->setOrigin(this->origin()); // to avoid incorrect number of positions.. + d->m_pictureMarker->setVisibility(m_visibility.value()); + d->m_pictureMarker->setSelected(m_selected.value()); + success = d->m_pictureMarker->updateView() && success; + } + + // in case of no pictureMarker: + if (m_graphic) + { + m_graphic->setZIndex(m_drawOrder.value()); + m_graphic->setVisible(m_visibility.value()); + m_graphic->setSelected(m_selected.value()); + } + + // reset flags + d->m_symbol.reset(); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " failure, debugInfo='" << m_debugInfo << "'"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'" + << " success = " << success + << " visibility=" << m_visibility; + return success; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/tactical_appearance.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/tactical_appearance.h new file mode 100755 index 000000000..d3e3a68c1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/GraphicalObjects/tactical_appearance.h @@ -0,0 +1,117 @@ +/** + * @RDE_HEADER@ + * TacticalAppearance + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_graphical_object.h" + +// Esri + + +// Qt + + +// forward: + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +// forward: +class MapImpl; +class GraphicalObjectPictureMarker; + + +/** + * @brief The TacticalAppearance class displays sidcs. + * To do so, the number of coordinate lead to use + * - the GraphicalObjectPictureMarker (exactly one coordinate) + * or + * - the Message (more than one coordinate) + * + * Use cases: + * - Participant: sidc with #coords == 1, TacticalAppearance should get exactly one coordinate. + * (GraphicalObjectPictureMarker on ESRI::GraphicsLayer is used) + * - Tactical display of a Building: sidc #coords >= 2, TacticalAppearance should get list of positions. + * (Message on ESRI::MessageGroupLayer is used) + * - (Tactical) PreviewIcon of a Building: sidc #coords == 1, TacticalAppearance should get exactly one coordinate. + * (GraphicalObjectPictureMarker on ESRI::GraphicsLayer is used) + * - Tactical symbols of plan editor + * + * In case of GraphicalObjectPictureMarker the image creation is done by AdvancedSymbology::SymbolDictionary::symbolImageBySidcExtended(). + */ +class SITUATIONMAP_EXPORT TacticalAppearance : public AbstractGraphicalObject +{ + + using BaseClass = AbstractGraphicalObject; + +public: + + /** + * @brief TacticalAppearance + * @param mapImpl map to be used. + * @param logicalLayerName logical layer name to be used. + * @param debugInfo debugInfo to be used + * @param uniqueMapId uniqueMapId to be used. (Optional to fulfill rubberband requirements) + */ + TacticalAppearance(MapImpl *mapImpl, + const QString &logicalLayerName, + const QString &debugInfo, + const UniqueMapId &uniqueMapId = INVALID_MAP_ID); + + TacticalAppearance(const TacticalAppearance &other) = delete; + TacticalAppearance &operator=(TacticalAppearance &other) = delete; + + virtual ~TacticalAppearance(); + + + SymbolData symbol() const; + void setSymbol(const SymbolData &symbol); + + QString uniqueDesignation() const; + void setUniqueDesignation(const QString &uniqueDesignation); + + + SymbolImage symbolImage() const; + + Esri::ArcGISRuntime::Geometry getGraphicGeometry() const override; + +protected: + // Internal/Admin + virtual bool create() override; + virtual void createPictureMarker(); + virtual void removePictureMarker(); + + void removeGraphicFromGraphicOverlayTactical(); + + bool changed() override; + void resetChanged() override; + + + bool updateViewImpl() override; + + bool useDictionaryRenderMode(const AdvancedSymbology::GeometryType &geomType) const; + + // logical + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility + + class Private; + /** + * The private d-pointer to not break BC when changing internals of this class + */ + Private *d = nullptr; + + +}; + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LayerAggregation/layer_aggregation_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LayerAggregation/layer_aggregation_widget.cpp new file mode 100755 index 000000000..65430dfc2 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LayerAggregation/layer_aggregation_widget.cpp @@ -0,0 +1,603 @@ +/** +** +** @RDE_HEADER@ +** Handles aggregation of participant layer +** $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 .........: +** +** 16.08.2017, JT : first edition +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "layer_aggregation_widget.h" + +#include + +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + + +using namespace SituationMap; + + +struct AggregationData +{ + QString layerName; + QString displayName; + double minScale = LayerParameter::UNDEFINED_SCALE; // 0 + double maxScale = LayerParameter::UNDEFINED_SCALE; // 0 + bool minScaleValid = false; + bool maxScaleValid = false; + +}; // AggregationData + + +struct AggregationWidgets +{ + QCheckBox *cbMinScaleValid = nullptr; + QPushButton *pbMinScale = nullptr; + QPushButton *pbMinScaleTo = nullptr; + + QCheckBox *cbMaxScaleValid = nullptr; + QPushButton *pbMaxScale = nullptr; + QPushButton *pbMaxScaleTo = nullptr; + + QPushButton *pbReset = nullptr; + QPushButton *pbApply = nullptr; +}; + + +class LayerAggregationWidget::InternalData +{ +public: + + Map *m_map = nullptr; + bool m_valid = false; + + LayerType m_layerType; + + QVector m_aggregationList; + QMap m_aggregationReset; + QMap m_aggregationWidgets; + + LOGGER_T m_logger; + + explicit InternalData(Map *map,LayerType layerType) + : m_map(map) + , m_layerType(layerType) + , m_logger(getLogger("LayerAggregation")) + {} + +}; // LayerAggregationWidget::InternalData + +//------------------------------------------------------------------------------ + +QString LayerAggregationWidget::scaleText(double scale) +{ + return (scale >= 1) ? QStringLiteral("1:%1").arg(int(scale)) : tr("undefined"); +} // QString LayerAggregationWidget::scaleText(double scale) + + +void LayerAggregationWidget::setMinScaleLabel(QPushButton *btn, double scale) +{ + btn->setText(tr("Min. Scale from Map\n%1").arg(scaleText(scale))); +} // void LayerAggregationWidget::setMinScaleLabel(QPushButton * btn, double scale) + + +void LayerAggregationWidget::setMaxScaleLabel(QPushButton *btn, double scale) +{ + btn->setText(tr("Max. Scale from Map\n%1").arg(scaleText(scale))); +} // void LayerAggregationWidget::setMaxScaleLabel(QPushButton * btn, double scale) + + +LayerAggregationWidget::LayerAggregationWidget(Map *map, LayerType layerType, const QRegularExpression &nameCondition, QWidget *parent) + : QWidget(parent), m_internal(new InternalData(map,layerType)) +{ + if (!nameCondition.isValid()) + { + ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " Illegal regular expression in nameCondition."; + return; + } + + if (map == nullptr) + { + ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " Invalid situation map, cannot initialize dialog"; + return; + } + + QGridLayout *layout = new QGridLayout(this); + + layout->setSpacing(10); + + const LayerFactory &layerFactory = map->layerFactory(); + + QIcon scaleToIcon(QStringLiteral("icon:search.svg")); + + const auto ln = layerFactory.layernames(layerType); + for (const QString &layerName : ln) + { + if (nameCondition.match(layerName).hasMatch()) + { + // get layer + AggregationData data; + AggregationWidgets &widgets = m_internal->m_aggregationWidgets[layerName]; + const LayerParameter &layerPara = layerFactory.layerParameter(layerName); + + data.layerName = layerName; + data.minScale = layerPara.minScale; + data.maxScale = layerPara.maxScale; + data.minScaleValid = layerPara.minScaleValid(); + data.maxScaleValid = layerPara.maxScaleValid(); + data.displayName = layerPara.displayName; + + int row = m_internal->m_aggregationList.size(); + + QLabel *laName = new QLabel(layerPara.displayName.isEmpty() ? layerName : layerPara.displayName, this); + widgets.pbReset = new QPushButton(this); + widgets.pbApply = new QPushButton(this); + + widgets.pbReset->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding)); + widgets.pbApply->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding)); + + QHBoxLayout *minLayout = new QHBoxLayout(); + + minLayout->addSpacing(20); + minLayout->addWidget(widgets.cbMinScaleValid = new QCheckBox(QString(), this)); + minLayout->addWidget(widgets.pbMinScale = new QPushButton(QString(), this)); + minLayout->addWidget(widgets.pbMinScaleTo = new QPushButton(scaleToIcon, QString(), this)); + minLayout->addSpacing(20); + setMinScaleLabel(widgets.pbMinScale, layerPara.minScale); + + QHBoxLayout *maxLayout = new QHBoxLayout(); + + maxLayout->addWidget(widgets.cbMaxScaleValid = new QCheckBox(QString(), this)); + maxLayout->addWidget(widgets.pbMaxScale = new QPushButton(QString(), this)); + maxLayout->addWidget(widgets.pbMaxScaleTo = new QPushButton(scaleToIcon, QString(), this)); + + maxLayout->addSpacing(20); + + setMaxScaleLabel(widgets.pbMaxScale, layerPara.maxScale); + + widgets.pbMinScale->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding)); + widgets.pbMaxScale->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding)); + + layout->addWidget(laName, row, 0); + layout->addLayout(minLayout, row, 1); + layout->addLayout(maxLayout, row, 2); + layout->addWidget(widgets.pbReset, row, 3); + layout->addWidget(widgets.pbApply, row, 4); + + m_internal->m_aggregationList << data; + m_internal->m_aggregationReset[layerName] = data; + + widgets.pbMinScale->setProperty("row", row); + widgets.pbMaxScale->setProperty("row", row); + widgets.cbMinScaleValid->setProperty("row", row); + widgets.cbMaxScaleValid->setProperty("row", row); + widgets.pbMinScaleTo->setProperty("row", row); + widgets.pbMaxScaleTo->setProperty("row", row); + + widgets.pbReset->setProperty("row", row); + widgets.pbApply->setProperty("row", row); + + connect(widgets.cbMinScaleValid, &QCheckBox::clicked, this, &LayerAggregationWidget::onSetValid); + connect(widgets.cbMaxScaleValid, &QCheckBox::clicked, this, &LayerAggregationWidget::onSetValid); + connect(widgets.pbMinScale, &QPushButton::clicked, this, &LayerAggregationWidget::onGetMinScale); + connect(widgets.pbMaxScale, &QPushButton::clicked, this, &LayerAggregationWidget::onGetMaxScale); + connect(widgets.pbMinScaleTo, &QPushButton::clicked, this, &LayerAggregationWidget::onZoomTo); + connect(widgets.pbMaxScaleTo, &QPushButton::clicked, this, &LayerAggregationWidget::onZoomTo); + connect(widgets.pbReset, &QPushButton::clicked, this, &LayerAggregationWidget::onReset); + connect(widgets.pbApply, &QPushButton::clicked, this, &LayerAggregationWidget::onApply); + + widgets.cbMinScaleValid->setChecked(data.minScaleValid); + widgets.cbMinScaleValid->setEnabled(data.minScaleValid); + widgets.cbMaxScaleValid->setChecked(data.maxScaleValid); + widgets.cbMaxScaleValid->setEnabled(data.maxScaleValid); + + widgets.pbMinScaleTo->setEnabled(data.minScaleValid); + widgets.pbMaxScaleTo->setEnabled(data.maxScaleValid); + + widgets.pbReset->setEnabled(false); + widgets.pbApply->setEnabled(false); + } + } + + retranslate(); + + if (m_internal->m_aggregationList.isEmpty()) + { + QLabel *lbEmpty = new QLabel(tr("Can't find any layer"), this); + QPixmap icon(QStringLiteral("icon:overlays.svg")); + + lbEmpty->setWindowIcon(icon); + layout->addWidget(lbEmpty, 0, 0, Qt::AlignCenter); + return; + } +} // LayerAggregation::LayerAggregation( Map * map, QObject * parent ) + + +void LayerAggregationWidget::retranslate() +{ + if (m_internal->m_map == nullptr) + return; + + const LayerFactory &layerFactory = m_internal->m_map->layerFactory(); + const auto ln = layerFactory.layernames(m_internal->m_layerType); + for (const QString &layerName : ln) + { + if (!m_internal->m_aggregationWidgets.contains(layerName)) continue; + + AggregationWidgets &widgets = m_internal->m_aggregationWidgets[layerName]; + + widgets.pbReset->setText(tr("Reset")); + widgets.pbApply->setText(tr("Apply to Map")); + + widgets.pbReset->setToolTip(tr("Restore initial scales of this level")); + widgets.pbApply->setToolTip(tr("Set scales to corresponding map layer temporarily")); + + widgets.cbMinScaleValid->setToolTip(tr("Activate or deactivate this scale value")); + widgets.pbMinScale ->setToolTip(tr("Get current map scale of map view")); + widgets.pbMinScaleTo ->setToolTip(tr("Set this scale value to map view as current scale")); + + widgets.cbMaxScaleValid->setToolTip(tr("Activate or deactivate this scale value")); + widgets.pbMaxScale ->setToolTip(tr("Get current map scale of map view")); + widgets.pbMaxScaleTo ->setToolTip(tr("Set this scale value to map view as current scale")); + } +} // void LayerAggregationWidget::retranslate() + + +LayerAggregationWidget::~LayerAggregationWidget() +{ + delete m_internal; m_internal = nullptr; +} // LayerAggregationWidget::~LayerAggregationWidget() + + +void LayerAggregationWidget::onZoomTo() +{ + QPushButton *senderButton = qobject_cast(sender()); + if (nullptr == senderButton) return; + + int row = senderButton->property("row").toInt(); + + if ((row < 0) || (row >= m_internal->m_aggregationList.size())) + return; + + const AggregationData &data = m_internal->m_aggregationList[row]; + const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[data.layerName]; + double scale = 0.0; + + if ((widgets.pbMinScaleTo == senderButton) && (data.minScaleValid)) + scale = data.minScale; + + if ((widgets.pbMaxScaleTo == senderButton) && (data.maxScaleValid)) + scale = data.maxScale; + + if (scale < 1) + return; + + m_internal->m_map->setScale(scale); + +} // void LayerAggregationWidget::onZoomTo() + + +void LayerAggregationWidget::onGetMinScale() +{ + QPushButton *senderButton = qobject_cast(sender()); + if (nullptr == senderButton) return; + + int row = senderButton->property("row").toInt(); + + if ((row < 0) || (row >= m_internal->m_aggregationList.size())) + return; + + double scale = m_internal->m_map->scale(); + + AggregationData &data = m_internal->m_aggregationList[row]; + + if (data.maxScaleValid && (data.maxScale >= scale)) + { + const QString &msg = tr("Chosen scale is too small"); + LtsWidgets::LtsMessageDialog::showMessage(this, msg, tr("Layer Aggregation")); + return; + } + + data.minScale = scale; + data.minScaleValid = true; + + setMinScaleLabel(senderButton, data.minScale); + + const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[data.layerName]; + widgets.cbMinScaleValid->setChecked(true); + widgets.cbMinScaleValid->setEnabled(true); + + widgets.pbMinScaleTo->setEnabled(true); + + widgets.pbReset->setEnabled(true); + widgets.pbApply->setEnabled(true); + + emit changesMade(); +} // void LayerAggregationWidget::onGetMinScale() + + +void LayerAggregationWidget::onGetMaxScale() +{ + QPushButton *senderButton = qobject_cast(sender()); + if (nullptr == senderButton) return; + + int row = senderButton->property("row").toInt(); + + if ((row < 0) || (row >= m_internal->m_aggregationList.size())) + return; + + double scale = m_internal->m_map->scale(); + + AggregationData &data = m_internal->m_aggregationList[row]; + + if (data.minScaleValid && (data.minScale <= scale)) + { + const QString &msg = tr("Chosen scale is too large"); + LtsWidgets::LtsMessageDialog::showMessage(this, msg, tr("Layer Aggregation")); + return; + } + + data.maxScale = scale; + data.maxScaleValid = true; + + setMaxScaleLabel(senderButton, data.maxScale); + + const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[data.layerName]; + widgets.cbMaxScaleValid->setChecked(true); + widgets.cbMaxScaleValid->setEnabled(true); + + widgets.pbMaxScaleTo->setEnabled(true); + + widgets.pbReset->setEnabled(true); + widgets.pbApply->setEnabled(true); + + emit changesMade(); + +} // void LayerAggregationWidget::onGetMaxScale() + + +void LayerAggregationWidget::onSetValid(bool value) +{ + QCheckBox *senderButton = qobject_cast(sender()); + if (nullptr == senderButton) return; + + int row = senderButton->property("row").toInt(); + + if ((row < 0) || (row >= m_internal->m_aggregationList.size())) + return; + + AggregationData &data = m_internal->m_aggregationList[row]; + const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[data.layerName]; + + if (senderButton == widgets.cbMinScaleValid) + { + if (data.maxScaleValid && value && (data.maxScale >= data.minScale)) + { + senderButton->setChecked(false); + const QString &msg = tr("Minimum scale is too small"); + LtsWidgets::LtsMessageDialog::showMessage(this, msg, tr("Layer Aggregation")); + return; + } + data.minScaleValid = value; + } + + if (senderButton == widgets.cbMaxScaleValid) + { + if (data.minScaleValid && value && (data.minScale <= data.maxScale)) + { + senderButton->setChecked(false); + const QString &msg = tr("Maximum scale is too large"); + LtsWidgets::LtsMessageDialog::showMessage(this, msg, tr("Layer Aggregation")); + return; + } + data.maxScaleValid = value; + } + + widgets.pbMinScaleTo->setEnabled(data.minScaleValid); + widgets.pbMaxScaleTo->setEnabled(data.maxScaleValid); + + widgets.pbReset->setEnabled(true); + widgets.pbApply->setEnabled(true); + + emit changesMade(); + +} // void LayerAggregationWidget::onSetValid(bool value) + + +void LayerAggregationWidget::doReset(int row) +{ + if ((row < 0) || (row >= m_internal->m_aggregationList.size())) + return; + + AggregationData &data = m_internal->m_aggregationList[row]; + data = m_internal->m_aggregationReset[data.layerName]; + + const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[data.layerName]; + + setMinScaleLabel(widgets.pbMinScale, data.minScale); + setMaxScaleLabel(widgets.pbMaxScale, data.maxScale); + + widgets.cbMinScaleValid->setChecked(data.minScaleValid); + widgets.cbMinScaleValid->setEnabled(data.minScaleValid); + + widgets.cbMaxScaleValid->setChecked(data.maxScaleValid); + widgets.cbMaxScaleValid->setEnabled(data.maxScaleValid); + + widgets.pbMinScaleTo->setEnabled(data.minScaleValid); + widgets.pbMaxScaleTo->setEnabled(data.maxScaleValid); + + widgets.pbReset->setEnabled(false); + widgets.pbApply->setEnabled(!widgets.pbApply->isEnabled()); + +} // void doReset(SituationMap::Map * map, const AggregationData & data) + + +void LayerAggregationWidget::onReset() +{ + QObject *senderObj = sender(); + if (nullptr == senderObj) return; + + int row = senderObj->property("row").toInt(); + + if ((row < 0) || (row >= m_internal->m_aggregationList.size())) + return; + + doReset(row); + +} // void LayerAggregationWidget::onReset() + + +static void doApply(SituationMap::Map *map, const AggregationData &data) +{ + LayerFactory &layerFactory = map->layerFactory(); + LayerParameter layerParamter = layerFactory.layerParameter(data.layerName); + + layerParamter.minScale = data.minScaleValid ? data.minScale : LayerParameter::UNDEFINED_SCALE; // 0; + layerParamter.maxScale = data.maxScaleValid ? data.maxScale : LayerParameter::UNDEFINED_SCALE; // 0; + + layerFactory.setLayerParameter(data.layerName, layerParamter); +} // void doApply(SituationMap::Map * map, const AggregationData & data) + + +void LayerAggregationWidget::onApply() +{ + QObject *senderObj = sender(); + if (!senderObj) + return; + + int row = senderObj->property("row").toInt(); + + if ((row < 0) || (row >= m_internal->m_aggregationList.size())) + return; + + const AggregationData &data = m_internal->m_aggregationList[row]; + doApply(m_internal->m_map, data); + + const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[data.layerName]; + widgets.pbApply->setEnabled(false); + +} // void LayerAggregationWidget::onApply() + + +void LayerAggregationWidget::onApplyAll() +{ + for (const AggregationData &data : std::as_const(m_internal->m_aggregationList)) + { + const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[data.layerName]; + if (widgets.pbApply->isEnabled()) + { + doApply(m_internal->m_map, data); + widgets.pbApply->setEnabled(false); + } + } +} // void LayerAggregationWidget::onApplyAll() + + +void LayerAggregationWidget::onResetAll() +{ + for (const auto &aggData : std::as_const(m_internal->m_aggregationList)) + { + const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[aggData.layerName]; + + doReset(widgets.pbApply->property("row").toInt()); + doApply(m_internal->m_map, aggData); + widgets.pbApply->setEnabled(false); + } +} // void LayerAggregationWidget::onResetAll() + + +void LayerAggregationWidget::onSetDefaultScales() +{ + if (m_internal->m_aggregationList.isEmpty()) + return; + + // Calculate scale defaults here ... + + double mapMinScaleSQRT = sqrt(m_internal->m_map->minScale()); + double mapMaxScaleSQRT = sqrt(m_internal->m_map->maxScale()); + + const int aggregationListCount = m_internal->m_aggregationList.size(); + double stepScaleSQRT = (mapMinScaleSQRT - mapMaxScaleSQRT) / static_cast(aggregationListCount); + + unsigned int step = 0; + + for (auto &aggData : m_internal->m_aggregationList) + { + double dScaleSQRT = mapMaxScaleSQRT + (step * stepScaleSQRT); + aggData.maxScaleValid = true; + aggData.maxScale = dScaleSQRT * dScaleSQRT; + + dScaleSQRT = mapMaxScaleSQRT + ((step + 1) * stepScaleSQRT); + aggData.minScaleValid = true; + aggData.minScale = dScaleSQRT * dScaleSQRT; + + } // for + + // disable start and end + if (aggregationListCount > 0) + { + // front -> max + m_internal->m_aggregationList.front().maxScale = 0; + m_internal->m_aggregationList.front().maxScaleValid = false; + // back -> min + m_internal->m_aggregationList.back().minScale = 0; + m_internal->m_aggregationList.back().minScaleValid = false; + } // if + + // update ui to m_aggregationList: + for (const auto &aggData : std::as_const(m_internal->m_aggregationList)) + { + const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[aggData.layerName]; + widgets.cbMinScaleValid->setEnabled(aggData.minScaleValid); + widgets.cbMinScaleValid->setChecked(aggData.minScaleValid); + widgets.pbMinScaleTo->setEnabled(aggData.minScaleValid); + + widgets.cbMaxScaleValid->setEnabled(aggData.maxScaleValid); + widgets.cbMaxScaleValid->setChecked(aggData.maxScaleValid); + widgets.pbMaxScaleTo->setEnabled(aggData.maxScaleValid); + + widgets.pbApply->setEnabled(true); + widgets.pbReset->setEnabled(true); + + setMinScaleLabel(widgets.pbMinScale, aggData.minScale); + setMaxScaleLabel(widgets.pbMaxScale, aggData.maxScale); + } // for + + emit changesMade(); + +} // void LayerAggregationWidget::onSetDefaultScales() diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LayerAggregation/layer_aggregation_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LayerAggregation/layer_aggregation_widget.h new file mode 100755 index 000000000..12c4efa88 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LayerAggregation/layer_aggregation_widget.h @@ -0,0 +1,110 @@ +/** +** +** @RDE_HEADER@ +** Handles aggregation of participant layer +** $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 .........: +** +** 16.08.2017, JT : first edition +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include + +#include + +#include + + +class QPushButton; +class QRegularExpression; + +namespace SituationMap +{ +class Map; + +/** + @class LayerAggregationWidget layer_aggregation_widget.h + @brief Class to handle aggregation of participant layer +*/ + +class SITUATIONMAP_EXPORT LayerAggregationWidget : public QWidget +{ + Q_OBJECT + +public: + + /** + Constructor to build gui widget for handling aggregation layers. + + @param map Map the layer are part of + @param layerType Type of layer in general this will be LayerType::Operational + @param nameCondition If the nameCondition doesn't match with the layername the layer will be skipped and is not handled. + Example of an expression "^Participant.+". + @param parent Parent for Qt object hierarchy + */ + explicit LayerAggregationWidget(Map *map, LayerType layerType, const QRegularExpression &nameCondition, QWidget *parent); + + /** + Destructor to clean up memory. + */ + ~LayerAggregationWidget(); + + void retranslate(); + +public slots: + + void onApplyAll(); + void onResetAll(); + + void onSetDefaultScales(); + +signals: + + void changesMade(); + +protected slots: + + void onSetValid(bool value); + + void onGetMinScale(); + void onGetMaxScale(); + void onZoomTo(); + void onReset(); + void onApply(); + +private: + + void doReset(int row); + + static QString scaleText(double scale); + + static void setMinScaleLabel(QPushButton *btn, double scale); + static void setMaxScaleLabel(QPushButton *btn, double scale); + + class InternalData; + + InternalData *m_internal; + +}; // LayerAggregationWidget + +} /* namespace SituationMap */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LayerAggregation/participant_aggregation_dialog.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LayerAggregation/participant_aggregation_dialog.cpp new file mode 100755 index 000000000..16b9e09ea --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LayerAggregation/participant_aggregation_dialog.cpp @@ -0,0 +1,105 @@ +/** +** +** @RDE_HEADER@ +** Dialog of aggregation of participant layer +** $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 .........: +** +** 18.08.2017, JT : first edition +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "participant_aggregation_dialog.h" +#include "layer_aggregation_widget.h" + +#include + +#include + + +using namespace SituationMap; + + +ParticipantAggregationDialog::ParticipantAggregationDialog(Map *map, QWidget *parent) + : LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel) +{ + m_law = new LayerAggregationWidget(map, LayerType::Operational, QRegularExpression(QStringLiteral("^Participant.+"), QRegularExpression::CaseInsensitiveOption), this); + + if (map) + connect(map, &Map::beginDestroy, this, &ParticipantAggregationDialog::onMapDestroyed, Qt::UniqueConnection); + + QVBoxLayout *layout = new QVBoxLayout(this); + + layout->addWidget(m_law); + layout->addStretch(2); + + LtsWidgets::DialogButton btnSetDefault = LtsWidgets::DialogButton::Print; + + m_buttonBox->connectButton(btnSetDefault, m_law, &LayerAggregationWidget::onSetDefaultScales); + + m_buttonBox->setButtonEnabled(btnSetDefault, true); + m_buttonBox->setButtonVisible(btnSetDefault, true); + + connect(this, &ParticipantAggregationDialog::canceled, m_law, &LayerAggregationWidget::onResetAll); + connect(this, &ParticipantAggregationDialog::applied, m_law, &LayerAggregationWidget::onApplyAll); + + connect(m_law, &LayerAggregationWidget::changesMade, this, &ParticipantAggregationDialog::slotChangesMade); + + retranslate(); + + setOnlineHelpFilename(QStringLiteral("Situation-Participant_Aggregation")); + +} // ParticipantAggregationDialog::ParticipantAggregationDialog( Map * map, QWidget * parent ) + + +void ParticipantAggregationDialog::retranslate() +{ + // lupdate doesn't like translate(tr("foo")) ... + const QString txt(tr("Participant Aggregation")); + translate(txt); + + LtsWidgets::DialogButton btnSetDefault = LtsWidgets::DialogButton::Print; + + m_buttonBox->setText( btnSetDefault, tr("Set Scale Defaults")); + m_buttonBox->setToolTip( btnSetDefault, tr("Calculate scale values for each aggregation from map scale range")); + + m_law->retranslate(); + +} // void ParticipantAggregationDialog::retranslate() + + +void ParticipantAggregationDialog::dialogCanceled() +{ + emit canceled(); +} // void ParticipantAggregationDialog::dialogCanceled() + + +bool ParticipantAggregationDialog::apply() +{ + emit applied(); + return true; +} + +void ParticipantAggregationDialog::onMapDestroyed() +{ + // commit suicide + delete this; +} // bool ParticipantAggregationDialog::apply() + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LayerAggregation/participant_aggregation_dialog.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LayerAggregation/participant_aggregation_dialog.h new file mode 100755 index 000000000..2521f3765 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LayerAggregation/participant_aggregation_dialog.h @@ -0,0 +1,79 @@ +/** +** +** @RDE_HEADER@ +** Dialog of aggregation of participant layer +** $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 .........: +** +** 18.08.2017, JT : first edition +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + + +#include + +#include + +#include + + +namespace SituationMap +{ +class Map; +class LayerAggregationWidget; + +/** + @class ParticpantAggregation particpant_aggregation.h + @brief Class to handle aggregation of participant layer dialog +*/ + +class SITUATIONMAP_EXPORT ParticipantAggregationDialog : public LtsWidgets::LtsDialog +{ + Q_OBJECT + +public: + + explicit ParticipantAggregationDialog(SituationMap::Map *map, QWidget *parent); + ~ParticipantAggregationDialog() = default; + + void retranslate() override; + + void dialogCanceled() override; + +protected: + + bool apply() override; + +Q_SIGNALS: + void canceled(); + void applied(); + +protected Q_SLOTS: + void onMapDestroyed(); + +private: + + LayerAggregationWidget *m_law; + +}; // ParticipantAggregationDialog + +} /* namespace SituationMap */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LocalSrv/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LocalSrv/CMakeLists.txt new file mode 100755 index 000000000..ad1d98874 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LocalSrv/CMakeLists.txt @@ -0,0 +1,37 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +# LIBRARY + +SET(LocalServer_SRCS + local_server_export + local_server.cpp + local_server.h +) + +SOURCE_GROUP(LocalServer FILES ${LocalServer_SRCS}) + +NGL_ADD_LIBRARY(WS situationMapLocalServer SHARED ${LocalServer_SRCS}) +NGL_TARGET_LINK_LIBRARIES(situationMapLocalServer + PRIVATE + ArcGIS::ArcGIS + NGLLogger + Qt6::Gui # can be removed once we get an official ESRI ArcGIS Qt version again + PUBLIC + Qt6::Core + situationMapCommon +) + +# ESRI: /usr/include/arcgis/LocalServer/LocalServer.h: '#include "Object.h"' +# so add required include path here as PRIVATE: +NGL_TARGET_INCLUDE_DIRECTORIES(situationMapLocalServer + PRIVATE + ${ARCGIS_INCLUDE_DIR}/arcgis + INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +NGL_INSTALL_TARGET(situationMapLocalServer) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LocalSrv/local_server.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LocalSrv/local_server.cpp new file mode 100755 index 000000000..34624bdf7 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LocalSrv/local_server.cpp @@ -0,0 +1,519 @@ +/** + * @RDE_HEADER@ + * LocalServer + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "local_server.h" +#include + + +// ESRI +#include + +#include +#include + + +//Qt +#include + + +namespace Esri +{ +namespace ArcGISRuntime +{ + +QString toString(const Esri::ArcGISRuntime::Error &value) +{ + std::ostringstream stream; + stream << "Esri::ArcGISRuntime::Error: " + << " code=" << value.code() + << ", domain=" << (int)value.domain() + << ", extendedErrorType=" << (int)value.extendedErrorType() + << ", message='" << value.message() << "'" + << ", additionalMessage='" << value.additionalMessage() << "'"; + return QString::fromStdString(stream.str()); +} + +std::ostream &operator<< (std::ostream &stream, + const Esri::ArcGISRuntime::Error &value) +{ + stream << toString(value); + return stream; +} + +} // namespace ArcGISRuntime +} // namespace Esri + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +bool LocalServiceData::isEmpty() const +{ + return m_svcUrl.isEmpty(); +} + +void LocalServiceData::setSvcUrl(const QUrl &svcUrl) +{ + m_svcUrl = svcUrl; +} + +const QUrl &LocalServiceData::svcUrl() const +{ + return m_svcUrl; +} + +void LocalServiceData::setSync(bool sync) +{ + m_sync = sync; +} + +bool LocalServiceData::isSync() const +{ + return m_sync; +} + + +//---------------------------------------------------------------------------- + +static QString toString(const Esri::ArcGISRuntime::LocalServerStatus &value) +{ + switch (value) + { + case Esri::ArcGISRuntime::LocalServerStatus::Stopped: + return QLatin1String("Stopped"); + case Esri::ArcGISRuntime::LocalServerStatus::Starting: + return QLatin1String("Starting"); + case Esri::ArcGISRuntime::LocalServerStatus::Started: + return QLatin1String("Started"); + case Esri::ArcGISRuntime::LocalServerStatus::Stopping: + return QLatin1String("Stopping"); + case Esri::ArcGISRuntime::LocalServerStatus::Failed: + return QLatin1String("Failed"); + } // switch + return QLatin1String("Esri-LocalServerStatusERROR"); +} + + + +//----------------------------------------------------------------------------- +class LocalServer::Private +{ +public: + Private() = default; + ~Private() = default; + + // ESRI data: + Esri::ArcGISRuntime::LocalServer *m_localServer = nullptr; + + // Own data: + QMap m_gpServiceMap; + QMap m_serviceSubLocationMap; + + + QMap m_svcData; + + +}; // class LocalServer::Private +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +LocalServer *LocalServer::instance() +{ + static LocalServer *_instance = nullptr; + if (_instance == nullptr) + _instance = new LocalServer; + return _instance; +} + +//----------------------------------------------------------------------------- +LocalServer::LocalServer() + : BaseClass() + , m_logger(getLogger("LocalServer")) + , d(new Private) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " installPath = " + << " '" << Esri::ArcGISRuntime::LocalServer::installPath() << "'"; + + QString newInstallPath; + + QString user = QString::fromLocal8Bit( qgetenv("USER") ); + if (user.compare(QLatin1Literal("ctc")) == 0) + newInstallPath = SituationMap::getResourcePath() + QLatin1String("/localserver100.2_ctc/"); // USER ctc + else + newInstallPath = SituationMap::getResourcePath() + QLatin1String("/localserver100.2/"); // USER ctc_ws + + Esri::ArcGISRuntime::LocalServer::setInstallPath(newInstallPath); + + if (Esri::ArcGISRuntime::LocalServer::isInstallValid()) + NOTICE_LOG(m_logger) << LOGFUNCTION << " installPath ok = " + << " '" << Esri::ArcGISRuntime::LocalServer::installPath() << "'"; + else + FATAL_LOG(m_logger) << LOGFUNCTION << " installPath failed = " + << " InstallPath = '" << Esri::ArcGISRuntime::LocalServer::installPath() << "'"; + + d->m_localServer = Esri::ArcGISRuntime::LocalServer::instance(); + + connect(d->m_localServer, &Esri::ArcGISRuntime::LocalServer::errorOccurred, + this, &LocalServer::onLocalServerError); + connect(d->m_localServer, &Esri::ArcGISRuntime::LocalServer::statusChanged, + this, &LocalServer::onLocalServerStatusChanged); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +LocalServer::~LocalServer() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + this->stop(); + delete d; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void LocalServer::start() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + d->m_localServer->start(); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} +void LocalServer::stop() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + stopAllLocalGeoprocessingServices(); + d->m_localServer->stop(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +bool LocalServer::isLocalServerStarted() const +{ + return d->m_localServer->status() == Esri::ArcGISRuntime::LocalServerStatus::Started; +} +bool LocalServer::isLocalServerStopped() const +{ + return d->m_localServer->status() == Esri::ArcGISRuntime::LocalServerStatus::Stopped; +} + + +bool LocalServer::setLocalGeoprocessingService(const QString &serviceName, + const QString &packagePath, + const QString &serviceSubLocation, + bool sync) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " serviceName: '" << serviceName << "'" + << " packagePath: '" << packagePath << "'" + << " serviceSubLocation: '" << serviceSubLocation << "'"; + if (d->m_gpServiceMap.contains(serviceName)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName: '" << serviceName << "'" << " already provided"; + return false; + } + + QString localPackagePath = packagePath; + + { + QFileInfo fileinfo(packagePath); + if (!fileinfo.isAbsolute()) + localPackagePath = SituationMap::getResourcePath() + QLatin1String("/gpk/") + packagePath; + } + QFileInfo fileinfo(localPackagePath); + + if (!fileinfo.exists()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName: '" << serviceName << "'" + << " packagePath does not exist: " + << " localPackagePath: '" << localPackagePath << "'"; + return false; + } + if (!fileinfo.isReadable()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName: '" << serviceName << "'" + << " packagePath is not readable: " + << " localPackagePath: '" << localPackagePath << "'"; + return false; + } + + + NOTICE_LOG(m_logger) << LOGFUNCTION << " create LocalGeoprocessingService: " + << " localPackagePath: '" << localPackagePath << "'"; + + + Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = + new Esri::ArcGISRuntime::LocalGeoprocessingService(localPackagePath, this); + lgps->setObjectName(QString(QLatin1Literal("LocalGeoprocessingService-%1")).arg(serviceName)); + + if (sync) + lgps->setServiceType(Esri::ArcGISRuntime::GeoprocessingServiceType::SynchronousExecute); // SynchronousExecute | AsynchronousSubmit | ... + else + lgps->setServiceType(Esri::ArcGISRuntime::GeoprocessingServiceType::AsynchronousSubmit); // SynchronousExecute | AsynchronousSubmit | ... + + connect(lgps, &Esri::ArcGISRuntime::LocalGeoprocessingService::errorOccurred, + this, &LocalServer::onLGPSError); + connect(lgps, &Esri::ArcGISRuntime::LocalGeoprocessingService::statusChanged, + this, &LocalServer::onLGPSStatusChanged); + + // add to own management: + d->m_gpServiceMap[serviceName] = lgps; + d->m_serviceSubLocationMap[serviceName] = serviceSubLocation; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " serviceName: '" << serviceName << "'" + << " localPackagePath: '" << localPackagePath << "'" + << " serviceSubLocation: '" << serviceSubLocation << "'"; + return true; +} + +bool LocalServer::startLocalGeoprocessingService(const QString &serviceName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!d->m_gpServiceMap.contains(serviceName)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << " not found"; + return false; + } + Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = d->m_gpServiceMap[serviceName]; + if (!lgps) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", no service"; + return false; + } + + if (isLocalServerStarted()) + lgps->start(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +bool LocalServer::stopLocalGeoprocessingService(const QString &serviceName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!d->m_gpServiceMap.contains(serviceName)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << " not found"; + return false; + } + Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = d->m_gpServiceMap[serviceName]; + if (!lgps) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", no service"; + return false; + } + + lgps->stop(); + return true; +} + +bool LocalServer::isLocalGeoprocessingServiceStarted(const QString &serviceName) const +{ + if (!d->m_gpServiceMap.contains(serviceName)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << " not found"; + return false; + } + Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = d->m_gpServiceMap[serviceName]; + if (!lgps) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", no service"; + return false; + } + return lgps->status() == Esri::ArcGISRuntime::LocalServerStatus::Started; +} +bool LocalServer::isLocalGeoprocessingServiceStopped(const QString &serviceName) const +{ + if (!d->m_gpServiceMap.contains(serviceName)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << " not found"; + return false; + } + Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = d->m_gpServiceMap[serviceName]; + if (!lgps) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", no service"; + return false; + } + return lgps->status() == Esri::ArcGISRuntime::LocalServerStatus::Stopped; +} + +bool LocalServer::isLocalGeoprocessingServiceSynchronousExecute(const QString &serviceName) const +{ + if (!d->m_gpServiceMap.contains(serviceName)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << " not found"; + return false; + } + Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = d->m_gpServiceMap[serviceName]; + if (!lgps) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", no service"; + return false; + } + return lgps->serviceType() == Esri::ArcGISRuntime::GeoprocessingServiceType::SynchronousExecute; +} + +bool LocalServer::isLocalGeoprocessingServiceAsynchronousSubmit(const QString &serviceName) const +{ + if (!d->m_gpServiceMap.contains(serviceName)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << " not found"; + return false; + } + Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = d->m_gpServiceMap[serviceName]; + if (!lgps) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", no service"; + return false; + } + return lgps->serviceType() == Esri::ArcGISRuntime::GeoprocessingServiceType::AsynchronousSubmit; +} + +bool LocalServer::localGeoprocessingServiceData(const QString &serviceName, LocalServiceData &localServiceDataOut) const +{ + if (!d->m_svcData.contains(serviceName)) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << " not found"; + return false; + } + localServiceDataOut = d->m_svcData[serviceName]; + return true; +} + +void LocalServer::startAllPendingLocalGeoprocessingServices() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + QMapIterator mapIt(d->m_gpServiceMap); + + while (mapIt.hasNext()) + { + mapIt.next(); + const QString &serviceName = mapIt.key(); + Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = mapIt.value(); + + if (!lgps) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", no service"; + continue; + } + + if (lgps->status() == Esri::ArcGISRuntime::LocalServerStatus::Started) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", already started, continue"; + continue; + } + else + lgps->start(); + } // while + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void LocalServer::stopAllLocalGeoprocessingServices() +{ + QMapIterator mapIt(d->m_gpServiceMap); + + while (mapIt.hasNext()) + { + mapIt.next(); + const QString &serviceName = mapIt.key(); + Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = mapIt.value(); + + if (!lgps) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", no service"; + continue; + } + lgps->stop(); + } // while + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +void LocalServer::onLocalServerError(const Esri::ArcGISRuntime::Error &error) +{ + ERROR_LOG(m_logger) << LOGFUNCTION << " EsriError occured: " << error; +} + +void LocalServer::onLocalServerStatusChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + NOTICE_LOG(m_logger) << LOGFUNCTION << " LocalServer: status changed to: " << toString(d->m_localServer->status()); + + emit localServerStatusChanged(); + + if (d->m_localServer->status() == Esri::ArcGISRuntime::LocalServerStatus::Started) + { + NOTICE_LOG(m_logger) << LOGFUNCTION << " LocalServer: STARTED" + << " (url= '" << d->m_localServer->url().toString() << "')" + << " (Esri::ArcGISRuntime::LocalServer->tempDataPath= '" << d->m_localServer->tempDataPath() << "')"; + startAllPendingLocalGeoprocessingServices(); + } + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void LocalServer::onLGPSError(const Esri::ArcGISRuntime::Error &error) +{ + QString senderName; + Esri::ArcGISRuntime::LocalGeoprocessingService *lgs = qobject_cast(sender()); + if (lgs) + senderName = lgs->objectName(); + ERROR_LOG(m_logger) << LOGFUNCTION << " LocalGeoprocessingService -> EsriError occured: " << error; +} + +void LocalServer::onLGPSStatusChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + Esri::ArcGISRuntime::LocalGeoprocessingService *lgsp = qobject_cast(sender()); + + if (!lgsp) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " qobject_cast failed to get sender!?"; + return; + } + + QString senderName = lgsp->objectName(); + const QString &serviceName = d->m_gpServiceMap.key(lgsp); + + if (lgsp->status() == Esri::ArcGISRuntime::LocalServerStatus::Started) + { + NOTICE_LOG(m_logger) << LOGFUNCTION << " ***** LocalGeoprocessingService:" + << " sender= '" << senderName << "'" + << " status changed to: " << toString(lgsp->status()) + << " url= '" << lgsp->url().toString() << "'"; + + LocalServiceData svcData; + + const QString &serviceSubLocation = d->m_serviceSubLocationMap[serviceName]; + const QString &url = lgsp->url().toString() + QLatin1String("/") + serviceSubLocation; + svcData.setSvcUrl(QUrl(url)); + svcData.setSync(lgsp->serviceType() == Esri::ArcGISRuntime::GeoprocessingServiceType::SynchronousExecute); + + d->m_svcData[serviceName] = svcData; // store in own management + + emit localGeoprocessingServiceStatusChanged(serviceName); + emit localGeoprocessingServiceStarted(serviceName, svcData); + } + + NOTICE_LOG(m_logger) << LOGFUNCTION << " LocalGeoprocessingService:" + << " sender= '" << senderName << "'" + << " status changed to: " << toString(lgsp->status()); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LocalSrv/local_server.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LocalSrv/local_server.h new file mode 100755 index 000000000..0fc760b25 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LocalSrv/local_server.h @@ -0,0 +1,128 @@ +/** + * @RDE_HEADER@ + * LocalServer + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "local_server_export.h" +#include + +namespace Esri { +namespace ArcGISRuntime { +class Error; +} +} + + + +// Qt +#include +#include +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +class LOCALSERVER_EXPORT LocalServiceData +{ +private: + QUrl m_svcUrl; + bool m_sync = false; +public: + bool isEmpty() const; + + void setSvcUrl(const QUrl &svcUrl); + const QUrl &svcUrl() const; + + void setSync(bool sync); + bool isSync() const; +}; + + + +/** + * @brief The LocalServer class wrap ESRI`s LocalServer. + * This is a container for geoprocessing services + * The LocalServer has to be started as well as the seperate instances of LocalGeoprocessingService. + * To ease the usage you should set the required number of LocalGeoprocessingService (with different serviceNames). + * Then simply start the LocalServer (and if that is up all pending will be started.) + */ +class LOCALSERVER_EXPORT LocalServer : public QObject +{ + Q_OBJECT + + using BaseClass = QObject; + +private: + explicit LocalServer(); + ~LocalServer(); + +public: + /*! \name Check Installation ( no guarantee )*/ + static bool isInstallValid(); + + + /*! \name Singleton */ + static LocalServer *instance(); + + + /*! \name LocalServer */ + //@{ + void start(); + void stop(); + + bool isLocalServerStarted() const; + bool isLocalServerStopped() const; + //@} + + /*! \name LocalGeoprocessingService */ + //@{ + bool setLocalGeoprocessingService(const QString &serviceName, const QString &packagePath, const QString &serviceSubLocation, bool sync); + bool startLocalGeoprocessingService(const QString &serviceName); + bool stopLocalGeoprocessingService(const QString &serviceName); + + bool isLocalGeoprocessingServiceStarted(const QString &serviceName) const; + bool isLocalGeoprocessingServiceStopped(const QString &serviceName) const; + + bool isLocalGeoprocessingServiceSynchronousExecute(const QString &serviceName) const; + bool isLocalGeoprocessingServiceAsynchronousSubmit(const QString &serviceName) const; + + bool localGeoprocessingServiceData(const QString &serviceName, LocalServiceData &localServiceDataOut) const; + //@} + +protected: + void startAllPendingLocalGeoprocessingServices(); + void stopAllLocalGeoprocessingServices(); + +protected Q_SLOTS: + void onLocalServerError(const Esri::ArcGISRuntime::Error &error); + void onLocalServerStatusChanged(); + + void onLGPSError(const Esri::ArcGISRuntime::Error &error); + void onLGPSStatusChanged(); + +Q_SIGNALS: + void localServerStatusChanged(); + + void localGeoprocessingServiceStatusChanged(const QString &serviceName); + void localGeoprocessingServiceStarted(const QString &serviceName, const LocalServiceData&localSvcData); + +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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LocalSrv/local_server_export.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LocalSrv/local_server_export.h new file mode 100755 index 000000000..4d9f2e0a8 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/LocalSrv/local_server_export.h @@ -0,0 +1,21 @@ +/** + * Export definitions + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include + +#ifndef LOCALSERVER_EXPORT +# ifdef BUILD_STATIC_LIBRARIES +# define LOCALSERVER_EXPORT +# else +# if defined(MAKE_SITUATIONMAPLOCALSERVER_LIB) +# define LOCALSERVER_EXPORT Q_DECL_EXPORT +# else +# define LOCALSERVER_EXPORT Q_DECL_IMPORT +# endif +# endif +#endif diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MIGRATION_STATUS.md b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MIGRATION_STATUS.md new file mode 100644 index 000000000..297141c50 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MIGRATION_STATUS.md @@ -0,0 +1,81 @@ +# SituationMap Qt6 Migration Status + +## Summary +The SituationMap module has been successfully migrated from Qt5 to Qt6 at the **source code level**. All Qt5 API calls have been updated to Qt6 equivalents. + +## Completed Changes + +### Qt6 API Migrations +1. **QStringView::slice() → sliced()** - Fixed in 5 files: + - Symbology/sidc_codec.cpp + - Symbology/sidc_builder.cpp + - Symbology/sidc_builder_base.cpp + - Symbology/symbol_properties.cpp + - SymbologyImpl/symbol_dictionary_impl.cpp + +2. **QSortFilterProxyModel filter changes** - Fixed in base_symbol_model.cpp: + - beginFilterChange()/endFilterChange() → invalidateFilter() (3 occurrences) + +3. **QFuture const-correctness** - Fixed in base_symbol_model.cpp: + - Added `mutable` keyword to 3 lambdas capturing QFuture for waitForFinished() calls + +### Directory Structure +All 22 subdirectories from Qt5 have been copied to Qt6: +- admin_scripts, ArcGISQt5Hack, ElevationMap, EvalApps, FunctionDialog +- GeometryModel, GraphicalObjects, LayerAggregation, LocalSrv +- MapImpl, MapObjects, MapObjectsImpl, MathUtils, ModuleTests +- Portal, README, Resources, Rubberbands, SideBar +- Symbology, SymbologyGui, Tests, UnitTests, UnitTestsGeoModel, Utils + +## Build Status + +### Without ArcGIS (BUILD_ARCGIS=OFF) +✅ Source code compiles successfully +⚠️ Some unrelated modules (e.g., EEMFakeServer) require Qt6WebSockets which is not installed + +### With ArcGIS (BUILD_ARCGIS=ON) +❌ **Linker errors** - ArcGIS SDK binary incompatibility + +## Critical Issue: ArcGIS SDK Compatibility + +### Problem +The ArcGIS Maps SDK for Qt v300.0.0 library (`libEsriRuntimeQt.a`) was built against **Qt 6.8+**, but the system has **Qt 6.2.4** installed. + +**Evidence:** +- ArcGIS SDK template requires: `Qt 6.8.2` minimum (per sdk/ideintegration/ArcGISRuntimeQtTemplate300_0_0/CMakeLists.txt) +- System Qt version: `6.2.4` +- Linker errors show undefined Qt6 internal symbols: + - `QMetaObject::invokeMethodImpl` + - `QFutureInterfaceBase::hasException()` + - `QMetaType::registerHelper` + - `QtPrivate::QMetaTypeInterfaceWrapper` + +### Required Resolution +To build with ArcGIS support, one of the following is needed: + +1. **Upgrade Qt to 6.8+** (recommended) + - Install Qt 6.8.2 or later + - Rebuild entire project against Qt 6.8+ + +2. **Obtain Qt 6.2-compatible ArcGIS SDK** + - Contact Esri for an ArcGIS SDK build compatible with Qt 6.2.x + - Note: ArcGIS SDK v300 may not support Qt 6.2 + +3. **Disable ArcGIS temporarily** + - Build with `-DBUILD_ARCGIS=OFF` + - Complete migration of non-ArcGIS code + - Re-enable ArcGIS when compatible SDK is available + +## Next Steps + +1. **Immediate**: Decide on ArcGIS strategy (upgrade Qt, get compatible SDK, or disable) +2. **If disabling ArcGIS**: Complete migration of remaining 27 plugin files with beginFilterChange()/endFilterChange() +3. **After ArcGIS resolution**: Run full test suite to validate migration + +## Files Modified in This Session +- Lts/Viewer/SituationMap/Symbology/sidc_codec.cpp +- Lts/Viewer/SituationMap/Symbology/sidc_builder.cpp +- Lts/Viewer/SituationMap/Symbology/sidc_builder_base.cpp +- Lts/Viewer/SituationMap/Symbology/symbol_properties.cpp +- Lts/Viewer/SituationMap/SymbologyImpl/symbol_dictionary_impl.cpp +- Lts/Viewer/SituationMap/SymbologyImpl/base_symbol_model.cpp diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/abstract_effect_simulation_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/abstract_effect_simulation_impl.cpp new file mode 100755 index 000000000..589b3b9ed --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/abstract_effect_simulation_impl.cpp @@ -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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/abstract_effect_simulation_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/abstract_effect_simulation_impl.h new file mode 100755 index 000000000..ede3fd20f --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/abstract_effect_simulation_impl.h @@ -0,0 +1,52 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of AbstractEffectSimulation + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_map_object_impl.h" +#include // 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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/abstract_map_object_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/abstract_map_object_impl.cpp new file mode 100755 index 000000000..31908463d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/abstract_map_object_impl.cpp @@ -0,0 +1,1206 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of AbstractMapObject + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "abstract_map_object_impl.h" +#include "map_impl.h" +#include "object_container_impl.h" + +#include + +#include + +#include +#include +#include +#include +#include +#include + + +// ESRI +#include +#include +#include +#include + + +// Qt + + +namespace SituationMap +{ + +struct ExtraData +{ + QVector propertyNames; + QVector propertyValues; +}; + + + +AbstractMapObjectImpl::AbstractMapObjectImpl(const QString &debugInfo) + : AbstractMapObject() + , m_uniqueMapId(SituationMap::INVALID_MAP_ID) + , m_logger(getLogger("AbstractMapObjectImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " debugInfo='" << debugInfo << "'"; + m_debugInfo = debugInfo; + TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " debugInfo='" << debugInfo << "'"; +} // AbstractMapObjectImpl::AbstractMapObjectImpl + + +AbstractMapObjectImpl::~AbstractMapObjectImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo=" << m_debugInfo + << " uniqueMapId=" << m_uniqueMapId; + delete m_mapConnector; + m_mapConnector = nullptr; + + resetPin(); + + delete m_tacticalAppearance; + m_tacticalAppearance = nullptr; + + delete m_mainGraphicalObjectText; + m_mainGraphicalObjectText = nullptr; + + resetGraphicalObjectExtrusion(); + + delete m_mainGraphicalObject; + m_mainGraphicalObject = nullptr; + + delete m_extraData; + m_extraData = nullptr; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo=" << m_debugInfo + << " uniqueMapId=" << m_uniqueMapId; + +} // AbstractMapObjectImpl::~AbstractMapObjectImpl + + +void AbstractMapObjectImpl::removeMySelf() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo=" << m_debugInfo; + + // To avoid accessing already deleted variables make some local copies 2016/01/08 JT + QString debugInfo = m_debugInfo; + UniqueMapId uid = m_uniqueMapId; + SituationMap::LOGGER_T logger(m_logger); + + if (m_mapImpl) + { + TRACE_LOG(m_logger) << "removeObject, uniqueMapId = " << m_uniqueMapId; + m_uniqueMapId = SituationMap::INVALID_MAP_ID; + m_mapImpl->objectContainer().removeObject(uid); + + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " m_mapImpl does not exist."; + + // Attention CRASH here if AbstractMapObject is not been referenced by a RefPointer. In this case the this-object has been deleted immediately 2016/01/08 JT + TRACE_LOG(logger) << LOGFUNCTION << " END" + << " debugInfo=" << debugInfo; +} + +bool AbstractMapObjectImpl::setProperty(const char *name, const QVariant &value) +{ + // following setProperty/property approach is take from qt4.8.5: + + if (!m_extraData) + m_extraData = new ExtraData; + + const int idx = m_extraData->propertyNames.indexOf(name); + + if (!value.isValid()) + { + if (idx == -1) + return false; + m_extraData->propertyNames.removeAt(idx); + m_extraData->propertyValues.removeAt(idx); + } + else + { + if (idx == -1) + { + m_extraData->propertyNames.append(name); + m_extraData->propertyValues.append(value); + } + else + m_extraData->propertyValues[idx] = value; + } + + return true; +} + +QVariant AbstractMapObjectImpl::property(const char *name) const +{ + if (!m_extraData) + return QVariant(); + const int i = m_extraData->propertyNames.indexOf(name); + return m_extraData->propertyValues.value(i); +} + + +UniqueMapId AbstractMapObjectImpl::uniqueMapId() const +{ + return m_uniqueMapId; +} + + +QString AbstractMapObjectImpl::debugInfo() const +{ + return m_debugInfo; +} + +void AbstractMapObjectImpl::setDebugInfo(const QString &debugInfo) +{ + m_debugInfo = debugInfo; + + if (m_mainGraphicalObject) + m_mainGraphicalObject->setDebugInfo(m_debugInfo + QLatin1String("#mainGO")); + else + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; + + if (m_mainGraphicalObjectText) + m_mainGraphicalObjectText->setDebugInfo(m_debugInfo + QLatin1String("#Text")); + + if (m_tacticalAppearance) + m_tacticalAppearance->setDebugInfo(m_debugInfo + QLatin1String("#TactApp")); + +} + +QString AbstractMapObjectImpl::toString() const +{ + QString str; + + if (!m_debugInfo.isEmpty()) + str.append(QStringLiteral("debugInfo='%1'").arg(m_debugInfo)); + + str.append(QStringLiteral("uniqueMapId='%1'").arg(m_uniqueMapId)); + + if ((m_mainGraphicalObjectText) && + (! m_mainGraphicalObjectText->text().isEmpty()) + ) + str.append(QStringLiteral(" identifier='%1'").arg(m_mainGraphicalObjectText->text())); + + if (!m_logicalLayerName.isEmpty()) + str.append(QStringLiteral(" logicalLayerName='%1'").arg(m_logicalLayerName)); + + str.append(QStringLiteral(" partyId=%1").arg(m_party.partyId.value())); + + str.append(QStringLiteral(" aesState=%1").arg(SituationMap::toString(m_aesState.value()))); + + if (m_mainGraphicalObject) + str.append(QStringLiteral(" scale='%1'").arg(m_mainGraphicalObject->scale())); + + str.append(QStringLiteral(" visibility='%1'").arg(m_visibility)); + str.append(QStringLiteral(" visibilityTech='%1'").arg(m_visibilityTechnical)); + str.append(QStringLiteral(" visibilityTact='%1'").arg(m_visibilityTactical)); + str.append(QStringLiteral(" visibilityTechGeo='%1'").arg(m_visibilityTechnicalGeometry)); + str.append(QStringLiteral(" identiferVisible=%1").arg(m_identiferVisible.value())); + str.append(QStringLiteral(" selected='%1'").arg(m_selected)); + str.append(QStringLiteral(" pickable='%1'").arg(m_pickable)); + + str.append(QStringLiteral(" majorColor='%1'").arg(this->majorColor().name(QColor::HexArgb))); + + return str; +} + +void AbstractMapObjectImpl::boundingBox(SituationMap::BoundingBox &) const +{ + // leave it empty and invalid. Method has to be overloaded if required. +} + + +void AbstractMapObjectImpl::setForceMajorColor(const QColor &majorColor) +{ + m_forcedMajorColor = majorColor; +} +QColor AbstractMapObjectImpl::majorColor() const +{ + if (m_forcedMajorColor.isValid()) + return m_forcedMajorColor; + + if (m_mainGraphicalObject) + return m_mainGraphicalObject->lineStyle().lineColor(); + + return QColor(); +} + +double AbstractMapObjectImpl::extrusionHeight() const +{ + return m_extrusionHeight; +} +void AbstractMapObjectImpl::setExtrusionHeight(double extrusionHeight) +{ + if (m_mapImpl->mode() != Mode::Mode_3D) + return; + + m_extrusionHeight = extrusionHeight; + + if (extrusionHeightValid()) + createGraphicalObjectExtrusion(); + else + resetGraphicalObjectExtrusion(); +} +bool AbstractMapObjectImpl::extrusionHeightValid() const +{ + return !std::isnan(m_extrusionHeight); +} + +const QString AbstractMapObjectImpl::extrusionColorName() const +{ + return m_extrusionColorName; +} +void AbstractMapObjectImpl::setExtrusionColorName(const QString &extrusionColorName) +{ + m_extrusionColorName = extrusionColorName; +} + + +QString AbstractMapObjectImpl::identifier() const +{ + if (m_mainGraphicalObjectText) + return m_mainGraphicalObjectText->text(); + +// else +// ERROR_LOG(m_logger) << LOGFUNCTION +// << " debugInfo=" << m_debugInfo +// << " no object" +// ; + + return QString(); +} + +void AbstractMapObjectImpl::createGraphicalObjectText() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo=" << m_debugInfo; + + if (!m_mainGraphicalObjectText) + { + m_mainGraphicalObjectText = new GraphicalObjectText(m_mapImpl->getEsriGeoView() + , m_graphicsLayer + , m_debugInfo + QLatin1String("#Text") + , uniqueMapId()); + // Set default text style for all MapObjects: + m_mainGraphicalObjectText->setTextStyle(defaultTextStyle()); + m_mainGraphicalObjectText->setXOffset(10); // use as initial default + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo=" << m_debugInfo; +} + +void AbstractMapObjectImpl::createTacticalAppearance() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo=" << m_debugInfo; + if (!m_tacticalAppearance) + { + m_tacticalAppearance = new TacticalAppearance(m_mapImpl, + m_logicalLayerName, + m_debugInfo + QStringLiteral("#TactApp"), + m_uniqueMapId + ); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo=" << m_debugInfo; +} + +void AbstractMapObjectImpl::resetTacticalAppearanceDrawOrderOffset() +{ + m_tacticalAppearanceDrawOrderOffset = 0; +} + +void AbstractMapObjectImpl::createPin() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo=" << m_debugInfo; + if (!m_graphicalObjectPin) + { + m_graphicalObjectPin = new GraphicalObjectPath(SituationMap::GraphicalObjectPathType::Polyline, + m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QStringLiteral("#Pin"), + m_uniqueMapId + ); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo=" << m_debugInfo; +} + +void AbstractMapObjectImpl::resetPin() +{ + delete m_graphicalObjectPin; + m_graphicalObjectPin = nullptr; +} + +void AbstractMapObjectImpl::setIdentifier(const QString &identifier) +{ + if (identifier.isEmpty()) + { + delete m_mainGraphicalObjectText; + m_mainGraphicalObjectText = nullptr; + } + else + { + createGraphicalObjectText(); + if (m_mainGraphicalObjectText) + m_mainGraphicalObjectText->setText(identifier); + } +} + +bool AbstractMapObjectImpl::identifierVisible() const +{ + return m_identiferVisible.value(); +} + +void AbstractMapObjectImpl::setIdentifierVisible(bool identifierVisible) +{ + m_identiferVisible = identifierVisible; +} + +int AbstractMapObjectImpl::partyId() const +{ + return m_party.partyId.value(); +} + +bool AbstractMapObjectImpl::isPartyIdValid() const +{ + return m_party.partyId.value() >= 0; +} + +void AbstractMapObjectImpl::setPartyId(int partyId) +{ + m_party.partyId = partyId; +} + +bool AbstractMapObjectImpl::setPartyIdChanged(int partyId) +{ + if ((partyId < 0) || (m_party.partyId.value() == partyId)) + { + m_party.partyId.setChanged(); + return true; + } + return false; +} + + +const QColor &AbstractMapObjectImpl::partyColor() const +{ + if (m_party.forcedPartyColor.value().isValid()) + return m_party.forcedPartyColor.value(); + + if (m_party.partyId.changed() || m_party.useLightColor.changed() || !m_party.partyColor.isValid()) + { + m_party.partyColor = + m_mapImpl->partyIdToColorFunctor().partyIdToColor(m_party.partyId.value(), + m_party.useLightColor.value()); + } + return m_party.partyColor; +} + +QColor AbstractMapObjectImpl::partyColor(int partyId, bool useLightColor) const +{ + return m_mapImpl->partyIdToColorFunctor().partyIdToColor(partyId, useLightColor); +} + +void AbstractMapObjectImpl::forcePartyColor(const QColor &partyColorToForce) +{ + m_party.forcedPartyColor = partyColorToForce; +} + +bool AbstractMapObjectImpl::useLightColor() const +{ + return m_party.useLightColor.value(); +} +void AbstractMapObjectImpl::setUseLightColor(bool useLightColor) +{ + m_party.useLightColor = useLightColor; +} + +AdvancedSymbology::Role AbstractMapObjectImpl::partyRole(bool roleIdentityAssumed) const +{ + return m_mapImpl->partyIdToColorFunctor().partyIdToRole(m_party.partyId.value(), roleIdentityAssumed); +} + + +AesEffectState AbstractMapObjectImpl::aesState() const +{ + return m_aesState.value(); +} +void AbstractMapObjectImpl::setAesState(AesEffectState aesState) +{ + m_aesState = aesState; +} + + +TextStyle AbstractMapObjectImpl::textStyle() const +{ + if (m_mainGraphicalObjectText) + return m_mainGraphicalObjectText->textStyle(); + else + return TextStyle(); +} + +TextStyle &AbstractMapObjectImpl::rTextStyle() +{ + createGraphicalObjectText(); + return m_mainGraphicalObjectText->rTextStyle(); +} + +void AbstractMapObjectImpl::setTextStyle(const TextStyle &textStyle) +{ + createGraphicalObjectText(); + m_mainGraphicalObjectText->setTextStyle(textStyle); +} + + +LineStyle AbstractMapObjectImpl::lineStyle() const +{ + return (m_mainGraphicalObject) ? m_mainGraphicalObject->lineStyle() : LineStyle(); +} + +LineStyle &AbstractMapObjectImpl::rLineStyle() +{ + static LineStyle dummy; + return (m_mainGraphicalObject) ? m_mainGraphicalObject->rLineStyle() : dummy; +} + +void AbstractMapObjectImpl::setLineStyle(const LineStyle &lineStyle) +{ + if (m_mainGraphicalObject) m_mainGraphicalObject->setLineStyle(lineStyle); +} + +FillStyle AbstractMapObjectImpl::fillStyle() const +{ + return (m_mainGraphicalObject) ? m_mainGraphicalObject->fillStyle() : FillStyle(); +} + +FillStyle &AbstractMapObjectImpl::rFillStyle() +{ + static FillStyle dummy; + return (m_mainGraphicalObject) ? m_mainGraphicalObject->rFillStyle() : dummy; +} + +void AbstractMapObjectImpl::setFillStyle(const FillStyle &fillStyle) +{ + if (m_mainGraphicalObject) m_mainGraphicalObject->setFillStyle(fillStyle); +} + + +QString AbstractMapObjectImpl::layer() const +{ + return logicalLayerName(); +} + +void AbstractMapObjectImpl::setDrawOrder(int drawOrder) +{ + m_drawOrder = drawOrder; + m_internalDrawOrder = m_drawOrder * 10; +} +int AbstractMapObjectImpl::drawOrder() const +{ + return m_drawOrder; +} + + +bool AbstractMapObjectImpl::visibility() const +{ + return m_visibility; +} +void AbstractMapObjectImpl::setVisibility(bool visibility) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo=" << m_debugInfo + << " visi old=" << m_visibility; + m_visibility = visibility; + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo=" << m_debugInfo + << " visi new=" << m_visibility; +} + +bool AbstractMapObjectImpl::visibility(ObjectAppearance appearance) const +{ + bool visi = true; + switch (appearance) + { + case SituationMap::ObjectAppearance::Technical: + visi = m_visibilityTechnical; + break; + case SituationMap::ObjectAppearance::Tactical: + visi = m_visibilityTactical; + break; + } + return visi; +} + +void AbstractMapObjectImpl::setVisibility(ObjectAppearance appearance, bool visibility) +{ + switch (appearance) + { + case SituationMap::ObjectAppearance::Technical: + m_visibilityTechnical = visibility; + break; + case SituationMap::ObjectAppearance::Tactical: + m_visibilityTactical = visibility; + break; + default: + ERROR_LOG(m_logger) << LOGFUNCTION << " ObjectAppearance not found"; + break; + } + TRACE_LOG(m_logger) << LOGFUNCTION + << " debugInfo=" << m_debugInfo + << " m_visibilityTechnical=" << m_visibilityTechnical + << " m_visibilityTactical=" << m_visibilityTactical; +} + +bool AbstractMapObjectImpl::visibilityOfGeometry() const +{ + return m_visibilityTechnicalGeometry; +} + +void AbstractMapObjectImpl::setVisibilityOfGeometry(bool visiGeometry) +{ + m_visibilityTechnicalGeometry = visiGeometry; +} + +bool AbstractMapObjectImpl::selected() const +{ + return m_selected; +} +void AbstractMapObjectImpl::setSelected(bool selected) +{ + m_selected = selected; +} + +bool AbstractMapObjectImpl::pickable() const +{ + return m_pickable; +} + +void AbstractMapObjectImpl::setPickable(bool pickable) +{ + m_pickable = pickable; +} + + +float AbstractMapObjectImpl::scale() const +{ + return (m_mainGraphicalObject) ? m_mainGraphicalObject->scale() : 0.0f; +} + +void AbstractMapObjectImpl::setScale(float scale) +{ + if (!m_enableScale) + return; + + // typically the mainGraphicalObject does NOT need any scale values, + // but keep it similar to identifier, textStyle, fillStyle etc. + // mainGraphicalObjectText will receive the value within ::updateView() + if (m_mainGraphicalObject) m_mainGraphicalObject->setScale(scale); +} + +bool AbstractMapObjectImpl::isScaleEnabled() const +{ + return m_enableScale; +} + +void AbstractMapObjectImpl::setScaleEnable(bool enabled) +{ + m_enableScale = enabled; +} + + +SymbolData AbstractMapObjectImpl::symbol() const +{ + return m_symbol.value(); +} +void AbstractMapObjectImpl::setSymbol(const SymbolData &symbol) +{ + m_symbol = symbol; +} + +void AbstractMapObjectImpl::reRenderSymbol() +{ + m_symbol.setChanged(); +} + +SymbolDataToRender AbstractMapObjectImpl::symbolDataToRender() const +{ + return m_symbol.value().getDefaultRenderData(); +} + +SymbolImage AbstractMapObjectImpl::symbolImage() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return m_symbolImage; +} + +QImage AbstractMapObjectImpl::previewImage() const +{ + return previewImageTypical(); +} + +QImage AbstractMapObjectImpl::previewImageTypical() const +{ + return m_symbolImage.image(); +} + +QImage AbstractMapObjectImpl::previewImageSpecific() const +{ + return m_symbolImage.image(); +} + +TextStyle AbstractMapObjectImpl::defaultTextStyle() +{ + TextStyle ts; // Define default text style for all MapObjects + ts.setTextOutlineColor(Qt::white); // Define default text style for all MapObjects + ts.setTextOutlineWidth(0.5); // Define default text style for all MapObjects + return ts; +} + +bool AbstractMapObjectImpl::updateTacticalAppearance() +{ + bool successTactAppearance = true; + + if (m_tacticalAppearance) + { + m_tacticalAppearance->setUniqueDesignation(identifier()); + + m_tacticalAppearance->setDrawOrder(m_internalDrawOrder + m_tacticalAppearanceDrawOrderOffset); // m_tacticalAppearanceDrawOrderOffset == 2 + m_tacticalAppearance->setVisibility(m_visibility && m_visibilityTactical); + m_tacticalAppearance->setSelected(m_selected); + m_tacticalAppearance->setPickable(m_pickable); + + if (m_symbol.changed()) + m_tacticalAppearance->setSymbol(m_symbol.value()); + + if (m_mainGraphicalObject) + m_tacticalAppearance->setScale(m_mainGraphicalObject->scale()); + else + ERROR_LOG(m_logger) << LOGFUNCTION << " m_mainGraphicalObject missing!!!"; + + successTactAppearance = m_tacticalAppearance->updateView(); + } + return successTactAppearance; +} + +void AbstractMapObjectImpl::createGraphicalObjectExtrusion() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo=" << m_debugInfo; + + if (m_mapImpl->mode() != Mode::Mode_3D) + return; + + if (!m_graphicalObjectExtrusion) + { + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = m_mapImpl->getGraphicsLayer3DExtrusion(m_logicalLayerName); + + if (!graphicsLayer) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " getGraphicsLayer3DExtrusion failed"; + return; + } + + m_graphicalObjectExtrusion = new GraphicalObjectCube(m_mapImpl->getEsriGeoView(), + graphicsLayer, // ESRI-Layer, not logical + m_debugInfo + QStringLiteral("#GO3D"), + m_uniqueMapId + ); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo=" << m_debugInfo; +} + +void AbstractMapObjectImpl::resetGraphicalObjectExtrusion() +{ + delete m_graphicalObjectExtrusion; + m_graphicalObjectExtrusion = nullptr; +} + +bool AbstractMapObjectImpl::updateGraphicalObjectExtrusion() +{ + bool successGo3D = true; + + if (m_graphicalObjectExtrusion) + { + m_graphicalObjectExtrusion->setUniqueMapId(m_uniqueMapId); + + // geometry: + if (m_mainGraphicalObject) + m_graphicalObjectExtrusion->setPositions(m_mainGraphicalObject->positions()); // translated / maybe they are densified? + + m_graphicalObjectExtrusion->setDrawOrder(m_internalDrawOrder); + m_graphicalObjectExtrusion->setVisibility(m_visibility && m_visibilityTechnical); + m_graphicalObjectExtrusion->setSelected(m_selected); + m_graphicalObjectExtrusion->setPickable(m_pickable); + + if (m_extrusionColorName.isEmpty()) + { + // Fallback + const QColor &partyColor = m_mapImpl->partyIdToColorFunctor().partyIdToColor(m_party.partyId.value()); + m_graphicalObjectExtrusion->setExtrusionColorName(partyColor.name()); + } else { + m_graphicalObjectExtrusion->setExtrusionColorName(m_extrusionColorName); + } + m_graphicalObjectExtrusion->setHeight(m_extrusionHeight); + + // scale....?? + + successGo3D = m_graphicalObjectExtrusion->updateView(); + } + + return successGo3D; +} + +bool AbstractMapObjectImpl::updatePin() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, m_debugInfo=" << m_debugInfo; + + if (m_graphicalObjectPin) + { + m_graphicalObjectPin->setDrawOrder(m_internalDrawOrder + 5); // m_tacticalAppearanceDrawOrderOffset == 2 + m_graphicalObjectPin->setVisibility(m_visibility); + m_graphicalObjectPin->setSelected(m_selected); + m_graphicalObjectPin->setPickable(m_pickable); + + if (m_mainGraphicalObject) + { + m_graphicalObjectPin->setLineStyle(LineStyle(Qt::black, 3, LinePattern::DashDotDot)); + m_graphicalObjectPin->setScale(m_mainGraphicalObject->scale()); + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " m_mainGraphicalObject missing!!!"; + + // + // m_graphicalObjectPin->setPositions and updateView is called at GeoImageImpl::onMapExtentChanged() + // + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, m_debugInfo=" << m_debugInfo; + return true; +} + +void AbstractMapObjectImpl::updateIdentifierPosition() +{ + m_identifierPositionCandidateTranslated = m_mainGraphicalObject->origin(); +} + +bool AbstractMapObjectImpl::updateViewImplPre() +{ + // + // createTacticalAppearance onDemand + // + if (!m_symbol.value().sidc.isEmpty()) + createTacticalAppearance(); + + // + // partyId (and its resulting color) has priority to lineColor, fillColor: + // + if (isPartyIdValid() && partyChanged()) + { + const QColor &partyCol = partyColor(); + + if (m_mainGraphicalObject) + { + if (m_lineColorFromParty) + m_mainGraphicalObject->rLineStyle().setLineColor(partyCol); + QColor fillColor(partyCol); + fillColor.setAlpha(m_fillColorAlpha); + m_mainGraphicalObject->rFillStyle().setFillColor(fillColor); + } + + if (m_mainGraphicalObjectText) + m_mainGraphicalObjectText->rTextStyle().setTextColor(partyCol); + + // maybe continued for TacticalAppearance + + } + + // + // if state!=Unknown -> its resulting pattern has priority to fillPattern: + // + if ((m_aesState.value() != AesEffectState::Unknown) && m_aesState.changed()) + { + if (m_mainGraphicalObject) + { + FillPattern pattern = m_mapImpl->partyIdToColorFunctor().aesStateToPattern(m_aesState.value()); + m_mainGraphicalObject->rFillStyle().setFillPattern(pattern); + } + } + + + return true; +} + +inline bool isSurfacePlacementRelevant(Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer) +{ + if (!graphicsLayer) + return false; + + // maybe also SurfacePlacement::RelativeToScene ??? + return (graphicsLayer->sceneProperties().surfacePlacement() == Esri::ArcGISRuntime::SurfacePlacement::Relative); +} + + +bool AbstractMapObjectImpl::updateViewImpl() +{ + bool success = true; + bool successTactAppearance = true; + + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo =" << m_debugInfo + << " uniqueMapId =" << m_uniqueMapId + << " m_pickable=" << m_pickable; + + // drawOrder : + + // The LayerOrder could be added if required like... + // finalDrawOrder = m_mapImpl->layerFactory()-> indexOf(m_logicalLayerName); + + if (m_mainGraphicalObjectText) + { + m_mainGraphicalObjectText->setDrawOrder(m_internalDrawOrder + 2); // +2 to provide space for indicationCont (e.g. mines of MineField) + m_mainGraphicalObjectText->setScale(m_mainGraphicalObject->scale()); + } + + // : drawOrder + + if (m_mainGraphicalObject) + { + m_mainGraphicalObject->setDrawOrder(m_internalDrawOrder); + m_mainGraphicalObject->setVisibility(m_visibility && m_visibilityTechnical && m_visibilityTechnicalGeometry); + m_mainGraphicalObject->setSelected(m_selected); + m_mainGraphicalObject->setPickable(m_pickable); + + success = m_mainGraphicalObject->updateView(); + + if (m_mainGraphicalObjectText) + { + this->updateIdentifierPosition(); + + m_mainGraphicalObjectText->setScale(m_mainGraphicalObject->scale()); + // m_mainGraphicalObjectText->setSelected(m_selected); // Text should NEVER be selected + m_mainGraphicalObjectText->setPickable(m_pickable); + + // mainGraphicalObjectText->updateView() will be called within following 'onMapExtentChanged()': + // successGoText = m_mainGraphicalObjectText->updateView(); + } + } + + successTactAppearance = updateTacticalAppearance(); + + bool successGoExtrusion = true; + if (m_graphicalObjectExtrusion && (m_mapImpl->mode() == Mode::Mode_3D)) + { + successGoExtrusion = updateGraphicalObjectExtrusion(); + if (!successGoExtrusion) + ERROR_LOG(m_logger) << LOGFUNCTION << " failure updateGraphicalObjectExtrusion " << " debugInfo =" << m_debugInfo << " uniqueMapId =" << m_uniqueMapId; + } + + bool successUpdatePin = true; + if (m_graphicalObjectPin && (this->mapImpl()->mode() == Mode::Mode_3D) && isSurfacePlacementRelevant(m_graphicsLayer)) + { + successUpdatePin = updatePin(); + if (!successUpdatePin) + ERROR_LOG(m_logger) << LOGFUNCTION << " failure0 (updatePin) " << " debugInfo =" << m_debugInfo << " uniqueMapId =" << m_uniqueMapId; + } + + bool successMapExtendChanged = this->onMapExtentChanged(); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " failure1 " << " debugInfo =" << m_debugInfo << " uniqueMapId =" << m_uniqueMapId; + if (!successTactAppearance) + ERROR_LOG(m_logger) << LOGFUNCTION << " failure2 " << " debugInfo =" << m_debugInfo << " uniqueMapId =" << m_uniqueMapId; + if (!successGoExtrusion) + ERROR_LOG(m_logger) << LOGFUNCTION << " failure3 " << " debugInfo =" << m_debugInfo << " uniqueMapId =" << m_uniqueMapId; + if (!successMapExtendChanged) + ERROR_LOG(m_logger) << LOGFUNCTION << " failure4 " << " debugInfo =" << m_debugInfo << " uniqueMapId =" << m_uniqueMapId; + + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo =" << m_debugInfo + << " uniqueMapId =" << m_uniqueMapId; + return success && successTactAppearance && successGoExtrusion && successUpdatePin && successMapExtendChanged; +} + +bool AbstractMapObjectImpl::updateViewImplPost() +{ + partyReset(); + m_aesState.reset(); + m_symbol.reset(); + m_identiferVisible.reset(); + + if (m_updateObjectIndicator) + { + for (auto *ptr : std::as_const(m_originPositionChangedCb)) + ptr->onOriginPositionChanged(); + } + m_updateObjectIndicator = false; + + return true; +} + +int AbstractMapObjectImpl::numberOfValuesChanged() +{ + int count = 0; + if (m_party.changed()) { WARNING_LOG(m_logger) << LOGFUNCTION << " START "; ++count; } + if (m_aesState.changed()) { WARNING_LOG(m_logger) << LOGFUNCTION << " START "; ++count; } + + if (m_symbol.changed()) { WARNING_LOG(m_logger) << LOGFUNCTION << " START "; ++count; } + + if (m_identiferVisible.changed()) { WARNING_LOG(m_logger) << LOGFUNCTION << " START "; ++count; } + + return count; +} + +void AbstractMapObjectImpl::addChangedCallback(OriginPositionChangedCallback *cb) +{ + m_originPositionChangedCb.push_back(cb); +} +void AbstractMapObjectImpl::removeChangedCallback(OriginPositionChangedCallback *cb) +{ + m_originPositionChangedCb.removeAll(cb); +} + + +bool AbstractMapObjectImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo=" << m_debugInfo; + bool success = true; + + if (mapImpl) + m_mapImpl = mapImpl; + else + { + success = false; + ERROR_LOG(m_logger) << LOGFUNCTION + << " debugInfo=" << m_debugInfo + << " no mapImpl"; + } + + m_mapConnector = new SituationMap::MapConnector(); + success = success && m_mapConnector->connectMapObject(m_mapImpl, this); + if (success) + { + success = this->setLogicalLayerName(logicalLayerName); + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " debugInfo=" << m_debugInfo + << " setLogicalLayerName failed" + << ", logicalLayerName='" << logicalLayerName << "'"; + } + } + else + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " debugInfo=" << m_debugInfo + << " MapConnector->connectObject failed"; + } + + // take static scaleHasVisibilityValue; + m_visibilityScaleRange.setInitial(s_scaleHasVisibilityValue); // static mamber of interface class. + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo=" << m_debugInfo + << " success=" << success; + return success; +} + +void AbstractMapObjectImpl::setUniqueId(const UniqueMapId &uniqueId) +{ + m_uniqueMapId = uniqueId; + + if (m_mainGraphicalObject) + m_mainGraphicalObject->setUniqueMapId(m_uniqueMapId); + + if (m_mainGraphicalObjectText) + m_mainGraphicalObjectText->setUniqueMapId(m_uniqueMapId); + + if (m_tacticalAppearance) + m_tacticalAppearance->setUniqueMapId(m_uniqueMapId); +} + +bool AbstractMapObjectImpl::onMapExtentChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " debugInfo=" << m_debugInfo; + + bool success = true; + + // Some MapObjects like ParticipantImpl do NOT use m_mainGraphicalObjectText: + if (m_mainGraphicalObjectText && m_mapImpl) + { + // hide identifier on zoom out??? + + // old approach: + // double meterPerPixel = -1.0; + // if (m_mapImpl) + // meterPerPixel = m_mapImpl->pixelSize(); + // bool textVisi = meterPerPixel < 3000; + + // new approach: + // double curScale = m_mapImpl->scale(); + // bool textVisi = m_visibilityScaleRange.scaleHasVisibility(curScale); + // m_mainGraphicalObjectText->setVisibility(m_visibility && m_visibilityTechnical && textVisi); + + // TRACE_LOG(m_logger) << LOGFUNCTION << " START" + // << " debugInfo=" << m_debugInfo << "'" + // << " meterPerPixel=" << m_mapImpl->pixelSize() + // << " newScale=" << m_mapImpl->scale() + // << " newExtent = " << SituationMap::toString(m_mapImpl->extent()); + // m_mainGraphicalObjectText->setVisibility(m_visibility && m_visibilityTechnical && textVisi); + + // do NOT hide identifier on zoom out: + m_mainGraphicalObjectText->setVisibility(m_visibility && m_visibilityTechnical && m_identiferVisible.value()); + + m_mainGraphicalObjectText->setOrigin(m_identifierPositionCandidateTranslated); + + success = m_mainGraphicalObjectText->updateView(); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo=" << m_debugInfo << "'" + << " success=" << success; + return true; +} + + +QString AbstractMapObjectImpl::logicalLayerName() const +{ + return m_logicalLayerName; +} + +bool AbstractMapObjectImpl::setLogicalLayerName(const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo='" << m_debugInfo << "'" + << " logicalLayerName='" << logicalLayerName << "'"; + + m_logicalLayerName = logicalLayerName; + + bool success = false; + + m_graphicsLayer = m_mapImpl->getGraphicsLayer(logicalLayerName); + + if (m_graphicsLayer) + success = true; + else + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " debugInfo=" << m_debugInfo << "'" + << ", no layer found"; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo='" << m_debugInfo << "'" + << " logicalLayerName='" << logicalLayerName << "'" + << " success=" << success; + return success; +} + + +bool AbstractMapObjectImpl::geometryIntersect(const MathUtils::GeometryEngine::Geometry &intersector_polyline) const +{ + if (intersector_polyline.isEmpty()) + return false; + + if (m_mainGraphicalObject) + { + Esri::ArcGISRuntime::GraphicsOverlay *esriLayer = m_mainGraphicalObject->graphicsLayer(); + if ((esriLayer) && (esriLayer->isVisible())) + { + // former: if (m_mainGraphicalObject->visibility()) + // MineField stores the ountline/boundary in m_mainGraphicalObject to transfer this to m_tacticalAppearance + // That m_mainGraphicalObject is NOT visible, because of that fillPattern. + // to 'find' that MineField, anaylze just m_visibility && m_visibilityTechnical, and not m_visibilityTechnicalGeometry + if (m_visibility && m_visibilityTechnical) + { + if (MathUtils::GeometryEngine::intersect(m_mainGraphicalObject->getGraphicGeometry(), intersector_polyline)) + return true; + } + } + } + // m_mainGraphicalObjectText + if (m_tacticalAppearance) + { + Esri::ArcGISRuntime::GraphicsOverlay *esriLayer = m_tacticalAppearance->graphicsLayer(); + if ((esriLayer) && (esriLayer->isVisible())) + { + if (m_tacticalAppearance->visibility()) + { + if (MathUtils::GeometryEngine::intersect(m_tacticalAppearance->getGraphicGeometry(), intersector_polyline)) + return true; + } + } + } + return false; +} // bool AbstractMapObjectImpl::geometryIntersect( const MathUtils::GeometryEngine::Geometry & geom ) + + +bool AbstractMapObjectImpl::geometryIsInHull(const MathUtils::GeometryEngine::Geometry &hull_polygon) const +{ + if (hull_polygon.isEmpty()) + return false; + + if (m_mainGraphicalObject) + { + Esri::ArcGISRuntime::GraphicsOverlay *esriLayer = m_mainGraphicalObject->graphicsLayer(); + if ((esriLayer) && (esriLayer->isVisible())) + { + // former: if (m_mainGraphicalObject->visibility()) + // MineField stores the ountline/boundary in m_mainGraphicalObject to transfer this to m_tacticalAppearance + // That m_mainGraphicalObject is NOT visible, because of that fillPattern. + // to 'find' that MineField, anaylze just m_visibility && m_visibilityTechnical, and not m_visibilityTechnicalGeometry + if (m_visibility && m_visibilityTechnical) + { + if (MathUtils::GeometryEngine::contains(m_mainGraphicalObject->getGraphicGeometry(), hull_polygon)) + return true; + } + } + } + // m_mainGraphicalObjectText + if (m_tacticalAppearance) + { + Esri::ArcGISRuntime::GraphicsOverlay *esriLayer = m_tacticalAppearance->graphicsLayer(); + if ((esriLayer) && (esriLayer->isVisible())) + { + if (m_tacticalAppearance->visibility()) + { + if (MathUtils::GeometryEngine::contains(m_tacticalAppearance->getGraphicGeometry(), hull_polygon)) + return true; + } + } + } + return false; +} + +Esri::ArcGISRuntime::Geometry AbstractMapObjectImpl::getEsriGeometry() const +{ + if (!m_mainGraphicalObject) + return Esri::ArcGISRuntime::Geometry(); + return m_mainGraphicalObject->getGraphicGeometry(); +} + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/abstract_map_object_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/abstract_map_object_impl.h new file mode 100755 index 000000000..2310e940c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/abstract_map_object_impl.h @@ -0,0 +1,443 @@ +/** + * @RDE_HEADER@ + * Implementation of AbstractMapObject + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include // Interface +#include // for AesEffectState +#include +#include + +#include +#include +#include + + +// Qt +#include +#include + + +#include + + +// 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::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 partyId = -1; + SimpleValue useLightColor = false; // useful for Participants + + mutable QColor partyColor; + + SimpleValue 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 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 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 m_symbol; + SymbolImage m_symbolImage; + + QColor m_forcedMajorColor; + + // Update ObjectIndicator : +protected: + bool m_updateObjectIndicator{false}; + QVector 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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/coord_translation_functor_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/coord_translation_functor_impl.h new file mode 100755 index 000000000..ff0c644e0 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/coord_translation_functor_impl.h @@ -0,0 +1,207 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of Map + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +// Own +#include +#include +#include "map_impl.h" + + +// ESRI +#include +#include +//#include + + +// Qt +#include +#include +#include + +#include + + +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 &points) const override + { + int wkid(buildWkid(referenceLon)); + const Esri::ArcGISRuntime::SpatialReference utmSR(wkid); + + QMutexLocker lock(&m_mutex); + + for(QList::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 &points) const override + + + void utmToGeographic(const double referenceLon, QList &points) const override + { + int wkid(buildWkid(referenceLon)); + const Esri::ArcGISRuntime::SpatialReference utmSR(wkid); + + QMutexLocker lock(&m_mutex); + + for (QList::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 &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 QRecursiveMutex m_mutex; + + mutable int m_projectionCounter = 0; // hmmm - really needed? + + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/coordinate_translation.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/coordinate_translation.cpp new file mode 100755 index 000000000..084d99fb0 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/coordinate_translation.cpp @@ -0,0 +1,358 @@ +/** + * @RDE_HEADER@ + * CoordinateTranslation + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "coordinate_translation.h" +#include +#include +#include +#include + + +// ESRI +#include +#include + + +// Qt +#include + + +#include + +//---------------------------------------------------------------------------- +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() +#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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/coordinate_translation.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/coordinate_translation.h new file mode 100755 index 000000000..bd6a9da98 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/coordinate_translation.h @@ -0,0 +1,191 @@ +/** + * @RDE_HEADER@ + * CoordinateTranslation + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include + +// #define WITH_LOGGING +#ifdef WITH_LOGGING +# include +#endif // WITH_LOGGING + +// ESRI + + +// Qt +#include +#include + + +//---------------------------------------------------------------------------- +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 QRecursiveMutex m_mutex; +}; + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/coordinate_translation_thread.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/coordinate_translation_thread.cpp new file mode 100755 index 000000000..ce5fd6329 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/coordinate_translation_thread.cpp @@ -0,0 +1,82 @@ +/** + * @RDE_HEADER@ + * CoordinateTranslation + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "coordinate_translation_thread.h" +#include +#include +#include + + +// ESRI +#include +#include + + +// 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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/coordinate_translation_thread.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/coordinate_translation_thread.h new file mode 100755 index 000000000..03214d06c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/coordinate_translation_thread.h @@ -0,0 +1,79 @@ +/** + * @RDE_HEADER@ + * CoordinateTranslation + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "coordinate_translation.h" +#include +#include + + +// Qt +#include +#include +#include + + +// 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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/geometry_model_renderer_esri.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/geometry_model_renderer_esri.cpp new file mode 100755 index 000000000..1ca93f81a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/geometry_model_renderer_esri.cpp @@ -0,0 +1,1197 @@ +/** + * @RDE_HEADER@ + * PlanModel - Renderer + * $Id$ + * $HeadURL$ + * $Author$ + */ + + +#include "geometry_model_renderer_esri.h" + +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace SituationMap +{ + + GeometryModelRendererEsri::GeometryModelRendererEsri(Map *map, const QString &layerName, GeometryModel &geometryModel, QObject *parent) + : GeometryModelRenderer(map, layerName, geometryModel, parent) + , m_editorMode(false) + , m_delayedRenderTimer(new QTimer(this)) + , m_delayedRenderIds() + { + m_delayedRenderTimer->setInterval(10); + m_delayedRenderTimer->setSingleShot(true); + + connect(m_delayedRenderTimer, &QTimer::timeout, this, &GeometryModelRendererEsri::onDelayedRenderNodes); + + connect(&geometryModel, &GeometryModel::nodeCleanup , this, &GeometryModelRendererEsri::onCleanupMapObjects); + connect(&geometryModel, &GeometryModel::userDataCleanup, this, &GeometryModelRendererEsri::onRemoveMapObject); + connect(&geometryModel, &GeometryModel::changed , this, &GeometryModelRendererEsri::onUpdate); + + } // GeometryModelRendererEsri::GeometryModelRendererEsri + + + bool GeometryModelRendererEsri::editorMode() const { return m_editorMode; } + void GeometryModelRendererEsri::setEditorMode(bool on) { m_editorMode = on; } + + + class CleanUpMapObjectsVisitor : public GeometryNodeVisitor + { + SituationMap::Map *m_map; + + public: + + CleanUpMapObjectsVisitor(SituationMap::Map *map, SituationMap::GeometryModel *geoModel) : GeometryNodeVisitor(geoModel), m_map(map) { ; } + virtual ~CleanUpMapObjectsVisitor() = default; + + bool apply(SituationMap::GeometryModel *geoModel, const SituationMap::GeometryModel::Node &node, SituationMap::GeometryModel::PrimitiveType primitiveType) override + { + switch (primitiveType) + { + case GeometryModel::PrimitiveType::RootNode: + case GeometryModel::PrimitiveType::Node: + break; + + default: + { + if (geoModel->hasUserData(node)) + { + bool ok = false; + int uid = geoModel->getUserData(node, &ok); + + if (ok) + { + SituationMap::AbstractMapObject *mapObj = m_map->objectFactory().idToMapObject(uid); + if (mapObj) + mapObj->removeMySelf(); + } + } + } break; + } + return true; + } // bool apply(SituationMap::GeometryModel * geoModel, const SituationMap::GeometryModel::Node & node, SituationMap::GeometryModel::PrimitiveType primitiveType ) + + }; // CleanUpMapObjectsVisitor + + + void GeometryModelRendererEsri::onCleanupMapObjects(const SituationMap::GeometryModel::Node &node) + { + if (m_map) + { + GeometryModel::Node p = node; + if (!p.isValid()) + { + p = m_geometryModel.getRootNode(); + } + CleanUpMapObjectsVisitor visitor(m_map, &m_geometryModel); + + visitor.traverse(p); + } + } // GeometryModelRendererEsri::onCleanupMapObjects + + + void GeometryModelRendererEsri::onRemoveMapObject(int uid) + { + if (m_map) + { + SituationMap::AbstractMapObject *mapObj = m_map->objectFactory().idToMapObject(uid); + + // crash see comment in abstract_map_object_impl.cpp 2016/01/08 JT + // SituationMap::RefPointer mapObj( m_map->objectFactory().idToMapObject( uid ) ); + + if (mapObj != nullptr) + { + mapObj->removeMySelf(); + } + } + } // GeometryModelRendererEsri::onRemoveMapObject + + + void GeometryModelRendererEsri::onUpdate(const SituationMap::GeometryModel::Node &node, SituationMap::GeometryModel::Attribute /*att*/, bool /*coordsChanged*/) + { + if (m_geometryModel.hasUserData(node)) + { + SituationMap::AbstractMapObject *mapObj = nullptr; + bool ok = false; + int uid = m_geometryModel.getUserData(node, &ok); + + if (ok) + mapObj = m_map->objectFactory().idToMapObject(uid); + + if (mapObj) + { + // render node later + if (!m_delayedRenderIds.contains(uid)) + m_delayedRenderIds += uid; + m_delayedRenderTimer->start(); + + return; + } + } + renderNode(node); + } // GeometryModelRendererEsri::onUpdate(const SituationMap::GeometryModel::Node & node, SituationMap::GeometryModel::Attribute att, bool coordsChanged) + + + void GeometryModelRendererEsri::onDelayedRenderNodes() + { + for (auto uid : std::as_const(m_delayedRenderIds)) + { + SituationMap::AbstractMapObject *mapObj = m_map->objectFactory().idToMapObject(uid); + renderNode(getAssociatedNode(mapObj)); + } + m_delayedRenderIds.clear(); + } // GeometryModelRendererEsri::onDelayedRenderNodes() + + + bool GeometryModelRendererEsri::render(QPixmap &drawable) + { + Q_UNUSED(drawable); + return false; + } // GeometryModelRendererEsri::render( QPixmap & drawable ) + + + bool GeometryModelRendererEsri::render() { return renderNode(m_geometryModel.getRootNode()); } + + + bool GeometryModelRendererEsri::renderNode(const GeometryModel::Node &node) + { + bool ok = false; + if (!node.isValid()) + { + const GeometryModel::Node &root = m_geometryModel.getRootNode(); + ok = drawNodesRecursive(root); + } + else + ok = drawNodesRecursive(node); + + return ok; + } // GeometryModelRendererEsri::renderNode( Map * map, const GeometryModel::Node & node, const QString & layername ) + + + template uint GeometryModelRendererEsri::setLineStyle(const GeometryModel::Node &p, MapObjectClass *objPtr) const + + { + SituationMap::LineStyle &&objLineStyle = objPtr->lineStyle(); + + const QColor &lineColor = m_geometryModel.getAttribute(p, GeometryModel::Attribute::Color).value(); + uint lineWidth = m_geometryModel.getAttribute(p, GeometryModel::Attribute::LineWidth).toUInt(); + GeometryModel::LineStyle lineStyle = GeometryModel::LineStyle((m_geometryModel.getAttribute(p, GeometryModel::Attribute::LineStyle).toInt())); + + objLineStyle.setLineColor(lineColor); + objLineStyle.setLineWidth((lineWidth > 0) ? lineWidth : 1); + + switch (lineStyle) + { + case GeometryModel::LineStyle::Dashed : objLineStyle.setLinePattern(LinePattern::Dash); break; + case GeometryModel::LineStyle::DashedDotted : objLineStyle.setLinePattern(LinePattern::DashDot); break; + case GeometryModel::LineStyle::Dotted : objLineStyle.setLinePattern(LinePattern::Dot); break; + case GeometryModel::LineStyle::LineSolid : objLineStyle.setLinePattern(LinePattern::Solid); break; + default : objLineStyle.setLinePattern(LinePattern::Solid); break; + } + + objPtr->setLineStyle(objLineStyle); + + return lineWidth; + } // template uint GeometryModelRendererEsri::setLineStyle( const GeometryModel::Node & p, MapObjectClass * objPtr ) const + + + template void GeometryModelRendererEsri::setFillStyle(const GeometryModel::Node &p, MapObjectClass *objPtr) const + { + SituationMap::FillStyle &&objFillStyle = objPtr->fillStyle(); + GeometryModel::FillPattern fillPattern = GeometryModel::FillPattern((m_geometryModel.getAttribute(p, GeometryModel::Attribute::FillPattern).toInt())); + const QColor &fillColor = m_geometryModel.getAttribute(p, GeometryModel::Attribute::FillColor).value(); + + objFillStyle.setFillColor(fillColor); + switch (fillPattern) + { + case GeometryModel::FillPattern::Diag1 : objFillStyle.setFillPattern(FillPattern::BackwardDiagonal); break; + case GeometryModel::FillPattern::Diag2 : objFillStyle.setFillPattern(FillPattern::ForwardDiagonal); break; + case GeometryModel::FillPattern::Hor1 : objFillStyle.setFillPattern(FillPattern::BackwardDiagonal); break; + case GeometryModel::FillPattern::Hor2 : objFillStyle.setFillPattern(FillPattern::Horizontal); break; + case GeometryModel::FillPattern::Solid : objFillStyle.setFillPattern(FillPattern::Solid); break; + case GeometryModel::FillPattern::Ver1 : + case GeometryModel::FillPattern::Ver2 : objFillStyle.setFillPattern(FillPattern::Vertical); break; + case GeometryModel::FillPattern::Dots : + default : objFillStyle.setFillPattern(FillPattern::Null); break; + } + + objPtr->setFillStyle(objFillStyle); + } // template void GeometryModelRendererEsri::setFillStyle( const GeometryModel::Node & p, MapObjectClass * objPtr ) const + + + class RenderMapObjectsVisitor : public GeometryNodeVisitor + { + GeometryModelRendererEsri *m_gmr; + + public: + + RenderMapObjectsVisitor(GeometryModelRendererEsri *gmr, SituationMap::GeometryModel *geoModel) : GeometryNodeVisitor(geoModel), m_gmr(gmr) { ; } + virtual ~RenderMapObjectsVisitor() = default; + + bool apply(SituationMap::GeometryModel *geoModel, const SituationMap::GeometryModel::Node &node, SituationMap::GeometryModel::PrimitiveType primitiveType) override + { + SituationMap::AbstractMapObject *mapObj = nullptr; + + if (geoModel->hasUserData(node)) + { + bool ok = false; + int uid = geoModel->getUserData(node, &ok); + + if (ok) mapObj = m_gmr->m_map->objectFactory().idToMapObject(uid); + } + + switch (primitiveType) + { + case GeometryModel::PrimitiveType::ParticipantSymbol : m_gmr->drawParticipantSymbol( node, m_gmr->m_layerName, mapObj); break; + case GeometryModel::PrimitiveType::DictionarySymbol : m_gmr->drawDictionarySymbol ( node, m_gmr->m_layerName, mapObj); break; + case GeometryModel::PrimitiveType::Ellipse : m_gmr->drawEllipse ( node, m_gmr->m_layerName, mapObj); break; + case GeometryModel::PrimitiveType::Line : m_gmr->drawLine ( node, m_gmr->m_layerName, mapObj); break; + case GeometryModel::PrimitiveType::Polyline : m_gmr->drawPolyline ( node, m_gmr->m_layerName, mapObj); break; + case GeometryModel::PrimitiveType::Rectangle : m_gmr->drawRectangle ( node, m_gmr->m_layerName, mapObj); break; + case GeometryModel::PrimitiveType::Spline : m_gmr->drawSpline ( node, m_gmr->m_layerName, mapObj); break; + case GeometryModel::PrimitiveType::Symbol : m_gmr->drawSymbol ( node, m_gmr->m_layerName, mapObj); break; + case GeometryModel::PrimitiveType::Text : m_gmr->drawText ( node, m_gmr->m_layerName, mapObj); break; + + case GeometryModel::PrimitiveType::RootNode : break; + case GeometryModel::PrimitiveType::Node : break; + case GeometryModel::PrimitiveType::Unknown : break; + default : break; + } + return true; + } // bool apply(SituationMap::GeometryModel * geoModel, const SituationMap::GeometryModel::Node & node, SituationMap::GeometryModel::PrimitiveType primitiveType ) + + }; // RenderMapObjectsVisitor + + + bool GeometryModelRendererEsri::drawNodesRecursive(const GeometryModel::Node &p) + { + if (!p.isValid()) return false; + + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + ++m_stats.m_drawElementsRecursiveCount; + + RenderMapObjectsVisitor visitor(this, &m_geometryModel); + visitor.traverse(p); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + + return true; + } // void GeometryModelRendererEsri::drawNodesRecursive + + + UniqueMapId GeometryModelRendererEsri::getIdFromNode(const GeometryModel::Node &node) const + { + SituationMap::UniqueMapId mapId = SituationMap::INVALID_MAP_ID; + + if (m_geometryModel.hasUserData(node)) + { + bool ok; + int userData = m_geometryModel.getUserData(node, &ok); + if (!ok) + DEBUG_LOG(m_logger) << LOGFUNCTION << " getUserData failed"; + else mapId = userData; + } + return mapId; + + } // UniqueMapId GeometryModelRendererEsri::getIdFromNode() const + + + GeometryModel::Node GeometryModelRendererEsri::getAssociatedNode(const AbstractMapObject *mapObject) + { + if (mapObject == nullptr) return GeometryModel::Node(); + SituationMap::UniqueMapId mapId = mapObject->uniqueMapId(); + + if (mapId == SituationMap::INVALID_MAP_ID) return GeometryModel::Node(); + + return m_geometryModel.findPrimitive(mapId); + } // getAssociatedNode( const AbstractMapObject * mapObject ) + + + bool GeometryModelRendererEsri::getNodesPickable() const { return m_nodesPickable; } + + + void GeometryModelRendererEsri::setNodesPickable(bool nodesPickable) { m_nodesPickable = nodesPickable; } + + + void GeometryModelRendererEsri::drawParticipantSymbol(const GeometryModel::Node &p, const QString &layer, SituationMap::AbstractMapObject *mapObj) + { + QList points = m_geometryModel.getPrimitivePoints(p); + SituationMap::UniqueMapId mapId = getIdFromNode(p); + + if (points.size() == 0) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << "Primitive Dictionary Symbol don't have any coordinates!" + << " (" << mapId << ")"; + return; + } + + + bool ok; + + const QString & sidc = m_geometryModel.getAttribute(p, GeometryModel::Attribute::SIDC).toString(); + const QString & sidcAttributes = m_geometryModel.getAttribute(p, GeometryModel::Attribute::SIDC_Attributes).toString(); + + int symbolForceId = m_geometryModel.getAttribute(p, GeometryModel::Attribute::ForceId).toInt(&ok); if (!ok) symbolForceId = -1; + int symbolInjuryId = m_geometryModel.getAttribute(p, GeometryModel::Attribute::DamageForceId).toInt(&ok); if (!ok) symbolInjuryId = -1; + int symbolInjury = m_geometryModel.getAttribute(p, GeometryModel::Attribute::Injury).toInt(&ok); if (!ok) symbolInjury = static_cast(AdvancedSymbology::Injury::NoInjury); + + const QVector &partyIds = m_map->partyIdToColorFunctor().partyIds(); + + if (!partyIds.contains(symbolForceId)) symbolForceId = -1; + if (!partyIds.contains(symbolInjuryId)) symbolInjuryId = -1; + + SituationMap::ObjectFactory & objFac = m_map->objectFactory(); + SituationMap::Participant * objPtr = dynamic_cast(mapObj); + + + if (objPtr == nullptr) + { + if (mapObj) + mapObj -> removeMySelf(); + objPtr = objFac.create (layer, QStringLiteral("Plan-ParticipantSymbol:sidc=%1").arg(sidc)).get(); + + if (objPtr == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " objFac.create failed"; + return; + } + } + + ++m_stats.m_drawDictionarySymbolCount; + m_stats.m_timer.restart(); + + SituationMap::SymbolData symbolData; + + symbolData.sidc = sidc; + + objPtr->setVisibility(true); + objPtr->setPickable(m_nodesPickable); + objPtr->setOrigin(SituationMap::Point3D::fromQPointF(points.first())); + objPtr->setSymbol(symbolData); + objPtr->setPartyId(symbolForceId); + objPtr->setInjury(static_cast(symbolInjury), symbolInjuryId ); + + + objPtr->setTextModifier( SituationMap::Participant::TextModifier::W_DateTimeGroup , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_W).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::T_UniqueDesignation , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_T).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::M_HigherFormation , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_M).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::EF_ReinforcedReduced , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_EF).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::XY_AltitudeLocation , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_XY).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::V_Type , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_V).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::Z_Speed , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_Z).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::G_StaffComments , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_G).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::H_AdditionalInformation , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_H).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::JKLNP , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_KLNPJ).toString()); + + QMap attrMap; + + m_geometryModel.parseSymbolAttributeString(sidcAttributes, attrMap); + + const auto tm = Participant::textModifiers(); + for (auto textModifier : tm) + objPtr->setTextModifierVisibility(textModifier, true); + + + bool success = objPtr->updateView(); + + if (!success) ERROR_LOG(m_logger) << LOGFUNCTION << " failed"; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueMapId=" << objPtr->uniqueMapId(); + + // Store id in primitive + m_geometryModel.setUserData(p, objPtr->uniqueMapId()); + + int ms = m_stats.m_timer.elapsed(); + m_stats.m_msDictionarySymbol += ms; + m_stats.m_msDictionarySymbolMin = std::min(ms, m_stats.m_msDictionarySymbolMin); + m_stats.m_msDictionarySymbolMax = std::max(ms, m_stats.m_msDictionarySymbolMax); + + } // void GeometryModelRendererEsri::drawParticipantSymbol(const GeometryModel::Node &p, const QString &layer, SituationMap::AbstractMapObject *mapObj) + + + void GeometryModelRendererEsri::drawDictionarySymbol(const GeometryModel::Node &p, const QString &layer, SituationMap::AbstractMapObject *mapObj) + { + QList points = m_geometryModel.getPrimitivePoints(p); + SituationMap::UniqueMapId mapId = getIdFromNode(p); + + if (points.size() == 0) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << "Primitive Dictionary Symbol don't have any coordinates!" + << " (" << mapId << ")"; + return; + } + + const QString & sidc = m_geometryModel.getAttribute(p, GeometryModel::Attribute::SIDC).toString(); + const QString & sidcAttributes = m_geometryModel.getAttribute(p, GeometryModel::Attribute::SIDC_Attributes).toString(); + SituationMap::ObjectFactory & objFac = m_map->objectFactory(); + SituationMap::SidcSymbol * objPtr = dynamic_cast(mapObj); + + + if (objPtr == nullptr) + { + if (mapObj) mapObj -> removeMySelf(); + + objPtr = objFac.create (layer, QStringLiteral("Plan-TacticalSymbol:sidc=%1").arg(sidc)).get(); + + if (objPtr == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " objFac.create failed"; + return; + } + } + + ++m_stats.m_drawDictionarySymbolCount; + m_stats.m_timer.restart(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << "TacticalAppearanceAttrList: " << sidcAttributes; + + QMap attrMap; + + m_geometryModel.parseSymbolAttributeString(sidcAttributes, attrMap); + + objPtr->setVisibility(true); + objPtr->setPickable(m_nodesPickable); + objPtr->defineSymbol(sidc, SituationMap::convert2D3D(points), attrMap ); + + bool success = objPtr->updateView(); + + if (!success) ERROR_LOG(m_logger) << LOGFUNCTION << " failed"; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueMapId=" << objPtr->uniqueMapId(); + + // Store id in primitive + m_geometryModel.setUserData(p, objPtr->uniqueMapId()); + + int ms = m_stats.m_timer.elapsed(); + m_stats.m_msDictionarySymbol += ms; + m_stats.m_msDictionarySymbolMin = std::min(ms, m_stats.m_msDictionarySymbolMin); + m_stats.m_msDictionarySymbolMax = std::max(ms, m_stats.m_msDictionarySymbolMax); + + } // GeometryModelRendererEsri::drawDictionarySymbol + + + void GeometryModelRendererEsri::drawEllipse(const GeometryModel::Node &p, const QString &layer, SituationMap::AbstractMapObject *mapObj) + { + SituationMap::CoordTranslationFunctor *ctf = m_map->coordTranslationFunctor(); + + if (nullptr == ctf) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << "Map don't have defined any map projection! Can't render ellipse ..."; + return; + } + + QList points = m_geometryModel.getPrimitivePoints(p); + + if (points.size() != 4) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << "Primitive Ellipse has wrong count of geo coordinates!"; + return; + } + + ++m_stats.m_drawEllipseCount; + m_stats.m_timer.restart(); + + double radiusX = m_geometryModel.getAttribute(p, GeometryModel::Attribute::Width).toDouble() * 0.5; + double radiusY = m_geometryModel.getAttribute(p, GeometryModel::Attribute::Height).toDouble() * 0.5; + double phi = m_geometryModel.getAttribute(p, GeometryModel::Attribute::Rotate).toDouble() * (M_PI / 180.0); + double start = m_geometryModel.getAttribute(p, GeometryModel::Attribute::Start).toDouble(); + double extent = m_geometryModel.getAttribute(p, GeometryModel::Attribute::Extent).toDouble(); + + bool fullCircle = (fabs(extent) > 359.5); + + if (fullCircle) + { + start = 0.0; + extent = 360.0; + } + + start *= (M_PI / 180.0); + extent *= (M_PI / 180.0); + + QPointF centerPoint(points.first()); + for (int i = 1; i < 4; ++i) centerPoint += points.at(i); + centerPoint /= 4; + centerPoint = ctf->geographicToMap(centerPoint); + + points.clear(); + + qreal referenceLon = 9999; + if (ctf->mapIsGeographic()) + { + referenceLon = centerPoint.x(); + points << centerPoint; + ctf->geographicToUtm( referenceLon, points); + centerPoint = points.first(); + points.clear(); + } + + SituationMap::MathUtils::Ellipse ellipseCurve(centerPoint, radiusX, radiusY, phi, start, start + extent); + std::unique_ptr pathRasterer(ellipseCurve.createPathRaster()); + + pathRasterer->epsilon(0.25 * m_map->pixelSizeMeter()); + pathRasterer->maxVertexCount(1000); + + pathRasterer->rasterize(ellipseCurve.minimumCurveParameter(), ellipseCurve.maximumCurveParameter(), points); + + if (!fullCircle) + { + GeometryModel::FillStyle fillStyle = (GeometryModel::FillStyle)(m_geometryModel.getAttribute(p, GeometryModel::Attribute::FillStyle).toInt()); + + switch (fillStyle) + { + case GeometryModel::FillStyle::Pieslice: points.append(centerPoint); break; + case GeometryModel::FillStyle::Chord: break; + case GeometryModel::FillStyle::Open: break; + } + } + + if (ctf->mapIsGeographic()) + { + ctf->utmToGeographic(referenceLon, points); + } + + drawPolyline(p, layer, points, mapObj, false); + + int ms = m_stats.m_timer.elapsed(); + m_stats.m_msEllipse += ms; + m_stats.m_msEllipseMin = std::min(ms, m_stats.m_msEllipseMin); + m_stats.m_msEllipseMax = std::max(ms, m_stats.m_msEllipseMax); + + } // void GeometryModelRendererEsri::drawEllipse + + + void GeometryModelRendererEsri::drawLine(const GeometryModel::Node &p, const QString &layer, SituationMap::AbstractMapObject *mapObj) + { + ++m_stats.m_drawLineCount; + m_stats.m_timer.restart(); + drawPolyline(p, layer, mapObj); + int ms = m_stats.m_timer.elapsed(); + m_stats.m_msLine += ms; + m_stats.m_msLineMin = std::min(ms, m_stats.m_msLineMin); + m_stats.m_msLineMax = std::max(ms, m_stats.m_msLineMax); + } // void GeometryModelRendererEsri::drawLine + + + void GeometryModelRendererEsri::drawPolyline(const GeometryModel::Node &p, const QString &layer, SituationMap::AbstractMapObject *mapObj) + { + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + ++m_stats.m_drawPolylineCount; + m_stats.m_timer.restart(); + + drawPolyline(p, layer, m_geometryModel.getPrimitivePoints(p), mapObj); + + int ms = m_stats.m_timer.elapsed(); + m_stats.m_msPolyline += ms; + m_stats.m_msPolylineMin = std::min(ms, m_stats.m_msPolylineMin); + m_stats.m_msPolylineMax = std::max(ms, m_stats.m_msPolylineMax); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + + } /* GeometryModelRendererEsri::drawPolyline */ + + + void GeometryModelRendererEsri::drawPolyline( const GeometryModel::Node & p + , const QString & layer + , const QList & points + , SituationMap::AbstractMapObject * mapObj + , bool geoCoordinates ) + { + bool isRectangle = (m_geometryModel.getType(p) == GeometryModel::PrimitiveType::Rectangle); + bool isEllipse = (m_geometryModel.getType(p) == GeometryModel::PrimitiveType::Ellipse); + bool isFilled = (points.size() > 2) && m_geometryModel.getAttribute(p, GeometryModel::Attribute::Fill).toBool(); + bool isClosed = ((isEllipse) || (isRectangle) || ((points.size() > 2) && m_geometryModel.getAttribute(p, GeometryModel::Attribute::Close).toBool())); + + + if ((points.size() > 2) || (isRectangle) || (isEllipse)) + { + GeometryModel::FillPattern fillPattern = GeometryModel::FillPattern((m_geometryModel.getAttribute(p, GeometryModel::Attribute::FillPattern).toInt())); + + if (isEllipse) + { + double extent = m_geometryModel.getAttribute(p, GeometryModel::Attribute::Extent).toDouble(); + + if (fabs(extent) <= 359.5) + { + GeometryModel::FillStyle fillStyle = GeometryModel::FillStyle((m_geometryModel.getAttribute(p, GeometryModel::Attribute::FillStyle).toInt())); + switch (fillStyle) + { + case GeometryModel::FillStyle::Chord : isClosed = true; break; + case GeometryModel::FillStyle::Pieslice : isClosed = true; break; + case GeometryModel::FillStyle::Open : isClosed = false; break; + } + } + else isClosed = true; + } + + if (fillPattern == GeometryModel::FillPattern::None) isFilled = false; + } + + SituationMap::RefPointer objPtr(mapObj); + SituationMap::ObjectFactory &objFac = m_map->objectFactory(); + bool createMapObj = false; + bool selectMapObj = false; + SituationMap::UniqueMapId removedUidMapObj = SituationMap::INVALID_MAP_ID; + + if (mapObj) + { + objPtr = (SituationMap::AbstractMultiPointMapObject *) dynamic_cast(mapObj); + if (objPtr.valid()) + { + if (isClosed) + { + createMapObj = true; + selectMapObj = objPtr->selected(); + removedUidMapObj = objPtr->uniqueMapId(); + + objPtr->removeMySelf(); + objPtr = nullptr; + } + } + else + { + objPtr = (SituationMap::AbstractMultiPointMapObject *) dynamic_cast(mapObj); + if (objPtr.valid()) + { + if (!isClosed) + { + createMapObj = true; + selectMapObj = objPtr->selected(); + removedUidMapObj = objPtr->uniqueMapId(); + + objPtr->removeMySelf(); + objPtr = nullptr; + } + } + else + { + createMapObj = true; + selectMapObj = objPtr->selected(); + removedUidMapObj = objPtr->uniqueMapId(); + + mapObj->removeMySelf(); + } + } + } + else createMapObj = true; + + if (createMapObj) + { + // re use the uid if the map object was removed before ... + if (isClosed) objPtr = (SituationMap::AbstractMultiPointMapObject *) objFac.create ( removedUidMapObj, layer, QLatin1String("PlanElement")).get(); + else objPtr = (SituationMap::AbstractMultiPointMapObject *) objFac.create( removedUidMapObj, layer, QLatin1String("PlanElement")).get(); + + if (!objPtr.valid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " SituationMap::ObjectFactory.create() failed"; + return; + } + + objPtr->setSelected(selectMapObj); + objPtr->setScaleEnable(false); // disable scaling (setScale, most of the time done by user) + } + + objPtr->setPickable(m_nodesPickable); + + uint lineWidth = setLineStyle(p, objPtr.get()); + + Q_UNUSED(lineWidth); + + if (!isFilled) + { + SituationMap::FillStyle &&objFillStyle = objPtr->fillStyle(); + objFillStyle.setFillPattern(SituationMap::FillPattern::Null); + objPtr->setFillStyle(objFillStyle); + } + else setFillStyle(p, objPtr.get()); + + if ((!isClosed) && (!isEllipse)) + { + SituationMap::Polyline *lineObj = dynamic_cast(objPtr.get()); + + if (lineObj) + { + // adding arrows if defined + + lineObj -> setArrowHeadStart(false); + lineObj -> setArrowHeadEnd(false); + + GeometryModel::Arrows arrows = (GeometryModel::Arrows) m_geometryModel.getAttribute(p, GeometryModel::Attribute::Arrows).toInt(); + + switch (arrows) + { + case GeometryModel::Arrows::Both : lineObj -> setArrowHeadEnd(true); + Q_FALLTHROUGH(); + case GeometryModel::Arrows::Start : lineObj -> setArrowHeadStart(true); break; + case GeometryModel::Arrows::End : lineObj -> setArrowHeadEnd(true); break; + case GeometryModel::Arrows::NoArrows : + default : arrows = GeometryModel::Arrows::NoArrows; break; + } + + if (arrows != GeometryModel::Arrows::NoArrows) + { + #ifdef NEVER + double scale = (lineWidth < 2) ? 1.0 : double(lineWidth) * 0.5; + + double arrowWidth = scale * (m_geometryModel.getAttribute(p, GeometryModel::Attribute::ArrowWidth).toDouble()); + double arrowHeight = scale * (m_geometryModel.getAttribute(p, GeometryModel::Attribute::ArrowHeight).toDouble()); + double arrowClampHeight = scale * (m_geometryModel.getAttribute(p, GeometryModel::Attribute::ArrowClampHeight).toDouble()); + + Q_UNUSED(arrowWidth); + Q_UNUSED(arrowHeight); + Q_UNUSED(arrowClampHeight); + #endif + } + } + } + + (geoCoordinates) ? objPtr->setPositions(SituationMap::convert2D3D(points)) : objPtr->setPositionsTranslated(SituationMap::convert2D3D(points)); + + bool success = objPtr->updateView(); + + if (!success) ERROR_LOG(m_logger) << LOGFUNCTION << " failed"; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueMapId=" << objPtr->uniqueMapId(); + + // Store id in primitive + m_geometryModel.setUserData(p, objPtr->uniqueMapId()); + + } // void GeometryModelRendererEsri::drawPolyline + + + void GeometryModelRendererEsri::drawRectangle(const GeometryModel::Node &p, const QString &layer, SituationMap::AbstractMapObject *mapObj) + { + QList points = m_geometryModel.getPrimitivePoints(p); + if (points.size() != 4) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << "Primitive Rectangle has wrong count of points!"; + return; + } + ++m_stats.m_drawRectangleCount; + m_stats.m_timer.restart(); + + GeometryModelRendererEsri::drawPolyline(p, layer, points, mapObj); + + int ms = m_stats.m_timer.elapsed(); + m_stats.m_msRectangle += ms; + m_stats.m_msRectangleMin = std::min(ms, m_stats.m_msRectangleMin); + m_stats.m_msRectangleMax = std::max(ms, m_stats.m_msRectangleMax); + } // void GeometryModelRendererEsri::drawRectangle + + + void GeometryModelRendererEsri::drawSpline(const GeometryModel::Node &p, const QString &layer, SituationMap::AbstractMapObject *mapObj) + { + SituationMap::CoordTranslationFunctor *ctf = m_map->coordTranslationFunctor(); + + if (nullptr == ctf) + { + ERROR_LOG(m_logger) << LOGFUNCTION << "Map doesn't have defined any map projection! Can't render spline ..."; +#if LTS_DEBUG + abort(); +#else + return; +#endif // LTS_DEBUG + } + + ++m_stats.m_drawSplineCount; + m_stats.m_timer.restart(); + + SituationMap::MathUtils::SmoothLine splineCurve; + std::unique_ptr pathRasterer(splineCurve.createPathRaster()); + bool isClosed(m_geometryModel.getAttribute(p, GeometryModel::Attribute::Close).toBool()); + + const auto & pointsMap = m_geometryModel.getPrimitivePoints(p); + + if (pointsMap.size() < 2) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << "Too little count of occordiantes. Can't render spline ..."; + return; + } + + QList points; + points.reserve(pointsMap.size()); + + for (int i = 0; i < pointsMap.size(); ++i) + { + points.push_back(ctf->geographicToMap(pointsMap.at(i))); + } + + qreal referenceLon = 9999; + if (ctf->mapIsGeographic()) + { + // In case of geographic coordinate system do rasterization in UTM coordinate system to avoid + // trouble with termination threshold. + referenceLon = points.first().x(); + ctf->geographicToUtm( referenceLon, points ); + } + + splineCurve.vertexVector(points.toVector(), isClosed); + + pathRasterer -> epsilon(0.25 * m_map->pixelSizeMeter()); + pathRasterer -> maxVertexCount(1000); + + QList rasterizedPoints; + pathRasterer -> rasterize(splineCurve.minimumCurveParameter(), splineCurve.maximumCurveParameter(), rasterizedPoints); + + if (ctf->mapIsGeographic()) + ctf->utmToGeographic( referenceLon, rasterizedPoints ); + + if (rasterizedPoints.size() <= 2) + { + NOTICE_LOG(m_logger) << LOGFUNCTION + << "< pointsMap.size() " << pointsMap.size() + << "< points.size() " << points.size() + << "< splineCurve.minimumCurveParameter() " << splineCurve.minimumCurveParameter() + << "< splineCurve.maximumCurveParameter() " << splineCurve.maximumCurveParameter() + << "< rasterizedPoints.size() === " << rasterizedPoints.size() + << "< m_map->pixelSizeMeter()=" << m_map->pixelSizeMeter(); + } + drawPolyline(p, layer, rasterizedPoints, mapObj, false); + + int ms = m_stats.m_timer.elapsed(); + m_stats.m_msSpline += ms; + m_stats.m_msSplineMin = std::min(ms, m_stats.m_msSplineMin); + m_stats.m_msSplineMax = std::max(ms, m_stats.m_msSplineMax); + + } // void GeometryModelRendererEsri::drawSpline + + + + void GeometryModelRendererEsri::drawSymbol(const GeometryModel::Node &p, const QString &layer, SituationMap::AbstractMapObject *mapObj) + { + if (nullptr == m_map->coordTranslationFunctor()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << "Map doesn't have defined any map projection! Can't render symbol ..."; + return; + } + + const QVector &partyIds = m_map->partyIdToColorFunctor().partyIds(); + if (partyIds.size() < 1) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Party colors are not defined. Can't render symbol ..."; + return; + } + + ++m_stats.m_drawSymbolCount; + + const auto & pointsMap = m_geometryModel.getPrimitivePoints(p); + + if (pointsMap.size() == 0) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Primitive Symbol has no points!"; + return; + } + + const QPointF & point = pointsMap.at(0); + + SituationMap::RefPointer objPtr; + SituationMap::ObjectFactory &objFac = m_map->objectFactory(); + bool createMapObj = false; + + if (mapObj) + { + objPtr = (SituationMap::Participant *) dynamic_cast(mapObj); + if (!objPtr.valid()) + { + createMapObj = true; + mapObj->removeMySelf(); + } + } + else createMapObj = true; + + if (createMapObj) + { + objPtr = (SituationMap::Participant *) objFac.create (layer, QLatin1String("PlanElement")).get(); + + if (!objPtr.valid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " SituationMap::ObjectFactory.create() failed"; + return; + } + objPtr->setScaleEnable(false); // disable scaling (setScale, most of the time done by user) + } + + bool ok; + + SituationMap::GeometryModel::t_vbg_et vbg = SituationMap::GeometryModel::t_vbg_et(m_geometryModel.getAttribute(p,SituationMap::GeometryModel::Attribute::att_vbg_et).toInt()); + SymbologyXML::SymbolTypes::t_state state = SymbologyXML::SymbolTypes::t_state( m_geometryModel.getAttribute(p,SituationMap::GeometryModel::Attribute::SymbolState).toInt() ); + SymbologyXML::SymbolTypes::t_uncertainty uncertainty = SymbologyXML::SymbolTypes::t_uncertainty( m_geometryModel.getAttribute(p,SituationMap::GeometryModel::Attribute::SymbolUncertainty).toInt() ); + SymbologyXML::SymbolTypes::t_affil affil = SymbologyXML::SymbolTypes::t_affil(m_geometryModel.getAttribute(p,SituationMap::GeometryModel::Attribute::Affiliation).toInt()); + SymbologyXML::SymbolTypes::t_mob mob = SymbologyXML::SymbolTypes::t_mob(m_geometryModel.getAttribute(p,SituationMap::GeometryModel::Attribute::Mobility).toInt()); + // bool abcProtect = m_geometryModel.getAttribute(p,SituationMap::GeometryModel::Attribute::AbcProtection).toBool(); + + SymbolData symData; + + int symbolID = m_geometryModel.getAttribute(p, GeometryModel::Attribute::SymbolId).toInt(); + if (symbolID < 0) + { + ERROR_LOG(m_logger) << LOGFUNCTION << "Primitive Symbol has undefinded symbol!"; + return; + } + + symData.sidc = AdvancedSymbology::SidcCodec::encodeCivilXmlSymbolId(uint(symbolID),state,uncertainty,affil,mob); + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + int symbolOffsetX = m_geometryModel.getAttribute(p,SituationMap::GeometryModel::Attribute::SymbolOffsetX).toInt(&ok); if (!ok) symbolOffsetX = 0; + int symbolOffsetY = m_geometryModel.getAttribute(p,SituationMap::GeometryModel::Attribute::SymbolOffsetY).toInt(&ok); if (!ok) symbolOffsetY = 0; + int symbolDirection = m_geometryModel.getAttribute(p,SituationMap::GeometryModel::Attribute::SymbolDirection).toInt(&ok); if (!ok) symbolDirection = 0; + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + float symbolScale = m_geometryModel.getAttribute(p,SituationMap::GeometryModel::Attribute::SymbolExplicitScale).toFloat(&ok); if (!ok) symbolScale = 1.0f; + int symbolForceId = m_geometryModel.getAttribute(p, GeometryModel::Attribute::ForceId).toInt(&ok); if (!ok) symbolForceId = -1; + int symbolInjuryId = m_geometryModel.getAttribute(p, GeometryModel::Attribute::DamageForceId).toInt(&ok); if (!ok) symbolInjuryId = -1; + + if (!partyIds.contains(symbolForceId)) symbolForceId = partyIds.first(); + if (!partyIds.contains(symbolInjuryId)) symbolInjuryId = partyIds.first(); + + symData.size = (symbolScale > 0) ? symbolScale : 1.0; + + objPtr->setOrigin( Point3D(point.x(),point.y(),0.0) ); + objPtr->setPickable(m_nodesPickable); + + objPtr->setSymbol(symData); + objPtr->setPartyId( symbolForceId ); + + AdvancedSymbology::Injury injury = AdvancedSymbology::Injury::NoInjury; + + switch (vbg) + { + case SituationMap::GeometryModel::t_vbg_et::ANNAEHENDER_TREFFER : + case SituationMap::GeometryModel::t_vbg_et::TREFFER_OHNE_WIRKUNG : + case SituationMap::GeometryModel::t_vbg_et::EINSATZBEREIT : injury = AdvancedSymbology::Injury::NoInjury; break; + + case SituationMap::GeometryModel::t_vbg_et::AUSFALL_WAFFENANLAGE : + case SituationMap::GeometryModel::t_vbg_et::AUSFALL_WAFFENANLAGE_SICHTB : injury = AdvancedSymbology::Injury::WeaponKill; break; + + case SituationMap::GeometryModel::t_vbg_et::BEWEGUNGSUNFAEHIG : + case SituationMap::GeometryModel::t_vbg_et::BEWEGUNGSUNFAEHIG_SICHTB : injury = AdvancedSymbology::Injury::MobilityKill; break; + + case SituationMap::GeometryModel::t_vbg_et::LEICHTER_TREFFER : + case SituationMap::GeometryModel::t_vbg_et::MITTLERER_TREFFER : + case SituationMap::GeometryModel::t_vbg_et::SCHWERER_TREFFER : injury = AdvancedSymbology::Injury::Hit; break; + case SituationMap::GeometryModel::t_vbg_et::TOTALAUSFALL : injury = AdvancedSymbology::Injury::TotalKill; break; + case SituationMap::GeometryModel::t_vbg_et::ZEITLICHER_AUSFALL : injury = AdvancedSymbology::Injury::TotalKill; break; + } + + objPtr->setInjury( injury , symbolInjuryId ); + + objPtr->setTextModifier( SituationMap::Participant::TextModifier::W_DateTimeGroup , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_W).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::T_UniqueDesignation , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_T).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::M_HigherFormation , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_M).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::EF_ReinforcedReduced , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_EF).toString()); + + objPtr->setTextModifier( SituationMap::Participant::TextModifier::XY_AltitudeLocation , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_XY).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::V_Type , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_V).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::Z_Speed , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_Z).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::G_StaffComments , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_G).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::H_AdditionalInformation, m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_H).toString()); + objPtr->setTextModifier( SituationMap::Participant::TextModifier::JKLNP , m_geometryModel.getAttribute(p, GeometryModel::Attribute::Text_KLNPJ).toString()); + + + /* + AdditionalSymbolType additionalSymbol; + + AdditionalSymbolType::NbcProtection, ///< Line at top left. + AdditionalSymbolType::Manipulation, ///< Black and white dashed lines at bottom (1st of three) + AdditionalSymbolType::NoPosition, ///< Red line at bottom (2nd of three) + AdditionalSymbolType::BiteError ///< Yellow line at bottom. (3rd of three) + + objPtr->setAdditionalSymbol(additionalSymbol); + */ + + bool success = objPtr->updateView(); + + if (!success) ERROR_LOG(m_logger) << LOGFUNCTION << " failed"; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueMapId=" << objPtr->uniqueMapId(); + + // Store id in primitive + m_geometryModel.setUserData(p, objPtr->uniqueMapId()); + + int ms = m_stats.m_timer.elapsed(); + m_stats.m_msSymbol += ms; + m_stats.m_msSymbolMin = std::min(ms, m_stats.m_msDictionarySymbolMin); + m_stats.m_msSymbolMax = std::max(ms, m_stats.m_msDictionarySymbolMax); + + } // void GeometryModelRendererEsri::drawSymbol + + + void GeometryModelRendererEsri::drawText(const GeometryModel::Node &p, const QString &layer, SituationMap::AbstractMapObject * /*mapObj*/) + { + QList points = m_geometryModel.getPrimitivePoints(p); + + if (points.size() != 1) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << "Primitive TextBox doesn't have origin!"; + return; + } + + SituationMap::UniqueMapId mapId = getIdFromNode(p); + SituationMap::ObjectFactory &objFac = m_map->objectFactory(); + SituationMap::RefPointer objPtr; + + if (SituationMap::isUniqueMapIdValid(mapId)) + objPtr = objFac.get(mapId).get(); + + if (!objPtr.valid()) + { + objPtr = objFac.create (layer, QLatin1String("PlanElement")).get(); + + if (!objPtr.valid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " objFac.create failed"; + return; + } + objPtr->setScaleEnable(false); // disable scaling (setScale, most of the time done by user) + } + + ++m_stats.m_drawTextCount; + m_stats.m_timer.restart(); + + double phi = m_geometryModel.getAttribute(p, GeometryModel::Attribute::Rotate).toDouble(); // *(M_PI/180.0); + const QString & text = m_geometryModel.getTextData(p); + double fontSize = m_geometryModel.getAttribute(p, GeometryModel::Attribute::FontSize).toDouble(); + const QColor & textColor = m_geometryModel.getAttribute(p, GeometryModel::Attribute::Color).value(); + const QColor & lineColor = m_geometryModel.getAttribute(p, GeometryModel::Attribute::LineColor).value(); + QColor fillColor = m_geometryModel.getAttribute(p, GeometryModel::Attribute::FillColor).value(); + int lineWidth = m_geometryModel.getAttribute(p, GeometryModel::Attribute::LineWidth).toInt(); + bool fill = m_geometryModel.getAttribute(p, GeometryModel::Attribute::Fill).toBool(); + bool bold = m_geometryModel.getAttribute(p, GeometryModel::Attribute::FontBold).toBool(); + bool italic = m_geometryModel.getAttribute(p, GeometryModel::Attribute::FontItalic).toBool(); + bool useBox = m_geometryModel.getAttribute(p, GeometryModel::Attribute::Box).toBool(); + GeometryModel::Font pmFont = (GeometryModel::Font)(m_geometryModel.getAttribute(p, GeometryModel::Attribute::Font).toInt()); + const GeometryModel::Node & td = p.firstChildNode(QLatin1String("text_data")); + GeometryModel::HorizontalAlign pmAlign = (GeometryModel::HorizontalAlign)(m_geometryModel.getAttribute(td, GeometryModel::Attribute::Alignment).toInt()); + GeometryModel::HorizontalAlign anchorH = (GeometryModel::HorizontalAlign)(m_geometryModel.getAttribute(p, GeometryModel::Attribute::AnchorHorizontal).toInt()); + GeometryModel::VerticalAlign anchorV = (GeometryModel::VerticalAlign)(m_geometryModel.getAttribute(p, GeometryModel::Attribute::AnchorVertical).toInt()); + int marginH = m_geometryModel.getAttribute(p, GeometryModel::Attribute::MarginHorizontal).toInt(); + int marginV = m_geometryModel.getAttribute(p, GeometryModel::Attribute::MarginVertical).toInt(); + + if (fontSize <= 0) fontSize = 14.0; + + Q_UNUSED(useBox); + Q_UNUSED(marginH); + Q_UNUSED(marginV); + + QString fontName; + + switch (pmFont) + { + case GeometryModel::Font::SansSerif : fontName = QLatin1String("Arial"); break; + case GeometryModel::Font::Serif : fontName = QLatin1String("Times New Roman"); break; + case GeometryModel::Font::Fixed : fontName = QLatin1String("Courier New"); break; + case GeometryModel::Font::Decorative : + { + QStringList fontFamilies = QFontDatabase().families(); + + fontName.clear(); + for (const QLatin1String &fontFamilyName : { QLatin1String("Freestyle Script"), QLatin1String("MV Boli"), QLatin1String("URW Chancery L")}) + { + if (fontFamilies.contains(fontFamilyName)) + { + fontName = fontFamilyName; + break; + } + } + } break; + default : fontName = QLatin1String("Wingdings"); break; + } + + SituationMap::HorizontalAlignment fontAlignm = SituationMap::HorizontalAlignment::Left; + SituationMap::HorizontalAlignment hAlignm = SituationMap::HorizontalAlignment::Center; + SituationMap::VerticalAlignment vAlignm = SituationMap::VerticalAlignment::Middle; + + switch (pmAlign) + { + case GeometryModel::HorizontalAlign::Left : fontAlignm = SituationMap::HorizontalAlignment::Left; break; + case GeometryModel::HorizontalAlign::HCenter : fontAlignm = SituationMap::HorizontalAlignment::Center; break; + case GeometryModel::HorizontalAlign::Right : fontAlignm = SituationMap::HorizontalAlignment::Right; break; + } + + Q_UNUSED(fontAlignm); + + switch (anchorV) + { + case GeometryModel::VerticalAlign::Bottom : vAlignm = SituationMap::VerticalAlignment::Bottom; break; + case GeometryModel::VerticalAlign::VCenter : vAlignm = SituationMap::VerticalAlignment::Middle; break; + case GeometryModel::VerticalAlign::Top : vAlignm = SituationMap::VerticalAlignment::Top; break; + } + + switch (anchorH) + { + case GeometryModel::HorizontalAlign::Left : hAlignm = SituationMap::HorizontalAlignment::Left; break; + case GeometryModel::HorizontalAlign::HCenter : hAlignm = SituationMap::HorizontalAlignment::Center; break; + case GeometryModel::HorizontalAlign::Right : hAlignm = SituationMap::HorizontalAlignment::Right; break; + } + + // uint lineWidth = setLineStyle( p, objPtr.get() ); + + setFillStyle(p, objPtr.get()); + + SituationMap::TextStyle &textStyle = objPtr->rTextStyle(); + + if (!fill) fillColor.setAlpha(0); + + textStyle.setTextColor(textColor); + textStyle.setTextOutlineColor(lineColor); + textStyle.setBackgroundColor(fillColor); + textStyle.setTextOutlineWidth(lineWidth-1); + // textStyle.setTextOutlineWidth(0.0f); + + textStyle.setFontFamily(fontName); + textStyle.setSize(fontSize); + textStyle.setAngle(phi); + textStyle.setBold(bold); + textStyle.setItalic(italic); + textStyle.setHorizontalAlignment(hAlignm); + textStyle.setVerticalAlignment(vAlignm); + + + objPtr -> setIdentifier(text.isEmpty() ? QLatin1String("????????") : text); + objPtr -> setPickable(m_nodesPickable); + objPtr -> setOrigin(SituationMap::Point3D::fromQPointF(points.first())); + objPtr -> setShowAnchorMarker(m_editorMode); + + bool success = objPtr->updateView(); + + if (!success) ERROR_LOG(m_logger) << LOGFUNCTION << " failed"; + + // Store id in primitive + m_geometryModel.setUserData(p, objPtr->uniqueMapId()); + + int ms = m_stats.m_timer.elapsed(); + m_stats.m_msText += ms; + m_stats.m_msTextMin = std::min(ms, m_stats.m_msTextMin); + m_stats.m_msTextMax = std::max(ms, m_stats.m_msTextMax); + + } // void GeometryModelRendererEsri::drawText + +} // namepace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/geometry_model_renderer_esri.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/geometry_model_renderer_esri.h new file mode 100755 index 000000000..80b118677 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/geometry_model_renderer_esri.h @@ -0,0 +1,95 @@ +/** + * @RDE_HEADER@ + * GeometryModel - Renderer + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include +#include +#include +#include "situation_map_export.h" + +// Qt +#include + + +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 &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 uint setLineStyle(const GeometryModel::Node &p, MapObjectClass *objPtr) const; + template void setFillStyle(const GeometryModel::Node &p, MapObjectClass *objPtr) const; + + bool m_editorMode; + QTimer *m_delayedRenderTimer; + QSet 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 +} + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/grid_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/grid_impl.cpp new file mode 100755 index 000000000..759293cd2 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/grid_impl.cpp @@ -0,0 +1,220 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of Grid + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "grid_impl.h" +#include "map_impl.h" + + +// ESRI +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// 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(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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/grid_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/grid_impl.h new file mode 100755 index 000000000..900a907d5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/grid_impl.h @@ -0,0 +1,61 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of Grid + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include // Interface +#include + + +//---------------------------------------------------------------------------- +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 + +//---------------------------------------------------------------------------- + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_custom_view.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_custom_view.cpp new file mode 100755 index 000000000..32c7b9384 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_custom_view.cpp @@ -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 + + +// Ot +#include +#include +#include + + +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(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(); + m_ui->image->setPixmap(QPixmap::fromImage(img)); + } +} + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_custom_view.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_custom_view.h new file mode 100755 index 000000000..37dd791b0 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_custom_view.h @@ -0,0 +1,48 @@ +/** + * @RDE_HEADER@ + * CustomView for MapObjects + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Norbert Thoden, SLSE, 2017-04-11 + */ +#pragma once + +// Own +#include +#include + + +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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_custom_view.ui b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_custom_view.ui new file mode 100755 index 000000000..67db1b88a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_custom_view.ui @@ -0,0 +1,80 @@ + + + JumpPointCustomView + + + + 0 + 0 + 62 + 82 + + + + + 0 + 0 + + + + + + + + + 6 + 6 + 50 + 50 + + + + + 0 + 0 + + + + + 50 + 50 + + + + + 50 + 50 + + + + + + + 6 + 57 + 50 + 19 + + + + <Desc> + + + Qt::AlignCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + LtsWidgets::LtsImageEditor + QWidget +
LtsWidgets/lts_image_editor.h
+ 1 +
+
+ + +
diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_model.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_model.cpp new file mode 100755 index 000000000..4f309de05 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_model.cpp @@ -0,0 +1,166 @@ +/** + * @RDE_HEADER@ + * Interface for JumpPointWidget + * $Id$ + * $HeadURL$ + * $Author$ + */ + + +// Own +#include "jump_point_model.h" +#include +#include +#include +#include +#include + + +// Qt +#include + + +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("")); + + 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("
").arg(QString::fromLatin1(baData.toBase64()))); // how can i center the image? + tooltip.append(QLatin1String("
")); + } + tooltip.append(QStringLiteral("

name: %1

").arg(data.m_name)); + tooltip.append(QStringLiteral("

origin: %1

").arg(m_map->geoToMgrsString(data.m_originGeo.toPointF()))); + tooltip.append(QStringLiteral("

scale: %1

").arg(data.m_mapScale)); + tooltip.append(QStringLiteral("")); + 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 : std::as_const(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 JumpPointModel::jumpPoints() +{ + return m_vecData; +} + +QStringList JumpPointModel::names() +{ + QStringList sl; + sl.reserve(m_vecData.size()); + + for (const JPData &item : std::as_const(m_vecData)) + sl.append(item.m_name); + return sl; +} + + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_model.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_model.h new file mode 100755 index 000000000..2f3d502dc --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_model.h @@ -0,0 +1,105 @@ +/** + * @RDE_HEADER@ + * Interface + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "situation_map_export.h" +#include +#include +#include + +#include + + +// 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 m_marker; + QImage m_image; +}; + + +//---------------------------------------------------------------------------- +// JumpPointModel +//---------------------------------------------------------------------------- + +class JumpPointModel: public LtsWidgets::LtsAbstractTableModel +{ + Q_OBJECT + using BaseClass = LtsWidgets::LtsAbstractTableModel; + +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 jumpPoints(); + + QStringList names(); + +private: + Map *m_map = nullptr; + +}; + +} // namespace SituationMap + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_namer.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_namer.cpp new file mode 100755 index 000000000..012554eca --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_namer.cpp @@ -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(); + } +} +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_namer.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_namer.h new file mode 100755 index 000000000..120377b7a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_namer.h @@ -0,0 +1,39 @@ +/** + * @RDE_HEADER@ + * Declaration/Definition + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + + +#include + +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; +}; + +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_namer.ui b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_namer.ui new file mode 100755 index 000000000..036153e12 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_namer.ui @@ -0,0 +1,46 @@ + + + JumpPointNamer + + + + 0 + 0 + 224 + 82 + + + + Set Jump Point Name + + + + + + + + Name: + + + + + + + + + + + + + + + + LtsWidgets::LtsDialogButtonBox + QWidget +
LtsWidgets/lts_dialog_button_box.h
+ 1 +
+
+ + +
diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_widget.cpp new file mode 100755 index 000000000..5cbc2547d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_widget.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + + +// Qt +#include +#include +#include +#include +#include +#include + + +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 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 JumpPointWidget::jumpPoints() +{ + if (!d->m_jumpPointModel) + return QVector(); + 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(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(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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_widget.h new file mode 100755 index 000000000..9918d612c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/jump_point_widget.h @@ -0,0 +1,103 @@ +/** + * @RDE_HEADER@ + * Interface + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "jump_point_model.h" +#include "situation_map_export.h" +#include +#include +#include + +// +#include + +#include + +// Qt +#include + + +//---------------------------------------------------------------------------- +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 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 + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/layer_control_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/layer_control_widget.cpp new file mode 100755 index 000000000..826b69ece --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/layer_control_widget.cpp @@ -0,0 +1,1134 @@ +/** + * @RDE_HEADER@ + * Interface + * $Id$ + * $HeadURL$ + * $Author$ + */ + + +// Own +#include "layer_control_widget.h" +#include +#include +#include + + +// Qt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +namespace SituationMap +{ + +const QLatin1String MIMETYPE("application/x-dndEsriLayerItemData"); + +//---------------------------------------------------------------------------- +const char LayernameProperty[] = "layername"; +//---------------------------------------------------------------------------- + + + +class DragableCheckBox : public QCheckBox +{ + Q_OBJECT + using BaseClass = QCheckBox; + +public: + DragableCheckBox() = default; + explicit DragableCheckBox(const QString &text) + : QCheckBox(text) + {} + + ~DragableCheckBox() = default; + + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + +private: + QPoint m_dragStartPos; +}; // class LayerControlWidget::Private +//----------------------------------------------------------------------------- + +void DragableCheckBox::mousePressEvent(QMouseEvent *event) +{ +// qWarning() << LOGFUNCTION << " START"; + + if (event->button() == Qt::LeftButton) + m_dragStartPos = event->pos(); + + BaseClass::mousePressEvent(event); + +// qWarning() << LOGFUNCTION << " END"; +} + +void DragableCheckBox::mouseMoveEvent(QMouseEvent *event) +{ +// qWarning() << LOGFUNCTION << " START"; + + if (!(event->buttons() & Qt::LeftButton)) + return; + if ((event->pos() - m_dragStartPos).manhattanLength() + < QApplication::startDragDistance()) + return; + + QByteArray itemData; + QDataStream dataStream(&itemData, QIODevice::WriteOnly); + dataStream << this->property(LayernameProperty).toString(); // == layername + + QMimeData *mimeData = new QMimeData; + mimeData->setData(MIMETYPE, itemData); + + QDrag *drag = new QDrag(this); + drag->setMimeData(mimeData); + + drag->exec(Qt::MoveAction); + +// qWarning() << LOGFUNCTION << " END"; +} +//----------------------------------------------------------------------------- + +struct SliderStruct +{ + QCheckBox *cbOnOff = nullptr; + QSlider *sl_opacity = nullptr; + QSlider *sl_brightness = nullptr; + QSlider *sl_contrast = nullptr; + QSlider *sl_gamma = nullptr; +}; + +class LayerControlWidget::Private +{ + +public: + Private() = default; + + // OWN data: + SituationMap::Map *m_map = nullptr; // Use INTERFACE + QString m_debugInfo; + + bool m_zoomRotScaleEnabled = true; + bool m_layerEnabled = true; + bool m_basemapEnabled = true; + + + QVBoxLayout *m_layout = nullptr; + + // MapLayer: + QGridLayout *m_mapLayerLayout = nullptr; + + // BaseMapSelection: + QHBoxLayout *m_baseMapSelectLayout = nullptr; + QList m_baseMapSelectIcons; + QLabel *m_lbBasemap = nullptr; + + + + QMap m_sliderMap; + + // Zoom and rotation + QHBoxLayout *m_zoomLayout = nullptr; // will contain zoom and rotation elements + QSlider *m_zoomSlider = nullptr; + QSlider *m_rotationSlider = nullptr; + QSlider *m_symbolSizeSlider = nullptr; // 10 ... 200 + QTimer *m_symbolSizeTimer = nullptr; + +}; // class LayerControlWidget::Private +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// class LayerControlWidget +//----------------------------------------------------------------------------- +LayerControlWidget::LayerControlWidget(QWidget *parent, + Map *map, + bool enableLayer, + bool enableBasemap, + const QString &debugInfo) + : QWidget(parent) + , d(new Private) + , m_logger(getLogger("LayerControlWidget")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo='" << debugInfo << "'"; + + d->m_debugInfo = debugInfo; + d->m_map = map; + d->m_layerEnabled = enableLayer; + d->m_basemapEnabled = enableBasemap; + + + d->m_layout = new QVBoxLayout(); + this->setLayout(d->m_layout); + setAcceptDrops(true); + + // use QueuedConnect to let dropEvent (and destroying of QDrag object) finish + // BEFORE the panel is updated (and DragableCheckBox - parent of QDrag object - are deleted.) + connect(d->m_map, &Map::layerOrderChanged, this, &LayerControlWidget::updatePanelLayer, Qt::ConnectionType::QueuedConnection); + connect(d->m_map, &Map::layerParameterChanged, this, &LayerControlWidget::onLayerParamterChanged); + connect(d->m_map, &Map::mapReady, this, &LayerControlWidget::onMapReady, Qt::ConnectionType::QueuedConnection); + + + updatePanel(); + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; +} + +LayerControlWidget::~LayerControlWidget() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; + delete d; +} + + +bool LayerControlWidget::updatePanel() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + + updatePanelZoomRotationScales(); + updatePanelLayer(); +// updatePanelBasemap(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; + return true; +} + +bool LayerControlWidget::updatePanelZoomRotationScales() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + + if (! d->m_zoomRotScaleEnabled) + return false; + + if (d->m_zoomLayout) + return true; + + d->m_zoomLayout = new QHBoxLayout(); + d->m_layout->addLayout(d->m_zoomLayout); + + d->m_zoomSlider = generateZoomSlider(); + d->m_zoomLayout->addWidget(d->m_zoomSlider); + + // problems around scale in 3D: + d->m_zoomSlider->setEnabled(d->m_map->mode() == Mode::Mode_2D); + + + QComboBox *cmbScale = new QComboBox(); + d->m_zoomLayout->addWidget(cmbScale); + + cmbScale->addItem(tr("Presets")); + cmbScale->insertSeparator(1); + + using scaleDataTyp = QPair; + const auto scaleData = { + scaleDataTyp(QLatin1String(" 1:25000"), 400000), + scaleDataTyp(QLatin1String(" 1:50000"), 200000), + scaleDataTyp(QLatin1String("1:100000"), 100000) + }; + + for (const scaleDataTyp &item : scaleData) + cmbScale->addItem(item.first, QVariant(item.second)); // stored in Qt::UserRole + + // c++11 -> lambda :-) + connect(cmbScale, static_cast(&QComboBox::activated), this, [this, cmbScale](int index) + { + if (index <= 1) return; // 1 Damit 'Presets' + Separator ohne Auswirkung bleibt + + int selectedScale = cmbScale->currentData().toInt(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " cmbScale selectedScale=" << selectedScale; + d->m_map->setScale(selectedScale); + + cmbScale->setCurrentIndex(0); // Textausgabe auf 'Presets' setzen + }); + + + // + // Rotation: + // + if (! d->m_rotationSlider) + { + d->m_rotationSlider = generateRotationSlider(); + d->m_layout->addWidget(d->m_rotationSlider); + } + + // + // SymbolSize: + // + if (! d->m_symbolSizeSlider) + { + d->m_symbolSizeSlider = generateSymbolSizeSlider(); + d->m_layout->addWidget(d->m_symbolSizeSlider); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; + return true; +} + +bool LayerControlWidget::updatePanelLayer() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + + + // + // Layer: + // + + // reNew the part of mapLayer : + if (d->m_mapLayerLayout) + { + for (int row = 0; row < d->m_mapLayerLayout->rowCount(); ++row) + { + for (int col = 0; col < d->m_mapLayerLayout->columnCount(); ++col) + { + QLayoutItem *item = d->m_mapLayerLayout->itemAtPosition(row, col); + if (item) + { + QWidget *w = item->widget(); + delete w; + // w->deleteLater() --> QCoreApplication::postEvent: Unexpected null receiver (ication/ngl_qt_msg_handler.inc:25) + } + } + } + } + delete d->m_mapLayerLayout; + d->m_mapLayerLayout = nullptr; + d->m_sliderMap.clear(); + + + + if (! d->m_layerEnabled) + return false; + + + d->m_mapLayerLayout = new QGridLayout(); + d->m_layout->addLayout(d->m_mapLayerLayout); + + int row = 0; + + QCheckBox *cbExpand = new QCheckBox(tr("Expand"), this); + d->m_mapLayerLayout->addWidget(cbExpand, row++, 0); + + cbExpand->setChecked(false); + connect(cbExpand, &QCheckBox::clicked, this, &LayerControlWidget::expandCollapse); + + + // + // Fill MapLayerLayout : + // + if (d->m_map) + { + int col = 0; + + const auto header = { + tr("Layer"), + tr("Opacity"), + tr("Brightness"), + tr("Contrast"), + tr("Gamma") + }; + + for (const QString &name : header) + d->m_mapLayerLayout->addWidget(new QLabel(name), row, col++); + + const QStringList sl = d->m_map->layerFactory().layernames(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " layers=" << sl.join(QLatin1Char(';')); + + row += sl.count(); + + for (const QString &layername : sl) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " layerName=" << layername; + int col = 0; + + const LayerParameter &lp = d->m_map->layerFactory().layerParameter(layername); + const LayerType &layerType = d->m_map->layerFactory().layerType(layername); + + DragableCheckBox *cbOnOff = new DragableCheckBox(); + if (lp.displayName.isEmpty()) + cbOnOff->setText(layername); + else + cbOnOff->setText(lp.displayName); + cbOnOff->setProperty(LayernameProperty, layername); + d->m_sliderMap[layername].cbOnOff = cbOnOff; + cbOnOff->setObjectName(QLatin1String("DragableCheckBox_") + layername); + cbOnOff->setChecked(lp.visible); + + + const QString &tooltip = generateToolTip(layername, lp); + if (!tooltip.isEmpty()) + cbOnOff->setToolTip(tooltip); + + connect(cbOnOff, &QCheckBox::stateChanged, this, &LayerControlWidget::layerOnOff); + d->m_mapLayerLayout->addWidget(cbOnOff, row, col++); + + + mySlider *sl_opac = new mySlider(layername, 0, 100); + d->m_sliderMap[layername].sl_opacity = sl_opac; + sl_opac->setObjectName(QLatin1String("sl_opac")); + sl_opac->setValue(lp.opacity); + connect(sl_opac, &mySlider::valueChanged, this, &LayerControlWidget::opacity); + d->m_mapLayerLayout->addWidget(sl_opac, row, col++); + + if (layerType == SituationMap::LayerType::Basemap) + { + mySlider *sl_brightness = new mySlider(layername, -100, 100); + d->m_sliderMap[layername].sl_brightness = sl_brightness; + sl_brightness->setObjectName(QLatin1String("sl_brightness")); + sl_brightness->setValue(lp.brightness); + connect(sl_brightness, &mySlider::valueChanged, this, &LayerControlWidget::brightness); + d->m_mapLayerLayout->addWidget(sl_brightness, row, col++); + + mySlider *sl_contrast = new mySlider(layername, -100, 100); + d->m_sliderMap[layername].sl_contrast = sl_contrast; + sl_contrast->setObjectName(QLatin1String("sl_contrast")); + sl_contrast->setValue(lp.contrast); + connect(sl_contrast, &mySlider::valueChanged, this, &LayerControlWidget::contrast); + d->m_mapLayerLayout->addWidget(sl_contrast, row, col++); + + mySlider *sl_gamma = new mySlider(layername, -100, 100); + d->m_sliderMap[layername].sl_gamma = sl_gamma; + sl_gamma->setObjectName(QLatin1String("sl_gamma")); + sl_gamma->setValue(lp.gamma); + connect(sl_gamma, &mySlider::valueChanged, this, &LayerControlWidget::gamma); + d->m_mapLayerLayout->addWidget(sl_gamma, row, col++); + } + + --row; + } + +#ifdef ONLY_FOR_TESTS + QPushButton *pbAddBasemapLayer = new QPushButton(QLatin1String("add BasemapLayer")); + d->m_mapLayerLayout->addWidget(pbAddBasemapLayer); + + QLatin1String newBasemapName("basemapAddedByBtn"); + QLatin1String newBasemapUrl("http://192.168.133.88:6080/arcgis/rest/services/BremenGeoTiff/MapServer"); + // QLatin1String newBasemap("http://192.168.133.88:6080/arcgis/rest/services/BremenGeoTiffUTM/MapServer"); + pbAddBasemapLayer->setToolTip(newBasemapUrl); + + connect(pbAddBasemapLayer, &QPushButton::clicked, this, [this]() + { + bool success = d->m_map->layerFactory().addBasemap(newBasemapName, newBasemapUrl); + INFO_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " adding basemapLayer, success=" << success; + }); + + QPushButton *pbRemoveBasemapLayer = new QPushButton(QLatin1String("remove BasemapLayer")); + d->m_mapLayerLayout->addWidget(pbRemoveBasemapLayer); + + pbRemoveBasemapLayer->setToolTip(newBasemapUrl); + + connect(pbRemoveBasemapLayer, &QPushButton::clicked, this, [this]() + { + bool success = d->m_map->layerFactory().removeBasemap(newBasemapName); + INFO_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " removing basemapLayer, success=" << success; + }); +#endif + + expandCollapse(cbExpand->isChecked()); + + + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " no map !!!"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; + return true; +} + +bool LayerControlWidget::updatePanelBasemap() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + + // clear old widgets: + d->m_baseMapSelectIcons.clear(); + if (d->m_baseMapSelectLayout) + { + QLayoutItem *item; + while ((item = d->m_baseMapSelectLayout->takeAt(0))) + { + QWidget *w = item->widget(); + delete w; + delete item; + } + } + delete d->m_baseMapSelectLayout; + d->m_baseMapSelectLayout = nullptr; + + + if (! d->m_basemapEnabled) + return false; + + + + // create from scratch: + if (! d->m_baseMapSelectLayout) + { + QVBoxLayout *vBox = new QVBoxLayout(); + + if (! d->m_lbBasemap) + d->m_lbBasemap = new QLabel(tr("Basemaps:")); + + vBox->addWidget(d->m_lbBasemap); + + d->m_baseMapSelectLayout = new QHBoxLayout(); + vBox->addLayout(d->m_baseMapSelectLayout); + + d->m_layout->addLayout(vBox); + + LayerFactory &layFac = d->m_map->layerFactory(); + const QStringList &basemapLayernames = layFac.layernames(LayerType::Basemap); + + for (const QString &layername : basemapLayernames) + { + // baseMapProxy->setStyleSheet(QLatin1String("QPushButton {background-color: transparent; border: 1px solid transparent;}")); + const LayerParameter &lp = d->m_map->layerFactory().layerParameter(layername); + + QToolButton *baseMapProxy = new QToolButton(this); + + baseMapProxy->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); // eTextUnderIcon? + baseMapProxy->setText(lp.displayName); + + + baseMapProxy->setObjectName(d->m_debugInfo + layername); + baseMapProxy->setProperty(LayernameProperty, layername); + + baseMapProxy->setIcon(QIcon(QPixmap::fromImage(lp.previewImage))); + baseMapProxy->setIconSize(QSize(40,40)); + + baseMapProxy->setCheckable(true); + baseMapProxy->setChecked(true); // Assume that all are enable by default + + const QString &tooltip = generateToolTip(layername, lp); + if (!tooltip.isEmpty()) + baseMapProxy->setToolTip(tooltip); + + d->m_baseMapSelectLayout->addWidget(baseMapProxy); + d->m_baseMapSelectIcons.append(baseMapProxy); + connect(baseMapProxy, &QToolButton::clicked, this, &LayerControlWidget::onBaseMapSelection); + } // Q_FOREACH + + d->m_baseMapSelectLayout->addStretch(42); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; + return true; +} + +void LayerControlWidget::expandCollapse(bool expanded) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + + for (int row = 1; row < d->m_mapLayerLayout->rowCount(); ++row) + { + for (int col = 2; col < d->m_mapLayerLayout->columnCount(); ++col) + { + QLayoutItem *item = d->m_mapLayerLayout->itemAtPosition(row, col); + if (item) + { + QWidget *w = item->widget(); + if (w) + w->setVisible(expanded); + } + } + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; +} + +QString LayerControlWidget::generateToolTip(const QString &layername, + const LayerParameter &lp) +{ + QString tooltip; + + tooltip.append(QStringLiteral("")); + + tooltip.append(QStringLiteral("

displayName=%1

").arg(lp.displayName)); + + if (! lp.previewImage.isNull()) + { + // without a temporary file: + QByteArray data; + QBuffer buffer(&data); + lp.previewImage.save(&buffer, "PNG", 100); + QString html(QLatin1String("
")); + html.append(QStringLiteral("").arg(QString::fromLatin1(data.toBase64()))); + tooltip.append(html); + } + + if (lp.basemapType != BasemapType::Undefined) + tooltip.append(QStringLiteral("

BasemapType=%1

").arg(toDisplayString(lp.basemapType))); + + + tooltip.append(QStringLiteral("

layername=%1

").arg(layername)); + tooltip.append(QStringLiteral("

description=%1

").arg(lp.description)); + tooltip.append(QStringLiteral("

svcDescription=%1

").arg(lp.svcDescription)); + tooltip.append(QStringLiteral("

url=%1

").arg(lp.url.toString())); + tooltip.append(QStringLiteral("

minScale=%1 - maxScale=%2

").arg((int)lp.minScale).arg((int)lp.maxScale)); + tooltip.append(QStringLiteral("")); + + return tooltip; +} + +QSlider *LayerControlWidget::generateZoomSlider() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + QSlider *slider = new QSlider(Qt::Orientation::Horizontal); + + // 1. Init + if (d->m_map->isReady()) + { + // This branch is taken in case of FunctionDialog: + onZoomSliderInit(slider); + } + + // 2. extentChanged + // Attention: use identical signal at any other zoom/scale-Slider!!! + connect(d->m_map, &Map::extentChangedDelayed, this, &LayerControlWidget::onMapExtentChanged); + + // 3. valueChanged + connect(slider, &QSlider::valueChanged, this, &LayerControlWidget::onZoomSliderValueChanged); + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; + return slider; +} + + + +QSlider *LayerControlWidget::generateRotationSlider() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + QSlider *slider = new QSlider(); + slider->setToolTip(tr("Rotation")); + slider->setOrientation(Qt::Orientation::Horizontal); + slider->setTickPosition(QSlider::TicksBelow); + slider->setTickInterval(10); + slider->setSingleStep(10); + slider->setMinimum(0); + slider->setMaximum(359); + slider->setValue(0); + + if (d->m_map->isReady()) + slider->setValue(d->m_map->rotation()); + + connect(slider, &QSlider::valueChanged, this, &LayerControlWidget::onRotationSliderValueChanged); + connect(d->m_map, &Map::rotationChanged, this, &LayerControlWidget::onRotationOfMapChanged); + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; + return slider; +} + +QSlider *LayerControlWidget::generateSymbolSizeSlider() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + QSlider *slider = new QSlider(); + slider->setToolTip(tr("Symbol Size")); + slider->setOrientation(Qt::Orientation::Horizontal); + slider->setTickPosition(QSlider::TicksBelow); + slider->setTickInterval(10); // 10% + slider->setMinimum(SituationMap::symbolScaleMinimum * 100); // 10% + slider->setMaximum(SituationMap::symbolScaleMaximum * 100); // 200% + slider->setSingleStep(10); // 10% + + if (d->m_map->isReady()) + slider->setValue(d->m_map->symbolScale() * 100); + + connect(slider, &QSlider::valueChanged, this, &LayerControlWidget::onSymbolSizeSliderValueChanged); + connect(d->m_map, &Map::symbolScaleChanged, this, &LayerControlWidget::onSymbolSizeOfMapChanged); + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; + return slider; +} + + + +void LayerControlWidget::onZoomSliderInit(QSlider *slider) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + + if (! slider) + return; + + const QSignalBlocker blocker(slider); + + + slider->setToolTip(tr("Zoom")); + slider->setMinimum(sqrt(d->m_map->maxScale())); + slider->setMaximum(sqrt(d->m_map->minScale())); + onMapExtentChanged(); + + if (slider->minimum() == slider->maximum()) + { + WARNING_LOG(m_logger) << LOGFUNCTION << " min/max value are identical, debugInfo='" << d->m_debugInfo << "'" + << " minimum=" << slider->minimum() + << " maximum=" << slider->maximum() + << " value=" << slider->value(); + + slider->setMinimum(sqrt(500)); + slider->setMaximum(sqrt(100000)); + + WARNING_LOG(m_logger) << LOGFUNCTION << " min/max value are identical, debugInfo='" << d->m_debugInfo << "'" + << " minimum=" << slider->minimum() + << " maximum=" << slider->maximum() + << " value=" << slider->value(); + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" + << " minimum=" << slider->minimum() + << " maximum=" << slider->maximum() + << " value=" << slider->value(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; +} + +void LayerControlWidget::onZoomSliderValueChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + + QSlider *slider = qobject_cast(sender()); + if (! slider) + return; + + if (d->m_map->isReady()) + d->m_map->setScale(slider->value() * slider->value()); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" + << " new target value: " << slider->value() * slider->value(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; +} + +void LayerControlWidget::onRotationSliderValueChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START" + << " rawValue = " << d->m_rotationSlider->value(); + + if (d->m_map->isReady() +// && slider->isSliderDown() + ) + { +// ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " push new value " +// << " slider= " << slider +// << slider->value(); + d->m_map->setRotation(d->m_rotationSlider->value()); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; +} + +void LayerControlWidget::onRotationOfMapChanged(double rotation) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START" << " rotation " << rotation; + + if (! d->m_rotationSlider) + return; + + if (d->m_map->isReady() && !d->m_rotationSlider->isSliderDown()) + { + const QSignalBlocker blocker(d->m_rotationSlider); + d->m_rotationSlider->setValue(rotation); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; +} + + + + + +void LayerControlWidget::onSymbolSizeSliderValueChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START" + << " rawValue = " << d->m_symbolSizeSlider->value(); + + if (!d->m_symbolSizeTimer) + { + d->m_symbolSizeTimer = new QTimer(this); + d->m_symbolSizeTimer->setSingleShot(true); + d->m_symbolSizeTimer->setInterval(500); // msec + connect(d->m_symbolSizeTimer, &QTimer::timeout, this, &LayerControlWidget::onSymbolSizeSliderValueChangedTimeout); + } + + d->m_symbolSizeTimer->start(); // start -> restart + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; +} + +void LayerControlWidget::onSymbolSizeSliderValueChangedTimeout() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START" + << " rawValue = " << d->m_symbolSizeSlider->value(); + + double symSize = d->m_symbolSizeSlider->value() / 100.0; + d->m_map->setSymbolScale(symSize); + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; +} + +void LayerControlWidget::onSymbolSizeOfMapChanged(double symbolSize) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START" << " symbolSize " << symbolSize; + + if (! d->m_symbolSizeSlider) + return; + + int symSizeInt = symbolSize * 100; + const QSignalBlocker blocker(d->m_symbolSizeSlider); + d->m_symbolSizeSlider->setValue(symSizeInt); + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; +} + + + +void LayerControlWidget::onBaseMapSelection() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + + QObject *pb = qobject_cast(sender()); + if (! pb) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " cast failed"; + return; + } + + // check/uncheck all: + for (auto widget : std::as_const(d->m_baseMapSelectIcons)) + widget->setChecked(widget == pb); + + + LayerFactory &layFac = d->m_map->layerFactory(); + const QStringList &basemapLayernames = layFac.layernames(LayerType::Basemap); + + const QString &curName = pb->property(LayernameProperty).toString(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " sender=" << curName; + for (const QString &name : basemapLayernames) + { + bool isCur = name.compare(curName) == 0; + LayerParameter lp = layFac.layerParameter(name); + if (isCur) + lp.resetView(); + lp.visible = isCur; + layFac.setLayerParameter(name, lp); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; +} + +void LayerControlWidget::onMapReady() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + onZoomSliderInit(d->m_zoomSlider); + onRotationOfMapChanged(d->m_map->rotation()); + onSymbolSizeOfMapChanged(d->m_map->symbolScale()); + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; +} + +void LayerControlWidget::onMapExtentChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + + if (d->m_zoomSlider && d->m_zoomSlider->isEnabled() && d->m_map->isReady() && (!d->m_zoomSlider->isSliderDown())) + { + const QSignalBlocker blocker(d->m_zoomSlider); + + float newVal = sqrt(d->m_map->scale()); + d->m_zoomSlider->setValue(newVal); + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" + << " d->m_map->scale(): " << d->m_map->scale() + << "; newVal: " << newVal + << "; value set to: " << d->m_zoomSlider->value(); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; +} + + + + +void LayerControlWidget::layerOnOff() +{ + QCheckBox *cb = qobject_cast(sender()); + if (!cb) + return; + + QString layername = cb->property(LayernameProperty).toString(); + + // get LayerParameter, update data and set: + LayerParameter lp = d->m_map->layerFactory().layerParameter(layername); + lp.visible = cb->isChecked(); + d->m_map->layerFactory().setLayerParameter(layername, lp); +} + +void LayerControlWidget::opacity(int) +{ + mySlider *sl = qobject_cast(sender()); + if (!sl) + return; + + QString layername = sl->m_layername; + + LayerParameter lp = d->m_map->layerFactory().layerParameter(layername); + lp.opacity = sl->value(); + d->m_map->layerFactory().setLayerParameter(layername, lp); + +} + +void LayerControlWidget::brightness(int) +{ + mySlider *sl = qobject_cast(sender()); + if (!sl) + return; + + QString layername = sl->m_layername; + + LayerParameter lp = d->m_map->layerFactory().layerParameter(layername); + lp.brightness = sl->value(); + d->m_map->layerFactory().setLayerParameter(layername, lp); +} + +void LayerControlWidget::contrast(int) +{ + mySlider *sl = qobject_cast(sender()); + if (!sl) + return; + + QString layername = sl->m_layername; + + LayerParameter lp = d->m_map->layerFactory().layerParameter(layername); + lp.contrast = sl->value(); + d->m_map->layerFactory().setLayerParameter(layername, lp); +} + +void LayerControlWidget::gamma(int) +{ + mySlider *sl = qobject_cast(sender()); + if (!sl) + return; + + QString layername = sl->m_layername; + + LayerParameter lp = d->m_map->layerFactory().layerParameter(layername); + lp.gamma = sl->value(); + d->m_map->layerFactory().setLayerParameter(layername, lp); +} + +void LayerControlWidget::onLayerParamterChanged(const QString &layername, const LayerParameter &newLayerParameter) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START" + << " layername= '" << layername << "'"; + +// INFO_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" +// << " layername=" << layername +// << " lp=" << newLayerParameter; + + if (! d->m_sliderMap.contains(layername)) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" + << " layername='" << layername << "' is new -> updatePanel"; + updatePanelLayer(); + return; + } + + // refresh GUI: + if (d->m_sliderMap.contains(layername)) + { + SliderStruct &data = d->m_sliderMap[layername]; + + if (data.cbOnOff) data.cbOnOff ->setChecked(newLayerParameter.visible); + if (data.sl_opacity) data.sl_opacity ->setValue(newLayerParameter.opacity); + if (data.sl_brightness) data.sl_brightness->setValue(newLayerParameter.brightness); + if (data.sl_contrast) data.sl_contrast ->setValue(newLayerParameter.contrast); + if (data.sl_gamma) data.sl_gamma ->setValue(newLayerParameter.gamma); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; +} + + +QString LayerControlWidget::layernameAt(const QPoint &pos) const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + QWidget *child = childAt(pos); + if (!child) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " no child"; + return QString(); + } + + DragableCheckBox *cb = dynamic_cast(child); + if (!cb) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " dynacast failed"; + return QString(); + } + + const QString &childLayername = cb->property(LayernameProperty).toString(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " childLayername=" << childLayername; + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; + return childLayername; +} + +QStringList LayerControlWidget::newLayerOrder(const QString &draggedLayername, const QString &childLayername) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + + // get current order: + QStringList sl = d->m_map->layerFactory().layernames(); + + if (draggedLayername == childLayername) + return sl; + + // remove + sl.removeOne(draggedLayername); + + // find new position: + int idx = sl.indexOf(childLayername); + + // insert AFTER; + sl.insert(++idx, draggedLayername); + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; + return sl; +} + + + + + + +void LayerControlWidget::dragMoveEvent(QDragMoveEvent *event) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + + if (event->mimeData()->hasFormat(MIMETYPE)) + { + if (event->source() == this) + { + event->setDropAction(Qt::MoveAction); + event->accept(); + } + else + { + + QByteArray itemData = event->mimeData()->data(MIMETYPE); + QDataStream dataStream(&itemData, QIODevice::ReadOnly); + + QString draggedLayername; + dataStream >> draggedLayername; + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " draggedLayername=" << draggedLayername; + + const QString &childLayername = layernameAt(event->pos()); + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " childLayername=" << childLayername; + + if (childLayername.isEmpty() || + (draggedLayername == childLayername)) + event->ignore(); + else + + { + const QStringList &newOrder = newLayerOrder(draggedLayername, childLayername); + + bool ok = d->m_map->layerFactory().checkLayerOrder(newOrder); + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " layerOrder ok: " << ok; + + if (ok) + event->acceptProposedAction(); + else + event->ignore(); + } + + } + } + else + event->ignore(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; +} + + + +void LayerControlWidget::dragEnterEvent(QDragEnterEvent *event) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + + if (event->mimeData()->hasFormat(MIMETYPE)) + { + + event->acceptProposedAction(); + +// if (event->source() != this) { +//// event->setDropAction(Qt::MoveAction); +// event->accept(); +// } else { +// event->acceptProposedAction(); +// } + } + else + event->ignore(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; +} + +void LayerControlWidget::dropEvent(QDropEvent *event) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " START"; + + if (event->mimeData()->hasFormat(MIMETYPE)) + { + QByteArray itemData = event->mimeData()->data(MIMETYPE); + QDataStream dataStream(&itemData, QIODevice::ReadOnly); + + QString draggedLayername; + dataStream >> draggedLayername; + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " draggedLayername=" << draggedLayername; + + + if (event->source() == this) + { + event->setDropAction(Qt::MoveAction); + event->accept(); + } + else + { + event->acceptProposedAction(); + + const QString &childLayername = layernameAt(event->pos()); + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " childLayername=" << childLayername; + + if (draggedLayername == childLayername) + return; + + const QStringList &newOrder = newLayerOrder(draggedLayername, childLayername); + + bool ok = d->m_map->layerFactory().orderLayerTo(newOrder); + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " layerOrder ok: " << ok; + } + } + else + event->ignore(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << d->m_debugInfo << "'" << " END"; +} + + + +} // namespace SituationMap + +#include "layer_control_widget.moc" diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/layer_control_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/layer_control_widget.h new file mode 100755 index 000000000..e7974d96d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/layer_control_widget.h @@ -0,0 +1,125 @@ +/** + * @RDE_HEADER@ + * Interface + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "situation_map_export.h" +#include + + +// Qt +#include + + + +//---------------------------------------------------------------------------- +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 + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/layer_factory_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/layer_factory_impl.cpp new file mode 100755 index 000000000..d33dbe2ca --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/layer_factory_impl.cpp @@ -0,0 +1,3049 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of LayerFactory + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "layer_factory_impl.h" +#include "map_impl.h" +#include "abstract_map_object_impl.h" +#include +#include "logical_layer.h" +#include "location_to_elevation_helper.h" +//#include "tms_layer.h" +#include // for getResourcePath +#include + +#include + +// ESRI +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// Qt +#include +#include +#include +#include +#include +#include +#include + +#include + +// FORCE_CREATION_GRAPHICSOVERLAY : +// N.Thoden, 10.02.2017 +// Esri Case #01887547 - [BOS] Dictionary Symbol crasches +// deals with order of creation of GraphicalOverlay and adding them to MapView. +// Although the reordering of the GraphicalOverlay is not included in the case, +// it seems to be relevant in this application. +// To find a quick solution, GraphicalOverlays are created as soon as possible +// (EsriDict must be loaded). +// +static bool s_forceCreationOfGraphicsOverlay = true; +// : FORCE_CREATION_GRAPHICSOVERLAY + + +namespace SituationMap +{ + +static const QLatin1String LayerNameJumpPoints("JumpPoints"); +static const QLatin1String LayerNameRubberband("RubberbandLayer"); +static const QLatin1String LayerNameTools("Tools"); +static const QLatin1String LayerNameSnapshotOverlay("SnaphotOverlay"); +static const QLatin1String LayerNameEmergencyCall("EmergencyCall"); + +// #define USE_ARCGIS_SCENE_LAYER_CACHE +#ifdef USE_ARCGIS_SCENE_LAYER_CACHE + +class ArcGISSceneLayerCache; +static ArcGISSceneLayerCache *s_arcGisSceneLayerCache = nullptr; + +/*! + * \brief The ArcGISSceneLayerCache class reduces the number of loading processes + * We are caching here to avoid numerous loading and unloading. + * At least at linux the ArcGISSceneLayerCache tends to alloc a hugh amount of data. + * Additionally allocation is influenced by the viewpoint choosen by the operator. + * And with other allocations (dds data, GBViewer of similar) at the same time + * the heap memory gets fragemented. + * So we try to reduce the number of loading processes. + */ +class ArcGISSceneLayerCache +{ +public: + static ArcGISSceneLayerCache *getInstance() + { + if (!s_arcGisSceneLayerCache) + { + s_arcGisSceneLayerCache = new ArcGISSceneLayerCache; + qAddPostRoutine(&ArcGISSceneLayerCache::cleanup); + } + return s_arcGisSceneLayerCache; + } + + struct Item + { + QPointer m_sceneLayer; + QPointer m_hostingScene; + }; + + Item& getFreeItemRef(const QString &itemName) + { + //Qt: If the map contains no item with key key, the function inserts a default-constructed value into the map with key key, and returns a reference to it. + QVector &vec = m_arcGISSceneLayerCache[itemName]; + + // search free item: + QMutableVectorIterator it(vec); + while (it.hasNext()) + { + Item& itemRef = it.next(); + if (itemRef.m_hostingScene.isNull()) + return itemRef; + } + + // Not found -> create: + vec.append(Item()); + return vec.last(); + } + + static void cleanup() + { + delete s_arcGisSceneLayerCache; + } + + ~ArcGISSceneLayerCache() + { + for (auto vec : m_arcGISSceneLayerCache) + { + for (auto item : vec) + { + std::cerr << " m_arcGISSceneLayerCache: delete " << item.m_sceneLayer << std::endl; + delete item.m_sceneLayer; // delete nullptr is ok + } + } + } + + void dump() + { + std::cerr << " ArcGISSceneLayer CacheSize = " << m_arcGISSceneLayerCache.size() << std::endl; + + for (auto vec : m_arcGISSceneLayerCache) + { + std::cerr << " ArcGISSceneLayer VectorSize: " << vec.size() << std::endl; + for (auto item : vec) + { + std::cerr << " ArcGISSceneLayer ITEM = " + << " m_hostingScene=" << item.m_hostingScene + << " m_sceneLayer=" << item.m_sceneLayer + << std::endl; + } // for + } // for + } + +private: + // slpk-name, .... + QMap> m_arcGISSceneLayerCache; +}; // class ArcGISSceneLayerCache + +#endif // USE_ARCGIS_SCENE_LAYER_CACHE + + +class LayerFactoryImpl::Private +{ +public: + Private() = default; + ~Private() = default; + + + // ESRI data: + Esri::ArcGISRuntime::Basemap *m_basemap = nullptr; + + Esri::ArcGISRuntime::GraphicsOverlayListModel *getEsriGraphicsOverlayListModel() + { + if (m_mapImpl && m_mapImpl->getEsriGeoView()) + return m_mapImpl->getEsriGeoView()->graphicsOverlays(); + else + return nullptr; + } + + + Esri::ArcGISRuntime::GraphicsOverlay *m_functionDialogGraphicsLayer = nullptr; + + + // Own data: + SituationMap::MapImpl *m_mapImpl = nullptr; + QStringList m_mapServiceUrlList; + QVector m_sceneObjectUrlCont; + Esri::ArcGISRuntime::Surface *m_baseSurface = nullptr; + bool m_isSceneObjectLayerVisible = true; + + QTimer *reCalculateMinMaxScaleTimer = nullptr; + + MapLayerOrderedList m_orderedMapLayer; // store order of basemap and logical Layernames + + bool hasLogicalLayer(const QString &logicalName) + { + return m_logicalLayerCont.contains(logicalName); + } + + // create and initalize LogicalLayer: + void createLogicalLayer(const QString &logicalName) + { + // LayerListModel + Esri::ArcGISRuntime::LayerListModel *layerListModel = nullptr; + if (m_basemap) + layerListModel = m_basemap->baseLayers(); + + // GraphicsOverlayListModel + Esri::ArcGISRuntime::GraphicsOverlayListModel *graphicsOverlayListModel = this->getEsriGraphicsOverlayListModel(); + +#ifdef LTS_DEBUG + if (!graphicsOverlayListModel) + qCritical() << " graphicsOverlayListModel" + << " logicalName='" << logicalName << "'" + << " m_mapImpl=" << m_mapImpl + << " m_mapImpl->getEsriGeoView()=" << m_mapImpl->getEsriGeoView() + << " m_mapImpl->isReady=" << m_mapImpl->isReady() + << " graphicsOverlayListModel=" << graphicsOverlayListModel; + Q_ASSERT(graphicsOverlayListModel); +#endif // LTS_DEBUG + + QSharedPointer ll = QSharedPointer(new LogicalLayer(logicalName)); + ll->init(m_mapImpl, layerListModel, graphicsOverlayListModel); + m_logicalLayerCont[logicalName] = ll; + } + + QSharedPointer getLogicalLayer(const QString &logicalName) const + { + if (m_logicalLayerCont.contains(logicalName)) + return m_logicalLayerCont[logicalName]; + return QSharedPointer(); + } + void removeLogicalLayer(const QString &logicalName) + { + m_logicalLayerCont.remove(logicalName); + } + + QMap> m_logicalLayerCont; // store logical layer without order + + QString m_basemapLayernamePrefix = QLatin1String("ArcGISTiledLayer_URL_"); + + QString m_participantLayername = QLatin1String("Participants"); + RenderingMode m_participantLayerRenderingMode = RenderingMode::Dynamic; + SurfacePlacement m_participantLayerSurfacePlacement = SurfacePlacement::Relative; + + + QString m_logicalLayerNameForCubes; + +#if ACRGIS_VERSION < 0x640500 // 100.5.0 / Update 5 + QColor m_selectionColor = Qt::magenta; +#endif // ACRGIS_VERSION + + +}; // class LayerFactoryImpl::Private +//----------------------------------------------------------------------------- + +// +// class LayerFactoryImpl; +// +LayerFactoryImpl::LayerFactoryImpl(QObject *parent, SituationMap::MapImpl *mapImpl) + : LayerFactory() + , BaseClass(parent) + , m_logger(getLogger("LayerFactoryImpl")) + , d(new Private) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + d->m_mapImpl = mapImpl; + if (d->m_mapImpl) + d->m_basemap = d->m_mapImpl->getEsriBasemap(); + + d->reCalculateMinMaxScaleTimer = new QTimer(this); + d->reCalculateMinMaxScaleTimer->setInterval(500); + d->reCalculateMinMaxScaleTimer->setSingleShot(true); + connect(d->reCalculateMinMaxScaleTimer, &QTimer::timeout, + this, &LayerFactoryImpl::reCalculateMinMaxScaleBasedOnLayers); + + INFO_LOG(m_logger) << LOGFUNCTION << " Resource Path = '" << SituationMap::getResourcePath() << "'"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +LayerFactoryImpl::~LayerFactoryImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete d; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +bool LayerFactoryImpl::initializeBasemapBuiltIn(const QString &tpkFile) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " tpkFile='" << tpkFile << "'"; + + QString tiledBaseMapLayer; + if (tpkFile.isEmpty()) + tiledBaseMapLayer = SituationMap::getResourcePath() + QLatin1String("Topographic.tpk"); + else + { + QFileInfo fi(tpkFile); + if (fi.isAbsolute()) + tiledBaseMapLayer = tpkFile; + else + { + tiledBaseMapLayer = SituationMap::getResourcePath() + tpkFile; + if (fi.suffix().isEmpty()) + tiledBaseMapLayer += QLatin1String(".tpk"); + } + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " tiledBaseMapLayer='" << tiledBaseMapLayer << "'"; + + //----------------------------------- + // Local Tiled Basemap Layer using: sdk/samples/data/tpks/Topographic.tpk + //----------------------------------- + Esri::ArcGISRuntime::TileCache *tpkCache = new Esri::ArcGISRuntime::TileCache(tiledBaseMapLayer, this); + tpkCache->setObjectName(QLatin1String("TPK-tpkCache")); + + Esri::ArcGISRuntime::ArcGISTiledLayer *tiledLayer = new Esri::ArcGISRuntime::ArcGISTiledLayer(tpkCache, this); + tiledLayer->setName(QLatin1String("TPK-tiledLayer")); + + + // loadStatusChanged -> + connect(tpkCache, &Esri::ArcGISRuntime::TileCache::loadStatusChanged, this, [this](Esri::ArcGISRuntime::LoadStatus loadStatus) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " TileCache: " << "loadStatus changed to: " << loadStatus; + }); + connect(tiledLayer, &Esri::ArcGISRuntime::ArcGISTiledLayer::loadStatusChanged, this, [this](Esri::ArcGISRuntime::LoadStatus loadStatus) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " ArcGISTiledLayer: " << "loadStatus changed to: " << loadStatus; + }); + + + QString logicalLayerName = QStringLiteral("%1%2").arg(d->m_basemapLayernamePrefix).arg(0); + tiledLayer->setName(logicalLayerName); + tiledLayer->setObjectName(tiledLayer->name()); + + QSharedPointer logicalLayer = addLayerToMgnt(logicalLayerName, LayerType::Basemap); + if (logicalLayer.isNull()) + ERROR_LOG(m_logger) << LOGFUNCTION << " getLogicalLayer invalid'" << logicalLayerName << "'"; + else + { + // update LayerParameter first: + LayerParameter lp = logicalLayer->layerParameter(); + lp.url = QUrl(tiledBaseMapLayer); + lp.displayName = logicalLayerName; + logicalLayer->setLayerParameter(lp); + + logicalLayer->setLayer(tiledLayer); + logicalLayer->setLayerType(LayerType::Basemap); + logicalLayer->setBasemapType(BasemapType::Topographic); // basemapType -> aerial, topo... + } + + + int basemapCount = -1; + if (d->m_basemap->baseLayers()) + basemapCount = d->m_basemap->baseLayers()->size(); + + INFO_LOG(m_logger) << LOGFUNCTION << " basemapCount = " << basemapCount; + + d->m_basemap->baseLayers()->append(tiledLayer); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +bool LayerFactoryImpl::isUsingBaseMapBuiltIn() const +{ + return d->m_mapServiceUrlList.isEmpty(); +} + +bool LayerFactoryImpl::addOperationalLayer(const QString &logicalLayerName, + const QString &displayName, + RenderingMode renderMode, + SurfacePlacement surfacePlacement) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " logicalLayerName='" << logicalLayerName << "'" + << " displayName='" << displayName << "'" + << " renderMode=" << renderMode + << " surfacePlacement=" << surfacePlacement; + + if (s_forceCreationOfGraphicsOverlay && !logicalLayerName.startsWith(QLatin1String("Participant"), Qt::CaseInsensitive)) + { + // First, wait until esriDict is load, second force creation of esriLayer: + + // wait until esriDict is load: + AdvancedSymbology::SymbolDictionary &dict = d->m_mapImpl->symbolDictionary(); + AdvancedSymbology::SymbolDictionaryImpl &dictImpl = static_cast(dict); + + // keep SymbologyType::None in mind: + Esri::ArcGISRuntime::DictionarySymbolStyle *esriDict = const_cast(dictImpl.getEsriSymbolDictionary()); + + if (esriDict) + { + if (esriDict->loadStatus() != Esri::ArcGISRuntime::LoadStatus::Loaded) + { + QEventLoop loop; + connect(esriDict, &Esri::ArcGISRuntime::DictionarySymbolStyle::doneLoading, + &loop, &QEventLoop::quit); + QTimer::singleShot(2000, &loop, &QEventLoop::quit); + loop.exec(); + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " FORCE_CREATION_GRAPHICSOVERLAY: " + << " esriDict: loadStatus=" << esriDict->loadStatus(); + } + } // if (s_forceCreationOfGraphicsOverlay) + + const LayerType layerType = LayerType::Operational; + + if (! layerExistsInMngt(logicalLayerName, layerType)) + { + QSharedPointer logicalLayer = addLayerToMgnt(logicalLayerName, layerType); + if (logicalLayer.isNull()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " addLayer failed: '" + << logicalLayerName << "'"; + return false; + } + LayerParameter lp = logicalLayer->layerParameter(); + lp.displayName = displayName; + logicalLayer->setLayerParameter(lp); + logicalLayer->setRenderingMode(renderMode); + logicalLayer->setSurfacePlacement(surfacePlacement); + } + + + // + // the real Esri-Layers will be create later on demand :-) + // + + //#ifdef FORCE_CREATION_GRAPHICSOVERLAY + if (s_forceCreationOfGraphicsOverlay && !logicalLayerName.startsWith(QLatin1String("Participant"), Qt::CaseInsensitive)) + { + QSharedPointer logicalLayer = d->getLogicalLayer(logicalLayerName); + if (logicalLayer) + { + logicalLayer->getCreateGraphicalOverlay(TechnicalLayerType::GraphicsLayer); + if (d->m_mapImpl->symbologyType() != AdvancedSymbology::SymbologyType::None) + logicalLayer->getCreateGraphicalOverlay(TechnicalLayerType::GraphicsLayer_Dict); + DEBUG_LOG(m_logger) << LOGFUNCTION << " FORCE_CREATION_GRAPHICSOVERLAY"; + } + } // if (s_forceCreationOfGraphicsOverlay) + //#endif // FORCE_CREATION_GRAPHICSOVERLAY + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " logicalLayerName='" << logicalLayerName << "'" + << " success=" << true; + return true; +} + + +bool LayerFactoryImpl::removeOperationalLayer(const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " logicalLayerName='" << logicalLayerName << "'"; + + if (logicalLayerName.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName empty" + << logicalLayerName; + return false; + } + + const LayerType layerType = LayerType::Operational; + + if (! d->m_orderedMapLayer.contains(logicalLayerName, layerType)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName does not exist: '" + << logicalLayerName << "'"; + return false; + } + + if (removeLayerInternal(layerType, logicalLayerName)) + d->m_mapImpl->emitLayerOrderChanged(); + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " logicalLayerName='" << logicalLayerName << "'"; + return true; +} + +bool LayerFactoryImpl::hasOperationalLayer(const QString &logicalLayerName) const +{ + if (logicalLayerName.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName empty" + << logicalLayerName; + return false; + } + return layerExistsInMngt(logicalLayerName, LayerType::Operational); +} + + +bool LayerFactoryImpl::addOverlayLayer(const QString &logicalLayerName, const QString &displayName, bool isSnaphot) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " logicalLayerName='" << logicalLayerName << "'" + << " displayName='" << displayName << "'" + << " isSnaphot='" << isSnaphot; + + const LayerType layerType = (isSnaphot) ? LayerType::SnapshotOverlay : LayerType::Overlay; + + if (! layerExistsInMngt(logicalLayerName, layerType)) + { + QSharedPointer logicalLayer = addLayerToMgnt(logicalLayerName, layerType); + if (logicalLayer.isNull()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " addLayer failed: '" + << logicalLayerName << "'"; + return false; + } + // logicalLayer->setRenderingMode(RenderingMode::Dynamic); + } + + // update displayName: + QSharedPointer logicalLayer = d->getLogicalLayer(logicalLayerName); + if (logicalLayer.isNull()) + { + WARNING_LOG(m_logger) << LOGFUNCTION << " getLogicalLayer failed: '" + << logicalLayerName << "'"; + } else { + LayerParameter lp = logicalLayer->layerParameter(); + lp.displayName = displayName; + logicalLayer->setLayerParameter(lp); + } + + + // + // the real Esri-Layers will be create later on demand :-) + // + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +QString LayerFactoryImpl::technicalNameOfOverlayLayer(const QString &logicalLayerName) +{ + // By default, our overlays are displayed on just one GraphicsLayer: + return createInternalLayerName(TechnicalLayerType::GraphicsLayer, logicalLayerName); +} + + +QSharedPointer LayerFactoryImpl::addLayerToMgnt(const QString &logicalLayerName, const LayerType &layerType) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " layerType=" << layerType + << " logicalLayerName='" << logicalLayerName << "'"; + + QSharedPointer logicalLayer; + + if (logicalLayerName.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName empty" + << logicalLayerName; + return logicalLayer; + } + if (d->m_orderedMapLayer.contains(logicalLayerName, LayerType::Undefined)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName already exists: '" + << logicalLayerName << "'"; + return logicalLayer; + } + + // 1. add to orderedList: + + if (! d->m_orderedMapLayer.addLayer(logicalLayerName, layerType)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " addLayer failed: '" + << logicalLayerName << "'" + << " layerType=" << layerType; + return logicalLayer; + } + + // getLogicalLayer will + // - initialize + // - do errorHandling + // - update LayerParameter on doneLoading + // - and add layer to LayerlistModel of basemap: + logicalLayer = this->getCreateLogicalLayer(logicalLayerName); + if (logicalLayer.isNull()) + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " getLogicalLayer invalid'" << logicalLayerName << "'"; + return logicalLayer; + } + logicalLayer->setLayerType(layerType); + + + DEBUG_LOG(m_logger) << LOGFUNCTION << " handleEmergencySpecials ..."; + handleEmergencyCallSpecials(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " handleEmergencySpecials ...DONE"; + + // trigger gui update: + d->m_mapImpl->emitLayerOrderChanged(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " layerType=" << layerType + << " logicalLayerName='" << logicalLayerName << "'"; + return logicalLayer; +} + +bool LayerFactoryImpl::layerExistsInMngt(const QString &logicalLayerName, const LayerType &layerType) const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " layerType=" << layerType + << " logicalLayerName='" << logicalLayerName << "'"; + + bool exists = d->m_orderedMapLayer.contains(logicalLayerName, layerType); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " layerType=" << layerType + << " logicalLayerName='" << logicalLayerName << "'" + << " exists=" << exists; + return exists; +} + +bool LayerFactoryImpl::removeOverlayLayer(const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " layername='" << logicalLayerName << "'"; + + if (logicalLayerName.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName empty" + << logicalLayerName; + return false; + } + const MapLayer &mapLayer = d->m_orderedMapLayer.getLayer(logicalLayerName); + if (!mapLayer.isValid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName does not exist: '" + << logicalLayerName << "'"; + return false; + } + + const LayerType type = mapLayer.type(); + const bool isOverlayLayer = (type == LayerType::Overlay) || (type == LayerType::SnapshotOverlay); + if (!isOverlayLayer) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName has wrong type: '" + << logicalLayerName << "'" + << " type = " << type; + return false; + } + + if (removeLayerInternal(type, logicalLayerName)) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " remove..." + << " layername='" << logicalLayerName << "'"; + d->m_mapImpl->emitLayerOrderChanged(); + } + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " logicalLayerName='" << logicalLayerName << "'"; + return true; +} + +QString LayerFactoryImpl::snapshotOverlayLayername() const +{ + return LayerNameSnapshotOverlay; +} + +QString LayerFactoryImpl::layernameForCubes() const +{ + return d->m_logicalLayerNameForCubes; +} + +void LayerFactoryImpl::setLayernameForCubes(const QString &logicalName) +{ + d->m_logicalLayerNameForCubes = logicalName; +} + +bool LayerFactoryImpl::hasLayernameForCubes() const +{ + return !d->m_logicalLayerNameForCubes.isEmpty(); +} + + +bool LayerFactoryImpl::assignUniqueValueRendererForCubes() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " m_logicalLayerNameForCubes='" << d->m_logicalLayerNameForCubes << "'"; + + if (d->m_mapImpl->mode() != Mode::Mode_3D) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " not in 3Dmode"; + return false; + } + + if (d->m_logicalLayerNameForCubes.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no m_logicalLayerNameForCubes configured"; + return false; + } + + + + Esri::ArcGISRuntime::GraphicsOverlay *graphicsOverlay = this->getGraphicsLayer(d->m_logicalLayerNameForCubes); + if (!graphicsOverlay) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " graphicsOverlay not found, d->m_logicalLayerNameForCubes='" << d->m_logicalLayerNameForCubes << "'"; + return false; + } + + + const PartyIdToColorFunctor::ColorMap &colorMap = d->m_mapImpl->partyIdToColorFunctor().colorMap(); + if (colorMap.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " colorMap is empty"; + return false; + } + + + + // prepare UniqueValues + Esri::ArcGISRuntime::SimpleFillSymbol *sls; + QList uvList; + + QObject *parent = graphicsOverlay; + + for (const PartyIdToColorFunctor::PartyData &partyData : colorMap) + { + const QString &partyName = partyData.m_name; + const QColor &partyColor = partyData.m_color; + + INFO_LOG(m_logger) << LOGFUNCTION + << " partyName=" << partyName << "'" + << " partyColor=" << partyColor.name() << "'"; + + sls = new Esri::ArcGISRuntime::SimpleFillSymbol(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, partyColor, parent); + + Esri::ArcGISRuntime::UniqueValue *uv = + new Esri::ArcGISRuntime::UniqueValue(partyName, // more or less relevant + QStringLiteral("Party '%1'").arg(partyName), + QVariantList() << partyColor.name(), // (was in former times: partyName) + sls, parent); + uvList.append(uv); + } // for + + //qt build in + // static QVector addColors = {Qt::darkGray, Qt::gray, Qt::lightGray, + // Qt::red, Qt::green, Qt::blue, + // Qt::cyan, Qt::magenta, Qt::yellow, + // Qt::darkRed, Qt::darkGreen, Qt::darkBlue, + // Qt::darkCyan, Qt::darkMagenta, Qt::darkYellow, Qt::white}; + static QVector addColors = { + Qt::yellow + }; + uvList.reserve(uvList.size() + addColors.size()); + + for (const QColor &addColor : std::as_const(addColors)) + { + sls = new Esri::ArcGISRuntime::SimpleFillSymbol(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, addColor.name(), parent); + + Esri::ArcGISRuntime::UniqueValue *uv = + new Esri::ArcGISRuntime::UniqueValue(addColor.name(), // more or less relevant + QStringLiteral("Party '%1'").arg(addColor.name()), + QVariantList() << addColor.name(), // (was in former times: partyName) + sls, parent); + uvList.append(uv); + } //for + + // setup UniqueValueRenderer: + + // setup default: + sls = new Esri::ArcGISRuntime::SimpleFillSymbol(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, Qt::white, this); + + + Esri::ArcGISRuntime::UniqueValueRenderer *uniqueRenderer = + new Esri::ArcGISRuntime::UniqueValueRenderer(QStringLiteral("UnknownParty"), + sls, // default + QStringList() << QStringLiteral("Extrusion_Color"), // fieldNames (was in former times: "PartyName") + uvList, parent); + static long s_counter = 0; + uniqueRenderer->setObjectName(QStringLiteral("UniqueValueRenderer_ForCubes_%1").arg(s_counter++)); + connect(uniqueRenderer, &Esri::ArcGISRuntime::UniqueValueRenderer::errorOccurred, this, [this] (const Esri::ArcGISRuntime::Error &error) + { + FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- assignUniqueValueRendererForCubes occured: "; + FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; + }); + connect(uniqueRenderer, &Esri::ArcGISRuntime::UniqueValueRenderer::destroyed, this, [this, uniqueRenderer] () { + INFO_LOG(m_logger) << " UniqueValueRenderer is destroyed , objectName='" << uniqueRenderer->objectName() << "'"; + }); + + + // set sceneProperties: + Esri::ArcGISRuntime::RendererSceneProperties props = uniqueRenderer->sceneProperties(); + props.setExtrusionMode(Esri::ArcGISRuntime::ExtrusionMode::AbsoluteHeight); // see BuildingOwnership::retrieveBuildingAbsHeight + props.setExtrusionExpression(QStringLiteral("[height]")); // <-- keyword + uniqueRenderer->setSceneProperties(props); + + + // take care that the old one is deleted: + Esri::ArcGISRuntime::Renderer *oldRenderer = graphicsOverlay->renderer(); + delete oldRenderer; // delete nullptr is safe! + + // assign new: + graphicsOverlay->setRenderer(uniqueRenderer); + + return true; +} + +bool LayerFactoryImpl::assignUniqueValueRenderer(const QString &layername, + const QString &fieldName, + const QVector &uvDataCont) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, layername='" << layername << "'" + << ", fieldName='" << fieldName << "'" + << ", uvDataCont.size()=" << uvDataCont.size(); + + // ensure existence: + getCreateLogicalLayer(layername); + + Esri::ArcGISRuntime::GraphicsOverlay *graphicsOverlay = this->getGraphicsLayer(layername); + if (!graphicsOverlay) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " graphicsOverlay not found, layername='" << layername << "'"; + return false; + } + + + // prepare UniqueValues + Esri::ArcGISRuntime::PictureMarkerSymbol *pms; + QList uvList; + QObject *parent = graphicsOverlay; + + for (const UniqueValueData &uvData : std::as_const(uvDataCont)) + { + INFO_LOG(m_logger) << LOGFUNCTION + << " uvData.value=" << uvData.value << "'"; + + pms = new Esri::ArcGISRuntime::PictureMarkerSymbol(uvData.img, parent); + + Esri::ArcGISRuntime::UniqueValue *uv = + new Esri::ArcGISRuntime::UniqueValue(uvData.value, + QStringLiteral("GenericUniqueValue '%1'").arg(uvData.value), + QVariantList() << uvData.value, pms, parent); + uvList.append(uv); + } // for + + // setup default: + Esri::ArcGISRuntime::SimpleMarkerSymbol *sms; + sms = new Esri::ArcGISRuntime::SimpleMarkerSymbol(Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::X, Qt::white, 5, parent); + + // Finally, setup Renderer: + Esri::ArcGISRuntime::UniqueValueRenderer* uniqueRenderer = + new Esri::ArcGISRuntime::UniqueValueRenderer(QStringLiteral("GenericUniqueValueRenderer"), + sms, // default + QStringList() << fieldName, + uvList, parent); + static long s_counter = 0; + uniqueRenderer->setObjectName(QStringLiteral("UniqueValueRenderer_assignUniqueValueRenderer_%1").arg(s_counter++)); + connect(uniqueRenderer, &Esri::ArcGISRuntime::UniqueValueRenderer::errorOccurred, this, [this] (const Esri::ArcGISRuntime::Error &error) + { + FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- assignUniqueValueRenderer occured: "; + FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; + }); + connect(uniqueRenderer, &Esri::ArcGISRuntime::UniqueValueRenderer::destroyed, this, [this, uniqueRenderer] () { + INFO_LOG(m_logger) << " UniqueValueRenderer is destroyed , objectName='" << uniqueRenderer->objectName() << "'"; + }); + + // take care that the old one is deleted: + Esri::ArcGISRuntime::Renderer *oldRenderer = graphicsOverlay->renderer(); + delete oldRenderer; // delete nullptr is safe! + + // assign new: + graphicsOverlay->setRenderer(uniqueRenderer); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, layername='" << layername << "'" + << ", fieldName='" << fieldName << "'" + << ", uvDataCont.size()=" << uvDataCont.size(); + return true; +} + +bool LayerFactoryImpl::assignUniqueValueRendererAesCentral(const QString &layername, + const QStringList &fieldNames, + const QVector &uvDataCont) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, layername='" << layername << "'" + << ", fieldNames='" << fieldNames.join(QStringLiteral(";")) << "'" + << ", uvDataCont.size()=" << uvDataCont.size(); + + // ensure existence: + getCreateLogicalLayer(layername); + + Esri::ArcGISRuntime::GraphicsOverlay *graphicsOverlay = this->getGraphicsLayer(layername); + if (!graphicsOverlay) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " graphicsOverlay not found, layername='" << layername << "'"; + return false; + } + + // prepare UniqueValues + QList uvList; + QObject *parent = graphicsOverlay; + for (const UniqueValueDataAesCentral &uvData : std::as_const(uvDataCont)) + { + INFO_LOG(m_logger) << LOGFUNCTION +// << " uvData.value=" << uvData.values << "'" + << ", type " << toString(uvData.type) + << ", col " << uvData.col + << ", col.name " << uvData.col.name() + << ", uvData.radius " << uvData.radius; + Esri::ArcGISRuntime::SimpleMarkerSceneSymbol *symbol = nullptr; + + switch (uvData.type) { + case UniqueValueDataAesCentral::TYPE::CONE: + symbol = Esri::ArcGISRuntime::SimpleMarkerSceneSymbol::cone(uvData.col, + // extent should be similar to sphere + uvData.radius, // V100.8: cone: diameter is interpreted as radius + uvData.height, + parent); + symbol->setAnchorPosition(Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Top); + break; + case UniqueValueDataAesCentral::TYPE::SPHERE: + symbol = Esri::ArcGISRuntime::SimpleMarkerSceneSymbol::sphere(uvData.col, + uvData.radius*2.0, // diameter + parent); + symbol->setAnchorPosition(Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Center); + default: + break; + } + + Esri::ArcGISRuntime::UniqueValue *uv = + new Esri::ArcGISRuntime::UniqueValue(QStringLiteral("uvData.value"), + QStringLiteral("AesCentralUniqueValue '%%%1'"), + uvData.values, symbol, parent); + uvList.append(uv); + } // for + + // setup default: + Esri::ArcGISRuntime::SimpleMarkerSymbol *sms; + sms = new Esri::ArcGISRuntime::SimpleMarkerSymbol(Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::X, Qt::white, 25, parent); + + // Finally, setup Renderer: + Esri::ArcGISRuntime::UniqueValueRenderer* uniqueRenderer = + new Esri::ArcGISRuntime::UniqueValueRenderer(QStringLiteral("AesCentralUniqueValueRenderer"), + sms, // default + fieldNames, + uvList, parent); + static long s_counter = 0; + uniqueRenderer->setObjectName(QStringLiteral("UniqueValueRenderer_assignUniqueValueRendererAesCentral_%1").arg(s_counter++)); + connect(uniqueRenderer, &Esri::ArcGISRuntime::UniqueValueRenderer::errorOccurred, this, [this] (const Esri::ArcGISRuntime::Error &error) + { + FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- UniqueValueRendererAesCentral occured: "; + FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; + }); + connect(uniqueRenderer, &Esri::ArcGISRuntime::UniqueValueRenderer::destroyed, this, [this, uniqueRenderer] () { + INFO_LOG(m_logger) << " UniqueValueRenderer is destroyed , objectName='" << uniqueRenderer->objectName() << "'"; + }); + + // setup SceneProperties + Esri::ArcGISRuntime::RendererSceneProperties sceneProps; + sceneProps.setHeadingExpression(QStringLiteral("[HEADING]")); // Expression with "[]", later on without "[]" + sceneProps.setPitchExpression(QStringLiteral("[PITCH]")); // Expression with "[]", later on without "[]" + sceneProps.setRollExpression(QStringLiteral("[ROLL]")); // Expression with "[]", later on without "[]" + uniqueRenderer->setSceneProperties(sceneProps); + + // take care that the old one is deleted: + Esri::ArcGISRuntime::Renderer *oldRenderer = graphicsOverlay->renderer(); + delete oldRenderer; // delete nullptr is safe! + + // assign new: + graphicsOverlay->setRenderer(uniqueRenderer); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, layername='" << layername << "'" + << ", fieldNames='" << fieldNames.join(QStringLiteral(";")) << "'" + << ", uvDataCont.size()=" << uvDataCont.size(); + return true; +} + +bool LayerFactoryImpl::assignUniqueValueRendererAesCentral3DExtrusion(const QString &layername, + const QStringList &fieldNames, + const QVector &uvDataCont) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, layername='" << layername << "'" + << ", fieldNames='" << fieldNames.join(QStringLiteral(";")) << "'" + << ", uvDataCont.size()=" << uvDataCont.size(); + + // ensure existence: + getCreateLogicalLayer(layername); + + Esri::ArcGISRuntime::GraphicsOverlay *graphicsOverlay = this->getGraphicsLayer3DExtrusion(layername); + if (!graphicsOverlay) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " graphicsOverlay not found, layername='" << layername << "'"; + return false; + } + + // prepare UniqueValues + QList uvList; + QObject *parent = graphicsOverlay; + for (const UniqueValueDataAesCentral &uvData : std::as_const(uvDataCont)) + { + INFO_LOG(m_logger) << LOGFUNCTION +// << " uvData.value=" << uvData.values << "'" + << ", type " << toString(uvData.type) + << ", col " << uvData.col + << ", col.name " << uvData.col.name() + << ", uvData.radius " << uvData.radius; + + Esri::ArcGISRuntime::SimpleFillSymbol *symbol = + new Esri::ArcGISRuntime::SimpleFillSymbol(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, uvData.col, parent); + + Esri::ArcGISRuntime::UniqueValue *uv = + new Esri::ArcGISRuntime::UniqueValue(uvData.label, + QString(QStringLiteral("Description '%1'")).arg(uvData.col.name()), + uvData.values, symbol, parent); + uvList.append(uv); + } // for + + // setup default: + Esri::ArcGISRuntime::SimpleFillSymbol *defaultSymbol = + new Esri::ArcGISRuntime::SimpleFillSymbol(Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid, Qt::white, parent); + + // Finally, setup Renderer: + Esri::ArcGISRuntime::UniqueValueRenderer* uniqueRenderer = + new Esri::ArcGISRuntime::UniqueValueRenderer(QStringLiteral("assignUniqueValueRendererAesCentral3DUNKNOWN"), + defaultSymbol, // default + fieldNames, + uvList, parent); + static long s_counter = 0; + uniqueRenderer->setObjectName(QStringLiteral("UniqueValueRenderer_assignUniqueValueRendererAesCentral3D_%1").arg(s_counter++)); + connect(uniqueRenderer, &Esri::ArcGISRuntime::UniqueValueRenderer::errorOccurred, this, [this] (const Esri::ArcGISRuntime::Error &error) + { + FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- UniqueValueRendererAesCentral3DExtrusion occured: "; + FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; + }); + connect(uniqueRenderer, &Esri::ArcGISRuntime::UniqueValueRenderer::destroyed, this, [this, uniqueRenderer] () { + INFO_LOG(m_logger) << " UniqueValueRenderer is destroyed , objectName='" << uniqueRenderer->objectName() << "'"; + }); + + Esri::ArcGISRuntime::RendererSceneProperties props = uniqueRenderer->sceneProperties(); + props.setExtrusionMode(Esri::ArcGISRuntime::ExtrusionMode::BaseHeight); + props.setExtrusionExpression(QStringLiteral("[height]")); // <-- keyword + uniqueRenderer->setSceneProperties(props); + + // take care that the old one is deleted: + Esri::ArcGISRuntime::Renderer *oldRenderer = graphicsOverlay->renderer(); + delete oldRenderer; // delete nullptr is safe! + + // assign new: + graphicsOverlay->setRenderer(uniqueRenderer); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, layername='" << layername << "'" + << ", fieldNames='" << fieldNames.join(QStringLiteral(";")) << "'" + << ", uvDataCont.size()=" << uvDataCont.size(); + return true; +} + +bool LayerFactoryImpl::initializeUnitTestLayer() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + QUrl mapUrl(QLatin1String("http://DOES_NOT_EXISTS")); // wkid = 102100/3857 + + this->addBasemap(QLatin1String("DummyLayerName"), mapUrl, BasemapType::Aerial); + + //----------------------------------- + // Layer: + //----------------------------------- + + QStringList layerNames; + layerNames << QLatin1String("AES"); + layerNames << QLatin1String("Participants"); + bool success = true; + for (const QString &layername : std::as_const(layerNames)) + if (!addOperationalLayer(layername, layername)) + success = false; + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " initializeOperationalLayers failed"; + + // enableFunctionDialog(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return success; + +} + +bool LayerFactoryImpl::addBasemap(const QString &layername, + const QUrl &mapServiceUrl, + const BasemapType basemapType, + const double minScale, + const double maxScale, + const QString &displayName, + const QString &description, + const QImage &previewImage) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " layername='" << layername << "'" + << " mapServiceUrl='" << mapServiceUrl.toString() << "'" + << " basemapType='" << basemapType << "'" + << " minScale='" << minScale << "'" + << " maxScale='" << maxScale << "'" + << " displayName='" << displayName << "'" + << " description='" << description << "'" + << " previewImage.isNull()=" << previewImage.isNull() + << " wkid=" << d->m_mapImpl->getEsriSpatialReference().wkid(); + + bool success = addBasemapInternal(layername, mapServiceUrl, basemapType, + minScale, maxScale, + displayName, description); + + // reOrder + success = success && reOrderLayer(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " layername='" << layername << "'" + << " mapServiceUrl='" << mapServiceUrl.toString() << "'" + << " displayName='" << displayName << "'" + << " success =" << success; + return success; +} + +bool LayerFactoryImpl::removeBasemap(const QString &layername) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " layername='" << layername << "'"; + if (!d->m_orderedMapLayer.contains(layername, LayerType::Basemap)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " layername='" << layername << "' not found"; + return false; + } + + bool success = false; + + if (d->m_orderedMapLayer.removeLayer(layername) && d->m_basemap) + { + Esri::ArcGISRuntime::Layer *layer = this->getEsriBasemapLayer(layername); + if (layer && d->m_basemap->baseLayers()) + { + d->m_basemap->baseLayers()->removeOne(layer); + success = true; + INFO_LOG(m_logger) << LOGFUNCTION << " layer" + << " layername='" << layername << "' removed"; + + d->m_mapImpl->emitLayerOrderChanged(); + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " layername='" << layername << "' can`t be found"; + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " layername='" << layername << "' can`t be removed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " layername='" << layername << "'" + << " success='" << success << "'"; + return success; +} + +Esri::ArcGISRuntime::Envelope LayerFactoryImpl::fullExtentOfBasemaps(double margin_zeroToOne) const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + Esri::ArcGISRuntime::LayerListModel *model = d->m_basemap->baseLayers(); + if (!model || model->isEmpty()) + return Esri::ArcGISRuntime::Envelope(); + + double xmin = std::numeric_limits::max(); + double ymin = std::numeric_limits::max(); + double xmax = std::numeric_limits::lowest(); + double ymax = std::numeric_limits::lowest(); + + Esri::ArcGISRuntime::SpatialReference sr; + + for (int idx = 0; idx < model->size(); ++idx) + { + Esri::ArcGISRuntime::Layer *layer = model->at(idx); + // 12.12.2018, N. Thoden, V100.4: + // Do not call fullExtent if layer is not loaded. + // Envelope contains nan and will not changed on further calls... + if (!layer || (layer->loadStatus() != Esri::ArcGISRuntime::LoadStatus::Loaded)) + continue; + + const Esri::ArcGISRuntime::Envelope singleEnv = layer->fullExtent(); + + DEBUG_LOG(m_logger) << LOGFUNCTION + << " layer->loadStatus()" << layer->loadStatus() + << ", singleEnv.toJson()" << singleEnv.toJson(); + + sr = singleEnv.spatialReference(); + + if (!std::isnan(singleEnv.xMin())) + xmin = std::min(singleEnv.xMin(), xmin); + if (!std::isnan(singleEnv.yMin())) + ymin = std::min(singleEnv.yMin(), ymin); + + if (!std::isnan(singleEnv.xMax())) + xmax = std::max(singleEnv.xMax(), xmax); + if (!std::isnan(singleEnv.yMax())) + ymax = std::max(singleEnv.yMax(), ymax); + } // for + + if (qFuzzyCompare(xmin, std::numeric_limits::max()) || sr.isEmpty()) + return Esri::ArcGISRuntime::Envelope(); + + if (margin_zeroToOne < 0.0) + margin_zeroToOne = 0.0; + + if (margin_zeroToOne > 1.0) + margin_zeroToOne = 1.0; + + if (margin_zeroToOne > 0) + { + double width = xmax - xmin; + double height = ymax - ymin; + + xmin -= width * margin_zeroToOne; + xmax += width * margin_zeroToOne; + + ymin -= height * margin_zeroToOne; + ymax += height * margin_zeroToOne; + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " return.x " << xmin << " - " << xmax; + DEBUG_LOG(m_logger) << LOGFUNCTION << " return.y " << ymin << " - " << ymax; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return Esri::ArcGISRuntime::Envelope(xmin, ymin, xmax, ymax, sr); +} + +bool LayerFactoryImpl::addSceneObjectLayer(const QString &layername, const QUrl &sceneLayerServiceUrl, const double altitudeOffset, const QString &displayName, const QString &description, const QImage &previewImage) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " layername='" << layername << "'" + << " sceneLayerServiceUrl='" << sceneLayerServiceUrl.toString() << "'" + << " altitudeOffset='" << altitudeOffset << "'" + << " displayName='" << displayName << "'" + << " description='" << description << "'" + << " previewImage.isNull()=" << previewImage.isNull(); + + if (d->m_mapImpl->mode() != Mode::Mode_3D) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " not allowd in Mode3D"; + return false; + } + + if (sceneLayerServiceUrl.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " sceneLayerServiceUrl empty, can not add layer"; + return false; + } + + if (d->m_orderedMapLayer.contains(layername, LayerType::Undefined)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " layername '" << layername << "' already in use"; + return false; + } + + //----------------------------------- + // ArcGIS ArcGISSceneLayer + //----------------------------------- + //----------------------------------------------------------------- + // Attention: + // local URLS -> do NOT specify any userCredentials (main) + // external URLS -> DO specify userCredentials (main) + //----------------------------------------------------------------- + + QUrl finalUrl = this->completeUrl(QLatin1String("SCENE_OBJ"), + sceneLayerServiceUrl, + QLatin1String("slpk/")); + INFO_LOG(m_logger) << LOGFUNCTION << " SCENE_OBJ finalUrl '" << finalUrl.toString() << "'"; + + Esri::ArcGISRuntime::Scene *esriScene = d->m_mapImpl->getEsriScene(); + +#ifdef USE_ARCGIS_SCENE_LAYER_CACHE + ArcGISSceneLayerCache::Item& aslCacheItem = ArcGISSceneLayerCache::getInstance()->getFreeItemRef(sceneLayerServiceUrl.toString()); + + if (m_logger.isInfoEnabled()) + ArcGISSceneLayerCache::getInstance()->dump(); + + + // const bool reUse = aslCacheItem.m_hostingScene.isNull() && aslCacheItem.m_sceneLayer; + // aslCacheItem.m_hostingScene is always null. See getItemRef + const bool reUse = aslCacheItem.m_sceneLayer; + + ERROR_LOG(m_logger) << LOGFUNCTION << " ArcGISSceneLayer -> reUse = "<< reUse + << " itemName = '" << sceneLayerServiceUrl.toString() << "'" + << " layername='" << layername << "'" + << " aslCacheItem.m_hostingScene.data() = " << aslCacheItem.m_hostingScene.data() + << " aslCacheItem.m_sceneLayer.data() = " << aslCacheItem.m_sceneLayer.data(); + + Esri::ArcGISRuntime::ArcGISSceneLayer *sceneLayer = nullptr; + + if (!reUse) + { + INFO_LOG(m_logger) << LOGFUNCTION << " ArcGISSceneLayer -> CREATE" + << " (itemName = '" << sceneLayerServiceUrl.toString() << "', layername='" << layername << "')"; + + sceneLayer = new Esri::ArcGISRuntime::ArcGISSceneLayer(finalUrl); // ArcGISSceneLayer should live longer than the scene: -> no parent here :-) + sceneLayer->setName(layername); + sceneLayer->setObjectName(sceneLayer->name()); + + connect(sceneLayer, &Esri::ArcGISRuntime::ArcGISSceneLayer::errorOccurred, this, [this](const Esri::ArcGISRuntime::Error &error) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " ArcGISSceneLayer -> EsriError occured: " << error; + }); + connect(sceneLayer, &Esri::ArcGISRuntime::ArcGISSceneLayer::doneLoading, this, [this]() + { + Esri::ArcGISRuntime::ArcGISSceneLayer *sceneLayer = qobject_cast(sender()); + if (!sceneLayer) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " qobject_cast failed to get sender!?"; + return; + } + + QString senderName = sceneLayer->objectName(); + INFO_LOG(m_logger) << LOGFUNCTION << " ArcGISSceneLayer -> doneLoading" + << " ,sendername= '" << senderName << "'" + << " ,URL= '" << sceneLayer->url().toString() << "'"; + }); + + connect(sceneLayer, &Esri::ArcGISRuntime::ArcGISSceneLayer::destroyed, this, [this]() + { + ERROR_LOG(m_logger) << LOGFUNCTION << " ArcGISSceneLayer -> destroyed"; + }); + + aslCacheItem.m_hostingScene = esriScene; // update cache + aslCacheItem.m_sceneLayer = sceneLayer; // update cache + + } else { + INFO_LOG(m_logger) << LOGFUNCTION << " ArcGISSceneLayer -> TAKE FROM STATIC" + << " (itemName = '" << sceneLayerServiceUrl.toString() << "', layername='" << layername << "')"; + + aslCacheItem.m_hostingScene = esriScene; // update cache + sceneLayer = aslCacheItem.m_sceneLayer; + } +#else + Esri::ArcGISRuntime::ArcGISSceneLayer *sceneLayer = new Esri::ArcGISRuntime::ArcGISSceneLayer(finalUrl, d->m_mapImpl); + sceneLayer->setName(layername); + sceneLayer->setObjectName(sceneLayer->name()); + sceneLayer->setAltitudeOffset(altitudeOffset); + + connect(sceneLayer, &Esri::ArcGISRuntime::ArcGISSceneLayer::errorOccurred, this, [this](const Esri::ArcGISRuntime::Error &error) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " ArcGISSceneLayer -> EsriError occured: " << error; + }); + connect(sceneLayer, &Esri::ArcGISRuntime::ArcGISSceneLayer::doneLoading, this, [this]() + { + Esri::ArcGISRuntime::ArcGISSceneLayer *sceneLayer = qobject_cast(sender()); + if (!sceneLayer) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " qobject_cast failed to get sender!?"; + return; + } + + QString senderName = sceneLayer->objectName(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " ArcGISSceneLayer -> doneLoading" + << " ,sendername= '" << senderName << "'" + << " ,URL= '" << sceneLayer->url().toString() << "'"; + }); +#endif // USE_ARCGIS_SCENE_LAYER_CACHE + + + if (esriScene) + { + esriScene->operationalLayers()->append(sceneLayer); + } + else + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no scene"; + return false; + } + +#ifdef USE_ARCGIS_SCENE_LAYER_CACHE + if (m_logger.isInfoEnabled()) + ArcGISSceneLayerCache::getInstance()->dump(); +#endif // USE_ARCGIS_SCENE_LAYER_CACHE + + d->m_sceneObjectUrlCont += sceneLayerServiceUrl; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " sceneLayerServiceUrl='" << sceneLayerServiceUrl.toString() << "'"; + return true; +} + +bool LayerFactoryImpl::isSceneObjectLayerVisible() const +{ + return d->m_isSceneObjectLayerVisible; +} + +void LayerFactoryImpl::setSceneObjectLayerVisible(bool visible) +{ + d->m_isSceneObjectLayerVisible = visible; + + if ( (!d->m_mapImpl) || d->m_mapImpl->mode() != Mode::Mode_3D) + return; + + Esri::ArcGISRuntime::Scene *esriScene = d->m_mapImpl->getEsriScene(); + if (!esriScene) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no scene"; + return; + } + + Esri::ArcGISRuntime::LayerListModel *layerListModel = esriScene->operationalLayers(); + if (!layerListModel) + return; + + const int size = layerListModel->size(); + for (int idx=0; idxat(idx); + if (!objLayer) + continue; + objLayer->setVisible(d->m_isSceneObjectLayerVisible); + } // for + + + +} + +bool LayerFactoryImpl::initializeElevationSources(const QStringList &sceneElevationServiceUrls) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " sceneElevationServiceUrls='" << sceneElevationServiceUrls.join(QLatin1String(";")) << "'"; + + const Mode mode = d->m_mapImpl->mode(); + + switch (mode) + { + case Mode::Mode_2D: + this->initializeElevationSources2D(sceneElevationServiceUrls); + break; + case Mode::Mode_3D: + this->initializeElevationSources3D(sceneElevationServiceUrls); + break; + default: + ERROR_LOG(m_logger) << LOGFUNCTION << " unknown mode"; + return false; + } // switch + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +bool LayerFactoryImpl::initializeElevationSources2D(const QStringList &sceneElevationServiceUrls) +{ + if (sceneElevationServiceUrls.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " sceneObjectServiceUrls is Empty"; + return false; + } + + if (d->m_mapImpl->mode() != Mode::Mode_2D) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " wrong mode"; + return false; + } + + // + // append to 'own' esri (base) surface: + // + delete d->m_baseSurface; + d->m_baseSurface = new Esri::ArcGISRuntime::Surface(this); + d->m_baseSurface->setName(QLatin1String("BaseSurface2D")); + d->m_baseSurface->setObjectName(d->m_baseSurface->name()); + + connect(d->m_baseSurface, &Esri::ArcGISRuntime::Surface::errorOccurred, this, &LayerFactoryImpl::onSurfaceErrorOccured2D, Qt::UniqueConnection); + connect(d->m_baseSurface, &Esri::ArcGISRuntime::Surface::doneLoading, this, &LayerFactoryImpl::onSurfaceDoneLoading2D, Qt::UniqueConnection); + + const QList &elevationSources = + initializeElevationSourcesInternal(sceneElevationServiceUrls); + + for (auto elevationSource : std::as_const(elevationSources)) + d->m_baseSurface->elevationSources()->append(elevationSource); + + + // trigger loading: + // As Luke suggested: load surface if first elevation entry is loaded: + // https://community.esri.com/thread/206757-elevation-in-2d + if (!elevationSources.isEmpty()) + { + Esri::ArcGISRuntime::ElevationSource* elevationSourcesFirst = elevationSources.first(); + if (elevationSourcesFirst) + { + connect(elevationSourcesFirst, &Esri::ArcGISRuntime::ElevationSource::doneLoading, this, [this](const Esri::ArcGISRuntime::Error&) + { + d->m_baseSurface->load(); + }); + elevationSourcesFirst->load(); + } + } + + return true; +} + +bool LayerFactoryImpl::initializeElevationSources3D(const QStringList &sceneElevationServiceUrls) +{ + if (sceneElevationServiceUrls.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " sceneObjectServiceUrls is Empty"; + return false; + } + + if (d->m_mapImpl->mode() != Mode::Mode_3D) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " wrong mode"; + return false; + } + + Esri::ArcGISRuntime::Scene *esriScene = d->m_mapImpl->getEsriScene(); + if (!esriScene) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no scene"; + return false; + } + + if (!esriScene->baseSurface() || !esriScene->baseSurface()->elevationSources()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no ElevationSourceListModel"; + return false; + } + + // + // append to esri scene: + // + const QList elevationSources = + initializeElevationSourcesInternal(sceneElevationServiceUrls); + + for (auto item : elevationSources) + esriScene->baseSurface()->elevationSources()->append(item); + + return true; +} + +QList LayerFactoryImpl::initializeElevationSourcesInternal(const QStringList &sceneElevationServiceUrls) +{ + if (sceneElevationServiceUrls.isEmpty()) + return {}; + + Esri::ArcGISRuntime::RasterElevationSource* rasterElevationSource = + initializeRasterElevationSourceInternal(sceneElevationServiceUrls); + + QList elevationSources; + elevationSources.append(rasterElevationSource); + + return elevationSources; +} + +Esri::ArcGISRuntime::RasterElevationSource *LayerFactoryImpl::initializeRasterElevationSourceInternal(const QStringList &filePaths) +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " filePaths = " << filePaths.join(QStringLiteral(";")) << "'"; + + QStringList absFilePaths_rough_to_fine; + absFilePaths_rough_to_fine.reserve(filePaths.size()); + + for (const QString &filePath : filePaths) + { + QUrl finalUrl = this->completeUrl(QLatin1String("ELEVATION"), + QUrl(filePath), + QLatin1String("elevation/")); + + INFO_LOG(m_logger) << LOGFUNCTION << " ELEVATION finalUrl '" << finalUrl.toString() << "'"; + absFilePaths_rough_to_fine.append(finalUrl.toLocalFile()); + } // for + + if (absFilePaths_rough_to_fine.isEmpty()) + { + WARNING_LOG(m_logger) << LOGFUNCTION << " filePaths are empty!!!"; + return nullptr; + } + DEBUG_LOG(m_logger) << LOGFUNCTION << " absFilePaths_rough_to_fine = " << absFilePaths_rough_to_fine.join(QStringLiteral(";")) << "'"; + + // + // init ElevationMapManager + // + if (!d->m_mapImpl->getElevationMapManager()) + { + if (!d->m_mapImpl->initElevationMapManager(absFilePaths_rough_to_fine)) + ERROR_LOG(m_logger) << LOGFUNCTION << " initElevationMapManager failed"; + } + + // + // Esri::ArcGISRuntime::RasterElevationSource with several files + // + Esri::ArcGISRuntime::RasterElevationSource *rasterElevationSource = + new Esri::ArcGISRuntime::RasterElevationSource(absFilePaths_rough_to_fine, this); + + const QString &elevationSourceName = QStringLiteral("RasterElevationSources_((%1))").arg(absFilePaths_rough_to_fine.size()); + rasterElevationSource->setName(elevationSourceName); + rasterElevationSource->setObjectName(rasterElevationSource->name()); + + // 2017-09-18; N.Thoden, V100.1: + // Following message occures very often. Therefore don`t connect to errorOccurred + // 'code=404 domain=0 extendedErrorType=0, message='Error transferring http://excgeo21.sl .. - server replied: Not Found' + connect(rasterElevationSource, &Esri::ArcGISRuntime::ElevationSource::errorOccurred, this, &LayerFactoryImpl::onElevationSourceErrorOccured); + connect(rasterElevationSource, &Esri::ArcGISRuntime::ElevationSource::doneLoading, + this, &LayerFactoryImpl::onElevationSourceDoneLoading); + return rasterElevationSource; +} + + +void LayerFactoryImpl::setForceCreationOfGraphicsOverlay(bool onOff) +{ + s_forceCreationOfGraphicsOverlay = onOff; +} + + +QString LayerFactoryImpl::basemapLayernamePrefix() const +{ + return d->m_basemapLayernamePrefix; +} +void LayerFactoryImpl::setBasemapLayernamePrefix(const QString &basemapLayernamePrefix) +{ + d->m_basemapLayernamePrefix = basemapLayernamePrefix; +} + +QString LayerFactoryImpl::participantLayername() const +{ + return d->m_participantLayername; +} +RenderingMode LayerFactoryImpl::participantLayerRenderingMode() const +{ + return d->m_participantLayerRenderingMode; +} +SurfacePlacement LayerFactoryImpl::participantLayerSurfacePlacement() const +{ + return d->m_participantLayerSurfacePlacement; +} + +void LayerFactoryImpl::setParticipantLayer(const QString &participantLayername, + RenderingMode renderingMode, + SurfacePlacement surfacePlacement) +{ + d->m_participantLayername = participantLayername; + d->m_participantLayerRenderingMode = renderingMode; + d->m_participantLayerSurfacePlacement = surfacePlacement; +} + +QString LayerFactoryImpl::jumpPointLayer() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + const QString &logicalLayerName = LayerNameJumpPoints; + const LayerType layerType = LayerType::JumpPoint; + + if (! layerExistsInMngt(logicalLayerName, layerType)) + { + QSharedPointer logicalLayer = addLayerToMgnt(logicalLayerName, layerType); + if (logicalLayer.isNull()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " addLayer failed: '" + << logicalLayerName << "'"; + return QString(); + } + LayerParameter lp = logicalLayer->layerParameter(); + lp.displayName = tr("JumpPoints"); // dt. Sprungmarken + logicalLayer->setLayerParameter(lp); + } + + + // + // the real Esri-Layers will be create later on demand :-) + // + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return logicalLayerName; // == LayerNameJumpPoints; +} + + +bool LayerFactoryImpl::jumpPointLayerExists() const +{ + const QString &logicalLayerName = LayerNameJumpPoints; + return (d->m_orderedMapLayer.contains(logicalLayerName, SituationMap::LayerType::JumpPoint)); +} + + + +QString LayerFactoryImpl::toolLayer() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + const QString &logicalLayerName = LayerNameTools; + const LayerType layerType = LayerType::Tools; + + if (! layerExistsInMngt(logicalLayerName, layerType)) + { + QSharedPointer logicalLayer = addLayerToMgnt(logicalLayerName, layerType); + if (logicalLayer.isNull()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " addLayer failed: '" + << logicalLayerName << "'"; + return QString(); + } + LayerParameter lp = logicalLayer->layerParameter(); + lp.displayName = tr("Tools"); // dt. Werkzeuge (?) + logicalLayer->setLayerParameter(lp); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + + // + // the real Esri-Layers will be create later on demand :-) + // + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return logicalLayerName; // == LayerNameTools; +} + +bool LayerFactoryImpl::toolLayerExists() const +{ + const QString &logicalLayerName = LayerNameTools; + return (d->m_orderedMapLayer.contains(logicalLayerName, SituationMap::LayerType::Tools)); +} + + +QString LayerFactoryImpl::rubberbandLayer() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + const QString &logicalLayerName = LayerNameRubberband; + const LayerType layerType = LayerType::Rubberband; + + if (! layerExistsInMngt(logicalLayerName, layerType)) + { + LayerFactoryImpl *THIS = const_cast(this); + QSharedPointer logicalLayer = THIS->addLayerToMgnt(logicalLayerName, layerType); + if (logicalLayer.isNull()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " addLayer failed: '" + << logicalLayerName << "'"; + return QString(); + } + } + + // + // the real Esri-Layers will be create later on demand :-) + // + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return logicalLayerName; // == LayerNameRubberband; +} +bool LayerFactoryImpl::rubberbandLayerExists() const +{ + const QString &logicalLayerName = LayerNameRubberband; + return (d->m_orderedMapLayer.contains(logicalLayerName, SituationMap::LayerType::Rubberband)); +} + +QString LayerFactoryImpl::emergencyCallLayer(bool getJustTheName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + const QString &logicalLayerName = LayerNameEmergencyCall; + + if (getJustTheName) + { + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return logicalLayerName; + } + + const LayerType layerType = LayerType::EmergencyCall; + + if (! layerExistsInMngt(logicalLayerName, layerType)) + { + QSharedPointer logicalLayer = addLayerToMgnt(logicalLayerName, layerType); + if (logicalLayer.isNull()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " addLayer failed: '" + << logicalLayerName << "'"; + return QString(); + } + LayerParameter lp = logicalLayer->layerParameter(); + lp.displayName = tr("EmergencyCalls"); // dt. Notrufe/SOS (?) + logicalLayer->setLayerParameter(lp); + } + + { + Esri::ArcGISRuntime::GraphicsOverlay *graphicsOverlay = this->getGraphicsLayer(logicalLayerName); + if (!graphicsOverlay) + { + FATAL_LOG(m_logger) << LOGFUNCTION << " graphicsOverlay not found, logicalLayerName'" << logicalLayerName<< "'"; + return QString(); + } + // set SurfacePlacement // EmergencyCalls have a pin/needle + Esri::ArcGISRuntime::LayerSceneProperties lsp = graphicsOverlay->sceneProperties(); + lsp.setSurfacePlacement(static_cast(Esri::ArcGISRuntime::SurfacePlacement::Relative)); + graphicsOverlay->setSceneProperties(lsp); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return logicalLayerName; // == LayerNameEmergencyCall; +} + +bool LayerFactoryImpl::emergencyCallLayerExists() const +{ + const QString &emergencyCallLayername = LayerNameEmergencyCall; + return (d->m_orderedMapLayer.contains(emergencyCallLayername, SituationMap::LayerType::EmergencyCall)); +} + +bool LayerFactoryImpl::switchEmergencyCallLayerOn() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + const QString &emergencyCallLayername = LayerNameEmergencyCall; + + if (!(d->m_orderedMapLayer.contains(emergencyCallLayername, SituationMap::LayerType::EmergencyCall))) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " EmergencyCallLayer does not exists, can´t be switched on."; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return false; + } + + LayerParameter lp = this->layerParameter(emergencyCallLayername); + lp.visible = true; + lp.opacity = 100; + lp.minScale = -1; + lp.maxScale = -1; + + const bool success = this->setLayerParameter(emergencyCallLayername, lp); + if (!success) { + ERROR_LOG(m_logger) << LOGFUNCTION << " setLayerParameter failed for '" << emergencyCallLayername << "'"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return false; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +bool LayerFactoryImpl::switchEmergencyCallLayerOff() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + const QString &emergencyCallLayername = LayerNameEmergencyCall; + + if (!(d->m_orderedMapLayer.contains(emergencyCallLayername, SituationMap::LayerType::EmergencyCall))) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " EmergencyCallLayer does not exists, can´t be switched off."; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return false; + } + + LayerParameter lp = this->layerParameter(emergencyCallLayername); + lp.visible = false; + lp.opacity = 100; + lp.minScale = -1; + lp.maxScale = -1; + + const bool success = this->setLayerParameter(emergencyCallLayername, lp); + if (!success) { + ERROR_LOG(m_logger) << LOGFUNCTION << " setLayerParameter failed for '" << emergencyCallLayername << "'"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return false; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; + +} + +void LayerFactoryImpl::handleEmergencyCallSpecials() +{ + const QString &emergencyCallLayername = LayerNameEmergencyCall; + const bool bEmergencyCallLayerExists = emergencyCallLayerExists(); + TRACE_LOG(m_logger) << LOGFUNCTION << " START, emergencyCallLayerExists() = " << bEmergencyCallLayerExists; + + if (!bEmergencyCallLayerExists) + return; + + // ensure to be on top: + QStringList slLayerNamesOrdered = d->m_orderedMapLayer.layerNamesOrdered(); + if (slLayerNamesOrdered.isEmpty()) + return; + + const QString &lastNameTOP = slLayerNamesOrdered.last(); + + if (lastNameTOP == emergencyCallLayer()) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " emergencyCallLayer already on top"; + return; + } + + slLayerNamesOrdered.removeAll(emergencyCallLayername); + slLayerNamesOrdered.push_back(emergencyCallLayername); + orderLayerTo(slLayerNamesOrdered); + + { + QStringList slLayerNamesOrderedLocal = d->m_orderedMapLayer.layerNamesOrdered(); + if (slLayerNamesOrderedLocal.isEmpty()) + return; + + const QString &lastNameTOPLocal = slLayerNamesOrderedLocal.last(); + if (lastNameTOPLocal == emergencyCallLayer()) + DEBUG_LOG(m_logger) << LOGFUNCTION << " emergencyCallLayer is now on top"; + } + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +Map *LayerFactoryImpl::map() const +{ + return d->m_mapImpl; +} + +int LayerFactoryImpl::count() const +{ + return d->m_orderedMapLayer.count(); +} + +QStringList LayerFactoryImpl::layernames() const +{ + return d->m_orderedMapLayer.layerNamesOrdered(); +} + +QStringList LayerFactoryImpl::layernames(const LayerType &layerType) const +{ + return d->m_orderedMapLayer.layerNamesOrdered(layerType); +} + +bool SituationMap::LayerFactoryImpl::checkLayerOrder(const QStringList &orderedLayernames) +{ + bool requestedOrderIsValid = false; + bool success = d->m_orderedMapLayer.orderLayerTo(orderedLayernames, &requestedOrderIsValid); + + if (success) + return requestedOrderIsValid; + else + return false; +} + +bool LayerFactoryImpl::orderLayerTo(const QStringList &orderedLayernames) +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " START" + << ", orderedLayername= " << orderedLayernames.join(QLatin1String(";")); + + if (orderedLayernames.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " orderedLayername is empty"; + return false; + } + + bool success = true; + + const QStringList &orderedLayernamesCurrent = d->m_orderedMapLayer.layerNamesOrdered(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " START" + << ", orderedLayernamesCurrent=" << orderedLayernamesCurrent.join(QLatin1String(";")); + + if (orderedLayernamesCurrent != orderedLayernames) + { + if ((success = d->m_orderedMapLayer.orderLayerTo(orderedLayernames))) + reOrderLayer(); + else + { + ERROR_LOG(m_logger) << LOGFUNCTION << " orderLayerTo failed"; + ERROR_LOG(m_logger) << LOGFUNCTION << " orderLayerTo failed, cur =" << d->m_orderedMapLayer.layerNamesOrdered().join(QLatin1String(";")); + ERROR_LOG(m_logger) << LOGFUNCTION << " orderLayerTo failed, should=" << orderedLayernames.join(QLatin1String(";")); + } + d->m_mapImpl->emitLayerOrderChanged(); + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " END, success=" << success + << ", orderedLayernamesCurrent=" << orderedLayernamesCurrent.join(QLatin1String(";")); + return success; +} + +LayerType LayerFactoryImpl::layerType(const QString &layername) const +{ + LayerType layerType = LayerType::Undefined; + + MapLayer mapLayer = d->m_orderedMapLayer.getLayer(layername); + if (mapLayer.isValid()) + layerType = mapLayer.type(); + else + { + ERROR_LOG(m_logger) << LOGFUNCTION << " layer not found" + << " layername='" << layername << "'"; + } + + return layerType; +} + +LayerParameter LayerFactoryImpl::layerParameter(const QString &layername) const +{ + LayerParameter lp; + + QSharedPointer logicalLayer = this->getLogicalLayer(layername); + if (logicalLayer) + lp = logicalLayer->layerParameter(); + else + { + ERROR_LOG(m_logger) << LOGFUNCTION << " not found" + << " layername=" << layername; + } + + return lp; +} + +bool LayerFactoryImpl::setLayerParameter(const QString &layername, const LayerParameter &layerParameter) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " layername=" << layername; + + bool success = false; + + QSharedPointer logicalLayer = this->getLogicalLayer(layername); + if (logicalLayer) + { + logicalLayer->setLayerParameter(layerParameter); + success = true; + } + else + { + ERROR_LOG(m_logger) << LOGFUNCTION << " not found" + << " layername=" << layername; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " success=" << success; + return success; +} + + +bool LayerFactoryImpl::setLayerParameter(LayerType layerType, int value, int LayerParameter::*memberPtr) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " layer type=" << int(layerType) << " layer parameter member=" << memberPtr << " value=" << value; + + const QStringList &layerNameList = d->m_orderedMapLayer.layerNamesOrdered(layerType); + bool success = true; + + for (const QString &layername : layerNameList) + { + QSharedPointer logicalLayer = this->getLogicalLayer(layername); + if (logicalLayer) + { + LayerParameter layerParamTmp = logicalLayer->layerParameter(); + layerParamTmp.*memberPtr = value; + logicalLayer->setLayerParameter(layerParamTmp); + + } + else + { + success = false; + ERROR_LOG(m_logger) << LOGFUNCTION << " not found" + << ", layername='" << layername << "'"; + } + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " success=" << success; + return success; +} + +bool LayerFactoryImpl::setLayerOpacity(LayerType layerType, int opacity) { return setLayerParameter(layerType, opacity, &LayerParameter::opacity); } +bool LayerFactoryImpl::setLayerBrightness(LayerType layerType, int brightness) { return setLayerParameter(layerType, brightness, &LayerParameter::brightness); } +bool LayerFactoryImpl::setLayerContrast(LayerType layerType, int contrast) { return setLayerParameter(layerType, contrast, &LayerParameter::contrast); } +bool LayerFactoryImpl::setLayerGamma(LayerType layerType, int gamma) { return setLayerParameter(layerType, gamma, &LayerParameter::gamma); } + +void LayerFactoryImpl::setLayersVisible(const QVector &layerTypes, bool visible) +{ + for (auto &logicalLayer : d->m_logicalLayerCont) + { + if (!layerTypes.contains(logicalLayer->layerType())) + continue; + LayerParameter layerParamTmp = logicalLayer->layerParameter(); + layerParamTmp.visible = visible; + logicalLayer->setLayerParameter(layerParamTmp); + } +} + +#if ACRGIS_VERSION < 0x640500 // 100.5.0 / Update 5 +// deprecated in V100.5, replaced by GeoView::setSelectionProperties() +QColor LayerFactoryImpl::selectionColor() const +{ + return d->m_selectionColor; +} +void LayerFactoryImpl::setSelectionColor(const QColor &selectionColor) +{ + d->m_selectionColor = selectionColor; +} +#endif // ACRGIS_VERSION + +bool LayerFactoryImpl::elevationAvailable() const +{ + if (!d->m_mapImpl) + return false; + return this->elevationSourcesCount() > 0; +} + +Esri::ArcGISRuntime::Surface *LayerFactoryImpl::baseSurface() const +{ + if (!elevationAvailable()) + return nullptr; + + Esri::ArcGISRuntime::Surface *baseSurface = nullptr; + + switch (d->m_mapImpl->mode()) + { + case Mode::Mode_2D: + baseSurface = d->m_baseSurface; + break; + case Mode::Mode_3D: + { + Esri::ArcGISRuntime::Scene *esriScene = d->m_mapImpl->getEsriScene(); + if (esriScene && + esriScene->baseSurface() + ) + baseSurface = esriScene->baseSurface(); + } + break; + default: + break; + } // switch + + + // Verify: + if (!baseSurface) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no baseSurface"; + return nullptr; + } + + if (baseSurface->loadStatus() != Esri::ArcGISRuntime::LoadStatus::Loaded) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " baseSurface not loaded yet"; + return nullptr; + } + + int elevationSourcesCount = -1; + Esri::ArcGISRuntime::ElevationSourceListModel* eslm = baseSurface->elevationSources(); + if (eslm) + elevationSourcesCount = eslm->size(); + + if (elevationSourcesCount <= 0) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no elevationSourcesCount"; + return nullptr; + } + + return baseSurface; +} + +int LayerFactoryImpl::elevationSourcesCount(bool checkIfLoaded) const +{ + if (!d->m_mapImpl) + return -1; + + Esri::ArcGISRuntime::ElevationSourceListModel* elevationSourceListModel = nullptr; + + switch (d->m_mapImpl->mode()) + { + case Mode::Mode_2D: + { + if (d->m_baseSurface && d->m_baseSurface->elevationSources()) + elevationSourceListModel = d->m_baseSurface->elevationSources(); + } + + break; + case Mode::Mode_3D: + { + if (d->m_mapImpl->getEsriScene() && + d->m_mapImpl->getEsriScene()->baseSurface() && + d->m_mapImpl->getEsriScene()->baseSurface()->elevationSources() + ) + elevationSourceListModel = d->m_mapImpl->getEsriScene()->baseSurface()->elevationSources(); + } + break; + default: + return false; + } // switch + + if (!elevationSourceListModel) + return -1; + + int elevationSourcesCount = -1; + + if (checkIfLoaded) + { + elevationSourcesCount = 0; + + int idx = 0; + const int esmSize = elevationSourceListModel->size(); + for (; idx < esmSize; ++idx) + { + Esri::ArcGISRuntime::ElevationSource *es = elevationSourceListModel->at(idx); + + if (es && (es->loadStatus() == Esri::ArcGISRuntime::LoadStatus::Loaded)) + ++elevationSourcesCount; + } // for + + INFO_LOG(m_logger) << LOGFUNCTION << " elevationSourcesCount = " << esmSize + << ", loaded = " << elevationSourcesCount; + } + else + { + elevationSourcesCount = elevationSourceListModel->size(); + } + + return elevationSourcesCount; +} + +bool LayerFactoryImpl::elevationSourcesLoaded() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + const int elevationSourcesCount = this->elevationSourcesCount(); + const int elevationSourcesCountLoaded = this->elevationSourcesCount(true); + return (elevationSourcesCount == elevationSourcesCountLoaded); +} + +QPointer LayerFactoryImpl::locationToElevation() const +{ + QPointer locationToElevationHelper; + + if (!elevationAvailable()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no elevation available"; + return locationToElevationHelper; + } + + Esri::ArcGISRuntime::Surface *baseSurface = this->baseSurface(); + + if (!baseSurface) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no baseSurface"; + return locationToElevationHelper; + } + + LayerFactoryImpl *THIS = const_cast(this); + locationToElevationHelper = new LocationToElevationHelper(THIS); // QPointer -> parent is ok! + + if (!locationToElevationHelper->init(d->m_mapImpl, + baseSurface, + d->m_mapImpl->getEsriSpatialReference())) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " locationToElevationHelper->init failed"; + delete locationToElevationHelper; + return nullptr; + } + + return locationToElevationHelper; +} + +Esri::ArcGISRuntime::GraphicsOverlay *LayerFactoryImpl::getGraphicsLayer(const QString &logicalName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, logicalName='" << logicalName << "'"; + + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = nullptr; + + QSharedPointer logicalLayer = d->getLogicalLayer(logicalName); + if (d->m_orderedMapLayer.contains(logicalName, LayerType::Undefined) && logicalLayer) + graphicsLayer = logicalLayer->getCreateGraphicalOverlay(TechnicalLayerType::GraphicsLayer); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, logicalName='" << logicalName << "'" + << " graphicsLayer=" << graphicsLayer; + return graphicsLayer; +} + +Esri::ArcGISRuntime::GraphicsOverlay *LayerFactoryImpl::getGraphicsLayerDict(const QString &logicalName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, logicalName='" << logicalName << "'"; + + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = nullptr; + + QSharedPointer logicalLayer = d->getLogicalLayer(logicalName); + if (d->m_orderedMapLayer.contains(logicalName, LayerType::Undefined) && logicalLayer) + graphicsLayer = logicalLayer->getCreateGraphicalOverlay(TechnicalLayerType::GraphicsLayer_Dict); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, logicalName='" << logicalName << "'" + << " graphicsLayer=" << graphicsLayer; + return graphicsLayer; +} + +Esri::ArcGISRuntime::GraphicsOverlay *LayerFactoryImpl::getGraphicsLayer3DExtrusion(const QString &logicalName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, logicalName='" << logicalName << "'"; + + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = nullptr; + + // getGraphicsLayer3DExtrusion -> SurfacePlacement::Relative + + QSharedPointer logicalLayer = d->getLogicalLayer(logicalName); + if (d->m_orderedMapLayer.contains(logicalName, LayerType::Undefined) && logicalLayer) + graphicsLayer = logicalLayer->getCreateGraphicalOverlay(TechnicalLayerType::GraphicsLayer_3D_Extrusion); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, logicalName='" << logicalName << "'" + << " graphicsLayer=" << graphicsLayer; + return graphicsLayer; +} + +Esri::ArcGISRuntime::GraphicsOverlay *LayerFactoryImpl::getGraphicsLayer3D_SpAbsolute(const QString &logicalName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, logicalName='" << logicalName << "'"; + + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = nullptr; + + // getGraphicsLayer3D_SpAbsolute -> SurfacePlacement::Absolute + + QSharedPointer logicalLayer = d->getLogicalLayer(logicalName); + if (d->m_orderedMapLayer.contains(logicalName, LayerType::Undefined) && logicalLayer) + graphicsLayer = logicalLayer->getCreateGraphicalOverlay(TechnicalLayerType::GraphicsLayer_3D_Absolute); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, logicalName='" << logicalName << "'" + << " graphicsLayer=" << graphicsLayer; + return graphicsLayer; +} + + + +QString LayerFactoryImpl::createInternalLayerName(const TechnicalLayerType &technicalLayerType, + const QString &logicalLayerName) const +{ + return LogicalLayer::createInternalLayerName(logicalLayerName, technicalLayerType); +} + +QSharedPointer LayerFactoryImpl::getLogicalLayer(const QString &logicalName) const +{ + return d->getLogicalLayer(logicalName); +} + +QSharedPointer LayerFactoryImpl::getCreateLogicalLayer(const QString &logicalName) +{ + if (! d->hasLogicalLayer(logicalName)) + d->createLogicalLayer(logicalName); + + QSharedPointer logicalLayer = d->getLogicalLayer(logicalName); + if (logicalLayer) + { + connect(logicalLayer.data(), &LogicalLayer::layerParameterChanged, + this, &LayerFactoryImpl::onLayerParameterChanged); + + connect(logicalLayer.data(), &LogicalLayer::layerCreatedOnDemand, + this, &LayerFactoryImpl::onLayerCreatedOnDemand); + + connect(logicalLayer.data(), &LogicalLayer::layerDoneLoading, + this, &LayerFactoryImpl::onLayerDoneLoading); + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " getLogicalLayer failed"; + return logicalLayer; +} + +bool LayerFactoryImpl::addBasemapInternal(const QString &layername, + const QUrl &mapServiceUrl, + const BasemapType basemapType, + const double minScale, + const double maxScale, + const QString &displayName, + const QString &description, + const QImage &previewImage) +{ + if (mapServiceUrl.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " mapServiceUrl empty, can not add layer " + << layername << " basemap type: " << basemapType; + return false; + } + + if (d->m_orderedMapLayer.contains(layername, LayerType::Undefined)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " layername '" << layername << "' already in use"; + return false; + } + + //----------------------------------- + // ArcGIS Tiled Basemap Layer + //----------------------------------- + //----------------------------------------------------------------- + // Attention: + // local URLS -> do NOT specify any userCredentials (main) + // external URLS -> DO specify userCredentials (main) + //----------------------------------------------------------------- + + Esri::ArcGISRuntime::Layer *layer = nullptr; + + + if (mapServiceUrl.isLocalFile()) + { + QUrl finalUrl = this->completeUrl(QLatin1String("BASEMAP"), + mapServiceUrl, + QLatin1String("/")); + INFO_LOG(m_logger) << LOGFUNCTION + << " TileCache: finalUrl '" << finalUrl.toString() << "'" + << ", finalUrltoLocalFile '" << finalUrl.toLocalFile() << "'"; + QString tcFileAbs = finalUrl.toLocalFile(); + + QFileInfo fi(tcFileAbs); + + if (!fi.exists(tcFileAbs)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " file '" << tcFileAbs << "' doesn't exists"; + return false; + } + + if (fi.isDir() || // TileCacheFolder + fi.suffix().compare(QLatin1String("tpk"))==0) // TilePackage + { + Esri::ArcGISRuntime::TileCache *tileCache = new Esri::ArcGISRuntime::TileCache(tcFileAbs, d->m_mapImpl); + tileCache->setObjectName(QLatin1String("BasemapTileCache-")+layername); + layer = new Esri::ArcGISRuntime::ArcGISTiledLayer(tileCache, d->m_mapImpl); + } + else if (fi.suffix().compare(QLatin1String("vtpk"))==0) // VectorTilePackage + { + Esri::ArcGISRuntime::VectorTileCache *vectorTileCache = new Esri::ArcGISRuntime::VectorTileCache(tcFileAbs, d->m_mapImpl); + vectorTileCache->setObjectName(QLatin1String("BasemapVectorTileCache-")+layername); + layer = new Esri::ArcGISRuntime::ArcGISVectorTiledLayer(vectorTileCache, d->m_mapImpl); + } + else + { + // geotiff (often '.tif') + Esri::ArcGISRuntime::Raster* raster = new Esri::ArcGISRuntime::Raster(tcFileAbs, d->m_mapImpl); + layer = new Esri::ArcGISRuntime::RasterLayer(raster, d->m_mapImpl); + + // loading of the geotiff may take a while (tile by tile) + } + } + else + { + INFO_LOG(m_logger) << LOGFUNCTION << " loading url '" << mapServiceUrl.toString() << "'"; + layer = new Esri::ArcGISRuntime::ArcGISTiledLayer(mapServiceUrl, d->m_mapImpl); + } + + layer->setName(layername); + layer->setObjectName(layer->name()); + + if (!d->m_orderedMapLayer.addLayer(layer->name(), SituationMap::LayerType::Basemap)) + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " addLayer failed'" << layer->name() << "'"; + } + + QImage previewImageFinal = previewImage; + if (previewImageFinal.isNull()) + { + switch (basemapType) + { + case BasemapType::Undefined: + case BasemapType::Aerial: + previewImageFinal = QImage(QLatin1String(":/situationMap/preview_aerial.png")); + break; + case BasemapType::Topographic: + previewImageFinal = QImage(QLatin1String(":/situationMap/preview_topographic.png")); + break; + } // switch + } + + if (! d->m_basemap) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no basemap!!!"; + return false; + } + + + int basemapCount = -1; + if (d->m_basemap->baseLayers()) + basemapCount = d->m_basemap->baseLayers()->size(); + + INFO_LOG(m_logger) << LOGFUNCTION << " basemapCount = " << basemapCount; + + d->m_basemap->baseLayers()->append(layer); + + + // getLogicalLayer will + // - initialize + // - do errorHandling + // - update Layerparamter on doneLoading + // - and add layer to LayerListModel of basemap: + QSharedPointer logicalLayer = this->getCreateLogicalLayer(layername); + if (logicalLayer.isNull()) + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " getLogicalLayer invalid'" << layername << "'"; + } + else + { + // update LayerParameter first: + LayerParameter lp = logicalLayer->layerParameter(); + lp.url = mapServiceUrl; + lp.minScale = minScale; + lp.maxScale = maxScale; + lp.basemapType = basemapType; + lp.displayName = displayName; + lp.description = description; + lp.previewImage = previewImageFinal; + logicalLayer->setLayerParameter(lp); + + logicalLayer->setLayer(layer); + logicalLayer->setLayerType(LayerType::Basemap); + logicalLayer->setBasemapType(basemapType); // basemapType -> aerial, topo... + } + + + // // load / retryLoad: + // if (d->m_esriMap->loadStatus() == Esri::ArcGISRuntime::LoadStatus::FailedToLoad) + // d->m_esriMap->retryLoad(); + // else + // d->m_esriMap->load(); + + return true; +} + +bool LayerFactoryImpl::removeLayerInternal(const LayerType &layerType, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " layerType=" << layerType + << " logicalLayerName=" << logicalLayerName; + + if (logicalLayerName.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " logicalLayerName empty" + << logicalLayerName; + return false; + } + if (! d->m_orderedMapLayer.contains(logicalLayerName, layerType)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " does not exist: '" + << " layerType=" << layerType + << " logicalLayerName='" << logicalLayerName << "'"; + return false; + } + + if (! d->hasLogicalLayer(logicalLayerName)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " does not exist: '" + << " layerType=" << layerType + << " logicalLayerName='" << logicalLayerName << "'"; + } + + if (! d->m_orderedMapLayer.removeLayer(logicalLayerName)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " remove failed: '" + << " layerType=" << layerType; + return false; + } + + d->removeLogicalLayer(logicalLayerName); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " logicalLayerName='" << logicalLayerName << "'"; + return true; +} + +bool LayerFactoryImpl::reOrderLayer() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " orderedList 1: " << d->m_orderedMapLayer.layerNamesOrdered().join(QLatin1String(";")); + + reOrderLayerBasemap(); + reOrderLayerGraphicOverlays(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " orderedList 2: " << d->m_orderedMapLayer.layerNamesOrdered().join(QLatin1String(";")); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +bool LayerFactoryImpl::reOrderLayerBasemap() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!(d->m_basemap && + d->m_basemap->baseLayers() && + ! d->m_basemap->baseLayers()->isEmpty()) + ) + return false; + + const QStringList &allLogicalLayerNames = d->m_orderedMapLayer.layerNamesOrdered(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " orderedListAll: " << allLogicalLayerNames.join(QLatin1String(";")); + + const QStringList &basemapLogicalNames = d->m_orderedMapLayer.layerNamesOrdered(LayerType::Basemap); + DEBUG_LOG(m_logger) << LOGFUNCTION << " orderedListBasemap: " << basemapLogicalNames.join(QLatin1String(";")); + + int newIdx = 0; + Esri::ArcGISRuntime::LayerListModel *model = d->m_basemap->baseLayers(); + for (const QString &basemapName : basemapLogicalNames) + { + int curIdx = getEsriBasemapLayerIndex(basemapName); + + if (curIdx != newIdx) + { + DEBUG_LOG(m_logger) << LOGFUNCTION + << " move" + << " basemapName ='" << basemapName << "'" + << " from " << curIdx + << " to " << newIdx << "..."; + model->move(curIdx, newIdx); + } + ++newIdx; + } // for + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " orderedList: " << d->m_orderedMapLayer.layerNamesOrdered().join(QLatin1String(";")); + return true; +} + +bool LayerFactoryImpl::reOrderLayerGraphicOverlays() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " orderedList: " << d->m_orderedMapLayer.layerNamesOrdered().join(QLatin1String(";")); + + if (! d->getEsriGraphicsOverlayListModel()) + return false; + + if (d->getEsriGraphicsOverlayListModel()->isEmpty()) + return false; + + const QStringList &layernamesOrdered = d->m_orderedMapLayer.layerNamesOrdered(); + + // + // reOrder all GraphicOverlays: + // + DEBUG_LOG(m_logger) << LOGFUNCTION << " layernamesOrdered: " << layernamesOrdered.join(QLatin1String(";")); + + Esri::ArcGISRuntime::GraphicsOverlayListModel *model = d->getEsriGraphicsOverlayListModel(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " model.size() 1= " << model->size(); + + + // N. Thoden, 28.03.2018, V100.2.1 - Consulting with Luke Smallwood: + // Using the testFix for the crashOnStartup, the approach of + // model->clear(); + // // re-append in required new order: + // for ( auto l, newOrderedGraphicsOverlayList) + // model->append(l) + // fails. -> go back to the initial (move) approach + // ==> use MOVE_APPROACH +#define MOVE_APPROACH + + // N. Thoden, 06.04.2018, V100.2.1 - AFTER Consulting with Luke Smallwood: + // Scenario: + // - start opApp, + // - add simple plan/overlay (one ETS point symbol) via Overlay Dialag + // (participants can be picked and MapObjectSelection is opened) + // - remove plan/overlay via Overlay Dialag + // (participants can no longer be picked and MapObjectSelection is opened) + // -> EventMouseNavigator::onGetPickingResult gets empty list from ESRI... + // ==> toogle back to NOT using MOVE_APPROACH +#undef MOVE_APPROACH + + // N. Thoden, 30.08.2018, V100.3.0 - behaviour is now: + // Using the MOVE_APPROACH the picking is working +#if ACRGIS_VERSION >= 0x640300 // 100.3.0 / Update 3 +# define MOVE_APPROACH +#endif + + +#ifdef MOVE_APPROACH + // + // model->move(curIdx, newIdx); seems to be buggy + // (N. Thoden, 29.09.2016, QuartzBeta1) + // + int newIdx = 0; + for (const QString &logicalname : layernamesOrdered) + { + QSharedPointer ll = d->getLogicalLayer(logicalname); + if (ll && ll->basemapType() == BasemapType::Undefined) //BasemapType::Undefined -> GraphicsOverlay + { + QList > pairs = + ll->getCurrentGraphicalOverlaysOrderedWithName(); + + QListIterator< QPair > it(pairs); + + while (it.hasNext()) + { + const QPair &pair = it.next(); + + const QString &esriLayername = pair.first; + Esri::ArcGISRuntime::GraphicsOverlay *graphicalOverlay = pair.second; + + DEBUG_LOG(m_logger) << LOGFUNCTION + << " handle" + << " esriLayername='" << esriLayername << "':"; + + int curIdx = model->indexOf(graphicalOverlay); + + DEBUG_LOG(m_logger) << LOGFUNCTION + << " handle" + << " esriLayername='" << esriLayername << "':" + << " curIdx=" << curIdx << " -> newIdx="<< newIdx; + + if (curIdx < 0) + { + FATAL_LOG(m_logger) << LOGFUNCTION + << " esriLayername='" << esriLayername << "':" + << " not found"; + abort(); + // continue; + } + + if (curIdx != newIdx) + { + DEBUG_LOG(m_logger) << LOGFUNCTION + << " move" + << " GraphicOverlays =" + << " from " << curIdx + << " to " << newIdx << "..." ; + DEBUG_LOG(m_logger) << LOGFUNCTION << " model.size() 2= " << model->size(); + model->move(curIdx, newIdx); + DEBUG_LOG(m_logger) << LOGFUNCTION << " model.size() 3= " << model->size(); + + int retIdx = model->indexOf(graphicalOverlay); + + if (retIdx != newIdx) + { + FATAL_LOG(m_logger) << LOGFUNCTION + << " esriLayername='" << esriLayername << "':" + << " move failed" + << " curIdx='" << curIdx + << " newIdx='" << newIdx + << " retIdx='" << retIdx; + abort(); + } + } + ++newIdx; + } // while + } // if + } // for + +#else + + // build new order of GraphicsOverlays : + QList newOrderedGraphicsOverlayList; + + for (const QString &logicalname : layernamesOrdered) + { + QSharedPointer ll = d->getLogicalLayer(logicalname); + if (ll && ll->basemapType() == BasemapType::Undefined) //BasemapType::Undefined -> GraphicsOverlay + { + QList > pairs = + ll->getCurrentGraphicalOverlaysOrderedWithName(); + + QListIterator< QPair > it(pairs); + + while (it.hasNext()) + { + const QPair &pair = it.next(); + + QString esriLayername = pair.first; + Esri::ArcGISRuntime::GraphicsOverlay *graphicalOverlay = pair.second; + + DEBUG_LOG(m_logger) << LOGFUNCTION + << " handle" + << " esriLayername='" << esriLayername << "'"; + newOrderedGraphicsOverlayList.append(graphicalOverlay); + } // while + } // if + } // for + // : build new order of GraphicsOverlays + + // clear model: + model->clear(); + + // re-append in required new order: + for (auto l : newOrderedGraphicsOverlayList) + model->append(l); + +#endif // MOVE_APPROACH + + DEBUG_LOG(m_logger) << LOGFUNCTION << " model.size() 4= " << model->size(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " orderedList: " << d->m_orderedMapLayer.layerNamesOrdered().join(QLatin1String(";")); + return true; +} + +int LayerFactoryImpl::getEsriBasemapLayerIndex(const QString &esriLayername) const +{ + if (!(d->m_basemap && d->m_basemap->baseLayers())) + return -1; + + const Esri::ArcGISRuntime::LayerListModel *model = d->m_basemap->baseLayers(); + for (int i = 0; i < model->size(); ++i) + { + const Esri::ArcGISRuntime::Layer *curLayer = model->at(i); + const QString &curName = curLayer->name(); + if (QString::compare(esriLayername, curName) == 0) + return i; + } + return -1; +} + +Esri::ArcGISRuntime::Layer *LayerFactoryImpl::getEsriBasemapLayer(const QString &esriLayername) const +{ + int idx = getEsriBasemapLayerIndex(esriLayername); + if (idx < 0) + return nullptr; + // d->m_basemap already checked within 'getEsriBasemapLayerIndex' + Esri::ArcGISRuntime::Layer *layerFound = d->m_basemap->baseLayers()->at(idx); + return layerFound; +} + +QUrl LayerFactoryImpl::completeUrl(const QString &context, + const QUrl &urlIn, + const QString &resourceSubdir) +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " START" + << " context='" << context << "'" + << ", urlIn='" << urlIn.toString() + << " resourceSubdir='" << resourceSubdir << "'"; + + if (!urlIn.isLocalFile()) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " http -> return" + << ", url ='" << urlIn.toString(); + return urlIn; + } + + // assume it´s a file url: + // make absolute path according resourcePath: + QString filePathAbs = urlIn.toLocalFile(); + if (QDir::isRelativePath(filePathAbs)) + filePathAbs = SituationMap::getResourcePath() + resourceSubdir + filePathAbs; + + if (!QFileInfo::exists(filePathAbs)) + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " context='" << context << "'" + << " file '" << filePathAbs << "' doesn't exists"; + return urlIn; + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " END" + << " context='" << context << "'" + << ", filePathAbs='" << filePathAbs << "'"; + return QUrl::fromLocalFile(filePathAbs); +} + + +void LayerFactoryImpl::onLayerParameterChanged(const QString &layername, const LayerParameter &newLayerParameter) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " layername='" << layername << "'" + << ", newLayerParameter='" << newLayerParameter; + d->m_mapImpl->emitLayerParameterChanged(layername, newLayerParameter); +} + +void LayerFactoryImpl::onLayerCreatedOnDemand(const QString &logicalName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " logicalName='" << logicalName << "'"; + this->reOrderLayer(); + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " logicalName='" << logicalName << "'"; +} + +void LayerFactoryImpl::onLayerDoneLoading(const QString &logicalName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " logicalName='" << logicalName << "'"; + + MapLayer mapLayer = d->m_orderedMapLayer.getLayer(logicalName); + if (!mapLayer.isValid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " mapLayer not found" + << " layername='" << logicalName << "'"; + } + + if (mapLayer.type() != LayerType::Basemap) + return; + + QSharedPointer layer = d->getLogicalLayer(logicalName); + if (!layer) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no layer, " + << " layername='" << logicalName << "'"; + return; + } + + // Bugfix : + // REGG-5350 - mit dem normalen Zoom ( Mausrad ) kann man 6 Stufen ansteuern. + // Erst nach der Ums... + // REGG-5338 - In der Live-Lage ist die Zoomstufe nicht + // vollständig nutzbar, das letzte Viertel... + // REGG-5317 - Die Lagekarte der 1. Lageanwendung lässt sich nicht bis + // zur untersten Begrenzung... + + // (Hinein)Zoomen nur bis Scale 4000 moeglich... + // Tritt mit TA Letzlingen und User OHNE Settings sgelgentlich auf. + // Loesungsansatz: Slot-Methoden von synchronen Aufruf zu + // reCalculateMinMaxScaleBasedOnLayers befreien und per Timer aufrufen. + // // reCalculate minMaxScale even if there is just one Basemap: + // // calulcate and set values direct: + // reCalculateMinMaxScaleBasedOnLayers(); + // : Bugfix + + // do via timer: + d->reCalculateMinMaxScaleTimer->start(); // start == restart + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " logicalName='" << logicalName << "'"; +} + +void LayerFactoryImpl::onElevationSourceDoneLoading() +{ + Esri::ArcGISRuntime::ElevationSource *elevationSource = qobject_cast(sender()); + if (elevationSource) + { + INFO_LOG(m_logger) << LOGFUNCTION << " ElevationSource -> doneLoading: " + << " sender= '" << elevationSource->objectName() << "'" + << " loadStatus= '" << elevationSource->loadStatus() << "'" + << " elevationSourceType= " << elevationSource->elevationSourceType(); + + INFO_LOG(m_logger) << LOGFUNCTION << " ElevationSources " << elevationSourcesLoaded(); + + if (elevationSourcesLoaded()) + { + INFO_LOG(m_logger) << LOGFUNCTION << " ElevationSources completed"; + d->m_mapImpl->emitAllElevationSourcesLoaded(); + } + } + else + INFO_LOG(m_logger) << LOGFUNCTION << " ElevationSource -> doneLoading"; + +} + +void LayerFactoryImpl::onElevationSourceErrorOccured(const Esri::ArcGISRuntime::Error &error) +{ + QString senderName; + Esri::ArcGISRuntime::ElevationSource *elevationSource = qobject_cast(sender()); + if (elevationSource) + senderName = elevationSource->objectName(); + + ERROR_LOG(m_logger) << LOGFUNCTION << " ElevationSource -> EsriError occured: " << error + << ", sender= '" << senderName << "'"; +} + +void LayerFactoryImpl::onSurfaceDoneLoading2D() +{ + QString senderName; + Esri::ArcGISRuntime::Surface *surface = qobject_cast(sender()); + if (surface) + senderName = surface->objectName(); + + INFO_LOG(m_logger) << LOGFUNCTION << " Surface -> doneLoading: " + << " sender= '" << senderName << "'"; +} + +void LayerFactoryImpl::onSurfaceErrorOccured2D(const Esri::ArcGISRuntime::Error &error) +{ + QString senderName; + Esri::ArcGISRuntime::Surface *surface = qobject_cast(sender()); + if (surface) + senderName = surface->objectName(); + + ERROR_LOG(m_logger) << LOGFUNCTION << " Surface -> EsriError occured: " << error + << ", sender= '" << senderName << "'"; +} + +void LayerFactoryImpl::reCalculateMinMaxScaleBasedOnLayers() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + const QStringList &layernames = d->m_orderedMapLayer.layerNamesOrdered(LayerType::Basemap); + + double minScale = 0; + double maxScale = 0; + + QList minScales; + QList maxScales; + + + bool initial = true; + + for (const QString &layername : layernames) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " layername" << layername; + + QSharedPointer layer = d->getLogicalLayer(layername); + if (!layer) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no layer, " + << " layername='" << layername << "'"; + continue; + } + + const LayerParameter &layerParameter = layer->layerParameter(); + + DEBUG_LOG(m_logger) << LOGFUNCTION + << " layername='" << layername << "'" + << " minScale=" << layerParameter.minScale + << " maxScale=" << layerParameter.maxScale; + + + // already loaded? ( + if (!layerParameter.minMaxScaleValid()) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " layername" << layername + << " not valid"; + continue; + } + + double localMinScale = layerParameter.minScale; + double localMaxScale = layerParameter.maxScale; + + minScales.append(static_cast(localMinScale)); + maxScales.append(static_cast(localMaxScale)); + + // first time: set initial values: + if (initial) + { + initial = false; + minScale = localMinScale; + maxScale = localMaxScale; + } + else + { + minScale = std::max(minScale, localMinScale); // use std::max for minScale!!! + maxScale = std::min(maxScale, localMaxScale); // use std::min for maxScale!!! + } + } // for + + DEBUG_LOG(m_logger) << LOGFUNCTION + << " a minScale=" << minScale + << " maxScale=" << maxScale; + + // OGC: scales are optional. In this case use '0': + // For MinScale: + if (minScales.contains(0)) + minScale = 0; + + // For MaxScale: + if (maxScales.contains(0)) + maxScale = 0; + + DEBUG_LOG(m_logger) << LOGFUNCTION + << " apply " + << " minScale=" << minScale + << " maxScale=" << maxScale; +// d->m_mapImpl->setMinScale(minScale+1); // why did i wrote this? +// d->m_mapImpl->setMaxScale(maxScale+1); // why did i wrote this? + d->m_mapImpl->setMinScale(minScale); + d->m_mapImpl->setMaxScale(maxScale); +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/layer_factory_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/layer_factory_impl.h new file mode 100755 index 000000000..3363a10a0 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/layer_factory_impl.h @@ -0,0 +1,329 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of LayerFactory + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "map_layer.h" +#include "logical_layer.h" +#include +#include +#include +#include + + +// ESRI +#include +#include +#include +#include +#include +#include +#include + + +// Qt +#include + + +// 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 &uvDataCont) override; + + // For BoobyTraps + bool assignUniqueValueRendererAesCentral(const QString &layername, + const QStringList &fieldNames, + const QVector &uvDataCont) override; + + // For AES / 3D Extrusion + bool assignUniqueValueRendererAesCentral3DExtrusion(const QString &layername, + const QStringList &fieldNames, + const QVector &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 &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(); + QPointerlocationToElevation() 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 initializeElevationSourcesInternal(const QStringList &sceneElevationServiceUrls); + Esri::ArcGISRuntime::ElevationSource* initializeElevationSourceInternal(const QUrl &elevationServiceUrl, int layerCount); + Esri::ArcGISRuntime::RasterElevationSource *initializeRasterElevationSourceInternal(const QStringList &filePaths); + + + QSharedPointer 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 getLogicalLayer(const QString &logicalName) const; + QSharedPointer 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 + +//---------------------------------------------------------------------------- + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_display_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_display_impl.cpp new file mode 100755 index 000000000..bbd78cd31 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_display_impl.cpp @@ -0,0 +1,245 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of LocationDisplay + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "location_display_impl.h" +#include "map_impl.h" +#include +#include + + +// ESRI +#include +#include + +#if ACRGIS_VERSION >= 0x640800 // 100.8.0 / Update 8 +# define HAS_LOCATION_DISPLAY +#endif // ACRGIS_VERSION + +#ifdef HAS_LOCATION_DISPLAY +# include +# include +#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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_display_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_display_impl.h new file mode 100755 index 000000000..150a1d461 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_display_impl.h @@ -0,0 +1,61 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of LocationDisplay + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include // Interface +#include + + +//---------------------------------------------------------------------------- +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 + +//---------------------------------------------------------------------------- diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_to_elevation_bulk.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_to_elevation_bulk.cpp new file mode 100755 index 000000000..a420ec4bd --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_to_elevation_bulk.cpp @@ -0,0 +1,218 @@ +/** + * @RDE_HEADER@ + * LocationToElevationBulk + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "location_to_elevation_bulk.h" +#include +#include "Map/situation_map_utils.h" + +#include "location_to_elevation_helper.h" // to get define USE_ELEVATION_MAP_AS_WORKAROUND + + +// ESRI +#include +#include + + +// Qt +#include +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +//---------------------------------------------------------------------------- +class LocationToElevationBulk::Private +{ +public: + Private() = default; + ~Private() = default; + + + // ESRI data: + QPointer m_surface; // for ESRI-Surface-based calculation + QPointer m_map; // for ElevationMap-based calculation + + Esri::ArcGISRuntime::Point m_location; + + Esri::ArcGISRuntime::SpatialReference m_spatialReference; + + + + QVector> 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(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(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(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> vecIt(d->m_locationDataCont); + while (vecIt.hasNext()) + { + QSharedPointer 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> vecIt(d->m_locationDataCont); + while (vecIt.hasNext()) + { + QSharedPointer 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 > LocationToElevationBulk::results() const +{ + QVector > results; + + QVectorIterator> vecIt(d->m_locationDataCont); + while (vecIt.hasNext()) + { + QSharedPointer ld = vecIt.next(); + if(!ld) + continue; + + const Esri::ArcGISRuntime::Point &location = ld->location(); + QPair 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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_to_elevation_bulk.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_to_elevation_bulk.h new file mode 100755 index 000000000..a508d246a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_to_elevation_bulk.h @@ -0,0 +1,212 @@ +/** + * @RDE_HEADER@ + * LocationToElevationBulk + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "screen_location_elevation_helper_base.h" +#include +#include + +// for ElevationMap-based calculation +#include +#include +#include + + +//ESRI +#include +#include + + +// Qt +#include +#include +#include +#include + + +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(surface); + } + + void setMap(Map *map) + { + m_surface.clear(); + m_map = map; + } + //@} + + + bool startLocationToElevation() + { + // ESRI-Surface-based calculation : + if (m_surface) + { + // SDK v300: locationToElevation() replaced with elevationAsync() + m_taskFuture = m_surface->elevationAsync(m_esriMapLocation); + if (!m_taskFuture.isValid()) + { + qCritical() << " elevationAsync failed, taskFuture invalid"; + return false; + } + // Handle async result with continuation + m_taskFuture.then(this, [this](double elevation) { + m_elevation = elevation; + emit elevationCompleted(); + }); + return true; + } + // : ESRI-Surface-based calculation + + // ElevationMap-based calculation : + if (m_map) + { + auto elevationMap = static_cast(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() + { + // QFuture in Qt6 doesn't have cancel(), but we can check if it's still running + return !m_taskFuture.isFinished(); + } + + 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(); + +private: + // In: + Esri::ArcGISRuntime::Point m_esriMapLocation; + + // processing: + QPointer m_surface; // for ESRI-Surface-based calculation + QPointer m_map; // for ElevationMap-based calculation + + + QFuture m_taskFuture; + + // 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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_to_elevation_helper.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_to_elevation_helper.cpp new file mode 100755 index 000000000..a4fef05c1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_to_elevation_helper.cpp @@ -0,0 +1,198 @@ +/** + * @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 +#endif // USE_ELEVATION_MAP_AS_WORKAROUND + +// ESRI +#include +#include + + +// Qt +#include + + + + + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +//---------------------------------------------------------------------------- +class LocationToElevationHelper::Private +{ +public: + Private() = default; + ~Private() = default; + + + // ESRI data: + QPointer m_surface; + + Esri::ArcGISRuntime::Point m_location; + + Esri::ArcGISRuntime::SpatialReference m_spatialReference; + + QFutureWatcher *m_elevationWatcher = nullptr; + + #ifdef USE_ELEVATION_MAP_AS_WORKAROUND + + CoordTranslationFunctor *m_coordTransFunctor; // ToDo: remove once Esri-Surface will work under windows without crash + QPointer 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(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); + + // Create QFutureWatcher for elevationAsync() results + d->m_elevationWatcher = new QFutureWatcher(this); + connect(d->m_elevationWatcher, &QFutureWatcher::finished, + this, [this]() { + if (d->m_elevationWatcher->future().isValid()) + { + double elevation = d->m_elevationWatcher->future().result(); + emit elevationCompleted(elevation); + } + }); + + 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; + } + + QFuture future = d->m_surface->elevationAsync(Esri::ArcGISRuntime::Point(d->m_location)); + + if (future.isValid()) + { + d->m_elevationWatcher->setFuture(future); + } + else + { + ERROR_LOG(m_logger) << LOGFUNCTION << " elevationAsync failed"; + } +} + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_to_elevation_helper.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_to_elevation_helper.h new file mode 100755 index 000000000..a88f889aa --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/location_to_elevation_helper.h @@ -0,0 +1,81 @@ +/** + * @RDE_HEADER@ + * LocationToElevationHelper + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "screen_location_elevation_helper_base.h" +#include +#include + + +#define USE_ELEVATION_MAP_AS_WORKAROUND // ToDo: remove if Esri-Surface will work without crash + + +// Qt +#include +#include + + +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); + +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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/logical_layer.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/logical_layer.cpp new file mode 100755 index 000000000..56b16ad83 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/logical_layer.cpp @@ -0,0 +1,802 @@ +/** + * @RDE_HEADER@ + * MapLayer + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "logical_layer.h" +#include +#include +#include +#include + + +// ESRI +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// Qt +// #include +#include + + +static_assert((int)SituationMap::SurfacePlacement::DrapedFlat == (int)Esri::ArcGISRuntime::SurfacePlacement::DrapedFlat, "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 : std::as_const(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 LogicalLayer::getCurrentGraphicalOverlaysOrdered() const +{ + QList 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 > LogicalLayer::getCurrentGraphicalOverlaysOrderedWithName() const +{ + QList > ret; + + for (TechnicalLayerType techType : std::as_const(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 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.errorType() == Esri::ArcGISRuntime::ErrorType::CommonNotFound) + 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(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(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(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(dict); + Esri::ArcGISRuntime::DictionarySymbolStyle *esriDict = const_cast(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); + + // setTextVisible removed in ArcGIS SDK v300 - text visibility is now controlled differently + + // 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; + } + } + + +// setSelectionColor removed in ArcGIS SDK v300 - replaced by GeoView::setSelectionProperties() + + 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(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(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 : std::as_const(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 &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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/logical_layer.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/logical_layer.h new file mode 100755 index 000000000..19e9be959 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/logical_layer.h @@ -0,0 +1,190 @@ +/** + * @RDE_HEADER@ + * MapLayer + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include +#include +#include + + +//ESRI +#include +#include +#include +#include + + +// Qt +#include + + +//---------------------------------------------------------------------------- +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 &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 getCurrentGraphicalOverlaysOrdered() const; + + QList> 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::DrapedFlat; + + // visi, opac, brigth, gamma, contrast... + LayerParameter m_layerParameter; + + // to enable/allow GraphicalOverlays and set their order: + QVector m_orderOfGraphicalOverlays; + + // ESRI: + Esri::ArcGISRuntime::Layer *m_layer = nullptr; // store BasemapLayer + QVector 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 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 + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/map_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/map_impl.cpp new file mode 100755 index 000000000..f302232b5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/map_impl.cpp @@ -0,0 +1,5811 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of Map + * $Id$ + * $HeadURL$ + * $Author$ + */ + + +// Own +#include "map_impl.h" +#include "grid_impl.h" +#include "screen_to_location_helper.h" +#include "location_to_elevation_helper.h" +#include "layer_factory_impl.h" +#include "layer_control_widget.h" +#include "coord_translation_functor_impl.h" +#include "object_indicator_impl.h" +#include +#include "jump_point_widget.h" +#include "overview_map_handler_impl.h" +#include "measurement_impl.h" +#include "terrain_profile_impl.h" +#include "viewshed_impl.h" +#include "location_display_impl.h" +#include "pixel_based_textbox_handler_impl.h" + +#include +#include + +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "overlay_handler_impl.h" + +#include +#include + +// NGL +#include + +// Lts +#include + +#include + +// ESRI +#include +#include +#include +#include +#include +#include +#include // enum NavigationConstraint +//#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if ACRGIS_VERSION >= 0x640500 // 100.5.0 +# define HAS_BELOW_GROUND +#endif + +#if ACRGIS_VERSION >= 0x640f00 // 100.15.0 +# define HAS_NETWORK_CACHE_CONFIGURATION +#endif + +#ifdef HAS_NETWORK_CACHE_CONFIGURATION +#include +#include +#endif // HAS_NETWORK_CACHE_CONFIGURATION + + +#include +#include +#include +#include +#include + +// Qt +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + + +#define USE_OWN_EVENTHANDLER + + +static_assert((int)SituationMap::DrawStatus::InProgress == (int)Esri::ArcGISRuntime::DrawStatus::InProgress, "DrawStatus mismatch"); +static_assert((int)SituationMap::DrawStatus::Completed == (int)Esri::ArcGISRuntime::DrawStatus::Completed, "DrawStatus mismatch"); + +#ifdef HAS_BELOW_GROUND +static_assert((int)SituationMap::NavigationConstraint::None == (int)Esri::ArcGISRuntime::NavigationConstraint::None, "NavigationConstraint mismatch"); +static_assert((int)SituationMap::NavigationConstraint::StayAbove == (int)Esri::ArcGISRuntime::NavigationConstraint::StayAbove, "NavigationConstraint mismatch"); +#endif // HAS_BELOW_GROUND + + +namespace SituationMap +{ + +class MyGraphicsPixmapItem final : public QObject, public QGraphicsPixmapItem +{ + Q_OBJECT +public: + explicit MyGraphicsPixmapItem(QGraphicsItem *parent = nullptr) + : QObject(), + QGraphicsPixmapItem(parent) + {} + +signals: + void doubleClicked(); + +protected: + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *) override + { + emit doubleClicked(); + } + +}; // class MyGraphicsPixmapItem +//----------------------------------------------------------------------------- + + +class MyPushButton : public QPushButton +{ + Q_OBJECT +public: + MyPushButton() : QPushButton() {} + + void setIconPale(const QIcon &icon) + { + m_iconPale = icon; + setIcon(m_iconPale); + } + + void setIconOnHover(const QIcon &icon) + { + m_iconOnHover = icon; + } + + void enterEvent(QEnterEvent *e) override + { + setIcon(m_iconOnHover); + QPushButton::enterEvent(e); + } + void leaveEvent(QEvent *e) override + { + setIcon(m_iconPale); + QPushButton::leaveEvent(e); + } + +protected: + QIcon m_iconPale; + QIcon m_iconOnHover; +}; // class MyPushButton +//----------------------------------------------------------------------------- + + + +class MapImpl::Private +{ +public: + Private() = default; + + static int s_counter; + + // ESRI data: + Esri::ArcGISRuntime::Map *m_esriMap = nullptr; + Esri::ArcGISRuntime::Scene *m_esriScene = nullptr; + Esri::ArcGISRuntime::Basemap *m_basemap = nullptr; + + std::shared_ptr m_elevationMap; + std::shared_ptr m_elevationMapManager; + + Esri::ArcGISRuntime::MapGraphicsView *m_mapGraphicsView = nullptr; + Esri::ArcGISRuntime::SceneGraphicsView *m_sceneGraphicsView = nullptr; + ScreenToLocationHelper *m_screenToLocationHelper = nullptr; + + QFutureWatcher *m_exportImageWatcher = nullptr; + + bool m_destroying = false; + bool m_mapReadyFlag = false; + bool m_mapReadyAndSymbolDictionaryEmitted = false; + + Viewpoint2D m_initialViewpoint; + double m_animationDuration = 0.5; + double m_rocketDuration = 2.0; + QTimer *m_rocketTimer = nullptr; + bool m_rocketRunning = false; + + QPointF m_currentCenterPointBeforeRocket; + double m_currentScaleBeforeRocket = 0.0; + Camera m_currentCameraBeforRocket; + + + // PositionLabel + QLabel *m_positionLabel = nullptr; + QString m_positionStr; + QString m_positionElevationStr = QLatin1String("0"); + QColor m_positionLabelForegroundColor; + QColor m_positionLabelBackgroundColor; + QPointer m_locationToElevationHelper; + + + void updatePositionText() + { + if (!m_positionLabel) + return; + m_positionLabel->setText(QStringLiteral("%1 %2"). + arg(m_positionStr). + arg(m_positionElevationStr, 3)); + } + + // ExerciseTimeLabel + QLabel *m_exerciseTimeLabel = nullptr; + QColor m_exerciseTimeLabelForegroundColor; + QColor m_exerciseTimeLabelBackgroundColor; + bool m_isReplay = false; + + // TimeLabel + QLabel *m_timeLabel = nullptr; + QColor m_timeLabelForegroundColor; + QColor m_timeLabelBackgroundColor; + + // Compass + MyGraphicsPixmapItem *m_compass = nullptr; // scene will take ownership. + + + // OWN data: + QVector m_trainingsAreaExtent; + + // Layout, SideBar etc + QStackedLayout *m_stackedLayout = nullptr; + QPointer m_sideBar = nullptr; // optional + QPushButton *m_pbSideBar = nullptr; // optional + JumpPointWidget *m_jumpPointWidget = nullptr; // optional + + // QTextEdit / TextBoxPixel + struct { + QPointer m_label; + QPointer m_textEdit; + QGraphicsRectItem *m_handle_or_proxyControl{nullptr}; + QGraphicsProxyWidget *m_proxy{nullptr}; + QPointer m_sizeGrip; + + void reset() + { + // ToDo: check + // delete without removing from scene/view? + +// qCritical() << " 11"; + delete m_handle_or_proxyControl; + m_handle_or_proxyControl = nullptr; + + // since m_proxy has m_handle_or_proxyControl as parent... + m_proxy = nullptr; + + +// qCritical() << " 33"; + delete m_textEdit; // QPointer + delete m_sizeGrip; // QPointer + +// qCritical() << " fini"; + +// qCritical() << " m_handle_or_proxyControl= " << m_handle_or_proxyControl +// << " m_proxy= " << m_proxy +// << " m_textEdit= " << m_textEdit +// << " m_sizeGrip= " << m_sizeGrip; + + } + + } m_textBoxPixelAndCo; + + + LayerFactoryImpl *m_layerFactoryImpl = nullptr; + + ObjectFactory *m_objectFactory = nullptr; + QSharedPointer m_objectContainer; + CoordTranslationFunctor *m_coordTranslationFunctorInitialize = nullptr; + CoordTranslationFunctor *m_coordTranslationFunctor = nullptr; + + QSharedPointer m_partyIdToColorFunctor; + QTimer *m_partychangedDelayTimer = nullptr; + + QSharedPointer m_overlayHandler; + + SituationMap::GridImpl *m_gridImpl = nullptr; + + + bool m_suppressFirstViewpointChange = true; + double m_pixelsize = -1.0; // meterPerPixel + double m_absoluteScale = -1.0; + + double m_rotationAbsoluteAngleDegrees = 0; + + QTimer *m_maxExtentTimer = nullptr; + + RB::RubberbandFactory *m_rubberbandFactory = nullptr; + + + OverviewMapHandlerImpl *m_overviewMapHandler = nullptr; + + ObjectIndicatorImpl *m_objectIndicatorImpl = nullptr; + LocationDisplayImpl *m_locationDisplayImpl = nullptr; + QPointer m_pixelBasedTextBoxHandlerImpl; + + + MeasurementImpl *m_measurementImpl = nullptr; + TerrainProfileImpl *m_terrainProfileImpl = nullptr; + ViewshedImpl *m_viewshedImpl = nullptr; + + AdvancedSymbology::SymbologyType m_symbologyType = AdvancedSymbology::SymbologyType::None; + double m_symbolScale = 1.0; + + // // 'Optional' / Internal : + // replaced by EventMoseNavigator + RB::EventMouseNavigator *m_mouseEventHandler = nullptr; + + QList> m_dropEventManagerList; + + // : 'Optional' / Internal + + +}; // class MapImpl::Private +//----------------------------------------------------------------------------- + +int MapImpl::Private::s_counter = 0; + +MapImpl::MapImpl(QWidget *parent) + : BaseClass(parent) + , m_logger(getLogger("MapImpl")) + , d(new Private()) +{ + INFO_LOG(m_logger) << LOGFUNCTION << " START, s_counter=" << ++d->s_counter; + + // connect errorHandling: + connect(Esri::ArcGISRuntime::GeometryEngine::instance(), &Esri::ArcGISRuntime::GeometryEngine::errorOccurred, + this, &MapImpl::onErrorGeometryEngine); + + + d->m_basemap = new Esri::ArcGISRuntime::Basemap(this); + d->m_basemap->setObjectName(QLatin1String("Basemap-Basemap")); + //---------------------------------------------------------------------------- + // connect errorHandling: + //---------------------------------------------------------------------------- + connect(d->m_basemap, &Esri::ArcGISRuntime::Map::errorOccurred, + this, &MapImpl::onErrorBasemap); + + + d->m_maxExtentTimer = new QTimer(this); + d->m_maxExtentTimer->setSingleShot(true); + d->m_maxExtentTimer->setInterval(200); + + connect(d->m_maxExtentTimer, &QTimer::timeout, this, [this]() + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " ************** extentChangedDELAYED="; + + if (d->m_destroying) + return; + emit extentChangedDelayed(); // will be sent if zooming/scaling/moving/resizing has finished for a while + }); + + + //---------------------------------------------------------------------------- + // add view to widget: + // (dependency to plugin???) + //---------------------------------------------------------------------------- + // QGridLayout* layout = new QGridLayout(this); + // layout->setObjectName(QLatin1String("MapImpl-layout")); + // layout->setMargin(0); + // layout->addWidget(d->m_mapGraphicsView, 0, 0); + // this->setLayout(layout); + + // StackedLayout: + d->m_stackedLayout = new QStackedLayout(); + d->m_stackedLayout->setStackingMode(QStackedLayout::StackAll); + this->setLayout(d->m_stackedLayout); + + //---------------------------------------------------------------------------- + // set default IdGenerator / IdContainer: + // can be replaced using 'setObjectContainer' + //---------------------------------------------------------------------------- + QSharedPointer idGen(new SituationMap::IdGenerator); + QSharedPointer objectCont(new SituationMap::ObjectContainerCTC(idGen)); + setObjectContainer(objectCont); + + + //---------------------------------------------------------------------------- + // set default partyIdToColorFunctor: + // can be replaced using '' + //---------------------------------------------------------------------------- + d->m_partyIdToColorFunctor = + QSharedPointer(new PartyIdToColorFunctor()); + + d->m_partychangedDelayTimer = new QTimer(this); + + d->m_partychangedDelayTimer->setSingleShot(true); + d->m_partychangedDelayTimer->setInterval(1000); + connect(d->m_partychangedDelayTimer, &QTimer::timeout, this, &MapImpl::onAllPartysChanged); + + + //---------------------------------------------------------------------------- + // setting of further details are continued within 'init D' and 'onMapReady'... + //---------------------------------------------------------------------------- + + TRACE_LOG(m_logger) << LOGFUNCTION << " END " << this; +} + +bool MapImpl::init(const Mode mapMode_2D3D) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " mode=" << mapMode_2D3D; + bool success = false; + switch (mapMode_2D3D) + { + case Mode::Mode_2D: + success = init2D(); + break; + case Mode::Mode_3D: + success = init3D(); + break; + case Mode::Undefined: + { + ERROR_LOG(m_logger) << LOGFUNCTION << " undefined 2D/3D mode"; + return false; + } + } //switch + + this->getEsriGeoView()->setAttributionTextVisible(false); // ToDo Check + +#if ACRGIS_VERSION >= 0x640500 // 100.5.0 / Update 5 + this->getEsriGeoView()->setSelectionProperties(Esri::ArcGISRuntime::SelectionProperties(Qt::magenta)); +#endif // ACRGIS_VERSION + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " mode=" << mapMode_2D3D + << " success=" << success; + + return success; +} + + +// for debugging : +class MyMapGraphicsView : public Esri::ArcGISRuntime::MapGraphicsView +{ + using BaseClass = Esri::ArcGISRuntime::MapGraphicsView; +public: + MyMapGraphicsView(QWidget* parent = nullptr) + : BaseClass(parent) + {} + + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE + { + BaseClass::paintEvent(event); + qCritical() << " MyMapGraphicsView::paintEvent called" + << " type=" << event->type() + << " rect=" << event->rect() + << " region=" << event->region(); + } +}; // class MyMapGraphicsView + +class MySceneGraphicsView : public Esri::ArcGISRuntime::SceneGraphicsView +{ + using BaseClass = Esri::ArcGISRuntime::SceneGraphicsView; +public: + MySceneGraphicsView(QWidget* parent = nullptr) + : BaseClass(parent) + {} + + void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE + { + BaseClass::paintEvent(event); + qCritical() << " MySceneGraphicsView::paintEvent called" + << " type=" << event->type() + << " rect=" << event->rect() + << " region=" << event->region(); + } +}; // class MySceneGraphicsView +// : for debugging + + + +bool MapImpl::init2D() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + d->m_mapReadyFlag = false; + +#ifdef LTS_DEBUG + //---------------------------------------------------------------------------- + // QNetworkProxy + //---------------------------------------------------------------------------- + { + QNetworkProxy proxy = QNetworkProxy::applicationProxy(); + INFO_LOG(m_logger) << LOGFUNCTION << " QNetworkProxy is NOT Empty" + << ", hostname: '" << proxy.hostName() << "'" + << ", port: '" << proxy.port() << "'" + << ", user: '" << proxy.user() << "'" + // << ", password: '" << proxy.password() << "'" + ; + } +#endif + + //---------------------------------------------------------------------------- + // Map + //---------------------------------------------------------------------------- + d->m_esriMap = new Esri::ArcGISRuntime::Map(d->m_basemap, this); + d->m_esriMap->setObjectName(QLatin1String("EsriMap")); + + // the fetch infos (like minMaxScale) of layers: + d->m_esriMap->setAutoFetchLegendInfos(true); + + //---------------------------------------------------------------------------- + // connect some signals: + //---------------------------------------------------------------------------- + connect(d->m_esriMap, &Esri::ArcGISRuntime::Map::errorOccurred, + this, &MapImpl::onErrorMap); + + connect(d->m_esriMap, &Esri::ArcGISRuntime::Map::doneLoading, + this, &MapImpl::onDoneLoading); + + connect(d->m_esriMap->operationalLayers(), &Esri::ArcGISRuntime::LayerListModel::errorOccurred, + this, &MapImpl::onErrorOperationalLayer); + + //---------------------------------------------------------------------------- + // MapGraphicsView + //---------------------------------------------------------------------------- + try + { + // d->m_mapGraphicsView = new Esri::ArcGISRuntime::MapGraphicsView(d->m_esriMap, this); + d->m_mapGraphicsView = new Esri::ArcGISRuntime::MapGraphicsView(this); + d->m_mapGraphicsView->setObjectName(QLatin1String("EsriMapGraphicsView")); + d->m_mapGraphicsView->setFrameShape(QFrame::NoFrame); // frameless +#ifdef USE_OWN_EVENTHANDLER + d->m_mapGraphicsView->viewport()->installEventFilter(this); // filter drag and drop events +#endif // USE_OWN_EVENTHANDLER + d->m_mapGraphicsView->installEventFilter(this); // filter keyboard events + + // filter keyboard events new, for 2D and 3D + connect(d->m_mapGraphicsView, &Esri::ArcGISRuntime::MapGraphicsView::keyPressed, + this, &MapImpl::onKeyPressEvent); + + } + catch (std::exception &stdEx) + { + const char *what = stdEx.what(); + if (what) + { + FATAL_LOG(m_logger) << LOGFUNCTION << "std Exception " + << " what: '" << what << "'"; + } + else + FATAL_LOG(m_logger) << LOGFUNCTION << "std Exception"; + throw; // re-throw + } + catch (...) + { + FATAL_LOG(m_logger) << LOGFUNCTION << "unknown Exception"; + throw; // re-throw + } + + //---------------------------------------------------------------------------- + // Add to StackedLayout: + //---------------------------------------------------------------------------- + d->m_stackedLayout->addWidget(d->m_mapGraphicsView); + + + //---------------------------------------------------------------------------- + // some details: + //---------------------------------------------------------------------------- + d->m_mapGraphicsView->setWrapAroundMode(Esri::ArcGISRuntime::WrapAroundMode::Disabled); + + + // initFeature(); + + // handle rotations controlled by ESRI View + connect( d->m_mapGraphicsView, &Esri::ArcGISRuntime::MapGraphicsView::mapRotationChanged, this, &MapImpl::onEsriRotate ); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + + +bool MapImpl::initFeature() +{ + QString urlStr(QLatin1String("http://sampleserver6.arcgisonline.com/arcgis/rest/services/Energy/Geology/FeatureServer/9")); + urlStr = QLatin1String("http://10.86.144.36/arcgis/rest/services/Bremen/MountingPoints/FeatureServer/0"); + + QUrl featureUrl(urlStr); + + // create the feature table + Esri::ArcGISRuntime::ServiceFeatureTable *featureTable = new Esri::ArcGISRuntime::ServiceFeatureTable(featureUrl, this); + + // create the feature layer using the feature table + Esri::ArcGISRuntime::FeatureLayer *featureLayer = new Esri::ArcGISRuntime::FeatureLayer(featureTable, this); + + // add the feature layer to the map + d->m_esriMap->operationalLayers()->append(featureLayer); + + return true; +} + + +bool MapImpl::init3D() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + d->m_mapReadyFlag = false; + + //---------------------------------------------------------------------------- + // Map + //---------------------------------------------------------------------------- + d->m_esriScene = new Esri::ArcGISRuntime::Scene(d->m_basemap, this); + d->m_esriScene->setObjectName(QLatin1String("EsriScene")); + + + //---------------------------------------------------------------------------- + // connect some signals: + //---------------------------------------------------------------------------- + connect(d->m_esriScene, &Esri::ArcGISRuntime::Scene::errorOccurred, + this, &MapImpl::onErrorScene); + + connect(d->m_esriScene, &Esri::ArcGISRuntime::Scene::doneLoading, + this, &MapImpl::onDoneLoading); + + connect(d->m_esriScene->operationalLayers(), &Esri::ArcGISRuntime::LayerListModel::errorOccurred, + this, &MapImpl::onErrorOperationalLayer); + + // the fetch infos (like minMaxScale) of layers: + d->m_esriScene->setAutoFetchLegendInfos(true); + + //---------------------------------------------------------------------------- + // SceneGraphicsView + //---------------------------------------------------------------------------- + try + { + // 2017-11-02, V100.1 + // restoreOperatorMapSettings (restoreViewpoint will not really work, if the parent is set. + // wierd, but true... + d->m_sceneGraphicsView = new Esri::ArcGISRuntime::SceneGraphicsView(); // no parent, + d->m_sceneGraphicsView->setObjectName(QLatin1String("EsriSceneView")); + d->m_sceneGraphicsView->setFrameShape(QFrame::NoFrame); // frameless + + // filter keyboard events new, for 2D and 3D + connect(d->m_sceneGraphicsView, &Esri::ArcGISRuntime::SceneGraphicsView::keyPressed, + this, &MapImpl::onKeyPressEvent); + } + catch (std::exception &stdEx) + { + const char *what = stdEx.what(); + if (what) + { + FATAL_LOG(m_logger) << LOGFUNCTION << "std Exception " + << " what: '" << what << "'"; + } + else + FATAL_LOG(m_logger) << LOGFUNCTION << "std Exception"; + throw; // re-throw + } + catch (...) + { + FATAL_LOG(m_logger) << LOGFUNCTION << "unknown Exception"; + throw; // re-throw + } + + //---------------------------------------------------------------------------- + // ScreenToLocationHelper + // to reduce the load of screenToLocation, limit the number of calculation to 10/sec + //---------------------------------------------------------------------------- + d->m_screenToLocationHelper = new ScreenToLocationHelper(d->m_sceneGraphicsView); + + connect(d->m_screenToLocationHelper, &ScreenToLocationHelper::mouseScenePositionChanged, + this, &MapImpl::onMouseScenePositionChanged, Qt::UniqueConnection); + + + //---------------------------------------------------------------------------- + // Add to StackedLayout: + //---------------------------------------------------------------------------- + d->m_stackedLayout->addWidget(d->m_sceneGraphicsView); + + + //---------------------------------------------------------------------------- + // some details: + //---------------------------------------------------------------------------- + + // SceneView has no member 'setWrapAroundMode' + // d->m_sceneGraphicsView->setWrapAroundMode(Esri::ArcGISRuntime::WrapAroundMode::Disabled); + + // handle rotations controlled by ESRI View + connect(d->m_sceneGraphicsView, &Esri::ArcGISRuntime::SceneGraphicsView::viewpointChanged, this, &MapImpl::onEsriRotate); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +void MapImpl::onPartyChanged(int changedPartyId) +{ + INFO_LOG(m_logger) << LOGFUNCTION << " START, changedPartyId=" << changedPartyId + << ", this->objectContainer().countObjects()=" << this->objectContainer().countObjects() + << ", isReady = " << this->isReady(); + + // partyIdToColorFunctor signals that colorTable has changed. + + // + // Update UniqueValueRenderer: (for cubes displaying the buildingOwnership + // + if ((this->mode() == Mode::Mode_3D) && this->layerFactory().hasLayernameForCubes()) + this->layerFactory().assignUniqueValueRendererForCubes(); + + + if (this->isReady()) + INFO_LOG(m_logger) << LOGFUNCTION << " do party-update."; + else + { + INFO_LOG(m_logger) << LOGFUNCTION << " skip party-update, because map is not ready yet."; + emit partysChangedDelayed(); + + return; + } + + + // + // clearXmlSymbolCache + // + // maybe on particpant has an XML-Symbol with NBC-Protection=1; + // to force re-creation of that addonSymbol: + symbolDictionary().clearXmlSymbolCache(); + + + // + // Update all MapObjects + // + long objectsToUpdate = 0; + QMutableMapIterator> mapObjIt = this->objectContainer().mutableIterator(); + + while (mapObjIt.hasNext()) + { + mapObjIt.next(); + RefPointer mapObj = mapObjIt.value(); + if (mapObj) + { + bool changed = mapObj->setPartyIdChanged(changedPartyId); + + if (changed) + { + ++objectsToUpdate; + if (! mapObj->updateView()) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed, debugInfo='" << mapObj->debugInfo() << "'"; + } + +// if (changedPartyId >= 0) +// if (mapObj->partyId() != changedPartyId) // ToDo: honor injuryPartyId also +// continue; +// mapObj->setPartyIdChanged(changedPartyId); + +// if (! mapObj->updateView()) +// ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed, debugInfo='" << mapObj->debugInfo() << "'"; + } + } // while + + + INFO_LOG(m_logger) << LOGFUNCTION << " START, partysChangedDelayed " ; + emit partysChangedDelayed(); + + INFO_LOG(m_logger) << LOGFUNCTION << objectsToUpdate << " of " << this->objectContainer().countObjects() + << " were updated"; +} + +void MapImpl::onAllPartysChanged() +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " START"; + onPartyChanged(-1); +} + +void MapImpl::onCheckMapReadyAndSymbolDictionaryLoaded() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (d->m_mapReadyFlag && + (AdvancedSymbology::SymbolDictionary::alreadyExists(d->m_symbologyType) && symbolDictionary().isLoaded() && (symbolDictionary().symbologySpecificCount()>0)) && + d->m_layerFactoryImpl->elevationSourcesLoaded() + ) + { + if (!d->m_mapReadyAndSymbolDictionaryEmitted) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " ***** emitting mapReadyAndSymbolDictionaryLoaded" + << ": '" << mode() << "'" + << ": '" << symbolDictionary().symbologyType() << "'" + << " (" << symbolDictionary().symbologySpecificCount() + << " + " + << symbolDictionary().etsSymbolCount() + << " + " + << symbolDictionary().xmlSymbolCount() << " = " + << symbolDictionary().count() << ")"; + emit mapReadyAndSymbolDictionaryLoaded(); + d->m_mapReadyAndSymbolDictionaryEmitted = true; + } + } +} + + +bool MapImpl::fini() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + // Eventhandling: + delete d->m_mouseEventHandler; + + delete d->m_measurementImpl; // ToDo: check + delete d->m_terrainProfileImpl; + delete d->m_viewshedImpl; + + if (d->m_partychangedDelayTimer) + d->m_partychangedDelayTimer->stop(); + + emit beginDestroy(); // to enable destruction of FunctionDialog and its object first + + // cleanup overlays: + if (d->m_overlayHandler) + { + INFO_LOG(m_logger) << LOGFUNCTION + << " cleanup " << d->m_overlayHandler->overlayCount() << " overlays..."; + d->m_overlayHandler->removeAllOverlays(); + } + else + { + INFO_LOG(m_logger) << LOGFUNCTION + << " no overlays to cleanup -> skip"; + } + + // cleanup overviewMap: + if (d->m_overviewMapHandler) + { + INFO_LOG(m_logger) << LOGFUNCTION + << " cleanup overviewMap"; + d->m_overviewMapHandler->removeAll(); + } + + // SideBar: + // (remove embedded widgets like JumpPointWidget and is JumpPoints from the map) + removeSideBar(); + + + // ObjectFactory: + int numberOfObjects = d->m_objectFactory->count(); + INFO_LOG(m_logger) << LOGFUNCTION + << " numberOfObjects at the moment = " << numberOfObjects; + + if (numberOfObjects > 0) + d->m_objectFactory->clearObjects(); + + // Verify: + numberOfObjects = d->m_objectFactory->count(); + INFO_LOG(m_logger) << LOGFUNCTION << " numberOfObjects remaining = " << numberOfObjects; + + // deleting the LayerFactory may cause changing to DratStatus::InProgress: + delete d->m_layerFactoryImpl; + delete d->m_gridImpl; + delete d->m_rubberbandFactory; + + // The static MapSingleton::getFactoryMap() should be cleaned up + // via 'qAddPostRoutine(cleanupFactoryMap);' + delete d->m_objectFactory; + + delete d->m_coordTranslationFunctorInitialize; + + // cleanup/detach ElevationMap + d->m_elevationMap = nullptr; + d->m_elevationMapManager = nullptr; + + INFO_LOG(m_logger) << LOGFUNCTION << " drawStatus = " << this->drawStatus(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +void MapImpl::fini2D() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + fini(); + delete d->m_esriMap; + d->m_esriMap = nullptr; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} +void MapImpl::fini3D() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + fini(); + + INFO_LOG(m_logger) << LOGFUNCTION << " drawStatus = " << this->drawStatus(); + + delete d->m_sceneGraphicsView; + d->m_sceneGraphicsView = nullptr; + // delete d->m_esriScene; + INFO_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +bool MapImpl::load() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (d->m_mapGraphicsView && d->m_sceneGraphicsView) + { + FATAL_LOG(m_logger) << LOGFUNCTION << " Upps, 2D(Map) and also 3D (Scene) -> error!!! "; + FATAL_LOG(m_logger) << LOGFUNCTION << " Upps, 2D(Map) and also 3D (Scene) -> error!!! "; + FATAL_LOG(m_logger) << LOGFUNCTION << " Upps, 2D(Map) and also 3D (Scene) -> error!!! "; + } + + // + // setMap + // + // will start esris cascading load dependecies... + + if (d->m_mapGraphicsView) + d->m_mapGraphicsView->setMap(d->m_esriMap); + + // or + + if (d->m_sceneGraphicsView) + d->m_sceneGraphicsView->setArcGISScene(d->m_esriScene); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +Mode MapImpl::mode() const +{ + Mode mode = Mode::Undefined; + if (d->m_esriMap && (!d->m_esriScene)) + mode = Mode::Mode_2D; + else if (!d->m_esriMap && d->m_esriScene) + mode = Mode::Mode_3D; + + return mode; +} + +void MapImpl::setParent(QWidget *parent, bool bBeginDestroyFini) +{ + QWidget::setParent(parent); + + if (bBeginDestroyFini) + { + // GUEZE-791 - RPL Nr 791: Arbeitsspeicherleck + // If map at 3D is cached and going to be unused, destroy + // some high level objects: + // see ::fini() + + delete d->m_measurementImpl; + d->m_measurementImpl = nullptr; + + delete d->m_terrainProfileImpl; + d->m_terrainProfileImpl = nullptr; + + delete d->m_viewshedImpl; + d->m_viewshedImpl = nullptr; + + delete d->m_objectIndicatorImpl; + d->m_objectIndicatorImpl = nullptr; + + emit beginDestroy(); + + // cleanup overlays: + if (d->m_overlayHandler) + { + INFO_LOG(m_logger) << LOGFUNCTION + << " cleanup " << d->m_overlayHandler->overlayCount() << " overlays..."; + d->m_overlayHandler->removeAllOverlays(); + } + else + { + INFO_LOG(m_logger) << LOGFUNCTION + << " no overlays to cleanup -> skip"; + } + + int numberOfObjects = d->m_objectFactory->count(); + INFO_LOG(m_logger) << LOGFUNCTION + << " numberOfObjects remaining = " << numberOfObjects; + + } +} + +void MapImpl::installViewPortEventFilter(QObject *eventFilter) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + if (! eventFilter) + return; + d->m_mapGraphicsView->viewport()->installEventFilter(eventFilter); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} // void MapImpl::installViewPortEventFilter( QObject * eventFilter ) + + +void MapImpl::removeViewPortEventFilter(QObject *eventFilter) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + if (! eventFilter) + return; + d->m_mapGraphicsView->viewport()->removeEventFilter(eventFilter); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +QPointer MapImpl::createMapNotifier() const +{ + MapImpl *THIS = const_cast(this); + return MapNotifier::createMapNotifier(THIS, THIS); +} + + +void MapImpl::onLoadStatusChanged(Esri::ArcGISRuntime::LoadStatus loadStatus) +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " loadStatus changes to '" << loadStatus << "'"; +} + +void MapImpl::onDoneLoading(const Esri::ArcGISRuntime::Error &loadError) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START " << this + << " d->m_mapGraphicsView=" << d->m_mapGraphicsView + << " d->m_sceneGraphicsView=" << d->m_sceneGraphicsView; + + if (! loadError.isEmpty()) + ERROR_LOG(m_logger) << LOGFUNCTION << " loadError: " << loadError; + + if (d->m_mouseEventHandler) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " already loaded, return"; + return; + } + + // d->m_absoluteScale = d->m_mapGraphicsView->mapScale(); // update d->m_absoluteScale + + // filter mouse events to navigate the map + +#ifdef USE_OWN_EVENTHANDLER + + { + switch (this->mode()) + { + case Mode::Mode_2D: + d->m_mouseEventHandler = new RB::EventMouseNavigator(d->m_mapGraphicsView->viewport(), this); + break; + case Mode::Mode_3D: + d->m_mouseEventHandler = new RB::EventMouseNavigator(d->m_sceneGraphicsView->viewport(), this); + break; + default: + // empty + break; + } // switch + + if (d->m_mouseEventHandler) + { + // handle mouse move events + connect(d->m_mouseEventHandler + , &RB::EventMouseNavigator::mouseMovedEvent + , this + , &MapImpl::onHandleMouseMove); + + connect(d->m_mouseEventHandler + , &RB::EventMouseNavigator::selectionRequest + , this + , &MapImpl::onClickMapObject); + + connect(d->m_mouseEventHandler + , &RB::EventMouseNavigator::contextMenuRequest + , this + , &MapImpl::onContextClick); + +#ifdef SITUATIONMAP_OWN_WHEEL_HANDLING + connect(d->m_mouseEventHandler + , &RB::EventMouseNavigator::mouseWheelEvent + , this + , &MapImpl::onWheelZoom); +#endif // SITUATIONMAP_OWN_WHEEL_HANDLING + + if (d->m_sideBar && d->m_pbSideBar) + { + connect(d->m_mouseEventHandler, &RB::EventMouseNavigator::selectionRequest, this, [this] + (const QList< SituationMap::UniqueMapId > & //idList + , const QPointF & viewPortPos) + { + // ignore event, if viewPortPos is at m_pbSideBar + // (In that case the SideBar is currently opening...) + QRect rect = d->m_pbSideBar->rect(); + rect.moveTo(d->m_pbSideBar->pos()); + + if (! rect.contains(viewPortPos.toPoint())) + d->m_sideBar->hideAnimated(); + }); + + } // if (d->m_sideBar && d->m_pbSideBar) + } // if (d->m_mouseEventHandler) + } +#endif // USE_OWN_EVENTHANDLER + + + + //---------------------------------------------------------------------------- + // set CoordTranslationFunctor to default within Combat Training Centers (CTC) + // (SLSE) + //---------------------------------------------------------------------------- + if (! d->m_coordTranslationFunctorInitialize) + d->m_coordTranslationFunctorInitialize = new CoordTranslationFunctorImpl(this); + + if (! d->m_coordTranslationFunctor) + d->m_coordTranslationFunctor = d->m_coordTranslationFunctorInitialize; + + INFO_LOG(m_logger) << LOGFUNCTION << " ***** CoordTranslationFunctor set" << " (" << d->m_coordTranslationFunctor << ")"; + INFO_LOG(m_logger) << LOGFUNCTION << " ***** wkid=" << this->getEsriSpatialReference().wkid() << " *****"; + INFO_LOG(m_logger) << LOGFUNCTION << " ***** wkt='" << this->getEsriSpatialReference().wkText() << "' *****"; + + + if (this->getEsriSpatialReference().wkid() >= 0) + { + // + // set map ready: + // + INFO_LOG(m_logger) << LOGFUNCTION << " ***** emitting mapReady" + << " (wkid = " << this->getEsriSpatialReference().wkid() + << ", mode = " << this->mode() + << ", minScale = " << this->minScale() + << ", maxScale = " << this->maxScale() + << ", this=" << this + << ", ThreadId=" << QThread::currentThreadId() + << ")"; + + d->m_mapReadyFlag = true; + emit mapReady(); + + // maybe SymbolDictionary was loaded earlier than map: + onCheckMapReadyAndSymbolDictionaryLoaded(); + + + } // if (this->getEsriSpatialReference().wkid() >= 0) + else + ERROR_LOG(m_logger) << LOGFUNCTION << " wkid failed, wkid=" << this->getEsriSpatialReference().wkid() << " *****"; + + + // Problems with order: + // mapReady and "connect viewpointChanged" -> ok + // "connect viewpointChanged" and mapReady -> NOT ok + + // connect some signals: + if (d->m_mapGraphicsView) + { + connect(d->m_mapGraphicsView, &Esri::ArcGISRuntime::MapGraphicsView::viewpointChanged, this, &MapImpl::onViewpointChanged); + connect(d->m_mapGraphicsView, &Esri::ArcGISRuntime::MapGraphicsView::rectChanged, this, &MapImpl::onRectChanged); + } + if (d->m_sceneGraphicsView) + { + connect(d->m_sceneGraphicsView, &Esri::ArcGISRuntime::SceneGraphicsView::viewpointChanged, this, &MapImpl::onViewpointChanged); + connect(d->m_sceneGraphicsView, &Esri::ArcGISRuntime::SceneGraphicsView::rectChanged, this, &MapImpl::onRectChanged); + } + + // check mapReady and SymbolDictionary::loaded + onCheckMapReadyAndSymbolDictionaryLoaded(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void MapImpl::onMouseScenePositionChanged(const Point3D &locationScene, bool isValid) +{ + if (d->m_destroying) + return; + + // 1 + emit mouseMapPositionChanged(locationScene, isValid); + + SituationMap::CoordTranslationFunctor *mapCtFunctor = coordTranslationFunctor(); + if (mapCtFunctor) + { + if (isSignalConnected(QMetaMethod::fromSignal(&Map::mouseGeoPositionChanged))) + { + // 2 + emit mouseGeoPositionChanged(mapCtFunctor->mapToGeographic(locationScene), isValid); + } + } +} + +void MapImpl::onRubberbandPickMapObjectsApply(RB::RubberbandBase *rubberband) +{ + if (d->m_destroying) + return; + + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool mapObjectIdsPicked = isSignalConnected(QMetaMethod::fromSignal(&Map::mapObjectIdsPickedByArea)); + bool mapObjectsPicked = isSignalConnected(QMetaMethod::fromSignal(&Map::mapObjectsPickedByArea)); + + // // cehck if any signal is connected: + if (!(mapObjectIdsPicked || mapObjectsPicked)) + return; + + QList pickedMapObjectIdList; + RefPointerMapObjectList pickedMapObjects; + + const QVector &mapCoordVec = rubberband->coordinateVector(RB::CoordinateSystem::Map); + if (mapCoordVec.size() != 4) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " failure, coordVec.size()=" << mapCoordVec.size(); + return; + } + + // create esriHull for geometryIsInHull ; create esriLine for geometryIntersect + // + // if required : + // Esri::ArcGISRuntime::PolylineBuilder polylineBuilder(this->getEsriSpatialReference()); + // polylineBuilder.addPoint(mapCoord.x(), mapCoord.y()); + // mapObj->geometryIntersect(esriLine) + // : if required + + + Esri::ArcGISRuntime::PolygonBuilder polygonBuilder(this->getEsriSpatialReference()); + for (const QPointF &mapCoord : mapCoordVec) + polygonBuilder.addPoint(mapCoord.x(), mapCoord.y()); + const Esri::ArcGISRuntime::Geometry &esriHull = polygonBuilder.toGeometry(); + + // check any MapObject + const QList allMapObj = this->objectContainer().getAllObjects(); + pickedMapObjectIdList.reserve(allMapObj.size()); + pickedMapObjects.reserve(allMapObj.size()); + + for (AbstractMapObject *mapObj : allMapObj) + { + if (!mapObj) + continue; + + if (!mapObj->pickable()) + continue; + + if (mapObj->geometryIsInHull(esriHull)) + { + pickedMapObjectIdList.append(mapObj->uniqueMapId()); + pickedMapObjects.append(RefPointer(mapObj)); + } + } + + // emit + if (mapObjectIdsPicked) + emit mapObjectIdsPickedByArea(pickedMapObjectIdList); + if (mapObjectsPicked) + emit mapObjectsPickedByArea(pickedMapObjects); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +MapImpl::~MapImpl() +{ + INFO_LOG(m_logger) << LOGFUNCTION << " START, s_counter=" << d->s_counter--; + + d->m_destroying = true; + + if (d->m_maxExtentTimer) + d->m_maxExtentTimer->stop(); + delete d->m_maxExtentTimer; + d->m_maxExtentTimer = nullptr; + + delete d->m_rocketTimer; + d->m_rocketTimer = nullptr; + + if (d->m_screenToLocationHelper) + { + d->m_screenToLocationHelper->disconnect(this); + INFO_LOG(m_logger) << LOGFUNCTION << " Helper pendingOperationsCount = " << d->m_screenToLocationHelper->pendingOperationsCount(); + delete d->m_screenToLocationHelper; + d->m_screenToLocationHelper = nullptr; + } + + if (d->m_locationToElevationHelper) + { + d->m_locationToElevationHelper->disconnect(this); + INFO_LOG(m_logger) << LOGFUNCTION << " Helper pendingOperationsCount = " << d->m_locationToElevationHelper->pendingOperationsCount(); + delete d->m_locationToElevationHelper; + d->m_locationToElevationHelper = nullptr; + } + + delete d->m_objectIndicatorImpl; + d->m_objectIndicatorImpl = nullptr; + + delete d->m_locationDisplayImpl; + d->m_locationDisplayImpl = nullptr; + + switch(mode()) + { + case Mode::Mode_2D: fini2D(); break; + case Mode::Mode_3D: fini3D(); break; + case Mode::Undefined: fini(); break; + } + + delete d->m_basemap; + d->m_basemap = nullptr; + delete d; + + INFO_LOG(m_logger) << LOGFUNCTION << " END " << this; +} + + +double MapImpl::animationDuration() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return d->m_animationDuration; +} + +void SituationMap::MapImpl::setAnimationDuration(double durationSec) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " durationSec=" << durationSec; + d->m_animationDuration = durationSec; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +double SituationMap::MapImpl::rocketDuration() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return d->m_rocketDuration; +} + +void SituationMap::MapImpl::setRocketDuration(double durationSec) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " durationSec=" << durationSec; + d->m_rocketDuration = durationSec; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +//class MyLabel : public QLabel +//{ +//public: +// void hideEvent(QHideEvent* event) override +// { +// qWarning() << LOGFUNCTION << " type=" << event->type(); +// QLabel::hideEvent(event); +//// abort(); +// } + +// bool event(QEvent *e) +// { +// qWarning() << LOGFUNCTION << " type=" << e->type(); +// return QLabel::event(e); +// } + +// void updateGeometry() +// { +// qWarning() << LOGFUNCTION; +// QLabel::updateGeometry(); +// } +//}; + + + + + +void MapImpl::addMapWidget() +{ + // return addMapWidget_OwnWidget(); + // return this->addMapWidget_AddToScene(); + + if (this->isReady()) + this->addMapWidget_AddToScene(); + else + { + connect(this, &Map::mapReady, this, [this]() + { + this->addMapWidget_AddToScene(); + }); + } +} + +bool MapImpl::setTextBox(const PixelTextBoxData &ptbd, bool enableGeometryModifications) +{ + FATAL_LOG(m_logger) << LOGFUNCTION << " XXXXXXXXXXXXXXXXXX START" + << " enableGeomtryUpdate " << enableGeometryModifications + << ", ptbd " << ptbd.toDebugString(); + + if (ptbd.m_htmlText.isEmpty()) + return false; + + QGraphicsView *gView = this->getQGraphicsView(); + if (!gView) + return false; + + d->m_textBoxPixelAndCo.reset(); + + if (!d->m_textBoxPixelAndCo.m_textEdit) + { + d->m_textBoxPixelAndCo.m_textEdit = new QTextEdit(); + d->m_textBoxPixelAndCo.m_textEdit->setObjectName(QStringLiteral("WidgetOverlay_QTextEdit")); + d->m_textBoxPixelAndCo.m_textEdit->setReadOnly(false); + + d->m_textBoxPixelAndCo.m_label = new QLabel(QStringLiteral("new QLabel")); + + // Make it 'modifiable' + if (enableGeometryModifications) + { + // 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???))) + d->m_textBoxPixelAndCo.m_handle_or_proxyControl = new QGraphicsRectItem(0, 0, 0, 0); // x,y == rel position, size will be set later + d->m_textBoxPixelAndCo.m_handle_or_proxyControl->setCursor(Qt::SizeAllCursor); + + d->m_textBoxPixelAndCo.m_proxy = new QGraphicsProxyWidget(d->m_textBoxPixelAndCo.m_handle_or_proxyControl); + d->m_textBoxPixelAndCo.m_proxy->setObjectName(QStringLiteral("ProxyWidgetOverlay_QTextEdit")); + d->m_textBoxPixelAndCo.m_proxy->setWidget(d->m_textBoxPixelAndCo.m_textEdit); +// d->m_textBoxPixelAndCo.m_proxy->setWidget(d->m_textBoxPixelAndCo.m_label); + + d->m_textBoxPixelAndCo.m_proxy->setPos(QPoint(0, 0)); // relative position to GraphicsItem !? + + d->m_textBoxPixelAndCo.m_proxy->setOpacity(0.99); // avoid white on white + // d->m_textBoxPixelAndCo.m_proxy->setZValue(std::numeric_limits::max()); + + d->m_textBoxPixelAndCo.m_handle_or_proxyControl->setPen(QPen(Qt::gray)); + d->m_textBoxPixelAndCo.m_handle_or_proxyControl->setBrush(QBrush(Qt::gray)); + d->m_textBoxPixelAndCo.m_handle_or_proxyControl->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable); + + this->getQGraphicsView()->scene()->addItem(d->m_textBoxPixelAndCo.m_handle_or_proxyControl); + + // restore geometry: + d->m_textBoxPixelAndCo.m_handle_or_proxyControl->setPos(ptbd.m_position); + + // SizeGrip : + QSizeGrip *sizeGrip = new QSizeGrip(d->m_textBoxPixelAndCo.m_textEdit); + QHBoxLayout *layout = new QHBoxLayout(d->m_textBoxPixelAndCo.m_textEdit); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(sizeGrip, 0, Qt::AlignRight | Qt::AlignBottom); + // : SizeGrip + + { + // Sync geometry of TextEdit and GraphicsItem/ProxyWidget: + // create fn... + std::function fnUpdateGraphicsItemGeometry = [this]() + { + d->m_textBoxPixelAndCo.m_handle_or_proxyControl->setRect( d->m_textBoxPixelAndCo.m_textEdit->geometry().adjusted(-10, -10, 10, 10) ); + +// qCritical() << " handle_or_proxyControl = " << d->m_textBoxPixelAndCo.m_handle_or_proxyControl->pos(); +// qCritical() << " d->m_textEdit->geometry() = " << d->m_textBoxPixelAndCo.m_textEdit->geometry(); + }; + + // ... to be executed initially + fnUpdateGraphicsItemGeometry(); + + // ... and on geometry changes + // QTextEdit has no geometryChanged or similar, but ProxyWidget has: + connect(d->m_textBoxPixelAndCo.m_proxy, &QGraphicsProxyWidget::geometryChanged, fnUpdateGraphicsItemGeometry); + } + + { + // get pos and geometry; + std::function fnGetPosAndGeometry = [this]() + { +// qCritical() << " 222 handle_or_proxyControl pos = " << handle_or_proxyControl->pos(); +// qCritical() << " 222 handle_or_proxyControl rect= " << handle_or_proxyControl->rect(); + +// const QPoint &pos = d->m_textBoxPixelAndCo.m_handle_or_proxyControl->pos().toPoint(); + const QSize size(d->m_textBoxPixelAndCo.m_handle_or_proxyControl->rect().width(), + d->m_textBoxPixelAndCo.m_handle_or_proxyControl->rect().height()); + + //maybe take border of 10 pixel into account!? + + +// qCritical() << " 222 handle_or_proxyControl pos= " << pos +// << ", size= " << size; + + +// ptbd.m_position = pos; +// ptbd.m_size = size; + }; + + auto *timer =new QTimer(this); + connect(timer, &QTimer::timeout, fnGetPosAndGeometry); +// timer->start(5000); + } + + } else { +// d->m_textBoxPixelAndCo.m_proxy = this->getQGraphicsView()->scene()->addWidget(d->m_textBoxPixelAndCo.m_textEdit); +// d->m_textBoxPixelAndCo.m_proxy->setOpacity(0.99); +// d->m_textBoxPixelAndCo.m_proxy->setZValue(std::numeric_limits::max()); +// d->m_textBoxPixelAndCo.m_proxy->setPos(QPoint(75, 75)); +// d->m_textBoxPixelAndCo.m_proxy->setObjectName(QStringLiteral("ProxyWidgetOverlay_QTextEdit")); + } + + } + +// qCritical() << " setText= " << ptbd.m_htmlText; + + d->m_textBoxPixelAndCo.m_textEdit->setText(ptbd.m_htmlText); + + d->m_textBoxPixelAndCo.m_proxy->setOpacity(0.99); + +// QTimer::singleShot(5000, this, [this] () { +// FATAL_LOG(m_logger) << LOGFUNCTION << " XXXX timer: reset..."; +// d->m_textBoxPixelAndCo.reset(); +// FATAL_LOG(m_logger) << LOGFUNCTION << " XXXX timer: reset... Done"; +// }); + +// QSizeF size = te->document()->size(); +// te->setFixedWidth(te->document()->idealWidth() + 100); + +// te->setFixedSize(size.toSize()); + +// QGraphicsProxyWidget *proxy = this->getQGraphicsView()->scene()->addWidget(d->m_textEdit); +// proxy->setOpacity(0.99); +// proxy->setZValue(std::numeric_limits::max()); + +// proxy->setPos(QPoint(75, 75)); + +// proxy->setObjectName(QStringLiteral("ProxyWidgetOverlay_QTextEdit")); + +/* + QTimer::singleShot(10000000, this, [this] () { + FATAL_LOG(m_logger) << LOGFUNCTION << " XXXX timer:"; + + qCritical() << " size " << d->m_textEdit->size(); + + auto *handle_or_proxyControl = new QGraphicsRectItem(0, 0, 0, 0); // x,y == rel position + handle_or_proxyControl->setCursor(Qt::SizeAllCursor); + + auto *localProxy = new QGraphicsProxyWidget(handle_or_proxyControl); + localProxy->setWidget(d->m_textEdit); + localProxy->setPos(QPoint(75, 75)); + + // SizeGrip : + QSizeGrip *sizeGrip = new QSizeGrip(d->m_textEdit); + QHBoxLayout *layout = new QHBoxLayout(d->m_textEdit); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(sizeGrip, 0, Qt::AlignRight | Qt::AlignBottom); + // : SizeGrip + + handle_or_proxyControl->setPen(QPen(Qt::yellow)); + handle_or_proxyControl->setBrush(QBrush(Qt::gray)); + handle_or_proxyControl->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable ); + + this->getQGraphicsView()->scene()->addItem(handle_or_proxyControl); + + handle_or_proxyControl->setRect(d->m_textEdit->geometry().adjusted(-10, -10, 10, 10) ); + + connect(d->m_textEdit.data(), &QTextEdit::cursorPositionChanged, [this, handle_or_proxyControl] () + { + handle_or_proxyControl->setRect( d->m_textEdit->geometry().adjusted(-10, -10, 10, 10) ); + }); + + +// QGraphicsWidget* parentWidget = new QGraphicsWidget(handle);//make parent of widget +// parentWidget->setCursor(Qt::SizeAllCursor); +// qCritical() << " size " << d->m_textEdit; +// parentWidget->setGeometry(100, 100, 500, 500); + +// parentWidget->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable ); +// parentWidget->setSelected(true); +// this->getQGraphicsView()->scene()->addItem(handle); + +// localProxy->setParentItem(parentWidget); + + }); +*/ + +/* + // Moving: + + QTimer::singleShot(1000, this, [this, &te] () { + + FATAL_LOG(m_logger) << LOGFUNCTION << " timer:"; + + QGraphicsWidget* parentWidget = new QGraphicsWidget();//make parent of widget + parentWidget->setCursor(Qt::SizeAllCursor); + qCritical() << " size " << te->size(); +// parentWidget->setGeometry(75, 75, te->size().width(), te->size().height()); + parentWidget->setGeometry(75, 75, 200, 200); + parentWidget->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable ); +// parentWidget->setSelected(true); + this->getQGraphicsView()->scene()->addItem(parentWidget); + + QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(); + proxy->setPos(QPoint(75, 75)); + +// QGraphicsSimpleTextItem *gitem = new QGraphicsSimpleTextItem(); +// gitem->setText(QStringLiteral("MyMovableText")); + + proxy->setWidget(te); + proxy->setParentItem(parentWidget); + + }); +*/ + + FATAL_LOG(m_logger) << LOGFUNCTION + << " XXXXXXXXXXXXXXXXXX Done"; + + + return true; +} + +bool MapImpl::removeTextBox() +{ + FATAL_LOG(m_logger) << LOGFUNCTION << "START"; + d->m_textBoxPixelAndCo.reset(); + FATAL_LOG(m_logger) << LOGFUNCTION << "START"; + return true; +} + +void moveLabelTo(QLabel *label, int windowWidth, int borderX, int y) +{ + QPoint newPos; + newPos.rx() = windowWidth - label->width() - borderX; + newPos.ry() = y; + label->move(newPos); +} + +QWidget *MapImpl::addMapWidget_AddToScene() +{ + const QFont font(QLatin1String("Courier New"), 13, QFont::DemiBold); + const QSize labelBorderPixel(5, 2); + const QPoint distanceToFrame(25, 25); + + QFontMetrics fm(font); + QSize labelMinSize; + if (true) // (mode() == Mode::Mode_3D) && layerFactory().elevationAvailable()) + labelMinSize = fm.size(0, QLatin1String("32U MD 12345 12345 - 123")); + else + labelMinSize = fm.size(0, QLatin1String("32U MD 12345 12345")); + + QPoint labelPos = distanceToFrame; + + // PositionLabel + if (1) // Todo: unnecessary if ? + { + if (! d->m_positionLabel) + { + d->m_positionLabel = new QLabel(); + d->m_positionLabel->setObjectName(QLatin1String("WidgetOverlayLayoutPositionLabel")); + d->m_positionLabel->setText(QLatin1String("-")); + d->m_positionLabel->setFont(font); + d->m_positionLabel->setAlignment(Qt::AlignBottom | Qt::AlignCenter); + d->m_positionLabel->setMinimumSize(labelMinSize + labelBorderPixel); + + // Bug iOSQz-000005 'QGraphicsProxyWidget will crash' + // Adding a widget will let the application crash + // Workaround: 'proxy->setOpacity(0.99);' + QGraphicsProxyWidget *proxy = this->getQGraphicsView()->scene()->addWidget(d->m_positionLabel); + proxy->setOpacity(0.99); + proxy->setZValue(std::numeric_limits::max()); + + // lambda :-) + connect(this, &SituationMap::Map::rectChanged, this, [this, distanceToFrame]() + { + moveLabelTo(d->m_positionLabel, getQGraphicsView()->width(), distanceToFrame.x(), d->m_positionLabel->pos().y()); + }); + + // lambda :-) + connect(this, &SituationMap::Map::mouseMapPositionChanged, this, [this](const Point3D &mapPos, bool isValid) + { + if (!this->isReady()) + return; + + if (!isValid) + { + d->m_positionLabel->setText(QStringLiteral("-")); + return; + } + + + // Shift + Strg -> lonLat in DEG + // Shift + Alt -> lonLat in RAD + // default -> MGRS + + if (d->m_positionLabel->isVisible()) + { + QString posStr; + + const Qt::KeyboardModifiers &curModifier = QGuiApplication::queryKeyboardModifiers(); + SituationMap::CoordTranslationFunctor *mapCtFunctor = coordTranslationFunctor(); + if (mapCtFunctor && curModifier.testFlag(Qt::KeyboardModifier::ShiftModifier)) + { + Point3D geoPos3D = mapCtFunctor->mapToGeographic(mapPos); + QPointF geoPos2D = geoPos3D.toPointF(); + if (curModifier.testFlag(Qt::ControlModifier)) + { + posStr = QStringLiteral("%1 - %2").arg(geoPos2D.x()).arg(geoPos2D.y()); + d->m_positionLabel->setToolTip(QLatin1String("lon/x - lat/y ")); + } else if (curModifier.testFlag(Qt::AltModifier)) + { + geoPos2D *= LtsCoordTrans::rad2deg(); // RAD to DEG + posStr = QStringLiteral("%1 - %2").arg(geoPos2D.x()).arg(geoPos2D.y()); + d->m_positionLabel->setToolTip(QLatin1String("lon/x - lat/y")); + } + } else { + d->m_positionLabel->setToolTip(QString()); + } + if (posStr.isEmpty()) + posStr = this->mapToMgrsString(mapPos.toPointF()); + d->m_positionStr = posStr; + + + switch(this->mode()) + { + case Mode::Mode_3D: + { + // 3D: simple -> use z value: + // based on Surface::screenToBaseSurface / Surface::screenToLocation + // + if (qIsNaN(mapPos.z())) + d->m_positionElevationStr = QLatin1String("INV"); + else + { + if (std::lround(mapPos.z()) < 10.0) + d->m_positionElevationStr.setNum(mapPos.z(), 'f', 1); + else + d->m_positionElevationStr.setNum(std::lround(mapPos.z())); + } + d->updatePositionText(); + } + break; + case Mode::Mode_2D: + { + // if elevation is available + if (this->layerFactory().elevationAvailable() && this->isReady()) + { + // 2D: use LocationToElevationHelper: + // 1. do setup: + setupLocationToElevationHelper(); + + // 2. trigger computation: + if (d->m_locationToElevationHelper) + d->m_locationToElevationHelper->locationToElevation(mapPos.toPointF()); + } // if (this->layerFactory().elevationAvailable() && this->isReady()) + else + d->updatePositionText(); // update if elevation is NOT available + } + break; + case Mode::Undefined: + // empty + break; + } //switch + + + + } // if (d->m_positionLabel->isVisible()) + }); // lambda + + } + moveLabelTo(d->m_positionLabel, getQGraphicsView()->width(), distanceToFrame.x(), labelPos.y()); + + + // Prepare next label: + if (d->m_positionLabel->isVisible()) + labelPos.ry() += d->m_positionLabel->minimumHeight(); + } + + // ExerciseTimeLabel + // if (isReplay()) + { + if (! d->m_exerciseTimeLabel) + { + d->m_exerciseTimeLabel = new QLabel(); + d->m_exerciseTimeLabel->setVisible(false); + d->m_exerciseTimeLabel->setObjectName(QLatin1String("WidgetOverlayLayoutExerciseTimeLabel")); + d->m_exerciseTimeLabel->setText(QLatin1String("-")); + d->m_exerciseTimeLabel->setFont(font); + d->m_exerciseTimeLabel->setAlignment(Qt::AlignBottom | Qt::AlignCenter); + d->m_exerciseTimeLabel->setMinimumSize(labelMinSize + labelBorderPixel); + + // Bug iOSQz-000005 'QGraphicsProxyWidget will crash' + // Adding a widget will let the application crash + // Workaround: 'proxy->setOpacity(0.99);' + QGraphicsProxyWidget *proxy = this->getQGraphicsView()->scene()->addWidget(d->m_exerciseTimeLabel); + proxy->setOpacity(0.99); + proxy->setZValue(std::numeric_limits::max()); + + connect(this, &SituationMap::Map::rectChanged, this, [this, distanceToFrame]() + { + moveLabelTo(d->m_exerciseTimeLabel, getQGraphicsView()->width(), distanceToFrame.x(), d->m_exerciseTimeLabel->pos().y()); + }); + } + moveLabelTo(d->m_exerciseTimeLabel, getQGraphicsView()->width(), distanceToFrame.x(), labelPos.y()); + // Prepare next label: + if (d->m_exerciseTimeLabel->isVisible()) + labelPos.ry() += d->m_exerciseTimeLabel->minimumHeight(); + } + + // TimeLabel: + if (0) // Todo: unnecessary if ? + { + if (! d->m_timeLabel) + { + d->m_timeLabel = new QLabel(); + d->m_timeLabel->setObjectName(QLatin1String("WidgetOverlayLayoutTimeStringLabel")); + d->m_timeLabel->setText(QLatin1String("#")); + d->m_timeLabel->setFont(font); + d->m_timeLabel->setAlignment(Qt::AlignBottom | Qt::AlignCenter); + d->m_timeLabel->setMinimumSize(labelMinSize + labelBorderPixel); + + // Bug iOSQz-000005 'QGraphicsProxyWidget will crash' + // Adding a widget will let the application crash + // Workaround: 'proxy->setOpacity(0.99);' + QGraphicsProxyWidget *proxy = this->getQGraphicsView()->scene()->addWidget(d->m_timeLabel); + proxy->setOpacity(0.99); + proxy->setZValue(std::numeric_limits::max()); + + // lambda :-) + connect(this, &SituationMap::Map::rectChanged, this, [this, distanceToFrame]() + { + moveLabelTo(d->m_timeLabel, getQGraphicsView()->width(), distanceToFrame.x(), d->m_timeLabel->pos().y()); + }); + + // by default -> not visible: + d->m_timeLabel->setVisible(false); + } + + moveLabelTo(d->m_timeLabel, getQGraphicsView()->width(), distanceToFrame.x(), labelPos.y()); + + + + // Prepare next label: + if (d->m_timeLabel->isVisible()) + labelPos.ry() += d->m_timeLabel->minimumHeight(); + + } + + // compass + if (this->mode() != Mode::Undefined) + { + if (! d->m_compass) + { + d->m_compass = new MyGraphicsPixmapItem(); // QGraphicsPixmapItem + + // get rde blue: + QColor rdeColor = this->palette().color(QPalette::WindowText); // RDE_DARK_BLUE + QColor bgColor;// = this->palette().color(QPalette::Shadow); // RDE_LIGHTER_GRAY is too pale + if (! bgColor.isValid()) + bgColor = Qt::gray; + bgColor.setAlphaF(0.5); + QImage image = renderCompass(rdeColor, Qt::white, bgColor, 8, 30, this->mode()==Mode::Mode_3D); + d->m_compass->setPixmap(QPixmap::fromImage(image)); + + // QPixmap pixmap(QLatin1String(":/compass.png")); + // d->m_compass->setPixmap(pixmap.scaledToHeight(height)); + + qreal xOffset = d->m_compass->pixmap().width() * 0.5; + qreal yOffset = d->m_compass->pixmap().height() * 0.5; + d->m_compass->setOffset(-xOffset, -yOffset); + + d->m_compass->setZValue(std::numeric_limits::max()); + d->m_compass->setVisible(true); + + d->m_compass->setFlags(QGraphicsItem::ItemIgnoresTransformations); + d->m_compass->setShapeMode(QGraphicsPixmapItem::BoundingRectShape); + + this->getQGraphicsView()->scene()->addItem(d->m_compass); + + // lambda :-) + connect(this, &SituationMap::Map::rectChanged, this, [this, distanceToFrame]() + { + QPoint newPos = d->m_compass->pos().toPoint(); + int maxDimHalf = std::max(d->m_compass->pixmap().width(), + d->m_compass->pixmap().height()) * 0.5; + newPos.rx() = this->getQGraphicsView()->width() - maxDimHalf - distanceToFrame.x(); + d->m_compass->setPos(newPos); + }); + + + if (this->mode() == Mode::Mode_2D) + { + // lambda :-) + connect(this, &Map::rotationChanged, this, [this](double absoluteAngleDegrees) + { + + // QGraphicsItem: + // Sets the clockwise rotation angle, in degrees, around the Z axis. + // The default value is 0 (i.e., the item is not rotated). + // Assigning a negative value will rotate the item counter-clockwise. + // Normally the rotation angle is in the range (-360, 360), + // but it's also possible to assign values outside of this range + // (e.g., a rotation of 370 degrees is the same as a rotation of 10 degrees). + + // -> use values directly: + d->m_compass->setRotation(absoluteAngleDegrees); + }); + } else if (this->mode() == Mode::Mode_3D) + { + // lambda :-) + connect(this, &Map::extentChanged, this, [this]() + { + const Esri::ArcGISRuntime::Camera &cam = d->m_sceneGraphicsView->currentViewpointCamera(); + d->m_compass->setRotation(360.0 - cam.heading()); + }); + } + + // lambda :-) + connect(d->m_compass, &MyGraphicsPixmapItem::doubleClicked, this, [this]() + { + if (mode() == Mode::Mode_2D) + { + if (qApp->keyboardModifiers().testFlag(Qt::ShiftModifier)) + { + const Esri::ArcGISRuntime::Envelope &fullExtent = this->layerFactoryImpl().fullExtentOfBasemaps(); + d->m_mapGraphicsView->setViewpointAsync(Esri::ArcGISRuntime::Viewpoint(fullExtent)); + } else if (qApp->keyboardModifiers().testFlag(Qt::ControlModifier)) + { + this->zoomToArea(d->m_trainingsAreaExtent, 50); + } else { + Viewpoint2D vp = this->viewpoint2D(); + vp.setRotation(0.0); + this->setViewpoint2D(vp); + } + } + else if(mode() == Mode::Mode_3D) + { + if (qApp->keyboardModifiers().testFlag(Qt::ShiftModifier)) + { + const Esri::ArcGISRuntime::Envelope &fullExtent = this->layerFactoryImpl().fullExtentOfBasemaps(); + d->m_sceneGraphicsView->setViewpointAsync(Esri::ArcGISRuntime::Viewpoint(fullExtent)); + } else if (qApp->keyboardModifiers().testFlag(Qt::ControlModifier)) + { + this->zoomToArea(d->m_trainingsAreaExtent, 50); + } else { + Camera cam = this->camera(); + cam.heading = 0.0; + cam.pitch = 0.0; + cam.roll = 0.0; + this->setCamera(cam); + } + } + }); + + } // if (! d->m_compass) + + QPoint newPos = labelPos; + newPos.ry() += 10; // small distance + int maxDimHalf = std::max(d->m_compass->pixmap().width(), + d->m_compass->pixmap().height()) * 0.5; + newPos.ry() += maxDimHalf; + newPos.rx() = this->getQGraphicsView()->width() - maxDimHalf - distanceToFrame.x(); + d->m_compass->setPos(newPos); + } + else + DEBUG_LOG(m_logger) << LOGFUNCTION << " compass not supported for 3D yet"; + + // to trigger all 'consumer' + emitVisualComponentChanged(VisualComponent::PosititionString); + emitVisualComponentChanged(VisualComponent::ExerciseTimeString); + emitVisualComponentChanged(VisualComponent::TimeString); + emitVisualComponentChanged(VisualComponent::CompassRose); + + return nullptr; +} + +void MapImpl::setupLocationToElevationHelper() +{ + if (d->m_locationToElevationHelper) + return; + + auto elevationMap = this->getElevationMapManager(); + if (!elevationMap) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " no ElevationMapManager"; + return; + } + + d->m_locationToElevationHelper = this->layerFactory().locationToElevation(); // layerFactory has ownership + if (d->m_locationToElevationHelper) + { + // lambda :-) + connect(d->m_locationToElevationHelper.data(), &SituationMap::LocationToElevationHelper::elevationCompleted, + this, [this](double elevation) + { + // ElevationMap return std::numeric_limits::min() or similar if location is not covered: + if (qIsNaN(elevation) || elevation < -30000) + d->m_positionElevationStr = QLatin1String("-"); + else + if (std::lround(elevation) < 10.0) + d->m_positionElevationStr.setNum(elevation, 'f', 1); + else + d->m_positionElevationStr.setNum(std::lround(elevation)); + d->updatePositionText(); + }); + } +} + + +#ifdef NEVER +class MyWidget : public QWidget +{ + Q_OBJECT +public: + MyWidget() + : QWidget() + , m_targetWidget(nullptr) + , m_enable(false) + {} + + void setTargetWidget(QWidget *targetWidget) + { + m_targetWidget = targetWidget; + } + + void enable() + { + qWarning() << " target is enabled now"; + m_enable = true; + } + + bool event(QEvent *e) override + { + qWarning() << " e=" << e; + + if (!m_enable) + return QWidget::event(e); + + + if (e->type() == QEvent::MouseButtonDblClick + || e->type() == QEvent::MouseMove + || e->type() == QEvent::MouseButtonPress + || e->type() == QEvent::MouseButtonRelease) + { + // Mouse events are forwarded to the underlying widget + QMouseEvent *event = static_cast(e); + + qWarning() << " event.pos=" << event->pos(); + + QWidget *child = childAt(event->pos()); + qWarning() << " child=" << child; + + // no own child widgets found: + if (! child) + { + QMouseEvent shiftedEvent(event->type(), event->pos(), + event->globalPos(), event->button(), event->buttons(), + event->modifiers()); + + qWarning() << " forwarding event=" << e; + // -> send event to 'underlying' widget: + QApplication::sendEvent(m_targetWidget, &shiftedEvent); + } + + } + // own child widgets found -> handle event normally: + return QWidget::event(e); + } + +private: + QWidget *m_targetWidget; + bool m_enable; +}; +#endif + + +QWidget *MapImpl::addMapWidget_OwnWidget() +{ +#ifdef NEVER + const QFont font(QLatin1String("Courier New"), 13, QFont::DemiBold); + const QSize labelBorderPixel(5, 2); + // const QPoint distanceToFrame (25,25); + + QFontMetrics fm(font); + //const QString defaultPostitionLabelStr(QLatin1String("32 U MD 12345 12345")); + const QString defaultTimeStr(QLatin1String("01234567890123456789")); + QSize labelMinSize = fm.size(0, defaultTimeStr); + + + MyWidget *widget = new MyWidget(); + // widget->setStyleSheet(QLatin1String("* {background-color: yellow;}")); + QHBoxLayout *layout = new QHBoxLayout(); + widget->setLayout(layout); + + + widget->setTargetWidget(d->m_stackedLayout->currentWidget()); + + QTimer *timer = new QTimer(); + timer->setSingleShot(true); + timer->setInterval(2000); + + connect(timer, &QTimer::timeout, widget, [widget]() + { + widget->enable(); + }); + + timer->start(); + + + // add WidgetOverlayLayout and make it current: + int idx = d->m_stackedLayout->addWidget(widget); + d->m_stackedLayout->setCurrentIndex(idx); + + d->m_positionLabel = new QLabel(); + d->m_positionLabel->setObjectName(QLatin1String("WidgetOverlayLayoutPositionLabel")); + d->m_positionLabel->setStyleSheet(QLatin1String("QLabel {background-color: gray;}")); + d->m_positionLabel->setText(QLatin1String("-")); + d->m_positionLabel->setFont(font); + d->m_positionLabel->setAlignment(Qt::AlignBottom | Qt::AlignCenter); + d->m_positionLabel->setMinimumSize(labelMinSize + labelBorderPixel); + d->m_positionLabel->setMaximumSize(this->minimumSize()); + + // QSpacerItem *spacer = new QSpacerItem(10, 1, QSizePolicy::Maximum, QSizePolicy::Maximum); + // layout->addItem(spacer, 0,0); + + layout->addStretch(2); + layout->addWidget(d->m_positionLabel); + +#endif + return nullptr; +} + +QImage SituationMap::MapImpl::renderCompass(const QColor &colorNorth, + const QColor &colorSouth, + const QColor &colorBackground, + int halfWidth, int halfHeight, + bool b_3D) +{ + int width = halfWidth * 2; + int height = halfHeight * 2; + QImage image(width, height, QImage::Format_ARGB32); + image.fill(Qt::transparent); + + QPainter painter(&image); + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + { + // QPen pen(Qt::black); + // pen.setWidth(1); + painter.setPen(Qt::NoPen); + painter.setBrush(QBrush(colorNorth, Qt::BrushStyle::SolidPattern)); + + // 2 || + // | | + // 1 | | 3 + // | | + // || + + QPolygon poly; + poly.reserve(3); + poly.append(QPoint( 0, halfHeight)); + poly.append(QPoint(halfWidth, 0)); + poly.append(QPoint( width, halfHeight)); + + painter.drawPolygon(poly); + } + { + // QPen pen(Qt::black); + // pen.setWidth(1); + painter.setPen(Qt::NoPen); + painter.setBrush(QBrush(colorSouth, Qt::BrushStyle::SolidPattern)); + + // || + // | | + // 1 | | 3 + // | | + // 2 || + + QPolygon poly; + poly.reserve(3); + poly.append(QPoint( 0, halfHeight)); + poly.append(QPoint(halfWidth, height)); + poly.append(QPoint( width, halfHeight)); + + painter.drawPolygon(poly); + } + + painter.end(); + + // background: + int maxDim = std::max(width, height); + int border = 5; + QImage backgroundImage(maxDim + (2 * border), + maxDim + (2 * border), + QImage::Format_ARGB32); + { + backgroundImage.fill(Qt::transparent); + + painter.begin(&backgroundImage); + painter.setBrush(QBrush(colorBackground)); + painter.setPen(Qt::NoPen); + painter.drawEllipse(backgroundImage.rect()); + painter.end(); + } + + + // merge: + { + painter.begin(&backgroundImage); + + int offsetX = (backgroundImage.width() - image.width()) / 2; + int offsetY = (backgroundImage.height() - image.height()) / 2; + + QRect r = image.rect(); + r.moveTo(offsetX, offsetY); + painter.drawImage(r, image, image.rect()); + painter.end(); + } + + + { + painter.begin(&backgroundImage); + QPointF pos(backgroundImage.width() / 2.0 * 1.1, + backgroundImage.height() / 2.0 * 0.4); + + // 'N' + QFont font = painter.font(); + font.setBold(true); + painter.setFont(font); + painter.setPen(colorNorth); + + painter.drawText(pos, QLatin1String("N")); + + pos.rx() = backgroundImage.width() / 2.0 * 1.1; + pos.ry() = backgroundImage.height() / 2.0 * 1.9; + if (b_3D) + painter.drawText(pos, QLatin1String("3D")); + else + painter.drawText(pos, QLatin1String("2D")); + } + + return backgroundImage; +} + + +bool SituationMap::MapImpl::isPositionStringVisible() const +{ + if (! d->m_positionLabel) + return false; + d->updatePositionText(); + return d->m_positionLabel->isVisible(); +} + +void MapImpl::showPositionString(bool visible) +{ + if (! d->m_positionLabel) + return; + + bool changed = d->m_positionLabel->isVisible() != visible; + d->m_positionLabel->setVisible(visible); + if (changed) + { + addMapWidget(); + emitVisualComponentChanged(VisualComponent::PosititionString); + } +} + +void SituationMap::MapImpl::setPositionStringColor(const QColor &foregroundColor, const QColor &backgroundColor) +{ + d->m_positionLabelForegroundColor = foregroundColor; + d->m_positionLabelBackgroundColor = backgroundColor; + + if (! d->m_positionLabel) + return; + + QLabel *label = d->m_positionLabel; + if (label) + { + QPalette palette = label->palette(); + if (d->m_positionLabelForegroundColor.isValid()) + palette.setColor(label->foregroundRole(), d->m_positionLabelForegroundColor); + if (d->m_positionLabelBackgroundColor.isValid()) + palette.setColor(label->backgroundRole(), d->m_positionLabelBackgroundColor); + label->setPalette(palette); + } +} + +void MapImpl::setPartitionTime(const qint64 &time) +{ + if(!d->m_exerciseTimeLabel) + return; + d->m_exerciseTimeLabel->setText(DTG::toStringMSec(time)); +} + +void MapImpl::setReplay(bool b) +{ + d->m_isReplay = b; +} + +bool MapImpl::isReplay() const +{ + return d->m_isReplay; +} + +bool MapImpl::isExerciseTimeStringVisible() const +{ + if (! d->m_exerciseTimeLabel) + return false; + return d->m_exerciseTimeLabel->isVisible(); +} + +void MapImpl::showExerciseTimeString(bool visible) +{ + if (! d->m_exerciseTimeLabel) + return; + bool changed = d->m_exerciseTimeLabel->isVisible() != visible; + d->m_exerciseTimeLabel->setVisible(visible); + if (changed) + { + addMapWidget(); + emitVisualComponentChanged(VisualComponent::ExerciseTimeString); + } +} + +void MapImpl::setExerciseTimeStringColor(const QColor &foregroundColor, const QColor &backgroundColor) +{ + d->m_exerciseTimeLabelForegroundColor = foregroundColor; + d->m_exerciseTimeLabelBackgroundColor = backgroundColor; + + if (! d->m_exerciseTimeLabel) + return; + + QLabel *label = d->m_exerciseTimeLabel; + if (label) + { + QPalette palette = label->palette(); + if (d->m_exerciseTimeLabelForegroundColor.isValid()) + palette.setColor(label->foregroundRole(), d->m_exerciseTimeLabelForegroundColor); + if (d->m_exerciseTimeLabelBackgroundColor.isValid()) + palette.setColor(label->backgroundRole(), d->m_exerciseTimeLabelBackgroundColor); + label->setPalette(palette); + } +} + +bool SituationMap::MapImpl::isTimeStringVisible() const +{ + if (! d->m_timeLabel) + return false; + return d->m_timeLabel->isVisible(); +} + +void MapImpl::setTimeString(const QString &timeString) +{ + if (! d->m_timeLabel) + return; + + d->m_timeLabel->setText(timeString); +} + +void SituationMap::MapImpl::showTimeString(bool visible) +{ + if (d->m_destroying) + return; + + if (!d->m_timeLabel) + return; + + bool changed = d->m_timeLabel->isVisible() != visible; + d->m_timeLabel->setVisible(visible); + if (changed) + { + addMapWidget(); + emitVisualComponentChanged(VisualComponent::TimeString); + } +} + +void SituationMap::MapImpl::setTimeStringColor(const QColor &foregroundColor, const QColor &backgroundColor) +{ + if (d->m_destroying) + return; + + d->m_timeLabelForegroundColor = foregroundColor; + d->m_timeLabelBackgroundColor = backgroundColor; + + if (! d->m_timeLabel) + return; + + QLabel *label = d->m_timeLabel; + if (label) + { + QPalette palette = label->palette(); + if (d->m_timeLabelForegroundColor.isValid()) + palette.setColor(label->foregroundRole(), d->m_timeLabelForegroundColor); + if (d->m_timeLabelBackgroundColor.isValid()) + palette.setColor(label->backgroundRole(), d->m_timeLabelBackgroundColor); + label->setPalette(palette); + } +} + +bool SituationMap::MapImpl::isCompassVisible() const +{ + if (! d->m_compass) + return false; + return d->m_compass->isVisible(); +} + +void MapImpl::showCompass(bool visible) +{ + if (d->m_destroying) + return; + + if (!d->m_compass) + return; + + bool changed = d->m_compass->isVisible() != visible; + d->m_compass->setVisible(visible); + if (changed) + emitVisualComponentChanged(VisualComponent::CompassRose); +} + + +void MapImpl::addSideBar() +{ + if (d->m_destroying) + return; + + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (d->m_sideBar) + return; + + // Create SideBar: + d->m_sideBar = QPointer(new SideBar()); // no parent, will be embedded to StackedItem taking ownership + + d->m_sideBar->setAnimationDuration(500); + + d->m_sideBar->setMinimumWidth(350); + d->m_sideBar->setMaximumWidth(d->m_sideBar->minimumWidth()); + d->m_sideBar->setMinimumHeight(300); + + + int width = d->m_sideBar->minimumWidth(); + d->m_sideBar->setVisiblePosition(QPoint(0, 0)); + d->m_sideBar->setInvisiblePosition(QPoint(-width, 0)); + + // Embed (and take ownership) + if (d->m_stackedLayout) + { + int idx = d->m_stackedLayout->addWidget(d->m_sideBar); // takes ownership + d->m_stackedLayout->setCurrentIndex(idx); + } + + // Visibility: + d->m_sideBar->hide(); + + + //---------------------------------------------- + // add Entries: + //---------------------------------------------- + SituationMap::SideBarItem *item; + + // Show Grid + item = new SituationMap::SideBarItem(tr("Show Grid")); + item->setEnabled(this->mode() == Mode::Mode_2D); + item->setCheckState((this->grid().isVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + d->m_sideBar->addCheckableItem(item); + connect(item, &SituationMap::SideBarItem::changed, this, [this](bool onOff) + { + this->grid().setVisible(onOff); + }); + connect(this, &SituationMap::Map::visualComponentChanged, this, [this, item]() + { + item->setCheckState((this->grid().isVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + }); + + // Show Position String + item = new SituationMap::SideBarItem(tr("Show Position String")); + item->setCheckState((this->isPositionStringVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + d->m_sideBar->addCheckableItem(item); + connect(item, &SituationMap::SideBarItem::changed, this, [this](bool onOff) + { + this->showPositionString(onOff); + }); + connect(this, &SituationMap::Map::visualComponentChanged, this, [this, item]() + { + item->setCheckState((this->isPositionStringVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + }); + + // Show Exercise Time String + // // if(isReplay()) // GUEZE-2921 'Anpassen der Uhrzeit auf Nutzerbasis (Checkbox)' + // Mit GUEZE-2921 ist es immer verfügbar. + // Muss es vielleicht über ein projectSetting gesteuert werden? + // Gilt es wirklich für alle Apps? + { + item = new SituationMap::SideBarItem(tr("Show Exercise Time String")); + item->setCheckState((this->isExerciseTimeStringVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + d->m_sideBar->addCheckableItem(item); + connect(item, &SituationMap::SideBarItem::changed, this, [this](bool onOff) + { + this->showExerciseTimeString(onOff); + }); + connect(this, &SituationMap::Map::visualComponentChanged, this, [this, item]() + { + item->setCheckState((this->isExerciseTimeStringVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + }); + } + + // Show Time String + // item = new SituationMap::SideBarItem(tr("Show Time String")); + // item->setCheckState((this->isTimeStringVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + // d->m_sideBar->addCheckableItem(item); + // connect(item, &SituationMap::SideBarItem::changed, this, [this](bool onOff) + // { + // this->showTimeString(onOff); + // }); + // connect(this, &SituationMap::Map::visualComponentChanged, this, [this, item]() + // { + // item->setCheckState((this->isTimeStringVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + // }); + + // Show Compass Rose + item = new SituationMap::SideBarItem(tr("Show Compass Rose")); + item->setCheckState((this->isCompassVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + d->m_sideBar->addCheckableItem(item); + connect(item, &SituationMap::SideBarItem::changed, this, [this](bool onOff) + { + this->showCompass(onOff); + }); + connect(this, &SituationMap::Map::visualComponentChanged, this, [this, item]() + { + item->setCheckState((this->isCompassVisible()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + }); + + + // Show Overview Map + item = new SituationMap::SideBarItem(tr("Show Overview Map")); + OverviewMapHandler &omHdl = overviewMapHandler(); + item->setCheckState((omHdl.builtInOverviewMapWidget()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + d->m_sideBar->addCheckableItem(item); + connect(item, &SituationMap::SideBarItem::changed, this, [this](bool onOff) + { + OverviewMapHandler &omHdl = overviewMapHandler(); + if (onOff) + omHdl.createBuiltInOverviewMap(); + else + omHdl.removeBuiltInOverviewMap(); + }); + connect(this, &SituationMap::Map::visualComponentChanged, this, [this, item]() + { + OverviewMapHandler &omHdl = overviewMapHandler(); + item->setCheckState((omHdl.builtInOverviewMapWidget()) ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + }); + + // could be continue like: + // item = new SituationMap::SideBarItem(tr("Scale Bar (tbd)")); + // item->setCheckState(Qt::CheckState::Checked); + // d->m_sideBar->addCheckableItem(item); + + // item = new SituationMap::SideBarItem(tr("Distance Measurement (tbd)")); + // item->setCheckState(Qt::CheckState::Checked); + // d->m_sideBar->addCheckableItem(item); + // item = new SituationMap::SideBarItem(tr("Terrain Profile (tbd)")); + // item->setCheckState(Qt::CheckState::Checked); + // d->m_sideBar->addCheckableItem(item); + // item = new SituationMap::SideBarItem(tr("Viewshed (tbd)")); + // item->setCheckState(Qt::CheckState::Checked); + // d->m_sideBar->addCheckableItem(item); + + + // Rocket + QPushButton *pbHome = new QPushButton(tr("Home")); + pbHome->setIcon(QIcon(QLatin1String("icon:go-home.svg"))); + d->m_sideBar->addWidget(pbHome); + connect(pbHome, &QPushButton::clicked, this, [this]() { + this->zoomToArea(d->m_trainingsAreaExtent, 50); + }); + + // Rocket + QPushButton *pbRocket = new QPushButton(tr("Rocket")); + pbRocket->setIcon(QIcon(QLatin1String("icon:rocket.svg"))); + d->m_sideBar->addWidget(pbRocket); + connect(pbRocket, &QPushButton::clicked, this, &MapImpl::startRocket); + + + // JumpPointWidget: + const QString jpLayername = this->layerFactoryImpl().jumpPointLayer(); + d->m_jumpPointWidget = new JumpPointWidget(jpLayername, this, QLatin1String("myJumpPointWidget")); + d->m_sideBar->addWidget(d->m_jumpPointWidget); + + // temporaery : + if (0) + { + if (this->isReady()) + { + d->m_jumpPointWidget->addJumpPoint(QLatin1String("JP_BuildingDemo111"), + QPointF(0.155275, 0.925975), + 1000); + } + else + { + connect(this, &Map::mapReady, this, [this]() + { + d->m_jumpPointWidget->addJumpPoint(QLatin1String("JP_BuildingDemo222"), + QPointF(0.155275, 0.925975), + 1000); + }); + } + } + // : temporaery + + + + // Add Button for SideBar/BurgerMenue + { + if (!d->m_pbSideBar) + { + MyPushButton *pb = new MyPushButton(); // parent = 0 -> toplevelWidget + + //const QColor &base = this->palette().color(QPalette::Base); // qt::white + QColor rdeColor = palette().color(QPalette::WindowText); // RDE_DARK_BLUE + + pb->setFlat(true); + pb->setIconSize(QSize(36, 36)); + pb->setFixedSize(pb->iconSize() * 1.2); + + pb->setIconOnHover(SideBar::createIcon(rdeColor, QColor(), 100)); + rdeColor.setAlphaF(0.7); + pb->setIconPale(SideBar::createIcon(rdeColor, QColor(), 100)); + + d->m_pbSideBar = pb; + } + + QPushButton *pbSideBar = d->m_pbSideBar; + // pbSideBar->setText(QLatin1String("MainMenue")); + pbSideBar->setObjectName(QLatin1String("QPushButton-MainMenue")); + + + // add button + + // Bug iOSQz-000005 'QGraphicsProxyWidget will crash' + // Adding a widget will let the application crash + // Workaround: 'proxy->setOpacity(0.99);' + switch (mode()) + { + case Mode::Mode_2D: + { + QGraphicsProxyWidget *proxy = d->m_mapGraphicsView->scene()->addWidget(pbSideBar); + proxy->setOpacity(0.99); + } + break; + case Mode::Mode_3D: + { + QGraphicsProxyWidget *proxy = d->m_sceneGraphicsView->QGraphicsView::scene()->addWidget(pbSideBar); + proxy->setOpacity(0.99); + } + break; + case Mode::Undefined: + // empty + break; + } // switch + + pbSideBar->move(25, 25); + + // connect(pbMainMenue, &QPushButton::clicked, this, &SitMap::onShowSideBar); + connect(pbSideBar, &QPushButton::clicked, this, [&]() + { + if (QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier) + && QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) + && QGuiApplication::keyboardModifiers().testFlag(Qt::AltModifier)) + { + + // 'debug' behaviour: toDumpString + std::string localLoggerName = m_logger.getLoggerName() + ".dump"; + LOGGER_T logger = m_logger.getNGLLogger(localLoggerName); + + NOTICE_LOG(logger) << "START DUMP --- objectName: " << this->objectName(); + const QString &dumpStr = this->toDumpString(); + NOTICE_LOG(logger) << dumpStr; + NOTICE_LOG(logger) << "START END --- objectName: " << this->objectName(); + } else { + + // typical behaviour: + d->m_sideBar->showAnimated(); + + } + }); + + // QDialog * d = new QDialog; + // d->setLayout(new QHBoxLayout); + // d->layout()->addWidget(pbSideBar); + // d->setMinimumSize(QSize(500, 500)); + // d->exec(); + + + + // enable/disable button depending on mapReady + if (this->isReady()) + { + pbSideBar->setStyleSheet(QLatin1String("QPushButton {background-color: transparent; border: 1px solid transparent;}")); + pbSideBar->setEnabled(true); + } + else + { + pbSideBar->setStyleSheet(QLatin1String("QPushButton {background-color: red; border: 1px solid transparent;}")); + pbSideBar->setEnabled(false); + + connect(this, &Map::mapReady, this, [pbSideBar]() + { + pbSideBar->setStyleSheet(QLatin1String("QPushButton {background-color: transparent; border: 1px solid transparent;}")); + pbSideBar->setEnabled(true); + + // pbSideBar->setStyleSheet(QLatin1String("QPushButton {background-color: transparent; border: 1px solid transparent;} QPushButton:hover {background-color: white; border: 1px solid transparent;}")); + // setStyleSheet(QLatin1String("QTabBar::close-button{ image: url(\"icon:dialog-close.svg\"); }")); + }); + } + + + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void MapImpl::removeSideBar() +{ + delete d->m_sideBar; +} + +QPointer MapImpl::sideBar() +{ + return d->m_sideBar; +} + + +bool MapImpl::applyLicense(const QString &licenseStr, const QStringList &extensions) +{ + LOGGER_T logger = getLogger("MapImpl"); + static int licensed = -1; + + INFO_LOG(logger) << LOGFUNCTION << " licenseStr = '" << licenseStr << "'"; + for (const auto &ex : extensions) + INFO_LOG(logger) << LOGFUNCTION << " extension = '" << ex << "'"; + + if (licensed != -1) + { + INFO_LOG(logger) << LOGFUNCTION + << " already licensed" + << " (licensed=" << licensed << ")"; + return licensed == 1; + } + + connect(Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::instance(), &Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::errorOccurred, + [](const Esri::ArcGISRuntime::Error & error) + { + std::cerr << " EsriError occured: " << error << std::endl; + LOGGER_T logger = getLogger("MapImpl"); + ERROR_LOG(logger) << LOGFUNCTION << " EsriError occured: " << error; + }); + + // 1. setLicense + Esri::ArcGISRuntime::LicenseResult licResult; + + if (extensions.isEmpty()) + licResult = Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setLicense(licenseStr); + else + licResult = Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setLicense(licenseStr, extensions); + + // 2. initialize + if (! Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::initialize()) + FATAL_LOG(logger) << LOGFUNCTION << " ArcGISRuntimeEnvironment::initialize failed"; + DEBUG_LOG(logger) << LOGFUNCTION << " licResult=" << (int)licResult.licenseStatus(); + // Invalid = 0, Expired = 2, LoginRequired = 3, Valid = 4 + + std::ostringstream msg; + msg << " licenseStatus = " << licResult.licenseStatus() + << " licenseStr='" << licenseStr.toStdString() << "'"; + for (const QString &extStr : std::as_const(extensions)) + msg << " extensionsStr= '" << extStr << "'"; + + if (licResult.licenseStatus() == Esri::ArcGISRuntime::LicenseStatus::Valid) + { + licensed = 1; + INFO_LOG(logger) << LOGFUNCTION << " success: " << msg.str(); + } + else + { + licensed = 0; + FATAL_LOG(logger) << LOGFUNCTION << " Error: " << msg.str(); + } + + return licResult.licenseStatus() == Esri::ArcGISRuntime::LicenseStatus::Valid; +} + +bool MapImpl::applyNetworkCacheSettings(const EsriNetworkCacheSettings &esriNetworkCacheSettings) +{ + LOGGER_T logger = getLogger("MapImpl"); + INFO_LOG(logger) << LOGFUNCTION << " EsriNetworkCacheSettings = " << esriNetworkCacheSettings; + +#if ACRGIS_VERSION < 0x640700 // 100.7.0 / Update 7 + WARNING_LOG(logger) << LOGFUNCTION << " EsriNetworkCacheSettings not supported yet"; +#else +# ifdef HAS_NETWORK_CACHE_CONFIGURATION // 100.15 + Esri::ArcGISRuntime::NetworkCacheConfiguration *globNetCacheConfig = + Esri::ArcGISRuntime::RequestConfiguration::globalNetworkCacheConfiguration(); + if (!globNetCacheConfig) + { + WARNING_LOG(logger) << LOGFUNCTION << " globalNetworkCacheConfiguration failed"; + return false; + } + + // V100.8: + // setNetworkCachingEnabled -> NetworkCacheConfiguration + // V100.15: default CachePolicy is 'PreferNetwork' + + + INFO_LOG(logger) << LOGFUNCTION << " globalNetworkCacheConfiguration: clearCache... '" << globNetCacheConfig->cacheDirectory() << "'"; + globNetCacheConfig->clearCacheAsync(); // clearCacheAsync - since 100.15.0 + + INFO_LOG(logger) << LOGFUNCTION << " globalNetworkCacheConfiguration: setNetworkCachingEnabledAsync... to " << globNetCacheConfig->networkCachingEnabled(); + globNetCacheConfig->setNetworkCachingEnabledAsync(esriNetworkCacheSettings.enabled); // setNetworkCachingEnabledAsync - since 100.15.0 + + NOTICE_LOG(logger) << LOGFUNCTION << " globalNetworkCacheConfiguration: " << globNetCacheConfig->cachePolicy() + << ", cacheDir='" << globNetCacheConfig->cacheDirectory() << "'" + << ", cacheSize=" << globNetCacheConfig->cacheSize(); +# else + Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setNetworkCachingEnabled(esriNetworkCacheSettings.enabled); +# endif // HAS_NETWORK_CACHE_CONFIGURATION +#endif // ACRGIS_VERSION + + return true; +} +EsriNetworkCacheSettings MapImpl::networkCacheSettings() +{ + EsriNetworkCacheSettings encs; + + +#if ACRGIS_VERSION < 0x640700 // 100.7.0 / Update 7 + LOGGER_T logger = getLogger("MapImpl"); + WARNING_LOG(logger) << LOGFUNCTION << " EsriNetworkCacheSettings not supported yet"; +#else + +# ifdef HAS_NETWORK_CACHE_CONFIGURATION // 100.15 + LOGGER_T logger = getLogger("MapImpl"); + Esri::ArcGISRuntime::NetworkCacheConfiguration *globNetCacheConfig = + Esri::ArcGISRuntime::RequestConfiguration::globalNetworkCacheConfiguration(); + if (globNetCacheConfig) + { + encs.enabled = globNetCacheConfig->networkCachingEnabled(); + } else { + WARNING_LOG(logger) << LOGFUNCTION << " globalNetworkCacheConfiguration failed"; + } + +# else + encs.enabled = Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::networkCachingEnabled(); +# endif // HAS_NETWORK_CACHE_CONFIGURATION +#endif // #if ACRGIS_VERSION < 0x640700 + + return encs; +} + + + +Esri::ArcGISRuntime::Map *MapImpl::getEsriMap() const +{ + return d->m_esriMap; +} + +Esri::ArcGISRuntime::Basemap *MapImpl::getEsriBasemap() const +{ + return d->m_basemap; +} + + +bool MapImpl::initElevationMapManager(const QStringList &filePaths_rough_to_fine) +{ + INFO_LOG(m_logger) << LOGFUNCTION << " read elevation data file \'" << filePaths_rough_to_fine.join(QStringLiteral(" ; ")) << "\' ..."; + + QString fileCombinationKey; // name of TrainingArea would be helpful :-) + for (const QString &filePath : filePaths_rough_to_fine) + fileCombinationKey.append(QFileInfo(filePath).fileName() + QStringLiteral("#")); + + + static QHash> s_elevationMapManagers; + auto it = s_elevationMapManagers.find(fileCombinationKey); + if (it != s_elevationMapManagers.end()) + { + d->m_elevationMapManager = it.value().lock(); + if (d->m_elevationMapManager) + return true; + } + d->m_elevationMapManager = std::make_shared(); + d->m_elevationMapManager->init(filePaths_rough_to_fine); + + INFO_LOG(m_logger) << LOGFUNCTION << " read elevation data file \'" << filePaths_rough_to_fine.join(QStringLiteral(" ; ")) << "\' ... done"; + + return true; +} + +std::shared_ptr MapImpl::getElevationMapManager() const +{ + if (d->m_elevationMapManager && !d->m_elevationMapManager->isValid()) + return nullptr; + return d->m_elevationMapManager; +} + +Esri::ArcGISRuntime::SpatialReference MapImpl::getEsriSpatialReference() const +{ + if (d->m_esriMap) + return d->m_esriMap->spatialReference(); + if (d->m_esriScene) + return d->m_esriScene->spatialReference(); + return Esri::ArcGISRuntime::SpatialReference(); +} + +Esri::ArcGISRuntime::Scene *MapImpl::getEsriScene() const +{ + if (!d->m_esriScene) + return nullptr; + return d->m_esriScene; +} + +Esri::ArcGISRuntime::MapGraphicsView *MapImpl::getEsriMapGraphicsView() const +{ + return d->m_mapGraphicsView; +} + +Esri::ArcGISRuntime::SceneGraphicsView *MapImpl::getEsriSceneGraphicsView() const +{ + return d->m_sceneGraphicsView; +} + +QGraphicsView *MapImpl::getEsriQGraphicsView() const +{ + if (d->m_mapGraphicsView) + return d->m_mapGraphicsView; + if (d->m_sceneGraphicsView) + return d->m_sceneGraphicsView; + return nullptr; +} + +Esri::ArcGISRuntime::GeoView *MapImpl::getEsriGeoView() const +{ + if (d->m_mapGraphicsView && d->m_sceneGraphicsView) + { + FATAL_LOG(m_logger) << LOGFUNCTION << " Upps, 2D(Map) and also 3D (Scene) -> error!!! "; + FATAL_LOG(m_logger) << LOGFUNCTION << " Upps, 2D(Map) and also 3D (Scene) -> error!!! "; + FATAL_LOG(m_logger) << LOGFUNCTION << " Upps, 2D(Map) and also 3D (Scene) -> error!!! "; + } + if (d->m_mapGraphicsView) + return d->m_mapGraphicsView; + if (d->m_sceneGraphicsView) + return d->m_sceneGraphicsView; + return nullptr; +} + +QString MapImpl::toDumpString() const +{ + std::stringstream myLine; + Esri::ArcGISRuntime::Map *esriMap = d->m_esriMap; + Esri::ArcGISRuntime::MapGraphicsView *esriGraphicsMapView = d->m_mapGraphicsView; + Esri::ArcGISRuntime::Scene *esriScene = d->m_esriScene; + // Esri::ArcGISRuntime::SceneGraphicsView *esriGraphicsSceneView = d->m_sceneGraphicsView; + + Esri::ArcGISRuntime::GeoView *geoView = this->getEsriGeoView(); + if (! geoView) + return QString(); + + int graphicsOverlayCount = -1; + if (geoView->graphicsOverlays()) + graphicsOverlayCount = geoView->graphicsOverlays()->size(); + + int basemapCount = -1; + if (this->getEsriBasemap() && this->getEsriBasemap()->baseLayers()) + basemapCount = this->getEsriBasemap()->baseLayers()->size(); + + QRectF curExtent = this->extent(); + QRectF fullExtent; // = this->fullExtent(); + + // Esri::ArcGISRuntime::Envelope env = esriMap->fullExtent(); + // QPointF fullExtentMin(env.xMin(), env.yMin()); + // QPointF fullExtentMax(env.xMax(), env.yMax()); + // QPointF fullExtentCenter(env.center().x(), env.center().y()); + + + // header + { + myLine << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX" << std::endl; + + // myLine << " animationMode \t\t= " << static_cast(esriMap->animationMode()) << std::endl; + // myLine << " animationDuration \t= " << esriMap->animationDuration() << std::endl; + + myLine << " current wkid \t\t= " << this->getEsriSpatialReference().wkid() << std::endl; + myLine << " wkText \t='" << this->getEsriSpatialReference().wkText() << "'" << std::endl; + myLine << " toJson='" << this->getEsriSpatialReference().toJson() << "'" << std::endl; + + if (esriMap) + myLine << " Map::isAutoFetchLegendInfos='" << esriMap->isAutoFetchLegendInfos() << std::endl; + if (esriScene) + myLine << " Scene::isAutoFetchLegendInfos='" << esriScene->isAutoFetchLegendInfos() << std::endl; + + myLine << " dict isLoaded \t\t= " << this->symbolDictionary().isLoaded() << std::endl; + myLine << " dict extraSymbolCount \t= " << this->symbolDictionary().etsSymbolCount() << std::endl; + + myLine << " graphicsOverlayCount \t= " << graphicsOverlayCount << std::endl; + myLine << " basemapCount \t\t= " << basemapCount << std::endl; + + myLine << " curExtent \t\t= " << "center=" << curExtent.center() + << " ; w=" << curExtent.width() << " h=" << curExtent.height() << std::endl; + myLine << " fullExtent \t\t= " << "center=" << fullExtent.center() + << " ; w=" << fullExtent.width() << " h=" << fullExtent.height() << std::endl; + + // myLine << " (fullExtent[esri] \t= " << "min=" << fullExtentMin << " ; max=" << fullExtentMax << ")" << std::endl; + // myLine << " (fullExtent[esri] \t= " << "center=" << fullExtentCenter + // << " ; w=" << env.width() << " h=" << env.height() << ")" << std::endl; + + if (esriMap) + { + myLine << " minScale \t\t\t= " << esriMap->minScale() << std::endl; + myLine << " maxScale \t\t\t= " << esriMap->maxScale() << std::endl; + } + myLine << " scale(SitMap) \t\t= " << this->scale() << std::endl; + + myLine << " rotation(SitMap) \t\t= " << this->rotation() << " [deg]" << std::endl; + if (esriMap) + myLine << " MapView::rotation \t\t= " << esriGraphicsMapView->mapRotation() << " [deg]" << std::endl; + if (esriScene) + myLine << " SceneView::rotation \t\t= " << "-" << std::endl; + + + myLine << " viewpoint \t\t\t= " << this->viewpoint2D() << std::endl; + if (d->m_mapGraphicsView) + myLine << " MapView::viewpoint \t\t= " << d->m_mapGraphicsView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale) << std::endl; + if (d->m_sceneGraphicsView) + { + myLine << " Camera(SitMap) \t\t= " << this->camera() << std::endl; + myLine << " SceneView::viewpoint \t\t= " << d->m_sceneGraphicsView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale) << std::endl; + myLine << " SceneView::camera \t\t= " << d->m_sceneGraphicsView->currentViewpointCamera() << std::endl; + + myLine << "\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" << std::endl; + myLine << " OperationalLayers:"; + + Esri::ArcGISRuntime::LayerListModel *llm = nullptr; + if (esriScene) + llm = esriScene->operationalLayers(); + if (llm) + { + myLine << " - count=" << llm->size(); + myLine << "\nLayer \tVisi \tOpac \tSts \t\tType \t\t\t\tName\n"; + for (int i = 0; i < llm->size(); ++i) + { + Esri::ArcGISRuntime::Layer *layer = llm->at(i); + if (!layer) + continue; + myLine << " " << i + << "\t" << layer->isVisible() + << "\t" << layer->opacity() + << "\t" << layer->loadStatus() + << "\t" << layer->layerType() + << "\t" << layer->name() + << " (minScale=" << layer->minScale() << " ; maxScale=" << layer->maxScale() << ")" + << " (attribution='" << layer->attribution() << "'')" + << "\t description=" << layer->description() + << "\t isShowInLegend=" << layer->isShowInLegend(); + + { + Esri::ArcGISRuntime::ArcGISSceneLayer *sceneLayer = + dynamic_cast(layer); + + if (sceneLayer) + myLine << "\n\t\t\t\t\t url:" << sceneLayer->url().toString(); + } + + myLine << std::endl; + } // for + + } + + + } + } + + + myLine << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX" << std::endl; + AdvancedSymbology::SymbolDictionary &dict = this->symbolDictionary(); + AdvancedSymbology::SymbolDictionaryImpl &dictImpl = static_cast(dict); + + myLine << " symbologyFieldNames: " << dictImpl.symbologyFieldNamesInternal().join(QLatin1String(" ; ")) << std::endl; + myLine << " textFieldNames: " << dictImpl.textFieldNamesInternal().join(QLatin1String(" ; ")) << std::endl; + + myLine << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX" << std::endl; + if (basemapCount > 0) + { + myLine << "basemapCount=" << basemapCount << std::endl; + + myLine << "\nLayer \tVisi \tOpac \tSts \t\tType \t\t\t\tName" << std::endl; + + const Esri::ArcGISRuntime::LayerListModel *model = this->getEsriBasemap()->baseLayers(); + for (int i = 0; i < model->size(); ++i) + { + /*const*/ Esri::ArcGISRuntime::Layer *layer = model->at(i); + + myLine << " " << i + << "\t" << layer->isVisible() + << "\t" << layer->opacity() + << "\t" << layer->loadStatus() + << "\t" << layer->layerType() + << "\t" << layer->name() + << " (minScale=" << layer->minScale() << " ; maxScale=" << layer->maxScale() << ")" + << " (attribution='" << layer->attribution() << "'')" + + << "\t description=" << layer->description() + << "\t isShowInLegend=" << layer->isShowInLegend(); + + { + Esri::ArcGISRuntime::ImageAdjustmentLayer *iaLayer = + dynamic_cast(layer); + + if (iaLayer) + { + myLine << "\t b:" << iaLayer->brightness() + << "\t g:" << iaLayer->gamma() + << "\t c:" << iaLayer->contrast(); + } + } + + myLine << std::endl; + + QString url(QLatin1String("no url availble")); + Esri::ArcGISRuntime::ArcGISTiledLayer *tiledLayer = dynamic_cast(layer); + if (tiledLayer) + url = tiledLayer->mapServiceInfo().url().toString(); + myLine << "url= '" << url << "'"; + + + { + Esri::ArcGISRuntime::LegendInfoListModel *lilm = layer->legendInfos(); + int lSize = -1; + if (lilm) + lSize = lilm->size(); + + myLine << std::endl; + myLine << "LegendInfoListModelCount=" << lSize << std::endl; + + for (int j = 0; j < lSize; ++j) + { + Esri::ArcGISRuntime::LegendInfo *li = lilm->at(j); + + myLine << " " << j + << "\t" << li->name() + << "\t" << (int)li->symbol()->symbolType(); + } + } + + myLine << std::endl; + } // for + } // if (basemapCount > 0) + + // + // GraphicsOverlays + // + + myLine << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; + int objCounter = 0; + QVector> gOverlayCounter; + if (graphicsOverlayCount) + { + myLine << std::endl; + myLine << "graphicsOverlayCount=" << graphicsOverlayCount << std::endl; + myLine << "i \tVisi \tOpac \tGRM \t\t\tSP \t\t\t#Obj \tName" << std::endl; + + Esri::ArcGISRuntime::GraphicsOverlayListModel *gOverlayListModel = getEsriGeoView()->graphicsOverlays(); + const int gOverlayCount = gOverlayListModel->size(); + for (int esriIdx=0; esriIdxat(esriIdx); + if (!gOverlay) + continue; + + const QString &name = gOverlay->overlayId(); // since 100.2.0 + if (gOverlay) + { + int i = 0; + + int objCountPerLayer = -1; + if (gOverlay->graphics()) + objCountPerLayer = gOverlay->graphics()->size(); + + objCounter += objCountPerLayer; + gOverlayCounter.append(QPair(name, objCountPerLayer)); + + int esriIdx = gOverlayListModel ? gOverlayListModel->indexOf(gOverlay) : -1; + + Esri::ArcGISRuntime::LayerSceneProperties sceneProperties = gOverlay->sceneProperties(); + + std::string typeName; + Esri::ArcGISRuntime::Renderer *renderer = gOverlay->renderer(); + if (renderer) + typeName = typeid(*renderer).name(); + + QString objName; + if (renderer) + objName = renderer->objectName(); + + myLine << " " << i++ + << "\t" << gOverlay->isVisible() + << "\t" << gOverlay->opacity() + << "\t" << gOverlay->renderingMode() << " " + << "\t" << sceneProperties.surfacePlacement() << " " + << "\t" << objCountPerLayer + << "\t (" << esriIdx << ")" + << "\t" << name + << " (minScale=" << gOverlay->minScale() << " ; maxScale=" << gOverlay->maxScale() << ")" + << " (Renderer=" << gOverlay->renderer() + << ", type_info=" << typeName + << ", objName=" << objName + << ")"; + + + Esri::ArcGISRuntime::GraphicListModel *gListModel = gOverlay->graphics(); + if (gListModel && !gListModel->isEmpty()) + { + myLine << std::endl; + myLine << "objIdx" << "\tUMapId \tVisi \tPick \tSel \tzIdx \tobjectName" << std::endl; + + for (int idx = 0; idx < gListModel->size(); ++idx) + { + Esri::ArcGISRuntime::Graphic *graphic = gListModel->at(idx); + if (graphic) + { + UniqueMapId uniqueMapId = INVALID_MAP_ID; + bool isPickable = false; + Esri::ArcGISRuntime::AttributeListModel *attLisModel = graphic->attributes(); + if (attLisModel) + { + bool ok; + uniqueMapId = attLisModel->attributeValue(QLatin1String(SituationMap::UniqueMapIdAttribute)).toUInt(&ok); + isPickable = attLisModel->attributeValue(QLatin1String(SituationMap::IsPickableAttribute)).toBool(); + } + + myLine << " " << idx + << "\t" << uniqueMapId + << "\t" << graphic->isVisible() + << "\t" << isPickable + << "\t" << graphic->isSelected() + << "\t" << graphic->zIndex() + << "\t objName " << graphic->objectName() + << "\t geom " << graphic->geometry().toJson().left(100) + << std::endl; + } + + } // for + + + } + + + myLine << std::endl; + + } + } // while + + } // if (graphicsOverlayCount) + + myLine << "PartyIdToColorFunctor: " << std::endl; + this->partyIdToColorFunctor().toString(myLine); + myLine << std::endl; + + myLine << "Obj per GraphicalOverlay:" << std::endl; + for (const QPair &item : gOverlayCounter) + myLine << " GraphicalOverlay: " << item.second << "\t" << " obj on '" << item.first << "'" << std::endl; + + myLine << "-------" << std::endl; + myLine << "#Total: " << objCounter << std::endl; + + myLine << std::endl; + + myLine << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX" << std::endl; + myLine << "count() of objectFactory/Container: " << d->m_objectFactory->count() << std::endl; + myLine << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX" << std::endl; + + + + myLine << std::endl; + return QString::fromStdString(myLine.str()); +} + + +bool MapImpl::isLoaded() const +{ + if (d->m_esriMap) + return d->m_esriMap->loadStatus() == Esri::ArcGISRuntime::LoadStatus::Loaded; + if (d->m_esriScene) + return d->m_esriScene->loadStatus() == Esri::ArcGISRuntime::LoadStatus::Loaded; + return false; +} + +QGraphicsView *MapImpl::getQGraphicsView() const +{ + if (d->m_mapGraphicsView) + return d->m_mapGraphicsView; + if (d->m_sceneGraphicsView) + return d->m_sceneGraphicsView; + return nullptr; +} + +double MapImpl::pixelSizeMeter() +{ + if (d->m_pixelsize <= 0) + { + if (d->m_mapGraphicsView) + { + Esri::ArcGISRuntime::Point p1_mapCoord = d->m_mapGraphicsView->screenToLocation(0, 0); + Esri::ArcGISRuntime::Point p2_mapCoord = d->m_mapGraphicsView->screenToLocation(0, 1); + + if (d->m_coordTranslationFunctor) + { + // const QPointF &dist = d->m_coordTranslationFunctor->mapToGeographic(p1_mapCoord) - d->m_coordTranslationFunctor->mapToGeographic(p2_mapCoord); + const QPointF &dist = QPointF(p1_mapCoord.x(), p1_mapCoord.y()) - QPointF(p2_mapCoord.x(), p2_mapCoord.y()); + + d->m_pixelsize = sqrt(dist.x() * dist.x() + dist.y() * dist.y()); + } + else + { + d->m_pixelsize = Esri::ArcGISRuntime::GeometryEngine::distance(d->m_mapGraphicsView->screenToLocation(0, 0), + d->m_mapGraphicsView->screenToLocation(0, 1)); + } + + // 2019-11-13, Norbert Thoden + // Sometimes (most of the time when starting a new map) the drawing of a + // spline results in a simple line (vertexCount=2 assumed) + // Maybe m_pixelsize is NaN? To avoid this: + if (std::isnan(d->m_pixelsize)) + { + FATAL_LOG(m_logger) << LOGFUNCTION << " d->m_pixelsize is Nan, set to default"; +#ifdef LTS_DEBUG + QMessageBox::critical(nullptr, QLatin1String("pixelSizeMeter"), QLatin1String("pixelSizeMeter is NaN - abort.")); + abort(); +#endif // LTS_DEBUG + d->m_pixelsize = 1.234567; + } + + if (d->m_sceneGraphicsView) + d->m_pixelsize = 1.2345; + } + } + return d->m_pixelsize; +} + +double MapImpl::pixelSizeGeographic() +{ + if (d->m_mapGraphicsView) + { + Esri::ArcGISRuntime::Point p1_mapCoord = d->m_mapGraphicsView->screenToLocation(0, 0); + Esri::ArcGISRuntime::Point p2_mapCoord = d->m_mapGraphicsView->screenToLocation(0, 1); + + if (d->m_coordTranslationFunctor) + { + const QPointF &dist = d->m_coordTranslationFunctor->mapToGeographic(p1_mapCoord) - d->m_coordTranslationFunctor->mapToGeographic(p2_mapCoord); + return sqrt(dist.x() * dist.x() + dist.y() * dist.y()); + } + } + return -1; +} + +//void MapImpl::moveEvent(QMoveEvent *e) +//{ +// qWarning() << LOGFUNCTION << "moveEvent" << e->type(); +// QWidget::moveEvent(e); +//} + +//bool MapImpl::event(QEvent *e) +//{ +// qWarning() << LOGFUNCTION << "EVENT" << e->type(); +// if (e->type() == QEvent::Move) +// qWarning() << "MOVEE"; + +// return QWidget::event(e); +//} + + +//Esri::ArcGISRuntime::Layer* MapImpl::getEsriLayerByName(const QString &layerName) +//{ +// Esri::ArcGISRuntime::Layer* foundLayer = nullptr; + +// Esri::ArcGISRuntime::Map* esriMap = this->getEsriMap(); +// QList layerList = esriMap->layers(); + +// for ( Esri::ArcGISRuntime::Layer* layer, layerList) { +// if (layer) +// { +// if ( layer->name() == layerName) +// { +// foundLayer = layer; +// break; +// } +// } +// } + +// return foundLayer; +//} + + +QPointF MapImpl::mapCoordinateToViewPortWindowPoint(const QPointF &mapPos) const +{ + // don NOT delegate to mapCoordinateToViewPortWindowPoint3D(Point3D::fromQPointF(mapPos)); + // In that case, the z-value is set to zero. That is different to this method. + // Here the z-value is not taken into account: + + if (!isReady()) + return QPointF(); + + if (d->m_mapGraphicsView) + { + Esri::ArcGISRuntime::Point esriCoordinate(mapPos.x(), mapPos.y(), d->m_mapGraphicsView->spatialReference()); + const QPointF &screenCoordinate = d->m_mapGraphicsView->locationToScreen(esriCoordinate); + return screenCoordinate; + } + else if (d->m_sceneGraphicsView) + { + Esri::ArcGISRuntime::Point esriCoordinate(mapPos.x(), mapPos.y(), d->m_sceneGraphicsView->spatialReference()); + const Esri::ArcGISRuntime::LocationToScreenResult &locToSrnRes = d->m_sceneGraphicsView->locationToScreen(esriCoordinate); + switch (locToSrnRes.visibility()) + { + case Esri::ArcGISRuntime::SceneLocationVisibility::Visible: + case Esri::ArcGISRuntime::SceneLocationVisibility::HiddenByBaseSurface: + case Esri::ArcGISRuntime::SceneLocationVisibility::HiddenByEarth: + case Esri::ArcGISRuntime::SceneLocationVisibility::HiddenByElevation: + { + const QPointF &screenCoordinate = locToSrnRes.screenPoint(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " screenCoordinate=" << screenCoordinate << " locToSrnRes.visibility()=" << locToSrnRes.visibility(); + return screenCoordinate; + } + break; + case Esri::ArcGISRuntime::SceneLocationVisibility::NotOnScreen: + DEBUG_LOG(m_logger) << LOGFUNCTION << " locToSrnRes.visibility()=" << locToSrnRes.visibility() + << ", locToSrnRes.screenPoint()=" << locToSrnRes.screenPoint(); + + } // switch + } + return QPointF(); +} + +QPointF MapImpl::mapCoordinateToViewPortWindowPoint3D(const Point3D &mapPos) const +{ + if (!isReady()) + return QPointF(); + + if (d->m_mapGraphicsView) + { + Esri::ArcGISRuntime::Point esriCoordinate(mapPos.x(), mapPos.y(), mapPos.z(), d->m_mapGraphicsView->spatialReference()); + const QPointF &screenCoordinate = d->m_mapGraphicsView->locationToScreen(esriCoordinate); + return screenCoordinate; + } + else if (d->m_sceneGraphicsView) + { + Esri::ArcGISRuntime::Point esriCoordinate(mapPos.x(), mapPos.y(), mapPos.z(), d->m_sceneGraphicsView->spatialReference()); + const Esri::ArcGISRuntime::LocationToScreenResult &locToSrnRes = d->m_sceneGraphicsView->locationToScreen(esriCoordinate); + switch (locToSrnRes.visibility()) + { + case Esri::ArcGISRuntime::SceneLocationVisibility::Visible: + case Esri::ArcGISRuntime::SceneLocationVisibility::HiddenByBaseSurface: + case Esri::ArcGISRuntime::SceneLocationVisibility::HiddenByEarth: + case Esri::ArcGISRuntime::SceneLocationVisibility::HiddenByElevation: + { + const QPointF &screenCoordinate = locToSrnRes.screenPoint(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " screenCoordinate=" << screenCoordinate << " locToSrnRes.visibility()=" << locToSrnRes.visibility(); + return screenCoordinate; + } + break; + case Esri::ArcGISRuntime::SceneLocationVisibility::NotOnScreen: + DEBUG_LOG(m_logger) << LOGFUNCTION << " locToSrnRes.visibility()=" << locToSrnRes.visibility() + << ", locToSrnRes.screenPoint()=" << locToSrnRes.screenPoint(); + break; + } // switch + } + return QPointF(); +} + + +QPointF MapImpl::viewPortWindowPointToMapCoordinate(const QPointF &pixelCoord) const +{ + if (!isReady()) + return QPointF(); + + if (d->m_mapGraphicsView) + { + const Esri::ArcGISRuntime::Point &mapCoordinate = d->m_mapGraphicsView->screenToLocation(pixelCoord.x(), pixelCoord.y()); + return QPointF(mapCoordinate.x(), mapCoordinate.y()); + } + else if (d->m_sceneGraphicsView) + { + const Esri::ArcGISRuntime::Point &baseCoordinate = d->m_sceneGraphicsView->screenToBaseSurface(pixelCoord.x(), pixelCoord.y()); + return QPointF(baseCoordinate.x(), baseCoordinate.y()); + } + return QPointF(); +} + +Point3D MapImpl::viewPortWindowPointToMapCoordinate3D(const QPointF &pixelCoord) const +{ + if (!isReady()) + return Point3D(); + + if (d->m_mapGraphicsView) + { + const Esri::ArcGISRuntime::Point &mapCoordinate = d->m_mapGraphicsView->screenToLocation(pixelCoord.x(), pixelCoord.y()); + return Point3D(mapCoordinate.x(), mapCoordinate.y(), mapCoordinate.z()); + } + else if (d->m_sceneGraphicsView) + { + const Esri::ArcGISRuntime::Point &baseCoordinate = d->m_sceneGraphicsView->screenToBaseSurface(pixelCoord.x(), pixelCoord.y()); + return Point3D(baseCoordinate.x(), baseCoordinate.y(), baseCoordinate.z()); + } + return Point3D(); +} + +QPointF MapImpl::geoCoordinateToViewPortWindowPoint(const QPointF &geoPos) const +{ + if (!isReady()) + return QPointF(); + const SituationMap::CoordTranslationFunctor *mapCtFunctor = coordTranslationFunctor(); + if (!mapCtFunctor) + return QPointF(); + + return mapCoordinateToViewPortWindowPoint(mapCtFunctor->geographicToMap(geoPos)); +} + +QPointF MapImpl::geoCoordinateToViewPortWindowPoint3D(const Point3D &geoPos) const +{ + if (!isReady()) + return QPointF(); + const SituationMap::CoordTranslationFunctor *mapCtFunctor = coordTranslationFunctor(); + if (!mapCtFunctor) + return QPointF(); + + Point3D mapPos = mapCtFunctor->geographicToMap(geoPos); + + return mapCoordinateToViewPortWindowPoint3D(mapPos); +} + +QPointF MapImpl::viewPortWindowPointToGeoCoordinate(const QPointF &pixelCoord) const +{ + if (!isReady()) + return QPointF(); + const SituationMap::CoordTranslationFunctor *mapCtFunctor = coordTranslationFunctor(); + if (!mapCtFunctor) + return QPointF(); + const QPointF mapCoordinate = viewPortWindowPointToMapCoordinate(pixelCoord); + return mapCtFunctor->mapToGeographic(mapCoordinate); +} + +Point3D MapImpl::viewPortWindowPointToGeoCoordinate3D(const QPointF &pixelCoord) const +{ + if (!isReady()) + return Point3D(); + const SituationMap::CoordTranslationFunctor *mapCtFunctor = coordTranslationFunctor(); + if (!mapCtFunctor) + return Point3D(); + const Point3D mapCoordinate = viewPortWindowPointToMapCoordinate3D(pixelCoord); + return mapCtFunctor->mapToGeographic(mapCoordinate); +} + + +QString MapImpl::geoToMgrsString(const QPointF &geoPos) const +{ + SituationMap::CoordTranslationFunctor *mapCtFunctor = coordTranslationFunctor(); + if (!mapCtFunctor) return QString(); + + return mapToMgrsString(mapCtFunctor->geographicToMap(geoPos)); +} + +QString MapImpl::mapToMgrsString(const QPointF &mapPos) const +{ + // DEBUG_LOG(m_logger) << LOGFUNCTION << "mapPos=" << mapPos; + + const CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor(); + + if (! ctFunctor) + return QString(); + + const QPointF &pointGeo = ctFunctor->mapToGeographic(mapPos); + + // DEBUG_LOG(m_logger) << LOGFUNCTION << " mapPos= " << mapPos << " pointGeo= " << pointGeo; + + return LtsCoordTrans::mgrsFromGeographic(pointGeo.x(), pointGeo.y()); +} + + +bool MapImpl::eventFilter(QObject *obj, QEvent *e) +{ + if (!e) + return false; + + bool filterDragAndDrop = static_cast(d->m_mapGraphicsView) != obj; + + if (filterDragAndDrop) + { + switch (e->type()) + { + case QEvent::DragEnter: + case QEvent::DragMove: + { + INFO_LOG(m_logger) << LOGFUNCTION << "Drag enter event reached."; + + const QMimeData *mimeData = ((QDropEvent *)e)->mimeData(); + + if (mimeData) + { + for (auto &mgrPtr : std::as_const(d->m_dropEventManagerList)) + { + if (mimeData->hasFormat(mgrPtr->mimeFormatString())) + e->accept(); + } + } + return true; + } break; + + case QEvent::Drop: + { + INFO_LOG(m_logger) << LOGFUNCTION << "Drop event reached."; + + const QMimeData *mimeData = ((QDropEvent *)e)->mimeData(); + + if (mimeData) + { + for (auto mgrPtr : std::as_const(d->m_dropEventManagerList)) + { + if (mimeData->hasFormat(mgrPtr->mimeFormatString())) + mgrPtr->manageDrop(((QDropEvent *)e)->pos(), mimeData); + } + e->accept(); + } + return true; + } break; + default : break; + } // switch + } + + if (QEvent::KeyPress == e->type()) + { + QKeyEvent *keyEvent = static_cast(e); + int key = keyEvent->key(); + Qt::KeyboardModifiers mod = keyEvent->modifiers(); + + if ((Qt::Key_J == key) && (mod.testFlag(Qt::KeyboardModifier::ControlModifier))) + { + INFO_LOG(m_logger) << LOGFUNCTION << " rethink jumpPointRequest Modifier"; + + onJumpPointRequest(static_cast(obj)->mapFromGlobal(QCursor::pos())); + + e->accept(); + return true; + } + } + return false; +} + +void MapImpl::onKeyPressEvent(const QKeyEvent &keyEvent) +{ + const int key = keyEvent.key(); + if (Qt::Key_Escape == key) + { + emit escapePressed(); + } +} + +void MapImpl::installDropEventManager(DropEventManager *demgr) +{ + if (demgr == nullptr) return; + + RefPointer refDemgr(demgr); + if (d->m_dropEventManagerList.contains(refDemgr)) return; + d->m_dropEventManagerList += refDemgr; +} // MapImpl::installDropEventManager + + +void MapImpl::deinstallDropEventManager(DropEventManager *demgr) +{ + if (demgr == nullptr) return; + RefPointer refDemgr(demgr); + if (d->m_dropEventManagerList.contains(refDemgr)) + d->m_dropEventManagerList.removeAll(refDemgr); +} + +void MapImpl::onHandleMouseMove(int windowPosX, int windowPosY) +{ + if (d->m_destroying) + return; + + if (!d->m_mapReadyFlag) + return; + + if (windowPosX < 0 || windowPosY < 0) + return; + + // 1 + emit mouseWindowPositionChanged(windowPosX, windowPosY); + + if (!isReady()) + { + return; + } + + if (mode() == Mode::Mode_2D) + { + // Mode_2D: + const QPointF pixelCoord(windowPosX, windowPosY); + const Point3D &mapCoordinate = viewPortWindowPointToMapCoordinate3D(pixelCoord); + + // 2 + emit mouseMapPositionChanged(mapCoordinate, true); + + SituationMap::CoordTranslationFunctor *mapCtFunctor = coordTranslationFunctor(); + if (mapCtFunctor) + { + if (isSignalConnected(QMetaMethod::fromSignal(&Map::mouseGeoPositionChanged))) + { + // 3 + emit mouseGeoPositionChanged(mapCtFunctor->mapToGeographic(mapCoordinate), true); + } + } + } + else + { + // Mode_3D: + // 'screenToLocation' is expensive. Use screenToLocationHelper: + if (d->m_screenToLocationHelper) + d->m_screenToLocationHelper->screenToLocation(windowPosX, windowPosY); + } +} // MapImpl::onHandleMouseMove + + +bool MapImpl::isReady() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, m_mapReadyFlag=" << d->m_mapReadyFlag; + return d->m_mapReadyFlag; +} + +DrawStatus MapImpl::drawStatus() const +{ + switch (this->mode()) + { + case Mode::Mode_2D: + return DrawStatus::Completed; + break; + case Mode::Mode_3D: + { + if (d->m_sceneGraphicsView) + return static_cast(d->m_sceneGraphicsView->drawStatus()); // save due to static_assert + } + break; + default: + // empty + break; + } // switch + return DrawStatus::Completed; +} + +int MapImpl::wkid() const +{ + return this->getEsriSpatialReference().wkid(); +} + +SituationMap::NavigationConstraint MapImpl::navigationConstraint() const +{ +#ifdef HAS_BELOW_GROUND + Esri::ArcGISRuntime::Surface* baseSurface = nullptr; + if (!d->m_esriScene || !(baseSurface = d->m_esriScene->baseSurface())) + return SituationMap::NavigationConstraint::Undefined; + return static_cast(baseSurface->navigationConstraint()); +#else + return SituationMap::NavigationConstraint::Undefined; +#endif // HAS_BELOW_GROUND +} + +bool MapImpl::setNavigationConstraint(SituationMap::NavigationConstraint navigationConstraint) +{ + if (navigationConstraint == SituationMap::NavigationConstraint::Undefined) + return false; +#ifdef HAS_BELOW_GROUND + Esri::ArcGISRuntime::Surface* baseSurface = nullptr; + if (!d->m_esriScene || !(baseSurface = d->m_esriScene->baseSurface())) + return false; + const SituationMap::NavigationConstraint currentNavConstraint = this->navigationConstraint(); + if (currentNavConstraint != navigationConstraint) + { + baseSurface->setNavigationConstraint(static_cast(navigationConstraint)); // save due to static_assert + emit navigationConstraintChanged(navigationConstraint); + } + return true; +#else + return true; // handle as successful operation +#endif // HAS_BELOW_GROUND +} + +float MapImpl::baseSurfaceOpacity() const +{ +#ifdef HAS_BELOW_GROUND + Esri::ArcGISRuntime::Surface* baseSurface = nullptr; + if (!d->m_esriScene || !(baseSurface = d->m_esriScene->baseSurface())) + return 1.0; // opac, no transparency + return baseSurface->opacity(); +#else + return 1.0; // opac, no transparency +#endif // HAS_BELOW_GROUND +} + +bool MapImpl::setBaseSurfaceOpacity(float surfaceOpacity) +{ +#ifdef HAS_BELOW_GROUND + Esri::ArcGISRuntime::Surface* baseSurface = nullptr; + if (!d->m_esriScene || !(baseSurface = d->m_esriScene->baseSurface())) + return false; + if (!qFuzzyCompare(baseSurface->opacity(), surfaceOpacity)) + { + baseSurface->setOpacity(surfaceOpacity); + emit baseSurfaceOpacityChanged(baseSurface->opacity()); + } + return true; +#else + Q_UNUSED(surfaceOpacity); + return true; // handle as successful operation +#endif // HAS_BELOW_GROUND +} + +OverviewMapHandler &MapImpl::overviewMapHandler() const +{ + if (! d->m_overviewMapHandler) + { + MapImpl *THIS = const_cast(this); + d->m_overviewMapHandler = new OverviewMapHandlerImpl(THIS); + } + return *d->m_overviewMapHandler; +} + + +OperatorMapSettings MapImpl::operatorMapSettings() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" ; + + OperatorMapSettings oms; + + if (!isReady()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " map not ready"; + return oms; + } + + + oms.mode = this->mode(); + + // + // 1. viewpoint/camera : + // + switch (oms.mode) + { + case Mode::Mode_2D: + oms.viewpoint = this->viewpoint2D(); + break; + case Mode::Mode_3D: + oms.camera = this->camera(); +// oms.viewpoint.setScale(this->scale()); // additionally save current scale (which is not in camera) + oms.viewpoint = this->viewpoint2D(); + break; + default: + // empty + break; + } + // : viewpoint/camera + + // + // 1a. below ground : + // + oms.navigationConstraint = this->navigationConstraint(); + oms.baseSurfaceOpacity = this->baseSurfaceOpacity(); + // : 1a. below ground + + // + // 2. orderedLayerList : + // + const LayerFactory &layerFactory = this->layerFactory(); + + const QList &layernames = layerFactory.layernames(); + for (const auto &layername : layernames) + { + QPair pair; + pair.first = layername; + pair.second = layerFactory.layerParameter(layername); + + oms.orderedLayers.append(pair); + } + // : orderedLayerList + + // + // 3. orderedOverlayList : + // + const OverlayHandler &overlayHndl = this->overlayHandler(); + oms.orderedOverlays = overlayHndl.overlays(); + // : orderedOverlayList + + // + // 4. some visiblities + // + oms.mapWidgetSettings.gridVisibility = this->grid().isVisible(); + oms.mapWidgetSettings.positionStringVisibility = this->isPositionStringVisible(); + oms.mapWidgetSettings.exerciseTimeStringVisibility = this->isExerciseTimeStringVisible(); + oms.mapWidgetSettings.timeStringVisibility = this->isTimeStringVisible(); + oms.mapWidgetSettings.compassVisibility = this->isCompassVisible(); + oms.mapWidgetSettings.overviewMapVisibility = this->overviewMapHandler().builtInOverviewMapWidget() != nullptr; + + oms.symbolScale = this->symbolScale(); + // oms.symbolOpacity = this->symbol + + + // + // 5. JumpPointWidget : + // + if (d->m_jumpPointWidget) + { + const QVector &jumpPoints = d->m_jumpPointWidget->jumpPoints(); + + JumpPointData jpd; + for (const JPData &jpKey : jumpPoints) + { + jpd.key = jpKey.m_name; + jpd.originGeo = jpKey.m_originGeo; + jpd.mapScale = jpKey.m_mapScale; + + oms.jumpPoints.append(jpd); + } + } + // : JumpPointWidget + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return oms; +} + +bool MapImpl::restoreOperatorMapSettings(const OperatorMapSettings &oms) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!isReady()) + { + INFO_LOG(m_logger) << LOGFUNCTION << " map not ready"; + return false; + } + + if (!isValid(oms.mode)) + { + INFO_LOG(m_logger) << LOGFUNCTION << " mode no valid"; + return false; + } + + bool success = true; + + const bool switchingBetweenModes = + isValid(this->mode()) && + isValid(oms.mode) && + (this->mode() != oms.mode); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " switchingBetweenModes = " /*<< std::boolalpha*/ << switchingBetweenModes + << " (this->mode()=" << this->mode() + << " oms.mode=" << oms.mode << ")"; + + // conflict with FilterManagement::SymbolAppearanceSettings::Settings::symbolScale + // so skip oms.symbolScale + // this->setSymbolScale(oms.symbolScale); + + // + // 1. viewpoint/camera : + // + + // Possible cases: + // F1 - 2D -> 2D : setViewpoint2D + // F2 - 3D -> 3D : setCamera + // F3 - 2D -> 3D : setViewpoint2D + // F4 - 3D -> 2D : setViewpoint2D ( finalViewpoint ) + + switch (oms.mode) + { + case Mode::Mode_2D: + this->setViewpoint2D(oms.viewpoint); // F1, F3 + break; + case Mode::Mode_3D: + if (this->mode() == Mode::Mode_3D && !oms.camera.isNull()) + this->setCamera(oms.camera); // F2 + else + this->setViewpoint2D(oms.viewpoint); // F4 + break; + default: + // empty + break; + } // switch + + // + // below ground : + // + this->setNavigationConstraint(oms.navigationConstraint); + this->setBaseSurfaceOpacity(oms.baseSurfaceOpacity); + // : below ground + + // + // process orderedOVERLAYList *before* orderedLAYERList + // + // 3. orderedOverlayList : + // + OverlayHandler &overlayHndl = this->overlayHandler(); + for (auto ovlKey : oms.orderedOverlays) + { + QByteArray overlayContent; + + QString displaynameEmpty; // empty -> logicalLayername will we shown. This name will be overwritten if overlay is loaded and updated. + + // GUEZE-811 3D Lage Filter werde nicht korrekt abgebaut... wenn 2D-Snap + // mit SnapshotPlan auf 3D-Topic folgt... + // Szenario: 2D-Snapshuss wird wiedergegeben (App Snapshot|Script) + // und SitMap muss dafür von 3D auf 2D wechseln. + // Dabei werden die Overlays via MapOperatorSettings zunächst vorbereitet. + // Dabei wurde aber in MapImpl::restoreOperatorMapSettings der SNAPSHOT-Overlay + // als herkömmlicher Overlay vorbereitet. + // Beim nachfolgendem OverlayHandlerImpl::setOverlay usw. ergaben sich Fehler + // wg. LayerType::SnapshotOverlay vs. LayerType::Overlay. + // Mit dem FixPart1 wird in restoreOperatorMapSettings der snapShotOverlayId == -4711 + // als isSnapshot==true eintragen. + // Mit FixPart2 bei SnapshotDialog::showSnapshotInternal ein + // setMapReadyCallback genutzt, weil die SitMap noch nicht mapReady ist. + // Mit FixPart3 bei InterfaceImpl::snapshotAdjustSitMap ebenfalls ein + // setMapReadyCallback genutzt, weil die SitMap noch nicht mapReady ist. + + // Zu FixPart1 / GUEZE-1011 + // Mit GUEZE-1011 'Absturz Planeditor' hat sich ein Problem gezeigt: + // Snap aufrufen, PlanEditor (auf dem Snap starten) ein TextObj anlegen... PlanEditor beenden -> Absturz + // Tritt auf, wenn -4711 als isSnapshot = true eingetragen wird. Daher lassen wir dass... + // const int snapShotOverlayId = -4711; // TODO: ??? + + // const bool isSnapshot = false; //(ovlKey == snapShotOverlayId); + + // GUEZE-1723 (swp_2023) + // Der SnapshotOverlay wurde (teilweise, ?3D -> 2D?) bei ersten Mal nicht geladen. + // Daher isSnapshot wieder auf true, + // Dafür den 'Absturz Planeditor' behoben (dtor geomodel at PlanEditor::onSignalClose) + // ---> Nun wird also der der SnapshotOverlay auch als Layer mit isSnapshot==true erstellt, + // korrekt in der layerOrder eingetragen. Displayname wird mit dem Laden "nachgeschoben. + const int snapShotOverlayId = -4711; + const bool isSnapshot = (ovlKey == snapShotOverlayId); + + if (!overlayHndl.setOverlay(ovlKey, displaynameEmpty, overlayContent, isSnapshot)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " addOverlay failed, key=" << ovlKey; + success = false; + } + } // foreach + // : orderedOverlayList + + + // + // 2. orderedLayerList : + // + LayerFactory &layerFactory = this->layerFactory(); + const QStringList &curLayernames = layerFactory.layernames(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " restore curLayernames = " << curLayernames.join(QStringLiteral(";")); + + QStringList newOrderedLayerAll; + newOrderedLayerAll.reserve(oms.orderedLayers.size()); + + for (const auto &item : oms.orderedLayers) + { + const QString &layername = item.first; + LayerParameter layerParameter = item.second; + newOrderedLayerAll.append(layername); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " restore orderedLayerList = " + << " layername='" << layername << "'" + << " layerParameter=" << layerParameter; + + // restore Parameter: + if (curLayernames.contains(layername)) + { + // maybe keep previewImage, displayName, description + const LayerParameter ¤tLp = layerFactory.layerParameter(layername); + if (layerParameter.previewImage.isNull()) layerParameter.previewImage = currentLp.previewImage; + if (layerParameter.displayName.isEmpty()) layerParameter.displayName = currentLp.displayName; + if (layerParameter.description.isEmpty()) layerParameter.description = currentLp.description; + + // // GUEZE-2928 // Filtervoreinstellungen werden nicht übernommen + // GUEZE-2928 steht aber auch im Widerspruch zu GUEZE-1711 + // if (!isReplay() && (layername == layerFactory.emergencyCallLayer(true))) + // { + // // for Live: + // layerParameter.visible = true; + // layerParameter.opacity = 100; + // layerParameter.minScale = -1; + // layerParameter.maxScale = -1; + // DEBUG_LOG(m_logger) << LOGFUNCTION << " 'fix' layerParameter due to emergencyCallLayer"; + // } + + if (! layerFactory.setLayerParameter(layername, layerParameter)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " setLayerParameter failed: layername='" << layername << "'"; + success = false; + } + } + else + WARNING_LOG(m_logger) << LOGFUNCTION << " layer not found: layername='" << layername << "'"; + + } // for + + // restore order : + if (!newOrderedLayerAll.isEmpty()) // in case of initial/empty oms, newOrderedLayerAll is empty + { + if (! layerFactory.orderLayerTo(newOrderedLayerAll)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " orderLayerTo failed"; + success = false; + } + } + // : restore order + + // : orderedLayerList + + + // + // 4. some flags, visiblities etc: + // + this->grid().setVisible(oms.mapWidgetSettings.gridVisibility); + this->showPositionString(oms.mapWidgetSettings.positionStringVisibility); + this->showExerciseTimeString(oms.mapWidgetSettings.exerciseTimeStringVisibility); +#ifndef TABLET_MODE + this->showTimeString(oms.mapWidgetSettings.timeStringVisibility); +#endif + this->showCompass(oms.mapWidgetSettings.compassVisibility); + + if (oms.mapWidgetSettings.overviewMapVisibility) + this->overviewMapHandler().createBuiltInOverviewMap(); + else + this->overviewMapHandler().removeBuiltInOverviewMap(); + + + // + // 5. JumpPointWidget : + // + if (d->m_jumpPointWidget) + { + d->m_jumpPointWidget->cleanup(); + for (const auto &jpd : oms.jumpPoints) + { + d->m_jumpPointWidget->addJumpPoint(jpd.key, + jpd.originGeo, + jpd.mapScale); + + } // foreach + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success = " << success; + return success; +} + + + + +Esri::ArcGISRuntime::GraphicsOverlay *MapImpl::getGraphicsLayer(const QString &logicalName) +{ + return this->layerFactoryImpl().getGraphicsLayer(logicalName); +} + +Esri::ArcGISRuntime::GraphicsOverlay *MapImpl::getGraphicsLayerDict(const QString &logicalName) +{ + return this->layerFactoryImpl().getGraphicsLayerDict(logicalName); +} + +Esri::ArcGISRuntime::GraphicsOverlay *MapImpl::getGraphicsLayer3DExtrusion(const QString &logicalName) +{ + return this->layerFactoryImpl().getGraphicsLayer3DExtrusion(logicalName); +} + +Esri::ArcGISRuntime::GraphicsOverlay *MapImpl::getGraphicsLayer3D_SpAbsolute(const QString &logicalName) +{ + return this->layerFactoryImpl().getGraphicsLayer3D_SpAbsolute(logicalName); +} + + +CoordTranslationFunctor *MapImpl::coordTranslationFunctor() const +{ + return d->m_coordTranslationFunctor; +} + + +ObjectContainer &MapImpl::objectContainer() +{ + return *(d->m_objectContainer.data()); +} +void MapImpl::setObjectContainer(QSharedPointer objectContainer) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + d->m_objectContainer = objectContainer; + + this->objectFactory().setObjectContainer(d->m_objectContainer); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +PartyIdToColorFunctor &MapImpl::partyIdToColorFunctor() const +{ + return *(d->m_partyIdToColorFunctor.data()); +} + +void MapImpl::setPartyIdToColorFunctor(QSharedPointer partyIdToColor) +{ + d->m_partyIdToColorFunctor = partyIdToColor; + + // delay that signal, in rpelay it occurs more often: + connect(d->m_partyIdToColorFunctor.data(), &PartyIdToColorFunctor::partyChanged, + this, [this] () { + d->m_partychangedDelayTimer->start(); // is restart timer if already running + }); + + // to create UniqueValueRenderer if required: + this->onPartyChanged(); +} + + +SituationMap::OverlayHandler &SituationMap::MapImpl::overlayHandler() const +{ + if (!d->m_overlayHandler) + { + MapImpl *map = const_cast(this); + d->m_overlayHandler = QSharedPointer (new OverlayHandlerImpl(map)); + } + return *(d->m_overlayHandler); +} + + +bool MapImpl::addJumpPoint(const QString &name, const Point3D &originGeo, double mapScale) +{ + if (!d->m_jumpPointWidget) + return false; + return d->m_jumpPointWidget->addJumpPoint(name, originGeo, mapScale); +} +bool MapImpl::removeJumpPoint(const QString &name) +{ + if (!d->m_jumpPointWidget) + return false; + return d->m_jumpPointWidget->removeJumpPoint(name); +} +QStringList MapImpl::jumpPointNames() const +{ + if (!d->m_jumpPointWidget) + return {}; + return d->m_jumpPointWidget->names(); +} + + +void MapImpl::initCoordTranslationFunctor() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (! d->m_coordTranslationFunctorInitialize) + d->m_coordTranslationFunctorInitialize = new CoordTranslationFunctorImpl(this); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +AdvancedSymbology::SymbolDictionary &MapImpl::symbolDictionary() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + AdvancedSymbology::SymbolDictionary &dict = + AdvancedSymbology::SymbolDictionary::dictionary(d->m_symbologyType); + + // check mapReady and SymbolDictionary::loaded + connect(&dict, &AdvancedSymbology::SymbolDictionary::loaded, + this, &MapImpl::onCheckMapReadyAndSymbolDictionaryLoaded, Qt::UniqueConnection); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return dict; +} + +bool MapImpl::isSymbolDictionaryLoaded() const +{ + if (d->m_symbologyType == AdvancedSymbology::SymbologyType::None) + return true; + + bool isExisting = AdvancedSymbology::SymbolDictionary::alreadyExists(d->m_symbologyType) && symbolDictionary().isLoaded(); + return isExisting; +} + +bool MapImpl::setSymbologyType(AdvancedSymbology::SymbologyType symbologyType) +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " set SymbologyType to " << d->m_symbologyType; + + if (d->m_symbologyType == AdvancedSymbology::SymbologyType::None) + { + d->m_symbologyType = symbologyType; + + AdvancedSymbology::SymbolDictionary &dict = this->symbolDictionary(); + dict.load(); + return dict.isOk(); + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " modification of symbologyType is not allowed"; + return false; +} + +AdvancedSymbology::SymbologyType MapImpl::symbologyType() +{ + return d->m_symbologyType; +} + + +Viewpoint2D MapImpl::initialViewpoint() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, initialVp=" << d->m_initialViewpoint; + return d->m_initialViewpoint; +} + + +void MapImpl::setInitialViewpoint(const Viewpoint2D &initialViewpoint) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, initialViewpoint=" << initialViewpoint; + + d->m_initialViewpoint = initialViewpoint; + if (d->m_initialViewpoint.scale() <= 0) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " initialViewpoint.scale less or equal zero!!! set to 1"; + d->m_initialViewpoint.setScale(1.0); + } + + // rad to deg: + QPointF centerGeo = d->m_initialViewpoint.centerGeo(); + centerGeo.rx() *= LtsCoordTrans::rad2deg(); // x = lon + centerGeo.ry() *= LtsCoordTrans::rad2deg(); // y = lat + + // lat, lon + const Esri::ArcGISRuntime::Viewpoint initialVp(centerGeo.y(), centerGeo.x(), + d->m_initialViewpoint.scale()); // skip rotation here + + switch (mode()) + { + case Mode::Mode_2D: + d->m_esriMap->setInitialViewpoint(initialVp); + break; + case Mode::Mode_3D: + d->m_esriScene->setInitialViewpoint(initialVp); + break; + default: + FATAL_LOG(m_logger) << LOGFUNCTION << " mode is undefined!!!"; + FATAL_LOG(m_logger) << LOGFUNCTION << " mode is undefined!!!"; + FATAL_LOG(m_logger) << LOGFUNCTION << " mode is undefined!!!"; + FATAL_LOG(m_logger) << LOGFUNCTION << " mode is undefined!!!"; + FATAL_LOG(m_logger) << LOGFUNCTION << " mode is undefined!!!"; + break; + } +} + + +Viewpoint2D MapImpl::viewpoint2D() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + Viewpoint2D vp; + vp.setCenterGeo(this->centerPoint()); + vp.setScale(this->scale()); + vp.setRotation(this->rotation()); + TRACE_LOG(m_logger) << LOGFUNCTION << " END, vp=" << vp; + return vp; +} + +void MapImpl::setViewpoint2D(const Viewpoint2D &viewpoint, float durationSeconds) +{ + this->setViewpoint2DInternal(viewpoint, false, durationSeconds); +} + +void MapImpl::setViewpoint2DAndWait(const Viewpoint2D &viewpoint) +{ + this->setViewpoint2DInternal(viewpoint, true, -1.0); +} + + +void MapImpl::setViewpoint2DInternal(const Viewpoint2D &viewpoint, bool andWait, float durationSeconds) +{ + INFO_LOG(m_logger) << LOGFUNCTION << " START, vp=" << viewpoint << " andWait=" << andWait << " durationSeconds=" << durationSeconds; + + Esri::ArcGISRuntime::GeoView *geoview = this->getEsriGeoView(); + const CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor(); + if (!geoview || !ctFunctor) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; + return; + } + + const QPointF ¢erPointMap = ctFunctor->geographicToMap(viewpoint.centerGeo()); + const Esri::ArcGISRuntime::Point esriPoint(centerPointMap.x(), centerPointMap.y(), this->getEsriSpatialReference()); + + if (!esriPoint.isValid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " esriPoint not valid!!!" + << " (viewpoint=" << viewpoint << ")"; + return; + } + + if (viewpoint.isNull()) + { + WARNING_LOG(m_logger) << LOGFUNCTION << " viewpoint is null -> skip!!!" + << " (viewpoint=" << viewpoint << ")"; + return; + } + + // scale in Viewport: value of 0 and below may raise an exception. Try to avoid: + double scale = viewpoint.scale(); + if (scale <= 0) + { + scale = Viewpoint2D::defaultScale; + ERROR_LOG(m_logger) << LOGFUNCTION << " viewpoint.scale less or equal zero!!! set to default (" << scale << ")" + << " (viewpoint=" << viewpoint << ")"; + } + + const Esri::ArcGISRuntime::Viewpoint targetVp(esriPoint, + scale, + viewpoint.rotation() * -1.0); // clock vs. counterClock + + if (andWait) + geoview->setViewpointAsync(targetVp); + else + { + + // use default (d->m_animationDuration) if not specified: + if (durationSeconds < 0) + durationSeconds = d->m_animationDuration; + + if (durationSeconds < 0) + geoview->setViewpointAsync(targetVp); + else + geoview->setViewpointAsync(targetVp, durationSeconds); + + } + + d->m_rotationAbsoluteAngleDegrees = viewpoint.rotation(); + emit rotationChanged(d->m_rotationAbsoluteAngleDegrees); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, vp=" << viewpoint; +} + +QPointF MapImpl::centerPoint() const +{ + Esri::ArcGISRuntime::GeoView *geoview = this->getEsriGeoView(); + const CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor(); + if (!geoview || !ctFunctor) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; + return QPointF(); + } + + const Esri::ArcGISRuntime::Viewpoint &curVp = + geoview->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + + Esri::ArcGISRuntime::Point mapCenter = + static_cast(curVp.targetGeometry()); + + const QPointF center(mapCenter.x(), mapCenter.y()); + + return ctFunctor->mapToGeographic(center); +} + + +void MapImpl::setCenterPoint(const QPointF ¢er, double scale, float durationSeconds) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " center=" << center + << " scale=" << scale + << " durationSeconds=" << durationSeconds + << " sp=" << this->getEsriSpatialReference().wkid(); + + Esri::ArcGISRuntime::GeoView *geoview = this->getEsriGeoView(); + const CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor(); + + if (!geoview || !ctFunctor) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; + return; + } + + const QPointF ¢erPointMap = ctFunctor->geographicToMap(center); + + const Esri::ArcGISRuntime::Point esriPoint(centerPointMap.x(), centerPointMap.y(), this->getEsriSpatialReference()); + + const Esri::ArcGISRuntime::Viewpoint &curVp = + geoview->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + + Esri::ArcGISRuntime::Viewpoint targetVp(esriPoint, + (scale < 0) ? curVp.targetScale() : scale + ); + // use default (d->m_animationDuration) if not specified: + if (durationSeconds < 0) + durationSeconds = d->m_animationDuration; + + if (durationSeconds < 0) + geoview->setViewpointAsync(targetVp); + else + geoview->setViewpointAsync(targetVp, durationSeconds); + + if (isPositionStringVisible()) + { + d->m_positionStr = this->mapToMgrsString(centerPointMap); + d->updatePositionText(); + } +} + +void MapImpl::zoomToArea(const QVector &coords, int paddingInDIPS) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " coords=" << coords.size(); + + Esri::ArcGISRuntime::GeoView *geoview = this->getEsriGeoView(); + const CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor(); + + if (!geoview || !ctFunctor) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; + return; + } + + if (coords.size() < 2) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " not enough coords (" << coords.size() << ")"; + return; + } + + Esri::ArcGISRuntime::PolygonBuilder pBuilder(geoview->spatialReference()); + for (const auto & coord : coords) + { + QPointF mapCoord = ctFunctor->geographicToMap(coord); + pBuilder.addPoint(mapCoord.x(), mapCoord.y()); + } // for + + switch (this->mode()) + { + case Mode::Mode_2D: + { + Esri::ArcGISRuntime::MapGraphicsView *mapView = static_cast(geoview); + mapView->setViewpointGeometryAsync(pBuilder.toGeometry(), paddingInDIPS); + } + break; + case Mode::Mode_3D: + { + Esri::ArcGISRuntime::SceneGraphicsView *sceneView = static_cast(geoview); + Esri::ArcGISRuntime::Viewpoint esriVp(pBuilder.toGeometry()); // unfortunately padding is not supported here + sceneView->setViewpointAsync(esriVp); + } + break; + case Mode::Undefined: + {} + } // switch +} + +void MapImpl::setTrainingsAreaExtent(const QVector &trainingsAreaExtent) +{ + d->m_trainingsAreaExtent = trainingsAreaExtent; +} + + +QRectF MapImpl::extent() const +{ + if (d->m_mapGraphicsView) + { + const Esri::ArcGISRuntime::Viewpoint &vp = + d->m_mapGraphicsView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::BoundingGeometry); + + if (vp.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Viewpoint is empty"; + return QRect(); + } + + const Esri::ArcGISRuntime::Envelope &env = + static_cast(vp.targetGeometry()); + + if (std::isnan(env.width()) || std::isnan(env.height())) + ERROR_LOG(m_logger) << LOGFUNCTION << " Envelope: width|height isnan"; + + // DEBUG_LOG(m_logger) << LOGFUNCTION + // << " xMin =" << env.xMin() + // << " yMin =" << env.yMin() + // << " xMax =" << env.xMax() + // << " yMax =" << env.yMax() + // << " width =" << env.width() << " height =" << env.height(); + + CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor(); + if (ctFunctor) + { + const QPointF upperLeft(ctFunctor->mapToGeographic(env.xMax(), + env.yMin())); + const QPointF lowerRight(ctFunctor->mapToGeographic(env.xMin(), + env.yMax())); + + return QRectF(upperLeft, lowerRight); + } + else + { + // QRect -> left, top, width, height + return QRectF(env.xMin(), + env.yMin(), // use yMin, not yMax + env.width(), + env.height()); + } + + + } + else + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " no object"; + return QRectF(); + } +} + +void MapImpl::setExtent(const QRectF &extent) +{ + // !!! extent.bottom__.y() > extent.bottom.top__.y() !!! + setExtent(extent.topLeft() .x(), extent.topLeft(). y(), + extent.bottomRight().x(), extent.bottomRight().y()); +} + +void MapImpl::setExtent(double xmin, double ymin, double xmax, double ymax) +{ + INFO_LOG(m_logger) << LOGFUNCTION << " xmin=" << xmin << " ymin=" << ymin << " xmax=" << xmax << " ymax=" << ymax + << " w=" << xmax - xmin << " h= " << ymax - ymin; + + if (d->m_mapGraphicsView) + { + Esri::ArcGISRuntime::Envelope env; + + CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor(); + if (ctFunctor) + { + const QPointF bottomLeft(ctFunctor->geographicToMap(xmin, ymin)); + const QPointF topRight(ctFunctor->geographicToMap(xmax, ymax)); + + // xmin,ymin,xmax,ymax + env = Esri::ArcGISRuntime::Envelope(bottomLeft.x(), bottomLeft.y(), + topRight.x(), topRight.y(), d->m_esriMap->spatialReference()); + } + else + { + ERROR_LOG(m_logger) << LOGFUNCTION << " !!! no CoordTranslationFunctor!!!" + " interpret values as absolute values"; + // setCoords -> xmin,ymin,xmax,ymax + env = Esri::ArcGISRuntime::Envelope(xmin, ymin, xmax, ymax, d->m_esriMap->spatialReference()); + } + d->m_mapGraphicsView->setViewpointAsync(env, d->m_animationDuration); + //d->m_mapGraphicsView->setViewpointGeometry(env); + ///// d->m_absoluteScale = d->m_esriMap->scale(); // update d->m_absoluteScale + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; +} + +void MapImpl::setExtentMapWorld(double xmin, double ymin, double xmax, double ymax) +{ + if (d->m_mapGraphicsView) + { + Esri::ArcGISRuntime::Envelope env(xmin, ymin, xmax, ymax, d->m_esriMap->spatialReference()); + d->m_mapGraphicsView->setViewpointAsync(Esri::ArcGISRuntime::Viewpoint(env)); + d->m_absoluteScale = this->scale(); // update d->m_absoluteScale + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; +} + + +/* +QRectF MapImpl::fullExtent() const +{ + if (d->m_esriMap) + { + const EsriRuntimeQt::Envelope &env = d->m_esriMap->fullExtent(); + + + CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor(); + if (ctFunctor) + { + const QPointF upperLeft(ctFunctor->mapToGeographic(env.upperLeft())); + const QPointF lowerRight(ctFunctor->mapToGeographic(env.lowerRight())); + + return QRectF(upperLeft, lowerRight); + + } + else + { + // QRect -> left, top, width, height + return QRectF(env.xMin(), + env.yMin(), // use yMin, not yMax + env.width(), + env.height()); + } + + } + else + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; + return QRectF(); + + } +} +*/ + + +double MapImpl::scale() const +{ + + if (!this->isReady()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " not ready yet!!!"; + return 0.0; + } + + if (d->m_destroying) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " is going to be destroyed!!!"; + return 0.0; + } + + // Keep in mind: while animation, esriMap delivers to old value. +#ifdef SITUATIONMAP_OWN_WHEEL_HANDLING + return d->m_absoluteScale; +#else + + if (this->getEsriGeoView()) + { + + const Esri::ArcGISRuntime::Viewpoint &vp = + this->getEsriGeoView()->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + + + if (vp.isEmpty()) + return 0.0; + + double scale = vp.targetScale(); + return scale; + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; + return 0.0; +#endif // SITUATIONMAP_OWN_WHEEL_HANDLING +} + +void MapImpl::setScale(double absoluteScale) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " absoluteScale = " << absoluteScale; + + if (d->m_mapGraphicsView) + { + const Esri::ArcGISRuntime::Viewpoint &curVp = + d->m_mapGraphicsView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + Esri::ArcGISRuntime::Viewpoint targetVp(curVp.targetGeometry(), absoluteScale); + d->m_mapGraphicsView->setViewpointAsync(targetVp); + d->m_absoluteScale = absoluteScale; + } + else if (d->m_sceneGraphicsView) + { + FATAL_LOG(m_logger) << LOGFUNCTION << " Does this work with V100 ???"; + FATAL_LOG(m_logger) << LOGFUNCTION << " Does this work with V100 ???"; + FATAL_LOG(m_logger) << LOGFUNCTION << " Does this work with V100 ???"; + FATAL_LOG(m_logger) << LOGFUNCTION << " Does this work with V100 ???"; + FATAL_LOG(m_logger) << LOGFUNCTION << " Does this work with V100 ???"; + + // Check: Does this work with QuartzFinal? + // Or Camera::moveForward ?? + const Esri::ArcGISRuntime::Viewpoint &curVp = + d->m_sceneGraphicsView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + + Esri::ArcGISRuntime::Viewpoint targetVp(curVp.targetGeometry(), + (absoluteScale < 0) ? curVp.targetScale() : absoluteScale); + + d->m_sceneGraphicsView->setViewpointAsync(targetVp); + + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; +} + + +void MapImpl::setMinScale(double minScaleToLimitZoomOut) +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " minScaleToLimitZoomOut = " << minScaleToLimitZoomOut; + + if (d->m_esriMap) + d->m_esriMap->setMinScale(minScaleToLimitZoomOut); + else + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; +} + + +double MapImpl::minScale() const +{ + if (d->m_esriMap) + return d->m_esriMap->minScale(); + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; + return 0; +} + + +void MapImpl::setMaxScale(double maxScaleToLimitZoomIn) +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " maxScaleToLimitZoomIn = " << maxScaleToLimitZoomIn; + + if (d->m_esriMap) + d->m_esriMap->setMaxScale(maxScaleToLimitZoomIn); + else + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; +} + +double MapImpl::maxScale() const +{ + if (d->m_esriMap) + return d->m_esriMap->maxScale(); + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; + return 0; +} + + +double MapImpl::rotation() const +{ + // return d->m_rotationAbsoluteAngleDegrees; + + if (this->getEsriGeoView()) + { + const Esri::ArcGISRuntime::Viewpoint &vp = + this->getEsriGeoView()->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + double rot = vp.rotation(); + if (std::isnan(rot)) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " nan detected..."; + return 0; + } + rot *= -1.0; // clock vs. counterClock + if (rot < 0) + rot += 360.0; + return rot; + } + else + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; + return 0.0; + } +} + +void MapImpl::setRotation(double absoluteAngleDegrees) +{ + Esri::ArcGISRuntime::GeoView *geoview = this->getEsriGeoView(); + + if (mode() == Mode::Mode_3D) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; + return; + } + + if (geoview) + { + d->m_rotationAbsoluteAngleDegrees = absoluteAngleDegrees; + + const Esri::ArcGISRuntime::Viewpoint &curVp = + geoview->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + + Esri::ArcGISRuntime::Viewpoint targetVp(curVp.targetGeometry(), + d->m_rotationAbsoluteAngleDegrees * -1.0); // clock vs. counterClock + geoview->setViewpointAsync(targetVp); + emit rotationChanged(d->m_rotationAbsoluteAngleDegrees); + } + + // if (d->m_mapGraphicsView) + // { + // d->m_rotationAbsoluteAngleDegrees = absoluteAngleDegrees; + // d->m_mapGraphicsView->setViewpointRotation( d->m_rotationAbsoluteAngleDegrees * -1.0 ); // clock vs. counterClock + // emit rotationChanged(d->m_rotationAbsoluteAngleDegrees); + // } +} + + +void MapImpl::onEsriRotate() +{ + double rot = rotation(); + + if (!qFuzzyCompare(rot, d->m_rotationAbsoluteAngleDegrees)) + { + d->m_rotationAbsoluteAngleDegrees = rot; + emit rotationChanged(d->m_rotationAbsoluteAngleDegrees); + } +} + + +Esri::ArcGISRuntime::Camera cameraToEsriCamera(const Camera &camIn) +{ + // rad to degree + const double factor = 180 / M_PI; + return Esri::ArcGISRuntime::Camera(camIn.loc_y_lat * factor, // latitude = y + camIn.loc_x_lon * factor, // longitude = x + camIn.loc_z, + camIn.heading * factor, + camIn.pitch * factor, + camIn.roll * factor + ); +} + +Camera esriCameraToCamera(const Esri::ArcGISRuntime::Camera &camIn) +{ + Camera cam; + const Esri::ArcGISRuntime::Point &loc = camIn.location(); + const double factor = M_PI / 180; + cam.loc_x_lon = loc.x() * factor; + cam.loc_y_lat = loc.y() * factor; + cam.loc_z = loc.z(); + + cam.heading = camIn.heading() * factor; + cam.roll = camIn.roll() * factor; + cam.pitch = camIn.pitch() * factor; + + return cam; +} + + +Camera MapImpl::camera() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!d->m_sceneGraphicsView) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; + return Camera(); + } + + const Esri::ArcGISRuntime::Camera &curCam = d->m_sceneGraphicsView->currentViewpointCamera(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return esriCameraToCamera(curCam); +} + +void MapImpl::setCamera(const Camera &camera, float durationSeconds) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + #ifndef WIN32 + << " camera=" << camera + #endif // WIN32 + << ", durationSeconds=" << durationSeconds; + + if (!d->m_sceneGraphicsView) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no object"; + return; + } + + const Esri::ArcGISRuntime::Camera &tarCam = cameraToEsriCamera(camera); + + // avoid neg durationSeconds + if (durationSeconds < 0) + d->m_sceneGraphicsView->setViewpointCameraAsync(tarCam); + else + d->m_sceneGraphicsView->setViewpointCameraAsync(tarCam, durationSeconds); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +Grid &MapImpl::grid() const +{ + if (!d->m_gridImpl) + { + MapImpl *THIS = const_cast(this); + d->m_gridImpl = new SituationMap::GridImpl(THIS); + } + return *(d->m_gridImpl); +} + + +ObjectFactory &MapImpl::objectFactory() +{ + if (!d->m_objectFactory) + d->m_objectFactory = new ObjectFactory(this); + + return *(d->m_objectFactory); +} + +LayerFactory &MapImpl::layerFactory() const +{ + return layerFactoryImpl(); +} + +LayerFactoryImpl &MapImpl::layerFactoryImpl() const +{ + if (!d->m_layerFactoryImpl) + { + MapImpl *THIS = const_cast(this); + d->m_layerFactoryImpl = new LayerFactoryImpl(THIS, THIS); + } + return *(d->m_layerFactoryImpl); +} + +int MapImpl::basemapLayerCount() const +{ + int counter = -1; + if (d->m_basemap && d->m_basemap->baseLayers()) + counter = d->m_basemap->baseLayers()->size(); + return counter; +} + +int MapImpl::graphicsOverlayCount() const +{ + int counter = -1; + if (d->m_mapGraphicsView && d->m_mapGraphicsView->graphicsOverlays()) + counter = d->m_mapGraphicsView->graphicsOverlays()->size(); + return counter; +} + +int MapImpl::technicalLayerCount() const +{ + int counter = 0; + + int cTemp = basemapLayerCount(); + if (cTemp >= 0) + counter += cTemp; + + cTemp = graphicsOverlayCount(); + if (cTemp >= 0) + counter += cTemp; + return counter; +} + +void MapImpl::emitVisualComponentChanged(const VisualComponent &visualComponent) +{ + if (d->m_destroying) + return; + emit visualComponentChanged(visualComponent); +} + + +void MapImpl::onClickMapObject(const QList &uniqueMapIdList, const QPointF &pos) +{ + if (d->m_destroying) + return; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " START onClickMapObject clicked ids at viewport position x = " << pos.x() << " ; y = " << pos.y() + << " uniqueMapIdList.size() = " << uniqueMapIdList.size(); + + RefPointerMapObjectList mapObjList; + QList mapObjIdList; + bool makeObjList = isSignalConnected(QMetaMethod::fromSignal(&Map::mapObjectsClicked)); + bool makeObjIdList = isSignalConnected(QMetaMethod::fromSignal(&Map::mapObjectIdsClicked)); + + for (const UniqueMapId &uniqueMapId : uniqueMapIdList) + { + AbstractMapObject *ifMapObjPtr = d->m_objectFactory->idToMapObject(uniqueMapId); + if (ifMapObjPtr) + { + if (ifMapObjPtr->pickable()) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueMapId=" << uniqueMapId + << QStringLiteral(" Address=%1").arg((qint64)ifMapObjPtr, 0, 16) + << " object-debugInfo=" << ifMapObjPtr->debugInfo() + << " object-identifier=" << ifMapObjPtr->identifier(); + + if (makeObjList) mapObjList += RefPointer(ifMapObjPtr); + if (makeObjIdList) mapObjIdList += uniqueMapId; + + } + else + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueMapId=" << uniqueMapId + << QStringLiteral(" Address=%1").arg((qint64)ifMapObjPtr, 0, 16) + << " object-debugInfo=" << ifMapObjPtr->debugInfo() + << " object-identifier=" << ifMapObjPtr->identifier() + << " object is NOT pickable!"; + } + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " Unknown map object with uniqueMapId=" << uniqueMapId << " found."; + + } // for + + DEBUG_LOG(m_logger) << LOGFUNCTION << " END onClickMapObject clicked ids" + << ", mapObjList=" << mapObjList.size() + << ", makeObjIdList=" << mapObjIdList.size(); + + if (makeObjList) + emit mapObjectsClicked(mapObjList, pos); + + if (makeObjIdList) + emit mapObjectIdsClicked(mapObjIdList, pos); + + +} // MapImpl::onClickMapObject + + +void MapImpl::onContextClick(const QList &uniqueMapIdList, const QPointF &pos) +{ + if (d->m_destroying) + return; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " START onContextClick clicked ids at viewport position x = " << pos.x() << " ; y = " << pos.y(); + + RefPointerMapObjectList mapObjList; + bool makeObjList = isSignalConnected(QMetaMethod::fromSignal((void (Map::*)(const RefPointerMapObjectList &, const QPointF &))(&Map::contextMenuClicked))); + + if (!makeObjList) + return; + + for (UniqueMapId uniqueMapId : uniqueMapIdList) + { + AbstractMapObject *ifMapObjPtr = d->m_objectFactory->idToMapObject(uniqueMapId); + + if (ifMapObjPtr) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " uniqueMapId=" << uniqueMapId + << QStringLiteral(" Address=%1").arg((qint64)ifMapObjPtr, 0, 16) + << " object-debugInfo=" << ifMapObjPtr->debugInfo() + << " object-identifier=" << ifMapObjPtr->identifier(); + + if (makeObjList) mapObjList += RefPointer(ifMapObjPtr); + + } + else ERROR_LOG(m_logger) << LOGFUNCTION << "Unknown map object with uniqueMapId=" << uniqueMapId << " found."; + } // for + + DEBUG_LOG(m_logger) << LOGFUNCTION << " END onContextClick clicked ids"; + + if (makeObjList) emit contextMenuClicked(mapObjList, pos); + +} // MapImpl::onContextClick() + + +void MapImpl::onWheelZoom(qreal factor, const QPointF &cursorPositionMap) +{ + if (d->m_destroying) + return; + + TRACE_LOG(m_logger) << LOGFUNCTION << " factor = " << factor + << " cursorPositionMap = " << cursorPositionMap; + + // scale -> non-animated ; zoomTo -> animated + // zoomToScale( d->m_absoluteScale *= factor ); + ///// zoomToScaleMapCoordinate(d->m_absoluteScale *= factor, cursorPositionMap); + +} // MapImpl::onWheelZoom( qreal factor ) + +void MapImpl::startRocket() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " current Scale = " << d->m_mapGraphicsView->mapScale(); + + if (rocketIsRunning()) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " rocket is running -> return"; + return; + } + + // Save value + // for 2D + d->m_currentCenterPointBeforeRocket = centerPoint(); + d->m_currentScaleBeforeRocket = scale(); + d->m_currentCameraBeforRocket = camera(); + + // Zoom out + switch (this->mode()) + { + case Mode::Mode_2D: + setCenterPoint(d->m_currentCenterPointBeforeRocket, d->m_currentScaleBeforeRocket * 10, d->m_rocketDuration); + break; + case Mode::Mode_3D: + { + Esri::ArcGISRuntime::Camera curCam = cameraToEsriCamera(d->m_currentCameraBeforRocket); + const double curHeight = d->m_currentCameraBeforRocket.loc_z; + const Esri::ArcGISRuntime::Camera &newCam = curCam.moveForward(curHeight * 10.0 * -1); + setCamera(esriCameraToCamera(newCam), d->m_rocketDuration); + break; + } + case Mode::Undefined: + // empty; + break; + } // switch + + delete d->m_rocketTimer; + d->m_rocketTimer = new QTimer(this); + d->m_rocketTimer->setSingleShot(true); + d->m_rocketTimer->setInterval((d->m_rocketDuration * 1000) + 100); // add small amount to let zoomSlider react :-) + connect(d->m_rocketTimer, &QTimer::timeout, this, &MapImpl::onRocketTimerZoomOutFinished); + + d->m_rocketTimer->start(); // to zoom out + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +bool SituationMap::MapImpl::rocketIsRunning() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return d->m_currentScaleBeforeRocket != 0.0; +} + +double MapImpl::symbolScale() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, symbolScale = " << d->m_symbolScale; + return d->m_symbolScale; +} + +void MapImpl::setSymbolScale(double symbolScale) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, new symbolScale = " << symbolScale; + + // 0 check range: + if (symbolScale < SituationMap::symbolScaleMinimum) + symbolScale = SituationMap::symbolScaleMinimum; + if (symbolScale > SituationMap::symbolScaleMaximum) + symbolScale = SituationMap::symbolScaleMaximum; + + // 1 + d->m_symbolScale = symbolScale; + + // 2 apply + QMutableMapIterator> mapObjIt = this->objectContainer().mutableIterator(); + + while (mapObjIt.hasNext()) + { + mapObjIt.next(); + RefPointer mapObj = mapObjIt.value(); + if (mapObj) + { + mapObj->setScale(symbolScale); + if (! mapObj->updateView()) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed, debugInfo='" << mapObj->debugInfo() << "'"; + } + } // while + + // 3 emit + emit symbolScaleChanged(d->m_symbolScale); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, new symbolScale = " << symbolScale; +} + +void MapImpl::onRocketTimerZoomOutFinished() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + // Zoom in / restore initial values: + switch (this->mode()) + { + case Mode::Mode_2D: + setCenterPoint(d->m_currentCenterPointBeforeRocket, d->m_currentScaleBeforeRocket, d->m_rocketDuration); + break; + case Mode::Mode_3D: + setCamera(d->m_currentCameraBeforRocket, d->m_rocketDuration); + break; + case Mode::Undefined: + // empty; + break; + } // switch + + + delete d->m_rocketTimer; + d->m_rocketTimer = new QTimer(this); + d->m_rocketTimer->setSingleShot(true); + d->m_rocketTimer->setInterval((d->m_rocketDuration * 1000) + 100); // add small amount to let zoomSlider react :-) + connect(d->m_rocketTimer, &QTimer::timeout, this, &MapImpl::onRocketTimerZoomInFinished); + d->m_rocketTimer->start(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void MapImpl::onRocketTimerZoomInFinished() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + delete d->m_rocketTimer; + d->m_rocketTimer = nullptr; + + d->m_currentCenterPointBeforeRocket = QPointF(); + d->m_currentScaleBeforeRocket = 0.0; + d->m_currentCameraBeforRocket.resetCamera(); + + emit rocketFinished(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void SituationMap::MapImpl::onJumpPointRequest(const QPointF &viewPortPos) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!d->m_jumpPointWidget) + return; + const QPointF &coordGeo = this->viewPortWindowPointToGeoCoordinate(viewPortPos); + connect(d->m_jumpPointWidget, &JumpPointWidget::newName, this, &MapImpl::onName); + d->m_jumpPointWidget->askUserForName(coordGeo); + +} +void SituationMap::MapImpl::onName(const QString &name, const QPointF &coordGeo) +{ + if (name.isEmpty()) + return; + + const double scale = this->scale(); + d->m_jumpPointWidget->addJumpPoint(name, coordGeo, scale); + disconnect(d->m_jumpPointWidget, &JumpPointWidget::newName, this, &MapImpl::onName); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +GeometryModelRenderer *MapImpl::createGeometryModelRenderer(const QString &layerName, GeometryModel *geometryModel, QObject *parent) +{ + return new SituationMap::GeometryModelRendererEsri(this, layerName, *geometryModel, parent); +} + + +RB::RubberbandFactory &MapImpl::rubberbandFactory() const +{ + if (!d->m_rubberbandFactory) + { + MapImpl *THIS = const_cast(this); + d->m_rubberbandFactory = new RB::RubberbandFactory(THIS); + } + return *(d->m_rubberbandFactory); +} + +bool MapImpl::pickMapObjectsArea() const +{ + // assume that we always use a rectangle (orientation north) + + RB::RubberbandRectangle *rubberband = rubberbandFactory().create(); + + if (!rubberband) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed"; + return false; + } + rubberband->constrainOrthogonal(true); + rubberband->setAutoDelete(true); + connect(rubberband, &RB::Rubberband::apply, this, &MapImpl::onRubberbandPickMapObjectsApply); + + return true; +} + + +bool MapImpl::scaleByArea() const +{ + SituationMap::RB::RubberbandRectangle *rubberband = rubberbandFactory().create(); + + if (!rubberband) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed"; + return false; + } + + rubberband->constrainOrthogonal(true); + rubberband->setAutoDelete(true); + + connect(rubberband, &SituationMap::RB::Rubberband::apply, this, [this](SituationMap::RB::RubberbandBase *rubberband) + { + if (rubberband) + { + const QVector &points = rubberband->coordinateVector(); + + if (points.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " rubberband failed, no coords"; + return; + } + + double xmin = points[0].x(); + double ymin = points[0].y(); + double xmax = xmin; + double ymax = ymin; + + for (auto pt : points) + { + if (pt.x() < xmin) xmin = pt.x(); + if (pt.y() < ymin) ymin = pt.y(); + if (pt.x() > xmax) xmax = pt.x(); + if (pt.y() > ymax) ymax = pt.y(); + } + + if ((!qFuzzyCompare(xmin, xmax)) && (!qFuzzyCompare(ymin, ymax))) + const_cast(this)->setExtent(xmin, ymin, xmax, ymax); + } + }); + return true; +} // bool MapImpl::scaleByArea() const + + +ObjectIndicator &MapImpl::objectIndicator() +{ + if (!d->m_objectIndicatorImpl) + { + d->m_objectIndicatorImpl = new ObjectIndicatorImpl(); + d->m_objectIndicatorImpl->init(this); + } + + return *(d->m_objectIndicatorImpl); +} + +LocationDisplay &MapImpl::locationDisplay() +{ + if (!d->m_locationDisplayImpl) + { + d->m_locationDisplayImpl = new LocationDisplayImpl(this); + d->m_locationDisplayImpl->init(); + } + return *(d->m_locationDisplayImpl); +} + +PixelBasedTextBoxHandler &MapImpl::pixelBasedTextBoxHandler() +{ + if (!d->m_pixelBasedTextBoxHandlerImpl) + { + d->m_pixelBasedTextBoxHandlerImpl = new PixelBasedTextBoxHandlerImpl(this); + } + return *(d->m_pixelBasedTextBoxHandlerImpl); +} + + +Measurement &MapImpl::measurement() +{ + if (!d->m_measurementImpl) + d->m_measurementImpl = new MeasurementImpl(this); + + return *(d->m_measurementImpl); +} + +TerrainProfile &MapImpl::terrainProfile() +{ + if (!d->m_terrainProfileImpl) + d->m_terrainProfileImpl = new TerrainProfileImpl(this); + + return *(d->m_terrainProfileImpl); +} + +Viewshed &MapImpl::viewshed() +{ + if (!d->m_viewshedImpl) + d->m_viewshedImpl = new ViewshedImpl(this); + + return *(d->m_viewshedImpl); +} + + +void MapImpl::onErrorGeometryEngine(const Esri::ArcGISRuntime::Error &error) +{ + FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; +} + +void MapImpl::onErrorMap(const Esri::ArcGISRuntime::Error &error) +{ + FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; +} + +void MapImpl::onErrorScene(const Esri::ArcGISRuntime::Error &error) +{ + FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; +} + +void MapImpl::onErrorOperationalLayer(const Esri::ArcGISRuntime::Error &error) +{ + FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; +} + +void MapImpl::onErrorBasemap(const Esri::ArcGISRuntime::Error &error) +{ + FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; +} + +void MapImpl::onViewpointChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (d->m_destroying) + return; + + if (!d->m_mapReadyFlag) + return; + + // to ensure recalculation: + d->m_pixelsize = -1.0; + + + // The very first time, the extent is nan (not a number) + // Therefore suppress this: + // 23.11.16, N.Thoden, V100.0 + if (d->m_suppressFirstViewpointChange) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " suppress firstViewpointChange"; + d->m_suppressFirstViewpointChange = false; + return; + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " ************** viewpointChanged=" << toString(this->extent()); + + + emit extentChanged(); + + d->m_maxExtentTimer->start(); // start -> restart + + // TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void MapImpl::onRectChanged() +{ + if (d->m_destroying) + return; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " ************** signal emitted: rectChanged ************** "; + DEBUG_LOG(m_logger) << LOGFUNCTION << " ************** signal emitted: rectChanged ************** " + << " w=" << this->width() << " - h=" << this->height(); + emit rectChanged(); +} + +QImage MapImpl::toImageSync() +{ + QScreen *screen = this->screen(); + if (!screen) + return {}; + + WId winId = this->winId(); + QSize size = this->size(); + QPixmap pix = screen->grabWindow(winId, + 0, 0, + size.width(), size.height()); + return pix.toImage(); +} + + +QUuid MapImpl::toImage() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + Esri::ArcGISRuntime::GeoView *geoview = this->getEsriGeoView(); + + if (! geoview) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no view"; + return QUuid(); + } + + // Create watcher if not exists + if (!d->m_exportImageWatcher) + { + d->m_exportImageWatcher = new QFutureWatcher(this); + connect(d->m_exportImageWatcher, &QFutureWatcher::finished, + this, [this]() { + const QImage &image = d->m_exportImageWatcher->result(); + emit imageCompleted(QUuid::createUuid(), image); + }); + } + + QFuture tw = geoview->exportImageAsync(); + if (! tw.isValid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " exportImageAsync invalid"; + return QUuid(); + } + + d->m_exportImageWatcher->setFuture(tw); + + const QUuid taskId = QUuid::createUuid(); + TRACE_LOG(m_logger) << LOGFUNCTION << " END, taskId=" << taskId; + return taskId; +} + + +bool MapImpl::toPdf(const QString &outputFilename, + const QString &title, + const QString &footerLeft, + const QString &footerCenter, + const QString &footerRight, + const QColor &textColor) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " outputFilename='" << outputFilename << "'"; + + connect(this, &MapImpl::imageCompleted, this, + [this, + outputFilename, + title, + footerLeft, footerCenter, footerRight, + textColor](const QUuid &, const QImage & image) + { + finishToPdf(image, + outputFilename, + title, + footerLeft, footerCenter, footerRight, + textColor); + }); + + if (this->toImage().isNull()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " taskId invalid"; + return false; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " outputFilename='" << outputFilename << "'"; + return true; +} + +void MapImpl::finishToPdf(const QImage &image, + const QString &outputFilename, + const QString &title, + const QString &footerLeft, + const QString &footerCenter, + const QString &footerRight, + const QColor &textColor) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " outputFilename='" << outputFilename << "'"; + + QColor textColorInternal; + if (textColor.isValid()) + textColorInternal = textColor; + else + textColorInternal = this->palette().color(QPalette::WindowText); // RDE_DARK_BLUE + + + QPrinter printer(QPrinter::PrinterMode::HighResolution); // QPrinter::PrinterMode::ScreenResolution ; ScreenResolution is Qt-default + printer.setOutputFormat(QPrinter::PdfFormat); + printer.setOutputFileName(outputFilename); + printer.setPageOrientation(QPageLayout::Landscape); + + int marginInt = 20; + QMarginsF margins; + margins.setLeft(marginInt); + margins.setTop(marginInt); + printer.setPageMargins(margins); + + printer.setCreator(QStringLiteral("Rde-") + qAppName()); + + printer.setDocName(title); + + + const QSizeF pageSize = printer.pageLayout().pageSize().size(QPageSize::Millimeter); + DEBUG_LOG(m_logger) << LOGFUNCTION << " pageSize=" << pageSize; + + const QSizeF drawingSize = pageSize - QSizeF(2 * marginInt, 2 * marginInt) * 1.5; + + + QPainter painter(&printer); + + QTextDocument document; + QTextCursor cursor(&document); + + + QTextTableFormat tableFormat; + // tableFormat.setBackground(QColor(QLatin1String("lightGray"))); + tableFormat.setBorder(0); + tableFormat.setCellPadding(10); + + QTextCharFormat defaultFormat; + defaultFormat.setForeground(QBrush(textColorInternal)); + + // + // Header: + // + { + QTextCharFormat format = defaultFormat; + format.setFontWeight(QFont::Bold); + format.setFontPointSize(20); + + QTextTable *headerTable = cursor.insertTable(1, 1, tableFormat); + QTextTableCell cell = headerTable->cellAt(0, 0); + QTextCursor cellCursor = cell.firstCursorPosition(); + cellCursor.mergeCharFormat(format); + cellCursor.insertText(title); + + // create small distance: + format.setFontPointSize(5); + cellCursor.mergeCharFormat(format); + cellCursor.insertBlock(); + + } + cursor.insertBlock(); + + + // + // Image: + // + const QUrl imageUrl(QLatin1String("situationMap://sitMapScreenshot.png")); + document.addResource(QTextDocument::ImageResource, imageUrl, QVariant(image)); + + + QTextImageFormat imageFormat; + imageFormat.setName(imageUrl.toString()); + imageFormat.setWidth(drawingSize.width() * 0.95); + cursor.insertImage(imageFormat); + + + // + // Footer + // + { + QVector constraints; + constraints << QTextLength(QTextLength::FixedLength, imageFormat.width() / 3); + constraints << QTextLength(QTextLength::FixedLength, imageFormat.width() / 3); + constraints << QTextLength(QTextLength::FixedLength, imageFormat.width() / 3); + tableFormat.setColumnWidthConstraints(constraints); + QTextTable *footerTable = cursor.insertTable(1, 3, tableFormat); + + QStringList sl; + sl << footerLeft << footerCenter << footerRight; + + // Footer -> centered: + QTextBlockFormat blockFormat = cursor.blockFormat(); + Qt::Alignment alignment = Qt::AlignHCenter; + + // Read vertical part of current alignment flags + Qt::Alignment vertAlign = blockFormat.alignment() & Qt::AlignVertical_Mask; + + // Mask out vertical part of specified alignment flags + Qt::Alignment horzAlign = alignment & Qt::AlignHorizontal_Mask; + + // Combine current vertical and specified horizontal alignment + Qt::Alignment combAlign = horzAlign | vertAlign; + + // Apply and write + blockFormat.setAlignment(combAlign); + + for (int i = 0; i < 3; ++i) + { + QTextTableCell cell = footerTable->cellAt(0, i); + QTextCursor cellCursor = cell.firstCursorPosition(); + cellCursor.setBlockFormat(blockFormat); + cellCursor.mergeCharFormat(defaultFormat); + cellCursor.insertText(sl.at(i)); + } + + // finally: + document.drawContents(&painter); + } + + emit pdfCompleted(outputFilename); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " outputFilename='" << outputFilename << "'"; +} + + + + +//---------------------------------------------------------------------------- +// MapConnector (Map -> MapObjects) +//---------------------------------------------------------------------------- +bool MapConnector::connectMapObject(MapImpl *mapImpl, AbstractMapObjectImpl *mapObjImpl) +{ + m_mapImpl = mapImpl; + m_mapObjectImpl = mapObjImpl; + if (m_mapImpl && m_mapObjectImpl) + { + connect(m_mapImpl, &MapImpl::extentChangedDelayed, + this, &MapConnector::onMapExtentChanged); + return true; + } + return false; +} + +void MapConnector::onMapExtentChanged() +{ + // if (m_mapImpl && m_mapObjectImpl) // checked in connectObject + m_mapObjectImpl->onMapExtentChanged(); + +} + +} // namespace SituationMap + +#include "map_impl.moc" diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/map_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/map_impl.h new file mode 100755 index 000000000..71b4b4722 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/map_impl.h @@ -0,0 +1,519 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of Map + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + + +// Own +#include // Interface +#include // Interface +#include + + +// ESRI +#include +#include +#include +#include + +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() 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 getSightFanElevationMap() const; + + bool initElevationMapManager(const QStringList &filePaths_rough_to_fine); + std::shared_ptr 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) override; + + PartyIdToColorFunctor &partyIdToColorFunctor() const override; + void setPartyIdToColorFunctor(QSharedPointer 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 &coords, int paddingInDIPS) override; + + void setTrainingsAreaExtent(const QVector &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 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 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 &uniqueMapIdList, const QPointF &pos); + + void onContextClick(const QList &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 + +//---------------------------------------------------------------------------- + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/map_layer.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/map_layer.cpp new file mode 100755 index 000000000..03cf9ed9a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/map_layer.cpp @@ -0,0 +1,325 @@ +/** + * @RDE_HEADER@ + * MapLayer + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "map_layer.h" +#include +#include +#include + + +// ESRI +#include +#include +#include + + +// Qt +// #include + +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 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 : std::as_const(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 : std::as_const(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 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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/map_layer.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/map_layer.h new file mode 100755 index 000000000..7086961bb --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/map_layer.h @@ -0,0 +1,94 @@ +/** + * @RDE_HEADER@ + * MapLayer + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include +#include + +//---------------------------------------------------------------------------- +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 m_mapLayerList; ///< using logical layernames +}; + + + +} // namespace SituationMap + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/measurement_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/measurement_impl.cpp new file mode 100755 index 000000000..52a957400 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/measurement_impl.cpp @@ -0,0 +1,428 @@ +/** + * @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 +#include +#include +#include +#include + + +// 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 m_rubberband; + + QVector m_coordsGeo; + QVector m_coordsMap; + + QVector m_lengthPerSegment; + double m_totalLength = 0; + + RefPointer m_polyline; + QVector> m_markerCont; + RefPointer 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 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> it(d->m_markerCont); + while (it.hasNext()) + { + RefPointer &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(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> it(d->m_markerCont); + + while (it.hasNext()) + { + RefPointer &marker = it.next(); + if (!marker.valid()) + { + marker = d->m_mapImpl->objectFactory().create(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(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 &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 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::LinearUnit(Esri::ArcGISRuntime::LinearUnitId::Meters), + Esri::ArcGISRuntime::AngularUnit(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(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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/measurement_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/measurement_impl.h new file mode 100755 index 000000000..f05502861 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/measurement_impl.h @@ -0,0 +1,66 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of DangerArea + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include // 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 &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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/object_container_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/object_container_impl.cpp new file mode 100755 index 000000000..7e478d9cb --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/object_container_impl.cpp @@ -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) + : 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)); + 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)); + 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 obj = it.value(); + return obj.get(); +} + +QList ObjectContainerCTC::getAllObjects() const +{ + QList list; + + QMapIterator< UniqueObjectId, RefPointer > it(m_map); + while (it.hasNext()) + { + it.next(); + const RefPointer &obj = it.value(); + if (obj) + list.append(const_cast(obj.get())); + } // while + + return list; +} + +QList ObjectContainerCTC::getAllObjects(const std::type_index &type) const +{ + QList list; + + QMapIterator< UniqueObjectId, RefPointer > it(m_map); + while (it.hasNext()) + { + it.next(); + const RefPointer &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(objGet)); + } // while + + return list; +} + +QList 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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/object_container_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/object_container_impl.h new file mode 100755 index 000000000..af7dfa9e6 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/object_container_impl.h @@ -0,0 +1,105 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of ObjectContainer + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + + +// Own +#include +#include +#include + + +// Qt +#include + +#include + + +//---------------------------------------------------------------------------- +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); + 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 getAllKeys() const override; + + virtual Object *getObject(const UniqueObjectId &id) const override; // Returns pointer to object + virtual QList getAllObjects() const override; + virtual QList getAllObjects(const std::type_index &type) const override; // without template + + QMutableMapIterator> mutableIterator() override + { + return QMutableMapIterator> (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 > m_map; + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/object_indicator_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/object_indicator_impl.cpp new file mode 100755 index 000000000..b0674c087 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/object_indicator_impl.cpp @@ -0,0 +1,517 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of ObjectIndicator + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "object_indicator_impl.h" +#include +#include +#include +#include +#include +#include + + +// ESRI +#include +#include + + +// Qt +#include + + +#include + +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(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 &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(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 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(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 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(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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/object_indicator_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/object_indicator_impl.h new file mode 100755 index 000000000..bec24f27d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/object_indicator_impl.h @@ -0,0 +1,114 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of ObjectIndicator + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include // Interface +#include +#include // OriginPositionChangedCallback + + +// Qt +#include +#include +#include +#include +#include +#include + + +//---------------------------------------------------------------------------- +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 If the object to point to disappears, you are responsible for removing the ObjectIndicatorImpl! + */ +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 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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/overlay_handler_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/overlay_handler_impl.cpp new file mode 100755 index 000000000..a700cf7f2 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/overlay_handler_impl.cpp @@ -0,0 +1,644 @@ +/** + * @RDE_HEADER@ + * Interface of AbstractMapObject + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "overlay_handler_impl.h" +#include +#include +#include +#include +#include +#include +#include +#include "geometry_model_renderer_esri.h" + +#include + +//---------------------------------------------------------------------------- +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 m_geoModel; + QSharedPointer m_geoModelRenderer; + + bool init(Map *map) + { + if (m_logicalLayerName.isEmpty()) + return false; + + m_geoModel = QSharedPointer(SituationMap::GeometryModelFactory::singleton()->create()); + + m_geoModelRenderer = + QSharedPointer(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> m_overlayCont; + +public: + QSharedPointer &getOverlayData(int key) + { + if (m_overlayCont[key].isNull()) + m_overlayCont[key] = QSharedPointer(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 keys() { return m_overlayCont.keys(); } + bool contains(int key) { return m_overlayCont.contains(key); } + + QMutableMapIterator> mutableIterator() + { + return QMutableMapIterator> (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; + + // 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(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 &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 : std::as_const(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 = d->getOverlayData(key); + return overlayData->m_geoModel->toByteArray(); +} + + +bool OverlayHandlerImpl::reorderOverlays(const QVector &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::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 & orderOfIds ) + + +double OverlayHandlerImpl::minScale(int key) const +{ + if (! d->contains(key)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " key=" << key << " unknown"; + return -1.0; + } + + QSharedPointer 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 ¤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 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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/overlay_handler_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/overlay_handler_impl.h new file mode 100755 index 000000000..4a90a58d5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/overlay_handler_impl.h @@ -0,0 +1,108 @@ +/** + * @RDE_HEADER@ + * Interface of AbstractMapObject + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include +#include +#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 &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 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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/overview_map_handler_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/overview_map_handler_impl.cpp new file mode 100755 index 000000000..243b44dd9 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/overview_map_handler_impl.cpp @@ -0,0 +1,350 @@ +/** + * @RDE_HEADER@ + * Implementation of AbstractMapObject + * $Id$ + * $HeadURL$ + * $Author$ + */ + + +// Own +#include "overview_map_handler_impl.h" +#include +#include +#include +#include +#include + +#include + + +// ESRI +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +//----------------------------------------------------------------------------- +class OverviewMapHandlerImpl::Private +{ +public: + Private() = default; + ~Private() = default; + + // ESRI data: + + // Own data: + QPointer 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(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(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 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(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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/overview_map_handler_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/overview_map_handler_impl.h new file mode 100755 index 000000000..def920478 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/overview_map_handler_impl.h @@ -0,0 +1,113 @@ +/** + * @RDE_HEADER@ + * Implementation of OverviewMap + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include +#include +#include "situation_map_export.h" + +// ESRI +#include + + +// Qt + +// Forward: +namespace Esri +{ +namespace ArcGISRuntime +{ +class Graphic; +class MapGraphicsView; +} +} + + +//---------------------------------------------------------------------------- +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 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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/pixel_based_textbox_handler_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/pixel_based_textbox_handler_impl.cpp new file mode 100755 index 000000000..a758f39ca --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/pixel_based_textbox_handler_impl.cpp @@ -0,0 +1,562 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of LocationDisplay + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "pixel_based_textbox_handler_impl.h" +#include "map_impl.h" +#include +#include + + +// ESRI +#include +#include + +// Qt +#include +#include +#include +#include +#include +#include +#include + +#include + +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> 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 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 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 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> it(d->m_tbDataCont); + while (it.hasNext()) + { + QSharedPointer 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 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 PixelBasedTextBoxHandlerImpl::getAllIdsOfParent(const PBTB_ID &pbtbId) const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, pbtbId=" << pbtbId; + + const int parentId = pbtbId.m_parentId; + QVector retVec; + retVec.reserve(d->m_tbDataCont.size()); + + QVectorIterator> it(d->m_tbDataCont); + while (it.hasNext()) + { + QSharedPointer 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 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> it(d->m_tbDataCont); + while (it.hasNext()) + { + QSharedPointer 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> it(d->m_tbDataCont); + while (it.hasNext()) + { + QSharedPointer 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 PixelBasedTextBoxHandlerImpl::getCreateTbData(const PBTB_ID &pbtbId) const +{ + QVectorIterator> it(d->m_tbDataCont); + while (it.hasNext()) + { + QSharedPointer item = it.next(); + if (item->m_pbtbId == pbtbId) + return item; + } // while + + // not found? + // insert and return; + QSharedPointer initialData = QSharedPointer(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 PixelBasedTextBoxHandlerImpl::getTbDataIfExists(const PBTB_ID &pbtbId) const +{ + QVectorIterator> it(d->m_tbDataCont); + while (it.hasNext()) + { + QSharedPointer 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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/pixel_based_textbox_handler_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/pixel_based_textbox_handler_impl.h new file mode 100755 index 000000000..fbf5c1d18 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/pixel_based_textbox_handler_impl.h @@ -0,0 +1,221 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of LocationDisplay + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include +#include +#include + +// Qt +#include +#include +#include +#include +#include +#include +#include + + +//---------------------------------------------------------------------------- +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 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 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 getCreateTbData(const PBTB_ID &pbtbId) const; + QSharedPointer 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 + +//---------------------------------------------------------------------------- diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/screen_location_elevation_helper_base.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/screen_location_elevation_helper_base.cpp new file mode 100755 index 000000000..296b1318a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/screen_location_elevation_helper_base.cpp @@ -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 + + +// Qt + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +//---------------------------------------------------------------------------- +class ScreenLocationElevationHelperBase::Private +{ +public: + Private() = default; + ~Private() = default; + + QMap> 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> it(d->m_taskWatcherCont); + + while (it.hasNext()) + { + it.next(); + // QFuture can't be canceled directly, just remove from tracking + DEBUG_LOG(m_logger) << LOGFUNCTION << " Task removed from tracking"; + it.remove(); + } // 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 QFuture &future) +{ + if (!future.isValid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " future not valid"; + return false; + } + // Generate a unique ID for tracking + QUuid taskId = QUuid::createUuid(); + d->m_taskWatcherCont.insert(taskId, future); + return true; +} + +bool ScreenLocationElevationHelperBase::removeTask(const QUuid &taskId) +{ + return (d->m_taskWatcherCont.remove(taskId) > 0); +} + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/screen_location_elevation_helper_base.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/screen_location_elevation_helper_base.h new file mode 100755 index 000000000..53e8e8419 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/screen_location_elevation_helper_base.h @@ -0,0 +1,77 @@ +/** + * @RDE_HEADER@ + * Base class for ScreenToLocation and LocationToElevation + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include +#include + + +// ESRI + + +// Qt +#include +#include +#include + + +//---------------------------------------------------------------------------- +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 QFuture &future); + 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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/screen_to_location_helper.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/screen_to_location_helper.cpp new file mode 100755 index 000000000..397fb40f7 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/screen_to_location_helper.cpp @@ -0,0 +1,122 @@ +/** + * @RDE_HEADER@ + * ScreenToLocationHelper + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "screen_to_location_helper.h" +#include "Map/situation_map_utils.h" + + +// ESRI + + +// Qt +#include + + +// 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 m_sceneGraphicsView; + QFutureWatcher m_locationWatcher; + + 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; + + connect(&d->m_locationWatcher, &QFutureWatcher::finished, + this, [this]() { + const Esri::ArcGISRuntime::Point &locationMap = d->m_locationWatcher.result(); + const Point3D mapCoordinate(locationMap.x(), locationMap.y(), locationMap.z()); + TRACE_LOG(m_logger) << LOGFUNCTION << " screenToLocationCompl" + << " (m_windowPosX " << d->m_windowPosX << " m_windowPosY " << d->m_windowPosY << ")..." + << " (mapCoordinate " << mapCoordinate << ", z=" << locationMap.z() << ")"; + emit mouseScenePositionChanged(mapCoordinate, !locationMap.isEmpty()); + }); + +} + +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; + + + d->m_locationWatcher.setFuture(d->m_sceneGraphicsView->screenToLocationAsync(d->m_windowPosX, d->m_windowPosY)); +#endif // USE_SCREEN_TO_BASE_SURFACE +} + + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/screen_to_location_helper.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/screen_to_location_helper.h new file mode 100755 index 000000000..4d05bfeb7 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/screen_to_location_helper.h @@ -0,0 +1,71 @@ +/** + * @RDE_HEADER@ + * ScreenToLocationHelper + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "screen_location_elevation_helper_base.h" +#include +#include + +// ESRI +#include +#include + +// Qt +#include +#include +#include + + +//---------------------------------------------------------------------------- +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); + +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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/sidc_symbol_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/sidc_symbol_impl.cpp new file mode 100755 index 000000000..bf02bbc9b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/sidc_symbol_impl.cpp @@ -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 +#include +#include + +#include +#include + +#include + +// ESRI +#include +#include + + +//Qt +#include + + +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(this)->mapImpl()) + { + // if useful, a MapObjectType specific functor can be prefered here: + // like: if (m_mapObjectImpl->coordTranslationFunctor()) ctFunctor=m_mapObjectImpl->coordTranslationFunctor(); + coordTransFunctor = const_cast(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 &SidcSymbolImpl::attributeMap() const { return m_symbol.value().attributeMap; } + + +void SidcSymbolImpl::setAttributeMap(const QMap &attrMap) +{ + SymbolData symbolData = symbol(); + symbolData.attributeMap = attrMap; + setSymbol(symbolData); +} // SidcSymbolImpl::setAttributeMap(const QMap & attrMap) + + +void SidcSymbolImpl::defineSymbol(const QString &sidc, const PositionCont &posCont, const QMap &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 & 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 + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/sidc_symbol_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/sidc_symbol_impl.h new file mode 100755 index 000000000..c9e185191 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/sidc_symbol_impl.h @@ -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 +#include + + + +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 &attributeMap() const override; + void setAttributeMap(const QMap &attrMap) override; + + void defineSymbol(const QString &sidc, + const PositionCont &posCont, + const QMap &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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/sight_fan_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/sight_fan_impl.cpp new file mode 100755 index 000000000..426bfb1b3 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/sight_fan_impl.cpp @@ -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 + +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +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; + + SightEntryVec _sightEntryVec; + + QVector> _graphicalQuadVec; + +}; /* SightBeam */ + + +class SightFanImpl::Internal +{ +public: + + using SightBeamVec = std::vector> ; + + QString m_layerName; + std::shared_ptr 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 sBeam = RefPointer(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 sBeam : std::as_const(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 &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(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 + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/sight_fan_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/sight_fan_impl.h new file mode 100755 index 000000000..359280058 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/sight_fan_impl.h @@ -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 + +#include + +#include + + +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 +** +** +** ----------------------------------------------------------------------------------------------- */ + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/terrain_profile_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/terrain_profile_impl.cpp new file mode 100755 index 000000000..516f675b4 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/terrain_profile_impl.cpp @@ -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 +#include + +#include "Rubberbands/rubberband_factory.h" + + +//ESRI +#include + + +// Qt +#include +#include +#include + + +#include + +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 m_rubbandBase = nullptr; + + using COUNTER_T = long; + static COUNTER_T m_terrainProfileCounter; + QMap> m_terrainProfileMap; + + std::function< bool(long id, const QVector> &)> 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 > &)> &finishedCallback) +{ + d->m_finishedCallback = finishedCallback; +} + +void TerrainProfileImpl::setTerrainProfileDialog(long id, QPointer &terrainProfileDialog) +{ + if (!d->m_terrainProfileMap.contains(id)) + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " no proc found for id = " << id; + return; + } + QPointer 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 &proc : std::as_const(d->m_terrainProfileMap)) + delete proc; + d->m_terrainProfileMap.clear(); + return; + } + + if (d->m_terrainProfileMap.contains(terrainProfileId)) + { + QPointer 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(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(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(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 proc = it.value(); + + if (!proc) + return; + + const QVector> &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(rbnd); + + const QVector &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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/terrain_profile_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/terrain_profile_impl.h new file mode 100755 index 000000000..2f3b7b757 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/terrain_profile_impl.h @@ -0,0 +1,78 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of TerrainProfile + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include // Interface +#include "map_impl.h" +#include "Rubberbands/rubberband_multi_point_feature.h" +#include + + +// Qt +#include +#include + + +//---------------------------------------------------------------------------- +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> &)> &finishedCallback) override; + + void setTerrainProfileDialog(long id, QPointer &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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/terrain_profile_processor.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/terrain_profile_processor.cpp new file mode 100755 index 000000000..8c951bc2e --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/terrain_profile_processor.cpp @@ -0,0 +1,365 @@ +/** + * @RDE_HEADER@ + * TerrainProfileProcessor + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "terrain_profile_processor.h" + +#include "map_impl.h" +#include +#include +#include +#include +#include + + + +// ESRI +#include +#include + + +// 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 > TerrainProfileProcessor::results() const +{ + return m_locationToElevationBulk.results(); +} + +void TerrainProfileProcessor::setTerrainProfileDialog(QPointer &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 : std::as_const(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(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> it(m_markerCont); + + while (it.hasNext()) + { + RefPointer &marker = it.next(); + if (!marker.valid()) + { + marker = m_mapImpl->objectFactory().create(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> it(m_markerCont); + while (it.hasNext()) + { + RefPointer &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(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> &results = m_locationToElevationBulk.results(); + + if (m_logger.isDebugEnabled()) + for (const QPair &item : std::as_const(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 marks; + marks.reserve(m_terrainProfileParameter.m_coordinates.size()); + for (const Point3D &coord : std::as_const(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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/terrain_profile_processor.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/terrain_profile_processor.h new file mode 100755 index 000000000..afbc7f969 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/terrain_profile_processor.h @@ -0,0 +1,125 @@ +/** + * @RDE_HEADER@ + * TerrainProfileProcessor + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "map_impl.h" +#include "location_to_elevation_bulk.h" +#include +#include "MapObjects/polyline.h" +#include "MapObjects/geo_image.h" +#include + + +// ESRI +#include +#include +#include + + +// Qt +#include +#include +#include + + +// 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); + +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 m_terrainProfileDialog; + + QString m_toolsLayername; + QColor m_color = Qt::magenta; + RefPointer m_polyline; + QVector> m_markerCont; + RefPointer m_currentMarker; + + + LOGGER_T m_logger; + +}; // class TerrainProfileProcessor + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/tms_layer.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/tms_layer.cpp new file mode 100755 index 000000000..110d0c7e1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/tms_layer.cpp @@ -0,0 +1,213 @@ +/** + * @RDE_HEADER@ + * Tile Map Service Layer for Esri + * $Id$ + * $HeadURL$ + * $Author$ + * License: BSD 2-Clause + */ + + +#include "tms_layer.h" + +#include +#include +#include + + +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:' 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 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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/tms_layer.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/tms_layer.h new file mode 100755 index 000000000..a9f2755bf --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/tms_layer.h @@ -0,0 +1,50 @@ +/** + * @RDE_HEADER@ + * Tile Map Service Layer for Esri + * $Id$ + * $HeadURL$ + * $Author$ + * License: BSD 2-Clause + */ + +#pragma once + +#include +#include + +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 m_tileUrls; + QMap m_tilesYAxis; + QString m_extension; + QNetworkAccessManager *m_nam; + +}; + +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_analysis.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_analysis.cpp new file mode 100755 index 000000000..3d4a609d1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_analysis.cpp @@ -0,0 +1,120 @@ +/** + * @RDE_HEADER@ + * ViewshedAnalysis + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include +#include "viewshed_analysis.h" + + +// ESRI +#include + +#include +#include +#include + +// Qt +#include + +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 + + + + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_analysis.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_analysis.h new file mode 100755 index 000000000..4a1902322 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_analysis.h @@ -0,0 +1,64 @@ +/** + * @RDE_HEADER@ + * ViewshedAnalysis + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "viewshed_base.h" + +#include + + +// 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 m_viewsheds; + + LOGGER_T m_logger; +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_base.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_base.cpp new file mode 100755 index 000000000..c99783f73 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_base.cpp @@ -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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_base.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_base.h new file mode 100755 index 000000000..71f2fc369 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_base.h @@ -0,0 +1,62 @@ +/** + * @RDE_HEADER@ + * Baseclass of Viewsheds + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include + +// ESRI +#include +#include + + +// Qt +#include +#include +#include + +// 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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_geo_processing.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_geo_processing.cpp new file mode 100755 index 000000000..0c20dd423 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_geo_processing.cpp @@ -0,0 +1,569 @@ +/** + * @RDE_HEADER@ + * ViewshedGeoProcessing + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "viewshed_geo_processing.h" + +#include "map_impl.h" +#include +#include +#include +#include + + +// ESRI +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// Qt +#include +#include + + +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::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 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 inputsIt(inputs); + while (inputsIt.hasNext()) + { + inputsIt.next(); + QString value(QLatin1String("-")); + Esri::ArcGISRuntime::GeoprocessingParameter *gpPara = inputsIt.value(); + Esri::ArcGISRuntime::GeoprocessingLinearUnit *gpParaLU = dynamic_cast(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 &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(outputsMap[key]); + + return processFeatureResults(viewshedResultFeatures); + } + + key = QStringLiteral("Output_raster"); + if (outputsMap.contains(key)) + { + Esri::ArcGISRuntime::GeoprocessingRaster *viewshedResultRaster = + static_cast(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(); + }); + + QFuture future = viewshedResultRaster->fetchFileAsync(QStringLiteral("/tmp/viewshedFetched.tif")); + if (!future.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::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::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 : std::as_const(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(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(sender()); + if (senderObj) + senderName = senderObj->objectName(); + + ERROR_LOG(m_logger) << LOGFUNCTION << " GeoprocessingJob -> EsriError occured: " << error + << ", sender= '" << senderName << "'"; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_geo_processing.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_geo_processing.h new file mode 100755 index 000000000..9ca82ef21 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_geo_processing.h @@ -0,0 +1,123 @@ +/** + * @RDE_HEADER@ + * ViewshedGeoProcessing + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "viewshed_base.h" + +// ESRI +#include +#include + + +// Qt +#include +#include +#include + +// 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 m_resultGraphics; + + Esri::ArcGISRuntime::Graphic *m_originGraphic = nullptr; + Esri::ArcGISRuntime::Graphic *m_extentGraphic = nullptr; + + LOGGER_T m_logger; + +}; // class ViewshedGeoProcessing + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_geo_processing.h_ConsultingBuchta.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_geo_processing.h_ConsultingBuchta.txt new file mode 100755 index 000000000..0c2077d3f --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_geo_processing.h_ConsultingBuchta.txt @@ -0,0 +1,116 @@ +/** + * @RDE_HEADER@ + * ViewshedGeoProcessing + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "viewshed_base.h" + +// ESRI +#include +#include + + +// Qt +#include +#include +#include + +// 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 m_resultGraphics; + + Esri::ArcGISRuntime::Graphic *m_originGraphic = nullptr; + + LOGGER_T m_logger; + +}; // class ViewshedGeoProcessing + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_impl.cpp new file mode 100755 index 000000000..edbe46667 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_impl.cpp @@ -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 + +#include +#include + +#include +#include + +#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 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 m_viewshedMap; + QMap 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 : std::as_const(d->m_viewshedMap)) + delete viewshed; + d->m_viewshedMap.clear(); + + for (SightFan *sightFan : std::as_const(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(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(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(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(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(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(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(this); + if (moveRB) + { + moveRB->setAutoDelete(true); + moveRB->enableModificationState(); + moveRB->setCoordinateVector(QVector() << sightFan->center()); + + std::function moveSightFan = [this, sightFanId](SituationMap::RB::RubberbandBase* rbBase) -> bool + { + SituationMap::RB::RubberbandPointFeature *rubberPFeat = dynamic_cast(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(rbnd); + const QString &toolLayerName = d->m_mapImpl->layerFactoryImpl().toolLayer(); + const QVector &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 objRef = objFac.create(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(this); + if (moveRB) + { + moveRB->setAutoDelete(true); + moveRB->enableModificationState(); + moveRB->setCoordinateVector(QVector() << sightFan->center()); + + std::function moveSightFan3D = [this, sightFanId](SituationMap::RB::RubberbandBase* rbBase) -> bool + { + SituationMap::RB::RubberbandPointFeature3D *rubberPFeat = dynamic_cast(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(rbnd); + + const QVector &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(rbnd); + + const QVector &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 diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_impl.h new file mode 100755 index 000000000..9cdcd8f24 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapImpl/viewshed_impl.h @@ -0,0 +1,91 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of Viewshed + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include // Interface +#include "map_impl.h" +#include "Rubberbands/rubberband_ellipse.h" +#include "Rubberbands/rubberband_multi_point_feature_3d.h" + + +// Qt +#include + + +//---------------------------------------------------------------------------- +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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/_single_fire_central.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/_single_fire_central.h new file mode 100755 index 000000000..7c9f117b2 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/_single_fire_central.h @@ -0,0 +1,58 @@ +/** + * @RDE_HEADER@ + * Interface of SingleFireCentral + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The SingleFireCentral class supports Indirect Single Fire. + */ +class SingleFireCentral : public virtual AbstractMultiPointMapObject, public virtual AbstractEffectSimulation +{ + using BaseClass = AbstractMultiPointMapObject; + +public: + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + SingleFireCentral() : BaseClass() {;} ///< Contructor + + virtual ~SingleFireCentral() = default; ///< Destructor + + /*! \name Specific data*/ + //@{ + struct DetonationData + { + DetonationData() = default; + DetonationData(const Point3D &origin, double radius) + : m_origin(origin), m_radius(radius) + {} + Point3D m_origin; + double m_radius{0.0}; + }; + virtual void setDetonation(const DetonationData &detonationData) = 0; + virtual DetonationData detonation() const = 0; + virtual bool startDetonationAnimation() = 0; + + // required??? + virtual Point3D impactOrigin() const = 0; + virtual void setImpactOrigin(const Point3D &impactOrigin) = 0; + //@} + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/abstract_multi_point_map_object.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/abstract_multi_point_map_object.h new file mode 100755 index 000000000..b1738b0d6 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/abstract_multi_point_map_object.h @@ -0,0 +1,65 @@ +/** + * @RDE_HEADER@ + * Interface of AbstractMultiPointMapObject + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include +#include +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The AbstractMultiPointMapObject class serves as baseclass for + * MapObjects with a multiple coordinates. + * Therefore setPositions and positions are supported. + */ +class AbstractMultiPointMapObject : public virtual AbstractMapObject +{ + + using BaseClass = AbstractMapObject; + +public: + + /** \name Positions + * Set positions of the object. + */ + //@{ + virtual PositionCont positions() const = 0; + virtual bool setPositions(const PositionCont &posCont) = 0; + virtual PositionCont positionsTranslated() const = 0; + virtual bool setPositionsTranslated(const PositionCont &posCont) = 0; + //@} + + /** \name LineStyle + * See AbstractMapObject for details. + */ + //@{ + virtual LineStyle lineStyle() const = 0; + virtual LineStyle &rLineStyle() = 0; + virtual void setLineStyle(const LineStyle &lineStyle) = 0; + //@} + + + /** \name FillStyle + * See AbstractMapObject for details. + */ + //@{ + virtual FillStyle fillStyle() const = 0; + virtual FillStyle &rFillStyle() = 0; + virtual void setFillStyle(const FillStyle &FillStyle) = 0; + //@} + +}; + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/abstract_single_point_map_object.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/abstract_single_point_map_object.h new file mode 100755 index 000000000..7d1298c22 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/abstract_single_point_map_object.h @@ -0,0 +1,63 @@ +/** + * @RDE_HEADER@ + * Interface of AbstractSinglePointMapObject + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include +#include +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The AbstractSinglePointMapObject class serves as baseclass for + * MapObjects with a single coordinate. + * Therefore setOrigin and origin are supported. + */ +class AbstractSinglePointMapObject : public virtual AbstractMapObject +{ + + using BaseClass = AbstractMapObject; + +public: + /** \name Origin + * Set origin of the object. + */ + //@{ + virtual Point3D origin() const = 0; + virtual bool setOrigin(const Point3D &origin) = 0; + //@} + + + /** \name LineStyle + * See AbstractMapObject for details. + */ + //@{ + virtual LineStyle lineStyle() const = 0; + virtual LineStyle &rLineStyle() = 0; + virtual void setLineStyle(const LineStyle &lineStyle) = 0; + //@} + + /** \name FillStyle + * See AbstractMapObject for details. + */ + //@{ + virtual FillStyle fillStyle() const = 0; + virtual FillStyle &rFillStyle() = 0; + virtual void setFillStyle(const FillStyle &FillStyle) = 0; + //@} + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/air_support.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/air_support.h new file mode 100755 index 000000000..11bba5d88 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/air_support.h @@ -0,0 +1,38 @@ +/** + * @RDE_HEADER@ + * Interface of AirSupport + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The AirSupport class supports AirSupport. + */ +class AirSupport : public virtual AbstractMultiPointMapObject, public virtual AbstractEffectSimulation +{ + +public: + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + AirSupport() : AbstractMultiPointMapObject(), AbstractEffectSimulation() {;} ///< Contructor + + virtual ~AirSupport() = default; ///< Destructor + + virtual void setReferencedObject(const UniqueMapId &UniqueMapId) = 0; +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/area_fire.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/area_fire.h new file mode 100755 index 000000000..fb424c801 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/area_fire.h @@ -0,0 +1,90 @@ +/** + * @RDE_HEADER@ + * Interface of AreaFire + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The AreaFire class supports AreaFire with impacts. + */ +//class SITUATIONMAP_EXPORT +class AreaFire : public virtual AbstractMultiPointMapObject, public virtual AbstractEffectSimulation +{ + + using BaseClass = SituationMap::AbstractMultiPointMapObject; + +public: + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + AreaFire() : AbstractMultiPointMapObject(), AbstractEffectSimulation() {;} ///< Contructor + virtual ~AreaFire() = default; ///< Destructor + + /*! \name Specific data - impacts */ + //@{ + virtual PositionCont scheduledImpacts() const = 0; + virtual PositionCont impacts() const = 0; + virtual PositionCont impactsAbsoluteHeight() const = 0; + + /** + * @brief Set impacts of AreaFire. + * @param posCont Impacts to be set. + */ + virtual void setScheduledImpacts(const PositionCont &posCont, const QColor &explicitColor = QColor()) = 0; + virtual void setImpacts(const PositionCont &posCont) = 0; + virtual void setImpactsAbsoluteHeight(const PositionCont &posCont) = 0; + //@} + + + virtual PositionCont detonationsAbsolute() const = 0; + virtual PositionCont detonationsDraped() const = 0; + + /** + * @brief Set detonation (3D) of AreaFire. + * @param detonationRadius radius to be used. + * @param detonationsPositions detonations to be set. + */ + virtual bool setDetonationsAbsolute(const PositionCont &detonationsPositions, int detonationRadius) = 0; // Absolute + virtual bool setDetonationsDraped(const PositionCont &detonationsPositions, int detonationRadius) = 0; + + /*! \name Specific data - GunToTargetLine / GunToTargetTrajectory */ + //@{ + virtual void setGunToTargetLineSurface(const Point3D &start, const Point3D &end) = 0; + virtual void setGunToTargetTrajectory3D(const PositionCont &gunToTargetPath) = 0; + //@} + + /*! \name Specific data - NonLethalArea */ + //@{ + virtual void setNonLethalArea(const PositionCont &nonLethalArea) = 0; + //@} + + + /*! \name Specific data - trajectory */ + //@{ + virtual PositionCont trajectory(int index) const = 0; + + /** + * @brief Set trajectoryPath of AreaFire. + * @param index path index. + * @param trajectoryPath path to be set. + */ + virtual bool setTrajectory(int index, const PositionCont &trajectoryPath) = 0; + //@} + + +}; + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/biological_agent.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/biological_agent.h new file mode 100755 index 000000000..e569dcc52 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/biological_agent.h @@ -0,0 +1,46 @@ +/** + * @RDE_HEADER@ + * Interface of BiologicalAgent + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The BiologicalAgent class supports BiologicalAgent. + */ +class BiologicalAgent : public virtual AbstractMultiPointMapObject, public virtual AbstractEffectSimulation +{ + + using BaseClass = AbstractMultiPointMapObject; + +public: + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + BiologicalAgent() : AbstractMultiPointMapObject(), AbstractEffectSimulation() {;} ///< Contructor + virtual ~BiologicalAgent() = default; ///< Destructor + + /*! \name Specific data*/ + //@{ + virtual Point3D origin() const = 0; + virtual void setOrigin(const Point3D &origin) = 0; + virtual float radius() const = 0; + virtual void setRadius(float radius) = 0; + //@} + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/building_outline.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/building_outline.h new file mode 100755 index 000000000..c3ee2d24d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/building_outline.h @@ -0,0 +1,58 @@ +/** + * @RDE_HEADER@ + * Interface of BuildingOutline + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" + +// Qt +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The BuildingOutline class supports BuildingOutline. + */ +class BuildingOutline : public virtual AbstractMultiPointMapObject +{ + + using BaseClass = AbstractMultiPointMapObject; + +public: + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + BuildingOutline() : AbstractMultiPointMapObject() {;} ///< Contructor + + virtual ~BuildingOutline() = default; ///< Destructor + + virtual LineStyle lineStyle() const = 0; + virtual LineStyle &rLineStyle() = 0; + virtual void setLineStyle(const LineStyle &lineStyle) = 0; + + virtual FillStyle fillStyle() const = 0; + virtual FillStyle &rFillStyle() = 0; + virtual void setFillStyle(const FillStyle &FillStyle) = 0; + + virtual void setMarker(const Point3D &markerOrigin) = 0; + + // The 3D model is using the markerOrigin + struct ModelSceneSymbolData + { + QUrl url; + float heading{0.0}; // degree, clockwise, 0 is north/12 + float scale{1.0}; + }; + virtual ModelSceneSymbolData getModelSceneSymbolData() = 0; + virtual void setModelSceneSymbolData(const ModelSceneSymbolData &modelSceneSymbolData) = 0; +}; + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/chemical_agent.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/chemical_agent.h new file mode 100755 index 000000000..eba12b457 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/chemical_agent.h @@ -0,0 +1,46 @@ +/** + * @RDE_HEADER@ + * Interface of ChemicalAgent + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The ChemicalAgent class supports different agent types. + */ +class ChemicalAgent : public virtual AbstractMultiPointMapObject, public virtual AbstractEffectSimulation +{ + +public: + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + ChemicalAgent() : AbstractMultiPointMapObject(), AbstractEffectSimulation() {;} ///< Contructor + + virtual ~ChemicalAgent() = default; ///< Destructor + + /*! \name Specific data*/ + //@{ + virtual Point3D origin() const = 0; + virtual void setOrigin(const Point3D &origin) = 0; + virtual float radius() const = 0; + virtual void setRadius(float radius, unsigned int maxPointCount = 0) = 0; + //@} + + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/cube.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/cube.h new file mode 100755 index 000000000..8e71e1f75 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/cube.h @@ -0,0 +1,44 @@ +/** + * @RDE_HEADER@ + * Interface of DangerArea + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The Cube class supports staked cubes displaying the building ownership for example. + * The cubes can have more than 3 positions. + * But be aware that there is no 'roof' yes. + */ +class Cube : public virtual AbstractMultiPointMapObject +{ + + using BaseClass = AbstractMultiPointMapObject; + +public: + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + Cube() : BaseClass() {;} ///< Contructor + + virtual ~Cube() = default; ///< Destructor + + + virtual double height() const = 0; + virtual void setHeight(double height) = 0; + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/danger_area.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/danger_area.h new file mode 100755 index 000000000..b32b1220a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/danger_area.h @@ -0,0 +1,37 @@ +/** + * @RDE_HEADER@ + * Interface of DangerArea + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The DangerArea class supports DangerAreas of different agents. + */ +class DangerArea : public virtual AbstractMultiPointMapObject, public virtual AbstractEffectSimulation +{ + +public: + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + DangerArea() : AbstractMultiPointMapObject(), AbstractEffectSimulation() {;} ///< Contructor + + virtual ~DangerArea() = default; ///< Destructor + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/functional_area.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/functional_area.h new file mode 100755 index 000000000..b9bb9a4c8 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/functional_area.h @@ -0,0 +1,48 @@ +/** + * @RDE_HEADER@ + * Interface of FunctionalArea + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The FunctionalArea class supports FunctionalArea. + */ +class FunctionalArea : public virtual AbstractMultiPointMapObject, public virtual AbstractEffectSimulation +{ + +public: + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + FunctionalArea() : AbstractMultiPointMapObject(), AbstractEffectSimulation() {;} ///< Contructor + + virtual ~FunctionalArea() = default; ///< Destructor + + + /*! \name Point symbol */ + //@{ + virtual Point3D pointSymbolOrigin() const = 0; + virtual void setPointSymbolOrigin(const Point3D &pointSymbolOrigin) = 0; + + virtual const QString &pointSymbolSidc() const = 0; + virtual void setPointSymbolSidc(const QString &pointSymbolOriginSidc) = 0; + //@} + + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/geo_image.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/geo_image.h new file mode 100755 index 000000000..8ebfc26a1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/geo_image.h @@ -0,0 +1,146 @@ +/** + * @RDE_HEADER@ + * Interface of GeoImage + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_single_point_map_object.h" + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The GeoImage class supports displaying image proxies like a small dot. + * By clicking of the proxy the image and an optional description will be displayed. + */ +class GeoImage : public virtual AbstractSinglePointMapObject +{ +public: + + struct GeoImageData + { + MarkerSymbolType proxyMarker = MarkerSymbolType::Undefined; ///< if not set, a Diamond will be used + int proxyMarkerSize = 20; + QColor proxyMarkerColor = Qt::red; + QImage proxyImage; ///< if not set, marker will be used + QSize proxyImageDefaultSize; + + QImage mainImage; + QString description; + bool pinEnabled = false; + + /** + * @brief operator== Compare two objects + * @param other to be compared with + * @return true if identical, otherwise false. + */ + bool operator==(const GeoImageData &other) const + { + return ((this->description == other.description) && + (this->mainImage == other.mainImage) && + (this->proxyImage == other.proxyImage) && + (this->proxyMarkerSize == other.proxyMarkerSize) && + (this->proxyMarkerColor== other.proxyMarkerColor) && + (this->proxyMarker == other.proxyMarker) + ); + } + + /** + * @brief operator!= Compare two objects + * @param other to be compared with + * @return true if NOT identical, otherwise false. + */ + bool operator!=(const GeoImageData &other) const + { + return !(*this == other); + } + + QString toString() const + { + QString str = QLatin1String("GeoImage("); + if (proxyImage.isNull()) + str.append(QLatin1String("noProxyImage, ")); + else + str.append(QLatin1String("withProxyImage, ")); + + if (mainImage.isNull()) + str.append(QLatin1String("noMainImage, ")); + else + str.append(QLatin1String("withMainImage, ")); + + str.append(QStringLiteral("desc='%1')").arg(description)); + + return str; + } + }; + + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + GeoImage() : AbstractSinglePointMapObject() {;} ///< Contructor + + virtual ~GeoImage() = default; ///< Destructor + + /*! \name Set data in one call */ + //@{ + virtual GeoImageData geoImageData() const = 0; + virtual void setGeoImageData(const GeoImageData &geoImageData) = 0; + //@} + + /*! \name Set data separately */ + //@{ + virtual MarkerSymbolType proxyMarker() const = 0; + virtual void setProxyMarker(const MarkerSymbolType &proxyMarker) = 0; + virtual int proxyMarkerSize() const = 0; + virtual void setProxyMarkerSize(int proxyMarkerSize) = 0; + virtual QColor proxyMarkerColor() const = 0; + virtual void setProxyMarkerColor(const QColor &proxyMarkerColor) = 0; + + virtual QImage proxyImage() const = 0; + virtual QSize proxyImageDefaultSize() const = 0; + + /** + * @brief setProxyImage to set a proxy image be displayed on the map (instead of marker) + * @param proxyImage proxy image be displayed on the map + * @param proxyImageDefaultSize default size image if (user) scale is 1.0. + * The image will be scaled to the proxyImageDefaultSize * scale. + * To ensure that the image will be displayed with high resolution, the proxyImage must have this resolution. + * Example: + * Set an image of QSize(100,100), and set a proxyImageDefaultSize of QSize(20,20). + * Initially (scale = 1.0) the image will be downscaled to QSize(20,20). + * If the uses increases the scale to 4.0, the image will be scaled to QSize(80,80) + * This should ensure a good image quality. + */ + virtual void setProxyImage(const QImage &proxyImage, const QSize &proxyImageDefaultSize) = 0; + + + virtual QImage mainImage() const = 0; + virtual void setMainImage(const QImage &mainImage) = 0; + + virtual QString description() const = 0; + virtual void setDescription(const QString &description) = 0; + + // for better visibility at 3D + virtual void setPinEnabled(bool enablePin) = 0; + virtual bool isPinEnabled() const = 0; + //@} + + + /*! \name Create default widget */ + //@{ + virtual QWidget *createWidget(QWidget *parent = nullptr) const = 0; + //@} + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/geometry.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/geometry.cpp new file mode 100755 index 000000000..5d62eb985 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/geometry.cpp @@ -0,0 +1,75 @@ +/** + * @RDE_HEADER@ + * Class Geometry + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "geometry.h" +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +QList Geometry::createCircle(const QPointF ¢er, qreal radius, int steps) +{ + QList circle; + circle.reserve(steps); + + if (steps < 10) + steps = 10; + for (int i = 0; i < 360; i += (360 / steps)) + { + double winkel = i * M_PI / 180.0; + circle << (center + QPointF(cos(winkel) * radius, sin(winkel) * radius)); + } + circle << circle.value(0); // 'close' the circle + // circle.translate(center); + return circle; +} + + +QList Geometry::createArcSegment(const QPointF ¢er, qreal radius, + double startAngle_degree, + double angleOffset_clockwise_degree, + int stepsPer360) +{ + QList points; + + if (stepsPer360 < 10) + stepsPer360 = 10; + + startAngle_degree *= -1.0; + startAngle_degree += 90; + + // normalize + startAngle_degree = fmod(startAngle_degree, 360); + angleOffset_clockwise_degree = fmod(angleOffset_clockwise_degree, 360); + + // steps + int steps = fabs(angleOffset_clockwise_degree) * stepsPer360 / 360; + points.reserve(steps + 1); + + double startAngle_rad = startAngle_degree * M_PI / 180.0; + double angleOffset_right_rad = angleOffset_clockwise_degree * M_PI / 180.0; + for (int i = 0; i < steps; ++i) + { + // '-' to go clockwise: + double winkel_rad = (startAngle_rad - (i * angleOffset_right_rad / steps)); + points << (center + QPointF(cos(winkel_rad) * radius, sin(winkel_rad) * radius)); + } + // calculate very last iteration (independent from 'steps'): + double winkel_rad = (startAngle_rad - (angleOffset_right_rad)); + points << (center + QPointF(cos(winkel_rad) * radius, sin(winkel_rad) * radius)); + + return points; +} + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/geometry.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/geometry.h new file mode 100755 index 000000000..b73359d5b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/geometry.h @@ -0,0 +1,46 @@ +/** + * @RDE_HEADER@ + * Class Geometry + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "situation_map_export.h" + + +// Qt +#include +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +class SITUATIONMAP_EXPORT Geometry final +{ +public: + Geometry() = default; + ~Geometry() = default; + + static QList createCircle(const QPointF ¢er, + qreal radius, + int steps = 72); // 72 -> 5° Schritte + + static QList createArcSegment(const QPointF ¢er, qreal radius, + double startAngle_degree, + double angleOffset_clockwise_degree, + int stepsPer360 = 72); // 72 -> 5° Schritte + + static const int minNumberOfVerticesPerCircle = 10; + +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/hit_line.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/hit_line.h new file mode 100755 index 000000000..f96468406 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/hit_line.h @@ -0,0 +1,58 @@ +/** + * @RDE_HEADER@ + * Interface of HitLine + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The HitLine class supports HitLines of different agents. + * The method setPositions() should never be called. + */ +class HitLine : public virtual AbstractMultiPointMapObject, public virtual AbstractEffectSimulation +{ + +public: + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + HitLine() : AbstractMultiPointMapObject(), AbstractEffectSimulation() {;} ///< Contructor + + virtual ~HitLine() = default; ///< Destructor + + + /*! \name Specific data*/ + //@{ + +// virtual PositionCont impacts() const = 0; + + /** + * @brief Start and end point:. + */ + virtual Point3D startPoint() const = 0; + virtual Point3D endPoint() const = 0; + + virtual void setStartAndEndPoint(const Point3D &start, const Point3D &end) = 0; + //@} + + + // void setPositions(const PositionCont& posCont) //SHOULD NEVER BE CALLED + + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/line_of_sight.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/line_of_sight.h new file mode 100755 index 000000000..4834f8564 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/line_of_sight.h @@ -0,0 +1,64 @@ +/** + * @RDE_HEADER@ + * Interface of LineOfSight + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The LineOfSight class supports a "single" line with two colors. + * The method setPositions() should never be called. + */ +class LineOfSight : public virtual AbstractMultiPointMapObject, public virtual AbstractEffectSimulation +{ +public: + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + LineOfSight() + : AbstractMultiPointMapObject(), AbstractEffectSimulation() + {} ///< Contructor + + virtual ~LineOfSight() = default; ///< Destructor + + + /*! \name Specific data*/ + //@{ + + + /*! + * \brief set relvants Points + * \param observer observerPoint ( + * \param obstacle obstacle point + * \param target targetPoint + * \param visibleColor color to visualize the visible part + * \param obstructedColor color to visualize the obstructed part + */ + virtual void setPoints(const Point3D &observer, + const Point3D &obstacle, + const Point3D &target, + const QColor &visibleColor = Qt::green, + const QColor &obstructedColor = Qt::red) = 0; + //@} + + + // void setPositions(const PositionCont& posCont) //SHOULD NEVER BE CALLED + + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/map_object_attributes.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/map_object_attributes.h new file mode 100755 index 000000000..e7ddb01c1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/map_object_attributes.h @@ -0,0 +1,35 @@ +/** + * @RDE_HEADER@ + * Interface of DangerArea + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_single_point_map_object.h" + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief + */ +class MapObjectAttributes : public virtual AbstractSinglePointMapObject +{ + +public: + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + MapObjectAttributes() : AbstractSinglePointMapObject() {;} ///< Contructor + + virtual ~MapObjectAttributes() = default; ///< Destructor + + virtual QVariantMap attributes() const = 0; + virtual void setAttributes(const QVariantMap &attributes) = 0; +}; + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/map_object_indicator.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/map_object_indicator.h new file mode 100755 index 000000000..f761d6bac --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/map_object_indicator.h @@ -0,0 +1,80 @@ +/** + * @RDE_HEADER@ + * Interface of MapObjectIndicator + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The MapObjectIndicator points to exactly one MapObject defined by its uniqueMapId. + * + * This MapObjectIndicator should be smart, you normally don´t have to set the position, + * color or similar directly. + * Just call setMapObjectToPointTo() and updateView(); + * \n The MapObjectIndicator shoud 'follow' the object to be pointed to. + * \n The color is taken from the objectToBePointedTo (if that changes, call updateView() again). + * + * Some Annotations / Recommendations: + * - Layer: create the MapObjectIndicator in the topmost (operational) layer (LayerType::Operational) + * - The drawOrder is internal choosen to be OVER the objectToBePointedTo + * - The method setPositions() should never be called. + * + */ +class MapObjectIndicator : public virtual AbstractMultiPointMapObject +{ + +public: + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + MapObjectIndicator() : AbstractMultiPointMapObject() {;} ///< Contructor + + virtual ~MapObjectIndicator() = default; ///< Destructor + + + /*! \name Specific data*/ + //@{ + + + /** + * @brief Start and end point:. + */ + virtual Point3D startPoint() const = 0; + virtual Point3D endPoint() const = 0; + + virtual void setStartAndEndPoint(const Point3D &start, const Point3D &end) = 0; + + /** + * @brief mapObjectToPointTo + * @return uniqueId of the object to be pointed to. + */ + virtual UniqueMapId mapObjectToPointTo() = 0; + + /** + * @brief Set uniqueId of object should to be pointed to. + * @attention But there is NO automatic update yet. Caller is responsable for operforming updateView()! + * @param uniqueId of the object to be pointed to. + */ + virtual void setMapObjectToPointTo(UniqueMapId uniqueId) = 0; + //@} + + + // void setPositions(const PositionCont& posCont) //SHOULD NEVER BE CALLED + + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/mine.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/mine.h new file mode 100755 index 000000000..34b5710c4 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/mine.h @@ -0,0 +1,53 @@ +/** + * @RDE_HEADER@ + * Interface of Mine + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_single_point_map_object.h" + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The SingleFire class supports Indirect Single Fire. + */ +class Mine : public virtual AbstractSinglePointMapObject +{ + + using BaseClass = AbstractSinglePointMapObject; + +public: + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + Mine() : AbstractSinglePointMapObject() {;} ///< Contructor + + virtual ~Mine() = default; ///< Destructor + + /*! \name Specific data*/ + //@{ + /** + * @brief Set mine state (true = aimed/active => filled symbol). + * @return True for aimed/active, otherwise false. + */ + virtual bool state() const = 0; + + /** + * @brief Set new mine state. + * @param state (true = aimed/active => filled symbol) + */virtual void setState(bool state) = 0; + //@} + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/mine_field.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/mine_field.h new file mode 100755 index 000000000..604e558c6 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/mine_field.h @@ -0,0 +1,90 @@ +/** + * @RDE_HEADER@ + * Interface of MineField + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The MineField class supports MineFields with Activated and DeActivated mines. + */ +class MineField : public virtual AbstractMultiPointMapObject, public virtual AbstractEffectSimulation +{ + + using BaseClass = AbstractMapObject; + +public: + enum class MineState + { + Activated, ///< live, aimed + DeActivated ///< + }; + +public: + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + MineField() : AbstractMultiPointMapObject(), AbstractEffectSimulation() { ; } + virtual ~MineField() = default; ///< Destructor + + + /*! \name Segments */ + //@{ + virtual PositionCont segment(int key) const = 0; + /** + * @brief Set positions of one segment. + * @param key of segment to be set. + * @param posCont positions of segment. + * @note: is key is negativ, no fill pattern is aplied. + * If key is >=0, the pattern of the segments take over the main pattern. + */ + virtual void setSegment(int key, const PositionCont &posCont) = 0; + + /** + * @brief Remove one segment + * @param key to be removed. + */ + virtual void removeSegment(int key) = 0; + + /** + * @brief Remove all segments. + */ + virtual void removeSegments() = 0; + + /** + * @brief Get a list of keys in use. + * @return List of keys in use. + */ + virtual QList keysInUse() const = 0; + + + /*! \name Positions of Mines */ + //@{ + /** + * @brief Set all positions of mines (of all states) + * @param mineState to be set. + * @param posCont positions of mines to be set. + */ + virtual void setPositionsMine(MineState mineState, const PositionCont &posCont) = 0; + //@} + + virtual bool mineVisibility(MineState mineState) const = 0; + virtual void setMineVisibility(MineState mineState, bool visible) = 0; + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/nine_liner.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/nine_liner.h new file mode 100755 index 000000000..c6c4621bf --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/nine_liner.h @@ -0,0 +1,112 @@ +/** + * @RDE_HEADER@ + * Interface of NineLiner + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The NineLiner class supports NineLiners of different agents. + * The method setPositions() should never be called. + */ +struct NineLiner : public virtual AbstractMultiPointMapObject, public virtual AbstractEffectSimulation +{ + class NineLinerData + { + public: + Point3D initialPoint; + Point3D targetPosition; + + int friendliesBearing = -1; // friendlies is optional + double friendliesDistance = -1; // friendlies is optional + + int fah = 0; //finalAttackHeading + int fahLow = 0; //finalAttackHeading-SectorLow + int fahHigh = 0; //finalAttackHeading-SectorHigh + + /** + * @brief operator== Compare two objects + * @param other to be compared with + * @return true if identical, otherwise false. + */ + bool operator==(const NineLinerData &other) const + { + return ((this->initialPoint == other.initialPoint) && + (this->targetPosition == other.targetPosition) && + + (this->friendliesBearing == other.friendliesBearing) && + (this->friendliesDistance == other.friendliesDistance) && + + (this->fah == other.fah) && + (this->fahLow == other.fahLow) && + (this->fahHigh == other.fahHigh) + ); + } + + /** + * @brief operator!= Compare two objects + * @param other to be compared with + * @return true if NOT identical, otherwise false. + */ + bool operator!=(const NineLinerData &other) const + { + return !(*this == other); + } + + QString toString() const + { + QString str; + + std::ostringstream stream; + stream << " initialPoint = " << initialPoint + << " targetPosition = " << targetPosition; + str.append(QLatin1String(stream.str().c_str())); + + str.append(QStringLiteral(", friendliesBearing=%1").arg(friendliesBearing)); + str.append(QStringLiteral(", friendliesDistance=%1").arg(friendliesDistance)); + + str.append(QStringLiteral(", fah=%1").arg(fah)); + str.append(QStringLiteral(", fahLow=%1").arg(fahLow)); + str.append(QStringLiteral(", fahHigh=%1").arg(fahHigh)); + return str; + } + }; // class NineLinerData + + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + NineLiner() : AbstractMultiPointMapObject(), AbstractEffectSimulation() {;} ///< Contructor + + virtual ~NineLiner() = default; ///< Destructor + + + /*! \name Specific data*/ + //@{ + + virtual void setNineLinerData(const NineLinerData &data) = 0; + +// virtual PositionCont impacts() const = 0; + + //@} + + + // void setPositions(const PositionCont& posCont) //SHOULD NEVER BE CALLED + + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/nuclear_agent.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/nuclear_agent.h new file mode 100755 index 000000000..0fb154e82 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/nuclear_agent.h @@ -0,0 +1,98 @@ +/** + * @RDE_HEADER@ + * Interface of NuclearAgent + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The ChemicalAgent class supports different agent types. + */ +class NuclearAgent : public virtual AbstractMultiPointMapObject, public virtual AbstractEffectSimulation +{ + +public: + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + NuclearAgent() : AbstractMultiPointMapObject(), AbstractEffectSimulation() {;} ///< Contructor + + virtual ~NuclearAgent() = default; ///< Destructor + + + /*! \name Subsidiary polyline/polygons */ + //@{ + /** + * @brief Get subsidiary line with key. + * @param key to be retrieved. + * @return subsidiary line at index + */ + virtual PositionCont subsidiaryLine(int key) const = 0; + + /** + * @brief Set subsidiary line with key. + * @param key to be set. + * @param positions subsidiary line to be set. + */ + + struct OptionalData + { + // ctor + OptionalData() {} + // member + bool closed{false}; + QSharedPointer lineStyle; // default is 'empty', do not honor + QSharedPointer fillStyle; // default is 'empty', do not honor + }; + +// virtual void setSubsidiaryLine(int key, const PositionCont &positions, bool clo = false) = 0; + virtual void setSubsidiaryLine(int key, const PositionCont &positions, const OptionalData &optData = OptionalData()) = 0; + + /** + * @brief Get a list of keys in use. + * @return List of keys in use. + */ + virtual QList subsidiaryKeysInUse() = 0; + + /** + * @brief Remove one subsidiary line. + * @param key to be removed. + */ + virtual void removeSubsidiaryLine(int key) = 0; + + /** + * @brief Remove all subsidiaryPositions. + */ + virtual void removeSubsidiaryLines() = 0; + //@} + + /*! \name Impact*/ + //@{ + virtual Point3D impactOrigin() const = 0; + virtual void setImpactOrigin(const Point3D &impactOrigin) = 0; + //@} + + /*! \name Single Detonation */ + //@{ + virtual void setDetonation(const Point3D &origin, double radius) = 0; + virtual bool hasDetonation() const = 0; + virtual void resetDetonation() = 0; + //@} + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/participant.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/participant.h new file mode 100755 index 000000000..4f8b0adeb --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/participant.h @@ -0,0 +1,279 @@ +/** + * @RDE_HEADER@ + * Interface of Participant + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include + + +// Qt +#include +#include +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +/** + * @brief The Participant class supports Participant and their wide range of parameters. + * + */ +class Participant : public virtual AbstractMapObject +{ + + using BaseClass = AbstractMapObject; + +public: + + /*! \name TextModifier */ + //@{ + /** + * @brief The TextModifier enum contains some standard Text Modifier (MIL2525, APP6) + */ + enum class TextModifier + { + EF_ReinforcedReduced = 0, ///< E -> FrameShapeModifier; F-> reinforced of reduced + M_HigherFormation, ///< Tactical Identifier (right handed) + T_UniqueDesignation, ///< Designator (left handed) + W_DateTimeGroup, + XY_AltitudeLocation, + V_Type, + Z_Speed, + G_StaffComments, + H_AdditionalInformation, + JKLNP, + END ///< This MUST mark the end, Do NOT use on interface + }; + + // Some helper functions: + static inline QLatin1String toString(TextModifier value) ///< 'EF_ReinforcedReduced', ... + { + switch (value) + { + case TextModifier::EF_ReinforcedReduced: + return QLatin1String("EF_ReinforcedReduced"); + case TextModifier::M_HigherFormation: + return QLatin1String("M_HigherFormation"); + case TextModifier::T_UniqueDesignation: + return QLatin1String("T_UniqueDesignation"); + case TextModifier::W_DateTimeGroup: + return QLatin1String("W_DateTimeGroup"); + case TextModifier::XY_AltitudeLocation: + return QLatin1String("XY_AltitudeLocation"); + case TextModifier::V_Type: + return QLatin1String("V_Type"); + case TextModifier::Z_Speed: + return QLatin1String("Z_Speed"); + case TextModifier::G_StaffComments: + return QLatin1String("G_StaffComments"); + case TextModifier::H_AdditionalInformation: + return QLatin1String("H_AdditionalInformation"); + case TextModifier::JKLNP: + return QLatin1String("JKLNP"); + case TextModifier::END: + break; + } //switch + return QLatin1String("ERROR-TextModifier"); + } + + static inline QLatin1String toDisplayString(TextModifier value) ///< 'ReinforcedReduced', ... + { + switch (value) + { + case TextModifier::EF_ReinforcedReduced: + return QLatin1String("ReinforcedReduced"); + case TextModifier::M_HigherFormation: + return QLatin1String("HigherFormation"); + case TextModifier::T_UniqueDesignation: + return QLatin1String("UniqueDesignation"); + case TextModifier::W_DateTimeGroup: + return QLatin1String("DateTimeGroup"); + case TextModifier::XY_AltitudeLocation: + return QLatin1String("AltitudeLocation"); + case TextModifier::V_Type: + return QLatin1String("Type"); + case TextModifier::Z_Speed: + return QLatin1String("Speed"); + case TextModifier::G_StaffComments: + return QLatin1String("StaffComments"); + case TextModifier::H_AdditionalInformation: + return QLatin1String("AdditionalInformation"); + case TextModifier::JKLNP: + return QLatin1String("JKLNP"); + case TextModifier::END: + break; + } //switch + return QLatin1String("ERROR-TextModifier"); + } + + static QVector textModifiers() ///< only relevant TextModifier items -> without 'END' + { + return + { + TextModifier::EF_ReinforcedReduced, + TextModifier::M_HigherFormation, + TextModifier::T_UniqueDesignation, + TextModifier::W_DateTimeGroup + }; + } + //@} + + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + Participant() : BaseClass() {;} ///< Contructor + + virtual ~Participant() = default; ///< Destructor + + /*! \name Origin*/ + //@{ + virtual Point3D origin() const = 0; + virtual bool setOrigin(const Point3D &origin) = 0; + + virtual void setPositions(const PositionCont &posCont) = 0; ///< To enable using as AES item (template@AdapterDds) + //@} + + /*! \name Symbol Display*/ + //@{ + virtual float userSymbolOpacity() const = 0; + virtual void setUserSymbolOpacity(float userSymbolOpacity) = 0; + virtual SymbolDataToRender symbolDataToRender(bool ignoreForcedColor = true) const = 0; + virtual QImage actualImage() const = 0; + virtual AdvancedSymbology::SymbologyType symbologyType() const = 0; + //@} + + /*! \name Text Modifier*/ + //@{ + virtual QString textModifier(TextModifier textModifier) const = 0; + virtual void setTextModifier(TextModifier textModifier, const QString &text) = 0; + virtual bool textModifierVisibility(TextModifier textModifier) const = 0; + virtual void setTextModifierVisibility(TextModifier textModifier, bool visible) = 0; + //@} + + /*! \name Graphical Modifier*/ + //@{ + virtual Injury injury() const = 0; + virtual QColor injuryColor() const = 0; + virtual bool setInjury(Injury injury, const QColor &injuryColor) = 0; ///< Attention: Qt::red is an int + virtual int injuryPartyId() const = 0; + virtual bool setInjury(Injury injury, int partyId) = 0; ///< Attention: Qt::red is an int + + virtual bool additionalSymbol(AdditionalSymbolType additionalSymbolType) const = 0; + + /*! + * \brief setAdditionalSymbol + * \param additionalSymbolType to set + * \param newState new value + * \return true if changed, otherwise false + */ + virtual bool setAdditionalSymbol(AdditionalSymbolType additionalSymbolType, bool newState) = 0; + + virtual bool isMarked() const = 0; + /*! + * \brief setMarked + * \param marked new value + * \return true if changed, otherwise false + */ + virtual bool setMarked(bool marked) = 0; + //@} + + /*! \name Ranges*/ + //@{ + virtual QVector ranges() const = 0; + virtual bool setRanges(const QVector &ranges) = 0; + + virtual bool isRangesVisible() const = 0; + virtual void setRangesVisibility(bool visible) = 0; + //@} + + /*! \name DirectionIndicator like Turret Alignment*/ + //@{ + struct DirectionIndicatorData + { + double directionDegree = 0.0; ///< degree, clockwise (0 means 12 on a analog clock) + int lineWidth = 2; ///< px @ sizeL==default + Qt::PenStyle lineStyle = Qt::SolidLine; + QColor color; ///< invalid, means partyColor + }; + + virtual void setDirectionIndicator(int key, const DirectionIndicatorData &data) = 0; + virtual void removeDirectionIndicator(int key = -1) = 0; + + virtual bool isDirectionIndicatorsVisible() const = 0; + virtual void setDirectionIndicatorsVisibility(bool visible) = 0; + //@} + + + /*! \name Designation*/ + //@{ + struct DesignationData + { + Point3D spotLocation; + int code; + + bool isNull() const { return spotLocation.isNull(); } + + bool operator==(const DesignationData& other) const + { + return this->spotLocation == other.spotLocation && this->code == other.code; + } + bool operator!=(const DesignationData& other) const + { + return !(*this == other); + } + }; + + virtual bool setDesignations(const QVector &designations) = 0; + //@} + + + /*! \name Movement Track + * First of all you have the enable the MovementTrack using enableMovementTrack(). + * \n Then you can optionally preset a couple of positions using setMovementTrack() or addToMovementTrack() to an MovementTrackContainer. + * \n Afterwards movement - transmitted by setOrigin() and a following updateView() - will be added to the MovementTrackContainer. + * \n If you hide the track by setMovementTrackVisibility() the movement will still be tracked until you disable the MovementTrack. + * + * Behaviour if you enabled the MovementTrack and update the origin (using setOrigin()): + * - Every position (different to last position) is added to track. + * \n There is currently no minimum movement required to be added to the track. + * - The number of positions in the track is unlimited. + * - There is no time-awareness of the positions. + */ + //@{ + virtual bool isMovementTrackEnabled() const = 0; + virtual void setMovementTrackEnabled(bool enable) = 0; + + virtual void setMovementTrack(const PositionCont &movementTrack) = 0; + virtual void addToMovementTrack(const Point3D &addToMovementTrack) = 0; + virtual PositionCont movementTrack() const = 0; + virtual int sizeOfMovementTrack() const = 0; + + virtual bool isMovementTrackVisible() const = 0; + virtual void setMovementTrackVisibility(bool visible) = 0; + //@} + + +private: + // Forbid some overloaded methods without effects of this object: +// virtual QString identifier() const { return QString(); } +// virtual void setIdentifier(const QString& identifier) { Q_UNUSED(identifier); } + +// virtual bool visibility(ObjectAppearance appearance) const { Q_UNUSED(appearance); return true;} +// virtual void setVisibility(ObjectAppearance appearance, bool visibility) { Q_UNUSED(appearance); Q_UNUSED(visibility); }; + +}; + + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/polygon.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/polygon.h new file mode 100755 index 000000000..8dd2fb4ee --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/polygon.h @@ -0,0 +1,39 @@ +/** + * @RDE_HEADER@ + * Interface of Polygon + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The Polygon class supports Polygons. + */ +class Polygon : public virtual AbstractMultiPointMapObject +{ + + using BaseClass = AbstractMultiPointMapObject; + +public: + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + Polygon() : BaseClass() {;} ///< Contructor + + virtual ~Polygon() = default; ///< Destructor + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/polyline.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/polyline.h new file mode 100755 index 000000000..63fb414fd --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/polyline.h @@ -0,0 +1,56 @@ +/** + * @RDE_HEADER@ + * Interface of Polyline + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The Polyline class supports polylines. + */ +class Polyline : public virtual AbstractMultiPointMapObject +{ + + using BaseClass = AbstractMultiPointMapObject; + +public: + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + Polyline() : BaseClass() {;} ///< Contructor + + virtual ~Polyline() = default; ///< Destructor + + /*! \name ArrowHeads */ + //@{ + virtual bool arrowHeadStart() const = 0; + virtual void setArrowHeadStart(bool enable) = 0; + + virtual bool arrowHeadEnd() const = 0; + virtual void setArrowHeadEnd(bool enable) = 0; + //@} + + /*! \name Text on lineSegments */ + //@{ + virtual QString textOnSegment(uint segment) const = 0; + virtual void setTextOnSegment(uint segment, const QString &segmentText) = 0; + virtual void clearTextOnSegment(uint segment) = 0; + //@} + + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/single_fire.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/single_fire.h new file mode 100755 index 000000000..434d27d91 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/single_fire.h @@ -0,0 +1,61 @@ +/** + * @RDE_HEADER@ + * Interface of SingleFire + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The SingleFire class supports Indirect Single Fire. + */ +class SingleFire : public virtual AbstractMultiPointMapObject, public virtual AbstractEffectSimulation +{ + + using BaseClass = AbstractMultiPointMapObject; + +public: + + enum class ImpactType + { + normalImpact, + doubleImpact + }; + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + SingleFire() : BaseClass() {;} ///< Contructor + + virtual ~SingleFire() = default; ///< Destructor + + /*! \name Specific data*/ + //@{ + virtual Point3D impactOrigin() const = 0; + virtual void setImpactOrigin(const Point3D &impactOrigin) = 0; + + virtual ImpactType impactType() const = 0; + virtual void setImpactType(ImpactType impactType = ImpactType::normalImpact) = 0; + + virtual Point3D origin() const = 0; + virtual void setOrigin(const Point3D &origin) = 0; + virtual float radius() const = 0; + virtual void setRadius(float radius) = 0; + virtual void setOriginatorToTargetLineSurface(const Point3D &start, const Point3D &end) = 0; + //@} + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/sound_area.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/sound_area.h new file mode 100755 index 000000000..1ea17ca21 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/sound_area.h @@ -0,0 +1,123 @@ +/** + * @RDE_HEADER@ + * Interface of SoundArea + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +class MarkerData +{ +public: + MarkerData() = default; + + /// ctor for marker + MarkerData(MarkerSymbolType _markerSymbolType, int _proxyMarkerSize, const QColor &_markerColor) + : m_proxyMarker(_markerSymbolType), m_proxyMarkerSize(_proxyMarkerSize), m_proxyMarkerColor(_markerColor) + {} + + /** + * @brief proxyImage to set a proxy image be displayed on the map (instead of marker) + * @param _proxyImage proxy image be displayed on the map + * @param _proxyImageDefaultSize default size image if (user) scale is 1.0. + * The image will be scaled to the proxyImageDefaultSize * scale. + * To ensure that the image will be displayed with high resolution, the proxyImage must have this resolution. + * Example: + * Set an image of QSize(100,100), and set a proxyImageDefaultSize of QSize(20,20). + * Initially (scale = 1.0) the image will be downscaled to QSize(20,20). + * If the user increases the scale to 4.0, the image will be scaled to QSize(80,80) + * This should ensure a good image quality. + */ + MarkerData(const QImage &_proxyImage, const QSize &_proxyImageDefaultSize) + : m_proxyImage(_proxyImage) + , m_proxyImageDefaultSize(_proxyImageDefaultSize) + {} + + /** + * @brief operator== Compare two objects + * @param other to be compared with + * @return true if identical, otherwise false. + */ + bool operator==(const MarkerData &other) const + { + return ((this->m_proxyImage == other.m_proxyImage) && + (this->m_proxyMarkerSize == other.m_proxyMarkerSize) && + (this->m_proxyMarkerColor == other.m_proxyMarkerColor) && + (this->m_proxyMarker == other.m_proxyMarker) + ); + } + + /** + * @brief operator!= Compare two objects + * @param other to be compared with + * @return true if NOT identical, otherwise false. + */ + bool operator!=(const MarkerData &other) const + { + return !(*this == other); + } + + bool hasMarker() const { return m_proxyMarker != MarkerSymbolType::Undefined; } + bool hasProxyImage() const { return !m_proxyImage.isNull(); } + QImage proxyImage() const { return m_proxyImage; } + bool isEmpty() const { return !hasMarker() && !hasProxyImage(); } + + QString toString() const + { + QString str = QLatin1String("GeoImage("); + if (m_proxyImage.isNull()) + str.append(QLatin1String("noProxyImage, ")); + else + str.append(QLatin1String("withProxyImage, ")); + return str; + } + +protected: + friend class SoundAreaImpl; + MarkerSymbolType m_proxyMarker{MarkerSymbolType::Undefined}; + int m_proxyMarkerSize{20}; + QColor m_proxyMarkerColor{Qt::red}; + QImage m_proxyImage; ///< if not set, marker will be used + QSize m_proxyImageDefaultSize; + +}; + + +/** + * @brief The SoundArea class supports SoundArea. + */ +class SoundArea : public virtual AbstractMultiPointMapObject, public virtual AbstractEffectSimulation +{ + +public: + SoundArea() ///< Contructor + : AbstractMultiPointMapObject(), AbstractEffectSimulation() + {} + + virtual ~SoundArea() = default; ///< Destructor + + /*! \name Point symbol */ + //@{ + virtual Point3D markerOrigin() const = 0; + virtual void setMarkerOrigin(const Point3D &origin) = 0; + + virtual const MarkerData &markerData() const = 0; + virtual void setMarkerData(const MarkerData &markerData) = 0; + //@} + + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/text.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/text.h new file mode 100755 index 000000000..018c91980 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/text.h @@ -0,0 +1,53 @@ +/** + * @RDE_HEADER@ + * Interface of Text + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_single_point_map_object.h" + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The Polyline class supports polylines. + */ +class Text : public virtual AbstractSinglePointMapObject +{ + + using BaseClass = AbstractSinglePointMapObject; + +public: + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + Text() : BaseClass() {;} ///< Contructor + + virtual ~Text() = default; ///< Destructor + + /** + * @brief Set flag 'showAnchorMarker' to switch on additional anchor marker graphic + * @return value of flag 'showAnchorPosition' + * + * If 'showAnchorMarker' is switched on, some additional graphic is drawn to visualize anchor points of text lines. Per default 'showAnchorMarker' is switched off. + */ + virtual void setShowAnchorMarker(bool on) = 0; + + /** + * @brief Get flag 'showAnchorPosition' + * @return value of flag 'showAnchorMarker' + */ + virtual bool showAnchorMarker() const = 0; + +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/virtual_mine.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/virtual_mine.h new file mode 100755 index 000000000..004f49ae5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjects/virtual_mine.h @@ -0,0 +1,111 @@ +/** + * @RDE_HEADER@ + * Interface of VirtualMine + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "abstract_multi_point_map_object.h" +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The ChemicalAgent class supports different agent types. + */ +class VirtualMine : public virtual AbstractMultiPointMapObject, public virtual AbstractEffectSimulation +{ + +public: + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Don't use "= default" because of bug in VS2015 JT 2016/11/7 + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + VirtualMine() : AbstractMultiPointMapObject(), AbstractEffectSimulation() {;} ///< Contructor + + virtual ~VirtualMine() = default; ///< Destructor + + + /*! \name Subsidiary polyline/polygons */ + //@{ + /** + * @brief Get subsidiary line with key. + * @param key to be retrieved. + * @return subsidiary line at index + */ + virtual PositionCont subsidiaryLine(int key) const = 0; + + /** + * @brief Set subsidiary line with key. + * @param key to be set. + * @param positions subsidiary line to be set. + */ + + struct OptionalData + { + // ctor + OptionalData() {} + // member + bool closed{false}; + QSharedPointer lineStyle; // default is 'empty', do not honor + QSharedPointer fillStyle; // default is 'empty', do not honor + }; + +// virtual void setSubsidiaryLine(int key, const PositionCont &positions, bool clo = false) = 0; + virtual void setSubsidiaryLine(int key, const PositionCont &positions, const OptionalData &optData = OptionalData()) = 0; + + /** + * @brief Get a list of keys in use. + * @return List of keys in use. + */ + virtual QList subsidiaryKeysInUse() = 0; + + /** + * @brief Remove one subsidiary line. + * @param key to be removed. + */ + virtual void removeSubsidiaryLine(int key) = 0; + + /** + * @brief Remove all subsidiaryPositions. + */ + virtual void removeSubsidiaryLines() = 0; + //@} + + /*! \name 3D marker / SimpleMarkerSceneSymbol (via renderer) */ + //@{ + virtual bool has3DMarkerRenderer() const = 0; + virtual void set3DMarkerRenderer(const Point3D &markerOrigin, const QVariantMap &attribMap) = 0; + //@} + + /*! \name 3D marker / SimpleMarkerSceneSymbol (explicit symbol) */ + //@{ + struct SceneSymbolData + { + // ctor + SceneSymbolData() {} + // member + Point3D markerOrigin; + QColor color{Qt::gray}; + double radius{1.0}; + + bool cone{false}; // cone - sphere + double heading{0.0}; + double pitch{0.0}; + double roll{0.0}; + }; + + virtual bool has3DMarkerSceneSymbol() const = 0; + virtual void set3DMarkerSceneSymbol(const Point3D &markerOrigin, const SceneSymbolData &sceneSymbolData) = 0; + //@} +}; + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/CMakeLists.txt new file mode 100755 index 000000000..d518cb26d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/CMakeLists.txt @@ -0,0 +1,110 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# +FIND_PACKAGE(ArcGIS REQUIRED) + + +# LIBRARY +SET(MapObj_SRCS +# geometry.cpp +) +SET(MapObj_HDRS + situation_map_objects_export.h +) + +SET(MapObjImpl_SRCS + abstract_multi_point_map_object_impl.cpp + abstract_single_point_map_object_impl.cpp + air_support_impl.cpp + area_fire_impl.cpp + basic_path_object_impl.cpp + biological_agent_impl.cpp + building_outline_impl.cpp + chemical_agent_impl.cpp + cube_impl.cpp + danger_area_impl.cpp + functional_area_impl.cpp + geo_image_impl.cpp + hit_line_impl.cpp + line_of_sight_impl.cpp + indication_container.cpp + mine_impl.cpp + map_object_attributes_impl.cpp + map_object_indicator_impl.cpp + mine_field_impl.cpp + nine_liner_impl.cpp + nuclear_agent_impl.cpp + path_container.cpp + path_container.h + participant_impl.cpp + polygon_impl.cpp + polyline_impl.cpp + single_fire_impl.cpp +# single_fire_central_impl.cpp + sound_area_impl.cpp + text_impl.cpp + virtual_mine_impl.cpp +) + +SET(MapObjImpl_HDRS + abstract_multi_point_map_object_impl.h + abstract_single_point_map_object_impl.h + air_support_impl.h + area_fire_impl.h + basic_path_object_impl.h + biological_agent_impl.h + building_outline_impl.h + chemical_agent_impl.h + cube_impl.h + danger_area_impl.h + functional_area_impl.h + geo_image_impl.h + hit_line_impl.h + line_of_sight_impl.h + indication_container.h + map_object_attributes_impl.h + map_object_indicator_impl.h + mine_field_impl.h + mine_impl.h + nuclear_agent_impl.h + nine_liner_impl.h + participant_impl.h + polygon_impl.h + polyline_impl.h + single_fire_impl.h +# single_fire_central_impl.h + sound_area_impl.h + text_impl.h + virtual_mine_impl.h + + situation_map_objects_export.h +) + + +SOURCE_GROUP(FunctionDialog FILES ${FunctionDialog_SRCS}) +SOURCE_GROUP(MapObjectsImpl FILES ${MapObj_SRCS} ${MapObj_HDRS} ${MapObjImpl_SRCS} ${MapObjImpl_HDRS}) + +NGL_ADD_LIBRARY(WS situationMapObjects SHARED ${MapObj_SRCS} ${MapObj_HDRS} ${MapObjImpl_SRCS} ${MapObjImpl_HDRS}) + +NGL_TARGET_INCLUDE_DIRECTORIES(situationMapObjects + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + + +NGL_TARGET_LINK_LIBRARIES(situationMapObjects + PRIVATE + NGLLogger + ArcGIS::ArcGIS + LtsCoordTrans + PUBLIC + situationMap + Qt6::Core + Qt6::Widgets +) + +NGL_INSTALL_TARGET(situationMapObjects) + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/_single_fire_central_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/_single_fire_central_impl.cpp new file mode 100755 index 000000000..976575e93 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/_single_fire_central_impl.cpp @@ -0,0 +1,415 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of SingleFire + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "single_fire_central_impl.h" +#include "single_fire_impl.h" +#include +#include +#include +#include + + +#include + +// ESRI +#include +#include + +#include + +// Qt +#include +#include +#include +#include + + +namespace SituationMap +{ + +class MyAnimation : public QVariantAnimation +{ +public: + MyAnimation(GraphicalObjectSceneMarker *detonationSceneMarker) + : m_detonationSceneMarker(detonationSceneMarker) + {} + ~MyAnimation() = default; + + void updateCurrentValue(const QVariant &value) + { + if (!m_detonationSceneMarker) + return; + fs = m_detonationSceneMarker->fillStyle(); + col = fs.fillColor(); + col.setAlphaF(value.toFloat()); + fs.setFillColor(col); + m_detonationSceneMarker->setFillStyle(fs); + m_detonationSceneMarker->updateView(); + } +private: + GraphicalObjectSceneMarker *m_detonationSceneMarker{nullptr}; + FillStyle fs; + QColor col; +}; + + +//----------------------------------------------------------------------------- +// class SingleFireCentralImpl +//----------------------------------------------------------------------------- +SingleFireCentralImpl::SingleFireCentralImpl(const QString &debugInfo) + : SingleFireCentral() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("SingleFireCentralImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + m_fillColorAlpha = 200; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +SingleFireCentralImpl::~SingleFireCentralImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete m_animation; + delete m_centerImage; + delete m_detonationPath; + delete m_detonationSceneMarker; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +QString SingleFireCentralImpl::toString() const +{ + QString str; + str.append(BaseClass::toString()); + str.append(BasicPathObjectImpl::toString()); + return str; +} + +Point3D SingleFireCentralImpl::impactOrigin() const +{ + return BaseClass::tacticalOrigin(); +} +void SingleFireCentralImpl::setImpactOrigin(const Point3D &impactOrigin) +{ + BaseClass::setTacticalOrigin(impactOrigin); +} + +void SingleFireCentralImpl::setDetonation(const SingleFireCentral::DetonationData &detonationData) +{ + if (m_detonationOrigin.value().origin() != detonationData.m_origin) + m_detonationOrigin.refValue().setOrigin(detonationData.m_origin); + m_detonationRadius = detonationData.m_radius; +} + +SingleFireCentral::DetonationData SingleFireCentralImpl::detonation() const +{ + return {m_detonationOrigin.value().origin(), m_detonationRadius.value()}; +} + +bool SingleFireCentralImpl::startDetonationAnimation() +{ + if (!m_detonationSceneMarker) + return false; + if (!m_animation) + { + m_animation = new MyAnimation(m_detonationSceneMarker); + m_animation->setDuration(500); + m_animation->setKeyValueAt(0.0 , 1.0); + m_animation->setKeyValueAt(0.2 , 0.5); + m_animation->setKeyValueAt(1.0 , 0.1); + // m_animation.setEasingCurve(QEasingCurve::InOutQuart); + } + m_animation->stop(); + m_animation->start(); + + + + if (!m_symbol3D) + { + qCritical() << " ModelSceneSymbol: ....:"; + + +// m_symbol3D = new Esri::ArcGISRuntime::ModelSceneSymbol(QUrl(QLatin1Literal("/shd/CTC/TOOLS/ArcGIS/Runtime/Data/3D/Bristol/Collada/Bristol.dae")), 5.0); // works +// m_symbol3D = new Esri::ArcGISRuntime::ModelSceneSymbol(QUrl(QLatin1Literal("/shd/CTC/TOOLS/ArcGIS/Proof_of_concept_Explosion_DAE/Proof_of_concept_Explosion_DAE_01.dae")), 10.0); +// m_symbol3D = new Esri::ArcGISRuntime::ModelSceneSymbol(QUrl(QLatin1Literal("/shd/CTC/TOOLS/ArcGIS/Proof_of_concept_Explosion_DAE/Proof_of_concept_Explosion_DAE_02_60FPS.dae")), 10.0); +// m_symbol3D = new Esri::ArcGISRuntime::ModelSceneSymbol(QUrl(QLatin1Literal("/shd/CTC/TOOLS/ArcGIS/Proof_of_concept_Explosion_DAE/Proof_of_concept_Explosion_DAE_Sampled.dae")), 10.0); +// m_symbol3D = new Esri::ArcGISRuntime::ModelSceneSymbol(QUrl(QLatin1Literal("/shd/CTC/TOOLS/ArcGIS/Proof_of_concept_Explosion_DAE/Proof_of_concept_Explosion_DAE_unbaked.dae")), 10.0); +// m_symbol3D = new Esri::ArcGISRuntime::ModelSceneSymbol(QUrl(QLatin1Literal("/shd/CTC/TOOLS/ArcGIS/Proof_of_concept_Explosion_DAE/Proof_of_concept_Explosion_3ds_NoAnimation.3ds")), 10.0); + + +// m_symbol3D = new Esri::ArcGISRuntime::ModelSceneSymbol(QUrl(QLatin1Literal("/shd/CTC/TOOLS/ArcGIS/Runtime/Data/3D/3dexport/explosion/explosionFBX.fbx")), 10.0); + +// m_symbol3D = new Esri::ArcGISRuntime::ModelSceneSymbol(QUrl(QLatin1Literal("/shd/CTC/TOOLS/ArcGIS/Runtime/Data/3D/3dexport/JetAnim/jet anim.fbx")), 10.0); // works +// m_symbol3D = new Esri::ArcGISRuntime::ModelSceneSymbol(QUrl(QLatin1Literal("/shd/CTC/TOOLS/ArcGIS/Runtime/Data/3D/bradley_low_3ds/bradle.3ds")), 10.0); // works + m_symbol3D = new Esri::ArcGISRuntime::ModelSceneSymbol(QUrl(QStringLiteral("/shd/CTC/TOOLS/ArcGIS/Runtime/Data/3D/dolmus_3ds/dolmus.3ds")), 10.0); + + + QObject::connect(m_symbol3D, &Esri::ArcGISRuntime::ModelSceneSymbol::errorOccurred, + m_animation, [](const Esri::ArcGISRuntime::Error &loadError){ + qCritical() << " Error: : isEmpty:" << loadError.isEmpty(); + qCritical() << " Error: : code:" << loadError.code(); + qCritical() << " Error: : loadError:" << loadError.message(); + }); + + + QObject::connect(m_symbol3D, &Esri::ArcGISRuntime::ModelSceneSymbol::doneLoading, + m_animation, [](const Esri::ArcGISRuntime::Error &loadError){ + qCritical() << " ModelSceneSymbol: doneLoading: isEmpty:" << loadError.isEmpty(); + qCritical() << " ModelSceneSymbol: doneLoading: code:" << loadError.code(); + qCritical() << " ModelSceneSymbol: doneLoading: loadError:" << loadError.message(); + }); + m_symbol3D->load(); + + if (!m_graphic3D) + { + Point3D pos = m_detonationOrigin.value().originTranslated(); + m_graphic3D = new Esri::ArcGISRuntime::Graphic( Esri::ArcGISRuntime::Point(pos.x(), pos.y(), 20, m_mapImpl->getEsriSpatialReference()), m_symbol3D); + m_graphicsLayer->graphics()->append(m_graphic3D); + } + + } + + + + + + return true; +} + +AesEffectState SingleFireCentralImpl::aesState() const +{ + return BaseClass::aesState(); +} +void SingleFireCentralImpl::setAesState(AesEffectState aesState) +{ + BaseClass::setAesState(aesState); +} + +SymbolImage SingleFireCentralImpl::symbolImage() const +{ + SymbolImage symImage = m_tacticalAppearance->symbolImage(); + return symImage; +} + + + +bool SingleFireCentralImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " positions =" << this->positions() + << " radius =" << this->radius() + << " partyId =" << this->partyId() + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + if (m_tacticalAppearance) + { + if (partyChanged()) + m_symbol.refValue().color = partyColor(); + } + + // centerImage: + if (!m_centerImage) + { + m_centerImage = + new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QLatin1String("#CenterImage"), + m_uniqueMapId); + } + + if (partyChanged()) + { + QImage img = SingleFireImpl::renderImpactSymbol(partyColor(), 20, true); // filled + m_centerImage->setImage(img); + } + +// m_centerImage->setScale(m_mainGraphicalObject->scale()); // do not scale centerImage, equal to mine of MineArea + + m_centerImage->setOrigin(m_tacticalOrigin.value().originTranslated()); + m_centerImage->setVisibility(m_visibility && m_visibilityTechnical); + + bool success = BaseClass::updateViewImpl(); // will call onMapExtentChanged and therefore m_centerImage->updateView + + bool successDetonationSceneMarker = true; + switch (m_mapImpl->mode()) + { + case Mode::Mode_2D: + successDetonationSceneMarker = updateDetonationPath(); + break; + case Mode::Mode_3D: + successDetonationSceneMarker = updateDetonationSceneMarker(); + break; + case Mode::Undefined: + // empty + break; + } + + success = success && successDetonationSceneMarker; + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool SingleFireCentralImpl::updateDetonationPath() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool success = true; + + // do we have a detonationPoint / impactOrigin? + if (m_detonationOrigin.value().origin().isNull() && (m_detonationRadius.value() < 1.0)) + { + delete m_detonationPath; + m_detonationPath = nullptr; + } else { + if (!m_detonationPath) + { + m_detonationPath = + new GraphicalObjectEllipse(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QLatin1String("#DetonationEllipse"), + m_uniqueMapId); + } + { + EllipseParameters ellipsePara; + ellipsePara.setLengthX(m_detonationRadius.value()); + ellipsePara.setLengthY(m_detonationRadius.value()); + ellipsePara.setMaxSegmentLength(1); + ellipsePara.setMaxPointCount(30); // limit to 30 to reduce the number of vertices. If to high, keep performance impact to 3D in mind!!! + m_detonationPath->setEllipseParameters(ellipsePara); + } + m_detonationPath->setOrigin(m_detonationOrigin.value().originTranslated()); + m_detonationPath->setPickable(m_pickable); + // path == 0; indicationCont == 1; Marker == 2 + m_detonationPath->setDrawOrder(m_internalDrawOrder+2); + QColor col = partyColor(); + m_detonationPath->setLineStyle(LineStyle(col, 2)); + col.setAlphaF(col.alphaF() / 2.0); + m_detonationPath->setFillStyle(FillStyle(col, + m_mainGraphicalObject->fillStyle().fillPattern())); // take fillpattern + m_detonationPath->setScale(this->scale()); + success = m_detonationPath->updateView(); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; + +} + +bool SingleFireCentralImpl::updateDetonationSceneMarker() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool success = true; + + // do we have a detonationPoint / impactOrigin? + if (m_detonationOrigin.value().origin().isNull() && (m_detonationRadius.value() < 1.0)) + { + delete m_detonationSceneMarker; + m_detonationSceneMarker = nullptr; + } else { + if (!m_detonationSceneMarker) + { + m_detonationSceneMarker = + new GraphicalObjectSceneMarker(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QLatin1String("#DetonationSceneMarker"), + m_uniqueMapId); + } + m_detonationSceneMarker->setOrigin(m_detonationOrigin.value().originTranslated()); + m_detonationSceneMarker->setDiameter(m_detonationRadius.value()*2.0); + m_detonationSceneMarker->setPickable(m_pickable); + // path == 0; indicationCont == 1; Marker == 2 + m_detonationSceneMarker->setDrawOrder(m_internalDrawOrder+2); + QColor col = partyColor(); + col.setAlphaF(col.alphaF() / 2.0); + m_detonationSceneMarker->setFillStyle(FillStyle(col, FillPattern::Solid)); + success = m_detonationSceneMarker->updateView(); + } + + // DEBUG: Animation in a loop + startDetonationAnimation(); + + + // check intersect + if (m_detonationSceneMarker && m_mainGraphicalObject ){ + Esri::ArcGISRuntime::GeometryEngine* geoEngine = Esri::ArcGISRuntime::GeometryEngine::instance(); + + Esri::ArcGISRuntime::Geometry geoLine = m_mainGraphicalObject->getGraphicGeometry(); + qCritical() << " geoLine " << geoLine.toJson(); + + Esri::ArcGISRuntime::Geometry geoSphere = m_detonationSceneMarker->getGraphicGeometry(); + qCritical() << " geoSphere " << geoSphere.toJson(); + + Esri::ArcGISRuntime::Geometry resultGeometry = + geoEngine->intersection(geoLine, geoSphere); + + qCritical() << " resultGeometry " << resultGeometry.toJson(); + } + + + + if (m_animation) + QObject::connect(m_animation, &QVariantAnimation::finished, [this]() + { + QTimer::singleShot(2000, m_animation, [this]() { + m_animation->start(); + }); + }); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool SingleFireCentralImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polyline); + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + setLineStyle(LineStyle(Qt::black, 2)); + + m_detonationOrigin.refValue().setMapObjectImpl(this); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool SingleFireCentralImpl::onMapExtentChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool successCenterImage = true; + bool visiScale = m_visibilityScaleRange.scaleHasVisibility(m_mapImpl->scale()); + if (m_centerImage) + { + m_centerImage->setVisibility(visiScale && m_visibility && m_visibilityTechnical); + successCenterImage = m_centerImage->updateView(); + } + bool successDetonationSceneMarker = true; + if (m_detonationSceneMarker) + { + m_detonationSceneMarker->setVisibility(visiScale && m_visibility && m_visibilityTechnical); + successCenterImage = m_detonationSceneMarker->updateView(); + } + + bool success = BaseClass::onMapExtentChanged() && successCenterImage && successDetonationSceneMarker; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; + +} + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/_single_fire_central_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/_single_fire_central_impl.h new file mode 100755 index 000000000..0340291ce --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/_single_fire_central_impl.h @@ -0,0 +1,116 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of SingleFire + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "basic_path_object_impl.h" +#include // Interface +#include +#include +#include + +#include + +// Qt + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ +class MyAnimation; + + +/** + * @brief The SingleFireCentralImpl class displays a single impact. + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (displaying a polyline) + * - mainGraphicalObjectText (displaying the text/name) + * - tacticalAppearance (displaying the tact. Appearance) + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT SingleFireCentralImpl : public BasicPathObjectImpl, public virtual SingleFireCentral +{ + using BaseClass = BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit SingleFireCentralImpl(const QString &debugInfo = QString()); + virtual ~SingleFireCentralImpl(); + +public: + SingleFireCentralImpl(const SingleFireCentralImpl &other) = delete; + SingleFireCentralImpl &operator=(SingleFireCentralImpl &other) = delete; + + QString toString() const override; + + + // some set/get methods: + //@{ + void setDetonation(const DetonationData &detonationData) override; + DetonationData detonation() const override; + bool startDetonationAnimation() override; + + // required??? + Point3D impactOrigin() const override; + void setImpactOrigin(const Point3D &impactOrigin) override; + //@} + + // Delegate to BaseClass: + AesEffectState aesState() const override; + void setAesState(AesEffectState aesState) override; + + SymbolImage symbolImage() const override; + +protected: + bool updateViewImpl() override; + + bool updateViewImplPost() override + { + BaseClass::updateViewImplPost(); + m_detonationOrigin.reset(); + m_detonationRadius.reset(); + return true; + } + + bool updateDetonationPath(); + bool updateDetonationSceneMarker(); + + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + bool onMapExtentChanged() override; + + // internal/admin + + // logical + + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility + + GraphicalObjectPictureMarker *m_centerImage{nullptr}; + + SimpleValue m_detonationOrigin; + SimpleValue m_detonationRadius{0.0}; + GraphicalObjectEllipse *m_detonationPath{nullptr}; // 2D + GraphicalObjectSceneMarker *m_detonationSceneMarker{nullptr}; // 3D + + MyAnimation *m_animation{nullptr}; + + Esri::ArcGISRuntime::Graphic *m_graphic3D{nullptr}; + Esri::ArcGISRuntime::ModelSceneSymbol *m_symbol3D{nullptr}; +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/abstract_multi_point_map_object_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/abstract_multi_point_map_object_impl.cpp new file mode 100755 index 000000000..046361381 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/abstract_multi_point_map_object_impl.cpp @@ -0,0 +1,287 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of AbstractMultiPointMapObject + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include +#include +#include +#include +#include + +// ESRI + + +// Qt +#include +#include + + +namespace SituationMap +{ + +// taken from AreaEffectSimulation::Icons::imageFromPolygon() +QImage imageFromForm(const PositionCont &polyAbsGeoPos, + QColor lineColor, + bool closed, + Qt::BrushStyle style, + QColor fillColor = Qt::black, + const int width = 100) +{ + if (polyAbsGeoPos.isEmpty()) + return QImage(); + QPolygonF polyInUtmCoords; + polyInUtmCoords.reserve(polyAbsGeoPos.size()); + int zone = LtsCoordTrans::UtmCoordinate::fromGeographic(polyAbsGeoPos.first().toPointF()).zone; + for (const Point3D &point : polyAbsGeoPos) + { + const LtsCoordTrans::UtmCoordinate &coord = LtsCoordTrans::UtmCoordinate::fromGeographic(point.toPointF(), zone); + polyInUtmCoords << QPointF(coord.x, coord.y); + } + + const QPolygonF bounding = polyInUtmCoords.boundingRect(); + + // center to center / Todo:: Does it always work? + const QTransform mTrans(QTransform::fromTranslate(-bounding.boundingRect().bottomLeft().x(), -bounding.boundingRect().bottomLeft().y())); + + + qreal w = bounding.boundingRect().width(); + qreal h = bounding.boundingRect().height(); + double scale = w > h ? width / w : width / h; + w *= scale; + h *= scale; + scale *= 0.90; // Leave 10% border (i.e. 5% each side) + const QTransform mScale(QTransform::fromScale(scale, -scale)); + const QTransform mTransSmall(QTransform::fromTranslate(0.05 * width + (width - w) / 2, 0.05 * width + (width - h) / 2)); + + QImage img(width, width, QImage::Format_ARGB32); + img.fill(Qt::transparent); + + QPainter painter(&img); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.setPen(QPen(lineColor, 2)); + + QPolygonF mPoly = mTrans.map(polyInUtmCoords); + mPoly = mScale.map(mPoly); + mPoly = mTransSmall.map(mPoly); + + if (closed && fillColor.isValid()) + { + // polyGON / closed: + painter.setBrush(QBrush(fillColor, style)); + painter.drawPolygon(mPoly); + } + else + { + // polyLINE / open: + painter.drawPolyline(mPoly); + } + painter.end(); + + return img; +} + +//----------------------------------------------------------------------------- +// class AbstractMultiPointMapObjectImpl +//----------------------------------------------------------------------------- +AbstractMultiPointMapObjectImpl::AbstractMultiPointMapObjectImpl(const QString &debugInfo) + : AbstractMultiPointMapObject() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("AbstractMultiPointMapObjectImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + m_positions.setMapObjectImpl(this); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +AbstractMultiPointMapObjectImpl::~AbstractMultiPointMapObjectImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +PositionCont AbstractMultiPointMapObjectImpl::positions() const +{ + return m_positions.positions(); +} + +PositionCont AbstractMultiPointMapObjectImpl::positionsTranslated() const +{ + return m_positions.positionsTranslated(); +} + +bool AbstractMultiPointMapObjectImpl::setPositionsTranslated(const PositionCont &positions) +{ + m_updateObjectIndicator = m_positions.setPositionsTranslated(positions) || m_updateObjectIndicator; + return m_updateObjectIndicator; +} + +bool AbstractMultiPointMapObjectImpl::setPositions(const PositionCont &positions) +{ + m_updateObjectIndicator = m_positions.setPositions(positions) || m_updateObjectIndicator; + return m_updateObjectIndicator; +} + +LineStyle AbstractMultiPointMapObjectImpl::lineStyle() const +{ + return BaseClass::lineStyle(); +} +LineStyle &AbstractMultiPointMapObjectImpl::rLineStyle() +{ + return BaseClass::rLineStyle(); +} +void AbstractMultiPointMapObjectImpl::setLineStyle(const LineStyle &lineStyle) +{ + BaseClass::setLineStyle(lineStyle); +} +FillStyle AbstractMultiPointMapObjectImpl::fillStyle() const +{ + return BaseClass::fillStyle(); +} +FillStyle &AbstractMultiPointMapObjectImpl::rFillStyle() +{ + return BaseClass::rFillStyle(); +} +void AbstractMultiPointMapObjectImpl::setFillStyle(const FillStyle &fillStyle) +{ + BaseClass::setFillStyle(fillStyle); +} + +void AbstractMultiPointMapObjectImpl::updateIdentifierPosition() +{ +// NOTICE_LOG(m_logger) << LOGFUNCTION << " START" << " debugInfo=" << m_debugInfo; + + if (!m_mainGraphicalObject) + return; + + + if (m_positions.isEmpty()) + return; + + // find right coordinate: + Point3D resultPos; + const PositionCont &posCont = m_positions.positionsTranslated(); + for (const Point3D &pos : std::as_const(posCont)) + if (pos.x() > resultPos.x()) + resultPos = pos; + + if (!qFuzzyIsNull(resultPos.x())) + m_identifierPositionCandidateTranslated = resultPos; +} + +QString AbstractMultiPointMapObjectImpl::toString() const +{ + QString str; + str.append(BaseClass::toString()); + str.append(QStringLiteral(" positions.count='%1'").arg(m_positions.positions().count())); + return str; +} + +void AbstractMultiPointMapObjectImpl::boundingBox(BoundingBox &bbox) const +{ + bbox.reset(); + + const PositionCont &posCont = m_positions.positions(); + if (posCont.isEmpty()) + return; + + QVectorIterator it(posCont); + const Point3D &pos = it.next(); + + double xmin = pos.x(); + double ymin = pos.y(); + double xmax = pos.x(); + double ymax = pos.y(); + + while (it.hasNext()) + { + const Point3D &pos2 = it.next(); + xmin = std::min(xmin, pos2.x()); + ymin = std::min(ymin, pos2.y()); + xmax = std::max(xmax, pos2.x()); + ymax = std::max(ymax, pos2.y()); + } + + double width = xmax - xmin; + double height = ymax - ymin; + + + QPointF center(xmin + width / 2.0, + ymin + height / 2.0); + + bbox = BoundingBox(BoundingBox::ExtentType::Sim, Point3D::fromQPointF(center), width, height); +} + +QImage AbstractMultiPointMapObjectImpl::previewImage() const +{ + return previewImageSpecific(); +} + +Qt::BrushStyle fillPatternToBrushStyle(const FillPattern &fillPattern) +{ + Qt::BrushStyle bs = Qt::SolidPattern; + + switch (fillPattern) { + case FillPattern::BackwardDiagonal: // / + bs = Qt::BDiagPattern; + break; + case FillPattern::Cross: + bs = Qt::CrossPattern; + break; + case FillPattern::DiagonalCross: + bs = Qt::DiagCrossPattern; + break; + case FillPattern::ForwardDiagonal: // '\' + bs = Qt::FDiagPattern; + break; + case FillPattern::Horizontal: + bs = Qt::HorPattern; + break; + case FillPattern::Null: + bs = Qt::NoBrush; + break; + case FillPattern::Solid: + bs = Qt::SolidPattern; + break; + case FillPattern::Vertical: + bs = Qt::VerPattern; + break; + default: + bs = Qt::SolidPattern; + break; + } + + return bs; +} + + + +QImage AbstractMultiPointMapObjectImpl::previewImageSpecific() const +{ + if (m_mainGraphicalObject) + { + FillStyle fs = m_mainGraphicalObject->fillStyle(); + Qt::BrushStyle bs = fillPatternToBrushStyle(fs.fillPattern()); + + return imageFromForm(this->positions(), + m_mainGraphicalObject->lineStyle().lineColor(), + m_isClosed, + bs, + fs.fillColor()); + } + + QColor lineColor = this->majorColor(); + if (!lineColor.isValid()) + lineColor = Qt::black; + return imageFromForm(this->positions(), lineColor, m_isClosed, Qt::BrushStyle::SolidPattern); +} + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/abstract_multi_point_map_object_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/abstract_multi_point_map_object_impl.h new file mode 100755 index 000000000..503f09458 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/abstract_multi_point_map_object_impl.h @@ -0,0 +1,83 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of AbstractMultiPointMapObject + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include +#include +#include // Interface +#include "situation_map_objects_export.h" + + +// Qt + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +/** + * @brief The AbstractMultiPointMapObjectImpl class adds a unlimited number of positions (PositionCont) to the AbstractMapObjectImpl. + * By this a polyline and a polygon can be described. Accordingly a LineStyle and a FillStyle is added also. + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT AbstractMultiPointMapObjectImpl : public AbstractMapObjectImpl, public virtual AbstractMultiPointMapObject +{ + using BaseClass = AbstractMapObjectImpl; + +public: + explicit AbstractMultiPointMapObjectImpl(const QString &debugInfo = QString()); + virtual ~AbstractMultiPointMapObjectImpl(); + + AbstractMultiPointMapObjectImpl(const AbstractMultiPointMapObjectImpl &other) = delete; + AbstractMultiPointMapObjectImpl &operator=(AbstractMultiPointMapObjectImpl &other) = delete; + + QString toString() const override; + + + PositionCont positions() const override; + bool setPositions(const PositionCont &positions) override; + PositionCont positionsTranslated() const override; + bool setPositionsTranslated(const PositionCont &positions) override; + + void boundingBox(BoundingBox &bbox) const override; + + QImage previewImage() const override; + QImage previewImageSpecific() const override; + + + 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; + + +protected: + // internal/admin + void updateIdentifierPosition() override; + + + // logical + PositionsTranslated m_positions; + bool m_isClosed = true; + + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/abstract_single_point_map_object_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/abstract_single_point_map_object_impl.cpp new file mode 100755 index 000000000..aa3f050b0 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/abstract_single_point_map_object_impl.cpp @@ -0,0 +1,175 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of AbstractSinglePointMapObject + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "abstract_single_point_map_object_impl.h" +#include +#include +#include +#include + + +// ESRI + + +// Qt + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class AbstractSinglePointMapObjectImpl +//----------------------------------------------------------------------------- +AbstractSinglePointMapObjectImpl::AbstractSinglePointMapObjectImpl(const QString &debugInfo) + : BaseClass(debugInfo) // Implementation + , m_logger(getLogger("AbstractSinglePointMapObjectImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + m_origin.setMapObjectImpl(this); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +AbstractSinglePointMapObjectImpl::~AbstractSinglePointMapObjectImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +QString AbstractSinglePointMapObjectImpl::toString() const +{ + QString str; + str.append(BaseClass::toString()); + + std::ostringstream stream; + stream << " " << m_origin.origin(); + str.append(QLatin1String(stream.str().c_str())); + + str.append(QStringLiteral(" radius=%1").arg(m_radius.value())); + + return str; +} + + +Point3D AbstractSinglePointMapObjectImpl::origin() const +{ + return m_origin.origin(); +} + +Point3D AbstractSinglePointMapObjectImpl::originTranslated() const +{ + return m_origin.originTranslated(); +} + +bool AbstractSinglePointMapObjectImpl::setOrigin(const Point3D &origin) +{ + m_updateObjectIndicator = m_origin.setOrigin(origin) || m_updateObjectIndicator; + return m_updateObjectIndicator; +} + +void AbstractSinglePointMapObjectImpl::boundingBox(BoundingBox &bbox) const +{ + if (m_radius.value() > 0) + bbox = BoundingBox(BoundingBox::ExtentType::Sim, m_origin.origin(), 2 * m_radius.value(), 2 * m_radius.value()); + else + { + GraphicalObjectPictureMarker *goPicture = dynamic_cast(m_mainGraphicalObject); + if (goPicture) + { + // ToDo: offset of goPicture has to be honored... + + if (!goPicture->image().isNull()) + { + // goPicture has Image + const QSize &imgSize = goPicture->currentSize(); // currentSize + bbox = BoundingBox(BoundingBox::ExtentType::Pixel, + m_origin.origin(), // Sim + imgSize.width(), imgSize.height() // pixel + ); + } else + { + // goPicture has Marker + int markerSize = goPicture->markerSymbolSize(); + + bbox = BoundingBox(BoundingBox::ExtentType::Pixel, + m_origin.origin(), // Sim + markerSize, markerSize // pixel + ); + + } + } + else + bbox = BoundingBox(BoundingBox::ExtentType::Sim, m_origin.origin()); // widht and heigth will be 0! + } +} + + +float AbstractSinglePointMapObjectImpl::radius() const +{ + return m_radius.value(); +} +void AbstractSinglePointMapObjectImpl::setRadius(float radius) +{ + m_radius = radius; +} + + + + +LineStyle AbstractSinglePointMapObjectImpl::lineStyle() const +{ + return BaseClass::lineStyle(); +} +LineStyle &AbstractSinglePointMapObjectImpl::rLineStyle() +{ + return BaseClass::rLineStyle(); +} +void AbstractSinglePointMapObjectImpl::setLineStyle(const LineStyle &lineStyle) +{ + BaseClass::setLineStyle(lineStyle); +} +FillStyle AbstractSinglePointMapObjectImpl::fillStyle() const +{ + return BaseClass::fillStyle(); +} +FillStyle &AbstractSinglePointMapObjectImpl::rFillStyle() +{ + return BaseClass::rFillStyle(); +} +void AbstractSinglePointMapObjectImpl::setFillStyle(const FillStyle &fillStyle) +{ + BaseClass::setFillStyle(fillStyle); +} + +void AbstractSinglePointMapObjectImpl::updateIdentifierPosition() +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " START" << " debugInfo=" << m_debugInfo; + + if (m_mainGraphicalObjectText) + { + SituationMap::BoundingBox bbox; + this->boundingBox(bbox); + + if (bbox.isValid() && bbox.extentType() == BoundingBox::ExtentType::Pixel) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " START" << " bbox=" << bbox; + m_mainGraphicalObjectText->setXOffset((bbox.width() / 2.0) + 10); + } + else + m_mainGraphicalObjectText->setXOffset(10); + + } + + BaseClass::updateIdentifierPosition(); // calculating the origin +} + + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/abstract_single_point_map_object_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/abstract_single_point_map_object_impl.h new file mode 100755 index 000000000..2182b0118 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/abstract_single_point_map_object_impl.h @@ -0,0 +1,82 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of AbstractSinglePointMapObject + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include +#include +#include // Interface +#include "situation_map_objects_export.h" + +// Qt + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +/** + * @brief The AbstractSinglePointMapObjectImpl class adds excatly one positions (QPointF) to the AbstractMapObjectImpl. + * For convenience a radius can also be parametrized. Accordingly a LineStyle and a FillStyle is added also. + * \note Maybe the radius be be removed in future. + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT AbstractSinglePointMapObjectImpl : public AbstractMapObjectImpl, public virtual AbstractSinglePointMapObject +{ + using BaseClass = AbstractMapObjectImpl; + +public: + explicit AbstractSinglePointMapObjectImpl(const QString &debugInfo = QString()); + virtual ~AbstractSinglePointMapObjectImpl(); + + AbstractSinglePointMapObjectImpl(const AbstractSinglePointMapObjectImpl &other) = delete; + AbstractSinglePointMapObjectImpl &operator=(AbstractSinglePointMapObjectImpl &other) = delete; + + QString toString() const override; + + + Point3D origin() const override; + bool setOrigin(const Point3D &origin) override; + virtual Point3D originTranslated() const; + + virtual float radius() const; + virtual void setRadius(float radius); + + void boundingBox(BoundingBox &bbox) const override; + + + 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; + + +protected: + // internal/admin + void updateIdentifierPosition() override; + + // logical + OriginTranslated m_origin; + + SimpleValue m_radius = -1; + + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/air_support_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/air_support_impl.cpp new file mode 100755 index 000000000..e9b71af53 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/air_support_impl.cpp @@ -0,0 +1,587 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of AirSupport + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "air_support_impl.h" +#include +#include +#include +#include +#include +#include + + +// ESRI +#include +#include + + +// Qt + + +namespace SituationMap +{ + +const int AIR_SUPPORT_LINE_WIDTH = 2; + +class AirSupportImpl::Private +{ +public: + Private() = default; + ~Private() + { + removeAesBuffer(); + removeAFP(); + removeALP(); + m_pm_AFP = nullptr; + m_pm_ALP = nullptr; + m_aesBuffer = nullptr; + } + + // ESRI data: + Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr; + + + // Own data: + MapImpl *m_mapImpl = nullptr; + UniqueMapId m_referencedObject = INVALID_MAP_ID; + + QString m_debugInfo; // will be appended by consecutive number + UniqueMapId m_uniqueMapId; + QColor m_color = Qt::black; + + + GraphicalObjectPictureMarker *m_pm_AFP = nullptr; + GraphicalObjectPictureMarker *m_pm_ALP = nullptr; + GraphicalObjectPath *m_aesBuffer = nullptr; + + QMap > m_cont; /// one polyline per subsidiary line + typedef QMapIterator> MAP_IT_T; +// int m_drawOrder = 0; + + + GraphicalObjectPictureMarker *getAFP() + { + if (!m_pm_AFP) + { + QString debugInfo = QStringLiteral("%1#Marker(AFP)").arg(m_debugInfo); + m_pm_AFP = new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + debugInfo, + m_uniqueMapId); + m_pm_AFP->setMarkerSymbol(MarkerSymbolType::Circle, 10, Qt::black); + } + return m_pm_AFP; + } + void removeAFP() + { + delete m_pm_AFP; + m_pm_AFP = nullptr; + } + + GraphicalObjectPictureMarker *getALP() + { + if (!m_pm_ALP) + { + QString debugInfo = QStringLiteral("%1#Marker(ALP)").arg(m_debugInfo); + m_pm_ALP = new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + debugInfo, + m_uniqueMapId); + m_pm_ALP->setMarkerSymbol(MarkerSymbolType::Circle, 10, Qt::black); + } + return m_pm_ALP; + } + void removeALP() + { + delete m_pm_ALP; + m_pm_ALP = nullptr; + } + + GraphicalObjectPath *getAesBuffer() + { + if (!m_aesBuffer) + { + QString debugInfo = QStringLiteral("%1#AesBuffer").arg(m_debugInfo); + m_aesBuffer = new GraphicalObjectPath(GraphicalObjectPathType::Polygon, + m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + debugInfo, + m_uniqueMapId); + m_aesBuffer->setFillStyle(FillStyle(FillPattern::Null)); + } + return m_aesBuffer; + } + void removeAesBuffer() + { + delete m_aesBuffer; + m_aesBuffer = nullptr; + } + + + + bool hasPathObject(int key) { return m_cont.contains(key); } + QList keysInUse() { return m_cont.keys(); } + void removePathObject(int key) { m_cont.remove(key); } + void clearPathObjects() { m_cont.clear(); } + + QSharedPointer getPathObject(int key) + { + if (m_cont[key].isNull()) + { + QString debugInfo = QStringLiteral("%1#SubsidiaryLine(%2)").arg(m_debugInfo).arg(key); + GraphicalObjectPath *temp = new GraphicalObjectPath(GraphicalObjectPathType::Polyline, + m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + debugInfo, + m_uniqueMapId); + if (temp) + m_cont[key] = QSharedPointer(temp); + } + return m_cont[key]; + } +}; // class AirSupportImpl::Private +//----------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- +// class AirSupportImpl +//----------------------------------------------------------------------------- +AirSupportImpl::AirSupportImpl(const QString &debugInfo) + : AirSupport() // Interface + , BaseClass(debugInfo) // Implementation + , d(new Private) + , m_logger(getLogger("AirSupportImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +AirSupportImpl::~AirSupportImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete d; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void AirSupportImpl::setUniqueId(const UniqueMapId &uniqueId) +{ + BaseClass::setUniqueId(uniqueId); + d->m_uniqueMapId = m_uniqueMapId; +} + +void AirSupportImpl::setReferencedObject(const UniqueMapId &referencedObject) +{ + d->m_referencedObject = referencedObject; +} + +bool AirSupportImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " m_debugInfo=" << m_debugInfo + << " symbol=" << m_symbol.value().sidc + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical + << " this->drawOrder=" << this->drawOrder() + << " this->positions()=" << this->positions(); + + // m_mainGraphicalObject stores the outline/'bounding box' (techn and tact) + // Those geometry should never be visible. + // The text of should be visible. + this->setVisibilityOfGeometry(false); + + if (this->positions().size() < 2) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " number of coordinates is not sufficiant" + << " (#coord=" << this->positions().size() << ")"; + return false; + } + + if (m_mainGraphicalObject) + d->m_color = m_mainGraphicalObject->lineStyle().lineColor(); + bool success = BaseClass::updateViewImpl(); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + + // real work: + + // + // First, draw AFP and ALP + // + + // AFP + { + Point3D coordTranslated = getCoordinateTranslated(COORD_INDEX_AFP); + if (coordTranslated.isNull()) + { + d->removeAFP(); + } else { + GraphicalObjectPictureMarker *pm = d->getAFP(); + pm->setMarkerSymbol(MarkerSymbolType::Circle, 10, d->m_color); + pm->setOrigin(coordTranslated); + pm->setVisibility(m_visibility && m_visibilityTechnical); + pm->setScale(m_mainGraphicalObject->scale()); + pm->setDrawOrder(m_mainGraphicalObject->drawOrder()); + if (!pm->updateView()) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed"; + } + } + + // ALP + { + Point3D coordTranslated = getCoordinateTranslated(COORD_INDEX_ALP); + if (coordTranslated.isNull()) + { + d->removeALP(); + } else { + GraphicalObjectPictureMarker *pm = d->getALP(); + pm->setMarkerSymbol(MarkerSymbolType::Circle, 10, d->m_color); + pm->setOrigin(coordTranslated); + pm->setVisibility(m_visibility && m_visibilityTechnical); + pm->setScale(m_mainGraphicalObject->scale()); + pm->setDrawOrder(m_mainGraphicalObject->drawOrder()); + if (!pm->updateView()) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed"; + } + } + + // + // delegate remaining to onMapExtentChanged + // + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool AirSupportImpl::onMapExtentChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + + bool success = BaseClass::onMapExtentChanged(); + + + if (!m_mainGraphicalObject) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no m_mainGraphicalObject yet"; + return false; + } + + // 1. create aesBuffer; + updateViewImplBuffer(); + + // 2. and see, if Alp is within: + bool contains = bufferContainsAlp(); // buffer and alp must exist!!! + DEBUG_LOG(m_logger) << LOGFUNCTION << " buffer contains Alp: " << contains; + + + // Line Afp Alp + Point3D start = this->getCoordinateTranslated(COORD_INDEX_AFP); + Point3D end = this->getCoordinateTranslated(COORD_INDEX_ALP); + if (end.isNull()) + updateViewImplLineAfpAlp(false, Point3D(), Point3D()); + else + updateViewImplLineAfpAlp(true, start, end); + + // Line Alp Aes + start = this->getCoordinateTranslated(COORD_INDEX_ALP); + end = this->getCoordinateTranslated(COORD_INDEX_AES); + + + // try to replace end by intersection with Aes: + Point3D intersection = this->intersectionAesAndLineAlpAes(); + DEBUG_LOG(m_logger) << " intersectionAes " << intersection; + if (!intersection.isNull()) + end = intersection; + + // try to replace end by intersection with buffer of Aes: + if (d->m_aesBuffer) + { + intersection = this->intersectionBufferAndLineAlpAes(); + DEBUG_LOG(m_logger) << " intersectionBuffer " << intersection; + + if (!intersection.isNull()) + end = intersection; + } + + // Maybe 3rd coordinate is not provided? + if (start.isNull() || end.isNull()) + updateViewImplLineAlpAes(false, Point3D(), Point3D()); + else + updateViewImplLineAlpAes(!contains, start, end); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return success; +} + +bool AirSupportImpl::updateViewImplBuffer() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " pixelSize= " << m_mapImpl->pixelSizeMeter(); + + if (m_mapImpl->pixelSizeMeter() < 1) + { + d->removeAesBuffer(); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; + } + + double bufferDistance = m_mapImpl->pixelSizeMeter() * 100; + DEBUG_LOG(m_logger) << LOGFUNCTION << " bufferDistance= " << bufferDistance; + + if (bufferDistance > 2000) + bufferDistance = 2000; + + // AesBuffer of referenced object + if (!isUniqueMapIdValid(d->m_referencedObject)) + { + // This can happen, if the object is just created: + DEBUG_LOG(m_logger) << LOGFUNCTION << " m_referencedObject is invalid" + << " (m_referencedObject=" << d->m_referencedObject << ")"; + return false; + } + + AbstractMapObject *amo = d->m_mapImpl->objectContainer().getObject(d->m_referencedObject); + if (!amo) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " m_referencedObject not found" + << " (m_referencedObject=" << d->m_referencedObject << ")"; + return false; + } + + QVector resultVec = MathUtils::GeometryEngine::buffer(amo, bufferDistance); + + GraphicalObjectPath *buffer = d->getAesBuffer(); + if (buffer) + { + buffer->setVisibility(m_visibility && m_visibilityTechnical); + buffer->setScale(m_mainGraphicalObject->scale()); + buffer->setLineStyle(LineStyle(d->m_color, AIR_SUPPORT_LINE_WIDTH, LinePattern::Solid)); + buffer->setPositions(resultVec); + buffer->setDrawOrder(m_mainGraphicalObject->drawOrder()); + if (!buffer->updateView()) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed"; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +void AirSupportImpl::updateViewImplLineAfpAlp(bool show, const Point3D &start, const Point3D &end) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (show) + { + QSharedPointer line = d->getPathObject(LINE_INDEX_AFP_ALP); + if (line) + { + line->setVisibility(m_visibility && m_visibilityTechnical); + line->setScale(m_mainGraphicalObject->scale()); + line->setDrawOrder(m_mainGraphicalObject->drawOrder()); + line->setLineStyle(LineStyle(d->m_color, AIR_SUPPORT_LINE_WIDTH, LinePattern::Dash)); + PositionCont posCont; + posCont.reserve(2); + posCont.append(start); + posCont.append(end); + line->setPositions(posCont); + if (!line->updateView()) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed"; + } + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + } + else + d->removePathObject(LINE_INDEX_AFP_ALP); +} + +void AirSupportImpl::updateViewImplLineAlpAes(bool show, const Point3D &start, const Point3D &end) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (show) + { + QSharedPointer line = d->getPathObject(LINE_INDEX_ALP_AES); + if (line) + { + line->setVisibility(m_visibility && m_visibilityTechnical); + line->setScale(m_mainGraphicalObject->scale()); + line->setDrawOrder(m_mainGraphicalObject->drawOrder()); + line->setLineStyle(LineStyle(d->m_color, AIR_SUPPORT_LINE_WIDTH, LinePattern::Solid)); + PositionCont posCont; + posCont.reserve(2); + posCont.append(start); + posCont.append(end); + line->setPositions(posCont); + if (!line->updateView()) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed"; + } + } + else + d->removePathObject(LINE_INDEX_ALP_AES); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +bool AirSupportImpl::bufferContainsAlp() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + Esri::ArcGISRuntime::Geometry geoHull; + if (d->m_aesBuffer) + geoHull = d->m_aesBuffer->getGraphicGeometry(); + + Esri::ArcGISRuntime::Geometry pntToCheck; + if (d->getALP()) + pntToCheck = d->getALP()->getGraphicGeometry(); + + bool contains = false; + + if (!geoHull.isEmpty() && !pntToCheck.isEmpty()) + contains = Esri::ArcGISRuntime::GeometryEngine::contains(geoHull, pntToCheck); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, contains" << contains; + return contains; +} + +Point3D AirSupportImpl::intersectionBufferAndLineAlpAes() +{ + Esri::ArcGISRuntime::Geometry geo1; + if (d->m_aesBuffer) + geo1 = d->m_aesBuffer->getGraphicGeometry(); + + if (geo1.isEmpty()) + return Point3D(); + + Esri::ArcGISRuntime::Geometry geo2 = geometryOfLine(getCoordinateTranslated(COORD_INDEX_ALP), + getCoordinateTranslated(COORD_INDEX_AES)); + + if (geo2.isEmpty()) + return Point3D(); + + return intersection(geo1, geo2); +} + +Point3D AirSupportImpl::intersectionAesAndLineAlpAes() +{ + AbstractMapObject *amo = d->m_mapImpl->objectContainer().getObject(d->m_referencedObject); + AbstractMapObjectImpl *mapObjImpl = dynamic_cast(amo); + + if (!mapObjImpl) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " m_referencedObject not found/dynamic_cast failed" + << " (m_referencedObject=" << d->m_referencedObject << ")"; + return Point3D(); + } + + Esri::ArcGISRuntime::Geometry geo1; + if (mapObjImpl) + geo1 = mapObjImpl->getEsriGeometry(); + + if (geo1.isEmpty()) + return Point3D(); + + Esri::ArcGISRuntime::Geometry geo2 = geometryOfLine(getCoordinateTranslated(COORD_INDEX_ALP), + getCoordinateTranslated(COORD_INDEX_AES)); + + if (geo2.isEmpty()) + return Point3D(); + + return intersection(geo1, geo2); +} + +Point3D AirSupportImpl::intersection(const Esri::ArcGISRuntime::Geometry &geo1, const Esri::ArcGISRuntime::Geometry &geo2) +{ + if (geo1.isEmpty() || geo2.isEmpty()) + return Point3D(); + + Esri::ArcGISRuntime::Geometry geoResult = + Esri::ArcGISRuntime::GeometryEngine::intersection(geo1, geo2); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " geoInter = " << geoResult.toJson(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " geoInter dimension = " << geoResult.dimension(); + + Esri::ArcGISRuntime::Point intersectPoint; + + if (geoResult.dimension() == 1) // dim 1 -> Lines and Polyline + { + Esri::ArcGISRuntime::Polyline polylineResult(geoResult); + if (polylineResult.parts().part(0).pointCount() > 0) + intersectPoint = polylineResult.parts().part(0).startPoint(); + } + else if (geoResult.dimension() == 0) + { + intersectPoint = Esri::ArcGISRuntime::Point(geoResult); // This is not tested yet + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " intersectPoint = " << intersectPoint.toJson(); + + if (std::isnan(intersectPoint.x()) || + std::isnan(intersectPoint.y()) || + std::isnan(intersectPoint.z())) + return Point3D(); + + Point3D intersection(intersectPoint.x(), + intersectPoint.y(), + intersectPoint.z()); + return intersection; +} + +Esri::ArcGISRuntime::Geometry AirSupportImpl::geometryOfLine(const Point3D &start, const Point3D &end) +{ + Esri::ArcGISRuntime::PolylineBuilder polylineBuilder(m_mapImpl->getEsriSpatialReference()); + polylineBuilder.addPoint(Esri::ArcGISRuntime::Point(start.x(), start.y())); + polylineBuilder.addPoint(Esri::ArcGISRuntime::Point(end.x(), end.y())); + + const Esri::ArcGISRuntime::Geometry &geo = polylineBuilder.toGeometry(); + return geo; +} + +bool AirSupportImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + // no symbol required + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polyline); + + setLineStyle(LineStyle(Qt::black, 1)); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + // set some values to Private: + d->m_mapImpl = m_mapImpl; + d->m_debugInfo = m_debugInfo; + d->m_uniqueMapId = m_uniqueMapId; + d->m_graphicsLayer = m_graphicsLayer; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +Point3D AirSupportImpl::getCoordinateTranslated(int idx) const +{ + if (idx < 0 || idx > 2) + return Point3D(); + + const PositionCont &posContTranslated = m_positions.positionsTranslated(); + if (idx >= posContTranslated.size()) + return Point3D(); + + return posContTranslated[idx]; +} + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/air_support_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/air_support_impl.h new file mode 100755 index 000000000..62bb89fd0 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/air_support_impl.h @@ -0,0 +1,119 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of AirSupport + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + + +// Qt +#include + +/* + o AFP (Anflugpunkt) + \ + \ + o ALP (Ausloesepunkt) + | + | + | + ------- + | | AesItem / Buffer (Waffeneinsatzflaeche) + ------- +*/ + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +// forward: +class ParticipantImpl; + + +/** + * @brief The AirSupportImpl class displays (guez) air support, aka AerialOrdenance + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath displaying the main techn. Appearance) + * Line from 'Anflugpunkt' (AFP) over 'Ausloesepunkt' (ALP) + * to target (center of EffectSimulation) + * - mainGraphicalObjectText (displaying the text/name) + * - tacticalAppearance (displaying the tact. Appearance) + * + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT AirSupportImpl : public BasicPathObjectImpl, public virtual AirSupport +{ + using BaseClass = BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit AirSupportImpl(const QString &debugInfo = QString()); + virtual ~AirSupportImpl(); + + void setUniqueId(const UniqueMapId &uniqueId) override; + +public: + AirSupportImpl(const AirSupportImpl &other) = delete; + AirSupportImpl &operator=(AirSupportImpl &other) = delete; + + void setReferencedObject(const UniqueMapId &referencedObject) override; + +protected: + bool updateViewImpl() override; + + bool onMapExtentChanged() override; + + bool updateViewImplBuffer(); + void updateViewImplLineAfpAlp(bool show, const Point3D &start, const Point3D &end); + void updateViewImplLineAlpAes(bool show, const Point3D &start, const Point3D &end); + + bool bufferContainsAlp(); + Point3D intersectionBufferAndLineAlpAes(); + Point3D intersectionAesAndLineAlpAes(); + Point3D intersection(const Esri::ArcGISRuntime::Geometry &geo1, const Esri::ArcGISRuntime::Geometry &geo2); + Esri::ArcGISRuntime::Geometry geometryOfLine(const Point3D &start, const Point3D &end); + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + static const int COORD_INDEX_AFP = 0; // Anflugpunkt + static const int COORD_INDEX_ALP = 1; // Ausloesepunkt + static const int COORD_INDEX_AES = 2; // target / EffectSimulation + + static const int LINE_INDEX_AFP_ALP = 0; // Anflugpunkt + static const int LINE_INDEX_ALP_AES = 1; // Ausloesepunkt + + Point3D getCoordinateTranslated(int idx) const; + + // logical + + +private: + + // logical + 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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/area_fire_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/area_fire_impl.cpp new file mode 100755 index 000000000..8ef0d778e --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/area_fire_impl.cpp @@ -0,0 +1,820 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of AreaFire + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "area_fire_impl.h" +#include +#include +#include +#include +#include "single_fire_impl.h" + +#include + +// ESRI +#ifdef LTS_DEBUG +#include +#include +#endif // LTS_DEBUG + +// Qt + + +//#define USE_MARKER + + +namespace SituationMap +{ + +static int s_gunToTargetLine3DKEY = -1; +static int s_gunToTargetLineSurfaceKEY = 0; +static int s_nonLethalAreaKEY = 1; + + +class AreaFireImpl::Private +{ +public: + Private() = default; + ~Private() = default; + + // ESRI data: +// Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr; + + + // Own data: + MapImpl *m_mapImpl = nullptr; + QString m_logicalLayer; + QString m_debugInfo; // will be appended by consecutive number + UniqueMapId m_uniqueMapId = INVALID_MAP_ID; + + SimpleValue m_scheduledImpactsExplcitColor; // by default invalid color + + struct Detonations + { + PositionsTranslated m_detonationPositions; + SimpleValue m_detonationRadius{5}; + bool m_detonationChanged{true}; + + bool changed() const { return m_detonationRadius.changed() || m_detonationChanged; } + void reset() { m_detonationRadius.reset(); m_detonationChanged = false; } + + + + } m_detonationsAbsolute, m_detonationsDraped; + + // + // ABSOLUTE using SceneMarker + // + + struct ValueDataGOSM_Abs + { + QSharedPointer m_goSceneMarker; + }; + QVector m_cont_VDGOSM_Abs; + + void resizeGOSM_Abs(int newSize) { m_cont_VDGOSM_Abs.resize(newSize); } + + ValueDataGOSM_Abs& getObjectGOSM_abs(int idx) + { + ValueDataGOSM_Abs &retRef = m_cont_VDGOSM_Abs[idx]; + if (retRef.m_goSceneMarker.isNull()) + { + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = m_mapImpl->getGraphicsLayer3D_SpAbsolute(m_logicalLayer); + if (!graphicsLayer) + { + qCritical() << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " getGraphicsLayer3D_SpAbsolute failed"; + } + + QString debugInfo = QStringLiteral("%1#ModelSceneMarkerAbs(%2)").arg(m_debugInfo).arg(idx); + GraphicalObjectSceneMarker *temp = new GraphicalObjectSceneMarker(SituationMap::SceneMarkerType::SPHERE, + m_mapImpl->getEsriGeoView(), + graphicsLayer, + debugInfo, + m_uniqueMapId); + temp->setUniqueMapId(m_uniqueMapId); + + retRef.m_goSceneMarker = QSharedPointer(temp); + } + return retRef; + } + + // + // DRAPED a) using renderer approach + // +#ifdef AREA_FIRE_DRAPED_RENDERER // GUEZE_2933 + struct ValueDataGORA_Drpd + { + QSharedPointer m_goRendererAtt; + }; + + QVector m_cont_GORA_drpd; + void resizeDraped_GORA(int newSize) + { + m_cont_GORA_drpd.resize(newSize); + } + + ValueDataGORA_Drpd& getObjectDraped_GORA(int idx, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer) + { + Q_ASSERT(graphicsLayer); + + ValueDataGORA_Drpd &retRef = m_cont_GORA_drpd[idx]; + if (retRef.m_goRendererAtt.isNull()) + { + QString debugInfo = QStringLiteral("%1#Drpd_goRendererAtt_%2").arg(m_debugInfo).arg(idx); + retRef.m_goRendererAtt = QSharedPointer + (new GraphicalObjectRendererAttributes(m_mapImpl->getEsriGeoView() + , graphicsLayer + , debugInfo + , m_uniqueMapId) + ); + retRef.m_goRendererAtt->setUniqueMapId(m_uniqueMapId); + } + + return retRef; + } + + static QVariantMap createGORA_AttributeMap(const QColor &majorColor, double radius) + { + QVariantMap attribMap; + QString attribValue; + { + attribValue = QString(QStringLiteral("Impact_Color_%1")).arg(majorColor.name()); + attribMap.insert(QStringLiteral("IMPACT_COLOR"), attribValue); + } + { + const double radiusWithMin = std::max(radius, 1.0); // at least 1 meter + attribValue = QString(QStringLiteral("Impact_Radius_%1")).arg(radiusWithMin); + attribMap.insert(QStringLiteral("IMPACT_RADIUS"), attribValue); + } + { + LayerFactory::UniqueValueDataAesCentral::TYPE type{LayerFactory::UniqueValueDataAesCentral::TYPE::SPHERE}; + attribValue = QString(QStringLiteral("Impact_Type_%1")).arg(LayerFactory::toString(type)); + attribMap.insert(QStringLiteral("IMPACT_TYPE"), attribValue); + } +// { +// // SceneProperties: HEADING, PITCH, ROLL +// int heading = 0; +// if (data.rotationAngle().is_set()) +// heading = static_cast(data.rotationAngle().get()); +// attribMap.insert(QStringLiteral("HEADING"), ((heading+90)%360)); // data.rotationAngle() +// attribMap.insert(QStringLiteral("PITCH"), 0); +// attribMap.insert(QStringLiteral("ROLL"), 90); +// } + +// qCritical() << " ident " << ", " << attribMap; + return attribMap; + } +#else + // + // DRAPED b) using SceneMarker (see also GUEZE_2933) + // + struct ValueDataGOSM_Drpd + { + QSharedPointer m_goSceneMarker; + }; + QVector m_cont_GOSM_drpd; + void resizeDraped_GOSM(int newSize) + { + m_cont_GOSM_drpd.resize(newSize); + } + + ValueDataGOSM_Drpd& getObjectDraped_GOSM(int idx, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer) + { + Q_ASSERT(graphicsLayer); + + ValueDataGOSM_Drpd &retRef = m_cont_GOSM_drpd[idx]; + if (retRef.m_goSceneMarker.isNull()) + { + QString debugInfo = QStringLiteral("%1#Drpd_GOSM_%2").arg(m_debugInfo).arg(idx); + GraphicalObjectSceneMarker *temp = new GraphicalObjectSceneMarker(SituationMap::SceneMarkerType::SPHERE, + m_mapImpl->getEsriGeoView(), + graphicsLayer, + debugInfo, + m_uniqueMapId); + temp->setUniqueMapId(m_uniqueMapId); + + retRef.m_goSceneMarker = QSharedPointer(temp); + } + return retRef; + } +#endif // AREA_FIRE_DRAPED_RENDERER + + + +}; // class AreaFireImpl::Private +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// class AreaFireImpl +//----------------------------------------------------------------------------- +AreaFireImpl::AreaFireImpl(const QString &debugInfo) + : AreaFire() // Interface + , BaseClass(debugInfo) // Implementation + , d(new Private) + , m_logger(getLogger("AreaFireImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo=" << m_debugInfo; + m_fillColorAlpha = 200; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo=" << m_debugInfo; +} + +AreaFireImpl::~AreaFireImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo=" << m_debugInfo; + delete d; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo=" << m_debugInfo; +} + +void AreaFireImpl::setUniqueId(const UniqueMapId &uniqueId) +{ + BaseClass::setUniqueId(uniqueId); + d->m_uniqueMapId = m_uniqueMapId; +} + +PositionCont AreaFireImpl::scheduledImpacts() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return this->rIndicationContainer().positions(1); +} +void AreaFireImpl::setScheduledImpacts(const PositionCont &posCont, const QColor &explicitColor) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " scheduledImpactsSize=" << posCont.size() + << " explicitColor=" << explicitColor + ; + + IndicationContainer &drapedImpactsIC = this->rIndicationContainer(); + drapedImpactsIC.setDebugInfo(m_debugInfo + QStringLiteral("DefaultSpDraped")); + drapedImpactsIC.setPositions(1, posCont); + + d->m_scheduledImpactsExplcitColor = explicitColor; +} + +PositionCont AreaFireImpl::impacts() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return this->rIndicationContainer().positions(0); +} +void AreaFireImpl::setImpacts(const PositionCont &posCont) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << ", impactSize=" << posCont.size(); + + IndicationContainer &drapedImpactsIC = this->rIndicationContainer(); + + drapedImpactsIC.setDebugInfo(m_debugInfo + QStringLiteral("DefaultSpDraped")); + drapedImpactsIC.setPositions(0, posCont); + +#ifdef USE_MARKER + drapedImpactsIC.setMarker(0, MarkerSymbolType::X, 20, partyColor()); +#endif + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +PositionCont AreaFireImpl::impactsAbsoluteHeight() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return this->rIndicationContainerAbsolute().positions(0); +} + +void AreaFireImpl::setImpactsAbsoluteHeight(const PositionCont &posCont) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " impactSize=" << posCont.size() + << " lineStyle().lineColor()=" << lineStyle().lineColor(); + + IndicationContainer &impactsAbsolute = this->rIndicationContainerAbsolute(); + + impactsAbsolute.setDebugInfo(m_debugInfo + QStringLiteral("SpAbso")); + impactsAbsolute.setPositions(0, posCont); + +#ifdef USE_MARKER + impacts.setMarker(0, MarkerSymbolType::X, 20, partyColor()); +#endif + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +PositionCont AreaFireImpl::detonationsAbsolute() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return d->m_detonationsAbsolute.m_detonationPositions.positions(); +} + +bool AreaFireImpl::setDetonationsAbsolute(const PositionCont &detonationsPositions, int detonationRadius) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << ", set detonationsPositions = " << detonationsPositions + << ", radius=" << detonationRadius; + bool bHasChanged = d->m_detonationsAbsolute.m_detonationPositions.setPositions(detonationsPositions); + d->m_detonationsAbsolute.m_detonationRadius = detonationRadius; + d->m_detonationsAbsolute.m_detonationChanged = d->m_detonationsAbsolute.m_detonationChanged || bHasChanged; // Do not reset of changed==true; (maybe it was already changed) + TRACE_LOG(m_logger) << LOGFUNCTION << " END" << ", bHasChanged=" << d->m_detonationsAbsolute.changed(); + return d->m_detonationsAbsolute.changed(); +} + +PositionCont AreaFireImpl::detonationsDraped() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return d->m_detonationsDraped.m_detonationPositions.positions(); +} + +bool AreaFireImpl::setDetonationsDraped(const PositionCont &detonationsPositions, int detonationRadius) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << ", set detonationsPositions = " << detonationsPositions + << ", radius=" << detonationRadius; + bool bHasChanged = d->m_detonationsDraped.m_detonationPositions.setPositions(detonationsPositions); + d->m_detonationsDraped.m_detonationRadius = detonationRadius; + d->m_detonationsDraped.m_detonationChanged = d->m_detonationsDraped.m_detonationChanged || bHasChanged; // Do not reset of changed==true; (maybe it was already changed) + TRACE_LOG(m_logger) << LOGFUNCTION << " END" << ", bHasChanged=" << d->m_detonationsDraped.changed(); + return d->m_detonationsDraped.changed(); +} + +void AreaFireImpl::setGunToTargetLineSurface(const Point3D &start, const Point3D &end) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << ", start=" << start << ", end=" << end << ", debugInfo=" << m_debugInfo; + + PathContainer &path = this->rPathContainer(); // should be SP::Draped + path.setDebugInfo(m_debugInfo + QStringLiteral("SpDraped")); + + if (start.isNull() && end.isNull()) + { + path.clear(s_gunToTargetLineSurfaceKEY); // 0 + } else { + LineStyle ls = m_mainGraphicalObject->lineStyle(); + ls.setLinePattern(LinePattern::Dot); + ls.setLineWidth(2); + path.setLineStyle(ls); + path.setPath(s_gunToTargetLineSurfaceKEY, {start, end}); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << ", start=" << start << ", end=" << end << ", debugInfo=" << m_debugInfo; +} + +void AreaFireImpl::setGunToTargetTrajectory3D(const PositionCont &gunToTargetPath) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << ", gunToTargetPath=" << gunToTargetPath.size() + << ", debugInfo=" << m_debugInfo; + + PathContainer &path3D_Abs = this->rPathContainerSpAbsolute2(); // should be SP::Absolute + path3D_Abs.setDebugInfo(m_debugInfo + QStringLiteral("Absolute")); + path3D_Abs.setDrawOrder(5); + + if (gunToTargetPath.isEmpty()) + { + path3D_Abs.clear(s_gunToTargetLine3DKEY); // -1 + } else { + LineStyle ls = m_mainGraphicalObject->lineStyle(); + + // Try to reduce z-Fighting between gunToTarget and trajectory. + // by reducing the opacity (GUEZE-2722) + QColor lCol = ls.lineColor(); + lCol.setAlphaF(0.99); + ls.setLineColor(lCol); + + ls.setLinePattern(LinePattern::Dot); + ls.setLineWidth(1); + path3D_Abs.setLineStyle(ls); + path3D_Abs.setPath(s_gunToTargetLine3DKEY, gunToTargetPath); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << ", gunToTargetPath=" << gunToTargetPath.size() + << ", debugInfo=" << m_debugInfo; +} + +void AreaFireImpl::setNonLethalArea(const PositionCont &nonLethalArea) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << ", nonLethalArea=" << nonLethalArea.size() + << ", debugInfo=" << m_debugInfo; + + // avoid creating PathContainer in case of non-existing data: + if (nonLethalArea.size() < 3) + { + clearPathContainerSpDrapedPolygon(s_nonLethalAreaKEY); + return; + } + + PathContainer &path = this->rPathContainerSpDrapedPolygon(); + path.setDebugInfo(m_debugInfo + QStringLiteral("SpDrapedNonLethalArea")); + + // LineStyle ls = m_mainGraphicalObject->lineStyle(); + // ls.setLineColor(partyColor()); + // ls.setLinePattern(LinePattern::Solid); + // ls.setLineWidth(2); + // path.setLineStyle(ls); + + // const FillStyle &fs = m_mainGraphicalObject->fillStyle(); + // path.setFillStyle(fs); + + path.setPath(s_nonLethalAreaKEY, nonLethalArea); // 1 + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << ", nonLethalArea=" << nonLethalArea.size() + << ", debugInfo=" << m_debugInfo; +} + +PositionCont AreaFireImpl::trajectory(int index) const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << ", index=" << index; + TRACE_LOG(m_logger) << LOGFUNCTION << " END" << ", index=" << index; + return this->rPathContainerSpAbsolute1().path(index); +} + +bool AreaFireImpl::setTrajectory(int index, const PositionCont &trajectoryPath) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " index=" << index + << ", trajectoryPath.size=" << trajectoryPath.size() + << ", lineStyle().lineColor()=" << lineStyle().lineColor(); + + PathContainer &path = this->rPathContainerSpAbsolute1(); // Trajectory -> Absolute + path.setDebugInfo(m_debugInfo + QStringLiteral("Draped")); + path.setDrawOrder(6); // 'above' guntToTargetLine/guntToTargetCurve + LineStyle ls = this->lineStyle(); + ls.setLinePattern(LinePattern::Solid); + ls.setLineWidth(2); + path.setLineStyle(ls); + return path.setPath(index, trajectoryPath); +} + + +bool AreaFireImpl::updateViewImpl() +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " START" + << ", m_debugInfo=" << m_debugInfo + << ", visibility=" << m_visibility + << ", visiTechn=" << m_visibilityTechnical + << ", visiTact=" << m_visibilityTactical + // IndicationContainer &drapedImpactsIC = this->rIndicationContainer(); + // IndicationContainer &impactsAbsolute = this->rIndicationContainerAbsolute(); + << ", d->m_detonationsAbsolute.changed() = " << d->m_detonationsAbsolute.changed() + << ", d->m_detonationsAbsolute.size() = " << d->m_detonationsAbsolute.m_detonationPositions.size() + << ", d->m_detonationsDraped.changed() = " << d->m_detonationsDraped.changed() + << ", d->m_detonationsDraped.size() = " << d->m_detonationsDraped.m_detonationPositions.size(); + + for (IndicationContainer *indCont : {m_indicationCont, m_indicationContSpAbsolute}) + { + if (!indCont) + continue; + + if (m_mainGraphicalObject) + { +#ifdef USE_MARKER + indCont->setMarker(0, col); +#else + if (indCont->image(0).isNull() || m_party.partyId.changed()) + { + const QColor &col = m_mainGraphicalObject->lineStyle().lineColor(); + indCont->setImage(0, SingleFireImpl::renderImpactSymbol(col, 20, true)); // filled for impacts + } + if (indCont->image(1).isNull() || m_party.partyId.changed() || d->m_scheduledImpactsExplcitColor.changed()) + { + d->m_scheduledImpactsExplcitColor.reset(); + QColor col = d->m_scheduledImpactsExplcitColor.value(); + if (!col.isValid()) + col = m_mainGraphicalObject->lineStyle().lineColor(); + indCont->setImage(1, SingleFireImpl::renderImpactSymbol(col, 20, false)); // open for scheduledImpacts + } +#endif + } + + indCont->setSelected(0, m_selected); + indCont->setPickable(0, m_pickable); + indCont->setVisible(0, m_visibility); + indCont->setSelected(1, m_selected); + indCont->setPickable(1, m_pickable); + indCont->setVisible(1, m_visibility); + } // for (IndicationContainer *indCont... + + // control selection + if (m_pathCont_SpDraped) + { + m_pathCont_SpDraped->setSelected(m_selected); + // m_pathCont_SpDraped->setPickable(0, m_pickable); + m_pathCont_SpDraped->setVisibility(s_gunToTargetLineSurfaceKEY, m_visibility); // 0 -> GunToTargetLineSurface + } + if (m_pathCont_SpDrapedPolygon) + { + m_pathCont_SpDrapedPolygon->setSelected(m_selected); + // m_pathCont->setPickable(0, m_pickable); + m_pathCont_SpDrapedPolygon->setVisibility(s_nonLethalAreaKEY, m_visibility); + + LineStyle ls = m_mainGraphicalObject->lineStyle(); + ls.setLinePattern(LinePattern::Solid); + ls.setLineWidth(2); + m_pathCont_SpDrapedPolygon->setLineStyle(ls); + + FillStyle fs = m_mainGraphicalObject->fillStyle(); + m_pathCont_SpDrapedPolygon->setFillStyle(fs); + } + + if (m_pathCont_SpAbsolute1) + { + const auto &keys = m_pathCont_SpAbsolute1->getIndicationIndexList(); + for (auto keyIndex : std::as_const(keys)) + m_pathCont_SpAbsolute1->setVisibility(keyIndex, m_visibility); // trajectories + + m_pathCont_SpAbsolute1->setSelected(m_selected); + // m_pathCont->setPickable(0, m_pickable); + } + if (m_pathCont_SpAbsolute2) + { + m_pathCont_SpAbsolute2->setSelected(m_selected); + // m_pathCont->setPickable(0, m_pickable); + m_pathCont_SpAbsolute2->setVisibility(s_gunToTargetLine3DKEY, m_visibility); // -1 -> GunToTargetLine3D + } + + const bool mgoVisiOld = m_mainGraphicalObject->visibility(); + + bool success = BaseClass::updateViewImpl(); + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + // Detonations (3D) + bool successDetonationsAbs = true; + + if ( (m_mapImpl->mode() == Mode::Mode_3D) && + (d->m_detonationsAbsolute.changed() || (mgoVisiOld != m_mainGraphicalObject->visibility())) + ) + { + const PositionCont &positionsTranslated = d->m_detonationsAbsolute.m_detonationPositions.positionsTranslated(); + const int newSize = positionsTranslated.size(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " resizeGOSM_Abs" << d->m_cont_VDGOSM_Abs.size() << " to " << newSize; + + d->resizeGOSM_Abs(newSize); + + int idx = 0; + for(const Point3D &point3DTranslated : std::as_const(positionsTranslated)) + { + Private::ValueDataGOSM_Abs &vdGOSM_absRef = d->getObjectGOSM_abs(idx); + QSharedPointer goSceneMarker = vdGOSM_absRef.m_goSceneMarker; + + goSceneMarker->setOrigin(point3DTranslated); + goSceneMarker->setDiameter(d->m_detonationsAbsolute.m_detonationRadius.value() * 2); + goSceneMarker->setPickable(false); + goSceneMarker->setSelected(false); + + QColor col = partyColor(); + col.setAlphaF(col.alphaF() / 2.0); + goSceneMarker->setFillStyle(FillStyle(col, FillPattern::Solid)); + goSceneMarker->setVisibility(m_visibility); + + if (!goSceneMarker->updateView()) + successDetonationsAbs = false; + ++idx; + } // for + + if (!successDetonationsAbs) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateView m_goSceneMarker failed, m_debugInfo = " << m_debugInfo; + + d->m_detonationsAbsolute.reset(); + } else { + // To simplifiy debugging: + d->m_detonationsAbsolute.reset(); + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " START" + << ", m_debugInfo=" << m_debugInfo + << ", d->m_detonationsDraped.changed = " << d->m_detonationsDraped.changed(); + + + bool successDetonationsDrpd = true; + if ( (m_mapImpl->mode() == Mode::Mode_3D) && + (d->m_detonationsDraped.changed() || (mgoVisiOld != m_mainGraphicalObject->visibility())) ) + { + +#ifdef AREA_FIRE_DRAPED_RENDERER + successDetonationsDrpd = updateViewImplDetonation3dDraped_RendererApproach(); +#else + successDetonationsDrpd = updateViewImplDetonation3dDraped_SceneMarkerApproach(); +#endif // AREA_FIRE_DRAPED_RENDERER + + } else { + // to simplify debugging: + d->m_detonationsDraped.reset(); + } + + success = success && successDetonationsAbs && successDetonationsDrpd; + + +#ifdef LTS_DEBUG + const bool afVisi = m_visibility; + const bool mgoVisi = m_mainGraphicalObject->visibility(); + const bool esriGraphicVisi = m_mainGraphicalObject->graphic()->isVisible(); + // REGG-5360 - Wenn man beim Beobachten der Live-Lage über den Wirksimulationsfilter alle gepla.. + // ... sind Flächenfeuer entgegen dem Filter sichtbar + + if ( (afVisi != mgoVisi) || (afVisi != esriGraphicVisi) ) + { + for (int i = 0; i < 100; ++i) + FATAL_LOG(m_logger) << LOGFUNCTION << " INKONSISTENCY!!!!!!!!!!!!!!"; + + FATAL_LOG(m_logger) << LOGFUNCTION << " INKONSISTENCY!!!!!!!!!!!!!!" + << ", afVisi=" << afVisi + << ", mgoVisi=" << mgoVisi + << ", esriGraphicVisi" << esriGraphicVisi; + + QMessageBox::critical(nullptr, + QLatin1String("INKONSISTENCY"), + QLatin1String("INKONSISTENCY, corefile wil be created ; check logfile regarding REGG-5360..")); + abort(); + + } +#endif // LTS_DEBUG + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; + +} + +#ifdef AREA_FIRE_DRAPED_RENDERER +bool AreaFireImpl::updateViewImplDetonation3dDraped_RendererApproach() // GUEZE_2933 +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " START" + << ", m_debugInfo=" << m_debugInfo + << ", visibility=" << m_visibility + << ", visiTechn=" << m_visibilityTechnical + << ", visiTact=" << m_visibilityTactical + << ", d->m_detonationsDraped.changed() = " << d->m_detonationsDraped.changed() + << ", d->m_detonationsDraped.size() = " << d->m_detonationsDraped.m_detonationPositions.size(); + + bool successDetonationsDrpd = true; + if ((m_mapImpl->mode() == Mode::Mode_3D) && d->m_detonationsDraped.changed()) + { + const PositionCont &positionsTranslated_drpdTrans = d->m_detonationsDraped.m_detonationPositions.positionsTranslated(); + const int newSize = positionsTranslated_drpdTrans.size(); + d->resizeDraped_GORA(newSize); + + int idx = 0; + for(const Point3D &point3DTranslated : std::as_const(positionsTranslated_drpdTrans)) + { + Private::ValueDataGORA_Drpd &vdGORA_drpdRef = d->getObjectDraped_GORA(idx, m_graphicsLayer); + QSharedPointer goRendererAtt = vdGORA_drpdRef.m_goRendererAtt; + + goRendererAtt->setOrigin(point3DTranslated); + + const QVariantMap &attribMap = d->createGORA_AttributeMap(m_mainGraphicalObject->lineStyle().lineColor(), d->m_detonationsDraped.m_detonationRadius.value()); + + if (m_logger.isDebugEnabled()) + qCritical() << LOGFUNCTION << " START" + << ", m_debugInfo=" << m_debugInfo + << ", attribMap = " << attribMap + << ", point3DTranslated = " << point3DTranslated.x() << " / " << point3DTranslated.y() << " / " << point3DTranslated.z() + << ", m_visibility = " << m_visibility; + + + goRendererAtt->setAttributeMap(attribMap); + goRendererAtt->setVisibility(m_visibility); + goRendererAtt->setPickable(false); + goRendererAtt->setSelected(false); + + if (!goRendererAtt->updateView()) + successDetonationsDrpd = false; + ++idx; + } // for + if (!successDetonationsDrpd) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl m_goRendererAtt failed, m_debugInfo=" << m_debugInfo; + + d->m_detonationsDraped.reset(); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, successDetonationsDrpd=" << successDetonationsDrpd; + return successDetonationsDrpd; +} + +#else // AREA_FIRE_DRAPED_RENDERER // GUEZE_2933 + +bool AreaFireImpl::updateViewImplDetonation3dDraped_SceneMarkerApproach() // GUEZE_2933 +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " START" + << ", m_debugInfo=" << m_debugInfo + << ", visibility=" << m_visibility + << ", visiTechn=" << m_visibilityTechnical + << ", visiTact=" << m_visibilityTactical + << ", d->m_detonationsDraped.changed() = " << d->m_detonationsDraped.changed() + << ", d->m_detonationsDraped.size() = " << d->m_detonationsDraped.m_detonationPositions.size(); + + bool successDetonationsDrpd = true; + if ((m_mapImpl->mode() == Mode::Mode_3D) && d->m_detonationsDraped.changed()) + { + const PositionCont &positionsTranslated_drpdTrans = d->m_detonationsDraped.m_detonationPositions.positionsTranslated(); + const int newSize = positionsTranslated_drpdTrans.size(); + d->resizeDraped_GOSM(newSize); + + int idx = 0; + for(const Point3D &point3DTranslated : std::as_const(positionsTranslated_drpdTrans)) + { + Private::ValueDataGOSM_Drpd &GOSM_drpdRef = d->getObjectDraped_GOSM(idx, m_graphicsLayer); + + QSharedPointer goSceneMarker = GOSM_drpdRef.m_goSceneMarker; + + goSceneMarker->setOrigin(point3DTranslated); + goSceneMarker->setDiameter(d->m_detonationsDraped.m_detonationRadius.value() * 2); + goSceneMarker->setPickable(false); + goSceneMarker->setSelected(false); + + QColor col = partyColor(); + col.setAlphaF(col.alphaF() / 2.0); + goSceneMarker->setFillStyle(FillStyle(col, FillPattern::Solid)); + goSceneMarker->setVisibility(m_visibility); + + if (!goSceneMarker->updateView()) + successDetonationsDrpd = false; + ++idx; + } // for + if (!successDetonationsDrpd) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl GOSM_drpdRef failed, m_debugInfo=" << m_debugInfo; + + d->m_detonationsDraped.reset(); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, successDetonationsDrpd=" << successDetonationsDrpd; + return successDetonationsDrpd; +} +#endif // AREA_FIRE_DRAPED_RENDERER + + + +bool AreaFireImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (mapImpl) + { + switch (mapImpl->symbologyType()) + { + case AdvancedSymbology::SymbologyType::APP6D: + case AdvancedSymbology::SymbologyType::MIL2525D: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "10012500002408010000"); // SymbolSet = 25 240801 + break; + case AdvancedSymbology::SymbologyType::MIL2525C: + // m_symbol.refValue().m_sidc = QLatin1String("GHFPATB---"); // GHFPATB--- 0879, Area Target Bomb + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GHFPATS---"); // GHFPATB--- 0879, Area Target Smoke + break; + case AdvancedSymbology::SymbologyType::APP6B: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GHCPFLKS--"); // APP6B: 3965: GHCPFLKS-- -> Polygon + break; + case AdvancedSymbology::SymbologyType::None: + // empty + break; + } // switch + } // if (mapImpl) + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polygon); + + setLineStyle(LineStyle(Qt::black, 2)); + + // setup Private: + d->m_mapImpl = mapImpl; + d->m_logicalLayer = logicalLayerName; + d->m_debugInfo = this->debugInfo(); + d->m_detonationsAbsolute.m_detonationPositions.setMapObjectImpl(this); + d->m_detonationsDraped.m_detonationPositions.setMapObjectImpl(this); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool AreaFireImpl::onMapExtentChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + for (auto item : {m_indicationCont, m_indicationContSpAbsolute}) + { + const bool visiScale = m_visibilityScaleRange.scaleHasVisibility(m_mapImpl->scale()); + if (item) + { + item->setVisible(0, m_visibility && m_visibilityTechnical && visiScale); + item->setVisible(1, m_visibility && m_visibilityTechnical && visiScale); + } + } + + bool success = BaseClass::onMapExtentChanged(); + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/area_fire_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/area_fire_impl.h new file mode 100755 index 000000000..e68aa4f93 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/area_fire_impl.h @@ -0,0 +1,108 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of AreaFire + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + +// Qt +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +// GUEZE_2933 +// // #define AREA_FIRE_DRAPED_RENDERER + + +/** + * @brief The AreaFireImpl class displays a area of impacts. + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath displaying the main techn. Appearance) + * - mainGraphicalObjectText (displaying the text/name) + * - tacticalAppearance (displaying the tact. Appearance) + * - an infinite number indication objects for displaying impacts (inherented from BasicPathObjectImpl) + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT AreaFireImpl : public BasicPathObjectImpl, public virtual AreaFire +{ + using BaseClass = BasicPathObjectImpl; + +public: + explicit AreaFireImpl(const QString &debugInfo = QString()); + virtual ~AreaFireImpl(); + + AreaFireImpl(const AreaFireImpl &other) = delete; + AreaFireImpl &operator=(AreaFireImpl &other) = delete; + + void setUniqueId(const UniqueMapId &uniqueId) override; + + PositionCont scheduledImpacts() const override; + void setScheduledImpacts(const PositionCont &posCont, const QColor &explicitColor = QColor()) override; + + PositionCont impacts() const override; + void setImpacts(const PositionCont &posCont) override; + + PositionCont impactsAbsoluteHeight() const override; + void setImpactsAbsoluteHeight(const PositionCont &posCont) override; + + + PositionCont detonationsAbsolute() const override; + bool setDetonationsAbsolute(const PositionCont &detonationsPositions, int detonationRadius) override; + + PositionCont detonationsDraped() const override; + bool setDetonationsDraped(const PositionCont &detonationsPositions, int detonationRadius) override; + + void setGunToTargetLineSurface(const Point3D &start, const Point3D &end) override; + void setGunToTargetTrajectory3D(const PositionCont &gunToTargetPath) override; + + void setNonLethalArea(const PositionCont &nonLethalArea) override; + + PositionCont trajectory(int index) const override; + bool setTrajectory(int index, const PositionCont &trajectoryPath) override; + +protected: + virtual bool updateViewImpl() override; + +#ifdef AREA_FIRE_DRAPED_RENDERER + virtual bool updateViewImplDetonation3dDraped_RendererApproach(); +#else + virtual bool updateViewImplDetonation3dDraped_SceneMarkerApproach(); +#endif // AREA_FIRE_DRAPED_RENDERER + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + bool onMapExtentChanged() override; + + + // internal/admin + + // logical + class Private; + /** + * The private d->pointer to not break BC when changing internals of this class + */ + Private *d = nullptr; + + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/basic_path_object_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/basic_path_object_impl.cpp new file mode 100755 index 000000000..aed0b9bcc --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/basic_path_object_impl.cpp @@ -0,0 +1,1112 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of BasicPathObjectImpl + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "basic_path_object_impl.h" +#include "indication_container.h" +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include + + +// ESRI +#include +#include + + +// Qt +#include +#include +#include +#include + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class BasicPathObjectImpl +//----------------------------------------------------------------------------- +BasicPathObjectImpl::BasicPathObjectImpl(const QString &debugInfo) + : BaseClass(debugInfo) // Implementation + , AbstractEffectSimulationImpl() // Secondary implementation + , m_logger(getLogger("BasicPathObjectImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo=" << m_debugInfo << "'"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo=" << m_debugInfo << "'"; +} + +BasicPathObjectImpl::~BasicPathObjectImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo=" << m_debugInfo << "'"; + delete m_indicationCont; + delete m_indicationContSpAbsolute; + delete m_pathCont_SpDraped; + delete m_pathCont_SpDrapedPolygon; + delete m_pathCont_SpAbsolute1; + delete m_pathCont_SpAbsolute2; + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo=" << m_debugInfo << "'"; +} + + +QString BasicPathObjectImpl::toString() const +{ + QString str; + str.append(BaseClass::toString()); + str.append(AbstractEffectSimulationImpl::toString()); + return str; +} + +Point3D BasicPathObjectImpl::tacticalOrigin() const +{ + return m_tacticalOrigin.value().origin(); +} + +void BasicPathObjectImpl::setTacticalOrigin(const Point3D &tacticalOrigin) +{ + if (m_tacticalOrigin.value().origin() != tacticalOrigin) + m_tacticalOrigin.refValue().setOrigin(tacticalOrigin); +} + +Point3D BasicPathObjectImpl::origin() const +{ + return m_origin.value(); +} +void BasicPathObjectImpl::setOrigin(const Point3D &origin) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo=" << m_debugInfo << "'" + << " origin=" << origin; + m_origin = origin; + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo=" << m_debugInfo << "'"; +} + +float BasicPathObjectImpl::radius() const +{ + return m_radius.value(); +} +void BasicPathObjectImpl::setRadius(float radius, unsigned int maxPointCount) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo=" << m_debugInfo << "'" + << " radius=" << radius + << " maxPointCount=" << maxPointCount; + m_radius = radius; + m_maxPointCount = maxPointCount; + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo=" << m_debugInfo << "'"; +} + + +#ifdef SUPPORT_SUBSIDIARY_LINES + +PositionCont BasicPathObjectImpl::subsidiaryPositions(int pathIndex) const +{ + if (m_subsidiaryPositionsCont.contains(pathIndex)) + return m_subsidiaryPositionsCont[pathIndex].positions(); + return PositionCont(); +} + +void BasicPathObjectImpl::setSubsidiaryPositions(int pathIndex, const PositionCont &positions) +{ + m_subsidiaryPositionsCont[pathIndex].setPositions(positions); +} + +QList BasicPathObjectImpl::subsidiaryPathIndexInUse() +{ + return m_subsidiaryPositionsCont.keys(); +} + +void BasicPathObjectImpl::clearSubsidiaryPositions(int pathIndex) +{ + m_subsidiaryPositionsCont.remove(pathIndex); +} + +void BasicPathObjectImpl::clearSubsidiaryPositions() +{ + m_subsidiaryPositionsCont.clear(); +} +#endif // SUPPORT_SUBSIDIARY_LINES + + +GraphicalObjectPathType BasicPathObjectImpl::pathType() const +{ + GraphicalObjectPath *path = static_cast(m_mainGraphicalObject); + return (path) ? path->pathType() : GraphicalObjectPathType::Polyline; +} + +int BasicPathObjectImpl::segmentCount() const +{ + if (m_positions.size() >= 2) + return m_positions.size() - 1; + + return 0; +} + + +AesEffectState BasicPathObjectImpl::aesState() const +{ + return BaseClass::aesState(); +} +void BasicPathObjectImpl::setAesState(AesEffectState aesState) +{ + BaseClass::setAesState(aesState); +} + + + + +IndicationContainer &BasicPathObjectImpl::rIndicationContainer() const +{ + if (!m_indicationCont) + { + m_indicationCont = new IndicationContainer(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_mapImpl->coordTranslationFunctor(), + m_uniqueMapId); + } + return *m_indicationCont; +} + +IndicationContainer &BasicPathObjectImpl::rIndicationContainerAbsolute() const +{ + if (!m_indicationContSpAbsolute) + { + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = m_mapImpl->getGraphicsLayer3D_SpAbsolute(m_logicalLayerName); + if (!graphicsLayer) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " getGraphicsLayer3D_SpAbsolute failed"; + } + m_indicationContSpAbsolute = new IndicationContainer(m_mapImpl->getEsriGeoView(), + graphicsLayer, + m_mapImpl->coordTranslationFunctor(), + m_uniqueMapId); + } + return *m_indicationContSpAbsolute; +} + +PathContainer &BasicPathObjectImpl::rPathContainer() const +{ + if (!m_pathCont_SpDraped) + { + m_pathCont_SpDraped = new PathContainer(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_mapImpl->coordTranslationFunctor(), + m_uniqueMapId); + } + return *m_pathCont_SpDraped; +} + +PathContainer &BasicPathObjectImpl::rPathContainerSpDrapedPolygon() const +{ + if (!m_pathCont_SpDrapedPolygon) + { + m_pathCont_SpDrapedPolygon = new PathContainer(GraphicalObjectPathType::Polygon, + m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_mapImpl->coordTranslationFunctor(), + m_uniqueMapId); + } + return *m_pathCont_SpDrapedPolygon; +} + +void BasicPathObjectImpl::clearPathContainerSpDrapedPolygon(int idx) +{ + if (!m_pathCont_SpDrapedPolygon) + return; + rPathContainerSpDrapedPolygon().clear(idx); +} + + +PathContainer &BasicPathObjectImpl::rPathContainerSpAbsolute1() const +{ + if (!m_pathCont_SpAbsolute1) + { + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = m_mapImpl->getGraphicsLayer3D_SpAbsolute(m_logicalLayerName); + if (!graphicsLayer) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " getGraphicsLayer3D_SpAbsolute failed"; + } + + m_pathCont_SpAbsolute1 = new PathContainer(m_mapImpl->getEsriGeoView(), + graphicsLayer, + m_mapImpl->coordTranslationFunctor(), + m_uniqueMapId); + } + return *m_pathCont_SpAbsolute1; +} +PathContainer &BasicPathObjectImpl::rPathContainerSpAbsolute2() const +{ + if (!m_pathCont_SpAbsolute2) + { + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = m_mapImpl->getGraphicsLayer3D_SpAbsolute(m_logicalLayerName); + if (!graphicsLayer) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " getGraphicsLayer3D_SpAbsolute failed"; + } + + m_pathCont_SpAbsolute2 = new PathContainer(m_mapImpl->getEsriGeoView(), + graphicsLayer, + m_mapImpl->coordTranslationFunctor(), + m_uniqueMapId); + } + return *m_pathCont_SpAbsolute2; +} + + + + +bool BasicPathObjectImpl::arrowHeadStart() const +{ + return m_arrowHeadStart.enable; +} +void BasicPathObjectImpl::setArrowHeadStart(bool enable) +{ + m_arrowHeadStart.enable = enable; +} + +bool BasicPathObjectImpl::arrowHeadEnd() const +{ + return m_arrowHeadEnd.enable; +} +void BasicPathObjectImpl::setArrowHeadEnd(bool enable) +{ + m_arrowHeadEnd.enable = enable; +} + +void BasicPathObjectImpl::setArrowHeadsSelected(bool select) +{ + if (m_arrowHeadStart.pmArrowHead) + m_arrowHeadStart.pmArrowHead->setSelected(select); + if (m_arrowHeadEnd.pmArrowHead) + m_arrowHeadEnd.pmArrowHead->setSelected(select); +} + +void BasicPathObjectImpl::setDetonation(const Point3D &origin, double radius) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + if (m_detonationOrigin.value().origin() != origin) + m_detonationOrigin.refValue().setOrigin(origin); + m_detonationRadius = radius; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +bool BasicPathObjectImpl::hasDetonation() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return m_detonationRadius.value() > 0; +} + +void BasicPathObjectImpl::resetDetonation() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + setDetonation({}, 0.0); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +QString BasicPathObjectImpl::textOnSegment(uint segment) const +{ + return m_textDataCont[segment].text; +} + +void BasicPathObjectImpl::setTextOnSegment(uint segment, const QString &segmentText) +{ + m_textDataCont[segment].text = segmentText; +} + +void BasicPathObjectImpl::clearTextOnSegment(uint segment) +{ + m_textDataCont.remove(segment); +} + + +QImage BasicPathObjectImpl::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 BasicPathObjectImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " m_debugInfo=" << m_debugInfo + << " symbol=" << m_symbol.value().sidc + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + if (!m_graphicsLayer) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no graphicsLayer"; + return false; + } + + if (!m_mainGraphicalObject) + { + ERROR_LOG(m_logger) << LOGFUNCTION << "no m_mainGraphicalObject"; + return false; + } + + // + // Use positions or origin/radius= + // + + if (m_radius.value() > 0) + { + // origin/radius : + if (m_origin.changed() || m_radius.changed() || m_maxPointCount.changed()) + { + // clear/disable some other things: + m_positions.clear(); + m_arrowHeadStart.enable = false; + m_arrowHeadEnd.enable = false; + m_textDataCont.clear(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " positions,arrowHeads,textDataCont cleared"; + } + + OriginTranslated orgTemp; + orgTemp.setMapObjectImpl(this); + orgTemp.setOrigin(m_origin.value()); + const Point3D &orgMapPoint = orgTemp.originTranslated(); + +// for 2D +// QList posContTrans = Geometry::createCircle(orgMapPoint, m_radius.value()); +// Q_FOREACH(const QPointF &pp, posContTrans) +// qWarning() << "posContTrans" << pp; + +// m_positions.setPositionsTranslated( posContTrans ); +// m_mainGraphicalObject->setPositions(this->positionsTranslated()); + +// for 2D and 3D: + Esri::ArcGISRuntime::Point centerP(orgMapPoint.x(), orgMapPoint.y(), orgMapPoint.z(), this->mapImpl()->getEsriSpatialReference()); + PositionCont posCont; + if (! GraphicalObjectEllipse::createCirclePolygon(centerP, m_radius.value(), m_maxPointCount.value(), posCont)) + ERROR_LOG(m_logger) << LOGFUNCTION << " createCirclePolygon failed"; + else + { + m_positions.setPositionsTranslated(posCont); + m_mainGraphicalObject->setPositions(this->positionsTranslated()); + } + + // : origin/radius + } + else + { + if (!m_positions.isEmpty()) + m_mainGraphicalObject->setPositions(this->positionsTranslated()); + } + + // verify: + const int minPoints = this->pathType() == GraphicalObjectPathType::Polyline ? 2 : 3; + if (m_mainGraphicalObject->positions().count() < minPoints) + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " debugInfo1='" << m_debugInfo << "'" + << " incorrect number of positions" + << " (" << this->positions().count() << ")"; + return false; + } + + // 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_tacticalOrigin has priority (SingleFire) + if (!m_tacticalOrigin.value().origin().toPointF().isNull()) // check if x,y are defined, just z is not sufficiant + m_tacticalAppearance->setOrigin(m_tacticalOrigin.value().originTranslated()); + else + m_tacticalAppearance->setPositions(m_mainGraphicalObject->positions()); + } + + + // Indication: + if (m_indicationCont) + { + // The drawOrder should be above the m_mainGraphicalObject (==Path) + // (case: path is closed and filled -> guarantee that indication is visible.) + m_indicationCont->setDrawOrder(m_internalDrawOrder + 1); + } + if (m_indicationContSpAbsolute) + { + m_indicationContSpAbsolute->setDrawOrder(m_internalDrawOrder + 1); + } + + // Paths: + if (m_pathCont_SpDraped) + { + // The drawOrder should be above the m_mainGraphicalObject (==Path) + // (case: path is closed and filled -> guarantee that indication is visible.) + m_pathCont_SpDraped->setDrawOrder(m_internalDrawOrder + 1); + + // lineStyle has to be set explicit + // m_pathCont->setLineStyle(m_mainGraphicalObject->lineStyle()); + } + + // Paths: + if (m_pathCont_SpDrapedPolygon) + { + // The drawOrder should be above the m_mainGraphicalObject (==Path) + // (case: path is closed and filled -> guarantee that indication is visible.) + m_pathCont_SpDrapedPolygon->setDrawOrder(m_internalDrawOrder + 1); + } + + +// if (m_pathCont_SpAbsolute) +// { +// // DrawOrder for SpAbsolute not really helpful: +// // m_pathCont_SpAbsolute->setDrawOrder(m_internalDrawOrder + 1); +// // m_pathCont_SpAbsolute->setLineStyle(m_mainGraphicalObject->lineStyle()); +// } + + if (m_pathCont_SpDraped) + if (!m_pathCont_SpDraped->updateView()) + WARNING_LOG(m_logger) << LOGFUNCTION << " m_pathCont_SpDraped updateView failed"; + if (m_pathCont_SpDrapedPolygon) + if (!m_pathCont_SpDrapedPolygon->updateView()) + WARNING_LOG(m_logger) << LOGFUNCTION << " m_pathCont_SpDrapedPolygon updateView failed"; + + if (m_pathCont_SpAbsolute1) + if (!m_pathCont_SpAbsolute1->updateView()) + WARNING_LOG(m_logger) << LOGFUNCTION << " m_pathCont_SpAbsolute1 updateView failed"; + if (m_pathCont_SpAbsolute2) + if (!m_pathCont_SpAbsolute2->updateView()) + WARNING_LOG(m_logger) << LOGFUNCTION << " m_pathCont_SpAbsolute2 updateView failed"; + + + // Single Scene Marker + // has to be executed in final class + // updateSingleSceneMarker(); + // updateSingleSceneMarker(true); + + + // Arrowhead: + updateArrowHead(m_arrowHeadStart, true); + updateArrowHead(m_arrowHeadEnd, false); + + // SegmentText: + bool successSegText = updateSegmentText(); + if (!successSegText) + ERROR_LOG(m_logger) << LOGFUNCTION << " successSegText failed"; + + +#ifdef SUPPORT_SUBSIDIARY_LINES + // subsidiaryPositionsCont : + GraphicalObjectMultiPath *multiPath = static_cast(m_mainGraphicalObject); + + QList pathIdxInUse; + int pathIdx = 0; + QMutableMapIterator mapIt(m_subsidiaryPositionsCont); + while (mapIt.hasNext()) + { + mapIt.next(); + PositionsTranslated &posTranslated = mapIt.value(); + posTranslated.setMapObjectImpl(this); // let functor work :-) + + // PRE-increment! (pathIdx=0 is the main path in GraphicalObjectMultiPath + multiPath->setPath(++pathIdx, posTranslated.positionsTranslated()); + pathIdxInUse.append(pathIdx); + + } // while + + // remove non-existing subsidiary idx from multiPath: + multiPath->reduceTo(pathIdxInUse); + + // : subsidiaryPositionsCont +#endif // SUPPORT_SUBSIDIARY_LINES + + + // BaseClass: + bool success = BaseClass::updateViewImpl() && successSegText; + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool BasicPathObjectImpl::updateSingleSceneMarker(bool newVisibility) +{ + DEBUG_LOG(m_logger) << " START, newVisibility = " << newVisibility + << ", m_debugInfo" << m_debugInfo; + + // do we have a detonationPoint / impactOrigin? + if (m_detonationOrigin.value().origin().isNull() && (m_detonationRadius.value() < 1.0)) + { + m_detonationSceneMarker.clear(); + } else { + if (!m_detonationSceneMarker) + { + m_detonationSceneMarker = QSharedPointer(new GraphicalObjectSceneMarker(SituationMap::SceneMarkerType::SPHERE, + m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QLatin1String("#DetonationSceneMarker"), + m_uniqueMapId)); + } + m_detonationSceneMarker->setOrigin(m_detonationOrigin.value().originTranslated()); + m_detonationSceneMarker->setDiameter(m_detonationRadius.value()*2.0); + m_detonationSceneMarker->setPickable(m_pickable); + // path == 0; indicationCont == 1; Marker == 2 + m_detonationSceneMarker->setDrawOrder(m_internalDrawOrder+2); + + QColor col = fillStyle().fillColor(); //partyColor(); partyColor could be invalid, e.g. in case of NContamination! + col.setAlphaF(col.alphaF() / 2.0); + m_detonationSceneMarker->setFillStyle(FillStyle(col, FillPattern::Solid)); + + m_detonationSceneMarker->setVisibility(newVisibility); + + if (!m_detonationSceneMarker->updateView()) + ERROR_LOG(m_logger) << LOGFUNCTION + << " updateView failed for" + << " debugInfo='" << m_detonationSceneMarker->debugInfo() << "'"; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +double viewAngleOfMapCoordinates3D(MapImpl * mapImpl, const Point3D &mapPos0, const Point3D &mapPos1, bool &bOkOut) +{ + bOkOut = false; + + if (!mapImpl) + return 0.0; + + QPointF p0 = mapImpl->mapCoordinateToViewPortWindowPoint3D(mapPos0); + if (p0.isNull()) + return 0.0; + QPointF p1 = mapImpl->mapCoordinateToViewPortWindowPoint3D(mapPos1); + if (p1.isNull()) + return 0.0; + + p0.ry() = p0.ry() * -1; + p1.ry() = p1.ry() * -1; + + double angle = MathUtils::getAngleDegreeNorth(p0, p1); + +// qCritical() << " mapPos0 = " << mapPos0 << " - mapPos1 = " << mapPos1; +// qCritical() << " p0 = " << p0 << " - p1 = " << p1; +// qCritical() << " angle = " << angle; + + bOkOut = true; + return angle; +} + +bool BasicPathObjectImpl::updateArrowHead(BasicPathObjectImpl::ArrowHead &arrowHead, + bool start) +{ + // pre-condition: + if (m_positions.size() < 2) + return false; + + if (!m_mainGraphicalObject) + return false; + + bool realEnable = arrowHead.enable; + + if (arrowHead.enable && + m_visibility && + m_visibilityTechnical && + (m_mapImpl->mode() == Mode::Mode_3D) + ) + { + // 3D -> take distance into account: + Point3D pt = m_positions.startPointTranslated(); + Esri::ArcGISRuntime::Point esriEnd3D(pt.x(), pt.y(), pt.z(), m_mapImpl->getEsriSpatialReference()); + const Esri::ArcGISRuntime::Point &camPoint = m_mapImpl->getEsriSceneGraphicsView()->currentViewpointCamera().location(); + + Esri::ArcGISRuntime::GeodeticDistanceResult distRes = Esri::ArcGISRuntime::GeometryEngine::distanceGeodetic(esriEnd3D, + camPoint, + Esri::ArcGISRuntime::LinearUnitId::Meters, + Esri::ArcGISRuntime::AngularUnitId::Degrees, + Esri::ArcGISRuntime::GeodeticCurveType::Geodesic); + realEnable = distRes.distance() < 20000; + } + + if (realEnable) + { + // delay creation if not visible: + if (arrowHead.pmArrowHead.isNull() && m_visibility && m_visibilityTechnical) + { + arrowHead.pmArrowHead = QSharedPointer + (new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView() + , m_graphicsLayer + , m_debugInfo + QLatin1String("#ArrowHead") + , m_uniqueMapId) + ); + } + + if (arrowHead.pmArrowHead) + { + + // maybe only if partyId changed or scale changed or selectionChanged + { + // colorChanged -> must be rendered again: + const float scale = m_mainGraphicalObject->scale() * m_mainGraphicalObject->lineStyle().lineWidth() * 0.5; + int lengthHeight = m_arrowHeadLengthHeight * scale; + int halfWidth = m_arrowHeadHalfWidth * scale; + + const QImage arrowImg = + renderArrowHeadSymbol(m_mainGraphicalObject->lineStyle().lineColor(), lengthHeight, halfWidth); + arrowHead.pmArrowHead->setImage(arrowImg); + arrowHead.pmArrowHead->setYOffset(lengthHeight / -2.0); + } + + + // take mapRotation into account, if necessary + double mapRotation = 0; + if (this->m_graphicsLayer->renderingMode() == Esri::ArcGISRuntime::GraphicsRenderingMode::Dynamic) + mapRotation = m_mapImpl->rotation(); + + + if (start) + { + arrowHead.pmArrowHead->setOrigin(m_positions.startPointTranslated()); + if (m_mapImpl->mode() == Mode::Mode_2D) + { + arrowHead.pmArrowHead->setRotationAngle(m_positions.angleOfStartSegment() + 180 + mapRotation); + } else { + // Mode::Mode_3D: + const PositionCont &posTranslated = m_positions.positionsTranslated(); + if (m_mapImpl && posTranslated.size() >= 2) + { + bool bOkOut = false; + double viewAngle = viewAngleOfMapCoordinates3D(m_mapImpl, posTranslated[1], posTranslated[0], bOkOut); + if (bOkOut) + arrowHead.pmArrowHead->setRotationAngle(viewAngle); + else + arrowHead.pmArrowHead->setRotationAngle(90); + } + } + } + else + { + switch (pathType()) + { + case GraphicalObjectPathType::Polyline: + arrowHead.pmArrowHead->setOrigin(m_positions.endPointTranslated()); + if (m_mapImpl->mode() == Mode::Mode_2D) + arrowHead.pmArrowHead->setRotationAngle(m_positions.angleOfEndSegment() + mapRotation); + else { + // Mode::Mode_3D: + const PositionCont &posTranslated = m_positions.positionsTranslated(); + const int size = posTranslated.size(); + if (m_mapImpl && size >= 2) + { + bool bOkOut = false; + double viewAngle = viewAngleOfMapCoordinates3D(m_mapImpl, posTranslated.at(size - 2), posTranslated.at(size - 1), bOkOut); + if (bOkOut) + arrowHead.pmArrowHead->setRotationAngle(viewAngle); + else + arrowHead.pmArrowHead->setRotationAngle(-90); + } + } + break; + case GraphicalObjectPathType::Polygon: + arrowHead.pmArrowHead->setOrigin(m_positions.startPointTranslated()); + if (m_mapImpl->mode() == Mode::Mode_2D) + arrowHead.pmArrowHead->setRotationAngle(m_positions.angleOfClosingSegment() + mapRotation); + else { + // Mode::Mode_3D: + const PositionCont &posTranslated = m_positions.positionsTranslated(); + const int size = posTranslated.size(); + if (m_mapImpl && size >= 2) + { + bool bOkOut = false; + double viewAngle = viewAngleOfMapCoordinates3D(m_mapImpl, posTranslated.first(), posTranslated.last(), bOkOut); + if (bOkOut) + arrowHead.pmArrowHead->setRotationAngle(viewAngle); + else + arrowHead.pmArrowHead->setRotationAngle(90); + } + } + break; + } // switch + + } + + arrowHead.pmArrowHead->setDrawOrder(m_internalDrawOrder + 1); + arrowHead.pmArrowHead->setVisibility(m_visibility && m_visibilityTechnical); + arrowHead.pmArrowHead->setPickable(m_pickable); + arrowHead.pmArrowHead->setSelected(m_selected); + arrowHead.pmArrowHead->updateView(); + + } // if (arrowHead.pmArrowHead) + + } + else + arrowHead.pmArrowHead.clear(); + + return true; +} + +bool BasicPathObjectImpl::updateSegmentText() +{ + // Qt5.4: QMap::lastKey(): "This function assumes that the map is not empty." + if (m_textDataCont.isEmpty()) + return true; + + if (!m_mapImpl) + return false; + + bool success = true; + + // Maybe the number of coordinates has been decreased and therefore the number of segments: + // They have to be removed to delete the TextData.goText (should be removed from screen.) + + while (!m_textDataCont.isEmpty() && m_textDataCont.lastKey() >= static_cast(this->segmentCount())) + { + // see QTBUG-59373 + const auto key = m_textDataCont.lastKey(); + m_textDataCont.remove(key); + } + + // ok, 'resized' :-) + // go on -> + + QMutableMapIterator it(m_textDataCont); + int i = -1; + while (it.hasNext()) + { + ++i; + + // current number of lineSegment exists? + if (i >= segmentCount()) + break; + + it.next(); + TextData &textData = it.value(); + + if (textData.goText.isNull()) + { + QString debugInfo = QStringLiteral("%1#Text_seg(%2)").arg(m_debugInfo).arg(i); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " create new Object " + << " " << debugInfo; + textData.goText = QSharedPointer + (new GraphicalObjectText(m_mapImpl->getEsriGeoView() + , m_graphicsLayer + , debugInfo + , m_uniqueMapId) + ); + } + + // calculate textOrigin on this segment: + const Point3D &mapCoord1 = m_positions.positionsTranslated()[i]; + const Point3D &mapCoord2 = m_positions.positionsTranslated()[i + 1]; + + const Point3D &textOrigin = MathUtils::getPointOnLine(mapCoord1, mapCoord2, 0.5); + + double angle = 0.0; + + if (m_mapImpl->mode() == Mode::Mode_2D) + { + angle = MathUtils::getAngleDegreeNorth(mapCoord1.toPointF(), mapCoord2.toPointF()); + } else { + // Mode::Mode_3D: + const PositionCont &posTranslated = m_positions.positionsTranslated(); + if (m_mapImpl && posTranslated.size() >= 2) + { + bool bOkOut = false; + angle = viewAngleOfMapCoordinates3D(m_mapImpl, mapCoord1, mapCoord2, bOkOut); + if (!bOkOut) + angle = 90; // degree, 3 o'clock + } + } + + + // take mapRotation into account, if necessary + if ((this->m_graphicsLayer->renderingMode() == Esri::ArcGISRuntime::GraphicsRenderingMode::Dynamic) && + (m_mapImpl->mode() == Mode::Mode_2D)) + angle += m_mapImpl->rotation(); + + if (angle > 360.0) + angle -= 360; + else if (angle < -360.0) + angle += 360; + + if ((angle >= 0) && (angle < 180)) + { + // Q1 & Q2 + angle -= 90; + } + else + { + // Q3 & Q4 + angle += 90; + } + + textData.goText->setOrigin(textOrigin); + textData.goText->setVisibility(m_visibility && m_visibilityTechnical); + textData.goText->setDrawOrder(m_internalDrawOrder + 1); + textData.goText->setText(textData.text); + + if (m_mainGraphicalObject) + { + textData.goText->setScale(m_mainGraphicalObject->scale()); + textData.goText->setPickable(m_mainGraphicalObject->pickable()); + } + + + TextStyle ts = defaultTextStyle(); + if (m_mainGraphicalObject) + ts.setTextColor(m_mainGraphicalObject->lineStyle().lineColor()); + // ts.setTextOutlineColor(Qt::white); + // ts.setTextOutlineWidth(2); + ts.setHorizontalAlignment(HorizontalAlignment::Center); + ts.setVerticalAlignment(VerticalAlignment::Bottom); + + ts.setAngle(angle); + // int outlineWith = 2 * scale(); + // if (outlineWith > 4) + // outlineWith = 4; + // ts.setTextOutlineWidth( outlineWith ); + textData.goText->setTextStyle(ts); + + + +// TextStyle ts; +// ts.setTextOutlineColor(Qt::white); +// ts.setTextOutlineWidth(2); +// ts.setHorizontalAlignment(HorizontalAlignment::Center); +// ts.setVerticalAlignment(VerticalAlignment::Middle); + +// ts.setAngle(angle); +// ts.setSize(ts.size() * scale()); +// int outlineWith = 2 * scale(); +// if (outlineWith > 4) +// outlineWith = 4; +// ts.setTextOutlineWidth( outlineWith ); +// textData.goText->setTextStyle(ts); + + if (! textData.goText->updateView()) + { + success = false; + ERROR_LOG(m_logger) << LOGFUNCTION + << " updateView failed" + << " debugInfo1='" << m_debugInfo << "'" + << " debugInfo2='" << textData.goText->debugInfo() << "'"; + } + + + } // while + + return success; +} + + + +bool BasicPathObjectImpl::init(MapImpl *mapImpl, + const QString &logicalLayerName, + GraphicalObjectPathType pathType, + double maxSegmentLength) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " logicalLayerName='" << logicalLayerName << "'" + << " pathType=" << pathType; + + // to create adequate preview: + m_isClosed = (pathType == GraphicalObjectPathType::Polygon); + + bool success = BaseClass::init(mapImpl, logicalLayerName); + + if (success) + { + // create underlying object (to save most attributes only there) + if (!m_mainGraphicalObject) + { + // m_mainGraphicalObject = new GraphicalObjectMultiPath(pathType, + GraphicalObjectPath *goPath = new GraphicalObjectPath(pathType, + m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QLatin1String("#GOPath"), + m_uniqueMapId); + goPath->setMaxSegmentLength(maxSegmentLength); + m_mainGraphicalObject = goPath; + } + + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + m_tacticalOrigin.refValue().setMapObjectImpl(this); + m_detonationOrigin.refValue().setMapObjectImpl(this); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +double BasicPathObjectImpl::maxSegmentLength() const +{ + if (!m_mainGraphicalObject) + return -1; + GraphicalObjectPath *goPath = dynamic_cast(m_mainGraphicalObject); + if (!goPath) + return -1; + return goPath->maxSegmentLength(); +} + +bool BasicPathObjectImpl::onMapExtentChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo=" << debugInfo(); + + bool success = BaseClass::onMapExtentChanged(); + + bool successIndication = true; + if (m_indicationCont) + { + QElapsedTimer timer; + + if (m_logger.isDebugEnabled()) + timer.start(); + + successIndication = m_indicationCont->updateView(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo=" << m_debugInfo + << " Indication.updateView took[ms] " << timer.elapsed(); + } + bool successIndicationAbs = true; + if (m_indicationContSpAbsolute) + { + QElapsedTimer timer; + + if (m_logger.isDebugEnabled()) + timer.start(); + + successIndicationAbs = m_indicationContSpAbsolute->updateView(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo=" << m_debugInfo + << " IndicationSpAbsolute.updateView took[ms] " << timer.elapsed(); + } + + + + bool successPath1 = true; + if (m_pathCont_SpDraped) + { + QElapsedTimer timer; + + if (m_logger.isDebugEnabled()) + timer.start(); + + successPath1 = m_pathCont_SpDraped->updateView(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo=" << m_debugInfo + << " m_pathCont_SpDraped.updateView took[ms] " << timer.elapsed(); + } + + bool successPath2 = true; + if (m_pathCont_SpDrapedPolygon) + { + QElapsedTimer timer; + + if (m_logger.isDebugEnabled()) + timer.start(); + + successPath2 = m_pathCont_SpDrapedPolygon->updateView(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo=" << m_debugInfo + << " m_pathCont_SpDrapedPolygon.updateView took[ms] " << timer.elapsed(); + } + + bool successPathSpA1 = true; + if (m_pathCont_SpAbsolute1) + { + QElapsedTimer timer; + + if (m_logger.isDebugEnabled()) + timer.start(); + + successPathSpA1 = m_pathCont_SpAbsolute1->updateView(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo=" << m_debugInfo + << " m_pathCont_SpAbsolute1.updateView took[ms] " << timer.elapsed(); + } + bool successPathSpA2 = true; + if (m_pathCont_SpAbsolute2) + { + QElapsedTimer timer; + + if (m_logger.isDebugEnabled()) + timer.start(); + + successPathSpA2 = m_pathCont_SpAbsolute2->updateView(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo=" << m_debugInfo + << " m_pathCont_SpAbsolute2.updateView took[ms] " << timer.elapsed(); + } + + // to honor mapRotation: + updateArrowHead(m_arrowHeadStart, true); + updateArrowHead(m_arrowHeadEnd, false); + updateSegmentText(); + + success = success && successIndication && successIndicationAbs && successPath1 && successPath2 && successPathSpA1 && successPathSpA2; + + if (!success) + ERROR_LOG(m_logger) + << " successIndication=" << successIndication + << " successIndicationAbs=" << successIndicationAbs + << " successPath1=" << successPath1 + << " successPath2=" << successPath2 + << " successPathSpA1=" << successPathSpA1 + << " successPathSpA2=" << successPathSpA2; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/basic_path_object_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/basic_path_object_impl.h new file mode 100755 index 000000000..3a6d98724 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/basic_path_object_impl.h @@ -0,0 +1,295 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of BasicPathObjectImpl + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "indication_container.h" +#include "path_container.h" +#include +#include +#include +#include + + +// Qt +#include +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ +class GraphicalObjectSceneMarker; + + +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +/** + * @brief The BasicPathObjectImpl class combines a couple of usefull features required by path objects. + * + * These features are: + * - For convenience an origin and radius can also be parametrized. This will be transformed into a polygon during updateView(). + * - an arrowHead at the start and/or at the end of that path (both polyline and polygon) + * - m_arrowHeadStart, m_arrowHeadEnd of type ArrowHead / GraphicalObjectPictureMarker / Image + * + * - text positioned on the line segments + * - m_textDataCont of type TextData / GraphicalObjectText + * + * - an infinite number of different indication objects for displaying impacts, mines etc. + * - m_indicationCont of type IndicationContainer / GraphicalObjectPictureMarker / Image + * - m_pathCont of type PathContainer (on main Layer) + * - m_pathCont_SpAbsolute of type PathContainer (on subLayer with SurfacePlacementMode::Absolute) + */ +class SITUATIONMAPOBJECTS_EXPORT BasicPathObjectImpl : public AbstractMultiPointMapObjectImpl, public AbstractEffectSimulationImpl +{ + using BaseClass = AbstractMultiPointMapObjectImpl; + +public: + /*! \name Construction, Destruction */ + //@{ + + explicit BasicPathObjectImpl(const QString &debugInfo = QString()); + virtual ~BasicPathObjectImpl(); + + BasicPathObjectImpl(const BasicPathObjectImpl &other) = delete; + BasicPathObjectImpl &operator=(BasicPathObjectImpl &other) = delete; + //@} + + QString toString() const override; + + virtual Point3D tacticalOrigin() const; + virtual void setTacticalOrigin(const Point3D &tacticalOrigin); + + /*! \name Set origin and radius as alternative to 'setPositions' */ + //@{ + virtual Point3D origin() const; + virtual void setOrigin(const Point3D &origin); + virtual float radius() const; + virtual void setRadius(float radius, unsigned int maxPointCount = 0); + //@} + +#ifdef SUPPORT_SUBSIDIARY_LINES + /*! \name Subsidiary polyline/polygons */ + //@{ + /** + * @brief Get positions at pathIndex. + * @param pathIndex to be retrieved (if '0' identical to positions();) + * @return positions at index + */ + virtual PositionCont subsidiaryPositions(int pathIndex) const; + + /** + * @brief Set positions at pathIndex. + * @param pathIndex to be set. + * @param positions values to be set. + */ + virtual void setSubsidiaryPositions(int pathIndex, const PositionCont &positions); + + virtual QList subsidiaryPathIndexInUse(); + + + /** + * @brief Clear positions. + * @param pathIndex to be cleared. + * @note if not pathIndex is specified, ALL are cleared!) + */ + virtual void clearSubsidiaryPositions(int pathIndex); + + /** + * @brief Clear all subsidiaryPositions. + */ + virtual void clearSubsidiaryPositions(); + + //@} +#endif // SUPPORT_SUBSIDIARY_LINES + + + + GraphicalObjectPathType pathType() const; + + virtual int segmentCount() const; + + // Delegate to BaseClass: + virtual AesEffectState aesState() const override; + virtual void setAesState(AesEffectState aesState) override; + + + /*! \name Indication (for displaying impacts, mines etc.) */ + //@{ + virtual IndicationContainer &rIndicationContainer() const; + virtual IndicationContainer &rIndicationContainerAbsolute() const; + //@} + + /*! \name Path (for displaying paths like trajectories) */ + //@{ + virtual PathContainer &rPathContainer() const; + + virtual PathContainer &rPathContainerSpDrapedPolygon() const; + virtual void clearPathContainerSpDrapedPolygon(int idx); + + virtual PathContainer &rPathContainerSpAbsolute1() const; + + virtual PathContainer &rPathContainerSpAbsolute2() const; + //@} + + /*! \name ArrowHeads */ + //@{ + + //@} + + /*! \name ArrowHeads */ + //@{ + virtual bool arrowHeadStart() const; + virtual void setArrowHeadStart(bool enable); + + virtual bool arrowHeadEnd() const; + virtual void setArrowHeadEnd(bool enable); + + virtual void setArrowHeadsSelected(bool select); + //@} + + + /*! \name Single Detonation */ + //@{ + void setDetonation(const Point3D &origin, double radius); + bool hasDetonation() const; + void resetDetonation(); + //@} + + + /*! \name Text on lineSegments */ + //@{ + virtual QString textOnSegment(uint segment) const; + virtual void setTextOnSegment(uint segment, const QString &segmentText); + virtual void clearTextOnSegment(uint segment); + //@} + + + static QImage renderArrowHeadSymbol(const QColor &color, int length, int halfWidth); + +protected: + bool updateViewImplPost() override + { + BaseClass::updateViewImplPost(); + m_tacticalOrigin.reset(); + m_origin.reset(); + m_radius.reset(); + m_maxPointCount.reset(); + + // Single Detonation by SingleSceneMarker + m_detonationOrigin.reset(); + m_detonationRadius.reset(); + return true; + } + + + bool updateViewImpl() override; + + bool updateSingleSceneMarker(bool newVisibility); + + + friend class MapImpl; + virtual bool init(MapImpl *mapImpl, + const QString &logicalLayerName, + GraphicalObjectPathType pathType, + double maxSegmentLength = -1); + + double maxSegmentLength() const; + + bool onMapExtentChanged() override; + + // internal/admin + + // logical + SimpleValue m_tacticalOrigin; // has priority if set (related to mainGO->positions - SingleFire) + + // origin, radius as alternative to 'setPositions' : + SimpleValue m_origin; + SimpleValue m_radius = 0; + SimpleValue m_maxPointCount = 0; + // : origin, radius as alternative to 'setPositions' + + + // Indication : + mutable IndicationContainer *m_indicationCont{nullptr}; + mutable IndicationContainer *m_indicationContSpAbsolute{nullptr}; // SurfacePlacementMode::Absolute + // : Indication + + // Paths : + mutable PathContainer *m_pathCont_SpDraped{nullptr}; + mutable PathContainer *m_pathCont_SpDrapedPolygon{nullptr}; // SurfacePlacementMode::Draped + + mutable PathContainer *m_pathCont_SpAbsolute1{nullptr}; // SurfacePlacementMode::Absolute + mutable PathContainer *m_pathCont_SpAbsolute2{nullptr}; // SurfacePlacementMode::Absolute + // : Paths + + // Single Scene Marker + // (arbitrary geometry like detonations of nContamination makes it impossible to use renderer approach) + SimpleValue m_detonationOrigin; + SimpleValue m_detonationRadius{0.0}; + QSharedPointer m_detonationSceneMarker; // 3D + // : Single Scene Marker + + + // ArrowHeads : + struct ArrowHead + { + bool enable = false; + QSharedPointer pmArrowHead; + + }; + ArrowHead m_arrowHeadStart; + ArrowHead m_arrowHeadEnd; + int m_arrowHeadLengthHeight = 20; + int m_arrowHeadHalfWidth = 7; + + bool updateArrowHead(ArrowHead &arrowHead, bool start); + // : ArrowHeads + + // Text on line segments : + struct TextData + { + QString text; + QSharedPointer goText; + }; + + QMap< uint, TextData > m_textDataCont; + + bool updateSegmentText(); + + // : Text on line segments + + +#ifdef SUPPORT_SUBSIDIARY_LINES + QMap m_subsidiaryPositionsCont; +#endif // SUPPORT_SUBSIDIARY_LINES + + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + +// Under special circumstances we have to instanciate BasicPathObjectImpl. +// Think of subsidiaryliens at VirtualMines. +// To to so, create 'LocalBasicPathObject' + +// to get access to 'init' +class LocalBasicPathObject final : public BasicPathObjectImpl +{ +public: + using BasicPathObjectImpl::BasicPathObjectImpl; + using BasicPathObjectImpl::init; + using BasicPathObjectImpl::setUniqueId; +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/biological_agent_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/biological_agent_impl.cpp new file mode 100755 index 000000000..2a8a3c37f --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/biological_agent_impl.cpp @@ -0,0 +1,156 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of BiologicalAgent + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "biological_agent_impl.h" +#include +#include +#include +#include + +#include + +// ESRI + + +// Qt + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class BiologicalAgentImpl +//----------------------------------------------------------------------------- +BiologicalAgentImpl::BiologicalAgentImpl(const QString &debugInfo) + : BiologicalAgent() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("BiologicalAgentImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + m_lineColorFromParty = false; + m_fillColorAlpha = 200; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +BiologicalAgentImpl::~BiologicalAgentImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +QString BiologicalAgentImpl::toString() const +{ + QString str; + str.append(BaseClass::toString()); + str.append(BasicPathObjectImpl::toString()); + return str; +} + + +Point3D BiologicalAgentImpl::origin() const +{ + return BaseClass::origin(); +} +void BiologicalAgentImpl::setOrigin(const Point3D &origin) +{ + BaseClass::setOrigin(origin); +} +float BiologicalAgentImpl::radius() const +{ + return BaseClass::radius(); +} + +void BiologicalAgentImpl::setRadius(float radius) +{ + BaseClass::setRadius(radius); +} + + +AesEffectState BiologicalAgentImpl::aesState() const +{ + return BaseClass::aesState(); +} +void BiologicalAgentImpl::setAesState(AesEffectState aesState) +{ + BaseClass::setAesState(aesState); +} + +SymbolImage BiologicalAgentImpl::symbolImage() const +{ + SymbolImage symImage = m_tacticalAppearance->symbolImage(); + return symImage; +} + + + +bool BiologicalAgentImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " origin =" << this->origin() //.x() << ", " << this->origin().y() + << " radius =" << this->radius() + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + bool success = BaseClass::updateViewImpl(); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + + +bool BiologicalAgentImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (mapImpl) + { + switch (mapImpl->symbologyType()) + { + case AdvancedSymbology::SymbologyType::APP6D: + case AdvancedSymbology::SymbologyType::MIL2525D: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "10012500002717000000"); // SymbolSet = 25 271700 + // ^^ ^^^^^^ + break; + case AdvancedSymbology::SymbologyType::MIL2525C: + // m_symbol.refValue().m_sidc = QLatin1String("GUMPNB----"); // 0953 GUMPNB---- -> Polygon -> Image WITH Area // small icon and big border + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GFMPNEB---"); // 0081 GFMPNEB--- -> Point -> Image WITHOUT Area // not 'centered' + break; + case AdvancedSymbology::SymbologyType::APP6B: + // m_symbol.refValue().m_sidc = QLatin1String("GHCPBWC---"); // APP6B: 3937 GHCPBWC--- -> Polygon + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GUCPBWK---"); // APP6B: 2875 GUCPBWK--- -> Point + break; + case AdvancedSymbology::SymbologyType::None: + // empty + break; + } // switch + + } // if (mapImpl) + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polygon); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + setLineStyle(LineStyle(Qt::yellow, 2)); + setFillStyle(FillStyle(Qt::yellow, FillPattern::Null)); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/biological_agent_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/biological_agent_impl.h new file mode 100755 index 000000000..e6a34cc52 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/biological_agent_impl.h @@ -0,0 +1,83 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of BiologicalAgent + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + +// Qt + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +/** + * @brief The BiologicalAgentImpl class can be described by an origin and a radius. + * \note Maybe this will be changed in future to be consistent to the ChemicalAgent. + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath/Polyline, displaying the main techn. Appearance) + * - mainGraphicalObjectText (displaying the text/name) + * - tacticalAppearance (displaying the tact. Appearance) + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT BiologicalAgentImpl : public BasicPathObjectImpl, public virtual BiologicalAgent +{ + using BaseClass = BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit BiologicalAgentImpl(const QString &debugInfo = QString()); + virtual ~BiologicalAgentImpl(); + +public: + BiologicalAgentImpl(const BiologicalAgentImpl &other) = delete; + BiologicalAgentImpl &operator=(BiologicalAgentImpl &other) = delete; + + QString toString() const override; + + + // some set/get methods: + Point3D origin() const override; + void setOrigin(const Point3D &origin) override; + float radius() const override; + void setRadius(float radius) override; + + + // Delegate to BaseClass: + AesEffectState aesState() const override; + void setAesState(AesEffectState aesState) override; + + SymbolImage symbolImage() const override; + + +protected: + bool updateViewImpl() override; + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + + // logical + + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/building_outline_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/building_outline_impl.cpp new file mode 100755 index 000000000..bb76b4827 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/building_outline_impl.cpp @@ -0,0 +1,269 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of BuildingOutline + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "building_outline_impl.h" +#include +#include +#include + +#include "Map/object_factory.h" + +#include + +// ESRI + + +// Qt + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class BuildingOutlineImpl +//----------------------------------------------------------------------------- +BuildingOutlineImpl::BuildingOutlineImpl(const QString &debugInfo) + : BuildingOutline() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("BuildingOutlineImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + m_markerOrigin.setMapObjectImpl(this); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +BuildingOutlineImpl::~BuildingOutlineImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete m_marker; + delete m_modelSceneMarker; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +PositionCont BuildingOutlineImpl::positions() const +{ + return BaseClass::positions(); +} + +bool BuildingOutlineImpl::setPositions(const PositionCont &posCont) +{ + return BaseClass::setPositions(posCont); +} + +QColor BuildingOutlineImpl::majorColor() const +{ + if (m_forcedMajorColor.isValid()) + return m_forcedMajorColor; + // use fillColor here: + return (m_mainGraphicalObject) ? m_mainGraphicalObject->fillStyle().fillColor() : BaseClass::majorColor(); +} + + + +LineStyle BuildingOutlineImpl::lineStyle() const +{ + return BaseClass::lineStyle(); +} +LineStyle &BuildingOutlineImpl::rLineStyle() +{ + return BaseClass::rLineStyle(); +} +void BuildingOutlineImpl::setLineStyle(const LineStyle &lineStyle) +{ + BaseClass::setLineStyle(lineStyle); +} +FillStyle BuildingOutlineImpl::fillStyle() const +{ + return BaseClass::fillStyle(); +} +FillStyle &BuildingOutlineImpl::rFillStyle() +{ + return BaseClass::rFillStyle(); +} +void BuildingOutlineImpl::setFillStyle(const FillStyle &fillStyle) +{ + BaseClass::setFillStyle(fillStyle); +} + +void BuildingOutlineImpl::setMarker(const Point3D &markerOrigin) +{ + m_markerOrigin.setOrigin(markerOrigin); +} + +BuildingOutline::ModelSceneSymbolData BuildingOutlineImpl::getModelSceneSymbolData() +{ + return m_modelSceneSymolData; +} + +void BuildingOutlineImpl::setModelSceneSymbolData(const BuildingOutline::ModelSceneSymbolData &modelSceneSymbolData) +{ + m_modelSceneSymolData = modelSceneSymbolData; +} + +bool BuildingOutlineImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical + << " scale=" << m_mainGraphicalObject->scale() + << " posCont=" << m_positions.positions() + << " url=" << m_modelSceneSymolData.url.toString() + << " scale=" << m_modelSceneSymolData.scale + << " m_heading=" << m_modelSceneSymolData.heading; + + bool success = BaseClass::updateViewImpl(); + updateViewImplMarker(); + + if (m_mapImpl->mode() == Mode::Mode_3D) + updateModelSceneSymbol(); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool BuildingOutlineImpl::updateViewImplMarker() +{ + if (m_markerOrigin.isEmpty()) + { + delete m_marker; + m_marker = nullptr; + } + else + { + if (!m_marker) + { + m_marker = new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QLatin1String("#Marker"), + m_uniqueMapId); + } + const float scale = m_mainGraphicalObject->scale() * m_mainGraphicalObject->lineStyle().lineWidth() * 0.5f; + + m_marker->setMarkerSymbol(MarkerSymbolType::Triangle, qRound(5.0f * scale), m_mainGraphicalObject->rLineStyle().lineColor()); + m_marker->setPickable(m_mainGraphicalObject->pickable()); + m_marker->setDrawOrder(m_internalDrawOrder+1); + m_marker->setOrigin(m_markerOrigin.originTranslated()); + m_marker->setVisibility(m_visibility && m_visibilityTechnical); + + if (!m_marker->updateView()) + ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" + << " updateView failed"; + } + + return true; +} + +bool BuildingOutlineImpl::updateModelSceneSymbol() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo='" << m_debugInfo << "'" + << " url=" << m_modelSceneSymolData.url.toString() + << " scale=" << m_modelSceneSymolData.scale + << " m_heading=" << m_modelSceneSymolData.heading; + + bool success = false; + + if (m_markerOrigin.isEmpty()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no markerOrigin" + << " debugInfo='" << m_debugInfo << "'"; + return false; + } + + if (m_modelSceneSymolData.url.isEmpty()) + { + delete m_modelSceneMarker; + m_modelSceneMarker= nullptr; + } else { + if (!m_modelSceneMarker) + m_modelSceneMarker = new GraphicalObjectModelSceneMarker(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QLatin1String("#ModelSceneMarker"), + m_uniqueMapId); + // Symbol: + m_modelSceneMarker->setUrl(m_modelSceneSymolData.url); + m_modelSceneMarker->setOrigin(m_markerOrigin.originTranslated()); + m_modelSceneMarker->setHeading(m_modelSceneSymolData.heading); + m_modelSceneMarker->setScale(m_modelSceneSymolData.scale); // use scale of m_modelSceneSymolData, not of m_mainGraphicalObject + m_modelSceneMarker->setVisibility(m_visibility && m_visibilityTechnical); + success = m_modelSceneMarker->updateView(); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed" + << " debugInfo='" << m_debugInfo << "'"; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo='" << m_debugInfo << "'"; + return success; +} + + +bool BuildingOutlineImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (mapImpl) + { + switch (mapImpl->symbologyType()) + { + case AdvancedSymbology::SymbologyType::APP6D: + case AdvancedSymbology::SymbologyType::MIL2525D: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "10042500002701000000"); // SymbolSet = 25 270100 + break; + case AdvancedSymbology::SymbologyType::MIL2525C: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "G*MPOGZ---****X"); + break; + case AdvancedSymbology::SymbologyType::APP6B: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GFCPBOGB--"); // APP6B: 3845: GFCPBOGB-- -> Polygon + break; + case AdvancedSymbology::SymbologyType::None: + // empty + break; + } // switch + } // if (mapImpl) + + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polygon); + + setLineStyle(LineStyle(Qt::black, 2)); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +void BuildingOutlineImpl::updateIdentifierPosition() +{ + if (m_positions.isEmpty()) + return; + + BoundingBox bbox; + this->boundingBox(bbox); + + if (!bbox.isValid()) + return BaseClass::updateIdentifierPosition(); + + OriginTranslated objCenterPoint; + objCenterPoint.setMapObjectImpl(this); + objCenterPoint.setOrigin(bbox.center()); + + m_identifierPositionCandidateTranslated = objCenterPoint.originTranslated(); +} + +} // namespace SituationMap + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/building_outline_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/building_outline_impl.h new file mode 100755 index 000000000..6ce1276fd --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/building_outline_impl.h @@ -0,0 +1,94 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of BuildingOutline + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "basic_path_object_impl.h" +#include // Interface +#include + +// Qt +#include +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +/** + * @brief The BuildingOutlineImpl class displays the outline of a building. + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath displaying the main techn. Appearance) + * - mainGraphicalObjectText (displaying the text/name) + * - tacticalAppearance (displaying the tact. Appearance) + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT BuildingOutlineImpl : public BasicPathObjectImpl, public virtual BuildingOutline +{ + using BaseClass = BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit BuildingOutlineImpl(const QString &debugInfo = QString()); + virtual ~BuildingOutlineImpl(); + +public: + BuildingOutlineImpl(const BuildingOutlineImpl &other) = delete; + BuildingOutlineImpl &operator=(BuildingOutlineImpl &other) = delete; + + PositionCont positions() const override; + bool setPositions(const PositionCont &posCont) override; + + QColor majorColor() const override; + + 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; + + void setMarker(const Point3D &markerOrigin) override; + + ModelSceneSymbolData getModelSceneSymbolData() override; + void setModelSceneSymbolData(const ModelSceneSymbolData &modelSceneSymbolData) override; + +protected: + bool updateViewImpl() override; + bool updateViewImplMarker(); + bool updateModelSceneSymbol(); + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + void updateIdentifierPosition() override; + + // logical + + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility + + GraphicalObjectPictureMarker *m_marker{nullptr}; + OriginTranslated m_markerOrigin; + + ModelSceneSymbolData m_modelSceneSymolData; + GraphicalObjectModelSceneMarker *m_modelSceneMarker{nullptr}; +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/chemical_agent_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/chemical_agent_impl.cpp new file mode 100755 index 000000000..b46a8c293 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/chemical_agent_impl.cpp @@ -0,0 +1,129 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of ChemicalAgent + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "chemical_agent_impl.h" +#include +#include +#include + +#include + +// ESRI + + +// Qt + + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class ChemicalAgentImpl +//----------------------------------------------------------------------------- +ChemicalAgentImpl::ChemicalAgentImpl(const QString &debugInfo) + : ChemicalAgent() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("ChemicalAgentImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + m_lineColorFromParty = false; + m_fillColorAlpha = 200; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +ChemicalAgentImpl::~ChemicalAgentImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +Point3D ChemicalAgentImpl::origin() const +{ + return BaseClass::origin(); +} +void ChemicalAgentImpl::setOrigin(const Point3D &origin) +{ + BaseClass::setOrigin(origin); +} +float ChemicalAgentImpl::radius() const +{ + return BaseClass::radius(); +} + +void ChemicalAgentImpl::setRadius(float radius, unsigned int maxPointCount) +{ + BaseClass::setRadius(radius, maxPointCount); +} + + +bool ChemicalAgentImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " symbol=" << m_symbol.value().sidc + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + bool success = BaseClass::updateViewImpl(); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + + +bool ChemicalAgentImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (mapImpl) + { + switch (mapImpl->symbologyType()) + { + case AdvancedSymbology::SymbologyType::APP6D: + case AdvancedSymbology::SymbologyType::MIL2525D: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "10012500002718000000"); // SymbolSet = 25 271800 + // ^^ ^^^^^^ + break; + case AdvancedSymbology::SymbologyType::MIL2525C: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GUMPNC----"); // 0985 GUMPNC---- -> Polygon + // m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"GFMPNEC---"); // MIL2525D: 0115: GFMPNEC--- "Chemical F"s + break; + case AdvancedSymbology::SymbologyType::APP6B: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GUCPBWH---"); // APP6B: 3943 GUCPBWH--- -> Polygon + // m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"GUCPBWK---"); // APP6B: 2879: GUCPBWK--- -> Point "Chemical Attack, Release Event U" + break; + case AdvancedSymbology::SymbologyType::None: + // empty + break; + } // switch + } // if (mapImpl) + + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polygon); + + setLineStyle(LineStyle(Qt::yellow, 2)); + setFillStyle(FillStyle(Qt::yellow, FillPattern::Null)); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/chemical_agent_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/chemical_agent_impl.h new file mode 100755 index 000000000..535844fa8 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/chemical_agent_impl.h @@ -0,0 +1,78 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of ChemicalAgent + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + + +// Qt +#include +#include + + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +/** + * @brief The ChemicalAgentImpl class can be described by an unlimited number of positions (PositionCont). + * For convenience an origin and radius can also be parametrized (inherented from BasicPathObjectImpl) + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath displaying the main techn. Appearance) + * - mainGraphicalObjectText (displaying the text/name) + * - tacticalAppearance (displaying the tact. Appearance) + * - an infinite number indication objects for displaying impacts (inherented from BasicPathObjectImpl). + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT ChemicalAgentImpl : public BasicPathObjectImpl, public virtual ChemicalAgent +{ + using BaseClass = SituationMap::BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit ChemicalAgentImpl(const QString &debugInfo = QString()); + virtual ~ChemicalAgentImpl(); + +public: + ChemicalAgentImpl(const ChemicalAgentImpl &other) = delete; + ChemicalAgentImpl &operator=(ChemicalAgentImpl &other) = delete; + + Point3D origin() const override; + void setOrigin(const Point3D &origin) override; + float radius() const override; + void setRadius(float radius, unsigned int maxPointCount = 0) override; + + + +protected: + bool updateViewImpl() override; + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + + // logical + + +private: + + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/cube_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/cube_impl.cpp new file mode 100755 index 000000000..d3dd02397 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/cube_impl.cpp @@ -0,0 +1,166 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of DangerArea + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "cube_impl.h" +#include +#include +#include +#include + + + +// ESRI +#include +#include +#include + + +// Qt + + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class CubeImpl +//----------------------------------------------------------------------------- +CubeImpl::CubeImpl(const QString &debugInfo) + : Cube() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("CubeImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +CubeImpl::~CubeImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +double CubeImpl::height() const +{ + return m_height; +} +void CubeImpl::setHeight(double height) +{ + m_height = height; +} + + +bool CubeImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " symbol=" << m_symbol.value().sidc + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + // pre-condition + Esri::ArcGISRuntime::Renderer *curRenderer = nullptr; + if (m_graphicsLayer) + curRenderer = m_graphicsLayer->renderer(); + if (!curRenderer) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no ArcGISRuntime::Renderer, layername='" << m_logicalLayerName << "'"; + return false; + } + + + // Destroy if invisible : + // 13.09.2018, Norbert Thoden + // Regarding ESRI Case #02170796 - [BOS] Crash with Destruction of 3D Scene + // To reduce likelihood of crash: + // Destroy GraphicalObject (GO, m_mainGraphicalObject) if it becomes invisible + // With V100.3.0 the behaviour does not change. + // (switching cubes for 180 bldgs on and off) + // + if (m_visibility) + { + if (!m_mainGraphicalObject) + { + m_mainGraphicalObject = new GraphicalObjectCube(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QStringLiteral("#GOCube"), + m_uniqueMapId); + } + } else { + delete m_mainGraphicalObject; + m_mainGraphicalObject = nullptr; + return true; + } + // : Destroy if invisible + + + GraphicalObjectCube *goCube = static_cast(m_mainGraphicalObject); + if (goCube) + { + // in former times: partyName was used + // it´s replaced by more flexible ExtrusionColorName: + // const QString &partyName = m_mapImpl->partyIdToColorFunctor().partyIdToName(m_party.partyId.value()); + // goCube->setPartyName(partyName); + + const QColor &partyColor= m_mapImpl->partyIdToColorFunctor().partyIdToColor(m_party.partyId.value()); + if (m_party.partyId.value() > -2) //Use Default: White or TeamColor + { + goCube->setExtrusionColorName( partyColor.name() ); + } + else // Use given Color //need to be prepared in layerfactory! + { + goCube->setExtrusionColorName( majorColor().name() ); + } + + goCube->setPositions(m_positions.positionsTranslated()); + goCube->setHeight(m_height); + } + else + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no m_mainGraphicalObject"; + return false; + } + + + bool success = BaseClass::updateViewImpl(); + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +bool CubeImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool success = BaseClass::init(mapImpl, logicalLayerName); + + // create underlying object (to save most attributes only there) + if (!m_mainGraphicalObject) + { + m_mainGraphicalObject = new GraphicalObjectCube(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QStringLiteral("#GOCube"), + m_uniqueMapId); + //this MapObject is not very typical, therefore set some attributes: + this->setPickable(false); + this->setScaleEnable(false); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return success; +} + + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/cube_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/cube_impl.h new file mode 100755 index 000000000..e2d1b4c51 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/cube_impl.h @@ -0,0 +1,76 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of DangerArea + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + + +// ESRI +#include + + +// Qt +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +/** + * @brief The CubeImpl class a cube if the UniqueValueRenderer is set. + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath displaying the main techn. Appearance, the cube) + * - mainGraphicalObjectText not used + * - tacticalAppearance not used + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT CubeImpl : public AbstractMultiPointMapObjectImpl, public virtual Cube +{ + using BaseClass = AbstractMultiPointMapObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit CubeImpl(const QString &debugInfo = QString()); + virtual ~CubeImpl(); + +public: + CubeImpl(const CubeImpl &other) = delete; + CubeImpl &operator=(CubeImpl &other) = delete; + + double height() const override; + void setHeight(double height) override; + + +protected: + bool updateViewImpl() override; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + + + // logical + + +private: + double m_height; + + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/danger_area_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/danger_area_impl.cpp new file mode 100755 index 000000000..cf0d6042f --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/danger_area_impl.cpp @@ -0,0 +1,105 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of DangerArea + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "danger_area_impl.h" +#include +#include +#include + +#include + +// ESRI + + +// Qt + + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class DangerAreaImpl +//----------------------------------------------------------------------------- +DangerAreaImpl::DangerAreaImpl(const QString &debugInfo) + : DangerArea() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("DangerAreaImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +DangerAreaImpl::~DangerAreaImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +bool DangerAreaImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " symbol=" << m_symbol.value().sidc + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + bool success = BaseClass::updateViewImpl(); + + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + + +bool DangerAreaImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (mapImpl) + { + switch (mapImpl->symbologyType()) + { + case AdvancedSymbology::SymbologyType::APP6D: + case AdvancedSymbology::SymbologyType::MIL2525D: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "10012500001511000000"); // SymbolSet = 25 151100 + break; + case AdvancedSymbology::SymbologyType::MIL2525C: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GUMPNC----"); // Image WITH Area // small icon and big border + break; + case AdvancedSymbology::SymbologyType::APP6B: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GUCPMGASM-"); // APP6B: 3556: GUCPMGASM- -> Polygon +// m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"GUCPBWC---"); // APP6B: 3939: GUCPBWC---- -> Polygon + break; + case AdvancedSymbology::SymbologyType::None: + // empty + break; + } // switch + } // if (mapImpl) + + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polygon); + + setLineStyle(LineStyle(Qt::yellow, 2)); + setFillStyle(FillStyle(Qt::yellow, FillPattern::Null)); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/danger_area_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/danger_area_impl.h new file mode 100755 index 000000000..12d7a7c76 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/danger_area_impl.h @@ -0,0 +1,68 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of DangerArea + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + + +// Qt +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +/** + * @brief The DangerAreaImpl class displays an area of undefined danger. + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath displaying the main techn. Appearance) + * - mainGraphicalObjectText (displaying the text/name) + * - tacticalAppearance (displaying the tact. Appearance) + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT DangerAreaImpl : public BasicPathObjectImpl, public virtual DangerArea +{ + using BaseClass = BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit DangerAreaImpl(const QString &debugInfo = QString()); + virtual ~DangerAreaImpl(); + +public: + DangerAreaImpl(const DangerAreaImpl &other) = delete; + DangerAreaImpl &operator=(DangerAreaImpl &other) = delete; + +protected: + bool updateViewImpl() override; + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + + // logical + + +private: + + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/functional_area_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/functional_area_impl.cpp new file mode 100755 index 000000000..e96844ff7 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/functional_area_impl.cpp @@ -0,0 +1,179 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of FunctionalArea + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "functional_area_impl.h" +#include +#include +#include +#include + + +// ESRI + + +// Qt + + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class FunctionalAreaImpl +//----------------------------------------------------------------------------- +FunctionalAreaImpl::FunctionalAreaImpl(const QString &debugInfo) + : FunctionalArea() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("FunctionalAreaImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + m_pointSymbolData.origin.setMapObjectImpl(this); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +FunctionalAreaImpl::~FunctionalAreaImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete m_pointSymbol; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void FunctionalAreaImpl::reRenderSymbol() +{ + BaseClass::reRenderSymbol(); + if (m_pointSymbol) + m_pointSymbol->reRenderSymbol(); +} + +Point3D FunctionalAreaImpl::pointSymbolOrigin() const +{ + return m_pointSymbolData.origin.origin(); +} +void FunctionalAreaImpl::setPointSymbolOrigin(const Point3D &pointSymbolOrigin) +{ + m_pointSymbolData.origin.setOrigin(pointSymbolOrigin); +} + +const QString &FunctionalAreaImpl::pointSymbolSidc() const +{ + return m_pointSymbolData.symbolData.sidc; +} +void FunctionalAreaImpl::setPointSymbolSidc(const QString &pointSymbolOriginSidc) +{ + m_pointSymbolData.symbolData.sidc = pointSymbolOriginSidc; +} + + +bool FunctionalAreaImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " symbol=" << m_symbol.value().sidc + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + bool success = BaseClass::updateViewImpl(); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + + if (!updateViewImplPointSymbolData()) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImplPointSymbolData failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool FunctionalAreaImpl::updateViewImplPointSymbolData() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + // sidc empty? + if (m_pointSymbolData.symbolData.sidc.isEmpty()) + { + delete m_pointSymbol; + m_pointSymbol = nullptr; + return true; + } + + // ok, sidc available: + if (!m_pointSymbol) + { + m_pointSymbol = new ParticipantImpl(m_debugInfo + QStringLiteral("#PntSymblTactApp")); + m_pointSymbol->init(m_mapImpl, m_logicalLayerName); // use same layer as mainGraphicalObj/TECHN. Appearance + m_pointSymbol->setUniqueId(m_uniqueMapId); + } + + if (m_party.partyId.changed()) + m_pointSymbol->setPartyIdChanged(m_party.partyId.value()); + + + // The drawOrder should be above the m_mainGraphicalObject (==Path) + m_pointSymbol->setDrawOrder(this->drawOrder() + 1); // since m_pointSymbol is a participant, use drawOrder instead of m_internalDrawOrder + + AbstractMapObjectImpl *amoImp = static_cast(m_pointSymbol); + amoImp->setVisibility((m_mainGraphicalObject) ? m_mainGraphicalObject->visibility() : false); + + m_pointSymbol->setOrigin(m_pointSymbolData.origin.origin()); + + m_pointSymbol->setPartyId(this->partyId()); + +// m_pointSymbolData.symbolData.color = this->partyColor(); + +// return m_mapImpl->partyIdToColorFunctor().partyIdToRole(m_party.partyId.value(), identityAssumed); + +// AdvancedSymbology::Role role = this->partyRole(); +// SymbolData symbolData = m_pointSymbolData.symbolData; +// symbolData = m_pointSymbolData.symbolData; + + m_pointSymbol->setSymbol(m_pointSymbolData.symbolData); + + m_pointSymbol->setScale(this->scale()); + + // add m_userSymbolOpacity??? + + bool success = m_pointSymbol->updateView(); + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed, m_debugInfo =" << m_debugInfo; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool FunctionalAreaImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + +// if (mapImpl) +// { +// switch (mapImpl->symbologyType()) { +// case AdvancedSymbology::SymbologyType::MIL2525C: +// m_symbol.refValue().sidc = QLatin1String("GUMPNC----"); // Image WITH Area // small icon and big border +// break; +// case AdvancedSymbology::SymbologyType::APP6B: +// m_symbol.refValue().sidc = QLatin1String("GUCPMGASM-"); // APP6B: 3556: GUCPMGASM- -> Polygon +// break; +// } // switch +// } // if (mapImpl) + + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polygon); + + setLineStyle(LineStyle(Qt::black, 2)); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/functional_area_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/functional_area_impl.h new file mode 100755 index 000000000..74424ea53 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/functional_area_impl.h @@ -0,0 +1,97 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of FunctionalArea + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + + +// Qt +#include + + + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +// forward: +class ParticipantImpl; + + +/** + * @brief The FunctionalAreaImpl class displays an functional area (e.g. medical area). + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath displaying the main techn. Appearance) + * - mainGraphicalObjectText (displaying the text/name) + * - tacticalAppearance (displaying the tact. Appearance) + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT FunctionalAreaImpl : public BasicPathObjectImpl, public virtual FunctionalArea +{ + using BaseClass = BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit FunctionalAreaImpl(const QString &debugInfo = QString()); + virtual ~FunctionalAreaImpl(); + +public: + FunctionalAreaImpl(const FunctionalAreaImpl &other) = delete; + FunctionalAreaImpl &operator=(FunctionalAreaImpl &other) = delete; + + void reRenderSymbol() override; + + /*! \name Point symbol */ + //@{ + Point3D pointSymbolOrigin() const override; + void setPointSymbolOrigin(const Point3D &pointSymbolOrigin) override; + + const QString &pointSymbolSidc() const override; + void setPointSymbolSidc(const QString &pointSymbolOriginSidc) override; + //@} + + +protected: + bool updateViewImpl() override; + + bool updateViewImplPointSymbolData(); + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + + // logical + + +private: + + // logical + struct PointSymbolData + { + OriginTranslated origin; + SymbolData symbolData; + }; + + PointSymbolData m_pointSymbolData; + ParticipantImpl *m_pointSymbol = nullptr; + + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/geo_image_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/geo_image_impl.cpp new file mode 100755 index 000000000..88ffbf696 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/geo_image_impl.cpp @@ -0,0 +1,371 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of GeoImage + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "geo_image_impl.h" +#include +#include +#include + + +// ESRI + + +// Qt +#include +#include +#include +#include +#include + + +namespace SituationMap +{ + + +class GeoImageImpl::Private +{ +public: + Private() = default; + ~Private() = default; + + // ESRI data: + + // Own data: + SimpleValue m_geoImageData; + QSharedPointer m_gopmPin; + + +}; // class GeoImageImpl::Private +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// class GeoImageImpl +//----------------------------------------------------------------------------- +GeoImageImpl::GeoImageImpl(const QString &debugInfo) + : GeoImage() // Interface + , BaseClass(debugInfo) // Implementation + , d(new Private) + , m_logger(getLogger("GeoImageImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +GeoImageImpl::~GeoImageImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete d; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +QString GeoImageImpl::toString() const +{ + QString str; + str.append(BaseClass::toString()); + str.append(d->m_geoImageData.value().toString()); + return str; +} + +GeoImage::GeoImageData GeoImageImpl::geoImageData() const { return d->m_geoImageData.value(); } +void GeoImageImpl::setGeoImageData(const GeoImageData &geoImageData) { d->m_geoImageData = geoImageData; } + +MarkerSymbolType GeoImageImpl::proxyMarker() const { return d->m_geoImageData.value().proxyMarker; } + +void GeoImageImpl::setProxyMarker(const MarkerSymbolType &proxyMarker) +{ + if (d->m_geoImageData.value().proxyMarker != proxyMarker) + d->m_geoImageData.refValue().proxyMarker = proxyMarker; +} + +int GeoImageImpl::proxyMarkerSize() const { return d->m_geoImageData.value().proxyMarkerSize; } +void GeoImageImpl::setProxyMarkerSize(int proxyMarkerSize) +{ + if (d->m_geoImageData.value().proxyMarkerSize != proxyMarkerSize) + d->m_geoImageData.refValue().proxyMarkerSize = proxyMarkerSize; +} + +QColor GeoImageImpl::proxyMarkerColor() const { return d->m_geoImageData.value().proxyMarkerColor; } +void GeoImageImpl::setProxyMarkerColor(const QColor &proxyMarkerColor) +{ + if (d->m_geoImageData.value().proxyMarkerColor != proxyMarkerColor) + d->m_geoImageData.refValue().proxyMarkerColor = proxyMarkerColor; +} + + +QImage GeoImageImpl::proxyImage() const { return d->m_geoImageData.value().proxyImage; } +void GeoImageImpl::setProxyImage(const QImage &proxyImage, const QSize &proxyImageDefaultSize) +{ + if (d->m_geoImageData.value().proxyImage != proxyImage) + d->m_geoImageData.refValue().proxyImage = proxyImage; + + if (d->m_geoImageData.value().proxyImageDefaultSize != proxyImageDefaultSize) + d->m_geoImageData.refValue().proxyImageDefaultSize = proxyImageDefaultSize; +} +QSize GeoImageImpl::proxyImageDefaultSize() const { return d->m_geoImageData.value().proxyImageDefaultSize; } + + +QImage GeoImageImpl::mainImage() const { return d->m_geoImageData.value().mainImage; } + +void GeoImageImpl::setMainImage(const QImage &mainImage) +{ + if (d->m_geoImageData.value().mainImage != mainImage) + d->m_geoImageData.refValue().mainImage = mainImage; +} + +QImage GeoImageImpl::previewImageTypical() const +{ + if (!d->m_geoImageData.value().proxyImage.isNull()) + return d->m_geoImageData.value().proxyImage; + if (!d->m_geoImageData.value().mainImage.isNull()) + return d->m_geoImageData.value().mainImage; + return QImage(); +} + +QString GeoImageImpl::description() const { return d->m_geoImageData.value().description; } + +void GeoImageImpl::setDescription(const QString &description) +{ + if (d->m_geoImageData.value().description != description) + d->m_geoImageData.refValue().description = description; +} + +void GeoImageImpl::setPinEnabled(bool enablePin) +{ + if (d->m_geoImageData.value().pinEnabled != enablePin) + d->m_geoImageData.refValue().pinEnabled = enablePin; +} +bool GeoImageImpl::isPinEnabled() const { return d->m_geoImageData.value().pinEnabled; } + + +QWidget *GeoImageImpl::createWidget(QWidget *parent) const +{ + const GeoImageData &geoImageData = d->m_geoImageData.value(); + + if (geoImageData.mainImage.isNull()) + return nullptr; + + QWidget *widget = new QWidget(parent); + widget->setLayout(new QVBoxLayout()); + + QLabel *img = new QLabel(); + img->setPixmap(QPixmap::fromImage(geoImageData.mainImage)); + widget->layout()->addWidget(img); + + widget->layout()->addWidget(new QLabel(geoImageData.description)); + + + // CloseButton + QPushButton *pbClose = new QPushButton(QLatin1String("Close")); + widget->layout()->addWidget(pbClose); + + pbClose->connect(pbClose, &QPushButton::clicked, widget, [widget]() + { + widget->deleteLater(); + }); + + return widget; +} + + +bool GeoImageImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " origin =" << this->origin() //.x() << ", " << this->origin().y() + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + if (!m_graphicsLayer) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no graphicsLayer"; + return false; + } + + if (!m_mainGraphicalObject) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no m_mainGraphicalObject"; + return false; + } + + // Marker: + GraphicalObjectPictureMarker *pMarker = static_cast(m_mainGraphicalObject); + + if (d->m_geoImageData.changed()) + { + const GeoImageData &geoImageData = d->m_geoImageData.value(); + + if (geoImageData.proxyImage.isNull()) + { + if (geoImageData.proxyMarker == MarkerSymbolType::Undefined) + pMarker->setMarkerSymbol(MarkerSymbolType::Diamond, 20, Qt::red); + else + pMarker->setMarkerSymbol(geoImageData.proxyMarker, + geoImageData.proxyMarkerSize, + geoImageData.proxyMarkerColor); + } + else + pMarker->setImage(geoImageData.proxyImage, geoImageData.proxyImageDefaultSize); + + // Pin + if (m_mapImpl->mode() == Mode::Mode_3D) + { + if (geoImageData.pinEnabled) + createPin(); + else + resetPin(); + } + } + + updateIdentifierPosition(); + + pMarker->setOrigin(this->originTranslated()); + + + bool success = BaseClass::updateViewImpl(); + + // reset changed: + d->m_geoImageData.reset(); + + onMapExtentChanged(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + + +bool GeoImageImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool success = BaseClass::init(mapImpl, logicalLayerName); + + if (success) + { + // create underlying object (to save most attributes only there) + if (!m_mainGraphicalObject) + m_mainGraphicalObject = new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QLatin1String("#Image"), + m_uniqueMapId); + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool GeoImageImpl::onMapExtentChanged() +{ + TRACE_LOG(m_logger) << " onMapExtentChanged"; + + if (m_graphicalObjectPin && m_mainGraphicalObject) // m_graphicalObjectPin exists only at Mode_3D + { + if (!d->m_gopmPin) + { + d->m_gopmPin = QSharedPointer + (new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView() + , m_graphicsLayer + , m_debugInfo + QStringLiteral("#PictureMarkerPin") + , m_uniqueMapId) + ); + + GraphicalObjectPictureMarker *pMarker = static_cast(m_mainGraphicalObject); + if (pMarker) + { + const GeoImageData &geoImageData = d->m_geoImageData.value(); + d->m_gopmPin->setImage(pMarker->image(), geoImageData.proxyImageDefaultSize); + } + } + + d->m_gopmPin->setScale(m_mainGraphicalObject->scale()); + + Point3D originTranslated = this->originTranslated(); + DEBUG_LOG(m_logger) << " XXX originTranslatedORG = " << originTranslated; + + // GUEZE-2620 3D Sicht SOS Symbole + // Bisher wurde die Höhe des SOS-Images und -Textes quasi getuned, wobei ja nun + // unter bestimmten Umständen leider zu einem Höhenwert ausserhalb des + // Sichtfensters kam. + // Daher: + // - Pflicht: SOS-Symbol und Text auf den Boden + // - Kuer: Zudem versucht die SW, eine zweite Instanz des Images + // (inkl. Verbindungslinie) zu positionieren. + // Und zwar grob mittig zw. Erdoberfläche und oberen Kante des Sichtbereichs. + // Leider funktioniert des dafür genutzte ESRI-Befehl nicht immer zuverlässig. + // Meist dann, wenn sich der Augenpunkt (ViewPoint) dicht am Objekt befinden... + // Noch ein bisschen Tuning, aber es verbleiben leider Fehlersituationen. + + if (m_mapImpl->scale() > 1500) + { + const double zVal = m_mapImpl->scale() * m_mapImpl->scale() * 0.0001; + DEBUG_LOG(m_logger) << " zVal = " << zVal; + + originTranslated.rz() = zVal; + + // 1 ... 10000 (qBound ~ std::clamp) + originTranslated.rz() = qBound(1.0, originTranslated.rz(), 10000.0); + + // check viewPort: + // Note: unfortunately its dows not work perfect :-( + int cnt = 0; + static int s_counterMax = 10; + while (m_mapImpl->mapCoordinateToViewPortWindowPoint3D(originTranslated).isNull() && (originTranslated.rz() > 1) && (cnt <= s_counterMax)) + { + originTranslated.rz() /= 2.0; + ++cnt; + } + + if (cnt == s_counterMax) + originTranslated.rz() = 0; + else + originTranslated.rz() *= 0.5; // try to show in the middle between baseSurface and upper edge of window + + d->m_gopmPin->setVisibility(true); + + } else { + // GUEZE-3460 - SOS Meldungen werden nicht korrekt angezeigt + if (m_mapImpl->scale() < 500) + originTranslated.rz() = 1.0; // meter + else if (m_mapImpl->scale() < 1000) + originTranslated.rz() = 2.5; // meter + else + originTranslated.rz() = 10.0; // meter + + d->m_gopmPin->setVisibility(true); + } + + DEBUG_LOG(m_logger) << " originTranslated = " << originTranslated; + + d->m_gopmPin->setOrigin(originTranslated); + if (!d->m_gopmPin->updateView()) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed" << m_mainGraphicalObject->debugInfo(); + + // update m_identifierPositionCandidateTranslated , + // it will be transfered to m_mainGraphicalObject via BaseClass::onMapExtentChanged() + // m_identifierPositionCandidateTranslated = originTranslated; + + Point3D centerEarth = originTranslated; + centerEarth.rz() = -100; // hopefully lower than lowest level of training area + m_graphicalObjectPin->setPositions({originTranslated, centerEarth}); + + m_graphicalObjectPin->setVisibility(d->m_gopmPin->visibility()); + + if (!m_graphicalObjectPin->updateView()) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateView failed" << m_graphicalObjectPin->debugInfo(); + + } // if (m_graphicalObjectPin && m_mainGraphicalObject) + + return BaseClass::onMapExtentChanged(); +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/geo_image_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/geo_image_impl.h new file mode 100755 index 000000000..082cd59d1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/geo_image_impl.h @@ -0,0 +1,101 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of GeoImage + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_single_point_map_object_impl.h" +#include // Interface + + +// Qt + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT GeoImageImpl : public AbstractSinglePointMapObjectImpl, public virtual GeoImage +{ + using BaseClass = AbstractSinglePointMapObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit GeoImageImpl(const QString &debugInfo = QString()); + virtual ~GeoImageImpl(); + +public: + + GeoImageImpl(const GeoImageImpl &other) = delete; + GeoImageImpl &operator=(GeoImageImpl &other) = delete; + + QString toString() const override; + + + GeoImageData geoImageData() const override; + void setGeoImageData(const GeoImageData &geoImageData) override; + + + MarkerSymbolType proxyMarker() const override; + void setProxyMarker(const MarkerSymbolType &proxyMarker) override; + + int proxyMarkerSize() const override; + void setProxyMarkerSize(int proxyMarkerSize) override; + + QColor proxyMarkerColor() const override; + void setProxyMarkerColor(const QColor &proxyMarkerColor) override; + + QImage proxyImage() const override; + QSize proxyImageDefaultSize() const override; + + void setProxyImage(const QImage &proxyImage, const QSize &proxyImageDefaultSize) override; + + + QImage mainImage() const override; + void setMainImage(const QImage &mainImage) override; + + QImage previewImageTypical() const override; + + + QString description() const override; + void setDescription(const QString &description) override; + + void setPinEnabled(bool enablePin) override; + bool isPinEnabled() const override; + + QWidget *createWidget(QWidget *parent = nullptr) const override; + + +protected: + virtual bool updateViewImpl() override; + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + bool onMapExtentChanged() override; + + // internal/admin + + // logical + class Private; + /** + * The private d-pointer to not break BC when changing internals of this class + */ + Private *d = nullptr; + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/hit_line_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/hit_line_impl.cpp new file mode 100755 index 000000000..e067047a5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/hit_line_impl.cpp @@ -0,0 +1,177 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of HitLine + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "hit_line_impl.h" +#include +#include +#include + +#include + +// ESRI + + +// Qt + + +#include + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class HitLineImpl +//----------------------------------------------------------------------------- +HitLineImpl::HitLineImpl(const QString &debugInfo) + : HitLine() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("HitLineImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo=" << m_debugInfo; + + setArrowHeadStart(true); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo=" << m_debugInfo; +} + +HitLineImpl::~HitLineImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo=" << m_debugInfo; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo=" << m_debugInfo; +} + +QString HitLineImpl::identifier() const +{ + return this->textOnSegment(0); +} +void HitLineImpl::setIdentifier(const QString &identifier) +{ + this->setTextOnSegment(0, identifier); +} + +Point3D HitLineImpl::startPoint() const +{ + // !!! see setStartAndEndPoint() !!! + + const PositionCont &pos = m_positions.positions(); + if (pos.size() < 2) + return Point3D(); + else + return pos[1]; // !!! see setStartAndEndPoint() !!! +} +Point3D HitLineImpl::endPoint() const +{ + // !!! see setStartAndEndPoint() !!! + + const PositionCont &pos = m_positions.positions(); + if (pos.size() < 2) + return Point3D(); + else + return pos[0]; // !!! see setStartAndEndPoint() !!! +} + + +void HitLineImpl::setStartAndEndPoint(const Point3D &start, const Point3D &end) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " start=" << start + << " end=" << end + << ", debugInfo=" << m_debugInfo; + + // According MIL2525 a line (like attack) starts with an arrow. + // But in this context (HitLine) + // start == shooter -> no arrowHead + // end == target -> with arrowHead + + // Therefore we have to add the 'end'/target point first: + m_positions.setPositions(PositionCont() << end << start); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +bool HitLineImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " symbol=" << m_symbol.value().sidc + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical + << ", debugInfo=" << m_debugInfo; + + + setArrowHeadsSelected(m_selected); + + bool success = BaseClass::updateViewImpl(); + + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + + +bool HitLineImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (mapImpl) + { + switch (mapImpl->symbologyType()) + { + case AdvancedSymbology::SymbologyType::APP6D: + case AdvancedSymbology::SymbologyType::MIL2525D: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "10032500001406030000"); // SymbolSet = 25 140602 + // ^^ ^^^^^^ + break; + case AdvancedSymbology::SymbologyType::MIL2525C: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GFGPOLKGS-"); // 1047 - Direction Of Attack Ground Supporting Attack F +// m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"GHGPOLKA--"); // +// m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"GHGPOLKGM-"); // + + break; + case AdvancedSymbology::SymbologyType::APP6B: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GFCPMOLDS-"); // APP6B-3733 GFCPMOLDS- -> Direction Of Attack Ground Supporting Attack F + break; + case AdvancedSymbology::SymbologyType::None: + // empty + break; + } // switch + } // if (mapImpl) + + + double maxSegmentLength = -1; // -> GeometryEngine::densifyGeodetic + if (mapImpl && (mapImpl->mode() == Mode::Mode_3D)) + maxSegmentLength = 10; // -> GeometryEngine::densifyGeodetic + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polyline, maxSegmentLength); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + m_mainGraphicalObject->rLineStyle().setLinePattern(LinePattern::Dash); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +bool HitLineImpl::setPositions(const PositionCont &) +{ + FATAL_LOG(m_logger) << LOGFUNCTION << " DO NOT CALL!!!"; + assert(false); + return false; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/hit_line_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/hit_line_impl.h new file mode 100755 index 000000000..2883adac6 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/hit_line_impl.h @@ -0,0 +1,82 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of HitLine + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + + +// Qt +#include +#include + + + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +/** + * @brief The HitLineImpl class displays shot from a startpoint to an endpoint. + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath displaying the main techn. Appearance) + * - mainGraphicalObjectText (displaying the text/name/identifier) + * - tacticalAppearance (displaying the tact. Appearance) + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT HitLineImpl : public BasicPathObjectImpl, public virtual HitLine +{ + using BaseClass = BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit HitLineImpl(const QString &debugInfo = QString()); + virtual ~HitLineImpl(); + +public: + HitLineImpl(const HitLineImpl &other) = delete; + HitLineImpl &operator=(HitLineImpl &other) = delete; + + QString identifier() const override; + void setIdentifier(const QString &identifier) override; + + Point3D startPoint() const override; + Point3D endPoint() const override; + + void setStartAndEndPoint(const Point3D &start, const Point3D &end) override; + + +protected: + bool updateViewImpl() override; + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + + // logical + +private: + bool setPositions(const PositionCont &) override; //SHOULD NEVER BE CALLED + +private: + + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/indication_container.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/indication_container.cpp new file mode 100755 index 000000000..b48492442 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/indication_container.cpp @@ -0,0 +1,575 @@ +/** + * @RDE_HEADER@ + * IndicationContainer + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "indication_container.h" +#include +#include +#include + + +// ESRI +#include +#include + +// Qt +#include + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class IndicationData +//----------------------------------------------------------------------------- +class IndicationData +{ +public: + const QImage &image() const { return m_image;} + void setImage(const QImage &image) { if (m_image != image) m_changed = true; m_image = image; } + + MarkerSymbolType markerSymbolType() const { return m_markerSymbolType; } + void setMarkerSymbolType(const MarkerSymbolType &markerSymbolType) { if (m_markerSymbolType != markerSymbolType) m_changed = true; m_markerSymbolType = markerSymbolType; } + + const QColor &markerColor() const { return m_markerColor; } + void setMarkerColor(const QColor &markerColor) { if (m_markerColor != markerColor) m_changed = true; m_markerColor = markerColor; } + + int markerSize() const { return m_markerSize; } + void setMarkerSize(int markerSize) { if (m_markerSize != markerSize) m_changed = true; m_markerSize = markerSize; } + + bool markerSelected() const { return m_markerSelected; } + void setMarkerSelected(bool markerSelected) { if (m_markerSelected != markerSelected) m_changed = true; m_markerSelected = markerSelected; } + + bool markerPickable() const { return m_markerPickable; } + void setMarkerPickable(bool markerPickable) { if (m_markerPickable != markerPickable) m_changed = true; m_markerPickable = markerPickable; } + + PositionsTranslated &positionsTrans() { return m_positions; } + void setPositions(const PositionCont &positions) { if (m_positions.positions() != positions) m_changed = true; m_positions.setPositions(positions); } + + bool markerVisible() const { return m_markerVisible; } + void setMarkerVisible(bool markerVisible) { if (m_markerVisible != markerVisible) m_changed = true; m_markerVisible = markerVisible; } + + + + bool changed() const { return m_changed; } + void resetChanged() { m_changed = false; } + + IndicationData() + {} + + ~IndicationData() + { + m_pictureMarker.clear(); + } + +public: + QSharedPointer m_coordTranslationThread; + QSharedPointer m_pictureMarker; + +private: + QImage m_image; + + PositionsTranslated m_positions; + MarkerSymbolType m_markerSymbolType = MarkerSymbolType::Undefined; + int m_markerSize = 20; + QColor m_markerColor = Qt::black; + bool m_markerVisible = true; + bool m_markerSelected = false; + bool m_markerPickable = true; + bool m_changed = true; +}; + + +class IndicationContainer::Private +{ +public: + Private() = default; + ~Private() = default; + + // ESRI data: + Esri::ArcGISRuntime::GeoView *m_geoView = nullptr; + Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr; + + + // Own data: + CoordTranslationFunctor *m_coordTransFunctor = nullptr; + QString m_debugInfo; // will be appended by consecutive number + UniqueMapId m_uniqueMapId; + + SimpleValue m_drawOrder = 0; + +private: + // TODO: use sorted vector - only max. 10 elements in there, so qhash/qmap is overkill + QHash> m_map; + +public: + QSharedPointer getIndicationData(int key) + { + auto it = m_map.find(key); + if (it != m_map.end()) + return it.value(); + auto ret = QSharedPointer(new IndicationData()); + m_map[key] = ret; + return ret; + } + + void remove(int key) // attention, will remove the provides image/marker... + { + auto it = m_map.find(key); + if (it == m_map.end()) + return; + const QSharedPointer &indicationData = it.value(); + if (indicationData && indicationData->m_coordTranslationThread) + indicationData->m_coordTranslationThread->wait(); + m_map.erase(it); + } + void clear() { m_map.clear(); } + int size() const { return m_map.size(); } + QList keys() const { return m_map.keys(); } + bool contains(int key) const { return m_map.contains(key); } + const QHash> &container() const { return m_map; } + +}; // class IndicationContainer::Private +//----------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- +// class IndicationContainer +//----------------------------------------------------------------------------- +IndicationContainer::IndicationContainer(const Esri::ArcGISRuntime::GeoView *geoView, + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer, + CoordTranslationFunctor *coordTransFunctor, + const UniqueMapId &uniqueMapId) + : QObject() + , m_logger(getLogger("IndicationContainer")) + , d(new Private) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + d->m_geoView = const_cast(geoView); + d->m_graphicsLayer = graphicsLayer; + d->m_coordTransFunctor = coordTransFunctor; + d->m_uniqueMapId = uniqueMapId; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +IndicationContainer::~IndicationContainer() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + // Thread::wait(): + for (const QSharedPointer &id : d->container()) + { + if (id && id->m_coordTranslationThread) + { + id->m_coordTranslationThread->quit(); + id->m_coordTranslationThread->wait(); + } + } // for + + + d->clear(); + delete d; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +QString IndicationContainer::debugInfo() const +{ + return d->m_debugInfo; +} +void IndicationContainer::setDebugInfo(const QString &debugInfo) +{ + d->m_debugInfo = debugInfo; +} + + +PositionCont IndicationContainer::positions(int idx) const +{ + return d->getIndicationData(idx)->positionsTrans().positions(); +} +void IndicationContainer::setPositions(int idx, const PositionCont &positions) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << ", debugInfo='" << d->m_debugInfo << "'" + << ", m_uniqueMapId = " << d->m_uniqueMapId + << ", idx=" << idx + << ", positions.size=" << positions.size(); + + d->getIndicationData(idx)->setPositions(positions); + + // // d->remove(idx); // attention: remove will implicit rmove the provided image... + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void IndicationContainer::setMarker(int idx, MarkerSymbolType mst, int size, const QColor &color) +{ + d->getIndicationData(idx)->setMarkerSymbolType(mst); + d->getIndicationData(idx)->setMarkerSize(size); + d->getIndicationData(idx)->setMarkerColor(color); +} + +void IndicationContainer::setMarker(int idx, int size) +{ + d->getIndicationData(idx)->setMarkerSize(size); +} + +void IndicationContainer::setMarker(int idx, const QColor &color) +{ + d->getIndicationData(idx)->setMarkerColor(color); +} + + +const QImage &IndicationContainer::image(int idx) const +{ + return d->getIndicationData(idx)->image(); +} +void IndicationContainer::setImage(int idx, const QImage &image) +{ + auto id = d->getIndicationData(idx); + + if (id->image() == image) + return; + id->setImage(image); +} + +void IndicationContainer::clear(int idx) +{ + d->remove(idx); +} + +void IndicationContainer::clearAll() +{ + d->clear(); +} + +int IndicationContainer::drawOrder() const +{ + return d->m_drawOrder.value(); +} +void IndicationContainer::setDrawOrder(int drawOrder) +{ + d->m_drawOrder = drawOrder; +} + +bool IndicationContainer::visible(int idx) const +{ + return d->getIndicationData(idx)->markerVisible(); +} +void IndicationContainer::setVisible(int idx, bool visible) +{ + d->getIndicationData(idx)->setMarkerVisible(visible); +} + +bool IndicationContainer::selected(int idx) const +{ + return d->getIndicationData(idx)->markerSelected(); +} +void IndicationContainer::setSelected(int idx, bool selected) +{ + d->getIndicationData(idx)->setMarkerSelected(selected); +} + +bool IndicationContainer::pickable(int idx) const +{ + return d->getIndicationData(idx)->markerPickable(); +} + +void IndicationContainer::setPickable(int idx, bool pickable) +{ + d->getIndicationData(idx)->setMarkerPickable(pickable); +} + + + +int IndicationContainer::getIndicationCount() const +{ + return d->size(); +} + +QList IndicationContainer::getIndicationIndexList() const +{ + return d->keys(); +} + + +bool IndicationContainer::updateView() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << ", debugInfo='" << d->m_debugInfo << "'" + << ", m_uniqueMapId = " << d->m_uniqueMapId + << " , d->m_map.size()=" << d->size(); + bool success = true; + + +// int outterSize = d->m_map.size(); + + for (auto it = d->container().begin(); it != d->container().end(); ++it) + { + const QSharedPointer &indicationData = it.value(); + // per single dimension: + + // nothing changed? + if ((!indicationData->changed()) && (!d->m_drawOrder.changed())) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " no change -> continue... " + << ", debugInfo='" << d->m_debugInfo << "'"; + continue; + } + + // set + indicationData->positionsTrans().setCoordTransFunctor(d->m_coordTransFunctor); + + int idx = it.key(); + +#define SYNC +#ifdef SYNC + bool updated = updateIndicationContainer(idx); + success = success && updated; + +#else + + // skip thread and do call sync if: + if (indicationData->positionsTrans().isTranslated() + || ! indicationData->markerVisible() + ) + { + // call sync: + updateIndicationContainer(idx); // update of invisible marker is cheap :-) + } + else + { + + // ok, thread is requested: + + // another thread prepared/running or finished/deleted? + if (indicationData->m_coordTranslationThread.isNull()) + { + // move time consuming work to worker thread: + // apply Deleter to SharedPointer (to avoid simple 'delete') + indicationData->m_coordTranslationThread = + QSharedPointer (new CoordTranslationThread(d->m_debugInfo), &QObject::deleteLater); + + indicationData->m_coordTranslationThread->setUserData(idx); + indicationData->m_coordTranslationThread->setPositions(& (indicationData->positionsTrans())); + + // QObject::connect(indicationData.m_coordTranslationThread.data(), &CoordTranslationThread::finished, indicationData.m_coordTranslationThread.data(), &QObject::deleteLater); + QObject::connect(indicationData->m_coordTranslationThread.data(), &CoordTranslationThread::finished, this, &IndicationContainer::onThreadFinished); + QObject::connect(indicationData->m_coordTranslationThread.data(), &CoordTranslationThread::resultReady, this, &IndicationContainer::onResultReady); + + qWarning() << " start workerThread, idx=" << idx; + indicationData->m_coordTranslationThread->start(); + + } + else + { + FATAL_LOG(m_logger) << LOGFUNCTION + << " new Translation via Thread requested, but still exists..."; + } + + } +#endif // SYNC + + indicationData->resetChanged(); + + } // while + d->m_drawOrder.reset(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo='" << d->m_debugInfo << "'" + << " success=" << success; + return success; +} + +bool IndicationContainer::onResultReady(const QVariant &userData) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo='" << d->m_debugInfo << "'" + << "threadId: " << QThread::currentThreadId(); + + bool success = false; + + bool bOk = false; + int idx = userData.toInt(&bOk); + if (bOk) + { + if (d->contains(idx)) + { + QSharedPointer indicationData = d->getIndicationData(idx); + if (! indicationData->positionsTrans().isTranslated()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " NOT TRANSLATED, should not happen!!!" + << " debugInfo='" << d->m_debugInfo << "'" + << " idx=" << idx; + } + } + + success = updateIndicationContainer(idx); + + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " QVariant toInt failed!!!"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo='" << d->m_debugInfo << "'" + << " success = " << success; + return success; +} + +void IndicationContainer::onThreadFinished(const QVariant &userData) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo='" << d->m_debugInfo << "'"; + bool bOk = false; + int idx = userData.toInt(&bOk); + if (bOk) + { + if (d->contains(idx)) + d->getIndicationData(idx)->m_coordTranslationThread.clear(); // will call the Deleter which is 'deleteLater' + } + else + qWarning() << "resultReady: QVariant toInt failed!!!"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo='" << d->m_debugInfo << "'"; +} + + +bool IndicationContainer::updateIndicationContainer(int idx) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo='" << d->m_debugInfo << "'" + << " idx=" << idx; + + QElapsedTimer timer; + + if (m_logger.isInfoEnabled()) + timer.start(); + + if (! d->contains(idx)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " idx does not exist: idx=" << idx; + return false; + } + + bool successPM = true; + QSharedPointer indicationData = d->getIndicationData(idx); + + // do we have positions or is PositionCont empty? + const PositionsTranslated &posTransRef = indicationData->positionsTrans(); + int numberOfPositions = posTransRef.size(); + if (posTransRef.isEmpty()) + { + QSharedPointer &picMarker = indicationData->m_pictureMarker; + picMarker.clear(); + } + else + { + successPM = updateIndicationContainerPictureMarker(idx); + } + + bool success = successPM; + + INFO_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo='" << d->m_debugInfo << "'" + << " success=" << success + << " successPM=" << successPM + << " numberOfPositions = " << numberOfPositions + << " took[ms] " << timer.elapsed(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo='" << d->m_debugInfo << "'" + << " numberOfPositions = " << numberOfPositions + << " successPM=" << successPM + << " success=" << success; + return success; +} + +bool IndicationContainer::updateIndicationContainerPictureMarker(int idx) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " debugInfo='" << d->m_debugInfo << "'" + << " idx=" << idx; + QElapsedTimer timer; + + if (m_logger.isInfoEnabled()) + timer.start(); + + bool success = true; + int numberOfPositions = -1; + + QSharedPointer indicationData = d->getIndicationData(idx); + + QSharedPointer &picMarker = indicationData->m_pictureMarker; + + const MarkerSymbolType &mst = indicationData->markerSymbolType(); + const bool bPicMarkerNeeded = (mst != MarkerSymbolType::Undefined) || !indicationData->image().isNull(); + + if (bPicMarkerNeeded) + { + if (picMarker.isNull()) + { + QString debugInfo = QStringLiteral("%1#Indication_idx(%2)_PM").arg(d->m_debugInfo).arg(idx); + DEBUG_LOG(m_logger) << LOGFUNCTION << " create new Object: " << debugInfo; + picMarker = QSharedPointer + (new GraphicalObjectPictureMarker(d->m_geoView, + d->m_graphicsLayer, + debugInfo, + d->m_uniqueMapId)); + } + + // set parameter: + + // [GUEZE-496] RPL Nr 770: 3D Lagedarstellung Einfrieren der SW Appl. + // GraphicalObjectPictureMarker supports multipoint geometry now + + // do we have an mst or image? + if (mst != MarkerSymbolType::Undefined) + { + // Marker: + picMarker->setMarkerSymbol(mst, + indicationData->markerSize(), + indicationData->markerColor()); + } + else + { + // Image: + picMarker->setImage(indicationData->image()); + } + + bool visible = indicationData->markerVisible(); + picMarker->setDrawOrder(d->m_drawOrder.value()); + picMarker->setVisibility(visible); + picMarker->setSelected(indicationData->markerSelected()); + picMarker->setPickable(indicationData->markerPickable()); + + // Geometry/Positions + const PositionCont &positionsTrans = indicationData->positionsTrans().positionsTranslated(); + picMarker->setPositions(positionsTrans); + numberOfPositions = positionsTrans.size(); + + success = picMarker->updateView(); + } else { + picMarker.clear(); + } + + INFO_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo='" << d->m_debugInfo << "'" + << " success=" << success + << " numberOfPositions = " << numberOfPositions + << " took[ms] " << timer.elapsed(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo='" << d->m_debugInfo << "'" + << " numberOfPositions = " << numberOfPositions + << " success=" << success; + return success; +} + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/indication_container.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/indication_container.h new file mode 100755 index 000000000..56b19e82e --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/indication_container.h @@ -0,0 +1,115 @@ +/** + * @RDE_HEADER@ + * IndicationContainer + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include +#include + + +// Qt +#include +#include +#include + + +// forward: +namespace Esri +{ +namespace ArcGISRuntime +{ +class GeoView; +class GraphicsOverlay; +} +} + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +// Forward: +class CoordTranslationFunctor; +class IndicationData; + + +class IndicationContainer : public QObject +{ + Q_OBJECT + +public: + explicit IndicationContainer(const Esri::ArcGISRuntime::GeoView *geoView, + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer, + CoordTranslationFunctor *coordTransFunctor, + const UniqueMapId &uniqueMapId); + virtual ~IndicationContainer(); + + IndicationContainer(const IndicationContainer &other) = delete; + IndicationContainer &operator=(IndicationContainer &other) = delete; + + QString debugInfo() const; + void setDebugInfo(const QString &debugInfo); + + // Marker : + + virtual PositionCont positions(int idx) const; + virtual void setPositions(int idx, const PositionCont &positions); + + virtual void setMarker(int idx, MarkerSymbolType mst, int size, const QColor &color); + virtual void setMarker(int idx, int size); + virtual void setMarker(int idx, const QColor &color); + + virtual const QImage &image(int idx) const; + virtual void setImage(int idx, const QImage &image); + + virtual void clear(int idx); + virtual void clearAll(); + + virtual int drawOrder() const; + virtual void setDrawOrder(int drawOrder); + + virtual bool visible(int idx) const; + virtual void setVisible(int idx, bool visible); + + virtual bool selected(int idx) const; + virtual void setSelected(int idx, bool selected); + + virtual bool pickable(int idx) const; + virtual void setPickable(int idx, bool pickable); + + + virtual int getIndicationCount() const; + virtual QList getIndicationIndexList() const; + + + virtual bool updateView(); + virtual bool updateIndicationContainer(int idx); + virtual bool updateIndicationContainerPictureMarker(int idx); +// virtual bool updateIndicationContainerSceneMarker(int idx); + +public slots: + virtual void onThreadFinished(const QVariant &userData); + virtual bool onResultReady(const QVariant &userData); + + +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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/line_of_sight_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/line_of_sight_impl.cpp new file mode 100755 index 000000000..2536710d1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/line_of_sight_impl.cpp @@ -0,0 +1,205 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of HitLine + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "line_of_sight_impl.h" +#include +#include +#include + +#include + +// ESRI + + +// Qt + + +#include + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class LineOfSightImpl +//----------------------------------------------------------------------------- +LineOfSightImpl::LineOfSightImpl(const QString &debugInfo) + : LineOfSight() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("LineOfSightImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + m_extensionLineCoordinate.setMapObjectImpl(this); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +LineOfSightImpl::~LineOfSightImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete m_extensionLine; + m_extensionLine = nullptr; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +QString LineOfSightImpl::identifier() const +{ + return this->textOnSegment(0); +} +void LineOfSightImpl::setIdentifier(const QString &identifier) +{ + this->setTextOnSegment(0, identifier); +} + +void LineOfSightImpl::setPoints(const Point3D &observer, + const Point3D &obstacle, + const Point3D &target, + const QColor &visibleColor, // green, + const QColor &obstructedColor) // red +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " observer=" << observer + << " obstacle=" << obstacle + << " target=" << target + << " visibleColor=" << visibleColor + << " obstructedColor=" << obstructedColor; + + // some checks + if (observer.isNull() || target.isNull()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " start of end is null"; + return; + } + + if (obstacle.isNull()) + { + // no obstacle -> just a single (green) line: + m_positions.setPositions(PositionCont() << observer << target); + m_extensionLineCoordinate.clear(); + } + else + { + // obstacle -> first (green) line: + m_positions.setPositions(PositionCont() << observer << obstacle); + m_extensionLineCoordinate.setOrigin(target); + } + + // handle color: + if (visibleColor.isValid()) + { + LineStyle ls = this->lineStyle(); + ls.setLineColor(visibleColor); + this->setLineStyle(ls); + } + + if (obstructedColor.isValid()) + m_extensionLineColor = obstructedColor; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +bool LineOfSightImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " symbol=" << m_symbol.value().sidc + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical + << " m_positions=" << m_positions.positions() + << " m_additionalCoordinate=" << m_extensionLineCoordinate.origin(); + + // + // + // maybe the line(s) are densified @ GraphicalObjectPath::updateViewImpl() + // to use more vertices (for 3D) !? + // + // + + + bool success = BaseClass::updateViewImpl(); + + updateExtensionLine(); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +void LineOfSightImpl::updateExtensionLine() +{ + if (!m_extensionLineCoordinate.isEmpty()) + { + if (!m_extensionLine) + { + QString debugInfo = QStringLiteral("%1#ExtensionLine").arg(m_debugInfo); + m_extensionLine = new GraphicalObjectPath(GraphicalObjectPathType::Polyline, + m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + debugInfo, + m_uniqueMapId); + } + + m_extensionLine->setVisibility(m_visibility && m_visibilityTechnical); + m_extensionLine->setScale(m_mainGraphicalObject->scale()); + m_extensionLine->setDrawOrder(m_mainGraphicalObject->drawOrder()); + + LineStyle ls = m_mainGraphicalObject->lineStyle(); + ls.setLineColor(m_extensionLineColor); + m_extensionLine->setLineStyle(ls); + + // positions: + PositionsTranslated tempPosTrans; + tempPosTrans.setPositionsTranslated(PositionCont() << + m_extensionLineCoordinate.originTranslated() // obstaclePosition + << m_positions.positionsTranslated()[1]); // end/targetPosition + + m_extensionLine->setPositions(tempPosTrans.positionsTranslated()); + + m_extensionLine->updateView(); + + } + else + { + // no m_extensionLineCoordinate -> remove extensionLine + delete m_extensionLine; + m_extensionLine = nullptr; + } +} + +bool LineOfSightImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polyline); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + // default: green, 3px + LineStyle ls = this->lineStyle(); + ls.setLineColor(Qt::green); + ls.setLineWidth(3); + this->setLineStyle(ls); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +bool LineOfSightImpl::setPositions(const PositionCont &) +{ + FATAL_LOG(m_logger) << LOGFUNCTION << " DO NOT CALL!!!"; + assert(false); + return false; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/line_of_sight_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/line_of_sight_impl.h new file mode 100755 index 000000000..879f97fd9 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/line_of_sight_impl.h @@ -0,0 +1,82 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of HitLine + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface +#include + +// Qt +#include +#include + + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The LineOfSightImpl class supports a "single" line with two colors. + * The method setPositions() should never be called. + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath displaying the main techn. Appearance) + * - mainGraphicalObjectText (displaying the text/name/identifier) + * - tacticalAppearance (displaying the tact. Appearance) + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT LineOfSightImpl : public BasicPathObjectImpl, public virtual LineOfSight +{ + using BaseClass = SituationMap::BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit LineOfSightImpl(const QString &debugInfo = QString()); + virtual ~LineOfSightImpl(); + +public: + QString identifier() const override; + void setIdentifier(const QString &identifier) override; + + void setPoints(const Point3D &observer, + const Point3D &obstacle, + const Point3D &target, + const QColor &visibleColor = Qt::green, + const QColor &obstructedColor = Qt::red) override; + +protected: + bool updateViewImpl() override; + void updateExtensionLine(); + + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + OriginTranslated m_extensionLineCoordinate; + GraphicalObjectPath *m_extensionLine = nullptr; + QColor m_extensionLineColor = Qt::red; + + // logical + +private: + bool setPositions(const PositionCont &) override; //SHOULD NEVER BE CALLED + +private: + + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/map_object_attributes_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/map_object_attributes_impl.cpp new file mode 100755 index 000000000..82c10a9cd --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/map_object_attributes_impl.cpp @@ -0,0 +1,111 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of DangerArea + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "map_object_attributes_impl.h" +#include +#include + +#include + +// ESRI + + +// Qt + + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class MapObjectAttributesImpl +//----------------------------------------------------------------------------- +MapObjectAttributesImpl::MapObjectAttributesImpl(const QString &debugInfo) + : MapObjectAttributes() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("MapObjectAttributesImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +MapObjectAttributesImpl::~MapObjectAttributesImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +QVariantMap MapObjectAttributesImpl::attributes() const +{ + if (!m_mainGraphicalObject) + return {}; + GraphicalObjectAttributes *goa = static_cast(m_mainGraphicalObject); + if (!goa) + return {}; + return goa->attributes(); +} + +void MapObjectAttributesImpl::setAttributes(const QVariantMap &attributes) +{ + if (!m_mainGraphicalObject) + return; + + GraphicalObjectAttributes *goa = static_cast(m_mainGraphicalObject); + if (!goa) + return; + goa->setAttributes(attributes); +} + +bool MapObjectAttributesImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " symbol=" << m_symbol.value().sidc + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + m_mainGraphicalObject->setOrigin(this->originTranslated()); + + bool success = BaseClass::updateViewImpl(); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool MapObjectAttributesImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool success = BaseClass::init(mapImpl, logicalLayerName); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + if (success) + { + // create underlying object (to save most attributes only there) + if (!m_mainGraphicalObject) + m_mainGraphicalObject = new GraphicalObjectAttributes(m_mapImpl, + m_graphicsLayer, + m_debugInfo + QLatin1String("#GOAttributes"), + m_uniqueMapId); + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/map_object_attributes_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/map_object_attributes_impl.h new file mode 100755 index 000000000..c64326112 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/map_object_attributes_impl.h @@ -0,0 +1,65 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of DangerArea + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "situation_map_objects_export.h" +#include "abstract_single_point_map_object_impl.h" +#include +#include // Interface + + +// Qt +#include +#include + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT MapObjectAttributesImpl : public AbstractSinglePointMapObjectImpl, public virtual MapObjectAttributes +{ + using BaseClass = AbstractSinglePointMapObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit MapObjectAttributesImpl(const QString &debugInfo = QString()); + virtual ~MapObjectAttributesImpl(); + +public: + MapObjectAttributesImpl(const MapObjectAttributesImpl &other) = delete; + MapObjectAttributesImpl &operator=(MapObjectAttributesImpl &other) = delete; + + QVariantMap attributes() const override; + void setAttributes(const QVariantMap &attributes) override; + + +protected: + bool updateViewImpl() override; + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + + // logical + + +private: + + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/map_object_indicator_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/map_object_indicator_impl.cpp new file mode 100755 index 000000000..b45f9a6b2 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/map_object_indicator_impl.cpp @@ -0,0 +1,289 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of MapObjectIndicator + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "map_object_indicator_impl.h" +#include +#include +#include +#include "abstract_single_point_map_object_impl.h" +#include +#include + + +// ESRI + + +// Qt + +#include + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class MapObjectIndicatorImpl +//----------------------------------------------------------------------------- +MapObjectIndicatorImpl::MapObjectIndicatorImpl(const QString &debugInfo) + : MapObjectIndicator() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("MapObjectIndicatorImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + setPickable(false); + + setArrowHeadStart(true); + + m_arrowHeadLengthHeight = 25; // size of arrow + m_arrowHeadHalfWidth = 7; // size of arrow + + m_pictureMarkerSize = 10; // size of filled circle at the origin (indicating the center of the map) + m_lineWidth = 2; // lineWidth of (dahed) line from origin to target + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +MapObjectIndicatorImpl::~MapObjectIndicatorImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + // disconnect lambda!!! Otherwise it will be called with a dangling 'this'pointer. + QObject::disconnect(m_extentChangedConnection); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +Point3D MapObjectIndicatorImpl::startPoint() const +{ + // !!! see setStartAndEndPoint() !!! + + const PositionCont &pos = m_positions.positions(); + if (pos.size() < 2) + return Point3D(); + else + return pos[1]; // !!! see setStartAndEndPoint() !!! +} +Point3D MapObjectIndicatorImpl::endPoint() const +{ + // !!! see setStartAndEndPoint() !!! + + const PositionCont &pos = m_positions.positions(); + if (pos.size() < 2) + return Point3D(); + else + return pos[0]; // !!! see setStartAndEndPoint() !!! +} + + +void MapObjectIndicatorImpl::setStartAndEndPoint(const Point3D &start, const Point3D &end) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " start=" << start + << " end=" << end; + + // According MIL2525 a line (like attack) starts with an arrow. + // But in this context (HitLine) + // start == shooter -> no arrowHead + // end == target -> with arrowHead + + // Therefore we have to add the 'end'/target point first: + m_positions.setPositions(PositionCont() << end << start); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +UniqueMapId MapObjectIndicatorImpl::mapObjectToPointTo() +{ + return m_uniqueIdToPointTo; +} + +void MapObjectIndicatorImpl::setMapObjectToPointTo(UniqueMapId uniqueId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, uniqueId=" << uniqueId; + + m_uniqueIdToPointTo = uniqueId; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, uniqueId=" << uniqueId; +} + + +bool MapObjectIndicatorImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " symbol=" << m_symbol.value().sidc + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + // create PictureMarker here, to have m_uniqueMapId already set ( compared to init() ) + if (m_pictureMarker.isNull()) + { + QString debugInfo = QStringLiteral("%1#CenterMarker").arg(m_debugInfo); + m_pictureMarker = QSharedPointer + (new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView() + , m_graphicsLayer + , debugInfo + , m_uniqueMapId)); + } + + + setArrowHeadsSelected(m_selected); + + + // real work: + + // + // point to desired obj now: + // + RefPointer objToPointTo; + if (SituationMap::isUniqueMapIdValid(m_uniqueIdToPointTo)) + objToPointTo = m_mapImpl->objectFactory().get(m_uniqueIdToPointTo); + else + DEBUG_LOG(m_logger) << LOGFUNCTION << " m_uniqueIdToPointTo invalid: " << m_uniqueIdToPointTo; + + // retrive values from objToPointTo: + PositionCont line; + QColor color; + + if (objToPointTo.valid()) + { + const QString &objToPointToDebugInfo = objToPointTo->debugInfo(); + DEBUG_LOG(m_logger) << LOGFUNCTION + << " objToPointTo is valid, uniqueIdToPointTo=" << m_uniqueIdToPointTo + << " (objToPointToDebugInfo=" << objToPointToDebugInfo << ")"; + // 1 + AbstractMapObjectImpl *impl = dynamic_cast(objToPointTo.get()); + if (impl) + { + color = impl->majorColor(); +// color.setAlpha(255); // no transperency + this->setDrawOrder(std::max(this->drawOrder(), impl->drawOrder())); + } + else + { + ERROR_LOG(m_logger) << LOGFUNCTION + << " dynamic_cast failed, uniqueIdToPointTo=" << m_uniqueIdToPointTo + << " (objToPointToDebugInfo=" << objToPointToDebugInfo << ")"; + } + + // get target position: + BoundingBox bbox; + objToPointTo->boundingBox(bbox); + if (!bbox.isValid()) + WARNING_LOG(m_logger) << LOGFUNCTION << " boundingBox not valid: bbox=" << bbox; + line.append(bbox.center()); + + const QPointF &curCenterPoint = m_mapImpl->centerPoint(); + const Point3D curCenterPoint3D(curCenterPoint.x(), curCenterPoint.y(), .0); + line.append(curCenterPoint3D); + + + // Color: + LineStyle ls = this->lineStyle(); + ls.setLineColor(color); + this->setLineStyle(ls); + + } + else // if (objToPointTo.valid()) + { + WARNING_LOG(m_logger) << LOGFUNCTION << " objToPointTo not found"; + + const QPointF &curCenterPoint = m_mapImpl->centerPoint(); + const Point3D curCenterPoint3D(curCenterPoint.x(), curCenterPoint.y(), .0); + line << curCenterPoint3D << curCenterPoint3D; // use curCenterPoint twice (to show a very 'short line'); + color = Qt::magenta; + } + + // + // transfer values: + // + if (line.size() == 2) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " setStartAndEndPoint, uniqueIdToPointTo=" << m_uniqueIdToPointTo + << " line=" << line; + m_positions.setPositions(line); + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " size insufficiant, " << " objToPointTo is valid, uniqueIdToPointTo=" << m_uniqueIdToPointTo; + + // update marker values: + const PositionCont &positionsTranslated = m_positions.positionsTranslated(); + if (positionsTranslated.size() >= 2) + m_pictureMarker->setOrigin(m_positions.positionsTranslated()[1]); + color.setAlpha(255); + m_pictureMarker->setMarkerSymbol(MarkerSymbolType::Circle, m_pictureMarkerSize, color); + + bool successBaseClass = BaseClass::updateViewImpl(); + if (!successBaseClass) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + + m_pictureMarker->setSelected(m_selected); + m_pictureMarker->setVisibility((m_mainGraphicalObject) ? m_mainGraphicalObject->visibility() : false); + + bool successPictureMarker = m_pictureMarker->updateView(); + if (!successPictureMarker) + ERROR_LOG(m_logger) << LOGFUNCTION << " m_pictureMarker->updateViewImpl failed"; + + + bool success = successPictureMarker && successBaseClass; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + + +bool MapObjectIndicatorImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + this->setVisibility(ObjectAppearance::Tactical, false); + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polyline); + + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + return false; + } + + if (!m_mainGraphicalObject) + { + ERROR_LOG(m_logger) << LOGFUNCTION << "no m_mainGraphicalObject"; + return false; + } + + m_mainGraphicalObject->rLineStyle().setLinePattern(LinePattern::Dash); + m_mainGraphicalObject->rLineStyle().setLineWidth(m_lineWidth); + + // connect to extentChanged... + // ! Must be disconnected in dtor ! + m_extentChangedConnection = QObject::connect(m_mapImpl, &MapImpl::extentChangedDelayed, m_mapImpl, [this]() + // ^^^^ to avoid CLazy warning + { + this->updateView(); + }); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +bool MapObjectIndicatorImpl::setPositions(const PositionCont &) +{ + FATAL_LOG(m_logger) << LOGFUNCTION << " DO NOT CALL!!!"; + assert(false); + return false; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/map_object_indicator_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/map_object_indicator_impl.h new file mode 100755 index 000000000..b14f83498 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/map_object_indicator_impl.h @@ -0,0 +1,101 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of MapObjectIndicator + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + + +// Qt +#include +#include +#include +#include + + + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +// forward +class GraphicalObjectPictureMarker; + + +/** + * @brief The MapObjectIndicatorImpl can be used to ease the locating of an object. + * The MapObjectIndicatorImpl can by parametrized with just the UniqueMapId to point to. + * updateView() 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 If the object to point to disappears, you are responsible for removing the MapObjectIndicatorImpl! + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath displaying the line with arrowhead) + * - m_pictureMarker of type GraphicalObjectPictureMarker / Image to show circle at the center of the map. + * - mainGraphicalObjectText (displaying the text/name/identifier) + * - tacticalAppearance (irrelevant) + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT MapObjectIndicatorImpl : public BasicPathObjectImpl, public virtual MapObjectIndicator +{ + using BaseClass = BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit MapObjectIndicatorImpl(const QString &debugInfo = QString()); + virtual ~MapObjectIndicatorImpl(); + +public: + MapObjectIndicatorImpl(const MapObjectIndicatorImpl &other) = delete; + MapObjectIndicatorImpl &operator=(MapObjectIndicatorImpl &other) = delete; + + + Point3D startPoint() const override; + Point3D endPoint() const override; + + void setStartAndEndPoint(const Point3D &start, const Point3D &end) override; + + UniqueMapId mapObjectToPointTo() override; + void setMapObjectToPointTo(UniqueMapId uniqueId) override; + + +protected: + bool updateViewImpl() override; + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + + // logical + +private: + bool setPositions(const PositionCont &) override; //SHOULD NEVER BE CALLED + +private: + UniqueMapId m_uniqueIdToPointTo = INVALID_MAP_ID; + QSharedPointer m_pictureMarker; // circle at the center of the map + int m_pictureMarkerSize = 20; + int m_lineWidth = 4; + + QMetaObject::Connection m_extentChangedConnection; + + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/mine_field_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/mine_field_impl.cpp new file mode 100755 index 000000000..21b18b8ee --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/mine_field_impl.cpp @@ -0,0 +1,381 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of MineField + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "mine_field_impl.h" +#include +#include "basic_path_object_impl.h" +#include +#include "mine_impl.h" + +#include + +// ESRI + + +// Qt +#include + + + +namespace SituationMap +{ + +// to get access to 'init' +class MineFieldLocalBasicPathObject final : public BasicPathObjectImpl +{ +public: + using BasicPathObjectImpl::BasicPathObjectImpl; + using BasicPathObjectImpl::init; + using BasicPathObjectImpl::setUniqueId; +}; + + + +//----------------------------------------------------------------------------- +class MineFieldImpl::Private +{ +public: + Private() = default; + ~Private() = default; + + // ESRI data: + Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr; + + + // Own data: + MapImpl *m_mapImpl = nullptr; + QString m_logicalLayer; + QString m_debugInfo; // will be appended by consecutive number + UniqueMapId m_uniqueMapId = INVALID_MAP_ID; + + QMap m_visibilityMines; + + + + QMap > m_cont; + using MAP_IT_T = QMapIterator>; + +// int m_drawOrder = 0; + + bool hasPathObject(int key) const { return m_cont.contains(key); } + QList keysInUse() const { return m_cont.keys(); } + void remove(int key) { m_cont.remove(key); } + void clear() { m_cont.clear(); } + + QSharedPointer getPathObject(int key) + { + auto ret = m_cont.value(key); + if (ret.isNull()) + { + QString debugInfo = QStringLiteral("%1#MineFieldSeg(%2)").arg(m_debugInfo).arg(key); + MineFieldLocalBasicPathObject *temp = new MineFieldLocalBasicPathObject(debugInfo); + bool success = temp->init(m_mapImpl, m_logicalLayer, GraphicalObjectPathType::Polygon); + temp->setUniqueId(m_uniqueMapId); + temp->setFillStyle(FillStyle(FillPattern::Null)); // no fill by default + + if (success) + { + ret = QSharedPointer(static_cast(temp)); + m_cont.insert(key, ret); + } + else + temp->removeMySelf(); + } + return ret; + } +}; // class MineFieldImpl::Private +//----------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- +// class MineFieldImpl +//----------------------------------------------------------------------------- +MineFieldImpl::MineFieldImpl(const QString &debugInfo) + : MineField() // Interface + , BaseClass(debugInfo) // Implementation + , d(new Private) + , m_logger(getLogger("MineFieldImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + d->m_visibilityMines[static_cast(MineState::Activated)] = true; + d->m_visibilityMines[static_cast(MineState::DeActivated)] = true; + + m_lineColorFromParty = false; + m_fillColorAlpha = 200; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +MineFieldImpl::~MineFieldImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete d; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void MineFieldImpl::setUniqueId(const UniqueMapId &uniqueId) +{ + BaseClass::setUniqueId(uniqueId); + d->m_uniqueMapId = m_uniqueMapId; +} + + +PositionCont MineFieldImpl::segment(int key) const +{ + if (d->hasPathObject(key)) + return d->getPathObject(key)->positions(); + else + return PositionCont(); +} + +void MineFieldImpl::setSegment(int key, const PositionCont &positions) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " key=" << key + << " positions.size=" << positions.size(); + + QSharedPointer pathObj = d->getPathObject(key); + if (pathObj) + pathObj->setPositions(positions); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void MineFieldImpl::removeSegment(int key) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return d->remove(key); +} + +void MineFieldImpl::removeSegments() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return d->clear(); +} + +QList MineFieldImpl::keysInUse() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return d->keysInUse(); +} + + +void MineFieldImpl::setPositionsMine(MineField::MineState mineState, const PositionCont &posCont) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " mineState=" << static_cast(mineState) + << " posCont.size()=" << posCont.size(); + + IndicationContainer &mines = this->rIndicationContainer(); + + mines.setDebugInfo(m_debugInfo); + mines.setPositions(static_cast(mineState), posCont); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " mineState=" << static_cast(mineState) + << " posCont.size()=" << posCont.size(); +} + +bool MineFieldImpl::mineVisibility(MineField::MineState mineState) const +{ + return d->m_visibilityMines[static_cast(mineState)]; +} +void MineFieldImpl::setMineVisibility(MineField::MineState mineState, bool visible) +{ + d->m_visibilityMines[static_cast(mineState)] = visible; +} + + +bool MineFieldImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical + << " mineVisibility(MineState::Activated)=" << this->mineVisibility(MineState::Activated) + << " mineVisibility(MineState::DeActivated)=" << this->mineVisibility(MineState::DeActivated) + << " m_positions->positions().count()=" << m_positions.positions().count() + << " m_mainGraphicalObject->positions().count()=" << m_mainGraphicalObject->positions().count() + << " m_mainGraphicalObject->fillStyle().fillPattern()=" << (int)m_mainGraphicalObject->fillStyle().fillPattern(); + + // indication : + if (nullptr == m_mainGraphicalObject) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no m_mainGraphicalObject"; + return false; + } + + if (m_indicationCont) + { + const QColor &col = m_mainGraphicalObject->lineStyle().lineColor(); + + const QList idxInUse = m_indicationCont->getIndicationIndexList(); + for (int idx : idxInUse) + { + // handle image on if not already set: + if (m_indicationCont->image(idx).isNull()) + { + bool filled = (idx == static_cast(MineState::DeActivated)); + const QImage &img = MineImpl::renderMineSymbol(col, 20, filled); // filled + m_indicationCont->setImage(idx, img); + } + + m_indicationCont->setSelected(idx, m_selected); + m_indicationCont->setPickable(idx, m_pickable); + } + } + // : indication + + + // m_mainGraphicalObject stores the outline/'bounding box' (techn and tact) + // Those geometry should never be visible. + // The text of should be visible. + this->setVisibilityOfGeometry(false); + + bool success = BaseClass::updateViewImpl(); + + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + + // segment : + + Private::MAP_IT_T mapIt(d->m_cont); + + while (mapIt.hasNext()) + { + mapIt.next(); + QSharedPointer pathObj = mapIt.value(); + if (pathObj) + { + int idx = mapIt.key(); + +// pathObj->setDebugInfo(m_mainGraphicalObject->debugInfo()); // will overwrite complete string -> skip + + pathObj->setLineStyle(m_mainGraphicalObject->lineStyle()); + if (idx >= 0) + pathObj->setFillStyle(m_mainGraphicalObject->fillStyle()); + + pathObj->setVisibility(m_visibility); + pathObj->setVisibility(ObjectAppearance::Technical, m_visibilityTechnical); + + // Do not Transfer symbol. Only m_mainGraphicalObject uses ObjectAppearance::Tactical: +// pathObj->setSymbol(this->symbol()); + pathObj->setVisibility(ObjectAppearance::Tactical, false); + + pathObj->setPickable(m_mainGraphicalObject->pickable()); + + pathObj->setSelected(m_mainGraphicalObject->selected()); + + pathObj->setDrawOrder(this->drawOrder()); // use m_drawOrder instead m_internalDrawOrder. + + if (!pathObj->updateView()) + { + int key = mapIt.key(); + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed, key=" << key; + } + } + } // while + + // : segment + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical + << " mineVisibility(MineState::Activated)=" << this->mineVisibility(MineState::Activated) + << " mineVisibility(MineState::DeActivated)=" << this->mineVisibility(MineState::DeActivated) + << " m_positions->positions().count()=" << m_positions.positions().count() + << " m_mainGraphicalObject->positions().count()=" << m_mainGraphicalObject->positions().count() + << " m_mainGraphicalObject->fillStyle().fillPattern()=" << (int)m_mainGraphicalObject->fillStyle().fillPattern(); + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + + +bool MineFieldImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (mapImpl) + { + switch (mapImpl->symbologyType()) + { + case AdvancedSymbology::SymbologyType::APP6D: + case AdvancedSymbology::SymbologyType::MIL2525D: +// m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"10012500002707060000"); // SymbolSet = 25 270701 , 270705, 270706, 270707 + // Case #01977851 - Runitme SDK for Qt: IdentifyGraphicsOverlays crashes + // BUG-000107707 : IdentifyGraphicsOverlays crashes on military symbol "10012500002707060000"] + // -> use another: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "10012500001511000000"); // SymbolSet = 25 151100 + break; + case AdvancedSymbology::SymbologyType::MIL2525C: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GFMPOFA---"); // Mil2525C: 0843: GFMPOFA--- -> Polygon (Minefields Mined Area F) + break; + case AdvancedSymbology::SymbologyType::APP6B: +// m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"GFCPBOAIL-"); // APP6B: 3852: GFCPBOAIL- -> Polygon (Minefield - Antipersonnel Mines) +// m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"GFCPBOAIN-"); // APP6B: 3853: GFCPBOAIN- -> Polygon (Minefield - Antitank) + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GFCPBOAIM-"); // APP6B: 3857: GFCPBOAIM- -> Polygon (Minefields Mined Area) + break; + case AdvancedSymbology::SymbologyType::None: + // empty + break; + } // switch + } // if (mapImpl) + + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polygon); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + setLineStyle(LineStyle(Qt::green, 2)); + setFillStyle(FillStyle(Qt::green, FillPattern::Null)); + + // set some values to Private: + d->m_mapImpl = m_mapImpl; + d->m_logicalLayer = m_logicalLayerName; + d->m_debugInfo = m_debugInfo; + d->m_uniqueMapId = m_uniqueMapId; + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool MineFieldImpl::onMapExtentChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (m_indicationCont) + { + bool visiScale = m_visibilityScaleRange.scaleHasVisibility(m_mapImpl->scale()); + const QList &idxInUse = m_indicationCont->getIndicationIndexList(); // idxInUse is representing all values of 'MineState' + for (int idx : std::as_const(idxInUse)) + { + m_indicationCont->setVisible(idx, m_visibility && m_visibilityTechnical && d->m_visibilityMines.value(idx) && visiScale); + } + } + + bool success = BaseClass::onMapExtentChanged(); + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/mine_field_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/mine_field_impl.h new file mode 100755 index 000000000..f1bb23bbe --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/mine_field_impl.h @@ -0,0 +1,110 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of MineField + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + + +// Qt +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +enum class MinesState; + +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +/** + * @brief The MineFieldImpl class supports MineFields with an unlimited number of segments. + * Additionally the positions of mines can be set. + * MineState::Activated and MineState::DeActivated are supported. + * + * The segments are implemented using a BasicPathObjectImpl. + * The subsidiary lines of the mainGraphicalObject (also of type BasicPathObjectImpl) + * are not used because the geometry part of the mainGraphicalObject is hidden (replaced by the segments.) + * But the envelope of the MineField is necessary to show the TacticalAppearance. + * + * The identifier of the MineFieldImpl is used, not the identifier of the segments. + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath displaying the main techn. Appearance (is hidden, see above) + * - mainGraphicalObjectText (displaying the text/name/identifier) + * - tacticalAppearance (displaying the tact. Appearance) + * - Container of BasicPathObjectImpl's to display the single segments. + */ +class SITUATIONMAPOBJECTS_EXPORT MineFieldImpl : public BasicPathObjectImpl, public virtual MineField +{ + using BaseClass = BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit MineFieldImpl(const QString &debugInfo = QString()); + virtual ~MineFieldImpl(); + +public: + MineFieldImpl(const MineFieldImpl &other) = delete; + MineFieldImpl &operator=(MineFieldImpl &other) = delete; + + void setUniqueId(const UniqueMapId &uniqueId) override; + + + PositionCont segment(int key) const override; + + /** + * @brief Set positions of one segment. + * @param key of segment to be set. + * @param posCont positions of segment. + * @note: is key is negativ, no fill pattern is aplied. + * If key is >=0, the pattern of the segments take over the main pattern. + */ + void setSegment(int key, const PositionCont &positions) override; + + void removeSegment(int key) override; + void removeSegments() override; + QList keysInUse() const override; + + void setPositionsMine(MineState mineState, const PositionCont &posCont) override; + + bool mineVisibility(MineState mineState) const override; + void setMineVisibility(MineState mineState, bool visible) override; + + +protected: + virtual bool updateViewImpl() override; + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + bool onMapExtentChanged() override; + + // internal/admin + + // logical + + +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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/mine_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/mine_impl.cpp new file mode 100755 index 000000000..e83ffefd9 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/mine_impl.cpp @@ -0,0 +1,241 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of Mine + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "mine_impl.h" +#include +#include +#include +#include +#include + +#include + +// ESRI + + +// Qt +#include +#include + + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// constants +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Utils: +//----------------------------------------------------------------------------- + + + + + +//----------------------------------------------------------------------------- +// class MineImpl +//----------------------------------------------------------------------------- +MineImpl::MineImpl(const QString &debugInfo) + : Mine() // Interface + , BaseClass(debugInfo) // Implementation +// , AbstractEffectSimulationImpl() // Secondary implementation + , m_logger(getLogger("MineImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +MineImpl::~MineImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +QString MineImpl::toString() const +{ + QString str; + str.append(BaseClass::toString()); +// str.append( AbstractEffectSimulationImpl::toString() ); + return str; +} + + +bool MineImpl::state() const +{ + return m_state.value(); +} +void MineImpl::setState(bool state) +{ + m_state = state; +} + +SymbolImage MineImpl::symbolImage() const +{ + SymbolImage symImage; + GraphicalObjectPictureMarker *pMarker = static_cast(m_mainGraphicalObject); + if (pMarker) + { + symImage.setImage(pMarker->image()); + symImage.setValid(!symImage.image().isNull()); + } + return symImage; +} + + +bool MineImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " origin =" << this->origin() //.x() << ", " << this->origin().y() + << " radius =" << this->radius() + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + if (!m_graphicsLayer) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no graphicsLayer"; + return false; + } + + if (nullptr == m_mainGraphicalObject) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no m_mainGraphicalObject"; + return false; + } + + // image: + GraphicalObjectPictureMarker *pMarker = static_cast(m_mainGraphicalObject); + if (m_state.changed()) + { + const QImage &img = renderMineSymbol(partyColor(), + 20, + m_state.value()); + pMarker->setImage(img); + } + pMarker->setOrigin(this->originTranslated()); + + + // tacticalAppearance: + if (m_tacticalAppearance) + { + if (partyChanged()) + m_symbol.refValue().color = partyColor(); + + m_tacticalAppearance->setOrigin(this->originTranslated()); + } + + bool success = BaseClass::updateViewImpl(); + + // reset changed: + m_state.reset(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + + +bool MineImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (mapImpl) + { + + switch (mapImpl->symbologyType()) + { + case AdvancedSymbology::SymbologyType::APP6D: + case AdvancedSymbology::SymbologyType::MIL2525D: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "10011500002102000000"); // SymbolSet = 15 210100 ; 280000 -> Crash on Click + break; + case AdvancedSymbology::SymbologyType::MIL2525C: +// m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"GHMPOB----"); // 0754 GHMPOMP--- (Antipersonnel (AP) Mines H) -> Point + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GHMPOB----"); // 0760 GHMPOB---- (Booby Trap H) -> Point + break; + case AdvancedSymbology::SymbologyType::APP6B: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GUOPVY----"); // APP6B: 2549 GUOPVY---- (Booby Trap U) -> Point + break; + case AdvancedSymbology::SymbologyType::None: + // empty + break; + } // switch + + } // if (mapImpl) + + bool success = BaseClass::init(mapImpl, logicalLayerName); + + if (success) + { + // create underlying object (to save most attributes only there) + if (!m_mainGraphicalObject) + m_mainGraphicalObject = new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QStringLiteral("#Image"), + m_uniqueMapId); + + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +QImage MineImpl::renderMineSymbol(const QColor &color, int size, bool filled) +{ + QImage image(size, size, QImage::Format_ARGB32); + image.fill(Qt::transparent); + + QPainter painter(&image); + + QPen pen(color); + pen.setWidth(2); // filled == deactivated == triggered + if (!filled) + pen.setWidth(1); // !filled == active == still armed + painter.setPen(pen); + + const int sizeHalf = size / 2.0; + + QPointF center(sizeHalf, sizeHalf); + + // circle + if (filled) + painter.setBrush(color); + painter.drawEllipse(center, sizeHalf * 0.5, sizeHalf * 0.5); + + // lines + QLine newLine(0, sizeHalf, size / 4.0, sizeHalf); + painter.drawLine(newLine); + + QTransform trans; + trans.translate(sizeHalf, sizeHalf); + trans.rotate(90); + trans.translate(-sizeHalf, -sizeHalf); + + newLine = trans.map(newLine); + painter.drawLine(newLine); + + newLine = trans.map(newLine); + painter.drawLine(newLine); + + newLine = trans.map(newLine); + painter.drawLine(newLine); + + painter.end(); + + return image; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/mine_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/mine_impl.h new file mode 100755 index 000000000..7f12b0085 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/mine_impl.h @@ -0,0 +1,77 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of Mine + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_single_point_map_object_impl.h" +//#include "abstract_effect_simulation_impl.h" +#include // Interface + + +// Qt + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +/** + * @brief The MineImpl class displays a mine impact. + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPictureMarker / Image, displaying the main techn. Appearance) + * - mainGraphicalObjectText (displaying the text/name) + * - tacticalAppearance (displaying the tact. Appearance) + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT MineImpl : public AbstractSinglePointMapObjectImpl, public virtual Mine +{ + using BaseClass = AbstractSinglePointMapObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit MineImpl(const QString &debugInfo = QString()); + virtual ~MineImpl(); + +public: + MineImpl(const MineImpl &other) = delete; + MineImpl &operator=(MineImpl &other) = delete; + + QString toString() const override; + + bool state() const override; + void setState(bool state) override; + + SymbolImage symbolImage() const override; + + + static QImage renderMineSymbol(const QColor &color, int size, bool filled); + + +protected: + virtual bool updateViewImpl() override; + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + + // logical + SimpleValue m_state = false; + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/nine_liner_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/nine_liner_impl.cpp new file mode 100755 index 000000000..7499aebd7 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/nine_liner_impl.cpp @@ -0,0 +1,388 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of NineLiner + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "nine_liner_impl.h" +#include +#include +#include + +#include +#include + +// ESRI +#include + +// Qt + + +#include + + +namespace SituationMap +{ + +double adaptScale(double scale) +{ + if (scale<1.0) + return scale; + return (0.33 * scale) + 0.66; +} + +//----------------------------------------------------------------------------- +// class NineLinerImpl +//----------------------------------------------------------------------------- +NineLinerImpl::NineLinerImpl(const QString &debugInfo) + : NineLiner() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("NineLinerImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + m_color = Qt::blue; + + m_otInitialPoint.setMapObjectImpl(this); + m_otTargetPosition.setMapObjectImpl(this); + m_otFriendliesPosition.setMapObjectImpl(this); + +// setArrowHeadStart(true); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +NineLinerImpl::~NineLinerImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +QString NineLinerImpl::identifier() const +{ + return this->textOnSegment(0); +} +void NineLinerImpl::setIdentifier(const QString &identifier) +{ + this->setTextOnSegment(0, identifier); +} + +void NineLinerImpl::setScale(float scale) +{ + m_ownScale = scale; // to use m_ownScale.changed(); + BaseClass::setScale(scale); +} + +bool NineLinerImpl::updateViewInitialPoint() +{ + bool success = false; + const NineLinerData &data = m_data.value(); + + // InitialPoint / square + const Point3D &ip = data.initialPoint; + if (ip.isNull()) + { + m_pmInitialPoint.clear(); + success = true; + } else { + if(!m_pmInitialPoint) + { + QString debugInfo = QStringLiteral("%1#InitialPointMarker").arg(m_debugInfo); + m_pmInitialPoint = QSharedPointer + (new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView() + , m_graphicsLayer + , debugInfo + , m_uniqueMapId)); + } + + m_pmInitialPoint->setMarkerSymbol(MarkerSymbolType::Square, + 20*adaptScale(m_mainGraphicalObject->scale()), // cover the lines less by reduced growing of pointSymbols + m_color.value()); + m_otInitialPoint.setOrigin(ip); + m_pmInitialPoint->setOrigin(m_otInitialPoint.originTranslated()); + m_pmInitialPoint->setPickable(m_mainGraphicalObject->pickable()); + m_pmInitialPoint->setVisibility(m_mainGraphicalObject->visibility()); + success = m_pmInitialPoint->updateView(); + } + TRACE_LOG(m_logger) << LOGFUNCTION << " success=" << success; + return success; +} + +bool NineLinerImpl::updateViewTargetPosition() +{ + bool success = false; + const NineLinerData &data = m_data.value(); + + // TargetPosition / triangle + const Point3D &ip = data.targetPosition; + if (ip.isNull()) + { + m_pmTargetPosition.clear(); + success = true; + } else { + if(!m_pmTargetPosition) + { + QString debugInfo = QStringLiteral("%1#TargetPositionMarker").arg(m_debugInfo); + m_pmTargetPosition = QSharedPointer + (new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView() + , m_graphicsLayer + , debugInfo + , m_uniqueMapId)); + } + + m_pmTargetPosition->setMarkerSymbol(MarkerSymbolType::Triangle, + 20*adaptScale(m_mainGraphicalObject->scale()), // cover the lines less by reduced growing of pointSymbols + m_color.value()); + m_otTargetPosition.setOrigin(ip); + m_pmTargetPosition->setOrigin(m_otTargetPosition.originTranslated()); + m_pmTargetPosition->setPickable(m_mainGraphicalObject->pickable()); + m_pmTargetPosition->setVisibility(m_mainGraphicalObject->visibility()); + success = m_pmTargetPosition->updateView(); + } + TRACE_LOG(m_logger) << LOGFUNCTION << " success=" << success; + return success; +} + +Point3D movePoint(const Point3D& point, int length, int angle) +{ + QList pColl; + + constexpr double deg2rad = LtsCoordTrans::deg2rad(); + constexpr double rad2deg = LtsCoordTrans::rad2deg(); + + // to wgs84 / degree + Esri::ArcGISRuntime::Point p(point.x()*rad2deg, point.y()*rad2deg, point.z()*rad2deg, Esri::ArcGISRuntime::SpatialReference::wgs84()); + + pColl = Esri::ArcGISRuntime::GeometryEngine::moveGeodetic({p}, + length, Esri::ArcGISRuntime::LinearUnit::meters(), + angle, Esri::ArcGISRuntime::AngularUnit::degrees(), + Esri::ArcGISRuntime::GeodeticCurveType::Geodesic); + p = pColl.first(); + // back to rad + const Point3D movedPoint (p.x()*deg2rad, p.y()*deg2rad, p.z()*deg2rad); + +// qCritical() << " length = " << length << " angle= " << angle; + + return movedPoint; +} + +Point3D movePointFah(const Point3D& point, int length, int angle) +{ + if (length < 1000) + length = 1000; + + if (length > 100000) + length = 100000; + + return movePoint(point, length, angle+180); +} + +bool NineLinerImpl::updateViewFriendlies() +{ + bool success = false; + const NineLinerData &data = m_data.value(); + + if (data.friendliesDistance < 0) + { + m_pmFriendlies.clear(); + success = true; + } + else + { + const Point3D &tartgetPosition = m_otTargetPosition.origin(); + const Point3D &friendliesOrigin = movePoint(tartgetPosition, static_cast(data.friendliesDistance), data.friendliesBearing); + + if(!m_pmFriendlies) + { + QString debugInfo = QStringLiteral("%1#FriendliesPositionMarker").arg(m_debugInfo); + m_pmFriendlies = QSharedPointer + (new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView() + , m_graphicsLayer + , debugInfo + , m_uniqueMapId)); + } + m_pmFriendlies->setMarkerSymbol(MarkerSymbolType::Circle, + 15*adaptScale(m_mainGraphicalObject->scale()), // cover the lines less by reduced growing of pointSymbols + m_color.value()); + m_otFriendliesPosition.setOrigin(friendliesOrigin); + m_pmFriendlies->setOrigin(m_otFriendliesPosition.originTranslated()); + m_pmFriendlies->setPickable(m_mainGraphicalObject->pickable()); + m_pmFriendlies->setVisibility(m_mainGraphicalObject->visibility()); + success = m_pmFriendlies->updateView(); + } + TRACE_LOG(m_logger) << LOGFUNCTION << " success=" << success; + return success; +} + + +bool NineLinerImpl::updateViewFinalAttackHeading(int length) +{ + const NineLinerData &data = m_data.value(); + + const Point3D &tartgetPosition = m_otTargetPosition.origin(); + const Point3D &fahOrigin = movePointFah(tartgetPosition, length, data.fah); + + m_positions.setPositions(PositionCont() << fahOrigin << tartgetPosition); + + return true; +} + +bool NineLinerImpl::updateViewFahSector(QSharedPointer &path, const QString &debugInfo, int length, int angle) +{ + const Point3D &tartgetPosition = m_otTargetPosition.origin(); + const Point3D &origin = movePointFah(tartgetPosition, length, angle); + + bool success = false; + + if (angle < 0) + { + path.clear(); + success = true; + } else { + if(!path) + { + path = QSharedPointer + (new GraphicalObjectPath(GraphicalObjectPathType::Polyline, + m_mapImpl->getEsriGeoView() + , m_graphicsLayer + , debugInfo + , m_uniqueMapId)); + path->setDrawOrder(m_internalDrawOrder - 1); + } + path->setLineStyle(LineStyle(m_color.value(), 3, LinePattern::Dash)); + + PositionsTranslated positions; + positions.setMapObjectImpl(this); + positions.addSinglePosition(tartgetPosition); + positions.addSinglePosition(origin); + + path->setPositions(positions.positionsTranslated()); + path->setScale(m_mainGraphicalObject->scale()); + path->setPickable(m_mainGraphicalObject->pickable()); + path->setVisibility(m_mainGraphicalObject->visibility()); + success = path->updateView(); + } + TRACE_LOG(m_logger) << LOGFUNCTION << " success=" << success; + return success; +} + +bool NineLinerImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " m_data=" << m_data.value().toString() + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + BaseClass::updateViewImpl(); + + m_color = partyColor(); + +// if (m_data.changed() || m_color.changed() || m_ownScale.changed()) + { + updateViewInitialPoint(); + updateViewTargetPosition(); + updateViewFriendlies(); + + const int length = static_cast(m_mapImpl->pixelSizeMeter() * 100); + updateViewFinalAttackHeading(length); + updateViewFahSector(m_pathFahSectorLow, QStringLiteral("%1#FahSectorLow").arg(m_debugInfo), length, m_data.value().fahLow); + updateViewFahSector(m_pathFahSectorHigh, QStringLiteral("%1#FahSectorHigh").arg(m_debugInfo), length, m_data.value().fahHigh); + } + + setArrowHeadsSelected(m_selected); + + bool success = BaseClass::updateViewImpl(); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + // reset changed flag -> updateViewImplPost + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool NineLinerImpl::onMapExtentChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool success = BaseClass::onMapExtentChanged(); + + // to handle unchanged pixelSizeMeter + const int newFahLength = static_cast(m_mapImpl->pixelSizeMeter() * 100); + + if (m_fahLength == newFahLength) + return true; + + // store new value + m_fahLength = newFahLength; + + updateViewFinalAttackHeading(m_fahLength); + m_mainGraphicalObject->setPositions(m_positions.positionsTranslated()); + if(!m_mainGraphicalObject->updateView()) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed (m_mainGraphicalObject)"; + + if (!updateViewFahSector(m_pathFahSectorLow, QStringLiteral("%1#FahSectorLow").arg(m_debugInfo), m_fahLength, m_data.value().fahLow)) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed (m_pathFahSectorLow)"; + + if (!updateViewFahSector(m_pathFahSectorHigh, QStringLiteral("%1#FahSectorHigh").arg(m_debugInfo), m_fahLength, m_data.value().fahHigh)) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed (m_pathFahSectorHigh)"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return success; +} + +bool NineLinerImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (mapImpl) + { + switch (mapImpl->symbologyType()) + { + case AdvancedSymbology::SymbologyType::APP6D: + case AdvancedSymbology::SymbologyType::MIL2525D: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "10032500001406030000"); // SymbolSet = 25 140602 + // ^^ ^^^^^^ + break; + case AdvancedSymbology::SymbologyType::MIL2525C: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GFGPOLKGS-"); // 1047 - Direction Of Attack Ground Supporting Attack F +// m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"GHGPOLKA--"); // +// m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"GHGPOLKGM-"); // + + break; + case AdvancedSymbology::SymbologyType::APP6B: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GFCPMOLDS-"); // APP6B-3733 GFCPMOLDS- -> Direction Of Attack Ground Supporting Attack F + break; + case AdvancedSymbology::SymbologyType::None: + // empty + break; + } // switch + } // if (mapImpl) + + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polyline); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + m_mainGraphicalObject->setLineStyle(LineStyle(Qt::cyan, 2, LinePattern::Solid)); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +bool NineLinerImpl::setPositions(const PositionCont &) +{ + FATAL_LOG(m_logger) << LOGFUNCTION << " DO NOT CALL!!!"; + assert(false); + return false; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/nine_liner_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/nine_liner_impl.h new file mode 100755 index 000000000..d7bae0236 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/nine_liner_impl.h @@ -0,0 +1,108 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of NineLiner + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + + +// Qt +#include +#include + + + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT NineLinerImpl : public BasicPathObjectImpl, public virtual NineLiner +{ + using BaseClass = SituationMap::BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit NineLinerImpl(const QString &debugInfo = QString()); + virtual ~NineLinerImpl(); + +public: + NineLinerImpl(const NineLinerImpl &other) = delete; + NineLinerImpl &operator=(NineLinerImpl &other) = delete; + + QString identifier() const override; + void setIdentifier(const QString &identifier) override; + + void setNineLinerData(const NineLinerData& data) override { m_data = data; } + + void setScale(float scale) override; + +protected: + bool updateViewInitialPoint(); + bool updateViewTargetPosition(); + + bool updateViewFriendlies(); + + bool updateViewFinalAttackHeading(int length); + bool updateViewFahSector(QSharedPointer &path, const QString &debugInfo, int length, int angle); + + + bool updateViewImpl() override; + bool onMapExtentChanged() override; + + bool updateViewImplPost() override + { + BaseClass::updateViewImplPost(); + m_data.reset(); + m_color.reset(); + m_ownScale.reset(); + return true; + } + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + + // logical + +private: + bool setPositions(const PositionCont &) override; //SHOULD NEVER BE CALLED + +private: + // input: + SimpleValue m_data; + + OriginTranslated m_otInitialPoint; + OriginTranslated m_otTargetPosition; + OriginTranslated m_otFriendliesPosition; + int m_fahLength = 1; + + // display: + SimpleValue m_ownScale; + SimpleValue m_color; + QSharedPointer m_pmInitialPoint; // square + QSharedPointer m_pmTargetPosition; // triangle + + QSharedPointer m_pathFahSectorLow; + QSharedPointer m_pathFahSectorHigh; + + QSharedPointer m_pmFriendlies; // circle + + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/nuclear_agent_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/nuclear_agent_impl.cpp new file mode 100755 index 000000000..4a665aaa7 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/nuclear_agent_impl.cpp @@ -0,0 +1,428 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of NuclearAgent + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "nuclear_agent_impl.h" +#include +#include +#include + +#include + + +// ESRI + + +// Qt +#include +#include + + +namespace SituationMap +{ + +// to get access to 'init' +using NAgentLocalBasicPathObject = LocalBasicPathObject; + +QImage renderImpactSymbol(const QColor &color, int size, bool filled) +{ + QImage image(size, size, QImage::Format_ARGB32); + image.fill(Qt::transparent); + + QPainter painter(&image); + + QPen pen(color); + int penWidth = size/10; + if (penWidth < 2) + penWidth = 2; + pen.setWidth(penWidth); + painter.setPen(pen); + + const int sizeHalf = size / 2; + + QPointF center(sizeHalf, sizeHalf); + + // circle + if (filled) + painter.setBrush(color); + painter.drawEllipse(center, sizeHalf * 0.5, sizeHalf * 0.5); + + // lines + QLine newLine(0, sizeHalf, size / 4.0, sizeHalf); + painter.drawLine(newLine); + + QTransform trans; + trans.translate(sizeHalf, sizeHalf); + trans.rotate(45); + trans.translate(-sizeHalf, -sizeHalf); + + // 8 are required, one is already draw, so 7: + for (int i=0; i<7; ++i) + { + newLine = trans.map(newLine); + painter.drawLine(newLine); + } + + painter.end(); + + return image; +} + + +class NuclearAgentImpl::Private +{ +public: + Private() = default; + ~Private() = default; + + // ESRI data: +// Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr; + + + // Own data: + MapImpl *m_mapImpl{nullptr}; + QString m_logicalLayer; + QString m_debugInfo; // will be appended by consecutive number + UniqueMapId m_uniqueMapId{INVALID_MAP_ID}; + + GraphicalObjectPictureMarker *m_centerImage{nullptr}; + + + struct ValueDataNA + { + QSharedPointer basicPathObj; + NuclearAgent::OptionalData optData; + }; + + QMap m_cont; /// one polyline per subsidiary line + using MAP_IT_T = QMapIterator; +// int m_drawOrder = 0; + + bool hasPathObject(int key) const { return m_cont.contains(key); } + QList keysInUse() const { return m_cont.keys(); } + void remove(int key) { m_cont.remove(key); } + void clear() { m_cont.clear(); } + + ValueDataNA getPathObject(int key, const OptionalData &optData = OptionalData()) + { + auto ret = m_cont.value(key); // copy + if (ret.basicPathObj.isNull()) + { + QString debugInfo = QStringLiteral("%1#SubsidiaryLine(%2)").arg(m_debugInfo).arg(key); + NAgentLocalBasicPathObject *temp = new NAgentLocalBasicPathObject(debugInfo); + GraphicalObjectPathType pathType = GraphicalObjectPathType::Polyline; + if (optData.closed) + pathType = GraphicalObjectPathType::Polygon; + bool success = temp->init(m_mapImpl, m_logicalLayer, pathType); + temp->setUniqueId(m_uniqueMapId); + + if (success) + ret.basicPathObj = QSharedPointer(static_cast(temp)); + else + temp->removeMySelf(); + + ret.optData.closed = optData.closed; + } + // transfer optional data (line|fill) to ret and then to container _if_ set: + if (optData.lineStyle) + ret.optData.lineStyle = optData.lineStyle; + if (optData.fillStyle) + ret.optData.fillStyle = optData.fillStyle; + m_cont.insert(key, ret); + + return ret; + } +}; // class NuclearAgentImpl::Private +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// class NuclearAgentImpl +//----------------------------------------------------------------------------- +NuclearAgentImpl::NuclearAgentImpl(const QString &debugInfo) + : NuclearAgent() // Interface + , BaseClass(debugInfo) // Implementation + , d(new Private) + , m_logger(getLogger("NuclearAgentImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + m_fillColorAlpha = 200; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +NuclearAgentImpl::~NuclearAgentImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete d->m_centerImage; + delete d; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void NuclearAgentImpl::setUniqueId(const UniqueMapId &uniqueId) +{ + BaseClass::setUniqueId(uniqueId); + d->m_uniqueMapId = m_uniqueMapId; +} + + +PositionCont NuclearAgentImpl::subsidiaryLine(int key) const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (d->hasPathObject(key)) + return d->getPathObject(key).basicPathObj->positions(); + else + return PositionCont(); +} + +void NuclearAgentImpl::setSubsidiaryLine(int key, const PositionCont &positions, const OptionalData &optData) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << ", key=" << key + << ", closed=" << optData.closed + << ", positions.size=" << positions.size(); + + Private::ValueDataNA vd = d->getPathObject(key, optData); + QSharedPointer pathObj = vd.basicPathObj; + if (pathObj) + pathObj->setPositions(positions); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +QList NuclearAgentImpl::subsidiaryKeysInUse() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return d->keysInUse(); +} + +void NuclearAgentImpl::removeSubsidiaryLine(int key) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return d->remove(key); +} + +void NuclearAgentImpl::removeSubsidiaryLines() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return d->clear(); +} + +Point3D NuclearAgentImpl::impactOrigin() const +{ + return BaseClass::tacticalOrigin(); +} + +void NuclearAgentImpl::setImpactOrigin(const Point3D &impactOrigin) +{ + BaseClass::setTacticalOrigin(impactOrigin); +} + +void NuclearAgentImpl::setDetonation(const Point3D &origin, double radius) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + BaseClass::setDetonation(origin, radius); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +bool NuclearAgentImpl::hasDetonation() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return BaseClass::hasDetonation(); +} + +void NuclearAgentImpl::resetDetonation() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + BaseClass::resetDetonation(); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +bool NuclearAgentImpl::updateViewImpl() +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " START" + << " symbol=" << m_symbol.value().sidc + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + // centerImage: + if (!d->m_centerImage) + { + d->m_centerImage = + new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QLatin1String("#CenterImage"), + m_uniqueMapId); + } +// if (partyChanged()) + { + // QImage img = renderImpactSymbol(partyColor(), 20, true); // filled + QImage img = renderImpactSymbol(m_mainGraphicalObject->lineStyle().lineColor(), 20, true); // filled + d->m_centerImage->setImage(img); + } + +// m_centerImage->setScale(m_mainGraphicalObject->scale()); // do not scale centerImage, equal to mine of MineArea + + d->m_centerImage->setOrigin(m_tacticalOrigin.value().originTranslated()); + d->m_centerImage->setVisibility(m_visibility && m_visibilityTechnical); + + + + bool success = BaseClass::updateViewImpl(); + + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + return false; + } + + if (nullptr == m_mainGraphicalObject) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no m_mainGraphicalObject"; + return false; + } + + + + + // subsidiary : + + Private::MAP_IT_T mapIt(d->m_cont); + while (mapIt.hasNext()) + { + mapIt.next(); + const Private::ValueDataNA &vd = mapIt.value(); + QSharedPointer pathObj = vd.basicPathObj; + const OptionalData &optData = vd.optData; + if (pathObj) + { +// pathObj->setDebugInfo(m_mainGraphicalObject->debugInfo()); // will overwrite complete string -> skip + + if (optData.lineStyle) + pathObj->setLineStyle((*optData.lineStyle.data())); + else + pathObj->setLineStyle(m_mainGraphicalObject->lineStyle()); + + // transfer fillStyle only if relevant: + if (pathObj->pathType() == GraphicalObjectPathType::Polygon) + { + if (optData.fillStyle) + pathObj->setFillStyle((*optData.fillStyle.data())); + else + pathObj->setFillStyle(m_mainGraphicalObject->fillStyle()); + } + + pathObj->setVisibility(m_visibility); + pathObj->setVisibility(ObjectAppearance::Technical, m_visibilityTechnical); + + // Do not Transfer symbol. Only m_mainGraphicalObject uses ObjectAppearance::Tactical: +// pathObj->setSymbol(this->symbol()); + + + pathObj->setVisibility(ObjectAppearance::Tactical, false); + + pathObj->setPickable(m_mainGraphicalObject->pickable()); + + pathObj->setSelected(m_mainGraphicalObject->selected()); + + pathObj->setDrawOrder(m_internalDrawOrder); + + success = pathObj->updateView(); + if (!success) + { + int key = mapIt.key(); + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed, key=" << key; + } + } + } // while + + // : subsidiary + + + // singleSceneMarker : + updateSingleSceneMarker(m_visibility); // inclusive updateView() + // : singleSceneMarker + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + + +bool NuclearAgentImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (mapImpl) + { + switch (mapImpl->symbologyType()) + { + case AdvancedSymbology::SymbologyType::APP6D: + case AdvancedSymbology::SymbologyType::MIL2525D: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "10012500002719000000"); // SymbolSet = 25 271900 + break; + case AdvancedSymbology::SymbologyType::MIL2525C: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GUMPNR----"); // MIL2525D: 1388 GUMPNR---- -> Polygon (Radioactive Area U) +// m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"GUMPNZ----"); // MIL2525D: 0359 GUMPNZ---- -> Point (Nuclear Detonations Ground Zero U) + break; + case AdvancedSymbology::SymbologyType::APP6B: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GUCPBWR---"); // APP6B: 3935 GUCPBWR--- -> Polygon +// m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"GUCPBWP---"); // APP6B: 2871 GUCPBWP--- -> Point (Fallout Producing U) + break; + case AdvancedSymbology::SymbologyType::None: + // empty + break; + } // switch + } // if (mapImpl) + + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polygon); + + setLineStyle(LineStyle(Qt::yellow, 2)); + setFillStyle(FillStyle(Qt::yellow, FillPattern::Null)); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + // set some values to Private: + d->m_mapImpl = m_mapImpl; + d->m_logicalLayer = m_logicalLayerName; + d->m_debugInfo = m_debugInfo; + d->m_uniqueMapId = m_uniqueMapId; + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool NuclearAgentImpl::onMapExtentChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool successCenterImage = true; + if (d->m_centerImage) + { + bool visiScale = m_visibilityScaleRange.scaleHasVisibility(m_mapImpl->scale()); + d->m_centerImage->setVisibility(visiScale && m_visibility && m_visibilityTechnical); + successCenterImage = d->m_centerImage->updateView(); + } + + bool success = BaseClass::onMapExtentChanged() && successCenterImage; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/nuclear_agent_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/nuclear_agent_impl.h new file mode 100755 index 000000000..ebae69f6d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/nuclear_agent_impl.h @@ -0,0 +1,106 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of NuclearAgent + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + + +// Qt +#include + + + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The NuclearAgentImpl class displays a nuclear area. The outline is displayed as well a subsidiary lines. + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath displaying the main techn. Appearance / outline) + * - mainGraphicalObjectText (displaying the text/name) + * - tacticalAppearance (displaying the tact. Appearance) + * - Container of BasicPathObjectImpl's to display the subsidiary lines. + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT NuclearAgentImpl : public BasicPathObjectImpl, public virtual NuclearAgent +{ + using BaseClass = BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit NuclearAgentImpl(const QString &debugInfo = QString()); + virtual ~NuclearAgentImpl(); + +public: + NuclearAgentImpl(const NuclearAgentImpl &other) = delete; + NuclearAgentImpl &operator=(NuclearAgentImpl &other) = delete; + + void setUniqueId(const UniqueMapId &uniqueId) override; + + /*! \name Subsidiary polylines, identified by an arbitrary choose key.*/ + //@{ + PositionCont subsidiaryLine(int key) const override; + void setSubsidiaryLine(int key, const PositionCont &positions, const OptionalData &optData = OptionalData()) override; + + QList subsidiaryKeysInUse() override; + + void removeSubsidiaryLine(int key) override; + void removeSubsidiaryLines() override; + //@} + + /*! \name Impact*/ + //@{ + Point3D impactOrigin() const override; + void setImpactOrigin(const Point3D &impactOrigin) override; + //@} + + + /*! \name Single Detonation */ + //@{ + void setDetonation(const Point3D &origin, double radius) override; + bool hasDetonation() const override; + void resetDetonation() override; + //@} + + +protected: + bool updateViewImpl() override; + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + bool onMapExtentChanged() override; + + // internal/admin + + + // logical + + +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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/participant_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/participant_impl.cpp new file mode 100755 index 000000000..400d4a08c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/participant_impl.cpp @@ -0,0 +1,1237 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of Participant + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "participant_impl.h" +#include "basic_path_object_impl.h" +#include +#include +#include +#include +#include // for renderExtensions +#include // +#include + + +// ESRI +#include + + +// Qt +#include +#include +#include +#include + +#ifdef LTS_DEBUG +# include +#endif + +#include + + +namespace SituationMap +{ + +// to get access to 'init' +class DesignationLocalBasicPathObject final : public BasicPathObjectImpl +{ + DesignationLocalBasicPathObject() {} + virtual ~DesignationLocalBasicPathObject() {} +public: + using BasicPathObjectImpl::BasicPathObjectImpl; + using BasicPathObjectImpl::init; + using BasicPathObjectImpl::setUniqueId; +}; + +//----------------------------------------------------------------------------- +// class ParticipantImpl +//----------------------------------------------------------------------------- +ParticipantImpl::ParticipantImpl(const QString &debugInfo) + : Participant() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("ParticipantImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + m_origin.setMapObjectImpl(this); + initTextModifierData(); + + m_movementTrackCoords.setMapObjectImpl(this); + + // m_symbolResultWatcher is canceled on destruction. + // So it`s safe that no signal will be fired afterwards. + // To avoid CLang warnings use m_symbolResultWatcher as context (3rd para), although it´s not intended. + // Better idea? + QObject::connect(&m_symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady, + &m_symbolResultWatcher, std::bind(&ParticipantImpl::onSymbolResultReady, this)); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void ParticipantImpl::onSymbolResultReady() +{ + QSharedPointer symResult = m_symbolResultWatcher.symbolResult(); + + if (AdvancedSymbology::SymbolDictionary::doParanoiaChecks() && (! symResult)) + { + FATAL_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " no symResult..." + << " m_symDataToRender= '" << m_symDataToRender.toString(); + abort(); + } + + + if (! symResult) + return; // should NEVER happen! (Which object should have triggered the signal???) + + const SymbolImage &symImage = symResult->symbolImage(); + + if (AdvancedSymbology::SymbolDictionary::doParanoiaChecks()) + { + if (m_symDataToRender.sidc != symResult->symbolDataToRender().sidc || + m_symDataToRender.role != symResult->symbolDataToRender().role) + { + FATAL_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " resultReady...sidc or role mismatch..." + << " m_symDataToRender= '" << m_symDataToRender.toString() + << " symResult->symbolDataToRender()= '" << symResult->symbolDataToRender().toString(); + abort(); + } + + if (symImage.image().isNull()) + { + FATAL_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " image is null"; + abort(); + } + } + + // For preview purpose: + m_symbolImage.setValid(true); + m_symbolImage.setImage(symImage.image()); + + if (m_mainGraphicalObject) + { + GraphicalObjectPictureMarker *goPicture = + static_cast(m_mainGraphicalObject); + + goPicture->setImage(symImage.image()); + if (!goPicture->updateView()) + FATAL_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " updateView failed"; + + + // Note: + // XML-Symbols have their own/symbol-specific scale. + // We don´t have that value directly here, so we have to 'reinterpret' + // the final image: + // do this calculation only for Xml symbols, + // otherwise the other symbols will scale in two steps if you modify the slider + if (AdvancedSymbology::SidcCodec::decodeSidcType(symResult->symbolDataToRender().sidc) == AdvancedSymbology::SidcCodec::SidcType::CivilXml) + { + int imageSize = actualImage().size().width(); // width and heigth are equal + if (imageSize > 0) + m_scale4XmlSymbolWithSymbolSpecificScale = 0.7 * imageSize / (51 * m_scale.value() * m_symbol.value().size); // 51 == EsriSymbolSize + } + } else { + if (AdvancedSymbology::SymbolDictionary::doParanoiaChecks()) + { + FATAL_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " no m_mainGraphicalObject"; + abort(); + } + } +} + + +ParticipantImpl::~ParticipantImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + // clear all LaserDesignations: + m_designationData.reset(); + + for (const auto &mod : m_textModifierCont) + delete mod.goText; + + // abort pending call: + m_symbolResultWatcher.cancel(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +AdvancedSymbology::SymbologyType ParticipantImpl::symbologyType() const +{ + return (m_dictionary) ? m_dictionary->symbologyType() : AdvancedSymbology::SymbolDictionary::defaultSymbologyType(); +} // AdvancedSymbology::SymbologyType ParticipantImpl::symbologyType() const + + +void ParticipantImpl::initTextModifierData() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + TextModifierData data; + data.verticalAlignment = VerticalAlignment::Bottom; + + // right side: + data.horizontalAlignment = HorizontalAlignment::Left; + + int xOffset = 0; // pixel + int yOffset = -9; // pixel + + const int x = 27; + + // right side: + + data.offset = QPoint(x + xOffset, 30 + yOffset); + m_textModifierCont[ static_cast(TextModifier::EF_ReinforcedReduced) ] = data; + + data.offset = QPoint(x + xOffset, 15 + yOffset); + m_textModifierCont[ static_cast(TextModifier::G_StaffComments) ] = data; + + data.offset = QPoint(x + xOffset, yOffset); + m_textModifierCont[ static_cast(TextModifier::H_AdditionalInformation) ] = data; + + data.offset = QPoint(x + xOffset, -15 + yOffset); + m_textModifierCont[ static_cast(TextModifier::M_HigherFormation) ] = data; + + data.offset = QPoint(x + xOffset, -30 + yOffset); + m_textModifierCont[ static_cast(TextModifier::JKLNP) ] = data; + // left side: + data.horizontalAlignment = HorizontalAlignment::Right; + + data.offset = QPoint(-x + xOffset, 30 + yOffset); + m_textModifierCont[ static_cast(TextModifier::W_DateTimeGroup) ] = data; + + data.offset = QPoint(-x + xOffset, 15 + yOffset); + m_textModifierCont[ static_cast(TextModifier::XY_AltitudeLocation) ] = data; + + data.offset = QPoint(-x + xOffset, yOffset); + m_textModifierCont[ static_cast(TextModifier::V_Type) ] = data; + + data.offset = QPoint(-x + xOffset, -15 + yOffset); + m_textModifierCont[ static_cast(TextModifier::T_UniqueDesignation) ] = data; + + data.offset = QPoint(-x + xOffset, -30 + yOffset); + m_textModifierCont[ static_cast(TextModifier::Z_Speed) ] = data; + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void ParticipantImpl::setDebugInfo(const QString &debugInfo) +{ + // Do NOT call the baseclass, + // ParticipantImpl will create m_mainGraphicalObject only on demand. + m_debugInfo = debugInfo; +} + + +QString ParticipantImpl::toString() const +{ + QString str; + str.append(BaseClass::toString()); + str.append(QStringLiteral(" symbol='%1'").arg(m_symbol.value().toString())); + return str; +} + +Point3D ParticipantImpl::origin() const +{ + return m_origin.origin(); +} +bool ParticipantImpl::setOrigin(const Point3D &origin) +{ + m_updateObjectIndicator = m_origin.setOrigin(origin) || m_updateObjectIndicator; + return m_updateObjectIndicator; +} + + +// Just to enable using as AES item (template@AdapterDds) +void ParticipantImpl::setPositions(const PositionCont &posCont) +{ + Q_UNUSED(posCont); + // Should not be called +} + + +void ParticipantImpl::boundingBox(BoundingBox &bbox) const +{ + GraphicalObjectPictureMarker *goPicture = + static_cast(m_mainGraphicalObject); + + if (goPicture && !goPicture->image().isNull()) + { + const QSize &imgSize = goPicture->image().size(); // maybe a HotSpot has to be honored!? + + bbox = BoundingBox(BoundingBox::ExtentType::Pixel, + m_origin.origin(), // Sim + imgSize.width(), imgSize.height() // pixel + ); + } +} + +QColor ParticipantImpl::majorColor() const +{ + const QColor &partyCol = partyColor(); + return partyCol; +} + + +float ParticipantImpl::userSymbolOpacity() const +{ + return m_userSymbolOpacity.value(); +} +void ParticipantImpl::setUserSymbolOpacity(float userSymbolOpacity) +{ + m_userSymbolOpacity = userSymbolOpacity; +} + +SymbolDataToRender ParticipantImpl::symbolDataToRender(bool ignoreForcedColor) const +{ + if (ignoreForcedColor && m_party.forcedPartyColor.value().isValid()) + { + SymbolDataToRender copy = m_symDataToRender; + copy.fillColor = m_party.partyColor; + return copy; + } + return m_symDataToRender; +} + +QImage ParticipantImpl::actualImage() const +{ + if (! m_mainGraphicalObject) + return QImage(); + + GraphicalObjectPictureMarker *goPicture = + static_cast(m_mainGraphicalObject); + + return goPicture->image(); +} + +float ParticipantImpl::scale() const +{ + return m_scale.value(); +} +void ParticipantImpl::setScale(float scale) +{ + m_scale = scale; +} + + +bool ParticipantImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " m_debugInfo=" << m_debugInfo + << " m_party.partyId=" << m_party.partyId + << " m_party.changed()=" << m_party.changed() + << " m_origin()=" << m_origin.origin() + << " m_height=" << m_height + << " m_origin.originTranslated()=" << m_origin.originTranslated() + << " visibility=" << m_visibility + << " m_symbol.changed=" << m_symbol.changed() + << " m_symbol=" << m_symbol.value().toString() + << " extensionsChanged=" << m_extensionsChanged + << " m_movementTrackCoords= " << m_movementTrackCoords.positions(); + + // Prepare / create : + if (!m_mainGraphicalObject) + { + m_mainGraphicalObject = new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QLatin1String("#Image"), + m_uniqueMapId); + // to avoid incorrect number of positions, set them already here: + m_mainGraphicalObject->setOrigin(m_origin.originTranslated()); + } + // : Prepare / create + + TRACE_LOG(m_logger) << LOGFUNCTION << " START2" + << " m_debugInfo=" << m_debugInfo + << " m_scale=" << m_scale.value() + << " m_scaleChanged=" << m_scale.changed(); + + bool successPicture = true; + const bool visiSwitchedChanged = m_mainGraphicalObject->visibility() != m_visibility; + + // scenario: object invisible, partyId changes, object becomes visible: + // So we have to force an update of the cached partyColor: + if (visiSwitchedChanged && m_visibility) + { + // enforce following block (fetchSymbol) being proccessed + // Szenario: + // 'Organize Participants': add template to Pool (ParticipantImpl are created) + // Move them to Attached (partyId is updated, invisible) + // On 'Move Participants' the become visible, but partyId does NOT change. + // So the symbol was still ocker + m_party.partyId.setChanged(); + m_party.partyColor = QColor(); // set invalid to force cache update + + // NLDEVCTRL: Ausfallkreuz Teilnehmersymbol + // Auch diese Farbe zurücksetzen: + m_injuryData.resetChanged(); + } + + if (m_visibility) + { + if (m_mainGraphicalObject) + { + GraphicalObjectPictureMarker *goPicture = + static_cast(m_mainGraphicalObject); + + goPicture->setOrigin(m_origin.originTranslated()); + + + if (m_scale.changed() || + m_symbol.changed() || + m_userSymbolOpacity.changed() || + m_marked.changed() || + m_party.changed() || + m_extensionsChanged || + m_injuryData.changed) + { + m_symDataToRender = m_symbol.value().getDefaultRenderData(); + + bool isIdentityAssumed = false; + + if (!m_symDataToRender.sidc.isEmpty()) + { + AdvancedSymbology::SidcBuilder sidcBuilder(m_dictionary); + if (sidcBuilder.setSidc(m_symDataToRender.sidc)) + isIdentityAssumed = sidcBuilder.isIdentityAssumed(); + else + DEBUG_LOG(m_logger) << LOGFUNCTION << " sidcBuilder.setSidc failed" + << " m_debugInfo=" << m_debugInfo; + } + + + m_symDataToRender.role = this->partyRole(isIdentityAssumed); + + int widthPixel = SituationMap::SymbolSizeLdefault; // * m_mapImpl->symbologyScaleFactor(); + widthPixel *= m_symbol.value().size * m_scale.value(); + m_symDataToRender.sizeL = widthPixel; + + // main symbol color + const QColor &partyCol = partyColor(); + m_symDataToRender.fillColor = partyCol; + // use userSymbolOpacity only for fillColor: + m_symDataToRender.fillColor.setAlpha(m_userSymbolOpacity.value() * 255); + m_symDataToRender.fillColorReplace = (m_party.partyId() >= 0); + + // add other data + m_symDataToRender.injury = m_injuryData.injury; + m_symDataToRender.injuryColor = injuryColorInternal(); + m_symDataToRender.marking = m_marked.value(); + m_symDataToRender.additionalSymbols = m_additionalSymbols; + + if (goPicture->image().isNull()) + goPicture->setImage(m_dictionary->temporaryImage(m_symDataToRender.sizeL)); + + successPicture = true; + + // until the result is delivered: + m_scale4XmlSymbolWithSymbolSpecificScale = 1.0; + + // you expect findImage - correct + // But findImage handles multiPoint object special, which is not relevant here. + // So, in this case (for pointSymbols) use fetchSymbol: + m_symbolResultWatcher.setSymbolResult(m_dictionary->fetchSymbol(m_symDataToRender)); + } + + } // if (m_mainGraphicalObject) + + } + + // + // MovementTrack : + // + // MovementTrackData: + + TRACE_LOG(m_logger) << LOGFUNCTION + << " m_debugInfo=" << m_debugInfo + << " m_movementTrackEnabled=" << m_movementTrackEnabled + << ", m_movementTrackCoords.size=" << m_movementTrackCoords.size() + << ", m_movementTrackVisibility=" << m_movementTrackVisibility; + + bool successMovementTrack = true; + if (!m_movementTrackEnabled) + { + m_movementTrackCoords.clear(); + m_movementTrack.clear(); // destroy GraphicalObjectPath + } // if (m_movementTrackEnabled) + + // Display: + if (m_movementTrackCoords.size() >= 2) + { + if (! m_movementTrack) + { + m_movementTrack = + QSharedPointer(new GraphicalObjectPath(GraphicalObjectPathType::Polyline, + m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QStringLiteral("#Track"), + m_uniqueMapId)); + m_movementTrack->setLineStyle(LineStyle(partyColor(), 2, LinePattern::Solid)); + } + // move one layer backwards to put the Track BEHIND the image (drawOrder==0): + m_movementTrack->setDrawOrder(m_internalDrawOrder - 1); + + m_movementTrack->setPositions(m_movementTrackCoords.positionsTranslated()); + + if (m_party.changed()) + m_movementTrack->rLineStyle().setLineColor(partyColor()); + + if (m_scale.changed()) + m_movementTrack->setScale(m_scale.value()>1.0f ? m_scale.value() : 1.0f); + + m_movementTrack->setVisibility(m_visibility && m_visibilityTechnical && m_movementTrackVisibility); + successMovementTrack = m_movementTrack->updateView(); + } + else + m_movementTrack.clear(); // destroy GraphicalObjectPath + // : MovementTrack + + bool successTextModifier = updateViewImplTextModifier(); + bool successRanges = updateViewImplRanges(); + bool successDesignation = updateViewImplDesignation(); + + bool successDirectionIndicators = updateViewImplDirectionIndicators(); + + bool success = BaseClass::updateViewImpl() && + successPicture && + successTextModifier && + successRanges && + successDesignation && + successDirectionIndicators && + successMovementTrack; + + // reset changed flags is done via updateViewImplPost + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << ", success=" << success; + return success; +} + +bool ParticipantImpl::updateViewImplTextModifier() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool success = true; + + const int size = static_cast(TextModifier::END); + for (int i = 0; i < size; ++i) + { + TextModifierData &tmData = m_textModifierCont[i]; + + if (tmData.text.isEmpty()) + { + // text empty + delete tmData.goText; + tmData.goText = nullptr; + } + else + { + // text filled: + if (!tmData.goText) + { + QString debugInfo = QStringLiteral("%1#textMod(%2)").arg(m_debugInfo).arg(i); + + tmData.goText = new GraphicalObjectText(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + debugInfo, + m_uniqueMapId); + } + } + + if (tmData.goText) + { + const bool bNewVisibility = tmData.visible && m_visibility; + tmData.goText->setVisibility(bNewVisibility); + + // save following fo invisble object + if (bNewVisibility) + { + tmData.goText->setText(tmData.text); + // move one layer topwards to let the GraphicalObjectRange stay behind: + tmData.goText->setDrawOrder(m_internalDrawOrder + 1); + + TextStyle ts; + const bool bigger = (i == static_cast(TextModifier::EF_ReinforcedReduced) && + tmData.text.compare(QLatin1String("+")) == 0); + + const float finalScale = m_scale.value() * m_symbol.value().size * m_scale4XmlSymbolWithSymbolSpecificScale; + ts.setSize(finalScale * (bigger ? 22.5 : 15)); + + if (m_party.partyId() < 0) + ts.setTextColor(QColor(Qt::black)); + else + ts.setTextColor(partyColor()); + + // Alignment: + ts.setHorizontalAlignment(tmData.horizontalAlignment); + ts.setVerticalAlignment(tmData.verticalAlignment); + tmData.goText->setTextStyle(ts); + + // Offset: + tmData.goText->setXOffset(finalScale * static_cast(tmData.offset.x())); + tmData.goText->setYOffset(finalScale * static_cast(tmData.offset.y())); + } + + // origin is required for updateView (otherwise 'incorrect number of positions'): + // (originTranslated is cached) + tmData.goText->setOrigin(m_origin.originTranslated()); + + success = success && tmData.goText->updateView(); + } + + } // for + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << ", success=" << success; + return success; +} + +QImage generateDirectionIndicatorImage(int sizeL, + const QColor &defaultColor, + const QMap &directionIndicatorCont) +{ + const int fullSize = sizeL * 1.7 * 2.0; // try and error: '1.7'; + + QImage fullImage(fullSize, fullSize, QImage::Format_ARGB32); + fullImage.fill(Qt::transparent); + +#ifdef NEVER + // for debugging : + if (1) + { + QPainter painter(&fullImage); + QPen pen(Qt::black); + pen.setWidth(2.0); + painter.setPen(pen); + painter.drawRect(fullImage.rect()); + } + // : for debugging +#endif + + QPainter painter(&fullImage); + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + + const QPointF center((fullImage.width() / 2), (fullImage.height() / 2)); + const QPointF dest(center.x(), 0); + const int width = fullImage.size().width(); + const int height = fullImage.size().height(); + + for (const auto &item : directionIndicatorCont) + { + QColor finalColor = item.color.isValid() ? item.color : defaultColor; + + QPen pen(finalColor); + pen.setWidthF(item.lineWidth * double(sizeL)/SituationMap::SymbolSizeLdefault); + pen.setStyle(item.lineStyle); + painter.setPen(pen); + + painter.save(); + + painter.translate(width / 2, height / 2); + painter.rotate(item.directionDegree); + painter.translate(-width / 2, -height / 2); + + // line + painter.drawLine(center, dest); // | to top + + // arrow + QPen penArrow(finalColor); + penArrow.setWidthF(item.lineWidth * double(sizeL)/SituationMap::SymbolSizeLdefault); + painter.setPen(penArrow); + + QPolygon poly; + poly.reserve(3); + + int aLength = 6 * pen.width(); + int aWidth = 2 * pen.width(); + + // 2 || (dest) + // | | + // 1 | | 3 + + poly.append(dest.toPoint() + QPoint(-aWidth, aLength) ); + poly.append(dest.toPoint()); + poly.append(dest.toPoint() + QPoint(+aWidth, aLength)); + + painter.drawPolyline(poly); + + painter.restore(); + } // for + + return fullImage; +} + + +bool ParticipantImpl::updateViewImplDirectionIndicators() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " m_directionIndicatorCont.count()=" << m_directionIndicatorCont.count(); + + bool success = true; + + if (m_directionIndicatorCont.isEmpty()) + m_directionIndicatorPms.clear(); // destroy object + else + { + // needs to be created? + if (!m_directionIndicatorPms) + { + m_directionIndicatorPms = QSharedPointer(new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QLatin1String("#DirectionIndImage"), + m_uniqueMapId)); + } + const QImage &image = generateDirectionIndicatorImage(m_symDataToRender.sizeL, majorColor(), m_directionIndicatorCont); + m_directionIndicatorPms->setImage(image); + m_directionIndicatorPms->setOrigin(m_origin.originTranslated()); + m_directionIndicatorPms->setVisibility(m_mainGraphicalObject->visibility()); + m_directionIndicatorPms->setSelected(m_selected); + + // move one level backwards to put the GraphicalObjectRange BEHIND the image (drawOrder==0), but on top of WaeaponRanges: + m_directionIndicatorPms->setDrawOrder(m_internalDrawOrder - 1); + + success = m_directionIndicatorPms->updateView(); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << ", success=" << success; + return success; +} + +bool ParticipantImpl::updateViewImplRanges() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " number Ranges=" << m_ranges.value().count(); + + bool success = true; + + const QVector &ranges = m_ranges.value(); + int size = ranges.size(); + m_graphObjRanges.resize(size); + + for (int i = 0; i < size; ++i) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " i=" << i; + + // get some values/references: + int range = ranges[i]; + QSharedPointer &goRange = m_graphObjRanges[i]; + const bool visi = m_visibility && m_rangesVisibility; + + if (!visi) + { + goRange.clear(); // destroy object + } + else + { + if (goRange.isNull()) + { + goRange = QSharedPointer + (new GraphicalObjectRange(m_mapImpl->getEsriGeoView() + , m_graphicsLayer + , QStringLiteral("%1#Range_i(%2)_r(%3)").arg(m_debugInfo).arg(i).arg(range) + , m_uniqueMapId) + ); + } + + // move two level backwards to put the GraphicalObjectRange BEHIND the image (drawOrder==0): + goRange->setVisibility(visi); + goRange->setDrawOrder(m_internalDrawOrder - 2); + goRange->setOrigin(m_origin.originTranslated()); + goRange->setRange(range, i * 15); + goRange->setLineStyle(LineStyle(partyColor(), 3, LinePattern::Solid)); + goRange->setFillStyle(FillStyle(Qt::black, FillPattern::Null)); + + bool updated = goRange->updateView(); + success = success && updated; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " updated=" << updated; + } // if (!visi) + + }// for + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << ", success=" << success; + return success; +} + +bool ParticipantImpl::updateViewImplDesignation() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << ", m_debugInfo=" << m_debugInfo + << ", m_designationData.value().size()=" << m_designationData.value().size(); + bool success = true; + + const QVector &designationDataVec = m_designationData.value(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " START 11" << ", m_debugInfo=" << m_debugInfo + << " m_designationMapObjects.size() = " << m_designationMapObjects.size() + << " m_designationData.value().size() = " << designationDataVec.size(); + + m_designationMapObjects.resize(designationDataVec.size()); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " START 22" << ", m_debugInfo=" << m_debugInfo + << " m_designationMapObjects.size() = " << m_designationMapObjects.size() + << " m_designationData.value().size() = " << designationDataVec.size(); + + int idx = -1; + for (const Participant::DesignationData &designationData : std::as_const(designationDataVec)) + { + ++idx; + + QSharedPointer &bpoRef = m_designationMapObjects[idx]; + if (bpoRef.isNull()) + { + QString debugInfo = QString(QStringLiteral("%1#Designation_code_%2")).arg(m_debugInfo).arg(idx); + DesignationLocalBasicPathObject *designationDPO = new DesignationLocalBasicPathObject(debugInfo); + + success = designationDPO->init(m_mapImpl, m_logicalLayerName, GraphicalObjectPathType::Polyline); + designationDPO->setUniqueId(m_uniqueMapId); + designationDPO->setArrowHeadEnd(true); + if (success) + { + bpoRef = QSharedPointer(static_cast(designationDPO)); + } else { + ERROR_LOG(m_logger) << LOGFUNCTION << ", m_debugInfo=" << m_debugInfo + << " designationPO->init failed"; + designationDPO->removeMySelf(); + continue; + } + } + + // since DesignationLocalBasicPathObject is a MapObject (and not GraphicalObject) + // untranslated coords have to be used here: + bpoRef->setPositions({ this->origin(), designationData.spotLocation }); + bpoRef->setTextOnSegment(0, QObject::tr("Code: %1").arg(designationData.code)); + bpoRef->setSelected(m_selected); + bpoRef->setLineStyle(LineStyle(partyColor(), 3, LinePattern::DashDot)); // partyColor may change + bpoRef->setVisibility(m_visibility); + + const bool updated = bpoRef->updateView(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " START" << ", m_debugInfo=" << m_debugInfo << " updated= " << updated; + + success = success && updated; + + DEBUG_LOG(m_logger) << LOGFUNCTION << ", m_debugInfo=" << m_debugInfo + << ", updated= " << updated + << ", success= " << success; + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << ", m_debugInfo=" << m_debugInfo + << ", success=" << success; + return success; +} + +bool ParticipantImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool success = BaseClass::init(mapImpl, logicalLayerName); + + if (mapImpl) + m_dictionary = &(m_mapImpl->symbolDictionary()); + + success = success && (m_dictionary); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << ", success=" << success; + return success; +} + +const QColor &ParticipantImpl::partyColor() const +{ + if (m_party.forcedPartyColor.value().isValid()) + return m_party.forcedPartyColor.value(); + + if ((m_party.partyId.value() == -1) && m_symbol.value().color.isValid()) + return m_symbol.value().color; + + if (m_party.partyId.changed() || m_party.useLightColor.changed() || !m_party.partyColor.isValid()) + { + m_party.partyColor = + m_mapImpl->partyIdToColorFunctor().partyIdToColor(m_party.partyId.value(), + m_party.useLightColor.value()); + } + return m_party.partyColor; +} + +QColor ParticipantImpl::partyColor(int partyId, bool isOutOfDate) const +{ + return BaseClass::partyColor(partyId, isOutOfDate); +} + + +//#define NEVER +#ifdef NEVER +QStringList sl; +sl << "uniquedesignation"; +sl << "type"; +sl << "datetimevalid"; +sl << "quantity"; + +sl << "speed"; +sl << "staffcomment"; +sl << "z"; +sl << "x"; +sl << "z/x"; +sl << "y"; +sl << "combateffectiveness"; +sl << "iff_sif"; + + +for (const QString &item : sl) + m_message->setProperty(item, QString("%1_V").arg(item)); + + +// toJson: +// "{"type":"esriMultiPartCIMSymbol","parts":[{"id":"GHMPNZ----_mil2525c"," +// content":[{"type":"CIMPointSymbol","symbolLayers":[{"type":"CIMTextLayer","fontFamilyName":"Arial","fontStyleName":"Bold","symbol":{"type":"CIMPolygonSymbol","symbolLayers":[{"type":"CIMFilledStroke","width":1,"pattern":{"type":"CIMSolidPattern","color":[255,255,255,255]},"capStyle":"Round","joinStyle":"Round","miterLimit":10,"alignment":"Center","lineStyle3D":"Strip","patternFollowsStroke":true},{"type":"CIMFill","pattern":{"type":"CIMSolidPattern","color":[255,0,0,255]}}]},"horizontalAlignment":"Left","verticalAlignment":"Baseline","textCase":"Normal","textDirection":"LTR","height":8.25,"letterWidth":100,"wordSpacing":100,"text": +// +// [uniquedesignation]","textPlacement":{"type":"point","method":"southwest","offset":1}}]},{"type":"CIMPointSymbol","symbolLayers":[{"type":"CIMTextLayer","fontFamilyName":"Arial","fontStyleName":"Bold","symbol":{"type":"CIMPolygonSymbol","symbolLayers":[{"type":"CIMFilledStroke","width":1,"pattern":{"type":"CIMSolidPattern","color":[255,255,255,255]},"capStyle":"Round","joinStyle":"Round","miterLimit":10,"alignment":"Center","lineStyle3D":"Strip","patternFollowsStroke":true},{"type":"CIMFill","pattern":{"type":"CIMSolidPattern","color":[255,0,0,255]}}]},"horizontalAlignment":"Left","verticalAlignment":"Baseline","textCase":"Normal","textDirection":"LTR","height":8.25,"letterWidth":100,"wordSpacing":100,"text": +// +// [type]","textPlacement":{"type":"point","method":"west","offset":1}}]},{"type":"CIMPointSymbol","symbolLayers":[{"type":"CIMTextLayer","fontFamilyName":"Arial","fontStyleName":"Bold","symbol":{"type":"CIMPolygonSymbol","symbolLayers":[{"type":"CIMFilledStroke","width":1, +// pattern":{"type":"CIMSolidPattern","color":[255,255,255,255]},"capStyle":"Round","joinStyle":"Round","miterLimit":10,"alignment":"Center","lineStyle3D":"Strip","patternFollowsStroke":true},{"type":"CIMFill","pattern":{"type":"CIMSolidPattern","color":[255,0,0,255]}}]},"horizontalAlignment":"Left","verticalAlignment":"Baseline","textCase":"Normal","textDirection":"LTR","height":8.25,"letterWidth":100,"wordSpacing":100,"text":"" + +// [datetimevalid]","textPlacement":{"type":"point","method":"northwest","offset":1}}]},{"type":"CIMPointSymbol","symbolLayers": +// [{"type":"CIMTextLayer","fontFamilyName":"Arial","fontStyleName":"Bold","symbol":{"type":"CIMPolygonSymbol","symbolLayers":[{"type":"CIMFilledStroke","width":1,"pattern":{"type":"CIMSolidPattern","color":[255,255,255,255]},"capStyle":"Round","joinStyle":"Round","miterLimit":10,"alignment":"Center","lineStyle3D":"Strip","patternFollowsStroke":true},{"type":"CIMFill","pattern":{"type":"CIMSolidPattern","color":[255,0,0,255]}}]},"horizontalAlignment":"Left","verticalAlignment":"Baseline","textCase":"Normal","textDirection":"LTR","height":8.25,"letterWidth":100,"wordSpacing":100, +// "text":"[x],[y]","textPlacement": +// {"type":"point","method":"south","offset":1}}]},{"type":"CIMPointSymbol","symbolLayers":[{"type":"CIMTextLayer","fontFamilyName":"Arial","fontStyleName":"Bold","symbol":{"type":"CIMPolygonSymbol","symbolLayers":[{"type":"CIMFilledStroke","width":1,"pattern":{"type":"CIMSolidPattern","color":[255,255,255,255]},"capStyle":"Round", +// "joinStyle":"Round","miterLimit":10,"alignment":"Center","lineStyle3D":"Strip","patternFollowsStroke":true},{"type":"CIMFill","pattern":{"type":"CIMSolidPattern","color":[255,0,0,255]}}]},"horizontalAlignment":"Left","verticalAlignment":"Baseline","textCase":"Normal","textDirection":"LTR","height":8.25,"letterWidth":100,"wordSpacing":100,"text":" + +// [quantity]","textPlacement":{"type":"point","method":"north","offset":1}}]},{"type":"CIMPointSymbol","symbolLayers":[{"type":"CIMTextLayer", +// "fontFamilyName":"Arial","fontStyleName":"Bold","symbol":{"type":"CIMPolygonSymbol","symbolLayers":[{"type":"CIMFilledStroke","width":1, +// "pattern":{"type":"CIMSolidPattern","color":[255,255,255,255]},"capStyle":"Round","joinStyle":"Round","miterLimit":10, +// "alignment":"Center","lineStyle3D":"Strip","patternFollowsStroke":true},{"type":"CIMFill","pattern":{"type":"CIMSolidPattern","color":[255,0,0,255]}}]},"horizontalAlignment":"Left","verticalAlignment":"Baseline","textCase":"Normal","textDirection":"LTR","height":8.25,"letterWidth":100,"wordSpacing":100,"text":" + +// [additionalinformation]", "textPlacement":{"type":"point","method":"northeast","offset":1}}]},{"type":"CIMPointSymbol", +// "symbolLayers":[{"type":"CIMVectorMarker","size":25,"anchorPoint":{"x":0,"y":0},"anchorPointUnits":"Relative","scaleStrokesAndFills":true,"offsetY":9,"dominantSizeAxis3D":"Z","frame":{"xmin":0,"ymin":0,"xmax":515,"ymax":515},"markerGraphics":[{"type":"CIMMarkerGraphic", +// "geometry":{"rings":[[[209.8,357.1],[305.3,357.1],[305.3,91.8],[209.8,91.8],[209.8,357.1]]]},"symbol":{"type":"CIMPolygonSymbol","symbolLayers":[{"type":"CIMFilledStroke","width":6.432937632884479, +// "pattern":{"type":"CIMSolidPattern","color":[255,0,0,255]},"capStyle": +// "Butt","joinStyle":"Miter","miterLimit":112,"alignment":"Center","lineStyle3D":"Strip","patternFollowsStroke":true}, +// {"type":"CIMFill","pattern":{"type":"CIMSolidPattern","color":[255,255,128,255]}}]}}, +// {"type":"CIMMarkerGraphic","geometry":{"rings":[[[232.1,197.9],[232.1,258.6],[240.4,258.6],[272.3,210.9],[272.3,258.6],[280,258.6],[280,197.9],[271.8,197.9],[239.8,245.6],[239.8,197.9],[232.1,197.9]]]}," +// symbol":{"type":"CIMPolygonSymbol","symbolLayers": +// [{"type":"CIMFilledStroke","width":1.2774627923458541,"pattern":{"type":"CIMSolidPattern","color":[255,0,0,255]}, +// "capStyle":"Butt","joinStyle":"Miter","miterLimit":112,"alignment":"Center","lineStyle3D":"Strip","patternFollowsStroke":true}, +// {"type":"CIMFill","pattern":{"type":"CIMSolidPattern","color":[255,0,0,255]}}]}}, +// {"type":"CIMMarkerGraphic","geometry":{"rings":[[[257.5,357.1],[124.9,357.1],[128.3,385.1],[136.5,412.4],[148.5,435.9],[164.2,455.3],[182.9,470.3],[204.7,481.1],[229.6,487.6],[257.5,489.7],[285.4,487.6],[310.3,481.1],[332.2,470.3],[350.9,455.3],[366.5,435.9],[378.5,412.4],[386.7,385.1],[390.2,357.1],[257.5,357.1]]]}, +// "symbol":{"type":"CIMPolygonSymbol","symbolLayers":[{"type":"CIMFilledStroke","width":6.432937632884479,"pattern":{"type":"CIMSolidPattern","color":[255,0,0,255]}, +// "capStyle":"Butt","joinStyle":"Miter","miterLimit":112,"alignment":"Center","lineStyle3D":"Strip","patternFollowsStroke":true}, +// {"type":"CIMFill","pattern":{"type":"CIMSolidPattern","color":[255,255,128,255]}}]}}]}]}]}]}" (ication/ngl_qt_msg_handler.inc:21) + +// Second: +// 2015-05-06 09:39:18,903 INFO SituationMap.MapImpl |#| SituationMap::MapImpl::onMouseRelease onMouseRelease for left mouse button gId= 4 symbol.toJson= '{"type":"esriMultiPartCIMSymbol","parts":[{"id":"SFGPUCD---_mil2525c","content":[{"type":"CIMPointSymbol","symbolLayers":[{"type":"CIMTextLayer","fontFamilyName":"Arial","fontStyleName":"Bold","symbol":{"type":"CIMPolygonSymbol","symbolLayers":[{"type":"CIMFilledStroke","width":1,"pattern":{"type":"CIMSolidPattern","color":[255,255,255,255]},"capStyle":"Round","joinStyle":"Round","miterLimit":10,"alignment":"Center","lineStyle3D":"Strip","patternFollowsStroke":true},{"type":"CIMFill","pattern":{"type":"CIMSolidPattern","color":[0,0,0,255]}}]},"horizontalAlignment":"Left","verticalAlignment":"Baseline","textCase":"Normal","textDirection":"LTR","height":8.25,"letterWidth":100,"wordSpacing":100,"text":"[uniquedesignation] +// [speed]","textPlacement":{"type":"point","method":"southwest","offset":1}}]},{"type":"CIMPointSymbol","symbolLayers":[{"type":"CIMTextLayer","fontFamilyName":"Arial","fontStyleName":"Bold","symbol":{"type":"CIMPolygonSymbol","symbolLayers":[{"type":"CIMFilledStroke","width":1,"pattern":{"type":"CIMSolidPattern","color":[255,255,255,255]},"capStyle":"Round","joinStyle":"Round","miterLimit":10,"alignment":"Center","lineStyle3D":"Strip","patternFollowsStroke":true},{"type":"CIMFill","pattern":{"type":"CIMSolidPattern","color":[0,0,0,255]}}]},"horizontalAlignment":"Center","verticalAlignment":"Center","textCase":"Normal","textDirection":"LTR","height":8.25,"letterWidth":100,"wordSpacing":100,"text":"[datetimevalid] +// [z]/[x],[y]","textPlacement":{"type":"point","method":"northwest","offset":1}}]},{"type":"CIMPointSymbol","symbolLayers":[{"type":"CIMTextLayer","fontFamilyName":"Arial","fontStyleName":"Bold","symbol":{"type":"CIMPolygonSymbol","symbolLayers":[{"type":"CIMFilledStroke","width":1,"pattern":{"type":"CIMSolidPattern","color":[255,255,255,255]},"capStyle":"Round","joinStyle":"Round","miterLimit":10,"alignment":"Center","lineStyle3D":"Strip","patternFollowsStroke":true},{"type":"CIMFill","pattern":{"type":"CIMSolidPattern","color":[0,0,0,255]}}]},"horizontalAlignment":"Center","verticalAlignment":"Center","textCase":"Normal","textDirection":"LTR","height":8.25,"letterWidth":100,"wordSpacing":100,"text":"[reinforced] +// [staffcomment]","textPlacement":{"type":"point","method":"northeast","offset":1}}]},{"type":"CIMPointSymbol","symbolLayers":[{"type":"CIMTextLayer","fontFamilyName":"Arial","fontStyleName":"Bold","symbol":{"type":"CIMPolygonSymbol","symbolLayers":[{"type":"CIMFilledStroke","width":1,"pattern":{"type":"CIMSolidPattern","color":[255,255,255,255]},"capStyle":"Round","joinStyle":"Round","miterLimit":10,"alignment":"Center","lineStyle3D":"Strip","patternFollowsStroke":true},{"type":"CIMFill","pattern":{"type":"CIMSolidPattern","color":[0,0,0,255]}}]},"horizontalAlignment":"Center","verticalAlignment":"Center","textCase":"Normal","textDirection":"LTR","height":8.25,"letterWidth":100,"wordSpacing":100,"text":"[additionalinformation]","textPlacement":{"type":"point","method":"east","offset":1}}]},{"type":"CIMPointSymbol","symbolLayers":[{"type":"CIMTextLayer","fontFamilyName":"Arial","fontStyleName":"Bold","symbol":{"type":"CIMPolygonSymbol","symbolLayers":[{"type":"CIMFilledStroke","width":1,"pattern":{"type":"CIMSolidPattern","color":[255,255,255,255]},"capStyle":"Round","joinStyle":"Round","miterLimit":10,"alignment":"Center","lineStyle3D":"Strip","patternFollowsStroke":true},{"type":"CIMFill","pattern":{"type":"CIMSolidPattern","color":[0,0,0,255]}}]},"horizontalAlignment":"Center","verticalAlignment":"Center","textCase":"Normal","textDirection":"LTR","height":8.25,"letterWidth":100,"wordSpacing":100,"text":"[higherformation] +// [combateffectiveness]/[iff_sif]","textPlacement":{"type":"point","method":"southeast","offset":1}}]},{"type":"CIMPointSymbol","symbolLayers":[{"type":"CIMVectorMarker","size":25,"anchorPoint":{"x":0,"y":0},"anchorPointUnits":"Relative","scaleStrokesAndFills":true,"dominantSizeAxis3D":"Z","frame":{"xmin":0,"ymin":0,"xmax":710,"ymax":530},"markerGraphics":[{"type":"CIMMarkerGraphic","geometry":{"rings":[[[85,445],[85,85],[625,85],[625,445],[85,445]]]},"symbol":{"type":"CIMPolygonSymbol","symbolLayers":[{"type":"CIMFilledStroke","width":15,"pattern":{"type":"CIMSolidPattern","color":[0,0,0,255]},"capStyle":"Butt","joinStyle":"Miter","miterLimit":10,"alignment":"Center","lineStyle3D":"Strip","patternFollowsStroke":true},{"type":"CIMFill","pattern":{"type":"CIMSolidPattern","color":[128,224,255,255]}}]}},{"type":"CIMMarkerGraphic","geometry":{"rings":[[[91,85],[113,120],[138,151],[168,178],[201,200],[237,219],[275,232],[314,240],[355,243],[396,240],[435,232],[473,219],[509,200],[542,178],[571,151],[597,120],[619,85],[91,85]]]},"symbol":{"type":"CIMPolygonSymbol","symbolLayers":[{"type":"CIMFilledStroke","width":15,"pattern":{"type":"CIMSolidPattern","color":[0,0,0,255]},"capStyle":"Butt","joinStyle":"Miter","miterLimit":10,"alignment":"Center","lineStyle3D":"Strip","patternFollowsStroke":true},{"type":"CIMFill","pattern":{"type":"CIMSolidPattern","color":[128,224,255,255]}}]}}]}]}]}]}' (t/Legatus/Plugins/SituationMap/MapImpl/MapImpl.cpp:1019) + +#endif + + + + +QString ParticipantImpl::textModifier(TextModifier textModifier) const +{ + return m_textModifierCont[ static_cast(textModifier) ].text; +} +void ParticipantImpl::setTextModifier(TextModifier textModifier, const QString &text) +{ + m_textModifierCont[ static_cast(textModifier) ].text = text; +} + +bool ParticipantImpl::textModifierVisibility(TextModifier textModifier) const +{ + return m_textModifierCont[ static_cast(textModifier) ].visible; +} +void ParticipantImpl::setTextModifierVisibility(TextModifier textModifier, bool visible) +{ + m_textModifierCont[ static_cast(textModifier) ].visible = visible; +} + + +QColor ParticipantImpl::injuryColor() const +{ + return m_injuryData.injuryColor; +} +Injury ParticipantImpl::injury() const +{ + return m_injuryData.injury; +} +bool ParticipantImpl::setInjury(Injury injury, + const QColor &injuryColor) +{ + bool changed = false; + + if (m_injuryData.injury != injury) + { + m_injuryData.injury = injury; + m_injuryData.changed = true; + changed = true; + } + + if (m_injuryData.injuryColor != injuryColor) + { + m_injuryData.injuryColor = injuryColor; + m_injuryData.changed = true; + changed = true; + } + + // switch from partyId to color: + m_injuryData.injuryPartyId = -1; + return changed; +} + +int ParticipantImpl::injuryPartyId() const +{ + return m_injuryData.injuryPartyId; +} +bool ParticipantImpl::setInjury(Injury injury, int partyId) +{ + bool changed = false; + + if (m_injuryData.injury != injury) + { + m_injuryData.injury = injury; + m_injuryData.changed = true; + changed = true; + } + + if (m_injuryData.injuryPartyId != partyId) + { + m_injuryData.injuryPartyId = partyId; + m_injuryData.changed = true; + changed = true; + } + return changed; +} + +bool ParticipantImpl::setPartyIdChanged(int partyId) +{ + bool changed = BaseClass::setPartyIdChanged(partyId); + if ((partyId < 0) || (m_injuryData.injuryPartyId == partyId)) + { + m_injuryData.changed = true; + changed = true; + } + return changed; +} + + +const QColor &ParticipantImpl::injuryColorInternal() +{ + if (m_injuryData.changed) + { + if (m_injuryData.partyIdValid()) + m_injuryData.injuryColor = this->partyColor(m_injuryData.injuryPartyId); + else + m_injuryData.injuryColor = QColor(100, 100, 0); // 100, 100, 0 -> ocker; same as ASB oreignInterfaceImpl::findImage() + } + return m_injuryData.injuryColor; +} + + + + + +bool ParticipantImpl::additionalSymbol(AdditionalSymbolType additionalSymbolType) const +{ + return (m_additionalSymbols.contains(additionalSymbolType)); +} + +bool ParticipantImpl::setAdditionalSymbol(AdditionalSymbolType additionalSymbolType, bool newState) +{ + bool changed = false; + + if (newState) + { + if (!m_additionalSymbols.contains(additionalSymbolType)) + { + changed = true; + m_extensionsChanged = true; + m_additionalSymbols.insert(additionalSymbolType); + } + } + else + { + if (m_additionalSymbols.contains(additionalSymbolType)) + { + changed = true; + m_extensionsChanged = true; + m_additionalSymbols.remove(additionalSymbolType); + } + } + return changed; +} + +bool SituationMap::ParticipantImpl::isMarked() const +{ + return m_marked.value(); +} + +bool ParticipantImpl::setMarked(bool marked) +{ + bool changed = m_marked.value() != marked; + m_marked = marked; + return changed; +} + +void ParticipantImpl::setDirectionIndicator(int key, const DirectionIndicatorData &data) +{ + m_directionIndicatorCont[key] = data; +} + +void ParticipantImpl::removeDirectionIndicator(int key) +{ + if (key < 0) + m_directionIndicatorCont.clear(); + else + m_directionIndicatorCont.remove(key); +} + +bool ParticipantImpl::isDirectionIndicatorsVisible() const +{ + return m_directionIndicatorsVisibility.value(); +} + +void ParticipantImpl::setDirectionIndicatorsVisibility(bool visible) +{ + m_directionIndicatorsVisibility = visible; +} + +QVector ParticipantImpl::ranges() const +{ + return m_ranges.value(); +} +bool ParticipantImpl::setRanges(const QVector &ranges) +{ + bool changed = m_ranges.value() != ranges; + m_ranges = ranges; + return changed; +} + +bool ParticipantImpl::isRangesVisible() const +{ + return m_rangesVisibility; +} +void ParticipantImpl::setRangesVisibility(bool visible) +{ + m_rangesVisibility = visible; +} + +bool ParticipantImpl::setDesignations(const QVector &designations) +{ + m_designationData = designations; + return m_designationData.changed(); +} + +//bool ParticipantImpl::addUpdateDesignation(const DesignationData &designationData) +//{ +// // try the keep the m_designationData clean +// if (designationData.isNull()) +// return removeDesignation(designationData.code); + +// bool changed = m_designationData.value(designationData.code) != designationData; +// if (changed) +// m_designationData[designationData.code] = designationData; +// return changed; +//} + +//bool ParticipantImpl::removeDesignation(int code) +//{ +// return (m_designationData.remove(code) > 0); +//} + +bool ParticipantImpl::isMovementTrackEnabled() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " m_movementTrackEnabled=" << m_movementTrackEnabled; + return m_movementTrackEnabled; +} + +void ParticipantImpl::setMovementTrackEnabled(bool enable) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " m_movementTrackEnabled=" << m_movementTrackEnabled; + m_movementTrackEnabled = enable; + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " m_movementTrackEnabled=" << m_movementTrackEnabled; +} + +void ParticipantImpl::setMovementTrack(const PositionCont &movementTrack) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " m_debugInfo=" << m_debugInfo + << ", movementTrack=" << movementTrack; + m_movementTrackCoords.setPositions(movementTrack); + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " m_debugInfo=" << m_debugInfo + << ", movementTrack=" << movementTrack; +} + +void ParticipantImpl::addToMovementTrack(const Point3D &addToMovementTrack) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + m_movementTrackCoords.addSinglePosition(addToMovementTrack); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +PositionCont ParticipantImpl::movementTrack() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " movementTrackSize=" << m_movementTrackCoords.size(); + return m_movementTrackCoords.positions(); +} + +int ParticipantImpl::sizeOfMovementTrack() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " movementTrackSize=" << m_movementTrackCoords.size(); + return m_movementTrackCoords.size(); +} + +bool ParticipantImpl::isMovementTrackVisible() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " m_movementTrackVisibility=" << m_movementTrackVisibility; + return m_movementTrackVisibility; +} + +void ParticipantImpl::setMovementTrackVisibility(bool enable) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + m_movementTrackVisibility = enable; + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " m_movementTrackVisibility=" << m_movementTrackVisibility; +} + +int ParticipantImpl::numberOfValuesChanged() +{ + int count = BaseClass::numberOfValuesChanged(); + + if (m_userSymbolOpacity.changed()) { WARNING_LOG(m_logger) << LOGFUNCTION << " START "; count++; } + if (m_scale.changed()) { WARNING_LOG(m_logger) << LOGFUNCTION << " START "; count++; } + + if (m_extensionsChanged) { WARNING_LOG(m_logger) << LOGFUNCTION << " START "; count++; } + + if (m_marked.changed()) { WARNING_LOG(m_logger) << LOGFUNCTION << " START "; count++; } + if (m_ranges.changed()) { WARNING_LOG(m_logger) << LOGFUNCTION << " START "; count++; } + if (m_injuryData.changed) { WARNING_LOG(m_logger) << LOGFUNCTION << " START "; count++; } + + return count; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/participant_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/participant_impl.h new file mode 100755 index 000000000..d0cf3e16d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/participant_impl.h @@ -0,0 +1,293 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of Participant + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include // Interface +#include +#include +#include +#include +#include +#include +#include +#include "situation_map_objects_export.h" + + +// Qt +#include +#include +#include +#include + + +// forward: + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +// forward: +class BasicPathObjectImpl; +class ParticipantImplMessage; + + +struct TextModifierData +{ + QString text; + GraphicalObjectText *goText = nullptr; + QPoint offset; + + HorizontalAlignment horizontalAlignment = HorizontalAlignment::Left; + VerticalAlignment verticalAlignment = VerticalAlignment::Bottom; + + bool visible = true; +}; + + + +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface + + +/** + * @brief The ParticipantImpl class implements the Participant. + * + * @internal Internal: Though this class derives from AbstractMapObject like many others this class is special. + * In contrast to most other sub classes the Participant does NOT call some baseclass methods (like updateView): + */ +class SITUATIONMAPOBJECTS_EXPORT ParticipantImpl : public AbstractMapObjectImpl, public virtual Participant +{ + using BaseClass = AbstractMapObjectImpl; + + template friend class GenericObjectFactory; + friend class FunctionalAreaImpl; +protected: + explicit ParticipantImpl(const QString &debugInfo = QString()); + virtual ~ParticipantImpl(); + +public: + void initTextModifierData(); + + + ParticipantImpl(const ParticipantImpl &other) = delete; + ParticipantImpl &operator=(ParticipantImpl &other) = delete; + + void setDebugInfo(const QString &debugInfo) override; + + QString toString() const override; + + + // Data access: + Point3D origin() const override; + bool setOrigin(const Point3D &origin) override; + + /*! Just to enable using as AES item (template@AdapterDds) !*/ + void setPositions(const PositionCont &posCont) override; + + void boundingBox(BoundingBox &bbox) const override; + QColor majorColor() const override; + + + float userSymbolOpacity() const override; + void setUserSymbolOpacity(float userSymbolOpacity) override; + + SymbolDataToRender symbolDataToRender(bool ignoreForcedColor = true) const override; + QImage actualImage() const override; + + AdvancedSymbology::SymbologyType symbologyType() const override; + + // Scale : ('overload' AbstractMapObjectImpl) + float scale() const override; + void setScale(float scale) override; + // : Scale + + + QString textModifier(TextModifier textModifier) const override; + void setTextModifier(TextModifier textModifier, const QString &text) override; + bool textModifierVisibility(TextModifier textModifier) const override; + void setTextModifierVisibility(TextModifier textModifier, bool visible) override; + + Injury injury() const override; + QColor injuryColor() const override; + bool setInjury(Injury injury, const QColor &injuryColor) override; ///< Attention: 'Qt::red' is an int + + int injuryPartyId() const override; + bool setInjury(Injury injury, int partyId) override; ///< Attention: 'Qt::red' is an int + + bool setPartyIdChanged(int partyId = -1) override; + + bool additionalSymbol(AdditionalSymbolType additionalSymbolType) const override; + bool setAdditionalSymbol(AdditionalSymbolType additionalSymbolType, bool newState) override; + + bool isMarked() const override; + bool setMarked(bool marked) override; + + /*! \name DirectionIndicator like Turret Alignment*/ + //@{ + void setDirectionIndicator(int key, const DirectionIndicatorData &data) override; + void removeDirectionIndicator(int key = -1) override; + + bool isDirectionIndicatorsVisible() const override; + void setDirectionIndicatorsVisibility(bool visible) override; + //@} + + QVector ranges() const override; + bool setRanges(const QVector &ranges) override; + bool isRangesVisible() const override; + void setRangesVisibility(bool visible) override; + + bool setDesignations(const QVector &designations) override; + + + bool isMovementTrackEnabled() const override; + void setMovementTrackEnabled(bool enable) override; + + void setMovementTrack(const PositionCont &movementTrack) override; + void addToMovementTrack(const Point3D &addToMovementTrack) override; + PositionCont movementTrack() const override; + int sizeOfMovementTrack() const override; + + bool isMovementTrackVisible() const override; + void setMovementTrackVisibility(bool enable) override; + + + // CHECK : + QString identifier() const override { return QString();} + virtual void setIdentifier(const QString &identifier) override { Q_UNUSED(identifier); } + + void setVisibility(ObjectAppearance appearance, bool visibility) override { Q_UNUSED(appearance); Q_UNUSED(visibility);} + // : CHECK + + + int numberOfValuesChanged(); ///< For Unit/Module Test + + +protected: + friend class MapImpl; + +private: + + +protected: + bool updateViewImplPre() override { return true; } // no tactical Appearance is necessary + bool updateViewImpl() override; + bool updateViewImplTextModifier(); + + bool updateViewImplDirectionIndicators(); + bool updateViewImplRanges(); + bool updateViewImplDesignation(); + + bool updateViewImplPost() override + { + // reset changed flags: + BaseClass::updateViewImplPost(); + m_userSymbolOpacity.reset(); + m_scale.reset(); + m_injuryData.changed = false; + m_marked.reset(); + m_directionIndicatorsVisibility.reset(); + m_ranges.reset(); + m_extensionsChanged = false; + m_designationData.reset(); + return true; + } + + + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + void onSymbolResultReady(); + + + // logical + OriginTranslated m_origin; + double m_height = 0; + + SimpleValue m_userSymbolOpacity = 1.0; // 0..1 + + SimpleValue m_scale = 1.0; // 'overload' AbstractMapObjectImpl + +public: + const QColor &partyColor() const override; + QColor partyColor(int partyId, bool isOutOfDate = false) const override; + +protected: + TextModifierData m_textModifierCont[static_cast(TextModifier::END)]; + + // Extensions : + struct InjuryData + { + SituationMap::Injury injury = SituationMap::Injury::NoInjury; + QColor injuryColor; + int injuryPartyId = -1; + + bool changed = true; + + void resetChanged() { changed = true; } + + bool partyIdValid() const + { + return injuryPartyId >= 0; + } + }; + + InjuryData m_injuryData; + const QColor &injuryColorInternal(); + + QSet m_additionalSymbols; + + SimpleValue m_marked = false; + + SymbolDataToRender m_symDataToRender; + float m_scale4XmlSymbolWithSymbolSpecificScale = 0; + + QMap m_directionIndicatorCont; + QSharedPointer m_directionIndicatorPms; + SimpleValue m_directionIndicatorsVisibility = true; + + SimpleValue< QVector > m_ranges; + bool m_rangesVisibility = false; + QVector< QSharedPointer > m_graphObjRanges; + + + SimpleValue< QVector > m_designationData; // Key = PRF Code + QVector> m_designationMapObjects; + + + PositionsTranslated m_movementTrackCoords; + bool m_movementTrackEnabled = false; + bool m_movementTrackVisibility = true; + QSharedPointer m_movementTrack; + + bool m_extensionsChanged = true; + // : Extensions + + AdvancedSymbology::SymbolDictionary *m_dictionary = nullptr; + AdvancedSymbology::SymbolResultWatcher m_symbolResultWatcher; + + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility + + // Forbid some overloaded methods without effects of this object: +// virtual QString identifier() const { return QString();}; +// virtual void setIdentifier(const QString& identifier) { Q_UNUSED(identifier); }; + +// virtual bool visibility(ObjectAppearance appearance) const { Q_UNUSED(appearance); return true;}; +// virtual void setVisibility(ObjectAppearance appearance, bool visibility) { Q_UNUSED(appearance); Q_UNUSED(appearance);}; + +}; + + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/path_container.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/path_container.cpp new file mode 100755 index 000000000..6d570ffef --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/path_container.cpp @@ -0,0 +1,370 @@ +/** + * @RDE_HEADER@ + * PathContainer + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "path_container.h" +#include +#include +#include + + +// ESRI +#include +#include + +// Qt +#include + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class PathData +//----------------------------------------------------------------------------- +class PathData +{ +public: + PathData() = default; + + PositionsTranslated &pathTrans() { return m_path; } + bool setPath(const PositionCont &path) + { + if (m_path.positions() != path) + m_changed = true; + m_path.setPositions(path); + return m_changed; + } + + bool visibility() const { return m_visibility.value(); } + void setVisibility(bool onOff) { m_visibility = onOff; } + + bool changed() const { return m_changed || m_visibility.changed(); } + void resetChanged() { m_changed = false; m_visibility.reset(); } + + + ~PathData() + { + } + +public: + +private: + PositionsTranslated m_path; + SimpleValue m_visibility{true}; + bool m_changed{true}; +}; + + +class PathContainer::Private +{ +public: + Private() = default; + ~Private() = default; + + // ESRI data: + Esri::ArcGISRuntime::GeoView *m_geoView{nullptr}; + Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer{nullptr}; + + + // Own data: + CoordTranslationFunctor *m_coordTransFunctor{nullptr}; + QString m_debugInfo; // will be appended by consecutive number + UniqueMapId m_uniqueMapId; + + SimpleValue m_lineStyle; + SimpleValue m_fillStyle; + SimpleValue m_drawOrder{0}; + SimpleValue m_selected{false}; + QSharedPointer m_multiPath; + +#ifdef LTS_DEBUG + static long m_counter; +#endif + +private: + QHash> m_map; + +public: + QSharedPointer getPathData(int key) + { + auto it = m_map.find(key); + if (it != m_map.end()) + return it.value(); + auto ret = QSharedPointer(new PathData()); + m_map[key] = ret; + return ret; + } + + bool remove(int key) + { + auto it = m_map.find(key); + if (it == m_map.end()) + return false; + m_map.erase(it); + return true; + } + void clear() { m_map.clear(); } + int size() const { return m_map.size(); } + QList keys() const { return m_map.keys(); } + bool contains(int key) const { return m_map.contains(key); } + const QHash> &container() const { return m_map; } + +}; // class PathContainer::Private +#ifdef LTS_DEBUG +long PathContainer::Private::m_counter = 0; +#endif +//----------------------------------------------------------------------------- + + + +//----------------------------------------------------------------------------- +// class PathContainer +//----------------------------------------------------------------------------- +PathContainer::PathContainer(const Esri::ArcGISRuntime::GeoView *geoView, + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer, + CoordTranslationFunctor *coordTransFunctor, + const UniqueMapId &uniqueMapId) + : QObject() + , m_logger(getLogger("PathContainer")) + , d(new Private) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + d->m_geoView = const_cast(geoView); + d->m_graphicsLayer = graphicsLayer; + d->m_coordTransFunctor = coordTransFunctor; + d->m_uniqueMapId = uniqueMapId; + + QString debugInfo = QStringLiteral("%1#PathContainerPolyline").arg(d->m_debugInfo); + DEBUG_LOG(m_logger) << LOGFUNCTION << " create new Object: " << debugInfo; + d->m_multiPath = QSharedPointer + (new GraphicalObjectMultiPath(GraphicalObjectPathType::Polyline, + d->m_geoView, + d->m_graphicsLayer, + debugInfo, + d->m_uniqueMapId)); + +#ifdef LTS_DEBUG + INFO_LOG(m_logger) << " ctor PathContainer-Polyline counter = " << d->m_counter++ + << ", debugInfo = " << debugInfo; +#endif // LTS_DEBUG + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} +PathContainer::PathContainer(const GraphicalObjectPathType pathType, + const Esri::ArcGISRuntime::GeoView *geoView, + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer, + CoordTranslationFunctor *coordTransFunctor, + const UniqueMapId &uniqueMapId) + : QObject() + , m_logger(getLogger("PathContainer")) + , d(new Private) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + d->m_geoView = const_cast(geoView); + d->m_graphicsLayer = graphicsLayer; + d->m_coordTransFunctor = coordTransFunctor; + d->m_uniqueMapId = uniqueMapId; + + QString debugInfo = QStringLiteral("%1#PathContainerPolygon").arg(d->m_debugInfo); + DEBUG_LOG(m_logger) << LOGFUNCTION << " create new Object: " << debugInfo; + d->m_multiPath = QSharedPointer + (new GraphicalObjectMultiPath(pathType, + d->m_geoView, + d->m_graphicsLayer, + debugInfo, + d->m_uniqueMapId)); +#ifdef LTS_DEBUG + INFO_LOG(m_logger) << " ctor PathContainer-Polygon counter = " << d->m_counter++ + << ", debugInfo = " << debugInfo; +#endif // LTS_DEBUG + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +PathContainer::~PathContainer() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + d->clear(); + delete d; + +#ifdef LTS_DEBUG + INFO_LOG(m_logger) << " dtor PathContainer counter =" << --d->m_counter; +#endif // LTS_DEBUG + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +QString PathContainer::debugInfo() const +{ + return d->m_debugInfo; +} +void PathContainer::setDebugInfo(const QString &debugInfo) +{ + d->m_debugInfo = debugInfo; + d->m_multiPath->setDebugInfo(QStringLiteral("%1#PathContainer").arg(d->m_debugInfo)); +} + + +PositionCont PathContainer::path(int idx) const +{ + return d->getPathData(idx)->pathTrans().positions(); +} +bool PathContainer::setPath(int idx, const PositionCont &path) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " idx=" << idx + << " path.size=" << path.size(); + + bool hasChanged = false; + + if (path.isEmpty()) + hasChanged = d->remove(idx); + else + hasChanged = d->getPathData(idx)->setPath(path); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return hasChanged; +} + +void PathContainer::clear(int idx) +{ + d->remove(idx); +} + +void PathContainer::clearAll() +{ + d->clear(); +} + +LineStyle PathContainer::lineStyle() const +{ + return d->m_lineStyle.value(); +} +void PathContainer::setLineStyle(const LineStyle &lineStyle) +{ + d->m_lineStyle = lineStyle; +} + +FillStyle PathContainer::fillStyle() const +{ + return d->m_fillStyle.value(); +} + +void PathContainer::setFillStyle(const FillStyle &fillStyle) +{ + d->m_fillStyle = fillStyle; +} + +int PathContainer::drawOrder() const +{ + return d->m_drawOrder.value(); +} +void PathContainer::setDrawOrder(int drawOrder) +{ + d->m_drawOrder = drawOrder; +} + +bool PathContainer::selected() const +{ + return d->m_selected.value(); +} +void PathContainer::setSelected(bool selected) +{ + d->m_selected = selected; +} + +bool PathContainer::visibility(int idx) const +{ + return d->getPathData(idx)->visibility(); +} + +void PathContainer::setVisibility(int idx, bool onOff) +{ + d->getPathData(idx)->setVisibility(onOff); +} + +int PathContainer::getPathCount() const +{ + return d->size(); +} + +QList PathContainer::getIndicationIndexList() const +{ + return d->keys(); +} + + +bool PathContainer::updateView() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << ", debugInfo='" << d->m_debugInfo << "'" + << ", d->m_map.size()= " << d->size(); + bool success = true; + + bool bDoUpdateView = false; + if (d->m_lineStyle.changed() || d->m_fillStyle.changed() || d->m_drawOrder.changed() || d->m_selected.changed()) + { + d->m_multiPath->setLineStyle(d->m_lineStyle.value()); + d->m_multiPath->setFillStyle(d->m_fillStyle.value()); + d->m_multiPath->setDrawOrder(d->m_drawOrder.value()); + d->m_multiPath->setSelected(d->m_selected.value()); + bDoUpdateView = true; + } + + // is something removed? + if (d->m_multiPath->pathIndexInUse() != d->keys()) + { + d->m_multiPath->reduceTo(d->keys()); + bDoUpdateView = true; + } + + for (auto it = d->container().begin(); it != d->container().end(); ++it) + { + QSharedPointer pathData = it.value(); + if (pathData->changed()) + { + const int idx = it.key(); + pathData->pathTrans().setCoordTransFunctor(d->m_coordTransFunctor); + + if (pathData->visibility()) + { + const PositionCont &pathTrans = pathData->pathTrans().positionsTranslated(); + d->m_multiPath->setPath(idx, pathTrans); + } else { + // a cleared path will not be transfered to ESRI::Graphic, see GraphicalObjectMultiPath + // So clearPath is sufficiant + d->m_multiPath->clearPath(idx); + } + bDoUpdateView = true; + pathData->resetChanged(); + } + } // for + + if (bDoUpdateView) + { + bool updated = d->m_multiPath->updateView(); + success = updated; + } + + // reset + d->m_lineStyle.reset(); + d->m_fillStyle.reset(); + d->m_drawOrder.reset(); + d->m_selected.reset(); + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << " debugInfo='" << d->m_debugInfo << "'" + << " success=" << success; + return success; +} + + +} +// namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/path_container.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/path_container.h new file mode 100755 index 000000000..cebcb29c2 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/path_container.h @@ -0,0 +1,110 @@ +/** + * @RDE_HEADER@ + * PathContainer + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include +#include +#include +#include + +// Qt +#include +#include +#include + + +// forward: +namespace Esri +{ +namespace ArcGISRuntime +{ +class GeoView; +class GraphicsOverlay; +} +} + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +// Forward: +class CoordTranslationFunctor; +class PathData; +enum class GraphicalObjectPathType; + + +class PathContainer : public QObject +{ + Q_OBJECT + +public: + explicit PathContainer(const Esri::ArcGISRuntime::GeoView *geoView, + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer, + CoordTranslationFunctor *coordTransFunctor, + const UniqueMapId &uniqueMapId); + + explicit PathContainer(const GraphicalObjectPathType pathType, + const Esri::ArcGISRuntime::GeoView *geoView, + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer, + CoordTranslationFunctor *coordTransFunctor, + const UniqueMapId &uniqueMapId); + virtual ~PathContainer(); + + PathContainer(const PathContainer &other) = delete; + PathContainer &operator=(PathContainer &other) = delete; + + QString debugInfo() const; + void setDebugInfo(const QString &debugInfo); + + // Marker : + + virtual PositionCont path(int idx) const; + virtual bool setPath(int idx, const PositionCont &path); + + virtual void clear(int idx); + virtual void clearAll(); + + virtual LineStyle lineStyle() const; + virtual void setLineStyle(const LineStyle &lineStyle); + + virtual FillStyle fillStyle() const; + virtual void setFillStyle(const FillStyle &fillStyle); + + virtual int drawOrder() const; + virtual void setDrawOrder(int drawOrder); + + virtual bool selected() const; + virtual void setSelected(bool selected); + + virtual bool visibility(int idx) const; + virtual void setVisibility(int idx, bool onOff); + + virtual int getPathCount() const; + virtual QList getIndicationIndexList() const; + + + virtual bool updateView(); + +public slots: + +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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/polygon_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/polygon_impl.cpp new file mode 100755 index 000000000..efa989d23 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/polygon_impl.cpp @@ -0,0 +1,75 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of Polygon + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "polygon_impl.h" +#include +#include + + +// ESRI + + +// Qt + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class PolygonImpl +//----------------------------------------------------------------------------- +PolygonImpl::PolygonImpl(const QString &debugInfo) + : Polygon() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("PolygonImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +PolygonImpl::~PolygonImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +bool PolygonImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + bool success = BaseClass::updateViewImpl(); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + + +bool PolygonImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polygon); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/polygon_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/polygon_impl.h new file mode 100755 index 000000000..91058d417 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/polygon_impl.h @@ -0,0 +1,69 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of Polygon + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + + +// Qt +#include + + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +/** + * @brief The PolygonImpl class displays a polygon. + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath displaying the line) + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT PolygonImpl : public BasicPathObjectImpl, public virtual Polygon +{ + using BaseClass = BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit PolygonImpl(const QString &debugInfo = QString()); + virtual ~PolygonImpl(); + +public: + PolygonImpl(const PolygonImpl &other) = delete; + PolygonImpl &operator=(PolygonImpl &other) = delete; + +// void setScale(float) override {} + + +protected: + virtual bool updateViewImpl() override; + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + + // logical + + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/polyline_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/polyline_impl.cpp new file mode 100755 index 000000000..fb158144b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/polyline_impl.cpp @@ -0,0 +1,115 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of Polyline + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "polyline_impl.h" +#include +#include + + +// ESRI + + +// Qt + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class PolylineImpl +//----------------------------------------------------------------------------- +PolylineImpl::PolylineImpl(const QString &debugInfo) + : Polyline() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("PolylineImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +PolylineImpl::~PolylineImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +bool PolylineImpl::arrowHeadStart() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return BaseClass::arrowHeadStart(); +} +void PolylineImpl::setArrowHeadStart(bool enable) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + BaseClass::setArrowHeadStart(enable); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +bool PolylineImpl::arrowHeadEnd() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return BaseClass::arrowHeadEnd(); +} +void PolylineImpl::setArrowHeadEnd(bool enable) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + BaseClass::setArrowHeadEnd(enable); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +QString PolylineImpl::textOnSegment(uint segment) const +{ + return BaseClass::textOnSegment(segment); +} +void PolylineImpl::setTextOnSegment(uint segment, const QString &segmentText) +{ + BaseClass::setTextOnSegment(segment, segmentText); +} +void PolylineImpl::clearTextOnSegment(uint segment) +{ + BaseClass::clearTextOnSegment(segment); +} + + +bool PolylineImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + bool success = BaseClass::updateViewImpl(); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + + +bool PolylineImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polyline); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/polyline_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/polyline_impl.h new file mode 100755 index 000000000..6d16bf342 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/polyline_impl.h @@ -0,0 +1,81 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of Polyline + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + + +// Qt +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +/** + * @brief The PolylineImpl class displays a polyline. + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath displaying the line) + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT PolylineImpl : public BasicPathObjectImpl, public virtual Polyline +{ + using BaseClass = BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit PolylineImpl(const QString &debugInfo = QString()); + virtual ~PolylineImpl(); + +public: + PolylineImpl(const PolylineImpl &other) = delete; + PolylineImpl &operator=(PolylineImpl &other) = delete; + +// void setScale(float) override {} + + + /*! \name ArrowHeads */ + //@{ + bool arrowHeadStart() const override; + void setArrowHeadStart(bool enable) override; + + bool arrowHeadEnd() const override; + void setArrowHeadEnd(bool enable) override; + //@} + + QString textOnSegment(uint segment) const override; + void setTextOnSegment(uint segment, const QString &segmentText) override; + void clearTextOnSegment(uint segment) override; + +protected: + virtual bool updateViewImpl() override; + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + + // logical + + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/position_marker.xpm b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/position_marker.xpm new file mode 100755 index 000000000..981c6d979 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/position_marker.xpm @@ -0,0 +1,22 @@ +/* XPM */ +static const char * position_marker_xpm[] = +{ + "15 15 2 1", + " c None", + ". c #FB0000", + " . ", + " . ", + " . ", + " . ", + " . ", + " . ", + " . ", + "...............", + " . ", + " . ", + " . ", + " . ", + " . ", + " . ", + " . " +}; diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/single_fire_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/single_fire_impl.cpp new file mode 100755 index 000000000..85bee45d0 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/single_fire_impl.cpp @@ -0,0 +1,323 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of SingleFire + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "single_fire_impl.h" +#include +#include +#include +#include + + +#include + +// ESRI + + +// Qt +#include +#include + + +namespace SituationMap +{ + +static int s_gunToTargetLine3DKEY = -1; +static int s_gunToTargetLineSurfaceKEY = 0; +static int s_nonLethalAreaKEY = 1; +//----------------------------------------------------------------------------- +// class SingleFireImpl +//----------------------------------------------------------------------------- +SingleFireImpl::SingleFireImpl(const QString &debugInfo) + : SingleFire() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("SingleFireImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + m_fillColorAlpha = 200; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +SingleFireImpl::~SingleFireImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete m_centerImage; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +QString SingleFireImpl::toString() const +{ + QString str; + str.append(BaseClass::toString()); + str.append(BasicPathObjectImpl::toString()); + return str; +} + +Point3D SingleFireImpl::impactOrigin() const +{ + return BaseClass::tacticalOrigin(); +} +void SingleFireImpl::setImpactOrigin(const Point3D &impactOrigin) +{ + BaseClass::setTacticalOrigin(impactOrigin); +} + +SingleFire::ImpactType SingleFireImpl::impactType() const +{ + return m_impactType.value(); +} + +void SingleFireImpl::setImpactType(SingleFire::ImpactType impactType) +{ + m_impactType = impactType; +} + +Point3D SingleFireImpl::origin() const +{ + return BaseClass::origin(); +} +void SingleFireImpl::setOrigin(const Point3D &origin) +{ + BaseClass::setOrigin(origin); +} +float SingleFireImpl::radius() const +{ + return BaseClass::radius(); +} + +void SingleFireImpl::setRadius(float radius) +{ + BaseClass::setRadius(radius); +} + +void SingleFireImpl::setOriginatorToTargetLineSurface(const Point3D &start, const Point3D &end) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << ", start=" << start << ", end=" << end << ", debugInfo=" << m_debugInfo; + + PathContainer &path = this->rPathContainer(); // should be SP::Draped + path.setDebugInfo(m_debugInfo + QStringLiteral("SpDraped")); + + if (start.isNull() && end.isNull()) + { + path.clear(s_gunToTargetLineSurfaceKEY); + } else { + LineStyle ls = m_mainGraphicalObject->lineStyle(); + ls.setLinePattern(LinePattern::Dash); + ls.setLineWidth(2); + path.setLineStyle(ls); + + path.setPath(s_gunToTargetLineSurfaceKEY, {start, end}); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" + << ", start=" << start << ", end=" << end << ", debugInfo=" << m_debugInfo; + +} + +AesEffectState SingleFireImpl::aesState() const +{ + return BaseClass::aesState(); +} +void SingleFireImpl::setAesState(AesEffectState aesState) +{ + BaseClass::setAesState(aesState); +} + +SymbolImage SingleFireImpl::symbolImage() const +{ + SymbolImage symImage = m_tacticalAppearance->symbolImage(); + return symImage; +} + + + +bool SingleFireImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " origin =" << this->origin() //.x() << ", " << this->origin().y() + << " radius =" << this->radius() + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + if (m_tacticalAppearance) + { + if (partyChanged()) + m_symbol.refValue().color = partyColor(); + } + + // centerImage: + if (!m_centerImage) + { + m_centerImage = + new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QLatin1String("#CenterImage"), + m_uniqueMapId); + m_impactType.setChanged(); + } + + if (m_impactType.changed() || partyChanged()) + { + QImage img; + switch(m_impactType.value()) + { + case SingleFire::ImpactType::normalImpact: + img = renderImpactSymbol(partyColor(), 20, true); // filled + break; + case SingleFire::ImpactType::doubleImpact: + img = renderDoubleImpactSymbol(partyColor(), 20, true); // filled + break; + } // switch + m_centerImage->setImage(img); + } + +// m_centerImage->setScale(m_mainGraphicalObject->scale()); // do not scale centerImage, equal to mine of MineArea + + m_centerImage->setOrigin(m_tacticalOrigin.value().originTranslated()); + m_centerImage->setVisibility(m_visibility && m_visibilityTechnical); + + bool success = BaseClass::updateViewImpl(); // will call onMapExtentChanged and therefore m_centerImage->updateView + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + + +bool SingleFireImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (mapImpl) + { + + switch (mapImpl->symbologyType()) + { + + case AdvancedSymbology::SymbologyType::MIL2525D: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "10012500002108000000"); // SymbolSet = 21 270800 , 270900 + break; + case AdvancedSymbology::SymbologyType::MIL2525C: + // m_symbol.refValue().m_sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"GUGPGPWP--"); // 0431 GUGPGPWP-- (Predicted Impact Point U) -> Point + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GUGPGPWI--"); // 0293 GHGPGPWI-- (Impact Point H) -> Point + break; + case AdvancedSymbology::SymbologyType::APP6B: + //m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"GUOPVMM---"); // APP6B: 2564 GUOPVMM--- -> Point + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GUOPVI----"); // APP6B: 2560 GUOPVI---- -> Point + break; + case AdvancedSymbology::SymbologyType::APP6D: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "10012500002108000000"); // SymbolSet = 21 270800 , 270900 + break; + case AdvancedSymbology::SymbologyType::None: + // empty + break; + } // switch + + } // if (mapImpl) + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polygon); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool SingleFireImpl::onMapExtentChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool successCenterImage = true; + if (m_centerImage) + { + bool visiScale = m_visibilityScaleRange.scaleHasVisibility(m_mapImpl->scale()); + m_centerImage->setVisibility(visiScale && m_visibility && m_visibilityTechnical); + successCenterImage = m_centerImage->updateView(); + } + + bool success = BaseClass::onMapExtentChanged() && successCenterImage; + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; + +} + +QImage SingleFireImpl::renderDoubleImpactSymbol(const QColor &color, int size, bool filled) +{ + QImage finalImage(2.25*size, size, QImage::Format_ARGB32); + finalImage.fill(Qt::transparent); + + const QImage &singleImg = renderImpactSymbol(color, size, filled); + QRect target = singleImg.rect(); + QPainter painter(&finalImage); + + // left + painter.drawImage(target, singleImg, singleImg.rect()); + + // right + target.moveTo(finalImage.width() - singleImg.width(), 0); + painter.drawImage(target, singleImg, singleImg.rect()); + + painter.end(); + + return finalImage; +} + +QImage SingleFireImpl::renderImpactSymbol(const QColor &color, int size, bool filled) +{ + QImage image(size, size, QImage::Format_ARGB32); + image.fill(Qt::transparent); + + QPainter painter(&image); + + QPen pen(color); + int penWidth = size/10; + if (penWidth < 2) + penWidth = 2; + pen.setWidth(penWidth); + painter.setPen(pen); + + const int sizeHalf = size / 2; + + QPointF center(sizeHalf, sizeHalf); + + // circle + if (filled) + painter.setBrush(color); + painter.drawEllipse(center, sizeHalf * 0.5, sizeHalf * 0.5); + + // lines + QLine newLine(0, sizeHalf, size / 4.0, sizeHalf); + painter.drawLine(newLine); + + QTransform trans; + trans.translate(sizeHalf, sizeHalf); + trans.rotate(45); + trans.translate(-sizeHalf, -sizeHalf); + + // 8 are required, one is already draw, so 7: + for (int i=0; i<7; ++i) + { + newLine = trans.map(newLine); + painter.drawLine(newLine); + } + + painter.end(); + + return image; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/single_fire_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/single_fire_impl.h new file mode 100755 index 000000000..fef90eaff --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/single_fire_impl.h @@ -0,0 +1,106 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of SingleFire + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "basic_path_object_impl.h" +#include // Interface +#include + + +// Qt + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +/** + * @brief The SingleFireImpl class displays a single impact. + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPictureMarker / Image, displaying the main techn. Appearance) + * - mainGraphicalObjectText (displaying the text/name) + * - tacticalAppearance (displaying the tact. Appearance) + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT SingleFireImpl : public BasicPathObjectImpl, public virtual SingleFire +{ + using BaseClass = BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit SingleFireImpl(const QString &debugInfo = QString()); + virtual ~SingleFireImpl(); + +public: + SingleFireImpl(const SingleFireImpl &other) = delete; + SingleFireImpl &operator=(SingleFireImpl &other) = delete; + + QString toString() const override; + + + // some set/get methods: + Point3D impactOrigin() const override; + void setImpactOrigin(const Point3D &impactOrigin) override; + + ImpactType impactType() const override; + void setImpactType(ImpactType impactType = ImpactType::normalImpact) override; + + Point3D origin() const override; + void setOrigin(const Point3D &origin) override; + float radius() const override; + void setRadius(float radius) override; + void setOriginatorToTargetLineSurface(const Point3D &start, const Point3D &end) override; + + + // Delegate to BaseClass: + AesEffectState aesState() const override; + void setAesState(AesEffectState aesState) override; + + SymbolImage symbolImage() const override; + + static QImage renderDoubleImpactSymbol(const QColor &color, int size, bool filled); + static QImage renderImpactSymbol(const QColor &color, int size, bool filled); + + + +protected: + bool updateViewImpl() override; + + bool updateViewImplPost() override + { + BaseClass::updateViewImplPost(); + m_impactType.reset(); + return true; + } + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + bool onMapExtentChanged() override; + + // internal/admin + + // logical + + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility + + SimpleValue m_impactType = ImpactType::normalImpact; + GraphicalObjectPictureMarker *m_centerImage = nullptr; +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/situation_map_objects_export.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/situation_map_objects_export.h new file mode 100755 index 000000000..2e4782dd6 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/situation_map_objects_export.h @@ -0,0 +1,21 @@ +/** + * Export definitions + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include + +#ifndef SITUATIONMAPOBJECTS_EXPORT +# ifdef BUILD_STATIC_LIBRARIES +# define SITUATIONMAPOBJECTS_EXPORT +# else +# if defined(MAKE_SITUATIONMAPOBJECTS_LIB) +# define SITUATIONMAPOBJECTS_EXPORT Q_DECL_EXPORT +# else +# define SITUATIONMAPOBJECTS_EXPORT Q_DECL_IMPORT +# endif +# endif +#endif diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/sound_area_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/sound_area_impl.cpp new file mode 100755 index 000000000..30eecf88c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/sound_area_impl.cpp @@ -0,0 +1,193 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of FunctionalArea + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "sound_area_impl.h" +#include +#include +#include +#include + + +// ESRI + + +// Qt +#include + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// class SoundAreaImpl +//----------------------------------------------------------------------------- +SoundAreaImpl::SoundAreaImpl(const QString &debugInfo) + : SoundArea() // Interface + , BaseClass(debugInfo) // Implementation + , m_logger(getLogger("SoundAreaImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + m_markerOrigin.setMapObjectImpl(this); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +SoundAreaImpl::~SoundAreaImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete m_pictureMarker; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +//void SoundAreaImpl::reRenderSymbol() +//{ +// BaseClass::reRenderSymbol(); +// if (m_pointSymbol) +// m_pointSymbol->reRenderSymbol(); +//} + +Point3D SoundAreaImpl::markerOrigin() const +{ + return m_markerOrigin.origin(); +} +void SoundAreaImpl::setMarkerOrigin(const Point3D &origin) +{ + m_markerOrigin.setOrigin(origin); +} + +const MarkerData &SoundAreaImpl::markerData() const +{ + return m_markerData.value(); +} +void SoundAreaImpl::setMarkerData(const MarkerData &markerData) +{ + m_markerData = markerData; +} + +QImage SoundAreaImpl::previewImageTypical() const +{ + return m_markerData.value().m_proxyImage; // maybe mainImage if it will be provided (see geo_image_impl) +} + +QImage SoundAreaImpl::previewImageSpecific() const +{ + QImage imageFromForm = BaseClass::previewImageSpecific(); + QImage markerImage = markerData().proxyImage(); + + // most likely bothj are available: + if (!imageFromForm.isNull() && !markerImage.isNull()) + { + // merge marker image on top: + const QSize &outerSize = imageFromForm.size(); + markerImage = markerImage.scaled(outerSize/1.5, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + + QPainter painter(&imageFromForm); + painter.setRenderHints(QPainter::Antialiasing|QPainter::SmoothPixmapTransform); + + QRect r = markerImage.rect(); + + int offsetX = (outerSize.width() - r.width()) / 2; + int offsetY = (outerSize.height() - r.height()) / 2; + + r.moveTo(offsetX, offsetY); + painter.drawImage(r, markerImage, markerImage.rect()); + return imageFromForm; + } else if (imageFromForm.isNull()) + return markerImage; + + return imageFromForm; +} + + +bool SoundAreaImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " symbol=" << m_symbol.value().sidc + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + bool success = BaseClass::updateViewImpl(); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + + if (!updateViewImplMarkerData()) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImplPointSymbolData failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool SoundAreaImpl::updateViewImplMarkerData() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (m_markerData.changed()) + { + const MarkerData &markerData = m_markerData.value(); + + // empty? + if (markerData.isEmpty()) + { + delete m_pictureMarker; + m_pictureMarker = nullptr; + return true; + } + + // ok, data available: + if (!m_pictureMarker) + { + m_pictureMarker = new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + m_debugInfo + QLatin1String("#Marker"), + m_uniqueMapId); + } + if (markerData.hasMarker()) + m_pictureMarker->setMarkerSymbol(markerData.m_proxyMarker, + markerData.m_proxyMarkerSize, + markerData.m_proxyMarkerColor); + else if (markerData.hasProxyImage()) + m_pictureMarker->setImage(markerData.m_proxyImage, + markerData.m_proxyImageDefaultSize); + } // if (m_markerData.changed()) + + m_pictureMarker->setOrigin(m_markerOrigin.originTranslated()); + + m_pictureMarker->setPickable(m_mainGraphicalObject->pickable()); + m_pictureMarker->setSelected(m_mainGraphicalObject->selected()); + m_pictureMarker->setDrawOrder(m_internalDrawOrder + 1); + // m_pictureMarker->setScale(); + m_pictureMarker->setVisibility(m_visibility && m_visibilityTechnical); + + bool success = m_pictureMarker->updateView(); + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed, m_debugInfo =" << m_debugInfo; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool SoundAreaImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polygon); + + setLineStyle(LineStyle(Qt::black, 2)); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/sound_area_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/sound_area_impl.h new file mode 100755 index 000000000..34d4d2757 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/sound_area_impl.h @@ -0,0 +1,89 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of FunctionalArea + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + + +// Qt +#include + + + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +// forward: + + +/** + * @brief The SoundAreaImpl class displays a sound area and an image. + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath displaying the main techn. Appearance) + * - mainGraphicalObjectText (displaying the text/name) + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT SoundAreaImpl : public BasicPathObjectImpl, public virtual SoundArea +{ + using BaseClass = BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit SoundAreaImpl(const QString &debugInfo = QString()); + virtual ~SoundAreaImpl(); + +public: + SoundAreaImpl(const SoundAreaImpl &other) = delete; + SoundAreaImpl &operator=(SoundAreaImpl &other) = delete; + +// void reRenderSymbol() override; + + /*! \name Marker / Point symbol*/ + //@{ + Point3D markerOrigin() const override; + void setMarkerOrigin(const Point3D &origin) override; + + const MarkerData &markerData() const override; + void setMarkerData(const MarkerData &markerData) override; + //@} + + QImage previewImageTypical() const override; + QImage previewImageSpecific() const override; + + +protected: + bool updateViewImpl() override; + + bool updateViewImplMarkerData(); + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + +private: + // logical + OriginTranslated m_markerOrigin; + SimpleValue m_markerData; + GraphicalObjectPictureMarker *m_pictureMarker{nullptr}; + + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/text_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/text_impl.cpp new file mode 100755 index 000000000..474606376 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/text_impl.cpp @@ -0,0 +1,149 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of Text + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "text_impl.h" +#include +#include +#include +#include +#include + +// ESRI + + +// Qt +#include + + +#include "position_marker.xpm" + + +namespace SituationMap +{ + +//----------------------------------------------------------------------------- +// constants +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Utils: +//----------------------------------------------------------------------------- + + + + + +//----------------------------------------------------------------------------- +// class TextImpl +//----------------------------------------------------------------------------- +TextImpl::TextImpl(const QString &debugInfo) + : Text() // Interface + , BaseClass(debugInfo) // Implementation + , m_showAnchorMarker(false) + , m_anchorMarker(nullptr) + , m_logger(getLogger("TextImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + m_visibilityScaleRange.resetToMax(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +TextImpl::~TextImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete m_anchorMarker; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +void TextImpl::setShowAnchorMarker(bool on) +{ + if (on == m_showAnchorMarker) + return; + + m_showAnchorMarker = on; + if (on) + { + static QImage markerSymbol; + + if (markerSymbol.isNull()) + markerSymbol = QImage(position_marker_xpm); + + m_anchorMarker = new GraphicalObjectPictureMarker(m_mapImpl->getEsriGeoView(), + m_graphicsLayer, + QLatin1String("AnchorMarker"), + INVALID_MAP_ID); + m_anchorMarker->setImage(markerSymbol); + } + else + { + delete m_anchorMarker; + m_anchorMarker = nullptr; + } +} // TextImpl::setShowAnchorMarker + + +bool TextImpl::showAnchorMarker() const { return m_showAnchorMarker; } + + +bool TextImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " visibility=" << m_visibility; + + // put translated origin to stub. Later GOText will takeover the (translated) origin. + if (m_mainGraphicalObject) m_mainGraphicalObject->setOrigin(originTranslated()); + + bool success = BaseClass::updateViewImpl(); + + if (m_anchorMarker) + { + m_anchorMarker -> setSelected(m_selected); + m_anchorMarker -> setDrawOrder(m_internalDrawOrder); + m_anchorMarker -> setOrigin(m_mainGraphicalObjectText->origin()); + m_anchorMarker -> setVisibility(m_mainGraphicalObjectText->visibility()); + m_anchorMarker -> updateView(); + } + + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + + +bool TextImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool success = BaseClass::init(mapImpl, logicalLayerName); + + if (success) + { + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Use the StubGraphicalObject, which is doing nothing of the work, because + * the text will be rendered using m_mainGraphicalObjectText + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + m_mainGraphicalObject = new StubGraphicalObject(); + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/text_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/text_impl.h new file mode 100755 index 000000000..bd4e65715 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/text_impl.h @@ -0,0 +1,76 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of Text + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_single_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + + +// Qt +#include + + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +class GraphicalObjectPictureMarker; + +/** + * @brief The TextImpl class displays a text. + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (irrelevant) + * - mainGraphicalObjectText (displaying the text/name) + * - tacticalAppearance (irrelevant) + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT TextImpl : public AbstractSinglePointMapObjectImpl, public virtual Text +{ + using BaseClass = AbstractSinglePointMapObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit TextImpl(const QString &debugInfo = QString()); + virtual ~TextImpl(); + +public: + TextImpl(const TextImpl &other) = delete; + TextImpl &operator=(TextImpl &other) = delete; + +// void setScale(float) override {} + + void setShowAnchorMarker(bool on) override; + bool showAnchorMarker() const override; + +protected: + + bool updateViewImpl() override; + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + + // logical + bool m_showAnchorMarker; + GraphicalObjectPictureMarker *m_anchorMarker; + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/virtual_mine_impl.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/virtual_mine_impl.cpp new file mode 100755 index 000000000..affacbed1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/virtual_mine_impl.cpp @@ -0,0 +1,494 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of VirtualMine + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "virtual_mine_impl.h" +#include +#include +#include +#include +#include + +#include + + +// ESRI +#include +#include +#include + +// Qt +#include +#include + +namespace SituationMap +{ + +//// to get access to 'init' +//class VMineLocalBasicPathObject final : public BasicPathObjectImpl +//{ +//public: +// using BasicPathObjectImpl::BasicPathObjectImpl; +// using BasicPathObjectImpl::init; +// using BasicPathObjectImpl::setUniqueId; +//}; + +using VMineLocalBasicPathObject = LocalBasicPathObject; + + +class VirtualMineImpl::Private +{ +public: + Private() = default; + ~Private() = default; + + // ESRI data: +// Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr; + + + // Own data: + MapImpl *m_mapImpl = nullptr; + QString m_logicalLayer; + QString m_debugInfo; // will be appended by consecutive number + UniqueMapId m_uniqueMapId = INVALID_MAP_ID; + + struct MarkerSymbolDataRenderer + { +// std::shared_ptr m_graphic3D; + QSharedPointer m_goRendererAtt; + QVariantMap m_attribMap; + OriginTranslated m_origin; + } m_markerSymbolDataRenderer; + + struct MarkerSymbolData + { + QSharedPointer m_goSceneMarker; + SceneSymbolData m_sceneSymbolData; + OriginTranslated m_origin; + } m_markerSymbolData; + + struct ValueDataVM + { + QSharedPointer basicPathObj; + VirtualMine::OptionalData optData; + }; + + QMap m_cont; /// one polyline per subsidiary line + using MAP_IT_T = QMapIterator; +// int m_drawOrder = 0; + + bool hasPathObject(int key) const { return m_cont.contains(key); } + QList keysInUse() const { return m_cont.keys(); } + void remove(int key) { m_cont.remove(key); } + void clear() { m_cont.clear(); } + + ValueDataVM getPathObject(int key, const OptionalData &optData = OptionalData()) + { + auto ret = m_cont.value(key); // copy + if (ret.basicPathObj.isNull()) + { + QString debugInfo = QStringLiteral("%1#SubsidiaryLine(%2)").arg(m_debugInfo).arg(key); + VMineLocalBasicPathObject *temp = new VMineLocalBasicPathObject(debugInfo); + GraphicalObjectPathType pathType = GraphicalObjectPathType::Polyline; + if (optData.closed) + pathType = GraphicalObjectPathType::Polygon; + bool success = temp->init(m_mapImpl, m_logicalLayer, pathType); + temp->setUniqueId(m_uniqueMapId); + + if (success) + ret.basicPathObj = QSharedPointer(static_cast(temp)); + else + temp->removeMySelf(); + + ret.optData.closed = optData.closed; + } + // transfer optional data (line|fill) to ret and then to container _if_ set: + if (optData.lineStyle) + ret.optData.lineStyle = optData.lineStyle; + if (optData.fillStyle) + ret.optData.fillStyle = optData.fillStyle; + m_cont.insert(key, ret); + + return ret; + } +}; // class VirtualMineImpl::Private +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// class VirtualMineImpl +//----------------------------------------------------------------------------- +VirtualMineImpl::VirtualMineImpl(const QString &debugInfo) + : VirtualMine() // Interface + , BaseClass(debugInfo) // Implementation + , d(new Private) + , m_logger(getLogger("VirtualMineImpl")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + m_fillColorAlpha = 200; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +VirtualMineImpl::~VirtualMineImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + d->m_markerSymbolDataRenderer.m_goRendererAtt.clear(); + d->m_markerSymbolData.m_goSceneMarker.clear(); + +// // approach for V100.15 ; will maybe crash with older versions +// if (d->m_markerSymbolData.m_graphic3D && m_graphicsLayer && m_graphicsLayer->graphics()) +// m_graphicsLayer->graphics()->removeOne(d->m_markerSymbolData.m_graphic3D.get()); +// d->m_markerSymbolData.m_graphic3D.reset(); + + delete d; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void VirtualMineImpl::setUniqueId(const UniqueMapId &uniqueId) +{ + BaseClass::setUniqueId(uniqueId); + d->m_uniqueMapId = m_uniqueMapId; +} + + +PositionCont VirtualMineImpl::subsidiaryLine(int key) const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (d->hasPathObject(key)) + return d->getPathObject(key).basicPathObj->positions(); + else + return PositionCont(); +} + +void VirtualMineImpl::setSubsidiaryLine(int key, const PositionCont &positions, const OptionalData &optData) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << ", key=" << key + << ", closed=" << optData.closed + << ", positions.size=" << positions.size(); + + Private::ValueDataVM vd = d->getPathObject(key, optData); + QSharedPointer pathObj = vd.basicPathObj; + if (pathObj) + pathObj->setPositions(positions); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +QList VirtualMineImpl::subsidiaryKeysInUse() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return d->keysInUse(); +} + +void VirtualMineImpl::removeSubsidiaryLine(int key) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return d->remove(key); +} + +void VirtualMineImpl::removeSubsidiaryLines() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return d->clear(); +} + +bool VirtualMineImpl::has3DMarkerRenderer() const +{ +// TRACE_LOG(m_logger) << LOGFUNCTION << " START"; +// TRACE_LOG(m_logger) << LOGFUNCTION << " END, isEmpty = " << d->m_markerSymbolData.m_origin.isEmpty(); + return !d->m_markerSymbolDataRenderer.m_origin.isEmpty(); +} + +void VirtualMineImpl::set3DMarkerRenderer(const Point3D &markerOrigin, const QVariantMap &attribMap) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + d->m_markerSymbolDataRenderer.m_origin.setOrigin(markerOrigin); + d->m_markerSymbolDataRenderer.m_attribMap = attribMap; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +bool VirtualMineImpl::has3DMarkerSceneSymbol() const +{ +// TRACE_LOG(m_logger) << LOGFUNCTION << " START"; +// TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return !d->m_markerSymbolData.m_origin.isEmpty(); +} + +void VirtualMineImpl::set3DMarkerSceneSymbol(const Point3D &markerOrigin, const SceneSymbolData &sceneSymbolData) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + d->m_markerSymbolData.m_origin.setOrigin(markerOrigin); + d->m_markerSymbolData.m_sceneSymbolData = sceneSymbolData; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +bool VirtualMineImpl::updateViewImpl() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " m_debugInfo=" << m_debugInfo + << " symbol=" << m_symbol.value().sidc + << " visibility=" << m_visibility + << " visiTechn=" << m_visibilityTechnical + << " visiTact=" << m_visibilityTactical; + + bool success = BaseClass::updateViewImpl(); + + if (!success) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed"; + return false; + } + + if (nullptr == m_mainGraphicalObject) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no m_mainGraphicalObject"; + return false; + } + + // subsidiary : + + Private::MAP_IT_T mapIt(d->m_cont); + while (mapIt.hasNext()) + { + mapIt.next(); + const Private::ValueDataVM &vd = mapIt.value(); + QSharedPointer pathObj = vd.basicPathObj; + const OptionalData &optData = vd.optData; + if (pathObj) + { +// pathObj->setDebugInfo(m_mainGraphicalObject->debugInfo()); // will overwrite complete string -> skip + + if (optData.lineStyle) + pathObj->setLineStyle((*optData.lineStyle.data())); + else + pathObj->setLineStyle(m_mainGraphicalObject->lineStyle()); + + // transfer fillStyle only if relevant: + if (pathObj->pathType() == GraphicalObjectPathType::Polygon) + { + if (optData.fillStyle) + pathObj->setFillStyle((*optData.fillStyle.data())); + else + pathObj->setFillStyle(m_mainGraphicalObject->fillStyle()); + } + + pathObj->setVisibility(m_visibility); + pathObj->setVisibility(ObjectAppearance::Technical, m_visibilityTechnical); + + // Do not Transfer symbol. Only m_mainGraphicalObject uses ObjectAppearance::Tactical: +// pathObj->setSymbol(this->symbol()); + + + pathObj->setVisibility(ObjectAppearance::Tactical, false); + + pathObj->setPickable(m_mainGraphicalObject->pickable()); + + pathObj->setSelected(m_mainGraphicalObject->selected()); + + pathObj->setDrawOrder(m_internalDrawOrder); + + success = pathObj->updateView(); + if (!success) + { + int key = mapIt.key(); + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed, key=" << key + << ", m_debugInfo=" << m_debugInfo; + + } + } + } // while + + // : subsidiary + + + // 3DMarkerSceneSymbol : + if (m_mapImpl->mode() == Mode::Mode_3D) + { + const bool successUpdateRender = updateViewImpl3DMarkerRenderer(); + if (!successUpdateRender) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl3DMarkerRenderer failed" << ", m_debugInfo=" << m_debugInfo; + + const bool successUpdateSceneSymbol = updateViewImpl3DMarkerSceneSymbol(); + if (!successUpdateSceneSymbol) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl3DMarkerSceneSymbol failed" << ", m_debugInfo=" << m_debugInfo; + + success = success && successUpdateRender && successUpdateSceneSymbol; + } + // : 3DMarkerSceneSymbol + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool VirtualMineImpl::updateViewImpl3DMarkerRenderer() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool success = true; + + // qCritical() << " attribMap " << attribMap; + if (m_mapImpl->mode() == Mode::Mode_3D) + { + if (d->m_markerSymbolDataRenderer.m_origin.isEmpty()) + { + d->m_markerSymbolDataRenderer.m_goRendererAtt.clear(); + } else { + + if (!d->m_markerSymbolDataRenderer.m_goRendererAtt) + { + QString debugInfo = QStringLiteral("%1#goRendererAtt").arg(m_debugInfo); + d->m_markerSymbolDataRenderer.m_goRendererAtt = QSharedPointer + (new GraphicalObjectRendererAttributes(m_mapImpl->getEsriGeoView() + , m_graphicsLayer + , debugInfo + , m_uniqueMapId) + ); + } + + d->m_markerSymbolDataRenderer.m_origin.setMapObjectImpl(this); + const Point3D &pos = d->m_markerSymbolDataRenderer.m_origin.originTranslated(); + d->m_markerSymbolDataRenderer.m_goRendererAtt->setOrigin(pos); + + d->m_markerSymbolDataRenderer.m_goRendererAtt->setAttributeMap(d->m_markerSymbolDataRenderer.m_attribMap); + + d->m_markerSymbolDataRenderer.m_goRendererAtt->setVisibility(m_visibility); + d->m_markerSymbolDataRenderer.m_goRendererAtt->setPickable(m_mainGraphicalObject->pickable()); + d->m_markerSymbolDataRenderer.m_goRendererAtt->setSelected(m_mainGraphicalObject->selected()); + d->m_markerSymbolDataRenderer.m_goRendererAtt->setDrawOrder(m_internalDrawOrder); + + // scale does not makes sense :-) + + success = d->m_markerSymbolDataRenderer.m_goRendererAtt->updateView(); + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed, m_goRendererAtt" + << ", m_debugInfo=" << m_debugInfo; + } + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + +bool VirtualMineImpl::updateViewImpl3DMarkerSceneSymbol() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + bool success = true; + + // qCritical() << " attribMap " << attribMap; + if (m_mapImpl->mode() == Mode::Mode_3D) + { + if (d->m_markerSymbolData.m_origin.isEmpty()) + { + d->m_markerSymbolData.m_goSceneMarker.clear(); + } else { + + if (!d->m_markerSymbolData.m_goSceneMarker) + { + SituationMap::SceneMarkerType type = SituationMap::SceneMarkerType::SPHERE; + if (d->m_markerSymbolData.m_sceneSymbolData.cone) + type = SituationMap::SceneMarkerType::CONE; + + QString debugInfo = QStringLiteral("%1#goSceneMarker").arg(m_debugInfo); + d->m_markerSymbolData.m_goSceneMarker = QSharedPointer + (new GraphicalObjectSceneMarker(type, + m_mapImpl->getEsriGeoView() + , m_graphicsLayer + , debugInfo + , m_uniqueMapId) + ); + } + + d->m_markerSymbolData.m_origin.setMapObjectImpl(this); + const Point3D &pos = d->m_markerSymbolData.m_origin.originTranslated(); + d->m_markerSymbolData.m_goSceneMarker->setOrigin(pos); + d->m_markerSymbolData.m_goSceneMarker->setDiameter(d->m_markerSymbolData.m_sceneSymbolData.radius*2.0); + + d->m_markerSymbolData.m_goSceneMarker->setHeading(d->m_markerSymbolData.m_sceneSymbolData.heading); + d->m_markerSymbolData.m_goSceneMarker->setPitch(d->m_markerSymbolData.m_sceneSymbolData.pitch); + d->m_markerSymbolData.m_goSceneMarker->setRoll(d->m_markerSymbolData.m_sceneSymbolData.roll); + + d->m_markerSymbolData.m_goSceneMarker->setVisibility(m_visibility); + d->m_markerSymbolData.m_goSceneMarker->setPickable(m_mainGraphicalObject->pickable()); + d->m_markerSymbolData.m_goSceneMarker->setSelected(m_mainGraphicalObject->selected()); + d->m_markerSymbolData.m_goSceneMarker->setDrawOrder(m_internalDrawOrder); + + QColor col = d->m_markerSymbolData.m_sceneSymbolData.color; + col.setAlphaF(col.alphaF() / 2.0); + d->m_markerSymbolData.m_goSceneMarker->setFillStyle(FillStyle(col, FillPattern::Solid)); + + // scale does not makes sense :-) + + success = d->m_markerSymbolData.m_goSceneMarker->updateView(); + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " updateViewImpl failed, m_goSceneMarker" + << ", m_debugInfo=" << m_debugInfo; + } + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + + +bool VirtualMineImpl::init(MapImpl *mapImpl, const QString &logicalLayerName) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (mapImpl) + { + switch (mapImpl->symbologyType()) + { + case AdvancedSymbology::SymbologyType::APP6D: + case AdvancedSymbology::SymbologyType::MIL2525D: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "10012500002719000000"); // SymbolSet = 25 271900 + break; + case AdvancedSymbology::SymbologyType::MIL2525C: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GUMPNR----"); // MIL2525D: 1388 GUMPNR---- -> Polygon (Radioactive Area U) +// m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"GUMPNZ----"); // MIL2525D: 0359 GUMPNZ---- -> Point (Nuclear Detonations Ground Zero U) + break; + case AdvancedSymbology::SymbologyType::APP6B: + m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(), "GUCPBWR---"); // APP6B: 3935 GUCPBWR--- -> Polygon +// m_symbol.refValue().sidc = AdvancedSymbology::SidcCodec::encodeComplete(mapImpl->symbologyType(),"GUCPBWP---"); // APP6B: 2871 GUCPBWP--- -> Point (Fallout Producing U) + break; + case AdvancedSymbology::SymbologyType::None: + // empty + break; + } // switch + } // if (mapImpl) + + + bool success = BaseClass::init(mapImpl, logicalLayerName, GraphicalObjectPathType::Polygon); + + setLineStyle(LineStyle(Qt::yellow, 2)); + setFillStyle(FillStyle(Qt::yellow, FillPattern::Null)); + + if (!success) + ERROR_LOG(m_logger) << LOGFUNCTION << " init failed"; + + // set some values to Private: + d->m_mapImpl = m_mapImpl; + d->m_logicalLayer = m_logicalLayerName; + d->m_debugInfo = m_debugInfo; + d->m_uniqueMapId = m_uniqueMapId; + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END, success=" << success; + return success; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/virtual_mine_impl.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/virtual_mine_impl.h new file mode 100755 index 000000000..a6e860620 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MapObjectsImpl/virtual_mine_impl.h @@ -0,0 +1,102 @@ +/** + * @RDE_HEADER@ + * Interface-Implementation of VirtualMine + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "abstract_multi_point_map_object_impl.h" +#include "basic_path_object_impl.h" +#include // Interface + + +// Qt +#include + + + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +/** + * @brief The VirtualMineImpl class displays a nuclear area. The outline is displayed as well a subsidiary lines. + * + * A class instance consists of the following logical elements: + * - mainGraphicalObject (GraphicalObjectPath displaying the main techn. Appearance / outline) + * - mainGraphicalObjectText (displaying the text/name) + * - tacticalAppearance (displaying the tact. Appearance) + * - Container of BasicPathObjectImpl's to display the subsidiary lines. + */ +// inherent 'public', to get that implementation +// inherent 'public virtual' to get that interface +class SITUATIONMAPOBJECTS_EXPORT VirtualMineImpl : public BasicPathObjectImpl, public virtual VirtualMine +{ + using BaseClass = BasicPathObjectImpl; + + template friend class GenericObjectFactory; +protected: + explicit VirtualMineImpl(const QString &debugInfo = QString()); + virtual ~VirtualMineImpl(); + +public: + VirtualMineImpl(const VirtualMineImpl &other) = delete; + VirtualMineImpl &operator=(VirtualMineImpl &other) = delete; + + void setUniqueId(const UniqueMapId &uniqueId) override; + + /*! \name Subsidiary polylines, identified by an arbitrary choose key.*/ + //@{ + PositionCont subsidiaryLine(int key) const override; + void setSubsidiaryLine(int key, const PositionCont &positions, const OptionalData &optData = OptionalData()) override; + + QList subsidiaryKeysInUse() override; + + void removeSubsidiaryLine(int key) override; + void removeSubsidiaryLines() override; + //@} + + + /*! \name 3D marker / SimpleMarkerSceneSymbol (via renderer) */ + //@{ + bool has3DMarkerRenderer() const override; + void set3DMarkerRenderer(const Point3D &markerOrigin, const QVariantMap &attribMap) override; + //@} + + bool has3DMarkerSceneSymbol() const override; + void set3DMarkerSceneSymbol(const Point3D &markerOrigin, const SceneSymbolData &sceneSymbolData) override; + + +protected: + bool updateViewImpl() override; + bool updateViewImpl3DMarkerRenderer(); + bool updateViewImpl3DMarkerSceneSymbol(); + + friend class MapImpl; + bool init(MapImpl *mapImpl, const QString &logicalLayerName) override; + + // internal/admin + + + // logical + + +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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/clipper.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/clipper.cpp new file mode 100755 index 000000000..d1b156223 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/clipper.cpp @@ -0,0 +1,4691 @@ +/** +** +** @RDE_HEADER@ +** Clipping and Offsetting of lines and polygons +** $Id$ +** $HeadURL$ +** $Author$ +** +** ----------------------------------------------------------------------------------------------- +** To use under Boost Software License Vers. 1 +** see also http://www.angusj.com/delphi/clipper/documentation/Docs/_Body.htm +** ----------------------------------------------------------------------------------------------- +*/ + +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.2.1 * +* Date : 31 October 2014 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2014 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +/******************************************************************************* +* * +* This is a translation of the Delphi Clipper library and the naming style * +* used has retained a Delphi flavour. * +* * +*******************************************************************************/ + +#include "clipper.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "math_const.h" + +namespace ClipperLib { + +static double const pi = MathConst::pi; +static double const two_pi = MathConst::pi_two; +static double const def_arc_tolerance = 0.25; + +enum Direction { dRightToLeft, dLeftToRight }; + +static int const Unassigned = -1; //edge not currently 'owning' a solution +static int const Skip = -2; //edge that would otherwise close a path + +#define HORIZONTAL (-1.0E+40) +#define TOLERANCE (1.0e-20) +#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) + +struct TEdge { + IntPoint Bot; + IntPoint Curr; + IntPoint Top; + IntPoint Delta; + double Dx; + PolyType PolyTyp; + EdgeSide Side; + int WindDelta; //1 or -1 depending on winding direction + int WindCnt; + int WindCnt2; //winding count of the opposite polytype + int OutIdx; + TEdge *Next; + TEdge *Prev; + TEdge *NextInLML; + TEdge *NextInAEL; + TEdge *PrevInAEL; + TEdge *NextInSEL; + TEdge *PrevInSEL; + + TEdge() : Bot() + , Curr() + , Top() + , Delta() + , Dx(0) + , PolyTyp(PolyType::ptClip) + , Side(EdgeSide::esLeft) + , WindDelta(0) + , WindCnt(0) + , WindCnt2(0) + , OutIdx(0) + , Next(nullptr) + , Prev(nullptr) + , NextInLML(nullptr) + , NextInAEL(nullptr) + , PrevInAEL(nullptr) + , NextInSEL(nullptr) + , PrevInSEL(nullptr) {;} + + void init() + { + Bot = IntPoint(); + Curr = IntPoint(); + Top = IntPoint(); + Delta = IntPoint(); + Dx = 0.0; + PolyTyp = PolyType::ptClip; + Side = EdgeSide::esLeft; + WindDelta = 0; + WindCnt = 0; + WindCnt2 = 0; + OutIdx = 0; + Next = nullptr; + Prev = nullptr; + NextInLML = nullptr; + NextInAEL = nullptr; + PrevInAEL = nullptr; + NextInSEL = nullptr; + PrevInSEL = nullptr; + } +}; + +struct IntersectNode { + TEdge *Edge1; + TEdge *Edge2; + IntPoint Pt; +}; + +struct LocalMinimum { + cInt Y; + TEdge *LeftBound; + TEdge *RightBound; +}; + +struct OutPt; + +struct OutRec { + int Idx; + bool IsHole; + bool IsOpen; + OutRec *FirstLeft; //see comments in clipper.pas + PolyNode *PolyNd; + OutPt *Pts; + OutPt *BottomPt; +}; + +struct OutPt { + int Idx; + IntPoint Pt; + OutPt *Next; + OutPt *Prev; +}; + +struct Join { + OutPt *OutPt1; + OutPt *OutPt2; + IntPoint OffPt; +}; + +struct LocMinSorter +{ + inline bool operator()(const LocalMinimum& locMin1, const LocalMinimum& locMin2) const + { + return locMin2.Y < locMin1.Y; + } +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +inline cInt Round(double val) +{ + if ((val < 0)) return static_cast(val - 0.5); + else return static_cast(val + 0.5); +} +//------------------------------------------------------------------------------ + +inline cInt Abs(cInt val) +{ + return val < 0 ? -val : val; +} + +//------------------------------------------------------------------------------ +// PolyTree methods ... +//------------------------------------------------------------------------------ + +void PolyTree::Clear() +{ + for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i) + delete AllNodes[i]; + AllNodes.resize(0); + Childs.resize(0); +} +//------------------------------------------------------------------------------ + +PolyNode* PolyTree::GetFirst() const +{ + if (!Childs.empty()) + return Childs[0]; + else + return nullptr; +} +//------------------------------------------------------------------------------ + +int PolyTree::Total() const +{ + int result = (int)AllNodes.size(); + //with negative offsets, ignore the hidden outer polygon ... + if (result > 0 && Childs[0] != AllNodes[0]) result--; + return result; +} + +//------------------------------------------------------------------------------ +// PolyNode methods ... +//------------------------------------------------------------------------------ + +PolyNode::PolyNode() + : Contour() + , Childs() + , Parent(nullptr) + , Index(0) + , m_IsOpen(false) + , m_jointype(JoinType::jtSquare) + , m_endtype(EndType::etClosedPolygon) +{ +} +//------------------------------------------------------------------------------ + +int PolyNode::ChildCount() const +{ + return (int)Childs.size(); +} +//------------------------------------------------------------------------------ + +void PolyNode::AddChild(PolyNode& child) +{ + unsigned cnt = (unsigned)Childs.size(); + Childs.emplace_back(&child); + child.Parent = this; + child.Index = cnt; +} +//------------------------------------------------------------------------------ + +PolyNode* PolyNode::GetNext() const +{ + if (!Childs.empty()) + return Childs[0]; + else + return GetNextSiblingUp(); +} +//------------------------------------------------------------------------------ + +PolyNode* PolyNode::GetNextSiblingUp() const +{ + if (!Parent) //protects against PolyTree.GetNextSiblingUp() + return nullptr; + else if (Index == Parent->Childs.size() - 1) + return Parent->GetNextSiblingUp(); + else + return Parent->Childs[Index + 1]; +} +//------------------------------------------------------------------------------ + +bool PolyNode::IsHole() const +{ + bool result = true; + PolyNode* node = Parent; + while (node) + { + result = !result; + node = node->Parent; + } + return result; +} +//------------------------------------------------------------------------------ + +bool PolyNode::IsOpen() const +{ + return m_IsOpen; +} +//------------------------------------------------------------------------------ + +#ifndef use_int32 + +//------------------------------------------------------------------------------ +// Int128 class (enables safe math on signed 64bit integers) +// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 +// Int128 val2((long64)9223372036854775807); +// Int128 val3 = val1 * val2; +// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) +//------------------------------------------------------------------------------ + +class Int128 +{ + public: + ulong64 lo = 0; + long64 hi = 0; + + Int128() = default; + explicit Int128(long64 _lo) + : lo(_lo), hi(_lo < 0 ? -1 : 0) + {} + + explicit Int128(const long64& _hi, const ulong64& _lo) + : lo(_lo), hi(_hi) + {} + + Int128 & operator = (const long64 &val) + { + lo = (ulong64)val; + if (val < 0) hi = -1; else hi = 0; + return *this; + } + + bool operator == (const Int128 &val) const + {return (hi == val.hi && lo == val.lo);} + + bool operator != (const Int128 &val) const + { return !(*this == val);} + + bool operator > (const Int128 &val) const + { + if (hi != val.hi) + return hi > val.hi; + else + return lo > val.lo; + } + + bool operator < (const Int128 &val) const + { + if (hi != val.hi) + return hi < val.hi; + else + return lo < val.lo; + } + + bool operator >= (const Int128 &val) const + { return !(*this < val);} + + bool operator <= (const Int128 &val) const + { return !(*this > val);} + + Int128& operator += (const Int128 &rhs) + { + hi += rhs.hi; + lo += rhs.lo; + if (lo < rhs.lo) hi++; + return *this; + } + + Int128 operator + (const Int128 &rhs) const + { + Int128 result(*this); + result+= rhs; + return result; + } + + Int128& operator -= (const Int128 &rhs) + { + *this += -rhs; + return *this; + } + + Int128 operator - (const Int128 &rhs) const + { + Int128 result(*this); + result -= rhs; + return result; + } + + Int128 operator-() const //unary negation + { + if (lo == 0) + return Int128(-hi, 0); + else + return Int128(~hi, ~lo + 1); + } + + operator double() const + { + const double shift64 = 18446744073709551616.0; //2^64 + if (hi < 0) + { + if (lo == 0) return (double)hi * shift64; + else return -(double)(~lo + ~hi * shift64); + } + else + return (double)(lo + hi * shift64); + } + +}; +//------------------------------------------------------------------------------ + +Int128 Int128Mul (long64 lhs, long64 rhs) +{ + bool negate = (lhs < 0) != (rhs < 0); + + if (lhs < 0) lhs = -lhs; + ulong64 int1Hi = ulong64(lhs) >> 32; + ulong64 int1Lo = ulong64(lhs & 0xFFFFFFFF); + + if (rhs < 0) rhs = -rhs; + ulong64 int2Hi = ulong64(rhs) >> 32; + ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF); + + //nb: see comments in clipper.pas + ulong64 a = int1Hi * int2Hi; + ulong64 b = int1Lo * int2Lo; + ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; + + Int128 tmp; + tmp.hi = long64(a + (c >> 32)); + tmp.lo = long64(c << 32); + tmp.lo += long64(b); + if (tmp.lo < b) tmp.hi++; + if (negate) tmp = -tmp; + return tmp; +}; +#endif + +//------------------------------------------------------------------------------ +// Miscellaneous global functions +//------------------------------------------------------------------------------ + +void Swap(cInt& val1, cInt& val2) +{ + cInt tmp = val1; + val1 = val2; + val2 = tmp; +} +//------------------------------------------------------------------------------ +bool Orientation(const Path &poly) +{ + return Area(poly) >= 0; +} +//------------------------------------------------------------------------------ + +double Area(const Path &poly) +{ + int size = (int)poly.size(); + if (size < 3) return 0; + + double a = 0; + for (int i = 0, j = size -1; i < size; ++i) + { + a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y); + j = i; + } + return -a * 0.5; +} +//------------------------------------------------------------------------------ + +double Area(const OutRec &outRec) +{ + OutPt *op = outRec.Pts; + if (!op) return 0; + double a = 0; + do { + a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y); + op = op->Next; + } while (op != outRec.Pts); + return a * 0.5; +} +//------------------------------------------------------------------------------ + +bool PointIsVertex(const IntPoint &Pt, OutPt *pp) +{ + OutPt *pp2 = pp; + do + { + if (pp2->Pt == Pt) return true; + pp2 = pp2->Next; + } + while (pp2 != pp); + return false; +} +//------------------------------------------------------------------------------ + +int PointInPolygon (const IntPoint &pt, const Path &path) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + //See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos + //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf + int result = 0; + size_t cnt = path.size(); + if (cnt < 3) return 0; + IntPoint ip = path[0]; + for(size_t i = 1; i <= cnt; ++i) + { + IntPoint ipNext = (i == cnt ? path[0] : path[i]); + if (ipNext.Y == pt.Y) + { + if ((ipNext.X == pt.X) || (ip.Y == pt.Y && + ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1; + } + if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y)) + { + if (ip.X >= pt.X) + { + if (ipNext.X > pt.X) result = 1 - result; + else + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } else + { + if (ipNext.X > pt.X) + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } + } + ip = ipNext; + } + return result; +} +//------------------------------------------------------------------------------ + +int PointInPolygon (const IntPoint &pt, OutPt *op) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + int result = 0; + OutPt* startOp = op; + for(;;) + { + if (op->Next->Pt.Y == pt.Y) + { + if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y && + ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1; + } + if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y)) + { + if (op->Pt.X >= pt.X) + { + if (op->Next->Pt.X > pt.X) result = 1 - result; + else + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } else + { + if (op->Next->Pt.X > pt.X) + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } + } + op = op->Next; + if (startOp == op) break; + } + return result; +} +//------------------------------------------------------------------------------ + +bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) +{ + OutPt* op = OutPt1; + do + { + //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon + int res = PointInPolygon(op->Pt, OutPt2); + if (res >= 0) return res > 0; + op = op->Next; + } + while (op != OutPt1); + return true; +} +//---------------------------------------------------------------------- + +bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(e1.Delta.Y, e2.Delta.X) == Int128Mul(e1.Delta.X, e2.Delta.Y); + else +#endif + return e1.Delta.Y * e2.Delta.X == e1.Delta.X * e2.Delta.Y; +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(const IntPoint & pt1, const IntPoint & pt2, + const IntPoint &pt3, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y); + else +#endif + return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(const IntPoint & pt1, const IntPoint & pt2, + const IntPoint & pt3, const IntPoint & pt4, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y); + else +#endif + return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); +} +//------------------------------------------------------------------------------ + +inline bool IsHorizontal(const TEdge &e) +{ + return e.Delta.Y == 0; +} +//------------------------------------------------------------------------------ + +inline double GetDx(const IntPoint & pt1, const IntPoint & pt2) +{ + return (pt1.Y == pt2.Y) ? + HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); +} +//--------------------------------------------------------------------------- + +inline void SetDx(TEdge &e) +{ + e.Delta.X = (e.Top.X - e.Bot.X); + e.Delta.Y = (e.Top.Y - e.Bot.Y); + + if (e.Delta.Y == 0) e.Dx = HORIZONTAL; + else e.Dx = (double)(e.Delta.X) / e.Delta.Y; +} +//--------------------------------------------------------------------------- + +inline void SwapSides(TEdge &Edge1, TEdge &Edge2) +{ + EdgeSide Side = Edge1.Side; + Edge1.Side = Edge2.Side; + Edge2.Side = Side; +} +//------------------------------------------------------------------------------ + +inline void SwapPolyIndexes(TEdge &Edge1, TEdge &Edge2) +{ + int OutIdx = Edge1.OutIdx; + Edge1.OutIdx = Edge2.OutIdx; + Edge2.OutIdx = OutIdx; +} +//------------------------------------------------------------------------------ + +inline cInt TopX(const TEdge &edge, const cInt currentY) +{ + return ( currentY == edge.Top.Y ) ? + edge.Top.X : edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y)); +} +//------------------------------------------------------------------------------ + +void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) +{ +#ifdef use_xyz + ip.Z = 0; +#endif + + double b1, b2; + if (Edge1.Dx == Edge2.Dx) + { + ip.Y = Edge1.Curr.Y; + ip.X = TopX(Edge1, ip.Y); + return; + } + else if (Edge1.Delta.X == 0) + { + ip.X = Edge1.Bot.X; + if (IsHorizontal(Edge2)) + ip.Y = Edge2.Bot.Y; + else + { + b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx); + ip.Y = Round(ip.X / Edge2.Dx + b2); + } + } + else if (Edge2.Delta.X == 0) + { + ip.X = Edge2.Bot.X; + if (IsHorizontal(Edge1)) + ip.Y = Edge1.Bot.Y; + else + { + b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx); + ip.Y = Round(ip.X / Edge1.Dx + b1); + } + } + else + { + b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx; + b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx; + double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); + ip.Y = Round(q); + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = Round(Edge1.Dx * q + b1); + else + ip.X = Round(Edge2.Dx * q + b2); + } + + if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y) + { + if (Edge1.Top.Y > Edge2.Top.Y) + ip.Y = Edge1.Top.Y; + else + ip.Y = Edge2.Top.Y; + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = TopX(Edge1, ip.Y); + else + ip.X = TopX(Edge2, ip.Y); + } + //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... + if (ip.Y > Edge1.Curr.Y) + { + ip.Y = Edge1.Curr.Y; + //use the more vertical edge to derive X ... + if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx)) + ip.X = TopX(Edge2, ip.Y); else + ip.X = TopX(Edge1, ip.Y); + } +} +//------------------------------------------------------------------------------ + +void ReversePolyPtLinks(OutPt *pp) +{ + if (!pp) return; + OutPt *pp1, *pp2; + pp1 = pp; + do { + pp2 = pp1->Next; + pp1->Next = pp1->Prev; + pp1->Prev = pp2; + pp1 = pp2; + } while( pp1 != pp ); +} +//------------------------------------------------------------------------------ + +void DisposeOutPts(OutPt*& pp) +{ + if (pp == nullptr) return; + pp->Prev->Next = nullptr; + while( pp ) + { + OutPt *tmpPp = pp; + pp = pp->Next; + delete tmpPp; + } +} +//------------------------------------------------------------------------------ + +inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt) +{ + #ifdef USE_MEMSET + std::memset(e, 0, sizeof(TEdge)); + #else + e->init(); + #endif + e->Next = eNext; + e->Prev = ePrev; + e->Curr = Pt; + e->OutIdx = Unassigned; +} +//------------------------------------------------------------------------------ + +void InitEdge2(TEdge& e, PolyType Pt) +{ + if (e.Curr.Y >= e.Next->Curr.Y) + { + e.Bot = e.Curr; + e.Top = e.Next->Curr; + } else + { + e.Top = e.Curr; + e.Bot = e.Next->Curr; + } + SetDx(e); + e.PolyTyp = Pt; +} +//------------------------------------------------------------------------------ + +TEdge* RemoveEdge(TEdge* e) +{ + //removes e from double_linked_list (but without removing from memory) + e->Prev->Next = e->Next; + e->Next->Prev = e->Prev; + TEdge* result = e->Next; + e->Prev = nullptr; //flag as removed (see ClipperBase.Clear) + return result; +} +//------------------------------------------------------------------------------ + +inline void ReverseHorizontal(TEdge &e) +{ + //swap horizontal edges' Top and Bottom x's so they follow the natural + //progression of the bounds - ie so their xbots will align with the + //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] + Swap(e.Top.X, e.Bot.X); +#ifdef use_xyz + Swap(e.Top.Z, e.Bot.Z); +#endif +} +//------------------------------------------------------------------------------ + +void SwapPoints(IntPoint &pt1, IntPoint &pt2) +{ + IntPoint tmp = pt1; + pt1 = pt2; + pt2 = tmp; +} +//------------------------------------------------------------------------------ + +bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, + IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) +{ + //precondition: segments are Collinear. + if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y)) + { + if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); + if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b); + if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; + return pt1.X < pt2.X; + } else + { + if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b); + if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b); + if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; + return pt1.Y > pt2.Y; + } +} +//------------------------------------------------------------------------------ + +bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) +{ + OutPt *p = btmPt1->Prev; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev; + double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + p = btmPt1->Next; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next; + double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + + p = btmPt2->Prev; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev; + double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + p = btmPt2->Next; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next; + double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); +} +//------------------------------------------------------------------------------ + +OutPt* GetBottomPt(OutPt *pp) +{ + OutPt* dups = nullptr; + OutPt* p = pp->Next; + while (p != pp) + { + if (p->Pt.Y > pp->Pt.Y) + { + pp = p; + dups = nullptr; + } + else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X) + { + if (p->Pt.X < pp->Pt.X) + { + dups = nullptr; + pp = p; + } else + { + if (p->Next != pp && p->Prev != pp) dups = p; + } + } + p = p->Next; + } + if (dups) + { + //there appears to be at least 2 vertices at BottomPt so ... + while (dups != p) + { + if (!FirstIsBottomPt(p, dups)) pp = dups; + dups = dups->Next; + while (dups->Pt != pp->Pt) dups = dups->Next; + } + } + return pp; +} +//------------------------------------------------------------------------------ + +bool Pt2IsBetweenPt1AndPt3(const IntPoint & pt1, + const IntPoint & pt2, const IntPoint & pt3) +{ + if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) + return false; + else if (pt1.X != pt3.X) + return (pt2.X > pt1.X) == (pt2.X < pt3.X); + else + return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); +} +//------------------------------------------------------------------------------ + +bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) +{ + if (seg1a > seg1b) Swap(seg1a, seg1b); + if (seg2a > seg2b) Swap(seg2a, seg2b); + return (seg1a < seg2b) && (seg2a < seg1b); +} + +//------------------------------------------------------------------------------ +// ClipperBase class methods ... +//------------------------------------------------------------------------------ + + +ClipperBase::ClipperBase() : m_MinimaList(), m_UseFullRange(false), m_edges(), m_PreserveCollinear(false), m_HasOpenPaths(false) +{ + m_CurrentLM = m_MinimaList.begin(); //begin() == end() here +} +//------------------------------------------------------------------------------ + +ClipperBase::~ClipperBase() //destructor +{ + Clear(); +} +//------------------------------------------------------------------------------ + +void RangeTest(const IntPoint& Pt, bool& useFullRange) +{ + if (useFullRange) + { + if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) + throw "Coordinate outside allowed range"; + } + else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) + { + useFullRange = true; + RangeTest(Pt, useFullRange); + } +} +//------------------------------------------------------------------------------ + +TEdge* FindNextLocMin(TEdge* E) +{ + for (;;) + { + while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next; + if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break; + while (IsHorizontal(*E->Prev)) E = E->Prev; + TEdge* E2 = E; + while (IsHorizontal(*E)) E = E->Next; + if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz. + if (E2->Prev->Bot.X < E->Bot.X) E = E2; + break; + } + return E; +} +//------------------------------------------------------------------------------ + +TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) +{ + TEdge *Result = E; + TEdge *Horz = nullptr; + + if (E->OutIdx == Skip) + { + //if edges still remain in the current bound beyond the skip edge then + //create another LocMin and call ProcessBound once more + if (NextIsForward) + { + while (E->Top.Y == E->Next->Bot.Y) E = E->Next; + //don't include top horizontals when parsing a bound a second time, + //they will be contained in the opposite bound ... + while (E != Result && IsHorizontal(*E)) E = E->Prev; + } + else + { + while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev; + while (E != Result && IsHorizontal(*E)) E = E->Next; + } + + if (E == Result) + { + if (NextIsForward) Result = E->Next; + else Result = E->Prev; + } + else + { + //there are more edges in the bound beyond result starting with E + if (NextIsForward) + E = Result->Next; + else + E = Result->Prev; + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + locMin.LeftBound = nullptr; + locMin.RightBound = E; + E->WindDelta = 0; + Result = ProcessBound(E, NextIsForward); + m_MinimaList.emplace_back(locMin); + } + return Result; + } + + TEdge *EStart; + + if (IsHorizontal(*E)) + { + //We need to be careful with open paths because this may not be a + //true local minima (ie E may be following a skip edge). + //Also, consecutive horz. edges may start heading left before going right. + if (NextIsForward) + EStart = E->Prev; + else + EStart = E->Next; + if (EStart->OutIdx != Skip) + { + if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge + { + if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X) + ReverseHorizontal(*E); + } + else if (EStart->Bot.X != E->Bot.X) + ReverseHorizontal(*E); + } + } + + EStart = E; + if (NextIsForward) + { + while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip) + Result = Result->Next; + if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip) + { + //nb: at the top of a bound, horizontals are added to the bound + //only when the preceding edge attaches to the horizontal's left vertex + //unless a Skip edge is encountered when that becomes the top divide + Horz = Result; + while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; + if (Horz->Prev->Top.X == Result->Next->Top.X) + { + if (!NextIsForward) Result = Horz->Prev; + } + else if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; + } + while (E != Result) + { + E->NextInLML = E->Next; + if (IsHorizontal(*E) && E != EStart && + E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + E = E->Next; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) + ReverseHorizontal(*E); + Result = Result->Next; //move to the edge just beyond current bound + } else + { + while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) + Result = Result->Prev; + if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip) + { + Horz = Result; + while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; + if (Horz->Next->Top.X == Result->Prev->Top.X) + { + if (!NextIsForward) Result = Horz->Next; + } + else if (Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; + } + + while (E != Result) + { + E->NextInLML = E->Prev; + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + E = E->Prev; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + Result = Result->Prev; //move to the edge just beyond current bound + } + + return Result; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) +{ +#ifdef use_lines + if (!Closed && PolyTyp == ptClip) + throw clipperException("AddPath: Open paths must be subject."); +#else + if (!Closed) + throw clipperException("AddPath: Open paths have been disabled."); +#endif + + int highI = (int)pg.size() -1; + if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI; + while (highI > 0 && (pg[highI] == pg[highI -1])) --highI; + if ((Closed && highI < 2) || (!Closed && highI < 1)) return false; + + //create a new edge array ... + TEdge *edges = new TEdge [highI +1]; + + bool IsFlat = true; + //1. Basic (first) edge initialization ... + try + { + edges[1].Curr = pg[1]; + RangeTest(pg[0], m_UseFullRange); + RangeTest(pg[highI], m_UseFullRange); + InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]); + InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]); + for (int i = highI - 1; i >= 1; --i) + { + RangeTest(pg[i], m_UseFullRange); + InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); + } + } + catch(...) + { + delete [] edges; + throw; //range test fails + } + TEdge *eStart = &edges[0]; + + //2. Remove duplicate vertices, and (when closed) collinear edges ... + TEdge *E = eStart, *eLoopStop = eStart; + for (;;) + { + //nb: allows matching start and end points when not Closed ... + if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart)) + { + if (E == E->Next) break; + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + eLoopStop = E; + continue; + } + if (E->Prev == E->Next) + break; //only two vertices + else if (Closed && + SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) && + (!m_PreserveCollinear || + !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr))) + { + //Collinear edges are allowed for open paths but in closed paths + //the default is to merge adjacent collinear edges into a single edge. + //However, if the PreserveCollinear property is enabled, only overlapping + //collinear edges (ie spikes) will be removed from closed paths. + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + E = E->Prev; + eLoopStop = E; + continue; + } + E = E->Next; + if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break; + } + + if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) + { + delete [] edges; + return false; + } + + if (!Closed) + { + m_HasOpenPaths = true; + eStart->Prev->OutIdx = Skip; + } + + //3. Do second stage of edge initialization ... + E = eStart; + do + { + InitEdge2(*E, PolyTyp); + E = E->Next; + if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false; + } + while (E != eStart); + + //4. Finally, add edge bounds to LocalMinima list ... + + //Totally flat paths must be handled differently when adding them + //to LocalMinima list to avoid endless loops etc ... + if (IsFlat) + { + if (Closed) + { + delete [] edges; + return false; + } + E->Prev->OutIdx = Skip; + if (E->Prev->Bot.X < E->Prev->Top.X) ReverseHorizontal(*E->Prev); + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + locMin.LeftBound = nullptr; + locMin.RightBound = E; + locMin.RightBound->Side = esRight; + locMin.RightBound->WindDelta = 0; + while (E->Next->OutIdx != Skip) + { + E->NextInLML = E->Next; + if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + E = E->Next; + } + m_MinimaList.emplace_back(locMin); + m_edges.emplace_back(edges); + return true; + } + + m_edges.emplace_back(edges); + bool leftBoundIsForward; + TEdge* EMin = nullptr; + + //workaround to avoid an endless loop in the while loop below when + //open paths have matching start and end points ... + if (E->Prev->Bot == E->Prev->Top) E = E->Next; + + for (;;) + { + E = FindNextLocMin(E); + if (E == EMin) break; + else if (!EMin) EMin = E; + + //E and E.Prev now share a local minima (left aligned if horizontal). + //Compare their slopes to find which starts which bound ... + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + if (E->Dx < E->Prev->Dx) + { + locMin.LeftBound = E->Prev; + locMin.RightBound = E; + leftBoundIsForward = false; //Q.nextInLML = Q.prev + } else + { + locMin.LeftBound = E; + locMin.RightBound = E->Prev; + leftBoundIsForward = true; //Q.nextInLML = Q.next + } + locMin.LeftBound->Side = esLeft; + locMin.RightBound->Side = esRight; + + if (!Closed) locMin.LeftBound->WindDelta = 0; + else if (locMin.LeftBound->Next == locMin.RightBound) + locMin.LeftBound->WindDelta = -1; + else locMin.LeftBound->WindDelta = 1; + locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta; + + E = ProcessBound(locMin.LeftBound, leftBoundIsForward); + if (E->OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward); + + TEdge* E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward); + if (E2->OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward); + + if (locMin.LeftBound->OutIdx == Skip) + locMin.LeftBound = nullptr; + else if (locMin.RightBound->OutIdx == Skip) + locMin.RightBound = nullptr; + m_MinimaList.emplace_back(locMin); + if (!leftBoundIsForward) E = E2; + } + return true; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) +{ + bool result = false; + for (Paths::size_type i = 0; i < ppg.size(); ++i) + if (AddPath(ppg[i], PolyTyp, Closed)) result = true; + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::Clear() +{ + DisposeLocalMinimaList(); + for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) + { + //for each edge array in turn, find the first used edge and + //check for and remove any hiddenPts in each edge in the array. + TEdge* edges = m_edges[i]; + delete [] edges; + } + m_edges.clear(); + m_UseFullRange = false; + m_HasOpenPaths = false; +} +//------------------------------------------------------------------------------ + +void ClipperBase::Reset() +{ + m_CurrentLM = m_MinimaList.begin(); + if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process + std::sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter()); + + //reset all edges ... + for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) + { + TEdge* e = lm->LeftBound; + if (e) + { + e->Curr = e->Bot; + e->Side = esLeft; + e->OutIdx = Unassigned; + } + + e = lm->RightBound; + if (e) + { + e->Curr = e->Bot; + e->Side = esRight; + e->OutIdx = Unassigned; + } + } +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeLocalMinimaList() +{ + m_MinimaList.clear(); + m_CurrentLM = m_MinimaList.begin(); +} +//------------------------------------------------------------------------------ + +void ClipperBase::PopLocalMinima() +{ + if (m_CurrentLM == m_MinimaList.end()) return; + ++m_CurrentLM; +} +//------------------------------------------------------------------------------ + +IntRect ClipperBase::GetBounds() +{ + IntRect result; + MinimaList::iterator lm = m_MinimaList.begin(); + if (lm == m_MinimaList.end()) + { + result.left = result.top = result.right = result.bottom = 0; + return result; + } + result.left = lm->LeftBound->Bot.X; + result.top = lm->LeftBound->Bot.Y; + result.right = lm->LeftBound->Bot.X; + result.bottom = lm->LeftBound->Bot.Y; + while (lm != m_MinimaList.end()) + { + result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y); + TEdge* e = lm->LeftBound; + for (;;) { + TEdge* bottomE = e; + while (e->NextInLML) + { + if (e->Bot.X < result.left) result.left = e->Bot.X; + if (e->Bot.X > result.right) result.right = e->Bot.X; + e = e->NextInLML; + } + result.left = std::min(result.left, e->Bot.X); + result.right = std::max(result.right, e->Bot.X); + result.left = std::min(result.left, e->Top.X); + result.right = std::max(result.right, e->Top.X); + result.top = std::min(result.top, e->Top.Y); + if (bottomE == lm->LeftBound) e = lm->RightBound; + else break; + } + ++lm; + } + return result; +} + +//------------------------------------------------------------------------------ +// TClipper methods ... +//------------------------------------------------------------------------------ + +Clipper::Clipper(int initOptions) + : ClipperBase() + , m_PolyOuts() + , m_Joins() + , m_GhostJoins() + , m_IntersectList() + , m_ClipType(ClipType::ctIntersection) + , m_Scanbeam() + , m_ActiveEdges(nullptr) + , m_SortedEdges(nullptr) + , m_ExecuteLocked(false) + , m_ClipFillType(PolyFillType::pftEvenOdd) + , m_SubjFillType(PolyFillType::pftEvenOdd) + , m_ReverseOutput((initOptions & ioReverseSolution) != 0) + , m_UsingPolyTree(false) + , m_StrictSimple((initOptions & ioStrictlySimple) != 0) +#ifdef use_xyz + , m_ZFill(nullptr) +#endif +{ + m_UseFullRange = false; + m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0); + m_HasOpenPaths = false; +} +//------------------------------------------------------------------------------ + +Clipper::~Clipper() //destructor +{ + Clear(); +} +//------------------------------------------------------------------------------ + +#ifdef use_xyz +void Clipper::ZFillFunction(ZFillCallback zFillFunc) +{ + m_ZFill = zFillFunc; +} +//------------------------------------------------------------------------------ +#endif + +void Clipper::Reset() +{ + ClipperBase::Reset(); + m_Scanbeam = ScanbeamList(); + m_ActiveEdges = nullptr; + m_SortedEdges = nullptr; + for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) + InsertScanbeam(lm->Y); +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, Paths &solution, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + if( m_ExecuteLocked ) return false; + if (m_HasOpenPaths) + throw clipperException("Error: PolyTree struct is need for open path clipping."); + m_ExecuteLocked = true; + solution.resize(0); + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = false; + bool succeeded = ExecuteInternal(); + if (succeeded) BuildResult(solution); + DisposeAllOutRecs(); + m_ExecuteLocked = false; + return succeeded; +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, PolyTree& polytree, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + if( m_ExecuteLocked ) return false; + m_ExecuteLocked = true; + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = true; + bool succeeded = ExecuteInternal(); + if (succeeded) BuildResult2(polytree); + DisposeAllOutRecs(); + m_ExecuteLocked = false; + return succeeded; +} +//------------------------------------------------------------------------------ + +void Clipper::FixHoleLinkage(OutRec &outrec) +{ + //skip OutRecs that (a) contain outermost polygons or + //(b) already have the correct owner/child linkage ... + if (!outrec.FirstLeft || + (outrec.IsHole != outrec.FirstLeft->IsHole && + outrec.FirstLeft->Pts)) return; + + OutRec* orfl = outrec.FirstLeft; + while (orfl && ((orfl->IsHole == outrec.IsHole) || !orfl->Pts)) + orfl = orfl->FirstLeft; + outrec.FirstLeft = orfl; +} +//------------------------------------------------------------------------------ + +bool Clipper::ExecuteInternal() +{ + bool succeeded = true; + try { + Reset(); + if (m_CurrentLM == m_MinimaList.end()) return true; + cInt botY = PopScanbeam(); + do { + InsertLocalMinimaIntoAEL(botY); + ClearGhostJoins(); + ProcessHorizontals(false); + if (m_Scanbeam.empty()) break; + cInt topY = PopScanbeam(); + succeeded = ProcessIntersections(topY); + if (!succeeded) break; + ProcessEdgesAtTopOfScanbeam(topY); + botY = topY; + } while (!m_Scanbeam.empty() || m_CurrentLM != m_MinimaList.end()); + } + catch(...) + { + succeeded = false; + } + + if (succeeded) + { + //fix orientations ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *outRec = m_PolyOuts[i]; + if (!outRec->Pts || outRec->IsOpen) continue; + if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) + ReversePolyPtLinks(outRec->Pts); + } + + if (!m_Joins.empty()) JoinCommonEdges(); + + //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *outRec = m_PolyOuts[i]; + if (outRec->Pts && !outRec->IsOpen) + FixupOutPolygon(*outRec); + } + + if (m_StrictSimple) DoSimplePolygons(); + } + + ClearJoins(); + ClearGhostJoins(); + return succeeded; +} +//------------------------------------------------------------------------------ + +void Clipper::InsertScanbeam(const cInt Y) +{ + //if (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) return;// avoid duplicates. + m_Scanbeam.push(Y); +} +//------------------------------------------------------------------------------ + +cInt Clipper::PopScanbeam() +{ + const cInt Y = m_Scanbeam.top(); + m_Scanbeam.pop(); + while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. + return Y; +} +//------------------------------------------------------------------------------ + +void Clipper::DisposeAllOutRecs(){ + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + DisposeOutRec(i); + m_PolyOuts.clear(); +} +//------------------------------------------------------------------------------ + +void Clipper::DisposeOutRec(PolyOutList::size_type index) +{ + OutRec *outRec = m_PolyOuts[index]; + if (outRec->Pts) DisposeOutPts(outRec->Pts); + delete outRec; + m_PolyOuts[index] = nullptr; +} +//------------------------------------------------------------------------------ + +void Clipper::SetWindingCount(TEdge &edge) const +{ + TEdge *e = edge.PrevInAEL; + //find the edge of the same polytype that immediately preceeds 'edge' in AEL + while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL; + if (!e) + { + edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); + edge.WindCnt2 = 0; + e = m_ActiveEdges; //ie get ready to calc WindCnt2 + } + else if (edge.WindDelta == 0 && m_ClipType != ctUnion) + { + edge.WindCnt = 1; + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else if (IsEvenOddFillType(edge)) + { + //EvenOdd filling ... + if (edge.WindDelta == 0) + { + //are we inside a subj polygon ... + bool Inside = true; + TEdge *e2 = e->PrevInAEL; + while (e2) + { + if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0) + Inside = !Inside; + e2 = e2->PrevInAEL; + } + edge.WindCnt = (Inside ? 0 : 1); + } + else + { + edge.WindCnt = edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else + { + //nonZero, Positive or Negative filling ... + if (e->WindCnt * e->WindDelta < 0) + { + //prev edge is 'decreasing' WindCount (WC) toward zero + //so we're outside the previous polygon ... + if (Abs(e->WindCnt) > 1) + { + //outside prev poly but still inside another. + //when reversing direction of prev poly use the same WC + if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise continue to 'decrease' WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + else + //now outside all polys of same polytype so set own WC ... + edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); + } else + { + //prev edge is 'increasing' WindCount (WC) away from zero + //so we're inside the previous polygon ... + if (edge.WindDelta == 0) + edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1); + //if wind direction is reversing prev then use same WC + else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise add to WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + + //update WindCnt2 ... + if (IsEvenOddAltFillType(edge)) + { + //EvenOdd filling ... + while (e != &edge) + { + if (e->WindDelta != 0) + edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0); + e = e->NextInAEL; + } + } else + { + //nonZero, Positive or Negative filling ... + while ( e != &edge ) + { + edge.WindCnt2 += e->WindDelta; + e = e->NextInAEL; + } + } +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddFillType(const TEdge& edge) const +{ + if (edge.PolyTyp == ptSubject) + return m_SubjFillType == pftEvenOdd; else + return m_ClipFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const +{ + if (edge.PolyTyp == ptSubject) + return m_ClipFillType == pftEvenOdd; else + return m_SubjFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsContributing(const TEdge& edge) const +{ + PolyFillType pft, pft2; + if (edge.PolyTyp == ptSubject) + { + pft = m_SubjFillType; + pft2 = m_ClipFillType; + } else + { + pft = m_ClipFillType; + pft2 = m_SubjFillType; + } + + switch(pft) + { + case pftEvenOdd: + //return false if a subj line has been flagged as inside a subj polygon + if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; + break; + case pftNonZero: + if (Abs(edge.WindCnt) != 1) return false; + break; + case pftPositive: + if (edge.WindCnt != 1) return false; + break; + default: //pftNegative + if (edge.WindCnt != -1) return false; + } + + switch(m_ClipType) + { + case ctIntersection: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 != 0); + case pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + break; + case ctUnion: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + break; + case ctDifference: + if (edge.PolyTyp == ptSubject) + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 != 0); + case pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + break; + case ctXor: + if (edge.WindDelta == 0) //XOr always contributing unless open + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + return true; + break; + default: + return true; + } +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + OutPt* result; + TEdge *e, *prevE; + if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) + { + result = AddOutPt(e1, Pt); + e2->OutIdx = e1->OutIdx; + e1->Side = esLeft; + e2->Side = esRight; + e = e1; + if (e->PrevInAEL == e2) + prevE = e2->PrevInAEL; + else + prevE = e->PrevInAEL; + } else + { + result = AddOutPt(e2, Pt); + e1->OutIdx = e2->OutIdx; + e1->Side = esRight; + e2->Side = esLeft; + e = e2; + if (e->PrevInAEL == e1) + prevE = e1->PrevInAEL; + else + prevE = e->PrevInAEL; + } + + if (prevE && prevE->OutIdx >= 0 && + (TopX(*prevE, Pt.Y) == TopX(*e, Pt.Y)) && + SlopesEqual(*e, *prevE, m_UseFullRange) && + (e->WindDelta != 0) && (prevE->WindDelta != 0)) + { + OutPt* outPt = AddOutPt(prevE, Pt); + AddJoin(result, outPt, e->Top); + } + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + AddOutPt( e1, Pt ); + if (e2->WindDelta == 0) AddOutPt(e2, Pt); + if( e1->OutIdx == e2->OutIdx ) + { + e1->OutIdx = Unassigned; + e2->OutIdx = Unassigned; + } + else if (e1->OutIdx < e2->OutIdx) + AppendPolygon(e1, e2); + else + AppendPolygon(e2, e1); +} +//------------------------------------------------------------------------------ + +void Clipper::AddEdgeToSEL(TEdge *edge) +{ + //SEL pointers in PEdge are reused to build a list of horizontal edges. + //However, we don't need to worry about order with horizontal edge processing. + if( !m_SortedEdges ) + { + m_SortedEdges = edge; + edge->PrevInSEL = nullptr; + edge->NextInSEL = nullptr; + } + else + { + edge->NextInSEL = m_SortedEdges; + edge->PrevInSEL = nullptr; + m_SortedEdges->PrevInSEL = edge; + m_SortedEdges = edge; + } +} +//------------------------------------------------------------------------------ + +void Clipper::CopyAELToSEL() +{ + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + while ( e ) + { + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e = e->NextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint & OffPt) +{ + Join* j = new Join; + j->OutPt1 = op1; + j->OutPt2 = op2; + j->OffPt = OffPt; + m_Joins.emplace_back(j); +} +//------------------------------------------------------------------------------ + +void Clipper::ClearJoins() +{ + for (JoinList::size_type i = 0; i < m_Joins.size(); i++) + delete m_Joins[i]; + m_Joins.resize(0); +} +//------------------------------------------------------------------------------ + +void Clipper::ClearGhostJoins() +{ + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); i++) + delete m_GhostJoins[i]; + m_GhostJoins.resize(0); +} +//------------------------------------------------------------------------------ + +void Clipper::AddGhostJoin(OutPt *op, const IntPoint & OffPt) +{ + Join* j = new Join; + j->OutPt1 = op; + j->OutPt2 = nullptr; + j->OffPt = OffPt; + m_GhostJoins.emplace_back(j); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) +{ + while (m_CurrentLM != m_MinimaList.end() && (m_CurrentLM->Y == botY)) + { + TEdge* lb = m_CurrentLM->LeftBound; + TEdge* rb = m_CurrentLM->RightBound; + PopLocalMinima(); + OutPt *Op1 = nullptr; + if (!lb) + { + //nb: don't insert LB into either AEL or SEL + InsertEdgeIntoAEL(rb, nullptr); + SetWindingCount(*rb); + if (IsContributing(*rb)) + Op1 = AddOutPt(rb, rb->Bot); + } + else if (!rb) + { + InsertEdgeIntoAEL(lb, nullptr); + SetWindingCount(*lb); + if (IsContributing(*lb)) + Op1 = AddOutPt(lb, lb->Bot); + InsertScanbeam(lb->Top.Y); + } + else + { + InsertEdgeIntoAEL(lb, nullptr); + InsertEdgeIntoAEL(rb, lb); + SetWindingCount( *lb ); + rb->WindCnt = lb->WindCnt; + rb->WindCnt2 = lb->WindCnt2; + if (IsContributing(*lb)) + Op1 = AddLocalMinPoly(lb, rb, lb->Bot); + InsertScanbeam(lb->Top.Y); + } + + if (rb) + { + if(IsHorizontal(*rb)) AddEdgeToSEL(rb); + else InsertScanbeam( rb->Top.Y ); + } + + if (!lb || !rb) continue; + + //if any output polygons share an edge, they'll need joining later ... + if (Op1 && IsHorizontal(*rb) && + !m_GhostJoins.empty() && (rb->WindDelta != 0)) + { + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) + { + Join* jr = m_GhostJoins[i]; + //if the horizontal Rb and a 'ghost' horizontal overlap, then convert + //the 'ghost' join to a real join ready for later ... + if (HorzSegmentsOverlap(jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X)) + AddJoin(jr->OutPt1, Op1, jr->OffPt); + } + } + + if (lb->OutIdx >= 0 && lb->PrevInAEL && + lb->PrevInAEL->Curr.X == lb->Bot.X && + lb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(*lb->PrevInAEL, *lb, m_UseFullRange) && + (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0)) + { + OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot); + AddJoin(Op1, Op2, lb->Top); + } + + if(lb->NextInAEL != rb) + { + + if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(*rb->PrevInAEL, *rb, m_UseFullRange) && + (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0)) + { + OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot); + AddJoin(Op1, Op2, rb->Top); + } + + TEdge* e = lb->NextInAEL; + if (e) + { + while( e != rb ) + { + //nb: For calculating winding counts etc, IntersectEdges() assumes + //that param1 will be to the Right of param2 ABOVE the intersection ... + IntersectEdges(rb , e , lb->Curr); //order important here + e = e->NextInAEL; + } + } + } + + } +} +//------------------------------------------------------------------------------ + +void Clipper::DeleteFromAEL(TEdge *e) +{ + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if( !AelPrev && !AelNext && (e != m_ActiveEdges) ) return; //already deleted + if( AelPrev ) AelPrev->NextInAEL = AelNext; + else m_ActiveEdges = AelNext; + if( AelNext ) AelNext->PrevInAEL = AelPrev; + e->NextInAEL = nullptr; + e->PrevInAEL = nullptr; +} +//------------------------------------------------------------------------------ + +void Clipper::DeleteFromSEL(TEdge *e) +{ + TEdge* SelPrev = e->PrevInSEL; + TEdge* SelNext = e->NextInSEL; + if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted + if( SelPrev ) SelPrev->NextInSEL = SelNext; + else m_SortedEdges = SelNext; + if( SelNext ) SelNext->PrevInSEL = SelPrev; + e->NextInSEL = nullptr; + e->PrevInSEL = nullptr; +} +//------------------------------------------------------------------------------ + +#ifdef use_xyz +void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2) +{ + if (pt.Z != 0 || !m_ZFill) return; + else if (pt == e1.Bot) pt.Z = e1.Bot.Z; + else if (pt == e1.Top) pt.Z = e1.Top.Z; + else if (pt == e2.Bot) pt.Z = e2.Bot.Z; + else if (pt == e2.Top) pt.Z = e2.Top.Z; + else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); +} +//------------------------------------------------------------------------------ +#endif + +void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + bool e1Contributing = ( e1->OutIdx >= 0 ); + bool e2Contributing = ( e2->OutIdx >= 0 ); + +#ifdef use_xyz + SetZ(Pt, *e1, *e2); +#endif + +#ifdef use_lines + //if either edge is on an OPEN path ... + if (e1->WindDelta == 0 || e2->WindDelta == 0) + { + //ignore subject-subject open path intersections UNLESS they + //are both open paths, AND they are both 'contributing maximas' ... + if (e1->WindDelta == 0 && e2->WindDelta == 0) return; + + //if intersecting a subj line with a subj poly ... + else if (e1->PolyTyp == e2->PolyTyp && + e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion) + { + if (e1->WindDelta == 0) + { + if (e2Contributing) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + } + else + { + if (e1Contributing) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + } + else if (e1->PolyTyp != e2->PolyTyp) + { + //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ... + if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 && + (m_ClipType != ctUnion || e2->WindCnt2 == 0)) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) && + (m_ClipType != ctUnion || e1->WindCnt2 == 0)) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + return; + } +#endif + + //update winding counts... + //assumes that e1 will be to the Right of e2 ABOVE the intersection + if ( e1->PolyTyp == e2->PolyTyp ) + { + if ( IsEvenOddFillType( *e1) ) + { + int oldE1WindCnt = e1->WindCnt; + e1->WindCnt = e2->WindCnt; + e2->WindCnt = oldE1WindCnt; + } else + { + if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt; + else e1->WindCnt += e2->WindDelta; + if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt; + else e2->WindCnt -= e1->WindDelta; + } + } else + { + if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta; + else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0; + if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta; + else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0; + } + + PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; + if (e1->PolyTyp == ptSubject) + { + e1FillType = m_SubjFillType; + e1FillType2 = m_ClipFillType; + } else + { + e1FillType = m_ClipFillType; + e1FillType2 = m_SubjFillType; + } + if (e2->PolyTyp == ptSubject) + { + e2FillType = m_SubjFillType; + e2FillType2 = m_ClipFillType; + } else + { + e2FillType = m_ClipFillType; + e2FillType2 = m_SubjFillType; + } + + cInt e1Wc, e2Wc; + switch (e1FillType) + { + case pftPositive: e1Wc = e1->WindCnt; break; + case pftNegative: e1Wc = -e1->WindCnt; break; + default: e1Wc = Abs(e1->WindCnt); + } + switch(e2FillType) + { + case pftPositive: e2Wc = e2->WindCnt; break; + case pftNegative: e2Wc = -e2->WindCnt; break; + default: e2Wc = Abs(e2->WindCnt); + } + + if ( e1Contributing && e2Contributing ) + { + if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || + (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) + { + AddLocalMaxPoly(e1, e2, Pt); + } + else + { + AddOutPt(e1, Pt); + AddOutPt(e2, Pt); + SwapSides( *e1 , *e2 ); + SwapPolyIndexes( *e1 , *e2 ); + } + } + else if ( e1Contributing ) + { + if (e2Wc == 0 || e2Wc == 1) + { + AddOutPt(e1, Pt); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); + } + } + else if ( e2Contributing ) + { + if (e1Wc == 0 || e1Wc == 1) + { + AddOutPt(e2, Pt); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); + } + } + else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) + { + //neither edge is currently contributing ... + + cInt e1Wc2, e2Wc2; + switch (e1FillType2) + { + case pftPositive: e1Wc2 = e1->WindCnt2; break; + case pftNegative : e1Wc2 = -e1->WindCnt2; break; + default: e1Wc2 = Abs(e1->WindCnt2); + } + switch (e2FillType2) + { + case pftPositive: e2Wc2 = e2->WindCnt2; break; + case pftNegative: e2Wc2 = -e2->WindCnt2; break; + default: e2Wc2 = Abs(e2->WindCnt2); + } + + if (e1->PolyTyp != e2->PolyTyp) + { + AddLocalMinPoly(e1, e2, Pt); + } + else if (e1Wc == 1 && e2Wc == 1) + switch( m_ClipType ) { + case ctIntersection: + if (e1Wc2 > 0 && e2Wc2 > 0) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctUnion: + if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctDifference: + if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || + ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctXor: + AddLocalMinPoly(e1, e2, Pt); + } + else + SwapSides( *e1, *e2 ); + } +} +//------------------------------------------------------------------------------ + +void Clipper::SetHoleState(TEdge *e, OutRec *outrec) const +{ + bool IsHole = false; + TEdge *e2 = e->PrevInAEL; + while (e2) + { + if (e2->OutIdx >= 0 && e2->WindDelta != 0) + { + IsHole = !IsHole; + if (! outrec->FirstLeft) + outrec->FirstLeft = m_PolyOuts[e2->OutIdx]; + } + e2 = e2->PrevInAEL; + } + if (IsHole) outrec->IsHole = true; +} +//------------------------------------------------------------------------------ + +OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) +{ + //work out which polygon fragment has the correct hole state ... + if (!outRec1->BottomPt) + outRec1->BottomPt = GetBottomPt(outRec1->Pts); + if (!outRec2->BottomPt) + outRec2->BottomPt = GetBottomPt(outRec2->Pts); + OutPt *OutPt1 = outRec1->BottomPt; + OutPt *OutPt2 = outRec2->BottomPt; + if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1; + else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2; + else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1; + else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2; + else if (OutPt1->Next == OutPt1) return outRec2; + else if (OutPt2->Next == OutPt2) return outRec1; + else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1; + else return outRec2; +} +//------------------------------------------------------------------------------ + +bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2) +{ + do + { + outRec1 = outRec1->FirstLeft; + if (outRec1 == outRec2) return true; + } while (outRec1); + return false; +} +//------------------------------------------------------------------------------ + +OutRec* Clipper::GetOutRec(int Idx) +{ + OutRec* outrec = m_PolyOuts[Idx]; + while (outrec != m_PolyOuts[outrec->Idx]) + outrec = m_PolyOuts[outrec->Idx]; + return outrec; +} +//------------------------------------------------------------------------------ + +void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const +{ + //get the start and ends of both output polygons ... + OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; + OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; + + OutRec *holeStateRec; + if (Param1RightOfParam2(outRec1, outRec2)) + holeStateRec = outRec2; + else if (Param1RightOfParam2(outRec2, outRec1)) + holeStateRec = outRec1; + else + holeStateRec = GetLowermostRec(outRec1, outRec2); + + //get the start and ends of both output polygons and + //join e2 poly onto e1 poly and delete pointers to e2 ... + + OutPt* p1_lft = outRec1->Pts; + OutPt* p1_rt = p1_lft->Prev; + OutPt* p2_lft = outRec2->Pts; + OutPt* p2_rt = p2_lft->Prev; + + EdgeSide Side; + //join e2 poly onto e1 poly and delete pointers to e2 ... + if( e1->Side == esLeft ) + { + if( e2->Side == esLeft ) + { + //z y x a b c + ReversePolyPtLinks(p2_lft); + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + outRec1->Pts = p2_rt; + } else + { + //x y z a b c + p2_rt->Next = p1_lft; + p1_lft->Prev = p2_rt; + p2_lft->Prev = p1_rt; + p1_rt->Next = p2_lft; + outRec1->Pts = p2_lft; + } + Side = esLeft; + } else + { + if( e2->Side == esRight ) + { + //a b c z y x + ReversePolyPtLinks(p2_lft); + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + } else + { + //a b c x y z + p1_rt->Next = p2_lft; + p2_lft->Prev = p1_rt; + p1_lft->Prev = p2_rt; + p2_rt->Next = p1_lft; + } + Side = esRight; + } + + outRec1->BottomPt = nullptr; + if (holeStateRec == outRec2) + { + if (outRec2->FirstLeft != outRec1) + outRec1->FirstLeft = outRec2->FirstLeft; + outRec1->IsHole = outRec2->IsHole; + } + outRec2->Pts = nullptr; + outRec2->BottomPt = nullptr; + outRec2->FirstLeft = outRec1; + + int OKIdx = e1->OutIdx; + int ObsoleteIdx = e2->OutIdx; + + e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly + e2->OutIdx = Unassigned; + + TEdge* e = m_ActiveEdges; + while( e ) + { + if( e->OutIdx == ObsoleteIdx ) + { + e->OutIdx = OKIdx; + e->Side = Side; + break; + } + e = e->NextInAEL; + } + + outRec2->Idx = outRec1->Idx; +} +//------------------------------------------------------------------------------ + +OutRec* Clipper::CreateOutRec() +{ + OutRec* result = new OutRec; + result->IsHole = false; + result->IsOpen = false; + result->FirstLeft = nullptr; + result->Pts = nullptr; + result->BottomPt = nullptr; + result->PolyNd = nullptr; + m_PolyOuts.emplace_back(result); + result->Idx = (int)m_PolyOuts.size()-1; + return result; +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) +{ + bool ToFront = (e->Side == esLeft); + if( e->OutIdx < 0 ) + { + OutRec *outRec = CreateOutRec(); + outRec->IsOpen = (e->WindDelta == 0); + OutPt* newOp = new OutPt; + outRec->Pts = newOp; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = newOp; + newOp->Prev = newOp; + if (!outRec->IsOpen) + SetHoleState(e, outRec); + e->OutIdx = outRec->Idx; + return newOp; + } else + { + OutRec *outRec = m_PolyOuts[e->OutIdx]; + //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' + OutPt* op = outRec->Pts; + + if (ToFront && (pt == op->Pt)) return op; + else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; + + OutPt* newOp = new OutPt; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = op; + newOp->Prev = op->Prev; + newOp->Prev->Next = newOp; + op->Prev = newOp; + if (ToFront) outRec->Pts = newOp; + return newOp; + } +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessHorizontals(bool IsTopOfScanbeam) +{ + TEdge* horzEdge = m_SortedEdges; + while(horzEdge) + { + DeleteFromSEL(horzEdge); + ProcessHorizontal(horzEdge, IsTopOfScanbeam); + horzEdge = m_SortedEdges; + } +} +//------------------------------------------------------------------------------ + +inline bool IsMinima(TEdge *e) +{ + return e && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e); +} +//------------------------------------------------------------------------------ + +inline bool IsMaxima(TEdge *e, const cInt Y) +{ + return e && e->Top.Y == Y && !e->NextInLML; +} +//------------------------------------------------------------------------------ + +inline bool IsIntermediate(TEdge *e, const cInt Y) +{ + return e->Top.Y == Y && e->NextInLML; +} +//------------------------------------------------------------------------------ + +TEdge *GetMaximaPair(TEdge *e) +{ + TEdge* result = nullptr; + if ((e->Next->Top == e->Top) && !e->Next->NextInLML) + result = e->Next; + else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML) + result = e->Prev; + + if (result && (result->OutIdx == Skip || + //result is false if both NextInAEL & PrevInAEL are nil & not horizontal ... + (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) + return nullptr; + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) +{ + //check that one or other edge hasn't already been removed from AEL ... + if (Edge1->NextInAEL == Edge1->PrevInAEL || + Edge2->NextInAEL == Edge2->PrevInAEL) return; + + if( Edge1->NextInAEL == Edge2 ) + { + TEdge* Next = Edge2->NextInAEL; + if( Next ) Next->PrevInAEL = Edge1; + TEdge* Prev = Edge1->PrevInAEL; + if( Prev ) Prev->NextInAEL = Edge2; + Edge2->PrevInAEL = Prev; + Edge2->NextInAEL = Edge1; + Edge1->PrevInAEL = Edge2; + Edge1->NextInAEL = Next; + } + else if( Edge2->NextInAEL == Edge1 ) + { + TEdge* Next = Edge1->NextInAEL; + if( Next ) Next->PrevInAEL = Edge2; + TEdge* Prev = Edge2->PrevInAEL; + if( Prev ) Prev->NextInAEL = Edge1; + Edge1->PrevInAEL = Prev; + Edge1->NextInAEL = Edge2; + Edge2->PrevInAEL = Edge1; + Edge2->NextInAEL = Next; + } + else + { + TEdge* Next = Edge1->NextInAEL; + TEdge* Prev = Edge1->PrevInAEL; + Edge1->NextInAEL = Edge2->NextInAEL; + if( Edge1->NextInAEL ) Edge1->NextInAEL->PrevInAEL = Edge1; + Edge1->PrevInAEL = Edge2->PrevInAEL; + if( Edge1->PrevInAEL ) Edge1->PrevInAEL->NextInAEL = Edge1; + Edge2->NextInAEL = Next; + if( Edge2->NextInAEL ) Edge2->NextInAEL->PrevInAEL = Edge2; + Edge2->PrevInAEL = Prev; + if( Edge2->PrevInAEL ) Edge2->PrevInAEL->NextInAEL = Edge2; + } + + if( !Edge1->PrevInAEL ) m_ActiveEdges = Edge1; + else if( !Edge2->PrevInAEL ) m_ActiveEdges = Edge2; +} +//------------------------------------------------------------------------------ + +void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2) +{ + if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) ) return; + if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) ) return; + + if( Edge1->NextInSEL == Edge2 ) + { + TEdge* Next = Edge2->NextInSEL; + if( Next ) Next->PrevInSEL = Edge1; + TEdge* Prev = Edge1->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge2; + Edge2->PrevInSEL = Prev; + Edge2->NextInSEL = Edge1; + Edge1->PrevInSEL = Edge2; + Edge1->NextInSEL = Next; + } + else if( Edge2->NextInSEL == Edge1 ) + { + TEdge* Next = Edge1->NextInSEL; + if( Next ) Next->PrevInSEL = Edge2; + TEdge* Prev = Edge2->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge1; + Edge1->PrevInSEL = Prev; + Edge1->NextInSEL = Edge2; + Edge2->PrevInSEL = Edge1; + Edge2->NextInSEL = Next; + } + else + { + TEdge* Next = Edge1->NextInSEL; + TEdge* Prev = Edge1->PrevInSEL; + Edge1->NextInSEL = Edge2->NextInSEL; + if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1; + Edge1->PrevInSEL = Edge2->PrevInSEL; + if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1; + Edge2->NextInSEL = Next; + if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2; + Edge2->PrevInSEL = Prev; + if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2; + } + + if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1; + else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2; +} +//------------------------------------------------------------------------------ + +TEdge* GetNextInAEL(TEdge *e, Direction dir) +{ + return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL; +} +//------------------------------------------------------------------------------ + +void GetHorzDirection(const TEdge &HorzEdge, Direction& Dir, cInt& Left, cInt& Right) +{ + if (HorzEdge.Bot.X < HorzEdge.Top.X) + { + Left = HorzEdge.Bot.X; + Right = HorzEdge.Top.X; + Dir = dLeftToRight; + } else + { + Left = HorzEdge.Top.X; + Right = HorzEdge.Bot.X; + Dir = dRightToLeft; + } +} +//------------------------------------------------------------------------ + +/******************************************************************************* +* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or * +* Bottom of a scanbeam) are processed as if layered. The order in which HEs * +* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] * +* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), * +* and with other non-horizontal edges [*]. Once these intersections are * +* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into * +* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * +*******************************************************************************/ + +void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam) +{ + Direction dir; + cInt horzLeft, horzRight; + + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + + TEdge* eLastHorz = horzEdge, *eMaxPair = nullptr; + while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML)) + eLastHorz = eLastHorz->NextInLML; + if (!eLastHorz->NextInLML) + eMaxPair = GetMaximaPair(eLastHorz); + + for (;;) + { + bool IsLastHorz = (horzEdge == eLastHorz); + TEdge* e = GetNextInAEL(horzEdge, dir); + while(e) + { + //Break if we've got to the end of an intermediate horizontal edge ... + //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. + if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && + e->Dx < horzEdge->NextInLML->Dx) break; + + TEdge* eNext = GetNextInAEL(e, dir); //saves eNext for later + + if ((dir == dLeftToRight && e->Curr.X <= horzRight) || + (dir == dRightToLeft && e->Curr.X >= horzLeft)) + { + //so far we're still in range of the horizontal Edge but make sure + //we're at the last of consec. horizontals when matching with eMaxPair + if(e == eMaxPair && IsLastHorz) + { + + if (horzEdge->OutIdx >= 0) + { + OutPt* op1 = AddOutPt(horzEdge, horzEdge->Top); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = AddOutPt(eNextHorz, eNextHorz->Bot); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Bot); + AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top); + } + DeleteFromAEL(horzEdge); + DeleteFromAEL(eMaxPair); + return; + } + else if(dir == dLeftToRight) + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges(horzEdge, e, Pt); + } + else + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges( e, horzEdge, Pt); + } + SwapPositionsInAEL( horzEdge, e ); + } + else if( (dir == dLeftToRight && e->Curr.X >= horzRight) || + (dir == dRightToLeft && e->Curr.X <= horzLeft) ) break; + e = eNext; + } //end while + + if (horzEdge->NextInLML && IsHorizontal(*horzEdge->NextInLML)) + { + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + } else + break; + } //end for (;;) + + if(horzEdge->NextInLML) + { + if(horzEdge->OutIdx >= 0) + { + OutPt* op1 = AddOutPt( horzEdge, horzEdge->Top); + if (isTopOfScanbeam) AddGhostJoin(op1, horzEdge->Bot); + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->WindDelta == 0) return; + //nb: HorzEdge is no longer horizontal here + TEdge* ePrev = horzEdge->PrevInAEL; + TEdge* eNext = horzEdge->NextInAEL; + if (ePrev && ePrev->Curr.X == horzEdge->Bot.X && + ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 && + (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(*horzEdge, *ePrev, m_UseFullRange))) + { + OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot); + AddJoin(op1, op2, horzEdge->Top); + } + else if (eNext && eNext->Curr.X == horzEdge->Bot.X && + eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(*horzEdge, *eNext, m_UseFullRange)) + { + OutPt* op2 = AddOutPt(eNext, horzEdge->Bot); + AddJoin(op1, op2, horzEdge->Top); + } + } + else + UpdateEdgeIntoAEL(horzEdge); + } + else + { + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top); + DeleteFromAEL(horzEdge); + } +} +//------------------------------------------------------------------------------ + +void Clipper::UpdateEdgeIntoAEL(TEdge *&e) +{ + if( !e->NextInLML ) throw + clipperException("UpdateEdgeIntoAEL: invalid call"); + + e->NextInLML->OutIdx = e->OutIdx; + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (AelPrev) AelPrev->NextInAEL = e->NextInLML; + else m_ActiveEdges = e->NextInLML; + if (AelNext) AelNext->PrevInAEL = e->NextInLML; + e->NextInLML->Side = e->Side; + e->NextInLML->WindDelta = e->WindDelta; + e->NextInLML->WindCnt = e->WindCnt; + e->NextInLML->WindCnt2 = e->WindCnt2; + e = e->NextInLML; + e->Curr = e->Bot; + e->PrevInAEL = AelPrev; + e->NextInAEL = AelNext; + if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y); +} +//------------------------------------------------------------------------------ + +bool Clipper::ProcessIntersections(const cInt topY) +{ + if( !m_ActiveEdges ) return true; + try { + BuildIntersectList(topY); + size_t IlSize = m_IntersectList.size(); + if (IlSize == 0) return true; + if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList(); + else return false; + } + catch(...) + { + m_SortedEdges = nullptr; + DisposeIntersectNodes(); + throw clipperException("ProcessIntersections error"); + } + m_SortedEdges = nullptr; + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DisposeIntersectNodes() +{ + for (size_t i = 0; i < m_IntersectList.size(); ++i ) + delete m_IntersectList[i]; + m_IntersectList.clear(); +} +//------------------------------------------------------------------------------ + +void Clipper::BuildIntersectList(const cInt topY) +{ + if ( !m_ActiveEdges ) return; + + //prepare for sorting ... + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + while( e ) + { + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e->Curr.X = TopX( *e, topY ); + e = e->NextInAEL; + } + + //bubblesort ... + bool isModified; + do + { + isModified = false; + e = m_SortedEdges; + while( e->NextInSEL ) + { + TEdge *eNext = e->NextInSEL; + IntPoint Pt; + if(e->Curr.X > eNext->Curr.X) + { + IntersectPoint(*e, *eNext, Pt); + IntersectNode * newNode = new IntersectNode; + newNode->Edge1 = e; + newNode->Edge2 = eNext; + newNode->Pt = Pt; + m_IntersectList.emplace_back(newNode); + + SwapPositionsInSEL(e, eNext); + isModified = true; + } + else + e = eNext; + } + if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = nullptr; + else break; + } + while ( isModified ); + m_SortedEdges = nullptr; //important +} +//------------------------------------------------------------------------------ + + +void Clipper::ProcessIntersectList() +{ + for (size_t i = 0; i < m_IntersectList.size(); ++i) + { + IntersectNode* iNode = m_IntersectList[i]; + { + IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt); + SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 ); + } + delete iNode; + } + m_IntersectList.clear(); +} +//------------------------------------------------------------------------------ + +bool IntersectListSort(IntersectNode* node1, IntersectNode* node2) +{ + return node2->Pt.Y < node1->Pt.Y; +} +//------------------------------------------------------------------------------ + +inline bool EdgesAdjacent(const IntersectNode &inode) +{ + return (inode.Edge1->NextInSEL == inode.Edge2) || + (inode.Edge1->PrevInSEL == inode.Edge2); +} +//------------------------------------------------------------------------------ + +bool Clipper::FixupIntersectionOrder() +{ + //pre-condition: intersections are sorted Bottom-most first. + //Now it's crucial that intersections are made only between adjacent edges, + //so to ensure this the order of intersections may need adjusting ... + CopyAELToSEL(); + std::sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort); + size_t cnt = m_IntersectList.size(); + for (size_t i = 0; i < cnt; ++i) + { + if (!EdgesAdjacent(*m_IntersectList[i])) + { + size_t j = i + 1; + while (j < cnt && !EdgesAdjacent(*m_IntersectList[j])) j++; + if (j == cnt) return false; + std::swap(m_IntersectList[i], m_IntersectList[j]); + } + SwapPositionsInSEL(m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2); + } + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DoMaxima(TEdge *e) +{ + TEdge* eMaxPair = GetMaximaPair(e); + if (!eMaxPair) + { + if (e->OutIdx >= 0) + AddOutPt(e, e->Top); + DeleteFromAEL(e); + return; + } + + TEdge* eNext = e->NextInAEL; + while(eNext && eNext != eMaxPair) + { + IntersectEdges(e, eNext, e->Top); + SwapPositionsInAEL(e, eNext); + eNext = e->NextInAEL; + } + + if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned) + { + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } + else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) + { + if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top); + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } +#ifdef use_lines + else if (e->WindDelta == 0) + { + if (e->OutIdx >= 0) + { + AddOutPt(e, e->Top); + e->OutIdx = Unassigned; + } + DeleteFromAEL(e); + + if (eMaxPair->OutIdx >= 0) + { + AddOutPt(eMaxPair, e->Top); + eMaxPair->OutIdx = Unassigned; + } + DeleteFromAEL(eMaxPair); + } +#endif + else throw clipperException("DoMaxima error"); +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) +{ + TEdge* e = m_ActiveEdges; + while( e ) + { + //1. process maxima, treating them as if they're 'bent' horizontal edges, + // but exclude maxima with horizontal edges. nb: e can't be a horizontal. + bool IsMaximaEdge = IsMaxima(e, topY); + + if(IsMaximaEdge) + { + TEdge* eMaxPair = GetMaximaPair(e); + IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); + } + + if(IsMaximaEdge) + { + TEdge* ePrev = e->PrevInAEL; + DoMaxima(e); + if( !ePrev ) e = m_ActiveEdges; + else e = ePrev->NextInAEL; + } + else + { + //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... + if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML)) + { + UpdateEdgeIntoAEL(e); + if (e->OutIdx >= 0) + AddOutPt(e, e->Bot); + AddEdgeToSEL(e); + } + else + { + e->Curr.X = TopX( *e, topY ); + e->Curr.Y = topY; + } + + if (m_StrictSimple) + { + TEdge* ePrev = e->PrevInAEL; + if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) && + (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0)) + { + IntPoint pt = e->Curr; +#ifdef use_xyz + SetZ(pt, *ePrev, *e); +#endif + OutPt* op = AddOutPt(ePrev, pt); + OutPt* op2 = AddOutPt(e, pt); + AddJoin(op, op2, pt); //StrictlySimple (type-3) join + } + } + + e = e->NextInAEL; + } + } + + //3. Process horizontals at the Top of the scanbeam ... + ProcessHorizontals(true); + + //4. Promote intermediate vertices ... + e = m_ActiveEdges; + while(e) + { + if(IsIntermediate(e, topY)) + { + OutPt* op = nullptr; + if( e->OutIdx >= 0 ) + op = AddOutPt(e, e->Top); + UpdateEdgeIntoAEL(e); + + //if output polygons share an edge, they'll need joining later ... + TEdge* ePrev = e->PrevInAEL; + TEdge* eNext = e->NextInAEL; + if (ePrev && ePrev->Curr.X == e->Bot.X && + ePrev->Curr.Y == e->Bot.Y && op && + ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(*e, *ePrev, m_UseFullRange) && + (e->WindDelta != 0) && (ePrev->WindDelta != 0)) + { + OutPt* op2 = AddOutPt(ePrev, e->Bot); + AddJoin(op, op2, e->Top); + } + else if (eNext && eNext->Curr.X == e->Bot.X && + eNext->Curr.Y == e->Bot.Y && op && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(*e, *eNext, m_UseFullRange) && + (e->WindDelta != 0) && (eNext->WindDelta != 0)) + { + OutPt* op2 = AddOutPt(eNext, e->Bot); + AddJoin(op, op2, e->Top); + } + } + e = e->NextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::FixupOutPolygon(OutRec &outrec) +{ + //FixupOutPolygon() - removes duplicate points and simplifies consecutive + //parallel edges by removing the middle vertex. + OutPt *lastOK = nullptr; + outrec.BottomPt = nullptr; + OutPt *pp = outrec.Pts; + + for (;;) + { + if (pp->Prev == pp || pp->Prev == pp->Next ) + { + DisposeOutPts(pp); + outrec.Pts = nullptr; + return; + } + + //test for duplicate points and collinear edges ... + if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || + (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && + (!m_PreserveCollinear || + !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) + { + lastOK = nullptr; + OutPt *tmp = pp; + pp->Prev->Next = pp->Next; + pp->Next->Prev = pp->Prev; + pp = pp->Prev; + delete tmp; + } + else if (pp == lastOK) break; + else + { + if (!lastOK) lastOK = pp; + pp = pp->Next; + } + } + outrec.Pts = pp; +} +//------------------------------------------------------------------------------ + +int PointCount(OutPt *Pts) +{ + if (!Pts) return 0; + int result = 0; + OutPt* p = Pts; + do + { + result++; + p = p->Next; + } + while (p != Pts); + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult(Paths &polys) +{ + polys.reserve(m_PolyOuts.size()); + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + if (!m_PolyOuts[i]->Pts) continue; + Path pg; + OutPt* p = m_PolyOuts[i]->Pts->Prev; + const int cnt = PointCount(p); + if (cnt < 2) continue; + pg.reserve(cnt); + for (int j = 0; j < cnt; ++j) + { + pg.emplace_back(p->Pt); + p = p->Prev; + } + polys.emplace_back(pg); + } +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult2(PolyTree& polytree) +{ + polytree.Clear(); + polytree.AllNodes.reserve(m_PolyOuts.size()); + //add each output polygon/contour to polytree ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) + { + OutRec* outRec = m_PolyOuts[i]; + int cnt = PointCount(outRec->Pts); + if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue; + FixHoleLinkage(*outRec); + PolyNode* pn = new PolyNode(); + //nb: polytree takes ownership of all the PolyNodes + polytree.AllNodes.emplace_back(pn); + outRec->PolyNd = pn; + pn->Parent = nullptr; + pn->Index = 0; + pn->Contour.reserve(cnt); + OutPt *op = outRec->Pts->Prev; + for (int j = 0; j < cnt; j++) + { + pn->Contour.emplace_back(op->Pt); + op = op->Prev; + } + } + + //fixup PolyNode links etc ... + polytree.Childs.reserve(m_PolyOuts.size()); + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) + { + OutRec* outRec = m_PolyOuts[i]; + if (!outRec->PolyNd) continue; + if (outRec->IsOpen) + { + outRec->PolyNd->m_IsOpen = true; + polytree.AddChild(*outRec->PolyNd); + } + else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) + outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); + else + polytree.AddChild(*outRec->PolyNd); + } +} +//------------------------------------------------------------------------------ + +void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) +{ + //just swap the contents (because fIntersectNodes is a single-linked-list) + IntersectNode inode = int1; //gets a copy of Int1 + int1.Edge1 = int2.Edge1; + int1.Edge2 = int2.Edge2; + int1.Pt = int2.Pt; + int2.Edge1 = inode.Edge1; + int2.Edge2 = inode.Edge2; + int2.Pt = inode.Pt; +} +//------------------------------------------------------------------------------ + +inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) +{ + if (e2.Curr.X == e1.Curr.X) + { + if (e2.Top.Y > e1.Top.Y) + return e2.Top.X < TopX(e1, e2.Top.Y); + else return e1.Top.X > TopX(e2, e1.Top.Y); + } + else return e2.Curr.X < e1.Curr.X; +} +//------------------------------------------------------------------------------ + +bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, + cInt& Left, cInt& Right) +{ + if (a1 < a2) + { + if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);} + else {Left = std::max(a1,b2); Right = std::min(a2,b1);} + } + else + { + if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);} + else {Left = std::max(a2,b2); Right = std::min(a1,b1);} + } + return Left < Right; +} +//------------------------------------------------------------------------------ + +inline void UpdateOutPtIdxs(const OutRec &outrec) +{ + OutPt* op = outrec.Pts; + do + { + op->Idx = outrec.Idx; + op = op->Prev; + } + while(op != outrec.Pts); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge) +{ + if(!m_ActiveEdges) + { + edge->PrevInAEL = nullptr; + edge->NextInAEL = nullptr; + m_ActiveEdges = edge; + } + else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge)) + { + edge->PrevInAEL = nullptr; + edge->NextInAEL = m_ActiveEdges; + m_ActiveEdges->PrevInAEL = edge; + m_ActiveEdges = edge; + } + else + { + if(!startEdge) startEdge = m_ActiveEdges; + while(startEdge->NextInAEL && + !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge)) + startEdge = startEdge->NextInAEL; + edge->NextInAEL = startEdge->NextInAEL; + if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge; + edge->PrevInAEL = startEdge; + startEdge->NextInAEL = edge; + } +} +//---------------------------------------------------------------------- + +OutPt* DupOutPt(OutPt* outPt, bool InsertAfter) +{ + OutPt* result = new OutPt; + result->Pt = outPt->Pt; + result->Idx = outPt->Idx; + if (InsertAfter) + { + result->Next = outPt->Next; + result->Prev = outPt; + outPt->Next->Prev = result; + outPt->Next = result; + } + else + { + result->Prev = outPt->Prev; + result->Next = outPt; + outPt->Prev->Next = result; + outPt->Prev = result; + } + return result; +} +//------------------------------------------------------------------------------ + +bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, + const IntPoint & Pt, bool DiscardLeft) +{ + Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight); + Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight); + if (Dir1 == Dir2) return false; + + //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we + //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) + //So, to facilitate this while inserting Op1b and Op2b ... + //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, + //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) + if (Dir1 == dLeftToRight) + { + while (op1->Next->Pt.X <= Pt.X && + op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = DupOutPt(op1, !DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = DupOutPt(op1, !DiscardLeft); + } + } + else + { + while (op1->Next->Pt.X >= Pt.X && + op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = DupOutPt(op1, DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = DupOutPt(op1, DiscardLeft); + } + } + + if (Dir2 == dLeftToRight) + { + while (op2->Next->Pt.X <= Pt.X && + op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = DupOutPt(op2, !DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = DupOutPt(op2, !DiscardLeft); + }; + } else + { + while (op2->Next->Pt.X >= Pt.X && + op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = DupOutPt(op2, DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = DupOutPt(op2, DiscardLeft); + }; + }; + + if ((Dir1 == dLeftToRight) == DiscardLeft) + { + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + } + else + { + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + } + return true; +} +//------------------------------------------------------------------------------ + +bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) +{ + OutPt *op1 = j->OutPt1, *op1b; + OutPt *op2 = j->OutPt2, *op2b; + + //There are 3 kinds of joins for output polygons ... + //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are a vertices anywhere + //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). + //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same + //location at the Bottom of the overlapping segment (& Join.OffPt is above). + //3. StrictSimple joins where edges touch but are not collinear and where + //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. + bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y); + + if (isHorizontal && (j->OffPt == j->OutPt1->Pt) && + (j->OffPt == j->OutPt2->Pt)) + { + //Strictly Simple join ... + if (outRec1 != outRec2) return false; + op1b = j->OutPt1->Next; + while (op1b != op1 && (op1b->Pt == j->OffPt)) + op1b = op1b->Next; + bool reverse1 = (op1b->Pt.Y > j->OffPt.Y); + op2b = j->OutPt2->Next; + while (op2b != op2 && (op2b->Pt == j->OffPt)) + op2b = op2b->Next; + bool reverse2 = (op2b->Pt.Y > j->OffPt.Y); + if (reverse1 == reverse2) return false; + if (reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } + } + else if (isHorizontal) + { + //treat horizontal joins differently to non-horizontal joins since with + //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt + //may be anywhere along the horizontal edge. + op1b = op1; + while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2) + op1 = op1->Prev; + while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2) + op1b = op1b->Next; + if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon' + + op2b = op2; + while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b) + op2 = op2->Prev; + while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1) + op2b = op2b->Next; + if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon' + + cInt Left, Right; + //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges + if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right)) + return false; + + //DiscardLeftSide: when overlapping edges are joined, a spike will created + //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up + //on the discard Side as either may still be needed for other joins ... + IntPoint Pt; + bool DiscardLeftSide; + if (op1->Pt.X >= Left && op1->Pt.X <= Right) + { + Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X); + } + else if (op2->Pt.X >= Left&& op2->Pt.X <= Right) + { + Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X); + } + else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right) + { + Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X; + } + else + { + Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X); + } + j->OutPt1 = op1; j->OutPt2 = op2; + return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide); + } else + { + //nb: For non-horizontal joins ... + // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y + // 2. Jr.OutPt1.Pt > Jr.OffPt.Y + + //make sure the polygons are correctly oriented ... + op1b = op1->Next; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next; + bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse1) + { + op1b = op1->Prev; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev; + if ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false; + }; + op2b = op2->Next; + while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next; + bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse2) + { + op2b = op2->Prev; + while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev; + if ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false; + } + + if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || + ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false; + + if (Reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } + } +} +//---------------------------------------------------------------------- + +static OutRec* ParseFirstLeft(OutRec* FirstLeft) +{ + while (FirstLeft && !FirstLeft->Pts) + FirstLeft = FirstLeft->FirstLeft; + return FirstLeft; +} +//------------------------------------------------------------------------------ + +void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const +{ + //tests if NewOutRec contains the polygon before reassigning FirstLeft + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + if (!outRec->Pts || !outRec->FirstLeft) continue; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (firstLeft == OldOutRec) + { + if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) + outRec->FirstLeft = NewOutRec; + } + } +} +//---------------------------------------------------------------------- + +void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const +{ + //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec; + } +} +//---------------------------------------------------------------------- + +void Clipper::JoinCommonEdges() +{ + for (JoinList::size_type i = 0; i < m_Joins.size(); i++) + { + Join* join = m_Joins[i]; + + OutRec *outRec1 = GetOutRec(join->OutPt1->Idx); + OutRec *outRec2 = GetOutRec(join->OutPt2->Idx); + + if (!outRec1->Pts || !outRec2->Pts) continue; + + //get the polygon fragment with the correct hole state (FirstLeft) + //before calling JoinPoints() ... + OutRec *holeStateRec; + if (outRec1 == outRec2) holeStateRec = outRec1; + else if (Param1RightOfParam2(outRec1, outRec2)) holeStateRec = outRec2; + else if (Param1RightOfParam2(outRec2, outRec1)) holeStateRec = outRec1; + else holeStateRec = GetLowermostRec(outRec1, outRec2); + + if (!JoinPoints(join, outRec1, outRec2)) continue; + + if (outRec1 == outRec2) + { + //instead of joining two polygons, we've just created a new one by + //splitting one polygon into two. + outRec1->Pts = join->OutPt1; + outRec1->BottomPt = nullptr; + outRec2 = CreateOutRec(); + outRec2->Pts = join->OutPt2; + + //update all OutRec2.Pts Idx's ... + UpdateOutPtIdxs(*outRec2); + + //We now need to check every OutRec.FirstLeft pointer. If it points + //to OutRec1 it may need to point to OutRec2 instead ... + if (m_UsingPolyTree) + for (PolyOutList::size_type j = 0; j < m_PolyOuts.size() - 1; j++) + { + OutRec* oRec = m_PolyOuts[j]; + if (!oRec->Pts || ParseFirstLeft(oRec->FirstLeft) != outRec1 || + oRec->IsHole == outRec1->IsHole) continue; + if (Poly2ContainsPoly1(oRec->Pts, join->OutPt2)) + oRec->FirstLeft = outRec2; + } + + if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) + { + //outRec2 is contained by outRec1 ... + outRec2->IsHole = !outRec1->IsHole; + outRec2->FirstLeft = outRec1; + + //fixup FirstLeft pointers that may need reassigning to OutRec1 + if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); + + if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) + ReversePolyPtLinks(outRec2->Pts); + + } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) + { + //outRec1 is contained by outRec2 ... + outRec2->IsHole = outRec1->IsHole; + outRec1->IsHole = !outRec2->IsHole; + outRec2->FirstLeft = outRec1->FirstLeft; + outRec1->FirstLeft = outRec2; + + //fixup FirstLeft pointers that may need reassigning to OutRec1 + if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); + + if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) + ReversePolyPtLinks(outRec1->Pts); + } + else + { + //the 2 polygons are completely separate ... + outRec2->IsHole = outRec1->IsHole; + outRec2->FirstLeft = outRec1->FirstLeft; + + //fixup FirstLeft pointers that may need reassigning to OutRec2 + if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2); + } + + } else + { + //joined 2 polygons together ... + + outRec2->Pts = nullptr; + outRec2->BottomPt = nullptr; + outRec2->Idx = outRec1->Idx; + + outRec1->IsHole = holeStateRec->IsHole; + if (holeStateRec == outRec2) + outRec1->FirstLeft = outRec2->FirstLeft; + outRec2->FirstLeft = outRec1; + + //fixup FirstLeft pointers that may need reassigning to OutRec1 + if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); + } + } +} + +//------------------------------------------------------------------------------ +// ClipperOffset support functions ... +//------------------------------------------------------------------------------ + +DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) +{ + if(pt2.X == pt1.X && pt2.Y == pt1.Y) + return DoublePoint(0, 0); + + double Dx = (double)(pt2.X - pt1.X); + double dy = (double)(pt2.Y - pt1.Y); + double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy ); + Dx *= f; + dy *= f; + return DoublePoint(dy, -Dx); +} + +//------------------------------------------------------------------------------ +// ClipperOffset class +//------------------------------------------------------------------------------ + +ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance) + : m_destPolys() + , m_srcPoly() + , m_destPoly() + , m_normals() + , m_delta() + , m_sinA(0.0) + , m_sin(0.0) + , m_cos(1.0) + , m_miterLim() + , m_StepsPerRad() + , m_lowest() + , m_polyNodes() +{ + this->MiterLimit = miterLimit; + this->ArcTolerance = arcTolerance; + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ + +ClipperOffset::~ClipperOffset() +{ + Clear(); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Clear() +{ + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + delete m_polyNodes.Childs[i]; + m_polyNodes.Childs.clear(); + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType) +{ + int highI = (int)path.size() - 1; + if (highI < 0) return; + PolyNode* newNode = new PolyNode(); + newNode->m_jointype = joinType; + newNode->m_endtype = endType; + + //strip duplicate points from path and also get index to the lowest point ... + if (endType == etClosedLine || endType == etClosedPolygon) + while (highI > 0 && path[0] == path[highI]) highI--; + newNode->Contour.reserve(highI + 1); + newNode->Contour.emplace_back(path[0]); + int j = 0, k = 0; + for (int i = 1; i <= highI; i++) + if (newNode->Contour[j] != path[i]) + { + j++; + newNode->Contour.emplace_back(path[i]); + if (path[i].Y > newNode->Contour[k].Y || + (path[i].Y == newNode->Contour[k].Y && + path[i].X < newNode->Contour[k].X)) k = j; + } + if (endType == etClosedPolygon && j < 2) + { + delete newNode; + return; + } + m_polyNodes.AddChild(*newNode); + + //if this path's lowest pt is lower than all the others then update m_lowest + if (endType != etClosedPolygon) return; + if (m_lowest.X < 0) + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); + else + { + IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y]; + if (newNode->Contour[k].Y > ip.Y || + (newNode->Contour[k].Y == ip.Y && + newNode->Contour[k].X < ip.X)) + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType) +{ + for (Paths::size_type i = 0; i < paths.size(); ++i) + AddPath(paths[i], joinType, endType); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::FixOrientations() +{ + //fixup orientations of all closed paths if the orientation of the + //closed path with the lowermost vertex is wrong ... + if (m_lowest.X >= 0 && + !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour)) + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon || + (node.m_endtype == etClosedLine && Orientation(node.Contour))) + ReversePath(node.Contour); + } + } else + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedLine && !Orientation(node.Contour)) + ReversePath(node.Contour); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(Paths& solution, double delta) +{ + solution.clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + if (!solution.empty()) solution.erase(solution.begin()); + } +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Builds a new path from "path" with offset "delta" + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +bool ClipperOffset::PathOffset( const Path & path, Path & offsetPath, double delta ) +{ + IntPoint pathBegin; + IntPoint pathEnd; + + Clear(); + + if (delta < 0) + { + Path reversePath; + + reversePath.reserve(path.size()); + + for (auto p = path.crbegin(); p != path.crend(); ++p) + { + reversePath.emplace_back(*p); + } + + AddPath( reversePath, JoinType::jtSquare, EndType::etOpenButt ); + + delta = fabs(delta); + pathBegin = path.back(); + pathEnd = path.front(); + } + else + { + pathBegin = path.front(); + pathEnd = path.back(); + AddPath( path, JoinType::jtSquare, EndType::etOpenButt ); + } + + // build offset polygon: + + Paths solution; + + FixOrientations(); + DoOffset(delta); + + Clipper clpr; + + clpr.AddPaths(m_destPolys, ptSubject, true); + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + + // filter edges to get only the moved path: + + bool onOffsetPath = false; + for (auto solutionPath : solution) + { + if (onOffsetPath) break; + + ClipperLib::Path::const_iterator it = solutionPath.begin(); + ClipperLib::Path::const_iterator beginningOnPathPoint = solutionPath.end(); + + if (it != solutionPath.end()) + { + ClipperLib::cInt px = it->X; + ClipperLib::cInt py = it->Y; + ClipperLib::cInt dx; + ClipperLib::cInt dy; + + ++it; + + // iterate over edges + while (it != beginningOnPathPoint) + { + ClipperLib::cInt rx = it->X - px; + ClipperLib::cInt ry = it->Y - py; + + if (onOffsetPath) + { + dx = pathEnd.X - px; + dy = pathEnd.Y - py; + } + else + { + dx = pathBegin.X - px; + dy = pathBegin.Y - py; + } + + // check for begin or end point is on edge + if (((dx*ry) == (dy*rx)) && ((std::abs(dx) <= std::abs(rx)) && ((dx*rx) >= 0))) + { + if (!onOffsetPath) + { + // begin point is on edge start copying geometry to offsetPath + onOffsetPath = true; + // set end condition of loop walk again over all verticies + beginningOnPathPoint = it; + } + else break; // end point is on edge, skip the rest of the path + } + + if (onOffsetPath) offsetPath.emplace_back( *it ); + + px = it->X; + py = it->Y; + + ++it; + + if (it == solutionPath.end()) + { + // end of geometry is reached, but there not all geometry copied now + // set it to the beginning and go on. Or pathBegin is lying in the + // in the last segment (closing segment) of the polygon + it = solutionPath.begin(); + if (!onOffsetPath) + { + // check the closing segment and finish search if not found + beginningOnPathPoint = it+1; + } + } + } // while + } + } + + // Should never be nessecary, but could be useful in worthed case + if ((offsetPath.empty()) && (!solution.empty())) + { + auto solutionPath = solution.front(); + for (auto vertex : solutionPath) + { + offsetPath.emplace_back(vertex); + } + return false; + } + + return (!offsetPath.empty()); +} /* ClipperOffset::PathOffset */ + +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(PolyTree& solution, double delta) +{ + solution.Clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + //remove the outer PolyNode rectangle ... + if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0) + { + PolyNode* outerNode = solution.Childs[0]; + solution.Childs.reserve(outerNode->ChildCount()); + solution.Childs[0] = outerNode->Childs[0]; + solution.Childs[0]->Parent = outerNode->Parent; + for (int i = 1; i < outerNode->ChildCount(); ++i) + solution.AddChild(*outerNode->Childs[i]); + } + else + solution.Clear(); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoOffset(double delta) +{ + m_destPolys.clear(); + m_delta = delta; + + //if Zero offset, just copy any CLOSED polygons to m_p and return ... + if (NEAR_ZERO(delta)) + { + m_destPolys.reserve(m_polyNodes.ChildCount()); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon) + m_destPolys.emplace_back(node.Contour); + } + return; + } + + //see offset_triginometry3.svg in the documentation folder ... + if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit); + else m_miterLim = 0.5; + + double y; + if (ArcTolerance <= 0.0) y = def_arc_tolerance; + else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance) + y = std::fabs(delta) * def_arc_tolerance; + else y = ArcTolerance; + //see offset_triginometry2.svg in the documentation folder ... + double steps = pi / std::acos(1 - y / std::fabs(delta)); + if (steps > std::fabs(delta) * pi) + steps = std::fabs(delta) * pi; //ie excessive precision check + m_sin = std::sin(two_pi / steps); + m_cos = std::cos(two_pi / steps); + m_StepsPerRad = steps / two_pi; + if (delta < 0.0) m_sin = -m_sin; + + m_destPolys.reserve(m_polyNodes.ChildCount() * 2); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + m_srcPoly = node.Contour; + + int len = (int)m_srcPoly.size(); + if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon))) + continue; + + m_destPoly.clear(); + if (len == 1) + { + if (node.m_jointype == jtRound) + { + double X = 1.0, Y = 0.0; + for (cInt j = 1; j <= steps; j++) + { + m_destPoly.emplace_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + double X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + } + else + { + double X = -1.0, Y = -1.0; + for (int j = 0; j < 4; ++j) + { + m_destPoly.emplace_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + if (X < 0) X = 1; + else if (Y < 0) Y = 1; + else X = -1; + } + } + m_destPolys.emplace_back(m_destPoly); + continue; + } + //build m_normals ... + m_normals.clear(); + m_normals.reserve(len); + for (int j = 0; j < len - 1; ++j) + m_normals.emplace_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); + if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon) + m_normals.emplace_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); + else + m_normals.emplace_back(DoublePoint(m_normals[len - 2])); + + if (node.m_endtype == etClosedPolygon) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.emplace_back(m_destPoly); + } + else if (node.m_endtype == etClosedLine) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.emplace_back(m_destPoly); + m_destPoly.clear(); + //re-build m_normals ... + DoublePoint n = m_normals[len -1]; + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-n.X, -n.Y); + k = 0; + for (int j = len - 1; j >= 0; j--) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.emplace_back(m_destPoly); + } + else + { + int k = 0; + for (int j = 1; j < len - 1; ++j) + OffsetPoint(j, k, node.m_jointype); + + IntPoint pt1; + if (node.m_endtype == etOpenButt) + { + int j = len - 1; + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); + m_destPoly.emplace_back(pt1); + + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); + m_destPoly.emplace_back(pt1); + } + else + { + int j = len - 1; + k = len - 2; + m_sinA = 0; + m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y); + if (node.m_endtype == etOpenSquare) + DoSquare(j, k); + else + DoRound(j, k); + } + + //re-build m_normals ... + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y); + k = len - 1; + + for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype); + + if (node.m_endtype == etOpenButt) + { + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); + m_destPoly.emplace_back(pt1); + + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); + m_destPoly.emplace_back(pt1); + } + else + { + m_sinA = 0; + if (node.m_endtype == etOpenSquare) + DoSquare(0, 1); + else + DoRound(0, 1); + } + m_destPolys.emplace_back(m_destPoly); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) +{ + //cross product ... + m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); + if (std::fabs(m_sinA * m_delta) < 1.0) + { + //dot product ... + double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y ); + if (cosA > 0) // angle => 0 degrees + { + m_destPoly.emplace_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + return; + } + //else angle => 180 degrees + } + else if (m_sinA > 1.0) m_sinA = 1.0; + else if (m_sinA < -1.0) m_sinA = -1.0; + + if (m_sinA * m_delta < 0) + { + + m_destPoly.emplace_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + m_destPoly.emplace_back(m_srcPoly[j]); + m_destPoly.emplace_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); + + } + else + switch (jointype) + { + case jtMiter: + { + double r = 1 + (m_normals[j].X * m_normals[k].X + + m_normals[j].Y * m_normals[k].Y); + if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k); + break; + } + case jtSquare: DoSquare(j, k); break; + case jtRound: DoRound(j, k); break; + } + k = j; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoSquare(int j, int k) +{ + double dx = std::tan(std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4); + + m_destPoly.emplace_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx)))); + + m_destPoly.emplace_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx)))); + +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoMiter(int j, int k, double r) +{ + double q = m_delta / r; + m_destPoly.emplace_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q), + Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q))); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoRound(int j, int k) +{ + double a = std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y); + int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1); + + double X = m_normals[k].X, Y = m_normals[k].Y; + for (int i = 0; i < steps; ++i) + { + m_destPoly.emplace_back(IntPoint( + Round(m_srcPoly[j].X + X * m_delta), + Round(m_srcPoly[j].Y + Y * m_delta))); + double X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + m_destPoly.emplace_back(IntPoint( + Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); +} + +//------------------------------------------------------------------------------ +// Miscellaneous public functions +//------------------------------------------------------------------------------ + +void Clipper::DoSimplePolygons() +{ + PolyOutList::size_type i = 0; + while (i < m_PolyOuts.size()) + { + OutRec* outrec = m_PolyOuts[i++]; + OutPt* op = outrec->Pts; + if (!op || outrec->IsOpen) continue; + do //for each Pt in Polygon until duplicate found do ... + { + OutPt* op2 = op->Next; + while (op2 != outrec->Pts) + { + if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) + { + //split the polygon into two ... + OutPt* op3 = op->Prev; + OutPt* op4 = op2->Prev; + op->Prev = op4; + op4->Next = op; + op2->Prev = op3; + op3->Next = op2; + + outrec->Pts = op; + OutRec* outrec2 = CreateOutRec(); + outrec2->Pts = op2; + UpdateOutPtIdxs(*outrec2); + if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) + { + //OutRec2 is contained by OutRec1 ... + outrec2->IsHole = !outrec->IsHole; + outrec2->FirstLeft = outrec; + if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); + } + else + if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) + { + //OutRec1 is contained by OutRec2 ... + outrec2->IsHole = outrec->IsHole; + outrec->IsHole = !outrec2->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + outrec->FirstLeft = outrec2; + if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); + } + else + { + //the 2 polygons are separate ... + outrec2->IsHole = outrec->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); + } + op2 = op; //ie get ready for the Next iteration + } + op2 = op2->Next; + } + op = op->Next; + } + while (op != outrec->Pts); + } +} +//------------------------------------------------------------------------------ + +void ReversePath(Path& p) +{ + std::reverse(p.begin(), p.end()); +} +//------------------------------------------------------------------------------ + +void ReversePaths(Paths& p) +{ + for (Paths::size_type i = 0; i < p.size(); ++i) + ReversePath(p[i]); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPath(in_poly, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPaths(in_polys, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(Paths &polys, PolyFillType fillType) +{ + SimplifyPolygons(polys, polys, fillType); +} +//------------------------------------------------------------------------------ + +inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2) +{ + double Dx = ((double)pt1.X - pt2.X); + double dy = ((double)pt1.Y - pt2.Y); + return (Dx*Dx + dy*dy); +} +//------------------------------------------------------------------------------ + +double DistanceFromLineSqrd( + const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2) +{ + //The equation of a line in general form (Ax + By + C = 0) + //given 2 points (x¹,y¹) & (x²,y²) is ... + //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0 + //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹ + //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) + //see http://en.wikipedia.org/wiki/Perpendicular_distance + double A = double(ln1.Y - ln2.Y); + double B = double(ln2.X - ln1.X); + double C = A * ln1.X + B * ln1.Y; + C = A * pt.X + B * pt.Y - C; + return (C * C) / (A * A + B * B); +} +//--------------------------------------------------------------------------- + +bool SlopesNearCollinear(const IntPoint& pt1, + const IntPoint& pt2, const IntPoint& pt3, double distSqrd) +{ + //this function is more accurate when the point that's geometrically + //between the other 2 points is the one that's tested for distance. + //ie makes it more likely to pick up 'spikes' ... + if (Abs(pt1.X - pt2.X) > Abs(pt1.Y - pt2.Y)) + { + if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.X > pt1.X) == (pt2.X < pt3.X)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } + else + { + if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } +} +//------------------------------------------------------------------------------ + +bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) +{ + double Dx = (double)pt1.X - pt2.X; + double dy = (double)pt1.Y - pt2.Y; + return ((Dx * Dx) + (dy * dy) <= distSqrd); +} +//------------------------------------------------------------------------------ + +OutPt* ExcludeOp(OutPt* op) +{ + OutPt* result = op->Prev; + result->Next = op->Next; + op->Next->Prev = result; + result->Idx = 0; + return result; +} +//------------------------------------------------------------------------------ + +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) +{ + //distance = proximity in units/pixels below which vertices + //will be stripped. Default ~= sqrt(2). + + size_t size = in_poly.size(); + + if (size == 0) + { + out_poly.clear(); + return; + } + + OutPt* outPts = new OutPt[size]; + for (size_t i = 0; i < size; ++i) + { + outPts[i].Pt = in_poly[i]; + outPts[i].Next = &outPts[(i + 1) % size]; + outPts[i].Next->Prev = &outPts[i]; + outPts[i].Idx = 0; + } + + double distSqrd = distance * distance; + OutPt* op = &outPts[0]; + while (op->Idx == 0 && op->Next != op->Prev) + { + if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd)) + { + ExcludeOp(op->Next); + op = ExcludeOp(op); + size -= 2; + } + else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else + { + op->Idx = 1; + op = op->Next; + } + } + + if (size < 3) size = 0; + out_poly.resize(size); + for (size_t i = 0; i < size; ++i) + { + out_poly[i] = op->Pt; + op = op->Next; + } + delete [] outPts; +} +//------------------------------------------------------------------------------ + +void CleanPolygon(Path& poly, double distance) +{ + CleanPolygon(poly, poly, distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance) +{ + for (Paths::size_type i = 0; i < in_polys.size(); ++i) + CleanPolygon(in_polys[i], out_polys[i], distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(Paths& polys, double distance) +{ + CleanPolygons(polys, polys, distance); +} +//------------------------------------------------------------------------------ + +void Minkowski(const Path& poly, const Path& path, + Paths& solution, bool isSum, bool isClosed) +{ + int delta = (isClosed ? 1 : 0); + size_t polyCnt = poly.size(); + size_t pathCnt = path.size(); + Paths pp; + pp.reserve(pathCnt); + if (isSum) + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.emplace_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y)); + pp.emplace_back(p); + } + else + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.emplace_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y)); + pp.emplace_back(p); + } + + solution.clear(); + solution.reserve((pathCnt + delta) * (polyCnt + 1)); + for (size_t i = 0; i < pathCnt - 1 + delta; ++i) + for (size_t j = 0; j < polyCnt; ++j) + { + Path quad; + quad.reserve(4); + quad.emplace_back(pp[i % pathCnt][j % polyCnt]); + quad.emplace_back(pp[(i + 1) % pathCnt][j % polyCnt]); + quad.emplace_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); + quad.emplace_back(pp[i % pathCnt][(j + 1) % polyCnt]); + if (!Orientation(quad)) ReversePath(quad); + solution.emplace_back(quad); + } +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) +{ + Minkowski(pattern, path, solution, true, pathIsClosed); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +void TranslatePath(const Path& input, Path& output, IntPoint delta) +{ + //precondition: input != output + output.resize(input.size()); + for (size_t i = 0; i < input.size(); ++i) + output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y); +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed) +{ + Clipper c; + for (size_t i = 0; i < paths.size(); ++i) + { + Paths tmp; + Minkowski(pattern, paths[i], tmp, true, pathIsClosed); + c.AddPaths(tmp, ptSubject, true); + if (pathIsClosed) + { + Path tmp2; + TranslatePath(paths[i], tmp2, pattern[0]); + c.AddPath(tmp2, ptClip, true); + } + } + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution) +{ + Minkowski(poly1, poly2, solution, false, true); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +enum NodeType {ntAny, ntOpen, ntClosed}; + +void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& paths) +{ + bool match = true; + if (nodetype == ntClosed) match = !polynode.IsOpen(); + else if (nodetype == ntOpen) return; + + if (!polynode.Contour.empty() && match) + paths.emplace_back(polynode.Contour); + for (int i = 0; i < polynode.ChildCount(); ++i) + AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths); +} +//------------------------------------------------------------------------------ + +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPaths(polytree, ntAny, paths); +} +//------------------------------------------------------------------------------ + +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPaths(polytree, ntClosed, paths); +} +//------------------------------------------------------------------------------ + +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + //Open paths are top level only, so ... + for (int i = 0; i < polytree.ChildCount(); ++i) + if (polytree.Childs[i]->IsOpen()) + paths.emplace_back(polytree.Childs[i]->Contour); +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const IntPoint &p) +{ + s << "(" << p.X << "," << p.Y << ")"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Path &p) +{ + if (p.empty()) return s; + Path::size_type last = p.size() -1; + for (Path::size_type i = 0; i < last; i++) + s << "(" << p[i].X << "," << p[i].Y << "), "; + s << "(" << p[last].X << "," << p[last].Y << ")\n"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Paths &p) +{ + for (Paths::size_type i = 0; i < p.size(); i++) + s << p[i]; + s << "\n"; + return s; +} +//------------------------------------------------------------------------------ + +} //ClipperLib namespace diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/clipper.hpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/clipper.hpp new file mode 100755 index 000000000..08dc41fd4 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/clipper.hpp @@ -0,0 +1,411 @@ +/** +** +** @RDE_HEADER@ +** Clipping and Offsetting of lines and polygons +** $Id$ +** $HeadURL$ +** $Author$ +** +** ----------------------------------------------------------------------------------------------- +** To use under Boost Software License Vers. 1 +** see also http://www.angusj.com/delphi/clipper/documentation/Docs/_Body.htm +** ----------------------------------------------------------------------------------------------- +*/ +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.2.1 * +* Date : 31 October 2014 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2014 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +#ifndef clipper_hpp +#define clipper_hpp + +#define CLIPPER_VERSION "6.2.0" + +//use_int32: When enabled 32bit ints are used instead of 64bit ints. This +//improve performance but coordinate values are limited to the range +/- 46340 +//#define use_int32 + +//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. +//#define use_xyz + +//use_lines: Enables line clipping. Adds a very minor cost to performance. +//#define use_lines + +//use_deprecated: Enables temporary support for the obsolete functions +//#define use_deprecated + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ClipperLib { + +enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; +enum PolyType { ptSubject, ptClip }; +//By far the most widely used winding rules for polygon filling are +//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) +//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) +//see http://glprogramming.com/red/chapter11.html +enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; + +#ifdef use_int32 + typedef int cInt; + static cInt const loRange = 0x7FFF; + static cInt const hiRange = 0x7FFF; +#else + typedef signed long long cInt; + static cInt const loRange = 0x3FFFFFFF; + static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; + typedef signed long long long64; //used by Int128 class + typedef unsigned long long ulong64; + +#endif + +struct IntPoint { + cInt X; + cInt Y; +#ifdef use_xyz + cInt Z; + IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; +#else + IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; +#endif + + friend inline bool operator== (const IntPoint& a, const IntPoint& b) + { + return a.X == b.X && a.Y == b.Y; + } + friend inline bool operator!= (const IntPoint& a, const IntPoint& b) + { + return a.X != b.X || a.Y != b.Y; + } +}; +//------------------------------------------------------------------------------ + +typedef std::vector< IntPoint > Path; +typedef std::vector< Path > Paths; + +inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} +inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} + +std::ostream& operator <<(std::ostream &s, const IntPoint &p); +std::ostream& operator <<(std::ostream &s, const Path &p); +std::ostream& operator <<(std::ostream &s, const Paths &p); + +struct DoublePoint +{ + double X; + double Y; + DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} + explicit DoublePoint(const IntPoint &ip) : X(ip.X), Y(ip.Y) {} +}; +//------------------------------------------------------------------------------ + +#ifdef use_xyz +typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); +#endif + +enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; +enum JoinType {jtSquare, jtRound, jtMiter}; +enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; + +class PolyNode; +typedef std::vector< PolyNode* > PolyNodes; + +class PolyNode +{ +public: + PolyNode(); + virtual ~PolyNode(){}; + Path Contour; + PolyNodes Childs; + PolyNode* Parent; + PolyNode* GetNext() const; + bool IsHole() const; + bool IsOpen() const; + int ChildCount() const; +private: + unsigned Index; //node index in Parent.Childs + bool m_IsOpen; + JoinType m_jointype; + EndType m_endtype; + PolyNode* GetNextSiblingUp() const; + void AddChild(PolyNode& child); + friend class Clipper; //to access Index + friend class ClipperOffset; +}; + +class PolyTree: public PolyNode +{ +public: + ~PolyTree(){Clear();}; + PolyNode* GetFirst() const; + void Clear(); + int Total() const; +private: + PolyNodes AllNodes; + friend class Clipper; //to access AllNodes +}; + +bool Orientation(const Path &poly); +double Area(const Path &poly); +int PointInPolygon(const IntPoint &pt, const Path &path); + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); + +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); +void CleanPolygon(Path& poly, double distance = 1.415); +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); +void CleanPolygons(Paths& polys, double distance = 1.415); + +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); + +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); + +void ReversePath(Path& p); +void ReversePaths(Paths& p); + +struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; + +//enums that are used internally ... +enum EdgeSide { esLeft = 1, esRight = 2}; + +//forward declarations (for stuff used internally) ... +struct TEdge; +struct IntersectNode; +struct LocalMinimum; +struct Scanbeam; +struct OutPt; +struct OutRec; +struct Join; + +typedef std::vector < OutRec* > PolyOutList; +typedef std::vector < TEdge* > EdgeList; +typedef std::vector < Join* > JoinList; +typedef std::vector < IntersectNode* > IntersectList; + +//------------------------------------------------------------------------------ + +//ClipperBase is the ancestor to the Clipper class. It should not be +//instantiated directly. This class simply abstracts the conversion of sets of +//polygon coordinates into edge objects that are stored in a LocalMinima list. +class ClipperBase +{ +public: + ClipperBase(); + virtual ~ClipperBase(); + bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); + bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); + virtual void Clear(); + IntRect GetBounds(); + bool PreserveCollinear() const {return m_PreserveCollinear;}; + void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; +protected: + void DisposeLocalMinimaList(); + TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); + void PopLocalMinima(); + virtual void Reset(); + TEdge* ProcessBound(TEdge* E, bool IsClockwise); + void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed); + TEdge* DescendToMin(TEdge *&E); + void AscendToMax(TEdge *&E, bool Appending, bool IsClosed); + + typedef std::vector MinimaList; + MinimaList::iterator m_CurrentLM; + MinimaList m_MinimaList; + + bool m_UseFullRange; + EdgeList m_edges; + bool m_PreserveCollinear; + bool m_HasOpenPaths; +}; +//------------------------------------------------------------------------------ + +class Clipper : public virtual ClipperBase +{ +public: + explicit Clipper(int initOptions = 0); + ~Clipper(); + bool Execute(ClipType clipType, + Paths &solution, + PolyFillType subjFillType = pftEvenOdd, + PolyFillType clipFillType = pftEvenOdd); + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType subjFillType = pftEvenOdd, + PolyFillType clipFillType = pftEvenOdd); + bool ReverseSolution() const {return m_ReverseOutput;}; + void ReverseSolution(bool value) {m_ReverseOutput = value;}; + bool StrictlySimple() const {return m_StrictSimple;}; + void StrictlySimple(bool value) {m_StrictSimple = value;}; + //set the callback function for z value filling on intersections (otherwise Z is 0) +#ifdef use_xyz + void ZFillFunction(ZFillCallback zFillFunc); +#endif +protected: + void Reset(); + virtual bool ExecuteInternal(); +private: + PolyOutList m_PolyOuts; + JoinList m_Joins; + JoinList m_GhostJoins; + IntersectList m_IntersectList; + ClipType m_ClipType; + typedef std::priority_queue ScanbeamList; + ScanbeamList m_Scanbeam; + TEdge *m_ActiveEdges; + TEdge *m_SortedEdges; + bool m_ExecuteLocked; + PolyFillType m_ClipFillType; + PolyFillType m_SubjFillType; + bool m_ReverseOutput; + bool m_UsingPolyTree; + bool m_StrictSimple; +#ifdef use_xyz + ZFillCallback m_ZFill; //custom callback +#endif + void SetWindingCount(TEdge& edge) const; + bool IsEvenOddFillType(const TEdge& edge) const; + bool IsEvenOddAltFillType(const TEdge& edge) const; + void InsertScanbeam(const cInt Y); + cInt PopScanbeam(); + void InsertLocalMinimaIntoAEL(const cInt botY); + void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); + void AddEdgeToSEL(TEdge *edge); + void CopyAELToSEL(); + void DeleteFromSEL(TEdge *e); + void DeleteFromAEL(TEdge *e); + void UpdateEdgeIntoAEL(TEdge *&e); + void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); + bool IsContributing(const TEdge& edge) const; + bool IsTopHorz(const cInt XPos); + void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); + void DoMaxima(TEdge *e); + void ProcessHorizontals(bool IsTopOfScanbeam); + void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam); + void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutRec* GetOutRec(int idx); + void AppendPolygon(TEdge *e1, TEdge *e2) const; + void IntersectEdges(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutRec* CreateOutRec(); + OutPt* AddOutPt(TEdge *e, const IntPoint &pt); + void DisposeAllOutRecs(); + void DisposeOutRec(PolyOutList::size_type index); + bool ProcessIntersections(const cInt topY); + void BuildIntersectList(const cInt topY); + void ProcessIntersectList(); + void ProcessEdgesAtTopOfScanbeam(const cInt topY); + void BuildResult(Paths& polys); + void BuildResult2(PolyTree& polytree); + void SetHoleState(TEdge *e, OutRec *outrec) const; + void DisposeIntersectNodes(); + bool FixupIntersectionOrder(); + void FixupOutPolygon(OutRec &outrec); + bool IsHole(TEdge *e); + bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); + static void FixHoleLinkage(OutRec &outrec); + void AddJoin(OutPt *op1, OutPt *op2, const IntPoint & offPt); + void ClearJoins(); + void ClearGhostJoins(); + void AddGhostJoin(OutPt *op, const IntPoint & offPt); + bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); + void JoinCommonEdges(); + void DoSimplePolygons(); + void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const; + void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const; +#ifdef use_xyz + void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); +#endif +}; +//------------------------------------------------------------------------------ + +class ClipperOffset +{ +public: + ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); + ~ClipperOffset(); + void AddPath(const Path& path, JoinType joinType, EndType endType); + void AddPaths(const Paths& paths, JoinType joinType, EndType endType); + void Execute(Paths& solution, double delta); + void Execute(PolyTree& solution, double delta); + + bool PathOffset( const Path& path, Path & offsetPath, double delta ); // calc offset of one path + + void Clear(); + double MiterLimit; + double ArcTolerance; +private: + Paths m_destPolys; + Path m_srcPoly; + Path m_destPoly; + std::vector m_normals; + double m_delta, m_sinA, m_sin, m_cos; + double m_miterLim, m_StepsPerRad; + IntPoint m_lowest; + PolyNode m_polyNodes; + + void FixOrientations(); + void DoOffset(double delta); + void OffsetPoint(int j, int& k, JoinType jointype); + void DoSquare(int j, int k); + void DoMiter(int j, int k, double r); + void DoRound(int j, int k); +}; +//------------------------------------------------------------------------------ + +class clipperException : public std::exception +{ + public: + explicit clipperException(const char* description): m_descr(description) {} + virtual ~clipperException() throw() {} + virtual const char* what() const throw() {return m_descr.c_str();} + private: + std::string m_descr; +}; +//------------------------------------------------------------------------------ + +} //ClipperLib namespace + +#endif //clipper_hpp + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/curve.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/curve.cpp new file mode 100755 index 000000000..51e1dbb74 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/curve.cpp @@ -0,0 +1,736 @@ +/** +** +** @RDE_HEADER@ +** Rasterize functional path geometries to polylines +** $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 .........: +** +** 12.08.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "curve.h" +#include "path_rasterer.h" + + +#include + +namespace SituationMap { namespace MathUtils +{ + + static QPointF calcBezierVertex( double t, const QPointF control[4] ) + { + double u, u2, u3, t2, t3; + double xx, yy; + + t2 = t*t; + t3 = t2*t; + u = 1.0 - t; + u2 = u*u; + u3 = u2*u; + xx = control[0].x()*u3 + 3.0*(control[1].x()*t*u2 + control[2].x()*t2*u) + control[3].x()*t3; + yy = control[0].y()*u3 + 3.0*(control[1].y()*t*u2 + control[2].y()*t2*u) + control[3].y()*t3; + + return QPointF(xx,yy); + + } /* calcBezierVertex */ + + + static QPointF calcBezierTangent( double t, const QPointF control[4] ) + { + double t2(t*t); + double u(1.0-t); + double u2(u*u); + double xx(3.0*((control[1].x()-control[0].x())*u2 + (control[2].x()-control[1].x())*t*u*2.0 + (control[3].x()-control[2].x())*t2)); + double yy(3.0*((control[1].y()-control[0].y())*u2 + (control[2].y()-control[1].y())*t*u*2.0 + (control[3].y()-control[2].y())*t2)); + + return QPointF(xx,yy); + + } /* calcBezierTangent */ + + + static double hypotBezierTangent( double t, const QPointF control[4] ) + { + QPointF p(calcBezierTangent(t,control)); + return hypot(p.x(),p.y()); + } /* hypotBezierTangent */ + + + static double simpsonApproximation( const QPointF control[4], double a, double b ) + { + const unsigned int NLenSteps = 800; + + double t( b-a ); + double h( t/NLenSteps ); + + double sum( 0.0 ); + + // Zusammengesetzte Integration nach der Simpson-Regel + + double s1( 0.0 ); + double s2( 0.0 ); + + for (unsigned int i = 0; i < NLenSteps; i++) + { + s1 += hypotBezierTangent( a+h*i, control ); + s2 += hypotBezierTangent( a+h*(double(i)-0.5), control ); + } + s2 += hypotBezierTangent( a+h*(double(NLenSteps)-0.5), control ); + + sum += h/6.0 *( hypotBezierTangent(a,control)+hypotBezierTangent(b,control)+2.0*s1+4.0*s2); + + return sum; + + } /* simpsonApproximation */ + + + static inline void buildControlPoints( const QPointF & p_i, + const QPointF & p_i1, + const QPointF & p_i2, + QPointF control[4], + bool startSegm = false, + bool endSegm = false ) + { + if (startSegm) + { + control[0] = p_i; + control[1] = p_i*0.333 + p_i1*0.667; + } + else + { + control[0] = p_i*0.5 + p_i1*0.5; + control[1] = p_i*0.167 + p_i1*0.833; + } + + if (endSegm) + { + control[2] = p_i1*0.667 + p_i2*0.333; + control[3] = p_i2; + } + else + { + control[2] = p_i1*0.833 + p_i2*0.167; + control[3] = p_i1*0.5 + p_i2*0.5; + } + } /* buildControlPoints */ + + + + SmoothLine::SmoothLine(const QVector & vertexVector, bool closedCurve ) + { + SmoothLine::vertexVector( vertexVector, closedCurve ); + } /* BezierSpline::SmoothLine */ + + void SmoothLine::vertexVector( const QVector & vertexVector, bool closedCurve ) + { + _closedCurve = closedCurve; + _vertexVector = vertexVector; + } /* SmoothLine::vertexVector */ + + + qreal SmoothLine::maximumCurveParameter() const + { + if (_vertexVector.size() > 2) + { + return (_closedCurve) ? (qreal)_vertexVector.size() : (qreal)_vertexVector.size() - 2.0; + } + return 0; + } /* SmoothLine::maximumCurveParameter */ + + + QPointF SmoothLine::positionOnCurve( qreal t ) const + { + if (_vertexVector.size() < 3) + { + if (_vertexVector.size() > 1) + { + // It's just a straight line + if (t < 0.0) t = 1.0-t; + + if ((t >= 0.0) && (t <= 1.0)) + { + const QPointF & v0 = _vertexVector[0]; + const QPointF & v1 = _vertexVector[1]; + return v0 + (v1-v0)*t; + } + } + } + else + { + int maxSegm = _vertexVector.size() - ((_closedCurve) ? 1 : 3); + + if (t < 0.0) t = double(maxSegm+1) - t; + + int segment( floor(t) ); + + if ((segment >= 0) && (segment <= maxSegm)) + { + QPointF control[4]; + QPointF p[3]; + + if ((_closedCurve) && (segment == (maxSegm-1))) + { + p[0] = _vertexVector[_vertexVector.size()-2]; + p[1] = _vertexVector.back(); + p[2] = _vertexVector[0]; + + buildControlPoints( p[0], p[1], p[2], control, false, false ); + } + else if ((_closedCurve) && (segment == maxSegm)) + { + p[0] = _vertexVector.back(); + p[1] = _vertexVector[0]; + p[2] = _vertexVector[1]; + + buildControlPoints( p[0], p[1], p[2], control, false, false ); + } + else + { + p[0] = _vertexVector[segment ]; + p[1] = _vertexVector[segment+1]; + p[2] = _vertexVector[segment+2]; + + buildControlPoints( p[0], p[1], p[2], control, ((segment == 0) && (!_closedCurve)), (segment == maxSegm) ); + } + return calcBezierVertex( t-double(segment), control ); + } + else + { + if (t == (maxSegm+1)) + { + QPointF control[4]; + QPointF p[3]; + + if (_closedCurve) + { + p[0] = _vertexVector[0]; + p[1] = _vertexVector[1]; + p[2] = _vertexVector[2]; + + buildControlPoints( p[0], p[1], p[2], control, true, false ); + return calcBezierVertex( 0.0, control ); + } + else + { + p[0] = _vertexVector[maxSegm ]; + p[1] = _vertexVector[maxSegm+1]; + p[2] = _vertexVector[maxSegm+2]; + + buildControlPoints( p[0], p[1], p[2], control, (maxSegm == 0), true ); + return calcBezierVertex( 1.0, control ); + } + } + } + } + return QPointF(0.0,0.0); + } /* SmoothLine::positionOnCurve */ + + + QPointF SmoothLine::directionOnCurve( qreal t ) const + { + if (_vertexVector.size() < 3) + { + if (_vertexVector.size() >1) + { + // It's just a straight line + if (t < 0.0) t = 1.0-t; + + if ((t >= 0.0) && (t <= 1.0)) + { + return (_vertexVector[1]-_vertexVector[0]); + } + } + } + else + { + int maxSegm = _vertexVector.size() - ((_closedCurve) ? 1 : 3); + + if (t < 0.0) t = double(maxSegm+1) - t; + + int segment( floor(t) ); + + if ((segment >= 0) && (segment <= maxSegm)) + { + QPointF control[4]; + QPointF p[3]; + + if ((_closedCurve) && (segment == (maxSegm-1))) + { + p[0] = _vertexVector[_vertexVector.size()-2]; + p[1] = _vertexVector.back(); + p[2] = _vertexVector[0]; + + buildControlPoints( p[0], p[1], p[2], control, false, false ); + } + else if ((_closedCurve) && (segment == maxSegm)) + { + p[0] = _vertexVector.back(); + p[1] = _vertexVector[0]; + p[2] = _vertexVector[1]; + + buildControlPoints( p[0], p[1], p[2], control, false, false ); + } + else + { + p[0] = _vertexVector[segment ]; + p[1] = _vertexVector[segment+1]; + p[2] = _vertexVector[segment+2]; + + buildControlPoints( p[0], p[1], p[2], control, ((segment == 0) && (!_closedCurve)), (segment == maxSegm) ); + } + return calcBezierTangent( t-double(segment), control ); + } + else + { + if (t == (maxSegm+1)) + { + QPointF control[4]; + QPointF p[3]; + + if (_closedCurve) + { + p[0] = _vertexVector[0]; + p[1] = _vertexVector[1]; + p[2] = _vertexVector[2]; + + buildControlPoints( p[0], p[1], p[2], control, true, false ); + return calcBezierTangent( 0.0, control ); + } + else + { + p[0] = _vertexVector[maxSegm ]; + p[1] = _vertexVector[maxSegm+1]; + p[2] = _vertexVector[maxSegm+2]; + + buildControlPoints( p[0], p[1], p[2], control, (maxSegm == 0), true ); + return calcBezierTangent( 1.0, control ); + } + } + } + } + return QPointF(0.0,0.0); + } /* SmoothLine::directionOnCurve */ + + + qreal SmoothLine::arcLength( qreal t) const + { + if (t < 0.0) return 0.0; + + QPointF control[4]; + QPointF p[3]; + QVector::const_iterator it = _vertexVector.begin(); + + if (_closedCurve) + { + int i = 0; + p[i++] = _vertexVector.back(); + while ((i<3) && (it != _vertexVector.end())) + { + p[i] = *it; + ++it; + ++i; + } + buildControlPoints( p[0], p[1], p[2], control, false, false ); + } + else + { + int i = 0; + while ((i<3) && (it != _vertexVector.end())) + { + p[i] = *it; + ++it; + ++i; + } + buildControlPoints( p[0], p[1], p[2], control, true, (it == _vertexVector.end()) ); + } + + if (t < 1.0) return simpsonApproximation( control, 0.0, t ); + + double sum = simpsonApproximation( control, 0.0, 1.0 ); t -= 1.0; + + while (it != _vertexVector.end()) + { + p[0] = p[1]; + p[1] = p[2]; + p[2] = *it; + ++it; + + if (_closedCurve) buildControlPoints( p[0], p[1], p[2], control, false, false ); + else buildControlPoints( p[0], p[1], p[2], control, false, (it == _vertexVector.end()) ); + + if (t < 1.0) + { + sum += simpsonApproximation( control, 0.0, t ); + break; + } + else sum += simpsonApproximation( control, 0.0, 1.0 ); + } + + if ((t > 0.0) && (_closedCurve)) + { + p[0] = p[1]; + p[1] = p[2]; + p[2] = _vertexVector.front(); + + buildControlPoints( p[0], p[1], p[2], control, false, false ); + + sum += simpsonApproximation( control, 0.0, t ); + } + return sum; + } /* SmoothLine::arcLength */ + + + HermiteSpline::HermiteSpline( const QVector & vertexVector + , const QVector & tangentVector, bool closedCurve ) + { + HermiteSpline::vertexVector(vertexVector, tangentVector, closedCurve ); + } /* HermiteSpline::HermiteSpline */ + + + void HermiteSpline::vertexVector( const QVector & vertexVector + , const QVector & tangentVector, bool closedCurve ) + { + _closedCurve = closedCurve; + _vertexVector = vertexVector; + _tangentVector = tangentVector; + + int countOfTangents = (_closedCurve) ? _vertexVector.size()*2 : (_vertexVector.size()-1) * 2; + + if (_tangentVector.size() >= countOfTangents) + { + // fatal error + throw + SituationMap::Exception( __FILE__, __LINE__, + "No enough tangents defined to calculate spline. Each spline segment must have defined 2 tangents per vertex, excepting the border segments where only 1 tangent must be defined."); + + } + } /* HermiteSpline::vertexVector */ + + + qreal HermiteSpline::maximumCurveParameter() const + { + if (_vertexVector.size() < 2) return 0.0; + return (_closedCurve) ? _vertexVector.size() : (_vertexVector.size() - 1.0); + } /* HermiteSpline::maximumCurveParameter */ + + + QPointF HermiteSpline::positionOnCurve( qreal t ) const + { + if (_vertexVector.size() < 2) return QPointF(0.0,0.0); + + int maxSegm = _vertexVector.size() - ((_closedCurve) ? 1 : 2); + + if (t < 0.0) t = double(maxSegm+1) - t; + + int segment( floor(t) ); + + if ((segment >= 0) && (segment <= maxSegm)) + { + QPointF control[4]; + + if ((_closedCurve) && (segment == maxSegm)) + { + control[0] = _vertexVector.back(); + control[3] = _vertexVector.front(); + control[1] = control[0] + _tangentVector[_tangentVector.size()-2]/3.0; + control[2] = control[3] - _tangentVector[_tangentVector.size()-1]/3.0; + } + else + { + control[0] = _vertexVector[segment]; + control[3] = _vertexVector[segment+1]; + control[1] = control[0] + _tangentVector[segment*2]/3.0; + control[2] = control[3] - _tangentVector[segment*2+1]/3.0; + } + return calcBezierVertex( t-double(segment), control ); + } + + return QPointF(0.0,0.0); + + } /* HermiteSpline::positionOnCurve */ + + + QPointF HermiteSpline::directionOnCurve( qreal t ) const + { + if (_vertexVector.size() < 2) return QPointF(0.0,0.0); + + int maxSegm = _vertexVector.size() - ((_closedCurve) ? 1 : 2); + + if (t < 0.0) t = double(maxSegm+1) - t; + + int segment( floor(t) ); + + if ((segment >= 0) && (segment <= maxSegm)) + { + QPointF control[4]; + + if ((_closedCurve) && (segment == maxSegm)) + { + control[0] = _vertexVector.back(); + control[3] = _vertexVector.front(); + control[1] = control[0] + _tangentVector[_tangentVector.size()-2]/3.0; + control[2] = control[3] - _tangentVector[_tangentVector.size()-1]/3.0; + } + else + { + control[0] = _vertexVector[segment]; + control[3] = _vertexVector[segment+1]; + control[1] = control[0] + _tangentVector[segment*2]/3.0; + control[2] = control[3] - _tangentVector[segment*2+1]/3.0; + } + return calcBezierTangent( t-double(segment), control ); + } + + return QPointF(0.0,0.0); + + } /* HermiteSpline::directionOnCurve */ + + + qreal HermiteSpline::arcLength( qreal t ) const + { + if (t < 0.0) return 0.0; + if (_vertexVector.size() < 2) return 0.0; + + double sum = 0.0; + QPointF control[4]; + + QVector::const_iterator itv = _vertexVector.begin(); + QVector::const_iterator itt = _vertexVector.begin(); + + if (_closedCurve) { if (2*_vertexVector.size() != _tangentVector.size()) return 0.0; } + else { if (2*_vertexVector.size()-2 != _tangentVector.size()) return 0.0; } + + control[3] = *itv; ++itv; + + while (itv != _vertexVector.end()) + { + control[0] = control[3]; + control[1] = control[0] + *itt / 3.0; ++itt; + control[2] = *itv - *itt / 3.0; ++itt; + control[3] = *itv; + + if (t < 1.0) return sum + simpsonApproximation( control, 0.0, t ); + sum = simpsonApproximation( control, 0.0, 1.0 ); t -= 1.0; + + ++itv; + } // while + + if (_closedCurve) + { + control[0] = control[3]; + control[1] = control[0] + *itt / 3.0; ++itt; + control[2] = _vertexVector.front() - *itt / 3.0; ++itt; + control[3] = _vertexVector.front(); + + if (t < 1.0) return sum + simpsonApproximation( control, 0.0, t ); + sum = simpsonApproximation( control, 0.0, 1.0 ); // t -= 1.0; // 'ScanBuild: 't is never read' + } + return sum; + } /* HermiteSpline::arcLength */ + + + + BezierSpline::BezierSpline( const QVector & ctrlPntVector, bool closedCurve ) + : HermiteSpline() + { + controlPointVector(ctrlPntVector,closedCurve); + } /* BezierSpline::BezierSpline */ + + + void BezierSpline::controlPointVector( const QVector & ctrlPntVector, bool closedCurve ) + { + if (ctrlPntVector.size() < 4) return; + + QVector vertexVector; + QVector tangentVector; + + QVector::const_iterator it = ctrlPntVector.begin(); + + vertexVector.append( *it ); + ++it; + while (it != ctrlPntVector.end()) + { + // set first tangent + tangentVector.append( ((*it) - vertexVector.back()) * 3.0 ); + ++it; + if (it == ctrlPntVector.end()) + { + throw SituationMap::Exception(__FILE__, __LINE__, "Missing bezier spline tangent control point." ); + } + ++it; + + if (it == ctrlPntVector.end()) + { + // error + if (closedCurve) + { + tangentVector.append( ((it[-1]) - vertexVector.front()) * 3.0 ); + } + else throw SituationMap::Exception(__FILE__, __LINE__, "Missing bezier spline vertex control point." ); + break; + } + else + { + vertexVector.append( *it ); + tangentVector.append( ((it[-1]) - vertexVector.back()) * 3.0 ); + ++it; + } + } // while + + HermiteSpline::vertexVector(vertexVector,tangentVector,closedCurve); + + } /* BezierSpline::controlPointVector */ + + + void BezierSpline::vertexVector( const QVector&, const QVector&, bool ) + { + throw SituationMap::Exception(__FILE__, __LINE__, "Function is not defined for BezierSpline. Use controlPointVector instead." ); + } /* BezierSpline::vertexVector */ + + + + + Ellipse::Ellipse( QPointF center, qreal rx, qreal ry, qreal phi, qreal sectorStart, qreal sectorEnd ) + : _center(center) + , _radiusX(fabs(rx)) + , _radiusY(fabs(ry)) + , _sinPhi(sin(phi)) + , _cosPhi(cos(phi)) + , _phiSectorStart(sectorStart) + , _phiSectorEnd(sectorEnd) + { + if (_radiusX < _radiusY) + { + // swap _radiusX and _radiusY + _radiusX = fabs(ry); + _radiusY = fabs(rx); + + // and rotate + phi += MathConst::pi_half; + _sinPhi = sin(phi); + _cosPhi = cos(phi); + } + + qreal a2 = _radiusX * _radiusX; + qreal b2 = _radiusY * _radiusY; + + _eccentricity2 = (a2-b2) / a2; + + if (fabs(_phiSectorStart) > MathConst::pi_two) _phiSectorStart /= MathConst::pi_two; + if (fabs(_phiSectorEnd) > MathConst::pi_two) _phiSectorEnd /= MathConst::pi_two; + + if (_phiSectorStart >= 0) + { + if (_phiSectorStart > _phiSectorEnd) _phiSectorStart -= MathConst::pi_two; + } + else + { + if (_phiSectorStart > _phiSectorEnd)_phiSectorEnd += MathConst::pi_two; + } + } /* Ellipse::Ellipse */ + + + qreal Ellipse::maximumCurveParameter() const { return _phiSectorEnd; } + qreal Ellipse::minimumCurveParameter() const { return _phiSectorStart; } + + QPointF Ellipse::positionOnCurve(qreal t) const + { + // Using ellipse equation in polar coordinates relative to center point(0,0) + // because in this case the parameter t is identical with polar angle. + + qreal sin_t = sin(t); + qreal cos_t = cos(t); + + /* + qreal r = _radiusY / (1.0 - _eccentricity2 * cos_t*cos_t); + qreal x = cos_t*r; + qreal y = sin_t*r; + */ + qreal x = cos_t*_radiusX; + qreal y = sin_t*_radiusY; + return _center + QPointF( x*_cosPhi - y*_sinPhi, y*_cosPhi + x*_sinPhi ); + + } /* Ellipse::positionOnCurve */ + + QPointF Ellipse::directionOnCurve(qreal t) const + { + qreal sin_t = sin(t); + qreal cos_t = cos(t); + + if (cos_t == 0.0) return QPointF( -sin_t, 0.0 ); + + qreal tan_t = sin_t / cos_t; + qreal tan_n = tan_t / 1.0 - _eccentricity2; // tan normal + + // normal QPointF( 1.0/sqrt(1+tan_n*tan_n), tan_n/sqrt(1+tan_n*tan_n) ) + QPointF tangente( -tan_n/sqrt(1+tan_n*tan_n), 1.0/sqrt(1+tan_n*tan_n) ); + + return QPointF( tangente.x()*_cosPhi - tangente.y()*_sinPhi, tangente.y()*_cosPhi + tangente.x()*_sinPhi ); + } /* Ellipse::directionOnCurve */ + + + qreal Ellipse::arcLength(qreal t) const + { + return arcLength( 0.0, t ); + } /* Ellipse::arcLength */ + + + qreal Ellipse::arcLength(qreal tmin, qreal tmax ) const + { + Q_UNUSED(tmin); + Q_UNUSED(tmax); + + // ToDo: calculate convergenting formula based on polar center points equation of ellipse if needed + return 0.0; + } /* Ellipse::arcLength */ + + + class CurvePathRasterer : public PathRasterer + { + public: + + CurvePathRasterer() : PathRasterer(), _curveFunction(nullptr) {} + explicit CurvePathRasterer( const Curve * splineFunction ) : PathRasterer(), _curveFunction(splineFunction) {} + ~CurvePathRasterer() = default; + + void function( qreal t, QPointF & result ) override + { + if (_curveFunction) result = _curveFunction->positionOnCurve(t); + else + { + throw SituationMap::Exception(__FILE__, __LINE__, "Curve function is undefined for PathRasterer." ); + } + } /* function */ + + + protected: + + const Curve * _curveFunction; + + }; /* CurvePathRasterer */ + + + PathRasterer * Ellipse::createPathRaster() const { return new CurvePathRasterer(this); } + PathRasterer * SmoothLine::createPathRaster() const { return new CurvePathRasterer(this); } + PathRasterer * HermiteSpline::createPathRaster() const { return new CurvePathRasterer(this); } + PathRasterer * BezierSpline::createPathRaster() const { return new CurvePathRasterer(this); } + + +} } // namespace SituationMap::MathUtils diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/curve.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/curve.h new file mode 100755 index 000000000..e4c67967f --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/curve.h @@ -0,0 +1,288 @@ +/** +** +** @RDE_HEADER@ +** Implementation of Splines +** $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 .........: +** +** 12.08.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include +#include + +#include +#include + + +namespace SituationMap { namespace MathUtils +{ + + class PathRasterer; + + /** + @class Curve curve.h + @brief Base class to calculate a polyline from curve functions like splines, circles, ellpises ... + + Pure virtual base class to calculate a polyline from curve functions. The curve function is defined + in derived classes. The resolution of rasterization can be set in PathRasterer class. + + @see PathRasterer, Ellipse, Spline, SmoothLine, HermiteSpline, BezierSpline + */ + + class SITUATIONMAP_EXPORT Curve + { + public: + + /** + Returns the maximum curve paramter t. The curve is defined in interval tmin ... tmax + */ + virtual qreal maximumCurveParameter() const = 0; + + + /** + Returns the minimum curve paramter t. The curve is defined in interval tmin ... tmax + */ + virtual qreal minimumCurveParameter() const = 0; + + + /** + Returns the position on curve at \c t + @param t curve parameter, should be in range of 0 ... tmax + */ + virtual QPointF positionOnCurve ( qreal t ) const = 0; + + /** + Returns the tangent at position \c t + @param t curve parameter, should be in range of 0 ... tmax + */ + virtual QPointF directionOnCurve( qreal t ) const = 0; + + /** + Returns the length of curve from start to position t + @param t curve parameter, should be in range of 0 ... tmax + */ + virtual qreal arcLength( qreal t ) const = 0; + + /** + Returns the length of curve from position \c tmin to position \c tmax + @param tmin curve parameter, should be in range of 0 ... maximumCurveParameter() + @param tmax curve parameter, should be in range of tmin ... maximumCurveParameter() + */ + virtual qreal arcLength( qreal tmin, qreal tmax ) const { return arcLength(tmax)-arcLength(tmin); } + + /** + Returns the length of complete curve + */ + inline qreal arcLength() const { return arcLength( maximumCurveParameter() ); } + + /** + Create instance of class \c PathRasterer to rasterize the spline curve. After usage the + caller should use delete to free memory and avoid memory leaks. This methode is pure virtual + and has to be override by the derived class. + + @see PathRasterer + */ + virtual PathRasterer * createPathRaster() const = 0; + + }; /* Curve */ + + + /** + @class SmoothLine curve.h + @brief Class to calculate a ellipse. A angle have to be defined in RAD in mathematical manner (counter clockwise and (1,0) is zero angle) + */ + class SITUATIONMAP_EXPORT Ellipse : public Curve + { + public: + + Ellipse() = default; + + Ellipse( QPointF center, qreal rx, qreal ry, qreal phi, qreal sectorStart = 0, qreal sectorEnd = MathConst::pi_two ); + + virtual ~Ellipse() = default; + + qreal maximumCurveParameter() const override; + qreal minimumCurveParameter() const override; + + QPointF positionOnCurve ( qreal t ) const override; + QPointF directionOnCurve( qreal t ) const override; + + qreal arcLength( qreal t ) const override; + qreal arcLength( qreal tmin, qreal tmax ) const override; + + PathRasterer * createPathRaster() const override; + + protected: + + QPointF _center; + qreal _radiusX; + qreal _radiusY; + qreal _eccentricity2; + qreal _sinPhi; + qreal _cosPhi; + qreal _phiSectorStart; + qreal _phiSectorEnd; + + }; /* Ellipse */ + + + /** + @class SmoothLine curve.h + @brief Class to calculate a line with rounded verticies based on spline functions + */ + + class SITUATIONMAP_EXPORT SmoothLine : public Curve + { + public: + + /** + @name Construction & Destruction + @{ + */ + SmoothLine() = default; + + explicit SmoothLine( const QVector & vertexVector, bool closedCurve = false ); + + virtual ~SmoothLine() = default; + /** @} */ + + /** + Defines the predicated polyline + @param vertexVector verticies of the polyline + @param closedCurve if \c closedCurve is true the polyline is closed to a polygon + */ + void vertexVector( const QVector & vertexVector, bool closedCurve = false ); + + + qreal maximumCurveParameter() const override; + qreal minimumCurveParameter() const override { return 0.0; } + + QPointF positionOnCurve ( qreal t ) const override; + QPointF directionOnCurve( qreal t ) const override; + + qreal arcLength( qreal t ) const override; + + PathRasterer * createPathRaster() const override; + + protected: + + bool _closedCurve; + QVector _vertexVector; + + }; /* SmoothLine */ + + + /** + @class HermitSpline curve.h + @brief Class to calculate 2D hermit spline curves + */ + class SITUATIONMAP_EXPORT HermiteSpline : public Curve + { + public: + + /** + @name Construction & Destruction + @{ + */ + HermiteSpline() = default; + + HermiteSpline( const QVector & vertexVector + , const QVector & tangentVector + , bool closedCurve = false ); + + ~HermiteSpline() = default; + /** @} */ + + /** + Defines the gemeotry of the hermite spline + @param vertexVector verticies to define the hermite spline + @param tangentVector tangents to define the hermite spline + @param closedCurve if \c closedCurve is true the spline curve is closed + */ + virtual void vertexVector( const QVector & vertexVector + , const QVector & tangentVector + , bool closedCurve = false ); + + + qreal maximumCurveParameter() const override; + qreal minimumCurveParameter() const override { return 0.0; } + + QPointF positionOnCurve ( qreal t ) const override; + QPointF directionOnCurve( qreal t ) const override; + + qreal arcLength( qreal t ) const override; + + PathRasterer * createPathRaster() const override; + + private: + + bool _closedCurve; + QVector _vertexVector; + QVector _tangentVector; + + }; /* HermiteSpline */ + + + /** + @class BezierSpline curve.h + @brief Class to calculate 2D bezier spline curves + */ + class SITUATIONMAP_EXPORT BezierSpline : public HermiteSpline + { + public: + + /** + @name Construction & Destruction + @{ + */ + BezierSpline() = default; + + explicit BezierSpline( const QVector & ctrlPntVector, bool closedCurve = false ); + + ~BezierSpline() = default; + /** @} */ + + + /** + Defines the control points of the bezier curve + @param ctrlPntVector control points of bezier curve + @param closedCurve if \c closedCurve is true the spline curve is closed + */ + void controlPointVector( const QVector & ctrlPntVector, bool closedCurve = false ); + + + /** + Don't call vertexVector methode from class HermiteSpline. It is not defined for bezier curves. + If you call it anyway, you will get an execption. + */ + void vertexVector( const QVector & vertexVector + , const QVector & tangentVector + , bool closedCurve = false ) override; + + PathRasterer * createPathRaster() const override; + + }; /* BezierSpline */ + + +} } // namespace SituationMap::MathUtils diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/geometry_engine.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/geometry_engine.cpp new file mode 100755 index 000000000..6ac626a09 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/geometry_engine.cpp @@ -0,0 +1,409 @@ +/** +** +** @RDE_HEADER@ +** Implementation of 2d matrix operations +** $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 .........: +** +** 22.06.2016, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + + +#include "geometry_engine.h" + + +#include +#include +#include +#include + +#include + +// ESRI +#include +#include +//#include +#include +#include +#include +#include + +#include +#include +#include + + +namespace SituationMap { namespace MathUtils +{ + // + // Line + // + + GeometryEngine::Geometry *GeometryEngine::createLine(Map *map, const QPointF &p1, const QPointF &p2) + { + QVector vertices; + vertices << p1 << p2; + + return SituationMap::MathUtils::GeometryEngine::createPolyline(map, vertices); + } // GeometryEngine::Geometry *GeometryEngine::createLine( Map * map, QPointF p1, QPointF p2 ) + + + template + inline GeometryEngine::Geometry * createPolyline(Map * map, const T & verticies) + { + MapImpl * mapImpl = dynamic_cast(map); + if (! mapImpl) + return nullptr; + + SituationMap::CoordTranslationFunctor *mapCtFunctor = map->coordTranslationFunctor(); + if (! mapCtFunctor) + return nullptr; + + Esri::ArcGISRuntime::PolylineBuilder builder ( mapImpl->getEsriSpatialReference() ); + + for (auto pit = verticies.cbegin(); pit != verticies.cend(); ++pit) + { + const QPointF p(mapCtFunctor->geographicToMap(*pit)); + builder.addPoint(p.x(),p.y()); + } + + Esri::ArcGISRuntime::Polyline * esriPolyline = new Esri::ArcGISRuntime::Polyline( builder.toGeometry() ); + + return esriPolyline; + } // GeometryEngine::Geometry * createPolyline(Map * map, const T & verticies) + + + + GeometryEngine::Geometry *GeometryEngine::createPolyline(Map *map, const QVector &vertices ) + { + return SituationMap::MathUtils::createPolyline>(map, vertices); + } // GeometryEngine::Geometry * GeometryEngine::createPolyline( Map * map, const QVector & verticies ) + + + // + // Polygon + // + + template + inline GeometryEngine::Geometry *createPolygon(Map *map, const T &verticies) + { + MapImpl * mapImpl = dynamic_cast(map); + + if (! mapImpl) return nullptr; + + SituationMap::CoordTranslationFunctor * mapCtFunctor = map->coordTranslationFunctor(); + + if (! mapCtFunctor) return nullptr; + + Esri::ArcGISRuntime::PolygonBuilder builder( mapImpl->getEsriSpatialReference() ); + + for (auto pit = verticies.cbegin(); pit != verticies.cend(); ++pit ) + { + const QPointF p(mapCtFunctor->geographicToMap(*pit)); + builder.addPoint(p.x(),p.y()); + } + + Esri::ArcGISRuntime::Polygon * esriPolygon = new Esri::ArcGISRuntime::Polygon( builder.toGeometry() ); + + return esriPolygon; + } // GeometryEngine::Geometry * GeometryEngine::createPolygon( Map * map, const QVector & verticies ) + + + GeometryEngine::Geometry * GeometryEngine::createPolygon(Map * map, const QVector & vertices ) + { + return SituationMap::MathUtils::createPolygon>( map, vertices ); + } // GeometryEngine::Geometry * GeometryEngine::createPolygon( Map * map, const QVector & verticies ) + + + // + // cleanup etc. + // + + void GeometryEngine::deleteGeometry(Geometry *geom) + { + delete geom; + } // void GeometryEngine::deleteGeoemtry( Geometry * geom ) + + + bool GeometryEngine::contains( AbstractMapObject *mapObj, const Geometry &hull) + { + if (nullptr == mapObj) return false; + return mapObj->geometryIsInHull( hull ); + + } //bool GeometryEngine::contains( AbstractMapObject * mapObj, const Geometry & hull ) + + + bool GeometryEngine::contains(const Geometry &geom, const Geometry &hull) + { + if (geom.isEmpty() || hull.isEmpty()) + return false; + return Esri::ArcGISRuntime::GeometryEngine::contains(hull, geom); + } // bool GeometryEngine::contains(const Geometry & geom, const Geometry & hull) + + + bool GeometryEngine::intersect( AbstractMapObject *mapObj, const Geometry &intersector) + { + if (! mapObj) + return false; + return mapObj->geometryIntersect(intersector); + } // bool GeometryEngine::intersect( const Geometry & inputGeom, const Geometry & intersector ) + + + bool GeometryEngine::intersect(const Geometry &inputGeom, const Geometry &intersector) + { + if (inputGeom.isEmpty() || intersector.isEmpty()) + return false; + +#ifdef DEBUGGERTEST + if (inputGeom.type() == EsriRuntimeQt::GeometryType::Polyline) + { + const EsriRuntimeQt::Polyline & l = inputGeom; + int numPoints = l.pointCount(); + + for (int i = 0; i < numPoints; ++i) + { + QPointF p = l.qxy(i); + __asm int 3 + } + } + + if (intersector.type() == EsriRuntimeQt::GeometryType::Line) + { + const EsriRuntimeQt::Line & l = intersector; + double startx = l.startX(); + double starty = l.startY(); + double endx = l.endX(); + double endy = l.endY(); + + __asm int 3 + } +#endif + + return Esri::ArcGISRuntime::GeometryEngine::crosses(inputGeom, intersector); + } // bool GeometryEngine::intersect( const Geometry & inputGeom, const Geometry & intersector ) + +QVector GeometryEngine::parallelTranslation(SituationMap::CoordTranslationFunctor *mapCtFunctor, const QVector &line, double distance) +{ + /* ~ first lokal functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + auto length = [](const QPointF &v) -> double { return sqrt(v.x()*v.x() + v.y()*v.y()); }; + + + auto normalize = [](const QPointF &v) -> QPointF { return v / sqrt(v.x()*v.x() + v.y()*v.y()); }; + + + auto intersection = [](const QPointF &p1, const QPointF &r1, const QPointF &p2, const QPointF &r2) -> QPointF + { + double f = r2.y()*r1.x() - r1.y()*r2.x(); + + double a = (p1.x() + r1.x()) * p1.y() - p1.x() * (p1.y() + r1.y()); + double b = (p2.x() + r2.x()) * p2.y() - p2.x() * (p2.y() + r2.y()); + + double sx = (r2.x() * a - r1.x() * b) / f; + double sy = (-r1.y() * b + r2.y() * a) / f; + + return QPointF(sx, sy); + + }; // intersection + + + auto orientation = [](const QPointF &p, const QPointF &q, const QPointF &r) -> int + { + // See https://www.geeksforgeeks.org/orientation-3-ordered-points/ + // for details of below formula. + double f = (q.y() - p.y()) * (r.x() - q.x()) - (q.x() - p.x()) * (r.y() - q.y()); + + if (qFuzzyIsNull(f)) return 0; // colinear + + return (f > 0) ? 1 : 2; // clock or counterclock wise + }; // orientation + + + auto testIntersection = [&orientation, &intersection, &normalize](const QPointF &p1, const QPointF &q1, const QPointF &p2, const QPointF &q2) -> bool + { + // Find the four orientations needed for general and + // special cases + int o1 = orientation(p1, q1, p2); + int o2 = orientation(p1, q1, q2); + int o3 = orientation(p2, q2, p1); + int o4 = orientation(p2, q2, q1); + + // General case + if (o1 != o2 && o3 != o4) + { + const QPointF & n1 = q1 - p1; + const QPointF & n2 = q2 - p2; + const QPointF & s = intersection(p1, normalize(n1), p2, normalize(n2)); + + double f1 = (!qFuzzyIsNull(n1.x())) ? (s.x() - p1.x()) / n1.x() : (s.y() - p1.y()) / n1.y(); + double f2 = (!qFuzzyIsNull(n2.x())) ? (s.x() - p2.x()) / n2.x() : (s.y() - p2.y()) / n2.y(); + + return ((f1 >= 0) && (f1 <= 1.0) && (f2 >= 0) && (f2 <= 1.0)); + } + + return false; // Doesn't fall in any of the above cases + + }; // testIntersection + + + /* ~ now let's do the job ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + if (line.size() < 2) + { + // Nothing to do --> return input line + return line; + } + + QVector transformedLine; + + transformedLine.reserve(line.size()); + + for (auto &point : std::as_const(line)) + { + // Let's transform the coordinates to maps coordinate system ... + transformedLine << mapCtFunctor->geographicToMap(point); + } + + struct LineSegment + { + QPointF m_p1; + QPointF m_p2; + QPointF m_t; + + LineSegment() = default; + LineSegment(const QPointF &p1, const QPointF &p2, const QPointF &t) + : m_p1(p1), m_p2(p2), m_t(t) + {} + }; + + // build single line segments to translate along it's normal + QVector multiSegmLine; + + multiSegmLine.reserve(transformedLine.size() - 1); + + for (int i = 1; i < transformedLine.size(); ++i) + { + const QPointF & p1 = transformedLine[i - 1]; + const QPointF & p2 = transformedLine[i]; + const QPointF & t = normalize(p2 - p1); + QPointF n = QPointF(t.y()*distance, -t.x()*distance); + multiSegmLine << LineSegment(p1 + n, p2 + n, t); + } + + + transformedLine.clear(); + transformedLine << multiSegmLine.first().m_p1; + + for (int i = 1; i < multiSegmLine.size(); ++i) + { + const LineSegment & l1 = multiSegmLine[i - 1]; + const LineSegment & l2 = multiSegmLine[i]; + + const QPointF & dt = l1.m_t - l2.m_t; + + transformedLine << ((qFuzzyIsNull(dt.x()) && qFuzzyIsNull(dt.y())) ? l1.m_p2 : intersection(l1.m_p2, l1.m_t, l2.m_p1, l2.m_t)); + } + + transformedLine << multiSegmLine.last().m_p2; + + // check result + for (int i = 1; i < transformedLine.size(); ++i) + { + const QPointF & p1 = transformedLine[i - 1]; + const QPointF & p2 = transformedLine[i]; + + if (length(p2 - p1) < distance) + { + // failure segment is to short relative to distance + return QVector(); + } + for (int j = 1; j < transformedLine.size(); ++j) + { + if (j == i) continue; + + const QPointF & g1 = transformedLine[j-1]; + const QPointF & g2 = transformedLine[j]; + + if (testIntersection(p1, p2, g1, g2)) + { + // failure resulting line has self intersections + return QVector(); + } + } + } + + QVector pLine; + + // Transform coordinates back to geographic ... + pLine.reserve(transformedLine.size()); + for (auto &p : std::as_const(transformedLine)) + { + pLine << mapCtFunctor->mapToGeographic(p); + } + + return pLine; +} +// QVector GeometryEngine::parallelTranslation(SituationMap::CoordTranslationFunctor *mapCtFunctor, const QVector &line, double distance) + +QVector GeometryEngine::buffer(AbstractMapObject *mapObj, double distance) +{ + if (!mapObj) + return QVector(); + + AbstractMapObjectImpl *mapObjImpl = dynamic_cast(mapObj); + + if (!mapObjImpl) + return QVector(); + + + const Esri::ArcGISRuntime::Geometry &esriGeometry = mapObjImpl->getEsriGeometry(); + + // Esri::ArcGISRuntime::GeometryEngine::buffer -> works for 2D + // Esri::ArcGISRuntime::GeometryEngine::bufferGeodetic -> works for 2D and 3D!? + const Esri::ArcGISRuntime::Polygon polyline = Esri::ArcGISRuntime::GeometryEngine::bufferGeodetic(esriGeometry, + distance, + Esri::ArcGISRuntime::LinearUnitId::Meters, + 1, + Esri::ArcGISRuntime::GeodeticCurveType::Geodesic); + if (polyline.parts().isEmpty()) + return QVector(); + + + const Esri::ArcGISRuntime::ImmutablePart &immutablePart = polyline.parts().part(0); + const int size = immutablePart.pointCount(); + + QVector resultVec; + resultVec.reserve(size); + for (int i = 0; i < size; ++i) + { + const Esri::ArcGISRuntime::Point &esriP = immutablePart.point(i); + resultVec.append(Point3D(esriP.x(), esriP.y(), esriP.z())); + } + + // qCritical() << " resultVec = " << resultVec.size(); + // qCritical() << " res = " << polyline.toJson(); + + return resultVec; +} + + +} } // namespace SituationMap::MathUtils + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/geometry_engine.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/geometry_engine.h new file mode 100755 index 000000000..56bef725d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/geometry_engine.h @@ -0,0 +1,86 @@ +/** +** +** @RDE_HEADER@ +** 2D matrix operations +** $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 .........: +** +** 22.06.2016, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include + +#include +#include + +namespace Esri { +namespace ArcGISRuntime { + class Geometry; +} } + + +namespace SituationMap +{ + class Map; + class AbstractMapObject; + class CoordTranslationFunctor; + + namespace MathUtils + { + + class SITUATIONMAP_EXPORT GeometryEngine + { + + public: + + using Geometry = Esri::ArcGISRuntime::Geometry; + + + static Geometry *createLine (Map *map, const QPointF &p1, const QPointF &p2); + static Geometry *createPolyline(Map *map, const QVector &vertices); + static Geometry *createPolygon (Map *map, const QVector &vertices); + + static void deleteGeometry(Geometry *geom); + + static bool contains(const Geometry &geom, const Geometry &hull); + static bool contains(AbstractMapObject *mapObj, const Geometry &hull); + + static bool intersect(const Geometry &inputGeom, const Geometry &intersector); + static bool intersect(AbstractMapObject *mapObj , const Geometry &intersector); + + /** + Calculating a parallel line. + @param mapCtFunctor CoordTranslationFunctor to translate coordinates from geographic to map coordinate system and vis versa. + @param line Line to use as input, the coordinates are geograpihc coordinates. + @param distance Distance in meter. + */ + static QVector parallelTranslation(SituationMap::CoordTranslationFunctor *mapCtFunctor, const QVector &line, double distance ); + + static QVector buffer(AbstractMapObject *mapObj, double distance); + + + }; // GeometryEngine + + + } // namespace MathUtils +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/math_const.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/math_const.h new file mode 100755 index 000000000..79be0f391 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/math_const.h @@ -0,0 +1,47 @@ +/** +** +** @RDE_HEADER@ +** Math const +** $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 .........: +** +** 04.12.2015, JT : First edition +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include + +namespace MathConst +{ + + constexpr double pi = M_PI; + constexpr double pi_two = pi * 2; + constexpr double pi_half = M_PI_2; + constexpr double pi_quater = M_PI_4; + + constexpr double EarthRadius = 6371001.0; + + constexpr double WGS84_Major_a = 6378137.0; // WGS84 Ellipsoid, a value (Radius am Äquator, große Halbachse) + constexpr double WGS84_Minor_b = 6378137.0*( 1.0 - 1.0/298.257223563); + constexpr double WGS84_SQR_EXC = ((WGS84_Major_a * WGS84_Major_a) - (WGS84_Minor_b * WGS84_Minor_b))/(WGS84_Major_a * WGS84_Major_a); + +} // namespace MathConst diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/matrix2d.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/matrix2d.cpp new file mode 100755 index 000000000..2af58b453 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/matrix2d.cpp @@ -0,0 +1,114 @@ +/** +** +** @RDE_HEADER@ +** Implementation of 2d matrix operations +** $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 .........: +** +** 22.06.2016, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "matrix2d.h" + +#include + + +namespace SituationMap { namespace MathUtils +{ + + QPointF Matrix2D::apply( const QPointF & v ) const + { + QGenericMatrix<1,3,qreal> vM; + + vM(0,0) = v.x(); + vM(1,0) = v.y(); + vM(2,0) = 1.0; + + QGenericMatrix<1,3,qreal> result = m_matrix * vM; + + qreal x = result(0,0) / result(2,0); + qreal y = result(1,0) / result(2,0); + + return QPointF(x,y); + + } /* Matrix2D::apply */ + + + void Matrix2D::putTranslation( const QPointF & t ) + { + QGenericMatrix<3,3,qreal> tM; + + tM(0,2) = t.x(); + tM(1,2) = t.y(); + + m_matrix = tM * m_matrix; + } /* Matrix2D::putTranslation */ + + + void Matrix2D::putScale( const QPointF & s) + { + QGenericMatrix<3,3,qreal> sM; + + sM(0,0) = s.x(); + sM(1,1) = s.y(); + + m_matrix = sM * m_matrix; + } /* Matrix2D::putScale */ + + + void Matrix2D::putScale( const QPointF & s, qreal sinPhi, qreal cosPhi ) + { + putRotate(QPointF(cosPhi,-sinPhi)); + putScale(s); + putRotate(sinPhi,cosPhi); + } /* Matrix2D::putScale */ + + + void Matrix2D::putRotate( qreal sinPhi, qreal cosPhi ) + { + QGenericMatrix<3,3,qreal> rM; + + rM(0,0) = cosPhi; + rM(0,1) = sinPhi; + rM(1,0) = -sinPhi; + rM(1,1) = cosPhi; + + m_matrix = rM * m_matrix; + } /* Matrix2D::putRotate */ + + + std::ostream & operator<< ( std::ostream & stream, const Matrix2D & mat ) + { + + stream << "Matrix2D:" << std::endl; + for (int r = 0; r < 3; ++r) + { + for (int c = 0; c < 3; ++c) + { + stream << "[" << r << "," << c << "] = " << mat(r,c) << " "; + } + stream << std::endl; + } + return stream; + } // std::ostream & operator<< ( std::ostream & stream, const Matrix2D & mat ) + + +} } // namespace SituationMap::MathUtils diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/matrix2d.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/matrix2d.h new file mode 100755 index 000000000..f286fd5df --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/matrix2d.h @@ -0,0 +1,413 @@ +/** +** +** @RDE_HEADER@ +** 2D matrix operations +** $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 .........: +** +** 22.06.2016, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include + +#include + +#include + +#include + + +namespace SituationMap { namespace MathUtils +{ + + /** + @class Matrix2D matrix2d.h + @brief The class implements a 3x3 matrix for calculating 2D coordinate transformation with homogeneouse coordinates. + + One of the most common and important tasks in computer graphics is to transform the coordinates ( position, + orientation, and size ) of either objects within the graphical scene or the camera that is viewing the scene. + It is also frequently necessary to transform coordinates from one coordinate system to another, ( e.g. world + coordinates to viewpoint coordinates to screen coordinates. ) + + All of these transformations can be efficiently and succintly handled using some simple matrix representations, + which we will see can be particularly useful for combining multiple transformations into a single composite + transform matrix. The base transformations for translating, scaling and rotating coordinates can be combined in + an arbitrary manner. + + Translation in 2D + + Point (X,Y) is to be translated by amount Dx and Dy to a new location (X',Y'). + + \code + X' = Dx + X + Y' = Dy + Y + \endcode + + or P' = T + P where + + \code + P' = | X' | + | Y' | + + T = | Dx | + | Dy | + + P = | X | + | Y | + \endcode + + Scaling in 2D + + Point (X,Y) is to be scaled by amount Sx and Sy to location (X',Y'). + + \code + X' = Sx * X + Y' = Sy * Y + \endcode + + or P' = S * P where + + \code + P' = | X' | + | Y' | + + S = | Sx 0 | + | 0 Sy | + + P = | X | + | Y | + \endcode + + Scaling is performed about the origin (0,0) not about the center of the line/polygon/whatever. + + - Scale > 1 enlarge the object and move it away from the origin. + - Scale = 1 leave the object alone + - Scale < 1 shrink the object and move it towards the origin. + - uniform scaling: Sx = Sy + - differential scaling Sx != Sy -> alters proportions + + + Rotation in 2D + + point (X,Y) is to be rotated about the origin by angle theta to location (X',Y') + + \code{.unparsed} + X' = X * cos(theta) - Y * sin(theta) + Y' = X * sin(theta) + Y * cos(theta) + \endcode + + or P' = R * P where + + \code + P' = | X' | + | Y' | + + R = | cos(theta) -sin(theta) | + | sin(theta) cos(theta) | + + P = | X | + | Y | + \endcode + + Note that this does involve sin and cos which are much more costly than addition or multiplication. + + Rotation is performed about the origin \f$(0,0)\f$ not about the center of the line/polygon/whatever. + + + Derivation of the 2D Rotation Equations + + Where does this matrix come from? + + (X,Y) is located r away from (0,0) at a CCW angle of phi from the X axis. + (X',Y') is located r away from (0,0) at a CCW angle of theta+phi from the X axis. + + Since rotation is about the origin, (X',Y') must be the same distance from the origin as (X,Y). + + From trigonometry we have: + + \code + X = r * cos(phi) + Y = r * sin(phi) + \endcode + + and + + \code + X' = r * cos(theta+phi) + Y' = r * sin(theta+phi) + \endcode + + Now making use of the following trigonometric identities: + + \code + cos(a+b) = cos(a) * cos(b) - sin(a) * sin(b) + sin(a+b) = sin(a) * cos(b) + cos(a) * sin(b) + \endcode + + and substituting in for the above equations for X' and Y', we get: + + \code + X' = r * cos(theta) * cos(phi) - r * sin(theta) * sin(phi) + Y' = r * sin(theta) * cos(phi) + r * cos(theta) * sin(phi) + \endcode + + Then we substitute in X and Y from their definitions above, and the final result simplifies to: + + \code + X' = X * cos(theta) - Y * sin(theta) + Y' = X * sin(theta) + Y * cos(theta) + \endcode + + Homogeneous Coordinates in 2 Dimensions + + Scaling and rotations are both handled using matrix multiplication, which can be combined as we + will see shortly. The translations cause a difficulty, however, since they use addition instead + of multiplication. We want to be able to treat all 3 transformations (translation, scaling, + rotation) in the same way - as multiplications. The solution is to give each point a third + coordinate (X,Y,W), which will allow translations to be handled as a multiplication also. (Note + that we are not really moving into the third dimension yet. The third coordinate is being added to + the mathematics solely in order to combine the addition and multiplication of 2-D coordinates.) + + Two triples (X,Y,W) and (X',Y',W') represent the same point if they are multiples of each other e.g. + (1,2,3) and (2,4,6). + + At least one of the three coordinates must be nonzero. If W is 0 then the point is at infinity. This + situation will rarely occur in practice in computer graphics. If W is nonzero we can divide the triple + by W to get the cartesian coordinates of X and Y which will be identical for triples representing the + same point (X/W,Y/W,1). This step can be considered as mapping the point from 3-D space onto the + plane W=1. Conversely, if the 2-D cartesian coordinates of a point are known as (X,Y), then the + homogenous coordinates can be given as (X,Y,1) + + So, how does this apply to translation, scaling, and rotation of 2D coordinates? + + + Translation of 2D Homogenous Coordinates + + point (X,Y) is to be translated by amount Dx and Dy to location (X',Y') + + \code + X' = Dx + X + Y' = Dy + Y + \endcode + + or P' = T * P where + + \code + | X' | + P' = | Y' | + | 1 | + + | 1 0 Dx | + T = | 0 1 Dy | = T(Dx,Dy) + | 0 0 1 | + + | X | + P = | Y | + | 1 | + \endcode + + Hey Look! Translation is now a multiplication instead of an addition! + + Scaling of 2D Homogenous Coordinates + + P' = S * P where + + \code + P' = | X' | + | Y' | + | 1 | + + | Sx 0 0 | + S = | 0 Sy 0 | = S(Sx,Sy) + | 0 0 1 | + + | X | + P = | Y | + | 1 | + \endcode + + Rotation of 2D Homogenous Coordinates + + P' = R * P where + + \code + | X' | + P' = | Y' | + | 1 | + + | cos(theta) -sin(theta) 0 | + R = | sin(theta) cos(theta) 0 | = R(theta) + | 0 0 1 | + + | X | + P = | Y | + | 1 | + \endcode + + Composition of 2D Transformations + + There are many situations in which the final transformation of a point is a combination of several + (often many) individual transformations. For example, the position of the finger of a robot might be + a function of the rotation of the robots hand, arm, and torso, as well as the position of the robot + on the railroad train and the position of the train in the world, and the rotation of the planet + around the sun, and . . . + + Applying each transformation individually to all points in a model would take a lot of time. Instead + of applying several transformations matrices to each point we want to combine the transformations to + produce 1 matrix which can be applied to each point. + + In the simplest case we want to apply the same type of transformation (translation, rotation, scaling) + more than once. + + - translation is additive as expected + - scaling is multiplicative as expected + - rotation is additive as expected + + But what if we want to combine different types of transformations? + + A very common reason for doing this is to rotate a polygon about an arbitrary point (e.g. the center of + the polygon) rather than around the origin. + + - Translate so that P1 is at the origin T(-Dx,-Dy) + - Rotate R(theta) + - Translate so that the point at the origin is at P1 T(Dx,Dy) + + Note the order of operations here is right to left: + + \code + P' = T(Dx,Dy) * R(theta) * T(-Dx,-Dy) * P + \endcode + + is equal to + + \code + P' = T(Dx,Dy) * { R(theta) * [ T(-Dx,-Dy) * P ] } + \endcode + + is equal to + + \code + P' = [ T(Dx,Dy) * R(theta) * T(-Dx,-Dy) ] * P + \endcode + + The matrix that results from these 3 steps can then be applied to all of the points in the polygon. Another + common reason for doing this is to scale a polygon about an arbitrary point (e.g. the center of the polygon) + rather than around the origin. + + - Translate so that P1 is at the origin + - Scale + - Translate so that the point at the origin is at P1 + + A detailed description can be also found in J.D.Foley, A.Van Dam "Fundamentals of Interactive Computer Graphics" + + */ + class SITUATIONMAP_EXPORT Matrix2D + { + public: + + /** + @name Construction, Destruction and Assign + @{ + */ + Matrix2D() = default; + explicit Matrix2D(const QGenericMatrix<3, 3, qreal> & matrix) : m_matrix(matrix) {;} + + Matrix2D & operator = ( const QGenericMatrix<3, 3, qreal> & matrix) { m_matrix = matrix; return *this; } + + /** @} */ + + /** + Set matrix to identity. + */ + void setToIdentity() { m_matrix.setToIdentity(); } + + /** + Returns true if this matrix is the identity; false otherwise. + */ + bool isIdentity() const { return m_matrix.isIdentity(); } + + /** + Returns true if a translation is set, otherwise false + */ + bool isTranslation() const { return (!qFuzzyCompare( m_matrix(0,2),0.0) || !qFuzzyCompare(m_matrix(1,2),0.0)); } + + /** + Returns true if a rotation is set, otherwise false + */ + bool isRotation() const { return (!qFuzzyCompare( m_matrix(0,1),0.0) || !qFuzzyCompare(m_matrix(1,0),0.0)); } + /** + Apply the coordinate transformation to one coordinate and returns the result. + */ + QPointF apply( const QPointF & ) const; + + /** + Add translation to the current matrix + */ + void putTranslation( const QPointF & ); + + /** + Add scaling to the current matrix. + */ + void putScale( const QPointF & ); + + + /** + Add scaling to the current matrix. + */ + void putScale(const QPointF & s, const QPointF & oVec) { putScale(s, oVec.y(), oVec.x() ); } + void putScale( const QPointF & s, qreal sinPhi, qreal cosPhi ); + + /** + Add rotation to the current matrix. + @param oVec vector defining the rotation regarding to (1,0). The length of \a oVec has to be 1 (unit vector) + */ + void putRotate( const QPointF & oVec ) { putRotate(oVec.y(),oVec.x()); } + void putRotate( qreal sinPhi, qreal cosPhi ); + + /** + Returns underlying QGenericMatrix. + */ + QGenericMatrix<3,3,qreal> & operator()() { return m_matrix; } + const QGenericMatrix<3,3,qreal> & operator()() const { return m_matrix; } + + qreal operator()(int row, int col) const { return m_matrix(row,col); } + qreal & operator()(int row, int col) { return m_matrix(row,col); } + + protected: + + QGenericMatrix<3,3,qreal> m_matrix; + + }; // Matrix2D + + /** + Returns the product of N*M + */ + inline Matrix2D operator* ( const Matrix2D & N, const Matrix2D & M ) { return Matrix2D( N()*M() ); } + + + /** + Output operator + */ + SITUATIONMAP_EXPORT std::ostream & operator<< (std::ostream & stream, const Matrix2D & mat ); + +} } // namespace SituationMap::MathUtils diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/path_rasterer.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/path_rasterer.cpp new file mode 100755 index 000000000..3a8e6850a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/path_rasterer.cpp @@ -0,0 +1,99 @@ +/** +** +** @RDE_HEADER@ +** Rasterize functional path geometries to polylines +** $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 .........: +** +** 12.08.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "path_rasterer.h" +#include + +namespace SituationMap { namespace MathUtils +{ + PathRasterer::PathRasterer() : _eps(1.0), _maxVertexCount(0) + { + } /* PathRasterer::PathRasterer */ + + + void PathRasterer::rasterize(qreal tMin, qreal tMax, QVector & vertexList) { exeRasterize( tMin, tMax, vertexList ); } + void PathRasterer::rasterize(qreal tMin, qreal tMax, QList & vertexList) { exeRasterize( tMin, tMax, vertexList ); } + + + template void PathRasterer::exeRasterize( qreal tMin, qreal tMax, List & vertexList ) + { + vertexList.clear(); + + if (tMin >= tMax) return; + + QPointF vMin, vMax; + + function( tMax, vMax ); + function( tMin, vMin ); + vertexList.append(vMin); + splitSegment( vMin, tMin, vMax, tMax, _maxVertexCount, vertexList ); + vertexList.append(vMax); + } /* PathRasterer::exeRasterize */ + + + template void PathRasterer::splitSegment( const QPointF & v0 + , qreal t0 + , const QPointF & v1 + , qreal t1 + , unsigned int maxVertexCount + , List & vertexList ) + { + if (maxVertexCount < 1) + return; + + QPointF v2; + qreal t2 = (t0+t1) * 0.5; + + function( t2, v2 ); + + auto length = []( const QPointF & v ) -> qreal { return sqrt(v.x()*v.x() + v.y()*v.y()); }; + + qreal l0 = length(v1-v0); + qreal l1 = length(v1-v2); + qreal l2 = length(v2-v0); + + + /* ------------------------------------------------------------------------- + * Berechnen der Höhe des Dreiecks über l0 für den Vergleich mit + * dem Schwellwert (siehe Kosinussatz, Trigonometrische Funktionen, etc.). + * ------------------------------------------------------------------------- */ + + qreal cosPhi = (l2*l2+l0*l0-l1*l1)/(2.0*l2*l0); + qreal h_eps = l2*sqrt(1.0-cosPhi*cosPhi); + + // (maxVertexCount > 0) -> see pre-condition + if (((std::isnan(h_eps)) || (h_eps > _eps))) + { + maxVertexCount /= 2; + splitSegment( v0, t0, v2, t2, maxVertexCount, vertexList ); + vertexList.append( v2 ); + splitSegment( v2, t2, v1, t1, maxVertexCount, vertexList ); + } + } /* PathRasterer::splitSegment */ + +} } // namespace SituationMap::MathUtils diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/path_rasterer.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/path_rasterer.h new file mode 100755 index 000000000..9f110fda5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/path_rasterer.h @@ -0,0 +1,94 @@ +/** +** +** @RDE_HEADER@ +** Rasterize functional path geometries to polylines +** $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 .........: +** +** 12.08.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + + +#include +#include +#include +#include + +namespace SituationMap { namespace MathUtils +{ + + /** + @class PathRasterer path_rasterer.h + @brief Base class to rasterize parametric functions, like splines + + Base class to rasterize parametric functions, like splines, with an interval halving method + to approximate the function by a discrete polyline. + + @see Ellipse, Spline, SmoothLine, HermiteSpline, BezierSpline + */ + class SITUATIONMAP_EXPORT PathRasterer + { + public: + + PathRasterer(); + virtual ~PathRasterer() = default; + + /** + Defines a threshold values to decide ending convergence + */ + inline void epsilon( qreal eps ) { _eps = eps; } + + /** + Defines the maximum of calculated verticies if \c epsilon doesn't terminate the convergence operation + */ + inline void maxVertexCount(unsigned int max) { _maxVertexCount = max; } + + /** + Method to override to define the parametric function + */ + virtual void function( qreal t, QPointF & result ) = 0; + + /** + Creating a vertex vector with an interval halving method to approximate a parametric function by a polyline + */ + void rasterize(qreal tMin, qreal tMax, QVector & vertexList); + + void rasterize(qreal tMin, qreal tMax, QList & vertexList); + + protected: + + qreal _eps; + unsigned int _maxVertexCount; + + template void exeRasterize( qreal tMin, qreal tMax, List & vertexList ); + + template void splitSegment( const QPointF & v0 + , qreal t0 + , const QPointF & v1 + , qreal t1 + , unsigned int maxVertexCount + , List & vertexList ); + + }; /* PathRasterer */ + +} } // namespace SituationMap::MathUtils diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/simple_helper.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/simple_helper.cpp new file mode 100755 index 000000000..3b5fe05ee --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/simple_helper.cpp @@ -0,0 +1,207 @@ +/** + * @RDE_HEADER@ + * Helper functions related to CoordinateTranslation + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "simple_helper.h" +#include "math_const.h" +#include "clipper.hpp" +#include +// Qt +#include +#include + +namespace SituationMap +{ +namespace MathUtils +{ + + +QPointF rotate(const QPointF &p, const QPointF ¢er, qreal sinA, qreal cosA) +{ + qreal x = p.x() - center.x(); + qreal y = p.y() - center.y(); + return QPointF(x * cosA - y * sinA + center.x(), y * cosA + x * sinA + center.y()); +} + + +QPointF rotate(const QPointF &p, const QPointF ¢er, qreal phi) { return rotate(p, center, sin(phi), cos(phi)); } + + +qreal getMathAngle(qreal sinPhi, qreal cosPhi) +{ + qreal phi = acos(cosPhi); + if (sinPhi < 0) phi = MathConst::pi_two - phi; + return phi; +} + + +qreal getMathAngle(const QPointF &oVec) { return getMathAngle(oVec.y(), oVec.x()); } + + +double getAngleDegreeNorth(const QPointF &p1, const QPointF &p2) +{ + double x = p2.x() - p1.x(); + double y = p2.y() - p1.y(); + + // Winkel der Geraden bestimmen + double alphaRad = atan2(y, x); + +// // Fuer Winkel > PI liefert atan2 math. rechts drehende (also negative) Werte. +// // Um Werte von 0-2PI zu bekommen muss deshalb hier von 2Pi abgezogen werden. + + alphaRad -= M_PI / 2.0; + alphaRad *= -1.0; + + if (alphaRad < 0.0) + alphaRad = 2 * M_PI + alphaRad; + + return (alphaRad * 180.0 / M_PI); +} + +QPointF getPointOnLine(const QPointF &p1, const QPointF &p2, double position_0_to_1) +{ + QPointF pRet; + + pRet.rx() = p1.x() + (p2.x() - p1.x()) * position_0_to_1; + pRet.ry() = p1.y() + (p2.y() - p1.y()) * position_0_to_1; + + return pRet; +} + +Point3D getPointOnLine(const Point3D &p1, const Point3D &p2, double position_0_to_1) +{ + Point3D pRet; + + pRet.rx() = p1.x() + (p2.x() - p1.x()) * position_0_to_1; + pRet.ry() = p1.y() + (p2.y() - p1.y()) * position_0_to_1; + pRet.rz() = p1.z() + (p2.z() - p1.z()) * position_0_to_1; + + return pRet; +} + +void rotatePoint(QPointF &pToBeRotated, const QPointF ¢er, double angleDegree) +{ + // angle to rad + double radAngle = ((M_PI) / -180.0) * angleDegree; + + // Translate + QPointF t = pToBeRotated - center; + + // Rotate + QPointF tmp; + tmp.rx() = t.x() * cos(radAngle) - t.y() * sin(radAngle); + tmp.ry() = t.x() * sin(radAngle) + t.y() * cos(radAngle); + + // Translate back and write result + pToBeRotated = tmp + center; +} + + + +QList mineFieldCenterLineToOutline(const QList ¢erLine, + double width) +{ + QList outline; + + // Clipper is using ints (typedef signed long long cInt;) + // The coordinate of centerline will have values much lower the the maximum. + // Therefore the factor can be 1000. So the resolution is bit enough (->Millimeter): + const int factorInt = 10; + const double factorDouble = 10.0; + + ClipperLib::ClipperOffset pathOffset; + ClipperLib::Path borderPath; // vector< int > + ClipperLib::Paths offsetPathVec; // vector< vector< int > > + + for (auto point : centerLine) + { + borderPath << ClipperLib::IntPoint(static_cast(point.x()*factorInt), + static_cast(point.y()*factorInt)); + } + + pathOffset.AddPath(borderPath, ClipperLib::JoinType::jtSquare, ClipperLib::EndType::etOpenButt); + pathOffset.Execute(offsetPathVec, static_cast(fabs(width)*factorInt)); + + for (auto path : offsetPathVec) + { + ClipperLib::Path::const_iterator it = path.begin(); + + for (; it != path.end(); ++it) + { + outline.append(QPointF(static_cast(it->X) / factorDouble, + static_cast(it->Y) / factorDouble) + ); + + } + } + + return outline; +} + +qreal getAngleDegree2North(qreal angleDeg) +{ + double rotationAngle = angleDeg < 360. ? angleDeg : std::fmod(angleDeg, 360.); + // set rotation to 0° North(12h) counter clock wise + rotationAngle = rotationAngle < 90. ? std::fabs(90 - rotationAngle) : std::fabs(360 - rotationAngle + 90) ; + return rotationAngle; +} + +qreal getAngleDegree2Math(qreal angleDeg) +{ + // set rotation to 0° math(3h) clockwise + const double rotationMath = angleDeg < 90. ? std::fabs(angleDeg - 90) : std::fabs(360 - angleDeg + 90) ; + return rotationMath; +} + +QPolygonF getCircleSectorPolygon(const QPointF ¢erPoint, qreal radius, double rotationAngleDeg, double centralAngleDeg) +{ + // | <-rotationAngle + // | .\ | / + const double angleRad = angle * LtsCoordTrans::deg2rad(); + + // relPosition des StartPoint (point on circle) + const QPointF position(cos(angleRad) * radius, sin(angleRad) * radius); + + // StartPoint einfügen + poly << centerUtm.geographic(position); + + // calculate all steps, inclusive EndPos + for (int step = 1; step<=steps; ++step) + { + double curAngle = angle + (step*singleIncrementDegree); + double curAngleRad = curAngle * LtsCoordTrans::deg2rad(); + poly << centerUtm.geographic( QPointF(cos(curAngleRad) * radius, sin(curAngleRad) * radius) ); + } // for + return poly; +} + +} // namespace SituationMap::MathUtils +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/simple_helper.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/simple_helper.h new file mode 100755 index 000000000..b70acd3bb --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/MathUtils/simple_helper.h @@ -0,0 +1,130 @@ +/** + * @RDE_HEADER@ + * Helper functions related to CoordinateTranslation + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Own +#include "situation_map_export.h" +#include + + +// Qt +#include +#include + + +// ESRI + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ +namespace MathUtils +{ + +/** + @brief Rotate a 2 dimensional point + @param p point to rotate + @param center center point of rotation + @param sinA sinus of rotation angle + @param cosA cosinus of rotation angle + */ +QPointF SITUATIONMAP_EXPORT rotate(const QPointF &p, const QPointF ¢er, qreal sinA, qreal cosA); + +/** + @brief Rotate a 2 dimensional point + @param p point to rotate + @param center center point of rotation + @param phi rotation angle in rad +*/ +QPointF SITUATIONMAP_EXPORT rotate(const QPointF &p, const QPointF ¢er, qreal phi); + +/** + * @brief Get math angle in radiant from orientation vector + * @param oVec orientation vector, must be normalized to length 1 + * @return mathematical angle in rad + */ +qreal SITUATIONMAP_EXPORT getMathAngle(const QPointF &oVec); + + +/** + * @brief Get math angle in radiant from sinus and cosinus + * @param sinPhi sinus of angle, is equal to y component of orientation vector + * @param cosPhi cosinus of angle, is equal to x component of orientation vector + * @return mathematical angle in rad + */ +qreal SITUATIONMAP_EXPORT getMathAngle(qreal sinPhi, qreal cosPhi); + + +double SITUATIONMAP_EXPORT getAngleDegreeNorth(const QPointF &p1, const QPointF &p2); + +/** + * @brief Get North angle from Math angle + * @param mathematical orientated angle in degree + * @return north (0° = 12h) orientated angle in degree + */ +qreal SITUATIONMAP_EXPORT getAngleDegree2North(qreal angleDeg); + +/** + * @brief Get North angle from Math angle + * @param mathematical orientated angle in degree + * @return math (0° = 3h) orientated angle in degree + */ +qreal SITUATIONMAP_EXPORT getAngleDegree2Math(qreal angleDeg); + + +/** + * @brief Get the position on a given line (for positioning a text) + * @param p1 startPoint of line + * @param p2 endPoint of line + * @param position_0_to_1 value from 0.0 up to 1.0. + * 0.0 -> 0% -> startPoint + * 0.5 -> 50% -> middle between startPoint and endPoint + * 1.0 -> 100% -> endPoint + * @return position between start and end. + */ +QPointF SITUATIONMAP_EXPORT getPointOnLine(const QPointF &p1, const QPointF &p2, double position_0_to_1); + +Point3D SITUATIONMAP_EXPORT getPointOnLine(const Point3D &p1, const Point3D &p2, double position_0_to_1); + +/** + * @brief getCircleSectorPolygon + * @param centerPoint [geo coordinates] + * @param radius [m] + * @param rotationAngleDeg (orientation of segment) + * @param centralAngleDeg + * @return polygon describing the circle sector + */ +QPolygonF SITUATIONMAP_EXPORT getCircleSectorPolygon(const QPointF ¢erPoint, qreal radius, double rotationAngleDeg, double centralAngleDeg); + + + +/** + * @brief Rotate a point around with another point as center. + * @param pToBeRotated Point to be rotated + * @param center Center + * @param angleDegree Angle in degree (clockwise). + */ +void SITUATIONMAP_EXPORT rotatePoint(QPointF &pToBeRotated, + const QPointF ¢er, + double angleDegree); + + + + +QList SITUATIONMAP_EXPORT mineFieldCenterLineToOutline(const QList ¢erLine, + double width); + + + + + + +} // namespace MathUtils +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/CMakeLists.txt new file mode 100755 index 000000000..91ff5d6a4 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/CMakeLists.txt @@ -0,0 +1,49 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +SET(SRCS_TEST1 + situation_map_test_main.cpp + sit_map.cpp + + log4cxxSituationMapModuleTests.props +) + +SET(HDRS_TEST1 + situation_map_test_main.h + sit_map.h +) + +IF(BUILD_TESTING AND TARGET situationMap) + ADD_EXECUTABLE(situationMapModuleTest ${SRCS_TEST1} ${HDRS_TEST1}) + TARGET_LINK_LIBRARIES(situationMapModuleTest + NGLLogger + nglapplication + LtsCoordTrans + advancedSymbology_static + situationMapObjects + situationMap_static + situationMapFunctionDialog + ArcGIS::ArcGIS + Qt6::Core + Qt6::Test + ) + + ADD_TEST(NAME situationMapModuleTest_NONE COMMAND $ none) + ADD_TEST(NAME situationMapModuleTest_MIL2525D COMMAND $ mil2525d) + + SET(LOG4CXX_PROPS_FILE log4cxxSituationMapModuleTests.props) + ADD_CUSTOM_COMMAND(TARGET situationMapModuleTest POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${LOG4CXX_PROPS_FILE} $/${LOG4CXX_PROPS_FILE} + ) + + IF(MSVC) + # Configure the template file + SET(VS_USER_FILE situationMapTest.vcxproj.user) + configure_file( ../SituationMap.vcxproj.user.in ${CMAKE_CURRENT_BINARY_DIR}/${VS_USER_FILE} @ONLY) + ENDIF(MSVC) + +ENDIF() diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/log4cxxSituationMapModuleTests.props b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/log4cxxSituationMapModuleTests.props new file mode 100755 index 000000000..fadc434b8 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/log4cxxSituationMapModuleTests.props @@ -0,0 +1,159 @@ +log4j.reset=true +log4j.rootLogger=WARN, file + +# SitMap +########################################################### +#log4j.logger.SituationMap.SituationMapTestsMain=DEBUG +#log4j.logger.SituationMap.SitMap=WARN + +# AdvancedSymbology (AdvSymbology) +########################################################### +#log4j.logger.AdvSymbology.SymbolDictionary=INFO +#log4j.logger.AdvSymbology.SymbolDictionaryImpl=INFO +#log4j.logger.AdvSymbology.Sidc2525Delta=INFO + +# SituationMap +########################################################### +#log4j.logger.SituationMap.INFO +#log4j.logger.SituationMap.MapImpl=FATAL +#log4j.logger.SituationMap.MapSettings=FATAL +#log4j.logger.SituationMap.MapInitializer=FATAL +#log4j.logger.SituationMap.LayerFactoryImpl=FATAL +#log4j.logger.SituationMap.LayerControl=FATAL +#log4j.logger.SituationMap.CoordTranslationFunctorImpl=FATAL +#log4j.logger.SituationMap.CoordTranslationThread=FATAL +#log4j.logger.SituationMap.PositionsTranslated=FATAL +#log4j.logger.SituationMap.GridImpl=FATAL +#log4j.logger.SituationMap.ObjectFactory=FATAL +#log4j.logger.SituationMap.IdGenerator=FATAL +#log4j.logger.SituationMap.ObjectContainer=FATAL +#log4j.logger.SituationMap.ObjectContainerCTC=FATAL +#log4j.logger.SituationMap.OverlayHandlerImpl=FATAL +#log4j.logger.SituationMap.ViewshedImpl=FATAL +#log4j.logger.SituationMap.ViewshedBase=FATAL + +#log4j.logger.SituationMap.AbstractMapObjectImpl=FATAL +#log4j.logger.SituationMap.AbstractEffectSimulationImpl=FATAL +#log4j.logger.SituationMap.OriginTranslated=FATAL +#log4j.logger.SituationMap.PositionsTranslated=FATAL +#log4j.logger.SituationMap.AbstractMultiPointMapObjectImpl=FATAL +#log4j.logger.SituationMap.AbstractSinglePointMapObjectImpl=FATAL +#log4j.logger.SituationMap.BasicPathObjectImpl=FATAL +#log4j.logger.SituationMap.IndicationContainer=FATAL + +#log4j.logger.SituationMap.PolygonImpl=FATAL +#log4j.logger.SituationMap.PolylineImpl=FATAL +#log4j.logger.SituationMap.TextImpl=FATAL + +#log4j.logger.SituationMap.AreaFireImpl=FATAL +#log4j.logger.SituationMap.BiologicalAgentImpl=FATAL +#log4j.logger.SituationMap.BuildingOutlineImpl=FATAL +#log4j.logger.SituationMap.ChemicalAgentImpl=FATAL +#log4j.logger.SituationMap.DangerAreaImpl=FATAL +#log4j.logger.SituationMap.FunctionalAreaImpl=FATAL +#log4j.logger.SituationMap.HitLineImpl=FATAL +#log4j.logger.SituationMap.MapObjectIndicatorImpl=FATAL +#log4j.logger.SituationMap.MineImpl=FATAL +#log4j.logger.SituationMap.MineFieldImpl=FATAL +#log4j.logger.SituationMap.NuclearAgentImpl=FATAL +#log4j.logger.SituationMap.ParticipantImpl=FATAL +#log4j.logger.SituationMap.SingleFireImpl=FATAL + +#log4j.logger.SituationMap.AbstractGraphicalObject=FATAL +#log4j.logger.SituationMap.GraphicalObjectEllipse=FATAL +#log4j.logger.SituationMap.GraphicalObjectPath=FATAL +#log4j.logger.SituationMap.GraphicalObjectMultiPath=FATAL +#log4j.logger.SituationMap.GraphicalObjectPictureMarker=FATAL +#log4j.logger.SituationMap.GraphicalObjectRange=FATAL +#log4j.logger.SituationMap.GraphicalObjectText=FATAL +#log4j.logger.SituationMap.TacticalAppearance=FATAL +#log4j.logger.SituationMap.MessageBase=FATAL + +#log4j.logger.SituationMap.EventDispatcher=FATAL +#log4j.logger.SituationMap.Rubberband=FATAL +#log4j.logger.SituationMap.RubberbandRectangle=FATAL +#log4j.logger.SituationMap.RubberbandDrawablesAndHandles=FATAL +#log4j.logger.SituationMap.RubberbandFrameHandle=FATAL +#log4j.logger.SituationMap.RubberbandPointHandle=FATAL +#log4j.logger.SituationMap.RubberbandStateSelect=FATAL + +# SituationMap-SideBar +########################################################### +#log4j.logger.SituationMap.SideBar=FATAL +#log4j.logger.SituationMap.SideBarItem=FATAL +#log4j.logger.SituationMap.SideBarModel=FATAL + + + +# Appnd_File +########################################################### +log4j.appender.file=org.apache.log4j.RollingFileAppender +log4j.appender.file.MaxFileSize=1MB +log4j.appender.file.MaxBackupIndex=9 +log4j.appender.file.file=${LOG4QT_LOGDIR}/${LOG4QT_USER}_SituationMap.log +log4j.appender.file.layout=org.apache.log4j.PatternLayout +log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n + + +# Appnd_Console +########################################################### +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern= %d %-5p %c{1} |#| %m (%-.30F:%L) %n + +#log4j.appender.stdout.layout.ConversionPattern= %d %-5p %-30c |#| %m (%-.30F:%L) %n + + +# Appnd_Syslog +########################################################### +log4j.appender.syslog=org.apache.log4j.SyslogAppender +log4j.appender.syslog.Threshold=ERROR +log4j.appender.syslog.SysLogHost=localhost +log4j.appender.syslog.Facility=LOCAL7 +log4j.appender.syslog.layout=org.apache.log4j.PatternLayout +log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m +log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true +log4j.appender.syslog.LevelRangeFilter.LevelMin=INFO +log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL + + +########################################################### +########################################################### +########################################################### +# 1) Available Levels in LOG4CXX Version 0.10.0: +# TRACE, +# DEBUG, +# INFO, +# WARN, +# ERROR +# FATAL +# +# d = date/time +# p = priority (Level) +# c = category/channel +# m = message +# F = file +# L = line +# n = platform dependent line separator character or characters. +# .... +# +# Examples for console: +# a) +# %d %p %c #%m# (%.50F:%L)%n +# date/time +# priority +# category/channel +# #message# +# (Maximal 50Character des Filename von rechts:Line) +# line separator +# +# b) +# %d (%.15F:%L) %-5p %c |#| %m%n +# date/time +# (Maximal 15Character des Filename von rechts:Line) +# priority, Right pad with spaces if the priority name is less than 5 characters long. +# category/channel +# message +# line separator +# + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/sit_map.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/sit_map.cpp new file mode 100755 index 000000000..76b63e188 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/sit_map.cpp @@ -0,0 +1,347 @@ +/** + * @RDE_HEADER@ + * Moduletest + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "sit_map.h" + +#include + +#include +//#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +#include + + +// Qt +#include +#include +#include +#include + + + + +class myMapInitializer : public SituationMap::MapInitializer +{ + Q_OBJECT + + using BaseClass = SituationMap::MapInitializer; + +public: + + explicit myMapInitializer(SituationMap::Map &map + , SituationMap::MapSettings &mapSettings + , const QString &debugName + , QObject *parent = nullptr) + : BaseClass(map, mapSettings, debugName, parent) {} + + bool initializeObjectFactory() override + { + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::MapObjectIndicator), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Cube), new SituationMap::GenericObjectFactory()); + + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Polygon), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Polyline), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Text), new SituationMap::GenericObjectFactory()); + + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Participant), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::AreaFire), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::BiologicalAgent), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::ChemicalAgent), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::BuildingOutline), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::DangerArea), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::FunctionalArea), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::GeoImage), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::HitLine), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Mine), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::MineField), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::NineLiner), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::NuclearAgent), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::SingleFire), new SituationMap::GenericObjectFactory()); +// SituationMap::ObjectFactory::registerMe(typeid(SituationMap::SingleFireCentral), new SituationMap::GenericObjectFactory()); + + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::SidcSymbol), new SituationMap::GenericObjectFactory()); + + return true; + } // initializeObjectFactory() + + // overload to support tpk-Files/BasemapBuiltIn + bool initializeCoordTranslationFunctor() override + { + if (m_map.layerFactory().isUsingBaseMapBuiltIn()) + { + int wkid = 102100; + NOTICE_LOG(m_logger) << LOGFUNCTION << "\e[31m !!! using BaseMapBuiltIn -> wkId=" << wkid << "\e[0m"; + + m_map.initCoordTranslationFunctor(); + return true; + } + return BaseClass::initializeCoordTranslationFunctor(); + } + + + AdvancedSymbology::SymbologyType symbologyType() const override + { + return m_symbologyType; + } // symbologyType + + void setSymbologyType(AdvancedSymbology::SymbologyType symbologyType) + { + m_symbologyType = symbologyType; + } + + +protected slots: + + void onMapReady() override + { + + // using center and scale: + m_map.setCenterPoint(QPointF(92750, 80525)); // QPointF(92750, 80525) -> Rennplatz - Bremen + m_map.setScale(3000000); // 3000000 -> Lower Saxony + + // instead of "center and scale" it is possible to set an extent + // double xmin, double ymin, double xmax, double ymax + // m_map.setExtent(-200000, -200000, 300000, 300000); + + + // minMaxScale is optional: + // m_map.setMinScale(10000000); + // m_map.setMaxScale(100); + } + +protected: + AdvancedSymbology::SymbologyType m_symbologyType = AdvancedSymbology::SymbologyType::APP6B; + +}; // myMapInitializer + + +using namespace SituationMap; + +class SitMap::Private +{ +public: + Private() = default; + ~Private() = default; + + // map + AdvancedSymbology::SymbologyType m_symbologyType; + SituationMap::Map *m_map = nullptr; + + // admin + QGridLayout *m_mainLayout = nullptr; + QPushButton *m_pbDestroyMap = nullptr; + QPushButton *m_pbQuit = nullptr; + + QTimer *m_getRotationTimer = nullptr; + +}; // class SitMap::Private +//----------------------------------------------------------------------------- + +SitMap::SitMap(AdvancedSymbology::SymbologyType symbologyType, QWidget *parent) + : QDialog(parent) + , m_logger(SituationMap::getLogger("SitMap")) + , d(new Private) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + this->setObjectName(QLatin1String("SitMap-Widget")); + + d->m_symbologyType = symbologyType; + d->m_mainLayout = new QGridLayout(this); + d->m_mainLayout->setObjectName(QLatin1String("layout")); + + //----------------------------------- + // Buttons + //----------------------------------- + d->m_pbDestroyMap = new QPushButton(QLatin1String("Destroy Map")); + d->m_mainLayout->addWidget(d->m_pbDestroyMap, 0, 0); + connect(d->m_pbDestroyMap, &QPushButton::clicked, this, &SitMap::onCreateDestroyClicked); + + + //----------------------------------- + // Map + //----------------------------------- + createMap(); + + { + QGridLayout *bottomLayout = new QGridLayout(); + bottomLayout->setObjectName(QLatin1String("bottomLayout")); + d->m_mainLayout->addLayout(bottomLayout, 2, 0); + + d->m_pbQuit = new QPushButton(QLatin1String("Quit")); + connect(d->m_pbQuit, &QPushButton::clicked, [ = ]() + { + QCoreApplication::quit(); + }); + bottomLayout->addWidget(d->m_pbQuit, 0, 0); + + // tbc + + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +SitMap::~SitMap() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete d->m_map; + delete d; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +bool SitMap::isReady() +{ + return d->m_map->isReady(); +} + +SituationMap::Map *SitMap::map() +{ + return d->m_map; +} + +void SitMap::onCreateDestroyClicked() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + static bool destroy = false; + destroy = !destroy; + if (destroy) + { + d->m_pbDestroyMap->setText(QLatin1String("Create Map")); + d->m_getRotationTimer->stop(); // to avoid slot being called + d->m_map->deleteLater(); + } + else + { + d->m_pbDestroyMap->setText(QLatin1String("Destroy Map")); + createMap(); + } + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void SitMap::createMap() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + // 1. Map + d->m_map = SituationMap::Map::create(this); + + connect(d->m_map, &SituationMap::Map::mapReady, this, [this]() + { + + this->update(); // to fix redraw problem + + DEBUG_LOG(m_logger) << " ===== emitting mapReady"; + emit mapReady(); + + // Timer + d->m_getRotationTimer = new QTimer(this); + connect(d->m_getRotationTimer, &QTimer::timeout, this, [this]() + { + DEBUG_LOG(m_logger) << " get rotationDegree of=" << d->m_map->rotation(); + }); + d->m_getRotationTimer->setSingleShot(true); + d->m_getRotationTimer->start(2000); + }); + + connect(d->m_map, &SituationMap::Map::mapReadyAndSymbolDictionaryLoaded, this, [this]() + { + DEBUG_LOG(m_logger) << " ===== emitting mapReadyAndSymbolDictionaryLoaded"; + emit mapReadyAndSymbolDictionaryLoaded(); + }); + + + // e.g. TDS-build/share/Lts/situation_map/situation_map_defaults.ini + QString initFilePath = NGL::findFile(NGL::FileType::ConfigFile, QStringLiteral("situation_map_defaults.ini")); + + if (initFilePath.isEmpty()) + { + FATAL_LOG(m_logger) << " initFilePath " << initFilePath; + abort(); + } + + SituationMap::MapSettings *mapSettings = new SituationMap::MapSettings(initFilePath, d->m_map); + myMapInitializer *mapInitializer = new myMapInitializer(*d->m_map, *mapSettings, QLatin1String("ModuleTest"), d->m_map); + + + // Take SymbologyType from argv not from ini file: + AdvancedSymbology::SymbologyType symbologyTypeArgv = d->m_symbologyType; + mapInitializer->setSymbologyType(symbologyTypeArgv); + + + if (mapInitializer->hasErrors()) + { + FATAL_LOG(m_logger) << LOGFUNCTION << " MapInitializer-filename='" << mapInitializer->filename() + << "'' has following errors: " + << " exists = " << mapInitializer->exists() + << " accessError = " << mapInitializer->accessError() + << " formatError = " << mapInitializer->formatError(); + return; + } + else + { + if (mapInitializer->initMap()) + { + INFO_LOG(m_logger) << LOGFUNCTION << " MapInitializer::initMap() ok"; + } + else + { + ERROR_LOG(m_logger) << LOGFUNCTION << " MapInitializer::initMap() failed"; + } + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " GridLevelCount=" << d->m_map->grid().levelCount(); + + // BasePoint, extent, rotation + connect(d->m_map, &SituationMap::Map::extentChanged, this, [ = ]() + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " extent changed: "; + }); + + + // Add widget + d->m_mainLayout->addWidget(d->m_map, 1, 0); + + // 50. turn over to application (if required): + + // Create FunctionDialog: + SituationMap::FunctionDialog::addButtonToMap(d->m_map); + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +#include "sit_map.moc" diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/sit_map.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/sit_map.h new file mode 100755 index 000000000..96e7940a8 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/sit_map.h @@ -0,0 +1,58 @@ +/** + * @RDE_HEADER@ + * Moduletest + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Qwn +#include + + +namespace SituationMap +{ +class Map; +} + +namespace AdvancedSymbology +{ +enum class SymbologyType; +} + + +// Qt +#include + +class SitMap : public QDialog +{ + Q_OBJECT + +public: + SitMap(AdvancedSymbology::SymbologyType symbologyType, QWidget *parent = nullptr); + virtual ~SitMap(); + + virtual bool isReady(); + + SituationMap::Map *map(); + + +signals: + void mapReady(); + void mapReadyAndSymbolDictionaryLoaded(); + +public slots: + void onCreateDestroyClicked(); + void createMap(); + + +private: + SituationMap::LOGGER_T m_logger; ///< Interface of the Logging-Facility + + class Private; + Private *d = nullptr; + +}; + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/situation_map_test_main.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/situation_map_test_main.cpp new file mode 100755 index 000000000..dc929c5a9 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/situation_map_test_main.cpp @@ -0,0 +1,3518 @@ +/** + * @RDE_HEADER@ + * Moduletest + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "situation_map_test_main.h" + + +#include +#include + +#include "sit_map.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +//#include +#include + +#include + +#include + +#include + +//ESRI +#include +#include +#include +#include +#include +//#include + + +//Qt +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + +static LtsCoordTrans::UtmCoordinate s_basePoint; + +const int timeToDelay = 0; // suggestion: 2000 + +using namespace SituationMap; // 'enable' operator<< (QPointF etc.) + +bool fuzzyCompare(const Point3D &p1, const Point3D &p2) +{ + const Point3D &diff = p1 - p2; + return (diff.x() < 0.00001) && + (diff.y() < 0.00001) && + (diff.z() < 0.00001); +} + +bool fuzzyCompare(const PositionCont &p1, const PositionCont &p2) +{ + if (p1.size() != p2.size()) + return false; + + for(int i=0; im_symbologyType = symbologyType; +} + +SituationMapTestsMain::~SituationMapTestsMain() +{ + delete d; +} + + +//------------------------------------------------------------------------------------------------------- + + +void SituationMapTestsMain::initTestCase() +{ + DEBUG_LOG(m_logger) << LOGFUNCTION; + + if (d->m_sitMap) + d->m_sitMap->close(); + + delete d->m_sitMap; + + LayerFactoryImpl::setForceCreationOfGraphicsOverlay(false); // false => createLayerOnDemand + + + d->m_sitMap = new SitMap(d->m_symbologyType); + QVERIFY2(d->m_sitMap, "Initialisation of SituationMap failed"); + + connect(d->m_sitMap, &SitMap::mapReadyAndSymbolDictionaryLoaded, [&]() + { +// INFO_LOG(m_logger) << LOGFUNCTION << " got mapReady "; + }); + + d->m_sitMap->setMinimumWidth(1100); + d->m_sitMap->setMinimumHeight(1100); + + d->m_sitMap->show(); + + d->m_map = d->m_sitMap->map(); + d->m_geoOff = QPointF( s_basePoint.x,s_basePoint.y ); + + SituationMap::LayerFactory &layerFac = d->m_map->layerFactory(); + QString msg = QStringLiteral("layerFactory: count failed (%1)").arg(layerFac.count()); + QVERIFY2((layerFac.count() >= 3) && (layerFac.count() <= 16) , msg.toStdString().c_str()); + + QSignalSpy spy(d->m_sitMap, &SitMap::mapReady); + QTRY_VERIFY(d->m_sitMap->isReady()); + QTRY_VERIFY2(spy.count() == 1, "mapReadyEmitted not emitted"); + + QVERIFY2(d->m_sitMap->isReady() , "map is NOT READY"); + + + // Wait also for Dictionary (since it´s done within a WorkerThread) + AdvancedSymbology::SymbolDictionary &dict = d->m_map->symbolDictionary(); + QTRY_VERIFY2(dict.isLoaded(), "dict is NOT Loaded"); + + QTest::qWait(1000); +} + + +void SituationMapTestsMain::checkMapReady() +{ + if (! d->m_sitMap->isReady()) + { + QSignalSpy spy(d->m_sitMap, &SitMap::mapReadyAndSymbolDictionaryLoaded); + QTRY_VERIFY2(spy.count() > 0, "mapReady not received"); + } +} + +void SituationMapTestsMain::checkMapNotifier() +{ + QPointer mapNotifier = d->m_sitMap->map()->createMapNotifier(); + QVERIFY2(mapNotifier.isNull() == false, "no MapNotifier"); + + QSignalSpy spy1(mapNotifier.data(), &MapNotifier::mapReady); + QSignalSpy spy2(mapNotifier.data(), &MapNotifier::mapReadyAndSymbolDictionaryLoaded); + + mapNotifier->run(); + + QTRY_VERIFY2(spy1.count() == 1, "mapReady not received"); + QTRY_VERIFY2(spy2.count() == 1, "mapReadyAndSymbolDictionaryLoaded not received"); +} + +void SituationMapTestsMain::checkCenter() +{ + QTest::qWait(timeToDelay); + + int scaleSAVE = d->m_map->scale(); + QPointF centerSAVE = d->m_map->centerPoint(); + + { + // real check: + QPointF center(LtsCoordTrans::geographicFromMgrs(QStringLiteral("32UMD9100084500"))); // Bremen motorway junction + + // double xmin, double ymin, double xmax, double ymax + d->m_map->setExtent(center.x() - 0.01, center.y() - 0.01, + center.x() + 0.01, center.y() + 0.01); + + QPointF readCenter = d->m_map->centerPoint(); + DEBUG_LOG(m_logger) << "Center: " << center << " got: " << readCenter; + + QPointF dist = center - readCenter; + + INFO_LOG(m_logger) << LOGFUNCTION + << " center =" << center + << " read center =" << readCenter + << " dist.manhattanLength=" << dist.manhattanLength(); + + QVERIFY2(dist.manhattanLength() < 10, "checkCenter failed"); + // QVERIFY2(center.toPoint()==QPoint(5500, 5500), "checkCenter failed"); + + QTest::qWait(timeToDelay); + } + + // restore old values: + d->m_map->setScale(scaleSAVE); + d->m_map->setCenterPoint(centerSAVE); + QTest::qWait(timeToDelay); +} + +void SituationMapTestsMain::checkExtent() +{ + const QPointF centerPnt(LtsCoordTrans::geographicFromMgrs(QStringLiteral("32UMD9100084500"))); // Bremen motorway junction + + d->m_map->setCenterPoint(centerPnt, 500000); // what is the useful + QTest::qWait(1000); // Let map react + + // some 'reference' values: + QPointF center = d->m_map->centerPoint(); + QRectF extent = d->m_map->extent(); +// QRectF fullExtent = d->m_map->fullExtent(); + + INFO_LOG(m_logger) << LOGFUNCTION << " center = " << center; + INFO_LOG(m_logger) << LOGFUNCTION << " extent = " << extent; +// INFO_LOG(m_logger) << LOGFUNCTION << " fullExtent = " << fullExtent; + + QVERIFY2(extent. contains(center), "extent.contains failed"); +// QVERIFY2(fullExtent.contains(center), "fullExtent.contains failed"); +// QVERIFY2(fullExtent.contains(extent.topLeft()), "fullExtent.contains failed"); +// QVERIFY2(fullExtent.contains(extent.topRight()), "fullExtent.contains failed"); +// QVERIFY2(fullExtent.contains(extent.bottomLeft()), "fullExtent.contains failed"); +// QVERIFY2(fullExtent.contains(extent.bottomRight()), "fullExtent.contains failed"); + QTest::qWait(1000); // Let map react + + INFO_LOG(m_logger) << LOGFUNCTION << " set identical values now...."; + + // I + // set extent (as QRectF) + QTest::qWait(1000); // Let map react + d->m_map->setExtent(extent); + QTest::qWait(1000); // Let map react + + // reference extent and new extent should be identical: + QRectF newExtent = d->m_map->extent(); + QCOMPARE(newExtent.x(), extent.x()); + QCOMPARE(newExtent.y(), extent.y()); + QCOMPARE(newExtent.width(), extent.width()); + QCOMPARE(newExtent.height(), extent.height()); + + +/* + // III + // set fullExtent (as QRectF) + QTest::qWait(1000); // Let map react + d->m_map->setFullExtent(fullExtent); + QTest::qWait(1000); // Let map react + + // reference fullExtent and new fullExtent should be identical: + QRectF newFullExtent = d->m_map->fullExtent(); + QCOMPARE(newFullExtent.x(), fullExtent.x()); + QCOMPARE(newFullExtent.y(), fullExtent.y()); + QCOMPARE(newFullExtent.width(), fullExtent.width()); + QCOMPARE(newFullExtent.height(), fullExtent.height()); +*/ +} + + + +void SituationMapTestsMain::checkMapIsUTM() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + if (d->m_map->layerFactory().isUsingBaseMapBuiltIn()) + { + #ifdef Q_OS_WIN + NOTICE_LOG(m_logger) << LOGFUNCTION << "***** !!! skipping test - isUsingBaseMapBuiltIn() = true !!! *****"; + #else + // Escape sequenz \e[31m should provide a red colored log output but doesn't work for all compilers and terminal types!!! JT 2016/07/13 + NOTICE_LOG(m_logger) << LOGFUNCTION << "\e[31m !!! skipping test - isUsingBaseMapBuiltIn() = true !!! \e[0m"; + NOTICE_LOG(m_logger) << LOGFUNCTION << "\e[31m !!! skipping test - isUsingBaseMapBuiltIn() = true !!! \e[0m"; + NOTICE_LOG(m_logger) << LOGFUNCTION << "\e[31m !!! skipping test - isUsingBaseMapBuiltIn() = true !!! \e[0m"; + NOTICE_LOG(m_logger) << LOGFUNCTION << "\e[31m !!! skipping test - isUsingBaseMapBuiltIn() = true !!! \e[0m"; + NOTICE_LOG(m_logger) << LOGFUNCTION << "\e[31m !!! skipping test - isUsingBaseMapBuiltIn() = true !!! \e[0m"; + #endif + return; + } + + int currentWkid = d->m_map->wkid(); + + INFO_LOG(m_logger) << LOGFUNCTION + << " currentWkid = " << currentWkid; + + char wkidInfo[50]; + sprintf(&wkidInfo[0], "wkid failed (wkid = %d)", currentWkid); + + QVERIFY2(currentWkid == 32632, &wkidInfo[0]); // wkid=32632 (UTM Zone 32 N WGS 84) / Bremen + + + // SimCoords: + QPointF simPosTopLeft = d->m_map->viewPortWindowPointToGeoCoordinate(QPoint(0, 0)); + QPointF simPosBottomLeft = d->m_map->viewPortWindowPointToGeoCoordinate(QPoint(0, 1000)); + + QPointF simPosTopRight = d->m_map->viewPortWindowPointToGeoCoordinate(QPoint(1000, 0)); + QPointF simPosBottomRight = d->m_map->viewPortWindowPointToGeoCoordinate(QPoint(1000, 1000)); + + INFO_LOG(m_logger) << LOGFUNCTION << " tl = " << simPosTopLeft << " tr = " << simPosTopRight; + INFO_LOG(m_logger) << LOGFUNCTION << " bl = " << simPosBottomLeft << " br = " << simPosBottomRight; + +// 2015-11-17 tl = QPointF(75954.216#96400.032) tr = QPointF(107704.280#96400.032) +// 2015-11-17 bl = QPointF(75954.216#64649.968) br = QPointF(107704.280#64649.968) + + double distXLeft = simPosTopLeft.x() - simPosBottomLeft.x(); + double distXRight = simPosTopRight.x() - simPosBottomRight.x(); + double distYTop = simPosTopLeft.y() - simPosTopRight.y(); + double distYBottom = simPosBottomLeft.y() - simPosBottomRight.y(); + + INFO_LOG(m_logger) << LOGFUNCTION + << " distXLeft = " << distXLeft + << " distXRight = " << distXRight + << " distYTop = " << distYTop + << " distYBottom = " << distYBottom; + + const double allowError = 1; //meter + QVERIFY2(fabs(distXLeft) < allowError, "distXLeft failed"); + QVERIFY2(fabs(distXRight) < allowError, "distXRight failed"); + QVERIFY2(fabs(distYTop) < allowError, "distYTop failed"); + QVERIFY2(fabs(distYBottom) < allowError, "distYBottom failed"); + + { + double distHor = simPosTopLeft.x() - simPosTopRight.x(); // horizontal + double distVer = simPosTopLeft.y() - simPosBottomLeft.y(); // vertical + + INFO_LOG(m_logger) << LOGFUNCTION + << " distHor = " << fabs(distHor) + << " distVer = " << fabs(distVer); + + QVERIFY2((fabs(distHor) - fabs(distVer)) < allowError, "dist12 failed"); + } + + QTest::qWait(timeToDelay); +} // SituationMapTestsMain::checkMapIsUTM + + +void SituationMapTestsMain::convertUTMBasePoint() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + + double simX = 94500; + double simY = 81500; + + // here add offset for 32U MD + + double UTMx = simX + 400000 ; // offset M + double UTMy = simY + 5800000 ; // offset for D + + Esri::ArcGISRuntime::SpatialReference UTMsr(32632); // UTM Zone 32 N WGS 84 + + Esri::ArcGISRuntime::Point UTMbasePoint(UTMx, UTMy, UTMsr); + + Esri::ArcGISRuntime::Point basePointMap = Esri::ArcGISRuntime::GeometryEngine::project(UTMbasePoint, Esri::ArcGISRuntime::SpatialReference(d->m_map->wkid())); + + INFO_LOG(m_logger) << LOGFUNCTION + << " UTMbasePoint, x=" << QString::number(UTMbasePoint.x(), 'f', 9) + << " ; " << QString::number(UTMbasePoint.y(), 'f', 9) + << " basePointMap, x=" << QString::number(basePointMap.x(), 'f', 9) + << " ; " << QString::number(basePointMap.y(), 'f', 9); + + // __asm int 3 +} // SituationMapTestsMain::convertUTMBasePoint + +void SituationMapTestsMain::checkOriginTranslated() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + CoordTranslationFunctor *coordTransFunctor = d->m_map->coordTranslationFunctor(); + QVERIFY2(coordTransFunctor, "OriginTranslated: coordTransFunctor failed"); + + { + const Point3D pRef(70000, 70000, .0); + + SituationMap::OriginTranslated oTrans; + oTrans.setCoordTransFunctor(coordTransFunctor); + + // do + oTrans.setOrigin(pRef); + + // verify + QVERIFY2(oTrans.isEmpty() == false, "OriginTranslated: isEmpty failed"); + + const Point3D originOut = oTrans.origin(); + QVERIFY2(pRef == originOut, "OriginTranslated: origin failed"); + + const Point3D originMapOut = oTrans.originTranslated(); + QVERIFY2(pRef != originMapOut, "OriginTranslated: originTranslated failed"); + + // Copy + SituationMap::OriginTranslated oTransCopy; + QVERIFY2(oTransCopy.isEmpty() == true, "OriginTranslated: isEmpty failed"); + QVERIFY2((oTrans == oTransCopy) == false, "OriginTranslated: operator== failed"); + QVERIFY2((oTrans != oTransCopy) == true, "OriginTranslated: operator!= failed"); + + oTransCopy = oTrans; + QVERIFY2(oTransCopy.isEmpty() == false, "OriginTranslated: isEmpty failed"); + QVERIFY2((oTrans == oTransCopy) == true, "OriginTranslated: operator== failed"); + QVERIFY2((oTrans != oTransCopy) == false, "OriginTranslated: operator!= failed"); + } +} + + +class MyPositionsTranslated : public SituationMap::PositionsTranslated +{ +public: + void setPositionsTranslated(const PositionCont &posCont) + { + SituationMap::PositionsTranslated::setPositionsTranslated(posCont); + } +}; + +void SituationMapTestsMain::checkPositionsTranslated() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + CoordTranslationFunctor *coordTransFunctor = d->m_map->coordTranslationFunctor(); + QVERIFY2(coordTransFunctor, "OriginTranslated: coordTransFunctor failed"); + + const QPointF pRef1_2D(LtsCoordTrans::geographicFromMgrs(QStringLiteral("32UMD7000070000"))); + const QPointF pRef2_2D(LtsCoordTrans::geographicFromMgrs(QStringLiteral("32UMD8000080000"))); + + const Point3D pRef1(pRef1_2D.x(), pRef1_2D.y(), .0); + const Point3D pRef2(pRef2_2D.x(), pRef2_2D.y(), .0); + + SituationMap::PositionCont posContRef; + posContRef << pRef1 << pRef2; + + SituationMap::PositionCont positionsMapOutRef; + + { + SituationMap::PositionsTranslated pTrans; + pTrans.setCoordTransFunctor(coordTransFunctor); + + coordTransFunctor->resetProjectionCounter(); + QCOMPARE(coordTransFunctor->projectionCounter(), 0); + + + // setPositions + pTrans.setPositions(posContRef); + + // verify + QVERIFY2(pTrans.isEmpty() == false, "PositionsTranslated: isEmpty failed"); + QVERIFY2(pTrans.isTranslated() == false, "PositionsTranslated: isTranslated failed"); + QCOMPARE(pTrans.size(), 2); + + QCOMPARE(pTrans.first(), pRef1); + QCOMPARE(pTrans.last(), pRef2); + + const SituationMap::PositionCont positionsOut = pTrans.positions(); + + QCOMPARE(posContRef, positionsOut); + + positionsMapOutRef = pTrans.positionsTranslated(); + QVERIFY2(pTrans.isTranslated() == true, "PositionsTranslated: isTranslated failed"); + QVERIFY2(posContRef != positionsMapOutRef, "PositionsTranslated: originTranslated failed"); + + QCOMPARE(coordTransFunctor->projectionCounter(), 2); + + // Copy -> not allowed + SituationMap::PositionsTranslated pTransCopy; + // pTransCopy = pTrans; // Compiler error :-) + + + // angle are verified within the UnitTest + + // clear + pTrans.clear(); + + // verify + QVERIFY2(pTrans.isEmpty() == true, "PositionsTranslated: isEmpty failed"); + QVERIFY2(pTrans.size() == 0, "PositionsTranslated: size failed"); + QVERIFY2(pTrans.positions().size() == 0, "PositionsTranslated: size failed"); + QVERIFY2(pTrans.isTranslated() == false, "PositionsTranslated: isTranslated failed"); + QVERIFY2(pTrans.positionsTranslated().size() == 0, "PositionsTranslated: size failed"); + QVERIFY2(pTrans.isTranslated() == true, "PositionsTranslated: isTranslated failed"); + QVERIFY2(coordTransFunctor->projectionCounter() == 2, "OriginTranslated: projectionCounter failed"); + } + + { + MyPositionsTranslated pTrans; + pTrans.setCoordTransFunctor(coordTransFunctor); + + coordTransFunctor->resetProjectionCounter(); + QVERIFY2(coordTransFunctor->projectionCounter() == 0, "OriginTranslated: projectionCounter failed"); + + + // setPositionsTranslated + pTrans.setPositionsTranslated(positionsMapOutRef); + + // verify + QVERIFY2(pTrans.isEmpty() == false, "PositionsTranslated: isEmpty failed"); + QVERIFY2(pTrans.isTranslated() == true, "PositionsTranslated: isTranslated failed"); + QCOMPARE(pTrans.size(), 2); + + QVERIFY2( fuzzyCompare(pTrans.first(), pRef1), "PositionsTranslated: verify failed"); + QVERIFY2( fuzzyCompare(pTrans.last(), pRef2), "PositionsTranslated: verify failed"); + + const SituationMap::PositionCont &positionsOut = pTrans.positions(); + QVERIFY2( fuzzyCompare(posContRef, positionsOut), "PositionsTranslated: verify failed"); + + QCOMPARE(coordTransFunctor->projectionCounter(), 2); + + + // try calling setPositionsTranslated with different values: + { + SituationMap::PositionCont positionsMapOutRefCopy = positionsMapOutRef; + positionsMapOutRefCopy.append(positionsMapOutRefCopy.first()); + + pTrans.setPositionsTranslated(positionsMapOutRefCopy); + + qCritical() << " size = " << pTrans.size(); + QVERIFY2(pTrans.isEmpty() == false, "PositionsTranslated: isEmpty failed"); + QVERIFY2(pTrans.isTranslated() == true, "PositionsTranslated: isTranslated failed"); + QCOMPARE(pTrans.size(), 3); + + const SituationMap::PositionCont &positionsOut = pTrans.positions(); + QCOMPARE(positionsOut.size(), 3); + + QVERIFY2( fuzzyCompare(positionsOut.first(), pRef1), "PositionsTranslated: verify failed"); + QVERIFY2( fuzzyCompare(positionsOut[1], pRef2), "PositionsTranslated: verify failed"); + QVERIFY2( fuzzyCompare(positionsOut.last(), pRef1), "PositionsTranslated: verify failed"); + } + } + + // setPositions and then addSinglePosition + { + SituationMap::PositionsTranslated pTrans; + pTrans.setCoordTransFunctor(coordTransFunctor); + + coordTransFunctor->resetProjectionCounter(); + QCOMPARE(coordTransFunctor->projectionCounter(), 0); + + + // setPositions + pTrans.setPositions(posContRef); + + // verify + QVERIFY2(pTrans.isEmpty() == false, "PositionsTranslated: isEmpty failed"); + QCOMPARE(pTrans.size(), 2); + QVERIFY2(pTrans.isTranslated() == false, "PositionsTranslated: isTranslated failed"); + + // add single position: + pTrans.addSinglePosition(pRef1); + QVERIFY2(pTrans.isEmpty() == false, "PositionsTranslated: isEmpty failed"); + QCOMPARE(pTrans.size(), 3); + QVERIFY2(pTrans.isTranslated() == false, "PositionsTranslated: isTranslated failed"); + + QCOMPARE(pTrans.first(), pRef1); + QCOMPARE(pTrans.last(), pRef1); + + // verify + const SituationMap::PositionCont positionsOut = pTrans.positions(); + QCOMPARE(positionsOut.size(), 3); + QCOMPARE(pRef1, positionsOut[2]); + QVERIFY2(pTrans.isTranslated() == false, "PositionsTranslated: isTranslated failed"); + + const SituationMap::PositionCont positionsMapOut333 = pTrans.positionsTranslated(); + QCOMPARE(pTrans.size(), 3); + QCOMPARE(positionsMapOutRef[0], positionsMapOut333[0]); + QCOMPARE(positionsMapOutRef[0], positionsMapOut333[2]); + QVERIFY2(pTrans.isTranslated() == true, "PositionsTranslated: isTranslated failed"); + QCOMPARE(coordTransFunctor->projectionCounter(), 3); + + + // setPositions (to original) + pTrans.setPositions(posContRef); + + // verify + QVERIFY2(pTrans.isEmpty() == false, "PositionsTranslated: isEmpty failed"); + QCOMPARE(pTrans.size(), 2); + QVERIFY2(pTrans.isTranslated() == false, "PositionsTranslated: isTranslated failed"); + + QCOMPARE(pTrans.first(), pRef1); + QCOMPARE(pTrans.last(), pRef2); + + const SituationMap::PositionCont positionsMapOut222 = pTrans.positionsTranslated(); + QCOMPARE(pTrans.size(), 2); + QCOMPARE(positionsMapOutRef, positionsMapOut222); + QVERIFY2(pTrans.isTranslated() == true, "PositionsTranslated: isTranslated failed"); + QCOMPARE(coordTransFunctor->projectionCounter(), 5); + + // clear + pTrans.clear(); + + // verify + QVERIFY2(pTrans.isEmpty() == true, "PositionsTranslated: isEmpty failed"); + QVERIFY2(pTrans.size() == 0, "PositionsTranslated: size failed"); + QVERIFY2(pTrans.positions().size() == 0, "PositionsTranslated: size failed"); + QVERIFY2(pTrans.isTranslated() == false, "PositionsTranslated: isTranslated failed"); + QVERIFY2(pTrans.positionsTranslated().size() == 0, "PositionsTranslated: size failed"); + QVERIFY2(pTrans.isTranslated() == true, "PositionsTranslated: isTranslated failed"); + QVERIFY2(coordTransFunctor->projectionCounter() == 5, "OriginTranslated: projectionCounter failed"); + } + + // addSinglePosition + { + SituationMap::PositionsTranslated pTrans; + pTrans.setCoordTransFunctor(coordTransFunctor); + + coordTransFunctor->resetProjectionCounter(); + QVERIFY2(coordTransFunctor->projectionCounter() == 0, "OriginTranslated: projectionCounter failed"); + + + // verify + QVERIFY2(pTrans.isEmpty() == true, "PositionsTranslated: isEmpty failed"); + QVERIFY2(pTrans.size() == 0, "PositionsTranslated: size failed"); + QVERIFY2(pTrans.positions().size() == 0, "PositionsTranslated: size failed"); + QVERIFY2(pTrans.isTranslated() == false, "PositionsTranslated: isTranslated failed"); + if (d->m_symbologyType != AdvancedSymbology::SymbologyType::None) // diversify flow + { + QVERIFY2(pTrans.positionsTranslated().size() == 0, "PositionsTranslated: size failed"); + QVERIFY2(pTrans.isTranslated() == true, "PositionsTranslated: isTranslated failed"); + } + + // add single position: + pTrans.addSinglePosition(pRef1); + QVERIFY2(pTrans.isEmpty() == false, "PositionsTranslated: isEmpty failed"); + QVERIFY2(pTrans.size() == 1, "PositionsTranslated: size failed"); + QVERIFY2(pTrans.isTranslated() == false, "PositionsTranslated: isTranslated failed"); + + QVERIFY2(pTrans.first() == pRef1, "PositionsTranslated: first failed"); + + + // verify + const SituationMap::PositionCont positionsOut = pTrans.positions(); + QVERIFY2(positionsOut.size() == 1, "PositionsTranslated: positions failed"); + QVERIFY2(pRef1 == positionsOut[0], "PositionsTranslated: positions failed"); + QVERIFY2(pTrans.isTranslated() == false, "PositionsTranslated: isTranslated failed"); + + const SituationMap::PositionCont positionsMapOut333 = pTrans.positionsTranslated(); + QVERIFY2(positionsMapOut333.size() == 1, "PositionsTranslated: size failed"); + QVERIFY2(positionsMapOutRef[0] == positionsMapOut333[0], "PositionsTranslated: positionsTranslated failed"); + QVERIFY2(pTrans.isTranslated() == true, "PositionsTranslated: isTranslated failed"); + QVERIFY2(coordTransFunctor->projectionCounter() == 1, "OriginTranslated: projectionCounter failed"); + + + // setPositions (to original) + pTrans.setPositions(posContRef); + + // verify + QVERIFY2(pTrans.isEmpty() == false, "PositionsTranslated: isEmpty failed"); + QVERIFY2(pTrans.size() == 2, "PositionsTranslated: size failed"); + QVERIFY2(pTrans.isTranslated() == false, "PositionsTranslated: isTranslated failed"); + + const SituationMap::PositionCont positionsMapOut222 = pTrans.positionsTranslated(); + QVERIFY2(pTrans.size() == 2, "PositionsTranslated: size failed"); + QVERIFY2(positionsMapOutRef == positionsMapOut222, "PositionsTranslated: positionsTranslated failed"); + QVERIFY2(pTrans.isTranslated() == true, "PositionsTranslated: isTranslated failed"); + QVERIFY2(coordTransFunctor->projectionCounter() == 3, "OriginTranslated: projectionCounter failed"); + + // clear + pTrans.clear(); + + // verify + QVERIFY2(pTrans.isEmpty() == true, "PositionsTranslated: isEmpty failed"); + QVERIFY2(pTrans.size() == 0, "PositionsTranslated: size failed"); + QVERIFY2(pTrans.positions().size() == 0, "PositionsTranslated: size failed"); + QVERIFY2(pTrans.isTranslated() == false, "PositionsTranslated: isTranslated failed"); + QVERIFY2(pTrans.positionsTranslated().size() == 0, "PositionsTranslated: size failed"); + QVERIFY2(pTrans.isTranslated() == true, "PositionsTranslated: isTranslated failed"); + QVERIFY2(coordTransFunctor->projectionCounter() == 3, "OriginTranslated: projectionCounter failed"); + } +} + +void SituationMapTestsMain::checkPositionAngles() +{ + // here we check if the angle calculation works as expected + // -> the underlying coord system used for calculation must be orthogonal - e.g. UTM + + CoordTranslationFunctor *coordTransFunctor = d->m_map->coordTranslationFunctor(); + SituationMap::PositionsTranslated pTrans; + pTrans.setCoordTransFunctor(coordTransFunctor); + + { + // first check: we use an orthogonal triangle --> 0,0, 0,4 3,4 + QList posCont2D({LtsCoordTrans::geographicFromMgrs(QStringLiteral("32UMD8000080000")), // p0 + LtsCoordTrans::geographicFromMgrs(QStringLiteral("32UMD8000080004")), // p1 + LtsCoordTrans::geographicFromMgrs(QStringLiteral("32UMD8000380004"))}); // p2 + SituationMap::PositionCont posCont = SituationMap::convert2D3D(posCont2D); + + pTrans.setPositions(posCont); + const double start = 0; + //const double close = 210; // p2 -> p0 + // calculated value: + const double close = 216.86989765331455; // p2 -> p0 + const double end = 90; // p1 -> p2 + + const double angleStart = pTrans.angleOfStartSegment(); + const double angleClose = pTrans.angleOfClosingSegment(); + const double angleEnd = pTrans.angleOfEndSegment(); + qDebug("Start: %20.15lf, Close: %20.15lf, End: %20.15lf", angleStart, angleClose, angleEnd); + QVERIFY2(qFuzzyCompare(angleStart, start), qPrintable(QStringLiteral("Start angle is not %1 degrees").arg(start))); + QVERIFY2(qFuzzyCompare(angleClose, close), qPrintable(QStringLiteral("Close angle is not %1 degrees").arg(close))); + QVERIFY2(qFuzzyCompare(angleEnd, end), qPrintable(QStringLiteral("End angle is not %1 degrees").arg(end))); + } + + { + // second check: a 'normal' triangle + QList posCont2D({LtsCoordTrans::geographicFromMgrs(QStringLiteral("32UMD8000080000")), + LtsCoordTrans::geographicFromMgrs(QStringLiteral("32UMD8000080004")), + LtsCoordTrans::geographicFromMgrs(QStringLiteral("32UMD8000480004"))}); + SituationMap::PositionCont posCont = SituationMap::convert2D3D(posCont2D); + + + pTrans.setPositions(posCont); + const double start = 0; + const double close = 225.00000000667; // should be not correct + const double end = 90; + + const double angleStart = pTrans.angleOfStartSegment(); + const double angleClose = pTrans.angleOfClosingSegment(); + const double angleEnd = pTrans.angleOfEndSegment(); + qDebug("Start: %20.15lf, Close: %20.15lf, End: %20.15lf", angleStart, angleClose, angleEnd); + QVERIFY2(qFuzzyCompare(angleStart, start), qPrintable(QStringLiteral("Start angle is not %1 degrees").arg(start))); + QVERIFY2(qFuzzyCompare(angleClose, close), qPrintable(QStringLiteral("Close angle is not %1 degrees").arg(close))); + QVERIFY2(qFuzzyCompare(angleEnd, end), qPrintable(QStringLiteral("End angle is not %1 degrees").arg(end))); + } +} + + + +void SituationMapTestsMain::measureProject() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + QElapsedTimer timer; + int wkid = d->m_map->wkid(); + if (wkid >= 0) + { + Esri::ArcGISRuntime::SpatialReference sr(wkid); + + int jMax = 10000; + + //-------------------------------------------------------------------------- + // create SpatialReference by wkid + //-------------------------------------------------------------------------- + int wkid = 102100; + timer.start(); + for (int j = 0; j < jMax; ++j) + { + Esri::ArcGISRuntime::SpatialReference temp(wkid); + int help = temp.wkid(); + Q_UNUSED(help); + } + qint64 msec = timer.elapsed(); + qDebug() << " create SpatialReference by wkid (" << wkid << "): " << jMax << " operations took " << msec << "ms"; + + //-------------------------------------------------------------------------- + // create SpatialReference by wkt + //-------------------------------------------------------------------------- + QLatin1String SpatialReferenceWkt( + "PROJCS[\"WGS_1984_Web_Mercator_Auxiliary_Sphere\",\ + GEOGCS[\"GCS_WGS_1984\",\ + DATUM[\"D_WGS_1984\",\ + SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],\ + PRIMEM[\"Greenwich\",0.0],\ + UNIT[\"Degree\",0.0174532925199433]],\ + PROJECTION[\"Mercator_Auxiliary_Sphere\"],\ + PARAMETER[\"False_Easting\",0.0],\ + PARAMETER[\"False_Northing\",0.0],\ + PARAMETER[\"Central_Meridian\",0.0],\ + PARAMETER[\"Standard_Parallel_1\",0.0],\ + PARAMETER[\"Auxiliary_Sphere_Type\",0.0],\ + UNIT[\"Meter\",1.0]]\""); + timer.restart(); + for (int j = 0; j < jMax; ++j) + { + Esri::ArcGISRuntime::SpatialReference temp(SpatialReferenceWkt); + int help = temp.wkid(); + Q_UNUSED(help); + } + msec = timer.elapsed(); + qDebug() << " create SpatialReference by wkt (" << wkid << "): " << jMax << " operations took " << msec << "ms"; + + + //-------------------------------------------------------------------------- + // null projection map to map + //-------------------------------------------------------------------------- + timer.restart(); + for (int j = 0; j < jMax; ++j) + { + Esri::ArcGISRuntime::Point source(1000000, 1000000 + j, sr); + Esri::ArcGISRuntime::Point bremenPoint = Esri::ArcGISRuntime::GeometryEngine::project(source, sr); + Q_UNUSED(bremenPoint); + } + msec = timer.elapsed(); + qDebug() << " null projection (" << wkid << "): " << jMax << " operations took " << msec << "ms"; + + + //-------------------------------------------------------------------------- + // project sim to map (BREMEN) + //-------------------------------------------------------------------------- + timer.restart(); + const Esri::ArcGISRuntime::SpatialReference &destSR = sr; + // Bremen: basePoint='32UMD 00000 00000' offset x=400000 (M) ; 5800000 (D) ; wkid=32632 (UTM Zone 32 N WGS 84) + const Esri::ArcGISRuntime::SpatialReference sourceSR(32632); + for (int j = 0; j < jMax; ++j) + { + Esri::ArcGISRuntime::Point simPoint(90000, 78000 + j, sourceSR); + Esri::ArcGISRuntime::Point mapPoint = Esri::ArcGISRuntime::GeometryEngine::project(simPoint, destSR); + Q_UNUSED(mapPoint); + } + msec = timer.elapsed(); + qDebug() << " simple sim2Map: " << jMax << " operations took " << msec << "ms"; + + + + { + //-------------------------------------------------------------------------- + // project lat/long to map (WGS84 to map) 1 + //-------------------------------------------------------------------------- + timer.restart(); + for (int j = 0; j < jMax; ++j) + { + // Esri::ArcGISRuntime::SpatialReference sr(4236); // EPSG:4236 = WGS84 + Esri::ArcGISRuntime::SpatialReference sr = static_cast(d->m_map)->getEsriSpatialReference(); + Esri::ArcGISRuntime::Point myPoint(0.155, 0.926 + (j * 0.001), sr); + Esri::ArcGISRuntime::Point bremenPoint = Esri::ArcGISRuntime::GeometryEngine::project(myPoint, sr); + Q_UNUSED(bremenPoint); + } + msec = timer.elapsed(); + qDebug() << " Lat/Long (WGS84) to map 1 (" << wkid << "): " << jMax << " operations took " << msec << "ms"; + + + //-------------------------------------------------------------------------- + // project lat/long to map (WGS84 to map) geographicToMap 2D + //-------------------------------------------------------------------------- + timer.restart(); + CoordTranslationFunctor *coordFunc = d->m_map->coordTranslationFunctor(); + for (int j = 0; j < jMax; ++j) + { + const QPointF &res = coordFunc->geographicToMap(QPointF(0.155, 0.926 + (j * 0.001))); + Q_UNUSED(res); + } + msec = timer.elapsed(); + qDebug() << " Lat/Long (WGS84) to map (geographicToMap 2D) " << jMax << " operations took " << msec << "ms"; + + + //-------------------------------------------------------------------------- + // project lat/long to map (WGS84 to map) geographicToMap Point3D + //-------------------------------------------------------------------------- + timer.restart(); + for (int j = 0; j < jMax; ++j) + { + const Point3D &res = coordFunc->geographicToMap(Point3D(0.155, 0.926 + (j * 0.001), 1.23456)); + Q_UNUSED(res); + } + msec = timer.elapsed(); + qDebug() << " Lat/Long (WGS84) to map (geographicToMap Point3D) " << jMax << " operations took " << msec << "ms"; + } + + + //-------------------------------------------------------------------------- + // project FalseEasting/Northing to map (to map) + //-------------------------------------------------------------------------- + QLatin1String myWkt( + "PROJCS[\"WGS_1984_Web_Mercator_Auxiliary_Sphere\",\ + GEOGCS[\"GCS_WGS_1984\",\ + DATUM[\"D_WGS_1984\",\ + SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],\ + PRIMEM[\"Greenwich\",0.0],\ + UNIT[\"Degree\",0.0174532925199433]],\ + PROJECTION[\"Mercator_Auxiliary_Sphere\"],\ + PARAMETER[\"False_Easting\",1000000.0],\ + PARAMETER[\"False_Northing\",1000000.0],\ + PARAMETER[\"Central_Meridian\",0.0],\ + PARAMETER[\"Standard_Parallel_1\",0.0],\ + PARAMETER[\"Auxiliary_Sphere_Type\",0.0],\ + UNIT[\"Meter\",1.0]]\""); + + int i = 1000000; + + { + Esri::ArcGISRuntime::SpatialReference mySR(myWkt); + + timer.restart(); + for (int j = 0; j < jMax; ++j) + { + Esri::ArcGISRuntime::Point myPoint(i, i + j, mySR); + Esri::ArcGISRuntime::Point projPoint = Esri::ArcGISRuntime::GeometryEngine::project(myPoint, sr); + Q_UNUSED(projPoint); + } + msec = timer.elapsed(); + } + qDebug() << " FalseEasting/Northing to map (" << wkid << "): " << jMax << " operations took " << msec << "ms"; + } + else + ERROR_LOG(m_logger) << " wkid invalid"; +} + +void SituationMapTestsMain::measurceLocationToElevationBulk() +{ + SituationMap::LayerFactory &layerFac = d->m_map->layerFactory(); + SituationMap::LayerFactoryImpl *layerFacImpl = dynamic_cast(&layerFac); + + MapImpl *mapImpl = static_cast(d->m_map); + + LocationToElevationBulk leBulk; + leBulk.init(d->m_map, layerFacImpl->baseSurface()); + + leBulk.addLocation(Esri::ArcGISRuntime::Point(495000, 5880000, mapImpl->getEsriSpatialReference())); + leBulk.addLocation(Esri::ArcGISRuntime::Point(495100, 5881000, mapImpl->getEsriSpatialReference())); + + QSignalSpy spy(&leBulk, &LocationToElevationBulk::locationsToElevationsCompleted); + + leBulk.start(); + + QTRY_VERIFY2(spy.count() == 1, "LocationToElevationBulk: signalCount failed,"); +} + +void SituationMapTestsMain::checkMapObjectProperties() +{ + SituationMap::RefPointer mapObject = + d->m_map->objectFactory().create(QLatin1String("AES"), QLatin1String("checkMapObjectPropertiesDebugInfo")); + + QVariant variant; + bool success; + + // check property + variant = mapObject->property("propA"); + QVERIFY2(!variant.isValid(), "variant should be INVALID"); + + // check setProperty + // set to '4711' + variant.setValue(4711); + QVERIFY2(variant.isValid(), "variant should be ok"); + + success = mapObject->setProperty("propA", variant); + QVERIFY2(success, "setProperty failed"); + + // get property '4711' + variant.clear(); + variant = mapObject->property("propA"); + QVERIFY2(variant.isValid(), "property should be ok"); + QVERIFY2(variant.toInt() == 4711, "property failed"); + + // change to '4712' + variant.setValue(4712); + QVERIFY2(variant.isValid(), "variant should be ok"); + success = mapObject->setProperty("propA", variant); + QVERIFY2(success, "setProperty failed"); + + // get property '4712' + variant.clear(); + variant = mapObject->property("propA"); + QVERIFY2(variant.isValid(), "property failed"); + QVERIFY2(variant.toInt() == 4712, "property failed"); + + + //******************* + // invalid QVariant: + //******************* + QVariant variantInvalid; + QVERIFY2(!variantInvalid.isValid(), "variantInvalid failed"); + success = mapObject->setProperty("propA", variantInvalid); // to remove property + QVERIFY2(success, "setProperty failed"); + + variant.clear(); + variant = mapObject->property("propA"); // get non-existing property + QVERIFY2(!variant.isValid(), "variant should be INVALID"); + + + success = mapObject->setProperty("doesNotExist", variantInvalid); // use non-existing propertyName + QVERIFY2(!success, "setProperty failed"); + + //******************* + // Two Properties: + //******************* + success = mapObject->setProperty("propA", 4711); + QVERIFY2(success, "setPropertyA failed"); + success = mapObject->setProperty("propB", 4712); + QVERIFY2(success, "setPropertyB failed"); + + variant.clear(); + variant = mapObject->property("propA"); + QVERIFY2(variant.toInt() == 4711, "property failed"); + + variant.clear(); + variant = mapObject->property("propB"); + QVERIFY2(variant.toInt() == 4712, "property failed"); + + + + //************************ + // Removing / Destroying + //************************ + mapObject->removeMySelf(); + +} + +void SituationMapTestsMain::checkObjectFactoryContainer() +{ + QElapsedTimer timer; + timer.start(); + + uint buildingId = 0; + uint bAgentId = 0; + + // mapObject 'BuildingOutline' -> 10 + for (int i = 0; i < 10; ++i) + { + SituationMap::RefPointer building = + d->m_map->objectFactory().create(QLatin1String("AES")); + QVERIFY2(building != nullptr, "create failed"); + building->setDebugInfo(QStringLiteral("building_%1").arg(i)); + + buildingId = building->uniqueMapId(); + } + + // mapObject 'BiologicalAgent' -> 100 + for (int i = 0; i < 100; ++i) + { + SituationMap::RefPointer biologicalAgent = + d->m_map->objectFactory().create(QLatin1String("AES")); + QVERIFY2(biologicalAgent != nullptr, "create failed"); + biologicalAgent->setDebugInfo(QStringLiteral("biologicalAgent_%1").arg(i)); + + biologicalAgent->setProperty("TYPE", QLatin1String("BiologicalAgent")); + bAgentId = biologicalAgent->uniqueMapId(); + } + + // mapObject 'Participant' -> 1000 + for (int i = 0; i < 1000; ++i) + { + SituationMap::RefPointer participant = + d->m_map->objectFactory().create(QLatin1String("Participants")); + QVERIFY2(participant != nullptr, "create failed"); + participant->setDebugInfo(QStringLiteral("participant_%1").arg(i)); + } + + + quint32 countUsedIds = d->m_map->objectFactory().count(); + QVERIFY2(countUsedIds == 1110, "wrong number of objects"); + + bool contains = false; + + // simple contains + contains = d->m_map->objectFactory().contains(11); // random select + QVERIFY2(contains, "contains failed"); + + contains = d->m_map->objectFactory().contains(123456789); // random select + QVERIFY2(!contains, "contains failed"); + + + // assume, that '5' is a building: + + // simple contains + contains = d->m_map->objectFactory().contains(buildingId); + QVERIFY2(contains, "contains failed"); + + // typed contains + contains = d->m_map->objectFactory().contains(buildingId); + QVERIFY2(!contains, "typed contains failed"); + + // get + { + SituationMap::RefPointer obj = d->m_map->objectFactory().get(buildingId); + QVERIFY2(obj != nullptr, "get failed"); + QVERIFY2(obj.get(), "get failed"); + QVERIFY2(obj->uniqueMapId() == buildingId, "get failed"); + int compare = obj->debugInfo().compare(QStringLiteral("building_9"), Qt::CaseInsensitive); + QVERIFY2(compare == 0, "get failed"); + } + + + + // get + { + SituationMap::RefPointer obj = d->m_map->objectFactory().get(123456789); + QVERIFY2(obj == nullptr, "get failed"); + QVERIFY2(!obj.get(), "get failed"); + } + + + // typed get + { + SituationMap::RefPointerbAgent + = d->m_map->objectFactory().get(bAgentId); + QVERIFY2(bAgent != nullptr, "typed get#1 failed"); + QVERIFY2(bAgent.get(), "typed get#2 failed"); + QVERIFY2(bAgent->uniqueMapId() == bAgentId, "get failed"); + } + + + { + QElapsedTimer innerTimer; + innerTimer.start(); + + for (int j = 0; j < 100; ++j) + { + int found = 0; + for (int i = 1000; i > 0; --i) + { + // get + SituationMap::RefPointer mapObj + = d->m_map->objectFactory().get(i); + if (mapObj.valid()) + ++found; + } + QVERIFY2(found == 1000, "typed get#3 failed"); + } + + INFO_LOG(m_logger) << " get (base type) took " << innerTimer.elapsed() << "ms"; + } + { + QElapsedTimer innerTimer; + innerTimer.start(); + + for (int j = 0; j < 100; ++j) + { + int found = 0; + for (int i = 1000; i > 0; --i) + { + // typed get + SituationMap::RefPointerbAgent + = d->m_map->objectFactory().get(i); + if (bAgent.valid()) + ++found; + } + QVERIFY2(found == 100, "typed get#3 failed"); + } + + INFO_LOG(m_logger) << " get (selected by type) took " << innerTimer.elapsed() << "ms"; + } + { + QElapsedTimer innerTimer; + innerTimer.start(); + + for (int j = 0; j < 100; ++j) + { + int found = 0; + for (int i = 1000; i > 0; --i) + { + // get + SituationMap::RefPointer mapObj + = d->m_map->objectFactory().get(i); + if (mapObj.valid() && mapObj->property("TYPE").toString().compare(QLatin1String("BiologicalAgent")) == 0) + ++found; + } + QVERIFY2(found == 100, "typed get#3 failed"); + } + + INFO_LOG(m_logger) << " get (selected by QLatin1String) took " << innerTimer.elapsed() << "ms"; + } + + INFO_LOG(m_logger) << " checkObjectFactoryContainer took " << timer.elapsed() << "ms"; + +} + +void SituationMapTestsMain::checkObjectAndLayerFactory() +{ + // Preperations: + bool success = false; + + initTestCase(); + checkMapReady(); + + SituationMap::LayerFactory &layerFac = d->m_map->layerFactory(); + SituationMap::ObjectFactory &objFac = d->m_map->objectFactory(); + + { + + INFO_LOG(m_logger) << " technicalLayerCount = " << d->m_map->technicalLayerCount(); + + + INFO_LOG(m_logger) << " layernames = " << layerFac.layernames().join(QLatin1String(";")); + int basemapCount = layerFac.layernames(LayerType::Basemap).count(); + int jumpPointLayerCount = layerFac.layernames(LayerType::JumpPoint).count(); + int objectLayerCount = layerFac.layernames(LayerType::Operational).count(); + QVERIFY2((1 <= basemapCount) && (basemapCount <= 3), "layerFactory: count failed"); + QVERIFY2(objectLayerCount == 13, QString(QStringLiteral("layerFactory: count failed (is %1)")).arg(objectLayerCount).toStdString().c_str()); + + QVERIFY2(d->m_map->technicalLayerCount() == basemapCount , "layerFactory: technicalLayerCount failed"); + QVERIFY2(layerFac.count() == basemapCount + jumpPointLayerCount + objectLayerCount , "layerFactory: count failed"); + + INFO_LOG(m_logger) << " objFac.count() =" << objFac.count(); + + + + qDebug() << " technicalLayerCount =" << d->m_map->technicalLayerCount(); + + int initialObjCount = objFac.count(); + + // use an LOGICAL layer (not basemap) + QStringList layernames = layerFac.layernames(LayerType::Operational); + QString layername = layernames[0]; + QVERIFY2(layername.isEmpty() == false, "objectFactory: count failed"); + + // real test + QVERIFY2(objFac.count() == 0, "objectFactory: count failed"); + QVERIFY2(d->m_map->technicalLayerCount() == basemapCount , "layerFactory: technicalLayerCount failed"); + + SituationMap::RefPointer objPtr1; + objPtr1 = objFac.create(layername, QLatin1String("UnitTestId1")); + QVERIFY2(objPtr1.valid() == true , "objectFactory: create11 failed"); + + SituationMap::RefPointer objPtr2; + objPtr2 = objFac.create(layername, QLatin1String("UnitTestId2")); + QVERIFY2(objPtr2.valid() == true , "objectFactory: create22 failed"); + + SituationMap::RefPointer objPtr3; + objPtr3 = objFac.create(layername, QLatin1String("UnitTestId3")); + QVERIFY2(objPtr3.valid() == true , "objectFactory: create33 failed"); + + SituationMap::RefPointer objPtr4; + objPtr4 = objFac.create(layername, QLatin1String("UnitTestId4")); + QVERIFY2(objPtr4.valid() == true , "objectFactory: create44 failed"); + + QVERIFY2(objFac.count() == (initialObjCount + 4) , "objectFactory: count failed"); + + + // now set required parameters: + QPointF offset = QPointF(50000, 50000); + int extent = 30000; + QList posCont2D; + posCont2D << s_basePoint.geographic( offset - QPointF( 0, 0) ); + posCont2D << s_basePoint.geographic( offset - QPointF(extent, 0) ); + posCont2D << s_basePoint.geographic( offset - QPointF(extent, extent) ); + posCont2D << s_basePoint.geographic( offset - QPointF( 0, extent) ); + + objPtr3->setPositions(convert2D3D(posCont2D)); + + // => technicalLayerCount increases by 1/one, caused by objFac.create<>) + QVERIFY2(d->m_map->technicalLayerCount() == basemapCount + 1 , "layerFactory: technicalLayerCount failed"); + + // now update the objects (to let layerFactory create MessageGroupLayer implicit/onDemand) + success = objPtr3->updateView(); + QVERIFY2(success , "object: updateView failed"); + + + qDebug() << " technicalLayerCount =" << d->m_map->technicalLayerCount(); + + if (d->m_symbologyType != AdvancedSymbology::SymbologyType::None) + { + // in the meantime, the corresponding technicalLayer (MessageGroup) for the objects is created + // => technicalLayerCount increases again by 1/one, caused by objFac.updateView) +// QVERIFY2(d->m_map->technicalLayerCount() == basemapCount + 1 , "layerFactory: technicalLayerCount failed"); + // => technicalLayerCount increases again by 2/two, caused by objFac.updateView) + QVERIFY2(d->m_map->technicalLayerCount() == basemapCount + 2 , "layerFactory: technicalLayerCount failed"); + } + else + QVERIFY2(d->m_map->technicalLayerCount() == basemapCount + 1 , "layerFactory: technicalLayerCount failed"); + + } + + // + // End of scope -> local RefPointer should be destroyed now: + // + + + //************************ + // Removing / Destroying + //************************ + const QList allUniqueIds = objFac.getAllKeys(); + qDebug() << " remove, current size()=" << allUniqueIds.size(); + QVERIFY2(objFac.count() == allUniqueIds.size(), "objectFactory: count/size/... inkonsistent"); + + int numberRemoved = 0; + for (SituationMap::UniqueMapId uniqueId : allUniqueIds) + { + qDebug() << " remove uniqueId=" << uniqueId << "..."; + { + RefPointer mapObj = objFac.get(uniqueId); + if (mapObj) + { + mapObj->removeMySelf(); + ++numberRemoved; + } + else + qWarning() << " uniqueId=" << uniqueId << " not found"; + } + // End of Scope.... + qDebug() << " remove uniqueId=" << uniqueId << "... Done"; + } + qDebug() << " remove finished, current size()=" << objFac.count(); + + QVERIFY2(allUniqueIds.size() == numberRemoved , "objectFactory: at least one failure"); + QVERIFY2(objFac.count() == 0 , "objectFactory: count failed"); +} // checkObjectAndLayerFactory + + +void SituationMapTestsMain::checkOverlayHandler1() +{ + // use nullptr as map: + SituationMap::Map *map = nullptr; + + SituationMap::OverlayHandlerImpl oh(map); + + QVERIFY2(oh.overlayCount() == 0 , "OverlayHandler: overlayCount failed"); + QVERIFY2(oh.containsOverlay(123456789) == false , "OverlayHandler: containsOverlay failed"); + + bool success = false; + + success = oh.setOverlay(1234, QStringLiteral("OverlayDisplayname"), QByteArray("myInvalidOverlayData")); + QVERIFY2(success == 0 , "OverlayHandler: setOverlay failed"); + QVERIFY2(oh.overlayCount() == 0 , "OverlayHandler: overlayCount failed"); + QVERIFY2(oh.containsOverlay(1234) == false , "OverlayHandler: containsOverlay failed"); +} + +void SituationMapTestsMain::checkOverlayHandler2() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + { + SituationMap::OverlayHandler &oh = d->m_map->overlayHandler(); + + QVERIFY2(oh.overlayCount() == 0 , "OverlayHandler: overlayCount failed"); + QVERIFY2(oh.containsOverlay(123456789) == false , "OverlayHandler: containsOverlay failed"); + + bool success = false; + + // add invalid data: + qWarning() << " add invalid data:"; + { + int layerCountBEFORE = d->m_map->layerFactory().count(); + + success = oh.setOverlay(1234, QStringLiteral("OverlayDisplayname"), QByteArray("myInvalidOverlayData")); + QVERIFY2(success == false, "OverlayHandler: setOverlay failed"); + QVERIFY2(oh.overlayCount() == 0, "OverlayHandler: overlayCount failed"); + QVERIFY2(oh.containsOverlay(1234) == false, "OverlayHandler: containsOverlay failed"); + + int layerCountAFTER = d->m_map->layerFactory().count(); + QVERIFY2((layerCountAFTER - layerCountBEFORE) == 0, "OverlayHandler: layerCountDiff failed"); + + QSignalSpy spy(d->m_map, &SituationMap::Map::layerOrderChanged); + QVERIFY2(spy.count() == 0, "OverlayHandler: signalCount failed, layerOrderChanged"); + } + } +} + + +void SituationMapTestsMain::checkOverlayHandler3() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + SituationMap::OverlayHandler &oh = d->m_map->overlayHandler(); + QVERIFY2(oh.overlayCount() == 0 , "OverlayHandler: overlayCount failed"); + QVERIFY2(oh.containsOverlay(123456789) == false , "OverlayHandler: containsOverlay failed"); + + bool success = false; + + + // + // create PlanData 1: + // + QScopedPointer geoModel(GeometryModelFactory::singleton()->create()); + QVERIFY2((geoModel != nullptr), "OverlayHandler: Creating empty GeometryModel failed."); + + QList vertexList; + vertexList << QPointF(0.15, 0.9) << QPointF(0.151, 0.901); // area Bremen + + QList> attrList; + attrList += QPair(GeometryModel::Attribute::Color, QVariant(QColor("red"))); + + const GeometryModel::Node &node1 = geoModel->addPrimitive(GeometryModel::PrimitiveType::Line, vertexList, attrList); + QVERIFY2((node1.isValid()), "OverlayHandler: node invalid."); + + QByteArray planData1 = geoModel->toByteArray(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " planData1=" << planData1; + + + // + // create PlanData 2: + // + vertexList.clear(); + vertexList << QPointF(0.1511, 0.911) << QPointF(0.15111, 0.9011); // area Bremen + const GeometryModel::Node &node2 = geoModel->addPrimitive(GeometryModel::PrimitiveType::Line, vertexList, attrList); + QVERIFY2((node2.isValid()), "OverlayHandler: node invalid."); + + QByteArray planData2 = geoModel->toByteArray(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " planData2=" << planData2; + + + // + // add valid data / planData 1: + // + qWarning() << " add valid data / planData 1:"; + { + int layerCountBEFORE = d->m_map->layerFactory().count(); + + + QSignalSpy spy(d->m_map, &SituationMap::Map::layerOrderChanged); + + success = oh.setOverlay(1234, QStringLiteral("OverlayDisplayname"), planData1); + QVERIFY2(success == true, "OverlayHandler: setOverlay failed"); + QVERIFY2(oh.overlayCount() == 1, "OverlayHandler: overlayCount failed"); + QVERIFY2(oh.containsOverlay(1234) == true, "OverlayHandler: containsOverlay failed"); + QVERIFY2(spy.count() == 1, "signalCount failed, layerOrderChanged"); + + + int layerCountAFTER = d->m_map->layerFactory().count(); + QVERIFY2((layerCountAFTER - layerCountBEFORE) == 1, "OverlayHandler: layerCountDiff failed"); + DEBUG_LOG(m_logger) << LOGFUNCTION << " ln=" << d->m_map->layerFactory().layernames().join(QLatin1String(";")); + + + // verify objCount on EsriLayer + SituationMap::LayerFactoryImpl *layFacImpl = dynamic_cast(&d->m_map->layerFactory()); + QVERIFY2((layFacImpl) , "OverlayHandler: layFacImpl failed"); + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = layFacImpl->getGraphicsLayer(QLatin1String("OverlayLayer_1234")); + QVERIFY2((layFacImpl) , "OverlayHandler: EsriLayer failed"); + int numberOfObjects = graphicsLayer->graphics()->size(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " planData1 -> numberOfObjects=" << numberOfObjects; + QVERIFY2(numberOfObjects == 1 , "OverlayHandler: numberOfObjects on EsriLayer failed"); + } + + + // + // updateOverlay with planData 2: + // + qWarning() << " add valid data / planData 2:"; + { + QSignalSpy spy(d->m_map, &SituationMap::Map::layerOrderChanged); + + success = oh.setOverlay(1234, QStringLiteral("OverlayDisplayname"), planData2); + QVERIFY2(success == true, "OverlayHandler: setOverlay failed"); + + success = oh.setOverlay(1234, QStringLiteral("OverlayDisplayname"), planData2); + QVERIFY2(success == true, "OverlayHandler: setOverlay failed"); + QVERIFY2(oh.overlayCount() == 1, "OverlayHandler: overlayCount failed"); + QVERIFY2(oh.containsOverlay(1234) == true, "OverlayHandler: containsOverlay failed"); + QVERIFY2(spy.count() == 0, "signalCount failed, layerOrderChanged"); // == 0 because layer already exists + + // verify objCount on EsriLayer + SituationMap::LayerFactoryImpl *layFacImpl = dynamic_cast(&d->m_map->layerFactory()); + QVERIFY2((layFacImpl) , "OverlayHandler: layFacImpl failed"); + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = layFacImpl->getGraphicsLayer(QLatin1String("OverlayLayer_1234")); + QVERIFY2((layFacImpl) , "OverlayHandler: EsriLayer failed"); + int numberOfObjects = graphicsLayer->graphics()->size(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " planData2 -> numberOfObjects=" << numberOfObjects; + QVERIFY2(numberOfObjects == 2 , "OverlayHandler: numberOfObjects on EsriLayer failed"); + } + + + // + // check order: + // OverlayLayer shall be directly on top of the basemaps + // + if (1) { + const QStringList &layerNames = d->m_map->layerFactory().layernames(); + const int layerCount = layerNames.count(); + const int basemapCount = d->m_map->layerFactory().layernames(LayerType::Basemap).count(); + QVERIFY2((layerCount - basemapCount) > 0 , "Order of Layer: count failed"); // preCondition + + const QString &ovlName = layerNames[basemapCount]; // access to successor of basemaps + const bool match = ovlName.startsWith(QLatin1String("OverlayLayer")); + QVERIFY2(match == true , "Order of Layer: OverlayLayer is NOT successor of basemaps"); // preCondition + } + + + // removeOverlay + { + QSignalSpy spy(d->m_map, &SituationMap::Map::layerOrderChanged); + + int layerCountBEFORE = d->m_map->layerFactory().count(); + + bool success = oh.removeOverlay(1234); + QVERIFY2(success == true, "OverlayHandler: removeOverlay failed"); + QVERIFY2(oh.overlayCount() == 0, "OverlayHandler: overlayCount failed"); + QVERIFY2(oh.containsOverlay(1234) == false, "OverlayHandler: containsOverlay failed"); + + QVERIFY2(spy.count() == 1, "signalCount failed, layerOrderChanged"); + + int layerCountAFTER = d->m_map->layerFactory().count(); + QVERIFY2((layerCountAFTER - layerCountBEFORE) == -1, "OverlayHandler: layerCountDiff failed"); + DEBUG_LOG(m_logger) << LOGFUNCTION << " ln=" << d->m_map->layerFactory().layernames().join(QLatin1String(";")); + } + +} + +/* +void SituationMapTestsMain::checkRubberbandFactory() +{ + initTestCase(); + checkMapReady(); + + SituationMap::RB::RubberbandPointFeature *rbandPt = d->m_map->rubberbandFactory().create(); + + QVERIFY2(rbandPt != nullptr, "RubberbandFactory::create failed for SituationMap::RB::RubberbandPointFeature"); + + SituationMap::RB::RubberbandMultiPointFeature *rbandMPt = d->m_map->rubberbandFactory().create(); + + QVERIFY2(rbandMPt == nullptr, "RubberbandFactory::create failed for second rubberband at time"); + + if (rbandPt) delete rbandPt; // here it is ok to use delete directly + if (rbandMPt) delete rbandMPt; + + rbandMPt = d->m_map->rubberbandFactory().create(); + + QVERIFY2(rbandMPt != nullptr, "RubberbandFactory::create failed for SituationMap::RB::RubberbandMultiPointFeature"); + if (rbandMPt) delete rbandMPt; + + // check creating other rubberbands with factory + SituationMap::RB::Rubberband *rband = d->m_map->rubberbandFactory().create(); + + QVERIFY2(rband != nullptr, "RubberbandFactory::create failed for SituationMap::RB::RubberbandPointCluster"); + if (rband) delete rband; + + rband = d->m_map->rubberbandFactory().create(); + + QVERIFY2(rband != nullptr, "RubberbandFactory::create failed for SituationMap::RB::RubberbandArrow"); + if (rband) delete rband; + + rband = d->m_map->rubberbandFactory().create(); + + QVERIFY2(rband != nullptr, "RubberbandFactory::create failed for SituationMap::RB::RubberbandCorridor"); + if (rband) delete rband; + + rband = d->m_map->rubberbandFactory().create(); + + QVERIFY2(rband != nullptr, "RubberbandFactory::create failed for SituationMap::RB::RubberbandEllipse"); + if (rband) delete rband; + + rband = d->m_map->rubberbandFactory().create(); + + QVERIFY2(rband != nullptr, "RubberbandFactory::create failed for SituationMap::RB::RubberbandRectangle"); + if (rband) delete rband; + + rband = d->m_map->rubberbandFactory().create(); + + QVERIFY2(rband != nullptr, "RubberbandFactory::create failed for SituationMap::RB::RubberbandSmoothLine"); + if (rband) delete rband; + +} // SituationMapTestsMain::checkRubberbandFactory +*/ + +void SituationMapTestsMain::measureCoordTranslationFunctor() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + // Bremen: basePoint='32UMD 00000 00000' offset x=400000 (M) ; 5800000 (D) ; wkid=32632 (UTM Zone 32 N WGS 84) + d->m_map->initCoordTranslationFunctor(); + + SituationMap::CoordTranslationFunctor *functor = d->m_map->coordTranslationFunctor(); + QVERIFY2(functor != nullptr, "CoordTranslationFunctor falied"); + + QElapsedTimer timer; + + int jMax = 1000; + { + timer.start(); + for (int j = 0; j < jMax; ++j) + { + QPointF point(0.01, 0.08 + j * 0.0001); + QPointF ret = functor->geographicToMap(point); + Q_UNUSED(ret); + } + INFO_LOG(m_logger) << " simToMap " << jMax << " operations took " << timer.elapsed() << "ms"; + } + + { + timer.restart(); + for (int j = 0; j < jMax; ++j) + { + QPointF point(850000, 6880000 + j); + QPointF ret = functor->mapToGeographic(point); + Q_UNUSED(ret); + } + INFO_LOG(m_logger) << " mapToSim " << jMax << " operations took " << timer.elapsed() << "ms"; + } + +} // SituationMapTestsMain::checkRubberbandFactory + + +void SituationMapTestsMain::checkCoordinateConversion() +{ + // Karte laden fehlt hier noch, nach meinem Urlaub geht's weiter ... JT + initTestCase(); + + checkMapReady(); + checkCenter(); + + // d->m_map->setExtent(100000, 100000, 10000000, 10000000); + + QPoint windowPos(400, 200); + + QPointF mapPos = d->m_map->viewPortWindowPointToMapCoordinate(windowPos); + QPointF geoPos = d->m_map->viewPortWindowPointToGeoCoordinate(windowPos); + + QPointF windowPosMap = d->m_map->mapCoordinateToViewPortWindowPoint(mapPos); + QPointF windowPosGeo = d->m_map->geoCoordinateToViewPortWindowPoint(geoPos); + + qDebug() << LOGFUNCTION << "ConversionCheck with windowPos " << windowPos; + qDebug() << "mapPos" << mapPos << " ---> winPos " << windowPosMap; + qDebug() << "geoPos" << geoPos << " ---> winPos " << windowPosGeo; + + QVERIFY2((windowPos - windowPosMap).manhattanLength() < 5, "Coordinate conversion for map coordinates failed."); + QVERIFY2((windowPos - windowPosGeo).manhattanLength() < 5, "Coordinate conversion for geo coordinates failed."); + + // define a square + QPoint wrp1(100, 100); + QPoint wrp2(300, 100); + QPoint wrp3(300, 300); + QPoint wrp4(100, 300); + + QPointF srp1 = d->m_map->viewPortWindowPointToGeoCoordinate(wrp1); + QPointF srp2 = d->m_map->viewPortWindowPointToGeoCoordinate(wrp2); + QPointF srp3 = d->m_map->viewPortWindowPointToGeoCoordinate(wrp3); + QPointF srp4 = d->m_map->viewPortWindowPointToGeoCoordinate(wrp4); + + qreal w1 = srp2.x() - srp1.x(); + qreal w2 = srp3.x() - srp4.x(); + qreal h1 = srp1.y() - srp4.y(); + qreal h2 = srp2.y() - srp3.y(); + + QString msg; + msg = QStringLiteral("Rectangle seems not to be a rectangle under current coordinate projections. (cur wkid=%1").arg(d->m_map->wkid()); + QVERIFY2(fabs(w1 - w2) < 0.1, msg.toUtf8().data()); + QVERIFY2(fabs(h1 - h2) < 0.1, msg.toUtf8().data()); + + msg = QStringLiteral("Square seems not to be a square under current coordinate projections. (cur wkid=%1").arg(d->m_map->wkid()); + QVERIFY2(fabs(h1 - w1) < 0.1, msg.toUtf8().data()); + + // d->m_map->layerFactory().initializeBasemaps( QStringList(QLatin1String("http://192.168.133.88:6080/arcgis/rest/services/BremenGeoTiffUTM/MapServer")) + // , AdvancedSymbology::SymbologyType::MIL2525C ); + +} /* SituationMapTestsMain::checkCoordinateConversion */ + + +#ifdef CrashUnderWindows +void SituationMapTestsMain::checkCreationAndRemoveMySelf() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + SituationMap::ObjectFactory &objFac = d->m_map->objectFactory(); + SituationMap::LayerFactory &layerFac = d->m_map->layerFactory(); + + int initialObjCount = objFac.count(); + + // use an LOGICAL layer (not basemap) + QStringList layernames = layerFac.layernames(); + QString layername = layernames[1]; + + // real test + QVERIFY2(objFac.count() == 0, "objectFactory: count failed"); + + { + QList > objList; + + SituationMap::RefPointer objPtr1; + objPtr1 = objFac.create(layername, QLatin1String("UnitTestId1")); + QVERIFY2(objPtr1.valid() == true , "objectFactory: create11 failed"); + objList.append(SituationMap::RefPointer (objPtr1.get())); + + SituationMap::RefPointer objPtr2; + objPtr2 = objFac.create(layername, QLatin1String("UnitTestId2")); + QVERIFY2(objPtr2.valid() == true , "objectFactory: create22 failed"); + objList.append(SituationMap::RefPointer (objPtr2.get())); + + SituationMap::RefPointer objPtr3; + objPtr3 = objFac.create(layername, QLatin1String("UnitTestId3")); + QVERIFY2(objPtr3.valid() == true , "objectFactory: create33 failed"); + objList.append(SituationMap::RefPointer (objPtr3.get())); + + SituationMap::RefPointer objPtr4; + objPtr4 = objFac.create(layername, QLatin1String("UnitTestId4")); + QVERIFY2(objPtr4.valid() == true , "objectFactory: create44 failed"); + objList.append(SituationMap::RefPointer (objPtr4.get())); + + QVERIFY2(objList.count() == 4 , "objList: count failed"); + QVERIFY2(objFac.count() == (initialObjCount + 4) , "objectFactory: count failed"); + + // remove all: + for (SituationMap::RefPointer item : std::as_const(objList)) + item->removeMySelf(); + } + + QVERIFY2(objFac.count() == 0, "objectFactory: count failed"); +} +#endif + +//------------------------------------------------------------------------------------------------------- + +#define QVERIFY3(statement, description, context) \ + { \ + QString myDesc = QString( QLatin1String("%1:%2") ).arg(context, QLatin1String(description) ); \ + QVERIFY2(statement, myDesc.toUtf8().data()); \ + } + + +template +void checkGraphicalObjectVisibility(const QScopedPointer &go, const QString &objName) +{ + // Check visibility: + + // hide: + go->setVisibility(false); + QVERIFY3(go->updateView() == true, "updateView failed", objName); + + Esri::ArcGISRuntime::Graphic *graphic = go->graphic(); + QVERIFY3(graphic, "graphic failed", objName); + + // 1 + QVERIFY3(graphic->isVisible() == false, "isVisible failed1", objName); + + // show: + go->setVisibility(true); + QVERIFY3(go->updateView() == true, "updateView failed", objName); + QVERIFY3(go->changed() == false, "changed failed", objName); + QVERIFY3(graphic->isVisible() == true, "isVisible failed2", objName); + + // hide again: + go->setVisibility(false); + QVERIFY3(go->updateView() == true, "updateView failed", objName); + QVERIFY3(go->changed() == false, "changed failed", objName); + QVERIFY3(graphic->isVisible() == false, "isVisible failed3", objName); +} + + +class GraphObj : public SituationMap::AbstractGraphicalObject +{ + using BaseClass = AbstractGraphicalObject; +public: + using BaseClass::BaseClass; + using BaseClass::changed; + using BaseClass::resetChanged; + + bool updateViewImpl() override + { + return true; + } + bool create() override + { + return true; + } +}; + +#ifdef CrashUnderWindows +void SituationMapTestsMain::checkGraphicalObject() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + SituationMap::LayerFactory &layerFac = d->m_map->layerFactory(); + MapImpl *mapImpl = static_cast(d->m_map); + + + // use an LOGICAL layer (not basemap) + QStringList layernames = layerFac.layernames(); + QString layername = layernames[1]; + + // real test + static_cast(d->m_map)->getGraphicsLayer(layername); + + + QScopedPointer go(new GraphObj(mapImpl->getEsriGeoView(), + mapImpl->getGraphicsLayer(layername), + QLatin1String("TacticalAppearanceModuleTest"), + 4712)); + QVERIFY2(go, "objectFactory: GraphObj failed"); + + QVERIFY2(go->changed() == true, "GraphObj: changed failed"); + go->resetChanged(); + QVERIFY2(go->changed() == false, "GraphObj: changed failed"); + + QVERIFY2(go->isSinglePoint() == false, "GraphObj: isSinglePoint failed"); + QVERIFY2(go->isMultiPoint() == false, "GraphObj: isMultiPoint failed"); + QVERIFY2(go->isArea() == false, "GraphObj: isArea failed"); +} +#endif + +//------------------------------------------------------------------------------------------------------- +class GraphObjEllipse : public SituationMap::GraphicalObjectEllipse +{ + using BaseClass = GraphicalObjectEllipse; + +public: + using BaseClass::BaseClass; + using BaseClass::changed; + using BaseClass::resetChanged; +}; + +void SituationMapTestsMain::checkGraphicalObjectEllipse() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + SituationMap::LayerFactory &layerFac = d->m_map->layerFactory(); + MapImpl *mapImpl = static_cast(d->m_map); + + // use an LOGICAL layer (not basemap) + QStringList layernames = layerFac.layernames(); + QString layername = layernames[1]; + + // real test + mapImpl->getGraphicsLayer(layername); + + QScopedPointer go(new GraphObjEllipse(mapImpl->getEsriGeoView(), + mapImpl->getGraphicsLayer(layername), + QLatin1String("TacticalAppearanceModuleTest"), + 4712)); + QVERIFY2(go, "objectFactory: GraphObjEllipse failed"); + + QVERIFY2(go->changed() == true, "GraphObjEllipse: changed failed"); + go->resetChanged(); + QVERIFY2(go->changed() == false, "GraphObjEllipse: changed failed"); + + QVERIFY2(go->isSinglePoint() == false, "GraphObjEllipse: isSinglePoint failed"); + QVERIFY2(go->isMultiPoint() == false, "GraphObjEllipse: isMultiPoint failed"); + QVERIFY2(go->isArea() == false, "GraphObjEllipse: isArea failed"); + + go->setOrigin(Point3D(94000, 78000, .0)); + QVERIFY2(go->isSinglePoint() == true, "GraphObjEllipse: isSinglePoint failed"); + + SituationMap::EllipseParameters ellipsePara; + ellipsePara.setLengthX(1000); + ellipsePara.setLengthY(1000); + // ellipsePara.setDirectionDegree(0); // circle -> irrelevant + // ellipsePara.setMaxSegmentLength(10); // use default + + go->setEllipseParameters(ellipsePara); + QVERIFY2(go->changed() == true, "GraphObjEllipse: changed failed"); + + bool success = go->updateView(); + QVERIFY2(success == true, "GraphObjEllipse: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjEllipse: changed failed"); + + success = go->updateView(); + QVERIFY2(success == true, "GraphObjEllipse: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjEllipse: changed failed"); + + // Check visibility: + checkGraphicalObjectVisibility(go, QStringLiteral("GraphObjEllipse")); +} + +//------------------------------------------------------------------------------------------------------- +class GraphObjPath : public SituationMap::GraphicalObjectPath +{ + using BaseClass = GraphicalObjectPath; + +public: + using BaseClass::BaseClass; + using BaseClass::changed; + using BaseClass::resetChanged; +}; + +template +void runGraphicalObjectPath(SituationMap::Map *map, + const SituationMap::GraphicalObjectPathType type) +{ + SituationMap::LayerFactory &layerFac = map->layerFactory(); + MapImpl *mapImpl = static_cast(map); + + // use an LOGICAL layer (not basemap) + QStringList layernames = layerFac.layernames(); + QString layername = layernames[1]; + + mapImpl->getGraphicsLayer(layername); + + QScopedPointer go(new TYPE(type, + mapImpl->getEsriGeoView(), + mapImpl->getGraphicsLayer(layername), + QLatin1String("TacticalAppearanceModuleTest"), + 4712)); + QVERIFY2(go, "objectFactory: GraphObjPath failed"); + + QVERIFY2(go->changed() == true, "GraphObjPath: changed failed"); + go->resetChanged(); + QVERIFY2(go->changed() == false, "GraphObjPath: changed failed"); + + QVERIFY2(go->isSinglePoint() == false, "GraphObjPath: isSinglePoint failed"); + QVERIFY2(go->isMultiPoint() == false, "GraphObjPath: isMultiPoint failed"); + QVERIFY2(go->isArea() == false, "GraphObjPath: isArea failed"); + + // With coordinates + PositionCont posCont; + posCont << Point3D(94000, 78000, .0); + posCont << Point3D(95000, 70000, .0); + posCont << Point3D(94500, 78500, .0); + go->setPositions(posCont); + QVERIFY2(go->isSinglePoint() == false, "GraphObjPath: isSinglePoint failed"); + QVERIFY2(go->isMultiPoint() == true, "GraphObjPath: isSinglePoint failed"); + QVERIFY2(go->isArea() == true, "GraphObjPath: isSinglePoint failed"); + + QVERIFY2(go->changed() == true, "GraphObjPath: changed failed"); + + bool success = go->updateView(); + QVERIFY2(success == true, "GraphObjPath: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPath: changed failed"); + + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPath: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPath: changed failed"); + + // check incorrect positions: + + // check doubled start and end position: + { + // set positions: + SituationMap::PositionCont posCont; + QPointF center = map->centerPoint(); + Point3D center3D(center.x(), center.y(), .0); + + posCont << center3D + Point3D( 0, 0, .0); + posCont << center3D + Point3D( 0, 5000, .0); + posCont << center3D + Point3D(5000, 5000, .0); + posCont << center3D + Point3D( 0, 0, .0); + go->setPositions(posCont); + + bool success = go->updateView(); + QVERIFY2(success == true, "GraphObjPath: updateView failed"); + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPath: updateView failed"); + } + + // check doubled positions: + { + // set positions: + SituationMap::PositionCont posCont; + QPointF center = map->centerPoint(); + Point3D center3D(center.x(), center.y(), .0); + + posCont << center3D + Point3D( 0, 0, .0); + posCont << center3D + Point3D( 0, 0, .0); + posCont << center3D + Point3D( 0, 0, .0); + posCont << center3D + Point3D(5000, 0, .0); + posCont << center3D + Point3D(5000, 5000, .0); + posCont << center3D + Point3D( 0, 0, .0); + posCont << center3D + Point3D( 0, 0, .0); + go->setPositions(posCont); + + bool success = go->updateView(); + QVERIFY2(success == true, "GraphObjPath: updateView failed"); + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPath: updateView failed"); + } + + // Check visibility: + checkGraphicalObjectVisibility(go, QStringLiteral("GraphObjPath")); +} + +void SituationMapTestsMain::checkGraphicalObjectPath() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + // real test + runGraphicalObjectPath(d->m_map, SituationMap::GraphicalObjectPathType::Polyline); + runGraphicalObjectPath(d->m_map, SituationMap::GraphicalObjectPathType::Polygon); +} + + + + + +//------------------------------------------------------------------------------------------------------- +class GraphObjPictureMarker : public SituationMap::GraphicalObjectPictureMarker +{ + using BaseClass = GraphicalObjectPictureMarker; + +public: + using BaseClass::BaseClass; + using BaseClass::changed; + using BaseClass::resetChanged; + + int scaleCounter() const + { + return m_scaleCounter4Moduletest; + } + + bool imageDefaultSizeChanged() const + { + return m_imageDefaultSize.changed(); + } + + QSize esriPictureMarkerSize() const + { + if (!m_pictureMarkerSymbol) + return QSize(); + return QSize(m_pictureMarkerSymbol->width(), m_pictureMarkerSymbol->height()); + } + +}; + + +void runGraphicalObjectPictureMarker(SituationMap::Map *map, + const SituationMap::MarkerSymbolType markerType) +{ + SituationMap::LayerFactory &layerFac = map->layerFactory(); + MapImpl *mapImpl = static_cast(map); + + // use an LOGICAL layer (not basemap) + QStringList layernames = layerFac.layernames(); + QString layername = layernames[1]; + + // real test + QScopedPointer go(new GraphObjPictureMarker(mapImpl->getEsriGeoView(), + mapImpl->getGraphicsLayer(layername), + QLatin1String("TacticalAppearanceModuleTest"), + 4712)); + QVERIFY2(go, "objectFactory: GraphObjPictureMarker failed"); + + QVERIFY2(go->changed() == true, "GraphObjPictureMarker: changed failed"); + go->resetChanged(); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + + QVERIFY2(go->isSinglePoint() == false, "GraphObjPictureMarker: isSinglePoint failed"); + QVERIFY2(go->isMultiPoint() == false, "GraphObjPictureMarker: isMultiPoint failed"); + QVERIFY2(go->isArea() == false, "GraphObjPictureMarker: isArea failed"); + + go->setOrigin(Point3D(94000, 78000, .0)); + QVERIFY2(go->isSinglePoint() == true, "GraphObjPictureMarker: isSinglePoint failed"); + QVERIFY2(go->changed() == true, "GraphObjPictureMarker: changed failed"); + + go->setMarkerSymbol(markerType, 100); + QVERIFY2(go->changed() == true, "GraphObjPictureMarker: changed failed"); + + bool success = go->updateView(); + QVERIFY2(success == true, "GraphObjPictureMarker: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPictureMarker: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + + // Change: + QImage image(100, 100, QImage::Format_ARGB32); + image.fill(Qt::transparent); + + go->setImage(image); + QVERIFY2(go->changed() == true, "GraphObjPictureMarker: changed failed"); + + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPictureMarker: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPictureMarker: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); +} + + +void SituationMapTestsMain::checkGraphicalObjectPictureMarker1() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + runGraphicalObjectPictureMarker(d->m_map, SituationMap::MarkerSymbolType::Circle); + runGraphicalObjectPictureMarker(d->m_map, SituationMap::MarkerSymbolType::Square); + + runGraphicalObjectPictureMarker(d->m_map, SituationMap::MarkerSymbolType::PushPin2D); +} + +void SituationMapTestsMain::checkGraphicalObjectPictureMarker2() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + SituationMap::LayerFactory &layerFac = d->m_map->layerFactory(); + MapImpl *mapImpl = static_cast(d->m_map); + + // use an LOGICAL layer (not basemap) + QStringList layernames = layerFac.layernames(); + QString layername = layernames[1]; + + // real test + QScopedPointer go(new GraphObjPictureMarker(mapImpl->getEsriGeoView(), + mapImpl->getGraphicsLayer(layername), + QLatin1String("TacticalAppearanceModuleTest"), + 4712)); + QVERIFY2(go, "objectFactory: GraphObjPictureMarker failed"); + + QVERIFY2(go->changed() == true, "GraphObjPictureMarker: changed failed"); + go->resetChanged(); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + + QVERIFY2(go->isSinglePoint() == false, "GraphObjPictureMarker: isSinglePoint failed"); + QVERIFY2(go->isMultiPoint() == false, "GraphObjPictureMarker: isMultiPoint failed"); + QVERIFY2(go->isArea() == false, "GraphObjPictureMarker: isArea failed"); + + go->setOrigin(Point3D(94000, 78000, .0)); + QVERIFY2(go->isSinglePoint() == true, "GraphObjPictureMarker: isSinglePoint failed"); + + QImage image(100, 100, QImage::Format_ARGB32); + image.fill(Qt::transparent); + + go->setImage(image); + QVERIFY2(go->changed() == true, "GraphObjPictureMarker: changed failed"); + + bool success = go->updateView(); + QVERIFY2(success == true, "GraphObjPictureMarker: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPictureMarker: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + + // Change: + go->setMarkerSymbol(SituationMap::MarkerSymbolType::Cross, 100); + QVERIFY2(go->changed() == true, "GraphObjPictureMarker: changed failed"); + + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPictureMarker: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPictureMarker: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + + // Check visibility: + checkGraphicalObjectVisibility(go, QStringLiteral("GraphObjPictureMarker")); +} + + +//------------------------------------------------------------------------------------------------------- +void SituationMapTestsMain::checkGraphicalObjectPictureMarker3() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + SituationMap::LayerFactory &layerFac = d->m_map->layerFactory(); + MapImpl *mapImpl = static_cast(d->m_map); + + // use an LOGICAL layer (not basemap) + QStringList layernames = layerFac.layernames(); + QString layername = layernames[1]; + + // real test + QScopedPointer go(new GraphObjPictureMarker(mapImpl->getEsriGeoView(), + mapImpl->getGraphicsLayer(layername), + QLatin1String("TacticalAppearanceModuleTest"), + 4712)); + QVERIFY2(go, "objectFactory: GraphObjPictureMarker failed"); + + go->setOrigin(Point3D(94000, 78000, .0)); + QVERIFY2(go->isSinglePoint() == true, "GraphObjPictureMarker: isSinglePoint failed"); + + QImage image(100, 100, QImage::Format_ARGB32); + image.fill(Qt::red); + + bool success = false; + + qCritical() << " Goo !!!!"; + + + // Set image first time + { + go->setImage(image); + QVERIFY2(go->changed() == true, "GraphObjPictureMarker: changed failed"); + QVERIFY2(go->imageDefaultSizeChanged() == true, "GraphObjPictureMarker: imageDefaultSizeChanged failed"); + + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPictureMarker: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + QVERIFY2(go->imageDefaultSizeChanged() == false, "GraphObjPictureMarker: imageDefaultSizeChanged failed"); + qCritical() << " scaleCounter1a=" << go->scaleCounter(); + QVERIFY2(go->scaleCounter() == 0, "GraphObjPictureMarker: scaleCounter failed"); + + + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPictureMarker: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + QVERIFY2(go->imageDefaultSizeChanged() == false, "GraphObjPictureMarker: imageDefaultSizeChanged failed"); + qCritical() << " scaleCounter1b=" << go->scaleCounter(); + QVERIFY2(go->scaleCounter() == 0, "GraphObjPictureMarker: scaleCounter failed"); + + QVERIFY2(go->currentSize() == QSize(100, 100), "GraphObjPictureMarker: esriPictureMarkerSize failed"); + } + + // Set identical image again, nothing changed + { + go->setImage(image); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + QVERIFY2(go->imageDefaultSizeChanged() == false, "GraphObjPictureMarker: imageDefaultSizeChanged failed"); + + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPictureMarker: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + QVERIFY2(go->imageDefaultSizeChanged() == false, "GraphObjPictureMarker: imageDefaultSizeChanged failed"); + qCritical() << " scaleCounter2=" << go->scaleCounter(); + QVERIFY2(go->scaleCounter() == 0, "GraphObjPictureMarker: scaleCounter failed"); + + QVERIFY2(go->currentSize() == QSize(100, 100), "GraphObjPictureMarker: esriPictureMarkerSize failed"); + } + + // Set different image + { + QImage image2(200, 200, QImage::Format_ARGB32); + image2.fill(Qt::red); + + go->setImage(image2); + QVERIFY2(go->changed() == true, "GraphObjPictureMarker: changed failed"); + QVERIFY2(go->imageDefaultSizeChanged() == false, "GraphObjPictureMarker: imageDefaultSizeChanged failed"); + + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPictureMarker: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + QVERIFY2(go->imageDefaultSizeChanged() == false, "GraphObjPictureMarker: imageDefaultSizeChanged failed"); + qCritical() << " scaleCounter3=" << go->scaleCounter(); + QVERIFY2(go->scaleCounter() == 0, "GraphObjPictureMarker: scaleCounter failed"); + + QVERIFY2(go->currentSize() == QSize(200, 200), "GraphObjPictureMarker: esriPictureMarkerSize failed"); + } + + // + // use value for defaultSize: + // + { + const QSize myDefaultSize(50, 50); + + // Set image first time + { + go->setImage(image, myDefaultSize); + QVERIFY2(go->changed() == true, "GraphObjPictureMarker: changed failed"); + QVERIFY2(go->imageDefaultSizeChanged() == true, "GraphObjPictureMarker: imageDefaultSizeChanged failed"); + + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPictureMarker: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + QVERIFY2(go->imageDefaultSizeChanged() == false, "GraphObjPictureMarker: imageDefaultSizeChanged failed"); + qCritical() << " scaleCounter11a=" << go->scaleCounter(); + QVERIFY2(go->scaleCounter() == 1, "GraphObjPictureMarker: scaleCounter failed"); + + + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPictureMarker: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + QVERIFY2(go->imageDefaultSizeChanged() == false, "GraphObjPictureMarker: imageDefaultSizeChanged failed"); + qCritical() << " scaleCounter11b=" << go->scaleCounter(); + QVERIFY2(go->scaleCounter() == 1, "GraphObjPictureMarker: scaleCounter failed"); + + QVERIFY2(go->currentSize() == myDefaultSize, "GraphObjPictureMarker: esriPictureMarkerSize failed"); + } + + // Set different image, but keep value for defaultSize + { + QImage image2(200, 200, QImage::Format_ARGB32); + image2.fill(Qt::red); + + go->setImage(image2, myDefaultSize); + QVERIFY2(go->changed() == true, "GraphObjPictureMarker: changed failed"); + QVERIFY2(go->imageDefaultSizeChanged() == false, "GraphObjPictureMarker: imageDefaultSizeChanged failed"); + + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPictureMarker: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + QVERIFY2(go->imageDefaultSizeChanged() == false, "GraphObjPictureMarker: imageDefaultSizeChanged failed"); + qCritical() << " scaleCounter12=" << go->scaleCounter(); + QVERIFY2(go->scaleCounter() == 2, "GraphObjPictureMarker: scaleCounter failed"); + + QVERIFY2(go->currentSize() == myDefaultSize, "GraphObjPictureMarker: esriPictureMarkerSize failed"); + } + } + + // + // take scale into account: + // + { + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + QVERIFY2(go->imageDefaultSizeChanged() == false, "GraphObjPictureMarker: imageDefaultSizeChanged failed"); + + go->setScale(0.5); + + success = go->updateView(); + QVERIFY2(success == true, "GraphObjPictureMarker: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjPictureMarker: changed failed"); + QVERIFY2(go->imageDefaultSizeChanged() == false, "GraphObjPictureMarker: imageDefaultSizeChanged failed"); + qCritical() << " scaleCounter21=" << go->scaleCounter(); + QVERIFY2(go->scaleCounter() == 3, "GraphObjPictureMarker: scaleCounter failed"); + + QVERIFY2(go->currentSize() == QSize(25, 25), "GraphObjPictureMarker: esriPictureMarkerSize failed"); + } +} + + +//------------------------------------------------------------------------------------------------------- +class GraphObjRange : public SituationMap::GraphicalObjectRange +{ + using BaseClass = GraphicalObjectRange; + +public: + using BaseClass::BaseClass; + using BaseClass::changed; + using BaseClass::resetChanged; +}; + +void SituationMapTestsMain::checkGraphicalObjectRange() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + SituationMap::LayerFactory &layerFac = d->m_map->layerFactory(); + MapImpl *mapImpl = static_cast(d->m_map); + + // use an LOGICAL layer (not basemap) + QStringList layernames = layerFac.layernames(); + QString layername = layernames[1]; + + // real test + QScopedPointer go(new GraphObjRange(mapImpl->getEsriGeoView(), + mapImpl->getGraphicsLayer(layername), + QLatin1String("TacticalAppearanceModuleTest"), + 4712)); + QVERIFY2(go, "objectFactory: GraphObjRange failed"); + + QVERIFY2(go->changed() == true, "GraphObjRange: changed failed"); + go->resetChanged(); + QVERIFY2(go->changed() == false, "GraphObjRange: changed failed"); + + QVERIFY2(go->isSinglePoint() == false, "GraphObjRange: isSinglePoint failed"); + QVERIFY2(go->isMultiPoint() == false, "GraphObjRange: isMultiPoint failed"); + QVERIFY2(go->isArea() == false, "GraphObjRange: isArea failed"); + + go->setOrigin(Point3D(94000, 78000, .0)); + QVERIFY2(go->isSinglePoint() == true, "GraphObjRange: isSinglePoint failed"); + + // set value: + go->setRange(1000, 15); + QVERIFY2(go->changed() == true, "GraphObjRange: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjRange: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjRange: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjRange: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjRange: changed failed"); + + // Change: + go->setRange(2000, 15); + QVERIFY2(go->changed() == true, "GraphObjRange: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjRange: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjRange: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjRange: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjRange: changed failed"); + + // Change: + go->setRange(2000, -15); + QVERIFY2(go->changed() == true, "GraphObjRange: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjRange: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjRange: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjRange: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjRange: changed failed"); + + // Check visibility: + checkGraphicalObjectVisibility(go, QStringLiteral("GraphObjRange")); +} + + +//------------------------------------------------------------------------------------------------------- +class GraphObjText : public SituationMap::GraphicalObjectText +{ + using BaseClass = GraphicalObjectText; + +public: + using BaseClass::BaseClass; + using BaseClass::changed; + using BaseClass::resetChanged; +}; + +void SituationMapTestsMain::checkGraphicalObjectText() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + SituationMap::LayerFactory &layerFac = d->m_map->layerFactory(); + MapImpl *mapImpl = static_cast(d->m_map); + + // use an LOGICAL layer (not basemap) + QStringList layernames = layerFac.layernames(); + QVERIFY(layernames.size() > 1); + QString layername = layernames[1]; + + // real test + QScopedPointer go(new GraphObjText(mapImpl->getEsriGeoView(), + mapImpl->getGraphicsLayer(layername), + QLatin1String("TacticalAppearanceModuleTest"), + 4712)); + QVERIFY2(go, "objectFactory: GraphObjText failed"); + + QVERIFY2(go->changed() == true, "GraphObjText: changed failed"); + go->resetChanged(); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + QVERIFY2(go->isSinglePoint() == false, "GraphObjText: isSinglePoint failed"); + QVERIFY2(go->isMultiPoint() == false, "GraphObjText: isMultiPoint failed"); + QVERIFY2(go->isArea() == false, "GraphObjText: isArea failed"); + + go->setOrigin(Point3D(94000, 78000, .0)); + QVERIFY2(go->isSinglePoint() == true, "GraphObjText: isSinglePoint failed"); + + // set value: + + // + // Text1 + // + go->setText(QLatin1String("myDummyText")); + QVERIFY2(go->changed() == true, "GraphObjText: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + // Text2 + go->setText(QLatin1String("myDummyTextMODIFIED")); + QVERIFY2(go->changed() == true, "GraphObjText: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + // + // textStyle1 + // + go->rTextStyle().setSize(11.23456f); + QVERIFY2(go->changed() == true, "GraphObjText: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + // set identical value again -> not changed + const TextStyle ts = go->textStyle(); + go->setTextStyle(ts); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + + // textStyle2 + go->rTextStyle().setSize(111.9876f); + QVERIFY2(go->changed() == true, "GraphObjText: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + // + // setXOffset1 + // + go->setXOffset(10.876f); + QVERIFY2(go->changed() == true, "GraphObjText: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + // set identical value again -> not changed + go->setXOffset(10.876f); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + // setXOffset2 + go->setXOffset(10.123f); + QVERIFY2(go->changed() == true, "GraphObjText: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + // + // setYOffset1 + // + go->setYOffset(222.876f); + QVERIFY2(go->changed() == true, "GraphObjText: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + // set identical value again -> not changed + go->setYOffset(222.876f); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + // setYOffset2 + go->setYOffset(222.123f); + QVERIFY2(go->changed() == true, "GraphObjText: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + QVERIFY2(go->updateView() == true, "GraphObjText: updateView failed"); + QVERIFY2(go->changed() == false, "GraphObjText: changed failed"); + + // Check visibility: + checkGraphicalObjectVisibility(go, QStringLiteral("GraphObjText")); +} + +//------------------------------------------------------------------------------------------------------- +class GraphObjTactApp : public SituationMap::TacticalAppearance +{ + using BaseClass = TacticalAppearance; + +public: + using BaseClass::BaseClass; + using BaseClass::changed; + using BaseClass::resetChanged; +}; + +void SituationMapTestsMain::checkGraphicalObjectTacticalAppearance() +{ + if (d->m_symbologyType == AdvancedSymbology::SymbologyType::None) + return; + + // Preperations: + reInitTestCaseUntilSetCenter(); + + SituationMap::LayerFactory &layerFac = d->m_map->layerFactory(); + + // use an LOGICAL layer (not basemap) + QStringList layernames = layerFac.layernames(); + QString layername = layernames[1]; + + // real test + GraphObjTactApp *go = new GraphObjTactApp(static_cast(d->m_map), + layername, + QLatin1String("TacticalAppearanceModuleTest"), + 4711); + QVERIFY2(go->changed() == true, "TacticalAppearance: changed failed"); + go->resetChanged(); + QVERIFY2(go->changed() == false, "TacticalAppearance: changed failed"); + + QVERIFY2(go->isSinglePoint() == false, "TacticalAppearance: isSinglePoint failed"); + QVERIFY2(go->isMultiPoint() == false, "TacticalAppearance: isMultiPoint failed"); + QVERIFY2(go->isArea() == false, "TacticalAppearance: isArea failed"); + + // set value: + + go->setUniqueDesignation(QLatin1String("uniquDesignation")); + go->setVisibility(true); + go->setSelected(false); + go->setPickable(false); + go->setScale(2.34f); + QVERIFY2(go->changed() == true, "TacticalAppearance: changed failed"); + + SituationMap::SymbolData symbolData; + + // + // Check SINGLE-POINTSYMBOL + // + { + switch (d->m_symbologyType) + { + case AdvancedSymbology::SymbologyType::APP6D: + case AdvancedSymbology::SymbologyType::MIL2525D: + symbolData.sidc = AdvancedSymbology::SidcCodec::encodeComplete( d->m_symbologyType, "10051000001213030000" ); + break; + case AdvancedSymbology::SymbologyType::MIL2525C: + case AdvancedSymbology::SymbologyType::APP6B: + symbolData.sidc = AdvancedSymbology::SidcCodec::encodeComplete( d->m_symbologyType, "SFGPUCD---"); // PointSymbol + break; + case AdvancedSymbology::SymbologyType::None: + break; + } + go->setSymbol(symbolData); + + QVERIFY2(go->changed() == true, "TacticalAppearance: changed failed"); + + // no origin/position + QVERIFY2(go->updateView() == false, "TacticalAppearance: updateView failed"); // -> 3 * ERROR-msg + QVERIFY2(go->changed() == false, "TacticalAppearance: changed failed"); + + // with origin/position + QPointF origin = s_basePoint.geographic( QPointF(94000, 78000) ); + go->setOrigin(Point3D(origin.x(), origin.y(), .0)); + QVERIFY2(go->changed() == true, "TacticalAppearance: changed failed"); + QVERIFY2(go->updateView() == true, "TacticalAppearance: updateView failed"); + QVERIFY2(go->updateView() == true, "TacticalAppearance: updateView failed"); + QVERIFY2(go->changed() == false, "TacticalAppearance: changed failed"); + } + + // + // Check MULTI-POINTSYMBOL + // + { + switch (d->m_symbologyType) + { + case AdvancedSymbology::SymbologyType::APP6D: + case AdvancedSymbology::SymbologyType::MIL2525D: + symbolData.sidc = AdvancedSymbology::SidcCodec::encodeComplete(d->m_symbologyType, "10042500002701000000"); // 01042500002701000000 -> SymbolSet = 25 270100 (obstacle) + break; + case AdvancedSymbology::SymbologyType::MIL2525C: + symbolData.sidc = AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::MIL2525C, "GUMPNB----"); // MIL2525: 1100, GHSPAR----, Forward Arming and Refueling Area H -> Polygon + break; + case AdvancedSymbology::SymbologyType::APP6B: + symbolData.sidc = AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::APP6B, "GHTPGC----"); // App6B: 3370, GHTPGC----, Task - Canalize H, -> Polyline + break; + case AdvancedSymbology::SymbologyType::None: + break; + } + + if (! symbolData.sidc.isEmpty()) + { + go->setSymbol(symbolData); + + // With ONE coordinate + QVERIFY2(go->updateView() == true, "TacticalAppearance: updateView failed"); + QVERIFY2(go->changed() == false, "TacticalAppearance: changed failed"); + + // With coordinates + QList posCont2D; + posCont2D << s_basePoint.geographic( QPointF(94000, 78000) ); + posCont2D << s_basePoint.geographic( QPointF(95000, 70000) ); + posCont2D << s_basePoint.geographic( QPointF(94500, 78500) ); + go->setPositions(convert2D3D(posCont2D)); + + QVERIFY2(go->changed() == true, "TacticalAppearance: changed failed"); + QVERIFY2(go->updateView() == true, "TacticalAppearance: updateView failed"); + QVERIFY2(go->updateView() == true, "TacticalAppearance: updateView failed"); + QVERIFY2(go->changed() == false, "TacticalAppearance: changed failed"); + } + } + + // cleanup: + delete go; +} + +//------------------------------------------------------------------------------------------------------- +class BasePathObjImp : public SituationMap::BasicPathObjectImpl +{ + using BaseClass = BasicPathObjectImpl; + +public: + using BaseClass::BaseClass; + using BaseClass::init; +}; + + + +void SituationMapTestsMain::checkBasicPathObjectImpl() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + SituationMap::LayerFactory &layerFac = d->m_map->layerFactory(); + SituationMap::ObjectFactory &objFac = d->m_map->objectFactory(); + + // use an LOGICAL layer (not basemap) + const QStringList layernames = layerFac.layernames(); + QVERIFY(layernames.size() > 1); + QString layername = layernames[1]; + + // real test + BasePathObjImp *pathObj = new BasePathObjImp(QLatin1String("BasicPathObjectModuleTest")); + bool initOk = pathObj->init(static_cast(d->m_map), + layername, + SituationMap::GraphicalObjectPathType::Polygon); + QVERIFY2(initOk == true, "BasicPathObjectImpl: init failed"); + + // set positions: + SituationMap::PositionCont posCont; + // check empty container: + pathObj->setPositions(posCont); + bool success = pathObj->updateView(); + QVERIFY2(success == false, "BasicPathObjectImpl: updateView failed"); + + + // check empty container with ArrowHeads + pathObj->setArrowHeadStart(true); + pathObj->setArrowHeadEnd(true); + success = pathObj->updateView(); + QVERIFY2(success == false, "BasicPathObjectImpl: updateView failed"); + + + // check container without ArrowHeads + pathObj->setArrowHeadStart(false); + pathObj->setArrowHeadEnd(false); + QPointF center = d->m_map->centerPoint(); + + QList posCont2D; + posCont2D << s_basePoint.geographic( center + QPointF( 0, 0) ); + posCont2D << s_basePoint.geographic( center + QPointF( 0, 5000) ); + posCont2D << s_basePoint.geographic( center + QPointF(5000, 5000) ); + posCont2D << s_basePoint.geographic( center + QPointF(5000, 0) ); + pathObj->setPositions(convert2D3D(posCont2D)); + + success = pathObj->updateView(); + QVERIFY2(success == true, "BasicPathObjectImpl: updateView failed"); + int numberOfValuesChanged = pathObj->numberOfValuesChanged(); + INFO_LOG(m_logger) << " numberOfValuesChanged = " << numberOfValuesChanged; + QVERIFY2(numberOfValuesChanged == 0, "BasicPathObject: numberOfValuesChanged failed"); + + success = pathObj->updateView(); + QVERIFY2(success == true, "BasicPathObjectImpl: updateView failed"); + numberOfValuesChanged = pathObj->numberOfValuesChanged(); + INFO_LOG(m_logger) << " numberOfValuesChanged = " << numberOfValuesChanged; + QVERIFY2(numberOfValuesChanged == 0, "BasicPathObject: numberOfValuesChanged failed"); + + QTest::qWait(timeToDelay); + + + pathObj->setArrowHeadStart(true); + pathObj->setArrowHeadEnd(true); + success = pathObj->updateView(); + QVERIFY2(success == true, "BasicPathObjectImpl: updateView failed"); + numberOfValuesChanged = pathObj->numberOfValuesChanged(); + INFO_LOG(m_logger) << " numberOfValuesChanged = " << numberOfValuesChanged; + QVERIFY2(numberOfValuesChanged == 0, "BasicPathObject: numberOfValuesChanged failed"); + + for (int i = 0; i < 2; ++i) + pathObj->setTextOnSegment(i, QStringLiteral("segText_%1").arg(i)); + success = pathObj->updateView(); + QVERIFY2(success == true, "BasicPathObjectImpl: updateView failed"); + numberOfValuesChanged = pathObj->numberOfValuesChanged(); + INFO_LOG(m_logger) << " numberOfValuesChanged = " << numberOfValuesChanged; + QVERIFY2(numberOfValuesChanged == 0, "BasicPathObject: numberOfValuesChanged failed"); + + QTest::qWait(timeToDelay); + + +// QSignalSpy spy(d->m_sitMap, SIGNAL(mapReady())); +// spy.wait(5000); + + // set origin/radius: + // !!!arrowHeads will disappear!!! + QPointF center2D = s_basePoint.geographic(center); + pathObj->setOrigin(Point3D(center2D.x(), center2D.y(), .0)); + pathObj->setRadius(2000); + success = pathObj->updateView(); + QVERIFY2(success == true, "BasicPathObjectImpl: updateView failed"); + numberOfValuesChanged = pathObj->numberOfValuesChanged(); + INFO_LOG(m_logger) << " numberOfValuesChanged = " << numberOfValuesChanged; + QVERIFY2(numberOfValuesChanged == 0, "BasicPathObject: numberOfValuesChanged failed"); + + + // setIdentifierVisible + pathObj->setIdentifierVisible(true); + success = pathObj->updateView(); + QVERIFY2(success == true, "BasicPathObjectImpl: updateView failed"); + numberOfValuesChanged = pathObj->numberOfValuesChanged(); + INFO_LOG(m_logger) << " numberOfValuesChanged = " << numberOfValuesChanged; + QVERIFY2(numberOfValuesChanged == 0, "BasicPathObject: numberOfValuesChanged failed"); + + QTest::qWait(timeToDelay); + +#ifdef SUPPORT_SUBSIDIARY_LINES + // + // Subsidiary: + // + QList subsidiaryIdxInUse; + + // check pathIndexInUse: + subsidiaryIdxInUse = pathObj->subsidiaryPathIndexInUse(); + QVERIFY2(subsidiaryIdxInUse.size() == 0 , "BasicPathObjectImpl: subsidiaryPathIndexInUse failed"); + + // check non-existing: + SituationMap::PositionCont retrieved; + retrieved = pathObj->subsidiaryPositions(12345); + QVERIFY2(retrieved.size() == 0 , "BasicPathObjectImpl: subsidiaryPositions failed"); + + // set path: + SituationMap::PositionCont posCont2; + posCont2 << center + QPointF(0, 0); + posCont2 << center + QPointF(0, 3000); + posCont2 << center + QPointF(3000, 3000); + + pathObj->setSubsidiaryPositions(12, posCont2); + const SituationMap::PositionCont &posContTemp = pathObj->subsidiaryPositions(12); + QVERIFY2(posContTemp == posContTemp, "BasicPathObjectImpl: subsidiaryPositions failed"); + + + success = pathObj->updateView(); + QVERIFY2(success == true, "BasicPathObjectImpl: updateView (setSubsidiaryPositions) failed"); + numberOfValuesChanged = pathObj->numberOfValuesChanged(); + INFO_LOG(m_logger) << " numberOfValuesChanged = " << numberOfValuesChanged; + QVERIFY2(numberOfValuesChanged == 0, "BasicPathObject: numberOfValuesChanged failed"); + + // Overlapping polygons (with fillpattern solid) will NOT show an fillpattern within the onverlapping area!!! + // Reason: ??? + // 2015-11-19, Version 10.2.6) + + QTest::qWait(timeToDelay); + + + subsidiaryIdxInUse = pathObj->subsidiaryPathIndexInUse(); + QVERIFY2(subsidiaryIdxInUse.size() == 1 , "BasicPathObjectImpl: subsidiaryPathIndexInUse failed"); + + // retrieve: + retrieved = pathObj->subsidiaryPositions(12); + QVERIFY2(posCont2 == retrieved , "BasicPathObjectImpl: subsidiaryPositions failed"); + + // clear: + pathObj->clearSubsidiaryPositions(); + subsidiaryIdxInUse = pathObj->subsidiaryPathIndexInUse(); + QVERIFY2(subsidiaryIdxInUse.size() == 0 , "BasicPathObjectImpl: subsidiaryPathIndexInUse failed"); +#endif // SUPPORT_SUBSIDIARY_LINES + + + // cleanup: + delete pathObj; + QVERIFY2(objFac.count() == 0 , "objectFactory: count failed"); + + + // + // check identical positions: + // + + { + // real test + QScopedPointer pathObj(new BasePathObjImp(QLatin1String("BasicPathObjectModuleTest"))); + bool initOk = pathObj->init(static_cast(d->m_map), + layername, + SituationMap::GraphicalObjectPathType::Polygon); + QVERIFY2(initOk == true, "BasicPathObjectImpl: init failed"); + + // check doubled start and end position: + { + // set positions: + QList posCont; + QPointF center = d->m_map->centerPoint(); + qWarning() << " center=" << center; + posCont << center + QPointF(0, 0); + posCont << center + QPointF(0, 0.001); + posCont << center + QPointF(0.001, 0.001); + posCont << center + QPointF(0, 0); + pathObj->setPositions(convert2D3D(posCont2D)); + + bool success = pathObj->updateView(); + QVERIFY2(success == true, "BasicPathObjectImpl: updateView failed"); + success = pathObj->updateView(); + QVERIFY2(success == true, "BasicPathObjectImpl: updateView failed"); + + QTest::qWait(timeToDelay); + } + + // check doubled positions: + { + // set positions: + SituationMap::PositionCont posCont; + QPointF center2D = d->m_map->centerPoint(); + Point3D center(center2D.x(), center2D.y(), .0); + + posCont << center + Point3D(0, 0, .0); + posCont << center + Point3D(0, 0, .0); + posCont << center + Point3D(0, 0, .0); + posCont << center + Point3D(0.001, 0, .0); + posCont << center + Point3D(0.001, 0.001, .0); + posCont << center + Point3D(0, 0, .0); + posCont << center + Point3D(0, 0, .0); + pathObj->setPositions(posCont); + + bool success = pathObj->updateView(); + QVERIFY2(success == true, "BasicPathObjectImpl: updateView failed"); + success = pathObj->updateView(); + QVERIFY2(success == true, "BasicPathObjectImpl: updateView failed"); + QTest::qWait(timeToDelay); + } + + } + +} + +void SituationMapTestsMain::checkChemicalAgent() +{ + // Preperations: + reInitTestCaseUntilSetCenter(); + + SituationMap::ObjectFactory &objFac = d->m_map->objectFactory(); + SituationMap::LayerFactory &layerFac = d->m_map->layerFactory(); + + // use an LOGICAL layer (not basemap) + QStringList layernames = layerFac.layernames(); + QString layername = layernames[1]; + + // real test + + SituationMap::RefPointer cAgent; + cAgent = objFac.create(layername, QLatin1String("ChemAgentId1")); + QVERIFY2(cAgent.valid() == true , "objectFactory: create failed"); + + QVERIFY2(objFac.count() == 1 , "objectFactory: count failed"); + + QList posCont2D; + posCont2D << s_basePoint.geographic( QPointF( 1000, 1000) ); + posCont2D << s_basePoint.geographic( QPointF(85000, 70000) ); + posCont2D << s_basePoint.geographic( QPointF(84500, 78500) ); + cAgent->setPositions(convert2D3D(posCont2D)); + + bool success = cAgent->updateView(); + QVERIFY2(success == true, "ChemicalAgent: updateView failed"); + success = cAgent->updateView(); + QVERIFY2(success == true, "ChemicalAgent: updateView failed"); + + QTest::qWait(timeToDelay); + + QPointF origin = s_basePoint.geographic( QPointF(0, 1000) ); + cAgent->setOrigin(Point3D::fromQPointF(origin)); + + cAgent->setRadius(20000); + success = cAgent->updateView(); + QVERIFY2(success == true, "ChemicalAgent: updateView failed"); + +} + +void SituationMapTestsMain::checkParticipant() +{ + if (d->m_symbologyType == AdvancedSymbology::SymbologyType::None) + + return; + + + + // Preperations: + reInitTestCaseUntilSetCenter(); + + SituationMap::ObjectFactory &objFac = d->m_map->objectFactory(); + SituationMap::LayerFactory &layerFac = d->m_map->layerFactory(); + + // use an LOGICAL layer (not basemap) + QStringList layernames = layerFac.layernames(); + QString layername = layernames[1]; + + // real test + + SituationMap::RefPointer objPtr1; + objPtr1 = objFac.create(layername, QLatin1String("UnitTestId1")); + QVERIFY2(objPtr1.valid() == true , "objectFactory: create failed"); + + QVERIFY2(objFac.count() == 1 , "objectFactory: count failed"); + + objPtr1->setTextModifier(SituationMap::Participant::TextModifier::EF_ReinforcedReduced, QLatin1String("EF_ReinforcedReduced")); + objPtr1->setTextModifier(SituationMap::Participant::TextModifier::M_HigherFormation, QLatin1String("M_HigherFormation")); + objPtr1->setTextModifier(SituationMap::Participant::TextModifier::T_UniqueDesignation, QLatin1String("T_UniqueDesignation")); + objPtr1->setTextModifier(SituationMap::Participant::TextModifier::W_DateTimeGroup, QLatin1String("W_DateTimeGroup")); + + objPtr1->setVisibility(true); +// objPtr1->setVisibility(SituationMap::ObjectAppearance::Technical, false); // no sense + + + SituationMap::SymbolData symData; + symData.sidc = QLatin1String("SUGPUSAJ--****X"); + // 123456789012345 + + symData.color = Qt::cyan; // Will be overwritten via partyId. + symData.size = 0.5; + + objPtr1->setSymbol(symData); + objPtr1->setScale(2.0); + objPtr1->setPartyId(4); + + QPointF origin = s_basePoint.geographic( QPointF(94000, 78003) ); + objPtr1->setOrigin(Point3D::fromQPointF(origin)); + + objPtr1->setInjury(SituationMap::Injury::TotalKill, 3); + + objPtr1->setAdditionalSymbol(SituationMap::AdditionalSymbolType::NbcProtection, true); + objPtr1->setAdditionalSymbol(SituationMap::AdditionalSymbolType::Manipulation, true); + objPtr1->setAdditionalSymbol(SituationMap::AdditionalSymbolType::NoPosition, true); + objPtr1->setAdditionalSymbol(SituationMap::AdditionalSymbolType::BiteError, true); + + objPtr1->setMarked(false); + + objPtr1->setRanges(QVector() << 1000 << 10000); + + bool success = objPtr1->updateView(); + QVERIFY2(success == true, "participant: updateView failed"); + + SituationMap::ParticipantImpl *partiImpl = dynamic_cast(objPtr1.get()); + + if (partiImpl) + { + int numberOfValuesChanged = partiImpl->numberOfValuesChanged(); + INFO_LOG(m_logger) << " numberOfValuesChanged = " << numberOfValuesChanged; + QVERIFY2(numberOfValuesChanged == 0, "participant: numberOfValuesChanged failed"); + + bool success2 = objPtr1->updateView(); + QVERIFY2(success2 == true, "participant: updateView failed"); + + int numberOfValuesChanged2 = partiImpl->numberOfValuesChanged(); + INFO_LOG(m_logger) << " numberOfValuesChanged2 = " << numberOfValuesChanged2; + QVERIFY2(numberOfValuesChanged2 == 0, "participant: numberOfValuesChanged2 failed"); + + } + + // remove: + objPtr1->removeMySelf(); + QVERIFY2(objFac.count() == 0, "objectFactory: count failed"); + +} + + +void SituationMapTestsMain::checkEventDispatcher() +{ + reInitTestCaseUntilSetCenter(); + + QGraphicsView * mapWidget = d->m_map->getQGraphicsView(); + + SituationMap::RB::EventDispatcher * dispatcher1 = new SituationMap::RB::EventDispatcher( static_cast(mapWidget->viewport()) ); + unsigned int noOfDisp1 = dispatcher1->numberOfEventDispatcher(); + + SituationMap::RB::EventDispatcher * dispatcher2 = new SituationMap::RB::EventDispatcher( static_cast(mapWidget->viewport()) ); + unsigned int noOfDisp2 = dispatcher1->numberOfEventDispatcher(); + + QVERIFY2( noOfDisp1 == 2 , "wrong number of event dispatcher (first test)"); + QVERIFY2( noOfDisp2 == noOfDisp1+1, "wrong number of event dispatcher (second test)"); + + delete dispatcher2; + + noOfDisp2 = dispatcher1->numberOfEventDispatcher(); + QVERIFY2(noOfDisp2 == noOfDisp1, "wrong number of event dispatcher (third test)"); + +} // SituationMapTestsMain::checkEventDispatcher + + + +static bool rubberbandcheckIsComplete = false; +static QVector rubberbandVertexList; + +template void SituationMapTestsMain::checkRubberband(const char *className) +{ + reInitTestCaseUntilSetCenter(); + + static bool testok = true; + + auto slotFunc = [ = ](SituationMap::RB::RubberbandBase * rubberband) + { + if (rubberband->coordinatesAreValid()) + { + QVector coordinateVector = rubberband->coordinateVector(); + if (coordinateVector.size() == rubberbandVertexList.size()) + { + for (int i = 0; i < coordinateVector.size(); ++i) + { + QPointF simPoint = coordinateVector[i]; + QPointF p = rubberbandVertexList[i]; + if ((fabs(simPoint.x() - p.x()) > d->m_map->pixelSizeMeter()) || (fabs(simPoint.y() - p.y()) > d->m_map->pixelSizeMeter())) + testok = false; + } + } + else testok = false; + } + rubberbandcheckIsComplete = true; + }; // slotFunc + + + rubberbandcheckIsComplete = false; + testok = true; + + RubberbandClass *rband = d->m_map->rubberbandFactory().create(); + + static char msg[200]; + + if (rband == nullptr) + { + sprintf(msg, "%s rubberband factory failure", className); + QVERIFY2(false, msg); + } + else + { + rband->setCoordinateVector(rubberbandVertexList); + + connect(rband, &SituationMap::RB::Rubberband::apply, slotFunc); + connect(rband, &SituationMap::RB::Rubberband::abort, slotFunc); + + QTimer::singleShot(200, this, [ = ]() + { + rband->applyAndEndAction(nullptr, 0, 0); + }); + + // Wait for finish Rubberband + while (!rubberbandcheckIsComplete) + QCoreApplication::processEvents(); + + delete rband; rband = nullptr; // here it is ok to use delete directly + + sprintf(msg, "%s coordinate test failed", className); + QVERIFY2(testok, msg); + } +} // SituationMapTestsMain::checkRubberband + + + +void SituationMapTestsMain::checkRubberbandMultiPointFeature() +{ + SituationMap::CoordTranslationFunctor * ct = d->m_map->coordTranslationFunctor(); + rubberbandVertexList.clear(); + + if (ct) + { + rubberbandVertexList << ct->mapToGeographic(QPointF(-338163 , 168551 )+d->m_geoOff) + << ct->mapToGeographic(QPointF( 154770 , 168551 )+d->m_geoOff) + << ct->mapToGeographic(QPointF( 154770 , - 71025 )+d->m_geoOff) + << ct->mapToGeographic(QPointF(-338163 , - 71025 )+d->m_geoOff) + << ct->mapToGeographic(QPointF( 36042.4 , 38162.5)+d->m_geoOff) + << ct->mapToGeographic(QPointF( 302120 , - 46643.1)+d->m_geoOff) + << ct->mapToGeographic(QPointF( 244876 , -224735 )+d->m_geoOff) + << ct->mapToGeographic(QPointF(- 21201.4, -139929 )+d->m_geoOff) + ; + } + else + { + rubberbandVertexList << QPointF(-338163 , 168551 )+d->m_geoOff + << QPointF( 154770 , 168551 )+d->m_geoOff + << QPointF( 154770 , - 71025 )+d->m_geoOff + << QPointF(-338163 , - 71025 )+d->m_geoOff + << QPointF( 36042.4 , 38162.5)+d->m_geoOff + << QPointF( 302120 , - 46643.1)+d->m_geoOff + << QPointF( 244876 , -224735 )+d->m_geoOff + << QPointF(- 21201.4, -139929 )+d->m_geoOff + ; + } + + checkRubberband("RubberbandMultiPointFeature"); + +} // SituationMapTestsMain::checkRubberbandMultiPointFeature + + + +void SituationMapTestsMain::checkRubberbandRectangle() +{ + SituationMap::CoordTranslationFunctor * ct = d->m_map->coordTranslationFunctor(); + rubberbandVertexList.clear(); + + if (ct) + { + rubberbandVertexList << ct->mapToGeographic( QPointF (154770 , 168551)+d->m_geoOff) + << ct->mapToGeographic(QPointF( 154770 , - 71025)+d->m_geoOff) + << ct->mapToGeographic(QPointF(-338163 , - 71025)+d->m_geoOff) + << ct->mapToGeographic(QPointF(-338163 , 168551)+d->m_geoOff) + ; + } + else + { + rubberbandVertexList << QPointF (154770 , 168551)+d->m_geoOff + << QPointF( 154770 , - 71025)+d->m_geoOff + << QPointF(-338163 , - 71025)+d->m_geoOff + << QPointF(-338163 , 168551)+d->m_geoOff + ; + } + + reInitTestCaseUntilSetCenter(); + + static bool testok = true; + + auto slotFunc = [ = ](SituationMap::RB::RubberbandBase * rubberband) + { + // check if coordinates of rubberband and input vector are remaining identical + if (rubberband->coordinatesAreValid()) + { + const QVector & coordinateVector = rubberband->coordinateVector(); + if (coordinateVector.size() == rubberbandVertexList.size()) + { + double mapPixelSize = d->m_map->pixelSizeMeter(); + + for (int i = 0; i < coordinateVector.size(); ++i) + { + const QPointF &simPoint = coordinateVector[i]; + int matchCount = 0; + for (int j = 0; j < rubberbandVertexList.size(); ++j) + { + QPointF p = rubberbandVertexList[j]; + double dx = fabs(simPoint.x() - p.x()); + double dy = fabs(simPoint.y() - p.y()); + if ((dx < mapPixelSize) && (dy < mapPixelSize)) + { + ++matchCount; + } + } + if (matchCount != coordinateVector.size()) testok = false; + } + } + else testok = false; + } + rubberbandcheckIsComplete = true; + }; // slotFunc + + + rubberbandcheckIsComplete = false; + testok = true; + + SituationMap::RB::RubberbandRectangle *rband = d->m_map->rubberbandFactory().create(); + + if (rband == nullptr) + QVERIFY2(false, "RubberbandRectangle rubberband factory failure"); + else + { + rband->setCoordinateVector(rubberbandVertexList); + + connect(rband, &SituationMap::RB::Rubberband::apply, slotFunc); + connect(rband, &SituationMap::RB::Rubberband::abort, slotFunc); + + QTimer::singleShot(200, this, [ = ]() + { + rband->applyAndEndAction(nullptr, 0, 0); + }); + + // Wait for finish Rubberband + while (!rubberbandcheckIsComplete) + QCoreApplication::processEvents(); + + delete rband; rband = nullptr; // here it is ok to use delete directly + + QVERIFY2(testok, "RubberbandRectangle coordinate test failed"); + + } +} // SituationMapTestsMain::checkRubberbandRectangle + + +void SituationMapTestsMain::checkRubberbandRectangleIndex() +{ + SituationMap::CoordTranslationFunctor * ct = d->m_map->coordTranslationFunctor(); + rubberbandVertexList.clear(); + + if (ct) + { + rubberbandVertexList << ct->mapToGeographic( QPointF (154770 , 168551)+d->m_geoOff) + << ct->mapToGeographic(QPointF( 154770 , - 71025)+d->m_geoOff) + << ct->mapToGeographic(QPointF(-338163 , - 71025)+d->m_geoOff) + <mapToGeographic( QPointF(-338163 , 168551)+d->m_geoOff) + ; + } + else + { + rubberbandVertexList << QPointF (154770 , 168551)+d->m_geoOff + << QPointF( 154770 , - 71025)+d->m_geoOff + << QPointF(-338163 , - 71025)+d->m_geoOff + << QPointF(-338163 , 168551)+d->m_geoOff + ; + } + + reInitTestCaseUntilSetCenter(); + + static bool testok = true; + + auto slotFunc = [ = ](SituationMap::RB::RubberbandBase *rubberband) + { + if (rubberband->coordinatesAreValid()) + { + QVector coordinateVector = rubberband->coordinateVector(); + if (coordinateVector.size() == rubberbandVertexList.size()) + { + for (int i = 0; i < coordinateVector.size(); ++i) + { + const QPointF & simPoint = coordinateVector[i]; + const QPointF & p = rubberbandVertexList[i]; + if ((fabs(simPoint.x() - p.x()) > d->m_map->pixelSizeMeter()) && (fabs(simPoint.y() - p.y()) > d->m_map->pixelSizeMeter())) + { + testok = false; + } + } + } + else testok = false; + } + rubberbandcheckIsComplete = true; + }; // slotFunc + + + rubberbandcheckIsComplete = false; + testok = true; + + SituationMap::RB::RubberbandRectangle *rband = d->m_map->rubberbandFactory().create(); + + if (rband == nullptr) + QVERIFY2(false, "RubberbandRectangle rubberband factory failure"); + else + { + rband->setCoordinateVector(rubberbandVertexList); + + connect(rband, &SituationMap::RB::Rubberband::apply, slotFunc); + connect(rband, &SituationMap::RB::Rubberband::abort, slotFunc); + + QTimer::singleShot(200, this, [ = ]() + { + rband->applyAndEndAction(nullptr, 0, 0); + }); + + // Wait for finish Rubberband + while (!rubberbandcheckIsComplete) + QCoreApplication::processEvents(); + + delete rband; rband = nullptr; // here it is ok to use delete directly + + QVERIFY2(testok, "RubberbandRectangle coordinate index test failed"); + } +} // SituationMapTestsMain::checkRubberbandRectangleIndex + + +void SituationMapTestsMain::checkRubberbandCorridor() +{ + rubberbandVertexList.clear(); + + checkRubberband( "RubberbandCorridor" ); +} // SituationMapTestsMain::checkRubberbandCorridor + + +template void SituationMapTestsMain::checkRubberbandProjection(const char *className) +{ + reInitTestCaseUntilSetCenter(); + + SituationMap::CoordTranslationFunctor * ct = d->m_map->coordTranslationFunctor(); + rubberbandVertexList.clear(); + + if (ct) + { + rubberbandVertexList << ct->mapToGeographic(QPointF( 154770 , 168551)+d->m_geoOff) + << ct->mapToGeographic(QPointF(-338163 , 168551)+d->m_geoOff) + << ct->mapToGeographic(QPointF( 154770 , - 71025)+d->m_geoOff) + ; + } + else + { + rubberbandVertexList << QPointF (154770 , 168551)+d->m_geoOff + << QPointF(-338163 , 168551)+d->m_geoOff + << QPointF( 154770 , - 71025)+d->m_geoOff + ; + } + + SituationMap::RB::Rubberband *rband = d->m_map->rubberbandFactory().create(); + + if (rband) + { + rband->setCoordinateVector(rubberbandVertexList); + bool mismatch = false; + QVector vertexList = rband->coordinateVector(); + + for (int i = 0; i < vertexList.size(); ++i) + { + qreal dx = fabs(vertexList[i].x() - rubberbandVertexList[i].x()); + qreal dy = fabs(vertexList[i].y() - rubberbandVertexList[i].y()); + + if ((dx > 0.001) && (dy > 0.001)) mismatch = true; + } + + delete rband; + + static char msg[200]; + + sprintf(msg, "%s oordinate projection failed.", className); + QVERIFY2((!mismatch), msg); + } +} // SituationMapTestsMain::checkRubberbandProjection + + +void SituationMapTestsMain::checkRubberCoordinateProjection() +{ + checkRubberbandProjection("RubberbandPointCluster"); + checkRubberbandProjection("RubberbandMultiPointFeature"); +} // SituationMapTestsMain::checkRubberCoordinateProjection + + +//------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------- + +void SituationMapTestsMain::reInitTestCaseUntilSetCenter() +{ + // Preperations: + initTestCase(); + checkMapReady(); +// // checkCenter(); + + SituationMap::LayerFactory &layerFac = d->m_map->layerFactory(); + INFO_LOG(m_logger) << " layernames = " << layerFac.layernames().join(QLatin1String(";")); + + + if (layerFac.isUsingBaseMapBuiltIn()) + { + d->m_basemapCountRef = 1; + } else { + d->m_basemapCountRef = 1; // maybe '2', depending on ini + } + + +#ifdef CHECK_LAYER + int basemapCount = layerFac.layernames(LayerType::Basemap).count(); + int jumpPointLayerCount = 0; + jumpPointLayerCount = layerFac.layernames(LayerType::JumpPoint).count(); + QVERIFY2(jumpPointLayerCount == 1 , "layerFactory: count failed"); + + int operationalLayerCount = layerFac.layernames(LayerType::Operational).count(); + QVERIFY2(basemapCount == d->m_basemapCountRef , "layerFactory: count failed"); + QVERIFY2(operationalLayerCount == 9 , "layerFactory: count failed"); // AES, Participants + + int technLayerCount = d->m_map->technicalLayerCount(); + INFO_LOG(m_logger) << " technicalLayerCount = " << technLayerCount; + QVERIFY2(technLayerCount == basemapCount, "objectFactory: count failed"); + + INFO_LOG(m_logger) << " basemapCount = " << basemapCount; + INFO_LOG(m_logger) << " jumpPointLayerCount = " << jumpPointLayerCount; + INFO_LOG(m_logger) << " operationalLayerCount = " << operationalLayerCount; + QVERIFY2(layerFac.count() == basemapCount + jumpPointLayerCount + operationalLayerCount , "layerFactory: count failed"); + + SituationMap::ObjectFactory &objFac = d->m_map->objectFactory(); + INFO_LOG(m_logger) << " objFac.count() =" << objFac.count(); + + QVERIFY2(objFac.count() == 0, "objectFactory: count failed"); +#endif +} + + +//------------------------------------------------------------------------------------------------------- +void SituationMapTestsMain::cleanupTestCase() +{ + DEBUG_LOG(m_logger) << LOGFUNCTION; + delete d->m_sitMap; + DEBUG_LOG(m_logger) << LOGFUNCTION; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/situation_map_test_main.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/situation_map_test_main.h new file mode 100755 index 000000000..4bf4cf953 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/ModuleTests/situation_map_test_main.h @@ -0,0 +1,118 @@ +/** + * @RDE_HEADER@ + * Moduletest + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include +#include +#include + +#include + +namespace AdvancedSymbology +{ +enum class SymbologyType; +} + + +class SituationMapTestsMain : public QObject +{ + Q_OBJECT + +public: + SituationMapTestsMain(AdvancedSymbology::SymbologyType symbologyType); + virtual ~SituationMapTestsMain(); + +private Q_SLOTS: + + void initTestCase(); + + void checkMapReady(); + void checkMapNotifier(); + void checkCenter(); + void checkExtent(); + + void checkMapIsUTM(); + + +// void convertMgrsBasePoint(); // not supported by QuartztBeta1 + void convertUTMBasePoint(); + + void checkOriginTranslated(); + void checkPositionsTranslated(); + void checkPositionAngles(); + + + void measureProject(); + void measurceLocationToElevationBulk(); + + void checkMapObjectProperties(); + + void checkObjectFactoryContainer(); // contains, get etc. + void checkObjectAndLayerFactory(); // checks layerOnDemand + + void checkOverlayHandler1(); + void checkOverlayHandler2(); + void checkOverlayHandler3(); + +// void checkRubberbandFactory(); + + + void measureCoordTranslationFunctor(); + void checkCoordinateConversion(); + +#ifndef Q_OS_WIN +# define CrashUnderWindows +#endif +#ifdef CrashUnderWindows + void checkCreationAndRemoveMySelf(); + + void checkGraphicalObject(); +#endif + void checkGraphicalObjectEllipse(); + void checkGraphicalObjectPath(); + void checkGraphicalObjectPictureMarker1(); + void checkGraphicalObjectPictureMarker2(); + void checkGraphicalObjectPictureMarker3(); + void checkGraphicalObjectRange(); + void checkGraphicalObjectText(); + void checkGraphicalObjectTacticalAppearance(); + + void checkBasicPathObjectImpl(); + + void checkChemicalAgent(); + void checkParticipant(); + + void checkEventDispatcher(); + + void checkRubberbandMultiPointFeature(); + void checkRubberbandRectangle(); + void checkRubberbandRectangleIndex(); + void checkRubberbandCorridor(); + + void checkRubberCoordinateProjection(); + + void cleanupTestCase(); + +protected: + + void reInitTestCaseUntilSetCenter(); + + template void checkRubberband(const char *className); + template void checkRubberbandProjection(const char *className); + + +private: + + SituationMap::LOGGER_T m_logger; + + class Private; + Private *d = nullptr; + + +}; + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Portal/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Portal/CMakeLists.txt new file mode 100755 index 000000000..b4b505b8a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Portal/CMakeLists.txt @@ -0,0 +1,34 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +# LIBRARY +SET(Portal_SRCS + portal.cpp + portal.h + portal_export.h +) + +SOURCE_GROUP(Portal FILES ${Portal_SRCS}) +NGL_ADD_LIBRARY(WS situationMapPortal SHARED ${Portal_SRCS}) + +NGL_TARGET_INCLUDE_DIRECTORIES(situationMapPortal + PUBLIC +) + + +NGL_TARGET_LINK_LIBRARIES(situationMapPortal + PRIVATE + ArcGIS::ArcGIS + NGLLogger + Qt6::Network + Qt6::Gui + Qt6::Core + situationMapCommon + PUBLIC +) + +NGL_INSTALL_TARGET(situationMapPortal) + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Portal/portal.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Portal/portal.cpp new file mode 100755 index 000000000..edbf7eaf7 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Portal/portal.cpp @@ -0,0 +1,351 @@ +/** + * @RDE_HEADER@ + * Portal + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Norbert Thoden, NLSE, 2018-10-08 + */ + + +// Own +#include "portal.h" +//#include + + +// Esri +#include +#include + +#include + + +// Qt + + +namespace Esri +{ +namespace ArcGISRuntime +{ + +QString toString(const Error &value) +{ + std::ostringstream stream; + stream << "Esri::ArcGISRuntime::Error: " + << " code=" << value.code() + << ", domain=" << (int)value.domain() + << ", extendedErrorType=" << (int)value.extendedErrorType() + << ", message='" << value.message() << "'" + << ", additionalMessage='" << value.additionalMessage() << "'"; + return QString::fromStdString(stream.str()); +} + +std::ostream &operator<< (std::ostream &stream, + const Esri::ArcGISRuntime::Error &value) +{ + stream << toString(value); + return stream; +} + +} +} + +namespace SituationMap +{ + + + + +class Portal::Private +{ +public: + Private() = default; + ~Private() = default; + + + Esri::ArcGISRuntime::Portal *m_portal = nullptr; +// QTimer *m_suppressTimer = nullptr; + +}; // class Portal::Private +//----------------------------------------------------------------------------- + + + + +//----------------------------------------------------------------------------- + +Portal::Portal(QObject *parent) + : BaseClass(parent) + , d(new Private) + , m_logger(getLogger("Portal")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +Portal::~Portal() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete d; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +bool Portal::init() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete d->m_portal; + + QUrl portalUrl(QStringLiteral("http://excgeo20.sl.defence-elec.de/arcgis")); // http://excgeo20.sl.defence-elec.de/arcgis/ + Esri::ArcGISRuntime::Credential credential(QStringLiteral("portaladmin"), QStringLiteral("mout11de")); + d->m_portal = new Esri::ArcGISRuntime::Portal(portalUrl, false); +// d->m_portal = new Esri::ArcGISRuntime::Portal(portalUrl, &credential); + + connect(d->m_portal, &Esri::ArcGISRuntime::Portal::errorOccurred, this, &Portal::onPortalErrorOccurred); + connect(d->m_portal, &Esri::ArcGISRuntime::Portal::doneLoading, this, &Portal::onPortalDoneLoading); + + connect(d->m_portal, &Esri::ArcGISRuntime::Portal::basemapsChanged, this, &Portal::onBasemapsChanged); + connect(d->m_portal, &Esri::ArcGISRuntime::Portal::featuredGroupsChanged, this, &Portal::onFeaturedGroupsChanged); + connect(d->m_portal, &Esri::ArcGISRuntime::Portal::featuredItemsChanged, this, &Portal::onFeaturedItemsChanged); + connect(d->m_portal, &Esri::ArcGISRuntime::Portal::homepageFeaturedContentChanged, this, &Portal::onHomepageFeatureContentChanged); + + d->m_portal->load(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +bool Portal::fetchData() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!d->m_portal || d->m_portal->loadStatus() != Esri::ArcGISRuntime::LoadStatus::Loaded) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " not loaded yet"; + return false; + } + + d->m_portal->fetchBasemaps(); + d->m_portal->fetchFeaturedGroups(); + d->m_portal->fetchFeaturedItems(); + d->m_portal->fetchHomepageFeaturedContent(); + + + // findItems : + Esri::ArcGISRuntime::PortalQueryParametersForItems query; + query.setSearchString(QLatin1String("1==1")); + // query.setTypes(QList() << PortalItemType::WebMap); + + connect(d->m_portal, &Esri::ArcGISRuntime::Portal::findItemsCompleted, this, &Portal::onFindItemsCompleted); + d->m_portal->findItems(query); + + + // findGroups + QString groupsQuery(QLatin1String("1==1")); + Esri::ArcGISRuntime::PortalQueryParametersForGroups findGroupsQueryParameters(groupsQuery); + + connect(d->m_portal, &Esri::ArcGISRuntime::Portal::findGroupsCompleted, this, &Portal::onFindGroupsCompleted); + d->m_portal->findGroups(findGroupsQueryParameters); + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + + +void Portal::onPortalErrorOccurred(const Esri::ArcGISRuntime::Error &error) +{ + ERROR_LOG(m_logger) << LOGFUNCTION << " Portal -> EsriError occured: " << error; +} + +void Portal::onPortalDoneLoading(const Esri::ArcGISRuntime::Error &loadError) +{ + if (loadError.isEmpty()) + { + NOTICE_LOG(m_logger) << LOGFUNCTION << " Portal loaded"; + fetchData(); + } else { + ERROR_LOG(m_logger) << LOGFUNCTION << " Portal -> EsriError occured: " << loadError; + } +} + +void Portal::onBasemapsChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + NOTICE_LOG(m_logger) << LOGFUNCTION << " ***** fired"; + + Esri::ArcGISRuntime::BasemapListModel *model = d->m_portal->basemaps(); + if (!model) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " model does not exits"; + return; + } + + for (int i=0; isize(); ++i) + { + Esri::ArcGISRuntime::Basemap *item = model->at(i); + if (!item) + { + WARNING_LOG(m_logger) << LOGFUNCTION << " item is empty"; + continue; + } + NOTICE_LOG(m_logger) << LOGFUNCTION + << " i=" << i + << " name = '" << item->name() << "'" + << " url = '" << item->url().toString() << "'"; + + Esri::ArcGISRuntime::LayerListModel *llm = item->baseLayers(); + if (!llm) + { + WARNING_LOG(m_logger) << LOGFUNCTION << " llm is empty"; + continue; + } + for (int j=0; isize(); ++j) + { + Esri::ArcGISRuntime::Layer *layer = llm->at(j); + if (!layer) + { + WARNING_LOG(m_logger) << LOGFUNCTION << " layer is empty"; + continue; + } + NOTICE_LOG(m_logger) << LOGFUNCTION + << " j=" << j + << " layer = '" << layer->name() << "'" + << " layerType = '" << (int)layer->layerType(); + } // for + + } // for + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} +void Portal::onFeaturedGroupsChanged() +{ + NOTICE_LOG(m_logger) << LOGFUNCTION << " ***** fired"; + + Esri::ArcGISRuntime::PortalGroupListModel *model = d->m_portal->featuredGroups(); + if (!model) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " model does not exits"; + return; + } + + for (int i=0; isize(); ++i) + { + Esri::ArcGISRuntime::PortalGroup *item = model->at(i); + if (!item) + { + WARNING_LOG(m_logger) << LOGFUNCTION << " item is empty"; + continue; + } + NOTICE_LOG(m_logger) << LOGFUNCTION + << " i=" << i + << " title = '" << item->title() << "'" +// << " url = '" << item->url().toString() << "'" + ; + } // for + +} +void Portal::onFeaturedItemsChanged() +{ + NOTICE_LOG(m_logger) << LOGFUNCTION << " ***** fired"; + + Esri::ArcGISRuntime::PortalItemListModel *model = d->m_portal->featuredItems(); + if (!model) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " model does not exits"; + return; + } + + for (int i=0; isize(); ++i) + { + Esri::ArcGISRuntime::PortalItem *item = model->at(i); + if (!item) + { + WARNING_LOG(m_logger) << LOGFUNCTION << " item is empty"; + continue; + } + NOTICE_LOG(m_logger) << LOGFUNCTION + << " i=" << i + << " name = '" << item->name() << "'" + << " serviceUrl = '" << item->serviceUrl().toString() << "'"; + } // for +} +void Portal::onHomepageFeatureContentChanged() +{ + NOTICE_LOG(m_logger) << LOGFUNCTION << " ***** fired"; + + Esri::ArcGISRuntime::PortalItemListModel *model = d->m_portal->homepageFeaturedContent(); + if (!model) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " model does not exits"; + return; + } + + for (int i=0; isize(); ++i) + { + Esri::ArcGISRuntime::PortalItem *item = model->at(i); + if (!item) + { + WARNING_LOG(m_logger) << LOGFUNCTION << " item is empty"; + continue; + } + NOTICE_LOG(m_logger) << LOGFUNCTION + << " i=" << i + << " name = '" << item->name() << "'" + << " serviceUrl = '" << item->serviceUrl().toString() << "'"; + } // for + +} + +void Portal::onFindItemsCompleted(Esri::ArcGISRuntime::PortalQueryResultSetForItems *result) +{ + NOTICE_LOG(m_logger) << LOGFUNCTION << " ***** fired"; + Esri::ArcGISRuntime::PortalItemListModel *model = result->itemResults(); + if (!model) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " model does not exits"; + return; + } + + for (int i=0; isize(); ++i) + { + Esri::ArcGISRuntime::PortalItem *item = model->at(i); + if (!item) + { + WARNING_LOG(m_logger) << LOGFUNCTION << " item is empty"; + continue; + } + NOTICE_LOG(m_logger) << LOGFUNCTION + << " i=" << i + << " name = '" << item->name() << "'" + << " serviceUrl = '" << item->serviceUrl().toString() << "'"; + } // for +} + +void Portal::onFindGroupsCompleted(Esri::ArcGISRuntime::PortalQueryResultSetForGroups *result) +{ + NOTICE_LOG(m_logger) << LOGFUNCTION << " ***** fired"; + Esri::ArcGISRuntime::PortalGroupListModel *model = result->groupResults(); + if (!model) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " model does not exits"; + return; + } + + for (int i=0; isize(); ++i) + { + Esri::ArcGISRuntime::PortalGroup *item = model->at(i); + if (!item) + { + WARNING_LOG(m_logger) << LOGFUNCTION << " item is empty"; + continue; + } + NOTICE_LOG(m_logger) << LOGFUNCTION + << " i=" << i + << " title = '" << item->title() << "'" +// << " toJson() = '" << item->toJson() << "'" + ; + } // for +} + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Portal/portal.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Portal/portal.h new file mode 100755 index 000000000..25a2501b9 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Portal/portal.h @@ -0,0 +1,80 @@ +/** + * @RDE_HEADER@ + * SideBar + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Norbert Thoden, NLSE, 2018-10-08 + */ + +#pragma once + +// Own +#include "portal_export.h" +#include + + +// Qt +#include + + +namespace Esri { +namespace ArcGISRuntime { +class Error; +class PortalQueryResultSetForItems; +class PortalQueryResultSetForGroups; +} +} +class QModelIndex; +class QStandardItem; + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +class SITUATIONMAPPORTAL_EXPORT Portal : public QObject +{ + using BaseClass = QObject; + + Q_OBJECT + +public: + explicit Portal(QObject *parent = nullptr); + virtual ~Portal(); + + bool init(); + + bool fetchData(); + +signals: + +protected Q_SLOTS: + void onPortalErrorOccurred(const Esri::ArcGISRuntime::Error &error); + void onPortalDoneLoading(const Esri::ArcGISRuntime::Error &loadError); + + void onBasemapsChanged(); + + void onFeaturedGroupsChanged(); + void onFeaturedItemsChanged(); + void onHomepageFeatureContentChanged(); + + void onFindItemsCompleted(Esri::ArcGISRuntime::PortalQueryResultSetForItems* result); + void onFindGroupsCompleted(Esri::ArcGISRuntime::PortalQueryResultSetForGroups* result); + + +private: + +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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Portal/portal_export.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Portal/portal_export.h new file mode 100755 index 000000000..d3a43cda5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Portal/portal_export.h @@ -0,0 +1,21 @@ +/** + * Export definitions + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include + +#ifndef SITUATIONMAPPORTAL_EXPORT +# ifdef BUILD_STATIC_LIBRARIES +# define SITUATIONMAPPORTAL_EXPORT +# else +# if defined(MAKE_SITUATIONMAPPORTAL_LIB) +# define SITUATIONMAPPORTAL_EXPORT Q_DECL_EXPORT +# else +# define SITUATIONMAPPORTAL_EXPORT Q_DECL_IMPORT +# endif +# endif +#endif diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/compass.png b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/compass.png new file mode 100755 index 000000000..0943a7a28 Binary files /dev/null and b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/compass.png differ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/compass.svg b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/compass.svg new file mode 100755 index 000000000..69d0f3b7e --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/compass.svg @@ -0,0 +1,81 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/preview_aerial.png b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/preview_aerial.png new file mode 100755 index 000000000..602bf28bc Binary files /dev/null and b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/preview_aerial.png differ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/preview_topographic.png b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/preview_topographic.png new file mode 100755 index 000000000..2596282ec Binary files /dev/null and b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/preview_topographic.png differ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/pushPinRedClipArt.png b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/pushPinRedClipArt.png new file mode 100755 index 000000000..d88e7c97a Binary files /dev/null and b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/pushPinRedClipArt.png differ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/situation_map.qrc b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/situation_map.qrc new file mode 100755 index 000000000..16f269476 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Resources/situation_map.qrc @@ -0,0 +1,7 @@ + + + preview_aerial.png + preview_topographic.png + pushPinRedClipArt.png + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/README.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/README.txt new file mode 100755 index 000000000..2d9c60c4b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/README.txt @@ -0,0 +1,34 @@ +// @RDE_HEADER@ +// Notizen zu den Rubberbands +// $Id$ +// $HeadURL$ +// $Author$ +// Created by Norbert Thoden, SLSE, 2023-08-26 + +Notizen zu den Rubberbands + +- Sie wurden von Jörg Turla erstellt + +- 2023: Erweiterung durch Steve Kemayou and Norbert Thoden + +Norbert Thoden, 08/2023 : +- Habe ein neues RB erstellt: RubberbandMultiEllipseFeature +- Vorlage war RubberbandMultiPointFeature (Polyline) + Weiterer Stützpunte habe nicht mehr die Linie verlängtert, sondern neue Ellipse/Kreise eingefügt. + +- API & Name wurden überwiegend für Ellipse konzipiert, getestet wurde aber im Wesentlichen mit Kreis (constrainQuadratic=true9 + + Probleme/ToDo sind u.a: + - Screenresolution führt dazu, das bei eng gefassten Constraints (z.B. radius =5m, min/Max ebenfalls bei 5m) + die checkMinMaxRadiusConstraint nicht bestehen und als Fehler betrachtet werden... :-( + + - Setzen der Predefines Values (setCenterAndRadii) unterstützt noch keine Constraints + + + Empfehlung für neues RB + - Bedeutung erlernen von: selectAction, moveAction, movePickedAction und ihrer returnValues. + - Hilfsfile für rotations methoden etc. um ständiges kopieren (von Lmabdas!?) zu vermeiden. + - ... + +: Norbert Thoden, 08/2023: + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/abort.xpm b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/abort.xpm new file mode 100755 index 000000000..62a95633d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/abort.xpm @@ -0,0 +1,149 @@ +/* XPM */ +static const char * abort_xpm[] = { +"24 24 122 2", +" c None", +". c #E98774", +"+ c #F6866F", +"@ c #FB6C50", +"# c #F28773", +"$ c #F8856E", +"% c #EF8672", +"& c #F5846E", +"* c #F96346", +"= c #F96042", +"- c #F96649", +"; c #F28670", +"> c #8E5246", +", c #F7836C", +"' c #F96245", +") c #F9674B", +"! c #F18570", +"~ c #F3836D", +"{ c #F76245", +"] c #F75F41", +"^ c #F76447", +"/ c #F2846E", +"( c #945549", +"_ c #F7806A", +": c #F76143", +"< c #F76548", +"[ c #F0856F", +"} c #E47B66", +"| c #F56043", +"1 c #F55D3F", +"2 c #F56144", +"3 c #9A574A", +"4 c #F57E67", +"5 c #F55E41", +"6 c #F56245", +"7 c #D1725F", +"8 c #E55539", +"9 c #F35A3C", +"0 c #F35D40", +"a c #F27F68", +"b c #9F5A4C", +"c c #F47B63", +"d c #F35B3E", +"e c #D44E34", +"f c #E35337", +"g c #F1583A", +"h c #F15B3D", +"i c #F17D66", +"j c #F37860", +"k c #F1593B", +"l c #D14C32", +"m c #DF5034", +"n c #EE5638", +"o c #EE583A", +"p c #EE5739", +"q c #CC4A30", +"r c #DC4D31", +"s c #EC5335", +"t c #CA472D", +"u c #D94B2F", +"v c #E95133", +"w c #C7452C", +"x c #DF4B2E", +"y c #E74E30", +"z c #CE462B", +"A c #A45949", +"B c #E55235", +"C c #E44C2E", +"D c #E35A3F", +"E c #975244", +"F c #E76950", +"G c #E2492B", +"H c #E24B2D", +"I c #E47059", +"J c #83473C", +"K c #E36A52", +"L c #DF4628", +"M c #E04A2C", +"N c #E06F59", +"O c #E16C55", +"P c #DC4528", +"Q c #DC4426", +"R c #DD482B", +"S c #DC6F59", +"T c #DD6C56", +"U c #DA4326", +"V c #DA4123", +"W c #D74022", +"X c #D84123", +"Y c #DB472A", +"Z c #D96E59", +"` c #DA6A55", +" . c #D94325", +".. c #D83F21", +"+. c #D13D20", +"@. c #D63F21", +"#. c #DA472B", +"$. c #D46C58", +"%. c #D26A55", +"&. c #D64326", +"*. c #D53D1F", +"=. c #CF3B1E", +"-. c #D33C1F", +";. c #D7482B", +">. c #C36552", +",. c #D03A1D", +"'. c #D33B1D", +"). c #CB391C", +"!. c #D13A1D", +"~. c #CA381C", +"{. c #CD381A", +"]. c #D2391B", +"^. c #CB371A", +"/. c #D0381B", +"(. c #C7361A", +"_. c #CA3518", +":. c #D03719", +"<. c #C93518", +"[. c #CD3619", +"}. c #C43417", +"|. c #BF3216", +"1. c #BD3116", +" ", +" . . ", +" + @ # $ @ % ", +" & * = - ; > , ' = ) ! ", +" ~ { ] ] ] ^ / ( _ : ] ] ] < [ ", +" } | 1 1 1 1 1 2 ~ 3 4 5 1 1 1 1 1 6 7 ", +" 8 9 9 9 9 9 9 0 a b c d 9 9 9 9 9 9 e ", +" f g g g g g g h i j k g g g g g g l ", +" m n n n n n n o p n n n n n n q ", +" r s s s s s s s s s s s s t ", +" u v v v v v v v v v v w ", +" x y y y y y y y y z ", +" A B C C C C C C C C D ", +" E F G G G G G G G G G H I ", +" J K L L L L L L L L L L L M N ", +" O P Q Q Q Q Q Q Q Q Q Q Q Q R S ", +" T U V V V V V V W X V V V V V V Y Z ", +" ` .............+. @.............#.$. ", +" %.&.*.*.*.*.*.*.=. -.*.*.*.*.*.*.;.>. ", +" ,.'.'.'.'.'.). !.'.'.'.'.'.~. ", +" {.].].].^. /.].].].(. ", +" _.:.<. [.:.}. ", +" |. 1. ", +" "}; diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/abstract_event_dispatcher.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/abstract_event_dispatcher.cpp new file mode 100755 index 000000000..595957822 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/abstract_event_dispatcher.cpp @@ -0,0 +1,291 @@ +/** +** +** @RDE_HEADER@ +** Abstract Event Dispatcher for Qt5 +** $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 .........: +** +** 28.04.2003, JT : First edition for use with Qt4 in START Feature Editor fee +** 25.03.2015, JT : Refactoring for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "abstract_event_dispatcher.h" +#include "gestures.h" + +#include + +#include +#include + + +namespace SituationMap { namespace RB +{ + + static QHash> &runningDispatcher() + { + static QHash> hashTable; + return hashTable; + } // runningDispatcher() + + + QGestureRecognizer * AbstractEventDispatcher::_applyGestureDetection = nullptr; + unsigned int AbstractEventDispatcher::_applyGestureID = 0; + + + AbstractEventDispatcher::AbstractEventDispatcher( QWidget * getEventsFrom ) + : QObject(getEventsFrom) + , _getEventsFrom(getEventsFrom) + , _logger(getLogger("EventDispatcher")) + , _cursorIsInWidget(false) + , _synthesisTouchFromMouseEvents(false) + , _deleteRequested(false) + , _hasGrab(false) + , _finishFlag(false) + , _activeEvent(false) + , _dispatcherCursor() + , _whatsThisBackup() + , _eventActions(nullptr) + { + DEBUG_LOG(_logger) << LOGFUNCTION << " " << this; + initDispatcher(); + } /* AbstractEventDispatcher::AbstractEventDispatcher */ + + + AbstractEventDispatcher::AbstractEventDispatcher( QWidget * getEventsFrom, EventActionInterface * eventActions ) + : QObject(getEventsFrom) + , _getEventsFrom(getEventsFrom) + , _logger(getLogger("EventDispatcher")) + , _cursorIsInWidget(false) + , _synthesisTouchFromMouseEvents(false) + , _deleteRequested(false) + , _hasGrab(false) + , _finishFlag(false) + , _activeEvent(false) + , _dispatcherCursor() + , _whatsThisBackup() + , _eventActions(eventActions) + + { + DEBUG_LOG(_logger) << LOGFUNCTION << " " << this; + initDispatcher(); + } /* AbstractEventDispatcher::AbstractEventDispatcher */ + + + void AbstractEventDispatcher::initDispatcher() + { + _restoreCursor = _getEventsFrom->testAttribute(Qt::WA_SetCursor); + + if (_restoreCursor) + { + _cursorToRestore = getCursorFromWidget(); + + DEBUG_LOG(_logger) << LOGFUNCTION << " this=" << this << " get cursorToRestore"; + } + _restoreMouseTracking = _getEventsFrom->hasMouseTracking(); + +// { +// bool activeEvent = _activeEvent; +// _activeEvent = true; // don't process events while init has not been finished +// QCoreApplication::process Events(); // remove events from event queue; in which circumstances this is necessary? +// _activeEvent = activeEvent; +// } + + _getEventsFrom->setMouseTracking(true); + _getEventsFrom->raise(); + _getEventsFrom->installEventFilter(this); + + _synthesisTouchFromMouseEvents = QCoreApplication::testAttribute( Qt::AA_SynthesizeTouchForUnhandledMouseEvents ); + QCoreApplication::setAttribute( Qt::AA_SynthesizeTouchForUnhandledMouseEvents, false ); + QCoreApplication::setAttribute( Qt::AA_SynthesizeMouseForUnhandledTouchEvents, true ); + + static const Qt::GestureType gestures[] = { Qt::GestureType::TapGesture + , Qt::GestureType::TapAndHoldGesture + , Qt::GestureType::PanGesture + , Qt::GestureType::PinchGesture + , Qt::GestureType::SwipeGesture }; + + for (auto gestureType : gestures) _getEventsFrom->grabGesture(gestureType); + + if (_applyGestureDetection == nullptr) + { + _applyGestureDetection = new ApplyGestureRecognizer(); + _applyGestureID = QGestureRecognizer::registerRecognizer( _applyGestureDetection ); + } + + _getEventsFrom->grabGesture( (Qt::GestureType)_applyGestureID ); + _getEventsFrom->setAttribute( Qt::WA_AcceptTouchEvents, true ); + + runningDispatcher()[_getEventsFrom].append(this); + } /* AbstractEventDispatcher::initDispatcher */ + + + AbstractEventDispatcher::~AbstractEventDispatcher() + { + DEBUG_LOG(_logger) << LOGFUNCTION << " " << this; + + _finishFlag = true; + + if (_getEventsFrom) + { + _getEventsFrom->removeEventFilter(this); + _getEventsFrom->setMouseTracking( _restoreMouseTracking ); + + ungrab(); + + if (!_whatsThisBackup.isNull()) + { + _getEventsFrom->setWhatsThis(_whatsThisBackup); + } + runningDispatcher()[_getEventsFrom].removeAll(this); + } + + QCoreApplication::setAttribute( Qt::AA_SynthesizeTouchForUnhandledMouseEvents, _synthesisTouchFromMouseEvents ); + + } /* AbstractEventDispatcher::~AbstractEventDispatcher */ + + + unsigned int AbstractEventDispatcher::numberOfEventDispatcher() const + { + return static_cast( runningDispatcher()[_getEventsFrom].size() ); + } /* AbstractEventDispatcher::numberOfEventDispatcher */ + + + void AbstractEventDispatcher::onDo_delete() + { + if (_deleteRequested) return; + _deleteRequested = true; + QObject::deleteLater(); + } /* AbstractEventDispatcher::onDo_delete */ + + + void AbstractEventDispatcher::KickTheBucket() + { + if (_getEventsFrom == nullptr) return; + + _finishFlag = true; + + unlinkEventDispatcher(); + + _getEventsFrom->removeEventFilter(this); + _getEventsFrom->setMouseTracking( _restoreMouseTracking ); + + ungrab(); + + if (!_whatsThisBackup.isNull()) + { + _getEventsFrom->setWhatsThis(_whatsThisBackup); + } + + runningDispatcher()[_getEventsFrom].removeAll(this); + _getEventsFrom = nullptr; + + emit signalKickTheBucket(); + +// QCoreApplication::sendPostedEvents(); +// QCoreApplication::process Events(); // dispatch window system events + + onDo_delete(); + + } /* AbstractEventDispatcher::KickTheBucket */ + + + void AbstractEventDispatcher::onDo_kickTheBucket() { KickTheBucket(); } + + + void AbstractEventDispatcher::setWhatsThis(const QString & whatsThis) + { + if (_whatsThisBackup.isNull()) + { + _whatsThisBackup = _getEventsFrom->whatsThis(); + } + _getEventsFrom->setWhatsThis(whatsThis); + } /* AbstractEventDispatcher::setWhatsThis */ + + + + void AbstractEventDispatcher::grab() + { + if (_hasGrab) return; + _hasGrab = true; + DEBUG_LOG(_logger) << LOGFUNCTION << " this=" << this << " set dispatcherCursor = " << (int)_dispatcherCursor.shape(); + setCursorToWidget( _dispatcherCursor ); + _getEventsFrom -> setFocus(); + } /* AbstractEventDispatcher::Grab */ + + + void AbstractEventDispatcher::ungrab() + { + if (!_hasGrab) return; + + _hasGrab = false; + if (_restoreCursor) + { + DEBUG_LOG(_logger) << LOGFUNCTION << " this=" << this << " set cursorToRestore = " << (int)_cursorToRestore.shape(); + setCursorToWidget( _cursorToRestore ); + } + else _getEventsFrom -> unsetCursor(); + + _getEventsFrom -> clearFocus(); + + } /* AbstractEventDispatcher::Ungrab */ + + + bool AbstractEventDispatcher::eventFilter( QObject * widget, QEvent * e ) + { + if (widget != ((QObject*)_getEventsFrom)) return false; + + if (_logger.isDebugEnabled()) + { + switch (e->type()) + { + case QEvent::CursorChange: + { + QCursor cursor = _getEventsFrom->cursor(); + DEBUG_LOG(_logger) << LOGFUNCTION << " Cursor has changed, this = " << this << " cursor = " << cursor.shape(); + } break; + + case QEvent::Enter : + { + _cursorIsInWidget = true; + QCursor cursor = _getEventsFrom->cursor(); + DEBUG_LOG(_logger) << LOGFUNCTION << " Enter event, this = " << this << " cursor = " << cursor.shape(); + } break; + + case QEvent::Leave : + { + _cursorIsInWidget = false; + QCursor cursor = _getEventsFrom->cursor(); + DEBUG_LOG(_logger) << LOGFUNCTION << " Leave event, this = " << " cursor = " << cursor.shape(); + } break; + + default: break; + } + } + return false; + } /* AbstractEventDispatcher::eventFilter */ + + + void AbstractEventDispatcher::setCursorToWidget( const QCursor & cursor ) { _getEventsFrom -> setCursor( cursor ); } + + QCursor AbstractEventDispatcher::getCursorFromWidget() const { return _getEventsFrom->cursor(); } + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/abstract_event_dispatcher.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/abstract_event_dispatcher.h new file mode 100755 index 000000000..687fcd433 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/abstract_event_dispatcher.h @@ -0,0 +1,234 @@ +/** +** +** @RDE_HEADER@ +** Abstract Event Dispatcher for Qt5 +** $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 .........: +** +** 28.04.2003, JT : First edition for use with Qt4 in START Feature Editor fee +** 25.03.2015, JT : Refactoring for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include + +#include +#include +#include + +#include + +class QEvent; +class QWidget; +class QGestureRecognizer; + + +namespace SituationMap { namespace RB +{ + + /** + @class AbstractEventDispatcher abstract_event_dispatcher.h + @brief Base class of Qt event filter implementations to handle user interactions of map window + + An EventDispatcher installs an event filter on a QWidget to receive all events that are send to + the widget. Depending on the type of event and the state of the event dispatcher the event will + be processed by the dispatcher and stopped or forwarded to the QWidget. In case of processing + the event methodes of the EventActionInterface are called to controll a rubberband. + + @see EventDelegater, EventDispatcher, EventMouseNavigator + */ + class AbstractEventDispatcher : public QObject + { + friend class EventDelegater; + + Q_OBJECT + + public: + + /** + @brief Logical interface between EventDispatcher and Rubberband + Defines interface functions the EventDispatcher will call to control a rubberband. The rubberband class + is derived from EventActionInterface and overrides the interface functions to implement them. The mapping + user interaction to interface action is defined the derived classes of AbstractEventDispatcher. + */ + class EventActionInterface + { + public: + + EventActionInterface() = default; + virtual ~EventActionInterface() = default; + + virtual void unlinkEventDispatcher ( AbstractEventDispatcher * causer ) = 0; + virtual void resetAction ( AbstractEventDispatcher * causer, int x, int y ) { Q_UNUSED(causer); Q_UNUSED(x); Q_UNUSED(y); } + virtual bool selectAction ( AbstractEventDispatcher * causer, int x, int y ) { Q_UNUSED(causer); Q_UNUSED(x); Q_UNUSED(y); return false; } + virtual bool multiSelectActionPress ( AbstractEventDispatcher * causer, int x, int y ) { Q_UNUSED(causer); Q_UNUSED(x); Q_UNUSED(y); return false; } + virtual bool multiSelectActionRelease( AbstractEventDispatcher * causer, int x, int y ) { Q_UNUSED(causer); Q_UNUSED(x); Q_UNUSED(y); return false; } + virtual bool setCoordinateAction ( AbstractEventDispatcher * causer, int x, int y ) { Q_UNUSED(causer); Q_UNUSED(x); Q_UNUSED(y); return false; } + virtual bool moveAction ( AbstractEventDispatcher * causer, int x, int y ) { Q_UNUSED(causer); Q_UNUSED(x); Q_UNUSED(y); return false; } + virtual bool movePickedAction ( AbstractEventDispatcher * causer, int x, int y ) { Q_UNUSED(causer); Q_UNUSED(x); Q_UNUSED(y); return false; } + virtual bool contextMenuAction ( AbstractEventDispatcher * causer, int x, int y ) { Q_UNUSED(causer); Q_UNUSED(x); Q_UNUSED(y); return false; } + virtual bool completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) { Q_UNUSED(causer); Q_UNUSED(x); Q_UNUSED(y); return false; } + virtual bool deleteAction ( AbstractEventDispatcher * causer, int x, int y ) { Q_UNUSED(causer); Q_UNUSED(x); Q_UNUSED(y); return false; } + virtual bool undoAction ( AbstractEventDispatcher * causer, int x, int y ) { Q_UNUSED(causer); Q_UNUSED(x); Q_UNUSED(y); return false; } + virtual bool applyAndEndAction ( AbstractEventDispatcher * causer, int x, int y ) { Q_UNUSED(causer); Q_UNUSED(x); Q_UNUSED(y); return false; } + virtual bool abortAction ( AbstractEventDispatcher * causer, int x, int y ) { Q_UNUSED(causer); Q_UNUSED(x); Q_UNUSED(y); return false; } + virtual bool buttonPositionAction ( AbstractEventDispatcher * causer, int x, int y ) { Q_UNUSED(causer); Q_UNUSED(x); Q_UNUSED(y); return false; } + + virtual void userActionFinished() = 0; + + }; /* EventActionInterface */ + + /** + @name Construction & Destruction + @{ + */ + explicit AbstractEventDispatcher( QWidget * getEventsFrom ); + explicit AbstractEventDispatcher( QWidget * getEventsFrom, EventActionInterface* ); + + virtual ~AbstractEventDispatcher(); + + AbstractEventDispatcher(const AbstractEventDispatcher &) = delete; + AbstractEventDispatcher & operator = (const AbstractEventDispatcher &) = delete; + /** @} */ + + /** + Conceal deleteLater slot. Use onDo_delete() instead to avoid "QCoreApplication::postEvent: Unexpected null receiver" warning. + */ + void deleteLater() = delete; + + /** + Provides the number of EventDispatcher installed to the QWidget of this EventDispatcher + */ + unsigned int numberOfEventDispatcher() const; + + inline void setEventActions(EventActionInterface * eventActions) { _eventActions = eventActions; } + + inline bool hasGrab() const { return _hasGrab; } + + inline void setCursor(const QCursor & cursor) { _dispatcherCursor = cursor; } + + void setWhatsThis(const QString & whatsThis); + + void grab(); + void ungrab(); + + + public slots: + + /** + @brief Slot calling internal \c KickTheBucket methode + + Calls internal methode \c KickTheBucket. Be careful using this function. Should only be used + from implementations of \c EventActionInterface methodes or \c EventDelegater class. + */ + void onDo_kickTheBucket(); + + /** + @brief Slot calling \c deleteLater function of QObject + + Never use \c delete directly for EventDispatcher class. Instead of use \c onDo_delete() function. + */ + void onDo_delete(); + + protected: + + QWidget * _getEventsFrom; + LOGGER_T _logger; + bool _cursorIsInWidget; + + inline bool hasFinished() { return _finishFlag; } + inline bool hasActiveEvent() { return _activeEvent; } + + inline void setActiveEvent(bool val) { _activeEvent = val; } + + /** + @brief Destroying EventDispatcher as result of an incoming event. + + As result of an incomming event (e.g. Keypress ESC) the EventDispatcher will stop, emiting signal \c signalKickTheBucket() + and calls methode \c EventActionInterface::unlinkEventDispatcher() before setup \c deleteLater() call to destroy it self. + */ + virtual void KickTheBucket(); + + + inline void unlinkEventDispatcher() + { + if (_eventActions) + { + _eventActions -> unlinkEventDispatcher( this ); + _eventActions = nullptr; + } + } /* unlinkEventDispatcher */ + + + inline bool callEventAction( bool( EventActionInterface::*actionMethodePtr)( AbstractEventDispatcher*,int,int), int x, int y ) + { + if (_eventActions == nullptr) return false; + return (_eventActions->*actionMethodePtr)( this, x, y ); + } /* callEventAction */ + + + /** + Event filter function to override to implement event handling. To workaround a cursor handling bug in QGraphicsView it is + necessary to call the eventFilter function of this base class, which will monitor enter and leave events. + */ + virtual bool eventFilter( QObject * widget, QEvent * e ); + + inline EventActionInterface * getEventActions() { return _eventActions; } + + void setCursorToWidget( const QCursor & cursor ); + QCursor getCursorFromWidget() const; + + bool _synthesisTouchFromMouseEvents; + + static unsigned int _applyGestureID; + + private: + + void initDispatcher(); + + bool _deleteRequested; + + bool _hasGrab; + bool _finishFlag; + + bool _restoreCursor; + bool _restoreMouseTracking; + QCursor _cursorToRestore; + + bool _activeEvent; + + QCursor _dispatcherCursor; + QString _whatsThisBackup; + + EventActionInterface * _eventActions; + + + + static QGestureRecognizer * _applyGestureDetection; + + signals: + + void signalKickTheBucket(); + + }; /* EventDispatcher */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/apply.xpm b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/apply.xpm new file mode 100755 index 000000000..cd1a26d20 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/apply.xpm @@ -0,0 +1,110 @@ +/* XPM */ +static const char * apply_xpm[] = { +"24 24 83 1", +" c None", +". c #7FBD7C", +"+ c #8BCF88", +"@ c #84C582", +"# c #588456", +"$ c #63BE5F", +"% c #57B953", +"& c #73BF70", +"* c #56B752", +"= c #499B45", +"- c #699F66", +"; c #59B755", +"> c #54B550", +", c #52B14E", +"' c #6ABC67", +") c #52B34E", +"! c #377334", +"~ c #6FAF6C", +"{ c #50B24C", +"] c #4FB14B", +"^ c #48A144", +"/ c #4C734A", +"( c #5DB559", +"_ c #4DAE49", +": c #4CAD48", +"< c #6BB569", +"[ c #4AAC46", +"} c #377F35", +"| c #5E915C", +"1 c #50AD4C", +"2 c #48A944", +"3 c #44A040", +"4 c #61B05E", +"5 c #45A641", +"6 c #527F50", +"7 c #7BBF78", +"8 c #78BA75", +"9 c #639C60", +"0 c #46A542", +"a c #42A33E", +"b c #368633", +"c c #389035", +"d c #3FA13B", +"e c #43A33F", +"f c #619B5E", +"g c #56AC53", +"h c #3D9D39", +"i c #3B9C37", +"j c #3C9E38", +"k c #54A951", +"l c #64A761", +"m c #3D9E39", +"n c #2A6B26", +"o c #3A9B35", +"p c #3B9B36", +"q c #62A55F", +"r c #4B7749", +"s c #48A243", +"t c #33892F", +"u c #328B2F", +"v c #379833", +"w c #459F41", +"x c #4F7E4D", +"y c #5CA659", +"z c #369632", +"A c #349530", +"B c #349630", +"C c #59A955", +"D c #3A9936", +"E c #276C25", +"F c #256C22", +"G c #32932E", +"H c #2F892B", +"I c #2C8728", +"J c #2F912B", +"K c #2F902B", +"L c #2D8E29", +"M c #2D8F29", +"N c #257322", +"O c #226E1F", +"P c #2B8D27", +"Q c #298826", +"R c #288424", +" ", +" ", +" .+++@ ", +" #$%%% ", +" &***= ", +" -;>>, ", +" ')))! ", +" ~{]]^ ", +" /(__: ", +" <[[[} ", +" |1223 ", +" 4555 ", +" 67778 90aab ", +" cddef gddh ", +" ijjk lmjj ", +" noopq rsoot ", +" uvvwxyvvz ", +" ABBCDBBE ", +" FGGGGGH ", +" IJJJK ", +" LMMN ", +" OPQ ", +" R ", +" "}; diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/constrainsMarker.xpm b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/constrainsMarker.xpm new file mode 100755 index 000000000..516636927 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/constrainsMarker.xpm @@ -0,0 +1,200 @@ +/* XPM */ +static const char * constrainsMarker_xpm[] = { +"24 24 173 2", +" c None", +". c #AE6557", +"+ c #DF826F", +"@ c #F18C78", +"# c #FE947F", +"$ c #FD937E", +"% c #EC8976", +"& c #E08270", +"* c #A35F51", +"= c #C17060", +"- c #F88C77", +"; c #FD7F67", +"> c #FC7257", +", c #FC6A4E", +"' c #FC6345", +") c #FC6446", +"! c #FC6C50", +"~ c #FD8269", +"{ c #B86A5B", +"] c #EA8672", +"^ c #FC7A61", +"/ c #FB6548", +"( c #FB6244", +"_ c #F45F42", +": c #E85B3F", +"< c #EF5D41", +"[ c #FC7C64", +"} c #EA8673", +"| c #EF846F", +"1 c #F9694D", +"2 c #F96042", +"3 c #F55E41", +"4 c #BB4832", +"5 c #C04A33", +"6 c #F65F41", +"7 c #EE8571", +"8 c #E9816C", +"9 c #F76548", +"0 c #F75E40", +"a c #F66143", +"b c #E6573C", +"c c #F76649", +"d c #E67F6B", +"e c #B86959", +"f c #F56549", +"g c #F45C3E", +"h c #E8583B", +"i c #F45D3F", +"j c #F27D66", +"k c #E25539", +"l c #F5674B", +"m c #F0725A", +"n c #F2593B", +"o c #EE583A", +"p c #B8442D", +"q c #F25C3F", +"r c #EA7C67", +"s c #EE7259", +"t c #A95E4F", +"u c #EF593B", +"v c #EF5638", +"w c #B4432D", +"x c #CF4B30", +"y c #F05D41", +"z c #DF7A66", +"A c #B9452E", +"B c #EF593C", +"C c #A75C4E", +"D c #D36650", +"E c #EC5436", +"F c #DA4E32", +"G c #ED6044", +"H c #CE7361", +"I c #D16550", +"J c #E35E44", +"K c #EA5133", +"L c #DF4D31", +"M c #E24E31", +"N c #EC6449", +"O c #B36354", +"P c #DF5D43", +"Q c #E75436", +"R c #E74E30", +"S c #D04D34", +"T c #E44D2F", +"U c #EB6A51", +"V c #8D4E42", +"W c #D54D32", +"X c #E45539", +"Y c #E34C2E", +"Z c #E44B2D", +"` c #CE5138", +" . c #E76F57", +".. c #D45036", +"+. c #E14E30", +"@. c #D94729", +"#. c #E1492A", +"$. c #D85339", +"%. c #A0341E", +"&. c #E14B2D", +"*. c #E0725B", +"=. c #DB5236", +"-. c #D8492C", +";. c #C24329", +">. c #DE4628", +",. c #E04F32", +"'. c #BB3B22", +"). c #DF4C2F", +"!. c #D56F5B", +"~. c #C2432A", +"{. c #963B28", +"]. c #DC4325", +"^. c #B25C4B", +"/. c #C93D22", +"(. c #DE4F33", +"_. c #C86A57", +":. c #B95D4A", +"<. c #913927", +"[. c #D53F21", +"}. c #D94022", +"|. c #DC5B42", +"1. c #CF3D21", +"2. c #DD5338", +"3. c #B25F4F", +"4. c #DB583F", +"5. c #9D311B", +"6. c #D73E20", +"7. c #D84123", +"8. c #D16A55", +"9. c #D33D1F", +"0. c #DD5A40", +"a. c #D86B55", +"b. c #D74023", +"c. c #9A2F1B", +"d. c #C3371C", +"e. c #D43C1E", +"f. c #D7472B", +"g. c #D06C58", +"h. c #D33C1E", +"i. c #D64326", +"j. c #C4381C", +"k. c #C7361A", +"l. c #D2391B", +"m. c #D54529", +"n. c #DB6B56", +"o. c #B35E4D", +"p. c #B85441", +"q. c #D0391C", +"r. c #CA371A", +"s. c #C13418", +"t. c #D0381A", +"u. c #D13A1D", +"v. c #D65339", +"w. c #DC6A53", +"x. c #D7715D", +"y. c #CF6C59", +"z. c #DC735E", +"A. c #DC6953", +"B. c #D64F35", +"C. c #BD3318", +"D. c #972812", +"E. c #CC3518", +"F. c #CF3618", +"G. c #D03C1E", +"H. c #D24226", +"I. c #D0381B", +"J. c #CA3517", +"K. c #912611", +"L. c #B12E14", +"M. c #BE3115", +"N. c #CA3417", +"O. c #CC3517", +"P. c #AF2D14", +" ", +" . + @ # $ % & * ", +" = - ; > , ' ) ! > ~ - { ", +" ] ^ / ( ( _ : : < ( ( / [ } ", +" | 1 2 3 4 5 6 2 1 7 ", +" 8 9 0 0 a b 0 c d ", +" e f g h g i j k g l e ", +" m n o p n q r o n s ", +" t u v w x v y z A v B C ", +" D E E F E G H E E I ", +" J K L M K N O M K P ", +" Q R S T R U V W R X ", +" Y Z ` Z Z . ..Z +. ", +" @.#.$. %.#.&.*. =.#.-. ", +" ;.>.,. '.>.).!. ).>.~. ", +" {.].].^. /.].(._. :.].].<. ", +" [.}.|. 1.}.2.3. 4.}.[. ", +" 5.6.7.8. 9.6.0.a.b.6.c. ", +" d.e.f.g. h.e.i.e.j. ", +" k.l.m.n.o. p.q.l.l.r. ", +" s.t.u.v.w.x.y.y.z.A.B.u.t.C. ", +" D.E.F.F.G.H.H.I.F.F.J.K. ", +" L.M.N.O.M.P. ", +" "}; diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_ellipse.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_ellipse.cpp new file mode 100755 index 000000000..d108b2937 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_ellipse.cpp @@ -0,0 +1,127 @@ + +/** +** +** @RDE_HEADER@ +** Definition of ellipse drawable +** $Id$ +** $HeadURL$ +** $Author$ +*/ + +#include "drawable_ellipse.h" + +#include +#include + + +namespace SituationMap { namespace RB +{ + + DrawableEllipse::DrawableEllipse(Esri::ArcGISRuntime::MapGraphicsView * map) : DrawableEllipse(map,map->scene()) {;} + + + DrawableEllipse::DrawableEllipse(Esri::ArcGISRuntime::MapGraphicsView * map, QGraphicsScene * scene) + : RubberbandDrawable(map, scene) + , _elliItem(nullptr) + , _center() + , _radiusX(0) + , _radiusY(0) + , _elliStyle() + , _sinRect(0) + , _cosRect(1) + , _sectorStart() + , _sectorEnd() + { + _elliItem = new QGraphicsEllipseItem(); + _scene -> addItem( _elliItem ); + makeLineStyle( false, _elliStyle ); + _elliItem -> setPen( _elliStyle ); + + _sectItem = new QGraphicsEllipseItem(); + _scene -> addItem( _sectItem ); + makeLineStyle( false, _elliStyle ); + _sectItem -> setPen( _elliStyle ); + } /* DrawableLine::DrawableLine */ + + + DrawableEllipse::~DrawableEllipse() + { + if (_scene) + { + if (_elliItem) _scene->removeItem(_elliItem); + if (_sectItem) _scene->removeItem(_sectItem); + delete _elliItem; + delete _sectItem; + } + _elliItem = nullptr; + _sectItem = nullptr; + } /* DrawableEllipse::~DrawableEllipse */ + + + void DrawableEllipse::updateGraphic() + { + auto length = []( const QPointF & p ) -> qreal { return sqrt(p.x()*p.x()+p.y()*p.y()); }; + + qreal mapRotatePhi = _mapView->mapRotation() * MathConst::pi / 180.0; + + qreal sinMap = sin(mapRotatePhi); + qreal cosMap = cos(mapRotatePhi); + + qreal sinPhi = _sinRect * cosMap + _cosRect * sinMap; + qreal cosPhi = _cosRect * cosMap - _sinRect * sinMap; + + _center.mapToScene(_mapView); + + QPointF mapCenter( _center._mapCoordinate.x(), _center._mapCoordinate.y() ); + + RubberbandCoordinate x0y0(_mapView, convert( mapCenter+QPointF(-_radiusX,-_radiusY), _mapView->map()->spatialReference()) ); + RubberbandCoordinate x1y0(_mapView, convert( mapCenter+QPointF( _radiusX,-_radiusY), _mapView->map()->spatialReference()) ); + RubberbandCoordinate x0y1(_mapView, convert( mapCenter+QPointF(-_radiusX, _radiusY), _mapView->map()->spatialReference()) ); + + qreal width = length(x1y0._sceneCoordinate-x0y0._sceneCoordinate); + qreal height = length(x0y1._sceneCoordinate-x0y0._sceneCoordinate); + + makeLineStyle( false, _elliStyle ); + + QColor fillColor = transparentFillColor(); + //fillColor.setAlpha(0); + + QPen pen(_drawColor); + pen.setWidth(_lineWidth); + + if (_sectorStart != _sectorEnd) + { + auto getAngle = [](qreal sinPhi, qreal cosPhi) -> int + { + qreal phi = acos( cosPhi ); + if (sinPhi < 0) phi = MathConst::pi_two - phi; + return int( (phi * 180.0 / MathConst::pi) * 16); + }; // getAngle + + int startAngle = getAngle(_sectorStart.y(), _sectorStart.x()); + int endAngle = getAngle( _sectorEnd.y(), _sectorEnd.x()); + int spanAngle = (endAngle > startAngle) ? endAngle-startAngle : 5760-startAngle+endAngle; + + _sectItem -> setRect( -width*0.5, -height*0.5, width, height ); + _sectItem -> setPen( pen ); + _sectItem -> setBrush( (fillColor.alpha() > 0) ? QBrush(fillColor) : QBrush() ); + _sectItem -> setTransform( QTransform( cosPhi, -sinPhi, sinPhi, cosPhi, _center._sceneCoordinate.x(), _center._sceneCoordinate.y() )); + _sectItem -> setStartAngle( startAngle ); + _sectItem -> setSpanAngle( spanAngle ); + _sectItem -> setVisible( true ); + + fillColor.setAlphaF(fillColor.alphaF() * 0.5 ); + } + else _sectItem -> setVisible( false ); + + _elliItem -> setRect( -width*0.5, -height*0.5, width, height ); + _elliItem -> setPen(pen); + _elliItem -> setBrush( (fillColor.alpha() > 0) ? QBrush(fillColor) : QBrush() ); + _elliItem -> setTransform( QTransform( cosPhi, -sinPhi, sinPhi, cosPhi, _center._sceneCoordinate.x(), _center._sceneCoordinate.y() )); + + } /* DrawableEllipse::updateGraphic */ + + void DrawableEllipse::adjustCoordinates() { updateGraphic(); } + void DrawableEllipse::adjustWindow( const QRectF& ) { updateGraphic(); } + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_ellipse.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_ellipse.h new file mode 100755 index 000000000..d53f4a347 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_ellipse.h @@ -0,0 +1,72 @@ +/** +** +** @RDE_HEADER@ +** Definition of ellipse drawable +** $Id$ +** $HeadURL$ +** $Author$ +*/ + +#pragma once + + +#include "rubberband_drawable.h" + +#include + + +namespace SituationMap { namespace RB +{ + class DrawableEllipse : public RubberbandDrawable + { + Q_OBJECT + public: + + explicit DrawableEllipse( Esri::ArcGISRuntime::MapGraphicsView * ); + explicit DrawableEllipse( Esri::ArcGISRuntime::MapGraphicsView *, QGraphicsScene * ); + ~DrawableEllipse(); + + DrawableEllipse(const RubberbandDrawable &) = delete; + DrawableEllipse & operator = (const RubberbandDrawable &) = delete; + + + inline void setRect(const QPointF & center, qreal radiusX, qreal radiusY, qreal sin, qreal cos, const QPointF & sectorStart = QPointF(), const QPointF & sectorEnd = QPointF() ) + { + // p1,p2 = corner points of unrotated ellipse in scene coordinates + _center = RubberbandCoordinate( _mapView, convert(center, _mapView->map()->spatialReference()) ); + _radiusX = radiusX; + _radiusY = radiusY; + + _sinRect = sin; + _cosRect = cos; + + _sectorStart = sectorStart; + _sectorEnd = sectorEnd; + updateGraphic(); + } + + qreal radiusX() { return _radiusX; } + qreal radiusY() { return _radiusY; } + + protected: + + QGraphicsEllipseItem * _elliItem; + QGraphicsEllipseItem * _sectItem; + RubberbandDrawable::RubberbandCoordinate _center; + qreal _radiusX; + qreal _radiusY; + QPen _elliStyle; + qreal _sinRect; + qreal _cosRect; + QPointF _sectorStart; + QPointF _sectorEnd; + + void updateGraphic() override; + + + void adjustCoordinates() override; + void adjustWindow( const QRectF& ) override; + + }; /* DrawableEllipse */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_line.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_line.cpp new file mode 100755 index 000000000..cab0e5861 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_line.cpp @@ -0,0 +1,109 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of line drawables +** $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 .........: +** +** 06.08.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "drawable_line.h" + +#include + + +namespace SituationMap { namespace RB +{ + + DrawableLine::DrawableLine(Esri::ArcGISRuntime::MapGraphicsView * map) + : RubberbandDrawable(map) + , _lineItem(nullptr) + , _lineStyle() + , _dottedLine(false) + { + _lineItem = new QGraphicsLineItem(); + _scene -> addItem( _lineItem ); + + makeLineStyle( _dottedLine, _lineStyle ); + _lineItem -> setPen( _lineStyle ); + + } /* DrawableLine::DrawableLine */ + + + DrawableLine::DrawableLine(Esri::ArcGISRuntime::MapGraphicsView * map, QGraphicsScene * scene) + : RubberbandDrawable(map, scene) + , _lineItem(nullptr) + , _lineStyle() + , _dottedLine(false) + { + _lineItem = new QGraphicsLineItem(); + _scene -> addItem( _lineItem ); + makeLineStyle( _dottedLine, _lineStyle ); + _lineItem -> setPen( _lineStyle ); + + } /* DrawableLine::DrawableLine */ + + DrawableLine::~DrawableLine() + { + if (_scene) + { + if (_lineItem) + { + _scene->removeItem(_lineItem); + delete _lineItem; + } + } + _lineItem = nullptr; + } /* DrawableLine::~DrawableLine */ + + + void DrawableLine::setLine( const RubberbandDrawable::RubberbandCoordinate & p1, const RubberbandDrawable::RubberbandCoordinate & p2 ) + { + _lineCoordinates[0] = p1; + _lineCoordinates[1] = p2; + updateGraphic(); + } /* DrawableLine::setPath */ + + + void DrawableLine::updateGraphic() + { + _lineCoordinates[0].mapToScene(_mapView); + _lineCoordinates[1].mapToScene(_mapView); + + qreal x1 = _lineCoordinates[0]._sceneCoordinate.x(); + qreal y1 = _lineCoordinates[0]._sceneCoordinate.y(); + qreal x2 = _lineCoordinates[1]._sceneCoordinate.x(); + qreal y2 = _lineCoordinates[1]._sceneCoordinate.y(); + + makeLineStyle( _dottedLine, _lineStyle ); + + _lineItem -> setLine( x1,y1,x2,y2 ); + _lineItem -> setPen( _lineStyle ); + _lineItem -> setVisible( _isVisible ); + + } /* DrawableLine::updateGraphic */ + + void DrawableLine::adjustCoordinates() { updateGraphic(); } + void DrawableLine::adjustWindow( const QRectF& ) { updateGraphic(); } + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_line.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_line.h new file mode 100755 index 000000000..00ade66ae --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_line.h @@ -0,0 +1,68 @@ +/** +** +** @RDE_HEADER@ +** Definition of line drawable +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + + +#include "rubberband_drawable.h" + + +namespace SituationMap { namespace RB +{ + + class DrawableLine : public RubberbandDrawable + { + Q_OBJECT + public: + + explicit DrawableLine( Esri::ArcGISRuntime::MapGraphicsView * ); + explicit DrawableLine( Esri::ArcGISRuntime::MapGraphicsView *, QGraphicsScene * ); + ~DrawableLine(); + + DrawableLine(const RubberbandDrawable &) = delete; + DrawableLine & operator = (const RubberbandDrawable &) = delete; + + void setLine( const RubberbandDrawable::RubberbandCoordinate & p1, const RubberbandDrawable::RubberbandCoordinate & p2 ); + + inline void dottedLine(bool on = true) { _dottedLine = on; } + + protected: + + QGraphicsLineItem * _lineItem; + RubberbandDrawable::RubberbandCoordinate _lineCoordinates[2]; + QPen _lineStyle; + bool _dottedLine; + + void updateGraphic() override; + + void adjustCoordinates() override; + void adjustWindow( const QRectF& ) override; + + }; /* DrawableLine */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_painter_path.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_painter_path.cpp new file mode 100755 index 000000000..c272673b4 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_painter_path.cpp @@ -0,0 +1,152 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of path drawables +** $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 .........: +** +** 27.07.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "drawable_painter_path.h" + +#include +#include + + +namespace SituationMap { namespace RB +{ + + DrawablePainterPath::DrawablePainterPath(Esri::ArcGISRuntime::MapGraphicsView * map) + : RubberbandDrawable(map) + , _painterPathItem(nullptr) + , _pathCoordinates() + , _lineStyle() + , _dottedPath(false) + , _closedLine(false) + , _fillSolid(false) + { + _painterPathItem = _scene->addPath( QPainterPath() ); + _painterPathItem -> setFlags( QGraphicsItem::ItemIgnoresTransformations ); + makeLineStyle( _dottedPath, _lineStyle ); + _painterPathItem -> setPen ( (_drawColor.alpha()) ? QPen(_drawColor) : QPen() ); + _fillColor.setAlpha(0); + _painterPathItem -> setBrush( (_fillColor.alpha()) ? QBrush(_fillColor) : QBrush() ); + } /* DrawablePainterPath::DrawablePainterPath */ + + + DrawablePainterPath::DrawablePainterPath(Esri::ArcGISRuntime::MapGraphicsView * map, QGraphicsScene * scene) + : RubberbandDrawable(map, scene) + , _painterPathItem(nullptr) + , _pathCoordinates() + , _lineStyle() + , _dottedPath(false) + , _closedLine(false) + , _fillSolid(false) + { + _painterPathItem = _scene->addPath( QPainterPath() ); + _painterPathItem -> setFlags( QGraphicsItem::ItemIgnoresTransformations ); + makeLineStyle( _dottedPath, _lineStyle ); + _painterPathItem -> setPen ( (_drawColor.alpha()) ? _lineStyle : QPen() ); + _fillColor.setAlpha(0); + _painterPathItem -> setBrush( (_fillColor.alpha()) ? QBrush(_fillColor) : QBrush() ); + } /* DrawablePainterPath::DrawablePainterPath */ + + + DrawablePainterPath::~DrawablePainterPath() + { + if (_scene) + { + if (_painterPathItem) + { + _scene->removeItem(_painterPathItem); + delete _painterPathItem; + } + _painterPathItem = nullptr; + } + } /* DrawablePainterPath::~DrawablePainterPath */ + + + void DrawablePainterPath::setPath( const QVector & path ) + { + _pathCoordinates = path; + updateGraphic(); + } /* DrawablePainterPath::setPath */ + + + void DrawablePainterPath::setPath( const QVector & scenePath ) + { + _pathCoordinates.clear(); + _pathCoordinates.reserve( scenePath.size() ); + + for ( auto p : scenePath ) + { + _pathCoordinates.append( RubberbandDrawable::RubberbandCoordinate(_mapView,p) ); + } + updateGraphic(); + + } /* DrawablePainterPath::setPath */ + + + void DrawablePainterPath::updateGraphic() + { + DEBUG_LOG(_logger) << LOGFUNCTION; + + if ((_closedLine) && (_pathCoordinates.size() < 3)) + { + _painterPathItem -> setVisible( false ); + return; + } + + QPainterPath painterPath; + + QVector::iterator it = _pathCoordinates.begin(); + + if (it != _pathCoordinates.end()) + { + painterPath.moveTo( it->mapToScene(_mapView) ); + DEBUG_LOG(_logger) << LOGFUNCTION << " map-x = " << it->_mapCoordinate.x() << " map-y = " << it->_mapCoordinate.y(); + while (true) + { + ++it; + if (it == _pathCoordinates.end()) break; + painterPath.lineTo( it->mapToScene(_mapView) ); + DEBUG_LOG(_logger) << LOGFUNCTION << " map-x = " << it->_mapCoordinate.x() << " map-y = " << it->_mapCoordinate.y(); + } + if (_closedLine) painterPath.closeSubpath(); + } + + const QColor &fillColor = transparentFillColor(); + makeLineStyle( _dottedPath, _lineStyle ); + + _painterPathItem -> setVisible( _isVisible ); + _painterPathItem -> setPen ( (_drawColor.alpha() > 0) ? _lineStyle : QPen() ); + _painterPathItem -> setBrush( ( fillColor.alpha() > 0) ? QBrush(fillColor) : QBrush() ); + _painterPathItem -> setZValue( _zBufferValue ); + _painterPathItem -> setPath( painterPath ); + + } /* DrawablePainterPath::updateGraphic */ + + void DrawablePainterPath::adjustCoordinates() { updateGraphic(); } + void DrawablePainterPath::adjustWindow( const QRectF& ) { updateGraphic(); } + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_painter_path.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_painter_path.h new file mode 100755 index 000000000..cdf043647 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_painter_path.h @@ -0,0 +1,75 @@ +/** +** +** @RDE_HEADER@ +** Definition of base class for rubberband drawing objects +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + + +#include "rubberband_drawable.h" + + +namespace SituationMap { namespace RB +{ + + class DrawablePainterPath : public RubberbandDrawable + { + Q_OBJECT + public: + + explicit DrawablePainterPath( Esri::ArcGISRuntime::MapGraphicsView * ); + explicit DrawablePainterPath( Esri::ArcGISRuntime::MapGraphicsView *, QGraphicsScene * ); + ~DrawablePainterPath(); + + DrawablePainterPath(const RubberbandDrawable &) = delete; + DrawablePainterPath & operator = (const RubberbandDrawable &) = delete; + + void setPath( const QVector & scenePath ); + + void setPath( const QVector & path ); + + inline void enableClosedLine(bool on = true) { _closedLine = on; } + + inline void dottedPath(bool on = true) { _dottedPath = on; } + inline void fillSolid(bool on = true) { _fillSolid = on; } + + protected: + + QGraphicsPathItem * _painterPathItem; + QVector _pathCoordinates; + QPen _lineStyle; + bool _dottedPath; + bool _closedLine; + bool _fillSolid; + + void updateGraphic() override; + + void adjustCoordinates() override; + void adjustWindow( const QRectF& ) override; + + }; /* DrawablePainterPath */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_smooth_path.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_smooth_path.cpp new file mode 100755 index 000000000..b36a06606 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_smooth_path.cpp @@ -0,0 +1,127 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of line drawables +** $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 .........: +** +** 06.08.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "drawable_smooth_path.h" + +#include +#include + +#include +#include + + +namespace SituationMap { namespace RB +{ + + DrawableSmoothPath::DrawableSmoothPath(Esri::ArcGISRuntime::MapGraphicsView * map) + : DrawablePainterPath(map) + , _smoothLine( new SituationMap::MathUtils::SmoothLine() ) + { + _pathRasterer = _smoothLine->createPathRaster(); + } /* DrawableSmoothPath::DrawableLine */ + + + DrawableSmoothPath::DrawableSmoothPath(Esri::ArcGISRuntime::MapGraphicsView * map, QGraphicsScene * scene) + : DrawablePainterPath(map, scene) + , _smoothLine( new SituationMap::MathUtils::SmoothLine() ) + { + _pathRasterer = _smoothLine->createPathRaster(); + } /* DrawableSmoothLine::DrawableLine */ + + + DrawableSmoothPath::~DrawableSmoothPath() + { + delete _pathRasterer; + delete _smoothLine; + _pathRasterer = nullptr; + _smoothLine = nullptr; + } /* DrawableSmoothPath::~DrawableSmoothPath */ + + + void DrawableSmoothPath::setPath( const QVector & vertexVector ) + { + _pathCoordinates = vertexVector; + updateGraphic(); + } /* DrawableSmoothPath::setPath */ + + + void DrawableSmoothPath::updateGraphic() + { + if (_pathCoordinates.size() > 2) + { + makeLineStyle( _dottedPath, _lineStyle ); + + _painterPathItem -> setPen( _lineStyle ); + _painterPathItem -> setVisible( _isVisible ); + + if (_closedLine) + { + const QColor &fillColor = transparentFillColor(); + _painterPathItem -> setBrush( (fillColor.alpha() > 0) ? QBrush(fillColor) : QBrush() ); + } + else _painterPathItem -> setBrush( QBrush() ); + + QVector vertexVector; + vertexVector.reserve( _pathCoordinates.size() ); + + for (auto vertex : std::as_const(_pathCoordinates)) + { + vertex.mapToScene(_mapView); + vertexVector.append( vertex._sceneCoordinate ); + } + + _smoothLine -> vertexVector( vertexVector, _closedLine ); + _pathRasterer -> epsilon( 2 ); + _pathRasterer -> maxVertexCount( 1000 ); + _pathRasterer -> rasterize( _smoothLine->minimumCurveParameter(), _smoothLine->maximumCurveParameter(), vertexVector ); + + QPainterPath path; + + if (vertexVector.size() > 1) + { + QVector::const_iterator vit = vertexVector.constBegin(); + + path.moveTo( *vit ); + for (++vit; vit != vertexVector.end(); ++vit) + { + path.lineTo( *vit ); + } + if (_closedLine) path.lineTo( QPointF(path.elementAt(0)) ); + } + _painterPathItem->setPath(path); + } + else _painterPathItem ->setVisible(false); + + } /* DrawableSmoothPath::updateGraphic */ + + + void DrawableSmoothPath::adjustCoordinates() { updateGraphic(); } + void DrawableSmoothPath::adjustWindow( const QRectF& ) { updateGraphic(); } + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_smooth_path.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_smooth_path.h new file mode 100755 index 000000000..24c9c64a6 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/drawable_smooth_path.h @@ -0,0 +1,77 @@ +/** +** +** @RDE_HEADER@ +** Definition of line drawable +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + + +#include "drawable_painter_path.h" + + +namespace SituationMap { + + + namespace MathUtils + { + class SmoothLine; + class PathRasterer; + } + + + namespace RB + { + + + class DrawableSmoothPath : public DrawablePainterPath + { + Q_OBJECT + public: + + explicit DrawableSmoothPath( Esri::ArcGISRuntime::MapGraphicsView * ); + explicit DrawableSmoothPath( Esri::ArcGISRuntime::MapGraphicsView *, QGraphicsScene * ); + ~DrawableSmoothPath(); + + DrawableSmoothPath(const DrawableSmoothPath &) = delete; + DrawableSmoothPath & operator = (const DrawableSmoothPath &) = delete; + + void setPath( const QVector & vertexVector ); + + protected: + + + SituationMap::MathUtils::SmoothLine * _smoothLine; + SituationMap::MathUtils::PathRasterer * _pathRasterer; + + void updateGraphic() override; + + void adjustCoordinates() override; + void adjustWindow( const QRectF& ) override; + + }; /* DrawableSmoothPath */ + + } /* namespace RB */ +} /* namespace SituationMap */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_delegater.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_delegater.cpp new file mode 100755 index 000000000..974e722a3 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_delegater.cpp @@ -0,0 +1,72 @@ +/** +** +** @RDE_HEADER@ +** Event Dispatcher for Qt5 to delegate mouse and keyboard event to another event dispatcher +** $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 .........: +** +** 15.04.2015, JT : First edition +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "event_delegater.h" + +namespace SituationMap { namespace RB +{ + + EventDelegater::EventDelegater(QWidget * getEventsFrom, AbstractEventDispatcher * delegateEventTo) + : AbstractEventDispatcher(getEventsFrom), _delegateEventTo(delegateEventTo) + { + connect( delegateEventTo, &AbstractEventDispatcher::signalKickTheBucket, + this , &AbstractEventDispatcher::onDo_kickTheBucket ); + + connect( this , &AbstractEventDispatcher::signalKickTheBucket, + delegateEventTo, &AbstractEventDispatcher::onDo_kickTheBucket ); + + _restoreMouseTracking = delegateEventTo->_restoreMouseTracking; + _whatsThisBackup = delegateEventTo->_whatsThisBackup; + + } /* EventDispatcher::EventDispatcher */ + + + bool EventDelegater::eventFilter(QObject * watched, QEvent * e) + { + if (hasFinished()) return true; // don't accept new events if dispatcher is going to finish + if (hasActiveEvent()) return true; // don't accept new events before last event has been handled completly + + if (_delegateEventTo != nullptr) + { + return _delegateEventTo->eventFilter( watched, e ); + } + return false; + } /* EventDispatcher::eventFilter */ + + + void EventDelegater::KickTheBucket() + { + if (_delegateEventTo != nullptr) + { + _delegateEventTo->unlinkEventDispatcher(); + } + AbstractEventDispatcher::KickTheBucket(); + } /* EventDelegater::KickTheBucket */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_delegater.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_delegater.h new file mode 100755 index 000000000..595372fd7 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_delegater.h @@ -0,0 +1,72 @@ +/** +** +** @RDE_HEADER@ +** Event Dispatcher for Qt5 to delegate mouse and keyboard event to another event dispatcher +** $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 .........: +** +** 15.04.2015, JT : first edition +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include + +#include "abstract_event_dispatcher.h" + + +namespace SituationMap { namespace RB +{ + + /** + @class EventDelegater event_delegater.h + @brief Event filter that delegates events to another event filter + + Some Qt-Widgets have a special viewport widget. In this case keyboard and mouse events + are sended to different widgets. To concentrate the event handling in one event filter + for both types of events, it is necessary to forward events from event filter to another + one. + + @see AbstractEventDispatcher, EventDispatcher, EventMouseNavigator + */ + class EventDelegater final : public AbstractEventDispatcher + { + Q_OBJECT + public: + + EventDelegater( QWidget * getEventsFrom, AbstractEventDispatcher * delegateEventTo ); + + ~EventDelegater() = default; + + EventDelegater(const EventDelegater &) = delete; + EventDelegater & operator = (const EventDelegater &) = delete; + + protected: + + QPointer _delegateEventTo; + + bool eventFilter(QObject*, QEvent*) override; + + void KickTheBucket() override; + }; /* EventDelegater */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_dispatcher.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_dispatcher.cpp new file mode 100755 index 000000000..093749071 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_dispatcher.cpp @@ -0,0 +1,626 @@ +/** +** +** @RDE_HEADER@ +** Event Dispatcher for Qt5 to handle mouse and keyboard event +** $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 .........: +** +** 28.04.2003, JT : First edition for use with Qt4 in START Feature Editor fee +** 25.03.2015, JT : Refactoring for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "event_dispatcher.h" + +#include "gestures.h" + +#include + +#include +#include + +#include +#include +#include + +namespace SituationMap { namespace RB +{ + +EventDispatcher::EventDispatcher(QWidget * getEventsFrom) + : AbstractEventDispatcher(getEventsFrom) + , _snapEnabled(false) + , _snapRatio(5) + , _drawRatio(false) + , _doubleClickHandled(false) + , _leftPressHandled(false) +{ +} /* EventDispatcher::EventDispatcher */ + + +EventDispatcher::EventDispatcher(QWidget * getEventsFrom, EventActionInterface * eventActions ) + : AbstractEventDispatcher(getEventsFrom, eventActions) + , _snapEnabled(false) + , _snapRatio(5) + , _drawRatio(false) + , _doubleClickHandled(false) + , _leftPressHandled(false) +{ +} /* EventDispatcher::EventDispatcher */ + + +bool EventDispatcher::eventFilterKey( int key, Qt::KeyboardModifiers mod ) +{ + QPoint moveDelta; + int multiply ( (mod.testFlag(Qt::KeyboardModifier::ShiftModifier)) ? 10 : 1); + bool controlKey( (mod.testFlag(Qt::KeyboardModifier::ControlModifier)) ); + + switch (key) + { + case Qt::Key_Delete : return onDoDelete(); break; + case Qt::Key_Z : if (controlKey) return onDoUndo(); break; + case Qt::Key_Escape : return onDoEscape(); break; + case Qt::Key_Return : return onDoEnd(); break; + case Qt::Key_Left : moveDelta = QPoint(-1, 0)*multiply; break; + case Qt::Key_Right : moveDelta = QPoint( 1, 0)*multiply; break; + case Qt::Key_Up : moveDelta = QPoint( 0,-1)*multiply; break; + case Qt::Key_Down : moveDelta = QPoint( 0, 1)*multiply; break; + default : return false; + } /* switch */ + + if (_getEventsFrom != nullptr) + { + QPoint cursorPos = _getEventsFrom->mapFromGlobal( QCursor::pos() ); + onPress( cursorPos.x(), cursorPos.y(), mod ); + cursorPos += moveDelta; + onMove ( cursorPos.x(), cursorPos.y(), Qt::MouseButton::LeftButton, mod ); + onClick( cursorPos.x(), cursorPos.y(), mod ); + + QCursor::setPos( _getEventsFrom->mapToGlobal(cursorPos) ); + } + return true; +} /* EventDispatcher::eventFilterKey */ + + +bool EventDispatcher::eventFilterGesture( QGestureEvent * gestureEvent ) +{ + bool handled = false; + + if (gestureEvent) + { + const QList & gestureList = gestureEvent->gestures(); + for (auto gesture : gestureList) + { + Qt::GestureType gestureType = gesture->gestureType(); + Qt::GestureState gestureState = gesture->state(); + + QTapGesture * tapGesture = nullptr; + QTapAndHoldGesture * tapAndHoldGesture = nullptr; + QPanGesture * panGesture = nullptr; + QPinchGesture * pinchGesture = nullptr; + QSwipeGesture * swipeGesture = nullptr; + ApplyGesture * applyGesture = nullptr; + QString typeName; + QString stateName; + + switch (gestureType) + { + case Qt::TapGesture : tapGesture = static_cast(gesture); typeName = QLatin1String("TapGesture"); break; + case Qt::TapAndHoldGesture : tapAndHoldGesture = static_cast(gesture); typeName = QLatin1String("TapAndHoldGesture"); break; + case Qt::PanGesture : panGesture = static_cast(gesture); typeName = QLatin1String("PanGesture"); break; + case Qt::PinchGesture : pinchGesture = static_cast(gesture); typeName = QLatin1String("PinchGesture"); break; + case Qt::SwipeGesture : swipeGesture = static_cast(gesture); typeName = QLatin1String("SwipeGesture"); break; + default: + { + if (gestureType == _applyGestureID) + { + applyGesture = static_cast(gesture); + typeName = QLatin1String("ApplyGesture"); + } + } break; + } + switch (gestureState) + { + case Qt::NoGesture : stateName = QLatin1String("NoGesture"); break; + case Qt::GestureStarted : stateName = QLatin1String("GestureStarted"); break; + case Qt::GestureUpdated : stateName = QLatin1String("GestureUpdated"); break; + case Qt::GestureFinished : stateName = QLatin1String("GestureFinished"); break; + case Qt::GestureCanceled : stateName = QLatin1String("GestureCanceled"); break; + } + + DEBUG_LOG(_logger) << LOGFUNCTION << " Gesture type=" << typeName << " Gesture state=" << stateName; + + if (panGesture) + { + const QPoint & offset = panGesture->offset().toPoint(); + const QPoint & hotSpot = _getEventsFrom->mapFromGlobal( panGesture->hotSpot().toPoint() ); + + switch (gestureState) + { + case Qt::GestureStarted: + { + handled = onPress( hotSpot.x(), hotSpot.y(), nullptr ); + if (handled) gestureEvent -> accept( panGesture ); + } break; + + case Qt::GestureUpdated: + handled = onMove ( hotSpot.x() + offset.x(), hotSpot.y() + offset.y(), Qt::MouseButton::LeftButton, nullptr ); + break; + case Qt::GestureFinished: + // handled = onMove ( hotSpot.x() + offset.x(), hotSpot.y() + offset.y(), Qt::MouseButton::LeftButton, 0 ); + handled = onClick( hotSpot.x() + offset.x(), hotSpot.y() + offset.y(), nullptr ); + break; + case Qt::GestureCanceled: + getEventActions()->resetAction( this, 0, 0 ); + break; + default: + break; + } + + } + + if (applyGesture) + { + switch (gestureState) + { + case Qt::GestureStarted : gestureEvent -> accept( applyGesture ); break; + case Qt::GestureFinished : + { + getEventActions()->resetAction( this, qRound(applyGesture->position().x()), qRound(applyGesture->position().y()) ); + handled = onDoubleClick( qRound(applyGesture->position().x()), qRound(applyGesture->position().y()), nullptr ); + if (handled) + { + gestureEvent -> accept( applyGesture ); + return true; + } + } break; + default : break; + } + } + + // currently not used + Q_UNUSED(handled); + Q_UNUSED(tapGesture); + Q_UNUSED(tapAndHoldGesture); + Q_UNUSED(pinchGesture); + Q_UNUSED(swipeGesture); + } + } + return handled; +} /* EventDispatcher::eventFilerGesture */ + + +bool EventDispatcher::eventFilterWheel( QWheelEvent * e ) +{ + const int WHEEL_DELTA = 120; // natural mouse driver constant + + if (e->modifiers().testFlag(Qt::KeyboardModifier::ControlModifier) && + e->buttons().testFlag(Qt::MidButton)) + { + int newValue = (int)_snapRatio + (e->angleDelta() / WHEEL_DELTA).manhattanLength(); + _snapRatio = (newValue < 5) ? 5 : newValue; + return onMove( e->x(), e->y(), e->buttons(), e->modifiers() ); + } + return false; +} /* EventDispatcher::eventFilterWheel */ + + +bool EventDispatcher::eventFilter( QObject * watched, QEvent * e ) +{ + // Maybe ??? + // qCritical() << " _getEventsFrom = " << _getEventsFrom + // << " watched = " << watched + // << " e->type() = " << e->type(); + // if (watched != _getEventsFrom) + // return AbstractEventDispatcher::eventFilter(watched,e); + + + Q_UNUSED(watched); + + QString debugString(QLatin1String("unhandled event")); + + if (hasFinished()) return true; // don't accept new events if dispatcher is going to finish + if (hasActiveEvent()) return true; // don't accept new events before last event has been handled completly + + if (e) + { + bool handled( false ); + + setActiveEvent( true ); + + switch( e->type() ) + { + case QEvent::Enter : + { + debugString = QLatin1String("enter event"); + _getEventsFrom -> setFocus(); + _doubleClickHandled = false; + _leftPressHandled = false; + } break; + + case QEvent::Leave : _doubleClickHandled = _leftPressHandled = false; break; + + case QEvent::GraphicsSceneMousePress : break; + case QEvent::MouseButtonPress : + { + debugString = QLatin1String("mouse button press event"); + + _doubleClickHandled = false; + + QMouseEvent *mouseEvent = static_cast(e); + if (Qt::RightButton == mouseEvent->button()) + { + handled = onMenuPress( mouseEvent->x() + , mouseEvent->y() + , mouseEvent->modifiers() ); + } + if (Qt::LeftButton == mouseEvent->button()) + { + if (!(mouseEvent->modifiers().testFlag(Qt::KeyboardModifier::ControlModifier))) + { + handled = onPress( mouseEvent->x() + , mouseEvent->y() + , mouseEvent->modifiers() ); + if (handled) _leftPressHandled = true; + } + } + if (Qt::MidButton == mouseEvent->button()) + { + handled = mouseEvent->modifiers().testFlag(Qt::KeyboardModifier::ControlModifier); + } + } break; + + case QEvent::TouchBegin: + { + debugString = QLatin1String("mouse button press event TOUCH"); + + _doubleClickHandled = false; + + QTouchEvent *mouseEvent = static_cast(e); + + if (mouseEvent->touchPoints().size() > 0) + { + const QTouchEvent::TouchPoint tp = mouseEvent->touchPoints().at(0); + + handled = onPress( tp.pos().x() + , tp.pos().y() + , mouseEvent->modifiers() ); + if (handled) _leftPressHandled = true; + } + break; + } + + + case QEvent::GraphicsSceneMouseDoubleClick : break; + case QEvent::MouseButtonDblClick : + { + debugString = QLatin1String("mouse double click event"); + QMouseEvent *mouseEvent = static_cast(e); + if (Qt::LeftButton == mouseEvent->button()) + { + if (!(mouseEvent->modifiers().testFlag(Qt::KeyboardModifier::ControlModifier))) + { + onDoubleClick( mouseEvent->x() + , mouseEvent->y() + , mouseEvent->modifiers() ); + + _doubleClickHandled = handled = true; + } + } + } break; + + case QEvent::GraphicsSceneMouseRelease : break; + case QEvent::MouseButtonRelease : + { + if (!_doubleClickHandled) + { + debugString = QLatin1String("mouse button release event"); + QMouseEvent *mouseEvent = static_cast(e); + if (Qt::RightButton == mouseEvent->button()) + { + handled = onMenuClick( mouseEvent->x() + , mouseEvent->y() + , mouseEvent->modifiers() ); + } + if (Qt::LeftButton == mouseEvent->button()) + { + if (!(mouseEvent->modifiers().testFlag(Qt::KeyboardModifier::ControlModifier))) + { + // The mouse button press event must had be handled before. If not skip button release ... + if (_leftPressHandled) + { + _leftPressHandled = false; + handled = onClick( mouseEvent->x() + , mouseEvent->y() + , mouseEvent->modifiers() ); + } + else callEventAction( &EventActionInterface::buttonPositionAction, mouseEvent->x(), mouseEvent->y() ); + } + } + if (Qt::MidButton == mouseEvent->button()) + { + handled = mouseEvent->modifiers().testFlag(Qt::KeyboardModifier::ControlModifier); + } + } + else _doubleClickHandled = false; + + } break; + + case QEvent::TouchEnd: + { + + debugString = QLatin1String("mouse button release event TOUCH"); + QTouchEvent *mouseEvent = static_cast(e); + + // The mouse button press event must had be handled before. If not skip button release ... + if (mouseEvent->touchPoints().size() > 0) + { + const QTouchEvent::TouchPoint tp = mouseEvent->touchPoints().at(0); + if (_leftPressHandled) + { + _leftPressHandled = false; + handled = onClick( tp.pos().x() + , tp.pos().y() + , mouseEvent->modifiers() ); + + } + else + { + callEventAction( &EventActionInterface::buttonPositionAction, tp.pos().x(), tp.pos().y() ); + } + } + + break; + } + + case QEvent::GraphicsSceneMouseMove : break; + case QEvent::MouseMove : + { + debugString = QLatin1String("mouse move event"); + + _doubleClickHandled = false; + + QMouseEvent *mouseEvent = static_cast(e); + + if (mouseEvent->buttons() != Qt::MidButton) + { + if (!(mouseEvent->modifiers().testFlag(Qt::KeyboardModifier::ControlModifier))) + { + if (_drawRatio) + { + if (!(mouseEvent->modifiers().testFlag(Qt::KeyboardModifier::AltModifier))) + { + _drawRatio = false; + } + } + else + { + if (mouseEvent->modifiers().testFlag(Qt::KeyboardModifier::AltModifier)) + { + _drawRatio = true; + } + } + + handled = onMove( mouseEvent->x() + , mouseEvent->y() + , mouseEvent->buttons() + , mouseEvent->modifiers()); + + // REGG-5230 - PlanEditor: Position beim Erstellen und Modifizieren + // von Elementen wird nicht aktualisiert + // Next handler is EventMouseNavigator. ::eventFilter() will handle + // QEvent::MouseMove and emit mouseMovedEvent to update PositionStr. + // If handled == true, the next handler is NOT called. + // So emit here alternatively. + if (handled) + emit mouseMovedEvent(mouseEvent->pos().x(), mouseEvent->pos().y()); + } + } + } break; + + case QEvent::TouchUpdate: + { + debugString = QLatin1String("mouse move event Touch"); + + _doubleClickHandled = false; + + QTouchEvent* mouseEvent = static_cast(e); + if (mouseEvent->touchPoints().size() > 0) + { + const QTouchEvent::TouchPoint tp = mouseEvent->touchPoints().at(0); + + handled = onMove( tp.pos().x() + , tp.pos().y() + , Qt::LeftButton + , mouseEvent->modifiers()); + } + break; + } + + + case QEvent::KeyPress : + { + QKeyEvent *keyEvent = static_cast(e); + + debugString = QStringLiteral("key press event (%1)").arg(keyEvent->key()); + + handled = eventFilterKey( keyEvent->key(),keyEvent->modifiers() ); + if ((keyEvent->modifiers().testFlag(Qt::KeyboardModifier::ControlModifier)) || (keyEvent->key() == Qt::Key_Control)) + { + setSnapEnabled( true ); + _drawRatio = true; + handled = true; + } + } break; + + case QEvent::KeyRelease : + { + debugString = QLatin1String("key release event"); + if (_drawRatio) + { + _drawRatio = false; + setSnapEnabled( true ); + } + // handled = true; + } break; + + + case QEvent::Wheel : + { + debugString = QLatin1String("mouse wheel event"); + QWheelEvent *wheelEvent = static_cast(e); + if (wheelEvent->modifiers() & (Qt::KeyboardModifier::ShiftModifier | Qt::KeyboardModifier::ControlModifier)) + { + handled = eventFilterWheel( wheelEvent ); + } + } break; + + case QEvent::Gesture: + { + handled = eventFilterGesture( static_cast(e) ); + } break; + + default: break; + + } /* switch */ + + if (!hasFinished()) setActiveEvent( false ); + + if (handled) e->accept(); + + TRACE_LOG(_logger) << LOGFUNCTION << " Event " << debugString << " handled = " << handled; + + if (handled) return true; + } + return AbstractEventDispatcher::eventFilter(watched,e); +} /* EventDispatcher::eventFilter */ + + +bool EventDispatcher::onMove(int x, int y, Qt::MouseButtons buttons, Qt::KeyboardModifiers mod) +{ + Q_UNUSED(mod); + if (buttons.testFlag(Qt::MouseButton::LeftButton)) return callEventAction( &EventActionInterface::movePickedAction, x, y ); + return callEventAction( &EventActionInterface::moveAction, x, y ); +} /* EventDispatcher::onMove */ + + +bool EventDispatcher::onPress(int x, int y, Qt::KeyboardModifiers mod) +{ + if (mod == Qt::NoModifier) + return callEventAction( &EventActionInterface::selectAction, x, y ); + if (mod.testFlag(Qt::KeyboardModifier::ShiftModifier)) + return callEventAction( &EventActionInterface::multiSelectActionPress, x, y ); + return false; +} /* EventDispatcher::onPress */ + + +bool EventDispatcher::onClick(int x, int y, Qt::KeyboardModifiers mod) +{ + Q_UNUSED(x); + Q_UNUSED(y); + + if (mod == Qt::NoModifier) + { + bool ret = callEventAction( &EventActionInterface::setCoordinateAction, x, y ); + if (ret) + { + EventActionInterface * eaIf = getEventActions(); + if (eaIf) eaIf->userActionFinished(); + } + return ret; + } + if (mod.testFlag( Qt::KeyboardModifier::ShiftModifier )) + return callEventAction( &EventActionInterface::multiSelectActionRelease, x, y ); + + return false; +} /* EventDispatcher::onClick */ + + +bool EventDispatcher::onMenuPress(int x, int y, Qt::KeyboardModifiers mod) +{ + Q_UNUSED(x); + Q_UNUSED(y); + Q_UNUSED(mod); + + return false; +} /* EventDispatcher::onMenuPress */ + + +bool EventDispatcher::onMenuClick(int x, int y, Qt::KeyboardModifiers mod) +{ + Q_UNUSED(mod); + return callEventAction( &EventActionInterface::contextMenuAction, x, y ); +} /* EventDispatcher::onMenuClick */ + + +bool EventDispatcher::onDoubleClick(int x, int y, Qt::KeyboardModifiers ) +{ + callEventAction( &EventActionInterface::completeStateAction, x, y ); + return true; +} /* EventDispatcher::onDoubleClick */ + + +bool EventDispatcher::onDoDelete() +{ + if (_getEventsFrom != nullptr) + { + QPoint cursorPos = _getEventsFrom->mapFromGlobal( QCursor::pos() ); + callEventAction( &EventActionInterface::selectAction, cursorPos.x(), cursorPos.y() ); + bool ret = callEventAction( &EventActionInterface::deleteAction, cursorPos.x(), cursorPos.y() ); + if (ret) + { + EventActionInterface * eaIf = getEventActions(); + if (eaIf) eaIf->userActionFinished(); + } + return ret; + } + return false; +} /* EventDispatcher::onDoDelete */ + + +bool EventDispatcher::onDoUndo() +{ + bool ret = callEventAction( &EventActionInterface::undoAction, 0, 0 ); + if (ret) + { + EventActionInterface * eaIf = getEventActions(); + if (eaIf) eaIf->userActionFinished(); + } + return ret; +} /* EventDispatcher::onDoUndo */ + + +bool EventDispatcher::onDoEscape() +{ + EventActionInterface * eventActions = getEventActions(); + KickTheBucket(); + if (eventActions) eventActions->abortAction( this, 0, 0 ); + return true; +} /* EventDispatcher::onDoEscape */ + +bool EventDispatcher::onDoEnd() +{ + EventActionInterface * eventActions = getEventActions(); + KickTheBucket(); + if (eventActions) eventActions->applyAndEndAction( this, 0, 0 ); + return true; +} /* EventDispatcher::onDoEnd */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_dispatcher.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_dispatcher.h new file mode 100755 index 000000000..004b9e840 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_dispatcher.h @@ -0,0 +1,140 @@ +/** +** +** @RDE_HEADER@ +** Event Dispatcher for Qt5 to handle mouse and keyboard event +** $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 .........: +** +** 28.04.2003, JT : First edition for use with Qt4 in START Feature Editor fee +** 25.03.2015, JT : Refactoring for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + + +#include "abstract_event_dispatcher.h" + +class QGestureEvent; +class QWheelEvent; + +namespace SituationMap { namespace RB +{ + + /** + @class EventDispatcher event_dispatcher.h + @brief Event filter that handles events when a rubberband it active. + + Implements class \c AbstractEventDispatcher to manage event handling for rubberbands. Following events are handled: + + | Input-Event | Interface Function | Description | + | :--------------------------------------------- | : ----------------- | : ------------------------------------------------------------------------------------------------------ | + | Mouse move without any modifier | moveAction | Move Cursor or preview | + | Mouse move with pressed left button | movePickedAction | Move selected items. If movePickedAction == false redirect to \c EventMouseNavigator for panning of map. | + | Mouse move with pressed middle button | | Redirect to \c EventMouseNavigator for panning of map | + | Mouse wheel | | Redirect to \c EventMouseNavigator for zooming of map | + | Left mouse button press | selectAction | Do selection of single item or panning of map. | + | Left mouse button press with pressed SHIFT key | multiSelectAction | Do selection of multiple items. | + | Left mouse button release | setCoordinateAction | Set final coordinate to selected items and deselects item. | + | Double click left mouse button | completeStateAction | Finish input. | + | Right mouse button release | contextMenuAction | Show context menu. | + | Key release CTRL-Z | undoMenuAction | Undo last left mouse button click if possible. | + | Key release Delete | deleteMenuAction | Delete selected item. | + | Key release ESC | abortAction | Abort rubberband without saving data. The rubberband emits the abort signal. | + | Key release RETURN | applyAndEndAction | Finish rubberband. The rubberband emits the apply signal. | + | Key release UP | moveAction | Moves 1 pixel up. | + | Key release DOWN | moveAction | Moves 1 pixel down. | + | Key release LEFT | moveAction | Moves 1 pixel left. | + | Key release RIGHT | moveAction | Moves 1 pixel right. | + | Key release UP with SHIFT key pressed | moveAction | Moves 10 pixel up. | + | Key release DOWN with SHIFT key pressed | moveAction | Moves 10 pixel down. | + | Key release LEFT with SHIFT key pressed | moveAction | Moves 10 pixel left. | + | Key release RIGHT with SHIFT key pressed | moveAction | Moves 10 pixel right. | + + @see AbstractEventDispatcher, EventDelegater, EventMouseNavigator, + Rubberband, RubberbandPointFeature, RubberbandPointCluster, + RubberbandMultiPointFeature, RubberbandCorridor, RubberbandRectangle, RubberbandEllipse, + EventMouseNavigator + */ + + class EventDispatcher final : public AbstractEventDispatcher + { + friend class RubberbandBase; + + Q_OBJECT + public: + + /** + @name Construction & Destruction + @{ + */ + explicit EventDispatcher( QWidget * getEventsFrom ); + explicit EventDispatcher( QWidget * getEventsFrom, EventActionInterface* ); + + ~EventDispatcher() = default; + + EventDispatcher(const EventDispatcher &) = delete; + EventDispatcher & operator = (const EventDispatcher &) = delete; + /** @} */ + + inline void setSnapEnabled(bool) {} + inline bool SnapEnabled() { return _snapEnabled; } + + signals: + /** + Signal will be emitted if a mouse move is done (and if event is handled) + */ + void mouseMovedEvent(int windowPosX, int windowPosY); + + + protected: + + bool eventFilter(QObject*, QEvent*) override; + + bool eventFilterKey( int key, Qt::KeyboardModifiers mod ); + bool eventFilterGesture( QGestureEvent * gestureEvent ); + bool eventFilterWheel( QWheelEvent * ); + + private: + + bool onMove (int x, int y, Qt::MouseButtons buttons, Qt::KeyboardModifiers mod); //!< Handle a mouse move event. + + bool onPress ( int x, int y, Qt::KeyboardModifiers mod); //!< Handle left mouse button press event. + bool onClick ( int x, int y, Qt::KeyboardModifiers mod); //!< Handle left mouse button release event. + bool onMenuPress ( int x, int y, Qt::KeyboardModifiers mod); //!< Handle right mouse button press event. + bool onMenuClick ( int x, int y, Qt::KeyboardModifiers mod); //!< Handle right mouse button release event. + bool onDoubleClick( int x, int y, Qt::KeyboardModifiers mod); //!< Handle left mouse button double click event. + + bool onDoDelete(); //!< Handle del key press event. + + bool onDoUndo(); //!< Handle ctrl-c key press event. + bool onDoEscape(); //!< Handle esc key press event. + bool onDoEnd(); //!< Handle enter key press event. + + bool _snapEnabled; + uint _snapRatio; + bool _drawRatio; + bool _doubleClickHandled; + bool _leftPressHandled; + + }; /* EventDispatcher */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_mouse_navigator.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_mouse_navigator.cpp new file mode 100755 index 000000000..dcd2817d6 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_mouse_navigator.cpp @@ -0,0 +1,643 @@ +/** +** +** @RDE_HEADER@ +** Event Dispatcher for Qt5 to delegate mouse and keyboard event to another event dispatcher +** $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 .........: +** +** 15.04.2015, JT : First edition +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "event_mouse_navigator.h" +#include "rubberband.h" + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +namespace SituationMap { namespace RB +{ + +int EventMouseNavigator::_pickTolerance = 5; +qreal EventMouseNavigator::_zoomFactorBase = 1.002; + +EventMouseNavigator::EventMouseNavigator(QWidget * getEventsFrom, SituationMap::MapImpl *sitMap ) + : AbstractEventDispatcher(getEventsFrom) + , _sitMap(sitMap) + , _cursorChanged(false) + , _viewPortPos(QPointF(-1, -1)) + , _selectionMode(SelectionMode::NoSelection) + , _pendingRequest(RequestType::None) + , _mousePanningActive(false) + , _mousePanningActiveForCenterMapMidButton(false) + #ifdef SITUATIONMAP_OWN_WHEEL_HANDLING + , _mouseZoomTask() + #endif + , _pressedMouseButton(Qt::NoButton) +{ + // prepare identify: + switch (_sitMap->mode()) + { + case Mode::Mode_2D: + connect( _sitMap->getEsriMapGraphicsView() + , &Esri::ArcGISRuntime::MapGraphicsView::identifyGraphicsOverlaysCompleted + , this + , &EventMouseNavigator::onGetPickingResult ); + break; + case Mode::Mode_3D: + connect( _sitMap->getEsriSceneGraphicsView() + , &Esri::ArcGISRuntime::SceneGraphicsView::identifyGraphicsOverlaysCompleted + , this + , &EventMouseNavigator::onGetPickingResult ); + break; + default: + // empty + break; + } +} // EventMouseNavigator::EventMouseNavigator + + +bool EventMouseNavigator::eventFilter(QObject * watched, QEvent * e) +{ + Q_UNUSED(watched); + + QString debugString(QLatin1String("unhandled event")); + + if (hasFinished()) return true; // don't accept new events if dispatcher is going to finish + if (hasActiveEvent()) return true; // don't accept new events before last event has been handled completly + + if (e) + { + bool handled( false ); + + setActiveEvent( true ); + + switch( e->type() ) + { + case QEvent::Enter : + { + _pressedMouseButton = Qt::NoButton; + emit enterEvent(); + } break; + + case QEvent::Leave : + { + emit leaveEvent(); + _pressedMouseButton = Qt::NoButton; + } break; + + case QEvent::MouseButtonPress : + { + handled = false; + debugString = QLatin1String("mouse button press event"); + + QMouseEvent * mouseEvent = static_cast(e); + + _pressedMouseButton = mouseEvent->button(); + + _mousePanningActive = false; + _mouseMoveRefPos = mouseEvent->pos(); // for panning + + _restoreMouseCursor = getCursorFromWidget(); + + if (Qt::MidButton == mouseEvent->button()) + { + handled = true; + + _mousePanningActive = true; + _mousePanningActiveForCenterMapMidButton = false; + // setCursorToWidget( QCursor(Qt::CursorShape::ClosedHandCursor) ); + } + } break; + case QEvent::TouchBegin : + { + handled = false; + debugString = QLatin1String("Touch button press event"); + + QTouchEvent * mouseEvent = static_cast(e); + + _pressedMouseButton = Qt::MouseButton::LeftButton; + + _mousePanningActive = false; + if (mouseEvent->touchPoints().size() > 0) + { + const QTouchEvent::TouchPoint tp = mouseEvent->touchPoints().at(0); + + _mouseMoveRefPos = tp.pos().toPoint(); // for panning + _restoreMouseCursor = getCursorFromWidget(); + } + } break; + + case QEvent::MouseButtonRelease : + { + debugString = QLatin1String("mouse button release event"); + + QMouseEvent * mouseEvent = static_cast(e); + + + if (Qt::LeftButton == mouseEvent->button()) + { + if (_cursorChanged) + { + setCursorToWidget(_restoreMouseCursor); + _cursorChanged = false; + } + + /* + * The ESRI::MapView is doing panning on mouse move when left mouse button is pressed. + * To prevent selection in this case check mouse move distance. + */ + if (Qt::LeftButton == _pressedMouseButton && ((mouseEvent->pos() - _mouseMoveRefPos).manhattanLength() < 3) ) + { + if (Rubberband::activeRubberbands(_sitMap).isEmpty()) + { + handled = false; // pass event to esri map to click buttons + selectionRequested(mouseEvent->localPos()); + // do not handled = true; because some buttons in GraphicsView needs to get the event later. + } + } + else + handled = true; // may be a double click catched at higher event filter + } + + if (Qt::RightButton == mouseEvent->button()) + { + handled = true; + pickRequested( mouseEvent->localPos() ); + } + + if ((Qt::MidButton == mouseEvent->button())) + { + if (!_mousePanningActiveForCenterMapMidButton) + { + // press MidButton and Release MidButton without panning in the meantime: + // center map to current mouse position: + + // set viewpoint : (perform this only in 2D - MapGraphicsView <-> MapSceneView) + // (3D had different screenToLocation...) + Esri::ArcGISRuntime::MapGraphicsView *esriMapGraphicsView = _sitMap->getEsriMapGraphicsView(); + if (esriMapGraphicsView) + { + const QPoint &mousePos = mouseEvent->pos(); + const Esri::ArcGISRuntime::Point & mapCoordEsri = esriMapGraphicsView->screenToLocation(mousePos.x(), mousePos.y()); + const Esri::ArcGISRuntime::Viewpoint & curVp = esriMapGraphicsView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + const Esri::ArcGISRuntime::Viewpoint newVp(mapCoordEsri, curVp.targetScale()); + + esriMapGraphicsView->setViewpointAndWait(newVp); // sync / andWAIT + + _getEventsFrom -> update(); + } + } + } + + if ((_mousePanningActive) || (Qt::MidButton == mouseEvent->button())) + { + handled = (Qt::MidButton == mouseEvent->button()); + setCursorToWidget( _restoreMouseCursor ); + } + _mousePanningActive = false; + + } break; + + case QEvent::TouchEnd : + { + debugString = QLatin1String("Touch button release event"); + + QTouchEvent * mouseEvent = static_cast(e); + if (mouseEvent->touchPoints().size() > 0) + { + const QTouchEvent::TouchPoint tp = mouseEvent->touchPoints().at(0); + + if (_cursorChanged) + { + setCursorToWidget(_restoreMouseCursor); + _cursorChanged = false; + } + + /* + * The ESRI::MapView is doing panning on mouse move when left mouse button is pressed. + * To prevent selection in this case check mouse move distance. + */ + if (Qt::LeftButton == _pressedMouseButton && ((tp.pos() - _mouseMoveRefPos).manhattanLength() < 3) ) + { + if (Rubberband::activeRubberbands(_sitMap).isEmpty()) + { + handled = false; // pass event to esri map to click buttons + selectionRequested(tp.pos()); + // do not handled = true; because some buttons in GraphicsView needs to get the event later. + } + } + else + handled = true; // may be a double click catched at higher event filter + + } + _mousePanningActive = false; + + } break; + + case QEvent::MouseMove : + { + handled = false; + debugString = QLatin1String("mouse button move event"); + QMouseEvent *mouseEvent = static_cast(e); + + if (isSignalConnected(QMetaMethod::fromSignal(&EventMouseNavigator::mouseMovedEvent))) + { + emit mouseMovedEvent(mouseEvent->pos().x(), mouseEvent->pos().y()); + } + + if (_mousePanningActive) + { + handled = true; + + if (mouseEvent->buttons().testFlag(Qt::MidButton)) + { + _mousePanningActiveForCenterMapMidButton = true; + + // move viewpoint : (perform this only in 2D - MapGraphicsView <-> MapSceneView) + // (3D had different screenToLocation...) + Esri::ArcGISRuntime::MapGraphicsView *esriMapGraphicsView = _sitMap->getEsriMapGraphicsView(); + if (esriMapGraphicsView) + { + QPointF deltaMove = mouseEvent->pos()-_mouseMoveRefPos; + QPointF winCenter = QPointF( qreal(_getEventsFrom->width())*0.5, qreal(_getEventsFrom->height())*0.5 ); + QPointF newCenter = winCenter-deltaMove; + + _mouseMoveRefPos = mouseEvent->pos(); + + const Esri::ArcGISRuntime::Point & mapCoordEsri = esriMapGraphicsView->screenToLocation(newCenter.x(), newCenter.y()); + const Esri::ArcGISRuntime::Viewpoint & curVp = esriMapGraphicsView->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + const Esri::ArcGISRuntime::Viewpoint newVp(mapCoordEsri, curVp.targetScale()); + + esriMapGraphicsView->setViewpointAndWait(newVp); // sync / andWAIT + + _getEventsFrom -> update(); + + DEBUG_LOG(_logger) << LOGFUNCTION << " Panning to " << mapCoordEsri.x() << " - " << mapCoordEsri.y() + << " newCenter pos=" << newCenter + << " mouse pos=" << mouseEvent->pos() + << " delta pos=" << deltaMove; + } + } + else + { + setCursorToWidget( _restoreMouseCursor ); + _mousePanningActive = false; + } + // : move viewpoint + } + } break; + case QEvent::TouchUpdate : + { + handled = false; + debugString = QLatin1String("touch button move event"); + QTouchEvent *mouseEvent = static_cast(e); + + if (mouseEvent->touchPoints().size() > 0) + { + const QTouchEvent::TouchPoint tp = mouseEvent->touchPoints().at(0); + + if (isSignalConnected(QMetaMethod::fromSignal(&EventMouseNavigator::mouseMovedEvent))) + { + emit mouseMovedEvent(tp.pos().x(), tp.pos().y()); + } + + if (_mousePanningActive) + { + handled = true; + } + else + { + setCursorToWidget( _restoreMouseCursor ); + _mousePanningActive = false; + } + + } + } break; + +#ifdef SITUATIONMAP_OWN_WHEEL_HANDLING + case QEvent::Wheel : + { + debugString = QLatin1String("mouse wheel event"); + handled = true; + if (!_mouseZoomTask.isValid() || _mouseZoomTask.isFinished()) + { + + QWheelEvent * wheelEvent = static_cast(e); + qreal delta = wheelEvent->angleDelta().manhattanLength(); + bool zoomIn = (wheelEvent->angleDelta().x() < 0) || (wheelEvent->angleDelta().y() < 0); + qreal zoomFactor = qreal( qRound( qPow( _zoomFactorBase, (zoomIn) ? delta : -delta ) * 10 )) / 10.0; + QPointF winCenter = QPointF( qreal(_getEventsFrom->width())*0.5, qreal(_getEventsFrom->height())*0.5 ); + + const QPoint & cursorPosWidget = _getEventsFrom->mapFromGlobal( QCursor::pos() ); // e doesn't have a position get position from cursor here + + Esri::ArcGISRuntime::MapView * esriMapView = _sitMap->getEsriMapGraphicsView(); + const Esri::ArcGISRuntime::Point & mapCenter = esriMapView -> screenToLocation(winCenter.x(), winCenter.y()); + const Esri::ArcGISRuntime::Point & cursorPosMap = esriMapView -> screenToLocation(cursorPosWidget.x(), cursorPosWidget.y()); + double mapScale = esriMapView -> mapScale() * zoomFactor; + + double mapX = (-cursorPosMap.x()+mapCenter.x())*zoomFactor + cursorPosMap.x(); + double mapY = (-cursorPosMap.y()+mapCenter.y())*zoomFactor + cursorPosMap.y(); + + _mouseZoomTask = esriMapView->setViewpointCenterAsync( Esri::ArcGISRuntime::Point(mapX,mapY,esriMapView->map()->spatialReference()), mapScale ); + if (isSignalConnected( QMetaMethod::fromSignal(&EventMouseNavigator::mouseWheelEvent) )) + { + emit mouseWheelEvent(zoomFactor, QPointF(cursorPosMap.x(), cursorPosMap.y()) ); + } + + } + } break; +#endif + + case QEvent::Gesture: + { + QGestureEvent * gestureEvent = static_cast(e); + + if (gestureEvent) + { + const QList & gestureList = gestureEvent->gestures(); + for (auto gesture : gestureList) + { + Qt::GestureType gestureType = gesture->gestureType(); + Qt::GestureState gestureState = gesture->state(); + + // QTapGesture * tapGesture = nullptr; + // QTapAndHoldGesture * tapAndHoldGesture = nullptr; + QPanGesture * panGesture = nullptr; + QPinchGesture * pinchGesture = nullptr; + // QSwipeGesture * swipeGesture = nullptr; + QString typeName; + QString stateName; + + switch (gestureType) + { + // case Qt::TapGesture : tapGesture = (QTapGesture *)gesture; typeName = QLatin1String("TapGesture"); break; + // case Qt::TapAndHoldGesture : tapAndHoldGesture = (QTapAndHoldGesture*)gesture; typeName = QLatin1String("TapAndHoldGesture"); break; + case Qt::PanGesture : panGesture = (QPanGesture *)gesture; typeName = QLatin1String("PanGesture"); break; + case Qt::PinchGesture : pinchGesture = (QPinchGesture *)gesture; typeName = QLatin1String("PinchGesture"); break; + // case Qt::SwipeGesture : swipeGesture = (QSwipeGesture *)gesture; typeName = QLatin1String("SwipeGesture"); break; + + default: break; + } + switch (gestureState) + { + case Qt::NoGesture : stateName = QLatin1String("NoGesture"); break; + case Qt::GestureStarted : stateName = QLatin1String("GestureStarted"); break; + case Qt::GestureUpdated : stateName = QLatin1String("GestureUpdated"); break; + case Qt::GestureFinished : stateName = QLatin1String("GestureFinished"); break; + case Qt::GestureCanceled : stateName = QLatin1String("GestureCanceled"); break; + } + DEBUG_LOG(_logger) << LOGFUNCTION << " Gesture type=" << typeName << " Gesture state=" << stateName; + + // tapGesture = nullptr; + panGesture = nullptr; // ToDo: check: Why this? + + if (panGesture) + { + QPointF deltaMove = panGesture->delta(); + QPoint winCenter = QPoint( _getEventsFrom->width() / 2, _getEventsFrom->height() / 2 ); + QPoint newCenter = winCenter-QPoint((int)deltaMove.x(),(int)deltaMove.y()); + + const Esri::ArcGISRuntime::Point mapCoordEsri = + _sitMap->getEsriMapGraphicsView()->screenToLocation(newCenter.x(), newCenter.y()); + Esri::ArcGISRuntime::Viewpoint curVp = _sitMap->getEsriMapGraphicsView()->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale); + Esri::ArcGISRuntime::Viewpoint newVp(mapCoordEsri, curVp.targetScale()); + _sitMap->getEsriMapGraphicsView()->setViewpointAndWait(newVp); + + _getEventsFrom -> update(); + + if (gestureState == Qt::GestureStarted) gestureEvent -> setAccepted(panGesture, true); + + } + + if (pinchGesture) + { + ///// QPinchGesture::ChangeFlags changeFlags = pinchGesture->changeFlags(); + + + ///// if (changeFlags & QPinchGesture::RotationAngleChanged) + ///// { + ///// qreal deltaPhi = pinchGesture->rotationAngle() - pinchGesture->lastRotationAngle(); + + ///// _esriMap -> setRotation( _esriMap->rotation()-deltaPhi ); + ///// } + ///// if (changeFlags & QPinchGesture::ScaleFactorChanged) _esriMap -> zoom( 1.0/pinchGesture->scaleFactor() ); + + _getEventsFrom -> update(); + + if (gestureState == Qt::GestureStarted) gestureEvent -> setAccepted(pinchGesture, true); + } + } + } + } break; + + default: break; + + } // switch + + if (!hasFinished()) setActiveEvent( false ); + + if (handled) e->accept(); + DEBUG_LOG(_logger) << LOGFUNCTION + << " Event '" << debugString << "'" + << " event->type() '" << e->type() << "'" + << " handled = " << handled + << " mouse panning is active = " << _mousePanningActive; + + // + // 'if (handled) return true;' lead to bad behaviour at 3D: globe is often connected to mouse, without any button pressed... + // + // if (handled) return true; + + } + return AbstractEventDispatcher::eventFilter(watched,e); + +} // EventMouseNavigator::eventFilter + + +bool EventMouseNavigator::checkRunningPickingTask() +{ + if ((!_pickingTask.isCanceled()) && (_pendingRequest != RequestType::None)) + { + _pickingTask.cancel(); + _pendingRequest = RequestType::None; + return true; + } + return false; +} // void EventMouseNavigator::checkRunningPickingTask() + + +void EventMouseNavigator::selectionRequested( const QPointF & pos ) +{ + // do the work only in case of slot connections are installed + if (! isSignalConnected( QMetaMethod::fromSignal(&EventMouseNavigator::selectionRequest) )) + { + return; + } + + if (checkRunningPickingTask()) + { + qWarning() << LOGFUNCTION << " request is still pending!"; + } + + _pendingRequest = RequestType::Selection; + _viewPortPos = pos; + + requestPicking(pos); + +} // EventMouseNavigator::selectionRequested + + +void EventMouseNavigator::pickRequested( const QPointF &pos ) +{ + // do the work only in case of slot connections are installed + if (! isSignalConnected( QMetaMethod::fromSignal(&EventMouseNavigator::contextMenuRequest) )) + { + return; + } + + if (checkRunningPickingTask()) + { + qWarning() << LOGFUNCTION << " request is still pending!"; + } + + _pendingRequest = RequestType::Picking; + _viewPortPos = pos; + + requestPicking(pos); + +} // EventMouseNavigator::pickRequested + + +void EventMouseNavigator::requestPicking(const QPointF &pos) +{ + _pickingTask.cancel(); // cancel old task + + // store new task + bool returnPopUpsOnly = false; + int maximumResultsPerOverlay = std::numeric_limits::max(); // huge number; + _pickingTask = _sitMap->getEsriGeoView()->identifyGraphicsOverlays(pos.x(), pos.y(), _pickTolerance, + returnPopUpsOnly, maximumResultsPerOverlay); + //qWarning() << " XXXXXXX" << LOGFUNCTION; + + // check new task + if (!_pickingTask.isValid()) + { + _pendingRequest = RequestType::None; + qWarning() << LOGFUNCTION << " _identfyGraphicsOverlayTask invalid"; + } + +} // EventMouseNavigator::requestPicking + + +void EventMouseNavigator::onGetPickingResult(const QUuid &taskId, const QList &identifyResults) +{ + // qWarning() << " YYYYYYY" << LOGFUNCTION; + + if (_pickingTask.taskId() != taskId) + { + qDebug() << " taskId does not match"; + return; + } + + const QList & selectedIdList = mapToUniqueMapId(identifyResults); + + // qWarning() << " _pendingRequest="<< (int)_pendingRequest << " count= " << _selectedIdList.count(); + + switch (_pendingRequest) + { + case RequestType::Picking: + emit contextMenuRequest(selectedIdList, _viewPortPos); + break; + case RequestType::Selection: + emit selectionRequest(selectedIdList, _viewPortPos); + break; + case RequestType::None: + //emtpy + break; + } // switch + + // reset some 'flags' to enable a new request: + _pendingRequest = RequestType::None; + _viewPortPos = QPointF(-1, -1); +} // EventMouseNavigator::onGetPickingResult + + +QList EventMouseNavigator::mapToUniqueMapId(const QList &identifyResults) +{ + QList idList; + + for (auto iResult : identifyResults) + { + // qWarning() << " process result of layer: " << iResult->graphicsOverlay(); + + const QList &gResults = iResult->graphics(); + for (auto pickedGraphic : gResults) + { + // qWarning() << " process pickedGraphic: " << pickedGraphic; + if (pickedGraphic->attributes()) + { + // qWarning() << " process pickedGraphic cont: " << pickedGraphic->attributes()->containsAttribute(QLatin1String(SituationMap::UniqueMapIdAttribute)); + // qWarning() << " process pickedGraphic cont: " << pickedGraphic->attributes()->containsAttribute(QLatin1String(SituationMap::IsPickableAttribute)); + + bool ok; + SituationMap::UniqueMapId uniqueMapId = pickedGraphic->attributes()->attributeValue(QLatin1String(SituationMap::UniqueMapIdAttribute)).toUInt(&ok); + + if (ok) + { + // qWarning() << " process pickedGraphic uid: " << uniqueMapId; + if (!idList.contains(uniqueMapId)) + { + idList += uniqueMapId; + } + } + else + { + ERROR_LOG(_logger) << " process pickedGraphic " << pickedGraphic + << " uid: '" << pickedGraphic->attributes()->attributeValue(QLatin1String(SituationMap::UniqueMapIdAttribute)).toString() + << "' (has uid attribute is " << pickedGraphic->attributes()->containsAttribute(QLatin1String(SituationMap::UniqueMapIdAttribute)) << ")"; + } + } + } // foreach inner + } // foreach outter + + return idList; +} // EventMouseNavigator::mapToUniqueMapId + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_mouse_navigator.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_mouse_navigator.h new file mode 100755 index 000000000..974a9b8d3 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/event_mouse_navigator.h @@ -0,0 +1,224 @@ +/** +** +** @RDE_HEADER@ +** Event Dispatcher for Qt5 to handle mouse events navigate on map +** $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 .........: +** +** 24.04.2015, JT : first edition +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +// Own +#include +#include "abstract_event_dispatcher.h" + + +//Esri + + +// Qt +#include + + +// #define SITUATIONMAP_OWN_WHEEL_HANDLING + + + +// Forward: +namespace Esri { +namespace ArcGISRuntime { + class Map; + class IdentifyGraphicsOverlayResult; +} } + +namespace SituationMap { +class MapImpl; +} + + +namespace SituationMap { namespace RB +{ + + /** + @class EventMouseNavigator event_mouse_navigator.h + @brief Event filter that handles mouse events for general map operation like selecting object, zooming and paning the map window + + Implements class \c AbstractEventDispatcher to manage event handling for general map operation like selecting object, zooming and + panning the map window. Monitoring the mouse position can be realized by connecting the \c mouseMovedEvent signal. If the user has + clicked to map objects the signal \c selectionRequest is emitted. + + Following events are handled: + + | Input-Event | Interface Function | Description | + | :--------------------------------------------- | : ----------------- | : --------------------------------------------------------------------------- | + | Mouse enter | | To show/hide some widgets | + | Mouse leave | | To show/hide some widgets | + | Mouse move without any modifier | | Move Cursor. | + | Mouse move with pressed left button | | Panning of map if more then 3 pixels moved. | + | Mouse move with pressed middle button | | Panning of map. | + | Mouse wheel | | Redirect to \c Esri map for zooming. | + | Left mouse button release | | Emits signal \c selectionRequest with list of map objects at cursor position. | + + @see AbstractEventDispatcher, EventDelegater, EventDispatcher + */ + + class EventMouseNavigator : public AbstractEventDispatcher + { + + Q_OBJECT + + public: + + /** + @name Construction & Destruction + @{ + */ + EventMouseNavigator(const EventMouseNavigator &) = delete; + EventMouseNavigator & operator = (const EventMouseNavigator &) = delete; + + EventMouseNavigator( QWidget * getEventsFrom, SituationMap::MapImpl *sitMap ); + + virtual ~EventMouseNavigator() = default; + /** @} */ + + inline static void setPickTolerance( int value = 5 ) { _pickTolerance = value; } + + inline static int pickTolerance() { return _pickTolerance; } + + inline static void setZoomFactorBase(qreal value = 1.002) { if ((value > 1) && (value <= 1.1))_zoomFactorBase = value; } + + inline static qreal zoomFactorBase() { return _zoomFactorBase; } + + signals: + /** + Will be emitted if the mouse enters the widget. + */ + void enterEvent(); + + /** + Will be emitted if the mouse leaves the widget. + */ + void leaveEvent(); + + + /** + Will be emitted if the user clicks to map objects with the left mouse button. + */ + void selectionRequest( const QList< SituationMap::UniqueMapId > & idList, const QPointF &viewPortPos ); + + /** + Will be emitted if the user clicks to the map with the rigth mouse button. + */ + void contextMenuRequest( const QList< SituationMap::UniqueMapId > & idList, const QPointF &viewPortPos ); + + /** + Signal will be emitted if a mouse move is done. + */ + void mouseMovedEvent( int x, int y ); + +#ifdef SITUATIONMAP_OWN_WHEEL_HANDLING + /** + Signal will be emitted if the mouse wheel has been rotated + */ + void mouseWheelEvent(qreal factor, const QPointF &cursorPositionMap); +#endif + + protected slots: + + void onGetPickingResult(const QUuid &taskId, const QList<::Esri::ArcGISRuntime::IdentifyGraphicsOverlayResult *> &identifyResults ); + + protected: + + + QList mapToUniqueMapId( const QList<::Esri::ArcGISRuntime::IdentifyGraphicsOverlayResult*> & identifyResults); + + bool eventFilter(QObject*, QEvent*) override; + + + enum class RequestType + { + None, + Selection, + Picking + }; // RequestType + + + enum SelectionMode + { + NoSelection = 0x00, + SingleSelection = 0x01, + MultiSelection = 0x02, + FrameSelection = 0x04 + }; // SelectionMode + + Q_DECLARE_FLAGS(SelectionModes,SelectionMode) + + SituationMap::MapImpl *_sitMap; + + QPoint _mouseMoveRefPos; + QCursor _restoreMouseCursor; + bool _cursorChanged; + + static int _pickTolerance; + + QPointF _viewPortPos; + SelectionModes _selectionMode; + RequestType _pendingRequest; + bool _mousePanningActive; + bool _mousePanningActiveForCenterMapMidButton; + +#ifdef SITUATIONMAP_OWN_WHEEL_HANDLING + QFuture _mouseZoomTask; +#endif + + Qt::MouseButton _pressedMouseButton; + + static qreal _zoomFactorBase; // Base value to calculate zoom factor values from QWheelEvent + + QFuture _pickingTask; + + /** + test for already running picking task. + */ + bool checkRunningPickingTask(); + + /** + Find out which map objects are at given mouse position and emits the signal selectionRequest + */ + void selectionRequested(const QPointF & ); + + /** + Find out which map objects are at given mouse position and emits the signal contextMenuRequest + */ + void pickRequested( const QPointF & ); + + /** + Helper function to get all map objects at a given position of all GraphicsOverlays + @param pos position in window coordinates + */ + void requestPicking(const QPointF &pos); + + }; /* EventMouseNavigator */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/gestures.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/gestures.cpp new file mode 100755 index 000000000..99e8e2e74 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/gestures.cpp @@ -0,0 +1,106 @@ +/** + * @RDE_HEADER@ + * Declaration/Definition + * $Id$ + * $HeadURL$ + * $Author$ + */ + + +#include "gestures.h" + +#include +#include + +#include + +namespace SituationMap { namespace RB { + +// TabGestureRecognizer + +ApplyGestureRecognizer::ApplyGestureRecognizer() : QGestureRecognizer() +{ +} + +ApplyGestureRecognizer::~ApplyGestureRecognizer() +{} + + +QGesture * ApplyGestureRecognizer::create( QObject * target ) +{ + // qDebug() << "ApplyGesture create"; + + if (target && target->isWidgetType()) + { + ((QWidget*)target)->setAttribute(Qt::WA_AcceptTouchEvents); + } + return new ApplyGesture(); +} // TabGestureRecognizer::create + + +QGestureRecognizer::Result ApplyGestureRecognizer::recognize( QGesture * stateGesture, QObject* , QEvent * event) +{ + ApplyGesture * q = static_cast(stateGesture); + + QGestureRecognizer::Result result = QGestureRecognizer::CancelGesture; + + switch (event->type()) + { + case QEvent::TouchBegin: + { + const QTouchEvent * ev = static_cast(event); + // qDebug() << "ApplyGesture begin"; + q->_position = ev->touchPoints().at(0).pos(); + q->setHotSpot(ev->touchPoints().at(0).screenPos()); + q->_tripleTouch = (ev->touchPoints().size() == 3); + if (ev->touchPoints().size() == 1 ) + result = QGestureRecognizer::Ignore; + else + result = QGestureRecognizer::TriggerGesture; + } break; + + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + { + const QTouchEvent * ev = static_cast(event); + if (q->state() != Qt::NoGesture && ev->touchPoints().size() >= 1 && ev->touchPoints().size() <= 3) + { + if (ev->touchPoints().size() == 3) q->_tripleTouch = true; + // qDebug() << "ApplyGesture update _tripleTouch=" << q->_tripleTouch; + QTouchEvent::TouchPoint p = ev->touchPoints().at(0); + QPoint delta = p.pos().toPoint() - p.startPos().toPoint(); + enum { TapRadius = 40 }; + if (delta.manhattanLength() <= TapRadius) + { + if (event->type() == QEvent::TouchEnd) + { + result = (q->_tripleTouch) ? QGestureRecognizer::FinishGesture : QGestureRecognizer::CancelGesture; + } + else result = QGestureRecognizer::TriggerGesture; + } + } + } break; + + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: + result = QGestureRecognizer::Ignore; + break; + default: + result = QGestureRecognizer::Ignore; + break; + } + + return result; +} // TabGestureRecognizer::recognize( QGesture * stateGesture, QObject * watched, QEvent * event) + + +void ApplyGestureRecognizer::reset( QGesture * stateGesture ) +{ + ApplyGesture * q = static_cast(stateGesture); + q->_tripleTouch = false; +} // TabGestureRecognizer::reset( QGesture * stateGesture ) + + + +}} // namspaces diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/gestures.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/gestures.h new file mode 100755 index 000000000..9847c0bb5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/gestures.h @@ -0,0 +1,72 @@ +/** +** +** @RDE_HEADER@ +** Gesture recognition +** $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 .........: +** +** 09.12.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include +#include +#include + + +namespace SituationMap { namespace RB +{ + class ApplyGestureRecognizer : public QGestureRecognizer + { + public: + ApplyGestureRecognizer(); + virtual ~ApplyGestureRecognizer(); + + QGesture * create( QObject * target ) override; + QGestureRecognizer::Result recognize( QGesture * stateGesture, QObject * watched, QEvent * event) override; + void reset( QGesture * stateGesture ) override; + + }; // ApplyGestureRecognizer + + + class ApplyGesture : public QGesture + { + Q_OBJECT + friend class ApplyGestureRecognizer; + + public: + + ApplyGesture() = default; + ~ApplyGesture() = default; + + ApplyGesture( const ApplyGesture & ) = delete; + ApplyGesture & operator = ( const ApplyGesture & ) = delete; + + QPointF position() const { return _position; } + + private: + + QPointF _position; + bool _tripleTouch; + }; // ApplyGesture + +}} // namespaces diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/polygon.xpm b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/polygon.xpm new file mode 100755 index 000000000..d7b513ff4 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/polygon.xpm @@ -0,0 +1,52 @@ +/* XPM */ +static const char * polygon_xpm[] = { +"24 24 25 1", +" c None", +". c #59BA55", +"+ c #57B953", +"@ c #56B852", +"# c #55B651", +"$ c #53B44F", +"% c #51B24D", +"& c #4FB04B", +"* c #4DAE49", +"= c #4AAC46", +"- c #48A944", +"; c #46A742", +"> c #43A53F", +", c #41A23D", +"' c #3E9F3A", +") c #3C9D38", +"! c #399B35", +"~ c #379833", +"{ c #349630", +"] c #32942E", +"^ c #30922C", +"/ c #2E902A", +"( c #2D8E28", +"_ c #2B8C27", +": c #2A8B26", +" . ", +" ++ ", +" @ @ @ ", +" # # ### ", +" $$ $ $$$ $$ ", +" % %% %%% % ", +" && & &&& & ", +" * *** * ", +" == = ", +" - - ", +" ; ;", +" > >", +" , ,,", +"'' '' ", +") )) ", +"! !! ", +"~~ ~~ ", +" {{{ {{ ", +" ]]] ]] ", +" ^^ ^^ ", +" // // ", +" (( (( ", +" __ __ ", +" :::: "}; diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband.cpp new file mode 100755 index 000000000..f58d84ac2 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband.cpp @@ -0,0 +1,309 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of base class for 2D rubberbands +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband.h" +#include "rubberband_drawable.h" +#include "rubberband_handle.h" +#include "rubberband_handle_proxy.h" +#include "rubberband_point_handle.h" +#include "rubberband_internal.h" +#include "rubberband_base_internal.h" + +#include "rubberband_factory.h" + +#include + +// Qt +#include +#include + + +namespace SituationMap { namespace RB +{ + + + Rubberband::Rubberband( SituationMap::Map * map, QObject * parent, bool isActive ) + : RubberbandBase(map,parent,isActive) + , _internal( new Rubberband::InternalData(map) ) + , _state( RubberbandState::ConstructionState ) + , _previewSidc() + , _previewCursorOffset(32,32) + , _previewSize(32) + { + _fillColor.setAlpha(100); + + connect( &_previewSymbolResultWatcher + , &AdvancedSymbology::SymbolResultWatcher::resultReady + , this + , &Rubberband::onPreviewSymbolResultReady ); + + } /* Rubberband::Rubberband */ + + + Rubberband::~Rubberband() + { + delete _internal; + _internal = nullptr; + } /* Rubberband::~Rubberband */ + + + void Rubberband::connectPreviewPixmap( const QPixmap & symbolPixmap ) + { + if (nullptr == _internal->_connectedPreviewPixmap) + { + _internal->_connectedPreviewPixmap = new QGraphicsPixmapItem( symbolPixmap ); + _internal->_connectedPreviewPixmap -> setOffset(-qreal(symbolPixmap.width())/2,-qreal(symbolPixmap.height())/2); + + _internal->_mapScene -> addItem( _internal->_connectedPreviewPixmap ); + } + } /* RubberbandPointFeature::connectPreviewPixmap */ + + unsigned int Rubberband::coordinateCount() const { return static_cast(_internal->_handleList.size()); } + + + void Rubberband::resetAction(AbstractEventDispatcher*, int, int) + { + disenableSelectedState(); + disenableMultiSelectedState(); + update(); + } /* Rubberband::resetAction */ + + + + bool Rubberband::selectAction( AbstractEventDispatcher*, int x, int y ) + { + if (rubberbandIsAlive()) + { + QPointF scenePos = pixel2ScenePosition(x,y); + + if (_baseInternal->buttonProxyActive(scenePos)) return false; + _baseInternal->_lastClickPosition = scenePos; + return true; + } + return false; + } /* Rubberband::selectAction */ + + + bool Rubberband::multiSelectActionPress( AbstractEventDispatcher * causer, int x, int y ) + { + if (rubberbandIsAlive()) + { + if (isEnabledModificationState()) + { + _baseInternal->_lastClickPosition = pixel2ScenePosition(x,y); + enableMultiSelectedState(); + } + return selectAction( causer, x, y); + } + return false; + } /* RubberbandPointFeature::multiSelectActionPress */ + + + bool Rubberband::multiSelectActionRelease( AbstractEventDispatcher * causer, int x, int y ) + { + if (rubberbandIsAlive()) + { + if (isEnabledModificationState()) + { + updateButtonPosition(); + activateButtons(); + } + else return setCoordinateAction(causer,x,y); + } + return false; + } /* RubberbandPointFeature::multiSelectActionRelease */ + + + void Rubberband::adjustConnectedPreviewPixmap( int x, int y ) + { + if (_internal->_connectedPreviewPixmap) + { + _internal->_connectedPreviewPixmap -> setPos(pixel2ScenePosition( x+_previewCursorOffset.x(), y+_previewCursorOffset.y() )); + } + } /* Rubberband::adjustConnectedPreviewPixmap */ + + + bool Rubberband::moveAction( AbstractEventDispatcher*, int x, int y ) + { + adjustConnectedPreviewPixmap(x,y); + return rubberbandIsAlive(); + + } /* moveAction */ + + + bool Rubberband::setCoordinateAction( AbstractEventDispatcher*, int x, int y ) + { + if (rubberbandIsAlive()) + { + QPointF scenePos = pixel2ScenePosition(x,y); + if (_baseInternal->buttonProxyActive(scenePos)) return false; + _internal->_lastClickPosition = scenePos; + return true; + } + return false; + } /* Rubberband::setCoordinateAction */ + + + bool Rubberband::movePickedAction ( AbstractEventDispatcher*, int x, int y ) { adjustConnectedPreviewPixmap(x,y); return false; } + bool Rubberband::contextMenuAction ( AbstractEventDispatcher*, int x, int y ) { Q_UNUSED(x); Q_UNUSED(y); return rubberbandIsAlive(); } + bool Rubberband::completeStateAction ( AbstractEventDispatcher*, int x, int y ) { Q_UNUSED(x); Q_UNUSED(y); return rubberbandIsAlive(); } + bool Rubberband::deleteAction ( AbstractEventDispatcher*, int x, int y ) { Q_UNUSED(x); Q_UNUSED(y); return rubberbandIsAlive(); } + bool Rubberband::undoAction ( AbstractEventDispatcher*, int x, int y ) { Q_UNUSED(x); Q_UNUSED(y); return rubberbandIsAlive(); } + + + RubberbandHandle * Rubberband::pickHandle( int x, int y ) const + { + if (_internal->_mapView != nullptr) + { + QPointF scenePosition = pixel2ScenePosition(x,y); + for (auto handle : std::as_const(_internal->_handleList)) + { + if (handle->isPositionInside(scenePosition)) + return handle.get(); + } + } + return nullptr; + } /* Rubberband::pickHandle */ + + + void Rubberband::deSelectHandles() + { + for (auto handle : std::as_const(_internal->_handleList)) + { + RubberbandHandle * handlePtr = handle.get(); + if (handlePtr) + { + handlePtr->setSelection(false); + } + } + } /* Rubberband::deSelectHandles */ + + + QRectF Rubberband::sceneBbox() const + { + DEBUG_LOG(_internal->_logger) << LOGFUNCTION; + + QPointF topLeft( std::numeric_limits::max(), std::numeric_limits::max() ); + QPointF bottomRight( std::numeric_limits::min(), std::numeric_limits::min() ); + + for (auto handle : std::as_const(_internal->_handleList)) + { + RubberbandPointHandle * pointHandlePtr = dynamic_cast(handle.get()); + + if (pointHandlePtr) + { + const QPointF & p = pointHandlePtr->getCoordinate()._sceneCoordinate; + if (topLeft.x() > p.x()) topLeft.setX(p.x()); + if (topLeft.y() > p.y()) topLeft.setY(p.y()); + if (bottomRight.x() < p.x()) bottomRight.setX(p.x()); + if (bottomRight.y() < p.y()) bottomRight.setY(p.y()); + } + } + + return QRectF( topLeft, bottomRight ); + + } /* Rubberband::sceneBbox */ + + + void Rubberband::update() + { + // Check Quartz + // if (isSignalConnected(QMetaMethod::fromSignal(&Rubberband::updatePreview))) + // { + // emit updatePreview( this ); + // } + // if (_internal->_map->symbolDictionary().contains(_previewSidc)) + // { + // const QImage& symbolImage = _internal->_map->symbolDictionary().symbolImageBySidc( _previewSidc, _previewSize ); + // connectPreviewPixmap( QPixmap::fromImage(symbolImage) ); + // } + + if ((!_previewSidc.isEmpty()) && (nullptr == _internal->_connectedPreviewPixmap) && (!_previewSymbolResultWatcher.resultIsValid())) + { + _previewSymbolResultWatcher.setSymbolResult(_baseInternal->_map->symbolDictionary().findBaseSymbol(_previewSidc)); + } + + for (auto handle : std::as_const(_internal->_handleList)) + { + handle -> setDrawColor(_handleDrawColor); + handle -> setFillColor(_handleFillColor); + handle -> setSelectionColor(_handleSelectionColor); + handle -> setPickingRatio( _pickingRatio ); + } + + updateButtonPosition(); + + if (_internal->_constructionObjectDraw != nullptr) + { + // _constructionObjectDraw -> setColors + } + + externalUpdate(); + + } /* Rubberband::update */ + + + void Rubberband::onPreviewSymbolResultReady() + { + QSharedPointer symResult = _previewSymbolResultWatcher.symbolResult(); + + if (!symResult) return; // should NEVER happen! (Which object should have triggered the signal???) + + const QPixmap & symbolPixmap = QPixmap::fromImage(symResult->symbolImage().image()).scaled(_previewSize,_previewSize,Qt::KeepAspectRatio); + + if (symbolPixmap.isNull()) + { + DEBUG_LOG(_internal->_logger) << LOGFUNCTION << "Preview symbol image is empty."; + } + connectPreviewPixmap( symbolPixmap ); + } /* Rubberband::onPreviewSymbolResultReady */ + + + #include "./constrainsMarker.xpm" + + void Rubberband::markFailedConstrains( const QPointF & scenePos, bool on ) + { + if (on) + { + QPixmap markerPixmap(constrainsMarker_xpm); + + if (nullptr == _internal->_constrainFailedMarker) + { + _internal->_constrainFailedMarker = new QGraphicsPixmapItem( markerPixmap ); + _internal->_mapScene -> addItem( _internal->_constrainFailedMarker ); + } + _internal->_constrainFailedMarker -> setOffset(-qreal(markerPixmap.width())/2,-qreal(markerPixmap.height())/2); + _internal->_constrainFailedMarker -> setPos(scenePos-QPointF(_previewCursorOffset)); + _internal->_constrainFailedMarker->setVisible(true); + } + else if (_internal->_constrainFailedMarker) _internal->_constrainFailedMarker->setVisible(false); + } /* Rubberband::markFailedConstrains */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband.h new file mode 100755 index 000000000..ed09383e3 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband.h @@ -0,0 +1,319 @@ +/** +** +** @RDE_HEADER@ +** Definition of base class for 2D rubberbands +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + + +#include "rubberband_base.h" + +#include +#include + +#include +#include + +#include + + +namespace Esri { +namespace ArcGISRuntime { + class MapGraphicsView; + class Point; +} } + + + +namespace SituationMap +{ + +/** + Namespace for all rubberband stuff (is part of namespace SituationMap) +*/ +namespace RB +{ + class RubberbandDrawable; + class RubberbandHandle; + + /** + @class Rubberband rubberband.h + @brief Base class of all 2D rubberbands + + Rubberbands are used to create or modifiy 2D geometries like lines, circles, polygons etc. by processing + Qt input events like keyboard and mouse events. To create a rubberband the RubberbandFactory has to be used. + At end of rubberband processing the signal \c abort or\c apply are emitted. To delete the rubberband the + function deleteMySelf must be called. To use the delete operator is dangerous because at end of signal + processing some program code may be performed. + + Example to how to use rubberband: + + @code + + SituationMap::Map & map = ... + + const RubberbandFactory & rubberbandFactory = map.rubberbandFactory(); + + RubberbandPointFeature * rubberband = rubberbandFactory.create(); + if (rubberband != nullptr) + { + QObject::connect( rubberband, &SiMapRubBnd::Rubberband::apply, [=](SiMapRubBnd::Rubberband * rubberband ) + { + // rubberband has finished input + if (rubberband) + { + const QVector & vertexList = rubberband->coordinateVector(); + + // do something with vertexList + + rubberband->deleteMySelf(); // notice don't call here delete rubberband directly !!! + } + } ); + + QObject::connect( rubberband, &SiMapRubBnd::Rubberband::abort, this, [=](SiMapRubBnd::Rubberband * rubberband ) + { + // Rubberband has been aborted by user + if (rubberband) + { + rubberband->deleteMySelf(); // notice don't call here delete rubberband directly !!! + } + } ); + } + + @endcode + + Following keyboard commands are available in active rubberbands: + + | Key | Modifier | Funktion | + | :----: |:-------: | :--------------------------------------------------------------------------- | + | Esc | | Rubberband abbrechen | + | Return | | Rubberband abschließen und Eingabe übernehmen wenn möglich, sonst Abbruch | + | Left | | Selektierten Handle (Punkt,Frame) um 1 Pixel nach links schieben | + | Right | | Selektierten Handle (Punkt,Frame) um 1 Pixel nach rechts schieben | + | Up | | Selektierten Handle (Punkt,Frame) um 1 Pixel nach oben schieben | + | Down | | Selektierten Handle (Punkt,Frame) um 1 Pixel nach unten schieben | + | Left | Shift | Selektierten Handle (Punkt,Frame) um 10 Pixel nach links schieben | + | Right | Shift | Selektierten Handle (Punkt,Frame) um 10 Pixel nach rechts schieben | + | Up | Shift | Selektierten Handle (Punkt,Frame) um 10 Pixel nach oben schieben | + | Down | Shift | Selektierten Handle (Punkt,Frame) um 10 Pixel nach unten schieben | + + @see EventDispatcher + @see RubberbandBase, Rubberband, RubberbandPointFeature, RubberbandPointCluster, RubberbandMultiPointFeature, RubberbandRectangle, RubberbandEllipse, RubberbandCorridor, RubberbandArrow + */ + + class SITUATIONMAP_EXPORT Rubberband : public RubberbandBase + { + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Because class EventDispatcher::EventActionInterface is a pure virtual interface function the classic way of declaration would be + * + * SITUATIONMAP_EXPORT Rubberband : public QObject, public virtual EventDispatcher::EventActionInterface + * + * But with Visual Studio 2013 virtual inheritance combined with constructor delegation doesn't work and may crash the program. In + * this case the virtual inheritance of EventActionInterface isn't need because in the derived classes multiple inheritance will + * not be used. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + Q_OBJECT + + public: + + /** + @defgroup States Internal state of the rubberband + @{ + */ + + enum RubberbandState : unsigned int + { + ConstructionState = 1 << 0, ///< Construction state is active when a geomtrie is been build new + ModificationState = 1 << 1, ///< Modification state is active when a existing geometrie is been modified + SelectedState = 1 << 2, ///< Selection state is active, one ore more handles are selected + MultiSelectedState = 1 << 3, ///< multiple handles can be selected + ConstructionWidthState = 1 << 4 ///< Initial define of a width e.b. for corridors + }; /* RubberbandSate */ + + Q_DECLARE_FLAGS(RubberbandStates,RubberbandState) + + /** @} */ + + /** + @name Construction & Destruction + @{ + */ + + private: + + friend class RubberbandFreeHand; + friend class RubberbandPointFeature; + friend class RubberbandMultiPointFeature; + friend class RubberbandMultiEllipseFeature; + friend class RubberbandRectangle; + friend class RubberbandStateSelect; + friend class RubberbandTransformMove; + friend class RubberbandTransformRotate; + friend class RubberbandTransformScale; + + /** + Constructs a Rubberband for a situation map. Use \c RubberbandFactor to create a rubberband. + In case of the user actions the autoDelete mechanism can be used to delete the rubberband. Sometimes + the application wants to finish a running rubberband. This can be done by calling \c deleteMySelf. The + rubberband will be destroyed also automatically if the parent QObject or the map (see + SituationMap::Map::beginDestroy signal) will be destroyed. + + @param map Pointer to SituationMap::Map + @param parent Pointer to QObject to use as parent. + @isActive If set to false the Rubberband is not active and have to be actived to start the input. + @see RubberbandFactory + */ + explicit Rubberband( SituationMap::Map * map, QObject * parent, bool isActive = true ); + + public: + Q_DISABLE_COPY(Rubberband) + + virtual ~Rubberband(); + + /** @} */ + + /** + @addtogroup States Methodes to query and modify rubberband states + @{ + */ + inline bool isEnabledConstructionState() const { return _state.testFlag(RubberbandState::ConstructionState); } + inline bool isEnabledModificationState() const { return _state.testFlag(RubberbandState::ModificationState); } + inline bool isEnabledSelectedState() const { return _state.testFlag(RubberbandState::SelectedState); } + inline bool isEnabledMultiSelectedState() const { return _state.testFlag(RubberbandState::MultiSelectedState); } + + inline void enableConstructionState() { disenableModificationState(); } + inline void enableModificationState() { disenableConstructionState(); } + inline void enableSelectedState() { _state |= RubberbandState::SelectedState; } + inline void enableMultiSelectedState() { _state |= RubberbandState::MultiSelectedState; } + + inline void disenableConstructionState() { _state &= ~QFlags(RubberbandState::ConstructionState); _state |= RubberbandState::ModificationState; } + inline void disenableModificationState() { _state &= ~QFlags(RubberbandState::ModificationState); _state |= RubberbandState::ConstructionState; } + inline void disenableSelectedState() { _state &= ~QFlags(RubberbandState::SelectedState); } + inline void disenableMultiSelectedState() { _state &= ~QFlags(RubberbandState::MultiSelectedState); } + //@} + + /** + @name EventHandlerInterface Methodes of AbstractEventDispatcher::EventActionInterface + The interface between EventDispatcher and Rubberband are defined in class AbstractEventDispatcher. + To Implement the interface these methodes have to be override again in the derived classes of the Rubberband class. */ + /** + @{ + */ + bool selectAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool multiSelectActionPress ( AbstractEventDispatcher * causer, int x, int y ) override; + bool multiSelectActionRelease( AbstractEventDispatcher * causer, int x, int y ) override; + bool setCoordinateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool moveAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool movePickedAction ( AbstractEventDispatcher * causer, int x, int y ) override; + void resetAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool contextMenuAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool deleteAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool undoAction ( AbstractEventDispatcher * causer, int x, int y ) override; + + + /** @} */ + + /** + @name Preview Setting Methodes to define visual behaviour of rubberband preview + @{ + */ + inline void setPreviewSidc (const QString & sidc ) { _previewSidc = sidc; update(); } ///< Defines tactical symbol for preview + inline void setPreviewOffset (const QPoint & off ) { _previewCursorOffset = off; update(); } ///< set offset to cursor position for preview pixmap + inline void setPreviewSize (unsigned int size ) { _previewSize = size; update(); } ///< set size of preview pixmap + + inline QString getPreviewSidc() const { return _previewSidc; } ///< get code of tactical preview symbol + inline const QPoint getPreviewOffset() const { return _previewCursorOffset; } ///< get offset to cursor position for preview pixmap + inline unsigned int getPreviewSize() const { return _previewSize; } ///< get size of preview pixmap + /** @} */ + + + /** + Returns Sidc code of preview symbol (if defined) + */ + inline QString previewSidc() const { return _previewSidc; } + + /** + Sets a QPixmap to use for drawing symbol preview image + @param symbolImage a nice QPixmap + */ + void connectPreviewPixmap( const QPixmap & symbolPixmap ); + + /** + Returns count of coordinates + */ + virtual unsigned int coordinateCount() const override; + + protected: + + void deSelectHandles(); + + + void update() override; + + /** + Returns the rubberband handle at screen position (x,y) or nullptr if there isn't any handle at this + position. + */ + RubberbandHandle * pickHandle( int x, int y ) const; + + /** + Moves preview image to QGraphicView position (x,y) in window coordinates. + */ + void adjustConnectedPreviewPixmap( int x, int y ); + + + /** + Shows a icon near the cursor to mark failing constrains. + */ + void markFailedConstrains( const QPointF & scenePos, bool on ); + + /* + Forward declaration of internal container class for member variables. The derived classes of Rubberband + often have also a declaration of InternalData for there own internal members. + */ + struct InternalData; + + InternalData * _internal; + + RubberbandStates _state; + + QString _previewSidc; + QPoint _previewCursorOffset; + unsigned int _previewSize; + + AdvancedSymbology::SymbolResultWatcher _previewSymbolResultWatcher; + + QRectF sceneBbox() const override; + + protected slots: + + void onPreviewSymbolResultReady(); + + }; /* Rubberband */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_3d.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_3d.cpp new file mode 100755 index 000000000..d5c806669 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_3d.cpp @@ -0,0 +1,116 @@ +/** +** +** @RDE_HEADER@ +** Implementation of base class for 3d rubberbands +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + + +#include "rubberband_3d.h" + +#include + +#include + +namespace SituationMap +{ + + +namespace RB +{ + + Rubberband3D::Rubberband3D( SituationMap::Map * map, QObject * parent, bool isActive ) + : RubberbandBase(map,parent,isActive), _markerPixmap(nullptr), _markerSelectionPixmap(nullptr) + { + } // Rubberband3D::Rubberband3D( SituationMap::Map * map, QObject * parent, bool isActive ) + + + Rubberband3D::~Rubberband3D() + { + delete _markerPixmap; + delete _markerSelectionPixmap; + } // Rubberband3D::~Rubberband3D() + + + void Rubberband3D::resetAction( AbstractEventDispatcher*,int,int ) {;} + bool Rubberband3D::selectAction( AbstractEventDispatcher*,int,int ) { return false; } + bool Rubberband3D::multiSelectActionPress ( AbstractEventDispatcher*,int,int ) { return false; } + bool Rubberband3D::multiSelectActionRelease( AbstractEventDispatcher*,int,int ) { return false; } + bool Rubberband3D::setCoordinateAction( AbstractEventDispatcher*,int,int ) { return false; } + bool Rubberband3D::moveAction( AbstractEventDispatcher*,int,int ) { return false; } + bool Rubberband3D::movePickedAction( AbstractEventDispatcher*,int,int ) { return false; } + bool Rubberband3D::contextMenuAction( AbstractEventDispatcher*,int,int ) { return false; } + bool Rubberband3D::completeStateAction( AbstractEventDispatcher*,int,int ) { return false; } + bool Rubberband3D::deleteAction( AbstractEventDispatcher*,int,int ) { return false; } + bool Rubberband3D::undoAction( AbstractEventDispatcher*,int,int ) { return false; } + + + QRectF Rubberband3D::sceneBbox() const { return QRectF(); } + + + #include "sphere_pattern.xpm" + + QImage Rubberband3D::markerSelectionImage() + { + if (nullptr == _markerSelectionPixmap) + { + _markerSelectionPixmap = new QPixmap( sphere_pattern_xpm ); + + QPainter painter(_markerSelectionPixmap); + QBrush brush; + + brush.setStyle(Qt::BrushStyle::SolidPattern); + brush.setColor(_handleSelectionColor); + + painter.setCompositionMode(QPainter::CompositionMode::CompositionMode_Multiply); + painter.setPen(_handleDrawColor); + painter.setBrush(brush); + painter.drawEllipse( 0, 0, _markerSelectionPixmap->width(), _markerSelectionPixmap->height() ); + } + return _markerSelectionPixmap->toImage(); + } // QImage Rubberband3D::markerSelectionImage() + + + QImage Rubberband3D::markerImage() + { + if (nullptr == _markerPixmap) + { + _markerPixmap = new QPixmap( sphere_pattern_xpm ); + + QPainter painter(_markerPixmap); + QBrush brush; + + brush.setStyle(Qt::BrushStyle::SolidPattern); + brush.setColor(_handleFillColor); + + painter.setCompositionMode(QPainter::CompositionMode::CompositionMode_Multiply); + painter.setPen(_handleDrawColor); + painter.setBrush(brush); + painter.drawEllipse( 0, 0, _markerPixmap->width(), _markerPixmap->height() ); + } + return _markerPixmap->toImage(); + } // QImage Rubberband3D::markerImage() + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_3d.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_3d.h new file mode 100755 index 000000000..0fac8bfd2 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_3d.h @@ -0,0 +1,214 @@ +/** +** +** @RDE_HEADER@ +** Definition of base class for 3d rubberbands +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + + +#include "rubberband_base.h" + +#include +#include + + +class QObject; +namespace Esri { +namespace ArcGISRuntime { + class MapGraphicsView; + class Point; +} } + + + +namespace SituationMap +{ + +/** + Namespace for all rubberband stuff (is part of namespace SituationMap) +*/ +namespace RB +{ + class RubberbandDrawable; + class RubberbandHandle; + + /** + @class Rubberband3D rubberband_3d.h + @brief Base class of all 3D rubberbands + + Rubberbands are used to create or modifiy geometries like single points or lines in 3D views by processing + Qt input events like keyboard and mouse events. To create a rubberband the RubberbandFactory has to be used. + At end of rubberband processing the signal \c abort or\c apply are emitted. To delete the rubberband the + function deleteMySelf must be called. To use the delete operator is dangerous because at end of signal + processing some program code may be performed. + + Example to how to use rubberband: + + @code + + SituationMap::Map & map = ... + + const RubberbandFactory & rubberbandFactory = map.rubberbandFactory(); + + RubberbandPointFeature * rubberband = rubberbandFactory.create(); // the map has to be in 3D mode + if (rubberband != nullptr) + { + QObject::connect( rubberband, &SiMapRubBnd::Rubberband::apply, [=](SiMapRubBnd::Rubberband * rubberband ) + { + // rubberband has finished input + if (rubberband) + { + const QVector & vertexList = rubberband->coordinateVector(); + + // do something with vertexList + + rubberband->deleteMySelf(); // notice don't call here delete rubberband directly !!! + } + } ); + + QObject::connect( rubberband, &SiMapRubBnd::Rubberband::abort, this, [=](SiMapRubBnd::Rubberband * rubberband ) + { + // Rubberband has been aborted by user + if (rubberband) + { + rubberband->deleteMySelf(); // notice don't call here delete rubberband directly !!! + } + } ); + } + + @endcode + + Following keyboard commands are available in active rubberbands: + + | Key | Modifier | Funktion | + | :----: |:-------: | :--------------------------------------------------------------------------- | + | Esc | | Rubberband abbrechen | + | Return | | Rubberband abschließen und Eingabe übernehmen wenn möglich, sonst Abbruch | + | Left | | Selektierten Handle (Punkt,Frame) um 1 Pixel nach links schieben | + | Right | | Selektierten Handle (Punkt,Frame) um 1 Pixel nach rechts schieben | + | Up | | Selektierten Handle (Punkt,Frame) um 1 Pixel nach oben schieben | + | Down | | Selektierten Handle (Punkt,Frame) um 1 Pixel nach unten schieben | + | Left | Shift | Selektierten Handle (Punkt,Frame) um 10 Pixel nach links schieben | + | Right | Shift | Selektierten Handle (Punkt,Frame) um 10 Pixel nach rechts schieben | + | Up | Shift | Selektierten Handle (Punkt,Frame) um 10 Pixel nach oben schieben | + | Down | Shift | Selektierten Handle (Punkt,Frame) um 10 Pixel nach unten schieben | + + @see EventDispatcher + @see RubberbandBase, Rubberband, RubberbandPointFeature3D, RubberbandMultiPointFeature3D + */ + + class SITUATIONMAP_EXPORT Rubberband3D : public RubberbandBase + { + Q_OBJECT + + public: + + /** + @name Construction & Destruction + @{ + */ + + private: + + friend class RubberbandPointFeature3D; + friend class RubberbandMultiPointFeature3D; + + + /** + Constructs a Rubberband for a situation map. Use \c RubberbandFactor to create a rubberband. + In case of the user actions the autoDelete mechanism can be used to delete the rubberband. Sometimes + the application wants to finish a running rubberband. This can be done by calling \c deleteMySelf. The + rubberband will be destroyed also automatically if the parent QObject or the map (see + SituationMap::Map::beginDestroy signal) will be destroyed. + + @param map Pointer to SituationMap::Map + @param parent Pointer to QObject to use as parent. + @isActive If set to false the Rubberband is not active and have to be actived to start the input. + @see RubberbandFactory + */ + explicit Rubberband3D( SituationMap::Map * map, QObject * parent, bool isActive = true ); + + public: + + ~Rubberband3D(); + + /** @} */ + + /** + @name EventHandlerInterface Methodes of AbstractEventDispatcher::EventActionInterface + The interface between EventDispatcher and Rubberband are defined in class AbstractEventDispatcher. + To Implement the interface these methodes have to be override again in the derived classes of the Rubberband class. */ + /** + @{ + */ + + bool selectAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool multiSelectActionPress ( AbstractEventDispatcher * causer, int x, int y ) override; + bool multiSelectActionRelease( AbstractEventDispatcher * causer, int x, int y ) override; + bool setCoordinateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool moveAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool movePickedAction ( AbstractEventDispatcher * causer, int x, int y ) override; + void resetAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool contextMenuAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool deleteAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool undoAction ( AbstractEventDispatcher * causer, int x, int y ) override; + + + /** @} */ + + + /** + Retrieve and set 3D coordinates. + @{ + */ + virtual Point3D singleCoordinate3D( unsigned int idx = 0, CoordinateSystem cs = CoordinateSystem::Geo ) const = 0; + + virtual QVector coordinateVector3D( CoordinateSystem cs = CoordinateSystem::Geo ) const = 0; + + virtual void setCoordinateVector( const QVector & coordinateVector, CoordinateSystem cs = CoordinateSystem::Geo ) = 0; + /** @} */ + + + + + protected: + + QRectF sceneBbox() const override; + + QImage markerImage(); + QImage markerSelectionImage(); + + + + private: + + QPixmap * _markerPixmap; + QPixmap * _markerSelectionPixmap; + + }; /* Rubberband3D */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_arrow.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_arrow.cpp new file mode 100755 index 000000000..2e789d6ec --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_arrow.cpp @@ -0,0 +1,359 @@ + +/** +** @RDE_HEADER@ +** Implementation of rubberband class to define multi point feature geoemtry with arrow +** $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 .........: +** +** 21.08.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_arrow.h" +#include "rubberband_frame_handle.h" +#include "rubberband_line_handle.h" +#include "rubberband_point_handle.h" +#include "rubberband_drawable.h" + +#include "drawable_painter_path.h" + +#include "rubberband_internal.h" +#include "rubberband_base_internal.h" + +namespace SituationMap { namespace RB +{ + + struct RubberbandArrow::InternalData + { + SituationMap::RefPointer _arrowHandle; + SituationMap::RefPointer _arrowItem; + + }; /* struct RubberbandArrow::InternalData */ + + + RubberbandArrow::RubberbandArrow( SituationMap::Map * map, QObject * parent ) + : RubberbandMultiPointFeature(map,parent) + , _internal(new InternalData) + , _arrowSceneHalfWidth(0) + , _arrowSceneLength(0) + + { + _isClosed = false; + } /* RubberbandArrow::RubberbandArrow */ + + + RubberbandArrow::~RubberbandArrow() + { + delete _internal; + _internal = nullptr; + } /* RubberbandArrow::~RubberbandArrow */ + + + unsigned int RubberbandArrow::arrowHalfWidth() const { return _arrowSceneHalfWidth; } + unsigned int RubberbandArrow::arrowLength() const { return _arrowSceneLength; } + + + void RubberbandArrow::setCoordinateVector(const QVector & coordinateVec, CoordinateSystem cs ) + { + setCoordinateVectorArrow( coordinateVec, 0.0, 0.0, cs ); + } /* RubberbandArrow::coordinateVector */ + + + void RubberbandArrow::setCoordinateVectorArrow(const QVector & coordinateVec, unsigned int arrowHalfWidth, unsigned int arrowLength, CoordinateSystem cs ) + { + if (coordinateVec.size() < 2) return; + + RubberbandMultiPointFeature::setCoordinateVector( coordinateVec, cs ); + + const QPointF & aux = pixel2ScenePosition(arrowHalfWidth,arrowLength); + + _arrowSceneHalfWidth = (unsigned int) aux.x(); + _arrowSceneLength = (unsigned int) aux.y(); + + enableModificationState(); + constructArrowHandle(); + + _baseInternal->_applyIsPossible = true; + + activateButtons(); + } /* RubberbandArrow::setCoordinateVectorArrow */ + + + QPointF RubberbandArrow::getArrowHandleScenePosition() + { + if (!_arrowSceneLength) _arrowSceneLength = 40; + if (!_arrowSceneHalfWidth) _arrowSceneHalfWidth = 20; + + + QPointF p0 = static_cast(Rubberband::_internal->_handleList.end()[-2].get())->getCoordinate()._sceneCoordinate; + QPointF p1 = static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._sceneCoordinate; + + QPointF rv( p0.x()-p1.x(), p0.y()-p1.y() ); + rv /= sqrt(rv.x()*rv.x()+rv.y()*rv.y()); + + return QPointF( p1.x() + rv.x()*_arrowSceneLength - rv.y()*_arrowSceneHalfWidth , + p1.y() + rv.y()*_arrowSceneLength + rv.x()*_arrowSceneHalfWidth ); + + } /* RubberbandArrow::getArrowHandleScenePosition */ + + + QPointF RubberbandArrow::getArrowHandlePosition() + { + const QPointF & handlePos = getArrowHandleScenePosition(); + RubberbandDrawable::RubberbandCoordinate mapHandlePos(Rubberband::_internal->_mapView, handlePos); + + return QPointF( mapHandlePos._mapCoordinate.x(), mapHandlePos._mapCoordinate.y() ); + + } /* RubberbandArrow::getArrowhHandlePosition */ + + + void RubberbandArrow::constructArrowHandle() + { + if (Rubberband::_internal->_handleList.size() < 2) return; + + const QPointF & p = getArrowHandlePosition(); + + _internal->_arrowHandle = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, p ); + + // ensure thate the arrow the arrow size in pixel remain the same size + connect( _internal->_arrowHandle.get(), &RubberbandHandle::adjusted, this, [this]() { + this->_internal->_arrowHandle->updateCoordinate( this->getArrowHandleScenePosition() ); + this->updateArrowPath(); + } ); + update(); + + } /* RubberbandArrow::constructArrowhHandle */ + + + void RubberbandArrow::constructArrow( const QPointF & p ) + { + qreal p0X = static_cast(Rubberband::_internal->_handleList.end()[-2].get())->getCoordinate()._sceneCoordinate.x(); + qreal p0Y = static_cast(Rubberband::_internal->_handleList.end()[-2].get())->getCoordinate()._sceneCoordinate.y(); + qreal p1X = static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._sceneCoordinate.x(); + qreal p1Y = static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._sceneCoordinate.y(); + + QPointF rv10( p0X-p1X , p0Y-p1Y ); + QPointF rv1p( p.x()-p1X, p.y()-p1Y ); + + rv10 /= sqrt(rv10.x()*rv10.x()+rv10.y()*rv10.y()); + + qreal scalarProduct = QPointF::dotProduct(rv1p, rv10); + if (scalarProduct > 0.0) + { + _internal->_arrowHandle->getCoordinate().updateSceneCoordinate( Rubberband::_internal->_mapView, p ); + + _arrowSceneLength = (unsigned int) scalarProduct; + _arrowSceneHalfWidth = (unsigned int) sqrt( rv1p.x()*rv1p.x()+rv1p.y()*rv1p.y() - scalarProduct*scalarProduct ); + } + } /* RubberbandArrow::constructArrow */ + + + void RubberbandArrow::updateHandleScenePosition() + { + if (!_internal->_arrowHandle) return; + + QPointF pos = getArrowHandleScenePosition(); + + _internal->_arrowHandle->updateCoordinate( pos ); + + } /* RubberbandArrow::updateHandleScenePosition */ + + + void RubberbandArrow::updateArrowPath() + { + if (!_internal->_arrowHandle) return; + + if (Rubberband::_internal->_handleList.size() < 2) return; + + const QPointF & p0 = static_cast(Rubberband::_internal->_handleList.end()[-2].get())->getCoordinate()._sceneCoordinate; + const QPointF & p1 = static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._sceneCoordinate; + const QPointF & p = _internal->_arrowHandle->getCoordinate()._sceneCoordinate; + + QPointF rv( p0.x()-p1.x(), p0.y()-p1.y() ); + rv /= sqrt(rv.x()*rv.x()+rv.y()*rv.y()); + + if (!_internal->_arrowItem) + { + _internal->_arrowItem = new DrawablePainterPath( Rubberband::_internal->_mapView ); + _internal->_arrowItem -> enableClosedLine( true ); + _internal->_arrowItem -> fillSolid( true ); + _internal->_arrowItem -> zBufferPlane( RubberbandDrawable::ZBufferPlane::ArrowSymbol ); + } + + QVector arrowPath; + + arrowPath.append( p1 ); + arrowPath.append( p ); + arrowPath.append( QPointF( p1.x() + rv.x()*_arrowSceneLength + rv.y()*_arrowSceneHalfWidth + , p1.y() + rv.y()*_arrowSceneLength - rv.x()*_arrowSceneHalfWidth ) ); + + _internal->_arrowItem -> setPath( arrowPath ); + + RubberbandFrameHandle * frameHandle = getFrameHandle(); + + if (frameHandle) + { + QVector vertexSet; + + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * pointHandlePtr = dynamic_cast(handle.get()); + + vertexSet.append(pointHandlePtr->getCoordinate() ); + } + vertexSet.append( _internal->_arrowHandle->getCoordinate() ); + vertexSet.append( RubberbandDrawable::RubberbandCoordinate(Rubberband::_internal->_mapView,p) ); + + frameHandle->updateFrameShape( vertexSet ); + } + } /* RubberbandArrow::updateArrowPath */ + + + bool RubberbandArrow::selectAction( AbstractEventDispatcher * causer, int x, int y ) + { + Q_UNUSED(causer); + + if (!rubberbandIsAlive()) return false; + + bool eventHandled = false; + QPointF scenePos(pixel2ScenePosition(x,y)); + + if (_baseInternal->buttonProxyActive(scenePos)) return false; + + _baseInternal->_lastClickPosition = scenePos; + + if (isEnabledConstructionState()) return RubberbandMultiPointFeature::selectAction(causer,x,y); + if (_internal->_arrowHandle->isPositionInside(scenePos)) + { + _internal->_arrowHandle->setSelection(); + eventHandled = true; + } + else eventHandled = selectHandles( x, y, scenePos ); + + if (eventHandled) + { + update(); + deactivateButtons(); + } + return eventHandled; + } /* RubberbandArrow::selectAction */ + + + bool RubberbandArrow::setCoordinateAction(AbstractEventDispatcher * causer, int x, int y) + { + Q_UNUSED(causer); + + if (!rubberbandIsAlive()) return false; + + bool eventHandled = false; + QPointF scenePos(pixel2ScenePosition(x,y)); + + Rubberband::_internal->_lastClickPosition = scenePos; + + if (isEnabledConstructionState()) return RubberbandMultiPointFeature::setCoordinateAction(causer, x, y); + + if (_internal->_arrowHandle->isSelected()) + { + _internal->_arrowHandle->setSelection(false); + constructArrow(scenePos); + eventHandled = true; + } + else return RubberbandMultiPointFeature::setCoordinateAction(causer, x, y); + + if (eventHandled) update(); + + updateButtonPosition(); + + activateButtons(); + + return eventHandled; + } /* RubberbandArrow::setCoordinateAction */ + + + bool RubberbandArrow::movePickedAction(AbstractEventDispatcher * causer, int x, int y ) + { + if (!rubberbandIsAlive()) return false; + + if ((_internal->_arrowHandle) && (_internal->_arrowHandle->isSelected())) + { + QPointF scenePos(pixel2ScenePosition(x,y)); + constructArrow( scenePos ); + update(); + return false; + } + + return RubberbandMultiPointFeature::movePickedAction(causer,x,y); + + } /* RubberbandArrow::movePickedAction */ + + + bool RubberbandArrow::moveAction(AbstractEventDispatcher * causer, int x, int y) + { + QPointF scenePos(pixel2ScenePosition(x,y)); + + if (_internal->_arrowHandle) + { + if (_internal->_arrowHandle->isPositionInside(scenePos)) + { + _internal->_arrowHandle->setSelection(true); + return false; + } + _internal->_arrowHandle->setSelection(false); + } + return RubberbandMultiPointFeature::moveAction(causer,x,y); + } /* RubberbandArrow::moveAction */ + + + bool RubberbandArrow::completeStateAction(AbstractEventDispatcher * causer, int x, int y) + { + if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) + { + if (isEnabledConstructionState()) + { + if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount) + { + constructArrowHandle(); + } + update(); + } + } + return RubberbandMultiPointFeature::completeStateAction(causer,x,y);; + } /* RubberbandArrow::completeStateAction */ + + + void RubberbandArrow::update() + { + _isClosed = false; + RubberbandMultiPointFeature::update(); + + if (_internal->_arrowHandle) + { + _internal->_arrowHandle -> setDrawColor(_handleDrawColor ); + _internal->_arrowHandle -> setFillColor(_handleFillColor ); + _internal->_arrowHandle -> setSelectionColor(_handleSelectionColor ); + _internal->_arrowHandle -> setPickingRatio( _pickingRatio ); + + updateHandleScenePosition(); + updateArrowPath(); + } + } /* RubberbandArrow::update */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_arrow.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_arrow.h new file mode 100755 index 000000000..ca63b054f --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_arrow.h @@ -0,0 +1,130 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define multi point feature geoemtry with Arrow +** $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 .........: +** +** 21.08.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband_multi_point_feature.h" + + +namespace SituationMap { namespace RB +{ + + class RubberbandLineHandle; + class RubberbandFrameHandle; + + /** + @class RubberbandArrow rubberband_arrow.h + @brief Implementation of a rubberband for lines with arrow. + @see RubberbandFactory, Rubberband, RubberbandMultiPointFeature + + Implementation of rubberbands used to input corridors. There are two different modes \c InputCenterLine and \c InputBoundary. In the + InputCenterLine mode the center line of the corridor has to be defined. The rubberband displays then the border geometry. + In the InputBoundary mode one side of the border has to be defined. The \c coordinateVector methodes always working on the input geometry. + */ + class SITUATIONMAP_EXPORT RubberbandArrow : public RubberbandMultiPointFeature + { + Q_OBJECT + public: + /** + @name Construction & Destruction + @{ + */ + private: + + friend class RubberbandFactory; + explicit RubberbandArrow( SituationMap::Map * map, QObject * parent = nullptr ); + + public: + ~RubberbandArrow(); + /** @} */ + + + /** + Sets coordinates to modify to internal coordinate list. + + @param coordinateVec List of coordinates to modify. + @param cs Defines the coordinate system of returned coordinates. + */ + void setCoordinateVector( const QVector & coordinateVec, CoordinateSystem cs = CoordinateSystem::Geo ) override; + + /** + Sets coordinates to modify to internal coordinate list. In \c InputCenterLine mode the coordinates will be interpreted as + center line of the corridor. In \c InputBoundary the coordinates are defining one border. Id \c corridorWidth is negative + the coordinates are the right border otherwise the left one. + + @param coordinateVec List of coordinates to modify. + @param arrowHalfWidth Half width of arrow in pixel. + @param arrowLength Length of arrow in pixel. + @param cs Defines the coordinate system of returned coordinates. + */ + void setCoordinateVectorArrow(const QVector & coordinateVec, unsigned int arrowHalfWidth, unsigned int arrowLength, CoordinateSystem cs = CoordinateSystem::Geo ); + + + /** + Return the width of the corridor. + */ + unsigned int arrowHalfWidth() const; + unsigned int arrowLength() const; + + + bool selectAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool setCoordinateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool movePickedAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool moveAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + + + protected: + /* + Forward declaration of InternalData to hold private member variables. Becareful the base class Rubberband + has similary a class for internal data member with name "InternalData". Don't be confused it is not the same. + */ + struct InternalData; + + InternalData * _internal; + + unsigned int _arrowSceneHalfWidth; + unsigned int _arrowSceneLength; + + void update() override; + + + void updateArrowPath(); + void updateHandleScenePosition(); + + void constructArrow( const QPointF & p ); + void constructArrowHandle(); + + QPointF getArrowHandleScenePosition(); + QPointF getArrowHandlePosition(); + + + + }; /* RubberbandCorridor */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_base.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_base.cpp new file mode 100755 index 000000000..3ef42621c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_base.cpp @@ -0,0 +1,654 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of base class for rubberbands +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_base.h" +#include "rubberband_base_internal.h" +#include "rubberband_handle_proxy.h" + +#include "rubberband_factory.h" + +#include + +#include +#include + +// Qt +#include +#include + +#include +#include +#include +#include +#include + +#include "apply.xpm" +#include "abort.xpm" +#include "undo.xpm" + + static SituationMap::Map * currentFactoryMap = nullptr; + static bool allowMultipleRubberbands = false; + + +namespace SituationMap { namespace RB +{ + QPixmap * RubberbandBase::BaseInternalData::_applyIcon = nullptr; + QPixmap * RubberbandBase::BaseInternalData::_abortIcon = nullptr; + QPixmap * RubberbandBase::BaseInternalData::_undoIcon = nullptr; + + RubberbandBase::BaseInternalData::BaseInternalData( SituationMap::Map * map, RubberbandBase * me ) + : _map(map) + , _mapCtFunctor(map->coordTranslationFunctor()) + , _isActive(false) + , _applyIsPossible(false) + , _undoIsPossible(false) + , _hasUndoButton(false) + , _lastClickPosition(-1,-1) + , _buttonBoxSceneAnchor() + , _buttonBoxFixPosition(-1,-1) + , _buttonProxy() + , _updateFunctorList() + , _logger(getLogger("RubberbandBase")) + { + QGraphicsView * view = map->getQGraphicsView(); + + if (view) + { + _restoreCursor = view->viewport()->cursor(); // workaround Qt bug. remove when fixed + } + + _buttonBox = new QWidget(); + _buttonBox->setObjectName(QLatin1String("RBButtonBoxWidget")); + _buttonBox->hide(); + _buttonBox->setAttribute( Qt::WidgetAttribute::WA_NoSystemBackground, true ); + + _applyButton = new QPushButton( (_applyIcon) ? *_applyIcon : QPixmap(apply_xpm), QString(), _buttonBox ); + _applyButton -> setAttribute( Qt::WidgetAttribute::WA_NoSystemBackground, true ); + _applyButton -> setObjectName(QLatin1String("RBApply")); + _applyButton -> setAttribute(Qt::WA_AcceptTouchEvents); + _applyButton -> grabGesture( Qt::GestureType::TapGesture ); + me->connect( _applyButton, &QPushButton::clicked, me, &RubberbandBase::onDo_applyButtonClicked ); + + _cancelButton = new QPushButton( (_abortIcon) ? *_abortIcon : QPixmap(abort_xpm), QString(), _buttonBox ); + _cancelButton -> setAttribute( Qt::WidgetAttribute::WA_NoSystemBackground, true ); + _cancelButton -> setObjectName(QLatin1String("RBCancel")); + _cancelButton -> setAttribute(Qt::WA_AcceptTouchEvents); + _cancelButton -> grabGesture( Qt::GestureType::TapGesture ); + me->connect( _cancelButton, &QPushButton::clicked, me, &RubberbandBase::onDo_cancelButtonClicked ); + + _undoButton = new QPushButton( (_undoIcon) ? *_undoIcon : QPixmap(undo_xpm), QString(), _buttonBox ); + _undoButton -> setAttribute( Qt::WidgetAttribute::WA_NoSystemBackground, true ); + _undoButton -> setObjectName(QLatin1String("RBUndo")); + _undoButton -> setAttribute(Qt::WA_AcceptTouchEvents); + _undoButton -> grabGesture( Qt::GestureType::TapGesture ); + _undoButton -> hide(); + me->connect( _undoButton, &QPushButton::clicked, me, &RubberbandBase::onDo_undoButtonClicked ); + + _buttonLayout = new QHBoxLayout( _buttonBox ); + _buttonLayout->setObjectName(QLatin1String("RBButtonBoxLayoutHBox")); + + _buttonLayout -> addWidget( _undoButton ); + _buttonLayout -> addWidget( _cancelButton ); + _buttonLayout -> addWidget( _applyButton ); + + } /* InternalData */ + + + Rubberband::BaseInternalData::~BaseInternalData() + { + _map = nullptr; + _mapCtFunctor = nullptr; + + _buttonProxy = nullptr; // delete RefPointer + if (_buttonBox) _buttonBox -> deleteLater(); // delete all widgets of button box + + _buttonBox = nullptr; + _buttonLayout = nullptr; + _applyButton = nullptr; + _cancelButton = nullptr; + _undoButton = nullptr; + + } /* ~InternalData */ + + + bool RubberbandBase::BaseInternalData::buttonProxyActive( const QPointF & pos ) const + { + if (_buttonProxy) + { + return ((_buttonProxy->isVisible()) && (_buttonProxy->isPositionInside(pos))); + } + return false; + } /* bool RubberbandBase::BaseInternalData::buttonProxyActive( const QPointF & pos ) const*/ + + + void RubberbandBase::BaseInternalData::enableAndShowOnlyPossibleButtons() + { + _applyButton->setEnabled(_applyIsPossible); + + if (_undoButton) + { + if (_hasUndoButton) + { + _undoButton -> show(); + } + _undoButton -> setEnabled(_undoIsPossible); + } + } /* void RubberbandBase::InternalData::showOnlyPossibleButtons() */ + + + static QHash> &activeRubberbands() + { + static QHash> hashTable; + return hashTable; + } // activeRubberbands() + + + void RubberbandBase::currentFactoryMap(SituationMap::Map * map) { ::currentFactoryMap = map; } + void RubberbandBase::allowMultipleRubberbands( bool ok ) { ::allowMultipleRubberbands = ok; } + + + void * RubberbandBase::operator new(size_t bytes) + { + QString exceptionMsg; + + // be careful it is not thread safe here + if (::currentFactoryMap) + { + bool doNew( ::allowMultipleRubberbands ); + + if (!doNew) + { + doNew = true; + const auto rubberbands = SituationMap::RB::activeRubberbands().value(::currentFactoryMap); + for (const auto rbnd : rubberbands) + { + if (rbnd->rubberbandIsAlive()) + { + doNew = false; + break; + } + } + } + + if (doNew) + { + RubberbandBase::currentFactoryMap( nullptr ); + RubberbandBase::allowMultipleRubberbands( false ); + return ::operator new(bytes); + } + else exceptionMsg = QLatin1String("Another rubberband is already alive at moment"); + RubberbandBase::currentFactoryMap( nullptr ); + } + else exceptionMsg = QLatin1String("Use rubberband factory to create rubberbands. Calling new directly is forbidden!"); + + RubberbandBase::currentFactoryMap( nullptr ); + RubberbandBase::allowMultipleRubberbands( false ); + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * because "return nullptr;" is not compliante with the c++ standard + * throw exception instead + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + throw Exception( __FILE__, __LINE__, exceptionMsg ); + + } /* RubberbandBase::operator new */ + + + QList RubberbandBase::activeRubberbands( SituationMap::Map * map ) + { + QList ret; + const auto rubberbands = SituationMap::RB::activeRubberbands().value(map); + for (const auto rbnd : rubberbands) + { + if (rbnd && rbnd->rubberbandIsAlive()) + ret += rbnd; + } + return ret; + } /* RubberbandBase::activeRubberbands */ + + void RubberbandBase::deleteAllActiveRubberbands(Map *map) + { + const QList &rbCont = RubberbandBase::activeRubberbands(map); + qDeleteAll(rbCont); + } /* RubberbandBase::deleteAllActiveRubberbands */ + + + bool RubberbandBase::isRubberbandActive(SituationMap::Map * map) + { + return (activeRubberbands(map).size() > 0); + } /* RubberbandBase::isRubberbandActive */ + + + void RubberbandBase::abortActiveRubberbands( SituationMap::Map * map ) + { + const QList & rbList = activeRubberbands(map); + for (auto rb : rbList) + { + emit rb->abort(rb); + rb->doAutoDelete(); + } + } /* RubberbandBase::abortActiveRubberbands */ + + + void RubberbandBase::deleteMySelf() + { + if (_viewPortEventDispatcher) _viewPortEventDispatcher -> onDo_kickTheBucket(); + if (_viewPortEventDispatcher) _viewPortEventDispatcher -> onDo_delete(); + if (_windowEventDispatcher) _windowEventDispatcher -> onDo_delete(); + + _viewPortEventDispatcher = nullptr; + _windowEventDispatcher = nullptr; + _baseInternal->_isActive = false; + + deleteLater(); + } /* RubberbandBase::deleteMySelf */ + + + RubberbandBase::RubberbandBase( SituationMap::Map * map, QObject * parent, bool isActive ) + : QObject( parent ) + , EventActionInterface() + , _baseInternal( new RubberbandBase::BaseInternalData(map,this) ) + , _drawColor(QColor("black")) + , _fillColor(QColor("black")) + , _handleDrawColor(QColor("black")) + , _handleFillColor(QColor("yellow")) + , _handleSelectionColor(QColor("red")) + , _frameColor(QColor("black")) + , _frameFillColor(QColor(150,150,0,50)) + , _frameSelectionColor(QColor(255,0,0,50)) + , _frameHandleMargin(10) + , _pickingRatio(0) + , _finishAfterConstruction(true) + , _autoDelete(false) + , _viewPortEventDispatcher(nullptr) + , _windowEventDispatcher (nullptr) + , _userData(nullptr) + { + _fillColor.setAlpha(100); + + if (isActive) activate(); + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * In case of multiple rubberbands can be active, it is forbidden to + * create a rubberband of the same type twice anyway. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + bool rubberbandOfSameTypeIsActive = false; + const auto rubberbands = SituationMap::RB::activeRubberbands().value(map); + for (auto rbnd : rubberbands) + { + // Just to check the class names with debugger + // const char * rbndName = typeid(*rbnd).name(); + // const char * thisName = typeid(*this).name(); + + if (( typeid(*rbnd) == typeid(*this)) && rbnd->rubberbandIsAlive()) + { + rubberbandOfSameTypeIsActive = true; + break; + } + } + + if (rubberbandOfSameTypeIsActive) + { + // disable dispatcher, so the rubberband is not alive yet. + if (_viewPortEventDispatcher) + { + _viewPortEventDispatcher -> onDo_kickTheBucket(); + _viewPortEventDispatcher -> onDo_delete(); + } + if (_windowEventDispatcher) _windowEventDispatcher->onDo_delete(); + _viewPortEventDispatcher = nullptr; + _windowEventDispatcher = nullptr; + } + else + { + SituationMap::RB::activeRubberbands()[map].append( this ); + if (_windowEventDispatcher) + { + _windowEventDispatcher->setCursor(QCursor(Qt::CrossCursor)); + _windowEventDispatcher->grab(); + } + if (_viewPortEventDispatcher) + { + _viewPortEventDispatcher->setCursor(QCursor(Qt::CrossCursor)); + _viewPortEventDispatcher->grab(); + } + } + + if (map) + { + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Sending signals of RubberbandFactory to inform about rubberband creation and destroy would be + * better placed in RubberbandFactory source (rubberband_factory.cpp) but there it's a lot of + * more writing stuff ... + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + SituationMap::RB::RubberbandFactory & rbndFactory = map -> rubberbandFactory(); + emit rbndFactory.rubberbandCreated(); + + connect( this, &QObject::destroyed, &rbndFactory, &SituationMap::RB::RubberbandFactory::rubberbandDestroyed ); + } + + } /* RubberbandBase::RubberbandBase */ + + + RubberbandBase::~RubberbandBase() + { + if (_viewPortEventDispatcher) _viewPortEventDispatcher -> onDo_kickTheBucket(); + if (_viewPortEventDispatcher) _viewPortEventDispatcher -> onDo_delete(); + if (_windowEventDispatcher) _windowEventDispatcher -> onDo_delete(); + + _viewPortEventDispatcher = nullptr; + _windowEventDispatcher = nullptr; + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Test if map has been already destroyed. If map exists all is nice ... + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + if (_baseInternal->_map) + { + SituationMap::RB::activeRubberbands()[_baseInternal->_map].removeOne( this ); + emit finished( this ); + } + else + { + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * ... otherwise we have to search the rubberband + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + const auto rubberbands = SituationMap::RB::activeRubberbands(); + for (auto rubberList : rubberbands) + rubberList.removeOne(this); + } + + delete _baseInternal; + _baseInternal = nullptr; + + } /* RubberbandBase::~RubberbandBase */ + + + QPointF RubberbandBase::projectCoordinateToMap( CoordinateSystem cs, const QPointF & loc ) const + { + return (cs != CoordinateSystem::Geo || !_baseInternal->_mapCtFunctor) ? loc : _baseInternal->_mapCtFunctor->geographicToMap( loc ); + } /* RubberbandBase::projectCoordinateToMap */ + + + QPointF RubberbandBase::projectCoordinateFromMap( CoordinateSystem cs, const QPointF & loc ) const + { + return (cs != CoordinateSystem::Geo || !_baseInternal->_mapCtFunctor) ? loc : _baseInternal->_mapCtFunctor->mapToGeographic( loc ); + } /* RubberbandBase::projectCoordinateFromMap */ + + + QPointF RubberbandBase::projectCoordinateFromMap( CoordinateSystem cs, const Esri::ArcGISRuntime::Point & loc ) const + { + return (cs != CoordinateSystem::Geo || !_baseInternal->_mapCtFunctor) ? QPointF(loc.x(), loc.y()) : _baseInternal->_mapCtFunctor->mapToGeographic( loc ); + } /* RubberbandBase::projectCoordinateFromMap */ + + + + void RubberbandBase::activate() + { + if (!_baseInternal->_map) return; + + if (!_baseInternal->_isActive) + { + QGraphicsView * view = _baseInternal->_map->getQGraphicsView(); + if (view) + { + _viewPortEventDispatcher = new EventDispatcher(view->viewport(), this); + _windowEventDispatcher = new EventDelegater(view, _viewPortEventDispatcher); + _baseInternal->_isActive = true; + + MapImpl *mapImpl = static_cast(_baseInternal->_map.data()); + + connect(_viewPortEventDispatcher.data(), &EventDispatcher::mouseMovedEvent, + mapImpl, &MapImpl::onHandleMouseMove); + } + } + } /* RubberbandBase::activate */ + + + bool RubberbandBase::autoSelectAction(int x, int y) + { + _viewPortEventDispatcher->_leftPressHandled = true; + return selectAction(nullptr,x,y); + } /* RubberbandBase::autoSelectAction */ + + + void RubberbandBase::installExternalUpdate(const std::function & updateFunctor) + { + _baseInternal->_updateFunctorList << updateFunctor; + } // void RubberbandBase::installExternalUpdate( const std::function & updateFunctor) + + + void RubberbandBase::externalUpdate() + { + for (const auto &update : std::as_const(_baseInternal->_updateFunctorList)) + { + if (!update(this)) + break; + } + } // void RubberbandBase::externalUpdate() + + + + void RubberbandBase::updateButtonPosition() + { + if ((_baseInternal->_lastClickPosition.x() < 0) || (_baseInternal->_lastClickPosition.y() < 0)) + _baseInternal->_buttonBoxSceneAnchor = sceneBbox().bottomRight(); + else + _baseInternal->_buttonBoxSceneAnchor = _baseInternal->_lastClickPosition; + + // _internal->_mapView->update(); + } /* RubberbandBase::updateButtonPosition */ + + + void RubberbandBase::setFixedButtonBoxPosition( QPoint widgetPos ) + { + _baseInternal->_buttonBoxFixPosition = widgetPos; + activateButtons(); + } /* RubberbandBase::setFixedButtonBoxPosition */ + + + void RubberbandBase::unsetFixedButtonBoxPosition() + { + _baseInternal->_buttonBoxFixPosition = QPoint(-1,-1); + + if (_baseInternal->_buttonProxy) + { + _baseInternal->_buttonProxy->setPos( _baseInternal->_buttonBoxSceneAnchor ); + } + } /* RubberbandBase::unsetFixedButtonBoxPosition */ + + + bool RubberbandBase::isButtonBoxPositionFixed() { return ((_baseInternal->_buttonBoxFixPosition.x() >= 0) && (_baseInternal->_buttonBoxFixPosition.y() >= 0)); } + + + void RubberbandBase::onDo_updateButtonFixCoordinates() + { + if (!_baseInternal->_buttonProxy) return; + + if (isButtonBoxPositionFixed()) + { + // Don't use bottom right corner of rubberband bbox to place the buttons, + // rather than use fixed window position. + + const QPoint & pos = _baseInternal->_buttonBoxFixPosition; + _baseInternal->_buttonProxy->setPos( pixel2ScenePosition(pos.x(),pos.y()) ); + } + } /* RubberbandStateSelect::onDo_updateButtonFixCoordinates */ + + + void RubberbandBase::activateButtons() + { + DEBUG_LOG(_baseInternal->_logger) << LOGFUNCTION; + if (!_baseInternal->_buttonProxy) + { + _baseInternal->_buttonProxy = new RubberbandHandleProxy( _baseInternal->_map->getQGraphicsView(), _baseInternal->_buttonBox ); + _baseInternal->_buttonProxy->setRestoreCursor( _baseInternal->_restoreCursor ); // workaround Qt bug. remove when fixed + + connect( _baseInternal->_buttonProxy.get(), &RubberbandHandleProxy::adjusted, this, [this]() + { + this->_baseInternal->_buttonBoxSceneAnchor = this->_baseInternal->_buttonProxy->getCoordinate()._sceneCoordinate; + } ); + } + + _baseInternal->enableAndShowOnlyPossibleButtons(); + + if (isButtonBoxPositionFixed()) + { + const QPoint & pos = _baseInternal->_buttonBoxFixPosition; + _baseInternal->_buttonProxy->setPos( pixel2ScenePosition(pos.x(),pos.y()) ); + _baseInternal->_buttonProxy->setSceneOffset( QPointF(0,0) ); + + } + else + { + _baseInternal->_buttonProxy->setPos( _baseInternal->_buttonBoxSceneAnchor ); + _baseInternal->_buttonProxy->setSceneOffset( QPointF(_frameHandleMargin+5,_frameHandleMargin+5) ); + } + + _baseInternal->_buttonProxy->show(); + + } /* Rubberband::activateButtons */ + + + void RubberbandBase::deactivateButtons() + { + DEBUG_LOG(_baseInternal->_logger) << LOGFUNCTION; + if (isButtonBoxPositionFixed()) return; + if (_baseInternal->_buttonProxy) _baseInternal->_buttonProxy->hide(); + } /* RubberbandBase::deactivateButtons() */ + + + void RubberbandBase::adjustButtons() + { + if (_baseInternal->_buttonProxy) + { + activateButtons(); + } + } /* RubberbandBase::adjustButtons */ + + + void RubberbandBase::applyButtonClicked() + { + completeStateAction( _viewPortEventDispatcher, 0.0, 0.0 ); + } /* RubberbandBase::applyButtonClicked */ + + + void RubberbandBase::cancelButtonClicked() + { + abortAction( _viewPortEventDispatcher, 0.0, 0.0 ); + } /* RubberbandBase::cancelButtonClicked */ + + + void RubberbandBase::undoButtonClicked() + { + undoAction( _viewPortEventDispatcher, 0.0, 0.0 ); + } /* RubberbandBase::undoButtonClicked */ + + + void RubberbandBase::resetIconsToDefault() + { + if (BaseInternalData::_applyIcon) { delete BaseInternalData::_applyIcon; BaseInternalData::_applyIcon = nullptr; } + if (BaseInternalData::_abortIcon) { delete BaseInternalData::_abortIcon; BaseInternalData::_abortIcon = nullptr; } + if (BaseInternalData::_undoIcon ) { delete BaseInternalData::_undoIcon; BaseInternalData::_undoIcon = nullptr; } + } /* RubberbandBase::resetIconsToDefault */ + + + void RubberbandBase::setIconApply(const QPixmap & icon) + { + if (BaseInternalData::_applyIcon == nullptr) BaseInternalData::_applyIcon = new QPixmap(icon); + else *BaseInternalData::_applyIcon = icon; + } /* RubberbandBase::setIconApply */ + + + void RubberbandBase::setIconAbort(const QPixmap & icon) + { + if (BaseInternalData::_abortIcon == nullptr) BaseInternalData::_abortIcon = new QPixmap(icon); + else *BaseInternalData::_abortIcon = icon; + } /* RubberbandBase::setIconAbort */ + + + void RubberbandBase::setIconUndo(const QPixmap & icon) + { + if (BaseInternalData::_undoIcon == nullptr) BaseInternalData::_undoIcon = new QPixmap(icon); + else *BaseInternalData::_undoIcon = icon; + } /* RubberbandBase::setIconUndo */ + + + QPointF RubberbandBase::pixel2ScenePosition( int x, int y ) const + { + QGraphicsView * view = _baseInternal->_map->getQGraphicsView(); + return (view) ? view->mapToScene(x, y) : QPointF(x,y); + } /* RubberbandBase::pixel2ScenePosition */ + + + bool RubberbandBase::buttonPositionAction(AbstractEventDispatcher *, int x, int y) + { + QPointF scenePos(pixel2ScenePosition(x,y)); + + if (!_baseInternal->buttonProxyActive(scenePos)) + { + _baseInternal->_buttonBoxSceneAnchor = scenePos; + adjustButtons(); + } + return false; + } /* bool RubberbandBase::buttonPositionAction(AbstractEventDispatcher * causer, int x, int y) */ + + + void RubberbandBase::unlinkEventDispatcher( AbstractEventDispatcher* ) + { + _viewPortEventDispatcher = nullptr; + _windowEventDispatcher = nullptr; + + } /* RubberbandBase::unlinkEventDispatcher */ + + + bool RubberbandBase::abortAction( AbstractEventDispatcher * causer, int, int ) + { + _baseInternal->_buttonBox->hide(); + + if (causer) causer->onDo_kickTheBucket(); + + emit abort( this ); + doAutoDelete(); + return true; + } /* RubberbandBase::abortAction */ + + + bool RubberbandBase::applyAndEndAction( AbstractEventDispatcher * causer, int,int ) + { + _baseInternal->_buttonBox->hide(); + + if (causer) causer->onDo_kickTheBucket(); + + emit apply( this ); + doAutoDelete(); + return true; + } /* RubberbandBase::applyAndEndAction */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_base.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_base.h new file mode 100755 index 000000000..d43ea7ca5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_base.h @@ -0,0 +1,500 @@ +/** +** +** @RDE_HEADER@ +** Definition of base class for rubberbands +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + + +#include "event_dispatcher.h" +#include "event_delegater.h" + +#include +#include + +#include +#include +#include + +#include + +#include + + +namespace Esri { +namespace ArcGISRuntime { + class MapGraphicsView; + class Point; +} } + + + +namespace SituationMap +{ + +/** + Namespace for all rubberband stuff (is part of namespace SituationMap) +*/ +namespace RB +{ + /** + */ + enum class CoordinateSystem + { + Map, + Geo + }; + + + /** + @class RubberbandBase rubberband_base.h + @brief Base class of all Rubberbands + + Rubberbands are used to create or modifiy geometries like lines, circles, polygons etc. by processing + Qt input events like keyboard and mouse events. To create a rubberband the RubberbandFactory has to be used. + At end of rubberband processing the signal \c abort or\c apply are emitted. To delete the rubberband the + function deleteMySelf must be called. To use the delete operator is dangerous because at end of signal + processing some program code may be performed. + + Example to how to use rubberband: + + @code + + SituationMap::Map & map = ... + + const RubberbandFactory & rubberbandFactory = map.rubberbandFactory(); + + RubberbandPointFeature * rubberband = rubberbandFactory.create(); + if (rubberband != nullptr) + { + QObject::connect( rubberband, &SiMapRubBnd::Rubberband::apply, [=](SiMapRubBnd::Rubberband * rubberband ) + { + // rubberband has finished input + if (rubberband) + { + const QVector & vertexList = rubberband->coordinateVector(); + + // do something with vertexList + + rubberband->deleteMySelf(); // notice don't call here delete rubberband directly !!! + } + } ); + + QObject::connect( rubberband, &SiMapRubBnd::Rubberband::abort, this, [=](SiMapRubBnd::Rubberband * rubberband ) + { + // Rubberband has been aborted by user + if (rubberband) + { + rubberband->deleteMySelf(); // notice don't call here delete rubberband directly !!! + } + } ); + } + + @endcode + + Following keyboard commands are available in active rubberbands: + + | Key | Modifier | Funktion | + | :----: |:-------: | :--------------------------------------------------------------------------- | + | Esc | | Rubberband abbrechen | + | Return | | Rubberband abschließen und Eingabe übernehmen wenn möglich, sonst Abbruch | + | Left | | Selektierten Handle (Punkt,Frame) um 1 Pixel nach links schieben | + | Right | | Selektierten Handle (Punkt,Frame) um 1 Pixel nach rechts schieben | + | Up | | Selektierten Handle (Punkt,Frame) um 1 Pixel nach oben schieben | + | Down | | Selektierten Handle (Punkt,Frame) um 1 Pixel nach unten schieben | + | Left | Shift | Selektierten Handle (Punkt,Frame) um 10 Pixel nach links schieben | + | Right | Shift | Selektierten Handle (Punkt,Frame) um 10 Pixel nach rechts schieben | + | Up | Shift | Selektierten Handle (Punkt,Frame) um 10 Pixel nach oben schieben | + | Down | Shift | Selektierten Handle (Punkt,Frame) um 10 Pixel nach unten schieben | + + @see EventDispatcher + @see Rubberband, RubberbandPointFeature, RubberbandPointCluster, RubberbandMultiPointFeature, RubberbandRectangle, RubberbandEllipse, RubberbandCorridor, RubberbandArrow + Rubberband3D, RubberbandPointFeature3D, RubberbandMultiPointFeature3D + + */ + + class SITUATIONMAP_EXPORT RubberbandBase : public QObject, public EventDispatcher::EventActionInterface + { + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Because class EventDispatcher::EventActionInterface is a pure virtual interface function the classic way of declaration would be + * + * SITUATIONMAP_EXPORT Rubberband : public QObject, public virtual EventDispatcher::EventActionInterface + * + * But with Visual Studio 2013 virtual inheritance combined with constructor delegation doesn't work and may crash the program. In + * this case the virtual inheritance of EventActionInterface isn't need because in the derived classes multiple inheritance will + * not be used. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + Q_OBJECT + + public: + + /** + @name Construction & Destruction + @{ + */ + + public: + + /** + Constructs a Rubberband for a situation map. Use \c RubberbandFactor to create a rubberband. + In case of the user actions the autoDelete mechanism can be used to delete the rubberband. Sometimes + the application wants to finish a running rubberband. This can be done by calling \c deleteMySelf. The + rubberband will be destroyed also automatically if the parent QObject or the map (see + SituationMap::Map::beginDestroy signal) will be destroyed. + + @param map Pointer to SituationMap::Map + @param parent Pointer to QObject to use as parent. + @isActive If set to false the Rubberband is not active and have to be actived to start the input. + @see RubberbandFactory + */ + RubberbandBase( SituationMap::Map * map, QObject * parent, bool isActive = true ); + + ~RubberbandBase(); + /** @} */ + + /** + Activate rubberband if it has been created with \c isActive = false. + */ + void activate(); + + /** + Provides a list of active rubberbands for a situation map. + */ + static QList activeRubberbands( SituationMap::Map * map ); + + /** + Delete active rubberbands for a situation map. + */ + static void deleteAllActiveRubberbands( SituationMap::Map * map ); + + /** + Use deleteMySelft instead of delete operator to avoid dangling this pointerin Qt signal processing. It calls delete this; later on. + */ + void deleteMySelf(); + + /** + If setting auto delete flag to true the delete will be done automatically after sending signal abort or apply. + The default value of auto delete flag is false. + @{ + */ + inline void setAutoDelete(bool on) { _autoDelete = on; } + + + inline bool autoDelete() const { return _autoDelete; } + /** @} */ + + /** + Returns true if the rubberband is already alive. Should be tested after creation of a rubberband because it is + possibled to create not living rubberbands, e.g. a rubberband was create twice and the first one has not finished + */ + inline bool rubberbandIsAlive() const { return !_viewPortEventDispatcher.isNull(); } + + /** A single user action step has finished. Interim results can be used to show a preview now. + */ + void userActionFinished() override { emit interimResultsChanged(this); } + + /** + @name ColorSettings Methodes to define visual behaviour of rubberband + @{ + */ + inline void setDrawColor (const QColor & col ) { _drawColor = col; update(); } ///< set color for general drawing + inline void setFillColor (const QColor & col ) { _fillColor = col; update(); } ///< set color for general filling + inline void setHandleDrawColor (const QColor & col ) { _handleDrawColor = col; update(); } ///< set color for drawing handles + inline void setHandleFillColor (const QColor & col ) { _handleFillColor = col; update(); } ///< set color for filling handles + inline void setHandleSelectionColor(const QColor & col ) { _handleSelectionColor = col; update(); } ///< set handle selection color + inline void setFrameColor (const QColor & col ) { _frameColor = col; update(); } ///< set frame handle outline color + inline void setFrameFillColor (const QColor & col ) { _frameFillColor = col; update(); } ///< set frame handle fill color + inline void setFrameHandleMargin (int margin ) { _frameHandleMargin = margin; update(); } ///< set margin of frame in pixel + + inline void setPickingRatio (unsigned int size ) { _pickingRatio = size; update(); } ///< set size of point handles + inline void setLineHandleWidth (unsigned int width ) { _lineHandleWidth = width; update(); } ///< set width of line handles + + inline const QColor getDrawColor () const { return _drawColor; } ///< get color for general drawing + inline const QColor getFillColor () const { return _fillColor; } ///< get color for general filling + inline const QColor getHandleDrawColor() const { return _handleDrawColor; } ///< get color for drawing handles + inline const QColor getHandleFillColor() const { return _handleFillColor; } ///< get color for filling handles + inline const QColor getHandleSelectionColor() const { return _handleSelectionColor; } ///< get handle selection color + inline const QColor getFrameColor() const { return _frameColor; } ///< get frame handle outline color + inline const QColor getFrameFillColor() const { return _frameFillColor; } ///< get frame handle fill color + inline int getFrameHandleMargin() const { return _frameHandleMargin; } ///< get margin of frame in pixel + + inline unsigned int getPickingRatio() const { return _pickingRatio; } ///< get size of point handles + /** @} */ + + bool applyAndEndAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool abortAction ( AbstractEventDispatcher * causer, int x, int y ) override; + + /** + @name Set icons for rubberband buttons + + In case of the application doesn't use the default rubberband icons other icons can be set with the setIcon functions. This redefine + of the button icons have to be done before creating a rubberband. + @{ + */ + static void setIconApply( const QPixmap & ); + static void setIconAbort( const QPixmap & ); + static void setIconUndo ( const QPixmap & ); + + static void resetIconsToDefault(); + /** @} */ + + /** + Start Rubberband mouse input automatically by program + */ + bool autoSelectAction( int x, int y ); + + /** + If set to true the rubberband will send apply signal after leaving construction state and ends + otherwise it continues in modification state. Default value is true. + */ + inline void enableFinishAfterConstruction(bool flag) { _finishAfterConstruction = flag; } + + + /** + Defines fixed widget position of rubberband button box + */ + void setFixedButtonBoxPosition( QPoint widgetPos ); + + + /** + Undefines fixed widget position. The rubberband button box will be placed at the bottom right coner of + the rubberbands bounding box. + */ + void unsetFixedButtonBoxPosition(); + + /** + Returns true if a fixed widget position of rubberband button box is used. + */ + bool isButtonBoxPositionFixed(); + + /** + @name EventHandlerInterface Methodes of AbstractEventDispatcher::EventActionInterface + The interface between EventDispatcher and Rubberband are defined in class AbstractEventDispatcher. + To Implement the interface these methodes have to be override again in the derived classes of the Rubberband class. */ + /** + @{ + */ + + bool buttonPositionAction ( AbstractEventDispatcher * causer, int x, int y ) override; + + void unlinkEventDispatcher ( AbstractEventDispatcher * causer ) override; + + /** @} */ + + + /** + Returns true if the coordinates are valid elsewise false + */ + virtual bool coordinatesAreValid() const { return true; } + + + /** + Returns count of coordinates + */ + virtual unsigned int coordinateCount() const = 0; + + /** + Returns constructed or modified coordinates + @param cs Defines the coordinate system of returned coordinates. + */ + virtual QVector coordinateVector( CoordinateSystem cs = CoordinateSystem::Geo ) const = 0; + + /** + Returns a single coordinate from rubberbands coordinate list + @param idx index of coordinate + @param cs Defines the coordinate system of returned coordinate. + */ + virtual QPointF singleCoordinate( unsigned int idx = 0, CoordinateSystem cs = CoordinateSystem::Geo ) const = 0; + + /** + Sets coordinates to modify to internal coordinate list + + @param pntList List of coordinates to modify + @param cs Defines the coordinate system of input coordinates. + */ + virtual void setCoordinateVector( const QVector & pntList, CoordinateSystem cs = CoordinateSystem::Geo ) = 0; + + /** + Set user defined informations to this rubberband + */ + inline void setUserData(void * p) + { + _userData = p; + } + + /** + returns the user defined informations, which are set with methode userData(void*) + */ + inline void * userData() { return _userData; } + + + /** + Draw rubberband manually by calling the update function. In general it is not neccessary to call draw function. + But in some cases, for example the color setting functions, the graphic view update will not be done automatically. + Here calling draw forces an update of graphic view. + */ + inline void draw() { update(); } + + + /** + Sets call back function to realize an external update. The updateFunctor should return false to abort update process. In other case it have to return true. + */ + void installExternalUpdate(const std::function & updateFunctor); + + + /** + Returns true if there is a active rubberband on map. + @param map Pointer to map + */ + static bool isRubberbandActive( SituationMap::Map * map ); + + /** + Abort all active rubberbands on map. + @param map Pointer to map + */ + static void abortActiveRubberbands( SituationMap::Map * map ); + + + protected: + + Q_REQUIRED_RESULT QPointF projectCoordinateToMap( CoordinateSystem cs, const QPointF &loc ) const; + Q_REQUIRED_RESULT QPointF projectCoordinateFromMap( CoordinateSystem cs, const QPointF & loc ) const; + Q_REQUIRED_RESULT QPointF projectCoordinateFromMap( CoordinateSystem cs, const Esri::ArcGISRuntime::Point & loc ) const; + + inline void doAutoDelete() { if (_autoDelete) deleteMySelf(); } + + void updateButtonPosition(); + + QPointF pixel2ScenePosition( int x, int y ) const; + + virtual void update() = 0; + + void externalUpdate(); + + /* + Forward declaration of internal container class for member variables. The derived classes of Rubberband + often have also a declaration of InternalData for there own internal members. + */ + class BaseInternalData; + + BaseInternalData *_baseInternal; + + QColor _drawColor; + QColor _fillColor; + QColor _handleDrawColor; + QColor _handleFillColor; + QColor _handleSelectionColor; + QColor _frameColor; + QColor _frameFillColor; + QColor _frameSelectionColor; + int _frameHandleMargin; + + unsigned int _lineHandleWidth; + + unsigned int _pickingRatio; + + bool _finishAfterConstruction; + bool _autoDelete; + + QPointer _viewPortEventDispatcher; + QPointer _windowEventDispatcher; + + virtual QRectF sceneBbox() const = 0; + + virtual void activateButtons(); + virtual void deactivateButtons(); + virtual void adjustButtons(); + virtual void applyButtonClicked(); + virtual void cancelButtonClicked(); + virtual void undoButtonClicked(); + + protected slots: + + void onDo_applyButtonClicked() { applyButtonClicked(); } + void onDo_cancelButtonClicked() { cancelButtonClicked(); } + void onDo_undoButtonClicked() { undoButtonClicked(); } + + void onDo_updateButtonFixCoordinates(); + + private: + + void * _userData; + + friend class RubberbandFactory; + + static void * operator new(size_t); + + /** + The function \c allowMultipleRubberbands set a flag to allow new to create more than one rubberband + per situation map for the next new operator call. The function is used in class RubberbandFactory. The + value will be resetted to \c false at end of new operator. + */ + static void allowMultipleRubberbands( bool ok ); + + /** + The function \c currentFactoryMap defines the calling situation map for the next new operator call. The + function is used in class RubberbandFactory.The value will be resetted to \c nullptr at end of new operator. + */ + static void currentFactoryMap( SituationMap::Map * map ); + + + signals: + + /** + The abort signal will be emitted, when the abortAction has been called by one of the attached event handlers. + Before emtting the signal the event handler are closed and deleted. To avoid memory leaks the application has to + ensure that the delete function is called for this rubberband. See also example code at beginning. + + @param rbnd pointer to this Rubberband + */ + void abort( RubberbandBase * rbnd ); + + /** + The apply signal will be emitted, when the applyAndEndAction has been called by one of the attached event handler. The connected slot can now + read the geometry information. Before emtting the signal the event handler are closed and deleted.To avoid memory leaks the application has to + ensure that the delete function is called for this rubberband. See also example code at beginning. + + @param rbnd pointer to this Rubberband. + */ + void apply( RubberbandBase * rbnd ); + + /** + The finished signal will be emitted, when the rubberband is going to terminate and all user actions are done. + + @param rbnd pointer to this Rubberband. + */ + void finished( RubberbandBase * rbnd ); + + /** + The coordinatesChanged signal will be emitted , when the user has changed, removed or added coordinates. Can be used to implement + a preview of interim results. + */ + void interimResultsChanged( RubberbandBase * rbnd ); + + }; /* Rubberband */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_base_internal.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_base_internal.h new file mode 100755 index 000000000..61039acca --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_base_internal.h @@ -0,0 +1,118 @@ +/** +** +** @RDE_HEADER@ +** Definition of internal data of base class for rubberbands +** $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 .........: +** +** 16.06.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + + +#include +#include + +#include +#include + +#include "rubberband_base.h" + + + +class QHBoxLayout; +class QWidget; +class QPushButton; +class QGraphicsScene; +class QGraphicsView; + + +namespace Esri { +namespace ArcGISRuntime { + class MapGraphicsView; +} } + + +namespace SituationMap +{ + class Map; +} + +namespace SituationMap { namespace RB +{ + + class RubberbandHandleProxy; + + /** + @class Rubberband::BaseInternalData rubberband_base_internal.h + @brief Definition of internal data of rubberband base class. + */ + class RubberbandBase::BaseInternalData + { + public: + + static QPixmap * _applyIcon; + static QPixmap * _abortIcon; + static QPixmap * _undoIcon; + + QPointer _map; + + SituationMap::CoordTranslationFunctor * _mapCtFunctor; + + bool _isActive; + + bool _applyIsPossible; + bool _undoIsPossible; + bool _hasUndoButton; + + QPointF _lastClickPosition; + + QPointF _buttonBoxSceneAnchor; + QPoint _buttonBoxFixPosition; + SituationMap::RefPointer _buttonProxy; + QPointer _buttonBox; + QHBoxLayout * _buttonLayout; + QPushButton * _undoButton; + QPushButton * _cancelButton; + QPushButton * _applyButton; + + QCursor _restoreCursor; // workaround Qt bug. remove when fixed + + QVector> _updateFunctorList; + + LOGGER_T _logger; + + explicit BaseInternalData( SituationMap::Map * map, RubberbandBase * me ); + + virtual ~BaseInternalData(); + + bool buttonProxyActive( const QPointF & pos ) const; + + void enableAndShowOnlyPossibleButtons(); + + BaseInternalData( const BaseInternalData & ) = delete; + BaseInternalData & operator = ( const BaseInternalData & ) = delete; + + }; /* BaseInternalData */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_corridor.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_corridor.cpp new file mode 100755 index 000000000..9a9f3248d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_corridor.cpp @@ -0,0 +1,991 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of rubberband class to define multi point feature geoemtry +** $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 .........: +** +** 17.06.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_corridor.h" +#include "rubberband_drawable.h" +#include "rubberband_frame_handle.h" +#include "rubberband_handle_proxy.h" +#include "rubberband_line_handle.h" +#include "rubberband_point_handle.h" + +#include "drawable_painter_path.h" + +#include "rubberband_internal.h" +#include "rubberband_base_internal.h" + +#include + +#include +#include + + +namespace SituationMap { namespace RB +{ + + struct RubberbandCorridor::InternalData + { + SituationMap::RefPointer _widthHandle; + qreal _corridorSceneWidth = 0.0; + SituationMap::RefPointer _widthLine; + SituationMap::RefPointer _geoBufferItem; + SituationMap::RefPointer _centerLineItem; + QRectF _sceneBbox; + + }; /* class RubberbandCorridor::InternalData */ + + + RubberbandCorridor::RubberbandCorridor( SituationMap::Map * map, QObject * parent ) : RubberbandCorridor( map, InputMode::InputBoundary, 0, parent ) {;} + + + RubberbandCorridor::RubberbandCorridor( SituationMap::Map * map, InputMode mode, qreal width, QObject * parent ) + : RubberbandMultiPointFeature(map, false, parent) // Map*, bool complexGeometry, QObject* parent + , _internal(new InternalData) + , _inputMode(mode) + , _corridorWidth(width) + , _minimumCorridorWidth( 0.0 ) + , _maximumCorridorWidth( std::numeric_limits::infinity() ) + , _corridorBorderColor("black") + , _corridorFillColor("yellow") + { + _corridorFillColor.setAlphaF( 0.5 ); + _isClosed = false; + + setCheckMaxAngle(160.0 / 180.0*M_PI); // restrict angle between line segments + } /* RubberbandCorridor::RubberbandCorridor */ + + + RubberbandCorridor::~RubberbandCorridor() + { + delete _internal; + _internal = nullptr; + } /* RubberbandCorridor::~RubberbandCorridor */ + + + void RubberbandCorridor::minimumCorridorWidth( qreal min ) + { + min = fabs(min); + if (min < _maximumCorridorWidth) + { + _minimumCorridorWidth = min; + if ((_minimumCorridorWidth) > _corridorWidth) + { + _corridorWidth = _minimumCorridorWidth; + constructWidthHandle(); + } + } + } /* RubberbandCorridor::minimumCorridorWidth */ + + + void RubberbandCorridor::maximumCorridorWidth( qreal max ) + { + max = fabs(max); + if (max > _minimumCorridorWidth) + { + _maximumCorridorWidth = max; + if ((_maximumCorridorWidth) < _corridorWidth) + { + _corridorWidth = _maximumCorridorWidth; + constructWidthHandle(); + } + } + } /* RubberbandCorridor::maximumCorridorWidth */ + + + bool RubberbandCorridor::isEnabledConstructionWidthState() const + { + return _state.testFlag( RubberbandState::ConstructionWidthState ); + } /* RubberbandCorridor::isEnabledConstructionWidthState */ + + void RubberbandCorridor::enableConstructionWidthState() + { + _state &= ~QFlags(RubberbandState::ModificationState); + _state |= RubberbandState::ConstructionWidthState; + } /* RubberbandCorridor::enableConstructionWidthState */ + + void RubberbandCorridor::disenableConstructionWidthState() + { + _state &= ~QFlags(RubberbandState::ConstructionWidthState); + _state |= RubberbandState::ModificationState; + } /* RubberbandCorridor::disenableConstructionWidthState */ + + + qreal RubberbandCorridor::corridorWidth() const + { + return _corridorWidth; + } /* RubberbandCorridor::corridorWidth */ + + + void RubberbandCorridor::setCoordinateVector(const QVector & coordinateVec, CoordinateSystem cs ) + { + setCoordinateVectorWidth( coordinateVec, 0.0, cs ); + } /* RubberbandCorridor::setCoordinateVector */ + + + void RubberbandCorridor::setCoordinateVectorWidth(const QVector & coordinateVec, qreal corridorWidth, CoordinateSystem cs ) + { + if (coordinateVec.size() < 2) return; + + if (corridorWidth < 0) + { + QVector reverseVec; + reverseVec.reserve(coordinateVec.size()); + for (const auto &point : coordinateVec) + { + reverseVec.push_front(point); + } + RubberbandMultiPointFeature::setCoordinateVector(reverseVec, cs); + } + else RubberbandMultiPointFeature::setCoordinateVector( coordinateVec, cs ); + + _corridorWidth = fabs(corridorWidth); + + enableModificationState(); + constructWidthHandle(); + + _baseInternal->_applyIsPossible = true; + + activateButtons(); + update(); + + } /* RubberbandCorridor::setCoordinateVectorWidth */ + + + + QVector RubberbandCorridor::centerLine( CoordinateSystem cs ) + { + if (_inputMode == InputMode::InputCenterLine) + { + return RubberbandMultiPointFeature::coordinateVector( cs ); + } + + QVector cLine; + QVector cLineRet; + + // getCenterLineFromHandleBorder( cLine, _corridorWidth * ((_internal->_corridorSceneWidth >= 0) ? 0.5 : -0.5), true ); + getCenterLineFromHandleBorder( cLine, _corridorWidth * 0.5, true ); + cLineRet.reserve(cLine.size()); + for (const auto &point : std::as_const(cLine)) + cLineRet.push_back(projectCoordinateFromMap( cs, point )); + return cLineRet; + } /* RubberbandCorridor::centerLine */ + + + void RubberbandCorridor::setCenterLine( const QVector & coordinateVec, qreal corridorWidth, CoordinateSystem cs ) + { + if (_inputMode == InputMode::InputBoundary) + { + const QVector &borderLine = getCenterLineFromBorder( coordinateVec, corridorWidth*-0.5,cs); + setCoordinateVectorWidth( borderLine, corridorWidth, CoordinateSystem::Map); + return; + } + + setCoordinateVectorWidth( coordinateVec, corridorWidth, cs ); + } /* RubberbandCorridor::setCenterLine */ + + + QVector RubberbandCorridor::frontLine(CoordinateSystem cs) + { + if (_inputMode == InputMode::InputBoundary) + { + const QVector & fLine = RubberbandMultiPointFeature::coordinateVector(cs); + if (_internal->_corridorSceneWidth >= 0) return fLine; + // reverse order of coordinates + QVector fLineRet; + fLineRet.reserve(fLine.size()); + for (const auto & point : fLine) + { + fLineRet.push_front(point); + } + return fLineRet; + } + + // In case of InputMode::InputCenterLine construct centerLine + + QVector fLine; + getCenterLineFromHandleBorder(fLine, _corridorWidth*-1.0, true); + + if (_internal->_corridorSceneWidth >= 0) + { + for (QPointF & point : fLine) + { + point = projectCoordinateFromMap(cs, point); + } + return fLine; + } + + // reverse order of coordinates + QVector fLineRet; + fLineRet.reserve(fLine.size()); + for (const auto & point : std::as_const(fLine)) + { + fLineRet.push_front( projectCoordinateFromMap(cs,point) ); + } + + return fLineRet; + + } /* RubberbandCorridor::frontLine */ + + + void RubberbandCorridor::setFrontLine(const QVector & coordinateVec, qreal corridorWidth, CoordinateSystem cs) + { + if (_inputMode == InputMode::InputBoundary) + { + setCoordinateVectorWidth(coordinateVec, corridorWidth, cs); + } + + // In case of InputMode::InputCenterLine construct centerLine + + const QVector ¢erLine = getCenterLineFromBorder( coordinateVec, corridorWidth*0.5, cs); + setCoordinateVectorWidth(centerLine, corridorWidth, CoordinateSystem::Map); + + } /* RubberbandCorridor::setFrontLine */ + + + void RubberbandCorridor::constructWidth( const QPointF & scenePos ) + { + if (Rubberband::_internal->_handleList.size() < 2) return; + + QPointF p0 = static_cast(Rubberband::_internal->_handleList.end()[-2].get())->getCoordinate()._sceneCoordinate; + QPointF p1 = static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._sceneCoordinate; + + QPointF rv( p0.y()-p1.y(), p1.x()-p0.x() ); + rv /= sqrt(rv.x()*rv.x()+rv.y()*rv.y()); + + QPointF pScene = scenePos - p1; + + qreal scalarProduct = pScene.x()*rv.x() + pScene.y()*rv.y(); + + if (_inputMode == InputMode::InputCenterLine) scalarProduct *= 2; + + _internal->_corridorSceneWidth = scalarProduct; + + rv *= scalarProduct; + + RubberbandHandle::RubberbandCoordinate pointW( Rubberband::_internal->_mapView, p1+rv ); + + rv = QPointF( pointW._mapCoordinate.x() - static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._mapCoordinate.x() + , pointW._mapCoordinate.y() - static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._mapCoordinate.y() ); + + +// _corridorWidth = sqrt(rv.x()*rv.x()+rv.y()*rv.y()); + _corridorWidth = (scalarProduct > 0 ) ? sqrt(rv.x()*rv.x()+rv.y()*rv.y()) : - sqrt(rv.x()*rv.x()+rv.y()*rv.y()); + + bool constrainsComplied = true; + + if (_corridorWidth != 0.0) + { + qreal constraintFactor = 1.0; + if (_minimumCorridorWidth > fabs(_corridorWidth)) { constraintFactor = _minimumCorridorWidth / _corridorWidth; constrainsComplied = false; } + if (_maximumCorridorWidth < fabs(_corridorWidth)) { constraintFactor = _maximumCorridorWidth / fabs(_corridorWidth); constrainsComplied = false; } + _corridorWidth *= constraintFactor; + _internal->_corridorSceneWidth *= constraintFactor; + } + else + { + if (_minimumCorridorWidth > 0) + { + _corridorWidth = _minimumCorridorWidth; + + const QPointF & whp = calcWidthHandlePosition(); + Esri::ArcGISRuntime::MapGraphicsView * mapView = Rubberband::_internal->_mapView; + RubberbandDrawable::RubberbandCoordinate widthCoordinate( mapView, convert( whp, mapView->map()->spatialReference() )); + + const QPointF & p0 = static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._sceneCoordinate; + const QPointF & p1 = widthCoordinate._sceneCoordinate; + + QPointF rv( p0.y()-p1.y(), p1.x()-p0.x() ); + + _internal->_corridorSceneWidth = sqrt(rv.x()*rv.x()+rv.y()*rv.y()); + if (_inputMode == InputMode::InputCenterLine) _internal->_corridorSceneWidth *= 2; + } + } + + markFailedConstrains( scenePos, !constrainsComplied ); + + } /* RubberbandCorridor::constructWidth */ + + + QPointF RubberbandCorridor::getWidthHandleScenePosition() const + { + QPointF p0 = static_cast(Rubberband::_internal->_handleList.end()[-2].get())->getCoordinate()._sceneCoordinate; + QPointF p1 = static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._sceneCoordinate; + + QPointF rv( p0.y()-p1.y(), p1.x()-p0.x() ); + + rv /= sqrt(rv.x()*rv.x()+rv.y()*rv.y()); + + if ((std::numeric_limits::infinity() != rv.x()) && (std::numeric_limits::infinity() != rv.y())) + { + rv *= _internal->_corridorSceneWidth; + if (_inputMode == InputMode::InputCenterLine) rv *= 0.5; + return p1 + rv; + } + return p1; + + } /* RubberbandCorridor::getWidthHandleScenePosition */ + + + QPointF RubberbandCorridor::calcWidthHandlePosition() + { + qreal p0X = static_cast(Rubberband::_internal->_handleList.end()[-2].get())->getCoordinate()._mapCoordinate.x(); + qreal p0Y = static_cast(Rubberband::_internal->_handleList.end()[-2].get())->getCoordinate()._mapCoordinate.y(); + qreal p1X = static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._mapCoordinate.x(); + qreal p1Y = static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._mapCoordinate.y(); + + QPointF p( p1Y-p0Y, p1X-p0X ); + qreal lp = _corridorWidth / sqrt(p.x()*p.x()+p.y()*p.y()); + + if (std::numeric_limits::infinity() != lp) + { + p *= lp; + if (_inputMode == InputMode::InputCenterLine) p *= 0.5; + p = QPointF(p1X,p1Y) + p; + } + else p = QPointF(p1X,p1Y); + return p; + } /* RubberbandCorridor::calcWidthHandlePosition */ + + + void RubberbandCorridor::constructWidthHandle() + { + if (Rubberband::_internal->_handleList.size() < 2) return; + + if ((fabs(_corridorWidth) <= _minimumCorridorWidth) || (fabs(_corridorWidth) >= _maximumCorridorWidth)) + { + enableConstructionWidthState(); + deactivateButtons(); + } + + const QPointF & p = calcWidthHandlePosition(); + const Esri::ArcGISRuntime::Point mapP( convert(p, Rubberband::_internal->_mapView->map()->spatialReference()) ); + + _internal->_widthHandle = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP ); + + qreal pX = static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._mapCoordinate.x(); + qreal pY = static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._mapCoordinate.y(); + + _internal->_widthLine = new RubberbandLineHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, QPointF(pX,pY), p, 2, true ); + + pX = static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._sceneCoordinate.x(); + pY = static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._sceneCoordinate.y(); + + const QPointF & pScene = _internal->_widthHandle->getCoordinate()._sceneCoordinate; + + pX -= pScene.x(); + pY -= pScene.y(); + + _internal->_corridorSceneWidth = sqrt(pX*pX+pY*pY); + if (_inputMode == InputMode::InputCenterLine) _internal->_corridorSceneWidth *= 2; + + // connect signal to call updateBufferGeometry when zooming or resizing the window + // connect( _internal->_widthHandle.get(), &RubberbandHandle::adjusted, [this]() { this->updateBufferGeometry(); } ); + // doesn't work because not all handles are adjusted this time ... + + // but we have to ensure that coordinateSceneWidth have all ways the correct value: + connect( _internal->_widthHandle.get(), &RubberbandHandle::adjusted, this, &RubberbandCorridor::onDo_adjustCorridorSceneWidth ); + + } /* RubberbandCorridor::constructWidthHandle */ + + + void RubberbandCorridor::onDo_adjustCorridorSceneWidth() + { + if (Rubberband::_internal->_handleList.size() > 1) + { + qreal pX = static_cast(this->Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._sceneCoordinate.x(); + qreal pY = static_cast(this->Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._sceneCoordinate.y(); + const QPointF & pScene = this->_internal->_widthHandle->getCoordinate()._sceneCoordinate; + + pX -= pScene.x(); + pY -= pScene.y(); + + qreal sceneWidth = sqrt(pX*pX+pY*pY); + if (_inputMode == InputMode::InputCenterLine) sceneWidth *= 2; + + _internal->_corridorSceneWidth = (_internal->_corridorSceneWidth >= 0) ? sceneWidth : -sceneWidth; + } + } /* RubberbandCorridor::onDo_adjustCorridorSceneWidth */ + + + void RubberbandCorridor::updateHandleScenePosition() + { + if (!_internal->_widthHandle) return; + + QPointF pos = getWidthHandleScenePosition(); + + DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " old handle Position = " << _internal->_widthHandle->getCoordinate()._sceneCoordinate.x() + << " ; " << _internal->_widthHandle->getCoordinate()._sceneCoordinate.y() + << " new handle position = " << pos.x() << " ; " << pos.y() + << " width = " << _corridorWidth + << " scene width = " << _internal->_corridorSceneWidth; + + _internal->_widthHandle->updateCoordinate( pos ); + + const QPointF & lp1 = static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._sceneCoordinate; + + _internal->_widthLine->updateCoordinate( 0, lp1 ); + _internal->_widthLine->updateCoordinate( 1, pos ); + + } /* RubberbandCorridor::updateHandleScenePosition */ + + + bool RubberbandCorridor::getCenterLineFromHandleBorder( QVector & centerLine, qreal corridorWidth, bool mapCoordinates ) + { + ClipperLib::ClipperOffset pathOffset; + ClipperLib::Path borderPath; + ClipperLib::Path offsetPath; + int precision = (mapCoordinates) ? 1000 : 10; + qreal baseX = 0.0; + qreal baseY = 0.0; + bool initBase = true; + + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * pointHandlePtr = dynamic_cast(handle.get()); + const RubberbandPointHandle::RubberbandCoordinate & pointCoordinate = pointHandlePtr->getCoordinate(); + + qreal x = (mapCoordinates) ? pointCoordinate._mapCoordinate.x() : pointCoordinate._sceneCoordinate.x(); + qreal y = (mapCoordinates) ? pointCoordinate._mapCoordinate.y() : -pointCoordinate._sceneCoordinate.y(); + + if (mapCoordinates) + { + // use relative coordinates to avoid to large numbers + if (initBase) + { + baseX = x; baseY = y; x = 0.0; y = 0.0; + initBase = false; + } + else { x -= baseX; y -= baseY; } + } + borderPath << ClipperLib::IntPoint(int(x*precision),int(y*precision)); + } + + bool ok = pathOffset.PathOffset( borderPath, offsetPath, int(corridorWidth*precision) ); + + ClipperLib::Path::const_iterator it = offsetPath.begin(); + + if (mapCoordinates) + { + for (; it != offsetPath.end(); ++it) + { + centerLine << QPointF(qreal(it->X)/qreal(precision)+baseX, qreal(it->Y)/qreal(precision)+baseY); + } + } + else + { + for (; it != offsetPath.end(); ++it) + { + centerLine << QPointF(qreal(it->X)/qreal(precision)+baseX, qreal(-it->Y)/qreal(precision)-baseY); + } + } + + return ok; + } /* RubberbandCorridor::getCenterLineFromHandleBorder */ + + + QVector RubberbandCorridor::getCenterLineFromBorder(const QVector & borderLine, qreal corridorWidth, CoordinateSystem cs) + { + const int precision = 1000; + + QVector centerLine; + + ClipperLib::ClipperOffset pathOffset; + ClipperLib::Path borderPath; + ClipperLib::Path offsetPath; + + qreal baseX = 0.0; + qreal baseY = 0.0; + bool initBase = true; + + for (const auto &point : borderLine) + { + const QPointF & mapPoint = projectCoordinateToMap(cs, point); + + qreal x = mapPoint.x(); + qreal y = mapPoint.y(); + + // use relative coordinates to avoid to large numbers + if (initBase) + { + baseX = x; baseY = y; x = 0.0; y = 0.0; + initBase = false; + } + else { x -= baseX; y -= baseY; } + + borderPath << ClipperLib::IntPoint(int(x*precision), int(y*precision)); + } + + if (pathOffset.PathOffset(borderPath, offsetPath, int(corridorWidth*precision))) + { + centerLine.reserve(100); + for (const auto &p : offsetPath) + { + centerLine << QPointF( qreal(p.X)/qreal(precision)+baseX, qreal(p.Y)/qreal(precision)+baseY ); + } + } + return centerLine; + } // RubberbandCorridor::getCenterLineFromBorder + + + + void RubberbandCorridor::updateBufferGeometry() + { + if (!_internal->_widthHandle) return; + + QVector centerLine; + qreal corridorWidth = 0.0; + bool centerLineOk = true; + + centerLine.reserve(Rubberband::_internal->_handleList.size() ); + + switch (_inputMode) + { + case InputMode::InputBoundary: + { + corridorWidth = _internal->_corridorSceneWidth * 0.5; + centerLineOk = getCenterLineFromHandleBorder( centerLine, corridorWidth, false ); + if (_internal->_centerLineItem == nullptr) + { + _internal->_centerLineItem = new DrawablePainterPath( Rubberband::_internal->_mapView ); + } + _internal -> _centerLineItem -> setDrawColor( centerLineOk ? _drawColor : QColor(255,0,0) ); + _internal -> _centerLineItem -> setFillColor( QColor(0,0,0,0) ); // turn off filling + _internal -> _centerLineItem -> dottedPath( centerLineOk ); + _internal -> _centerLineItem -> setVisibility( true ); + _internal -> _centerLineItem -> enableClosedLine( !centerLineOk ); + _internal -> _centerLineItem -> setPath( centerLine ); + + } break; + + case InputMode::InputCenterLine: + { + corridorWidth = _internal->_corridorSceneWidth * 0.5; + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * pointHandlePtr = dynamic_cast(handle.get()); + const RubberbandPointHandle::RubberbandCoordinate & pointCoordinate = pointHandlePtr->getCoordinate(); + + qreal x = pointCoordinate._sceneCoordinate.x(); + qreal y = pointCoordinate._sceneCoordinate.y(); + + centerLine.append( QPointF(x,y) ); + } + _internal -> _centerLineItem = nullptr; + } break; + } + + ClipperLib::ClipperOffset pathOffset; + ClipperLib::Path borderPath; + ClipperLib::Paths offsetPathVec; + QVector vertexSet; + + for (auto point : std::as_const(centerLine)) + borderPath << ClipperLib::IntPoint(int(point.x()*10),int(-point.y()*10)); + + pathOffset.AddPath( borderPath, ClipperLib::JoinType::jtSquare, ClipperLib::EndType::etOpenButt); + pathOffset.Execute( offsetPathVec, int(fabs(corridorWidth)*10) ); + + if (centerLineOk) + { + if (_internal->_geoBufferItem) _internal->_geoBufferItem->setVisibility(false); + + for (auto path : offsetPathVec) + { + ClipperLib::Path::const_iterator it = path.begin(); + + for (; it != path.end(); ++it) + { + vertexSet.append( RubberbandHandle::RubberbandCoordinate( Rubberband::_internal->_mapView + , QPointF(qreal(it->X)/10.0,qreal(-it->Y)/10.0) )); + } + + if (!vertexSet.empty()) + { + if (!_internal->_geoBufferItem) + { + _internal->_geoBufferItem = new DrawablePainterPath( Rubberband::_internal->_mapView ); + } + + _internal -> _geoBufferItem -> setVisibility( true ); + _internal -> _geoBufferItem -> enableClosedLine( true ); + _internal -> _geoBufferItem -> setDrawColor( _corridorBorderColor ); + _internal -> _geoBufferItem -> setFillColor( _corridorFillColor ); + _internal -> _geoBufferItem -> setPath( vertexSet ); + } + break; + } + } + else if (_internal->_geoBufferItem) _internal->_geoBufferItem->setVisibility(false); + + RubberbandFrameHandle * frameHandle = getFrameHandle(); + + if (frameHandle) + { + if (vertexSet.isEmpty()) + { + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * pointHandlePtr = dynamic_cast(handle.get()); + + vertexSet.append(pointHandlePtr->getCoordinate() ); + } + + if (_internal->_widthHandle != nullptr) + { + vertexSet.append( _internal->_widthHandle->getCoordinate() ); + } + } + DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " frameHandle->updateFrameShape( vertexSet );"; + frameHandle->updateFrameShape( vertexSet ); + } + + if (!vertexSet.isEmpty()) + { + qreal minX = vertexSet.first()._sceneCoordinate.x(); + qreal maxX = minX; + qreal minY = vertexSet.first()._sceneCoordinate.y(); + qreal maxY = minY; + + for (const auto &v : std::as_const(vertexSet)) + { + const QPointF & p = v._sceneCoordinate; + if (p.x() < minX) minX = p.x(); + if (p.y() < minY) minY = p.y(); + if (p.x() > maxX) maxX = p.x(); + if (p.y() > maxY) maxY = p.y(); + } + _internal->_sceneBbox.setCoords( minX, minY, maxX, maxY ); + + if (!isButtonBoxPositionFixed()) + { + RubberbandBase::_baseInternal->_buttonBoxSceneAnchor = QPointF(maxX,maxY); + } + } + else _internal->_sceneBbox = QRectF(); + } /* RubberbandCorridor::updateBufferGeometry*/ + + + bool RubberbandCorridor::bufferPolygon( QVector & coordinateVec, CoordinateSystem cs ) + { + coordinateVec.clear(); + + const QVector & cLine = centerLine( CoordinateSystem::Map ); + + if (cLine.size() < 2) return false; + + ClipperLib::ClipperOffset pathOffset; + ClipperLib::Path borderPath; + ClipperLib::Paths offsetPathVec; + int precision = 1000; + const QPointF & basePoint = cLine.first(); + + for (const auto &point : cLine) + { + borderPath << ClipperLib::IntPoint(int((point.x()-basePoint.x())*precision), + int((point.y()-basePoint.y())*precision)); + } + + pathOffset.AddPath( borderPath, ClipperLib::JoinType::jtSquare, ClipperLib::EndType::etOpenButt); + pathOffset.Execute( offsetPathVec, int(fabs(_corridorWidth*0.5)*precision) ); + + if (offsetPathVec.empty()) return false; + + const ClipperLib::Path & bufferCoordinates = offsetPathVec.front(); + + if (bufferCoordinates.empty()) return false; + + coordinateVec.reserve(bufferCoordinates.size()); + + for (const ClipperLib::IntPoint & p : bufferCoordinates) + { + const QPointF & vertex = QPointF( qreal(p.X)/qreal(precision), qreal(p.Y)/qreal(precision) ) + basePoint; + coordinateVec << projectCoordinateFromMap( cs, vertex ); + } + return true; + } /* RubberbandCorridor::bufferPolygon */ + + + void RubberbandCorridor::resetAction(AbstractEventDispatcher * causer, int x, int y ) + { + if (rubberbandIsAlive()) + { + if (!isEnabledConstructionWidthState()) + { + if (isEnabledConstructionState()) + { + RubberbandMultiPointFeature::resetAction(causer,x,y); + return; + } + } + } + RubberbandMultiPointFeature::resetAction(causer,x,y); + } /* RubberbandCorridor::resetAction */ + + + bool RubberbandCorridor::selectAction( AbstractEventDispatcher * causer, int x, int y ) + { + Q_UNUSED(causer); + + if (!rubberbandIsAlive()) return false; + + bool eventHandled = false; + QPointF scenePos(pixel2ScenePosition(x,y)); + + if (_baseInternal->buttonProxyActive(scenePos)) return false; + + _baseInternal->_lastClickPosition = scenePos; + + if (!isEnabledConstructionWidthState()) + { + if (isEnabledConstructionState()) return RubberbandMultiPointFeature::selectAction(causer,x,y); + if (_internal->_widthHandle->isPositionInside(scenePos)) + { + _internal->_widthHandle->setSelection(); + eventHandled = true; + } + else return RubberbandMultiPointFeature::selectAction(causer,x,y); + } + else eventHandled = true; + + + if (eventHandled) + { + update(); + deactivateButtons(); + } + return eventHandled; + } /* RubberbandCorridor::selectAction */ + + + bool RubberbandCorridor::setCoordinateAction(AbstractEventDispatcher * causer, int x, int y) + { + Q_UNUSED(causer); + + markFailedConstrains( QPointF(), false ); + + if (!rubberbandIsAlive()) return false; + + bool eventHandled = false; + QPointF scenePos(pixel2ScenePosition(x,y)); + + if (isEnabledConstructionWidthState()) + { + Rubberband::_internal->_lastClickPosition = scenePos; + constructWidth( scenePos ); + updateHandleScenePosition(); + disenableConstructionWidthState(); + enableModificationState(); + eventHandled = true; + markFailedConstrains( QPointF(), false ); + } + else + { + if (isEnabledConstructionState()) + { + bool handled = RubberbandMultiPointFeature::setCoordinateAction(causer,x,y); + if (isEnabledConstructionWidthState()) + { + // guarantee that buttons are of + deactivateButtons(); + } + return handled; + } + + if (_internal->_widthHandle->isSelected()) + { + _internal->_widthHandle->setSelection(false); + constructWidth( scenePos ); + markFailedConstrains( QPointF(), false ); + eventHandled = true; + RubberbandMultiPointFeature::setCoordinateAction(causer,x,y); + } + else return RubberbandMultiPointFeature::setCoordinateAction(causer,x,y); + } + + if (eventHandled) update(); + + updateButtonPosition(); + + activateButtons(); + + return eventHandled; + } /* RubberbandCorridor::setCoordinateAction */ + + + bool RubberbandCorridor::movePickedAction(AbstractEventDispatcher * causer, int x, int y ) + { + adjustConnectedPreviewPixmap(x,y); + + if (!rubberbandIsAlive()) return false; + + if ((isEnabledConstructionWidthState()) || ((_internal->_widthHandle) && (_internal->_widthHandle->isSelected()))) + { + QPointF scenePos(pixel2ScenePosition(x,y)); + + constructWidth( scenePos ); + update(); + return false; + } + + return RubberbandMultiPointFeature::movePickedAction(causer,x,y); + + } /* RubberbandCorridor::movePickedAction */ + + + bool RubberbandCorridor::moveAction(AbstractEventDispatcher * causer, int x, int y) + { + adjustConnectedPreviewPixmap(x,y); + + QPointF scenePos(pixel2ScenePosition(x,y)); + + if (isEnabledConstructionWidthState()) + { + constructWidth( scenePos ); + update(); + return false; + } + + if (_internal->_widthHandle) + { + if (_internal->_widthHandle->isPositionInside(scenePos)) + { + _internal->_widthHandle->setSelection(true); + return true; + } + _internal->_widthHandle->setSelection(false); + } + return RubberbandMultiPointFeature::moveAction(causer,x,y); + } /* RubberbandCorridor::moveAction */ + + + bool RubberbandCorridor::completeStateAction(AbstractEventDispatcher * causer, int x, int y) + { + if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) + { + if (isEnabledConstructionWidthState()) + { + if (!_finishAfterConstruction) + { + disenableSelectedState(); + enableModificationState(); + update(); + return true; + } + } + if (isEnabledConstructionState()) + { + if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount) + { + disenableConstructionState(); + + if ((fabs(_corridorWidth) <= _minimumCorridorWidth) || (fabs(_corridorWidth) >= _maximumCorridorWidth)) + { + enableConstructionWidthState(); + + deSelectHandles(); + disenableSelectedState(); + + deactivateButtons(); + + updatePreviewLine(QPointF(0,0)); + + constructWidth( static_cast(Rubberband::_internal->_handleList.end()[-1].get())->getCoordinate()._sceneCoordinate ); + constructWidthHandle(); + + if (_internal->_widthHandle) + { + _internal->_widthHandle->setSelection(true); + } + } + else + { + if (_finishAfterConstruction) + { + return applyAndEndAction(causer,x,y); + } + else + { + deSelectHandles(); + constructWidthHandle(); + disenableSelectedState(); + enableModificationState(); + } + } + } + update(); + return true; + } + return applyAndEndAction(causer,x,y); + } + return false; + } /* RubberbandCorridor::completeStateAction */ + + + void RubberbandCorridor::activateButtons() + { + if (isEnabledConstructionWidthState()) + { + _baseInternal->_applyIsPossible = false; + _baseInternal->_undoIsPossible = false; + RubberbandMultiPointFeature::activateButtons(); + } + else RubberbandMultiPointFeature::activateButtons(); + + } /* RubberbandCorridor::activateButtons */ + + + QRectF RubberbandCorridor::sceneBbox() const + { + if (_internal->_sceneBbox.isValid()) return _internal->_sceneBbox; + else return Rubberband::sceneBbox(); + } /* RubberbandCorridor::sceneBbox */ + + + void RubberbandCorridor::update() + { + RubberbandMultiPointFeature::update(); + + DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION; + + _isClosed = false; + + if (_internal->_widthHandle) + { + _internal->_widthHandle -> setDrawColor(_handleDrawColor); + _internal->_widthHandle -> setFillColor(_handleFillColor); + _internal->_widthHandle -> setSelectionColor(_handleSelectionColor); + _internal->_widthHandle -> setPickingRatio( _pickingRatio ); + + updateHandleScenePosition(); + updateBufferGeometry(); + } + + } /* RubberbandCorridor::update */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_corridor.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_corridor.h new file mode 100755 index 000000000..9879a3238 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_corridor.h @@ -0,0 +1,239 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define multi point feature geoemtry +** $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 .........: +** +** 17.06.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband_multi_point_feature.h" + + +namespace SituationMap { namespace RB +{ + /** + @class RubberbandCorridor rubberband_corridor.h + @brief Implementation of a rubberband for corridors. + @see RubberbandFactory, Rubberband, RubberbandMultiPointFeature + + Implementation of rubberbands used to input corridors. There are two different modes \c InputCenterLine and \c InputBoundary. In the + InputCenterLine mode the center line of the corridor has to be defined. The rubberband displays then the border geometry. + In the InputBoundary mode one side of the border has to be defined. The \c coordinateVector methodes always working on the input geometry. + */ + class SITUATIONMAP_EXPORT RubberbandCorridor : public RubberbandMultiPointFeature + { + Q_OBJECT + + public: + + enum class InputMode + { + InputCenterLine, + InputBoundary + }; /* InputMethode */ + + + /** + @name Construction & Destruction + @{ + + Construction of one instance of class RubberbandCorridor. + + @param map Pointer to SituationMap class instance + @param mode Two mode are distinguished. In \c InputCenterLine mode the coordinates will be interpreted as center line of the corridor. + In \c InputBoundary the coordinates are defining one border. + @param width With of a corridor in meters. In \c InputBoundary the coordinates are defining one border. If \c corridorWidth is negative + the coordinates are the right border otherwise the left one. + */ + + private: + + friend class RubberbandFactory; + + explicit RubberbandCorridor( SituationMap::Map * map, QObject * parent ); + explicit RubberbandCorridor( SituationMap::Map * map, InputMode mode = InputMode::InputBoundary, qreal width = 0, QObject * parent = nullptr ); + + + public: + + ~RubberbandCorridor(); + /** @} */ + + /** + @addtogroup States Methodes to query and modify rubberband states + @{ + */ + bool isEnabledConstructionWidthState() const; + void enableConstructionWidthState(); + void disenableConstructionWidthState(); + /** @} */ + + + + inline bool isEnabledInputBoundary() const { return _inputMode == InputMode::InputBoundary; } + inline void setInputBoundary() { _inputMode = InputMode::InputBoundary; } + inline void setInputCenterline() { _inputMode = InputMode::InputCenterLine; } + + /** + Sets coordinates to modify to internal coordinate list. + + @param coordinateVec List of coordinates to modify + @param cs Defines the coordinate system of returned coordinates. + */ + void setCoordinateVector( const QVector & coordinateVec, CoordinateSystem cs = CoordinateSystem::Geo ) override; + + /** + Sets coordinates to modify to internal coordinate list. In \c InputCenterLine mode the coordinates will be interpreted as + center line of the corridor. In \c InputBoundary the coordinates are defining one border. If \c corridorWidth is negative + the coordinates are the right border otherwise the left one. + + @param coordinateVec List of coordinates to modify + @param corridorWidth Width of corridor in meters. + @param cs Defines the coordinate system of returned coordinates. + */ + void setCoordinateVectorWidth(const QVector & coordinateVec, qreal corridorWidth, CoordinateSystem cs = CoordinateSystem::Geo ); + + /** + Returns the center line of the corridor + + @param cs Defines the coordinate system of returned coordinates. + */ + QVector centerLine( CoordinateSystem cs = CoordinateSystem::Geo ); + + /** + Sets the center line of the corridor. + + @param coordinateVec List of coordinates of border to modify + @param corridorWidth Width of corridor in meters + @param cs Defines the coordinate system of returned coordinates. + */ + void setCenterLine( const QVector & coordinateVec, qreal corridorWidth, CoordinateSystem cs = CoordinateSystem::Geo ); + + /** + Returns the front line of the rubberband. + + @param cs Defines the coordinate system of returned coordinates. + */ + QVector frontLine(CoordinateSystem cs = CoordinateSystem::Geo); + + /** + Sets the front line of the corridor. + + @param coordinateVec List of coordinates of border to modify + @param corridorWidth Width of corridor in meters + @param cs Defines the coordinate system of returned coordinates. + */ + void setFrontLine(const QVector & coordinateVec, qreal corridorWidth, CoordinateSystem cs = CoordinateSystem::Geo); + + /** + Provides the buffer polygon (outline) of the corridor. + + @param coordinateVec Coordinate list of buffer polygon + @param cs Defines the coordinate system of returned coordinates. + @return true on success otherwise false + */ + bool bufferPolygon( QVector & coordinateVec, CoordinateSystem cs = CoordinateSystem::Geo ); + + /** + Return the width of the corridor. + */ + qreal corridorWidth() const; + + /** + Set the width of the corridor. + */ + inline void setCorridorWidth(qreal width) { _corridorWidth = width;} + + /** + @name Constrains for corridor width + @{ + */ + void minimumCorridorWidth( qreal min ); + void maximumCorridorWidth( qreal max ); + + qreal minimumCorridorWidth() const { return _minimumCorridorWidth; } + qreal maximumCorridorWidth() const { return _maximumCorridorWidth; } + /** @} */ + + + bool selectAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool setCoordinateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool movePickedAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool moveAction ( AbstractEventDispatcher * causer, int x, int y ) override; + void resetAction ( AbstractEventDispatcher * causer, int x, int y ) override; + + bool completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + + QColor getCorridorBorderColor() const { return _corridorBorderColor; } + QColor getCorridorFillColor() const { return _corridorFillColor; } + + void setCorridorBorderColor( const QColor & color ) { _corridorBorderColor = color; } + void setCorridorFillColor ( const QColor & color ) { _corridorFillColor = color; } + + protected: + /* + Forward declaration of InternalData to hold private member variables. Becareful the base class Rubberband + has similary a class for internal data member with name "InternalData". Don't be confused it is not the same. + */ + struct InternalData; + + InternalData * _internal; + + InputMode _inputMode; + qreal _corridorWidth; + + qreal _minimumCorridorWidth; + qreal _maximumCorridorWidth; + + QColor _corridorBorderColor; + QColor _corridorFillColor; + + void activateButtons() override; + + QRectF sceneBbox() const override; + + void update() override; + + QPointF calcWidthHandlePosition(); + + void constructWidth( const QPointF & ); + void constructWidthHandle(); + + QPointF getWidthHandleScenePosition() const; + + bool getCenterLineFromHandleBorder(QVector & centerLine, qreal corridorWidth, bool mapCoordinates); + + QVector getCenterLineFromBorder(const QVector & borderLine, qreal corridorWidth, CoordinateSystem cs ); + + void updateHandleScenePosition(); + void updateBufferGeometry(); + + private slots: + + void onDo_adjustCorridorSceneWidth(); + + }; /* RubberbandCorridor */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_drawable.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_drawable.cpp new file mode 100755 index 000000000..6b53338f1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_drawable.cpp @@ -0,0 +1,155 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of base class for rubberband handles +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_drawable.h" + + +// ESRI +#include + + + +namespace SituationMap { namespace RB +{ + + Esri::ArcGISRuntime::Point convert(const QPointF &point, const Esri::ArcGISRuntime::SpatialReference& spatialReference) + { + return Esri::ArcGISRuntime::Point(point.x(), point.y(), spatialReference); + } // convert + + + RubberbandDrawable::RubberbandDrawable( QGraphicsView * view ) + : _scene((view) ? view->scene() : nullptr) + , _mapView() + , _isVisible(true) + , _lineWidth(1) + , _zBufferValue((unsigned int) ZBufferPlane::Drawables) + , _logger(getLogger("RubberbandDrawable")) + + { + doConnecting(); + } /* RubberbandDrawable::RubberbandDrawable */ + + RubberbandDrawable::RubberbandDrawable( Esri::ArcGISRuntime::MapGraphicsView * mapView ) + : _scene(mapView->scene()) + , _mapView(mapView) + , _isVisible(true) + , _lineWidth(1) + , _zBufferValue((unsigned int) ZBufferPlane::Drawables) + , _logger(getLogger("RubberbandDrawable")) + + { + doConnecting(); + } /* RubberbandDrawable::RubberbandDrawable */ + + RubberbandDrawable::RubberbandDrawable( Esri::ArcGISRuntime::MapGraphicsView * mapView, QGraphicsScene * scene) + : _scene(scene) + , _mapView(mapView) + , _isVisible(true) + , _lineWidth(1) + , _zBufferValue((unsigned int) ZBufferPlane::Drawables) + , _logger(getLogger("RubberbandDrawable")) + + { + doConnecting(); + } /* RubberbandDrawable::RubberbandDrawable */ + + +#ifdef ADJUST_COORDINATES_EVERY_PAINT_EVENT + bool RubberbandDrawable::eventFilter( QObject*, QEvent * e ) + { + if (e) + { + if (e->type() == QEvent::Paint) + { + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Sometimes the signals extentChanges and rectChanged of Esri Map Widget are not emitted. In this case + * the position update of handles and drawabled fails and they are drawn at wrong positions on map. As + * workaround updates coordinates at every paint event. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + DEBUG_LOG(_logger) << LOGFUNCTION << " paint event"; + onDo_adjustCoordinates(); + } + } + return false; + } /* RubberbandDrawable::eventFilter */ +#endif + + void RubberbandDrawable::doConnecting() + { + if (nullptr == _mapView) return; + + #ifdef ADJUST_COORDINATES_EVERY_PAINT_EVENT + _view -> installEventFilter( this ); + #endif + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * It is really important to specify the connnection type as following. Because the onDo_adjustCoordinates + * should be call as soon as possible to get smooth changes of map. The connection type is DirectConnnection here. + * The rectChanged signal is some times emitted, when the calculation of screen coordinates to map coordinates + * and vice versa has not been updated to the new map window size. In this case all calculations provides wrong + * results. Using the connection type QueuedConnection defers the call of onDo_adjustWindow. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + // Check Quartz + connect( _mapView.data(), &Esri::ArcGISRuntime::MapGraphicsView::viewpointChanged, + this, &RubberbandDrawable::onDo_adjustCoordinates, Qt::ConnectionType::DirectConnection ); + connect( _mapView.data(), &Esri::ArcGISRuntime::MapGraphicsView::rectChanged , + this, &RubberbandDrawable::onDo_adjustWindow , Qt::ConnectionType::QueuedConnection ); + + } /* RubberbandDrawable::doConnecting */ + + + void RubberbandDrawable::onDo_adjustCoordinates() + { + adjustCoordinates(); + forceUpdateViewWidget(); + } /* RubberbandDrawable::onDo_adjustCoordinates */ + + + void RubberbandDrawable::onDo_adjustWindow( const QRectF &r ) + { + adjustWindow(r); + forceUpdateViewWidget(); + } /* RubberbandDrawable::onDo_adjustWindow */ + + + void RubberbandDrawable::makeLineStyle( bool dottedLine, QPen & lineStyle ) + { + lineStyle.setColor( _drawColor ); + lineStyle.setWidth( int(_lineWidth) ); + + if (dottedLine) lineStyle.setStyle( Qt::DashDotLine ); + else lineStyle.setStyle( Qt::SolidLine ); + } /* DrawablePainterPathItem::makeLineStyle */ + + + RubberbandDrawable::~RubberbandDrawable() { if (_mapView) _mapView->removeEventFilter(this); } + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_drawable.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_drawable.h new file mode 100755 index 000000000..01ae5ec2a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_drawable.h @@ -0,0 +1,214 @@ +/** +** +** @RDE_HEADER@ +** Definition of base class for rubberband drawing objects +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + + +#include + +#include + +#include +#include + +#include +#include + + +class QGraphicsScene; + +namespace Esri { namespace ArcGISRuntime { class MapGraphicsView; } } + +namespace SituationMap { namespace RB +{ + + Esri::ArcGISRuntime::Point convert(const QPointF &point, const Esri::ArcGISRuntime::SpatialReference& spatialReference); + + + + /** + @class RubberbandDrawable rubberband_drawable.h + @brief Base class of all rubberband drawables and handles for rubberband internal use. + */ + class RubberbandDrawable : public QObject, public SituationMap::Referenced + { + Q_OBJECT + public: + + enum class ZBufferPlane + { + FrameHandle = 10, + Drawables = 20, + LineHandle = 30, + ArrowSymbol = 31, + PointHandle = 40, + Widget = 100 + }; /* InputMethode */ + + + /** + Manages scene and map coordinates. + */ + struct RubberbandCoordinate + { + QPointF _sceneCoordinate; + Esri::ArcGISRuntime::Point _mapCoordinate; + + + RubberbandCoordinate() + {} + + explicit RubberbandCoordinate( const QPointF & sceneCoordinate) + { + updateSceneCoordinate( sceneCoordinate ); + } /* RubberbandCoordinate */ + + explicit RubberbandCoordinate( Esri::ArcGISRuntime::MapGraphicsView * mapView, const QPointF & sceneCoordinate) + { + updateSceneCoordinate( mapView, sceneCoordinate ); + } /* RubberbandCoordinate */ + + + explicit RubberbandCoordinate( Esri::ArcGISRuntime::MapGraphicsView * mapView, const Esri::ArcGISRuntime::Point & mapCoordinate) + : _mapCoordinate(mapCoordinate) + { + mapToScene( mapView ); + } /* RubberbandCoordinate */ + + + inline QPointF mapToScene(Esri::ArcGISRuntime::MapGraphicsView * mapView) + { + if (!_mapCoordinate.isValid()) + return QPointF(); + const QPointF pixelCoordinate = mapView->locationToScreen(_mapCoordinate); + _sceneCoordinate = mapView->mapToScene( pixelCoordinate.x(), pixelCoordinate.y() ); + return _sceneCoordinate; + } /* QPointF mapToScene(Esri::ArcGISRuntime::MapGraphicsView * mapView) */ + + + inline void updateSceneCoordinate( Esri::ArcGISRuntime::MapGraphicsView * mapView, const QPointF & sceneCoordinate) + { + if (mapView) + { + _sceneCoordinate = sceneCoordinate; + QPoint pixelCoordinate = mapView->mapFromScene(sceneCoordinate); + _mapCoordinate = mapView->screenToLocation( pixelCoordinate.x(), pixelCoordinate.y() ); + } + else updateSceneCoordinate( sceneCoordinate ); + } /* void updateSceneCoordinate( Esri::ArcGISRuntime::MapGraphicsView * mapView, const QPointF & sceneCoordinate) */ + + + inline void updateSceneCoordinate( const QPointF & sceneCoordinate ) + { + _sceneCoordinate = sceneCoordinate; + _mapCoordinate = Esri::ArcGISRuntime::Point(); + } /* void updateSceneCoordinate( const QPointF & sceneCoordinate ) */ + + bool isNull() const + { + return _sceneCoordinate.isNull(); + } + + }; /* RubberbandCoordinate */ + + /** + @name Construction & Destruction + @{ + */ + explicit RubberbandDrawable( QGraphicsView *view ); + explicit RubberbandDrawable( Esri::ArcGISRuntime::MapGraphicsView * ); + explicit RubberbandDrawable( Esri::ArcGISRuntime::MapGraphicsView *, QGraphicsScene * ); + ~RubberbandDrawable(); + /** @} */ + + inline bool isVisible() const { return _isVisible; } + inline void setVisibility(bool on = true) { _isVisible = on; updateGraphic(); } + + inline void setDrawColor (const QColor & col) { _drawColor = col; updateGraphic(); } + inline void setFillColor (const QColor & col) { _fillColor = col; updateGraphic(); } + inline void setLineWidth(unsigned int width) { _lineWidth = width; updateGraphic(); } + + inline void zBufferPlane( ZBufferPlane zPlane ) { _zBufferValue = (unsigned int) zPlane; } + + inline void setZValue(qreal zValue) { _zValue = zValue; updateGraphic(); } + inline qreal zValue() const { return _zValue; } + + protected: + + QPointer _scene; + QPointer _mapView; + + bool _isVisible; + QColor _drawColor; + QColor _fillColor; + unsigned int _lineWidth; + qreal _zBufferValue; + qreal _zValue{0}; // combine _zBufferValue and _zValue??? + + LOGGER_T _logger; + + #ifdef ADJUST_COORDINATES_EVERY_PAINT_EVENT + bool eventFilter(QObject*, QEvent*) override; + #endif + + virtual void updateGraphic() = 0; + + void makeLineStyle( bool dottedLine, QPen & lineStyle ); + + inline void forceUpdateViewWidget() { _mapView->viewport()->update(); } + + virtual void adjustCoordinates() = 0; + virtual void adjustWindow( const QRectF& ) = 0; + + + QColor transparentFillColor() const + { + QColor fillColor(_fillColor); + + if (fillColor.alphaF() > 0.85) + { + fillColor.setAlphaF( fillColor.alphaF()*0.5 ); + } + return fillColor; + } // QColor transparentFillColor() + + + protected slots: + + void onDo_adjustCoordinates(); + void onDo_adjustWindow(const QRectF & ); + + private: + + + + void doConnecting(); + + }; /* RubberbandDrawable */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_ellipse.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_ellipse.cpp new file mode 100755 index 000000000..5d03f1dd5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_ellipse.cpp @@ -0,0 +1,455 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of rubberband class to define point feature geoemtry +** $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 .........: +** +** 17.06.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_ellipse.h" +#include "drawable_ellipse.h" +#include "rubberband_point_handle.h" + +#include "rubberband_internal.h" +#include "rubberband_base_internal.h" + +#include +#include +#include + +namespace SituationMap { namespace RB +{ + + struct RubberbandEllipse::InternalData + { + SituationMap::RefPointer _ellipseSymbol; + int _alphaFill = 50; // alpha value used for fill color of ellipse + + QPointF _sectorOStart = QPointF(1,0); + QPointF _sectorOEnd = QPointF(1,0); + SituationMap::RefPointer _sectorStartHandle; + SituationMap::RefPointer _sectorEndHandle; + + bool _sectorEnabled = false; + bool _sectorFixedAngleEnabled = false; + + }; /* RubberbandEllipse::InternalData */ + + + RubberbandEllipse::RubberbandEllipse( SituationMap::Map * map, QObject * parent ) + : RubberbandRectangle(map,parent), _internal(new InternalData) + { + useDottedLineHandles(true); + } /* RubberbandEllipse::RubberbandEllipse */ + + + RubberbandEllipse::~RubberbandEllipse() + { + delete _internal; + _internal = nullptr; + } /* RubberbandEllipse::~RubberbandEllipse */ + + + QPointF RubberbandEllipse::getRotatedPoint(QPointF sector) const + { + qreal sinRect = 0, cosRect = 1; + getSceneRotation( sinRect, cosRect); + double angleRad = SituationMap::MathUtils::getMathAngle(QPointF(cosRect,sinRect));// angle rad + //double angleDeg = SituationMap::MathUtils::getMathAngle(QPointF(cosRect,sinRect)) * LtsCoordTrans::rad2deg(); + const double clockwiseAngle = ((360*LtsCoordTrans::deg2rad()) - angleRad);// angle in rad; + + // map clockwise rotation + QPointF point = sector; + point.setX( sector.x()*std::cos(clockwiseAngle) + sector.y()*std::sin(clockwiseAngle)); + point.setY( -sector.x()*std::sin(clockwiseAngle) + sector.y()*std::cos(clockwiseAngle)); + + return point; + } + + void RubberbandEllipse::setSectorStart(qreal sinPhi, qreal cosPhi) + { + _internal->_sectorOStart = QPointF( cosPhi, sinPhi ); + update(); + } /* RubberbandEllipse::setSectorStart */ + + + void RubberbandEllipse::setSectorEnd(qreal sinPhi, qreal cosPhi) + { + _internal->_sectorOEnd = QPointF( cosPhi, sinPhi ); + update(); + } /* RubberbandEllipse::setSectorEnd */ + + + QPointF RubberbandEllipse::sectorStart() const + { + return getRotatedPoint(_internal->_sectorOStart); + } /* RubberbandEllipse::sectorStart */ + + + QPointF RubberbandEllipse::sectorEnd() const + { + return getRotatedPoint(_internal->_sectorOEnd); + } /* RubberbandEllipse::sectorEnd */ + + + bool RubberbandEllipse::hasSector() const + { + auto getAngle = [](qreal sinPhi, qreal cosPhi) -> int + { + qreal phi = acos( cosPhi ); + if (sinPhi < 0) phi = MathConst::pi_two - phi; + return int( (phi * 180.0 / MathConst::pi) * 16); + }; // getAngle + + int phiStart = getAngle(_internal->_sectorOStart.y(),_internal->_sectorOStart.y()); + int phiEnd = getAngle( _internal->_sectorOEnd.y(), _internal->_sectorOEnd.y()); + return phiStart != phiEnd; + } /* RubberbandEllipse::hasSector */ + + void RubberbandEllipse::enableSector(bool on) { _internal -> _sectorEnabled = on; } + bool RubberbandEllipse::isSectorEnabled() const { return _internal -> _sectorEnabled; } + + void RubberbandEllipse::setSectorAngleFixed(bool on) + { + _internal -> _sectorFixedAngleEnabled = on; + } + + bool RubberbandEllipse::isSectorFixedAngeEnabled() const + { + return _internal->_sectorFixedAngleEnabled; + } + + + bool RubberbandEllipse::selectAction(AbstractEventDispatcher * causer, int x, int y) + { + if (rubberbandIsAlive()) + { + QPointF scenePos(pixel2ScenePosition(x,y)); + + _baseInternal->_lastClickPosition = scenePos; + + if (_internal -> _sectorEnabled) + { + if ((_internal->_sectorEndHandle.valid()) && (_internal->_sectorEndHandle->isPositionInside(scenePos))) + { + _internal->_sectorEndHandle->setSelection(true); + enableSelectedState(); + update(); + deactivateButtons(); + return true; + } + + if ((_internal->_sectorStartHandle.valid()) && (_internal->_sectorStartHandle->isPositionInside(scenePos))) + { + _internal->_sectorStartHandle->setSelection(true); + enableSelectedState(); + update(); + deactivateButtons(); + return true; + } + } + } + return RubberbandRectangle::selectAction(causer,x,y); + } /* RubberbandEllipse::selectAction */ + + + bool RubberbandEllipse::moveAction(AbstractEventDispatcher * causer, int x, int y) + { + if (rubberbandIsAlive()) + { + QPointF scenePos(pixel2ScenePosition(x,y)); + if (_internal -> _sectorEnabled) + { + if (_internal->_sectorEndHandle.valid()) + { + if (_internal->_sectorEndHandle->isPositionInside(scenePos)) + { + _internal->_sectorEndHandle->setSelection(true); + update(); + return false; + } + else _internal->_sectorEndHandle->setSelection(false); + } + + if (_internal->_sectorStartHandle.valid()) + { + if (_internal->_sectorStartHandle->isPositionInside(scenePos)) + { + _internal->_sectorStartHandle->setSelection(true); + update(); + return false; + } + else _internal->_sectorStartHandle->setSelection(false); + } + } + } + return RubberbandRectangle::moveAction(causer,x,y); + } /* RubberbandEllipse::moveAction */ + + + bool RubberbandEllipse::setCoordinateAction(AbstractEventDispatcher * causer, int x, int y) + { + if (rubberbandIsAlive()) + { + if (_internal -> _sectorEnabled) + { + if ((_internal->_sectorEndHandle.valid()) && (_internal->_sectorEndHandle->isSelected())) + { + _internal->_sectorEndHandle->setSelection(false); + update(); + activateButtons(); + return true; + } + + if ((_internal->_sectorStartHandle.valid()) && (_internal->_sectorStartHandle->isSelected())) + { + _internal->_sectorStartHandle->setSelection(false); + update(); + activateButtons(); + return true; + } + } + } + return RubberbandRectangle::setCoordinateAction(causer,x,y); + } /* RubberbandEllipse::setCoordinateAction */ + + + bool RubberbandEllipse::movePickedAction(AbstractEventDispatcher * causer, int x, int y) + { + if (rubberbandIsAlive()) + { + auto updateHandle = [this](RubberbandPointHandle * handle, const RubberbandHandle::RubberbandCoordinate & pos, QPointF & oVector ) -> bool + { + if (handle == nullptr) return false; + if (!handle->isSelected()) return false; + if (isSectorFixedAngeEnabled()) return false; + + qreal sinRect = 0; + qreal cosRect = 1; + const QPointF & center = getCenter(); + + getSceneRotation( sinRect, cosRect); + + qreal cosA = pos._mapCoordinate.x() - center.x(); + qreal sinA = pos._mapCoordinate.y() - center.y(); + qreal len = sqrt(cosA*cosA+sinA*sinA); + + cosA /= len; + sinA /= len; + + oVector.setX( cosA*cosRect + sinA*sinRect ); + oVector.setY( sinA*cosRect - cosA*sinRect ); + update(); + return true; + }; // updateHandle + + QPointF scenePos(pixel2ScenePosition(x,y)); + + RubberbandHandle::RubberbandCoordinate pos(Rubberband::_internal->_mapView, scenePos ); + + if (updateHandle( _internal->_sectorEndHandle.get() , pos, _internal->_sectorOEnd )) return false; + if (updateHandle( _internal->_sectorStartHandle.get(), pos, _internal->_sectorOStart )) return false; + } + return RubberbandRectangle::movePickedAction(causer,x,y); + } /* RubberbandEllipse::movePickedAction */ + + + void RubberbandEllipse::updateSectorHandles() + { + + auto ellipse = []( qreal a, qreal b, qreal sinA, qreal cosA ) -> QPointF + { + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Qt does not use the paramater equation of a ellipse to draw sectors. Instead of a simpler term + * is used. For simpleness we use the same of. + * + qreal e2 = 1 - (b*b) / (a*a); + qreal cos2 = cosA * cosA; + qreal f = b / sqrt(1-e2*cos2); + return QPointF( f*cosA, f*sinA ); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + return QPointF(a*cosA,b*sinA); + }; + + auto rotate = [] ( const QPointF & p, qreal sinA, qreal cosA ) -> QPointF { return QPointF( p.x()*cosA - p.y()*sinA, p.y()*cosA + p.x()*sinA ); }; + + qreal a = getWidth() * 0.5; + qreal b = getHeight() * 0.5; + QPointF startP = ellipse(a,b,_internal->_sectorOStart.y(),_internal->_sectorOStart.x() ); + QPointF endP = ellipse(a,b,_internal->_sectorOEnd.y() ,_internal->_sectorOEnd.x() ); + + qreal sinRect = 0; + qreal cosRect = 1; + + getSceneRotation( sinRect, cosRect); + + startP = rotate(startP, sinRect, cosRect) + getCenter(); + endP = rotate( endP, sinRect, cosRect) + getCenter(); + + const Esri::ArcGISRuntime::Point &mapStartP = convert( startP, Rubberband::_internal->_mapView->map()->spatialReference() ); + const Esri::ArcGISRuntime::Point &mapEndP = convert( endP, Rubberband::_internal->_mapView->map()->spatialReference() ); + + if (!_internal->_sectorStartHandle.valid()) + { + _internal->_sectorStartHandle = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapStartP ); + } + else _internal->_sectorStartHandle->setCoordinate( RubberbandHandle::RubberbandCoordinate(Rubberband::_internal->_mapView,mapStartP) ); + + _internal->_sectorStartHandle -> setVisibility((isSectorEnabled() && !isSectorFixedAngeEnabled())/*_internal->_sectorEnabled*/); + _internal->_sectorStartHandle -> setDrawColor(_handleDrawColor); + _internal->_sectorStartHandle -> setFillColor( _handleFillColor ); + _internal->_sectorStartHandle -> setSelectionColor(_handleSelectionColor); + + if (!_internal->_sectorEndHandle.valid()) + { + _internal->_sectorEndHandle = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapEndP ); + } + else _internal->_sectorEndHandle->setCoordinate( RubberbandHandle::RubberbandCoordinate(Rubberband::_internal->_mapView,mapEndP) ); + + _internal->_sectorEndHandle -> setVisibility(isSectorEnabled() && !isSectorFixedAngeEnabled()/*_internal->_sectorEnabled*/); + _internal->_sectorEndHandle -> setDrawColor(_handleDrawColor); + _internal->_sectorEndHandle -> setFillColor( _handleFillColor ); + _internal->_sectorEndHandle -> setSelectionColor(_handleSelectionColor); + + } /* RubberbandEllipse::updateSectorHandles */ + + + void RubberbandEllipse::update() + { + + RubberbandRectangle::update(); + + if (isEnabledModificationState()) + { + updateSectorHandles(); + + QPointF center = getCenter(); + qreal width2 = getWidth()*0.5; + qreal height2 = getHeight()*0.5; + qreal sinRect = 0.0; + qreal cosRect = 1.0; + + getSceneRotation( sinRect, cosRect ); + + if (_internal->_ellipseSymbol == nullptr) + { + _internal->_ellipseSymbol = new DrawableEllipse( Rubberband::_internal->_mapView ); + } + + QColor drawColor = _drawColor; + + _internal->_ellipseSymbol->setDrawColor( drawColor ); + drawColor.setAlpha(_internal->_alphaFill); + if (_internal -> _sectorEnabled){ + _fillColor.setAlpha(_internal->_alphaFill+10); + _internal->_ellipseSymbol->setFillColor( _fillColor ); + } + else + { + _fillColor.setAlpha(0); + _internal->_ellipseSymbol->setFillColor( _fillColor ); + } + + _internal->_ellipseSymbol->setRect( center, width2, height2, sinRect, cosRect, _internal->_sectorOStart, _internal->_sectorOEnd ); + + } + } /* RubberbandEllipse::update */ + + + void RubberbandEllipse::buildTmpConstruction( SituationMap::RefPointer &tmpGraphic, QPointF &start, QPointF &end ) + { + if (!tmpGraphic.valid()) + { + tmpGraphic = new DrawableEllipse( Rubberband::_internal->_mapView ); + tmpGraphic -> setDrawColor( _drawColor ); + tmpGraphic -> setFillColor( _fillColor ); + tmpGraphic -> setLineWidth( 1 ); + tmpGraphic -> zBufferPlane( RubberbandDrawable::ZBufferPlane::Drawables ); + } + + QVector path; + QPointF sceneCenter; + qreal sceneWidth; + qreal sceneHeight; + qreal sinMap; + qreal cosMap; + + getMapRotation( sinMap, cosMap ); + + auto rotate = [sinMap, cosMap](qreal x, qreal y) -> QPointF + { + qreal px = x*cosMap - y*sinMap; + qreal py = y*cosMap + x*sinMap; + return QPointF(px,py); + }; // rotate + + buildTmpConstructionPath( start-end + start, end, path, sceneCenter, sceneWidth, sceneHeight ); + + DrawableEllipse * tmpEllipse = static_cast(tmpGraphic.get()); + + RubberbandHandle::RubberbandCoordinate mapCenter( Rubberband::_internal->_mapView, start ); + RubberbandHandle::RubberbandCoordinate mapRadius( Rubberband::_internal->_mapView, start+rotate(sceneWidth*0.5,sceneHeight*0.5) ); + + qreal w = mapRadius._mapCoordinate.x() - mapCenter._mapCoordinate.x(); + qreal h = mapRadius._mapCoordinate.y() - mapCenter._mapCoordinate.y(); + + tmpEllipse->setRect( QPointF(mapCenter._mapCoordinate.x(),mapCenter._mapCoordinate.y()), w, h, 0, 1 ); + } /* RubberbandEllipse::buildTmpConstruction */ + + + void RubberbandEllipse::getTmpConstructionParameter(const QPointF & start, const QPointF & end, QPointF & sceneCenter, qreal & sceneWidth, qreal & sceneHeight) + { + QVector path; + qreal sinMap; + qreal cosMap; + + getMapRotation( sinMap, cosMap ); + + auto rotate = [sinMap, cosMap]( qreal x, qreal y ) -> QPointF + { + qreal px = x*cosMap - y*sinMap; + qreal py = y*cosMap + x*sinMap; + return QPointF(px,py); + }; // rotate + + auto length = [](qreal x, qreal y) -> qreal { return sqrt(x*x+y*y); }; + + buildTmpConstructionPath( start, end, path, sceneCenter, sceneWidth, sceneHeight ); + + RubberbandHandle::RubberbandCoordinate mapCenter ( Rubberband::_internal->_mapView, start ); + RubberbandHandle::RubberbandCoordinate mapRadiusX( Rubberband::_internal->_mapView, start+rotate(sceneWidth,0) ); + RubberbandHandle::RubberbandCoordinate mapRadiusY( Rubberband::_internal->_mapView, start+rotate(0,sceneHeight) ); + + qreal w = length( mapRadiusX._sceneCoordinate.x()-mapCenter._sceneCoordinate.x(), mapRadiusX._sceneCoordinate.y()-mapCenter._sceneCoordinate.y() ); + qreal h = length( mapRadiusY._sceneCoordinate.x()-mapCenter._sceneCoordinate.x(), mapRadiusY._sceneCoordinate.y()-mapCenter._sceneCoordinate.y() ); + + sceneCenter = start; + sceneWidth = 2*w; + sceneHeight = 2*h; + } /* RubberbandEllipse::getTmpConstructionParameter */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_ellipse.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_ellipse.h new file mode 100755 index 000000000..a92f0aa6a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_ellipse.h @@ -0,0 +1,95 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define point feature geoemtry +** $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 .........: +** +** 17.06.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband_rectangle.h" + + +namespace SituationMap { namespace RB +{ + + + /** + @class RubberbandEllipse rubberband_ellipse.h + @brief Implementation of a rubberband to input ellipses and circles + @see Rubberband, RubberbandRectangle + */ + class SITUATIONMAP_EXPORT RubberbandEllipse : public RubberbandRectangle + { + Q_OBJECT + /** + @name Construction & Destruction + @{ + */ + + friend class RubberbandFactory; + explicit RubberbandEllipse( SituationMap::Map * map, QObject * parent = nullptr ); + + public: + + virtual ~RubberbandEllipse(); + + RubberbandEllipse(const RubberbandEllipse &) = delete; + RubberbandEllipse & operator = (const RubberbandEllipse &) = delete; + /** @} */ + + void setSectorStart( qreal sinPhi, qreal cosPhi ); + void setSectorEnd( qreal sinPhi, qreal cosPhi ); + + QPointF getRotatedPoint(QPointF sector)const; + bool hasSector() const; + QPointF sectorStart() const; + QPointF sectorEnd() const; + + void enableSector( bool on = true ); + bool isSectorEnabled() const; + void setSectorAngleFixed(bool on); // true: start and end sectors positon can't be modifed + bool isSectorFixedAngeEnabled() const; + + bool selectAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool setCoordinateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool movePickedAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool moveAction ( AbstractEventDispatcher * causer, int x, int y ) override; + + protected: + + void updateSectorHandles(); + + void update() override; + + struct InternalData; + + InternalData * _internal; + + void buildTmpConstruction( SituationMap::RefPointer & tmpGraphic, QPointF & start, QPointF & end ) override; + void getTmpConstructionParameter( const QPointF & start, const QPointF & end, QPointF & sceneCenter, qreal & sceneWidth, qreal & sceneHeight ) override; + + }; /* RubberbandPointFeature */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_ellipse_handle.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_ellipse_handle.cpp new file mode 100755 index 000000000..c5a1cbb6f --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_ellipse_handle.cpp @@ -0,0 +1,236 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of base class for rubberband handles +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_ellipse_handle.h" +#include + +#include +#include + +#include +#include + +#include + +namespace SituationMap { namespace RB +{ + +RubberbandEllipseHandle::RubberbandEllipseHandle(Esri::ArcGISRuntime::MapGraphicsView * mapView, QGraphicsScene *scene) + : RubberbandHandle(mapView, scene) +{ + zBufferPlane( ZBufferPlane::LineHandle ); + updateGraphic(); +} + +RubberbandEllipseHandle::~RubberbandEllipseHandle() +{ + if (!_scene) + return; + + if (_ellipseSymbol) + { + _scene->removeItem(_ellipseSymbol); + delete _ellipseSymbol; + } +} + +void RubberbandEllipseHandle::adjustCoordinates() +{ + _center.mapToScene(_mapView); + updateGraphic(); + + emit adjusted(); +} + + +void RubberbandEllipseHandle::adjustWindow( const QRectF& ) +{ + onDo_adjustCoordinates(); +} + +qreal length(const QPointF &p) +{ + return sqrt(p.x()*p.x()+p.y()*p.y()); +} + + +void RubberbandEllipseHandle::updateGraphic() +{ + if (_scene) + { + if (!_ellipseSymbol) + { + _ellipseSymbol = new QGraphicsEllipseItem(); + _scene->addItem(_ellipseSymbol); + _ellipseSymbol->setFlags( QGraphicsItem::ItemIgnoresTransformations ); + } + + _ellipseSymbol->setZValue(_zValue); + + // Style + makeLineStyle( _dottedLine, _lineStyle ); + + if (_isSelected) + { + _lineStyle.setWidth( _lineStyle.width() + 1 ); + _lineStyle.setColor( _selectionColor ); + } + + _ellipseSymbol->setPen(_lineStyle); + + if (isSelected()) + _ellipseSymbol->setBrush( QBrush(_selectionColor) ); + else + _ellipseSymbol->setBrush( QBrush(_fillColor) ); + + _ellipseSymbol->setVisible(_isVisible); + + // geometry: + { + _center.mapToScene(_mapView); + + QPointF mapCenter( _center._mapCoordinate.x(), _center._mapCoordinate.y() ); + + qreal mapRotatePhi = _mapView->mapRotation() * MathConst::pi / 180.0; + + qreal sinMap = sin(mapRotatePhi); + qreal cosMap = cos(mapRotatePhi); + + qreal sinPhi = _sinRect * cosMap + _cosRect * sinMap; + qreal cosPhi = _cosRect * cosMap - _sinRect * sinMap; + + + RubberbandCoordinate x0y0(_mapView, convert( mapCenter+QPointF(-_radiusX,-_radiusY), _mapView->map()->spatialReference()) ); + RubberbandCoordinate x1y0(_mapView, convert( mapCenter+QPointF( _radiusX,-_radiusY), _mapView->map()->spatialReference()) ); + RubberbandCoordinate x0y1(_mapView, convert( mapCenter+QPointF(-_radiusX, _radiusY), _mapView->map()->spatialReference()) ); + + const qreal width = length(x1y0._sceneCoordinate-x0y0._sceneCoordinate); + const qreal height = length(x0y1._sceneCoordinate-x0y0._sceneCoordinate); + + // honor sector?? + + if (_sectorStart != _sectorEnd) + { + auto getAngle = [](qreal sinPhi, qreal cosPhi) -> int + { + qreal phi = acos( cosPhi ); + if (sinPhi < 0) phi = MathConst::pi_two - phi; + return int( (phi * 180.0 / MathConst::pi) * 16); + }; // getAngle + + int startAngle = getAngle(_sectorStart.y(), _sectorStart.x()); + int endAngle = getAngle( _sectorEnd.y(), _sectorEnd.x()); + int spanAngle = (endAngle > startAngle) ? endAngle-startAngle : 5760-startAngle+endAngle; + + QColor fillColor = transparentFillColor(); + _ellipseSymbol->setRect(-width*0.5, -height*0.5, width, height); + _ellipseSymbol->setPen(_lineStyle ); + _ellipseSymbol->setBrush( (fillColor.alpha() > 0) ? QBrush(fillColor) : QBrush() ); + _ellipseSymbol->setTransform( QTransform( cosPhi, -sinPhi, sinPhi, cosPhi, _center._sceneCoordinate.x(), _center._sceneCoordinate.y() )); + _ellipseSymbol->setStartAngle(startAngle ); + _ellipseSymbol->setSpanAngle(spanAngle); + _ellipseSymbol->setVisible(true); + } + + _ellipseSymbol->setRect(-width*0.5, -height*0.5, width, height); + _ellipseSymbol->setTransform( QTransform( cosPhi, -sinPhi, sinPhi, cosPhi, _center._sceneCoordinate.x(), _center._sceneCoordinate.y()) ); + } + } + +} + +QPointF RubberbandEllipseHandle::getRadius() +{ + return {_radiusX, _radiusY}; +} + + +void RubberbandEllipseHandle::setRect(const RubberbandCoordinate ¢er, qreal radiusX, qreal radiusY, + qreal sin, qreal cos, + const QPointF §orStart, const QPointF §orEnd) +{ + // p1,p2 = corner points of unrotated ellipse in scene coordinates + _center = center; + _radiusX = radiusX; + _radiusY = radiusY; + + _sinRect = sin; + _cosRect = cos; + + _sectorStart = sectorStart; + _sectorEnd = sectorEnd; + + updateGraphic(); +} + + +bool RubberbandEllipseHandle::isPositionInside(const QPointF &scenePosition) const +{ + if (_ellipseSymbol) + { + if (_ellipseSymbol->contains( scenePosition )) + { + return true; + } +// else +// { +// // Ok, Qt's methodes doesn't work safely +// // if it fails do some own calculations + +// QPointF V0 = _coordinates[0]._sceneCoordinate; +// QPointF V1 = _coordinates[1]._sceneCoordinate; +// QPointF R = V1-V0; +// QPointF P = scenePosition-V0; +// qreal lenR = R.x() * R.x() + R.y() * R.y(); +// qreal lenP = P.x() * P.x() + P.y() * P.y(); + +// if ((lenR == 0) || (lenP > lenR)) return false; // scene Position is to far away + +// // now check the direction + +// lenR = sqrt(lenR); +// lenP = sqrt(lenP); + +// R /= lenR; +// P /= lenP; + +// qreal cosPhi = QPointF::dotProduct(R,P); + +// if (cosPhi < 0) return false; + +// qreal epsilon = sqrt(1.0-cosPhi*cosPhi) * lenR; + +// return (epsilon < qreal(_pickingRatio)); +// } + } + + return false; +} + +} } diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_ellipse_handle.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_ellipse_handle.h new file mode 100755 index 000000000..d4e5e7ce6 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_ellipse_handle.h @@ -0,0 +1,96 @@ +/** +** +** @RDE_HEADER@ +** Definition of base class for rubberband handles +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include + +#include + +#include "rubberband_handle.h" + +class QGraphicsEllipse; +class QGraphicsScene; + +namespace SituationMap +{ +namespace RB +{ + +/** + @class RubberbandEllipseHandle rubberband_line_handle.h + @brief Implementation of a line handle for rubberband internal use. + @see RubberbandHandle + */ +class RubberbandEllipseHandle : public RubberbandHandle +{ + Q_OBJECT +public: + + RubberbandEllipseHandle(Esri::ArcGISRuntime::MapGraphicsView * mapView ,QGraphicsScene * scene); + ~RubberbandEllipseHandle(); + + bool isPositionInside(const QPointF &scenePosition) const override; + +// virtual void updateCoordinates(const QVector &scenePositionVector); +// virtual void updateCoordinate(unsigned index, const QPointF &scenePosition); + + QPointF getRadius(); + + void setRect(const RubberbandDrawable::RubberbandCoordinate ¢er, + qreal radiusX, qreal radiusY, + qreal sin, qreal cos, + const QPointF & sectorStart = QPointF(), const QPointF & sectorEnd = QPointF()); + + + void dottedLine(bool on = true) { _dottedLine = on; } + +protected: + + QGraphicsEllipseItem *_ellipseSymbol{nullptr}; + QPen _lineStyle; + RubberbandDrawable::RubberbandCoordinate _center; + qreal _radiusX{0.0}; + qreal _radiusY{0.0}; + qreal _sinRect{0.0}; + qreal _cosRect{0.0}; + QPointF _sectorStart; + QPointF _sectorEnd; + + bool _dottedLine{false}; + + void updateGraphic() override; + + void adjustCoordinates() override; + void adjustWindow(const QRectF&) override; + + +}; // RubberbandEllipseHandle + +} +} // namespace SituationMap::RB diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_factory.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_factory.cpp new file mode 100755 index 000000000..1b6d114ed --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_factory.cpp @@ -0,0 +1,634 @@ +/** +** +** @RDE_HEADER@ +** Implementation of base class for rubberbands +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** 30.09.2015, JT : New create method for creating rubberbands fitting to a special sidc code +** +** ----------------------------------------------------------------------------------------------- +*/ + + +#include "rubberband_factory.h" +#include "rubberband_corridor.h" +#include + +#include +#include + +namespace SituationMap { namespace RB +{ + + class RubberbandFactory::Defaults + { + public: + + QColor _rubberbandDrawColor = QColor(); + QColor _rubberbandFillColor = QColor(); + QColor _rubberbandHandleDrawColor = QColor(); + QColor _rubberbandHandleFillColor = QColor(); + QColor _rubberbandSelectionColor = QColor(); + QColor _rubberbandFrameColor = QColor(); + QColor _rubberbandFrameFillColor = QColor(); + int _rubberbandFrameHandleMargin = 0; + QPoint _rubberbandPreviewOffset = QPoint(); + unsigned int _rubberbandPreviewSize = 0; + unsigned int _rubberbandPickingRatio = 0; + unsigned int _rubberbandLineHandleWidth = 0; + + }; // RubberbandFactory::Defaults + + + RubberbandFactory::RubberbandFactory( SituationMap::Map * map ) + : QObject() + , _map(map) + , _defaults(new Defaults()) + , _allowMultipleRubberbands(false) + , _logger(getLogger("RubberbandFactory")) + {} + + RubberbandFactory::~RubberbandFactory() { delete _defaults; } + + + class SymbolConstruction + { + public: + + enum class Mode + { + Unknown = 0, + + Point, + + ArcCurve, + Triangle, + MultiPoint, + + PolyLine, + PolyLineWidth, + + Polygon, + Rectangle, + Circular, + Corridor + }; // Mode + + Mode _mode; + unsigned int _minPoints; + unsigned int _maxPoints; + + explicit SymbolConstruction( const AdvancedSymbology::GeometryType & geomType, const QString & gctString ) + : _mode(Mode::Unknown), _minPoints(-1), _maxPoints(-1) + { + if (QLatin1String("gct_arrow") == gctString) { _mode = Mode::PolyLine; _minPoints = 2; _maxPoints = 0; } + if (QLatin1String("gct_arrow_with_offset") == gctString) { _mode = Mode::PolyLineWidth; _minPoints = 2; _maxPoints = 0; } + if (QLatin1String("gct_arrow_with_tail") == gctString) { _mode = Mode::Triangle; _minPoints = 3; _maxPoints = 3; } + + if (QLatin1String("gct_circle") == gctString) { _mode = Mode::MultiPoint; _minPoints = 2; _maxPoints = 2; } + if (QLatin1String("gct_circular") == gctString) { _mode = Mode::Circular; _minPoints = 1; _maxPoints = 1; } + + if (QLatin1String("gct_freehand_arrow") == gctString) { _mode = Mode::ArcCurve; _minPoints = 3; _maxPoints = 3; } + if (QLatin1String("gct_freehand_line") == gctString) { _mode = Mode::ArcCurve; _minPoints = 3; _maxPoints = 3; } + if (QLatin1String("gct_freehand_reverse_arrow") == gctString) { _mode = Mode::ArcCurve; _minPoints = 3; _maxPoints = 3; } + if (QLatin1String("gct_freehand_u") == gctString) { _mode = Mode::ArcCurve; _minPoints = 4; _maxPoints = 4; } + + if (QLatin1String("gct_hook") == gctString) { _mode = Mode::MultiPoint; _minPoints = 3; _maxPoints = 3; } + if (QLatin1String("gct_horns") == gctString) { _mode = Mode::MultiPoint; _minPoints = 4; _maxPoints = 4; } + + if (QLatin1String("gct_open_triangle") == gctString) { _mode = Mode::Triangle; _minPoints = 3; _maxPoints = 3; } + + if (QLatin1String("gct_parallel_lines") == gctString) { _mode = Mode::PolyLineWidth; _minPoints = 2; _maxPoints = 2; } + if (QLatin1String("gct_parallel_lines_mid_line") == gctString) { _mode = Mode::PolyLineWidth; _minPoints = 2; _maxPoints = 2; } + + if (QLatin1String("gct_parallel_lines_with_ticks") == gctString) + { + _mode = Mode::MultiPoint; + // ESRI implmentation seems to differ from symbology standard and takes always 4 input + // coordinates here to construct the symbol! + // + // if (AdvancedSymbology::GeometryType::Polygon == geomType) _minPoints = _maxPoints = 4; + // else _minPoints = _maxPoints = 3; + _minPoints = _maxPoints = 4; + } + + if (QLatin1String("gct_rectangular") == gctString) { _mode = Mode::Corridor; _minPoints = 2; _maxPoints = 2; } + if (QLatin1String("gct_rectangular_1_pt") == gctString) { _mode = Mode::Rectangle; _minPoints = 1; _maxPoints = 1; } + + if (QLatin1String("gct_t") == gctString) { _mode = Mode::MultiPoint; _minPoints = 3; _maxPoints = 3; } + if (QLatin1String("gct_triple_arrow") == gctString) { _mode = Mode::MultiPoint; _minPoints = 3; _maxPoints = 3; } + + if (QLatin1String("gct_two_line") == gctString) { _mode = Mode::Triangle; _minPoints = 3; _maxPoints = 3; } + if (QLatin1String("gct_two_line_3_or_4_pt") == gctString) { _mode = Mode::MultiPoint; _minPoints = 3; _maxPoints = 4; } + + if (QLatin1String("gct_u_or_t_shape") == gctString) { _mode = Mode::MultiPoint; _minPoints = 3; _maxPoints = 3; } + + if (Mode::Unknown == _mode) + { + // gct string is empty or not supported yet + switch (geomType) + { + case AdvancedSymbology::GeometryType::Point : { _mode = Mode::Point; _minPoints = 1; _maxPoints = 1; } break; + case AdvancedSymbology::GeometryType::Polyline : { _mode = Mode::PolyLine; _minPoints = 2; _maxPoints = 0; } break; + case AdvancedSymbology::GeometryType::Polygon : { _mode = Mode::Polygon; _minPoints = 3; _maxPoints = 0; } break; + case AdvancedSymbology::GeometryType::Undefined : break; + default : break; + } // switch + } + } // SymbolConstruction( const QString & gctString ) + + ~SymbolConstruction() = default; + + Mode mode() const { return _mode; } + unsigned int minPoints() const { return _minPoints; } + unsigned int maxPoints() const { return _maxPoints; } + + bool unlimitedMaxPoints() const { return (_maxPoints == 0); } + + }; // SymbolConstruction + + + struct ActivateRubberbandInfo + { + Rubberband * _rubberband; + SymbolConstruction _symbolConstruction; + + explicit ActivateRubberbandInfo( Rubberband * r, const SymbolConstruction & sc ) : _rubberband(r), _symbolConstruction(sc) {;} + ~ActivateRubberbandInfo() = default; + + ActivateRubberbandInfo() = delete; + + }; // ActivateRubberbandInfo + + + QString toString( SymbolConstruction::Mode mode ) + { + switch (mode) + { + case SymbolConstruction::Mode::Unknown : return QStringLiteral("Unknown Rubberband"); break; + case SymbolConstruction::Mode::Point : return QStringLiteral("Point Rubberband"); break; + case SymbolConstruction::Mode::PolyLine : return QStringLiteral("Polyline Rubberband"); break; + case SymbolConstruction::Mode::PolyLineWidth : return QStringLiteral("PolylineWidth Rubberband"); break; + case SymbolConstruction::Mode::Polygon : return QStringLiteral("Polygon Rubberband"); break; + case SymbolConstruction::Mode::Triangle : return QStringLiteral("Triangle Rubberband"); break; + case SymbolConstruction::Mode::ArcCurve : return QStringLiteral("ArcCurve Rubberband"); break; + case SymbolConstruction::Mode::Rectangle : return QStringLiteral("Rectangle Rubberband"); break; + case SymbolConstruction::Mode::Circular : return QStringLiteral("Circular Rubberband"); break; + case SymbolConstruction::Mode::Corridor : return QStringLiteral("Corridor Rubberband"); break; + case SymbolConstruction::Mode::MultiPoint : return QStringLiteral("MultiPoint Rubberband"); break; + } + return QString(); + } // QString toString( SymbolConstruction::Mode mode ) + + + static bool defaultSymbolRubberbandCoordinates( Map * map, Rubberband * rbnd, const QPointF & pixelPos, const SymbolConstruction & symbolConstruction ); + + + Rubberband * RubberbandFactory::createSymbol(const QString & sidc, QObject * parent) const { return createSymbol(sidc,QPoint(-1,-1),parent); } + + Rubberband * RubberbandFactory::createSymbol( const QString & sidc, const QPoint & winPos, QObject * parent ) const + { + if (Mode::Mode_2D != _map->mode()) return nullptr; + + const QString & baseSymbolCode = AdvancedSymbology::SidcBuilderBase::concreteSidcToBaseSymbolCode( sidc ); + const AdvancedSymbology::SymbolProperties & symbolProperties = _map->symbolDictionary().symbolProperties( baseSymbolCode ); + const AdvancedSymbology::GeometryType & geomType = symbolProperties.geometryType(); + + SymbolConstruction symbolConstruction( geomType, symbolProperties.gctString() ); + + Rubberband::currentFactoryMap(_map); + Rubberband::allowMultipleRubberbands(_allowMultipleRubberbands); + Rubberband *rubberband = nullptr; + + DEBUG_LOG(_logger) << LOGFUNCTION << QStringLiteral(" - %1").arg(toString(geomType), 30); + DEBUG_LOG(_logger) << LOGFUNCTION << QStringLiteral(" - %1").arg(symbolProperties.gctString(), 30); + DEBUG_LOG(_logger) << LOGFUNCTION << QStringLiteral(" - %1").arg(toString(symbolConstruction.mode()),30); + + bool rubberbandConstructionMode = ((winPos.x() < 0) || (winPos.y() < 0)); + + try + { + switch (geomType) + { + case AdvancedSymbology::GeometryType::Point : + { + rubberband = new RubberbandPointFeature(_map, parent); + } break; + + case AdvancedSymbology::GeometryType::Polyline : + { + switch (symbolConstruction.mode()) + { + case SymbolConstruction::Mode::PolyLine : + { + RubberbandMultiPointFeature * r = new RubberbandMultiPointFeature(_map,false,parent); + if (r) + { + r->minimumPointCount( symbolConstruction.minPoints() ); + r->maximumPointCount( symbolConstruction.maxPoints() ); + r->enableFinishAfterConstruction(true); + rubberband = r; + } + } break; + + case SymbolConstruction::Mode::PolyLineWidth : + { + RubberbandMultiPointFeature * r = new RubberbandPolylineWidth(_map,parent); + if (r) + { + r->minimumPointCount( symbolConstruction.minPoints() ); + r->maximumPointCount( symbolConstruction.maxPoints() ); + r->enableFinishAfterConstruction(true); + rubberband = r; + } + } break; + + case SymbolConstruction::Mode::ArcCurve : + case SymbolConstruction::Mode::MultiPoint : + case SymbolConstruction::Mode::Triangle : + { + RubberbandPointCluster * r = new RubberbandPointCluster(_map,parent,false); + if (r) + { + r->minimumPointCount(symbolConstruction.minPoints()); + r->maximumPointCount(symbolConstruction.maxPoints()); + r->enableFinishAfterConstruction(true); + + rubberband = r; + + if ((!symbolConstruction.unlimitedMaxPoints()) && rubberbandConstructionMode) + { + // Rubberband is not active here because we need one position to initialize + // the coordinates + Rubberband::currentFactoryMap(_map); + + RubberbandPointFeature * askPosition = new RubberbandPointFeature(_map, parent); + + setDefaults(askPosition); + + askPosition -> enableFinishAfterConstruction(true); + askPosition -> setUserData( new ActivateRubberbandInfo(rubberband, symbolConstruction) ); + askPosition -> setPreviewSidc(sidc); + askPosition -> setAutoDelete(true); + + connect(askPosition, &SituationMap::RB::Rubberband::apply, this, &RubberbandFactory::onActivateRubberband ); + connect(askPosition, &SituationMap::RB::Rubberband::abort, this, &RubberbandFactory::onAbortRubberband ); + } + else rubberband->activate(); + } + } break; + + default: + { + rubberband = new RubberbandPointCluster(_map,parent); + if (rubberband) rubberband->activate(); + } break; + } // switch + } break; + + case AdvancedSymbology::GeometryType::Polygon : + { + switch (symbolConstruction.mode()) + { + case SymbolConstruction::Mode::Circular : + { + RubberbandEllipse * r = new RubberbandEllipse(_map, parent); + if (r) + { + r->enableSector(false); + r->constrainOrthogonal(true); + r->constrainQuadratic(true); + r->enableFinishAfterConstruction(true); + } + rubberband = r; + } break; + + case SymbolConstruction::Mode::Rectangle : + { + RubberbandRectangle * r = new RubberbandRectangle(_map, parent); + if (r) + { + r->constrainOrthogonal(false); + r->enableFinishAfterConstruction(true); + } + rubberband = r; + } break; + + case SymbolConstruction::Mode::Corridor : + { + RubberbandCorridor * r = new RubberbandCorridor(_map, RubberbandCorridor::InputMode::InputCenterLine, 0, parent); + if (r) + { + r->setCorridorBorderColor( QColor("Black") ); + r->setCorridorFillColor( QColor(255,255,0,100) ); + r->minimumPointCount( symbolConstruction.minPoints() ); + r->maximumPointCount( symbolConstruction.maxPoints() ); + r->enableFinishAfterConstruction(true); + + rubberband = r; + } + } break; + + case SymbolConstruction::Mode::MultiPoint : + { + RubberbandPointCluster * r = new RubberbandPointCluster(_map,parent,false); + if (r) + { + r->minimumPointCount(symbolConstruction.minPoints()); + r->maximumPointCount(symbolConstruction.maxPoints()); + r->enableFinishAfterConstruction(true); + + rubberband = r; + + if ((!symbolConstruction.unlimitedMaxPoints()) && rubberbandConstructionMode) + { + // Rubberband is not active here because we need one position to initialize + // the coordinates + Rubberband::currentFactoryMap(_map); + + RubberbandPointFeature * askPosition = new RubberbandPointFeature(_map, parent); + + setDefaults(askPosition); + + askPosition -> enableFinishAfterConstruction(true); + askPosition -> setUserData( new ActivateRubberbandInfo(rubberband, symbolConstruction) ); + askPosition -> setPreviewSidc(sidc); + askPosition -> setAutoDelete(true); + + connect(askPosition, &SituationMap::RB::Rubberband::apply, this, &RubberbandFactory::onActivateRubberband ); + connect(askPosition, &SituationMap::RB::Rubberband::abort, this, &RubberbandFactory::onAbortRubberband ); + } + else rubberband->activate(); + } + } break; + + case SymbolConstruction::Mode::Polygon : + default: + { + RubberbandMultiPointFeature * rubberArea = new RubberbandMultiPointFeature( _map, parent ); + if (rubberArea) + { + rubberArea -> enableClosed(true); + } + rubberband = rubberArea; + } break; + } // switch + } break; + + default : break; + + } //switch + } + catch (const SituationMap::Exception &) {;} + + if (rubberband) + { + setDefaults( rubberband ); + rubberband -> setAutoDelete(true); + rubberband -> setPreviewSidc( sidc ); + + if (!rubberbandConstructionMode) + { + QPointF pixelPos( winPos.x(), winPos.y() ); + if (!defaultSymbolRubberbandCoordinates(_map, rubberband, pixelPos, symbolConstruction)) + { + ERROR_LOG(_logger) << LOGFUNCTION << "Can't init symbol rubberband coordinate list, undefined proposal."; + rubberband->deleteMySelf(); + rubberband = nullptr; + } + } + } + + return rubberband; + } /* RubberbandFactory::create */ + + + static bool defaultSymbolRubberbandCoordinates( Map * map, Rubberband * rbnd, const QPointF & pixelPos, const SymbolConstruction & symbolConstruction ) + { + QVector pointList; + + pointList.reserve( symbolConstruction.maxPoints() ); + + switch (symbolConstruction.mode()) + { + case SymbolConstruction::Mode::ArcCurve: + { + if (4 == symbolConstruction.maxPoints()) + { + pointList.append(pixelPos + QPointF(-150,-100)); + pointList.append(pixelPos + QPointF(-100,-100)); + pointList.append(pixelPos + QPointF(-100, 100)); + pointList.append(pixelPos + QPointF(-150, 100)); + } + + if (3 == symbolConstruction.maxPoints()) + { + pointList.append(pixelPos + QPointF(-150, 100)); + pointList.append(pixelPos + QPointF(+150, -20)); + pointList.append(pixelPos); + } + } break; + + case SymbolConstruction::Mode::MultiPoint: + { + if (4 == symbolConstruction.maxPoints()) + { + pointList.append(pixelPos + QPointF(-100, 100)); + pointList.append(pixelPos + QPointF( 100, 100)); + pointList.append(pixelPos + QPointF(-150,-100)); + pointList.append(pixelPos + QPointF( 150,-100)); + } + if (3 == symbolConstruction.maxPoints()) + { + pointList.append(pixelPos + QPointF(-150, 0)); + pointList.append(pixelPos + QPointF( 150, 0)); + pointList.append(pixelPos + QPointF(-150,-100)); + } + if (2 == symbolConstruction.maxPoints()) + { + pointList.append(pixelPos); + pointList.append(pixelPos + QPointF(150, 150)); + } + } break; + + case SymbolConstruction::Mode::Polygon: + case SymbolConstruction::Mode::Triangle: + { + pointList.append(pixelPos); + pointList.append(pixelPos + QPointF(-150, +150)); + pointList.append(pixelPos + QPointF(+150, +150)); + } break; + + case SymbolConstruction::Mode::Point: + { + pointList.append(pixelPos); + } break; + + case SymbolConstruction::Mode::Circular: + case SymbolConstruction::Mode::Rectangle: + { + pointList.append(pixelPos + QPointF(-150, 150)); + pointList.append(pixelPos + QPointF( 150, 150)); + pointList.append(pixelPos + QPointF( 150,-150)); + pointList.append(pixelPos + QPointF(-150,-150)); + } break; + + case SymbolConstruction::Mode::PolyLineWidth: + { + pointList.append(pixelPos + QPointF( 50, 0)); + pointList.append(pixelPos + QPointF(-150, 0)); + pointList.append(pixelPos + QPointF( 0, +50)); + } break; + + case SymbolConstruction::Mode::Corridor: + case SymbolConstruction::Mode::PolyLine: + { + pointList.append(pixelPos); + pointList.append(pixelPos + QPointF(150, 150)); + } break; + + case SymbolConstruction::Mode::Unknown: + default: + { + // empty + } break; + + } // switch + + if (!pointList.isEmpty()) + { + for (int i = 0; i < pointList.size(); ++i) { pointList[i] = map->viewPortWindowPointToGeoCoordinate(pointList[i]); } + + if (SymbolConstruction::Mode::Corridor == symbolConstruction.mode()) + { + SituationMap::CoordTranslationFunctor * cf = map->coordTranslationFunctor(); + QPointF p1 = pointList[0]; + QPointF p2 = pointList[1]; + + if (cf) + { + p1 = cf->geographicToMap(p1); + p2 = cf->geographicToMap(p2); + } + p1 -= p2; + qreal wCorr = sqrt(p1.x()*p1.x()+p1.y()*p1.y()) * 0.15; + RubberbandCorridor * rbndCorr = static_cast(rbnd); + rbndCorr->setCoordinateVectorWidth(pointList,wCorr); + } + else rbnd->setCoordinateVector(pointList); + return true; + } + return false; + } /* RubberbandFactory::defaultSymbolRubberbandCoordinates */ + + + void RubberbandFactory::onActivateRubberband( RubberbandBase * rubberband ) + { + if (nullptr == rubberband) return; + + ActivateRubberbandInfo * arbInfo = static_cast( rubberband->userData() ); + + if (nullptr == arbInfo) return; + + const QPointF & position = rubberband->singleCoordinate(); + const QPointF & pixelPos = _map->geoCoordinateToViewPortWindowPoint(position); + + if (defaultSymbolRubberbandCoordinates( _map, arbInfo->_rubberband, pixelPos, arbInfo->_symbolConstruction)) + { + arbInfo->_rubberband->activate(); + } + else + { + arbInfo->_rubberband->deleteMySelf(); + ERROR_LOG(_logger) << LOGFUNCTION << "Can't init symbol rubberband coordinate list, undefined proposal."; + } + delete arbInfo; + rubberband->setUserData(nullptr); + } /* RubberbandFactory::onActivateRubberband */ + + + void RubberbandFactory::onAbortRubberband( RubberbandBase * rubberband ) + { + if (nullptr == rubberband) return; + + ActivateRubberbandInfo * arbInfo = static_cast(rubberband->userData()); + if (arbInfo) + { + arbInfo->_rubberband->deleteMySelf(); + delete arbInfo; + } + rubberband->setUserData(nullptr); + } /* RubberbandFactory::onAbortRubberband */ + + + void RubberbandFactory::setRubberbandDrawColor ( const QColor & c ) { _defaults->_rubberbandDrawColor = c; } + void RubberbandFactory::setRubberbandFillColor ( const QColor & c ) { _defaults->_rubberbandFillColor = c; } + void RubberbandFactory::setRubberbandHandleDrawColor ( const QColor & c ) { _defaults->_rubberbandHandleDrawColor = c; } + void RubberbandFactory::setRubberbandHandleFillColor ( const QColor & c ) { _defaults->_rubberbandHandleFillColor = c; } + void RubberbandFactory::setRubberbandSelectionColor ( const QColor & c ) { _defaults->_rubberbandSelectionColor = c; } + void RubberbandFactory::setRubberbandFrameColor ( const QColor & c ) { _defaults->_rubberbandFrameColor = c; } + void RubberbandFactory::setRubberbandFrameFillColor ( const QColor & c ) { _defaults->_rubberbandFrameFillColor = c; } + void RubberbandFactory::setRubberbandFrameHandleMargin ( int m ) { _defaults->_rubberbandFrameHandleMargin = m; } + void RubberbandFactory::setRubberbandPreviewOffset ( const QPoint & o ) { _defaults->_rubberbandPreviewOffset = o; } + void RubberbandFactory::setRubberbandPreviewSize ( unsigned int s ) { _defaults->_rubberbandPreviewSize = s; } + void RubberbandFactory::setRubberbandPickingRatio ( unsigned int s ) { _defaults->_rubberbandPickingRatio = s; } + void RubberbandFactory::setRubberbandLineHandleWidth ( unsigned int s ) { _defaults->_rubberbandLineHandleWidth = s; } + + + void RubberbandFactory::setDefaults(RubberbandBase * rband) const + { + if (nullptr == rband) return; + + if ( _defaults->_rubberbandDrawColor.isValid() ) rband->setDrawColor ( _defaults->_rubberbandDrawColor ); + if ( _defaults->_rubberbandFillColor.isValid() ) rband->setFillColor ( _defaults->_rubberbandFillColor ); + if ( _defaults->_rubberbandHandleDrawColor.isValid()) rband->setHandleDrawColor ( _defaults->_rubberbandHandleDrawColor ); + if ( _defaults->_rubberbandHandleFillColor.isValid()) rband->setHandleFillColor ( _defaults->_rubberbandHandleFillColor ); + if ( _defaults->_rubberbandSelectionColor.isValid() ) rband->setHandleSelectionColor( _defaults->_rubberbandSelectionColor ); + if ( _defaults->_rubberbandFrameColor.isValid() ) rband->setFrameColor ( _defaults->_rubberbandFrameColor ); + if ( _defaults->_rubberbandFrameFillColor.isValid() ) rband->setFrameFillColor ( _defaults->_rubberbandFrameFillColor ); + + rband->setFrameHandleMargin( _defaults->_rubberbandFrameHandleMargin ); + } /* RubberbandFactory::setDefaults */ + + + void RubberbandFactory::setDefaults(Rubberband * rband) const + { + if (nullptr == rband) return; + + setDefaults( static_cast(rband) ); + if (_defaults->_rubberbandPreviewSize > 0) rband->setPreviewSize ( _defaults->_rubberbandPreviewSize ); + if (_defaults->_rubberbandPickingRatio > 0) rband->setPickingRatio ( _defaults->_rubberbandPickingRatio ); + + if (!_defaults->_rubberbandPreviewOffset.isNull()) rband->setPreviewOffset( _defaults->_rubberbandPreviewOffset ); + + if (_defaults->_rubberbandLineHandleWidth > 0) rband->setLineHandleWidth ( _defaults->_rubberbandLineHandleWidth ); + + RubberbandCorridor *rbCorridor = qobject_cast(rband); + if (rbCorridor) + { + rbCorridor->setCorridorBorderColor(rband->getDrawColor()); + rbCorridor->setCorridorFillColor(rband->getFillColor()); + } + + } /* RubberbandFactory::setDefaults */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_factory.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_factory.h new file mode 100755 index 000000000..61e777124 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_factory.h @@ -0,0 +1,290 @@ +/** +** +** @RDE_HEADER@ +** Definition of base class for rubberbands +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "event_dispatcher.h" +#include "event_delegater.h" + +#include "rubberband_base.h" +#include "rubberband.h" +#include "rubberband_arrow.h" +#include "rubberband_corridor.h" +#include "rubberband_ellipse.h" +#include "rubberband_free_hand.h" +#include "rubberband_multi_point_feature.h" +#include "rubberband_multi_ellipse_feature.h" +#include "rubberband_point_cluster.h" +#include "rubberband_point_feature.h" +#include "rubberband_polyline_width.h" +#include "rubberband_rectangle.h" +#include "rubberband_smooth_line.h" +#include "rubberband_state_select.h" +#include "rubberband_transform.h" +#include "rubberband_transform_move.h" +#include "rubberband_transform_rotate.h" +#include "rubberband_transform_scale.h" + +#include "rubberband_3d.h" +#include "rubberband_point_feature_3d.h" +#include "rubberband_multi_point_feature_3d.h" + +#include +#include + +#include + +namespace SituationMap { namespace RB +{ + /** + @brief Factory class to create rubberbands + + Rubberbands are used to create or modifiy 2D geometries like lines, circles, polygons etc. on a 2D map view + + Rubberbands are used to create or modifiy 2D geometries like lines, circles, polygons etc. by processing + Qt input events like keyboard and mouse events. Following rubberbands are available: + + | Class Name | Description | + | :----------------------------- | : --------------------------------------------------------------------------------------- | + | RubberbandPointFeature | Rubberband to edit a single position for a point feature. | + | RubberbandPointCluster | Rubberband to edit multiple positions to define a cluster of point features. | + | RubberbandMultiPointFeature | Rubberband to edit a polyline or polygon to define line or areal features. | + | RubberbandSmoothLine | Like RubberbandMultiPointFeature with rounded edges. | + | RubberbandPolylineWidth | Rubberband like RubberbandMultiPointFeature for symbols with additional width coordinate. | + | RubberbandArrow | Rubberband to edit a polyline with an arrow at the end. | + | RubberbandCorridor | Rubberband to edit corridors by defining a polyline and a width. | + | RubberbandRectangle | Rubberband to edit a rectangle or square. | + | RubberbandEllipse | Rubberband to edit a ellipse or circle. | + | RubberbandFreeHand | Rubberband to input a free hand line. | + | RubberbandStateSelect | Rubberband to set states to coordinates. | + | RubberbandTransform | Rubberband to manipulate moving, rotating and scaling in a single step | + | RubberbandTransformMove | Rubberband to define moving transformations | + | RubberbandTransformRotate | Rubberband to define rotating transformations | + | RubberbandTransformScale | Rubberband to define scaling transformations | + + On a 3D map scene following rubberbands are available: + + | Class Name | Description | + | :----------------------------- | : --------------------------------------------------------------------------------------- | + | RubberbandPointFeature3D | Rubberband to edit a single position for a point feature. | + | RubberbandMultiPointFeature3D | Rubberband to edit a polyline or polygon to define line or areal features. | + + + @see Rubberband, RubberbandPointFeature, RubberbandPointCluster, RubberbandMultiPointFeature, RubberbandSmoothLine, RubberbandPolylineWidth + RubberbandRectangle, RubberbandEllipse, RubberbandCorridor, RubberbandArrow, RubberbandFreeHand, RubberbandStateSelect + RubberbandTransform, RubberbandTransformMove, RubberbandTransformRotate, RubberbandTransformScale, + Rubberband3D, RubberbandPointFeature3D, RubberbandMultiPointFeature3D + + */ + class SITUATIONMAP_EXPORT RubberbandFactory : public QObject + { + Q_OBJECT + + public: + + explicit RubberbandFactory( SituationMap::Map * map ); + virtual ~RubberbandFactory(); + + /** + @brief template methode to create a rubberband instance + + Creates with new a rubberband from type RubberbandClass. RubberbandClass must be inherited + from class SiMapRubBnd::Rubberband otherwise a nullptr will be retruned. After use the + pointer have to be freed with delete. + + Expample to create a rubberband: + @code + + SituationMap::Map & map = ... + + const RubberbandFactory & rubberbandFactory = map.rubberbandFactory(); + + RubberbandPointFeature * rubberband = rubberbandFactory.create(); + if (rubberband != nullptr) doSomething(rubberband); + + @endcode + + @param parent Parent of rubberband object. If parent is nullptr \c _map will be used as parent + + @see Rubberband, RubberbandPointFeature, RubberbandPointCluster, RubberbandMultiPointFeature, RubberbandRectangle, RubberbandEllipse, RubberbandCorridor, RubberbandArrow + */ + template RubberbandClass * create( QObject * parent = nullptr ) const + { + RubberbandClass * rubberband = nullptr; + + if ((std::is_base_of::value) && (Mode::Mode_2D == _map->mode())) + { + // be careful it is not thread safe here + Rubberband::currentFactoryMap(_map); + Rubberband::allowMultipleRubberbands(_allowMultipleRubberbands); + + try + { + rubberband = new RubberbandClass( _map, parent ); + } + catch (const SituationMap::Exception &) {;} + setDefaults( rubberband ); + } + return rubberband; + } + + /** + Creates a new corridor rubberband and defines the input method of the rubberband + @param inputMethode Defines the input method of the rubberband + @param width With of a corridor in meters. + @param parent Parent of rubberband object. If parent is nullptr \c _map will be used as parent + @see RubberbandCorridor + */ + RubberbandCorridor * create( RubberbandCorridor::InputMode inputMode, qreal width = 0, QObject * parent = nullptr ) const + { + if (!_map->isReady() || (Mode::Mode_2D != _map->mode()) ) + return nullptr; + + Rubberband::currentFactoryMap(_map); + Rubberband::allowMultipleRubberbands(_allowMultipleRubberbands); + + RubberbandCorridor * rubberband = nullptr; + try + { + rubberband = new RubberbandCorridor( _map, inputMode, width, parent ); + } + catch (const SituationMap::Exception &) {;} + setDefaults( rubberband ); + return rubberband; + } + + + /** + Creates a new rubberband fitting to the sidc code + @param sidc sidc of symbol that will be created with rubberband. + + @see RubberbandPointFeature, RubberbandMultiPointFeature + */ + Rubberband * createSymbol( const QString & sidc, QObject * parent = nullptr ) const; + + /** + Creates a new rubberband fitting to the sidc code + @param sidc sidc of symbol that will be created with rubberband. + @param winPos Default position of the rubberband on window + + @see RubberbandPointFeature, RubberbandMultiPointFeature + */ + Rubberband * createSymbol( const QString & sidc, const QPoint & winPos, QObject * parent = nullptr ) const; + + + /** + @brief template methode to create a rubberband instance in 3D maps + + Creates with new a rubberband from type RubberbandClass. RubberbandClass must be inherited + from class SiMapRubBnd::Rubberband otherwise a nullptr will be retruned. After use the + pointer have to be freed with delete. + + Expample to create a rubberband: + @code + + SituationMap::Map & map = ... + + const RubberbandFactory & rubberbandFactory = map.rubberbandFactory(); + + RubberbandPointFeature * rubberband = rubberbandFactory.create(); + if (rubberband != nullptr) doSomething(rubberband); + + @endcode + + @param parent Parent of rubberband object. If parent is nullptr \c _map will be used as parent + + @see Rubberband, RubberbandPointFeature, RubberbandPointCluster, RubberbandMultiPointFeature, RubberbandRectangle, RubberbandEllipse, RubberbandCorridor, RubberbandArrow + */ + template RubberbandClass * create3D( QObject * parent = nullptr ) const + { + RubberbandClass * rubberband = nullptr; + + if ((std::is_base_of::value) && (Mode::Mode_3D == _map->mode())) + { + // be careful it is not thread safe here + RubberbandBase::currentFactoryMap(_map); + RubberbandBase::allowMultipleRubberbands(_allowMultipleRubberbands); + + try + { + rubberband = new RubberbandClass( _map, parent ); + } + catch (const SituationMap::Exception &) {;} + setDefaults( rubberband ); + } + return rubberband; + }; + + /** + If pass \c true multiple rubberbands per time can be created per map. Creating a rubberbad of the same type twice per time will return one + rubberband that is not alive. Default is \c false. In this case creating a second rubberband before the first has finsihed (rubberband is not alive) + will retrun \c nullptr. + */ + void allowMultipleRubberbands(bool on) { _allowMultipleRubberbands = on; } + + bool allowMultipleRubberbands() const { return _allowMultipleRubberbands; } + + void setRubberbandDrawColor( const QColor & ); + void setRubberbandFillColor( const QColor & ); + void setRubberbandHandleDrawColor( const QColor & ); + void setRubberbandHandleFillColor( const QColor & ); + void setRubberbandSelectionColor( const QColor & ); + void setRubberbandFrameColor( const QColor & ); + void setRubberbandFrameFillColor( const QColor & ); + void setRubberbandFrameHandleMargin( int ); + void setRubberbandPreviewOffset( const QPoint & ); + void setRubberbandPreviewSize( unsigned int ); + void setRubberbandPickingRatio( unsigned int ); + void setRubberbandLineHandleWidth( unsigned int ); + + signals: + + void rubberbandCreated(); + void rubberbandDestroyed(); + + private: + + class Defaults; + + SituationMap::Map * _map; + Defaults * _defaults; + bool _allowMultipleRubberbands; + + LOGGER_T _logger; + + void setDefaults( RubberbandBase * ) const; + void setDefaults( Rubberband * ) const; + + private slots: + + void onActivateRubberband( RubberbandBase * rubberband ); + void onAbortRubberband ( RubberbandBase * rubberband ); + + }; /* RubberbandFactory */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_frame_handle.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_frame_handle.cpp new file mode 100755 index 000000000..9b6847bca --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_frame_handle.cpp @@ -0,0 +1,314 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of base class for rubberband frame handles +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_frame_handle.h" + +#include + +#include +#include + +#include + + +namespace SituationMap { namespace RB +{ + + + + + static QGraphicsPolygonItem * createFrameGraphic( QGraphicsScene * mapScene, qreal zBufferValue ) + { + if (mapScene != nullptr) + { + QGraphicsPolygonItem * frameSymbol = new QGraphicsPolygonItem(); + + frameSymbol -> setZValue( zBufferValue ); + frameSymbol -> setFlags( QGraphicsItem::ItemIgnoresTransformations ); + + mapScene -> addItem( frameSymbol ); + + return frameSymbol; + } + return nullptr; + } /* createFrameGraphic */ + + + class FramePoint : public QPointF + { + public: + Q_DECL_CONSTEXPR FramePoint(const QPointF &p) : QPointF(p) {} + Q_DECL_CONSTEXPR FramePoint(qreal x, qreal y) : QPointF(x, y) {} + + FramePoint rotate(const QPointF ¢er, qreal sinA, qreal cosA) const + { + qreal cx = x() - center.x(); + qreal cy = y() - center.y(); + qreal px = cx*cosA - cy*sinA + center.x(); + qreal py = cy*cosA + cx*sinA + center.y(); + return FramePoint(px, py); + } // rotate( const QPointF & center, qreal sin qreal cosA ) + + FramePoint rotate(const RubberbandHandle::RubberbandCoordinate & center, qreal sinA, qreal cosA) const + { + return rotate(QPointF(center._mapCoordinate.x(), center._mapCoordinate.y()), sinA, cosA); + } /* rotate */ + + FramePoint rotate(qreal sinA, qreal cosA) const + { + return rotate(QPointF(), sinA, cosA); + } // rotate( const QPointF & center, qreal sin qreal cosA ) + + + }; /* FramePoint */ + + + RubberbandFrameHandle::RubberbandFrameHandle( Esri::ArcGISRuntime::MapGraphicsView * mapView + , QGraphicsScene * scene + , qreal sceneMargin ) + : RubberbandHandle(mapView,scene) + , _frameSymbol(nullptr) + , _frameSceneMargin(sceneMargin) + , _frameRotationSin(0) + , _frameRotationCos(1) + , _frameWidth(0) + , _frameHeight(0) + , _frameCenterCoordinate() + , _mapRotateSin(0) + , _mapRotateCos(1) + , _drawDisabled(false) + , _frameLineHandleList() + { + qreal mapRotatePhi = _mapView->mapRotation() * MathConst::pi / 180.0; + + _mapRotateSin = sin(mapRotatePhi); + _mapRotateCos = cos(mapRotatePhi); + + zBufferPlane( ZBufferPlane::FrameHandle ); + + _frameSymbol = createFrameGraphic( _scene, _zBufferValue ); + if (_drawDisabled) _frameSymbol->hide(); + updateGraphic(); + } /* RubberbandFrameHandle::RubberbandFrameHandle */ + + + + RubberbandFrameHandle::~RubberbandFrameHandle() + { + if (_scene) + { + if (_frameSymbol) + { + _scene->removeItem(_frameSymbol); + delete _frameSymbol; + } + } + _frameSymbol = nullptr; + } /* RubberbandFrameHandle::~RubberbandFrameHandle */ + + + void RubberbandFrameHandle::buildFrameVertexCoordinates() + { + auto length = [](qreal x, qreal y) -> qreal { return sqrt(x*x+y*y); }; + + QPointF center( _frameCenterCoordinate._mapCoordinate.x(), _frameCenterCoordinate._mapCoordinate.y()); + RubberbandCoordinate marginP( _mapView, _frameCenterCoordinate._sceneCoordinate+QPointF(_frameSceneMargin,0) ); + qreal margin = length( marginP._mapCoordinate.x()-center.x(), marginP._mapCoordinate.y()-center.y() ); + + QPointF vertex[4]; + + qreal frameWidth2 = _frameWidth * 0.5; + qreal frameHeight2 = _frameHeight * 0.5; + + vertex[0] = QPointF( center.x() - frameWidth2 - margin, center.y() - frameHeight2 - margin ); + vertex[1] = QPointF( center.x() + frameWidth2 + margin, center.y() - frameHeight2 - margin ); + vertex[2] = QPointF( center.x() + frameWidth2 + margin, center.y() + frameHeight2 + margin ); + vertex[3] = QPointF( center.x() - frameWidth2 - margin, center.y() + frameHeight2 + margin ); + + for (int i = 0; i < 4; ++i) + { + const FramePoint & v = FramePoint(vertex[i]).rotate(_frameCenterCoordinate, -_frameRotationSin, _frameRotationCos ); + _frameVertexCoordinate[i] = RubberbandCoordinate(_mapView, convert(v, _mapView->map()->spatialReference())); + } + // Now we have the vertex coordinates in map coordinate system + + } /* RubberbandFrameHandle::buildFrameVertexCoordinates */ + + + void RubberbandFrameHandle::adjustCoordinates() + { + qreal mapRotatePhi = _mapView->mapRotation() * MathConst::pi / 180.0; + + _mapRotateSin = sin(mapRotatePhi); + _mapRotateCos = cos(mapRotatePhi); + + _frameCenterCoordinate.mapToScene(_mapView); + + buildFrameVertexCoordinates(); + updateFramePolygonItem(); + + emit adjusted(); + + } /* RubberbandFrameHandle::adjustCoordinates */ + + + void RubberbandFrameHandle::adjustWindow( const QRectF& ) + { + onDo_adjustCoordinates(); + } /* RubberbandFrameHandle::adjustWindow */ + + + void RubberbandFrameHandle::updateFrameShape( const QVector & vertexList, qreal sinA, qreal cosA ) + { + DEBUG_LOG(_logger) << LOGFUNCTION << "( const QVector & vertexList, qreal sinA, qreal cosA )"; + + qreal sinPhi = sinA * _mapRotateCos - cosA * _mapRotateSin; // Addition-Theorem get rotation of the frame without map rotation + qreal cosPhi = cosA * _mapRotateCos + sinA * _mapRotateSin; + + qreal minX = std::numeric_limits::infinity(); + qreal maxX = -std::numeric_limits::infinity(); + qreal minY = std::numeric_limits::infinity(); + qreal maxY = -std::numeric_limits::infinity(); + + for (const RubberbandCoordinate & vertex : vertexList) + { + // eliminate rotation from vertex scene coordinate + FramePoint xy = FramePoint(vertex._sceneCoordinate.x(),vertex._sceneCoordinate.y()).rotate( -sinPhi, cosPhi ); + qreal x = xy.x(); + qreal y = xy.y(); + + if (minX > x) minX = x; + if (minY > y) minY = y; + if (maxX < x) maxX = x; + if (maxY < y) maxY = y; + } + + _frameRotationSin = sinA; + _frameRotationCos = cosA; + + // calculate rotated coordinates (map rotation) of (minX,minY), (maxX,maxY) and frame scene center + FramePoint frameSceneV0 = FramePoint( minX , minY ).rotate( sinPhi, cosPhi ); + FramePoint frameSceneVW = FramePoint( maxX , minY ).rotate( sinPhi, cosPhi ); + FramePoint frameSceneVH = FramePoint( minX , maxY ).rotate( sinPhi, cosPhi ); + FramePoint frameSceneCenter = FramePoint( (minX+maxX)*0.5, (minY+maxY)*0.5 ).rotate( sinPhi, cosPhi ); + + _frameCenterCoordinate.updateSceneCoordinate( _mapView, frameSceneCenter ); + + // Calculate map coordinates of bounding rectangle + RubberbandCoordinate frameV0( _mapView, QPointF(frameSceneV0.x(),frameSceneV0.y()) ); + RubberbandCoordinate frameVW( _mapView, QPointF(frameSceneVW.x(),frameSceneVW.y()) ); + RubberbandCoordinate frameVH( _mapView, QPointF(frameSceneVH.x(),frameSceneVH.y()) ); + + + auto length = []( qreal x1, qreal y1, qreal x2, qreal y2 ) -> qreal + { + qreal dx = x2-x1; + qreal dy = y2-y1; + return sqrt(dx*dx+dy*dy); + }; + + // get frame width and height in map coordinates + + _frameWidth = length( frameV0._mapCoordinate.x(), frameV0._mapCoordinate.y(), frameVW._mapCoordinate.x(), frameVW._mapCoordinate.y() ); + _frameHeight = length( frameV0._mapCoordinate.x(), frameV0._mapCoordinate.y(), frameVH._mapCoordinate.x(), frameVH._mapCoordinate.y() ); + + buildFrameVertexCoordinates(); + updateFramePolygonItem(); + + } /* RubberbandFrameHandle::updateFrameShape */ + + + void RubberbandFrameHandle::updateFrameShape() + { + DEBUG_LOG(_logger) << LOGFUNCTION; + + buildFrameVertexCoordinates(); + updateFramePolygonItem(); + + } /* RubberbandFrameHandle::updateFrameShape */ + + + void RubberbandFrameHandle::updateFramePolygonItem() + { + QPolygonF framePolygon; + + framePolygon.reserve( 4 ); + for (auto vertex : _frameVertexCoordinate) + { + framePolygon.append(vertex.mapToScene(_mapView)); + } + framePolygon.append(_frameVertexCoordinate[0]._sceneCoordinate); + + _frameSymbol->setPolygon(framePolygon); + + } /* RubberbandFrameHandle::updateFramePolygonItem */ + + + void RubberbandFrameHandle::updateGraphic() + { + if (_frameSymbol) + { + _frameSymbol -> setPen( QPen(QColor("black"),1,Qt::PenStyle::DashDotLine) ); + _frameSymbol -> setVisible( _isVisible ); + + if (isSelected()) + _frameSymbol -> setBrush( QBrush(_selectionColor) ); + else + _frameSymbol -> setBrush( QBrush(_fillColor) ); + } + } /* RubberbandFrameHandle::updateGraphic */ + + + void RubberbandFrameHandle::updateCoordinate(const QPointF & scenePosition) + { + _frameCenterCoordinate.updateSceneCoordinate( _mapView, scenePosition ); + updateFrameShape(); + } /* RubberbandFrameHandle::updateCoordinate */ + + + void RubberbandFrameHandle::moveCoordinate(const QPointF & sceneDelta) + { + _frameCenterCoordinate.updateSceneCoordinate( _mapView, _frameCenterCoordinate._sceneCoordinate+sceneDelta ); + updateFrameShape(); + } /* RubberbandFrameHandle::moveCoordinate */ + + + RubberbandHandle::RubberbandCoordinate RubberbandFrameHandle::getCoordinate() const { return _frameCenterCoordinate; } + + + bool RubberbandFrameHandle::isPositionInside( const QPointF & scenePosition ) const + { + if (_frameSymbol) + { + return _frameSymbol->contains( scenePosition ); + } + return false; + } /* RubberbandFrameHandle::isPositionInside */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_frame_handle.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_frame_handle.h new file mode 100755 index 000000000..227d0d0c1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_frame_handle.h @@ -0,0 +1,153 @@ +/** +** +** @RDE_HEADER@ +** Definition of base class for rubberband frame handles +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include + +#include +#include + +#include + +#include "rubberband_handle.h" +#include "rubberband_line_handle.h" + +class QGraphicsScene; + +namespace SituationMap { namespace RB +{ + + + /** + @class RubberbandFrameHandle rubberband_frame_handle.h + @brief Implementation of a frame handle for rubberband internal use. + @see RubberbandHandle + */ + class RubberbandFrameHandle : public RubberbandHandle + { + Q_OBJECT + public: + + RubberbandFrameHandle( Esri::ArcGISRuntime::MapGraphicsView * mapView + , QGraphicsScene * scene + , qreal sceneMargin ); + + ~RubberbandFrameHandle(); + + inline void updateFrameShape(const QVector & vertexList) + { + updateFrameShape( vertexList, 0.0, 1.0 ); + } /* updateFrameShape */ + + + void updateFrameShape( const QVector & vertexList, qreal sinA, qreal cosA ); + + bool isPositionInside( const QPointF & scenePosition ) const override; + + virtual void updateCoordinate( const QPointF & scenePosition ); + virtual void moveCoordinate ( const QPointF & sceneDelta ); + + virtual RubberbandCoordinate getCoordinate() const; + + inline qreal frameRotationAngle() const + { + qreal phi = acos(_frameRotationCos); + return (_frameRotationSin < 0.0) ? -phi : phi; + } /* frameRotationAngle */ + + + inline void frameRotationAngle( qreal & sinA, qreal & cosA ) + { + sinA = _frameRotationSin; + cosA = _frameRotationCos; + } /* frameRotationAngle */ + + inline qreal frameSceneMargin() const { return _frameSceneMargin; } + + inline void updateFrameRotationAngle( qreal sinA, qreal cosA ) + { + _frameRotationSin = sinA; + _frameRotationCos = cosA; + updateFrameShape(); + } /* updateFrameRotationAngle */ + + + inline void updateFrameRotationAngle( qreal x ) + { + _frameRotationSin = sin(x); + _frameRotationCos = cos(x); + updateFrameShape(); + } /* updateFrameRotationAngle */ + + + inline void updateFrameSceneMargin(qreal x) { _frameSceneMargin = x; updateFrameShape(); } + + + inline void drawDisabled(bool v) + { + _drawDisabled = v; + if (_drawDisabled) _frameSymbol->hide(); + else _frameSymbol->show(); + } /* drawDisabled */ + + + inline bool drawDisabled() const { return _drawDisabled; } + + + protected: + + QGraphicsPolygonItem * _frameSymbol; + qreal _frameSceneMargin; + qreal _frameRotationSin; + qreal _frameRotationCos; + qreal _frameWidth; + qreal _frameHeight; + RubberbandCoordinate _frameCenterCoordinate; + RubberbandCoordinate _frameVertexCoordinate[4]; + qreal _mapRotateSin; + qreal _mapRotateCos; + bool _drawDisabled; + + QList> _frameLineHandleList; + + void updateGraphic() override; + + void updateFrameShape(); + void updateFramePolygonItem(); + + void buildFrameVertexCoordinates(); + + void adjustCoordinates() override; + void adjustWindow( const QRectF& ) override; + + QPointF getMapRotation() { return QPointF( _mapRotateCos, _mapRotateSin ); } + + }; /* RubberbandFrameHandle */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_free_hand.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_free_hand.cpp new file mode 100755 index 000000000..6391cd1c1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_free_hand.cpp @@ -0,0 +1,267 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of rubberband class to define point feature geoemtry +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_free_hand.h" +#include "rubberband_point_handle.h" +#include "rubberband_drawable.h" +#include "drawable_line.h" +#include "drawable_painter_path.h" +#include "rubberband_handle_proxy.h" + +#include "rubberband_internal.h" +#include "rubberband_base_internal.h" + +#include + +namespace SituationMap { namespace RB +{ + + struct RubberbandFreeHand::InternalData + { + QVector _coordinateList; + QVector _inOneGoIndex; + QPoint _lastWindowPos; + bool _coordinateRecordingActive = false; + SituationMap::RefPointer _cursorPreviewLine; + SituationMap::RefPointer _cursorPreviewCloseLine; + SituationMap::RefPointer _pathItem; + }; /* RubberbandFreeHand::InternalData */ + + + RubberbandFreeHand::RubberbandFreeHand( SituationMap::Map * map, QObject * parent ) + : Rubberband(map,parent) + , _internal(new RubberbandFreeHand::InternalData) + , _isClosed(false) + { + } /* RubberbandFreeHand::RubberbandFreeHand */ + + + RubberbandFreeHand::~RubberbandFreeHand() + { + delete _internal; + _internal = nullptr; + } /* RubberbandFreeHand::~RubberbandFreeHand */ + + + bool RubberbandFreeHand::selectAction( AbstractEventDispatcher * causer, int x, int y ) + { + if (Rubberband::selectAction(causer, x, y)) + { + deactivateButtons(); + _internal->_coordinateRecordingActive = true; + _internal->_inOneGoIndex << _internal->_coordinateList.size(); + return movePickedAction(causer,x,y); + } + return false; + } /* RubberbandFreeHand::selectAction */ + + + bool RubberbandFreeHand::movePickedAction( AbstractEventDispatcher *, int x, int y ) + { + if (_internal->_coordinateRecordingActive) + { + QPointF scenePos(pixel2ScenePosition(x,y)); + if (_internal->_coordinateList.isEmpty()) + { + _internal->_lastWindowPos.setX(x); + _internal->_lastWindowPos.setY(y); + _internal->_coordinateList.append(RubberbandDrawable::RubberbandCoordinate(Rubberband::_internal->_mapView,scenePos)); + return true; + } + + if ((std::abs(_internal->_lastWindowPos.x() - x) > 2) || (std::abs(_internal->_lastWindowPos.y() - y) > 2)) + { + _internal->_lastWindowPos.setX(x); + _internal->_lastWindowPos.setY(y); + + _internal->_coordinateList.append(RubberbandDrawable::RubberbandCoordinate(Rubberband::_internal->_mapView,scenePos)); + } + + update(); + + return true; + } + return false; + } /* RubberbandFreeHand::movePickedAction */ + + + bool RubberbandFreeHand::moveAction( AbstractEventDispatcher *, int x, int y ) + { + if (!_internal->_coordinateList.isEmpty()) + { + const RubberbandDrawable::RubberbandCoordinate & firstPos = _internal->_coordinateList.first(); + const RubberbandDrawable::RubberbandCoordinate & lastPos = _internal->_coordinateList.last(); + + QPointF scenePos(pixel2ScenePosition(x,y)); + + if (!_internal->_cursorPreviewLine) _internal->_cursorPreviewLine = new DrawableLine( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene ); + if ((isClosedEnabled()) && (!_internal->_cursorPreviewCloseLine)) + { + _internal->_cursorPreviewCloseLine = new DrawableLine( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene ); + } + _internal -> _cursorPreviewLine -> setVisibility( true ); + _internal -> _cursorPreviewLine -> setDrawColor( _handleSelectionColor ); + _internal -> _cursorPreviewLine -> setLine( lastPos, RubberbandDrawable::RubberbandCoordinate( Rubberband::_internal->_mapView, scenePos )); + + if (_internal->_cursorPreviewCloseLine) + { + _internal -> _cursorPreviewCloseLine -> setVisibility( true ); + _internal -> _cursorPreviewCloseLine -> setDrawColor( _handleSelectionColor ); + _internal -> _cursorPreviewCloseLine -> setLine( firstPos, RubberbandDrawable::RubberbandCoordinate( Rubberband::_internal->_mapView, scenePos )); + } + } + return false; + } /* RubberbandFreeHand::moveAction */ + + + bool RubberbandFreeHand::setCoordinateAction( AbstractEventDispatcher * causer, int x, int y ) + { + _internal->_coordinateRecordingActive = false; + + if (Rubberband::setCoordinateAction(causer,x,y)) + { + QPointF scenePos(pixel2ScenePosition(x,y)); + activateButtons(); + _baseInternal->buttonProxyActive( scenePos ); + } + return false; + } /* RubberbandFreeHand::setCoordinateAction */ + + + void RubberbandFreeHand::activateButtons() + { + _baseInternal->_hasUndoButton = true; + _baseInternal->_applyIsPossible = true; + _baseInternal->_undoIsPossible = !_internal->_inOneGoIndex.isEmpty(); + + Rubberband::activateButtons(); + } /* RubberbandFreeHand::activateButtons */ + + + bool RubberbandFreeHand::undoAction( AbstractEventDispatcher*, int, int ) + { + if (!_internal->_inOneGoIndex.isEmpty()) + { + unsigned int coordinateIdx = _internal->_inOneGoIndex.takeLast(); + + while (coordinateIdx < (unsigned int)_internal->_coordinateList.size()) + { + _internal->_coordinateList.removeLast(); + } + update(); + activateButtons(); + } + return true; + } /* RubberbandFreeHand::undoAction */ + + + bool RubberbandFreeHand::completeStateAction( AbstractEventDispatcher * causer, int x, int y ) + { + if (rubberbandIsAlive()) + { + return applyAndEndAction(causer,x,y); + } + return false; + } /* RubberbandFreeHand::completeStateAction */ + + + unsigned int RubberbandFreeHand::coordinateCount() const + { + return _internal->_coordinateList.size(); + } /* RubberbandFreeHand::coordinateCount */ + + + QVector RubberbandFreeHand::coordinateVector( CoordinateSystem cs ) const + { + QVector coordinateVector; + coordinateVector.reserve(_internal->_coordinateList.size()); + + for (const RubberbandDrawable::RubberbandCoordinate &pos : std::as_const(_internal->_coordinateList)) + coordinateVector.push_back(projectCoordinateFromMap( cs, pos._mapCoordinate )); + + return coordinateVector; + + } /* RubberbandFreeHand::coordinateVector */ + + + QPointF RubberbandFreeHand::singleCoordinate(unsigned int idx, CoordinateSystem cs ) const + { + if (idx >= (unsigned int)_internal->_coordinateList.size()) return QPointF(); + + const Esri::ArcGISRuntime::Point & pos = _internal->_coordinateList.at(idx)._mapCoordinate; + + return projectCoordinateFromMap( cs, pos ); + + } /* RubberbandFreeHand::singleCoordinate */ + + + void RubberbandFreeHand::update() + { + if (_internal->_coordinateList.size() > 1) + { + if (!_internal->_pathItem) + { + _internal->_pathItem = new DrawablePainterPath( Rubberband::_internal->_mapView ); + } + _internal -> _pathItem -> setVisibility(true); + _internal -> _pathItem -> setLineWidth( 2 ); + _internal -> _pathItem -> enableClosedLine( _isClosed ); + _internal -> _pathItem -> setDrawColor( _drawColor ); + _internal -> _pathItem -> setFillColor( _fillColor ); + _internal -> _pathItem -> setPath( _internal->_coordinateList ); + } + else + { + if (_internal -> _pathItem) _internal->_pathItem->setVisibility(false); + if (_internal->_coordinateList.isEmpty()) + { + if (_internal -> _cursorPreviewLine ) _internal -> _cursorPreviewLine -> setVisibility(false); + if (_internal -> _cursorPreviewCloseLine) _internal -> _cursorPreviewCloseLine -> setVisibility(false); + } + } + if (!_internal->_coordinateList.isEmpty()) + { + QPointF bottomRight( std::numeric_limits::min(), std::numeric_limits::min() ); + + for (const RubberbandDrawable::RubberbandCoordinate &coordinate : std::as_const(_internal->_coordinateList)) + { + const QPointF & p = coordinate._sceneCoordinate; + if (bottomRight.x() < p.x()) bottomRight.setX(p.x()); + if (bottomRight.y() < p.y()) bottomRight.setY(p.y()); + } + + if (isButtonBoxPositionFixed()) + { + RubberbandBase::_baseInternal->_buttonBoxSceneAnchor = bottomRight; + } + } + } /* RubberbandFreeHand::update */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_free_hand.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_free_hand.h new file mode 100755 index 000000000..d5e07c370 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_free_hand.h @@ -0,0 +1,102 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class for free hand drawing +** $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 .........: +** +** 06.10.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband.h" + + +namespace SituationMap { namespace RB +{ + + /** + @class RubberbandPointCluster rubberband_point_cluster.h + @brief Implementation of a rubberband to input single coodinates for building point feature clusters + @see Rubberband, RubberbandPointFeature + */ + class SITUATIONMAP_EXPORT RubberbandFreeHand : public Rubberband + { + Q_OBJECT + /** + @name Construction & Destruction + @{ + */ + + friend class RubberbandFactory; + explicit RubberbandFreeHand( SituationMap::Map * map, QObject * parent = nullptr ); + + public: + + ~RubberbandFreeHand(); + /** @} */ + + bool selectAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool movePickedAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool moveAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool setCoordinateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool undoAction ( AbstractEventDispatcher * causer, int x, int y ) override; + + unsigned int coordinateCount() const override; + + virtual QVector coordinateVector( CoordinateSystem cs = CoordinateSystem::Geo ) const override; + + virtual QPointF singleCoordinate( unsigned int idx = 0, CoordinateSystem cs = CoordinateSystem::Geo ) const override; + + /** + If set to true a polygon the rubberband draws a polygon otherwise a polyline. + */ + inline void enableClosed( bool flag ) { _isClosed = flag; } + + /** + Gets the value of the close flag + */ + inline bool isClosedEnabled() const { return _isClosed; } + + + private: + + virtual void setCoordinateVector( const QVector & /*pntList*/, CoordinateSystem /*cs*/ = CoordinateSystem::Geo ) override {} + + protected: + + /* + Forward declaration of internal container class for member variables. The derived classes of Rubberband + often have also a declaration of InternalData for there own internal members. + */ + struct InternalData; + + InternalData * _internal; + bool _isClosed; + + void activateButtons() override; + void update() override; + + }; /* RubberbandFreeHand */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_handle.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_handle.cpp new file mode 100755 index 000000000..7d80c7da8 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_handle.cpp @@ -0,0 +1,70 @@ +/** +** +** @RDE_HEADER@ +** Implementation of base class for rubberband handles +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_handle.h" + +namespace SituationMap { namespace RB +{ + + + RubberbandHandle::RubberbandHandle( QGraphicsView *view ) + : RubberbandDrawable(view) + , _pickingRatio(5) + , _isSelected(false) + , _isSelectable(true) + , _userData(nullptr) + { + } /* RubberbandHandle::RubberbandHandle */ + + + + RubberbandHandle::RubberbandHandle( Esri::ArcGISRuntime::MapGraphicsView * mapView ) + : RubberbandDrawable( mapView ) + , _pickingRatio(5) + , _isSelected(false) + , _isSelectable(true) + , _userData(nullptr) + { + } /* RubberbandHandle::RubberbandHandle */ + + + RubberbandHandle::RubberbandHandle( Esri::ArcGISRuntime::MapGraphicsView * mapView, QGraphicsScene * scene) + : RubberbandDrawable( mapView, scene ) + , _pickingRatio(5) + , _isSelected(false) + , _isSelectable(true) + , _userData(nullptr) + { + } /* RubberbandHandle::RubberbandHandle */ + + + RubberbandHandle::~RubberbandHandle() + { + } /* RubberbandHandle::~Rubberbandhandle */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_handle.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_handle.h new file mode 100755 index 000000000..f8017e07f --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_handle.h @@ -0,0 +1,100 @@ +/** +** +** @RDE_HEADER@ +** Definition of base class for rubberband handles +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband_drawable.h" + +namespace SituationMap { namespace RB +{ + + /** + @class RubberbandHandle rubberband_handle.h + @brief Base class of all rubberband handles for rubberband internal use. + */ + class RubberbandHandle : public RubberbandDrawable + { + Q_OBJECT + + public: + + /** + @name Construction & Destruction + @{ + */ + explicit RubberbandHandle( QGraphicsView * ); + explicit RubberbandHandle( Esri::ArcGISRuntime::MapGraphicsView * ); + explicit RubberbandHandle( Esri::ArcGISRuntime::MapGraphicsView *, QGraphicsScene * ); + + ~RubberbandHandle(); + /** @} */ + + + virtual bool isPositionInside( const QPointF & scenePosition ) const = 0; + + inline bool isSelected() const { return _isSelected; } + inline void setSelection( bool on = true ) { _isSelected = _isSelectable && on; updateGraphic(); } + + inline bool isSelectable() const { return _isSelectable; } + inline void setSelectable(bool on = true) { _isSelectable = on; if (!on) { _isSelected = false; updateGraphic(); } } + + inline void setSelectionColor( const QColor & col ) { _selectionColor = col; updateGraphic(); } + + inline void setUserData( void * ptr ) { _userData = ptr; } + inline void* userData() { return _userData; } + + inline unsigned int pickingRatio() const { return _pickingRatio; } + inline void setPickingRatio(unsigned int value) { if (value > 5) _pickingRatio = value; } + + inline bool isFirst() const { return _is_first; } + inline void setIsFirst(bool on = true) { _is_first = on; } + + protected: + + void updateGraphic() override = 0; + + void adjustCoordinates() override = 0; + void adjustWindow( const QRectF& ) override = 0; + + unsigned int _pickingRatio; + bool _isSelected; + bool _isSelectable; + QColor _selectionColor; + bool _is_first{false}; + + private: + + void * _userData; + + signals: + + void adjusted(); + + }; /* RubberbandHandle */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_handle_proxy.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_handle_proxy.cpp new file mode 100755 index 000000000..7b0912b5b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_handle_proxy.cpp @@ -0,0 +1,167 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of class RubberbandHandleProxy to forward adjust signals from view +** $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 .........: +** +** 10.07.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_handle_proxy.h" + +#include + +#include + +#define BUG_iOSQz_000005 + + +namespace SituationMap { namespace RB +{ + + void RubberbandHandleProxy::init( QWidget * widget ) + { + zBufferPlane( ZBufferPlane::Widget ); + + _widgetsProxy = new QGraphicsProxyWidget(); + _widgetsProxy -> setWidget( widget ); + _widgetsProxy -> setZValue( _zBufferValue ); + +#ifdef BUG_iOSQz_000005 + // Bug iOSQz-000005 'QGraphicsProxyWidget will crash' + // Adding a widget will let the application crash + // Workaround: 'proxy->setOpacity(0.99);' + + _widgetsProxy->setOpacity(0.99); +#endif + + _scene->addItem( _widgetsProxy ); + + } /* RubberbandHandleProxy::init */ + + + RubberbandHandleProxy::RubberbandHandleProxy( QGraphicsView * view, QWidget * widget ) + : RubberbandHandle(view) + , _coordinate() + , _sceneOffset(0, 0) + , _rotateSceneOffset(true) + , _restoreView(view) + { + init( widget ); + } /* RubberbandHandleProxy::RubberbandHandleProxy */ + + + RubberbandHandleProxy::RubberbandHandleProxy( Esri::ArcGISRuntime::MapGraphicsView * view, QWidget * widget ) + : RubberbandHandle(view) + , _coordinate() + , _sceneOffset(0, 0) + , _rotateSceneOffset(true) + , _restoreView(view) + { + init( widget ); + } /* RubberbandHandleProxy::RubberbandHandleProxy */ + + + RubberbandHandleProxy::RubberbandHandleProxy( Esri::ArcGISRuntime::MapGraphicsView * view, QGraphicsScene * scene, QWidget * widget ) + : RubberbandHandle(view, scene) + , _coordinate() + , _sceneOffset(0, 0) + , _rotateSceneOffset(true) + , _restoreView(view) + { + init( widget ); + } /* RubberbandHandleProxy::RubberbandHandleProxy */ + + + RubberbandHandleProxy::~RubberbandHandleProxy() + { + if (_scene) + { + if (_widgetsProxy) + { + _widgetsProxy->setWidget(nullptr); + _scene->removeItem(_widgetsProxy); + delete _widgetsProxy; + } + } + _widgetsProxy = nullptr; + + if (_restoreView && _restoreView->viewport()) + _restoreView->viewport()->setCursor(_restoreCursor); // workaround Qt bug. remove when fixed + + } /* RubberbandHandleProxy::~RubberbandHandleProxy */ + + + QPointF RubberbandHandleProxy::getRotatedSceneOffset() const + { + if ((_mapView) && (_rotateSceneOffset)) + { + auto rotate = [](const QPointF & p, qreal sinA, qreal cosA) -> QPointF { return QPointF(p.x()*cosA+p.y()*sinA,p.y()*cosA-p.x()*sinA); }; + + qreal mapRotatePhi = _mapView->mapRotation() * MathConst::pi / 180.0; + + qreal sinMap = sin(mapRotatePhi); + qreal cosMap = cos(mapRotatePhi); + + return rotate( _sceneOffset, sinMap, cosMap ); + } + else return _sceneOffset; + } /* RubberbandHandleProxy::getRotatedSceneOffset */ + + + void RubberbandHandleProxy::setPos(const QPointF & pos) + { + _coordinate.updateSceneCoordinate( _mapView, pos ); + if (_widgetsProxy) + { + _widgetsProxy -> setPos(pos+getRotatedSceneOffset()); + _widgetsProxy -> setZValue( _zBufferValue ); + } + } /* RubberbandHandleProxy::setPos */ + + + bool RubberbandHandleProxy::isPositionInside( const QPointF & scenePosition ) const + { + const QRectF & bbox = _widgetsProxy->windowFrameGeometry(); + return bbox.contains(scenePosition); + } /* RubberbandHandleProxy::isPositionInside */ + + + void RubberbandHandleProxy::adjustCoordinates() + { + const QPointF & scenePosition = _coordinate.mapToScene(_mapView); + if (_widgetsProxy) + { + _widgetsProxy -> setZValue( _zBufferValue ); + _widgetsProxy -> setPos(scenePosition+getRotatedSceneOffset()); + } + emit adjusted(); + } /* RubberbandHandleProxy::adjustCoordinates */ + + + void RubberbandHandleProxy::adjustWindow( const QRectF & ) + { + onDo_adjustCoordinates(); + } /* RubberbandHandleProxy::adjustWindow */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_handle_proxy.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_handle_proxy.h new file mode 100755 index 000000000..c7a8ae069 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_handle_proxy.h @@ -0,0 +1,104 @@ +/** +** +** @RDE_HEADER@ +** Definition of class RubberbandHandleProxy to forward adjust signals from View +** $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 .........: +** +** 10.07.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband_handle.h" + +#include + +class QGraphicsView; +class QGraphicsScene; + +namespace SituationMap { namespace RB +{ + + /** + @class RubberbandHandleProxy rubberband_handle_proxy.h + @brief Implementation of a procx to handle small menues and other widgets like buttons + @see RubberbandHandle + */ + class RubberbandHandleProxy : public RubberbandHandle + { + Q_OBJECT + + public: + + /** + @name Construction & Destruction + @{ + */ + RubberbandHandleProxy( QGraphicsView *view, QWidget * widget ); + + RubberbandHandleProxy( Esri::ArcGISRuntime::MapGraphicsView * view, QWidget * widget ); + RubberbandHandleProxy( Esri::ArcGISRuntime::MapGraphicsView * view, QGraphicsScene * scene, QWidget * widget ); + + ~RubberbandHandleProxy(); + /** @} */ + + bool isPositionInside( const QPointF & scenePosition ) const override; + + void setPos(const QPointF & pos); + + const RubberbandCoordinate & getCoordinate() const { return _coordinate; } + + inline void setSceneOffset(const QPointF & offset) { _sceneOffset = offset; } + + inline void show() { setVisibility(false); if (_widgetsProxy) { _widgetsProxy->show(); setVisibility(true); } } + inline void hide() { setVisibility(false); if (_widgetsProxy) { _widgetsProxy->hide(); setVisibility(false); } } + + inline void setRotateSceneOffset(bool on = true) { _rotateSceneOffset = on; } + + inline void setRestoreCursor( const QCursor & cursor ) { _restoreCursor = cursor; } // workaround Qt bug. remove when fixed + + protected: + + RubberbandCoordinate _coordinate; + QGraphicsProxyWidget * _widgetsProxy = nullptr; + QPointF _sceneOffset; + bool _rotateSceneOffset; + + QPointer _restoreView; + QCursor _restoreCursor; // workaround Qt bug. remove when fixed + + void updateGraphic() override {} + + void adjustCoordinates() override; + void adjustWindow( const QRectF& ) override; + + private: + + QPointF getRotatedSceneOffset() const; + + private: + + void init( QWidget * widget ); + + }; /* RubberbandHandleProxy */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_internal.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_internal.cpp new file mode 100755 index 000000000..36e486482 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_internal.cpp @@ -0,0 +1,98 @@ +/** +** +** @RDE_HEADER@ +** Definition of internal data of base class for rubberbands +** $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 .........: +** +** 16.06.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_internal.h" +#include "rubberband.h" + +#include "rubberband_drawable.h" +#include "rubberband_handle.h" +#include "rubberband_handle_proxy.h" + +#include +#include + + + + +namespace SituationMap { namespace RB +{ + + Rubberband::InternalData::InternalData( SituationMap::Map * map ) + : _mapView(nullptr) + , _mapScene(nullptr) + , _handleList() + , _constructionObjectDraw(nullptr) + , _connectedPreviewPixmap(nullptr) + , _lastClickPosition(-1,-1) + , _constrainFailedMarker(nullptr) + , _logger(getLogger("Rubberband")) + { + SituationMap::MapImpl * mapImpl = dynamic_cast(map); + + if (mapImpl) + { + _mapView = mapImpl -> getEsriMapGraphicsView(); + + if (_mapView != nullptr) + { + _mapScene = _mapView -> scene(); + } + } + } /* InternalData */ + + Rubberband::InternalData::~InternalData() + { + /* Because of using shared RefPointer we don't need to call delete for every handle, + * just clear the handle-list. + for (auto handle : _handleList) + { + handle = nullptr;; + } + */ + _handleList.clear(); + + delete _constructionObjectDraw; + + _constructionObjectDraw = nullptr; + + if (_mapScene) + { + if (nullptr != _connectedPreviewPixmap) + { + _mapScene->removeItem( _connectedPreviewPixmap ); + delete _connectedPreviewPixmap; + _connectedPreviewPixmap = nullptr; + } + + delete _constrainFailedMarker; + } + _mapView = nullptr; + _mapScene = nullptr; + } /* ~InternalData */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_internal.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_internal.h new file mode 100755 index 000000000..aab73ea54 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_internal.h @@ -0,0 +1,98 @@ +/** +** +** @RDE_HEADER@ +** Definition of internal data of base class for rubberbands +** $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 .........: +** +** 16.06.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + + +#include +#include + +#include +#include + +#include "rubberband.h" + +#include "rubberband_handle.h" +#include "rubberband_drawable.h" + + +class QHBoxLayout; +class QWidget; +class QPushButton; +class QGraphicsScene; + + +namespace Esri { +namespace ArcGISRuntime { + class MapGraphicsView; +} } + + +namespace SituationMap +{ + class Map; + class CoordTranslationFunctor; +} + +namespace SituationMap { namespace RB +{ + + class RubberbandHandleProxy; + + /** + @class Rubberband::InternalData rubberband_internal.h + @brief Definition of internal data of rubberband base class. + */ + struct Rubberband::InternalData + { + Q_DISABLE_COPY(InternalData) + + QPointer _mapView; + QPointer _mapScene; + + QList> _handleList; + RubberbandDrawable * _constructionObjectDraw; + QGraphicsPixmapItem * _connectedPreviewPixmap; + + + + QPointF _lastClickPosition; + + QGraphicsPixmapItem * _constrainFailedMarker; + + LOGGER_T _logger; + + explicit InternalData( SituationMap::Map * map ); + + ~InternalData(); + + void markFailedConstrains( QPoint pos, bool on ); + }; /* InternalData */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_line_handle.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_line_handle.cpp new file mode 100755 index 000000000..8b7f1539b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_line_handle.cpp @@ -0,0 +1,231 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of base class for rubberband handles +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_line_handle.h" + +#include + +#include +#include + +#include + +namespace SituationMap { namespace RB +{ + + RubberbandLineHandle::RubberbandLineHandle( Esri::ArcGISRuntime::MapGraphicsView * mapView + , QGraphicsScene * scene + , const QPointF & p1 + , const QPointF & p2 + , unsigned int lineWidth + , bool dottedLine ) + : RubberbandHandle(mapView,scene) + , _lineSymbol(nullptr) + , _lineStyle() + , _coordinates() + , _dottedLine(dottedLine) + { + _lineWidth = lineWidth; + + zBufferPlane( ZBufferPlane::LineHandle ); + _lineStyle.setWidth(_lineWidth); + + if (_scene != nullptr) + { + QGraphicsLineItem * line = new QGraphicsLineItem(); + + _lineSymbol = line; + + _coordinates.resize(2); + _coordinates[0].updateSceneCoordinate(mapView,p1); + _coordinates[1].updateSceneCoordinate(mapView,p2); + + if (_lineSymbol != nullptr) + { + _scene -> addItem( _lineSymbol ); + line->setLine( _coordinates[0]._sceneCoordinate.x(), _coordinates[0]._sceneCoordinate.y() + , _coordinates[1]._sceneCoordinate.x(), _coordinates[1]._sceneCoordinate.y() ); + + line -> setZValue(10); + line -> setFlags( QGraphicsItem::ItemIgnoresTransformations ); + + makeLineStyle( _dottedLine, _lineStyle ); + + line -> setPen( _lineStyle ); + } + } + } /* RubberbandLineHandle::RubberbandLineHandle */ + + + RubberbandLineHandle::~RubberbandLineHandle() + { + if (_scene) + { + if (_lineSymbol) + { + _scene->removeItem(_lineSymbol); + delete _lineSymbol; + } + } + _coordinates.clear(); + + } /* RubberbandHandle::~Rubberbandhandle */ + + void RubberbandLineHandle::adjustCoordinates() + { + if (_coordinates.size() >= 2) + { + _coordinates[0].mapToScene( _mapView ); + _coordinates[1].mapToScene( _mapView ); + + static_cast(_lineSymbol)->setLine( _coordinates[0]._sceneCoordinate.x(), _coordinates[0]._sceneCoordinate.y() + , _coordinates[1]._sceneCoordinate.x(), _coordinates[1]._sceneCoordinate.y() ); + } + + DEBUG_LOG(_logger) << LOGFUNCTION << " map-coordinate = " << QString::number(_coordinates[0]._mapCoordinate.x(),'f',15) + << " ; " << QString::number(_coordinates[0]._mapCoordinate.y(),'f',15) + << " scene-coordinate = " << _coordinates[1]._sceneCoordinate.x() << " ; " << _coordinates[1]._sceneCoordinate.y(); + emit adjusted(); + } /* RubberbandLineHandle::adjustCoordinates */ + + + void RubberbandLineHandle::adjustWindow( const QRectF& ) + { + onDo_adjustCoordinates(); + } /* RubberbandLineHandle::adjustWindow */ + + + void RubberbandLineHandle::updateGraphic() + { + if (_lineSymbol) + { + QGraphicsLineItem * line = static_cast( _lineSymbol); + + makeLineStyle( _dottedLine, _lineStyle ); + + if (_isSelected) + { + _lineStyle.setWidth( _lineStyle.width() + 1 ); + _lineStyle.setColor( _selectionColor ); + } + line -> setPen( _lineStyle ); + line -> setVisible( _isVisible ); + } + } /* RubberbandLineHandle::updateGraphic */ + + + void RubberbandLineHandle::updateCoordinates(const QVector & scenePositionVector) + { + if (scenePositionVector.size() == 2) + { + if (_coordinates.isEmpty()) _coordinates.resize(2); + _coordinates[0].updateSceneCoordinate(_mapView,scenePositionVector[0]); + _coordinates[1].updateSceneCoordinate(_mapView,scenePositionVector[1]); + + if (_lineSymbol != nullptr) + { + QGraphicsLineItem * line = static_cast( _lineSymbol); + line->setLine( _coordinates[0]._sceneCoordinate.x(), _coordinates[0]._sceneCoordinate.y() + , _coordinates[1]._sceneCoordinate.x(), _coordinates[1]._sceneCoordinate.y() ); + } + } + } /* RubberbandLineHandle::updateCoordinates */ + + + void RubberbandLineHandle::updateCoordinate( unsigned index, const QPointF & scenePosition ) + { + if (index < 2) + { + _coordinates[index].updateSceneCoordinate(_mapView,scenePosition); + QGraphicsLineItem * line = static_cast( _lineSymbol); + line->setLine( _coordinates[0]._sceneCoordinate.x(), _coordinates[0]._sceneCoordinate.y() + , _coordinates[1]._sceneCoordinate.x(), _coordinates[1]._sceneCoordinate.y() ); + } + } /* RubberbandLineHandle::updateCoordinate */ + + + QVector RubberbandLineHandle::getCoordinates() const + { + return _coordinates; + } /* RubberbandLineHandle::getCoordinates */ + + + RubberbandHandle::RubberbandCoordinate RubberbandLineHandle::getCoordinate( unsigned index ) const + { + if (index < 2) + { + return _coordinates[index]; + } + return RubberbandCoordinate(); + } /* RubberbandLineHandle::getCoordinate */ + + + bool RubberbandLineHandle::isPositionInside( const QPointF & scenePosition ) const + { + if (_lineSymbol) + { + if (_lineSymbol->contains( scenePosition )) + { + return true; + } + else + { + // Ok, Qt's methodes doesn't work safely + // if it fails do some own calculations + + QPointF V0 = _coordinates[0]._sceneCoordinate; + QPointF V1 = _coordinates[1]._sceneCoordinate; + QPointF R = V1-V0; + QPointF P = scenePosition-V0; + qreal lenR = R.x() * R.x() + R.y() * R.y(); + qreal lenP = P.x() * P.x() + P.y() * P.y(); + + if ((lenR == 0) || (lenP > lenR)) return false; // scene Position is to far away + + // now check the direction + + lenR = sqrt(lenR); + lenP = sqrt(lenP); + + R /= lenR; + P /= lenP; + + qreal cosPhi = QPointF::dotProduct(R,P); + + if (cosPhi < 0) return false; + + qreal epsilon = sqrt(1.0-cosPhi*cosPhi) * lenR; + + return (epsilon < qreal(_pickingRatio)); + } + } + return false; + } /* RubberbandLineHandle::isPositionInside */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_line_handle.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_line_handle.h new file mode 100755 index 000000000..d01f9e520 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_line_handle.h @@ -0,0 +1,81 @@ +/** +** +** @RDE_HEADER@ +** Definition of base class for rubberband handles +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include + +#include + +#include "rubberband_handle.h" + +class QGraphicsItem; +class QGraphicsScene; + +namespace SituationMap { namespace RB +{ + + /** + @class RubberbandLineHandle rubberband_line_handle.h + @brief Implementation of a line handle for rubberband internal use. + @see RubberbandHandle + */ + class RubberbandLineHandle : public RubberbandHandle + { + Q_OBJECT + public: + + RubberbandLineHandle( Esri::ArcGISRuntime::MapGraphicsView * mapView ,QGraphicsScene * scene , const QPointF & p1, const QPointF & p2, unsigned int lineWidth = 2, bool dottedLine = false ); + ~RubberbandLineHandle(); + + bool isPositionInside( const QPointF & scenePosition ) const override; + + virtual void updateCoordinates( const QVector & scenePositionVector ); + virtual void updateCoordinate( unsigned index, const QPointF & scenePosition ); + + virtual QVector getCoordinates() const; + virtual RubberbandCoordinate getCoordinate( unsigned index ) const; + + void dottedLine(bool on = true) { _dottedLine = on; } + + protected: + + QGraphicsItem * _lineSymbol; + QPen _lineStyle; + QVector _coordinates; + bool _dottedLine; + + void updateGraphic() override; + + void adjustCoordinates() override; + void adjustWindow( const QRectF& ) override; + + + }; /* RubberbandLineHandle */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_ellipse_feature.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_ellipse_feature.cpp new file mode 100755 index 000000000..805aab5d5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_ellipse_feature.cpp @@ -0,0 +1,1747 @@ +/** + * @RDE_HEADER@ + * Rubberband class to define multiple ellipse/circle + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "rubberband_multi_ellipse_feature.h" +#include "rubberband_frame_handle.h" +#include "rubberband_line_handle.h" +//#include "rubberband_ellipse.h" +#include "rubberband_ellipse_handle.h" +#include "rubberband_point_handle.h" +#include "rubberband_drawable.h" +#include "rubberband_handle_proxy.h" +#include "drawable_line.h" +#include "drawable_ellipse.h" +#include "drawable_painter_path.h" +#include "transient_tactical_symbol.h" + +#include "rubberband_internal.h" +#include "rubberband_base_internal.h" + +#include "winkel_icon.xpm" + +//#include +#include +#include +#include + +#include + +#include + +namespace SituationMap +{ +namespace RB +{ + +struct RubberbandMultiEllipseFeature::InternalData +{ + Q_DISABLE_COPY(InternalData) + + QList> _ellipseHandleList; + SituationMap::RefPointer _frameHandle; + + // sector : + QPointF _sectorOStart{1, 0}; + QPointF _sectorOEnd{0, 1}; + SituationMap::RefPointer _sectorStartHandle; + SituationMap::RefPointer _sectorEndHandle; + bool _sectorEnabled = false; + bool _sectorFixedAngleEnabled{false}; + + QPointF _oVec{1,0}; // rb: // normalized orientation vector according to scene coordinate system + // : sector + + // rotationHandle : + SituationMap::RefPointer _rotationHandle; + unsigned int _rotationHandleSize{0}; + // : rotationHandle + + + // Coordinate matrix to describe the change of coordinates of the last user input + MathUtils::Matrix2D _lastCoordinateChange; + + + SituationMap::RefPointer _previewEllipse; + SituationMap::RefPointer _previewSymbol; + + QVector _preparedRadiiConstraints; + + bool _complexGeometry; + bool _addVertexAtEnd; + bool _showCursorPreviewEllipse; + bool _constrainQuadratic{true}; + + + InternalData(QGraphicsScene *scene, bool complexGeometry) + : _complexGeometry(complexGeometry) + , _addVertexAtEnd(false) + , _showCursorPreviewEllipse(false) + { + Q_UNUSED(scene); + } // InternalData +}; // RubberbandMultiEllipseFeature::InternalData + +QPointF rotateVertex(const QPointF ¢er, qreal x, qreal y, qreal sinA, qreal cosA) +{ + return QPointF(x*cosA-y*sinA,y*cosA+x*sinA) + center; +} + + +RubberbandMultiEllipseFeature::RubberbandMultiEllipseFeature(SituationMap::Map *map, QObject *parent) + : RubberbandMultiEllipseFeature(map, false, parent) +{ +} + + +RubberbandMultiEllipseFeature::RubberbandMultiEllipseFeature(SituationMap::Map *map, bool complexGeometry, QObject *parent) + : Rubberband(map, parent) + , _internal(new InternalData(Rubberband::_internal->_mapScene, complexGeometry)) + , _isClosed(false) + , _minimumPointCount(2) + , _maximumPointCount(0) + , _maximumSegmentLength(0.0) + , _maximumLength(0.0) +{ +} + + +RubberbandMultiEllipseFeature::~RubberbandMultiEllipseFeature() +{ + delete _internal; +} + +RubberbandFrameHandle *RubberbandMultiEllipseFeature::getFrameHandle() const +{ + return _internal->_frameHandle.get(); +} + +QPointF RubberbandMultiEllipseFeature::centerCoordinate(CoordinateSystem cs) const +{ + if (Rubberband::_internal->_handleList.isEmpty()) + return {}; + RubberbandPointHandle *handlePointCenter = dynamic_cast(Rubberband::_internal->_handleList.first().get()); + + if (!handlePointCenter) + return {}; + + return projectCoordinateFromMap(cs, handlePointCenter->getCoordinate()._mapCoordinate); +} + +QVector RubberbandMultiEllipseFeature::radii() const +{ + QPointF center = centerCoordinate(CoordinateSystem::Map); + if (center.isNull()) + return {}; + if (_internal->_ellipseHandleList.isEmpty()) + return {}; + + QVector ret; + ret.reserve(_internal->_ellipseHandleList.size()); + + for (SituationMap::RefPointer handle : std::as_const(_internal->_ellipseHandleList)) + { + if (!handle) + continue; + ret.append(handle->getRadius()); + } + return ret; +} + +QPointF RubberbandMultiEllipseFeature::getBiggestRadius() const +{ + QPointF max(0,0); + const QVector &radiiCont = radii(); + + for (const QPointF &item : std::as_const(radiiCont)) + { + max.setX( std::max(max.x(), item.x()) ); + max.setY( std::max(max.y(), item.y()) ); + } + return max; +} + +bool RubberbandMultiEllipseFeature::setCenterAndRadii(const QPointF ¢er, CoordinateSystem cs, + const QVector &radii) +{ + if (center.isNull() || radii.isEmpty()) + return false; + + const QPointF &myCenter = projectCoordinateToMap(cs, center); + + QVector coordinateVector; + coordinateVector.reserve(radii.size()+1); + + // Center: + coordinateVector.append(myCenter); + + // radii: + for (const QPointF& radius : std::as_const(radii)) + coordinateVector.append(myCenter + radius); + + setCoordinateVector(coordinateVector, CoordinateSystem::Map); + + return true; +} + +bool RubberbandMultiEllipseFeature::setCenterAndRadii2(const QPointF ¢er, CoordinateSystem cs, + const QVector &radiiWithConstraints) +{ + if (center.isNull() || radiiWithConstraints.isEmpty()) + return false; + + const QPointF &myCenter = projectCoordinateToMap(cs, center); + + QVector coordinateVector; + coordinateVector.reserve(radiiWithConstraints.size()+1); + + // Center: + coordinateVector.append(myCenter); + + // Center with the radii: + for (const RubberbandMultiEllipseFeature::RadiusWithContraints& radiusItem : std::as_const(radiiWithConstraints)) + if (radiusItem.m_radius > 0) + coordinateVector.append(myCenter + QPointF(radiusItem.m_radius, radiusItem.m_radius)); + + + Rubberband::_internal->_handleList.clear(); + for (const auto &p : coordinateVector) + constructPoint(p, CoordinateSystem::Map); + + // are we still in construction or in modification? + // from scratch: constraints are provided, but no radius + // predefined: radius is provided, maybe constraints + if (coordinateVector.size() > 1) + enableModificationState(); + update(); + activateButtons(); + + return true; +} + + +void RubberbandMultiEllipseFeature::prepareFixedNumberOfRadiiWithConstraints(const QVector &radiiContraints) +{ + // for creation from scratch, just min/max should be prepared. + // for creation with predefined values, the m_radius is relevant. + _internal->_preparedRadiiConstraints = radiiContraints; +} + + +QPointF RubberbandMultiEllipseFeature::getRotatedPoint(const QPointF §or) const +{ + // Rotation of entire Rubberband + qreal sinRect = 0, cosRect = 1; + getSceneRotation(sinRect, cosRect); + const double angleRad = SituationMap::MathUtils::getMathAngle(QPointF(cosRect, sinRect)); // angle rad + const double clockwiseAngleRad = ((2*LtsCoordTrans::pi()) - angleRad); // angle in rad; + + // map clockwise rotation + QPointF point; + point.setX( sector.x()*std::cos(clockwiseAngleRad) + sector.y()*std::sin(clockwiseAngleRad)); + point.setY( -sector.x()*std::sin(clockwiseAngleRad) + sector.y()*std::cos(clockwiseAngleRad)); + + return point; +} + +void RubberbandMultiEllipseFeature::enableSector(bool on) +{ + _internal->_sectorEnabled = on; +} +bool RubberbandMultiEllipseFeature::isSectorEnabled() const +{ + return _internal->_sectorEnabled; +} + +QPointF RubberbandMultiEllipseFeature::sectorStart() const +{ + return getRotatedPoint(_internal->_sectorOStart); +} + +qreal RubberbandMultiEllipseFeature::sectorStartDegMath() const +{ + const QPointF &point = sectorStart(); + return MathUtils::getMathAngle(point) * LtsCoordTrans::rad2deg(); +} + +QPointF RubberbandMultiEllipseFeature::sectorEnd() const +{ + return getRotatedPoint(_internal->_sectorOEnd); +} + +qreal RubberbandMultiEllipseFeature::sectorEndDegMath() const +{ + const QPointF &point = sectorEnd(); + return MathUtils::getMathAngle(point) * LtsCoordTrans::rad2deg(); +} + +void RubberbandMultiEllipseFeature::setSectorStart(qreal sinPhi, qreal cosPhi) +{ + _internal->_sectorOStart = QPointF(cosPhi, sinPhi); + enableSelectedState(); + update(); +} + +void RubberbandMultiEllipseFeature::setSectorEnd(qreal sinPhi, qreal cosPhi) +{ + _internal->_sectorOEnd = QPointF(cosPhi, sinPhi); + enableSelectedState(); + update(); +} + +void RubberbandMultiEllipseFeature::setSectorAngleFixed(bool on) +{ + _internal->_sectorFixedAngleEnabled = on; + update(); +} + +bool RubberbandMultiEllipseFeature::isSectorFixedAngeEnabled() const +{ + return _internal->_sectorFixedAngleEnabled; +} + +QVector RubberbandMultiEllipseFeature::coordinateVector(CoordinateSystem cs) const +{ + QVector coordinateVector; + coordinateVector.reserve(Rubberband::_internal->_handleList.size()); + + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle *handlePointPtr = dynamic_cast(handle.get()); + + coordinateVector.push_back(projectCoordinateFromMap(cs, handlePointPtr->getCoordinate()._mapCoordinate)); + } + return coordinateVector; +} + +QPointF RubberbandMultiEllipseFeature::singleCoordinate(unsigned int idx, CoordinateSystem cs ) const +{ + if (idx < (unsigned int)Rubberband::_internal->_handleList.size()) + { + const RubberbandPointHandle * handle = dynamic_cast(Rubberband::_internal->_handleList[idx].get()); + + return projectCoordinateFromMap( cs, handle->getCoordinate()._mapCoordinate ); + } + return QPointF(); +} + +void RubberbandMultiEllipseFeature::setCoordinateVector(const QVector &coordinateVector, CoordinateSystem cs) +{ + Rubberband::_internal->_handleList.clear(); + + for (const auto &p : coordinateVector) + constructPoint(p, cs); + enableModificationState(); + update(); + activateButtons(); +} + +bool RubberbandMultiEllipseFeature::getRadiusXY(const QPointF &scenePos, qreal &radiusX_out, qreal &radiusY_out) +{ + if (Rubberband::_internal->_handleList.isEmpty()) + return false; + + RubberbandPointHandle *handlePointCenter = dynamic_cast(Rubberband::_internal->_handleList.first().get()); + if (!handlePointCenter) + return false; + + const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointCenter->getCoordinate(); + RubberbandHandle::RubberbandCoordinate currentMapPos(Rubberband::_internal->_mapView, scenePos); + + radiusX_out = fabs(mapCenter._mapCoordinate.x() - currentMapPos._mapCoordinate.x()); + radiusY_out = fabs(mapCenter._mapCoordinate.y() - currentMapPos._mapCoordinate.y()); + return true; +} + +void RubberbandMultiEllipseFeature::constructPoint(const QPointF &point, CoordinateSystem cs) +{ + const QPointF &mapPos = projectCoordinateToMap(cs, point); + + RubberbandHandle::RubberbandCoordinate coordinate(Rubberband::_internal->_mapView, convert(mapPos, Rubberband::_internal->_mapView->map()->spatialReference())); + RubberbandHandle *handlePtr = constructPoint(coordinate._sceneCoordinate); + if (handlePtr) + { + static_cast(handlePtr)->setCoordinate(coordinate); + handlePtr->setSelection(false); + } +} + +RubberbandHandle *RubberbandMultiEllipseFeature::constructPoint(const QPointF &scenePos) +{ + DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION + << " RubberbandMultiEllipseFeature::constructPoint, _handleList.size " << Rubberband::_internal->_handleList.size(); + RubberbandHandle *handlePtr{nullptr}; + + if ((_maximumPointCount>0) && ((unsigned int)Rubberband::_internal->_handleList.size() >= _maximumPointCount)) + { + DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " skip due to _maximumPointCount (_sectorInputIsRunning)"; + return nullptr; + } + + + if (!Rubberband::_internal->_handleList.empty()) + { + handlePtr = Rubberband::_internal->_handleList.first().get(); + if (handlePtr) + { + // center == first + const RubberbandHandle::RubberbandCoordinate &firstPosCenterPos = static_cast(handlePtr)->getCoordinate(); + + qreal radiusX_out, radiusY_out; + if (getRadiusXY(scenePos, radiusX_out, radiusY_out)) + { + bool minMaxOk = checkMinMaxRadiusConstraint(radiusX_out, radiusY_out); + if (minMaxOk) + { + RubberbandEllipseHandle *temp = new RubberbandEllipseHandle(Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene); + + if (_internal->_constrainQuadratic) + radiusX_out = radiusY_out = std::max(radiusX_out, radiusY_out); + + temp->setRect(firstPosCenterPos, radiusX_out, radiusY_out, 0, 1); + + QColor fillColor = _fillColor; + fillColor.setAlphaF(_fillColor.alphaF()*0.5); + temp->setFillColor(fillColor); + + QColor selectionColor = _handleSelectionColor; + selectionColor.setAlphaF(_handleSelectionColor.alphaF()*0.5); + temp->setSelectionColor(selectionColor); + + _internal->_ellipseHandleList.append(RefPointer(static_cast(temp))); + } else { + DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION + << " SKIP RubberbandMultiEllipseFeature::constructPoint, " + << ", radiusX_out" << radiusX_out << ", radiusY_out" << radiusY_out; + return nullptr; + } + } + } + } + + // + // RubberbandPointHandle einfügen + // + handlePtr = new RubberbandPointHandle(Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, scenePos); + handlePtr->setSelection(); + handlePtr->setIsFirst(Rubberband::_internal->_handleList.empty()); // mark centerPoint with 'isFirst' to simplify later recognation + Rubberband::_internal->_handleList.append(SituationMap::RefPointer(handlePtr)); + + // activateButtons? + if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount) + activateButtons(); + + if ((!_maximumPointCount) || ((unsigned int)Rubberband::_internal->_handleList.size() < _maximumPointCount)) + { + if (!_internal->_previewEllipse) + _internal->_previewEllipse = new DrawableEllipse(Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene); + _internal->_previewEllipse->setVisibility(true); + } + else + { + _internal->_previewEllipse = nullptr; + } + return handlePtr; +} + +bool RubberbandMultiEllipseFeature::checkLengthConstrains(const QPointF &scenePos, const QPointF &deltaScenePos) +{ + Q_UNUSED(scenePos); + Q_UNUSED(deltaScenePos); + return true; + +/* + if (_internal->_complexGeometry) + { + markFailedConstrains(QPointF(), false); + return true; + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Checking constrains for moving selected coordinates (handles) + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + bool allHandlesSelected = true; + QVector coordinateVector; + coordinateVector.reserve(Rubberband::_internal->_handleList.size()); + + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle *handlePointPtr = dynamic_cast(handle.get()); + if (handlePointPtr) + { + coordinateVector.append(handlePointPtr->getCoordinate()); + if (handlePointPtr->isSelected()) + { + RubberbandHandle::RubberbandCoordinate &coordinate = coordinateVector.back(); + coordinate.updateSceneCoordinate(Rubberband::_internal->_mapView, coordinate._sceneCoordinate + deltaScenePos); + } + else allHandlesSelected = false; + } + } + + if ((allHandlesSelected) || ((_internal->_frameHandle) && (_internal->_frameHandle->isSelected()))) + { + markFailedConstrains(QPointF(), false); + return true; + } + + // + // check totalLength + // + if (_maximumLength > 0.0) + { + qreal totalLength = 0.0; + const Esri::ArcGISRuntime::Point *lastMapCoord = nullptr; // Check Quartz + for (int i = 0; i < coordinateVector.size(); ++i) + { + const RubberbandHandle::RubberbandCoordinate &coordinate = coordinateVector[i]; + if (lastMapCoord) + { + qreal x = coordinate._mapCoordinate.x() - lastMapCoord->x(); + qreal y = coordinate._mapCoordinate.y() - lastMapCoord->y(); + + totalLength += sqrt(x * x + y * y); + } + lastMapCoord = &coordinateVector.at(i)._mapCoordinate; + } + if (totalLength > _maximumLength) + { + markFailedConstrains(scenePos, true); + return false; + } + } + + // + // check maximumSegmentLength + // + const RubberbandHandle::RubberbandCoordinate *mapPointPtr = nullptr; + bool lastSelected = false; + auto handleIter = Rubberband::_internal->_handleList.constBegin(); + + for (int i = 0; i < coordinateVector.size(); ++i) + { + const RubberbandHandle::RubberbandCoordinate &coordinate = coordinateVector[i]; + bool isSelected = (*handleIter)->isSelected(); + + if ((isSelected || lastSelected) && (mapPointPtr)) + { + if (_maximumSegmentLength > 0.0) + { + qreal x = coordinate._mapCoordinate.x() - mapPointPtr->_mapCoordinate.x(); + qreal y = coordinate._mapCoordinate.y() - mapPointPtr->_mapCoordinate.y(); + + if (sqrt(x * x + y * y) > _maximumSegmentLength) + { + markFailedConstrains(scenePos, true); + return false; + } + } + // looking for identical coordinate with last point + qreal x = coordinate._sceneCoordinate.x() - mapPointPtr->_sceneCoordinate.x(); + qreal y = coordinate._sceneCoordinate.y() - mapPointPtr->_sceneCoordinate.y(); + + if ((x * x + y * y) <= 1) + { + markFailedConstrains(scenePos, true); + return false; + } + } + ++handleIter; + mapPointPtr = &coordinateVector.at(i); + lastSelected = isSelected; + } // for + + markFailedConstrains(QPointF(), false); + return true; +*/ +} + +bool RubberbandMultiEllipseFeature::checkMinMaxRadiusConstraint(const QPointF &curScenePos) +{ + qreal radiusX_out, radiusY_out; + if (!getRadiusXY(curScenePos, radiusX_out, radiusY_out)) + return true; + + if (_internal->_constrainQuadratic) + radiusX_out = radiusY_out = std::max(radiusX_out, radiusY_out); + + return checkMinMaxRadiusConstraint(radiusX_out, radiusY_out); +} + +bool RubberbandMultiEllipseFeature::checkMinMaxRadiusConstraint(qreal radiusX, qreal radiusY, int idxOfConstraint) +{ + // Which radius is relevant? + if (idxOfConstraint < 0) + idxOfConstraint = Rubberband::_internal->_handleList.size() - 1; // '-1' - keep handle for center (isFirst) in mind + + if ((idxOfConstraint < 0) || (idxOfConstraint >= _internal->_preparedRadiiConstraints.size())) + return true; + + const RadiusWithContraints &curConstraints = _internal->_preparedRadiiConstraints[idxOfConstraint]; + +// qCritical() << " radiusX = " << radiusX; +// qCritical() << " radiusY = " << radiusY; + + bool bMinOk = true; + double minRadius = curConstraints.m_minRadius; + if (minRadius > 0) + bMinOk = ((radiusX >= minRadius) && (radiusY >= minRadius)); + + bool bMaxOk = true; + double maxRadius = curConstraints.m_maxRadius; + if (maxRadius > 0) + bMaxOk = ((radiusX <= maxRadius) && (radiusY <= maxRadius)); + + if (!bMinOk) + INFO_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " checkMinMaxRadiusConstraintNew false by bMinOk" + << ", idxOfConstraint=" << idxOfConstraint + << ", radiusX=" << radiusX << ", radiusY=" << radiusY + << ", minRadius=" << minRadius << ", maxRadius=" << maxRadius; + if (!bMaxOk) + INFO_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " checkMinMaxRadiusConstraintNew false by bMaxOk" + << ", idxOfConstraint=" << idxOfConstraint + << ", radiusX=" << radiusX << ", radiusY=" << radiusY + << ", minRadius=" << minRadius << ", maxRadius=" << maxRadius; + return bMinOk && bMaxOk; +} + +bool RubberbandMultiEllipseFeature::checkLengthConstrains(unsigned int checkHandleIdx, const QPointF &scenePos) +{ + Q_UNUSED(checkHandleIdx); + + if (!checkMinMaxRadiusConstraint(scenePos)) + { + markFailedConstrains(scenePos, true); + return false; + } + markFailedConstrains(QPoint(), true); + + return true; + + // return true -> construct point + // return false -> don not construct point + +/* + if (_internal->_complexGeometry) + { + markFailedConstrains(QPointF(), false); + return true; + } + + bool markFailedConstrainsFlag = false; + + // calculate pixel size for later use + RubberbandDrawable::RubberbandCoordinate pi1(Rubberband::_internal->_mapView, scenePos); + RubberbandDrawable::RubberbandCoordinate pi2(Rubberband::_internal->_mapView, scenePos + QPointF(1, 1)); + + qreal piX = pi2._mapCoordinate.x() - pi1._mapCoordinate.x(); + qreal piY = pi2._mapCoordinate.y() - pi1._mapCoordinate.y(); + qreal pixelSize = sqrt(piX * piX + piY * piY); + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Checking constrains for adding single coordinate at end of polyline + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + RubberbandHandle::RubberbandCoordinate mapPos(Rubberband::_internal->_mapView, scenePos); + + if ((!Rubberband::_internal->_handleList.empty()) && (_maximumLength > 0.0)) + { + qreal totalLength = 0.0; + RubberbandHandle::RubberbandCoordinate mapPoint; + + QList>::iterator it = Rubberband::_internal->_handleList.begin(); + + for (; it != Rubberband::_internal->_handleList.end(); ++it) + { + RubberbandPointHandle *handlePointPtr = dynamic_cast(it->get()); + if (handlePointPtr) + { + if (it != Rubberband::_internal->_handleList.begin()) + { + qreal x = handlePointPtr->getCoordinate()._mapCoordinate.x() - mapPoint._mapCoordinate.x(); + qreal y = handlePointPtr->getCoordinate()._mapCoordinate.y() - mapPoint._mapCoordinate.y(); + + totalLength += sqrt(x * x + y * y); + } + mapPoint = handlePointPtr->getCoordinate(); + } + } + if (totalLength >= _maximumLength) + { + markFailedConstrains(scenePos, true); + return false; + } + + qreal x = mapPos._mapCoordinate.x() - mapPoint._mapCoordinate.x(); + qreal y = mapPos._mapCoordinate.y() - mapPoint._mapCoordinate.y(); + qreal l = sqrt(x * x + y * y); + + qreal d = _maximumLength - totalLength - l - pixelSize; + if (d < 0.0) + { + qreal scale = (1.0 + d / l); + const Esri::ArcGISRuntime::Point newMapCoordinate(mapPoint._mapCoordinate.x() + x * scale, + mapPoint._mapCoordinate.y() + y * scale, + mapPos._mapCoordinate.spatialReference()); + mapPos._mapCoordinate = newMapCoordinate; + markFailedConstrainsFlag = true; + scenePos = mapPos.mapToScene(Rubberband::_internal->_mapView); + } + } + + // check radius + if (!checkMinMaxRadiusConstraint(scenePos)) + { + markFailedConstrains(scenePos, true); + return false; + +// markFailedConstrainsFlag = true; +// return true; + } + + markFailedConstrains(scenePos, markFailedConstrainsFlag); + + return true; +*/ +} + +bool RubberbandMultiEllipseFeature::selectHandles(int x, int y, const QPointF &scenePos) +{ + bool eventHandled = false; + RubberbandHandle *handlePtr = pickHandle(x, y); + + _internal->_addVertexAtEnd = false; + + if (handlePtr && handlePtr->isVisible()) + { + handlePtr->setSelection(); + enableSelectedState(); + eventHandled = true; + } + + if (!isEnabledSelectedState()) + { + // ok doesn't hit any point or line handle now looking to frame handle + if (_internal->_frameHandle && _internal->_frameHandle->isPositionInside(scenePos)) + { + _internal->_frameHandle->setSelection(true); + enableSelectedState(); + eventHandled = true; + } + + if (_internal->_rotationHandle && _internal->_rotationHandle->isPositionInside(scenePos)) + { + _internal->_rotationHandle->setSelection(true); + enableSelectedState(); + eventHandled = true; + } + } + return eventHandled; +} + +void RubberbandMultiEllipseFeature::updateHandles(const QPointF &scenePos, const QPointF &deltaMove) +{ + Q_UNUSED(deltaMove); + bool constrainsComplied = true; + + if (Rubberband::_internal->_handleList.size() < 2) + return; + + if ((_internal->_rotationHandle.valid()) && _internal->_rotationHandle->isSelected()) + { + const RubberbandPointHandle *handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList.first().get()); + if (!handlePointPtr) + return; + const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointPtr->getCoordinate(); + + + // _internal->_centerScene --> centerScene + const QPointF ¢erScene = mapCenter._sceneCoordinate; + const Esri::ArcGISRuntime::Point ¢erMapCoords = mapCenter._mapCoordinate; + const QPointF centerMapCoordsAsPointF(centerMapCoords.x(), centerMapCoords.y()); + + qreal sinA = scenePos.x() - centerScene.x(); // _internal->_centerScene.x(); + qreal cosA = - scenePos.y() + centerScene.y(); + qreal len = sqrt(cosA*cosA+sinA*sinA); + + cosA /= len; + sinA /= len; + +// for circle it´s not really helpfull to rotate the handles -> skip here +// for (auto handle : std::as_const(Rubberband::_internal->_handleList)) +// { +// RubberbandPointHandle *handlePointPtr = dynamic_cast(handle.get()); +// if (!handlePointPtr) +// continue; + +// QPointF p = MathUtils::rotate( handlePointPtr->getCoordinate()._sceneCoordinate, centerScene, -_internal->_oVec.y(), _internal->_oVec.x() ); +// p = MathUtils::rotate( p, centerScene, sinA, cosA ); +// handlePointPtr->updateCoordinate( p ); + +// p.setX( handlePointPtr->getCoordinate()._mapCoordinate.x() ); +// p.setY( handlePointPtr->getCoordinate()._mapCoordinate.y() ); +// } // for + + qreal dSin = sinA*_internal->_oVec.x() - cosA*_internal->_oVec.y(); // addition theorem + qreal dCos = cosA*_internal->_oVec.x() + sinA*_internal->_oVec.y(); + + _internal->_lastCoordinateChange.setToIdentity(); // clear matrix + _internal->_lastCoordinateChange.putTranslation( -centerMapCoordsAsPointF ); // move center to (0,0) + _internal->_lastCoordinateChange.putRotate( QPointF(dCos, dSin) ); // do rotation + _internal->_lastCoordinateChange.putTranslation( centerMapCoordsAsPointF ); // move center back + + _internal->_oVec.setX(cosA); + _internal->_oVec.setY(sinA); + + _baseInternal->_lastClickPosition = scenePos; + } + + markFailedConstrains( scenePos, !constrainsComplied ); + + update(); +} + +void RubberbandMultiEllipseFeature::resetAction(AbstractEventDispatcher *causer, int x, int y) +{ + if (rubberbandIsAlive()) + { + if (isEnabledConstructionState()) + { + if (_internal->_addVertexAtEnd) + { + Rubberband::_internal->_handleList.removeLast(); + //_internal->_lineHandleList.removeLast(); + } + } + } + Rubberband::resetAction(causer, x, y); +} + +bool RubberbandMultiEllipseFeature::selectAction(AbstractEventDispatcher *, int x, int y) +{ + if (rubberbandIsAlive()) + { + DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " selectAction"; + + QPointF scenePos(pixel2ScenePosition(x, y)); + + // starting a rubberband with radii prepared but without center? + if (!_internal->_preparedRadiiConstraints.isEmpty() && Rubberband::_internal->_handleList.isEmpty()) + { + RubberbandDrawable::RubberbandCoordinate rbC(Rubberband::_internal->_mapView, scenePos); + setCenterAndRadii2(QPointF(rbC._mapCoordinate.x(), rbC._mapCoordinate.y()), CoordinateSystem::Map, + _internal->_preparedRadiiConstraints); + enableSelectedState(); + + _internal->_addVertexAtEnd = true; + _internal->_showCursorPreviewEllipse = true; + + return true; + } + + + if (_baseInternal->buttonProxyActive(scenePos)) + return false; + + if (isEnabledConstructionState()) + disenableMultiSelectedState(); + + _baseInternal->_lastClickPosition = scenePos; + + bool eventHandled = selectHandles(x, y, scenePos); + if ((!eventHandled) && (isEnabledConstructionState())) + { + _internal->_addVertexAtEnd = true; + _internal->_showCursorPreviewEllipse = true; + + deSelectHandles(); + + if (checkLengthConstrains(1, scenePos)) + { + constructPoint(scenePos); + enableSelectedState(); + _baseInternal->_lastClickPosition = scenePos; + } + + eventHandled = true; + } + else + _internal->_addVertexAtEnd = false; + + if (eventHandled) + { + update(); + // deactivateButtons(); + } + + return eventHandled; + } + return false; +} + +bool RubberbandMultiEllipseFeature::setCoordinateAction(AbstractEventDispatcher *causer, int x, int y) +{ + Q_UNUSED(causer); + + if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) + { + QPointF scenePos(pixel2ScenePosition(x, y)); + Rubberband::_internal->_lastClickPosition = scenePos; + + if (Rubberband::_internal->_handleList.isEmpty()) + { + disenableSelectedState(); + disenableMultiSelectedState(); + return false; + } + } + return false; + + + // ----------------------------------- +/* + markFailedConstrains(QPointF(), false); + + if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) + { + bool eventHandled = false; + QPointF scenePos(pixel2ScenePosition(x, y)); + + Rubberband::_internal->_lastClickPosition = scenePos; + + if (_internal->_addVertexAtEnd) // isEnabledConstructionState()) + { + if (Rubberband::_internal->_handleList.isEmpty()) + { + disenableSelectedState(); + disenableMultiSelectedState(); + return false; + } + + if ((!isEnabledSelectedState()) && (_baseInternal->buttonProxyActive(scenePos))) + { + deSelectHandles(); + return false; + } + + if (checkLengthConstrains(2, scenePos)) + { + QPointF deltaMove = scenePos - _baseInternal->_lastClickPosition; + + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle *handlePointPtr = dynamic_cast(handle.get()); + if ((handlePointPtr) && ((handlePointPtr->isSelected()))) + { + handlePointPtr->setSelection(false); + handlePointPtr->moveCoordinate(deltaMove); + eventHandled = true; + } + } + } + markFailedConstrains(QPointF(), false); + + if ((_maximumPointCount > 0) && ((unsigned int)Rubberband::_internal->_handleList.size() >= _maximumPointCount)) + { + INFO_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " do Sector now..."; + + _internal->_showCursorPreviewEllipse = false; + // continue contructionState, so we are able to move the whole object by moving the first handle (center) + + // reset _lastClickPosition to improve ButtonPosition + _baseInternal->_lastClickPosition = QPointF(-1,-1); + + // remove former selection: + for (SituationMap::RefPointer handle : std::as_const(_internal->_ellipseHandleList)) + if (handle) + handle->setSelection(false); + deSelectHandles(); + + update(); + + // do not complete + // return false; + // completeStateAction(causer, x, y); + } + if (eventHandled) + update(); + if (_baseInternal->buttonProxyActive(scenePos)) + return false; + updateButtonPosition(); + activateButtons(); + } + else + { + if (isEnabledSelectedState()) + { + bool frameIsSelected(((_internal->_frameHandle != nullptr) && (_internal->_frameHandle->isSelected()))); + + QPointF deltaMove = scenePos - _baseInternal->_lastClickPosition; + + if (!checkLengthConstrains(scenePos, deltaMove)) + { + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle *handlePointPtr = dynamic_cast(handle.get()); + if (handlePointPtr) + handlePointPtr->setSelection(false); + } + if (_internal->_frameHandle) + _internal->_frameHandle->setSelection(false); + updateButtonPosition(); + activateButtons(); + disenableSelectedState(); + disenableMultiSelectedState(); + markFailedConstrains(QPointF(), false); + return true; + } + markFailedConstrains(QPointF(), false); + + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle *handlePointPtr = dynamic_cast(handle.get()); + if ((handlePointPtr) && ((handlePointPtr->isSelected()) || (frameIsSelected))) + { + handlePointPtr->setSelection(false); + handlePointPtr->moveCoordinate(deltaMove); + eventHandled = true; + } + } + if (frameIsSelected) + { + _internal->_frameHandle->setSelection(false); + eventHandled = true; + } + + disenableSelectedState(); + disenableMultiSelectedState(); + } + + if (isEnabledConstructionState()) + { + _internal->_addVertexAtEnd = true; + _internal->_showCursorPreviewEllipse = true; + + updatePreviewEllipse(scenePos); + } + else if (_baseInternal->buttonProxyActive(scenePos)) + return false; + + if (eventHandled) + update(); + updateButtonPosition(); + activateButtons(); + + } + return eventHandled; + } + return false; +*/ +} + +void RubberbandMultiEllipseFeature::updatePreviewEllipse(const QPointF &scenePosition) +{ + DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " START"; +// qCritical() << " RubberbandMultiEllipseFeature::updatePreviewEllipse" +// << ", scenePosition " << scenePosition +// << ", _addVertexAtEnd " << _internal->_addVertexAtEnd +// << ", isEnabledConstructionState " << isEnabledConstructionState() +// << " _internal->_handleList.size() " <_handleList.size() +// << " _ellipseHandleList.size() " <<_internal->_ellipseHandleList.size(); + + if (isEnabledConstructionState() && (!Rubberband::_internal->_handleList.isEmpty()) && (_internal->_previewEllipse)) + { + // center / first point + RubberbandPointHandle *handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList.first().get()); + if ((handlePointPtr) && (_internal->_addVertexAtEnd)) + { + RubberbandHandle::RubberbandCoordinate mapCenter = handlePointPtr->getCoordinate(); + RubberbandDrawable::RubberbandCoordinate rbCoordinate(Rubberband::_internal->_mapView, scenePosition); + + _internal->_previewEllipse->setVisibility(_internal->_showCursorPreviewEllipse); + _internal->_previewEllipse->setDrawColor(_handleSelectionColor); + + int distX = fabs(mapCenter._mapCoordinate.x() - rbCoordinate._mapCoordinate.x()); + int distY = fabs(mapCenter._mapCoordinate.y() - rbCoordinate._mapCoordinate.y()); + if (_internal->_constrainQuadratic) + distX = distY = std::max(distX, distY); + _internal->_previewEllipse->setRect(QPointF(mapCenter._mapCoordinate.x(), mapCenter._mapCoordinate.y()), + distX, distY, 0, 1); + + // sync FrameHandle: + updateFrameHandle(scenePosition); + } + else + { + _internal->_previewEllipse->setVisibility(false); + } + return; + } + else + { + _internal->_previewEllipse = nullptr; + } +} + +bool RubberbandMultiEllipseFeature::moveAction(AbstractEventDispatcher *, int x, int y) +{ + DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " START"; + + adjustConnectedPreviewPixmap(x, y); + + if (rubberbandIsAlive()) + { + QPointF scenePosition(pixel2ScenePosition(x, y)); + + //------------------------------ + // sectorStartHand, _sectorEndHandle + { + if (_internal->_sectorStartHandle.valid()) + { + if (_internal->_sectorStartHandle->isPositionInside(scenePosition)) + { + _internal->_sectorStartHandle->setSelection(true); + _internal->_frameHandle = nullptr; + update(); + return false; + } + else + _internal->_sectorStartHandle->setSelection(false); + } + if (_internal->_sectorEndHandle.valid()) + { + if (_internal->_sectorEndHandle->isPositionInside(scenePosition)) + { + _internal->_sectorEndHandle->setSelection(true); + _internal->_frameHandle = nullptr; + update(); + return false; + } + else + _internal->_sectorEndHandle->setSelection(false); + } + } + //------------------------------ + //------------------------------ + //------------------------------ + + //------------------------------ + if (_internal->_rotationHandle.valid()) + _internal->_rotationHandle->setSelection(_internal->_rotationHandle->isPositionInside(scenePosition)); + + + if (_baseInternal->buttonProxyActive(scenePosition)) + return false; + if (!Rubberband::_internal->_mapView) + return false; + + if (!isEnabledMultiSelectedState()) + deSelectHandles(); + + RubberbandHandle *handlePtr = pickHandle(x, y); + if (!handlePtr) + { + _internal->_showCursorPreviewEllipse = isEnabledConstructionState(); + } + else + { + _internal->_showCursorPreviewEllipse = false; + + // set handlePtr selected... + handlePtr->setSelection(); + + // and updateFrameHandle if not first handle: + RubberbandPointHandle *handlePointPtr = dynamic_cast(handlePtr); + if (handlePointPtr) + { + if (handlePointPtr->isFirst()) + updateFrameHandle({}); + else + updateFrameHandle(handlePointPtr->getCoordinate()._sceneCoordinate); + } + } + + if (isEnabledConstructionState()) + { + if (checkLengthConstrains(1, scenePosition)) + { + updatePreviewEllipse(scenePosition); + + if (isSignalConnected(QMetaMethod::fromSignal(&RubberbandMultiEllipseFeature::currentCursorPositionChanged))) + { + RubberbandDrawable::RubberbandCoordinate mapCoord(Rubberband::_internal->_mapView, scenePosition); + emit currentCursorPositionChanged(Point3D(mapCoord._mapCoordinate.x(), mapCoord._mapCoordinate.y(), 0.0)); + } + } + } + return false; + } + return false; +} + +bool RubberbandMultiEllipseFeature::movePickedAction(AbstractEventDispatcher *, int x, int y) +{ + DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " START" + << ", isEnabledSelectedState " << isEnabledSelectedState(); + + adjustConnectedPreviewPixmap(x, y); + + if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) + { + QPointF scenePos(pixel2ScenePosition(x,y)); + + updateHandles(scenePos, scenePos-_baseInternal->_lastClickPosition); + + + if (isEnabledSelectedState()) + { + // sectorHandles: + if (!isSectorFixedAngeEnabled()) + { + auto updateHandle = [this](RubberbandPointHandle * handle, const RubberbandHandle::RubberbandCoordinate & pos, QPointF & oVector ) -> bool + { + if (!handle) + return false; + if (!handle->isSelected()) + return false; + + RubberbandPointHandle *handlePointCenter = dynamic_cast(Rubberband::_internal->_handleList.first().get()); + if (!handlePointCenter) + return false; + const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointCenter->getCoordinate(); + + qreal sinRect = 0; + qreal cosRect = 1; + getSceneRotation( sinRect, cosRect); + + qreal cosA = pos._mapCoordinate.x() - mapCenter._mapCoordinate.x(); + qreal sinA = pos._mapCoordinate.y() - mapCenter._mapCoordinate.y(); + qreal len = sqrt(cosA*cosA+sinA*sinA); + + cosA /= len; + sinA /= len; + + oVector.setX( cosA*cosRect + sinA*sinRect ); + oVector.setY( sinA*cosRect - cosA*sinRect ); + update(); + return true; + }; // updateHandle + + RubberbandHandle::RubberbandCoordinate pos(Rubberband::_internal->_mapView, scenePos); + updateHandle( _internal->_sectorStartHandle.get(), pos, _internal->_sectorOStart); + updateHandle( _internal->_sectorEndHandle.get() , pos, _internal->_sectorOEnd ); + } + // // return true; + //------------------------------ + //------------------------------ + //------------------------------ + + // look for (first) selected PointHandle: + RubberbandPointHandle *selectedPointHandlePtr{nullptr}; + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle *handlePointPtr = dynamic_cast(handle.get()); + if (!handlePointPtr) + continue; + if (handlePointPtr->isSelected()) + selectedPointHandlePtr = handlePointPtr; + } // for + const bool bFirstHandlePointSelected = (selectedPointHandlePtr && selectedPointHandlePtr->isFirst()); + + + RubberbandHandle::RubberbandCoordinate rcNewCorner; + + // nur wenn _constrainQuadratic UND nicht der 'isFirst'/centerPoint + if (_internal->_constrainQuadratic && !bFirstHandlePointSelected) + { + RubberbandPointHandle *handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList.first().get()); + if (!handlePointPtr) + return false; + + const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointPtr->getCoordinate(); + + qreal radiusX, radiusY; + if (getRadiusXY(scenePos, radiusX, radiusY)) + radiusX = radiusY = std::max(radiusX, radiusY); + + + RefPointer refPtr(selectedPointHandlePtr); + int idx = Rubberband::_internal->_handleList.indexOf(refPtr); + if (idx > 0) + { + if (!checkMinMaxRadiusConstraint(radiusX, radiusY, idx-1)) + { + update(); // show/create preview!? + return false; + } + } + + // adjusted coord: (prefer topRight) + Esri::ArcGISRuntime::Point newCorner(mapCenter._mapCoordinate.x()+radiusX, + mapCenter._mapCoordinate.y()+radiusY, + mapCenter._mapCoordinate.spatialReference()); + + rcNewCorner = RubberbandHandle::RubberbandCoordinate(Rubberband::_internal->_mapView, newCorner); + } + + + QPointF deltaMove(scenePos - _baseInternal->_lastClickPosition); + if (checkLengthConstrains(scenePos, deltaMove)) + { + const bool frameIsSelected(((_internal->_frameHandle) && (_internal->_frameHandle->isSelected()))); + const bool moveAll = bFirstHandlePointSelected; + + _baseInternal->_lastClickPosition = scenePos; + + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle *handlePointPtr = dynamic_cast(handle.get()); + if ((handlePointPtr) && ((handlePointPtr->isSelected()) || (frameIsSelected||moveAll))) + { + if (rcNewCorner.isNull()) + handlePointPtr->moveCoordinate(deltaMove); + else + handlePointPtr->setCoordinate(rcNewCorner); + } + } + updatePreviewEllipse(scenePos); + update(); + } + return true; + } + + } + return false; +} + +bool RubberbandMultiEllipseFeature::deleteAction(AbstractEventDispatcher *, int x, int y) +{ + if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) + { + if (((unsigned int)Rubberband::_internal->_handleList.size() > _minimumPointCount) && (isEnabledSelectedState())) + { + QList> savedHandleList= Rubberband::_internal->_handleList; + + QPointF scenePosition = pixel2ScenePosition(x, y); + QList>::iterator handleIter = Rubberband::_internal->_handleList.begin(); + + while (handleIter != Rubberband::_internal->_handleList.end()) + { + if (handleIter->get()->isPositionInside(scenePosition)) + { + handleIter = Rubberband::_internal->_handleList.erase(handleIter); + updateFrameHandle({}); + continue; + } + ++handleIter; + } // while handleIter + + if (!checkLengthConstrains(scenePosition, QPointF(0, 0))) + { + // constrain failed, do not delete yet + Rubberband::_internal->_handleList = savedHandleList; + } + update(); + activateButtons(); + + disenableSelectedState(); + disenableMultiSelectedState(); + } + return true; + } + return false; +} + +bool RubberbandMultiEllipseFeature::contextMenuAction(AbstractEventDispatcher *, int, int) +{ + return false; +} + + +bool RubberbandMultiEllipseFeature::completeStateAction(AbstractEventDispatcher *causer, int x, int y) +{ + markFailedConstrains(QPointF(), false); + + if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) + { + if (isEnabledConstructionState()) + { + if (!_finishAfterConstruction) + { + if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount) + { + enableModificationState(); + _internal->_previewEllipse = nullptr; // remove preview + } + update(); + return true; + } + } + + if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount) + return applyAndEndAction(causer, x, y); + } + return false; +} + +bool RubberbandMultiEllipseFeature::undoAction(AbstractEventDispatcher * /*causer*/, int, int) +{ + if (rubberbandIsAlive()) + { + if (!Rubberband::_internal->_handleList.empty()) + { + if (isEnabledConstructionState()) + { + Rubberband::_internal->_handleList.pop_back(); + if (!_internal->_ellipseHandleList.empty()) + _internal->_ellipseHandleList.pop_back(); + + if (_internal->_previewEllipse) + _internal->_previewEllipse->setVisibility(false); + + update(); + activateButtons(); + } + } + return true; + } + return false; +} + +bool RubberbandMultiEllipseFeature::applyAndEndAction(AbstractEventDispatcher *causer, int x, int y) +{ + if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount) + Rubberband::applyAndEndAction(causer, x, y); + else abortAction(causer, x, y); + return true; +} + +void RubberbandMultiEllipseFeature::constrainQuadratic(bool on) +{ + _internal->_constrainQuadratic = on; +} + +void RubberbandMultiEllipseFeature::activateButtons() +{ + _baseInternal->_hasUndoButton = isEnabledConstructionState(); + _baseInternal->_applyIsPossible = ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount); + _baseInternal->_undoIsPossible = (isEnabledConstructionState() && ((unsigned int)Rubberband::_internal->_handleList.size() > _minimumPointCount)); + + Rubberband::activateButtons(); +} + +void RubberbandMultiEllipseFeature::update() +{ + DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " START"; + + Rubberband::update(); + + // update _ellipseHandleList according _handleList: (context modification) + + RubberbandHandle::RubberbandCoordinate centerCoord; + if (!Rubberband::_internal->_handleList.isEmpty()) + { + RubberbandPointHandle *handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList.first().get()); + centerCoord = handlePointPtr->getCoordinate(); + } + + // + // adjust EllipseHandles according size, selection, zOrder: + // + RubberbandPointHandle *selectedHandlePointPtrNotCenter{nullptr}; + int handleIdxMax = 0; + const int handleListSize = Rubberband::_internal->_handleList.size(); + for (int handleIdx=0; handleIdx < handleListSize; ++handleIdx) + { + if (handleIdx >= Rubberband::_internal->_handleList.size()) + continue; + + handleIdxMax = handleIdx; + + RubberbandPointHandle *pointHandle = dynamic_cast(Rubberband::_internal->_handleList[handleIdx].get()); + if (!pointHandle) + continue; + + if (pointHandle->isSelected() && handleIdx>0) + selectedHandlePointPtrNotCenter = pointHandle; + + if (handleIdx==0) + continue; + + // --- now we have finished the first iteration --- + + // 2 pointHandles are required for an elli: + SituationMap::RefPointer ellipseHandle = _internal->_ellipseHandleList[handleIdx-1]; + if (!ellipseHandle) + continue; + + const RubberbandHandle::RubberbandCoordinate &curCoord = pointHandle->getCoordinate(); + qreal distX = fabs(curCoord._mapCoordinate.x() - centerCoord._mapCoordinate.x()); + qreal distY = fabs(curCoord._mapCoordinate.y() - centerCoord._mapCoordinate.y()); + if (_internal->_constrainQuadratic) + distX = distY = std::max(distX, distY); + + if (isSectorEnabled()) + { + // _internal->_sectorOStart + _internal->_oVec + QPointF sectorStartRotated = getRotatedPoint(_internal->_sectorOStart); + QPointF sectorEndRotated = getRotatedPoint(_internal->_sectorOEnd); + + ellipseHandle->setRect(centerCoord, distX, distY, 0, 1, sectorStartRotated, sectorEndRotated); + } + else + ellipseHandle->setRect(centerCoord, distX, distY, 0, 1); + + // transfer selection: + ellipseHandle->setSelection(pointHandle->isSelected()); + + // update zOrder + ellipseHandle->setZValue(handleListSize-handleIdx); + + } // for + + // Maybe there has ist one RubberbandPointHandle deleted (deleteAction) + // remove superfluous ellipse handles + // like: 2 RubberbandPointHandle (handleIdxMax==1) -> 1 RubberbandEllipseHandle + while (_internal->_ellipseHandleList.size() > handleIdxMax) + _internal->_ellipseHandleList.removeLast(); + + if (selectedHandlePointPtrNotCenter) + updateFrameHandle(selectedHandlePointPtrNotCenter->getCoordinate()._sceneCoordinate); + + // transfer selection: + if (_internal->_frameHandle && selectedHandlePointPtrNotCenter) + _internal->_frameHandle->setSelection( selectedHandlePointPtrNotCenter->isSelected() ); + + updateSectorHandles(); + updateRotationHandle(); + DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " END"; +} + + +void RubberbandMultiEllipseFeature::updateFrameHandle(const QPointF &sceneCoordinate) +{ + const RubberbandDrawable::RubberbandCoordinate vertex(Rubberband::_internal->_mapView, sceneCoordinate); + + if (//isEnabledModificationState() && + (!vertex.isNull()) && + !Rubberband::_internal->_handleList.isEmpty() + ) + { + if (!_internal->_frameHandle) + { + _internal->_frameHandle = SituationMap::RefPointer(new RubberbandFrameHandle(Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, _frameHandleMargin)); + _internal->_frameHandle->setSelectable(false); + + _internal->_frameHandle->setDrawColor(_frameColor); + _internal->_frameHandle->setFillColor(_frameFillColor); + _internal->_frameHandle->setSelectionColor(_frameSelectionColor); + _internal->_frameHandle->updateFrameSceneMargin(_frameHandleMargin); + _internal->_frameHandle->drawDisabled((_frameHandleMargin < 0)); + } + } else { + _internal->_frameHandle = nullptr; + } + + if (_internal->_frameHandle) + { + const RubberbandPointHandle *handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList.first().get()); + if (!handlePointPtr) + return; + + const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointPtr->getCoordinate(); + qreal radiusX, radiusY; + if (!getRadiusXY(sceneCoordinate, radiusX, radiusY)) + return; + + if (_internal->_constrainQuadratic) + radiusX = radiusY = std::max(radiusX, radiusY); + + RubberbandHandle::RubberbandCoordinate rcNewCorner1(Rubberband::_internal->_mapView, + Esri::ArcGISRuntime::Point(mapCenter._mapCoordinate.x()+(radiusX), + mapCenter._mapCoordinate.y()+(radiusY), + mapCenter._mapCoordinate.spatialReference())); + + RubberbandHandle::RubberbandCoordinate rcNewCorner2(Rubberband::_internal->_mapView, + Esri::ArcGISRuntime::Point(mapCenter._mapCoordinate.x()-(radiusX), + mapCenter._mapCoordinate.y()-(radiusY), + mapCenter._mapCoordinate.spatialReference())); + + _internal->_frameHandle->updateFrameShape({rcNewCorner1, rcNewCorner2}); + } +} + +void RubberbandMultiEllipseFeature::getSceneRotation(qreal &sinA, qreal &cosA) const +{ + sinA = -_internal->_oVec.y(); + cosA = _internal->_oVec.x(); +} + +Esri::ArcGISRuntime::Point addOffsetToMapCoordinate(const Esri::ArcGISRuntime::Point &mapCoordinate, + const QPointF &offset) +{ + return Esri::ArcGISRuntime::Point(mapCoordinate.x() + offset.x(), + mapCoordinate.y() + offset.y(), + mapCoordinate.spatialReference()); + +} + +void RubberbandMultiEllipseFeature::updateSectorHandles() +{ + if (Rubberband::_internal->_handleList.isEmpty()) + return; + + if ((unsigned int)Rubberband::_internal->_handleList.size() < _minimumPointCount) + return; + + if (!isSectorEnabled()) + return; + + auto ellipse = []( qreal a, qreal b, qreal sinA, qreal cosA ) -> QPointF + { + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Qt does not use the paramater equation of a ellipse to draw sectors. Instead of a simpler term + * is used. For simpleness we use the same of. + * + qreal e2 = 1 - (b*b) / (a*a); + qreal cos2 = cosA * cosA; + qreal f = b / sqrt(1-e2*cos2); + return QPointF( f*cosA, f*sinA ); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + return QPointF(a*cosA,b*sinA); + }; + + auto rotate = [] ( const QPointF & p, qreal sinA, qreal cosA ) -> QPointF { return QPointF( p.x()*cosA - p.y()*sinA, p.y()*cosA + p.x()*sinA ); }; + + + const QPointF &biggestRadius = getBiggestRadius(); + + qreal a = biggestRadius.x(); // getWidth() * 0.5; + qreal b = biggestRadius.y(); // getHeight() * 0.5; + QPointF startP = ellipse(a,b,_internal->_sectorOStart.y(),_internal->_sectorOStart.x() ); + QPointF endP = ellipse(a,b,_internal->_sectorOEnd.y() ,_internal->_sectorOEnd.x() ); + + qreal sinRect = 0; + qreal cosRect = 1; + + getSceneRotation(sinRect, cosRect); + + startP = rotate(startP, sinRect, cosRect) ; // + getCenter(); + endP = rotate( endP, sinRect, cosRect) ; // + getCenter(); + + RubberbandPointHandle *handlePointCenter = dynamic_cast(Rubberband::_internal->_handleList.first().get()); + if (!handlePointCenter) + return; + const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointCenter->getCoordinate(); + + // const Esri::ArcGISRuntime::Point &mapStartP = convert( startP, Rubberband::_internal->_mapView->map()->spatialReference() ); + // const Esri::ArcGISRuntime::Point &mapEndP = convert( endP, Rubberband::_internal->_mapView->map()->spatialReference() ); + const Esri::ArcGISRuntime::Point &mapStartP = addOffsetToMapCoordinate(mapCenter._mapCoordinate, startP); + const Esri::ArcGISRuntime::Point &mapEndP = addOffsetToMapCoordinate(mapCenter._mapCoordinate, endP); + + if (!_internal->_sectorStartHandle.valid()) + _internal->_sectorStartHandle = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapStartP ); + else + _internal->_sectorStartHandle->setCoordinate( RubberbandHandle::RubberbandCoordinate(Rubberband::_internal->_mapView, mapStartP) ); + + _internal->_sectorStartHandle->setVisibility((!isSectorFixedAngeEnabled())/*_internal->_sectorEnabled*/); + _internal->_sectorStartHandle->setDrawColor(Qt::red); + _internal->_sectorStartHandle->setFillColor( _handleFillColor ); + _internal->_sectorStartHandle->setSelectionColor(_handleSelectionColor); + _internal->_sectorStartHandle->setSelectable(true); + + if (!_internal->_sectorEndHandle.valid()) + _internal->_sectorEndHandle = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapEndP ); + else + _internal->_sectorEndHandle->setCoordinate( RubberbandHandle::RubberbandCoordinate(Rubberband::_internal->_mapView, mapEndP) ); + + _internal->_sectorEndHandle->setVisibility(!isSectorFixedAngeEnabled()/*_internal->_sectorEnabled*/); + _internal->_sectorEndHandle->setDrawColor(Qt::red); + _internal->_sectorEndHandle->setFillColor( _handleFillColor ); + _internal->_sectorEndHandle->setSelectionColor(_handleSelectionColor); + _internal->_sectorEndHandle->setSelectable(true); +} + +void RubberbandMultiEllipseFeature::updateRotationHandle() +{ + DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " START" + << ", isSectorFixedAngeEnabled()=" << isSectorFixedAngeEnabled() + << ", _handleList.size=" << Rubberband::_internal->_handleList.size(); + + if (!isSectorEnabled() || !isSectorFixedAngeEnabled() || (Rubberband::_internal->_handleList.size()<2)) + { + _internal->_rotationHandle = nullptr; // destroy + } + else + { + static QPixmap *s_icon = nullptr; + if (!s_icon) + s_icon = new QPixmap( QPixmap( winkelIcon_xpm ).transformed(QTransform::fromScale(0.5,0.5)) ); + + if (!_internal->_rotationHandle) + { + _internal->_rotationHandleSize = std::max( s_icon->width(), s_icon->height() ) * 0.5; + + _internal->_rotationHandle = new RubberbandPointHandle(Rubberband::_internal->_mapView + , Rubberband::_internal->_mapScene + , QPointF(100, 100) // , _internal->getScenePositionRotationHandle(), + , *s_icon ); + + _internal->_rotationHandle->setDrawColor(_handleDrawColor); + _internal->_rotationHandle->setFillColor(_handleFillColor); + _internal->_rotationHandle->setSelectionColor(_handleSelectionColor); + } + + _internal -> _rotationHandle->setSceneOffset(getSceneOffsetRotationHandle( (_frameHandleMargin > 0) ? _frameHandleMargin : 0 ) ); + _internal -> _rotationHandle->updateCoordinate(getScenePositionRotationHandle()); + } + DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " END"; +} + +QPointF RubberbandMultiEllipseFeature::getScenePositionRotationHandle() const +{ + if (Rubberband::_internal->_handleList.size()<2) + return {}; + + RubberbandPointHandle *handlePointCenter = dynamic_cast(Rubberband::_internal->_handleList.first().get()); + if (!handlePointCenter) + return {}; + const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointCenter->getCoordinate(); + + const QPointF &biggestRadius = getBiggestRadius(); + + Esri::ArcGISRuntime::Point handlePosPoint(mapCenter._mapCoordinate.x() + 0, + mapCenter._mapCoordinate.y() + biggestRadius.y(), + mapCenter._mapCoordinate.spatialReference()); + + RubberbandHandle::RubberbandCoordinate handlePos(Rubberband::_internal->_mapView, handlePosPoint); + + QPointF scenePoint (handlePos._sceneCoordinate); + scenePoint.ry() -= _internal->_rotationHandleSize; // keep center of sceneCoordSystem in mind + + qreal sinRect = 0; + qreal cosRect = 1; + getSceneRotation(sinRect, cosRect); + + scenePoint = MathUtils::rotate(scenePoint, mapCenter._sceneCoordinate, -sinRect, cosRect); // be carefull the scene coordinate system is contra clockwise -- sinA must be negated + + return scenePoint; +} + +QPointF RubberbandMultiEllipseFeature::getSceneOffsetRotationHandle(qreal margin) +{ + qreal sinPhi = _internal->_oVec.y(); // * _mapOVec.x() + _oVec.x() * _mapOVec.y(); // Addition-Theorem + qreal cosPhi = _internal->_oVec.x(); // * _mapOVec.x() - _oVec.y() * _mapOVec.y(); + return rotateVertex( QPointF(0,0), 0.0,-(margin+_internal->_rotationHandleSize), sinPhi, cosPhi ); +} + + +} +} // namespace SituationMap::RB diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_ellipse_feature.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_ellipse_feature.h new file mode 100755 index 000000000..4d80e0e42 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_ellipse_feature.h @@ -0,0 +1,254 @@ +/** + * @RDE_HEADER@ + * Rubberband class to define multiple ellipse/circle + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include "rubberband.h" + + +namespace SituationMap +{ +namespace RB +{ +class RubberbandHandle; +class RubberbandFrameHandle; +class DrawableEllipse; + +/** + @class RubberbandMultiEllipseFeature rubberband_multi_circle_feature.h + @brief Implementation of a rubberband to input multi point features, like areals or linears + @see Rubberband +*/ +class SITUATIONMAP_EXPORT RubberbandMultiEllipseFeature : public Rubberband +{ + Q_OBJECT +public: + + /** + @name Construction & Destruction + @{ + */ + +private: + + friend class RubberbandFactory; + friend class RubberbandArrow; + friend class RubberbandCorridor; + friend class RubberbandPolylineWidth; + friend class RubberbandSmoothLine; + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Constructor delegation doesn't work with Visual Studio 2013 + + explicit RubberbandMultiEllipseFeature( SituationMap::Map * map ); + explicit RubberbandMultiEllipseFeature( SituationMap::Map * map, bool complexGeometry ); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + /** + Construction of RubberbandMultiEllipseFeature in most common cases. The parameter parent is + optional. + */ + explicit RubberbandMultiEllipseFeature(SituationMap::Map *map, QObject *parent = nullptr); + + /** + Construction of RubberbandMultiEllipseFeature in case of controlling complex geometry objects, like + splines or multi point symbols. In this case set complexGeometry true. + */ + explicit RubberbandMultiEllipseFeature(SituationMap::Map *map, bool complexGeometry, QObject *parent = nullptr); + +public: + + ~RubberbandMultiEllipseFeature(); + /** @} */ + + bool selectAction(AbstractEventDispatcher *causer, int x, int y) override; + bool setCoordinateAction(AbstractEventDispatcher *causer, int x, int y) override; + bool moveAction(AbstractEventDispatcher *causer, int x, int y) override; + bool movePickedAction(AbstractEventDispatcher *causer, int x, int y) override; + void resetAction(AbstractEventDispatcher *causer, int x, int y) override; + bool contextMenuAction(AbstractEventDispatcher *causer, int x, int y) override; + bool completeStateAction(AbstractEventDispatcher *causer, int x, int y) override; + bool deleteAction(AbstractEventDispatcher *causer, int x, int y) override; + bool undoAction(AbstractEventDispatcher *causer, int x, int y) override; + bool applyAndEndAction(AbstractEventDispatcher *causer, int x, int y) override; + + /** + * @brief If set to true only quadratic rectangles can be created. + */ + void constrainQuadratic( bool on ); + + + /** + @name Setting Constrains + @{ + */ + + /** + Defines minimum count of verticies that has to be defined by users input to complete the rubberband with apply signal. The default value is 2. + */ + inline void minimumRadiusCount(unsigned int x) + { + _minimumPointCount = x+1; + } + + + /** + Defines maximum count of verticies that can be aceppted by the rubberband. A values of 0 stands for undefined. The default is 0. + */ + inline void maximumRadiusCount(unsigned int x) + { + _maximumPointCount = x+1; + } + + + /** + Defines the maximum length of a segment (distance between to adjaced verticies). A value of 0 stands for no limit is set. The default is 0. + */ + inline void maximumSegmentLength(qreal x) { _maximumSegmentLength = (x > 0) ? x : 0.0; } + inline qreal getMaximumSegmentLength() const { return _maximumSegmentLength; } + + /** + Defines the maximum length of the complete polyline. \c enableClosed must be set to false. A value of 0 stands for no limit is set. The default is 0. + */ + inline void maximumLength(qreal x) { _maximumLength = (x > 0) ? x : 0.0; } + /** @} */ + inline qreal getMaximumLength() const { return _maximumLength; } + + + QPointF centerCoordinate(CoordinateSystem cs = CoordinateSystem::Geo) const; + + /** + * @brief radii get vector of radius (QPointF containing radiusX and radius Y in meter) + * @return radii + */ + QVector radii() const; + QPointF getBiggestRadius() const; + + + struct RadiusWithContraints + { + // to be able to supply int, avoid explicit: + RadiusWithContraints(double radius = 0.0, double minRadius = 0.0, double maxRadius = 0.0) + : m_radius(radius), + m_minRadius(minRadius), + m_maxRadius(maxRadius) + {} + double m_radius{0}; + double m_minRadius{0}; + double m_maxRadius{0}; + }; + + bool setCenterAndRadii(const QPointF ¢er, CoordinateSystem cs, const QVector &radii); // no constraints + + bool setCenterAndRadii2(const QPointF ¢er, CoordinateSystem cs, + const QVector &radiiWithConstraints); + + void prepareFixedNumberOfRadiiWithConstraints(const QVector &radiiContraints); + + + // get SectorStartEnd + QPointF getRotatedPoint(const QPointF §or)const; + + + void enableSector(bool on); + bool isSectorEnabled() const; + + /** + * @brief sectorStart in mathematical sense (starts counter-clockwise from 3 o'clock) + * @return sectorStart + */ + QPointF sectorStart() const; + + qreal sectorStartDegMath() const; // in mathematical sense (starts counter-clockwise from 3 o'clock) + + /** + * @brief sectorEnd in mathematical sense (starts counter-clockwise from 3 o'clock) + * @return sectorEnd + */ + QPointF sectorEnd() const; + + qreal sectorEndDegMath() const; // in mathematical sense (starts counter-clockwise from 3 o'clock) + + void setSectorStart(qreal sinPhi, qreal cosPhi); + void setSectorEnd(qreal sinPhi, qreal cosPhi); + + void setSectorAngleFixed(bool on); // true: start and end sectors positon can't be modifed + bool isSectorFixedAngeEnabled() const; + + + virtual QVector coordinateVector(CoordinateSystem cs = CoordinateSystem::Geo) const override; + virtual QPointF singleCoordinate( unsigned int idx = 0, CoordinateSystem cs = CoordinateSystem::Geo ) const override; + + virtual void setCoordinateVector(const QVector &, CoordinateSystem cs = CoordinateSystem::Geo) override; + +signals: + + /** + The signal will be emitted, when the mouse cursor has changed and the rubberband is in construction mode and is accepting new input coordinates. + */ + void currentCursorPositionChanged(const Point3D &mapCoord); + + +protected: + + /* + Forward declaration of InternalData to hold private member variables. Be careful the base class Rubberband + has similary a class for internal data member with name "InternalData". Don't be confused it is not the same. + */ + struct InternalData; + + InternalData *_internal; + + bool _isClosed{false}; + + unsigned int _minimumRadius = 0; + unsigned int _maximumRadius = 0; + + unsigned int _minimumPointCount{2}; // 2 Punkte == 1 Kreis + unsigned int _maximumPointCount{0}; + + qreal _maximumSegmentLength; + qreal _maximumLength; + + void activateButtons() override; + + void update() override; + void updateFrameHandle(const QPointF &sceneCoordinate); // Unfortunately, RubberbandCoordinate can´t be forwarded, so use sceneCoordinate + + void getSceneRotation(qreal &sinA, qreal &cosA) const; + void updateSectorHandles(); + + void updateRotationHandle(); + QPointF getScenePositionRotationHandle() const; + QPointF getSceneOffsetRotationHandle(qreal margin); + + + // some helper methodes + bool getRadiusXY(const QPointF &scenePos, qreal &radiusX_out, qreal &radiusY_out); + + RubberbandHandle *constructPoint(const QPointF &scenePos); + void constructPoint(const QPointF &point, CoordinateSystem cs); + + bool selectHandles(int x, int y, const QPointF &scenePos); + void updateHandles(const QPointF &scenePos, const QPointF &deltaMove); + + bool checkLengthConstrains(unsigned int checkHandleIdx, const QPointF &scenePos); + bool checkLengthConstrains(const QPointF &scenePos, const QPointF &deltaScenePos); + + + bool checkMinMaxRadiusConstraint(const QPointF &curScenePos); + bool checkMinMaxRadiusConstraint(qreal radiusX, qreal radiusY, int idxOfConstraint = -1); + + void updatePreviewEllipse(const QPointF &scenePosition); + + RubberbandFrameHandle *getFrameHandle() const; + +}; /* RubberbandMultiEllipseFeature */ + +} +} /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_point_feature.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_point_feature.cpp new file mode 100755 index 000000000..62fd79efe --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_point_feature.cpp @@ -0,0 +1,1137 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of rubberband class to define multi point feature geoemtry +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_multi_point_feature.h" +#include "rubberband_frame_handle.h" +#include "rubberband_line_handle.h" +#include "rubberband_point_handle.h" +#include "rubberband_drawable.h" +#include "rubberband_handle_proxy.h" +#include "drawable_line.h" +#include "drawable_painter_path.h" +#include "transient_tactical_symbol.h" + +#include "rubberband_internal.h" +#include "rubberband_base_internal.h" + +#include + +#include + +namespace SituationMap { namespace RB +{ + + struct RubberbandMultiPointFeature::InternalData + { + Q_DISABLE_COPY(InternalData) + + QList> _lineHandleList; + SituationMap::RefPointer _frameHandle; + + SituationMap::RefPointer _fillPath; + SituationMap::RefPointer _cursorPreviewLine; + SituationMap::RefPointer _cursorPreviewCloseLine; + SituationMap::RefPointer _previewSymbol; + + bool _complexGeometry; + bool _addVertexAtEnd; + bool _showCursorPreviewLine; + double _checkMaxAngle; + + InternalData( QGraphicsScene * scene, bool complexGeometry ) + : _lineHandleList() + , _frameHandle() + , _fillPath() + , _cursorPreviewLine() + , _cursorPreviewCloseLine() + , _previewSymbol() + , _complexGeometry(complexGeometry) + , _addVertexAtEnd(false) + , _showCursorPreviewLine(false) + , _checkMaxAngle(0.0) + { + Q_UNUSED(scene); + } /* InternalData */ + }; /* RubberbandMultiPointFeature::InternalData */ + + RubberbandMultiPointFeature::RubberbandMultiPointFeature( SituationMap::Map * map, QObject * parent ) + : RubberbandMultiPointFeature(map, false, parent) + { + } /* RubberbandMultiPointFeature::RubberbandMultiPointFeature */ + + + RubberbandMultiPointFeature::RubberbandMultiPointFeature( SituationMap::Map * map, bool complexGeometry, QObject * parent ) + : Rubberband(map,parent) + , _internal(new InternalData(Rubberband::_internal->_mapScene, complexGeometry)) + , _isClosed(false) + , _minimumPointCount(2) + , _maximumPointCount(0) + , _maximumSegmentLength(0.0) + , _maximumLength(0.0) + { + } /* RubberbandMultiPointFeature::RubberbandMultiPointFeature */ + + + RubberbandMultiPointFeature::~RubberbandMultiPointFeature() + { + delete _internal; + } /* RubberbandMultiPointFeature::~RubberbandMultiPointFeature */ + + + void RubberbandMultiPointFeature::setCheckMaxAngle(double phi) + { + _internal->_checkMaxAngle = ((phi > 0.0) && (phi < M_PI)) ? phi : 0.0; + + } // RubberbandMultiPointFeature::setCheckMaxAngle + + + RubberbandFrameHandle * RubberbandMultiPointFeature::getFrameHandle() const { return _internal->_frameHandle.get(); } + + + QVector RubberbandMultiPointFeature::coordinateVector( CoordinateSystem cs ) const + { + QVector coordinateVector; + coordinateVector.reserve(Rubberband::_internal->_handleList.size()); + + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); + + coordinateVector.push_back(projectCoordinateFromMap( cs, handlePointPtr->getCoordinate()._mapCoordinate )); + } + return coordinateVector; + } /* RubberbandMultiPointFeature::coordinateVector */ + + + QPointF RubberbandMultiPointFeature::singleCoordinate(unsigned int idx, CoordinateSystem cs ) const + { + if (idx < (unsigned int)Rubberband::_internal->_handleList.size()) + { + const RubberbandPointHandle * handle = dynamic_cast(Rubberband::_internal->_handleList[idx].get()); + + return projectCoordinateFromMap( cs, handle->getCoordinate()._mapCoordinate ); + } + return QPointF(); + } /* RubberbandMultiPointFeature::singleCoordinate */ + + + void RubberbandMultiPointFeature::setCoordinateVector(const QVector & coordinateVector, CoordinateSystem cs ) + { + Rubberband::_internal->_handleList.clear(); + + for (const auto &p : coordinateVector) + { + constructPoint( p, cs ); + } + enableModificationState(); + update(); + activateButtons(); + } /* RubberbandMultiPointFeature::setCoordinateVector */ + + + void RubberbandMultiPointFeature::constructPoint( const QPointF &point, CoordinateSystem cs ) + { + const QPointF &mapPos = projectCoordinateToMap( cs, point ); + + RubberbandHandle::RubberbandCoordinate coordinate( Rubberband::_internal->_mapView, convert(mapPos, Rubberband::_internal->_mapView->map()->spatialReference()) ); + RubberbandHandle * handlePtr = constructPoint( coordinate._sceneCoordinate ); + if (handlePtr) + { + static_cast(handlePtr)->setCoordinate( coordinate ); + handlePtr->setSelection(false); + } + } /* RubberbandMultiPointFeature::constructPoint */ + + + RubberbandHandle * RubberbandMultiPointFeature::constructPoint( QPointF scenePos ) + { + RubberbandHandle * handlePtr; + + if (!Rubberband::_internal->_handleList.empty()) + { + handlePtr = Rubberband::_internal->_handleList.last().get(); + + if (handlePtr) + { + const RubberbandHandle::RubberbandCoordinate && lastPos = static_cast(handlePtr)->getCoordinate(); + + if (isClosedEnabled()) + { + handlePtr = Rubberband::_internal->_handleList.first().get(); // get pointer to first point + + if ((unsigned int)Rubberband::_internal->_handleList.size() == 2) + { + const RubberbandHandle::RubberbandCoordinate && firstPos = static_cast(handlePtr)->getCoordinate(); + + // create line from first point of polyline to new point to close the polyline + handlePtr = new RubberbandLineHandle( Rubberband::_internal->_mapView + , Rubberband::_internal->_mapScene + , scenePos + , firstPos._sceneCoordinate + , _lineHandleWidth + , _internal->_complexGeometry ); + _internal->_lineHandleList.insert( 0, SituationMap::RefPointer(static_cast(handlePtr)) ); + } + else if ((unsigned int)Rubberband::_internal->_handleList.size() > 2) + { + // update coordinates + handlePtr = _internal->_lineHandleList.first().get(); // get pointer to first line handle + static_cast(handlePtr)->updateCoordinate(0,scenePos); + } + } + + // create line from last point to new point + handlePtr = new RubberbandLineHandle( Rubberband::_internal->_mapView + , Rubberband::_internal->_mapScene + , lastPos._sceneCoordinate + , scenePos + , _lineHandleWidth + , _internal->_complexGeometry ); + _internal->_lineHandleList.append( SituationMap::RefPointer(static_cast(handlePtr)) ); + } + } + + handlePtr = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, scenePos ); + handlePtr -> setSelection(); + + Rubberband::_internal->_handleList.append( SituationMap::RefPointer(handlePtr) ); + + if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount) + { + activateButtons(); + } + + if ((!_maximumPointCount) || ((unsigned int)Rubberband::_internal->_handleList.size() < _maximumPointCount)) + { + if (!_internal->_cursorPreviewLine) _internal->_cursorPreviewLine = new DrawableLine( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene ); + if ((isClosedEnabled()) && (!_internal->_cursorPreviewCloseLine)) + { + _internal->_cursorPreviewCloseLine = new DrawableLine( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene ); + } + _internal->_cursorPreviewLine -> setVisibility( true ); + if (_internal->_cursorPreviewCloseLine) _internal -> _cursorPreviewCloseLine -> setVisibility( true ); + } + else + { + _internal->_cursorPreviewLine = nullptr; + _internal->_cursorPreviewCloseLine = nullptr; + } + return handlePtr; + } /* RubberbandMultiPointFeature::constructPoint */ + + + + bool RubberbandMultiPointFeature::checkLengthConstrains( const QPointF & scenePos, const QPointF & deltaScenePos ) + { + if (_internal->_complexGeometry) + { + markFailedConstrains( QPointF(), false ); + return true; + } + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Checking constrains for moving selected coordinates (handles) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + bool allHandlesSelected = true; + QVector coordinateVector; + + coordinateVector.reserve( Rubberband::_internal->_handleList.size() ); + + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); + if (handlePointPtr) + { + coordinateVector.append(handlePointPtr->getCoordinate()); + if (handlePointPtr->isSelected()) + { + RubberbandHandle::RubberbandCoordinate & coordinate = coordinateVector.back(); + coordinate.updateSceneCoordinate( Rubberband::_internal->_mapView , coordinate._sceneCoordinate + deltaScenePos ); + } + else allHandlesSelected = false; + } + } + + if ((allHandlesSelected) || ((_internal->_frameHandle) && (_internal->_frameHandle->isSelected()))) + { + markFailedConstrains( QPointF(), false ); + return true; + } + + if (_internal->_checkMaxAngle > 0.0) + { + // Check angle of line segments first + + for (int i = 2; i < coordinateVector.size(); ++i) + { + const RubberbandHandle::RubberbandCoordinate & p1 = coordinateVector[i - 2]; + const RubberbandHandle::RubberbandCoordinate & p2 = coordinateVector[i - 1]; + const RubberbandHandle::RubberbandCoordinate & p3 = coordinateVector[i]; + + // build tangent vectors + QPointF t1 = QPointF(p2._mapCoordinate.x() - p1._mapCoordinate.x(), p2._mapCoordinate.y() - p1._mapCoordinate.y()); + QPointF t2 = QPointF(p3._mapCoordinate.x() - p2._mapCoordinate.x(), p3._mapCoordinate.y() - p2._mapCoordinate.y()); + double l1 = sqrt(t1.x()*t1.x() + t1.y()*t1.y()); + double l2 = sqrt(t2.x()*t2.x() + t2.y()*t2.y()); + + if (qFuzzyIsNull(l1) || qFuzzyIsNull(l2)) + { + continue; + } + + t1 /= l1; + t2 /= l2; + + double phi = acos(t1.x()*t2.x() + t1.y()*t2.y()); + if (phi > _internal->_checkMaxAngle) + { + markFailedConstrains(scenePos, true); + return false; + } + } + } + + + if ((!isClosedEnabled()) && (_maximumLength > 0.0)) + { + qreal totalLength = 0.0; + const Esri::ArcGISRuntime::Point * mapPointPtr = nullptr; // Check Quartz + + for (int i = 0; i < coordinateVector.size(); ++i) + { + const RubberbandHandle::RubberbandCoordinate & coordinate = coordinateVector[i]; + if (mapPointPtr) + { + qreal x = coordinate._mapCoordinate.x() - mapPointPtr->x(); + qreal y = coordinate._mapCoordinate.y() - mapPointPtr->y(); + + totalLength += sqrt(x*x+y*y); + } + mapPointPtr = &coordinateVector.at(i)._mapCoordinate; + } + if (totalLength > _maximumLength) + { + markFailedConstrains( scenePos, true ); + return false; + } + } + + const RubberbandHandle::RubberbandCoordinate * mapPointPtr = nullptr; + bool lastSelected = false; + auto handleIter = Rubberband::_internal->_handleList.constBegin(); + + for (int i = 0; i < coordinateVector.size(); ++i) + { + const RubberbandHandle::RubberbandCoordinate & coordinate = coordinateVector[i]; + bool isSelected = (*handleIter)->isSelected(); + + if ((isSelected || lastSelected) && (mapPointPtr)) + { + if ((!isClosedEnabled()) && (_maximumSegmentLength > 0.0)) + { + qreal x = coordinate._mapCoordinate.x() - mapPointPtr->_mapCoordinate.x(); + qreal y = coordinate._mapCoordinate.y() - mapPointPtr->_mapCoordinate.y(); + + if (sqrt(x*x+y*y) > _maximumSegmentLength) + { + markFailedConstrains( scenePos, true ); + return false; + } + } + // looking for identical coordinate with last point + qreal x = coordinate._sceneCoordinate.x() - mapPointPtr->_sceneCoordinate.x(); + qreal y = coordinate._sceneCoordinate.y() - mapPointPtr->_sceneCoordinate.y(); + + if ((x*x + y*y) <= 1) + { + markFailedConstrains( scenePos, true ); + return false; + } + } + ++handleIter; + mapPointPtr = &coordinateVector.at(i); + lastSelected = isSelected; + } // for + + markFailedConstrains( QPointF(), false ); + return true; + } /* RubberbandMultiPointFeature::checkLengthConstrains */ + + + bool RubberbandMultiPointFeature::checkLengthConstrains( unsigned int checkHandleIdx, QPointF & scenePos ) + { + if (_internal->_complexGeometry) + { + markFailedConstrains( QPointF(), false ); + return true; + } + + bool markFailedConstrainsFlag = false; + + // calculate pixel size for later use + + RubberbandDrawable::RubberbandCoordinate pi1( Rubberband::_internal->_mapView,scenePos ); + RubberbandDrawable::RubberbandCoordinate pi2( Rubberband::_internal->_mapView,scenePos+QPointF(1,1) ); + + qreal piX = pi2._mapCoordinate.x() - pi1._mapCoordinate.x(); + qreal piY = pi2._mapCoordinate.y() - pi1._mapCoordinate.y(); + qreal pixelSize = sqrt(piX*piX+piY*piY); + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Checking constrains for adding single coordinate at end of polyline + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + RubberbandHandle::RubberbandCoordinate mapPos( Rubberband::_internal->_mapView, scenePos ); + + if ((!Rubberband::_internal->_handleList.empty()) && (_maximumLength > 0.0) && (!isClosedEnabled())) + { + qreal totalLength = 0.0; + RubberbandHandle::RubberbandCoordinate mapPoint; + + QList>::iterator it = Rubberband::_internal->_handleList.begin(); + + for (; it != Rubberband::_internal->_handleList.end(); ++it) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(it->get()); + if (handlePointPtr) + { + if (it != Rubberband::_internal->_handleList.begin()) + { + qreal x = handlePointPtr->getCoordinate()._mapCoordinate.x() - mapPoint._mapCoordinate.x(); + qreal y = handlePointPtr->getCoordinate()._mapCoordinate.y() - mapPoint._mapCoordinate.y(); + + totalLength += sqrt(x*x+y*y); + } + mapPoint = handlePointPtr->getCoordinate(); + } + } + if (totalLength >= _maximumLength) + { + markFailedConstrains( scenePos, true ); + return false; + } + + qreal x = mapPos._mapCoordinate.x() - mapPoint._mapCoordinate.x(); + qreal y = mapPos._mapCoordinate.y() - mapPoint._mapCoordinate.y(); + qreal l = sqrt(x*x+y*y); + + qreal d = _maximumLength - totalLength - l - pixelSize; + if (d < 0.0) + { + qreal scale = (1.0 + d/l); + const Esri::ArcGISRuntime::Point newMapCoordinate(mapPoint._mapCoordinate.x() + x * scale, + mapPoint._mapCoordinate.y() + y * scale, + mapPos._mapCoordinate.spatialReference()); + mapPos._mapCoordinate = newMapCoordinate; + markFailedConstrainsFlag = true; + scenePos = mapPos.mapToScene( Rubberband::_internal->_mapView ); + } + } + + if (checkHandleIdx <= (unsigned int)Rubberband::_internal->_handleList.size()) + { + unsigned int handleListIdx = Rubberband::_internal->_handleList.size()-checkHandleIdx; + const RubberbandPointHandle * handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList.at(handleListIdx).get()); + + if ((!isClosedEnabled()) && (_maximumSegmentLength > 0.0)) + { + qreal x = mapPos._mapCoordinate.x() - handlePointPtr->getCoordinate()._mapCoordinate.x(); + qreal y = mapPos._mapCoordinate.y() - handlePointPtr->getCoordinate()._mapCoordinate.y(); + qreal l = sqrt(x*x+y*y); + qreal d = _maximumSegmentLength - l - pixelSize; + + if (d < 0.0) + { + qreal scale = (1.0 + d/l); + const Esri::ArcGISRuntime::Point newMapCoordinate(handlePointPtr->getCoordinate()._mapCoordinate.x() + x * scale, + handlePointPtr->getCoordinate()._mapCoordinate.y() + y * scale, + mapPos._mapCoordinate.spatialReference()); + + mapPos._mapCoordinate = newMapCoordinate; + markFailedConstrainsFlag = true; + scenePos = mapPos.mapToScene( Rubberband::_internal->_mapView ); + } + } + // looking for identical coordinate with last point + qreal x = handlePointPtr->getCoordinate()._sceneCoordinate.x() - scenePos.x(); + qreal y = handlePointPtr->getCoordinate()._sceneCoordinate.y() - scenePos.y(); + + if ((x*x+y*y) <= 1) + { + markFailedConstrains( scenePos, true ); + return false; + } + } + + markFailedConstrains( scenePos, markFailedConstrainsFlag ); + + return true; + } /* RubberbandMultiPointFeature::checkLengthConstrains */ + + + bool RubberbandMultiPointFeature::selectHandles(int x, int y, const QPointF & scenePos) + { + bool eventHandled = false; + RubberbandHandle * handlePtr = pickHandle(x,y); + + _internal -> _addVertexAtEnd = false; + + if (handlePtr) + { + handlePtr->setSelection(); + enableSelectedState(); + eventHandled = true; + } + else + { + if ((_maximumPointCount < 1) || ((unsigned int)Rubberband::_internal->_handleList.size() < _maximumPointCount)) + { + unsigned int lineCounter = 0; + + for (auto lineHandle : std::as_const(_internal->_lineHandleList)) + { + if (lineHandle->isSelected()) + { + lineHandle->setSelection(false); + + handlePtr = new RubberbandLineHandle( Rubberband::_internal->_mapView + , Rubberband::_internal->_mapScene + , lineHandle->getCoordinate(0)._sceneCoordinate + , scenePos + , _lineHandleWidth + , _internal->_complexGeometry ); + + _internal->_lineHandleList.insert( lineCounter, SituationMap::RefPointer(static_cast(handlePtr)) ); + lineHandle->updateCoordinate( 0, scenePos ); + + handlePtr = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, scenePos ); + handlePtr -> setSelection(); + + unsigned int insertHandleIndex = lineCounter; + + if (!isClosedEnabled()) insertHandleIndex += 1; + Rubberband::_internal->_handleList.insert( insertHandleIndex, SituationMap::RefPointer(handlePtr) ); + + enableSelectedState(); + eventHandled = true; + break; + } + ++lineCounter; + } + } + } + + if (!isEnabledSelectedState()) + { + // ok doesn't hit any point or line handle now looking to frame handle + if (_internal->_frameHandle != nullptr) + { + if (_internal->_frameHandle->isPositionInside(scenePos)) + { + _internal->_frameHandle->setSelection(true); + enableSelectedState(); + eventHandled = true; + } + } + } + return eventHandled; + } /* RubberbandMultiPointFeature::selectHandles */ + + + void RubberbandMultiPointFeature::resetAction(AbstractEventDispatcher * causer, int x, int y ) + { + if (rubberbandIsAlive()) + { + if (isEnabledConstructionState()) + { + if (_internal->_addVertexAtEnd) + { + Rubberband::_internal->_handleList.removeLast(); + _internal->_lineHandleList.removeLast(); + } + } + } + Rubberband::resetAction(causer,x,y); + } /* RubberbandMultiPointFeature::resetAction */ + + + bool RubberbandMultiPointFeature::selectAction( AbstractEventDispatcher* , int x, int y ) + { + if (rubberbandIsAlive()) + { + QPointF scenePos(pixel2ScenePosition(x,y)); + + if (_baseInternal->buttonProxyActive(scenePos)) return false; + + if (isEnabledConstructionState()) disenableMultiSelectedState(); + + _baseInternal->_lastClickPosition = scenePos; + + bool eventHandled = selectHandles( x, y, scenePos ); + + if ((!eventHandled) && (isEnabledConstructionState())) + { + _internal -> _addVertexAtEnd = true; + _internal -> _showCursorPreviewLine = true; + + deSelectHandles(); + + if (checkLengthConstrains(1,scenePos)) + { + constructPoint( scenePos ); + enableSelectedState(); + _baseInternal->_lastClickPosition = scenePos; + } + + eventHandled = true; + } + else _internal->_addVertexAtEnd = false; + + if (eventHandled) + { + update(); + deactivateButtons(); + } + + return eventHandled; + } + return false; + } /* RubberbandMultiPointFeature::selectAction */ + + + bool RubberbandMultiPointFeature::setCoordinateAction( AbstractEventDispatcher * causer, int x, int y ) + { + markFailedConstrains( QPointF(), false ); + + if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) + { + bool eventHandled = false; + QPointF scenePos(pixel2ScenePosition(x,y)); + + Rubberband::_internal->_lastClickPosition = scenePos; + + if (_internal->_addVertexAtEnd) // isEnabledConstructionState()) + { + if (Rubberband::_internal->_handleList.isEmpty()) + { + disenableSelectedState(); + disenableMultiSelectedState(); + return false; + } + + if ((!isEnabledSelectedState()) && (_baseInternal->buttonProxyActive(scenePos))) + { + deSelectHandles(); + return false; + } + + if (checkLengthConstrains(2, scenePos)) + { + QPointF deltaMove = scenePos - _baseInternal->_lastClickPosition; + + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); + if ((handlePointPtr) && ((handlePointPtr->isSelected()))) + { + handlePointPtr->setSelection(false); + handlePointPtr->moveCoordinate( deltaMove ); + eventHandled = true; + } + } + } + markFailedConstrains( QPointF(), false ); + + if ((_maximumPointCount >0) && ((unsigned int)Rubberband::_internal->_handleList.size() >= _maximumPointCount)) + { + completeStateAction(causer,x,y); + } + if (eventHandled) update(); + if (_baseInternal->buttonProxyActive(scenePos)) return false; + updateButtonPosition(); + activateButtons(); + } + else + { + if (isEnabledSelectedState()) + { + bool frameIsSelected( ((_internal->_frameHandle != nullptr) && (_internal->_frameHandle->isSelected())) ); + + QPointF deltaMove = scenePos - _baseInternal->_lastClickPosition; + + if (!checkLengthConstrains(scenePos, deltaMove)) + { + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); + if (handlePointPtr) handlePointPtr->setSelection(false); + } + if (_internal->_frameHandle) _internal->_frameHandle->setSelection(false); + updateButtonPosition(); + activateButtons(); + disenableSelectedState(); + disenableMultiSelectedState(); + markFailedConstrains( QPointF(), false ); + return true; + } + markFailedConstrains( QPointF(), false ); + + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); + if ((handlePointPtr) && ((handlePointPtr->isSelected()) || (frameIsSelected))) + { + handlePointPtr->setSelection(false); + handlePointPtr->moveCoordinate( deltaMove ); + eventHandled = true; + } + } + if (frameIsSelected) + { + _internal->_frameHandle->setSelection(false); + eventHandled = true; + } + + disenableSelectedState(); + disenableMultiSelectedState(); + } + + if (isEnabledConstructionState()) + { + _internal->_addVertexAtEnd = true; + _internal->_showCursorPreviewLine = true; + + updatePreviewLine( scenePos ); + } + else if (_baseInternal->buttonProxyActive(scenePos)) return false; + + if (eventHandled) update(); + updateButtonPosition(); + activateButtons(); + + } + return eventHandled; + } + return false; + } /* RubberbandMultiPointFeature::setCoordinateAction */ + + + void RubberbandMultiPointFeature::updatePreviewLine( const QPointF & scenePosition ) + { + if (isEnabledConstructionState() && (!Rubberband::_internal->_handleList.isEmpty()) && (_internal->_cursorPreviewLine)) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList.last().get()); + if ((handlePointPtr) && (_internal->_addVertexAtEnd)) + { + RubberbandDrawable::RubberbandCoordinate rbCoordinate( Rubberband::_internal->_mapView, scenePosition ); + + _internal->_cursorPreviewLine -> setVisibility( _internal->_showCursorPreviewLine ); + _internal->_cursorPreviewLine -> setDrawColor( _handleSelectionColor ); + + _internal->_cursorPreviewLine->setLine( handlePointPtr->getCoordinate(), rbCoordinate ); + + if (_internal->_cursorPreviewCloseLine) + { + _internal->_cursorPreviewCloseLine -> setVisibility( _internal->_showCursorPreviewLine ); + _internal->_cursorPreviewCloseLine -> setDrawColor( _handleSelectionColor ); + + handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList.front().get()); + + if (handlePointPtr) + { + _internal->_cursorPreviewCloseLine->setLine( handlePointPtr->getCoordinate(), rbCoordinate ); + } + } + } + else + { + _internal->_cursorPreviewLine -> setVisibility( false ); + if (_internal->_cursorPreviewCloseLine) _internal->_cursorPreviewCloseLine -> setVisibility( false ); + } + return; + } + + _internal->_cursorPreviewLine = nullptr; + _internal->_cursorPreviewCloseLine = nullptr; + + } /* RubberbandMultiPointFeature::updatePreviewLine */ + + + bool RubberbandMultiPointFeature::moveAction( AbstractEventDispatcher*, int x, int y ) + { + adjustConnectedPreviewPixmap(x,y); + + if (rubberbandIsAlive()) + { + QPointF scenePosition(pixel2ScenePosition(x,y)); + + if (_baseInternal->buttonProxyActive(scenePosition)) return false; + if (Rubberband::_internal->_mapView == nullptr) return false; + + if (!isEnabledMultiSelectedState()) deSelectHandles(); + + RubberbandHandle * handlePtr = pickHandle(x,y); + if (handlePtr == nullptr) + { + _internal->_showCursorPreviewLine = isEnabledConstructionState(); + for (auto handle : std::as_const(_internal->_lineHandleList)) + { + handle->setSelection(false); + if (handle->isPositionInside(scenePosition)) + handle->setSelection(); + } + } + else + { + _internal->_showCursorPreviewLine = false; + handlePtr->setSelection(); + } + + if (isEnabledConstructionState()) + { + + if (checkLengthConstrains(1,scenePosition)) + { + updatePreviewLine( scenePosition ); + + if (isSignalConnected(QMetaMethod::fromSignal(&RubberbandMultiPointFeature::currentCursorPositionChanged))) + { + RubberbandDrawable::RubberbandCoordinate mapCoord(Rubberband::_internal->_mapView, scenePosition); + emit currentCursorPositionChanged( Point3D(mapCoord._mapCoordinate.x(),mapCoord._mapCoordinate.y(),0.0) ); + } + } + } + + return false; + } + return false; + } /* RubberbandMultiPointFeature::moveAction */ + + + bool RubberbandMultiPointFeature::movePickedAction( AbstractEventDispatcher*, int x, int y ) + { + adjustConnectedPreviewPixmap(x,y); + + if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) + { + if (isEnabledSelectedState()) + { + QPointF scenePos( pixel2ScenePosition(x,y) ); + QPointF deltaMove( scenePos-_baseInternal->_lastClickPosition ); + + if (checkLengthConstrains(scenePos, deltaMove)) + { + bool frameIsSelected( ((_internal->_frameHandle != nullptr) && (_internal->_frameHandle->isSelected())) ); + + _baseInternal->_lastClickPosition = scenePos; + + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); + if ((handlePointPtr) && ((handlePointPtr->isSelected()) || (frameIsSelected))) + { + handlePointPtr->moveCoordinate( deltaMove ); + } + } + updatePreviewLine(scenePos); + update(); + } + return true; + } + } + return false; + } /* RubberbandMultiPointFeature::movePickedAction */ + + + bool RubberbandMultiPointFeature::deleteAction(AbstractEventDispatcher*, int x, int y) + { + if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) + { + if (((unsigned int)Rubberband::_internal->_handleList.size() > _minimumPointCount) && (isEnabledSelectedState())) + { + QList> savedHandleList = Rubberband::_internal->_handleList; + QList> savedLineHandleList = _internal->_lineHandleList; + + QPointF scenePosition = pixel2ScenePosition(x,y); + QList>::iterator handleIter = Rubberband::_internal->_handleList.begin(); + QList>::iterator lineHandleIter = _internal->_lineHandleList.begin(); + + while (handleIter != Rubberband::_internal->_handleList.end()) + { + if (handleIter->get()->isPositionInside(scenePosition)) + { + handleIter = Rubberband::_internal->_handleList.erase(handleIter); + lineHandleIter = _internal->_lineHandleList.erase(lineHandleIter); + if (lineHandleIter != _internal->_lineHandleList.end()) + { + if (lineHandleIter != _internal->_lineHandleList.begin()) + { + lineHandleIter->get()->updateCoordinate( 0, (lineHandleIter-1)->get()->getCoordinate(1)._sceneCoordinate ); + } + } + continue; + } + ++handleIter; + if ((lineHandleIter != _internal->_lineHandleList.end()) + && (lineHandleIter+1 != _internal->_lineHandleList.end())) + { + ++lineHandleIter; + } + } /* while handleIter */ + + if (!checkLengthConstrains(scenePosition, QPointF(0,0))) + { + // constrain failed, do not delete yet + Rubberband::_internal->_handleList = savedHandleList; + _internal->_lineHandleList = savedLineHandleList; + } + update(); + activateButtons(); + + disenableSelectedState(); + disenableMultiSelectedState(); + } + return true; + } + return false; + } /* RubberbandMultiPointFeature::deleteAction */ + + + bool RubberbandMultiPointFeature::contextMenuAction( AbstractEventDispatcher*, int,int ) + { + return false; + } /* RubberbandMultiPointFeature::contextMenuAction */ + + + bool RubberbandMultiPointFeature::completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) + { + markFailedConstrains( QPointF(), false ); + + if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) + { + if (isEnabledConstructionState()) + { + if (!_finishAfterConstruction) + { + if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount) + { + enableModificationState(); + _internal->_cursorPreviewLine = nullptr; // remove preview lines + _internal->_cursorPreviewCloseLine = nullptr; + } + update(); + return true; + } + } + + if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount) + { + return applyAndEndAction(causer,x,y); + } + } + return false; + } /* RubberbandMultiPointFeature::completeStateAction */ + + + bool RubberbandMultiPointFeature::undoAction( AbstractEventDispatcher * /*causer*/, int,int ) + { + if (rubberbandIsAlive()) + { + if (!Rubberband::_internal->_handleList.empty()) + { + if (isEnabledConstructionState()) + { + Rubberband::_internal->_handleList.pop_back(); + if (!_internal->_lineHandleList.empty()) _internal->_lineHandleList.pop_back(); + + if (_internal->_cursorPreviewLine) _internal->_cursorPreviewLine -> setVisibility( false ); + if (_internal -> _cursorPreviewCloseLine) _internal -> _cursorPreviewCloseLine -> setVisibility( false ); + + update(); + activateButtons(); + } + } + return true; + } + return false; + } /* RubberbandMultiPointFeature::undoAction */ + + + bool RubberbandMultiPointFeature::applyAndEndAction( AbstractEventDispatcher * causer, int x, int y) + { + if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount) Rubberband::applyAndEndAction( causer,x,y ); + else abortAction(causer,x,y); + return true; + } /* RubberbandMultiPointFeature::applyAndEndAction */ + + + void RubberbandMultiPointFeature::activateButtons() + { + _baseInternal->_hasUndoButton = isEnabledConstructionState(); + _baseInternal->_applyIsPossible = ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount); + _baseInternal->_undoIsPossible = (isEnabledConstructionState() && ((unsigned int)Rubberband::_internal->_handleList.size() > _minimumPointCount)); + + Rubberband::activateButtons(); + + } /* RubberbandMultiPointFeature::activateButtons */ + + + void RubberbandMultiPointFeature::update() + { + Rubberband::update(); + + DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION; + + auto handleLine = _internal->_lineHandleList.begin(); + bool closePolygon = ((unsigned int)Rubberband::_internal->_handleList.size() > 2) && (isClosedEnabled()); + bool firstLoop = !closePolygon; + + QColor lineHandleDrawColor = _drawColor; + QColor lineHandleFillColor = _drawColor; + + if (_internal->_complexGeometry) + { + lineHandleDrawColor.setAlphaF( lineHandleDrawColor.alphaF()*0.5 ); + lineHandleFillColor.setAlphaF( lineHandleFillColor.alphaF()*0.5 ); + } + else + { + if (!_previewSidc.isEmpty()) + { + lineHandleDrawColor.setAlphaF( 0.0 ); + lineHandleFillColor.setAlphaF( 0.0 ); + } + } + + QVector coordinateList; + coordinateList.reserve( Rubberband::_internal->_handleList.size() ); + + for (auto handlePoint : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(handlePoint.get()); + if (handlePointPtr) + { + if (handleLine != _internal->_lineHandleList.end()) + { + RubberbandHandle::RubberbandCoordinate && pos = handlePointPtr->getCoordinate(); + RubberbandLineHandle * handleLinePtr = dynamic_cast(handleLine->get()); + + if (!firstLoop) + { + if (handleLinePtr) + { + handleLinePtr -> updateCoordinate( 1, pos._sceneCoordinate ); + + handleLinePtr -> setDrawColor(lineHandleDrawColor); + handleLinePtr -> setFillColor(lineHandleFillColor); + handleLinePtr -> setSelectionColor(_handleSelectionColor); + handleLinePtr -> setPickingRatio( _pickingRatio ); + } + ++handleLine; + if (handleLine != _internal->_lineHandleList.end()) + { + handleLinePtr = dynamic_cast(handleLine->get()); + } + else handleLinePtr = nullptr; + } + else firstLoop = false; + + if (handleLinePtr) handleLinePtr->updateCoordinate( 0, pos._sceneCoordinate ); + } + + coordinateList.append( handlePointPtr->getCoordinate() ); + } + + } /* for handlePoint */ + + + // remove superfluous line handles + while (handleLine != _internal->_lineHandleList.end()) + { + handleLine = _internal->_lineHandleList.erase( handleLine ); + } + + + if (closePolygon) + { + SituationMap::RefPointer & handleLinePtr = _internal->_lineHandleList.first(); + SituationMap::RefPointer & handlePointPtr = Rubberband::_internal->_handleList.last(); + RubberbandHandle::RubberbandCoordinate && pos = dynamic_cast(handlePointPtr.get())->getCoordinate(); + + handleLinePtr->updateCoordinate( 0, pos._sceneCoordinate ); + + if (_fillColor.alpha()) + { + if (!_internal->_fillPath) + { + _internal->_fillPath = new DrawablePainterPath( Rubberband::_internal->_mapView ); + _internal->_fillPath -> setDrawColor( QColor() ); + } + _internal -> _fillPath -> setVisibility( true ); + _internal -> _fillPath -> setFillColor(_fillColor ); + _internal -> _fillPath -> setPath( coordinateList ); + } + else _internal->_fillPath = nullptr; + } + else if (_internal->_fillPath) _internal->_fillPath->setVisibility( false ); + + if ((isEnabledModificationState()) && (coordinateList.size() >= 2) && (_internal->_frameHandle == nullptr)) + { + _internal->_frameHandle = SituationMap::RefPointer( new RubberbandFrameHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, _frameHandleMargin )); + } + + + if (_internal->_frameHandle != nullptr) + { + if (coordinateList.size() >= 2) + { + _internal->_frameHandle -> setDrawColor(_frameColor); + _internal->_frameHandle -> setFillColor(_frameFillColor); + _internal->_frameHandle -> setSelectionColor(_frameSelectionColor); + _internal->_frameHandle -> updateFrameSceneMargin( _frameHandleMargin ); + _internal->_frameHandle -> updateFrameShape( coordinateList ); + _internal->_frameHandle -> drawDisabled( (_frameHandleMargin < 0) ); + } + else _internal->_frameHandle = nullptr; + } + + if (!_previewSidc.isEmpty()) + { + if (!_internal->_previewSymbol) + { + _internal->_previewSymbol = new TransientTacticalSymbol( static_cast(_baseInternal->_map.data()), _previewSidc ); + } + _internal->_previewSymbol->update( coordinateList ); + } + else _internal->_previewSymbol = nullptr; + + } /* RubberbandMultiPointFeature::update */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_point_feature.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_point_feature.h new file mode 100755 index 000000000..9d23ecf98 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_point_feature.h @@ -0,0 +1,209 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define multi point feature geoemtry +** $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 .........: +** +** 10.04.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband.h" + + +namespace SituationMap { namespace RB +{ + class RubberbandHandle; + class RubberbandFrameHandle; + + /** + @class RubberbandMultiPointFeature rubberband_multi_point_feature.h + @brief Implementation of a rubberband to input multi point features, like areals or linears + @see Rubberband + */ + class SITUATIONMAP_EXPORT RubberbandMultiPointFeature : public Rubberband + { + Q_OBJECT + public: + + /** + @name Construction & Destruction + @{ + */ + + private: + + friend class RubberbandFactory; + friend class RubberbandArrow; + friend class RubberbandCorridor; + friend class RubberbandPolylineWidth; + friend class RubberbandSmoothLine; + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Constructor delegation doesn't work with Visual Studio 2013 + + explicit RubberbandMultiPointFeature( SituationMap::Map * map ); + explicit RubberbandMultiPointFeature( SituationMap::Map * map, bool complexGeometry ); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + /** + Construction of RubberbandMultiPointFeature in most common cases. The parameter parent is + optional. + */ + explicit RubberbandMultiPointFeature( SituationMap::Map * map, QObject * parent = nullptr ); + + /** + Construction of RubberbandMultiPointFeature in case of controlling complex geometry objects, like + splines or multi point symbols. In this case set complexGeometry true. + */ + explicit RubberbandMultiPointFeature( SituationMap::Map * map, bool complexGeometry, QObject * parent = nullptr ); + + public: + + ~RubberbandMultiPointFeature(); + /** @} */ + + bool selectAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool setCoordinateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool moveAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool movePickedAction ( AbstractEventDispatcher * causer, int x, int y ) override; + void resetAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool contextMenuAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool deleteAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool undoAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool applyAndEndAction ( AbstractEventDispatcher * causer, int x, int y ) override; + + + /** + If set to true a polygon the rubberband draws a polygon otherwise a polyline. + */ + inline void enableClosed( bool flag ) + { + _isClosed = flag; + if (( _isClosed) && (_minimumPointCount < 3)) _minimumPointCount = 3; + if ((!_isClosed) && (_minimumPointCount < 2)) _minimumPointCount = 2; + } /* enableClosed */ + + /** + Gets the value of the close flag + */ + inline bool isClosedEnabled() const { return _isClosed; } + + /** + @name Setting Constrains + @{ + */ + + /** + Defines minimum count of verticies that has to be defined by users input to complete the rubberband with apply signal. The default value is 0. + */ + inline void minimumPointCount( unsigned int x ) + { + if (( _isClosed) && (x < 3)) x = 3; + if ((!_isClosed) && (x < 2)) x = 2; + _minimumPointCount = x; + } /* minimumPointCount */ + + + /** + Defines maximum count of verticies that can be aceppted by the rubberband. A values of 0 stands for undefined. The default is 0. + */ + inline void maximumPointCount( unsigned int x ) + { + if (x > 0) + { + if (( _isClosed) && (x < 3)) x = 3; + if ((!_isClosed) && (x < 2)) x = 2; + } + _maximumPointCount = x; + } /* maximumPointCount */ + + + /** + Defines the maximum length of a segment (distance between to adjaced verticies). A value of 0 stands for no limit is set. The default is 0. + */ + inline void maximumSegmentLength( qreal x ) { _maximumSegmentLength = (x > 0) ? x : 0.0; } + inline qreal getMaximumSegmentLength() const{ return _maximumSegmentLength; } + + /** + Defines the maximum length of the complete polyline. \c enableClosed must be set to false. A value of 0 stands for no limit is set. The default is 0. + */ + inline void maximumLength( qreal x ) { _maximumLength = (x > 0) ? x : 0.0; } + /** @} */ + inline qreal getMaximumLength() const { return _maximumLength; } + + + virtual QVector coordinateVector( CoordinateSystem cs = CoordinateSystem::Geo ) const override; + virtual QPointF singleCoordinate( unsigned int idx = 0, CoordinateSystem cs = CoordinateSystem::Geo ) const override; + + virtual void setCoordinateVector( const QVector &, CoordinateSystem cs = CoordinateSystem::Geo ) override; + + signals: + + /** + The signal will be emitted, when the mouse cursor has changed and the rubberband is in construction mode and is accepting new input coordinates. + */ + void currentCursorPositionChanged(const Point3D &mapCoord); + + + protected: + + /* + Forward declaration of InternalData to hold private member variables. Becareful the base class Rubberband + has similary a class for internal data member with name "InternalData". Don't be confused it is not the same. + */ + struct InternalData; + + InternalData * _internal; + + bool _isClosed; + + unsigned int _minimumPointCount; + unsigned int _maximumPointCount; + + qreal _maximumSegmentLength; + qreal _maximumLength; + + void activateButtons() override; + + void update() override; + + // some helper methodes + RubberbandHandle * constructPoint( QPointF scenePos ); + + void constructPoint(const QPointF &point, CoordinateSystem cs ); + + bool selectHandles( int x, int y, const QPointF & scenePos ); + + bool checkLengthConstrains( unsigned int checkHandleIdx, QPointF & scenePos ); + bool checkLengthConstrains( const QPointF & scenePos, const QPointF & deltaScenePos ); + void updatePreviewLine( const QPointF & scenePosition ); + + RubberbandFrameHandle * getFrameHandle() const; + + void setCheckMaxAngle( double phi ); + + }; /* RubberbandMultiPointFeature */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_point_feature_3d.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_point_feature_3d.cpp new file mode 100755 index 000000000..cd86bc68a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_point_feature_3d.cpp @@ -0,0 +1,660 @@ +/** +** +** @RDE_HEADER@ +** Implementation of rubberband class to define point feature geometry in 3d +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include + +#include "rubberband_multi_point_feature_3d.h" +#include "rubberband_base_internal.h" + +#include "event_mouse_navigator.h" + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +namespace SituationMap { namespace RB +{ + + class RubberbandMultiPointFeature3D::InternalData + { + public: + + QVector m_marker; + GraphicalObjectPath *m_path; + + QVector m_coordinates; + + int m_currentIndex; + int m_selectionIndex; + QFuture m_pickingTask; + QFuture m_selectionTask; + QPoint m_pickingPoint; + QPointF * m_startMove; + ScreenToLocationHelper * m_screenToMap; + QPointer m_toElevation; + PositionCont m_positionList3D; + bool m_doCompleteAction; + + + InternalData() + : m_marker() + , m_path(nullptr) + , m_coordinates() + , m_currentIndex(-1) + , m_selectionIndex(-1) + , m_pickingTask() + , m_selectionTask() + , m_pickingPoint() + , m_startMove(nullptr) + , m_screenToMap() + , m_toElevation(nullptr) + , m_positionList3D() + , m_doCompleteAction(false) + { + m_marker.reserve(100); + m_coordinates.reserve(100); + } // InternalData() + + ~InternalData() + { + delete m_startMove; + for (auto marker : std::as_const(m_marker)) + delete marker; + delete m_path; + delete m_screenToMap; + } // ~InternalData() + + }; // RubberbandPointFeature3D::InternalData + + + RubberbandMultiPointFeature3D::RubberbandMultiPointFeature3D( SituationMap::Map * map, QObject * parent, bool isActive ) + : Rubberband3D(map,parent,isActive) + , _internal( new RubberbandMultiPointFeature3D::InternalData() ) + , _isClosed(false) + , _isCircular(false) + { + MapImpl *mapImpl = static_cast(_baseInternal->_map.data()); + Esri::ArcGISRuntime::SceneGraphicsView *view = mapImpl->getEsriSceneGraphicsView(); + + connect( view, &Esri::ArcGISRuntime::SceneGraphicsView::identifyGraphicsOverlaysCompleted, this, &RubberbandMultiPointFeature3D::onGetPickingResult ); + _internal->m_screenToMap = new ScreenToLocationHelper(view); + connect( _internal->m_screenToMap, &ScreenToLocationHelper::mouseScenePositionChanged, this, &RubberbandMultiPointFeature3D::onMapPosition ); // ToDO + + } // RubberbandMultiPointFeature3D::RubberbandMultiPointFeature3D( SituationMap::Map * map, QObject * parent = nullptr, bool isActive ) + + + RubberbandMultiPointFeature3D::~RubberbandMultiPointFeature3D() + { + delete _internal; + _internal = nullptr; + } // RubberbandMultiPointFeature3D::~RubberbandMultiPointFeature3D() + + void RubberbandMultiPointFeature3D::activateButtons() + { + _baseInternal->_hasUndoButton = false; + _baseInternal->_undoIsPossible = false; + _baseInternal->_applyIsPossible = (_internal->m_coordinates.size() > ((_isClosed && !_isCircular) ? 2 : 1)); + + RubberbandBase::activateButtons(); + + } /* RubberbandMultiPointFeature3D::activateButtons */ + + + void circleShape( GraphicalObjectPath *pathObj, const PositionCont &positionList ) + { + if (positionList.size() < 2) return; + + PositionCont circleShape; + + double cx = positionList[0].x(); + double cy = positionList[0].y(); + double rx = positionList[1].x()-cx; + double ry = positionList[1].y()-cy; + + double corrLon = cos(cy / 57.2957795147); // correct factor from nautic simulation to get a real circle + double dPhi = MathConst::pi / 50; // delta angle to rasterize the circle to a polyline + + auto sqr = [](double x) -> double { return x * x; }; + double r = sqrt(sqr(rx*corrLon) + sqr(ry) ); + + double phi = 0.0; + while (phi < MathConst::pi_two) + { + double sinPhi = sin(phi); + double cosPhi = cos(phi); + double px = cx + r * cosPhi / corrLon; + double py = cy + r*sinPhi; + + circleShape.append(Point3D(px, py, 0.0)); + phi += dPhi; + } + + pathObj->setPositions(circleShape); + + } // circleShape + + + void RubberbandMultiPointFeature3D::onMapPosition(const Point3D &mapCoordinate) + { + if (_internal->m_currentIndex < 0) return; + + if (qIsNaN(mapCoordinate.x())) return; // illegal map position + if (qIsNaN(mapCoordinate.y())) return; + + if ((!_internal->m_toElevation) && (RubberbandBase::_baseInternal->_map->layerFactory().elevationAvailable())) + { + _internal->m_toElevation = RubberbandBase::_baseInternal->_map->layerFactory().locationToElevation(); // layerFactory has ownership + } + + if ((std::numeric_limits::max()) == _internal->m_currentIndex) + { + // move all coordinates + if (_internal->m_startMove) + { + QPointF deltaMove = mapCoordinate.toPointF() - *(_internal->m_startMove); + + _internal->m_positionList3D.clear(); + _internal->m_positionList3D.reserve(_internal->m_coordinates.size()); + + for (int i = 0; i < _internal->m_coordinates.size(); ++i) + { + const QPointF & pos = _internal->m_coordinates[i] += deltaMove; + _internal->m_marker[i]->setOrigin( pos ); + + if (_internal->m_toElevation) + { + QMetaObject::Connection conn; + + conn = connect(_internal->m_toElevation.data(), &SituationMap::LocationToElevationHelper::elevationCompleted, this, [this,pos,conn](double elevation) + { + disconnect(conn); + _internal->m_positionList3D << Point3D(pos.x(),pos.y(),elevation); + if (_internal->m_positionList3D.size() == _internal->m_coordinates.size()) + { + if (_internal->m_path) _internal->m_path->setPositions( _internal->m_positionList3D ); + update(); + } + + // qCritical() << "elevation at (" << pos.x() << " , " << pos.y() << ") : " << elevation; + + }); + _internal->m_toElevation->locationToElevation(pos); + } + else + { + _internal->m_positionList3D << Point3D::fromQPointF(pos); + } + } + + if (_internal->m_toElevation) + { + if (_internal->m_path) _internal->m_path->setPositions( _internal->m_positionList3D ); + update(); + } + } + else _internal->m_startMove = new QPointF(mapCoordinate.toPointF()); + return; + } + + if (_internal->m_currentIndex >= _internal->m_coordinates.size()) return; + + MapImpl *mapImpl = static_cast(_baseInternal->_map.data()); + Esri::ArcGISRuntime::SceneGraphicsView *view = mapImpl->getEsriSceneGraphicsView(); + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = mapImpl->getGraphicsLayer(mapImpl->layerFactory().rubberbandLayer()); + + if (nullptr == view) return; + + for (int idx = _internal->m_positionList3D.size(); idx <= _internal->m_currentIndex; ++idx) + { + _internal->m_positionList3D << mapCoordinate; + } + _internal->m_coordinates[_internal->m_currentIndex] = mapCoordinate.toPointF(); + _internal->m_positionList3D[_internal->m_currentIndex] = mapCoordinate; + + // qCritical() << "elevation idx=" << _internal->m_currentIndex <<" at (" << mapCoordinate.x() << " , " << mapCoordinate.y() << ") : " << mapCoordinate.z(); + + GraphicalObjectPictureMarker *&marker = _internal->m_marker[_internal->m_currentIndex]; + + if (nullptr == marker) + { + marker = new GraphicalObjectPictureMarker( view, graphicsLayer, QStringLiteral("RubberbandMultiPointFeature3D"), INVALID_MAP_ID ); + + marker->setImage(markerImage()); + marker->setOrigin( mapCoordinate ); + marker->updateView(); + marker->setPickable(true); + + Esri::ArcGISRuntime::Graphic *graphic = marker->graphic(); + if (graphic) + { + Esri::ArcGISRuntime::AttributeListModel *attLisModel = graphic->attributes(); + if (attLisModel) + { + // qCritical() << "Rubberband3D-Item =" << _internal->m_currentIndex; + attLisModel->insertAttribute(QStringLiteral("Rubberband3D-Item"),_internal->m_currentIndex); + } + } + } + else marker->setOrigin( mapCoordinate ); + + if (_internal->m_coordinates.size() > ((_isClosed && !_isCircular) ? 2 : 1)) + { + + if (nullptr == _internal->m_path) + { + bool isPolygon = (_isClosed || _isCircular); + + _internal->m_path = new GraphicalObjectPath(isPolygon ? GraphicalObjectPathType::Polygon : GraphicalObjectPathType::Polyline, view, graphicsLayer, QStringLiteral("RubberbandMultiPointFeature3D"), INVALID_MAP_ID); + + _internal->m_path->rLineStyle().setLineColor(_handleDrawColor); + _internal->m_path->rLineStyle().setLinePattern(LinePattern::Solid); + _internal->m_path->rLineStyle().setLineWidth(3); + _internal->m_path->setPickable(false); + if (isPolygon) + { + _internal->m_path->rFillStyle().setFillColor(_frameFillColor); + } + + if (_isCircular) + { + circleShape(_internal->m_path, _internal->m_positionList3D); + } + else _internal->m_path->setPositions(_internal->m_positionList3D); + + _internal->m_path->updateView(); + } + else + { + if (_isCircular) + { + circleShape(_internal->m_path, _internal->m_positionList3D); + } + else _internal->m_path->setPositions(_internal->m_positionList3D); + } + } + + if (_isCircular && (0 == _internal->m_currentIndex) && (1 == _internal->m_coordinates.size())) + { + _internal->m_coordinates << _internal->m_coordinates.first(); + _internal->m_positionList3D << _internal->m_positionList3D.first(); + _internal->m_marker << nullptr; + _internal->m_currentIndex++; + + // qCritical() << QCursor::pos() << " != " << view->viewport()->mapToGlobal(_internal->m_pickingPoint); + + _internal->m_doCompleteAction = (QCursor::pos() != view->viewport()->mapToGlobal(_internal->m_pickingPoint)); + + if (!_internal->m_doCompleteAction) + { + // Click with out moving the mouse, set a default size of the circle and let the user do some editing + // The rubberband doesn't complete automatically in this case. + _internal->m_screenToMap->screenToLocation(_internal->m_pickingPoint.x()+100, _internal->m_pickingPoint.y()); + } + } + + update(); + + if (!QGuiApplication::mouseButtons().testFlag(Qt::MouseButton::LeftButton)) + { + if (_isCircular && _internal->m_doCompleteAction && (1 == _internal->m_currentIndex)) + { + completeStateAction(nullptr, 0, 0); + } + else + { + updateButtonPosition(); + activateButtons(); + } + } + + } // void RubberbandMultiPointFeature3D::onMapPosition(const QPointF &mapCoordinate) + + + void RubberbandMultiPointFeature3D::onGetPickingResult(const QUuid &taskId, const QList &identifyResults) + { + if (_internal->m_pickingTask.taskId() == taskId) + { + for (auto iResult : identifyResults) + { + const QList &gResults = iResult->graphics(); + for (auto pickedGraphic : gResults) + { + if (pickedGraphic->attributes()) + { + bool ok = false; + int pickedIndex = pickedGraphic->attributes()->attributeValue(QStringLiteral("Rubberband3D-Item")).toInt(&ok); + + if (ok) + { + qCritical() << "Picking index " << pickedIndex; + _internal->m_currentIndex = pickedIndex; + _internal->m_screenToMap->screenToLocation(_internal->m_pickingPoint.x(), _internal->m_pickingPoint.y()); + + if (pickedIndex < _internal->m_marker.size()) + { + GraphicalObjectPictureMarker *&marker = _internal->m_marker[pickedIndex]; + + if (marker) + { + marker->setImage(markerSelectionImage()); + marker->updateView(); + } + } + return; + } + } + + } // foreach pickedGraphic + } // foreach iResult + + if ((!_isCircular) || (_internal->m_coordinates.size() < 2)) + { + // add new coordinate and calculate map location + _internal->m_currentIndex = _internal->m_marker.size(); + _internal->m_marker << nullptr; + _internal->m_coordinates << QPointF(); + + _internal->m_screenToMap->screenToLocation(_internal->m_pickingPoint.x(), _internal->m_pickingPoint.y()); + } + } + + if (_internal->m_selectionTask.taskId() == taskId) + { + for (auto iResult : identifyResults) + { + const QList &gResults = iResult->graphics(); + for (auto pickedGraphic : gResults) + { + if (pickedGraphic->attributes()) + { + bool ok = false; + int pickedIndex = pickedGraphic->attributes()->attributeValue(QStringLiteral("Rubberband3D-Item")).toInt(&ok); + + if ((ok) && (pickedIndex < _internal->m_marker.size())) + { + GraphicalObjectPictureMarker *&marker = _internal->m_marker[pickedIndex]; + + if (marker) + { + _internal->m_selectionIndex = pickedIndex; + marker->setImage(markerSelectionImage()); + marker->updateView(); + return; + } + } + } + } + } + } + if ((_internal->m_selectionIndex >= 0) && (_internal->m_selectionIndex < _internal->m_marker.size())) + { + GraphicalObjectPictureMarker *&marker = _internal->m_marker[_internal->m_selectionIndex]; + + if (marker) + { + marker->setImage(markerImage()); + marker->updateView(); + } + _internal->m_selectionIndex = -1; + } + } // void RubberbandMultiPointFeature3D::onGetPickingResult(const QUuid &taskId, const QList &identifyResults) + + + bool RubberbandMultiPointFeature3D::selectAction(AbstractEventDispatcher *, int x, int y) + { + delete _internal->m_startMove; + _internal->m_startMove = nullptr; + + if (rubberbandIsAlive()) + { + if (_baseInternal->buttonProxyActive(QPointF(x,y))) return false; + + MapImpl *mapImpl = static_cast(_baseInternal->_map.data()); + + _internal->m_pickingTask.cancel(); // cancel old task + _internal->m_pickingPoint = QPoint(x,y); + _internal->m_pickingTask = mapImpl->getEsriGeoView()->identifyGraphicsOverlays(x,y,EventMouseNavigator::pickTolerance(),false,std::numeric_limits::max()); + _internal->m_currentIndex = -1; + + return true; + } + return false; + } // bool RubberbandMultiPointFeature3D::selectAction(AbstractEventDispatcher *, int x, int y) + + + bool RubberbandMultiPointFeature3D::setCoordinateAction(AbstractEventDispatcher *, int x, int y) + { + delete _internal->m_startMove; + _internal->m_startMove = nullptr; + + if (rubberbandIsAlive()) + { + _baseInternal->_lastClickPosition = QPointF(x,y); + + if (_baseInternal->buttonProxyActive(_baseInternal->_lastClickPosition)) return false; + + _internal->m_screenToMap->screenToLocation(x,y); + + if ((_internal->m_currentIndex >= 0) && (_internal->m_currentIndex < _internal->m_marker.size())) + { + GraphicalObjectPictureMarker *&marker = _internal->m_marker[_internal->m_currentIndex]; + + if (marker) + { + marker->setImage(markerImage()); + marker->updateView(); + } + } + + return true; + } + return false; + } // bool RubberbandMultiPointFeature3D::setCoordinateAction(AbstractEventDispatcher *, int x, int y) + + + bool RubberbandMultiPointFeature3D::movePickedAction(AbstractEventDispatcher *, int x, int y) + { + if (rubberbandIsAlive()) + { + _internal->m_screenToMap->screenToLocation(x,y); + return true; + } + return false; + } // bool RubberbandMultiPointFeature3D::movePickedAction(AbstractEventDispatcher *, int x, int y) + + + bool RubberbandMultiPointFeature3D::moveAction( AbstractEventDispatcher*, int x, int y ) + { + if (rubberbandIsAlive()) + { + if (_baseInternal->buttonProxyActive(QPointF(x,y))) return false; + + MapImpl *mapImpl = static_cast(_baseInternal->_map.data()); + + _internal->m_selectionTask.cancel(); // cancel old task + _internal->m_selectionTask = mapImpl->getEsriGeoView()->identifyGraphicsOverlays(x,y,EventMouseNavigator::pickTolerance(),false,std::numeric_limits::max()); + + if ((_internal->m_selectionIndex >= 0) && (_internal->m_selectionIndex < _internal->m_marker.size())) + { + GraphicalObjectPictureMarker *&marker = _internal->m_marker[_internal->m_selectionIndex]; + + if (marker) + { + marker->setImage(markerImage()); + marker->updateView(); + } + } + _internal->m_selectionIndex = -1; + } + return false; + } // bool RubberbandMultiPointFeature3D::moveAction(AbstractEventDispatcher*,int,int) + + + + bool RubberbandMultiPointFeature3D::completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) + { + if (_internal->m_coordinates.size() > ((_isClosed && !_isCircular) ? 2 : 1)) + { + return applyAndEndAction(causer,x,y); + } + return false; + } // bool RubberbandMultiPointFeature3D::completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) + + + bool RubberbandMultiPointFeature3D::contextMenuAction ( AbstractEventDispatcher*,int,int ) { return rubberbandIsAlive(); } + bool RubberbandMultiPointFeature3D::deleteAction ( AbstractEventDispatcher*,int,int ) { return rubberbandIsAlive(); } + bool RubberbandMultiPointFeature3D::undoAction ( AbstractEventDispatcher*,int,int ) { return rubberbandIsAlive(); } + + + unsigned int RubberbandMultiPointFeature3D::coordinateCount() const + { + return (_internal->m_coordinates.size()); + } // unsigned int RubberbandMultiPointFeature3D::coordinateCount() const + + + void RubberbandMultiPointFeature3D::update() + { + for (auto marker : std::as_const(_internal->m_marker)) + if (marker) marker->updateView(); + if (_internal->m_path) + _internal->m_path->updateView(); + + externalUpdate(); + + } // RubberbandMultiPointFeature3D::update() + + + QVector RubberbandMultiPointFeature3D::coordinateVector( CoordinateSystem cs ) const + { + QVector coordinateVector; + coordinateVector.reserve(_internal->m_coordinates.size()); + + for (const auto &mapCoordinate : std::as_const(_internal->m_coordinates)) + coordinateVector << projectCoordinateFromMap( cs, mapCoordinate ); + return coordinateVector; + } // QVector RubberbandMultiPointFeature3D::coordinateVector( CoordinateSystem cs ) const + + + QPointF RubberbandMultiPointFeature3D::singleCoordinate( unsigned int idx, CoordinateSystem cs ) const + { + if (coordinateCount() > idx) + return coordinateVector(cs).at(idx); + + return QPointF(); + } // QPointF RubberbandMultiPointFeature3D::singleCoordinate( unsigned int idx, CoordinateSystem cs ) const + + + void RubberbandMultiPointFeature3D::setCoordinateVector( const QVector & coordinateVector, CoordinateSystem cs ) + { + _internal->m_coordinates.clear(); + for (const auto &geoCoordinate : coordinateVector) + _internal->m_coordinates << projectCoordinateToMap( cs, geoCoordinate ); + + if ((!_internal->m_toElevation) && (_baseInternal->_map->layerFactory().elevationAvailable())) + { + _internal->m_toElevation = _baseInternal->_map->layerFactory().locationToElevation(); // layerFactory has ownership + } + + // now get elevations + _internal->m_positionList3D.clear(); + for (const auto &pos : std::as_const(_internal->m_coordinates)) + { + if (_internal->m_toElevation) + { + QMetaObject::Connection conn; + + conn = connect(_internal->m_toElevation.data(), &SituationMap::LocationToElevationHelper::elevationCompleted, this, [this,pos,conn](double elevation) + { + disconnect(conn); + + _internal->m_positionList3D << Point3D(pos.x(),pos.y(),elevation); + if (_internal->m_positionList3D.size() == _internal->m_coordinates.size()) + { + if (_internal->m_path) _internal->m_path->setPositions( _internal->m_positionList3D ); + update(); + } + }); + _internal->m_toElevation->locationToElevation(pos); + } + else _internal->m_positionList3D << Point3D::fromQPointF(pos); + } + } // void RubberbandMultiPointFeature3D::setCoordinateVector( const QVector & coordinateVector, CoordinateSystem cs ) + + + void RubberbandMultiPointFeature3D::setCoordinateVector(const QVector & coordinateVector, CoordinateSystem cs) + { + _internal->m_coordinates.clear(); + _internal->m_positionList3D.clear(); + + for (const auto &geoCoordinate : coordinateVector) + { + const QPointF & projCoordinate = projectCoordinateToMap( cs, geoCoordinate.toPointF() ); + _internal->m_coordinates << projCoordinate; + _internal->m_positionList3D << Point3D(projCoordinate.x(),projCoordinate.y(),geoCoordinate.z()); + } + } // void RubberbandMultiPointFeature3D::setCoordinateVector(const QVector & coordinateVector, CoordinateSystem cs) + + + QVector RubberbandMultiPointFeature3D::coordinateVector3D(CoordinateSystem cs) const + { + QVector coordinateVector; + + coordinateVector.reserve(_internal->m_coordinates.size()); + + for (const auto &mapCoordinate : std::as_const(_internal->m_coordinates)) + coordinateVector << Point3D::fromQPointF( projectCoordinateFromMap(cs,mapCoordinate) ); + + return coordinateVector; + } // QVector RubberbandMultiPointFeature3D::coordinateVector3D(CoordinateSystem cs) const + + + Point3D RubberbandMultiPointFeature3D::singleCoordinate3D(unsigned int idx, CoordinateSystem cs) const + { + if (coordinateCount() > idx) + { + return coordinateVector3D(cs).at(idx); + } + return Point3D(); + } // Point3D RubberbandMultiPointFeature3D::singleCoordinate3D(unsigned int idx, CoordinateSystem cs) const + + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_point_feature_3d.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_point_feature_3d.h new file mode 100755 index 000000000..5edca24f7 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_multi_point_feature_3d.h @@ -0,0 +1,128 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define multipoint feature geometry in 3d +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband_3d.h" +#include "IdentifyGraphicsOverlayResult.h" + +namespace SituationMap { namespace RB +{ + + /** + @class RubberbandPointFeature rubberband_point_feature.h + @brief Implementation of a rubberband to input single coodinates for building point features + @see Rubberband3D + */ + class SITUATIONMAP_EXPORT RubberbandMultiPointFeature3D : public Rubberband3D + { + Q_OBJECT + /** + @name Construction & Destruction + @{ + */ + + friend class RubberbandFactory; + + explicit RubberbandMultiPointFeature3D( SituationMap::Map * map, QObject * parent = nullptr, bool isActive = true ); + + public: + + virtual ~RubberbandMultiPointFeature3D(); + + RubberbandMultiPointFeature3D(const RubberbandMultiPointFeature3D &) = delete; + RubberbandMultiPointFeature3D & operator = (const RubberbandMultiPointFeature3D &) = delete; + /** @} */ + + bool selectAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool setCoordinateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool moveAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool movePickedAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool contextMenuAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool deleteAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool undoAction ( AbstractEventDispatcher * causer, int x, int y ) override; + + + inline void enableClosed( bool flag ) + { + _isClosed = flag; + } /* enableClosed */ + + /** + Gets the value of the close flag + */ + inline bool isClosedEnabled() const { return _isClosed; } + + unsigned int coordinateCount() const override; + + QVector coordinateVector( CoordinateSystem cs = CoordinateSystem::Geo ) const override; + + QPointF singleCoordinate( unsigned int idx = 0, CoordinateSystem cs = CoordinateSystem::Geo ) const override; + + void setCoordinateVector( const QVector & coordinateVector, CoordinateSystem cs = CoordinateSystem::Geo ) override; + + Point3D singleCoordinate3D( unsigned int idx = 0, CoordinateSystem cs = CoordinateSystem::Geo ) const override; + QVector coordinateVector3D( CoordinateSystem cs = CoordinateSystem::Geo ) const override; + + void setCoordinateVector( const QVector & coordinateVector, CoordinateSystem cs = CoordinateSystem::Geo ) override; + + inline void enableCircular(bool flag) + { + _isCircular = flag; + } // enableCircular + + inline bool isCircularEnabled() const { return _isCircular; } + + protected: + + virtual bool buttonProxyActive( const QPointF & scenePos) const { Q_UNUSED(scenePos); return false; } + + void activateButtons() override; + + void update() override; + + /* + Forward declaration of internal container class for member variables. The derived classes of Rubberband + often have also a declaration of InternalData for there own internal members. + */ + class InternalData; + + InternalData * _internal; + + bool _isClosed; + bool _isCircular; + + protected slots: + + void onGetPickingResult(const QUuid &taskId, const QList<::Esri::ArcGISRuntime::IdentifyGraphicsOverlayResult*> &identifyResults); + void onMapPosition( const Point3D &mapCoordinate ); + + }; /* RubberbandMultiPointFeature3D */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_cluster.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_cluster.cpp new file mode 100755 index 000000000..3ab843e1d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_cluster.cpp @@ -0,0 +1,133 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of rubberband class to define point feature geoemtry +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_point_cluster.h" +#include "rubberband_point_handle.h" +#include "rubberband_drawable.h" +#include "rubberband_handle_proxy.h" +#include "transient_tactical_symbol.h" + +#include "rubberband_internal.h" +#include "rubberband_base_internal.h" + +namespace SituationMap { namespace RB +{ + + struct RubberbandPointCluster::InternalData + { + Q_DISABLE_COPY(InternalData) + InternalData() = default; + SituationMap::RefPointer _previewSymbol; + }; /* RubberbandMultiPointFeature::InternalData */ + + + RubberbandPointCluster::RubberbandPointCluster( SituationMap::Map * map, QObject * parent, bool isActive ) : RubberbandPointCluster( map, INT_MAX, parent, isActive ) {;} + + + RubberbandPointCluster::RubberbandPointCluster( SituationMap::Map * map, unsigned int maxCoordinates, QObject * parent, bool isActive ) + : RubberbandPointFeature(map,parent,isActive), _internal(new InternalData()), _minimalPointCount(1) + { + _maximalPointCount = maxCoordinates; + } /* RubberbandPointCluster::RubberbandPointCluster */ + + + RubberbandPointCluster::~RubberbandPointCluster() + { + delete _internal; + } /* RubberbandPointCluster::~RubberbandPointCluster */ + + + void RubberbandPointCluster::activateButtons() + { + _baseInternal->_hasUndoButton = isEnabledConstructionState(); + _baseInternal->_applyIsPossible = (static_cast(Rubberband::_internal->_handleList.size()) >= _minimalPointCount); + _baseInternal->_undoIsPossible = (isEnabledConstructionState() && (static_cast(Rubberband::_internal->_handleList.size()) > _minimalPointCount)); + Rubberband::activateButtons(); + } /* RubberbandMultiPointFeature::activateButton */ + + + void RubberbandPointCluster::setCoordinateVector(const QVector & coordinateVector, CoordinateSystem cs ) + { + RubberbandPointFeature::setCoordinateVector( coordinateVector, cs ); + enableModificationState(); + activateButtons(); + } /* RubberbandPointCluster::setCoordinateVector */ + + + bool RubberbandPointCluster::setCoordinateAction( AbstractEventDispatcher * causer, int x, int y ) + { + bool ok = false; + + if (rubberbandIsAlive()) + { + ok = RubberbandPointFeature::setCoordinateAction(causer,x,y); + if (ok) activateButtons(); + } + return ok; + } /* RubberbandPointCluster::setCoordinateAction */ + + + void RubberbandPointCluster::update() + { + RubberbandPointFeature::update(); + if (!_previewSidc.isEmpty()) + { + if (!_internal->_previewSymbol) + { + _internal->_previewSymbol = new TransientTacticalSymbol( static_cast(_baseInternal->_map.data()), _previewSidc ); + } + + QVector coordinateList; + coordinateList.reserve( Rubberband::_internal->_handleList.size() ); + + for (auto handlePoint : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(handlePoint.get()); + if (handlePointPtr) + { + coordinateList.append( handlePointPtr->getCoordinate() ); + } + } + _internal->_previewSymbol->update( coordinateList ); + } + else _internal->_previewSymbol = nullptr; + } /* RubberbandPointCluster::update */ + + + bool RubberbandPointCluster::deleteAction(AbstractEventDispatcher * causer, int x, int y) + { + if (rubberbandIsAlive()) + { + if (static_cast(RubberbandPointFeature::_internal->_handleList.size()) <= _minimalPointCount) return true; + return RubberbandPointFeature::deleteAction(causer,x,y); + } + return false; + } /* RubberbandPointCluster::deleteAction */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_cluster.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_cluster.h new file mode 100755 index 000000000..839206aa1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_cluster.h @@ -0,0 +1,102 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define multiple point feature geoemtry +** $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 .........: +** +** 18.06.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include + +#include "rubberband_point_feature.h" + + +namespace SituationMap { namespace RB +{ + + /** + @class RubberbandPointCluster rubberband_point_cluster.h + @brief Implementation of a rubberband to input single coodinates for building point feature clusters + @see Rubberband, RubberbandPointFeature + */ + class SITUATIONMAP_EXPORT RubberbandPointCluster : public RubberbandPointFeature + { + Q_OBJECT + /** + @name Construction & Destruction + @{ + */ + + friend class RubberbandFactory; + + RubberbandPointCluster( SituationMap::Map * map, QObject * parent, bool isActive = true ); + + RubberbandPointCluster( SituationMap::Map * map + , unsigned int maxCoordinates = std::numeric_limits::max() + , QObject * parent = nullptr + , bool isActive = true ); + + public: + + virtual ~RubberbandPointCluster(); + + RubberbandPointCluster(const RubberbandPointCluster &) = delete; + RubberbandPointCluster & operator = (const RubberbandPointCluster &) = delete; + /** @} */ + + bool setCoordinateAction( AbstractEventDispatcher * causer, int x, int y ) override; + bool deleteAction ( AbstractEventDispatcher * causer, int x, int y ) override; + + + /** + Defines the minimum number of coordinates to input. Default is 1 + */ + inline void minimumPointCount(unsigned int n) { _minimalPointCount = (n > 0) ? n : 1; } + + /** + Returns the setting for minimum number of coordinates to input. + */ + inline unsigned int getMinimumPointCount() { return _minimalPointCount; } + + void setCoordinateVector(const QVector & coordinateVector, CoordinateSystem cs = CoordinateSystem::Geo ) override; + + protected: + + /* + Forward declaration of InternalData to hold private member variables. Becareful the base class Rubberband + has similary a class for internal data member with name "InternalData". Don't be confused it is not the same. + */ + struct InternalData; + + InternalData * _internal; + unsigned int _minimalPointCount; + + void activateButtons() override; + + void update() override; + + }; /* RubberbandPointCluster */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_feature.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_feature.cpp new file mode 100755 index 000000000..21a437a8c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_feature.cpp @@ -0,0 +1,311 @@ + +/** +** +** @RDE_HEADER@ +** Implementationof rubberband class to define point feature geoemtry +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_point_feature.h" +#include "rubberband_point_handle.h" +#include "rubberband_drawable.h" + +#include "rubberband_internal.h" +#include "rubberband_base_internal.h" + +#include +#include + + +namespace SituationMap { namespace RB +{ + + RubberbandPointFeature::RubberbandPointFeature( SituationMap::Map * map, QObject * parent, bool isActive ) + : Rubberband(map,parent,isActive), _maximalPointCount(1) + { + } /* RubberbandPointFeature::RubberbandPointFeature */ + + + RubberbandPointFeature::~RubberbandPointFeature() + { + } /* RubberbandPointFeature::~RubberbandPointFeature */ + + + QVector RubberbandPointFeature::coordinateVector( CoordinateSystem cs ) const + { + QVector coordinateVector; + coordinateVector.reserve(_internal->_handleList.size()); + + for (auto handle : std::as_const(_internal->_handleList)) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); + + coordinateVector.push_back(projectCoordinateFromMap( cs, handlePointPtr->getCoordinate()._mapCoordinate )); + } + return coordinateVector; + } /* RubberbandPointFeature::coordinateVector */ + + QVector RubberbandPointFeature::coordinateVectorPixel() const + { + QVector coordinateVector; + coordinateVector.reserve(_internal->_handleList.size()); + + for (auto handle : std::as_const(_internal->_handleList)) + { + RubberbandPointHandle *handlePointPtr = dynamic_cast(handle.get()); + coordinateVector.push_back(handlePointPtr->getPixelCoordinate()); + } + return coordinateVector; + } + + + QPointF RubberbandPointFeature::singleCoordinate(unsigned int idx, CoordinateSystem cs ) const + { + if (idx < (unsigned int) _internal->_handleList.size()) + { + const RubberbandPointHandle * handle = dynamic_cast(_internal->_handleList[idx].get()); + + return projectCoordinateFromMap( cs, handle->getCoordinate()._mapCoordinate ); + } + return QPointF(); + } /* RubberbandPointFeature::singleCoordinate */ + + + void RubberbandPointFeature::setCoordinateVector(const QVector & coordinateVector, CoordinateSystem cs ) + { + _internal->_handleList.clear(); + + for (auto p : coordinateVector) + { + const QPointF &mapPos = projectCoordinateToMap( cs, p ); + + RubberbandPointHandle * handlePointPtr = new RubberbandPointHandle( _internal->_mapView + , _internal->_mapScene + , Esri::ArcGISRuntime::Point(mapPos.x(),mapPos.y(),Rubberband::_internal->_mapView->map()->spatialReference()) ); + _internal->_handleList.append( SituationMap::RefPointer(handlePointPtr) ); + } + update(); + } /* RubberbandPointFeature::setCoordinateVector */ + + + bool RubberbandPointFeature::selectAction( AbstractEventDispatcher * , int x, int y ) + { + if (rubberbandIsAlive()) + { + bool eventHandled = false; + QPointF scenePos(pixel2ScenePosition(x,y)); + + if (_baseInternal->buttonProxyActive(scenePos)) return false; + + _baseInternal->_lastClickPosition = scenePos; + + RubberbandHandle * handlePtr = pickHandle(x,y); + if (handlePtr) + { + handlePtr->setSelection(); + enableSelectedState(); + eventHandled = true; + } + + if (!isEnabledSelectedState()) + { + if (isEnabledConstructionState()) + { + if (!buttonProxyActive(scenePos)) + { + RubberbandPointHandle * handlePtr = new RubberbandPointHandle( _internal->_mapView, _internal->_mapScene, scenePos ); + + _internal->_handleList.append( SituationMap::RefPointer(handlePtr) ); + handlePtr->setSelection(); + enableSelectedState(); + eventHandled = true; + } + } + } + + if (eventHandled) + { + update(); + deactivateButtons(); + } + + + return eventHandled; + } + return false; + } /* RubberbandPointFeature::selectAction */ + + + + bool RubberbandPointFeature::setCoordinateAction( AbstractEventDispatcher * causer, int x, int y ) + { + if ((rubberbandIsAlive()) && (_internal->_mapView) && (_internal->_mapScene)) + { + bool eventHandled = false; + QPointF scenePos(pixel2ScenePosition(x,y)); + + Rubberband::_internal->_lastClickPosition = scenePos; + + if (isEnabledSelectedState()) + { + if (_baseInternal->buttonProxyActive(scenePos)) return false; + for (auto handle : std::as_const(_internal->_handleList)) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); + if ((handlePointPtr) && (handlePointPtr->isSelected())) + { + handlePointPtr->setSelection(false); + handlePointPtr->moveCoordinate( scenePos - _baseInternal->_lastClickPosition ); + eventHandled = true; + } + } + disenableSelectedState(); + disenableMultiSelectedState(); + } + else if (_baseInternal->buttonProxyActive(scenePos)) return false; + + if (eventHandled) update(); + + if ((isEnabledConstructionState()) && (_internal->_handleList.size() >= static_cast(_maximalPointCount))) + { + completeStateAction( causer, x, y ); + eventHandled = true; + } + return eventHandled; + } + return false; + } /* RubberbandPointFeature::setCoordinateAction */ + + + bool RubberbandPointFeature::moveAction( AbstractEventDispatcher*, int x, int y ) + { + adjustConnectedPreviewPixmap(x,y); + + if (rubberbandIsAlive()) + { + if (!isEnabledMultiSelectedState()) deSelectHandles(); + + RubberbandHandle * handlePtr = pickHandle(x,y); + if (handlePtr) + { + handlePtr->setSelection(); + return false; + } + } + return false; + } /* RubberbandPointFeature::moveAction */ + + + bool RubberbandPointFeature::movePickedAction( AbstractEventDispatcher*, int x, int y ) + { + adjustConnectedPreviewPixmap(x,y); + + if ((rubberbandIsAlive()) && (_internal->_mapView) && (_internal->_mapScene)) + { + if (isEnabledSelectedState()) + { + QPointF scenePos(pixel2ScenePosition(x,y)); + QPointF deltaMove(scenePos-_baseInternal->_lastClickPosition); + + _baseInternal->_lastClickPosition = scenePos; + + bool eventHandled = false; + + for (auto handle : std::as_const(_internal->_handleList)) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); + if ((handlePointPtr) && (handlePointPtr->isSelected())) + { + handlePointPtr->moveCoordinate( deltaMove ); + eventHandled = true; + } + } + if (eventHandled) update(); + } + return false; + } + return false; + } /* RubberbandPointFeature::movePickedAction */ + + + bool RubberbandPointFeature::contextMenuAction( AbstractEventDispatcher*, int , int ) + { + return rubberbandIsAlive(); + } /* RubberbandPointFeature::contextMenuAction */ + + + bool RubberbandPointFeature::completeStateAction( AbstractEventDispatcher * causer, int x, int y ) + { + if (!_internal->_handleList.empty()) + { + return applyAndEndAction(causer,x,y); + } + return false; + } /* RubberbandPointFeature::completeStateAction */ + + + bool RubberbandPointFeature::undoAction( AbstractEventDispatcher*, int , int ) + { + if (rubberbandIsAlive()) + { + if (!_internal->_handleList.empty()) + { + _internal->_handleList.pop_back(); + update(); + activateButtons(); + } + return true; + } + return false; + } /* RubberbandPointFeature::undoAction */ + + + bool RubberbandPointFeature::deleteAction(AbstractEventDispatcher*, int x, int y) + { + if ((rubberbandIsAlive()) && (_internal->_mapView) && (_internal->_mapScene)) + { + if ((_internal->_handleList.size() > 1) && (isEnabledSelectedState())) + { + QPointF scenePosition = pixel2ScenePosition(x,y); + QList>::iterator handleIter = _internal->_handleList.begin(); + + while (handleIter != _internal->_handleList.end()) + { + if (handleIter->get()->isPositionInside(scenePosition)) + { + handleIter = _internal->_handleList.erase(handleIter); + continue; + } + ++handleIter; + } + update(); + activateButtons(); + } + return true; + } + return false; + } /* RubberbandPointFeature::deleteAction */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_feature.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_feature.h new file mode 100755 index 000000000..38d425bdc --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_feature.h @@ -0,0 +1,99 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define point feature geoemtry +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include + +#include "rubberband.h" + + +namespace SituationMap { namespace RB +{ + + /** + @class RubberbandPointFeature rubberband_point_feature.h + @brief Implementation of a rubberband to input single coodinates for building point features + @see Rubberband + */ + class SITUATIONMAP_EXPORT RubberbandPointFeature : public Rubberband + { + Q_OBJECT + /** + @name Construction & Destruction + @{ + */ + + friend class RubberbandFactory; + friend class RubberbandPointCluster; + + explicit RubberbandPointFeature( SituationMap::Map * map, QObject * parent = nullptr, bool isActive = true ); + + public: + ~RubberbandPointFeature(); + /** @} */ + + bool selectAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool setCoordinateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool moveAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool movePickedAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool contextMenuAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool deleteAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool undoAction ( AbstractEventDispatcher * causer, int x, int y ) override; + + + /** + Defines the maximum number of coordinates to input. Default is 1 + */ + inline void maximumPointCount(unsigned int n) { _maximalPointCount = (n > 0) ? n : std::numeric_limits::max(); } + + /** + Returns the setting for maximum number of coordinates to input. + */ + inline unsigned int getMaximumPointCount() { return _maximalPointCount; } + + + QVector coordinateVector( CoordinateSystem cs = CoordinateSystem::Geo ) const override; + QVector coordinateVectorPixel() const; + + QPointF singleCoordinate( unsigned int idx = 0, CoordinateSystem cs = CoordinateSystem::Geo ) const override; + + void setCoordinateVector( const QVector & coordinateVector, CoordinateSystem cs = CoordinateSystem::Geo ) override; + + protected: + + virtual bool buttonProxyActive( const QPointF & scenePos) const { Q_UNUSED(scenePos); return false; } + + + unsigned int _maximalPointCount; + + + }; /* RubberbandPointFeature */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_feature_3d.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_feature_3d.cpp new file mode 100755 index 000000000..a10e4fbd8 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_feature_3d.cpp @@ -0,0 +1,272 @@ +/** +** +** @RDE_HEADER@ +** Implementation of rubberband class to define point feature geometry in 3d +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include + +#include "rubberband_point_feature_3d.h" +#include "rubberband_base_internal.h" + +#include +#include + +#include +#include +#include + +#include + +#include + + +namespace SituationMap { namespace RB +{ + + struct RubberbandPointFeature3D::InternalData + { + QPoint m_screenCordinate = QPoint(std::numeric_limits::min(),std::numeric_limits::min()); + Point3D m_position; + GraphicalObjectPictureMarker * m_marker = nullptr; + ScreenToLocationHelper * m_screenToMap = nullptr; + AbstractEventDispatcher * m_causer = nullptr; + bool m_modifyPosition = false; + bool m_positionReady = false; + + InternalData() = default; + + ~InternalData() + { + delete m_marker; + delete m_screenToMap; + } + }; // RubberbandPointFeature3D::InternalData + + + RubberbandPointFeature3D::RubberbandPointFeature3D( SituationMap::Map * map, QObject * parent, bool isActive ) + : Rubberband3D(map,parent,isActive) + , _internal( new RubberbandPointFeature3D::InternalData() ) + { + MapImpl *mapImpl = static_cast(_baseInternal->_map.data()); + Esri::ArcGISRuntime::SceneGraphicsView *view = mapImpl->getEsriSceneGraphicsView(); + + _internal->m_screenToMap = new ScreenToLocationHelper(view); + connect( _internal->m_screenToMap, &ScreenToLocationHelper::mouseScenePositionChanged, this, &RubberbandPointFeature3D::onMapPosition ); + + } // RubberbandPointFeature3D::RubberbandPointFeature3D( SituationMap::Map * map, QObject * parent = nullptr, bool isActive ) + + + RubberbandPointFeature3D::~RubberbandPointFeature3D() + { + delete _internal; + _internal = nullptr; + } // RubberbandPointFeature3D::~RubberbandPointFeature3D() + + + void RubberbandPointFeature3D::enableModificationState() + { + _internal->m_modifyPosition = true; + _internal->m_positionReady = true; + } // void RubberbandPointFeature3D::enableModificationState() + + + void RubberbandPointFeature3D::onMapPosition(const Point3D &mapCoordinate) + { + if (qIsNaN(mapCoordinate.x())) return; // illegal map position + if (qIsNaN(mapCoordinate.y())) return; + + _internal->m_position = mapCoordinate; + + if (nullptr == _internal->m_marker) + { + MapImpl *mapImpl = static_cast(_baseInternal->_map.data()); + Esri::ArcGISRuntime::SceneGraphicsView *view = mapImpl->getEsriSceneGraphicsView(); + Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = mapImpl->getGraphicsLayer(mapImpl->layerFactory().rubberbandLayer()); + + if (nullptr == view) return; + + _internal->m_marker = new GraphicalObjectPictureMarker( view, graphicsLayer, QStringLiteral("RubberbandPointFeature3D"), INVALID_MAP_ID ); + + _internal->m_marker->setImage(markerImage()); + } + _internal->m_marker->setOrigin( mapCoordinate ); + + update(); + + if (_internal->m_causer) + { + _internal->m_positionReady = true; + completeStateAction(_internal->m_causer,_internal->m_screenCordinate.x(), _internal->m_screenCordinate.y()); + } + } // void RubberbandPointFeature3D::onMapPosition(const QPointF &mapCoordinate) + + + bool RubberbandPointFeature3D::selectAction(AbstractEventDispatcher *, int x, int y) + { + if (rubberbandIsAlive()) + { + if (nullptr ==_internal->m_causer) + { + _internal->m_screenCordinate = QPoint(x,y); + _internal->m_screenToMap->screenToLocation(x,y); + } + return true; + } + return false; + } // bool RubberbandPointFeature3D::selectAction(AbstractEventDispatcher *, int x, int y) + + + bool RubberbandPointFeature3D::setCoordinateAction(AbstractEventDispatcher * causer, int x, int y) + { + if (rubberbandIsAlive()) + { + _internal->m_causer = (_internal->m_modifyPosition) ? nullptr : causer; + _internal->m_screenCordinate = QPoint(x,y); + _internal->m_screenToMap->screenToLocation(x,y); + + return true; + } + return false; + } // bool RubberbandPointFeature3D::setCoordinateAction(AbstractEventDispatcher * causer, int x, int y) + + + bool RubberbandPointFeature3D::movePickedAction(AbstractEventDispatcher *, int x, int y) + { + if (rubberbandIsAlive()) + { + if (nullptr == _internal->m_causer) + { + _internal->m_screenCordinate = QPoint(x,y); + _internal->m_screenToMap->screenToLocation(x,y); + } + return true; + } + return false; + } // bool RubberbandPointFeature3D::movePickedAction(AbstractEventDispatcher *, int x, int y) + + + bool RubberbandPointFeature3D::moveAction(AbstractEventDispatcher*,int,int) + { + if (rubberbandIsAlive()) + { + } + return false; + } // bool RubberbandPointFeature3D::moveAction(AbstractEventDispatcher*,int,int) + + + + bool RubberbandPointFeature3D::completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) + { + if (coordinateCount() > 0) + { + return applyAndEndAction(causer,x,y); + } + return false; + } // bool RubberbandPointFeature3D::completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) + + + bool RubberbandPointFeature3D::contextMenuAction ( AbstractEventDispatcher*,int,int ) { return rubberbandIsAlive(); } + bool RubberbandPointFeature3D::deleteAction ( AbstractEventDispatcher*,int,int ) { return rubberbandIsAlive(); } + bool RubberbandPointFeature3D::undoAction ( AbstractEventDispatcher*,int,int ) { return rubberbandIsAlive(); } + + + unsigned int RubberbandPointFeature3D::coordinateCount() const + { + return (_internal->m_positionReady) ? 1 : 0; + } // unsigned int RubberbandPointFeature3D::coordinateCount() const + + + void RubberbandPointFeature3D::update() + { + if (_internal->m_marker) _internal->m_marker->updateView(); + externalUpdate(); + } // RubberbandPointFeature3D::update() + + + QVector RubberbandPointFeature3D::coordinateVector( CoordinateSystem cs ) const + { + if (coordinateCount() > 0) + { + const QPointF &geoPos = projectCoordinateFromMap( cs, _internal->m_position.toPointF() ); + return QVector() << geoPos; + } + return QVector(); + } // QVector RubberbandPointFeature3D::coordinateVector( CoordinateSystem cs ) const + + + QPointF RubberbandPointFeature3D::singleCoordinate( unsigned int idx, CoordinateSystem cs ) const + { + Q_UNUSED(idx); + if (coordinateCount() > 0) + { + return coordinateVector(cs).value(0); + } + return QPointF(); + } // QPointF RubberbandPointFeature3D::singleCoordinate( unsigned int idx, CoordinateSystem cs ) const + + + void RubberbandPointFeature3D::setCoordinateVector( const QVector & coordinateVector, CoordinateSystem cs ) + { + if (coordinateVector.size() > 0) + { + const QPointF &mapPos = projectCoordinateToMap(cs,coordinateVector[0]); + onMapPosition( Point3D::fromQPointF(mapPos) ); + } + } // void RubberbandPointFeature3D::setCoordinateVector( const QVector & coordinateVector, CoordinateSystem cs ) + + + void RubberbandPointFeature3D::setCoordinateVector(const QVector & coordinateVector, CoordinateSystem cs ) + { + if (coordinateVector.size() > 0) + { + const QPointF &mapPos = projectCoordinateToMap(cs,coordinateVector[0].toPointF()); + _internal->m_position = Point3D(mapPos.x(),mapPos.y(),coordinateVector[0].z()); + } + } // void RubberbandPointFeature3D::setCoordinateVector(const QVector & coordinateVector, CoordinateSystem cs ) + + + QVector RubberbandPointFeature3D::coordinateVector3D( CoordinateSystem cs ) const + { + if (coordinateCount() > 0) + { + const QPointF &geoPos = projectCoordinateFromMap( cs, _internal->m_position.toPointF() ); + return QVector() << Point3D(geoPos.x(),geoPos.y(),_internal->m_position.z()); + } + return QVector(); + } // QVector RubberbandPointFeature3D::coordinateVector3D( CoordinateSystem cs ) const + + + Point3D RubberbandPointFeature3D::singleCoordinate3D(unsigned int idx , CoordinateSystem cs) const + { + Q_UNUSED(idx); + if (coordinateCount() > 0) + { + return coordinateVector3D(cs).value(0); + } + return Point3D(); + } // Point3D RubberbandPointFeature3D::singleCoordinate3D(unsigned int idx, CoordinateSystem cs) const + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_feature_3d.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_feature_3d.h new file mode 100755 index 000000000..ed3f980ee --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_feature_3d.h @@ -0,0 +1,104 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define point feature geometry in 3d +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband_3d.h" + + +namespace SituationMap { namespace RB +{ + + /** + @class RubberbandPointFeature rubberband_point_feature.h + @brief Implementation of a rubberband to input single coodinates for building point features + @see Rubberband + */ + class SITUATIONMAP_EXPORT RubberbandPointFeature3D : public Rubberband3D + { + Q_OBJECT + /** + @name Construction & Destruction + @{ + */ + + friend class RubberbandFactory; + + explicit RubberbandPointFeature3D( SituationMap::Map * map, QObject * parent = nullptr, bool isActive = true ); + + public: + + ~RubberbandPointFeature3D(); + /** @} */ + + bool selectAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool setCoordinateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool moveAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool movePickedAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool contextMenuAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool deleteAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool undoAction ( AbstractEventDispatcher * causer, int x, int y ) override; + + + unsigned int coordinateCount() const override; + + QVector coordinateVector( CoordinateSystem cs = CoordinateSystem::Geo ) const override; + + QPointF singleCoordinate( unsigned int idx = 0, CoordinateSystem cs = CoordinateSystem::Geo ) const override; + + void setCoordinateVector( const QVector & coordinateVector, CoordinateSystem cs = CoordinateSystem::Geo ) override; + + Point3D singleCoordinate3D( unsigned int idx = 0, CoordinateSystem cs = CoordinateSystem::Geo ) const override; + QVector coordinateVector3D( CoordinateSystem cs = CoordinateSystem::Geo ) const override; + + void setCoordinateVector( const QVector & coordinateVector, CoordinateSystem cs = CoordinateSystem::Geo ) override; + + void enableModificationState(); + + protected: + + virtual bool buttonProxyActive( const QPointF & scenePos) const { Q_UNUSED(scenePos); return false; } + + void update() override; + + /* + Forward declaration of internal container class for member variables. The derived classes of Rubberband + often have also a declaration of InternalData for there own internal members. + */ + struct InternalData; + + InternalData * _internal; + + protected slots: + + void onMapPosition( const Point3D &mapCoordinate ); + + }; /* RubberbandPointFeature3D */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_handle.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_handle.cpp new file mode 100755 index 000000000..55087de3e --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_handle.cpp @@ -0,0 +1,390 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of base class for rubberband handles +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_point_handle.h" + +#include + +#include + +#include +#include + +namespace SituationMap { namespace RB +{ + + RubberbandPointHandle::RubberbandPointHandle( Esri::ArcGISRuntime::MapGraphicsView * mapView + , QGraphicsScene * scene + , const QPointF & scenePosition ) + : RubberbandHandle(mapView,scene) + , _pointSymbol(nullptr) + , _coordinate( mapView, scenePosition ) + , _sceneOffset(0,0) + , _iconPixmap(nullptr) + , _boundingRect(nullptr) + { + zBufferPlane( ZBufferPlane::PointHandle ); + buildHandle( scenePosition ); + } /* RubberbandPointHandle::RubberbandPointHandle */ + + + RubberbandPointHandle::RubberbandPointHandle( Esri::ArcGISRuntime::MapGraphicsView * mapView + , QGraphicsScene * scene + , const Esri::ArcGISRuntime::Point & mapCoordinate ) + : RubberbandHandle(mapView,scene) + , _pointSymbol(nullptr) + , _coordinate( mapView, mapCoordinate ) + , _sceneOffset(0,0) + , _iconPixmap(nullptr) + , _boundingRect(nullptr) + { + zBufferPlane( ZBufferPlane::PointHandle ); + buildHandle( _coordinate._sceneCoordinate ); + } /* RubberbandPointHandle::RubberbandPointHandle */ + + + RubberbandPointHandle::RubberbandPointHandle( Esri::ArcGISRuntime::MapGraphicsView * mapView + , QGraphicsScene * scene + , const QPointF & scenePosition + , const QPixmap & icon ) + : RubberbandHandle(mapView,scene) + , _pointSymbol(nullptr) + , _coordinate( mapView, scenePosition ) + , _sceneOffset(0,0) + , _iconPixmap( new QPixmap(icon) ) + , _boundingRect(nullptr) + { + zBufferPlane( ZBufferPlane::PointHandle ); + buildHandle( scenePosition ); + } /* RubberbandPointHandle::RubberbandPointHandle */ + + + RubberbandPointHandle::RubberbandPointHandle( Esri::ArcGISRuntime::MapGraphicsView * mapView + , QGraphicsScene * scene + , const Esri::ArcGISRuntime::Point & mapCoordinate + , const QPixmap & icon ) + : RubberbandHandle(mapView,scene) + , _pointSymbol(nullptr) + , _coordinate( mapView, mapCoordinate ) + , _sceneOffset(0,0) + , _iconPixmap( new QPixmap(icon) ) + , _boundingRect(nullptr) + { + zBufferPlane( ZBufferPlane::PointHandle ); + buildHandle( _coordinate._sceneCoordinate ); + } /* RubberbandPointHandle::RubberbandPointHandle */ + + + void RubberbandPointHandle::setIcon( const QPixmap & icon ) + { + if (_iconPixmap == nullptr) + { + _iconPixmap = new QPixmap(icon); + if (_pointSymbol) + { + _scene->removeItem(_pointSymbol); + delete _pointSymbol; + _pointSymbol = nullptr; + } + buildHandle( _coordinate._sceneCoordinate ); + return; + } + + *_iconPixmap = icon; + + QGraphicsPixmapItem * iconItem = static_cast( _pointSymbol); + if (iconItem) + { + iconItem->setPixmap( *_iconPixmap ); + QRectF bbox = iconItem->boundingRect(); + bbox.moveCenter( _coordinate._sceneCoordinate ); + if (_boundingRect) _boundingRect->setRect(bbox); + } + else buildHandle( _coordinate._sceneCoordinate ); + } /* RubberbandPointHandle::setIcon */ + + + void RubberbandPointHandle::buildHandle( const QPointF & scenePosition ) + { + if (_scene != nullptr) + { + if (_iconPixmap != nullptr) + { + if (_iconPixmap->isNull()) + { + delete _iconPixmap; + _iconPixmap = nullptr; + } + } + + if (_iconPixmap == nullptr) + { + QGraphicsEllipseItem * kuller = new QGraphicsEllipseItem(); + + _pointSymbol = kuller; + + if (_pointSymbol != nullptr) + { + _scene -> addItem( _pointSymbol ); + + kuller -> setPos( scenePosition+_sceneOffset ); + kuller -> setRect( -qreal(_pickingRatio), -qreal(_pickingRatio), qreal(2*_pickingRatio), qreal(2*_pickingRatio) ); + kuller -> setZValue( _zBufferValue ); + + kuller -> setCacheMode(QGraphicsItem::CacheMode::NoCache, QSize(_pickingRatio*2,_pickingRatio*2) ); + kuller -> setFlags( QGraphicsItem::ItemIgnoresTransformations ); + kuller -> setVisible( _isVisible ); + } + } + else + { + QGraphicsPixmapItem * icon = new QGraphicsPixmapItem(*_iconPixmap); + + _pointSymbol = icon; + + if (_pointSymbol != nullptr) + { + _scene -> addItem( _pointSymbol ); + + qreal xOff = -(_iconPixmap->width() * 0.5); + qreal yOff = -(_iconPixmap->height() * 0.5); + + icon -> setPos( scenePosition+_sceneOffset ); + icon -> setZValue( _zBufferValue ); + icon -> setFlags( QGraphicsItem::ItemIgnoresTransformations ); + icon -> setVisible( _isVisible ); + icon -> setOffset( xOff, yOff ); + icon -> setShapeMode( QGraphicsPixmapItem::BoundingRectShape ); + + QRectF bbox = icon->boundingRect(); + bbox.moveCenter( scenePosition ); + + _boundingRect = new QGraphicsRectItem( bbox ); + _boundingRect -> setZValue(_zBufferValue-1); + _boundingRect -> setVisible( _isVisible ); + + _scene -> addItem( _boundingRect ); + + } + } + } + } /* RubberbandPointHandle::buildHandle */ + + + RubberbandPointHandle::~RubberbandPointHandle() + { + delete _iconPixmap; + if (_scene) + { + + if (nullptr != _boundingRect) + { + _scene->removeItem(_boundingRect); + delete _boundingRect; + } + + if (nullptr != _pointSymbol) + { + _scene->removeItem(_pointSymbol); + delete _pointSymbol; + } + } + + _pointSymbol = nullptr; + _iconPixmap = nullptr; + _boundingRect = nullptr; + + } /* RubberbandHandle::~Rubberbandhandle */ + + + QPointF RubberbandPointHandle::getRotatedSceneOffset() const + { + auto rotate = [](const QPointF & p, qreal sinA, qreal cosA) -> QPointF { return QPointF(p.x()*cosA+p.y()*sinA,p.y()*cosA-p.x()*sinA); }; + + qreal mapRotatePhi = _mapView->mapRotation() * MathConst::pi / 180.0; + + qreal sinMap = sin(mapRotatePhi); + qreal cosMap = cos(mapRotatePhi); + + return rotate( _sceneOffset, sinMap, cosMap ); + } /* RubberbandPointHandle::getRotatedSceneOffset */ + + + void RubberbandPointHandle::adjustCoordinates() + { + const QPointF & rotatedSceneOffset = getRotatedSceneOffset(); + + if (_iconPixmap == nullptr) + { + static_cast(_pointSymbol)->setPos(_coordinate.mapToScene(_mapView)+rotatedSceneOffset); + } + else + { + QGraphicsPixmapItem * icon = static_cast( _pointSymbol); + + const QPointF & scenePosition = _coordinate.mapToScene(_mapView); + icon -> setPos( scenePosition+rotatedSceneOffset ); + + QRectF bbox = icon->boundingRect(); + bbox.moveCenter( scenePosition+rotatedSceneOffset ); + _boundingRect->setRect(bbox); + } + + DEBUG_LOG(_logger) << LOGFUNCTION << " map-coordinate = " << QString::number(_coordinate._mapCoordinate.x(),'f',15) + << " ; " << QString::number(_coordinate._mapCoordinate.y(),'f',15) + << " scene-coordinate = " << _coordinate._sceneCoordinate.x() << " ; " << _coordinate._sceneCoordinate.y(); + emit adjusted(); + } /* RubberbandPointHandle::onDo_adjustCoordinates */ + + + void RubberbandPointHandle::adjustWindow( const QRectF& ) + { + onDo_adjustCoordinates(); + } /* RubberbandPointHandle::onDo_adjustWindow */ + + + void RubberbandPointHandle::updateGraphic() + { + if (_pointSymbol) + { + if (_pointSymbol != nullptr) + { + if (_iconPixmap == nullptr) + { + QGraphicsEllipseItem * kuller = static_cast( _pointSymbol); + + kuller -> setPen( QPen(_drawColor) ); + + QRadialGradient gradient(0, 0, _pickingRatio, 0, 0); + QColor gradientColor = (_isSelected) ? _selectionColor : _fillColor; + + gradient.setColorAt( 0, gradientColor); + gradient.setColorAt( 1, QColor(gradientColor.red(), gradientColor.green(), gradientColor.blue(), 0) ); + kuller -> setBrush( QBrush(gradient) ); + kuller -> setVisible( _isVisible ); + } + else + { + QGraphicsPixmapItem * icon = static_cast( _pointSymbol); + + _boundingRect -> setPen( QPen(_drawColor) ); + const QColor &backColor = (_isSelected) ? _selectionColor : _fillColor; + _boundingRect -> setBrush( QBrush(backColor) ); + _boundingRect -> setVisible( _isVisible ); + icon -> setVisible( _isVisible ); + } + } + } + } /* RubberbandPointHandle::updateGraphic */ + + + void RubberbandPointHandle::updateCoordinate(const QPointF & scenePosition) + { + const QPointF & rotatedSceneOffset = getRotatedSceneOffset(); + + _coordinate.updateSceneCoordinate( _mapView, scenePosition ); + + if (_pointSymbol != nullptr) + { + if (_iconPixmap == nullptr) + { + QGraphicsEllipseItem * kuller = static_cast( _pointSymbol); + kuller -> setPos( scenePosition+rotatedSceneOffset ); + } + else + { + QGraphicsPixmapItem * icon = static_cast( _pointSymbol); + icon -> setPos( scenePosition+rotatedSceneOffset ); + + QRectF bbox = icon->boundingRect(); + bbox.moveCenter( scenePosition+rotatedSceneOffset ); + + _boundingRect->setRect(bbox); + } + } + } /* RubberbandPointHandle::updateCoordinate */ + + + void RubberbandPointHandle::updateCoordinate() + { + const QPointF & rotatedSceneOffset = getRotatedSceneOffset(); + + if (_pointSymbol != nullptr) + { + if (_iconPixmap == nullptr) + { + QGraphicsEllipseItem * kuller = static_cast( _pointSymbol); + kuller -> setPos( _coordinate._sceneCoordinate+rotatedSceneOffset ); + } + else + { + QGraphicsPixmapItem * icon = static_cast( _pointSymbol); + icon -> setPos( _coordinate._sceneCoordinate+rotatedSceneOffset ); + + QRectF bbox = icon->boundingRect(); + + bbox.moveCenter( _coordinate._sceneCoordinate+rotatedSceneOffset ); + _boundingRect->setRect(bbox); + } + } + } /* RubberbandPointHandle::updateCoordinate */ + + + void RubberbandPointHandle::moveCoordinate(const QPointF & sceneDelta) + { + _coordinate.updateSceneCoordinate( _mapView, _coordinate._sceneCoordinate+sceneDelta ); + updateCoordinate(); + } /* RubberbandPointHandle::moveCoordinate */ + + + void RubberbandPointHandle::setCoordinate(const RubberbandHandle::RubberbandCoordinate & p) + { + _coordinate = p; + updateCoordinate(); + } /* RubberbandPointHandle::setCoordinate */ + + + RubberbandHandle::RubberbandCoordinate RubberbandPointHandle::getCoordinate() const { return _coordinate; } + + QPointF RubberbandPointHandle::getPixelCoordinate() const + { + return _coordinate._sceneCoordinate; + } + + + bool RubberbandPointHandle::isPositionInside( const QPointF & scenePosition ) const + { + if (_pointSymbol) + { + return _pointSymbol->contains( _coordinate._sceneCoordinate+getRotatedSceneOffset() - scenePosition ); + } + return false; + } /* RubberbandPointHandle::isPositionInside */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_handle.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_handle.h new file mode 100755 index 000000000..82601814f --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_point_handle.h @@ -0,0 +1,116 @@ +/** +** +** @RDE_HEADER@ +** Definition of base class for rubberband handles +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include + +#include + +#include "rubberband_handle.h" + +class QGraphicsItem; +class QGraphicsScene; + +namespace SituationMap { namespace RB +{ + + /** + @class RubberbandPointHandle rubberband_point_handle.h + @brief Implementation of a point handle for rubberband internal use. + @see RubberbandHandle + */ + class RubberbandPointHandle : public RubberbandHandle + { + Q_OBJECT + public: + + /** + @name Construction & Destruction + @{ + */ + RubberbandPointHandle( Esri::ArcGISRuntime::MapGraphicsView* + , QGraphicsScene* + , const QPointF & scenePosition ); + + RubberbandPointHandle( Esri::ArcGISRuntime::MapGraphicsView* + , QGraphicsScene* + , const ::Esri::ArcGISRuntime::Point & mapCoordinate ); + + RubberbandPointHandle( Esri::ArcGISRuntime::MapGraphicsView* + , QGraphicsScene* + , const QPointF & scenePosition + , const QPixmap & icon ); + + RubberbandPointHandle( Esri::ArcGISRuntime::MapGraphicsView* + , QGraphicsScene* + , const ::Esri::ArcGISRuntime::Point & mapCoordinate + , const QPixmap & icon ); + + ~RubberbandPointHandle(); + /** @} */ + + void setIcon( const QPixmap & icon ); + + bool isPositionInside( const QPointF & scenePosition ) const override; + virtual void updateCoordinate( const QPointF & scenePosition ); + virtual void moveCoordinate( const QPointF & sceneDelta ); + + virtual void setCoordinate( const RubberbandCoordinate & ); + virtual RubberbandCoordinate getCoordinate() const; + + QPointF getPixelCoordinate() const; + + inline void setSceneOffset(const QPointF & offset) { _sceneOffset = offset; } + + protected: + + QGraphicsItem * _pointSymbol; + RubberbandCoordinate _coordinate; + QPointF _sceneOffset; + QPixmap * _iconPixmap; + QGraphicsRectItem * _boundingRect; + + void updateGraphic() override; + + virtual void updateCoordinate(); + + void adjustCoordinates() override; + void adjustWindow( const QRectF & ) override; + + private: + + QPointF getRotatedSceneOffset() const; + + void buildHandle( const QPointF & scenePosition ); + + + + }; /* RubberbandPointHandle */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_polyline_width.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_polyline_width.cpp new file mode 100755 index 000000000..686979853 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_polyline_width.cpp @@ -0,0 +1,318 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define multi point feature geometry with an additional width coordinate +** $Id$ +** $HeadURL$ +** $Author$ +** +** ----------------------------------------------------------------------------------------------- +** +** Hardware-Environment: Intel-PC +** Software-Environment: Linux, Windows 7, Qt 5.7. ArcGISRuntimeQt 100 +** +** Language ........: C++ 11 +** +** ----------------------------------------------------------------------------------------------- +** +** Author ...........: Jörg Turla, SPD G, Tel. 3132 +** +** ----------------------------------------------------------------------------------------------- +** +** Notes .........: +** +** 7.12.2016, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_polyline_width.h" +#include "rubberband_point_handle.h" + +#include "rubberband_internal.h" +#include "rubberband_base_internal.h" + +#include + +#include + + + +namespace SituationMap { namespace RB +{ + + struct RubberbandPolylineWidth::InternalData + { + SituationMap::RefPointer _widthItem; + }; /* RubberbandPolylineWidth::InternalData*/ + + + RubberbandPolylineWidth::RubberbandPolylineWidth( SituationMap::Map * map, QObject * parent ) + : RubberbandMultiPointFeature(map,true,parent), _internal( new InternalData() ) + { + } /* RubberbandPolylineWidth::RubberbandPolylineWidth */ + + + RubberbandPolylineWidth::~RubberbandPolylineWidth() + { + delete _internal; + } /* RubberbandPolylineWidth::~RubberbandPolylineWidth */ + + + void RubberbandPolylineWidth::buildWidthItem(const QPointF & mapPos1, const QPointF & mapPos2) const + { + double dx = mapPos2.x() - mapPos1.x(); + double dy = mapPos2.y() - mapPos1.y(); + QPointF handleMapPos = mapPos1 + QPointF(dx,dy)*0.35 + QPointF(dy,dx)*0.35; + + setUpWidthItem(handleMapPos); + } /* RubberbandPolylineWidth::buildWidthItem */ + + + void RubberbandPolylineWidth::setUpWidthItem(const QPointF & mapPos) const + { + RubberbandHandle::RubberbandCoordinate coordinate( Rubberband::_internal->_mapView, convert(mapPos,Rubberband::_internal->_mapView->map()->spatialReference()) ); + + _internal->_widthItem = new RubberbandPointHandle(Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, coordinate._sceneCoordinate); + } /* RubberbandPolylineWidth::setUpWidthItem */ + + + QVector RubberbandPolylineWidth::coordinateVector( CoordinateSystem cs ) const + { + QVector polylineCoords = RubberbandMultiPointFeature::coordinateVector(cs); + if (polylineCoords.size() > 1) + { + if (_internal->_widthItem == nullptr) + { + RubberbandPointHandle * pointHandle1Ptr = static_cast(Rubberband::_internal->_handleList[0].get()); + RubberbandPointHandle * pointHandle2Ptr = static_cast(Rubberband::_internal->_handleList[1].get()); + Esri::ArcGISRuntime::Point p1 = pointHandle1Ptr->getCoordinate()._mapCoordinate; + Esri::ArcGISRuntime::Point p2 = pointHandle2Ptr->getCoordinate()._mapCoordinate; + + buildWidthItem( QPointF(p1.x(),p1.y()), QPointF(p2.x(),p2.y()) ); + + } + polylineCoords += projectCoordinateFromMap(cs, _internal->_widthItem->getCoordinate()._mapCoordinate); + } + return polylineCoords; + } /* RubberbandPolylineWidth::coordinateVector */ + + + QPointF RubberbandPolylineWidth::singleCoordinate( unsigned int idx, CoordinateSystem cs ) const + { + if (static_cast(Rubberband::_internal->_handleList.size()) == idx) + { + return projectCoordinateFromMap(cs, _internal->_widthItem->getCoordinate()._mapCoordinate); + } + return RubberbandMultiPointFeature::singleCoordinate(idx, cs); + } /* RubberbandPolylineWidth::singleCoordinate */ + + + void RubberbandPolylineWidth::setCoordinateVector( const QVector & coordinateVector, CoordinateSystem cs ) + { + if (coordinateVector.size() > 2) + { + QVector lineCoords = coordinateVector; + setUpWidthItem( projectCoordinateToMap(cs, lineCoords.last()) ); + lineCoords.removeLast(); + RubberbandMultiPointFeature::setCoordinateVector(lineCoords, cs); + + return; + } + if (coordinateVector.size() == 2) + { + const QPointF & mapP1 = projectCoordinateToMap(cs, coordinateVector[0]); + const QPointF & mapP2 = projectCoordinateToMap(cs, coordinateVector[1]); + + buildWidthItem(mapP1, mapP2); + } + RubberbandMultiPointFeature::setCoordinateVector(coordinateVector,cs); + } /* RubberbandPolylineWidth::setCoordinateVector */ + + + bool RubberbandPolylineWidth::selectAction( AbstractEventDispatcher * causer, int x, int y ) + { + if (rubberbandIsAlive() && (nullptr != _internal->_widthItem.get())) + { + _internal->_widthItem->setSelection(false); + QPointF scenePos(pixel2ScenePosition(x, y)); + if (_baseInternal->buttonProxyActive(scenePos)) return false; + if (_internal->_widthItem->isPositionInside(scenePos)) + { + _internal->_widthItem->setSelection(); + _baseInternal->_lastClickPosition = scenePos; + update(); + return true; + } + } + return RubberbandMultiPointFeature::selectAction(causer,x,y); + } /* RubberbandPolylineWidth::selectAction */ + + + bool RubberbandPolylineWidth::setCoordinateAction( AbstractEventDispatcher * causer, int x, int y ) + { + if (rubberbandIsAlive() && (nullptr != _internal->_widthItem.get())) + { + if (_internal->_widthItem->isSelected()) + { + QPointF scenePos(pixel2ScenePosition(x, y)); + QPointF deltaMove = scenePos - _baseInternal->_lastClickPosition; + + _internal->_widthItem->setSelection(false); + _internal->_widthItem->moveCoordinate(deltaMove); + update(); + return true; + } + } + return RubberbandMultiPointFeature::setCoordinateAction(causer, x, y); + } /* RubberbandPolylineWidth::setCoordinateAction */ + + + bool RubberbandPolylineWidth::movePickedAction( AbstractEventDispatcher * causer, int x, int y ) + { + if (rubberbandIsAlive() && (nullptr != _internal->_widthItem.get())) + { + if (_internal->_widthItem->isSelected()) + { + adjustConnectedPreviewPixmap(x, y); + + QPointF scenePos(pixel2ScenePosition(x, y)); + QPointF deltaMove = scenePos - _baseInternal->_lastClickPosition; + _baseInternal->_lastClickPosition = scenePos; + + _internal->_widthItem->moveCoordinate(deltaMove); + update(); + return false; + } + else + { + RubberbandPointHandle * handlePoint1Ptr = dynamic_cast(Rubberband::_internal->_handleList[0].get()); + RubberbandPointHandle * handlePoint2Ptr = dynamic_cast(Rubberband::_internal->_handleList[1].get()); + + if (handlePoint1Ptr && handlePoint2Ptr) + { + const RubberbandDrawable::RubberbandCoordinate & loc1Start = handlePoint1Ptr->getCoordinate(); + const RubberbandDrawable::RubberbandCoordinate & loc2Start = handlePoint2Ptr->getCoordinate(); + + bool ret = RubberbandMultiPointFeature::movePickedAction(causer, x, y); + + const RubberbandDrawable::RubberbandCoordinate & loc1End = handlePoint1Ptr->getCoordinate(); + const RubberbandDrawable::RubberbandCoordinate & loc2End = handlePoint2Ptr->getCoordinate(); + + QPointF deltaMove1 = loc1End._sceneCoordinate - loc1Start._sceneCoordinate; + QPointF deltaMove2 = loc2End._sceneCoordinate - loc2Start._sceneCoordinate; + + + if ((fabs(deltaMove1.x()) >= 1) || (fabs(deltaMove1.y()) >= 1) || (fabs(deltaMove2.x()) >= 1) || (fabs(deltaMove2.y()) >= 1)) + { + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * First or second point have been moved. Now recalculate the coordinate of the width handle point as follows: + * The width handle "C" and the first two coordinates ("A","B") are providing a triangle: + * B + * / \ + * / \ + * /sinB \ + * / cosB \ + * / \ + * / \ + * c / \ a + * / \ + * / \ + * / \ + * / \ + * / sinA sinC \ + * / cosA cosC \ + * A ------------------------- C + * b + * If the coordinates of "A" or "B" are changed, try to keep the ratio of angles and sides of the triangle constant. For this + * use some trigonometric identities. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + auto rotate = []( const QPointF & p, qreal sinPhi, qreal cosPhi ) -> QPointF + { + return QPointF( p.x()*cosPhi - p.y()*sinPhi, p.y()*cosPhi + p.x()*sinPhi ); + }; + + QPointF A = loc2Start._sceneCoordinate; + QPointF B = loc1Start._sceneCoordinate; + QPointF C = _internal->_widthItem->getCoordinate()._sceneCoordinate; + + qreal a = QLineF(B,C).length(); // side length of triangle + qreal b = QLineF(A,C).length(); + qreal c = QLineF(A,B).length(); + qreal cosA = (b*b+c*c-a*a)/(b*c*2); // cosin rule + qreal cosB = (a*a+c*c-b*b)/(a*c*2); + qreal cosC = (a*a+b*b-c*c)/(a*b*2); + qreal sinA = sqrt(1-cosA*cosA); // pythagorean trigonometric identity + qreal sinB = sqrt(1-cosB*cosB); + qreal sinC = sqrt(1-cosC*cosC); + + // now we have all angle of the triangle, set A and B to the new coordinates ... + + A = loc2End._sceneCoordinate; + B = loc1End._sceneCoordinate; + + // ... and calculate the coordinate of C with the same ratio of angles as before ... + + QPointF AB = B-A; + +// c = QLineF(QPointF(0,0),AB).length(); + + if (sinC != 0.0) + { + // ... rotate AB and scale it to the right length + C = A + rotate(AB,sinA,cosA) * sinB / sinC; + + // the RubberbandPointHandle dosn't has a set to set scene coordinate. Using the moveCoordinate methode instead. + + _internal->_widthItem->moveCoordinate( C - _internal->_widthItem->getCoordinate()._sceneCoordinate ); + } + // else could only happen when the triangle is degenrated to a line + update(); + } + return ret; + } + } + } + return RubberbandMultiPointFeature::movePickedAction(causer, x, y); + } /* RubberbandPolylineWidth::movePickedAction */ + + + bool RubberbandPolylineWidth::moveAction( AbstractEventDispatcher * causer, int x, int y ) + { + if (rubberbandIsAlive() && (nullptr != _internal->_widthItem.get())) + { + QPointF scenePos(pixel2ScenePosition(x, y)); + _internal->_widthItem->setSelection( _internal->_widthItem->isPositionInside(scenePos) ); + } + return RubberbandMultiPointFeature::moveAction( causer, x, y); + + } /* RubberbandPolylineWidth::moveAction */ + + + void RubberbandPolylineWidth::update() + { + if ((_internal->_widthItem.get() == nullptr) && (Rubberband::_internal->_handleList.size() > 1)) + { + RubberbandPointHandle * pointHandle1Ptr = static_cast(Rubberband::_internal->_handleList[0].get()); + RubberbandPointHandle * pointHandle2Ptr = static_cast(Rubberband::_internal->_handleList[1].get()); + Esri::ArcGISRuntime::Point p1 = pointHandle1Ptr->getCoordinate()._mapCoordinate; + Esri::ArcGISRuntime::Point p2 = pointHandle2Ptr->getCoordinate()._mapCoordinate; + + buildWidthItem(QPointF(p1.x(), p1.y()), QPointF(p2.x(), p2.y())); + } + + if (_internal->_widthItem) + { + Rubberband::_internal->_handleList += static_cast>(_internal->_widthItem); + RubberbandMultiPointFeature::update(); + Rubberband::_internal->_handleList.removeLast(); + } + else RubberbandMultiPointFeature::update(); + + } /* RubberbandPolylineWidth::update */ + + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_polyline_width.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_polyline_width.h new file mode 100755 index 000000000..759068155 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_polyline_width.h @@ -0,0 +1,88 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define multi point feature geometry with an additional width coordinate +** $Id$ +** $HeadURL$ +** $Author$ +** +** ----------------------------------------------------------------------------------------------- +** +** Hardware-Environment: Intel-PC +** Software-Environment: Linux, Windows 7, Qt 5.7. ArcGISRuntimeQt 100 +** +** Language ........: C++ 11 +** +** ----------------------------------------------------------------------------------------------- +** +** Author ...........: Jörg Turla, SPD G, Tel. 3132 +** +** ----------------------------------------------------------------------------------------------- +** +** Notes .........: +** +** 7.12.2016, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband_multi_point_feature.h" + + +namespace SituationMap { namespace RB +{ + + class RubberbandLineHandle; + class RubberbandFrameHandle; + + /** + @class RubberbandPolylineWidth rubberband_smooth_line.h + @brief Implementation of a multi point feature rubberband with rounded edges. + @see RubberbandFactory, Rubberband, RubberbandMultiPointFeature + + */ + class SITUATIONMAP_EXPORT RubberbandPolylineWidth : public RubberbandMultiPointFeature + { + Q_OBJECT + /** + @name Construction & Destruction + @{ + */ + + friend class RubberbandFactory; + + explicit RubberbandPolylineWidth( SituationMap::Map * map, QObject * parent = nullptr ); + + public: + + virtual ~RubberbandPolylineWidth(); + /** @} */ + + virtual QVector coordinateVector( CoordinateSystem cs = CoordinateSystem::Geo ) const override; + virtual QPointF singleCoordinate( unsigned int idx = 0, CoordinateSystem cs = CoordinateSystem::Geo ) const override; + + virtual void setCoordinateVector( const QVector &, CoordinateSystem cs = CoordinateSystem::Geo ) override; + + bool selectAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool setCoordinateAction( AbstractEventDispatcher * causer, int x, int y ) override; + bool movePickedAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool moveAction ( AbstractEventDispatcher * causer, int x, int y ) override; + + protected: + /* + Forward declaration of InternalData to hold private member variables. Becareful the base class Rubberband + has similary a class for internal data member with name "InternalData". Don't be confused it is not the same. + */ + struct InternalData; + + InternalData * _internal; + + void update() override; + + void buildWidthItem(const QPointF & mapPos1, const QPointF & mapPos2) const; + void setUpWidthItem(const QPointF & mapPos) const; + }; /* RubberbandSmoothLine */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_rectangle.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_rectangle.cpp new file mode 100755 index 000000000..b256868b2 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_rectangle.cpp @@ -0,0 +1,1913 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of rubberband class to define point feature geoemtry +** $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 .........: +** +** 17.06.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_rectangle.h" +#include "rubberband_point_handle.h" +#include "rubberband_frame_handle.h" +#include "rubberband_drawable.h" +#include "drawable_painter_path.h" +#include "rubberband_handle_proxy.h" + +#include "rubberband_internal.h" +#include "rubberband_base_internal.h" + +#include +#include + +#include +#include + +#include + + +namespace SituationMap { namespace RB +{ + + static bool epsilonEqual( qreal x1, qreal x2, qreal thresholdValue = 0.001 ) + { + return (fabs(x1-x2) < thresholdValue); + } /* epsilonEqual */ + + static qreal distance(const QPointF & p0, const QPointF & p1) + { + qreal dx = p1.x() - p0.x(); + qreal dy = p1.y() - p0.y(); + return sqrt(dx*dx+dy*dy); + } /* distance */ + + + struct RubberbandRectangle::InternalData + { + Q_DISABLE_COPY(InternalData) + + const int _initialRectPixelSize = 100; + + RubberbandRectangle * _me; + + bool _constrainQuadratic; + bool _constrainOrthogonal; + + qreal _minWidth; + qreal _maxWidth; + qreal _minHeight; + qreal _maxHeight; + + qreal _rectWidth; // referred to map coordinates + qreal _rectHeight; // referred to map coordinate + QPointF _center; // center refferd to map coordinates + + QPointF _oVec; // normalized orientation vector according to scene coordinate system + QPointF _screenCenter; // center of screen in scene coordinates + QPointF _mapOVec; // map rotation as normalized orientation vector + + qreal _rectWidthScene; // rectangle width in scene coordinates + qreal _rectHeightScene; // rectangle height in scene coordinates + QPointF _centerScene; // rectangle center in scene coordinates + + QPointF _constructionStart; + + unsigned int _rotationHandleSize; + bool _useDottedLineHandle; + + SituationMap::RefPointer _tmpConstruction; + + SituationMap::RefPointer _frameHandle; + SituationMap::RefPointer _pointHandle[4]; + SituationMap::RefPointer _lineHandle[4]; + SituationMap::RefPointer _rotateHandle; + + QVector _pointIndexVector; + + LOGGER_T _logger; + + ModificationMode _modificationMode; + MathUtils::Matrix2D & _lastCoordinateChange; + + InternalData( MathUtils::Matrix2D & lastCoordinateChange, RubberbandRectangle * me ) + : _me(me) + , _constrainQuadratic(false) + , _constrainOrthogonal(false) + , _minWidth(0.0) + , _maxWidth(0.0) + , _minHeight(0.0) + , _maxHeight(0.0) + , _rectWidth(0.0) + , _rectHeight(0.0) + , _center() + , _oVec(1,0) + , _screenCenter(0,0) + , _mapOVec(1,0) + , _rectWidthScene(0.0) + , _rectHeightScene(0.0) + , _centerScene() + , _constructionStart() + , _rotationHandleSize(0) + , _useDottedLineHandle(false) + , _tmpConstruction() + , _frameHandle() + , _pointHandle() + , _lineHandle() + , _rotateHandle() + , _pointIndexVector() + , _logger(getLogger("RubberbandRectangle")) + , _modificationMode( ModificationMode::NoneRestriction ) + , _lastCoordinateChange(lastCoordinateChange) + { + } + + inline static QPointF rotateVertex( const QPointF & center, qreal x, qreal y, qreal sinA, qreal cosA ) + { + return QPointF(x*cosA-y*sinA,y*cosA+x*sinA) + center; + } // rotateVertex + + + void setScreenCenter( Esri::ArcGISRuntime::MapGraphicsView * mapView ); + + void setRotatedFrame( Esri::ArcGISRuntime::MapGraphicsView * mapView ); + + void updateFrame( Esri::ArcGISRuntime::MapGraphicsView * mapView, QVector & points ); + + void updateFrame( Esri::ArcGISRuntime::MapGraphicsView * mapView, const QVector & coordinateList ); + + + inline QPointF getSceneOffsetRotationHandle(qreal margin) + { + qreal sinPhi = _oVec.y(); // * _mapOVec.x() + _oVec.x() * _mapOVec.y(); // Addition-Theorem + qreal cosPhi = _oVec.x(); // * _mapOVec.x() - _oVec.y() * _mapOVec.y(); + + return rotateVertex( QPointF(0,0), 0.0,-(margin+2*_rotationHandleSize), sinPhi, cosPhi ); + } /* getSceneOffsetRotationHandle */ + + + inline QPointF getScenePositionRotationHandle() + { + if ((_pointHandle[2]) && (_pointHandle[3])) + { + return (_pointHandle[2]->getCoordinate()._sceneCoordinate + _pointHandle[3]->getCoordinate()._sceneCoordinate) * 0.5; + } + + QPointF p( 0.0, -_rectHeightScene*0.5); + return rotateVertex(_centerScene, p.x(), p.y(), _oVec.y(), _oVec.x() ); + + } /* getScenePositionRotationHandle */ + + + inline void verifyConstrains() + { + if (_constrainQuadratic) + { + if ((_minWidth > 0) || (_minHeight > 0)) + { + if (_minWidth < _minHeight) _minWidth = _minHeight; + else _minHeight = _minWidth; + } + + if ((_maxWidth > 0) || (_maxHeight > 0)) + { + if (_maxWidth < _maxHeight) _maxHeight = _maxWidth; + else _maxWidth = _maxHeight; + } + } + } /* verifyConstrains */ + + + inline void setInitialRectSize(RubberbandRectangle * rubBnd, const QPointF & scenePos) + { + const QPoint & pixelPos = rubBnd->Rubberband::_internal->_mapView -> mapFromScene(scenePos); + setInitialRectSize( rubBnd, scenePos, pixelPos.x(), pixelPos.y() ); + } /* setInitialRectSize */ + + + + + inline void setInitialRectSize( RubberbandRectangle * rubBnd, const QPointF & scenePos, qreal pixelPosX, qreal pixelPosY ) + { + verifyConstrains(); + if ((_rectWidthScene <= 0.0) && (_rectHeightScene <= 0.0)) + { + QPointF rectSize(rubBnd->pixel2ScenePosition(pixelPosX+_initialRectPixelSize,pixelPosY+_initialRectPixelSize)); + + _rectWidthScene = fabs(rectSize.x() - scenePos.x()); + _rectHeightScene = fabs(rectSize.y() - scenePos.y()); + setInitialRectSize( rubBnd ); + } + } /* setInitialRectSize */ + + + inline void setInitialRectSize(RubberbandRectangle * rubBnd, qreal sceneWidth, qreal sceneHeight) + { + verifyConstrains(); + if ((_rectWidthScene <= 0.0) && (_rectHeightScene <= 0.0)) + { + _rectWidthScene = sceneWidth; + _rectHeightScene = sceneHeight; + setInitialRectSize( rubBnd ); + } + } /* setInitialRectSize */ + + + bool minimumSizeReached( int pointHandleIdx = -1, int lineHandleIdx = -1, const QPointF & loc = QPointF() ) + { + qreal min = 10; + bool result = false; + bool isPointHandle = ((pointHandleIdx >= 0) && (pointHandleIdx < 4)); + bool isLineHandle = ((lineHandleIdx >= 0) && (lineHandleIdx < 4)); + + if (isPointHandle ^ isLineHandle) + { + qreal h = loc.y() - _centerScene.y(); + qreal w = loc.x() - _centerScene.x(); + + + if (lineHandleIdx >=0) + { + switch (lineHandleIdx) + { + case 0 : if (h > min) result = true; break; + case 1 : if (w > min) result = true; break; + case 2 : if (h < -min) result = true; break; + case 3 : if (w < -min) result = true; break; + } + } + + if (isPointHandle) + { + switch (pointHandleIdx) + { + case 0 : if ((w < -min) && (h > min)) result = true; break; + case 1 : if ((w > min) && (h > min)) result = true; break; + case 2 : if ((w > min) && (h < -min)) result = true; break; + case 3 : if ((w < -min) && (h < -min)) result = true; break; + } + } + return result; + } + else + { + for (const auto &ph : _pointHandle) if (nullptr == ph.get()) return false; + + min -= 1; + if (QLineF( _pointHandle[0].get()->getCoordinate()._sceneCoordinate, _pointHandle[1].get()->getCoordinate()._sceneCoordinate ).length() < min) result = true; + if (QLineF( _pointHandle[0].get()->getCoordinate()._sceneCoordinate, _pointHandle[2].get()->getCoordinate()._sceneCoordinate ).length() < min) result = true; + if (QLineF( _pointHandle[0].get()->getCoordinate()._sceneCoordinate, _pointHandle[3].get()->getCoordinate()._sceneCoordinate ).length() < min) result = true; + if (QLineF( _pointHandle[1].get()->getCoordinate()._sceneCoordinate, _pointHandle[2].get()->getCoordinate()._sceneCoordinate ).length() < min) result = true; + if (QLineF( _pointHandle[2].get()->getCoordinate()._sceneCoordinate, _pointHandle[3].get()->getCoordinate()._sceneCoordinate ).length() < min) result = true; + } + return result; + } // minimumSizeReached + + private: + inline void setInitialRectSize( RubberbandRectangle * rubBnd ) + { + auto length = [](qreal x, qreal y) -> qreal { return sqrt(x*x+y*y); }; + + RubberbandHandle::RubberbandCoordinate mapPointCenter( rubBnd->Rubberband::_internal->_mapView, _centerScene ); + + const Esri::ArcGISRuntime::Point & mapCenter = mapPointCenter._mapCoordinate; + + const Esri::ArcGISRuntime::Point mapMaxW( mapCenter.x() + _maxWidth + , mapCenter.y() + , rubBnd->Rubberband::_internal->_mapView->map()->spatialReference() ); + + const Esri::ArcGISRuntime::Point mapMinW( mapCenter.x() + _minWidth + , mapCenter.y() + , rubBnd->Rubberband::_internal->_mapView->map()->spatialReference() ); + + const Esri::ArcGISRuntime::Point mapMaxH( mapCenter.x() + , mapCenter.y() + _maxHeight + , rubBnd->Rubberband::_internal->_mapView->map()->spatialReference() ); + + const Esri::ArcGISRuntime::Point mapMinH( mapCenter.x() + , mapCenter.y() + _minHeight + , rubBnd->Rubberband::_internal->_mapView->map()->spatialReference() ); + + RubberbandHandle::RubberbandCoordinate mapPointMaxW( rubBnd->Rubberband::_internal->_mapView, mapMaxW ); + RubberbandHandle::RubberbandCoordinate mapPointMinW( rubBnd->Rubberband::_internal->_mapView, mapMinW ); + RubberbandHandle::RubberbandCoordinate mapPointMaxH( rubBnd->Rubberband::_internal->_mapView, mapMaxH ); + RubberbandHandle::RubberbandCoordinate mapPointMinH( rubBnd->Rubberband::_internal->_mapView, mapMinH ); + + qreal maxSceneWidth = length(mapPointMaxW._sceneCoordinate.x()-_centerScene.x(), mapPointMaxW._sceneCoordinate.y()-_centerScene.y()); + qreal minSceneWidth = length(mapPointMinW._sceneCoordinate.x()-_centerScene.x(), mapPointMinW._sceneCoordinate.y()-_centerScene.y()); + qreal maxSceneHeight = length(mapPointMaxH._sceneCoordinate.x()-_centerScene.x(), mapPointMaxH._sceneCoordinate.y()-_centerScene.y()); + qreal minSceneHeight = length(mapPointMinH._sceneCoordinate.x()-_centerScene.x(), mapPointMinH._sceneCoordinate.y()-_centerScene.y()); + + if ((_maxWidth > 0) && (_rectWidthScene > maxSceneWidth )) _rectWidthScene = maxSceneWidth; + if ((_minWidth > 0) && (_rectWidthScene < minSceneWidth )) _rectWidthScene = minSceneWidth; + if ((_maxHeight > 0) && (_rectHeightScene > maxSceneHeight)) _rectHeightScene = maxSceneHeight; + if ((_minHeight > 0) && (_rectHeightScene < minSceneHeight)) _rectHeightScene = minSceneHeight; + + RubberbandHandle::RubberbandCoordinate mapRectWidth ( rubBnd->Rubberband::_internal->_mapView, _centerScene + QPointF( _rectWidthScene, 0.0 )); + RubberbandHandle::RubberbandCoordinate mapRectHeight( rubBnd->Rubberband::_internal->_mapView, _centerScene + QPointF( 0.0 , _rectHeightScene )); + + _rectWidth = length( mapRectWidth._mapCoordinate.x()-mapCenter.x(), mapRectWidth._mapCoordinate.y()-mapCenter.y() ); + _rectHeight = length( mapRectHeight._mapCoordinate.x()-mapCenter.x(), mapRectHeight._mapCoordinate.y()-mapCenter.y() ); + _center = QPointF(mapCenter.x(),mapCenter.y()); + + rubBnd->_rectangleIsValid = true; + } /* setInitialRectSize */ + + }; /* InternalData */ + + + void RubberbandRectangle::InternalData::setScreenCenter( Esri::ArcGISRuntime::MapGraphicsView * mapView ) + { + qreal mapRotatePhi = mapView->mapRotation() * MathConst::pi / 180.0; + + _mapOVec = QPointF( cos(mapRotatePhi), sin(mapRotatePhi) ); + _screenCenter = QPointF( mapView->viewport()->size().width()* 0.5, mapView->viewport()->size().height()* 0.5 ); + + } /* RubberbandRectangle::InternalData::setScreenCenter */ + + + void RubberbandRectangle::InternalData::setRotatedFrame( Esri::ArcGISRuntime::MapGraphicsView* ) + { + // _center and unrotated _rectWidth and _rectHeight is already set from setInitialRectSize() which has been executed before + + qreal sinA = -_oVec.y(); // be carefull the scene coordinate system is contra clockwise -- sinA must be negated + qreal cosA = _oVec.x(); + qreal w = _rectWidth * 0.5; + qreal h = _rectHeight * 0.5; + + QPointF p0 = MathUtils::rotate( _center + QPointF(-w,-h ), _center, sinA, cosA ); + QPointF p1 = MathUtils::rotate( _center + QPointF( w,-h ), _center, sinA, cosA ); + QPointF p2 = MathUtils::rotate( _center + QPointF( w, h ), _center, sinA, cosA ); + + _rectWidth = distance(p1,p0); + _rectHeight = distance(p2,p1); + + } /* RubberbandRectangle::InternalData::setRotatedFrame */ + + + void RubberbandRectangle::InternalData::updateFrame( Esri::ArcGISRuntime::MapGraphicsView * mapView, QVector & points ) + { + DEBUG_LOG(_logger) << LOGFUNCTION << " RubberbandRectangle::InternalData::updateFrame for map-coordinates " << _oVec.x() << " ; " << _oVec.y(); + + qreal cosA = _oVec.x(); + qreal sinA = _oVec.y(); // be carefull the scene coordinate system is contra clockwise -- sinA must be negated + + qreal xmin = std::numeric_limits::infinity(); + qreal xmax = - std::numeric_limits::infinity(); + qreal ymin = std::numeric_limits::infinity(); + qreal ymax = - std::numeric_limits::infinity(); + + for (unsigned int i = 0; i < 4; ++i) + { + const QPointF & p = MathUtils::rotate( points[i], points[0], sinA, cosA ); // rotate back to orthogonal position + const qreal & x = p.x(); + const qreal & y = p.y(); + + if (x < xmin) xmin = x; + if (x > xmax) xmax = x; + if (y < ymin) ymin = y; + if (y > ymax) ymax = y; + + DEBUG_LOG(_logger) << LOGFUNCTION << " point " << i << " : " << p.x() << " ; " << p.y(); + } + + // set center, keep in mind that the center point must be rotated to it's real position + _center = MathUtils::rotate( QPointF((xmin + xmax)*0.5, (ymin + ymax)*0.5), points[0], -sinA, cosA ); + + if (_constrainQuadratic) + { + double w = xmax-xmin; + double h = ymax-ymin; + + if (w > h) w = h; + if (h > w) h = w; + + if ((_modificationMode != ModificationMode::RotationOnly) && (_modificationMode != ModificationMode::TranslationOnly)) + { + // size of rectangle + _rectWidth = w; w *= 0.5; + _rectHeight = h; h *= 0.5; + } + + points[0] = MathUtils::rotate( _center + QPointF(-w,-h), _center, -sinA, cosA ); + points[1] = MathUtils::rotate( _center + QPointF( w,-h), _center, -sinA, cosA ); + points[2] = MathUtils::rotate( _center + QPointF( w, h), _center, -sinA, cosA ); + points[3] = MathUtils::rotate( _center + QPointF(-w, h), _center, -sinA, cosA ); + } + else + { + // size of rectangle + _rectWidth = xmax-xmin; + _rectHeight = ymax-ymin; + } + + QVector sceneCoordinates; + sceneCoordinates.reserve(4); + + for (unsigned int i = 0; i < 4; ++i) + { + const QPointF & p = MathUtils::rotate( points[i], points[0], sinA, cosA ); // rotate back to orthogonal position, keep in mind points are map coordinates + const qreal & x = p.x(); + const qreal & y = p.y(); + RubberbandHandle::RubberbandCoordinate point( mapView, Esri::ArcGISRuntime::Point( x, y, mapView->map()->spatialReference() )); + sceneCoordinates.append(point._sceneCoordinate ); + } + + RubberbandHandle::RubberbandCoordinate point( mapView, Esri::ArcGISRuntime::Point( _center.x(), _center.y(), mapView->map()->spatialReference() )); + + _centerScene = point._sceneCoordinate; + + xmin = std::numeric_limits::infinity(); + xmax = - std::numeric_limits::infinity(); + ymin = std::numeric_limits::infinity(); + ymax = - std::numeric_limits::infinity(); + + for (const QPointF & p : std::as_const(sceneCoordinates)) + { + const QPointF & unRotMap = MathUtils::rotate( p, _screenCenter, -_mapOVec.y(), _mapOVec.x() ); + const qreal & x = unRotMap.x(); + const qreal & y = unRotMap.y(); + + if (x < xmin) xmin = x; + if (x > xmax) xmax = x; + if (y < ymin) ymin = y; + if (y > ymax) ymax = y; + } + + + // size of scene rectangle + _rectWidthScene = xmax-xmin; + _rectHeightScene = ymax-ymin; + + + DEBUG_LOG(_logger) << LOGFUNCTION << " center = " << _centerScene.x() << " ; " << _centerScene.y() + << " _rectWidthScene = " << _rectWidthScene + << " _rectHeightScene = " << _rectHeightScene; + + } /* RubberbandRectangle::InternalData::updateFrame */ + + + void RubberbandRectangle::InternalData::updateFrame( Esri::ArcGISRuntime::MapGraphicsView*, const QVector & coordinateList ) + { + if (coordinateList.isEmpty()) return; + + DEBUG_LOG(_logger) << LOGFUNCTION << " RubberbandRectangle::InternalData::updateFrame for scene-coordinates " << _oVec.x() << " ; " << _oVec.y(); + + qreal cosA = _oVec.x(); + qreal sinA = _oVec.y(); + + qreal xmin = std::numeric_limits::infinity(); + qreal xmax = - std::numeric_limits::infinity(); + qreal ymin = std::numeric_limits::infinity(); + qreal ymax = - std::numeric_limits::infinity(); + + QVector sceneCoordinates; + sceneCoordinates.reserve(4); + + for (unsigned int i = 0; i < 4; ++i) + { + QPointF p = MathUtils::rotate( coordinateList[i]._sceneCoordinate, coordinateList[0]._sceneCoordinate, -sinA, cosA ); // rotate back to orthogonal position + + sceneCoordinates.append( p ); + + const QPointF & unRotMap = MathUtils::rotate( p, _screenCenter, -_mapOVec.y(), _mapOVec.x() ); + const qreal & x = unRotMap.x(); + const qreal & y = unRotMap.y(); + + if (x < xmin) xmin = x; + if (x > xmax) xmax = x; + if (y < ymin) ymin = y; + if (y > ymax) ymax = y; + } + + // set center, keep in mind that the center point must be rotated to it's real position + const QPointF & rotMap = MathUtils::rotate( QPointF((xmin + xmax)*0.5, (ymin + ymax)*0.5), _screenCenter, _mapOVec.y(), _mapOVec.x() ); + + _centerScene = MathUtils::rotate( rotMap, coordinateList[0]._sceneCoordinate, sinA, cosA ); + + // size of scene rectangle + _rectWidthScene = xmax - xmin; + _rectHeightScene = ymax - ymin; + + xmin = std::numeric_limits::infinity(); + xmax = -std::numeric_limits::infinity(); + ymin = std::numeric_limits::infinity(); + ymax = -std::numeric_limits::infinity(); + + QPointF p0(coordinateList[0]._mapCoordinate.x(), coordinateList[0]._mapCoordinate.y()); + + for (unsigned int i = 0; i < 4; ++i) + { + QPointF pi(coordinateList[i]._mapCoordinate.x(), coordinateList[i]._mapCoordinate.y()); + + QPointF p = MathUtils::rotate(pi, p0, sinA, cosA); // rotate back to orthogonal position + + const qreal & x = p.x(); + const qreal & y = p.y(); + + if (x < xmin) xmin = x; + if (x > xmax) xmax = x; + if (y < ymin) ymin = y; + if (y > ymax) ymax = y; + } + + qreal sRectWidth = _rectWidth; + qreal sRectHeight = _rectHeight; + + // size of rectangle + _rectWidth = xmax - xmin; + _rectHeight = ymax - ymin; + + QPointF lastCenter = _center; + + _center = MathUtils::rotate(QPointF((xmin + xmax)*0.5, (ymin + ymax)*0.5), p0, -sinA, cosA); + + DEBUG_LOG(_logger) << LOGFUNCTION << " centerScene = " << _centerScene.x() << " ; " << _centerScene.y() << " _rectWidthScene = " << _rectWidthScene << " _rectHeightScene = " << _rectHeightScene; + DEBUG_LOG(_logger) << LOGFUNCTION << " center = " << _center.x() << " ; " << _center.y() << " _rectWidth = " << _rectWidth << " _rectHeight = " << _rectHeight ; + + sRectWidth = _rectWidth / sRectWidth; + sRectHeight = _rectHeight / sRectHeight; + + if (!_lastCoordinateChange.isRotation()) // _lastCoordinateChange.isIdentity()) + { + // move center to origin + _lastCoordinateChange.putTranslation( -lastCenter ); + + // do scaling + _lastCoordinateChange.putScale( QPointF(sRectWidth,sRectHeight), QPointF(cosA,sinA) ); + + // move back to new center + _lastCoordinateChange.putTranslation( _center ); + } + + } /* RubberbandRectangle::InternalData::updateFrame */ + + + RubberbandRectangle::RubberbandRectangle(SituationMap::Map * map, QObject * parent) + : Rubberband(map,parent) + , _internal(new InternalData(_lastCoordinateChange,this)) + , _rectangleIsValid(false) + , _lastCoordinateChange() + { + } /* RubberbandRectangle::RubberbandRectangle */ + + + RubberbandRectangle::~RubberbandRectangle() + { + delete _internal; + } /* RubberbandRectangle::~RubberbandRectangle */ + + + void RubberbandRectangle::setModificationMode( ModificationMode mode ) { _internal->_modificationMode = mode; } + + + bool RubberbandRectangle::rectangleIsValid() const { return _rectangleIsValid; } + + + void RubberbandRectangle::minWidth(qreal x) { _internal->_minWidth = (x > 0) ? x : 0.0; } + void RubberbandRectangle::maxWidth(qreal x) { _internal->_maxWidth = (x > 0) ? x : 0.0; } + void RubberbandRectangle::minHeight(qreal x) { _internal->_minHeight = (x > 0) ? x : 0.0; } + void RubberbandRectangle::maxHeight(qreal x) { _internal->_maxHeight = (x > 0) ? x : 0.0; } + + + QPointF RubberbandRectangle::getSceneCenter() const { return _internal->_centerScene; } + qreal RubberbandRectangle::getSceneWidth() const { return _internal->_rectWidthScene; } + qreal RubberbandRectangle::getSceneHeight() const { return _internal->_rectHeightScene; } + + QPointF RubberbandRectangle::getCenter() const { return _internal->_center; } + qreal RubberbandRectangle::getWidth() const { return _internal->_rectWidth; } + qreal RubberbandRectangle::getHeight() const { return _internal->_rectHeight; } + + void RubberbandRectangle::getSceneRotation( qreal & sinA, qreal & cosA ) const { sinA = -_internal->_oVec.y() ; cosA = _internal->_oVec.x(); } + void RubberbandRectangle::getMapRotation ( qreal & sinA, qreal & cosA ) const { sinA = -_internal->_mapOVec.y(); cosA = _internal->_mapOVec.x(); } + + void RubberbandRectangle::useDottedLineHandles(bool on) + { + _internal->_useDottedLineHandle = on; + } /* RubberbandRectangle::useDottedLineHandles */ + + + void RubberbandRectangle::setButtonPosition() + { + QPointF buttonPos(0, 0); + + if (Rubberband::_internal->_handleList.size() == 4) + { + for (unsigned int i = 0; i < 4; ++i) + { + if (Rubberband::_internal->_handleList[i].valid()) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList[i].get()); + const QPointF & p = handlePointPtr->getCoordinate()._sceneCoordinate; + + if (i > 0) + { + if ((p.x() == buttonPos.x()) && (p.y() >= buttonPos.y())) + { + buttonPos = p; + } + else if (p.x() > buttonPos.x()) buttonPos = p; + } + else buttonPos = p; + } + } + } + else buttonPos = InternalData::rotateVertex( _internal->_centerScene, _internal->_rectWidthScene*0.5, _internal->_rectHeightScene*0.5, _internal->_oVec.y(), fabs(_internal->_oVec.x()) ); + + _baseInternal->_buttonProxy->setPos( QPointF(buttonPos.x(),buttonPos.y()) ); + } /* RubberbandRectangle::setButtonPosition */ + + + void RubberbandRectangle::activateButtons() + { + if (isEnabledModificationState()) + { + _baseInternal->_hasUndoButton = false; + _baseInternal->_applyIsPossible = true; + _baseInternal->_undoIsPossible = false; + + Rubberband::activateButtons(); + _baseInternal->_buttonProxy->setRotateSceneOffset(false); + setButtonPosition(); + _baseInternal->_buttonProxy->show(); + } + else Rubberband::deactivateButtons(); + } /* RubberbandRectangle::activateButtons */ + + + +#include "./Rubberbands/winkel_icon.xpm" + + void RubberbandRectangle::constructHandles() + { + _internal->setScreenCenter( Rubberband::_internal->_mapView ); + _internal->verifyConstrains(); + + // remove existing handles + Rubberband::_internal->_handleList.clear(); + + if ((_internal->_modificationMode != ModificationMode::RotationOnly) && (_internal->_modificationMode != ModificationMode::TranslationOnly)) + { + qreal w2 = _internal->_rectWidth * 0.5; + qreal h2 = _internal->_rectHeight * 0.5; + qreal sinA = _internal->_oVec.y(); + qreal cosA = _internal->_oVec.x(); + + QPointF p0 = InternalData::rotateVertex( _internal->_center, -w2, -h2, -sinA, cosA ); + QPointF p1 = InternalData::rotateVertex( _internal->_center, w2, -h2, -sinA, cosA ); + QPointF p2 = InternalData::rotateVertex( _internal->_center, w2, h2, -sinA, cosA ); + QPointF p3 = InternalData::rotateVertex( _internal->_center, -w2, h2, -sinA, cosA ); + + RubberbandHandle::RubberbandCoordinate mapP0( Rubberband::_internal->_mapView, Esri::ArcGISRuntime::Point( p0.x(), p0.y(), Rubberband::_internal->_mapView->map()->spatialReference() )); + RubberbandHandle::RubberbandCoordinate mapP1( Rubberband::_internal->_mapView, Esri::ArcGISRuntime::Point( p1.x(), p1.y(), Rubberband::_internal->_mapView->map()->spatialReference() )); + RubberbandHandle::RubberbandCoordinate mapP2( Rubberband::_internal->_mapView, Esri::ArcGISRuntime::Point( p2.x(), p2.y(), Rubberband::_internal->_mapView->map()->spatialReference() )); + RubberbandHandle::RubberbandCoordinate mapP3( Rubberband::_internal->_mapView, Esri::ArcGISRuntime::Point( p3.x(), p3.y(), Rubberband::_internal->_mapView->map()->spatialReference() )); + + _internal->_lineHandle[0] = new RubberbandLineHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP0._sceneCoordinate, mapP1._sceneCoordinate, _lineHandleWidth, _internal->_useDottedLineHandle ); + _internal->_lineHandle[1] = new RubberbandLineHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP1._sceneCoordinate, mapP2._sceneCoordinate, _lineHandleWidth, _internal->_useDottedLineHandle ); + _internal->_lineHandle[2] = new RubberbandLineHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP2._sceneCoordinate, mapP3._sceneCoordinate, _lineHandleWidth, _internal->_useDottedLineHandle ); + _internal->_lineHandle[3] = new RubberbandLineHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP3._sceneCoordinate, mapP0._sceneCoordinate, _lineHandleWidth, _internal->_useDottedLineHandle ); + + _internal->_pointHandle[0] = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP0._mapCoordinate ); + _internal->_pointHandle[1] = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP1._mapCoordinate ); + _internal->_pointHandle[2] = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP2._mapCoordinate ); + _internal->_pointHandle[3] = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapP3._mapCoordinate ); + + for (auto ph : _internal->_pointHandle) + { + Rubberband::_internal->_handleList.append( SituationMap::RefPointer(ph.get()) ); + + ph -> setDrawColor(_handleDrawColor); + ph -> setFillColor( _handleFillColor ); + ph -> setSelectionColor(_handleSelectionColor); + } + + for (auto lh : _internal->_lineHandle) + { + lh -> setDrawColor(_handleDrawColor); + lh -> setFillColor( _handleFillColor ); + lh -> setSelectionColor(_handleSelectionColor); + } + } + + + if ((_internal->_modificationMode == ModificationMode::NoneRestriction) || (_internal->_modificationMode == ModificationMode::RotationOnly)) + { + static QPixmap * icon = nullptr; + + if (icon == nullptr) + { + icon = new QPixmap( QPixmap( winkelIcon_xpm ).transformed(QTransform::fromScale(0.5,0.5)) ); + } + + if (!_internal->_constrainOrthogonal) + { + _internal->_rotationHandleSize = std::max( icon->width(), icon->height() ) * 0.5; + + _internal->_rotateHandle = new RubberbandPointHandle( Rubberband::_internal->_mapView + , Rubberband::_internal->_mapScene + , _internal->getScenePositionRotationHandle(), *icon ); + + _internal->_rotateHandle -> setSceneOffset( _internal->getSceneOffsetRotationHandle( (_frameHandleMargin > 0) ? _frameHandleMargin : 0) ); + _internal->_rotateHandle -> setDrawColor(_handleDrawColor); + _internal->_rotateHandle -> setFillColor( _handleFillColor ); + _internal->_rotateHandle -> setSelectionColor(_handleSelectionColor ); + } + } + + _internal->_frameHandle = new RubberbandFrameHandle( Rubberband::_internal->_mapView + , Rubberband::_internal->_mapScene + , _frameHandleMargin ); + + + _internal->_frameHandle -> setDrawColor( _frameColor ); + _internal->_frameHandle -> setFillColor( _frameFillColor ); + _internal->_frameHandle -> setSelectionColor(_frameSelectionColor); + _internal->_frameHandle -> setSelectable((_internal->_modificationMode != ModificationMode::ScalingOnly) && (_internal->_modificationMode != ModificationMode::RotationOnly)); + _internal->_frameHandle -> drawDisabled( true ); + + // lambda slot to update screen center and map rotation + connect( _internal->_frameHandle.get(), &RubberbandFrameHandle::adjusted, this, [this]() + { + this->_internal->setScreenCenter( this->Rubberband::_internal->_mapView ); + this->setButtonPosition(); + } ); + + activateButtons(); + + } /* RubberbandRectangle::constructHandles */ + + + QVector RubberbandRectangle::coordinateVector( CoordinateSystem cs ) const { return rectangleVerticies(cs); } + QPointF RubberbandRectangle::singleCoordinate(unsigned int, CoordinateSystem cs ) const { return centerPoint(cs); } + + + void RubberbandRectangle::setCoordinateVector( const QVector & coordinateVector, CoordinateSystem cs ) + { + if (coordinateVector.size() == 4) + { + rectangleVerticies( coordinateVector, cs ); + } + } /* RubberbandRectangle::setCoordinateVector */ + + + QPointF RubberbandRectangle::centerPoint( CoordinateSystem cs ) const + { + return projectCoordinateFromMap( cs, _internal->_center ); + } /* RubberbandRectangle::centerPoint */ + + + void RubberbandRectangle::centerPoint(const QPointF ¢erPoint, CoordinateSystem cs ) + { + _internal->_center = projectCoordinateToMap( cs, centerPoint ); + + RubberbandHandle::RubberbandCoordinate point( Rubberband::_internal->_mapView, + Esri::ArcGISRuntime::Point( _internal->_center.x(), + _internal->_center.y(), + Rubberband::_internal->_mapView->map()->spatialReference() )); + _internal->_centerScene = point._sceneCoordinate; + + _internal->setInitialRectSize( this, _internal->_centerScene ); + + _internal->setRotatedFrame( Rubberband::_internal->_mapView ); + + disenableConstructionState(); + constructHandles(); + activateButtons(); + update(); + } /* RubberbandRectangle::centerPoint */ + + + + void RubberbandRectangle::rectangleParameter( QPointF & center, qreal & width, qreal & height, QPointF & direction, CoordinateSystem cs ) + { + center = projectCoordinateFromMap(cs,_internal->_center); + width = _internal->_rectWidth; + height = _internal->_rectHeight; + direction = QPointF(_internal->_oVec.x(),-_internal->_oVec.y()); + + // projectCoordinateFromMap( CoordinateSystem::Geo, center ); + } /* RubberbandRectangle::rectangleParameter */ + + + QVector RubberbandRectangle::rectangleVerticies( CoordinateSystem cs ) const + { + QVector points(4); + + for ( unsigned int i = 0; i < 4; ++i ) + { + // they may be emtpy (e.g. select action, move mouse but don't release button, hit 'ENTER' + if (!_internal->_pointHandle[i]) + return {}; + const Esri::ArcGISRuntime::Point & p = _internal->_pointHandle[i]-> getCoordinate()._mapCoordinate; + + if (_internal->_pointIndexVector.size() == 4) + points[_internal->_pointIndexVector[i]] = projectCoordinateFromMap( cs,p); + else + points[i] = projectCoordinateFromMap( cs,p); + } + return points; + } /* RubberbandRectangle::rectangleVerticies */ + + + void RubberbandRectangle::rectangleVerticies( const QVector & coordinateVector, CoordinateSystem cs ) + { + if (coordinateVector.size() == 4) + { + QVector rectPoints; + + // first do coordinate projection to map coordinate system + rectPoints.reserve(4); + for (auto loc : coordinateVector) + { + rectPoints.append( projectCoordinateToMap( cs, loc ) ); + } + + // here first sort the verticies + _internal->_pointIndexVector.clear(); + for (unsigned int i = 0; i< 4;++i) _internal->_pointIndexVector.append(i); + + // here first sort the verticies + + for (unsigned i = 0; i < 4; ++i) + { + for (unsigned k = i + 1; k < 4; ++k) + { + if (rectPoints[k].y() < rectPoints[i].y()) + { + std::swap(rectPoints[i], rectPoints[k]); + std::swap(_internal->_pointIndexVector[i],_internal->_pointIndexVector[k]); + } + } + } + + if (rectPoints[0].x() > rectPoints[1].x()) + { + std::swap(rectPoints[0], rectPoints[1]); + std::swap(_internal->_pointIndexVector[0],_internal->_pointIndexVector[1]); + } + + if (rectPoints[3].x() > rectPoints[2].x()) + { + std::swap(rectPoints[3], rectPoints[2]); + std::swap(_internal->_pointIndexVector[3],_internal->_pointIndexVector[2]); + } + + // Verticies are now sorted in counter clockwise order with beginning + // with bottom left verticies as shown in following sketch + // + // 3 2 + // X------------------------------------X + // | | + // | | + // | | + // X------------------------------------X + // 0 1 + // + + // now check input verticies for beeing a rectangle + + QPointF p1( (rectPoints[0] + rectPoints[3]) * 0.5); // construct 2 helpful points + QPointF p2( (rectPoints[1] + rectPoints[2]) * 0.5); + + qreal cosA = p2.x()-p1.x(); + qreal sinA = p2.y()-p1.y(); + + qreal len = sqrt(cosA*cosA+sinA*sinA); + + auto epsilonThreshold = [this](qreal distance) -> qreal + { + Esri::ArcGISRuntime::Point p1 = Rubberband::_internal->_mapView->screenToLocation(0, 0); + Esri::ArcGISRuntime::Point p2 = Rubberband::_internal->_mapView->screenToLocation(0, 1); + + qreal dx = p2.x() - p1.x(); + qreal dy = p2.y() - p1.y(); + + return sqrt(dx*dx + dy * dy) / distance; + }; + + qreal eps = epsilonThreshold(len); + + cosA /= len; + sinA /= len; + + qreal cosP = rectPoints[1].x()-rectPoints[0].x(); + qreal sinP = rectPoints[1].y()-rectPoints[0].y(); + + len = sqrt(cosP*cosP+sinP*sinP); + cosP /= len; + sinP /= len; + + _rectangleIsValid = (epsilonEqual(cosA,cosP,eps) && epsilonEqual(sinA,sinP,eps)); + + // be careful _oVec is relative to scene coordinate system and the scene coordinate system is counter clockwise -- sinA must be negated + + if (_internal->_constrainOrthogonal) + { + _internal->_oVec = QPointF(1,0); + if (_rectangleIsValid) + { + _rectangleIsValid = epsilonEqual( fabs(cosA-1), fabs(sinA), eps ); + } + } + else _internal->_oVec = QPointF( cosA, -sinA ); + + _internal->updateFrame( Rubberband::_internal->_mapView, rectPoints ); + + disenableConstructionState(); + constructHandles(); + activateButtons(); + update(); + + } + } /* RubberbandRectangle::rectangleVerticies */ + + + void RubberbandRectangle::buildFromRectangleParamter(const QPointF & center, qreal width, qreal height, const QPointF & direction, CoordinateSystem cs) + { + auto rotate = [](const QPointF & p, qreal sinA, qreal cosA) -> QPointF + { + return QPointF(p.x()*cosA-p.y()*sinA,p.y()*cosA+p.x()*sinA); + }; + + qreal dl = sqrt(direction.x()*direction.x()+direction.y()*direction.y()); + + qreal sinA = direction.y()/dl; + qreal cosA = direction.x()/dl; + + _internal->_constrainOrthogonal = false; + _internal->_oVec = QPointF( cosA, -sinA ); + + _internal->_pointIndexVector.clear(); + + QVector rectPoints(4); + + width *= 0.5; + height *= 0.5; + + QPointF centerMapCoordinate = projectCoordinateToMap( cs, center ); + + rectPoints[0] = centerMapCoordinate + rotate( QPointF(-width,-height), sinA, cosA ); + rectPoints[1] = centerMapCoordinate + rotate( QPointF( width,-height), sinA, cosA ); + rectPoints[2] = centerMapCoordinate + rotate( QPointF( width, height), sinA, cosA ); + rectPoints[3] = centerMapCoordinate + rotate( QPointF(-width, height), sinA, cosA ); + + _internal->updateFrame( Rubberband::_internal->_mapView, rectPoints ); + + disenableConstructionState(); + constructHandles(); + activateButtons(); + update(); + + } /* RubberbandRectangle::buildFromRectangleParamter */ + + + void RubberbandRectangle::buildRectangleRelativeToBottomLeftCorner(const QPointF & bottomLeft, qreal width, qreal height, const QPointF & direction, CoordinateSystem cs) + { + auto rotate = [](const QPointF & p, qreal sinA, qreal cosA) -> QPointF + { + return QPointF(p.x()*cosA-p.y()*sinA,p.y()*cosA+p.x()*sinA); + }; + + qreal dl = sqrt(direction.x()*direction.x()+direction.y()*direction.y()); + + qreal sinA = direction.y()/dl; + qreal cosA = direction.x()/dl; + + _internal->_constrainOrthogonal = false; + _internal->_oVec = QPointF( cosA, -sinA ); + + _internal->_pointIndexVector.clear(); + + QVector rectPoints(4); + + QPointF bottomLeftMapCoordinate = projectCoordinateToMap( cs, bottomLeft ); + + rectPoints[0] = bottomLeftMapCoordinate; + rectPoints[1] = bottomLeftMapCoordinate + rotate( QPointF(width, 0), sinA, cosA ); + rectPoints[2] = bottomLeftMapCoordinate + rotate( QPointF(width,height), sinA, cosA ); + rectPoints[3] = bottomLeftMapCoordinate + rotate( QPointF( 0,height), sinA, cosA ); + + _internal->updateFrame( Rubberband::_internal->_mapView, rectPoints ); + + disenableConstructionState(); + constructHandles(); + activateButtons(); + update(); + + } /* RubberbandRectangle::buildRectangleRelativeToBottomLeftCorner */ + + + void RubberbandRectangle::constrainQuadratic(bool on) + { + _internal->_constrainQuadratic = on; + if (isEnabledModificationState()) + { + constructHandles(); + activateButtons(); + update(); + } + } /* RubberbandRectangle::constrainQuadratic */ + + + bool RubberbandRectangle::constrainQuadratic() const { return _internal->_constrainQuadratic; } + + + void RubberbandRectangle::constrainOrthogonal(bool on) + { + _internal->_constrainOrthogonal = on; + + if (_internal->_constrainOrthogonal) + { + _internal->_rotateHandle = nullptr; + _internal->_oVec = QPointF(1,0); + } + } /* RubberbandRectangle::constrainQuadratic */ + + + bool RubberbandRectangle::constrainOrthogonal() const { return _internal->_constrainOrthogonal; } + + + bool RubberbandRectangle::multiSelectActionPress ( AbstractEventDispatcher * causer, int x, int y ) { return selectAction ( causer, x, y ); } + bool RubberbandRectangle::multiSelectActionRelease( AbstractEventDispatcher * causer, int x, int y ) { return setCoordinateAction( causer, x, y ); } + + + bool RubberbandRectangle::selectAction( AbstractEventDispatcher *, int x, int y ) + { + if (rubberbandIsAlive()) + { + bool eventHandled = false; + QPointF scenePos(pixel2ScenePosition(x,y)); + + _baseInternal->_lastClickPosition = scenePos; + + if (isEnabledConstructionState()) + { + _internal->_constructionStart = scenePos; + enableSelectedState(); + eventHandled = true; + } + else + { + if (_internal->minimumSizeReached()) return true; + + RubberbandHandle * handlePtr = pickHandle(x,y); + if (handlePtr) + { + handlePtr->setSelection(); + enableSelectedState(); + eventHandled = true; + } + else + { + for (auto lh : _internal->_lineHandle) + { + if ((lh.valid()) && (lh->isPositionInside(scenePos))) + { + lh->setSelection(true); + enableSelectedState(); + eventHandled = true; + } + } + + if ((_internal->_rotateHandle.valid()) && (_internal->_rotateHandle->isPositionInside(scenePos))) + { + _internal->_rotateHandle->setSelection(true); + enableSelectedState(); + eventHandled = true; + } + } + if (!isEnabledSelectedState()) + { + // ok doesn't hit any point or line handle now looking to frame handle + if (_internal->_frameHandle.valid()) + { + if ((_internal->_frameHandle->isSelectable()) && (_internal->_frameHandle->isPositionInside(scenePos))) + { + _internal->_frameHandle->setSelection(true); + enableSelectedState(); + eventHandled = true; + } + } + } + } + + if (eventHandled) + { + update(); + deactivateButtons(); + } + return eventHandled; + } + return false; + } /* RubberbandRectangle::selectAction */ + + + bool RubberbandRectangle::moveAction( AbstractEventDispatcher*, int x, int y ) + { + adjustConnectedPreviewPixmap(x,y); + + if (rubberbandIsAlive()) + { + if (!isEnabledSelectedState()) + { + deSelectHandles(); + + if (_internal->minimumSizeReached()) return true; + + if ((_internal->_pointHandle[0] == nullptr) && (_internal->_lineHandle[0] == nullptr) && (_internal->_rotateHandle == nullptr) && (_internal->_frameHandle == nullptr)) + { + // constructHandles has not been called jet. Leave function and eat event + return false; + } + + if (_internal->_rotateHandle.valid()) + { + _internal->_rotateHandle->setSelection(false); + } + RubberbandHandle * handlePtr = pickHandle(x,y); + if (handlePtr) + { + handlePtr->setSelection(); + for (auto lh : _internal->_lineHandle) + lh->setSelection( false ); + } + else + { + QPointF scenePos(pixel2ScenePosition(x,y)); + + bool selectRotationHandle = _internal->_rotateHandle.valid() ? _internal->_rotateHandle->isPositionInside(scenePos) : false; + + if (!selectRotationHandle) + { + for (auto lh : _internal->_lineHandle) + { + if (lh != nullptr) + { + bool selectHandle = lh->isPositionInside(scenePos); + lh->setSelection( selectHandle ); + } + } + } + else _internal->_rotateHandle->setSelection(true); + } + } + } + return false; + } /* RubberbandRectangle::moveAction */ + + + bool RubberbandRectangle::checkMinMaxConstrains(const QPointF & vertexScene, QPointF & deltaMovePtr ) + { + bool ok_x = true; + bool ok_y = true; + // check min max constrains + RubberbandHandle::RubberbandCoordinate vertex( Rubberband::_internal->_mapView, vertexScene ); + + qreal mapRectWidth = 2.0 * fabs(vertex._mapCoordinate.x() - _internal->_center.x()); + qreal mapRectHeight = 2.0 * fabs(vertex._mapCoordinate.y() - _internal->_center.y()); + + if ((_internal->_minWidth > 0) && (mapRectWidth < _internal->_minWidth )) { ok_x = false; deltaMovePtr.setX(0); } + if ((_internal->_maxWidth > 0) && (mapRectWidth > _internal->_maxWidth )) { ok_x = false; deltaMovePtr.setX(0); } + if ((_internal->_minHeight > 0) && (mapRectHeight < _internal->_minHeight)) { ok_y = false; deltaMovePtr.setY(0); } + if ((_internal->_maxHeight > 0) && (mapRectHeight > _internal->_maxHeight)) { ok_y = false; deltaMovePtr.setY(0); } + + return (ok_x || ok_y); + + } /* RubberbandRectangle::checkMinMaxConstrains */ + + + bool RubberbandRectangle::checkMinMaxConstrains(const QPointF & vertexScene, bool isVertical) + { + bool ok = true; + + if (_internal->_constrainQuadratic) + { + _internal->verifyConstrains(); + /* + if (_internal->_minHeight > 0) _internal->_minWidth = _internal->_minHeight; + else if (_internal->_minWidth > 0) _internal->_minHeight = _internal->_minWidth; + if (_internal->_maxHeight > 0) _internal->_maxWidth = _internal->_maxHeight; + else if (_internal->_maxWidth > 0) _internal->_maxHeight = _internal->_maxWidth; + */ + } + + // check min max constrains + RubberbandHandle::RubberbandCoordinate vertex( Rubberband::_internal->_mapView, vertexScene ); + + if (isVertical) + { + qreal mapRectWidth = 2.0 * fabs(vertex._mapCoordinate.x() - _internal->_center.x()); + if ((_internal->_minWidth > 0) && (mapRectWidth < _internal->_minWidth )) { ok = false; } + if ((_internal->_maxWidth > 0) && (mapRectWidth > _internal->_maxWidth )) { ok = false; } + return ok; + } + else + { + qreal mapRectHeight = 2.0 * fabs(vertex._mapCoordinate.y() - _internal->_center.y()); + if ((_internal->_minHeight > 0) && (mapRectHeight < _internal->_minHeight)) { ok = false; } + if ((_internal->_maxHeight > 0) && (mapRectHeight > _internal->_maxHeight)) { ok = false; } + return ok; + } + } /* RubberbandRectangle::checkMinMaxConstrains */ + + + bool RubberbandRectangle::constrainsSatisfied( RubberbandPointHandle * handlePointPtr, unsigned int handleIdx, QPointF & deltaMove, bool & constrainsComplied ) + { + constrainsComplied = true; + + qreal sinMap = _internal->_mapOVec.y(); // calculate rotation + qreal cosMap = _internal->_mapOVec.x(); + qreal sinRec = _internal->_oVec.y(); + qreal cosRec = _internal->_oVec.x(); + + qreal sinPhi = sinRec * cosMap - cosRec * sinMap; // Addition-Theorem + qreal cosPhi = cosRec * cosMap + sinRec * sinMap; + + QPointF rotatedDeltaMove = MathUtils::rotate( deltaMove, QPointF( 0.0, 0.0 ), -sinPhi, cosPhi ); // rotate to orthogonal position relative to rectangle + + if (_internal->_constrainQuadratic) + { + qreal d = floor(sqrt(0.5*(rotatedDeltaMove.x()*rotatedDeltaMove.x() + rotatedDeltaMove.y()*rotatedDeltaMove.y()))); + + switch (handleIdx) + { + case 0 : rotatedDeltaMove = (rotatedDeltaMove.x() < 0) ? QPointF(-d, d) : QPointF( d,-d); break; + case 1 : rotatedDeltaMove = (rotatedDeltaMove.x() > 0) ? QPointF( d, d) : QPointF(-d,-d); break; + case 2 : rotatedDeltaMove = (rotatedDeltaMove.x() > 0) ? QPointF( d,-d) : QPointF(-d, d); break; + case 3 : rotatedDeltaMove = (rotatedDeltaMove.x() < 0) ? QPointF(-d,-d) : QPointF( d, d); break; + default: break; + } + } + + int dx = int((fabs(rotatedDeltaMove.x()) > 0) ? rotatedDeltaMove.x()/fabs(rotatedDeltaMove.x()) : 0 ); + int dy = int((fabs(rotatedDeltaMove.y()) > 0) ? rotatedDeltaMove.y()/fabs(rotatedDeltaMove.y()) : 0 ); + + QPointF moveStepByStep(0,0); + QPointF resultDeltaMove(0,0); + + while (constrainsComplied) + { + moveStepByStep.setX( moveStepByStep.x() + dx ); + moveStepByStep.setY( moveStepByStep.y() + dy ); + + int endCondition = 2; + + if (fabs(moveStepByStep.x()) >= fabs(rotatedDeltaMove.x())) { moveStepByStep.setX(rotatedDeltaMove.x()); --endCondition; } + if (fabs(moveStepByStep.y()) >= fabs(rotatedDeltaMove.y())) { moveStepByStep.setY(rotatedDeltaMove.y()); --endCondition; } + + QPointF loc = MathUtils::rotate( handlePointPtr-> getCoordinate()._sceneCoordinate, _internal->_centerScene, -sinPhi, cosPhi ); // rotate back to orthogonal position + + loc += moveStepByStep; + + constrainsComplied = _internal->minimumSizeReached( handleIdx, -1, loc ); + + if (constrainsComplied) + { + QPointF tmpDeltaMove(moveStepByStep); + + constrainsComplied = checkMinMaxConstrains( loc, tmpDeltaMove ); + + if (constrainsComplied) + { + if (fabs(tmpDeltaMove.x()) > fabs(resultDeltaMove.x())) resultDeltaMove.setX( tmpDeltaMove.x() ); + if (fabs(tmpDeltaMove.y()) > fabs(resultDeltaMove.y())) resultDeltaMove.setY( tmpDeltaMove.y() ); + } + } + + if (0 == endCondition) break; + + } // while + + deltaMove = MathUtils::rotate( resultDeltaMove, QPointF( 0.0, 0.0 ), sinPhi, cosPhi ); + + DEBUG_LOG(_internal->_logger) << LOGFUNCTION << " " << handleIdx << " deltaMove " << deltaMove.x() << " ; " << deltaMove.y() << " centerScene " + << _internal->_centerScene.x() << " ; " << _internal->_centerScene.y() << " --> " << constrainsComplied; + + return (constrainsComplied || !deltaMove.isNull()); + + } /* RubberbandRectangle::constrainsSatisfied */ + + + void RubberbandRectangle::adjustNeighbouringPoints( RubberbandPointHandle * handlePointPtr, unsigned int handleIdx, QPointF deltaMove ) + { + // adjust neighbouring point handles + + unsigned int beforeIdx = (handleIdx > 0) ? handleIdx-1 : 3; + unsigned int nextIdx = (handleIdx < 3) ? handleIdx+1 : 0; + + RubberbandPointHandle * beforePoint = dynamic_cast(Rubberband::_internal->_handleList[beforeIdx].get()); + RubberbandPointHandle * nextPoint = dynamic_cast(Rubberband::_internal->_handleList[nextIdx ].get()); + + qreal sinMap = _internal->_mapOVec.y(); + qreal cosMap = _internal->_mapOVec.x(); + qreal sinRec = _internal->_oVec.y(); + qreal cosRec = _internal->_oVec.x(); + + qreal sinPhi = sinRec * cosMap - cosRec * sinMap; // Addition-Theorem + qreal cosPhi = cosRec * cosMap + sinRec * sinMap; + + QPointF loc = handlePointPtr -> getCoordinate()._sceneCoordinate; + QPointF beforeLoc = beforePoint -> getCoordinate()._sceneCoordinate; + QPointF nextLoc = nextPoint -> getCoordinate()._sceneCoordinate; + + loc = MathUtils::rotate( loc , _internal->_center, -sinPhi, cosPhi ); + beforeLoc = MathUtils::rotate( beforeLoc, _internal->_center, -sinPhi, cosPhi ); + nextLoc = MathUtils::rotate( nextLoc , _internal->_center, -sinPhi, cosPhi ); + deltaMove = MathUtils::rotate( deltaMove, QPointF(0.0, 0.0) , -sinPhi, cosPhi ); + + bool beforeIsHorizontal( fabs(loc.x() - beforeLoc.x()) > fabs(loc.y() - beforeLoc.y()) ); + bool nextIsHorizontal ( fabs(loc.x() - nextLoc.x()) > fabs(loc.y() - nextLoc.y()) ); + +#ifdef NEVER + qDebug() << "RubberbandRectangle::adjustNeibouringPoints " << deltaMove.x() << " , " << deltaMove.y(); + qDebug() << " before is horizontal " << beforeIsHorizontal; + qDebug() << " next is horizontal " << nextIsHorizontal; + qDebug() << " point " << loc; + qDebug() << " point before " << beforeLoc; + qDebug() << " point next " << nextLoc; +#endif + + if (beforeIsHorizontal) beforeLoc.setY( beforeLoc.y() + deltaMove.y() ); + else beforeLoc.setX( beforeLoc.x() + deltaMove.x() ); + + if (nextIsHorizontal) nextLoc.setY( nextLoc.y() + deltaMove.y() ); + else nextLoc.setX( nextLoc.x() + deltaMove.x() ); + +#ifdef NEVER + qDebug() << " point before " << beforeLoc; + qDebug() << " point next " << nextLoc; +#endif + + // rotate back + beforeLoc = MathUtils::rotate( beforeLoc, _internal->_center, sinPhi, cosPhi ); + nextLoc = MathUtils::rotate( nextLoc , _internal->_center, sinPhi, cosPhi ); + + // store coordinates + beforePoint -> updateCoordinate(beforeLoc); + nextPoint -> updateCoordinate(nextLoc ); + + } /* RubberbandRectangle::adjustNeighbouringPoints */ + + + bool RubberbandRectangle::adjustLine( QPointF & deltaMove, bool & constrainsComplied ) + { + constrainsComplied = true; + for (unsigned i = 0; i < 4; ++i) + { + if (_internal->_lineHandle[i]->isSelected()) + { + int idx0 = i; + int idx1 = (i+1) % 4; + + QPointF loc0 = dynamic_cast(Rubberband::_internal->_handleList[idx0].get())->getCoordinate()._sceneCoordinate; + QPointF loc1 = dynamic_cast(Rubberband::_internal->_handleList[idx1].get())->getCoordinate()._sceneCoordinate; + + qreal sinMap = _internal->_mapOVec.y(); + qreal cosMap = _internal->_mapOVec.x(); + qreal sinRec = _internal->_oVec.y(); + qreal cosRec = _internal->_oVec.x(); + + qreal sinPhi = sinRec * cosMap - cosRec * sinMap; // Addition-Theorem + qreal cosPhi = cosRec * cosMap + sinRec * sinMap; + + loc0 = MathUtils::rotate( loc0 , _internal->_centerScene, -sinPhi, cosPhi ); + loc1 = MathUtils::rotate( loc1 , _internal->_centerScene, -sinPhi, cosPhi ); + deltaMove = MathUtils::rotate( deltaMove, QPointF( 0.0, 0.0 ) , -sinPhi, cosPhi ); + + bool isVertical((i%2) == 1); + + int dx = int( ( isVertical && (fabs(deltaMove.x()) > 0)) ? deltaMove.x()/fabs(deltaMove.x()) : 0 ); + int dy = int( (!isVertical && (fabs(deltaMove.y()) > 0)) ? deltaMove.y()/fabs(deltaMove.y()) : 0 ); + bool doStepByStep = ((0 != dx) || (0 != dy)); + + + QPointF moveStepByStep(0,0); + QPointF resultDeltaMove(0,0); + + while (doStepByStep) + { + QPointF stepLoc0( loc0 ); + QPointF stepLoc1( loc1 ); + + if (0 != dx) + { + moveStepByStep.setX( moveStepByStep.x() + dx ); + if (fabs(moveStepByStep.x()) >= fabs(deltaMove.x())) { moveStepByStep.setX(deltaMove.x()); doStepByStep = false; } + stepLoc0.setX( stepLoc0.x() + moveStepByStep.x() ); stepLoc1.setX( stepLoc1.x() + moveStepByStep.x() ); + } + + if (0 != dy) + { + moveStepByStep.setY( moveStepByStep.y() + dy ); + if (fabs(moveStepByStep.y()) >= fabs(deltaMove.y())) { moveStepByStep.setY(deltaMove.y()); doStepByStep = false; } + stepLoc0.setY( stepLoc0.y() + moveStepByStep.y() ); stepLoc1.setY( stepLoc1.y() + moveStepByStep.y() ); + } + + constrainsComplied = _internal->minimumSizeReached(-1,i,stepLoc0); + + if (!constrainsComplied) continue; + + constrainsComplied = checkMinMaxConstrains( stepLoc0, isVertical ); + + if (!constrainsComplied) continue; + + resultDeltaMove = moveStepByStep; + + if (_internal->_constrainQuadratic) + { + qreal h = stepLoc0.y() - _internal->_centerScene.y(); + qreal w = stepLoc0.x() - _internal->_centerScene.x(); + + qreal d = fabs( isVertical ? w : h ); + + stepLoc0 = MathUtils::rotate( QPointF(_internal->_centerScene.x() - d, _internal->_centerScene.y() + d ), _internal->_centerScene , sinPhi, cosPhi ); + dynamic_cast(Rubberband::_internal->_handleList[0].get())->updateCoordinate( stepLoc0 ); + + stepLoc0 = MathUtils::rotate( QPointF(_internal->_centerScene.x() + d, _internal->_centerScene.y() + d ), _internal->_centerScene , sinPhi, cosPhi ); + dynamic_cast(Rubberband::_internal->_handleList[1].get())->updateCoordinate( stepLoc0 ); + + stepLoc0 = MathUtils::rotate( QPointF(_internal->_centerScene.x() + d, _internal->_centerScene.y() - d ), _internal->_centerScene , sinPhi, cosPhi ); + dynamic_cast(Rubberband::_internal->_handleList[2].get())->updateCoordinate( stepLoc0 ); + + stepLoc0 = MathUtils::rotate( QPointF(_internal->_centerScene.x() - d, _internal->_centerScene.y() - d ), _internal->_centerScene , sinPhi, cosPhi ); + dynamic_cast(Rubberband::_internal->_handleList[3].get())->updateCoordinate( stepLoc0 ); + } + else + { + stepLoc0 = MathUtils::rotate( stepLoc0, _internal->_centerScene , sinPhi, cosPhi ); + stepLoc1 = MathUtils::rotate( stepLoc1, _internal->_centerScene , sinPhi, cosPhi ); + + // store coordinates + dynamic_cast(Rubberband::_internal->_handleList[idx0].get())->updateCoordinate(stepLoc0); + dynamic_cast(Rubberband::_internal->_handleList[idx1].get())->updateCoordinate(stepLoc1); + } + } // while (doStepByStep) + + deltaMove = MathUtils::rotate( resultDeltaMove, QPointF( 0.0, 0.0 ), sinPhi, cosPhi ); + + return (constrainsComplied || !deltaMove.isNull()); + } // if handle is selected + } // for handle-index + return true; + } /* RubberbandRectangle::adjustLine */ + + + void RubberbandRectangle::updateHandles(const QPointF & scenePos, QPointF deltaMove) + { + bool constrainsComplied = true; + + if ((_internal->_rotateHandle.valid()) && _internal->_rotateHandle->isSelected()) + { + qreal sinA = scenePos.x() - _internal->_centerScene.x(); + qreal cosA = - scenePos.y() + _internal->_centerScene.y(); + qreal len = sqrt(cosA*cosA+sinA*sinA); + + cosA /= len; + sinA /= len; + + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); + + if (handlePointPtr == nullptr) continue; + + QPointF p = MathUtils::rotate( handlePointPtr->getCoordinate()._sceneCoordinate, _internal->_centerScene, -_internal->_oVec.y(), _internal->_oVec.x() ); + p = MathUtils::rotate( p, _internal->_centerScene, sinA, cosA ); + handlePointPtr->updateCoordinate( p ); + + p.setX( handlePointPtr->getCoordinate()._mapCoordinate.x() ); + p.setY( handlePointPtr->getCoordinate()._mapCoordinate.y() ); + } + + qreal dSin = sinA*_internal->_oVec.x() - cosA*_internal->_oVec.y(); // addition theorem + qreal dCos = cosA*_internal->_oVec.x() + sinA*_internal->_oVec.y(); + + _lastCoordinateChange.setToIdentity(); // clear matrix + _lastCoordinateChange.putTranslation( -_internal->_center ); // move center to (0,0) + _lastCoordinateChange.putRotate( QPointF(dCos,dSin) ); // do rotation + _lastCoordinateChange.putTranslation( _internal->_center ); // move center back + + _internal->_oVec.setX(cosA); + _internal->_oVec.setY(sinA); + + _baseInternal->_lastClickPosition = scenePos; + } + else + { + bool frameIsSelected( ((_internal->_frameHandle.valid()) && (_internal->_frameHandle->isSelected())) ); + bool pointsAdjusted( false ); + + unsigned int handleIdx = 0; + + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); + + if ((handlePointPtr) && ((handlePointPtr->isSelected()) || (frameIsSelected))) + { + pointsAdjusted = true; + + if (!frameIsSelected) + { + if (constrainsSatisfied( handlePointPtr, handleIdx, deltaMove, constrainsComplied )) + { + adjustNeighbouringPoints( handlePointPtr, handleIdx, deltaMove ); + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Move selected handle after moving the neighbouring points because + * for larger deltaMove the decision which values have to be modified + * will fail if the handle has already moved to new position. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + handlePointPtr->moveCoordinate( deltaMove ); + + if (_internal->_constrainQuadratic) _baseInternal->_lastClickPosition = scenePos; + else _baseInternal->_lastClickPosition += deltaMove; + + break; // only one handle can be selected at time + } + } + else handlePointPtr->moveCoordinate( deltaMove ); // frameIsSelected == true + } + ++handleIdx; + } + + + if (frameIsSelected) + { + _internal->_centerScene += deltaMove; + _baseInternal->_lastClickPosition = scenePos; + + if (Rubberband::_internal->_handleList.isEmpty()) + { + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Rubberband has only frame handle, we have to do some calculations to set the new center point + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + QPointF lastCenter = _internal->_center; + + RubberbandHandle::RubberbandCoordinate movedCenter(Rubberband::_internal->_mapView, _internal->_centerScene ); + _internal->_center.setX( movedCenter._mapCoordinate.x() ); + _internal->_center.setY( movedCenter._mapCoordinate.y() ); + + _lastCoordinateChange.setToIdentity(); // clear matrix + _lastCoordinateChange.putTranslation( _internal->_center - lastCenter ); // set translation + } + } + else if ((!pointsAdjusted) && (adjustLine(deltaMove,constrainsComplied))) _baseInternal->_lastClickPosition += deltaMove; + } + + markFailedConstrains( scenePos, !constrainsComplied ); + + update(); + + } /* RubberbandRectangle::updateHandles */ + + + void RubberbandRectangle::buildTmpConstructionPath( const QPointF & start + , const QPointF & end + , QVector & path + , QPointF & sceneCenter + , qreal & sceneWidth + , qreal & sceneHeight ) + { + _internal->setScreenCenter( Rubberband::_internal->_mapView ); + + auto length = [ ]( qreal x, qreal y ) -> qreal { return sqrt(x*x+y*y); }; + + auto rotate = [this]( qreal x, qreal y, bool back ) -> QPointF + { + qreal sinPhi = (back) ? -_internal->_mapOVec.y() : _internal->_mapOVec.y(); + qreal cosPhi = _internal->_mapOVec.x(); + + qreal px = x*cosPhi + y*sinPhi; + qreal py = y*cosPhi - x*sinPhi; + return QPointF(px,py); + }; // lambda rotate + + + RubberbandHandle::RubberbandCoordinate mapPointStart( Rubberband::_internal->_mapView, start ); + + const Esri::ArcGISRuntime::Point & mapStart = mapPointStart._mapCoordinate; + + const Esri::ArcGISRuntime::Point mapMaxW( mapStart.x() + _internal->_maxWidth + , mapStart.y() + , Rubberband::_internal->_mapView->map()->spatialReference() ); + + const Esri::ArcGISRuntime::Point mapMinW( mapStart.x() + _internal->_minWidth + , mapStart.y() + , Rubberband::_internal->_mapView->map()->spatialReference() ); + + const Esri::ArcGISRuntime::Point mapMaxH( mapStart.x() + , mapStart.y() + _internal->_maxHeight + , Rubberband::_internal->_mapView->map()->spatialReference() ); + + const Esri::ArcGISRuntime::Point mapMinH( mapStart.x() + , mapStart.y() + _internal->_minHeight + , Rubberband::_internal->_mapView->map()->spatialReference() ); + + RubberbandHandle::RubberbandCoordinate mapPointMaxW( Rubberband::_internal->_mapView, mapMaxW ); + RubberbandHandle::RubberbandCoordinate mapPointMinW( Rubberband::_internal->_mapView, mapMinW ); + RubberbandHandle::RubberbandCoordinate mapPointMaxH( Rubberband::_internal->_mapView, mapMaxH ); + RubberbandHandle::RubberbandCoordinate mapPointMinH( Rubberband::_internal->_mapView, mapMinH ); + + qreal maxSceneWidth = length( mapPointMaxW._sceneCoordinate.x() - mapPointStart._sceneCoordinate.x(), mapPointMaxW._sceneCoordinate.y() - mapPointStart._sceneCoordinate.y() ); + qreal minSceneWidth = length( mapPointMinW._sceneCoordinate.x() - mapPointStart._sceneCoordinate.x(), mapPointMinW._sceneCoordinate.y() - mapPointStart._sceneCoordinate.y() ); + qreal maxSceneHeight = length( mapPointMaxH._sceneCoordinate.x() - mapPointStart._sceneCoordinate.x(), mapPointMaxH._sceneCoordinate.y() - mapPointStart._sceneCoordinate.y() ); + qreal minSceneHeight = length( mapPointMinH._sceneCoordinate.x() - mapPointStart._sceneCoordinate.x(), mapPointMinH._sceneCoordinate.y() - mapPointStart._sceneCoordinate.y() ); + + const QPointF & pe = rotate( end.x() - start.x(), end.y() - start.y(), true ); + + qreal w = pe.x(); + qreal h = pe.y(); + + if ((_internal->_minWidth > 0) && (minSceneWidth > fabs(w))) w = (w>=0) ? minSceneWidth : -minSceneWidth; + if ((_internal->_maxWidth > 0) && (maxSceneWidth < fabs(w))) w = (w>=0) ? maxSceneWidth : -maxSceneWidth; + if ((_internal->_minHeight > 0) && (minSceneHeight > fabs(h))) h = (h>=0) ? minSceneHeight : -minSceneHeight; + if ((_internal->_maxHeight > 0) && (maxSceneHeight < fabs(h))) h = (h>=0) ? maxSceneHeight : -maxSceneHeight; + + if (_internal->_constrainQuadratic) + { + if (fabs(w) > fabs(h)) h = (h >= 0) ? fabs(w) : -fabs(w); + else if (fabs(w) < fabs(h)) w = (w >= 0) ? fabs(h) : -fabs(h); + } + + path << start; + path << start + rotate( w, 0, false ); + path << start + rotate( w, h, false ); + path << start + rotate( 0, h, false ); + + sceneWidth = fabs(w); + sceneHeight = fabs(h); + sceneCenter = start + rotate(w*0.5,h*0.5,false); + + } /* RubberbandRectangle::buildTmpConstructionPath */ + + + void RubberbandRectangle::buildTmpConstruction( SituationMap::RefPointer & tmpGraphic, QPointF & start, QPointF & end ) + { + if (!tmpGraphic.valid()) + { + tmpGraphic = new DrawablePainterPath( Rubberband::_internal->_mapView ); + tmpGraphic -> setDrawColor( _drawColor ); + tmpGraphic -> setFillColor( _fillColor ); + tmpGraphic -> setLineWidth( 1 ); + tmpGraphic -> zBufferPlane( RubberbandDrawable::ZBufferPlane::Drawables ); + } + + QVector path; + QPointF sceneCenter; + qreal sceneWidth; + qreal sceneHeight; + + buildTmpConstructionPath( start, end, path, sceneCenter, sceneWidth, sceneHeight ); + + DrawablePainterPath * tmpPath = static_cast(tmpGraphic.get()); + + tmpPath -> setPath( path ); + tmpPath -> enableClosedLine(true); + + } /* RubberbandRectangle::buildTmpConstruction */ + + + void RubberbandRectangle::getTmpConstructionParameter( const QPointF & start, const QPointF & end, QPointF & sceneCenter, qreal & sceneWidth, qreal & sceneHeight ) + { + QVector path; + + buildTmpConstructionPath( start, end, path, sceneCenter, sceneWidth, sceneHeight ); + + } /* RubberbandRectangle::getTmpConstructionParameter */ + + + bool RubberbandRectangle::setCoordinateAction( AbstractEventDispatcher * causer, int x, int y ) + { + markFailedConstrains( QPointF(), false ); + + if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) + { + QPointF scenePos(pixel2ScenePosition(x,y)); + + Rubberband::_internal->_lastClickPosition = scenePos; + + if (isEnabledConstructionState()) + { + if (_internal->_tmpConstruction.valid()) + { + qreal sceneWidth; + qreal sceneHeight; + + getTmpConstructionParameter( _internal->_constructionStart, scenePos, _internal->_centerScene, sceneWidth, sceneHeight ); + _internal->setInitialRectSize( this, sceneWidth, sceneHeight ); + _internal->_tmpConstruction = nullptr; + + if (_finishAfterConstruction) + { + disenableConstructionState(); + constructHandles(); + update(); + applyAndEndAction(causer,x,y); + return true; + } + } + else + { + _internal->_centerScene = scenePos; + + _internal->setInitialRectSize( this, scenePos, x, y ); + + // now set values in map coordinates + } + + _internal->setRotatedFrame( Rubberband::_internal->_mapView ); + + disenableSelectedState(); + disenableConstructionState(); + constructHandles(); + update(); + activateButtons(); + return true; + } + else // isEnabledConstructionState() + { + if (isEnabledSelectedState()) + { + updateHandles( scenePos, scenePos-_baseInternal->_lastClickPosition ); + + markFailedConstrains( QPointF(), false ); + + for (auto lh : _internal->_lineHandle) + { + if (lh.valid()) lh->setSelection(false); + } + if (_internal->_rotateHandle.valid()) _internal->_rotateHandle->setSelection(false); + if (_internal->_frameHandle.valid()) _internal->_frameHandle->setSelection(false); + + disenableSelectedState(); + disenableMultiSelectedState(); + updateButtonPosition(); + update(); + activateButtons(); + return true; + + } + else if (_baseInternal->buttonProxyActive( scenePos )) return false; + } + + updateButtonPosition(); + if (isEnabledModificationState()) activateButtons(); + } + return false; + } /* RubberbandRectangle::setCoordinateAction */ + + + bool RubberbandRectangle::movePickedAction( AbstractEventDispatcher*, int x, int y ) + { + if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) + { + if (isEnabledConstructionState()) + { + QPointF scenePos(pixel2ScenePosition(x,y)); + if ((_internal->_tmpConstruction.valid()) || ((_internal->_constructionStart - scenePos).manhattanLength() > 5)) + { + buildTmpConstruction( _internal->_tmpConstruction, _internal->_constructionStart, scenePos ); + } + return true; + } + else + { + if (isEnabledSelectedState()) + { + QPointF scenePos(pixel2ScenePosition(x,y)); + updateHandles( scenePos, scenePos-_baseInternal->_lastClickPosition ); + return true; + } + } + } + return false; + } /* RubberbandRectangle::movePickedAction */ + + + bool RubberbandRectangle::contextMenuAction( AbstractEventDispatcher * , int , int ) + { + return rubberbandIsAlive(); + } /* RubberbandRectangle::contextMenuAction */ + + + bool RubberbandRectangle::completeStateAction( AbstractEventDispatcher * causer, int x, int y ) + { + if (isEnabledConstructionState()) + { + // Position of rectangle is still unknown, there are no data to apply + return abortAction(causer,x,y); + } + return applyAndEndAction(causer,x,y); + } /* RubberbandRectangle::completeStateAction */ + + + bool RubberbandRectangle::undoAction( AbstractEventDispatcher * , int , int ) + { + return rubberbandIsAlive(); + } /* RubberbandRectangle::undoAction */ + + + bool RubberbandRectangle::deleteAction(AbstractEventDispatcher * , int , int ) + { + return rubberbandIsAlive(); + } /* RubberbandRectangle::deleteAction */ + + + + void RubberbandRectangle::update() + { + Rubberband::update(); + + QVector coordinateList; + + coordinateList.reserve(4); + + if (Rubberband::_internal->_handleList.count() == 4) + { + for (unsigned int i = 0; i < 4; ++i) + { + auto handlePoint = Rubberband::_internal->_handleList[i]; + RubberbandPointHandle * handlePointPtr = dynamic_cast(handlePoint.get()); + + if (handlePointPtr) + { + const RubberbandHandle::RubberbandCoordinate & pos = handlePointPtr->getCoordinate(); + + _internal->_lineHandle[i] -> updateCoordinate( 0, pos._sceneCoordinate ); + _internal->_lineHandle[i] -> setDrawColor(_handleDrawColor); + _internal->_lineHandle[i] -> setFillColor(_handleFillColor); + _internal->_lineHandle[i] -> setSelectionColor(_handleSelectionColor); + _internal->_lineHandle[i] -> setPickingRatio( _pickingRatio ); + + if (i == 0) _internal->_lineHandle[ 3 ] -> updateCoordinate( 1, pos._sceneCoordinate ); + else _internal->_lineHandle[i-1] -> updateCoordinate( 1, pos._sceneCoordinate ); + + coordinateList.append( handlePointPtr->getCoordinate() ); + } + } /* for handlePoint */ + } + if (_internal->_frameHandle.valid()) + { + if (coordinateList.isEmpty()) + { + const QPointF & center = _internal->_center; + qreal w = _internal->_rectWidth*0.5; + qreal h = _internal->_rectHeight*0.5; + qreal sinPhi = -_internal->_oVec.y(); + qreal cosPhi = _internal->_oVec.x(); + QPointF p; + + p = _internal->rotateVertex( center, -w, -h, sinPhi, cosPhi ); + coordinateList += RubberbandHandle::RubberbandCoordinate( Rubberband::_internal->_mapView, convert(p,Rubberband::_internal->_mapView->map()->spatialReference()) ); + + p = _internal->rotateVertex( center, w, -h, sinPhi, cosPhi ); + coordinateList += RubberbandHandle::RubberbandCoordinate( Rubberband::_internal->_mapView, convert(p,Rubberband::_internal->_mapView->map()->spatialReference()) ); + + p = _internal->rotateVertex( center, w, h, sinPhi, cosPhi ); + coordinateList += RubberbandHandle::RubberbandCoordinate( Rubberband::_internal->_mapView, convert(p,Rubberband::_internal->_mapView->map()->spatialReference()) ); + + p = _internal->rotateVertex( center, -w, h, sinPhi, cosPhi ); + coordinateList += RubberbandHandle::RubberbandCoordinate( Rubberband::_internal->_mapView, convert(p,Rubberband::_internal->_mapView->map()->spatialReference()) ); + } + + _internal->_frameHandle -> setDrawColor(_frameColor); + _internal->_frameHandle -> setFillColor(_frameFillColor); + _internal->_frameHandle -> setSelectionColor(_frameSelectionColor); + _internal->_frameHandle -> updateFrameSceneMargin( _frameHandleMargin ); + _internal->_frameHandle -> updateFrameShape( coordinateList, _internal->_oVec.y(), _internal->_oVec.x() ); + _internal->_frameHandle -> drawDisabled( (_frameHandleMargin < 0) ); + } + + _internal -> updateFrame( Rubberband::_internal->_mapView, coordinateList ); + + if (_internal->_rotateHandle.valid()) + { + _internal -> _rotateHandle -> setSceneOffset( _internal->getSceneOffsetRotationHandle( (_frameHandleMargin > 0) ? _frameHandleMargin : 0 ) ); + _internal -> _rotateHandle -> updateCoordinate( _internal->getScenePositionRotationHandle() ); + } + + } /* RubberbandRectangle::update */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_rectangle.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_rectangle.h new file mode 100755 index 000000000..1e293b46a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_rectangle.h @@ -0,0 +1,274 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define point feature geoemtry +** $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 .........: +** +** 17.06.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband.h" + +#include + + +namespace SituationMap { namespace RB +{ + + class RubberbandPointHandle; + + /** + @class RubberbandRectangle rubberband_rectangle.h + @brief Implementation of a rectangle rubberband + @see Rubberband + */ + class SITUATIONMAP_EXPORT RubberbandRectangle : public Rubberband + { + Q_OBJECT + + /** + @name Construction & Destruction + @} + */ + + friend class RubberbandFactory; + friend class RubberbandEllipse; + friend class RubberbandTransform; + + explicit RubberbandRectangle( SituationMap::Map * map, QObject * parent = nullptr ); + + public: + + ~RubberbandRectangle(); + /** @} */ + + bool selectAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool multiSelectActionPress ( AbstractEventDispatcher * causer, int x, int y ) override; + bool multiSelectActionRelease( AbstractEventDispatcher * causer, int x, int y ) override; + bool setCoordinateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool moveAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool movePickedAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool contextMenuAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool deleteAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool undoAction ( AbstractEventDispatcher * causer, int x, int y ) override; + + + /** + @see rectangleVerticies + */ + bool coordinatesAreValid() const override { return rectangleIsValid(); } + /** + * @brief Get 4 verticies of rectangle + */ + QVector coordinateVector( CoordinateSystem cs = CoordinateSystem::Geo ) const override; + + /** + * @brief Get center point of rectangle, see methode centerPoint + */ + QPointF singleCoordinate( unsigned int idx = 0, CoordinateSystem cs = CoordinateSystem::Geo ) const override; + + /** + * @brief Set 4 verticies of rectangle + */ + void setCoordinateVector( const QVector &, CoordinateSystem cs = CoordinateSystem::Geo ) override; + + + /** + * @brief Returns center point of rectangle + */ + QPointF centerPoint( CoordinateSystem cs = CoordinateSystem::Geo ) const; + + /** + * @brief Sets center point of rectangle + */ + void centerPoint(const QPointF ¢erPoint, CoordinateSystem cs = CoordinateSystem::Geo ); + + /** + * @brief Get 4 verticies of rectangle + */ + QVector rectangleVerticies( CoordinateSystem cs = CoordinateSystem::Geo ) const; + + /** + * @brief Set 4 verticies of rectangle + */ + void rectangleVerticies( const QVector &, CoordinateSystem cs = CoordinateSystem::Geo ); + + /** + * @brief Returns true if a valid rectangle has been set with method \c rectangleVerticies + * The rectangle rubberband is expecting input coordinates defining a rectangle in map coordinate system. Otherwise + * a similar as possible rectangle is used for rubberbanding and the coordinates returned by \c coordinateVector() + * will be different from input coordinates. + */ + bool rectangleIsValid() const; + + /** + * @brief Pass back the rectangle parameter + * For convenience some times it would be an asset to get the parameter like center, width, height and rotation. This + * function pass back these parameter. A coordinate transformation or projection will \b not be done. All values are related to + * the coordinate system of the map. + * Warning, if the map coordinate system doesn't use the same geographical projection as the simulation coordinate system + * the returned parameter may be all wrong. + * + * @param center Center point of the rectangle in map coordinates. + * @param width Width of the rectangle. + * @param height Height of the rectangle. + * @param direction Normalized direction vector related to width axis. + */ + void rectangleParameter( QPointF & center, qreal & width, qreal & height, QPointF & direction, CoordinateSystem cs = CoordinateSystem::Geo ); + + /** + * @brief Build rectangle from rectangle parameter + * For convenience some times it would be an asset to initialize a rectangle rubberband from rectangle the parameter like width, height and rotation. + * A coordinate transformation or projection will \b not be done. All values are related to the coordinate system of the map. + * Warning, if the map coordinate system doesn't use the same geographical projection as the simulation coordinate system all parameter may be wrong. + * + * @param center Center of the rectangle in \b cs coordinate. + * @param width Width of the rectangle. + * @param height Height of the rectangle. + * @param direction Normalized direction vector related to width axis. + * @param cs Coordinate System. + */ + void buildFromRectangleParamter( const QPointF & center, qreal width, qreal height, const QPointF & direction, CoordinateSystem cs = CoordinateSystem::Geo ); + + /** + * @brief Build rectangle relative to the bottom left corner + * For convenience some times it would be an asset to initialize a rectangle rubberband from rectangle the parameter like width, height and rotation. + * A coordinate transformation or projection will \b not be done. All values are related to the coordinate system of the map. + * Warning, if the map coordinate system doesn't use the same geographical projection as the simulation coordinate system all parameter may be wrong. + * + * @param bottomLeft Bottom left corner of the rectangle in \b cs coordinates. + * @param width Width of the rectangle. + * @param height Height of the rectangle. + * @param direction Normalized direction vector related to width axis. + * @param cs Coordinate System. + */ + void buildRectangleRelativeToBottomLeftCorner( const QPointF & bottomLeft, qreal width, qreal height, const QPointF & direction, CoordinateSystem cs = CoordinateSystem::Geo ); + + + /** + * @brief If set to true only quadratic rectangles can be created. + */ + void constrainQuadratic( bool on ); + + /** + * @brief Returns the setting for quadratic constrain. + */ + bool constrainQuadratic() const; + + /** + * @brief If set to true the rectangle can not be rotated. + */ + void constrainOrthogonal( bool on ); + + /** + * @brief Returns the setting fpr orthogonsl constrain. + */ + bool constrainOrthogonal() const; + + + /** + @name Setting Constrains for Width and Height + To define a constrain value a postive values has to be assigned. Negative values will reset the constrain setting to undefined state. + @{ + */ + void minWidth ( qreal x ); + void maxWidth ( qreal x ); + void minHeight( qreal x ); + void maxHeight( qreal x ); + /** @} */ + + + protected: + + /* + Forward declaration of InternalData to hold private member variables. Becareful the base class Rubberband + has similary a class for internal data member with name "InternalData". Don't be confused it is not the same. + */ + struct InternalData; + + InternalData * _internal; + bool _rectangleIsValid; + + /* + Coordinate matrix to describe the change of coordinates of the last user input + */ + MathUtils::Matrix2D _lastCoordinateChange; + + + /* + ModificationMode is used internally to restrict input for special purposes. It will be defined + by derived class + */ + enum class ModificationMode + { + NoneRestriction, + NoneRotation, + TranslationOnly, + ScalingOnly, + RotationOnly + }; + + void setModificationMode( ModificationMode ); + + + void activateButtons() override; + void constructHandles(); + void updateHandles( const QPointF & scenePos, QPointF deltaMove ); + + bool adjustLine( QPointF & deltaMove, bool & constrainsComplied ); + void adjustNeighbouringPoints( RubberbandPointHandle * handlePointPtr, unsigned int handleIdx, QPointF deltaMove ); + + bool checkMinMaxConstrains(const QPointF & vertexScene, QPointF & deltaMovePtr ); + bool checkMinMaxConstrains(const QPointF & vertexScene, bool isVertical ); + + bool constrainsSatisfied( RubberbandPointHandle * handlePointPtr, unsigned int handleIdx, QPointF & deltaMove, bool & constrainsComplied ); + + void useDottedLineHandles( bool on = true ); + + void update() override; + + QPointF getSceneCenter() const; + qreal getSceneWidth() const; + qreal getSceneHeight() const; + + QPointF getCenter() const; + qreal getWidth() const; + qreal getHeight() const; + + void getSceneRotation( qreal & sinA, qreal & cosA ) const; + void getMapRotation ( qreal & sinA, qreal & cosA ) const; + + void buildTmpConstructionPath(const QPointF & start, const QPointF & end, QVector & path, QPointF & sceneCenter, qreal & sceneWidth, qreal & sceneHeight ); + + virtual void buildTmpConstruction( SituationMap::RefPointer & tmpGraphic, QPointF & start, QPointF & end ); + virtual void getTmpConstructionParameter( const QPointF & start, const QPointF & end, QPointF & sceneCenter, qreal & sceneWidth, qreal & sceneHeight ); + + private: + + void setButtonPosition(); + + }; /* RubberbandRectangle */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_smooth_line.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_smooth_line.cpp new file mode 100755 index 000000000..fa4da4ce9 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_smooth_line.cpp @@ -0,0 +1,93 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define multi point feature geometry +** $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 .........: +** +** 17.06.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_smooth_line.h" +#include "rubberband_point_handle.h" + +#include "drawable_smooth_path.h" + +#include "rubberband_internal.h" + + +namespace SituationMap { namespace RB +{ + + struct RubberbandSmoothLine::InternalData + { + SituationMap::RefPointer _smoothLineItem; + }; /* RubberbandSmoothLine::InternalData*/ + + + RubberbandSmoothLine::RubberbandSmoothLine( SituationMap::Map * map, QObject * parent ) + : RubberbandMultiPointFeature(map,true,parent), _internal( new InternalData() ) + { + } /* RubberbandSmoothLine::RubberbandSmoothLine */ + + RubberbandSmoothLine::~RubberbandSmoothLine() + { + delete _internal; + } /* RubberbandSmoothLine::~RubberbandSmoothLine */ + + + void RubberbandSmoothLine::update() + { + QVector vertexVector; + + vertexVector.reserve( Rubberband::_internal->_handleList.size() ); + + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * pointHandlePtr = dynamic_cast(handle.get()); + + if (pointHandlePtr) + { + vertexVector.append( pointHandlePtr -> getCoordinate() ); + } + } + if (vertexVector.size() > 2) + { + if (!_internal->_smoothLineItem) + { + _internal->_smoothLineItem = new DrawableSmoothPath( Rubberband::_internal->_mapView ); + } + + _internal->_smoothLineItem -> enableClosedLine( isClosedEnabled() ); + _internal->_smoothLineItem -> setDrawColor( _drawColor ); + _internal->_smoothLineItem -> setFillColor( _fillColor ); + _internal->_smoothLineItem -> setVisibility( true ); + _internal->_smoothLineItem -> setPath( vertexVector ); + } + else if (_internal->_smoothLineItem) _internal->_smoothLineItem->setVisibility( false ); + + RubberbandMultiPointFeature::update(); + + } /* RubberbandSmoothLine::update */ + + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_smooth_line.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_smooth_line.h new file mode 100755 index 000000000..ce667aa73 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_smooth_line.h @@ -0,0 +1,72 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define multi point feature geoemtry with rounded edges +** $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 .........: +** +** 17.06.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband_multi_point_feature.h" + + +namespace SituationMap { namespace RB +{ + /** + @class RubberbandSmoothLine rubberband_smooth_line.h + @brief Implementation of a multi point feature rubberband with rounded edges. + @see RubberbandFactory, Rubberband, RubberbandMultiPointFeature + + */ + class SITUATIONMAP_EXPORT RubberbandSmoothLine : public RubberbandMultiPointFeature + { + Q_OBJECT + /** + @name Construction & Destruction + @{ + */ + + friend class RubberbandFactory; + + explicit RubberbandSmoothLine( SituationMap::Map * map, QObject * parent = nullptr ); + + public: + + ~RubberbandSmoothLine(); + /** @} */ + + protected: + /* + Forward declaration of InternalData to hold private member variables. Becareful the base class Rubberband + has similary a class for internal data member with name "InternalData". Don't be confused it is not the same. + */ + struct InternalData; + + InternalData * _internal; + + void update() override; + + }; /* RubberbandSmoothLine */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_state_select.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_state_select.cpp new file mode 100755 index 000000000..dcc13802b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_state_select.cpp @@ -0,0 +1,572 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of rubberband class to edit states of point objects +** $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 .........: +** +** 19.10.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_state_select.h" +#include "rubberband_point_handle.h" +#include "rubberband_drawable.h" +#include "rubberband_handle_proxy.h" +#include "rubberband_factory.h" + +#include "rubberband_internal.h" +#include "rubberband_base_internal.h" + + +#include +#include + +#include +#include +#include +#include + +#include "polygon.xpm" + + +namespace SituationMap { namespace RB +{ + + struct RubberbandStateSelect::InternalData + { + Q_DISABLE_COPY(InternalData) + + InternalData() : _logger(getLogger("RubberbandStateSelect")) + {} + + ~InternalData() + { + delete _stateMenu; + } + + QVector _states; + QVector _items; + QList> _stateMenuSelectedHandleList; + LOGGER_T _logger; + + const State * getStateFromValue( int stateValue ) const + { + for (const State &state : _states) + { + if (state.value() == stateValue) return &state; + } + return nullptr; + } /* getStateFromValue */ + + const State * getStateFromItem( const StateItem & item ) const { return getStateFromValue(item.stateValue()); } + + static QPixmap * _polygonIcon; + + QPushButton * _polygonButton = nullptr; + QMenu * _stateMenu = nullptr; + bool _stateMenuIsActive = false; + + }; /* RubberbandMultiPointFeature::InternalData */ + + + QPixmap * RubberbandStateSelect::InternalData::_polygonIcon = nullptr; + + void RubberbandStateSelect::setIconPolygon(const QPixmap & icon) + { + if (InternalData::_polygonIcon == nullptr) InternalData::_polygonIcon = new QPixmap(icon); + else *InternalData::_polygonIcon = icon; + } /* RubberbandStateSelect::setIconPolygon */ + + + RubberbandStateSelect::RubberbandStateSelect( SituationMap::Map * map, QObject * parent ) + : Rubberband(map,parent), _internal(new InternalData()) + { + delete RubberbandBase::_baseInternal->_undoButton; + RubberbandBase::_baseInternal->_undoButton = nullptr; + + _internal->_polygonButton = new QPushButton( (InternalData::_polygonIcon) ? *InternalData::_polygonIcon : QPixmap(polygon_xpm), QString(), RubberbandBase::_baseInternal->_buttonBox ); + + RubberbandBase::_baseInternal->_buttonLayout->insertWidget( 0, _internal->_polygonButton ); + + connect( _internal->_polygonButton, &QPushButton::clicked, this, &RubberbandStateSelect::onDo_areaSelectionBegin ); + + // Check Quartz + connect( Rubberband::_internal->_mapView.data(), &Esri::ArcGISRuntime::MapGraphicsView::viewpointChanged, + this, &RubberbandStateSelect::onDo_updateButtonFixCoordinates ); + connect( Rubberband::_internal->_mapView.data(), &Esri::ArcGISRuntime::MapGraphicsView::rectChanged , + this, &RubberbandStateSelect::onDo_updateButtonFixCoordinates ); + + + setFixedButtonBoxPosition(QPoint(5,5)); + + } /* RubberbandStateSelect::RubberbandStateSelect */ + + + bool RubberbandStateSelect::setStateItems( const QVector & stateVector + , const QVector & itemVector + , CoordinateSystem cs ) + { + if (stateVector.size() < 2) return false; + if (itemVector.isEmpty()) return false; + + // check for all states are valid + for (const State & state : std::as_const(_internal->_states)) + { + if (!state.isValid()) + return false; + } + + bool ok = true; + + _internal -> _states = stateVector; + _internal -> _items = itemVector; + + for (QVector::iterator it = _internal->_items.begin(); it != _internal->_items.end(); ++it) + { + if (it->isValid()) + { + const State * statePtr = _internal->getStateFromItem( *it ); + + if (statePtr == nullptr) + { + ok = false; + statePtr = & _internal->_states.first(); + } + + const QPointF &mapPos = projectCoordinateToMap( cs, it->coordinate() ); + + Esri::ArcGISRuntime::Point mapCoordinate( mapPos.x(), mapPos.y(), Rubberband::_internal->_mapView->map()->spatialReference() ); + RubberbandPointHandle * handlePtr = new RubberbandPointHandle( Rubberband::_internal->_mapView + , Rubberband::_internal->_mapScene + , mapCoordinate + , statePtr->icon() ); + if (handlePtr) + { + handlePtr->setSelection(false); + handlePtr->setUserData( &(*it) ); + Rubberband::_internal->_handleList.append( SituationMap::RefPointer(handlePtr) ); + } + } + else ok = false; + } + update(); + activateButtons(); + buildStateMenu(); + return ok; + } /* RubberbandStateSelect::setStateItems */ + + + const QVector & RubberbandStateSelect::stateItems() const + { + return _internal->_items; + } /* RubberbandStateSelect::stateItems */ + + + void RubberbandStateSelect::activateButtons() + { + _baseInternal->_hasUndoButton = false; + _baseInternal->_applyIsPossible = (!Rubberband::_internal->_handleList.isEmpty()); + _baseInternal->_undoIsPossible = false; + Rubberband::activateButtons(); + } /* RubberbandStateSelect::activateButton */ + + + void RubberbandStateSelect::toggleState( RubberbandPointHandle * handlePtr ) + { + if (_internal->_states.size() < 2) return; + if (handlePtr == nullptr) return; + + StateItem * itemPtr = static_cast(handlePtr->userData()); + + if (itemPtr == nullptr) return; + + if (_internal->_states.size() == 2) + { + const State * statePtr = _internal -> getStateFromItem( *itemPtr ); + + if (statePtr != nullptr) + { + if (statePtr == &_internal->_states.first()) statePtr = &_internal->_states.last(); + else statePtr = &_internal->_states.first(); + } + else statePtr = &_internal->_states.first(); + + itemPtr->_stateValue = statePtr->value(); + handlePtr->setIcon( statePtr->icon() ); + } + + } /* RubberbandStateSelect::toggleState */ + + + void RubberbandStateSelect::setState( RubberbandPointHandle * handlePtr, int stateValue ) + { + if (handlePtr == nullptr) return; + + const State * statePtr = _internal -> getStateFromValue(stateValue); + StateItem * itemPtr = static_cast(handlePtr->userData()); + + if (itemPtr == nullptr) return; + if (statePtr == nullptr) return; + + itemPtr->_stateValue = stateValue; + handlePtr->setIcon( statePtr->icon() ); + + } /* RubberbandStateSelect::setState */ + + + void RubberbandStateSelect::buildStateMenu() + { + if (_internal->_states.isEmpty()) return; + + _internal->_stateMenu = new QMenu(); + + for (const State & state : std::as_const(_internal->_states)) + { + QAction * action = _internal->_stateMenu->addAction( state.icon(), state.description() ); + action -> setData( state.value() ); + + connect( action, &QAction::triggered, this, &RubberbandStateSelect::onDo_setState ); + } + + connect( _internal->_stateMenu, &QMenu::aboutToShow, this, [this]() + { + this->_internal->_stateMenuSelectedHandleList.clear(); + + _baseInternal->_buttonBox->setDisabled(true); + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + if (handle->isSelected()) + { + this->_internal->_stateMenuSelectedHandleList << handle; + } + } + } ); + + connect( _internal->_stateMenu, &QMenu::aboutToHide, this, [this]() + { + _baseInternal->_buttonBox->setEnabled(true); + deSelectHandles(); + } ); + + } /* RubberbandStateSelect::buildStateMenu */ + + + void RubberbandStateSelect::onDo_setState(bool) + { + QAction * action = (QAction*) sender(); + + if (action == nullptr) return; + + int stateValue = action->data().toInt(); + + for (auto handle : std::as_const(_internal->_stateMenuSelectedHandleList)) + setState( dynamic_cast(handle.get()), stateValue ); + + _internal -> _stateMenuSelectedHandleList.clear(); + _internal -> _stateMenuIsActive = false; + + update(); + + } /* RubberbandStateSelect::onDo_setState */ + + + void RubberbandStateSelect::chooseStateMenu() + { + // popup a window with states the user can choose + _internal->_stateMenu->popup( QCursor::pos() ); + _internal -> _stateMenuIsActive = true; + } /* RubberbandStateSelect::chooseStateMenu */ + + + bool RubberbandStateSelect::selectAction( AbstractEventDispatcher*, int x, int y ) + { + DEBUG_LOG(_internal->_logger) << LOGFUNCTION << x << " ; " << y; + + _internal->_stateMenuSelectedHandleList.clear(); + + if (rubberbandIsAlive()) + { + if (_internal->_stateMenuIsActive) return true; + + QPointF scenePos(pixel2ScenePosition(x,y)); + + if (_baseInternal->buttonProxyActive(scenePos)) return false; + + RubberbandHandle * handlePtr = pickHandle(x,y); + + if (handlePtr) + { + handlePtr->setSelection(); + enableSelectedState(); + + update(); + deactivateButtons(); + return true; + } + } + return false; + } /* RubberbandStateSelect::selectAction */ + + + bool RubberbandStateSelect::multiSelectActionPress(AbstractEventDispatcher*, int x, int y) + { + DEBUG_LOG(_internal->_logger) << LOGFUNCTION << x << " ; " << y; + + if (rubberbandIsAlive()) + { + if (_internal -> _stateMenuIsActive) return true; + + RubberbandHandle * handlePtr = pickHandle(x,y); + + if (handlePtr) + { + handlePtr->setSelection(); + enableSelectedState(); + enableMultiSelectedState(); + + update(); + deactivateButtons(); + return true; + } + } + return false; + } /* RubberbandStateSelect::multiSelectActionPress */ + + + bool RubberbandStateSelect::multiSelectActionRelease( AbstractEventDispatcher*, int, int ) + { + return false; + } /* RubberbandStateSelect::multiSelectActionRelease */ + + + bool RubberbandStateSelect::setCoordinateAction( AbstractEventDispatcher * causer, int x, int y ) + { + DEBUG_LOG(_internal->_logger) << LOGFUNCTION << x << " ; " << y; + + if (rubberbandIsAlive()) + { + Rubberband::_internal->_lastClickPosition = pixel2ScenePosition(x,y); + + if (_internal->_stateMenuIsActive) + { + _internal -> _stateMenuIsActive = false; + disenableSelectedState(); + disenableMultiSelectedState(); + activateButtons(); + return true; + } + + if (isEnabledSelectedState()) + { + bool handlePicked = (pickHandle(x,y) != nullptr); + bool toggleMode = (_internal->_states.size() == 2); + bool eventHandled = false; + + if (!toggleMode) + { + disenableSelectedState(); + disenableMultiSelectedState(); + + chooseStateMenu(); + return true; + } + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); + if ((handlePointPtr) && (handlePointPtr->isSelected())) + { + handlePointPtr->setSelection(false); + if ((handlePicked) && (toggleMode)) toggleState( handlePointPtr ); + eventHandled = true; + } + } + + disenableSelectedState(); + disenableMultiSelectedState(); + activateButtons(); + return eventHandled; + } + else Rubberband::setCoordinateAction(causer,x,y); + } + return false; + } /* RubberbandStateSelect::setCoordinateAction */ + + + bool RubberbandStateSelect::moveAction(AbstractEventDispatcher*, int x, int y) + { + DEBUG_LOG(_internal->_logger) << LOGFUNCTION << x << " ; " << y; + + if (rubberbandIsAlive()) + { + if (_internal -> _stateMenuIsActive) return false; + + bool eventHandled = false; + + QPointF scenePosition(pixel2ScenePosition(x,y)); + + if (_baseInternal->buttonProxyActive(scenePosition)) return false; + + if (Rubberband::_internal->_mapView != nullptr) + { + if (!isEnabledMultiSelectedState()) deSelectHandles(); + + RubberbandHandle * handlePtr = pickHandle(x,y); + if (handlePtr != nullptr) + { + handlePtr->setSelection(); + eventHandled = true; + } + } + + if (eventHandled) update(); + } + return false; + } /* RubberbandStateSelect::moveAction */ + + + bool RubberbandStateSelect::movePickedAction(AbstractEventDispatcher * causer, int x, int y ) + { + DEBUG_LOG(_internal->_logger) << LOGFUNCTION << x << " ; " << y; + + Rubberband::movePickedAction(causer,x,y); + return false; // isEnabledSelectedState() || _internal->_stateMenuIsActive; + } /* RubberbandStateSelect::movePickedAction */ + + + bool RubberbandStateSelect::contextMenuAction( AbstractEventDispatcher*, int, int ) + { + if (!_internal -> _stateMenuIsActive) chooseStateMenu(); + return true; + } /* RubberbandStateSelect::contextMenuAction */ + + + void RubberbandStateSelect::onDo_areaSelectionBegin() + { + bool mRbands = _baseInternal->_map->rubberbandFactory().allowMultipleRubberbands(); + _baseInternal->_map->rubberbandFactory().allowMultipleRubberbands(true); + { + RB::RubberbandMultiPointFeature * selectionRB = _baseInternal->_map->rubberbandFactory().create(this); + + if (selectionRB == nullptr) + { + qWarning() << LOGFUNCTION << "Creating rubberband multi points feature failed"; + } + else + { + selectionRB->setAutoDelete(true); // when finished rubberband will commit suicide + selectionRB->setDrawColor( QColor(255,0,0,255) ); + selectionRB->setFillColor( QColor( 0,0,0,100) ); + + selectionRB -> enableClosed( true ); + selectionRB -> enableFinishAfterConstruction( true ); + + selectionRB -> setFixedButtonBoxPosition(QPoint(15,15)); + + QObject::connect( selectionRB, &RB::Rubberband::apply, this, &RubberbandStateSelect::onDo_areaSelectionApply ); + QObject::connect( selectionRB, &RB::Rubberband::abort, this, &RubberbandStateSelect::onDo_areaSelectionAbort ); + + RubberbandBase::_baseInternal->_buttonBox->setDisabled(true); + } + } + _baseInternal->_map->rubberbandFactory().allowMultipleRubberbands( mRbands ); + } /* RubberbandStateSelect::onDo_areaSelectionBegin */ + + + void RubberbandStateSelect::onDo_areaSelectionApply( RubberbandBase * selectionRB ) + { + RubberbandBase::_baseInternal->_buttonBox->setEnabled(true); + if (selectionRB) + { + QVector polygonVertexList = selectionRB->coordinateVector( CoordinateSystem::Map ); + + // Looking for all handle which are inside polygon and select + + unsigned int hitCounter = 0; + + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); + if (handlePointPtr) + { + const Esri::ArcGISRuntime::Point &v = handlePointPtr->getCoordinate()._mapCoordinate; + bool inside = false; + int nvert = polygonVertexList.size(); + + for (int i = 0, j = nvert-1; i < nvert; j = i++) + { + const QPointF & vertex_i = polygonVertexList[i]; + const QPointF & vertex_j = polygonVertexList[j]; + + if ((vertex_i.y()>v.y()) != (vertex_j.y()>v.y())) + { + if (v.x() < (vertex_j.x() - vertex_i.x()) * (v.y() - vertex_i.y()) / (vertex_j.y() - vertex_i.y()) + vertex_i.x()) + { + inside = !inside; + } + } + } + if (inside) + { + hitCounter++; + handlePointPtr->setSelection(); + } + } + } + if (hitCounter > 0) + { + if (_internal->_states.size() == 2) + { + for (auto handle : std::as_const(Rubberband::_internal->_handleList)) + { + if (!handle->isSelected()) continue; + handle->setSelection(false); + toggleState( dynamic_cast(handle.get()) ); + } + } + else + { + chooseStateMenu(); + // ????? enableMultiSelectedState(); + } + } + } + } /* RubberbandStateSelect::onDo_areaSelectionApply */ + + + void RubberbandStateSelect::onDo_areaSelectionAbort( RubberbandBase * ) + { + _baseInternal->_buttonBox->setEnabled(true); + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Really nothing else to do here? + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + } /* RubberbandStateSelect::onDo_areaSelectionAbort */ + + + void RubberbandStateSelect::update() + { + Rubberband::update(); + + onDo_updateButtonFixCoordinates(); + + } /* RubberbandPointCluster::update */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_state_select.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_state_select.h new file mode 100755 index 000000000..018708c8a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_state_select.h @@ -0,0 +1,180 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to edit states of point objects +** $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 .........: +** +** 18.06.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband.h" + + +namespace SituationMap { namespace RB +{ + class RubberbandPointHandle; + + + /** + @class RubberbandPointCluster rubberband_point_cluster.h + @brief Implementation of a rubberband to input single coodinates for building point feature clusters + @see Rubberband, RubberbandPointFeature + */ + class SITUATIONMAP_EXPORT RubberbandStateSelect : public Rubberband + { + Q_OBJECT + /** + @name Construction & Destruction + @{ + */ + + friend class RubberbandFactory; + + explicit RubberbandStateSelect( SituationMap::Map * map, QObject * parent = nullptr ); + + public: + + class State + { + public: + State() = default; + State(int value, const QString & description, const QPixmap & icon) + : _description(description), _icon(icon), _value(value), _valid(true) + { + } + + inline bool isValid() const { return _valid; } + inline int value() const { return _value; } + inline const QString & description() const { return _description; } + inline const QPixmap & icon() const { return _icon; } + + protected: + + QString _description; + QPixmap _icon; + int _value = 0; + bool _valid = false; + + }; /* State */ + + + class StateItem + { + friend class RubberbandStateSelect; + + public: + + StateItem() : _valid(false), _id(0), _description(), _coordinate(), _stateValue(0) {;} + + StateItem( quint32 id, const QString &description, const QPointF & coordinate, int stateValue ) + : _valid(true), _id(id), _description(description), _coordinate(coordinate), _stateValue(stateValue) + { + } /* StateItem */ + + ~StateItem() = default; + + StateItem(const StateItem &) = default; + StateItem & operator = (const StateItem &) = default; + + inline bool isValid() const { return _valid; } + inline quint32 id() const { return _id; } + inline const QPointF & coordinate() const { return _coordinate; } + inline int stateValue() const { return _stateValue; } + + protected: + + bool _valid; + quint32 _id; + QString _description; + QPointF _coordinate; + int _stateValue; + + }; /* StateItem */ + + + virtual ~RubberbandStateSelect() = default; + + RubberbandStateSelect(const RubberbandStateSelect &) = delete; + RubberbandStateSelect & operator = (const RubberbandStateSelect &) = delete; + /** @} */ + + bool selectAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool multiSelectActionPress ( AbstractEventDispatcher * causer, int x, int y ) override; + bool multiSelectActionRelease( AbstractEventDispatcher * causer, int x, int y ) override; + bool setCoordinateAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool moveAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool movePickedAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool contextMenuAction ( AbstractEventDispatcher * causer, int x, int y ) override; + bool undoAction ( AbstractEventDispatcher * causer, int x, int y ) override { return contextMenuAction(causer,x,y); } + + bool setStateItems( const QVector & stateVector, + const QVector & itemVector, + CoordinateSystem cs = CoordinateSystem::Geo ); + + const QVector & stateItems() const; + + static void setIconPolygon( const QPixmap & ); + + protected: + + /* + Forward declaration of InternalData to hold private member variables. Becareful the base class Rubberband + has similary a class for internal data member with name "InternalData". Don't be confused it is not the same. + */ + struct InternalData; + + InternalData * _internal; + + void activateButtons() override; + + void update() override; + + void applyButtonClicked() override { applyAndEndAction( _windowEventDispatcher, 0.0, 0.0 ); } + + void buildStateMenu(); + void chooseStateMenu(); + + void toggleState( RubberbandPointHandle * handlePtr ); + void setState( RubberbandPointHandle * handlePtr, int stateValue ); + + + + protected slots: + + void onDo_areaSelectionBegin(); + void onDo_areaSelectionApply( RubberbandBase* ); + void onDo_areaSelectionAbort( RubberbandBase* ); + + void onDo_setState( bool ); + + private: + + void setCoordinateVector(const QVector &, CoordinateSystem ) override {} + + QVector coordinateVector( CoordinateSystem ) const override { return QVector(); } + QPointF singleCoordinate( unsigned int, CoordinateSystem ) const override { return QPointF(); } + + }; /* RubberbandStateSelect */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform.cpp new file mode 100755 index 000000000..3d8cddd74 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform.cpp @@ -0,0 +1,92 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of rubberband class to define moving transforms +** $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 .........: +** +** 07.04.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_transform.h" + +#include "rubberband_internal.h" + +#include + + +namespace SituationMap { namespace RB +{ + + struct RubberbandTransform::InternalData + { + Q_DISABLE_COPY(InternalData) + MathUtils::Matrix2D _transformCoordinates; + LOGGER_T _logger; + + InternalData() + : _transformCoordinates() + , _logger(getLogger("RubberbandTransform")) + { + } /* InternalData */ + }; /* RubberbandTransform::InternalData */ + + + + + RubberbandTransform::RubberbandTransform( SituationMap::Map * map, QObject * parent ) + : RubberbandRectangle(map,parent) + , _internal(new InternalData()) + { + } /* RubberbandTransform::RubberbandTransform */ + + + RubberbandTransform::~RubberbandTransform() + { + delete _internal; + _internal = nullptr; + } /* RubberbandTransform::~RubberbandTransform */ + + + const MathUtils::Matrix2D & RubberbandTransform::transformMatrix() const { return _internal->_transformCoordinates; } + + + void RubberbandTransform::update() + { + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * It is importent here to call RubberbandRectangle::update(); first to do an update, because + * in other case the RubberbandRectangle::_lastCoordinateChange variable will have wrong values. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + RubberbandRectangle::update(); + + if (!_lastCoordinateChange.isIdentity()) + { + _internal->_transformCoordinates = _lastCoordinateChange * _internal->_transformCoordinates; + } + + emit updatePreview( this, _lastCoordinateChange ); + + _lastCoordinateChange.setToIdentity(); // reset matrix + + } /* RubberbandTransform::update */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform.h new file mode 100755 index 000000000..dbadbbd2a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform.h @@ -0,0 +1,99 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define moving, scale and rotating transforms +** $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 .........: +** +** 22.06.2016, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband_rectangle.h" + +namespace SituationMap { namespace RB +{ + + /** + @class RubberbandTransform rubberband_transform.h + @brief Implementation of a rubberband to move, scale and rotate features + @see Rubberband + */ + class SITUATIONMAP_EXPORT RubberbandTransform : public RubberbandRectangle + { + Q_OBJECT + + friend class RubberbandFactory; + + friend class RubberbandTransformMove; + friend class RubberbandTransformRotate; + friend class RubberbandTransformScale; + + + /** + @name Construction & Destruction + @} + */ + + explicit RubberbandTransform( SituationMap::Map * map, QObject * parent = nullptr ); + + public: + + ~RubberbandTransform(); + /** @} */ + + /** + Provides the transformation matrix + */ + const MathUtils::Matrix2D & transformMatrix() const; + + signals: + + /** + The updatePreview signal is emitted before the rubbberband itself will be redrawn. Connecting this signal can be + used to draw an preview e.g in case of transform rubberbands. + + @param rbnd pointer to this Rubberband + @param mat transformation matrix to transform features for preview + @see Rubberband + */ + void updatePreview( Rubberband * rbnd, const MathUtils::Matrix2D & mat ); + + protected: + + /* + Forward declaration of InternalData to hold private member variables. Becareful the base class Rubberband + has similary a class for internal data member with name "InternalData". Don't be confused it is not the same. + */ + struct InternalData; + + InternalData * _internal; + + void update() override; + + private: + + void setButtonPosition(); + + }; /* RubberbandTransform */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_move.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_move.cpp new file mode 100755 index 000000000..b2c810a38 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_move.cpp @@ -0,0 +1,48 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of rubberband class to define moving transforms +** $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 .........: +** +** 07.04.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_transform_move.h" + +namespace SituationMap { namespace RB +{ + + RubberbandTransformMove::RubberbandTransformMove( SituationMap::Map * map, QObject * parent ) + : RubberbandTransform(map,parent) + { + setModificationMode( ModificationMode::TranslationOnly ); + } /* RubberbandTransformMove::RubberbandTransformMove */ + + + QPointF RubberbandTransformMove::translationVector() const + { + const MathUtils::Matrix2D & M = transformMatrix(); + return QPointF(M(0,2),M(1,2)); + } /* RubberbandTransformMove::translationVector() const */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_move.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_move.h new file mode 100755 index 000000000..492448105 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_move.h @@ -0,0 +1,67 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define moving transforms +** $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 .........: +** +** 17.06.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband_transform.h" + + +namespace SituationMap { namespace RB +{ + + class RubberbandPointHandle; + + /** + @class RubberbandTransformMove rubberband_transform_move.h + @brief Implementation of a rubberband to move features + @see Rubberband, RubberbandRectangle, RubberbandTransform + */ + class SITUATIONMAP_EXPORT RubberbandTransformMove : public RubberbandTransform + { + Q_OBJECT + + friend class RubberbandFactory; + + /** + @name Construction & Destruction + @} + */ + + explicit RubberbandTransformMove( SituationMap::Map * map, QObject * parent = nullptr ); + + /** + Returns vector of translation + */ + QPointF translationVector() const; + + public: + /** @} */ + + }; /* RubberbandTransformMove */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_rotate.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_rotate.cpp new file mode 100755 index 000000000..ee1295e76 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_rotate.cpp @@ -0,0 +1,62 @@ +/** +** +** @RDE_HEADER@ +** Implementation of rubberband class to define rotating transforms +** $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 .........: +** +** 07.04.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_transform_rotate.h" + +#include + + +namespace SituationMap { namespace RB +{ + + + RubberbandTransformRotate::RubberbandTransformRotate( SituationMap::Map * map, QObject * parent ) + : RubberbandTransform(map,parent) + { + setModificationMode( ModificationMode::RotationOnly ); + } /* RubberbandTransformRotate::RubberbandTransformRotate */ + + qreal RubberbandTransformRotate::rotationSin() const + { + return transformMatrix()(0,1); + } // RubberbandTransformRotate::rotationSin + + + qreal RubberbandTransformRotate::rotationCos() const + { + return transformMatrix()(0,0); + } // RubberbandTransformRotate::rotationCos + + + qreal RubberbandTransformRotate::rotationAngle() const + { + return MathUtils::getMathAngle(rotationSin(),rotationCos()); + } // RubberbandTransformRotate::rotationAngle + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_rotate.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_rotate.h new file mode 100755 index 000000000..ecf1c86b2 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_rotate.h @@ -0,0 +1,77 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define moving transformation +** $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 .........: +** +** 07.04.2016, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband_transform.h" + + +namespace SituationMap { namespace RB +{ + + class RubberbandPointHandle; + + /** + @class RubberbandTransformRotate rubberband_transform_rotate.h + @brief Implementation of a rubberband to rotate features + @see Rubberband + */ + class SITUATIONMAP_EXPORT RubberbandTransformRotate : public RubberbandTransform + { + Q_OBJECT + + friend class RubberbandFactory; + + /** + @name Construction & Destruction + @} + */ + + explicit RubberbandTransformRotate( SituationMap::Map * map, QObject * parent ); + + public: + /** @} */ + + /** + Returns sinus of rotation angle + */ + qreal rotationSin() const; + + /** + Return cosinus of roation angle + */ + qreal rotationCos() const; + + /** + Return rotation angle (math.) in radians + */ + qreal rotationAngle() const; + + }; /* RubberbandTransformRotate */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_scale.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_scale.cpp new file mode 100755 index 000000000..6d0d4370d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_scale.cpp @@ -0,0 +1,47 @@ +/** +** +** @RDE_HEADER@ +** Implementation of rubberband class to define scaling transforms +** $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 .........: +** +** 07.04.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "rubberband_transform_scale.h" + + +namespace SituationMap { namespace RB +{ + + + + RubberbandTransformScale::RubberbandTransformScale( SituationMap::Map * map, QObject * parent ) + : RubberbandTransform(map,parent) + { + setModificationMode( ModificationMode::ScalingOnly ); + } /* RubberbandTransformScale::RubberbandTransformScale */ + + + qreal RubberbandTransformScale::scaleX() const { return transformMatrix()(0,0); } + qreal RubberbandTransformScale::scaleY() const { return transformMatrix()(1,1); } + +} } /* namespace SituationMap::RB */ \ No newline at end of file diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_scale.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_scale.h new file mode 100755 index 000000000..190541b33 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/rubberband_transform_scale.h @@ -0,0 +1,64 @@ +/** +** +** @RDE_HEADER@ +** Definition of rubberband class to define scaling transformation +** $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 .........: +** +** 07.04.2016, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband_transform.h" + + +namespace SituationMap { namespace RB +{ + + /** + @class RubberbandTransformScale rubberband_transform_scale.h + @brief Implementation of a rubberband to scale features + @see Rubberband + */ + class SITUATIONMAP_EXPORT RubberbandTransformScale : public RubberbandTransform + { + Q_OBJECT + + friend class RubberbandFactory; + + /** + @name Construction & Destruction + @} + */ + + explicit RubberbandTransformScale( SituationMap::Map * map, QObject * parent = nullptr ); + + public: + /** @} */ + + + qreal scaleX() const; /**< Returns scaling factor in x direction */ + qreal scaleY() const; /**< Returns scaling factor in y direction */ + + }; /* RubberbandTransformRotate */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/sphere_pattern.xpm b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/sphere_pattern.xpm new file mode 100755 index 000000000..b04c1c9b1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/sphere_pattern.xpm @@ -0,0 +1,32 @@ +/* XPM */ +static const char *sphere_pattern_xpm[] = { +"16 16 13 1", +" c None", +". c #FFFFFF", +"+ c #F8F8F8", +"@ c #DCDCDC", +"# c #BFBFBF", +"$ c #E3E3E3", +"% c #929292", +"& c #6C6C6C", +"* c #484848", +"= c #393939", +"- c #F3F3F3", +"; c #B5B5B5", +"> c #0A0A0A", +" ...... ", +" .....+.... ", +" ..@++++++... ", +" ..#$++....++.. ", +" .%#$+.......+. ", +"..%#$........+..", +".&%#@........+..", +".*%%#$........+.", +".=&%##@-.....+-.", +".=*&%##@$--++-@.", +".;=*&%##@@$$@@#.", +" .>==&%%######. ", +" ..>>==&&%%%%.. ", +" ..>>>>==*=.. ", +" ..;>>>>;.. ", +" ...... "}; diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/transient_tactical_symbol.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/transient_tactical_symbol.cpp new file mode 100755 index 000000000..c0d9dbdad --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/transient_tactical_symbol.cpp @@ -0,0 +1,88 @@ + +/** +** +** @RDE_HEADER@ +** Implementation of preview for tactical symbols +** $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 .........: +** +** 27.03.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "transient_tactical_symbol.h" + +#include +#include +#include + + +namespace SituationMap { namespace RB +{ + + TransientTacticalSymbol::TransientTacticalSymbol( MapImpl * mapImpl, const QString & sidc ) + { + QString layerIdent; + if (mapImpl) + { + layerIdent = mapImpl->layerFactoryImpl().rubberbandLayer(); + } + + if (layerIdent.isEmpty()) + { + qWarning() << LOGFUNCTION << "not layername found"; + } + + _tacticalRubberbandObj = new SidcSymbolImpl( sidc, QStringLiteral("TransientTacticalSymbol:sidc=%1").arg(sidc) ); + _tacticalRubberbandObj -> init( mapImpl, layerIdent ); + //_tacticalRubberbandObj -> setDrawOrder( 2000 ); + + } /* PreviewTaticalSymbol::PreviewTaticalSymbol */ + + TransientTacticalSymbol::~TransientTacticalSymbol() + { + delete _tacticalRubberbandObj; + } /* TransientTacticalSymbol::~TransientTacticalSymbol */ + + + void TransientTacticalSymbol::update(const QVector & coordinateVector) + { + PositionCont controlPoints; + controlPoints.reserve(coordinateVector.size()); + + for (const RubberbandDrawable::RubberbandCoordinate &pos : coordinateVector) + { + const Esri::ArcGISRuntime::Point & p = pos._mapCoordinate; + + controlPoints.append(Point3D(p.x(), p.y(), p.z())); + } + _tacticalRubberbandObj -> setPositionsTranslated( controlPoints ); + try + { + _tacticalRubberbandObj -> updateView(); + } + catch (...) + { + qWarning() << LOGFUNCTION << "Exception from TacticalAppearance::updateView() catched!!!!"; + } + } /* TransientTacticalSymbol::update */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/transient_tactical_symbol.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/transient_tactical_symbol.h new file mode 100755 index 000000000..5d2144ed7 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/transient_tactical_symbol.h @@ -0,0 +1,79 @@ +/** +** +** @RDE_HEADER@ +** Definition of tansient tactical symbols while rubberband input 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 .........: +** +** 01.09.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include "rubberband_drawable.h" + +#include +#include + +#include + +namespace SituationMap { class SidcSymbolImpl; } + +namespace SituationMap { namespace RB +{ + + /** + @class TransientTacticalSymbol transient_tactical_symbol.h + The class \c TransientTacticalSymbol a map object of type RubberbandObjectImpl to display a tactical symbol + transiently while rubberband input is done. The tactical symbol is defined by a \a sidc code which is passed + to the ctor as second parameter. + + @see RubberbandObjectImpl + */ + class TransientTacticalSymbol : public QObject, public SituationMap::Referenced + { + Q_OBJECT + public: + + + /** + @name Construction & Destruction + @{ + */ + explicit TransientTacticalSymbol( MapImpl *, const QString & sidc ); + virtual ~TransientTacticalSymbol(); + + TransientTacticalSymbol(const TransientTacticalSymbol &) = delete; + TransientTacticalSymbol & operator = (const TransientTacticalSymbol &) = delete; + /** @} */ + + virtual void update( const QVector & coordinateVector ); + + protected: + + SidcSymbolImpl * _tacticalRubberbandObj; + + protected slots: + + + }; /* TransientTacticalSymbol */ + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/undo.xpm b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/undo.xpm new file mode 100755 index 000000000..4193abac9 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/undo.xpm @@ -0,0 +1,134 @@ +/* XPM */ +static const char * undo_xpm[] = { +"24 24 107 2", +" c None", +". c #BEAD52", +"+ c #D4C15C", +"@ c #CCBA59", +"# c #A49546", +"$ c #F7E16A", +"% c #FEE65E", +"& c #FEE350", +"* c #FEE248", +"= c #FEE34C", +"- c #FEE662", +"; c #DAC75E", +"> c #CBBA57", +", c #FDE456", +"' c #FCDE32", +") c #FCDD2D", +"! c #FCE143", +"~ c #E5D15F", +"{ c #C1B152", +"] c #FBE047", +"^ c #FADB2B", +"/ c #FADE3C", +"( c #BBAB4F", +"_ c #94873F", +": c #F9DF49", +"< c #F8D929", +"[ c #F5DC4B", +"} c #EFD751", +"| c #F6D627", +"1 c #EACC25", +"2 c #E3C524", +"3 c #F1D226", +"4 c #F6D72A", +"5 c #AA9A46", +"6 c #BAAA4B", +"7 c #F3D52B", +"8 c #F3D424", +"9 c #E1C421", +"0 c #C0A81C", +"a c #D3BD43", +"b c #EBD242", +"c c #F1D122", +"d c #D3B71E", +"e c #B29B19", +"f c #E7CB37", +"g c #97893C", +"h c #EECF25", +"i c #EECE1F", +"j c #E7C81E", +"k c #D8BB1C", +"l c #ECCE28", +"m c #CDB741", +"n c #EBCC1C", +"o c #EBCD1F", +"p c #CBB84D", +"q c #DCC857", +"r c #B5A447", +"s c #E6CA31", +"t c #E8C919", +"u c #D0B416", +"v c #DFC118", +"w c #E8CA26", +"x c #EACF3B", +"y c #ECD552", +"z c #E3CE59", +"A c #CAB74F", +"B c #E6C71C", +"C c #E6C617", +"D c #BEA51A", +"E c #D3B513", +"F c #E3C314", +"G c #E4C51D", +"H c #E6CA2E", +"I c #DBBC13", +"J c #917F20", +"K c #917E15", +"L c #E0C011", +"M c #DDBF17", +"N c #C0AE49", +"O c #D8BA0E", +"P c #DDBE0E", +"Q c #DEBF12", +"R c #E1C62C", +"S c #E5CE4A", +"T c #D3BF4F", +"U c #928436", +"V c #AC930E", +"W c #DBBB0C", +"X c #DEC01D", +"Y c #E2C93D", +"Z c #CCB84B", +"` c #D7B809", +" . c #D8B909", +".. c #CCAF09", +"+. c #BDA107", +"@. c #D6B607", +"#. c #CFB007", +"$. c #897504", +"%. c #D4B405", +"&. c #D0B105", +"*. c #978004", +"=. c #C9AA03", +"-. c #D2B203", +";. c #D0B003", +">. c #A48B03", +",. c #968109", +"'. c #AD9301", +" ", +" ", +" . + @ ", +" # $ % & * = - ; ", +" > , ' ) ) ) ) ) ! ~ ", +" { ] ^ ^ ^ ^ ^ ^ ^ ^ / ( ", +" _ : < < < < < < < < < < [ ", +" } | | | | | | 1 2 3 | | 4 5 ", +" 6 7 8 8 8 8 9 0 8 8 a ", +" b c c c c d e c f ", +" g h i i i j k l ", +" m n n n n o ", +" p q r s t t t u v ", +" w x y z A B C C C D ", +" E F F G H F F F I J ", +" K L L L L L L L M N ", +" O P P P P P P Q R S T U ", +" V W W W W W W W W W X Y Z ", +" ` . . . . . . . ... ", +" +.@.@.@.@.@.@.#.$. ", +" %.%.%.%.&.*. ", +" =.-.;.>. ", +" ,.'. ", +" "}; diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/winkel_icon.xpm b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/winkel_icon.xpm new file mode 100755 index 000000000..65ed9a370 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Rubberbands/winkel_icon.xpm @@ -0,0 +1,84 @@ +/* XPM */ +static const char * winkelIcon_xpm[] = { +"40 40 41 1", +" c None", +". c #250075", +"+ c #250074", +"@ c #260074", +"# c #250073", +"$ c #240072", +"% c #250071", +"& c #22006F", +"* c #24006E", +"= c #280078", +"- c #290076", +"; c #270076", +"> c #240073", +", c #250072", +"' c #240071", +") c #240070", +"! c #23006E", +"~ c #23006D", +"{ c #22006D", +"] c #23006C", +"^ c #21006D", +"/ c #270075", +"( c #230073", +"_ c #24006F", +": c #22006E", +"< c #22006C", +"[ c #240075", +"} c #260075", +"| c #230070", +"1 c #24006C", +"2 c #230072", +"3 c #260073", +"4 c #23006F", +"5 c #230071", +"6 c #24006D", +"7 c #210064", +"8 c #210066", +"9 c #200065", +"0 c #210065", +"a c #200063", +"b c #1D0062", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" .++ ", +" @+#+$%&* ", +" =-;#>,')!~{]^ ", +" .;/>(>)_:~~~<~< ", +" [}###'|&:~<<~~1!* ", +" .2$3'')4::{~~~~~^ ", +" $%'5' 4{6<~~~~_ ", +" 4)| <~~~~! ", +" :~~~!~ ", +" 4{~~~< ", +" ~~~~{_ ", +" ~~~6~ ", +" ~~~~: ", +" ]~~{~! ", +" ~~~{~ ", +" ~~~]! ", +" ~~~~:| ", +" {<~~~! ", +" ~~~~!~ ", +" <~~~~! ", +" :{{<~~~*] ", +" !::]1!{{{{<~{~~~~]! ", +" ^64~~]!~~~<~~~~~*6{ ", +" {!&!]~~~~~~~<]~*~ ", +" :*]~~~~~~~<_< ", +" 7890009ab ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/CMakeLists.txt new file mode 100755 index 000000000..285567da1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/CMakeLists.txt @@ -0,0 +1,44 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +# LIBRARY +SET(SideBar_SRCS + side_bar.cpp + side_bar.h + side_bar_export.h + side_bar_item.cpp + side_bar_item.h + side_bar_model.cpp + side_bar_model.h +) + +SET(SidBar_ICONS +# state_icon_b.xpm +# state_icon_a.xpm +# state_icon_c.xpm +) + +SOURCE_GROUP(SideBar FILES ${SideBar_SRCS}) +NGL_ADD_LIBRARY(WS situationMapSideBar SHARED ${SideBar_SRCS}) + +NGL_TARGET_INCLUDE_DIRECTORIES(situationMapSideBar + PUBLIC +# ${CMAKE_CURRENT_SOURCE_DIR} +# ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + + +NGL_TARGET_LINK_LIBRARIES(situationMapSideBar + PRIVATE + NGLLogger + PUBLIC + situationMapCommon + Qt6::Core + Qt6::Widgets +) + +NGL_INSTALL_TARGET(situationMapSideBar) + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar.cpp new file mode 100755 index 000000000..44245dd6c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar.cpp @@ -0,0 +1,416 @@ +/** + * @RDE_HEADER@ + * SideBar + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Norbert Thoden, SLSE, 2015-01-19 + */ + + +// Own +#include "side_bar.h" +#include "side_bar_model.h" + +// Qt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace SituationMap +{ + +class SideBar::Private +{ +public: + Private() = default; + ~Private() = default; + + + SideBarModel *m_model = nullptr; + QTreeView *m_view = nullptr; + bool m_suppressClick = false; + QTimer *m_suppressTimer = nullptr; + + QSharedPointer m_sideBarAniOn; + QSharedPointer m_sideBarAniOff; + + +}; // class SideBar::Private +//----------------------------------------------------------------------------- + + + + +//----------------------------------------------------------------------------- +QImage SideBar::createImage(const QColor &fgColor, + const QColor &bgColor, + int size) +{ + QImage burgerMenueImage(size, size, QImage::Format_ARGB32); + if (bgColor.isValid()) + burgerMenueImage.fill(bgColor); + else + burgerMenueImage.fill(Qt::transparent); + + QPainter p(&burgerMenueImage); + + { + QPen pen(Qt::white); + pen.setWidth(size * 0.15); + pen.setCapStyle(Qt::PenCapStyle::RoundCap); + p.setPen(pen); + + double yFactor = 0.1; + p.drawLine(QPointF(size * yFactor, size * 0.25), QPointF(size - size * yFactor, size * 0.25)); + p.drawLine(QPointF(size * yFactor, size * 0.50), QPointF(size - size * yFactor, size * 0.50)); + p.drawLine(QPointF(size * yFactor, size * 0.75), QPointF(size - size * yFactor, size * 0.75)); + } + + { + QPen pen(fgColor); + pen.setWidth(size * 0.1); + pen.setCapStyle(Qt::PenCapStyle::RoundCap); + p.setPen(pen); + + double yFactor = 0.1; + p.drawLine(QPointF(size * yFactor, size * 0.25), QPointF(size - size * yFactor, size * 0.25)); + p.drawLine(QPointF(size * yFactor, size * 0.50), QPointF(size - size * yFactor, size * 0.50)); + p.drawLine(QPointF(size * yFactor, size * 0.75), QPointF(size - size * yFactor, size * 0.75)); + } + + return burgerMenueImage; +} + +QIcon SideBar::createIcon(const QColor &fgColor, + const QColor &bgColor, + int size) +{ + return QIcon(QPixmap::fromImage(SideBar::createImage(fgColor, bgColor, size))); +} + + +SideBar::SideBar(QWidget *parent) + : BaseClass(parent) + , d(new Private) + , m_logger(getLogger("SideBar")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + // Prepare animations: + d->m_sideBarAniOn = QSharedPointer(new QPropertyAnimation(this, "pos")); + connect(d->m_sideBarAniOn.data(), &QPropertyAnimation::finished, this, &SideBar::onAnimationOnFinished); + + d->m_sideBarAniOff = QSharedPointer(new QPropertyAnimation(this, "pos")); + connect(d->m_sideBarAniOff.data(), &QPropertyAnimation::finished, this, &SideBar::onAnimationOffFinished); + + QVBoxLayout *layout = new QVBoxLayout(); + layout->setObjectName(QLatin1String("SideBarLayout")); + this->setLayout(layout); + + // 1st row + QHBoxLayout *layoutTopRow = new QHBoxLayout(); + layoutTopRow->setObjectName(QLatin1String("SideBarTopRowLayout")); + layout->addLayout(layoutTopRow); + layoutTopRow->addStretch(2); + + QPushButton *close = new QPushButton(); + close->setObjectName(QLatin1String("SideBarCloseButton")); + close->setMinimumSize(QSize(0, 0)); + close->setStyleSheet(QLatin1String("QPushButton {background-color: white; border: 1px solid transparent;}")); + close->setIcon(QIcon(QLatin1String("icon:dialog-close.svg"))); +// close->setIcon(QIcon(QLatin1String("qrc:///icons/svg/dialog-close.svg"))); + close->setIconSize(QSize(20, 20)); +// close->setStyleSheet(QLatin1String("QPushButton { image: url(\"icon:dialog-close.svg\"); }")); + + + + // lambda :-) + connect(close, &QPushButton::clicked, this, [this]() + { + emit closeRequest(); + this->hideAnimated(); + }); + + + layoutTopRow->addWidget(close); + + // 2nd Row + d->m_view = new QTreeView(this); + if (d->m_view) + { + d->m_view->setObjectName(QLatin1String("SideBarTreeView")); + d->m_view->setMinimumSize(QSize(0, 0)); + d->m_view->setAlternatingRowColors(true); + connect(d->m_view, &QTreeView::clicked, this, &SideBar::onClicked); + + layout->addWidget(d->m_view); + + initModel(); + d->m_view->setModel(d->m_model); + } + + initAppearanceToSideBar(true); + //initAppearanceToSideBar(false); + + + d->m_suppressTimer = new QTimer(this); + d->m_suppressTimer->setSingleShot(true); + d->m_suppressTimer->setInterval(20); + connect(d->m_suppressTimer, &QTimer::timeout, this, [this]() { + d->m_suppressClick = false; + }); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +SideBar::~SideBar() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete d; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +bool SideBar::initAppearanceToSideBar(bool toSideBar) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, toSideBar = " << toSideBar; + + if (toSideBar) + { + setWindowFlag(Qt::FramelessWindowHint); + if (d->m_view) + d->m_view->setHeaderHidden(true); // only TreeView + + this->layout()->setSpacing(0); + this->layout()->setContentsMargins(0, 0, 0, 0); + +// d->m_view->setStyleSheet(QLatin1String("QLabel {border: none;}")); + + } + else + { + if (d->m_view) + d->m_view->setHeaderHidden(false); + + this->layout()->setSpacing(2); + this->layout()->setContentsMargins(-1, -1, -1, -1); // -1 -> invalid (!?) + + } + + setAutoFillBackground(true); + // // this->setStyleSheet(QLatin1String("QWidget {background-color: white;}")); + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +void SideBar::setAnimationDuration(int msecs) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + d->m_sideBarAniOn->setDuration(msecs); + d->m_sideBarAniOff->setDuration(msecs); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void SideBar::setInvisiblePosition(const QPoint &invisiblePos) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " invisiblePos=" << invisiblePos; + + // x---> + d->m_sideBarAniOn->setStartValue(invisiblePos); + + // x<--- + d->m_sideBarAniOff->setEndValue(invisiblePos); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " invisiblePos=" << invisiblePos; +} + +void SideBar::setVisiblePosition(const QPoint &visiblePos) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " visiblePos=" << visiblePos; + + // --->x + d->m_sideBarAniOn->setEndValue(visiblePos); + + // <---x + d->m_sideBarAniOff->setStartValue(visiblePos); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " visiblePos=" << visiblePos; +} + +void SideBar::showAnimated() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + this->show(); + this->raise(); + + onAnimationStarted(); + d->m_sideBarAniOn->start(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +bool SideBar::isCurrentlyShowing() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return d->m_sideBarAniOn->state() == QAbstractAnimation::Running; +} + +void SideBar::hideAnimated() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + // use current position (maybe showAnimation is interrupted) + d->m_sideBarAniOff->setStartValue(this->pos()); + + onAnimationStarted(); + d->m_sideBarAniOff->start(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +bool SideBar::isCurrentlyHiding() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return d->m_sideBarAniOff->state() == QAbstractAnimation::Running; +} + + +bool SideBar::initModel() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + delete d->m_model; + d->m_model = new SideBarModel(this); + d->m_model->setObjectName(QLatin1String("SideBarModel")); + + connect(d->m_model, &SideBarModel::itemChanged, this, &SideBar::onItemChanged); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +bool SideBar::addCheckableItem(SideBarItem *item) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + item->setCheckable(true); + item->setEditable(false); + + d->m_model->addItem(item); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + + +bool SideBar::addWidget(QWidget *widget) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + this->layout()->addWidget(widget); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +void SideBar::onItemChanged(QStandardItem *item) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + QString itemText(QLatin1String("-")); + if (item) + itemText = item->text(); + INFO_LOG(m_logger) << LOGFUNCTION << " itemChanged, itemText='" << itemText << "'"; + + // hide if empty: + if (d->m_view) + d->m_view->setEnabled(d->m_model->rowCount() > 0); + + emit dataChanged(); + + // If user clicks checkbox, 'clicked' is also emitted afterwards... + // In this case, avoid evaluating that signal for a small amount on time + d->m_suppressClick = true; + d->m_suppressTimer->start(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void SideBar::onAnimationOnFinished() +{ + onAnimationFinished(); +} + +void SideBar::onAnimationOffFinished() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + this->hide(); + onAnimationFinished(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void SideBar::onClicked(const QModelIndex &index) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!d->m_model) + return; + + if (d->m_suppressClick) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " m_suppressClick"; + return; + } + + QStandardItem *item = d->m_model->itemFromIndex(index); + if (!item) + return; + if (!item->isCheckable()) + return; + if (!item->isEnabled()) + return; + + if (item->checkState() == Qt::CheckState::Checked) + item->setCheckState(Qt::CheckState::Unchecked); + else + item->setCheckState(Qt::CheckState::Checked); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void SideBar::onAnimationStarted() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + int h = this->parentWidget()->height(); + this->setFixedHeight(h); // SetFixedSize/setFixedHeight to have smooth animations, especially in case of some JumpPoints + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void SideBar::onAnimationFinished() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + this->setFixedHeight(QWIDGETSIZE_MAX); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar.h new file mode 100755 index 000000000..1f22e96e1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar.h @@ -0,0 +1,102 @@ +/** + * @RDE_HEADER@ + * SideBar + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Norbert Thoden, SLSE, 2015-01-19 + */ + +#pragma once + +// Own +#include "side_bar_export.h" +#include "side_bar_item.h" +#include + +// Qt +#include + +class QModelIndex; +class QStandardItem; + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +// forward: +class Map; + + +// using baseclass QDialog -> with background / no transparency +// using baseclass QWidget -> no background / will be transparent +class SIDEBAR_EXPORT SideBar : public QDialog +{ + using BaseClass = QDialog; + + Q_OBJECT + +public: + static QImage createImage(const QColor &fgColor + , const QColor &bgColor + , int size); + + static QIcon createIcon(const QColor &fgColor + , const QColor &bgColor + , int size); + + + explicit SideBar(QWidget *parent = nullptr); + virtual ~SideBar(); + + // Animation regarding position: + virtual void setAnimationDuration(int msecs); + virtual void setInvisiblePosition(const QPoint &invisiblePos); + virtual void setVisiblePosition(const QPoint &visiblePos); + + virtual void showAnimated(); + virtual bool isCurrentlyShowing(); + + virtual void hideAnimated(); + virtual bool isCurrentlyHiding(); + + virtual bool addCheckableItem(SideBarItem *item); + + virtual bool addWidget(QWidget *widget); + +signals: + void dataChanged(); + void closeRequest(); + + void positionString(bool onOff); + + +protected Q_SLOTS: + void onItemChanged(QStandardItem *item); + void onAnimationOnFinished(); + void onAnimationOffFinished(); + void onClicked(const QModelIndex &index); + + void onAnimationStarted(); + void onAnimationFinished(); + + +private: + bool initAppearanceToSideBar(bool toSideBar = true); + bool initModel(); + +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 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar_export.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar_export.h new file mode 100755 index 000000000..ccbd301ad --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar_export.h @@ -0,0 +1,21 @@ +/** + * Export definitions + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include + +#ifndef SIDEBAR_EXPORT +# ifdef BUILD_STATIC_LIBRARIES +# define SIDEBAR_EXPORT +# else +# if defined(MAKE_SITUATIONMAPSIDEBAR_LIB) +# define SIDEBAR_EXPORT Q_DECL_EXPORT +# else +# define SIDEBAR_EXPORT Q_DECL_IMPORT +# endif +# endif +#endif diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar_item.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar_item.cpp new file mode 100755 index 000000000..3894fb2c7 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar_item.cpp @@ -0,0 +1,42 @@ +/** + * @RDE_HEADER@ + * SideBarItem + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Norbert Thoden, SLSE, 2015-01-19 + */ + + +// Own +#include "side_bar_item.h" + + +namespace SituationMap +{ + +SideBarItem::SideBarItem(const QString &text) + : QObject() + , QStandardItem(text) + , m_logger(getLogger("SideBarItem")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +SideBarItem::~SideBarItem() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void SideBarItem::doEmitChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + emit changed(this->checkState() == Qt::CheckState::Checked); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar_item.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar_item.h new file mode 100755 index 000000000..11614fc0d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar_item.h @@ -0,0 +1,51 @@ +/** + * @RDE_HEADER@ + * SideBarItem + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Norbert Thoden, SLSE, 2015-01-19 + */ + +#pragma once + +// Own +#include +#include "side_bar_export.h" + + +// Qt +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + + +class SIDEBAR_EXPORT SideBarItem : public QObject, public QStandardItem +{ + Q_OBJECT + +public: + explicit SideBarItem(const QString &text); + virtual ~SideBarItem(); + + virtual int type() const + { + return QStandardItem::UserType + 1; + } + + void doEmitChanged(); + +signals: + bool changed(bool); + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility + +}; + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar_model.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar_model.cpp new file mode 100755 index 000000000..61fce1555 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar_model.cpp @@ -0,0 +1,123 @@ +/** + * @RDE_HEADER@ + * SideBarModel + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Norbert Thoden, SLSE, 2015-01-19 + */ + + +// Own +#include "side_bar_model.h" +#include + + +namespace SituationMap +{ + +SideBarModel::SideBarModel(QObject *parent) + : BaseClass(0, 1, parent) + , m_logger(getLogger("SideBarModel")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + +// fillDummyData(); + + connect(this, &SideBarModel::itemChanged, this, &SideBarModel::onItemChanged); +// connect(this, &SideBarModel::dataChanged, this, &SideBarModel::onDataChanged); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +SideBarModel::~SideBarModel() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void SideBarModel::fillDummyData() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + // remove old entries: + removeRows(0, rowCount()); + + // setHeader + setHeaderData(0, Qt::Horizontal, QStringLiteral("HeaderH")); + + // fill data: + QStandardItem *rootItem = invisibleRootItem(); + + // be prepared for a tree: + { + QList itemRow; + QStandardItem *item = new QStandardItem(QLatin1String("Entry01")); + item->setCheckable(true); + itemRow.append(item); + rootItem->appendRow(itemRow); + } + + { + QList itemRow; + QStandardItem *item = new QStandardItem(QLatin1String("Entry02")); + item->setCheckable(true); + itemRow.append(item); + rootItem->appendRow(itemRow); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +//void SideBarModel::addItem(QStandardItem *item, QStandardItem *parentItem) +//{ +// TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + +// QList itemRow; +// itemRow.append(item); + +// if (parentItem) +// parentItem->appendRow(itemRow); +// else +// invisibleRootItem()->appendRow(itemRow); + +// TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +//} + +void SideBarModel::addItem(SideBarItem *item, SideBarItem *parentItem) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + QList itemRow; + itemRow.append(item); + + if (parentItem) + parentItem->appendRow(itemRow); + else + invisibleRootItem()->appendRow(itemRow); + + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void SideBarModel::onItemChanged(QStandardItem *item) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + QString itemText(QLatin1String("-")); + if (item) + itemText = item->text(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " itemChanged, itemText='" << itemText << "'"; + + SideBarItem *sideBarItem = dynamic_cast(item); + if (sideBarItem) + sideBarItem->doEmitChanged(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar_model.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar_model.h new file mode 100755 index 000000000..8bf171c9e --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SideBar/side_bar_model.h @@ -0,0 +1,67 @@ +/** + * @RDE_HEADER@ + * SideBarModel + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Norbert Thoden, SLSE, 2015-01-19 + */ + +#pragma once + +// Own +#include +#include "side_bar_item.h" +#include "side_bar_export.h" + + +// Qt +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +class SIDEBAR_EXPORT SideBarModel : public QStandardItemModel +{ + using BaseClass = QStandardItemModel; + + Q_OBJECT + +public: + explicit SideBarModel(QObject *parent); + virtual ~SideBarModel(); + + void fillDummyData(); + +// void addItem(QStandardItem *item, QStandardItem *parentItem = nullptr); + void addItem(SideBarItem *item, SideBarItem *parentItem = nullptr); + + + +// // QAbstractItemModel: reimplemented : +// QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; +// QModelIndex parent(const QModelIndex &child) const; + +// virtual int rowCount(const QModelIndex &parent = QModelIndex()) const = 0; +// virtual int columnCount(const QModelIndex &parent = QModelIndex()) const = 0; + +// virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const = 0; +// // : reimplemented + +signals: + +protected Q_SLOTS: + void onItemChanged(QStandardItem *item); + + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility + +}; + + + +} // namespace SituationMap + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/CMakeLists.txt new file mode 100755 index 000000000..4313a334b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/CMakeLists.txt @@ -0,0 +1,46 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +# LIBRARY + +SET(SymbologyGui_SRCS + base_symbol_lookup_dialog.cpp + base_symbol_lookup_dialog.h + base_symbol_lookup_widget.cpp + base_symbol_lookup_widget.h + field_table_model.cpp + field_table_model.h + symbol_modification_widget.h + symbol_modification_widget.cpp + symbol_modification_dialog.h + symbol_modification_dialog.cpp +) + +SOURCE_GROUP(SymbologyGui FILES ${SymbologyGui_SRCS}) + +NGL_WRAP_UI(SymbologyGui_SRCS + base_symbol_lookup_widget.ui +) + +NGL_ADD_LIBRARY2(WS advancedSymbologyGui SHARED ${SymbologyGui_SRCS}) + +NGL_TARGET_INCLUDE_DIRECTORIES(advancedSymbologyGui + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_SOURCE_DIR}/../../../Common +) + +NGL_TARGET_LINK_LIBRARIES(advancedSymbologyGui + PRIVATE + Qt6::Gui + Qt6::Network + PUBLIC + LtsWidgets + LtsUtilsSettings + advancedSymbology + situationMap + Qt6::Core +) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_filter_proxy.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_filter_proxy.cpp new file mode 100755 index 000000000..5911fd6e2 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_filter_proxy.cpp @@ -0,0 +1,339 @@ +/** +** +** @RDE_HEADER@ +** PlanEditor. +** $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 .........: +** +** 13.06.2016, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + + +#include "base_symbol_filter_proxy.h" + +#include +#include +#include + +#include +#include +#include + + +namespace AdvancedSymbology +{ +namespace Gui +{ + +struct BaseSymbolProxyFilter::InternalData +{ + QStringList m_sidcList; + QStringList m_keyList; + // no need for QHash/QSet - element count is very small + QVector m_geometrySet = {GeometryType::Point, GeometryType::Polyline, GeometryType::Polygon}; + bool m_filterParicipantSymbols = false; + bool m_etsSymbols = true; + bool m_xmlSymbols = true; + bool m_esriDictSymbols = true; + +}; // class BaseSymbolProxyFilter::InternalData + + +BaseSymbolProxyFilter::BaseSymbolProxyFilter(QObject *parent) + : QSortFilterProxyModel(parent) + , m_internal(new InternalData()) +{} + +BaseSymbolProxyFilter::~BaseSymbolProxyFilter() +{ + delete m_internal; +} // BaseSymbolProxyFilter::~BaseSymbolProxyFilter() + +QStringList BaseSymbolProxyFilter::mimeTypes() const +{ + QStringList mineTypeList = QSortFilterProxyModel::mimeTypes(); + mineTypeList << QStringLiteral("Participant"); + return mineTypeList; +} // QStringList BaseSymbolProxyFilter::mimeTypes() const + + +QMimeData *BaseSymbolProxyFilter::mimeData(const QModelIndexList &indexes) const +{ + QMimeData *mimeData = QSortFilterProxyModel::mimeData(indexes); + + if (mimeData) + { + const QByteArray &data = QByteArray::number(int(filterParticipants())); + mimeData->setData(QStringLiteral("Participant"), data); + } + + return mimeData; +} // QMimeData * BaseSymbolProxyFilter::mimeData(const QModelIndexList &indexes) const + + +void BaseSymbolProxyFilter::updateModel(const QStringList &keyList) +{ + m_internal->m_keyList = keyList; + + if (keyList.isEmpty()) + { + m_internal->m_sidcList.clear(); + beginFilterChange(); + endFilterChange(); + return; + } + + const AdvancedSymbology::SymbolDictionary &dict = symbolDictionary(); + + AdvancedSymbology::SearchParameters lookupDefs; + + lookupDefs.tags = keyList; + lookupDefs.symbolClasses = QStringList({QStringLiteral("3"), QStringLiteral("4"), QStringLiteral("5")}); + + AdvancedSymbology::PropertyResultWatcher *symbolGuard = new AdvancedSymbology::PropertyResultWatcher(this); + + connect(symbolGuard, &AdvancedSymbology::SymbolResultWatcher::resultReady, this, [this, symbolGuard]() + { + BaseSymbolModel *sidcLookupModel = static_cast(sourceModel()); + if (!sidcLookupModel) + return; + + QSharedPointer lookupResult = symbolGuard->propertyResult(); + const SymbolPropertyList &symbolPropertyList = lookupResult->properties(); + if (symbolPropertyList.isEmpty()) + return; + + { + m_internal->m_sidcList.clear(); + for (const AdvancedSymbology::SymbolProperties &prop : symbolPropertyList) + { + const QString &baseSymbolCode = SidcCodec::encodeBaseSymbolCode(symbolDictionary().symbologyType(), prop.key()); + QString sidc = AdvancedSymbology::SidcBuilderBase::baseSymbolCodeToSidc(baseSymbolCode); + + if (sidc.isEmpty()) sidc = prop.sidc(); // case of ETS and XML symbol the props.key() is the filename and sidc will be empty + + if (sidcLookupModel->isInModel(sidc)) + { + m_internal->m_sidcList += sidc; + } + } + beginFilterChange(); + endFilterChange(); + } + symbolGuard->deleteLater(); + }); + + symbolGuard->setPropertyResult(dict.searchSymbols(lookupDefs)); + +} // void BaseSymbolProxyFilter::updateModel( const QStringList & keyList ) + + +bool BaseSymbolProxyFilter::filterAcceptsRow(int sourceRow, const QModelIndex &) const +{ + BaseSymbolModel *sidcLookupModel = static_cast(sourceModel()); + const QString &sidc = sidcLookupModel->getSidc(sourceRow); + + bool accept = (m_internal->m_sidcList.isEmpty()) ? true : m_internal->m_sidcList.contains(sidc); + + SidcCodec::SidcType sidcType = SidcCodec::decodeSidcType(sidc); + + accept = (accept) ? ( (!SidcCodec::isCivil(sidc) && m_internal->m_esriDictSymbols) + || (m_internal->m_etsSymbols && (SidcCodec::SidcType::CivilEts == sidcType)) + || (m_internal->m_xmlSymbols && (SidcCodec::SidcType::CivilXml == sidcType)) ) : false; + + if (accept) + { + if (m_internal->m_filterParicipantSymbols) + accept = m_internal->m_geometrySet.contains(GeometryType::Point) && sidcLookupModel->isParticipant(sourceRow); + else + accept = m_internal->m_geometrySet.contains(sidcLookupModel->getSymbolGeometry(sourceRow)); + } + return accept; +} // bool BaseSymbolProxyFilter::filterAcceptsRow( int sourceRow, const QModelIndex & sourceParent ) + + +const AdvancedSymbology::SymbolDictionary &BaseSymbolProxyFilter::symbolDictionary() const +{ + BaseSymbolModel *sidcLookupModel = static_cast(sourceModel()); + return sidcLookupModel->symbolDictionary(); +} // const AdvancedSymbology::SymbolDictionary & BaseSymbolProxyFilter::symbolDictionary() + + +void BaseSymbolProxyFilter::setSourceModel(BaseSymbolModel *sourceModel) +{ + setSourceModel(static_cast(sourceModel)); +} // void BaseSymbolProxyFilter::setSourceModel( AdvancedSymbologyGui::BaseSymbolModel * sourceModel ) + + +bool BaseSymbolProxyFilter::isEmpty() const +{ + BaseSymbolModel *sidcLookupModel = static_cast(sourceModel()); + return sidcLookupModel->isEmpty(); +} // bool BaseSymbolProxyFilter::isEmpty() const + + +QString BaseSymbolProxyFilter::getSidc(int idx) const +{ + const QModelIndex &sourceIndex = mapToSource(index(idx, 0)); + BaseSymbolModel *sidcLookupModel = static_cast(sourceModel()); + return sidcLookupModel->getSidc(sourceIndex.row()); +} // QString BaseSymbolProxyFilter::getSidc( int idx ) const + + +int BaseSymbolProxyFilter::size() const +{ + BaseSymbolModel *sidcLookupModel = static_cast(sourceModel()); + return sidcLookupModel->size(); +} // int BaseSymbolProxyFilter::size() const + + +const QStringList &BaseSymbolProxyFilter::filterSymbolTags() const +{ + return m_internal->m_keyList; +} // const QStringList & BaseSymbolProxyFilter::filterSymbolTags() const + + +bool BaseSymbolProxyFilter::filterPoints() const +{ + return m_internal->m_geometrySet.contains(AdvancedSymbology::GeometryType::Point); +} // bool BaseSymbolProxyFilter::filterPoints() const + + +bool BaseSymbolProxyFilter::filterLines() const +{ + return m_internal->m_geometrySet.contains(AdvancedSymbology::GeometryType::Polyline); +} // bool BaseSymbolProxyFilter::filterLines() const + + +bool BaseSymbolProxyFilter::filterAreas() const +{ + return m_internal->m_geometrySet.contains(AdvancedSymbology::GeometryType::Polygon); +} // bool BaseSymbolProxyFilter::filterAreas() const + + +bool BaseSymbolProxyFilter::filterParticipants() const +{ + return m_internal->m_filterParicipantSymbols; +} // bool BaseSymbolProxyFilter::filterParticipants() const + + +bool BaseSymbolProxyFilter::filterExtraSymbols() const +{ + return m_internal->m_etsSymbols && m_internal->m_xmlSymbols; +} // bool BaseSymbolProxyFilter::filterExtraSymbols() const + +bool BaseSymbolProxyFilter::filterEtsSymbols() const +{ + return m_internal->m_etsSymbols; +} // bool BaseSymbolProxyFilter::filterEtsSymbols() const + +bool BaseSymbolProxyFilter::filterXmlSymbols() const +{ + return m_internal->m_xmlSymbols; +} // bool BaseSymbolProxyFilter::filterXmlSymbols() const + +bool BaseSymbolProxyFilter::filterEsriDictSymbols() const +{ + return m_internal->m_esriDictSymbols; +} // bool BaseSymbolProxyFilter::filterXmlSymbols() const + + +void BaseSymbolProxyFilter::onFilterSymbolTags(const QStringList &keyList) +{ + updateModel(keyList); +} // void BaseSymbolProxyFilter::onFilterSymbolTags(const QStringList &keyList) + +void BaseSymbolProxyFilter::onFilterExtraSymbols(bool on) +{ + if (m_internal->m_etsSymbols == on && m_internal->m_xmlSymbols == on) + return; + m_internal->m_etsSymbols = on; + m_internal->m_xmlSymbols = on; + beginFilterChange(); + endFilterChange(); +} // void BaseSymbolProxyFilter::onFilterExtraSymbols(bool on) + + +void BaseSymbolProxyFilter::onFilterEtsSymbols(bool on) +{ + if (m_internal->m_etsSymbols == on) + return; + m_internal->m_etsSymbols = on; + beginFilterChange(); + endFilterChange(); +} // void BaseSymbolProxyFilter::onFilterEtsSymbols(bool on) + +void BaseSymbolProxyFilter::onFilterXmlSymbols(bool on) +{ + if (m_internal->m_xmlSymbols == on) + return; + m_internal->m_xmlSymbols = on; + beginFilterChange(); + endFilterChange(); +} // void BaseSymbolProxyFilter::onFilterXmlSymbols(bool on) + + +void BaseSymbolProxyFilter::onFilterEsriDictSymbols(bool on) +{ + if (m_internal->m_esriDictSymbols == on) + return; + m_internal->m_esriDictSymbols = on; + beginFilterChange(); + endFilterChange(); +} // void BaseSymbolProxyFilter::onFilterXmlSymbols(bool on) + +void BaseSymbolProxyFilter::onFilterParticipants(bool on) +{ + if (m_internal->m_filterParicipantSymbols == on) + return; + m_internal->m_filterParicipantSymbols = on; + beginFilterChange(); + endFilterChange(); +} // void BaseSymbolProxyFilter::onFilterParticipants(bool on) + +void BaseSymbolProxyFilter::onFilterPoints(bool on) +{ + if (on) m_internal->m_geometrySet.push_back(AdvancedSymbology::GeometryType::Point); + else m_internal->m_geometrySet.removeAll(AdvancedSymbology::GeometryType::Point); + beginFilterChange(); + endFilterChange(); +} // void BaseSymbolProxyFilter::onFilterPoints(bool on) + + +void BaseSymbolProxyFilter::onFilterLines(bool on) +{ + if (on) m_internal->m_geometrySet.push_back(AdvancedSymbology::GeometryType::Polyline); + else m_internal->m_geometrySet.removeAll(AdvancedSymbology::GeometryType::Polyline); + beginFilterChange(); + endFilterChange(); +} // void BaseSymbolProxyFilter::onFilterLines(bool on) + + +void BaseSymbolProxyFilter::onFilterAreas(bool on) +{ + if (on) m_internal->m_geometrySet.push_back(AdvancedSymbology::GeometryType::Polygon); + else m_internal->m_geometrySet.removeAll(AdvancedSymbology::GeometryType::Polygon); + beginFilterChange(); + endFilterChange(); +} // void BaseSymbolProxyFilter::onFilterAreas(bool on) + +} // namespace Gui +} // namespace AdvancedSymbology diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_filter_proxy.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_filter_proxy.h new file mode 100755 index 000000000..7fc220d08 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_filter_proxy.h @@ -0,0 +1,142 @@ +/** +** +** @RDE_HEADER@ +** PlanEditor. +** $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 .........: +** +** 13.06.2016, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + + +#pragma once + +#include "base_symbol_model.h" + +#include +#include +#include + + +namespace AdvancedSymbology +{ +namespace Gui +{ +/** + @brief Filter proxy to BaseSymbolModel to retain list of symbols + + To retain the full list of symbols to a smaller list which can be easier handled + by the user a QSortFilterProxyModel is used. + + @see BaseSymbolModel, BaseSymbolLookupWidget + + */ + +class ADVANCED_SYMBOLOGY_GUI_EXPORT BaseSymbolProxyFilter : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + + explicit BaseSymbolProxyFilter(QObject *parent = nullptr); + + ~BaseSymbolProxyFilter(); + + const AdvancedSymbology::SymbolDictionary &symbolDictionary() const; + + void setSourceModel(AdvancedSymbology::Gui::BaseSymbolModel *sourceModel); + + int size() const; + + bool isEmpty() const; + + /** + Provides the code of the symbol as string. + @param idx row number of the symbol + @return full concret sidc of symbol + */ + QString getSidc(int idx) const; + + /** + Provides current constraints to filter full symbol list of BaseSymbolModel + @{ + */ + const QStringList &filterSymbolTags() const; + + bool filterExtraSymbols() const; + bool filterEtsSymbols() const; + bool filterXmlSymbols() const; + bool filterEsriDictSymbols() const; + + bool filterParticipants() const; + + bool filterPoints() const; + bool filterLines() const; + bool filterAreas() const; + /* @} */ + + QStringList mimeTypes() const override; + QMimeData * mimeData(const QModelIndexList &indexes) const override; + +protected: + + struct InternalData; + + InternalData *m_internal; + + /** + Internal update of filtering tags by using the dictionary (is called from \ c onFilterSymbolTags). + */ + void updateModel(const QStringList &keyList); + + /** + Overriden from QSortFilterProxyModel to associate source model with filter model. + */ + void setSourceModel(QAbstractItemModel *sourceModel) override { QSortFilterProxyModel::setSourceModel(sourceModel); } + + /** + Overriden from QSortFilterProxyModel to do the filtering. + */ + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; + +public slots: + + /** + Define constraints to filter full symbol list of BaseSymbolModel + @{ + */ + void onFilterSymbolTags(const QStringList &keyList); + + void onFilterExtraSymbols(bool on); + void onFilterEtsSymbols(bool on); + void onFilterXmlSymbols(bool on); + void onFilterEsriDictSymbols(bool on); + + void onFilterParticipants(bool on); + + void onFilterPoints(bool on); + void onFilterLines(bool on); + void onFilterAreas(bool on); + /* @} */ +}; // BaseSymbolProxyFilter + +} // namespace Gui +} // namespace AdvancedSymbology diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_lookup_dialog.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_lookup_dialog.cpp new file mode 100755 index 000000000..2aa107603 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_lookup_dialog.cpp @@ -0,0 +1,210 @@ +/** + * @RDE_HEADER@ + * Dialog for selecting (base) symbols + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Norbert Thoden, NLE, 12.12.2019 + */ + +#include "base_symbol_lookup_dialog.h" +#include "base_symbol_lookup_widget.h" +#include +#include + +#include +#include +#include + +#include // for baseSymbolCodeToSidc + +#include +#include +#include + + +namespace AdvancedSymbology +{ +namespace Gui +{ + +//------------------------------------------------------------------------------ +// BaseSymbolLookupDialog::Private +//------------------------------------------------------------------------------ +struct BaseSymbolLookupDialog::Private +{ +public: + Private() = default; + + ~Private() + { + delete m_baseSymbolWidget; + delete m_buttonBox; + } + BaseSymbolLookupWidget *m_baseSymbolWidget = nullptr; + LtsWidgets::LtsDialogButtonBox *m_buttonBox = nullptr; +}; + + +//------------------------------------------------------------------------------ +// BaseSymbolLookupDialog +//------------------------------------------------------------------------------ +BaseSymbolLookupDialog::BaseSymbolLookupDialog(QWidget *parent) + : BaseClass(parent, LtsWidgets::Behaviour::Modal) + , m_logger(getLogger("BaseSymbolLookupDialog")) + , d(new Private()) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +bool BaseSymbolLookupDialog::participantMode() const +{ + if (!d->m_baseSymbolWidget) + return false; + return d->m_baseSymbolWidget->participantMode(); +} +void BaseSymbolLookupDialog::setParticipantMode(bool onOff) +{ + if (!d->m_baseSymbolWidget) + return; + d->m_baseSymbolWidget->setParticipantMode(onOff); +} + +void BaseSymbolLookupDialog::setValidBaseSymbolCodes(const QSet &validBaseSymbolCodes) +{ + if (!d->m_baseSymbolWidget) + return; + d->m_baseSymbolWidget->setValidBaseSymbolCodes(validBaseSymbolCodes); +} + +bool BaseSymbolLookupDialog::init(AdvancedSymbology::SymbolDictionary &symbolDictionary) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance(); + + bool enableEtsSymbols = ps->value(QStringLiteral("SituationMap"), QStringLiteral("enableEtsSymbols"), true).toBool(); + + // setup: + delete d->m_baseSymbolWidget; + delete d->m_buttonBox; + + AdvancedSymbology::SymbolDictionaryImpl &sdi = static_cast(symbolDictionary); + AdvancedSymbology::BaseSymbolModel *bsmEsri = sdi.getBaseSymbolModelEsri(); + AdvancedSymbology::BaseSymbolModel *bsmXml = sdi.getBaseSymbolModelXML(); + AdvancedSymbology::BaseSymbolModel *bsmEts = sdi.getBaseSymbolModelEts(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " bsmEsri rowCount" << bsmEsri->rowCount(); + const SymbologyType symbologyType = sdi.symbologyType(); + AdvancedSymbology::Gui::MODEL_CONT modelCont; + modelCont.append(AdvancedSymbology::Gui::ModelItem(AdvancedSymbology::Gui::SYMBOL_TYPE::ESRI, toString(symbologyType), bsmEsri)); + modelCont.append(AdvancedSymbology::Gui::ModelItem(AdvancedSymbology::Gui::SYMBOL_TYPE::XML, QLatin1String("User-defined Symbols"), bsmXml)); + if (enableEtsSymbols ) + modelCont.append(AdvancedSymbology::Gui::ModelItem(AdvancedSymbology::Gui::SYMBOL_TYPE::ETS, QLatin1String("Ets"), bsmEts)); + + d->m_baseSymbolWidget = new AdvancedSymbology::Gui::BaseSymbolLookupWidget(modelCont, this); + + connect(d->m_baseSymbolWidget, &AdvancedSymbology::Gui::BaseSymbolLookupWidget::symbolClicked, this, &BaseSymbolLookupDialog::onSymbolClicked); + connect(d->m_baseSymbolWidget, &AdvancedSymbology::Gui::BaseSymbolLookupWidget::symbolDoubleClicked, this, &BaseSymbolLookupDialog::onSymbolDoubleClicked); + + + d->m_buttonBox = new LtsWidgets::LtsDialogButtonBox(this); + + connect(d->m_buttonBox->getButton(LtsWidgets::DialogButton::Ok), &QPushButton::clicked, this, &BaseSymbolLookupDialog::apply); + connect(d->m_buttonBox->getButton(LtsWidgets::DialogButton::Close), &QPushButton::clicked, this, &BaseSymbolLookupDialog::close); + + d->m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->setEnabled(false); // until an item is selected + + + // setup layout: + QVBoxLayout *layout = new QVBoxLayout(); + + layout->addWidget(d->m_baseSymbolWidget, 2); + layout->addWidget(d->m_buttonBox); + setLayout(layout); + + setMinimumWidth(800); + resize(sizeHint().height(), minimumWidth()); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +BaseSymbolLookupDialog::~BaseSymbolLookupDialog() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete d; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +QString BaseSymbolLookupDialog::getCurrentBaseSymbolCode() const +{ + if (!d->m_baseSymbolWidget) + return QString(); + return d->m_baseSymbolWidget->getCurrentBaseSymbolCode(); +} + +bool BaseSymbolLookupDialog::setCurrentSidc(const QString &sidc) +{ + if (!d->m_baseSymbolWidget) + return false; + bool success = d->m_baseSymbolWidget->setCurrentSidc(sidc); +// d->m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->setEnabled(success); + return success; +} + +void BaseSymbolLookupDialog::onSymbolClicked(const QString &baseSymbolCode, const QString &sidc) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, " + << " baseSymbolCode='" << baseSymbolCode << "'" + << " sidc='" << sidc << "'"; + + d->m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->setEnabled(!sidc.isEmpty()); + +// slotChangesMade(); +} + +void BaseSymbolLookupDialog::onSymbolDoubleClicked(const QString &baseSymbolCode, const QString &sidc) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, " + << " baseSymbolCode='" << baseSymbolCode << "'" + << " sidc='" << sidc << "'"; + apply(); +} + +void BaseSymbolLookupDialog::apply() +{ + if (!d->m_baseSymbolWidget) + return ; + QSharedPointer bsi = d->m_baseSymbolWidget->getCurrentBaseSymbolItem(); + if (!bsi) + return; + + const QString &curBsc = bsi->baseSymbolCode(); + const QString &curSidc = bsi->sidc(); + + emit lookupAppliedBaseSymbolCode(curBsc); + emit lookupAppliedCompleteSidc(curSidc); + accept(); +} + +BaseSymbolLookupDialog *BaseSymbolLookupDialog::showInstance() +{ + static QPointer s_BaseSymbolLookupDialog; + + if (!s_BaseSymbolLookupDialog) + s_BaseSymbolLookupDialog = createDlg(); + + return s_BaseSymbolLookupDialog; +} + +BaseSymbolLookupDialog *BaseSymbolLookupDialog::createDlg(QWidget *parent) +{ + AdvancedSymbology::Gui::BaseSymbolLookupDialog *dlg = new AdvancedSymbology::Gui::BaseSymbolLookupDialog(parent); + dlg->init(AdvancedSymbology::SymbolDictionary::dictionary(AdvancedSymbology::SymbolDictionary::defaultSymbologyType())); + return dlg; +} + + +} // namespace Gui +} // namespace AdvancedSymbology diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_lookup_dialog.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_lookup_dialog.h new file mode 100755 index 000000000..9fead78c2 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_lookup_dialog.h @@ -0,0 +1,76 @@ +/** + * @RDE_HEADER@ + * Dialog for selecting (base) symbols + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Norbert Thoden, NLE, 12.12.2019 + */ + +#pragma once + +#include +#include +#include +#include + + +namespace AdvancedSymbology +{ +class SymbolDictionary; + + +namespace Gui +{ + +//------------------------------------------------------------------------------ +// BaseSymbolLookupDialog +//------------------------------------------------------------------------------ +class ADVANCEDSYMBOLOGYGUI_EXPORT BaseSymbolLookupDialog : public LtsWidgets::LtsFramelessDialog +{ + Q_OBJECT + using BaseClass = LtsWidgets::LtsFramelessDialog; + +public: + static BaseSymbolLookupDialog *showInstance(); + + static BaseSymbolLookupDialog *createDlg(QWidget *parent = nullptr); + + + explicit BaseSymbolLookupDialog(QWidget *parent = nullptr); + ~BaseSymbolLookupDialog(); + + bool init(SymbolDictionary &symbolDictionary); + + bool participantMode() const; + void setParticipantMode(bool onOff); + + void setValidBaseSymbolCodes(const QSet &validBaseSymbolCodes); + + QString getCurrentBaseSymbolCode() const; + bool setCurrentSidc(const QString &sidc); + +Q_SIGNALS: + void lookupAppliedBaseSymbolCode(const QString &sidc); + void lookupAppliedCompleteSidc(const QString &sidc); + +protected: + void apply(); + +protected Q_SLOTS: + void onSymbolDoubleClicked(const QString &baseSymbolCode, const QString &sidc); + void onSymbolClicked(const QString &baseSymbolCode, const QString &sidc); + +private: + SituationMap::LOGGER_T m_logger; + + struct Private; + /** + * The private d->pointer to not break BC when changing internals of this class + */ + Private *d = nullptr; +}; // BaseSymbolLookupDialog + + +} // namespace Gui +} // namespace AdvancedSymbology diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_lookup_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_lookup_widget.cpp new file mode 100755 index 000000000..ad13d5289 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_lookup_widget.cpp @@ -0,0 +1,481 @@ +/** + * @RDE_HEADER@ + * Widget for selecting (base) symbols + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Norbert Thoden, NLE, 12.12.2019 + */ + +#include "base_symbol_lookup_widget.h" +#include "ui_base_symbol_lookup_widget.h" + +#include // for concreteSidcToBaseSymbolCode +#include + +// Lts +//#include +#include "LtsDataView/lts_data_view.h" + +// Qt +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace AdvancedSymbology +{ +namespace Gui +{ + +//------------------------------------------------------------------------------ +// BaseSymbolLookupWidget::Private +//------------------------------------------------------------------------------ +struct BaseSymbolLookupWidget::Private +{ + MODEL_CONT modelContainer; + + QMap m_symbologyTypeToIdx; + QMap m_idxToSymbologyType; + QMap m_idxToDataViewCont; // tabIdx -> view + QMap m_idxToModel; // tabIdx -> view + + bool m_participantMode = false; + QCheckBox *m_cbPoint = nullptr; + QCheckBox *m_cbLine = nullptr; + QCheckBox *m_cbArea = nullptr; + QSet m_validBaseSymbolCodes; +}; + + +//------------------------------------------------------------------------------ +// BaseSymbolLookupWidget +//------------------------------------------------------------------------------ +BaseSymbolLookupWidget::BaseSymbolLookupWidget(const MODEL_CONT &modelCont, QWidget *parent) + : QWidget(parent) + , m_logger(getLogger("BaseSymbolLookupWidget")) + , m_ui(new Ui::BaseSymbolLookupWidget) + , d(new Private()) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + m_ui->setupUi(this); + + // clear, we use our own widgets: + m_ui->tabWidget->removeTab(0); + + d->modelContainer = modelCont; + + for (const auto &modelData : std::as_const(d->modelContainer)) + { + if (!modelData.m_baseSymbolModel) + continue; + + LtsDataView::LtsDataView *dv = new LtsDataView::LtsDataView(this); + + BaseSymbolProxyModel *proxy = new BaseSymbolProxyModel(this); + proxy->setSourceModel(modelData.m_baseSymbolModel); + proxy->setDynamicSortFilter(false); + dv->setModel(proxy); + + // connect: + connect(dv, &LtsDataView::LtsDataView::clicked, this, &BaseSymbolLookupWidget::onClicked); + connect(dv, &LtsDataView::LtsDataView::doubleClicked, this, &BaseSymbolLookupWidget::onDoubleClicked); + + if (modelData.m_symbolType == SYMBOL_TYPE::ESRI) + { + // ESRI -> add some cbs for Point, Line, Area: + // HBox with 3 cbs: + QHBoxLayout *layoutGeomSelect = new QHBoxLayout(); + d->m_cbPoint = new QCheckBox(tr("Point"), this); + d->m_cbLine = new QCheckBox(tr("Line"), this); + d->m_cbArea = new QCheckBox(tr("Area"), this); + layoutGeomSelect->addWidget(d->m_cbPoint, 0); + layoutGeomSelect->addWidget(d->m_cbLine, 0); + layoutGeomSelect->addWidget(d->m_cbArea, 0); + layoutGeomSelect->addStretch(9); + + d->m_cbPoint->setChecked(proxy->filterPoints()); + d->m_cbLine->setChecked(proxy->filterLines()); + d->m_cbArea->setChecked(proxy->filterAreas()); + connect(d->m_cbPoint, &QCheckBox::toggled, proxy, &BaseSymbolProxyModel::onFilterPoints); + connect(d->m_cbLine, &QCheckBox::toggled, proxy, &BaseSymbolProxyModel::onFilterLines); + connect(d->m_cbArea, &QCheckBox::toggled, proxy, &BaseSymbolProxyModel::onFilterAreas); + + // VBox with layoutGeomSelect and LtsDataView + QVBoxLayout *layout = new QVBoxLayout(); + layout->addLayout(layoutGeomSelect); + layout->addWidget(dv); + + // wrap within a widget: + QWidget *w = new QWidget(); + w->setLayout(layout); + + int idx = m_ui->tabWidget->addTab(w, modelData.m_name); + d->m_symbologyTypeToIdx[modelData.m_symbolType] = idx; + d->m_idxToSymbologyType[idx] = modelData.m_symbolType; + d->m_idxToDataViewCont[idx] = dv; + d->m_idxToModel[idx] = modelData.m_baseSymbolModel; + } + else + { + const int idx = m_ui->tabWidget->addTab(dv, modelData.m_name); + d->m_symbologyTypeToIdx[modelData.m_symbolType] = idx; + d->m_idxToSymbologyType[idx] = modelData.m_symbolType; + d->m_idxToDataViewCont[idx] = dv; + d->m_idxToModel[idx] = modelData.m_baseSymbolModel; + } + + showHideColumns(modelData.m_symbolType); + + } // for + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +BaseSymbolLookupWidget::~BaseSymbolLookupWidget() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete d; + delete m_ui; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void BaseSymbolLookupWidget::setValidBaseSymbolCodes(const QSet &validBaseSymbolCodes) +{ + d->m_validBaseSymbolCodes = validBaseSymbolCodes; + + const int tabCount = m_ui->tabWidget->count(); + for (int tabIdx = 0; tabIdx < tabCount; ++tabIdx) + { + LtsDataView::LtsDataView *dv = d->m_idxToDataViewCont[tabIdx]; + if (!dv) + continue; + + BaseSymbolProxyModel *proxy = qobject_cast(dv->proxyModel()); + if (proxy) + proxy->setValidBaseSymbolCodes(d->m_validBaseSymbolCodes); + } // for +} + +bool BaseSymbolLookupWidget::participantMode() const +{ + return d->m_participantMode; +} + +void BaseSymbolLookupWidget::setParticipantMode(bool onOff) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, participantMode =" << onOff; + d->m_participantMode = onOff; + if (d->m_participantMode) + { + d->m_cbPoint->setChecked(true); + d->m_cbLine->setChecked(false); + d->m_cbArea->setChecked(false); + } + else + { + d->m_cbPoint->setChecked(true); + d->m_cbLine->setChecked(true); + d->m_cbArea->setChecked(true); + } + + d->m_cbPoint->setVisible(!d->m_participantMode); + d->m_cbLine->setVisible(!d->m_participantMode); + d->m_cbArea->setVisible(!d->m_participantMode); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void BaseSymbolLookupWidget::showHideColumns(SYMBOL_TYPE symType) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, symType=" << static_cast(symType); + if (!d->m_symbologyTypeToIdx.contains(symType)) + return; + + const int idx = d->m_symbologyTypeToIdx[symType]; + LtsDataView::LtsDataView *dv = d->m_idxToDataViewCont[idx]; + if (!dv) + return; + +#ifdef LTS_DEBUG + const bool showAll = QGuiApplication::keyboardModifiers().testFlag(Qt::KeyboardModifier::ControlModifier); +#else + const bool showAll = false; +#endif // LTS_DEBUG + + switch (symType) + { + case SYMBOL_TYPE::ESRI: +// dv->setColumnHidden(BaseSymbolModel::Columns::ID, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::ORIGINAL_KEY, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::KEY, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::OPERATOR_NAME, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::LAST_MODIFIED, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::SIDC, !showAll); + + + dv->setColumnHidden(BaseSymbolModel::Columns::SEARCH_ICON, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::FETCH_ICON, !showAll); +// dv->setColumnHidden(BaseSymbolModel::Columns::FIND_BASE_SYMBOL, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::FIND_PREVIEW, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::FIND_IMAGE, !showAll); + + dv->setColumnHidden(BaseSymbolModel::Columns::XML_SYMBOL_SIZE, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::CATEGORY, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::SYMBOLCLASS, !showAll); +// dv->setColumnHidden(BaseSymbolModel::Columns::TAGS, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::GCTSTRING, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::GEOMETRYTYPE, !showAll); + break; + + case SYMBOL_TYPE::XML: +// dv->setColumnHidden(BaseSymbolModel::Columns::ID, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::ORIGINAL_KEY, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::KEY, !showAll); +// dv->setColumnHidden(BaseSymbolModel::Columns::OPERATOR_NAME, !showAll); +// dv->setColumnHidden(BaseSymbolModel::Columns::LAST_MODIFIED, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::SIDC, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::SYMBOL_SET, true); + + dv->setColumnHidden(BaseSymbolModel::Columns::SEARCH_ICON, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::FETCH_ICON, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::FIND_BASE_SYMBOL, !showAll); +// dv->setColumnHidden(BaseSymbolModel::Columns::FIND_PREVIEW, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::FIND_IMAGE, !showAll); + +// dv->setColumnHidden(BaseSymbolModel::Columns::XML_SYMBOL_SIZE, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::CATEGORY, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::SYMBOLCLASS, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::TAGS, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::GCTSTRING, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::GEOMETRYTYPE, !showAll); + break; + + case SYMBOL_TYPE::ETS: +// dv->setColumnHidden(BaseSymbolModel::Columns::ID, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::ORIGINAL_KEY, true); + dv->setColumnHidden(BaseSymbolModel::Columns::KEY, true); + dv->setColumnHidden(BaseSymbolModel::Columns::OPERATOR_NAME, true); + dv->setColumnHidden(BaseSymbolModel::Columns::LAST_MODIFIED, true); + dv->setColumnHidden(BaseSymbolModel::Columns::SIDC, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::SYMBOL_SET, true); + + dv->setColumnHidden(BaseSymbolModel::Columns::SEARCH_ICON, true); + dv->setColumnHidden(BaseSymbolModel::Columns::FETCH_ICON, true); +// dv->setColumnHidden(BaseSymbolModel::Columns::FIND_BASE_SYMBOL, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::FIND_PREVIEW, true); + dv->setColumnHidden(BaseSymbolModel::Columns::FIND_IMAGE, true); + + dv->setColumnHidden(BaseSymbolModel::Columns::XML_SYMBOL_SIZE, true); +// dv->setColumnHidden(BaseSymbolModel::Columns::CATEGORY, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::SYMBOLCLASS, true); +// dv->setColumnHidden(BaseSymbolModel::Columns::TAGS, !showAll); + dv->setColumnHidden(BaseSymbolModel::Columns::GCTSTRING, true); + dv->setColumnHidden(BaseSymbolModel::Columns::GEOMETRYTYPE, true); + break; + } // switch + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +bool BaseSymbolLookupWidget::setCurrentTab(SYMBOL_TYPE symType) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START, symType=" << static_cast(symType); + if (!d->m_symbologyTypeToIdx.contains(symType)) + return false; + const int idx = d->m_symbologyTypeToIdx[symType]; + m_ui->tabWidget->setCurrentIndex(idx); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return true; +} + +void BaseSymbolLookupWidget::showEvent(QShowEvent *event) +{ + Q_UNUSED(event); + showHideColumns(SYMBOL_TYPE::ESRI); + showHideColumns(SYMBOL_TYPE::XML); + showHideColumns(SYMBOL_TYPE::ETS); +} + + +QSharedPointer BaseSymbolLookupWidget::getCurrentBaseSymbolItem() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + const int tabIdx = m_ui->tabWidget->currentIndex(); + LtsDataView::LtsDataView *dv = d->m_idxToDataViewCont[tabIdx]; + BaseSymbolModel *bsm = d->m_idxToModel[tabIdx]; + + if (!dv || !bsm) + return nullptr; + + // found :-) + QItemSelectionModel *selectModel = dv->selectionModel(); + + if (!selectModel || !selectModel->hasSelection()) + return nullptr; + + const QItemSelection &selectionSource = dv->mapSelectionToSource(selectModel->selection()); + const int row = selectionSource.indexes().first().row(); // assume singleSelection + + return bsm->getRowData(row); +} + +QString BaseSymbolLookupWidget::getCurrentBaseSymbolCode() const +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + QSharedPointer bsi = getCurrentBaseSymbolItem(); + if (bsi) + { + DEBUG_LOG(m_logger) << LOGFUNCTION + << " m_id" << bsi->m_id + << ", baseSymbolCode" << bsi->baseSymbolCode(); + return bsi->baseSymbolCode(); + } + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return QString(); +} + +QString BaseSymbolLookupWidget::getCurrentSidc() const +{ + QSharedPointer bsi = getCurrentBaseSymbolItem(); + if (bsi) + { + DEBUG_LOG(m_logger) << LOGFUNCTION + << " m_id" << bsi->m_id + << ", sidc" << bsi->sidc(); + return bsi->sidc(); + } + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return QString(); +} + +bool BaseSymbolLookupWidget::setCurrentSidc(const QString &sidc) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " sidc='" << sidc << "'"; + + if (sidc.isEmpty()) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " sidc is empty, maybe not set..."; + return false; + } + + if (!SidcCodec::isEncoded(sidc)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " sidc isn't encoded: '" << sidc << "'"; + return false; + } + + SYMBOL_TYPE symType = SYMBOL_TYPE::ESRI; + QString baseSymbolCode = sidc; + + const SidcCodec::SidcType sidcType = SidcCodec::decodeSidcType(sidc); + switch (sidcType) + { + case SidcCodec::SidcType::None: + { + ERROR_LOG(m_logger) << LOGFUNCTION << " SidcType::None isn't supported. Sidc= '" << sidc << "'"; + return false; + } + case SidcCodec::SidcType::Complete: + baseSymbolCode = AdvancedSymbology::SidcBuilderBase::concreteSidcToBaseSymbolCode(sidc); + case SidcCodec::SidcType::Base: + break; + case SidcCodec::SidcType::CivilEts: + symType = SYMBOL_TYPE::ETS; + break; + case SidcCodec::SidcType::CivilXml: + symType = SYMBOL_TYPE::XML; + break; + default : + { + ERROR_LOG(m_logger) << LOGFUNCTION << " SidcType is unknown. Sidc= '" << sidc << "'"; + return false; + } + } // switch + + + // switch to model/view/tabIdx + + if (!d->m_symbologyTypeToIdx.contains(symType)) + return false; + + const int tabIdx = d->m_symbologyTypeToIdx[symType]; + BaseSymbolModel *bsm = d->m_idxToModel[tabIdx]; + LtsDataView::LtsDataView *dv = d->m_idxToDataViewCont[tabIdx]; + + if (!bsm || !dv) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " switch to model/view/tabIndex failed, sidc '" << sidc << "'"; + return false; + } + + // switch to tabIdx + m_ui->tabWidget->setCurrentIndex(tabIdx); + + // switch to model/view + const int row = bsm->findRowForBaseSymbolCode(baseSymbolCode); + if (row >= 0) + { + const QModelIndex sourceIdx = bsm->index(row, 0); + dv->selectRow(dv->proxyModel()->mapFromSource(sourceIdx)); + dv->scrollTo(dv->proxyModel()->mapFromSource(sourceIdx)); + } + + INFO_LOG(m_logger) << LOGFUNCTION << " sidc='" << sidc << "'" + << " tabIdx = " << tabIdx + << " findRowForBaseSymbolCode -> row = " << row; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + return (row >= 0); +} + +void BaseSymbolLookupWidget::onClicked(const QModelIndex &index) +{ + Q_UNUSED(index); + + QSharedPointer bsi = getCurrentBaseSymbolItem(); + if (!bsi) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " item not found"; + return; + } + const QString &curBsc = bsi->baseSymbolCode(); + const QString &curSidc = bsi->sidc(); + emit symbolClicked(curBsc, curSidc); +} + +void BaseSymbolLookupWidget::onDoubleClicked(const QModelIndex &index) +{ + Q_UNUSED(index); + + QSharedPointer bsi = getCurrentBaseSymbolItem(); + if (!bsi) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " item not found"; + return; + } + const QString &curBsc = bsi->baseSymbolCode(); + const QString &curSidc = bsi->sidc(); + emit symbolDoubleClicked(curBsc, curSidc); +} + + +} // namespace Gui +} // namespace AdvancedSymbology + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_lookup_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_lookup_widget.h new file mode 100755 index 000000000..782f376bc --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_lookup_widget.h @@ -0,0 +1,104 @@ +/** + * @RDE_HEADER@ + * Widget for selecting (base) symbols + * $Id$ + * $HeadURL$ + * $Author$ + * Created by Norbert Thoden, NLE, 12.12.2019 + */ + +#pragma once + +#include +#include + +#include +#include + + +namespace Ui +{ +class BaseSymbolLookupWidget; +} + + +namespace AdvancedSymbology +{ +class BaseSymbolModel; +class BaseSymbolItem; +namespace Gui +{ + +enum class SYMBOL_TYPE +{ + ESRI = 0, + XML, + ETS +}; + +struct ModelItem +{ + ModelItem(SYMBOL_TYPE symbolType = SYMBOL_TYPE::ESRI, + const QString &name = QString(), + BaseSymbolModel *baseSymbolModel = nullptr) + : m_symbolType(symbolType), + m_name(name), + m_baseSymbolModel(baseSymbolModel) + {} + SYMBOL_TYPE m_symbolType; + QString m_name; + BaseSymbolModel *m_baseSymbolModel = nullptr; +}; + +using MODEL_CONT = QVector; + + +//------------------------------------------------------------------------------ +// BaseSymbolLookupWidget +//------------------------------------------------------------------------------ +class ADVANCEDSYMBOLOGYGUI_EXPORT BaseSymbolLookupWidget : public QWidget +{ + Q_OBJECT + +public: + explicit BaseSymbolLookupWidget(const MODEL_CONT &modelCont, QWidget *parent); + ~BaseSymbolLookupWidget(); + + bool participantMode() const; + void setParticipantMode(bool onOff); + + void setValidBaseSymbolCodes(const QSet &validBaseSymbolCodes); + + QSharedPointer getCurrentBaseSymbolItem() const; + QString getCurrentBaseSymbolCode() const; // encoded, baseSymbolCode + QString getCurrentSidc() const; // encoded, complete + bool setCurrentSidc(const QString &sidc); // encoded, baseSymbolCode or complete Sidc + +Q_SIGNALS: + void symbolClicked(const QString &baseSymbolCode, const QString &sidc); + void symbolDoubleClicked(const QString &baseSymbolCode, const QString &sidc); + +protected: + bool setCurrentTab(SYMBOL_TYPE symType); + void showHideColumns(SYMBOL_TYPE symType); + +protected Q_SLOTS: + void showEvent(QShowEvent *event) override; + + void onClicked(const QModelIndex &index); + void onDoubleClicked(const QModelIndex &index); + +private: + SituationMap::LOGGER_T m_logger; + + Ui::BaseSymbolLookupWidget *m_ui; + + struct Private; + /** + * The private d->pointer to not break BC when changing internals of this class + */ + Private *d = nullptr; +}; + +} // namespace Gui +} // namespace AdvancedSymbology diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_lookup_widget.ui b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_lookup_widget.ui new file mode 100755 index 000000000..ec83a4540 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/base_symbol_lookup_widget.ui @@ -0,0 +1,33 @@ + + + BaseSymbolLookupWidget + + + + 0 + 0 + 840 + 463 + + + + Form + + + + + + 0 + + + + Tab 1 + + + + + + + + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/field_table_model.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/field_table_model.cpp new file mode 100755 index 000000000..21e05ba86 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/field_table_model.cpp @@ -0,0 +1,126 @@ +/** + * + * @RDE_HEADER@ + * Helper model for SymbolModificationWidget + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "field_table_model.h" + +namespace AdvancedSymbology +{ +namespace Gui +{ + +int FieldTableModel::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : 2; +} + +int FieldTableModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : m_fieldKeys.size(); +} + +QVariant FieldTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) return QVariant(); + if (Qt::Orientation::Horizontal == orientation) + { + switch (section) + { + case 0 : return tr("Field Title"); + case 1 : return tr("Value"); + default : break; + } + } + return QVariant(); +} // QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override + + +QVariant FieldTableModel::data(const QModelIndex &index, int role) const +{ + if (index.row() >= 0 && index.row() < m_fieldKeys.size() && + role == Qt::DisplayRole) + { + const QString &key = m_fieldKeys.at(index.row()); + switch (index.column()) + { + case 0 : return key; + case 1 : return m_fieldValueMap[key]; + default : QVariant(); + } + } + return QVariant(); +} // QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override + +bool FieldTableModel::setFieldAttrMap(const QMap &fieldAttrMap) +{ + beginResetModel(); + { + m_fieldKeys = fieldAttrMap.keys(); + m_fieldKeys.sort(); + m_fieldValueMap = fieldAttrMap; + } + endResetModel(); + return true; +} // bool setFieldAttrMap( const QMap & fieldAttrMap ) + + +void FieldTableModel::setFieldAttribute(const QString &key, const QString &value) +{ + beginResetModel(); + { + m_fieldValueMap[key] = value; + if (!m_fieldKeys.contains(key)) + { + m_fieldKeys += key; + m_fieldKeys.sort(); + } + } + endResetModel(); +} // void setFieldAttribute( const QString & key, const QString & value ) + + +void FieldTableModel::setFieldAttribute(SituationMap::Participant::TextModifier textMod, const QString &value) +{ + const QString &key = participantAttributeKey(textMod); + if (!key.isEmpty()) setFieldAttribute(key, value); +} // void setFieldAttribute(SituationMap::TextModifier textMod, const QString & value) + + +void FieldTableModel::removeFieldAttrMap(const QString &key) +{ + if (m_fieldKeys.contains(key)) + { + beginResetModel(); + { + m_fieldKeys.removeAll(key); + m_fieldValueMap.remove(key); + } + endResetModel(); + } +} // void removeFieldAttrMap( const QString & key ) + + +QString FieldTableModel::fieldAttribute(const QString &key) const +{ + return m_fieldValueMap.value(key).toString(); +} // void fieldAttribute( const QString & key ) + +QString FieldTableModel::fieldAttribute(SituationMap::Participant::TextModifier textMod) +{ + const QString &key = participantAttributeKey(textMod); + if (key.isEmpty()) return QString(); + return fieldAttribute(key); +} // QString fieldAttribute( SituationMap::TextModifier textMod ) + +QString FieldTableModel::participantAttributeKey(SituationMap::Participant::TextModifier textMod) +{ + return SituationMap::Participant::toString(textMod); +} // QString participantAttributeKey(SituationMap::TextModifier textMod) + +} // namespace Gui +} // namespace AdvancedSymbology diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/field_table_model.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/field_table_model.h new file mode 100755 index 000000000..805a6bd78 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/field_table_model.h @@ -0,0 +1,47 @@ +/** + * + * @RDE_HEADER@ + * Helper model for SymbolModificationWidget + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include +#include + +namespace AdvancedSymbology +{ +namespace Gui +{ + +class FieldTableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + using QAbstractTableModel::QAbstractTableModel; + + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + const QMap &fieldAttrMap() const { return m_fieldValueMap; } + bool setFieldAttrMap(const QMap &fieldAttrMap); + void setFieldAttribute(const QString &key, const QString &value); + void setFieldAttribute(SituationMap::Participant::TextModifier textMod, const QString &value); + void removeFieldAttrMap(const QString &key); + QString fieldAttribute(const QString &key) const; + QString fieldAttribute(SituationMap::Participant::TextModifier textMod); + +private: + static QString participantAttributeKey(SituationMap::Participant::TextModifier textMod); +private: + QStringList m_fieldKeys; + QMap m_fieldValueMap; + +}; // FieldTableModel + +} // namespace Gui +} // namespace AdvancedSymbology diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/symbol_modification_dialog.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/symbol_modification_dialog.cpp new file mode 100755 index 000000000..c9499d411 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/symbol_modification_dialog.cpp @@ -0,0 +1,137 @@ +/** +* @RDE_HEADER@ +* symbol_modification_dialog for sidc configuration +* $Id$ +* $HeadURL$ +* $Author$ +* Created by Karwat, SLSE, 2017-09-07 +*/ + +#include "symbol_modification_dialog.h" +#include "symbol_modification_widget.h" +#include + + +namespace AdvancedSymbology +{ +namespace Gui +{ + +//Dialog + +SymbolModificationDialog::SymbolModificationDialog(SituationMap::Map *map, bool isParticipant, QWidget *parent) + : LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkCancel) +{ + initLayout(map, isParticipant); +} + +/* + * TODO: FIXME: Warum ist das ein OkCancel Dialog aber Ok wird gehided und Cancel dann wieder zu close umbenannt? + * Warum ist das nicht gleich ein CloseONly Dialog? + */ +void SymbolModificationDialog::removeOkAndApplyButtons() +{ + m_buttonBox->getButton(LtsWidgets::DialogButton::Ok)->setVisible(false); + m_buttonBox->getButton(LtsWidgets::DialogButton::Apply)->setVisible(false); + m_buttonBox->getButton(LtsWidgets::DialogButton::Close)->setText(tr("Close")); +} + +void SymbolModificationDialog::initLayout(SituationMap::Map *map, bool isParticipant) +{ + + QVBoxLayout *layout = new QVBoxLayout(this); + m_symbolModificationWidget = new SymbolModificationWidget(map, isParticipant, map->symbolDictionary(), this); + connect(m_symbolModificationWidget, &SymbolModificationWidget::changed, this, &SymbolModificationDialog::changed); + connect(m_symbolModificationWidget, &SymbolModificationWidget::changed, this, &SymbolModificationDialog::slotChangesMade); + m_buttonBox->connectButton(LtsWidgets::DialogButton::No, m_symbolModificationWidget, &SymbolModificationWidget::onRevert); + m_buttonBox->setText(LtsWidgets::DialogButton::No, tr("Revert")); + layout->addWidget(m_symbolModificationWidget, 2); +} + + + +void SymbolModificationDialog::aboutToClose() { emit signalAboutToClose(); } +bool SymbolModificationDialog::apply() +{ + //const QString & sidc = getCurrentSidc(); + + //if (sidc.isEmpty()) return false; + + //emit lookupApplied( sidc ); + return true; +} // SymbolModificationDialog::apply() + + +void SymbolModificationDialog::retranslate() +{ + // lupdate doesn't like translate(tr("foo")) ... + const QString txt(tr("Symbol Modification")); + translate(txt); + +} // SymbolModificationDialog::retranslate() + + + +//Frameless +SymbolModificationDialogFrameless::SymbolModificationDialogFrameless(SituationMap::Map *map, bool isParticipant, QWidget *parent) + : LtsWidgets::LtsFramelessDialog(parent) + , m_buttonBox(nullptr) +{ + initLayout(map, isParticipant, map->symbolDictionary()); +} + +SymbolModificationDialogFrameless::SymbolModificationDialogFrameless(SymbolDictionary &symbolDictionary, bool isParticipant, QWidget *parent) + : LtsWidgets::LtsFramelessDialog(parent) + , m_buttonBox(nullptr) +{ + initLayout(nullptr, isParticipant, symbolDictionary); +} + + +void SymbolModificationDialogFrameless::initLayout(SituationMap::Map *map, bool isParticipant, SymbolDictionary &symbolDictionary) +{ + QVBoxLayout *layout = new QVBoxLayout(this); + m_buttonBox = new LtsWidgets::LtsDialogButtonBox(this); + m_symbolModificationWidget = new SymbolModificationWidget(map, isParticipant, symbolDictionary, this); + connect(m_symbolModificationWidget, &SymbolModificationWidget::changed, this, &SymbolModificationDialogFrameless::sidcChanged); + + layout->addWidget(m_symbolModificationWidget, 2); + layout->addWidget(m_buttonBox); + + connect(m_buttonBox->getButton(LtsWidgets::DialogButton::Ok), &QPushButton::clicked, this, &SymbolModificationDialogFrameless::apply); + connect(m_buttonBox->getButton(LtsWidgets::DialogButton::Close), &QPushButton::clicked, this, &SymbolModificationDialogFrameless::onClose); + m_buttonBox->connectButton(LtsWidgets::DialogButton::No, m_symbolModificationWidget, &SymbolModificationWidget::onRevert); + m_buttonBox->setText(LtsWidgets::DialogButton::No, tr("Revert")); + setMinimumWidth(800); + setMinimumHeight(300); + resize(minimumHeight(), minimumWidth()); +} + + +void SymbolModificationDialogFrameless::setSidc(const QString &sidc) +{ + m_symbolModificationWidget->setSidc(sidc); + m_oldSidc = sidc; +} + +void SymbolModificationDialogFrameless::apply() +{ + emit sidcOnClose(m_newSidc); + close(); +} + +void SymbolModificationDialogFrameless::onClose() +{ + emit sidcOnClose(m_oldSidc); + close(); +} + +void SymbolModificationDialogFrameless::sidcChanged() +{ + const QString &newSidc = m_symbolModificationWidget->modifiedSidc(); + m_newSidc = newSidc; + emit changed(); +} + +} // namespace Gui +} // namespace AdvancedSymbology diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/symbol_modification_dialog.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/symbol_modification_dialog.h new file mode 100755 index 000000000..a856ea879 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/symbol_modification_dialog.h @@ -0,0 +1,86 @@ +/** +* +* @RDE_HEADER@ +* symbol_modification_dialog for sidc configuration +* $Id$ +* $HeadURL$ +* $Author$ +* Created by Karwat, SLSE, 2017-09-07 +*/ + +#pragma once + +#include + +#include + +#include "symbol_modification_widget.h" + + + +namespace AdvancedSymbology +{ +class SymbolDictionary; + +namespace Gui +{ + +class ADVANCEDSYMBOLOGYGUI_EXPORT SymbolModificationDialog : public LtsWidgets::LtsDialog +{ + Q_OBJECT + +public: + + explicit SymbolModificationDialog(SituationMap::Map *map, bool isParticipant, QWidget *parent = nullptr); + + SymbolModificationWidget *getWidget() { return m_symbolModificationWidget; } + void removeOkAndApplyButtons(); + +signals: + void changed(); + void signalAboutToClose(); + +protected: + + bool apply() override; + + void aboutToClose() override; + +private: + void retranslate() override; + void initLayout(SituationMap::Map *map, bool isParticipant); + + SymbolModificationWidget *m_symbolModificationWidget; +}; // SymbolModificationDialog + +class ADVANCEDSYMBOLOGYGUI_EXPORT SymbolModificationDialogFrameless : public LtsWidgets::LtsFramelessDialog +{ + Q_OBJECT + +public: + explicit SymbolModificationDialogFrameless(SituationMap::Map *map, bool isParticipant, QWidget *parent = nullptr); + explicit SymbolModificationDialogFrameless(SymbolDictionary &symbolDictionary, bool isParticipant, QWidget *parent = nullptr); + + void setSidc(const QString &sidc); + + +signals: + void changed(); + void sidcOnClose(const QString &); + +protected slots: + void apply(); //next override + void onClose(); + void sidcChanged(); + +private: + void initLayout(SituationMap::Map *map, bool isParticipant, SymbolDictionary &symbolDictionary); + SymbolModificationWidget *m_symbolModificationWidget; + LtsWidgets::LtsDialogButtonBox *m_buttonBox; + QString m_newSidc; + QString m_oldSidc; + +}; // SymbolModificationDialogFrameless + +} // namespace Gui +} // namespace AdvancedSymbology diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/symbol_modification_widget.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/symbol_modification_widget.cpp new file mode 100755 index 000000000..9f7608eb0 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/symbol_modification_widget.cpp @@ -0,0 +1,1298 @@ +/** +* +* @RDE_HEADER@ +* symbol_modification_wdiget for sidc configuration +* $Id$ +* $HeadURL$ +* $Author$ +* Created by Karwat, SLSE, 2017-09-07 +*/ + + +#include "symbol_modification_widget.h" + +#include "field_table_model.h" + +#include + +#include +#include +#include + +// Lts +#include + +// Qt +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace AdvancedSymbology +{ +namespace Gui +{ + +class SidcWithIcon : public QWidget +{ + +public: + + explicit SidcWithIcon(QWidget *parent = nullptr) : QWidget(parent) + { + QHBoxLayout *layout = new QHBoxLayout(this); + // layout->setContentsMargins(0,0,0,0); + // layout->setSpacing(0); + + + m_lbImage = new QLabel(this); + // m_lbImage->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + // m_lbImage->setMaximumSize(QSize(100,100)); + layout->addWidget(m_lbImage); + + m_lbText = new QLabel(this); + // m_lbText->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + // m_lbImage->setMaximumSize(QSize(100,100)); + layout->addWidget(m_lbText); + + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + // setMaximumHeight(100); + // setMaximumWidth(300); + } // SidcWithIcon(QWidget* parent = nullptr) + + void setImage(const QImage &img) { m_lbImage->setPixmap(QPixmap::fromImage(img).scaledToHeight(100)); } + + void setText(const QString &text) { m_lbText->setText(text); } + +private: + + QLabel *m_lbImage; + QLabel *m_lbText; + +}; // SidcWithIcon + + + + +//--------------------------------- +// EnumComboBox +//--------------------------------- + +using MyComboBox = LtsWidgets::LtsComboBox; + + +template class EnumComboBox : public MyComboBox +{ + +public: + + explicit EnumComboBox(QWidget *parent = nullptr) : MyComboBox(parent) {;} + + void insertNumberedItem(int idx, T value) + { + insertNumberedItem(idx, AdvancedSymbology::SidcDefinitions::toPixmap(value), AdvancedSymbology::SidcDefinitions::toString(value), static_cast(value)); + } // void insertNumberedItem( int idx, T value ) + + + void insertNumberedItem(int idx, const QString &text, int value) + { + MyComboBox::insertItem(idx, QStringLiteral("%1 %2").arg(value).arg(text), value); + } // void insertNumberedItem( int idx, const QString & text, int value ) + + + void insertNumberedItem(int idx, const QPixmap & pixmap, const QString &text, int value) + { + MyComboBox::insertItem(idx, pixmap, QStringLiteral("%1 %2").arg(value).arg(text), value); + } // void insertNumberedItem( int idx, const QString & text, int value ) + + + void insertItems(const QVector &items) + { + MyComboBox::clear(); + + int idx = 0; + for (const auto &item : items) + insertNumberedItem(idx++, item); + if (MyComboBox::count() > 0) + setCurrentIndex(0); + } // void insertItems( const QVector & items ) + + + T currentValue() const { return static_cast(currentData().toInt()); } + int currentValueAsInt() const { return currentData().toInt(); } + + + bool setCurrent(const T &newCurrentValue) + { + QVariant newCurVar(static_cast(newCurrentValue)); + MyComboBox::setCurrentIndex(findData(newCurVar)); + return true; + } // bool setCurrent( const T & newCurrentValue ) + +signals: + + void changed(T newValue); + +protected Q_SLOTS: + + void onCurrentIndexChanged(int) { emit changed(currentValue()); } + +}; // template class EnumComboBox + + + + + +class SymbolModificationWidget::Internal +{ +public: + + QPointer m_map; + + AdvancedSymbology::SidcDefinitions m_sidcDefinitions; + AdvancedSymbology::SidcBuilder m_sidcBuilder; + AdvancedSymbology::SymbolDictionary &m_dict; + AdvancedSymbology::SymbologyType m_symbologyType; + + bool m_isParticipant; + + QString m_originalSidc; + QString m_lastModifiedSidc; + QString m_baseSymbolCode; + QString m_modifiedSidc; + + QTabWidget *m_tabWidget; + QStackedWidget *m_tabSidcEdit; + QWidget *m_tabFieldEdit; + QWidget *m_tabPreview; + + EnumComboBox *m_cbContext_MIL2525D; + EnumComboBox *m_cbIdentity_MIL2525D; + EnumComboBox *m_cbSymbolSet_MIL2525D; + EnumComboBox *m_cbStatus_MIL2525D; + EnumComboBox *m_cbHQTFDummy_MIL2525D; + + EnumComboBox *m_cbAmplifierGroup_MIL2525D; + EnumComboBox *m_cbAmplifierDescriptor_MIL2525D; + EnumComboBox *m_cbSector1Modifier_MIL2525D; + EnumComboBox *m_cbSector2Modifier_MIL2525D; + + EnumComboBox *m_cbIdentity_APP6; + MyComboBox *m_cbSize_APP6; + MyComboBox *m_cbSizeHQ_APP6; + MyComboBox *m_cbSizeTF_APP6; + MyComboBox *m_cbSizeFDHQ_APP6; + MyComboBox *m_cbSizeFDTF_APP6; + MyComboBox *m_cbSizeFD_APP6; + MyComboBox *m_cbMobility_APP6; + MyComboBox *m_cbStatus_APP6; + MyComboBox *m_cbOrderOfBattle_APP6; + + MyComboBox *m_cbFieldTitle; + QLineEdit *m_leFieldValue; + QTableView *m_fieldTable; + FieldTableModel *m_fieldTableModel; + + + explicit Internal(SituationMap::Map *map, SymbolDictionary &symbolDictionary) + : m_map(map) + , m_sidcDefinitions((map) ? & map->symbolDictionary() : & symbolDictionary) + , m_sidcBuilder((map) ? & map->symbolDictionary() : & symbolDictionary) + , m_dict(symbolDictionary) + , m_symbologyType(AdvancedSymbology::SymbologyType::None) + , m_isParticipant(false) + , m_originalSidc() + , m_lastModifiedSidc() + , m_baseSymbolCode() + , m_modifiedSidc() + , m_tabWidget(nullptr) + , m_tabSidcEdit(nullptr) + , m_tabFieldEdit(nullptr) + , m_tabPreview(nullptr) + , m_cbContext_MIL2525D(nullptr) + , m_cbIdentity_MIL2525D(nullptr) + , m_cbSymbolSet_MIL2525D(nullptr) + , m_cbStatus_MIL2525D(nullptr) + , m_cbHQTFDummy_MIL2525D(nullptr) + , m_cbAmplifierGroup_MIL2525D(nullptr) + , m_cbAmplifierDescriptor_MIL2525D(nullptr) + , m_cbSector1Modifier_MIL2525D(nullptr) + , m_cbSector2Modifier_MIL2525D(nullptr) + , m_cbIdentity_APP6(nullptr) + , m_cbSize_APP6(nullptr) + , m_cbSizeHQ_APP6(nullptr) + , m_cbSizeTF_APP6(nullptr) + , m_cbSizeFDHQ_APP6(nullptr) + , m_cbSizeFDTF_APP6(nullptr) + , m_cbSizeFD_APP6(nullptr) + , m_cbMobility_APP6(nullptr) + , m_cbStatus_APP6(nullptr) + , m_cbOrderOfBattle_APP6(nullptr) + , m_cbFieldTitle(nullptr) + , m_leFieldValue(nullptr) + , m_fieldTable(nullptr) + , m_fieldTableModel(nullptr) + { + } // explicit Internal() + + ~Internal() = default; + + +}; // SymbolModificationWidget::Internal + + +enum class SymbologyTypeStackIndex +{ + APP6 = 0, + MIL2525D = 1 +}; // SymbologyTypeStackIndex + + +//--------------------------------- +// SymbolModificationWidget +//--------------------------------- +SymbolModificationWidget::SymbolModificationWidget(SituationMap::Map *map, bool isParticipant, SymbolDictionary &symbolDictionary, QWidget *parent) + : QWidget(parent) + , m_internal(new Internal(map, symbolDictionary)) +{ + m_internal->m_isParticipant = isParticipant; + + QVBoxLayout *layout = new QVBoxLayout(this); + QHBoxLayout *layoutCtrl = new QHBoxLayout(); + + m_internal->m_tabWidget = new QTabWidget(this); + + m_internal->m_tabSidcEdit = new QStackedWidget(m_internal->m_tabWidget); + m_internal->m_tabFieldEdit = new QWidget(m_internal->m_tabWidget); + m_internal->m_tabPreview = new QWidget(m_internal->m_tabWidget); + + m_internal->m_tabWidget->setTabsClosable(false); + m_internal->m_tabWidget->setMovable(false); + m_internal->m_tabWidget->setTabBarAutoHide(true); + + m_internal->m_tabSidcEdit -> setObjectName(QStringLiteral("SymbolModificationWidgetSymbolSidcEdit")); + m_internal->m_tabFieldEdit-> setObjectName(QStringLiteral("SymbolModificationWidgetSymbolFieldEdit")); + m_internal->m_tabPreview -> setObjectName(QStringLiteral("SymbolModificationWidgetSymbolPreview")); + + m_internal->m_tabSidcEdit -> setEnabled(false); + m_internal->m_tabFieldEdit -> setEnabled(false); + m_internal->m_tabPreview -> setEnabled(false); + + m_internal->m_tabSidcEdit -> hide(); + m_internal->m_tabFieldEdit -> hide(); + m_internal->m_tabPreview -> hide(); + + layout->setContentsMargins(10, 10, 10, 10); + layout->addSpacing(10); + layout->addWidget(m_internal->m_tabWidget); + layout->addSpacing(10); + layout->addLayout(layoutCtrl); + + // d->m_preview = new QVBoxLayout(); + // d->m_preview->setObjectName(QLatin1String("previewCol")); + // layout->addLayout(d->m_preview, 0, 0); + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + QWidget * sidcEditAPP6 = new QWidget(m_internal->m_tabSidcEdit); + QWidget * sidcEditMIL2525D = new QWidget(m_internal->m_tabSidcEdit); + + + m_internal->m_tabSidcEdit->addWidget(sidcEditAPP6); // SymbologyTypeStackIndex::APP6 + m_internal->m_tabSidcEdit->addWidget(sidcEditMIL2525D); // SymbologyTypeStackIndex::MIL2525D + + buildSidcModifierTab_APP6(sidcEditAPP6); + buildSidcModifierTab_MIL2525D( sidcEditMIL2525D ); + + + buildFieldModifierTab(m_internal->m_tabFieldEdit); + + + buildPreview(m_internal->m_tabPreview); + + +} // SymbolModificationWidget::SymbolModificationWidget + +void SymbolModificationWidget::retranslate() +{ + // FIXME: UI Retranslation does not work with the entire Plan Editor, as there are no UIs ... + //translate(m_ui, tr("??")); +} + +void SymbolModificationWidget::buildSidcModifierTab_MIL2525D(QWidget *widget) +{ + QHBoxLayout *layout = new QHBoxLayout(widget); + QFormLayout *layoutEditLeft = new QFormLayout(); + QFormLayout *layoutEditRight = new QFormLayout(); + + layout->addStretch(2); + layout->addLayout(layoutEditLeft); + layout->addStretch(2); + QFrame *separator = new QFrame(widget); + separator->setFrameStyle(QFrame::HLine | QFrame::Plain); + layout->addWidget(separator); + layout->addStretch(2); + layout->addLayout(layoutEditRight); + layout->addStretch(2); + + // Context + m_internal->m_cbContext_MIL2525D = new EnumComboBox(widget); + m_internal->m_cbContext_MIL2525D->insertItems(AdvancedSymbology::SidcDefinitions::contexts()); + + connect(m_internal->m_cbContext_MIL2525D + , static_cast(&QComboBox::currentIndexChanged) + , this + , &SymbolModificationWidget::onContextChanged); + + // Identity + m_internal->m_cbIdentity_MIL2525D = new EnumComboBox(widget); + m_internal->m_cbIdentity_MIL2525D->insertItems(AdvancedSymbology::SidcDefinitions::identities()); + + connect(m_internal->m_cbIdentity_MIL2525D + , static_cast(&QComboBox::currentIndexChanged) + , this + , &SymbolModificationWidget::onIdentityChanged); + + // Status + m_internal->m_cbStatus_MIL2525D = new EnumComboBox(widget); + m_internal->m_cbStatus_MIL2525D->insertItems(AdvancedSymbology::SidcDefinitions::statuss()); + + connect(m_internal->m_cbStatus_MIL2525D + , static_cast(&QComboBox::currentIndexChanged) + , this + , &SymbolModificationWidget::onStatusChanged); + + // HQTFDummy + m_internal->m_cbHQTFDummy_MIL2525D = new EnumComboBox(widget); + m_internal->m_cbHQTFDummy_MIL2525D->insertItems(AdvancedSymbology::SidcDefinitions::hQTFDummy()); + + connect(m_internal->m_cbHQTFDummy_MIL2525D + , static_cast(&QComboBox::currentIndexChanged) + , this + , &SymbolModificationWidget::onHQTFDummyChanged); + + layoutEditLeft->setHorizontalSpacing(10); + layoutEditLeft->setLabelAlignment(Qt::AlignRight); + + layoutEditLeft->addRow(tr("Context"), m_internal->m_cbContext_MIL2525D); + layoutEditLeft->addRow(tr("Identity"), m_internal->m_cbIdentity_MIL2525D); + layoutEditLeft->addRow(tr("Status"), m_internal->m_cbStatus_MIL2525D); + layoutEditLeft->addRow(tr("HqTfDummy"), m_internal->m_cbHQTFDummy_MIL2525D); + + // AmplifierGroup + m_internal->m_cbAmplifierGroup_MIL2525D = new EnumComboBox(widget); + m_internal->m_cbAmplifierGroup_MIL2525D->insertItems(AdvancedSymbology::SidcDefinitions::amplifierGroups()); + + connect(m_internal->m_cbAmplifierGroup_MIL2525D + , static_cast(&QComboBox::currentIndexChanged) + , this + , &SymbolModificationWidget::onAmplifierGroupChanged, Qt::DirectConnection); // as soon as possible!!! + + // AmplifierDescriptor + m_internal->m_cbAmplifierDescriptor_MIL2525D = new EnumComboBox(widget); + // to fill->use slot of primary ComboBox: + onAmplifierGroupChanged(); + + connect(m_internal->m_cbAmplifierDescriptor_MIL2525D + , static_cast(&QComboBox::currentIndexChanged) + , this + , &SymbolModificationWidget::onAmplifierDesriptorChanged); + + // Sector1Modifier + m_internal->m_cbSector1Modifier_MIL2525D = new EnumComboBox(widget); + connect(m_internal->m_cbSector1Modifier_MIL2525D + , static_cast(&QComboBox::currentIndexChanged) + , this + , &SymbolModificationWidget::onSector1ModifierChanged); + + // Sector2Modifier + m_internal->m_cbSector2Modifier_MIL2525D = new EnumComboBox(widget); + connect(m_internal->m_cbSector2Modifier_MIL2525D + , static_cast(&QComboBox::currentIndexChanged) + , this + , &SymbolModificationWidget::onSector2ModifierChanged); + + + layoutEditRight->setHorizontalSpacing(10); + layoutEditRight->setLabelAlignment(Qt::AlignRight); + + layoutEditRight->addRow(tr("AmplifierGroup"), m_internal->m_cbAmplifierGroup_MIL2525D); + layoutEditRight->addRow(tr("AmplifierDescriptor"), m_internal->m_cbAmplifierDescriptor_MIL2525D); + separator = new QFrame(widget); + separator->setFrameStyle(QFrame::HLine | QFrame::Plain); + layoutEditRight->addRow(separator); + layoutEditRight->addRow( tr("Sector 1 Modifier"), m_internal->m_cbSector1Modifier_MIL2525D ); + layoutEditRight->addRow( tr("Sector 2 Modifier"), m_internal->m_cbSector2Modifier_MIL2525D ); + +} // void SymbolModificationWidget::buildSidcModifierTab_MIL2525D( QWidget * widget ) + + +void SymbolModificationWidget::buildSidcModifierTab_APP6(QWidget * widget) +{ + QFormLayout *layout = new QFormLayout(widget); + + // Identity + m_internal->m_cbIdentity_APP6 = new EnumComboBox(widget); + m_internal->m_cbIdentity_APP6->insertItems(AdvancedSymbology::SidcDefinitions::identities()); + + m_internal->m_cbStatus_APP6 = new MyComboBox(widget); + m_internal->m_cbOrderOfBattle_APP6 = new MyComboBox(widget); + m_internal->m_cbSize_APP6 = new MyComboBox(widget); + m_internal->m_cbSizeHQ_APP6 = new MyComboBox(widget); + m_internal->m_cbSizeTF_APP6 = new MyComboBox(widget); + m_internal->m_cbSizeFDHQ_APP6 = new MyComboBox(widget); + m_internal->m_cbSizeFDTF_APP6 = new MyComboBox(widget); + m_internal->m_cbSizeFD_APP6 = new MyComboBox(widget); + m_internal->m_cbMobility_APP6 = new MyComboBox(widget); + + // Size/Mobility + // Zwei abhaengige ComboBoxen + m_internal->m_cbStatus_APP6->addItem(tr("Anticipated/Planned"), QVariant(QLatin1Char('A'))); + m_internal->m_cbStatus_APP6->addItem(tr("Present") , QVariant(QLatin1Char('P'))); + + m_internal->m_cbOrderOfBattle_APP6->addItem(tr("None") , QVariant(QLatin1Char('*'))); + m_internal->m_cbOrderOfBattle_APP6->addItem(tr("Air OB") , QVariant(QLatin1Char('A'))); + m_internal->m_cbOrderOfBattle_APP6->addItem(tr("Electronic OB") , QVariant(QLatin1Char('E'))); + m_internal->m_cbOrderOfBattle_APP6->addItem(tr("Civilian OB") , QVariant(QLatin1Char('C'))); + m_internal->m_cbOrderOfBattle_APP6->addItem(tr("Ground OB") , QVariant(QLatin1Char('G'))); + m_internal->m_cbOrderOfBattle_APP6->addItem(tr("Maritime OB") , QVariant(QLatin1Char('M'))); + m_internal->m_cbOrderOfBattle_APP6->addItem(tr("Strategic Force Related"), QVariant(QLatin1Char('S'))); + m_internal->m_cbOrderOfBattle_APP6->addItem(tr("Control Markings") , QVariant(QLatin1Char('X'))); + + m_internal->m_cbSize_APP6->addItem(tr("None") , QStringLiteral("--")); + m_internal->m_cbSize_APP6->addItem(tr("Team/Crew") , QStringLiteral("_A")); + m_internal->m_cbSize_APP6->addItem(tr("Squad") , QStringLiteral("_B")); + m_internal->m_cbSize_APP6->addItem(tr("Platoon") , QStringLiteral("_C")); + m_internal->m_cbSize_APP6->addItem(tr("Company") , QStringLiteral("_D")); + m_internal->m_cbSize_APP6->addItem(tr("Battalion") , QStringLiteral("_E")); + m_internal->m_cbSize_APP6->addItem(tr("Regiment/Group") , QStringLiteral("_F")); + m_internal->m_cbSize_APP6->addItem(tr("Brigade") , QStringLiteral("_H")); + m_internal->m_cbSize_APP6->addItem(tr("Division") , QStringLiteral("_I")); + m_internal->m_cbSize_APP6->addItem(tr("Corps/Mef") , QStringLiteral("_J")); + m_internal->m_cbSize_APP6->addItem(tr("Army") , QStringLiteral("_K")); + m_internal->m_cbSize_APP6->addItem(tr("Army Group/Front"), QStringLiteral("_L")); + m_internal->m_cbSize_APP6->addItem(tr("Region") , QStringLiteral("-M")); + + m_internal->m_cbSizeHQ_APP6->addItem(tr("None") , QStringLiteral("--")); + m_internal->m_cbSizeHQ_APP6->addItem(tr("Headquarters (HQ)"), QStringLiteral("A-")); + m_internal->m_cbSizeHQ_APP6->addItem(tr("HQ Team/Crew") , QStringLiteral("AA")); + m_internal->m_cbSizeHQ_APP6->addItem(tr("HQ Squad") , QStringLiteral("AB")); + m_internal->m_cbSizeHQ_APP6->addItem(tr("HQ Section") , QStringLiteral("AC")); + m_internal->m_cbSizeHQ_APP6->addItem(tr("HQ Platoon") , QStringLiteral("AD")); + m_internal->m_cbSizeHQ_APP6->addItem(tr("HQ Company") , QStringLiteral("AE")); + m_internal->m_cbSizeHQ_APP6->addItem(tr("HQ Battalion") , QStringLiteral("AF")); + m_internal->m_cbSizeHQ_APP6->addItem(tr("HQ Regiment/Group"), QStringLiteral("AG")); + m_internal->m_cbSizeHQ_APP6->addItem(tr("HQ Brigade") , QStringLiteral("AH")); + m_internal->m_cbSizeHQ_APP6->addItem(tr("HQ Division") , QStringLiteral("AI")); + m_internal->m_cbSizeHQ_APP6->addItem(tr("HQ Corp/MEF") , QStringLiteral("AJ")); + m_internal->m_cbSizeHQ_APP6->addItem(tr("HQ Army") , QStringLiteral("AK")); + m_internal->m_cbSizeHQ_APP6->addItem(tr("HQ Region") , QStringLiteral("AM")); + + m_internal->m_cbSizeTF_APP6->addItem(tr("None") , QStringLiteral("--")); + m_internal->m_cbSizeTF_APP6->addItem(tr("Task Force (TF) HQ"), QStringLiteral("B-")); + m_internal->m_cbSizeTF_APP6->addItem(tr("TF Team/Crew") , QStringLiteral("BA")); + m_internal->m_cbSizeTF_APP6->addItem(tr("TF Squad") , QStringLiteral("BB")); + m_internal->m_cbSizeTF_APP6->addItem(tr("TF Section") , QStringLiteral("BC")); + m_internal->m_cbSizeTF_APP6->addItem(tr("TF Platoon") , QStringLiteral("BD")); + m_internal->m_cbSizeTF_APP6->addItem(tr("TF Company") , QStringLiteral("BE")); + m_internal->m_cbSizeTF_APP6->addItem(tr("TF Battalion") , QStringLiteral("BF")); + m_internal->m_cbSizeTF_APP6->addItem(tr("TF Regiment/Group") , QStringLiteral("BG")); + m_internal->m_cbSizeTF_APP6->addItem(tr("TF Brigade") , QStringLiteral("BH")); + m_internal->m_cbSizeTF_APP6->addItem(tr("TF Division") , QStringLiteral("BI")); + m_internal->m_cbSizeTF_APP6->addItem(tr("TF Corp/MEF") , QStringLiteral("BJ")); + m_internal->m_cbSizeTF_APP6->addItem(tr("TF Army") , QStringLiteral("BK")); + m_internal->m_cbSizeTF_APP6->addItem(tr("TF Region") , QStringLiteral("BM")); + + m_internal->m_cbSizeFDHQ_APP6->addItem(tr("None") , QStringLiteral("--")); + m_internal->m_cbSizeFDHQ_APP6->addItem(tr("Feint Dummy (FD) HQ") , QStringLiteral("C-")); + m_internal->m_cbSizeFDHQ_APP6->addItem(tr("FD HQ Team/Crew") , QStringLiteral("CA")); + m_internal->m_cbSizeFDHQ_APP6->addItem(tr("FD HQ Squad") , QStringLiteral("CB")); + m_internal->m_cbSizeFDHQ_APP6->addItem(tr("FD HQ Section") , QStringLiteral("CC")); + m_internal->m_cbSizeFDHQ_APP6->addItem(tr("FD HQ Platoon)") , QStringLiteral("CD")); + m_internal->m_cbSizeFDHQ_APP6->addItem(tr("FD HQ Company") , QStringLiteral("CE")); + m_internal->m_cbSizeFDHQ_APP6->addItem(tr("FD HQ Battalion") , QStringLiteral("CF")); + m_internal->m_cbSizeFDHQ_APP6->addItem(tr("FD HQ Regiment/Group"), QStringLiteral("CG")); + m_internal->m_cbSizeFDHQ_APP6->addItem(tr("FD HQ Brigade") , QStringLiteral("CH")); + m_internal->m_cbSizeFDHQ_APP6->addItem(tr("FD HQ Division") , QStringLiteral("CI")); + m_internal->m_cbSizeFDHQ_APP6->addItem(tr("FD HQ Corp/MEF") , QStringLiteral("CJ")); + m_internal->m_cbSizeFDHQ_APP6->addItem(tr("FD HQ Army") , QStringLiteral("CK")); + m_internal->m_cbSizeFDHQ_APP6->addItem(tr("FD HQ Region") , QStringLiteral("CM")); + + m_internal->m_cbSizeFDTF_APP6->addItem(tr("None") , QStringLiteral("--")); + m_internal->m_cbSizeFDTF_APP6->addItem(tr("Feint Dummy (FD/TF) HQ") , QStringLiteral("D-")); + m_internal->m_cbSizeFDTF_APP6->addItem(tr("FD/TF HQ Team/Crew") , QStringLiteral("DA")); + m_internal->m_cbSizeFDTF_APP6->addItem(tr("FD/TF HQ Squad") , QStringLiteral("DB")); + m_internal->m_cbSizeFDTF_APP6->addItem(tr("FD/TF HQ Section") , QStringLiteral("DC")); + m_internal->m_cbSizeFDTF_APP6->addItem(tr("FD/TF HQ Platoon)") , QStringLiteral("DD")); + m_internal->m_cbSizeFDTF_APP6->addItem(tr("FD/TF HQ Company") , QStringLiteral("DE")); + m_internal->m_cbSizeFDTF_APP6->addItem(tr("FD/TF HQ Battalion") , QStringLiteral("DF")); + m_internal->m_cbSizeFDTF_APP6->addItem(tr("FD/TF HQ Regiment/Group"), QStringLiteral("DG")); + m_internal->m_cbSizeFDTF_APP6->addItem(tr("FD/TF HQ Brigade") , QStringLiteral("DH")); + m_internal->m_cbSizeFDTF_APP6->addItem(tr("FD/TF HQ Division") , QStringLiteral("DI")); + m_internal->m_cbSizeFDTF_APP6->addItem(tr("FD/TF HQ Corp/MEF") , QStringLiteral("DJ")); + m_internal->m_cbSizeFDTF_APP6->addItem(tr("FD/TF HQ Army") , QStringLiteral("DK")); + m_internal->m_cbSizeFDTF_APP6->addItem(tr("FD/TF HQ Region") , QStringLiteral("DM")); + + + m_internal->m_cbSizeFD_APP6->addItem(tr("None") , QStringLiteral("--")); + m_internal->m_cbSizeFD_APP6->addItem(tr("Feint Dummy (FD)") , QStringLiteral("F-")); + m_internal->m_cbSizeFD_APP6->addItem(tr("FD Team/Crew") , QStringLiteral("FA")); + m_internal->m_cbSizeFD_APP6->addItem(tr("FD Squad") , QStringLiteral("FB")); + m_internal->m_cbSizeFD_APP6->addItem(tr("FD Section") , QStringLiteral("FC")); + m_internal->m_cbSizeFD_APP6->addItem(tr("FD Platoon)") , QStringLiteral("FD")); + m_internal->m_cbSizeFD_APP6->addItem(tr("FD Company") , QStringLiteral("FE")); + m_internal->m_cbSizeFD_APP6->addItem(tr("FD Battalion") , QStringLiteral("FF")); + m_internal->m_cbSizeFD_APP6->addItem(tr("FD Regiment/Group"), QStringLiteral("FG")); + m_internal->m_cbSizeFD_APP6->addItem(tr("FD Brigade") , QStringLiteral("FH")); + m_internal->m_cbSizeFD_APP6->addItem(tr("FD Division") , QStringLiteral("FI")); + m_internal->m_cbSizeFD_APP6->addItem(tr("FD Corp/MEF") , QStringLiteral("FJ")); + m_internal->m_cbSizeFD_APP6->addItem(tr("FD Army") , QStringLiteral("FK")); + m_internal->m_cbSizeFD_APP6->addItem(tr("FD Region") , QStringLiteral("FM")); + + + m_internal->m_cbMobility_APP6->addItem(tr("None") , QStringLiteral("--")); + m_internal->m_cbMobility_APP6->addItem(tr("Mobility Equipment") , QStringLiteral("M-")); + m_internal->m_cbMobility_APP6->addItem(tr("Mobility Fixed Wing") , QStringLiteral("MF")); + m_internal->m_cbMobility_APP6->addItem(tr("Mobility Helicopter") , QStringLiteral("MH")); + m_internal->m_cbMobility_APP6->addItem(tr("Mobility Missile") , QStringLiteral("MM")); + m_internal->m_cbMobility_APP6->addItem(tr("Mobility Wheeled / Limited Cross Country") , QStringLiteral("MO")); + m_internal->m_cbMobility_APP6->addItem(tr("Mobility Cross Country") , QStringLiteral("MP")); + m_internal->m_cbMobility_APP6->addItem(tr("Mobility Wheeled and Tracked Combination") , QStringLiteral("MR")); + m_internal->m_cbMobility_APP6->addItem(tr("Mobility Towed") , QStringLiteral("MS")); + m_internal->m_cbMobility_APP6->addItem(tr("Mobility Rail") , QStringLiteral("MT")); + m_internal->m_cbMobility_APP6->addItem(tr("Mobility Space") , QStringLiteral("S-")); + + layout->addRow(tr("APP6 Affiliation") , m_internal->m_cbIdentity_APP6); + layout->addRow(tr("APP6 Size") , m_internal->m_cbSize_APP6); + layout->addRow(tr("APP6 Headquarters (HQ)") , m_internal->m_cbSizeHQ_APP6); + layout->addRow(tr("APP6 Task Force (TF) HQ") , m_internal->m_cbSizeTF_APP6); + layout->addRow(tr("APP6 Feint Dummy (FD) HQ") , m_internal->m_cbSizeFDHQ_APP6); + layout->addRow(tr("APP6 Feint Dummy (FD/TF) HQ"), m_internal->m_cbSizeFDTF_APP6); + layout->addRow(tr("APP6 Feint Dummy (FD)") , m_internal->m_cbSizeFD_APP6); + layout->addRow(tr("Mobility") , m_internal->m_cbMobility_APP6); + layout->addRow(tr("APP6 Status") , m_internal->m_cbStatus_APP6); + layout->addRow(tr("APP6 Order Of Battle") , m_internal->m_cbOrderOfBattle_APP6); + + connect( m_internal->m_cbIdentity_APP6, static_cast(&QComboBox::activated), this, [this](int) + { + if (!m_internal->m_tabWidget->isEnabled()) return; + + m_internal->m_lastModifiedSidc = modifiedSidc(); + + QChar code = QLatin1Char('*'); + + switch (m_internal->m_cbIdentity_APP6->currentValue()) + { + case SidcDefinitions::Identity::Pending : code = QLatin1Char('P'); break; + case SidcDefinitions::Identity::Unknown : code = QLatin1Char('U'); break; + case SidcDefinitions::Identity::AssumedFriend : code = QLatin1Char('A'); break; + case SidcDefinitions::Identity::Friend : code = QLatin1Char('F'); break; + case SidcDefinitions::Identity::Neutral : code = QLatin1Char('N'); break; + case SidcDefinitions::Identity::SuspectJoker : code = QLatin1Char('S'); break; + case SidcDefinitions::Identity::HostileFaker : code = QLatin1Char('H'); break; + } + + QString rawSidc = SidcCodec::decodeRawSidc(modifiedSidc()); + rawSidc[1] = code; + m_internal->m_modifiedSidc = SidcCodec::encodeComplete(SymbologyType::APP6B, rawSidc); + + emitChanged(); + }); + + + connect(m_internal->m_cbSize_APP6 , static_cast(&QComboBox::activated), this, &SymbolModificationWidget::onChangeApp6SizeMobility); + connect(m_internal->m_cbSizeHQ_APP6 , static_cast(&QComboBox::activated), this, &SymbolModificationWidget::onChangeApp6SizeMobility); + connect(m_internal->m_cbSizeTF_APP6 , static_cast(&QComboBox::activated), this, &SymbolModificationWidget::onChangeApp6SizeMobility); + connect(m_internal->m_cbSizeFDHQ_APP6, static_cast(&QComboBox::activated), this, &SymbolModificationWidget::onChangeApp6SizeMobility); + connect(m_internal->m_cbSizeFDTF_APP6, static_cast(&QComboBox::activated), this, &SymbolModificationWidget::onChangeApp6SizeMobility); + connect(m_internal->m_cbSizeFD_APP6 , static_cast(&QComboBox::activated), this, &SymbolModificationWidget::onChangeApp6SizeMobility); + connect(m_internal->m_cbMobility_APP6, static_cast(&QComboBox::activated), this, &SymbolModificationWidget::onChangeApp6SizeMobility); + + + connect(m_internal->m_cbStatus_APP6, static_cast(&QComboBox::activated), this, [this]() + { + if (!m_internal->m_tabWidget->isEnabled()) return; + + m_internal->m_lastModifiedSidc = modifiedSidc(); + + QChar code = m_internal->m_cbStatus_APP6->currentData().toChar(); + + QString rawSidc = SidcCodec::decodeRawSidc(modifiedSidc()); + rawSidc[3] = code; + m_internal->m_modifiedSidc = SidcCodec::encodeComplete(SymbologyType::APP6B, rawSidc); + + emitChanged(); + }); + + connect(m_internal->m_cbOrderOfBattle_APP6, static_cast(&QComboBox::activated), this, [this]() + { + if (!m_internal->m_tabWidget->isEnabled()) return; + + m_internal->m_lastModifiedSidc = modifiedSidc(); + + QChar code = m_internal->m_cbOrderOfBattle_APP6->currentData().toChar(); + + QString rawSidc = SidcCodec::decodeRawSidc(modifiedSidc()); + rawSidc[14] = code; + m_internal->m_modifiedSidc = SidcCodec::encodeComplete(SymbologyType::APP6B, rawSidc); + + emitChanged(); + }); +} // void SymbolModificationWidget::buildSidcModifierTab_APP6(QWidget * widget) + + +void SymbolModificationWidget::buildFieldModifierTab(QWidget *widget) +{ + QVBoxLayout *layout = new QVBoxLayout(widget); + QHBoxLayout *layoutFieldEdit = new QHBoxLayout(); + + m_internal->m_cbFieldTitle = new MyComboBox(widget); + m_internal->m_leFieldValue = new QLineEdit(widget); + QPushButton *pbAdd = new QPushButton( QIcon(QStringLiteral("icon:add.svg")), QString(), widget ); + QPushButton *pbDel = new QPushButton( QIcon(QStringLiteral("icon:remove.svg")), QString(), widget ); + + layoutFieldEdit->addWidget(new QLabel(tr("Field"), widget)); + layoutFieldEdit->addWidget(m_internal->m_cbFieldTitle, 2); + layoutFieldEdit->addWidget(new QLabel(tr("Value"), widget)); + layoutFieldEdit->addWidget(m_internal->m_leFieldValue, 3); + layoutFieldEdit->addWidget(pbAdd); + layoutFieldEdit->addWidget(pbDel); + + m_internal->m_fieldTableModel = new FieldTableModel(widget); + + m_internal->m_fieldTable = new QTableView(widget); + m_internal->m_fieldTable->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows); + m_internal->m_fieldTable->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection); + m_internal->m_fieldTable->setModel(m_internal->m_fieldTableModel); + m_internal->m_fieldTable->setAlternatingRowColors(true); + m_internal->m_fieldTable->setShowGrid(false); + + QHeaderView *header = m_internal->m_fieldTable->horizontalHeader(); + + header->setSectionResizeMode(0, QHeaderView::ResizeMode::Stretch); + header->setSectionResizeMode(1, QHeaderView::ResizeMode::Stretch); + + layout->addLayout(layoutFieldEdit); + layout->addWidget(m_internal->m_fieldTable, 2); + + connect(pbAdd, &QPushButton::clicked, this, &SymbolModificationWidget::onFieldAttributeAdd); + connect(pbDel, &QPushButton::clicked, this, &SymbolModificationWidget::onFieldAttributeRemove); + + connect(m_internal->m_leFieldValue, &QLineEdit::returnPressed, this, [this]() { onFieldAttributeAdd(true); }); + connect(m_internal->m_fieldTable, &QTableView::clicked, this, &SymbolModificationWidget::onFieldItemActived); + connect(m_internal->m_fieldTable, &QTableView::activated, this, &SymbolModificationWidget::onFieldItemActived); + + connect(m_internal->m_cbFieldTitle, static_cast(&QComboBox::activated), this, &SymbolModificationWidget::onSetFieldTitle); + +} // void SymbolModificationWidget::buildFieldModifierTab( QWidget * widget ) + + +void SymbolModificationWidget::emitChanged() +{ + emit changed(); +} // void SymbolModificationWidget::emitChanged() + + +void SymbolModificationWidget::buildPreview(QWidget *widget) +{ + delete m_previewTab; + + m_previewTab = new PreviewTab(widget); + + m_previewTab->lbBaseSymbolName->setAlignment(Qt::AlignCenter); + + m_previewTab->lbSidcOutImg->setAlignment(Qt::AlignCenter); + m_previewTab->lbSidcInImg ->setAlignment(Qt::AlignCenter); + + m_previewTab->layout->addWidget(m_previewTab->lbBaseSymbolName); + m_previewTab->layout->addLayout(m_previewTab->layoutPreview, 2); + m_previewTab->layout->addLayout(m_previewTab->layoutPreview); + + m_previewTab->layoutPreview->setHorizontalSpacing(10); + m_previewTab->layoutPreview->setVerticalSpacing(10); + m_previewTab->layoutPreview->setLabelAlignment(Qt::AlignRight); + + m_previewTab->layoutPreview->addRow(tr("Base Symbol Code"), m_previewTab->lbBaseSymbolId); + + + QFrame *separator = new QFrame(widget); + separator->setFrameStyle(QFrame::HLine | QFrame::Plain); + m_previewTab->layoutPreview->addRow(separator); + m_previewTab->layoutPreview->addRow(tr("Modified SIDC"), m_previewTab->lbSidcOutTxt); + m_previewTab->layoutPreview->addWidget(m_previewTab->lbSidcOutImg); + + separator = new QFrame(widget); + separator->setFrameStyle(QFrame::HLine | QFrame::Plain); + m_previewTab->layoutPreview->addRow(separator); + m_previewTab->layoutPreview->addRow(tr("Original SIDC"), m_previewTab->lbSidcInTxt); + m_previewTab->layoutPreview->addWidget(m_previewTab->lbSidcInImg); + + connect(this, &SymbolModificationWidget::changed, + this, &SymbolModificationWidget::updatePreviewTab); + +} // void SymbolModificationWidget::buildPreview(QWidget * widget) + +void SymbolModificationWidget::updatePreviewTab() +{ + if (!m_previewTab) + return; + + const QString &originalSidc = this->originalSidc(); + const QString &modifiedSidc = this->modifiedSidc(); + + if (originalSidc.isEmpty()) + return; + + m_previewTab->lbBaseSymbolId ->setText(SidcCodec::decodeRawSidc(m_internal->m_baseSymbolCode)); + m_previewTab->lbBaseSymbolName->setText(this->symbolName()); + + m_previewTab->lbSidcInTxt ->setText(SidcCodec::decodeRawSidc(originalSidc)); + m_previewTab->lbSidcOutTxt->setText(SidcCodec::decodeRawSidc(modifiedSidc)); + + setSymbolPixmap(m_previewTab->lbSidcInImg, originalSidc); + if (originalSidc == modifiedSidc) + m_previewTab->lbSidcOutImg->setPixmap(QPixmap()); + else + setSymbolPixmap(m_previewTab->lbSidcOutImg, modifiedSidc); +} + +SymbolModificationWidget::~SymbolModificationWidget() +{ + delete m_internal; + m_internal = nullptr; +} + + +static void fillUpComboBox(const AdvancedSymbology::SymbolDictionary &dict, const QString &baseSymbolCode, unsigned int sector, QComboBox *cb) +{ + cb->clear(); + + cb->addItem(QIcon(), QStringLiteral("0 Not Applicable"), 0); + const QList &modifierList = dict.symbolModifierList(baseSymbolCode, sector); + for (const auto &modifier : modifierList) + { + const QString &modName = QString::number(modifier.m_modifierValue) + QLatin1Char(' ') + modifier.m_modifierName; + cb->addItem(QIcon(modifier.m_modifierIcon), modName, modifier.m_modifierValue); + } +} // static void fillUpComboBox(const QString & baseSymbolCode, unsigned int sector, QComboBox * cb) + + +SymbologyType SymbolModificationWidget::symbologyType() const +{ + return m_internal->m_symbologyType; +} // SymbologyType SymbolModificationWidget::symbologyType() const + + +bool SymbolModificationWidget::setSidc(const QString &sidc) +{ + if (sidc.isEmpty()) + return false; + + const AdvancedSymbology::SymbolDictionary &dict = m_internal->m_dict; + + if (!m_internal->m_sidcBuilder.setSidc(sidc)) + { + m_internal->m_tabWidget->setEnabled(false); + return false; + } + + m_internal->m_symbologyType = SidcCodec::decodeSymbologyType(sidc); + + switch (m_internal->m_symbologyType) + { + case SymbologyType::None : return false; + + case SymbologyType::APP6B : m_internal->m_tabSidcEdit->setCurrentIndex(static_cast(SymbologyTypeStackIndex::APP6)); break; + + case SymbologyType::APP6D : + case SymbologyType::MIL2525C : + case SymbologyType::MIL2525D : m_internal->m_tabSidcEdit->setCurrentIndex(static_cast(SymbologyTypeStackIndex::MIL2525D)); break; + } + + + m_internal->m_tabWidget->setEnabled(true); + + m_internal->m_originalSidc = m_internal->m_sidcBuilder.toSidc(); + m_internal->m_lastModifiedSidc = m_internal->m_sidcBuilder.toSidc(); + m_internal->m_modifiedSidc = m_internal->m_sidcBuilder.toSidc(); + + if (SidcCodec::isCivil(sidc)) + { + m_internal->m_tabSidcEdit ->setEnabled(false); + m_internal->m_tabFieldEdit->setEnabled(false); + m_internal->m_tabPreview ->setEnabled(false); + + m_internal->m_tabSidcEdit ->hide(); + m_internal->m_tabFieldEdit->hide(); + m_internal->m_tabPreview ->hide(); + + m_internal->m_tabWidget->removeTab(m_internal->m_tabWidget->indexOf(m_internal->m_tabSidcEdit)); + m_internal->m_tabWidget->removeTab(m_internal->m_tabWidget->indexOf(m_internal->m_tabFieldEdit)); + m_internal->m_tabWidget->removeTab(m_internal->m_tabWidget->indexOf(m_internal->m_tabPreview)); + + return false; // ToDo: implement buildParticipantTab and return true here + } + + m_internal->m_tabWidget->addTab(m_internal->m_tabSidcEdit, tr("Sector A and B Attributes")); + + m_internal->m_tabSidcEdit ->setEnabled(true); + m_internal->m_tabFieldEdit->setEnabled(true); + + if (m_internal->m_isParticipant) + { + m_internal->m_tabWidget->removeTab(m_internal->m_tabWidget->indexOf(m_internal->m_tabFieldEdit)); + + m_internal->m_tabFieldEdit->setEnabled(false); + m_internal->m_tabFieldEdit->hide(); + } + else + { + m_internal->m_tabWidget->addTab(m_internal->m_tabFieldEdit, tr("Field Modifier")); + m_internal->m_tabFieldEdit->setEnabled(true); + } + + #ifdef LTS_DEBUG + m_internal->m_tabWidget->addTab(m_internal->m_tabPreview, tr("Preview")); + m_internal->m_tabPreview->setEnabled(true); + #endif + + const QString &baseSymbolCode = AdvancedSymbology::SidcBuilderBase::concreteSidcToBaseSymbolCode(sidc); + const QString &baseSymbolKey = SidcCodec::decodeRawSidc(baseSymbolCode); + + + fillUpComboBox(dict, baseSymbolKey, 1, m_internal->m_cbSector1Modifier_MIL2525D); + fillUpComboBox(dict, baseSymbolKey, 2, m_internal->m_cbSector2Modifier_MIL2525D); + + m_internal->m_cbSector1Modifier_MIL2525D->setEnabled((m_internal->m_cbSector1Modifier_MIL2525D->count() > 0)); + m_internal->m_cbSector2Modifier_MIL2525D->setEnabled((m_internal->m_cbSector2Modifier_MIL2525D->count() > 0)); + + m_internal->m_baseSymbolCode = baseSymbolCode; + + if (!analyze(m_internal->m_symbologyType,sidc)) + return false; + + const AdvancedSymbology::SymbolProperties &symbolProps = dict.symbolProperties(baseSymbolCode); + + if (symbolProps.geometryType() != AdvancedSymbology::GeometryType::Point) + { + const QString &widgetName = m_internal->m_tabPreview->objectName(); + m_internal->m_tabPreview->setStyleSheet(QLatin1String("QWidget#") + widgetName + QLatin1String(" { background-color: lightgrey }")); + } + else + m_internal->m_tabPreview->setStyleSheet(QString()); + + QStringList fieldKeys = dict.symbologyAttributeKeyList(); + + fieldKeys.sort(); + + m_internal->m_cbFieldTitle->clear(); + for (const auto &fieldKey : std::as_const(fieldKeys)) + m_internal->m_cbFieldTitle->addItem(fieldKey); + + if (m_internal->m_tabWidget->isEnabled()) + emitChanged(); + + return true; +} // bool SymbolModificationWidget::setSidc( const QString & sidc ) + + +const QMap SymbolModificationWidget::fieldAttributes() +{ + return m_internal->m_fieldTableModel->fieldAttrMap(); +} // const QMap SymbolModificationWidget::fieldAttributes() + + +bool SymbolModificationWidget::setFieldAttributes(const QMap &fieldAttrMap) +{ + return m_internal->m_fieldTableModel->setFieldAttrMap(fieldAttrMap); +} // bool SymbolModificationWidget::setFieldAttributes(const QString & symbolFieldAttr) + + +bool SymbolModificationWidget::analyze(SymbologyType symbologyType, const QString &sidc) +{ + if (!m_internal->m_tabWidget->isEnabled()) return false; + + m_internal->m_lastModifiedSidc = modifiedSidc(); + + m_internal->m_sidcBuilder.setSidc(sidc); + + switch (symbologyType) + { + case SymbologyType::None : break; + case SymbologyType::APP6B : break; + case SymbologyType::MIL2525C: break; + + case SymbologyType::APP6D : + case SymbologyType::MIL2525D: + { + m_internal->m_cbContext_MIL2525D->setCurrent(m_internal->m_sidcBuilder.standardIdentity().context); + m_internal->m_cbIdentity_MIL2525D->setCurrent(m_internal->m_sidcBuilder.standardIdentity().identity); + + m_internal->m_cbStatus_MIL2525D->setCurrent(m_internal->m_sidcBuilder.status()); + + m_internal->m_cbHQTFDummy_MIL2525D->setCurrent(m_internal->m_sidcBuilder.hqTfFd()); + + m_internal->m_cbAmplifierGroup_MIL2525D->setCurrent(m_internal->m_sidcBuilder.amplifierGroup()); + + int idxAmpDescr = m_internal->m_cbAmplifierDescriptor_MIL2525D->findData(m_internal->m_sidcBuilder.amplifier2ndDigit()); + + m_internal->m_cbAmplifierDescriptor_MIL2525D->setCurrentIndex(idxAmpDescr); + + int idxMod1 = m_internal->m_cbSector1Modifier_MIL2525D->findData(m_internal->m_sidcBuilder.sectorModifier1Digit()); + int idxMod2 = m_internal->m_cbSector2Modifier_MIL2525D->findData(m_internal->m_sidcBuilder.sectorModifier2Digit()); + + m_internal->m_cbSector1Modifier_MIL2525D->setCurrentIndex(idxMod1); + m_internal->m_cbSector2Modifier_MIL2525D->setCurrentIndex(idxMod2); + } break; + } + + m_internal->m_modifiedSidc = m_internal->m_sidcBuilder.toSidc(); + + emitChanged(); // to update previewIcon (sidcNew) + + return true; // (m_internal->m_sidcDelta.toSidc() == sidc); +} // bool SymbolModificationWidget::setSidc( const QString & sidc ) + + +QString SymbolModificationWidget::originalSidc() const { return m_internal->m_originalSidc; } +QString SymbolModificationWidget::modifiedSidc() const { return m_internal->m_modifiedSidc; } +QString SymbolModificationWidget::lastModifiedSidc() const { return m_internal->m_lastModifiedSidc; } + + +QString SymbolModificationWidget::symbolName() const +{ + const QString &baseSymbolCode = AdvancedSymbology::SidcBuilderBase::concreteSidcToBaseSymbolCode(originalSidc()); + const AdvancedSymbology::SymbolDictionary &dict = m_internal->m_dict; + const AdvancedSymbology::SymbolProperties &symbolProps = dict.symbolProperties(baseSymbolCode); + return symbolProps.name(); +} // QString SymbolModificationWidget::symbolName() const + + +void SymbolModificationWidget::setSymbolPixmap(QLabel *label, const QString &sidc) +{ + label->setPixmap(QPixmap()); // first clear pixmap on label + + AdvancedSymbology::SymbolResultWatcher *symbolGuard = new AdvancedSymbology::SymbolResultWatcher(this); + + QObject::connect(symbolGuard, &AdvancedSymbology::SymbolResultWatcher::resultReady, this, [label, symbolGuard]() + { + QSharedPointer symbolResult = symbolGuard->symbolResult(); + if (symbolResult) + { + QPixmap symbolPixmap = QPixmap::fromImage(symbolResult->symbolImage().image()); + + if (symbolPixmap.width() > 200) symbolPixmap = symbolPixmap.scaledToWidth(200, Qt::TransformationMode::SmoothTransformation); + if (symbolPixmap.height() > 100) symbolPixmap = symbolPixmap.scaledToHeight(100, Qt::TransformationMode::SmoothTransformation); + + label->setPixmap(symbolPixmap); + } + }); + + const AdvancedSymbology::SymbolDataToRender symDataToRender(sidc, 200); + + symbolGuard->setSymbolResult(m_internal->m_dict.findImage(symDataToRender)); + +} // void SymbolModificationWidget::setSymbolPixmap( QLabel * label, const QString & sidc ) + + +void SymbolModificationWidget::onRevert() +{ + const QString &restoreSidc = originalSidc(); + analyze(SidcCodec::decodeSymbologyType(restoreSidc),restoreSidc); +} // void SymbolModificationWidget::onRevert() + + +void SymbolModificationWidget::onContextChanged() +{ + if (!m_internal->m_tabWidget->isEnabled()) return; + + m_internal->m_lastModifiedSidc = modifiedSidc(); + + AdvancedSymbology::SidcDefinitions::StandardIdentity si = m_internal->m_sidcBuilder.standardIdentity(); + si.context = m_internal->m_cbContext_MIL2525D->currentValue(); + m_internal->m_sidcBuilder.setStandardIdentity(si); + + m_internal->m_modifiedSidc = m_internal->m_sidcBuilder.toSidc(); + + emitChanged(); +} // void SymbolModificationWidget::onContextChanged() + + +void SymbolModificationWidget::onIdentityChanged() +{ + if (!m_internal->m_tabWidget->isEnabled()) return; + + m_internal->m_lastModifiedSidc = modifiedSidc(); + + AdvancedSymbology::SidcDefinitions::StandardIdentity si = m_internal->m_sidcBuilder.standardIdentity(); + si.identity = m_internal->m_cbIdentity_MIL2525D->currentValue(); + m_internal->m_sidcBuilder.setStandardIdentity(si); + + m_internal->m_modifiedSidc = m_internal->m_sidcBuilder.toSidc(); + + emitChanged(); +} // void SymbolModificationWidget::onIdentityChanged() + + +void SymbolModificationWidget::onSymbolSetChanged() +{ + if (!m_internal->m_tabWidget->isEnabled()) return; + + m_internal->m_lastModifiedSidc = modifiedSidc(); + + m_internal->m_sidcBuilder.setSymbolSet(m_internal->m_cbSymbolSet_MIL2525D->currentValue()); + m_internal->m_modifiedSidc = m_internal->m_sidcBuilder.toSidc(); + emitChanged(); +} // void SymbolModificationWidget::onSymbolSetChanged() + + +void SymbolModificationWidget::onStatusChanged() +{ + if (!m_internal->m_tabWidget->isEnabled()) return; + + m_internal->m_lastModifiedSidc = modifiedSidc(); + + m_internal->m_sidcBuilder.setStatus(m_internal->m_cbStatus_MIL2525D->currentValue()); + m_internal->m_modifiedSidc = m_internal->m_sidcBuilder.toSidc(); + emitChanged(); +} // void SymbolModificationWidget::onStatusChanged() + + +void SymbolModificationWidget::onHQTFDummyChanged() +{ + if (!m_internal->m_tabWidget->isEnabled()) return; + + m_internal->m_lastModifiedSidc = modifiedSidc(); + + m_internal->m_sidcBuilder.setHqTfFd(m_internal->m_cbHQTFDummy_MIL2525D->currentValue()); + m_internal->m_modifiedSidc = m_internal->m_sidcBuilder.toSidc(); + emitChanged(); +} // void SymbolModificationWidget::onHQTFDummyChanged() + + +void SymbolModificationWidget::onAmplifierGroupChanged() +{ + if (!m_internal->m_tabWidget->isEnabled()) return; + + m_internal->m_lastModifiedSidc = modifiedSidc(); + + const AdvancedSymbology::SidcDefinitions::AmplifierGroup &lifierGroup = m_internal->m_cbAmplifierGroup_MIL2525D->currentValue(); + + // First, renew the secondary comboBox!!! + // Remember, this is of type QComboBox + m_internal->m_cbAmplifierDescriptor_MIL2525D->clear(); + + + m_internal->m_cbAmplifierDescriptor_MIL2525D->blockSignals(true); + { + int idx = 0; + + switch (amplifierGroup) + { + case AdvancedSymbology::SidcDefinitions::AmplifierGroup::Unknown: break; + case AdvancedSymbology::SidcDefinitions::AmplifierGroup::Echelon_BrigadeAndBelow: + { + const QVector &items = AdvancedSymbology::SidcDefinitions::echelonBrigadeAndBelows(); + for (const auto &item : items) + m_internal->m_cbAmplifierDescriptor_MIL2525D->insertNumberedItem(idx++, QString(AdvancedSymbology::SidcDefinitions::toString(item)), static_cast(item)); + } + break; + + case AdvancedSymbology::SidcDefinitions::AmplifierGroup::Echelon_DivisionAndAbove: + { + const QVector &items = AdvancedSymbology::SidcDefinitions::echelonDivisionAndAboves(); + for (const auto &item : items) + m_internal->m_cbAmplifierDescriptor_MIL2525D->insertNumberedItem(idx++, AdvancedSymbology::SidcDefinitions::toString(item), static_cast(item)); + } + break; + + case AdvancedSymbology::SidcDefinitions::AmplifierGroup::Mobility_OnLand: + { + const QVector &items = AdvancedSymbology::SidcDefinitions::mobilityOnLands(); + for (const auto &item : items) + m_internal->m_cbAmplifierDescriptor_MIL2525D->insertNumberedItem(idx++, AdvancedSymbology::SidcDefinitions::toString(item), static_cast(item)); + } + break; + + case AdvancedSymbology::SidcDefinitions::AmplifierGroup::Mobility_OnWater: + { + const QVector &items = AdvancedSymbology::SidcDefinitions::mobilityOnWaters(); + for (const auto &item : items) + m_internal->m_cbAmplifierDescriptor_MIL2525D->insertNumberedItem(idx++, AdvancedSymbology::SidcDefinitions::toString(item), static_cast(item)); + } + break; + + case AdvancedSymbology::SidcDefinitions::AmplifierGroup::Mobility_OnSnow: + { + const QVector &items = AdvancedSymbology::SidcDefinitions::mobilityOnSnows(); + for (const auto &item : items) + m_internal->m_cbAmplifierDescriptor_MIL2525D->insertNumberedItem(idx++, AdvancedSymbology::SidcDefinitions::toString(item), static_cast(item)); + } + break; + + case AdvancedSymbology::SidcDefinitions::AmplifierGroup::Naval_TowedArry: + { + const QVector &items = AdvancedSymbology::SidcDefinitions::navalTowedArrays(); + for (const auto &item : items) + m_internal->m_cbAmplifierDescriptor_MIL2525D->insertNumberedItem(idx++, AdvancedSymbology::SidcDefinitions::toString(item), static_cast(item)); + } + break; + + } // switch + } + m_internal->m_cbAmplifierDescriptor_MIL2525D->blockSignals(false); + + + m_internal->m_sidcBuilder.setAmplifierGroup(amplifierGroup); + + if (m_internal->m_cbAmplifierDescriptor_MIL2525D->count() > 0) + { + m_internal->m_cbAmplifierDescriptor_MIL2525D->setEnabled(true); + + unsigned int ampDescr = m_internal->m_sidcBuilder.amplifier2ndDigit(); + int index = m_internal->m_cbAmplifierDescriptor_MIL2525D->findData(ampDescr); + + m_internal->m_cbAmplifierDescriptor_MIL2525D->setCurrentIndex(index); + } + else m_internal->m_cbAmplifierDescriptor_MIL2525D->setEnabled(false); + + m_internal->m_modifiedSidc = m_internal->m_sidcBuilder.toSidc(); + + emitChanged(); + +} // void SymbolModificationWidget::onAmplifierGroupChanged() + + +void SymbolModificationWidget::onAmplifierDesriptorChanged(int index) +{ + if (!m_internal->m_tabWidget->isEnabled()) return; + + unsigned int ampDescr = m_internal->m_cbAmplifierDescriptor_MIL2525D->itemData(index).toUInt(); + m_internal->m_sidcBuilder.setAmplifier2ndDigit(ampDescr); + m_internal->m_modifiedSidc = m_internal->m_sidcBuilder.toSidc(); + emitChanged(); +} // void SymbolModificationWidget::onAmplifierDesriptorChanged( int index ) + + +void SymbolModificationWidget::onSector1ModifierChanged(int index) +{ + if (!m_internal->m_tabWidget->isEnabled()) return; + + unsigned int sectorModifier = m_internal->m_cbSector1Modifier_MIL2525D->itemData(index).toUInt(); + m_internal->m_sidcBuilder.setSectorModifier1Digit(sectorModifier); + m_internal->m_modifiedSidc = m_internal->m_sidcBuilder.toSidc(); + emitChanged(); +} // void SymbolModificationWidget::onSector1ModifierChanged( int ) + + +void SymbolModificationWidget::onSector2ModifierChanged(int index) +{ + if (!m_internal->m_tabWidget->isEnabled()) return; + + unsigned int sectorModifier = m_internal->m_cbSector2Modifier_MIL2525D->itemData(index).toUInt(); + m_internal->m_sidcBuilder.setSectorModifier2Digit(sectorModifier); + m_internal->m_modifiedSidc = m_internal->m_sidcBuilder.toSidc(); + emitChanged(); +} // void SymbolModificationWidget::onSector2ModifierChanged( int ) + + +void SymbolModificationWidget::onFieldAttributeAdd(bool) +{ + if (!m_internal->m_tabWidget->isEnabled()) return; + + const QString &value = m_internal->m_leFieldValue->text(); + + if (!value.isEmpty()) + { + const QString &key = m_internal->m_cbFieldTitle->currentText(); + m_internal->m_fieldTableModel->setFieldAttribute(key, value); + emitChanged(); + } + else onFieldAttributeRemove(true); +} // void SymbolModificationWidget::onFieldAttributeAdd() + + +void SymbolModificationWidget::onFieldAttributeRemove(bool) +{ + if (!m_internal->m_tabWidget->isEnabled()) return; + + const AdvancedSymbology::SymbolDictionary &dict = m_internal->m_dict; + const QString &key = m_internal->m_cbFieldTitle->currentText(); + + if (dict.symbologyAttributeKeyList().contains(key)) + { + m_internal->m_fieldTableModel->removeFieldAttrMap(key); + emitChanged(); + } +} // void PlanEditor::SymbolModificationWidget::onFieldAttributeRemove() + + +void SymbolModificationWidget::onSetFieldTitle(const QString &fieldTitel) +{ + const AdvancedSymbology::SymbolDictionary &dict = m_internal->m_dict; + + if (dict.symbologyAttributeKeyList().contains(fieldTitel)) + m_internal->m_leFieldValue->setText(m_internal->m_fieldTableModel->fieldAttribute(fieldTitel)); +}// void SymbolModificationWidget::onSetFieldTitle(const QString &) + + +void SymbolModificationWidget::onFieldItemActived(const QModelIndex &index) +{ + const AdvancedSymbology::SymbolDictionary &dict = m_internal->m_dict; + const QString key = m_internal->m_fieldTableModel->data(m_internal->m_fieldTableModel->index(index.row(), 0)).toString(); + const QString val = m_internal->m_fieldTableModel->data(m_internal->m_fieldTableModel->index(index.row(), 1)).toString(); + + if (dict.symbologyAttributeKeyList().contains(key)) + { + m_internal->m_leFieldValue->setText(val); + m_internal->m_cbFieldTitle->setCurrentText(key); + } +} // void SymbolModificationWidget::onFieldItemActived( const QModelIndex & index ) + + +void SymbolModificationWidget::onChangeApp6SizeMobility() +{ + if (!m_internal->m_tabWidget->isEnabled()) return; + + m_internal->m_lastModifiedSidc = modifiedSidc(); + + MyComboBox * cb = qobject_cast(sender()); + if (cb) + { + QString rawSidc = SidcCodec::decodeRawSidc(modifiedSidc()); + QString code = cb->currentData().toString(); + + rawSidc[10] = code[0]; + rawSidc[11] = code[1]; + + m_internal->m_modifiedSidc = SidcCodec::encodeComplete(SymbologyType::APP6B, rawSidc); + + if (cb != m_internal->m_cbSize_APP6) m_internal->m_cbSize_APP6->setCurrentIndex(0); + if (cb != m_internal->m_cbSizeHQ_APP6) m_internal->m_cbSizeHQ_APP6->setCurrentIndex(0); + if (cb != m_internal->m_cbSizeTF_APP6) m_internal->m_cbSizeTF_APP6->setCurrentIndex(0); + if (cb != m_internal->m_cbSizeFDHQ_APP6) m_internal->m_cbSizeFDHQ_APP6->setCurrentIndex(0); + if (cb != m_internal->m_cbSizeFDTF_APP6) m_internal->m_cbSizeFDTF_APP6->setCurrentIndex(0); + if (cb != m_internal->m_cbSizeFD_APP6) m_internal->m_cbSizeFD_APP6->setCurrentIndex(0); + if (cb != m_internal->m_cbMobility_APP6) m_internal->m_cbMobility_APP6->setCurrentIndex(0); + + emitChanged(); + } +} // void SymbolModificationWidget::onChangeApp6SizeMobility(int) + + +} // namespace Gui +} // namespace AdvancedSymbology diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/symbol_modification_widget.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/symbol_modification_widget.h new file mode 100755 index 000000000..9d3f86f00 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/SymbologyGui/symbol_modification_widget.h @@ -0,0 +1,135 @@ +/** +* @RDE_HEADER@ +* symbol_modification_wdiget for sidc configuration +* $Id$ +* $HeadURL$ +* $Author$ +* Created by Karwat, SLSE, 2017-09-07 +*/ + +#pragma once + +#include + + +#include +#include +#include +#include + +#include +#include +#include +#include + +class QLabel; +class QBoxLayout; + +namespace AdvancedSymbology +{ + +namespace Gui +{ + +class ADVANCEDSYMBOLOGYGUI_EXPORT SymbolModificationWidget : public QWidget +{ + Q_OBJECT + +public: + + explicit SymbolModificationWidget(SituationMap::Map *map, bool isParticipant, SymbolDictionary &symbolDictionary, QWidget *parent = nullptr); + ~SymbolModificationWidget(); + + bool setSidc(const QString &sidc); + bool setFieldAttributes(const QMap &fieldAttrMap); + + const QMap fieldAttributes(); + bool analyze(SymbologyType symbologyType, const QString &sidc); + QString originalSidc() const; + QString modifiedSidc() const; + QString lastModifiedSidc() const; + QString symbolName() const; + + SymbologyType symbologyType() const; + +protected: + + void retranslate(); + + void setSymbolPixmap(QLabel *, const QString &sidc); + + void buildSidcModifierTab_MIL2525D(QWidget *widget); + void buildSidcModifierTab_APP6(QWidget *widget); + + void buildFieldModifierTab(QWidget *widget); + void buildParticipantTab(QWidget *widget); + void buildPreview(QWidget *widget); + + void emitChanged(); + + class Internal; + + Internal *m_internal; + + struct PreviewTab + { + explicit PreviewTab(QWidget *widget) + : layout(new QVBoxLayout(widget)) + , layoutPreview(new QFormLayout()) + , lbBaseSymbolId(new QLabel(widget)) + , lbBaseSymbolName(new QLabel(widget)) + , lbSidcOutTxt(new QLabel(widget)) + , lbSidcInTxt(new QLabel(widget)) + , lbSidcOutImg(new QLabel(widget)) + , lbSidcInImg(new QLabel(widget)) + {} + QVBoxLayout *layout; + QFormLayout *layoutPreview; + + QLabel *lbBaseSymbolId; + QLabel *lbBaseSymbolName; + QLabel *lbSidcOutTxt; + QLabel *lbSidcInTxt; + QLabel *lbSidcOutImg; + QLabel *lbSidcInImg; + }; + PreviewTab *m_previewTab = nullptr; + + +signals: + + void changed(); + + +public Q_SLOTS: + + void updatePreviewTab(); + void onRevert(); + + +private Q_SLOTS: + + void onContextChanged(); + void onIdentityChanged(); + void onSymbolSetChanged(); + void onStatusChanged(); + + void onHQTFDummyChanged(); + + void onAmplifierGroupChanged(); + void onAmplifierDesriptorChanged(int); + + void onSector1ModifierChanged(int); + void onSector2ModifierChanged(int); + + void onFieldAttributeAdd(bool); + void onFieldAttributeRemove(bool); + + void onSetFieldTitle(const QString &); + void onFieldItemActived(const QModelIndex &); + + void onChangeApp6SizeMobility(); +}; +} //namespace Gui + +} //namespace AdvancedSymbology diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/CMakeLists.txt new file mode 100755 index 000000000..f60146849 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/CMakeLists.txt @@ -0,0 +1,14 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +ADD_SUBDIRECTORY(SituationMap) + +IF(BUILD_SITMAP_UNIT_TESTS) +# ADD_SUBDIRECTORY(SitMapTestApp) +ADD_SUBDIRECTORY(exampleSvgRenderer) +ADD_SUBDIRECTORY(LineOfSightEvaluation) +ENDIF() + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/CrashDictSymbol/CrashDictSymbol.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/CrashDictSymbol/CrashDictSymbol.cpp new file mode 100755 index 000000000..a45a5f61b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/CrashDictSymbol/CrashDictSymbol.cpp @@ -0,0 +1,391 @@ +/* +** +** @RDE_HEADER@ +** Shows a crash when using QGraphicsItems and ESRI-Graphics in same scene +** $Id$ +** $HeadURL$ +** $Author$ +** +** ----------------------------------------------------------------------------------------------- +** +** Hardware-Environment: Intel-PC +** Software-Environment: Windows 7, Linux, Qt 5.7. ArcGISRuntimeQt 100 +** +** Language ........: C++ 11 +** +** ----------------------------------------------------------------------------------------------- +** +** Author ...........: Jörg Turla, SPD G, Tel. 3132 +** +** ----------------------------------------------------------------------------------------------- +** +** Notes .........: +** +** 16.01.2017, JT : Initial +** +** ----------------------------------------------------------------------------------------------- +*/ +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * The class MapWindow creates a tiled base map from an UTM based TPK from Bremen. Loading the map will be done + * by clicking the "Load Map" button. When the map is loaded, you can click button "Symbol Failure" to create a + * MIL2525D symbol on map. With the combobox left of this button you can setup the geometry that will be use to + * build the symbol. The symbol with sidc="10012500002713000000" will cause always a crash. Other symbols can + * cause a crash some times if not choosing the fitting geometry. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +class MapWindow : public QWidget +{ + Esri::ArcGISRuntime::ArcGISTiledLayer * m_tiledLayer; + Esri::ArcGISRuntime::Basemap * m_basemap; + Esri::ArcGISRuntime::Map * m_map; + Esri::ArcGISRuntime::MapGraphicsView * m_mapView; + + QComboBox * m_cbGeometry; + QPushButton * m_pbSymbolFailure; + + Esri::ArcGISRuntime::DictionarySymbolStyle * m_dictionarySymbolStyle; + + public: + + MapWindow() : QWidget(), m_tiledLayer(nullptr), m_dictionarySymbolStyle(nullptr) + { + m_basemap = new Esri::ArcGISRuntime::Basemap(this); + m_map = new Esri::ArcGISRuntime::Map(this); + + m_map->setAutoFetchLegendInfos(true); + + m_mapView = new Esri::ArcGISRuntime::MapGraphicsView(this); + + m_mapView->setObjectName(QLatin1String("EsriMapView")); + m_mapView->setFrameShape(QFrame::NoFrame); + m_mapView->setWrapAroundMode(Esri::ArcGISRuntime::WrapAroundMode::Disabled); + m_mapView->setFixedSize(900,900); + + m_cbGeometry = new QComboBox( this ); + m_cbGeometry->addItem( "Point" ); + m_cbGeometry->addItem( "Linear" ); + m_cbGeometry->addItem( "Areal" ); + m_cbGeometry->setCurrentIndex(0); + m_cbGeometry->setEditable(false); + + QPushButton * pbLoadMap = new QPushButton(QStringLiteral("Load Map"), this); + + m_pbSymbolFailure = new QPushButton(QStringLiteral("Symbol Failure"), this); + m_pbSymbolFailure -> setEnabled(false); + + connect( pbLoadMap , &QPushButton::clicked, this, [this](bool) { initMap(); }); + connect( m_pbSymbolFailure, &QPushButton::clicked, this, [this](bool) { buildDictSymbol(); }); + + QVBoxLayout * layout = new QVBoxLayout(this); + QHBoxLayout * hLayout = new QHBoxLayout(); + + hLayout->addStretch(2); + hLayout->addWidget(m_cbGeometry); + hLayout->addSpacing(10); + hLayout->addWidget(m_pbSymbolFailure); + hLayout->addStretch(2); + + layout->addWidget(pbLoadMap); + layout->addLayout(hLayout); + layout->addWidget(m_mapView,2); + + + connect(m_map, &Esri::ArcGISRuntime::Map::doneLoading, this, [this](Esri::ArcGISRuntime::Error loadError) + { + if (!loadError.isEmpty()) + { + qCritical() << loadError.message(); + qCritical() << loadError.additionalMessage(); + + Esri::ArcGISRuntime::ErrorDomainType errorDomain = loadError.domain(); + Esri::ArcGISRuntime::ExtendedErrorType extendedErr = loadError.extendedErrorType(); + + Q_UNUSED(errorDomain); + Q_UNUSED(extendedErr); + } + if (Esri::ArcGISRuntime::LoadStatus::Loaded == m_map->loadStatus()) + { + if (m_dictionarySymbolStyle && (Esri::ArcGISRuntime::LoadStatus::Loaded == m_dictionarySymbolStyle->loadStatus())) + { + m_pbSymbolFailure->setEnabled(true); + } + } + }); + + } // MapWindow() + + + void initMap() + { + #define BremenMap + #ifdef BremenMap + // m_tiledLayer = new Esri::ArcGISRuntime::ArcGISTiledLayer(QUrl("http://192.168.133.90:6080/arcgis/rest/services/BremenGeoTiffUTM/MapServer")); + Esri::ArcGISRuntime::TileCache * tpkCache = new Esri::ArcGISRuntime::TileCache("Topographic.tpk", this); + m_tiledLayer = new Esri::ArcGISRuntime::ArcGISTiledLayer(tpkCache); + #else + Esri::ArcGISRuntime::TileCache * tpkCache = new Esri::ArcGISRuntime::TileCache("/usr/share/arcgis/resources/Topographic.tpk", this); + m_tiledLayer = new Esri::ArcGISRuntime::ArcGISTiledLayer(tpkCache); + #endif + + m_basemap->baseLayers()->append(m_tiledLayer); + + m_map->setBasemap(m_basemap); + + Esri::ArcGISRuntime::GraphicsOverlayListModel * graphicsOverlayListModel = m_mapView->graphicsOverlays(); + Esri::ArcGISRuntime::GraphicsOverlay * graphicalOverlay = new Esri::ArcGISRuntime::GraphicsOverlay(); + + graphicalOverlay->setSelectionColor(Qt::magenta); + graphicsOverlayListModel->append(graphicalOverlay); + + m_mapView->setMap(m_map); + + loadDictionary(); + + } // void initMap() + + + void loadDictionary() + { + + #ifdef BremenMap + QString stylxPath = "C:/Program Files (x86)/ArcGIS SDKs/Qt100.0/resources/symbols/mil2525d/mil2525d.stylx"; // warning must be stylx file path + #else // if path is wrong --> crash (only Windows) + QString stylxPath = "/usr/share/arcgis/resources/symbols/mil2525d/mil2525d.stylx"; + #endif + + m_dictionarySymbolStyle = new Esri::ArcGISRuntime::DictionarySymbolStyle("mil2525d", stylxPath, this); + + connect(m_dictionarySymbolStyle, &Esri::ArcGISRuntime::DictionarySymbolStyle::loadStatusChanged, this, [this]( Esri::ArcGISRuntime::LoadStatus loadStatus ) + { + if ((Esri::ArcGISRuntime::LoadStatus::Loaded == loadStatus) && (Esri::ArcGISRuntime::LoadStatus::Loaded == m_map->loadStatus())) + { + m_pbSymbolFailure->setEnabled(true); + } + } ); + + m_dictionarySymbolStyle->load(); + + } // void loadDictionary() + + + ~MapWindow() = default; + + + void buildDictSymbol() + { + Esri::ArcGISRuntime::GraphicsOverlay * layer = new Esri::ArcGISRuntime::GraphicsOverlay(this); + Esri::ArcGISRuntime::DictionaryRenderer * dictRenderer = new Esri::ArcGISRuntime::DictionaryRenderer( m_dictionarySymbolStyle, this); + + connect( dictRenderer, &Esri::ArcGISRuntime::DictionaryRenderer::errorOccurred, this, [this]( const Esri::ArcGISRuntime::Error & error ) + { + qCritical() << "Error loading dictionary: " << error.message() << endl; + } ); + + //layer->setRenderingMode(Esri::ArcGISRuntime::GraphicsRenderingMode::Static); + layer->setRenderingMode(Esri::ArcGISRuntime::GraphicsRenderingMode::Dynamic); + layer->setRenderer( dictRenderer ); + + + QVariantMap rawAttributes; + + rawAttributes["sidc"] = "10012500002713000000"; // --> always crash + + // rawAttributes["sidc"] = "10012500001521000000"; // as polygon geometry --> failed to generate sequences for graphics + // or crash if swap coordinate 3 and 4 (see #define CRASH_25152100) + // as multipoint --> ok, with strange behaviour under zoom + + // rawAttributes["sidc"] = "10012500003419000000"; // --> failed to generate sequences for graphics + + setWindowTitle( "Crash Dictionary Symbol - Symbol Code " + rawAttributes["sidc"].toString() ); + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * see mil2525d.pdf, page 600 (25 271300) + * , page 459 (25 152100) + * coordinate sequenze as follows: + * + * (3) ------------------------------------------->(4) + * green orange + * + * + * + * (1) ------------------------------------------->(2) + * red blue + * + * or page 649 (25 341900): + * + * (4)<------------------------------------------- (3) + * orange green + * ^ + * | + * | + * (1) ------------------------------------------->(2) + * red blue + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + /* for Bremen map: + QPointF p1 = QPointF( 493200,5878900 ); // UTM Bremen 493200,5878900 + QPointF p2 = QPointF( 495200,5878900 ); // UTM Bremen 495200,5878900 + QPointF p3 = QPointF( 493200,5880900 ); // UTM Bremen 493200,5880900 + QPointF p4 = QPointF( 495200,5880900 ); // UTM Bremen 495200,5880900 + */ + + QPointF p1 = QPointF( 0, 0 ); + QPointF p2 = QPointF( 3000000, 0 ); + QPointF p3 = QPointF( 0, 5890000 ); + QPointF p4 = QPointF( 3000000, 5800000 ); + + // first show coordinate in map + auto drawPictureMarker = [this](const QPointF & p, const QColor & c) -> Esri::ArcGISRuntime::Graphic * + { + Esri::ArcGISRuntime::Graphic * graphic = new Esri::ArcGISRuntime::Graphic(); + Esri::ArcGISRuntime::SimpleMarkerSymbol * marker = new Esri::ArcGISRuntime::SimpleMarkerSymbol(); + + marker->setSize(8); + marker->setStyle( Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle ); + marker->setColor(c); + + graphic->setGeometry( Esri::ArcGISRuntime::Point( p.x(), p.y(), m_map->spatialReference() ) ); + graphic->setSymbol( marker ); + + return graphic; + }; // drawPictureMarker + + if (rawAttributes["sidc"] == "10012500003419000000") + { + // swap coordinate 3 and 4 in case of Relief in Place symbol (25 341900) stay compatible with MIL2525D + QPointF swap = p3; p3 = p4; p4 = swap; + } + + #define CRASH_25152100 + #ifdef CRASH_25152100 + if ((m_cbGeometry->currentIndex() == 2) && (rawAttributes["sidc"] == "10012500001521000000")) + { + // swap coordinate 3 and 4 in case of Suppoprt by Fire Position symbol show crash + QPointF swap = p3; p3 = p4; p4 = swap; + } + #endif + + layer->graphics()->append( drawPictureMarker(p1,QColor("red" )) ); + layer->graphics()->append( drawPictureMarker(p2,QColor("blue" )) ); + layer->graphics()->append( drawPictureMarker(p3,QColor("green" )) ); + layer->graphics()->append( drawPictureMarker(p4,QColor("orange")) ); + + Esri::ArcGISRuntime::Graphic * graphic = new Esri::ArcGISRuntime::Graphic(); + + switch(m_cbGeometry->currentIndex()) + { + case 0: + case 1: + { + qWarning() << " use 4 coordinates - MultipointBuilder"; + Esri::ArcGISRuntime::MultipointBuilder pointsBuilder( m_map->spatialReference() ); + Esri::ArcGISRuntime::PointCollection points( m_map->spatialReference() ); + + points.addPoint( p1.x(),p1.y() ); // 1 + points.addPoint( p2.x(),p2.y() ); // 2 + points.addPoint( p3.x(),p3.y() ); // 3 + points.addPoint( p4.x(),p4.y() ); // 4 + + pointsBuilder.setPoints( &points ); + + graphic->setGeometry( pointsBuilder.toGeometry() ); + } break; + + case 2: + { + qWarning() << " use 4 coordinates - PolygonBuilder"; + Esri::ArcGISRuntime::PolygonBuilder polygonBuilder( m_map->spatialReference() ); + + polygonBuilder.addPoint( Esri::ArcGISRuntime::Point(p1.x(),p1.y(),m_map->spatialReference()) ); // 1 + polygonBuilder.addPoint( Esri::ArcGISRuntime::Point(p2.x(),p2.y(),m_map->spatialReference()) ); // 2 + polygonBuilder.addPoint( Esri::ArcGISRuntime::Point(p3.x(),p3.y(),m_map->spatialReference()) ); // 3 + polygonBuilder.addPoint( Esri::ArcGISRuntime::Point(p4.x(),p4.y(),m_map->spatialReference()) ); // 4 + + graphic->setGeometry( polygonBuilder.toPolygon() ); + } + } + + qWarning() << " rawAttributes[sidc] = '" << rawAttributes[QLatin1String("sidc")] << "'"; + + graphic->attributes()->setAttributesMap(rawAttributes); + + //add the graphic to the graphics overlay + layer->graphics()->append(graphic); + + m_mapView->graphicsOverlays()->append(layer); + + qWarning() << " END of buildDictSymbol"; + } // void buildDictSymbol() + + +}; // MapWindow + + +int main(int argc, char* argv[]) +{ + QApplication app(argc,argv); + + // ESRI : +#ifdef Q_OS_WIN + // Force usage of OpenGL ES through ANGLE on Windows + QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); +#endif + // : ESRI + + MapWindow * mapWin = new MapWindow(); + + int width = mapWin->frameGeometry().width(); + int height = mapWin->frameGeometry().height(); + + QScreen *screen = QGuiApplication::primaryScreen(); + int screenWidth = screen ? screen->size().width() : 1920; + int screenHeight = screen ? screen->size().height() : 1080; + + mapWin->setGeometry((screenWidth / 2) - (width / 2), (screenHeight / 2) - (height / 2), width, height); + + mapWin->setWindowTitle( "Crash Dictionary Symbol" ); + mapWin->show(); + + app.setQuitOnLastWindowClosed(true); + + int retval = app.exec(); + + delete mapWin; + + return retval; + +} // main diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/CrashDictSymbol/CrashDictSymbol.pro b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/CrashDictSymbol/CrashDictSymbol.pro new file mode 100755 index 000000000..b38064fea --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/CrashDictSymbol/CrashDictSymbol.pro @@ -0,0 +1,47 @@ +#------------------------------------------------- +# Copyright 2015 Esri. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------- + +mac { + cache() +} + + +CONFIG += c++11 esri_runtime_qt100_0_0 + +QT += core gui opengl xml network positioning sensors + +QMAKE_CXXFLAGS += -std=c++0x + + +# $$(ENVVAR) +# $(ENVVAR) +INCLUDEPATH += /XXX +LIBPATH += /XXX +LIBS += -lEsriCommonQt +LIBS += -lEsriRuntimeQt +LIBS += -lc++ + + + +win32:CONFIG += \ + embed_manifest_exe + +TARGET = CrashDictSymbol +TEMPLATE = app + +SOURCES += \ + CrashDictSymbol.cpp + +HEADERS += diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/CMakeLists.txt new file mode 100755 index 000000000..fa0c99714 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/CMakeLists.txt @@ -0,0 +1,23 @@ + +cmake_minimum_required(VERSION 3.16) + +SET(QT_MIN_VERSION 6.0.0) +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +FILE ( GLOB sourceFiles *.h *.cpp ) + +FIND_PACKAGE(OpenGL) +FIND_PACKAGE(Qt6 COMPONENTS Core Gui Location OpenGL Positioning Sensors Widgets REQUIRED) + +INCLUDE_DIRECTORIES ( ${Qt6Widgets_INCLUDE_DIRS}, "C:/Program Files (x86)/ArcGIS SDKs/Qt10.2.6/sdk/windows/include" ) + +add_definitions(${Qt6Widgets_DEFINITIONS}) + +SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) + +ADD_EXECUTABLE( EsriMapTouchTest WIN32 ${sourceFiles} ) + +TARGET_LINK_LIBRARIES( EsriMapTouchTest Qt6::Core Qt6::Gui Qt6::Location Qt6::OpenGL Qt6::Sensors Qt6::Widgets ) +TARGET_LINK_LIBRARIES( EsriMapTouchTest debug "C:/Program Files (x86)/ArcGIS SDKs/Qt10.2.6/sdk/windows/x86/lib/EsriRuntimeQtd.lib" ) +TARGET_LINK_LIBRARIES( EsriMapTouchTest optimized "C:/Program Files (x86)/ArcGIS SDKs/Qt10.2.6/sdk/windows/x86/lib/EsriRuntimeQt.lib" ) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/abstract_event_dispatcher.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/abstract_event_dispatcher.cpp new file mode 100755 index 000000000..0715c5489 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/abstract_event_dispatcher.cpp @@ -0,0 +1,311 @@ +/** +** +** @RDE_HEADER@ +** Abstract Event Dispatcher for Qt5 +** $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 .........: +** +** 28.04.2003, JT : First edition for use with Qt4 in START Feature Editor fee +** 25.03.2015, JT : Refactoring for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "abstract_event_dispatcher.h" +#include "gestures.h" + +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include // for debugging only + + +namespace SituationMap { namespace RB +{ + + + QGestureRecognizer * AbstractEventDispatcher::_applyGestureDetection = nullptr; + unsigned int AbstractEventDispatcher::_applyGestureID = 0; + + + AbstractEventDispatcher::AbstractEventDispatcher( QWidget * getEventsFrom ) + : QObject(getEventsFrom) + , _getEventsFrom(getEventsFrom) + , _cursorIsInWidget(false) + , _ignoreMouseMoveTimestamp() + , _synthesisTouchFromMouseEvents(false) + , _deleteRequested(false) + , _hasGrab(false) + , _finishFlag(false) + , _activeEvent(false) + , _dispatcherCursor() + , _whatsThisBackup() + , _eventActions(nullptr) + { + initDispatcher(); + } /* AbstractEventDispatcher::AbstractEventDispatcher */ + + + AbstractEventDispatcher::AbstractEventDispatcher( QWidget * getEventsFrom, EventActionInterface * eventActions ) + : QObject(getEventsFrom) + , _getEventsFrom(getEventsFrom) + , _cursorIsInWidget(false) + , _ignoreMouseMoveTimestamp() + , _synthesisTouchFromMouseEvents(false) + , _deleteRequested(false) + , _hasGrab(false) + , _finishFlag(false) + , _activeEvent(false) + , _dispatcherCursor() + , _whatsThisBackup() + , _eventActions(eventActions) + + { + initDispatcher(); + } /* AbstractEventDispatcher::AbstractEventDispatcher */ + + + void AbstractEventDispatcher::initDispatcher() + { + _restoreCursor = true; + + /* The methode QWidget::ownCursor doesn't exist any more. + * Try to find out if the inherited from parent + */ + + // _restoreCursor = getEventsFrom->ownCursor(); + + if (_restoreCursor) + { + _cursorToRestore = getCursorFromWidget(); + } + _restoreMouseTracking = _getEventsFrom->hasMouseTracking(); + + // just a test exe, but remove process Events + // QCoreApplication::process Events ( ); /// removePostedEvents( _getEventsFrom ); + + _getEventsFrom->setMouseTracking(true); + _getEventsFrom->raise(); + _getEventsFrom->installEventFilter(this); + + _synthesisTouchFromMouseEvents = QCoreApplication::testAttribute( Qt::AA_SynthesizeTouchForUnhandledMouseEvents ); + QCoreApplication::setAttribute( Qt::AA_SynthesizeTouchForUnhandledMouseEvents, false ); + QCoreApplication::setAttribute( Qt::AA_SynthesizeMouseForUnhandledTouchEvents, true ); +#ifdef NEVER + static const Qt::GestureType gestures[] = { Qt::GestureType::TapGesture + , Qt::GestureType::TapAndHoldGesture + , Qt::GestureType::PanGesture + , Qt::GestureType::PinchGesture + , Qt::GestureType::SwipeGesture }; + + for (auto gestureType : gestures) _getEventsFrom->grabGesture(gestureType); + + if (_applyGestureDetection == nullptr) + { + _applyGestureDetection = new ApplyGestureRecognizer(); + _applyGestureID = QGestureRecognizer::registerRecognizer( _applyGestureDetection ); + } + + _getEventsFrom->grabGesture( (Qt::GestureType)_applyGestureID ); + _getEventsFrom->setAttribute( Qt::WA_AcceptTouchEvents, true ); +#endif + } /* AbstractEventDispatcher::initDispatcher */ + + + AbstractEventDispatcher::~AbstractEventDispatcher() + { + + _finishFlag = true; + + if (_getEventsFrom) + { + _getEventsFrom->removeEventFilter(this); + _getEventsFrom->setMouseTracking( _restoreMouseTracking ); + + ungrab(); + + if (!_whatsThisBackup.isNull()) + { + _getEventsFrom->setWhatsThis(_whatsThisBackup); + } + } + QCoreApplication::setAttribute( Qt::AA_SynthesizeTouchForUnhandledMouseEvents, _synthesisTouchFromMouseEvents ); + + } /* AbstractEventDispatcher::~AbstractEventDispatcher */ + + + void AbstractEventDispatcher::onDo_delete() + { + if (_deleteRequested) return; + _deleteRequested = true; + QObject::deleteLater(); + } /* AbstractEventDispatcher::onDo_delete */ + + + void AbstractEventDispatcher::KickTheBucket() + { + if (_getEventsFrom == nullptr) return; + + _finishFlag = true; + + unlinkEventDispatcher(); + + _getEventsFrom->removeEventFilter(this); + _getEventsFrom->setMouseTracking( _restoreMouseTracking ); + + ungrab(); + + if (!_whatsThisBackup.isNull()) + { + _getEventsFrom->setWhatsThis(_whatsThisBackup); + } + + _getEventsFrom = nullptr; + + emit signalKickTheBucket(); + + QCoreApplication::flush(); + // just a test exe, but remove process Events + // QCoreApplication::process Events(); // dispatch window system events + + onDo_delete(); + + } /* AbstractEventDispatcher::KickTheBucket */ + + + void AbstractEventDispatcher::onDo_kickTheBucket() { KickTheBucket(); } + + + void AbstractEventDispatcher::setWhatsThis(const QString & whatsThis) + { + if (_whatsThisBackup.isNull()) + { + _whatsThisBackup = _getEventsFrom->whatsThis(); + } + _getEventsFrom->setWhatsThis(whatsThis); + } /* AbstractEventDispatcher::setWhatsThis */ + + + + void AbstractEventDispatcher::grab() + { + if (_hasGrab) return; + _hasGrab = true; + + setCursorToWidget( _dispatcherCursor ); + _getEventsFrom -> setFocus(); + } /* AbstractEventDispatcher::Grab */ + + + void AbstractEventDispatcher::ungrab() + { + if (!_hasGrab) return; + + _hasGrab = false; + if (_restoreCursor) + { + setCursorToWidget( _cursorToRestore ); + } + // else _getEventsFrom -> unsetCursor(); + + _getEventsFrom -> clearFocus(); + + } /* AbstractEventDispatcher::Ungrab */ + + + bool AbstractEventDispatcher::eventFilter( QObject * widget, QEvent * e ) + { + if (widget != ((QObject*)_getEventsFrom)) return false; + + + return false; + } /* AbstractEventDispatcher::eventFilter */ + + + void AbstractEventDispatcher::setCursorToWidget( const QCursor & cursor ) + { + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * The cursor handling of QGraphicView are not free of bugs. If it tries to restore the default cursor after setting a special cursor for one item + * it can happend that the restored cursor is not the last cursor that was set with setCursor function because the internal stored restore cursor is + * not updated with setCursor function. As work around a mouse move event is send to the widget to reset cursor handling. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + const QPoint & currentCursorPosition = _getEventsFrom->mapFromGlobal( QCursor::pos() ); + +#ifdef ShowBug + QMouseEvent && mouseEvent = QMouseEvent( QEvent::Type::MouseMove, QPointF(-1,-1), Qt::MouseButton::NoButton, Qt::MouseButtons(), Qt::KeyboardModifier::NoModifier ); + + _ignoreMouseMoveTimestamp = mouseEvent.timestamp(); + + QCoreApplication::sendEvent(_getEventsFrom, &mouseEvent ); + QCoreApplication::process Events(); + + _getEventsFrom -> setCursor( cursor ); + + if (_cursorIsInWidget) + { + mouseEvent = QMouseEvent( QEvent::Type::MouseMove, currentCursorPosition, Qt::MouseButton::NoButton, Qt::MouseButtons(), Qt::KeyboardModifier::NoModifier ); + QCoreApplication::sendEvent(_getEventsFrom, &mouseEvent ); + } + QCoreApplication::process Events(); + _getEventsFrom -> update(); +#else + _getEventsFrom -> setCursor( cursor ); +#endif + } /* AbstractEventDispatcher::setCursorToWidget */ + + + QCursor AbstractEventDispatcher::getCursorFromWidget() + { + const QPoint & currentCursorPosition = _getEventsFrom->mapFromGlobal( QCursor::pos() ); + +#ifdef ShowBug + QMouseEvent && mouseEvent = QMouseEvent( QEvent::Type::MouseMove, QPointF(-1,-1), Qt::MouseButton::NoButton, Qt::MouseButtons(), Qt::KeyboardModifier::NoModifier ); + + _ignoreMouseMoveTimestamp = mouseEvent.timestamp(); + + QCoreApplication::sendEvent(_getEventsFrom, &mouseEvent ); + QCoreApplication::process Events(); + + const QCursor & currentCursor = _getEventsFrom->cursor(); + + if (_cursorIsInWidget) + { + mouseEvent = QMouseEvent( QEvent::Type::MouseMove, currentCursorPosition, Qt::MouseButton::NoButton, Qt::MouseButtons(), Qt::KeyboardModifier::NoModifier ); + QCoreApplication::sendEvent(_getEventsFrom, &mouseEvent ); + } + QCoreApplication::process Events(); + _getEventsFrom -> update(); +#else + const QCursor & currentCursor = _getEventsFrom->cursor(); +#endif + return currentCursor; + } /* AbstractEventDispatcher::getCursorFromWidget */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/abstract_event_dispatcher.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/abstract_event_dispatcher.h new file mode 100755 index 000000000..00eb9b392 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/abstract_event_dispatcher.h @@ -0,0 +1,232 @@ +/** +** +** @RDE_HEADER@ +** Abstract Event Dispatcher for Qt5 +** $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 .........: +** +** 28.04.2003, JT : First edition for use with Qt4 in START Feature Editor fee +** 25.03.2015, JT : Refactoring for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include + + +class QWidget; +class QWheelEvent; +class QGestureRecognizer; + + +namespace SituationMap { namespace RB +{ + + /** + @class AbstractEventDispatcher abstract_event_dispatcher.h + @brief Base class of Qt event filter implementations to handle user interactions of map window + + An EventDispatcher installs an event filter on a QWidget to receive all events that are send to + the widget. Depending on the type of event and the state of the event dispatcher the event will + be processed by the dispatcher and stopped or forwarded to the QWidget. In case of processing + the event methodes of the EventActionInterface are called to controll a rubberband. + + @see EventDelegater, EventDispatcher, EventMouseNavigator + */ + class AbstractEventDispatcher : public QObject + { + friend class EventDelegater; + + Q_OBJECT + + public: + + /** + @brief Logical interface between EventDispatcher and Rubberband + Defines interface functions the EventDispatcher will call to control a rubberband. The rubberband class + is derived from EventActionInterface and overrides the interface functions to implement them. The mapping + user interaction to interface action is defined the derived classes of AbstractEventDispatcher. + */ + class EventActionInterface + { + public: + + EventActionInterface() = default; + virtual ~EventActionInterface() = default; + + virtual void unlinkEventDispatcher( AbstractEventDispatcher * causer ) = 0; + + virtual bool selectAction ( AbstractEventDispatcher * causer, int x, int y ) = 0; + virtual bool multiSelectAction ( AbstractEventDispatcher * causer, int x, int y ) = 0; + virtual bool setCoordinateAction ( AbstractEventDispatcher * causer, int x, int y ) = 0; + virtual bool moveAction ( AbstractEventDispatcher * causer, int x, int y ) = 0; + virtual bool movePickedAction ( AbstractEventDispatcher * causer, int x, int y ) = 0; + virtual void resetAction ( AbstractEventDispatcher * causer, int x, int y ) = 0; + virtual bool contextMenuAction ( AbstractEventDispatcher * causer, int x, int y ) = 0; + virtual bool completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) = 0; + virtual bool deleteAction ( AbstractEventDispatcher * causer, int x, int y ) = 0; + virtual bool undoAction ( AbstractEventDispatcher * causer, int x, int y ) = 0; + virtual bool applyAndEndAction ( AbstractEventDispatcher * causer, int x, int y ) = 0; + virtual bool abortAction ( AbstractEventDispatcher * causer, int x, int y ) = 0; + + }; /* EventActionInterface */ + + /** + @name Construction & Destruction + @{ + */ + AbstractEventDispatcher( QWidget * getEventsFrom ); + AbstractEventDispatcher( QWidget * getEventsFrom, EventActionInterface* ); + + virtual ~AbstractEventDispatcher(); + + AbstractEventDispatcher(const AbstractEventDispatcher &) = delete; + AbstractEventDispatcher & operator = (const AbstractEventDispatcher &) = delete; + /** @} */ + + /** + Conceal deleteLater slot. Use onDo_delete() instead to avoid "QCoreApplication::postEvent: Unexpected null receiver" warning. + */ + void deleteLater() = delete; + + + inline void setEventActions(EventActionInterface * eventActions) { _eventActions = eventActions; } + + inline bool hasGrab() const { return _hasGrab; } + + inline void setCursor(const QCursor & cursor) { _dispatcherCursor = cursor; } + + void setWhatsThis(const QString & whatsThis); + + virtual void grab(); + virtual void ungrab(); + + + public slots: + + /** + @brief Slot calling internal \c KickTheBucket methode + + Calls internal methode \c KickTheBucket. Be careful using this function. Should only be used + from implementations of \c EventActionInterface methodes or \c EventDelegater class. + */ + void onDo_kickTheBucket(); + + /** + @brief Slot calling \c deleteLater function of QObject + + Never use \c delete directly for EventDispatcher class. Instead of use \c onDo_delete() function. + */ + void onDo_delete(); + + protected: + + QWidget * _getEventsFrom; + bool _cursorIsInWidget; + + inline bool hasFinished() { return _finishFlag; } + inline bool hasActiveEvent() { return _activeEvent; } + + inline void setActiveEvent(bool val) { _activeEvent = val; } + + /** + @brief Destroying EventDispatcher as result of an incoming event. + + As result of an incomming event (e.g. Keypress ESC) the EventDispatcher will stop, emiting signal \c signalKickTheBucket() + and calls methode \c EventActionInterface::unlinkEventDispatcher() before setup \c deleteLater() call to destroy it self. + */ + virtual void KickTheBucket(); + + + inline void unlinkEventDispatcher() + { + if (_eventActions) + { + _eventActions -> unlinkEventDispatcher( this ); + _eventActions = nullptr; + } + } /* unlinkEventDispatcher */ + + + inline bool callEventAction( bool( EventActionInterface::*actionMethodePtr)( AbstractEventDispatcher*,int,int), int x, int y ) + { + if (_eventActions == nullptr) return false; + return (_eventActions->*actionMethodePtr)( this, x, y ); + } /* callEventAction */ + + + /** + Event filter function to override to implement event handling. To workaround a cursor handling bug in QGraphicsView it is + necessary to call the eventFilter function of this base class, which will monitor enter and leave events. + */ + virtual bool eventFilter( QObject * widget, QEvent * e ); + + inline EventActionInterface * getEventActions() const { return _eventActions; } + + void setCursorToWidget( const QCursor & cursor ); + QCursor getCursorFromWidget(); + + ulong _ignoreMouseMoveTimestamp; + + bool _synthesisTouchFromMouseEvents; + + static unsigned int _applyGestureID; + + private: + + void initDispatcher(); + + bool _deleteRequested; + + bool _hasGrab; + bool _finishFlag; + + bool _restoreCursor; + bool _restoreMouseTracking; + QCursor _cursorToRestore; + + bool _activeEvent; + + QCursor _dispatcherCursor; + QString _whatsThisBackup; + + EventActionInterface * _eventActions; + + + + static QGestureRecognizer * _applyGestureDetection; + + signals: + + void signalKickTheBucket(); + + }; /* EventDispatcher */ + +} } /* namespace SituationMap::RB */ + \ No newline at end of file diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/event_mouse_navigator.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/event_mouse_navigator.cpp new file mode 100755 index 000000000..e49c5678f --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/event_mouse_navigator.cpp @@ -0,0 +1,495 @@ +/** +** +** @RDE_HEADER@ +** Event Dispatcher for Qt5 to delegate mouse and keyboard event to another event dispatcher +** $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 .........: +** +** 15.04.2015, JT : First edition +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include "event_mouse_navigator.h" + +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include // for debugging only +#include + +#include +#include +#include +#include +#include +#include + + +namespace SituationMap { namespace RB +{ + + int EventMouseNavigator::_pickTolerance = 20; + + EventMouseNavigator::EventMouseNavigator(QWidget * getEventsFrom, EsriRuntimeQt::Map * esriMap ) + : AbstractEventDispatcher(getEventsFrom) + , _esriMap(esriMap) + , _selectionMode() + , _selectedIdList() + , _idConnectionCount(0) + , _mousePanningActive(false) + { + } /* EventMouseNavigator::EventMouseNavigator */ + + + bool EventMouseNavigator::eventFilter(QObject * watched, QEvent * e) + { + Q_UNUSED(watched); + + QString debugString(QLatin1String("unhandled event")); + + if (hasFinished()) return true; // don't accept new events if dispatcher is going to finish + if (hasActiveEvent()) return true; // don't accept new events before last event has been handled completly + + if (e) + { + bool handled( false ); + + setActiveEvent( true ); + + switch( e->type() ) + { + + case QEvent::MouseButtonPress : + { + debugString = QLatin1String("mouse button press event"); + + QMouseEvent * mouseEvent = static_cast(e); + + _mousePanningActive = false; + + _mouseMoveRefPos = mouseEvent->pos(); + _restoreMouseCursor = getCursorFromWidget(); + + if (Qt::LeftButton == mouseEvent->button()) + { + handled = false; + + if (mouseEvent->modifiers() == Qt::KeyboardModifier::ControlModifier) + { + _selectionMode = SelectionMode::MultiSelection; + setCursorToWidget( QCursor(Qt::CursorShape::PointingHandCursor) ); + } + else if (mouseEvent->modifiers().testFlag( Qt::KeyboardModifier::ShiftModifier )) + { + _selectionMode = SelectionMode::FrameSelection; + if (mouseEvent->modifiers().testFlag(Qt::KeyboardModifier::ControlModifier)) + { + _selectionMode &= SelectionMode::MultiSelection; + } + else + { + _selectionMode &= SelectionMode::SingleSelection; + } + setCursorToWidget( QCursor(Qt::CursorShape::SizeAllCursor) ); + } + else + { + setCursorToWidget( QCursor(Qt::CursorShape::ArrowCursor) ); + _selectionMode = SelectionMode::SingleSelection; + } + } + + if (Qt::MidButton == mouseEvent->button()) + { + handled = (Qt::MidButton == mouseEvent->button()); + + _mousePanningActive = true; + setCursorToWidget( QCursor(Qt::CursorShape::ClosedHandCursor) ); + } + } break; + + case QEvent::MouseButtonRelease : + { + debugString = QLatin1String("mouse button release event"); + + QMouseEvent * mouseEvent = static_cast(e); + + if (Qt::LeftButton == mouseEvent->button()) + { + handled = false; + + if (!_mousePanningActive) + { + setCursorToWidget( _restoreMouseCursor ); + + selectionRequested( mouseEvent->pos() ); + + _selectionMode = SelectionMode::NoSelection; + // do not handled = true; because some buttons in GraphicsView needs to get the event later. + } + } + + if (Qt::RightButton == mouseEvent->button()) + { + handled = true; + pickRequested( mouseEvent->pos() ); + } + + if ((_mousePanningActive) || (Qt::MidButton == mouseEvent->button())) + { + handled = (Qt::MidButton == mouseEvent->button()); + setCursorToWidget( _restoreMouseCursor ); + } + + _mousePanningActive = false; + } break; + + case QEvent::MouseMove : + { + debugString = QLatin1String("mouse button move event"); + QMouseEvent *mouseEvent = static_cast(e); + + if (mouseEvent->timestamp() != _ignoreMouseMoveTimestamp) + { + if (isSignalConnected(QMetaMethod::fromSignal(&EventMouseNavigator::mouseMovedEvent))) + { + mouseMovedEvent( mouseEvent->pos().x(), mouseEvent->pos().y() ); + } + + if ((!_mousePanningActive) && mouseEvent->buttons().testFlag(Qt::LeftButton)) + { + QPoint deltaMove = mouseEvent->pos()-_mouseMoveRefPos; + if (deltaMove.manhattanLength() > 3) + { + _mousePanningActive = true; + setCursorToWidget( QCursor(Qt::CursorShape::ClosedHandCursor) ); + } + } + + if ((_mousePanningActive) || mouseEvent->buttons().testFlag(Qt::MidButton)) + { + handled = true; + EsriRuntimeQt::AnimationMode mode = _esriMap->animationMode(); + + QPoint deltaMove = mouseEvent->pos()-_mouseMoveRefPos; + QPoint winCenter = QPoint( _getEventsFrom->width() / 2, _getEventsFrom->height() / 2 ); + QPoint newCenter = winCenter-deltaMove; + + const EsriRuntimeQt::Point & mapCenter = _esriMap->toMapPoint(newCenter.x(), newCenter.y()); + + _esriMap -> setAnimationMode( EsriRuntimeQt::AnimationMode::NoAnimation ); + _esriMap -> panTo( mapCenter ); + _esriMap -> setAnimationMode(mode); + + _getEventsFrom -> update(); + + _mouseMoveRefPos = mouseEvent->pos(); + } + } + } break; + + case QEvent::Wheel : + { + debugString = QLatin1String("mouse wheel event"); + // QWheelEvent * wheelEvent = static_cast(e); + // handled = true; + handled = false; + } break; + + case QEvent::Gesture: + { + QGestureEvent * gestureEvent = static_cast(e); + + if (gestureEvent) + { + const QList & gestureList = gestureEvent->gestures(); + for (auto gesture : gestureList) + { + Qt::GestureType gestureType = gesture->gestureType(); + Qt::GestureState gestureState = gesture->state(); + + QTapGesture * tapGesture = nullptr; + QTapAndHoldGesture * tapAndHoldGesture = nullptr; + QPanGesture * panGesture = nullptr; + QPinchGesture * pinchGesture = nullptr; + QSwipeGesture * swipeGesture = nullptr; + QString typeName; + QString stateName; + + switch (gestureType) + { + case Qt::TapGesture : tapGesture = (QTapGesture *)gesture; typeName = QLatin1String("TapGesture"); break; + case Qt::TapAndHoldGesture : tapAndHoldGesture = (QTapAndHoldGesture*)gesture; typeName = QLatin1String("TapAndHoldGesture"); break; + case Qt::PanGesture : panGesture = (QPanGesture *)gesture; typeName = QLatin1String("PanGesture"); break; + case Qt::PinchGesture : pinchGesture = (QPinchGesture *)gesture; typeName = QLatin1String("PinchGesture"); break; + case Qt::SwipeGesture : swipeGesture = (QSwipeGesture *)gesture; typeName = QLatin1String("SwipeGesture"); break; + + default: break; + } + switch (gestureState) + { + case Qt::NoGesture : stateName = QLatin1String("NoGesture"); break; + case Qt::GestureStarted : stateName = QLatin1String("GestureStarted"); break; + case Qt::GestureUpdated : stateName = QLatin1String("GestureUpdated"); break; + case Qt::GestureFinished : stateName = QLatin1String("GestureFinished"); break; + case Qt::GestureCanceled : stateName = QLatin1String("GestureCanceled"); break; + } + + // tapGesture = nullptr; + panGesture = nullptr; + + if (panGesture) + { + EsriRuntimeQt::AnimationMode mode = _esriMap->animationMode(); + + QPointF deltaMove = panGesture->delta(); + QPoint winCenter = QPoint( _getEventsFrom->width() / 2, _getEventsFrom->height() / 2 ); + QPoint newCenter = winCenter-QPoint((int)deltaMove.x(),(int)deltaMove.y()); + + const EsriRuntimeQt::Point & mapCenter = _esriMap->toMapPoint(newCenter.x(), newCenter.y()); + + _esriMap -> setAnimationMode( EsriRuntimeQt::AnimationMode::NoAnimation ); + _esriMap -> panTo( mapCenter ); + _esriMap -> setAnimationMode(mode); + + _getEventsFrom -> update(); + + if (gestureState == Qt::GestureStarted) gestureEvent -> setAccepted(panGesture, true); + + } + + if (pinchGesture) + { + EsriRuntimeQt::AnimationMode mode = _esriMap->animationMode(); + QPinchGesture::ChangeFlags changeFlags = pinchGesture->changeFlags(); + + _esriMap -> setAnimationMode( EsriRuntimeQt::AnimationMode::NoAnimation ); + + if (changeFlags & QPinchGesture::RotationAngleChanged) + { + qreal deltaPhi = pinchGesture->rotationAngle() - pinchGesture->lastRotationAngle(); + _esriMap -> setRotation( _esriMap->rotation()-deltaPhi ); + } + if (changeFlags & QPinchGesture::ScaleFactorChanged) _esriMap -> zoom( 1.0/pinchGesture->scaleFactor() ); + + _esriMap -> setAnimationMode(mode); + + _getEventsFrom -> update(); + + if (gestureState == Qt::GestureStarted) gestureEvent -> setAccepted(pinchGesture, true); + } + } + } + } break; + + default: break; + + } /* switch */ + + if (!hasFinished()) setActiveEvent( false ); + + if (handled) e->accept(); + + if (handled) return true; + } + return AbstractEventDispatcher::eventFilter(watched,e); + + } /* EventMouseNavigator::eventFilter */ + + + void EventMouseNavigator::selectionRequested( const QPoint & pos ) + { + const bool appendSelection = _selectionMode.testFlag( SelectionMode::MultiSelection ); + + if (isSignalConnected( QMetaMethod::fromSignal(&EventMouseNavigator::selectionRequest) )) + { + // do the work only in case of slot connections are installed + + if (0 != _idConnectionCount) + { + qWarning() << LOGFUNCTION << " graphic layer id list request is still pending!"; + return; + } + + pickObjects( pos, _selectedIdList, appendSelection, &EventMouseNavigator::selectionRequest ); + + } + } /* EventMouseNavigator::selectionRequested */ + + + void EventMouseNavigator::pickRequested( const QPoint & pos ) + { + if (isSignalConnected( QMetaMethod::fromSignal(&EventMouseNavigator::contextMenuRequest) )) + { + // do the work only in case of slot connections are installed + + if (0 != _idConnectionCount) + { + qWarning() << LOGFUNCTION << " graphic layer id list request is still pending!"; + return; + } + + pickObjects( pos, _selectedIdList, false, &EventMouseNavigator::contextMenuRequest ); + + } + } /* EventMouseNavigator::pickRequested */ + + + void EventMouseNavigator::pickObjects( const QPoint & pos + , QList & pickIdList + , bool append + , void (EventMouseNavigator::*pickSignal)(const QList< uint>&,QPoint) ) + { +#ifdef NEVER + if (!append) _selectedIdList.clear(); + + QList layerList = _esriMap->layers(); + QList selectLayerList; + + /* ~~~~~~~~~~~~~~~~~~~ first look for all layers in map ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + for (auto layer : layerList) + { + EsriRuntimeQt::MessageGroupLayer* msgGrpLayer = qobject_cast(layer); + if (msgGrpLayer) + { + selectLayerList.append( msgGrpLayer->getLayers() ); + } + else selectLayerList.append(layer); + } + + /* ~~~~~~~~~~~~~~~~~~~ loop over all layers in layer list ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + for (auto layer : selectLayerList) + { + EsriRuntimeQt::FeatureLayer* featureLayer = qobject_cast(layer); + if (featureLayer) + { + /*~~~~~~~~~~~~~~~~~~~~~~~ get selected features by direct function call ~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + QList fidList = featureLayer -> featureIds( pos.x(), pos.y(), _pickTolerance ); + featureLayer->selectFeatures( fidList, append ); + + EsriRuntimeQt::FeatureTable* featureTable = featureLayer->featureTable(); + + for (auto featureId : fidList) + { + if (featureTable) + { + EsriRuntimeQt::Feature* feature = featureTable->feature( featureId ); + if (feature) + { + bool ok; + SituationMap::UniqueMapId id; + + id = feature->property( SituationMap::UniqueMapIdProperty ).toUInt( &ok ); + if (ok) + { + if (!pickIdList.contains(id)) + { + pickIdList += id; + } + } + } + } + } // for + } // if (featureLayer) + + EsriRuntimeQt::GraphicsLayer * graphicsLayer = qobject_cast(layer); + if (graphicsLayer) + { + + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * In case of message symbols, in a graphic layer a slot method has to be used to get + * the ids of selected symbols. The slot installed (connected) each time + * EventMouseNavigator::selectionRequested() has been called and will be disconnect itself at + * beginning of slot function. Because the slot methode will be called later, we have to store + * some information like the _selectedIdList as member variable. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * connect here, because the layer in MessageGroupLayer can change: + * do not use QMetaObject:.Connection directly because disconnect will fail. + * See also http://stackoverflow.com/questions/13847507/qt5-new-signal-to-lambda-connections-memory-leak + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + _idConnectionCount++; + + auto conn = std::make_shared(); + *conn = connect( graphicsLayer, &EsriRuntimeQt::GraphicsLayer::graphicIdsComplete, + this , [conn,pos,&pickIdList,graphicsLayer,pickSignal,this]( const QList& gIdList ) + { + disconnect( *conn ); + for (auto gId : gIdList) + { + QString objectName(QLatin1String(" - no graphic - ")); + + EsriRuntimeQt::Graphic * graphic = graphicsLayer->graphic(gId); + if (graphic) + objectName = graphic->objectName(); + + qDebug() << LOGFUNCTION + << " gId=" << gId + << " name= " << objectName; + + if (graphic) + { + bool ok; + SituationMap::UniqueMapId id; + + id = graphic->property( SituationMap::UniqueMapIdProperty ).toUInt( &ok ); + if (ok) + { + if (!pickIdList.contains(id)) + { + pickIdList += id; + } + } + } + } + + // Just emit selectionRequest only once + if (_idConnectionCount <= 1) + { + (this->*pickSignal)( pickIdList, pos ); + _idConnectionCount = 0; + } + else _idConnectionCount--; + + } ); // end of lambda methode + + graphicsLayer->graphicIds( pos.x(), pos.y(), _pickTolerance, 100 ); + + } + } // for layer + if (_idConnectionCount == 0) + { + (this->*pickSignal)( pickIdList, pos ); + } +#endif + } // EventMouseNavigator::pickObjects + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/event_mouse_navigator.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/event_mouse_navigator.h new file mode 100755 index 000000000..42bbb84f0 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/event_mouse_navigator.h @@ -0,0 +1,154 @@ +/** +** +** @RDE_HEADER@ +** Event Dispatcher for Qt5 to handle mouse events navigate on map +** $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 .........: +** +** 24.04.2015, JT : first edition +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include + +#include "abstract_event_dispatcher.h" + + +namespace EsriRuntimeQt +{ + class Map; + +} /* namespace EsriRuntimeQt */ + + +namespace SituationMap { namespace RB +{ + + /** + @class EventMouseNavigator event_mouse_navigator.h + @brief Event filter that handles mouse events for general map operation like selecting object, zooming and paning the map window + + Implements class \c AbstractEventDispatcher to manage event handling for general map operation like selecting object, zooming and + panning the map window. Monitoring the mouse position can be realized by connecting the \c mouseMovedEvent signal. If the user has + clicked to map objects the signal \c selectionRequest is emitted. + + Following events are handled: + + | Input-Event | Interface Function | Description | + | :--------------------------------------------- | : ----------------- | : --------------------------------------------------------------------------- | + | Mouse move without any modifier | | Move Cursor. | + | Mouse move with pressed left button | | Panning of map if more then 3 pixels moved. | + | Mouse move with pressed middle button | | Panning of map. | + | Mouse wheel | | Redirect to \c Esri map for zooming. | + | Left mouse button release | | Emits signal \c selectionRequest with list of map objects at cursor position. | + + @see AbstractEventDispatcher, EventDelegater, EventDispatcher + */ + + class EventMouseNavigator : public AbstractEventDispatcher + { + + Q_OBJECT + + public: + + /** + @name Construction & Destruction + @{ + */ + EventMouseNavigator(const EventMouseNavigator &) = delete; + EventMouseNavigator & operator = (const EventMouseNavigator &) = delete; + + EventMouseNavigator( QWidget * getEventsFrom, EsriRuntimeQt::Map * esriMap ); + + virtual ~EventMouseNavigator() {} + /** @} */ + + inline static void setPickTolerance( int value = 20 ) { _pickTolerance = value; } + + signals: + + /** + Will be emitted if the user clicks to map objects with the left mouse button. + */ + void selectionRequest( const QList< uint > & idList, QPoint viewPortPos ); + + /** + Will be emitted if the user clicks to the map with the rigth mouse button. + */ + void contextMenuRequest( const QList< uint > & idList, QPoint viewPortPos ); + + /** + Signal will be emitted if a mouse move is done. + */ + void mouseMovedEvent( int x, int y ); + + protected: + + virtual bool eventFilter(QObject*, QEvent*); + + enum SelectionMode + { + NoSelection = 0x00, + SingleSelection = 0x01, + MultiSelection = 0x02, + FrameSelection = 0x04 + }; // SelectionMode + + Q_DECLARE_FLAGS(SelectionModes,SelectionMode) + + EsriRuntimeQt::Map * _esriMap; + QPoint _mouseMoveRefPos; + QCursor _restoreMouseCursor; + + static int _pickTolerance; + SelectionModes _selectionMode; + QList _selectedIdList; // List of selected object-ids. Is completly handled in selectionRequested() + unsigned int _idConnectionCount; // counter to avoid multiple signals selectionRequest, see implementation of selectionRequested() + bool _mousePanningActive; // is panning with mouse active + + /** + Find out which map objects are at given mouse position and emits the signal selectionRequest + */ + void selectionRequested( const QPoint & ); + + /** + Find out which map objects are at given mouse position and emits the signal contextMenuRequest + */ + void pickRequested( const QPoint & ); + + /** + Helper function to get all map objets at a given position + @param pos position in window coordinates + @param pickIdList List to store uid's of picked map objects. Don't use a local variable here because the picking is a asynchrone function + @param append If true the uid's are appended at end of list. Otherwise the list will be cleared first. + @param pickSignal Function pointer to signal to be emitted at end. + */ + void pickObjects( const QPoint & pos + , QList & pickIdList + , bool append + , void (EventMouseNavigator::*pickSignal)(const QList< uint>&,QPoint) ); + + }; /* EventMouseNavigator */ + + +} } /* namespace SituationMap::RB */ diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/gestures.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/gestures.cpp new file mode 100755 index 000000000..cd9147589 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/gestures.cpp @@ -0,0 +1,102 @@ +/** + * @RDE_HEADER@ + * Declaration/Definition + * $Id$ + * $HeadURL$ + * $Author$ + */ + + +#include "gestures.h" + +#include +#include + +#include + +namespace SituationMap { namespace RB { + + // TabGestureRecognizer + + ApplyGestureRecognizer::ApplyGestureRecognizer() : QGestureRecognizer() + { + } + + ApplyGestureRecognizer::~ApplyGestureRecognizer() + {} + + + QGesture * ApplyGestureRecognizer::create( QObject * target ) + { + qDebug() << "ApplyGesture create"; + + if (target && target->isWidgetType()) + { + ((QWidget*)target)->setAttribute(Qt::WA_AcceptTouchEvents); + } + return new ApplyGesture(); + } // TabGestureRecognizer::create + + + QGestureRecognizer::Result ApplyGestureRecognizer::recognize( QGesture * state, QObject* , QEvent * event) + { + ApplyGesture * q = static_cast(state); + const QTouchEvent * ev = static_cast(event); + + QGestureRecognizer::Result result = QGestureRecognizer::CancelGesture; + + switch (event->type()) + { + case QEvent::TouchBegin: + { + qDebug() << "ApplyGesture begin"; + q->_position = ev->touchPoints().at(0).pos(); + q->setHotSpot(ev->touchPoints().at(0).screenPos()); + q->_tripleTouch = (ev->touchPoints().size() == 3); + result = QGestureRecognizer::TriggerGesture; + } break; + + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + { + if (q->state() != Qt::NoGesture && ev->touchPoints().size() >= 1 && ev->touchPoints().size() <= 3) + { + if (ev->touchPoints().size() == 3) q->_tripleTouch = true; + qDebug() << "ApplyGesture update _tripleTouch=" << q->_tripleTouch; + QTouchEvent::TouchPoint p = ev->touchPoints().at(0); + QPoint delta = p.pos().toPoint() - p.startPos().toPoint(); + enum { TapRadius = 40 }; + if (delta.manhattanLength() <= TapRadius) + { + if (event->type() == QEvent::TouchEnd) + { + result = (q->_tripleTouch) ? QGestureRecognizer::FinishGesture : QGestureRecognizer::CancelGesture; + } + else result = QGestureRecognizer::TriggerGesture; + } + } + } break; + + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: + result = QGestureRecognizer::Ignore; + break; + default: + result = QGestureRecognizer::Ignore; + break; + } + + return result; + } // TabGestureRecognizer::recognize( QGesture * state, QObject * watched, QEvent * event) + + + void ApplyGestureRecognizer::reset( QGesture * state ) + { + ApplyGesture * q = static_cast(state); + q->_tripleTouch = false; + } // TabGestureRecognizer::reset( QGesture * gesture ) + + + + }} // namspaces diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/gestures.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/gestures.h new file mode 100755 index 000000000..4b367870a --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/gestures.h @@ -0,0 +1,71 @@ +/** +** +** @RDE_HEADER@ +** Gesture recognition +** $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 .........: +** +** 09.12.2015, JT : First edition for use with Qt5 +** +** ----------------------------------------------------------------------------------------------- +*/ + +#pragma once + +#include +#include +#include + + +namespace SituationMap { namespace RB +{ + class ApplyGestureRecognizer : public QGestureRecognizer + { + public: + ApplyGestureRecognizer(); + virtual ~ApplyGestureRecognizer(); + + QGesture * create( QObject * target ) override; + QGestureRecognizer::Result recognize( QGesture * gesture, QObject * watched, QEvent * event) override; + void reset( QGesture * gesture ) override; + + }; // ApplyGestureRecognizer + + + class ApplyGesture : public QGesture + { + friend class ApplyGestureRecognizer; + + public: + + ApplyGesture() = default; + ~ApplyGesture() = default; + + ApplyGesture( const ApplyGesture & ) = delete; + ApplyGesture & operator = ( const ApplyGesture & ) = delete; + + QPointF position() const { return _position; } + + private: + + QPointF _position; + bool _tripleTouch; + }; // ApplyGesture + +}} // namespaces diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/main.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/main.cpp new file mode 100755 index 000000000..190e66d17 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/EsriMapTouchTest/main.cpp @@ -0,0 +1,83 @@ +/** + * @RDE_HEADER@ + * Declaration/Definition + * $Id$ + * $HeadURL$ + * $Author$ + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "Map.h" +#include "MapGraphicsView.h" +#include "ArcGISLocalTiledLayer.h" + +#include "event_mouse_navigator.h" + + +int main (int argc, char* argv[]) +{ + QApplication app ( argc, argv ); + + // under windows for ArcGIS 10.2.6 and higher + #ifdef Q_OS_WIN + QCoreApplication::setAttribute( Qt::AA_UseOpenGLES ); + #endif + + QApplication::setApplicationName(QString("Esri - Map Touch Test")); + + QWidget top; + QHBoxLayout * layout = new QHBoxLayout( &top ); + QTextEdit * console = new QTextEdit( &top ); + EsriRuntimeQt::Map * map = new EsriRuntimeQt::Map(); + EsriRuntimeQt::MapGraphicsView * mapGraphicsView = EsriRuntimeQt::MapGraphicsView::create(map); + + QString samplePath = EsriRuntimeQt::ArcGISRuntime::installDirectory(); + + samplePath.append("/sdk/samples/data/tpks/Topographic.tpk"); + + samplePath = QDir::toNativeSeparators(samplePath); + + if (!QFileInfo::exists(samplePath)) + { + qDebug() << "Load map from not existing file" << samplePath; + } + + EsriRuntimeQt::ArcGISLocalTiledLayer * geoMapBaseLayer = new EsriRuntimeQt::ArcGISLocalTiledLayer( samplePath, &top ); + geoMapBaseLayer -> setName( "Base Map Layer" ); + + QObject::connect( geoMapBaseLayer, &EsriRuntimeQt::ArcGISLocalTiledLayer::layerCreateError , []() { qDebug() << "Loading map failed!"; } ); + QObject::connect( geoMapBaseLayer, &EsriRuntimeQt::ArcGISLocalTiledLayer::layerCreateComplete, []() { qDebug() << "Loading map finished."; } ); + + map->setAnimationMode( EsriRuntimeQt::AnimationMode::NoAnimation ); + map->addLayer(geoMapBaseLayer); + map->setWrapAroundEnabled(true); + + map->grid()->setType( EsriRuntimeQt::GridType::Mgrs ); + map->grid()->setVisible(false); + + mapGraphicsView -> setFixedSize( 600, 400 ); + + SituationMap::RB::EventMouseNavigator * eventDispatcher = new SituationMap::RB::EventMouseNavigator( mapGraphicsView->viewport(), map ); + layout->addWidget( mapGraphicsView ); + layout->addWidget( console ); + + top.show(); + + QPushButton * button = new QPushButton( QString("Touch or Click me"), nullptr ); // button must be toplevel widget + QGraphicsProxyWidget * widgetProxy = mapGraphicsView->scene()->addWidget( button ); + + widgetProxy -> setPos( mapGraphicsView->mapToScene(250,200) ); + + QObject::connect( button, &QPushButton::clicked, [console]() { console->append(QString("Button has been clicked.\n")); }); + QObject::connect( button, &QPushButton::pressed, [console]() { console->append(QString("Button has been pressed.\n")); }); + + return app.exec(); +} // main (int argc, char* argv[]) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/GraphicsViewTouchTest/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/GraphicsViewTouchTest/CMakeLists.txt new file mode 100755 index 000000000..bbcc6de44 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/GraphicsViewTouchTest/CMakeLists.txt @@ -0,0 +1,20 @@ + +cmake_minimum_required(VERSION 3.16) + +SET(QT_MIN_VERSION 6.0.0) +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +SET(CMAKE_CXX_STANDARD 11) + +FILE ( GLOB sourceFiles *.h *.cpp ) + +FIND_PACKAGE(OpenGL) +FIND_PACKAGE(Qt6 COMPONENTS Core Gui OpenGL Widgets REQUIRED) + +INCLUDE_DIRECTORIES ( ${QT_INCLUDE_DIR} ) + +SET(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) + +ADD_EXECUTABLE( GraphicsViewTouchTest WIN32 ${sourceFiles} ) + +target_link_libraries( GraphicsViewTouchTest Qt6::Core Qt6::Gui Qt6::OpenGL Qt6::Widgets ) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/GraphicsViewTouchTest/main.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/GraphicsViewTouchTest/main.cpp new file mode 100755 index 000000000..c2b029a7f --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/GraphicsViewTouchTest/main.cpp @@ -0,0 +1,86 @@ +/** + * @RDE_HEADER@ + * Declaration/Definition + * $Id$ + * $HeadURL$ + * $Author$ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +class View : public QGraphicsView +{ + private: + + QPushButton * m_button; + QPushButton * m_reset; + QTextEdit * m_console; + + public: + + View(QGraphicsScene * scene, QTextEdit * console, QWidget * parent = nullptr) : QGraphicsView(scene, parent), m_console(console) + { + setFixedSize( 600, 400 ); + + QGraphicsProxyWidget * widgetProxy; + + m_reset = new QPushButton( "Reset", nullptr ); // button must be toplevel widget + widgetProxy = scene->addWidget( m_reset ); + widgetProxy -> setPos( this->mapToScene(150,100) ); + + connect(m_reset, &QPushButton::clicked, this, [this]() + { + this->m_console->append("Reset cursor.\n"); + this->viewport()->setCursor( Qt::CursorShape::CrossCursor ); + }); + + m_button = new QPushButton( "Touch or Click me \n to change cursor.", nullptr ); // button must be toplevel widget + widgetProxy = scene->addWidget( m_button ); + widgetProxy -> setPos( this->mapToScene(250,200) ); + + connect(m_button, &QPushButton::clicked, this, [this]() + { + this->m_console->append("Button has been clicked --> cursor shape changed to cross cursor.\n"); + this->viewport()->setCursor( Qt::CursorShape::CrossCursor ); + update(); + }); + + connect(m_button, &QPushButton::pressed, this, [this]() + { + this->m_console->append("Button has been pressed.\n"); + }); + + this->viewport()->setCursor( Qt::CursorShape::PointingHandCursor ); + + } // View( QGraphicsScene * scene, QWidget * parent = nullptr ) + +}; // class View + + +int main (int argc, char* argv[]) +{ + QApplication app ( argc, argv ); + + QApplication::setApplicationName("QGraphicsView Touch Test"); + + QGraphicsScene scene; + QWidget top; + QHBoxLayout * layout = new QHBoxLayout( &top ); + QTextEdit * console = new QTextEdit( &top ); + View * view = new View( &scene, console, &top ); + + layout->addWidget( view ); + layout->addWidget( console ); + + top.show(); + + return app.exec(); +} // main (int argc, char* argv[]) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/LineOfSightEvaluation/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/LineOfSightEvaluation/CMakeLists.txt new file mode 100755 index 000000000..390444c8b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/LineOfSightEvaluation/CMakeLists.txt @@ -0,0 +1,37 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +# EXAMPLE +SET(EXAMPLE_SRCS + lineOfSightEvaluation.cpp +) + +SET(EXAMPLE_HDRS +) + +IF(MSVC) + + # Configure the template file + SET(VS_USER_FILE exampleSvgRenderer.vcxproj.user) + configure_file( ../../SituationMap.vcxproj.user.in ${CMAKE_CURRENT_BINARY_DIR}/${VS_USER_FILE} @ONLY) + +ENDIF(MSVC) + +NGL_ADD_EXECUTABLE(WS NO_TRANSLATION lineOfSightEvaluation ${EXAMPLE_SRCS} ${EXAMPLE_HDRS}) +NGL_TARGET_LINK_LIBRARIES(lineOfSightEvaluation + NGLLogger + nglapplication + Qt6::Widgets + Qt6::Core + Qt6::Svg + Qt6::Gui + + ArcGIS::ArcGIS + ArcGISQt5Hack + + situationMapCommon + situationMapElevationMap +) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/LineOfSightEvaluation/lineOfSightEvaluation.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/LineOfSightEvaluation/lineOfSightEvaluation.cpp new file mode 100755 index 000000000..01e327c74 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/LineOfSightEvaluation/lineOfSightEvaluation.cpp @@ -0,0 +1,330 @@ +/** + * @RDE_HEADER@ + * Interface + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include +#include +#include +#include +#include + + +#include +#include + +#include + +#include + +#include +using namespace SituationMap; + + +// some helper functions +inline Q_CONSTEXPR double pi() { return M_PI; } +inline Q_CONSTEXPR double deg2rad() { return pi() / 180.; } +inline Q_CONSTEXPR double rad2deg() { return 1. / deg2rad(); } + + +//---------------------------------------------------------------------------- +/*! + \brief Define visibility. + */ +enum class LineOfSightVisibility +{ + Undefined, /*!< May be no data? */ + Visible, + Obstructed +}; +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- +class ObserverTargetData +{ +public: + explicit ObserverTargetData(const Point3D &oberserver = Point3D(), // z-value: elevation@terrain + observerHeight + const Point3D &target = Point3D()) // z-value: elevation@terrain + targetHeight + : m_oberserver(oberserver), + m_target(target) + {} + + const Point3D &observer() const { return m_oberserver; } + const Point3D &target() const { return m_target; } + + const Point3D &obstacle() const { return m_obstacle; } + void setObstacle(const Point3D &obstacle) { m_obstacle = obstacle; } + + LineOfSightVisibility lineOfSightVisibility() const { return m_visible; } + void setLineOfSightVisibility(const LineOfSightVisibility &visible) + { + m_visible = visible; + } + + bool isObserverAndTargetValid() + { + return (!m_oberserver.isNull()) && (!m_target.isNull()); + } + +private: + Point3D m_oberserver; + Point3D m_target; + + Point3D m_obstacle; + LineOfSightVisibility m_visible = LineOfSightVisibility::Undefined; + +}; +//---------------------------------------------------------------------------- + + + +class LineOfSightCalculation +{ +public: + explicit LineOfSightCalculation(ElevationMap *elevationMap) + : m_logger(getLogger("LineOfSightCalculation")) + , m_elevationMap(elevationMap) + { + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + } + + bool calculate(ObserverTargetData &observerTarget, unsigned int sampleIntervallMeter = 5) + { + // same checks: + if (!m_elevationMap || !m_elevationMap->isValid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " no elevationMap configured or not valid"; + observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Undefined); + return false; + } + + if (!observerTarget.isObserverAndTargetValid()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " observer and/or target not valid"; + observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Undefined); + return false; + } + + if (observerTarget.observer().toPointF() == observerTarget.target().toPointF()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " observer and target are identical regard x and y"; + observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Undefined); + return false; + } + + + bool success = true; + + QElapsedTimer timer; + if (m_logger.isErrorEnabled()) + timer.start(); + + // + // + // consider using ElevationMap::LOSProfile + // + // + + // determine how many iterations we need: + double distanceInMeter = distanceMeter(observerTarget.observer(), + observerTarget.target()); + ERROR_LOG(m_logger) << LOGFUNCTION << " distanceMeter = " << distanceInMeter; + + int maxNumberOfIterations = distanceInMeter / sampleIntervallMeter; // check visibility every ~5m + // maxNumberOfIterations = 1000000; + + if (maxNumberOfIterations<=0) + maxNumberOfIterations = 5; + + + double distanceFromObserverToTargetAsFraction = 0; + int i = 0; + while (i<=maxNumberOfIterations) + { + /* + + z-axis (Elevation) pCandidate + |---------- / + | ---------- + | ---------- + | ---------- + | ---------- + |------------------------------------------------------------ xy-axis + ^ ^ ^ ^ ^ ^ ^ ^ samples (every ~5meters) + ^ ^ + ObserverPosition TargetPosition + + On every sample position, check if directLineElevation (maximum) is + obstructed by elevationMapElevation (real value) + if (elevationMapElevation>directLineElevation) -> obstructed/break + */ + + distanceFromObserverToTargetAsFraction = (double)i/maxNumberOfIterations; + + + const Point3D &pCandidate = movePoint(observerTarget.observer(), + observerTarget.target(), + distanceFromObserverToTargetAsFraction); + +// { +// static Point3D oldCandidate; +// double distanceInMeter = distanceMeter(pCandidate, oldCandidate); +// ERROR_LOG(m_logger) << LOGFUNCTION << " distanceMeter = " << distanceInMeter; +// oldCandidate = pCandidate; +// } + + + // z is the elevation of a *direct* line: + const double directLineElevation = pCandidate.z(); + const double elevationMapElevation = m_elevationMap->elevation(pCandidate.x(), pCandidate.y()); + + // Coord covered by elevationMap? + if (elevationMapElevation <= std::numeric_limits::min()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Error, coordinate not covered be elevationData. Finish this LineOfSightCalculation." + << " i=" << i + << ", pCandidate = " << pCandidate; + + // write back data + observerTarget.setObstacle(pCandidate); + observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Undefined); + + // finish the loop: + success = false; + break; + } + + // analyze / check visibility: + if (elevationMapElevation>directLineElevation) + { + INFO_LOG(m_logger) << LOGFUNCTION << " i=" << i + << " break - pCandidate = " << pCandidate + << " -> directLineElevation = " << directLineElevation + << " -> elevationMapElevation = " << elevationMapElevation; + + // write result + observerTarget.setObstacle(pCandidate); + observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Obstructed); + + // finish the loop: + break; + } + + + ++i; + } // while + + + if (m_logger.isErrorEnabled()) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " END" + << ", success = " << success + << ", " << i << " of "<< maxNumberOfIterations << " iterations" + << ", distanceFromObserverToTargetAsFraction = " << distanceFromObserverToTargetAsFraction + << ", pCandidate=" << observerTarget.obstacle() + << ", lineOfSightVisibility=" << static_cast(observerTarget.lineOfSightVisibility()) + << ", took [ms] " << timer.elapsed(); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" << ", success = " << success; + return success; + } + +protected: + static Point3D movePoint(const Point3D &oberserver, + const Point3D &target, + double distanceFromObserverToTargetAsFraction) // 0.0 ... 1.0 + { + // positions are lon/lat @ rad. + // for evaluation we do that very simple: + + // strange, but: using Point3D seems to be faster then using QPointF + const Point3D &dist = target - oberserver; + const Point3D &point = oberserver + (dist * distanceFromObserverToTargetAsFraction); + +// FATAL_LOG(m_logger) << LOGFUNCTION << " obs = " << oberserver; +// FATAL_LOG(m_logger) << LOGFUNCTION << " tgt = " << target; +// FATAL_LOG(m_logger) << LOGFUNCTION << " fct = " << distanceFromObserverToTargetInPercent; +// FATAL_LOG(m_logger) << LOGFUNCTION << " mvd = " << point; + + return point; + } + + static double distanceMeter(const Point3D &oberserver, + const Point3D &target) + { + Esri::ArcGISRuntime::Point geom1(oberserver.x()*rad2deg(), oberserver.y()*rad2deg(), oberserver.z(), Esri::ArcGISRuntime::SpatialReference::wgs84()); + Esri::ArcGISRuntime::Point geom2(target.x() *rad2deg(), target.y() *rad2deg(), target.z() , Esri::ArcGISRuntime::SpatialReference::wgs84()); + + Esri::ArcGISRuntime::GeometryEngine *gEngine = Esri::ArcGISRuntime::GeometryEngine::instance(); + Esri::ArcGISRuntime::GeodeticDistanceResult geodeticDistanceResult = gEngine->distanceGeodetic(geom1, geom2, + Esri::ArcGISRuntime::LinearUnitId::Meters, + Esri::ArcGISRuntime::AngularUnitId::Degrees, + Esri::ArcGISRuntime::GeodeticCurveType::Geodesic); + + double distance = geodeticDistanceResult.distance(); + + return distance; + } + + + + +private: + LOGGER_T m_logger; ///< Interface of the Logging-Facility + ElevationMap *m_elevationMap = nullptr; + + ObserverTargetData m_observerTarget; +}; + + + +#include + + +int main (int argc, char* argv[]) +{ + NGLApplication app ( argc, argv ); + + app.createLogger("lineOfSightEvaluation", "log4cxx_sitmapMain.props"); + + LOGGER_T m_logger=app.baseLogger(); + + STARTUP_LOG(m_logger); + + + QString elevationFileAbs(QLatin1String("/usr/share/arcgis/resources/elevation/srtm_38_02.tif")); // srtm_38_02.tif -> Bremen + QFileInfo fi (elevationFileAbs); + + if (!fi.exists(elevationFileAbs)) + { + ERROR_LOG(m_logger) << LOGFUNCTION << ", elevationFile '" << elevationFileAbs << "' doesn't exists"; + return -1; + } + + ElevationMap *elevationMap = new ElevationMap(fi); + + elevationMap->waitForInitialization(); + ERROR_LOG(m_logger) << LOGFUNCTION << " elevation.isValid() =" << elevationMap->isValid(); + + //qCritical() << LOGFUNCTION << " elevation.isValid() = " << elevationMap->isValid() + // << " elevation =" << elevationMap->elevation(0.155, 0.926); + + LineOfSightCalculation losCalc(elevationMap); + + ObserverTargetData obsTgt(Point3D(0.1504, 0.926, 20), + Point3D(0.1530, 0.926, 5)); + + losCalc.calculate(obsTgt); + + int iRet = app.exec(); + + delete elevationMap; + SHUTDOWN_LOG(m_logger); + + return iRet; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/QGraphicsItem_Fill/QGraphicsItem_FillBug.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/QGraphicsItem_Fill/QGraphicsItem_FillBug.cpp new file mode 100755 index 000000000..94e9b8419 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/QGraphicsItem_Fill/QGraphicsItem_FillBug.cpp @@ -0,0 +1,267 @@ +/* +** +** @RDE_HEADER@ +** Shows a crash when using QGraphicsItems and ESRI-Graphics in same scene +** $Id$ +** $HeadURL$ +** $Author$ +** +** ----------------------------------------------------------------------------------------------- +** +** Hardware-Environment: Intel-PC +** Software-Environment: Windows 7, Qt 5.7. ArcGISRuntimeQt 100 +** +** Language ........: C++ 11 +** +** ----------------------------------------------------------------------------------------------- +** +** Author ...........: Jörg Turla, SPD G, Tel. 3132 +** +** ----------------------------------------------------------------------------------------------- +** +** Notes .........: +** +** 16.01.2017, JT : Initial +** +** ----------------------------------------------------------------------------------------------- +*/ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * The class MapWindow creates a tiled base map from an UTM based TPK from Bremen. Loading the map will be done + * by clicking the "Load Map" button. The next 2 buttons creates graphics on top of the map, as follows: + * + * (1) Draw QGraphicsPathItem + * The "Draw QGraphicsPathItem" is creating an QGraphicsItemPath. The QGraphicsItemPath has a blue border line and + * is filled with a tranparent green color. See drawQGraphicPathItem(). + * + * (2) Draw Overlay + * The "Draw Overlay" is creating GraphicsOverlay with one simple polygon. See buildSymbol(). + * + * All is working as expected when using only one of each button, restart the program and click the other one. + * Each of both drawings are ok. In one case you will see a red filled polygon and in other case a green polygon + * with a border is drawn on the map. + * + * But now restart the program click "Draw QGraphicsPathItem" the green area with + * a blue border is visible now. After that click "Draw Overlay". The red area of GraphicOverlay polygon appears + * on the map. But the green filling of the QGraphicsItemPath has been vanished. + * + * Restart: Next click the buttons in reversed order ( "Draw Overlay" first and next "Draw QGraphicsPathItem"). On Windows + * the behaviour is the same as before - the green polygon filling is not drawn. On Linux a crash in the OpenGl + * Driver appears. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +class MapWindow : public QWidget +{ + Esri::ArcGISRuntime::ArcGISTiledLayer * m_tiledLayer; + Esri::ArcGISRuntime::Basemap * m_basemap; + Esri::ArcGISRuntime::Map * m_map; + Esri::ArcGISRuntime::MapGraphicsView * m_mapView; + + private: + + + public: + + MapWindow() : QWidget(), m_tiledLayer(nullptr) + { + m_basemap = new Esri::ArcGISRuntime::Basemap(this); + m_map = new Esri::ArcGISRuntime::Map(this); + + m_map->setAutoFetchLegendInfos(true); + + m_mapView = new Esri::ArcGISRuntime::MapGraphicsView(this); + + m_mapView->setObjectName(QLatin1String("EsriMapView")); + m_mapView->setFrameShape(QFrame::NoFrame); + m_mapView->setWrapAroundMode(Esri::ArcGISRuntime::WrapAroundMode::Disabled); + m_mapView->setFixedSize(900,900); + + + QPushButton * btn1 = new QPushButton(QStringLiteral("Load Map"), this); + QPushButton * btn2 = new QPushButton(QStringLiteral("Draw QGraphicsPathItem"), this); + QPushButton * btn3 = new QPushButton(QStringLiteral("Draw Overlay (Esri primitive)"), this); + + btn2->setEnabled(false); + btn3->setEnabled(false); + + connect( btn1, &QPushButton::clicked, this, [this](bool) { initMap(); }); + connect( btn2, &QPushButton::clicked, this, [this](bool) { drawQGraphicPathItem(); }); + connect( btn3, &QPushButton::clicked, this, [this](bool) { buildSymbol(); }); + + QVBoxLayout * layout = new QVBoxLayout(this); + + layout->addWidget(btn1); + layout->addWidget(btn2); + layout->addWidget(btn3); + layout->addWidget(m_mapView,2); + + + connect(m_map, &Esri::ArcGISRuntime::Map::doneLoading, this, [this,btn2,btn3](Esri::ArcGISRuntime::Error loadError) + { + if (loadError.isEmpty()) + { + btn2->setEnabled(true); + btn3->setEnabled(true); + } else { + qCritical() << loadError.message(); + qCritical() << loadError.additionalMessage(); + + // Esri::ArcGISRuntime::ErrorDomainType errorDomain = loadError.domain(); + // Esri::ArcGISRuntime::ExtendedErrorType extendedErr = loadError.extendedErrorType(); + } + + }); + } // MapWindow() + + + void initMap() + { + //m_tiledLayer = new Esri::ArcGISRuntime::ArcGISTiledLayer(QUrl("http://192.168.133.90:6080/arcgis/rest/services/BremenGeoTiffUTM/MapServer")); + Esri::ArcGISRuntime::TileCache * tpkCache = new Esri::ArcGISRuntime::TileCache("/usr/share/arcgis/resources/Topographic.tpk", this); + + m_tiledLayer = new Esri::ArcGISRuntime::ArcGISTiledLayer(tpkCache); + + m_basemap->baseLayers()->append(m_tiledLayer); + + m_map->setBasemap(m_basemap); + + Esri::ArcGISRuntime::GraphicsOverlayListModel * graphicsOverlayListModel = m_mapView->graphicsOverlays(); + Esri::ArcGISRuntime::GraphicsOverlay * graphicalOverlay = new Esri::ArcGISRuntime::GraphicsOverlay(); + + graphicalOverlay->setSelectionColor(Qt::magenta); + graphicsOverlayListModel->append(graphicalOverlay); + + m_mapView->setMap(m_map); + + + } // void initMap() + + + ~MapWindow() = default; + + void buildSymbol() + { + Esri::ArcGISRuntime::GraphicsOverlay * layer = new Esri::ArcGISRuntime::GraphicsOverlay(this); + m_mapView->graphicsOverlays()->append(layer); + + //layer->setRenderingMode(Esri::ArcGISRuntime::GraphicsRenderingMode::Static); + layer->setRenderingMode(Esri::ArcGISRuntime::GraphicsRenderingMode::Dynamic); + + Esri::ArcGISRuntime::PolygonBuilder builder( m_map->spatialReference() ); + + builder.addPoint( Esri::ArcGISRuntime::Point( 0, 0,m_map->spatialReference()) ); + builder.addPoint( Esri::ArcGISRuntime::Point( 0,5878900,m_map->spatialReference()) ); + builder.addPoint( Esri::ArcGISRuntime::Point(493200,5880900,m_map->spatialReference()) ); + builder.addPoint( Esri::ArcGISRuntime::Point(495200, 0,m_map->spatialReference()) ); + + Esri::ArcGISRuntime::Graphic * graphic = new Esri::ArcGISRuntime::Graphic(this); + Esri::ArcGISRuntime::SimpleFillSymbol * simpleFillSym = new Esri::ArcGISRuntime::SimpleFillSymbol(this); + + simpleFillSym->setColor( QColor("red") ); + simpleFillSym->setStyle( Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid ); + + graphic->setGeometry( builder.toPolygon() ); + + graphic->setSymbol(simpleFillSym); + + //add the graphic to the graphics overlay + layer->graphics()->append(graphic); + + } // void buildSymbol() + + + + void drawQGraphicPathItem() + { + QGraphicsScene * scene = m_mapView->scene(); + QGraphicsPathItem * pathItem = scene->addPath( QPainterPath() ); + + QColor drawColor("darkblue"); + + QColor fillColor("green"); + fillColor.setAlphaF( 0.5 ); + + pathItem -> setFlags( QGraphicsItem::ItemIgnoresTransformations ); + + pathItem -> setPen ( QPen(drawColor) ); + pathItem -> setBrush ( QBrush(fillColor) ); + pathItem -> setZValue( 100 ); + + QPainterPath painterPath; + + painterPath.moveTo(QPointF(200,200)); + painterPath.lineTo(QPointF(700,200)); + painterPath.lineTo(QPointF(450,700)); + painterPath.lineTo(QPointF(200,700)); + painterPath.closeSubpath(); + + pathItem -> setPath( painterPath ); + } // void drawQGraphicPathItem() + +}; // MapWindow + + +int main(int argc, char* argv[]) +{ + QApplication app(argc,argv); + + // ESRI : +#ifdef Q_OS_WIN + // Force usage of OpenGL ES through ANGLE on Windows + QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); +#endif + // : ESRI + + MapWindow * mapWin = new MapWindow(); + + int width = mapWin->frameGeometry().width(); + int height = mapWin->frameGeometry().height(); + + QScreen *screen = QGuiApplication::primaryScreen(); + int screenWidth = screen ? screen->size().width() : 1920; + int screenHeight = screen ? screen->size().height() : 1080; + + mapWin->setGeometry((screenWidth / 2) - (width / 2), (screenHeight / 2) - (height / 2), width, height); + + mapWin->show(); + + app.setQuitOnLastWindowClosed(true); + + int retval = app.exec(); + + delete mapWin; + + return retval; + +} // main diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/QGraphicsItem_Fill/QGraphicsItem_FillBug.pro b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/QGraphicsItem_Fill/QGraphicsItem_FillBug.pro new file mode 100755 index 000000000..414350b86 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/QGraphicsItem_Fill/QGraphicsItem_FillBug.pro @@ -0,0 +1,47 @@ +#------------------------------------------------- +# Copyright 2015 Esri. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#------------------------------------------------- + +mac { + cache() +} + + +CONFIG += c++11 esri_runtime_qt100_0_0 + +QT += core gui opengl xml network positioning sensors + +QMAKE_CXXFLAGS += -std=c++0x + + +# $$(ENVVAR) +# $(ENVVAR) +INCLUDEPATH += /XXX +LIBPATH += /XXX +LIBS += -lEsriCommonQt +LIBS += -lEsriRuntimeQt +LIBS += -lc++ + + + +win32:CONFIG += \ + embed_manifest_exe + +TARGET = QGraphicsItem_FillBug +TEMPLATE = app + +SOURCES += \ + QGraphicsItem_FillBug.cpp + +HEADERS += diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/RdeExample_Viewshed/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/RdeExample_Viewshed/CMakeLists.txt new file mode 100755 index 000000000..3a345c0ba --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/RdeExample_Viewshed/CMakeLists.txt @@ -0,0 +1,32 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +# EXAMPLE +SET(EXAMPLE_SRCS + main.cpp + Viewshed.cpp +) + +SET(EXAMPLE_HDRS + Viewshed.h +) + + +IF(MSVC) + + # Configure the template file + SET(VS_USER_FILE rdeexample.vcxproj.user) + configure_file( ../../SituationMap.vcxproj.user.in ${CMAKE_CURRENT_BINARY_DIR}/${VS_USER_FILE} @ONLY) + +ENDIF(MSVC) + +NGL_ADD_EXECUTABLE(WS rdeexample_viewshed ${EXAMPLE_SRCS} ${EXAMPLE_HDRS}) +NGL_TARGET_LINK_LIBRARIES(rdeexample_viewshed + NGLLogger + situationMap + ArcGIS::ArcGIS +) + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/RdeExample_Viewshed/Viewshed.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/RdeExample_Viewshed/Viewshed.cpp new file mode 100755 index 000000000..fc1350aaa --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/RdeExample_Viewshed/Viewshed.cpp @@ -0,0 +1,556 @@ +/** + * @RDE_HEADER@ + * Declaration/Definition + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "Viewshed.h" + + +// Qt +#include +#include +#include +#include +#include +#include + + +// Esri +#include +#include +#include +#include +#include +#include + + + +// Uncomment if needed +//#include +//#include +//#include +//#include +//#include +//#include + + +//#define USE_SAMPLE_SERVER + + +EsriRuntimeQt::SpatialReference sr(102100); + +mainwindow::mainwindow(QWidget *parent) +: QMainWindow(parent) +{ + m_map = new EsriRuntimeQt::Map(this); + + connect(m_map, SIGNAL(mapReady()), this, SLOT(onMapReady())); + + m_mapGraphicsView = EsriRuntimeQt::MapGraphicsView::create(m_map, this); + setCentralWidget(m_mapGraphicsView); + m_map->setWrapAroundEnabled(false); + + // create and add the tiled layer + EsriRuntimeQt::ArcGISTiledMapServiceLayer* mapServiceLayer = + new EsriRuntimeQt::ArcGISTiledMapServiceLayer(QLatin1String("http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"), this); + m_map->addLayer(mapServiceLayer); + + QString path = EsriRuntimeQt::ArcGISRuntime::installDirectory(); + path.append(QLatin1String("/sdk/samples/data")); + QDir dataDir(path); // using QDir to convert to correct file separator + QString pathSampleData = dataDir.path() + QDir::separator(); + qDebug() << " installDirectory='" << path << "'"; + + QString tiledBaseMapLayer = pathSampleData + QLatin1String("tpks/Topographic.tpk"); + qDebug() << " tiledBaseMapLayer='" << tiledBaseMapLayer << "'"; + +// m_tiledLayer = new EsriRuntimeQt::ArcGISLocalTiledLayer(tiledBaseMapLayer, this); +// m_tiledLayer->setName( QLatin1String("ArcGISLocalTiledLayer") ); +// m_map->addLayer(m_tiledLayer); + + + // Create Graphics Layer + m_graphicsLayer = new EsriRuntimeQt::GraphicsLayer(this); + m_graphicsLayer -> setName(QLatin1String("GraphicsLayer")); + + m_map->addLayer(m_graphicsLayer); + +// { +// // +// // Create red circle at maps origin +// // + +// EsriRuntimeQt::Point nullPoint(0, 0, m_map->spatialReference()); +// EsriRuntimeQt::SimpleMarkerSymbol redCircle(Qt::red, 20, EsriRuntimeQt::SimpleMarkerSymbolStyle::Circle); +// EsriRuntimeQt::Graphic* graphic1 = new EsriRuntimeQt::Graphic( nullPoint, redCircle); + +// qint64 gId = m_graphicsLayer->addGraphic(graphic1); + +// // +// // Get access to graphic and hide: -> Works fine :-) +// // +// EsriRuntimeQt::Graphic* graphicRetrieved = m_graphicsLayer->graphic(gId); + +// if (graphicRetrieved) +// { +// graphicRetrieved->setVisible(false); +// } else { +// qWarning() << LOGFUNCTION << "could NOT retrieve graphic" ; +// } +// } + + + QString url; +#ifdef USE_SAMPLE_SERVER + url = QLatin1String("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Elevation/ESRI_Elevation_World/GPServer/Viewshed"); +#else + url = QLatin1String("http://elevation.arcgis.com/arcgis/rest/services/Tools/Elevation/GPServer/Viewshed"); +#endif + + qDebug() << LOGFUNCTION << " url='" << url << "'"; + + + // create a Geoprocessor that points to the remote geoprocessing service. + EsriRuntimeQt::UserCredentials *ucEsri = new EsriRuntimeQt::UserCredentials; + ucEsri->setUserAccount("norbert.thoden", "RDEESRI7"); + m_geoprocessor = new EsriRuntimeQt::Geoprocessor(url, + ucEsri, + this); + + connect(m_geoprocessor, + SIGNAL(gpError(const EsriRuntimeQt::ServiceError&)), this, + SLOT(onGpError(const EsriRuntimeQt::ServiceError&))); + +#ifdef USE_SAMPLE_SERVER + connect(m_geoprocessor, + SIGNAL(gpExecuteComplete(const QList&)), this, + SLOT(onGpExecuteComplete(const QList&))); + +#else + connect(m_geoprocessor, + SIGNAL(gpSubmitJobComplete(const EsriRuntimeQt::GPJobResource&)), this, + SLOT(onGpSubmitJobComplete(const EsriRuntimeQt::GPJobResource&))); + // used: + connect(m_geoprocessor, + SIGNAL(gpJobStatusComplete(const EsriRuntimeQt::GPJobResource&)), this, + SLOT(onGpJobStatusComplete(const EsriRuntimeQt::GPJobResource&))); + + // used: + connect(m_geoprocessor, + SIGNAL(gpResultComplete(EsriRuntimeQt::GPParameter*)), this, + SLOT(onGpResultComplete(EsriRuntimeQt::GPParameter*))); + + // unused!? + connect(m_geoprocessor, + SIGNAL(gpServiceInfoComplete(EsriRuntimeQt::GPServiceInfo)), this, + SLOT(onGpServiceInfoComplete(EsriRuntimeQt::GPServiceInfo))); + + // unused!? + connect(m_geoprocessor, + SIGNAL(gpSubmitJobAndGetResultComplete(const EsriRuntimeQt::GPJobResource&, const QList&)), this, + SLOT(onGpSubmitJobAndGetResultComplete(const EsriRuntimeQt::GPJobResource&, const QList&))); + + // unused!? + connect(m_geoprocessor, + SIGNAL(statusUpdated(const EsriRuntimeQt::GPJobResource&, const QList&)), this, + SLOT(onStatusUpdated(const EsriRuntimeQt::GPJobResource&, const QList&))); + + // unused!? + connect(m_geoprocessor, + SIGNAL(gpTaskInfoComplete(EsriRuntimeQt::GPTaskInfo)), this, + SLOT(onGpTaskInfoComplete(const EsriRuntimeQt::GPTaskInfo&))); +#endif + + // connect signals + connect(m_map, SIGNAL(mapReady()), this, SLOT(onMapReady())); + connect(m_map, SIGNAL(mousePressRelease(QMouseEvent&)), this, SLOT(onMousePressRelease(QMouseEvent&))); + + +// // set up progress bar +// m_progressDialog = new QProgressDialog(); +// m_progressDialog->setBaseSize(280, 20); +// m_progressDialog->setRange(0, 0); +// m_progressDialog->setValue(0); +// m_progressDialog->setCancelButton(0); +// connect(m_mapGraphicsView->scene(), SIGNAL(sceneRectChanged(QRectF)), this, SLOT(onResizeProgressBar())); +// m_progressDialog->setLabelText(""); +// m_progressDialog->setAttribute(Qt::WA_NoBackground); +// m_progressDialog->setAutoFillBackground(false); +// m_progressDialog->setVisible(false); + +// WidgetPanel* widget = new WidgetPanel(); +// QLabel* instructionsLabel = new QLabel( +// QString("Click on the map to set the viewshed point. The viewshed will be computed ") + +// QString("from the specified buffer area around the viewshed point. ")); +// instructionsLabel->setWordWrap(true); +// QFont font; +// font.setPixelSize(11); +// instructionsLabel->setFont(font); +// instructionsLabel->setMargin(5); +// instructionsLabel->setObjectName("instructions"); +// instructionsLabel->setStyleSheet("QLabel#instructions { background: rgba(0,0,0, 192); color: #ddd; }"); +// instructionsLabel->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); +// +// QVBoxLayout* layout = new QVBoxLayout(); +// layout->setMargin(0); +//// layout->addWidget(instructionsLabel, Qt::AlignLeft); + +// QHBoxLayout* horizontalBox = new QHBoxLayout(); +// horizontalBox->setMargin(1); + +// QFont font; +// font.setPixelSize(12); +// QLabel* distanceLabel = new QLabel("Viewshed Buffer (miles):"); +// distanceLabel->setFixedSize(180, 25); +// distanceLabel->setFont(font); +// distanceLabel->setMargin(5); +// horizontalBox->addWidget(distanceLabel, Qt::AlignRight); + +// m_textDistanceLineEdit = new QLineEdit("9"); +// m_textDistanceLineEdit->setFixedSize(60, 25); +// m_textDistanceLineEdit->setFont(font); + +// horizontalBox->addWidget(m_textDistanceLineEdit, Qt::AlignLeft); +// layout->addLayout(horizontalBox); + +//// widget->setFixedSize(210, 105); +//// widget->setLayout(layout); + +// QGraphicsProxyWidget *proxy = m_mapGraphicsView->scene()->addWidget(widget); +// proxy->setPos(10, 10); +// proxy->setAcceptedMouseButtons(Qt::LeftButton); +// proxy->setFlag(QGraphicsItem::ItemIsFocusable, true); +// proxy->setFlag(QGraphicsItem::ItemIsSelectable, true); +// proxy->setFlag(QGraphicsItem::ItemAcceptsInputMethod, true); +// proxy->setOpacity(0.85); + +// m_proxyProgressBar = m_mapGraphicsView->scene()->addWidget(m_progressDialog); +// m_proxyProgressBar->setAcceptedMouseButtons(Qt::NoButton); +// m_proxyProgressBar->setFlag(QGraphicsItem::ItemIsSelectable, false); + + + + +// m_viewShed = new +// SituationMap::ViewshedBase( +// m_geoprocessor->url() +// , *m_graphicsLayer, +// this); + +// bool init = m_viewShed->init(); +// qDebug() << LOGFUNCTION << " init=" << init; + + +} /* mainwindow::mainwindow */ + + +mainwindow::~mainwindow() +{ +} + + +void mainwindow::onMapReady() +{ + qDebug() << LOGFUNCTION << " ready"; +// m_map->setExtent(EsriRuntimeQt::Envelope(-10000, -10000, 10000, 10000, sr)); + + m_map->setExtent(EsriRuntimeQt::Envelope(-13386516, 4516694, -13236394, 4616827, m_map->spatialReference())); + m_srMap = m_map->spatialReference(); +} + + +void mainwindow::onMousePressRelease(QMouseEvent &event) +{ + qDebug() << LOGFUNCTION; + + if (event.button() != Qt::LeftButton) + return; + + if (m_inProgress) + return; + + QPoint scenePosition = event.pos(); + +// // make sure the mouse didn't move - if so it was a pan so do nothing +// QGraphicsItem * graphicsItem = nullptr; +// graphicsItem = itemFromQGraphicsScene(m_mapGraphicsView->scene(), scenePosition); +// if (graphicsItem) +// return; // Clicked item box + + // remove viewshed from previous computation + m_graphicsLayer->removeAll(); + + int inputValue = 10; +// inputValue = m_textDistanceLineEdit->text().toInt(); + if (inputValue > 12 || inputValue < 1) + { + if (m_progressDialog) + m_progressDialog->setVisible(false); + QMessageBox::warning(this, "Invalid Distance", "Please enter an integer from 1 to 12."); + return; + } + + // the click point is the viewshed point + EsriRuntimeQt::Point viewshedPoint = m_map->toMapPoint(scenePosition.x(), scenePosition.y()); + + qDebug() << LOGFUNCTION<< " esriViewshedPoint.x=" << viewshedPoint.x() + << " ;y=" << viewshedPoint.y() + << " wkid=" << viewshedPoint.spatialReference().wkid() + << " m_inProgress=" << m_inProgress + ; + + + // make sure it is a point on the map + if (!EsriRuntimeQt::GeometryEngine::within(viewshedPoint, m_map->extent())) + return; + + m_inProgress = true; + + if (m_progressDialog) + { + m_progressDialog->setLabelText("Computing the viewshed..."); + m_progressDialog->setVisible(true); + } + + EsriRuntimeQt::Graphic* viewshedPointGraphic = new EsriRuntimeQt::Graphic(viewshedPoint, EsriRuntimeQt::SimpleMarkerSymbol(QColor("Black"), 14, EsriRuntimeQt::SimpleMarkerSymbolStyle::X)); + m_graphicsLayer->addGraphic(viewshedPointGraphic); + + // initialize the input parameters. refer to help link in the geoprocessing service URL. + m_geoprocessor->setProcessSR(m_srMap); + m_geoprocessor->setOutSR(m_srMap); + + QList gpInputParams; + +#ifdef USE_SAMPLE_SERVER + EsriRuntimeQt::GPFeatureRecordSetLayer* gpInputViewshedPoint = + new EsriRuntimeQt::GPFeatureRecordSetLayer("Input_Observation_Point", this); +#else + EsriRuntimeQt::GPFeatureRecordSetLayer* gpInputViewshedPoint = + new EsriRuntimeQt::GPFeatureRecordSetLayer("InputPoints", this); +#endif + gpInputViewshedPoint->setSpatialReference(m_srMap); + gpInputViewshedPoint->addGraphic(qobject_cast(viewshedPointGraphic->clone(this))); + + + +#ifdef USE_SAMPLE_SERVER + EsriRuntimeQt::GPLinearUnit* gpInputViewshedDistance = + new EsriRuntimeQt::GPLinearUnit("Viewshed_Distance", this); + gpInputViewshedDistance->setUnits("esriMiles"); + gpInputViewshedDistance->setDistance(inputValue); + + gpInputParams.append(gpInputViewshedPoint); + gpInputParams.append(gpInputViewshedDistance); + +#else + EsriRuntimeQt::GPDouble* gpInputMaxDistance = + new EsriRuntimeQt::GPDouble("MaximumDistance", this); + gpInputMaxDistance->setValue(inputValue); + + EsriRuntimeQt::GPString* gpInputMaxDistanceUnits = + new EsriRuntimeQt::GPString("MaximumDistanceUnits", this); + gpInputMaxDistanceUnits->setValue(QLatin1String("Miles")); + + EsriRuntimeQt::GPDouble* gpInputObserverHeight = + new EsriRuntimeQt::GPDouble("ObserverHeight", this); + gpInputObserverHeight->setValue(3000); // [Meter] + + EsriRuntimeQt::GPBoolean* gpInputGeneralize = + new EsriRuntimeQt::GPBoolean("GeneralizeViewshedPolygons", this); + gpInputGeneralize->setValue(false); + + + gpInputParams.append(gpInputViewshedPoint); + gpInputParams.append(gpInputMaxDistance); + gpInputParams.append(gpInputMaxDistanceUnits); + gpInputParams.append(gpInputObserverHeight); +// gpInputParams.append(gpInputGeneralize); +#endif + + // execute the geoprocessing request + + // ------------------------------------------------------------------------------------- + // Option 1 - execute sync + // Client execution will happen in this thread. It will be blocked on server. + // Client side result collection will happen in this thread. It will be blocked on server. + // + // This is ideal for jobs that take less processing time and whose result size is small. + // This will freeze the UI. + // ------------------------------------------------------------------------------------- + /*QList result = m_geoprocessor->execute(gpInputParams); + processResult(result);*/ + + // ------------------------------------------------------------------------------------- + // Option 2 - execute async + // Client execution will happen in a new thread. It will be blocked on server. + // Client side result collection will happen on that thread. It will be blocked on server. + // + // This is ideal for jobs that take relatively more processing time than option 1 + // and whose result size is small. + // ------------------------------------------------------------------------------------- +// m_geoprocessor->execute(gpInputParams); + + m_geoprocessor->setStatusPollingInterval(10); + +#ifdef USE_SAMPLE_SERVER + m_geoprocessor->execute( gpInputParams ); +#else + m_geoprocessor->submitJob(gpInputParams); +#endif + + + +} + +void mainwindow::onGpExecuteComplete(const QList &result) +{ + qDebug() << LOGFUNCTION << " result.size()=" << result.size(); + + for ( EsriRuntimeQt::GPParameter* outputParameter, result) + { + if (!outputParameter) + continue; + + EsriRuntimeQt::GPFeatureRecordSetLayer* gpLayer = qobject_cast(outputParameter); + if (gpLayer) + { + qDebug() << LOGFUNCTION << " gpLayer->graphics().size=" << gpLayer->graphics().size(); + // get all the graphics and add them to the graphics layer. + for ( EsriRuntimeQt::Graphic* resultGraphic, gpLayer->graphics()) + { + EsriRuntimeQt::Graphic* theGraphic = new EsriRuntimeQt::Graphic(resultGraphic->geometry(), EsriRuntimeQt::SimpleFillSymbol(QColor(255, 0, 0, 80), EsriRuntimeQt::SimpleFillSymbolStyle::Solid, EsriRuntimeQt::SimpleLineSymbol(QColor("Red"), 0.5))); + // add to the graphics layer + m_graphicsLayer->addGraphic(theGraphic); + } + } + + outputParameter->deleteLater(); + } + + if (m_progressDialog) + m_progressDialog->setVisible(false); + + m_inProgress = false; + +} + +void mainwindow::onGpError(const EsriRuntimeQt::ServiceError &error) +{ + if (m_progressDialog) + m_progressDialog->setVisible(false); + qWarning() << LOGFUNCTION << QString("Service Error, code: %1, message: %2, details: %3").arg(QString::number(error.code()), error.message(), error.details()); + + m_inProgress = false; +} + +void mainwindow::onGpSubmitJobComplete(const EsriRuntimeQt::GPJobResource &jobResource) +{ + qWarning() << "\n"; + qWarning() << LOGFUNCTION << " isEmpty=" << jobResource.isEmpty(); + qWarning() << LOGFUNCTION << " jobId='" << jobResource.jobId() << "'"; + qWarning() << LOGFUNCTION << " jobStatus='" << (int)jobResource.jobStatus() << "'"; + qWarning() << LOGFUNCTION << " inputParameters().size()='" << (int)jobResource.inputParameters().size() << "'"; + qWarning() << LOGFUNCTION << " outputParameters().size()='" << (int)jobResource.outputParameters().size() << "'"; + qWarning() << LOGFUNCTION << " messages().size()='" << (int)jobResource.messages().size() << "'"; + + + delete m_timer; + m_timer = new QTimer; + connect(m_timer, &QTimer::timeout, [=]() { + qWarning() << " jobStatus() ..."; + m_geoprocessor->jobStatus(); + + } ); + + qWarning() << "\n starting timer..."; + m_timer->start(1000); +} + +void mainwindow::onGpJobStatusComplete(const EsriRuntimeQt::GPJobResource &jobResource) +{ + qWarning() << "\n"; + qWarning() << LOGFUNCTION << " isEmpty=" << jobResource.isEmpty(); + qWarning() << LOGFUNCTION << " jobId='" << jobResource.jobId() << "'"; + qWarning() << LOGFUNCTION << " jobStatus='" << (int)jobResource.jobStatus() << "'"; + qWarning() << LOGFUNCTION << " inputParameters().size()='" << (int)jobResource.inputParameters().size() << "'"; + qWarning() << LOGFUNCTION << " outputParameters().size()='" << (int)jobResource.outputParameters().size() << "'"; + qWarning() << LOGFUNCTION << " messages().size()='" << (int)jobResource.messages().size() << "'"; + + if (jobResource.jobStatus() == EsriRuntimeQt::JobStatus::Succeeded) + { + qWarning() << " Succeeded :-)"; + + if (m_timer) + m_timer->stop(); + + m_geoprocessor->result(jobResource, + QLatin1String("OutputViewshed"), + true); // GetAsImageResult + } +} + +void mainwindow::onGpResultComplete(EsriRuntimeQt::GPParameter *outputParameter) +{ + qWarning() << "\n"; + qWarning() << LOGFUNCTION << " outputParameter=" << outputParameter; + + if (outputParameter) + { + EsriRuntimeQt::GPFeatureRecordSetLayer* gpLayer = qobject_cast(outputParameter); + if (gpLayer) + { + qDebug() << LOGFUNCTION << " gpLayer->graphics().size=" << gpLayer->graphics().size(); + // get all the graphics and add them to the graphics layer. + for ( EsriRuntimeQt::Graphic* resultGraphic, gpLayer->graphics()) + { + EsriRuntimeQt::Graphic* theGraphic = + new EsriRuntimeQt::Graphic(resultGraphic->geometry(), + EsriRuntimeQt::SimpleFillSymbol(QColor(0, 255, 0, 80), EsriRuntimeQt::SimpleFillSymbolStyle::Solid, + EsriRuntimeQt::SimpleLineSymbol(QColor("Red"), 0.5))); + // add to the graphics layer + m_graphicsLayer->addGraphic(theGraphic); + } + } + outputParameter->deleteLater(); + } +} + +void mainwindow::onGpServiceInfoComplete(EsriRuntimeQt::GPServiceInfo serviceInfo) +{ + qWarning() << "\n"; + qWarning() << LOGFUNCTION ; +} + +void mainwindow::onGpSubmitJobAndGetResultComplete(const EsriRuntimeQt::GPJobResource &, const QList &results) +{ + qWarning() << "\n"; + qWarning() << LOGFUNCTION ; +} + +void mainwindow::onStatusUpdated(const EsriRuntimeQt::GPJobResource &jobResource, const QList &msgList) +{ + qWarning() << "\n"; + qWarning() << LOGFUNCTION << " isEmpty=" << jobResource.isEmpty(); + qWarning() << LOGFUNCTION << " jobId='" << jobResource.jobId() << "'"; + qWarning() << LOGFUNCTION << " jobStatus='" << (int)jobResource.jobStatus() << "'"; + qWarning() << LOGFUNCTION << " inputParameters().size()='" << (int)jobResource.inputParameters().size() << "'"; + qWarning() << LOGFUNCTION << " outputParameters().size()='" << (int)jobResource.outputParameters().size() << "'"; + qWarning() << LOGFUNCTION << " messages().size()='" << (int)jobResource.messages().size() << "'"; + + qWarning() << LOGFUNCTION << " 2 msgList().size()='" << msgList.size() << "'"; + + m_geoprocessor->jobStatus(); +} + +void mainwindow::onGpTaskInfoComplete(const EsriRuntimeQt::GPTaskInfo &) +{ + qWarning() << "\n"; + qWarning() << LOGFUNCTION ; +} + + + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/RdeExample_Viewshed/Viewshed.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/RdeExample_Viewshed/Viewshed.h new file mode 100755 index 000000000..f3449dd19 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/RdeExample_Viewshed/Viewshed.h @@ -0,0 +1,87 @@ +/** + * @RDE_HEADER@ + * Declaration/Definition + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +namespace EsriRuntimeQt +{ +class MapGraphicsView; +} + +// Esri +#include + +//Uncommented layer(s) needed +#include +#include +#include +#include + +#include +#include + + +#include + +//#include + + + +class mainwindow : public QMainWindow +{ + Q_OBJECT +public: + mainwindow (QWidget *parent = 0); + ~mainwindow (); + +public slots: + void onMapReady(); + void onMousePressRelease(QMouseEvent& event); + + void onGpExecuteComplete(const QList& result); + void onGpError(const EsriRuntimeQt::ServiceError& error); + + void onGpSubmitJobComplete(const EsriRuntimeQt::GPJobResource& jobResource); + void onGpJobStatusComplete(const EsriRuntimeQt::GPJobResource&); + + void onGpResultComplete(EsriRuntimeQt::GPParameter* outputParameter); + void onGpServiceInfoComplete(EsriRuntimeQt::GPServiceInfo serviceInfo); + + void onGpSubmitJobAndGetResultComplete(const EsriRuntimeQt::GPJobResource&, const QList& results); + + + void onStatusUpdated(const EsriRuntimeQt::GPJobResource& jobResource, const QList &msgList); + + void onGpTaskInfoComplete(const EsriRuntimeQt::GPTaskInfo&); + + +private: + EsriRuntimeQt::Map *m_map = nullptr; + EsriRuntimeQt::MapGraphicsView *m_mapGraphicsView = nullptr; + EsriRuntimeQt::ArcGISTiledMapServiceLayer* m_tiledServiceLayer; + EsriRuntimeQt::ArcGISLocalTiledLayer *m_tiledLayer = nullptr; + EsriRuntimeQt::GraphicsLayer *m_graphicsLayer = nullptr; + + + EsriRuntimeQt::Geoprocessor *m_geoprocessor = nullptr; + EsriRuntimeQt::SpatialReference m_srMap; + QTimer *m_timer = nullptr; + + QProgressDialog *m_progressDialog = nullptr; + QLineEdit *m_textDistanceLineEdit = nullptr; + QGraphicsProxyWidget *m_proxyProgressBar = nullptr; + + bool m_inProgress = false; + + +// SituationMap::ViewshedBase *m_viewShed = nullptr; + +private slots: + + +}; + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/RdeExample_Viewshed/main.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/RdeExample_Viewshed/main.cpp new file mode 100755 index 000000000..0d45a341b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/RdeExample_Viewshed/main.cpp @@ -0,0 +1,92 @@ +/** + * @RDE_HEADER@ + * Declaration/Definition + * $Id$ + * $HeadURL$ + * $Author$ + */ +#include "Viewshed.h" + +#include + + +// Esri +#include +#include + + +// Qt +#include +#include +#include +#include + + + +int main (int argc, char* argv[]) +{ + QApplication app ( argc, argv ); +#ifdef Q_OS_WIN + QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); +#endif + + +#define USEPROXY +#ifdef USEPROXY + EsriRuntimeQt::ProxySetup::setupProxy("192.168.133.178" // proxy @ mike + , 8080 + , QNetworkProxy::HttpProxy); +#endif + + +#define DO_LIC +#ifdef DO_LIC + // ESRI-Account: + QLatin1String clientId("8UJNp7vsRWT4l9lc"); // 8UJNp7vsRWT4l9lc -> taken from ESRI-Account + EsriRuntimeQt::ArcGISRuntime::setClientId(clientId); + +#define OLD_LICENSE +#ifdef OLD_LICENSE + //alter methode (langer String): + QLatin1String licenseCode("runtimestandard,101,rud077222135,none,FAYRMD1AJ42HXH46C167"); +#else + // Neue Methode (kurzer String) + QLatin1String licenseCode("RUD077222135"); // RUD077222135 -> taken from mail "ESRI order 2874794" +#endif + + + EsriRuntimeQt::LicenseStatus status = + EsriRuntimeQt::ArcGISRuntime::License::setLicense(licenseCode); + + qDebug() << LOGFUNCTION + << " clientId='" << clientId << "'" + << " licenseCode='" << licenseCode << "'" + << " LicenseStatus=" << static_cast(status) + << " (LicenseStatusVALID=" << static_cast(EsriRuntimeQt::LicenseStatus::Valid) << ")" + ; + + // Use this code to check for licensing errors + if (EsriRuntimeQt::ArcGISRuntime::initialize() == false) + { + // QMessageBox msgBox; + // msgBox.setText(EsriRuntimeQt::ArcGISRuntime::error()); + // msgBox.exec(); + // app.quit(); + return 1; + } +#endif // DO_LIC + + // Init the logger + NGL::NGLLoggerBootstrap::createNGLRootLogger("log4cxxSituationMapUnitTest.props", true); + + + + mainwindow applicationWindow; + applicationWindow.setMinimumWidth(800); + applicationWindow.setMinimumHeight(600); + + applicationWindow.show(); + + return app.exec(); +} + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/CMakeLists.txt new file mode 100755 index 000000000..7d7c7cb91 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/CMakeLists.txt @@ -0,0 +1,69 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +# Situation Map Test Application +SET(EXE_SRCS + main.cpp + mainwindow.cpp + nglsettings.cpp + + log4cxx_sitMapTestApp.props + mapsettings.ini +) + +SET(EXE_HDRS + mainwindow.h + nglsettings.h +) + +SET(EXE_UICS + ui/mainwindow.ui +) + +NGL_WRAP_UI(EXE_SRCS ${EXE_UICS}) + +SET(SETTINGS_FILE mapsettings.ini) +SET(LOG4CXX_PROPS_FILE log4cxx_sitMapTestApp.props) + + +NGL_ADD_EXECUTABLE(WS NO_TRANSLATION sitMapTestApp ${EXE_SRCS} ${EXE_HDRS} ${EXE_UICS}) +NGL_TARGET_LINK_LIBRARIES( + sitMapTestApp + NGLLogger + nglapplication + nglwidgets + nglsettings + situationMap + situationMapObjects + situationMapFunctionDialog + ArcGIS::ArcGIS + LtsCoordTrans +) + +IF(TARGET sitMapTestApp) + ADD_CUSTOM_COMMAND( TARGET sitMapTestApp POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${LOG4CXX_PROPS_FILE} $/${LOG4CXX_PROPS_FILE} + COMMAND ${CMAKE_COMMAND} + ARGS -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${SETTINGS_FILE} $/${SETTINGS_FILE} + ) + IF (UNIX) + ADD_CUSTOM_COMMAND( TARGET sitMapTestApp POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${SETTINGS_FILE} $ENV{HOME}/${SETTINGS_FILE} + ) + ENDIF() +ENDIF() + +IF(MSVC) + # Configure the template file + SET(VS_USER_FILE sitMapTestApp.vcxproj.user) + configure_file( ../../SituationMap.vcxproj.user.in ${CMAKE_CURRENT_BINARY_DIR}/${VS_USER_FILE} @ONLY) +ENDIF(MSVC) + +NGL_INSTALL_TARGET_DEVEL(sitMapTestApp) +NGL_INSTALL(FILES WS ${LOG4CXX_PROPS_FILE} DESTINATION "bin" COMPONENT Devel) +NGL_INSTALL(FILES WS ${SETTINGS_FILE} DESTINATION "." COMPONENT Devel) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/log4cxx_sitMapTestApp.props b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/log4cxx_sitMapTestApp.props new file mode 100755 index 000000000..01ba98a75 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/log4cxx_sitMapTestApp.props @@ -0,0 +1,165 @@ +log4j.reset=true +log4j.rootLogger=WARN, file + +# SituationMapTestApp +########################################################### +log4j.logger.SituationMap.SitMapTestApp=DEBUG + +# AdvancedSymbology (AdvSymbology) +########################################################### +log4j.logger.AdvSymbology=FATAL +log4j.logger.AdvSymbology.SymbolDictionary=FATAL +log4j.logger.AdvSymbology.SymbolDictionaryImpl=FATAL +log4j.logger.AdvSymbology.Sidc2525Delta=FATAL + +# SituationMap +########################################################### +log4j.logger.SituationMap=FATAL +log4j.logger.SituationMap.MapImpl=FATAL +log4j.logger.SituationMap.MapSettings=FATAL +log4j.logger.SituationMap.MapInitializer=FATAL +log4j.logger.SituationMap.LayerFactoryImpl=FATAL +log4j.logger.SituationMap.LayerControl=INFO +log4j.logger.SituationMap.CoordTranslationFunctorImpl=FATAL +log4j.logger.SituationMap.CoordTranslationThread=FATAL +log4j.logger.SituationMap.PositionsTranslated=FATAL +log4j.logger.SituationMap.GridImpl=FATAL +log4j.logger.SituationMap.ObjectFactory=FATAL +log4j.logger.SituationMap.IdGenerator=FATAL +log4j.logger.SituationMap.ObjectContainer=FATAL +log4j.logger.SituationMap.ObjectContainerCTC=FATAL +log4j.logger.SituationMap.OverlayHandlerImpl=FATAL +log4j.logger.SituationMap.ViewshedImpl=FATAL +log4j.logger.SituationMap.ViewshedBase=FATAL + +log4j.logger.SituationMap.AbstractMapObjectImpl=FATAL +log4j.logger.SituationMap.AbstractEffectSimulationImpl=FATAL +log4j.logger.SituationMap.OriginTranslated=FATAL +log4j.logger.SituationMap.PositionsTranslated=FATAL +log4j.logger.SituationMap.AbstractMultiPointMapObjectImpl=FATAL +log4j.logger.SituationMap.AbstractSinglePointMapObjectImpl=FATAL +log4j.logger.SituationMap.BasicPathObjectImpl=FATAL +log4j.logger.SituationMap.IndicationContainer=INFO + +log4j.logger.SituationMap.PolygonImpl=FATAL +log4j.logger.SituationMap.PolylineImpl=FATAL +log4j.logger.SituationMap.TextImpl=FATAL + +log4j.logger.SituationMap.AreaFireImpl=FATAL +log4j.logger.SituationMap.BiologicalAgentImpl=FATAL +log4j.logger.SituationMap.BuildingOutlineImpl=FATAL +log4j.logger.SituationMap.ChemicalAgentImpl=FATAL +log4j.logger.SituationMap.DangerAreaImpl=FATAL +log4j.logger.SituationMap.FunctionalAreaImpl=FATAL +log4j.logger.SituationMap.HitLineImpl=FATAL +log4j.logger.SituationMap.MapObjectIndicatorImpl=FATAL +log4j.logger.SituationMap.MineImpl=FATAL +log4j.logger.SituationMap.MineFieldImpl=FATAL +log4j.logger.SituationMap.NuclearAgentImpl=FATAL +log4j.logger.SituationMap.ParticipantImpl=FATAL +log4j.logger.SituationMap.SingleFireImpl=FATAL + +log4j.logger.SituationMap.AbstractGraphicalObject=FATAL +log4j.logger.SituationMap.GraphicalObjectEllipse=FATAL +log4j.logger.SituationMap.GraphicalObjectPath=FATAL +log4j.logger.SituationMap.GraphicalObjectMultiPath=FATAL +log4j.logger.SituationMap.GraphicalObjectPictureMarker=FATAL +log4j.logger.SituationMap.GraphicalObjectRange=FATAL +log4j.logger.SituationMap.GraphicalObjectText=FATAL +log4j.logger.SituationMap.TacticalAppearance=FATAL +log4j.logger.SituationMap.MessageBase=FATAL + +log4j.logger.SituationMap.EventDispatcher=FATAL +log4j.logger.SituationMap.Rubberband=FATAL +log4j.logger.SituationMap.RubberbandRectangle=FATAL +log4j.logger.SituationMap.RubberbandDrawablesAndHandles=FATAL +log4j.logger.SituationMap.RubberbandFrameHandle=FATAL +log4j.logger.SituationMap.RubberbandPointHandle=FATAL +log4j.logger.SituationMap.RubberbandStateSelect=FATAL + +# SituationMap-SideBar +########################################################### +log4j.logger.SituationMap.SideBar=FATAL +log4j.logger.SituationMap.SideBarItem=FATAL +log4j.logger.SituationMap.SideBarModel=FATAL + + +# PlanEditor +########################################################### +log4j.logger.SituationMap.PlanEditor=FATAL +log4j.logger.SituationMap.PlanRenderer=FATAL + + + +# Appnd_File +########################################################### +log4j.appender.file=org.apache.log4j.RollingFileAppender +log4j.appender.file.MaxFileSize=1MB +log4j.appender.file.MaxBackupIndex=9 +log4j.appender.file.file=${LOG4QT_LOGDIR}/${LOG4QT_USER}_SituationMapTestApp.log +log4j.appender.file.layout=org.apache.log4j.PatternLayout +log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n + + +# Appnd_Console +########################################################### +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern= %d %-5p %c{1} |#| %m (%-.30F:%L) %n + +#log4j.appender.stdout.layout.ConversionPattern= %d %-5p %-30c |#| %m (%-.30F:%L) %n + + +# Appnd_Syslog +########################################################### +log4j.appender.syslog=org.apache.log4j.SyslogAppender +log4j.appender.syslog.Threshold=ERROR +log4j.appender.syslog.SysLogHost=localhost +log4j.appender.syslog.Facility=LOCAL7 +log4j.appender.syslog.layout=org.apache.log4j.PatternLayout +log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m +log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true +log4j.appender.syslog.LevelRangeFilter.LevelMin=FATAL +log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL + + +########################################################### +########################################################### +########################################################### +# 1) Available Levels in LOG4CXX Version 0.10.0: +# TRACE, +# DEBUG, +# INFO, +# WARN, +# ERROR +# FATAL +# +# d = date/time +# p = priority (Level) +# c = category/channel +# m = message +# F = file +# L = line +# n = platform dependent line separator character or characters. +# .... +# +# Examples for console: +# a) +# %d %p %c #%m# (%.50F:%L)%n +# date/time +# priority +# category/channel +# #message# +# (Maximal 50Character des Filename von rechts:Line) +# line separator +# +# b) +# %d (%.15F:%L) %-5p %c |#| %m%n +# date/time +# (Maximal 15Character des Filename von rechts:Line) +# priority, Right pad with spaces if the priority name is less than 5 characters long. +# category/channel +# message +# line separator +# + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/main.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/main.cpp new file mode 100755 index 000000000..e3a6930f6 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/main.cpp @@ -0,0 +1,67 @@ +/** + * @RDE_HEADER@ + * SquishTest + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include +#include + +#include "mainwindow.h" + +#include + +// ------------------------------------------------------------------- +int main (int argc, char* argv[]) +{ + NGLApplication app ( argc, argv ); + + // under windows for ArcGIS 10.2.6 and higher +#ifdef Q_OS_WIN + QCoreApplication::setAttribute( Qt::AA_UseOpenGLES ); +#endif + + app.createLogger("sitMapTestApp", "log4cxx_sitMapTestApp.props"); + TRACE_LOG(app.baseLogger()) << LOGFUNCTION << " START"; + + STARTUP_LOG(app.baseLogger()); + + app.setOrganizationName(QLatin1String("Rheinmetall Defence Electronics")); + app.setApplicationName(QLatin1String("Situation Map Test Application")); + + MainWindow *mw = new MainWindow(); +/* + QDesktopWidget *desktopWidget = QApplication::desktop(); + QRect geom; + +// DEBUG_LOG(app.baseLogger()) << LOGFUNCTION << " desktopWidget->screenCount() = " << desktopWidget->screenCount(); + + if (desktopWidget->screenCount() > 1) + geom = desktopWidget->screenGeometry(1); + else + geom = desktopWidget->screenGeometry(); + + int width = mw->frameGeometry().width(); + int height = mw->frameGeometry().height(); + int screenWidth = geom.width(); + int screenHeight = geom.height(); + int screenWidth = 1024; + int screenHeight = 768; + mw->setGeometry((screenWidth/2)-(width/2),(screenHeight/2)-(height/2),width,height); +*/ + mw->setGeometry(0,0,1024,768); +// mw->setWindowState(Qt::WindowMaximized); + mw->show(); + + app.setQuitOnLastWindowClosed(true); + + int retval = app.exec(); + + delete mw; + + SHUTDOWN_LOG(app.baseLogger()); + + return retval; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/mainwindow.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/mainwindow.cpp new file mode 100755 index 000000000..c48572d10 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/mainwindow.cpp @@ -0,0 +1,2702 @@ +/** + * @RDE_HEADER@ + * + * MainWindow for SitMapTestApp + * + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "mainwindow.h" +#include "nglsettings.h" +#include "ui_mainwindow.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +//TODO: use templates + + +//static LtsCoordTrans::UtmCoordinate s_basePoint; + + +//---------------------------------------------------------------------------------------------------------- +MainWindow::MainWindow(QWidget *parent) + : NGLMainWindow(parent) + , m_ui(new Ui::widgetMain) + , m_logger(SituationMap::getLogger("SitMapTestApp")) +{ + m_ui->setupUi(this); + + connect(m_ui->actionExit, SIGNAL(triggered()), this, SLOT(onClose())); + + // map slots + connect(m_ui->actionLoadMap, SIGNAL(triggered()), this, SLOT(createMap())); + connect(m_ui->actionRemoveMap, SIGNAL(triggered()), this, SLOT(destroyMap())); + + // map objects + connect(m_ui->actionCreateBuildings, SIGNAL(triggered()), this, SLOT(createBuildings())); + connect(m_ui->actionRemoveBuildings, SIGNAL(triggered()), this, SLOT(removeBuildings())); + connect(m_ui->actionUpdateBuildings, SIGNAL(triggered()), this, SLOT(updateMapObjects())); + connect(m_ui->actionHideBuildings, SIGNAL(triggered()), this, SLOT(hideBuildings())); + + connect(m_ui->actionCreateDangerAreas, SIGNAL(triggered()), this, SLOT(createDangerAreas())); + connect(m_ui->actionRemoveDangerAreas, SIGNAL(triggered()), this, SLOT(removeDangerAreas())); + connect(m_ui->actionUpdateDangerAreas, SIGNAL(triggered()), this, SLOT(updateMapObjects())); + connect(m_ui->actionHideDangerAreas, SIGNAL(triggered()), this, SLOT(hideDangerAreas())); + + connect(m_ui->actionCreateFireAreas, SIGNAL(triggered()), this, SLOT(createFireAreas())); + connect(m_ui->actionRemoveFireAreas, SIGNAL(triggered()), this, SLOT(removeFireAreas())); + connect(m_ui->actionUpdateFireAreas, SIGNAL(triggered()), this, SLOT(updateMapObjects())); + connect(m_ui->actionHideFireAreas, SIGNAL(triggered()), this, SLOT(hideFireAreas())); + + connect(m_ui->actionCreateBAgents, SIGNAL(triggered()), this, SLOT(createBAgents())); + connect(m_ui->actionRemoveBAgents, SIGNAL(triggered()), this, SLOT(removeBAgents())); + connect(m_ui->actionUpdateBAgents, SIGNAL(triggered()), this, SLOT(updateMapObjects())); + connect(m_ui->actionHideBAgents, SIGNAL(triggered()), this, SLOT(hideBAgents())); + + connect(m_ui->actionCreateHitLines, SIGNAL(triggered()), this, SLOT(createHitLines())); + connect(m_ui->actionRemoveHitLines, SIGNAL(triggered()), this, SLOT(removeHitLines())); + connect(m_ui->actionUpdateHitLines, SIGNAL(triggered()), this, SLOT(updateMapObjects())); + connect(m_ui->actionHideHitLines, SIGNAL(triggered()), this, SLOT(hideHitLines())); + + //Draw slots + connect(m_ui->actionPointCluster, SIGNAL(triggered()), this, SLOT(rubberbandPointCluster())); + connect(m_ui->actionMultiPoint, SIGNAL(triggered()), this, SLOT(rubberbandMultiPoint())); + connect(m_ui->actionFilledMultiPoint, SIGNAL(triggered()), this, SLOT(rubberbandFilledMultiPoint())); + connect(m_ui->actionSmoothLine, SIGNAL(triggered()), this, SLOT(rubberbandSmoothLine())); + connect(m_ui->actionFilledSmoothLine, SIGNAL(triggered()), this, SLOT(rubberbandFilledSmoothLine())); + connect(m_ui->actionRectangle, SIGNAL(triggered()), this, SLOT(rubberbandRectangle())); + connect(m_ui->actionSquare, SIGNAL(triggered()), this, SLOT(rubberbandSquare())); + connect(m_ui->actionEllipse, SIGNAL(triggered()), this, SLOT(rubberbandEllipse())); + connect(m_ui->actionArrow, SIGNAL(triggered()), this, SLOT(rubberbandArrow())); + connect(m_ui->actionCorridorBoundary, SIGNAL(triggered()), this, SLOT(rubberbandCorridorBoundary())); + connect(m_ui->actionCorridorCenterline, SIGNAL(triggered()), this, SLOT(rubberbandCorridorCenterline())); + + // Create slots + connect(m_ui->actionCreatePointCluster, SIGNAL(triggered()), this, SLOT(rubberbandCreatePointClusters())); + connect(m_ui->actionCreateMultiPoint, SIGNAL(triggered()), this, SLOT(rubberbandCreateMultiPoints())); + connect(m_ui->actionCreateFilledMultiPoint, SIGNAL(triggered()), this, SLOT(rubberbandCreateFilledMultiPoints())); + connect(m_ui->actionCreateSmoothLine, SIGNAL(triggered()), this, SLOT(rubberbandCreateSmoothLines())); + connect(m_ui->actionCreateFilledSmoothLine, SIGNAL(triggered()), this, SLOT(rubberbandCreateFilledSmoothLines())); + connect(m_ui->actionCreateRectangle, SIGNAL(triggered()), this, SLOT(rubberbandCreateRectangles())); + connect(m_ui->actionCreateSquare, SIGNAL(triggered()), this, SLOT(rubberbandCreateSquares())); + connect(m_ui->actionCreateEllipse, SIGNAL(triggered()), this, SLOT(rubberbandCreateEllipses())); + connect(m_ui->actionCreateArrow, SIGNAL(triggered()), this, SLOT(rubberbandCreateArrows())); + connect(m_ui->actionCreateCorridorBoundary, SIGNAL(triggered()), this, SLOT(rubberbandCreateCorridorBoundaries())); + connect(m_ui->actionCreateCorridorCenterline, SIGNAL(triggered()), this, SLOT(rubberbandCreateCorridorCenterlines())); + + init(); + +} + +//---------------------------------------------------------------------------------------------------------- +MainWindow::~MainWindow() +{ + clearMapObjects(); + + delete m_ui; + delete m_timer; +} + +//---------------------------------------------------------------------------------------------------------- +void MainWindow::init() +{ + m_scale = MAP::Settings::mapScale(); + INFO_LOG(m_logger) << LOGFUNCTION << " MAP SCALE = " << QString::number(m_scale,'f',6); + + s_basePoint = LtsCoordTrans::UtmCoordinate::fromMgrs(MAP::Settings::basePoint()); + m_objectBasePoint = QPointF(s_basePoint.x,s_basePoint.y); + INFO_LOG(m_logger) << LOGFUNCTION << " MAP BASEPOINT = " << MAP::Settings::basePoint(); + + m_wkid = MAP::Settings::mapWkid(); + INFO_LOG(m_logger) << LOGFUNCTION << " MAP WKID = " << QString::number(m_wkid,'f',6); + + m_timer = new QTimer(this); +} + +//---------------------------------------------------------------------------------------------------------- +void MainWindow::onClose() +{ + this->close(); +} + +//---------------------------------------------------------------------------------------------------------- +void MainWindow::destroyMap() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " REMOVE MAP "; + + m_ui->gridLayout->removeWidget(m_map); + + clearMapObjects(); + + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +//---------------------------------------------------------------------------------------------------------- +void MainWindow::clearMapObjects() +{ + m_building.reset(); + m_buildingMap.clear(); + + m_dangerArea.reset(); + m_dangerAreaMap.clear(); + + m_fireArea.reset(); + m_fireAreaMap.clear(); + + m_hitLine.reset(); + m_hitLineMap.clear(); + + m_bAgent.reset(); + m_bAgentMap.clear(); + + delete m_map; + m_map = nullptr; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::createMap() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " CREATE MAP "; + + // 0. License: + if (!SituationMap::Map::applyLicense()) + FATAL_LOG(m_logger) << LOGFUNCTION << " applyLicense failed"; + + // 1. Map + m_map = SituationMap::Map::create(this); + + connect(m_map, &SituationMap::Map::mapReady, this, &MainWindow::onMapReady); + connect(m_map, &SituationMap::Map::mapObjectsClicked, this, &MainWindow::onMapObjectClicked); + connect(m_map, &SituationMap::Map::extentChanged, this, &MainWindow::onExtentChanged); + connect(m_map, &SituationMap::Map::rectChanged, this, &MainWindow::onExtentChanged); + + // 2. SymbologyType + AdvancedSymbology::SymbologyType symType = AdvancedSymbology::SymbologyType::MIL2525D; //default + fromString(symType, MAP::Settings::mapSymbologyType()); + m_map->setSymbologyType(symType); + + // 3. init (choose 2d or 3D) + m_map->init(SituationMap::Mode::Mode_2D); + + // 4. Basemaps + QString mapURL = MAP::Settings::mapURL(); + m_map->layerFactory().addBasemap(QLatin1Literal("DummyLayerName"), + QUrl(mapURL), + SituationMap::BasemapType::Aerial); + DEBUG_LOG(m_logger) << LOGFUNCTION << " MAP created with SymbologyType = " << toString(symType); + DEBUG_LOG(m_logger) << LOGFUNCTION << " MAP URL = " << mapURL; + + // 5. load + m_map->load(); + + // 6. initializeOperationalLayers + m_map->layerFactory().initializeOperationalLayers(QStringList() << QLatin1String("AES") << QLatin1String("Participants")); + + // 7. Grid + m_map->grid().setGridType(MAP::Settings::getGridType(MAP::Settings::mapGridType())); + m_map->grid().setVisible(MAP::Settings::mapGridVisible()); + + // 8. Initialize objectFactory + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::MapObjectIndicator), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Cube), new SituationMap::GenericObjectFactory() ); + + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Polygon), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Polyline), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Text), new SituationMap::GenericObjectFactory() ); + + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Participant), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::AreaFire), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::BiologicalAgent), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::ChemicalAgent), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::BuildingOutline), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::DangerArea), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::GeoImage), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::FunctionalArea), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::HitLine), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Mine), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::MineField), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::NuclearAgent), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::SingleFire), new SituationMap::GenericObjectFactory() ); + + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::SidcSymbol), new SituationMap::GenericObjectFactory() ); + + // 9: CoordTransFunctor + m_map->initCoordTranslationFunctor(); + + // 10. extent, rotation: set later in mapReady + + // 11. Add widget to main grid layout + m_ui->gridLayout->addWidget(m_map, 1, 0); + + // 12. Create FunctionDialog: + SituationMap::FunctionDialog::addButtonToMap(m_map, MAP::Settings::mapFunctionDialogVisible()); + + } + else + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " UPDATE MAP"; + m_map->update(); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::onMapObjectClicked(const SituationMap::Map::RefPointerMapObjectList &mapObjList) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + for (auto mapObj : mapObjList) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " id=" << mapObj->uniqueMapId() + << " object-debugInfo=" << mapObj->debugInfo() + << " object-identifier=" << mapObj->identifier(); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::onMapReady() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + +// QString centerPoint = MAP::Settings::centerPointMgrs(); +// m_map->setCenterPoint(LtsCoordTrans::geographicFromMgrs(centerPoint)); // QPointF(92750, 80525) -> Rennplatz - Bremen + + m_map->setScale(m_scale); + + m_map->setRotation(MAP::Settings::mapRotationDegree()); + +// m_map->setExtent(MAP::Settings::XMin(), MAP::Settings::YMin(), MAP::Settings::XMax(), MAP::Settings::YMax()); + + m_map->update(); + + INFO_LOG(m_logger) << LOGFUNCTION << " MAP READY"; + + INFO_LOG(m_logger) << LOGFUNCTION << " MAP PIXEL SIZE = " << m_map->pixelSize(); + INFO_LOG(m_logger) << LOGFUNCTION << " MAP SCALE = " << m_map->scale(); + + INFO_LOG(m_logger) << LOGFUNCTION << " MAP Width = " << m_map->width(); + INFO_LOG(m_logger) << LOGFUNCTION << " MAP Height = " << m_map->height(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::onExtentChanged() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + TRACE_LOG(m_logger) << LOGFUNCTION << " MAP EXTENT CHANGED "; + + TRACE_LOG(m_logger) << LOGFUNCTION << " MAP PIXEL SIZE = " << m_map->pixelSize(); + TRACE_LOG(m_logger) << LOGFUNCTION << " MAP SCALE = " << m_map->scale(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " MAP Width = " << m_map->width(); + TRACE_LOG(m_logger) << LOGFUNCTION << " MAP Height = " << m_map->height(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::setVersionView() +{ + //TODO: +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::createBuildings() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + this->createBuilding(1); + this->createBuilding(2); + //TODO: more buildings + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::createBuilding(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + m_building = m_buildingMap[objId]; + if (m_building) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " Building already exists " << objId; + return; + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " Creating building " << objId; + m_building = m_map->objectFactory().create( QLatin1String("AES"), QLatin1String("BuildingOutlineId")); + + if (!m_building) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No building object "; + return; + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " Building Identifier: " << MAP::Settings::objectIdentifier(QLatin1String("Building"),objId); + + m_building->setIdentifier(MAP::Settings::objectIdentifier(QLatin1String("Building"),objId)); + m_building->setVisibility(MAP::Settings::getObjectBoolAttribute(QLatin1String("Building"),QLatin1String("Visibility"),objId,true)); + m_building->setVisibility(SituationMap::ObjectAppearance::Technical, MAP::Settings::getObjectBoolAttribute(QLatin1String("Building"),QLatin1String("VisibilityTech"),objId,true)); + m_building->setVisibility(SituationMap::ObjectAppearance::Tactical, MAP::Settings::getObjectBoolAttribute(QLatin1String("Building"),QLatin1String("VisibilityTact"),objId,true)); + + m_building->rTextStyle().setTextColor(QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("Building"),QLatin1String("TextColor"),objId,QLatin1String("magenta")))); + m_building->rTextStyle().setSize(MAP::Settings::getObjectIntAttribute(QLatin1String("Building"),QLatin1String("TextSize"),objId,30)); + m_building->rTextStyle().setAngle(MAP::Settings::getObjectIntAttribute(QLatin1String("Building"),QLatin1String("TextAngle"),objId,0)); + m_building->rTextStyle().setAntiAliased(MAP::Settings::getObjectBoolAttribute(QLatin1String("Building"),QLatin1String("TextItalic"),objId,true)); + m_building->rTextStyle().setItalic(MAP::Settings::getObjectBoolAttribute(QLatin1String("Building"),QLatin1String("TextItalic"),objId,false)); + + m_building->setScale(MAP::Settings::mapObjectsTextScale()); + + // set additional values: + SituationMap::PositionCont posCont; + + posCont.reserve(4); + for (int i = 1; i <= 4; i++) + { + const QPointF &pIni = MAP::Settings::getPoint(QLatin1String("Building"),objId,i); + const QPointF &pTrans = s_basePoint.geographic(pIni); + DEBUG_LOG(m_logger) << LOGFUNCTION << " Point " << i << " : " + << "pIni= " << pIni << ", pTrans= " << pTrans; + posCont << SituationMap::Point3D::fromQPointF(pTrans); + } + + m_building->setPositions(posCont); + + // update view + bool success = m_building->updateView(); + + if (!success) + DEBUG_LOG(m_logger) << LOGFUNCTION << " updateView NOT OK"; + + m_buildingMap[objId] = m_building; + + connect(m_timer, &QTimer::timeout, this, [=]() + { + DEBUG_LOG(m_logger) << " Updating building " << objId; + onBuildingUpdate(objId); + }); + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::onBuildingUpdate(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + m_building = m_buildingMap[objId]; + if (!m_building) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No building object "; + return; + } + + m_building->setIdentifier(MAP::Settings::objectIdentifier(QLatin1String("Building"),objId) + QLatin1String("Update")); + + m_building->rTextStyle().setAngle(MAP::Settings::getObjectIntAttribute(QLatin1String("Building"),QLatin1String("TextAngleUpdate"),objId,0)); + + m_building->rFillStyle().setFillColor(QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("Building"),QLatin1String("FillColor"),objId,QLatin1String("yellow")))); + m_building->rFillStyle().setFillPattern((SituationMap::FillPattern)MAP::Settings::getObjectIntAttribute(QLatin1String("Building"),QLatin1String("FillPattern"),objId,2)); + + m_building->rLineStyle().setLineColor(QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("Building"),QLatin1String("LineColor"),objId,QLatin1String("red")))); + m_building->rLineStyle().setLineWidth(MAP::Settings::getObjectIntAttribute(QLatin1String("Building"),QLatin1String("LineWidth"),objId,6)); + m_building->rLineStyle().setLinePattern(MAP::Settings::getLinePattern(MAP::Settings::getObjectStringAttribute(QLatin1String("Building"),QLatin1String("LinePattern"),objId,QLatin1String("DashDotDot")))); + + //TODO: move building on update ? + + bool success = m_building->updateView(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " success=" << success; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::removeBuildings() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + const QList keys = m_buildingMap.keys(); + for ( int objId, keys) + { + DEBUG_LOG(m_logger) << "Removing Building: " << objId; + removeBuilding(objId); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::removeBuilding(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + m_building = m_buildingMap[objId]; + if (m_building) + { + m_building->removeMySelf(); + m_building.reset(); + m_buildingMap.remove(objId); + } + + if (m_map) + { + m_map->update(); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::hideBuildings() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + for (auto it = m_buildingMap.cbegin(), end = m_buildingMap.cend(); it != end; ++it) + { + int objId = it.key(); + DEBUG_LOG(m_logger) << "Hide/Show Building: " << objId; + hideBuilding(objId); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::hideBuilding(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + m_building = m_buildingMap[objId]; + if (m_building) + { + bool isVisible = m_building->visibility(); + m_building->setVisibility(!isVisible); + m_building->updateView(); + } + if (m_map) + m_map->update(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::updateMapObjects() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + m_timer->setSingleShot(true); + m_timer->start(1000); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +// ------------------------------------------------------------------------------------------------- +void MainWindow::createDangerAreas() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + this->createDangerArea(1); + // this->createDangerArea(2); + //TODO: more danger areas + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::createDangerArea(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + m_dangerArea = m_dangerAreaMap[objId]; + if (m_dangerArea) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " Danger Area already exists " << objId; + return; + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " Creating danger area " << objId; + m_dangerArea = m_map->objectFactory().create( QLatin1String("AES"), QLatin1String("DangerAreaId")); + + if (!m_dangerArea) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No danger area object "; + return; + } + + m_dangerArea->setIdentifier(MAP::Settings::objectIdentifier(QLatin1String("DangerArea"),objId)); + + m_dangerArea->setVisibility(MAP::Settings::getObjectBoolAttribute(QLatin1String("DangerArea"),QLatin1String("Visibility"),objId,true)); + m_dangerArea->setVisibility(SituationMap::ObjectAppearance::Technical, MAP::Settings::getObjectBoolAttribute(QLatin1String("DangerArea"),QLatin1String("VisibilityTech"),objId,true)); + m_dangerArea->setVisibility(SituationMap::ObjectAppearance::Tactical, MAP::Settings::getObjectBoolAttribute(QLatin1String("DangerArea"),QLatin1String("VisibilityTact"),objId,true)); + + m_dangerArea->setScale(MAP::Settings::mapObjectsTextScale()); + + m_dangerArea->rFillStyle().setFillColor(QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("DangerArea"),QLatin1String("FillColor"),objId,QLatin1String("yellow")))); + m_dangerArea->rFillStyle().setFillPattern(MAP::Settings::getFillPattern(MAP::Settings::getObjectStringAttribute(QLatin1String("DangerArea"),QLatin1String("FillPattern"),objId,QLatin1String("DiagonalCross")))); + + m_dangerArea->rLineStyle().setLineColor(QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("DangerArea"),QLatin1String("LineColor"),objId,QLatin1String("red")))); + m_dangerArea->rLineStyle().setLineWidth(MAP::Settings::getObjectIntAttribute(QLatin1String("DangerArea"),QLatin1String("LineWidth"),objId,6)); + m_dangerArea->rLineStyle().setLinePattern(MAP::Settings::getLinePattern(MAP::Settings::getObjectStringAttribute(QLatin1String("DangerArea"),QLatin1String("LinePattern"),objId,QLatin1String("DashDotDot")))); + + // set additional values: + SituationMap::PositionCont posCont; + QPointF offset = QPointF(MAP::Settings::getObjectIntAttribute(QLatin1String("DangerArea"),QLatin1String("Offset_X"),objId,50000), MAP::Settings::getObjectIntAttribute(QLatin1String("DangerArea"),QLatin1String("Offset_Y"),objId,50000)); + + int extent = MAP::Settings::getObjectIntAttribute(QLatin1String("DangerArea"),QLatin1String("Extent"),objId,30000); + posCont << SituationMap::Point3D::fromQPointF(offset - QPointF( 0, 0)); + posCont << SituationMap::Point3D::fromQPointF(offset - QPointF(extent, 0)); + posCont << SituationMap::Point3D::fromQPointF(offset - QPointF(extent, extent)); + posCont << SituationMap::Point3D::fromQPointF(offset - QPointF( 0, extent)); + + m_dangerArea->setPositions(posCont); + + // update view + bool success = m_dangerArea->updateView(); + + if (!success) + DEBUG_LOG(m_logger) << LOGFUNCTION << " updateView NOT OK"; + + m_dangerAreaMap[objId] = m_dangerArea; + + connect(m_timer, &QTimer::timeout, this, [=]() + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " Updating danger area " << objId; + onDangerAreaUpdate(objId); + }); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::removeDangerAreas() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + const QList keys = m_dangerAreaMap.keys(); + for ( int objId, keys ) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " Removing Danger Area: " << objId; + removeDangerArea(objId); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::removeDangerArea(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + m_dangerArea = m_dangerAreaMap[objId]; + if (m_dangerArea) + { + m_dangerArea->removeMySelf(); + m_dangerArea.reset(); + m_dangerAreaMap.remove(objId); + } + + if (m_map) + { + m_map->update(); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::hideDangerAreas() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + for (auto it = m_dangerAreaMap.cbegin(), end = m_dangerAreaMap.cend(); it != end; ++it) + { + int objId = it.key(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " Hide/Show Danger Area: " << objId; + hideDangerArea(objId); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::hideDangerArea(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + m_dangerArea = m_dangerAreaMap[objId]; + if (m_dangerArea) + { + bool isVisible = m_dangerArea->visibility(); + m_dangerArea->setVisibility(!isVisible); + m_dangerArea->updateView(); + } + + if (m_map) + m_map->update(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::onDangerAreaUpdate(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + m_dangerArea = m_dangerAreaMap[objId]; + if (!m_dangerArea) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No danger area object "; + return; + } + + m_dangerArea->setIdentifier(MAP::Settings::objectIdentifier(QLatin1String("DangerArea"),objId) + QLatin1String("Update")); + + //TODO: change more on update ? + + bool success = m_dangerArea->updateView(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " success=" << success; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::createBAgents() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + this->createBAgent(1); + // this->createBAgent(2); + //TODO: more B-agents + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::createBAgent(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + m_bAgent = m_bAgentMap[objId]; + if (m_bAgent) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " BAgent already exists " << objId; + return; + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " Creating BAgent " << objId; + m_bAgent = m_map->objectFactory().create( QLatin1String("AES"), QLatin1String("BAgentId")); + + if (!m_bAgent) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No BAgent object "; + return; + } + + m_bAgent->setIdentifier(MAP::Settings::objectIdentifier(QLatin1String("BAgent"),objId)); + + m_bAgent->setVisibility(MAP::Settings::getObjectBoolAttribute(QLatin1String("BAgent"),QLatin1String("Visibility"),objId,true)); + m_bAgent->setVisibility(SituationMap::ObjectAppearance::Technical, MAP::Settings::getObjectBoolAttribute(QLatin1String("BAgent"),QLatin1String("VisibilityTech"),objId,true)); + m_bAgent->setVisibility(SituationMap::ObjectAppearance::Tactical, MAP::Settings::getObjectBoolAttribute(QLatin1String("BAgent"),QLatin1String("VisibilityTact"),objId,true)); + + m_bAgent->setScale(MAP::Settings::mapObjectsTextScale()); + + m_bAgent->rFillStyle().setFillColor(QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("BAgent"),QLatin1String("FillColor"),objId,QLatin1String("yellow")))); + m_bAgent->rFillStyle().setFillPattern(MAP::Settings::getFillPattern(MAP::Settings::getObjectStringAttribute(QLatin1String("BAgent"),QLatin1String("FillPattern"),objId,QLatin1String("DiagonalCross")))); + + m_bAgent->rLineStyle().setLineColor(QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("BAgent"),QLatin1String("LineColor"),objId,QLatin1String("red")))); + m_bAgent->rLineStyle().setLineWidth(MAP::Settings::getObjectIntAttribute(QLatin1String("BAgent"),QLatin1String("LineWidth"),objId,6)); + m_bAgent->rLineStyle().setLinePattern(MAP::Settings::getLinePattern(MAP::Settings::getObjectStringAttribute(QLatin1String("BAgent"),QLatin1String("LinePattern"),objId,QLatin1String("DashDotDot")))); + + m_bAgent->rTextStyle().setTextColor(QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("BAgent"),QLatin1String("TextColor"),objId,QLatin1String("magenta")))); + m_bAgent->rTextStyle().setSize(MAP::Settings::getObjectIntAttribute(QLatin1String("BAgent"),QLatin1String("TextSize"),objId,30)); + m_bAgent->rTextStyle().setBold(MAP::Settings::getObjectBoolAttribute(QLatin1String("BAgent"),QLatin1String("TextBold"),objId,false)); + + // set additional values: + QPointF center = QPointF(MAP::Settings::getObjectIntAttribute(QLatin1String("BAgent"),QLatin1String("Offset_X"),objId,50000), MAP::Settings::getObjectIntAttribute(QLatin1String("BAgent"),QLatin1String("Offset_Y"),objId,50000)); + int radius = MAP::Settings::getObjectIntAttribute(QLatin1String("BAgent"),QLatin1String("Extent"),objId,30000); + + m_bAgent->setOrigin(SituationMap::Point3D::fromQPointF(center)); + m_bAgent->setRadius(radius); + + // update view + bool success = m_bAgent->updateView(); + + if (!success) + DEBUG_LOG(m_logger) << LOGFUNCTION << " updateView NOT OK"; + + m_bAgentMap[objId] = m_bAgent; + + connect(m_timer, &QTimer::timeout, this, [=]() + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " Updating BAgent " << objId; + onBAgentUpdate(objId); + }); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::removeBAgents() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + const QList keys = m_bAgentMap.keys(); + for ( int objId, keys ) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << "Removing Fire Area: " << objId; + removeBAgent(objId); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::removeBAgent(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + m_bAgent = m_bAgentMap[objId]; + if (m_bAgent) + { + m_bAgent->removeMySelf(); + m_bAgent.reset(); + m_bAgentMap.remove(objId); + } + + if (m_map) + { + m_map->update(); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::hideBAgents() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + for (auto it = m_bAgentMap.cbegin(), end = m_bAgentMap.cend(); it != end; ++it) + { + int objId = it.key(); + DEBUG_LOG(m_logger) << LOGFUNCTION << "Hide/Show Fire Area: " << objId; + hideBAgent(objId); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::hideBAgent(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + m_bAgent = m_bAgentMap[objId]; + if (m_bAgent) + { + bool isVisible = m_bAgent->visibility(); + m_bAgent->setVisibility(!isVisible); + m_bAgent->updateView(); + } + + if (m_map) + m_map->update(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::onBAgentUpdate(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + m_bAgent = m_bAgentMap[objId]; + if (!m_bAgent) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No Fire area object "; + return; + } + + m_bAgent->setIdentifier(MAP::Settings::objectIdentifier(QLatin1String("BAgent"),objId) + QLatin1String("Update")); + + //TODO: change more on update ? + + bool success = m_bAgent->updateView(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " success=" << success; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::createFireAreas() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + this->createFireArea(1); + // this->createFireArea(2); + //TODO: more Fire areas + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::createFireArea(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + m_fireArea = m_fireAreaMap[objId]; + if (m_fireArea) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " Fire Area already exists " << objId; + return; + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " Creating Fire Area " << objId; + m_fireArea = m_map->objectFactory().create( QLatin1String("AES"), QLatin1String("AreaFireId")); + + if (!m_fireArea) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No Fire Area object "; + return; + } + + m_fireArea->setIdentifier(MAP::Settings::objectIdentifier(QLatin1String("FireArea"),objId)); + + m_fireArea->setVisibility(MAP::Settings::getObjectBoolAttribute(QLatin1String("FireArea"),QLatin1String("Visibility"),objId,true)); + m_fireArea->setVisibility(SituationMap::ObjectAppearance::Technical, MAP::Settings::getObjectBoolAttribute(QLatin1String("FireArea"),QLatin1String("VisibilityTech"),objId,true)); + m_fireArea->setVisibility(SituationMap::ObjectAppearance::Tactical, MAP::Settings::getObjectBoolAttribute(QLatin1String("FireArea"),QLatin1String("VisibilityTact"),objId,true)); + + m_fireArea->setScale(MAP::Settings::mapObjectsTextScale()); + + m_fireArea->rFillStyle().setFillColor(QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("FireArea"),QLatin1String("FillColor"),objId,QLatin1String("blue")))); + m_fireArea->rFillStyle().setFillPattern(MAP::Settings::getFillPattern(MAP::Settings::getObjectStringAttribute(QLatin1String("FireArea"),QLatin1String("FillPattern"),objId,QLatin1String("null")))); + + m_fireArea->rLineStyle().setLineColor(QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("FireArea"),QLatin1String("LineColor"),objId,QLatin1String("red")))); + m_fireArea->rLineStyle().setLineWidth(MAP::Settings::getObjectIntAttribute(QLatin1String("FireArea"),QLatin1String("LineWidth"),objId,6)); + m_fireArea->rLineStyle().setLinePattern(MAP::Settings::getLinePattern(MAP::Settings::getObjectStringAttribute(QLatin1String("FireArea"),QLatin1String("LinePattern"),objId,QLatin1String("Solid")))); + + // set additional values: + SituationMap::PositionCont posCont; + QPointF offset = QPointF(MAP::Settings::getObjectIntAttribute(QLatin1String("FireArea"),QLatin1String("Offset_X"),objId,170000), MAP::Settings::getObjectIntAttribute(QLatin1String("FireArea"),QLatin1String("Offset_Y"),objId,170000)); + + int extent = MAP::Settings::getObjectIntAttribute(QLatin1String("FireArea"),QLatin1String("Extent"),objId,100000); + posCont << SituationMap::Point3D::fromQPointF(offset + QPointF( 0, 0)); + posCont << SituationMap::Point3D::fromQPointF(offset + QPointF(extent, 0)); + posCont << SituationMap::Point3D::fromQPointF(offset + QPointF(extent, extent)); + posCont << SituationMap::Point3D::fromQPointF(offset + QPointF(extent/2, extent/2)); + posCont << SituationMap::Point3D::fromQPointF(offset + QPointF( 0, extent)); + + m_fireArea->setPositions(posCont); + + posCont.removeAt(3); + m_fireArea->setImpacts(posCont); + + // TODO: use colors from PartyId ? + // m_fireArea->setPartyId(3); + // m_fireArea->setSelected(true); + + // update view + bool success = m_fireArea->updateView(); + + if (!success) + DEBUG_LOG(m_logger) << LOGFUNCTION << " updateView NOT OK"; + + m_fireAreaMap[objId] = m_fireArea; + + connect(m_timer, &QTimer::timeout, this, [=]() + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " Updating Fire Area " << objId; + onFireAreaUpdate(objId); + }); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::removeFireAreas() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + const QList keys = m_fireAreaMap.keys(); + for ( int objId, keys) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << "Removing Fire Area: " << objId; + removeFireArea(objId); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::removeFireArea(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + m_fireArea = m_fireAreaMap[objId]; + if (m_fireArea) + { + m_fireArea->removeMySelf(); + m_fireArea.reset(); + m_fireAreaMap.remove(objId); + } + + if (m_map) + { + m_map->update(); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::hideFireAreas() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + for (auto it = m_fireAreaMap.cbegin(), end = m_fireAreaMap.cend(); it != end; ++it) + { + int objId = it.key(); + DEBUG_LOG(m_logger) << LOGFUNCTION << "Hide/Show Fire Area: " << objId; + hideFireArea(objId); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::hideFireArea(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + m_fireArea = m_fireAreaMap[objId]; + if (m_fireArea) + { + bool isVisible = m_fireArea->visibility(); + m_fireArea->setVisibility(!isVisible); + m_fireArea->updateView(); + } + + if (m_map) + m_map->update(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::onFireAreaUpdate(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + m_fireArea = m_fireAreaMap[objId]; + if (!m_fireArea) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No Fire area object "; + return; + } + + m_fireArea->setIdentifier(MAP::Settings::objectIdentifier(QLatin1String("FireArea"),objId) + QLatin1String("Update")); + m_fireArea->setPartyId(4); // Will overwrite line/fillcolor + + SituationMap::PositionCont posCont; + QPointF offset = QPointF(MAP::Settings::getObjectIntAttribute(QLatin1String("FireArea"),QLatin1String("Offset_X"),objId,170000), MAP::Settings::getObjectIntAttribute(QLatin1String("FireArea"),QLatin1String("Offset_Y"),objId,170000)); + + int extent = MAP::Settings::getObjectIntAttribute(QLatin1String("FireArea"),QLatin1String("Extent"),objId,100000); + posCont << SituationMap::Point3D::fromQPointF(offset + QPointF( 0, 0)); + posCont << SituationMap::Point3D::fromQPointF(offset + QPointF(extent, 0)); + posCont << SituationMap::Point3D::fromQPointF(offset + QPointF(extent, extent*2)); + posCont << SituationMap::Point3D::fromQPointF(offset + QPointF( 0, extent)); + + m_fireArea->setPositions(posCont); + + posCont.removeAt(1); + posCont.removeAt(3); + + m_fireArea->updateView(); + + //TODO: change more on update ? + + bool success = m_fireArea->updateView(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " success=" << success; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::createHitLines() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + this->createHitLine(1); + // this->createHitLine(2); + //TODO: more Hit Lines + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::createHitLine(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + m_hitLine = m_hitLineMap[objId]; + if (m_hitLine) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " Fire Line already exists " << objId; + return; + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " Creating Fire Line " << objId; + m_hitLine = m_map->objectFactory().create( QLatin1String("AES"), QLatin1String("HitLineId")); + + if (!m_hitLine) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No Fire Line object "; + return; + } + + m_hitLine->setIdentifier(MAP::Settings::objectIdentifier(QLatin1String("HitLine"),objId)); + + m_hitLine->setVisibility(MAP::Settings::getObjectBoolAttribute(QLatin1String("HitLine"),QLatin1String("Visibility"),objId,true)); + m_hitLine->setVisibility(SituationMap::ObjectAppearance::Technical, MAP::Settings::getObjectBoolAttribute(QLatin1String("HitLine"),QLatin1String("VisibilityTech"),objId,true)); + m_hitLine->setVisibility(SituationMap::ObjectAppearance::Tactical, MAP::Settings::getObjectBoolAttribute(QLatin1String("HitLine"),QLatin1String("VisibilityTact"),objId,true)); + + m_hitLine->setScale(MAP::Settings::mapObjectsTextScale()); + + m_hitLine->rLineStyle().setLineColor(QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("HitLine"),QLatin1String("LineColor"),objId,QLatin1String("red")))); + m_hitLine->rLineStyle().setLineWidth(MAP::Settings::getObjectIntAttribute(QLatin1String("HitLine"),QLatin1String("LineWidth"),objId,6)); + m_hitLine->rLineStyle().setLinePattern(MAP::Settings::getLinePattern(MAP::Settings::getObjectStringAttribute(QLatin1String("HitLine"),QLatin1String("LinePattern"),objId,QLatin1String("DashDotDot")))); + + // set additional values: + QPointF offset = QPointF(MAP::Settings::getObjectIntAttribute(QLatin1String("HitLine"),QLatin1String("Offset_X"),objId,50000), MAP::Settings::getObjectIntAttribute(QLatin1String("HitLine"),QLatin1String("Offset_Y"),objId,50000)); + int extent = MAP::Settings::getObjectIntAttribute(QLatin1String("HitLine"),QLatin1String("Extent"),objId,30000); + + m_hitLine->setStartAndEndPoint(SituationMap::Point3D::fromQPointF(offset), + SituationMap::Point3D::fromQPointF(offset + QPointF(0, extent))); + + // update view + bool success = m_hitLine->updateView(); + + if (!success) + DEBUG_LOG(m_logger) << LOGFUNCTION << " updateView NOT OK"; + + m_hitLineMap[objId] = m_hitLine; + + connect(m_timer, &QTimer::timeout, this, [=]() + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " Updating Fire Line " << objId; + onHitLineUpdate(objId); + }); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::removeHitLines() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + const QList keys = m_hitLineMap.keys(); + for ( int objId, keys) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << "Removing Hit Line: " << objId; + removeHitLine(objId); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::removeHitLine(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + m_hitLine = m_hitLineMap[objId]; + if (m_hitLine) + { + m_hitLine->removeMySelf(); + m_hitLine.reset(); + m_hitLineMap.remove(objId); + } + + if (m_map) + { + m_map->update(); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::hideHitLines() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + for (auto it = m_hitLineMap.cbegin(), end = m_hitLineMap.cend(); it != end; ++it) + { + int objId = it.key(); + DEBUG_LOG(m_logger) << LOGFUNCTION << "Hide/Show Hit Line: " << objId; + hideHitLine(objId); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::hideHitLine(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + m_hitLine = m_hitLineMap[objId]; + if (m_hitLine) + { + bool isVisible = m_hitLine->visibility(); + m_hitLine->setVisibility(!isVisible); + m_hitLine->updateView(); + } + + if (m_map) + m_map->update(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::onHitLineUpdate(int objId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + m_hitLine = m_hitLineMap[objId]; + if (!m_hitLine) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No Fire Line object "; + return; + } + + m_hitLine->setIdentifier(MAP::Settings::objectIdentifier(QLatin1String("HitLine"),objId) + QLatin1String("Update")); + + // set additional values: + SituationMap::Point3D start = m_hitLine->startPoint(); + SituationMap::Point3D end = m_hitLine->endPoint(); + + m_hitLine->setStartAndEndPoint(end, + start + SituationMap::Point3D(10000, 1000, .0)); + + /* + // set additional values: + SituationMap::PositionCont posCont; + QPointF offset = QPointF(MAP::Settings::getObjectIntAttribute(QLatin1String("HitLine"),QLatin1String("Offset_X"),objId,50000), MAP::Settings::getObjectIntAttribute(QLatin1String("HitLine"),QLatin1String("Offset_Y"),objId,50000)); + int extent = MAP::Settings::getObjectIntAttribute(QLatin1String("HitLine"),QLatin1String("Extent"),objId,30000); + + posCont << offset + QPointF(extent, 0); + posCont << offset + QPointF( 0, extent); + + m_hitLine->setPositions(posCont); +*/ + + //TODO: change more on update ? + + bool success = m_hitLine->updateView(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " success=" << success; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandPointCluster() +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandPointCluster * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandPointClusterApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +//------------------------------------------------------------------------------------------ +void MainWindow::onRubberbandAbort( SituationMap::RB::RubberbandBase * rubberband ) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " Aborting rubberband "; + + rubberband->deleteMySelf(); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandMultiPoint() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandMultiPointFeature * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << "Creating rubberband failed"; + } + else + { + rubberband->enableClosed(false); + + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandMultiPointApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandRectangle() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandRectangle * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandRectangleApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandSquare() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandRectangle * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + rubberband->constrainQuadratic(true); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandRectangleApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandEllipse() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandEllipse * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandEllipseApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandArrow() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandArrow * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandArrowApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCorridorCenterline() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandCorridor * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + rubberband->setInputCenterline(); + rubberband->setCorridorWidth(MAP::Settings::getObjectIntAttribute(QLatin1String("RB_Corridor"),QLatin1String("Width"),1,8000) ); + rubberband->setCorridorFillColor((QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("RB_Corridor"),QLatin1String("FillColor"),1,QLatin1String("orange")))) ); + + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandCorridorApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCorridorBoundary() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandCorridor * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + rubberband->setInputBoundary(); + rubberband->setCorridorFillColor((QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("RB_Corridor"),QLatin1String("FillColor"),1,QLatin1String("orange")))) ); + rubberband->maximumLength( MAP::Settings::getObjectIntAttribute(QLatin1String("RB_Corridor"),QLatin1String("MaxLength"),1,100000) ); + rubberband->maximumSegmentLength( MAP::Settings::getObjectIntAttribute(QLatin1String("RB_Corridor"),QLatin1String("MaxSegmentLength"),1,10000) ); + rubberband->minimumCorridorWidth( MAP::Settings::getObjectIntAttribute(QLatin1String("RB_Corridor"),QLatin1String("MinWidth"),1,2000) ); + rubberband->maximumCorridorWidth( MAP::Settings::getObjectIntAttribute(QLatin1String("RB_Corridor"),QLatin1String("MaxWidth"),1,10000) ); + rubberband->minimumPointCount( MAP::Settings::getObjectIntAttribute(QLatin1String("RB_Corridor"),QLatin1String("MinPointCount"),1,3) ); + rubberband->maximumPointCount( MAP::Settings::getObjectIntAttribute(QLatin1String("RB_Corridor"),QLatin1String("MaxPointCount"),1,8) ); + rubberband->draw(); + + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandCorridorApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandFilledMultiPoint() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandMultiPointFeature * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + rubberband->enableClosed(true); + rubberband->setFillColor( (QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("RB_MultiPoint"),QLatin1String("FillColor"),1,QLatin1String("teal")))) ); + rubberband->maximumLength( MAP::Settings::getObjectIntAttribute(QLatin1String("RB_MultiPoint"),QLatin1String("MaxLength"),1,1000000) ); + rubberband->maximumSegmentLength( MAP::Settings::getObjectIntAttribute(QLatin1String("RB_MultiPoint"),QLatin1String("MaxSegmentLength"),1,500000) ); + + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandMultiPointApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandSmoothLine() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandSmoothLine * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandSmoothLineApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandFilledSmoothLine() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandSmoothLine * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + rubberband->enableClosed(true); + rubberband->setFillColor( (QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("RB_SmoothLine"),QLatin1String("FillColor"),1,QLatin1String("olive")))) ); + + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandSmoothLineApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreateRectangle(int objId, int noPoints, bool isSquare) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandRectangle * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + QVector vertexList; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " CREATE RUBBERBAND with values from ini file"; + + vertexList.reserve(noPoints); + for (int i = 1; i <= noPoints; i++) + { + vertexList.append(MAP::Settings::getPoint(QLatin1String("RB_Rectangle"),objId,i)); + DEBUG_LOG(m_logger) << LOGFUNCTION << " Point " << i << " : (" << MAP::Settings::getPoint(QLatin1String("RB_Rectangle"),objId,i).x() << "," << MAP::Settings::getPoint(QLatin1String("RB_Rectangle"),objId,i).y() << ")"; + } + + rubberband->setCoordinateVector(vertexList); + rubberband->setFrameHandleMargin(MAP::Settings::getObjectIntAttribute(QLatin1String("RB_Rectangle"),QLatin1String("HandleMargin"),objId,10)); + rubberband->constrainQuadratic(isSquare); + + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandRectangleApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreateRectangles() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + rubberbandCreateRectangle(1, 4, false); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreateSquares() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + rubberbandCreateRectangle(2, 4, true); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreateEllipses() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + rubberbandCreateEllipse(1, 4); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreatePointClusters() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + rubberbandCreatePointCluster(1, 8); + //rubberbandCreatePointCluster(2, 4); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreateEllipse(int objId, int noPoints) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandEllipse * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + QVector vertexList; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " CREATE RUBBERBAND with values from ini file"; + + vertexList.reserve(noPoints); + for (int i = 1; i <= noPoints; i++) + { + vertexList.append(MAP::Settings::getPoint(QLatin1String("RB_Ellipse"),objId,i)); + DEBUG_LOG(m_logger) << LOGFUNCTION << " Point " << i << " : (" << MAP::Settings::getPoint(QLatin1String("RB_Ellipse"),objId,i).x() << "," << MAP::Settings::getPoint(QLatin1String("RB_Ellipse"),objId,i).y() << ")"; + } + + rubberband->setCoordinateVector(vertexList); + rubberband->setFrameHandleMargin(MAP::Settings::getObjectIntAttribute(QLatin1String("RB_Ellipse"),QLatin1String("HandleMargin"),objId,10)); + + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandEllipseApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreatePointCluster(int objId, int noPoints) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandPointCluster * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " CREATE RUBBERBAND with values from ini file"; + + QVector vertexList; + + vertexList.reserve(noPoints); + for (int i = 1; i <= noPoints; i++) + { + vertexList.append(MAP::Settings::getPoint(QLatin1String("RB_PointCluster"),objId,i)); + DEBUG_LOG(m_logger) << LOGFUNCTION << " Point " << i << " : (" << MAP::Settings::getPoint(QLatin1String("RB_PointCluster"),objId,i).x() << "," << MAP::Settings::getPoint(QLatin1String("RB_PointCluster"),objId,i).y() << ")"; + } + + rubberband->setCoordinateVector(vertexList); + + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandPointClusterApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreateMultiPoint(int objId, int noPoints, bool filled) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandMultiPointFeature * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " CREATE RUBBERBAND with values from ini file"; + + QVector vertexList; + + vertexList.reserve(noPoints); + for (int i = 1; i <= noPoints; i++) + { + vertexList.append(MAP::Settings::getPoint(QLatin1String("RB_MultiPoint"),objId,i)); + DEBUG_LOG(m_logger) << LOGFUNCTION << " Point " << i << " : (" << MAP::Settings::getPoint(QLatin1String("RB_MultiPoint"),objId,i).x() << "," << MAP::Settings::getPoint(QLatin1String("RB_MultiPoint"),objId,i).y() << ")"; + } + + rubberband->setCoordinateVector(vertexList); + rubberband->enableClosed( false ); + + if (filled) + { + rubberband->enableClosed( true ); + rubberband->setFillColor( (QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("RB_MultiPoint"),QLatin1String("FillColor"),objId,QLatin1String("teal")))) ); + rubberband->maximumLength( MAP::Settings::getObjectIntAttribute(QLatin1String("RB_MultiPoint"),QLatin1String("MaxLength"), objId, 1000000) ); + rubberband->maximumSegmentLength( MAP::Settings::getObjectIntAttribute(QLatin1String("RB_MultiPoint"),QLatin1String("MaxSegmentLength"), objId, 500000) ); + } + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandMultiPointApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreateMultiPoints() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + rubberbandCreateMultiPoint(1, 5, false); + // TODO: more + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreateFilledMultiPoints() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + rubberbandCreateMultiPoint(1, 5, true); + // TODO: more + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreateSmoothLines() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + rubberbandCreateSmoothLine(1, 4, false); + // TODO: more + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreateFilledSmoothLines() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + rubberbandCreateSmoothLine(1, 4, true); + // TODO: more + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreateSmoothLine(int objId, int noPoints, bool filled) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandSmoothLine * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " CREATE RUBBERBAND with values from ini file"; + + QVector vertexList; + + vertexList.reserve(noPoints); + for (int i = 1; i <= noPoints; i++) + { + vertexList.append(MAP::Settings::getPoint(QLatin1String("RB_SmoothLine"),objId,i)); + DEBUG_LOG(m_logger) << LOGFUNCTION << " Point " << i << " : (" << MAP::Settings::getPoint(QLatin1String("RB_SmoothLine"),objId,i).x() << "," << MAP::Settings::getPoint(QLatin1String("RB_SmoothLine"),objId,i).y() << ")"; + } + + rubberband->setCoordinateVector(vertexList); + rubberband->enableClosed( false ); + if (filled) + { + rubberband->enableClosed( true ); + rubberband->setFillColor( (QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("RB_SmoothLine"),QLatin1String("FillColor"),objId,QLatin1String("olive")))) ); + } + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandSmoothLineApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreateCorridorBoundaries() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + rubberbandCreateCorridor(1, 5, true); + // TODO: more + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreateCorridorCenterlines() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + rubberbandCreateCorridor(1, 5, false); + // TODO: more + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreateCorridor(int objId, int noPoints, bool bound) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandCorridor * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " CREATE RUBBERBAND with values from ini file"; + + QVector vertexList; + + vertexList.reserve(noPoints); + for (int i = 1; i <= noPoints; i++) + { + vertexList.append(MAP::Settings::getPoint(QLatin1String("RB_Corridor"),objId,i)); + DEBUG_LOG(m_logger) << LOGFUNCTION << " Point " << i << " : (" << MAP::Settings::getPoint(QLatin1String("RB_Corridor"),objId,i).x() << "," << MAP::Settings::getPoint(QLatin1String("RB_Corridor"),objId,i).y() << ")"; + } + + double corrWidth = (double)MAP::Settings::getObjectIntAttribute(QLatin1String("RB_Corridor"),QLatin1String("Width"), objId, 8000); + DEBUG_LOG(m_logger) << LOGFUNCTION << " Width: " << corrWidth; + + QColor corrFillColor = (QColor(MAP::Settings::getObjectStringAttribute(QLatin1String("RB_Corridor"),QLatin1String("FillColor"), objId, QLatin1String("orange")))); + DEBUG_LOG(m_logger) << LOGFUNCTION << " Fill Color: " << corrFillColor.name(); + + rubberband->setCoordinateVectorWidth(vertexList, corrWidth); + rubberband->setCorridorFillColor(corrFillColor); + + if (bound) + { + rubberband->setInputBoundary(); +// rubberband->minimumPointCount( MAP::Settings::getObjectIntAttribute(QLatin1String("RB_Corridor"),QLatin1String("MinPointCount"), objId, 3) ); +// rubberband->maximumPointCount( MAP::Settings::getObjectIntAttribute(QLatin1String("RB_Corridor"),QLatin1String("MaxPointCount"), objId, 8) ); +// rubberband->minimumCorridorWidth( MAP::Settings::getObjectIntAttribute(QLatin1String("RB_Corridor"),QLatin1String("MinWidth"), objId, 5000) ); +// rubberband->maximumCorridorWidth( MAP::Settings::getObjectIntAttribute(QLatin1String("RB_Corridor"),QLatin1String("MaxWidth"), objId, 10000) ); +// rubberband->maximumLength( MAP::Settings::getObjectIntAttribute(QLatin1String("RB_Corridor"),QLatin1String("MaxLength"), objId, 10000) ); +// rubberband->maximumSegmentLength( MAP::Settings::getObjectIntAttribute(QLatin1String("RB_Corridor"),QLatin1String("MaxSegmentLength"), objId, 50000) ); + } + else + { + rubberband->setInputCenterline(); + } + + rubberband->draw(); + + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandCorridorApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreateArrows() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + rubberbandCreateArrow(1, 4); + // TODO: more + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +// ------------------------------------------------------------------------------------------------- +void MainWindow::rubberbandCreateArrow(int objId, int noPoints) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + if (!m_map) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " No map object "; + return; + } + + SituationMap::RB::RubberbandArrow * rubberband = nullptr; + + try + { + rubberband = m_map->rubberbandFactory().create(); + } + catch (SituationMap::Exception &e) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " " << e.getMessage(); + } + + if (rubberband == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating rubberband failed"; + } + else + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " CREATE RUBBERBAND with values from ini file"; + + QVector vertexList; + + vertexList.reserve(noPoints); + for (int i = 1; i <= noPoints; i++) + { + vertexList.append(MAP::Settings::getPoint(QLatin1String("RB_Arrow"),objId,i)); + DEBUG_LOG(m_logger) << LOGFUNCTION << " Point " << i << " : (" << MAP::Settings::getPoint(QLatin1String("RB_Arrow"),objId,i).x() << "," << MAP::Settings::getPoint(QLatin1String("RB_Arrow"),objId,i).y() << ")"; + } + + rubberband->setCoordinateVector(vertexList); + + QObject::connect( rubberband, &SituationMap::RB::Rubberband::apply, this, &MainWindow::onRubberbandArrowApply); + QObject::connect( rubberband, &SituationMap::RB::Rubberband::abort, this, &MainWindow::onRubberbandAbort); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +//-------------------------------------------------------------------------------------------------- +void MainWindow::onRubberbandPointClusterApply( SituationMap::RB::RubberbandBase *rubberband) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + SituationMap::RB::RubberbandPointCluster *initialRB = dynamic_cast(rubberband); + + if(initialRB != nullptr) + { + bool mRbands = m_map->rubberbandFactory().allowMultipleRubberbands(); + m_map->rubberbandFactory().allowMultipleRubberbands(true); + + SituationMap::RB::RubberbandPointCluster *finalRB = m_map->rubberbandFactory().create(); + + if (finalRB == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating final rubberband failed"; + } + else + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " Count of Coordinates " << initialRB->coordinateCount(); + QVector vertexList = initialRB->coordinateVector(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " INITIAL RUBBERBAND"; + for (int i = 0; i < vertexList.size(); ++i) + DEBUG_LOG(m_logger) << LOGFUNCTION << " Point " << i << " : (" << vertexList.at(i).x() << "," << vertexList.at(i).y() << ")"; + + finalRB->setCoordinateVector(vertexList); + + connect( finalRB, &SituationMap::RB::Rubberband::apply, this, &MainWindow::endRubberband); + connect( finalRB, &SituationMap::RB::Rubberband::abort, this, &MainWindow::endRubberband); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " DELETING INITIAL RUBBERBAND"; + initialRB->deleteMySelf(); + } + + m_map->rubberbandFactory().allowMultipleRubberbands(mRbands); + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " Converting initial rubberband failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +//-------------------------------------------------------------------------------------------------- +void MainWindow::onRubberbandMultiPointApply( SituationMap::RB::RubberbandBase *rubberband) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + SituationMap::RB::RubberbandMultiPointFeature *initialRB = dynamic_cast(rubberband); + + if (initialRB != nullptr) + { + bool mRbands = m_map->rubberbandFactory().allowMultipleRubberbands(); + m_map->rubberbandFactory().allowMultipleRubberbands(true); + + SituationMap::RB::RubberbandMultiPointFeature *finalRB = m_map->rubberbandFactory().create(); + + if (finalRB == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating final rubberband failed"; + } + else + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " Count of Coordinates " << initialRB->coordinateCount(); + QVector vertexList = initialRB->coordinateVector(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " INITIAL RUBBERBAND"; + for (int i = 0; i < vertexList.size(); ++i) + DEBUG_LOG(m_logger) << LOGFUNCTION << " Point " << i << " : (" << vertexList.at(i).x() << "," << vertexList.at(i).y() << ")"; + + QColor rbDrawColor = initialRB->getDrawColor(); + QColor rbFillColor = initialRB->getFillColor(); + + bool isClosed = initialRB->isClosedEnabled(); + + finalRB->setCoordinateVector(vertexList); + finalRB->setDrawColor(rbDrawColor); + + if (isClosed) + { + finalRB->enableClosed(true); + finalRB->setFillColor(rbFillColor); + finalRB->maximumLength(initialRB->getMaximumLength()); + finalRB->maximumSegmentLength(initialRB->getMaximumSegmentLength()); + } + + connect( finalRB, &SituationMap::RB::Rubberband::apply, this, &MainWindow::endRubberband); + connect( finalRB, &SituationMap::RB::Rubberband::abort, this, &MainWindow::endRubberband); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " DELETING INITIAL RUBBERBAND"; + initialRB->deleteMySelf(); + } + + m_map->rubberbandFactory().allowMultipleRubberbands(mRbands); + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " Converting initial rubberband failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +//-------------------------------------------------------------------------------------------------- +void MainWindow::onRubberbandRectangleApply( SituationMap::RB::RubberbandBase *rubberband) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + SituationMap::RB::RubberbandRectangle *initialRB = dynamic_cast(rubberband); + + if(initialRB != nullptr) + { + bool mRbands = m_map->rubberbandFactory().allowMultipleRubberbands(); + m_map->rubberbandFactory().allowMultipleRubberbands(true); + + SituationMap::RB::RubberbandRectangle *finalRB = m_map->rubberbandFactory().create(); + + if (finalRB == nullptr) + { + INFO_LOG(m_logger) << LOGFUNCTION << " Creating final rubberband failed"; + } + else + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " Count of Coordinates " << initialRB->coordinateCount(); + QVector vertexList = initialRB->coordinateVector(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " INITIAL RUBBERBAND"; + for (int i = 0; i < vertexList.size(); ++i) + DEBUG_LOG(m_logger) << LOGFUNCTION << " Point " << i << " : (" << vertexList.at(i).x() << "," << vertexList.at(i).y() << ")"; + DEBUG_LOG(m_logger) << LOGFUNCTION << " VALID = " << initialRB->coordinatesAreValid(); + + QPointF simPoint = initialRB->singleCoordinate( 0, SituationMap::RB::CoordinateSystem::Geo ); + QPointF mapPoint = initialRB->singleCoordinate( 0, SituationMap::RB::CoordinateSystem::Map ); + TRACE_LOG(m_logger) << LOGFUNCTION << " Rectangle center : sim coordinate (" << simPoint.x() << "," << simPoint.y() + << ") map coordinate (" << mapPoint.x() << "," << mapPoint.y() << ")"; + + bool constrains = initialRB->constrainQuadratic(); + QColor rbDrawColor = initialRB->getDrawColor(); + QColor rbFillColor = initialRB->getFillColor(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " QUADRATIC = " << constrains; + + finalRB->constrainQuadratic(constrains); + finalRB->setDrawColor(rbDrawColor); + finalRB->setFillColor(rbFillColor); + finalRB->setFrameHandleMargin(initialRB->getFrameHandleMargin()); + finalRB->setCoordinateVector(vertexList); + + connect( finalRB, &SituationMap::RB::Rubberband::apply, this, &MainWindow::endRubberband); + connect( finalRB, &SituationMap::RB::Rubberband::abort, this, &MainWindow::endRubberband); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " DELETING INITIAL RUBBERBAND"; + initialRB->deleteMySelf(); + } + + m_map->rubberbandFactory().allowMultipleRubberbands(mRbands); + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " Converting initial rubberband failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +//-------------------------------------------------------------------------------------------------- +void MainWindow::onRubberbandEllipseApply( SituationMap::RB::RubberbandBase *rubberband) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + SituationMap::RB::RubberbandEllipse *initialRB = dynamic_cast(rubberband); + + if(initialRB != nullptr) + { + bool mRbands = m_map->rubberbandFactory().allowMultipleRubberbands(); + m_map->rubberbandFactory().allowMultipleRubberbands(true); + + SituationMap::RB::RubberbandEllipse *finalRB = m_map->rubberbandFactory().create(); + + if (finalRB == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating final rubberband failed"; + } + else + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " Count of Coordinates " << initialRB->coordinateCount(); + QVector vertexList = initialRB->coordinateVector(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " INITIAL RUBBERBAND"; + for (int i = 0; i < vertexList.size(); ++i) + DEBUG_LOG(m_logger) << LOGFUNCTION << " Point " << i << " : (" << vertexList.at(i).x() << "," << vertexList.at(i).y() << ")"; + + QPointF simPoint = initialRB->singleCoordinate( 0, SituationMap::RB::CoordinateSystem::Geo ); + QPointF mapPoint = initialRB->singleCoordinate( 0, SituationMap::RB::CoordinateSystem::Map ); + DEBUG_LOG(m_logger) << LOGFUNCTION << " Ellipse center : sim coordinate (" << simPoint.x() << "," << simPoint.y() + << ") map coordinate (" << mapPoint.x() << "," << mapPoint.y() << ")"; + + bool constrains = initialRB->constrainQuadratic(); + QColor rbDrawColor = initialRB->getDrawColor(); + QColor rbFillColor = initialRB->getFillColor(); + + finalRB->constrainQuadratic(constrains); + finalRB->setCoordinateVector(vertexList); + finalRB->setDrawColor(rbDrawColor); + finalRB->setFillColor(rbFillColor); + finalRB->setFrameHandleMargin(initialRB->getFrameHandleMargin()); + + connect( finalRB, &SituationMap::RB::Rubberband::apply, this, &MainWindow::endRubberband); + connect( finalRB, &SituationMap::RB::Rubberband::abort, this, &MainWindow::endRubberband); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " DELETING INITIAL RUBBERBAND"; + initialRB->deleteMySelf(); + } + + m_map->rubberbandFactory().allowMultipleRubberbands(mRbands); + + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " Converting initial rubberband failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +//-------------------------------------------------------------------------------------------------- +void MainWindow::endRubberband( SituationMap::RB::RubberbandBase *rubberband) +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " START"; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " FINAL RUBBERBAND"; + + QVector vertexList = rubberband->coordinateVector(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " Count of Coordinates " << rubberband->coordinateCount(); + for (int i = 0; i < vertexList.size(); ++i) + DEBUG_LOG(m_logger) << LOGFUNCTION << " Point " << i << " : (" << vertexList.at(i).x() << "," << vertexList.at(i).y() << ")"; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " Draw Color = " << rubberband->getDrawColor().name(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " Fill Color = " << rubberband->getFillColor().name(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " Frame margin = " << rubberband->getFrameHandleMargin(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " DELETING FINAL RUBBERBAND"; + rubberband->deleteMySelf(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " END"; +} + +//-------------------------------------------------------------------------------------------------- +void MainWindow::onRubberbandSmoothLineApply( SituationMap::RB::RubberbandBase *rubberband) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + SituationMap::RB::RubberbandSmoothLine *initialRB = dynamic_cast(rubberband); + + if (initialRB != nullptr) + { + + bool mRbands = m_map->rubberbandFactory().allowMultipleRubberbands(); + m_map->rubberbandFactory().allowMultipleRubberbands(true); + + SituationMap::RB::RubberbandSmoothLine *finalRB = m_map->rubberbandFactory().create(); + + if (finalRB == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating final rubberband failed"; + } + else + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " Count of Coordinates " << initialRB->coordinateCount(); + QVector vertexList = initialRB->coordinateVector(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " INITIAL RUBBERBAND"; + for (int i = 0; i < vertexList.size(); ++i) + DEBUG_LOG(m_logger) << LOGFUNCTION << " Point " << i << " : (" << vertexList.at(i).x() << "," << vertexList.at(i).y() << ")"; + + QColor rbDrawColor = initialRB->getDrawColor(); + QColor rbFillColor = initialRB->getFillColor(); + bool isClosed = initialRB->isClosedEnabled(); + + finalRB->setDrawColor(rbDrawColor); + finalRB->setCoordinateVector(vertexList); + if (isClosed) + { + finalRB->enableClosed(true); + finalRB->setFillColor(rbFillColor); + } + + connect( finalRB, &SituationMap::RB::Rubberband::apply, this, &MainWindow::endRubberband); + connect( finalRB, &SituationMap::RB::Rubberband::abort, this, &MainWindow::endRubberband); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " DELETING INITIAL RUBBERBAND"; + initialRB->deleteMySelf(); + } + + m_map->rubberbandFactory().allowMultipleRubberbands(mRbands); + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " Converting initial rubberband failed"; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + + +//-------------------------------------------------------------------------------------------------- +void MainWindow::onRubberbandArrowApply( SituationMap::RB::RubberbandBase *rubberband) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + SituationMap::RB::RubberbandArrow* initialRB = dynamic_cast(rubberband); + + if (initialRB != nullptr) + { + bool mRbands = m_map->rubberbandFactory().allowMultipleRubberbands(); + m_map->rubberbandFactory().allowMultipleRubberbands(true); + + SituationMap::RB::RubberbandArrow *finalRB = m_map->rubberbandFactory().create(); + + if (finalRB == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating final rubberband failed"; + } + else + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " Count of Coordinates " << initialRB->coordinateCount(); + QVector vertexList = initialRB->coordinateVector(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " INITIAL RUBBERBAND"; + for (int i = 0; i < vertexList.size(); ++i) + DEBUG_LOG(m_logger) << LOGFUNCTION << " Point " << i << " : (" << vertexList.at(i).x() << "," << vertexList.at(i).y() << ")"; + + finalRB->setCoordinateVector(vertexList); + finalRB->setCoordinateVectorArrow( vertexList, initialRB->arrowHalfWidth(), initialRB->arrowLength() ); + + connect( finalRB, &SituationMap::RB::Rubberband::apply, this, &MainWindow::endRubberband); + connect( finalRB, &SituationMap::RB::Rubberband::abort, this, &MainWindow::endRubberband); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " DELETING INITIAL RUBBERBAND"; + initialRB->deleteMySelf(); + + } + + m_map->rubberbandFactory().allowMultipleRubberbands(mRbands); + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " Converting initial rubberband failed"; + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +//-------------------------------------------------------------------------------------------------- +void MainWindow::onRubberbandCorridorApply( SituationMap::RB::RubberbandBase *rubberband) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + SituationMap::RB::RubberbandCorridor *initialRB = dynamic_cast(rubberband); + + if (initialRB != nullptr) + { + bool mRbands = m_map->rubberbandFactory().allowMultipleRubberbands(); + m_map->rubberbandFactory().allowMultipleRubberbands(true); + + SituationMap::RB::RubberbandCorridor * finalRB = m_map->rubberbandFactory().create(); + + if (finalRB == nullptr) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Creating final rubberband failed"; + } + else + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " Count of Coordinates " << initialRB->coordinateCount(); + QVector vertexList = initialRB->coordinateVector(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " INITIAL RUBBERBAND"; + for (int i = 0; i < vertexList.size(); ++i) + DEBUG_LOG(m_logger) << LOGFUNCTION << " Point " << i << " : (" << vertexList.at(i).x() << "," << vertexList.at(i).y() << ")"; + DEBUG_LOG(m_logger) << LOGFUNCTION << " Corridor width = " << initialRB->corridorWidth(); + + QColor rbDrawColor = initialRB->getCorridorBorderColor(); + QColor rbFillColor = initialRB->getCorridorFillColor(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " Corridor Draw color = " << rbDrawColor.name(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " Corridor Fill color = " << rbFillColor.name(); + + finalRB->setCorridorBorderColor(rbDrawColor); + finalRB->setCorridorFillColor(rbFillColor); + + bool bound = initialRB->isEnabledInputBoundary(); + if (bound) + { + finalRB->setInputBoundary(); + finalRB->setCoordinateVectorWidth(vertexList, initialRB->corridorWidth()); + } + else + { + finalRB->setInputCenterline(); + finalRB->setCoordinateVectorWidth(vertexList, initialRB->corridorWidth()); + } + + connect(finalRB, &SituationMap::RB::Rubberband::apply, this, &MainWindow::endRubberband); + connect(finalRB, &SituationMap::RB::Rubberband::abort, this, &MainWindow::endRubberband); + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " DELETING INITIAL RUBBERBAND"; + initialRB->deleteMySelf(); + + m_map->rubberbandFactory().allowMultipleRubberbands(mRbands); + } + else + ERROR_LOG(m_logger) << LOGFUNCTION << " Converting initial rubberband failed"; + + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/mainwindow.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/mainwindow.h new file mode 100755 index 000000000..09e98af63 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/mainwindow.h @@ -0,0 +1,201 @@ +/** + * @RDE_HEADER@ + * + * MainWindow for DDS View + * + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "nglsettings.h" +#include + +namespace Ui { + class widgetMain; +} + +class MainWindow : public NGLMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + void init(); + +private: + Ui::widgetMain* m_ui; + +// map + SituationMap::Map* m_map = nullptr; + +// map settings + float m_scale = 500000.0; + int m_wkid = 32632; + QPointF m_objectBasePoint = QPointF(0, 0); +// QPointF m_objectCenterPoint = QPointF(0, 0); + LtsCoordTrans::UtmCoordinate s_basePoint; + +// map objects + +// buildings + SituationMap::RefPointer m_building; + QMap > m_buildingMap; + +//danger areas + SituationMap::RefPointer m_dangerArea; + QMap > m_dangerAreaMap; + +//fire areas + SituationMap::RefPointer m_fireArea; + QMap > m_fireAreaMap; + +// B agents + SituationMap::RefPointer m_bAgent; + QMap > m_bAgentMap; + +// Fire line + SituationMap::RefPointer m_hitLine; + QMap > m_hitLineMap; + +//settings + bool m_visibility = true; + bool m_visibilityTechn = true; + bool m_visibilityTact = true; + + void setVersionView(); + +//timer for update buildings + QTimer* m_timer; + + SituationMap::LOGGER_T m_logger; + +private slots: + void onClose(); + +// map + void createMap(); + void destroyMap(); + void onMapReady(); + void onExtentChanged(); + void onMapObjectClicked( const SituationMap::Map::RefPointerMapObjectList &mapObjList ); + void updateMapObjects(); + void clearMapObjects(); + +// buildings + void createBuildings(); + void createBuilding(int objId); + void removeBuildings(); + void removeBuilding(int objId); + void onBuildingUpdate(int objId); + void hideBuildings(); + void hideBuilding(int objId); + +//danger areas + void createDangerAreas(); + void createDangerArea(int objId); + void removeDangerAreas(); + void removeDangerArea(int objId); + void onDangerAreaUpdate(int objId); + void hideDangerAreas(); + void hideDangerArea(int objId); + +//fire areas + void createFireAreas(); + void createFireArea(int objId); + void removeFireAreas(); + void removeFireArea(int objId); + void onFireAreaUpdate(int objId); + void hideFireAreas(); + void hideFireArea(int objId); + +// b agents + void createBAgents(); + void createBAgent(int objId); + void removeBAgents(); + void removeBAgent(int objId); + void onBAgentUpdate(int objId); + void hideBAgents(); + void hideBAgent(int objId); + +// fire lines + void createHitLines(); + void createHitLine(int objId); + void removeHitLines(); + void removeHitLine(int objId); + void onHitLineUpdate(int objId); + void hideHitLines(); + void hideHitLine(int objId); + +// rubberbands + void rubberbandPointCluster(); + void rubberbandMultiPoint(); + void rubberbandRectangle(); + void rubberbandSquare(); + void rubberbandEllipse(); + void rubberbandArrow(); + void rubberbandCorridorBoundary(); + void rubberbandCorridorCenterline(); + void rubberbandFilledMultiPoint(); + void rubberbandSmoothLine(); + void rubberbandFilledSmoothLine(); + + void rubberbandCreateRectangle(int objId, int noPoints, bool isSquare); + void rubberbandCreateRectangles(); + void rubberbandCreateSquares(); + + void rubberbandCreateEllipse(int objId, int noPoints); + void rubberbandCreateEllipses(); + + void rubberbandCreatePointClusters(); + void rubberbandCreatePointCluster(int objId, int noPoints); + + void rubberbandCreateMultiPoints(); + void rubberbandCreateMultiPoint(int objId, int noPoints, bool filled = false); + + void rubberbandCreateFilledMultiPoints(); + + void rubberbandCreateSmoothLines(); + void rubberbandCreateSmoothLine(int objId, int noPoints, bool filled = false); + + void rubberbandCreateFilledSmoothLines(); + + void rubberbandCreateCorridorBoundaries(); + void rubberbandCreateCorridor(int objId, int noPoints, bool bound = false); + + void rubberbandCreateCorridorCenterlines(); + + void rubberbandCreateArrows(); + void rubberbandCreateArrow(int objId, int noPoints); + +// void onRubberbandApply( SituationMap::RB::Rubberband * rubberband); + void onRubberbandAbort( SituationMap::RB::RubberbandBase * rubberband); + + void onRubberbandPointClusterApply(SituationMap::RB::RubberbandBase *rubberband); + void onRubberbandMultiPointApply( SituationMap::RB::RubberbandBase *rubberband); + void onRubberbandRectangleApply( SituationMap::RB::RubberbandBase *rubberband); + void onRubberbandEllipseApply( SituationMap::RB::RubberbandBase *rubberband); + void onRubberbandSmoothLineApply( SituationMap::RB::RubberbandBase *rubberband); + void onRubberbandArrowApply( SituationMap::RB::RubberbandBase *rubberband); + void onRubberbandCorridorApply(SituationMap::RB::RubberbandBase *rubberband); + + void endRubberband( SituationMap::RB::RubberbandBase *rubberband); + +}; + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/mapsettings.ini b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/mapsettings.ini new file mode 100755 index 000000000..01eae740b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/mapsettings.ini @@ -0,0 +1,241 @@ +# $Id$ +# $HeadURL$ +# $Author$ + +[Map] +URL = "http://excgeo21.sl.defence-elec.de/arcgis/rest/services/Bremen/BremenHemelingenAerialGeoAmt_32632/MapServer" +URL1 = "http://excgeo21.sl.defence-elec.de/arcgis/rest/services/Norddeutschland/Norddeutschland_32632/MapServer" +Wkid = 32632 +BasePoint = "32UMD000000000000" ;# if CoordTrans in not initialized: +Scale = 50000.00 +GridType = "MGRS" ;# GridType: NoType, MGRS, UTM, USGN +GridVisible = false +RotationDegree = 0.0 +SymbologyType = "MIL2525D" +FunctionDialogVisible = true + +[MapObjects] +TextScale = 1.00 + +[Building_1] +Identifier = "Building_001" + +P1 = "93064,78331" +P2 = "93069.9,78337.8" +P3 = "93076.8,78331.8" +P4 = "93070.9,78325" + +P11 = "0.155274,0.925975" +P22 = "0.155275,0.925976" +P33 = "0.155277,0.925975" +P44 = "0.155275,0.925974" + +TextColor = "red" +TextSize = 20 +TextAngle = 0 +TextItalic = true +TextAntiAliased = true + +Visibility = true +VisibilityTech = true +VisibilityTact = true + +LineColor = "red" +LineWidth = 6 +LinePattern = "DashDotDot" + +FillColor = "yellow" +FillPattern = "DiagonalCross" + +TextAngleUpdate = 30 + +[Building_2] +Identifier = "Building_002" + +P1 = "93049,78363" +P2 = "93071,78344" +P3 = "93062,78333" +P4 = "93041,78352" + +P11 = "0.155270,0.925980" +P22 = "0.155275,0.925977" +P33 = "0.155273,0.925975" +P44 = "0.155268,0.925978" + +TextColor = "blue" +TextSize = 16 +TextAngle = -45 +TextItalic = true +TextAntiAliased = true + +Visibility = true +VisibilityTech = true +VisibilityTact = true + +LineColor = "red" +LineWidth = 6 +LinePattern = "DashDotDot" + +FillColor = "yellow" +FillPattern = "DiagonalCross" + +TextAngleUpdate = 0 + +[DangerArea_1] +Identifier = "DangerArea_001" + +Extent = 10000 +Offset_X = 60000 +Offset_Y = 90000 + +Visibility = true +VisibilityTech = true +VisibilityTact = true + +LineColor = "red" +LineWidth = 6 +LinePattern = "DashDotDot" + +FillColor = "yellow" +FillPattern = "DiagonalCross" + +[BAgent_1] +Identifier = "BAgent_001" + +Extent = 10000 +Offset_X = 60000 +Offset_Y = 60000 + +Visibility = true +VisibilityTech = true +VisibilityTact = true + +TextColor = "magenta" +TextSize = 16 +TextBold = true + +LineColor = "cyan" +LineWidth = 6 +LinePattern = "DashDotDot" + +FillColor = "blue" +FillPattern = "DiagonalCross" + +[FireArea_1] +Identifier = "FireArea_001" +Extent = 10000 +Offset_X = 110000 +Offset_Y = 50000 + +Visibility = true +VisibilityTech = true +VisibilityTact = true + +LineColor = "red" +LineWidth = 6 +LinePattern = "Solid" + +FillColor = "blue" +FillPattern = "null" + +[HitLine_1] +Identifier = "HitLine_001" +Extent = 25000 +Offset_X = 120000 +Offset_Y = 90000 + +Visibility = true +VisibilityTech = true +VisibilityTact = true + +LineColor = "blue" +LineWidth = 3 +LinePattern = "Solid" + +[RB_PointCluster_1] +Points = 8 +P1 = "97748.9,111304" +P2 = "82617,57261.7" +P3 = "62215.9,78203.2" +P4 = "74510.6,99955.4" +P5 = "91128.7,89011.7" +P6 = "120582,105225" +P7 = "59784,94145.8" +P8 = "104639,70096.8" + +[RB_PointCluster_2] +Points = 4 +P1 = "79509.5,62260.6" +P2 = "103288,77392.5" +P3 = "88156.4,101171" +P4 = "64377.6,86039.4" + +[RB_MultiPoint_1] +P1 = "80185.1,94551.1" +P2 = "59243.5,75636.2" +P3 = "80725.5,61720.2" +P4 = "74510.6,77797.9" +P5 = "95046.8,74960.6" +FillColor = "teal" +MaxLength = 100000 +MaxSegmentLength = 50000 + +[RB_SmoothLine_1] +P1 = "64918,92254.3" +P2 = "75726.6,70637.2" +P3 = "118555,79824.5" +P4 = "93425.5,88336.2" +FillColor = "olive" + +[RB_Arrow_1] +P1 = "58973.3,102657" +P2 = "115853,82256.4" +P3 = "85454.2,53613.8" +P4 = "69106.3,92794.7" + +[RB_Rectangle_1] +// rectangle +P1 = "48570.1,66689.1" +P2 = "122609,66689.1" +P3 = "122609,103438" +P4 = "48570.1,103438" +HandleMargin = 8 + +[RB_Rectangle_2] +// rotated square +P1 = "64377.6,81783.5" +P2 = "90588.3,63138.8" +P3 = "109233,89349.5" +P4 = "83022.3,107994" +HandleMargin = 8 + +[RB_Rectangle_3] +// square +P1 = "84238.3,73339.3" +P2 = "97748.9,73339.3" +P3 = "97748.9,86850" +P4 = "84238.3,86850" +HandleMargin = 8 + +[RB_Ellipse_1] +P1 = "59648.9,66989.3" +P2 = "127337,66989.3" +P3 = "127337,92254.3" +P4 = "59648.9,92254.3" +HandleMargin = 6 + +[RB_Corridor_1] +P1 = "54109.5,63746.8" +P2 = "107477,69556.4" +P3 = "76942.5,97118.1" +P4 = "77077.6,97118.1" +P5 = "55730.8,79959.6" +FillColor = "orange" +Width = 8000 +MinWidth = 5000 +MaxWidth = 10000 +MaxLength = 1000000 +MaxSegmentLength = 200000 +MinPointCount = 3 +MaxPointCount = 8 + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/nglsettings.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/nglsettings.cpp new file mode 100755 index 000000000..0491d04b5 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/nglsettings.cpp @@ -0,0 +1,222 @@ +/** + * @RDE_HEADER@ + * Settings for sitMapTestApp + * $Id$ + * $HeadURL$ + * $Author$ + */ +#include "nglsettings.h" +#include +#include +#include +#include +#include + +using namespace NGL; + +//----------------------------------------------------------------------------------------- +namespace MAP +{ +class NGLSettingsClass : public NGL::Settings +{ + public: + NGLSettingsClass(); + ~NGLSettingsClass() {} +}; + +Q_GLOBAL_STATIC ( NGLSettingsClass, s ) + +NGLSettingsClass::NGLSettingsClass() + : NGL::Settings(QLatin1String("mapsettings.ini")) +{ +} + +NGL::Settings *Settings::settings() +{ + qDebug("%s", s()->settings()->fileName().toStdString().c_str()); + return s(); +} + +//---------------------------------------- +QString Settings::basePoint() +{ + return s()->getString(QLatin1String("Map/BasePoint"), QLatin1String("32UMD000000000000")); +} + +/* +//----------------------------------------------------------------------------------------- +double Settings::mapBasePointX() +{ + return s()->getDouble(QLatin1String("Map/BasePoint_X"), 0.0); +} + +//---------------------------------------- +QString Settings::centerPointMgrs() +{ + return s()->getString(QLatin1String("Map/Center"), QLatin1String("32UMD8000085000")); +} + +double Settings::mapCenterPointX() +{ + return s()->getDouble(QLatin1String("Map/CenterPoint_X"), 0.0); +} + +double Settings::mapCenterPointY() +{ + return s()->getDouble(QLatin1String("Map/CenterPoint_Y"), 0.0); +} + +double Settings::mapBasePointY() +{ + return s()->getDouble(QLatin1String("Map/BasePoint_Y"), 0.0); +} +*/ + +int Settings::mapWkid() +{ + return s()->getInteger(QLatin1String("Map/Wkid"), 32632); +} + +double Settings::mapObjectsTextScale() +{ + return s()->getDouble(QLatin1String("MapObjects/TextScale"), 1.0); +} + +double Settings::mapScale() +{ + return s()->getDouble(QLatin1String("Map/Scale"), 100000.0); +} + +double Settings::mapRotationDegree() +{ + return s()->getDouble(QLatin1String("Map/RotationDegree"), 0.0); +} +/* +int Settings::XMin() +{ + return s()->getInteger(QLatin1String("Map/XMin"), 0); +} + +int Settings::YMin() +{ + return s()->getInteger(QLatin1String("Map/YMin"), 0); +} + +int Settings::XMax() +{ + return s()->getInteger(QLatin1String("Map/XMax"), 0); +} + +int Settings::YMax() +{ + return s()->getInteger(QLatin1String("Map/YMax"), 0); +} +*/ +bool Settings::mapGridVisible() +{ + return s()->getBoolean(QLatin1String("Map/GridVisible"), false); +} + +bool Settings::mapFunctionDialogVisible() +{ + return s()->getBoolean(QLatin1String("Map/FunctionDialogVisible"), false); +} + +QString Settings::mapSymbologyType() +{ + return s()->getString(QLatin1String("Map/SymbologyType"), QLatin1String("APP6B") ); +} + +QString Settings::mapGridType() +{ + return s()->getString(QLatin1String("Map/GridType"), QLatin1String("MGRS") ); +} + +QString Settings::mapURL() +{ + return s()->getString(QLatin1String("Map/URL"), QString() ); +} + +// conversion functions +//------------------------------------------------------------------------------------- +SituationMap::LinePattern Settings::getLinePattern(const QString &pattern) +{ + SituationMap::LinePattern linePattern = SituationMap::LinePattern::Null; + if (pattern.compare(QLatin1String("Dash")) == 0) + linePattern = SituationMap::LinePattern::Dash; + if (pattern.compare(QLatin1String("DashDot")) == 0) + linePattern = SituationMap::LinePattern::DashDot; + if (pattern.compare(QLatin1String("DashDotDot")) == 0) + linePattern = SituationMap::LinePattern::DashDotDot; + if (pattern.compare(QLatin1String("Dot")) == 0) + linePattern = SituationMap::LinePattern::Dot; + if (pattern.compare(QLatin1String("Solid")) == 0) + linePattern = SituationMap::LinePattern::Solid; + return linePattern; +} + +SituationMap::FillPattern Settings::getFillPattern(const QString &pattern) +{ + SituationMap::FillPattern fillPattern = SituationMap::FillPattern::Null; + if (pattern.compare(QLatin1String("BackwardDiagonal")) == 0) + fillPattern = SituationMap::FillPattern::BackwardDiagonal; + if (pattern.compare(QLatin1String("Cross")) == 0) + fillPattern = SituationMap::FillPattern::Cross; + if (pattern.compare(QLatin1String("DiagonalCross")) == 0) + fillPattern = SituationMap::FillPattern::DiagonalCross; + if (pattern.compare(QLatin1String("ForwardDiagonal")) == 0) + fillPattern = SituationMap::FillPattern::ForwardDiagonal; + if (pattern.compare(QLatin1String("Solid")) == 0) + fillPattern = SituationMap::FillPattern::Solid; + if (pattern.compare(QLatin1String("Horizontal")) == 0) + fillPattern = SituationMap::FillPattern::Horizontal; + if (pattern.compare(QLatin1String("Vertical")) == 0) + fillPattern = SituationMap::FillPattern::Vertical; + return fillPattern; +} + +SituationMap::GridType Settings::getGridType(const QString &pattern) +{ + SituationMap::GridType gridType = SituationMap::GridType::MGRS; + if (pattern.compare(QLatin1String("MGRS")) == 0) + gridType = SituationMap::GridType::MGRS; + if (pattern.compare(QLatin1String("UTM")) == 0) + gridType = SituationMap::GridType::UTM; + if (pattern.compare(QLatin1String("USGN")) == 0) + gridType = SituationMap::GridType::USGN; + return gridType; +} + +//map objects general functions +//----------------------------------------------------------------------------------------- +QString Settings::objectIdentifier(const QString &prefix, int id) +{ + return s()->getString(prefix+QLatin1String("_")+QString::number(id)+QLatin1String("/Identifier"), prefix+QLatin1String("_")+QString::number(id)); +} + +bool Settings::getObjectBoolAttribute(const QString &prefix, const QString &postfix, int objId, bool defaultValue) +{ + return s()->getBoolean(prefix+QLatin1String("_")+QString::number(objId)+QLatin1String("/")+postfix, defaultValue); +} + +int Settings::getObjectIntAttribute(const QString &prefix, const QString &postfix, int objId, int defaultValue) +{ + return s()->getInteger(prefix+QLatin1String("_")+QString::number(objId)+QLatin1String("/")+postfix, defaultValue); +} + +QString Settings::getObjectStringAttribute(const QString &prefix, const QString &postfix, int objId, const QString &defaultValue) +{ + return s()->getString(prefix+QLatin1String("_")+QString::number(objId)+QLatin1String("/")+postfix, defaultValue); +} + +QPointF Settings::getPoint(const QString &prefix, int objId, int nr ) +{ + QString strPoint = s()->getString(prefix+QLatin1String("_")+QString::number(objId)+QLatin1String("/P")+QString::number(nr), QLatin1String("0,0")); + double point_x, point_y; + QStringList list = strPoint.split(QLatin1Char(',')); + point_x = list[0].toDouble(); + point_y = list[1].toDouble(); + return QPointF(point_x,point_y); +} + +} //namespace diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/nglsettings.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/nglsettings.h new file mode 100755 index 000000000..c5b0ba033 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/nglsettings.h @@ -0,0 +1,63 @@ +/** + * @RDE_HEADER@ + * Settings for sitMapTestApp + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include +#include +#include +#include + + +namespace NGL { + class Settings; +} + +namespace MAP +{ +namespace Settings +{ + // read map settings + double mapBasePointX(); + double mapBasePointY(); + double mapCenterPointX(); + double mapCenterPointY(); + int mapWkid(); + double mapObjectsTextScale(); + double mapScale(); + bool mapGridVisible(); + bool mapFunctionDialogVisible(); + QString mapGridType(); + double mapRotationDegree(); + QString mapSymbologyType(); + QString mapURL(); + QString centerPointMgrs() ; + QString basePoint(); + int XMin(); + int YMin(); + int XMax(); + int YMax(); + +// general functions for map objects + QString objectIdentifier(const QString &prefix, int id); + bool getObjectBoolAttribute(const QString &prefix, const QString &postfix, int objId, bool defaultValue = false); + int getObjectIntAttribute(const QString &prefix, const QString &postfix, int objId, int defaultValue = 0); + QString getObjectStringAttribute(const QString &prefix, const QString &postfix, int objId, const QString &defaultValue = QString()); + QPointF getPoint(const QString &prefix, int objId, int nr ); + +// conversion functions from strings + SituationMap::LinePattern getLinePattern(const QString &pattern); + SituationMap::FillPattern getFillPattern(const QString &pattern); + SituationMap::GridType getGridType(const QString &pattern); + + //!< The real NGL::Settings - object + NGL::Settings *settings(); + +} +}; + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/ui/mainwindow.ui b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/ui/mainwindow.ui new file mode 100755 index 000000000..fae6c9103 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SitMapTestApp/ui/mainwindow.ui @@ -0,0 +1,594 @@ + + + widgetMain + + + + 0 + 0 + 800 + 641 + + + + + 0 + 0 + + + + Qt::NoContextMenu + + + Situation Map Test Application + + + + :/Rde/RdeIcon:/Rde/RdeIcon + + + Situation Map Test Application + + + + 14 + 14 + + + + + + + + + + + + + 0 + 0 + 800 + 19 + + + + + &Map + + + + + + + + + Map &Objects + + + + &Buildings + + + + + + + + + &Danger Areas + + + + + + + + + B-&Agents + + + + + + + + + &Fire Areas + + + + + + + + + Hit &Lines + + + + + + + + + + + + + + + + + + + + &Rubberbands + + + + &Rectangle + + + + + + + S&quare + + + + + + + &Ellipse + + + + + + + &Point Cluster + + + + + + + &Multipoint + + + + + + + &Filled Multipoint + + + + + + + &Smooth Line + + + + + + + Filled Smooth &Line + + + + + + + Corridor &Boundary + + + + + + + Corridor &Centerline + + + + + + + &Arrow + + + + + + + + + + + + + + + + + + + + + + + + + + &Load Map + + + Load Map + + + Loading default map + + + + + &Remove Map + + + Removing current map + + + + + E&xit + + + Exit application + + + + + &Create + + + Create Buildings + + + + + &Remove + + + Remove Buildings + + + + + &Update + + + Update Buildings + + + + + &Create + + + Create Danger Areas + + + + + &Remove + + + Remove Danger Areas + + + + + &Update + + + Update Danger Areas + + + + + &Create + + + Create B Agents + + + + + &Remove + + + Remove B Agents + + + + + &Update + + + Update B Agents + + + + + &Create + + + Create Fire Areas + + + + + &Remove + + + RemoveFireAreas + + + + + &Update + + + Update Fire Areas + + + + + &Create + + + Create Fire Lines + + + + + &Remove + + + Remove Fire Lines + + + + + &Update + + + Update Hit Lines + + + + + &Rubberbands + + + + + &Create + + + Create RB Rectangle + + + + + &Draw + + + Create RB Rectangle Free Hand + + + + + &Create + + + Create RB Square + + + + + &Draw + + + Create RB Square Free Hand + + + + + &Create + + + Create RB Ellipse + + + + + &Draw + + + Draw RB Ellipse + + + + + &Create + + + Create RB PointCluster + + + + + &Draw + + + Draw RB PointCluster + + + + + &Create + + + Create RB MultiPoint + + + + + &Draw + + + Draw RB MultiPoint + + + + + &Create + + + Create RB Filled MultiPoint + + + + + &Draw + + + Draw RB FilledMultiPoint + + + + + &Create + + + Create RB SmoothLine + + + + + &Draw + + + Draw RB SmoothLine + + + + + &Create + + + Create RB FilledSmoothLine + + + + + Draw + + + Draw RB FilledSmoothLine + + + + + &Create + + + Create RB Corridor Boundary + + + + + &Draw + + + Draw RB Corridor Boundary + + + + + &Create + + + Create RB Corridor Centerline + + + + + &Draw + + + Draw RB Corridor Centerline + + + + + &Create + + + Create RB Arrow + + + + + &Draw + + + Draw RB Arrow + + + + + &Hide/Show + + + Toggle Hide/Show Buildings + + + + + &Hide/Show + + + Toggle Hide/Show Danger Areas + + + + + &Hide/Show + + + Toggle Hide/Show B-Agents + + + + + &Hide/Show + + + Toggle Hide/Show Fire Areas + + + + + &Hide/Show + + + Toggle Hide/Show Hit Lines + + + + + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SituationMap/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SituationMap/CMakeLists.txt new file mode 100755 index 000000000..2030d1311 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SituationMap/CMakeLists.txt @@ -0,0 +1,53 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +# SIT_MAP +SET(SIT_MAP_EXE_SRCS + sit_map_main.cpp + sit_map.cpp + sit_map.h + + log4cxx_sitmapMain.props +) + + +NGL_ADD_EXECUTABLE(WS NO_TRANSLATION sitMapExample ${SIT_MAP_EXE_SRCS}) +NGL_TARGET_LINK_LIBRARIES(sitMapExample + Qt6::Network + NGLLogger + nglapplication + LtsPluginManager + LtsPluginLoader + situationMap + situationMapObjects + situationMapFunctionDialog + situationMapSideBar + + advancedSymbologyGui + + ArcGIS::ArcGIS + ArcGISQt5Hack +# situationMapSideBar +) + +# Copy on LIBchanges: +IF(TARGET sitMapExample) + SET(LOG4CXX_PROPS_FILE log4cxx_sitmapMain.props) + ADD_CUSTOM_COMMAND(TARGET sitMapExample POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${LOG4CXX_PROPS_FILE} $/${LOG4CXX_PROPS_FILE} + ) +ENDIF() + +IF(MSVC) + # Configure the template file + SET(VS_USER_FILE sitMapExample.vcxproj.user) + configure_file( ../../SituationMap.vcxproj.user.in ${CMAKE_CURRENT_BINARY_DIR}/${VS_USER_FILE} @ONLY) +ENDIF(MSVC) + +NGL_INSTALL_TARGET(sitMapExample) +NGL_INSTALL(FILES WS ${CMAKE_CURRENT_SOURCE_DIR}/${LOG4CXX_PROPS_FILE} DESTINATION "bin") + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SituationMap/log4cxx_sitmapMain.props b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SituationMap/log4cxx_sitmapMain.props new file mode 100755 index 000000000..0a92b46db --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SituationMap/log4cxx_sitmapMain.props @@ -0,0 +1,175 @@ +log4j.reset=true +log4j.rootLogger=INFO, file, stdout + +# SitMap +########################################################### +log4j.logger.SituationMap.sitmapMain=INFO +log4j.logger.SituationMap.SitMap=INFO + +# AdvancedSymbology (AdvSymbology) +########################################################### +#log4j.logger.AdvSymbology.SymbolDictionary=INFO +#log4j.logger.AdvSymbology.SymbolDictionaryImpl=INFO +#log4j.logger.AdvSymbology.EsriDictionaryProcessor=INFO +#log4j.logger.AdvSymbology.SymbolResult=INFO +#log4j.logger.AdvSymbology.Sidc2525Delta=INFO + +# SituationMap +########################################################### +#log4j.logger.SituationMap.INFO +#log4j.logger.SituationMap.MapImpl=INFO +#log4j.logger.SituationMap.MapSettings=INFO +#log4j.logger.SituationMap.MapInitializer=INFO +#log4j.logger.SituationMap.JumpPoints=INFO +#log4j.logger.SituationMap.LayerFactoryImpl=INFO +#log4j.logger.SituationMap.LogicalLayer=INFO +#log4j.logger.SituationMap.LayerControl=INFO +#log4j.logger.SituationMap.CoordTranslationFunctorImpl=INFO +#log4j.logger.SituationMap.CoordTranslationThread=INFO +#log4j.logger.SituationMap.PositionsTranslated=INFO +#log4j.logger.SituationMap.GridImpl=INFO +#log4j.logger.SituationMap.ObjectFactory=INFO +#log4j.logger.SituationMap.IdGenerator=INFO +#log4j.logger.SituationMap.ObjectContainer=INFO +#log4j.logger.SituationMap.ObjectContainerCTC=INFO +#log4j.logger.SituationMap.OverlayHandlerImpl=INFO +#log4j.logger.SituationMap.OverviewMapHandlerImpl=INFO +#log4j.logger.SituationMap.OverviewMap=INFO +log4j.logger.SituationMap.LocationDisplayImpl=TRACE +log4j.logger.SituationMap.ViewshedImpl=TRACE +log4j.logger.SituationMap.ViewshedBase=TRACE + +log4j.logger.SituationMap.AbstractMapObjectImpl=INFO +log4j.logger.SituationMap.AbstractEffectSimulationImpl=INFO +log4j.logger.SituationMap.OriginTranslated=INFO +log4j.logger.SituationMap.PositionsTranslated=INFO +log4j.logger.SituationMap.AbstractMultiPointMapObjectImpl=INFO +log4j.logger.SituationMap.AbstractSinglePointMapObjectImpl=INFO +log4j.logger.SituationMap.BasicPathObjectImpl=INFO +log4j.logger.SituationMap.IndicationContainer=INFO + +log4j.logger.SituationMap.PolygonImpl=INFO +log4j.logger.SituationMap.PolylineImpl=INFO +log4j.logger.SituationMap.TextImpl=INFO + +log4j.logger.SituationMap.AreaFireImpl=INFO +log4j.logger.SituationMap.BiologicalAgentImpl=INFO +log4j.logger.SituationMap.BuildingOutlineImpl=INFO +log4j.logger.SituationMap.ChemicalAgentImpl=INFO +log4j.logger.SituationMap.DangerAreaImpl=INFO +log4j.logger.SituationMap.FunctionalAreaImpl=INFO +log4j.logger.SituationMap.GeoImageImpl=INFO +log4j.logger.SituationMap.HitLineImpl=INFO +log4j.logger.SituationMap.MapObjectIndicatorImpl=INFO +log4j.logger.SituationMap.MineImpl=INFO +log4j.logger.SituationMap.MineFieldImpl=INFO +log4j.logger.SituationMap.NuclearAgentImpl=INFO +log4j.logger.SituationMap.ParticipantImpl=INFO +log4j.logger.SituationMap.SingleFireImpl=INFO + +log4j.logger.SituationMap.AbstractGraphicalObject=INFO +log4j.logger.SituationMap.GraphicalObjectEllipse=INFO +log4j.logger.SituationMap.GraphicalObjectPath=INFO +log4j.logger.SituationMap.GraphicalObjectMultiPath=INFO +log4j.logger.SituationMap.GraphicalObjectPictureMarker=INFO +log4j.logger.SituationMap.GraphicalObjectRange=INFO +log4j.logger.SituationMap.GraphicalObjectText=INFO +log4j.logger.SituationMap.TacticalAppearance=INFO + +log4j.logger.SituationMap.EventDispatcher=INFO +log4j.logger.SituationMap.Rubberband=DEBUG +log4j.logger.SituationMap.RubberbandRectangle=INFO +log4j.logger.SituationMap.RubberbandDrawablesAndHandles=INFO +log4j.logger.SituationMap.RubberbandFrameHandle=INFO +log4j.logger.SituationMap.RubberbandPointHandle=INFO +log4j.logger.SituationMap.RubberbandStateSelect=INFO + +log4j.logger.SituationMap.ObjectIndicatorImpl=INFO + + +# SituationMap-SideBar +########################################################### +log4j.logger.SituationMap.SideBar=INFO +log4j.logger.SituationMap.SideBarItem=INFO +log4j.logger.SituationMap.SideBarModel=INFO + + +# PlanEditor +########################################################### +log4j.logger.SituationMap.PlanEditor=TRACE +log4j.logger.SituationMap.PlanRenderer=TRACE + + + +# Appnd_File +########################################################### +log4j.appender.file=org.apache.log4j.RollingFileAppender +log4j.appender.file.MaxFileSize=1MB +log4j.appender.file.MaxBackupIndex=9 +log4j.appender.file.file=${LOG4QT_LOGDIR}/${LOG4QT_USER}_SituationMap.log +log4j.appender.file.layout=org.apache.log4j.PatternLayout +log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n + + +# Appnd_Console +########################################################### +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern= %d %-5p %c{1} |#| %m (%-.30F:%L) %n + +#log4j.appender.stdout.layout.ConversionPattern= %d %-5p %-30c |#| %m (%-.30F:%L) %n + + +# Appnd_Syslog +########################################################### +log4j.appender.syslog=org.apache.log4j.SyslogAppender +log4j.appender.syslog.Threshold=ERROR +log4j.appender.syslog.SysLogHost=localhost +log4j.appender.syslog.Facility=LOCAL7 +log4j.appender.syslog.layout=org.apache.log4j.PatternLayout +log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m +log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true +log4j.appender.syslog.LevelRangeFilter.LevelMin=INFO +log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL + + +########################################################### +########################################################### +########################################################### +# 1) Available Levels in LOG4CXX Version 0.10.0: +# TRACE, +# DEBUG, +# INFO, +# WARN, +# ERROR +# FATAL +# +# d = date/time +# p = priority (Level) +# c = category/channel +# m = message +# F = file +# L = line +# n = platform dependent line separator character or characters. +# .... +# +# Examples for console: +# a) +# %d %p %c #%m# (%.50F:%L)%n +# date/time +# priority +# category/channel +# #message# +# (Maximal 50Character des Filename von rechts:Line) +# line separator +# +# b) +# %d (%.15F:%L) %-5p %c |#| %m%n +# date/time +# (Maximal 15Character des Filename von rechts:Line) +# priority, Right pad with spaces if the priority name is less than 5 characters long. +# category/channel +# message +# line separator +# + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SituationMap/sit_map.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SituationMap/sit_map.cpp new file mode 100755 index 000000000..5b641105d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SituationMap/sit_map.cpp @@ -0,0 +1,688 @@ +/** + * @RDE_HEADER@ + * Interface + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "sit_map.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +// PlanModel: + +#include +#include +#include + + +// Qt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//----------------------------------------------------------------------------- + + +class myMapInitializer : public SituationMap::MapInitializer +{ + Q_OBJECT + + using BaseClass = SituationMap::MapInitializer; + +public: + explicit myMapInitializer(SituationMap::Map &map + , SituationMap::MapSettings &mapSettings + , const QString &debugName) + : SituationMap::MapInitializer(map, mapSettings, debugName) + {} + + bool initializeObjectFactory() override + { + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::MapObjectIndicator), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Cube), new SituationMap::GenericObjectFactory() ); + + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Polygon), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Polyline), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Text), new SituationMap::GenericObjectFactory() ); + + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Participant), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::AreaFire), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::BiologicalAgent), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::ChemicalAgent), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::BuildingOutline), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::DangerArea), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::FunctionalArea), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::GeoImage), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::HitLine), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::LineOfSight), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::MapObjectAttributes), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Mine), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::MineField), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::NuclearAgent), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::SingleFire), new SituationMap::GenericObjectFactory() ); +// SituationMap::ObjectFactory::registerMe(typeid(SituationMap::SingleFireCentral), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::SoundArea), new SituationMap::GenericObjectFactory() ); + + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::SidcSymbol), new SituationMap::GenericObjectFactory() ); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::SightFan), new SituationMap::GenericObjectFactory()); + + return true; + } // initializeObjectFactory() + + AdvancedSymbology::SymbologyType symbologyType() const override + { + return m_symbologyType; + } // symbologyType + + void setSymbologyType(AdvancedSymbology::SymbologyType symbologyType) + { + m_symbologyType = symbologyType; + } + +protected: + AdvancedSymbology::SymbologyType m_symbologyType = AdvancedSymbology::SymbologyType::APP6B; + +}; +//----------------------------------------------------------------------------- + + +class SitMap::Private +{ +public: + Private() = default; + ~Private() = default; + + // map + QPointer m_map = nullptr; + + bool m_demoMode = false; + + QStackedLayout* m_stackedLayout = nullptr; + + + QSharedPointer m_mapSettings; + QSharedPointer m_mapInitializer; + + // admin + QGridLayout* m_mainLayout = nullptr; + QPushButton* m_pbDestroyMap = nullptr; + QPushButton* m_pbToggleMode = nullptr; + SituationMap::Viewpoint2D m_viewpoint4Toggle; + QPushButton* m_pbQuit = nullptr; + QComboBox* m_cbIniFile = nullptr; + QString m_initFilePath; + QComboBox* m_cb2D3D = nullptr; + QComboBox* m_cbSymbologyType = nullptr; + + + int symbologyType() const + { + if (m_cbSymbologyType) + { + return m_cbSymbologyType->currentData().toInt(); + } + return -1; + } + + + QPushButton* m_pbPlan = nullptr; + + QMenu* m_popupMenu = nullptr; + + QTimer* m_getRotationTimer = nullptr; + +}; // class SitMap::Private +//----------------------------------------------------------------------------- + + + + +//----------------------------------------------------------------------------- +//SitMap +//----------------------------------------------------------------------------- +SitMap::SitMap(bool demoMode, QWidget *parent) + : QDialog(parent) + , m_logger(SituationMap::getLogger("SitMap")) + , d(new Private) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + this->setObjectName( QLatin1String("SitMapExample-Widget") ); + + d->m_mainLayout = new QGridLayout(this); + d->m_mainLayout->setObjectName( QLatin1String("SitMapExample-MainLayoutGrid") ); + + //----------------------------------- + // Buttons + //----------------------------------- + QVariant data; + + QGridLayout* topLayout = new QGridLayout(); + topLayout->setObjectName( QLatin1String("SitMapExample-TopLayoutGrid") ); + d->m_mainLayout->addLayout(topLayout, 0, 0); + + + d->m_cbIniFile = new QComboBox(); + d->m_cbIniFile->setObjectName( QLatin1String("SitMapExample-IniFileComboBox") ); + topLayout->addWidget(d->m_cbIniFile, 0, 0); + + d->m_cbIniFile->addItem(QStringLiteral("situation_map_defaults.ini"), QLatin1String("situation_map_defaults.ini")); + d->m_cbIniFile->addItem(QStringLiteral("situation_map_ahtc.ini"), QLatin1String("situation_map_ahtc.ini")); + d->m_cbIniFile->addItem(QStringLiteral("situation_map_demo_center.ini"), QLatin1String("situation_map_demo_center.ini")); + d->m_cbIniFile->addItem(QStringLiteral("situation_map_guez.ini"), QLatin1String("situation_map_guez.ini")); + d->m_cbIniFile->addItem(QStringLiteral("situation_map_land400.ini"), QLatin1String("situation_map_land400.ini")); + d->m_cbIniFile->addItem(QStringLiteral("situation_map_qatar.ini"), QLatin1String("situation_map_qatar.ini")); + d->m_cbIniFile->addItem(QStringLiteral("situation_map_luxemburg.ini"), QLatin1String("situation_map_luxemburg.ini")); + d->m_cbIniFile->addItem(QStringLiteral("select"), QString()); + onIniFileActivated(d->m_cbIniFile->currentText()); + + connect(d->m_cbIniFile, QOverload::of(&QComboBox::activated), + this, &SitMap::onIniFileActivated); + + + d->m_cbSymbologyType = new QComboBox(); + d->m_cbSymbologyType->setObjectName( QLatin1String("SitMapExample-SymbologyComboBox") ); + topLayout->addWidget(d->m_cbSymbologyType, 0, 1); + + + // debugging : + if (0) + { + // highlighted -> UserInteraction + connect(d->m_cbSymbologyType, static_cast(&QComboBox::highlighted), this, [](int idx){ + qCritical() << " called: highlighted(int) " << idx; + }); + + // currentIndexChanged -> programmatically & UserInteraction + connect(d->m_cbSymbologyType, static_cast(&QComboBox::currentIndexChanged), this, [](int idx){ + qCritical() << " called: currentIndexChanged(int) " << idx; + }); + + // activated -> UserInteraction + connect(d->m_cbSymbologyType, static_cast(&QComboBox::activated), this, [](int idx) + { + qCritical() << " called: activated(int) " << idx; + }); + + } // + + // : debugging + + + d->m_cb2D3D = new QComboBox(); + d->m_cb2D3D->setObjectName( QLatin1String("SitMapExample-cb2D3D") ); + data.setValue(SituationMap::Mode::Mode_2D); + d->m_cb2D3D->addItem(QStringLiteral("2D"), data); + data.setValue(SituationMap::Mode::Mode_3D); + d->m_cb2D3D->addItem(QStringLiteral("3D"), data); + topLayout->addWidget(d->m_cb2D3D, 0, 2); + + d->m_pbDestroyMap = new QPushButton( QLatin1String("Create Map") ); + d->m_pbDestroyMap->setObjectName( QLatin1String("SitMapExample-CreateDestroyMapButton") ); + topLayout->addWidget(d->m_pbDestroyMap, 0, 3); + connect(d->m_pbDestroyMap, &QPushButton::clicked, this, &SitMap::onCreateDestroyClicked); + + d->m_pbToggleMode = new QPushButton( QLatin1String("Toggle Mode") ); + d->m_pbToggleMode->setObjectName( QLatin1String("SitMapExample-ToggleModeButton") ); + topLayout->addWidget(d->m_pbToggleMode, 0, 4); + connect(d->m_pbToggleMode, &QPushButton::clicked, this, &SitMap::onToggleModeClicked); + d->m_pbToggleMode->setDisabled(true); + + + AdvancedSymbology::SymbologyType symType; + + symType = AdvancedSymbology::SymbologyType::MIL2525D; + data.setValue(symType); d->m_cbSymbologyType->addItem(AdvancedSymbology::toString(symType), data); + + symType = AdvancedSymbology::SymbologyType::MIL2525C; + data.setValue(symType); d->m_cbSymbologyType->addItem(AdvancedSymbology::toString(symType), data); + + symType = AdvancedSymbology::SymbologyType::APP6B; + data.setValue(symType); d->m_cbSymbologyType->addItem(AdvancedSymbology::toString(symType), data); + + symType = AdvancedSymbology::SymbologyType::APP6D; + data.setValue(symType); d->m_cbSymbologyType->addItem(AdvancedSymbology::toString(symType), data); + + symType = AdvancedSymbology::SymbologyType::None; + data.setValue(symType); d->m_cbSymbologyType->addItem(AdvancedSymbology::toString(symType), data); + + d->m_cbSymbologyType->setCurrentText(AdvancedSymbology::toString(AdvancedSymbology::SymbologyType::APP6D)); + + //----------------------------------- + // Map + //----------------------------------- +// createMap(); + + { + QGridLayout* bottomLayout = new QGridLayout(); + bottomLayout->setObjectName( QLatin1String("SitMapExample-TopBottomLayoutGrid") ); + d->m_mainLayout->addLayout(bottomLayout, 2, 0); + + d->m_pbPlan = new QPushButton( QLatin1String("Plan") ); + d->m_pbPlan->setObjectName( QLatin1String("SitMapExample-Plan") ); + connect(d->m_pbPlan, &QPushButton::clicked, this, &SitMap::onPlanClicked); + bottomLayout->addWidget(d->m_pbPlan, 0, 0); + + + d->m_pbQuit = new QPushButton( QLatin1String("Quit") ); + d->m_pbQuit->setObjectName( QLatin1String("SitMapExample-Quit") ); + bottomLayout->addWidget(d->m_pbQuit, 1, 0); + connect(d->m_pbQuit, &QPushButton::clicked, []() { + QCoreApplication::quit(); + }); + + // tbc + + } + + if (demoMode) + { + // topLayout + d->m_cbSymbologyType->hide(); + d->m_cb2D3D->hide(); + d->m_pbDestroyMap->hide(); + + // bottomLayout + d->m_pbPlan->hide(); + + // createMap: + onCreateDestroyClicked(); + + } // if (demoMode) + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +SitMap::~SitMap() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + delete d; + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +SituationMap::Map *SitMap::map() +{ + return d->m_map; +} + +void SitMap::onCreateDestroyClicked() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + static bool destroy = true; + destroy = !destroy; + if (destroy) + { + d->m_cbIniFile->setEnabled(true); + d->m_cbSymbologyType->setEnabled(true); + d->m_cb2D3D->setEnabled(true); + d->m_pbDestroyMap->setText( QLatin1String("Create Map") ); + if (d->m_getRotationTimer) + d->m_getRotationTimer->stop(); // to avoid slot being called + delete d->m_map; + } else { + d->m_cbIniFile->setEnabled(false); + d->m_cbSymbologyType->setEnabled(false); + d->m_cb2D3D->setEnabled(false); + d->m_pbDestroyMap->setText( QLatin1String("Destroy Map") ); + createMap(); + } + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +void SitMap::onToggleModeClicked() +{ + NOTICE_LOG(m_logger) << LOGFUNCTION << " START" << " curMode=" << d->m_cb2D3D->currentText(); + + d->m_pbToggleMode->setDisabled(true); + + d->m_viewpoint4Toggle = d->m_map->viewpoint2D(); + qCritical() << " save last d->m_viewpoint4Toggle=" << d->m_viewpoint4Toggle.toString(); + + // toogle mode: + SituationMap::Mode newMapMode = SituationMap::toggleMode(d->m_map->mode()); + + // destroy: + onCreateDestroyClicked(); + + // rebuild + QVariant data; + data.setValue(newMapMode); + int idx = d->m_cb2D3D->findData(data); + d->m_cb2D3D->setCurrentIndex(idx); + + // create + onCreateDestroyClicked(); +} + +void SitMap::createMap() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + d->m_map = SituationMap::Map::create(this); + d->m_map->setObjectName(QLatin1String("SitMapExample-Map")); + + // d->m_mainLayout->addWidget(d->m_map, 1, 0); + + if (!d->m_stackedLayout) + d->m_stackedLayout = new QStackedLayout(); + d->m_stackedLayout->setStackingMode(QStackedLayout::StackAll); + d->m_mainLayout->addLayout(d->m_stackedLayout, 1, 0); + d->m_stackedLayout->addWidget(d->m_map); + + NOTICE_LOG(m_logger) << LOGFUNCTION << " iniFile = '" << d->m_initFilePath << "'"; + + d->m_mapSettings = QSharedPointer ( new SituationMap::MapSettings(d->m_initFilePath) ); + + d->m_mapInitializer = QSharedPointer + ( new myMapInitializer(*d->m_map, *d->m_mapSettings, QLatin1String("SitMapExample")) ); + + // toggle? + if (! d->m_viewpoint4Toggle.isNull()) + d->m_mapInitializer->setViewPoint(d->m_viewpoint4Toggle); + + + // Take SymbologyType from UI and not from ini file: + int symbologyTypeInt = d->symbologyType(); + if (symbologyTypeInt == -1) + { + ERROR_LOG(m_logger) << LOGFUNCTION << " Error retrieving symbologyType"; + abort(); + } + + AdvancedSymbology::SymbologyType symbologyTypeUI = + static_cast(symbologyTypeInt); + d->m_mapInitializer->setSymbologyType(symbologyTypeUI); + + + if (d->m_mapInitializer->hasErrors()) + { + FATAL_LOG(m_logger) << LOGFUNCTION << " MapInitializer-filename='" << d->m_mapInitializer->filename() + << "'' has following errors: " + << " exists = " << d->m_mapInitializer->exists() + << " accessError = " << d->m_mapInitializer->accessError() + << " formatError = " << d->m_mapInitializer->formatError(); + return; + } else { + const SituationMap::Mode mapMode = d->m_cb2D3D->currentData().value(); + if (d->m_mapInitializer->initMap(mapMode) ) + { + INFO_LOG(m_logger) << LOGFUNCTION << " MapInitializer::initMap() ok"; + } else { + ERROR_LOG(m_logger) << LOGFUNCTION << " MapInitializer::initMap() failed"; + return; + } + } + + // 50. turn over to application (if required): + + d->m_map->setTimeString(QStringLiteral("#")); + + +// connect (d->m_map.data(), &SituationMap::Map::mapObjectsClicked, this, &SitMap::onMapObjectClicked); + connect( d->m_map.data() + , &SituationMap::Map::contextMenuClicked + , this + , &SitMap::onMapContextClicked ); + + + if (d->m_map->isReady()) + d->m_pbToggleMode->setEnabled(true); + else + connect(d->m_map.data(), &SituationMap::Map::mapReady, this, [this]() { + d->m_pbToggleMode->setEnabled(true); + }); + + + + // Create FunctionDialog: + SituationMap::FunctionDialog::addButtonToMap(d->m_map); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; + +} // SitMap::createMap + + +static QVector>& participantMap() +{ + static QVector> s_participantCont; + return s_participantCont; +} + +void SitMap::onPlanClicked() +{ + if (!d->m_map || !d->m_map->isReady()) + return; + + const SituationMap::PartyIdToColorFunctor::PartyData pData1(QStringLiteral("P1"), + AdvancedSymbology::Role::Friend, + Qt::blue, Qt::blue); + const SituationMap::PartyIdToColorFunctor::PartyData pData2(QStringLiteral("P2"), + AdvancedSymbology::Role::HostileFaker, + Qt::red, Qt::red); + d->m_map->partyIdToColorFunctor().setParty(1, pData1); + d->m_map->partyIdToColorFunctor().setParty(2, pData2); + + const QSize windowSize = this->size(); + const QSize offset(50, 50); + + SituationMap::LayerFactory &layerFac = d->m_map->layerFactory(); + QStringList layernames = layerFac.layernames(SituationMap::LayerType::Operational); + QString layername = layernames[0]; + layername = QStringLiteral("Participants"); + SituationMap::ObjectFactory &objFac = d->m_map->objectFactory(); + + SituationMap::RefPointer participant; + + + int counter = 0; + int partyId = 1; + for (int x = offset.width(); xm_map->viewPortWindowPointToGeoCoordinate(QPointF(x,y)); + + SituationMap::SymbolData symData; + symData.sidc = QStringLiteral("AP6D|CMPL|10031000111205000000"); + symData.color = Qt::cyan; // Will be overwritten via partyId. + + + participant = objFac.create(layername, debugName); + participantMap().append(participant); + + participant->setOrigin(SituationMap::Point3D::fromQPointF(geoCoord)); + participant->setSymbol(symData); + + if (partyId == 2) + partyId = 1; + else + partyId = 2; + + participant->setPartyId(partyId); + + if (!participant->updateView()) + { + qCritical() << "updateView failed: debugName='" << debugName << "'"; + abort(); + } + + } // for + } // for + + + QTimer *timer = new QTimer(this); + timer->setInterval(150); + static int s_partyId = 1; + static int s_finishCounter=0; + connect(timer, &QTimer::timeout, this, [timer]() { + + qCritical() << " new partyId= " << s_partyId << " count=" << participantMap().size() << ")"; + + ++s_finishCounter; + if (s_finishCounter>10) + timer->stop(); + + int counter = rand()%5; + + for(auto participant : std::as_const(participantMap())) + { + int curPartyId = participant->partyId(); + if (curPartyId == 2) + curPartyId = 1; + else + curPartyId = 2; + + participant->setPartyId(curPartyId); + + SituationMap::SymbolData symData = participant->symbol(); + int mod = ((++counter)%9); + switch (mod) + { + case 0: symData.sidc = QStringLiteral("AP6D|CMPL|10031000111205000000"); break; + case 1: symData.sidc = QStringLiteral("AP6D|CMPL|10031500001202000000"); break; + case 2: symData.sidc = QStringLiteral("AP6D|CMPL|10031500001302000000"); break; + case 3: symData.sidc = QStringLiteral("AP6D|CMPL|10031500001402000000"); break; + case 4: symData.sidc = QStringLiteral("AP6D|CMPL|10031500001502000000"); break; + case 5: symData.sidc = QStringLiteral("AP6D|CMPL|10031500001602000000"); break; + case 6: symData.sidc = QStringLiteral("AP6D|CMPL|10031500001702000000"); break; + case 7: symData.sidc = QStringLiteral("AP6D|CMPL|10031500001802000000"); break; + case 8: symData.sidc = QStringLiteral("AP6D|CMPL|10031500001902000000"); break; + } + participant->setSymbol(symData); + + if (!participant->updateView()) + { + qCritical() << "updateView failed:"; + abort(); + } + } + }); + timer->start(); +} + +void SitMap::onMapObjectClicked(const SituationMap::Map::RefPointerMapObjectList & mapObjList, const QPointF &viewportPosition) +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << "START onMapObjectClicked" + << " viewportPosition=" << viewportPosition + << " mapObjList.size()=" << mapObjList.size(); + + for (auto mapObj : mapObjList) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " id=" << mapObj->uniqueMapId() + << " object-debugInfo=" << mapObj->debugInfo() + << " object-identifier=" << mapObj->identifier(); + + // Selection: + if (mapObj->pickable()) + { + mapObj->setSelected(!mapObj->selected()); + mapObj->updateView(); + DEBUG_LOG(m_logger) << LOGFUNCTION << (mapObj->selected() ? " is selected" : " is not selected"); + } + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << "END onMapObjectClicked"; +} + +void SitMap::onMapContextClicked(const SituationMap::Map::RefPointerMapObjectList & mapObjList, const QPointF & pos) +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << "START onMapContextClicked window pos x=" << pos.x() << " y=" << pos.y() + << " mapObjList.size()=" << mapObjList.size(); + + return; + +// delete d->m_popupMenu; + +// d->m_popupMenu = new QMenu( d->m_map ); + +// d->m_popupMenu->addSection( QLatin1String("Object at mouse position") ); +// for (auto mapObj : mapObjList) +// { +// DEBUG_LOG(m_logger) << LOGFUNCTION << " id=" << mapObj->uniqueMapId() +// << "object-debugInfo=" << mapObj->debugInfo() +// << "object-identifier=" << mapObj->identifier(); +// d->m_popupMenu->addAction( mapObj->identifier() ); +// } +// d->m_popupMenu->addSeparator(); + +// // d->m_popupMenu->popup(d->m_map->getQGraphicsView()->mapToGlobal(pos.toPoint())); +// d->m_popupMenu->popup( QPoint(100, 100) ); + +// DEBUG_LOG(m_logger) << LOGFUNCTION << "END onMapContextClicked"; + +} + +void SitMap::onIniFileActivated(const QString &text) +{ + if (text.compare(QLatin1String("select")) == 0) + { + d->m_initFilePath = QFileDialog::getOpenFileName(this, + QLatin1String("get ini file"), + QLatin1String(""), + QLatin1String("Ini files (*.ini)")); + } else { + // e.g. TDS-build/share/Lts/situation_map/situation_map_defaults.ini + d->m_initFilePath = NGL::findFile(NGL::FileType::ConfigFile, text); + } + NOTICE_LOG(m_logger) << LOGFUNCTION << "selected filename= '" << d->m_initFilePath << "'"; +} +// SitMap::onMapContextClicked(const SituationMap::Map::RefPointerMapObjectList & mapObjList, const QPoint &) + +#include "sit_map.moc" diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SituationMap/sit_map.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SituationMap/sit_map.h new file mode 100755 index 000000000..21eaae000 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SituationMap/sit_map.h @@ -0,0 +1,50 @@ +/** + * @RDE_HEADER@ + * Interface + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +// Qwn +#include +#include + +#include +#include + +// Qt +#include + +class SitMap : public QDialog +{ + Q_OBJECT + +public: + explicit SitMap(bool demoMode, QWidget *parent = nullptr); + virtual ~SitMap (); + + SituationMap::Map *map(); + +public slots: + void onCreateDestroyClicked(); + void onToggleModeClicked(); + void createMap(); + + void onPlanClicked(); + void onMapObjectClicked (const SituationMap::Map::RefPointerMapObjectList & mapObjList, const QPointF &viewportPosition); + void onMapContextClicked( const SituationMap::Map::RefPointerMapObjectList & mapObjList, const QPointF &pos); + +protected slots: + void onIniFileActivated(const QString &text); + +private: + SituationMap::LOGGER_T m_logger; ///< Interface of the Logging-Facility + + class Private; + Private* d = nullptr; + +}; + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SituationMap/sit_map_main.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SituationMap/sit_map_main.cpp new file mode 100755 index 000000000..3ebd10183 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/SituationMap/sit_map_main.cpp @@ -0,0 +1,119 @@ +/** + * @RDE_HEADER@ + * Interface + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own + +#include "sit_map.h" + +#include + +#include +#include +#include + +#include +#include + + +// Qt +#include +#include +#include +#include + + +// ESRI +//#include + + +int main (int argc, char* argv[]) +{ + NGLApplication app ( argc, argv ); + + // ESRI : +#ifdef Q_OS_WIN + // Force usage of OpenGL ES through ANGLE on Windows + QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); +#endif + // : ESRI + + app.createLogger("sitmapMain", "log4cxx_sitmapMain.props"); + + STARTUP_LOG( app.baseLogger() ); + + // TODO: load style plugin + //QApplication::setStyle(QLatin1String("rde")); + //QDir::setSearchPaths(QLatin1String("icon"), QStringList(QLatin1String(":/icons/light"))); + + app.setOrganizationName( QLatin1String( "Rheinmetall Defence Electronics" ) ); + app.setApplicationName( QLatin1String( "SituationMap Example Application" ) ); + + LtsPluginManager::LtsPluginLoader *pluginLoader = LtsPluginManager::LtsPluginLoader::getInstance(); + + pluginLoader->loadPlugins(LtsPluginManager::PluginType::STYLE, nullptr, + QStringList() << QStringLiteral("rme.light")); + pluginLoader->loadPlugins(LtsPluginManager::PluginType::PROJECT, nullptr, + QStringList() << QStringLiteral("legatus")); + + // .. to keep some compatibility to old resource structure TODO: rm ! + QDir::setSearchPaths( QLatin1String( "icon" ), QStringList( QLatin1String( ":/style/icons/" ) ) ); + QDir::setSearchPaths( QLatin1String( "icon" ), QStringList( QLatin1String( ":/style/" ) ) ); // FIXME: does qt check for recursion ?? + + if(0) + { + INFO_LOG(app.baseLogger()) << LOGFUNCTION << " set PROXY PROXY PROXY PROXY PROXY PROXY PROXY"; + + QNetworkProxy proxy; + proxy.setType(QNetworkProxy::HttpProxy); + proxy.setHostName(QLatin1String("213.61.254.67")); // proxy-rde > 213.61.254.67 + proxy.setUser(QLatin1String("Norbert Thoden/RH-UBD-BC")); + proxy.setPassword(QLatin1String("RDEWWW11")); + proxy.setPort(8080); + QNetworkProxy::setApplicationProxy(proxy); + } else { + NOTICE_LOG(app.baseLogger()) << LOGFUNCTION << " reset !!! QNETWORKPROXY QNETWORKPROXY QNETWORKPROXY QNETWORKPROXY QNETWORKPROXY !!!"; + QNetworkProxy::setApplicationProxy(QNetworkProxy()); + } + + //QNetworkProxy proxy; + //proxy.setType(QNetworkProxy::HttpProxy); + //proxy.setHostName(QStringLiteral("proxy-rde")); + //proxy.setPort(8080); + //proxy.setUser(QStringLiteral("norbert.thoden@rheinmetall.com")); + //proxy.setPassword(QStringLiteral("RDEWWW11")); + //QNetworkProxy::setApplicationProxy(proxy); + + + bool demoMode = false; + if (argc == 2) + demoMode = true; + + SitMap* sitMap= new SitMap(demoMode); + + sitMap->setMinimumWidth(200); + sitMap->setMinimumHeight(200); + + sitMap->resize(800, 600); + + sitMap->show(); + + + + + + + + + int ret = app.exec(); + + delete sitMap; + + SHUTDOWN_LOG(app.baseLogger()); + + return ret; +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/exampleSvgRenderer/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/exampleSvgRenderer/CMakeLists.txt new file mode 100755 index 000000000..9084bf05c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/exampleSvgRenderer/CMakeLists.txt @@ -0,0 +1,31 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +# EXAMPLE +SET(EXAMPLE_SRCS + exampleMain.cpp +) + +SET(EXAMPLE_HDRS +) + +IF(MSVC) + + # Configure the template file + SET(VS_USER_FILE exampleSvgRenderer.vcxproj.user) + configure_file( ../../SituationMap.vcxproj.user.in ${CMAKE_CURRENT_BINARY_DIR}/${VS_USER_FILE} @ONLY) + +ENDIF(MSVC) + +NGL_ADD_EXECUTABLE(WS NO_TRANSLATION exampleSvgRenderer ${EXAMPLE_SRCS} ${EXAMPLE_HDRS}) +NGL_TARGET_LINK_LIBRARIES(exampleSvgRenderer + NGLLogger + Qt6::Widgets + Qt6::Core + Qt6::Svg + Qt6::Gui +) + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/exampleSvgRenderer/exampleMain.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/exampleSvgRenderer/exampleMain.cpp new file mode 100755 index 000000000..c9e88e746 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Tests/exampleSvgRenderer/exampleMain.cpp @@ -0,0 +1,108 @@ +/** + * @RDE_HEADER@ + * Interface + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include +#include +#include +#include +#include +#include + +void renderImpactSymbol(QPainter& painter) +{ + // From SymbolDictionaryExtendedImpl::renderImpactSymbol + + QColor color = Qt::red; + int size = 100; + bool filled = false; + + + QPen pen(color); + pen.setWidth(2); + painter.setPen(pen); + + const int sizeHalf = size/2.0; + + QPointF center(sizeHalf, sizeHalf); + + // circle + if (filled) + painter.setBrush( color ); + painter.drawEllipse(center, sizeHalf*0.5, sizeHalf*0.5); + +// // lines +// QLine newLine(0, sizeHalf, size, sizeHalf); +// painter.drawLine(newLine); + +// QTransform trans; +// trans.translate(sizeHalf, sizeHalf); +// trans.rotate(45); +// trans.translate(-sizeHalf, -sizeHalf); + +// newLine = trans.map(newLine); +// painter.drawLine(newLine); + +// newLine = trans.map(newLine); +// painter.drawLine(newLine); + +// newLine = trans.map(newLine); +// painter.drawLine(newLine); +} + + + +int main (int argc, char* argv[]) +{ + QApplication app ( argc, argv ); + + // under windows for ArcGIS 10.2.6 and higher + #ifdef Q_OS_WIN + QCoreApplication::setAttribute( Qt::AA_UseOpenGLES ); + #endif + + int fullSize = 100; + QImage image(fullSize, fullSize, QImage::Format_ARGB32); + image.fill(qRgba(0,0,0,0)); + + { + // Image: + QPainter painterI(&image); + renderImpactSymbol(painterI); + painterI.end(); + image.save(QLatin1String("dummy.png")); + + // SaveSvg: + QSvgGenerator generator; + generator.setFileName(QLatin1String("dummy.svg")); + generator.setSize(QSize(100, 100)); + generator.setViewBox(QRect(0, 0, 100, 100)); + generator.setTitle(QLatin1String("SVG Generator Example Drawing")); + generator.setDescription(QLatin1String("An SVG drawing created by the SVG Generator")); + + QPainter painterSvg; + painterSvg.begin(&generator); + renderImpactSymbol(painterSvg); + painterSvg.end(); + } + + { + QSvgRenderer svgRenderer; + QImage image(fullSize, fullSize, QImage::Format_ARGB32); + image.fill(qRgba(0,0,0,0)); + + QPainter painter(&image); + svgRenderer.load(QLatin1String("manualDummy.svg")); + svgRenderer.render(&painter); + painter.end(); + + image.save(QLatin1String("manualDummy.png")); + } + + + return app.exec(); +} diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTests/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTests/CMakeLists.txt new file mode 100755 index 000000000..7b148b942 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTests/CMakeLists.txt @@ -0,0 +1,52 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +SET(LinkedSources +# ../Utils/IdHandler.cpp +# ../Utils/IdHandler.h + + log4cxxSituationMapUnitTest.props +) + +SET(UnitTest_SRCS + unit_test.cpp + unit_test.h + virtual_inheritances.cpp +) + +SOURCE_GROUP( LinkedSources FILES ${LinkedSources} ) +SOURCE_GROUP( UnitTestSources FILES ${UnitTest_SRCS} ) + +IF(BUILD_TESTING AND TARGET situationMap) + ADD_EXECUTABLE(situationMapUnitTest ${LinkedSources} ${UnitTest_SRCS} ) + + TARGET_LINK_LIBRARIES(situationMapUnitTest + ArcGISQt5Hack + NGLLogger + nglapplication + advancedSymbology_static + situationMap_static + situationMapObjects + ArcGIS::ArcGIS + Qt6::Core + Qt6::Test + ) + ADD_TEST(NAME situationMapUnitTest_NONE COMMAND $ none) + ADD_TEST(NAME situationMapUnitTest_MIL2525D COMMAND $ mil2525d) + + SET(LOG4CXX_PROPS_FILE log4cxxSituationMapUnitTest.props) + ADD_CUSTOM_COMMAND(TARGET situationMapUnitTest POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${LOG4CXX_PROPS_FILE} $/${LOG4CXX_PROPS_FILE} + ) + + IF(MSVC) + # Configure the template file + SET(VS_USER_FILE situationMapUnitTest.vcxproj.user) + configure_file( ../SituationMap.vcxproj.user.in ${CMAKE_CURRENT_BINARY_DIR}/${VS_USER_FILE} @ONLY) + ENDIF() + +ENDIF() diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTests/log4cxxSituationMapUnitTest.props b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTests/log4cxxSituationMapUnitTest.props new file mode 100755 index 000000000..fa4be4cb6 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTests/log4cxxSituationMapUnitTest.props @@ -0,0 +1,167 @@ +log4j.reset=true +log4j.rootLogger=WARN, file, stdout + +# SitMap +########################################################### +#log4j.logger.SituationMap.SitMapUnitTest=DEBUG +#log4j.logger.SituationMap.SitMap=WARN + +# AdvancedSymbology (AdvSymbology) +########################################################### +#log4j.logger.AdvSymbology.SymbolDictionary=INFO +#log4j.logger.AdvSymbology.SymbolDictionaryImpl=INFO +#log4j.logger.AdvSymbology.EsriDictionaryProcessor=INFO +#log4j.logger.AdvSymbology.SymbolResult=INFO +#log4j.logger.AdvSymbology.Sidc2525Delta=INFO + +# SituationMap +########################################################### +#log4j.logger.SituationMap.INFO + +log4j.logger.SituationMap.LoggingTest=ERROR +log4j.logger.SituationMap.LoggingTest.Chan1=INFO +log4j.logger.SituationMap.LoggingTest.Chan2=ERROR + +#log4j.logger.SituationMap.MapImpl=WARN +#log4j.logger.SituationMap.MapSettings=WARN +#log4j.logger.SituationMap.MapInitializer=WARN +#log4j.logger.SituationMap.LayerFactoryImpl=WARN +#log4j.logger.SituationMap.LayerControl=WARN +#log4j.logger.SituationMap.CoordTranslationFunctorImpl=WARN +#log4j.logger.SituationMap.CoordTranslationThread=WARN +#log4j.logger.SituationMap.PositionsTranslated=WARN +#log4j.logger.SituationMap.GridImpl=WARN +#log4j.logger.SituationMap.ObjectFactory=WARN +#log4j.logger.SituationMap.IdGenerator=TRACE +#log4j.logger.SituationMap.ObjectContainer=TRACE +#log4j.logger.SituationMap.ObjectContainerCTC=TRACE +#log4j.logger.SituationMap.OverlayHandlerImpl=TRACE +#log4j.logger.SituationMap.ViewshedImpl=INFO +#log4j.logger.SituationMap.ViewshedBase=INFO + +#log4j.logger.SituationMap.AbstractMapObjectImpl=INFO +#log4j.logger.SituationMap.AbstractEffectSimulationImpl=INFO +#log4j.logger.SituationMap.OriginTranslated=INFO +#log4j.logger.SituationMap.PositionsTranslated=INFO +#log4j.logger.SituationMap.AbstractMultiPointMapObjectImpl=INFO +#log4j.logger.SituationMap.AbstractSinglePointMapObjectImpl=INFO +#log4j.logger.SituationMap.BasicPathObjectImpl=INFO +#log4j.logger.SituationMap.IndicationContainer=INFO + +#log4j.logger.SituationMap.PolygonImpl=INFO +#log4j.logger.SituationMap.PolylineImpl=INFO +#log4j.logger.SituationMap.TextImpl=INFO + +#log4j.logger.SituationMap.AreaFireImpl=INFO +#log4j.logger.SituationMap.BiologicalAgentImpl=INFO +#log4j.logger.SituationMap.BuildingOutlineImpl=INFO +#log4j.logger.SituationMap.ChemicalAgentImpl=INFO +#log4j.logger.SituationMap.DangerAreaImpl=INFO +#log4j.logger.SituationMap.FunctionalAreaImpl=INFO +#log4j.logger.SituationMap.HitLineImpl=INFO +#log4j.logger.SituationMap.MapObjectIndicatorImpl=INFO +#log4j.logger.SituationMap.MineImpl=INFO +#log4j.logger.SituationMap.MineFieldImpl=INFO +#log4j.logger.SituationMap.NuclearAgentImpl=INFO +#log4j.logger.SituationMap.ParticipantImpl=INFO +#log4j.logger.SituationMap.SingleFireImpl=INFO + +#log4j.logger.SituationMap.AbstractGraphicalObject=INFO +#log4j.logger.SituationMap.GraphicalObjectEllipse=INFO +#log4j.logger.SituationMap.GraphicalObjectPath=INFO +#log4j.logger.SituationMap.GraphicalObjectMultiPath=INFO +#log4j.logger.SituationMap.GraphicalObjectPictureMarker=INFO +#log4j.logger.SituationMap.GraphicalObjectRange=INFO +#log4j.logger.SituationMap.GraphicalObjectText=INFO +#log4j.logger.SituationMap.TacticalAppearance=INFO +#log4j.logger.SituationMap.MessageBase=INFO + +#log4j.logger.SituationMap.EventDispatcher=FATAL +#log4j.logger.SituationMap.Rubberband=FATAL +#log4j.logger.SituationMap.RubberbandRectangle=FATAL +#log4j.logger.SituationMap.RubberbandDrawablesAndHandles=FATAL +#log4j.logger.SituationMap.RubberbandFrameHandle=FATAL +#log4j.logger.SituationMap.RubberbandPointHandle=FATAL +#log4j.logger.SituationMap.RubberbandStateSelect=FATAL + +# SituationMap-SideBar +########################################################### +#log4j.logger.SituationMap.SideBar=INFO +#log4j.logger.SituationMap.SideBarItem=INFO +#log4j.logger.SituationMap.SideBarModel=INFO + + + + +# Appnd_File +########################################################### +log4j.appender.file=org.apache.log4j.RollingFileAppender +log4j.appender.file.MaxFileSize=1MB +log4j.appender.file.MaxBackupIndex=9 +log4j.appender.file.file=${LOG4QT_LOGDIR}/${LOG4QT_USER}_SituationMap.log +log4j.appender.file.layout=org.apache.log4j.PatternLayout +log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n + + +# Appnd_Console +########################################################### +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern= %d %-5p %c{1} |#| %m (%-.30F:%L) %n + +#log4j.appender.stdout.layout.ConversionPattern= %d %-5p %-30c |#| %m (%-.30F:%L) %n + + +# Appnd_Syslog +########################################################### +log4j.appender.syslog=org.apache.log4j.SyslogAppender +log4j.appender.syslog.Threshold=ERROR +log4j.appender.syslog.SysLogHost=localhost +log4j.appender.syslog.Facility=LOCAL7 +log4j.appender.syslog.layout=org.apache.log4j.PatternLayout +log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m +log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true +log4j.appender.syslog.LevelRangeFilter.LevelMin=INFO +log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL + + +########################################################### +########################################################### +########################################################### +# 1) Available Levels in LOG4CXX Version 0.10.0: +# TRACE, +# DEBUG, +# INFO, +# WARN, +# ERROR +# FATAL +# +# d = date/time +# p = priority (Level) +# c = category/channel +# m = message +# F = file +# L = line +# n = platform dependent line separator character or characters. +# .... +# +# Examples for console: +# a) +# %d %p %c #%m# (%.50F:%L)%n +# date/time +# priority +# category/channel +# #message# +# (Maximal 50Character des Filename von rechts:Line) +# line separator +# +# b) +# %d (%.15F:%L) %-5p %c |#| %m%n +# date/time +# (Maximal 15Character des Filename von rechts:Line) +# priority, Right pad with spaces if the priority name is less than 5 characters long. +# category/channel +# message +# line separator +# + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTests/unit_test.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTests/unit_test.cpp new file mode 100755 index 000000000..ae6c90172 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTests/unit_test.cpp @@ -0,0 +1,3637 @@ +/** + * @RDE_HEADER@ + * Unittest + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "unit_test.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +//#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + + +bool CheckSignal::isConnected(quint64 &msec) +{ + QElapsedTimer timeClock; + timeClock.start(); + + bool isConnected = isSignalConnected(QMetaMethod::fromSignal(&CheckSignal::mySignal)); + + msec = timeClock.elapsed(); + return isConnected; +} + + + +//------------------------------------------------------------------------------------------------------- +// Helper class 'IdGeneratorCounting' +//------------------------------------------------------------------------------------------------------- +class IdGeneratorCounting : public SituationMap::IdGenerator +{ +public: + IdGeneratorCounting() : SituationMap::IdGenerator() + { + ++counter; + } + ~IdGeneratorCounting() + { + --counter; + } + static ulong getCounter() + { + return counter; + } + +private: + static ulong counter; +}; +ulong IdGeneratorCounting::counter = 0; + + + + + + +//------------------------------------------------------------------------------------------------------- +int main(int argc, char *argv[]) +{ + NGLApplication app(argc, argv); + app.createLogger("SituationMapUnitTest", "log4cxxSituationMapUnitTest.props"); + + AdvancedSymbology::SymbologyType symbologyType = AdvancedSymbology::SymbologyType::MIL2525C; + + if (argc < 2) + { + // usage: + std::cout << argv[0] + << std::endl + << " usage: append 'mil' | 'app' to argumentlist" << std::endl + << " e.g " << argv[0] << " [QTest options] [testfunction] none" << std::endl + << " e.g " << argv[0] << " [QTest options] [testfunction] mil2525D" << std::endl + << " e.g " << argv[0] << " [QTest options] [testfunction] mil2525C" << std::endl + << " e.g " << argv[0] << " [QTest options] [testfunction] app6b" << std::endl + << " e.g " << argv[0] << " [QTest options] [testfunction] app6d" << std::endl + << std::endl; + + return -1; + } + + const QString strSymType = app.arguments().constLast(); // the last argument + if (strSymType.compare(QLatin1String("none"), Qt::CaseInsensitive) == 0) + symbologyType = AdvancedSymbology::SymbologyType::None; + else if (strSymType.compare(QLatin1String("mil2525D"), Qt::CaseInsensitive) == 0) + symbologyType = AdvancedSymbology::SymbologyType::MIL2525D; + else if (strSymType.compare(QLatin1String("mil2525C"), Qt::CaseInsensitive) == 0) + symbologyType = AdvancedSymbology::SymbologyType::MIL2525C; + else if (strSymType.compare(QLatin1String("app6b"), Qt::CaseInsensitive) == 0) + symbologyType = AdvancedSymbology::SymbologyType::APP6B; + else if (strSymType.compare(QLatin1String("app6d"), Qt::CaseInsensitive) == 0) + symbologyType = AdvancedSymbology::SymbologyType::APP6D; + argc -= 1; // remove last argument for QTest::qExec + + + SituationMap::LayerFactoryImpl::setForceCreationOfGraphicsOverlay(false); // false => createLayerOnDemand + + + QSharedPointer tc = QSharedPointer(new SitMapUnitTest(symbologyType)); + int retCode = QTest::qExec(tc.data(), argc, argv); + + return retCode; +} + + + +class SitMapUnitTest::Private +{ +public: + + Private() + : m_symbologyType(AdvancedSymbology::SymbologyType::MIL2525C) + { +// m_uniqueIdHandler = new SituationMap::IdHandler(m_initReserve,m_blockSize); + } + + ~Private() = default; + + + +// std::unique_ptr m_uniqueIdHandler; + + AdvancedSymbology::SymbologyType m_symbologyType; + + +}; // class Private +//----------------------------------------------------------------------------- + + + +class Foo +{ +public: + virtual ~Foo() = default; + + static Foo *createFoo() + { + return new Foo(); + } + + Foo(const Foo &) = delete; // if needed, put as private + Foo &operator=(const Foo &) = delete; // if needed, put as private + Foo(Foo &&) = delete; // if needed, put as private + Foo &operator=(Foo &&) = delete; // if needed, put as private + +private: + Foo() {} +}; + + + +class baseClass +{ +public: + virtual ~baseClass() = default; + + baseClass(const baseClass &) = delete; + baseClass &operator=(const baseClass &) = delete; + baseClass(baseClass &&) = delete; + baseClass &operator=(baseClass &&) = delete; + + virtual void doSomething() = 0; + + +protected: + baseClass() {} +}; + +class subClass : public baseClass +{ +public: + ~subClass() = default; + + static subClass *create() + { + return new subClass(); + } + + void doSomething() override { } + +private: + subClass() = default; + +}; + + + + +SitMapUnitTest::SitMapUnitTest(AdvancedSymbology::SymbologyType symbologyType) + : m_logger(SituationMap::getLogger("SitMapUnitTest")) + , m_d(new Private) +{ + m_d->m_symbologyType = symbologyType; + + NOTICE_LOG(m_logger) << "Running test with symbologyType " << m_d->m_symbologyType; +} + +SitMapUnitTest::~SitMapUnitTest() +{ + NOTICE_LOG(m_logger) << "Finished running test with symbologyType " << m_d->m_symbologyType; +} + +void SitMapUnitTest::checkLogging() +{ + SituationMap::LOGGER_T myLogger = SituationMap::getLogger("LoggingTest.Chan1"); + FATAL_LOG(myLogger) << LOGFUNCTION << " myLogger.getLoggerName() -> " << myLogger.getLoggerName(); + + + + DEBUG_LOG(myLogger) << LOGFUNCTION << " (LoggingTest.Chan1) DEBUG-MSG" ; + INFO_LOG(myLogger) << LOGFUNCTION << " (LoggingTest.Chan1) INFO-MSG" ; + WARNING_LOG(myLogger) << LOGFUNCTION << " (LoggingTest.Chan1) WARNING-MSG" ; + ERROR_LOG(myLogger) << LOGFUNCTION << " (LoggingTest.Chan1) ERROR-MSG" ; + FATAL_LOG(myLogger) << LOGFUNCTION << " (LoggingTest.Chan1) FATAL-MSG" ; + + FATAL_LOG(myLogger) << LOGFUNCTION << " ------------"; + + { + SituationMap::LOGGER_T myLogger = SituationMap::getLogger("LoggingTest.Chan2"); + FATAL_LOG(myLogger) << LOGFUNCTION << " myLogger.getLoggerName() -> " << myLogger.getLoggerName(); + + DEBUG_LOG(myLogger) << LOGFUNCTION << " (LoggingTest.Chan2) DEBUG-MSG" ; + INFO_LOG(myLogger) << LOGFUNCTION << " (LoggingTest.Chan2) INFO-MSG" ; + WARNING_LOG(myLogger) << LOGFUNCTION << " (LoggingTest.Chan2) WARNING-MSG" ; + ERROR_LOG(myLogger) << LOGFUNCTION << " (LoggingTest.Chan2) ERROR-MSG" ; + FATAL_LOG(myLogger) << LOGFUNCTION << " (LoggingTest.Chan2) FATAL-MSG" ; + } + FATAL_LOG(myLogger) << LOGFUNCTION << " ------------" ; + + // update logger-obj + SituationMap::LOGGER_T myLogger2 = SituationMap::getLogger("LoggingTest.Chan2"); + FATAL_LOG(myLogger2) << LOGFUNCTION << " myLogger.getLoggerName() -> " << myLogger2.getLoggerName() ; + + DEBUG_LOG(myLogger2) << LOGFUNCTION << " (LoggingTest.Chan2) DEBUG-MSG" ; + INFO_LOG(myLogger2) << LOGFUNCTION << " (LoggingTest.Chan2) INFO-MSG" ; + WARNING_LOG(myLogger2) << LOGFUNCTION << " (LoggingTest.Chan2) WARNING-MSG" ; + ERROR_LOG(myLogger2) << LOGFUNCTION << " (LoggingTest.Chan2) ERROR-MSG" ; + FATAL_LOG(myLogger2) << LOGFUNCTION << " (LoggingTest.Chan2) FATAL-MSG" ; + +} + +struct shm_punkt_rt +{ + shm_punkt_rt(double x = 0, double y = 0) + { + p[0] = x; // Ost + p[1] = y; // Nord + } + + double p[2]; +}; + +void SitMapUnitTest::checkQRect() +{ + QRectF rect; + + QVERIFY2((rect.isEmpty() == true), "isEmpty: failed"); + QVERIFY2((rect.isValid() == false), "isValid: failed"); + + rect = QRectF(100, 100, 40, 60); + QVERIFY2((rect.isEmpty() == false), "isEmpty: failed"); + QVERIFY2((rect.isValid() == true), "isValid: failed"); + + QVERIFY2(rect.width() == 40, "center: failed"); + QVERIFY2(rect.height() == 60, "center: failed"); + + qDebug() << " rect.center()" << rect.center(); + QVERIFY2(((rect.center() - QPoint(120, 130)).manhattanLength() <= 2), "center: failed"); + + QVERIFY2(rect.top() == 100, "top: failed"); // <----- + QVERIFY2(rect.bottom() == 160, "bottom: failed"); // <----- + + + QVERIFY2(rect.topLeft() == QPointF(100, 100), "topLeft: failed"); // <----- + qDebug() << " rect.topRight()" << rect.topRight(); + QVERIFY2(rect.topRight() == QPointF(140, 100), "topLeft: failed"); // <----- + +} + + +void SitMapUnitTest::checkQListQVector() +{ + + std::vector refPoints; + + int refSize = 1000000; + refPoints.reserve(refSize); + + for (int i = 0; i < refSize; ++i) + refPoints.emplace_back(i, 100.1 * i); + + // check within 2*5: + QList qContL; // do NOT modify, QList is used to compare with QVector (a couple of lines later) + QVector qContV; + for (int i = 0; i < 10; ++i) + { + if (i % 2 == 0) + { + QElapsedTimer timer; + if (m_logger.isInfoEnabled()) + timer.start(); + + + qContL.clear(); + qContL.reserve(refSize); + for (const shm_punkt_rt &item : refPoints) + qContL.append(QPointF(item.p[0], item.p[1])); + + INFO_LOG(m_logger) << LOGFUNCTION + << " QList -> " + << timer.elapsed() << "ms"; + } + else + { + QElapsedTimer timer; + if (m_logger.isInfoEnabled()) + timer.start(); + + + qContV.clear(); + qContV.reserve(refSize); + for (const shm_punkt_rt &item : refPoints) + qContV.append(QPointF(item.p[0], item.p[1])); + + INFO_LOG(m_logger) << LOGFUNCTION + << " QVector -> " + << timer.elapsed() << "ms"; + } + + } +} + +void SitMapUnitTest::checkQWeakPointer() +{ + QWeakPointer ts_weak; // TextStyle does NOT inherent from QObject + QVERIFY2((ts_weak.isNull() == true), "isNull: failed"); + { + QSharedPointer ts_shared(new SituationMap::TextStyle()); + ts_weak = ts_shared; // 'connected' weak and shared + + QVERIFY2((ts_weak.isNull() == false), "isNull: failed"); + } // shared will delete object + QVERIFY2((ts_weak.isNull() == true), "isNull: failed"); // weak 'knows' the deletion :-) + +// // you should continue like this: +// QSharedPointer ts_shared2 = ts_weak.toStrongRef(); +// if (ts_shared2) +// { +// ts_shared2->doSomething(); +// } else { +// // error, object down not exists anymore +// } +} + +void SitMapUnitTest::checkFactoryCreationContraints() +{ +// Links: http://stackoverflow.com/questions/124880/is-it-possible-to-prevent-stack-allocation-of-an-object-and-only-allow-it-to-be +// Links: https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Requiring_or_Prohibiting_Heap-based_Objects + + + // + // Foo + // + + // creation on stack: +// Foo f; // will not compile, ctor is private + + // creation on heap: +// Foo *f_ptr1 = new Foo(); // will not compile, ctor is private + + Foo *f_ptr2 = Foo::createFoo(); + delete f_ptr2; + + + // + // subClass + // + + // creation on stack: +// subClass sC; // will not compile, ctor is private + + // creation on heap: +// subClass *sC_ptr1 = new subClass(); // will not compile, ctor is private + + subClass *sC_ptr2 = subClass::create(); + delete sC_ptr2; + +} + + +//------------------------------------------------------------------------------------------------------- + +/* +void SitMapUnitTest::checkIdHandlerCreate() +{ + quint32 usedIdCount = m_d->m_uniqueIdHandler->countUsedIds(); + QVERIFY2( (usedIdCount == 0), "Check IdHandler initial value failed, wrong id count" ); + + DEBUG_LOG(m_logger) << LOGFUNCTION + << " used id count before test = " + << usedIdCount + ; + + // create one object + + SituationMap::IdHandler::AccessableId * myPtr = new SituationMap::IdHandler::AccessableId(); + SituationMap::IdHandler::UniqueMapId myId = m_d->m_uniqueIdHandler->createId( myPtr ); + + DEBUG_LOG(m_logger) << LOGFUNCTION + << " created id = " + << myId + << " obj address = " + << myPtr + ; + + // check count of ids, myId and accessId function + QVERIFY2( ((usedIdCount+1) == m_d->m_uniqueIdHandler->countUsedIds()), "Check IdHandler create failed, wrong id count" ); + QVERIFY2( SituationMap::IdHandler::isIdValid(myId) , "Check IdHandler create failed, bad id" ); + QVERIFY2( (m_d->m_uniqueIdHandler->accessId(myId) == myPtr) , "Check IdHandler create failed, pointer mismatch" ); + + // check invalid access + myPtr = m_d->m_uniqueIdHandler->accessId( SituationMap::IdHandler::UniqueMapId(-2) ); + + QVERIFY2( (myPtr == nullptr) , "Check IdHandler create failed, invalid access doesn_t return nullptr" ); + +} + +void SitMapUnitTest::checkIdHandlerFree() +{ + quint32 usedIdCount = m_d->m_uniqueIdHandler->countUsedIds(); + SituationMap::IdHandler::AccessableId * myPtr = new SituationMap::IdHandler::AccessableId(); + SituationMap::IdHandler::UniqueMapId myId = m_d->m_uniqueIdHandler->createId( myPtr ); + + + DEBUG_LOG(m_logger) << LOGFUNCTION + << " created id =" + << myId + << " obj address =" + << myPtr + ; + + bool ok = m_d->m_uniqueIdHandler->freeId( myId ); + + QVERIFY2( ok, "Check IdHandler freeId failed, return value of freeId is false." ); + QVERIFY2( (usedIdCount == m_d->m_uniqueIdHandler->countUsedIds()), "Check IdHandler freeId failed, wrong id count." ); + QVERIFY2( (m_d->m_uniqueIdHandler->accessId(myId) == nullptr) , "Check IdHandler freeId failed, id is still in use." ); + + ok = m_d->m_uniqueIdHandler->freeId( SituationMap::IdHandler::BadId ); + QVERIFY2(ok == false, "Check IdHandler freeId failed, return value of freeId(BadId) is not false." ); + + ok = m_d->m_uniqueIdHandler->freeId( SituationMap::IdHandler::UniqueMapId(-2) ); + QVERIFY2(ok == false, "Check IdHandler freeId failed, return value of freeId() is not false." ); + +} + +void SitMapUnitTest::checkIdHandlerReUseId() +{ + quint32 usedIdCount = m_d->m_uniqueIdHandler->countUsedIds(); + + QList idList; + + for (unsigned int idx = usedIdCount; idx < m_initReserve; ++idx) + { + idList += m_d->m_uniqueIdHandler->createId( new SituationMap::IdHandler::AccessableId() ); + } + + bool ok = m_d->m_uniqueIdHandler->freeId( idList.first() ); + + QVERIFY2( ok, "Check IdHandler reUseId failed, return value of freeId is false." ); + + if (ok) + { + SituationMap::IdHandler::UniqueMapId id = m_d->m_uniqueIdHandler->createId( new SituationMap::IdHandler::AccessableId() ); + QVERIFY2(id == idList.first(), "Check IdHandler reUseId failed, freed id has not been used again." ); + } + +} + +void SitMapUnitTest::checkIdHandlerReserveId() +{ + quint32 usedIdCount = m_d->m_uniqueIdHandler->countUsedIds(); + + SituationMap::IdHandler::UniqueMapId reserveId = usedIdCount + 7; + + // look for a unused id + while (m_d->m_uniqueIdHandler->isIdInUse(reserveId)) + { + reserveId++; + } + + // first reserve should return true + bool ok = m_d->m_uniqueIdHandler->reserveId( reserveId, new SituationMap::IdHandler::AccessableId() ); + + QVERIFY2( ok, "Check IdHandler reserveId failed, reserveId returns false." ); + + if (ok) + { + // second reserve should return false + ok = m_d->m_uniqueIdHandler->reserveId( reserveId, new SituationMap::IdHandler::AccessableId() ); + QVERIFY2( !ok, "Check IdHandler reserveId failed, second reserveId doesn't return false." ); + + for (int idx = reserveId - usedIdCount + 20; idx >= 0; --idx) + { + SituationMap::IdHandler::UniqueMapId myId = m_d->m_uniqueIdHandler->createId( new SituationMap::IdHandler::AccessableId() ); + QVERIFY2( myId != reserveId, "Check IdHandler reserveId failed, createId returns reserved id." ); + } + + } +} +*/ + +void SitMapUnitTest::checkSimpleValue() +{ + const int intVal = 42; + const int intValModified = 123; + + // assign + SituationMap::SimpleValue wrappedValue = intVal; + QVERIFY2((wrappedValue.changed() == true), "SimpleValue: failed"); + QVERIFY2((wrappedValue.value() == 42), "SimpleValue: failed"); + + // reset + wrappedValue.reset(); + QVERIFY2((wrappedValue.changed() == false), "SimpleValue: failed"); + QVERIFY2((wrappedValue.value() == 42), "SimpleValue: failed"); + + // access: + bool ok = false; + ok = (wrappedValue.value() == intVal); + QVERIFY2((ok), "SimpleValue: failed"); + QVERIFY2((wrappedValue.changed() == false), "SimpleValue: failed"); + + ok = (wrappedValue.refValue() == intVal); + QVERIFY2((ok), "SimpleValue: failed"); + QVERIFY2((wrappedValue.changed() == true), "SimpleValue: failed"); + + ok = (wrappedValue() == intVal); + QVERIFY2((ok), "SimpleValue: failed"); + QVERIFY2((wrappedValue.changed() == true), "SimpleValue: failed"); + + // assign + SituationMap::SimpleValue wrappedValue2 = wrappedValue; + QVERIFY2((wrappedValue2.changed() == true), "SimpleValue: failed"); + + wrappedValue.reset(); + wrappedValue = intValModified; + QVERIFY2((wrappedValue.changed() == true), "SimpleValue: failed"); + QVERIFY2((wrappedValue.value() == intValModified), "SimpleValue: failed"); + + // refValue + wrappedValue.reset(); + wrappedValue.refValue() = intVal; + QVERIFY2((wrappedValue.changed() == true), "SimpleValue: failed"); + QVERIFY2((wrappedValue.value() == intVal), "SimpleValue: failed"); + + // setChanged + wrappedValue.reset(); + QVERIFY2((wrappedValue.changed() == false), "SimpleValue: failed"); + wrappedValue.setChanged(); + QVERIFY2((wrappedValue.changed() == true), "SimpleValue: failed"); + + + // ok = wrappedValue == wrappedValue2; // not supported + +} + +void SitMapUnitTest::checkIdGenerator() +{ + QSharedPointer idGenerator(new SituationMap::IdGenerator()); + QVERIFY2((idGenerator.isNull() == false), "Check IdGenerator does not exits"); + + SituationMap::UniqueMapId id = idGenerator->getNextId(); + + QVERIFY2((id == 0), "Check IdGenerator initial value failed, wrong id"); + QVERIFY2((SituationMap::IdGenerator::idIsValid(id)), "Check IdGenerator id invalid"); + QVERIFY2((SituationMap::isUniqueMapIdValid(id)), "Check IdGenerator id invalid"); + + id = idGenerator->getNextId(); + QVERIFY2((id == 1), "Check IdGenerator initial value failed, wrong id"); + QVERIFY2((SituationMap::IdGenerator::idIsValid(id)), "Check IdGenerator id invalid"); + QVERIFY2((SituationMap::isUniqueMapIdValid(id)), "Check IdGenerator id invalid"); + + id = idGenerator->getNextId(); + QVERIFY2((id == 2), "Check IdGenerator initial value failed, wrong id"); + QVERIFY2((SituationMap::IdGenerator::idIsValid(id)), "Check IdGenerator id invalid"); + QVERIFY2((SituationMap::isUniqueMapIdValid(id)), "Check IdGenerator id invalid"); + + id = SituationMap::IdGenerator::INVALID_ID; + QVERIFY2((SituationMap::IdGenerator::idIsValid(id) == false), "Check INVALID_ID is NOT invalid"); + QVERIFY2((SituationMap::isUniqueMapIdValid(id) == false), "Check INVALID_ID is NOT invalid"); +} + +//class MyBuildingOutlineImpl : public SituationMap::BuildingOutlineImpl +//{ +//public: +// MyBuildingOutlineImpl(const QString &debugInfo = QString()) +// : SituationMap::BuildingOutlineImpl(debugInfo) +// {} +//}; + + +// create template to derive from XY-MapObjectImpl to overcome protected ctor: +template +class MyMapObjectType : public T +{ +public: + MyMapObjectType(const QString &debugInfo = QString()) + : T(debugInfo) + {} +}; + +using MyParticipantImpl = MyMapObjectType; +using MyBiologicalAgentImpl = MyMapObjectType; +using MyBuildingOutlineImpl = MyMapObjectType; + + +void SitMapUnitTest::checkIdContainer() +{ + QVERIFY2((IdGeneratorCounting::getCounter() == 0), "Check IdGeneratorCounting, count failed"); + + + // SituationMap::BuildingOutlineImpl bbbbbbbbb( QLatin1String("bio2") ); + + + { + // + // ONE IdGenerator connected to TWO ObjectContainer: + // + + QSharedPointer idGenerator(new IdGeneratorCounting()); + QVERIFY2((idGenerator.isNull() == false), "Check IdGenerator does not exits"); + QVERIFY2((IdGeneratorCounting::getCounter() == 1), "Check IdGeneratorCounting, count failed"); + + { + // + // FIRST ObjectContainer: + // + SituationMap::ObjectContainer *container1 = + new SituationMap::ObjectContainerCTC(idGenerator); + QVERIFY2((container1), "Check ObjectContainerCTC does not exits"); + + qlonglong count = container1->countObjects(); + QVERIFY2((count == 0), "Check ObjectContainer countObjects failed"); + + // create some dummyObjects: + SituationMap::RefPointer part1(new MyParticipantImpl(QLatin1String("part1"))); + QVERIFY2(part1.valid() == true , "create part failed"); + + SituationMap::RefPointer bio1(new MyBiologicalAgentImpl(QLatin1String("bio1"))); + QVERIFY2(bio1.valid() == true , "create failed"); + SituationMap::RefPointer bio2(new MyBiologicalAgentImpl(QLatin1String("bio2"))); + QVERIFY2(bio2.valid() == true , "create failed"); + + SituationMap::RefPointer build1(new MyBuildingOutlineImpl(QLatin1String("build1"))); + QVERIFY2(build1.valid() == true , "create failed"); + SituationMap::RefPointer build2(new MyBuildingOutlineImpl(QLatin1String("build2"))); + QVERIFY2(build2.valid() == true , "create failed"); + SituationMap::RefPointer build3(new MyBuildingOutlineImpl(QLatin1String("build3"))); + QVERIFY2(build3.valid() == true , "create failed"); + + + SituationMap::UniqueMapId id = SituationMap::IdGenerator::INVALID_ID; + uint i = 0; + // add to our idContainer1: + id = container1->insertObject(part1.get()); + QVERIFY2((id == i++), "Check IdContainer wrong id"); + id = container1->insertObject(bio1.get()); + QVERIFY2((id == i++), "Check IdContainer wrong id"); + id = container1->insertObject(bio2.get()); + QVERIFY2((id == i++), "Check IdContainer wrong id"); + id = container1->insertObject(build1.get()); + QVERIFY2((id == i++), "Check IdContainer wrong id"); + id = container1->insertObject(build2.get()); + QVERIFY2((id == i++), "Check IdContainer wrong id"); + id = container1->insertObject(build3.get()); + QVERIFY2((id == i++), "Check IdContainer wrong id"); + +// std::cerr << " id=" << id << std::endl; + + // check countObjects + count = container1->countObjects(); + QVERIFY2((count == 6), "Check IdContainer countObjects failed"); + + // cleanup + delete container1; + } + + // + // Instantiate SECOND IdContainer with SAME IdGenerator: + // + SituationMap::ObjectContainer *container2 = new SituationMap::ObjectContainerCTC(idGenerator); + + // idGenerator is reused, IdGeneratorCounting::getCounter() must stay at '1': + QVERIFY2((IdGeneratorCounting::getCounter() == 1), "Check IdGeneratorCounting, count failed"); + + qint32 count = container2->countObjects(); + QVERIFY2((count == 0), "Check IdContainer countObjects failed"); + + // create some dummyObjects: + SituationMap::RefPointer part1(new MyParticipantImpl(QLatin1String("part1"))); + QVERIFY2(part1.valid() == true , "create part failed"); + + SituationMap::RefPointer bio1(new MyBiologicalAgentImpl(QLatin1String("bio1"))); + QVERIFY2(bio1.valid() == true , "create failed"); + SituationMap::RefPointer bio2(new MyBiologicalAgentImpl(QLatin1String("bio2"))); + QVERIFY2(bio2.valid() == true , "create failed"); + + SituationMap::UniqueMapId id = SituationMap::IdGenerator::INVALID_ID; + // add to our idContainer2: + + // idGenerator is reused and therefore the uniqueId will continue -> start with '6' + uint i = 6; + id = container2->insertObject(part1.get()); + QVERIFY2((id == i++), "Check IdContainer wrong id"); + id = container2->insertObject(bio1.get()); + QVERIFY2((id == i++), "Check IdContainer wrong id"); + id = container2->insertObject(bio2.get()); + QVERIFY2((id == i++), "Check IdContainer wrong id"); + count = container2->countObjects(); + QVERIFY2((count == 3), "Check IdContainer countObjects failed"); + + // check access methods: + QList keyList = container2->getAllKeys(); + QVERIFY2((keyList.count() == 3), "Check IdContainer getAllKeys failed"); + + QList objList = container2->getAllObjects(); + QVERIFY2((objList.count() == 3), "Check IdContainer getAllObjects failed"); + + // SitMapUnitTest: look for BiologicalAgentIMPL + // ^^^^^^^^ ^^^^ +// std::type_index bioTypeIndex(typeid(SituationMap::BiologicalAgentImpl)); + std::type_index bioTypeIndex(typeid(MyBiologicalAgentImpl)); // use 'MyBiologicalAgentImpl' instead of 'SituationMap::BiologicalAgentImpl' - see insertion + objList = container2->getAllObjects(bioTypeIndex); + QVERIFY2((objList.count() == 2), "Check IdContainer getAllObjects(bioTypeIndex) failed"); + + + SituationMap::ObjectContainer::Object *access = container2->getObject(-2) ; + QVERIFY2((!access), "Check IdContainer getObject failed"); + + access = container2->getObject(7) ; + QVERIFY2((access), "Check IdContainer getObject failed"); + + + + // remove + bool success = container2->removeObject(7); + QVERIFY2((success), "Check IdContainer remove failed"); + count = container2->countObjects(); + QVERIFY2((count == 2), "Check IdContainer countObjects failed"); + + // clear: + container2->clear(); + count = container2->countObjects(); + QVERIFY2((count == 0), "Check IdContainer countObjects failed"); + + + // cleanup; + delete container2; + } + + QVERIFY2((IdGeneratorCounting::getCounter() == 0), "Check IdGeneratorCounting, count failed"); + +} + +void SitMapUnitTest::checkIsSignalConnected() +{ + { + CheckSignal cs; + connect(&cs, &CheckSignal::mySignal, this, &SitMapUnitTest::onCheckIsSignalConnected); + quint64 msec = 0; + bool isConnected = cs.isConnected(msec); + INFO_LOG(m_logger) << LOGFUNCTION << " isSignalConnected took " << msec << "[msec]"; + QVERIFY2(isConnected, "isSignalConnected failed (real slot)"); + } + + { + CheckSignal cs; + connect(&cs, &CheckSignal::mySignal, this, [ = ]() + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " mySignal recognized (lambda slot)"; + }); + quint64 msec = 0; + bool isConnected = cs.isConnected(msec); + INFO_LOG(m_logger) << LOGFUNCTION << " isSignalConnected took " << msec << "[msec]"; + QVERIFY2(isConnected, "isSignalConnected failed (lambda slot)"); + } + +} + +void SitMapUnitTest::onCheckIsSignalConnected() +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " mySignal recognized (real slot)"; +} + + +void SitMapUnitTest::checkMapLayer() +{ + SituationMap::MapLayer first; + QVERIFY2(first.isValid() == false, "MapLayer1 isValid failed"); + + SituationMap::MapLayer second(QLatin1String("LayerName"), + SituationMap::LayerType::Basemap); + QVERIFY2(second.isValid() == true, "MapLayer2 isValid failed"); +} + +void SitMapUnitTest::checkMapLayerOrderedList() +{ + SituationMap::MapLayerOrderedList mlol; + QVERIFY2(mlol.count() == 0, "MapLayerOrderedList count failed"); + + bool success = false; + + // add, check LayerType::Undefined + success = mlol.addLayer(QLatin1String("Basemap1"), SituationMap::LayerType::Undefined); + QVERIFY2(success == false, "MapLayerOrderedList: addLayer LayerType::Undefined failed"); + QVERIFY2(mlol.count() == 0, "MapLayerOrderedList count failed"); + + // add, check emptyName + success = mlol.addLayer(QString(), SituationMap::LayerType::Basemap); + QVERIFY2(success == false, "MapLayerOrderedList: addLayer LayerType::Undefined failed"); + QVERIFY2(mlol.count() == 0, "MapLayerOrderedList count failed"); + + // add, check emptyName + success = mlol.addLayer(QString(), SituationMap::LayerType::Operational); + QVERIFY2(success == false, "MapLayerOrderedList: addLayer LayerType::Undefined failed"); + QVERIFY2(mlol.count() == 0, "MapLayerOrderedList count failed"); + + + + // + // add first + // + success = mlol.addLayer(QLatin1String("Basemap1"), SituationMap::LayerType::Basemap); + QVERIFY2(success, "MapLayerOrderedList: addLayer failed"); + QVERIFY2(mlol.count() == 1, "MapLayerOrderedList count failed"); + + // add again: + success = mlol.addLayer(QLatin1String("Basemap1"), SituationMap::LayerType::Basemap); + QVERIFY2(success == false, "MapLayerOrderedList: addLayer failed"); + QVERIFY2(mlol.count() == 1, "MapLayerOrderedList count failed"); + + // add again: + success = mlol.addLayer(QLatin1String("Basemap2"), SituationMap::LayerType::Basemap); + QVERIFY2(success == true, "MapLayerOrderedList: addLayer failed"); + QVERIFY2(mlol.count() == 2, "MapLayerOrderedList count failed"); + + // add again (same name, different LayerType is not allowed) + success = mlol.addLayer(QLatin1String("Basemap2"), SituationMap::LayerType::Operational); + QVERIFY2(success == false, "MapLayerOrderedList: addLayer failed"); + QVERIFY2(mlol.count() == 2, "MapLayerOrderedList count failed"); + + // add again: + success = mlol.addLayer(QLatin1String("ObjectLayer3"), SituationMap::LayerType::Operational); + QVERIFY2(success == true, "MapLayerOrderedList: addLayer failed"); + QVERIFY2(mlol.count() == 3, "MapLayerOrderedList count failed"); + + // add again: + success = mlol.addLayer(QLatin1String("EmergencyCall"), SituationMap::LayerType::EmergencyCall); + QVERIFY2(success == true, "MapLayerOrderedList: addLayer failed"); + QVERIFY2(mlol.count() == 4, "MapLayerOrderedList count failed"); + + // + // contains + // + success = mlol.contains(QLatin1String("Basemap1"), SituationMap::LayerType::Undefined); + QVERIFY2(success == true, "MapLayerOrderedList: contains failed"); + success = mlol.contains(QLatin1String("Basemap1"), SituationMap::LayerType::Basemap); + QVERIFY2(success == true, "MapLayerOrderedList: contains failed"); + success = mlol.contains(QLatin1String("Basemap1"), SituationMap::LayerType::Operational); + QVERIFY2(success == false, "MapLayerOrderedList: contains failed"); + + success = mlol.contains(QLatin1String("ObjectLayer3"), SituationMap::LayerType::Undefined); + QVERIFY2(success == true, "MapLayerOrderedList: contains failed"); + success = mlol.contains(QLatin1String("ObjectLayer3"), SituationMap::LayerType::Basemap); + QVERIFY2(success == false, "MapLayerOrderedList: contains failed"); + success = mlol.contains(QLatin1String("ObjectLayer3"), SituationMap::LayerType::Operational); + QVERIFY2(success == true, "MapLayerOrderedList: contains failed"); + + success = mlol.contains(QLatin1String("EmergencyCall"), SituationMap::LayerType::EmergencyCall); + QVERIFY2(success == true, "MapLayerOrderedList: contains failed"); + + // + // getLayer + // + SituationMap::MapLayer mapLayer; + mapLayer = mlol.getLayer(QLatin1String("Basemap1")); + QVERIFY2(mapLayer.isValid() == true , "MapLayerOrderedList: getLayer failed"); + QVERIFY2(mapLayer.type() == SituationMap::LayerType::Basemap , "MapLayerOrderedList: getLayer failed"); + QVERIFY2(mapLayer.name().compare(QLatin1String("Basemap1")) == 0 , "MapLayerOrderedList: getLayer failed"); + + mapLayer = mlol.getLayer(QLatin1String("Basemap2")); + QVERIFY2(mapLayer.isValid() == true , "MapLayerOrderedList: getLayer failed"); + QVERIFY2(mapLayer.type() == SituationMap::LayerType::Basemap , "MapLayerOrderedList: getLayer failed"); + QVERIFY2(mapLayer.name().compare(QLatin1String("Basemap2")) == 0 , "MapLayerOrderedList: getLayer failed"); + + mapLayer = mlol.getLayer(QLatin1String("ObjectLayer3")); + QVERIFY2(mapLayer.isValid() == true , "MapLayerOrderedList: getLayer failed"); + QVERIFY2(mapLayer.type() == SituationMap::LayerType::Operational , "MapLayerOrderedList: getLayer failed"); + QVERIFY2(mapLayer.name().compare(QLatin1String("ObjectLayer3")) == 0 , "MapLayerOrderedList: getLayer failed"); + + mapLayer = mlol.getLayer(QLatin1String("EmergencyCall")); + QVERIFY2(mapLayer.isValid() == true , "MapLayerOrderedList: getLayer failed"); + QVERIFY2(mapLayer.type() == SituationMap::LayerType::EmergencyCall , "MapLayerOrderedList: getLayer failed"); + QVERIFY2(mapLayer.name().compare(QLatin1String("EmergencyCall")) == 0 , "MapLayerOrderedList: getLayer failed"); + + // + // layerNamesOrdered + // + QStringList slBase; slBase << QLatin1String("Basemap1") << QLatin1String("Basemap2"); + QStringList slObj; slObj << QLatin1String("ObjectLayer3"); + QStringList slEmer; slEmer << QLatin1String("EmergencyCall"); + + QStringList sl; + sl = mlol.layerNamesOrdered(SituationMap::LayerType::Basemap); + QVERIFY2(sl == slBase , "MapLayerOrderedList: layerNamesOrdered failed"); + sl = mlol.layerNamesOrdered(SituationMap::LayerType::Operational); + QVERIFY2(sl == slObj , "MapLayerOrderedList: layerNamesOrdered failed"); + sl = mlol.layerNamesOrdered(SituationMap::LayerType::EmergencyCall); + QVERIFY2(sl == slEmer, "MapLayerOrderedList: layerNamesOrdered failed"); + sl = mlol.layerNamesOrdered(); + QVERIFY2(sl == slBase + slObj + slEmer , "MapLayerOrderedList: layerNamesOrdered failed"); + + qDebug() << "layer11=" << mlol.layerNamesOrdered(); + + // + // addBasemap + // + success = mlol.addLayer(QLatin1String("newBasemapLayer"), SituationMap::LayerType::Basemap); + QVERIFY2(success == true, "MapLayerOrderedList: addLayer failed"); + QVERIFY2(mlol.count() == 5, "MapLayerOrderedList count failed"); + QStringList slOk = slBase; + slOk << QLatin1String("newBasemapLayer"); + slOk << slObj << slEmer; + sl = mlol.layerNamesOrdered(); + QVERIFY2(sl == slOk , "MapLayerOrderedList: layerNamesOrdered failed"); + + qDebug() << "layer22=" << mlol.layerNamesOrdered(); + + success = mlol.addLayer(QLatin1String("newObjectLayer"), SituationMap::LayerType::Operational); + QVERIFY2(success == true, "MapLayerOrderedList: addLayer failed"); + QVERIFY2(mlol.count() == 6, "MapLayerOrderedList count failed"); + slOk << QLatin1String("newObjectLayer"); + sl = mlol.layerNamesOrdered(); + QVERIFY2(sl == slOk , "MapLayerOrderedList: layerNamesOrdered failed"); + + + success = mlol.addLayer(QLatin1String("newBasemapLayer2"), SituationMap::LayerType::Basemap); + + qDebug() << "layer33=" << mlol.layerNamesOrdered(); + + QVERIFY2(success == true, "MapLayerOrderedList: addLayer failed"); + QVERIFY2(mlol.count() == 7, "MapLayerOrderedList count failed"); + slOk.insert(3, QLatin1String("newBasemapLayer2")); + sl = mlol.layerNamesOrdered(); + QVERIFY2(sl == slOk , "MapLayerOrderedList: layerNamesOrdered failed"); + + QVERIFY2(mlol.layerNamesOrdered(SituationMap::LayerType::Basemap).size() == 4 , "MapLayerOrderedList: layerNamesOrdered failed"); + QVERIFY2(mlol.layerNamesOrdered(SituationMap::LayerType::Operational).size() == 2 , "MapLayerOrderedList: layerNamesOrdered failed"); + + qDebug() << "layer44=" << mlol.layerNamesOrdered(); + + // + // orderTo + // + QStringList newOrder; + newOrder << QLatin1String("Basemap1") + << QLatin1String("newObjectLayer") + << QLatin1String("newBasemapLayer2") + << QLatin1String("ObjectLayer3") + << QLatin1String("newBasemapLayer") + << QLatin1String("EmergencyCall") // <-- must be moved on top + << QLatin1String("Basemap2") + ; + + success = mlol.orderLayerTo(newOrder); + QVERIFY2(success == true, "MapLayerOrderedList: orderLayerTo failed"); + + slOk.clear(); + slOk << QLatin1String("Basemap1") + << QLatin1String("newBasemapLayer2") + << QLatin1String("newBasemapLayer") + << QLatin1String("Basemap2") + << QLatin1String("newObjectLayer") + << QLatin1String("ObjectLayer3") + << QLatin1String("EmergencyCall") + ; + sl = mlol.layerNamesOrdered(); + QVERIFY2(sl == slOk , "MapLayerOrderedList: orderLayerTo failed"); + + qDebug() << "layer55=" << mlol.layerNamesOrdered(); + + // + // orderTo with missing 'EmergencyCall' + // + { + QStringList newOrder; + newOrder << QLatin1String("Basemap1") + << QLatin1String("newObjectLayer") + << QLatin1String("newBasemapLayer2") + << QLatin1String("ObjectLayer3") + << QLatin1String("newBasemapLayer") +// << QLatin1String("EmergencyCall") // <-- must be moved on top, even if not provided! + << QLatin1String("Basemap2") + ; + + success = mlol.orderLayerTo(newOrder); + QVERIFY2(success == true, "MapLayerOrderedList: orderLayerTo failed"); + + slOk.clear(); + slOk << QLatin1String("Basemap1") + << QLatin1String("newBasemapLayer2") + << QLatin1String("newBasemapLayer") + << QLatin1String("Basemap2") + << QLatin1String("newObjectLayer") + << QLatin1String("ObjectLayer3") + << QLatin1String("EmergencyCall") + ; + sl = mlol.layerNamesOrdered(); + QVERIFY2(sl == slOk , "MapLayerOrderedList: orderLayerTo failed"); + } + + qDebug() << "layer66=" << mlol.layerNamesOrdered(); + + + + + // take EmergencyCall into account: +} + +void SitMapUnitTest::checkLayerFactory() +{ + // Preperations: + bool success = false; + SituationMap::MapImpl mapImplDummy; + QVERIFY2(mapImplDummy.technicalLayerCount() == 0 , "layerFactory: technicalLayerCount failed"); + + SituationMap::LayerFactory &layerFac = mapImplDummy.layerFactory(); + + // real test + QVERIFY2(layerFac.layernames().count() == 0 , "layerFactory: creation failed"); + QVERIFY2(layerFac.count() == 0 , "layerFactory: creation failed"); + + SituationMap::LayerParameter lp; + success = layerFac.setLayerParameter(QLatin1String("test"), lp); + QVERIFY2(success == false , "layerFactory: setLayerParameter failed"); + + // #layer == 0 + QVERIFY2(layerFac.layernames().count() == 0 , "layerFactory: count failed"); + QVERIFY2(layerFac.count() == 0 , "layerFactory: count failed"); + QVERIFY2(mapImplDummy.technicalLayerCount() == 0 , "layerFactory: technicalLayerCount failed"); + + QVERIFY2(layerFac.layernames(SituationMap::LayerType::Basemap) .count() == 0 , "layerFactory: layernames failed"); + QVERIFY2(layerFac.layernames(SituationMap::LayerType::Operational).count() == 0 , "layerFactory: layernames failed"); + QVERIFY2(layerFac.layernames(SituationMap::LayerType::Rubberband) .count() == 0 , "layerFactory: layernames failed"); + +} + +class MyMapImpl : public SituationMap::MapImpl +{ + Q_OBJECT // -> include "unit_test.moc" explicit +public: + bool isReady() const override + { + return true; + } +}; + + + +void SitMapUnitTest::checkObjectFactory() +{ + // Preperations: + + MyMapImpl mapImplDummy; + mapImplDummy.init(SituationMap::Mode::Mode_2D); // to create MpaGraphicsView and m_graphicsOverlayListModel + + SituationMap::LayerFactory &layerFac = mapImplDummy.layerFactory(); + QVERIFY2(layerFac.count() == 0 , "layerFactory: count failed"); + + layerFac.initializeUnitTestLayer(); + + + SituationMap::ObjectFactory &objFac = mapImplDummy.objectFactory(); + + std::cerr << " count " << objFac.count() << std::endl; + + QVERIFY2(objFac.count() <= 0 , "objectFactory: count failed"); // IdHandler -> 0 ; IDContainer -> -1 + + QVERIFY2(layerFac.count() == 3 , "layerFac: count failed"); + + // use an LOGICAL layer (not basemap) + QStringList layernames = layerFac.layernames(); + QString layername = layernames[1]; + + // real test + // ObjectFactory::registerMe not called yet: + + SituationMap::RefPointer objPtr1; + objPtr1 = objFac.create(layername, QLatin1String("UnitTestId1")); + QVERIFY2(objPtr1.valid() == false , "objectFactory: create1 failed"); + + SituationMap::RefPointer objPtr2; + objPtr2 = objFac.create(layername, QLatin1String("UnitTestId2")); + QVERIFY2(objPtr2.valid() == false , "objectFactory: create2 failed"); + + SituationMap::RefPointer objPtr3; + objPtr3 = objFac.create(layername, QLatin1String("UnitTestId3")); + QVERIFY2(objPtr3.valid() == false , "objectFactory: create3 failed"); + + SituationMap::RefPointer objPtr4; + objPtr4 = objFac.create(layername, QLatin1String("UnitTestId4")); + QVERIFY2(objPtr4.valid() == false , "objectFactory: create4 failed"); + + QVERIFY2(layerFac.count() == 3 , "layerFactory: count failed"); + QVERIFY2(mapImplDummy.technicalLayerCount() == 1 , "layerFactory: technicalLayerCount failed"); + + // ObjectFactory::registerMe + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::Participant), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::BiologicalAgent), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::BuildingOutline), new SituationMap::GenericObjectFactory()); + SituationMap::ObjectFactory::registerMe(typeid(SituationMap::DangerArea), new SituationMap::GenericObjectFactory()); + + + QVERIFY2(objFac.count() == 0 , "objectFactory: count failed"); + + // now technicalLayerCount == 1 (basemap) + QVERIFY2(mapImplDummy.technicalLayerCount() == 1 , "layerFactory: technicalLayerCount failed"); + + objPtr1 = objFac.create(layername, QLatin1String("UnitTestId1")); + QVERIFY2(objPtr1.valid() == true , "objectFactory: create11 failed"); + + objPtr2 = objFac.create(layername, QLatin1String("UnitTestId2")); + QVERIFY2(objPtr2.valid() == true , "objectFactory: create22 failed"); + + objPtr3 = objFac.create(layername, QLatin1String("UnitTestId3")); + QVERIFY2(objPtr3.valid() == true , "objectFactory: create33 failed"); + + objPtr4 = objFac.create(layername, QLatin1String("UnitTestId4")); + QVERIFY2(objPtr4.valid() == true , "objectFactory: create44 failed"); + + QVERIFY2(objFac.count() == 4 , "objectFactory: count failed"); + + // now technicalLayerCount == 1 (basemap, layername#...) + QVERIFY2(mapImplDummy.technicalLayerCount() == 2 , "layerFactory: technicalLayerCount failed"); + + + // check rubberbandLayer: + // method 'rubberbandLayer' is not available at the interface: + SituationMap::LayerFactoryImpl *layerFacImpl = + dynamic_cast(&layerFac); + QVERIFY2(layerFacImpl , "layerFac: dynamic_cast failed"); + + const QString &rbLayerName = layerFacImpl->rubberbandLayer(); + QVERIFY2(rbLayerName.isEmpty() == false , "layerFac: rubberbandLayer failed"); + + objPtr2 = objFac.create(rbLayerName, QLatin1String("RbLayerTestId1")); + QVERIFY2(objPtr2.valid() == true , "objectFactory: create111 failed"); + + // now technicalLayerCount == 1 (basemap, layername#..., RubberbandLayer#...) + QVERIFY2(mapImplDummy.technicalLayerCount() == 3 , "layerFactory: technicalLayerCount failed"); + + + const QString &toolLayerName = layerFacImpl->toolLayer(); + QVERIFY2(toolLayerName.isEmpty() == false , "layerFac: toolLayer failed"); + + objPtr3 = objFac.create(toolLayerName, QLatin1String("ToolLayerTestId1")); + QVERIFY2(objPtr3.valid() == true , "objectFactory: create222 failed"); + + if (objPtr1.valid()) + { + // test re-use an uniqueId + SituationMap::UniqueMapId uid = objPtr1->uniqueMapId(); + objPtr1->removeMySelf(); + objPtr1 = objFac.create(uid,layername, QLatin1String("UnitTestRecreate")); + QVERIFY2(objPtr1.valid() == true, "objectFactory: re-create failed"); + QVERIFY2(uid == objPtr1->uniqueMapId(), "objectFactory: re-used removed uniqueId failed"); + } + + //further test require GUI -> another UnitTest + +} + +void SitMapUnitTest::checkPickableOnInvalidUniqueMapId() +{ + // Preperations: + + SituationMap::MapImpl mapImplDummy; + + SituationMap::LayerFactory &layerFac = mapImplDummy.layerFactory(); + QVERIFY2(layerFac.count() == 0 , "LayerFactory: count failed"); + + + // use an LOGICAL layer (not basemap) + QStringList layernames = layerFac.layernames(); + + + // real test + + SituationMap::LayerFactoryImpl *layerFacImpl = + dynamic_cast(&layerFac); + QVERIFY2(layerFacImpl , "LayerFactory: layerFacImpl failed"); + + QSharedPointer tactApp + = QSharedPointer ( + new SituationMap::TacticalAppearance(&mapImplDummy, QLatin1String("AES"), QLatin1String("debugIdUnitTest")) + ); + QVERIFY2(tactApp.data() , "TacticalAppearance failed"); + + QVERIFY2(tactApp->uniqueMapId() == SituationMap::INVALID_MAP_ID , "uniqueMapId failed"); + QVERIFY2(tactApp->pickable() == false , "pickable failed"); + + // must not succeed due to invalid uniqueMapId: + tactApp->setPickable(true); // must not succeed due to invalid uniqueMapId + + QVERIFY2(tactApp->pickable() == false , "pickable failed"); + + +} + + +void SitMapUnitTest::checkBoundingBox() +{ + { + SituationMap::BoundingBox bbox(SituationMap::BoundingBox::ExtentType::Sim); + QVERIFY2(bbox.isValid() == false, "isValid failed"); + } + { + SituationMap::BoundingBox bbox(SituationMap::BoundingBox::ExtentType::Pixel); + QVERIFY2(bbox.isValid() == false, "isValid failed"); + } + + const double zVal = 1.23456; + SituationMap::Point3D center(100, 1000, zVal); + double width = 20; + double height = 60; + + SituationMap::BoundingBox bbox(SituationMap::BoundingBox::ExtentType::Sim, + center, width, height); + QVERIFY2(bbox.isValid() == true, "isValid failed"); + QVERIFY2(bbox.extentType() == SituationMap::BoundingBox::ExtentType::Sim, "extentType failed"); + + QVERIFY2(bbox.center() == center, "center failed"); + QVERIFY2(bbox.width() == width, "width failed"); + QVERIFY2(bbox.height() == height, "height failed"); + + QVERIFY2(bbox.topLeft() == SituationMap::Point3D( 90, 1030, zVal), "topLeft failed"); + QVERIFY2(bbox.topRight() == SituationMap::Point3D(110, 1030, zVal), "topLeft failed"); + QVERIFY2(bbox.bottomLeft() == SituationMap::Point3D( 90, 970, zVal), "topLeft failed"); + QVERIFY2(bbox.bottomRight() == SituationMap::Point3D(110, 970, zVal), "topLeft failed"); + + bbox.reset(); + QVERIFY2(bbox.isValid() == false, "isValid failed"); + QVERIFY2(bbox.extentType() == SituationMap::BoundingBox::ExtentType::Undefined, "extentType failed"); + + QVERIFY2(bbox.center().isNull() == true, "center failed"); + QVERIFY2(bbox.width() < 0.001, "width failed"); + QVERIFY2(bbox.height() < 0.001, "height failed"); + +} + +void SitMapUnitTest::checkDistanceMeasurement() +{ + Esri::ArcGISRuntime::SpatialReference sr = Esri::ArcGISRuntime::SpatialReference::wgs84(); + + Esri::ArcGISRuntime::Point p1 = + Esri::ArcGISRuntime::CoordinateFormatter::fromMgrs(QLatin1String("32UMD 00001 00001"), sr, Esri::ArcGISRuntime::MgrsConversionMode::Automatic); + Esri::ArcGISRuntime::Point p2 = + Esri::ArcGISRuntime::CoordinateFormatter::fromMgrs(QLatin1String("32UMD 00011 00001"), sr, Esri::ArcGISRuntime::MgrsConversionMode::Automatic); + + QVERIFY2(p1.isEmpty() == false, "fromMgrs failed"); + QVERIFY2(p1.spatialReference().wkid() == 4326, "spatialReference failed"); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " p.x=" << p1.x() << " p.y=" << p1.y() << " p.z=" << p1.z() + << " sr=" << p1.spatialReference().wkid(); + + // + // 1. checkDistanceMeasurement + // + const double distance = + Esri::ArcGISRuntime::GeometryEngine::distance(Esri::ArcGISRuntime::Point(p1.x(), p1.y(), sr), + Esri::ArcGISRuntime::Point(p2.x(), p2.y(), sr)); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " GeometryEngine::distance() " << distance; + + // + // 2. distanceGeodetic + // + const Esri::ArcGISRuntime::GeodeticDistanceResult geodeticDistance = + Esri::ArcGISRuntime::GeometryEngine::distanceGeodetic(Esri::ArcGISRuntime::Point(p1.x(), p1.y(), sr), + Esri::ArcGISRuntime::Point(p2.x(), p2.y(), sr), + Esri::ArcGISRuntime::LinearUnitId::Meters, + Esri::ArcGISRuntime::AngularUnitId::Degrees, + Esri::ArcGISRuntime::GeodeticCurveType::Geodesic); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " GeometryEngine::distanceGeodetic() " << geodeticDistance.distance(); + + QVERIFY2(geodeticDistance.distance() -10.0 < 0.01, "distanceGeodetic failed"); + + // ====> use distanceGeodetic +} + + +void SitMapUnitTest::recalculateAngle() +{ + // aus Transformationspunkte GUEZ, Stand 08/2018: + // (Werte sind EPSG-Code 31468) + // BN_94: x=4473185.721 ; y=5817742.350 ; Drehwinkel=149.3805 + // Wir benötigen den Drehwinkel aber in Bezug auf EPSG-Code 32632. + + const Esri::ArcGISRuntime::SpatialReference srcSr(31468); + const Esri::ArcGISRuntime::SpatialReference destSr(32632); + const QPointF srcPoint(4473185.721, 5817742.350); // BN_94 +// const QPointF srcPoint(4472996.165, 5817720.809); // BN_152 + const double srcAngleDegree = 149.3805; + + // Ansatz: + // Einen zusätzlichen Hilfspunkt bestimmen (Winkel == 0.0), + // beiden Punkte in die Zielprojektion überführen, + // dann den Winkel zw. diesen projezierten Punkten bestimmen. + // Somit erhalten wir eine Differenz zum ursprünglichen Winkel + + const double offset = 1.0; // meter + + QPointF h1 = srcPoint; + QPointF h2 = srcPoint + QPointF(offset, 0.0); // nur x, damit Winkel 0.0 ist + qCritical() << " h1= " << h1; + qCritical() << " h2= " << h2; + + // project: + Esri::ArcGISRuntime::Point esriH1 = + Esri::ArcGISRuntime::GeometryEngine::project(Esri::ArcGISRuntime::Point(h1.x(), h1.y(), 0.0, srcSr), destSr); + Esri::ArcGISRuntime::Point esriH2 = + Esri::ArcGISRuntime::GeometryEngine::project(Esri::ArcGISRuntime::Point(h2.x(), h2.y(), 0.0, srcSr), destSr); + + + // Winkel bestimmen: + double dX = esriH1.x() - esriH2.x(); + double dY = esriH1.y() - esriH2.y(); + + double destAngleRad = atan(dY / dX); + double destAngleDeg = destAngleRad / M_PI * 180.0; + qCritical() << " destAngleRad= " << QString::number(destAngleRad, 'f', 6); + qCritical() << " destAngleDeg= " << QString::number(destAngleDeg, 'f', 6); + + + qCritical() << " srcAngle : " << QString::number(srcAngleDegree, 'f', 6); + qCritical() << " destAngle: " << QString::number(srcAngleDegree + destAngleDeg, 'f', 6); +} + + +void SitMapUnitTest::measureDistanceGeodetic() +{ + Esri::ArcGISRuntime::SpatialReference sr = Esri::ArcGISRuntime::SpatialReference::wgs84(); + + Esri::ArcGISRuntime::Point p1 = + Esri::ArcGISRuntime::CoordinateFormatter::fromMgrs(QLatin1String("32UMD 00001 00001"), sr, Esri::ArcGISRuntime::MgrsConversionMode::Automatic); + Esri::ArcGISRuntime::Point p2 = + Esri::ArcGISRuntime::CoordinateFormatter::fromMgrs(QLatin1String("32UMD 00011 00001"), sr, Esri::ArcGISRuntime::MgrsConversionMode::Automatic); + + int jMax = 1000; + QElapsedTimer timer; + timer.start(); + for (int j = 0; j < jMax; ++j) + { + const Esri::ArcGISRuntime::GeodeticDistanceResult geodeticDistance = + Esri::ArcGISRuntime::GeometryEngine::distanceGeodetic(Esri::ArcGISRuntime::Point(p1.x(), p1.y(), sr), + Esri::ArcGISRuntime::Point(p2.x(), p2.y(), sr), + Esri::ArcGISRuntime::LinearUnitId::Meters, + Esri::ArcGISRuntime::AngularUnitId::Degrees, + Esri::ArcGISRuntime::GeodeticCurveType::Geodesic); + if (geodeticDistance.distance() > 11) + qDebug() << " geodeticDistance: "; + } + qDebug() << " distanceGeodetic: " << jMax << " operations took " << timer.elapsed() << "ms"; + +} + +void SitMapUnitTest::checkDensify() +{ + // connect errorHandling: + connect(Esri::ArcGISRuntime::GeometryEngine::instance(), &Esri::ArcGISRuntime::GeometryEngine::errorOccurred, + this, [this](const Esri::ArcGISRuntime::Error &error) + { + FATAL_LOG(m_logger) << LOGFUNCTION << " ---EsriError--- occured: " << error; + }); + + + Esri::ArcGISRuntime::SpatialReference sr = Esri::ArcGISRuntime::SpatialReference::wgs84(); + + Esri::ArcGISRuntime::Point p1, p2; + + + // + // 1. densify + // + { + p1 = Esri::ArcGISRuntime::Point(8.0, 53.0, sr); + p2 = Esri::ArcGISRuntime::Point(9.0, 53.0, sr); + + // create Polyline: + Esri::ArcGISRuntime::PolylineBuilder polylineBuilder(sr, this); + polylineBuilder.addPoint(p1); + polylineBuilder.addPoint(p2); + const Esri::ArcGISRuntime::Polyline &polylineIn = polylineBuilder.toPolyline(); + + + const Esri::ArcGISRuntime::Geometry &geometryOut = Esri::ArcGISRuntime::GeometryEngine::densify(polylineIn, 0.1); + + QVERIFY2(geometryOut.geometryType() == Esri::ArcGISRuntime::GeometryType::Polyline, "geometryType failed"); + + const Esri::ArcGISRuntime::Polyline polylineOut(geometryOut); + + QVERIFY2(polylineOut.parts().size() == 1, "polylineOut.parts().size() failed"); + qCritical() << "polylineOut.parts().part(0).pointCount() = " << polylineOut.parts().part(0).pointCount(); + QVERIFY2(polylineOut.parts().part(0).pointCount() > 10, "polylineOut.parts().size() failed"); + QVERIFY2(polylineOut.parts().part(0).pointCount() < 15, "polylineOut.parts().size() failed"); + } + + // + // 2. densifyGeodetic - ok? + // + { + + p1 = Esri::ArcGISRuntime::CoordinateFormatter::fromMgrs(QLatin1String("32UMD 00001 00001"), sr, Esri::ArcGISRuntime::MgrsConversionMode::Automatic); + p2 = Esri::ArcGISRuntime::CoordinateFormatter::fromMgrs(QLatin1String("32UMD 00001 01001"), sr, Esri::ArcGISRuntime::MgrsConversionMode::Automatic); + + // create Polyline: + Esri::ArcGISRuntime::PolylineBuilder polylineBuilder(sr, this); + polylineBuilder.addPoint(p1); + polylineBuilder.addPoint(p2); + const Esri::ArcGISRuntime::Polyline &polylineIn = polylineBuilder.toPolyline(); + + const Esri::ArcGISRuntime::Geometry &geometryOut = Esri::ArcGISRuntime::GeometryEngine::densifyGeodetic(polylineIn, + 100, + Esri::ArcGISRuntime::LinearUnit::meters(), + Esri::ArcGISRuntime::GeodeticCurveType::Geodesic); + + QVERIFY2(geometryOut.geometryType() == Esri::ArcGISRuntime::GeometryType::Polyline, "geometryType failed"); + + const Esri::ArcGISRuntime::Polyline polylineOut(geometryOut); + + QVERIFY2(polylineOut.parts().size() == 1, "polylineOut.parts().size() failed"); + qCritical() << "polylineOut.parts().part(0).pointCount() = " << polylineOut.parts().part(0).pointCount(); + QVERIFY2(polylineOut.parts().part(0).pointCount() > 10, "polylineOut.parts().size() failed"); + QVERIFY2(polylineOut.parts().part(0).pointCount() < 15, "polylineOut.parts().size() failed"); + } + + +} + + +QPolygonF bufferUtmPolygon(const QPolygonF &polygonUtmIn, double distance, double maxDeviation = 1, QString *errorOut = nullptr) +{ + Q_UNUSED(maxDeviation); + + Esri::ArcGISRuntime::PolygonBuilder polygonBuilder(Esri::ArcGISRuntime::SpatialReference(32632)); + + for (const QPointF &pointUtm : std::as_const(polygonUtmIn)) + polygonBuilder.addPoint(Esri::ArcGISRuntime::Point(pointUtm.x(), pointUtm.y(), 0)); + const Esri::ArcGISRuntime::Geometry &esriGeometry = polygonBuilder.toGeometry(); + + const Esri::ArcGISRuntime::Polygon polyline = + Esri::ArcGISRuntime::GeometryEngine::buffer(esriGeometry, distance); + + if (polyline.parts().isEmpty()) + { + if (errorOut) + *errorOut = QStringLiteral("GeometryEngine::buffer failed (no parts"); + return {}; + } + + + const Esri::ArcGISRuntime::ImmutablePart &immutablePart = polyline.parts().part(0); + const int size = immutablePart.pointCount(); + + QPolygonF bufferedPoly; + bufferedPoly.reserve(size); + + for (int i = 0; i < size; ++i) + { + const Esri::ArcGISRuntime::Point &esriP = immutablePart.point(i); + bufferedPoly.append(QPointF(esriP.x(), esriP.y())); + } + + return bufferedPoly; +} + + +void SitMapUnitTest::checkBuffer() +{ + // Für Gerlinde, Thema Gefährungsabstand + + // Hier der Stellvertreter für das Wirkfeld: + QPolygonF wirkfeldPoly({ {0.0, 0.0}, {1000.0, 0.0}, {1000.0, 1000.0} }); + + NOTICE_LOG(m_logger) << LOGFUNCTION << " wirkfeldPoly.size = " << wirkfeldPoly.size(); + for (auto item : std::as_const(wirkfeldPoly)) + NOTICE_LOG(m_logger) << LOGFUNCTION << " wirkfeldPoly = " << item; + + + QString errorOut; + const QPolygonF &bufferedPoly = bufferUtmPolygon(wirkfeldPoly, 10, 1, &errorOut); + + NOTICE_LOG(m_logger) << LOGFUNCTION << " bufferedPoly.size = " << bufferedPoly.size(); + for (auto item : std::as_const(bufferedPoly)) + NOTICE_LOG(m_logger) << LOGFUNCTION << " bufferedPoly = " << item; +} + +void SitMapUnitTest::checkMovementTrack() +{ + const Esri::ArcGISRuntime::SpatialReference &sr = Esri::ArcGISRuntime::SpatialReference::wgs84(); + + SituationMap::MovementTrack mt(4711); + + bool success; + + QVERIFY2(mt.size() == 0, "size failed"); + QVERIFY2(mt.isActive() == true, "isActive failed"); + + QVERIFY2(mt.getTrack().size() == 0, "getTrack failed"); + QVERIFY2(mt.getVisibleTrack(0, 999999, 999999).size() == 0, "getVisibleTrack failed"); + + Esri::ArcGISRuntime::Point eP1 = + Esri::ArcGISRuntime::CoordinateFormatter::fromMgrs(QLatin1String("32UMD 10000 10000"), sr, Esri::ArcGISRuntime::MgrsConversionMode::Automatic); + SituationMap::Point3D p1(eP1.x()/180.0*M_PI, eP1.y()/180.0*M_PI, eP1.z()); // -> rad + + success = mt.addPosition(p1, 1000, 1, 99999); + QVERIFY2(success, "success failed"); + QVERIFY2(mt.size() == 1, "size failed"); + QVERIFY2(mt.getTrack().size() == 1, "getTrack failed"); + + // check via maxViewAgeMsec (3rd) + QVERIFY2(mt.getVisibleTrack(2000, 999999, 999).size() == 0, "getVisibleTrack failed"); + QVERIFY2(mt.getVisibleTrack(2000, 999999, 1000).size() == 1, "getVisibleTrack failed"); + + // check via maxAgeMsec (2rd) + QVERIFY2(mt.getTrack().size() == 1, "getTrack failed"); + QVERIFY2(mt.getVisibleTrack(2000, 1000, 1000).size() == 1, "getVisibleTrack failed"); + QVERIFY2(mt.getTrack().size() == 1, "getTrack failed"); + QVERIFY2(mt.getVisibleTrack(2000, 999, 1000).size() == 0, "getVisibleTrack failed"); + QVERIFY2(mt.getTrack().size() == 0, "getTrack failed"); + + Esri::ArcGISRuntime::Point eP2 = + Esri::ArcGISRuntime::CoordinateFormatter::fromMgrs(QLatin1String("32UMD 10001 10000"), sr, Esri::ArcGISRuntime::MgrsConversionMode::Automatic); + SituationMap::Point3D p2(eP2.x()/180.0*M_PI, eP2.y()/180.0*M_PI, eP2.z()); // -> rad + + Esri::ArcGISRuntime::Point eP3 = + Esri::ArcGISRuntime::CoordinateFormatter::fromMgrs(QLatin1String("32UMD 10010 10000"), sr, Esri::ArcGISRuntime::MgrsConversionMode::Automatic); + SituationMap::Point3D p3(eP3.x()/180.0*M_PI, eP3.y()/180.0*M_PI, eP3.z()); // -> rad + + success = mt.addPosition(p1, 1000, 2, 99999); + QVERIFY2(success, "success failed addPosition"); + QVERIFY2(mt.getTrack().size() == 1, "getTrack failed"); + + success = mt.addPosition(p2, 1100, 2, 99999); // p2 should NOT be added: + QVERIFY2(!success, "success failed addPosition"); + QVERIFY2(mt.getTrack().size() == 1, "getTrack failed"); + + success = mt.addPosition(p3, 1100, 2, 99999); // p3 should be added: + QVERIFY2(success, "success failed addPosition"); + QVERIFY2(mt.getTrack().size() == 2, "getTrack failed"); + + + QJsonDocument jDoc(mt.toJsonObject()); + { + // to json +// QFile saveFile(QStringLiteral("/tmp/mt.json")); +// if (!saveFile.open(QIODevice::WriteOnly)) { +// qWarning("Couldn't open mt file."); +// return; +// } +// saveFile.write(jDoc.toJson()); +// qCritical() << "wrote " << QFileInfo(saveFile).absoluteFilePath(); +// qCritical() << "mt.toJson()=" << jDoc.toJson(); + } + { + // read json + const QJsonObject &objTemp = jDoc.object(); + QSharedPointer newMt = SituationMap::MovementTrack::read(objTemp); + + QVERIFY2(newMt->trackId() == mt.trackId(), " json: trackId failed"); + QVERIFY2(newMt->isActive() == mt.isActive(), " json: isActive failed"); + QVERIFY2(newMt->getTrack().size() == mt.getTrack().size(), " json: getTrack failed"); + } +} + +void SitMapUnitTest::checkMovementTrackManager() +{ + SituationMap::MovementTrackManager mtMgr; + + mtMgr.setCurrentTimeMsec(123); + QVERIFY2(mtMgr.currentTimeMsec() == 123, "currentTimeMsec failed"); + + mtMgr.setMaxAgeMsec(1234); + QVERIFY2(mtMgr.maxAgeMsec() == 1234, "maxAgeMsec failed"); + + mtMgr.setMaxViewAgeMsec(55); + QVERIFY2(mtMgr.maxViewAgeMsec() == 55, "maxViewAgeMsec failed"); + + mtMgr.setMaxNumberOfTracks(3); + QVERIFY2(mtMgr.maxNumberOfTracks() == 3, "maxNumberOfTracks failed"); + + mtMgr.setMaxNumberOfPointsPerTrack(5); + QVERIFY2(mtMgr.maxNumberOfPointsPerTrack() == 5, "maxNumberOfTracks failed"); + + mtMgr.setMinDistance(7); + QVERIFY2(mtMgr.minDistance() == 7, "minDistance failed"); + + bool success; + + QVERIFY2(mtMgr.size() == 0, "size failed"); + QVERIFY2(mtMgr.hasTrack(0) == false, "hasTrack failed"); + QVERIFY2(mtMgr.activeTrackIds().size() == 0, "objectIdsOfActiveTracks failed"); + QVERIFY2(mtMgr.activeAndVisibleTrack(0).size() == 0, "activeAndVisibleTrack failed"); + + // set some positions: + double lon = 0.155; + double lat = 0.926; + const SituationMap::Point3D p1(lon+0.000, lat, 0.0); + const SituationMap::Point3D p2(lon+0.001, lat, 0.0); + const SituationMap::Point3D p3(lon+0.002, lat, 0.0); + const SituationMap::Point3D p4(lon+0.003, lat, 0.0); + const SituationMap::Point3D p5(lon+0.004, lat, 0.0); + + // check MaxNumberOfPointsPerTrack + { + QVERIFY2(mtMgr.maxNumberOfPointsPerTrack() == 5, "maxNumberOfTracks failed"); + QVERIFY2(mtMgr.minDistance() == 7, "minDistance failed"); + + mtMgr.activateTrack(4711); + + success = true; + success = success && mtMgr.addPosition(4711, p1, 10001); + success = success && mtMgr.addPosition(4711, p2, 10002); + success = success && mtMgr.addPosition(4711, p3, 10003); + success = success && mtMgr.addPosition(4711, p4, 10004); + success = success && mtMgr.addPosition(4711, p5, 10005); + success = success && mtMgr.addPosition(4711, p1, 10006); // maxNumberOfTracks exceeded -> pt1 removed + QVERIFY2(success == true, "addPosition failed"); + QVERIFY2(mtMgr.activeAndVisibleTrack(4711).size() == 5, "activeAndVisibleTrack failed"); + + QVERIFY2(mtMgr.size() == 1, "size failed"); + QVERIFY2(mtMgr.hasTrack(4711) == true, "hasTrack failed"); + + QVERIFY2(mtMgr.activeTrackIds().size() == 1, "objectIdsOfActiveTracks failed"); + QVERIFY2(mtMgr.activeAndVisibleTrack(4711).size() == 5, "activeAndVisibleTrack failed"); + } + + // maxViewAgeMsec + { + int size = 0; + + // a) + mtMgr.setCurrentTimeMsec(10006); + QVERIFY2(mtMgr.currentTimeMsec() == 10006, "currentTimeMsec failed"); + + mtMgr.setMaxViewAgeMsec(2); + size = mtMgr.activeAndVisibleTrack(4711).size(); + QVERIFY2(size == 3, "activeAndVisibleTrack failed"); // 10006, 10005, 10004 + + // b) set currentTimeMsec forward + mtMgr.setCurrentTimeMsec(10008); + + mtMgr.setMaxViewAgeMsec(2); + size = mtMgr.activeAndVisibleTrack(4711).size(); + QVERIFY2(size == 1, "activeAndVisibleTrack failed"); // 10008, 10007, 10006 + + // c) set currentTimeMsec backward + mtMgr.setCurrentTimeMsec(10002); + + size = mtMgr.activeAndVisibleTrack(4711).size(); + QVERIFY2(size == 1, "activeAndVisibleTrack failed"); // 10002 (10001/pt1 removed) + } + + + // check removing of track, 1 + { + QVERIFY2(mtMgr.maxNumberOfTracks() == 3, "maxNumberOfTracks failed"); + + mtMgr.activateTrack(4712); + QVERIFY2(mtMgr.addPosition(4712, p1, 10001) == true, "addPosition failed"); + + mtMgr.activateTrack(4713); + QVERIFY2(mtMgr.addPosition(4713, p1, 10001) == true, "addPosition failed"); + QVERIFY2(mtMgr.size() == 3, "size failed"); + QVERIFY2(mtMgr.activeTrackIds().size() == 3, "objectIdsOfActiveTracks failed"); + + mtMgr.activateTrack(4714); + QVERIFY2(mtMgr.addPosition(4714, p1, 10001) == true, "addPosition failed"); + QVERIFY2(mtMgr.size() == 3, "size failed"); + QVERIFY2(mtMgr.activeTrackIds().size() == 3, "objectIdsOfActiveTracks failed"); + + QVERIFY2(mtMgr.hasTrack(4711) == false, "hasTrack failed"); + } + + // check removing of track, 2 + { + // 4712, 4713, 4714 + const QVector activeTracks1 = mtMgr.activeTrackIds(); + QVERIFY2(activeTracks1 == QVector() << 4712 << 4713 << 4714, "objectIdsOfActiveTracks failed"); + + mtMgr.deactivateTrack(4713); + + // still 3 tracks, but just 2 active: + QVERIFY2(mtMgr.size() == 3, "size failed"); + const QVector activeTracks2 = mtMgr.activeTrackIds(); + QVERIFY2(activeTracks2 == QVector() << 4712 /*<< 4713*/ << 4714, "objectIdsOfActiveTracks failed"); + + + // activate track 4715 -> deactived track 4713 has to be removed: + mtMgr.activateTrack(4715); + QVERIFY2(success == true, "addPosition failed"); + QVERIFY2(mtMgr.size() == 3, "size failed"); + + // 4712, 4714, 4715 + QVERIFY2(mtMgr.activeTrackIds().size() == 3, "objectIdsOfActiveTracks failed"); + const QVector activeTracks3 = mtMgr.activeTrackIds(); + QVERIFY2(activeTracks3 == QVector() << 4712 << 4714 << 4715, "objectIdsOfActiveTracks failed"); + + QVERIFY2(mtMgr.hasTrack(4711) == false, "hasTrack failed"); + QVERIFY2(mtMgr.hasTrack(4712) == true, "hasTrack failed"); + QVERIFY2(mtMgr.hasTrack(4713) == false, "hasTrack failed"); + QVERIFY2(mtMgr.hasTrack(4714) == true, "hasTrack failed"); + QVERIFY2(mtMgr.hasTrack(4715) == true, "hasTrack failed"); + } + + + QVERIFY2(mtMgr.size() > 0, " json: size insufficiant"); + + const qint64 CURRENT_TIME_MSEC = 1516632216.0 * 1000.0; // to use qint64 + mtMgr.setCurrentTimeMsec(CURRENT_TIME_MSEC); + + QJsonDocument jDoc(mtMgr.toJsonObject()); + { + // to json + QFile saveFile(QStringLiteral("/tmp/mtMgr.json")); + if (!saveFile.open(QIODevice::WriteOnly)) { + qWarning("Couldn't open mt file."); + return; + } + saveFile.write(jDoc.toJson()); + qCritical() << "wrote " << QFileInfo(saveFile).absoluteFilePath(); + qCritical() << "mtMgr.toJson()=" << jDoc.toJson(); + } + { + // read json + SituationMap::MovementTrackManager newMtMgr; + const QJsonObject &objTemp = jDoc.object(); + newMtMgr.read(objTemp); + + QVERIFY2(newMtMgr.size() > 0, " json: size failed"); + QVERIFY2(newMtMgr.size() == mtMgr.size(), " json: size failed"); + + qint64 diff; + diff = mtMgr.currentTimeMsec() - CURRENT_TIME_MSEC; + QVERIFY2(qFuzzyIsNull(double(diff)) == true, " json: maxAgeMsec failed"); + diff = newMtMgr.currentTimeMsec() - CURRENT_TIME_MSEC; + QVERIFY2(qFuzzyIsNull(double(diff)) == true, " json: maxAgeMsec failed"); + + diff = newMtMgr.maxAgeMsec() - mtMgr.maxAgeMsec(); + QVERIFY2(qFuzzyIsNull(double(diff)) == true, " json: maxAgeMsec failed"); + + diff = newMtMgr.maxViewAgeMsec() - mtMgr.maxViewAgeMsec(); + QVERIFY2(qFuzzyIsNull(double(diff)) == true, " json: maxViewAgeMsec failed"); + + QVERIFY2(newMtMgr.maxNumberOfTracks() == mtMgr.maxNumberOfTracks(), " json: maxNumberOfTracks failed"); + QVERIFY2(newMtMgr.maxNumberOfPointsPerTrack() == mtMgr.maxNumberOfPointsPerTrack(), " json: maxNumberOfPointsPerTrack failed"); + QVERIFY2(newMtMgr.minDistance() == mtMgr.minDistance(), " json: minDistance failed"); + + const QVector &activeTrackIds = newMtMgr.activeTrackIds(); + for (const auto &trackId : activeTrackIds) + QVERIFY2(newMtMgr.activeAndVisibleTrack(trackId).size() == mtMgr.activeAndVisibleTrack(trackId).size(), " json: activeAndVisibleTrack failed"); + } +} + + +void checkSidcCodecEncoded(const QString &sidc, + const QString &rawSidc, + AdvancedSymbology::SymbologyType symType, + AdvancedSymbology::SidcCodec::SidcType sidcType) +{ + QVERIFY2(AdvancedSymbology::SidcCodec::isEncoded(sidc) == true, "isEncoded"); + QVERIFY2(AdvancedSymbology::SidcCodec::decodeSymbologyType(sidc) == symType, "decodeSymbologyType"); + QVERIFY2(AdvancedSymbology::SidcCodec::decodeSidcType(sidc) == sidcType, "decodeSidcType"); + QVERIFY2(AdvancedSymbology::SidcCodec::decodeRawSidc(sidc).compare(rawSidc) == 0, "decodeRawSidc"); +} + + +void SitMapUnitTest::checkSidcCodec() +{ + // M25D|CMPL|10012500002713000000 + { + { + QString sidc; + sidc = QLatin1String("AP6B|CMPL|10012500002713000000"); + checkSidcCodecEncoded(sidc, QLatin1String("10012500002713000000"), AdvancedSymbology::SymbologyType::APP6B, AdvancedSymbology::SidcCodec::SidcType::Complete); + + sidc = QLatin1String("AP6D|CMPL|10012500002713000000"); + checkSidcCodecEncoded(sidc, QLatin1String("10012500002713000000"), AdvancedSymbology::SymbologyType::APP6D, AdvancedSymbology::SidcCodec::SidcType::Complete); + + sidc = QLatin1String("M25C|CMPL|10012500002713000000"); + checkSidcCodecEncoded(sidc, QLatin1String("10012500002713000000"), AdvancedSymbology::SymbologyType::MIL2525C, AdvancedSymbology::SidcCodec::SidcType::Complete); + + sidc = QLatin1String("M25D|CMPL|10012500002713000000"); + checkSidcCodecEncoded(sidc, QLatin1String("10012500002713000000"), AdvancedSymbology::SymbologyType::MIL2525D, AdvancedSymbology::SidcCodec::SidcType::Complete); + } + + { + QString temp; + temp = AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6B, QLatin1String("25271300")); + QVERIFY2(temp.compare(QLatin1String("AP6B|BASE|25271300")) == 0, "encodeBaseSymbolCode"); + + temp = AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::APP6D, QLatin1String("25271300")); + QVERIFY2(temp.compare(QLatin1String("AP6D|BASE|25271300")) == 0, "encodeBaseSymbolCode"); + + temp = AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::MIL2525C, QLatin1String("25271300")); + QVERIFY2(temp.compare(QLatin1String("M25C|BASE|25271300")) == 0, "encodeBaseSymbolCode"); + + temp = AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(AdvancedSymbology::SymbologyType::MIL2525D, QLatin1String("25271300")); + QVERIFY2(temp.compare(QLatin1String("M25D|BASE|25271300")) == 0, "encodeBaseSymbolCode"); + + + temp = AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::MIL2525D, QLatin1String("10012500002713000000")); + QVERIFY2(temp.compare(QLatin1String("M25D|CMPL|10012500002713000000")) == 0, "encodeComplete"); + } + } + + // Fehlerfälle + { + QString sidc = QLatin1String("10012500002713000000"); + QVERIFY2(AdvancedSymbology::SidcCodec::decodeSymbologyType(sidc) == AdvancedSymbology::SymbologyType::None, "decodeSymbologyType"); + QVERIFY2(AdvancedSymbology::SidcCodec::decodeSidcType(sidc) == AdvancedSymbology::SidcCodec::SidcType::None, "decodeSidcType"); + QVERIFY2(AdvancedSymbology::SidcCodec::decodeRawSidc(sidc).compare(sidc) == 0, "decodeRawSidc"); + } + +} + + +void SitMapUnitTest::checkSIDCDefinitions1() +{ + if (m_d->m_symbologyType == AdvancedSymbology::SymbologyType::None) + return; + + AdvancedSymbology::SymbolDictionary &dict = AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + QSignalSpy spy(&dict, &AdvancedSymbology::SymbolDictionary::loaded); + if(dict.isLoaded() == false) + { + dict.load(); + if (spy.count() == 0) + spy.wait(10000); + } + QVERIFY2((dict.isLoaded() == true), "SymbolDictionary: isLoaded failed"); + + + // real work: + + bool success; + AdvancedSymbology::SidcDefinitions::StandardIdentity standardIdentity; + + QVERIFY2((int)AdvancedSymbology::SidcDefinitions::Identity::Pending == 0 , "Identity emumValue wrong"); + QVERIFY2((int)AdvancedSymbology::SidcDefinitions::Identity::Unknown == 1 , "Identity emumValue wrong"); + QVERIFY2((int)AdvancedSymbology::SidcDefinitions::Identity::AssumedFriend == 2 , "Identity emumValue wrong"); + QVERIFY2((int)AdvancedSymbology::SidcDefinitions::Identity::Friend == 3 , "Identity emumValue wrong"); + QVERIFY2((int)AdvancedSymbology::SidcDefinitions::Identity::Neutral == 4 , "Identity emumValue wrong"); + QVERIFY2((int)AdvancedSymbology::SidcDefinitions::Identity::SuspectJoker == 5 , "Identity emumValue wrong"); + QVERIFY2((int)AdvancedSymbology::SidcDefinitions::Identity::HostileFaker == 6 , "Identity emumValue wrong"); + + // to be continued... + + + const QVector &supportedSmTypes = { AdvancedSymbology::SymbologyType::MIL2525D, AdvancedSymbology::SymbologyType::APP6D}; + + if (supportedSmTypes.contains(m_d->m_symbologyType)) + { + QString baseSymbolCode = AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(m_d->m_symbologyType, "25270100"); + + AdvancedSymbology::SidcBuilder sidcBuilder(&dict); + success = sidcBuilder.initFromSymbologyTypeBaseSymbolCode(m_d->m_symbologyType, baseSymbolCode); + QVERIFY2(success == true , "initFromSymbologyTypeBaseSymbolCode failed"); + + const QString &sidc = sidcBuilder.toSidc(); + qCritical() << "sidc=" << sidc; + // 12345678901234567890 + // 25 270100 // baseSymbolCode (SymbolSet + Entity) + + const QString &rawSidc = AdvancedSymbology::SidcCodec::decodeRawSidc(sidc); + + QVERIFY2(rawSidc.compare(QLatin1String("10012500002701000000")) == 0 , "initFromMil2525DBaseSymbolCode failed"); + } + + + // + // test some arbitrary sidc (MIL2525D): + // + + + + if (supportedSmTypes.contains(m_d->m_symbologyType)) + { + // 1234567890 + QString sidcSTR = AdvancedSymbology::SidcCodec::encodeComplete(m_d->m_symbologyType, "10051000001213030000"); // MIL2525D: 01011000001213030000 -> LandUnits, Observer, Motorized + qDebug() << "----------- " << sidcSTR; + + AdvancedSymbology::SidcBuilder sidcBuilder(&dict); + success = sidcBuilder.initFromSymbologyType(m_d->m_symbologyType, sidcSTR); + QVERIFY2(success == true , "initFromSymbologyTypeBaseSymbolCode failed"); + + // First10 + standardIdentity = sidcBuilder.standardIdentity(); + + QVERIFY2(standardIdentity.context == AdvancedSymbology::SidcDefinitions::Context::Reality , "standardIdentity.context failed"); + QVERIFY2(standardIdentity.identity == AdvancedSymbology::SidcDefinitions::Identity::SuspectJoker , "standardIdentity.identity failed"); + + AdvancedSymbology::SidcDefinitions::SymbolSet symSet = sidcBuilder.symbolSet(); + QVERIFY2(symSet == AdvancedSymbology::SidcDefinitions::SymbolSet::LandUnit, "symbolSet failed"); + + AdvancedSymbology::SidcDefinitions::Status status = sidcBuilder.status(); + QVERIFY2(status == AdvancedSymbology::SidcDefinitions::Status::Present, "status failed"); + + QVERIFY2(sidcBuilder.headquarters() == false , "headquarters failed"); + QVERIFY2(sidcBuilder.taskForce() == false , "taskForce failed"); + QVERIFY2(sidcBuilder.feintDummy() == false , "feintDummy failed"); + + AdvancedSymbology::SidcDefinitions::AmplifierGroup amplGroup = sidcBuilder.amplifierGroup(); + QVERIFY2(amplGroup == AdvancedSymbology::SidcDefinitions::AmplifierGroup::Unknown , "amplifierGroup failed"); + + int amplSub = sidcBuilder.amplifier2ndDigit(); + QVERIFY2(amplSub == 0 , "amplifierSecondDigit failed"); + + // Second10 + + } + + if (supportedSmTypes.contains(m_d->m_symbologyType)) + { + // 1234567890 + QString sidcSTR = AdvancedSymbology::SidcCodec::encodeComplete(m_d->m_symbologyType, "10012547231213030000"); + qDebug() << "----------- " << sidcSTR; + + AdvancedSymbology::SidcBuilder sidcBuilder(&dict); + success = sidcBuilder.initFromSymbologyType(m_d->m_symbologyType, sidcSTR); + QVERIFY2(success == true , "initFromSymbologyType failed"); + + // First10 + standardIdentity = sidcBuilder.standardIdentity(); + + QVERIFY2(standardIdentity.context == AdvancedSymbology::SidcDefinitions::Context::Reality , "standardIdentity.context failed"); + QVERIFY2(standardIdentity.identity == AdvancedSymbology::SidcDefinitions::Identity::Unknown , "standardIdentity.identity failed"); + + AdvancedSymbology::SidcDefinitions::SymbolSet symSet = sidcBuilder.symbolSet(); + QVERIFY2(symSet == AdvancedSymbology::SidcDefinitions::SymbolSet::ControlMeasure, "symbolSet failed"); + + AdvancedSymbology::SidcDefinitions::Status status = sidcBuilder.status(); + QVERIFY2(status == AdvancedSymbology::SidcDefinitions::Status::Present_Destroyed, "status failed"); + + QVERIFY2(sidcBuilder.headquarters() == true , "headquarters failed"); + QVERIFY2(sidcBuilder.taskForce() == true , "taskForce failed"); + QVERIFY2(sidcBuilder.feintDummy() == true , "feintDummy failed"); + + AdvancedSymbology::SidcDefinitions::AmplifierGroup amplGroup = sidcBuilder.amplifierGroup(); + QVERIFY2(amplGroup == AdvancedSymbology::SidcDefinitions::AmplifierGroup::Echelon_DivisionAndAbove , "amplifierGroup failed"); + + int amplSub = sidcBuilder.amplifier2ndDigit(); + QVERIFY2(amplSub == 3 , "amplifierSecondDigit failed"); + + // Second10 + + } + +} + +void SitMapUnitTest::checkSIDCDefinitions2() +{ + if (m_d->m_symbologyType == AdvancedSymbology::SymbologyType::None) + return; + + checkSymbolDictionaryLoad(); + + AdvancedSymbology::SymbolDictionary &dict = + AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + AdvancedSymbology::SidcDefinitions sidcDefinitions(&dict); + + const QStringList &modifierKeys = sidcDefinitions.modifierKeys(); + QVERIFY2(modifierKeys.isEmpty() == false , "modifierKeys failed"); + + QVERIFY2(sidcDefinitions.modifierList(AdvancedSymbology::SidcDefinitions::SymbolSet::Activities, 1).isEmpty() == false , "modifierList failed"); +// QVERIFY2(sidcDefinitions.modifierList(AdvancedSymbology::SidcDefinitions::SymbolSet::Activities, 2).isEmpty() == false , "modifierList failed"); + + QVERIFY2(sidcDefinitions.modifierList(AdvancedSymbology::SidcDefinitions::SymbolSet::Air, 1).isEmpty() == false , "modifierList failed"); + QVERIFY2(sidcDefinitions.modifierList(AdvancedSymbology::SidcDefinitions::SymbolSet::Air, 2).isEmpty() == false , "modifierList failed"); + + QVERIFY2(sidcDefinitions.modifierList(AdvancedSymbology::SidcDefinitions::SymbolSet::AirMissle, 1).isEmpty() == false , "modifierList failed"); + QVERIFY2(sidcDefinitions.modifierList(AdvancedSymbology::SidcDefinitions::SymbolSet::AirMissle, 2).isEmpty() == false , "modifierList failed"); + + // to be cont. + +} + + +/* +void SitMapUnitTest::checkCreateSidc() +{ + + // + // MIL + // + { + AdvancedSymbology::SymbolDictionary &dict = + AdvancedSymbology::SymbolDictionary::dictionary(AdvancedSymbology::SymbologyType::MIL2525C); + + + { + QString sidc(QLatin1String("S*G*UCD---*****")); // ('taken' from typen@sys_db) + dict.createSidc(sidc, SymbolTypes::FRIEND, SymbolTypes::STATE_PRESENT); + QVERIFY2(dict.contains(sidc), "contains failed"); + QVERIFY2(sidc.length() == 10, "createSidc failed"); + QVERIFY2(sidc.compare(QLatin1String("SFGPUCD---")) == 0, "createSidc failed"); + } + { + QString sidc(QLatin1String("S*G*UCD---*****")); + dict.createSidc(sidc, SymbolTypes::ENEMY, SymbolTypes::STATE_PRESENT); + QVERIFY2(dict.contains(sidc), "contains failed"); + QVERIFY2(sidc.length() == 10, "createSidc failed"); + QVERIFY2(sidc.compare(QLatin1String("SHGPUCD---")) == 0, "createSidc failed"); + } + { + QString sidc(QLatin1String("S*G*UCD---*****")); + dict.createSidc(sidc, SymbolTypes::NEUTRAL, SymbolTypes::STATE_PRESENT); + QVERIFY2(dict.contains(sidc), "contains failed"); + QVERIFY2(sidc.length() == 10, "createSidc failed"); + QVERIFY2(sidc.compare(QLatin1String("SNGPUCD---")) == 0, "createSidc failed"); + } + { + QString sidc(QLatin1String("S*G*UCD---*****")); + dict.createSidc(sidc, SymbolTypes::UNKNOWN, SymbolTypes::STATE_PRESENT); + QVERIFY2(dict.contains(sidc), "contains failed"); + QVERIFY2(sidc.length() == 10, "createSidc failed"); + QVERIFY2(sidc.compare(QLatin1String("SUGPUCD---")) == 0, "createSidc failed"); + } + + + { + QString sidc(QLatin1String("S*G*UCD---*****")); + dict.createSidc(sidc, SymbolTypes::ENEMY, SymbolTypes::STATE_ANTICIPATED); + // QVERIFY2( dict.contains(sidc), "contains failed"); // ANTICIPATED -> 'A' is not supported by ESRI-Dictionary yet + QVERIFY2(sidc.length() == 10, "createSidc failed"); + QVERIFY2(sidc.compare(QLatin1String("SHGAUCD---")) == 0, "createSidc failed"); + } + { + QString sidc(QLatin1String("S*G*UCD---************")); + dict.createSidc(sidc, SymbolTypes::ENEMY, SymbolTypes::STATE_PRESENT); + QVERIFY2(dict.contains(sidc), "contains failed"); + QVERIFY2(sidc.length() == 10, "createSidc failed"); + QVERIFY2(sidc.compare(QLatin1String("SHGPUCD---")) == 0, "createSidc failed"); + } + + } + + // + // APP6 + // + + { + AdvancedSymbology::SymbolDictionary &dict = + AdvancedSymbology::SymbolDictionary::dictionary(AdvancedSymbology::SymbologyType::APP6B); + + + { + QString sidc(QLatin1String("S*G*UCD---*****")); + dict.createSidc(sidc, SymbolTypes::FRIEND, SymbolTypes::STATE_PRESENT); + QVERIFY2(dict.contains(sidc), "contains failed"); + QVERIFY2(sidc.length() == 10, "createSidc failed"); + QVERIFY2(sidc.compare(QLatin1String("SFGPUCD---")) == 0, "createSidc failed"); + } + { + QString sidc(QLatin1String("S*G*UCD---*****")); + dict.createSidc(sidc, SymbolTypes::ENEMY, SymbolTypes::STATE_PRESENT); + QVERIFY2(dict.contains(sidc), "contains failed"); + QVERIFY2(sidc.length() == 10, "createSidc failed"); + QVERIFY2(sidc.compare(QLatin1String("SHGPUCD---")) == 0, "createSidc failed"); + } + { + QString sidc(QLatin1String("S*G*UCD---*****")); + dict.createSidc(sidc, SymbolTypes::NEUTRAL, SymbolTypes::STATE_PRESENT); + QVERIFY2(dict.contains(sidc), "contains failed"); + QVERIFY2(sidc.length() == 10, "createSidc failed"); + QVERIFY2(sidc.compare(QLatin1String("SNGPUCD---")) == 0, "createSidc failed"); + } + { + QString sidc(QLatin1String("S*G*UCD---*****")); + dict.createSidc(sidc, SymbolTypes::UNKNOWN, SymbolTypes::STATE_PRESENT); + QVERIFY2(dict.contains(sidc), "contains failed"); + QVERIFY2(sidc.length() == 10, "createSidc failed"); + QVERIFY2(sidc.compare(QLatin1String("SUGPUCD---")) == 0, "createSidc failed"); + } + + + { + QString sidc(QLatin1String("S*G*UCD---*****")); + dict.createSidc(sidc, SymbolTypes::ENEMY, SymbolTypes::STATE_ANTICIPATED); + // QVERIFY2( dict.contains(sidc), "contains failed"); // ANTICIPATED -> 'A' is not supported by ESRI-Dictionary yet + QVERIFY2(sidc.length() == 10, "createSidc failed"); + QVERIFY2(sidc.compare(QLatin1String("SHGAUCD---")) == 0, "createSidc failed"); + } + { + QString sidc(QLatin1String("S*G*UCD---************")); + dict.createSidc(sidc, SymbolTypes::ENEMY, SymbolTypes::STATE_PRESENT); + QVERIFY2(dict.contains(sidc), "contains failed"); + QVERIFY2(sidc.length() == 10, "createSidc failed"); + QVERIFY2(sidc.compare(QLatin1String("SHGPUCD---")) == 0, "createSidc failed"); + } + + } + +} +*/ + +/* +void SitMapUnitTest::checkSymbolProperties() +{ + AdvancedSymbology::SymbolDictionary &dict = + AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + if (dict.symbologyType() == AdvancedSymbology::SymbologyType::None) + return; + + + // 1 + { + QString sidc(QLatin1String("SUGPUSAJ--")); // SUGPUSAJ-- should exist as MIL and APP :-) + QVERIFY2(dict.contains(sidc), "contains failed"); + + const AdvancedSymbology::SymbolProperties &props = dict.symbolProperties(sidc); + QVERIFY2(props.sidc().compare(QLatin1String("SUGPUSAJ--")) == 0, "symbolProperties failed"); + QVERIFY2(props.geometryType() == AdvancedSymbology::GeometryType::Point, "geometryType failed"); + int minPoints = props.minNumberOfPoints(); + QVERIFY2(minPoints == 1, "minNumberOfPoints failed"); + } + + // 2 + { + QString sidc; + switch (m_d->m_symbologyType) + { + case AdvancedSymbology::SymbologyType::MIL2525C: + sidc = QLatin1String("GUGPOLAGM-"); + break; + case AdvancedSymbology::SymbologyType::APP6B: + sidc = QLatin1String("GHTPGC----"); + break; + case AdvancedSymbology::SymbologyType::None: + // empty + break; + } + + if (! sidc.isEmpty()) + { + QVERIFY2(dict.contains(sidc), "contains failed"); + + const AdvancedSymbology::SymbolProperties &props = dict.symbolProperties(sidc); + QVERIFY2(props.sidc().compare(sidc) == 0, "symbolProperties failed"); + QVERIFY2(props.geometryType() == AdvancedSymbology::GeometryType::Polyline, "geometryType failed"); + int minPoints = props.minNumberOfPoints(); + QVERIFY2(minPoints == 2, "minNumberOfPoints failed"); + } + } + + // 3 + if (m_d->m_symbologyType == AdvancedSymbology::SymbologyType::APP6B) + { + // Check if esriBug is fixed (by our own or by esri) + // Bug 1: Tank Light Recovery: + // APP6: correct: "S*G*EVATW-" + // (MIL: correct: "S*G*EVATLR" + // APP6: 10.2.6: "S*G*EVATLR" (e.g. 0695) + + QString sidc(QLatin1String("SUGPEVATW-")); // SUGPEVATW- + QVERIFY2(dict.contains(sidc), "contains failed"); + + const AdvancedSymbology::SymbolProperties &props = dict.symbolProperties(sidc); + QVERIFY2(props.sidc().compare(sidc) == 0, "symbolProperties failed"); + QVERIFY2(props.geometryType() == AdvancedSymbology::GeometryType::Point, "geometryType failed"); + int minPoints = props.minNumberOfPoints(); + QVERIFY2(minPoints == 1, "minNumberOfPoints failed"); + + // check keywords: + QStringList result = props.keywords().filter(sidc, Qt::CaseInsensitive); + QVERIFY2(result.size() == 1, "keywords failed"); + } + +} +*/ + +QImage createDummyImage() +{ + int widthPixel = 100; + QImage image = QImage(widthPixel, widthPixel, QImage::Format_ARGB32); + image.fill(Qt::transparent); + + QPainter p; + QPen pen(Qt::red); + pen.setWidth(5); + + p.begin(&image); + p.setPen(pen); + p.drawLine(QPointF(0, 0), QPointF(100, 100)); + p.end(); + +// bool success = image.save(QLatin1String("/tmp/dummy.png")); +// qWarning() << " image.save = " << success; + return image; +} + +/* +bool checkAllCount(AdvancedSymbology::SymbolDictionary &dict) +{ + SituationMap::LOGGER_T myLogger = SituationMap::getLogger("SitMapUnitTest"); + + if (dict.symbologyType() == AdvancedSymbology::SymbologyType::None) + { + // None: + if (dict.count() > 100) // 100 -> arbitrary + return false; + } + else + { + // App, Mil: + if (dict.count() <= 0) + return false; + } + + // verify size of different lists: + int count = dict.count(); + int sizeFindSymbolsAll = dict.findSymbols().size(); + QString sidcPattern; // empty + int sizeFindSymbolsSidcPattern = dict.findSymbols(sidcPattern).size(); + int sizeSidcList = dict.sidcList().size(); + + INFO_LOG(myLogger) << LOGFUNCTION + << " count = " << count + << " sizeFindSymbolsAll = " << sizeFindSymbolsAll + << " sizeFindSymbolsSidcPattern = " << sizeFindSymbolsSidcPattern + << " sizeSidcList = " << sizeSidcList; + + bool ok = (count == sizeFindSymbolsAll) + && (count == sizeFindSymbolsSidcPattern) + && (count == sizeSidcList); + if (! ok) + { + ERROR_LOG(myLogger) << LOGFUNCTION + << " count = " << count + << " sizeFindSymbolsAll = " << sizeFindSymbolsAll + << " sizeFindSymbolsSidcPattern = " << sizeFindSymbolsSidcPattern + << " sizeSidcList = " << sizeSidcList; + } + return ok; +} +*/ + +/* +bool addOneSymbol(AdvancedSymbology::SymbolDictionary &dict) +{ + SituationMap::LOGGER_T myLogger = SituationMap::getLogger("SitMapUnitTest"); + Q_UNUSED(myLogger) + + bool success = false; + + QImage image = createDummyImage(); + + const QString sidc = QLatin1String("Nt-sidc"); + const QString name = QLatin1String("NT-name"); + + QStringList keywords; + keywords << QLatin1String("NT-key1"); + keywords << QLatin1String("NT-keyDuplicate") << QLatin1String("NT-keyDuplicate"); + + QHash values; + values[QLatin1String("Category")] = QLatin1String("Other"); + values[QLatin1String("SymbolID")] = sidc; + values[QLatin1String("GeometryType")] = QLatin1String("Point"); + values[QLatin1String("GeometryConversionType")] = QLatin1String("GeometryConversionType-Unknown"); + values[QLatin1String("StyleFile")] = QLatin1String("StyleFile-Unknown"); + + AdvancedSymbology::SymbolProperties props(name, keywords, values); + + success = dict.addSymbol(sidc, props, image); + return success; +} +*/ + +void SitMapUnitTest::checkSymbolDictionaryLoad() +{ + AdvancedSymbology::cleanupSymbolDictionaryInternal(); + + AdvancedSymbology::SymbolDictionary &dict = + AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + // SymbologyType::None -> no esriDict -> immediately loaded: + // ! SymbologyType::None -> esriDict -> load... + + QSignalSpy spy(&dict, &AdvancedSymbology::SymbolDictionary::loaded); + dict.load(); + QTRY_VERIFY2(dict.isLoaded(), "SymbolDictionary: isLoaded failed"); + + if (m_d->m_symbologyType == AdvancedSymbology::SymbologyType::None) + { + QTest::qWait(100); + QVERIFY2(spy.count() == 0, "SymbolDictionary: signalCount failed, layerOrderChanged"); + } + else + { + QTRY_VERIFY2(spy.count() == 1, "SymbolDictionary: signalCount failed, layerOrderChanged"); + } + + // get cached Dictionary: + qWarning() << "get cached Dictionary"; + { + // -> immediately loaded + AdvancedSymbology::SymbolDictionary &dict = + AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + QVERIFY2((dict.isLoaded() == true), "SymbolDictionary: isLoaded failed"); + } + + + +/* + int count = dict.count(); + + bool dictChanged = false; + + // lambda + connect(&dict, &AdvancedSymbology::SymbolDictionary::changed, [&]() + { + dictChanged = true; + }); + + + // + // Initial Check + // + bool countOk = checkAllCount(dict); + QVERIFY2((countOk == true), "at least one count failed"); + + // + // add new symbol an check: + // + { + bool addSym = addOneSymbol(dict); + QVERIFY2((addSym == true), "addSymbol failed"); + QVERIFY2((dictChanged == true), "dictChanged signal not emitted"); + + countOk = checkAllCount(dict); + QVERIFY2((countOk == true), "at least one count failed"); + QVERIFY2(dict.count() == (count + 1), "count failed"); + } +*/ +} + +void SitMapUnitTest::checkSymbolDictionaryLoadSymbolXml() +{ + AdvancedSymbology::cleanupSymbolDictionaryInternal(); + + AdvancedSymbology::SymbolDictionary &dict = AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + bool ok; + AdvancedSymbology::SymbolProperties symProps; + + ok = dict.insertXmlTestSymbol(symProps); + QVERIFY2(ok, "insert xml test symbol failed."); + + ok = dict.insertXmlComplexTestSymbol(symProps); + QVERIFY2(ok, "insert xml test complex symbol failed."); + + QVERIFY2( (dict.xmlSymbolCount() == 2), "count of xml symbols has wrong value - should be 2" ); + + const QString &sidc = AdvancedSymbology::SidcCodec::encodeCivilXml(QString::number(4711)); + dict.removeXmlSymbol(sidc); + dict.removeXmlSymbol(4711); + + QVERIFY2( (dict.xmlSymbolCount() == 1), "count of xml symbols has wrong value after removeXmlSymbol() - should be 1" ); + + dict.clearXmlSymbols(); + + QVERIFY2( (dict.xmlSymbolCount() == 0), "count of xml symbols is not zero after clearXmlSymbols()" ); + +} // void SitMapUnitTest::checkSymbolDictionaryLoadSymbolXml() + + +void SitMapUnitTest::checkSymbolDictionaryConsistency() +{ + if (m_d->m_symbologyType == AdvancedSymbology::SymbologyType::None) + return; + + checkSymbolDictionaryLoad(); + auto &dict = AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + // get access to esriDict: + AdvancedSymbology::SymbolDictionaryImpl &dictImpl = static_cast(dict); + Esri::ArcGISRuntime::DictionarySymbolStyle *esriDict = const_cast(dictImpl.getEsriSymbolDictionary()); + + QHash baseSymbolCodeToSymbolPropertyTable; + + + // Use QFuture-based async API + Esri::ArcGISRuntime::SymbolStyleSearchParameters sssp; + QFuture future = esriDict->searchSymbolsAsync(sssp); + + // Wait for completion + QFutureWatcher watcher; + watcher.setFuture(future); + watcher.waitForFinished(); + + Esri::ArcGISRuntime::SymbolStyleSearchResultListModel *searchResults = future.result(); + + INFO_LOG(m_logger) << LOGFUNCTION << " proceessing..."; + + // + // process all results and store them in member: + // + const QList &resultList = + searchResults->searchResults(); + + int symbolClass; + for (const Esri::ArcGISRuntime::SymbolStyleSearchResult &resultItem : resultList) + { + // INFO_LOG(m_logger) << LOGFUNCTION << " resultItem.key ='" << resultItem.key() << "'"; + // INFO_LOG(m_logger) << LOGFUNCTION << " resultItem.name ='" << resultItem.name() << "'"; + // INFO_LOG(m_logger) << LOGFUNCTION << " resultItem.category ='" << resultItem.category() << "'"; + // INFO_LOG(m_logger) << LOGFUNCTION << " resultItem.symbolClass ='" << resultItem.symbolClass() << "'"; + // INFO_LOG(m_logger) << LOGFUNCTION << " resultItem.tags ='" << resultItem.tags().join(QLatin1String(";")) << "'"; + + const QString &originalKey = resultItem.key(); + + // to reduce empty keys: + symbolClass = resultItem.symbolClass().toInt(); + if ( (symbolClass == 6) || // symbolClass ( 6) == TextSymbol + (symbolClass == 10) // symbolClass (10) == MaplexLabelPlacement + ) + { + DEBUG_LOG(m_logger) << LOGFUNCTION + << " continue:: symbolClass does not match " + << " originalKey='" << originalKey << "'" + << " name='" << resultItem.name() << "'"; + continue; + } + + if (originalKey.isEmpty()) + { + DEBUG_LOG(m_logger) << LOGFUNCTION + << " continue:: empty key, " + << " name='" << resultItem.name() << "'"; + continue; + } + + // + // size (25270100 || 25270100_4) + // + const int keySize = originalKey.size(); + if ( ! ((keySize == 8) || (keySize == 10)) ) + { + DEBUG_LOG(m_logger) << LOGFUNCTION + << " continue:: keySize does not match " + << " originalKey='" << originalKey << "'" + << " name='" << resultItem.name() << "'"; + continue; + } + + // + // isFrame (key like '0_110_1c') + // + if ( QStringRef(&originalKey, 1, 1).compare(QLatin1String("_"))==0 ) + { + DEBUG_LOG(m_logger) << LOGFUNCTION + << " continue:: isFrame " + << " originalKey='" << originalKey << "'" + << " name='" << resultItem.name() << "'"; + continue; + } + + // + // isCharlie (key like '46120203_C') + // + if ( (keySize == 10) && (originalKey.rightRef(2).compare(QLatin1String("_C"))==0) ) + { + DEBUG_LOG(m_logger) << LOGFUNCTION + << " continue:: isCharlie" + << " originalKey='" << originalKey << "'" + << " name='" << resultItem.name() << "'"; + continue; + } + + const QString &key = originalKey; + + // + // just digits: + // + bool ok = false; + const QStringRef &key8 = originalKey.leftRef(8); + key8.toInt(&ok); + if (! ok) + { + DEBUG_LOG(m_logger) << LOGFUNCTION + << " continue:: chars" + << " originalKey='" << originalKey << "'" + << " name='" << resultItem.name() << "'"; + continue; + } + + // + // special (46110304_X) + // + if ( (keySize == 10) && (originalKey.rightRef(2).compare(QLatin1String("_X"))==0) ) + { + DEBUG_LOG(m_logger) << LOGFUNCTION + << " continue:: is _X" + << " originalKey='" << originalKey << "'" + << " name='" << resultItem.name() << "'"; + continue; + } + + + AdvancedSymbology::SymbolProperties symProps; + symProps.setKey (key); + symProps.setSidc (AdvancedSymbology::SidcCodec::encodeBaseSymbolCode(m_d->m_symbologyType,key8)); + symProps.setName (resultItem.name()); + symProps.setCategory (resultItem.category()); + symProps.setSymbolClass(resultItem.symbolClass()); + symProps.setTags (resultItem.tags()); + + baseSymbolCodeToSymbolPropertyTable[originalKey] = symProps; + } // Q_FOREACH + + FATAL_LOG(m_logger) << LOGFUNCTION << " baseSymbolCodeToSymbolPropertyTable.size=" << baseSymbolCodeToSymbolPropertyTable.size(); + + + QStringList noKey8Entries; + QStringList noKey8EntriesOfUnknown; + QStringList propsDiffer; + + for (const AdvancedSymbology::SymbolProperties &symPropItem : std::as_const(baseSymbolCodeToSymbolPropertyTable)) + { + const QString &curKey = symPropItem.key(); +// INFO_LOG(m_logger) << LOGFUNCTION << " curKey ='" << curKey << "'"; + const QString &key8 = curKey.left(8); + + // check 1a: + // are there entries with postfix (_1, _2, _3) but not without?? + + bool noKey8EntriesFlag = false; + // 25270100_1 but not 25270100 ? + // (e.g. '10120100_0' ; '10120100_1' ; '10120100_2' ; '10120100_3' ; but no '10120100' + // 10120100 = Movement And Maneuver ; Air Assult with Organic Lift - FullFrame + // These are the FULL_FRAME symbols. (except "Maneuver Lines : Line of Contact" = 25140200_6) + // + // Exception: "Maneuver Lines : Line of Contact" = 25140200_6, _4, _3, _1) + + if (curKey.size() == 10) + { + if (! baseSymbolCodeToSymbolPropertyTable.contains(key8)) + { + noKey8Entries.append(curKey); + noKey8EntriesFlag = true; + } + } // if (curKey.size() == 10) + + // check 1b + if (noKey8EntriesFlag && + symPropItem.name().contains(QLatin1String("Unknown"))) + { + noKey8EntriesOfUnknown.append(curKey); + } + + + + // check 2: + AdvancedSymbology::SymbolProperties curSymProps = baseSymbolCodeToSymbolPropertyTable[curKey]; + { + QStringList sl = curSymProps.name().split(QLatin1Char(':')); + sl.removeOne(QLatin1String("Friend")); + sl.removeOne(QLatin1String("Friendly")); + sl.removeOne(QLatin1String("Hostile")); + sl.removeOne(QLatin1String("Neutral")); + sl.removeOne(QLatin1String("Unknown")); + curSymProps.setName(sl.join(QLatin1Char(':'))); + } + + if (baseSymbolCodeToSymbolPropertyTable.contains(key8)) + { + AdvancedSymbology::SymbolProperties symPropsKey8 = baseSymbolCodeToSymbolPropertyTable[key8]; + QStringList sl = symPropsKey8.name().split(QLatin1Char(':')); + sl.removeOne(QLatin1String("Friend")); + sl.removeOne(QLatin1String("Friendly")); + sl.removeOne(QLatin1String("Hostile")); + sl.removeOne(QLatin1String("Neutral")); + sl.removeOne(QLatin1String("Unknown")); + symPropsKey8.setName(sl.join(QLatin1Char(':'))); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " key8=" << key8 << " curSymProps.name = " << curSymProps.name(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " key8=" << key8 << " symPropsKey8.name = " << symPropsKey8.name(); + + + bool ok = + // (curSymProps.name() == symPropsKey8.name()) && +// (curSymProps.category() == symPropsKey8.category()) && + // // (curSymProps.symbolClass() == symPropsKey8.symbolClass()) && // do not compare SymbolClass + (curSymProps.tags() == symPropsKey8.tags()); + // (curSymProps.geometryType() == symPropsKey8.geometryType()); + + if (! ok) + { + propsDiffer.append(curKey); + } + } + } // Q_FOREACH + + DEBUG_LOG(m_logger) << LOGFUNCTION << " noKey8Entries.size = " << noKey8Entries.size(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " noKey8Entries = " << noKey8Entries.join(QLatin1String(" ; ")); + DEBUG_LOG(m_logger) << LOGFUNCTION << " noKey8EntriesOfUnknown.size = " << noKey8EntriesOfUnknown.size(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " noKey8EntriesOfUnknown = " << noKey8EntriesOfUnknown.join(QLatin1String(" ; ")); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " propsDiffer.size = " << propsDiffer.size(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " propsDiffer = " << propsDiffer.join(QLatin1String(" ; ")); + + // dump differ: + for (const auto &curKey : propsDiffer) + { + AdvancedSymbology::SymbolProperties curSymProps = baseSymbolCodeToSymbolPropertyTable[curKey]; + + const QString &key8 = curKey.left(8); + AdvancedSymbology::SymbolProperties symPropsKey8 = baseSymbolCodeToSymbolPropertyTable[key8]; + + DEBUG_LOG(m_logger) << LOGFUNCTION << " curSymProps " << curSymProps.tags().join(QLatin1String(" @ ")); + DEBUG_LOG(m_logger) << LOGFUNCTION << " symPropsKey8 " << symPropsKey8.tags().join(QLatin1String(" @ ")); + } + + +} + +void SitMapUnitTest::checkSymbolDictionaryFetchSymbol() +{ + checkSymbolDictionaryLoad(); + auto &dict = AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + if (m_d->m_symbologyType == AdvancedSymbology::SymbologyType::MIL2525D) + { + + { + const QString sidc = AdvancedSymbology::SidcCodec::encodeComplete(AdvancedSymbology::SymbologyType::MIL2525D, "10042500002701000000"); // baseSymbolCode = 25 270100 + + AdvancedSymbology::SymbolResultWatcher symbolResultWatcher; + QSignalSpy spy(&symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady); + + QVERIFY2((symbolResultWatcher.resultIsValid() == false), "SymbolDictionary: resultIsValid failed"); + + symbolResultWatcher.setSymbolResult(dict.fetchSymbol(sidc)); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + QVERIFY2((symbolResultWatcher.symbolResult().isNull() == false), "SymbolDictionary: symbolResult failed"); + + QTRY_VERIFY2((spy.count() == 1), "SymbolDictionary: resultReady-signal failed"); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + + const QUuid &taskId1 = symbolResultWatcher.symbolResult()->taskId(); + QVERIFY2((taskId1.isNull() == false), "SymbolDictionary: taskId failed"); + + // inspect result: + QVERIFY2((symbolResultWatcher.symbolResult()->geometryType() == AdvancedSymbology::GeometryType::Polygon), "SymbolDictionary: geometryType failed"); + QVERIFY2((symbolResultWatcher.symbolResult()->symbolImage().image().isNull() == false), "SymbolDictionary: symbolImage failed"); + } + + // find different: (PointSymbol) + { + const QString sidc = AdvancedSymbology::SidcCodec::encodeComplete( AdvancedSymbology::SymbologyType::MIL2525D,"10031500001110000000"); // baseSymbolCode = 15 111000 Missle Launcher + + AdvancedSymbology::SymbolResultWatcher symbolResultWatcher; + QSignalSpy spy(&symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady); + + symbolResultWatcher.setSymbolResult(dict.fetchSymbol(sidc)); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + QVERIFY2((symbolResultWatcher.symbolResult().isNull() == false), "SymbolDictionary: symbolResult failed"); + + QTRY_VERIFY2((spy.count() == 1), "SymbolDictionary: resultReady-signal failed"); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + + const QUuid &taskId2 = symbolResultWatcher.symbolResult()->taskId(); + QVERIFY2((taskId2.isNull() == false), "SymbolDictionary: taskId failed"); + + QVERIFY2((symbolResultWatcher.symbolResult()->geometryType() == AdvancedSymbology::GeometryType::Point), "SymbolDictionary: geometryType failed"); + QVERIFY2((symbolResultWatcher.symbolResult()->symbolImage().image().isNull() == false), "SymbolDictionary: symbolImage failed"); + } + + } +} + +void SitMapUnitTest::checkSymbolDictionaryFetchSymbol2() +{ + checkSymbolDictionaryLoad(); + auto &dict = AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + if (m_d->m_symbologyType != AdvancedSymbology::SymbologyType::MIL2525D) + { + return; + } + + // find some PointSymbols: + int counter = 0; + + const QStringList sidcs = AdvancedSymbology::listOfPointSidcs_Internal(); + + for (const auto &sidc : sidcs) + { + AdvancedSymbology::SymbolResultWatcher symbolResultWatcher; + QSignalSpy spy(&symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady); + + symbolResultWatcher.setSymbolResult(dict.fetchSymbol(sidc)); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + QVERIFY2((symbolResultWatcher.symbolResult().isNull() == false), "SymbolDictionary: symbolResult failed"); + + QTRY_VERIFY2((spy.count() == 1), "SymbolDictionary: resultReady-signal failed"); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + + const QUuid &taskId2 = symbolResultWatcher.symbolResult()->taskId(); + QVERIFY2((taskId2.isNull() == false), "SymbolDictionary: taskId failed"); + + QVERIFY2((symbolResultWatcher.symbolResult()->geometryType() == AdvancedSymbology::GeometryType::Point), "SymbolDictionary: geometryType failed"); + QVERIFY2((symbolResultWatcher.symbolResult()->symbolImage().image().isNull() == false), "SymbolDictionary: symbolImage failed"); + ++counter; + } + + QVERIFY2((counter == sidcs.size()), "SymbolDictionary: something failed"); + qWarning() << " counter=" << counter; +} + +void SitMapUnitTest::checkSymbolDictionaryFetchSymbolAll() +{ + checkSymbolDictionaryLoad(); + auto &dict = AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + // add ExtraSymbols: + const QString extraSymbolDir(QLatin1String(":/advancedSymbology/CivilSymbols")); + dict.addEtsSymbols(extraSymbolDir); + + + // fetch all Extra symbols: + int counter = 0; + + const QStringList extraSymbolSidcs = dict.etsSymbols(); + qCritical() << " extraSymbolSidcs.size = " << extraSymbolSidcs.size(); + + for (const auto &sidc : extraSymbolSidcs) + { + AdvancedSymbology::SymbolResultWatcher *symbolResultWatcher = new AdvancedSymbology::SymbolResultWatcher(this); + connect(symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady, this, [sidc, &counter, symbolResultWatcher]() + { + // lambda: +// qCritical() << " sidc=" << sidc; + QVERIFY2((symbolResultWatcher->resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + QVERIFY2((symbolResultWatcher->symbolResult().isNull() == false), "SymbolDictionary: symbolResult failed"); + + // Note: TaskId is null (esri not needed) + QSharedPointer symResult = symbolResultWatcher->symbolResult(); + + QVERIFY2((symResult), "SymbolDictionary: symResult failed"); + QVERIFY2((symResult->symbolImage().image().isNull() == false), "SymbolDictionary: symbolImage failed"); + QVERIFY2((symResult->symbolImage().image().size().isEmpty() == false), "SymbolDictionary: symbolImage failed"); + ++counter; + }); + + if (1) { + symbolResultWatcher->setSymbolResult(dict.fetchSymbol(sidc)); + } + + if (0) { + symbolResultWatcher->setSymbolResult(dict.findPreview(sidc)); + } + + if (0) { + AdvancedSymbology::SymbolDataToRender symDataToRender; + symDataToRender.sidc = sidc; + symbolResultWatcher->setSymbolResult(dict.findImage(symDataToRender)); + } + } + + QTRY_VERIFY2_WITH_TIMEOUT((counter == extraSymbolSidcs.size()), "SymbolDictionary: something failed", 20000); + qWarning() << " counter=" << counter; +} + + + +void SitMapUnitTest::checkSymbolDictionaryExtraDirFetchSymbol() +{ + checkSymbolDictionaryLoad(); + auto &dict = AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + const QString extraSymbolDir(QLatin1String(":/advancedSymbology/CivilSymbols")); + + int addedFiles = dict.addEtsSymbols(extraSymbolDir); + int extraSymbolCount = dict.etsSymbolCount(); + + qWarning() << "addedFiles=" << addedFiles; + qWarning() << "extraSymbolCount=" << extraSymbolCount; + + QVERIFY2((addedFiles > 500), "SymbolDictionary: addExtraSymbols failed"); + QVERIFY2((addedFiles == extraSymbolCount), "SymbolDictionary: extraSymbolCount failed"); + + // find: + { + const QString sidc( QLatin1String("DOES_NOT_EXISTS") ); + + AdvancedSymbology::SymbolResultWatcher symbolResultWatcher; + QSignalSpy spy(&symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady); + + symbolResultWatcher.setSymbolResult(dict.fetchSymbol(sidc)); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + + QTRY_VERIFY2((spy.count() == 1), "SymbolDictionary: resultReady-signal failed"); + + // Here is a notFoundSymbolResult: ('?') returned. + // Because it´s not really correct, the symbolImage is marked as 'not valid': + QVERIFY2((symbolResultWatcher.symbolResult()->symbolImage().isValid() == false), "SymbolDictionary: symbolImage should be invalid (notFoundSymbolResult: '?')"); + } + + // find: + { + const QString sidc = AdvancedSymbology::SidcCodec::encodeCivilEts("FzSanKrad"); // FzSanKrad.gif + + AdvancedSymbology::SymbolResultWatcher symbolResultWatcher; + QSignalSpy spy(&symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady); + + symbolResultWatcher.setSymbolResult(dict.fetchSymbol(sidc)); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + QVERIFY2((symbolResultWatcher.symbolResult().isNull() == false), "SymbolDictionary: symbolResult failed"); + + QTRY_VERIFY2((spy.count() == 1), "SymbolDictionary: resultReady-signal failed"); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + + // inspect result: + QVERIFY2((symbolResultWatcher.symbolResult()->geometryType() == AdvancedSymbology::GeometryType::Point), "SymbolDictionary: geometryType failed"); + QVERIFY2((symbolResultWatcher.symbolResult()->symbolImage().image().isNull() == false), "SymbolDictionary: symbolImage failed"); + } +} + + +void SitMapUnitTest::checkSymbolDictionaryFetchSymbolStress() +{ + return; // just for manual usage + + if (m_d->m_symbologyType == AdvancedSymbology::SymbologyType::None) + return; + + checkSymbolDictionaryLoad(); + auto &dict = AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + QStringList sidcs = AdvancedSymbology::listOfPointSidcs_Internal(); + QString sidc = sidcs.first(); + +// AdvancedSymbology::SymbolResultWatcher *symbolResultWatcher = +// new AdvancedSymbology::SymbolResultWatcher(this); + + int counter = 0; + + QTimer *timer = new QTimer(this); + timer->setInterval(1); + connect(timer, &QTimer::timeout, this, [timer, &dict, sidc, &counter]() + { + if ((++counter%100) == 0) + qCritical() << " timeout... counter = " << counter; + + QSharedPointer result = dict.fetchSymbol(sidc); + // symbolResultWatcher->setSymbolResult(dict.fetchSymbol(sidc)); + + if (counter == 10000) + timer->stop(); + }); + + timer->start(); + + QTRY_VERIFY_WITH_TIMEOUT(!timer->isActive(), 60 * 1000); +} + +void SitMapUnitTest::checkSymbolDictionaryBulkRenderer() +{ + if (m_d->m_symbologyType == AdvancedSymbology::SymbologyType::None) + return; + + checkSymbolDictionaryLoad(); + + QStringList pointSidcs = AdvancedSymbology::listOfPointSidcs_Internal(); + QStringList lineSidcs = AdvancedSymbology::listOfLineSidcs_Internal(); + + + { + AdvancedSymbology::BulkRendererFindBaseSymbol bulkRenderer(m_d->m_symbologyType); + + QSignalSpy spy(&bulkRenderer, &AdvancedSymbology::BulkRendererFindBaseSymbol::resultsReady); + + bulkRenderer.addRequest(pointSidcs.first()); + bulkRenderer.addRequest(lineSidcs.first()); + bulkRenderer.addRequest(lineSidcs.first()); + bulkRenderer.addRequest(lineSidcs.first()); + bulkRenderer.addRequest(lineSidcs.first()); + bulkRenderer.addRequest(lineSidcs.first()); + bulkRenderer.addRequest(lineSidcs.first()); + bulkRenderer.addRequest(lineSidcs.first()); + bulkRenderer.addRequest(lineSidcs.first()); + bulkRenderer.addRequest(lineSidcs.first()); + bulkRenderer.addRequest(lineSidcs.first()); + + bulkRenderer.run(); // <--- run + + QTRY_VERIFY2((spy.count() == 1), "BulkRenderer: resultsReady-signal failed"); + + int successfullCnt = 0; + AdvancedSymbology::BulkRendererFindBaseSymbol::RequestResultVector rrVec = bulkRenderer.results(); + for (const AdvancedSymbology::BulkRendererFindBaseSymbol::RequestResult &rr : std::as_const(rrVec)) + { + if (rr.second + && rr.second->symbolResult() + && rr.second->symbolResult()->isResultReady()) + { + const QImage img = rr.second->symbolResult()->symbolImage().image(); + if (img.width() > 10) + ++successfullCnt; + // const QString sidc = rr.first; qCritical() << " sidc= '" << sidc << "'" << " img.size(): " << img.size(); + // img.save(QStringLiteral("/tmp/img.png")); + } + } // for + + QVERIFY2((successfullCnt == rrVec.size()), "BulkRenderer: size comparision failed"); + + bulkRenderer.reset(); + } +} + +void fetchSymbolGeoType(const AdvancedSymbology::SymbolDictionary &dict, + const QString &sidc, + AdvancedSymbology::GeometryType geoType) +{ + AdvancedSymbology::SymbolResultWatcher symbolResultWatcher; + QSignalSpy spy(&symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady); + + QVERIFY2((symbolResultWatcher.resultIsValid() == false), "SymbolDictionary: resultIsValid failed"); + + symbolResultWatcher.setSymbolResult(dict.fetchSymbol(sidc)); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + QVERIFY2((symbolResultWatcher.symbolResult().isNull() == false), "SymbolDictionary: symbolResult failed"); + + QTRY_VERIFY2((spy.count() == 1), "SymbolDictionary: resultReady-signal failed"); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + + + + // inspect result: + QSharedPointer symResult = symbolResultWatcher.symbolResult(); + QVERIFY2((symResult.isNull() == false), "SymbolDictionary: isNull failed"); + + QVERIFY2((symResult->symbolImage().image().isNull() == false), "SymbolDictionary: symbolImage failed"); + + if (symResult->geometryType() != geoType) + { + qWarning() << " sidc=" << sidc << "'" << ", geometryType, failed, geoType="<< (int)symResult->geometryType(); + } + + QVERIFY2((symResult->geometryType() == geoType), "SymbolDictionary: geometryType failed"); +} + + +void SitMapUnitTest::checkSymbolDictionaryFindImage() +{ + AdvancedSymbology::SymbolDictionary &dict = + AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + // SymbologyType::None -> no esriDict -> immediately loaded: + // ! SymbologyType::None -> esriDict -> load... + + if (!dict.isLoaded()) + { + dict.load(); + QTRY_VERIFY2((dict.isLoaded() == true), "SymbolDictionary: isLoaded failed"); + } + + if (m_d->m_symbologyType == AdvancedSymbology::SymbologyType::MIL2525D) + { + QStringList sidcs = AdvancedSymbology::listOfPointSidcs_Internal(); + + int counter = 0; + + // + // SinglePoint: + // + for (const QString &sidc : std::as_const(sidcs)) + { + SituationMap::SymbolData symData; + AdvancedSymbology::SymbolDataToRender symDataToRender = symData.getDefaultRenderData(); + symDataToRender.sidc = sidc; + + AdvancedSymbology::SymbolResultWatcher symbolResultWatcher; + QSignalSpy spy(&symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady); + + symbolResultWatcher.setSymbolResult(dict.findImage(symDataToRender)); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + QVERIFY2((symbolResultWatcher.symbolResult().isNull() == false), "SymbolDictionary: symbolResult failed"); + + QTRY_VERIFY2((spy.count() == 1), "SymbolDictionary: resultReady-signal failed"); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + + const QUuid &taskId2 = symbolResultWatcher.symbolResult()->taskId(); + QVERIFY2((taskId2.isNull() == false), "SymbolDictionary: taskId failed"); + + QVERIFY2((symbolResultWatcher.symbolResult()->geometryType() == AdvancedSymbology::GeometryType::Point), "SymbolDictionary: geometryType failed"); + QVERIFY2((symbolResultWatcher.symbolResult()->symbolImage().image().isNull() == false), "SymbolDictionary: symbolImage failed"); + counter++; + } + QVERIFY2((counter == sidcs.size()), "SymbolDictionary: something failed"); + qWarning() << " counter1=" << counter; + + // + // Line: + // + counter = 0; + + sidcs = AdvancedSymbology::listOfLineSidcs_Internal(); + + for (const QString &sidc : std::as_const(sidcs)) + { + qWarning() << " sidc=" << sidc; + SituationMap::SymbolData symData; + AdvancedSymbology::SymbolDataToRender symDataToRender = symData.getDefaultRenderData(); + symDataToRender.sidc = sidc; + + AdvancedSymbology::SymbolResultWatcher symbolResultWatcher; + QSignalSpy spy(&symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady); + + symbolResultWatcher.setSymbolResult(dict.findImage(symDataToRender)); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + QVERIFY2((symbolResultWatcher.symbolResult().isNull() == false), "SymbolDictionary: symbolResult failed"); + + QTRY_VERIFY2((spy.count() == 1), "SymbolDictionary: resultReady-signal failed"); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + + // TaskId is skipped here : + + QVERIFY2((symbolResultWatcher.symbolResult()->geometryType() == AdvancedSymbology::GeometryType::Polyline), "SymbolDictionary: geometryType failed"); + QVERIFY2((symbolResultWatcher.symbolResult()->symbolImage().image().isNull() == false), "SymbolDictionary: symbolImage failed"); + counter++; + } + QVERIFY2((counter == sidcs.size()), "SymbolDictionary: something failed"); + qWarning() << " counter2=" << counter; + + // + // Area: + // + counter = 0; + + sidcs = AdvancedSymbology::listOfAreaSidcs_Internal(); + + for (const QString &sidc : std::as_const(sidcs)) + { + // qWarning() << " sidc=" << sidc; + SituationMap::SymbolData symData; + AdvancedSymbology::SymbolDataToRender symDataToRender = symData.getDefaultRenderData(); + symDataToRender.sidc = sidc; + + AdvancedSymbology::SymbolResultWatcher symbolResultWatcher; + QSignalSpy spy(&symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady); + + symbolResultWatcher.setSymbolResult(dict.findImage(symDataToRender)); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + QVERIFY2((symbolResultWatcher.symbolResult().isNull() == false), "SymbolDictionary: symbolResult failed"); + + QTRY_VERIFY2((spy.count() == 1), "SymbolDictionary: resultReady-signal failed"); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + + // TaskId is skipped here : + + QVERIFY2((symbolResultWatcher.symbolResult()->geometryType() == AdvancedSymbology::GeometryType::Polygon), "SymbolDictionary: geometryType failed"); + QVERIFY2((symbolResultWatcher.symbolResult()->symbolImage().image().isNull() == false), "SymbolDictionary: symbolImage failed"); + counter++; + } + + QVERIFY2((counter == sidcs.size()), "SymbolDictionary: something failed"); + qWarning() << " counter3=" << counter; + } + +} + +void SitMapUnitTest::checkSymbolDictionaryFindPreview() +{ + AdvancedSymbology::SymbolDictionary &dict = + AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + // SymbologyType::None -> no esriDict -> immediately loaded: + // ! SymbologyType::None -> esriDict -> load... + + dict.load(); + QTRY_VERIFY2((dict.isLoaded() == true), "SymbolDictionary: isLoaded failed"); + + if (m_d->m_symbologyType == AdvancedSymbology::SymbologyType::MIL2525D) + { + QStringList sidcs = AdvancedSymbology::listOfPointSidcs_Internal(); + + // + // SinglePoint: + // + int counter = 0; + for (const QString &sidc : std::as_const(sidcs)) + { + AdvancedSymbology::SymbolResultWatcher symbolResultWatcher; + QSignalSpy spy(&symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady); + + symbolResultWatcher.setSymbolResult(dict.findPreview(sidc)); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + QVERIFY2((symbolResultWatcher.symbolResult().isNull() == false), "SymbolDictionary: symbolResult failed"); + + QTRY_VERIFY2((spy.count() == 1), "SymbolDictionary: resultReady-signal failed"); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + + const QUuid &taskId2 = symbolResultWatcher.symbolResult()->taskId(); + QVERIFY2((taskId2.isNull() == false), "SymbolDictionary: taskId failed"); + + QVERIFY2((symbolResultWatcher.symbolResult()->geometryType() == AdvancedSymbology::GeometryType::Point), "SymbolDictionary: geometryType failed"); + QVERIFY2((symbolResultWatcher.symbolResult()->symbolImage().image().isNull() == false), "SymbolDictionary: symbolImage failed"); + counter++; + } + QVERIFY2((counter == sidcs.size()), "SymbolDictionary: something failed"); + qWarning() << " counter1=" << counter; + + // + // Line: + // + counter = 0; + + sidcs = AdvancedSymbology::listOfLineSidcs_Internal(); + + for (const QString &sidc : std::as_const(sidcs)) + { + // qWarning() << " sidc=" << sidc; + AdvancedSymbology::SymbolResultWatcher symbolResultWatcher; + QSignalSpy spy(&symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady); + + symbolResultWatcher.setSymbolResult(dict.findPreview(sidc)); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + QVERIFY2((symbolResultWatcher.symbolResult().isNull() == false), "SymbolDictionary: symbolResult failed"); + + QTRY_VERIFY2((spy.count() == 1), "SymbolDictionary: resultReady-signal failed"); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + + // TaskId is slipped here : + + QVERIFY2((symbolResultWatcher.symbolResult()->geometryType() == AdvancedSymbology::GeometryType::Polyline), "SymbolDictionary: geometryType failed"); + QVERIFY2((symbolResultWatcher.symbolResult()->symbolImage().image().isNull() == false), "SymbolDictionary: symbolImage failed"); + counter++; + } + QVERIFY2((counter == sidcs.size()), "SymbolDictionary: something failed"); + qWarning() << " counter2=" << counter; + + // + // Area: + // + counter = 0; + + sidcs = AdvancedSymbology::listOfAreaSidcs_Internal(); + + for (const QString &sidc : std::as_const(sidcs)) + { + // qWarning() << " sidc=" << sidc; + AdvancedSymbology::SymbolResultWatcher symbolResultWatcher; + QSignalSpy spy(&symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady); + + symbolResultWatcher.setSymbolResult(dict.findPreview(sidc)); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + QVERIFY2((symbolResultWatcher.symbolResult().isNull() == false), "SymbolDictionary: symbolResult failed"); + + QTRY_VERIFY2((spy.count() == 1), "SymbolDictionary: resultReady-signal failed"); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + + // TaskId is slipped here : + + QVERIFY2((symbolResultWatcher.symbolResult()->geometryType() == AdvancedSymbology::GeometryType::Polygon), "SymbolDictionary: geometryType failed"); + QVERIFY2((symbolResultWatcher.symbolResult()->symbolImage().image().isNull() == false), "SymbolDictionary: symbolImage failed"); + counter++; + } + QVERIFY2((counter == sidcs.size()), "SymbolDictionary: something failed"); + qWarning() << " counter3=" << counter; + + } +} + +void SitMapUnitTest::checkSymbolDictionaryFindBaseSymbol() +{ + AdvancedSymbology::SymbolDictionary &dict = + AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + // SymbologyType::None -> no esriDict -> immediately loaded: + // ! SymbologyType::None -> esriDict -> load... + + dict.load(); + QTRY_VERIFY2((dict.isLoaded() == true), "SymbolDictionary: isLoaded failed"); + + if (m_d->m_symbologyType == AdvancedSymbology::SymbologyType::MIL2525D) + { + // + // SinglePoint: + // + QStringList sidcs = AdvancedSymbology::listOfPointSidcs_Internal(); + + int counter = 0; + for (const QString &sidc : std::as_const(sidcs)) + { + // qWarning() << " sidc=" << sidc; + + AdvancedSymbology::SymbolResultWatcher symbolResultWatcher; + QSignalSpy spy(&symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady); + + symbolResultWatcher.setSymbolResult(dict.findBaseSymbol(sidc)); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + QVERIFY2((symbolResultWatcher.symbolResult().isNull() == false), "SymbolDictionary: symbolResult failed"); + + QTRY_VERIFY2((spy.count() == 1), "SymbolDictionary: resultReady-signal failed"); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + + // TaskId is skipped here + + QVERIFY2((symbolResultWatcher.symbolResult()->geometryType() == AdvancedSymbology::GeometryType::Point), "SymbolDictionary: geometryType failed"); + QVERIFY2((symbolResultWatcher.symbolResult()->symbolImage().image().isNull() == false), "SymbolDictionary: symbolImage failed"); + counter++; + } + + QVERIFY2((counter == sidcs.size()), "SymbolDictionary: something failed"); + qWarning() << " counter1=" << counter; + + // + // Line: + // + counter = 0; + + sidcs = AdvancedSymbology::listOfLineSidcs_Internal(); + + for (const QString &sidc : std::as_const(sidcs)) + { + // qWarning() << " sidc=" << sidc; + + AdvancedSymbology::SymbolResultWatcher symbolResultWatcher; + QSignalSpy spy(&symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady); + + symbolResultWatcher.setSymbolResult(dict.findBaseSymbol(sidc)); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + QVERIFY2((symbolResultWatcher.symbolResult().isNull() == false), "SymbolDictionary: symbolResult failed"); + + QTRY_VERIFY2((spy.count() == 1), "SymbolDictionary: resultReady-signal failed"); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + + // TaskId is skipped here: + + QVERIFY2((symbolResultWatcher.symbolResult()->geometryType() == AdvancedSymbology::GeometryType::Polyline), "SymbolDictionary: geometryType failed"); + QVERIFY2((symbolResultWatcher.symbolResult()->symbolImage().image().isNull() == false), "SymbolDictionary: symbolImage failed"); + counter++; + } + QVERIFY2((counter == sidcs.size()), "SymbolDictionary: something failed"); + qWarning() << " counter2=" << counter; + + + // + // Area: + // + counter = 0; + + sidcs = AdvancedSymbology::listOfAreaSidcs_Internal(); + + for (const QString &sidc : std::as_const(sidcs)) + { + // qWarning() << " sidc=" << sidc; + AdvancedSymbology::SymbolResultWatcher symbolResultWatcher; + QSignalSpy spy(&symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady); + + symbolResultWatcher.setSymbolResult(dict.findPreview(sidc)); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + QVERIFY2((symbolResultWatcher.symbolResult().isNull() == false), "SymbolDictionary: symbolResult failed"); + + QTRY_VERIFY2((spy.count() == 1), "SymbolDictionary: resultReady-signal failed"); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: resultIsValid failed"); + + // TaskId is slipped here : + + QVERIFY2((symbolResultWatcher.symbolResult()->geometryType() == AdvancedSymbology::GeometryType::Polygon), "SymbolDictionary: geometryType failed"); + QVERIFY2((symbolResultWatcher.symbolResult()->symbolImage().image().isNull() == false), "SymbolDictionary: symbolImage failed"); + counter++; + } + QVERIFY2((counter == sidcs.size()), "SymbolDictionary: something failed"); + qWarning() << " counter3=" << counter; + } +} + + + +void SitMapUnitTest::checkSymbolDictionaryGctString() +{ + AdvancedSymbology::SymbolDictionary &dict = + AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + // SymbologyType::None -> no esriDict -> immediately loaded: + // ! SymbologyType::None -> esriDict -> load... + + dict.load(); + QTRY_VERIFY2((dict.isLoaded() == true), "SymbolDictionary: isLoaded failed"); + + // search: + if (m_d->m_symbologyType == AdvancedSymbology::SymbologyType::MIL2525D) + { + QStringList slGctLine; + QStringList slGctArea; + { + slGctLine += QLatin1String("gct_arrow"); + + slGctLine += QLatin1String("gct_arrow_with_offset"); + slGctLine += QLatin1String("gct_parallel_lines"); + slGctLine += QLatin1String("gct_parallel_lines_mid_line"); + slGctLine += QLatin1String("gct_parallel_lines_with_ticks"); + slGctLine += QLatin1String("gct_two_line"); + slGctLine += QLatin1String("gct_arrow_with_tail"); + slGctLine += QLatin1String("gct_open_triangle"); + + slGctLine += QLatin1String("gct_freehand_line"); + slGctLine += QLatin1String("gct_freehand_arrow"); + slGctLine += QLatin1String("gct_freehand_reverse_arrow"); + slGctLine += QLatin1String("gct_freehand_u"); + + slGctLine += QLatin1String("gct_circle"); + slGctLine += QLatin1String("gct_hook"); + slGctLine += QLatin1String("gct_horns"); + slGctLine += QLatin1String("gct_parallel_lines_with_ticks"); + slGctLine += QLatin1String("gct_t"); + slGctLine += QLatin1String("gct_triple_arrow"); + slGctLine += QLatin1String("gct_two_line_3_or_4_pt"); + slGctLine += QLatin1String("gct_u_or_t_shape"); + + slGctArea += QLatin1String("gct_rectangular_1_pt"); + slGctArea += QLatin1String("gct_circular"); + slGctArea += QLatin1String("gct_rectangular"); + slGctArea += QLatin1String("gct_parallel_lines_with_ticks"); + } + + unsigned int symbolCounter = 0; + unsigned int unexpectedCounter = 0; + unsigned int emptyCounter = 0; + + AdvancedSymbology::SymbolDictionaryImpl &dictImpl = + static_cast(dict); + + const AdvancedSymbology::SidcCodec::SidcHashTable &allSymbolProps = + dictImpl.getEsriDictionaryProcessor()->baseSymbolCodeToSymbolPropertyTable(); + + + for (const AdvancedSymbology::SymbolProperties &prop : std::as_const(allSymbolProps)) + { + QString baseKey = prop.key(); + if (baseKey.isEmpty()) + continue; + + ++symbolCounter; + const QString & gctString = prop.gctString(); + if (! gctString.isEmpty()) + { + + switch (prop.geometryType()) + { + case AdvancedSymbology::GeometryType::Point: + { + if (! gctString.isEmpty()) + { + ++unexpectedCounter; + qDebug() << " Point feature has none empty gctString, baseKey=" << baseKey << " gctString =" << gctString << endl; + continue; + } + } + break; + + case AdvancedSymbology::GeometryType::Polyline: + { + if (! slGctLine.contains(gctString) ) + { + ++unexpectedCounter; + qDebug() << " Unexpected gctString for line symbol found, baseKey=" << baseKey << " gctString =" << gctString << endl; + } + continue; + } + break; + + case AdvancedSymbology::GeometryType::Polygon: + { + if (! slGctArea.contains(gctString) ) + { + ++unexpectedCounter; + qDebug() << " Unexpected gctString for area symbol found, baseKey=" << baseKey << " gctString =" << gctString << endl; + } + continue; + } + break; + + default : break; + } // switch + } + else + { + switch (prop.geometryType()) + { + case AdvancedSymbology::GeometryType::Polyline: qDebug() << " Empty gctString found for line symbol, baseKey=" << baseKey << endl; ++emptyCounter; break; + case AdvancedSymbology::GeometryType::Polygon : qDebug() << " Empty gctString found for area symbol, baseKey=" << baseKey << endl; ++emptyCounter; break; + case AdvancedSymbology::GeometryType::Point : + default : break; + } + } + } // foreach + + qDebug() << "Checked symbols: " << symbolCounter << " empty gctString at line or areals: " << emptyCounter << endl; + + static char msg[128]; + sprintf( msg, "%d unexpected gctStrings found.", unexpectedCounter); + QVERIFY2( (unexpectedCounter == 0), msg ); + + } + return; + +} // void SitMapUnitTest::checkSymbolDictionaryGctString() + + +void SitMapUnitTest::checkSymbolDictionarySymbolPropertyImage() +{ + AdvancedSymbology::SymbolDictionary &dict = + AdvancedSymbology::SymbolDictionary::dictionary(m_d->m_symbologyType); + + // SymbologyType::None -> no esriDict -> immediately loaded: + // ! SymbologyType::None -> esriDict -> load... + + dict.load(); + QTRY_VERIFY2((dict.isLoaded() == true), "SymbolDictionary: isLoaded failed"); + + if (m_d->m_symbologyType == AdvancedSymbology::SymbologyType::MIL2525D) + { + AdvancedSymbology::SymbolProperties symProps; + symProps.setOriginalKey(QLatin1String("25270100")); // obstacle / building + + + AdvancedSymbology::SymbolResultWatcher symbolResultWatcher; + QSignalSpy spy(&symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady); + + QVERIFY2((symbolResultWatcher.resultIsValid() == false), "SymbolDictionary: resultIsValid failed"); + + symbolResultWatcher.setSymbolResult(dict.symbolPropertyImage(symProps)); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: symbolPropertyImage resultIsValid failed"); + QVERIFY2((symbolResultWatcher.symbolResult().isNull() == false), "SymbolDictionary: symbolPropertyImage symbolResult failed"); + + QTRY_VERIFY2((spy.count() == 1), "SymbolDictionary: symbolPropertyImage resultReady-signal failed"); + QVERIFY2((symbolResultWatcher.resultIsValid() == true), "SymbolDictionary: symbolPropertyImage resultIsValid failed"); + + // inspect result: +// QVERIFY2((symbolResultWatcher.symbolResult()->geometryType() == AdvancedSymbology::GeometryType::Polygon), "SymbolDictionary: geometryType failed"); + QVERIFY2((symbolResultWatcher.symbolResult()->symbolImage().image().isNull() == false), "SymbolDictionary: symbolImage failed"); + QImage img = symbolResultWatcher.symbolResult()->symbolImage().image(); + img.save(QLatin1String("/tmp/unitTest.png")); + + } +} + + + + +void SitMapUnitTest::checkMapSettings() +{ + if (1) + { + // use absolute path: + // SituationMap::MapSettings ms(QLatin1String("/shd/CTC/TOOLS/TDS/TDS-build-product/bin/situation_map_defaults.ini")); + + QString initFilePath = NGL::findFile(NGL::FileType::ConfigFile, QStringLiteral("situation_map_defaults.ini")); + + if (initFilePath.isEmpty()) + { + FATAL_LOG(m_logger) << " initFilePath " << initFilePath; + abort(); + } + SituationMap::MapSettings ms(initFilePath); + + QVERIFY2(ms.hasErrors() == false, "hasError failed"); + QVERIFY2(ms.exists() == true, "exists failed"); + QVERIFY2(ms.accessError() == false, "accessError failed"); + QVERIFY2(ms.formatError() == false, "formatError failed"); + + std::cerr << " operator<<: \n" << ms << std::endl; + + SituationMap::GridType gridType = ms.gridType(); + QVERIFY2(gridType == SituationMap::GridType::MGRS, "gridType failed"); + } +} + + +void SitMapUnitTest::checkMatrix2D() +{ + SituationMap::MathUtils::Matrix2D matrix; + + matrix.setToIdentity(); + + QVERIFY2( ( (matrix(0,0) == 1.0) && (matrix(0,1) == 0.0) && (matrix(0,2) == 0.0) && + (matrix(1,0) == 0.0) && (matrix(1,1) == 1.0) && (matrix(1,2) == 0.0) && + (matrix(2,0) == 0.0) && (matrix(2,1) == 0.0) && (matrix(2,2) == 1.0) ), "setToIdentity failed" ); + + + SituationMap::MathUtils::Matrix2D matrix2; + + QVERIFY2( ( (matrix2(0,0) == 1.0) && (matrix2(0,1) == 0.0) && (matrix2(0,2) == 0.0) && + (matrix2(1,0) == 0.0) && (matrix2(1,1) == 1.0) && (matrix2(1,2) == 0.0) && + (matrix2(2,0) == 0.0) && (matrix2(2,1) == 0.0) && (matrix2(2,2) == 1.0) ), "standard constructor does not set the identity matrix"); + + matrix2(0,0) = 11; matrix2(0,1) = 12; matrix2(0,2) = 13; + matrix2(1,0) = 21; matrix2(1,1) = 22; matrix2(1,2) = 23; + matrix2(2,0) = 31; matrix2(2,1) = 32; matrix2(2,2) = 33; + + QVERIFY2( ( (matrix2(0,0) == 11) && (matrix2(0,1) == 12) && (matrix2(0,2) == 13) && + (matrix2(1,0) == 21) && (matrix2(1,1) == 22) && (matrix2(1,2) == 23) && + (matrix2(2,0) == 31) && (matrix2(2,1) == 32) && (matrix2(2,2) == 33) ), "set matrix elements failed"); + + SituationMap::MathUtils::Matrix2D matrix3(matrix2); + + QVERIFY2( ( (matrix3(0,0) == 11) && (matrix3(0,1) == 12) && (matrix3(0,2) == 13) && + (matrix3(1,0) == 21) && (matrix3(1,1) == 22) && (matrix3(1,2) == 23) && + (matrix3(2,0) == 31) && (matrix3(2,1) == 32) && (matrix3(2,2) == 33) ), "copy constructor failed"); + + matrix = matrix2; + + QVERIFY2( ( (matrix(0,0) == 11) && (matrix(0,1) == 12) && (matrix(0,2) == 13) && + (matrix(1,0) == 21) && (matrix(1,1) == 22) && (matrix(1,2) == 23) && + (matrix(2,0) == 31) && (matrix(2,1) == 32) && (matrix(2,2) == 33) ), "assign operator failed"); + + matrix.setToIdentity(); + matrix.putTranslation(QPointF(5,7)); + QPointF p = matrix.apply(QPointF(2,1)); + + QVERIFY2( ((p.x() == 7) && (p.y() == 8)), "putTranslation failed"); + + matrix.setToIdentity(); + matrix.putScale(QPointF(2,3)); + p = matrix.apply(QPointF(2,1)); + + QVERIFY2( ((p.x() == 4) && (p.y() == 3)), "putScale failed"); + + matrix.setToIdentity(); + matrix.putScale(QPointF(2,2)); + matrix.putTranslation(QPointF(5,7)); + p = matrix.apply(QPointF(2,1)); + QVERIFY2( ((p.x() == 9) && (p.y() == 9)), "putScale combined with putTranslate failed"); + + matrix.setToIdentity(); + matrix.putRotate(QPointF(0,1)); + p = matrix.apply(QPointF(2,1)); + + QVERIFY2( ((p.x() == 1) && (p.y() == -2)), "putRotated failed"); + + matrix.setToIdentity(); + matrix.putRotate(QPointF(0.5,0.8660254)); // 60 degrees + p = matrix.apply(QPointF(1,0)); + + QVERIFY2( ((int(p.x()*10) == 5) && (int(p.y()*1000000) == -866025)), "putRotated 60 degrees failed"); + + matrix.setToIdentity(); + matrix.putTranslation(QPointF(-5,-7)); + matrix.putRotate(QPointF(0,1)); + matrix.putTranslation(QPointF(5,7)); + p = matrix.apply(QPointF(7,8)); + + QVERIFY2( ((p.x() == 6) && (p.y() == 5)), "putRotated combined with putTranslate failed"); + + matrix.setToIdentity(); + matrix.putTranslation(QPointF(-5,-7)); + matrix.putRotate(QPointF(0.5,0.8660254)); // 60 degrees + matrix.putTranslation(QPointF(5,7)); + p = matrix.apply(QPointF(6,7)); + + QVERIFY2( ((int(p.x()*10) == 55) && (int(p.y()*10000000) == 61339746)), "putRotated 60 degrees combined with putTranslate failed"); + +} // SitMapUnitTtest::checkMatrix2D() + + +//------------------------------------------------------------------------------------------------------- + +#include "unit_test.moc" diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTests/unit_test.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTests/unit_test.h new file mode 100755 index 000000000..595727222 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTests/unit_test.h @@ -0,0 +1,142 @@ +/** + * @RDE_HEADER@ + * Unittest + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include + +#include +#include +#include + +#include + +namespace AdvancedSymbology +{ +enum class SymbologyType; +} + + +class CheckSignal : public QObject +{ + Q_OBJECT +public: + bool isConnected(quint64 &msec); + +signals: + void mySignal(); +}; + + + +class SitMapUnitTest : public QObject +{ + Q_OBJECT + +public: + SitMapUnitTest(AdvancedSymbology::SymbologyType symbologyType); + virtual ~SitMapUnitTest(); + +private: + void checkSymbolDictionaryLoad(); + void checkSymbolDictionaryLoad(AdvancedSymbology::SymbologyType st); +private Q_SLOTS: + + void checkLogging(); + + void checkQRect(); + void checkQListQVector(); + void checkQWeakPointer(); + + void checkFactoryCreationContraints(); + +// void checkIdHandlerCreate(); +// void checkIdHandlerFree(); +// void checkIdHandlerReUseId(); +// void checkIdHandlerReserveId(); + + void checkSimpleValue(); + + void checkIdGenerator(); + void checkIdContainer(); + + void checkIsSignalConnected(); + void onCheckIsSignalConnected(); + + void checkMapLayer(); + void checkMapLayerOrderedList(); + + void checkLayerFactory(); + + void checkObjectFactory(); + + void checkPickableOnInvalidUniqueMapId(); + + void checkBoundingBox(); + + void checkDistanceMeasurement(); + void measureDistanceGeodetic(); + + void recalculateAngle(); + + void checkDensify(); + void checkBuffer(); + + void checkMovementTrack(); + void checkMovementTrackManager(); + + + void checkSidcCodec(); + + void checkSIDCDefinitions1(); + void checkSIDCDefinitions2(); +// void checkCreateSidc(); +// void checkSymbolProperties(); + void checkSymbolDictionaryLoadSymbolXml(); + + void checkSymbolDictionaryConsistency(); + + void checkSymbolDictionaryFetchSymbol(); + void checkSymbolDictionaryFetchSymbol2(); + void checkSymbolDictionaryFetchSymbolAll(); + void checkSymbolDictionaryExtraDirFetchSymbol(); + void checkSymbolDictionaryFetchSymbolStress(); + + void checkSymbolDictionaryBulkRenderer(); + + void checkSymbolDictionarySymbolPropertyImage(); + + void checkSymbolDictionaryFindImage(); + void checkSymbolDictionaryFindPreview(); + void checkSymbolDictionaryFindBaseSymbol(); + + void checkSymbolDictionaryGctString(); + + + void checkMapSettings(); + + void checkMatrix2D(); + + void virtualInheritances(); + + void multiInheritancesVisualStudio2015Bug(); + +protected: + +private: + + class Private; + + static const unsigned int m_initReserve = 20; + static const unsigned int m_blockSize = 10; + + SituationMap::LOGGER_T m_logger; + std::unique_ptr m_d; + +}; + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTests/virtual_inheritances.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTests/virtual_inheritances.cpp new file mode 100755 index 000000000..cd0e167fa --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTests/virtual_inheritances.cpp @@ -0,0 +1,252 @@ +/** + * @RDE_HEADER@ + * Unittest + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "unit_test.h" + +#include +#include +#include + + +#include + +#include + + + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * Test of virtual inheritance combined with constuctor delegation. With gcc all works fine. With Visual Studio 2013 on Windows + * the constructor of VirtualBaseInterface class is called twice. This may crash the program or result in other unexpected behaviour. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + + + +class VirtualBaseInterface +{ + +public: + + static unsigned int _ctorCount; + + VirtualBaseInterface() + { + qDebug() << "ctor of class VirtualBaseInterface() has been called"; + _ctorCount++; + } + + VirtualBaseInterface(int, int) + { + // in case of virtual inheritance this ctor will never be used. + qDebug() << "ctor of class VirtualBaseInterface(int x, int y) has been called"; + _ctorCount++; + } + + ~VirtualBaseInterface() = default; + + virtual void foo() = 0; +}; + +unsigned int VirtualBaseInterface::_ctorCount = 0; + + +class BaseClass : virtual public VirtualBaseInterface , public QObject +{ +protected: + + int c; + +public: + + BaseClass(int x, int y) : BaseClass(x, y, 10) + { + qDebug() << "ctor of class BaseClass(int x,int y) has been called"; + } + + BaseClass(int x, int y, int z) : VirtualBaseInterface(), QObject(), c(z) + { + Q_UNUSED(x) + Q_UNUSED(y) + qDebug() << "ctor of class BaseClass(int x,int y,int z) has been called"; + } + + virtual ~BaseClass() = default; + + void foo() override + { + c = 10; + } +}; + + +class Derived1 : public BaseClass +{ +protected: + + int d; + +public: + + Derived1(int x, int y) : BaseClass(x, y), d(10) + {} + + Derived1(int x, int y, int z, int w) : BaseClass(x, y, z), d(w) + {} + + void foo() override + { + d = 13; + } +}; + +class Derived2 : public Derived1 +{ +public: + + Derived2(int x, int y) : Derived1(x, y) + {} + + Derived2(int x, int y, int z, int w) : Derived1(x, y, z, w) + {} + + void foo() override + { + d = 17; + } +}; + + +void SitMapUnitTest::virtualInheritances() +{ + bool ok = true; + qDebug() << endl << "Check Derived2(int x,int y,int z,int w)" << endl; + try + { + Derived2 d(1, 2, 3, 4); + d.foo(); + } + catch (...) + { + ok = false; + } + QVERIFY2(ok, "virtualInheritances for Derived2(int x,int y,int z,int w) failed."); + + qDebug() << endl << "Check Derived2(int x,int y)" << endl; + try + { + Derived2 d(1, 2); + d.foo(); + } + catch (...) + { + ok = false; + } + QVERIFY2(ok, "virtualInheritances for Derived2(int x,int y) failed."); + + QVERIFY2(VirtualBaseInterface::_ctorCount >= 2, "Too few calles of VirtualBaseInterface ctor."); + QVERIFY2(VirtualBaseInterface::_ctorCount <= 2, "VirtualBaseInterface ctor has been called too often."); + +} // SitMapUnitTest::virtualInheritances + + + +class Referenced +{ + public: + Referenced() { _refCount++; } + ~Referenced() = default; + + unsigned int refcount() { return _refCount; } + + static unsigned int _refCount; +}; + +unsigned int Referenced::_refCount = 0; + + +class AbstractInterface : public virtual Referenced +{ + public: + AbstractInterface() = default; + ~AbstractInterface() = default; + + virtual void foo() = 0; +}; + + +class AbstractInterfaceImpl : public virtual AbstractInterface +{ + unsigned int _data; + + public: + + AbstractInterfaceImpl() : AbstractInterface() { _data = 42; } + ~AbstractInterfaceImpl() { _data = 0; } + void foo() override; + +}; + + +class ObjectInterface : public virtual AbstractInterface +{ + public: + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Using the default constructor here like + // ObjectInterface() = default; + // gives a crash!!!! + // Implementing the ctor works. + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + ObjectInterface() : AbstractInterface() {;} + ~ObjectInterface() = default; + + virtual void foo2() = 0; +}; + + +class ObjectInterfaceImpl : public AbstractInterfaceImpl, public virtual ObjectInterface +{ + unsigned int _objData; +public: + explicit ObjectInterfaceImpl(unsigned int data = 7) + : ObjectInterface(), AbstractInterfaceImpl() + { + _objData = data; + } + + ~ObjectInterfaceImpl() + { + _objData = 0; + } + + void foo2() override; +}; + +void AbstractInterfaceImpl::foo() { _data = 4711; } +void ObjectInterfaceImpl::foo2() { _objData = 815; } + + +void SitMapUnitTest::multiInheritancesVisualStudio2015Bug() +{ + Referenced::_refCount = 0; + + bool ok = true; + qDebug() << endl << "Check ctor ObjectInterfaceImpl " << endl; + try + { + ObjectInterfaceImpl obj; + obj.foo(); + if (obj.refcount() != 1) ok = false; + } + catch (...) + { + ok = false; + } + QVERIFY2(ok, "Check ctor ObjectInterfaceImpl failed."); +} +//------------------------------------------------------------------------------------------------------- + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTestsGeoModel/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTestsGeoModel/CMakeLists.txt new file mode 100755 index 000000000..191839b85 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTestsGeoModel/CMakeLists.txt @@ -0,0 +1,47 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +SET(LinkedSources + log4cxxGeometryModelUnitTest.props +) + +SET(UnitTestSources + unit_test.cpp + unit_test.h +) + +SOURCE_GROUP( LinkedSources FILES ${LinkedSources} ) +SOURCE_GROUP( UnitTestSources FILES ${UnitTestSources} ) + +IF(BUILD_TESTING AND TARGET situationMap) + ADD_EXECUTABLE(situationMapGeometryModelUnitTest ${LinkedSources} ${UnitTestSources} ) + + TARGET_LINK_LIBRARIES(situationMapGeometryModelUnitTest + NGLLogger + nglcoreapplication + situationMapCommon + situationMapGeometryModel + ArcGIS::ArcGIS + Qt6::Core + Qt6::Test + ArcGISQt5Hack + ) + + ADD_TEST(NAME situationMapGeometryModelUnitTest COMMAND $) + + SET(LOG4CXX_PROPS_FILE log4cxxGeometryModelUnitTest.props) + ADD_CUSTOM_COMMAND(TARGET situationMapGeometryModelUnitTest POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${LOG4CXX_PROPS_FILE} $/${LOG4CXX_PROPS_FILE} + ) + + IF(MSVC) + # Configure the template file + SET(VS_USER_FILE situationMapGeometryModelUnitTest.vcxproj.user) + configure_file( ../SituationMap.vcxproj.user.in ${CMAKE_CURRENT_BINARY_DIR}/${VS_USER_FILE} @ONLY) + ENDIF() + +ENDIF() diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTestsGeoModel/log4cxxGeometryModelUnitTest.props b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTestsGeoModel/log4cxxGeometryModelUnitTest.props new file mode 100755 index 000000000..a45f1301c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTestsGeoModel/log4cxxGeometryModelUnitTest.props @@ -0,0 +1,129 @@ +log4j.reset=true +log4j.rootLogger=WARN, file + +# SituationMap +########################################################### +#log4j.logger.SituationMap.SitMapUnitTest=DEBUG +#log4j.logger.SituationMap.SitMap=WARN + +log4j.logger.SituationMap.LoggingTest=ERROR +log4j.logger.SituationMap.LoggingTest.Chan1=INFO +log4j.logger.SituationMap.LoggingTest.Chan2=ERROR + +#log4j.logger.SituationMap.MapImpl=WARN +#log4j.logger.SituationMap.LayerFactoryImpl=INFO +#log4j.logger.SituationMap.CoordTranslationFunctorImpl=INFO +#log4j.logger.SituationMap.GridImpl=WARN +#log4j.logger.SituationMap.ObjectFactory=WARN +#log4j.logger.SituationMap.IdGenerator=TRACE +#log4j.logger.SituationMap.ObjectContainer=TRACE +#log4j.logger.SituationMap.ObjectContainerCTC=TRACE +#log4j.logger.SituationMap.ViewshedImpl=INFO +#log4j.logger.SituationMap.ViewshedBase=INFO +#log4j.logger.SituationMap.SymbolDictionary=WARN +#log4j.logger.SituationMap.SymbolDictionaryImpl=WARN +#log4j.logger.SituationMap.Sidc2525Delta=WARN + +#log4j.logger.SituationMap.AbstractMapObjectImpl=INFO +#log4j.logger.SituationMap.AbstractEffectSimulationImpl=INFO +#log4j.logger.SituationMap.OriginTranslated=INFO +#log4j.logger.SituationMap.PositionsTranslated=INFO +#log4j.logger.SituationMap.AbstractMultiPointMapObjectImpl=INFO +#log4j.logger.SituationMap.AbstractSinglePointMapObjectImpl=INFO +#log4j.logger.SituationMap.BasicPathObjectImpl=INFO +#log4j.logger.SituationMap.IndicationContainer=INFO + +#log4j.logger.SituationMap.AreaFireImpl=INFO +#log4j.logger.SituationMap.BiologicalAgentImpl=INFO +#log4j.logger.SituationMap.BuildingOutlineImpl=INFO +#log4j.logger.SituationMap.ChemicalAgentImpl=INFO +#log4j.logger.SituationMap.DangerAreaImpl=INFO +#log4j.logger.SituationMap.FunctionalAreaImpl=INFO +#log4j.logger.SituationMap.HitLineImpl=INFO +#log4j.logger.SituationMap.MineImpl=INFO +#log4j.logger.SituationMap.NuclearAgentImpl=INFO +#log4j.logger.SituationMap.ParticipantImpl=INFO +#log4j.logger.SituationMap.SingleFireImpl=INFO + +#log4j.logger.SituationMap.AbstractGraphicalObject=WARN +#log4j.logger.SituationMap.GraphicalObjectEllipse=WARN +#log4j.logger.SituationMap.GraphicalObjectPath=WARN +#log4j.logger.SituationMap.GraphicalObjectMultiPath=WARN +#log4j.logger.SituationMap.GraphicalObjectPictureMarker=WARN +#log4j.logger.SituationMap.GraphicalObjectText=WARN +#log4j.logger.SituationMap.TacticalAppearance=INFO +#log4j.logger.SituationMap.MessageBase=INFO + + +# Appnd_File +########################################################### +log4j.appender.file=org.apache.log4j.RollingFileAppender +log4j.appender.file.MaxFileSize=1MB +log4j.appender.file.MaxBackupIndex=9 +log4j.appender.file.file=${LOG4QT_LOGDIR}/${LOG4QT_USER}_SituationMap.log +log4j.appender.file.layout=org.apache.log4j.PatternLayout +log4j.appender.file.layout.ConversionPattern=%d %-5p %c |#| %m (%.50F:%L) %n + + +# Appnd_Console +########################################################### +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern= %d %-5p %c{1} |#| %m (%-.30F:%L) %n + +#log4j.appender.stdout.layout.ConversionPattern= %d %-5p %-30c |#| %m (%-.30F:%L) %n + + +# Appnd_Syslog +########################################################### +log4j.appender.syslog=org.apache.log4j.SyslogAppender +log4j.appender.syslog.Threshold=ERROR +log4j.appender.syslog.SysLogHost=localhost +log4j.appender.syslog.Facility=LOCAL7 +log4j.appender.syslog.layout=org.apache.log4j.PatternLayout +log4j.appender.syslog.layout.ConversionPattern= %-4r %-5p %c %x - %m +log4j.appender.syslog.LevelRangeFilter.AcceptOnMatch=true +log4j.appender.syslog.LevelRangeFilter.LevelMin=INFO +log4j.appender.syslog.LevelRangeFilter.LevelMax=FATAL + + +########################################################### +########################################################### +########################################################### +# 1) Available Levels in LOG4CXX Version 0.10.0: +# TRACE, +# DEBUG, +# INFO, +# WARN, +# ERROR +# FATAL +# +# d = date/time +# p = priority (Level) +# c = category/channel +# m = message +# F = file +# L = line +# n = platform dependent line separator character or characters. +# .... +# +# Examples for console: +# a) +# %d %p %c #%m# (%.50F:%L)%n +# date/time +# priority +# category/channel +# #message# +# (Maximal 50Character des Filename von rechts:Line) +# line separator +# +# b) +# %d (%.15F:%L) %-5p %c |#| %m%n +# date/time +# (Maximal 15Character des Filename von rechts:Line) +# priority, Right pad with spaces if the priority name is less than 5 characters long. +# category/channel +# message +# line separator +# + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTestsGeoModel/unit_test.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTestsGeoModel/unit_test.cpp new file mode 100755 index 000000000..109790719 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTestsGeoModel/unit_test.cpp @@ -0,0 +1,807 @@ +/** + * @RDE_HEADER@ + * Unittest + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "unit_test.h" + +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +#include + + +using namespace SituationMap; + + +bool CheckSignal::isConnected() +{ + bool isConnected = isSignalConnected( QMetaMethod::fromSignal(&CheckSignal::mySignal) ); + return isConnected; +} + + +GeoModelUnitTest::GeoModelUnitTest() : m_logger(getLogger("GeometryModelUnitTest")) +{ +} + +void GeoModelUnitTest::init() +{ + qRegisterMetaType("GeometryModel::Node"); // not really working? + qRegisterMetaType("SituationMap::GeometryModel::Node"); // not really working? + qRegisterMetaType("GeometryModel::Attribute"); +} + + +void GeoModelUnitTest::checkSignals1() +{ + GeometryModel * geoModelW = GeometryModelFactory::singleton()->create(); + QList vertexListW; + QList> attrListW; + + attrListW += QPair(GeometryModel::Attribute::Color,QVariant(QColor("red"))); + + vertexListW << QPointF(1, 1) << QPointF(2,2); + + QVERIFY2( (geoModelW != nullptr), "Creating empty GeometryModel failed."); + + // connect signals... + QSignalSpy spyTypeError(geoModelW, &GeometryModel::typeError); + QSignalSpy spyAttributeError(geoModelW, &GeometryModel::attributeError); + QSignalSpy spyModelError(geoModelW, &GeometryModel::modelError); + QSignalSpy spyUserDataCleanup(geoModelW,&GeometryModel::userDataCleanup); + QSignalSpy spyNodeCleanup(geoModelW, &GeometryModel::nodeCleanup); + QSignalSpy spyChanged(geoModelW, &GeometryModel::changed); + + // + // addPrimitive + // + + // do operation : + const GeometryModel::Node & nodeWP = geoModelW->addPrimitive( GeometryModel::PrimitiveType::Line, vertexListW, attrListW ); + + // verify, how often signals are emitted: + QVERIFY2(spyTypeError.count() == 0, "GeometryModel: signalCount failed, spyTypeError"); + QVERIFY2(spyAttributeError.count() == 0, "GeometryModel: signalCount failed, spyAttributeError"); + QVERIFY2(spyModelError.count() == 0, "GeometryModel: signalCount failed, spyModelError"); + QVERIFY2(spyUserDataCleanup.count() == 0, "GeometryModel: signalCount failed, spyUserDataCleanup"); + QVERIFY2(spyNodeCleanup.count() == 0, "GeometryModel: signalCount failed, spyNodeCleanup"); + QVERIFY2(spyChanged.count() == 1, "GeometryModel: signalCount failed, spyChanged"); + // : do operation + + // + // setUserData + // + { + // do operation : + qWarning() << "setUserData..."; + spyTypeError.clear();spyAttributeError.clear();spyModelError.clear();spyUserDataCleanup.clear();spyNodeCleanup.clear();spyChanged.clear(); + geoModelW->setUserData( nodeWP, 4711 ); + QVERIFY2(spyTypeError.count() == 0, "GeometryModel: signalCount failed, spyTypeError"); + QVERIFY2(spyAttributeError.count() == 0, "GeometryModel: signalCount failed, spyAttributeError"); + QVERIFY2(spyModelError.count() == 0, "GeometryModel: signalCount failed, spyModelError"); + QVERIFY2(spyUserDataCleanup.count() == 0, "GeometryModel: signalCount failed, spyUserDataCleanup"); + QVERIFY2(spyNodeCleanup.count() == 0, "GeometryModel: signalCount failed, spyNodeCleanup"); + QVERIFY2(spyChanged.count() == 1, "GeometryModel: signalCount failed, spyChanged"); + // : do operation + } + + // + // Transfer via QByteArray: + // + + const QByteArray stringRep = geoModelW->toByteArray(); // qDebug() << stringRep; + + GeometryModel * geoModelR = GeometryModelFactory::singleton()->create(); + QVERIFY2(geoModelR, "Creating second GeometryModel failed."); + + { + // + // change to mode geoModelR + // + + QSignalSpy spyTypeError(geoModelR, &GeometryModel::typeError); + QSignalSpy spyAttributeError(geoModelR, &GeometryModel::attributeError); + QSignalSpy spyModelError(geoModelR, &GeometryModel::modelError); + QSignalSpy spyUserDataCleanup(geoModelR,&GeometryModel::userDataCleanup); + QSignalSpy spyNodeCleanup(geoModelR, &GeometryModel::nodeCleanup); + QSignalSpy spyChanged(geoModelR, &GeometryModel::changed); + + // + // fromString + // + { + // do operation : + qWarning() << "fromString..."; + spyTypeError.clear();spyAttributeError.clear();spyModelError.clear();spyUserDataCleanup.clear();spyNodeCleanup.clear();spyChanged.clear(); + QVERIFY2( geoModelR->fromByteArray(stringRep) , "Building GeometryModel from String failed." ); + QVERIFY2(spyTypeError.count() == 0, "GeometryModel: signalCount failed, spyTypeError"); + QVERIFY2(spyAttributeError.count() == 0, "GeometryModel: signalCount failed, spyAttributeError"); + QVERIFY2(spyModelError.count() == 0, "GeometryModel: signalCount failed, spyModelError"); + QVERIFY2(spyUserDataCleanup.count() == 0, "GeometryModel: signalCount failed, spyUserDataCleanup"); + QVERIFY2(spyNodeCleanup.count() == 0, "GeometryModel: signalCount failed, spyNodeCleanup"); + QVERIFY2(spyChanged.count() == 0, "GeometryModel: signalCount failed, spyChanged"); // correct? + // : do operation + } + + + // + // removePrimitive + // + + { + geoModelR->enableUndo( true ); + geoModelR->startUndoBlock(); + + // do operation : + qWarning() << "removePrimitive..."; + spyTypeError.clear();spyAttributeError.clear();spyModelError.clear();spyUserDataCleanup.clear();spyNodeCleanup.clear();spyChanged.clear(); + geoModelR->removePrimitive( nodeWP ); + QVERIFY2(spyTypeError.count() == 0, "GeometryModel: signalCount failed, spyTypeError"); + QVERIFY2(spyAttributeError.count() == 0, "GeometryModel: signalCount failed, spyAttributeError"); + QVERIFY2(spyModelError.count() == 0, "GeometryModel: signalCount failed, spyModelError"); + QVERIFY2(spyUserDataCleanup.count() == 1, "GeometryModel: signalCount failed, spyUserDataCleanup"); + QVERIFY2(spyNodeCleanup.count() == 0, "GeometryModel: signalCount failed, spyNodeCleanup"); + QVERIFY2(spyChanged.count() == 0, "GeometryModel: signalCount failed, spyChanged"); // see r45452, 'avoid invalid errorMsg' + //QVERIFY2(spyChanged.count() == 1, "GeometryModel: signalCount failed, spyChanged"); + + + // : do operation + } + geoModelR->endUndoBlock( QLatin1String("Remove Primitive") ); + + + // + // undo + // + { + geoModelR->enableUndo( true ); + geoModelR->startUndoBlock(); + + // do operation : + qWarning() << "undo..."; + spyTypeError.clear();spyAttributeError.clear();spyModelError.clear();spyUserDataCleanup.clear();spyNodeCleanup.clear();spyChanged.clear(); + geoModelR->undo(); + QVERIFY2(spyTypeError.count() == 0, "GeometryModel: signalCount failed, spyTypeError"); + QVERIFY2(spyAttributeError.count() == 0, "GeometryModel: signalCount failed, spyAttributeError"); + QVERIFY2(spyModelError.count() == 0, "GeometryModel: signalCount failed, spyModelError"); + QVERIFY2(spyUserDataCleanup.count() == 0, "GeometryModel: signalCount failed, spyUserDataCleanup"); + QVERIFY2(spyNodeCleanup.count() == 1, "GeometryModel: signalCount failed, spyNodeCleanup"); + QVERIFY2(spyChanged.count() == 1, "GeometryModel: signalCount failed, spyChanged"); + // : do operation + } + } +} // GeoModelUnitTest::checkSignals1 + + +void GeoModelUnitTest::checkSignals2() +{ + // create Reference model (...W) + GeometryModel * geoModelW = GeometryModelFactory::singleton()->create(); + QList vertexListW; + QList> attrListW; + + attrListW += QPair(GeometryModel::Attribute::Color,QVariant(QColor("red"))); + vertexListW << QPointF(1, 1) << QPointF(2,2); + + geoModelW->addPrimitive( GeometryModel::PrimitiveType::Line, vertexListW, attrListW, 42 ); + // ==> root -> line42 + + const GeometryModel::Node & rootNode = geoModelW->getRootNode(); + const GeometryModel::Node & nodeWP = geoModelW->addPrimitive( GeometryModel::PrimitiveType::Line, vertexListW, attrListW, 815 ); + GeometryModel::Node && groupWP = geoModelW->createGroup( GeometryModel::NodeList() ); + // ==> root -> line42 + // -> line815 + // -> group + + QVERIFY2( groupWP.parentNode() == rootNode, "Group parent is not the root node." ); + + + QSignalSpy spyTypeError(geoModelW, &GeometryModel::typeError); + QSignalSpy spyAttributeError(geoModelW, &GeometryModel::attributeError); + QSignalSpy spyModelError(geoModelW, &GeometryModel::modelError); + QSignalSpy spyUserDataCleanup(geoModelW,&GeometryModel::userDataCleanup); + QSignalSpy spyNodeCleanup(geoModelW, &GeometryModel::nodeCleanup); + QSignalSpy spyChanged(geoModelW, &GeometryModel::changed); + + // + // movePrimitiveIntoGroup + // + { + // do operation : + qWarning() << "movePrimitiveIntoGroup..."; + spyTypeError.clear();spyAttributeError.clear();spyModelError.clear();spyUserDataCleanup.clear();spyNodeCleanup.clear();spyChanged.clear(); + geoModelW -> movePrimitiveIntoGroup( nodeWP, groupWP ); + QVERIFY2(spyTypeError.count() == 0, "GeometryModel: signalCount failed, spyTypeError"); + QVERIFY2(spyAttributeError.count() == 0, "GeometryModel: signalCount failed, spyAttributeError"); + QVERIFY2(spyModelError.count() == 0, "GeometryModel: signalCount failed, spyModelError"); + QVERIFY2(spyUserDataCleanup.count() == 0, "GeometryModel: signalCount failed, spyUserDataCleanup"); + QVERIFY2(spyNodeCleanup.count() == 0, "GeometryModel: signalCount failed, spyNodeCleanup"); + QVERIFY2(spyChanged.count() == 1, "GeometryModel: signalCount failed, spyChanged"); + // : do operation + } + // ==> root -> line42 + // -> group -> line815 + + + + // + // createGroup + // + // do operation : + qWarning() << "createGroup..."; + spyTypeError.clear();spyAttributeError.clear();spyModelError.clear();spyUserDataCleanup.clear();spyNodeCleanup.clear();spyChanged.clear(); + GeometryModel::Node && group = geoModelW->createGroup( GeometryModel::NodeList() ); + QVERIFY2(spyTypeError.count() == 0, "GeometryModel: signalCount failed, spyTypeError"); + QVERIFY2(spyAttributeError.count() == 0, "GeometryModel: signalCount failed, spyAttributeError"); + QVERIFY2(spyModelError.count() == 0, "GeometryModel: signalCount failed, spyModelError"); + QVERIFY2(spyUserDataCleanup.count() == 0, "GeometryModel: signalCount failed, spyUserDataCleanup"); + QVERIFY2(spyNodeCleanup.count() == 0, "GeometryModel: signalCount failed, spyNodeCleanup"); + QVERIFY2(spyChanged.count() == 1, "GeometryModel: signalCount failed, spyChanged"); + // : do operation + + + // + // createGroup + // + { + // do operation : + qWarning() << "deleteGroup..."; + spyTypeError.clear();spyAttributeError.clear();spyModelError.clear();spyUserDataCleanup.clear();spyNodeCleanup.clear();spyChanged.clear(); + geoModelW -> deleteGroup( group ); + QVERIFY2(spyTypeError.count() == 0, "GeometryModel: signalCount failed, spyTypeError"); + QVERIFY2(spyAttributeError.count() == 0, "GeometryModel: signalCount failed, spyAttributeError"); + QVERIFY2(spyModelError.count() == 0, "GeometryModel: signalCount failed, spyModelError"); + QVERIFY2(spyUserDataCleanup.count() == 0, "GeometryModel: signalCount failed, spyUserDataCleanup"); + QVERIFY2(spyNodeCleanup.count() == 0, "GeometryModel: signalCount failed, spyNodeCleanup"); + QVERIFY2(spyChanged.count() == 1, "GeometryModel: signalCount failed, spyChanged"); + // : do operation + } + + qDebug() << geoModelW->toByteArray(); + delete geoModelW; + +} // GeoModelUnitTest::checkSignals2 + + +void GeoModelUnitTest::checkCoordinatePrecision() +{ + GeometryModel * geoModel = GeometryModelFactory::singleton()->create(); + QList vertexList; + QList> attrList; + QPointF p1( 0.00913852259360125798040230648137, 0.92502450355699467576955610729896 ); + QPointF p2( 0.00913852261853453300889272900885, 0.92502450357271836272445997736134 ); // distance to p1 is 0.0001 mm in geographic coordinates + + vertexList << p1 << p2; + + QVERIFY2( (geoModel != nullptr), "Creating empty GeometryModel failed." ); + + const GeometryModel::Node & node = geoModel->addPrimitive( GeometryModel::PrimitiveType::Line, vertexList, attrList ); + + vertexList = geoModel->getPrimitivePoints( node ); // read coordinates back from model + + QVERIFY2((vertexList.size() == 2), "Unexpected size of coordinate list after read." ); + + const QPointF & v1 = vertexList[0]; + const QPointF & v2 = vertexList[1]; + + QVERIFY2(((p1 == v1) && (p2 == v2) && (v1 != v2)), "Precision test failed." ); + +} // GeoModelUnitTest::checkCoordinatePrecision + + +void GeoModelUnitTest::createModelWithLine() +{ + GeometryModel * geoModelW = GeometryModelFactory::singleton()->create(); + QList vertexListW; + QList> attrListW; + + attrListW += QPair(GeometryModel::Attribute::Color,QVariant(QColor("red"))); + + vertexListW << QPointF(1, 1) << QPointF(2,2); + + QVERIFY2( (geoModelW != nullptr), "Creating empty GeometryModel failed."); + + const GeometryModel::Node & nodeWP = geoModelW->addPrimitive( GeometryModel::PrimitiveType::Line, vertexListW, attrListW ); + + geoModelW->setUserData( nodeWP, 4711 ); + + const QByteArray stringRep = geoModelW->toByteArrayDebug(); // qDebug() << stringRep; + + const GeometryModel::Node & nodeW = geoModelW->getRootNode(); + GeometryModel::PrimitiveType nodeTypeW = geoModelW->getType( nodeW ); + + // + // transfer via string: + // + + GeometryModel * geoModelR = GeometryModelFactory::singleton()->create(); + + QVERIFY2( (GeometryModel::PrimitiveType::RootNode == nodeTypeW), "Unexpected root node type."); + QVERIFY2( (geoModelR != nullptr) , "Creating second GeometryModel failed."); + QVERIFY2( geoModelR->fromByteArrayDebug(stringRep) , "Building GeometryModel from String failed." ); + + const GeometryModel::Node & nodeR = geoModelR->getRootNode(); + GeometryModel::PrimitiveType nodeTypeR = geoModelR->getType( nodeR ); + + QVERIFY2( (nodeTypeR == nodeTypeW) , "Second GeometryModel differs from first one."); + + const GeometryModel::NodeList & nodeList = geoModelR->getGroupPrimitives( nodeR ); + + QVERIFY2( nodeList.size() == 1 , "Unexpected size of root node list."); + + const GeometryModel::Node & nodeRP = nodeList.first(); + + QVERIFY2( (geoModelR->getType(nodeRP) == GeometryModel::PrimitiveType::Line), "Unexpected primitive type."); + + const QList & vertexListR = geoModelR->getPrimitivePoints(nodeRP); + + QVERIFY2( geoModelR->hasAttribute(nodeRP,GeometryModel::Attribute::Color), "Attribute color is missing." ); + + QColor color = geoModelR->getAttribute(nodeRP,GeometryModel::Attribute::Color); + + QVERIFY2( color == QColor("red") , "Attribute color has wrong value." ); + QVERIFY2( geoModelR->getUserData(nodeRP) == 4711, "UserData has wrong value." ); + + QVERIFY2( ((vertexListR[0] == QPointF(1,1)) && (vertexListR[1] == QPointF(2,2))), "Wrong coordinate values." ); + QVERIFY2( nodeW.hasChildNodes() , "Wrong return value of Node::hasChildNodes()" ); + + const GeometryModel::Node & lineNode = nodeW.firstChildNode(); + + QVERIFY2( geoModelR->getUserData(lineNode) == 4711, "Wrong return value of Node::firstChildNode()." ); + + delete geoModelW; + delete geoModelR; +} // GeoModelUnitTest::createModelWithLine() + + +void GeoModelUnitTest::createModelWithGroup() +{ + + // create Reference model (...W) + GeometryModel * geoModelW = GeometryModelFactory::singleton()->create(); + QList vertexListW; + QList> attrListW; + + attrListW += QPair(GeometryModel::Attribute::Color,QVariant(QColor("red"))); + vertexListW << QPointF(1, 1) << QPointF(2,2); + + geoModelW->addPrimitive( GeometryModel::PrimitiveType::Line, vertexListW, attrListW, 42 ); + // ==> root -> line42 + + const GeometryModel::Node & rootNode = geoModelW->getRootNode(); + const GeometryModel::Node & nodeWP = geoModelW->addPrimitive( GeometryModel::PrimitiveType::Line, vertexListW, attrListW, 815 ); + GeometryModel::Node && groupWP = geoModelW->createGroup( GeometryModel::NodeList() ); + // ==> root -> line42 + // -> line815 + // -> group + + QVERIFY2( groupWP.parentNode() == rootNode, "Group parent is not the root node." ); + + geoModelW -> movePrimitiveIntoGroup( nodeWP, groupWP ); + // ==> root -> line42 + // -> group -> line815 + + const QByteArray stringRep = geoModelW->toByteArrayDebug(); qDebug() << stringRep; + + delete geoModelW; + + // + // Rebuild model + // + { + GeometryModel * geoModelRebuilded = GeometryModelFactory::singleton()->create(); + QVERIFY2( geoModelRebuilded->fromByteArrayDebug(stringRep), "Building GeometryModel from String failed." ); + + qDebug() << geoModelRebuilded->toByteArray(); + + // compare string: + QVERIFY2( (stringRep == geoModelRebuilded->toByteArrayDebug()), "GeometryModel stringCompare failed." ); + + + // Find primitive: + const GeometryModel::Node & lineNode42 = geoModelRebuilded -> findPrimitive( 42 ); + QVERIFY2( lineNode42.isValid(), "GeometryModel::findPrimitive failed." ); + + const GeometryModel::Node & lineNode815 = geoModelRebuilded -> findPrimitive( 815 ); + QVERIFY2( lineNode815.isValid(), "GeometryModel::findPrimitive failed." ); + + + // check isInGroup: + const GeometryModel::Node & groupNode = geoModelRebuilded->isInGroup( lineNode815 ); + QVERIFY2( groupNode.isValid(), "GeometryModel::isInGroup failed." ); + + const GeometryModel::Node & groupNode42 = geoModelRebuilded -> isInGroup( lineNode42 ); + QVERIFY2( groupNode42.isValid() == false, "GeometryModel::isInGroup failed." ); + + // check isRootNode: + + QVERIFY2( (groupNode42.isRootNode() == false) , "GeometryModel::Node::isRootNode for id=42 returns wrong value" ); + + QVERIFY2( (!geoModelRebuilded->isInGroup(lineNode815).isRootNode()), "GeometryModel::Node::isRootNode for id=815 returns wrong value" ); + + delete geoModelRebuilded; + } + +} // GeoModelUnitTest::createModelWithGroup + + +void GeoModelUnitTest::createAndDeletePrimitives() +{ + GeometryModel * geoModel = GeometryModelFactory::singleton()->create(); + if (geoModel == nullptr) return; + + const GeometryModel::Node & rootNode = geoModel->getRootNode(); + QList vertexList; + QList> attrList; + + attrList += QPair(GeometryModel::Attribute::Color,QVariant(QColor("red"))); + + vertexList << QPointF(1, 1) << QPointF(2,2); + + geoModel->addPrimitive( GeometryModel::PrimitiveType::Line, vertexList, attrList, 42 ); + + // qDebug() << geoModel->toString(); + + const GeometryModel::Node & group = geoModel->createGroup( GeometryModel::NodeList() ); + + geoModel->setUserData( group, 8888 ); + + QVERIFY2( group.parentNode() == rootNode , "Group parent is not the root node." ); + QVERIFY2( !geoModel->findPrimitive(8888).isValid(), "findPrimitive returns group node" ); + + const GeometryModel::Node & lineNode = geoModel->findPrimitive(42); + QVERIFY2( lineNode.isValid(), "findPrimitive failed to find primitive" ); + + geoModel->movePrimitiveIntoGroup( lineNode, group ); + + geoModel->enableUndo( true ); + geoModel->startUndoBlock(); + geoModel->removePrimitive( lineNode ); + geoModel->endUndoBlock( QLatin1String("Remove Primitive") ); + + QVERIFY2( !geoModel->findPrimitive(42).isValid(), "deleted primitive found." ); + +} // GeoModelUnitTest::createAndDeletePrimitives + + +void GeoModelUnitTest::createAndDeleteGroups() +{ + GeometryModel * geoModel = GeometryModelFactory::singleton()->create(); + if (geoModel == nullptr) return; + + const QByteArray & modelXML = geoModel->toByteArray(); + + const GeometryModel::Node & rootNode = geoModel->getRootNode(); + QList vertexList; + QList> attrList; + + attrList += QPair(GeometryModel::Attribute::Color,QVariant(QColor("red"))); + + vertexList << QPointF(1, 1) << QPointF(2,2); + + geoModel->addPrimitive( GeometryModel::PrimitiveType::Line, vertexList, attrList, 42 ); + + // qDebug() << geoModel->toString(); + + const GeometryModel::Node & group = geoModel->createGroup( GeometryModel::NodeList() ); + + geoModel->setUserData( group, 8888 ); + + QVERIFY2( group.parentNode() == rootNode , "Group parent is not the root node." ); + QVERIFY2( !geoModel->findPrimitive(8888).isValid(), "findPrimitive returns group node" ); + + const GeometryModel::Node & lineNode = geoModel->findPrimitive(42); + QVERIFY2( lineNode.isValid(), "findPrimitive failed to find primitive" ); + + geoModel->movePrimitiveIntoGroup( lineNode, group ); + + geoModel -> deleteGroup( group ); + + // qDebug() << geoModel->toString(); + + QVERIFY2( geoModel->toByteArray() == modelXML, "delete group failed." ); + +} // GeoModelUnitTest::createAndDeleteGroups + + +void GeoModelUnitTest::checkUndo() +{ + GeometryModel * geoModel = GeometryModelFactory::singleton()->create(); + if (geoModel == nullptr) return; + + geoModel->enableUndo( true ); + + const QByteArray geoModelClean = geoModel->toByteArray(); + + QList vertexList; + QList> attrList; + + attrList += QPair(GeometryModel::Attribute::Color,QVariant(QColor("red"))); + + vertexList << QPointF(1, 1) << QPointF(2,2); + + geoModel->addPrimitive( GeometryModel::PrimitiveType::Line, vertexList, attrList, 42 ); + + GeometryModel::Node group = geoModel->createGroup( GeometryModel::NodeList() ); + const GeometryModel::Node & lineNode = geoModel->findPrimitive(42); + + QVERIFY2( group.isValid() , "Group node isn't valid." ); + QVERIFY2( lineNode.isValid(), "Line node isn't valid." ); + + geoModel->movePrimitiveIntoGroup( lineNode, group ); + + const QByteArray geoModelXml = geoModel->toByteArray(); + + geoModel->startUndoBlock(); + geoModel->removePrimitive( lineNode ); + geoModel->endUndoBlock( QLatin1String("Remove Primitive") ); + + geoModel->undo(); + + group = geoModel->isInGroup( geoModel->findPrimitive(42) ); + + QVERIFY2( geoModel->toByteArray() == geoModelXml, "Undo doesn't restore deleted nodes." ); + QVERIFY2( group.isValid() , "Group node isn't valid after undo." ); + + geoModel -> deleteGroup( group ); + + // qDebug() << geoModelClean; + // qDebug() << geoModel->toString(); + + QVERIFY2( geoModel->toByteArray() == geoModelClean, "Delete group failed." ); + +} // GeoModelUnitTest::checkUndo + + +void GeoModelUnitTest::checkMatrixTransform() +{ + GeometryModel * geoModel = GeometryModelFactory::singleton()->create(); + if (geoModel == nullptr) return; + + geoModel->enableUndo( true ); + + QList vertexList; + QList> attrList; + + attrList += QPair(GeometryModel::Attribute::Color,QVariant(QColor("red"))); + + vertexList << QPointF(1, 1) << QPointF(2,2); + + const GeometryModel::Node & lineNode = geoModel->addPrimitive( GeometryModel::PrimitiveType::Line, vertexList, attrList, 42 ); + const GeometryModel::Node & group = geoModel->createGroup( GeometryModel::NodeList() ); + + geoModel->movePrimitiveIntoGroup( lineNode, group ); + + geoModel->startUndoBlock(); + geoModel->translateNode( group, 500, 1000 ); + vertexList = geoModel->getPrimitivePoints( lineNode ); + QVERIFY2( ((vertexList[0].x() == 501) && (vertexList[0].y() == 1001) && (vertexList[1].x() == 502) && (vertexList[1].y() == 1002)), "TranslateNode failed." ); + + geoModel->scaleNode( group, 0.5, -0.5 ); + geoModel->scaleNode( group, 4 , 4 ); + + vertexList = geoModel->getPrimitivePoints( lineNode ); + + QVERIFY2( ((vertexList[0].x() == 500.5) && (vertexList[0].y() == 1002.5) && (vertexList[1].x() == 502.5) && (vertexList[1].y() == 1000.5)), "ScaleNode failed." ); + + geoModel->rotateNode(group,90); + + vertexList = geoModel->getPrimitivePoints( lineNode ); + + QVERIFY2( ((vertexList[0].x() == 500.5) && (vertexList[0].y() == 1000.5) && (vertexList[1].x() == 502.5) && (vertexList[1].y() == 1002.5)), "RotateNode failed." ); + + geoModel->endUndoBlock( QLatin1String("Move and scale primitive node") ); + +} // GeoModelUnitTest::checkMatrixTransform + + +void GeoModelUnitTest::checkMerge() +{ + GeometryModel * geoModel = GeometryModelFactory::singleton()->create(); + if (geoModel == nullptr) return; + + geoModel->enableUndo( true ); + + QList vertexList; + QList> attrList; + + attrList += QPair(GeometryModel::Attribute::Color,QVariant(QColor("red"))); + + vertexList << QPointF(1, 1) << QPointF(2,2); + + const GeometryModel::Node & lineNode = geoModel->addPrimitive( GeometryModel::PrimitiveType::Line, vertexList, attrList, 42 ); + const GeometryModel::Node & group = geoModel->createGroup( GeometryModel::NodeList() ); + + geoModel->movePrimitiveIntoGroup( lineNode, group ); + + GeometryModel * geoModel2Merge = GeometryModelFactory::singleton()->create(); + if (geoModel2Merge == nullptr) return; + + geoModel2Merge->enableUndo( true ); + + geoModel2Merge->movePrimitiveIntoGroup( geoModel2Merge->addPrimitive( GeometryModel::PrimitiveType::Line, vertexList, attrList, 815 ) + , geoModel2Merge->createGroup( GeometryModel::NodeList() ) ); + + geoModel->merge( geoModel2Merge->getRootNode() ); + + // qDebug() << geoModel->toString(); + + QVERIFY2( geoModel->findPrimitive(42).isValid(), "Primitive node (id=42) is missing." ); + QVERIFY2( geoModel->findPrimitive(815).isValid(), "Primitive node (id=815) is missing." ); + +} // GeoModelUnitTest::checkMerge + + +void GeoModelUnitTest::checkDictionarySymbol() +{ + GeometryModel * geoModel = GeometryModelFactory::singleton()->create(); + if (geoModel == nullptr) return; + + geoModel->enableUndo( true ); + + QList vertexList; + QList> attrList; + + attrList += QPair(GeometryModel::Attribute::SIDC,QVariant(QLatin1String("GFMPOGL---"))); + + vertexList << QPointF(0, 1) << QPointF(20,1) << QPointF(20,5); + geoModel->addPrimitive( GeometryModel::PrimitiveType::DictionarySymbol, vertexList, attrList, 42 ); + + const GeometryModel::Node & symbolNode = geoModel->findPrimitive(42); + vertexList = geoModel->getPrimitivePoints( symbolNode ); + + // qDebug() << geoModel->toString(); + + QVERIFY2( symbolNode.isValid() , "DictionarySymbol node isn't valid." ); + const QString attr = geoModel->getAttribute(symbolNode, GeometryModel::Attribute::SIDC).toString(); + QVERIFY2( attr == QLatin1String("GFMPOGL---"), "Wrong SIDC at dictionary symbol." ); + QVERIFY2( vertexList.size() == 3 , "Wrong size of primitive point list." ); + + QVERIFY2(vertexList[0] == QPointF( 0, 1), "Wrong coordinate." ); + QVERIFY2(vertexList[1] == QPointF(20, 1), "Wrong coordinate." ); + QVERIFY2(vertexList[2] == QPointF(20, 5), "Wrong coordinate." ); +} + +void GeoModelUnitTest::checkPixelTextBoxData() +{ + // toJsonStr: + SituationMap::PixelTextBoxData inData; + inData.m_htmlText = QStringLiteral("meinText");; + inData.m_backgroundColor = QColor(Qt::green); + inData.m_position = QPoint(11, 22); + inData.m_size = QSize(555, 666); + + QString jsonStr = inData.toJsonStr(); + qCritical() << " jsonStr: '" << jsonStr << "'" << " size=" << jsonStr.size(); + + QVERIFY2(jsonStr.size() == 184, "Error at pixelTextBoxToJson: wrong size "); + + // fromJsonStr: + { + PixelTextBoxData read = PixelTextBoxData::fromJsonStr(jsonStr); + QVERIFY2(read.m_htmlText == inData.m_htmlText , "Error at pixelTextBoxFromJson: htmlText "); + QVERIFY2(read.m_backgroundColor == inData.m_backgroundColor, "Error at pixelTextBoxFromJson: backgroundColor "); + QVERIFY2(read.m_position == inData.m_position, "Error at pixelTextBoxFromJson: position "); + QVERIFY2(read.m_size == inData.m_size, "Error at pixelTextBoxFromJson: m_size "); + + qDebug() << read.m_position; + qDebug() << read.m_size; + } +} + +void GeoModelUnitTest::checkPixelTextBoxDataCont() +{ + SituationMap::PixelTextBoxData inData1(QStringLiteral("meinText11"), QColor(Qt::red)); + SituationMap::PixelTextBoxData inData2(QStringLiteral("meinText22"), QColor(Qt::green)); + + QVector vec = {inData1, inData2}; + + // debug + const QJsonObject &jObj = PixelTextBoxData::pixelTextBoxContainerToJson(vec); + qDebug() << " pixelTextBoxContainerToJson =" << QJsonDocument(jObj).toJson().toStdString().c_str(); + + // toJson/fromJson: + { + QVector read = PixelTextBoxData::pixelTextBoxContainerFromJson(jObj); + qCritical() << " pixelTextBoxContainerFromJson: read.size() =" << read.size(); + QVERIFY2(read.size() == 2 , "Error at pixelTextBoxContainerFromJson: size"); + QVERIFY2(read.first() == inData1, "Error at pixelTextBoxContainerFromJson: inData1"); + } + + // toJson/fromJson STR: + { + const QString &strRef = PixelTextBoxData::pixelTextBoxContainerToJsonStr(vec, QJsonDocument::JsonFormat::Compact); + qCritical() << " pixelTextBoxContainerToJsonStr Compact: " << strRef; + { + QVector read = PixelTextBoxData::pixelTextBoxContainerFromJsonStr(strRef); + qCritical() << " pixelTextBoxContainerFromJsonStr: read.size() =" << read.size(); + + QVERIFY2(read.size() == 2 , "Error at pixelTextBoxContainerFromJsonStr: size"); + QVERIFY2(read.first() == inData1, "Error at pixelTextBoxContainerFromJsonStr: inData1"); + } + } +} + +void GeoModelUnitTest::checkMetaData() +{ + // test GeometryModel::Attributes + // introduced for storing "MetaPixelBasedTextBoxes" (multiple): + + GeometryModel * geoModel = GeometryModelFactory::singleton()->create(); + if (geoModel == nullptr) return; + + geoModel->enableUndo(true); + + const QString &metaData = QStringLiteral("MyMetaDataString"); + + QVector attCont = + { + GeometryModel::Attribute::MetaDataTitle, + GeometryModel::Attribute::MetaDataAuthor, + GeometryModel::Attribute::MetaDataDate, + GeometryModel::Attribute::MetaDataDesciption, + }; + + for (auto att : attCont) + { + geoModel->setModelAttribute(att, metaData); + { + const QString get = geoModel->getModelAttribute(att).toString(); + QVERIFY2(get == metaData, "set get mismatch"); + } + geoModel->setModelAttribute(att, {}); + } + + SituationMap::GeometryModel::Attribute att; + { + att = GeometryModel::Attribute::MetaDataMinScale; + geoModel->setModelAttribute(att, 1234); + const int get = geoModel->getModelAttribute(att).toInt(); + QVERIFY2(get == 1234, "set get mismatch MetaDataMinScale"); + } + { + att = GeometryModel::Attribute::MetaDataMaxScale; + geoModel->setModelAttribute(att, 123456); + const int get = geoModel->getModelAttribute(att).toInt(); + QVERIFY2(get == 123456, "set get mismatch MetaDataMaxScale"); + } + + // GeometryModel::Attribute::MetaPixelBasedTextBoxes; + { + const QString dummyStrIn = QStringLiteral("setModelAttribute(GeometryModel::Attribute::MetaPixelBasedTextBoxes, dummyStrIn); + + { + const QString read = geoModel->getModelAttribute(GeometryModel::Attribute::MetaPixelBasedTextBoxes).toString(); + QVERIFY2(dummyStrIn == read, "Modelerror set/getPixelTextData"); + } + } +} + + +int main(int argc, char *argv[]) +{ + NGLCoreApplication app(argc, argv); + app.createLogger("GeometryModelUnitTest", "log4cxxGeometryModelUnitTest.props"); + + QSharedPointer tc = QSharedPointer( new GeoModelUnitTest() ); + int retCode = QTest::qExec(tc.data(), argc, argv); + + return retCode; +} // main(int argc, char *argv[]) diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTestsGeoModel/unit_test.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTestsGeoModel/unit_test.h new file mode 100755 index 000000000..36345d54c --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/UnitTestsGeoModel/unit_test.h @@ -0,0 +1,66 @@ +/** + * @RDE_HEADER@ + * Unittest + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include +#include + +#include + + + +class CheckSignal : public QObject +{ + Q_OBJECT + + public: + bool isConnected(); + + signals: + void mySignal(); +}; // CheckSignal + + + +class GeoModelUnitTest : public QObject +{ + Q_OBJECT + + public: + + GeoModelUnitTest(); + ~GeoModelUnitTest() = default; + + private: + + SituationMap::LOGGER_T m_logger; + + private Q_SLOTS: + + void init(); + + void checkSignals1(); + void checkSignals2(); + void checkCoordinatePrecision(); + void createModelWithLine(); + void createModelWithGroup(); + void createAndDeletePrimitives(); + void createAndDeleteGroups(); + void checkUndo(); + void checkMatrixTransform(); + void checkMerge(); + void checkDictionarySymbol(); + + void checkPixelTextBoxData(); + void checkPixelTextBoxDataCont(); + void checkMetaData(); + + +}; // GeoModelUnitTest + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/mouse_move_overlay.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/mouse_move_overlay.cpp new file mode 100755 index 000000000..0f5e4f808 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/mouse_move_overlay.cpp @@ -0,0 +1,102 @@ +/** + * @RDE_HEADER@ + * MouseMoveOverlay + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#include "mouse_move_overlay.h" + +// Own +#include + +// ESRI +#include +#include +#include + +// Qt +#include +#include + + +namespace SituationMap +{ + +class MouseMoveOverlay::Private +{ +public: + Private() = default; + ~Private() = default; + + // ESRI data: + EsriRuntimeQt::Map *m_map = nullptr; + + // OWN data: + SituationMap::MapImpl *m_mapImpl = nullptr; + +}; // class MouseMoveOverlay::Private +//----------------------------------------------------------------------------- + + + + +MouseMoveOverlay::MouseMoveOverlay(MapImpl *mapImpl) + : QGraphicsItem() + , d(new Private) +{ + qDebug() << LOGFUNCTION; + + // object to be called: + d->m_mapImpl = mapImpl; + + if (d->m_mapImpl) + d->m_map = d->m_mapImpl->getEsriMap(); + + setAcceptHoverEvents(true); +} + +MouseMoveOverlay::~MouseMoveOverlay() +{ + qDebug() << LOGFUNCTION; + delete d; +} + +QRectF MouseMoveOverlay::boundingRect() const +{ + if (d->m_map) + return QRect(0, 0, d->m_map->width(), d->m_map->height()); + return QRect(); // fallback +} + +void MouseMoveOverlay::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + // No need to paint + Q_UNUSED(painter) + Q_UNUSED(option) + Q_UNUSED(widget) +} + + +void MouseMoveOverlay::hoverMoveEvent(QGraphicsSceneHoverEvent *event) +{ +// qDebug() << LOGFUNCTION; + + if (d->m_map) + { + if (!d->m_map->isReady()) + return; + + QPointF scenePoint = event->scenePos(); + int scenePointX = scenePoint.x(); + int scenePointY = scenePoint.y(); + if (scenePointX < 0 || scenePointY < 0) + return; + + d->m_mapImpl->onHandleMouseMove(scenePointX, scenePointY); + } +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/mouse_move_overlay.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/mouse_move_overlay.h new file mode 100755 index 000000000..2cdc0d3dc --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/mouse_move_overlay.h @@ -0,0 +1,57 @@ +/** + * @RDE_HEADER@ + * MouseMoveOverlay + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + +#include + +namespace EsriRuntimeQt +{ +class MapGraphicsView; +} + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +class MapImpl; + +class MouseMoveOverlay : public QGraphicsItem +{ + // Q_OBJECT +public: + + explicit MouseMoveOverlay(MapImpl *mapImpl); + virtual ~MouseMoveOverlay(); + +signals: + // void mousePositionChanged(const QPointF& screenPos, QPointF& mapPos); + // void mousePositionChanged(); + +protected: + QRectF boundingRect() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + void hoverMoveEvent(QGraphicsSceneHoverEvent *event); + +private : + + MouseMoveOverlay(const MouseMoveOverlay &other) = delete; + MouseMoveOverlay &operator=(const MouseMoveOverlay &other) = delete; + + class Private; + /** + * The private d-pointer to not break BC when changing internals of this class + */ + Private *d = nullptr; +}; + + + +} // namespace SituationMap + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/movement_track.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/movement_track.cpp new file mode 100755 index 000000000..4ed27702e --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/movement_track.cpp @@ -0,0 +1,268 @@ +/** + * @RDE_HEADER@ + * Class MovementTrack + * $Id$ + * $HeadURL$ + * $Author$ + */ + +// Own +#include "movement_track.h" + +#include + +// Qt +#include +#include +#include + + +namespace SituationMap +{ +int MovementTrack::m_count = 0; + +MovementTrack::MovementTrack(int trackId) +// : m_logger(getLogger("MovementTrack", trackId)) + : m_logger(getLogger("MovementTrack")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" + << " trackId/participantId=" << trackId + << " m_count=" << m_count; + ++m_count; + + m_trackId = trackId; + m_active = true; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " m_trackId=" << m_trackId << " m_count=" << m_count; +} + +MovementTrack::~MovementTrack() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " m_trackId=" << m_trackId << " m_count=" << m_count; + --m_count; + TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " m_trackId=" << m_trackId << " m_count=" << m_count; +} + +void MovementTrack::setActive(bool active) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START " << " active=" << active << " m_count=" << m_count; + m_active = active; +} + +bool MovementTrack::addPosition(const COORD_T &newPosition, + qint64 currentTimeMsec, double minDistance, int maxNumberOfPositions) +{ + DEBUG_LOG(m_logger) << LOGFUNCTION << " START" + << " currentTimeMsec=" << currentTimeMsec + << " newPosition=" << newPosition + << " #ofCoords=" << m_cont.size(); + bool success = false; + + // Very first access? + if (m_cont.isEmpty()) + { + m_cont.insert(currentTimeMsec, newPosition); + success = true; + } + else + { + // Avoid identical values: + const COORD_T &lastPosition = m_cont.last(); + + // isEmpty already checked: + if (lastPosition != newPosition) + { + const double distance = LtsCoordTrans::distance(lastPosition.x(), lastPosition.y(), + newPosition.x(), newPosition.y()); + DEBUG_LOG(m_logger) << LOGFUNCTION << " distance " << distance; + + if (distance > minDistance) + { + m_cont.insert(currentTimeMsec, newPosition); + success = true; + } + } // if (lastPosition != newPosition) + } + + if (!m_cont.isEmpty() && + (m_cont.size() > maxNumberOfPositions) && (maxNumberOfPositions > 0)) + { + // see QTBUG-59373 + m_cont.erase(m_cont.begin()); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END " << "success=" << success << " #ofCoords=" << m_cont.size(); + return success; +} + +bool MovementTrack::removeEntriesNewer(qint64 currentTimeMsec) +{ +// TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " m_active=" << m_active << " m_count=" << m_count; + + bool modified = false; + QMapIterator mapIt(m_cont); + mapIt.toBack(); + + while (mapIt.hasPrevious()) + { + mapIt.previous(); + if (mapIt.key() > currentTimeMsec) + { + m_cont.remove(mapIt.key()); + modified = true; + } + else + break; + } // while + +// TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " m_active=" << m_active << " m_count=" << m_count << " modified=" << modified; + return modified; +} + +bool MovementTrack::removeEntriesOlder(qint64 currentTimeMsec, qint64 maxAgeMsec) +{ +// TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " m_active=" << m_active << " m_count=" << m_count << " currentTimeMsec=" << currentTimeMsec << " maxAgeMsec=" << maxAgeMsec; + + bool modified = false; + auto it = m_cont.begin(); + while (it != m_cont.end()) + { + const KEY_T &ageMsec = currentTimeMsec - it.key(); + if (ageMsec > maxAgeMsec) + { + it = m_cont.erase(it); + modified = true; + } + else + ++it; + } // while + +// TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " m_active=" << m_active << " m_count=" << m_count << " modified=" << modified; + return modified; +} + +QVector MovementTrack::getTrack() const +{ + QVector ret; + ret.reserve(m_cont.size()); + for (const auto &pos : m_cont) + ret.push_back(pos); + return ret; +} + +QVector MovementTrack::getVisibleTrack(qint64 currentTimeMsec, + qint64 maxAgeMsec, + qint64 maxViewAgeMsec) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " m_trackId=" << m_trackId << " currentTimeMsec=" << currentTimeMsec << " maxAgeMsec=" << maxAgeMsec << " maxViewAgeMsec=" << maxViewAgeMsec; + + + removeEntriesOlder(currentTimeMsec, maxAgeMsec); + + // Auch wenn keine neuen Positionen eingegangen sind, + // hat sich die Spur evtl. wegen der modifizierten Zeit 'currentTimeMsec' + // geändert: + // uebertragen in validCoords + + QVector validCoords; + validCoords.reserve(m_cont.size()); + for (auto it = m_cont.cbegin(); it != m_cont.cend(); ++it) + { + const KEY_T &ageMsec = currentTimeMsec - it.key(); + if (ageMsec > maxViewAgeMsec && maxViewAgeMsec > 0) + continue; + + if (ageMsec < 0) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " age is negative!!! - " + << " currentTimeMsec=" << currentTimeMsec + << " m_trackId=" << m_trackId + << " ageMsec=" << ageMsec + << " - " + << " size = " << m_cont.size() + << ", dis = " << std::distance(m_cont.cbegin(), it); + if (ageMsec < -1500) + WARNING_LOG(m_logger) << LOGFUNCTION << " age is negative!!! - " + << " currentTimeMsec=" << currentTimeMsec + << " m_trackId=" << m_trackId + << " ageMsec=" << ageMsec + << " - " + << " size = " << m_cont.size() + << ", dis = " << std::distance(m_cont.cbegin(), it); + + } + + DEBUG_LOG(m_logger) << LOGFUNCTION << " m_trackId=" << m_trackId << " ageMsec=" << ageMsec << " coord=" << it.value(); + validCoords.push_back(it.value()); + } // for + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " validCoords.size()/m_cont.size(): " << validCoords.size() << " / " << m_cont.size(); + + return validCoords; +} + +QSharedPointer MovementTrack::read(const QJsonObject &json) +{ + if (!json.contains(QLatin1String("trackId"))) + return nullptr; + + int trackId = -1; + if (json.contains(QLatin1String("trackId"))) + trackId = json[QLatin1String("trackId")].toInt(); + + if (trackId < 0) + return nullptr; + + QSharedPointer mt = + QSharedPointer(new MovementTrack(trackId)); + + // active + if (json.contains(QLatin1String("active"))) + mt->setActive(json[QLatin1String("active")].toBool()); + + // coordinates + QJsonArray jArray = json[QLatin1String("coordinates")].toArray(); + for (int idx = 0; idx < jArray.size(); ++idx) + { + const QJsonObject &objTemp = jArray[idx].toObject(); + const KEY_T &timeMsec = objTemp[QLatin1String("pointInTimeMsec")].toDouble(); // toDouble!!! + + Point3D p3D; + p3D.rx() = objTemp[QLatin1String("loc_x_lon")].toDouble(); + p3D.ry() = objTemp[QLatin1String("loc_y_lat")].toDouble(); + if (json.contains(QLatin1String("loc_z"))) + p3D.rz() = objTemp[QLatin1String("loc_z")].toDouble(); + mt->addPosition(p3D, timeMsec, 0, 0); + } // for + + return mt; +} + +QJsonObject MovementTrack::toJsonObject() const +{ + QJsonObject json; + + json[QLatin1String("trackId")] = m_trackId; + json[QLatin1String("active")] = m_active; + + QJsonArray jArray; + QMapIterator it(m_cont); + while (it.hasNext()) + { + it.next(); + const KEY_T &key = it.key(); + const COORD_T &coord = it.value(); + + QJsonObject jsonCoord; + jsonCoord[QLatin1String("loc_x_lon")] = coord.x(); + jsonCoord[QLatin1String("loc_y_lat")] = coord.y(); + jsonCoord[QLatin1String("loc_z")] = coord.z(); + jsonCoord[QLatin1String("pointInTimeMsec")] = key; + jArray.append(jsonCoord); + } + json[QLatin1String("coordinates")] = jArray; + return json; +} + + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/movement_track.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/movement_track.h new file mode 100755 index 000000000..a3566b0b8 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/movement_track.h @@ -0,0 +1,85 @@ +/** + * @RDE_HEADER@ + * Class MovementTrack + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + + +// Own +#include +#include +#include + + +// Qt +#include +#include + + +class QJsonObject; + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +class SITUATIONMAP_EXPORT MovementTrack +{ + +public: + + using KEY_T = qint64; // msec + using COORD_T = Point3D; // lon, lat + using CONT_T = QMap; // ordered by time [msec] + +public: + /// Constructor + explicit MovementTrack(int trackId); + + /// Destructor + virtual ~MovementTrack(); + + int trackId() const { return m_trackId; } + int size() const { return m_cont.size(); } + void clear() { m_cont.clear(); } + + void setActive(bool active); + bool isActive() const { return m_active; } + + bool addPosition(const COORD_T &newPosition, + qint64 currentTimeMsec, double minDistance, int maxNumberOfPositions); + + const COORD_T lastPosition() const + { + if (this->m_cont.empty()) + return COORD_T(); + return this->m_cont.last(); + } + + bool removeEntriesOlder(qint64 currentTimeMsec, qint64 maxAgeMsec); + bool removeEntriesNewer(qint64 currentTimeMsec); + + QVector getTrack() const; + QVector getVisibleTrack(qint64 currentTimeMsec, qint64 maxAgeMsec, qint64 maxViewAgeMsec); + + + static QSharedPointer read(const QJsonObject &json); + QJsonObject toJsonObject() const; + +protected: + static int m_count; + int m_trackId; // UniqueMapId + bool m_active; + + CONT_T m_cont; + + +private: + LOGGER_T m_logger; ///< Interface to the Logging-Facility +}; + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/movement_track_manager.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/movement_track_manager.cpp new file mode 100755 index 000000000..7fd79cfe1 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/movement_track_manager.cpp @@ -0,0 +1,338 @@ +/** + * @RDE_HEADER@ + * Class MovementTrackManager + * $Id$ + * $HeadURL$ + * $Author$ + */ + + +// Own +#include "movement_track_manager.h" + + +// Qt +#include +#include +#include +#include + +namespace SituationMap +{ + +MovementTrackManager::MovementTrackManager(QObject *parent) + : BaseClass(parent) + , m_logger(getLogger("MovementTrackManager")) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + init(); + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +MovementTrackManager::~MovementTrackManager() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " size=" << m_cont.size(); + m_cont.clear(); + TRACE_LOG(m_logger) << LOGFUNCTION << " END "; +} + +void MovementTrackManager::init() +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + m_cont.clear(); + m_currentTimeMsec = 0; + m_maxNumberOfTracks = 10; + m_maxNumberOfPointsPerTrack = 600; // 600 + + // min sec msec + m_maxAgeMsec = 30 * 60 * 1000; // (30Min * 60 * 1000= 1 800 000sec) + m_maxViewAgeMsec = 30 * 60 * 1000; // (30Min * 60 * 1000= 1 800 000sec) + + m_minDistance = 2; // 2 Meter + + // just for testing: + // m_maxNumberOfTracks = 3; + // m_maxAgeMsec = 6000 * 60 * 1000; + // m_maxViewAgeMsec = 6000 * 60 * 1000; + // m_maxNumberOfPointsPerTrack = 10; + // m_minDistance = 0; + + TRACE_LOG(m_logger) << LOGFUNCTION << " END"; +} + +QSet MovementTrackManager::setCurrentTimeMsec(qint64 timeMsec) +{ +// TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " timeMsec=" << timeMsec; + + QSet modifiedMapObjects; + + if (m_currentTimeMsec == timeMsec) + return modifiedMapObjects; + + // forward or backward? + bool forward = timeMsec > m_currentTimeMsec; + m_currentTimeMsec = timeMsec; + + + QVectorIterator vecIt(m_cont); + while (vecIt.hasNext()) + { + DATA_T item = vecIt.next(); + + if (!item) + continue; + + bool mod; + if (forward) + mod = item->removeEntriesOlder(timeMsec, m_maxAgeMsec); + else + mod = item->removeEntriesNewer(timeMsec); // set time backward -> remove all entries whose time is too new + + if (mod) + { + int trackId = item->trackId(); + DEBUG_LOG(m_logger) << LOGFUNCTION << " Track changes, simId/trackId=" << trackId + << " (m_maxAgeMsec=" << m_maxAgeMsec << ")" + << " (m_maxViewAgeMsec=" << m_maxViewAgeMsec << ")"; + modifiedMapObjects.insert(trackId); + } + } // while + +// TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " timeMsec=" << timeMsec << " modifiedMapObjects=" << modifiedMapObjects.size(); + return modifiedMapObjects; +} + + +bool MovementTrackManager::addTrack(int trackId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " size=" << m_cont.size() << " trackId=" << trackId; + + if (hasTrack(trackId)) + return false; + + m_cont.push_back(DATA_T(new MovementTrack(trackId))); + + if (!m_cont.isEmpty() && (m_cont.size() > m_maxNumberOfTracks)) + { + const int trackIdSave = m_cont.first()->trackId(); + m_cont.pop_front(); + emit trackRemoved(trackIdSave); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END " << " size=" << m_cont.size() << " trackId=" << trackId; + return true; +} + +MovementTrackManager::DATA_T MovementTrackManager::getTrack(int trackId) const +{ + QVectorIterator vecIt(m_cont); + while (vecIt.hasNext()) + { + DATA_T item = vecIt.next(); + + if (item && item->trackId() == trackId) + return item; + } // while + + return nullptr; +} + +void MovementTrackManager::removeTrack(int trackId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " size=" << m_cont.size() << " trackId=" << trackId; + + for (int idx = m_cont.size() - 1; idx >= 0; --idx) + { + const DATA_T &item = m_cont.at(idx); + if (item && item->trackId() == trackId) + { + m_cont.removeAt(idx); + emit trackRemoved(trackId); + } + } // for + TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " size=" << m_cont.size() << " trackId=" << trackId; +} + +void MovementTrackManager::activateTrack(int trackId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " size=" << m_cont.size() << " trackId=" << trackId; + + // Eintrag finden + DATA_T track = getTrack(trackId); + + if (track) + { + if (!track->isActive()) + { + track->setActive(true); + emit trackActivityChanged(trackId, true); + } + } + else + { + addTrack(trackId); // new Track -> active + emit trackActivityChanged(trackId, true); + } + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " size=" << m_cont.size() << " trackId=" << trackId; +} + +void MovementTrackManager::deactivateTrack(int trackId) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START" << " size=" << m_cont.size() << " trackId=" << trackId; + + // 1. find track and save: + DATA_T savedTrack = getTrack(trackId); + if (!savedTrack) + return; + + if (!savedTrack->isActive()) // is already deactivated + return; + + // 2. remove track: + for (int idx = m_cont.size() - 1; idx >= 0; --idx) + { + const DATA_T &item = m_cont.at(idx); + if (item && item->trackId() == trackId) + m_cont.removeAt(idx); + } // for + + // 3. re-add savedTrack: + savedTrack->setActive(false); + m_cont.push_front(savedTrack); // front -> remove candidate / Guez: was push_back!? + + emit trackActivityChanged(trackId, false); + + TRACE_LOG(m_logger) << LOGFUNCTION << " END" << " size=" << m_cont.size() << " trackId=" << trackId; +} + +bool MovementTrackManager::addPosition(int trackId, const MovementTrack::COORD_T &coord, qint64 timeMsec) +{ + DATA_T track = getTrack(trackId); + if (track.isNull()) + return false; + return track->addPosition(coord, timeMsec, m_minDistance, m_maxNumberOfPointsPerTrack); +} + +PositionCont MovementTrackManager::activeAndVisibleTrack(int trackId) const +{ + DATA_T track = getTrack(trackId); + if (!track || !track->isActive()) + return PositionCont(); + + return track->getVisibleTrack(m_currentTimeMsec, m_maxAgeMsec, m_maxViewAgeMsec); +} + +void MovementTrackManager::emit_activeSimIdsChanged(const QVector &activeSimIds) +{ + emit activeSimIdsChanged(activeSimIds); +} + +bool MovementTrackManager::read(const QJsonObject &json) +{ + TRACE_LOG(m_logger) << LOGFUNCTION << " START"; + + this->init(); + + if (json.contains(QLatin1String("currentTimeMsec"))) + this->setCurrentTimeMsec(json[QLatin1String("currentTimeMsec")].toDouble()); // qint64 -> toDouble + if (json.contains(QLatin1String("maxAgeMsec"))) + this->setMaxAgeMsec(json[QLatin1String("maxAgeMsec")].toDouble()); // qint64 -> toDouble + if (json.contains(QLatin1String("maxViewAgeMsec"))) + this->setMaxViewAgeMsec(json[QLatin1String("maxViewAgeMsec")].toDouble()); // qint64 -> toDouble + + if (json.contains(QLatin1String("minDistance"))) + this->setMinDistance(json[QLatin1String("minDistance")].toInt()); + if (json.contains(QLatin1String("maxNumberOfTracks"))) + this->setMaxNumberOfTracks(json[QLatin1String("maxNumberOfTracks")].toInt()); + if (json.contains(QLatin1String("maxNumberOfPointsPerTrack"))) + this->setMaxNumberOfPointsPerTrack(json[QLatin1String("maxNumberOfPointsPerTrack")].toInt()); + + + // coords + const QJsonArray &jArray = json[QLatin1String("movementTracks")].toArray(); + + DEBUG_LOG(m_logger) << LOGFUNCTION << " movementTrack QJsonArray size=" << jArray.size(); + + for (int idx = 0; idx < jArray.size(); ++idx) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " movementTrack QJsonArray, idx=" << idx; + + const QJsonObject &objTemp = jArray[idx].toObject(); + const QSharedPointer &mt = MovementTrack::read(objTemp); + if (!mt) + continue; + if (mt->trackId() >= 0) + m_cont.append(mt); + + } // for + + // send signals for all tracks: + for (const QSharedPointer &mt : std::as_const(m_cont)) + { + DEBUG_LOG(m_logger) << LOGFUNCTION << " emit trackActivityChanged," + << " mt->trackId()=" << mt->trackId() + << " mt->isActive()=" << mt->isActive(); + emit trackActivityChanged(mt->trackId(), mt->isActive()); + } + + return true; +} + +QJsonObject MovementTrackManager::toJsonObject() const +{ + QJsonObject json; + + json[QLatin1String("currentTimeMsec")] = m_currentTimeMsec; + json[QLatin1String("maxAgeMsec")] = m_maxAgeMsec; + json[QLatin1String("maxViewAgeMsec")] = m_maxViewAgeMsec; + json[QLatin1String("minDistance")] = m_minDistance; + json[QLatin1String("maxNumberOfTracks")] = m_maxNumberOfTracks; + json[QLatin1String("maxNumberOfPointsPerTrack")] = m_maxNumberOfPointsPerTrack; + + QJsonArray jArray; + for (const auto &item : m_cont) + { + if (!item) + continue; + QJsonObject objTemp = item->toJsonObject(); + jArray.append(objTemp); + } + json[QLatin1String("movementTracks")] = jArray; + return json; +} + +QByteArray MovementTrackManager::toJson() const +{ + // final - toJson + QJsonDocument jDoc(toJsonObject()); + return jDoc.toJson(); +} + +QVector MovementTrackManager::activeTrackIds() const +{ + QVector trackIds; + trackIds.reserve(m_cont.size()); + + for (const auto &item : m_cont) + { + if (item && item->isActive()) + trackIds.append(item->trackId()); + } + return trackIds; +} + +QVector MovementTrackManager::deactiveTrackIds() const +{ + QVector deactiveTrackIds; + deactiveTrackIds.reserve(m_cont.size()); + + for (const auto &item : m_cont) + { + if (item && !item->isActive()) + deactiveTrackIds.append(item->trackId()); + } + return deactiveTrackIds; +} + +} // namespace GDW_CTC diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/movement_track_manager.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/movement_track_manager.h new file mode 100755 index 000000000..10363e755 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/movement_track_manager.h @@ -0,0 +1,114 @@ +/** + * @RDE_HEADER@ + * Class MovementTrackManager + * $Id$ + * $HeadURL$ + * $Author$ + */ + +#pragma once + + +// Own +#include "movement_track.h" +#include + + +// Qt +#include +#include +#include +#include + + +//---------------------------------------------------------------------------- +namespace SituationMap +{ + +class SITUATIONMAP_EXPORT MovementTrackManager : public QObject +{ + Q_OBJECT + +public: + using COORD_T = MovementTrack::COORD_T; + using BaseClass = QObject; + + /// Constructor + explicit MovementTrackManager(QObject *parent = nullptr); + + /// Destructor + virtual ~MovementTrackManager(); + + void init(); + + qint64 currentTimeMsec() { return m_currentTimeMsec; } + QSet setCurrentTimeMsec(qint64 timeMsec); + + qint64 maxAgeMsec() { return m_maxAgeMsec; } + void setMaxAgeMsec(qint64 timeMsec) { m_maxAgeMsec = timeMsec; } + + qint64 maxViewAgeMsec() { return m_maxViewAgeMsec; } + void setMaxViewAgeMsec(qint64 maxViewAgeMsec) { m_maxViewAgeMsec = maxViewAgeMsec; } + + int maxNumberOfTracks() const { return m_maxNumberOfTracks; } + void setMaxNumberOfTracks(int maxNumberOfTracks) { m_maxNumberOfTracks = maxNumberOfTracks; } + + int maxNumberOfPointsPerTrack() const { return m_maxNumberOfPointsPerTrack; } + void setMaxNumberOfPointsPerTrack(int maxPoints) { m_maxNumberOfPointsPerTrack = maxPoints; } + + int minDistance() const { return m_minDistance; } + void setMinDistance(int minDistance) { m_minDistance = minDistance; } + + + + bool addPosition(int trackId, const COORD_T &coord, qint64 timeMsec); // if track exists + void removeTrack(int trackId); + + void activateTrack(int trackId); + void deactivateTrack(int trackId); + + bool hasTrack(int trackId) const { return static_cast(getTrack(trackId)); } + int size() const { return m_cont.size(); } + + QVector activeTrackIds() const; + QVector deactiveTrackIds() const; + PositionCont activeAndVisibleTrack(int trackId) const; + + void emit_activeSimIdsChanged(const QVector &activeSimIds); + + bool read(const QJsonObject &json); + QJsonObject toJsonObject() const; + QByteArray toJson() const; + + +Q_SIGNALS: + void trackActivityChanged(int trackId, bool active); + void trackRemoved(int trackId); + + void activeSimIdsChanged(const QVector &activeSimIds); + +private: + bool addTrack(int trackId); + + using DATA_T = QSharedPointer; + using CONT_T = QVector; + + DATA_T getTrack(int trackId) const; + + +protected: + CONT_T m_cont; + + qint64 m_currentTimeMsec; + qint64 m_maxAgeMsec; + qint64 m_maxViewAgeMsec; + int m_minDistance; + int m_maxNumberOfTracks; + int m_maxNumberOfPointsPerTrack; + + +private: + LOGGER_T m_logger; ///< Interface to the Logging-Facility +}; + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/terrain_profile_dialog.cpp b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/terrain_profile_dialog.cpp new file mode 100755 index 000000000..dc2e4fd09 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/terrain_profile_dialog.cpp @@ -0,0 +1,75 @@ +/** + * @RDE_HEADER@ + * TerrainProfileDialog/ Höhendialog + * $Id: zwmsui_TerrainProfileDialog.cpp 21168 2014-10-27 07:35:54Z ehrlicher $ + * $HeadURL: svn://slssvn/TDS/branches/product/devel/vor_cleanup_zw_zs/ZW/ZWMS/ZWMSUI/zwmsui_TerrainProfileDialog.cpp $ + * $Author: ehrlicher $ + */ + +#include "terrain_profile_dialog.h" +#include "ui_terrain_profile_dialog.h" + +//#include +#include + +namespace SituationMap +{ +TerrainProfileDialog::TerrainProfileDialog(QWidget *parent, LtsWidgets::ButtonCombination buttonCombination) + : LtsWidgets::LtsDialog(parent, buttonCombination) + , m_ui(new Ui::TerrainProfile) + , m_logger(getLogger("TerrainProfileDialog")) +{ + m_ui->setupUi(this); + + connect(m_ui->heightPlotWidget, &LtsHeightPlotWidget::mousePositionChanged, this, &TerrainProfileDialog::onUpdatePosition); + + // SIGNAL-SIGNAL um die Informationen nach aussen zu tragen + connect(m_ui->heightPlotWidget, &LtsHeightPlotWidget::mousePositionChanged, this, &TerrainProfileDialog::mousePositionChanged); + connect(m_ui->heightPlotWidget, &LtsHeightPlotWidget::startLineOfSight, this, &TerrainProfileDialog::startLineOfSight); + connect(m_ui->heightPlotWidget, &LtsHeightPlotWidget::stopLineOfSight, this, &TerrainProfileDialog::stopLineOfSight); + + setOnlineHelpFilename(QStringLiteral("Situation-Start_Terrain_Profil")); +} + +TerrainProfileDialog::~TerrainProfileDialog() +{ + delete m_ui; +} + +void TerrainProfileDialog::setMap(Map *map) +{ + m_map = map; +} + +void TerrainProfileDialog::retranslate() +{ + translate(m_ui, QString(), false, tr("Terrain Profile")); +} + +void TerrainProfileDialog::setData(const QVector &data) +{ + m_ui->heightPlotWidget->setData(data); +} + +void TerrainProfileDialog::setMarks(const QVector &data) +{ + m_ui->heightPlotWidget->setMarks(data); +} + +void TerrainProfileDialog::setMarkerPen(const QPen &pen) +{ + m_ui->heightPlotWidget->setMarkerPen(pen); +} + +void TerrainProfileDialog::onUpdatePosition(const QPointF &position, double curHeight, double mouseHeight) +{ + QString str; // = LtsCoordTrans::UtmCoordinate::fromGeographic(position).mgrs(); + if (m_map) + str = m_map->mapToMgrsString(position); + + m_ui->lblPosition->setText(str); + m_ui->lblHeight->setText(QString::number(curHeight, 'f', 2)); + m_ui->lblHeightCursor->setText(QString::number(mouseHeight, 'f', 2)); +} + +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/terrain_profile_dialog.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/terrain_profile_dialog.h new file mode 100755 index 000000000..d9f6407e0 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/terrain_profile_dialog.h @@ -0,0 +1,61 @@ +/** + * @RDE_HEADER@ + * TerrainProfileDialog/ Höhendialog + * $Id: zwmsui_TerrainProfileDialog.h 4471 2010-06-15 06:56:51Z ehrlicher $ + * $HeadURL: svn://slssvn/TDS/branches/product/devel/vor_cleanup_zw_zs/ZW/ZWMS/ZWMSUI/zwmsui_TerrainProfileDialog.h $ + * $Author: ehrlicher $ + */ + +#pragma once + +#include +#include +#include + +namespace Ui +{ +class TerrainProfile; +} + +namespace QtCore +{ +class QList; +class QPair; +class QPointF; +} + +namespace SituationMap +{ +class Map; + +class SITUATIONMAP_EXPORT TerrainProfileDialog final : public LtsWidgets::LtsDialog +{ + Q_OBJECT + +public: + using heightData = QPair; + +public: + explicit TerrainProfileDialog(QWidget *parent = nullptr, LtsWidgets::ButtonCombination buttonCombination = LtsWidgets::ButtonCombination::CloseOnly); + ~TerrainProfileDialog(); + void setMap(Map *map); + + void setData(const QVector &data); + void setMarks(const QVector &data); + void setMarkerPen(const QPen &pen); + void retranslate() override; + +signals: + void mousePositionChanged(const QPointF &curPoint); + void startLineOfSight(const QPointF &startPoint); + void stopLineOfSight(); + +protected slots: + void onUpdatePosition(const QPointF &position, double curHeight, double mouseHeight); + +private: + Ui::TerrainProfile *m_ui; + Map *m_map = nullptr; + LOGGER_T m_logger; +}; +} // namespace SituationMap diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/terrain_profile_dialog.ui b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/terrain_profile_dialog.ui new file mode 100755 index 000000000..158f3bd77 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/Utils/terrain_profile_dialog.ui @@ -0,0 +1,111 @@ + + + TerrainProfile + + + + 0 + 0 + 500 + 300 + + + + Terrain Profile + + + + + + + + Position: + + + + + + + XXXXX YYYYY + + + + + + + Qt::Horizontal + + + + 17 + 20 + + + + + + + + Height: + + + + + + + xxxx m + + + + + + + Qt::Horizontal + + + + 17 + 20 + + + + + + + + Cursor height: + + + + + + + xxxx m + + + + + + + + + + 0 + 0 + + + + + + + + + LtsHeightPlotWidget + QWidget +
LtsWidgets/lts_height_plot_widget.h
+ 1 +
+
+ + +
diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/admin_scripts/CMakeLists.txt b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/admin_scripts/CMakeLists.txt new file mode 100755 index 000000000..38c3b0836 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/admin_scripts/CMakeLists.txt @@ -0,0 +1,11 @@ +# +# $Id$ +# $HeadURL$ +# $Author$ +# + +# FILE ctc_check_situation_map.sh +configure_file(ctc_check_situation_map.sh ${CMAKE_BINARY_DIR}/admin_scripts/ctc_check_situation_map.sh COPYONLY USE_SOURCE_PERMISSIONS) +NGL_INSTALL(WS FILES ctc_check_situation_map.sh DESTINATION ./admin_scripts) +NGL_INSTALL(WS FILES ctc_operator_app_removal_tool.sh DESTINATION ./admin_scripts) + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/admin_scripts/ctc_check_situation_map.sh b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/admin_scripts/ctc_check_situation_map.sh new file mode 100755 index 000000000..93c6474d3 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/admin_scripts/ctc_check_situation_map.sh @@ -0,0 +1,83 @@ +#!/bin/bash +# Helper script to check presence of some rpms +# $Id$ +# $HeadURL$ +# $Author$ + + +RPMS="" + +# Stand 2022/09: + +# Bremen +RPMS="${RPMS} ctc_arcgis-100.8" +RPMS="${RPMS} legatus_basemap_bremen_osm_32632" +RPMS="${RPMS} legatus_basemap_bremen_osm_WebMerc" +RPMS="${RPMS} legatus_basemap_bremen_hemelingen_aerial_32632" +RPMS="${RPMS} legatus_basemap_bremen_hemelingen_aerial_WebMerc" +RPMS="${RPMS} ctc_arcgis_elevation_map_bremen" +RPMS="${RPMS} ctc_arcgis_BT_56" + +# GUEZ +RPMS="${RPMS} guez_basemap_topo2015_32632" +RPMS="${RPMS} guez_basemap_topo2015_WebMerc" +RPMS="${RPMS} guez_basemap_aerial2017_32632" +RPMS="${RPMS} guez_basemap_aerial2017_WebMerc" +RPMS="${RPMS} guez_basemap_schnoeggersburg_drone_32632" +RPMS="${RPMS} guez_basemap_schnoeggersburg_drone_WebMerc" +RPMS="${RPMS} guez_arcgis_elevation_map_letzlingen" +RPMS="${RPMS} guez_arcgis_bldgs" +RPMS="${RPMS} guez_arcgis_3Dmodels" + +###RPMS="${RPMS} DoeNotExt" + + +numOk=0; +numFailure=0; + +echo "---one moment please...."; +rpm -qa > /tmp/allRpms.txt + +for RPM in ${RPMS} +do + #echo "------------------------------------"; + #echo " RPM = '$RPM' "; + grep $RPM /tmp/allRpms.txt > /dev/null + if [ $? -eq 0 ]; + then + echo " RPM seems to be installed: '$RPM'"; + ((numOk=numOk+1)) + else + echo "-> missing -> RPM '$RPM' "; + ((numFailure=numFailure+1)) + fi +done + +echo "------------------------------------"; +echo " installed = $numOk "; +echo " missing = $numFailure "; + +# cleanup +rm /tmp/allRpms.txt + + +echo "------------------------------------"; +echo "------------------------------------"; +echo "------------------------------------"; + +echo ""; +read -p "Press [Enter] key to continue" +echo ""; + + +echo "------------------------------------"; +zypper --repo RDE_15.0 se --details --installed-only ctc_ +zypper --repo RDE_15.0 se --details --installed-only legatus +zypper --repo RDE_15.0 se --details --installed-only guez + +echo "------------------------------------"; +zypper --repo RDE_15.0 se --not-installed-only ctc_ +zypper --repo RDE_15.0 se --not-installed-only legatus +zypper --repo RDE_15.0 se --not-installed-only guez + + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/admin_scripts/ctc_find_getlogger.sh b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/admin_scripts/ctc_find_getlogger.sh new file mode 100755 index 000000000..7058d100d --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/admin_scripts/ctc_find_getlogger.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# Helper script to check presence of some rpms +# $Id$ +# $HeadURL$ +# $Author$ + + +# Stand 2024 + + +echo "--- part 1"; + +# suche alle cpp file, mit 'getLogger' +#find . -iname "*.cpp" | xargs grep getLogger + +# ersetze a) getLogger durch @ b) entferne " c) entferne ) | nur das rechts vom @ +#find . -iname "*.cpp" | xargs grep getLogger | sed -e 's/getLogger(/@/g' -e 's/\"//g' -e 's/)//g' | cut -d "@" -f2 + +# sortiere und entferne doppelte: +#find . -iname "*.cpp" | xargs grep getLogger | sed -e 's/getLogger(/@/g' -e 's/\"//g' -e 's/)//g' | cut -d "@" -f2 | sort -u + +# prepend +find . -iname "*.cpp" | xargs grep getLogger | sed -e 's/getLogger(/@/g' -e 's/\"//g' -e 's/)//g' | cut -d "@" -f2 | sort -u | awk '{print "#log4j.logger.AES." $0 "=DEBUG"}' + + +echo "--- part 2"; + +find . -iname "*.cpp" | xargs grep ": BaseEdit" | sed -e 's/QLatin1String(/@/g' -e 's/QStringLiteral(/@/g' -e 's/\")/@/g' -e 's/\"//g' | cut -d "@" -f2 | sort -u | awk '{print "#log4j.logger.AES." $0 "=DEBUG"}' + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/admin_scripts/ctc_operator_app_removal_tool.sh b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/admin_scripts/ctc_operator_app_removal_tool.sh new file mode 100755 index 000000000..b5c12a12b --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/admin_scripts/ctc_operator_app_removal_tool.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Helper script to cleanup (temporary) files +# $Id$ +# $HeadURL$ +# $Author$ + + +# Stand 2023/06: + +# +# cleanup (temporary) files +# +# Buildingviewer: +# problems may arise due to different users crating the dir/files... +echo "------------------------------------"; +echo "cleanup /tmp/excon_bgviewer_symbols"; +rm -rf /tmp/excon_bgviewer_symbols + diff --git a/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/situation_map_export.h b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/situation_map_export.h new file mode 100755 index 000000000..d8e20d986 --- /dev/null +++ b/qt6-after/qt6-migrated-legatus/Lts/Viewer/SituationMap/situation_map_export.h @@ -0,0 +1,34 @@ +/** + * @RDE_HEADER@ + * Export definitions + * $Id$ + * $HeadURL$ + * $Author$ + */ +#pragma once + +#include + +#ifndef SITUATIONMAP_EXPORT +# ifdef BUILD_STATIC_LIBRARIES +# define SITUATIONMAP_EXPORT +# else +# if defined(MAKE_SITUATIONMAP_LIB) +# define SITUATIONMAP_EXPORT Q_DECL_EXPORT +# else +# define SITUATIONMAP_EXPORT Q_DECL_IMPORT +# endif +# endif +#endif + +#ifndef SITMAPGUIPLUGIN_EXPORT +# ifdef BUILD_STATIC_LIBRARIES +# define SITMAPGUIPLUGIN_EXPORT +# else +# if defined(MAKE_SITMAPGUIPLUGIN_LIB) +# define SITMAPGUIPLUGIN_EXPORT Q_DECL_EXPORT +# else +# define SITMAPGUIPLUGIN_EXPORT Q_DECL_IMPORT +# endif +# endif +#endif