Skip to content

Commit 39d1486

Browse files
committed
Optimise conversion of geometry from OGR -> QGIS
Avoid conversion to/from WKB at OGR/QGIS side, and just directly utilise OGR geometry API to construct QGIS geometries. Shaves ~10% off rendering time for a large point layer (GPKG)
1 parent 2f214c6 commit 39d1486

File tree

6 files changed

+139
-4
lines changed

6 files changed

+139
-4
lines changed

python/core/auto_generated/geometry/qgslinestring.sip.in

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,20 @@ in the vector.
3838

3939
QgsLineString( const QVector<double> &x, const QVector<double> &y,
4040
const QVector<double> &z = QVector<double>(),
41-
const QVector<double> &m = QVector<double>() );
41+
const QVector<double> &m = QVector<double>(), bool is25DType = false );
4242
%Docstring
4343
Construct a linestring from arrays of coordinates. If the z or m
4444
arrays are non-empty then the resultant linestring will have
4545
z and m types accordingly.
4646
This constructor is more efficient then calling setPoints()
4747
or repeatedly calling addVertex()
4848

49+
If the \z vector is filled, then the geometry type will either
50+
be a LineStringZ(M) or LineString25D depending on the ``is25DType``
51+
argument. If ``is25DType`` is true (and the ``m`` vector is unfilled) then
52+
the created Linestring will be a LineString25D type. Otherwise, the
53+
LineString will be LineStringZ (or LineStringZM) type.
54+
4955
.. versionadded:: 3.0
5056
%End
5157

src/core/geometry/qgslinestring.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ QgsLineString::QgsLineString( const QVector<QgsPoint> &points )
7979
}
8080
}
8181

