void visitData( const IData& d ) override { QgsFeatureId id = d.getIdentifier(); QgsGeometry* geom = mLocator->mGeoms.value( id ); int vertexIndex, beforeVertex, afterVertex; double sqrDist; QgsPoint pt = geom->closestVertex( mSrcPoint, vertexIndex, beforeVertex, afterVertex, sqrDist ); QgsPointLocator::Match m( QgsPointLocator::Vertex, mLocator->mLayer, id, sqrt( sqrDist ), pt, vertexIndex ); // in range queries the filter may reject some matches if ( mFilter && !mFilter->acceptMatch( m ) ) return; if ( !mBest.isValid() || m.distance() < mBest.distance() ) mBest = m; }
QgsGeometry QgsInternalGeometryEngine::variableWidthBuffer( int segments, const std::function< std::unique_ptr< double[] >( const QgsLineString *line ) > &widthFunction ) const { if ( !mGeometry ) { return QgsGeometry(); } std::vector< std::unique_ptr<QgsLineString > > linesToProcess; const QgsMultiCurve *multiCurve = qgsgeometry_cast< const QgsMultiCurve * >( mGeometry ); if ( multiCurve ) { for ( int i = 0; i < multiCurve->partCount(); ++i ) { if ( static_cast< const QgsCurve * >( multiCurve->geometryN( i ) )->nCoordinates() == 0 ) continue; // skip 0 length lines linesToProcess.emplace_back( static_cast<QgsLineString *>( multiCurve->geometryN( i )->clone() ) ); } } const QgsCurve *curve = qgsgeometry_cast< const QgsCurve * >( mGeometry ); if ( curve ) { if ( curve->nCoordinates() > 0 ) linesToProcess.emplace_back( static_cast<QgsLineString *>( curve->segmentize() ) ); } if ( linesToProcess.empty() ) { QgsGeometry g; g.mLastError = QStringLiteral( "Input geometry was not a curve type geometry" ); return g; } QVector<QgsGeometry> bufferedLines; for ( std::unique_ptr< QgsLineString > &line : linesToProcess ) { QVector<QgsGeometry> parts; QgsPoint prevPoint; double prevRadius = 0; QgsGeometry prevCircle; std::unique_ptr< double[] > widths = widthFunction( line.get() ) ; for ( int i = 0; i < line->nCoordinates(); ++i ) { QgsPoint thisPoint = line->pointN( i ); QgsGeometry thisCircle; double thisRadius = widths[ i ] / 2.0; if ( thisRadius > 0 ) { QgsGeometry p = QgsGeometry( thisPoint.clone() ); QgsCircle circ( thisPoint, thisRadius ); thisCircle = QgsGeometry( circ.toPolygon( segments * 4 ) ); parts << thisCircle; } else { thisCircle = QgsGeometry( thisPoint.clone() ); } if ( i > 0 ) { if ( prevRadius > 0 || thisRadius > 0 ) { QVector< QgsPointXY > points = generateSegmentCurve( prevPoint, prevRadius, thisPoint, thisRadius ); if ( !points.empty() ) { // snap points to circle vertices int atVertex = 0; int beforeVertex = 0; int afterVertex = 0; double sqrDist = 0; double sqrDistPrev = 0; for ( int j = 0; j < points.count(); ++j ) { QgsPointXY pA = prevCircle.closestVertex( points.at( j ), atVertex, beforeVertex, afterVertex, sqrDistPrev ); QgsPointXY pB = thisCircle.closestVertex( points.at( j ), atVertex, beforeVertex, afterVertex, sqrDist ); points[j] = sqrDistPrev < sqrDist ? pA : pB; } // close ring points.append( points.at( 0 ) ); std::unique_ptr< QgsPolygon > poly = qgis::make_unique< QgsPolygon >(); poly->setExteriorRing( new QgsLineString( points ) ); if ( poly->area() > 0 ) parts << QgsGeometry( std::move( poly ) ); } } } prevPoint = thisPoint; prevRadius = thisRadius; prevCircle = thisCircle; } bufferedLines << QgsGeometry::unaryUnion( parts ); } return QgsGeometry::collectGeometry( bufferedLines ); }
void QgsMapToolNodeTool::createTopologyRubberBands( QgsVectorLayer* vlayer, const QList<QgsVertexEntry*> &vertexMap, int vertex ) { QMultiMap<double, QgsSnappingResult> currentResultList; QgsGeometry *geometry = mSelectedFeature->geometry(); // snap from current vertex currentResultList.clear(); vlayer->snapWithContext( vertexMap[vertex]->point(), ZERO_TOLERANCE, currentResultList, QgsSnapper::SnapToVertex ); QMultiMap<double, QgsSnappingResult>::iterator resultIt = currentResultList.begin(); for ( ; resultIt != currentResultList.end(); ++resultIt ) { // move all other if ( mSelectedFeature->featureId() != resultIt.value().snappedAtGeometry ) { if ( mTopologyMovingVertexes.contains( resultIt.value().snappedAtGeometry ) ) { if ( mTopologyMovingVertexes[resultIt.value().snappedAtGeometry]->contains( resultIt.value().snappedVertexNr ) ) { // skip vertex already exists in some rubberband continue; } } QgsRubberBand* trb = new QgsRubberBand( mCanvas, QGis::Line ); mTopologyRubberBand.append( trb ); int rbId = mTopologyRubberBand.size() - 1; trb->setWidth( 1 ); trb->setColor( Qt::red ); int tVertex = resultIt.value().snappedVertexNr; int tVertexBackup = -1, tVertexAfter = -1; int tVertexFirst = tVertex; // vertex number to check for cycling QgsFeature topolFeature; vlayer->getFeatures( QgsFeatureRequest().setFilterFid( resultIt.value().snappedAtGeometry ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( topolFeature ); QgsGeometry* topolGeometry = topolFeature.geometry(); while ( tVertex != -1 ) // looking for first vertex to rubberband { tVertexBackup = tVertex; topolGeometry->adjacentVertices( tVertex, tVertex, tVertexAfter ); if ( tVertex == -1 || tVertex == tVertexFirst ) break; // check if this is not first vertex of the feature or cycling error // if closest vertex is not from selected feature or is not selected end double dist; QgsPoint point = topolGeometry->vertexAt( tVertex ); int at, before, after; geometry->closestVertex( point, at, before, after, dist ); if ( dist > ZERO_TOLERANCE || !vertexMap[at]->isSelected() ) // problem with double precision { break; // found first vertex } } int movingPointIndex = 0; Vertexes* movingPoints = new Vertexes(); Vertexes* addedPoints = new Vertexes(); if ( mTopologyMovingVertexes.contains( resultIt.value().snappedAtGeometry ) ) { addedPoints = mTopologyMovingVertexes[ resultIt.value().snappedAtGeometry ]; } if ( tVertex == -1 ) // adding first point if needed { tVertex = tVertexBackup; } else { trb->addPoint( toMapCoordinates( vlayer, topolGeometry->vertexAt( tVertex ) ) ); if ( tVertex == tVertexFirst ) // cycle first vertex need to be added also { movingPoints->insert( movingPointIndex ); } movingPointIndex = 1; topolGeometry->adjacentVertices( tVertex, tVertexAfter, tVertex ); } while ( tVertex != -1 ) { // if closest vertex is not from selected feature or is not selected end double dist; QgsPoint point = topolGeometry->vertexAt( tVertex ); int at, before, after; geometry->closestVertex( point, at, before, after, dist ); // find first no matching vertex if ( dist > ZERO_TOLERANCE || !vertexMap[at]->isSelected() ) // problem with double precision { trb->addPoint( toMapCoordinates( vlayer, topolGeometry->vertexAt( tVertex ) ) ); break; // found first vertex } else // add moving point to rubberband { if ( addedPoints->contains( tVertex ) ) break; // just preventing to circle trb->addPoint( toMapCoordinates( vlayer, topolGeometry->vertexAt( tVertex ) ) ); movingPoints->insert( movingPointIndex ); movingPointIndex++; addedPoints->insert( tVertex ); } topolGeometry->adjacentVertices( tVertex, tVertexAfter, tVertex ); } mTopologyMovingVertexes.insert( resultIt.value().snappedAtGeometry, addedPoints ); mTopologyRubberBandVertexes.insert( rbId, movingPoints ); } } }
int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPointXY &p ) { if ( !mLayer->isSpatial() ) return 1; double segmentSearchEpsilon = mLayer->crs().isGeographic() ? 1e-12 : 1e-8; //work with a tolerance because coordinate projection may introduce some rounding double threshold = 0.0000001; if ( mLayer->crs().mapUnits() == QgsUnitTypes::DistanceMeters ) { threshold = 0.001; } else if ( mLayer->crs().mapUnits() == QgsUnitTypes::DistanceFeet ) { threshold = 0.0001; } QgsRectangle searchRect( p.x() - threshold, p.y() - threshold, p.x() + threshold, p.y() + threshold ); double sqrSnappingTolerance = threshold * threshold; QgsFeature f; QgsFeatureIterator fit = mLayer->getFeatures( QgsFeatureRequest() .setFilterRect( searchRect ) .setFlags( QgsFeatureRequest::ExactIntersect ) .setSubsetOfAttributes( QgsAttributeList() ) ); QMap<QgsFeatureId, QgsGeometry> features; QMap<QgsFeatureId, int> segments; while ( fit.nextFeature( f ) ) { int afterVertex; QgsPointXY snappedPoint; double sqrDistSegmentSnap = f.geometry().closestSegmentWithContext( p, snappedPoint, afterVertex, nullptr, segmentSearchEpsilon ); if ( sqrDistSegmentSnap < sqrSnappingTolerance ) { segments[f.id()] = afterVertex; features[f.id()] = f.geometry(); } } if ( segments.isEmpty() ) return 2; for ( QMap<QgsFeatureId, int>::const_iterator it = segments.constBegin(); it != segments.constEnd(); ++it ) { QgsFeatureId fid = it.key(); int segmentAfterVertex = it.value(); QgsGeometry geom = features[fid]; int atVertex, beforeVertex, afterVertex; double sqrDistVertexSnap; geom.closestVertex( p, atVertex, beforeVertex, afterVertex, sqrDistVertexSnap ); if ( sqrDistVertexSnap < sqrSnappingTolerance ) continue; // the vertex already exists - do not insert it if ( !mLayer->insertVertex( p.x(), p.y(), fid, segmentAfterVertex ) ) { QgsDebugMsg( "failed to insert topo point" ); } } return 0; }