diff --git a/python/plugins/processing/tests/testdata/expected/split_lines_by_length.gml b/python/plugins/processing/tests/testdata/expected/split_lines_by_length.gml
new file mode 100644
index 00000000000..1fc2c849979
--- /dev/null
+++ b/python/plugins/processing/tests/testdata/expected/split_lines_by_length.gml
@@ -0,0 +1,83 @@
+
+
+
+
+ -1-3
+ 115
+
+
+
+
+
+ 6,2 8.2,2.0
+
+
+
+
+ 8.2,2.0 9,2 9,3 9.28284271247462,3.28284271247462
+
+
+
+
+ 9.28284271247462,3.28284271247462 10.838477631085,4.83847763108502
+
+
+
+
+ 10.838477631085,4.83847763108502 11,5
+
+
+
+
+ -1,-1 1,-1
+
+
+
+
+ 2,0 2,2 2.2,2.0
+
+
+
+
+ 2.2,2.0 3,2 3,3
+
+
+
+
+ 3,1 5,1
+
+
+
+
+ 7,-3 9.2,-3.0
+
+
+
+
+ 9.2,-3.0 10,-3
+
+
+
+
+ 6,-3 7.55563491861041,-1.4443650813896
+
+
+
+
+ 7.55563491861041,-1.4443650813896 9.11126983722081,0.111269837220809
+
+
+
+
+ 9.11126983722081,0.111269837220809 10,1
+
+
+
+
+
+
+
diff --git a/python/plugins/processing/tests/testdata/expected/split_lines_by_length.xsd b/python/plugins/processing/tests/testdata/expected/split_lines_by_length.xsd
new file mode 100644
index 00000000000..93aaed6f635
--- /dev/null
+++ b/python/plugins/processing/tests/testdata/expected/split_lines_by_length.xsd
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/python/plugins/processing/tests/testdata/expected/split_linez_by_length.dbf b/python/plugins/processing/tests/testdata/expected/split_linez_by_length.dbf
new file mode 100644
index 00000000000..41b6f927905
Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/split_linez_by_length.dbf differ
diff --git a/python/plugins/processing/tests/testdata/expected/split_linez_by_length.prj b/python/plugins/processing/tests/testdata/expected/split_linez_by_length.prj
new file mode 100644
index 00000000000..a30c00a55de
--- /dev/null
+++ b/python/plugins/processing/tests/testdata/expected/split_linez_by_length.prj
@@ -0,0 +1 @@
+GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
\ No newline at end of file
diff --git a/python/plugins/processing/tests/testdata/expected/split_linez_by_length.qpj b/python/plugins/processing/tests/testdata/expected/split_linez_by_length.qpj
new file mode 100644
index 00000000000..5fbc831e743
--- /dev/null
+++ b/python/plugins/processing/tests/testdata/expected/split_linez_by_length.qpj
@@ -0,0 +1 @@
+GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]]
diff --git a/python/plugins/processing/tests/testdata/expected/split_linez_by_length.shp b/python/plugins/processing/tests/testdata/expected/split_linez_by_length.shp
new file mode 100644
index 00000000000..e6cc5496886
Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/split_linez_by_length.shp differ
diff --git a/python/plugins/processing/tests/testdata/expected/split_linez_by_length.shx b/python/plugins/processing/tests/testdata/expected/split_linez_by_length.shx
new file mode 100644
index 00000000000..001e17fa573
Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/split_linez_by_length.shx differ
diff --git a/python/plugins/processing/tests/testdata/expected/split_multiline_by_length.gml b/python/plugins/processing/tests/testdata/expected/split_multiline_by_length.gml
new file mode 100644
index 00000000000..a4a9e778318
--- /dev/null
+++ b/python/plugins/processing/tests/testdata/expected/split_multiline_by_length.gml
@@ -0,0 +1,98 @@
+
+
+
+
+ -1-1
+ 5.580422264875244.119769673704415
+
+
+
+
+
+ -1,-1 0.1,-1.0
+
+
+
+
+ 0.1,-1.0 1,-1
+
+
+
+
+ 3,1 4.1,1.0
+
+
+
+
+ 4.1,1.0 5,1
+
+
+
+
+ 5.02418426103647,2.4147792706334 5.00538358886158,1.3149399484023
+
+
+
+
+ 5.00538358886158,1.3149399484023 5,1
+
+
+
+
+
+
+
+
+ 2,0 2.0,1.1
+
+
+
+
+ 2.0,1.1 2,2 2.2,2.0
+
+
+
+
+ 2.2,2.0 3,2 3.0,2.3
+
+
+
+
+ 3.0,2.3 3,3
+
+
+
+
+ 2.94433781190019,4.04721689059501 4.04388044198829,4.07893446646294
+
+
+
+
+ 4.04388044198829,4.07893446646294 5.1434230720764,4.11065204233086
+
+
+
+
+ 5.1434230720764,4.11065204233086 5.4595009596929,4.11976967370441
+
+
+
+
+ 3,3 4.09976658596272,2.97734042366024
+
+
+
+
+ 4.09976658596272,2.97734042366024 5.19953317192545,2.95468084732049
+
+
+
+
+ 5.19953317192545,2.95468084732049 5.58042226487524,2.9468330134357
+
+
+
diff --git a/python/plugins/processing/tests/testdata/expected/split_multiline_by_length.xsd b/python/plugins/processing/tests/testdata/expected/split_multiline_by_length.xsd
new file mode 100644
index 00000000000..e8b95138b8b
--- /dev/null
+++ b/python/plugins/processing/tests/testdata/expected/split_multiline_by_length.xsd
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
index 7ebacd0ba6d..1ef6a52172c 100755
--- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
+++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
@@ -6360,4 +6360,41 @@ tests:
name: expected/force_rhr_multipolys.gml
type: vector
+ - algorithm: native:splitlinesbylength
+ name: Split multilines by length
+ params:
+ INPUT:
+ name: multilines.gml|layername=multilines
+ type: vector
+ LENGTH: 1.1
+ results:
+ OUTPUT:
+ name: expected/split_multiline_by_length.gml
+ type: vector
+
+ - algorithm: native:splitlinesbylength
+ name: Split lines by length
+ params:
+ INPUT:
+ name: lines.gml|layername=lines
+ type: vector
+ LENGTH: 2.2
+ results:
+ OUTPUT:
+ name: expected/split_lines_by_length.gml
+ type: vector
+
+ - algorithm: native:splitlinesbylength
+ name: Split linesz by length
+ params:
+ INPUT:
+ name: lines_z.shp
+ type: vector
+ LENGTH: 3.1
+ results:
+ OUTPUT:
+ name: expected/split_linez_by_length.shp
+ type: vector
+
+
# See ../README.md for a description of the file format
diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt
index de4b521655a..6937ae26fac 100644
--- a/src/analysis/CMakeLists.txt
+++ b/src/analysis/CMakeLists.txt
@@ -92,6 +92,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmsimplify.cpp
processing/qgsalgorithmsmooth.cpp
processing/qgsalgorithmsnaptogrid.cpp
+ processing/qgsalgorithmsplitlinesbylength.cpp
processing/qgsalgorithmsplitwithlines.cpp
processing/qgsalgorithmstringconcatenation.cpp
processing/qgsalgorithmswapxy.cpp
diff --git a/src/analysis/processing/qgsalgorithmsplitlinesbylength.cpp b/src/analysis/processing/qgsalgorithmsplitlinesbylength.cpp
new file mode 100644
index 00000000000..8ff44b18472
--- /dev/null
+++ b/src/analysis/processing/qgsalgorithmsplitlinesbylength.cpp
@@ -0,0 +1,151 @@
+/***************************************************************************
+ qgsalgorithmsplitlinesbylength.cpp
+ ---------------------
+ begin : December 2018
+ copyright : (C) 2018 by Nyall Dawson
+ email : nyall dot dawson at gmail dot com
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#include "qgsalgorithmsplitlinesbylength.h"
+#include "qgscurve.h"
+#include "qgslinestring.h"
+#include "qgscircularstring.h"
+#include "qgscompoundcurve.h"
+#include "qgsgeometrycollection.h"
+
+///@cond PRIVATE
+
+QString QgsSplitLinesByLengthAlgorithm::name() const
+{
+ return QStringLiteral( "splitlinesbylength" );
+}
+
+QString QgsSplitLinesByLengthAlgorithm::displayName() const
+{
+ return QObject::tr( "Split lines by maximum length" );
+}
+
+QStringList QgsSplitLinesByLengthAlgorithm::tags() const
+{
+ return QObject::tr( "segments,parts,distance,cut,chop" ).split( ',' );
+}
+
+QString QgsSplitLinesByLengthAlgorithm::group() const
+{
+ return QObject::tr( "Vector geometry" );
+}
+
+QString QgsSplitLinesByLengthAlgorithm::groupId() const
+{
+ return QStringLiteral( "vectorgeometry" );
+}
+
+QString QgsSplitLinesByLengthAlgorithm::shortHelpString() const
+{
+ return QObject::tr( "This algorithm takes a line (or curve) layer and splits each feature into multiple parts, "
+ "where each part is of a specified maximum length.\n\n"
+ "Z and M values at the start and end of the new line substrings are linearly interpolated from existing values." );
+}
+
+QString QgsSplitLinesByLengthAlgorithm::shortDescription() const
+{
+ return QObject::tr( "Splits lines into parts which are no longer than a specified length." );
+}
+
+QList QgsSplitLinesByLengthAlgorithm::inputLayerTypes() const
+{
+ return QList() << QgsProcessing::TypeVectorLine;
+}
+
+QgsProcessing::SourceType QgsSplitLinesByLengthAlgorithm::outputLayerType() const
+{
+ return QgsProcessing::TypeVectorLine;
+}
+
+QgsSplitLinesByLengthAlgorithm *QgsSplitLinesByLengthAlgorithm::createInstance() const
+{
+ return new QgsSplitLinesByLengthAlgorithm();
+}
+
+void QgsSplitLinesByLengthAlgorithm::initParameters( const QVariantMap & )
+{
+ std::unique_ptr< QgsProcessingParameterDistance > length = qgis::make_unique< QgsProcessingParameterDistance >( QStringLiteral( "LENGTH" ),
+ QObject::tr( "Maximum line length" ), 10, QStringLiteral( "INPUT" ), false, 0 );
+ length->setIsDynamic( true );
+ length->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "LENGTH" ), QObject::tr( "Maximum length" ), QgsPropertyDefinition::DoublePositive ) );
+ length->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
+ addParameter( length.release() );
+}
+
+bool QgsSplitLinesByLengthAlgorithm::prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback * )
+{
+ mLength = parameterAsDouble( parameters, QStringLiteral( "LENGTH" ), context );
+ mDynamicLength = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "LENGTH" ) );
+ if ( mDynamicLength )
+ mLengthProperty = parameters.value( QStringLiteral( "LENGTH" ) ).value< QgsProperty >();
+
+ return true;
+}
+
+QString QgsSplitLinesByLengthAlgorithm::outputName() const
+{
+ return QObject::tr( "Split" );
+}
+
+QgsWkbTypes::Type QgsSplitLinesByLengthAlgorithm::outputWkbType( QgsWkbTypes::Type inputWkbType ) const
+{
+ return QgsWkbTypes::singleType( inputWkbType );
+}
+
+QgsFeatureList QgsSplitLinesByLengthAlgorithm::processFeature( const QgsFeature &f, QgsProcessingContext &context, QgsProcessingFeedback * )
+{
+ if ( !f.hasGeometry() )
+ {
+ return QgsFeatureList() << f;
+ }
+ else
+ {
+ double distance = mLength;
+ if ( mDynamicLength )
+ distance = mLengthProperty.valueAsDouble( context.expressionContext(), distance );
+
+ QgsFeature outputFeature;
+ outputFeature.setAttributes( f.attributes() );
+ QgsFeatureList features;
+ const QgsGeometry inputGeom = f.geometry();
+ for ( auto it = inputGeom.const_parts_begin(); it != inputGeom.const_parts_end(); ++it )
+ {
+ const QgsCurve *part = qgsgeometry_cast< const QgsCurve * >( *it );
+ if ( !part )
+ continue;
+
+ double start = 0.0;
+ double end = distance;
+ const double length = part->length();
+ while ( start < length )
+ {
+ outputFeature.setGeometry( QgsGeometry( part->curveSubstring( start, end ) ) );
+ start += distance;
+ end += distance;
+ features << outputFeature;
+ }
+
+ }
+ return features;
+ }
+}
+
+
+///@endcond
+
+
+
diff --git a/src/analysis/processing/qgsalgorithmsplitlinesbylength.h b/src/analysis/processing/qgsalgorithmsplitlinesbylength.h
new file mode 100644
index 00000000000..97384a29039
--- /dev/null
+++ b/src/analysis/processing/qgsalgorithmsplitlinesbylength.h
@@ -0,0 +1,67 @@
+/***************************************************************************
+ qgsalgorithmsplitlinesbylength.h
+ ---------------------
+ begin : December 2018
+ copyright : (C) 2018 by Nyall Dawson
+ email : nyall dot dawson at gmail dot com
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef QGSALGORITHMSPLITLINESBYLENGTH_H
+#define QGSALGORITHMSPLITLINESBYLENGTH_H
+
+#define SIP_NO_FILE
+
+#include "qgis.h"
+#include "qgsprocessingalgorithm.h"
+
+///@cond PRIVATE
+
+/**
+ * Native split lines by maximum length algorithm.
+ */
+class QgsSplitLinesByLengthAlgorithm : public QgsProcessingFeatureBasedAlgorithm
+{
+
+ public:
+
+ QgsSplitLinesByLengthAlgorithm() = default;
+ QString name() const override;
+ QString displayName() const override;
+ QStringList tags() const override;
+ QString group() const override;
+ QString groupId() const override;
+ QString shortHelpString() const override;
+ QString shortDescription() const override;
+ QList inputLayerTypes() const override;
+ QgsProcessing::SourceType outputLayerType() const override;
+ QgsSplitLinesByLengthAlgorithm *createInstance() const override SIP_FACTORY;
+ void initParameters( const QVariantMap &configuration = QVariantMap() ) override;
+ bool prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
+
+ protected:
+ QString outputName() const override;
+ QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const override;
+ QgsFeatureList processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
+
+ private:
+
+ double mLength = 0.0;
+ bool mDynamicLength = false;
+ QgsProperty mLengthProperty;
+
+};
+
+///@endcond PRIVATE
+
+#endif // QGSALGORITHMSPLITLINESBYLENGTH_H
+
+
diff --git a/src/analysis/processing/qgsnativealgorithms.cpp b/src/analysis/processing/qgsnativealgorithms.cpp
index cf4f53fa9e6..cc62f226a4e 100644
--- a/src/analysis/processing/qgsnativealgorithms.cpp
+++ b/src/analysis/processing/qgsnativealgorithms.cpp
@@ -87,6 +87,7 @@
#include "qgsalgorithmsimplify.h"
#include "qgsalgorithmsmooth.h"
#include "qgsalgorithmsnaptogrid.h"
+#include "qgsalgorithmsplitlinesbylength.h"
#include "qgsalgorithmsplitwithlines.h"
#include "qgsalgorithmstringconcatenation.h"
#include "qgsalgorithmsubdivide.h"
@@ -221,6 +222,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsSimplifyAlgorithm() );
addAlgorithm( new QgsSmoothAlgorithm() );
addAlgorithm( new QgsSnapToGridAlgorithm() );
+ addAlgorithm( new QgsSplitLinesByLengthAlgorithm() );
addAlgorithm( new QgsSplitWithLinesAlgorithm() );
addAlgorithm( new QgsStringConcatenationAlgorithm() );
addAlgorithm( new QgsSubdivideAlgorithm() );
diff --git a/tests/src/python/test_qgsprocessinginplace.py b/tests/src/python/test_qgsprocessinginplace.py
index 8b9c7ba57a6..5e1ee479670 100644
--- a/tests/src/python/test_qgsprocessinginplace.py
+++ b/tests/src/python/test_qgsprocessinginplace.py
@@ -175,6 +175,7 @@ class TestQgsProcessingInPlace(unittest.TestCase):
self._support_inplace_edit_tester('native:difference', GEOMETRY_ONLY)
self._support_inplace_edit_tester('native:dropgeometries', ALL)
self._support_inplace_edit_tester('native:splitwithlines', LINESTRING_AND_POLYGON_ONLY)
+ self._support_inplace_edit_tester('native:splitlinesbylength', LINESTRING_ONLY)
self._support_inplace_edit_tester('native:buffer', POLYGON_ONLY_NOT_M_NOT_Z)
def _make_compatible_tester(self, feature_wkt, layer_wkb_name, attrs=[1]):