22
22
#include < QDesktopServices>
23
23
24
24
// qgis includes...
25
- #include < qgsmaplayer.h>
26
- #include < qgsvectorlayer.h>
27
- #include < qgsapplication.h>
28
- #include < qgsproviderregistry.h>
29
- #include < qgsproject.h>
25
+ #include " qgsmaplayer.h"
26
+ #include " qgsvectorlayer.h"
27
+ #include " qgsapplication.h"
28
+ #include " qgsproviderregistry.h"
29
+ #include " qgsproject.h"
30
+ #include " qgsmultipolygon.h"
31
+ #include " qgspolygon.h"
32
+ #include " qgslinestring.h"
33
+ #include " qgsmultilinestring.h"
34
+ #include " qgsmultipoint.h"
30
35
// qgis test includes
31
36
#include " qgsmultirenderchecker.h"
32
37
@@ -52,13 +57,15 @@ class TestQgsRenderers : public QObject
52
57
void cleanup () {} // will be called after every testfunction.
53
58
54
59
void singleSymbol ();
60
+ void emptyGeometry ();
55
61
// void uniqueValue();
56
62
// void graduatedSymbol();
57
63
// void continuousSymbol();
58
64
private:
59
65
bool mTestHasError = false ;
60
66
bool setQml ( const QString &type ); // uniquevalue / continuous / single /
61
67
bool imageCheck ( const QString &type ); // as above
68
+ bool checkEmptyRender ( const QString &name, QgsVectorLayer *layer );
62
69
QgsMapSettings *mMapSettings = nullptr ;
63
70
QgsMapLayer *mpPointsLayer = nullptr ;
64
71
QgsMapLayer *mpLinesLayer = nullptr ;
@@ -147,6 +154,80 @@ void TestQgsRenderers::singleSymbol()
147
154
QVERIFY ( imageCheck ( " single" ) );
148
155
}
149
156
157
+ void TestQgsRenderers::emptyGeometry ()
158
+ {
159
+ // test rendering an empty geometry
160
+ // note - this test is of limited use, because the features with empty geometries should not be rendered
161
+ // by the feature iterator given that renderer uses a filter rect on the request. It's placed here
162
+ // for manual testing by commenting out the part of the renderer which places the filterrect on the
163
+ // layer's feature request. The purpose of this test is to ensure that we do not crash for empty geometries,
164
+ // as it's possible that malformed providers OR bugs in underlying libraries will still return empty geometries
165
+ // even when a filter rect request was made, and we shouldn't crash for these.
166
+ QgsVectorLayer *vl = new QgsVectorLayer ( QStringLiteral ( " MultiPolygon?crs=epsg:4326&field=pk:int&field=col1:string" ), QStringLiteral ( " vl" ), QStringLiteral ( " memory" ) );
167
+ QVERIFY ( vl->isValid () );
168
+ QgsProject::instance ()->addMapLayer ( vl );
169
+
170
+ QgsFeature f;
171
+ std::unique_ptr< QgsMultiPolygon > mp = qgis::make_unique< QgsMultiPolygon >();
172
+ mp->addGeometry ( new QgsPolygon () );
173
+ f.setGeometry ( QgsGeometry ( std::move ( mp ) ) );
174
+ QVERIFY ( vl->dataProvider ()->addFeature ( f ) );
175
+ QVERIFY ( checkEmptyRender ( " Multipolygon" , vl ) );
176
+
177
+ // polygon
178
+ vl = new QgsVectorLayer ( QStringLiteral ( " Polygon?crs=epsg:4326&field=pk:int&field=col1:string" ), QStringLiteral ( " vl" ), QStringLiteral ( " memory" ) );
179
+ QVERIFY ( vl->isValid () );
180
+ QgsProject::instance ()->addMapLayer ( vl );
181
+ f.setGeometry ( QgsGeometry ( new QgsPolygon () ) );
182
+ QVERIFY ( vl->dataProvider ()->addFeature ( f ) );
183
+ QVERIFY ( checkEmptyRender ( " Polygon" , vl ) );
184
+
185
+ // linestring
186
+ vl = new QgsVectorLayer ( QStringLiteral ( " LineString?crs=epsg:4326&field=pk:int&field=col1:string" ), QStringLiteral ( " vl" ), QStringLiteral ( " memory" ) );
187
+ QVERIFY ( vl->isValid () );
188
+ QgsProject::instance ()->addMapLayer ( vl );
189
+ f.setGeometry ( QgsGeometry ( new QgsLineString () ) );
190
+ QVERIFY ( vl->dataProvider ()->addFeature ( f ) );
191
+ QVERIFY ( checkEmptyRender ( " LineString" , vl ) );
192
+
193
+ // multilinestring
194
+ vl = new QgsVectorLayer ( QStringLiteral ( " MultiLineString?crs=epsg:4326&field=pk:int&field=col1:string" ), QStringLiteral ( " vl" ), QStringLiteral ( " memory" ) );
195
+ QVERIFY ( vl->isValid () );
196
+ QgsProject::instance ()->addMapLayer ( vl );
197
+ std::unique_ptr< QgsMultiLineString > mls = qgis::make_unique< QgsMultiLineString >();
198
+ mls->addGeometry ( new QgsLineString () );
199
+ f.setGeometry ( QgsGeometry ( std::move ( mls ) ) );
200
+ QVERIFY ( vl->dataProvider ()->addFeature ( f ) );
201
+ QVERIFY ( checkEmptyRender ( " MultiLineString" , vl ) );
202
+
203
+ // multipoint
204
+ vl = new QgsVectorLayer ( QStringLiteral ( " MultiPoint?crs=epsg:4326&field=pk:int&field=col1:string" ), QStringLiteral ( " vl" ), QStringLiteral ( " memory" ) );
205
+ QVERIFY ( vl->isValid () );
206
+ QgsProject::instance ()->addMapLayer ( vl );
207
+ std::unique_ptr< QgsMultiPoint > mlp = qgis::make_unique< QgsMultiPoint >();
208
+ f.setGeometry ( QgsGeometry ( std::move ( mlp ) ) );
209
+ QVERIFY ( vl->dataProvider ()->addFeature ( f ) );
210
+ QVERIFY ( checkEmptyRender ( " MultiPoint" , vl ) );
211
+ }
212
+
213
+ bool TestQgsRenderers::checkEmptyRender ( const QString &testName, QgsVectorLayer *layer )
214
+ {
215
+ QgsMapSettings ms;
216
+ QgsRectangle extent ( -180 , -90 , 180 , 90 );
217
+ ms.setExtent ( extent );
218
+ ms.setFlag ( QgsMapSettings::ForceVectorOutput );
219
+ ms.setOutputDpi ( 96 );
220
+ ms.setLayers ( QList< QgsMapLayer * >() << layer );
221
+ QgsMultiRenderChecker myChecker;
222
+ myChecker.setControlName ( " expected_emptygeometry" );
223
+ myChecker.setMapSettings ( ms );
224
+ myChecker.setControlPathPrefix ( " map_renderer" );
225
+ myChecker.setColorTolerance ( 15 );
226
+ bool myResultFlag = myChecker.runTest ( testName, 200 );
227
+ mReport += myChecker.report ();
228
+ return myResultFlag;
229
+ }
230
+
150
231
//
151
232
// Private helper functions not called directly by CTest
152
233
//
0 commit comments