QGIS API Documentation  2.7.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 
62 int QgsMapCanvasSnapper::snapToCurrentLayer( const QPoint& p, QList<QgsSnappingResult>& results,
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  if ( mSnapper->snapPoint( p, results, excludePoints ) != 0 )
112  return 4;
113 
114  return 0;
115 }
116 
117 int QgsMapCanvasSnapper::snapToBackgroundLayers( const QPoint& p, QList<QgsSnappingResult>& results, const QList<QgsPoint>& excludePoints )
118 {
119  const QgsPoint mapCoordPoint = mMapCanvas->mapSettings().mapToPixel().toMapCoordinates( p.x(), p.y() );
120  return snapToBackgroundLayers(mapCoordPoint,results,excludePoints);
121 }
122 
123 int QgsMapCanvasSnapper::snapToBackgroundLayers( const QgsPoint& point, QList<QgsSnappingResult>& results, const QList<QgsPoint>& excludePoints )
124 {
125  results.clear();
126 
127  if ( !mSnapper )
128  return 5;
129 
130  //topological editing on?
131  int topologicalEditing = QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 );
132 
133  //snapping on intersection on?
134  int intersectionSnapping = QgsProject::instance()->readNumEntry( "Digitizing", "/IntersectionSnapping", 0 );
135 
136  if ( topologicalEditing == 0 )
137  {
138  if ( intersectionSnapping == 0 )
140  else
142  }
143  else if ( intersectionSnapping == 0 )
144  {
146  }
147  else
148  {
150  }
151 
152  //read snapping settings from project
153  bool snappingDefinedInProject, ok;
154  QStringList layerIdList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingList", QStringList(), &snappingDefinedInProject );
155  QStringList enabledList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingEnabledList", QStringList(), &ok );
156  QStringList toleranceList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceList", QStringList(), &ok );
157  QStringList toleranceUnitList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList", QStringList(), &ok );
158  QStringList snapToList = QgsProject::instance()->readListEntry( "Digitizing", "/LayerSnapToList", QStringList(), &ok );
159 
160  if ( !( layerIdList.size() == enabledList.size() &&
161  layerIdList.size() == toleranceList.size() &&
162  layerIdList.size() == toleranceUnitList.size() &&
163  layerIdList.size() == snapToList.size() ) )
164  {
165  // lists must have the same size, otherwise something is wrong
166  return 1;
167  }
168 
169  QList<QgsSnapper::SnapLayer> snapLayers;
170  QgsSnapper::SnapLayer snapLayer;
171 
172  // Use snapping information from the project
173  if ( snappingDefinedInProject )
174  {
175  // set layers, tolerances, snap to segment/vertex to QgsSnapper
176  QStringList::const_iterator layerIt( layerIdList.constBegin() );
177  QStringList::const_iterator tolIt( toleranceList.constBegin() );
178  QStringList::const_iterator tolUnitIt( toleranceUnitList.constBegin() );
179  QStringList::const_iterator snapIt( snapToList.constBegin() );
180  QStringList::const_iterator enabledIt( enabledList.constBegin() );
181  for ( ; layerIt != layerIdList.constEnd(); ++layerIt, ++tolIt, ++tolUnitIt, ++snapIt, ++enabledIt )
182  {
183  if ( *enabledIt != "enabled" )
184  {
185  // skip layer if snapping is not enabled
186  continue;
187  }
188 
189  //layer
190  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( QgsMapLayerRegistry::instance()->mapLayer( *layerIt ) );
191  if ( !vlayer || !vlayer->hasGeometryType() )
192  continue;
193 
194  snapLayer.mLayer = vlayer;
195 
196  //tolerance
197  snapLayer.mTolerance = tolIt->toDouble();
198  snapLayer.mUnitType = ( QgsTolerance::UnitType ) tolUnitIt->toInt();
199 
200  // segment or vertex
201  if ( *snapIt == "to_vertex" )
202  {
203  snapLayer.mSnapTo = QgsSnapper::SnapToVertex;
204  }
205  else if ( *snapIt == "to_segment" )
206  {
208  }
209  else
210  {
211  // to vertex and segment
213  }
214 
215  snapLayers.append( snapLayer );
216  }
217  }
218  else
219  {
220  // nothing in project. Use default snapping tolerance to vertex of current layer
221  QgsMapLayer* currentLayer = mMapCanvas->currentLayer();
222  if ( !currentLayer )
223  return 2;
224 
225  QgsVectorLayer* currentVectorLayer = qobject_cast<QgsVectorLayer *>( currentLayer );
226  if ( !currentVectorLayer )
227  return 3;
228 
229  snapLayer.mLayer = currentVectorLayer;
230 
231  //default snap mode
232  QSettings settings;
233  QString defaultSnapString = settings.value( "/qgis/digitizing/default_snap_mode", "off" ).toString();
234  if ( defaultSnapString == "to segment" )
235  {
237  }
238  else if ( defaultSnapString == "to vertex and segment" )
239  {
241  }
242  else if ( defaultSnapString == "to vertex" )
243  {
244  snapLayer.mSnapTo = QgsSnapper::SnapToVertex;
245  }
246  else
247  {
248  return 0;
249  }
250 
251  //default snapping tolerance (returned in map units)
252  snapLayer.mTolerance = QgsTolerance::defaultTolerance( currentVectorLayer, mMapCanvas->mapSettings() );
253  snapLayer.mUnitType = QgsTolerance::MapUnits;
254 
255  snapLayers.append( snapLayer );
256  }
257 
258  mSnapper->setSnapLayers( snapLayers );
259 
260  if ( mSnapper->snapPoint( point, results, excludePoints ) != 0 )
261  return 4;
262 
263  if ( intersectionSnapping != 1 )
264  return 0;
265 
266  QList<QgsSnappingResult> segments;
267  QList<QgsSnappingResult> points;
268  for ( QList<QgsSnappingResult>::const_iterator it = results.constBegin();
269  it != results.constEnd();
270  ++it )
271  {
272  if ( it->snappedVertexNr == -1 )
273  {
274  QgsDebugMsg( "segment" );
275  segments.push_back( *it );
276  }
277  else
278  {
279  QgsDebugMsg( "no segment" );
280  points.push_back( *it );
281  }
282  }
283 
284  if ( segments.length() < 2 )
285  return 0;
286 
287  QList<QgsSnappingResult> myResults;
288 
289  for ( QList<QgsSnappingResult>::const_iterator oSegIt = segments.constBegin();
290  oSegIt != segments.constEnd();
291  ++oSegIt )
292  {
293  QgsDebugMsg( QString::number( oSegIt->beforeVertexNr ) );
294 
295  QVector<QgsPoint> vertexPoints;
296  vertexPoints.append( oSegIt->beforeVertex );
297  vertexPoints.append( oSegIt->afterVertex );
298 
299  QgsGeometry* lineA = QgsGeometry::fromPolyline( vertexPoints );
300 
301  for ( QList<QgsSnappingResult>::iterator iSegIt = segments.begin();
302  iSegIt != segments.end();
303  ++iSegIt )
304  {
305  QVector<QgsPoint> vertexPoints;
306  vertexPoints.append( iSegIt->beforeVertex );
307  vertexPoints.append( iSegIt->afterVertex );
308  QgsGeometry* lineB = QgsGeometry::fromPolyline( vertexPoints );
309 
310  QgsGeometry* intersectionPoint = lineA->intersection( lineB );
311  if ( intersectionPoint->type() == QGis::Point )
312  {
313  //We have to check the intersection point is inside the tolerance distance for both layers
314  double toleranceA, toleranceB;
315  for ( int i = 0 ;i < snapLayers.size();++i )
316  {
317  if ( snapLayers[i].mLayer == oSegIt->layer )
318  {
319  toleranceA = QgsTolerance::toleranceInMapUnits( snapLayers[i].mTolerance, snapLayers[i].mLayer, mMapCanvas->mapSettings(), snapLayers[i].mUnitType );
320  }
321  if ( snapLayers[i].mLayer == iSegIt->layer )
322  {
323  toleranceB = QgsTolerance::toleranceInMapUnits( snapLayers[i].mTolerance, snapLayers[i].mLayer, mMapCanvas->mapSettings(), snapLayers[i].mUnitType );
324  }
325  }
326  QgsGeometry* cursorPoint = QgsGeometry::fromPoint( point );
327  double distance = intersectionPoint->distance( *cursorPoint );
328  if ( distance < toleranceA && distance < toleranceB )
329  {
330  iSegIt->snappedVertex = intersectionPoint->asPoint();
331  myResults.append( *iSegIt );
332  }
333  }
334  }
335  }
336 
337  if ( myResults.length() > 0 )
338  {
339  results.clear();
340  results = myResults;
341  }
342 
343  return 0;
344 }
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:48
static double toleranceInMapUnits(double tolerance, QgsMapLayer *layer, const QgsMapSettings &mapSettings, UnitType units=MapUnits)
Static function to translate tolerance value into current map unit value.
int snapPoint(const QPoint &startPoint, QList< QgsSnappingResult > &snappingResult, const QList< QgsPoint > &excludePoints=QList< QgsPoint >())
Does the snapping operation.
Definition: qgssnapper.cpp:44
double mTolerance
The snapping tolerances for the layers, always in source coordinate systems of the layer...
Definition: qgssnapper.h:90
All results within the given layer tolerances are returned.
Definition: qgssnapper.h:82
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
static QgsMapLayerRegistry * instance()
Definition: qgssingleton.h:23
QGis::GeometryType type()
Returns type of the vector.
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
const QgsMapToPixel & mapToPixel() const
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:104
QgsSnapper::SnappingType mSnapTo
What snapping type to use (snap to segment or to vertex)
Definition: qgssnapper.h:92
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
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
QgsGeometry * intersection(QgsGeometry *geometry)
Returns a geometry representing the points shared by this geometry and other.
A class to represent a point.
Definition: qgspoint.h:63
Map unit value.
Definition: qgstolerance.h:34
static QgsGeometry * fromPoint(const QgsPoint &point)
construct geometry from a point
QgsMapLayer * currentLayer()
returns current layer (set by legend widget)
QgsPoint toMapCoordinates(int x, int y) const
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...
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:362
QgsVectorLayer * mLayer
The layer to which snapping is applied.
Definition: qgssnapper.h:88
static QgsGeometry * fromPolyline(const QgsPolyline &polyline)
construct geometry from a polyline
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:31
void setSnapLayers(const QList< QgsSnapper::SnapLayer > &snapLayers)
Definition: qgssnapper.cpp:149
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.
static double vertexSearchRadius(QgsMapLayer *layer, const QgsMapSettings &mapSettings)
Static function to get vertex tolerance value for a layer.
void setMapCanvas(QgsMapCanvas *canvas)
void setSnapMode(QgsSnapper::SnappingMode snapMode)
Definition: qgssnapper.cpp:155
double distance(QgsGeometry &geom)