/** ** ** @RDE_HEADER@ ** Implementation of rubberband class to define multi 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 .........: ** ** 27.03.2015, JT : First edition for use with Qt5 ** ** ----------------------------------------------------------------------------------------------- */ #include "rubberband_multi_point_feature.h" #include "rubberband_frame_handle.h" #include "rubberband_line_handle.h" #include "rubberband_point_handle.h" #include "rubberband_drawable.h" #include "rubberband_handle_proxy.h" #include "drawable_line.h" #include "drawable_painter_path.h" #include "transient_tactical_symbol.h" #include "rubberband_internal.h" #include "rubberband_base_internal.h" #include #include namespace SituationMap { namespace RB { struct RubberbandMultiPointFeature::InternalData { Q_DISABLE_COPY(InternalData) QList> _lineHandleList; SituationMap::RefPointer _frameHandle; SituationMap::RefPointer _fillPath; SituationMap::RefPointer _cursorPreviewLine; SituationMap::RefPointer _cursorPreviewCloseLine; SituationMap::RefPointer _previewSymbol; bool _complexGeometry; bool _addVertexAtEnd; bool _showCursorPreviewLine; double _checkMaxAngle; InternalData( QGraphicsScene * scene, bool complexGeometry ) : _lineHandleList() , _frameHandle() , _fillPath() , _cursorPreviewLine() , _cursorPreviewCloseLine() , _previewSymbol() , _complexGeometry(complexGeometry) , _addVertexAtEnd(false) , _showCursorPreviewLine(false) , _checkMaxAngle(0.0) { Q_UNUSED(scene); } /* InternalData */ }; /* RubberbandMultiPointFeature::InternalData */ RubberbandMultiPointFeature::RubberbandMultiPointFeature( SituationMap::Map * map, QObject * parent ) : RubberbandMultiPointFeature(map, false, parent) { } /* RubberbandMultiPointFeature::RubberbandMultiPointFeature */ RubberbandMultiPointFeature::RubberbandMultiPointFeature( SituationMap::Map * map, bool complexGeometry, QObject * parent ) : Rubberband(map,parent) , _internal(new InternalData(Rubberband::_internal->_mapScene, complexGeometry)) , _isClosed(false) , _minimumPointCount(2) , _maximumPointCount(0) , _maximumSegmentLength(0.0) , _maximumLength(0.0) { } /* RubberbandMultiPointFeature::RubberbandMultiPointFeature */ RubberbandMultiPointFeature::~RubberbandMultiPointFeature() { delete _internal; } /* RubberbandMultiPointFeature::~RubberbandMultiPointFeature */ void RubberbandMultiPointFeature::setCheckMaxAngle(double phi) { _internal->_checkMaxAngle = ((phi > 0.0) && (phi < M_PI)) ? phi : 0.0; } // RubberbandMultiPointFeature::setCheckMaxAngle RubberbandFrameHandle * RubberbandMultiPointFeature::getFrameHandle() const { return _internal->_frameHandle.get(); } QVector RubberbandMultiPointFeature::coordinateVector( CoordinateSystem cs ) const { QVector coordinateVector; coordinateVector.reserve(Rubberband::_internal->_handleList.size()); for (auto handle : qAsConst(Rubberband::_internal->_handleList)) { RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); coordinateVector.push_back(projectCoordinateFromMap( cs, handlePointPtr->getCoordinate()._mapCoordinate )); } return coordinateVector; } /* RubberbandMultiPointFeature::coordinateVector */ QPointF RubberbandMultiPointFeature::singleCoordinate(unsigned int idx, CoordinateSystem cs ) const { if (idx < (unsigned int)Rubberband::_internal->_handleList.size()) { const RubberbandPointHandle * handle = dynamic_cast(Rubberband::_internal->_handleList[idx].get()); return projectCoordinateFromMap( cs, handle->getCoordinate()._mapCoordinate ); } return QPointF(); } /* RubberbandMultiPointFeature::singleCoordinate */ void RubberbandMultiPointFeature::setCoordinateVector(const QVector & coordinateVector, CoordinateSystem cs ) { Rubberband::_internal->_handleList.clear(); for (const auto &p : coordinateVector) { constructPoint( p, cs ); } enableModificationState(); update(); activateButtons(); } /* RubberbandMultiPointFeature::setCoordinateVector */ void RubberbandMultiPointFeature::constructPoint( const QPointF &point, CoordinateSystem cs ) { const QPointF &mapPos = projectCoordinateToMap( cs, point ); RubberbandHandle::RubberbandCoordinate coordinate( Rubberband::_internal->_mapView, convert(mapPos, Rubberband::_internal->_mapView->map()->spatialReference()) ); RubberbandHandle * handlePtr = constructPoint( coordinate._sceneCoordinate ); if (handlePtr) { static_cast(handlePtr)->setCoordinate( coordinate ); handlePtr->setSelection(false); } } /* RubberbandMultiPointFeature::constructPoint */ RubberbandHandle * RubberbandMultiPointFeature::constructPoint( QPointF scenePos ) { RubberbandHandle * handlePtr; if (!Rubberband::_internal->_handleList.empty()) { handlePtr = Rubberband::_internal->_handleList.last().get(); if (handlePtr) { const RubberbandHandle::RubberbandCoordinate && lastPos = static_cast(handlePtr)->getCoordinate(); if (isClosedEnabled()) { handlePtr = Rubberband::_internal->_handleList.first().get(); // get pointer to first point if ((unsigned int)Rubberband::_internal->_handleList.size() == 2) { const RubberbandHandle::RubberbandCoordinate && firstPos = static_cast(handlePtr)->getCoordinate(); // create line from first point of polyline to new point to close the polyline handlePtr = new RubberbandLineHandle( Rubberband::_internal->_mapView , Rubberband::_internal->_mapScene , scenePos , firstPos._sceneCoordinate , _lineHandleWidth , _internal->_complexGeometry ); _internal->_lineHandleList.insert( 0, SituationMap::RefPointer(static_cast(handlePtr)) ); } else if ((unsigned int)Rubberband::_internal->_handleList.size() > 2) { // update coordinates handlePtr = _internal->_lineHandleList.first().get(); // get pointer to first line handle static_cast(handlePtr)->updateCoordinate(0,scenePos); } } // create line from last point to new point handlePtr = new RubberbandLineHandle( Rubberband::_internal->_mapView , Rubberband::_internal->_mapScene , lastPos._sceneCoordinate , scenePos , _lineHandleWidth , _internal->_complexGeometry ); _internal->_lineHandleList.append( SituationMap::RefPointer(static_cast(handlePtr)) ); } } handlePtr = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, scenePos ); handlePtr -> setSelection(); Rubberband::_internal->_handleList.append( SituationMap::RefPointer(handlePtr) ); if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount) { activateButtons(); } if ((!_maximumPointCount) || ((unsigned int)Rubberband::_internal->_handleList.size() < _maximumPointCount)) { if (!_internal->_cursorPreviewLine) _internal->_cursorPreviewLine = new DrawableLine( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene ); if ((isClosedEnabled()) && (!_internal->_cursorPreviewCloseLine)) { _internal->_cursorPreviewCloseLine = new DrawableLine( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene ); } _internal->_cursorPreviewLine -> setVisibility( true ); if (_internal->_cursorPreviewCloseLine) _internal -> _cursorPreviewCloseLine -> setVisibility( true ); } else { _internal->_cursorPreviewLine = nullptr; _internal->_cursorPreviewCloseLine = nullptr; } return handlePtr; } /* RubberbandMultiPointFeature::constructPoint */ bool RubberbandMultiPointFeature::checkLengthConstrains( const QPointF & scenePos, const QPointF & deltaScenePos ) { if (_internal->_complexGeometry) { markFailedConstrains( QPointF(), false ); return true; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Checking constrains for moving selected coordinates (handles) * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ bool allHandlesSelected = true; QVector coordinateVector; coordinateVector.reserve( Rubberband::_internal->_handleList.size() ); for (auto handle : qAsConst(Rubberband::_internal->_handleList)) { RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); if (handlePointPtr) { coordinateVector.append(handlePointPtr->getCoordinate()); if (handlePointPtr->isSelected()) { RubberbandHandle::RubberbandCoordinate & coordinate = coordinateVector.back(); coordinate.updateSceneCoordinate( Rubberband::_internal->_mapView , coordinate._sceneCoordinate + deltaScenePos ); } else allHandlesSelected = false; } } if ((allHandlesSelected) || ((_internal->_frameHandle) && (_internal->_frameHandle->isSelected()))) { markFailedConstrains( QPointF(), false ); return true; } if (_internal->_checkMaxAngle > 0.0) { // Check angle of line segments first for (int i = 2; i < coordinateVector.size(); ++i) { const RubberbandHandle::RubberbandCoordinate & p1 = coordinateVector[i - 2]; const RubberbandHandle::RubberbandCoordinate & p2 = coordinateVector[i - 1]; const RubberbandHandle::RubberbandCoordinate & p3 = coordinateVector[i]; // build tangent vectors QPointF t1 = QPointF(p2._mapCoordinate.x() - p1._mapCoordinate.x(), p2._mapCoordinate.y() - p1._mapCoordinate.y()); QPointF t2 = QPointF(p3._mapCoordinate.x() - p2._mapCoordinate.x(), p3._mapCoordinate.y() - p2._mapCoordinate.y()); double l1 = sqrt(t1.x()*t1.x() + t1.y()*t1.y()); double l2 = sqrt(t2.x()*t2.x() + t2.y()*t2.y()); if (qFuzzyIsNull(l1) || qFuzzyIsNull(l2)) { continue; } t1 /= l1; t2 /= l2; double phi = acos(t1.x()*t2.x() + t1.y()*t2.y()); if (phi > _internal->_checkMaxAngle) { markFailedConstrains(scenePos, true); return false; } } } if ((!isClosedEnabled()) && (_maximumLength > 0.0)) { qreal totalLength = 0.0; const Esri::ArcGISRuntime::Point * mapPointPtr = nullptr; // Check Quartz for (int i = 0; i < coordinateVector.size(); ++i) { const RubberbandHandle::RubberbandCoordinate & coordinate = coordinateVector[i]; if (mapPointPtr) { qreal x = coordinate._mapCoordinate.x() - mapPointPtr->x(); qreal y = coordinate._mapCoordinate.y() - mapPointPtr->y(); totalLength += sqrt(x*x+y*y); } mapPointPtr = &coordinateVector.at(i)._mapCoordinate; } if (totalLength > _maximumLength) { markFailedConstrains( scenePos, true ); return false; } } const RubberbandHandle::RubberbandCoordinate * mapPointPtr = nullptr; bool lastSelected = false; auto handleIter = Rubberband::_internal->_handleList.constBegin(); for (int i = 0; i < coordinateVector.size(); ++i) { const RubberbandHandle::RubberbandCoordinate & coordinate = coordinateVector[i]; bool isSelected = (*handleIter)->isSelected(); if ((isSelected || lastSelected) && (mapPointPtr)) { if ((!isClosedEnabled()) && (_maximumSegmentLength > 0.0)) { qreal x = coordinate._mapCoordinate.x() - mapPointPtr->_mapCoordinate.x(); qreal y = coordinate._mapCoordinate.y() - mapPointPtr->_mapCoordinate.y(); if (sqrt(x*x+y*y) > _maximumSegmentLength) { markFailedConstrains( scenePos, true ); return false; } } // looking for identical coordinate with last point qreal x = coordinate._sceneCoordinate.x() - mapPointPtr->_sceneCoordinate.x(); qreal y = coordinate._sceneCoordinate.y() - mapPointPtr->_sceneCoordinate.y(); if ((x*x + y*y) <= 1) { markFailedConstrains( scenePos, true ); return false; } } ++handleIter; mapPointPtr = &coordinateVector.at(i); lastSelected = isSelected; } // for markFailedConstrains( QPointF(), false ); return true; } /* RubberbandMultiPointFeature::checkLengthConstrains */ bool RubberbandMultiPointFeature::checkLengthConstrains( unsigned int checkHandleIdx, QPointF & scenePos ) { if (_internal->_complexGeometry) { markFailedConstrains( QPointF(), false ); return true; } bool markFailedConstrainsFlag = false; // calculate pixel size for later use RubberbandDrawable::RubberbandCoordinate pi1( Rubberband::_internal->_mapView,scenePos ); RubberbandDrawable::RubberbandCoordinate pi2( Rubberband::_internal->_mapView,scenePos+QPointF(1,1) ); qreal piX = pi2._mapCoordinate.x() - pi1._mapCoordinate.x(); qreal piY = pi2._mapCoordinate.y() - pi1._mapCoordinate.y(); qreal pixelSize = sqrt(piX*piX+piY*piY); /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Checking constrains for adding single coordinate at end of polyline * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ RubberbandHandle::RubberbandCoordinate mapPos( Rubberband::_internal->_mapView, scenePos ); if ((!Rubberband::_internal->_handleList.empty()) && (_maximumLength > 0.0) && (!isClosedEnabled())) { qreal totalLength = 0.0; RubberbandHandle::RubberbandCoordinate mapPoint; QList>::iterator it = Rubberband::_internal->_handleList.begin(); for (; it != Rubberband::_internal->_handleList.end(); ++it) { RubberbandPointHandle * handlePointPtr = dynamic_cast(it->get()); if (handlePointPtr) { if (it != Rubberband::_internal->_handleList.begin()) { qreal x = handlePointPtr->getCoordinate()._mapCoordinate.x() - mapPoint._mapCoordinate.x(); qreal y = handlePointPtr->getCoordinate()._mapCoordinate.y() - mapPoint._mapCoordinate.y(); totalLength += sqrt(x*x+y*y); } mapPoint = handlePointPtr->getCoordinate(); } } if (totalLength >= _maximumLength) { markFailedConstrains( scenePos, true ); return false; } qreal x = mapPos._mapCoordinate.x() - mapPoint._mapCoordinate.x(); qreal y = mapPos._mapCoordinate.y() - mapPoint._mapCoordinate.y(); qreal l = sqrt(x*x+y*y); qreal d = _maximumLength - totalLength - l - pixelSize; if (d < 0.0) { qreal scale = (1.0 + d/l); const Esri::ArcGISRuntime::Point newMapCoordinate(mapPoint._mapCoordinate.x() + x * scale, mapPoint._mapCoordinate.y() + y * scale, mapPos._mapCoordinate.spatialReference()); mapPos._mapCoordinate = newMapCoordinate; markFailedConstrainsFlag = true; scenePos = mapPos.mapToScene( Rubberband::_internal->_mapView ); } } if (checkHandleIdx <= (unsigned int)Rubberband::_internal->_handleList.size()) { unsigned int handleListIdx = Rubberband::_internal->_handleList.size()-checkHandleIdx; const RubberbandPointHandle * handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList.at(handleListIdx).get()); if ((!isClosedEnabled()) && (_maximumSegmentLength > 0.0)) { qreal x = mapPos._mapCoordinate.x() - handlePointPtr->getCoordinate()._mapCoordinate.x(); qreal y = mapPos._mapCoordinate.y() - handlePointPtr->getCoordinate()._mapCoordinate.y(); qreal l = sqrt(x*x+y*y); qreal d = _maximumSegmentLength - l - pixelSize; if (d < 0.0) { qreal scale = (1.0 + d/l); const Esri::ArcGISRuntime::Point newMapCoordinate(handlePointPtr->getCoordinate()._mapCoordinate.x() + x * scale, handlePointPtr->getCoordinate()._mapCoordinate.y() + y * scale, mapPos._mapCoordinate.spatialReference()); mapPos._mapCoordinate = newMapCoordinate; markFailedConstrainsFlag = true; scenePos = mapPos.mapToScene( Rubberband::_internal->_mapView ); } } // looking for identical coordinate with last point qreal x = handlePointPtr->getCoordinate()._sceneCoordinate.x() - scenePos.x(); qreal y = handlePointPtr->getCoordinate()._sceneCoordinate.y() - scenePos.y(); if ((x*x+y*y) <= 1) { markFailedConstrains( scenePos, true ); return false; } } markFailedConstrains( scenePos, markFailedConstrainsFlag ); return true; } /* RubberbandMultiPointFeature::checkLengthConstrains */ bool RubberbandMultiPointFeature::selectHandles(int x, int y, const QPointF & scenePos) { bool eventHandled = false; RubberbandHandle * handlePtr = pickHandle(x,y); _internal -> _addVertexAtEnd = false; if (handlePtr) { handlePtr->setSelection(); enableSelectedState(); eventHandled = true; } else { if ((_maximumPointCount < 1) || ((unsigned int)Rubberband::_internal->_handleList.size() < _maximumPointCount)) { unsigned int lineCounter = 0; for (auto lineHandle : qAsConst(_internal->_lineHandleList)) { if (lineHandle->isSelected()) { lineHandle->setSelection(false); handlePtr = new RubberbandLineHandle( Rubberband::_internal->_mapView , Rubberband::_internal->_mapScene , lineHandle->getCoordinate(0)._sceneCoordinate , scenePos , _lineHandleWidth , _internal->_complexGeometry ); _internal->_lineHandleList.insert( lineCounter, SituationMap::RefPointer(static_cast(handlePtr)) ); lineHandle->updateCoordinate( 0, scenePos ); handlePtr = new RubberbandPointHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, scenePos ); handlePtr -> setSelection(); unsigned int insertHandleIndex = lineCounter; if (!isClosedEnabled()) insertHandleIndex += 1; Rubberband::_internal->_handleList.insert( insertHandleIndex, SituationMap::RefPointer(handlePtr) ); enableSelectedState(); eventHandled = true; break; } ++lineCounter; } } } if (!isEnabledSelectedState()) { // ok doesn't hit any point or line handle now looking to frame handle if (_internal->_frameHandle != nullptr) { if (_internal->_frameHandle->isPositionInside(scenePos)) { _internal->_frameHandle->setSelection(true); enableSelectedState(); eventHandled = true; } } } return eventHandled; } /* RubberbandMultiPointFeature::selectHandles */ void RubberbandMultiPointFeature::resetAction(AbstractEventDispatcher * causer, int x, int y ) { if (rubberbandIsAlive()) { if (isEnabledConstructionState()) { if (_internal->_addVertexAtEnd) { Rubberband::_internal->_handleList.removeLast(); _internal->_lineHandleList.removeLast(); } } } Rubberband::resetAction(causer,x,y); } /* RubberbandMultiPointFeature::resetAction */ bool RubberbandMultiPointFeature::selectAction( AbstractEventDispatcher* , int x, int y ) { if (rubberbandIsAlive()) { QPointF scenePos(pixel2ScenePosition(x,y)); if (_baseInternal->buttonProxyActive(scenePos)) return false; if (isEnabledConstructionState()) disenableMultiSelectedState(); _baseInternal->_lastClickPosition = scenePos; bool eventHandled = selectHandles( x, y, scenePos ); if ((!eventHandled) && (isEnabledConstructionState())) { _internal -> _addVertexAtEnd = true; _internal -> _showCursorPreviewLine = true; deSelectHandles(); if (checkLengthConstrains(1,scenePos)) { constructPoint( scenePos ); enableSelectedState(); _baseInternal->_lastClickPosition = scenePos; } eventHandled = true; } else _internal->_addVertexAtEnd = false; if (eventHandled) { update(); deactivateButtons(); } return eventHandled; } return false; } /* RubberbandMultiPointFeature::selectAction */ bool RubberbandMultiPointFeature::setCoordinateAction( AbstractEventDispatcher * causer, int x, int y ) { markFailedConstrains( QPointF(), false ); if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) { bool eventHandled = false; QPointF scenePos(pixel2ScenePosition(x,y)); Rubberband::_internal->_lastClickPosition = scenePos; if (_internal->_addVertexAtEnd) // isEnabledConstructionState()) { if (Rubberband::_internal->_handleList.isEmpty()) { disenableSelectedState(); disenableMultiSelectedState(); return false; } if ((!isEnabledSelectedState()) && (_baseInternal->buttonProxyActive(scenePos))) { deSelectHandles(); return false; } if (checkLengthConstrains(2, scenePos)) { QPointF deltaMove = scenePos - _baseInternal->_lastClickPosition; for (auto handle : qAsConst(Rubberband::_internal->_handleList)) { RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); if ((handlePointPtr) && ((handlePointPtr->isSelected()))) { handlePointPtr->setSelection(false); handlePointPtr->moveCoordinate( deltaMove ); eventHandled = true; } } } markFailedConstrains( QPointF(), false ); if ((_maximumPointCount >0) && ((unsigned int)Rubberband::_internal->_handleList.size() >= _maximumPointCount)) { completeStateAction(causer,x,y); } if (eventHandled) update(); if (_baseInternal->buttonProxyActive(scenePos)) return false; updateButtonPosition(); activateButtons(); } else { if (isEnabledSelectedState()) { bool frameIsSelected( ((_internal->_frameHandle != nullptr) && (_internal->_frameHandle->isSelected())) ); QPointF deltaMove = scenePos - _baseInternal->_lastClickPosition; if (!checkLengthConstrains(scenePos, deltaMove)) { for (auto handle : qAsConst(Rubberband::_internal->_handleList)) { RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); if (handlePointPtr) handlePointPtr->setSelection(false); } if (_internal->_frameHandle) _internal->_frameHandle->setSelection(false); updateButtonPosition(); activateButtons(); disenableSelectedState(); disenableMultiSelectedState(); markFailedConstrains( QPointF(), false ); return true; } markFailedConstrains( QPointF(), false ); for (auto handle : qAsConst(Rubberband::_internal->_handleList)) { RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); if ((handlePointPtr) && ((handlePointPtr->isSelected()) || (frameIsSelected))) { handlePointPtr->setSelection(false); handlePointPtr->moveCoordinate( deltaMove ); eventHandled = true; } } if (frameIsSelected) { _internal->_frameHandle->setSelection(false); eventHandled = true; } disenableSelectedState(); disenableMultiSelectedState(); } if (isEnabledConstructionState()) { _internal->_addVertexAtEnd = true; _internal->_showCursorPreviewLine = true; updatePreviewLine( scenePos ); } else if (_baseInternal->buttonProxyActive(scenePos)) return false; if (eventHandled) update(); updateButtonPosition(); activateButtons(); } return eventHandled; } return false; } /* RubberbandMultiPointFeature::setCoordinateAction */ void RubberbandMultiPointFeature::updatePreviewLine( const QPointF & scenePosition ) { if (isEnabledConstructionState() && (!Rubberband::_internal->_handleList.isEmpty()) && (_internal->_cursorPreviewLine)) { RubberbandPointHandle * handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList.last().get()); if ((handlePointPtr) && (_internal->_addVertexAtEnd)) { RubberbandDrawable::RubberbandCoordinate rbCoordinate( Rubberband::_internal->_mapView, scenePosition ); _internal->_cursorPreviewLine -> setVisibility( _internal->_showCursorPreviewLine ); _internal->_cursorPreviewLine -> setDrawColor( _handleSelectionColor ); _internal->_cursorPreviewLine->setLine( handlePointPtr->getCoordinate(), rbCoordinate ); if (_internal->_cursorPreviewCloseLine) { _internal->_cursorPreviewCloseLine -> setVisibility( _internal->_showCursorPreviewLine ); _internal->_cursorPreviewCloseLine -> setDrawColor( _handleSelectionColor ); handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList.front().get()); if (handlePointPtr) { _internal->_cursorPreviewCloseLine->setLine( handlePointPtr->getCoordinate(), rbCoordinate ); } } } else { _internal->_cursorPreviewLine -> setVisibility( false ); if (_internal->_cursorPreviewCloseLine) _internal->_cursorPreviewCloseLine -> setVisibility( false ); } return; } _internal->_cursorPreviewLine = nullptr; _internal->_cursorPreviewCloseLine = nullptr; } /* RubberbandMultiPointFeature::updatePreviewLine */ bool RubberbandMultiPointFeature::moveAction( AbstractEventDispatcher*, int x, int y ) { adjustConnectedPreviewPixmap(x,y); if (rubberbandIsAlive()) { QPointF scenePosition(pixel2ScenePosition(x,y)); if (_baseInternal->buttonProxyActive(scenePosition)) return false; if (Rubberband::_internal->_mapView == nullptr) return false; if (!isEnabledMultiSelectedState()) deSelectHandles(); RubberbandHandle * handlePtr = pickHandle(x,y); if (handlePtr == nullptr) { _internal->_showCursorPreviewLine = isEnabledConstructionState(); for (auto handle : qAsConst(_internal->_lineHandleList)) { handle->setSelection(false); if (handle->isPositionInside(scenePosition)) handle->setSelection(); } } else { _internal->_showCursorPreviewLine = false; handlePtr->setSelection(); } if (isEnabledConstructionState()) { if (checkLengthConstrains(1,scenePosition)) { updatePreviewLine( scenePosition ); if (isSignalConnected(QMetaMethod::fromSignal(&RubberbandMultiPointFeature::currentCursorPositionChanged))) { RubberbandDrawable::RubberbandCoordinate mapCoord(Rubberband::_internal->_mapView, scenePosition); emit currentCursorPositionChanged( Point3D(mapCoord._mapCoordinate.x(),mapCoord._mapCoordinate.y(),0.0) ); } } } return false; } return false; } /* RubberbandMultiPointFeature::moveAction */ bool RubberbandMultiPointFeature::movePickedAction( AbstractEventDispatcher*, int x, int y ) { adjustConnectedPreviewPixmap(x,y); if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) { if (isEnabledSelectedState()) { QPointF scenePos( pixel2ScenePosition(x,y) ); QPointF deltaMove( scenePos-_baseInternal->_lastClickPosition ); if (checkLengthConstrains(scenePos, deltaMove)) { bool frameIsSelected( ((_internal->_frameHandle != nullptr) && (_internal->_frameHandle->isSelected())) ); _baseInternal->_lastClickPosition = scenePos; for (auto handle : qAsConst(Rubberband::_internal->_handleList)) { RubberbandPointHandle * handlePointPtr = dynamic_cast(handle.get()); if ((handlePointPtr) && ((handlePointPtr->isSelected()) || (frameIsSelected))) { handlePointPtr->moveCoordinate( deltaMove ); } } updatePreviewLine(scenePos); update(); } return true; } } return false; } /* RubberbandMultiPointFeature::movePickedAction */ bool RubberbandMultiPointFeature::deleteAction(AbstractEventDispatcher*, int x, int y) { if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) { if (((unsigned int)Rubberband::_internal->_handleList.size() > _minimumPointCount) && (isEnabledSelectedState())) { QList> savedHandleList = Rubberband::_internal->_handleList; QList> savedLineHandleList = _internal->_lineHandleList; QPointF scenePosition = pixel2ScenePosition(x,y); QList>::iterator handleIter = Rubberband::_internal->_handleList.begin(); QList>::iterator lineHandleIter = _internal->_lineHandleList.begin(); while (handleIter != Rubberband::_internal->_handleList.end()) { if (handleIter->get()->isPositionInside(scenePosition)) { handleIter = Rubberband::_internal->_handleList.erase(handleIter); lineHandleIter = _internal->_lineHandleList.erase(lineHandleIter); if (lineHandleIter != _internal->_lineHandleList.end()) { if (lineHandleIter != _internal->_lineHandleList.begin()) { lineHandleIter->get()->updateCoordinate( 0, (lineHandleIter-1)->get()->getCoordinate(1)._sceneCoordinate ); } } continue; } ++handleIter; if ((lineHandleIter != _internal->_lineHandleList.end()) && (lineHandleIter+1 != _internal->_lineHandleList.end())) { ++lineHandleIter; } } /* while handleIter */ if (!checkLengthConstrains(scenePosition, QPointF(0,0))) { // constrain failed, do not delete yet Rubberband::_internal->_handleList = savedHandleList; _internal->_lineHandleList = savedLineHandleList; } update(); activateButtons(); disenableSelectedState(); disenableMultiSelectedState(); } return true; } return false; } /* RubberbandMultiPointFeature::deleteAction */ bool RubberbandMultiPointFeature::contextMenuAction( AbstractEventDispatcher*, int,int ) { return false; } /* RubberbandMultiPointFeature::contextMenuAction */ bool RubberbandMultiPointFeature::completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) { markFailedConstrains( QPointF(), false ); if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) { if (isEnabledConstructionState()) { if (!_finishAfterConstruction) { if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount) { enableModificationState(); _internal->_cursorPreviewLine = nullptr; // remove preview lines _internal->_cursorPreviewCloseLine = nullptr; } update(); return true; } } if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount) { return applyAndEndAction(causer,x,y); } } return false; } /* RubberbandMultiPointFeature::completeStateAction */ bool RubberbandMultiPointFeature::undoAction( AbstractEventDispatcher * /*causer*/, int,int ) { if (rubberbandIsAlive()) { if (!Rubberband::_internal->_handleList.empty()) { if (isEnabledConstructionState()) { Rubberband::_internal->_handleList.pop_back(); if (!_internal->_lineHandleList.empty()) _internal->_lineHandleList.pop_back(); if (_internal->_cursorPreviewLine) _internal->_cursorPreviewLine -> setVisibility( false ); if (_internal -> _cursorPreviewCloseLine) _internal -> _cursorPreviewCloseLine -> setVisibility( false ); update(); activateButtons(); } } return true; } return false; } /* RubberbandMultiPointFeature::undoAction */ bool RubberbandMultiPointFeature::applyAndEndAction( AbstractEventDispatcher * causer, int x, int y) { if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount) Rubberband::applyAndEndAction( causer,x,y ); else abortAction(causer,x,y); return true; } /* RubberbandMultiPointFeature::applyAndEndAction */ void RubberbandMultiPointFeature::activateButtons() { _baseInternal->_hasUndoButton = isEnabledConstructionState(); _baseInternal->_applyIsPossible = ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount); _baseInternal->_undoIsPossible = (isEnabledConstructionState() && ((unsigned int)Rubberband::_internal->_handleList.size() > _minimumPointCount)); Rubberband::activateButtons(); } /* RubberbandMultiPointFeature::activateButtons */ void RubberbandMultiPointFeature::update() { Rubberband::update(); DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION; auto handleLine = _internal->_lineHandleList.begin(); bool closePolygon = ((unsigned int)Rubberband::_internal->_handleList.size() > 2) && (isClosedEnabled()); bool firstLoop = !closePolygon; QColor lineHandleDrawColor = _drawColor; QColor lineHandleFillColor = _drawColor; if (_internal->_complexGeometry) { lineHandleDrawColor.setAlphaF( lineHandleDrawColor.alphaF()*0.5 ); lineHandleFillColor.setAlphaF( lineHandleFillColor.alphaF()*0.5 ); } else { if (!_previewSidc.isEmpty()) { lineHandleDrawColor.setAlphaF( 0.0 ); lineHandleFillColor.setAlphaF( 0.0 ); } } QVector coordinateList; coordinateList.reserve( Rubberband::_internal->_handleList.size() ); for (auto handlePoint : qAsConst(Rubberband::_internal->_handleList)) { RubberbandPointHandle * handlePointPtr = dynamic_cast(handlePoint.get()); if (handlePointPtr) { if (handleLine != _internal->_lineHandleList.end()) { RubberbandHandle::RubberbandCoordinate && pos = handlePointPtr->getCoordinate(); RubberbandLineHandle * handleLinePtr = dynamic_cast(handleLine->get()); if (!firstLoop) { if (handleLinePtr) { handleLinePtr -> updateCoordinate( 1, pos._sceneCoordinate ); handleLinePtr -> setDrawColor(lineHandleDrawColor); handleLinePtr -> setFillColor(lineHandleFillColor); handleLinePtr -> setSelectionColor(_handleSelectionColor); handleLinePtr -> setPickingRatio( _pickingRatio ); } ++handleLine; if (handleLine != _internal->_lineHandleList.end()) { handleLinePtr = dynamic_cast(handleLine->get()); } else handleLinePtr = nullptr; } else firstLoop = false; if (handleLinePtr) handleLinePtr->updateCoordinate( 0, pos._sceneCoordinate ); } coordinateList.append( handlePointPtr->getCoordinate() ); } } /* for handlePoint */ // remove superfluous line handles while (handleLine != _internal->_lineHandleList.end()) { handleLine = _internal->_lineHandleList.erase( handleLine ); } if (closePolygon) { SituationMap::RefPointer & handleLinePtr = _internal->_lineHandleList.first(); SituationMap::RefPointer & handlePointPtr = Rubberband::_internal->_handleList.last(); RubberbandHandle::RubberbandCoordinate && pos = dynamic_cast(handlePointPtr.get())->getCoordinate(); handleLinePtr->updateCoordinate( 0, pos._sceneCoordinate ); if (_fillColor.alpha()) { if (!_internal->_fillPath) { _internal->_fillPath = new DrawablePainterPath( Rubberband::_internal->_mapView ); _internal->_fillPath -> setDrawColor( QColor() ); } _internal -> _fillPath -> setVisibility( true ); _internal -> _fillPath -> setFillColor(_fillColor ); _internal -> _fillPath -> setPath( coordinateList ); } else _internal->_fillPath = nullptr; } else if (_internal->_fillPath) _internal->_fillPath->setVisibility( false ); if ((isEnabledModificationState()) && (coordinateList.size() >= 2) && (_internal->_frameHandle == nullptr)) { _internal->_frameHandle = SituationMap::RefPointer( new RubberbandFrameHandle( Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, _frameHandleMargin )); } if (_internal->_frameHandle != nullptr) { if (coordinateList.size() >= 2) { _internal->_frameHandle -> setDrawColor(_frameColor); _internal->_frameHandle -> setFillColor(_frameFillColor); _internal->_frameHandle -> setSelectionColor(_frameSelectionColor); _internal->_frameHandle -> updateFrameSceneMargin( _frameHandleMargin ); _internal->_frameHandle -> updateFrameShape( coordinateList ); _internal->_frameHandle -> drawDisabled( (_frameHandleMargin < 0) ); } else _internal->_frameHandle = nullptr; } if (!_previewSidc.isEmpty()) { if (!_internal->_previewSymbol) { _internal->_previewSymbol = new TransientTacticalSymbol( static_cast(_baseInternal->_map.data()), _previewSidc ); } _internal->_previewSymbol->update( coordinateList ); } else _internal->_previewSymbol = nullptr; } /* RubberbandMultiPointFeature::update */ } } /* namespace SituationMap::RB */