Add optimised constructors for QgsLineString

Instead of requiring all linestrings to be constructed by
first creating QgsPointSequence (requiring creation or
conversion of points to QgsPointV2), allow construction
of LineStrings directly from vectors of values (fastest!)
or lists of QgsPoint.

Likely results in speedups for lots of geometry operations,
but using the same layer as earlier tested for densify
improvements the densify operation time dropped further
from 25 seconds to 15 seconds.
This commit is contained in:
Nyall Dawson 2017-03-25 20:18:11 +10:00
parent 345ce734c4
commit 51035cfb67
12 changed files with 299 additions and 66 deletions

View File

@ -11,14 +11,14 @@ class QgsLineString: public QgsCurve
public:
QgsLineString();
~QgsLineString();
QgsLineString( const QVector<double> &x, const QVector<double> &y,
const QVector<double> &z = QVector<double>(),
const QVector<double> &m = QVector<double>() );
QgsLineString( const QList<QgsPoint> &points );
bool operator==( const QgsCurve& other ) const;
bool operator!=( const QgsCurve& other ) const;
bool operator==( const QgsCurve &other ) const;
bool operator!=( const QgsCurve &other ) const;
/** Returns the specified point from inside the line string.
* @param i index of point, starting at 0 for the first point
*/
QgsPointV2 pointN( int i ) const;
/** Returns the z-coordinate of the specified node in the line string.

View File

@ -630,10 +630,7 @@ int QgsGeometry::addRing( const QList<QgsPoint> &ring )
{
detach( true );
QgsLineString *ringLine = new QgsLineString();
QgsPointSequence ringPoints;
convertPointList( ring, ringPoints );
ringLine->setPoints( ringPoints );
QgsLineString *ringLine = new QgsLineString( ring );
return addRing( ringLine );
}
@ -797,10 +794,7 @@ int QgsGeometry::splitGeometry( const QList<QgsPoint> &splitLine, QList<QgsGeome
}
QList<QgsAbstractGeometry *> newGeoms;
QgsLineString splitLineString;
QgsPointSequence splitLinePointsV2;
convertPointList( splitLine, splitLinePointsV2 );
splitLineString.setPoints( splitLinePointsV2 );
QgsLineString splitLineString( splitLine );
QgsPointSequence tp;
QgsGeos geos( d->geometry );
@ -830,10 +824,7 @@ int QgsGeometry::reshapeGeometry( const QList<QgsPoint> &reshapeWithLine )
return 0;
}
QgsPointSequence reshapeLine;
convertPointList( reshapeWithLine, reshapeLine );
QgsLineString reshapeLineString;
reshapeLineString.setPoints( reshapeLine );
QgsLineString reshapeLineString( reshapeWithLine );
QgsGeos geos( d->geometry );
int errorCode = 0;

View File

@ -217,15 +217,17 @@ QgsAbstractGeometry *QgsGeometryFactory::fromRect( const QgsRectangle &rect )
QgsLineString *QgsGeometryFactory::linestringFromPolyline( const QgsPolyline &polyline )
{
QgsLineString *line = new QgsLineString();
QgsPointSequence points;
QVector< double > x;
x.reserve( polyline.size() );
QVector< double > y;
y.reserve( polyline.size() );
QgsPolyline::const_iterator it = polyline.constBegin();
for ( ; it != polyline.constEnd(); ++it )
{
points.append( QgsPointV2( it->x(), it->y() ) );
x << it->x();
y << it->y();
}
line->setPoints( points );
QgsLineString *line = new QgsLineString( x, y );
return line;
}

View File

@ -987,17 +987,41 @@ QgsPolygonV2 *QgsGeos::fromGeosPolygon( const GEOSGeometry *geos )
QgsLineString *QgsGeos::sequenceToLinestring( const GEOSGeometry *geos, bool hasZ, bool hasM )
{
QgsPointSequence pts;
const GEOSCoordSequence *cs = GEOSGeom_getCoordSeq_r( geosinit.ctxt, geos );
unsigned int nPoints;
GEOSCoordSeq_getSize_r( geosinit.ctxt, cs, &nPoints );
pts.reserve( nPoints );
QVector< double > xOut;
xOut.reserve( nPoints );
QVector< double > yOut;
yOut.reserve( nPoints );
QVector< double > zOut;
if ( hasZ )
zOut.reserve( nPoints );
QVector< double > mOut;
if ( hasM )
mOut.reserve( nPoints );
double x = 0;
double y = 0;
double z = 0;
double m = 0;
for ( unsigned int i = 0; i < nPoints; ++i )
{
pts.push_back( coordSeqPoint( cs, i, hasZ, hasM ) );
GEOSCoordSeq_getX_r( geosinit.ctxt, cs, i, &x );
xOut << x;
GEOSCoordSeq_getY_r( geosinit.ctxt, cs, i, &y );
yOut << y;
if ( hasZ )
{
GEOSCoordSeq_getZ_r( geosinit.ctxt, cs, i, &z );
zOut << z;
}
if ( hasM )
{
GEOSCoordSeq_getOrdinate_r( geosinit.ctxt, cs, i, 3, &m );
mOut << m;
}
}
QgsLineString *line = new QgsLineString();
line->setPoints( pts );
QgsLineString *line = new QgsLineString( xOut, yOut, zOut, mOut );
return line;
}

View File

@ -480,18 +480,21 @@ QgsGeometry QgsInternalGeometryEngine::orthogonalize( double tolerance, int maxI
// if extraNodesPerSegment < 0, then use distance based mode
QgsLineString *doDensify( QgsLineString *ring, int extraNodesPerSegment = -1, double distance = 1 )
{
QgsPointSequence out;
QVector< double > outX;
QVector< double > outY;
QVector< double > outZ;
QVector< double > outM;
double multiplier = 1.0 / double( extraNodesPerSegment + 1 );
int nPoints = ring->numPoints();
out.reserve( ( extraNodesPerSegment + 1 ) * nPoints );
outX.reserve( ( extraNodesPerSegment + 1 ) * nPoints );
outY.reserve( ( extraNodesPerSegment + 1 ) * nPoints );
bool withZ = ring->is3D();
if ( withZ )
outZ.reserve( ( extraNodesPerSegment + 1 ) * nPoints );
bool withM = ring->isMeasure();
QgsWkbTypes::Type outType = QgsWkbTypes::Point;
if ( ring->is3D() )
outType = QgsWkbTypes::addZ( outType );
if ( ring->isMeasure() )
outType = QgsWkbTypes::addM( outType );
if ( withM )
outM.reserve( ( extraNodesPerSegment + 1 ) * nPoints );
double x1 = 0;
double x2 = 0;
double y1 = 0;
@ -522,7 +525,12 @@ QgsLineString *doDensify( QgsLineString *ring, int extraNodesPerSegment = -1, do
m2 = ring->mAt( i + 1 );
}
out << QgsPointV2( outType, x1, y1, z1, m1 );
outX << x1;
outY << y1;
if ( withZ )
outZ << z1;
if ( withM )
outM << m1;
if ( extraNodesPerSegment < 0 )
{
@ -542,14 +550,22 @@ QgsLineString *doDensify( QgsLineString *ring, int extraNodesPerSegment = -1, do
if ( withM )
mOut = m1 + delta * ( m2 - m1 );
out << QgsPointV2( outType, xOut, yOut, zOut, mOut );
outX << xOut;
outY << yOut;
if ( withZ )
outZ << zOut;
if ( withM )
outM << mOut;
}
}
out << QgsPointV2( outType, ring->xAt( nPoints - 1 ), ring->yAt( nPoints - 1 ),
withZ ? ring->zAt( nPoints - 1 ) : 0, withM ? ring->mAt( nPoints - 1 ) : 0 );
outX << ring->xAt( nPoints - 1 );
outY << ring->yAt( nPoints - 1 );
if ( withZ )
outZ << ring->zAt( nPoints - 1 );
if ( withM )
outM << ring->mAt( nPoints - 1 );
QgsLineString *result = new QgsLineString();
result->setPoints( out );
QgsLineString *result = new QgsLineString( outX, outY, outZ, outM );
return result;
}

View File

@ -40,6 +40,64 @@ QgsLineString::QgsLineString(): QgsCurve()
mWkbType = QgsWkbTypes::LineString;
}
QgsLineString::QgsLineString( const QVector<double> &x, const QVector<double> &y, const QVector<double> &z, const QVector<double> &m )
{
mWkbType = QgsWkbTypes::LineString;
int pointCount = qMin( x.size(), y.size() );
if ( x.size() == pointCount )
{
mX = x;
}
else
{
mX = x.mid( 0, pointCount );
}
if ( y.size() == pointCount )
{
mY = y;
}
else
{
mY = y.mid( 0, pointCount );
}
if ( !z.isEmpty() && z.count() >= pointCount )
{
mWkbType = QgsWkbTypes::addZ( mWkbType );
if ( z.size() == pointCount )
{
mZ = z;
}
else
{
mZ = z.mid( 0, pointCount );
}
}
if ( !m.isEmpty() && m.count() >= pointCount )
{
mWkbType = QgsWkbTypes::addM( mWkbType );
if ( m.size() == pointCount )
{
mM = m;
}
else
{
mM = m.mid( 0, pointCount );
}
}
}
QgsLineString::QgsLineString( const QList<QgsPoint> &points )
{
mWkbType = QgsWkbTypes::LineString;
mX.reserve( points.size() );
mY.reserve( points.size() );
Q_FOREACH ( const QgsPoint &p, points )
{
mX << p.x();
mY << p.y();
}
}
bool QgsLineString::operator==( const QgsCurve &other ) const
{
const QgsLineString *otherLine = dynamic_cast< const QgsLineString * >( &other );

View File

@ -38,6 +38,26 @@ class CORE_EXPORT QgsLineString: public QgsCurve
public:
QgsLineString();
/**
* Construct a linestring from arrays of coordinates. If the z or m
* arrays are non-empty then the resultant linestring will have
* z and m types accordingly.
* This constructor is more efficient then calling setPoints()
* or repeatedly calling addVertex()
* @note added in QGIS 3.0
*/
QgsLineString( const QVector<double> &x, const QVector<double> &y,
const QVector<double> &z = QVector<double>(),
const QVector<double> &m = QVector<double>() );
/**
* Construct a linestring from list of points.
* This constructor is more efficient then calling setPoints()
* or repeatedly calling addVertex()
* @note added in QGIS 3.0
*/
QgsLineString( const QList<QgsPoint> &points );
bool operator==( const QgsCurve &other ) const override;
bool operator!=( const QgsCurve &other ) const override;

