319 lines
13 KiB
C++
Executable File
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 */
|