Branch data Line data Source code
1 : : /***************************************************************************
2 : : * qgsgeometrysnapper.h *
3 : : * ------------------- *
4 : : * copyright : (C) 2014 by Sandro Mani / Sourcepole AG *
5 : : * email : smani@sourcepole.ch *
6 : : ***************************************************************************/
7 : :
8 : : /***************************************************************************
9 : : * *
10 : : * This program is free software; you can redistribute it and/or modify *
11 : : * it under the terms of the GNU General Public License as published by *
12 : : * the Free Software Foundation; either version 2 of the License, or *
13 : : * (at your option) any later version. *
14 : : * *
15 : : ***************************************************************************/
16 : :
17 : : #ifndef QGS_GEOMETRY_SNAPPER_H
18 : : #define QGS_GEOMETRY_SNAPPER_H
19 : :
20 : : #include <QMutex>
21 : : #include <QFuture>
22 : : #include <QStringList>
23 : : #include "qgsspatialindex.h"
24 : : #include "qgsabstractgeometry.h"
25 : : #include "qgspoint.h"
26 : : #include "qgsgeometry.h"
27 : : #include "qgis_analysis.h"
28 : :
29 : : class QgsVectorLayer;
30 : :
31 : : /**
32 : : * \class QgsGeometrySnapper
33 : : * \ingroup analysis
34 : : * \brief QgsGeometrySnapper allows a geometry to be snapped to the geometries within a
35 : : * different reference layer. Vertices in the geometries will be modified to
36 : : * match the reference layer features within a specified snap tolerance.
37 : : * \since QGIS 3.0
38 : : */
39 : 13 : class ANALYSIS_EXPORT QgsGeometrySnapper : public QObject
40 : : {
41 : : Q_OBJECT
42 : :
43 : : public:
44 : :
45 : : //! Snapping modes
46 : : enum SnapMode
47 : : {
48 : : PreferNodes = 0, //!< Prefer to snap to nodes, even when a segment may be closer than a node. New nodes will be inserted to make geometries follow each other exactly when inside allowable tolerance.
49 : : PreferClosest, //!< Snap to closest point, regardless of it is a node or a segment. New nodes will be inserted to make geometries follow each other exactly when inside allowable tolerance.
50 : : PreferNodesNoExtraVertices, //!< Prefer to snap to nodes, even when a segment may be closer than a node. No new nodes will be inserted.
51 : : PreferClosestNoExtraVertices, //!< Snap to closest point, regardless of it is a node or a segment. No new nodes will be inserted.
52 : : EndPointPreferNodes, //!< Only snap start/end points of lines (point features will also be snapped, polygon features will not be modified), prefer to snap to nodes
53 : : EndPointPreferClosest, //!< Only snap start/end points of lines (point features will also be snapped, polygon features will not be modified), snap to closest point
54 : : EndPointToEndPoint, //!< Only snap the start/end points of lines to other start/end points of lines
55 : : };
56 : :
57 : : /**
58 : : * Constructor for QgsGeometrySnapper. A reference feature source which contains geometries to snap to must be
59 : : * set. It is assumed that all geometries snapped using this object will have the
60 : : * same CRS as the reference source (ie, no reprojection is performed).
61 : : */
62 : : QgsGeometrySnapper( QgsFeatureSource *referenceSource );
63 : :
64 : : /**
65 : : * Snaps a geometry to the reference layer and returns the result. The geometry must be in the same
66 : : * CRS as the reference layer, and must have the same type as the reference layer geometry. The snap tolerance
67 : : * is specified in the layer units for the reference layer.
68 : : */
69 : : QgsGeometry snapGeometry( const QgsGeometry &geometry, double snapTolerance, SnapMode mode = PreferNodes ) const;
70 : :
71 : : /**
72 : : * Snaps a set of features to the reference layer and returns the result. This operation is
73 : : * multithreaded for performance. The featureSnapped() signal will be emitted each time a feature
74 : : * is processed. The snap tolerance is specified in the layer units for the reference layer.
75 : : */
76 : : QgsFeatureList snapFeatures( const QgsFeatureList &features, double snapTolerance, SnapMode mode = PreferNodes );
77 : :
78 : : /**
79 : : * Snaps a single geometry against a list of reference geometries.
80 : : */
81 : : static QgsGeometry snapGeometry( const QgsGeometry &geometry, double snapTolerance, const QList<QgsGeometry> &referenceGeometries, SnapMode mode = PreferNodes );
82 : :
83 : : signals:
84 : :
85 : : //! Emitted each time a feature has been processed when calling snapFeatures()
86 : : void featureSnapped();
87 : :
88 : : private:
89 : : struct ProcessFeatureWrapper
90 : : {
91 : : QgsGeometrySnapper *instance = nullptr;
92 : : double snapTolerance;
93 : : SnapMode mode;
94 : 0 : explicit ProcessFeatureWrapper( QgsGeometrySnapper *_instance, double snapTolerance, SnapMode mode )
95 : 0 : : instance( _instance )
96 : 0 : , snapTolerance( snapTolerance )
97 : 0 : , mode( mode )
98 : 0 : {}
99 : 0 : void operator()( QgsFeature &feature ) { instance->processFeature( feature, snapTolerance, mode ); }
100 : : };
101 : :
102 : : enum PointFlag { SnappedToRefNode, SnappedToRefSegment, Unsnapped };
103 : :
104 : : QgsFeatureSource *mReferenceSource = nullptr;
105 : : QgsFeatureList mInputFeatures;
106 : :
107 : : QgsSpatialIndex mIndex;
108 : : mutable QMutex mIndexMutex;
109 : : mutable QMutex mReferenceLayerMutex;
110 : :
111 : : void processFeature( QgsFeature &feature, double snapTolerance, SnapMode mode );
112 : :
113 : : static int polyLineSize( const QgsAbstractGeometry *geom, int iPart, int iRing );
114 : :
115 : : };
116 : :
117 : :
118 : : /**
119 : : * \class QgsInternalGeometrySnapper
120 : : * \ingroup analysis
121 : : * \brief QgsInternalGeometrySnapper allows a set of geometries to be snapped to each other. It can be used to close gaps in layers.
122 : : *
123 : : * To use QgsInternalGeometrySnapper, first construct the snapper using the desired snap parameters. Then,
124 : : * features are fed to to the snapper one-by-one by calling snapFeature(). Each feature passed by calling
125 : : * snapFeature() will be snapped to any features which have already been processed by the snapper.
126 : : *
127 : : * After processing all desired features, the results can be fetched by calling snappedGeometries().
128 : : * The returned QgsGeometryMap can be passed to QgsVectorDataProvider::changeGeometryValues() to save
129 : : * the snapped geometries back to the source layer.
130 : : *
131 : : * \since QGIS 3.0
132 : : */
133 : 21 : class ANALYSIS_EXPORT QgsInternalGeometrySnapper
134 : : {
135 : :
136 : : public:
137 : :
138 : : /**
139 : : * Constructor for QgsInternalGeometrySnapper. The \a snapTolerance and \a mode parameters dictate
140 : : * how geometries will be snapped by the snapper.
141 : : */
142 : : QgsInternalGeometrySnapper( double snapTolerance, QgsGeometrySnapper::SnapMode mode = QgsGeometrySnapper::PreferNodes );
143 : :
144 : : /**
145 : : * Snaps a single feature's geometry against all feature geometries already processed by
146 : : * calls to snapFeature() in this object, and returns the snapped geometry.
147 : : */
148 : : QgsGeometry snapFeature( const QgsFeature &feature );
149 : :
150 : : /**
151 : : * Returns a QgsGeometryMap of all feature geometries snapped by this object.
152 : : */
153 : 1 : QgsGeometryMap snappedGeometries() const { return mProcessedGeometries; }
154 : :
155 : : private:
156 : :
157 : : bool mFirstFeature = true;
158 : : double mSnapTolerance = 0;
159 : : QgsGeometrySnapper::SnapMode mMode = QgsGeometrySnapper::PreferNodes;
160 : : QgsSpatialIndex mProcessedIndex;
161 : : QgsGeometryMap mProcessedGeometries;
162 : :
163 : : };
164 : :
165 : : #ifndef SIP_RUN
166 : :
167 : : ///@cond PRIVATE
168 : : class QgsSnapIndex
169 : : {
170 : : public:
171 : : struct CoordIdx
172 : : {
173 : 1382 : CoordIdx( const QgsAbstractGeometry *_geom, QgsVertexId _vidx )
174 : 1382 : : geom( _geom )
175 : 1382 : , vidx( _vidx )
176 : 1382 : {}
177 : 4294 : QgsPoint point() const { return geom->vertexAt( vidx ); }
178 : :
179 : : const QgsAbstractGeometry *geom = nullptr;
180 : : QgsVertexId vidx;
181 : : };
182 : :
183 : : enum SnapType { SnapPoint, SnapEndPoint, SnapSegment };
184 : :
185 : : class SnapItem
186 : : {
187 : : public:
188 : 1814 : virtual ~SnapItem() = default;
189 : : SnapType type;
190 : : virtual QgsPoint getSnapPoint( const QgsPoint &p ) const = 0;
191 : :
192 : : protected:
193 : 1814 : explicit SnapItem( SnapType _type ) : type( _type ) {}
194 : : };
195 : :
196 : 1382 : class PointSnapItem : public QgsSnapIndex::SnapItem
197 : : {
198 : : public:
199 : : explicit PointSnapItem( const CoordIdx *_idx, bool isEndPoint );
200 : : QgsPoint getSnapPoint( const QgsPoint &/*p*/ ) const override;
201 : : const CoordIdx *idx = nullptr;
202 : : };
203 : :
204 : 2246 : class SegmentSnapItem : public QgsSnapIndex::SnapItem
205 : : {
206 : : public:
207 : : SegmentSnapItem( const CoordIdx *_idxFrom, const CoordIdx *_idxTo );
208 : : QgsPoint getSnapPoint( const QgsPoint &p ) const override;
209 : : bool getIntersection( const QgsPoint &p1, const QgsPoint &p2, QgsPoint &inter ) const;
210 : : bool getProjection( const QgsPoint &p, QgsPoint &pProj );
211 : : const CoordIdx *idxFrom = nullptr;
212 : : const CoordIdx *idxTo = nullptr;
213 : : };
214 : :
215 : : QgsSnapIndex( const QgsPoint &origin, double cellSize );
216 : : ~QgsSnapIndex();
217 : :
218 : : QgsSnapIndex( const QgsSnapIndex &rh ) = delete;
219 : : QgsSnapIndex &operator=( const QgsSnapIndex &rh ) = delete;
220 : :
221 : : void addGeometry( const QgsAbstractGeometry *geom );
222 : : QgsPoint getClosestSnapToPoint( const QgsPoint &p, const QgsPoint &q );
223 : : SnapItem *getSnapItem( const QgsPoint &pos, double tol, PointSnapItem **pSnapPoint = nullptr, SegmentSnapItem **pSnapSegment = nullptr, bool endPointOnly = false ) const;
224 : :
225 : : private:
226 : : typedef QList<SnapItem *> Cell;
227 : : typedef QPair<QgsPoint, QgsPoint> Segment;
228 : :
229 : 331 : class GridRow
230 : : {
231 : : public:
232 : 331 : GridRow() = default;
233 : : ~GridRow();
234 : : const Cell *getCell( int col ) const;
235 : : Cell &getCreateCell( int col );
236 : : QList<SnapItem *> getSnapItems( int colStart, int colEnd ) const;
237 : :
238 : : private:
239 : : QList<QgsSnapIndex::Cell> mCells;
240 : 331 : int mColStartIdx = 0;
241 : : };
242 : :
243 : : QgsPoint mOrigin;
244 : : double mCellSize;
245 : :
246 : : QList<CoordIdx *> mCoordIdxs;
247 : : QList<GridRow> mGridRows;
248 : : int mRowsStartIdx;
249 : :
250 : : void addPoint( const CoordIdx *idx, bool isEndPoint );
251 : : void addSegment( const CoordIdx *idxFrom, const CoordIdx *idxTo );
252 : : const Cell *getCell( int col, int row ) const;
253 : : Cell &getCreateCell( int col, int row );
254 : :
255 : : };
256 : :
257 : : ///@endcond
258 : :
259 : : #endif
260 : :
261 : : #endif // QGS_GEOMETRY_SNAPPER_H
|