QGIS API Documentation  2.12.0-Lyon
qgsmapcanvassnapper.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmapcanvassnapper.cpp
3  -----------------------
4  begin : June 21, 2007
5  copyright : (C) 2007 by Marco Hugentobler
6  email : marco dot hugentobler at karto dot baug dot ethz dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsmapcanvassnapper.h"
19 #include "qgsmapcanvas.h"
20 #include "qgsmaplayerregistry.h"
21 #include "qgsmaptopixel.h"
22 #include "qgsproject.h"
23 #include "qgsvectorlayer.h"
24 #include "qgstolerance.h"
25 #include <QSettings>
26 #include "qgslogger.h"
27 #include "qgsgeometry.h"
28 
30  : mMapCanvas( canvas )
31  , mSnapper( 0 )
32 {
33  if ( !canvas )
34  return;
35 
36  mSnapper = new QgsSnapper( canvas->mapSettings() );
37 }
38 
39 QgsMapCanvasSnapper::QgsMapCanvasSnapper(): mMapCanvas( 0 ), mSnapper( 0 )
40 {
41 }
42 
44 {
45  delete mSnapper;
46 }
47 
49 {
50  mMapCanvas = canvas;
51  delete mSnapper;
52  if ( mMapCanvas )
53  {
54  mSnapper = new QgsSnapper( canvas->mapSettings() );
55  }
56  else
57  {
58  mSnapper = 0;
59  }
60 }
61 
64  double snappingTol,
65  const QList<QgsPoint>& excludePoints )
66 {
67  results.clear();
68 
69  if ( !mSnapper || !mMapCanvas )
70  return 1;
71 
72  //topological editing on?
73  int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 );
74  if ( topologicalEditing == 0 )
75  {
77  }
78  else
79  {
81  }
82 
83  //current vector layer
84  QgsMapLayer* currentLayer = mMapCanvas->currentLayer();
85  if ( !currentLayer )
86  return 2;
87 
88  QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer *>( currentLayer );
89  if ( !vlayer )
90  return 3;
91 
92  QgsSnapper::SnapLayer snapLayer;
93  snapLayer.mLayer = vlayer;
94  snapLayer.mSnapTo = snap_to;
96 
97  if ( snappingTol < 0 )
98  {
99  //use search tolerance for vertex editing
100  snapLayer.mTolerance = QgsTolerance::vertexSearchRadius( vlayer, mMapCanvas->mapSettings() );
101  }
102  else
103  {
104  snapLayer.mTolerance = snappingTol;
105  }
106 
107  QList<QgsSnapper::SnapLayer> snapLayers;
108  snapLayers.append( snapLayer );
109  mSnapper->setSnapLayers( snapLayers );
110 
111  QgsPoint mapPoint = mMapCanvas->mapSettings().mapToPixel().toMapCoordinates( p );
112  if ( mSnapper->snapMapPoint( mapPoint, results, excludePoints ) != 0 )
113  return 4;
114 
115  return 0;
116 }
117 
119 {
120  const QgsPoint mapCoordPoint = mMapCanvas->mapSettings().mapToPixel().toMapCoordinates( p.x(), p.y() );
121  return snapToBackgroundLayers( mapCoordPoint, results, excludePoints );
122 }
123 
125 {
126  results.clear();
127 
128  if ( !mSnapper )
129  return 5;
130 
131  //topological editing on?
132  int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 );
133 
134  //snapping on intersection on?
135  int intersectionSnapping = QgsProject::instance()->readNumEntry( "Digitizing", "/IntersectionSnapping", 0 );
136 
137  if ( topologicalEditing == 0 )
138  {
139  if ( intersectionSnapping == 0 )
141  else
143  }
144  else if ( intersectionSnapping == 0 )
145  {
147  }
148  else
149  {
151  }
152 
153  QgsVectorLayer* currentVectorLayer = dynamic_cast<QgsVectorLayer*>( mMapCanvas->currentLayer() );
154  if ( !currentVectorLayer )
155  {
156  return 1;
157  }
158 
159  //read snapping settings from project
160  QStringList layerIdList, enabledList, toleranceList, toleranceUnitList, snapToList;
161 
162  bool ok, snappingDefinedInProject;
163 
164  QSettings settings;
165  QString snappingMode = QgsProject::instance()->readEntry( "Digitizing", "/SnappingMode", "current_layer", &snappingDefinedInProject );
166  QString defaultSnapToleranceUnit = snappingDefinedInProject ? QgsProject::instance()->readEntry( "Digitizing", "/DefaultSnapToleranceUnit" ) : settings.value( "/qgis/digitizing/default_snapping_tolerance_unit", "0" ).toString();
167  QString defaultSnapType = snappingDefinedInProject ? QgsProject::instance()->readEntry( "Digitizing", "/DefaultSnapType" ) : settings.value( "/qgis/digitizing/default_snap_mode", "off" ).toString();
168  QString defaultSnapTolerance = snappingDefinedInProject ? QString::number( QgsProject::instance()->readDoubleEntry( "Digitizing", "/DefaultSnapTolerance" ) ) : settings.value( "/qgis/digitizing/default_snapping_tolerance", "0" ).toString();
169 
170  if ( !snappingDefinedInProject && defaultSnapType == "off" )
171  {
172  return 0;
173  }
174 
175  if ( snappingMode == "current_layer" || !snappingDefinedInProject )
176  {
177  layerIdList.append( currentVectorLayer->id() );
178  enabledList.append( "enabled" );
179  toleranceList.append( defaultSnapTolerance );
180  toleranceUnitList.append( defaultSnapToleranceUnit );
181  snapToList.append( defaultSnapType );
182  }
183  else if ( snappingMode == "all_layers" )
184  {
185  QList<QgsMapLayer*> allLayers = mMapCanvas->layers();
186  QList<QgsMapLayer*>::const_iterator layerIt = allLayers.constBegin();
187  for ( ; layerIt != allLayers.constEnd(); ++layerIt )
188  {
189  if ( !( *layerIt ) )
190  {
191  continue;
192  }
193  layerIdList.append(( *layerIt )->id() );
194  enabledList.append( "enabled" );
195  toleranceList.append( defaultSnapTolerance );
196  toleranceUnitList.append( defaultSnapToleranceUnit );
197  snapToList.append( defaultSnapType );
198  }
199  }
200  else //advanced
201  {
202  layerIdList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingList", QStringList(), &ok );
203  enabledList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingEnabledList", QStringList(), &ok );
204  toleranceList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceList", QStringList(), &ok );
205  toleranceUnitList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList", QStringList(), &ok );
206  snapToList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnapToList", QStringList(), &ok );
207  }
208 
209  if ( !( layerIdList.size() == enabledList.size() &&
210  layerIdList.size() == toleranceList.size() &&
211  layerIdList.size() == toleranceUnitList.size() &&
212  layerIdList.size() == snapToList.size() ) )
213  {
214  // lists must have the same size, otherwise something is wrong
215  return 1;
216  }
217 
218  QList<QgsSnapper::SnapLayer> snapLayers;
219  QgsSnapper::SnapLayer snapLayer;
220 
221 
222 
223  // set layers, tolerances, snap to segment/vertex to QgsSnapper
224  QStringList::const_iterator layerIt( layerIdList.constBegin() );
225  QStringList::const_iterator tolIt( toleranceList.constBegin() );
226  QStringList::const_iterator tolUnitIt( toleranceUnitList.constBegin() );
227  QStringList::const_iterator snapIt( snapToList.constBegin() );
228  QStringList::const_iterator enabledIt( enabledList.constBegin() );
229  for ( ; layerIt != layerIdList.constEnd(); ++layerIt, ++tolIt, ++tolUnitIt, ++snapIt, ++enabledIt )
230  {
231  if ( *enabledIt != "enabled" )
232  {
233  // skip layer if snapping is not enabled
234  continue;
235  }
236 
237  //layer
238  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( QgsMapLayerRegistry::instance()->mapLayer( *layerIt ) );
239  if ( !vlayer || !vlayer->hasGeometryType() )
240  continue;
241 
242  snapLayer.mLayer = vlayer;
243 
244  //tolerance
245  snapLayer.mTolerance = tolIt->toDouble();
246  snapLayer.mUnitType = ( QgsTolerance::UnitType ) tolUnitIt->toInt();
247 
248  // segment or vertex
249  if ( *snapIt == "to vertex" || *snapIt == "to_vertex" )
250  {
251  snapLayer.mSnapTo = QgsSnapper::SnapToVertex;
252  }
253  else if ( *snapIt == "to segment" || *snapIt == "to_segment" )
254  {
256  }
257  else if ( *snapIt == "to vertex and segment" || *snapIt == "to_vertex_and_segment" )
258  {
260  }
261  else //off
262  {
263  continue;
264  }
265 
266  snapLayers.append( snapLayer );
267  }
268 
269  mSnapper->setSnapLayers( snapLayers );
270 
271  if ( mSnapper->snapMapPoint( point, results, excludePoints ) != 0 )
272  return 4;
273 
274  if ( intersectionSnapping != 1 )
275  return 0;
276 
277  QList<QgsSnappingResult> segments;
280  it != results.constEnd();
281  ++it )
282  {
283  if ( it->snappedVertexNr == -1 )
284  {
285  QgsDebugMsg( "segment" );
286  segments.push_back( *it );
287  }
288  else
289  {
290  QgsDebugMsg( "no segment" );
291  points.push_back( *it );
292  }
293  }
294 
295  if ( segments.length() < 2 )
296  return 0;
297 
298  QList<QgsSnappingResult> myResults;
299 
300  for ( QList<QgsSnappingResult>::const_iterator oSegIt = segments.constBegin();
301  oSegIt != segments.constEnd();
302  ++oSegIt )
303  {
304  QgsDebugMsg( QString::number( oSegIt->beforeVertexNr ) );
305 
306  QVector<QgsPoint> vertexPoints;
307  vertexPoints.append( oSegIt->beforeVertex );
308  vertexPoints.append( oSegIt->afterVertex );
309 
310  QgsGeometry* lineA = QgsGeometry::fromPolyline( vertexPoints );
311 
312  for ( QList<QgsSnappingResult>::iterator iSegIt = segments.begin();
313  iSegIt != segments.end();
314  ++iSegIt )
315  {
316  QVector<QgsPoint> vertexPoints;
317  vertexPoints.append( iSegIt->beforeVertex );
318  vertexPoints.append( iSegIt->afterVertex );
319 
320  QgsGeometry* lineB = QgsGeometry::fromPolyline( vertexPoints );
321  QgsGeometry* intersectionPoint = lineA->intersection( lineB );
322  delete lineB;
323 
324  if ( intersectionPoint && intersectionPoint->type() == QGis::Point )
325  {
326  //We have to check the intersection point is inside the tolerance distance for both layers
327  double toleranceA = 0;
328  double toleranceB = 0;
329  for ( int i = 0 ;i < snapLayers.size();++i )
330  {
331  if ( snapLayers[i].mLayer == oSegIt->layer )
332  {
333  toleranceA = QgsTolerance::toleranceInMapUnits( snapLayers[i].mTolerance, snapLayers[i].mLayer, mMapCanvas->mapSettings(), snapLayers[i].mUnitType );
334  }
335  if ( snapLayers[i].mLayer == iSegIt->layer )
336  {
337  toleranceB = QgsTolerance::toleranceInMapUnits( snapLayers[i].mTolerance, snapLayers[i].mLayer, mMapCanvas->mapSettings(), snapLayers[i].mUnitType );
338  }
339  }
340  QgsGeometry* cursorPoint = QgsGeometry::fromPoint( point );
341  double distance = intersectionPoint->distance( *cursorPoint );
342  if ( distance < toleranceA && distance < toleranceB )
343  {
344  iSegIt->snappedVertex = intersectionPoint->asPoint();
345  myResults.append( *iSegIt );
346  }
347  delete cursorPoint;
348  }
349  delete intersectionPoint;
350 
351  }
352 
353  delete lineA;
354  }
355 
356  if ( myResults.length() > 0 )
357  {
358  results.clear();
359  results = myResults;
360  }
361 
362  return 0;
363 }
void clear()
int snapToCurrentLayer(const QPoint &p, QList< QgsSnappingResult > &results, QgsSnapper::SnappingType snap_to, double snappingTol=-1, const QList< QgsPoint > &excludePoints=QList< QgsPoint >())
Does a snap to the current layer.
Base class for all map layer types.
Definition: qgsmaplayer.h:49
double mTolerance
The snapping tolerances for the layers, always in source coordinate systems of the layer...
Definition: qgssnapper.h:90
void append(const T &value)
void push_back(const T &value)
All results within the given layer tolerances are returned.
Definition: qgssnapper.h:82
double distance(const QgsGeometry &geom) const
Returns the minimum distanace between this geometry and another geometry, using GEOS.
int length() const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QList< QgsMapLayer * > layers() const
return list of layers within map canvas.
QGis::GeometryType type() const
Returns type of the geometry as a QGis::GeometryType.
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
const QgsMapToPixel & mapToPixel() const
static double toleranceInMapUnits(double tolerance, QgsMapLayer *layer, const QgsMapSettings &mapSettings, UnitType units=LayerUnits)
Static function to translate tolerance value into layer units.
QgsTolerance::UnitType mUnitType
What unit is used for tolerance.
Definition: qgssnapper.h:94
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=0) const
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:107
int x() const
int y() const
QgsSnapper::SnappingType mSnapTo
What snapping type to use (snap to segment or to vertex)
Definition: qgssnapper.h:92
int size() const
QgsMapLayer * mapLayer(const QString &theLayerId)
Retrieve a pointer to a loaded layer by id.
int snapMapPoint(const QgsPoint &mapCoordPoint, QList< QgsSnappingResult > &snappingResult, const QList< QgsPoint > &excludePoints=QList< QgsPoint >())
Does the snapping operation.
Definition: qgssnapper.cpp:52
SnappingType
Snap to vertex, to segment or both.
Definition: qgssnapper.h:66
QString number(int n, int base)
void append(const T &value)
int snapToBackgroundLayers(const QPoint &p, QList< QgsSnappingResult > &results, const QList< QgsPoint > &excludePoints=QList< QgsPoint >())
Snaps to the background layers.
A class that allows advanced snapping operations on a set of vector layers.
Definition: qgssnapper.h:62
QString id() const
Get this layer's unique ID, this ID is used to access this layer from map layer registry.
Several snapping results which have the same position are returned.
Definition: qgssnapper.h:80
A class to represent a point.
Definition: qgspoint.h:63
static QgsGeometry * fromPoint(const QgsPoint &point)
Creates a new geometry from a QgsPoint object.
iterator end()
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
QgsPoint toMapCoordinates(int x, int y) const
static double vertexSearchRadius(const QgsMapSettings &mapSettings)
Static function to get vertex tolerance value.
QgsGeometry * intersection(const QgsGeometry *geometry) const
Returns a geometry representing the points shared by this geometry and other.
Only one snapping result is returned.
Definition: qgssnapper.h:77
bool hasGeometryType() const
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QVariant value(const QString &key, const QVariant &defaultValue) const
QString readEntry(const QString &scope, const QString &key, const QString &def=QString::null, bool *ok=0) const
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
Layer unit value.
Definition: qgstolerance.h:38
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:353
QgsVectorLayer * mLayer
The layer to which snapping is applied.
Definition: qgssnapper.h:88
static QgsGeometry * fromPolyline(const QgsPolyline &polyline)
Creates a new geometry from a QgsPolyline object.
UnitType
Type of unit of tolerance value from settings.
Definition: qgstolerance.h:33
const_iterator constEnd() const
const_iterator constBegin() const
void setSnapLayers(const QList< QgsSnapper::SnapLayer > &snapLayers)
Definition: qgssnapper.cpp:151
QgsPoint asPoint() const
Return contents of the geometry as a point if wkbType is WKBPoint, otherwise returns [0...
Represents a vector layer which manages a vector based data sets.
QString toString() const
void setMapCanvas(QgsMapCanvas *canvas)
void setSnapMode(QgsSnapper::SnappingMode snapMode)
Definition: qgssnapper.cpp:157
iterator begin()
QStringList readListEntry(const QString &scope, const QString &key, const QStringList &def=QStringList(), bool *ok=0) const
Key value accessors.