View File

@ -36,8 +36,11 @@ QgsTriangle::QgsTriangle( const QgsPointV2 &p1, const QgsPointV2 &p2, const QgsP
{
return;
}
QgsLineString *ext = new QgsLineString();
ext->setPoints( QgsPointSequence() << p1 << p2 << p3 << p1 );
QVector< double > x;
x << p1.x() << p2.x() << p3.x();
QVector< double > y;
y << p1.y() << p2.y() << p3.y();
QgsLineString *ext = new QgsLineString( x, y );
setExteriorRing( ext );
}
@ -54,8 +57,11 @@ QgsTriangle::QgsTriangle( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint
{
return;
}
QgsLineString *ext = new QgsLineString();
ext->setPoints( QgsPointSequence() << pt1 << pt2 << pt3 << pt1 );
QVector< double > x;
x << p1.x() << p2.x() << p3.x();
QVector< double > y;
y << p1.y() << p2.y() << p3.y();
QgsLineString *ext = new QgsLineString( x, y );
setExteriorRing( ext );
}
@ -71,8 +77,11 @@ QgsTriangle::QgsTriangle( const QPointF p1, const QPointF p2, const QPointF p3 )
{
return;
}
QgsLineString *ext = new QgsLineString();
ext->setPoints( QgsPointSequence() << pt1 << pt2 << pt3 << pt1 );
QVector< double > x;
x << p1.x() << p2.x() << p3.x();
QVector< double > y;
y << p1.y() << p2.y() << p3.y();
QgsLineString *ext = new QgsLineString( x, y );
setExteriorRing( ext );
}

