446 changes: 211 additions & 235 deletions src/gui/qgsmapcanvassnapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,22 @@
#include "qgslogger.h"
#include "qgsgeometry.h"

QgsMapCanvasSnapper::QgsMapCanvasSnapper( QgsMapCanvas* canvas ): mMapCanvas( canvas ), mSnapper( 0 )
QgsMapCanvasSnapper::QgsMapCanvasSnapper( QgsMapCanvas* canvas )
: mMapCanvas( canvas )
, mSnapper( 0 )
{
if ( canvas )
{
QgsMapRenderer* canvasRender = canvas->mapRenderer();
if ( canvasRender )
{
mSnapper = new QgsSnapper( canvasRender );
}
}
if ( !canvas )
return;

QgsMapRenderer *canvasRender = canvas->mapRenderer();
if ( !canvasRender )
return;

mSnapper = new QgsSnapper( canvasRender );
}

QgsMapCanvasSnapper::QgsMapCanvasSnapper(): mMapCanvas( 0 ), mSnapper( 0 )
{

}

QgsMapCanvasSnapper::~QgsMapCanvasSnapper()
Expand All @@ -62,287 +63,262 @@ void QgsMapCanvasSnapper::setMapCanvas( QgsMapCanvas* canvas )
}
}

int QgsMapCanvasSnapper::snapToCurrentLayer( const QPoint& p, QList<QgsSnappingResult>& results, QgsSnapper::SnappingType snap_to, double snappingTol, const QList<QgsPoint>& excludePoints )
int QgsMapCanvasSnapper::snapToCurrentLayer( const QPoint& p, QList<QgsSnappingResult>& results,
QgsSnapper::SnappingType snap_to,
double snappingTol,
const QList<QgsPoint>& excludePoints )
{
results.clear();

if ( mSnapper && mMapCanvas )
{

//topological editing on?
int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 );
if ( topologicalEditing == 0 )
{
mSnapper->setSnapMode( QgsSnapper::SnapWithOneResult );
}
else
{
mSnapper->setSnapMode( QgsSnapper::SnapWithResultsForSamePosition );
}

//current vector layer
QgsMapLayer* currentLayer = mMapCanvas->currentLayer();
if ( !currentLayer )
{
return 2;
}
QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer *>( currentLayer );
if ( !vlayer )
{
return 3;
}

QgsSnapper::SnapLayer snapLayer;
snapLayer.mLayer = vlayer;
snapLayer.mSnapTo = snap_to;
snapLayer.mUnitType = QgsTolerance::MapUnits;
if ( !mSnapper || !mMapCanvas )
return 1;

QSettings settings;
//topological editing on?
int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 );
if ( topologicalEditing == 0 )
{
mSnapper->setSnapMode( QgsSnapper::SnapWithOneResult );
}
else
{
mSnapper->setSnapMode( QgsSnapper::SnapWithResultsForSamePosition );
}

if ( snappingTol < 0 )
{
//use search tolerance for vertex editing
snapLayer.mTolerance = QgsTolerance::vertexSearchRadius( vlayer, mMapCanvas->mapRenderer() );
}
else
{
snapLayer.mTolerance = snappingTol;
}
//current vector layer
QgsMapLayer* currentLayer = mMapCanvas->currentLayer();
if ( !currentLayer )
return 2;

QList<QgsSnapper::SnapLayer> snapLayers;
snapLayers.append( snapLayer );
mSnapper->setSnapLayers( snapLayers );
QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer *>( currentLayer );
if ( !vlayer )
return 3;

if ( mSnapper->snapPoint( p, results, excludePoints ) != 0 )
{
return 4;
}
QgsSnapper::SnapLayer snapLayer;
snapLayer.mLayer = vlayer;
snapLayer.mSnapTo = snap_to;
snapLayer.mUnitType = QgsTolerance::MapUnits;

return 0;
if ( snappingTol < 0 )
{
//use search tolerance for vertex editing
snapLayer.mTolerance = QgsTolerance::vertexSearchRadius( vlayer, mMapCanvas->mapRenderer() );
}
else
{
return 1;
snapLayer.mTolerance = snappingTol;
}

QList<QgsSnapper::SnapLayer> snapLayers;
snapLayers.append( snapLayer );
mSnapper->setSnapLayers( snapLayers );

if ( mSnapper->snapPoint( p, results, excludePoints ) != 0 )
return 4;

