456 lines
17 KiB
C++
Executable File
456 lines
17 KiB
C++
Executable File
|
|
/**
|
|
**
|
|
** @RDE_HEADER@
|
|
** Implementation of rubberband class to define point feature geoemtry
|
|
** $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 .........:
|
|
**
|
|
** 17.06.2015, JT : First edition for use with Qt5
|
|
**
|
|
** -----------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "rubberband_ellipse.h"
|
|
#include "drawable_ellipse.h"
|
|
#include "rubberband_point_handle.h"
|
|
|
|
#include "rubberband_internal.h"
|
|
#include "rubberband_base_internal.h"
|
|
|
|
#include <MathUtils/math_const.h>
|
|
#include <MathUtils/simple_helper.h>
|
|
#include <LtsCoordTrans/lts_coord_trans.h>
|
|
|
|
namespace SituationMap { namespace RB
|
|
{
|
|
|
|
struct RubberbandEllipse::InternalData
|
|
{
|
|
SituationMap::RefPointer<DrawableEllipse> _ellipseSymbol;
|
|
int _alphaFill = 50; // alpha value used for fill color of ellipse
|
|
|
|
QPointF _sectorOStart = QPointF(1,0);
|
|
QPointF _sectorOEnd = QPointF(1,0);
|
|
SituationMap::RefPointer<RubberbandPointHandle> _sectorStartHandle;
|
|
SituationMap::RefPointer<RubberbandPointHandle> _sectorEndHandle;
|
|
|
|
bool _sectorEnabled = false;
|
|
bool _sectorFixedAngleEnabled = false;
|
|
|
|
}; /* RubberbandEllipse::InternalData */
|
|
|
|
|
|
RubberbandEllipse::RubberbandEllipse( SituationMap::Map * map, QObject * parent )
|
|
: RubberbandRectangle(map,parent), _internal(new InternalData)
|
|
{
|
|
useDottedLineHandles(true);
|
|
} /* RubberbandEllipse::RubberbandEllipse */
|
|
|
|
|
|
RubberbandEllipse::~RubberbandEllipse()
|
|
{
|
|
delete _internal;
|
|
_internal = nullptr;
|
|
} /* RubberbandEllipse::~RubberbandEllipse */
|
|
|
|
|
|
QPointF RubberbandEllipse::getRotatedPoint(QPointF sector) const
|
|
{
|
|
qreal sinRect = 0, cosRect = 1;
|
|
getSceneRotation( sinRect, cosRect);
|
|
double angleRad = SituationMap::MathUtils::getMathAngle(QPointF(cosRect,sinRect));// angle rad
|
|
//double angleDeg = SituationMap::MathUtils::getMathAngle(QPointF(cosRect,sinRect)) * LtsCoordTrans::rad2deg();
|
|
const double clockwiseAngle = ((360*LtsCoordTrans::deg2rad()) - angleRad);// angle in rad;
|
|
|
|
// map clockwise rotation
|
|
QPointF point = sector;
|
|
point.setX( sector.x()*std::cos(clockwiseAngle) + sector.y()*std::sin(clockwiseAngle));
|
|
point.setY( -sector.x()*std::sin(clockwiseAngle) + sector.y()*std::cos(clockwiseAngle));
|
|
|
|
return point;
|
|
}
|
|
|
|
void RubberbandEllipse::setSectorStart(qreal sinPhi, qreal cosPhi)
|
|
{
|
|
_internal->_sectorOStart = QPointF( cosPhi, sinPhi );
|
|
update();
|
|
} /* RubberbandEllipse::setSectorStart */
|
|
|
|
|
|
void RubberbandEllipse::setSectorEnd(qreal sinPhi, qreal cosPhi)
|
|
{
|
|
_internal->_sectorOEnd = QPointF( cosPhi, sinPhi );
|
|
update();
|
|
} /* RubberbandEllipse::setSectorEnd */
|
|
|
|
|
|
QPointF RubberbandEllipse::sectorStart() const
|
|
{
|
|
return getRotatedPoint(_internal->_sectorOStart);
|
|
} /* RubberbandEllipse::sectorStart */
|
|
|
|
|
|
QPointF RubberbandEllipse::sectorEnd() const
|
|
{
|
|
return getRotatedPoint(_internal->_sectorOEnd);
|
|
} /* RubberbandEllipse::sectorEnd */
|
|
|
|
|
|
bool RubberbandEllipse::hasSector() const
|
|
{
|
|
auto getAngle = [](qreal sinPhi, qreal cosPhi) -> int
|
|
{
|
|
qreal phi = acos( cosPhi );
|
|
if (sinPhi < 0) phi = MathConst::pi_two - phi;
|
|
return int( (phi * 180.0 / MathConst::pi) * 16);
|
|
}; // getAngle
|
|
|
|
int phiStart = getAngle(_internal->_sectorOStart.y(),_internal->_sectorOStart.y());
|
|
int phiEnd = getAngle( _internal->_sectorOEnd.y(), _internal->_sectorOEnd.y());
|
|
return phiStart != phiEnd;
|
|
} /* RubberbandEllipse::hasSector */
|
|
|
|
void RubberbandEllipse::enableSector(bool on) { _internal -> _sectorEnabled = on; }
|
|
bool RubberbandEllipse::isSectorEnabled() const { return _internal -> _sectorEnabled; }
|
|
|
|
void RubberbandEllipse::setSectorAngleFixed(bool on)
|
|
{
|
|
_internal -> _sectorFixedAngleEnabled = on;
|
|
}
|
|
|
|
bool RubberbandEllipse::isSectorFixedAngeEnabled() const
|
|
{
|
|
return _internal->_sectorFixedAngleEnabled;
|
|
}
|
|
|
|
|
|
bool RubberbandEllipse::selectAction(AbstractEventDispatcher * causer, int x, int y)
|
|
{
|
|
if (rubberbandIsAlive())
|
|
{
|
|
QPointF scenePos(pixel2ScenePosition(x,y));
|
|
|
|
_baseInternal->_lastClickPosition = scenePos;
|
|
|
|
if (_internal -> _sectorEnabled)
|
|
{
|
|
if ((_internal->_sectorEndHandle.valid()) && (_internal->_sectorEndHandle->isPositionInside(scenePos)))
|
|
{
|
|
_internal->_sectorEndHandle->setSelection(true);
|
|
enableSelectedState();
|
|
update();
|
|
deactivateButtons();
|
|
return true;
|
|
}
|
|
|
|
if ((_internal->_sectorStartHandle.valid()) && (_internal->_sectorStartHandle->isPositionInside(scenePos)))
|
|
{
|
|
_internal->_sectorStartHandle->setSelection(true);
|
|
enableSelectedState();
|
|
update();
|
|
deactivateButtons();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return RubberbandRectangle::selectAction(causer,x,y);
|
|
} /* RubberbandEllipse::selectAction */
|
|
|
|
|
|
bool RubberbandEllipse::moveAction(AbstractEventDispatcher * causer, int x, int y)
|
|
{
|
|
if (rubberbandIsAlive())
|
|
{
|
|
QPointF scenePos(pixel2ScenePosition(x,y));
|
|
if (_internal -> _sectorEnabled)
|
|
{
|
|
if (_internal->_sectorEndHandle.valid())
|
|
{
|
|
if (_internal->_sectorEndHandle->isPositionInside(scenePos))
|
|
{
|
|
_internal->_sectorEndHandle->setSelection(true);
|
|
update();
|
|
return false;
|
|
}
|
|
else _internal->_sectorEndHandle->setSelection(false);
|
|
}
|
|
|
|
if (_internal->_sectorStartHandle.valid())
|
|
{
|
|
if (_internal->_sectorStartHandle->isPositionInside(scenePos))
|
|
{
|
|
_internal->_sectorStartHandle->setSelection(true);
|
|
update();
|
|
return false;
|
|
}
|
|
else _internal->_sectorStartHandle->setSelection(false);
|
|
}
|
|
}
|
|
}
|
|
return RubberbandRectangle::moveAction(causer,x,y);
|
|
} /* RubberbandEllipse::moveAction */
|
|
|
|
|
|
bool RubberbandEllipse::setCoordinateAction(AbstractEventDispatcher * causer, int x, int y)
|
|
{
|
|
if (rubberbandIsAlive())
|
|
{
|
|
if (_internal -> _sectorEnabled)
|
|
{
|
|
if ((_internal->_sectorEndHandle.valid()) && (_internal->_sectorEndHandle->isSelected()))
|
|
{
|
|
_internal->_sectorEndHandle->setSelection(false);
|
|
update();
|
|
activateButtons();
|
|
return true;
|
|
}
|
|
|
|
if ((_internal->_sectorStartHandle.valid()) && (_internal->_sectorStartHandle->isSelected()))
|
|
{
|
|
_internal->_sectorStartHandle->setSelection(false);
|
|
update();
|
|
activateButtons();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return RubberbandRectangle::setCoordinateAction(causer,x,y);
|
|
} /* RubberbandEllipse::setCoordinateAction */
|
|
|
|
|
|
bool RubberbandEllipse::movePickedAction(AbstractEventDispatcher * causer, int x, int y)
|
|
{
|
|
if (rubberbandIsAlive())
|
|
{
|
|
auto updateHandle = [this](RubberbandPointHandle * handle, const RubberbandHandle::RubberbandCoordinate & pos, QPointF & oVector ) -> bool
|
|
{
|
|
if (handle == nullptr) return false;
|
|
if (!handle->isSelected()) return false;
|
|
if (isSectorFixedAngeEnabled()) return false;
|
|
|
|
qreal sinRect = 0;
|
|
qreal cosRect = 1;
|
|
const QPointF & center = getCenter();
|
|
|
|
getSceneRotation( sinRect, cosRect);
|
|
|
|
qreal cosA = pos._mapCoordinate.x() - center.x();
|
|
qreal sinA = pos._mapCoordinate.y() - center.y();
|
|
qreal len = sqrt(cosA*cosA+sinA*sinA);
|
|
|
|
cosA /= len;
|
|
sinA /= len;
|
|
|
|
oVector.setX( cosA*cosRect + sinA*sinRect );
|
|
oVector.setY( sinA*cosRect - cosA*sinRect );
|
|
update();
|
|
return true;
|
|
}; // updateHandle
|
|
|
|
QPointF scenePos(pixel2ScenePosition(x,y));
|
|
|
|
RubberbandHandle::RubberbandCoordinate pos(Rubberband::_internal->_mapView, scenePos );
|
|
|
|
if (updateHandle( _internal->_sectorEndHandle.get() , pos, _internal->_sectorOEnd )) return false;
|
|
if (updateHandle( _internal->_sectorStartHandle.get(), pos, _internal->_sectorOStart )) return false;
|
|
}
|
|
return RubberbandRectangle::movePickedAction(causer,x,y);
|
|
} /* RubberbandEllipse::movePickedAction */
|
|
|
|
|
|
void RubberbandEllipse::updateSectorHandles()
|
|
{
|
|
|
|
auto ellipse = []( qreal a, qreal b, qreal sinA, qreal cosA ) -> QPointF
|
|
{
|
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
*
|
|
* Qt does not use the paramater equation of a ellipse to draw sectors. Instead of a simpler term
|
|
* is used. For simpleness we use the same of.
|
|
*
|
|
qreal e2 = 1 - (b*b) / (a*a);
|
|
qreal cos2 = cosA * cosA;
|
|
qreal f = b / sqrt(1-e2*cos2);
|
|
return QPointF( f*cosA, f*sinA );
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
|
|
|
return QPointF(a*cosA,b*sinA);
|
|
};
|
|
|
|
auto rotate = [] ( const QPointF & p, qreal sinA, qreal cosA ) -> QPointF { return QPointF( p.x()*cosA - p.y()*sinA, p.y()*cosA + p.x()*sinA ); };
|
|
|
|
qreal a = getWidth() * 0.5;
|
|
qreal b = getHeight() * 0.5;
|
|
QPointF startP = ellipse(a,b,_internal->_sectorOStart.y(),_internal->_sectorOStart.x() );
|
|
QPointF endP = ellipse(a,b,_internal->_sectorOEnd.y() ,_internal->_sectorOEnd.x() );
|
|
|
|
qreal sinRect = 0;
|
|
qreal cosRect = 1;
|
|
|
|
getSceneRotation( sinRect, cosRect);
|
|
|
|
startP = rotate(startP, sinRect, cosRect) + getCenter();
|
|
endP = rotate( endP, sinRect, cosRect) + getCenter();
|
|
|
|
const Esri::ArcGISRuntime::Point &mapStartP = convert( startP, Rubberband::_internal->_mapView->map()->spatialReference() );
|
|
const Esri::ArcGISRuntime::Point &mapEndP = convert( endP, Rubberband::_internal->_mapView->map()->spatialReference() );
|
|
|
|
if (!_internal->_sectorStartHandle.valid())
|
|
{
|
|
_internal->_sectorStartHandle = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapStartP );
|
|
}
|
|
else _internal->_sectorStartHandle->setCoordinate( RubberbandHandle::RubberbandCoordinate(Rubberband::_internal->_mapView,mapStartP) );
|
|
|
|
_internal->_sectorStartHandle -> setVisibility((isSectorEnabled() && !isSectorFixedAngeEnabled())/*_internal->_sectorEnabled*/);
|
|
_internal->_sectorStartHandle -> setDrawColor(_handleDrawColor);
|
|
_internal->_sectorStartHandle -> setFillColor( _handleFillColor );
|
|
_internal->_sectorStartHandle -> setSelectionColor(_handleSelectionColor);
|
|
|
|
if (!_internal->_sectorEndHandle.valid())
|
|
{
|
|
_internal->_sectorEndHandle = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, mapEndP );
|
|
}
|
|
else _internal->_sectorEndHandle->setCoordinate( RubberbandHandle::RubberbandCoordinate(Rubberband::_internal->_mapView,mapEndP) );
|
|
|
|
_internal->_sectorEndHandle -> setVisibility(isSectorEnabled() && !isSectorFixedAngeEnabled()/*_internal->_sectorEnabled*/);
|
|
_internal->_sectorEndHandle -> setDrawColor(_handleDrawColor);
|
|
_internal->_sectorEndHandle -> setFillColor( _handleFillColor );
|
|
_internal->_sectorEndHandle -> setSelectionColor(_handleSelectionColor);
|
|
|
|
} /* RubberbandEllipse::updateSectorHandles */
|
|
|
|
|
|
void RubberbandEllipse::update()
|
|
{
|
|
|
|
RubberbandRectangle::update();
|
|
|
|
if (isEnabledModificationState())
|
|
{
|
|
updateSectorHandles();
|
|
|
|
QPointF center = getCenter();
|
|
qreal width2 = getWidth()*0.5;
|
|
qreal height2 = getHeight()*0.5;
|
|
qreal sinRect = 0.0;
|
|
qreal cosRect = 1.0;
|
|
|
|
getSceneRotation( sinRect, cosRect );
|
|
|
|
if (_internal->_ellipseSymbol == nullptr)
|
|
{
|
|
_internal->_ellipseSymbol = new DrawableEllipse( Rubberband::_internal->_mapView );
|
|
}
|
|
|
|
QColor drawColor = _drawColor;
|
|
|
|
_internal->_ellipseSymbol->setDrawColor( drawColor );
|
|
drawColor.setAlpha(_internal->_alphaFill);
|
|
if (_internal -> _sectorEnabled){
|
|
_fillColor.setAlpha(_internal->_alphaFill+10);
|
|
_internal->_ellipseSymbol->setFillColor( _fillColor );
|
|
}
|
|
else
|
|
{
|
|
_fillColor.setAlpha(0);
|
|
_internal->_ellipseSymbol->setFillColor( _fillColor );
|
|
}
|
|
|
|
_internal->_ellipseSymbol->setRect( center, width2, height2, sinRect, cosRect, _internal->_sectorOStart, _internal->_sectorOEnd );
|
|
|
|
}
|
|
} /* RubberbandEllipse::update */
|
|
|
|
|
|
void RubberbandEllipse::buildTmpConstruction( SituationMap::RefPointer<RubberbandDrawable> &tmpGraphic, QPointF &start, QPointF &end )
|
|
{
|
|
if (!tmpGraphic.valid())
|
|
{
|
|
tmpGraphic = new DrawableEllipse( Rubberband::_internal->_mapView );
|
|
tmpGraphic -> setDrawColor( _drawColor );
|
|
tmpGraphic -> setFillColor( _fillColor );
|
|
tmpGraphic -> setLineWidth( 1 );
|
|
tmpGraphic -> zBufferPlane( RubberbandDrawable::ZBufferPlane::Drawables );
|
|
}
|
|
|
|
QVector<QPointF> path;
|
|
QPointF sceneCenter;
|
|
qreal sceneWidth;
|
|
qreal sceneHeight;
|
|
qreal sinMap;
|
|
qreal cosMap;
|
|
|
|
getMapRotation( sinMap, cosMap );
|
|
|
|
auto rotate = [sinMap, cosMap](qreal x, qreal y) -> QPointF
|
|
{
|
|
qreal px = x*cosMap - y*sinMap;
|
|
qreal py = y*cosMap + x*sinMap;
|
|
return QPointF(px,py);
|
|
}; // rotate
|
|
|
|
buildTmpConstructionPath( start-end + start, end, path, sceneCenter, sceneWidth, sceneHeight );
|
|
|
|
DrawableEllipse * tmpEllipse = static_cast<DrawableEllipse*>(tmpGraphic.get());
|
|
|
|
RubberbandHandle::RubberbandCoordinate mapCenter( Rubberband::_internal->_mapView, start );
|
|
RubberbandHandle::RubberbandCoordinate mapRadius( Rubberband::_internal->_mapView, start+rotate(sceneWidth*0.5,sceneHeight*0.5) );
|
|
|
|
qreal w = mapRadius._mapCoordinate.x() - mapCenter._mapCoordinate.x();
|
|
qreal h = mapRadius._mapCoordinate.y() - mapCenter._mapCoordinate.y();
|
|
|
|
tmpEllipse->setRect( QPointF(mapCenter._mapCoordinate.x(),mapCenter._mapCoordinate.y()), w, h, 0, 1 );
|
|
} /* RubberbandEllipse::buildTmpConstruction */
|
|
|
|
|
|
void RubberbandEllipse::getTmpConstructionParameter(const QPointF & start, const QPointF & end, QPointF & sceneCenter, qreal & sceneWidth, qreal & sceneHeight)
|
|
{
|
|
QVector<QPointF> path;
|
|
qreal sinMap;
|
|
qreal cosMap;
|
|
|
|
getMapRotation( sinMap, cosMap );
|
|
|
|
auto rotate = [sinMap, cosMap]( qreal x, qreal y ) -> QPointF
|
|
{
|
|
qreal px = x*cosMap - y*sinMap;
|
|
qreal py = y*cosMap + x*sinMap;
|
|
return QPointF(px,py);
|
|
}; // rotate
|
|
|
|
auto length = [](qreal x, qreal y) -> qreal { return sqrt(x*x+y*y); };
|
|
|
|
buildTmpConstructionPath( start, end, path, sceneCenter, sceneWidth, sceneHeight );
|
|
|
|
RubberbandHandle::RubberbandCoordinate mapCenter ( Rubberband::_internal->_mapView, start );
|
|
RubberbandHandle::RubberbandCoordinate mapRadiusX( Rubberband::_internal->_mapView, start+rotate(sceneWidth,0) );
|
|
RubberbandHandle::RubberbandCoordinate mapRadiusY( Rubberband::_internal->_mapView, start+rotate(0,sceneHeight) );
|
|
|
|
qreal w = length( mapRadiusX._sceneCoordinate.x()-mapCenter._sceneCoordinate.x(), mapRadiusX._sceneCoordinate.y()-mapCenter._sceneCoordinate.y() );
|
|
qreal h = length( mapRadiusY._sceneCoordinate.x()-mapCenter._sceneCoordinate.x(), mapRadiusY._sceneCoordinate.y()-mapCenter._sceneCoordinate.y() );
|
|
|
|
sceneCenter = start;
|
|
sceneWidth = 2*w;
|
|
sceneHeight = 2*h;
|
|
} /* RubberbandEllipse::getTmpConstructionParameter */
|
|
|
|
|
|
} } /* namespace SituationMap::RB */
|