573 lines
19 KiB
C++
Executable File
573 lines
19 KiB
C++
Executable File
|
|
/**
|
|
**
|
|
** @RDE_HEADER@
|
|
** Implementation of rubberband class to edit states of point objects
|
|
** $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 .........:
|
|
**
|
|
** 19.10.2015, JT : First edition for use with Qt5
|
|
**
|
|
** -----------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "rubberband_state_select.h"
|
|
#include "rubberband_point_handle.h"
|
|
#include "rubberband_drawable.h"
|
|
#include "rubberband_handle_proxy.h"
|
|
#include "rubberband_factory.h"
|
|
|
|
#include "rubberband_internal.h"
|
|
#include "rubberband_base_internal.h"
|
|
|
|
|
|
#include <arcgis/Map.h>
|
|
#include <arcgis/MapGraphicsView.h>
|
|
|
|
#include <QtGui/QCursor>
|
|
#include <QtWidgets/QPushButton>
|
|
#include <QtWidgets/QHBoxLayout>
|
|
#include <QtWidgets/QMenu>
|
|
|
|
#include "polygon.xpm"
|
|
|
|
|
|
namespace SituationMap { namespace RB
|
|
{
|
|
|
|
struct RubberbandStateSelect::InternalData
|
|
{
|
|
Q_DISABLE_COPY(InternalData)
|
|
|
|
InternalData() : _logger(getLogger("RubberbandStateSelect"))
|
|
{}
|
|
|
|
~InternalData()
|
|
{
|
|
delete _stateMenu;
|
|
}
|
|
|
|
QVector<State> _states;
|
|
QVector<StateItem> _items;
|
|
QList<SituationMap::RefPointer<RubberbandHandle>> _stateMenuSelectedHandleList;
|
|
LOGGER_T _logger;
|
|
|
|
const State * getStateFromValue( int stateValue ) const
|
|
{
|
|
for (const State &state : _states)
|
|
{
|
|
if (state.value() == stateValue) return &state;
|
|
}
|
|
return nullptr;
|
|
} /* getStateFromValue */
|
|
|
|
const State * getStateFromItem( const StateItem & item ) const { return getStateFromValue(item.stateValue()); }
|
|
|
|
static QPixmap * _polygonIcon;
|
|
|
|
QPushButton * _polygonButton = nullptr;
|
|
QMenu * _stateMenu = nullptr;
|
|
bool _stateMenuIsActive = false;
|
|
|
|
}; /* RubberbandMultiPointFeature::InternalData */
|
|
|
|
|
|
QPixmap * RubberbandStateSelect::InternalData::_polygonIcon = nullptr;
|
|
|
|
void RubberbandStateSelect::setIconPolygon(const QPixmap & icon)
|
|
{
|
|
if (InternalData::_polygonIcon == nullptr) InternalData::_polygonIcon = new QPixmap(icon);
|
|
else *InternalData::_polygonIcon = icon;
|
|
} /* RubberbandStateSelect::setIconPolygon */
|
|
|
|
|
|
RubberbandStateSelect::RubberbandStateSelect( SituationMap::Map * map, QObject * parent )
|
|
: Rubberband(map,parent), _internal(new InternalData())
|
|
{
|
|
delete RubberbandBase::_baseInternal->_undoButton;
|
|
RubberbandBase::_baseInternal->_undoButton = nullptr;
|
|
|
|
_internal->_polygonButton = new QPushButton( (InternalData::_polygonIcon) ? *InternalData::_polygonIcon : QPixmap(polygon_xpm), QString(), RubberbandBase::_baseInternal->_buttonBox );
|
|
|
|
RubberbandBase::_baseInternal->_buttonLayout->insertWidget( 0, _internal->_polygonButton );
|
|
|
|
connect( _internal->_polygonButton, &QPushButton::clicked, this, &RubberbandStateSelect::onDo_areaSelectionBegin );
|
|
|
|
// Check Quartz
|
|
connect( Rubberband::_internal->_mapView.data(), &Esri::ArcGISRuntime::MapGraphicsView::viewpointChanged,
|
|
this, &RubberbandStateSelect::onDo_updateButtonFixCoordinates );
|
|
connect( Rubberband::_internal->_mapView.data(), &Esri::ArcGISRuntime::MapGraphicsView::rectChanged ,
|
|
this, &RubberbandStateSelect::onDo_updateButtonFixCoordinates );
|
|
|
|
|
|
setFixedButtonBoxPosition(QPoint(5,5));
|
|
|
|
} /* RubberbandStateSelect::RubberbandStateSelect */
|
|
|
|
|
|
bool RubberbandStateSelect::setStateItems( const QVector<State> & stateVector
|
|
, const QVector<StateItem> & itemVector
|
|
, CoordinateSystem cs )
|
|
{
|
|
if (stateVector.size() < 2) return false;
|
|
if (itemVector.isEmpty()) return false;
|
|
|
|
// check for all states are valid
|
|
for (const State & state : qAsConst(_internal->_states))
|
|
{
|
|
if (!state.isValid())
|
|
return false;
|
|
}
|
|
|
|
bool ok = true;
|
|
|
|
_internal -> _states = stateVector;
|
|
_internal -> _items = itemVector;
|
|
|
|
for (QVector<StateItem>::iterator it = _internal->_items.begin(); it != _internal->_items.end(); ++it)
|
|
{
|
|
if (it->isValid())
|
|
{
|
|
const State * statePtr = _internal->getStateFromItem( *it );
|
|
|
|
if (statePtr == nullptr)
|
|
{
|
|
ok = false;
|
|
statePtr = & _internal->_states.first();
|
|
}
|
|
|
|
const QPointF &mapPos = projectCoordinateToMap( cs, it->coordinate() );
|
|
|
|
Esri::ArcGISRuntime::Point mapCoordinate( mapPos.x(), mapPos.y(), Rubberband::_internal->_mapView->map()->spatialReference() );
|
|
RubberbandPointHandle * handlePtr = new RubberbandPointHandle( Rubberband::_internal->_mapView
|
|
, Rubberband::_internal->_mapScene
|
|
, mapCoordinate
|
|
, statePtr->icon() );
|
|
if (handlePtr)
|
|
{
|
|
handlePtr->setSelection(false);
|
|
handlePtr->setUserData( &(*it) );
|
|
Rubberband::_internal->_handleList.append( SituationMap::RefPointer<RubberbandHandle>(handlePtr) );
|
|
}
|
|
}
|
|
else ok = false;
|
|
}
|
|
update();
|
|
activateButtons();
|
|
buildStateMenu();
|
|
return ok;
|
|
} /* RubberbandStateSelect::setStateItems */
|
|
|
|
|
|
const QVector<RubberbandStateSelect::StateItem> & RubberbandStateSelect::stateItems() const
|
|
{
|
|
return _internal->_items;
|
|
} /* RubberbandStateSelect::stateItems */
|
|
|
|
|
|
void RubberbandStateSelect::activateButtons()
|
|
{
|
|
_baseInternal->_hasUndoButton = false;
|
|
_baseInternal->_applyIsPossible = (!Rubberband::_internal->_handleList.isEmpty());
|
|
_baseInternal->_undoIsPossible = false;
|
|
Rubberband::activateButtons();
|
|
} /* RubberbandStateSelect::activateButton */
|
|
|
|
|
|
void RubberbandStateSelect::toggleState( RubberbandPointHandle * handlePtr )
|
|
{
|
|
if (_internal->_states.size() < 2) return;
|
|
if (handlePtr == nullptr) return;
|
|
|
|
StateItem * itemPtr = static_cast<StateItem*>(handlePtr->userData());
|
|
|
|
if (itemPtr == nullptr) return;
|
|
|
|
if (_internal->_states.size() == 2)
|
|
{
|
|
const State * statePtr = _internal -> getStateFromItem( *itemPtr );
|
|
|
|
if (statePtr != nullptr)
|
|
{
|
|
if (statePtr == &_internal->_states.first()) statePtr = &_internal->_states.last();
|
|
else statePtr = &_internal->_states.first();
|
|
}
|
|
else statePtr = &_internal->_states.first();
|
|
|
|
itemPtr->_stateValue = statePtr->value();
|
|
handlePtr->setIcon( statePtr->icon() );
|
|
}
|
|
|
|
} /* RubberbandStateSelect::toggleState */
|
|
|
|
|
|
void RubberbandStateSelect::setState( RubberbandPointHandle * handlePtr, int stateValue )
|
|
{
|
|
if (handlePtr == nullptr) return;
|
|
|
|
const State * statePtr = _internal -> getStateFromValue(stateValue);
|
|
StateItem * itemPtr = static_cast<StateItem*>(handlePtr->userData());
|
|
|
|
if (itemPtr == nullptr) return;
|
|
if (statePtr == nullptr) return;
|
|
|
|
itemPtr->_stateValue = stateValue;
|
|
handlePtr->setIcon( statePtr->icon() );
|
|
|
|
} /* RubberbandStateSelect::setState */
|
|
|
|
|
|
void RubberbandStateSelect::buildStateMenu()
|
|
{
|
|
if (_internal->_states.isEmpty()) return;
|
|
|
|
_internal->_stateMenu = new QMenu();
|
|
|
|
for (const State & state : qAsConst(_internal->_states))
|
|
{
|
|
QAction * action = _internal->_stateMenu->addAction( state.icon(), state.description() );
|
|
action -> setData( state.value() );
|
|
|
|
connect( action, &QAction::triggered, this, &RubberbandStateSelect::onDo_setState );
|
|
}
|
|
|
|
connect( _internal->_stateMenu, &QMenu::aboutToShow, this, [this]()
|
|
{
|
|
this->_internal->_stateMenuSelectedHandleList.clear();
|
|
|
|
_baseInternal->_buttonBox->setDisabled(true);
|
|
for (auto handle : qAsConst(Rubberband::_internal->_handleList))
|
|
{
|
|
if (handle->isSelected())
|
|
{
|
|
this->_internal->_stateMenuSelectedHandleList << handle;
|
|
}
|
|
}
|
|
} );
|
|
|
|
connect( _internal->_stateMenu, &QMenu::aboutToHide, this, [this]()
|
|
{
|
|
_baseInternal->_buttonBox->setEnabled(true);
|
|
deSelectHandles();
|
|
} );
|
|
|
|
} /* RubberbandStateSelect::buildStateMenu */
|
|
|
|
|
|
void RubberbandStateSelect::onDo_setState(bool)
|
|
{
|
|
QAction * action = (QAction*) sender();
|
|
|
|
if (action == nullptr) return;
|
|
|
|
int stateValue = action->data().toInt();
|
|
|
|
for (auto handle : qAsConst(_internal->_stateMenuSelectedHandleList))
|
|
setState( dynamic_cast<RubberbandPointHandle*>(handle.get()), stateValue );
|
|
|
|
_internal -> _stateMenuSelectedHandleList.clear();
|
|
_internal -> _stateMenuIsActive = false;
|
|
|
|
update();
|
|
|
|
} /* RubberbandStateSelect::onDo_setState */
|
|
|
|
|
|
void RubberbandStateSelect::chooseStateMenu()
|
|
{
|
|
// popup a window with states the user can choose
|
|
_internal->_stateMenu->popup( QCursor::pos() );
|
|
_internal -> _stateMenuIsActive = true;
|
|
} /* RubberbandStateSelect::chooseStateMenu */
|
|
|
|
|
|
bool RubberbandStateSelect::selectAction( AbstractEventDispatcher*, int x, int y )
|
|
{
|
|
DEBUG_LOG(_internal->_logger) << LOGFUNCTION << x << " ; " << y;
|
|
|
|
_internal->_stateMenuSelectedHandleList.clear();
|
|
|
|
if (rubberbandIsAlive())
|
|
{
|
|
if (_internal->_stateMenuIsActive) return true;
|
|
|
|
QPointF scenePos(pixel2ScenePosition(x,y));
|
|
|
|
if (_baseInternal->buttonProxyActive(scenePos)) return false;
|
|
|
|
RubberbandHandle * handlePtr = pickHandle(x,y);
|
|
|
|
if (handlePtr)
|
|
{
|
|
handlePtr->setSelection();
|
|
enableSelectedState();
|
|
|
|
update();
|
|
deactivateButtons();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
} /* RubberbandStateSelect::selectAction */
|
|
|
|
|
|
bool RubberbandStateSelect::multiSelectActionPress(AbstractEventDispatcher*, int x, int y)
|
|
{
|
|
DEBUG_LOG(_internal->_logger) << LOGFUNCTION << x << " ; " << y;
|
|
|
|
if (rubberbandIsAlive())
|
|
{
|
|
if (_internal -> _stateMenuIsActive) return true;
|
|
|
|
RubberbandHandle * handlePtr = pickHandle(x,y);
|
|
|
|
if (handlePtr)
|
|
{
|
|
handlePtr->setSelection();
|
|
enableSelectedState();
|
|
enableMultiSelectedState();
|
|
|
|
update();
|
|
deactivateButtons();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
} /* RubberbandStateSelect::multiSelectActionPress */
|
|
|
|
|
|
bool RubberbandStateSelect::multiSelectActionRelease( AbstractEventDispatcher*, int, int )
|
|
{
|
|
return false;
|
|
} /* RubberbandStateSelect::multiSelectActionRelease */
|
|
|
|
|
|
bool RubberbandStateSelect::setCoordinateAction( AbstractEventDispatcher * causer, int x, int y )
|
|
{
|
|
DEBUG_LOG(_internal->_logger) << LOGFUNCTION << x << " ; " << y;
|
|
|
|
if (rubberbandIsAlive())
|
|
{
|
|
Rubberband::_internal->_lastClickPosition = pixel2ScenePosition(x,y);
|
|
|
|
if (_internal->_stateMenuIsActive)
|
|
{
|
|
_internal -> _stateMenuIsActive = false;
|
|
disenableSelectedState();
|
|
disenableMultiSelectedState();
|
|
activateButtons();
|
|
return true;
|
|
}
|
|
|
|
if (isEnabledSelectedState())
|
|
{
|
|
bool handlePicked = (pickHandle(x,y) != nullptr);
|
|
bool toggleMode = (_internal->_states.size() == 2);
|
|
bool eventHandled = false;
|
|
|
|
if (!toggleMode)
|
|
{
|
|
disenableSelectedState();
|
|
disenableMultiSelectedState();
|
|
|
|
chooseStateMenu();
|
|
return true;
|
|
}
|
|
for (auto handle : qAsConst(Rubberband::_internal->_handleList))
|
|
{
|
|
RubberbandPointHandle * handlePointPtr = dynamic_cast<RubberbandPointHandle*>(handle.get());
|
|
if ((handlePointPtr) && (handlePointPtr->isSelected()))
|
|
{
|
|
handlePointPtr->setSelection(false);
|
|
if ((handlePicked) && (toggleMode)) toggleState( handlePointPtr );
|
|
eventHandled = true;
|
|
}
|
|
}
|
|
|
|
disenableSelectedState();
|
|
disenableMultiSelectedState();
|
|
activateButtons();
|
|
return eventHandled;
|
|
}
|
|
else Rubberband::setCoordinateAction(causer,x,y);
|
|
}
|
|
return false;
|
|
} /* RubberbandStateSelect::setCoordinateAction */
|
|
|
|
|
|
bool RubberbandStateSelect::moveAction(AbstractEventDispatcher*, int x, int y)
|
|
{
|
|
DEBUG_LOG(_internal->_logger) << LOGFUNCTION << x << " ; " << y;
|
|
|
|
if (rubberbandIsAlive())
|
|
{
|
|
if (_internal -> _stateMenuIsActive) return false;
|
|
|
|
bool eventHandled = false;
|
|
|
|
QPointF scenePosition(pixel2ScenePosition(x,y));
|
|
|
|
if (_baseInternal->buttonProxyActive(scenePosition)) return false;
|
|
|
|
if (Rubberband::_internal->_mapView != nullptr)
|
|
{
|
|
if (!isEnabledMultiSelectedState()) deSelectHandles();
|
|
|
|
RubberbandHandle * handlePtr = pickHandle(x,y);
|
|
if (handlePtr != nullptr)
|
|
{
|
|
handlePtr->setSelection();
|
|
eventHandled = true;
|
|
}
|
|
}
|
|
|
|
if (eventHandled) update();
|
|
}
|
|
return false;
|
|
} /* RubberbandStateSelect::moveAction */
|
|
|
|
|
|
bool RubberbandStateSelect::movePickedAction(AbstractEventDispatcher * causer, int x, int y )
|
|
{
|
|
DEBUG_LOG(_internal->_logger) << LOGFUNCTION << x << " ; " << y;
|
|
|
|
Rubberband::movePickedAction(causer,x,y);
|
|
return false; // isEnabledSelectedState() || _internal->_stateMenuIsActive;
|
|
} /* RubberbandStateSelect::movePickedAction */
|
|
|
|
|
|
bool RubberbandStateSelect::contextMenuAction( AbstractEventDispatcher*, int, int )
|
|
{
|
|
if (!_internal -> _stateMenuIsActive) chooseStateMenu();
|
|
return true;
|
|
} /* RubberbandStateSelect::contextMenuAction */
|
|
|
|
|
|
void RubberbandStateSelect::onDo_areaSelectionBegin()
|
|
{
|
|
bool mRbands = _baseInternal->_map->rubberbandFactory().allowMultipleRubberbands();
|
|
_baseInternal->_map->rubberbandFactory().allowMultipleRubberbands(true);
|
|
{
|
|
RB::RubberbandMultiPointFeature * selectionRB = _baseInternal->_map->rubberbandFactory().create<RB::RubberbandMultiPointFeature>(this);
|
|
|
|
if (selectionRB == nullptr)
|
|
{
|
|
qWarning() << LOGFUNCTION << "Creating rubberband multi points feature failed";
|
|
}
|
|
else
|
|
{
|
|
selectionRB->setAutoDelete(true); // when finished rubberband will commit suicide
|
|
selectionRB->setDrawColor( QColor(255,0,0,255) );
|
|
selectionRB->setFillColor( QColor( 0,0,0,100) );
|
|
|
|
selectionRB -> enableClosed( true );
|
|
selectionRB -> enableFinishAfterConstruction( true );
|
|
|
|
selectionRB -> setFixedButtonBoxPosition(QPoint(15,15));
|
|
|
|
QObject::connect( selectionRB, &RB::Rubberband::apply, this, &RubberbandStateSelect::onDo_areaSelectionApply );
|
|
QObject::connect( selectionRB, &RB::Rubberband::abort, this, &RubberbandStateSelect::onDo_areaSelectionAbort );
|
|
|
|
RubberbandBase::_baseInternal->_buttonBox->setDisabled(true);
|
|
}
|
|
}
|
|
_baseInternal->_map->rubberbandFactory().allowMultipleRubberbands( mRbands );
|
|
} /* RubberbandStateSelect::onDo_areaSelectionBegin */
|
|
|
|
|
|
void RubberbandStateSelect::onDo_areaSelectionApply( RubberbandBase * selectionRB )
|
|
{
|
|
RubberbandBase::_baseInternal->_buttonBox->setEnabled(true);
|
|
if (selectionRB)
|
|
{
|
|
QVector<QPointF> polygonVertexList = selectionRB->coordinateVector( CoordinateSystem::Map );
|
|
|
|
// Looking for all handle which are inside polygon and select
|
|
|
|
unsigned int hitCounter = 0;
|
|
|
|
for (auto handle : qAsConst(Rubberband::_internal->_handleList))
|
|
{
|
|
RubberbandPointHandle * handlePointPtr = dynamic_cast<RubberbandPointHandle*>(handle.get());
|
|
if (handlePointPtr)
|
|
{
|
|
const Esri::ArcGISRuntime::Point &v = handlePointPtr->getCoordinate()._mapCoordinate;
|
|
bool inside = false;
|
|
int nvert = polygonVertexList.size();
|
|
|
|
for (int i = 0, j = nvert-1; i < nvert; j = i++)
|
|
{
|
|
const QPointF & vertex_i = polygonVertexList[i];
|
|
const QPointF & vertex_j = polygonVertexList[j];
|
|
|
|
if ((vertex_i.y()>v.y()) != (vertex_j.y()>v.y()))
|
|
{
|
|
if (v.x() < (vertex_j.x() - vertex_i.x()) * (v.y() - vertex_i.y()) / (vertex_j.y() - vertex_i.y()) + vertex_i.x())
|
|
{
|
|
inside = !inside;
|
|
}
|
|
}
|
|
}
|
|
if (inside)
|
|
{
|
|
hitCounter++;
|
|
handlePointPtr->setSelection();
|
|
}
|
|
}
|
|
}
|
|
if (hitCounter > 0)
|
|
{
|
|
if (_internal->_states.size() == 2)
|
|
{
|
|
for (auto handle : qAsConst(Rubberband::_internal->_handleList))
|
|
{
|
|
if (!handle->isSelected()) continue;
|
|
handle->setSelection(false);
|
|
toggleState( dynamic_cast<RubberbandPointHandle*>(handle.get()) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
chooseStateMenu();
|
|
// ????? enableMultiSelectedState();
|
|
}
|
|
}
|
|
}
|
|
} /* RubberbandStateSelect::onDo_areaSelectionApply */
|
|
|
|
|
|
void RubberbandStateSelect::onDo_areaSelectionAbort( RubberbandBase * )
|
|
{
|
|
_baseInternal->_buttonBox->setEnabled(true);
|
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
* Really nothing else to do here?
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
|
} /* RubberbandStateSelect::onDo_areaSelectionAbort */
|
|
|
|
|
|
void RubberbandStateSelect::update()
|
|
{
|
|
Rubberband::update();
|
|
|
|
onDo_updateButtonFixCoordinates();
|
|
|
|
} /* RubberbandPointCluster::update */
|
|
|
|
} } /* namespace SituationMap::RB */
|