Skip to content

Commit

Permalink
Fix Points Along Geometry handling of multipart geometries
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] authored and nirvn committed Jan 31, 2020
1 parent d421313 commit ac3b446
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 14 deletions.
Expand Up @@ -453,74 +453,82 @@
</gml:featureMember>
<gml:featureMember>
<ogr:points_along_polys fid="polys.3">
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>7,-1</gml:coordinates></gml:Point></ogr:geometryProperty>
<ogr:name>ASDF</ogr:name>
<ogr:intval>0</ogr:intval>
<ogr:floatval xsi:nil="true"/>
<ogr:distance>17</ogr:distance>
<ogr:angle>135</ogr:angle>
<ogr:angle>180</ogr:angle>
</ogr:points_along_polys>
</gml:featureMember>
<gml:featureMember>
<ogr:points_along_polys fid="polys.3">
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>7,-2</gml:coordinates></gml:Point></ogr:geometryProperty>
<ogr:name>ASDF</ogr:name>
<ogr:intval>0</ogr:intval>
<ogr:floatval xsi:nil="true"/>
<ogr:distance>18</ogr:distance>
<ogr:angle>180</ogr:angle>
<ogr:angle>135</ogr:angle>
</ogr:points_along_polys>
</gml:featureMember>
<gml:featureMember>
<ogr:points_along_polys fid="polys.3">
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>8,-2</gml:coordinates></gml:Point></ogr:geometryProperty>
<ogr:name>ASDF</ogr:name>
<ogr:intval>0</ogr:intval>
<ogr:floatval xsi:nil="true"/>
<ogr:distance>19</ogr:distance>
<ogr:angle>180</ogr:angle>
<ogr:angle>90</ogr:angle>
</ogr:points_along_polys>
</gml:featureMember>
<gml:featureMember>
<ogr:points_along_polys fid="polys.3">
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>9,-2</gml:coordinates></gml:Point></ogr:geometryProperty>
<ogr:name>ASDF</ogr:name>
<ogr:intval>0</ogr:intval>
<ogr:floatval xsi:nil="true"/>
<ogr:distance>20</ogr:distance>
<ogr:angle>90</ogr:angle>
<ogr:angle>45</ogr:angle>
</ogr:points_along_polys>
</gml:featureMember>
<gml:featureMember>
<ogr:points_along_polys fid="polys.3">
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>9,-1</gml:coordinates></gml:Point></ogr:geometryProperty>
<ogr:name>ASDF</ogr:name>
<ogr:intval>0</ogr:intval>
<ogr:floatval xsi:nil="true"/>
<ogr:distance>21</ogr:distance>
<ogr:angle>90</ogr:angle>
<ogr:angle>0</ogr:angle>
</ogr:points_along_polys>
</gml:featureMember>
<gml:featureMember>
<ogr:points_along_polys fid="polys.3">
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>9,0</gml:coordinates></gml:Point></ogr:geometryProperty>
<ogr:name>ASDF</ogr:name>
<ogr:intval>0</ogr:intval>
<ogr:floatval xsi:nil="true"/>
<ogr:distance>22</ogr:distance>
<ogr:angle>0</ogr:angle>
<ogr:angle>315</ogr:angle>
</ogr:points_along_polys>
</gml:featureMember>
<gml:featureMember>
<ogr:points_along_polys fid="polys.3">
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>8,0</gml:coordinates></gml:Point></ogr:geometryProperty>
<ogr:name>ASDF</ogr:name>
<ogr:intval>0</ogr:intval>
<ogr:floatval xsi:nil="true"/>
<ogr:distance>23</ogr:distance>
<ogr:angle>0</ogr:angle>
<ogr:angle>270</ogr:angle>
</ogr:points_along_polys>
</gml:featureMember>
<gml:featureMember>
<ogr:points_along_polys fid="polys.3">
<ogr:geometryProperty><gml:Point srsName="EPSG:4326"><gml:coordinates>7,0</gml:coordinates></gml:Point></ogr:geometryProperty>
<ogr:name>ASDF</ogr:name>
<ogr:intval>0</ogr:intval>
<ogr:floatval xsi:nil="true"/>
<ogr:distance>24</ogr:distance>
<ogr:angle>270</ogr:angle>
<ogr:angle>225</ogr:angle>
</ogr:points_along_polys>
</gml:featureMember>
<gml:featureMember>
Expand Down
18 changes: 14 additions & 4 deletions src/core/geometry/qgsgeometry.cpp
Expand Up @@ -2125,7 +2125,7 @@ QgsGeometry QgsGeometry::subdivide( int maxNodes ) const
return QgsGeometry( std::move( result ) );
}

