5806 lines
182 KiB
C++
Executable File
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 ¤tLp = layerFactory.layerParameter(layername);
|
|
if (layerParameter.previewImage.isNull()) layerParameter.previewImage = currentLp.previewImage;
|
|
if (layerParameter.displayName.isEmpty()) layerParameter.displayName = currentLp.displayName;
|
|
if (layerParameter.description.isEmpty()) layerParameter.description = currentLp.description;
|
|
|
|
// // GUEZE-2928 // Filtervoreinstellungen werden nicht übernommen
|
|
// GUEZE-2928 steht aber auch im Widerspruch zu GUEZE-1711
|
|
// if (!isReplay() && (layername == layerFactory.emergencyCallLayer(true)))
|
|
// {
|
|
// // for Live:
|
|
// layerParameter.visible = true;
|
|
// layerParameter.opacity = 100;
|
|
// layerParameter.minScale = -1;
|
|
// layerParameter.maxScale = -1;
|
|
// DEBUG_LOG(m_logger) << LOGFUNCTION << " 'fix' layerParameter due to emergencyCallLayer";
|
|
// }
|
|
|
|
if (! layerFactory.setLayerParameter(layername, layerParameter))
|
|
{
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " setLayerParameter failed: layername='" << layername << "'";
|
|
success = false;
|
|
}
|
|
}
|
|
else
|
|
WARNING_LOG(m_logger) << LOGFUNCTION << " layer not found: layername='" << layername << "'";
|
|
|
|
} // for
|
|
|
|
// restore order :
|
|
if (!newOrderedLayerAll.isEmpty()) // in case of initial/empty oms, newOrderedLayerAll is empty
|
|
{
|
|
if (! layerFactory.orderLayerTo(newOrderedLayerAll))
|
|
{
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " orderLayerTo failed";
|
|
success = false;
|
|
}
|
|
}
|
|
// : restore order
|
|
|
|
// : orderedLayerList
|
|
|
|
|
|
//
|
|
// 4. some flags, visiblities etc:
|
|
//
|
|
this->grid().setVisible(oms.mapWidgetSettings.gridVisibility);
|
|
this->showPositionString(oms.mapWidgetSettings.positionStringVisibility);
|
|
this->showExerciseTimeString(oms.mapWidgetSettings.exerciseTimeStringVisibility);
|
|
#ifndef TABLET_MODE
|
|
this->showTimeString(oms.mapWidgetSettings.timeStringVisibility);
|
|
#endif
|
|
this->showCompass(oms.mapWidgetSettings.compassVisibility);
|
|
|
|
if (oms.mapWidgetSettings.overviewMapVisibility)
|
|
this->overviewMapHandler().createBuiltInOverviewMap();
|
|
else
|
|
this->overviewMapHandler().removeBuiltInOverviewMap();
|
|
|
|
|
|
//
|
|
// 5. JumpPointWidget :
|
|
//
|
|
if (d->m_jumpPointWidget)
|
|
{
|
|
d->m_jumpPointWidget->cleanup();
|
|
for (const auto &jpd : oms.jumpPoints)
|
|
{
|
|
d->m_jumpPointWidget->addJumpPoint(jpd.key,
|
|
jpd.originGeo,
|
|
jpd.mapScale);
|
|
|
|
} // foreach
|
|
}
|
|
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " END, success = " << success;
|
|
return success;
|
|
}
|
|
|
|
|
|
|
|
|
|
Esri::ArcGISRuntime::GraphicsOverlay *MapImpl::getGraphicsLayer(const QString &logicalName)
|
|
{
|
|
return this->layerFactoryImpl().getGraphicsLayer(logicalName);
|
|
}
|
|
|
|
Esri::ArcGISRuntime::GraphicsOverlay *MapImpl::getGraphicsLayerDict(const QString &logicalName)
|
|
{
|
|
return this->layerFactoryImpl().getGraphicsLayerDict(logicalName);
|
|
}
|
|
|
|
Esri::ArcGISRuntime::GraphicsOverlay *MapImpl::getGraphicsLayer3DExtrusion(const QString &logicalName)
|
|
{
|
|
return this->layerFactoryImpl().getGraphicsLayer3DExtrusion(logicalName);
|
|
}
|
|
|
|
Esri::ArcGISRuntime::GraphicsOverlay *MapImpl::getGraphicsLayer3D_SpAbsolute(const QString &logicalName)
|
|
{
|
|
return this->layerFactoryImpl().getGraphicsLayer3D_SpAbsolute(logicalName);
|
|
}
|
|
|
|
|
|
CoordTranslationFunctor *MapImpl::coordTranslationFunctor() const
|
|
{
|
|
return d->m_coordTranslationFunctor;
|
|
}
|
|
|
|
|
|
ObjectContainer &MapImpl::objectContainer()
|
|
{
|
|
return *(d->m_objectContainer.data());
|
|
}
|
|
void MapImpl::setObjectContainer(QSharedPointer<ObjectContainer> 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 ¢erPointMap = ctFunctor->geographicToMap(viewpoint.centerGeo());
|
|
const Esri::ArcGISRuntime::Point esriPoint(centerPointMap.x(), centerPointMap.y(), this->getEsriSpatialReference());
|
|
|
|
if (!esriPoint.isValid())
|
|
{
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " esriPoint not valid!!!"
|
|
<< " (viewpoint=" << viewpoint << ")";
|
|
return;
|
|
}
|
|
|
|
if (viewpoint.isNull())
|
|
{
|
|
WARNING_LOG(m_logger) << LOGFUNCTION << " viewpoint is null -> skip!!!"
|
|
<< " (viewpoint=" << viewpoint << ")";
|
|
return;
|
|
}
|
|
|
|
// scale in Viewport: value of 0 and below may raise an exception. Try to avoid:
|
|
double scale = viewpoint.scale();
|
|
if (scale <= 0)
|
|
{
|
|
scale = Viewpoint2D::defaultScale;
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " viewpoint.scale less or equal zero!!! set to default (" << scale << ")"
|
|
<< " (viewpoint=" << viewpoint << ")";
|
|
}
|
|
|
|
const Esri::ArcGISRuntime::Viewpoint targetVp(esriPoint,
|
|
scale,
|
|
viewpoint.rotation() * -1.0); // clock vs. counterClock
|
|
|
|
if (andWait)
|
|
geoview->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 ¢er, double scale, float durationSeconds)
|
|
{
|
|
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
|
|
<< " center=" << center
|
|
<< " scale=" << scale
|
|
<< " durationSeconds=" << durationSeconds
|
|
<< " sp=" << this->getEsriSpatialReference().wkid();
|
|
|
|
Esri::ArcGISRuntime::GeoView *geoview = this->getEsriGeoView();
|
|
const CoordTranslationFunctor *ctFunctor = this->coordTranslationFunctor();
|
|
|
|
if (!geoview || !ctFunctor)
|
|
{
|
|
ERROR_LOG(m_logger) << LOGFUNCTION << " no object";
|
|
return;
|
|
}
|
|
|
|
const QPointF ¢erPointMap = ctFunctor->geographicToMap(center);
|
|
|
|
const Esri::ArcGISRuntime::Point esriPoint(centerPointMap.x(), centerPointMap.y(), this->getEsriSpatialReference());
|
|
|
|
const Esri::ArcGISRuntime::Viewpoint &curVp =
|
|
geoview->currentViewpoint(Esri::ArcGISRuntime::ViewpointType::CenterAndScale);
|
|
|
|
Esri::ArcGISRuntime::Viewpoint targetVp(esriPoint,
|
|
(scale < 0) ? curVp.targetScale() : scale
|
|
);
|
|
// use default (d->m_animationDuration) if not specified:
|
|
if (durationSeconds < 0)
|
|
durationSeconds = d->m_animationDuration;
|
|
|
|
if (durationSeconds < 0)
|
|
geoview->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"
|