/** * @RDE_HEADER@ * Rubberband class to define multiple ellipse/circle * $Id$ * $HeadURL$ * $Author$ */ #include "rubberband_multi_ellipse_feature.h" #include "rubberband_frame_handle.h" #include "rubberband_line_handle.h" //#include "rubberband_ellipse.h" #include "rubberband_ellipse_handle.h" #include "rubberband_point_handle.h" #include "rubberband_drawable.h" #include "rubberband_handle_proxy.h" #include "drawable_line.h" #include "drawable_ellipse.h" #include "drawable_painter_path.h" #include "transient_tactical_symbol.h" #include "rubberband_internal.h" #include "rubberband_base_internal.h" #include "winkel_icon.xpm" //#include #include #include #include #include #include namespace SituationMap { namespace RB { struct RubberbandMultiEllipseFeature::InternalData { Q_DISABLE_COPY(InternalData) QList> _ellipseHandleList; SituationMap::RefPointer _frameHandle; // sector : QPointF _sectorOStart{1, 0}; QPointF _sectorOEnd{0, 1}; SituationMap::RefPointer _sectorStartHandle; SituationMap::RefPointer _sectorEndHandle; bool _sectorEnabled = false; bool _sectorFixedAngleEnabled{false}; QPointF _oVec{1,0}; // rb: // normalized orientation vector according to scene coordinate system // : sector // rotationHandle : SituationMap::RefPointer _rotationHandle; unsigned int _rotationHandleSize{0}; // : rotationHandle // Coordinate matrix to describe the change of coordinates of the last user input MathUtils::Matrix2D _lastCoordinateChange; SituationMap::RefPointer _previewEllipse; SituationMap::RefPointer _previewSymbol; QVector _preparedRadiiConstraints; bool _complexGeometry; bool _addVertexAtEnd; bool _showCursorPreviewEllipse; bool _constrainQuadratic{true}; InternalData(QGraphicsScene *scene, bool complexGeometry) : _complexGeometry(complexGeometry) , _addVertexAtEnd(false) , _showCursorPreviewEllipse(false) { Q_UNUSED(scene); } // InternalData }; // RubberbandMultiEllipseFeature::InternalData QPointF rotateVertex(const QPointF ¢er, qreal x, qreal y, qreal sinA, qreal cosA) { return QPointF(x*cosA-y*sinA,y*cosA+x*sinA) + center; } RubberbandMultiEllipseFeature::RubberbandMultiEllipseFeature(SituationMap::Map *map, QObject *parent) : RubberbandMultiEllipseFeature(map, false, parent) { } RubberbandMultiEllipseFeature::RubberbandMultiEllipseFeature(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) { } RubberbandMultiEllipseFeature::~RubberbandMultiEllipseFeature() { delete _internal; } RubberbandFrameHandle *RubberbandMultiEllipseFeature::getFrameHandle() const { return _internal->_frameHandle.get(); } QPointF RubberbandMultiEllipseFeature::centerCoordinate(CoordinateSystem cs) const { if (Rubberband::_internal->_handleList.isEmpty()) return {}; RubberbandPointHandle *handlePointCenter = dynamic_cast(Rubberband::_internal->_handleList.first().get()); if (!handlePointCenter) return {}; return projectCoordinateFromMap(cs, handlePointCenter->getCoordinate()._mapCoordinate); } QVector RubberbandMultiEllipseFeature::radii() const { QPointF center = centerCoordinate(CoordinateSystem::Map); if (center.isNull()) return {}; if (_internal->_ellipseHandleList.isEmpty()) return {}; QVector ret; ret.reserve(_internal->_ellipseHandleList.size()); for (SituationMap::RefPointer handle : qAsConst(_internal->_ellipseHandleList)) { if (!handle) continue; ret.append(handle->getRadius()); } return ret; } QPointF RubberbandMultiEllipseFeature::getBiggestRadius() const { QPointF max(0,0); const QVector &radiiCont = radii(); for (const QPointF &item : qAsConst(radiiCont)) { max.setX( std::max(max.x(), item.x()) ); max.setY( std::max(max.y(), item.y()) ); } return max; } bool RubberbandMultiEllipseFeature::setCenterAndRadii(const QPointF ¢er, CoordinateSystem cs, const QVector &radii) { if (center.isNull() || radii.isEmpty()) return false; const QPointF &myCenter = projectCoordinateToMap(cs, center); QVector coordinateVector; coordinateVector.reserve(radii.size()+1); // Center: coordinateVector.append(myCenter); // radii: for (const QPointF& radius : qAsConst(radii)) coordinateVector.append(myCenter + radius); setCoordinateVector(coordinateVector, CoordinateSystem::Map); return true; } bool RubberbandMultiEllipseFeature::setCenterAndRadii2(const QPointF ¢er, CoordinateSystem cs, const QVector &radiiWithConstraints) { if (center.isNull() || radiiWithConstraints.isEmpty()) return false; const QPointF &myCenter = projectCoordinateToMap(cs, center); QVector coordinateVector; coordinateVector.reserve(radiiWithConstraints.size()+1); // Center: coordinateVector.append(myCenter); // Center with the radii: for (const RubberbandMultiEllipseFeature::RadiusWithContraints& radiusItem : qAsConst(radiiWithConstraints)) if (radiusItem.m_radius > 0) coordinateVector.append(myCenter + QPointF(radiusItem.m_radius, radiusItem.m_radius)); Rubberband::_internal->_handleList.clear(); for (const auto &p : coordinateVector) constructPoint(p, CoordinateSystem::Map); // are we still in construction or in modification? // from scratch: constraints are provided, but no radius // predefined: radius is provided, maybe constraints if (coordinateVector.size() > 1) enableModificationState(); update(); activateButtons(); return true; } void RubberbandMultiEllipseFeature::prepareFixedNumberOfRadiiWithConstraints(const QVector &radiiContraints) { // for creation from scratch, just min/max should be prepared. // for creation with predefined values, the m_radius is relevant. _internal->_preparedRadiiConstraints = radiiContraints; } QPointF RubberbandMultiEllipseFeature::getRotatedPoint(const QPointF §or) const { // Rotation of entire Rubberband qreal sinRect = 0, cosRect = 1; getSceneRotation(sinRect, cosRect); const double angleRad = SituationMap::MathUtils::getMathAngle(QPointF(cosRect, sinRect)); // angle rad const double clockwiseAngleRad = ((2*LtsCoordTrans::pi()) - angleRad); // angle in rad; // map clockwise rotation QPointF point; point.setX( sector.x()*std::cos(clockwiseAngleRad) + sector.y()*std::sin(clockwiseAngleRad)); point.setY( -sector.x()*std::sin(clockwiseAngleRad) + sector.y()*std::cos(clockwiseAngleRad)); return point; } void RubberbandMultiEllipseFeature::enableSector(bool on) { _internal->_sectorEnabled = on; } bool RubberbandMultiEllipseFeature::isSectorEnabled() const { return _internal->_sectorEnabled; } QPointF RubberbandMultiEllipseFeature::sectorStart() const { return getRotatedPoint(_internal->_sectorOStart); } qreal RubberbandMultiEllipseFeature::sectorStartDegMath() const { const QPointF &point = sectorStart(); return MathUtils::getMathAngle(point) * LtsCoordTrans::rad2deg(); } QPointF RubberbandMultiEllipseFeature::sectorEnd() const { return getRotatedPoint(_internal->_sectorOEnd); } qreal RubberbandMultiEllipseFeature::sectorEndDegMath() const { const QPointF &point = sectorEnd(); return MathUtils::getMathAngle(point) * LtsCoordTrans::rad2deg(); } void RubberbandMultiEllipseFeature::setSectorStart(qreal sinPhi, qreal cosPhi) { _internal->_sectorOStart = QPointF(cosPhi, sinPhi); enableSelectedState(); update(); } void RubberbandMultiEllipseFeature::setSectorEnd(qreal sinPhi, qreal cosPhi) { _internal->_sectorOEnd = QPointF(cosPhi, sinPhi); enableSelectedState(); update(); } void RubberbandMultiEllipseFeature::setSectorAngleFixed(bool on) { _internal->_sectorFixedAngleEnabled = on; update(); } bool RubberbandMultiEllipseFeature::isSectorFixedAngeEnabled() const { return _internal->_sectorFixedAngleEnabled; } QVector RubberbandMultiEllipseFeature::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; } QPointF RubberbandMultiEllipseFeature::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(); } void RubberbandMultiEllipseFeature::setCoordinateVector(const QVector &coordinateVector, CoordinateSystem cs) { Rubberband::_internal->_handleList.clear(); for (const auto &p : coordinateVector) constructPoint(p, cs); enableModificationState(); update(); activateButtons(); } bool RubberbandMultiEllipseFeature::getRadiusXY(const QPointF &scenePos, qreal &radiusX_out, qreal &radiusY_out) { if (Rubberband::_internal->_handleList.isEmpty()) return false; RubberbandPointHandle *handlePointCenter = dynamic_cast(Rubberband::_internal->_handleList.first().get()); if (!handlePointCenter) return false; const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointCenter->getCoordinate(); RubberbandHandle::RubberbandCoordinate currentMapPos(Rubberband::_internal->_mapView, scenePos); radiusX_out = fabs(mapCenter._mapCoordinate.x() - currentMapPos._mapCoordinate.x()); radiusY_out = fabs(mapCenter._mapCoordinate.y() - currentMapPos._mapCoordinate.y()); return true; } void RubberbandMultiEllipseFeature::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); } } RubberbandHandle *RubberbandMultiEllipseFeature::constructPoint(const QPointF &scenePos) { DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " RubberbandMultiEllipseFeature::constructPoint, _handleList.size " << Rubberband::_internal->_handleList.size(); RubberbandHandle *handlePtr{nullptr}; if ((_maximumPointCount>0) && ((unsigned int)Rubberband::_internal->_handleList.size() >= _maximumPointCount)) { DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " skip due to _maximumPointCount (_sectorInputIsRunning)"; return nullptr; } if (!Rubberband::_internal->_handleList.empty()) { handlePtr = Rubberband::_internal->_handleList.first().get(); if (handlePtr) { // center == first const RubberbandHandle::RubberbandCoordinate &firstPosCenterPos = static_cast(handlePtr)->getCoordinate(); qreal radiusX_out, radiusY_out; if (getRadiusXY(scenePos, radiusX_out, radiusY_out)) { bool minMaxOk = checkMinMaxRadiusConstraint(radiusX_out, radiusY_out); if (minMaxOk) { RubberbandEllipseHandle *temp = new RubberbandEllipseHandle(Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene); if (_internal->_constrainQuadratic) radiusX_out = radiusY_out = std::max(radiusX_out, radiusY_out); temp->setRect(firstPosCenterPos, radiusX_out, radiusY_out, 0, 1); QColor fillColor = _fillColor; fillColor.setAlphaF(_fillColor.alphaF()*0.5); temp->setFillColor(fillColor); QColor selectionColor = _handleSelectionColor; selectionColor.setAlphaF(_handleSelectionColor.alphaF()*0.5); temp->setSelectionColor(selectionColor); _internal->_ellipseHandleList.append(RefPointer(static_cast(temp))); } else { DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " SKIP RubberbandMultiEllipseFeature::constructPoint, " << ", radiusX_out" << radiusX_out << ", radiusY_out" << radiusY_out; return nullptr; } } } } // // RubberbandPointHandle einfügen // handlePtr = new RubberbandPointHandle(Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, scenePos); handlePtr->setSelection(); handlePtr->setIsFirst(Rubberband::_internal->_handleList.empty()); // mark centerPoint with 'isFirst' to simplify later recognation Rubberband::_internal->_handleList.append(SituationMap::RefPointer(handlePtr)); // activateButtons? if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount) activateButtons(); if ((!_maximumPointCount) || ((unsigned int)Rubberband::_internal->_handleList.size() < _maximumPointCount)) { if (!_internal->_previewEllipse) _internal->_previewEllipse = new DrawableEllipse(Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene); _internal->_previewEllipse->setVisibility(true); } else { _internal->_previewEllipse = nullptr; } return handlePtr; } bool RubberbandMultiEllipseFeature::checkLengthConstrains(const QPointF &scenePos, const QPointF &deltaScenePos) { Q_UNUSED(scenePos); Q_UNUSED(deltaScenePos); return true; /* 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; } // // check totalLength // if (_maximumLength > 0.0) { qreal totalLength = 0.0; const Esri::ArcGISRuntime::Point *lastMapCoord = nullptr; // Check Quartz for (int i = 0; i < coordinateVector.size(); ++i) { const RubberbandHandle::RubberbandCoordinate &coordinate = coordinateVector[i]; if (lastMapCoord) { qreal x = coordinate._mapCoordinate.x() - lastMapCoord->x(); qreal y = coordinate._mapCoordinate.y() - lastMapCoord->y(); totalLength += sqrt(x * x + y * y); } lastMapCoord = &coordinateVector.at(i)._mapCoordinate; } if (totalLength > _maximumLength) { markFailedConstrains(scenePos, true); return false; } } // // check maximumSegmentLength // 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 (_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; */ } bool RubberbandMultiEllipseFeature::checkMinMaxRadiusConstraint(const QPointF &curScenePos) { qreal radiusX_out, radiusY_out; if (!getRadiusXY(curScenePos, radiusX_out, radiusY_out)) return true; if (_internal->_constrainQuadratic) radiusX_out = radiusY_out = std::max(radiusX_out, radiusY_out); return checkMinMaxRadiusConstraint(radiusX_out, radiusY_out); } bool RubberbandMultiEllipseFeature::checkMinMaxRadiusConstraint(qreal radiusX, qreal radiusY, int idxOfConstraint) { // Which radius is relevant? if (idxOfConstraint < 0) idxOfConstraint = Rubberband::_internal->_handleList.size() - 1; // '-1' - keep handle for center (isFirst) in mind if ((idxOfConstraint < 0) || (idxOfConstraint >= _internal->_preparedRadiiConstraints.size())) return true; const RadiusWithContraints &curConstraints = _internal->_preparedRadiiConstraints[idxOfConstraint]; // qCritical() << " radiusX = " << radiusX; // qCritical() << " radiusY = " << radiusY; bool bMinOk = true; double minRadius = curConstraints.m_minRadius; if (minRadius > 0) bMinOk = ((radiusX >= minRadius) && (radiusY >= minRadius)); bool bMaxOk = true; double maxRadius = curConstraints.m_maxRadius; if (maxRadius > 0) bMaxOk = ((radiusX <= maxRadius) && (radiusY <= maxRadius)); if (!bMinOk) INFO_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " checkMinMaxRadiusConstraintNew false by bMinOk" << ", idxOfConstraint=" << idxOfConstraint << ", radiusX=" << radiusX << ", radiusY=" << radiusY << ", minRadius=" << minRadius << ", maxRadius=" << maxRadius; if (!bMaxOk) INFO_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " checkMinMaxRadiusConstraintNew false by bMaxOk" << ", idxOfConstraint=" << idxOfConstraint << ", radiusX=" << radiusX << ", radiusY=" << radiusY << ", minRadius=" << minRadius << ", maxRadius=" << maxRadius; return bMinOk && bMaxOk; } bool RubberbandMultiEllipseFeature::checkLengthConstrains(unsigned int checkHandleIdx, const QPointF &scenePos) { Q_UNUSED(checkHandleIdx); if (!checkMinMaxRadiusConstraint(scenePos)) { markFailedConstrains(scenePos, true); return false; } markFailedConstrains(QPoint(), true); return true; // return true -> construct point // return false -> don not construct point /* 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)) { 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); } } // check radius if (!checkMinMaxRadiusConstraint(scenePos)) { markFailedConstrains(scenePos, true); return false; // markFailedConstrainsFlag = true; // return true; } markFailedConstrains(scenePos, markFailedConstrainsFlag); return true; */ } bool RubberbandMultiEllipseFeature::selectHandles(int x, int y, const QPointF &scenePos) { bool eventHandled = false; RubberbandHandle *handlePtr = pickHandle(x, y); _internal->_addVertexAtEnd = false; if (handlePtr && handlePtr->isVisible()) { handlePtr->setSelection(); enableSelectedState(); eventHandled = true; } if (!isEnabledSelectedState()) { // ok doesn't hit any point or line handle now looking to frame handle if (_internal->_frameHandle && _internal->_frameHandle->isPositionInside(scenePos)) { _internal->_frameHandle->setSelection(true); enableSelectedState(); eventHandled = true; } if (_internal->_rotationHandle && _internal->_rotationHandle->isPositionInside(scenePos)) { _internal->_rotationHandle->setSelection(true); enableSelectedState(); eventHandled = true; } } return eventHandled; } void RubberbandMultiEllipseFeature::updateHandles(const QPointF &scenePos, const QPointF &deltaMove) { Q_UNUSED(deltaMove); bool constrainsComplied = true; if (Rubberband::_internal->_handleList.size() < 2) return; if ((_internal->_rotationHandle.valid()) && _internal->_rotationHandle->isSelected()) { const RubberbandPointHandle *handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList.first().get()); if (!handlePointPtr) return; const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointPtr->getCoordinate(); // _internal->_centerScene --> centerScene const QPointF ¢erScene = mapCenter._sceneCoordinate; const Esri::ArcGISRuntime::Point ¢erMapCoords = mapCenter._mapCoordinate; const QPointF centerMapCoordsAsPointF(centerMapCoords.x(), centerMapCoords.y()); qreal sinA = scenePos.x() - centerScene.x(); // _internal->_centerScene.x(); qreal cosA = - scenePos.y() + centerScene.y(); qreal len = sqrt(cosA*cosA+sinA*sinA); cosA /= len; sinA /= len; // for circle it´s not really helpfull to rotate the handles -> skip here // for (auto handle : qAsConst(Rubberband::_internal->_handleList)) // { // RubberbandPointHandle *handlePointPtr = dynamic_cast(handle.get()); // if (!handlePointPtr) // continue; // QPointF p = MathUtils::rotate( handlePointPtr->getCoordinate()._sceneCoordinate, centerScene, -_internal->_oVec.y(), _internal->_oVec.x() ); // p = MathUtils::rotate( p, centerScene, sinA, cosA ); // handlePointPtr->updateCoordinate( p ); // p.setX( handlePointPtr->getCoordinate()._mapCoordinate.x() ); // p.setY( handlePointPtr->getCoordinate()._mapCoordinate.y() ); // } // for qreal dSin = sinA*_internal->_oVec.x() - cosA*_internal->_oVec.y(); // addition theorem qreal dCos = cosA*_internal->_oVec.x() + sinA*_internal->_oVec.y(); _internal->_lastCoordinateChange.setToIdentity(); // clear matrix _internal->_lastCoordinateChange.putTranslation( -centerMapCoordsAsPointF ); // move center to (0,0) _internal->_lastCoordinateChange.putRotate( QPointF(dCos, dSin) ); // do rotation _internal->_lastCoordinateChange.putTranslation( centerMapCoordsAsPointF ); // move center back _internal->_oVec.setX(cosA); _internal->_oVec.setY(sinA); _baseInternal->_lastClickPosition = scenePos; } markFailedConstrains( scenePos, !constrainsComplied ); update(); } void RubberbandMultiEllipseFeature::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); } bool RubberbandMultiEllipseFeature::selectAction(AbstractEventDispatcher *, int x, int y) { if (rubberbandIsAlive()) { DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " selectAction"; QPointF scenePos(pixel2ScenePosition(x, y)); // starting a rubberband with radii prepared but without center? if (!_internal->_preparedRadiiConstraints.isEmpty() && Rubberband::_internal->_handleList.isEmpty()) { RubberbandDrawable::RubberbandCoordinate rbC(Rubberband::_internal->_mapView, scenePos); setCenterAndRadii2(QPointF(rbC._mapCoordinate.x(), rbC._mapCoordinate.y()), CoordinateSystem::Map, _internal->_preparedRadiiConstraints); enableSelectedState(); _internal->_addVertexAtEnd = true; _internal->_showCursorPreviewEllipse = true; return true; } 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->_showCursorPreviewEllipse = 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; } bool RubberbandMultiEllipseFeature::setCoordinateAction(AbstractEventDispatcher *causer, int x, int y) { Q_UNUSED(causer); if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) { QPointF scenePos(pixel2ScenePosition(x, y)); Rubberband::_internal->_lastClickPosition = scenePos; if (Rubberband::_internal->_handleList.isEmpty()) { disenableSelectedState(); disenableMultiSelectedState(); return false; } } return false; // ----------------------------------- /* 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)) { INFO_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " do Sector now..."; _internal->_showCursorPreviewEllipse = false; // continue contructionState, so we are able to move the whole object by moving the first handle (center) // reset _lastClickPosition to improve ButtonPosition _baseInternal->_lastClickPosition = QPointF(-1,-1); // remove former selection: for (SituationMap::RefPointer handle : qAsConst(_internal->_ellipseHandleList)) if (handle) handle->setSelection(false); deSelectHandles(); update(); // do not complete // return false; // 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->_showCursorPreviewEllipse = true; updatePreviewEllipse(scenePos); } else if (_baseInternal->buttonProxyActive(scenePos)) return false; if (eventHandled) update(); updateButtonPosition(); activateButtons(); } return eventHandled; } return false; */ } void RubberbandMultiEllipseFeature::updatePreviewEllipse(const QPointF &scenePosition) { DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " START"; // qCritical() << " RubberbandMultiEllipseFeature::updatePreviewEllipse" // << ", scenePosition " << scenePosition // << ", _addVertexAtEnd " << _internal->_addVertexAtEnd // << ", isEnabledConstructionState " << isEnabledConstructionState() // << " _internal->_handleList.size() " <_handleList.size() // << " _ellipseHandleList.size() " <<_internal->_ellipseHandleList.size(); if (isEnabledConstructionState() && (!Rubberband::_internal->_handleList.isEmpty()) && (_internal->_previewEllipse)) { // center / first point RubberbandPointHandle *handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList.first().get()); if ((handlePointPtr) && (_internal->_addVertexAtEnd)) { RubberbandHandle::RubberbandCoordinate mapCenter = handlePointPtr->getCoordinate(); RubberbandDrawable::RubberbandCoordinate rbCoordinate(Rubberband::_internal->_mapView, scenePosition); _internal->_previewEllipse->setVisibility(_internal->_showCursorPreviewEllipse); _internal->_previewEllipse->setDrawColor(_handleSelectionColor); int distX = fabs(mapCenter._mapCoordinate.x() - rbCoordinate._mapCoordinate.x()); int distY = fabs(mapCenter._mapCoordinate.y() - rbCoordinate._mapCoordinate.y()); if (_internal->_constrainQuadratic) distX = distY = std::max(distX, distY); _internal->_previewEllipse->setRect(QPointF(mapCenter._mapCoordinate.x(), mapCenter._mapCoordinate.y()), distX, distY, 0, 1); // sync FrameHandle: updateFrameHandle(scenePosition); } else { _internal->_previewEllipse->setVisibility(false); } return; } else { _internal->_previewEllipse = nullptr; } } bool RubberbandMultiEllipseFeature::moveAction(AbstractEventDispatcher *, int x, int y) { DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " START"; adjustConnectedPreviewPixmap(x, y); if (rubberbandIsAlive()) { QPointF scenePosition(pixel2ScenePosition(x, y)); //------------------------------ // sectorStartHand, _sectorEndHandle { if (_internal->_sectorStartHandle.valid()) { if (_internal->_sectorStartHandle->isPositionInside(scenePosition)) { _internal->_sectorStartHandle->setSelection(true); _internal->_frameHandle = nullptr; update(); return false; } else _internal->_sectorStartHandle->setSelection(false); } if (_internal->_sectorEndHandle.valid()) { if (_internal->_sectorEndHandle->isPositionInside(scenePosition)) { _internal->_sectorEndHandle->setSelection(true); _internal->_frameHandle = nullptr; update(); return false; } else _internal->_sectorEndHandle->setSelection(false); } } //------------------------------ //------------------------------ //------------------------------ //------------------------------ if (_internal->_rotationHandle.valid()) _internal->_rotationHandle->setSelection(_internal->_rotationHandle->isPositionInside(scenePosition)); if (_baseInternal->buttonProxyActive(scenePosition)) return false; if (!Rubberband::_internal->_mapView) return false; if (!isEnabledMultiSelectedState()) deSelectHandles(); RubberbandHandle *handlePtr = pickHandle(x, y); if (!handlePtr) { _internal->_showCursorPreviewEllipse = isEnabledConstructionState(); } else { _internal->_showCursorPreviewEllipse = false; // set handlePtr selected... handlePtr->setSelection(); // and updateFrameHandle if not first handle: RubberbandPointHandle *handlePointPtr = dynamic_cast(handlePtr); if (handlePointPtr) { if (handlePointPtr->isFirst()) updateFrameHandle({}); else updateFrameHandle(handlePointPtr->getCoordinate()._sceneCoordinate); } } if (isEnabledConstructionState()) { if (checkLengthConstrains(1, scenePosition)) { updatePreviewEllipse(scenePosition); if (isSignalConnected(QMetaMethod::fromSignal(&RubberbandMultiEllipseFeature::currentCursorPositionChanged))) { RubberbandDrawable::RubberbandCoordinate mapCoord(Rubberband::_internal->_mapView, scenePosition); emit currentCursorPositionChanged(Point3D(mapCoord._mapCoordinate.x(), mapCoord._mapCoordinate.y(), 0.0)); } } } return false; } return false; } bool RubberbandMultiEllipseFeature::movePickedAction(AbstractEventDispatcher *, int x, int y) { DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " START" << ", isEnabledSelectedState " << isEnabledSelectedState(); adjustConnectedPreviewPixmap(x, y); if ((rubberbandIsAlive()) && (Rubberband::_internal->_mapView) && (Rubberband::_internal->_mapScene)) { QPointF scenePos(pixel2ScenePosition(x,y)); updateHandles(scenePos, scenePos-_baseInternal->_lastClickPosition); if (isEnabledSelectedState()) { // sectorHandles: if (!isSectorFixedAngeEnabled()) { auto updateHandle = [this](RubberbandPointHandle * handle, const RubberbandHandle::RubberbandCoordinate & pos, QPointF & oVector ) -> bool { if (!handle) return false; if (!handle->isSelected()) return false; RubberbandPointHandle *handlePointCenter = dynamic_cast(Rubberband::_internal->_handleList.first().get()); if (!handlePointCenter) return false; const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointCenter->getCoordinate(); qreal sinRect = 0; qreal cosRect = 1; getSceneRotation( sinRect, cosRect); qreal cosA = pos._mapCoordinate.x() - mapCenter._mapCoordinate.x(); qreal sinA = pos._mapCoordinate.y() - mapCenter._mapCoordinate.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 RubberbandHandle::RubberbandCoordinate pos(Rubberband::_internal->_mapView, scenePos); updateHandle( _internal->_sectorStartHandle.get(), pos, _internal->_sectorOStart); updateHandle( _internal->_sectorEndHandle.get() , pos, _internal->_sectorOEnd ); } // // return true; //------------------------------ //------------------------------ //------------------------------ // look for (first) selected PointHandle: RubberbandPointHandle *selectedPointHandlePtr{nullptr}; for (auto handle : qAsConst(Rubberband::_internal->_handleList)) { RubberbandPointHandle *handlePointPtr = dynamic_cast(handle.get()); if (!handlePointPtr) continue; if (handlePointPtr->isSelected()) selectedPointHandlePtr = handlePointPtr; } // for const bool bFirstHandlePointSelected = (selectedPointHandlePtr && selectedPointHandlePtr->isFirst()); RubberbandHandle::RubberbandCoordinate rcNewCorner; // nur wenn _constrainQuadratic UND nicht der 'isFirst'/centerPoint if (_internal->_constrainQuadratic && !bFirstHandlePointSelected) { RubberbandPointHandle *handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList.first().get()); if (!handlePointPtr) return false; const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointPtr->getCoordinate(); qreal radiusX, radiusY; if (getRadiusXY(scenePos, radiusX, radiusY)) radiusX = radiusY = std::max(radiusX, radiusY); RefPointer refPtr(selectedPointHandlePtr); int idx = Rubberband::_internal->_handleList.indexOf(refPtr); if (idx > 0) { if (!checkMinMaxRadiusConstraint(radiusX, radiusY, idx-1)) { update(); // show/create preview!? return false; } } // adjusted coord: (prefer topRight) Esri::ArcGISRuntime::Point newCorner(mapCenter._mapCoordinate.x()+radiusX, mapCenter._mapCoordinate.y()+radiusY, mapCenter._mapCoordinate.spatialReference()); rcNewCorner = RubberbandHandle::RubberbandCoordinate(Rubberband::_internal->_mapView, newCorner); } QPointF deltaMove(scenePos - _baseInternal->_lastClickPosition); if (checkLengthConstrains(scenePos, deltaMove)) { const bool frameIsSelected(((_internal->_frameHandle) && (_internal->_frameHandle->isSelected()))); const bool moveAll = bFirstHandlePointSelected; _baseInternal->_lastClickPosition = scenePos; for (auto handle : qAsConst(Rubberband::_internal->_handleList)) { RubberbandPointHandle *handlePointPtr = dynamic_cast(handle.get()); if ((handlePointPtr) && ((handlePointPtr->isSelected()) || (frameIsSelected||moveAll))) { if (rcNewCorner.isNull()) handlePointPtr->moveCoordinate(deltaMove); else handlePointPtr->setCoordinate(rcNewCorner); } } updatePreviewEllipse(scenePos); update(); } return true; } } return false; } bool RubberbandMultiEllipseFeature::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; QPointF scenePosition = pixel2ScenePosition(x, y); QList>::iterator handleIter = Rubberband::_internal->_handleList.begin(); while (handleIter != Rubberband::_internal->_handleList.end()) { if (handleIter->get()->isPositionInside(scenePosition)) { handleIter = Rubberband::_internal->_handleList.erase(handleIter); updateFrameHandle({}); continue; } ++handleIter; } // while handleIter if (!checkLengthConstrains(scenePosition, QPointF(0, 0))) { // constrain failed, do not delete yet Rubberband::_internal->_handleList = savedHandleList; } update(); activateButtons(); disenableSelectedState(); disenableMultiSelectedState(); } return true; } return false; } bool RubberbandMultiEllipseFeature::contextMenuAction(AbstractEventDispatcher *, int, int) { return false; } bool RubberbandMultiEllipseFeature::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->_previewEllipse = nullptr; // remove preview } update(); return true; } } if ((unsigned int)Rubberband::_internal->_handleList.size() >= _minimumPointCount) return applyAndEndAction(causer, x, y); } return false; } bool RubberbandMultiEllipseFeature::undoAction(AbstractEventDispatcher * /*causer*/, int, int) { if (rubberbandIsAlive()) { if (!Rubberband::_internal->_handleList.empty()) { if (isEnabledConstructionState()) { Rubberband::_internal->_handleList.pop_back(); if (!_internal->_ellipseHandleList.empty()) _internal->_ellipseHandleList.pop_back(); if (_internal->_previewEllipse) _internal->_previewEllipse->setVisibility(false); update(); activateButtons(); } } return true; } return false; } bool RubberbandMultiEllipseFeature::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; } void RubberbandMultiEllipseFeature::constrainQuadratic(bool on) { _internal->_constrainQuadratic = on; } void RubberbandMultiEllipseFeature::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(); } void RubberbandMultiEllipseFeature::update() { DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " START"; Rubberband::update(); // update _ellipseHandleList according _handleList: (context modification) RubberbandHandle::RubberbandCoordinate centerCoord; if (!Rubberband::_internal->_handleList.isEmpty()) { RubberbandPointHandle *handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList.first().get()); centerCoord = handlePointPtr->getCoordinate(); } // // adjust EllipseHandles according size, selection, zOrder: // RubberbandPointHandle *selectedHandlePointPtrNotCenter{nullptr}; int handleIdxMax = 0; const int handleListSize = Rubberband::_internal->_handleList.size(); for (int handleIdx=0; handleIdx < handleListSize; ++handleIdx) { if (handleIdx >= Rubberband::_internal->_handleList.size()) continue; handleIdxMax = handleIdx; RubberbandPointHandle *pointHandle = dynamic_cast(Rubberband::_internal->_handleList[handleIdx].get()); if (!pointHandle) continue; if (pointHandle->isSelected() && handleIdx>0) selectedHandlePointPtrNotCenter = pointHandle; if (handleIdx==0) continue; // --- now we have finished the first iteration --- // 2 pointHandles are required for an elli: SituationMap::RefPointer ellipseHandle = _internal->_ellipseHandleList[handleIdx-1]; if (!ellipseHandle) continue; const RubberbandHandle::RubberbandCoordinate &curCoord = pointHandle->getCoordinate(); qreal distX = fabs(curCoord._mapCoordinate.x() - centerCoord._mapCoordinate.x()); qreal distY = fabs(curCoord._mapCoordinate.y() - centerCoord._mapCoordinate.y()); if (_internal->_constrainQuadratic) distX = distY = std::max(distX, distY); if (isSectorEnabled()) { // _internal->_sectorOStart + _internal->_oVec QPointF sectorStartRotated = getRotatedPoint(_internal->_sectorOStart); QPointF sectorEndRotated = getRotatedPoint(_internal->_sectorOEnd); ellipseHandle->setRect(centerCoord, distX, distY, 0, 1, sectorStartRotated, sectorEndRotated); } else ellipseHandle->setRect(centerCoord, distX, distY, 0, 1); // transfer selection: ellipseHandle->setSelection(pointHandle->isSelected()); // update zOrder ellipseHandle->setZValue(handleListSize-handleIdx); } // for // Maybe there has ist one RubberbandPointHandle deleted (deleteAction) // remove superfluous ellipse handles // like: 2 RubberbandPointHandle (handleIdxMax==1) -> 1 RubberbandEllipseHandle while (_internal->_ellipseHandleList.size() > handleIdxMax) _internal->_ellipseHandleList.removeLast(); if (selectedHandlePointPtrNotCenter) updateFrameHandle(selectedHandlePointPtrNotCenter->getCoordinate()._sceneCoordinate); // transfer selection: if (_internal->_frameHandle && selectedHandlePointPtrNotCenter) _internal->_frameHandle->setSelection( selectedHandlePointPtrNotCenter->isSelected() ); updateSectorHandles(); updateRotationHandle(); DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " END"; } void RubberbandMultiEllipseFeature::updateFrameHandle(const QPointF &sceneCoordinate) { const RubberbandDrawable::RubberbandCoordinate vertex(Rubberband::_internal->_mapView, sceneCoordinate); if (//isEnabledModificationState() && (!vertex.isNull()) && !Rubberband::_internal->_handleList.isEmpty() ) { if (!_internal->_frameHandle) { _internal->_frameHandle = SituationMap::RefPointer(new RubberbandFrameHandle(Rubberband::_internal->_mapView, Rubberband::_internal->_mapScene, _frameHandleMargin)); _internal->_frameHandle->setSelectable(false); _internal->_frameHandle->setDrawColor(_frameColor); _internal->_frameHandle->setFillColor(_frameFillColor); _internal->_frameHandle->setSelectionColor(_frameSelectionColor); _internal->_frameHandle->updateFrameSceneMargin(_frameHandleMargin); _internal->_frameHandle->drawDisabled((_frameHandleMargin < 0)); } } else { _internal->_frameHandle = nullptr; } if (_internal->_frameHandle) { const RubberbandPointHandle *handlePointPtr = dynamic_cast(Rubberband::_internal->_handleList.first().get()); if (!handlePointPtr) return; const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointPtr->getCoordinate(); qreal radiusX, radiusY; if (!getRadiusXY(sceneCoordinate, radiusX, radiusY)) return; if (_internal->_constrainQuadratic) radiusX = radiusY = std::max(radiusX, radiusY); RubberbandHandle::RubberbandCoordinate rcNewCorner1(Rubberband::_internal->_mapView, Esri::ArcGISRuntime::Point(mapCenter._mapCoordinate.x()+(radiusX), mapCenter._mapCoordinate.y()+(radiusY), mapCenter._mapCoordinate.spatialReference())); RubberbandHandle::RubberbandCoordinate rcNewCorner2(Rubberband::_internal->_mapView, Esri::ArcGISRuntime::Point(mapCenter._mapCoordinate.x()-(radiusX), mapCenter._mapCoordinate.y()-(radiusY), mapCenter._mapCoordinate.spatialReference())); _internal->_frameHandle->updateFrameShape({rcNewCorner1, rcNewCorner2}); } } void RubberbandMultiEllipseFeature::getSceneRotation(qreal &sinA, qreal &cosA) const { sinA = -_internal->_oVec.y(); cosA = _internal->_oVec.x(); } Esri::ArcGISRuntime::Point addOffsetToMapCoordinate(const Esri::ArcGISRuntime::Point &mapCoordinate, const QPointF &offset) { return Esri::ArcGISRuntime::Point(mapCoordinate.x() + offset.x(), mapCoordinate.y() + offset.y(), mapCoordinate.spatialReference()); } void RubberbandMultiEllipseFeature::updateSectorHandles() { if (Rubberband::_internal->_handleList.isEmpty()) return; if ((unsigned int)Rubberband::_internal->_handleList.size() < _minimumPointCount) return; if (!isSectorEnabled()) return; 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 ); }; const QPointF &biggestRadius = getBiggestRadius(); qreal a = biggestRadius.x(); // getWidth() * 0.5; qreal b = biggestRadius.y(); // 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(); RubberbandPointHandle *handlePointCenter = dynamic_cast(Rubberband::_internal->_handleList.first().get()); if (!handlePointCenter) return; const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointCenter->getCoordinate(); // const Esri::ArcGISRuntime::Point &mapStartP = convert( startP, Rubberband::_internal->_mapView->map()->spatialReference() ); // const Esri::ArcGISRuntime::Point &mapEndP = convert( endP, Rubberband::_internal->_mapView->map()->spatialReference() ); const Esri::ArcGISRuntime::Point &mapStartP = addOffsetToMapCoordinate(mapCenter._mapCoordinate, startP); const Esri::ArcGISRuntime::Point &mapEndP = addOffsetToMapCoordinate(mapCenter._mapCoordinate, endP); 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((!isSectorFixedAngeEnabled())/*_internal->_sectorEnabled*/); _internal->_sectorStartHandle->setDrawColor(Qt::red); _internal->_sectorStartHandle->setFillColor( _handleFillColor ); _internal->_sectorStartHandle->setSelectionColor(_handleSelectionColor); _internal->_sectorStartHandle->setSelectable(true); 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(!isSectorFixedAngeEnabled()/*_internal->_sectorEnabled*/); _internal->_sectorEndHandle->setDrawColor(Qt::red); _internal->_sectorEndHandle->setFillColor( _handleFillColor ); _internal->_sectorEndHandle->setSelectionColor(_handleSelectionColor); _internal->_sectorEndHandle->setSelectable(true); } void RubberbandMultiEllipseFeature::updateRotationHandle() { DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " START" << ", isSectorFixedAngeEnabled()=" << isSectorFixedAngeEnabled() << ", _handleList.size=" << Rubberband::_internal->_handleList.size(); if (!isSectorEnabled() || !isSectorFixedAngeEnabled() || (Rubberband::_internal->_handleList.size()<2)) { _internal->_rotationHandle = nullptr; // destroy } else { static QPixmap *s_icon = nullptr; if (!s_icon) s_icon = new QPixmap( QPixmap( winkelIcon_xpm ).transformed(QTransform::fromScale(0.5,0.5)) ); if (!_internal->_rotationHandle) { _internal->_rotationHandleSize = std::max( s_icon->width(), s_icon->height() ) * 0.5; _internal->_rotationHandle = new RubberbandPointHandle(Rubberband::_internal->_mapView , Rubberband::_internal->_mapScene , QPointF(100, 100) // , _internal->getScenePositionRotationHandle(), , *s_icon ); _internal->_rotationHandle->setDrawColor(_handleDrawColor); _internal->_rotationHandle->setFillColor(_handleFillColor); _internal->_rotationHandle->setSelectionColor(_handleSelectionColor); } _internal -> _rotationHandle->setSceneOffset(getSceneOffsetRotationHandle( (_frameHandleMargin > 0) ? _frameHandleMargin : 0 ) ); _internal -> _rotationHandle->updateCoordinate(getScenePositionRotationHandle()); } DEBUG_LOG(Rubberband::_internal->_logger) << LOGFUNCTION << " END"; } QPointF RubberbandMultiEllipseFeature::getScenePositionRotationHandle() const { if (Rubberband::_internal->_handleList.size()<2) return {}; RubberbandPointHandle *handlePointCenter = dynamic_cast(Rubberband::_internal->_handleList.first().get()); if (!handlePointCenter) return {}; const RubberbandHandle::RubberbandCoordinate &mapCenter = handlePointCenter->getCoordinate(); const QPointF &biggestRadius = getBiggestRadius(); Esri::ArcGISRuntime::Point handlePosPoint(mapCenter._mapCoordinate.x() + 0, mapCenter._mapCoordinate.y() + biggestRadius.y(), mapCenter._mapCoordinate.spatialReference()); RubberbandHandle::RubberbandCoordinate handlePos(Rubberband::_internal->_mapView, handlePosPoint); QPointF scenePoint (handlePos._sceneCoordinate); scenePoint.ry() -= _internal->_rotationHandleSize; // keep center of sceneCoordSystem in mind qreal sinRect = 0; qreal cosRect = 1; getSceneRotation(sinRect, cosRect); scenePoint = MathUtils::rotate(scenePoint, mapCenter._sceneCoordinate, -sinRect, cosRect); // be carefull the scene coordinate system is contra clockwise -- sinA must be negated return scenePoint; } QPointF RubberbandMultiEllipseFeature::getSceneOffsetRotationHandle(qreal margin) { qreal sinPhi = _internal->_oVec.y(); // * _mapOVec.x() + _oVec.x() * _mapOVec.y(); // Addition-Theorem qreal cosPhi = _internal->_oVec.x(); // * _mapOVec.x() - _oVec.y() * _mapOVec.y(); return rotateVertex( QPointF(0,0), 0.0,-(margin+_internal->_rotationHandleSize), sinPhi, cosPhi ); } } } // namespace SituationMap::RB