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

319 lines
13 KiB
C++
Executable File

/**
**
** @RDE_HEADER@
** Definition of rubberband class to define multi point feature geometry with an additional width coordinate
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Linux, Windows 7, Qt 5.7. ArcGISRuntimeQt 100
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 7.12.2016, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#include "rubberband_polyline_width.h"
#include "rubberband_point_handle.h"
#include "rubberband_internal.h"
#include "rubberband_base_internal.h"
#include <cmath>
#include <arcgis/Map.h>
namespace SituationMap { namespace RB
{
struct RubberbandPolylineWidth::InternalData
{
SituationMap::RefPointer<RubberbandPointHandle> _widthItem;
}; /* RubberbandPolylineWidth::InternalData*/
RubberbandPolylineWidth::RubberbandPolylineWidth( SituationMap::Map * map, QObject * parent )
: RubberbandMultiPointFeature(map,true,parent), _internal( new InternalData() )
{
} /* RubberbandPolylineWidth::RubberbandPolylineWidth */
RubberbandPolylineWidth::~RubberbandPolylineWidth()
{
delete _internal;
} /* RubberbandPolylineWidth::~RubberbandPolylineWidth */
void RubberbandPolylineWidth::buildWidthItem(const QPointF & mapPos1, const QPointF & mapPos2) const
{
double dx = mapPos2.x() - mapPos1.x();
double dy = mapPos2.y() - mapPos1.y();
QPointF handleMapPos = mapPos1 + QPointF(dx,dy)*0.35 + QPointF(dy,dx)*0.35;
setUpWidthItem(handleMapPos);
} /* RubberbandPolylineWidth::buildWidthItem */
void RubberbandPolylineWidth::setUpWidthItem(const QPointF & mapPos) const
{
RubberbandHandle::RubberbandCoordinate coordinate( Rubberband::_internal->_mapView, convert(mapPos,Rubberband::_internal->_mapView->map()->spatialReference()) );
_internal->_widthItem = new RubberbandPointHandle(Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, coordinate._sceneCoordinate);
} /* RubberbandPolylineWidth::setUpWidthItem */
QVector<QPointF> RubberbandPolylineWidth::coordinateVector( CoordinateSystem cs ) const
{
QVector<QPointF> polylineCoords = RubberbandMultiPointFeature::coordinateVector(cs);
if (polylineCoords.size() > 1)
{
if (_internal->_widthItem == nullptr)
{
RubberbandPointHandle * pointHandle1Ptr = static_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[0].get());
RubberbandPointHandle * pointHandle2Ptr = static_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[1].get());
Esri::ArcGISRuntime::Point p1 = pointHandle1Ptr->getCoordinate()._mapCoordinate;
Esri::ArcGISRuntime::Point p2 = pointHandle2Ptr->getCoordinate()._mapCoordinate;
buildWidthItem( QPointF(p1.x(),p1.y()), QPointF(p2.x(),p2.y()) );
}
polylineCoords += projectCoordinateFromMap(cs, _internal->_widthItem->getCoordinate()._mapCoordinate);
}
return polylineCoords;
} /* RubberbandPolylineWidth::coordinateVector */
QPointF RubberbandPolylineWidth::singleCoordinate( unsigned int idx, CoordinateSystem cs ) const
{
if (static_cast<unsigned int>(Rubberband::_internal->_handleList.size()) == idx)
{
return projectCoordinateFromMap(cs, _internal->_widthItem->getCoordinate()._mapCoordinate);
}
return RubberbandMultiPointFeature::singleCoordinate(idx, cs);
} /* RubberbandPolylineWidth::singleCoordinate */
void RubberbandPolylineWidth::setCoordinateVector( const QVector<QPointF> & coordinateVector, CoordinateSystem cs )
{
if (coordinateVector.size() > 2)
{
QVector<QPointF> lineCoords = coordinateVector;
setUpWidthItem( projectCoordinateToMap(cs, lineCoords.last()) );
lineCoords.removeLast();
RubberbandMultiPointFeature::setCoordinateVector(lineCoords, cs);
return;
}
if (coordinateVector.size() == 2)
{
const QPointF & mapP1 = projectCoordinateToMap(cs, coordinateVector[0]);
const QPointF & mapP2 = projectCoordinateToMap(cs, coordinateVector[1]);
buildWidthItem(mapP1, mapP2);
}
RubberbandMultiPointFeature::setCoordinateVector(coordinateVector,cs);
} /* RubberbandPolylineWidth::setCoordinateVector */
bool RubberbandPolylineWidth::selectAction( AbstractEventDispatcher * causer, int x, int y )
{
if (rubberbandIsAlive() && (nullptr != _internal->_widthItem.get()))
{
_internal->_widthItem->setSelection(false);
QPointF scenePos(pixel2ScenePosition(x, y));
if (_baseInternal->buttonProxyActive(scenePos)) return false;
if (_internal->_widthItem->isPositionInside(scenePos))
{
_internal->_widthItem->setSelection();
_baseInternal->_lastClickPosition = scenePos;
update();
return true;
}
}
return RubberbandMultiPointFeature::selectAction(causer,x,y);
} /* RubberbandPolylineWidth::selectAction */
bool RubberbandPolylineWidth::setCoordinateAction( AbstractEventDispatcher * causer, int x, int y )
{
if (rubberbandIsAlive() && (nullptr != _internal->_widthItem.get()))
{
if (_internal->_widthItem->isSelected())
{
QPointF scenePos(pixel2ScenePosition(x, y));
QPointF deltaMove = scenePos - _baseInternal->_lastClickPosition;
_internal->_widthItem->setSelection(false);
_internal->_widthItem->moveCoordinate(deltaMove);
update();
return true;
}
}
return RubberbandMultiPointFeature::setCoordinateAction(causer, x, y);
} /* RubberbandPolylineWidth::setCoordinateAction */
bool RubberbandPolylineWidth::movePickedAction( AbstractEventDispatcher * causer, int x, int y )
{
if (rubberbandIsAlive() && (nullptr != _internal->_widthItem.get()))
{
if (_internal->_widthItem->isSelected())
{
adjustConnectedPreviewPixmap(x, y);
QPointF scenePos(pixel2ScenePosition(x, y));
QPointF deltaMove = scenePos - _baseInternal->_lastClickPosition;
_baseInternal->_lastClickPosition = scenePos;
_internal->_widthItem->moveCoordinate(deltaMove);
update();
return false;
}
else
{
RubberbandPointHandle * handlePoint1Ptr = dynamic_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[0].get());
RubberbandPointHandle * handlePoint2Ptr = dynamic_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[1].get());
if (handlePoint1Ptr && handlePoint2Ptr)
{
const RubberbandDrawable::RubberbandCoordinate & loc1Start = handlePoint1Ptr->getCoordinate();
const RubberbandDrawable::RubberbandCoordinate & loc2Start = handlePoint2Ptr->getCoordinate();
bool ret = RubberbandMultiPointFeature::movePickedAction(causer, x, y);
const RubberbandDrawable::RubberbandCoordinate & loc1End = handlePoint1Ptr->getCoordinate();
const RubberbandDrawable::RubberbandCoordinate & loc2End = handlePoint2Ptr->getCoordinate();
QPointF deltaMove1 = loc1End._sceneCoordinate - loc1Start._sceneCoordinate;
QPointF deltaMove2 = loc2End._sceneCoordinate - loc2Start._sceneCoordinate;
if ((fabs(deltaMove1.x()) >= 1) || (fabs(deltaMove1.y()) >= 1) || (fabs(deltaMove2.x()) >= 1) || (fabs(deltaMove2.y()) >= 1))
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* First or second point have been moved. Now recalculate the coordinate of the width handle point as follows:
* The width handle "C" and the first two coordinates ("A","B") are providing a triangle:
* B
* / \
* / \
* /sinB \
* / cosB \
* / \
* / \
* c / \ a
* / \
* / \
* / \
* / \
* / sinA sinC \
* / cosA cosC \
* A ------------------------- C
* b
* If the coordinates of "A" or "B" are changed, try to keep the ratio of angles and sides of the triangle constant. For this
* use some trigonometric identities.
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
auto rotate = []( const QPointF & p, qreal sinPhi, qreal cosPhi ) -> QPointF
{
return QPointF( p.x()*cosPhi - p.y()*sinPhi, p.y()*cosPhi + p.x()*sinPhi );
};
QPointF A = loc2Start._sceneCoordinate;
QPointF B = loc1Start._sceneCoordinate;
QPointF C = _internal->_widthItem->getCoordinate()._sceneCoordinate;
qreal a = QLineF(B,C).length(); // side length of triangle
qreal b = QLineF(A,C).length();
qreal c = QLineF(A,B).length();
qreal cosA = (b*b+c*c-a*a)/(b*c*2); // cosin rule
qreal cosB = (a*a+c*c-b*b)/(a*c*2);
qreal cosC = (a*a+b*b-c*c)/(a*b*2);
qreal sinA = sqrt(1-cosA*cosA); // pythagorean trigonometric identity
qreal sinB = sqrt(1-cosB*cosB);
qreal sinC = sqrt(1-cosC*cosC);
// now we have all angle of the triangle, set A and B to the new coordinates ...
A = loc2End._sceneCoordinate;
B = loc1End._sceneCoordinate;
// ... and calculate the coordinate of C with the same ratio of angles as before ...
QPointF AB = B-A;
// c = QLineF(QPointF(0,0),AB).length();
if (sinC != 0.0)
{
// ... rotate AB and scale it to the right length
C = A + rotate(AB,sinA,cosA) * sinB / sinC;
// the RubberbandPointHandle dosn't has a set to set scene coordinate. Using the moveCoordinate methode instead.
_internal->_widthItem->moveCoordinate( C - _internal->_widthItem->getCoordinate()._sceneCoordinate );
}
// else could only happen when the triangle is degenrated to a line
update();
}
return ret;
}
}
}
return RubberbandMultiPointFeature::movePickedAction(causer, x, y);
} /* RubberbandPolylineWidth::movePickedAction */
bool RubberbandPolylineWidth::moveAction( AbstractEventDispatcher * causer, int x, int y )
{
if (rubberbandIsAlive() && (nullptr != _internal->_widthItem.get()))
{
QPointF scenePos(pixel2ScenePosition(x, y));
_internal->_widthItem->setSelection( _internal->_widthItem->isPositionInside(scenePos) );
}
return RubberbandMultiPointFeature::moveAction( causer, x, y);
} /* RubberbandPolylineWidth::moveAction */
void RubberbandPolylineWidth::update()
{
if ((_internal->_widthItem.get() == nullptr) && (Rubberband::_internal->_handleList.size() > 1))
{
RubberbandPointHandle * pointHandle1Ptr = static_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[0].get());
RubberbandPointHandle * pointHandle2Ptr = static_cast<RubberbandPointHandle*>(Rubberband::_internal->_handleList[1].get());
Esri::ArcGISRuntime::Point p1 = pointHandle1Ptr->getCoordinate()._mapCoordinate;
Esri::ArcGISRuntime::Point p2 = pointHandle2Ptr->getCoordinate()._mapCoordinate;
buildWidthItem(QPointF(p1.x(), p1.y()), QPointF(p2.x(), p2.y()));
}
if (_internal->_widthItem)
{
Rubberband::_internal->_handleList += static_cast<SituationMap::RefPointer<RubberbandHandle>>(_internal->_widthItem);
RubberbandMultiPointFeature::update();
Rubberband::_internal->_handleList.removeLast();
}
else RubberbandMultiPointFeature::update();
} /* RubberbandPolylineWidth::update */
} } /* namespace SituationMap::RB */