QgsFeature QgsConvexHullAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback *feedback ) { QgsFeature f = feature; if ( f.hasGeometry() ) { QgsGeometry outputGeometry = f.geometry().convexHull(); if ( !outputGeometry ) feedback->reportError( outputGeometry.lastError() ); f.setGeometry( outputGeometry ); if ( outputGeometry ) { QgsAttributes attrs = f.attributes(); attrs << outputGeometry.constGet()->area() << outputGeometry.constGet()->perimeter(); f.setAttributes( attrs ); } else { QgsAttributes attrs = f.attributes(); attrs << QVariant() << QVariant(); f.setAttributes( attrs ); } } return f; }
QgsFeatureList QgsConvexHullAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback *feedback ) { QgsFeature f = feature; if ( f.hasGeometry() ) { QgsGeometry outputGeometry; if ( QgsWkbTypes::flatType( f.geometry().wkbType() ) == QgsWkbTypes::Point ) { feedback->reportError( QObject::tr( "Cannot calculate convex hull for a single Point feature (try 'Minimum bounding geometry' algorithm instead)." ) ); f.clearGeometry(); } else { outputGeometry = f.geometry().convexHull(); if ( outputGeometry.isNull() ) feedback->reportError( outputGeometry.lastError() ); f.setGeometry( outputGeometry ); } if ( !outputGeometry.isNull() ) { QgsAttributes attrs = f.attributes(); attrs << outputGeometry.constGet()->area() << outputGeometry.constGet()->perimeter(); f.setAttributes( attrs ); } else { QgsAttributes attrs = f.attributes(); attrs << QVariant() << QVariant(); f.setAttributes( attrs ); } } return QgsFeatureList() << f; }
Qt3DRender::QGeometryRenderer *QgsLine3DSymbolEntityNode::renderer( const Qgs3DMapSettings &map, const QgsLine3DSymbol &symbol, const QgsVectorLayer *layer, const QgsFeatureRequest &request ) { QgsPointXY origin( map.origin().x(), map.origin().y() ); // TODO: configurable int nSegments = 4; QgsGeometry::EndCapStyle endCapStyle = QgsGeometry::CapRound; QgsGeometry::JoinStyle joinStyle = QgsGeometry::JoinStyleRound; double mitreLimit = 0; QList<QgsPolygon *> polygons; QgsFeature f; QgsFeatureIterator fi = layer->getFeatures( request ); while ( fi.nextFeature( f ) ) { if ( f.geometry().isNull() ) continue; QgsGeometry geom = f.geometry(); // segmentize curved geometries if necessary if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) ) geom = QgsGeometry( geom.constGet()->segmentize() ); const QgsAbstractGeometry *g = geom.constGet(); QgsGeos engine( g ); QgsAbstractGeometry *buffered = engine.buffer( symbol.width() / 2., nSegments, endCapStyle, joinStyle, mitreLimit ); // factory if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::Polygon ) { QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( buffered ); Qgs3DUtils::clampAltitudes( polyBuffered, symbol.altitudeClamping(), symbol.altitudeBinding(), symbol.height(), map ); polygons.append( polyBuffered ); } else if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::MultiPolygon ) { QgsMultiPolygon *mpolyBuffered = static_cast<QgsMultiPolygon *>( buffered ); for ( int i = 0; i < mpolyBuffered->numGeometries(); ++i ) { QgsAbstractGeometry *partBuffered = mpolyBuffered->geometryN( i ); Q_ASSERT( QgsWkbTypes::flatType( partBuffered->wkbType() ) == QgsWkbTypes::Polygon ); QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( partBuffered )->clone(); // need to clone individual geometry parts Qgs3DUtils::clampAltitudes( polyBuffered, symbol.altitudeClamping(), symbol.altitudeBinding(), symbol.height(), map ); polygons.append( polyBuffered ); } delete buffered; } } mGeometry = new QgsTessellatedPolygonGeometry; mGeometry->setPolygons( polygons, origin, symbol.extrusionHeight() ); Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer; renderer->setGeometry( mGeometry ); return renderer; }
void QgsGeometryHoleCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const { QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->get( error->featureId(), feature ) ) { error->setObsolete(); return; } QgsGeometry featureGeom = feature.geometry(); const QgsAbstractGeometry *geom = featureGeom.constGet(); QgsVertexId vidx = error->vidx(); // Check if ring still exists if ( !vidx.isValid( geom ) ) { error->setObsolete(); return; } // Fix error if ( method == NoChange ) { error->setFixed( method ); } else if ( method == RemoveHoles ) { deleteFeatureGeometryRing( error->layerId(), feature, vidx.part, vidx.ring, changes ); error->setFixed( method ); } else { error->setFixFailed( tr( "Unknown method" ) ); } }
void QgsBufferedLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context ) { if ( f.geometry().isNull() ) return; LineData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal; QgsGeometry geom = f.geometry(); // segmentize curved geometries if necessary if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) ) geom = QgsGeometry( geom.constGet()->segmentize() ); const QgsAbstractGeometry *g = geom.constGet(); // TODO: configurable const int nSegments = 4; const QgsGeometry::EndCapStyle endCapStyle = QgsGeometry::CapRound; const QgsGeometry::JoinStyle joinStyle = QgsGeometry::JoinStyleRound; const double mitreLimit = 0; QgsGeos engine( g ); QgsAbstractGeometry *buffered = engine.buffer( mSymbol.width() / 2., nSegments, endCapStyle, joinStyle, mitreLimit ); // factory if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::Polygon ) { QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( buffered ); Qgs3DUtils::clampAltitudes( polyBuffered, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), context.map() ); out.polygons.append( polyBuffered ); out.fids.append( f.id() ); } else if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::MultiPolygon ) { QgsMultiPolygon *mpolyBuffered = static_cast<QgsMultiPolygon *>( buffered ); for ( int i = 0; i < mpolyBuffered->numGeometries(); ++i ) { QgsAbstractGeometry *partBuffered = mpolyBuffered->geometryN( i ); Q_ASSERT( QgsWkbTypes::flatType( partBuffered->wkbType() ) == QgsWkbTypes::Polygon ); QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( partBuffered )->clone(); // need to clone individual geometry parts Qgs3DUtils::clampAltitudes( polyBuffered, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), context.map() ); out.polygons.append( polyBuffered ); out.fids.append( f.id() ); } delete buffered; } }
void QgsGeometryAreaCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> &mergeAttributeIndices, Changes &changes ) const { QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->get( error->featureId(), feature ) ) { error->setObsolete(); return; } double layerToMapUnits = featurePool->getLayerToMapUnits(); QgsGeometry g = feature.geometry(); const QgsAbstractGeometry *geom = g.constGet(); QgsVertexId vidx = error->vidx(); // Check if polygon still exists if ( !vidx.isValid( geom ) ) { error->setObsolete(); return; } // Check if error still applies double value; if ( !checkThreshold( layerToMapUnits, QgsGeometryCheckerUtils::getGeomPart( geom, vidx.part ), value ) ) { error->setObsolete(); return; } // Fix with selected method if ( method == NoChange ) { error->setFixed( method ); } else if ( method == Delete ) { deleteFeatureGeometryPart( error->layerId(), feature, vidx.part, changes ); error->setFixed( method ); } else if ( method == MergeLongestEdge || method == MergeLargestArea || method == MergeIdenticalAttribute ) { QString errMsg; if ( mergeWithNeighbor( error->layerId(), feature, vidx.part, method, mergeAttributeIndices[error->layerId()], changes, errMsg ) ) { error->setFixed( method ); } else { error->setFixFailed( tr( "Failed to merge with neighbor: %1" ).arg( errMsg ) ); } } else { error->setFixFailed( tr( "Unknown method" ) ); } }
QList<QgsSingleGeometryCheckError *> QgsGeometryMultipartCheck::processGeometry( const QgsGeometry &geometry ) const { QList<QgsSingleGeometryCheckError *> errors; const QgsAbstractGeometry *geom = geometry.constGet(); QgsWkbTypes::Type type = geom->wkbType(); if ( geom->partCount() == 1 && QgsWkbTypes::isMultiType( type ) ) { errors.append( new QgsSingleGeometryCheckError( this, geometry, geometry ) ); } return errors; }
void QgsExpressionSelectionDialog::mButtonZoomToFeatures_clicked() { if ( mExpressionBuilder->expressionText().isEmpty() || !mMapCanvas ) return; QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( mLayer ) ); QgsFeatureRequest request = QgsFeatureRequest().setFilterExpression( mExpressionBuilder->expressionText() ) .setExpressionContext( context ) .setNoAttributes(); QgsFeatureIterator features = mLayer->getFeatures( request ); QgsRectangle bbox; bbox.setMinimal(); QgsFeature feat; int featureCount = 0; while ( features.nextFeature( feat ) ) { QgsGeometry geom = feat.geometry(); if ( geom.isNull() || geom.constGet()->isEmpty() ) continue; QgsRectangle r = mMapCanvas->mapSettings().layerExtentToOutputExtent( mLayer, geom.boundingBox() ); bbox.combineExtentWith( r ); featureCount++; } features.close(); QgsSettings settings; int timeout = settings.value( QStringLiteral( "qgis/messageTimeout" ), 5 ).toInt(); if ( featureCount > 0 ) { mMapCanvas->zoomToFeatureExtent( bbox ); if ( mMessageBar ) { mMessageBar->pushMessage( QString(), tr( "Zoomed to %n matching feature(s)", "number of matching features", featureCount ), Qgis::Info, timeout ); } } else if ( mMessageBar ) { mMessageBar->pushMessage( QString(), tr( "No matching features found" ), Qgis::Info, timeout ); } saveRecent(); }
QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId featureId, int vertex ) { if ( !mLayer->isSpatial() ) return QgsVectorLayer::InvalidLayer; QgsFeature f; if ( !mLayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() ) return QgsVectorLayer::FetchFeatureFailed; // geometry not found QgsGeometry geometry = f.geometry(); if ( !geometry.deleteVertex( vertex ) ) return QgsVectorLayer::EditFailed; if ( geometry.constGet() && geometry.constGet()->nCoordinates() == 0 ) { //last vertex deleted, set geometry to null geometry.set( nullptr ); } mLayer->editBuffer()->changeGeometry( featureId, geometry ); return !geometry.isNull() ? QgsVectorLayer::Success : QgsVectorLayer::EmptyGeometry; }
void QgsGeometryMultipartCheck::fixError( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const { QgsFeaturePool *featurePool = featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->getFeature( error->featureId(), feature ) ) { error->setObsolete(); return; } QgsGeometry featureGeom = feature.geometry(); const QgsAbstractGeometry *geom = featureGeom.constGet(); // Check if error still applies if ( geom->partCount() > 1 || !QgsWkbTypes::isMultiType( geom->wkbType() ) ) { error->setObsolete(); return; } // Fix error if ( method == NoChange ) { error->setFixed( method ); } else if ( method == ConvertToSingle ) { feature.setGeometry( QgsGeometry( QgsGeometryCheckerUtils::getGeomPart( geom, 0 )->clone() ) ); featurePool->updateFeature( feature ); error->setFixed( method ); changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); } else if ( method == RemoveObject ) { featurePool->deleteFeature( feature.id() ); error->setFixed( method ); changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeRemoved ) ); } else { error->setFixFailed( tr( "Unknown method" ) ); } }
QgsFeatureList QgsBoundaryAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback *feedback ) { QgsFeature outFeature = feature; if ( feature.hasGeometry() ) { QgsGeometry inputGeometry = feature.geometry(); QgsGeometry outputGeometry = QgsGeometry( inputGeometry.constGet()->boundary() ); if ( !outputGeometry ) { feedback->reportError( QObject::tr( "No boundary for feature %1 (possibly a closed linestring?)'" ).arg( feature.id() ) ); outFeature.clearGeometry(); } else { outFeature.setGeometry( outputGeometry ); } } return QgsFeatureList() << outFeature; }
void QgsSimpleLine3DSymbolHandler::processFeature( QgsFeature &f, const Qgs3DRenderContext &context ) { if ( f.geometry().isNull() ) return; LineData &out = mSelectedIds.contains( f.id() ) ? outSelected : outNormal; QgsPoint centroid; if ( mSymbol.altitudeBinding() == Qgs3DTypes::AltBindCentroid ) centroid = QgsPoint( f.geometry().centroid().asPoint() ); QgsGeometry geom = f.geometry(); const QgsAbstractGeometry *g = geom.constGet(); if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) ) { for ( int i = 0; i < ls->vertexCount(); ++i ) { QgsPoint p = ls->pointN( i ); float z = Qgs3DUtils::clampAltitude( p, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), centroid, context.map() ); out.vertices << QVector3D( p.x() - context.map().origin().x(), z, -( p.y() - context.map().origin().y() ) ); out.indexes << out.vertices.count() - 1; } } else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) ) { for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom ) { const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( mls->geometryN( nGeom ) ); for ( int i = 0; i < ls->vertexCount(); ++i ) { QgsPoint p = ls->pointN( i ); float z = Qgs3DUtils::clampAltitude( p, mSymbol.altitudeClamping(), mSymbol.altitudeBinding(), mSymbol.height(), centroid, context.map() ); out.vertices << QVector3D( p.x() - context.map().origin().x(), z, -( p.y() - context.map().origin().y() ) ); out.indexes << out.vertices.count() - 1; } out.indexes << 0; // add primitive restart } } out.indexes << 0; // add primitive restart }
QgsFeatureList QgsLineSubstringAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback * ) { QgsFeature f = feature; if ( f.hasGeometry() ) { const QgsGeometry geometry = f.geometry(); double startDistance = mStartDistance; if ( mDynamicStartDistance ) startDistance = mStartDistanceProperty.valueAsDouble( context.expressionContext(), startDistance ); double endDistance = mEndDistance; if ( mDynamicEndDistance ) endDistance = mEndDistanceProperty.valueAsDouble( context.expressionContext(), endDistance ); const QgsCurve *curve = nullptr; if ( !geometry.isMultipart() ) curve = qgsgeometry_cast< const QgsCurve * >( geometry.constGet() ); else { if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geometry.constGet() ) ) { if ( collection->numGeometries() > 0 ) { curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) ); } } } if ( curve ) { std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) ); QgsGeometry result( std::move( substring ) ); f.setGeometry( result ); } else { f.clearGeometry(); } } return QgsFeatureList() << f; }
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; }
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; }
QgsInternalGeometryEngine::QgsInternalGeometryEngine( const QgsGeometry &geometry ) : mGeometry( geometry.constGet() ) { }
QString QgsExpression::formatPreviewString( const QVariant &value ) { static const int MAX_PREVIEW = 60; if ( value.canConvert<QgsGeometry>() ) { //result is a geometry QgsGeometry geom = value.value<QgsGeometry>(); if ( geom.isNull() ) return tr( "<i><empty geometry></i>" ); else return tr( "<i><geometry: %1></i>" ).arg( QgsWkbTypes::displayString( geom.constGet()->wkbType() ) ); } else if ( !value.isValid() ) { return tr( "<i>NULL</i>" ); } else if ( value.canConvert< QgsFeature >() ) { //result is a feature QgsFeature feat = value.value<QgsFeature>(); return tr( "<i><feature: %1></i>" ).arg( feat.id() ); } else if ( value.canConvert< QgsInterval >() ) { //result is a feature QgsInterval interval = value.value<QgsInterval>(); return tr( "<i><interval: %1 days></i>" ).arg( interval.days() ); } else if ( value.canConvert< QgsGradientColorRamp >() ) { return tr( "<i><gradient ramp></i>" ); } else if ( value.type() == QVariant::Date ) { QDate dt = value.toDate(); return tr( "<i><date: %1></i>" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd" ) ) ); } else if ( value.type() == QVariant::Time ) { QTime tm = value.toTime(); return tr( "<i><time: %1></i>" ).arg( tm.toString( QStringLiteral( "hh:mm:ss" ) ) ); } else if ( value.type() == QVariant::DateTime ) { QDateTime dt = value.toDateTime(); return tr( "<i><datetime: %1></i>" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd hh:mm:ss" ) ) ); } else if ( value.type() == QVariant::String ) { QString previewString = value.toString(); if ( previewString.length() > MAX_PREVIEW + 3 ) { return tr( "'%1…'" ).arg( previewString.left( MAX_PREVIEW ) ); } else { return previewString.prepend( '\'' ).append( '\'' ); } } else if ( value.type() == QVariant::Map ) { QString mapStr; const QVariantMap map = value.toMap(); for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it ) { if ( !mapStr.isEmpty() ) mapStr.append( ", " ); mapStr.append( it.key() ).append( ": " ).append( formatPreviewString( it.value() ) ); if ( mapStr.length() > MAX_PREVIEW + 3 ) { mapStr = QString( tr( "%1…" ) ).arg( mapStr.left( MAX_PREVIEW ) ); break; } } return tr( "<i><map: %1></i>" ).arg( mapStr ); } else if ( value.type() == QVariant::List || value.type() == QVariant::StringList ) { QString listStr; const QVariantList list = value.toList(); for ( QVariantList::const_iterator it = list.constBegin(); it != list.constEnd(); ++it ) { if ( !listStr.isEmpty() ) listStr.append( ", " ); listStr.append( formatPreviewString( *it ) ); if ( listStr.length() > MAX_PREVIEW + 3 ) { listStr = QString( tr( "%1…" ) ).arg( listStr.left( MAX_PREVIEW ) ); break; } } return tr( "<i><array: %1></i>" ).arg( listStr ); } else { return value.toString(); } }
Qt3DRender::QGeometryRenderer *QgsLine3DSymbolEntityNode::rendererSimple( const Qgs3DMapSettings &map, const QgsLine3DSymbol &symbol, const QgsVectorLayer *layer, const QgsFeatureRequest &request ) { QVector<QVector3D> vertices; vertices << QVector3D(); // the first index is invalid, we use it for primitive restart QVector<unsigned int> indexes; QgsPoint centroid; QgsPointXY origin( map.origin().x(), map.origin().y() ); QgsFeature f; QgsFeatureIterator fi = layer->getFeatures( request ); while ( fi.nextFeature( f ) ) { if ( f.geometry().isNull() ) continue; if ( symbol.altitudeBinding() == AltBindCentroid ) centroid = QgsPoint( f.geometry().centroid().asPoint() ); QgsGeometry geom = f.geometry(); const QgsAbstractGeometry *g = geom.constGet(); if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( g ) ) { for ( int i = 0; i < ls->vertexCount(); ++i ) { QgsPoint p = ls->pointN( i ); float z = Qgs3DUtils::clampAltitude( p, symbol.altitudeClamping(), symbol.altitudeBinding(), symbol.height(), centroid, map ); vertices << QVector3D( p.x() - map.origin().x(), z, -( p.y() - map.origin().y() ) ); indexes << vertices.count() - 1; } } else if ( const QgsMultiLineString *mls = qgsgeometry_cast<const QgsMultiLineString *>( g ) ) { for ( int nGeom = 0; nGeom < mls->numGeometries(); ++nGeom ) { const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( mls->geometryN( nGeom ) ); for ( int i = 0; i < ls->vertexCount(); ++i ) { QgsPoint p = ls->pointN( i ); float z = Qgs3DUtils::clampAltitude( p, symbol.altitudeClamping(), symbol.altitudeBinding(), symbol.height(), centroid, map ); vertices << QVector3D( p.x() - map.origin().x(), z, -( p.y() - map.origin().y() ) ); indexes << vertices.count() - 1; } indexes << 0; // add primitive restart } } indexes << 0; // add primitive restart } QByteArray vertexBufferData; vertexBufferData.resize( vertices.size() * 3 * sizeof( float ) ); float *rawVertexArray = reinterpret_cast<float *>( vertexBufferData.data() ); int idx = 0; for ( const auto &v : vertices ) { rawVertexArray[idx++] = v.x(); rawVertexArray[idx++] = v.y(); rawVertexArray[idx++] = v.z(); } QByteArray indexBufferData; indexBufferData.resize( indexes.size() * sizeof( int ) ); unsigned int *rawIndexArray = reinterpret_cast<unsigned int *>( indexBufferData.data() ); idx = 0; for ( unsigned int indexVal : indexes ) { rawIndexArray[idx++] = indexVal; } Qt3DRender::QBuffer *vertexBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::VertexBuffer, this ); vertexBuffer->setData( vertexBufferData ); Qt3DRender::QBuffer *indexBuffer = new Qt3DRender::QBuffer( Qt3DRender::QBuffer::IndexBuffer, this ); indexBuffer->setData( indexBufferData ); Qt3DRender::QAttribute *positionAttribute = new Qt3DRender::QAttribute( this ); positionAttribute->setAttributeType( Qt3DRender::QAttribute::VertexAttribute ); positionAttribute->setBuffer( vertexBuffer ); positionAttribute->setVertexBaseType( Qt3DRender::QAttribute::Float ); positionAttribute->setVertexSize( 3 ); positionAttribute->setName( Qt3DRender::QAttribute::defaultPositionAttributeName() ); Qt3DRender::QAttribute *indexAttribute = new Qt3DRender::QAttribute( this ); indexAttribute->setAttributeType( Qt3DRender::QAttribute::IndexAttribute ); indexAttribute->setBuffer( indexBuffer ); indexAttribute->setVertexBaseType( Qt3DRender::QAttribute::UnsignedInt ); Qt3DRender::QGeometry *geom = new Qt3DRender::QGeometry; geom->addAttribute( positionAttribute ); geom->addAttribute( indexAttribute ); Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer; renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::LineStrip ); renderer->setGeometry( geom ); renderer->setVertexCount( vertices.count() ); renderer->setPrimitiveRestartEnabled( true ); renderer->setRestartIndexValue( 0 ); return renderer; }
void QgsMapToolAddPart::cadCanvasReleaseEvent( QgsMapMouseEvent *e ) { //check if we operate on a vector layer QgsVectorLayer *vlayer = currentVectorLayer(); if ( !vlayer ) { notifyNotVectorLayer(); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } bool isGeometryEmpty = false; QgsFeatureList selectedFeatures = vlayer->selectedFeatures(); if ( !selectedFeatures.isEmpty() && selectedFeatures.at( 0 ).geometry().isNull() ) isGeometryEmpty = true; if ( !checkSelection() ) { stopCapturing(); return; } int errorCode = 0; switch ( mode() ) { case CapturePoint: { QgsPoint layerPoint; QgsPointXY mapPoint = e->mapPoint(); if ( nextPoint( QgsPoint( mapPoint ), layerPoint ) != 0 ) { QgsDebugMsg( "nextPoint failed" ); return; } vlayer->beginEditCommand( tr( "Part added" ) ); errorCode = vlayer->addPart( QgsPointSequence() << layerPoint ); } break; case CaptureLine: case CapturePolygon: { //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->mapPoint(), e->mapPointMatch() ); if ( error == 1 ) { QgsDebugMsg( "current layer is not a vector layer" ); return; } else if ( error == 2 ) { //problem with coordinate transformation emit messageEmitted( tr( "Coordinate transform error. Cannot transform the point to the layers coordinate system" ), Qgis::Warning ); return; } startCapturing(); return; } else if ( e->button() != Qt::RightButton ) { deleteTempRubberBand(); return; } if ( !isCapturing() ) return; if ( mode() == CapturePolygon ) { closePolygon(); } //does compoundcurve contain circular strings? //does provider support circular strings? bool hasCurvedSegments = captureCurve()->hasCurvedSegments(); bool providerSupportsCurvedSegments = vlayer->dataProvider()->capabilities() & QgsVectorDataProvider::CircularGeometries; QgsCurve *curveToAdd = nullptr; if ( hasCurvedSegments && providerSupportsCurvedSegments ) { curveToAdd = captureCurve()->clone(); } else { curveToAdd = captureCurve()->curveToLine(); } vlayer->beginEditCommand( tr( "Part added" ) ); if ( mode() == CapturePolygon ) { //avoid intersections QgsCurvePolygon *cp = new QgsCurvePolygon(); cp->setExteriorRing( curveToAdd ); QgsGeometry *geom = new QgsGeometry( cp ); geom->avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() ); const QgsCurvePolygon *cpGeom = qgsgeometry_cast<const QgsCurvePolygon *>( geom->constGet() ); if ( !cpGeom ) { stopCapturing(); delete geom; vlayer->destroyEditCommand(); return; } errorCode = vlayer->addPart( cpGeom->exteriorRing()->clone() ); delete geom; } else { errorCode = vlayer->addPart( curveToAdd ); } stopCapturing(); } break; default: Q_ASSERT( !"invalid capture mode" ); errorCode = 6; break; } QString errorMessage; switch ( errorCode ) { case 0: { // remove previous message emit messageDiscarded(); //add points to other features to keep topology up-to-date bool topologicalEditing = QgsProject::instance()->topologicalEditing(); if ( topologicalEditing ) { addTopologicalPoints( points() ); } vlayer->endEditCommand(); vlayer->triggerRepaint(); if ( ( !isGeometryEmpty ) && QgsWkbTypes::isSingleType( vlayer->wkbType() ) ) { emit messageEmitted( tr( "Add part: Feature geom is single part and you've added more than one" ), Qgis::Warning ); } return; } case 1: errorMessage = tr( "Selected feature is not multi part." ); break; case 2: errorMessage = tr( "New part's geometry is not valid." ); break; case 3: errorMessage = tr( "New polygon ring not disjoint with existing polygons." ); break; case 4: errorMessage = tr( "No feature selected. Please select a feature with the selection tool or in the attribute table" ); break; case 5: errorMessage = tr( "Several features are selected. Please select only one feature to which an island should be added." ); break; case 6: errorMessage = tr( "Selected geometry could not be found" ); break; } emit messageEmitted( errorMessage, Qgis::Warning ); vlayer->destroyEditCommand(); }
void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeight ) { if ( _minimum_distance_between_coordinates( polygon ) < 0.001 ) { // when the distances between coordinates of input points are very small, // the triangulation likes to crash on numerical errors - when the distances are ~ 1e-5 // Assuming that the coordinates should be in a projected CRS, we should be able // to simplify geometries that may cause problems and avoid possible crashes QgsGeometry polygonSimplified = QgsGeometry( polygon.clone() ).simplify( 0.001 ); const QgsPolygon *polygonSimplifiedData = qgsgeometry_cast<const QgsPolygon *>( polygonSimplified.constGet() ); if ( _minimum_distance_between_coordinates( *polygonSimplifiedData ) < 0.001 ) { // Failed to fix that. It could be a really tiny geometry... or maybe they gave us // geometry in unprojected lat/lon coordinates QgsMessageLog::logMessage( "geometry's coordinates are too close to each other and simplification failed - skipping", "3D" ); } else { addPolygon( *polygonSimplifiedData, extrusionHeight ); } return; } if ( !_check_intersecting_rings( polygon ) ) { // skip the polygon - it would cause a crash inside poly2tri library QgsMessageLog::logMessage( "polygon rings intersect each other - skipping", "3D" ); return; } const QgsCurve *exterior = polygon.exteriorRing(); QList< std::vector<p2t::Point *> > polylinesToDelete; QHash<p2t::Point *, float> z; std::vector<p2t::Point *> polyline; const QVector3D pNormal = _calculateNormal( exterior, mOriginX, mOriginY ); const int pCount = exterior->numPoints(); // Polygon is a triangle if ( pCount == 4 ) { QgsPoint pt; QgsVertexId::VertexType vt; for ( int i = 0; i < 3; i++ ) { exterior->pointAt( i, pt, vt ); mData << pt.x() - mOriginX << pt.z() << - pt.y() + mOriginY; if ( mAddNormals ) mData << pNormal.x() << pNormal.z() << - pNormal.y(); } } else { if ( !qgsDoubleNear( pNormal.length(), 1, 0.001 ) ) return; // this should not happen - pNormal should be normalized to unit length QVector3D pXVector, pYVector; _normalVectorToXYVectors( pNormal, pXVector, pYVector ); // so now we have three orthogonal unit vectors defining new base // let's build transform matrix. We actually need just a 3x3 matrix, // but Qt does not have good support for it, so using 4x4 matrix instead. QMatrix4x4 toNewBase( pXVector.x(), pXVector.y(), pXVector.z(), 0, pYVector.x(), pYVector.y(), pYVector.z(), 0, pNormal.x(), pNormal.y(), pNormal.z(), 0, 0, 0, 0, 0 ); // our 3x3 matrix is orthogonal, so for inverse we only need to transpose it QMatrix4x4 toOldBase = toNewBase.transposed(); const QgsPoint ptFirst( exterior->startPoint() ); _ringToPoly2tri( exterior, ptFirst, toNewBase, polyline, z ); polylinesToDelete << polyline; // TODO: robustness (no nearly duplicate points, invalid geometries ...) double x0 = ptFirst.x(), y0 = ptFirst.y(), z0 = ( std::isnan( ptFirst.z() ) ? 0 : ptFirst.z() ); if ( polyline.size() == 3 && polygon.numInteriorRings() == 0 ) { for ( std::vector<p2t::Point *>::iterator it = polyline.begin(); it != polyline.end(); it++ ) { p2t::Point *p = *it; QVector4D ptInNewBase( p->x, p->y, z[p], 0 ); QVector4D nPoint = toOldBase * ptInNewBase; const double fx = nPoint.x() - mOriginX + x0; const double fy = nPoint.y() - mOriginY + y0; const double fz = nPoint.z() + extrusionHeight + z0; mData << fx << fz << -fy; if ( mAddNormals ) mData << pNormal.x() << pNormal.z() << - pNormal.y(); } } else if ( polyline.size() >= 3 ) { p2t::CDT *cdt = new p2t::CDT( polyline ); // polygon holes for ( int i = 0; i < polygon.numInteriorRings(); ++i ) { std::vector<p2t::Point *> holePolyline; const QgsCurve *hole = polygon.interiorRing( i ); _ringToPoly2tri( hole, ptFirst, toNewBase, holePolyline, z ); cdt->AddHole( holePolyline ); polylinesToDelete << holePolyline; } try { cdt->Triangulate(); std::vector<p2t::Triangle *> triangles = cdt->GetTriangles(); for ( size_t i = 0; i < triangles.size(); ++i ) { p2t::Triangle *t = triangles[i]; for ( int j = 0; j < 3; ++j ) { p2t::Point *p = t->GetPoint( j ); QVector4D ptInNewBase( p->x, p->y, z[p], 0 ); QVector4D nPoint = toOldBase * ptInNewBase; const double fx = nPoint.x() - mOriginX + x0; const double fy = nPoint.y() - mOriginY + y0; const double fz = nPoint.z() + extrusionHeight + z0; mData << fx << fz << -fy; if ( mAddNormals ) mData << pNormal.x() << pNormal.z() << - pNormal.y(); } } } catch ( ... ) { QgsMessageLog::logMessage( "Triangulation failed. Skipping polygon...", "3D" ); } delete cdt; } for ( int i = 0; i < polylinesToDelete.count(); ++i ) qDeleteAll( polylinesToDelete[i] ); } // add walls if extrusion is enabled if ( extrusionHeight != 0 ) { _makeWalls( *exterior, false, extrusionHeight, mData, mAddNormals, mOriginX, mOriginY ); for ( int i = 0; i < polygon.numInteriorRings(); ++i ) _makeWalls( *polygon.interiorRing( i ), true, extrusionHeight, mData, mAddNormals, mOriginX, mOriginY ); } }
bool QgsGeometryAreaCheck::mergeWithNeighbor( const QString &layerId, QgsFeature &feature, int partIdx, int method, int mergeAttributeIndex, Changes &changes, QString &errMsg ) const { QgsFeaturePool *featurePool = mContext->featurePools[ layerId ]; double maxVal = 0.; QgsFeature mergeFeature; int mergePartIdx = -1; bool matchFound = false; QgsGeometry featureGeometry = feature.geometry(); const QgsAbstractGeometry *geom = featureGeometry.constGet(); // Search for touching neighboring geometries const QgsFeatureIds intersects = featurePool->getIntersects( featureGeometry.boundingBox() ); for ( QgsFeatureId testId : intersects ) { QgsFeature testFeature; if ( !featurePool->get( testId, testFeature ) ) { continue; } QgsGeometry testFeatureGeom = testFeature.geometry(); const QgsAbstractGeometry *testGeom = testFeatureGeom.constGet(); for ( int testPartIdx = 0, nTestParts = testGeom->partCount(); testPartIdx < nTestParts; ++testPartIdx ) { if ( testId == feature.id() && testPartIdx == partIdx ) { continue; } double len = QgsGeometryCheckerUtils::sharedEdgeLength( QgsGeometryCheckerUtils::getGeomPart( geom, partIdx ), QgsGeometryCheckerUtils::getGeomPart( testGeom, testPartIdx ), mContext->reducedTolerance ); if ( len > 0. ) { if ( method == MergeLongestEdge || method == MergeLargestArea ) { double val; if ( method == MergeLongestEdge ) { val = len; } else { if ( dynamic_cast<const QgsGeometryCollection *>( testGeom ) ) val = static_cast<const QgsGeometryCollection *>( testGeom )->geometryN( testPartIdx )->area(); else val = testGeom->area(); } if ( val > maxVal ) { maxVal = val; mergeFeature = testFeature; mergePartIdx = testPartIdx; } } else if ( method == MergeIdenticalAttribute ) { if ( testFeature.attribute( mergeAttributeIndex ) == feature.attribute( mergeAttributeIndex ) ) { mergeFeature = testFeature; mergePartIdx = testPartIdx; matchFound = true; break; } } } } if ( matchFound ) { break; } } if ( !matchFound && maxVal == 0. ) { return method == MergeIdenticalAttribute; } // Merge geometries QgsGeometry mergeFeatureGeom = mergeFeature.geometry(); const QgsAbstractGeometry *mergeGeom = mergeFeatureGeom.constGet(); std::unique_ptr<QgsGeometryEngine> geomEngine( QgsGeometryCheckerUtils::createGeomEngine( QgsGeometryCheckerUtils::getGeomPart( mergeGeom, mergePartIdx ), mContext->reducedTolerance ) ); QgsAbstractGeometry *combinedGeom = geomEngine->combine( QgsGeometryCheckerUtils::getGeomPart( geom, partIdx ), &errMsg ); if ( !combinedGeom || combinedGeom->isEmpty() || !QgsWkbTypes::isSingleType( combinedGeom->wkbType() ) ) { return false; } // Replace polygon in merge geometry if ( mergeFeature.id() == feature.id() && mergePartIdx > partIdx ) { --mergePartIdx; } replaceFeatureGeometryPart( layerId, mergeFeature, mergePartIdx, combinedGeom, changes ); // Remove polygon from source geometry deleteFeatureGeometryPart( layerId, feature, partIdx, changes ); return true; }
void QgsGeometryTypeCheck::fixError( QgsGeometryCheckError *error, int method, const QMap<QString, int> & /*mergeAttributeIndices*/, Changes &changes ) const { QgsFeaturePool *featurePool = mContext->featurePools[ error->layerId() ]; QgsFeature feature; if ( !featurePool->get( error->featureId(), feature ) ) { error->setObsolete(); return; } QgsGeometry featureGeom = feature.geometry(); const QgsAbstractGeometry *geom = featureGeom.constGet(); // Check if error still applies QgsWkbTypes::Type type = QgsWkbTypes::flatType( geom->wkbType() ); if ( ( mAllowedTypes & ( 1 << type ) ) != 0 ) { error->setObsolete(); return; } // Fix with selected method if ( method == NoChange ) { error->setFixed( method ); } else if ( method == Convert ) { // Check if corresponding single type is allowed if ( QgsWkbTypes::isMultiType( type ) && ( ( 1 << QgsWkbTypes::singleType( type ) ) & mAllowedTypes ) != 0 ) { // Explode multi-type feature into single-type features for ( int iPart = 1, nParts = geom->partCount(); iPart < nParts; ++iPart ) { QgsFeature newFeature; newFeature.setAttributes( feature.attributes() ); newFeature.setGeometry( QgsGeometry( QgsGeometryCheckerUtils::getGeomPart( geom, iPart )->clone() ) ); featurePool->addFeature( newFeature ); changes[error->layerId()][newFeature.id()].append( Change( ChangeFeature, ChangeAdded ) ); } // Recycle feature for part 0 feature.setGeometry( QgsGeometry( QgsGeometryCheckerUtils::getGeomPart( geom, 0 )->clone() ) ); featurePool->updateFeature( feature ); changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); } // Check if corresponding multi type is allowed else if ( QgsWkbTypes::isSingleType( type ) && ( ( 1 << QgsWkbTypes::multiType( type ) ) & mAllowedTypes ) != 0 ) { QgsGeometryCollection *geomCollection = nullptr; switch ( QgsWkbTypes::multiType( type ) ) { case QgsWkbTypes::MultiPoint: { geomCollection = new QgsMultiPoint(); break; } case QgsWkbTypes::MultiLineString: { geomCollection = new QgsMultiLineString(); break; } case QgsWkbTypes::MultiPolygon: { geomCollection = new QgsMultiPolygon(); break; } case QgsWkbTypes::MultiCurve: { geomCollection = new QgsMultiCurve(); break; } case QgsWkbTypes::MultiSurface: { geomCollection = new QgsMultiSurface(); break; } default: break; } if ( !geomCollection ) { error->setFixFailed( tr( "Unknown geometry type" ) ); } else { geomCollection->addGeometry( geom->clone() ); feature.setGeometry( QgsGeometry( geomCollection ) ); featurePool->updateFeature( feature ); changes[error->layerId()][feature.id()].append( Change( ChangeFeature, ChangeChanged ) ); } } // Delete feature else { featurePool->deleteFeature( feature.id() ); changes[error->layerId()][error->featureId()].append( Change( ChangeFeature, ChangeRemoved ) ); } error->setFixed( method ); } else if ( method == Delete ) { featurePool->deleteFeature( feature.id() ); error->setFixed( method ); changes[error->layerId()][error->featureId()].append( Change( ChangeFeature, ChangeRemoved ) ); } else { error->setFixFailed( tr( "Unknown method" ) ); } }
bool QgsGeometryGapCheck::mergeWithNeighbor( const QMap<QString, QgsFeaturePool *> &featurePools, QgsGeometryGapCheckError *err, Changes &changes, QString &errMsg ) const { double maxVal = 0.; QString mergeLayerId; QgsFeature mergeFeature; int mergePartIdx = -1; const QgsGeometry geometry = err->geometry(); const QgsAbstractGeometry *errGeometry = QgsGeometryCheckerUtils::getGeomPart( geometry.constGet(), 0 ); const auto layerIds = err->neighbors().keys(); // Search for touching neighboring geometries for ( const QString &layerId : layerIds ) { QgsFeaturePool *featurePool = featurePools.value( layerId ); std::unique_ptr<QgsAbstractGeometry> errLayerGeom( errGeometry->clone() ); QgsCoordinateTransform ct( featurePool->crs(), mContext->mapCrs, mContext->transformContext ); errLayerGeom->transform( ct, QgsCoordinateTransform::ReverseTransform ); const auto featureIds = err->neighbors().value( layerId ); for ( QgsFeatureId testId : featureIds ) { QgsFeature testFeature; if ( !featurePool->getFeature( testId, testFeature ) ) { continue; } QgsGeometry featureGeom = testFeature.geometry(); const QgsAbstractGeometry *testGeom = featureGeom.constGet(); for ( int iPart = 0, nParts = testGeom->partCount(); iPart < nParts; ++iPart ) { double len = QgsGeometryCheckerUtils::sharedEdgeLength( errLayerGeom.get(), QgsGeometryCheckerUtils::getGeomPart( testGeom, iPart ), mContext->reducedTolerance ); if ( len > maxVal ) { maxVal = len; mergeFeature = testFeature; mergePartIdx = iPart; mergeLayerId = layerId; } } } } if ( maxVal == 0. ) { return false; } // Merge geometries QgsFeaturePool *featurePool = featurePools[ mergeLayerId ]; std::unique_ptr<QgsAbstractGeometry> errLayerGeom( errGeometry->clone() ); QgsCoordinateTransform ct( featurePool->crs(), mContext->mapCrs, mContext->transformContext ); errLayerGeom->transform( ct, QgsCoordinateTransform::ReverseTransform ); QgsGeometry mergeFeatureGeom = mergeFeature.geometry(); const QgsAbstractGeometry *mergeGeom = mergeFeatureGeom.constGet(); std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( errLayerGeom.get(), mContext->reducedTolerance ); std::unique_ptr<QgsAbstractGeometry> combinedGeom( geomEngine->combine( QgsGeometryCheckerUtils::getGeomPart( mergeGeom, mergePartIdx ), &errMsg ) ); if ( !combinedGeom || combinedGeom->isEmpty() || !QgsWkbTypes::isSingleType( combinedGeom->wkbType() ) ) { return false; } // Add merged polygon to destination geometry replaceFeatureGeometryPart( featurePools, mergeLayerId, mergeFeature, mergePartIdx, combinedGeom.release(), changes ); return true; }
void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeight ) { const QgsCurve *exterior = polygon.exteriorRing(); const QVector3D pNormal = _calculateNormal( exterior, mOriginX, mOriginY, mInvertNormals ); const int pCount = exterior->numPoints(); if ( pCount == 4 && polygon.numInteriorRings() == 0 ) { // polygon is a triangle - write vertices to the output data array without triangulation QgsPoint pt; QgsVertexId::VertexType vt; for ( int i = 0; i < 3; i++ ) { exterior->pointAt( i, pt, vt ); mData << pt.x() - mOriginX << pt.z() << - pt.y() + mOriginY; if ( mAddNormals ) mData << pNormal.x() << pNormal.z() << - pNormal.y(); } if ( mAddBackFaces ) { // the same triangle with reversed order of coordinates and inverted normal for ( int i = 2; i >= 0; i-- ) { exterior->pointAt( i, pt, vt ); mData << pt.x() - mOriginX << pt.z() << - pt.y() + mOriginY; if ( mAddNormals ) mData << -pNormal.x() << -pNormal.z() << pNormal.y(); } } } else { if ( !qgsDoubleNear( pNormal.length(), 1, 0.001 ) ) return; // this should not happen - pNormal should be normalized to unit length std::unique_ptr<QMatrix4x4> toNewBase, toOldBase; if ( pNormal != QVector3D( 0, 0, 1 ) ) { // this is not a horizontal plane - need to reproject the polygon to a new base so that // we can do the triangulation in a plane QVector3D pXVector, pYVector; _normalVectorToXYVectors( pNormal, pXVector, pYVector ); // so now we have three orthogonal unit vectors defining new base // let's build transform matrix. We actually need just a 3x3 matrix, // but Qt does not have good support for it, so using 4x4 matrix instead. toNewBase.reset( new QMatrix4x4( pXVector.x(), pXVector.y(), pXVector.z(), 0, pYVector.x(), pYVector.y(), pYVector.z(), 0, pNormal.x(), pNormal.y(), pNormal.z(), 0, 0, 0, 0, 0 ) ); // our 3x3 matrix is orthogonal, so for inverse we only need to transpose it toOldBase.reset( new QMatrix4x4( toNewBase->transposed() ) ); } const QgsPoint ptStart( exterior->startPoint() ); const QgsPoint pt0( QgsWkbTypes::PointZ, ptStart.x(), ptStart.y(), std::isnan( ptStart.z() ) ? 0 : ptStart.z() ); // subtract ptFirst from geometry for better numerical stability in triangulation // and apply new 3D vector base if the polygon is not horizontal std::unique_ptr<QgsPolygon> polygonNew( _transform_polygon_to_new_base( polygon, pt0, toNewBase.get() ) ); if ( _minimum_distance_between_coordinates( *polygonNew ) < 0.001 ) { // when the distances between coordinates of input points are very small, // the triangulation likes to crash on numerical errors - when the distances are ~ 1e-5 // Assuming that the coordinates should be in a projected CRS, we should be able // to simplify geometries that may cause problems and avoid possible crashes QgsGeometry polygonSimplified = QgsGeometry( polygonNew->clone() ).simplify( 0.001 ); const QgsPolygon *polygonSimplifiedData = qgsgeometry_cast<const QgsPolygon *>( polygonSimplified.constGet() ); if ( _minimum_distance_between_coordinates( *polygonSimplifiedData ) < 0.001 ) { // Failed to fix that. It could be a really tiny geometry... or maybe they gave us // geometry in unprojected lat/lon coordinates QgsMessageLog::logMessage( QObject::tr( "geometry's coordinates are too close to each other and simplification failed - skipping" ), QObject::tr( "3D" ) ); return; } else { polygonNew.reset( polygonSimplifiedData->clone() ); } } if ( !_check_intersecting_rings( *polygonNew.get() ) ) { // skip the polygon - it would cause a crash inside poly2tri library QgsMessageLog::logMessage( QObject::tr( "polygon rings self-intersect or intersect each other - skipping" ), QObject::tr( "3D" ) ); return; } QList< std::vector<p2t::Point *> > polylinesToDelete; QHash<p2t::Point *, float> z; // polygon exterior std::vector<p2t::Point *> polyline; _ringToPoly2tri( polygonNew->exteriorRing(), polyline, z ); polylinesToDelete << polyline; std::unique_ptr<p2t::CDT> cdt( new p2t::CDT( polyline ) ); // polygon holes for ( int i = 0; i < polygonNew->numInteriorRings(); ++i ) { std::vector<p2t::Point *> holePolyline; const QgsCurve *hole = polygonNew->interiorRing( i ); _ringToPoly2tri( hole, holePolyline, z ); cdt->AddHole( holePolyline ); polylinesToDelete << holePolyline; } // run triangulation and write vertices to the output data array try { cdt->Triangulate(); std::vector<p2t::Triangle *> triangles = cdt->GetTriangles(); for ( size_t i = 0; i < triangles.size(); ++i ) { p2t::Triangle *t = triangles[i]; for ( int j = 0; j < 3; ++j ) { p2t::Point *p = t->GetPoint( j ); QVector4D pt( p->x, p->y, z[p], 0 ); if ( toOldBase ) pt = *toOldBase * pt; const double fx = pt.x() - mOriginX + pt0.x(); const double fy = pt.y() - mOriginY + pt0.y(); const double fz = pt.z() + extrusionHeight + pt0.z(); mData << fx << fz << -fy; if ( mAddNormals ) mData << pNormal.x() << pNormal.z() << - pNormal.y(); } if ( mAddBackFaces ) { // the same triangle with reversed order of coordinates and inverted normal for ( int j = 2; j >= 0; --j ) { p2t::Point *p = t->GetPoint( j ); QVector4D pt( p->x, p->y, z[p], 0 ); if ( toOldBase ) pt = *toOldBase * pt; const double fx = pt.x() - mOriginX + pt0.x(); const double fy = pt.y() - mOriginY + pt0.y(); const double fz = pt.z() + extrusionHeight + pt0.z(); mData << fx << fz << -fy; if ( mAddNormals ) mData << -pNormal.x() << -pNormal.z() << pNormal.y(); } } } } catch ( ... ) { QgsMessageLog::logMessage( QObject::tr( "Triangulation failed. Skipping polygon…" ), QObject::tr( "3D" ) ); } for ( int i = 0; i < polylinesToDelete.count(); ++i ) qDeleteAll( polylinesToDelete[i] ); } // add walls if extrusion is enabled if ( extrusionHeight != 0 ) { _makeWalls( *exterior, false, extrusionHeight, mData, mAddNormals, mOriginX, mOriginY ); for ( int i = 0; i < polygon.numInteriorRings(); ++i ) _makeWalls( *polygon.interiorRing( i ), true, extrusionHeight, mData, mAddNormals, mOriginX, mOriginY ); } }
void QgsGeometryGapCheck::collectErrors( const QMap<QString, QgsFeaturePool *> &featurePools, QList<QgsGeometryCheckError *> &errors, QStringList &messages, QgsFeedback *feedback, const LayerFeatureIds &ids ) const { if ( feedback ) feedback->setProgress( feedback->progress() + 1.0 ); QVector<QgsGeometry> geomList; QMap<QString, QgsFeatureIds> featureIds = ids.isEmpty() ? allLayerFeatureIds( featurePools ) : ids.toMap(); const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds, compatibleGeometryTypes(), nullptr, mContext, true ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { geomList.append( layerFeature.geometry() ); if ( feedback && feedback->isCanceled() ) { geomList.clear(); break; } } if ( geomList.isEmpty() ) { return; } std::unique_ptr< QgsGeometryEngine > geomEngine = QgsGeometryCheckerUtils::createGeomEngine( nullptr, mContext->tolerance ); geomEngine->prepareGeometry(); // Create union of geometry QString errMsg; std::unique_ptr<QgsAbstractGeometry> unionGeom( geomEngine->combine( geomList, &errMsg ) ); if ( !unionGeom ) { messages.append( tr( "Gap check: %1" ).arg( errMsg ) ); return; } // Get envelope of union geomEngine = QgsGeometryCheckerUtils::createGeomEngine( unionGeom.get(), mContext->tolerance ); geomEngine->prepareGeometry(); std::unique_ptr<QgsAbstractGeometry> envelope( geomEngine->envelope( &errMsg ) ); if ( !envelope ) { messages.append( tr( "Gap check: %1" ).arg( errMsg ) ); return; } // Buffer envelope geomEngine = QgsGeometryCheckerUtils::createGeomEngine( envelope.get(), mContext->tolerance ); geomEngine->prepareGeometry(); QgsAbstractGeometry *bufEnvelope = geomEngine->buffer( 2, 0, GEOSBUF_CAP_SQUARE, GEOSBUF_JOIN_MITRE, 4. ); //#spellok //#spellok envelope.reset( bufEnvelope ); // Compute difference between envelope and union to obtain gap polygons geomEngine = QgsGeometryCheckerUtils::createGeomEngine( envelope.get(), mContext->tolerance ); geomEngine->prepareGeometry(); std::unique_ptr<QgsAbstractGeometry> diffGeom( geomEngine->difference( unionGeom.get(), &errMsg ) ); if ( !diffGeom ) { messages.append( tr( "Gap check: %1" ).arg( errMsg ) ); return; } // For each gap polygon which does not lie on the boundary, get neighboring polygons and add error for ( int iPart = 0, nParts = diffGeom->partCount(); iPart < nParts; ++iPart ) { std::unique_ptr<QgsAbstractGeometry> gapGeom( QgsGeometryCheckerUtils::getGeomPart( diffGeom.get(), iPart )->clone() ); // Skip the gap between features and boundingbox const double spacing = context()->tolerance; if ( gapGeom->boundingBox().snappedToGrid( spacing ) == envelope->boundingBox().snappedToGrid( spacing ) ) { continue; } // Skip gaps above threshold if ( ( mGapThresholdMapUnits > 0 && gapGeom->area() > mGapThresholdMapUnits ) || gapGeom->area() < mContext->reducedTolerance ) { continue; } QgsRectangle gapAreaBBox = gapGeom->boundingBox(); // Get neighboring polygons QMap<QString, QgsFeatureIds> neighboringIds; const QgsGeometryCheckerUtils::LayerFeatures layerFeatures( featurePools, featureIds.keys(), gapAreaBBox, compatibleGeometryTypes(), mContext ); for ( const QgsGeometryCheckerUtils::LayerFeature &layerFeature : layerFeatures ) { const QgsGeometry geom = layerFeature.geometry(); if ( QgsGeometryCheckerUtils::sharedEdgeLength( gapGeom.get(), geom.constGet(), mContext->reducedTolerance ) > 0 ) { neighboringIds[layerFeature.layer()->id()].insert( layerFeature.feature().id() ); gapAreaBBox.combineExtentWith( layerFeature.geometry().boundingBox() ); } } if ( neighboringIds.isEmpty() ) { continue; } // Add error double area = gapGeom->area(); errors.append( new QgsGeometryGapCheckError( this, QString(), QgsGeometry( gapGeom.release() ), neighboringIds, area, gapAreaBBox ) ); } }
QVariantMap QgsTransectAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { Side orientation = static_cast< QgsTransectAlgorithm::Side >( parameterAsInt( parameters, QStringLiteral( "SIDE" ), context ) ); double angle = fabs( parameterAsDouble( parameters, QStringLiteral( "ANGLE" ), context ) ); bool dynamicAngle = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "ANGLE" ) ); QgsProperty angleProperty; if ( dynamicAngle ) angleProperty = parameters.value( QStringLiteral( "ANGLE" ) ).value< QgsProperty >(); double length = parameterAsDouble( parameters, QStringLiteral( "LENGTH" ), context ); bool dynamicLength = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "LENGTH" ) ); QgsProperty lengthProperty; if ( dynamicLength ) lengthProperty = parameters.value( QStringLiteral( "LENGTH" ) ).value< QgsProperty >(); if ( orientation == QgsTransectAlgorithm::Both ) length /= 2.0; std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); if ( !source ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); QgsExpressionContext expressionContext = createExpressionContext( parameters, context, dynamic_cast< QgsProcessingFeatureSource * >( source.get() ) ); QgsFields fields = source->fields(); fields.append( QgsField( QStringLiteral( "TR_FID" ), QVariant::Int, QString(), 20 ) ); fields.append( QgsField( QStringLiteral( "TR_ID" ), QVariant::Int, QString(), 20 ) ); fields.append( QgsField( QStringLiteral( "TR_SEGMENT" ), QVariant::Int, QString(), 20 ) ); fields.append( QgsField( QStringLiteral( "TR_ANGLE" ), QVariant::Double, QString(), 5, 2 ) ); fields.append( QgsField( QStringLiteral( "TR_LENGTH" ), QVariant::Double, QString(), 20, 6 ) ); fields.append( QgsField( QStringLiteral( "TR_ORIENT" ), QVariant::Int, QString(), 1 ) ); QgsWkbTypes::Type outputWkb = QgsWkbTypes::LineString; if ( QgsWkbTypes::hasZ( source->wkbType() ) ) outputWkb = QgsWkbTypes::addZ( outputWkb ); if ( QgsWkbTypes::hasM( source->wkbType() ) ) outputWkb = QgsWkbTypes::addM( outputWkb ); QString dest; std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, outputWkb, source->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); QgsFeatureIterator features = source->getFeatures( ); int current = -1; int number = 0; double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1; QgsFeature feat; while ( features.nextFeature( feat ) ) { current++; if ( feedback->isCanceled() ) { break; } feedback->setProgress( current * step ); if ( !feat.hasGeometry() ) continue; QgsGeometry inputGeometry = feat.geometry(); if ( dynamicLength || dynamicAngle ) { expressionContext.setFeature( feat ); } double evaluatedLength = length; if ( dynamicLength ) evaluatedLength = lengthProperty.valueAsDouble( context.expressionContext(), length ); double evaluatedAngle = angle; if ( dynamicAngle ) evaluatedAngle = angleProperty.valueAsDouble( context.expressionContext(), angle ); inputGeometry.convertToMultiType(); const QgsMultiLineString *multiLine = static_cast< const QgsMultiLineString * >( inputGeometry.constGet() ); for ( int id = 0; id < multiLine->numGeometries(); ++id ) { const QgsLineString *line = static_cast< const QgsLineString * >( multiLine->geometryN( id ) ); QgsAbstractGeometry::vertex_iterator it = line->vertices_begin(); while ( it != line->vertices_end() ) { QgsVertexId vertexId = it.vertexId(); int i = vertexId.vertex; QgsFeature outFeat; QgsAttributes attrs = feat.attributes(); attrs << current << number << i + 1 << evaluatedAngle << ( ( orientation == QgsTransectAlgorithm::Both ) ? evaluatedLength * 2 : evaluatedLength ) << orientation; outFeat.setAttributes( attrs ); double angleAtVertex = line->vertexAngle( vertexId ); outFeat.setGeometry( calcTransect( *it, angleAtVertex, evaluatedLength, orientation, evaluatedAngle ) ); sink->addFeature( outFeat, QgsFeatureSink::FastInsert ); number++; it++; } } } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); return outputs; }
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() ); } }
QString QgsExpression::formatPreviewString( const QVariant &value, const bool htmlOutput ) { static const int MAX_PREVIEW = 60; const QString startToken = htmlOutput ? QStringLiteral( "<i><" ) : QStringLiteral( "<" ); const QString endToken = htmlOutput ? QStringLiteral( "></i>" ) : QStringLiteral( ">" ); if ( value.canConvert<QgsGeometry>() ) { //result is a geometry QgsGeometry geom = value.value<QgsGeometry>(); if ( geom.isNull() ) return startToken + tr( "empty geometry" ) + endToken; else return startToken + tr( "geometry: %1" ).arg( QgsWkbTypes::displayString( geom.constGet()->wkbType() ) ) + endToken; } else if ( value.value< QgsWeakMapLayerPointer >().data() ) { return startToken + tr( "map layer" ) + endToken; } else if ( !value.isValid() ) { return htmlOutput ? tr( "<i>NULL</i>" ) : QString(); } else if ( value.canConvert< QgsFeature >() ) { //result is a feature QgsFeature feat = value.value<QgsFeature>(); return startToken + tr( "feature: %1" ).arg( feat.id() ) + endToken; } else if ( value.canConvert< QgsInterval >() ) { //result is a feature QgsInterval interval = value.value<QgsInterval>(); return startToken + tr( "interval: %1 days" ).arg( interval.days() ) + endToken; } else if ( value.canConvert< QgsGradientColorRamp >() ) { return startToken + tr( "gradient ramp" ) + endToken; } else if ( value.type() == QVariant::Date ) { QDate dt = value.toDate(); return startToken + tr( "date: %1" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd" ) ) ) + endToken; } else if ( value.type() == QVariant::Time ) { QTime tm = value.toTime(); return startToken + tr( "time: %1" ).arg( tm.toString( QStringLiteral( "hh:mm:ss" ) ) ) + endToken; } else if ( value.type() == QVariant::DateTime ) { QDateTime dt = value.toDateTime(); return startToken + tr( "datetime: %1" ).arg( dt.toString( QStringLiteral( "yyyy-MM-dd hh:mm:ss" ) ) ) + endToken; } else if ( value.type() == QVariant::String ) { const QString previewString = value.toString(); if ( previewString.length() > MAX_PREVIEW + 3 ) { return tr( "'%1…'" ).arg( previewString.left( MAX_PREVIEW ) ); } else { return '\'' + previewString + '\''; } } else if ( value.type() == QVariant::Map ) { QString mapStr = QStringLiteral( "{" ); const QVariantMap map = value.toMap(); QString separator; for ( QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it ) { mapStr.append( separator ); if ( separator.isEmpty() ) separator = QStringLiteral( "," ); mapStr.append( QStringLiteral( " '%1': %2" ).arg( it.key(), formatPreviewString( it.value(), htmlOutput ) ) ); if ( mapStr.length() > MAX_PREVIEW - 3 ) { mapStr = tr( "%1…" ).arg( mapStr.left( MAX_PREVIEW - 2 ) ); break; } } if ( !map.empty() ) mapStr += QStringLiteral( " " ); mapStr += QStringLiteral( "}" ); return mapStr; } else if ( value.type() == QVariant::List || value.type() == QVariant::StringList ) { QString listStr = QStringLiteral( "[" ); const QVariantList list = value.toList(); QString separator; for ( const QVariant &arrayValue : list ) { listStr.append( separator ); if ( separator.isEmpty() ) separator = QStringLiteral( "," ); listStr.append( " " ); listStr.append( formatPreviewString( arrayValue, htmlOutput ) ); if ( listStr.length() > MAX_PREVIEW - 3 ) { listStr = QString( tr( "%1…" ) ).arg( listStr.left( MAX_PREVIEW - 2 ) ); break; } } if ( !list.empty() ) listStr += QStringLiteral( " " ); listStr += QStringLiteral( "]" ); return listStr; } else { return value.toString(); } }
Qt3DRender::QGeometryRenderer *QgsPolygon3DSymbolEntityNode::renderer( const Qgs3DMapSettings &map, const QgsPolygon3DSymbol &symbol, const QgsVectorLayer *layer, const QgsFeatureRequest &request ) { QgsPointXY origin( map.origin().x(), map.origin().y() ); QList<QgsPolygon *> polygons; QList<float> extrusionHeightPerPolygon; // will stay empty if not needed per polygon QgsExpressionContext ctx( _expressionContext3D() ); ctx.setFields( layer->fields() ); const QgsPropertyCollection &ddp = symbol.dataDefinedProperties(); bool hasDDHeight = ddp.isActive( QgsAbstract3DSymbol::PropertyHeight ); bool hasDDExtrusion = ddp.isActive( QgsAbstract3DSymbol::PropertyExtrusionHeight ); QgsFeature f; QgsFeatureIterator fi = layer->getFeatures( request ); while ( fi.nextFeature( f ) ) { if ( f.geometry().isNull() ) continue; QgsGeometry geom = f.geometry(); // segmentize curved geometries if necessary if ( QgsWkbTypes::isCurvedType( geom.constGet()->wkbType() ) ) geom = QgsGeometry( geom.constGet()->segmentize() ); const QgsAbstractGeometry *g = geom.constGet(); ctx.setFeature( f ); float height = symbol.height(); float extrusionHeight = symbol.extrusionHeight(); if ( hasDDHeight ) height = ddp.valueAsDouble( QgsAbstract3DSymbol::PropertyHeight, ctx, height ); if ( hasDDExtrusion ) extrusionHeight = ddp.valueAsDouble( QgsAbstract3DSymbol::PropertyExtrusionHeight, ctx, extrusionHeight ); if ( const QgsPolygon *poly = qgsgeometry_cast< const QgsPolygon *>( g ) ) { QgsPolygon *polyClone = poly->clone(); Qgs3DUtils::clampAltitudes( polyClone, symbol.altitudeClamping(), symbol.altitudeBinding(), height, map ); polygons.append( polyClone ); if ( hasDDExtrusion ) extrusionHeightPerPolygon.append( extrusionHeight ); } else if ( const QgsMultiPolygon *mpoly = qgsgeometry_cast< const QgsMultiPolygon *>( g ) ) { for ( int i = 0; i < mpoly->numGeometries(); ++i ) { const QgsAbstractGeometry *g2 = mpoly->geometryN( i ); Q_ASSERT( QgsWkbTypes::flatType( g2->wkbType() ) == QgsWkbTypes::Polygon ); QgsPolygon *polyClone = static_cast< const QgsPolygon *>( g2 )->clone(); Qgs3DUtils::clampAltitudes( polyClone, symbol.altitudeClamping(), symbol.altitudeBinding(), height, map ); polygons.append( polyClone ); if ( hasDDExtrusion ) extrusionHeightPerPolygon.append( extrusionHeight ); } } else qDebug() << "not a polygon"; } mGeometry = new QgsTessellatedPolygonGeometry; mGeometry->setPolygons( polygons, origin, symbol.extrusionHeight(), extrusionHeightPerPolygon ); Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer; renderer->setGeometry( mGeometry ); return renderer; }
QgsVirtualLayerFeatureIterator::QgsVirtualLayerFeatureIterator( QgsVirtualLayerFeatureSource *source, bool ownSource, const QgsFeatureRequest &request ) : QgsAbstractFeatureIteratorFromSource<QgsVirtualLayerFeatureSource>( source, ownSource, request ) { // NOTE: this is really bad and should be removed. // it's only here to guard mSource->mSqlite - because if the provider is removed // then mSqlite will be meaningless. // this needs to be totally reworked so that mSqlite no longer depends on the provider // and can be fully encapsulated in the source if ( !mSource->mProvider ) { close(); return; } if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs ) { mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs(), mRequest.transformContext() ); } try { mFilterRect = filterRectToSourceCrs( mTransform ); } catch ( QgsCsException & ) { // can't reproject mFilterRect close(); return; } try { QString tableName = mSource->mTableName; QStringList wheres; QString offset; QString subset = mSource->mSubset; if ( !subset.isEmpty() ) { wheres << subset; } if ( !mSource->mDefinition.uid().isNull() ) { // filters are only available when a column with unique id exists if ( mSource->mDefinition.hasDefinedGeometry() && !mFilterRect.isNull() ) { bool do_exact = request.flags() & QgsFeatureRequest::ExactIntersect; QString mbr = QStringLiteral( "%1,%2,%3,%4" ).arg( mFilterRect.xMinimum() ).arg( mFilterRect.yMinimum() ).arg( mFilterRect.xMaximum() ).arg( mFilterRect.yMaximum() ); wheres << quotedColumn( mSource->mDefinition.geometryField() ) + " is not null"; wheres << QStringLiteral( "%1Intersects(%2,BuildMbr(%3))" ) .arg( do_exact ? "" : "Mbr", quotedColumn( mSource->mDefinition.geometryField() ), mbr ); } else if ( request.filterType() == QgsFeatureRequest::FilterFid ) { wheres << QStringLiteral( "%1=%2" ) .arg( quotedColumn( mSource->mDefinition.uid() ) ) .arg( request.filterFid() ); } else if ( request.filterType() == QgsFeatureRequest::FilterFids ) { QString values = quotedColumn( mSource->mDefinition.uid() ) + " IN ("; bool first = true; const auto constFilterFids = request.filterFids(); for ( QgsFeatureId v : constFilterFids ) { if ( !first ) { values += QLatin1String( "," ); } first = false; values += QString::number( v ); } values += QLatin1String( ")" ); wheres << values; } } else { if ( request.filterType() == QgsFeatureRequest::FilterFid ) { if ( request.filterFid() >= 0 ) offset = QStringLiteral( " LIMIT 1 OFFSET %1" ).arg( request.filterFid() ); else // never return a feature if the id is negative offset = QStringLiteral( " LIMIT 0" ); } else if ( !mFilterRect.isNull() && mRequest.flags() & QgsFeatureRequest::ExactIntersect ) { // if an exact intersection is requested, prepare the geometry to intersect QgsGeometry rectGeom = QgsGeometry::fromRect( mFilterRect ); mRectEngine.reset( QgsGeometry::createGeometryEngine( rectGeom.constGet() ) ); mRectEngine->prepareGeometry(); } } if ( request.flags() & QgsFeatureRequest::SubsetOfAttributes ) { // copy only selected fields const auto subsetOfAttributes = request.subsetOfAttributes(); for ( int idx : subsetOfAttributes ) { mAttributes << idx; } // ensure that all attributes required for expression filter are being fetched if ( request.filterType() == QgsFeatureRequest::FilterExpression ) { const auto constReferencedColumns = request.filterExpression()->referencedColumns(); for ( const QString &field : constReferencedColumns ) { int attrIdx = mSource->mFields.lookupField( field ); if ( !mAttributes.contains( attrIdx ) ) mAttributes << attrIdx; } } // also need attributes required by order by if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes && !mRequest.orderBy().isEmpty() ) { const auto usedAttributeIndices = mRequest.orderBy().usedAttributeIndices( mSource->mFields ); for ( int attrIdx : usedAttributeIndices ) { if ( !mAttributes.contains( attrIdx ) ) mAttributes << attrIdx; } } } else { mAttributes = mSource->mFields.allAttributesList(); } QString columns; { // the first column is always the uid (or 0) if ( !mSource->mDefinition.uid().isNull() ) { columns = quotedColumn( mSource->mDefinition.uid() ); } else { if ( request.filterType() == QgsFeatureRequest::FilterFid ) { columns = QString::number( request.filterFid() ); } else { columns = QStringLiteral( "0" ); } } const auto constMAttributes = mAttributes; for ( int i : constMAttributes ) { columns += QLatin1String( "," ); QString cname = mSource->mFields.at( i ).name().toLower(); columns += quotedColumn( cname ); } } // the last column is the geometry, if any if ( ( !( request.flags() & QgsFeatureRequest::NoGeometry ) || ( request.filterType() == QgsFeatureRequest::FilterExpression && request.filterExpression()->needsGeometry() ) ) && !mSource->mDefinition.geometryField().isNull() && mSource->mDefinition.geometryField() != QLatin1String( "*no*" ) ) { columns += "," + quotedColumn( mSource->mDefinition.geometryField() ); } mSqlQuery = "SELECT " + columns + " FROM " + tableName; if ( !wheres.isEmpty() ) { mSqlQuery += " WHERE " + wheres.join( QStringLiteral( " AND " ) ); } if ( !offset.isEmpty() ) { mSqlQuery += offset; } mQuery.reset( new Sqlite::Query( mSource->mSqlite, mSqlQuery ) ); mFid = 0; } catch ( std::runtime_error &e ) { QgsMessageLog::logMessage( e.what(), QObject::tr( "VLayer" ) ); close(); } }