QgsMultiLineString: add method measuredLine

This commit is contained in:
Loïc Bartoletti 2023-12-12 10:00:58 +01:00 committed by Nyall Dawson
parent 6cb243571f
commit a93136c313
6 changed files with 120 additions and 5 deletions

View File

@ -88,6 +88,14 @@ Returns the geometry converted to the more generic curve type :py:class:`QgsMult
sipRes = PyUnicode_FromString( str.toUtf8().constData() );
%End
QgsMultiLineString *measuredLine( double start, double end );
%Docstring
Re-write the measure ordinate (or add one, if it isn't already there) interpolating
the measure between the supplied ``start`` and ``end`` values.
.. versionadded:: 3.36
%End
protected:
virtual bool wktOmitChildType() const;

View File

@ -14,6 +14,7 @@ email : marco.hugentobler at sourcepole dot com
***************************************************************************/
#include "qgsmultilinestring.h"
#include "qgsabstractgeometry.h"
#include "qgsapplication.h"
#include "qgscurve.h"
#include "qgscircularstring.h"
@ -178,3 +179,40 @@ bool QgsMultiLineString::wktOmitChildType() const
return true;
}
QgsMultiLineString *QgsMultiLineString::measuredLine( double start, double end )
{
QgsMultiLineString *result{createEmptyWithSameType()};
if ( isEmpty() )
{
return result;
}
if ( !result->convertTo( QgsWkbTypes::addM( mWkbType ) ) )
{
qDebug() << "Impossible to convert type to M type\n";
return result;
}
/* Calculate the total length of the line */
const double length{this->length()};
const double range{end - start};
double length_so_far{0.0};
result->reserve( numGeometries() );
for ( int i = 0; i < numGeometries(); i++ )
{
const double sub_length{geometryN( i )->length()};
const double sub_start{ ( start + range *length_so_far / length ) };
const double sub_end{ ( start + range * ( length_so_far + sub_length ) / length ) };
result->addGeometry( qgsgeometry_cast<QgsLineString *>( geometryN( i ) )->measuredLine( sub_start, sub_end ) );
length_so_far += sub_length;
}
return result;
}

View File

@ -127,6 +127,14 @@ class CORE_EXPORT QgsMultiLineString: public QgsMultiCurve
% End
#endif
/**
* Re-write the measure ordinate (or add one, if it isn't already there) interpolating
* the measure between the supplied \a start and \a end values.
*
* \since QGIS 3.36
*/
QgsMultiLineString *measuredLine( double start, double end );
protected:
bool wktOmitChildType() const override;

View File

@ -175,6 +175,7 @@ ADD_PYTHON_TEST(PyQgsMetadataBase test_qgsmetadatabase.py)
ADD_PYTHON_TEST(PyQgsMetadataUtils test_qgsmetadatautils.py)
ADD_PYTHON_TEST(PyQgsMemoryProvider test_provider_memory.py)
ADD_PYTHON_TEST(PyQgsMeshLayerProfileGenerator test_qgsmeshlayerprofilegenerator.py)
ADD_PYTHON_TEST(PyQgsMultiLineString test_qgsmultilinestring.py)
ADD_PYTHON_TEST(PyQgsMssqlSqlQueryBuilder test_qgsmssqlsqlquerybuilder.py)
ADD_PYTHON_TEST(PyQgsNetworkAccessManager test_qgsnetworkaccessmanager.py)
ADD_PYTHON_TEST(PyQgsNetworkContentFetcher test_qgsnetworkcontentfetcher.py)

View File

@ -27,18 +27,22 @@ class TestQgsLineString(QgisTestCase):
self.assertEqual(line.endPoint(), QgsPoint(3, 4))
# With list
line = QgsLineString([ [1, 2], [3, 4] ])
line = QgsLineString([[1, 2], [3, 4]])
self.assertEqual(line.startPoint(), QgsPoint(1, 2))
self.assertEqual(line.endPoint(), QgsPoint(3, 4))
def testMeasureLine(self):
line = QgsLineString([ [0, 0], [2, 0], [4, 0]])
line = QgsLineString()
m_line = line.measuredLine(10, 20)
self.assertEqual(m_line, QgsLineString([QgsPoint(0, 0, m=10),QgsPoint(2, 0, m=15) ,QgsPoint(4, 0, m=20) ]))
self.assertEqual(m_line, QgsLineString())
line = QgsLineString([ [0, 0], [9, 0], [10, 0]])
line = QgsLineString([[0, 0], [2, 0], [4, 0]])
m_line = line.measuredLine(10, 20)
self.assertEqual(m_line, QgsLineString([QgsPoint(0, 0, m=10),QgsPoint(9, 0, m=19) ,QgsPoint(10, 0, m=20) ]))
self.assertEqual(m_line, QgsLineString([QgsPoint(0, 0, m=10), QgsPoint(2, 0, m=15), QgsPoint(4, 0, m=20)]))
line = QgsLineString([[0, 0], [9, 0], [10, 0]])
m_line = line.measuredLine(10, 20)
self.assertEqual(m_line, QgsLineString([QgsPoint(0, 0, m=10), QgsPoint(9, 0, m=19), QgsPoint(10, 0, m=20)]))
if __name__ == '__main__':

View File

@ -0,0 +1,56 @@
"""QGIS Unit tests for QgsMultiLineString.
.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"""
__author__ = 'Loïc Bartoletti'
__date__ = '12/12/2023'
__copyright__ = 'Copyright 2023, The QGIS Project'
import qgis # NOQA
from qgis.core import QgsMultiLineString, QgsLineString, QgsPoint
import unittest
from qgis.testing import start_app, QgisTestCase
start_app()
class TestQgsLineString(QgisTestCase):
def testConstruct(self):
# With list
line = QgsLineString([QgsPoint(1, 2), QgsPoint(3, 4)])
multiline = QgsMultiLineString()
multiline.addGeometry(line)
self.assertEqual(multiline.numGeometries(), 1)
line = multiline.geometryN(0)
self.assertEqual(line.startPoint(), QgsPoint(1, 2))
self.assertEqual(line.endPoint(), QgsPoint(3, 4))
def testMeasureLine(self):
multiline = QgsMultiLineString()
m_line = multiline.measuredLine(10, 20)
self.assertEqual(m_line, QgsMultiLineString())
multiline.addGeometry(QgsLineString([[0, 0], [2, 0], [4, 0]]))
m_line = multiline.measuredLine(10, 20)
self.assertEqual(m_line.geometryN(0), QgsLineString([QgsPoint(0, 0, m=10), QgsPoint(2, 0, m=15), QgsPoint(4, 0, m=20)]))
multiline = QgsMultiLineString()
multiline.addGeometry(QgsLineString([[0, 0], [9, 0], [10, 0]]))
m_line = multiline.measuredLine(10, 20)
self.assertEqual(m_line.geometryN(0), QgsLineString([QgsPoint(0, 0, m=10), QgsPoint(9, 0, m=19), QgsPoint(10, 0, m=20)]))
multiline = QgsMultiLineString()
multiline.addGeometry(QgsLineString([[1, 0], [3, 0], [4, 0]]))
multiline.addGeometry(QgsLineString([[0, 0], [9, 0], [10, 0]]))
m_line = multiline.measuredLine(10, 20)
self.assertEqual(m_line.numGeometries(), 2)
self.assertEqual(m_line.asWkt(0), "MultiLineStringM ((1 0 10, 3 0 12, 4 0 12),(0 0 12, 9 0 19, 10 0 20))")
if __name__ == '__main__':
unittest.main()