82-
QgsLineString::QgsLineString( const QVector<double> &x, const QVector<double> &y, const QVector<double> &z, const QVector<double> &m )
82+
QgsLineString::QgsLineString( const QVector<double> &x, const QVector<double> &y, const QVector<double> &z, const QVector<double> &m, bool is25DType )
8383
{
8484
mWkbType = QgsWkbTypes::LineString;
8585
int pointCount = std::min( x.size(), y.size() );
@@ -101,7 +101,7 @@ QgsLineString::QgsLineString( const QVector<double> &x, const QVector<double> &y
101101
}
102102
if ( !z.isEmpty() && z.count() >= pointCount )
103103
{
104-
mWkbType = QgsWkbTypes::addZ( mWkbType );
104+
mWkbType = is25DType ? QgsWkbTypes::LineString25D : QgsWkbTypes::LineStringZ;
105105
if ( z.size() == pointCount )
106106
{
107107
mZ = z;

src/core/geometry/qgslinestring.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,18 @@ class CORE_EXPORT QgsLineString: public QgsCurve
5959
* z and m types accordingly.
6060
* This constructor is more efficient then calling setPoints()
6161
* or repeatedly calling addVertex()
62+
*
63+
* If the \z vector is filled, then the geometry type will either
64+
* be a LineStringZ(M) or LineString25D depending on the \a is25DType
65+
* argument. If \a is25DType is true (and the \a m vector is unfilled) then
66+
* the created Linestring will be a LineString25D type. Otherwise, the
67+
* LineString will be LineStringZ (or LineStringZM) type.
68+
*
6269
* \since QGIS 3.0
6370
*/
6471
QgsLineString( const QVector<double> &x, const QVector<double> &y,
6572
const QVector<double> &z = QVector<double>(),
66-
const QVector<double> &m = QVector<double>() );
73+
const QVector<double> &m = QVector<double>(), bool is25DType = false );
6774

6875
/**
6976
* Constructs a linestring with a single segment from \a p1 to \a p2.

src/core/qgsogrutils.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "qgslogger.h"
1919
#include "qgsgeometry.h"
2020
#include "qgsfields.h"
21+
#include "qgslinestring.h"
2122
#include <QTextCodec>
2223
#include <QUuid>
2324
#include <cpl_error.h>
@@ -269,11 +270,68 @@ bool QgsOgrUtils::readOgrFeatureGeometry( OGRFeatureH ogrFet, QgsFeature &featur
269270
return true;
270271
}
271272

273+
std::unique_ptr< QgsPoint > ogrGeometryToQgsPoint( OGRGeometryH geom )
274+
{
275+
QgsWkbTypes::Type wkbType = static_cast<QgsWkbTypes::Type>( OGR_G_GetGeometryType( geom ) );
276+
277+
double x, y, z, m;
278+
OGR_G_GetPointZM( geom, 0, &x, &y, &z, &m );
279+
return qgis::make_unique< QgsPoint >( wkbType, x, y, z, m );
280+
}
281+
282+
std::unique_ptr< QgsLineString > ogrGeometryToQgsLineString( OGRGeometryH geom )
283+
{
284+
QgsWkbTypes::Type wkbType = static_cast<QgsWkbTypes::Type>( OGR_G_GetGeometryType( geom ) );
285+
286+
int count = OGR_G_GetPointCount( geom );
287+
QVector< double > x( count );
288+
QVector< double > y( count );
289+
QVector< double > z;
290+
double *pz = nullptr;
291+
if ( QgsWkbTypes::hasZ( wkbType ) )
292+
{
293+
z.resize( count );
294+
pz = z.data();
295+
}
296+
double *pm = nullptr;
297+
QVector< double > m;
298+
if ( QgsWkbTypes::hasM( wkbType ) )
299+
{
300+
m.resize( count );
301+
pm = m.data();
302+
}
303+
OGR_G_GetPointsZM( geom, x.data(), sizeof( double ), y.data(), sizeof( double ), pz, sizeof( double ), pm, sizeof( double ) );
304+
305+
return qgis::make_unique< QgsLineString>( x, y, z, m, wkbType == QgsWkbTypes::LineString25D );
306+
}
307+
272308
QgsGeometry QgsOgrUtils::ogrGeometryToQgsGeometry( OGRGeometryH geom )
273309
{
274310
if ( !geom )
275311
return QgsGeometry();
276312

313+
QgsWkbTypes::Type wkbType = static_cast<QgsWkbTypes::Type>( OGR_G_GetGeometryType( geom ) );
314+
// optimised case for some geometry classes, avoiding wkb conversion on OGR/QGIS sides
315+
// TODO - extend to other classes!
316+
switch ( QgsWkbTypes::flatType( wkbType ) )
317+
{
318+
case QgsWkbTypes::Point:
319+
{
320+
return QgsGeometry( ogrGeometryToQgsPoint( geom ) );
321+
}
322+
323+
case QgsWkbTypes::LineString:
324+
{
325+
// optimised case for line -- avoid wkb conversion
326+
return QgsGeometry( ogrGeometryToQgsLineString( geom ) );
327+
}
328+
329+
default:
330+
break;
331+
};
332+
333+
// Fallback to inefficient WKB conversions
334+
277335
// get the wkb representation
278336
int memorySize = OGR_G_WkbSize( geom );
279337
unsigned char *wkb = new unsigned char[memorySize];

tests/src/core/testqgsgeometry.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2853,6 +2853,19 @@ void TestQgsGeometry::lineString()
28532853
QCOMPARE( fromArray4.xAt( 2 ), 3.0 );
28542854
QCOMPARE( fromArray4.yAt( 2 ), 13.0 );
28552855
QCOMPARE( fromArray4.zAt( 2 ), 23.0 );
2856+
fromArray4 = QgsLineString( xx, yy, zz, QVector< double >(), true ); // LineString25D
2857+
QCOMPARE( fromArray4.wkbType(), QgsWkbTypes::LineString25D );
2858+
QCOMPARE( fromArray4.numPoints(), 3 );
2859+
QCOMPARE( fromArray4.xAt( 0 ), 1.0 );
2860+
QCOMPARE( fromArray4.yAt( 0 ), 11.0 );
2861+
QCOMPARE( fromArray4.zAt( 0 ), 21.0 );
2862+
QCOMPARE( fromArray4.xAt( 1 ), 2.0 );
2863+
QCOMPARE( fromArray4.yAt( 1 ), 12.0 );
2864+
QCOMPARE( fromArray4.zAt( 1 ), 22.0 );
2865+
QCOMPARE( fromArray4.xAt( 2 ), 3.0 );
2866+
QCOMPARE( fromArray4.yAt( 2 ), 13.0 );
2867+
QCOMPARE( fromArray4.zAt( 2 ), 23.0 );
2868+
28562869
// unbalanced -> z ignored
28572870
zz = QVector< double >() << 21 << 22;
28582871
QgsLineString fromArray5( xx, yy, zz );

tests/src/core/testqgsogrutils.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ class TestQgsOgrUtils: public QObject
3838
void init();// will be called before each testfunction is executed.
3939
void cleanup();// will be called after every testfunction.
4040
void ogrGeometryToQgsGeometry();
41+
void ogrGeometryToQgsGeometry2_data();
42+
void ogrGeometryToQgsGeometry2();
4143
void readOgrFeatureGeometry();
4244
void getOgrFeatureAttribute();
4345
void readOgrFeatureAttributes();
@@ -102,6 +104,55 @@ void TestQgsOgrUtils::ogrGeometryToQgsGeometry()
102104

103105
OGR_F_Destroy( oFeat );
104106
OGR_DS_Destroy( hDS );
107+
108+
ogrGeom = nullptr;
109+
QByteArray wkt( "point( 1.1 2.2)" );
110+
char *wktChar = wkt.data();
111+
OGR_G_CreateFromWkt( &wktChar, nullptr, &ogrGeom );
112+
geom = QgsOgrUtils::ogrGeometryToQgsGeometry( ogrGeom );
113+
QCOMPARE( geom.asWkt( 3 ), QStringLiteral( "Point (1.1 2.2)" ) );
114+
115+
}
116+
117+
void TestQgsOgrUtils::ogrGeometryToQgsGeometry2_data()
118+
{
119+
QTest::addColumn<QString>( "wkt" );
120+
QTest::addColumn<int>( "type" );
121+
122+
QTest::newRow( "point" ) << QStringLiteral( "Point (1.1 2.2)" ) << static_cast< int >( QgsWkbTypes::Point );
123+
QTest::newRow( "pointz" ) << QStringLiteral( "PointZ (1.1 2.2 3.3)" ) << static_cast< int >( QgsWkbTypes::Point25D ); // ogr uses 25d for z
124+
QTest::newRow( "pointm" ) << QStringLiteral( "PointM (1.1 2.2 3.3)" ) << static_cast< int >( QgsWkbTypes::PointM );
125+
QTest::newRow( "pointzm" ) << QStringLiteral( "PointZM (1.1 2.2 3.3 4.4)" ) << static_cast< int >( QgsWkbTypes::PointZM );
126+
QTest::newRow( "point25d" ) << QStringLiteral( "Point25D (1.1 2.2 3.3)" ) << static_cast< int >( QgsWkbTypes::Point25D );
127+
128+
QTest::newRow( "linestring" ) << QStringLiteral( "LineString (1.1 2.2, 3.3 4.4)" ) << static_cast< int >( QgsWkbTypes::LineString );
129+
QTest::newRow( "linestringz" ) << QStringLiteral( "LineStringZ (1.1 2.2 3.3, 4.4 5.5 6.6)" ) << static_cast< int >( QgsWkbTypes::LineString25D ); // ogr uses 25d for z
130+
QTest::newRow( "linestringm" ) << QStringLiteral( "LineStringM (1.1 2.2 3.3, 4.4 5.5 6.6)" ) << static_cast< int >( QgsWkbTypes::LineStringM );
131+
QTest::newRow( "linestringzm" ) << QStringLiteral( "LineStringZM (1.1 2.2 3.3 4.4, 5.5 6.6 7.7 8.8)" ) << static_cast< int >( QgsWkbTypes::LineStringZM );
132+
QTest::newRow( "linestring25d" ) << QStringLiteral( "LineString25D (1.1 2.2 3.3, 4.4 5.5 6.6)" ) << static_cast< int >( QgsWkbTypes::LineString25D );
133+
}
134+
135+
void TestQgsOgrUtils::ogrGeometryToQgsGeometry2()
136+
{
137+
QFETCH( QString, wkt );
138+
QFETCH( int, type );
139+
140+
QgsGeometry input = QgsGeometry::fromWkt( wkt );
141+
QVERIFY( !input.isNull() );
142+
143+
// to OGR Geometry
144+
QByteArray wkb( input.asWkb() );
145+
OGRGeometryH ogrGeom = nullptr;
146+
147+
QCOMPARE( OGR_G_CreateFromWkb( reinterpret_cast<unsigned char *>( const_cast<char *>( wkb.constData() ) ), nullptr, &ogrGeom, wkb.length() ), OGRERR_NONE );
148+
149+
// back again!
150+
QgsGeometry geom = QgsOgrUtils::ogrGeometryToQgsGeometry( ogrGeom );
151+
QCOMPARE( static_cast< int >( geom.wkbType() ), type );
152+
153+
// bit of trickiness here - QGIS wkt conversion changes 25D -> Z, so account for that
154+
wkt.replace( QStringLiteral( "25D" ), QStringLiteral( "Z" ) );
155+
QCOMPARE( geom.asWkt( 3 ), wkt );
105156
}
106157

107158
void TestQgsOgrUtils::readOgrFeatureGeometry()

0 commit comments

Comments
 (0)