1
/***************************************************************************
3
--------------------------------------
5
Copyright : (C) 2008 by Tim Sutton
6
Email : tim @ linfiniti.com
7
***************************************************************************
9
* This program is free software; you can redistribute it and/or modify *
10
* it under the terms of the GNU General Public License as published by *
11
* the Free Software Foundation; either version 2 of the License, or *
12
* (at your option) any later version. *
14
***************************************************************************/
18
#include <QStringList>
5
//header for class being tested
20
#include <QApplication>
23
#include <QDesktopServices>
31
#include <qgsapplication.h>
6
32
#include <qgsgeometry.h>
35
//qgs unit test utility class
36
#include "qgsrenderchecker.h"
38
/** \ingroup UnitTests
39
* This is a unit test for the different geometry operations on vector features.
8
41
class TestQgsGeometry: public QObject
12
void QgsGeometryQgsGeometry()
16
void QgsGeometrysetWkbAndOwnership()
20
void QgsGeometrywkbBuffer()
24
void QgsGeometrywkbSize()
32
void QgsGeometrysetGeos()
36
void QgsGeometryclosestVertex()
40
void QgsGeometryinsertVertexBefore()
44
void QgsGeometrymoveVertexAt()
48
void QgsGeometrydeleteVertexAt()
52
void QgsGeometryvertexAt()
56
void QgsGeometryclosestVertexWithContext()
60
void QgsGeometryclosestSegmentWithContext()
64
void QgsGeometryboundingBox()
68
void QgsGeometryintersects()
72
void QgsGeometryexportToWkt()
76
void QgsGeometrygeosGeometry()
80
void QgsGeometryexportWkbToGeos()
84
void QgsGeometryexportGeosToWkb()
88
void QgsGeometrydistanceSquaredPointToSegment()
95
QTEST_MAIN(TestQgsGeometry)
96
#include "testqgsgeometry.moc.cpp"
45
void initTestCase();// will be called before the first testfunction is executed.
46
void cleanupTestCase();// will be called after the last testfunction was executed.
47
void init();// will be called before each testfunction is executed.
48
void cleanup();// will be called after every testfunction.
50
void simplifyCheck1();
51
void intersectionCheck1();
52
void intersectionCheck2();
55
void differenceCheck1();
56
void differenceCheck2();
59
/** A helper method to do a render check to see if the geometry op is as expected */
60
bool renderCheck( QString theTestName, QString theComment = "" );
61
/** A helper method to return wkb geometry type as a string */
62
QString wkbTypeAsString( QGis::WkbType theType );
63
/** A helper method to dump to qdebug the geometry of a multipolygon */
64
void dumpMultiPolygon( QgsMultiPolygon &theMultiPolygon );
65
/** A helper method to dump to qdebug the geometry of a polygon */
66
void dumpPolygon( QgsPolygon &thePolygon );
67
/** A helper method to dump to qdebug the geometry of a polyline */
68
void dumpPolyline( QgsPolyline &thePolyline );
81
QgsPolyline mPolylineA;
82
QgsPolyline mPolylineB;
83
QgsPolyline mPolylineC;
84
QgsGeometry * mpPolylineGeometryD;
88
QgsGeometry * mpPolygonGeometryA;
89
QgsGeometry * mpPolygonGeometryB;
90
QgsGeometry * mpPolygonGeometryC;
101
void TestQgsGeometry::init()
104
// Reset / reinitialise the geometries before each test is run
106
mPoint1 = QgsPoint( 20.0, 20.0 );
107
mPoint2 = QgsPoint( 80.0, 20.0 );
108
mPoint3 = QgsPoint( 80.0, 80.0 );
109
mPoint4 = QgsPoint( 20.0, 80.0 );
110
mPointA = QgsPoint( 40.0, 40.0 );
111
mPointB = QgsPoint( 100.0, 40.0 );
112
mPointC = QgsPoint( 100.0, 100.0 );
113
mPointD = QgsPoint( 40.0, 100.0 );
114
mPointW = QgsPoint( 200.0, 200.0 );
115
mPointX = QgsPoint( 240.0, 200.0 );
116
mPointY = QgsPoint( 240.0, 240.0 );
117
mPointZ = QgsPoint( 200.0, 240.0 );
119
mWktLine = QString( "LINESTRING(117.623198 35.198654, 117.581274 35.198654, 117.078178 35.324427, 116.868555 35.534051, 116.617007 35.869448, 116.491233 35.953297, 116.155836 36.288694, 116.071987 36.372544, 115.443117 36.749865, 114.814247 37.043338, 114.311152 37.169112, 113.388810 37.378735, 113.095337 37.378735, 112.592241 37.378735, 111.753748 37.294886, 111.502201 37.252961, 111.082954 37.127187, 110.747557 37.127187, 110.160612 36.917564, 110.034838 36.833715, 109.741366 36.749865, 109.573667 36.666016, 109.238270 36.498317, 109.070571 36.414468, 108.819023 36.288694, 108.693250 36.246770, 108.483626 36.162920, 107.645134 35.911372, 106.597017 35.869448, 106.051997 35.701749, 105.800449 35.617900, 105.590826 35.575975, 105.297354 35.575975, 104.961956 35.575975, 104.710409 35.534051, 104.458861 35.492126, 103.871916 35.492126, 103.788066 35.492126, 103.326895 35.408277, 102.949574 35.408277, 102.488402 35.450201, 102.069156 35.450201, 101.482211 35.450201, 100.937191 35.659825, 100.308321 35.869448, 100.056773 36.037146, 99.050582 36.079071, 97.667069 35.743674, 97.163973 35.617900, 96.115857 35.534051, 95.612761 35.534051, 94.396947 35.911372, 93.684228 36.288694, 92.929584 36.833715, 92.258790 37.169112, 91.629920 37.504509, 90.414105 37.881831, 90.414105 37.881831, 90.246407 37.923755, 89.491763 37.839906, 89.156366 37.672207, 88.485572 37.504509, 87.814778 37.252961, 87.563230 37.169112, 87.143983 37.043338, 85.970093 36.875639, 85.802395 36.875639, 84.083484 36.959489, 84.041560 37.043338, 82.951519 37.546433, 82.699971 37.630283)" );
127
mPolylineA << mPoint1 << mPoint2 << mPoint3 << mPoint4 << mPoint1;
128
mPolygonA << mPolylineA;
129
//Polygon B intersects Polygon A
130
mPolylineB << mPointA << mPointB << mPointC << mPointD << mPointA;
131
mPolygonB << mPolylineB;
132
// Polygon C should intersect no other polys
133
mPolylineC << mPointW << mPointX << mPointY << mPointZ << mPointW;
134
mPolygonC << mPolylineC;
136
mpPolylineGeometryD = QgsGeometry::fromWkt( mWktLine );
138
//polygon: first item of the list is outer ring,
139
// inner rings (if any) start from second item
140
mpPolygonGeometryA = QgsGeometry::fromPolygon( mPolygonA );
141
mpPolygonGeometryB = QgsGeometry::fromPolygon( mPolygonB );
142
mpPolygonGeometryC = QgsGeometry::fromPolygon( mPolygonC );
144
mImage = QImage( 250, 250, QImage::Format_RGB32 );
145
mImage.fill( qRgb( 152, 219, 249 ) );
146
mpPainter = new QPainter( &mImage );
148
// Draw the test shapes first
151
mPen1.setBrush( Qt::green );
152
mpPainter->setPen( mPen1 );
153
dumpPolygon( mPolygonA );
154
mPen1.setBrush( Qt::red );
155
mpPainter->setPen( mPen1 );
156
dumpPolygon( mPolygonB );
157
mPen1.setBrush( Qt::blue );
158
mpPainter->setPen( mPen1 );
159
dumpPolygon( mPolygonC );
163
mPen2.setBrush( Qt::black );
164
QBrush myBrush( Qt::DiagCrossPattern );
167
//set the pen to a different color -
168
//any test outs will be drawn in pen2
169
mpPainter->setPen( mPen2 );
170
mpPainter->setBrush( myBrush );
173
void TestQgsGeometry::cleanup()
175
// will be called after every testfunction.
176
delete mpPolygonGeometryA;
177
delete mpPolygonGeometryB;
178
delete mpPolygonGeometryC;
182
void TestQgsGeometry::initTestCase()
185
// Runs once before any tests are run
187
// init QGIS's paths - true means that all path will be inited from prefix
188
QString qgisPath = QCoreApplication::applicationDirPath();
189
QgsApplication::setPrefixPath( INSTALL_PREFIX, true );
190
QgsApplication::showSettings();
191
mReport += "<h1>Geometry Tests</h1>\n";
192
mReport += "<p><font color=\"green\">Green = polygonA</font></p>\n";
193
mReport += "<p><font color=\"red\">Red = polygonB</font></p>\n";
194
mReport += "<p><font color=\"blue\">Blue = polygonC</font></p>\n";
198
void TestQgsGeometry::cleanupTestCase()
201
// Runs once after all tests are run
203
QString myReportFile = QDir::tempPath() + QDir::separator() + "geometrytest.html";
204
QFile myFile( myReportFile );
205
if ( myFile.open( QIODevice::WriteOnly ) )
207
QTextStream myQTextStream( &myFile );
208
myQTextStream << mReport;
210
QDesktopServices::openUrl( "file://" + myReportFile );
215
void TestQgsGeometry::simplifyCheck1()
217
QVERIFY( mpPolylineGeometryD->simplify( 0.5 ) );
218
// should be a single polygon as A intersect B
219
QgsGeometry * mypSimplifyGeometry = mpPolylineGeometryD->simplify( 0.5 );
220
qDebug( "Geometry Type: %s", wkbTypeAsString( mypSimplifyGeometry->wkbType() ).toLocal8Bit().constData() );
221
QVERIFY( mypSimplifyGeometry->wkbType() == QGis::WKBLineString );
222
QgsPolyline myLine = mypSimplifyGeometry->asPolyline();
223
QVERIFY( myLine.size() > 0 ); //check that the union created a feature
224
dumpPolyline( myLine );
225
delete mypSimplifyGeometry;
226
QVERIFY( renderCheck( "geometry_simplifyCheck1", "Checking simplify of line" ) );
228
void TestQgsGeometry::intersectionCheck1()
230
QVERIFY( mpPolygonGeometryA->intersects( mpPolygonGeometryB ) );
231
// should be a single polygon as A intersect B
232
QgsGeometry * mypIntersectionGeometry = mpPolygonGeometryA->intersection( mpPolygonGeometryB );
233
qDebug( "Geometry Type: %s", wkbTypeAsString( mypIntersectionGeometry->wkbType() ).toLocal8Bit().constData() );
234
QVERIFY( mypIntersectionGeometry->wkbType() == QGis::WKBPolygon );
235
QgsPolygon myPolygon = mypIntersectionGeometry->asPolygon();
236
QVERIFY( myPolygon.size() > 0 ); //check that the union created a feature
237
dumpPolygon( myPolygon );
238
delete mypIntersectionGeometry;
239
QVERIFY( renderCheck( "geometry_intersectionCheck1", "Checking if A intersects B" ) );
241
void TestQgsGeometry::intersectionCheck2()
243
QVERIFY( !mpPolygonGeometryA->intersects( mpPolygonGeometryC ) );
246
void TestQgsGeometry::unionCheck1()
248
// should be a multipolygon with 2 parts as A does not intersect C
249
QgsGeometry * mypUnionGeometry = mpPolygonGeometryA->combine( mpPolygonGeometryC );
250
qDebug( "Geometry Type: %s", wkbTypeAsString( mypUnionGeometry->wkbType() ).toLocal8Bit().constData() );
251
QVERIFY( mypUnionGeometry->wkbType() == QGis::WKBMultiPolygon );
252
QgsMultiPolygon myMultiPolygon = mypUnionGeometry->asMultiPolygon();
253
QVERIFY( myMultiPolygon.size() > 0 ); //check that the union did not fail
254
dumpMultiPolygon( myMultiPolygon );
255
delete mypUnionGeometry;
256
QVERIFY( renderCheck( "geometry_unionCheck1", "Checking A union C produces 2 polys" ) );
259
void TestQgsGeometry::unionCheck2()
261
// should be a single polygon as A intersect B
262
QgsGeometry * mypUnionGeometry = mpPolygonGeometryA->combine( mpPolygonGeometryB );
263
qDebug( "Geometry Type: %s", wkbTypeAsString( mypUnionGeometry->wkbType() ).toLocal8Bit().constData() );
264
QVERIFY( mypUnionGeometry->wkbType() == QGis::WKBPolygon );
265
QgsPolygon myPolygon = mypUnionGeometry->asPolygon();
266
QVERIFY( myPolygon.size() > 0 ); //check that the union created a feature
267
dumpPolygon( myPolygon );
268
delete mypUnionGeometry;
269
QVERIFY( renderCheck( "geometry_unionCheck2", "Checking A union B produces single union poly" ) );
272
void TestQgsGeometry::differenceCheck1()
274
// should be same as A since A does not intersect C so diff is 100% of A
275
QgsGeometry * mypDifferenceGeometry = mpPolygonGeometryA->difference( mpPolygonGeometryC );
276
qDebug( "Geometry Type: %s", wkbTypeAsString( mypDifferenceGeometry->wkbType() ).toLocal8Bit().constData() );
277
QVERIFY( mypDifferenceGeometry->wkbType() == QGis::WKBPolygon );
278
QgsPolygon myPolygon = mypDifferenceGeometry->asPolygon();
279
QVERIFY( myPolygon.size() > 0 ); //check that the union did not fail
280
dumpPolygon( myPolygon );
281
delete mypDifferenceGeometry;
282
QVERIFY( renderCheck( "geometry_differenceCheck1", "Checking (A - C) = A" ) );
285
void TestQgsGeometry::differenceCheck2()
287
// should be a single polygon as (A - B) = subset of A
288
QgsGeometry * mypDifferenceGeometry = mpPolygonGeometryA->difference( mpPolygonGeometryB );
289
qDebug( "Geometry Type: %s", wkbTypeAsString( mypDifferenceGeometry->wkbType() ).toLocal8Bit().constData() );
290
QVERIFY( mypDifferenceGeometry->wkbType() == QGis::WKBPolygon );
291
QgsPolygon myPolygon = mypDifferenceGeometry->asPolygon();
292
QVERIFY( myPolygon.size() > 0 ); //check that the union created a feature
293
dumpPolygon( myPolygon );
294
delete mypDifferenceGeometry;
295
QVERIFY( renderCheck( "geometry_differenceCheck2", "Checking (A - B) = subset of A" ) );
297
void TestQgsGeometry::bufferCheck()
299
// should be a single polygon
300
QgsGeometry * mypBufferGeometry = mpPolygonGeometryB->buffer( 10, 10 );
301
qDebug( "Geometry Type: %s", wkbTypeAsString( mypBufferGeometry->wkbType() ).toLocal8Bit().constData() );
302
QVERIFY( mypBufferGeometry->wkbType() == QGis::WKBPolygon );
303
QgsPolygon myPolygon = mypBufferGeometry->asPolygon();
304
QVERIFY( myPolygon.size() > 0 ); //check that the buffer created a feature
305
dumpPolygon( myPolygon );
306
delete mypBufferGeometry;
307
QVERIFY( renderCheck( "geometry_bufferCheck", "Checking buffer(10,10) of B" ) );
309
bool TestQgsGeometry::renderCheck( QString theTestName, QString theComment )
311
mReport += "<h2>" + theTestName + "</h2>\n";
312
mReport += "<h3>" + theComment + "</h3>\n";
313
QString myTmpDir = QDir::tempPath() + QDir::separator() ;
314
QString myFileName = myTmpDir + theTestName + ".png";
315
QString myDataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
316
QString myTestDataDir = myDataDir + QDir::separator();
317
mImage.save( myFileName, "PNG" );
318
QgsRenderChecker myChecker;
319
myChecker.setExpectedImage( myTestDataDir + "expected_" + theTestName + ".png" );
320
myChecker.setRenderedImage( myFileName );
321
bool myResultFlag = myChecker.compareImages( theTestName );
322
mReport += myChecker.report();
326
void TestQgsGeometry::dumpMultiPolygon( QgsMultiPolygon &theMultiPolygon )
328
qDebug( "Multipolygon Geometry Dump" );
329
for ( int i = 0; i < theMultiPolygon.size(); i++ )
331
QgsPolygon myPolygon = theMultiPolygon.at( i );
332
qDebug( "\tPolygon in multipolygon: %d", i );
333
dumpPolygon( myPolygon );
337
void TestQgsGeometry::dumpPolygon( QgsPolygon &thePolygon )
339
QVector<QPointF> myPoints;
340
for ( int j = 0; j < thePolygon.size(); j++ )
342
QgsPolyline myPolyline = thePolygon.at( j ); //rings of polygon
343
qDebug( "\t\tRing in polygon: %d", j );
345
for ( int k = 0; k < myPolyline.size(); k++ )
347
QgsPoint myPoint = myPolyline.at( k );
348
qDebug( "\t\t\tPoint in ring %d : %s", k, myPoint.toString().toLocal8Bit().constData() );
349
myPoints << QPointF( myPoint.x(), myPoint.y() );
352
mpPainter->drawPolygon( myPoints );
355
void TestQgsGeometry::dumpPolyline( QgsPolyline &thePolyline )
357
QVector<QPointF> myPoints;
358
// QgsPolyline myPolyline = thePolyline.at( j ); //rings of polygon
359
for ( int j = 0; j < thePolyline.size(); j++ )
361
QgsPoint myPoint = thePolyline.at( j );
362
// QgsPolyline myPolyline = thePolygon.at( j ); //rings of polygon
363
myPoints << QPointF( myPoint.x(), myPoint.y() );
364
qDebug( "\t\tPoint in line: %d", j );
366
// for ( int k = 0; k < myPolyline.size(); k++ )
368
// QgsPoint myPoint = myPolyline.at( k );
369
// qDebug( "\t\t\tPoint in ring %d : %s", k, myPoint.toString().toLocal8Bit().constData() );
370
// myPoints << QPointF( myPoint.x(), myPoint.y() );
373
mpPainter->drawPolyline( myPoints );
376
QString TestQgsGeometry::wkbTypeAsString( QGis::WkbType theType )
382
case QGis::WKBLineString:
383
return "WKBLineString";
384
case QGis::WKBPolygon:
386
case QGis::WKBMultiPoint:
387
return "WKBMultiPoint";
388
case QGis::WKBMultiLineString:
389
return "WKBMultiLineString";
390
case QGis::WKBMultiPolygon:
391
return "WKBMultiPolygon";
392
case QGis::WKBUnknown:
394
case QGis::WKBPoint25D:
395
return "WKBPoint25D";
396
case QGis::WKBLineString25D:
397
return "WKBLineString25D";
398
case QGis::WKBPolygon25D:
399
return "WKBPolygon25D";
400
case QGis::WKBMultiPoint25D:
401
return "WKBMultiPoint25D";
402
case QGis::WKBMultiLineString25D:
403
return "WKBMultiLineString25D";
404
case QGis::WKBMultiPolygon25D:
405
return "WKBMultiPolygon25D";
407
return "Unknown type";
411
QTEST_MAIN( TestQgsGeometry )
412
#include "moc_testqgsgeometry.cxx"