Skip to content

Commit

Permalink
Fixes #50742 : keep ZM coordinates when simplifying geometry
Browse files Browse the repository at this point in the history
  • Loading branch information
troopa81 committed Feb 1, 2023
1 parent ea325a1 commit 22d2a32
Show file tree
Hide file tree
Showing 13 changed files with 215 additions and 9 deletions.
4 changes: 4 additions & 0 deletions python/core/auto_generated/geometry/qgscircularstring.sip.in
Expand Up @@ -200,6 +200,10 @@ Appends the contents of another circular ``string`` to the end of this circular

virtual double yAt( int index ) const /HoldGIL/;

virtual double zAt( int index ) const /HoldGIL/;

virtual double mAt( int index ) const /HoldGIL/;


virtual bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = 0 );

Expand Down
4 changes: 4 additions & 0 deletions python/core/auto_generated/geometry/qgscompoundcurve.sip.in
Expand Up @@ -197,6 +197,10 @@ Appends first point if not already closed.

virtual double yAt( int index ) const /HoldGIL/;

virtual double zAt( int index ) const /HoldGIL/;

virtual double mAt( int index ) const /HoldGIL/;


virtual bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = 0 );

Expand Down
22 changes: 22 additions & 0 deletions python/core/auto_generated/geometry/qgscurve.sip.in
Expand Up @@ -227,6 +227,28 @@ Returns the y-coordinate of the specified node in the line string.
:param index: index of node, where the first node in the line is 0

:return: y-coordinate of node, or 0.0 if index is out of bounds
%End

virtual double zAt( int index ) const = 0;
%Docstring
Returns the z-coordinate of the specified node in the line string.

:param index: index of node, where the first node in the line is 0

:return: z-coordinate of node, or 0.0 if index is out of bounds

.. versionadded:: 3.28
%End

virtual double mAt( int index ) const = 0;
%Docstring
Returns the m-coordinate of the specified node in the line string.

:param index: index of node, where the first node in the line is 0

:return: m-coordinate of node, or 0.0 if index is out of bounds

.. versionadded:: 3.28
%End

virtual QPolygonF asQPolygonF() const;
Expand Down
6 changes: 4 additions & 2 deletions python/core/auto_generated/geometry/qgslinestring.sip.in
Expand Up @@ -363,7 +363,8 @@ corresponds to the last point in the line.



double zAt( int index ) const;
virtual double zAt( int index ) const;

%Docstring
Returns the z-coordinate of the specified node in the line string.

Expand Down Expand Up @@ -391,7 +392,8 @@ corresponds to the last point in the line.
%End


double mAt( int index ) const;
virtual double mAt( int index ) const;

%Docstring
Returns the m-coordinate of the specified node in the line string.

Expand Down
16 changes: 16 additions & 0 deletions src/core/geometry/qgscircularstring.cpp
Expand Up @@ -792,6 +792,22 @@ double QgsCircularString::yAt( int index ) const
return 0.0;
}

double QgsCircularString::zAt( int index ) const
{
if ( index >= 0 && index < mZ.size() )
return mZ.at( index );
else
return 0.0;
}

double QgsCircularString::mAt( int index ) const
{
if ( index >= 0 && index < mM.size() )
return mM.at( index );
else
return 0.0;
}

bool QgsCircularString::transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback )
{
if ( !transformer )
Expand Down
2 changes: 2 additions & 0 deletions src/core/geometry/qgscircularstring.h
Expand Up @@ -161,6 +161,8 @@ class CORE_EXPORT QgsCircularString: public QgsCurve
void swapXy() override;
double xAt( int index ) const override SIP_HOLDGIL;
double yAt( int index ) const override SIP_HOLDGIL;
double zAt( int index ) const override SIP_HOLDGIL;
double mAt( int index ) const override SIP_HOLDGIL;

bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = nullptr ) override;
void scroll( int firstVertexIndex ) final;
Expand Down
30 changes: 30 additions & 0 deletions src/core/geometry/qgscompoundcurve.cpp
Expand Up @@ -1067,6 +1067,36 @@ double QgsCompoundCurve::yAt( int index ) const
return 0.0;
}

