void QgsPointSample::addSamplePoints( QgsFeature& inputFeature, QgsVectorFileWriter& writer, int nPoints, double minDistance ) { if ( !inputFeature.constGeometry() ) return; const QgsGeometry* geom = inputFeature.constGeometry(); QgsRectangle geomRect = geom->boundingBox(); if ( geomRect.isEmpty() ) { return; } QgsSpatialIndex sIndex; //to check minimum distance QMap< QgsFeatureId, QgsPoint > pointMapForFeature; int nIterations = 0; int maxIterations = nPoints * 200; int points = 0; double randX = 0; double randY = 0; while ( nIterations < maxIterations && points < nPoints ) { randX = (( double )mt_rand() / MD_RAND_MAX ) * geomRect.width() + geomRect.xMinimum(); randY = (( double )mt_rand() / MD_RAND_MAX ) * geomRect.height() + geomRect.yMinimum(); QgsPoint randPoint( randX, randY ); QgsGeometry* ptGeom = QgsGeometry::fromPoint( randPoint ); if ( ptGeom->within( geom ) && checkMinDistance( randPoint, sIndex, minDistance, pointMapForFeature ) ) { //add feature to writer QgsFeature f( mNCreatedPoints ); f.setAttribute( "id", mNCreatedPoints + 1 ); f.setAttribute( "station_id", points + 1 ); f.setAttribute( "stratum_id", inputFeature.id() ); f.setGeometry( ptGeom ); writer.addFeature( f ); sIndex.insertFeature( f ); pointMapForFeature.insert( mNCreatedPoints, randPoint ); ++points; ++mNCreatedPoints; } else { delete ptGeom; } ++nIterations; } }
ErrorList topolTest::checkPolygonContainsPoint( double tolerance, QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( tolerance ); Q_UNUSED( isExtent ); int i = 0; ErrorList errorList; if ( layer1->geometryType() != QgsWkbTypes::PolygonGeometry ) { return errorList; } if ( layer2->geometryType() != QgsWkbTypes::PointGeometry ) { return errorList; } QgsSpatialIndex *index = mLayerIndexes[layer2->id()]; QList<FeatureLayer>::Iterator it; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); if ( testCanceled() ) break; QgsGeometry g1 = it->feature.geometry(); QgsRectangle bb = g1.boundingBox(); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( bb ); QList<QgsFeatureId>::ConstIterator cit = crossingIds.begin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.constEnd(); bool touched = false; for ( ; cit != crossingIdsEnd; ++cit ) { QgsFeature &f = mFeatureMap2[*cit].feature; QgsGeometry g2 = f.geometry(); if ( g2.isNull() || !_canExportToGeos( g2 ) ) { QgsMessageLog::logMessage( tr( "Second geometry missing or GEOS import failed." ), tr( "Topology plugin" ) ); continue; } if ( g1.contains( g2 ) ) { touched = true; break; } } if ( !touched ) { QList<FeatureLayer> fls; fls << *it << *it; //bb.scale(10); TopolErrorPolygonContainsPoint *err = new TopolErrorPolygonContainsPoint( bb, g1, fls ); errorList << err; } } return errorList; }
bool QgsPointSample::checkMinDistance( QgsPoint& pt, QgsSpatialIndex& index, double minDistance, QMap< QgsFeatureId, QgsPoint >& pointMap ) { if ( minDistance <= 0 ) { return true; } QList<QgsFeatureId> neighborList = index.nearestNeighbor( pt, 1 ); if ( neighborList.isEmpty() ) { return true; } QMap< QgsFeatureId, QgsPoint >::const_iterator it = pointMap.find( neighborList[0] ); if ( it == pointMap.constEnd() ) //should not happen { return true; } QgsPoint neighborPt = it.value(); if ( neighborPt.sqrDist( pt ) < ( minDistance * minDistance ) ) { return false; } return true; }
QgsSpatialIndex *topolTest::createIndex( QgsVectorLayer *layer, const QgsRectangle &extent ) { QgsSpatialIndex *index = new QgsSpatialIndex(); QgsFeatureIterator fit; if ( extent.isEmpty() ) { fit = layer->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ) ); } else { fit = layer->getFeatures( QgsFeatureRequest() .setFilterRect( extent ) .setFlags( QgsFeatureRequest::ExactIntersect ) .setSubsetOfAttributes( QgsAttributeList() ) ); } int i = 0; QgsFeature f; while ( fit.nextFeature( f ) ) { if ( !( ++i % 100 ) ) emit progress( i ); if ( testCanceled() ) { delete index; return nullptr; } if ( f.hasGeometry() ) { index->addFeature( f ); mFeatureMap2[f.id()] = FeatureLayer( layer, f ); } } return index; }
static void buildSnapIndex( QgsFeatureIterator &fi, QgsSpatialIndex &index, QVector<AnchorPoint> &pnts, QgsFeedback *feedback, int &count, int totalCount ) { QgsFeature f; int pntId = 0; while ( fi.nextFeature( f ) ) { if ( feedback->isCanceled() ) break; QgsGeometry g = f.geometry(); for ( auto it = g.vertices_begin(); it != g.vertices_end(); ++it ) { QgsPoint pt = *it; QgsRectangle rect( pt.x(), pt.y(), pt.x(), pt.y() ); QList<QgsFeatureId> ids = index.intersects( rect ); if ( ids.isEmpty() ) { // add to tree and to structure index.insertFeature( pntId, pt.boundingBox() ); AnchorPoint xp; xp.x = pt.x(); xp.y = pt.y(); xp.anchor = -1; pnts.append( xp ); pntId++; } } ++count; feedback->setProgress( 100. * count / totalCount ); } }
static void assignAnchors( QgsSpatialIndex &index, QVector<AnchorPoint> &pnts, double thresh ) { double thresh2 = thresh * thresh; int nanchors = 0, ntosnap = 0; for ( int point = 0; point < pnts.count(); ++point ) { if ( pnts[point].anchor >= 0 ) continue; pnts[point].anchor = -2; // make it anchor nanchors++; // Find points in threshold double x = pnts[point].x, y = pnts[point].y; QgsRectangle rect( x - thresh, y - thresh, x + thresh, y + thresh ); const QList<QgsFeatureId> ids = index.intersects( rect ); for ( QgsFeatureId pointb : ids ) { if ( pointb == point ) continue; double dx = pnts[pointb].x - pnts[point].x; double dy = pnts[pointb].y - pnts[point].y; double dist2 = dx * dx + dy * dy; if ( dist2 > thresh2 ) continue; // outside threshold if ( pnts[pointb].anchor == -1 ) { // doesn't have an anchor yet pnts[pointb].anchor = point; ntosnap++; } else if ( pnts[pointb].anchor >= 0 ) { // check distance to previously assigned anchor double dx2 = pnts[pnts[pointb].anchor].x - pnts[pointb].x; double dy2 = pnts[pnts[pointb].anchor].y - pnts[pointb].y; double dist2_a = dx2 * dx2 + dy2 * dy2; if ( dist2 < dist2_a ) pnts[pointb].anchor = point; // replace old anchor } } } }
static bool snapPoint( QgsPoint *pt, QgsSpatialIndex &index, QVector<AnchorPoint> &pnts ) { // Find point ( should always find one point ) QList<QgsFeatureId> fids = index.intersects( QgsRectangle( pt->x(), pt->y(), pt->x(), pt->y() ) ); Q_ASSERT( fids.count() == 1 ); int spoint = fids[0]; int anchor = pnts[spoint].anchor; if ( anchor >= 0 ) { // to be snapped pt->setX( pnts[anchor].x ); pt->setY( pnts[anchor].y ); return true; } return false; }
bool QgsTransectSample::otherTransectWithinDistance( QgsGeometry* geom, double minDistLayerUnit, double minDistance, QgsSpatialIndex& sIndex, const QMap< QgsFeatureId, QgsGeometry* >& lineFeatureMap, QgsDistanceArea& da ) { if ( !geom ) { return false; } QgsGeometry* buffer = geom->buffer( minDistLayerUnit, 8 ); if ( !buffer ) { return false; } QgsRectangle rect = buffer->boundingBox(); QList<QgsFeatureId> lineIdList = sIndex.intersects( rect ); QList<QgsFeatureId>::const_iterator lineIdIt = lineIdList.constBegin(); for ( ; lineIdIt != lineIdList.constEnd(); ++lineIdIt ) { const QMap< QgsFeatureId, QgsGeometry* >::const_iterator idMapIt = lineFeatureMap.find( *lineIdIt ); if ( idMapIt != lineFeatureMap.constEnd() ) { double dist = 0; QgsPoint pt1, pt2; closestSegmentPoints( *geom, *( idMapIt.value() ), dist, pt1, pt2 ); dist = da.measureLine( pt1, pt2 ); //convert degrees to meters if necessary if ( dist < minDistance ) { delete buffer; return true; } } } delete buffer; return false; }
QVariantMap QgsSplitWithLinesAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); if ( !source ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); std::unique_ptr< QgsFeatureSource > linesSource( parameterAsSource( parameters, QStringLiteral( "LINES" ), context ) ); if ( !linesSource ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "LINES" ) ) ); bool sameLayer = parameters.value( QStringLiteral( "INPUT" ) ) == parameters.value( QStringLiteral( "LINES" ) ); QString dest; std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(), QgsWkbTypes::multiType( source->wkbType() ), source->sourceCrs() ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); QgsSpatialIndex spatialIndex; QMap< QgsFeatureId, QgsGeometry > splitGeoms; QgsFeatureRequest request; request.setSubsetOfAttributes( QgsAttributeList() ); request.setDestinationCrs( source->sourceCrs(), context.transformContext() ); QgsFeatureIterator splitLines = linesSource->getFeatures( request ); QgsFeature aSplitFeature; while ( splitLines.nextFeature( aSplitFeature ) ) { if ( feedback->isCanceled() ) { break; } splitGeoms.insert( aSplitFeature.id(), aSplitFeature.geometry() ); spatialIndex.addFeature( aSplitFeature ); } QgsFeature outFeat; QgsFeatureIterator features = source->getFeatures(); double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1; int i = 0; QgsFeature inFeatureA; while ( features.nextFeature( inFeatureA ) ) { i++; if ( feedback->isCanceled() ) { break; } if ( !inFeatureA.hasGeometry() ) { sink->addFeature( inFeatureA, QgsFeatureSink::FastInsert ); continue; } QgsGeometry inGeom = inFeatureA.geometry(); outFeat.setAttributes( inFeatureA.attributes() ); QVector< QgsGeometry > inGeoms = inGeom.asGeometryCollection(); const QgsFeatureIds lines = spatialIndex.intersects( inGeom.boundingBox() ).toSet(); if ( !lines.empty() ) // has intersection of bounding boxes { QVector< QgsGeometry > splittingLines; // use prepared geometries for faster intersection tests std::unique_ptr< QgsGeometryEngine > engine; for ( QgsFeatureId line : lines ) { // check if trying to self-intersect if ( sameLayer && inFeatureA.id() == line ) continue; QgsGeometry splitGeom = splitGeoms.value( line ); if ( !engine ) { engine.reset( QgsGeometry::createGeometryEngine( inGeom.constGet() ) ); engine->prepareGeometry(); } if ( engine->intersects( splitGeom.constGet() ) ) { QVector< QgsGeometry > splitGeomParts = splitGeom.asGeometryCollection(); splittingLines.append( splitGeomParts ); } } if ( !splittingLines.empty() ) { for ( const QgsGeometry &splitGeom : qgis::as_const( splittingLines ) ) { QVector<QgsPointXY> splitterPList; QVector< QgsGeometry > outGeoms; // use prepared geometries for faster intersection tests std::unique_ptr< QgsGeometryEngine > splitGeomEngine( QgsGeometry::createGeometryEngine( splitGeom.constGet() ) ); splitGeomEngine->prepareGeometry(); while ( !inGeoms.empty() ) { if ( feedback->isCanceled() ) { break; } QgsGeometry inGeom = inGeoms.takeFirst(); if ( !inGeom ) continue; if ( splitGeomEngine->intersects( inGeom.constGet() ) ) { QgsGeometry before = inGeom; if ( splitterPList.empty() ) { const QgsCoordinateSequence sequence = splitGeom.constGet()->coordinateSequence(); for ( const QgsRingSequence &part : sequence ) { for ( const QgsPointSequence &ring : part ) { for ( const QgsPoint &pt : ring ) { splitterPList << QgsPointXY( pt ); } } } } QVector< QgsGeometry > newGeometries; QVector<QgsPointXY> topologyTestPoints; QgsGeometry::OperationResult result = inGeom.splitGeometry( splitterPList, newGeometries, false, topologyTestPoints ); // splitGeometry: If there are several intersections // between geometry and splitLine, only the first one is considered. if ( result == QgsGeometry::Success ) // split occurred { if ( inGeom.isGeosEqual( before ) ) { // bug in splitGeometry: sometimes it returns 0 but // the geometry is unchanged outGeoms.append( inGeom ); } else { inGeoms.append( inGeom ); inGeoms.append( newGeometries ); } } else { outGeoms.append( inGeom ); } } else { outGeoms.append( inGeom ); } } inGeoms = outGeoms; } } } QVector< QgsGeometry > parts; for ( const QgsGeometry &aGeom : qgis::as_const( inGeoms ) ) { if ( feedback->isCanceled() ) { break; } bool passed = true; if ( QgsWkbTypes::geometryType( aGeom.wkbType() ) == QgsWkbTypes::LineGeometry ) { int numPoints = aGeom.constGet()->nCoordinates(); if ( numPoints <= 2 ) { if ( numPoints == 2 ) passed = !static_cast< const QgsCurve * >( aGeom.constGet() )->isClosed(); // tests if vertex 0 = vertex 1 else passed = false; // sometimes splitting results in lines of zero length } } if ( passed ) parts.append( aGeom ); } for ( const QgsGeometry &g : parts ) { outFeat.setGeometry( g ); sink->addFeature( outFeat, QgsFeatureSink::FastInsert ); } feedback->setProgress( i * step ); } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); return outputs; }
void QgsOverlayUtils::resolveOverlaps( const QgsFeatureSource &source, QgsFeatureSink &sink, QgsProcessingFeedback *feedback ) { int count = 0; int totalCount = source.featureCount(); if ( totalCount == 0 ) return; // nothing to do here QgsFeatureId newFid = -1; QgsWkbTypes::GeometryType geometryType = QgsWkbTypes::geometryType( QgsWkbTypes::multiType( source.wkbType() ) ); QgsFeatureRequest requestOnlyGeoms; requestOnlyGeoms.setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureRequest requestOnlyAttrs; requestOnlyAttrs.setFlags( QgsFeatureRequest::NoGeometry ); QgsFeatureRequest requestOnlyIds; requestOnlyIds.setFlags( QgsFeatureRequest::NoGeometry ); requestOnlyIds.setSubsetOfAttributes( QgsAttributeList() ); // make a set of used feature IDs so that we do not try to reuse them for newly added features QgsFeature f; QSet<QgsFeatureId> fids; QgsFeatureIterator it = source.getFeatures( requestOnlyIds ); while ( it.nextFeature( f ) ) { if ( feedback->isCanceled() ) return; fids.insert( f.id() ); } QHash<QgsFeatureId, QgsGeometry> geometries; QgsSpatialIndex index; QHash<QgsFeatureId, QList<QgsFeatureId> > intersectingIds; // which features overlap a particular area // resolve intersections it = source.getFeatures( requestOnlyGeoms ); while ( it.nextFeature( f ) ) { if ( feedback->isCanceled() ) return; QgsFeatureId fid1 = f.id(); QgsGeometry g1 = f.geometry(); std::unique_ptr< QgsGeometryEngine > g1engine; geometries.insert( fid1, g1 ); index.insertFeature( f ); QgsRectangle bbox( f.geometry().boundingBox() ); const QList<QgsFeatureId> ids = index.intersects( bbox ); for ( QgsFeatureId fid2 : ids ) { if ( fid1 == fid2 ) continue; if ( !g1engine ) { // use prepared geometries for faster intersection tests g1engine.reset( QgsGeometry::createGeometryEngine( g1.constGet() ) ); g1engine->prepareGeometry(); } QgsGeometry g2 = geometries.value( fid2 ); if ( !g1engine->intersects( g2.constGet() ) ) continue; QgsGeometry geomIntersection = g1.intersection( g2 ); if ( !sanitizeIntersectionResult( geomIntersection, geometryType ) ) continue; // // add intersection geometry // // figure out new fid while ( fids.contains( newFid ) ) --newFid; fids.insert( newFid ); geometries.insert( newFid, geomIntersection ); QgsFeature fx( newFid ); fx.setGeometry( geomIntersection ); index.insertFeature( fx ); // figure out which feature IDs belong to this intersection. Some of the IDs can be of the newly // created geometries - in such case we need to retrieve original IDs QList<QgsFeatureId> lst; if ( intersectingIds.contains( fid1 ) ) lst << intersectingIds.value( fid1 ); else lst << fid1; if ( intersectingIds.contains( fid2 ) ) lst << intersectingIds.value( fid2 ); else lst << fid2; intersectingIds.insert( newFid, lst ); // // update f1 // QgsGeometry g12 = g1.difference( g2 ); index.deleteFeature( f ); geometries.remove( fid1 ); if ( sanitizeDifferenceResult( g12 ) ) { geometries.insert( fid1, g12 ); QgsFeature f1x( fid1 ); f1x.setGeometry( g12 ); index.insertFeature( f1x ); } // // update f2 // QgsGeometry g21 = g2.difference( g1 ); QgsFeature f2old( fid2 ); f2old.setGeometry( g2 ); index.deleteFeature( f2old ); geometries.remove( fid2 ); if ( sanitizeDifferenceResult( g21 ) ) { geometries.insert( fid2, g21 ); QgsFeature f2x( fid2 ); f2x.setGeometry( g21 ); index.insertFeature( f2x ); } // update our temporary copy of the geometry to what is left from it g1 = g12; g1engine.reset(); } ++count; feedback->setProgress( count / ( double ) totalCount * 100. ); } // release some memory of structures we don't need anymore fids.clear(); index = QgsSpatialIndex(); // load attributes QHash<QgsFeatureId, QgsAttributes> attributesHash; it = source.getFeatures( requestOnlyAttrs ); while ( it.nextFeature( f ) ) { if ( feedback->isCanceled() ) return; attributesHash.insert( f.id(), f.attributes() ); } // store stuff in the sink for ( auto i = geometries.constBegin(); i != geometries.constEnd(); ++i ) { if ( feedback->isCanceled() ) return; QgsFeature outFeature( i.key() ); outFeature.setGeometry( i.value() ); if ( intersectingIds.contains( i.key() ) ) { const QList<QgsFeatureId> ids = intersectingIds.value( i.key() ); for ( QgsFeatureId id : ids ) { outFeature.setAttributes( attributesHash.value( id ) ); sink.addFeature( outFeature, QgsFeatureSink::FastInsert ); } } else { outFeature.setAttributes( attributesHash.value( i.key() ) ); sink.addFeature( outFeature, QgsFeatureSink::FastInsert ); } } }
int QgsTransectSample::createSample( QProgressDialog* pd ) { Q_UNUSED( pd ); if ( !mStrataLayer || !mStrataLayer->isValid() ) { return 1; } if ( !mBaselineLayer || !mBaselineLayer->isValid() ) { return 2; } //stratum id is not necessarily an integer QVariant::Type stratumIdType = QVariant::Int; if ( !mStrataIdAttribute.isEmpty() ) { stratumIdType = mStrataLayer->pendingFields().field( mStrataIdAttribute ).type(); } //create vector file writers for output QgsFields outputPointFields; outputPointFields.append( QgsField( "id", stratumIdType ) ); outputPointFields.append( QgsField( "station_id", QVariant::Int ) ); outputPointFields.append( QgsField( "stratum_id", stratumIdType ) ); outputPointFields.append( QgsField( "station_code", QVariant::String ) ); outputPointFields.append( QgsField( "start_lat", QVariant::Double ) ); outputPointFields.append( QgsField( "start_long", QVariant::Double ) ); QgsVectorFileWriter outputPointWriter( mOutputPointLayer, "utf-8", outputPointFields, QGis::WKBPoint, &( mStrataLayer->crs() ) ); if ( outputPointWriter.hasError() != QgsVectorFileWriter::NoError ) { return 3; } outputPointFields.append( QgsField( "bearing", QVariant::Double ) ); //add bearing attribute for lines QgsVectorFileWriter outputLineWriter( mOutputLineLayer, "utf-8", outputPointFields, QGis::WKBLineString, &( mStrataLayer->crs() ) ); if ( outputLineWriter.hasError() != QgsVectorFileWriter::NoError ) { return 4; } QgsFields usedBaselineFields; usedBaselineFields.append( QgsField( "stratum_id", stratumIdType ) ); usedBaselineFields.append( QgsField( "ok", QVariant::String ) ); QgsVectorFileWriter usedBaselineWriter( mUsedBaselineLayer, "utf-8", usedBaselineFields, QGis::WKBLineString, &( mStrataLayer->crs() ) ); if ( usedBaselineWriter.hasError() != QgsVectorFileWriter::NoError ) { return 5; } //debug: write clipped buffer bounds with stratum id to same directory as out_point QFileInfo outputPointInfo( mOutputPointLayer ); QString bufferClipLineOutput = outputPointInfo.absolutePath() + "/out_buffer_clip_line.shp"; QgsFields bufferClipLineFields; bufferClipLineFields.append( QgsField( "id", stratumIdType ) ); QgsVectorFileWriter bufferClipLineWriter( bufferClipLineOutput, "utf-8", bufferClipLineFields, QGis::WKBLineString, &( mStrataLayer->crs() ) ); //configure distanceArea depending on minDistance units and output CRS QgsDistanceArea distanceArea; distanceArea.setSourceCrs( mStrataLayer->crs().srsid() ); if ( mMinDistanceUnits == Meters ) { distanceArea.setEllipsoidalMode( true ); } else { distanceArea.setEllipsoidalMode( false ); } //possibility to transform output points to lat/long QgsCoordinateTransform toLatLongTransform( mStrataLayer->crs(), QgsCoordinateReferenceSystem( 4326, QgsCoordinateReferenceSystem::EpsgCrsId ) ); //init random number generator mt_srand( QTime::currentTime().msec() ); QgsFeatureRequest fr; fr.setSubsetOfAttributes( QStringList() << mStrataIdAttribute << mMinDistanceAttribute << mNPointsAttribute, mStrataLayer->pendingFields() ); QgsFeatureIterator strataIt = mStrataLayer->getFeatures( fr ); QgsFeature fet; int nTotalTransects = 0; int nFeatures = 0; if ( pd ) { pd->setMaximum( mStrataLayer->featureCount() ); } while ( strataIt.nextFeature( fet ) ) { if ( pd ) { pd->setValue( nFeatures ); } if ( pd && pd->wasCanceled() ) { break; } if ( !fet.constGeometry() ) { continue; } const QgsGeometry* strataGeom = fet.constGeometry(); //find baseline for strata QVariant strataId = fet.attribute( mStrataIdAttribute ); QgsGeometry* baselineGeom = findBaselineGeometry( strataId.isValid() ? strataId : -1 ); if ( !baselineGeom ) { continue; } double minDistance = fet.attribute( mMinDistanceAttribute ).toDouble(); double minDistanceLayerUnits = minDistance; //if minDistance is in meters and the data in degrees, we need to apply a rough conversion for the buffer distance double bufferDist = bufferDistance( minDistance ); if ( mMinDistanceUnits == Meters && mStrataLayer->crs().mapUnits() == QGis::DecimalDegrees ) { minDistanceLayerUnits = minDistance / 111319.9; } QgsGeometry* clippedBaseline = strataGeom->intersection( baselineGeom ); if ( !clippedBaseline || clippedBaseline->wkbType() == QGis::WKBUnknown ) { delete clippedBaseline; continue; } QgsGeometry* bufferLineClipped = clipBufferLine( strataGeom, clippedBaseline, bufferDist ); if ( !bufferLineClipped ) { delete clippedBaseline; continue; } //save clipped baseline to file QgsFeature blFeature; blFeature.setGeometry( *clippedBaseline ); blFeature.setAttribute( "stratum_id", strataId ); blFeature.setAttribute( "ok", "f" ); usedBaselineWriter.addFeature( blFeature ); //start loop to create random points along the baseline int nTransects = fet.attribute( mNPointsAttribute ).toInt(); int nCreatedTransects = 0; int nIterations = 0; int nMaxIterations = nTransects * 50; QgsSpatialIndex sIndex; //to check minimum distance QMap< QgsFeatureId, QgsGeometry* > lineFeatureMap; while ( nCreatedTransects < nTransects && nIterations < nMaxIterations ) { double randomPosition = (( double )mt_rand() / MD_RAND_MAX ) * clippedBaseline->length(); QgsGeometry* samplePoint = clippedBaseline->interpolate( randomPosition ); ++nIterations; if ( !samplePoint ) { continue; } QgsPoint sampleQgsPoint = samplePoint->asPoint(); QgsPoint latLongSamplePoint = toLatLongTransform.transform( sampleQgsPoint ); QgsFeature samplePointFeature; samplePointFeature.setGeometry( samplePoint ); samplePointFeature.setAttribute( "id", nTotalTransects + 1 ); samplePointFeature.setAttribute( "station_id", nCreatedTransects + 1 ); samplePointFeature.setAttribute( "stratum_id", strataId ); samplePointFeature.setAttribute( "station_code", strataId.toString() + "_" + QString::number( nCreatedTransects + 1 ) ); samplePointFeature.setAttribute( "start_lat", latLongSamplePoint.y() ); samplePointFeature.setAttribute( "start_long", latLongSamplePoint.x() ); //find closest point on clipped buffer line QgsPoint minDistPoint; int afterVertex; if ( bufferLineClipped->closestSegmentWithContext( sampleQgsPoint, minDistPoint, afterVertex ) < 0 ) { continue; } //bearing between sample point and min dist point (transect direction) double bearing = distanceArea.bearing( sampleQgsPoint, minDistPoint ) / M_PI * 180.0; QgsPolyline sampleLinePolyline; QgsPoint ptFarAway( sampleQgsPoint.x() + ( minDistPoint.x() - sampleQgsPoint.x() ) * 1000000, sampleQgsPoint.y() + ( minDistPoint.y() - sampleQgsPoint.y() ) * 1000000 ); QgsPolyline lineFarAway; lineFarAway << sampleQgsPoint << ptFarAway; QgsGeometry* lineFarAwayGeom = QgsGeometry::fromPolyline( lineFarAway ); QgsGeometry* lineClipStratum = lineFarAwayGeom->intersection( strataGeom ); if ( !lineClipStratum ) { delete lineFarAwayGeom; delete lineClipStratum; continue; } //cancel if distance between sample point and line is too large (line does not start at point if ( lineClipStratum->distance( *samplePoint ) > 0.000001 ) { delete lineFarAwayGeom; delete lineClipStratum; continue; } //if lineClipStratum is a multiline, take the part line closest to sampleQgsPoint if ( lineClipStratum->wkbType() == QGis::WKBMultiLineString || lineClipStratum->wkbType() == QGis::WKBMultiLineString25D ) { QgsGeometry* singleLine = closestMultilineElement( sampleQgsPoint, lineClipStratum ); if ( singleLine ) { delete lineClipStratum; lineClipStratum = singleLine; } } //cancel if length of lineClipStratum is too small double transectLength = distanceArea.measure( lineClipStratum ); if ( transectLength < mMinTransectLength ) { delete lineFarAwayGeom; delete lineClipStratum; continue; } //search closest existing profile. Cancel if dist < minDist if ( otherTransectWithinDistance( lineClipStratum, minDistanceLayerUnits, minDistance, sIndex, lineFeatureMap, distanceArea ) ) { delete lineFarAwayGeom; delete lineClipStratum; continue; } QgsFeatureId fid( nCreatedTransects ); QgsFeature sampleLineFeature( fid ); sampleLineFeature.setGeometry( lineClipStratum ); sampleLineFeature.setAttribute( "id", nTotalTransects + 1 ); sampleLineFeature.setAttribute( "station_id", nCreatedTransects + 1 ); sampleLineFeature.setAttribute( "stratum_id", strataId ); sampleLineFeature.setAttribute( "station_code", strataId.toString() + "_" + QString::number( nCreatedTransects + 1 ) ); sampleLineFeature.setAttribute( "start_lat", latLongSamplePoint.y() ); sampleLineFeature.setAttribute( "start_long", latLongSamplePoint.x() ); sampleLineFeature.setAttribute( "bearing", bearing ); outputLineWriter.addFeature( sampleLineFeature ); //add point to file writer here. //It can only be written if the corresponding transect has been as well outputPointWriter.addFeature( samplePointFeature ); sIndex.insertFeature( sampleLineFeature ); Q_NOWARN_DEPRECATED_PUSH lineFeatureMap.insert( fid, sampleLineFeature.geometryAndOwnership() ); Q_NOWARN_DEPRECATED_POP delete lineFarAwayGeom; ++nTotalTransects; ++nCreatedTransects; } delete clippedBaseline; QgsFeature bufferClipFeature; bufferClipFeature.setGeometry( bufferLineClipped ); bufferClipFeature.setAttribute( "id", strataId ); bufferClipLineWriter.addFeature( bufferClipFeature ); //delete bufferLineClipped; //delete all line geometries in spatial index QMap< QgsFeatureId, QgsGeometry* >::iterator featureMapIt = lineFeatureMap.begin(); for ( ; featureMapIt != lineFeatureMap.end(); ++featureMapIt ) { delete( featureMapIt.value() ); } lineFeatureMap.clear(); delete baselineGeom; ++nFeatures; } if ( pd ) { pd->setValue( mStrataLayer->featureCount() ); } return 0; }
ErrorList topolTest::checkPointCoveredByLineEnds( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { int i = 0; ErrorList errorList; if ( layer1->geometryType() != QgsWkbTypes::PointGeometry ) { return errorList; } if ( layer2->geometryType() != QgsWkbTypes::LineGeometry ) { return errorList; } QgsSpatialIndex *index = mLayerIndexes[layer2->id()]; QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() ); QList<FeatureLayer>::Iterator it; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); if ( testCanceled() ) break; QgsGeometry g1 = it->feature.geometry(); QgsRectangle bb = g1.boundingBox(); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( bb ); QList<QgsFeatureId>::ConstIterator cit = crossingIds.constBegin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.constEnd(); bool touched = false; for ( ; cit != crossingIdsEnd; ++cit ) { QgsFeature &f = mFeatureMap2[*cit].feature; QgsGeometry g2 = f.geometry(); if ( g2.isNull() || !_canExportToGeos( g2 ) ) { QgsMessageLog::logMessage( tr( "Second geometry missing or GEOS import failed." ), tr( "Topology plugin" ) ); continue; } QgsPolylineXY g2Line = g2.asPolyline(); QgsGeometry startPoint = QgsGeometry::fromPointXY( g2Line.at( 0 ) ); QgsGeometry endPoint = QgsGeometry::fromPointXY( g2Line.last() ); touched = g1.intersects( startPoint ) || g1.intersects( endPoint ); if ( touched ) { break; } } if ( !touched ) { QgsGeometry conflictGeom = g1; if ( isExtent ) { if ( canvasExtentPoly.disjoint( conflictGeom ) ) { continue; } } QList<FeatureLayer> fls; fls << *it << *it; //bb.scale(10); TopolErrorPointNotCoveredByLineEnds *err = new TopolErrorPointNotCoveredByLineEnds( bb, conflictGeom, fls ); errorList << err; } } return errorList; }
ErrorList topolTest::checkOverlapWithLayer( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { int i = 0; ErrorList errorList; bool skipItself = layer1 == layer2; QgsSpatialIndex *index = mLayerIndexes[layer2->id()]; QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() ); QList<FeatureLayer>::iterator it; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); if ( testCanceled() ) break; QgsGeometry g1 = it->feature.geometry(); QgsRectangle bb = g1.boundingBox(); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( bb ); QList<QgsFeatureId>::ConstIterator cit = crossingIds.begin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end(); for ( ; cit != crossingIdsEnd; ++cit ) { QgsFeature &f = mFeatureMap2[*cit].feature; QgsGeometry g2 = f.geometry(); // skip itself, when invoked with the same layer if ( skipItself && f.id() == it->feature.id() ) continue; if ( g2.isNull() ) { QgsMessageLog::logMessage( tr( "Second geometry missing." ), tr( "Topology plugin" ) ); continue; } if ( g1.overlaps( g2 ) ) { QgsRectangle r = bb; QgsRectangle r2 = g2.boundingBox(); r.combineExtentWith( r2 ); QgsGeometry conflictGeom = g1.intersection( g2 ); // could this for some reason return NULL? if ( conflictGeom.isNull() ) { continue; } if ( isExtent ) { if ( canvasExtentPoly.disjoint( conflictGeom ) ) { continue; } if ( canvasExtentPoly.crosses( conflictGeom ) ) { conflictGeom = conflictGeom.intersection( canvasExtentPoly ); } } //c = new QgsGeometry; QList<FeatureLayer> fls; FeatureLayer fl; fl.feature = f; fl.layer = layer2; fls << *it << fl; TopolErrorIntersection *err = new TopolErrorIntersection( r, conflictGeom, fls ); errorList << err; } } } return errorList; }
ErrorList topolTest::checkPointCoveredBySegment( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { int i = 0; ErrorList errorList; if ( layer1->geometryType() != QgsWkbTypes::PointGeometry ) { return errorList; } if ( layer2->geometryType() == QgsWkbTypes::PointGeometry ) { return errorList; } QgsSpatialIndex *index = mLayerIndexes[layer2->id()]; QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() ); QList<FeatureLayer>::Iterator it; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); if ( testCanceled() ) break; QgsGeometry g1 = it->feature.geometry(); QgsRectangle bb = g1.boundingBox(); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( bb ); QList<QgsFeatureId>::ConstIterator cit = crossingIds.begin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end(); bool touched = false; for ( ; cit != crossingIdsEnd; ++cit ) { QgsFeature &f = mFeatureMap2[*cit].feature; QgsGeometry g2 = f.geometry(); if ( g2.isNull() ) { QgsMessageLog::logMessage( tr( "Invalid geometry in covering test." ), tr( "Topology plugin" ) ); continue; } // test if point touches other geometry if ( g1.touches( g2 ) ) { touched = true; break; } } if ( !touched ) { QgsGeometry conflictGeom = QgsGeometry( g1 ); if ( isExtent ) { if ( canvasExtentPoly.disjoint( conflictGeom ) ) { continue; } } QList<FeatureLayer> fls; fls << *it << *it; //bb.scale(10); TopolErrorCovered *err = new TopolErrorCovered( bb, conflictGeom, fls ); errorList << err; } } return errorList; }
ErrorList topolTest::checkOverlaps( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( layer2 ); int i = 0; ErrorList errorList; // could be enabled for lines and points too // so duplicate rule may be removed? if ( layer1->geometryType() != QgsWkbTypes::PolygonGeometry ) { return errorList; } QList<QgsFeatureId> *duplicateIds = new QList<QgsFeatureId>(); QgsSpatialIndex *index = mLayerIndexes[layer1->id()]; if ( !index ) { qDebug() << "no index present"; delete duplicateIds; return errorList; } QMap<QgsFeatureId, FeatureLayer>::const_iterator it; for ( it = mFeatureMap2.constBegin(); it != mFeatureMap2.constEnd(); ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); QgsFeatureId currentId = it->feature.id(); if ( duplicateIds->contains( currentId ) ) { //is already a duplicate geometry..skip.. continue; } if ( testCanceled() ) break; QgsGeometry g1 = it->feature.geometry(); if ( !g1.isGeosValid() ) { qDebug() << "invalid geometry(g1) found..skipping.." << it->feature.id(); continue; } QgsRectangle bb = g1.boundingBox(); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( bb ); QList<QgsFeatureId>::ConstIterator cit = crossingIds.begin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end(); bool duplicate = false; QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() ); for ( ; cit != crossingIdsEnd; ++cit ) { duplicate = false; // skip itself if ( mFeatureMap2[*cit].feature.id() == it->feature.id() ) continue; QgsGeometry g2 = mFeatureMap2[*cit].feature.geometry(); if ( g2.isNull() ) { QgsMessageLog::logMessage( tr( "Invalid second geometry in overlaps test." ), tr( "Topology plugin" ) ); continue; } if ( !_canExportToGeos( g2 ) ) { QgsMessageLog::logMessage( tr( "Failed to import second geometry into GEOS in overlaps test." ), tr( "Topology plugin" ) ); continue; } if ( !g2.isGeosValid() ) { QgsMessageLog::logMessage( tr( "Skipping invalid second geometry of feature %1 in overlaps test." ).arg( it->feature.id() ), tr( "Topology plugin" ) ); continue; } qDebug() << "checking overlap for" << it->feature.id(); if ( g1.overlaps( g2 ) ) { duplicate = true; duplicateIds->append( mFeatureMap2[*cit].feature.id() ); } if ( duplicate ) { QList<FeatureLayer> fls; fls << *it << *it; QgsGeometry conflictGeom = g1.intersection( g2 ); if ( isExtent ) { if ( canvasExtentPoly.disjoint( conflictGeom ) ) { continue; } if ( canvasExtentPoly.crosses( conflictGeom ) ) { conflictGeom = conflictGeom.intersection( canvasExtentPoly ); } } TopolErrorOverlaps *err = new TopolErrorOverlaps( bb, conflictGeom, fls ); errorList << err; } } } delete duplicateIds; return errorList; }
bool QgsOverlayAnalyzer::intersection( QgsVectorLayer* layerA, QgsVectorLayer* layerB, const QString& shapefileName, bool onlySelectedFeatures, QProgressDialog* p ) { if ( !layerA && !layerB ) { return false; } QgsVectorDataProvider* dpA = layerA->dataProvider(); QgsVectorDataProvider* dpB = layerB->dataProvider(); if ( !dpA && !dpB ) { return false; } QGis::WkbType outputType = dpA->geometryType(); const QgsCoordinateReferenceSystem crs = layerA->srs(); QgsFieldMap fieldsA = dpA->fields(); QgsFieldMap fieldsB = dpB->fields(); combineFieldLists( fieldsA, fieldsB ); QgsVectorFileWriter vWriter( shapefileName, dpA->encoding(), fieldsA, outputType, &crs ); QgsFeature currentFeature; QgsSpatialIndex index; //take only selection if ( onlySelectedFeatures ) { const QgsFeatureIds selectionB = layerB->selectedFeaturesIds(); QgsFeatureIds::const_iterator it = selectionB.constBegin(); for ( ; it != selectionB.constEnd(); ++it ) { if ( !layerB->featureAtId( *it, currentFeature, true, true ) ) { continue; } index.insertFeature( currentFeature ); } //use QgsVectorLayer::featureAtId const QgsFeatureIds selectionA = layerA->selectedFeaturesIds(); if ( p ) { p->setMaximum( selectionA.size() ); } QgsFeature currentFeature; int processedFeatures = 0; it = selectionA.constBegin(); for ( ; it != selectionA.constEnd(); ++it ) { if ( p ) { p->setValue( processedFeatures ); } if ( p && p->wasCanceled() ) { break; } if ( !layerA->featureAtId( *it, currentFeature, true, true ) ) { continue; } intersectFeature( currentFeature, &vWriter, layerB, &index ); ++processedFeatures; } if ( p ) { p->setValue( selectionA.size() ); } } //take all features else { layerB->select( layerB->pendingAllAttributesList(), QgsRectangle(), true, false ); while ( layerB->nextFeature( currentFeature ) ) { index.insertFeature( currentFeature ); } QgsFeature currentFeature; layerA->select( layerA->pendingAllAttributesList(), QgsRectangle(), true, false ); int featureCount = layerA->featureCount(); if ( p ) { p->setMaximum( featureCount ); } int processedFeatures = 0; while ( layerA->nextFeature( currentFeature ) ) { if ( p ) { p->setValue( processedFeatures ); } if ( p && p->wasCanceled() ) { break; } intersectFeature( currentFeature, &vWriter, layerB, &index ); ++processedFeatures; } if ( p ) { p->setValue( featureCount ); } } return true; }
static bool snapLineString( QgsLineString *linestring, QgsSpatialIndex &index, QVector<AnchorPoint> &pnts, double thresh ) { QVector<QgsPoint> newPoints; QVector<int> anchors; // indexes of anchors for vertices double thresh2 = thresh * thresh; double minDistX, minDistY; // coordinates of the closest point on the segment line bool changed = false; // snap vertices for ( int v = 0; v < linestring->numPoints(); v++ ) { double x = linestring->xAt( v ); double y = linestring->yAt( v ); QgsRectangle rect( x, y, x, y ); // Find point ( should always find one point ) QList<QgsFeatureId> fids = index.intersects( rect ); Q_ASSERT( fids.count() == 1 ); int spoint = fids.first(); int anchor = pnts[spoint].anchor; if ( anchor >= 0 ) { // to be snapped linestring->setXAt( v, pnts[anchor].x ); linestring->setYAt( v, pnts[anchor].y ); anchors.append( anchor ); // point on new location changed = true; } else { anchors.append( spoint ); // old point } } // Snap all segments to anchors in threshold for ( int v = 0; v < linestring->numPoints() - 1; v++ ) { double x1 = linestring->xAt( v ); double x2 = linestring->xAt( v + 1 ); double y1 = linestring->yAt( v ); double y2 = linestring->yAt( v + 1 ); newPoints << linestring->pointN( v ); // Box double xmin = x1, xmax = x2, ymin = y1, ymax = y2; if ( xmin > xmax ) std::swap( xmin, xmax ); if ( ymin > ymax ) std::swap( ymin, ymax ); QgsRectangle rect( xmin - thresh, ymin - thresh, xmax + thresh, ymax + thresh ); // Find points const QList<QgsFeatureId> fids = index.intersects( rect ); QVector<AnchorAlongSegment> newVerticesAlongSegment; // Snap to anchor in threshold different from end points for ( QgsFeatureId fid : fids ) { int spoint = fid; if ( spoint == anchors[v] || spoint == anchors[v + 1] ) continue; // end point if ( pnts[spoint].anchor >= 0 ) continue; // point is not anchor // Check the distance double dist2 = QgsGeometryUtils::sqrDistToLine( pnts[spoint].x, pnts[spoint].y, x1, y1, x2, y2, minDistX, minDistY, 0 ); // skip points that are behind segment's endpoints or extremely close to them double dx1 = minDistX - x1, dx2 = minDistX - x2; double dy1 = minDistY - y1, dy2 = minDistY - y2; bool isOnSegment = !qgsDoubleNear( dx1 * dx1 + dy1 * dy1, 0 ) && !qgsDoubleNear( dx2 * dx2 + dy2 * dy2, 0 ); if ( isOnSegment && dist2 <= thresh2 ) { // an anchor is in the threshold AnchorAlongSegment item; item.anchor = spoint; item.along = QgsPointXY( x1, y1 ).distance( minDistX, minDistY ); newVerticesAlongSegment << item; } } if ( !newVerticesAlongSegment.isEmpty() ) { // sort by distance along the segment std::sort( newVerticesAlongSegment.begin(), newVerticesAlongSegment.end(), []( const AnchorAlongSegment & p1, const AnchorAlongSegment & p2 ) { return ( p1.along < p2.along ? -1 : ( p1.along > p2.along ) ); } ); // insert new vertices for ( int i = 0; i < newVerticesAlongSegment.count(); i++ ) { int anchor = newVerticesAlongSegment[i].anchor; newPoints << QgsPoint( pnts[anchor].x, pnts[anchor].y, 0 ); } changed = true; } } // append end point newPoints << linestring->pointN( linestring->numPoints() - 1 ); // replace linestring's points if ( changed ) linestring->setPoints( newPoints ); return changed; }
void QgsPointDisplacementRenderer::createDisplacementGroups( QgsVectorLayer* vlayer, const QgsRectangle& viewExtent ) { if ( !vlayer || ( vlayer->wkbType() != QGis::WKBPoint && vlayer->wkbType() != QGis::WKBPoint25D ) ) { return; } mDisplacementGroups.clear(); mDisplacementIds.clear(); //use a spatial index to check if there is already a point at a position QgsSpatialIndex spatialIndex; //attributes QgsAttributeList attList; QList<QString> attributeStrings = usedAttributes(); QList<QString>::const_iterator attStringIt = attributeStrings.constBegin(); for ( ; attStringIt != attributeStrings.constEnd(); ++attStringIt ) { attList.push_back( vlayer->fieldNameIndex( *attStringIt ) ); } QgsFeature f; QList<int> intersectList; vlayer->select( attList, viewExtent, true, false ); while ( vlayer->nextFeature( f ) ) { intersectList.clear(); //check, if there is already a point at that position if ( f.geometry() ) { intersectList = spatialIndex.intersects( searchRect( f.geometry()->asPoint() ) ); if ( intersectList.empty() ) { spatialIndex.insertFeature( f ); } else { //go through all the displacement group maps and search an entry where the id equals the result of the spatial search int existingEntry = intersectList.at( 0 ); bool found = false; QList<QMap<int, QgsFeature> >::iterator it = mDisplacementGroups.begin(); for ( ; it != mDisplacementGroups.end(); ++it ) { if ( it->size() > 0 && it->contains( existingEntry ) ) { found = true; QgsFeature feature; it->insert( f.id(), f ); mDisplacementIds.insert( f.id() ); break; } } if ( !found )//insert the already existing feature and the new one into a map { QMap<int, QgsFeature> newMap; QgsFeature existingFeature; vlayer->featureAtId( existingEntry, existingFeature ); newMap.insert( existingEntry, existingFeature ); mDisplacementIds.insert( existingEntry ); newMap.insert( f.id(), f ); mDisplacementIds.insert( f.id() ); mDisplacementGroups.push_back( newMap ); } } } } //refresh the selection because the vector layer is going to step through all features now vlayer->select( attList, viewExtent, true, false ); }
ErrorList topolTest::checkDuplicates( double tolerance, QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( tolerance ); Q_UNUSED( layer2 ); //TODO: multilines - check all separate pieces int i = 0; ErrorList errorList; QList<QgsFeatureId> duplicateIds; QgsSpatialIndex* index = mLayerIndexes[layer1->id()]; QgsGeometry* canvasExtentPoly = QgsGeometry::fromWkt( theQgsInterface->mapCanvas()->extent().asWktPolygon() ); QMap<QgsFeatureId, FeatureLayer>::Iterator it; QMap<QgsFeatureId, FeatureLayer>::ConstIterator FeatureListEnd = mFeatureMap2.end(); for ( it = mFeatureMap2.begin(); it != FeatureListEnd; ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); QgsFeatureId currentId = it->feature.id(); if ( duplicateIds.contains( currentId ) ) { //is already a duplicate geometry..skip.. continue; } if ( testCancelled() ) break; QgsGeometry* g1 = it->feature.geometry(); QgsRectangle bb = g1->boundingBox(); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( bb ); QList<QgsFeatureId>::Iterator cit = crossingIds.begin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end(); bool duplicate = false; for ( ; cit != crossingIdsEnd; ++cit ) { duplicate = false; // skip itself if ( mFeatureMap2[*cit].feature.id() == it->feature.id() ) continue; const QgsGeometry* g2 = mFeatureMap2[*cit].feature.constGeometry(); if ( !g2 ) { QgsMessageLog::logMessage( tr( "Invalid second geometry in duplicate geometry test." ), tr( "Topology plugin" ) ); continue; } if ( !g2->asGeos() ) { QgsMessageLog::logMessage( tr( "Failed to import second geometry into GEOS in duplicate geometry test." ), tr( "Topology plugin" ) ); continue; } if ( g1->equals( g2 ) ) { duplicate = true; duplicateIds.append( mFeatureMap2[*cit].feature.id() ); } if ( duplicate ) { QList<FeatureLayer> fls; fls << *it << *it; QScopedPointer<QgsGeometry> conflict( new QgsGeometry( *g1 ) ); if ( isExtent ) { if ( canvasExtentPoly->disjoint( conflict.data() ) ) { continue; } if ( canvasExtentPoly->crosses( conflict.data() ) ) { conflict.reset( conflict->intersection( canvasExtentPoly ) ); } } TopolErrorDuplicates* err = new TopolErrorDuplicates( bb, conflict.take(), fls ); errorList << err; } } } delete canvasExtentPoly; return errorList; }
ErrorList topolTest::checkCloseFeature( double tolerance, QgsVectorLayer* layer1, QgsVectorLayer* layer2, bool isExtent ) { Q_UNUSED( isExtent ); ErrorList errorList; QgsSpatialIndex* index = 0; bool badG1 = false, badG2 = false; bool skipItself = layer1 == layer2; int i = 0; QList<FeatureLayer>::Iterator it; QList<FeatureLayer>::ConstIterator FeatureListEnd = mFeatureList1.end(); for ( it = mFeatureList1.begin(); it != FeatureListEnd; ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); if ( testCancelled() ) break; QgsGeometry* g1 = it->feature.geometry(); if ( !g1 || !g1->asGeos() ) { badG1 = true; continue; } QgsRectangle bb = g1->boundingBox(); // increase bounding box by tolerance QgsRectangle frame( bb.xMinimum() - tolerance, bb.yMinimum() - tolerance, bb.xMaximum() + tolerance, bb.yMaximum() + tolerance ); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( frame ); QList<QgsFeatureId>::Iterator cit = crossingIds.begin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end(); for ( ; cit != crossingIdsEnd; ++cit ) { QgsFeature& f = mFeatureMap2[*cit].feature; QgsGeometry* g2 = f.geometry(); // skip itself, when invoked with the same layer if ( skipItself && f.id() == it->feature.id() ) continue; if ( !g2 || !g2->asGeos() ) { badG2 = true; continue; } if ( g1->distance( *g2 ) < tolerance ) { QgsRectangle r = g2->boundingBox(); r.combineExtentWith( &bb ); QList<FeatureLayer> fls; FeatureLayer fl; fl.feature = f; fl.layer = layer2; fls << *it << fl; QgsGeometry* conflict = new QgsGeometry( *g2 ); TopolErrorClose* err = new TopolErrorClose( r, conflict, fls ); //TopolErrorClose* err = new TopolErrorClose(r, g2, fls); errorList << err; } } } if ( badG2 ) QgsMessageLog::logMessage( tr( "Invalid second geometry." ), tr( "Topology plugin" ) ); if ( badG1 ) QgsMessageLog::logMessage( tr( "Invalid first geometry." ), tr( "Topology plugin" ) ); return errorList; }
ErrorList topolTest::checkPointInPolygon( double tolerance, QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( tolerance ); int i = 0; ErrorList errorList; if ( layer1->geometryType() != QGis::Point ) { return errorList; } if ( layer2->geometryType() != QGis::Polygon ) { return errorList; } QgsSpatialIndex* index = mLayerIndexes[layer2->id()]; QgsGeometry* canvasExtentPoly = QgsGeometry::fromWkt( theQgsInterface->mapCanvas()->extent().asWktPolygon() ); QList<FeatureLayer>::Iterator it; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); if ( testCancelled() ) break; QgsGeometry* g1 = it->feature.geometry(); QgsRectangle bb = g1->boundingBox(); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( bb ); QList<QgsFeatureId>::Iterator cit = crossingIds.begin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.end(); bool touched = false; for ( ; cit != crossingIdsEnd; ++cit ) { QgsFeature& f = mFeatureMap2[*cit].feature; const QgsGeometry* g2 = f.constGeometry(); if ( !g2 || !g2->asGeos() ) { QgsMessageLog::logMessage( tr( "Second geometry missing or GEOS import failed." ), tr( "Topology plugin" ) ); continue; } if ( g2->contains( g1 ) ) { touched = true; break; } } if ( !touched ) { QgsGeometry* conflictGeom = new QgsGeometry( *g1 ); if ( isExtent ) { if ( canvasExtentPoly->disjoint( conflictGeom ) ) { delete conflictGeom; continue; } } QList<FeatureLayer> fls; fls << *it << *it; //bb.scale(10); TopolErrorPointNotInPolygon* err = new TopolErrorPointNotInPolygon( bb, conflictGeom, fls ); errorList << err; } } delete canvasExtentPoly; return errorList; }
void QgsPointDisplacementRenderer::createDisplacementGroups( QgsVectorLayer* vlayer, const QgsRectangle& viewExtent ) { if ( !vlayer || ( vlayer->wkbType() != QGis::WKBPoint && vlayer->wkbType() != QGis::WKBPoint25D ) ) { return; } mDisplacementGroups.clear(); mDisplacementIds.clear(); //use a spatial index to check if there is already a point at a position QgsSpatialIndex spatialIndex; //attributes QgsAttributeList attList; QList<QString> attributeStrings = usedAttributes(); QList<QString>::const_iterator attStringIt = attributeStrings.constBegin(); for ( ; attStringIt != attributeStrings.constEnd(); ++attStringIt ) { attList.push_back( vlayer->fieldNameIndex( *attStringIt ) ); } QgsFeature f; QList<QgsFeatureId> intersectList; //Because the new vector api does not allow querying features by id within a nextFeature loop, default constructed QgsFeature() is //inserted first and the real features are created in a second loop QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( viewExtent ).setSubsetOfAttributes( attList ) ); while ( fit.nextFeature( f ) ) { intersectList.clear(); //check, if there is already a point at that position if ( f.geometry() ) { intersectList = spatialIndex.intersects( searchRect( f.geometry()->asPoint() ) ); if ( intersectList.empty() ) { spatialIndex.insertFeature( f ); } else { //go through all the displacement group maps and search an entry where the id equals the result of the spatial search QgsFeatureId existingEntry = intersectList.at( 0 ); bool found = false; QList< QMap<QgsFeatureId, QgsFeature> >::iterator it = mDisplacementGroups.begin(); for ( ; it != mDisplacementGroups.end(); ++it ) { if ( it->size() > 0 && it->contains( existingEntry ) ) { found = true; QgsFeature feature; it->insert( f.id(), QgsFeature() ); mDisplacementIds.insert( f.id() ); break; } } if ( !found )//insert the already existing feature and the new one into a map { QMap<QgsFeatureId, QgsFeature> newMap; newMap.insert( existingEntry, QgsFeature() ); mDisplacementIds.insert( existingEntry ); newMap.insert( f.id(), QgsFeature() ); mDisplacementIds.insert( f.id() ); mDisplacementGroups.push_back( newMap ); } } } } //insert the real features into mDisplacementGroups QList< QMap<QgsFeatureId, QgsFeature> >::iterator it = mDisplacementGroups.begin(); for ( ; it != mDisplacementGroups.end(); ++it ) { QMap<QgsFeatureId, QgsFeature>::iterator mapIt = it->begin(); for ( ; mapIt != it->end(); ++mapIt ) { QgsFeature fet; vlayer->getFeatures( QgsFeatureRequest().setFilterFid( mapIt.key() ) ).nextFeature( fet ); mapIt.value() = fet; } } }
ErrorList topolTest::checkDuplicates( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( layer2 ); //TODO: multilines - check all separate pieces int i = 0; ErrorList errorList; QList<QgsFeatureId> duplicateIds; QgsSpatialIndex *index = mLayerIndexes[layer1->id()]; QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() ); QMap<QgsFeatureId, FeatureLayer>::const_iterator it; for ( it = mFeatureMap2.constBegin(); it != mFeatureMap2.constEnd(); ++it ) { if ( !( ++i % 100 ) ) emit progress( i ); QgsFeatureId currentId = it->feature.id(); if ( duplicateIds.contains( currentId ) ) { //is already a duplicate geometry..skip.. continue; } if ( testCanceled() ) break; QgsGeometry g1 = it->feature.geometry(); QgsRectangle bb = g1.boundingBox(); QList<QgsFeatureId> crossingIds; crossingIds = index->intersects( bb ); QList<QgsFeatureId>::ConstIterator cit = crossingIds.constBegin(); QList<QgsFeatureId>::ConstIterator crossingIdsEnd = crossingIds.constEnd(); bool duplicate = false; for ( ; cit != crossingIdsEnd; ++cit ) { duplicate = false; // skip itself if ( mFeatureMap2[*cit].feature.id() == it->feature.id() ) continue; QgsGeometry g2 = mFeatureMap2[*cit].feature.geometry(); if ( g2.isNull() ) { QgsMessageLog::logMessage( tr( "Invalid second geometry in duplicate geometry test." ), tr( "Topology plugin" ) ); continue; } if ( !_canExportToGeos( g2 ) ) { QgsMessageLog::logMessage( tr( "Failed to import second geometry into GEOS in duplicate geometry test." ), tr( "Topology plugin" ) ); continue; } if ( g1.isGeosEqual( g2 ) ) { duplicate = true; duplicateIds.append( mFeatureMap2[*cit].feature.id() ); } if ( duplicate ) { QList<FeatureLayer> fls; fls << *it << *it; QgsGeometry conflict( g1 ); if ( isExtent ) { if ( canvasExtentPoly.disjoint( conflict ) ) { continue; } if ( canvasExtentPoly.crosses( conflict ) ) { conflict = conflict.intersection( canvasExtentPoly ); } } TopolErrorDuplicates *err = new TopolErrorDuplicates( bb, conflict, fls ); errorList << err; } } } return errorList; }