Skip to content

Commit

Permalink
Fix some overlay algorithms output multipoint geometries but
Browse files Browse the repository at this point in the history
output layer is single point, causing insertion errors

Fixes #49456

(cherry picked from commit c9a2a5e)
  • Loading branch information
nyalldawson committed Nov 18, 2022
1 parent 21cd918 commit 0564be1
Show file tree
Hide file tree
Showing 10 changed files with 80 additions and 40 deletions.
Expand Up @@ -10,35 +10,35 @@
<gml:coord><gml:X>8</gml:X><gml:Y>3</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:clip_points_by_multipolygons fid="points.1">
<ogr:geometryProperty><gml:MultiPoint srsName="EPSG:4326"><gml:pointMember><gml:Point><gml:coordinates>3,3</gml:coordinates></gml:Point></gml:pointMember></gml:MultiPoint></ogr:geometryProperty>
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>3,3</gml:coordinates></gml:Point></ogr:geometryProperty>
</ogr:clip_points_by_multipolygons>
</gml:featureMember>
<gml:featureMember>
<ogr:clip_points_by_multipolygons fid="points.2">
<ogr:geometryProperty><gml:MultiPoint srsName="EPSG:4326"><gml:pointMember><gml:Point><gml:coordinates>2,2</gml:coordinates></gml:Point></gml:pointMember></gml:MultiPoint></ogr:geometryProperty>
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>2,2</gml:coordinates></gml:Point></ogr:geometryProperty>
</ogr:clip_points_by_multipolygons>
</gml:featureMember>
<gml:featureMember>
<ogr:clip_points_by_multipolygons fid="points.4">
<ogr:geometryProperty><gml:MultiPoint srsName="EPSG:4326"><gml:pointMember><gml:Point><gml:coordinates>4,1</gml:coordinates></gml:Point></gml:pointMember></gml:MultiPoint></ogr:geometryProperty>
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>4,1</gml:coordinates></gml:Point></ogr:geometryProperty>
</ogr:clip_points_by_multipolygons>
</gml:featureMember>
<gml:featureMember>
<ogr:clip_points_by_multipolygons fid="points.6">
<ogr:geometryProperty><gml:MultiPoint srsName="EPSG:4326"><gml:pointMember><gml:Point><gml:coordinates>8,-1</gml:coordinates></gml:Point></gml:pointMember></gml:MultiPoint></ogr:geometryProperty>
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>8,-1</gml:coordinates></gml:Point></ogr:geometryProperty>
</ogr:clip_points_by_multipolygons>
</gml:featureMember>
<gml:featureMember>
<ogr:clip_points_by_multipolygons fid="points.7">
<ogr:geometryProperty><gml:MultiPoint srsName="EPSG:4326"><gml:pointMember><gml:Point><gml:coordinates>7,-1</gml:coordinates></gml:Point></gml:pointMember></gml:MultiPoint></ogr:geometryProperty>
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>7,-1</gml:coordinates></gml:Point></ogr:geometryProperty>
</ogr:clip_points_by_multipolygons>
</gml:featureMember>
<gml:featureMember>
<ogr:clip_points_by_multipolygons fid="points.0">
<ogr:geometryProperty><gml:MultiPoint srsName="EPSG:4326"><gml:pointMember><gml:Point><gml:coordinates>1,1</gml:coordinates></gml:Point></gml:pointMember></gml:MultiPoint></ogr:geometryProperty>
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>1,1</gml:coordinates></gml:Point></ogr:geometryProperty>
</ogr:clip_points_by_multipolygons>
</gml:featureMember>
</ogr:FeatureCollection>
Expand Up @@ -10,35 +10,35 @@
<gml:coord><gml:X>7</gml:X><gml:Y>3</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:clip_points_by_polygons fid="points.0">
<ogr:geometryProperty><gml:MultiPoint srsName="EPSG:4326"><gml:pointMember><gml:Point><gml:coordinates>1,1</gml:coordinates></gml:Point></gml:pointMember></gml:MultiPoint></ogr:geometryProperty>
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>1,1</gml:coordinates></gml:Point></ogr:geometryProperty>
</ogr:clip_points_by_polygons>
</gml:featureMember>
<gml:featureMember>
<ogr:clip_points_by_polygons fid="points.1">
<ogr:geometryProperty><gml:MultiPoint srsName="EPSG:4326"><gml:pointMember><gml:Point><gml:coordinates>3,3</gml:coordinates></gml:Point></gml:pointMember></gml:MultiPoint></ogr:geometryProperty>
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>3,3</gml:coordinates></gml:Point></ogr:geometryProperty>
</ogr:clip_points_by_polygons>
</gml:featureMember>
<gml:featureMember>
<ogr:clip_points_by_polygons fid="points.2">
<ogr:geometryProperty><gml:MultiPoint srsName="EPSG:4326"><gml:pointMember><gml:Point><gml:coordinates>2,2</gml:coordinates></gml:Point></gml:pointMember></gml:MultiPoint></ogr:geometryProperty>
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>2,2</gml:coordinates></gml:Point></ogr:geometryProperty>
</ogr:clip_points_by_polygons>
</gml:featureMember>
<gml:featureMember>
<ogr:clip_points_by_polygons fid="points.8">
<ogr:geometryProperty><gml:MultiPoint srsName="EPSG:4326"><gml:pointMember><gml:Point><gml:coordinates>0,-1</gml:coordinates></gml:Point></gml:pointMember></gml:MultiPoint></ogr:geometryProperty>
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>0,-1</gml:coordinates></gml:Point></ogr:geometryProperty>
</ogr:clip_points_by_polygons>
</gml:featureMember>
<gml:featureMember>
<ogr:clip_points_by_polygons fid="points.7">
<ogr:geometryProperty><gml:MultiPoint srsName="EPSG:4326"><gml:pointMember><gml:Point><gml:coordinates>7,-1</gml:coordinates></gml:Point></gml:pointMember></gml:MultiPoint></ogr:geometryProperty>
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>7,-1</gml:coordinates></gml:Point></ogr:geometryProperty>
</ogr:clip_points_by_polygons>
</gml:featureMember>
<gml:featureMember>
<ogr:clip_points_by_polygons fid="points.4">
<ogr:geometryProperty><gml:MultiPoint srsName="EPSG:4326"><gml:pointMember><gml:Point><gml:coordinates>4,1</gml:coordinates></gml:Point></gml:pointMember></gml:MultiPoint></ogr:geometryProperty>
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>4,1</gml:coordinates></gml:Point></ogr:geometryProperty>
</ogr:clip_points_by_polygons>
</gml:featureMember>
</ogr:FeatureCollection>
Expand Up @@ -6,7 +6,6 @@
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml/3.2">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326"><gml:lowerCorner>-5 0</gml:lowerCorner><gml:upperCorner>2 8</gml:upperCorner></gml:Envelope></gml:boundedBy>