View File

@ -138,14 +138,7 @@ QgsVectorLayer::EditResult QgsVectorLayerEditUtils::deleteVertex( QgsFeatureId f
int QgsVectorLayerEditUtils::addRing( const QList<QgsPoint> &ring, const QgsFeatureIds &targetFeatureIds, QgsFeatureId *modifiedFeatureId )
{
QgsLineString *ringLine = new QgsLineString();
QgsPointSequence ringPoints;
QList<QgsPoint>::const_iterator ringIt = ring.constBegin();
for ( ; ringIt != ring.constEnd(); ++ringIt )
{
ringPoints.append( QgsPointV2( ringIt->x(), ringIt->y() ) );
}
ringLine->setPoints( ringPoints );
QgsLineString *ringLine = new QgsLineString( ring );
return addRing( ringLine, targetFeatureIds, modifiedFeatureId );
}

View File

@ -300,11 +300,11 @@ QgsGeometry *QgsVectorLayerLabelProvider::getPointObstacleGeometry( QgsFeature &
}
//convert bounds to a geometry
QgsLineString *boundLineString = new QgsLineString();
boundLineString->addVertex( QgsPointV2( bounds.topLeft() ) );
boundLineString->addVertex( QgsPointV2( bounds.topRight() ) );
boundLineString->addVertex( QgsPointV2( bounds.bottomRight() ) );
boundLineString->addVertex( QgsPointV2( bounds.bottomLeft() ) );
QVector< double > bX;
bX << bounds.left() << bounds.right() << bounds.right() << bounds.left();
QVector< double > bY;
bY << bounds.top() << bounds.top() << bounds.bottom() << bounds.bottom();
QgsLineString *boundLineString = new QgsLineString( bX, bY );
//then transform back to map units
//TODO - remove when labeling is refactored to use screen units

View File

@ -711,12 +711,7 @@ QList<QgsPoint> QgsMapToolCapture::points()
void QgsMapToolCapture::setPoints( const QList<QgsPoint> &pointList )
{
QgsPointSequence pts;
QgsGeometry::convertPointList( pointList, pts );
QgsLineString *line = new QgsLineString();
line->setPoints( pts );
QgsLineString *line = new QgsLineString( pointList );
mCaptureCurve.clear();
mCaptureCurve.addCurve( line );
}

View File

@ -935,6 +935,131 @@ void TestQgsGeometry::lineString()
QCOMPARE( l1.area(), 0.0 );
QCOMPARE( l1.perimeter(), 0.0 );
// from array
QVector< double > xx;
xx << 1 << 2 << 3;
QVector< double > yy;
yy << 11 << 12 << 13;
QgsLineString fromArray( xx, yy );
QCOMPARE( fromArray.wkbType(), QgsWkbTypes::LineString );
QCOMPARE( fromArray.numPoints(), 3 );
QCOMPARE( fromArray.xAt( 0 ), 1.0 );
QCOMPARE( fromArray.yAt( 0 ), 11.0 );
QCOMPARE( fromArray.xAt( 1 ), 2.0 );
QCOMPARE( fromArray.yAt( 1 ), 12.0 );
QCOMPARE( fromArray.xAt( 2 ), 3.0 );
QCOMPARE( fromArray.yAt( 2 ), 13.0 );
// unbalanced
xx = QVector< double >() << 1 << 2;
yy = QVector< double >() << 11 << 12 << 13;
QgsLineString fromArray2( xx, yy );
QCOMPARE( fromArray2.wkbType(), QgsWkbTypes::LineString );
QCOMPARE( fromArray2.numPoints(), 2 );
QCOMPARE( fromArray2.xAt( 0 ), 1.0 );
QCOMPARE( fromArray2.yAt( 0 ), 11.0 );
QCOMPARE( fromArray2.xAt( 1 ), 2.0 );
QCOMPARE( fromArray2.yAt( 1 ), 12.0 );
xx = QVector< double >() << 1 << 2 << 3;
yy = QVector< double >() << 11 << 12;
QgsLineString fromArray3( xx, yy );
QCOMPARE( fromArray3.wkbType(), QgsWkbTypes::LineString );
QCOMPARE( fromArray3.numPoints(), 2 );
QCOMPARE( fromArray3.xAt( 0 ), 1.0 );
QCOMPARE( fromArray3.yAt( 0 ), 11.0 );
QCOMPARE( fromArray3.xAt( 1 ), 2.0 );
QCOMPARE( fromArray3.yAt( 1 ), 12.0 );
// with z
QVector< double > zz;
xx = QVector< double >() << 1 << 2 << 3;
yy = QVector< double >() << 11 << 12 << 13;
zz = QVector< double >() << 21 << 22 << 23;
QgsLineString fromArray4( xx, yy, zz );
QCOMPARE( fromArray4.wkbType(), QgsWkbTypes::LineStringZ );
QCOMPARE( fromArray4.numPoints(), 3 );
QCOMPARE( fromArray4.xAt( 0 ), 1.0 );
QCOMPARE( fromArray4.yAt( 0 ), 11.0 );
QCOMPARE( fromArray4.zAt( 0 ), 21.0 );
QCOMPARE( fromArray4.xAt( 1 ), 2.0 );
QCOMPARE( fromArray4.yAt( 1 ), 12.0 );
QCOMPARE( fromArray4.zAt( 1 ), 22.0 );
QCOMPARE( fromArray4.xAt( 2 ), 3.0 );
QCOMPARE( fromArray4.yAt( 2 ), 13.0 );
QCOMPARE( fromArray4.zAt( 2 ), 23.0 );
// unbalanced -> z ignored
zz = QVector< double >() << 21 << 22;
QgsLineString fromArray5( xx, yy, zz );
QCOMPARE( fromArray5.wkbType(), QgsWkbTypes::LineString );
QCOMPARE( fromArray5.numPoints(), 3 );
QCOMPARE( fromArray5.xAt( 0 ), 1.0 );
QCOMPARE( fromArray5.yAt( 0 ), 11.0 );
QCOMPARE( fromArray5.xAt( 1 ), 2.0 );
QCOMPARE( fromArray5.yAt( 1 ), 12.0 );
QCOMPARE( fromArray5.xAt( 2 ), 3.0 );
QCOMPARE( fromArray5.yAt( 2 ), 13.0 );
// with m
QVector< double > mm;
xx = QVector< double >() << 1 << 2 << 3;
yy = QVector< double >() << 11 << 12 << 13;
mm = QVector< double >() << 21 << 22 << 23;
QgsLineString fromArray6( xx, yy, QVector< double >(), mm );
QCOMPARE( fromArray6.wkbType(), QgsWkbTypes::LineStringM );
QCOMPARE( fromArray6.numPoints(), 3 );
QCOMPARE( fromArray6.xAt( 0 ), 1.0 );
QCOMPARE( fromArray6.yAt( 0 ), 11.0 );
QCOMPARE( fromArray6.mAt( 0 ), 21.0 );
QCOMPARE( fromArray6.xAt( 1 ), 2.0 );
QCOMPARE( fromArray6.yAt( 1 ), 12.0 );
QCOMPARE( fromArray6.mAt( 1 ), 22.0 );
QCOMPARE( fromArray6.xAt( 2 ), 3.0 );
QCOMPARE( fromArray6.yAt( 2 ), 13.0 );
QCOMPARE( fromArray6.mAt( 2 ), 23.0 );
// unbalanced -> m ignored
mm = QVector< double >() << 21 << 22;
QgsLineString fromArray7( xx, yy, QVector< double >(), mm );
QCOMPARE( fromArray7.wkbType(), QgsWkbTypes::LineString );
QCOMPARE( fromArray7.numPoints(), 3 );
QCOMPARE( fromArray7.xAt( 0 ), 1.0 );
QCOMPARE( fromArray7.yAt( 0 ), 11.0 );
QCOMPARE( fromArray7.xAt( 1 ), 2.0 );
QCOMPARE( fromArray7.yAt( 1 ), 12.0 );
QCOMPARE( fromArray7.xAt( 2 ), 3.0 );
QCOMPARE( fromArray7.yAt( 2 ), 13.0 );
// zm
xx = QVector< double >() << 1 << 2 << 3;
yy = QVector< double >() << 11 << 12 << 13;
zz = QVector< double >() << 21 << 22 << 23;
mm = QVector< double >() << 31 << 32 << 33;
QgsLineString fromArray8( xx, yy, zz, mm );
QCOMPARE( fromArray8.wkbType(), QgsWkbTypes::LineStringZM );
QCOMPARE( fromArray8.numPoints(), 3 );
QCOMPARE( fromArray8.xAt( 0 ), 1.0 );
QCOMPARE( fromArray8.yAt( 0 ), 11.0 );
QCOMPARE( fromArray8.zAt( 0 ), 21.0 );
QCOMPARE( fromArray8.mAt( 0 ), 31.0 );
QCOMPARE( fromArray8.xAt( 1 ), 2.0 );
QCOMPARE( fromArray8.yAt( 1 ), 12.0 );
QCOMPARE( fromArray8.zAt( 1 ), 22.0 );
QCOMPARE( fromArray8.mAt( 1 ), 32.0 );
QCOMPARE( fromArray8.xAt( 2 ), 3.0 );
QCOMPARE( fromArray8.yAt( 2 ), 13.0 );
QCOMPARE( fromArray8.zAt( 2 ), 23.0 );
QCOMPARE( fromArray8.mAt( 2 ), 33.0 );
// from QList<QgsPoint>
QList<QgsPoint> ptsA;
ptsA << QgsPoint( 1, 2 ) << QgsPoint( 11, 12 ) << QgsPoint( 21, 22 );
QgsLineString fromPts( ptsA );
QCOMPARE( fromPts.wkbType(), QgsWkbTypes::LineString );
QCOMPARE( fromPts.numPoints(), 3 );
QCOMPARE( fromPts.xAt( 0 ), 1.0 );
QCOMPARE( fromPts.yAt( 0 ), 2.0 );
QCOMPARE( fromPts.xAt( 1 ), 11.0 );
QCOMPARE( fromPts.yAt( 1 ), 12.0 );
QCOMPARE( fromPts.xAt( 2 ), 21.0 );
QCOMPARE( fromPts.yAt( 2 ), 22.0 );
//addVertex
QgsLineString l2;
l2.addVertex( QgsPointV2( 1.0, 2.0 ) );