return 0;
}

int QgsMapCanvasSnapper::snapToBackgroundLayers( const QPoint& p, QList<QgsSnappingResult>& results, const QList<QgsPoint>& excludePoints )
{
results.clear();

if ( mSnapper )
if ( !mSnapper )
return 5;

//topological editing on?
int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 );

//snapping on intersection on?
int intersectionSnapping = QgsProject::instance()->readNumEntry( "Digitizing", "/IntersectionSnapping", 0 );

if ( topologicalEditing == 0 )
{
//topological editing on?
int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 );
if ( intersectionSnapping == 0 )
mSnapper->setSnapMode( QgsSnapper::SnapWithOneResult );
else
mSnapper->setSnapMode( QgsSnapper::SnapWithResultsWithinTolerances );
}
else if ( intersectionSnapping == 0 )
{
mSnapper->setSnapMode( QgsSnapper::SnapWithResultsForSamePosition );
}
else
{
mSnapper->setSnapMode( QgsSnapper::SnapWithResultsWithinTolerances );
}

//snapping on intersection on?
int intersectionSnapping = QgsProject::instance()->readNumEntry( "Digitizing", "/IntersectionSnapping", 0 );
//read snapping settings from project
bool snappingDefinedInProject, ok;
QStringList layerIdList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingList", QStringList(), &snappingDefinedInProject );
QStringList enabledList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingEnabledList", QStringList(), &ok );
QStringList toleranceList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceList", QStringList(), &ok );
QStringList toleranceUnitList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList", QStringList(), &ok );
QStringList snapToList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnapToList", QStringList(), &ok );

if ( !( layerIdList.size() == enabledList.size() &&
layerIdList.size() == toleranceList.size() &&
layerIdList.size() == toleranceUnitList.size() &&
layerIdList.size() == snapToList.size() ) )
{
// lists must have the same size, otherwise something is wrong
return 1;
}

if ( topologicalEditing == 0 )
QList<QgsSnapper::SnapLayer> snapLayers;
QgsSnapper::SnapLayer snapLayer;