<ogr:featureMember>
<ogr:difference_points_vs_polys gml:id="difference_points_vs_polys.0">
<ogr:fid>points.0</ogr:fid>
Expand All @@ -31,7 +30,7 @@
<ogr:featureMember>
<ogr:difference_points_vs_polys gml:id="difference_points_vs_polys.3">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326"><gml:lowerCorner>2 5</gml:lowerCorner><gml:upperCorner>2 5</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:MultiPoint srsName="urn:ogc:def:crs:EPSG::4326" gml:id="difference_points_vs_polys.geom.3"><gml:pointMember><gml:Point gml:id="difference_points_vs_polys.geom.3.0"><gml:pos>2 5</gml:pos></gml:Point></gml:pointMember></gml:MultiPoint></ogr:geometryProperty>
<ogr:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326" gml:id="difference_points_vs_polys.geom.3"><gml:pos>2 5</gml:pos></gml:Point></ogr:geometryProperty>
<ogr:fid>points.3</ogr:fid>
<ogr:id>4</ogr:id>
<ogr:id2>2</ogr:id2>
Expand All @@ -47,7 +46,7 @@
<ogr:featureMember>
<ogr:difference_points_vs_polys gml:id="difference_points_vs_polys.5">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326"><gml:lowerCorner>-5 0</gml:lowerCorner><gml:upperCorner>-5 0</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:MultiPoint srsName="urn:ogc:def:crs:EPSG::4326" gml:id="difference_points_vs_polys.geom.5"><gml:pointMember><gml:Point gml:id="difference_points_vs_polys.geom.5.0"><gml:pos>-5 0</gml:pos></gml:Point></gml:pointMember></gml:MultiPoint></ogr:geometryProperty>
<ogr:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326" gml:id="difference_points_vs_polys.geom.5"><gml:pos>-5 0</gml:pos></gml:Point></ogr:geometryProperty>
<ogr:fid>points.5</ogr:fid>
<ogr:id>6</ogr:id>
<ogr:id2>0</ogr:id2>
Expand All @@ -56,7 +55,7 @@
<ogr:featureMember>
<ogr:difference_points_vs_polys gml:id="difference_points_vs_polys.6">
<gml:boundedBy><gml:Envelope srsName="urn:ogc:def:crs:EPSG::4326"><gml:lowerCorner>-1 8</gml:lowerCorner><gml:upperCorner>-1 8</gml:upperCorner></gml:Envelope></gml:boundedBy>
<ogr:geometryProperty><gml:MultiPoint srsName="urn:ogc:def:crs:EPSG::4326" gml:id="difference_points_vs_polys.geom.6"><gml:pointMember><gml:Point gml:id="difference_points_vs_polys.geom.6.0"><gml:pos>-1 8</gml:pos></gml:Point></gml:pointMember></gml:MultiPoint></ogr:geometryProperty>
<ogr:geometryProperty><gml:Point srsName="urn:ogc:def:crs:EPSG::4326" gml:id="difference_points_vs_polys.geom.6"><gml:pos>-1 8</gml:pos></gml:Point></ogr:geometryProperty>
<ogr:fid>points.6</ogr:fid>
<ogr:id>7</ogr:id>
<ogr:id2>0</ogr:id2>
Expand Down
2 changes: 1 addition & 1 deletion src/analysis/processing/qgsalgorithmclip.cpp
Expand Up @@ -208,7 +208,7 @@ QVariantMap QgsClipAlgorithm::processAlgorithm( const QVariantMap &parameters, Q
newGeometry = inputFeature.geometry();
}

