Skip to content
Permalink
Browse files
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.
  • Loading branch information
nyalldawson committed Mar 25, 2017
1 parent 345ce73 commit 51035cfb6726b79fa3bf7d96e2091aff826a4768
@@ -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.
@@ -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;
@@ -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;
}

@@ -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;
}

@@ -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 );

QgsLineString *result = new QgsLineString();
result->setPoints( out );
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( outX, outY, outZ, outM );
return result;
}

@@ -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 );
@@ -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;

@@ -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 );
}

@@ -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 );
}

@@ -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
@@ -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 );
}

0 comments on commit 51035cf

Please sign in to comment.