Fix #8174 (overlaps when using 'avoid intersections' functionality) + test

I am not entirely confident if it will work perfectly with older versions of GEOS (< 3.3)
because of the lack of unary union (that is emulated by series of unions of two geometries)
This commit is contained in:
Martin Dobias 2014-02-07 11:58:59 +07:00
parent e3aef431a8
commit caf33b657f
5 changed files with 148 additions and 4 deletions

View File

@ -612,8 +612,10 @@ class QgsVectorLayer : QgsMapLayer
* @param geom geometry to modify
* @param ignoreFeatures list of feature ids where intersections should be ignored
* @return 0 in case of success
*
* @deprecated since 2.2 - not being used for "avoid intersections" functionality anymore
*/
int removePolygonIntersections( QgsGeometry* geom, QgsFeatureIds ignoreFeatures = QgsFeatureIds() );
int removePolygonIntersections( QgsGeometry* geom, QgsFeatureIds ignoreFeatures = QgsFeatureIds() ) /Deprecated/;
/** Adds topological points for every vertex of the geometry.
* @param geom the geometry where each vertex is added to segments of other features

View File

@ -5799,6 +5799,32 @@ bool QgsGeometry::deletePart( int partNum )
return true;
}
/** Return union of several geometries - try to use unary union if available (GEOS >= 3.3) otherwise use a cascade of unions.
* Takes ownership of passed geometries, returns a new instance */
static GEOSGeometry* _makeUnion( QList<GEOSGeometry*> geoms )
{
#if defined(GEOS_VERSION_MAJOR) && defined(GEOS_VERSION_MINOR) && (((GEOS_VERSION_MAJOR==3) && (GEOS_VERSION_MINOR>=3)) || (GEOS_VERSION_MAJOR>3))
GEOSGeometry* geomCollection = 0;
geomCollection = createGeosCollection( GEOS_GEOMETRYCOLLECTION, geoms.toVector() );
GEOSGeometry* geomUnion = GEOSUnaryUnion( geomCollection );
GEOSGeom_destroy( geomCollection );
return geomUnion;
#else
GEOSGeometry* geomCollection = geoms.takeFirst();
while ( !geoms.isEmpty() )
{
GEOSGeometry* g = geoms.takeFirst();
GEOSGeometry* geomCollectionNew = GEOSUnion( geomCollection, g );
GEOSGeom_destroy( geomCollection );
GEOSGeom_destroy( g );
geomCollection = geomCollectionNew;
}
return geomCollection;
#endif
}
int QgsGeometry::avoidIntersections( QMap<QgsVectorLayer*, QSet< QgsFeatureId > > ignoreFeatures )
{
int returnValue = 0;
@ -5815,6 +5841,8 @@ int QgsGeometry::avoidIntersections( QMap<QgsVectorLayer*, QSet< QgsFeatureId >
if ( !listReadOk )
return true; //no intersections stored in project does not mean error
QList<GEOSGeometry*> nearGeometries;
//go through list, convert each layer to vector layer and call QgsVectorLayer::removePolygonIntersections for each
QgsVectorLayer* currentLayer = 0;
QStringList::const_iterator aIt = avoidIntersectionsList.constBegin();
@ -5828,12 +5856,48 @@ int QgsGeometry::avoidIntersections( QMap<QgsVectorLayer*, QSet< QgsFeatureId >
if ( ignoreIt != ignoreFeatures.constEnd() )
ignoreIds = ignoreIt.value();
if ( currentLayer->removePolygonIntersections( this, ignoreIds ) != 0 )
returnValue = 3;
QgsFeatureIterator fi = currentLayer->getFeatures( QgsFeatureRequest( boundingBox() )
.setFlags( QgsFeatureRequest::ExactIntersect )
.setSubsetOfAttributes( QgsAttributeList() ) );
QgsFeature f;
while ( fi.nextFeature( f ) )
{
if ( ignoreIds.contains( f.id() ) )
continue;
if ( !f.geometry() )
continue;
nearGeometries << GEOSGeom_clone( f.geometry()->asGeos() );
}
}
}
if ( nearGeometries.isEmpty() )
return 0;
GEOSGeometry* nearGeometriesUnion = 0;
GEOSGeometry* geomWithoutIntersections = 0;
try
{
nearGeometriesUnion = _makeUnion( nearGeometries );
geomWithoutIntersections = GEOSDifference( asGeos(), nearGeometriesUnion );
fromGeos( geomWithoutIntersections );
GEOSGeom_destroy( nearGeometriesUnion );
}
catch ( GEOSException &e )
{
if ( nearGeometriesUnion )
GEOSGeom_destroy( nearGeometriesUnion );
if ( geomWithoutIntersections )
GEOSGeom_destroy( geomWithoutIntersections );
QgsMessageLog::logMessage( QObject::tr( "Exception: %1" ).arg( e.what() ), QObject::tr( "GEOS" ) );
return 3;
}
//make sure the geometry still has the same type (e.g. no change from polygon to multipolygon)
if ( wkbType() != geomTypeBeforeModification )
return 2;

View File

@ -963,8 +963,10 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer
* @param geom geometry to modify
* @param ignoreFeatures list of feature ids where intersections should be ignored
* @return 0 in case of success
*
* @deprecated since 2.2 - not being used for "avoid intersections" functionality anymore
*/
int removePolygonIntersections( QgsGeometry* geom, QgsFeatureIds ignoreFeatures = QgsFeatureIds() );
Q_DECL_DEPRECATED int removePolygonIntersections( QgsGeometry* geom, QgsFeatureIds ignoreFeatures = QgsFeatureIds() );
/** Adds topological points for every vertex of the geometry.
* @param geom the geometry where each vertex is added to segments of other features

View File

@ -3,6 +3,7 @@ ADD_PYTHON_TEST(PyQgsApplication test_qgsapplication.py)
ADD_PYTHON_TEST(PyQgsFeature test_qgsfeature.py)
ADD_PYTHON_TEST(PyQgsFeatureIterator test_qgsfeatureiterator.py)
ADD_PYTHON_TEST(PyQgsGeometry test_qgsgeometry.py)
ADD_PYTHON_TEST(PyQgsGeometryAvoidIntersections test_qgsgeometry_avoid_intersections.py)
ADD_PYTHON_TEST(PyQgsVectorLayer test_qgsvectorlayer.py)
ADD_PYTHON_TEST(PyQgsRasterLayer test_qgsrasterlayer.py)
ADD_PYTHON_TEST(PyQgsBlendModes test_qgsblendmodes.py)

View File

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsGeometry avoid intersections functionality.
.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"""
__author__ = 'Martin Dobias'
__date__ = '20/08/2012'
__copyright__ = 'Copyright 2012, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
from qgis.core import *
from utilities import (getQgisTestApp,
TestCase,
unittest)
feat_wkt = [
"POLYGON((11564467.4556225873529911 1004058.0506714906077832,11574000.25562258623540401 1001788.33638577628880739,11573173.43113279156386852 993520.09148781711701304,11573092.36990830115973949 992709.47924291924573481,11572184.48419401608407497 986808.22210006206296384,11569460.82705115899443626 977275.42210006201639771,11570059.38887163810431957 972187.64662597852293402,11570368.71276544407010078 969558.39352863351814449,11567645.05562258698046207 962749.25067149056121707,11561743.79847973026335239 965472.9078143477672711,11562197.74133687280118465 972735.99352863349486142,11562651.68419401533901691 980453.02210006199311465,11562197.74133687280118465 991801.59352863347157836,11564467.4556225873529911 1004058.0506714906077832))",
"POLYGON((11585348.82705115899443626 1001788.33638577628880739,11595335.56990830041468143 1000426.50781434774398804,11598967.11276544444262981 994525.25067149056121707,11596726.68511567451059818 987803.96772218181286007,11594881.62705115787684917 982268.79352863342501223,11596210.5081571489572525 973631.06633969338145107,11596697.39847972989082336 970466.27924291917588562,11596243.4556225873529911 956847.99352863349486142,11589241.51456319727003574 957431.48861691600177437,11585348.82705115899443626 957755.87924291915260255,11583079.11276544444262981 965472.9078143477672711,11584503.3406173400580883 970315.28251079504843801,11585348.82705115899443626 973189.93638577638193965,11585802.76990830153226852 980906.96495720488019288,11585348.82705115899443626 991801.59352863347157836,11585348.82705115899443626 992546.05981434776913375,11585348.82705115899443626 999405.1363857762189582,11585348.82705115899443626 1001788.33638577628880739))",
"POLYGON((11613039.34133687242865562 1000880.45067149063106626,11623026.08419401571154594 998156.79352863342501223,11623933.96990830078721046 992709.47924291924573481,11623927.13341948762536049 992620.60488835815340281,11623026.08419401571154594 980906.96495720488019288,11623933.96990830078721046 971828.10781434772070497,11624841.85562258772552013 956847.99352863349486142,11613493.28419401496648788 957301.93638577638193965,11612131.4556225873529911 963657.13638577633537352,11612131.4556225873529911 972258.57086853496730328,11612131.4556225873529911 973189.93638577638193965,11612131.4556225873529911 983176.67924291919916868,11611885.51540027372539043 988095.48368919338099658,11611677.51276544481515884 992255.53638577635865659,11612251.3477884866297245 995889.82486503885593265,11613039.34133687242865562 1000880.45067149063106626))",
"POLYGON((11573173.43113279156386852 993520.09148781711701304,11576724.69587560556828976 993235.99030839197803289,11578337.0769300926476717 993106.99982403311878443,11579916.07919318228960037 992980.67964298592414707,11585348.82705115899443626 992546.05981434776913375,11585348.82705115899443626 991801.59352863347157836,11585802.76990830153226852 980906.96495720488019288,11585348.82705115899443626 973189.93638577638193965,11584503.3406173400580883 970315.28251079504843801,11577674.00869971700012684 971200.56627789419144392,11574530.55707954056560993 971608.05074717639945447,11570059.38887163810431957 972187.64662597852293402,11569460.82705115899443626 977275.42210006201639771,11572184.48419401608407497 986808.22210006206296384,11573092.36990830115973949 992709.47924291924573481,11573173.43113279156386852 993520.09148781711701304))",
"POLYGON((11596726.68511567451059818 987803.96772218181286007,11602386.29350295476615429 987912.80634501413442194,11606709.58121678233146667 987995.94649335695430636,11611885.51540027372539043 988095.48368919338099658,11612131.4556225873529911 983176.67924291919916868,11612131.4556225873529911 973189.93638577638193965,11612131.4556225873529911 972258.57086853496730328,11605332.19643574021756649 972844.7139018839225173,11601876.97527708858251572 973142.57779487106017768,11596210.5081571489572525 973631.06633969338145107,11594881.62705115787684917 982268.79352863342501223,11596726.68511567451059818 987803.96772218181286007))",
"MULTIPOLYGON(((11602386.29350295476615429 987912.80634501413442194,11602598.65562258660793304 994071.30781434779055417,11605393.122793298214674 993232.96766313433181494,11607138.08419401571154594 992709.47924291924573481,11606709.58121678233146667 987995.94649335695430636,11602386.29350295476615429 987912.80634501413442194)),((11605332.19643574021756649 972844.7139018839225173,11604868.36990830115973949 967742.62210006208624691,11603208.23603074997663498 967742.62210006208624691,11601690.76990830153226852 967742.62210006208624691,11601876.97527708858251572 973142.57779487106017768,11605332.19643574021756649 972844.7139018839225173)))",
"MULTIPOLYGON(((11576724.69587560556828976 993235.99030839197803289,11577177.85562258772552013 997702.85067149065434933,11578957.31162258610129356 997448.64267149078659713,11580355.4556225873529911 997248.9078143477672711,11579916.07919318228960037 992980.67964298592414707,11578337.0769300926476717 993106.99982403311878443,11576724.69587560556828976 993235.99030839197803289)),((11577674.00869971700012684 971200.56627789419144392,11577177.85562258772552013 966380.79352863342501223,11574519.0474593210965395 966380.79352863342501223,11574000.25562258623540401 966380.79352863342501223,11574530.55707954056560993 971608.05074717639945447,11577674.00869971700012684 971200.56627789419144392)))"
]
newg_wkt = "POLYGON((11556863.91276544518768787 1008143.53638577624224126,11632785.85562258586287498 1005192.9078143477672711,11633693.74133687280118465 996794.96495720488019288,11605776.25562258809804916 996794.96495720488019288,11604300.94133687205612659 962862.73638577631209046,11602712.14133687317371368 962976.22210006194654852,11603620.0270511582493782 996794.96495720488019288,11588299.4556225873529911 997929.82210006203968078,11588753.39847972989082336 961160.45067149063106626,11575362.08419401571154594 961841.36495720490347594,11577745.28419401682913303 1000199.53638577624224126,11557658.3127654455602169 1001107.42210006201639771,11556863.91276544518768787 1008143.53638577624224126))"
"""
Correct result (three parts):
MULTIPOLYGON(((11556863.91276544518768787 1008143.53638577624224126,11632785.85562258586287498 1005192.9078143477672711,11633693.74133687280118465 996794.96495720488019288,11623253.05562258698046207 996794.96495720488019288,11623026.08419401571154594 998156.79352863342501223,11613039.34133687242865562 1000880.45067149063106626,11612394.26464514434337616 996794.96495720488019288,11605776.25562258809804916 996794.96495720488019288,11605618.44716152176260948 993165.37035266752354801,11605393.122793298214674 993232.96766313433181494,11603539.33281490206718445 993789.104656653245911,11603620.0270511582493782 996794.96495720488019288,11597281.42645414359867573 997264.49092735419981182,11595335.56990830041468143 1000426.50781434774398804,11585348.82705115899443626 1001788.33638577628880739,11585348.82705115899443626 999405.1363857762189582,11585348.82705115899443626 992546.05981434776913375,11579916.07919318228960037 992980.67964298592414707,11580355.4556225873529911 997248.9078143477672711,11578957.31162258610129356 997448.64267149078659713,11577586.53731508925557137 997644.46757256181444973,11577745.28419401682913303 1000199.53638577624224126,11573858.94101548381149769 1000375.19031474948860705,11574000.25562258623540401 1001788.33638577628880739,11564467.4556225873529911 1004058.0506714906077832,11563869.05927479639649391 1000826.71039342461153865,11557658.3127654455602169 1001107.42210006201639771,11556863.91276544518768787 1008143.53638577624224126)),((11604513.11028097197413445 967742.62210006208624691,11604300.94133687205612659 962862.73638577631209046,11602712.14133687317371368 962976.22210006194654852,11602840.09838385321199894 967742.62210006208624691,11603208.23603074997663498 967742.62210006208624691,11604513.11028097197413445 967742.62210006208624691)),((11584280.59107660688459873 961387.88155639520846307,11575362.08419401571154594 961841.36495720490347594,11575644.1196969747543335 966380.79352863342501223,11577177.85562258772552013 966380.79352863342501223,11577674.00869971700012684 971200.56627789419144392,11584503.3406173400580883 970315.28251079504843801,11583079.11276544444262981 965472.9078143477672711,11584280.59107660688459873 961387.88155639520846307)))
Incorrect result from QGIS < 2.2 (four parts):
MULTIPOLYGON(((11556863.91276544518768787 1008143.53638577624224126,11632785.85562258586287498 1005192.9078143477672711,11633693.74133687280118465 996794.96495720488019288,11623253.05562258698046207 996794.96495720488019288,11623026.08419401571154594 998156.79352863342501223,11613039.34133687242865562 1000880.45067149063106626,11612394.26464514434337616 996794.96495720488019288,11605776.25562258809804916 996794.96495720488019288,11605618.44716151989996433 993165.37035266763996333,11605393.122793298214674 993232.96766313433181494,11603539.33281490206718445 993789.104656653245911,11603620.0270511582493782 996794.96495720488019288,11597281.42645414359867573 997264.49092735419981182,11595335.56990830041468143 1000426.50781434774398804,11585348.82705115899443626 1001788.33638577628880739,11585348.82705115899443626 999405.1363857762189582,11585348.82705115899443626 992546.05981434776913375,11579916.07919318228960037 992980.67964298592414707,11580355.4556225873529911 997248.9078143477672711,11578957.31162258610129356 997448.64267149078659713,11577586.53731508925557137 997644.46757256181444973,11577745.28419401682913303 1000199.53638577624224126,11573858.94101548381149769 1000375.19031474948860705,11574000.25562258623540401 1001788.33638577628880739,11564467.4556225873529911 1004058.0506714906077832,11563869.05927479639649391 1000826.71039342461153865,11557658.3127654455602169 1001107.42210006201639771,11556863.91276544518768787 1008143.53638577624224126)),((11578337.0769300926476717 993106.99982403311878443,11577309.72997828386723995 993189.18758017767686397,11577309.72997828386723995 993189.18758017779327929,11578337.0769300926476717 993106.99982403311878443)),((11604513.11028097197413445 967742.62210006208624691,11604300.94133687205612659 962862.73638577631209046,11602712.14133687317371368 962976.22210006194654852,11602840.09838385321199894 967742.62210006208624691,11603208.23603074997663498 967742.62210006208624691,11604513.11028097197413445 967742.62210006208624691)),((11584503.3406173400580883 970315.28251079504843801,11583079.11276544444262981 965472.9078143477672711,11584280.59107660688459873 961387.88155639520846307,11575362.08419401571154594 961841.36495720490347594,11575644.1196969747543335 966380.79352863342501223,11577177.85562258772552013 966380.79352863342501223,11577674.00869971700012684 971200.56627789419144392,11584503.3406173400580883 970315.28251079504843801)))
"""
QGISAPP, CANVAS, IFACE, PARENT = getQgisTestApp()
class TestQgsGeometryAvoidIntersections(TestCase):
def testNoSliverPolygons(self):
# create a layer with some polygons that will be used as a source for "avoid intersections"
l = QgsVectorLayer('MultiPolygon', 'test_layer', 'memory')
assert l.isValid()
QgsMapLayerRegistry.instance().addMapLayer(l)
QgsProject.instance().writeEntry( "Digitizing", "/AvoidIntersectionsList", [l.id()] )
features = []
for i,wkt in enumerate(feat_wkt):
f = QgsFeature(i+1)
f.setGeometry(QgsGeometry.fromWkt(wkt))
features.append(f)
l.dataProvider().addFeatures(features)
assert l.pendingFeatureCount() == 7
# create a geometry and remove its intersections with other geometries
g = QgsGeometry.fromWkt(newg_wkt)
assert g.avoidIntersections() == 2
# the resulting multi-polygon must have exactly three parts
# (in QGIS 2.0 it has one more tiny part that appears at the border between two of the original polygons)
mpg = g.asMultiPolygon()
assert len(mpg) == 3
if __name__ == '__main__':
unittest.main()