double QgsCompoundCurve::zAt( int index ) const
{
int currentVertexId = 0;
for ( int j = 0; j < mCurves.size(); ++j )
{
int nCurvePoints = mCurves.at( j )->numPoints();
if ( ( index - currentVertexId ) < nCurvePoints )
{
return mCurves.at( j )->zAt( index - currentVertexId );
}
currentVertexId += ( nCurvePoints - 1 );
}
return 0.0;
}

double QgsCompoundCurve::mAt( int index ) const
{
int currentVertexId = 0;
for ( int j = 0; j < mCurves.size(); ++j )
{
int nCurvePoints = mCurves.at( j )->numPoints();
if ( ( index - currentVertexId ) < nCurvePoints )
{
return mCurves.at( j )->mAt( index - currentVertexId );
}
currentVertexId += ( nCurvePoints - 1 );
}
return 0.0;
}

bool QgsCompoundCurve::transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback )
{
bool res = true;
Expand Down
2 changes: 2 additions & 0 deletions src/core/geometry/qgscompoundcurve.h
Expand Up @@ -157,6 +157,8 @@ class CORE_EXPORT QgsCompoundCurve: public QgsCurve

double xAt( int index ) const override SIP_HOLDGIL;
double yAt( int index ) const override SIP_HOLDGIL;
double zAt( int index ) const override SIP_HOLDGIL;
double mAt( int index ) const override SIP_HOLDGIL;

bool transform( QgsAbstractGeometryTransformer *transformer, QgsFeedback *feedback = nullptr ) override;
void scroll( int firstVertexIndex ) final;
Expand Down
16 changes: 16 additions & 0 deletions src/core/geometry/qgscurve.h
Expand Up @@ -208,6 +208,22 @@ class CORE_EXPORT QgsCurve: public QgsAbstractGeometry SIP_ABSTRACT
*/
virtual double yAt( int index ) const = 0;

/**
* Returns the z-coordinate of the specified node in the line string.
* \param index index of node, where the first node in the line is 0
* \returns z-coordinate of node, or 0.0 if index is out of bounds
* \since QGIS 3.28
*/
virtual double zAt( int index ) const = 0;

/**
* Returns the m-coordinate of the specified node in the line string.
* \param index index of node, where the first node in the line is 0
* \returns m-coordinate of node, or 0.0 if index is out of bounds
* \since QGIS 3.28
*/
virtual double mAt( int index ) const = 0;

/**
* Returns a QPolygonF representing the points.
*/
Expand Down
8 changes: 4 additions & 4 deletions src/core/geometry/qgslinestring.h
Expand Up @@ -508,7 +508,7 @@ class CORE_EXPORT QgsLineString: public QgsCurve
* does not have a z dimension
* \see setZAt()
*/
double zAt( int index ) const
double zAt( int index ) const override
{
if ( index >= 0 && index < mZ.size() )
return mZ.at( index );
Expand All @@ -527,7 +527,7 @@ class CORE_EXPORT QgsLineString: public QgsCurve
*
* \throws IndexError if no point with the specified index exists.
*/
double zAt( int index ) const;
double zAt( int index ) const override;
% MethodCode
const int count = sipCpp->numPoints();
if ( a0 < -count || a0 >= count )
Expand All @@ -554,7 +554,7 @@ class CORE_EXPORT QgsLineString: public QgsCurve
* does not have m values
* \see setMAt()
*/
double mAt( int index ) const
double mAt( int index ) const override
{
if ( index >= 0 && index < mM.size() )
return mM.at( index );
Expand All @@ -573,7 +573,7 @@ class CORE_EXPORT QgsLineString: public QgsCurve
*
* \throws IndexError if no point with the specified index exists.
*/
double mAt( int index ) const;
double mAt( int index ) const override;
% MethodCode
const int count = sipCpp->numPoints();
if ( a0 < -count || a0 >= count )
Expand Down
46 changes: 43 additions & 3 deletions src/core/qgsmaptopixelgeometrysimplifier.cpp
Expand Up @@ -150,19 +150,27 @@ std::unique_ptr< QgsAbstractGeometry > QgsMapToPixelSimplifier::simplifyGeometry

QVector< double > lineStringX;
QVector< double > lineStringY;
QVector< double > lineStringZ;
QVector< double > lineStringM;
if ( flatType == QgsWkbTypes::LineString )
{
// if we are making a linestring, we do it in an optimised way by directly constructing
// the final x/y vectors, which avoids calling the slower insertVertex method
lineStringX.reserve( numPoints );
lineStringY.reserve( numPoints );

if ( geometry.is3D() )
lineStringZ.reserve( numPoints );

if ( geometry.isMeasure() )
lineStringM.reserve( numPoints );
}
else
{
output.reset( qgsgeometry_cast< QgsCurve * >( srcCurve.createEmptyWithSameType() ) );
}

double x = 0.0, y = 0.0, lastX = 0.0, lastY = 0.0;
double x = 0.0, y = 0.0, z = 0.0, m = 0.0, lastX = 0.0, lastY = 0.0;

if ( numPoints <= ( isaLinearRing ? 4 : 2 ) )
isGeneralizable = false;
Expand Down Expand Up @@ -190,10 +198,18 @@ std::unique_ptr< QgsAbstractGeometry > QgsMapToPixelSimplifier::simplifyGeometry

const double *xData = nullptr;
const double *yData = nullptr;
const double *zData = nullptr;
const double *mData = nullptr;
if ( flatType == QgsWkbTypes::LineString )
{
xData = qgsgeometry_cast< const QgsLineString * >( &srcCurve )->xData();
yData = qgsgeometry_cast< const QgsLineString * >( &srcCurve )->yData();

if ( geometry.is3D() )
zData = qgsgeometry_cast< const QgsLineString * >( &srcCurve )->zData();

if ( geometry.isMeasure() )
mData = qgsgeometry_cast< const QgsLineString * >( &srcCurve )->mData();
}

for ( int i = 0; i < numPoints; ++i )
Expand All @@ -202,11 +218,23 @@ std::unique_ptr< QgsAbstractGeometry > QgsMapToPixelSimplifier::simplifyGeometry
{
x = *xData++;
y = *yData++;

if ( geometry.is3D() )
z = *zData++;

if ( geometry.isMeasure() )
m = *mData++;
}
else
{
x = srcCurve.xAt( i );
y = srcCurve.yAt( i );

if ( geometry.is3D() )
z = srcCurve.zAt( i );

if ( geometry.isMeasure() )
m = srcCurve.mAt( i );
}

if ( i == 0 ||
Expand All @@ -215,11 +243,17 @@ std::unique_ptr< QgsAbstractGeometry > QgsMapToPixelSimplifier::simplifyGeometry
( !isaLinearRing && ( i == 1 || i >= numPoints - 2 ) ) )
{
if ( output )
output->insertVertex( QgsVertexId( 0, 0, output->numPoints() ), QgsPoint( x, y ) );
output->insertVertex( QgsVertexId( 0, 0, output->numPoints() ), QgsPoint( x, y, z, m ) );
else
{
lineStringX.append( x );
lineStringY.append( y );

if ( geometry.is3D() )
lineStringZ.append( z );

if ( geometry.isMeasure() )
lineStringM.append( m );
}
lastX = x;
lastY = y;
Expand Down Expand Up @@ -253,6 +287,12 @@ std::unique_ptr< QgsAbstractGeometry > QgsMapToPixelSimplifier::simplifyGeometry
{
lineStringX.append( ea.inpts.at( i ).x() );
lineStringY.append( ea.inpts.at( i ).y() );

if ( geometry.is3D() )
lineStringZ.append( ea.inpts.at( i ).z() );

if ( geometry.isMeasure() )
lineStringM.append( ea.inpts.at( i ).m() );
}
}
}
Expand Down Expand Up @@ -309,7 +349,7 @@ std::unique_ptr< QgsAbstractGeometry > QgsMapToPixelSimplifier::simplifyGeometry

if ( !output )
{
output = std::make_unique< QgsLineString >( lineStringX, lineStringY );
output = std::make_unique< QgsLineString >( lineStringX, lineStringY, lineStringZ, lineStringM );
}
if ( output->numPoints() < ( isaLinearRing ? 4 : 2 ) )
{
Expand Down
32 changes: 32 additions & 0 deletions tests/src/core/geometry/testqgscompoundcurve.cpp
Expand Up @@ -672,6 +672,10 @@ void TestQgsCompoundCurve::gettersSetters()
( void )cc.xAt( 1 );
( void )cc.yAt( -1 );
( void )cc.yAt( 1 );
( void )cc.zAt( -1 );
( void )cc.zAt( 1 );
( void )cc.mAt( -1 );
( void )cc.mAt( 1 );

QgsCircularString cs;
cs.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 1, 2, 3, 4 )
Expand All @@ -689,6 +693,16 @@ void TestQgsCompoundCurve::gettersSetters()
QCOMPARE( cc.yAt( 2 ), 22.0 );
( void ) cc.yAt( -1 ); //out of range
( void ) cc.yAt( 11 ); //out of range
QCOMPARE( cc.zAt( 0 ), 3.0 );
QCOMPARE( cc.zAt( 1 ), 13.0 );
QCOMPARE( cc.zAt( 2 ), 23.0 );
( void ) cc.zAt( -1 ); //out of range
( void ) cc.zAt( 11 ); //out of range
QCOMPARE( cc.mAt( 0 ), 4.0 );
QCOMPARE( cc.mAt( 1 ), 14.0 );
QCOMPARE( cc.mAt( 2 ), 24.0 );
( void ) cc.mAt( -1 ); //out of range
( void ) cc.mAt( 11 ); //out of range

cs.setPoints( QgsPointSequence() << QgsPoint( QgsWkbTypes::PointZM, 21, 22, 23, 24 )
<< QgsPoint( QgsWkbTypes::PointZM, 31, 22, 13, 14 ) );
Expand All @@ -708,14 +722,32 @@ void TestQgsCompoundCurve::gettersSetters()
QCOMPARE( cc.yAt( 4 ), 0.0 );
( void ) cc.yAt( -1 ); //out of range
( void ) cc.yAt( 11 ); //out of range
QCOMPARE( cc.zAt( 0 ), 3.0 );
QCOMPARE( cc.zAt( 1 ), 13.0 );
QCOMPARE( cc.zAt( 2 ), 23.0 );
QCOMPARE( cc.zAt( 3 ), 13.0 );
QCOMPARE( cc.zAt( 4 ), 0.0 );
( void ) cc.zAt( -1 ); //out of range
( void ) cc.zAt( 11 ); //out of range
QCOMPARE( cc.mAt( 0 ), 4.0 );
QCOMPARE( cc.mAt( 1 ), 14.0 );
QCOMPARE( cc.mAt( 2 ), 24.0 );
QCOMPARE( cc.mAt( 3 ), 14.0 );
QCOMPARE( cc.mAt( 4 ), 0.0 );
( void ) cc.mAt( -1 ); //out of range
( void ) cc.mAt( 11 ); //out of range

cc.moveVertex( QgsVertexId( 0, 0, 0 ), QgsPoint( 51.0, 52.0 ) );
QCOMPARE( cc.xAt( 0 ), 51.0 );
QCOMPARE( cc.yAt( 0 ), 52.0 );
QCOMPARE( cc.zAt( 0 ), 3.0 );
QCOMPARE( cc.mAt( 0 ), 4.0 );

cc.moveVertex( QgsVertexId( 0, 0, 1 ), QgsPoint( 61.0, 62 ) );
QCOMPARE( cc.xAt( 1 ), 61.0 );
QCOMPARE( cc.yAt( 1 ), 62.0 );
QCOMPARE( cc.zAt( 1 ), 13.0 );
QCOMPARE( cc.mAt( 1 ), 14.0 );

cc.moveVertex( QgsVertexId( 0, 0, -1 ), QgsPoint( 71.0, 2 ) ); //out of range
cc.moveVertex( QgsVertexId( 0, 0, 11 ), QgsPoint( 71.0, 2 ) ); //out of range
Expand Down

0 comments on commit 22d2a32

Please sign in to comment.