Fix some overlay algorithms output multipoint geometries but

output layer is single point, causing insertion errors

Fixes #49456
This commit is contained in:
Nyall Dawson 2022-10-17 12:12:54 +10:00
parent 83a9715e1a
commit c9a2a5efa8
10 changed files with 82 additions and 40 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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>
@ -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>
@ -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>
@ -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>

View File

@ -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;

View File

@ -120,7 +120,7 @@ QVariantMap QgsDifferenceAlgorithm::processAlgorithm( const QVariantMap &paramet
geometryParameters.setGridSize( parameterAsDouble( parameters, QStringLiteral( "GRID_SIZE" ), context ) );
}
QgsOverlayUtils::difference( *sourceA, *sourceB, *sink, context, feedback, count, total, QgsOverlayUtils::OutputA, geometryParameters );
QgsOverlayUtils::difference( *sourceA, *sourceB, *sink, context, feedback, count, total, QgsOverlayUtils::OutputA, geometryParameters, QgsOverlayUtils::SanitizeFlag::DontPromotePointGeometryToMultiPoint );
return outputs;
}

View File

@ -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 );
}

View File

@ -145,7 +145,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

View File

@ -110,11 +110,11 @@ QVariantMap QgsSymmetricalDifferenceAlgorithm::processAlgorithm( const QVariantM
geometryParameters.setGridSize( parameterAsDouble( parameters, QStringLiteral( "GRID_SIZE" ), context ) );
}
QgsOverlayUtils::difference( *sourceA, *sourceB, *sink, context, feedback, count, total, QgsOverlayUtils::OutputAB, geometryParameters );
QgsOverlayUtils::difference( *sourceA, *sourceB, *sink, context, feedback, count, total, QgsOverlayUtils::OutputAB, geometryParameters, QgsOverlayUtils::SanitizeFlag::DontPromotePointGeometryToMultiPoint );
if ( feedback->isCanceled() )
return outputs;
QgsOverlayUtils::difference( *sourceB, *sourceA, *sink, context, feedback, count, total, QgsOverlayUtils::OutputBA, geometryParameters );
QgsOverlayUtils::difference( *sourceB, *sourceA, *sink, context, feedback, count, total, QgsOverlayUtils::OutputBA, geometryParameters, QgsOverlayUtils::SanitizeFlag::DontPromotePointGeometryToMultiPoint );
return outputs;
}

View File

@ -23,7 +23,7 @@
///@cond PRIVATE
bool QgsOverlayUtils::sanitizeIntersectionResult( QgsGeometry &geom, QgsWkbTypes::GeometryType geometryType )
bool QgsOverlayUtils::sanitizeIntersectionResult( QgsGeometry &geom, QgsWkbTypes::GeometryType geometryType, SanitizeFlags flags )
{
if ( geom.isNull() )
{
@ -48,16 +48,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() )
{
@ -77,9 +81,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;
}
@ -90,7 +98,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, const QgsGeometryParameters &parameters )
void QgsOverlayUtils::difference( const QgsFeatureSource &sourceA, const QgsFeatureSource &sourceB, QgsFeatureSink &sink, QgsProcessingContext &context, QgsProcessingFeedback *feedback, long &count, long totalCount, QgsOverlayUtils::DifferenceOutput outputAttrs, const QgsGeometryParameters &parameters, SanitizeFlags flags )
{
const QgsWkbTypes::GeometryType geometryType = QgsWkbTypes::geometryType( QgsWkbTypes::multiType( sourceA.wkbType() ) );
QgsFeatureRequest requestB;
@ -166,7 +174,7 @@ void QgsOverlayUtils::difference( const QgsFeatureSource &sourceA, const QgsFeat
geom = geom.difference( geomB, parameters );
}
if ( !geom.isNull() && !sanitizeDifferenceResult( geom, geometryType ) )
if ( !geom.isNull() && !sanitizeDifferenceResult( geom, geometryType, flags ) )
continue;
const QgsAttributes attrsA( featA.attributes() );
@ -282,7 +290,7 @@ void QgsOverlayUtils::intersection( const QgsFeatureSource &sourceA, const QgsFe
}
}
void QgsOverlayUtils::resolveOverlaps( const QgsFeatureSource &source, QgsFeatureSink &sink, QgsProcessingFeedback *feedback, const QgsGeometryParameters &parameters )
void QgsOverlayUtils::resolveOverlaps( const QgsFeatureSource &source, QgsFeatureSink &sink, QgsProcessingFeedback *feedback, const QgsGeometryParameters &parameters, SanitizeFlags flags )
{
long count = 0;
const long totalCount = source.featureCount();
@ -393,7 +401,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 );
@ -414,7 +422,7 @@ void QgsOverlayUtils::resolveOverlaps( const QgsFeatureSource &source, QgsFeatur
geometries.remove( fid2 );
if ( sanitizeDifferenceResult( g21, geometryType ) )
if ( sanitizeDifferenceResult( g21, geometryType, flags ) )
{
geometries.insert( fid2, g21 );

View File

@ -40,12 +40,32 @@ 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, const QgsGeometryParameters &parameters = QgsGeometryParameters() );
/**
* 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, const QgsGeometryParameters &parameters = QgsGeometryParameters(),
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, const QgsGeometryParameters &parameters = QgsGeometryParameters() );
//! 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
@ -57,9 +77,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, const QgsGeometryParameters &parameters = QgsGeometryParameters() );
void resolveOverlaps( const QgsFeatureSource &source, QgsFeatureSink &sink, QgsProcessingFeedback *feedback, const QgsGeometryParameters &parameters = QgsGeometryParameters(), SanitizeFlags flags = SanitizeFlags() );
}
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsOverlayUtils::SanitizeFlags )
///@endcond PRIVATE
#endif // QGSOVERLAYUTILS_H