QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgssnapper.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssnapper.cpp
3  --------------
4  begin : June 7, 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 "qgssnapper.h"
19 #include "qgsmapsettings.h"
20 #include "qgsmaprenderer.h"
21 #include "qgsmaptopixel.h"
22 #include "qgsvectorlayer.h"
23 #include <QMultiMap>
24 #include <QPoint>
25 #include <cmath>
26 
27 
29  : mMapSettings( mapRenderer->mapSettings() )
30  , mSnapMode( SnapWithOneResult )
31 {
32 
33 }
34 
36  : mMapSettings( mapSettings )
37  , mSnapMode( SnapWithOneResult )
38 {
39 }
40 
42 {
43 
44 }
45 
46 int QgsSnapper::snapPoint( const QPoint& startPoint, QList<QgsSnappingResult>& snappingResult, const QList<QgsPoint>& excludePoints )
47 {
48  QgsPoint mapCoordPoint = mMapSettings.mapToPixel().toMapCoordinates( startPoint.x(), startPoint.y() );
49  return snapMapPoint( mapCoordPoint, snappingResult, excludePoints );
50 }
51 
52 int QgsSnapper::snapMapPoint( const QgsPoint& mapCoordPoint, QList<QgsSnappingResult>& snappingResult, const QList<QgsPoint>& excludePoints )
53 {
54  snappingResult.clear();
55 
56  QMultiMap<double, QgsSnappingResult> snappingResultList;//all snapping results
57  QMultiMap<double, QgsSnappingResult> currentResultList; //snapping results of examined layer
58 
59  //start point in (output) map coordinates
60 
61  QgsPoint layerCoordPoint; //start point in layer coordinates
62  QgsSnappingResult newResult;
63 
64  QList<QgsSnapper::SnapLayer>::iterator snapLayerIt;
65  for ( snapLayerIt = mSnapLayers.begin(); snapLayerIt != mSnapLayers.end(); ++snapLayerIt )
66  {
67  if ( !snapLayerIt->mLayer->hasGeometryType() )
68  continue;
69 
70  currentResultList.clear();
71  //transform point from map coordinates to layer coordinates
72  layerCoordPoint = mMapSettings.mapToLayerCoordinates( snapLayerIt->mLayer, mapCoordPoint );
73 
74  double tolerance = QgsTolerance::toleranceInMapUnits( snapLayerIt->mTolerance, snapLayerIt->mLayer, mMapSettings, snapLayerIt->mUnitType );
75  if ( snapLayerIt->mLayer->snapWithContext( layerCoordPoint, tolerance,
76  currentResultList, snapLayerIt->mSnapTo ) != 0 )
77  {
78  //error
79  }
80 
81  //transform each result from layer crs to map crs (including distance)
82  QMultiMap<double, QgsSnappingResult>::iterator currentResultIt;
83  for ( currentResultIt = currentResultList.begin(); currentResultIt != currentResultList.end(); ++currentResultIt )
84  {
85  //for each snapping result: transform start point, snap point and other points into map coordinates to find out distance
86  //store results in snapping result list
87  newResult = currentResultIt.value();
88  newResult.snappedVertex = mMapSettings.layerToMapCoordinates( snapLayerIt->mLayer, currentResultIt.value().snappedVertex );
89  newResult.beforeVertex = mMapSettings.layerToMapCoordinates( snapLayerIt->mLayer, currentResultIt.value().beforeVertex );
90  newResult.afterVertex = mMapSettings.layerToMapCoordinates( snapLayerIt->mLayer, currentResultIt.value().afterVertex );
91  snappingResultList.insert( sqrt( newResult.snappedVertex.sqrDist( mapCoordPoint ) ), newResult );
92  }
93  }
94 
95  //excluded specific points from result
96  cleanResultList( snappingResultList, excludePoints );
97 
98  //evaluate results according to snap mode
99  QMultiMap<double, QgsSnappingResult>::iterator evalIt = snappingResultList.begin();
100  if ( evalIt == snappingResultList.end() )
101  {
102  return 0;
103  }
104 
105 
106  //Gives a priority to vertex snapping over segment snapping
107  QgsSnappingResult returnResult = evalIt.value();
108  for ( evalIt = snappingResultList.begin(); evalIt != snappingResultList.end(); ++evalIt )
109  {
110  if ( evalIt.value().snappedVertexNr != -1 )
111  {
112  returnResult = evalIt.value();
113  snappingResultList.erase( evalIt );
114  break;
115  }
116  }
117 
118  //We return the preferred result
119  snappingResult.push_back( returnResult );
120 
121  if ( mSnapMode == QgsSnapper::SnapWithOneResult )
122  {
123  //return only a single result, nothing more to do
124  }
125  else if ( mSnapMode == QgsSnapper::SnapWithResultsForSamePosition )
126  {
127  //take all snapping results within a certain tolerance because rounding differences may occur
128  double tolerance = 0.000001;
129 
130  for ( evalIt = snappingResultList.begin(); evalIt != snappingResultList.end(); ++evalIt )
131  {
132  if ( returnResult.snappedVertex.sqrDist( evalIt.value().snappedVertex ) < tolerance*tolerance )
133  {
134  snappingResult.push_back( evalIt.value() );
135  }
136  }
137 
138  }
139 
140  else //take all results
141  {
142  for ( evalIt = snappingResultList.begin(); evalIt != snappingResultList.end(); ++evalIt )
143  {
144  snappingResult.push_back( evalIt.value() );
145  }
146  }
147 
148  return 0;
149 }
150 
151 void QgsSnapper::setSnapLayers( const QList<QgsSnapper::SnapLayer>& snapLayers )
152 {
153  mSnapLayers = snapLayers;
154 }
155 
156 
158 {
159  mSnapMode = snapMode;
160 }
161 
162 void QgsSnapper::cleanResultList( QMultiMap<double, QgsSnappingResult>& list, const QList<QgsPoint>& excludeList ) const
163 {
164  QgsPoint currentResultPoint;
165  QgsSnappingResult currentSnappingResult;
166  QList<double> keysToRemove;
167 
168  QMultiMap<double, QgsSnappingResult>::iterator result_it = list.begin();
169  for ( ; result_it != list.end(); ++result_it )
170  {
171  currentSnappingResult = result_it.value();
172  if ( currentSnappingResult.snappedVertexNr != -1 )
173  {
174  currentResultPoint = currentSnappingResult.snappedVertex;
175  if ( excludeList.contains( currentResultPoint ) )
176  {
177  keysToRemove.push_back( result_it.key() );
178  }
179  }
180  }
181 
182  QList<double>::const_iterator remove_it = keysToRemove.constBegin();
183  for ( ; remove_it != keysToRemove.constEnd(); ++remove_it )
184  {
185  list.remove( *remove_it );
186  }
187 }