mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-28 00:05:04 -04:00
794 lines
22 KiB
C++
794 lines
22 KiB
C++
/***************************************************************************
|
|
qgsvectorlayereditutils.cpp
|
|
---------------------
|
|
begin : Dezember 2012
|
|
copyright : (C) 2012 by Martin Dobias
|
|
email : wonder dot sk at gmail dot com
|
|
***************************************************************************
|
|
* *
|
|
* 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. *
|
|
* *
|
|
***************************************************************************/
|
|
#include "qgsvectorlayereditutils.h"
|
|
|
|
#include "qgsvectordataprovider.h"
|
|
#include "qgsfeatureiterator.h"
|
|
#include "qgsgeometrycache.h"
|
|
#include "qgsvectorlayereditbuffer.h"
|
|
#include "qgslinestringv2.h"
|
|
#include "qgslogger.h"
|
|
#include "qgspointv2.h"
|
|
#include "qgsgeometryfactory.h"
|
|
#include "qgis.h"
|
|
#include "qgswkbtypes.h"
|
|
|
|
#include <limits>
|
|
|
|
|
|
QgsVectorLayerEditUtils::QgsVectorLayerEditUtils( QgsVectorLayer* layer )
|
|
: L( layer )
|
|
{
|
|
}
|
|
|
|
bool QgsVectorLayerEditUtils::insertVertex( double x, double y, QgsFeatureId atFeatureId, int beforeVertex )
|
|
{
|
|
if ( !L->hasGeometryType() )
|
|
return false;
|
|
|
|
QgsGeometry geometry;
|
|
if ( !cache()->geometry( atFeatureId, geometry ) )
|
|
{
|
|
// it's not in cache: let's fetch it from layer
|
|
QgsFeature f;
|
|
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
|
|
return false; // geometry not found
|
|
|
|
geometry = f.geometry();
|
|
}
|
|
|
|
geometry.insertVertex( x, y, beforeVertex );
|
|
|
|
L->editBuffer()->changeGeometry( atFeatureId, geometry );
|
|
return true;
|
|
}
|
|
|
|
|
|
bool QgsVectorLayerEditUtils::moveVertex( double x, double y, QgsFeatureId atFeatureId, int atVertex )
|
|
{
|
|
QgsPointV2 p( x, y );
|
|
return moveVertex( p, atFeatureId, atVertex );
|
|
}
|
|
|
|
bool QgsVectorLayerEditUtils::moveVertex( const QgsPointV2& p, QgsFeatureId atFeatureId, int atVertex )
|
|
{
|
|
if ( !L->hasGeometryType() )
|
|
return false;
|
|
|
|
QgsGeometry geometry;
|
|
if ( !cache()->geometry( atFeatureId, geometry ) )
|
|
{
|
|
// it's not in cache: let's fetch it from layer
|
|
QgsFeature f;
|
|
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( atFeatureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
|
|
return false; // geometry not found
|
|
|
|
geometry = f.geometry();
|
|
}
|
|
|
|
geometry.moveVertex( p, atVertex );
|
|
|
|
L->editBuffer()->changeGeometry( atFeatureId, geometry );
|
|
return true;
|
|
}
|
|
|
|
|
|
QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId featureId, int vertex )
|
|
{
|
|
if ( !L->hasGeometryType() )
|
|
return QgsVectorLayer::InvalidLayer;
|
|
|
|
QgsGeometry geometry;
|
|
if ( !cache()->geometry( featureId, geometry ) )
|
|
{
|
|
// it's not in cache: let's fetch it from layer
|
|
QgsFeature f;
|
|
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
|
|
return QgsVectorLayer::FetchFeatureFailed; // geometry not found
|
|
|
|
geometry = f.geometry();
|
|
}
|
|
|
|
if ( !geometry.deleteVertex( vertex ) )
|
|
return QgsVectorLayer::EditFailed;
|
|
|
|
if ( geometry.geometry() && geometry.geometry()->nCoordinates() == 0 )
|
|
{
|
|
//last vertex deleted, set geometry to null
|
|
geometry.setGeometry( nullptr );
|
|
}
|
|
|
|
L->editBuffer()->changeGeometry( featureId, geometry );
|
|
return !geometry.isEmpty() ? QgsVectorLayer::Success : QgsVectorLayer::EmptyGeometry;
|
|
}
|
|
|
|
int QgsVectorLayerEditUtils::addRing( const QList<QgsPoint>& ring, const QgsFeatureIds& targetFeatureIds, QgsFeatureId* modifiedFeatureId )
|
|
{
|
|
QgsLineString* ringLine = new QgsLineString();
|
|
QgsPointSequence ringPoints;
|
|
QList<QgsPoint>::const_iterator ringIt = ring.constBegin();
|
|
for ( ; ringIt != ring.constEnd(); ++ringIt )
|
|
{
|
|
ringPoints.append( QgsPointV2( ringIt->x(), ringIt->y() ) );
|
|
}
|
|
ringLine->setPoints( ringPoints );
|
|
return addRing( ringLine, targetFeatureIds, modifiedFeatureId );
|
|
}
|
|
|
|
int QgsVectorLayerEditUtils::addRing( QgsCurve* ring, const QgsFeatureIds& targetFeatureIds, QgsFeatureId* modifiedFeatureId )
|
|
{
|
|
if ( !L->hasGeometryType() )
|
|
{
|
|
delete ring;
|
|
return 5;
|
|
}
|
|
|
|
int addRingReturnCode = 5; //default: return code for 'ring not inserted'
|
|
QgsFeature f;
|
|
|
|
QgsFeatureIterator fit;
|
|
if ( !targetFeatureIds.isEmpty() )
|
|
{
|
|
//check only specified features
|
|
fit = L->getFeatures( QgsFeatureRequest().setFilterFids( targetFeatureIds ) );
|
|
}
|
|
else
|
|
{
|
|
//check all intersecting features
|
|
QgsRectangle bBox = ring->boundingBox();
|
|
fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
|
|
}
|
|
|
|
//find first valid feature we can add the ring to
|
|
while ( fit.nextFeature( f ) )
|
|
{
|
|
if ( !f.hasGeometry() )
|
|
continue;
|
|
|
|
//add ring takes ownership of ring, and deletes it if there's an error
|
|
QgsGeometry g = f.geometry();
|
|
|
|
addRingReturnCode = g.addRing( static_cast< QgsCurve* >( ring->clone() ) );
|
|
if ( addRingReturnCode == 0 )
|
|
{
|
|
L->editBuffer()->changeGeometry( f.id(), g );
|
|
if ( modifiedFeatureId )
|
|
*modifiedFeatureId = f.id();
|
|
|
|
//setModified( true, true );
|
|
break;
|
|
}
|
|
}
|
|
|
|
delete ring;
|
|
return addRingReturnCode;
|
|
}
|
|
|
|
int QgsVectorLayerEditUtils::addPart( const QList<QgsPoint> &points, QgsFeatureId featureId )
|
|
{
|
|
QgsPointSequence l;
|
|
for ( QList<QgsPoint>::const_iterator it = points.constBegin(); it != points.constEnd(); ++it )
|
|
{
|
|
l << QgsPointV2( *it );
|
|
}
|
|
return addPart( l, featureId );
|
|
}
|
|
|
|
int QgsVectorLayerEditUtils::addPart( const QgsPointSequence &points, QgsFeatureId featureId )
|
|
{
|
|
if ( !L->hasGeometryType() )
|
|
return 6;
|
|
|
|
QgsGeometry geometry;
|
|
bool firstPart = false;
|
|
if ( !cache()->geometry( featureId, geometry ) ) // maybe it's in cache
|
|
{
|
|
// it's not in cache: let's fetch it from layer
|
|
QgsFeature f;
|
|
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) )
|
|
return 6; //not found
|
|
|
|
if ( !f.hasGeometry() )
|
|
{
|
|
//no existing geometry, so adding first part to null geometry
|
|
firstPart = true;
|
|
}
|
|
else
|
|
{
|
|
geometry = f.geometry();
|
|
}
|
|
}
|
|
|
|
int errorCode = geometry.addPart( points, L->geometryType() ) ;
|
|
if ( errorCode == 0 )
|
|
{
|
|
if ( firstPart && QgsWkbTypes::isSingleType( L->wkbType() )
|
|
&& L->dataProvider()->doesStrictFeatureTypeCheck() )
|
|
{
|
|
//convert back to single part if required by layer
|
|
geometry.convertToSingleType();
|
|
}
|
|
L->editBuffer()->changeGeometry( featureId, geometry );
|
|
}
|
|
return errorCode;
|
|
}
|
|
|
|
int QgsVectorLayerEditUtils::addPart( QgsCurve* ring, QgsFeatureId featureId )
|
|
{
|
|
if ( !L->hasGeometryType() )
|
|
return 6;
|
|
|
|
QgsGeometry geometry;
|
|
bool firstPart = false;
|
|
if ( !cache()->geometry( featureId, geometry ) ) // maybe it's in cache
|
|
{
|
|
// it's not in cache: let's fetch it from layer
|
|
QgsFeature f;
|
|
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) )
|
|
return 6; //not found
|
|
|
|
if ( !f.hasGeometry() )
|
|
{
|
|
//no existing geometry, so adding first part to null geometry
|
|
firstPart = true;
|
|
}
|
|
else
|
|
{
|
|
geometry = f.geometry();
|
|
}
|
|
}
|
|
|
|
int errorCode = geometry.addPart( ring, L->geometryType() ) ;
|
|
if ( errorCode == 0 )
|
|
{
|
|
if ( firstPart && QgsWkbTypes::isSingleType( L->wkbType() )
|
|
&& L->dataProvider()->doesStrictFeatureTypeCheck() )
|
|
{
|
|
//convert back to single part if required by layer
|
|
geometry.convertToSingleType();
|
|
}
|
|
L->editBuffer()->changeGeometry( featureId, geometry );
|
|
}
|
|
return errorCode;
|
|
}
|
|
|
|
|
|
int QgsVectorLayerEditUtils::translateFeature( QgsFeatureId featureId, double dx, double dy )
|
|
{
|
|
if ( !L->hasGeometryType() )
|
|
return 1;
|
|
|
|
QgsGeometry geometry;
|
|
if ( !cache()->geometry( featureId, geometry ) ) // maybe it's in cache
|
|
{
|
|
// it's not in cache: let's fetch it from layer
|
|
QgsFeature f;
|
|
if ( !L->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setSubsetOfAttributes( QgsAttributeList() ) ).nextFeature( f ) || !f.hasGeometry() )
|
|
return 1; //geometry not found
|
|
|
|
geometry = f.geometry();
|
|
}
|
|
|
|
int errorCode = geometry.translate( dx, dy );
|
|
if ( errorCode == 0 )
|
|
{
|
|
L->editBuffer()->changeGeometry( featureId, geometry );
|
|
}
|
|
return errorCode;
|
|
}
|
|
|
|
|
|
int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPoint>& splitLine, bool topologicalEditing )
|
|
{
|
|
if ( !L->hasGeometryType() )
|
|
return 4;
|
|
|
|
QgsFeatureList newFeatures; //store all the newly created features
|
|
double xMin, yMin, xMax, yMax;
|
|
QgsRectangle bBox; //bounding box of the split line
|
|
int returnCode = 0;
|
|
int splitFunctionReturn; //return code of QgsGeometry::splitGeometry
|
|
int numberOfSplittedFeatures = 0;
|
|
|
|
QgsFeatureIterator features;
|
|
const QgsFeatureIds selectedIds = L->selectedFeaturesIds();
|
|
|
|
if ( !selectedIds.isEmpty() ) //consider only the selected features if there is a selection
|
|
{
|
|
features = L->selectedFeaturesIterator();
|
|
}
|
|
else //else consider all the feature that intersect the bounding box of the split line
|
|
{
|
|
if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) == 0 )
|
|
{
|
|
bBox.setXMinimum( xMin );
|
|
bBox.setYMinimum( yMin );
|
|
bBox.setXMaximum( xMax );
|
|
bBox.setYMaximum( yMax );
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if ( bBox.isEmpty() )
|
|
{
|
|
//if the bbox is a line, try to make a square out of it
|
|
if ( bBox.width() == 0.0 && bBox.height() > 0 )
|
|
{
|
|
bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 );
|
|
bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 );
|
|
}
|
|
else if ( bBox.height() == 0.0 && bBox.width() > 0 )
|
|
{
|
|
bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 );
|
|
bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 );
|
|
}
|
|
else
|
|
{
|
|
//If we have a single point, we still create a non-null box
|
|
double bufferDistance = 0.000001;
|
|
if ( L->crs().isGeographic() )
|
|
bufferDistance = 0.00000001;
|
|
bBox.setXMinimum( bBox.xMinimum() - bufferDistance );
|
|
bBox.setXMaximum( bBox.xMaximum() + bufferDistance );
|
|
bBox.setYMinimum( bBox.yMinimum() - bufferDistance );
|
|
bBox.setYMaximum( bBox.yMaximum() + bufferDistance );
|
|
}
|
|
}
|
|
|
|
features = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
|
|
}
|
|
|
|
QgsFeature feat;
|
|
while ( features.nextFeature( feat ) )
|
|
{
|
|
if ( !feat.hasGeometry() )
|
|
{
|
|
continue;
|
|
}
|
|
QList<QgsGeometry*> newGeometries;
|
|
QList<QgsPoint> topologyTestPoints;
|
|
QgsGeometry* newGeometry = nullptr;
|
|
QgsGeometry featureGeom = feat.geometry();
|
|
splitFunctionReturn = featureGeom.splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints );
|
|
if ( splitFunctionReturn == 0 )
|
|
{
|
|
//change this geometry
|
|
L->editBuffer()->changeGeometry( feat.id(), featureGeom );
|
|
|
|
//insert new features
|
|
for ( int i = 0; i < newGeometries.size(); ++i )
|
|
{
|
|
newGeometry = newGeometries.at( i );
|
|
QgsFeature newFeature;
|
|
newFeature.setGeometry( *newGeometry );
|
|
|
|
//use default value where possible for primary key (e.g. autoincrement),
|
|
//and use the value from the original (split) feature if not primary key
|
|
QgsAttributes newAttributes = feat.attributes();
|
|
Q_FOREACH ( int pkIdx, L->dataProvider()->pkAttributeIndexes() )
|
|
{
|
|
const QVariant defaultValue = L->dataProvider()->defaultValue( pkIdx );
|
|
if ( !defaultValue.isNull() )
|
|
{
|
|
newAttributes[ pkIdx ] = defaultValue;
|
|
}
|
|
else //try with NULL
|
|
{
|
|
newAttributes[ pkIdx ] = QVariant();
|
|
}
|
|
}
|
|
|
|
newFeature.setAttributes( newAttributes );
|
|
|
|
newFeatures.append( newFeature );
|
|
}
|
|
|
|
if ( topologicalEditing )
|
|
{
|
|
QList<QgsPoint>::const_iterator topol_it = topologyTestPoints.constBegin();
|
|
for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it )
|
|
{
|
|
addTopologicalPoints( *topol_it );
|
|
}
|
|
}
|
|
++numberOfSplittedFeatures;
|
|
}
|
|
else if ( splitFunctionReturn > 1 ) //1 means no split but also no error
|
|
{
|
|
returnCode = splitFunctionReturn;
|
|
}
|
|
}
|
|
|
|
if ( numberOfSplittedFeatures == 0 && !selectedIds.isEmpty() )
|
|
{
|
|
//There is a selection but no feature has been split.
|
|
//Maybe user forgot that only the selected features are split
|
|
returnCode = 4;
|
|
}
|
|
|
|
|
|
//now add the new features to this vectorlayer
|
|
L->editBuffer()->addFeatures( newFeatures );
|
|
|
|
return returnCode;
|
|
}
|
|
|
|
int QgsVectorLayerEditUtils::splitParts( const QList<QgsPoint>& splitLine, bool topologicalEditing )
|
|
{
|
|
if ( !L->hasGeometryType() )
|
|
return 4;
|
|
|
|
double xMin, yMin, xMax, yMax;
|
|
QgsRectangle bBox; //bounding box of the split line
|
|
int returnCode = 0;
|
|
int splitFunctionReturn; //return code of QgsGeometry::splitGeometry
|
|
int numberOfSplittedParts = 0;
|
|
|
|
QgsFeatureIterator fit;
|
|
|
|
if ( L->selectedFeatureCount() > 0 ) //consider only the selected features if there is a selection
|
|
{
|
|
fit = L->selectedFeaturesIterator();
|
|
}
|
|
else //else consider all the feature that intersect the bounding box of the split line
|
|
{
|
|
if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) == 0 )
|
|
{
|
|
bBox.setXMinimum( xMin );
|
|
bBox.setYMinimum( yMin );
|
|
bBox.setXMaximum( xMax );
|
|
bBox.setYMaximum( yMax );
|
|
}
|
|
else
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if ( bBox.isEmpty() )
|
|
{
|
|
//if the bbox is a line, try to make a square out of it
|
|
if ( bBox.width() == 0.0 && bBox.height() > 0 )
|
|
{
|
|
bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 );
|
|
bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 );
|
|
}
|
|
else if ( bBox.height() == 0.0 && bBox.width() > 0 )
|
|
{
|
|
bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 );
|
|
bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 );
|
|
}
|
|
else
|
|
{
|
|
//If we have a single point, we still create a non-null box
|
|
double bufferDistance = 0.000001;
|
|
if ( L->crs().isGeographic() )
|
|
bufferDistance = 0.00000001;
|
|
bBox.setXMinimum( bBox.xMinimum() - bufferDistance );
|
|
bBox.setXMaximum( bBox.xMaximum() + bufferDistance );
|
|
bBox.setYMinimum( bBox.yMinimum() - bufferDistance );
|
|
bBox.setYMaximum( bBox.yMaximum() + bufferDistance );
|
|
}
|
|
}
|
|
|
|
fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) );
|
|
}
|
|
|
|
int addPartRet = 0;
|
|
|
|
QgsFeature feat;
|
|
while ( fit.nextFeature( feat ) )
|
|
{
|
|
QList<QgsGeometry*> newGeometries;
|
|
QList<QgsPoint> topologyTestPoints;
|
|
QgsGeometry featureGeom = feat.geometry();
|
|
splitFunctionReturn = featureGeom.splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints );
|
|
if ( splitFunctionReturn == 0 )
|
|
{
|
|
//add new parts
|
|
if ( !newGeometries.isEmpty() )
|
|
featureGeom.convertToMultiType();
|
|
|
|
for ( int i = 0; i < newGeometries.size(); ++i )
|
|
{
|
|
addPartRet = featureGeom.addPart( newGeometries.at( i ) );
|
|
if ( addPartRet )
|
|
break;
|
|
}
|
|
|
|
// For test only: Exception already thrown here...
|
|
// feat.geometry()->asWkb();
|
|
|
|
if ( !addPartRet )
|
|
{
|
|
L->editBuffer()->changeGeometry( feat.id(), featureGeom );
|
|
}
|
|
else
|
|
{
|
|
// Test addPartRet
|
|
switch ( addPartRet )
|
|
{
|
|
case 1:
|
|
QgsDebugMsg( "Not a multipolygon" );
|
|
break;
|
|
|
|
case 2:
|
|
QgsDebugMsg( "Not a valid geometry" );
|
|
break;
|
|
|
|
case 3:
|
|
QgsDebugMsg( "New polygon ring" );
|
|
break;
|
|
}
|
|
}
|
|
L->editBuffer()->changeGeometry( feat.id(), featureGeom );
|
|
|
|
if ( topologicalEditing )
|
|
{
|
|
QList<QgsPoint>::const_iterator topol_it = topologyTestPoints.constBegin();
|
|
for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it )
|
|
{
|
|
addTopologicalPoints( *topol_it );
|
|
}
|
|
}
|
|
++numberOfSplittedParts;
|
|
}
|
|
else if ( splitFunctionReturn > 1 ) //1 means no split but also no error
|
|
{
|
|
returnCode = splitFunctionReturn;
|
|
}
|
|
|
|
qDeleteAll( newGeometries );
|
|
}
|
|
|
|
if ( numberOfSplittedParts == 0 && L->selectedFeatureCount() > 0 && returnCode == 0 )
|
|
{
|
|
//There is a selection but no feature has been split.
|
|
//Maybe user forgot that only the selected features are split
|
|
returnCode = 4;
|
|
}
|
|
|
|
return returnCode;
|
|
}
|
|
|
|
|
|
int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsGeometry& geom )
|
|
{
|
|
if ( !L->hasGeometryType() )
|
|
return 1;
|
|
|
|
if ( geom.isEmpty() )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
int returnVal = 0;
|
|
|
|
QgsWkbTypes::Type wkbType = geom.wkbType();
|
|
|
|
switch ( wkbType )
|
|
{
|
|
//line
|
|
case QgsWkbTypes::LineString25D:
|
|
case QgsWkbTypes::LineString:
|
|
{
|
|
QgsPolyline theLine = geom.asPolyline();
|
|
QgsPolyline::const_iterator line_it = theLine.constBegin();
|
|
for ( ; line_it != theLine.constEnd(); ++line_it )
|
|
{
|
|
if ( addTopologicalPoints( *line_it ) != 0 )
|
|
{
|
|
returnVal = 2;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
//multiline
|
|
case QgsWkbTypes::MultiLineString25D:
|
|
case QgsWkbTypes::MultiLineString:
|
|
{
|
|
QgsMultiPolyline theMultiLine = geom.asMultiPolyline();
|
|
QgsPolyline currentPolyline;
|
|
|
|
for ( int i = 0; i < theMultiLine.size(); ++i )
|
|
{
|
|
QgsPolyline::const_iterator line_it = currentPolyline.constBegin();
|
|
for ( ; line_it != currentPolyline.constEnd(); ++line_it )
|
|
{
|
|
if ( addTopologicalPoints( *line_it ) != 0 )
|
|
{
|
|
returnVal = 2;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
//polygon
|
|
case QgsWkbTypes::Polygon25D:
|
|
case QgsWkbTypes::Polygon:
|
|
{
|
|
QgsPolygon thePolygon = geom.asPolygon();
|
|
QgsPolyline currentRing;
|
|
|
|
for ( int i = 0; i < thePolygon.size(); ++i )
|
|
{
|
|
currentRing = thePolygon.at( i );
|
|
QgsPolyline::const_iterator line_it = currentRing.constBegin();
|
|
for ( ; line_it != currentRing.constEnd(); ++line_it )
|
|
{
|
|
if ( addTopologicalPoints( *line_it ) != 0 )
|
|
{
|
|
returnVal = 2;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
//multipolygon
|
|
case QgsWkbTypes::MultiPolygon25D:
|
|
case QgsWkbTypes::MultiPolygon:
|
|
{
|
|
QgsMultiPolygon theMultiPolygon = geom.asMultiPolygon();
|
|
QgsPolygon currentPolygon;
|
|
QgsPolyline currentRing;
|
|
|
|
for ( int i = 0; i < theMultiPolygon.size(); ++i )
|
|
{
|
|
currentPolygon = theMultiPolygon.at( i );
|
|
for ( int j = 0; j < currentPolygon.size(); ++j )
|
|
{
|
|
currentRing = currentPolygon.at( j );
|
|
QgsPolyline::const_iterator line_it = currentRing.constBegin();
|
|
for ( ; line_it != currentRing.constEnd(); ++line_it )
|
|
{
|
|
if ( addTopologicalPoints( *line_it ) != 0 )
|
|
{
|
|
returnVal = 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return returnVal;
|
|
}
|
|
|
|
|
|
int QgsVectorLayerEditUtils::addTopologicalPoints( const QgsPoint& p )
|
|
{
|
|
if ( !L->hasGeometryType() )
|
|
return 1;
|
|
|
|
QMultiMap<double, QgsSnappingResult> snapResults; //results from the snapper object
|
|
//we also need to snap to vertex to make sure the vertex does not already exist in this geometry
|
|
QMultiMap<double, QgsSnappingResult> vertexSnapResults;
|
|
|
|
QList<QgsSnappingResult> filteredSnapResults; //we filter out the results that are on existing vertices
|
|
|
|
//work with a tolerance because coordinate projection may introduce some rounding
|
|
double threshold = 0.0000001;
|
|
if ( L->crs().mapUnits() == QgsUnitTypes::DistanceMeters )
|
|
{
|
|
threshold = 0.001;
|
|
}
|
|
else if ( L->crs().mapUnits() == QgsUnitTypes::DistanceFeet )
|
|
{
|
|
threshold = 0.0001;
|
|
}
|
|
|
|
|
|
if ( L->snapWithContext( p, threshold, snapResults, QgsSnapper::SnapToSegment ) != 0 )
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
QMultiMap<double, QgsSnappingResult>::const_iterator snap_it = snapResults.constBegin();
|
|
QMultiMap<double, QgsSnappingResult>::const_iterator vertex_snap_it;
|
|
for ( ; snap_it != snapResults.constEnd(); ++snap_it )
|
|
{
|
|
//test if p is already a vertex of this geometry. If yes, don't insert it
|
|
bool vertexAlreadyExists = false;
|
|
if ( L->snapWithContext( p, threshold, vertexSnapResults, QgsSnapper::SnapToVertex ) != 0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
vertex_snap_it = vertexSnapResults.constBegin();
|
|
for ( ; vertex_snap_it != vertexSnapResults.constEnd(); ++vertex_snap_it )
|
|
{
|
|
if ( snap_it.value().snappedAtGeometry == vertex_snap_it.value().snappedAtGeometry )
|
|
{
|
|
vertexAlreadyExists = true;
|
|
}
|
|
}
|
|
|
|
if ( !vertexAlreadyExists )
|
|
{
|
|
filteredSnapResults.push_back( *snap_it );
|
|
}
|
|
}
|
|
insertSegmentVerticesForSnap( filteredSnapResults );
|
|
return 0;
|
|
}
|
|
|
|
|
|
int QgsVectorLayerEditUtils::insertSegmentVerticesForSnap( const QList<QgsSnappingResult>& snapResults )
|
|
{
|
|
if ( !L->hasGeometryType() )
|
|
return 1;
|
|
|
|
int returnval = 0;
|
|
QgsPoint layerPoint;
|
|
|
|
QList<QgsSnappingResult>::const_iterator it = snapResults.constBegin();
|
|
for ( ; it != snapResults.constEnd(); ++it )
|
|
{
|
|
if ( it->snappedVertexNr == -1 ) // segment snap
|
|
{
|
|
layerPoint = it->snappedVertex;
|
|
if ( !insertVertex( layerPoint.x(), layerPoint.y(), it->snappedAtGeometry, it->afterVertexNr ) )
|
|
{
|
|
returnval = 3;
|
|
}
|
|
}
|
|
}
|
|
return returnval;
|
|
}
|
|
|
|
|
|
|
|
|
|
int QgsVectorLayerEditUtils::boundingBoxFromPointList( const QList<QgsPoint>& list, double& xmin, double& ymin, double& xmax, double& ymax ) const
|
|
{
|
|
if ( list.size() < 1 )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
xmin = std::numeric_limits<double>::max();
|
|
xmax = -std::numeric_limits<double>::max();
|
|
ymin = std::numeric_limits<double>::max();
|
|
ymax = -std::numeric_limits<double>::max();
|
|
|
|
for ( QList<QgsPoint>::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
|
|
{
|
|
if ( it->x() < xmin )
|
|
{
|
|
xmin = it->x();
|
|
}
|
|
if ( it->x() > xmax )
|
|
{
|
|
xmax = it->x();
|
|
}
|
|
if ( it->y() < ymin )
|
|
{
|
|
ymin = it->y();
|
|
}
|
|
if ( it->y() > ymax )
|
|
{
|
|
ymax = it->y();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|