if ( !QgsOverlayUtils::sanitizeIntersectionResult( newGeometry, sinkType ) )
if ( !QgsOverlayUtils::sanitizeIntersectionResult( newGeometry, sinkType, QgsOverlayUtils::SanitizeFlag::DontPromotePointGeometryToMultiPoint ) )
continue;

QgsFeature outputFeature;
Expand Down
2 changes: 1 addition & 1 deletion src/analysis/processing/qgsalgorithmdifference.cpp
Expand Up @@ -108,7 +108,7 @@ QVariantMap QgsDifferenceAlgorithm::processAlgorithm( const QVariantMap &paramet

long count = 0;
const long total = sourceA->featureCount();
QgsOverlayUtils::difference( *sourceA, *sourceB, *sink, context, feedback, count, total, QgsOverlayUtils::OutputA );
QgsOverlayUtils::difference( *sourceA, *sourceB, *sink, context, feedback, count, total, QgsOverlayUtils::OutputA, QgsOverlayUtils::SanitizeFlag::DontPromotePointGeometryToMultiPoint ); );

return outputs;
}
Expand Down
9 changes: 8 additions & 1 deletion src/analysis/processing/qgsalgorithmextractbyextent.cpp
Expand Up @@ -102,7 +102,14 @@ QVariantMap QgsExtractByExtentAlgorithm::processAlgorithm( const QVariantMap &pa
if ( clip )
{
QgsGeometry g = f.geometry().intersection( clipGeom );
g.convertToMultiType();

if ( g.type() != QgsWkbTypes::GeometryType::PointGeometry )
{
// some data providers are picky about the geometries we pass to them: we can't add single-part geometries
// when we promised multi-part geometries, so ensure we have the right type
g.convertToMultiType();
}

f.setGeometry( g );
}

Expand Down
8 changes: 7 additions & 1 deletion src/analysis/processing/qgsalgorithmfixgeometries.cpp
Expand Up @@ -117,7 +117,13 @@ QgsFeatureList QgsFixGeometriesAlgorithm::processFeature( const QgsFeature &feat
outputGeometry = QgsGeometry();
}

outputGeometry.convertToMultiType();
if ( outputGeometry.type() != QgsWkbTypes::GeometryType::PointGeometry )
{
// some data providers are picky about the geometries we pass to them: we can't add single-part geometries
// when we promised multi-part geometries, so ensure we have the right type
outputGeometry.convertToMultiType();
}

if ( QgsWkbTypes::geometryType( outputGeometry.wkbType() ) != QgsWkbTypes::geometryType( feature.geometry().wkbType() ) )
{
// don't keep geometries which have different types - e.g. lines converted to points
Expand Down
4 changes: 2 additions & 2 deletions src/analysis/processing/qgsalgorithmsymmetricaldifference.cpp
Expand Up @@ -95,11 +95,11 @@ QVariantMap QgsSymmetricalDifferenceAlgorithm::processAlgorithm( const QVariantM
long count = 0;
const long total = sourceA->featureCount() + sourceB->featureCount();

QgsOverlayUtils::difference( *sourceA, *sourceB, *sink, context, feedback, count, total, QgsOverlayUtils::OutputAB );
QgsOverlayUtils::difference( *sourceA, *sourceB, *sink, context, feedback, count, total, QgsOverlayUtils::OutputAB, QgsOverlayUtils::SanitizeFlag::DontPromotePointGeometryToMultiPoint );
if ( feedback->isCanceled() )
return outputs;

QgsOverlayUtils::difference( *sourceB, *sourceA, *sink, context, feedback, count, total, QgsOverlayUtils::OutputBA );
QgsOverlayUtils::difference( *sourceB, *sourceA, *sink, context, feedback, count, total, QgsOverlayUtils::OutputBA, QgsOverlayUtils::SanitizeFlag::DontPromotePointGeometryToMultiPoint );

return outputs;
}
Expand Down
34 changes: 21 additions & 13 deletions src/analysis/processing/qgsoverlayutils.cpp
Expand Up @@ -20,7 +20,7 @@

///@cond PRIVATE

bool QgsOverlayUtils::sanitizeIntersectionResult( QgsGeometry &geom, QgsWkbTypes::GeometryType geometryType )
bool QgsOverlayUtils::sanitizeIntersectionResult( QgsGeometry &geom, QgsWkbTypes::GeometryType geometryType, SanitizeFlags flags )
{
if ( geom.isNull() )
{
Expand All @@ -45,16 +45,20 @@ bool QgsOverlayUtils::sanitizeIntersectionResult( QgsGeometry &geom, QgsWkbTypes
return false;
}

// some data providers are picky about the geometries we pass to them: we can't add single-part geometries
// when we promised multi-part geometries, so ensure we have the right type
geom.convertToMultiType();
if ( geometryType != QgsWkbTypes::GeometryType::PointGeometry
|| !( flags & SanitizeFlag::DontPromotePointGeometryToMultiPoint ) )
{
// some data providers are picky about the geometries we pass to them: we can't add single-part geometries
// when we promised multi-part geometries, so ensure we have the right type
geom.convertToMultiType();
}

return true;
}


//! Makes sure that what came out from difference of two geometries is good to be used in the output
static bool sanitizeDifferenceResult( QgsGeometry &geom, QgsWkbTypes::GeometryType geometryType )
static bool sanitizeDifferenceResult( QgsGeometry &geom, QgsWkbTypes::GeometryType geometryType, QgsOverlayUtils::SanitizeFlags flags )
{
if ( geom.isNull() )
{
Expand All @@ -74,9 +78,13 @@ static bool sanitizeDifferenceResult( QgsGeometry &geom, QgsWkbTypes::GeometryTy
if ( geom.isEmpty() )
return false;

// some data providers are picky about the geometries we pass to them: we can't add single-part geometries
// when we promised multi-part geometries, so ensure we have the right type
geom.convertToMultiType();
if ( geometryType != QgsWkbTypes::GeometryType::PointGeometry
|| !( flags & QgsOverlayUtils::SanitizeFlag::DontPromotePointGeometryToMultiPoint ) )
{
// some data providers are picky about the geometries we pass to them: we can't add single-part geometries
// when we promised multi-part geometries, so ensure we have the right type
geom.convertToMultiType();
}

return true;
}
Expand All @@ -87,7 +95,7 @@ static QString writeFeatureError()
return QObject::tr( "Could not write feature" );
}

void QgsOverlayUtils::difference( const QgsFeatureSource &sourceA, const QgsFeatureSource &sourceB, QgsFeatureSink &sink, QgsProcessingContext &context, QgsProcessingFeedback *feedback, long &count, long totalCount, QgsOverlayUtils::DifferenceOutput outputAttrs )
void QgsOverlayUtils::difference( const QgsFeatureSource &sourceA, const QgsFeatureSource &sourceB, QgsFeatureSink &sink, QgsProcessingContext &context, QgsProcessingFeedback *feedback, long &count, long totalCount, QgsOverlayUtils::DifferenceOutput outputAttrs, SanitizeFlags flags )
{
const QgsWkbTypes::GeometryType geometryType = QgsWkbTypes::geometryType( QgsWkbTypes::multiType( sourceA.wkbType() ) );
QgsFeatureRequest requestB;
Expand Down Expand Up @@ -163,7 +171,7 @@ void QgsOverlayUtils::difference( const QgsFeatureSource &sourceA, const QgsFeat
geom = geom.difference( geomB );
}

if ( !geom.isNull() && !sanitizeDifferenceResult( geom, geometryType ) )
if ( !geom.isNull() && !sanitizeDifferenceResult( geom, geometryType, flags ) )
continue;

const QgsAttributes attrsA( featA.attributes() );
Expand Down Expand Up @@ -279,7 +287,7 @@ void QgsOverlayUtils::intersection( const QgsFeatureSource &sourceA, const QgsFe
}
}

void QgsOverlayUtils::resolveOverlaps( const QgsFeatureSource &source, QgsFeatureSink &sink, QgsProcessingFeedback *feedback )
void QgsOverlayUtils::resolveOverlaps( const QgsFeatureSource &source, QgsFeatureSink &sink, QgsProcessingFeedback *feedback, SanitizeFlags flags )
{
long count = 0;
const long totalCount = source.featureCount();
Expand Down Expand Up @@ -390,7 +398,7 @@ void QgsOverlayUtils::resolveOverlaps( const QgsFeatureSource &source, QgsFeatur
index.deleteFeature( f );
geometries.remove( fid1 );

if ( sanitizeDifferenceResult( g12, geometryType ) )
if ( sanitizeDifferenceResult( g12, geometryType, flags ) )
{
geometries.insert( fid1, g12 );

Expand All @@ -411,7 +419,7 @@ void QgsOverlayUtils::resolveOverlaps( const QgsFeatureSource &source, QgsFeatur

geometries.remove( fid2 );

if ( sanitizeDifferenceResult( g21, geometryType ) )
if ( sanitizeDifferenceResult( g21, geometryType, flags ) )
{
geometries.insert( fid2, g21 );

Expand Down
26 changes: 23 additions & 3 deletions src/analysis/processing/qgsoverlayutils.h
Expand Up @@ -41,12 +41,30 @@ namespace QgsOverlayUtils
OutputBA, //!< Write attributes of both layers, inverted (first attributes of B, then attributes of A)
};

void difference( const QgsFeatureSource &sourceA, const QgsFeatureSource &sourceB, QgsFeatureSink &sink, QgsProcessingContext &context, QgsProcessingFeedback *feedback, long &count, long totalCount, DifferenceOutput outputAttrs );
/**
* Flags for controlling the geometry sanitization behavior.
*
* \since QGIS 3.28
*/
enum SanitizeFlag
{
DontPromotePointGeometryToMultiPoint = 1 << 0, //!< Don't force promote point geometries to a multipoint type
};

/**
* Flags for controlling the geometry sanitization behavior.
*
* \since QGIS 3.28
*/
Q_DECLARE_FLAGS( SanitizeFlags, SanitizeFlag )

void difference( const QgsFeatureSource &sourceA, const QgsFeatureSource &sourceB, QgsFeatureSink &sink, QgsProcessingContext &context, QgsProcessingFeedback *feedback, long &count, long totalCount, DifferenceOutput outputAttrs,
SanitizeFlags flags = SanitizeFlags() );

void intersection( const QgsFeatureSource &sourceA, const QgsFeatureSource &sourceB, QgsFeatureSink &sink, QgsProcessingContext &context, QgsProcessingFeedback *feedback, long &count, long totalCount, const QList<int> &fieldIndicesA, const QList<int> &fieldIndicesB );

//! Makes sure that what came out from intersection of two geometries is good to be used in the output
bool sanitizeIntersectionResult( QgsGeometry &geom, QgsWkbTypes::GeometryType geometryType );
bool sanitizeIntersectionResult( QgsGeometry &geom, QgsWkbTypes::GeometryType geometryType, SanitizeFlags flags = SanitizeFlags() );

/**
* Copies features from the source to the sink and resolves overlaps: for each pair of overlapping features A and B
Expand All @@ -58,9 +76,11 @@ namespace QgsOverlayUtils
*
* As a result, for all pairs of features in the output, a pair either has no common interior or their interior is the same.
*/
void resolveOverlaps( const QgsFeatureSource &source, QgsFeatureSink &sink, QgsProcessingFeedback *feedback );
void resolveOverlaps( const QgsFeatureSource &source, QgsFeatureSink &sink, QgsProcessingFeedback *feedback, SanitizeFlags flags = SanitizeFlags() );
}

Q_DECLARE_OPERATORS_FOR_FLAGS( QgsOverlayUtils::SanitizeFlags )

///@endcond PRIVATE

#endif // QGSOVERLAYUTILS_H

0 comments on commit 0564be1

Please sign in to comment.