|
22 | 22 | #include "qgsattributedialog.h" |
23 | 23 | #include "qgisapp.h" |
24 | 24 | #include "qgsvectorlayerutils.h" |
25 | | -#include <QMouseEvent> |
26 | 25 |
|
| 26 | +#include <QMouseEvent> |
27 | 27 | #include <limits> |
28 | 28 |
|
29 | 29 | QgsMapToolFillRing::QgsMapToolFillRing( QgsMapCanvas *canvas ) |
@@ -52,38 +52,49 @@ void QgsMapToolFillRing::cadCanvasReleaseEvent( QgsMapMouseEvent *e ) |
52 | 52 | return; |
53 | 53 | } |
54 | 54 |
|
55 | | - //add point to list and to rubber band |
56 | | - if ( e->button() == Qt::LeftButton ) |
| 55 | + if ( e->button() == Qt::LeftButton && QApplication::keyboardModifiers() == Qt::ShiftModifier && !isCapturing() ) |
| 56 | + { |
| 57 | + // left button with shift fills an existing ring |
| 58 | + } |
| 59 | + else if ( e->button() == Qt::LeftButton ) |
57 | 60 | { |
| 61 | + // add point to list and to rubber band |
| 62 | + |
58 | 63 | int error = addVertex( e->mapPoint() ); |
59 | 64 | if ( error == 1 ) |
60 | 65 | { |
61 | | - //current layer is not a vector layer |
| 66 | + // current layer is not a vector layer |
62 | 67 | return; |
63 | 68 | } |
64 | 69 | else if ( error == 2 ) |
65 | 70 | { |
66 | | - //problem with coordinate transformation |
| 71 | + // problem with coordinate transformation |
67 | 72 | emit messageEmitted( tr( "Cannot transform the point to the layers coordinate system" ), QgsMessageBar::WARNING ); |
68 | 73 | return; |
69 | 74 | } |
70 | 75 |
|
71 | 76 | startCapturing(); |
| 77 | + return; |
72 | 78 | } |
73 | | - else if ( e->button() == Qt::RightButton ) |
| 79 | + else if ( e->button() != Qt::RightButton || !isCapturing() ) |
74 | 80 | { |
75 | | - if ( !isCapturing() ) |
76 | | - return; |
| 81 | + return; |
| 82 | + } |
| 83 | + |
| 84 | + QgsGeometry g; |
| 85 | + QgsFeatureId fid; |
77 | 86 |
|
| 87 | + if ( isCapturing() ) |
| 88 | + { |
78 | 89 | deleteTempRubberBand(); |
79 | 90 |
|
80 | 91 | closePolygon(); |
81 | 92 |
|
82 | 93 | vlayer->beginEditCommand( tr( "Ring added and filled" ) ); |
| 94 | + |
83 | 95 | QList< QgsPointXY > pointList = points(); |
84 | 96 |
|
85 | | - QgsFeatureId modifiedFid; |
86 | | - int addRingReturnCode = vlayer->addRing( pointList, &modifiedFid ); |
| 97 | + int addRingReturnCode = vlayer->addRing( pointList, &fid ); |
87 | 98 | if ( addRingReturnCode != 0 ) |
88 | 99 | { |
89 | 100 | QString errorMessage; |
@@ -114,65 +125,114 @@ void QgsMapToolFillRing::cadCanvasReleaseEvent( QgsMapMouseEvent *e ) |
114 | 125 | } |
115 | 126 | emit messageEmitted( tr( "could not add ring since %1." ).arg( errorMessage ), QgsMessageBar::CRITICAL ); |
116 | 127 | vlayer->destroyEditCommand(); |
| 128 | + |
| 129 | + return; |
117 | 130 | } |
118 | | - else |
| 131 | + |
| 132 | + g = QgsGeometry::fromPolygon( QgsPolygon() << pointList.toVector() ); |
| 133 | + } |
| 134 | + else |
| 135 | + { |
| 136 | + vlayer->beginEditCommand( tr( "Ring filled" ) ); |
| 137 | + |
| 138 | + g = ringUnderPoint( e->mapPoint(), fid ); |
| 139 | + |
| 140 | + if ( fid == -1 ) |
119 | 141 | { |
120 | | - // find parent feature and get it attributes |
121 | | - double xMin, xMax, yMin, yMax; |
122 | | - QgsRectangle bBox; |
| 142 | + emit messageEmitted( tr( "No ring found to fill." ), QgsMessageBar::CRITICAL ); |
| 143 | + vlayer->destroyEditCommand(); |
| 144 | + return; |
| 145 | + } |
| 146 | + } |
123 | 147 |
|
124 | | - xMin = std::numeric_limits<double>::max(); |
125 | | - xMax = -std::numeric_limits<double>::max(); |
126 | | - yMin = std::numeric_limits<double>::max(); |
127 | | - yMax = -std::numeric_limits<double>::max(); |
| 148 | + QgsRectangle bBox = g.boundingBox(); |
128 | 149 |
|
129 | | - Q_FOREACH ( const QgsPointXY &point, pointList ) |
130 | | - { |
131 | | - xMin = qMin( xMin, point.x() ); |
132 | | - xMax = qMax( xMax, point.x() ); |
133 | | - yMin = qMin( yMin, point.y() ); |
134 | | - yMax = qMax( yMax, point.y() ); |
135 | | - } |
| 150 | + QgsExpressionContext context = vlayer->createExpressionContext(); |
| 151 | + |
| 152 | + QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterFid( fid ) ); |
| 153 | + |
| 154 | + QgsFeature f; |
| 155 | + if ( fit.nextFeature( f ) ) |
| 156 | + { |
| 157 | + //create QgsFeature with wkb representation |
| 158 | + QgsFeature ft = QgsVectorLayerUtils::createFeature( vlayer, g, f.attributes().toMap(), &context ); |
| 159 | + |
| 160 | + bool res = false; |
| 161 | + if ( QApplication::keyboardModifiers() == Qt::ControlModifier ) |
| 162 | + { |
| 163 | + res = vlayer->addFeature( ft ); |
| 164 | + } |
| 165 | + else |
| 166 | + { |
| 167 | + QgsAttributeDialog *dialog = new QgsAttributeDialog( vlayer, &ft, false, nullptr, true ); |
| 168 | + dialog->setMode( QgsAttributeForm::AddFeatureMode ); |
| 169 | + res = dialog->exec(); // will also add the feature |
| 170 | + } |
136 | 171 |
|
137 | | - bBox.setXMinimum( xMin ); |
138 | | - bBox.setYMinimum( yMin ); |
139 | | - bBox.setXMaximum( xMax ); |
140 | | - bBox.setYMaximum( yMax ); |
| 172 | + if ( res ) |
| 173 | + { |
| 174 | + vlayer->endEditCommand(); |
| 175 | + } |
| 176 | + else |
| 177 | + { |
| 178 | + vlayer->destroyEditCommand(); |
| 179 | + } |
| 180 | + } |
141 | 181 |
|
142 | | - QgsExpressionContext context = vlayer->createExpressionContext(); |
| 182 | + if ( isCapturing() ) |
| 183 | + stopCapturing(); |
| 184 | +} |
143 | 185 |
|
144 | | - QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterFid( modifiedFid ) ); |
| 186 | +// TODO refactor - shamelessly copied from QgsMapToolDeleteRing::ringUnderPoint |
| 187 | +QgsGeometry QgsMapToolFillRing::ringUnderPoint( const QgsPointXY &p, QgsFeatureId &fid ) |
| 188 | +{ |
| 189 | + //check if we operate on a vector layer |
| 190 | + QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ); |
145 | 191 |
|
146 | | - QgsFeature f; |
147 | | - if ( fit.nextFeature( f ) ) |
148 | | - { |
149 | | - QgsGeometry g = QgsGeometry::fromPolygon( QgsPolygon() << pointList.toVector() ); |
| 192 | + //There is no clean way to find if we are inside the ring of a feature, |
| 193 | + //so we iterate over all the features visible in the canvas |
| 194 | + //If several rings are found at this position, the smallest one is chosen, |
| 195 | + //in order to be able to delete a ring inside another ring |
| 196 | + double area = std::numeric_limits<double>::max(); |
150 | 197 |
|
151 | | - //create QgsFeature with wkb representation |
152 | | - QgsFeature ft = QgsVectorLayerUtils::createFeature( vlayer, g, f.attributes().toMap(), &context ); |
| 198 | + QgsGeometry ringGeom; |
| 199 | + QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( toLayerCoordinates( vlayer, mCanvas->extent() ) ) ); |
153 | 200 |
|
154 | | - bool res = false; |
155 | | - if ( QApplication::keyboardModifiers() == Qt::ControlModifier ) |
156 | | - { |
157 | | - res = vlayer->addFeature( ft ); |
158 | | - } |
159 | | - else |
160 | | - { |
161 | | - QgsAttributeDialog *dialog = new QgsAttributeDialog( vlayer, &ft, false, nullptr, true ); |
162 | | - dialog->setMode( QgsAttributeForm::AddFeatureMode ); |
163 | | - res = dialog->exec(); // will also add the feature |
164 | | - } |
| 201 | + QgsFeature f; |
| 202 | + while ( fit.nextFeature( f ) ) |
| 203 | + { |
| 204 | + QgsGeometry g = f.geometry(); |
| 205 | + if ( g.isNull() ) |
| 206 | + continue; |
165 | 207 |
|
166 | | - if ( res ) |
167 | | - { |
168 | | - vlayer->endEditCommand(); |
169 | | - } |
170 | | - else |
| 208 | + QgsMultiPolygon pol; |
| 209 | + if ( g.wkbType() == QgsWkbTypes::Polygon || g.wkbType() == QgsWkbTypes::Polygon25D ) |
| 210 | + { |
| 211 | + pol = QgsMultiPolygon() << g.asPolygon(); |
| 212 | + } |
| 213 | + else |
| 214 | + { |
| 215 | + pol = g.asMultiPolygon(); |
| 216 | + } |
| 217 | + |
| 218 | + for ( int i = 0; i < pol.size() ; ++i ) |
| 219 | + { |
| 220 | + //for each part |
| 221 | + if ( pol[i].size() > 1 ) |
| 222 | + { |
| 223 | + for ( int j = 1; j < pol[i].size(); ++j ) |
171 | 224 | { |
172 | | - vlayer->destroyEditCommand(); |
| 225 | + QgsPolygon tempPol = QgsPolygon() << pol[i][j]; |
| 226 | + QgsGeometry tempGeom = QgsGeometry::fromPolygon( tempPol ); |
| 227 | + if ( tempGeom.area() < area && tempGeom.contains( &p ) ) |
| 228 | + { |
| 229 | + fid = f.id(); |
| 230 | + area = tempGeom.area(); |
| 231 | + ringGeom = tempGeom; |
| 232 | + } |
173 | 233 | } |
174 | 234 | } |
175 | 235 | } |
176 | | - stopCapturing(); |
177 | 236 | } |
| 237 | + return ringGeom; |
178 | 238 | } |
0 commit comments