Files
qt5-legatus-legacy/Lts/Viewer/SituationMap/MapImpl/map_impl.cpp
T
2026-08-13 18:33:10 +02:00

5806 lines
182 KiB
C++
Executable File

/**
* @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 <SideBar/side_bar.h>
#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 <ElevationMap/elevation_map.h>
#include <ElevationMap/elevation_map_manager.h>
#include <Map/situation_map_utils.h>
#include <Map/map_notifier.h>
#include <Map/object_factory.h>
#include <Map/party_id_to_color_functor.h>
#include <Map/drop_event_manager.h>
#include <Map/camera.h>
#include <MapImpl/object_container_impl.h>
#include <MapImpl/abstract_map_object_impl.h>
#include <Symbology/symbol_dictionary.h>
#include <SymbologyImpl/symbol_dictionary_impl.h>
#include <Common/ref_pointer.h>
#include <Rubberbands/event_mouse_navigator.h>
#include <Rubberbands/rubberband_factory.h>
#include "overlay_handler_impl.h"
#include <Map/geometry_model_renderer.h>
#include <MapImpl/geometry_model_renderer_esri.h>
// NGL
#include <ngltools/ngltools_dtg.h>
// Lts
#include <LtsCoordTrans/lts_coord_trans.h>
#include <GeometryModel/geometry_pixelTextBox.h>
// ESRI
#include <arcgis/Map.h>
#include <arcgis/Scene.h>
#include <arcgis/ArcGISRuntimeEnvironment.h>
#include <arcgis/SpatialReference.h>
#include <arcgis/MapGraphicsView.h>
#include <arcgis/SceneGraphicsView.h>
#include <arcgis/SceneViewTypes.h> // enum NavigationConstraint
//#include <arcgis/CoordinateConversion.h>
#include <arcgis/GeometryEngine.h>
#include <arcgis/Basemap.h>
#include <arcgis/LayerListModel.h>
#include <arcgis/ArcGISTiledLayer.h>
#include <arcgis/ArcGISTiledElevationSource.h>
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/SimpleMarkerSymbol.h>
#include <arcgis/PolylineBuilder.h>
#include <arcgis/PolygonBuilder.h>
#include <arcgis/Polyline.h>
#include <arcgis/Graphic.h>
#include <arcgis/TextSymbol.h>
#include <arcgis/ServiceFeatureTable.h>
#include <arcgis/FeatureLayer.h>
#include <arcgis/ArcGISSceneLayer.h>
#include <arcgis/SimpleRenderer.h>
#include <arcgis/GraphicsOverlayListModel.h>
#include <arcgis/SimpleFillSymbol.h>
#include <arcgis/SelectionProperties.h>
#include <arcgis/arcgisversion.h> // This file is created by RME, not ESRI
#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 <arcgis/RequestConfiguration.h>
#include <arcgis/NetworkCacheConfiguration.h>
#endif // HAS_NETWORK_CACHE_CONFIGURATION
#include <GraphicalObjects/graphical_object_text.h>
#include <GraphicalObjects/graphical_object_picture_marker.h>
#include <GraphicalObjects/graphical_object_path.h>
#include <GraphicalObjects/graphical_object_ellipse.h>
#include <GraphicalObjects/tactical_appearance.h>
// Qt
#include <QFileInfo>
#include <QGuiApplication>
#include <QStackedLayout>
#include <QGraphicsProxyWidget>
#include <QMimeData>
#include <QMetaMethod>
#include <QThread>
#include <QImage>
#include <QMessageBox>
#include <QtPrintSupport/QPrinter>
#include <QTextTable>
#include <QNetworkProxy>
#include <QSizeGrip>
#include <QScreen>
#include <limits>
#include <iostream>
#include <typeinfo>
#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(QEvent *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<ElevationMap> m_elevationMap;
std::shared_ptr<ElevationMapManager> m_elevationMapManager;
Esri::ArcGISRuntime::MapGraphicsView *m_mapGraphicsView = nullptr;
Esri::ArcGISRuntime::SceneGraphicsView *m_sceneGraphicsView = nullptr;
ScreenToLocationHelper *m_screenToLocationHelper = 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<LocationToElevationHelper> 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<QPointF> m_trainingsAreaExtent;
// Layout, SideBar etc
QStackedLayout *m_stackedLayout = nullptr;
QPointer<SideBar> m_sideBar = nullptr; // optional
QPushButton *m_pbSideBar = nullptr; // optional
JumpPointWidget *m_jumpPointWidget = nullptr; // optional
// QTextEdit / TextBoxPixel
struct {
QPointer<QLabel> m_label;
QPointer<QTextEdit> m_textEdit;
QGraphicsRectItem *m_handle_or_proxyControl{nullptr};
QGraphicsProxyWidget *m_proxy{nullptr};
QPointer<QSizeGrip> 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<ObjectContainer> m_objectContainer;
CoordTranslationFunctor *m_coordTranslationFunctorInitialize = nullptr;
CoordTranslationFunctor *m_coordTranslationFunctor = nullptr;
QSharedPointer<PartyIdToColorFunctor> m_partyIdToColorFunctor;
QTimer *m_partychangedDelayTimer = nullptr;
QSharedPointer<OverlayHandlerImpl> 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<PixelBasedTextBoxHandlerImpl> 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<RefPointer<DropEventManager>> 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<SituationMap::IdGenerator> idGen(new SituationMap::IdGenerator);
QSharedPointer<SituationMap::ObjectContainerCTC> objectCont(new SituationMap::ObjectContainerCTC(idGen));
setObjectContainer(objectCont);
//----------------------------------------------------------------------------
// set default partyIdToColorFunctor:
// can be replaced using ''
//----------------------------------------------------------------------------
d->m_partyIdToColorFunctor =
QSharedPointer<PartyIdToColorFunctor>(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<UniqueMapId, RefPointer<AbstractMapObject>> mapObjIt = this->objectContainer().mutableIterator();
while (mapObjIt.hasNext())
{
mapObjIt.next();
RefPointer<AbstractMapObject> 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<MapNotifier> MapImpl::createMapNotifier() const
{
MapImpl *THIS = const_cast<MapImpl *>(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<UniqueMapId> pickedMapObjectIdList;
RefPointerMapObjectList pickedMapObjects;
const QVector<QPointF> &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<AbstractMapObject *> 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<AbstractMapObject>(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<double>::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<void()> 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<void()> 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<double>::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<double>::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<double>::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<double>::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<double>::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<double>::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->setViewpoint(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->setViewpoint(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<int>::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<QMouseEvent *>(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<SideBar>(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<SideBar> 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 : qAsConst(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<QString, std::weak_ptr<ElevationMapManager>> 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<ElevationMapManager>();
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<ElevationMapManager> 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<int>(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<Esri::ArcGISRuntime::ArcGISSceneLayer *>(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<AdvancedSymbology::SymbolDictionaryImpl &>(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<Esri::ArcGISRuntime::ImageAdjustmentLayer *>(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<Esri::ArcGISRuntime::ArcGISTiledLayer *>(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<QPair<QString, int>> 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; esriIdx<gOverlayCount; ++esriIdx)
{
Esri::ArcGISRuntime::GraphicsOverlay *gOverlay = gOverlayListModel->at(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<QString, int>(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<QString, int> &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<Esri::ArcGISRuntime::Layer*> layerList = esriMap->layers();
// foreach (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<QObject *>(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 : qAsConst(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 : qAsConst(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<QKeyEvent *>(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<QWidget *>(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<DropEventManager> 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<DropEventManager> 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<SituationMap::DrawStatus>(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<SituationMap::NavigationConstraint>(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<Esri::ArcGISRuntime::NavigationConstraint>(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<MapImpl *>(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<QString> &layernames = layerFactory.layernames();
for (const auto &layername : layernames)
{
QPair<QString, LayerParameter> 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<JPData> &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 &currentLp = 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> 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<PartyIdToColorFunctor> 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<MapImpl *>(this);
d->m_overlayHandler = QSharedPointer<OverlayHandlerImpl> (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 &centerPointMap = 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->setViewpointAndWait(targetVp);
else
{
// use default (d->m_animationDuration) if not specified:
if (durationSeconds < 0)
durationSeconds = d->m_animationDuration;
if (durationSeconds < 0)
geoview->setViewpoint(targetVp);
else
geoview->setViewpoint(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<Esri::ArcGISRuntime::Point>(curVp.targetGeometry());
const QPointF center(mapCenter.x(), mapCenter.y());
return ctFunctor->mapToGeographic(center);
}
void MapImpl::setCenterPoint(const QPointF &center, 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 &centerPointMap = 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->setViewpoint(targetVp);
else
geoview->setViewpoint(targetVp, durationSeconds);
if (isPositionStringVisible())
{
d->m_positionStr = this->mapToMgrsString(centerPointMap);
d->updatePositionText();
}
}
void MapImpl::zoomToArea(const QVector<QPointF> &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<Esri::ArcGISRuntime::MapGraphicsView *>(geoview);
mapView->setViewpointGeometry(pBuilder.toGeometry(), paddingInDIPS);
}
break;
case Mode::Mode_3D:
{
Esri::ArcGISRuntime::SceneGraphicsView *sceneView = static_cast<Esri::ArcGISRuntime::SceneGraphicsView *>(geoview);
Esri::ArcGISRuntime::Viewpoint esriVp(pBuilder.toGeometry()); // unfortunately padding is not supported here
sceneView->setViewpoint(esriVp);
}
break;
case Mode::Undefined:
{}
} // switch
}
void MapImpl::setTrainingsAreaExtent(const QVector<QPointF> &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<Esri::ArcGISRuntime::Envelope>(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->setViewpoint(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->setViewpoint(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)
{
d->m_mapGraphicsView->setViewpointScale(absoluteScale);
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->setViewpoint(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(),
curVp.targetScale(),
d->m_rotationAbsoluteAngleDegrees * -1.0); // clock vs. counterClock
geoview->setViewpoint(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->setViewpointCamera(tarCam);
else
d->m_sceneGraphicsView->setViewpointCamera(tarCam, durationSeconds);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
Grid &MapImpl::grid() const
{
if (!d->m_gridImpl)
{
MapImpl *THIS = const_cast<MapImpl *>(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<MapImpl *>(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<UniqueMapId> &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<UniqueMapId> 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<AbstractMapObject>(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<UniqueMapId> &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<AbstractMapObject>(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<UniqueMapId, RefPointer<AbstractMapObject>> mapObjIt = this->objectContainer().mutableIterator();
while (mapObjIt.hasNext())
{
mapObjIt.next();
RefPointer<AbstractMapObject> 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<MapImpl *>(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<SituationMap::RB::RubberbandRectangle>();
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<SituationMap::RB::RubberbandRectangle>();
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<QPointF> &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<MapImpl*>(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();
}
switch (mode())
{
case Mode::Mode_2D:
connect(d->m_mapGraphicsView, &Esri::ArcGISRuntime::MapGraphicsView::exportImageCompleted,
this, &MapImpl::onExportImageCompleted, Qt::UniqueConnection);
break;
case Mode::Mode_3D:
connect(d->m_sceneGraphicsView, &Esri::ArcGISRuntime::SceneGraphicsView::exportImageCompleted,
this, &MapImpl::onExportImageCompleted, Qt::UniqueConnection);
break;
case Mode::Undefined:
// empty
break;
} // switch
Esri::ArcGISRuntime::TaskWatcher tw = geoview->exportImage();
if (! tw.isValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " TaskWatcher invalid";
return QUuid();
}
const QUuid &taskId = tw.taskId();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, taskId=" << taskId;
return taskId;
}
void MapImpl::onExportImageCompleted(const QUuid &taskId, const QImage &image)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, taskId=" << taskId;
emit imageCompleted(taskId, image);
// image.save(QLatin1String("./ExportImageCompleted.png"), "PNG");
TRACE_LOG(m_logger) << LOGFUNCTION << " END, taskId=" << 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.setOrientation(QPrinter::Orientation::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.paperRect().size();
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<QTextLength> 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"