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