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

661 lines
23 KiB
C++
Executable File

/**
**
** @RDE_HEADER@
** Implementation of rubberband class to define point feature geometry in 3d
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 27.03.2015, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#include <limits>
#include "rubberband_multi_point_feature_3d.h"
#include "rubberband_base_internal.h"
#include "event_mouse_navigator.h"
#include <Map/map.h>
#include <Map/layer_factory.h>
#include <MapImpl/map_impl.h>
#include <MapImpl/coordinate_translation.h>
#include <MapImpl/screen_to_location_helper.h>
#include <MapImpl/location_to_elevation_helper.h>
#include <MathUtils/math_const.h>
#include <GraphicalObjects/graphical_object_picture_marker.h>
#include <GraphicalObjects/graphical_object_path.h>
#include <QtGui/QGuiApplication>
#include <arcgis/IdentifyGraphicsOverlayResult.h>
namespace SituationMap { namespace RB
{
class RubberbandMultiPointFeature3D::InternalData
{
public:
QVector<GraphicalObjectPictureMarker*> m_marker;
GraphicalObjectPath *m_path;
QVector<QPointF> m_coordinates;
int m_currentIndex;
int m_selectionIndex;
Esri::ArcGISRuntime::TaskWatcher m_pickingTask;
Esri::ArcGISRuntime::TaskWatcher m_selectionTask;
QPoint m_pickingPoint;
QPointF * m_startMove;
ScreenToLocationHelper * m_screenToMap;
QPointer<LocationToElevationHelper> m_toElevation;
PositionCont m_positionList3D;
bool m_doCompleteAction;
InternalData()
: m_marker()
, m_path(nullptr)
, m_coordinates()
, m_currentIndex(-1)
, m_selectionIndex(-1)
, m_pickingTask()
, m_selectionTask()
, m_pickingPoint()
, m_startMove(nullptr)
, m_screenToMap()
, m_toElevation(nullptr)
, m_positionList3D()
, m_doCompleteAction(false)
{
m_marker.reserve(100);
m_coordinates.reserve(100);
} // InternalData()
~InternalData()
{
delete m_startMove;
for (auto marker : qAsConst(m_marker))
delete marker;
delete m_path;
delete m_screenToMap;
} // ~InternalData()
}; // RubberbandPointFeature3D::InternalData
RubberbandMultiPointFeature3D::RubberbandMultiPointFeature3D( SituationMap::Map * map, QObject * parent, bool isActive )
: Rubberband3D(map,parent,isActive)
, _internal( new RubberbandMultiPointFeature3D::InternalData() )
, _isClosed(false)
, _isCircular(false)
{
MapImpl *mapImpl = static_cast<MapImpl*>(_baseInternal->_map.data());
Esri::ArcGISRuntime::SceneGraphicsView *view = mapImpl->getEsriSceneGraphicsView();
connect( view, &Esri::ArcGISRuntime::SceneGraphicsView::identifyGraphicsOverlaysCompleted, this, &RubberbandMultiPointFeature3D::onGetPickingResult );
_internal->m_screenToMap = new ScreenToLocationHelper(view);
connect( _internal->m_screenToMap, &ScreenToLocationHelper::mouseScenePositionChanged, this, &RubberbandMultiPointFeature3D::onMapPosition ); // ToDO
} // RubberbandMultiPointFeature3D::RubberbandMultiPointFeature3D( SituationMap::Map * map, QObject * parent = nullptr, bool isActive )
RubberbandMultiPointFeature3D::~RubberbandMultiPointFeature3D()
{
delete _internal;
_internal = nullptr;
} // RubberbandMultiPointFeature3D::~RubberbandMultiPointFeature3D()
void RubberbandMultiPointFeature3D::activateButtons()
{
_baseInternal->_hasUndoButton = false;
_baseInternal->_undoIsPossible = false;
_baseInternal->_applyIsPossible = (_internal->m_coordinates.size() > ((_isClosed && !_isCircular) ? 2 : 1));
RubberbandBase::activateButtons();
} /* RubberbandMultiPointFeature3D::activateButtons */
void circleShape( GraphicalObjectPath *pathObj, const PositionCont &positionList )
{
if (positionList.size() < 2) return;
PositionCont circleShape;
double cx = positionList[0].x();
double cy = positionList[0].y();
double rx = positionList[1].x()-cx;
double ry = positionList[1].y()-cy;
double corrLon = cos(cy / 57.2957795147); // correct factor from nautic simulation to get a real circle
double dPhi = MathConst::pi / 50; // delta angle to rasterize the circle to a polyline
auto sqr = [](double x) -> double { return x * x; };
double r = sqrt(sqr(rx*corrLon) + sqr(ry) );
double phi = 0.0;
while (phi < MathConst::pi_two)
{
double sinPhi = sin(phi);
double cosPhi = cos(phi);
double px = cx + r * cosPhi / corrLon;
double py = cy + r*sinPhi;
circleShape.append(Point3D(px, py, 0.0));
phi += dPhi;
}
pathObj->setPositions(circleShape);
} // circleShape
void RubberbandMultiPointFeature3D::onMapPosition(const Point3D &mapCoordinate)
{
if (_internal->m_currentIndex < 0) return;
if (qIsNaN(mapCoordinate.x())) return; // illegal map position
if (qIsNaN(mapCoordinate.y())) return;
if ((!_internal->m_toElevation) && (RubberbandBase::_baseInternal->_map->layerFactory().elevationAvailable()))
{
_internal->m_toElevation = RubberbandBase::_baseInternal->_map->layerFactory().locationToElevation(); // layerFactory has ownership
}
if ((std::numeric_limits<int>::max()) == _internal->m_currentIndex)
{
// move all coordinates
if (_internal->m_startMove)
{
QPointF deltaMove = mapCoordinate.toPointF() - *(_internal->m_startMove);
_internal->m_positionList3D.clear();
_internal->m_positionList3D.reserve(_internal->m_coordinates.size());
for (int i = 0; i < _internal->m_coordinates.size(); ++i)
{
const QPointF & pos = _internal->m_coordinates[i] += deltaMove;
_internal->m_marker[i]->setOrigin( pos );
if (_internal->m_toElevation)
{
QMetaObject::Connection conn;
conn = connect(_internal->m_toElevation.data(), &SituationMap::LocationToElevationHelper::elevationCompleted, this, [this,pos,conn](double elevation)
{
disconnect(conn);
_internal->m_positionList3D << Point3D(pos.x(),pos.y(),elevation);
if (_internal->m_positionList3D.size() == _internal->m_coordinates.size())
{
if (_internal->m_path) _internal->m_path->setPositions( _internal->m_positionList3D );
update();
}
// qCritical() << "elevation at (" << pos.x() << " , " << pos.y() << ") : " << elevation;
});
_internal->m_toElevation->locationToElevation(pos);
}
else
{
_internal->m_positionList3D << Point3D::fromQPointF(pos);
}
}
if (_internal->m_toElevation)
{
if (_internal->m_path) _internal->m_path->setPositions( _internal->m_positionList3D );
update();
}
}
else _internal->m_startMove = new QPointF(mapCoordinate.toPointF());
return;
}
if (_internal->m_currentIndex >= _internal->m_coordinates.size()) return;
MapImpl *mapImpl = static_cast<MapImpl*>(_baseInternal->_map.data());
Esri::ArcGISRuntime::SceneGraphicsView *view = mapImpl->getEsriSceneGraphicsView();
Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = mapImpl->getGraphicsLayer(mapImpl->layerFactory().rubberbandLayer());
if (nullptr == view) return;
for (int idx = _internal->m_positionList3D.size(); idx <= _internal->m_currentIndex; ++idx)
{
_internal->m_positionList3D << mapCoordinate;
}
_internal->m_coordinates[_internal->m_currentIndex] = mapCoordinate.toPointF();
_internal->m_positionList3D[_internal->m_currentIndex] = mapCoordinate;
// qCritical() << "elevation idx=" << _internal->m_currentIndex <<" at (" << mapCoordinate.x() << " , " << mapCoordinate.y() << ") : " << mapCoordinate.z();
GraphicalObjectPictureMarker *&marker = _internal->m_marker[_internal->m_currentIndex];
if (nullptr == marker)
{
marker = new GraphicalObjectPictureMarker( view, graphicsLayer, QStringLiteral("RubberbandMultiPointFeature3D"), INVALID_MAP_ID );
marker->setImage(markerImage());
marker->setOrigin( mapCoordinate );
marker->updateView();
marker->setPickable(true);
Esri::ArcGISRuntime::Graphic *graphic = marker->graphic();
if (graphic)
{
Esri::ArcGISRuntime::AttributeListModel *attLisModel = graphic->attributes();
if (attLisModel)
{
// qCritical() << "Rubberband3D-Item =" << _internal->m_currentIndex;
attLisModel->insertAttribute(QStringLiteral("Rubberband3D-Item"),_internal->m_currentIndex);
}
}
}
else marker->setOrigin( mapCoordinate );
if (_internal->m_coordinates.size() > ((_isClosed && !_isCircular) ? 2 : 1))
{
if (nullptr == _internal->m_path)
{
bool isPolygon = (_isClosed || _isCircular);
_internal->m_path = new GraphicalObjectPath(isPolygon ? GraphicalObjectPathType::Polygon : GraphicalObjectPathType::Polyline, view, graphicsLayer, QStringLiteral("RubberbandMultiPointFeature3D"), INVALID_MAP_ID);
_internal->m_path->rLineStyle().setLineColor(_handleDrawColor);
_internal->m_path->rLineStyle().setLinePattern(LinePattern::Solid);
_internal->m_path->rLineStyle().setLineWidth(3);
_internal->m_path->setPickable(false);
if (isPolygon)
{
_internal->m_path->rFillStyle().setFillColor(_frameFillColor);
}
if (_isCircular)
{
circleShape(_internal->m_path, _internal->m_positionList3D);
}
else _internal->m_path->setPositions(_internal->m_positionList3D);
_internal->m_path->updateView();
}
else
{
if (_isCircular)
{
circleShape(_internal->m_path, _internal->m_positionList3D);
}
else _internal->m_path->setPositions(_internal->m_positionList3D);
}
}
if (_isCircular && (0 == _internal->m_currentIndex) && (1 == _internal->m_coordinates.size()))
{
_internal->m_coordinates << _internal->m_coordinates.first();
_internal->m_positionList3D << _internal->m_positionList3D.first();
_internal->m_marker << nullptr;
_internal->m_currentIndex++;
// qCritical() << QCursor::pos() << " != " << view->viewport()->mapToGlobal(_internal->m_pickingPoint);
_internal->m_doCompleteAction = (QCursor::pos() != view->viewport()->mapToGlobal(_internal->m_pickingPoint));
if (!_internal->m_doCompleteAction)
{
// Click with out moving the mouse, set a default size of the circle and let the user do some editing
// The rubberband doesn't complete automatically in this case.
_internal->m_screenToMap->screenToLocation(_internal->m_pickingPoint.x()+100, _internal->m_pickingPoint.y());
}
}
update();
if (!QGuiApplication::mouseButtons().testFlag(Qt::MouseButton::LeftButton))
{
if (_isCircular && _internal->m_doCompleteAction && (1 == _internal->m_currentIndex))
{
completeStateAction(nullptr, 0, 0);
}
else
{
updateButtonPosition();
activateButtons();
}
}
} // void RubberbandMultiPointFeature3D::onMapPosition(const QPointF &mapCoordinate)
void RubberbandMultiPointFeature3D::onGetPickingResult(const QUuid &taskId, const QList<Esri::ArcGISRuntime::IdentifyGraphicsOverlayResult*> &identifyResults)
{
if (_internal->m_pickingTask.taskId() == taskId)
{
for (auto iResult : identifyResults)
{
const QList<Esri::ArcGISRuntime::Graphic*> &gResults = iResult->graphics();
for (auto pickedGraphic : gResults)
{
if (pickedGraphic->attributes())
{
bool ok = false;
int pickedIndex = pickedGraphic->attributes()->attributeValue(QStringLiteral("Rubberband3D-Item")).toInt(&ok);
if (ok)
{
qCritical() << "Picking index " << pickedIndex;
_internal->m_currentIndex = pickedIndex;
_internal->m_screenToMap->screenToLocation(_internal->m_pickingPoint.x(), _internal->m_pickingPoint.y());
if (pickedIndex < _internal->m_marker.size())
{
GraphicalObjectPictureMarker *&marker = _internal->m_marker[pickedIndex];
if (marker)
{
marker->setImage(markerSelectionImage());
marker->updateView();
}
}
return;
}
}
} // foreach pickedGraphic
} // foreach iResult
if ((!_isCircular) || (_internal->m_coordinates.size() < 2))
{
// add new coordinate and calculate map location
_internal->m_currentIndex = _internal->m_marker.size();
_internal->m_marker << nullptr;
_internal->m_coordinates << QPointF();
_internal->m_screenToMap->screenToLocation(_internal->m_pickingPoint.x(), _internal->m_pickingPoint.y());
}
}
if (_internal->m_selectionTask.taskId() == taskId)
{
for (auto iResult : identifyResults)
{
const QList<Esri::ArcGISRuntime::Graphic*> &gResults = iResult->graphics();
for (auto pickedGraphic : gResults)
{
if (pickedGraphic->attributes())
{
bool ok = false;
int pickedIndex = pickedGraphic->attributes()->attributeValue(QStringLiteral("Rubberband3D-Item")).toInt(&ok);
if ((ok) && (pickedIndex < _internal->m_marker.size()))
{
GraphicalObjectPictureMarker *&marker = _internal->m_marker[pickedIndex];
if (marker)
{
_internal->m_selectionIndex = pickedIndex;
marker->setImage(markerSelectionImage());
marker->updateView();
return;
}
}
}
}
}
}
if ((_internal->m_selectionIndex >= 0) && (_internal->m_selectionIndex < _internal->m_marker.size()))
{
GraphicalObjectPictureMarker *&marker = _internal->m_marker[_internal->m_selectionIndex];
if (marker)
{
marker->setImage(markerImage());
marker->updateView();
}
_internal->m_selectionIndex = -1;
}
} // void RubberbandMultiPointFeature3D::onGetPickingResult(const QUuid &taskId, const QList<Esri::ArcGISRuntime::IdentifyGraphicsOverlayResult*> &identifyResults)
bool RubberbandMultiPointFeature3D::selectAction(AbstractEventDispatcher *, int x, int y)
{
delete _internal->m_startMove;
_internal->m_startMove = nullptr;
if (rubberbandIsAlive())
{
if (_baseInternal->buttonProxyActive(QPointF(x,y))) return false;
MapImpl *mapImpl = static_cast<MapImpl*>(_baseInternal->_map.data());
_internal->m_pickingTask.cancel(); // cancel old task
_internal->m_pickingPoint = QPoint(x,y);
_internal->m_pickingTask = mapImpl->getEsriGeoView()->identifyGraphicsOverlays(x,y,EventMouseNavigator::pickTolerance(),false,std::numeric_limits<int>::max());
_internal->m_currentIndex = -1;
return true;
}
return false;
} // bool RubberbandMultiPointFeature3D::selectAction(AbstractEventDispatcher *, int x, int y)
bool RubberbandMultiPointFeature3D::setCoordinateAction(AbstractEventDispatcher *, int x, int y)
{
delete _internal->m_startMove;
_internal->m_startMove = nullptr;
if (rubberbandIsAlive())
{
_baseInternal->_lastClickPosition = QPointF(x,y);
if (_baseInternal->buttonProxyActive(_baseInternal->_lastClickPosition)) return false;
_internal->m_screenToMap->screenToLocation(x,y);
if ((_internal->m_currentIndex >= 0) && (_internal->m_currentIndex < _internal->m_marker.size()))
{
GraphicalObjectPictureMarker *&marker = _internal->m_marker[_internal->m_currentIndex];
if (marker)
{
marker->setImage(markerImage());
marker->updateView();
}
}
return true;
}
return false;
} // bool RubberbandMultiPointFeature3D::setCoordinateAction(AbstractEventDispatcher *, int x, int y)
bool RubberbandMultiPointFeature3D::movePickedAction(AbstractEventDispatcher *, int x, int y)
{
if (rubberbandIsAlive())
{
_internal->m_screenToMap->screenToLocation(x,y);
return true;
}
return false;
} // bool RubberbandMultiPointFeature3D::movePickedAction(AbstractEventDispatcher *, int x, int y)
bool RubberbandMultiPointFeature3D::moveAction( AbstractEventDispatcher*, int x, int y )
{
if (rubberbandIsAlive())
{
if (_baseInternal->buttonProxyActive(QPointF(x,y))) return false;
MapImpl *mapImpl = static_cast<MapImpl*>(_baseInternal->_map.data());
_internal->m_selectionTask.cancel(); // cancel old task
_internal->m_selectionTask = mapImpl->getEsriGeoView()->identifyGraphicsOverlays(x,y,EventMouseNavigator::pickTolerance(),false,std::numeric_limits<int>::max());
if ((_internal->m_selectionIndex >= 0) && (_internal->m_selectionIndex < _internal->m_marker.size()))
{
GraphicalObjectPictureMarker *&marker = _internal->m_marker[_internal->m_selectionIndex];
if (marker)
{
marker->setImage(markerImage());
marker->updateView();
}
}
_internal->m_selectionIndex = -1;
}
return false;
} // bool RubberbandMultiPointFeature3D::moveAction(AbstractEventDispatcher*,int,int)
bool RubberbandMultiPointFeature3D::completeStateAction ( AbstractEventDispatcher * causer, int x, int y )
{
if (_internal->m_coordinates.size() > ((_isClosed && !_isCircular) ? 2 : 1))
{
return applyAndEndAction(causer,x,y);
}
return false;
} // bool RubberbandMultiPointFeature3D::completeStateAction ( AbstractEventDispatcher * causer, int x, int y )
bool RubberbandMultiPointFeature3D::contextMenuAction ( AbstractEventDispatcher*,int,int ) { return rubberbandIsAlive(); }
bool RubberbandMultiPointFeature3D::deleteAction ( AbstractEventDispatcher*,int,int ) { return rubberbandIsAlive(); }
bool RubberbandMultiPointFeature3D::undoAction ( AbstractEventDispatcher*,int,int ) { return rubberbandIsAlive(); }
unsigned int RubberbandMultiPointFeature3D::coordinateCount() const
{
return (_internal->m_coordinates.size());
} // unsigned int RubberbandMultiPointFeature3D::coordinateCount() const
void RubberbandMultiPointFeature3D::update()
{
for (auto marker : qAsConst(_internal->m_marker))
if (marker) marker->updateView();
if (_internal->m_path)
_internal->m_path->updateView();
externalUpdate();
} // RubberbandMultiPointFeature3D::update()
QVector<QPointF> RubberbandMultiPointFeature3D::coordinateVector( CoordinateSystem cs ) const
{
QVector<QPointF> coordinateVector;
coordinateVector.reserve(_internal->m_coordinates.size());
for (const auto &mapCoordinate : qAsConst(_internal->m_coordinates))
coordinateVector << projectCoordinateFromMap( cs, mapCoordinate );
return coordinateVector;
} // QVector<QPointF> RubberbandMultiPointFeature3D::coordinateVector( CoordinateSystem cs ) const
QPointF RubberbandMultiPointFeature3D::singleCoordinate( unsigned int idx, CoordinateSystem cs ) const
{
if (coordinateCount() > idx)
return coordinateVector(cs).at(idx);
return QPointF();
} // QPointF RubberbandMultiPointFeature3D::singleCoordinate( unsigned int idx, CoordinateSystem cs ) const
void RubberbandMultiPointFeature3D::setCoordinateVector( const QVector<QPointF> & coordinateVector, CoordinateSystem cs )
{
_internal->m_coordinates.clear();
for (const auto &geoCoordinate : coordinateVector)
_internal->m_coordinates << projectCoordinateToMap( cs, geoCoordinate );
if ((!_internal->m_toElevation) && (_baseInternal->_map->layerFactory().elevationAvailable()))
{
_internal->m_toElevation = _baseInternal->_map->layerFactory().locationToElevation(); // layerFactory has ownership
}
// now get elevations
_internal->m_positionList3D.clear();
for (const auto &pos : qAsConst(_internal->m_coordinates))
{
if (_internal->m_toElevation)
{
QMetaObject::Connection conn;
conn = connect(_internal->m_toElevation.data(), &SituationMap::LocationToElevationHelper::elevationCompleted, this, [this,pos,conn](double elevation)
{
disconnect(conn);
_internal->m_positionList3D << Point3D(pos.x(),pos.y(),elevation);
if (_internal->m_positionList3D.size() == _internal->m_coordinates.size())
{
if (_internal->m_path) _internal->m_path->setPositions( _internal->m_positionList3D );
update();
}
});
_internal->m_toElevation->locationToElevation(pos);
}
else _internal->m_positionList3D << Point3D::fromQPointF(pos);
}
} // void RubberbandMultiPointFeature3D::setCoordinateVector( const QVector<QPointF> & coordinateVector, CoordinateSystem cs )
void RubberbandMultiPointFeature3D::setCoordinateVector(const QVector<Point3D> & coordinateVector, CoordinateSystem cs)
{
_internal->m_coordinates.clear();
_internal->m_positionList3D.clear();
for (const auto &geoCoordinate : coordinateVector)
{
const QPointF & projCoordinate = projectCoordinateToMap( cs, geoCoordinate.toPointF() );
_internal->m_coordinates << projCoordinate;
_internal->m_positionList3D << Point3D(projCoordinate.x(),projCoordinate.y(),geoCoordinate.z());
}
} // void RubberbandMultiPointFeature3D::setCoordinateVector(const QVector<Point3D> & coordinateVector, CoordinateSystem cs)
QVector<Point3D> RubberbandMultiPointFeature3D::coordinateVector3D(CoordinateSystem cs) const
{
QVector<Point3D> coordinateVector;
coordinateVector.reserve(_internal->m_coordinates.size());
for (const auto &mapCoordinate : qAsConst(_internal->m_coordinates))
coordinateVector << Point3D::fromQPointF( projectCoordinateFromMap(cs,mapCoordinate) );
return coordinateVector;
} // QVector<Point3D> RubberbandMultiPointFeature3D::coordinateVector3D(CoordinateSystem cs) const
Point3D RubberbandMultiPointFeature3D::singleCoordinate3D(unsigned int idx, CoordinateSystem cs) const
{
if (coordinateCount() > idx)
{
return coordinateVector3D(cs).at(idx);
}
return Point3D();
} // Point3D RubberbandMultiPointFeature3D::singleCoordinate3D(unsigned int idx, CoordinateSystem cs) const
} } /* namespace SituationMap::RB */