QGIS API Documentation  master-3f58142
src/core/qgsvectorlayereditutils.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002     qgsvectorlayereditutils.cpp
00003     ---------------------
00004     begin                : Dezember 2012
00005     copyright            : (C) 2012 by Martin Dobias
00006     email                : wonder dot sk at gmail dot com
00007  ***************************************************************************
00008  *                                                                         *
00009  *   This program is free software; you can redistribute it and/or modify  *
00010  *   it under the terms of the GNU General Public License as published by  *
00011  *   the Free Software Foundation; either version 2 of the License, or     *
00012  *   (at your option) any later version.                                   *
00013  *                                                                         *
00014  ***************************************************************************/
00015 #include "qgsvectorlayereditutils.h"
00016 
00017 #include "qgsvectordataprovider.h"
00018 #include "qgsgeometrycache.h"
00019 #include "qgsvectorlayereditbuffer.h"
00020 
00021 #include <limits>
00022 
00023 
00024 QgsVectorLayerEditUtils::QgsVectorLayerEditUtils( QgsVectorLayer* layer )
00025     : L( layer )
00026 {
00027 }
00028 
00029 bool QgsVectorLayerEditUtils::insertVertex( double x, double y, QgsFeatureId atFeatureId, int beforeVertex )
00030 {
00031   if ( !L->hasGeometryType() )
00032     return false;
00033 
00034   QgsGeometry geometry;
00035   if ( !cache()->geometry( atFeatureId, geometry ) )
00036     return false;   // TODO: support also uncached geometries
00037 
00038   geometry.insertVertex( x, y, beforeVertex );
00039 
00040   L->editBuffer()->changeGeometry( atFeatureId, &geometry );
00041   return true;
00042 }
00043 
00044 
00045 bool QgsVectorLayerEditUtils::moveVertex( double x, double y, QgsFeatureId atFeatureId, int atVertex )
00046 {
00047   if ( !L->hasGeometryType() )
00048     return false;
00049 
00050   QgsGeometry geometry;
00051   if ( !cache()->geometry( atFeatureId, geometry ) )
00052     return false;   // TODO: support also uncached geometries
00053 
00054   geometry.moveVertex( x, y, atVertex );
00055 
00056   L->editBuffer()->changeGeometry( atFeatureId, &geometry );
00057   return true;
00058 }
00059 
00060 
00061 bool QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId atFeatureId, int atVertex )
00062 {
00063   if ( !L->hasGeometryType() )
00064     return false;
00065 
00066   QgsGeometry geometry;
00067   if ( !cache()->geometry( atFeatureId, geometry ) )
00068     return false;   // TODO: support also uncached geometries
00069 
00070   if ( !geometry.deleteVertex( atVertex ) )
00071     return false;
00072 
00073   L->editBuffer()->changeGeometry( atFeatureId, &geometry );
00074   return true;
00075 }
00076 
00077 
00078 int QgsVectorLayerEditUtils::addRing( const QList<QgsPoint>& ring )
00079 {
00080   if ( !L->hasGeometryType() )
00081     return 5;
00082 
00083   int addRingReturnCode = 5; //default: return code for 'ring not inserted'
00084   double xMin, yMin, xMax, yMax;
00085   QgsRectangle bBox;
00086 
00087   if ( boundingBoxFromPointList( ring, xMin, yMin, xMax, yMax ) == 0 )
00088   {
00089     bBox.setXMinimum( xMin ); bBox.setYMinimum( yMin );
00090     bBox.setXMaximum( xMax ); bBox.setYMaximum( yMax );
00091   }
00092   else
00093   {
00094     return 3; //ring not valid
00095   }
00096 
00097   QgsFeatureIterator fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
00098 
00099   QgsFeature f;
00100   while ( fit.nextFeature( f ) )
00101   {
00102     addRingReturnCode = f.geometry()->addRing( ring );
00103     if ( addRingReturnCode == 0 )
00104     {
00105       L->editBuffer()->changeGeometry( f.id(), f.geometry() );
00106 
00107       //setModified( true, true );
00108       break;
00109     }
00110   }
00111 
00112   return addRingReturnCode;
00113 }
00114 
00115 
00116 int QgsVectorLayerEditUtils::addPart( const QList<QgsPoint> &points, QgsFeatureId featureId )
00117 {
00118   if ( !L->hasGeometryType() )
00119     return 6;
00120 
00121   QgsGeometry geometry;
00122   if ( !cache()->geometry( featureId, geometry ) ) // maybe it's in cache
00123   {
00124     // it's not in cache: let's fetch it from layer
00125     QgsFeature f;
00126     if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.geometry() )
00127       return 6; //geometry not found
00128 
00129     geometry = *f.geometry();
00130   }
00131 
00132   int errorCode = geometry.addPart( points );
00133   if ( errorCode == 0 )
00134   {
00135     L->editBuffer()->changeGeometry( featureId, &geometry );
00136   }
00137   return errorCode;
00138 }
00139 
00140 
00141 
00142 int QgsVectorLayerEditUtils::translateFeature( QgsFeatureId featureId, double dx, double dy )
00143 {
00144   if ( !L->hasGeometryType() )
00145     return 1;
00146 
00147   QgsGeometry geometry;
00148   if ( !cache()->geometry( featureId, geometry ) ) // maybe it's in cache
00149   {
00150     // it's not in cache: let's fetch it from layer
00151     QgsFeature f;
00152     if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.geometry() )
00153       return 1; //geometry not found
00154 
00155     geometry = *f.geometry();
00156   }
00157 
00158   int errorCode = geometry.translate( dx, dy );
00159   if ( errorCode == 0 )
00160   {
00161     L->editBuffer()->changeGeometry( featureId, &geometry );
00162   }
00163   return errorCode;
00164 }
00165 
00166 
00167 int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPoint>& splitLine, bool topologicalEditing )
00168 {
00169   if ( !L->hasGeometryType() )
00170     return 4;
00171 
00172   QgsFeatureList newFeatures; //store all the newly created features
00173   double xMin, yMin, xMax, yMax;
00174   QgsRectangle bBox; //bounding box of the split line
00175   int returnCode = 0;
00176   int splitFunctionReturn; //return code of QgsGeometry::splitGeometry
00177   int numberOfSplittedFeatures = 0;
00178 
00179   QgsFeatureList featureList;
00180   const QgsFeatureIds selectedIds = L->selectedFeaturesIds();
00181 
00182   if ( selectedIds.size() > 0 ) //consider only the selected features if there is a selection
00183   {
00184     featureList = L->selectedFeatures();
00185   }
00186   else //else consider all the feature that intersect the bounding box of the split line
00187   {
00188     if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) == 0 )
00189     {
00190       bBox.setXMinimum( xMin ); bBox.setYMinimum( yMin );
00191       bBox.setXMaximum( xMax ); bBox.setYMaximum( yMax );
00192     }
00193     else
00194     {
00195       return 1;
00196     }
00197 
00198     if ( bBox.isEmpty() )
00199     {
00200       //if the bbox is a line, try to make a square out of it
00201       if ( bBox.width() == 0.0 && bBox.height() > 0 )
00202       {
00203         bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 );
00204         bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 );
00205       }
00206       else if ( bBox.height() == 0.0 && bBox.width() > 0 )
00207       {
00208         bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 );
00209         bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 );
00210       }
00211       else
00212       {
00213         return 2;
00214       }
00215     }
00216 
00217     QgsFeatureIterator fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
00218 
00219     QgsFeature f;
00220     while ( fit.nextFeature( f ) )
00221       featureList << QgsFeature( f );
00222   }
00223 
00224   QgsFeatureList::iterator select_it = featureList.begin();
00225   for ( ; select_it != featureList.end(); ++select_it )
00226   {
00227     QList<QgsGeometry*> newGeometries;
00228     QList<QgsPoint> topologyTestPoints;
00229     QgsGeometry* newGeometry = 0;
00230     splitFunctionReturn = select_it->geometry()->splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints );
00231     if ( splitFunctionReturn == 0 )
00232     {
00233       //change this geometry
00234       L->editBuffer()->changeGeometry( select_it->id(), select_it->geometry() );
00235 
00236       //insert new features
00237       for ( int i = 0; i < newGeometries.size(); ++i )
00238       {
00239         newGeometry = newGeometries.at( i );
00240         QgsFeature newFeature;
00241         newFeature.setGeometry( newGeometry );
00242 
00243         //use default value where possible (primary key issue), otherwise the value from the original (split) feature
00244         QgsAttributes newAttributes = select_it->attributes();
00245         QVariant defaultValue;
00246         for ( int j = 0; j < newAttributes.count(); ++j )
00247         {
00248           defaultValue = L->dataProvider()->defaultValue( j );
00249           if ( !defaultValue.isNull() )
00250           {
00251             newAttributes[ j ] = defaultValue;
00252           }
00253         }
00254 
00255         newFeature.setAttributes( newAttributes );
00256 
00257         newFeatures.append( newFeature );
00258       }
00259 
00260       if ( topologicalEditing )
00261       {
00262         QList<QgsPoint>::const_iterator topol_it = topologyTestPoints.constBegin();
00263         for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it )
00264         {
00265           addTopologicalPoints( *topol_it );
00266         }
00267       }
00268       ++numberOfSplittedFeatures;
00269     }
00270     else if ( splitFunctionReturn > 1 ) //1 means no split but also no error
00271     {
00272       returnCode = splitFunctionReturn;
00273     }
00274   }
00275 
00276   if ( numberOfSplittedFeatures == 0 && selectedIds.size() > 0 )
00277   {
00278     //There is a selection but no feature has been split.
00279     //Maybe user forgot that only the selected features are split
00280     returnCode = 4;
00281   }
00282 
00283 
00284   //now add the new features to this vectorlayer
00285   L->editBuffer()->addFeatures( newFeatures );
00286 
00287   return returnCode;
00288 }
00289 
00290 
00291 
00292 int QgsVectorLayerEditUtils::addTopologicalPoints( QgsGeometry* geom )
00293 {
00294   if ( !L->hasGeometryType() )
00295     return 1;
00296 
00297   if ( !geom )
00298   {
00299     return 1;
00300   }
00301 
00302   int returnVal = 0;
00303 
00304   QGis::WkbType wkbType = geom->wkbType();
00305 
00306   switch ( wkbType )
00307   {
00308       //line
00309     case QGis::WKBLineString25D:
00310     case QGis::WKBLineString:
00311     {
00312       QgsPolyline theLine = geom->asPolyline();
00313       QgsPolyline::const_iterator line_it = theLine.constBegin();
00314       for ( ; line_it != theLine.constEnd(); ++line_it )
00315       {
00316         if ( addTopologicalPoints( *line_it ) != 0 )
00317         {
00318           returnVal = 2;
00319         }
00320       }
00321       break;
00322     }
00323 
00324     //multiline
00325     case QGis::WKBMultiLineString25D:
00326     case QGis::WKBMultiLineString:
00327     {
00328       QgsMultiPolyline theMultiLine = geom->asMultiPolyline();
00329       QgsPolyline currentPolyline;
00330 
00331       for ( int i = 0; i < theMultiLine.size(); ++i )
00332       {
00333         QgsPolyline::const_iterator line_it = currentPolyline.constBegin();
00334         for ( ; line_it != currentPolyline.constEnd(); ++line_it )
00335         {
00336           if ( addTopologicalPoints( *line_it ) != 0 )
00337           {
00338             returnVal = 2;
00339           }
00340         }
00341       }
00342       break;
00343     }
00344 
00345     //polygon
00346     case QGis::WKBPolygon25D:
00347     case QGis::WKBPolygon:
00348     {
00349       QgsPolygon thePolygon = geom->asPolygon();
00350       QgsPolyline currentRing;
00351 
00352       for ( int i = 0; i < thePolygon.size(); ++i )
00353       {
00354         currentRing = thePolygon.at( i );
00355         QgsPolyline::const_iterator line_it = currentRing.constBegin();
00356         for ( ; line_it != currentRing.constEnd(); ++line_it )
00357         {
00358           if ( addTopologicalPoints( *line_it ) != 0 )
00359           {
00360             returnVal = 2;
00361           }
00362         }
00363       }
00364       break;
00365     }
00366 
00367     //multipolygon
00368     case QGis::WKBMultiPolygon25D:
00369     case QGis::WKBMultiPolygon:
00370     {
00371       QgsMultiPolygon theMultiPolygon = geom->asMultiPolygon();
00372       QgsPolygon currentPolygon;
00373       QgsPolyline currentRing;
00374 
00375       for ( int i = 0; i < theMultiPolygon.size(); ++i )
00376       {
00377         currentPolygon = theMultiPolygon.at( i );
00378         for ( int j = 0; j < currentPolygon.size(); ++j )
00379         {
00380           currentRing = currentPolygon.at( j );
00381           QgsPolyline::const_iterator line_it = currentRing.constBegin();
00382           for ( ; line_it != currentRing.constEnd(); ++line_it )
00383           {
00384             if ( addTopologicalPoints( *line_it ) != 0 )
00385             {
00386               returnVal = 2;
00387             }
00388           }
00389         }
00390       }
00391       break;
00392     }
00393     default:
00394       break;
00395   }
00396   return returnVal;
00397 }
00398 
00399 
00400 int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPoint& p )
00401 {
00402   if ( !L->hasGeometryType() )
00403     return 1;
00404 
00405   QMultiMap<double, QgsSnappingResult> snapResults; //results from the snapper object
00406   //we also need to snap to vertex to make sure the vertex does not already exist in this geometry
00407   QMultiMap<double, QgsSnappingResult> vertexSnapResults;
00408 
00409   QList<QgsSnappingResult> filteredSnapResults; //we filter out the results that are on existing vertices
00410 
00411   //work with a tolerance because coordinate projection may introduce some rounding
00412   double threshold =  0.0000001;
00413   if ( L->crs().mapUnits() == QGis::Meters )
00414   {
00415     threshold = 0.001;
00416   }
00417   else if ( L->crs().mapUnits() == QGis::Feet )
00418   {
00419     threshold = 0.0001;
00420   }
00421 
00422 
00423   if ( L->snapWithContext( p, threshold, snapResults, QgsSnapper::SnapToSegment ) != 0 )
00424   {
00425     return 2;
00426   }
00427 
00428   QMultiMap<double, QgsSnappingResult>::const_iterator snap_it = snapResults.constBegin();
00429   QMultiMap<double, QgsSnappingResult>::const_iterator vertex_snap_it;
00430   for ( ; snap_it != snapResults.constEnd(); ++snap_it )
00431   {
00432     //test if p is already a vertex of this geometry. If yes, don't insert it
00433     bool vertexAlreadyExists = false;
00434     if ( L->snapWithContext( p, threshold, vertexSnapResults, QgsSnapper::SnapToVertex ) != 0 )
00435     {
00436       continue;
00437     }
00438 
00439     vertex_snap_it = vertexSnapResults.constBegin();
00440     for ( ; vertex_snap_it != vertexSnapResults.constEnd(); ++vertex_snap_it )
00441     {
00442       if ( snap_it.value().snappedAtGeometry == vertex_snap_it.value().snappedAtGeometry )
00443       {
00444         vertexAlreadyExists = true;
00445       }
00446     }
00447 
00448     if ( !vertexAlreadyExists )
00449     {
00450       filteredSnapResults.push_back( *snap_it );
00451     }
00452   }
00453   insertSegmentVerticesForSnap( filteredSnapResults );
00454   return 0;
00455 }
00456 
00457 
00458 int QgsVectorLayerEditUtils::insertSegmentVerticesForSnap( const QList<QgsSnappingResult>& snapResults )
00459 {
00460   if ( !L->hasGeometryType() )
00461     return 1;
00462 
00463   int returnval = 0;
00464   QgsPoint layerPoint;
00465 
00466   QList<QgsSnappingResult>::const_iterator it = snapResults.constBegin();
00467   for ( ; it != snapResults.constEnd(); ++it )
00468   {
00469     if ( it->snappedVertexNr == -1 ) // segment snap
00470     {
00471       layerPoint = it->snappedVertex;
00472       if ( !insertVertex( layerPoint.x(), layerPoint.y(), it->snappedAtGeometry, it->afterVertexNr ) )
00473       {
00474         returnval = 3;
00475       }
00476     }
00477   }
00478   return returnval;
00479 }
00480 
00481 
00482 
00483 
00484 int QgsVectorLayerEditUtils::boundingBoxFromPointList( const QList<QgsPoint>& list, double& xmin, double& ymin, double& xmax, double& ymax ) const
00485 {
00486   if ( list.size() < 1 )
00487   {
00488     return 1;
00489   }
00490 
00491   xmin = std::numeric_limits<double>::max();
00492   xmax = -std::numeric_limits<double>::max();
00493   ymin = std::numeric_limits<double>::max();
00494   ymax = -std::numeric_limits<double>::max();
00495 
00496   for ( QList<QgsPoint>::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
00497   {
00498     if ( it->x() < xmin )
00499     {
00500       xmin = it->x();
00501     }
00502     if ( it->x() > xmax )
00503     {
00504       xmax = it->x();
00505     }
00506     if ( it->y() < ymin )
00507     {
00508       ymin = it->y();
00509     }
00510     if ( it->y() > ymax )
00511     {
00512       ymax = it->y();
00513     }
00514   }
00515 
00516   return 0;
00517 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines