|
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