// Use snapping information from the project
if ( snappingDefinedInProject )
{
// set layers, tolerances, snap to segment/vertex to QgsSnapper
QStringList::const_iterator layerIt( layerIdList.constBegin() );
QStringList::const_iterator tolIt( toleranceList.constBegin() );
QStringList::const_iterator tolUnitIt( toleranceUnitList.constBegin() );
QStringList::const_iterator snapIt( snapToList.constBegin() );
QStringList::const_iterator enabledIt( enabledList.constBegin() );
for ( ; layerIt != layerIdList.constEnd(); ++layerIt, ++tolIt, ++tolUnitIt, ++snapIt, ++enabledIt )
{
if ( intersectionSnapping == 0 )
if ( *enabledIt != "enabled" )
{
mSnapper->setSnapMode( QgsSnapper::SnapWithOneResult );
// skip layer if snapping is not enabled
continue;
}
else

//layer
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( QgsMapLayerRegistry::instance()->mapLayer( *layerIt ) );
if ( !vlayer )
continue;

snapLayer.mLayer = vlayer;

//tolerance
snapLayer.mTolerance = tolIt->toDouble();
snapLayer.mUnitType = ( QgsTolerance::UnitType ) tolUnitIt->toInt();

// segment or vertex
if ( *snapIt == "to_vertex" )
{
mSnapper->setSnapMode( QgsSnapper::SnapWithResultsWithinTolerances );
snapLayer.mSnapTo = QgsSnapper::SnapToVertex;
}
}
else
{
if ( intersectionSnapping == 0 )
else if ( *snapIt == "to_segment" )
{
mSnapper->setSnapMode( QgsSnapper::SnapWithResultsForSamePosition );
snapLayer.mSnapTo = QgsSnapper::SnapToSegment;
}
else
{
mSnapper->setSnapMode( QgsSnapper::SnapWithResultsWithinTolerances );
// to vertex and segment
snapLayer.mSnapTo = QgsSnapper::SnapToVertexAndSegment;
}

snapLayers.append( snapLayer );
}
}
else
{
// nothing in project. Use default snapping tolerance to vertex of current layer
QgsMapLayer* currentLayer = mMapCanvas->currentLayer();
if ( !currentLayer )
return 2;

QgsVectorLayer* currentVectorLayer = qobject_cast<QgsVectorLayer *>( currentLayer );
if ( !currentVectorLayer )
return 3;

snapLayer.mLayer = currentVectorLayer;

//read snapping settings from project
bool ok; //todo: take the default snapping tolerance for all vector layers if snapping not defined in project
bool snappingDefinedInProject = true;
QStringList defList;
QStringList layerIdList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingList", defList, &ok );
if ( !ok )
//default snap mode
QSettings settings;
QString defaultSnapString = settings.value( "/qgis/digitizing/default_snap_mode", "off" ).toString();
if ( defaultSnapString == "to segment" )
{
snappingDefinedInProject = false;
snapLayer.mSnapTo = QgsSnapper::SnapToSegment;
}
QStringList enabledList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingEnabledList", defList, &ok );
QStringList toleranceList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceList", defList, &ok );
QStringList toleranceUnitList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList", defList, &ok );
QStringList snapToList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnapToList", defList, &ok );

if ( !( layerIdList.size() == enabledList.size() && layerIdList.size() == toleranceList.size() && layerIdList.size() == toleranceUnitList.size() && layerIdList.size() == snapToList.size() ) )
else if ( defaultSnapString == "to vertex and segment" )
{
return 1; //lists must have the same size, otherwise something is wrong
snapLayer.mSnapTo = QgsSnapper::SnapToVertexAndSegment;
}

QList<QgsSnapper::SnapLayer> snapLayers;
QgsSnapper::SnapLayer snapLayer;

//Use snapping information from the project
if ( snappingDefinedInProject )
else if ( defaultSnapString == "to vertex" )
{
//set layers, tolerances, snap to segment/vertex to QgsSnapper
QgsMapLayer* layer = 0;
QgsVectorLayer* vlayer = 0;

QStringList::const_iterator layerIt = layerIdList.constBegin();
QStringList::const_iterator tolIt = toleranceList.constBegin();
QStringList::const_iterator tolUnitIt = toleranceUnitList.constBegin();
QStringList::const_iterator snapIt = snapToList.constBegin();
QStringList::const_iterator enabledIt = enabledList.constBegin();

for ( ; layerIt != layerIdList.constEnd(); ++layerIt, ++tolIt, ++tolUnitIt, ++snapIt, ++enabledIt )
{
if (( *enabledIt ) != "enabled" ) //skip layer if snapping is not enabled
{
continue;
}

//layer
layer = QgsMapLayerRegistry::instance()->mapLayer( *layerIt );
if ( layer == NULL )
continue;
vlayer = qobject_cast<QgsVectorLayer *>( layer );
if ( vlayer == NULL )
continue;

snapLayer.mLayer = vlayer;

//tolerance
snapLayer.mTolerance = tolIt->toDouble();
snapLayer.mUnitType = ( QgsTolerance::UnitType ) tolUnitIt->toInt();

//segment or vertex
if (( *snapIt ) == "to_vertex" )
{
snapLayer.mSnapTo = QgsSnapper::SnapToVertex;
}
else if (( *snapIt ) == "to_segment" )
{
snapLayer.mSnapTo = QgsSnapper::SnapToSegment;
}
else //to vertex and segment
{
snapLayer.mSnapTo = QgsSnapper::SnapToVertexAndSegment;
}

snapLayers.append( snapLayer );
}
snapLayer.mSnapTo = QgsSnapper::SnapToVertex;
}
else //nothing in project. Use default snapping tolerance to vertex of current layer
else
{
QgsMapLayer* currentLayer = mMapCanvas->currentLayer();
if ( !currentLayer )
{
return 2;
}
return 0;
}

QgsVectorLayer* currentVectorLayer = qobject_cast<QgsVectorLayer *>( currentLayer );
if ( !currentVectorLayer )
{
return 3;
}
//default snapping tolerance (returned in map units)
snapLayer.mTolerance = QgsTolerance::defaultTolerance( currentVectorLayer, mMapCanvas->mapRenderer() );
snapLayer.mUnitType = QgsTolerance::MapUnits;

snapLayer.mLayer = currentVectorLayer;
QSettings settings;
snapLayers.append( snapLayer );
}

//default snap mode
QString defaultSnapString = settings.value( "/qgis/digitizing/default_snap_mode", "to vertex" ).toString();
if ( defaultSnapString == "to segment" )
{
snapLayer.mSnapTo = QgsSnapper::SnapToSegment;
}
else if ( defaultSnapString == "to vertex and segment" )
{
snapLayer.mSnapTo = QgsSnapper::SnapToVertexAndSegment;
}
else
{
snapLayer.mSnapTo = QgsSnapper::SnapToVertex;
}
mSnapper->setSnapLayers( snapLayers );

