bool QgsClockwiseAngleComparer::operator()( const QgsPointXY &a, const QgsPointXY &b ) const { const bool aIsLeft = a.x() < mVertex.x(); const bool bIsLeft = b.x() < mVertex.x(); if ( aIsLeft != bIsLeft ) return bIsLeft; if ( qgsDoubleNear( a.x(), mVertex.x() ) && qgsDoubleNear( b.x(), mVertex.x() ) ) { if ( a.y() >= mVertex.y() || b.y() >= mVertex.y() ) { return b.y() < a.y(); } else { return a.y() < b.y(); } } else { const QgsVector oa = a - mVertex; const QgsVector ob = b - mVertex; const double det = oa.crossProduct( ob ); if ( qgsDoubleNear( det, 0.0 ) ) { return oa.lengthSquared() < ob.lengthSquared(); } else { return det < 0; } } }
void QgsSelectedFeature::moveSelectedVertexes( QgsVector v ) { int nUpdates = 0; Q_FOREACH ( QgsVertexEntry *entry, mVertexMap ) { if ( entry->isSelected() ) nUpdates++; } if ( nUpdates == 0 ) return; mVlayer->beginEditCommand( QObject::tr( "Moved vertices" ) ); int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ); beginGeometryChange(); QMultiMap<double, QgsSnappingResult> currentResultList; for ( int i = mVertexMap.size() - 1; i > -1 && nUpdates > 0; i-- ) { QgsVertexEntry *entry = mVertexMap.value( i, nullptr ); if ( !entry || !entry->isSelected() ) continue; if ( topologicalEditing ) { // snap from current vertex currentResultList.clear(); mVlayer->snapWithContext( entry->pointV1(), ZERO_TOLERANCE, currentResultList, QgsSnapper::SnapToVertex ); } // only last update should trigger the geometry update // as vertex selection gets lost on the update if ( --nUpdates == 0 ) endGeometryChange(); QgsPointV2 p = entry->point(); p.setX( p.x() + v.x() ); p.setY( p.y() + v.y() ); mVlayer->moveVertex( p, mFeatureId, i ); if ( topologicalEditing ) { QMultiMap<double, QgsSnappingResult>::iterator resultIt = currentResultList.begin(); for ( ; resultIt != currentResultList.end(); ++resultIt ) { // move all other if ( mFeatureId != resultIt.value().snappedAtGeometry ) mVlayer->moveVertex( p, resultIt.value().snappedAtGeometry, resultIt.value().snappedVertexNr ); } } } if ( nUpdates > 0 ) endGeometryChange(); mVlayer->endEditCommand(); }
// // distance of point q from line through p in direction v // return >0 => q lies left of the line // <0 => q lies right of the line // double QgsGeometryValidator::distLine2Point( const QgsPoint& p, QgsVector v, const QgsPoint& q ) { if ( qgsDoubleNear( v.length(), 0 ) ) { throw QgsException( QObject::tr( "invalid line" ) ); } return ( v.x()*( q.y() - p.y() ) - v.y()*( q.x() - p.x() ) ) / v.length(); }
QgsVector calcMotion( const QgsPoint &a, const QgsPoint &b, const QgsPoint &c, double lowerThreshold, double upperThreshold ) { QgsVector p = a - b; QgsVector q = c - b; if ( qgsDoubleNear( p.length(), 0.0 ) || qgsDoubleNear( q.length(), 0.0 ) ) return QgsVector( 0, 0 ); // 2.0 is a magic number from the original JOSM source code double scale = 2.0 * std::min( p.length(), q.length() ); p = p.normalized(); q = q.normalized(); double dotProduct = p * q; if ( !dotProductWithinAngleTolerance( dotProduct, lowerThreshold, upperThreshold ) ) { return QgsVector( 0, 0 ); } // wonderful nasty hack which has survived through JOSM -> id -> QGIS // to deal with almost-straight segments (angle is closer to 180 than to 90/270). if ( dotProduct < -M_SQRT1_2 ) dotProduct += 1.0; QgsVector new_v = p + q; // 0.1 magic number from JOSM implementation - think this is to limit each iterative step return new_v.normalized() * ( 0.1 * dotProduct * scale ); }
bool QgsRay2D::intersects( const QgsLineSegment2D &segment, QgsPointXY &intersectPoint ) const { const QgsVector ao = origin - segment.start(); const QgsVector ab = segment.end() - segment.start(); const double det = ab.crossProduct( direction ); if ( qgsDoubleNear( det, 0.0 ) ) { const int abo = segment.pointLeftOfLine( origin ); if ( abo != 0 ) { return false; } else { const double distA = ao * direction; const double distB = ( origin - segment.end() ) * direction; if ( distA > 0 && distB > 0 ) { return false; } else { if ( ( distA > 0 ) != ( distB > 0 ) ) intersectPoint = origin; else if ( distA > distB ) // at this point, both distances are negative intersectPoint = segment.start(); // hence the nearest point is A else intersectPoint = segment.end(); return true; } } } else { const double u = ao.crossProduct( direction ) / det; if ( u < 0.0 || 1.0 < u ) { return false; } else { const double t = -ab.crossProduct( ao ) / det; intersectPoint = origin + direction * t; return qgsDoubleNear( t, 0.0 ) || t > 0; } } }
bool QgsGeometryUtils::lineIntersection( const QgsPointV2& p1, const QgsVector& v, const QgsPointV2& q1, const QgsVector& w, QgsPointV2& inter ) { double d = v.y() * w.x() - v.x() * w.y(); if ( d == 0 ) return false; double dx = q1.x() - p1.x(); double dy = q1.y() - p1.y(); double k = ( dy * w.x() - dx * w.y() ) / d; inter = QgsPointV2( p1.x() + v.x() * k, p1.y() + v.y() * k ); return true; }
double normalizedDotProduct( const QgsPoint &a, const QgsPoint &b, const QgsPoint &c ) { QgsVector p = a - b; QgsVector q = c - b; if ( p.length() > 0 ) p = p.normalized(); if ( q.length() > 0 ) q = q.normalized(); return p * q; }
bool QgsGeometryValidator::intersectLines( const QgsPoint& p, QgsVector v, const QgsPoint& q, QgsVector w, QgsPoint &s ) { double d = v.y() * w.x() - v.x() * w.y(); if ( qgsDoubleNear( d, 0 ) ) return false; double dx = q.x() - p.x(); double dy = q.y() - p.y(); double k = ( dy * w.x() - dx * w.y() ) / d; s = p + v * k; return true; }
void QgsGeometryValidator::checkRingIntersections( int p0, int i0, const QgsPolyline &ring0, int p1, int i1, const QgsPolyline &ring1 ) { for ( int i = 0; !mStop && i < ring0.size() - 1; i++ ) { QgsVector v = ring0[i+1] - ring0[i]; for ( int j = 0; !mStop && j < ring1.size() - 1; j++ ) { QgsVector w = ring1[j+1] - ring1[j]; QgsPoint s; if ( intersectLines( ring0[i], v, ring1[j], w, s ) ) { double d = -distLine2Point( ring0[i], v.perpVector(), s ); if ( d >= 0 && d <= v.length() ) { d = -distLine2Point( ring1[j], w.perpVector(), s ); if ( d > 0 && d < w.length() && ring0[i+1] != ring1[j+1] && ring0[i+1] != ring1[j] && ring0[i+0] != ring1[j+1] && ring0[i+0] != ring1[j] ) { QString msg = QObject::tr( "segment %1 of ring %2 of polygon %3 intersects segment %4 of ring %5 of polygon %6 at %7" ) .arg( i0 ).arg( i ).arg( p0 ) .arg( i1 ).arg( j ).arg( p1 ) .arg( s.toString() ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg, s ) ); mErrorCount++; } } } } } }
double QgsVector::angle( QgsVector v ) const { return v.angle() - angle(); }
void QgsGeometryValidator::validatePolyline( int i, QgsPolyline line, bool ring ) { if ( ring ) { if ( line.size() < 4 ) { QString msg = QObject::tr( "ring %1 with less than four points" ).arg( i ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg ) ); mErrorCount++; return; } if ( line[0] != line[ line.size()-1 ] ) { QString msg = QObject::tr( "ring %1 not closed" ).arg( i ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg ) ); mErrorCount++; return; } } else if ( line.size() < 2 ) { QString msg = QObject::tr( "line %1 with less than two points" ).arg( i ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg ) ); mErrorCount++; return; } int j = 0; while ( j < line.size() - 1 ) { int n = 0; while ( j < line.size() - 1 && line[j] == line[j+1] ) { line.remove( j ); n++; } if ( n > 0 ) { QString msg = QObject::tr( "line %1 contains %n duplicate node(s) at %2", "number of duplicate nodes", n ).arg( i ).arg( j ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg, line[j] ) ); mErrorCount++; } j++; } for ( j = 0; !mStop && j < line.size() - 3; j++ ) { QgsVector v = line[j+1] - line[j]; double vl = v.length(); int n = ( j == 0 && ring ) ? line.size() - 2 : line.size() - 1; for ( int k = j + 2; !mStop && k < n; k++ ) { QgsVector w = line[k+1] - line[k]; QgsPoint s; if ( !intersectLines( line[j], v, line[k], w, s ) ) continue; double d = 0.0; try { d = -distLine2Point( line[j], v.perpVector(), s ); } catch ( QgsException & e ) { Q_UNUSED( e ); QgsDebugMsg( "Error validating: " + e.what() ); continue; } if ( d < 0 || d > vl ) continue; try { d = -distLine2Point( line[k], w.perpVector(), s ); } catch ( QgsException & e ) { Q_UNUSED( e ); QgsDebugMsg( "Error validating: " + e.what() ); continue; } if ( d <= 0 || d >= w.length() ) continue; QString msg = QObject::tr( "segments %1 and %2 of line %3 intersect at %4" ).arg( j ).arg( k ).arg( i ).arg( s.toString() ); QgsDebugMsg( msg ); emit errorFound( QgsGeometry::Error( msg, s ) ); mErrorCount++; } } }
void QgsMapToolNodeTool::canvasMoveEvent( QMouseEvent * e ) { if ( !mSelectedFeature || !mClicked ) return; QgsVectorLayer* vlayer = mSelectedFeature->vlayer(); Q_ASSERT( vlayer ); mSelectAnother = false; if ( mMoving ) { // create rubberband, if none exists if ( mRubberBands.empty() ) { if ( mIsPoint ) { QList<QgsVertexEntry*> &vertexMap = mSelectedFeature->vertexMap(); for ( int i = 0; i < vertexMap.size(); i++ ) { if ( vertexMap[i]->isSelected() ) { QgsRubberBand* rb = createRubberBandMarker( vertexMap[i]->point(), vlayer ); mRubberBands.append( rb ); } } } createMovingRubberBands(); QList<QgsSnappingResult> snapResults; QgsPoint posMapCoord = snapPointFromResults( snapResults, e->pos() ); mPosMapCoordBackup = posMapCoord; } else { // move rubberband QList<QgsSnappingResult> snapResults; mSnapper.snapToBackgroundLayers( e->pos(), snapResults, QList<QgsPoint>() << mClosestMapVertex ); // get correct coordinates to move to QgsPoint posMapCoord = snapPointFromResults( snapResults, e->pos() ); QgsPoint pressMapCoords; if ( snapResults.size() > 0 ) { pressMapCoords = mClosestMapVertex; } else { pressMapCoords = toMapCoordinates( mPressCoordinates ); } QgsVector offset = posMapCoord - pressMapCoords; // handle points if ( mIsPoint ) { for ( int i = 0; i < mRubberBands.size(); i++ ) { mRubberBands[i]->setTranslationOffset( offset.x(), offset.y() ); } return; } // move points QList<QgsVertexEntry*> &vertexMap = mSelectedFeature->vertexMap(); for ( int i = 0; i < vertexMap.size(); i++ ) { if ( !vertexMap[i]->isSelected() ) continue; QgsPoint p = toMapCoordinates( vlayer, vertexMap[i]->point() ) + offset; mRubberBands[vertexMap[i]->rubberBandNr()]->movePoint( vertexMap[i]->rubberBandIndex(), p ); if ( vertexMap[i]->rubberBandIndex() == 0 ) { mRubberBands[vertexMap[i]->rubberBandNr()]->movePoint( 0, p ); } } // topological editing offset = posMapCoord - mPosMapCoordBackup; for ( int i = 0; i < mTopologyRubberBand.size(); i++ ) { for ( int pointIndex = 0; pointIndex < mTopologyRubberBand[i]->numberOfVertices(); pointIndex++ ) { if ( mTopologyRubberBandVertexes[i]->contains( pointIndex ) ) { const QgsPoint* point = mTopologyRubberBand[i]->getPoint( 0, pointIndex ); if ( point == 0 ) { break; } mTopologyRubberBand[i]->movePoint( pointIndex, *point + offset ); } } } mPosMapCoordBackup = posMapCoord; } } else { if ( !mSelectionRectangle ) { mSelectionRectangle = true; mSelectionRubberBand = new QRubberBand( QRubberBand::Rectangle, mCanvas ); mRect = new QRect(); mRect->setTopLeft( mPressCoordinates ); } mRect->setBottomRight( e->pos() ); QRect normalizedRect = mRect->normalized(); mSelectionRubberBand->setGeometry( normalizedRect ); mSelectionRubberBand->show(); } }