|
Quantum GIS API Documentation
master-ce49b66
|
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 }