QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups 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->snapMapPoint( 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() );
254 
255  snapLayers.append( snapLayer );
256  }
257 
258  mSnapper->setSnapLayers( snapLayers );
259 
260  if ( mSnapper->snapMapPoint( 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 = 0;
315  double toleranceB = 0;
316  for ( int i = 0 ;i < snapLayers.size();++i )
317  {
318  if ( snapLayers[i].mLayer == oSegIt->layer )
319  {
320  toleranceA = QgsTolerance::toleranceInMapUnits( snapLayers[i].mTolerance, snapLayers[i].mLayer, mMapCanvas->mapSettings(), snapLayers[i].mUnitType );
321  }
322  if ( snapLayers[i].mLayer == iSegIt->layer )
323  {
324  toleranceB = QgsTolerance::toleranceInMapUnits( snapLayers[i].mTolerance, snapLayers[i].mLayer, mMapCanvas->mapSettings(), snapLayers[i].mUnitType );
325  }
326  }
327  QgsGeometry* cursorPoint = QgsGeometry::fromPoint( point );
328  double distance = intersectionPoint->distance( *cursorPoint );
329  if ( distance < toleranceA && distance < toleranceB )
330  {
331  iSegIt->snappedVertex = intersectionPoint->asPoint();
332  myResults.append( *iSegIt );
333  }
334  }
335  }
336  }
337 
338  if ( myResults.length() > 0 )
339  {
340  results.clear();
341  results = myResults;
342  }
343 
344  return 0;
345 }