QgsGeometry QgsGeometry::interpolate( const double distance ) const
QgsGeometry QgsGeometry::interpolate( double distance ) const
{
if ( !d->geometry )
{
Expand All @@ -2143,11 +2143,21 @@ QgsGeometry QgsGeometry::interpolate( const double distance ) const
const QgsCurve *curve = nullptr;
if ( line.isMultipart() )
{
// if multi part, just use first part
// if multi part, iterate through parts to find target part
const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( line.constGet() );
if ( collection && collection->numGeometries() > 0 )
for ( int part = 0; part < collection->numGeometries(); ++part )
{
curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
const QgsCurve *candidate = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( part ) );
if ( !candidate )
continue;
const double candidateLength = candidate->length();
if ( candidateLength >= distance )
{
curve = candidate;
break;
}

distance -= candidateLength;
}
}
else
Expand Down
2 changes: 1 addition & 1 deletion src/core/geometry/qgsgeometryutils.cpp
Expand Up @@ -166,7 +166,7 @@ bool QgsGeometryUtils::verticesAtDistance( const QgsAbstractGeometry &geometry,
bool first = true;
while ( currentDist < distance && geometry.nextVertex( nextVertex, point ) )
{
if ( !first )
if ( !first && nextVertex.part == previousVertex.part && nextVertex.ring == previousVertex.ring )
{
currentDist += std::sqrt( QgsGeometryUtils::sqrDistance2D( previousPoint, point ) );
}
Expand Down
42 changes: 42 additions & 0 deletions tests/src/core/testqgsgeometryutils.cpp
Expand Up @@ -511,6 +511,48 @@ void TestQgsGeometryUtils::testVerticesAtDistance()
QCOMPARE( previous, QgsVertexId( 0, 0, 4 ) );
QCOMPARE( next, QgsVertexId( 0, 0, 4 ) );

// with ring
QgsLineString *ring1 = new QgsLineString();
ring1->setPoints( QVector<QgsPoint>() << QgsPoint( 1.1, 1.1 ) << QgsPoint( 1.1, 1.2 ) << QgsPoint( 1.2, 1.2 ) << QgsPoint( 1.2, 1.1 ) << QgsPoint( 1.1, 1.1 ) );
polygon1.addInteriorRing( ring1 );
QVERIFY( QgsGeometryUtils::verticesAtDistance( polygon1, 4, previous, next ) );
QCOMPARE( previous, QgsVertexId( 0, 0, 4 ) );
QCOMPARE( next, QgsVertexId( 0, 0, 4 ) );
QVERIFY( QgsGeometryUtils::verticesAtDistance( polygon1, 4.01, previous, next ) );
QCOMPARE( previous, QgsVertexId( 0, 1, 0 ) );
QCOMPARE( next, QgsVertexId( 0, 1, 1 ) );
QVERIFY( QgsGeometryUtils::verticesAtDistance( polygon1, 4.11, previous, next ) );
QCOMPARE( previous, QgsVertexId( 0, 1, 1 ) );
QCOMPARE( next, QgsVertexId( 0, 1, 2 ) );

// multipolygon
outerRing1 = new QgsLineString();
outerRing1->setPoints( QVector<QgsPoint>() << QgsPoint( 1, 1 ) << QgsPoint( 1, 2 ) << QgsPoint( 2, 2 ) << QgsPoint( 2, 1 ) << QgsPoint( 1, 1 ) );
QgsPolygon *polygon2 = new QgsPolygon();
polygon2->setExteriorRing( outerRing1 );

QgsLineString *outerRing2 = new QgsLineString();
outerRing2->setPoints( QVector<QgsPoint>() << QgsPoint( 10, 10 ) << QgsPoint( 10, 20 ) << QgsPoint( 20, 20 ) << QgsPoint( 20, 10 ) << QgsPoint( 10, 10 ) );
QgsPolygon *polygon3 = new QgsPolygon();
polygon3->setExteriorRing( outerRing2 );

QgsLineString *innerRing2 = new QgsLineString();
innerRing2->setPoints( QVector<QgsPoint>() << QgsPoint( 14, 14 ) << QgsPoint( 14, 16 ) << QgsPoint( 16, 16 ) << QgsPoint( 16, 14 ) << QgsPoint( 14, 14 ) );
polygon3->setInteriorRings( QVector<QgsCurve *>() << innerRing2 );

QgsMultiPolygon mpg;
mpg.addGeometry( polygon2 );
mpg.addGeometry( polygon3 );
QVERIFY( QgsGeometryUtils::verticesAtDistance( mpg, 0.1, previous, next ) );
QCOMPARE( previous, QgsVertexId( 0, 0, 0 ) );
QCOMPARE( next, QgsVertexId( 0, 0, 1 ) );
QVERIFY( QgsGeometryUtils::verticesAtDistance( mpg, 5, previous, next ) );
QCOMPARE( previous, QgsVertexId( 1, 0, 0 ) );
QCOMPARE( next, QgsVertexId( 1, 0, 1 ) );
QVERIFY( QgsGeometryUtils::verticesAtDistance( mpg, 45, previous, next ) );
QCOMPARE( previous, QgsVertexId( 1, 1, 0 ) );
QCOMPARE( next, QgsVertexId( 1, 1, 1 ) );

//test with point
QgsPoint point( 1, 2 );
QVERIFY( !QgsGeometryUtils::verticesAtDistance( point, .5, previous, next ) );
Expand Down
39 changes: 38 additions & 1 deletion tests/src/python/test_qgsgeometry.py
Expand Up @@ -4317,13 +4317,50 @@ def testInterpolate(self):
exp = 'Point(5 0)'
result = linestring.interpolate(5).asWkt()
self.assertTrue(compareWkt(result, exp, 0.00001), "Interpolate: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))
self.assertTrue(linestring.interpolate(25).isNull())

# multilinestring
linestring = QgsGeometry.fromWkt('MultiLineString((0 0, 10 0, 10 10),(20 0, 30 0, 30 10))')
exp = 'Point(5 0)'
result = linestring.interpolate(5).asWkt()
self.assertTrue(compareWkt(result, exp, 0.00001),
"Interpolate: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))
exp = 'Point(10 5)'
result = linestring.interpolate(15).asWkt()
self.assertTrue(compareWkt(result, exp, 0.00001),
"Interpolate: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))
exp = 'Point(10 10)'
result = linestring.interpolate(20).asWkt()
self.assertTrue(compareWkt(result, exp, 0.00001),
"Interpolate: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))
exp = 'Point(25 0)'
result = linestring.interpolate(25).asWkt()
self.assertTrue(compareWkt(result, exp, 0.00001),
"Interpolate: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))
exp = 'Point(30 0)'
result = linestring.interpolate(30).asWkt()
self.assertTrue(compareWkt(result, exp, 0.00001),
"Interpolate: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))
exp = 'Point(30 5)'
result = linestring.interpolate(35).asWkt()
self.assertTrue(compareWkt(result, exp, 0.00001),
"Interpolate: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))
self.assertTrue(linestring.interpolate(50).isNull())

# polygon
polygon = QgsGeometry.fromWkt('Polygon((0 0, 10 0, 10 10, 20 20, 10 20, 0 0))') # NOQA
exp = 'Point(10 5)'
result = linestring.interpolate(15).asWkt()
result = polygon.interpolate(15).asWkt()
self.assertTrue(compareWkt(result, exp, 0.00001),
"Interpolate: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))
self.assertTrue(polygon.interpolate(68).isNull())

# polygon with ring
polygon = QgsGeometry.fromWkt('Polygon((0 0, 10 0, 10 10, 20 20, 10 20, 0 0),(5 5, 6 5, 6 6, 5 6, 5 5))') # NOQA
exp = 'Point (6 5.5)'
result = polygon.interpolate(68).asWkt()
self.assertTrue(compareWkt(result, exp, 0.1),
"Interpolate: mismatch Expected:\n{}\nGot:\n{}\n".format(exp, result))

def testAngleAtVertex(self):
""" test QgsGeometry.angleAtVertex """
Expand Down

0 comments on commit ac3b446

Please sign in to comment.