Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsgeometryeditutils.cpp 3 : : ------------------------------------------------------------------- 4 : : Date : 21 Jan 2015 5 : : Copyright : (C) 2015 by Marco Hugentobler 6 : : email : marco.hugentobler at sourcepole dot com 7 : : *************************************************************************** 8 : : * * 9 : : * This program is free software; you can redistribute it and/or modify * 10 : : * it under the terms of the GNU General Public License as published by * 11 : : * the Free Software Foundation; either version 2 of the License, or * 12 : : * (at your option) any later version. * 13 : : * * 14 : : ***************************************************************************/ 15 : : 16 : : #include "qgsgeometryeditutils.h" 17 : : #include "qgsfeatureiterator.h" 18 : : #include "qgscurve.h" 19 : : #include "qgscurvepolygon.h" 20 : : #include "qgspolygon.h" 21 : : #include "qgsgeometryutils.h" 22 : : #include "qgsgeometry.h" 23 : : #include "qgsgeos.h" 24 : : #include "qgsmultisurface.h" 25 : : #include "qgsproject.h" 26 : : #include "qgsvectorlayer.h" 27 : : #include <limits> 28 : : 29 : 0 : QgsGeometry::OperationResult QgsGeometryEditUtils::addRing( QgsAbstractGeometry *geom, std::unique_ptr<QgsCurve> ring ) 30 : : { 31 : 0 : if ( !ring ) 32 : : { 33 : 0 : return QgsGeometry::InvalidInputGeometryType; 34 : : } 35 : : 36 : 0 : QVector< QgsCurvePolygon * > polygonList; 37 : 0 : QgsCurvePolygon *curvePoly = qgsgeometry_cast< QgsCurvePolygon * >( geom ); 38 : 0 : QgsGeometryCollection *multiGeom = qgsgeometry_cast< QgsGeometryCollection * >( geom ); 39 : 0 : if ( curvePoly ) 40 : : { 41 : 0 : polygonList.append( curvePoly ); 42 : 0 : } 43 : 0 : else if ( multiGeom ) 44 : : { 45 : 0 : polygonList.reserve( multiGeom->numGeometries() ); 46 : 0 : for ( int i = 0; i < multiGeom->numGeometries(); ++i ) 47 : : { 48 : 0 : polygonList.append( qgsgeometry_cast< QgsCurvePolygon * >( multiGeom->geometryN( i ) ) ); 49 : 0 : } 50 : 0 : } 51 : : else 52 : : { 53 : 0 : return QgsGeometry::OperationResult::InvalidInputGeometryType; //not polygon / multipolygon; 54 : : } 55 : : 56 : : //ring must be closed 57 : 0 : if ( !ring->isClosed() ) 58 : : { 59 : 0 : return QgsGeometry::OperationResult::AddRingNotClosed; 60 : : } 61 : 0 : else if ( !ring->isRing() ) 62 : : { 63 : 0 : return QgsGeometry::OperationResult::AddRingNotValid; 64 : : } 65 : : 66 : 0 : std::unique_ptr<QgsGeometryEngine> ringGeom( QgsGeometry::createGeometryEngine( ring.get() ) ); 67 : 0 : ringGeom->prepareGeometry(); 68 : : 69 : : //for each polygon, test if inside outer ring and no intersection with other interior ring 70 : 0 : QVector< QgsCurvePolygon * >::const_iterator polyIter = polygonList.constBegin(); 71 : 0 : for ( ; polyIter != polygonList.constEnd(); ++polyIter ) 72 : : { 73 : 0 : if ( ringGeom->within( *polyIter ) ) 74 : : { 75 : : //check if disjoint with other interior rings 76 : 0 : int nInnerRings = ( *polyIter )->numInteriorRings(); 77 : 0 : for ( int i = 0; i < nInnerRings; ++i ) 78 : : { 79 : 0 : if ( !ringGeom->disjoint( ( *polyIter )->interiorRing( i ) ) ) 80 : : { 81 : 0 : return QgsGeometry::OperationResult::AddRingCrossesExistingRings; 82 : : } 83 : 0 : } 84 : : 85 : : //make sure dimensionality of ring matches geometry 86 : 0 : if ( QgsWkbTypes::hasZ( geom->wkbType() ) ) 87 : 0 : ring->addZValue( 0 ); 88 : 0 : if ( QgsWkbTypes::hasM( geom->wkbType() ) ) 89 : 0 : ring->addMValue( 0 ); 90 : : 91 : 0 : ( *polyIter )->addInteriorRing( ring.release() ); 92 : 0 : return QgsGeometry::OperationResult::Success; //success 93 : : } 94 : 0 : } 95 : 0 : return QgsGeometry::OperationResult::AddRingNotInExistingFeature; //not contained in any outer ring 96 : 0 : } 97 : : 98 : 0 : QgsGeometry::OperationResult QgsGeometryEditUtils::addPart( QgsAbstractGeometry *geom, std::unique_ptr<QgsAbstractGeometry> part ) 99 : : { 100 : 0 : if ( !geom ) 101 : : { 102 : 0 : return QgsGeometry::OperationResult::InvalidBaseGeometry; 103 : : } 104 : : 105 : 0 : if ( !part ) 106 : : { 107 : 0 : return QgsGeometry::OperationResult::InvalidInputGeometryType; 108 : : } 109 : : 110 : : //multitype? 111 : 0 : QgsGeometryCollection *geomCollection = qgsgeometry_cast<QgsGeometryCollection *>( geom ); 112 : 0 : if ( !geomCollection ) 113 : : { 114 : 0 : return QgsGeometry::OperationResult::AddPartNotMultiGeometry; 115 : : } 116 : : 117 : 0 : bool added = false; 118 : 0 : if ( QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiSurface 119 : 0 : || QgsWkbTypes::flatType( geom->wkbType() ) == QgsWkbTypes::MultiPolygon ) 120 : : { 121 : 0 : QgsCurve *curve = qgsgeometry_cast<QgsCurve *>( part.get() ); 122 : : 123 : 0 : if ( curve && curve->isClosed() && curve->numPoints() >= 4 ) 124 : : { 125 : 0 : std::unique_ptr<QgsCurvePolygon> poly; 126 : 0 : if ( QgsWkbTypes::flatType( curve->wkbType() ) == QgsWkbTypes::LineString ) 127 : : { 128 : 0 : poly = std::make_unique< QgsPolygon >(); 129 : 0 : } 130 : : else 131 : : { 132 : 0 : poly = std::make_unique< QgsCurvePolygon >(); 133 : : } 134 : : // Ownership is still with part, curve points to the same object and is transferred 135 : : // to poly here. 136 : 0 : part.release(); 137 : 0 : poly->setExteriorRing( curve ); 138 : 0 : added = geomCollection->addGeometry( poly.release() ); 139 : 0 : } 140 : 0 : else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::Polygon 141 : 0 : || QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::Triangle 142 : 0 : || QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::CurvePolygon ) 143 : : { 144 : 0 : added = geomCollection->addGeometry( part.release() ); 145 : 0 : } 146 : 0 : else if ( QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::MultiPolygon 147 : 0 : || QgsWkbTypes::flatType( part->wkbType() ) == QgsWkbTypes::MultiSurface ) 148 : : { 149 : 0 : std::unique_ptr<QgsGeometryCollection> parts( static_cast<QgsGeometryCollection *>( part.release() ) ); 150 : : 151 : : int i; 152 : 0 : int n = geomCollection->numGeometries(); 153 : 0 : for ( i = 0; i < parts->numGeometries() && geomCollection->addGeometry( parts->geometryN( i )->clone() ); i++ ) 154 : : ; 155 : : 156 : 0 : added = i == parts->numGeometries(); 157 : 0 : if ( !added ) 158 : : { 159 : 0 : while ( geomCollection->numGeometries() > n ) 160 : 0 : geomCollection->removeGeometry( n ); 161 : 0 : return QgsGeometry::OperationResult::InvalidInputGeometryType; 162 : : } 163 : 0 : } 164 : : else 165 : : { 166 : 0 : return QgsGeometry::OperationResult::InvalidInputGeometryType; 167 : : } 168 : 0 : } 169 : : else 170 : : { 171 : 0 : added = geomCollection->addGeometry( part.release() ); 172 : : } 173 : 0 : return added ? QgsGeometry::Success : QgsGeometry::OperationResult::InvalidInputGeometryType; 174 : 0 : } 175 : : 176 : 0 : bool QgsGeometryEditUtils::deleteRing( QgsAbstractGeometry *geom, int ringNum, int partNum ) 177 : : { 178 : 0 : if ( !geom || partNum < 0 ) 179 : : { 180 : 0 : return false; 181 : : } 182 : : 183 : 0 : if ( ringNum < 1 ) //cannot remove exterior ring 184 : : { 185 : 0 : return false; 186 : : } 187 : : 188 : 0 : QgsAbstractGeometry *g = geom; 189 : 0 : QgsGeometryCollection *c = qgsgeometry_cast<QgsGeometryCollection *>( geom ); 190 : 0 : if ( c ) 191 : : { 192 : 0 : g = c->geometryN( partNum ); 193 : 0 : } 194 : 0 : else if ( partNum > 0 ) 195 : : { 196 : : //part num specified, but not a multi part geometry type 197 : 0 : return false; 198 : : } 199 : : 200 : 0 : QgsCurvePolygon *cpoly = qgsgeometry_cast<QgsCurvePolygon *>( g ); 201 : 0 : if ( !cpoly ) 202 : : { 203 : 0 : return false; 204 : : } 205 : : 206 : 0 : return cpoly->removeInteriorRing( ringNum - 1 ); 207 : 0 : } 208 : : 209 : 0 : bool QgsGeometryEditUtils::deletePart( QgsAbstractGeometry *geom, int partNum ) 210 : : { 211 : 0 : if ( !geom ) 212 : : { 213 : 0 : return false; 214 : : } 215 : : 216 : 0 : QgsGeometryCollection *c = qgsgeometry_cast<QgsGeometryCollection *>( geom ); 217 : 0 : if ( !c ) 218 : : { 219 : 0 : return false; 220 : : } 221 : : 222 : 0 : return c->removeGeometry( partNum ); 223 : 0 : } 224 : : 225 : 0 : std::unique_ptr<QgsAbstractGeometry> QgsGeometryEditUtils::avoidIntersections( const QgsAbstractGeometry &geom, 226 : : const QList<QgsVectorLayer *> &avoidIntersectionsLayers, 227 : : bool &haveInvalidGeometry, 228 : : const QHash<QgsVectorLayer *, QSet<QgsFeatureId> > &ignoreFeatures 229 : : ) 230 : : { 231 : : 232 : 0 : haveInvalidGeometry = false; 233 : 0 : std::unique_ptr<QgsGeometryEngine> geomEngine( QgsGeometry::createGeometryEngine( &geom ) ); 234 : 0 : if ( !geomEngine ) 235 : : { 236 : 0 : return nullptr; 237 : : } 238 : 0 : QgsWkbTypes::Type geomTypeBeforeModification = geom.wkbType(); 239 : : 240 : : 241 : : //check if g has polygon type 242 : 0 : if ( QgsWkbTypes::geometryType( geomTypeBeforeModification ) != QgsWkbTypes::PolygonGeometry ) 243 : : { 244 : 0 : return nullptr; 245 : : } 246 : : 247 : 0 : if ( avoidIntersectionsLayers.isEmpty() ) 248 : 0 : return nullptr; //no intersections stored in project does not mean error 249 : : 250 : 0 : QVector< QgsGeometry > nearGeometries; 251 : : 252 : : //go through list, convert each layer to vector layer and call QgsVectorLayer::removePolygonIntersections for each 253 : 0 : for ( QgsVectorLayer *currentLayer : avoidIntersectionsLayers ) 254 : : { 255 : 0 : QgsFeatureIds ignoreIds; 256 : 0 : QHash<QgsVectorLayer *, QSet<qint64> >::const_iterator ignoreIt = ignoreFeatures.constFind( currentLayer ); 257 : 0 : if ( ignoreIt != ignoreFeatures.constEnd() ) 258 : 0 : ignoreIds = ignoreIt.value(); 259 : : 260 : 0 : QgsFeatureIterator fi = currentLayer->getFeatures( QgsFeatureRequest( geom.boundingBox() ) 261 : 0 : .setFlags( QgsFeatureRequest::ExactIntersect ) 262 : 0 : .setNoAttributes() ); 263 : 0 : QgsFeature f; 264 : 0 : while ( fi.nextFeature( f ) ) 265 : : { 266 : 0 : if ( ignoreIds.contains( f.id() ) ) 267 : 0 : continue; 268 : : 269 : 0 : if ( !f.hasGeometry() ) 270 : 0 : continue; 271 : : 272 : 0 : if ( !f.geometry().isGeosValid() ) 273 : 0 : haveInvalidGeometry = true; 274 : : 275 : 0 : nearGeometries << f.geometry(); 276 : : } 277 : 0 : } 278 : : 279 : 0 : if ( nearGeometries.isEmpty() ) 280 : : { 281 : 0 : return nullptr; 282 : : } 283 : : 284 : 0 : std::unique_ptr< QgsAbstractGeometry > combinedGeometries( geomEngine->combine( nearGeometries ) ); 285 : 0 : if ( !combinedGeometries ) 286 : : { 287 : 0 : return nullptr; 288 : : } 289 : : 290 : 0 : std::unique_ptr< QgsAbstractGeometry > diffGeom( geomEngine->difference( combinedGeometries.get() ) ); 291 : : 292 : 0 : return diffGeom; 293 : 0 : }