QGIS API Documentation  2.11.0-Master
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  //read snapping settings from project
154  bool snappingDefinedInProject, ok;
155  QStringList layerIdList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingList", QStringList(), &snappingDefinedInProject );
156  QStringList enabledList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingEnabledList", QStringList(), &ok );
157  QStringList toleranceList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceList", QStringList(), &ok );
158  QStringList toleranceUnitList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList", QStringList(), &ok );
159  QStringList snapToList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnapToList", QStringList(), &ok );
160 
161  if ( !( layerIdList.size() == enabledList.size() &&
162  layerIdList.size() == toleranceList.size() &&
163  layerIdList.size() == toleranceUnitList.size() &&
164  layerIdList.size() == snapToList.size() ) )
165  {
166  // lists must have the same size, otherwise something is wrong
167  return 1;
168  }
169 
170  QList<QgsSnapper::SnapLayer> snapLayers;
171  QgsSnapper::SnapLayer snapLayer;
172 
173  // Use snapping information from the project
174  if ( snappingDefinedInProject )
175  {
176  // set layers, tolerances, snap to segment/vertex to QgsSnapper
177  QStringList::const_iterator layerIt( layerIdList.constBegin() );
178  QStringList::const_iterator tolIt( toleranceList.constBegin() );
179  QStringList::const_iterator tolUnitIt( toleranceUnitList.constBegin() );
180  QStringList::const_iterator snapIt( snapToList.constBegin() );
181  QStringList::const_iterator enabledIt( enabledList.constBegin() );
182  for ( ; layerIt != layerIdList.constEnd(); ++layerIt, ++tolIt, ++tolUnitIt, ++snapIt, ++enabledIt )
183  {
184  if ( *enabledIt != "enabled" )
185  {
186  // skip layer if snapping is not enabled
187  continue;
188  }
189 
190  //layer
191  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( QgsMapLayerRegistry::instance()->mapLayer( *layerIt ) );
192  if ( !vlayer || !vlayer->hasGeometryType() )
193  continue;
194 
195  snapLayer.mLayer = vlayer;
196 
197  //tolerance
198  snapLayer.mTolerance = tolIt->toDouble();
199  snapLayer.mUnitType = ( QgsTolerance::UnitType ) tolUnitIt->toInt();
200 
201  // segment or vertex
202  if ( *snapIt == "to_vertex" )
203  {
204  snapLayer.mSnapTo = QgsSnapper::SnapToVertex;
205  }
206  else if ( *snapIt == "to_segment" )
207  {
209  }
210  else
211  {
212  // to vertex and segment
214  }
215 
216  snapLayers.append( snapLayer );
217  }
218  }
219  else
220  {
221  // nothing in project. Use default snapping tolerance to vertex of current layer
222  QgsMapLayer* currentLayer = mMapCanvas->currentLayer();
223  if ( !currentLayer )
224  return 2;
225 
226  QgsVectorLayer* currentVectorLayer = qobject_cast<QgsVectorLayer *>( currentLayer );
227  if ( !currentVectorLayer )
228  return 3;
229 
230  snapLayer.mLayer = currentVectorLayer;
231 
232  //default snap mode
233  QSettings settings;
234  QString defaultSnapString = settings.value( "/qgis/digitizing/default_snap_mode", "off" ).toString();
235  if ( defaultSnapString == "to segment" )
236  {
238  }
239  else if ( defaultSnapString == "to vertex and segment" )
240  {
242  }
243  else if ( defaultSnapString == "to vertex" )
244  {
245  snapLayer.mSnapTo = QgsSnapper::SnapToVertex;
246  }
247  else
248  {
249  return 0;
250  }
251 
252  //default snapping tolerance (returned in map units)
253  snapLayer.mTolerance = QgsTolerance::defaultTolerance( currentVectorLayer, mMapCanvas->mapSettings() );
255 
256  snapLayers.append( snapLayer );
257  }
258 
259  mSnapper->setSnapLayers( snapLayers );
260 
261  if ( mSnapper->snapMapPoint( point, results, excludePoints ) != 0 )
262  return 4;
263 
264  if ( intersectionSnapping != 1 )
265  return 0;
266 
267  QList<QgsSnappingResult> segments;
270  it != results.constEnd();
271  ++it )
272  {
273  if ( it->snappedVertexNr == -1 )
274  {
275  QgsDebugMsg( "segment" );
276  segments.push_back( *it );
277  }
278  else
279  {
280  QgsDebugMsg( "no segment" );
281  points.push_back( *it );
282  }
283  }
284 
285  if ( segments.length() < 2 )
286  return 0;
287 
288  QList<QgsSnappingResult> myResults;
289 
290  for ( QList<QgsSnappingResult>::const_iterator oSegIt = segments.constBegin();
291  oSegIt != segments.constEnd();
292  ++oSegIt )
293  {
294  QgsDebugMsg( QString::number( oSegIt->beforeVertexNr ) );
295 
296  QVector<QgsPoint> vertexPoints;
297  vertexPoints.append( oSegIt->beforeVertex );
298  vertexPoints.append( oSegIt->afterVertex );
299 
300  QgsGeometry* lineA = QgsGeometry::fromPolyline( vertexPoints );
301 
302  for ( QList<QgsSnappingResult>::iterator iSegIt = segments.begin();
303  iSegIt != segments.end();
304  ++iSegIt )
305  {
306  QVector<QgsPoint> vertexPoints;
307  vertexPoints.append( iSegIt->beforeVertex );
308  vertexPoints.append( iSegIt->afterVertex );
309 
310  QgsGeometry* lineB = QgsGeometry::fromPolyline( vertexPoints );
311  QgsGeometry* intersectionPoint = lineA->intersection( lineB );
312  delete lineB;
313 
314  if ( intersectionPoint && intersectionPoint->type() == QGis::Point )
315  {
316  //We have to check the intersection point is inside the tolerance distance for both layers
317  double toleranceA = 0;
318  double toleranceB = 0;
319  for ( int i = 0 ;i < snapLayers.size();++i )
320  {
321  if ( snapLayers[i].mLayer == oSegIt->layer )
322  {
323  toleranceA = QgsTolerance::toleranceInMapUnits( snapLayers[i].mTolerance, snapLayers[i].mLayer, mMapCanvas->mapSettings(), snapLayers[i].mUnitType );
324  }
325  if ( snapLayers[i].mLayer == iSegIt->layer )
326  {
327  toleranceB = QgsTolerance::toleranceInMapUnits( snapLayers[i].mTolerance, snapLayers[i].mLayer, mMapCanvas->mapSettings(), snapLayers[i].mUnitType );
328  }
329  }
330  QgsGeometry* cursorPoint = QgsGeometry::fromPoint( point );
331  double distance = intersectionPoint->distance( *cursorPoint );
332  if ( distance < toleranceA && distance < toleranceB )
333  {
334  iSegIt->snappedVertex = intersectionPoint->asPoint();
335  myResults.append( *iSegIt );
336  }
337  delete cursorPoint;
338  }
339  delete intersectionPoint;
340 
341  }
342 
343  delete lineA;
344  }
345 
346  if ( myResults.length() > 0 )
347  {
348  results.clear();
349  results = myResults;
350  }
351 
352  return 0;
353 }
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
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:75
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:105
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
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
QStringList readListEntry(const QString &scope, const QString &key, QStringList def=QStringList(), bool *ok=0) const
Key value accessors.
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
static double defaultTolerance(QgsMapLayer *layer, const QgsMapSettings &mapSettings)
Static function to get default tolerance value for a layer.
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
QVariant value(const QString &key, const QVariant &defaultValue) const
bool hasGeometryType() const
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
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:352
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.
QgsMapLayer * mapLayer(QString theLayerId)
Retrieve a pointer to a loaded layer by id.
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()