bool acceptMatch( const QgsPointLocator::Match& match ) { return match.point() != mPoint; }
QgsPoint QgsMeasureTool::snapPoint( const QPoint& p ) { QgsPointLocator::Match m = mCanvas->snappingUtils()->snapToMap( p ); return m.isValid() ? m.point() : mCanvas->getCoordinateTransform()->toMapCoordinates( p ); }
void testLayerUpdates() { QgsPointLocator loc( mVL ); QgsPointLocator::Match mAddV0 = loc.nearestVertex( QgsPoint( 12, 12 ), 999 ); QVERIFY( mAddV0.isValid() ); QCOMPARE( mAddV0.point(), QgsPoint( 1, 1 ) ); mVL->startEditing(); // add a new feature QgsFeature ff( 0 ); QgsPolygon polygon; QgsPolyline polyline; polyline << QgsPoint( 10, 11 ) << QgsPoint( 11, 10 ) << QgsPoint( 11, 11 ) << QgsPoint( 10, 11 ); polygon << polyline; ff.setGeometry( QgsGeometry::fromPolygon( polygon ) ); QgsFeatureList flist; flist << ff; bool resA = mVL->addFeature( ff ); QVERIFY( resA ); // verify it is added in the point locator QgsPointLocator::Match mAddV = loc.nearestVertex( QgsPoint( 12, 12 ), 999 ); QVERIFY( mAddV.isValid() ); QCOMPARE( mAddV.point(), QgsPoint( 11, 11 ) ); QgsPointLocator::Match mAddE = loc.nearestEdge( QgsPoint( 11.1, 10.5 ), 999 ); QVERIFY( mAddE.isValid() ); QCOMPARE( mAddE.point(), QgsPoint( 11, 10.5 ) ); QgsPointLocator::MatchList mAddA = loc.pointInPolygon( QgsPoint( 10.8, 10.8 ) ); QVERIFY( mAddA.count() == 1 ); // change geometry QgsGeometry* newGeom = new QgsGeometry( *ff.constGeometry() ); newGeom->moveVertex( 10, 10, 2 ); // change 11,11 to 10,10 mVL->changeGeometry( ff.id(), newGeom ); delete newGeom; // verify it is changed in the point locator QgsPointLocator::Match mChV = loc.nearestVertex( QgsPoint( 12, 12 ), 999 ); QVERIFY( mChV.isValid() ); QVERIFY( mChV.point() != QgsPoint( 11, 11 ) ); // that point does not exist anymore mChV = loc.nearestVertex( QgsPoint( 9, 9 ), 999 ); QVERIFY( mChV.isValid() ); QVERIFY( mChV.point() == QgsPoint( 10, 10 ) ); // updated point // delete feature bool resD = mVL->deleteFeature( ff.id() ); QVERIFY( resD ); // verify it is deleted from the point locator QgsPointLocator::Match mDelV = loc.nearestVertex( QgsPoint( 12, 12 ), 999 ); QVERIFY( mDelV.isValid() ); QCOMPARE( mDelV.point(), QgsPoint( 1, 1 ) ); mVL->rollBack(); }
bool acceptMatch( const QgsPointLocator::Match& m ) override { return m.hasEdge(); }
QgsCadUtils::AlignMapPointOutput QgsCadUtils::alignMapPoint( const QgsPointXY &originalMapPoint, const QgsCadUtils::AlignMapPointContext &ctx ) { QgsCadUtils::AlignMapPointOutput res; res.valid = true; res.softLockCommonAngle = -1; // try to snap to anything QgsPointLocator::Match snapMatch = ctx.snappingUtils->snapToMap( originalMapPoint ); QgsPointXY point = snapMatch.isValid() ? snapMatch.point() : originalMapPoint; // try to snap explicitly to a segment - useful for some constraints QgsPointXY edgePt0, edgePt1; EdgesOnlyFilter edgesOnlyFilter; QgsPointLocator::Match edgeMatch = ctx.snappingUtils->snapToMap( originalMapPoint, &edgesOnlyFilter ); if ( edgeMatch.hasEdge() ) edgeMatch.edgePoints( edgePt0, edgePt1 ); res.edgeMatch = edgeMatch; QgsPointXY previousPt, penultimatePt; if ( ctx.cadPointList.count() >= 2 ) previousPt = ctx.cadPointList.at( 1 ); if ( ctx.cadPointList.count() >= 3 ) penultimatePt = ctx.cadPointList.at( 2 ); // ***************************** // ---- X constraint if ( ctx.xConstraint.locked ) { if ( !ctx.xConstraint.relative ) { point.setX( ctx.xConstraint.value ); } else if ( ctx.cadPointList.count() >= 2 ) { point.setX( previousPt.x() + ctx.xConstraint.value ); } if ( edgeMatch.hasEdge() && !ctx.yConstraint.locked ) { // intersect with snapped segment line at X ccordinate const double dx = edgePt1.x() - edgePt0.x(); if ( dx == 0 ) { point.setY( edgePt0.y() ); } else { const double dy = edgePt1.y() - edgePt0.y(); point.setY( edgePt0.y() + ( dy * ( point.x() - edgePt0.x() ) ) / dx ); } } } // ***************************** // ---- Y constraint if ( ctx.yConstraint.locked ) { if ( !ctx.yConstraint.relative ) { point.setY( ctx.yConstraint.value ); } else if ( ctx.cadPointList.count() >= 2 ) { point.setY( previousPt.y() + ctx.yConstraint.value ); } if ( edgeMatch.hasEdge() && !ctx.xConstraint.locked ) { // intersect with snapped segment line at Y ccordinate const double dy = edgePt1.y() - edgePt0.y(); if ( dy == 0 ) { point.setX( edgePt0.x() ); } else { const double dx = edgePt1.x() - edgePt0.x(); point.setX( edgePt0.x() + ( dx * ( point.y() - edgePt0.y() ) ) / dy ); } } } // ***************************** // ---- Common Angle constraint if ( !ctx.angleConstraint.locked && ctx.cadPointList.count() >= 2 && ctx.commonAngleConstraint.locked && ctx.commonAngleConstraint.value != 0 ) { double commonAngle = ctx.commonAngleConstraint.value * M_PI / 180; // see if soft common angle constraint should be performed // only if not in HardLock mode double softAngle = std::atan2( point.y() - previousPt.y(), point.x() - previousPt.x() ); double deltaAngle = 0; if ( ctx.commonAngleConstraint.relative && ctx.cadPointList.count() >= 3 ) { // compute the angle relative to the last segment (0° is aligned with last segment) deltaAngle = std::atan2( previousPt.y() - penultimatePt.y(), previousPt.x() - penultimatePt.x() ); softAngle -= deltaAngle; } int quo = std::round( softAngle / commonAngle ); if ( std::fabs( softAngle - quo * commonAngle ) * 180.0 * M_1_PI <= SOFT_CONSTRAINT_TOLERANCE_DEGREES ) { // also check the distance in pixel to the line, otherwise it's too sticky at long ranges softAngle = quo * commonAngle; // http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html // use the direction vector (cos(a),sin(a)) from previous point. |x2-x1|=1 since sin2+cos2=1 const double dist = std::fabs( std::cos( softAngle + deltaAngle ) * ( previousPt.y() - point.y() ) - std::sin( softAngle + deltaAngle ) * ( previousPt.x() - point.x() ) ); if ( dist / ctx.mapUnitsPerPixel < SOFT_CONSTRAINT_TOLERANCE_PIXEL ) { res.softLockCommonAngle = 180.0 / M_PI * softAngle; } } } // angle can be locked in one of the two ways: // 1. "hard" lock defined by the user // 2. "soft" lock from common angle (e.g. 45 degrees) bool angleLocked = false, angleRelative = false; int angleValueDeg = 0; if ( ctx.angleConstraint.locked ) { angleLocked = true; angleRelative = ctx.angleConstraint.relative; angleValueDeg = ctx.angleConstraint.value; } else if ( res.softLockCommonAngle != -1 ) { angleLocked = true; angleRelative = ctx.commonAngleConstraint.relative; angleValueDeg = res.softLockCommonAngle; } // ***************************** // ---- Angle constraint // input angles are in degrees if ( angleLocked ) { double angleValue = angleValueDeg * M_PI / 180; if ( angleRelative && ctx.cadPointList.count() >= 3 ) { // compute the angle relative to the last segment (0° is aligned with last segment) angleValue += std::atan2( previousPt.y() - penultimatePt.y(), previousPt.x() - penultimatePt.x() ); } double cosa = std::cos( angleValue ); double sina = std::sin( angleValue ); double v = ( point.x() - previousPt.x() ) * cosa + ( point.y() - previousPt.y() ) * sina; if ( ctx.xConstraint.locked && ctx.yConstraint.locked ) { // do nothing if both X,Y are already locked } else if ( ctx.xConstraint.locked ) { if ( qgsDoubleNear( cosa, 0.0 ) ) { res.valid = false; } else { double x = ctx.xConstraint.value; if ( !ctx.xConstraint.relative ) { x -= previousPt.x(); } point.setY( previousPt.y() + x * sina / cosa ); } } else if ( ctx.yConstraint.locked ) { if ( qgsDoubleNear( sina, 0.0 ) ) { res.valid = false; } else { double y = ctx.yConstraint.value; if ( !ctx.yConstraint.relative ) { y -= previousPt.y(); } point.setX( previousPt.x() + y * cosa / sina ); } } else { point.setX( previousPt.x() + cosa * v ); point.setY( previousPt.y() + sina * v ); } if ( edgeMatch.hasEdge() && !ctx.distanceConstraint.locked ) { // magnetize to the intersection of the snapped segment and the lockedAngle // line of previous point + locked angle const double x1 = previousPt.x(); const double y1 = previousPt.y(); const double x2 = previousPt.x() + cosa; const double y2 = previousPt.y() + sina; // line of snapped segment const double x3 = edgePt0.x(); const double y3 = edgePt0.y(); const double x4 = edgePt1.x(); const double y4 = edgePt1.y(); const double d = ( x1 - x2 ) * ( y3 - y4 ) - ( y1 - y2 ) * ( x3 - x4 ); // do not compute intersection if lines are almost parallel // this threshold might be adapted if ( std::fabs( d ) > 0.01 ) { point.setX( ( ( x3 - x4 ) * ( x1 * y2 - y1 * x2 ) - ( x1 - x2 ) * ( x3 * y4 - y3 * x4 ) ) / d ); point.setY( ( ( y3 - y4 ) * ( x1 * y2 - y1 * x2 ) - ( y1 - y2 ) * ( x3 * y4 - y3 * x4 ) ) / d ); } } } // ***************************** // ---- Distance constraint if ( ctx.distanceConstraint.locked && ctx.cadPointList.count() >= 2 ) { if ( ctx.xConstraint.locked || ctx.yConstraint.locked ) { // perform both to detect errors in constraints if ( ctx.xConstraint.locked ) { QgsPointXY verticalPt0( ctx.xConstraint.value, point.y() ); QgsPointXY verticalPt1( ctx.xConstraint.value, point.y() + 1 ); res.valid &= lineCircleIntersection( previousPt, ctx.distanceConstraint.value, verticalPt0, verticalPt1, point ); } if ( ctx.yConstraint.locked ) { QgsPointXY horizontalPt0( point.x(), ctx.yConstraint.value ); QgsPointXY horizontalPt1( point.x() + 1, ctx.yConstraint.value ); res.valid &= lineCircleIntersection( previousPt, ctx.distanceConstraint.value, horizontalPt0, horizontalPt1, point ); } } else { const double dist = std::sqrt( point.sqrDist( previousPt ) ); if ( dist == 0 ) { // handle case where mouse is over origin and distance constraint is enabled // take arbitrary horizontal line point.set( previousPt.x() + ctx.distanceConstraint.value, previousPt.y() ); } else { const double vP = ctx.distanceConstraint.value / dist; point.set( previousPt.x() + ( point.x() - previousPt.x() ) * vP, previousPt.y() + ( point.y() - previousPt.y() ) * vP ); } if ( edgeMatch.hasEdge() && !ctx.angleConstraint.locked ) { // we will magnietize to the intersection of that segment and the lockedDistance ! res.valid &= lineCircleIntersection( previousPt, ctx.distanceConstraint.value, edgePt0, edgePt1, point ); } } } // ***************************** // ---- calculate CAD values QgsDebugMsgLevel( QString( "point: %1 %2" ).arg( point.x() ).arg( point.y() ), 4 ); QgsDebugMsgLevel( QString( "previous point: %1 %2" ).arg( previousPt.x() ).arg( previousPt.y() ), 4 ); QgsDebugMsgLevel( QString( "penultimate point: %1 %2" ).arg( penultimatePt.x() ).arg( penultimatePt.y() ), 4 ); //QgsDebugMsg( QString( "dx: %1 dy: %2" ).arg( point.x() - previousPt.x() ).arg( point.y() - previousPt.y() ) ); //QgsDebugMsg( QString( "ddx: %1 ddy: %2" ).arg( previousPt.x() - penultimatePt.x() ).arg( previousPt.y() - penultimatePt.y() ) ); res.finalMapPoint = point; return res; }
bool acceptMatch( const QgsPointLocator::Match& match ) { QgsPoint p1, p2; match.edgePoints( p1, p2 ); return !( p1 == mP1 && p2 == mP2 ) && !( p1 == mP2 && p2 == mP1 ); }
QgsGeometry QgsMapToolDeletePart::partUnderPoint( QPoint point, QgsFeatureId &fid, int &partNum ) { QgsFeature f; QgsGeometry geomPart; switch ( vlayer->geometryType() ) { case QgsWkbTypes::PointGeometry: case QgsWkbTypes::LineGeometry: { QgsPointLocator::Match match = mCanvas->snappingUtils()->snapToCurrentLayer( point, QgsPointLocator::Types( QgsPointLocator::Vertex | QgsPointLocator::Edge ) ); if ( !match.isValid() ) return geomPart; int snapVertex = match.vertexIndex(); vlayer->getFeatures( QgsFeatureRequest().setFilterFid( match.featureId() ) ).nextFeature( f ); QgsGeometry g = f.geometry(); if ( !g.isMultipart() ) { fid = match.featureId(); return QgsGeometry::fromPointXY( match.point() ); } if ( g.wkbType() == QgsWkbTypes::MultiPoint || g.wkbType() == QgsWkbTypes::MultiPoint25D ) { fid = match.featureId(); partNum = snapVertex; return QgsGeometry::fromPointXY( match.point() ); } if ( g.wkbType() == QgsWkbTypes::MultiLineString || g.wkbType() == QgsWkbTypes::MultiLineString25D ) { QgsMultiPolylineXY mline = g.asMultiPolyline(); for ( int part = 0; part < mline.count(); part++ ) { if ( snapVertex < mline[part].count() ) { fid = match.featureId(); partNum = part; return QgsGeometry::fromPolylineXY( mline[part] ); } snapVertex -= mline[part].count(); } } break; } case QgsWkbTypes::PolygonGeometry: { QgsPointXY layerCoords = toLayerCoordinates( vlayer, point ); double searchRadius = QgsTolerance::vertexSearchRadius( mCanvas->currentLayer(), mCanvas->mapSettings() ); QgsRectangle selectRect( layerCoords.x() - searchRadius, layerCoords.y() - searchRadius, layerCoords.x() + searchRadius, layerCoords.y() + searchRadius ); QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( selectRect ) ); fit.nextFeature( f ); QgsGeometry g = f.geometry(); if ( g.isNull() ) return geomPart; if ( !g.isMultipart() ) { fid = f.id(); return geomPart; } QgsMultiPolygonXY mpolygon = g.asMultiPolygon(); for ( int part = 0; part < mpolygon.count(); part++ ) // go through the polygons { const QgsPolygonXY &polygon = mpolygon[part]; QgsGeometry partGeo = QgsGeometry::fromPolygonXY( polygon ); if ( partGeo.contains( &layerCoords ) ) { fid = f.id(); partNum = part; return partGeo; } } break; } default: { break; } } return geomPart; }
void QgsMapToolOffsetCurve::canvasReleaseEvent( QgsMapMouseEvent* e ) { if ( !mCanvas ) { return; } QgsVectorLayer* layer = currentVectorLayer(); if ( !layer ) { deleteRubberBandAndGeometry(); notifyNotVectorLayer(); return; } if ( e->button() == Qt::RightButton ) { deleteRubberBandAndGeometry(); deleteDistanceWidget(); return; } if ( !mOriginalGeometry ) { deleteRubberBandAndGeometry(); mGeometryModified = false; mForceCopy = false; if ( e->button() == Qt::RightButton ) { return; } QgsSnappingUtils* snapping = mCanvas->snappingUtils(); // store previous settings int oldType; double oldSearchRadius; QgsTolerance::UnitType oldSearchRadiusUnit; QgsSnappingUtils::SnapToMapMode oldMode = snapping->snapToMapMode(); snapping->defaultSettings( oldType, oldSearchRadius, oldSearchRadiusUnit ); // setup new settings (temporary) QSettings settings; snapping->setSnapToMapMode( QgsSnappingUtils::SnapAllLayers ); snapping->setDefaultSettings( QgsPointLocator::Edge, settings.value( "/qgis/digitizing/search_radius_vertex_edit", 10 ).toDouble(), ( QgsTolerance::UnitType ) settings.value( "/qgis/digitizing/search_radius_vertex_edit_unit", QgsTolerance::Pixels ).toInt() ); QgsPointLocator::Match match = snapping->snapToMap( e->pos() ); // restore old settings snapping->setSnapToMapMode( oldMode ); snapping->setDefaultSettings( oldType, oldSearchRadius, oldSearchRadiusUnit ); if ( match.hasEdge() && match.layer() ) { mSourceLayerId = match.layer()->id(); QgsFeature fet; if ( match.layer()->getFeatures( QgsFeatureRequest( match.featureId() ) ).nextFeature( fet ) ) { mForceCopy = ( e->modifiers() & Qt::ControlModifier ); //no geometry modification if ctrl is pressed mOriginalGeometry = createOriginGeometry( match.layer(), match, fet ); mRubberBand = createRubberBand(); if ( mRubberBand ) { mRubberBand->setToGeometry( mOriginalGeometry, layer ); } mModifiedFeature = fet.id(); createDistanceWidget(); } } return; } applyOffset(); }