//default snapping tolerance (returned in map units)
snapLayer.mTolerance = QgsTolerance::defaultTolerance( currentVectorLayer, mMapCanvas->mapRenderer() );
snapLayer.mUnitType = QgsTolerance::MapUnits;
if ( mSnapper->snapPoint( p, results, excludePoints ) != 0 )
return 4;

snapLayers.append( snapLayer );
}
mSnapper->setSnapLayers( snapLayers );
if ( intersectionSnapping != 1 )
return 0;

if ( mSnapper->snapPoint( p, results, excludePoints ) != 0 )
QList<QgsSnappingResult> segments;
QList<QgsSnappingResult> points;
for ( QList<QgsSnappingResult>::const_iterator it = results.constBegin();
it != results.constEnd();
++it )
{
if ( it->snappedVertexNr == -1 )
{
QgsDebugMsg( "segment" );
segments.push_back( *it );
}
else
{
return 4;
QgsDebugMsg( "no segment" );
points.push_back( *it );
}
}

if ( segments.length() < 2 )
return 0;

QList<QgsSnappingResult> myResults;

for ( QList<QgsSnappingResult>::const_iterator oSegIt = segments.constBegin();
oSegIt != segments.constEnd();
++oSegIt )
{
QgsDebugMsg( QString::number( oSegIt->beforeVertexNr ) );

QVector<QgsPoint> vertexPoints;
vertexPoints.append( oSegIt->beforeVertex );
vertexPoints.append( oSegIt->afterVertex );

QgsGeometry* lineA = QgsGeometry::fromPolyline( vertexPoints );

if ( intersectionSnapping == 1 )
for ( QList<QgsSnappingResult>::iterator iSegIt = segments.begin();
iSegIt != segments.end();
++iSegIt )
{
QList<QgsSnappingResult>::const_iterator it = results.constBegin();
QList<QgsSnappingResult> segments;
QList<QgsSnappingResult> points;
for ( ; it != results.constEnd(); ++it )
{
if ( it->snappedVertexNr == -1 )
{
QgsDebugMsg( "segment" );
segments.push_back( *it );
}
else
{
QgsDebugMsg( "no segment" );
points.push_back( *it );
}
}
QVector<QgsPoint> vertexPoints;
vertexPoints.append( iSegIt->beforeVertex );
vertexPoints.append( iSegIt->afterVertex );
QgsGeometry* lineB = QgsGeometry::fromPolyline( vertexPoints );

if ( segments.length() >= 2 )
QgsGeometry* intersectionPoint = lineA->intersection( lineB );
if ( intersectionPoint->type() == QGis::Point )
{

QList<QgsSnappingResult> myResults;

QList<QgsSnappingResult>::const_iterator oSegIt = segments.constBegin();
QList<QgsSnappingResult>::iterator iSegIt;
for ( ; oSegIt != segments.constEnd(); ++oSegIt )
{
QgsDebugMsg( QString::number( oSegIt->beforeVertexNr ) );

QVector<QgsPoint> vertexPoints;
vertexPoints.append( oSegIt->beforeVertex );
vertexPoints.append( oSegIt->afterVertex );
QgsGeometry* lineA = QgsGeometry::fromPolyline( vertexPoints );

for ( iSegIt = segments.begin(); iSegIt != segments.end(); ++iSegIt )
{
QVector<QgsPoint> vertexPoints;
vertexPoints.append( iSegIt->beforeVertex );
vertexPoints.append( iSegIt->afterVertex );
QgsGeometry* lineB = QgsGeometry::fromPolyline( vertexPoints );

QgsGeometry* intersectionPoint = lineA->intersection( lineB );
if ( intersectionPoint->type() == QGis::Point )
{
iSegIt->snappedVertex = intersectionPoint->asPoint();
myResults.append( *iSegIt );
}
}

}

if ( myResults.length() > 0 )
{
results.clear();
results = myResults;
}
iSegIt->snappedVertex = intersectionPoint->asPoint();
myResults.append( *iSegIt );
}
}
return 0;
}
else

if ( myResults.length() > 0 )
{
return 5;
results.clear();
results = myResults;
}

return 0;
}