void QgsZonalStatistics::statisticsFromPreciseIntersection( void* band, const QgsGeometry* poly, int pixelOffsetX, int pixelOffsetY, int nCellsX, int nCellsY, double cellSizeX, double cellSizeY, const QgsRectangle& rasterBBox, FeatureStats &stats ) { stats.reset(); double currentY = rasterBBox.yMaximum() - pixelOffsetY * cellSizeY - cellSizeY / 2; float* pixelData = ( float * ) CPLMalloc( sizeof( float ) ); QgsGeometry* pixelRectGeometry = nullptr; double hCellSizeX = cellSizeX / 2.0; double hCellSizeY = cellSizeY / 2.0; double pixelArea = cellSizeX * cellSizeY; double weight = 0; for ( int row = 0; row < nCellsY; ++row ) { double currentX = rasterBBox.xMinimum() + cellSizeX / 2.0 + pixelOffsetX * cellSizeX; for ( int col = 0; col < nCellsX; ++col ) { if ( GDALRasterIO( band, GF_Read, pixelOffsetX + col, pixelOffsetY + row, nCellsX, 1, pixelData, 1, 1, GDT_Float32, 0, 0 ) != CE_None ) QgsDebugMsg( "Raster IO Error" ); if ( !validPixel( *pixelData ) ) continue; pixelRectGeometry = QgsGeometry::fromRect( QgsRectangle( currentX - hCellSizeX, currentY - hCellSizeY, currentX + hCellSizeX, currentY + hCellSizeY ) ); if ( pixelRectGeometry ) { //intersection QgsGeometry *intersectGeometry = pixelRectGeometry->intersection( poly ); if ( intersectGeometry ) { double intersectionArea = intersectGeometry->area(); if ( intersectionArea >= 0.0 ) { weight = intersectionArea / pixelArea; stats.addValue( *pixelData, weight ); } delete intersectGeometry; } delete pixelRectGeometry; pixelRectGeometry = nullptr; } currentX += cellSizeX; } currentY -= cellSizeY; } CPLFree( pixelData ); }
void QgsZonalStatistics::statisticsFromPreciseIntersection( const QgsGeometry &poly, int pixelOffsetX, int pixelOffsetY, int nCellsX, int nCellsY, double cellSizeX, double cellSizeY, const QgsRectangle &rasterBBox, FeatureStats &stats ) { stats.reset(); double currentY = rasterBBox.yMaximum() - pixelOffsetY * cellSizeY - cellSizeY / 2; QgsGeometry pixelRectGeometry; double hCellSizeX = cellSizeX / 2.0; double hCellSizeY = cellSizeY / 2.0; double pixelArea = cellSizeX * cellSizeY; double weight = 0; QgsRectangle featureBBox = poly.boundingBox().intersect( &rasterBBox ); QgsRectangle intersectBBox = rasterBBox.intersect( &featureBBox ); QgsRasterBlock *block = mRasterProvider->block( mRasterBand, intersectBBox, nCellsX, nCellsY ); for ( int i = 0; i < nCellsY; ++i ) { double currentX = rasterBBox.xMinimum() + cellSizeX / 2.0 + pixelOffsetX * cellSizeX; for ( int j = 0; j < nCellsX; ++j ) { if ( !validPixel( block->value( i, j ) ) ) { continue; } pixelRectGeometry = QgsGeometry::fromRect( QgsRectangle( currentX - hCellSizeX, currentY - hCellSizeY, currentX + hCellSizeX, currentY + hCellSizeY ) ); if ( !pixelRectGeometry.isNull() ) { //intersection QgsGeometry intersectGeometry = pixelRectGeometry.intersection( poly ); if ( !intersectGeometry.isNull() ) { double intersectionArea = intersectGeometry.area(); if ( intersectionArea >= 0.0 ) { weight = intersectionArea / pixelArea; stats.addValue( block->value( i, j ), weight ); } } pixelRectGeometry = QgsGeometry(); } currentX += cellSizeX; } currentY -= cellSizeY; } delete block; }
void QgsZonalStatistics::statisticsFromPreciseIntersection( void* band, QgsGeometry* poly, int pixelOffsetX, int pixelOffsetY, int nCellsX, int nCellsY, double cellSizeX, double cellSizeY, const QgsRectangle& rasterBBox, double& sum, double& count ) { sum = 0; count = 0; double currentY = rasterBBox.yMaximum() - pixelOffsetY * cellSizeY - cellSizeY / 2; float* pixelData = ( float * ) CPLMalloc( sizeof( float ) ); QgsGeometry* pixelRectGeometry = 0; double hCellSizeX = cellSizeX / 2.0; double hCellSizeY = cellSizeY / 2.0; double pixelArea = cellSizeX * cellSizeY; double weight = 0; for ( int row = 0; row < nCellsY; ++row ) { double currentX = rasterBBox.xMinimum() + cellSizeX / 2.0 + pixelOffsetX * cellSizeX; for ( int col = 0; col < nCellsX; ++col ) { GDALRasterIO( band, GF_Read, pixelOffsetX + col, pixelOffsetY + row, nCellsX, 1, pixelData, 1, 1, GDT_Float32, 0, 0 ); pixelRectGeometry = QgsGeometry::fromRect( QgsRectangle( currentX - hCellSizeX, currentY - hCellSizeY, currentX + hCellSizeX, currentY + hCellSizeY ) ); if ( pixelRectGeometry ) { //intersection QgsGeometry *intersectGeometry = pixelRectGeometry->intersection( poly ); if ( intersectGeometry ) { double intersectionArea = intersectGeometry->area(); if ( intersectionArea >= 0.0 ) { weight = intersectionArea / pixelArea; count += weight; sum += *pixelData * weight; } delete intersectGeometry; } delete pixelRectGeometry; pixelRectGeometry = 0; } currentX += cellSizeX; } currentY -= cellSizeY; } CPLFree( pixelData ); }
void QgsOverlayAnalyzer::intersectFeature( QgsFeature& f, QgsVectorFileWriter* vfw, QgsVectorLayer* vl, QgsSpatialIndex* index ) { if ( !f.hasGeometry() ) { return; } QgsGeometry featureGeometry = f.geometry(); QgsGeometry intersectGeometry; QgsFeature overlayFeature; QList<QgsFeatureId> intersects; intersects = index->intersects( featureGeometry.boundingBox() ); QList<QgsFeatureId>::const_iterator it = intersects.constBegin(); QgsFeature outFeature; for ( ; it != intersects.constEnd(); ++it ) { if ( !vl->getFeatures( QgsFeatureRequest().setFilterFid( *it ) ).nextFeature( overlayFeature ) ) { continue; } if ( featureGeometry.intersects( overlayFeature.geometry() ) ) { intersectGeometry = featureGeometry.intersection( overlayFeature.geometry() ); outFeature.setGeometry( intersectGeometry ); QgsAttributes attributesA = f.attributes(); QgsAttributes attributesB = overlayFeature.attributes(); combineAttributeMaps( attributesA, attributesB ); outFeature.setAttributes( attributesA ); //add it to vector file writer if ( vfw ) { vfw->addFeature( outFeature ); } } } }
void QgsOverlayAnalyzer::intersectFeature( QgsFeature& f, QgsVectorFileWriter* vfw, QgsVectorLayer* vl, QgsSpatialIndex* index ) { QgsGeometry* featureGeometry = f.geometry(); QgsGeometry* intersectGeometry = 0; QgsFeature overlayFeature; if ( !featureGeometry ) { return; } QList<int> intersects; intersects = index->intersects( featureGeometry->boundingBox() ); QList<int>::const_iterator it = intersects.constBegin(); QgsFeature outFeature; for ( ; it != intersects.constEnd(); ++it ) { if ( !vl->featureAtId( *it, overlayFeature, true, true ) ) { continue; } if ( featureGeometry->intersects( overlayFeature.geometry() ) ) { intersectGeometry = featureGeometry->intersection( overlayFeature.geometry() ); outFeature.setGeometry( intersectGeometry ); QgsAttributeMap attributeMapA = f.attributeMap(); QgsAttributeMap attributeMapB = overlayFeature.attributeMap(); combineAttributeMaps( attributeMapA, attributeMapB ); outFeature.setAttributeMap( attributeMapA ); //add it to vector file writer if ( vfw ) { vfw->addFeature( outFeature ); } } } }
void QgsOverlayAnalyzer::intersectFeature( QgsFeature &f, QgsVectorFileWriter *vfw, QgsVectorLayer *vl, QgsSpatialIndex *index ) { if ( !f.hasGeometry() ) { return; } QgsGeometry featureGeometry = f.geometry(); QgsGeometry intersectGeometry; QgsFeature overlayFeature; QList<QgsFeatureId> intersects = index->intersects( featureGeometry.boundingBox() ); QgsFeatureRequest req = QgsFeatureRequest().setFilterFids( intersects.toSet() ); QgsFeatureIterator intersectIt = vl->getFeatures( req ); QgsFeature outFeature; while ( intersectIt.nextFeature( overlayFeature ) ) { if ( featureGeometry.intersects( overlayFeature.geometry() ) ) { intersectGeometry = featureGeometry.intersection( overlayFeature.geometry() ); outFeature.setGeometry( intersectGeometry ); QgsAttributes attributesA = f.attributes(); QgsAttributes attributesB = overlayFeature.attributes(); combineAttributeMaps( attributesA, attributesB ); outFeature.setAttributes( attributesA ); //add it to vector file writer if ( vfw ) { vfw->addFeature( outFeature ); } } } }
int QgsMapCanvasSnapper::snapToBackgroundLayers( const QgsPoint& point, QList<QgsSnappingResult>& results, const QList<QgsPoint>& excludePoints ) { results.clear(); if ( !mSnapper ) return 5; //topological editing on? int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ); //snapping on intersection on? int intersectionSnapping = QgsProject::instance()->readNumEntry( "Digitizing", "/IntersectionSnapping", 0 ); if ( topologicalEditing == 0 ) { if ( intersectionSnapping == 0 ) mSnapper->setSnapMode( QgsSnapper::SnapWithOneResult ); else mSnapper->setSnapMode( QgsSnapper::SnapWithResultsWithinTolerances ); } else if ( intersectionSnapping == 0 ) { mSnapper->setSnapMode( QgsSnapper::SnapWithResultsForSamePosition ); } else { mSnapper->setSnapMode( QgsSnapper::SnapWithResultsWithinTolerances ); } //read snapping settings from project bool snappingDefinedInProject, ok; QStringList layerIdList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingList", QStringList(), &snappingDefinedInProject ); QStringList enabledList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingEnabledList", QStringList(), &ok ); QStringList toleranceList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceList", QStringList(), &ok ); QStringList toleranceUnitList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList", QStringList(), &ok ); QStringList snapToList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnapToList", QStringList(), &ok ); if ( !( layerIdList.size() == enabledList.size() && layerIdList.size() == toleranceList.size() && layerIdList.size() == toleranceUnitList.size() && layerIdList.size() == snapToList.size() ) ) { // lists must have the same size, otherwise something is wrong return 1; } QList<QgsSnapper::SnapLayer> snapLayers; QgsSnapper::SnapLayer snapLayer; // Use snapping information from the project if ( snappingDefinedInProject ) { // set layers, tolerances, snap to segment/vertex to QgsSnapper QStringList::const_iterator layerIt( layerIdList.constBegin() ); QStringList::const_iterator tolIt( toleranceList.constBegin() ); QStringList::const_iterator tolUnitIt( toleranceUnitList.constBegin() ); QStringList::const_iterator snapIt( snapToList.constBegin() ); QStringList::const_iterator enabledIt( enabledList.constBegin() ); for ( ; layerIt != layerIdList.constEnd(); ++layerIt, ++tolIt, ++tolUnitIt, ++snapIt, ++enabledIt ) { if ( *enabledIt != "enabled" ) { // skip layer if snapping is not enabled continue; } //layer QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( QgsMapLayerRegistry::instance()->mapLayer( *layerIt ) ); if ( !vlayer || !vlayer->hasGeometryType() ) continue; snapLayer.mLayer = vlayer; //tolerance snapLayer.mTolerance = tolIt->toDouble(); snapLayer.mUnitType = ( QgsTolerance::UnitType ) tolUnitIt->toInt(); // segment or vertex if ( *snapIt == "to_vertex" ) { snapLayer.mSnapTo = QgsSnapper::SnapToVertex; } else if ( *snapIt == "to_segment" ) { snapLayer.mSnapTo = QgsSnapper::SnapToSegment; } else { // to vertex and segment snapLayer.mSnapTo = QgsSnapper::SnapToVertexAndSegment; } snapLayers.append( snapLayer ); } } else { // nothing in project. Use default snapping tolerance to vertex of current layer QgsMapLayer* currentLayer = mMapCanvas->currentLayer(); if ( !currentLayer ) return 2; QgsVectorLayer* currentVectorLayer = qobject_cast<QgsVectorLayer *>( currentLayer ); if ( !currentVectorLayer ) return 3; snapLayer.mLayer = currentVectorLayer; //default snap mode QSettings settings; QString defaultSnapString = settings.value( "/qgis/digitizing/default_snap_mode", "off" ).toString(); if ( defaultSnapString == "to segment" ) { snapLayer.mSnapTo = QgsSnapper::SnapToSegment; } else if ( defaultSnapString == "to vertex and segment" ) { snapLayer.mSnapTo = QgsSnapper::SnapToVertexAndSegment; } else if ( defaultSnapString == "to vertex" ) { snapLayer.mSnapTo = QgsSnapper::SnapToVertex; } else { return 0; } //default snapping tolerance (returned in map units) snapLayer.mTolerance = QgsTolerance::defaultTolerance( currentVectorLayer, mMapCanvas->mapSettings() ); snapLayer.mUnitType = QgsTolerance::LayerUnits; snapLayers.append( snapLayer ); } mSnapper->setSnapLayers( snapLayers ); if ( mSnapper->snapMapPoint( point, results, excludePoints ) != 0 ) return 4; if ( intersectionSnapping != 1 ) return 0; QList<QgsSnappingResult> segments; QList<QgsSnappingResult> points; for ( QList<QgsSnappingResult>::const_iterator it = results.constBegin(); it != results.constEnd(); ++it ) { if ( it->snappedVertexNr == -1 ) { QgsDebugMsg( "segment" ); segments.push_back( *it ); } else { QgsDebugMsg( "no segment" ); points.push_back( *it ); } } if ( segments.length() < 2 ) return 0; QList<QgsSnappingResult> myResults; for ( QList<QgsSnappingResult>::const_iterator oSegIt = segments.constBegin(); oSegIt != segments.constEnd(); ++oSegIt ) { QgsDebugMsg( QString::number( oSegIt->beforeVertexNr ) ); QVector<QgsPoint> vertexPoints; vertexPoints.append( oSegIt->beforeVertex ); vertexPoints.append( oSegIt->afterVertex ); QgsGeometry* lineA = QgsGeometry::fromPolyline( vertexPoints ); for ( QList<QgsSnappingResult>::iterator iSegIt = segments.begin(); iSegIt != segments.end(); ++iSegIt ) { QVector<QgsPoint> vertexPoints; vertexPoints.append( iSegIt->beforeVertex ); vertexPoints.append( iSegIt->afterVertex ); QgsGeometry* lineB = QgsGeometry::fromPolyline( vertexPoints ); QgsGeometry* intersectionPoint = lineA->intersection( lineB ); delete lineB; if ( intersectionPoint && intersectionPoint->type() == QGis::Point ) { //We have to check the intersection point is inside the tolerance distance for both layers double toleranceA = 0; double toleranceB = 0; for ( int i = 0 ;i < snapLayers.size();++i ) { if ( snapLayers[i].mLayer == oSegIt->layer ) { toleranceA = QgsTolerance::toleranceInMapUnits( snapLayers[i].mTolerance, snapLayers[i].mLayer, mMapCanvas->mapSettings(), snapLayers[i].mUnitType ); } if ( snapLayers[i].mLayer == iSegIt->layer ) { toleranceB = QgsTolerance::toleranceInMapUnits( snapLayers[i].mTolerance, snapLayers[i].mLayer, mMapCanvas->mapSettings(), snapLayers[i].mUnitType ); } } QgsGeometry* cursorPoint = QgsGeometry::fromPoint( point ); double distance = intersectionPoint->distance( *cursorPoint ); if ( distance < toleranceA && distance < toleranceB ) { iSegIt->snappedVertex = intersectionPoint->asPoint(); myResults.append( *iSegIt ); } delete cursorPoint; } delete intersectionPoint; } delete lineA; } if ( myResults.length() > 0 ) { results.clear(); results = myResults; } return 0; }
QVariantMap QgsLineIntersectionAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { std::unique_ptr< QgsFeatureSource > sourceA( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); if ( !sourceA ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); std::unique_ptr< QgsFeatureSource > sourceB( parameterAsSource( parameters, QStringLiteral( "INTERSECT" ), context ) ); if ( !sourceB ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INTERSECT" ) ) ); const QStringList fieldsA = parameterAsFields( parameters, QStringLiteral( "INPUT_FIELDS" ), context ); const QStringList fieldsB = parameterAsFields( parameters, QStringLiteral( "INTERSECT_FIELDS" ), context ); QgsAttributeList fieldIndicesA = QgsProcessingUtils::fieldNamesToIndices( fieldsA, sourceA->fields() ); QgsAttributeList fieldIndicesB = QgsProcessingUtils::fieldNamesToIndices( fieldsB, sourceB->fields() ); QString intersectFieldsPrefix = parameterAsString( parameters, QStringLiteral( "INTERSECT_FIELDS_PREFIX" ), context ); QgsFields outFields = QgsProcessingUtils::combineFields( QgsProcessingUtils::indicesToFields( fieldIndicesA, sourceA->fields() ), QgsProcessingUtils::indicesToFields( fieldIndicesB, sourceB->fields() ), intersectFieldsPrefix ); QString dest; std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, outFields, QgsWkbTypes::Point, sourceA->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); QgsSpatialIndex spatialIndex( sourceB->getFeatures( QgsFeatureRequest().setNoAttributes().setDestinationCrs( sourceA->sourceCrs(), context.transformContext() ) ), feedback ); QgsFeature outFeature; QgsFeatureIterator features = sourceA->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( fieldIndicesA ) ); double step = sourceA->featureCount() > 0 ? 100.0 / sourceA->featureCount() : 1; int i = 0; QgsFeature inFeatureA; while ( features.nextFeature( inFeatureA ) ) { i++; if ( feedback->isCanceled() ) { break; } if ( !inFeatureA.hasGeometry() ) continue; QgsGeometry inGeom = inFeatureA.geometry(); QgsFeatureIds lines = spatialIndex.intersects( inGeom.boundingBox() ).toSet(); if ( !lines.empty() ) { // use prepared geometries for faster intersection tests std::unique_ptr< QgsGeometryEngine > engine( QgsGeometry::createGeometryEngine( inGeom.constGet() ) ); engine->prepareGeometry(); QgsFeatureRequest request = QgsFeatureRequest().setFilterFids( lines ); request.setDestinationCrs( sourceA->sourceCrs(), context.transformContext() ); request.setSubsetOfAttributes( fieldIndicesB ); QgsFeature inFeatureB; QgsFeatureIterator featuresB = sourceB->getFeatures( request ); while ( featuresB.nextFeature( inFeatureB ) ) { if ( feedback->isCanceled() ) { break; } QgsGeometry tmpGeom = inFeatureB.geometry(); if ( engine->intersects( tmpGeom.constGet() ) ) { QgsMultiPointXY points; QgsGeometry intersectGeom = inGeom.intersection( tmpGeom ); QgsAttributes outAttributes; for ( int a : qgis::as_const( fieldIndicesA ) ) { outAttributes.append( inFeatureA.attribute( a ) ); } for ( int b : qgis::as_const( fieldIndicesB ) ) { outAttributes.append( inFeatureB.attribute( b ) ); } if ( QgsWkbTypes::flatType( intersectGeom.wkbType() ) == QgsWkbTypes::GeometryCollection ) { const QVector<QgsGeometry> geomCollection = intersectGeom.asGeometryCollection(); for ( const QgsGeometry &part : geomCollection ) { if ( part.type() == QgsWkbTypes::PointGeometry ) { if ( part.isMultipart() ) { points = part.asMultiPoint(); } else { points.append( part.asPoint() ); } } } } else if ( intersectGeom.type() == QgsWkbTypes::PointGeometry ) { if ( intersectGeom.isMultipart() ) { points = intersectGeom.asMultiPoint(); } else { points.append( intersectGeom.asPoint() ); } } for ( const QgsPointXY &j : qgis::as_const( points ) ) { outFeature.setGeometry( QgsGeometry::fromPointXY( j ) ); outFeature.setAttributes( outAttributes ); sink->addFeature( outFeature, QgsFeatureSink::FastInsert ); } } } } feedback->setProgress( i * step ); } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); return outputs; }
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; }
QgsGeometry* QgsTransectSample::clipBufferLine( QgsGeometry* stratumGeom, QgsGeometry* clippedBaseline, double tolerance ) { if ( !stratumGeom || !clippedBaseline || clippedBaseline->wkbType() == QGis::WKBUnknown ) { return 0; } double currentBufferDist = tolerance; int maxLoops = 10; for ( int i = 0; i < maxLoops; ++i ) { //loop with tolerance: create buffer, convert buffer to line, clip line by stratum, test if result is (single) line QgsGeometry* clipBaselineBuffer = clippedBaseline->buffer( currentBufferDist, 8 ); if ( !clipBaselineBuffer ) { delete clipBaselineBuffer; continue; } //it is also possible that clipBaselineBuffer is a multipolygon QgsGeometry* bufferLine = 0; //buffer line or multiline QgsGeometry* bufferLineClipped = 0; QgsMultiPolyline mpl; if ( clipBaselineBuffer->isMultipart() ) { QgsMultiPolygon bufferMultiPolygon = clipBaselineBuffer->asMultiPolygon(); if ( bufferMultiPolygon.size() < 1 ) { delete clipBaselineBuffer; continue; } for ( int j = 0; j < bufferMultiPolygon.size(); ++j ) { int size = bufferMultiPolygon.at( j ).size(); for ( int k = 0; k < size; ++k ) { mpl.append( bufferMultiPolygon.at( j ).at( k ) ); } } bufferLine = QgsGeometry::fromMultiPolyline( mpl ); } else { QgsPolygon bufferPolygon = clipBaselineBuffer->asPolygon(); if ( bufferPolygon.size() < 1 ) { delete clipBaselineBuffer; continue; } int size = bufferPolygon.size(); for ( int j = 0; j < size; ++j ) { mpl.append( bufferPolygon[j] ); } bufferLine = QgsGeometry::fromMultiPolyline( mpl ); } bufferLineClipped = bufferLine->intersection( stratumGeom ); if ( bufferLineClipped && bufferLineClipped->type() == QGis::Line ) { //if stratumGeom is a multipolygon, bufferLineClipped must intersect each part bool bufferLineClippedIntersectsStratum = true; if ( stratumGeom->wkbType() == QGis::WKBMultiPolygon || stratumGeom->wkbType() == QGis::WKBMultiPolygon25D ) { QVector<QgsPolygon> multiPoly = stratumGeom->asMultiPolygon(); QVector<QgsPolygon>::const_iterator multiIt = multiPoly.constBegin(); for ( ; multiIt != multiPoly.constEnd(); ++multiIt ) { QgsGeometry* poly = QgsGeometry::fromPolygon( *multiIt ); if ( !poly->intersects( bufferLineClipped ) ) { bufferLineClippedIntersectsStratum = false; delete poly; break; } delete poly; } } if ( bufferLineClippedIntersectsStratum ) { return bufferLineClipped; } } delete bufferLineClipped; delete clipBaselineBuffer; delete bufferLine; currentBufferDist /= 2; } return 0; //no solution found even with reduced tolerances }
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::checkGaps( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( layer2 ); int i = 0; ErrorList errorList; GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler(); // could be enabled for lines and points too // so duplicate rule may be removed? if ( layer1->geometryType() != QgsWkbTypes::PolygonGeometry ) { return errorList; } QList<FeatureLayer>::iterator it; QgsGeometry g1; QList<GEOSGeometry *> geomList; qDebug() << mFeatureList1.count() << " features in list!"; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { qDebug() << "reading features-" << i; if ( !( ++i % 100 ) ) { emit progress( i ); } if ( testCanceled() ) { break; } g1 = it->feature.geometry(); if ( g1.isNull() ) { continue; } if ( !_canExportToGeos( g1 ) ) { continue; } if ( !g1.isGeosValid() ) { qDebug() << "invalid geometry found..skipping.." << it->feature.id(); continue; } if ( g1.isMultipart() ) { QgsMultiPolygonXY polys = g1.asMultiPolygon(); for ( int m = 0; m < polys.count(); m++ ) { QgsPolygonXY polygon = polys[m]; QgsGeometry polyGeom = QgsGeometry::fromPolygonXY( polygon ); geomList.push_back( QgsGeos::asGeos( polyGeom ).release() ); } } else { geomList.push_back( QgsGeos::asGeos( g1 ).release() ); } } GEOSGeometry **geomArray = new GEOSGeometry*[geomList.size()]; for ( int i = 0; i < geomList.size(); ++i ) { //qDebug() << "filling geometry array-" << i; geomArray[i] = geomList.at( i ); } qDebug() << "creating geometry collection-"; if ( geomList.isEmpty() ) { //qDebug() << "geometry list is empty!"; delete [] geomArray; return errorList; } GEOSGeometry *collection = nullptr; collection = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOLYGON, geomArray, geomList.size() ); qDebug() << "performing cascaded union..might take time..-"; GEOSGeometry *unionGeom = GEOSUnionCascaded_r( geosctxt, collection ); //delete[] geomArray; QgsGeometry test = QgsGeos::geometryFromGeos( unionGeom ); //qDebug() << "wktmerged - " << test.exportToWkt(); QString extentWkt = test.boundingBox().asWktPolygon(); QgsGeometry extentGeom = QgsGeometry::fromWkt( extentWkt ); QgsGeometry bufferExtent = extentGeom.buffer( 2, 3 ); //qDebug() << "extent wkt - " << bufferExtent->exportToWkt(); QgsGeometry diffGeoms = bufferExtent.difference( test ); if ( !diffGeoms ) { qDebug() << "difference result 0-"; return errorList; } //qDebug() << "difference gometry - " << diffGeoms->exportToWkt(); QVector<QgsGeometry> geomColl = diffGeoms.asGeometryCollection(); QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() ); for ( int i = 1; i < geomColl.count() ; ++i ) { QgsGeometry conflictGeom = geomColl[i]; if ( isExtent ) { if ( canvasExtentPoly.disjoint( conflictGeom ) ) { continue; } if ( canvasExtentPoly.crosses( conflictGeom ) ) { conflictGeom = conflictGeom.intersection( canvasExtentPoly ); } } QgsRectangle bBox = conflictGeom.boundingBox(); FeatureLayer ftrLayer1; ftrLayer1.layer = layer1; QList<FeatureLayer> errorFtrLayers; errorFtrLayers << ftrLayer1 << ftrLayer1; TopolErrorGaps *err = new TopolErrorGaps( bBox, conflictGeom, errorFtrLayers ); 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; }
ErrorList topolTest::checkyLineEndsCoveredByPoints( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { int i = 0; ErrorList errorList; if ( layer1->geometryType() != QgsWkbTypes::LineGeometry ) { 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(); QgsPolylineXY g1Polyline = g1.asPolyline(); QgsGeometry startPoint = QgsGeometry::fromPointXY( g1Polyline.at( 0 ) ); QgsGeometry endPoint = QgsGeometry::fromPointXY( g1Polyline.last() ); 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; bool touchStartPoint = false; bool touchEndPoint = 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 ( g2.intersects( startPoint ) ) { touchStartPoint = true; } if ( g2.intersects( endPoint ) ) { touchEndPoint = true; } if ( touchStartPoint && touchEndPoint ) { touched = true; break; } } if ( !touched ) { QgsGeometry conflictGeom = g1; if ( isExtent ) { if ( canvasExtentPoly.disjoint( conflictGeom ) ) { continue; } if ( canvasExtentPoly.crosses( conflictGeom ) ) { conflictGeom = conflictGeom.intersection( canvasExtentPoly ); } } QList<FeatureLayer> fls; fls << *it << *it; //bb.scale(10); TopolErrorLineEndsNotCoveredByPoints *err = new TopolErrorLineEndsNotCoveredByPoints( bb, conflictGeom, fls ); errorList << err; } } return errorList; }
QVariantMap QgsClipAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { std::unique_ptr< QgsFeatureSource > featureSource( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); if ( !featureSource ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); std::unique_ptr< QgsFeatureSource > maskSource( parameterAsSource( parameters, QStringLiteral( "OVERLAY" ), context ) ); if ( !maskSource ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "OVERLAY" ) ) ); QString dest; QgsWkbTypes::GeometryType sinkType = QgsWkbTypes::geometryType( featureSource->wkbType() ); std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, featureSource->fields(), QgsWkbTypes::multiType( featureSource->wkbType() ), featureSource->sourceCrs() ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); // first build up a list of clip geometries QVector< QgsGeometry > clipGeoms; QgsFeatureIterator it = maskSource->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QList< int >() ).setDestinationCrs( featureSource->sourceCrs(), context.transformContext() ) ); QgsFeature f; while ( it.nextFeature( f ) ) { if ( f.hasGeometry() ) clipGeoms << f.geometry(); } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); if ( clipGeoms.isEmpty() ) return outputs; // are we clipping against a single feature? if so, we can show finer progress reports bool singleClipFeature = false; QgsGeometry combinedClipGeom; if ( clipGeoms.length() > 1 ) { combinedClipGeom = QgsGeometry::unaryUnion( clipGeoms ); if ( combinedClipGeom.isEmpty() ) { throw QgsProcessingException( QObject::tr( "Could not create the combined clip geometry: %1" ).arg( combinedClipGeom.lastError() ) ); } singleClipFeature = false; } else { combinedClipGeom = clipGeoms.at( 0 ); singleClipFeature = true; } // use prepared geometries for faster intersection tests std::unique_ptr< QgsGeometryEngine > engine( QgsGeometry::createGeometryEngine( combinedClipGeom.constGet() ) ); engine->prepareGeometry(); QgsFeatureIds testedFeatureIds; int i = -1; Q_FOREACH ( const QgsGeometry &clipGeom, clipGeoms ) { i++; if ( feedback->isCanceled() ) { break; } QgsFeatureIterator inputIt = featureSource->getFeatures( QgsFeatureRequest().setFilterRect( clipGeom.boundingBox() ) ); QgsFeatureList inputFeatures; QgsFeature f; while ( inputIt.nextFeature( f ) ) inputFeatures << f; if ( inputFeatures.isEmpty() ) continue; double step = 0; if ( singleClipFeature ) step = 100.0 / inputFeatures.length(); int current = 0; Q_FOREACH ( const QgsFeature &inputFeature, inputFeatures ) { if ( feedback->isCanceled() ) { break; } if ( !inputFeature.hasGeometry() ) continue; if ( testedFeatureIds.contains( inputFeature.id() ) ) { // don't retest a feature we have already checked continue; } testedFeatureIds.insert( inputFeature.id() ); if ( !engine->intersects( inputFeature.geometry().constGet() ) ) continue; QgsGeometry newGeometry; if ( !engine->contains( inputFeature.geometry().constGet() ) ) { QgsGeometry currentGeometry = inputFeature.geometry(); newGeometry = combinedClipGeom.intersection( currentGeometry ); if ( newGeometry.wkbType() == QgsWkbTypes::Unknown || QgsWkbTypes::flatType( newGeometry.wkbType() ) == QgsWkbTypes::GeometryCollection ) { QgsGeometry intCom = inputFeature.geometry().combine( newGeometry ); QgsGeometry intSym = inputFeature.geometry().symDifference( newGeometry ); newGeometry = intCom.difference( intSym ); } } else { // clip geometry totally contains feature geometry, so no need to perform intersection newGeometry = inputFeature.geometry(); } if ( !QgsOverlayUtils::sanitizeIntersectionResult( newGeometry, sinkType ) ) continue; QgsFeature outputFeature; outputFeature.setGeometry( newGeometry ); outputFeature.setAttributes( inputFeature.attributes() ); sink->addFeature( outputFeature, QgsFeatureSink::FastInsert ); if ( singleClipFeature ) feedback->setProgress( current * step ); } if ( !singleClipFeature ) { // coarse progress report for multiple clip geometries feedback->setProgress( 100.0 * static_cast< double >( i ) / clipGeoms.length() ); } }
QgsGeometry* QgsTransectSample::clipBufferLine( const QgsGeometry& stratumGeom, QgsGeometry* clippedBaseline, double tolerance ) { if ( !stratumGeom || !clippedBaseline || clippedBaseline->wkbType() == QgsWkbTypes::Unknown ) { return nullptr; } QgsGeometry usedBaseline = *clippedBaseline; if ( mBaselineSimplificationTolerance >= 0 ) { //int verticesBefore = usedBaseline->asMultiPolyline().count(); usedBaseline = clippedBaseline->simplify( mBaselineSimplificationTolerance ); if ( usedBaseline.isEmpty() ) { return nullptr; } //int verticesAfter = usedBaseline->asMultiPolyline().count(); //debug: write to file /*QgsVectorFileWriter debugWriter( "/tmp/debug.shp", "utf-8", QgsFields(), QgsWkbTypes::LineString, &( mStrataLayer->crs() ) ); QgsFeature debugFeature; debugFeature.setGeometry( usedBaseline ); debugWriter.addFeature( debugFeature );*/ } double currentBufferDist = tolerance; int maxLoops = 10; for ( int i = 0; i < maxLoops; ++i ) { //loop with tolerance: create buffer, convert buffer to line, clip line by stratum, test if result is (single) line QgsGeometry clipBaselineBuffer = usedBaseline.buffer( currentBufferDist, 8 ); if ( clipBaselineBuffer.isEmpty() ) { continue; } //it is also possible that clipBaselineBuffer is a multipolygon QgsGeometry bufferLine; //buffer line or multiline QgsGeometry bufferLineClipped; QgsMultiPolyline mpl; if ( clipBaselineBuffer.isMultipart() ) { QgsMultiPolygon bufferMultiPolygon = clipBaselineBuffer.asMultiPolygon(); if ( bufferMultiPolygon.size() < 1 ) { continue; } for ( int j = 0; j < bufferMultiPolygon.size(); ++j ) { int size = bufferMultiPolygon.at( j ).size(); for ( int k = 0; k < size; ++k ) { mpl.append( bufferMultiPolygon.at( j ).at( k ) ); } } bufferLine = QgsGeometry::fromMultiPolyline( mpl ); } else { QgsPolygon bufferPolygon = clipBaselineBuffer.asPolygon(); if ( bufferPolygon.size() < 1 ) { continue; } int size = bufferPolygon.size(); mpl.reserve( size ); for ( int j = 0; j < size; ++j ) { mpl.append( bufferPolygon[j] ); } bufferLine = QgsGeometry::fromMultiPolyline( mpl ); } bufferLineClipped = bufferLine.intersection( stratumGeom ); if ( bufferLineClipped.isEmpty() && bufferLineClipped.type() == QgsWkbTypes::LineGeometry ) { //if stratumGeom is a multipolygon, bufferLineClipped must intersect each part bool bufferLineClippedIntersectsStratum = true; if ( stratumGeom.wkbType() == QgsWkbTypes::MultiPolygon || stratumGeom.wkbType() == QgsWkbTypes::MultiPolygon25D ) { QVector<QgsPolygon> multiPoly = stratumGeom.asMultiPolygon(); QVector<QgsPolygon>::const_iterator multiIt = multiPoly.constBegin(); for ( ; multiIt != multiPoly.constEnd(); ++multiIt ) { QgsGeometry poly = QgsGeometry::fromPolygon( *multiIt ); if ( !poly.intersects( bufferLineClipped ) ) { bufferLineClippedIntersectsStratum = false; break; } } } if ( bufferLineClippedIntersectsStratum ) { return new QgsGeometry( bufferLineClipped ); } } currentBufferDist /= 2; } return nullptr; //no solution found even with reduced tolerances }
void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext& context ) { QString labelText; if ( formatNumbers == true && ( f.attributeMap()[fieldIndex].type() == QVariant::Int || f.attributeMap()[fieldIndex].type() == QVariant::Double ) ) { QString numberFormat; double d = f.attributeMap()[fieldIndex].toDouble(); if ( d > 0 && plusSign == true ) { numberFormat.append( "+" ); } numberFormat.append( "%1" ); labelText = numberFormat.arg( d, 0, 'f', decimals ); } else { labelText = f.attributeMap()[fieldIndex].toString(); } double labelX, labelY; // will receive label size QFont labelFont = textFont; //data defined label size? QMap< DataDefinedProperties, int >::const_iterator it = dataDefinedProperties.find( QgsPalLayerSettings::Size ); if ( it != dataDefinedProperties.constEnd() ) { //find out size QVariant size = f.attributeMap().value( *it ); if ( size.isValid() ) { double sizeDouble = size.toDouble(); if ( sizeDouble <= 0 ) { return; } labelFont.setPixelSize( sizeToPixel( sizeDouble, context ) ); } QFontMetricsF labelFontMetrics( labelFont ); calculateLabelSize( &labelFontMetrics, labelText, labelX, labelY ); } else { calculateLabelSize( fontMetrics, labelText, labelX, labelY ); } QgsGeometry* geom = f.geometry(); if ( ct ) // reproject the geometry if necessary geom->transform( *ct ); if ( !checkMinimumSizeMM( context, geom, minFeatureSize ) ) { return; } // CLIP the geometry if it is bigger than the extent QgsGeometry* geomClipped = NULL; GEOSGeometry* geos_geom; bool do_clip = !extentGeom->contains( geom ); if ( do_clip ) { geomClipped = geom->intersection( extentGeom ); // creates new geometry geos_geom = geomClipped->asGeos(); } else { geos_geom = geom->asGeos(); } if ( geos_geom == NULL ) return; // invalid geometry GEOSGeometry* geos_geom_clone = GEOSGeom_clone( geos_geom ); if ( do_clip ) delete geomClipped; //data defined position / alignment / rotation? bool dataDefinedPosition = false; bool dataDefinedRotation = false; double xPos = 0.0, yPos = 0.0, angle = 0.0; bool ddXPos, ddYPos; QMap< DataDefinedProperties, int >::const_iterator dPosXIt = dataDefinedProperties.find( QgsPalLayerSettings::PositionX ); if ( dPosXIt != dataDefinedProperties.constEnd() ) { QMap< DataDefinedProperties, int >::const_iterator dPosYIt = dataDefinedProperties.find( QgsPalLayerSettings::PositionY ); if ( dPosYIt != dataDefinedProperties.constEnd() ) { //data defined position. But field values could be NULL -> positions will be generated by PAL xPos = f.attributeMap().value( *dPosXIt ).toDouble( &ddXPos ); yPos = f.attributeMap().value( *dPosYIt ).toDouble( &ddYPos ); if ( ddXPos && ddYPos ) { dataDefinedPosition = true; //x/y shift in case of alignment double xdiff = 0; double ydiff = 0; //horizontal alignment QMap< DataDefinedProperties, int >::const_iterator haliIt = dataDefinedProperties.find( QgsPalLayerSettings::Hali ); if ( haliIt != dataDefinedProperties.end() ) { QString haliString = f.attributeMap().value( *haliIt ).toString(); if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 ) { xdiff -= labelX / 2.0; } else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 ) { xdiff -= labelX; } } //vertical alignment QMap< DataDefinedProperties, int >::const_iterator valiIt = dataDefinedProperties.find( QgsPalLayerSettings::Vali ); if ( valiIt != dataDefinedProperties.constEnd() ) { QString valiString = f.attributeMap().value( *valiIt ).toString(); if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 ) { if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 || valiString.compare( "Cap", Qt::CaseInsensitive ) == 0 ) { ydiff -= labelY; } else { QFontMetrics labelFontMetrics( labelFont ); double descentRatio = labelFontMetrics.descent() / labelFontMetrics.height(); if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 ) { ydiff -= labelY * descentRatio; } else if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 ) { ydiff -= labelY * descentRatio; ydiff -= labelY * 0.5 * ( 1 - descentRatio ); } } } } //data defined rotation? QMap< DataDefinedProperties, int >::const_iterator rotIt = dataDefinedProperties.find( QgsPalLayerSettings::Rotation ); if ( rotIt != dataDefinedProperties.constEnd() ) { dataDefinedRotation = true; angle = f.attributeMap().value( *rotIt ).toDouble() * M_PI / 180; //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center double xd = xdiff * cos( angle ) - ydiff * sin( angle ); double yd = xdiff * sin( angle ) + ydiff * cos( angle ); xdiff = xd; ydiff = yd; } //project xPos and yPos from layer to map CRS double z = 0; if ( ct ) { ct->transformInPlace( xPos, yPos, z ); } yPos += ydiff; xPos += xdiff; } } } QgsPalGeometry* lbl = new QgsPalGeometry( f.id(), labelText, geos_geom_clone ); // record the created geometry - it will be deleted at the end. geometries.append( lbl ); // register feature to the layer try { if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData(), xPos, yPos, dataDefinedPosition, angle, dataDefinedRotation ) ) return; } catch ( std::exception &e ) { Q_UNUSED( e ); QgsDebugMsg( QString( "Ignoring feature %1 due PAL exception: " ).arg( f.id() ) + QString::fromLatin1( e.what() ) ); return; } // TODO: only for placement which needs character info pal::Feature* feat = palLayer->getFeature( lbl->strId() ); feat->setLabelInfo( lbl->info( fontMetrics, xform, rasterCompressFactor ) ); // TODO: allow layer-wide feature dist in PAL...? //data defined label-feature distance? double distance = dist; QMap< DataDefinedProperties, int >::const_iterator dDistIt = dataDefinedProperties.find( QgsPalLayerSettings::LabelDistance ); if ( dDistIt != dataDefinedProperties.constEnd() ) { distance = f.attributeMap().value( *dDistIt ).toDouble(); } if ( distance != 0 ) { if ( distInMapUnits ) //convert distance from mm/map units to pixels { distance /= context.mapToPixel().mapUnitsPerPixel(); } else //mm { distance *= vectorScaleFactor; } feat->setDistLabel( qAbs( ptOne.x() - ptZero.x() )* distance ); } //add parameters for data defined labeling to QgsPalGeometry QMap< DataDefinedProperties, int >::const_iterator dIt = dataDefinedProperties.constBegin(); for ( ; dIt != dataDefinedProperties.constEnd(); ++dIt ) { lbl->addDataDefinedValue( dIt.key(), f.attributeMap()[dIt.value()] ); } }