/** ** ** @RDE_HEADER@ ** Implementation of rubberband class to define point feature geometry in 3d ** $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 #include "rubberband_multi_point_feature_3d.h" #include "rubberband_base_internal.h" #include "event_mouse_navigator.h" #include #include #include #include #include #include #include #include #include #include #include namespace SituationMap { namespace RB { class RubberbandMultiPointFeature3D::InternalData { public: QVector m_marker; GraphicalObjectPath *m_path; QVector m_coordinates; int m_currentIndex; int m_selectionIndex; Esri::ArcGISRuntime::TaskWatcher m_pickingTask; Esri::ArcGISRuntime::TaskWatcher m_selectionTask; QPoint m_pickingPoint; QPointF * m_startMove; ScreenToLocationHelper * m_screenToMap; QPointer m_toElevation; PositionCont m_positionList3D; bool m_doCompleteAction; InternalData() : m_marker() , m_path(nullptr) , m_coordinates() , m_currentIndex(-1) , m_selectionIndex(-1) , m_pickingTask() , m_selectionTask() , m_pickingPoint() , m_startMove(nullptr) , m_screenToMap() , m_toElevation(nullptr) , m_positionList3D() , m_doCompleteAction(false) { m_marker.reserve(100); m_coordinates.reserve(100); } // InternalData() ~InternalData() { delete m_startMove; for (auto marker : qAsConst(m_marker)) delete marker; delete m_path; delete m_screenToMap; } // ~InternalData() }; // RubberbandPointFeature3D::InternalData RubberbandMultiPointFeature3D::RubberbandMultiPointFeature3D( SituationMap::Map * map, QObject * parent, bool isActive ) : Rubberband3D(map,parent,isActive) , _internal( new RubberbandMultiPointFeature3D::InternalData() ) , _isClosed(false) , _isCircular(false) { MapImpl *mapImpl = static_cast(_baseInternal->_map.data()); Esri::ArcGISRuntime::SceneGraphicsView *view = mapImpl->getEsriSceneGraphicsView(); connect( view, &Esri::ArcGISRuntime::SceneGraphicsView::identifyGraphicsOverlaysCompleted, this, &RubberbandMultiPointFeature3D::onGetPickingResult ); _internal->m_screenToMap = new ScreenToLocationHelper(view); connect( _internal->m_screenToMap, &ScreenToLocationHelper::mouseScenePositionChanged, this, &RubberbandMultiPointFeature3D::onMapPosition ); // ToDO } // RubberbandMultiPointFeature3D::RubberbandMultiPointFeature3D( SituationMap::Map * map, QObject * parent = nullptr, bool isActive ) RubberbandMultiPointFeature3D::~RubberbandMultiPointFeature3D() { delete _internal; _internal = nullptr; } // RubberbandMultiPointFeature3D::~RubberbandMultiPointFeature3D() void RubberbandMultiPointFeature3D::activateButtons() { _baseInternal->_hasUndoButton = false; _baseInternal->_undoIsPossible = false; _baseInternal->_applyIsPossible = (_internal->m_coordinates.size() > ((_isClosed && !_isCircular) ? 2 : 1)); RubberbandBase::activateButtons(); } /* RubberbandMultiPointFeature3D::activateButtons */ void circleShape( GraphicalObjectPath *pathObj, const PositionCont &positionList ) { if (positionList.size() < 2) return; PositionCont circleShape; double cx = positionList[0].x(); double cy = positionList[0].y(); double rx = positionList[1].x()-cx; double ry = positionList[1].y()-cy; double corrLon = cos(cy / 57.2957795147); // correct factor from nautic simulation to get a real circle double dPhi = MathConst::pi / 50; // delta angle to rasterize the circle to a polyline auto sqr = [](double x) -> double { return x * x; }; double r = sqrt(sqr(rx*corrLon) + sqr(ry) ); double phi = 0.0; while (phi < MathConst::pi_two) { double sinPhi = sin(phi); double cosPhi = cos(phi); double px = cx + r * cosPhi / corrLon; double py = cy + r*sinPhi; circleShape.append(Point3D(px, py, 0.0)); phi += dPhi; } pathObj->setPositions(circleShape); } // circleShape void RubberbandMultiPointFeature3D::onMapPosition(const Point3D &mapCoordinate) { if (_internal->m_currentIndex < 0) return; if (qIsNaN(mapCoordinate.x())) return; // illegal map position if (qIsNaN(mapCoordinate.y())) return; if ((!_internal->m_toElevation) && (RubberbandBase::_baseInternal->_map->layerFactory().elevationAvailable())) { _internal->m_toElevation = RubberbandBase::_baseInternal->_map->layerFactory().locationToElevation(); // layerFactory has ownership } if ((std::numeric_limits::max()) == _internal->m_currentIndex) { // move all coordinates if (_internal->m_startMove) { QPointF deltaMove = mapCoordinate.toPointF() - *(_internal->m_startMove); _internal->m_positionList3D.clear(); _internal->m_positionList3D.reserve(_internal->m_coordinates.size()); for (int i = 0; i < _internal->m_coordinates.size(); ++i) { const QPointF & pos = _internal->m_coordinates[i] += deltaMove; _internal->m_marker[i]->setOrigin( pos ); if (_internal->m_toElevation) { QMetaObject::Connection conn; conn = connect(_internal->m_toElevation.data(), &SituationMap::LocationToElevationHelper::elevationCompleted, this, [this,pos,conn](double elevation) { disconnect(conn); _internal->m_positionList3D << Point3D(pos.x(),pos.y(),elevation); if (_internal->m_positionList3D.size() == _internal->m_coordinates.size()) { if (_internal->m_path) _internal->m_path->setPositions( _internal->m_positionList3D ); update(); } // qCritical() << "elevation at (" << pos.x() << " , " << pos.y() << ") : " << elevation; }); _internal->m_toElevation->locationToElevation(pos); } else { _internal->m_positionList3D << Point3D::fromQPointF(pos); } } if (_internal->m_toElevation) { if (_internal->m_path) _internal->m_path->setPositions( _internal->m_positionList3D ); update(); } } else _internal->m_startMove = new QPointF(mapCoordinate.toPointF()); return; } if (_internal->m_currentIndex >= _internal->m_coordinates.size()) return; MapImpl *mapImpl = static_cast(_baseInternal->_map.data()); Esri::ArcGISRuntime::SceneGraphicsView *view = mapImpl->getEsriSceneGraphicsView(); Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer = mapImpl->getGraphicsLayer(mapImpl->layerFactory().rubberbandLayer()); if (nullptr == view) return; for (int idx = _internal->m_positionList3D.size(); idx <= _internal->m_currentIndex; ++idx) { _internal->m_positionList3D << mapCoordinate; } _internal->m_coordinates[_internal->m_currentIndex] = mapCoordinate.toPointF(); _internal->m_positionList3D[_internal->m_currentIndex] = mapCoordinate; // qCritical() << "elevation idx=" << _internal->m_currentIndex <<" at (" << mapCoordinate.x() << " , " << mapCoordinate.y() << ") : " << mapCoordinate.z(); GraphicalObjectPictureMarker *&marker = _internal->m_marker[_internal->m_currentIndex]; if (nullptr == marker) { marker = new GraphicalObjectPictureMarker( view, graphicsLayer, QStringLiteral("RubberbandMultiPointFeature3D"), INVALID_MAP_ID ); marker->setImage(markerImage()); marker->setOrigin( mapCoordinate ); marker->updateView(); marker->setPickable(true); Esri::ArcGISRuntime::Graphic *graphic = marker->graphic(); if (graphic) { Esri::ArcGISRuntime::AttributeListModel *attLisModel = graphic->attributes(); if (attLisModel) { // qCritical() << "Rubberband3D-Item =" << _internal->m_currentIndex; attLisModel->insertAttribute(QStringLiteral("Rubberband3D-Item"),_internal->m_currentIndex); } } } else marker->setOrigin( mapCoordinate ); if (_internal->m_coordinates.size() > ((_isClosed && !_isCircular) ? 2 : 1)) { if (nullptr == _internal->m_path) { bool isPolygon = (_isClosed || _isCircular); _internal->m_path = new GraphicalObjectPath(isPolygon ? GraphicalObjectPathType::Polygon : GraphicalObjectPathType::Polyline, view, graphicsLayer, QStringLiteral("RubberbandMultiPointFeature3D"), INVALID_MAP_ID); _internal->m_path->rLineStyle().setLineColor(_handleDrawColor); _internal->m_path->rLineStyle().setLinePattern(LinePattern::Solid); _internal->m_path->rLineStyle().setLineWidth(3); _internal->m_path->setPickable(false); if (isPolygon) { _internal->m_path->rFillStyle().setFillColor(_frameFillColor); } if (_isCircular) { circleShape(_internal->m_path, _internal->m_positionList3D); } else _internal->m_path->setPositions(_internal->m_positionList3D); _internal->m_path->updateView(); } else { if (_isCircular) { circleShape(_internal->m_path, _internal->m_positionList3D); } else _internal->m_path->setPositions(_internal->m_positionList3D); } } if (_isCircular && (0 == _internal->m_currentIndex) && (1 == _internal->m_coordinates.size())) { _internal->m_coordinates << _internal->m_coordinates.first(); _internal->m_positionList3D << _internal->m_positionList3D.first(); _internal->m_marker << nullptr; _internal->m_currentIndex++; // qCritical() << QCursor::pos() << " != " << view->viewport()->mapToGlobal(_internal->m_pickingPoint); _internal->m_doCompleteAction = (QCursor::pos() != view->viewport()->mapToGlobal(_internal->m_pickingPoint)); if (!_internal->m_doCompleteAction) { // Click with out moving the mouse, set a default size of the circle and let the user do some editing // The rubberband doesn't complete automatically in this case. _internal->m_screenToMap->screenToLocation(_internal->m_pickingPoint.x()+100, _internal->m_pickingPoint.y()); } } update(); if (!QGuiApplication::mouseButtons().testFlag(Qt::MouseButton::LeftButton)) { if (_isCircular && _internal->m_doCompleteAction && (1 == _internal->m_currentIndex)) { completeStateAction(nullptr, 0, 0); } else { updateButtonPosition(); activateButtons(); } } } // void RubberbandMultiPointFeature3D::onMapPosition(const QPointF &mapCoordinate) void RubberbandMultiPointFeature3D::onGetPickingResult(const QUuid &taskId, const QList &identifyResults) { if (_internal->m_pickingTask.taskId() == taskId) { for (auto iResult : identifyResults) { const QList &gResults = iResult->graphics(); for (auto pickedGraphic : gResults) { if (pickedGraphic->attributes()) { bool ok = false; int pickedIndex = pickedGraphic->attributes()->attributeValue(QStringLiteral("Rubberband3D-Item")).toInt(&ok); if (ok) { qCritical() << "Picking index " << pickedIndex; _internal->m_currentIndex = pickedIndex; _internal->m_screenToMap->screenToLocation(_internal->m_pickingPoint.x(), _internal->m_pickingPoint.y()); if (pickedIndex < _internal->m_marker.size()) { GraphicalObjectPictureMarker *&marker = _internal->m_marker[pickedIndex]; if (marker) { marker->setImage(markerSelectionImage()); marker->updateView(); } } return; } } } // foreach pickedGraphic } // foreach iResult if ((!_isCircular) || (_internal->m_coordinates.size() < 2)) { // add new coordinate and calculate map location _internal->m_currentIndex = _internal->m_marker.size(); _internal->m_marker << nullptr; _internal->m_coordinates << QPointF(); _internal->m_screenToMap->screenToLocation(_internal->m_pickingPoint.x(), _internal->m_pickingPoint.y()); } } if (_internal->m_selectionTask.taskId() == taskId) { for (auto iResult : identifyResults) { const QList &gResults = iResult->graphics(); for (auto pickedGraphic : gResults) { if (pickedGraphic->attributes()) { bool ok = false; int pickedIndex = pickedGraphic->attributes()->attributeValue(QStringLiteral("Rubberband3D-Item")).toInt(&ok); if ((ok) && (pickedIndex < _internal->m_marker.size())) { GraphicalObjectPictureMarker *&marker = _internal->m_marker[pickedIndex]; if (marker) { _internal->m_selectionIndex = pickedIndex; marker->setImage(markerSelectionImage()); marker->updateView(); return; } } } } } } if ((_internal->m_selectionIndex >= 0) && (_internal->m_selectionIndex < _internal->m_marker.size())) { GraphicalObjectPictureMarker *&marker = _internal->m_marker[_internal->m_selectionIndex]; if (marker) { marker->setImage(markerImage()); marker->updateView(); } _internal->m_selectionIndex = -1; } } // void RubberbandMultiPointFeature3D::onGetPickingResult(const QUuid &taskId, const QList &identifyResults) bool RubberbandMultiPointFeature3D::selectAction(AbstractEventDispatcher *, int x, int y) { delete _internal->m_startMove; _internal->m_startMove = nullptr; if (rubberbandIsAlive()) { if (_baseInternal->buttonProxyActive(QPointF(x,y))) return false; MapImpl *mapImpl = static_cast(_baseInternal->_map.data()); _internal->m_pickingTask.cancel(); // cancel old task _internal->m_pickingPoint = QPoint(x,y); _internal->m_pickingTask = mapImpl->getEsriGeoView()->identifyGraphicsOverlays(x,y,EventMouseNavigator::pickTolerance(),false,std::numeric_limits::max()); _internal->m_currentIndex = -1; return true; } return false; } // bool RubberbandMultiPointFeature3D::selectAction(AbstractEventDispatcher *, int x, int y) bool RubberbandMultiPointFeature3D::setCoordinateAction(AbstractEventDispatcher *, int x, int y) { delete _internal->m_startMove; _internal->m_startMove = nullptr; if (rubberbandIsAlive()) { _baseInternal->_lastClickPosition = QPointF(x,y); if (_baseInternal->buttonProxyActive(_baseInternal->_lastClickPosition)) return false; _internal->m_screenToMap->screenToLocation(x,y); if ((_internal->m_currentIndex >= 0) && (_internal->m_currentIndex < _internal->m_marker.size())) { GraphicalObjectPictureMarker *&marker = _internal->m_marker[_internal->m_currentIndex]; if (marker) { marker->setImage(markerImage()); marker->updateView(); } } return true; } return false; } // bool RubberbandMultiPointFeature3D::setCoordinateAction(AbstractEventDispatcher *, int x, int y) bool RubberbandMultiPointFeature3D::movePickedAction(AbstractEventDispatcher *, int x, int y) { if (rubberbandIsAlive()) { _internal->m_screenToMap->screenToLocation(x,y); return true; } return false; } // bool RubberbandMultiPointFeature3D::movePickedAction(AbstractEventDispatcher *, int x, int y) bool RubberbandMultiPointFeature3D::moveAction( AbstractEventDispatcher*, int x, int y ) { if (rubberbandIsAlive()) { if (_baseInternal->buttonProxyActive(QPointF(x,y))) return false; MapImpl *mapImpl = static_cast(_baseInternal->_map.data()); _internal->m_selectionTask.cancel(); // cancel old task _internal->m_selectionTask = mapImpl->getEsriGeoView()->identifyGraphicsOverlays(x,y,EventMouseNavigator::pickTolerance(),false,std::numeric_limits::max()); if ((_internal->m_selectionIndex >= 0) && (_internal->m_selectionIndex < _internal->m_marker.size())) { GraphicalObjectPictureMarker *&marker = _internal->m_marker[_internal->m_selectionIndex]; if (marker) { marker->setImage(markerImage()); marker->updateView(); } } _internal->m_selectionIndex = -1; } return false; } // bool RubberbandMultiPointFeature3D::moveAction(AbstractEventDispatcher*,int,int) bool RubberbandMultiPointFeature3D::completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) { if (_internal->m_coordinates.size() > ((_isClosed && !_isCircular) ? 2 : 1)) { return applyAndEndAction(causer,x,y); } return false; } // bool RubberbandMultiPointFeature3D::completeStateAction ( AbstractEventDispatcher * causer, int x, int y ) bool RubberbandMultiPointFeature3D::contextMenuAction ( AbstractEventDispatcher*,int,int ) { return rubberbandIsAlive(); } bool RubberbandMultiPointFeature3D::deleteAction ( AbstractEventDispatcher*,int,int ) { return rubberbandIsAlive(); } bool RubberbandMultiPointFeature3D::undoAction ( AbstractEventDispatcher*,int,int ) { return rubberbandIsAlive(); } unsigned int RubberbandMultiPointFeature3D::coordinateCount() const { return (_internal->m_coordinates.size()); } // unsigned int RubberbandMultiPointFeature3D::coordinateCount() const void RubberbandMultiPointFeature3D::update() { for (auto marker : qAsConst(_internal->m_marker)) if (marker) marker->updateView(); if (_internal->m_path) _internal->m_path->updateView(); externalUpdate(); } // RubberbandMultiPointFeature3D::update() QVector RubberbandMultiPointFeature3D::coordinateVector( CoordinateSystem cs ) const { QVector coordinateVector; coordinateVector.reserve(_internal->m_coordinates.size()); for (const auto &mapCoordinate : qAsConst(_internal->m_coordinates)) coordinateVector << projectCoordinateFromMap( cs, mapCoordinate ); return coordinateVector; } // QVector RubberbandMultiPointFeature3D::coordinateVector( CoordinateSystem cs ) const QPointF RubberbandMultiPointFeature3D::singleCoordinate( unsigned int idx, CoordinateSystem cs ) const { if (coordinateCount() > idx) return coordinateVector(cs).at(idx); return QPointF(); } // QPointF RubberbandMultiPointFeature3D::singleCoordinate( unsigned int idx, CoordinateSystem cs ) const void RubberbandMultiPointFeature3D::setCoordinateVector( const QVector & coordinateVector, CoordinateSystem cs ) { _internal->m_coordinates.clear(); for (const auto &geoCoordinate : coordinateVector) _internal->m_coordinates << projectCoordinateToMap( cs, geoCoordinate ); if ((!_internal->m_toElevation) && (_baseInternal->_map->layerFactory().elevationAvailable())) { _internal->m_toElevation = _baseInternal->_map->layerFactory().locationToElevation(); // layerFactory has ownership } // now get elevations _internal->m_positionList3D.clear(); for (const auto &pos : qAsConst(_internal->m_coordinates)) { if (_internal->m_toElevation) { QMetaObject::Connection conn; conn = connect(_internal->m_toElevation.data(), &SituationMap::LocationToElevationHelper::elevationCompleted, this, [this,pos,conn](double elevation) { disconnect(conn); _internal->m_positionList3D << Point3D(pos.x(),pos.y(),elevation); if (_internal->m_positionList3D.size() == _internal->m_coordinates.size()) { if (_internal->m_path) _internal->m_path->setPositions( _internal->m_positionList3D ); update(); } }); _internal->m_toElevation->locationToElevation(pos); } else _internal->m_positionList3D << Point3D::fromQPointF(pos); } } // void RubberbandMultiPointFeature3D::setCoordinateVector( const QVector & coordinateVector, CoordinateSystem cs ) void RubberbandMultiPointFeature3D::setCoordinateVector(const QVector & coordinateVector, CoordinateSystem cs) { _internal->m_coordinates.clear(); _internal->m_positionList3D.clear(); for (const auto &geoCoordinate : coordinateVector) { const QPointF & projCoordinate = projectCoordinateToMap( cs, geoCoordinate.toPointF() ); _internal->m_coordinates << projCoordinate; _internal->m_positionList3D << Point3D(projCoordinate.x(),projCoordinate.y(),geoCoordinate.z()); } } // void RubberbandMultiPointFeature3D::setCoordinateVector(const QVector & coordinateVector, CoordinateSystem cs) QVector RubberbandMultiPointFeature3D::coordinateVector3D(CoordinateSystem cs) const { QVector coordinateVector; coordinateVector.reserve(_internal->m_coordinates.size()); for (const auto &mapCoordinate : qAsConst(_internal->m_coordinates)) coordinateVector << Point3D::fromQPointF( projectCoordinateFromMap(cs,mapCoordinate) ); return coordinateVector; } // QVector RubberbandMultiPointFeature3D::coordinateVector3D(CoordinateSystem cs) const Point3D RubberbandMultiPointFeature3D::singleCoordinate3D(unsigned int idx, CoordinateSystem cs) const { if (coordinateCount() > idx) { return coordinateVector3D(cs).at(idx); } return Point3D(); } // Point3D RubberbandMultiPointFeature3D::singleCoordinate3D(unsigned int idx, CoordinateSystem cs) const } } /* namespace SituationMap::RB */