Skip to content

Commit 7ef5631

Browse files
committed
[FEATURE][processing] Line substring algorithm
This algorithm returns the portion of a line (or curve) which falls between the specified start and end distances (measured from the beginning of the line). Z and M values are linearly interpolated from existing values.
1 parent a4386d6 commit 7ef5631

10 files changed

+406
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<GMLFeatureClassList>
2+
<GMLFeatureClass>
3+
<Name>airports</Name>
4+
<ElementPath>airports</ElementPath>
5+
<!--POINT-->
6+
<GeometryType>1</GeometryType>
7+
<SRSName>EPSG:2964</SRSName>
8+
<DatasetSpecificInfo>
9+
<FeatureCount>76</FeatureCount>
10+
<ExtentXMin>-4480198.52221</ExtentXMin>
11+
<ExtentXMax>4615124.97899</ExtentXMax>
12+
<ExtentYMin>1433525.79887</ExtentYMin>
13+
<ExtentYMax>6502586.83035</ExtentYMax>
14+
</DatasetSpecificInfo>
15+
<PropertyDefn>
16+
<Name>ID</Name>
17+
<ElementPath>ID</ElementPath>
18+
<Type>Integer</Type>
19+
</PropertyDefn>
20+
<PropertyDefn>
21+
<Name>fk_region</Name>
22+
<ElementPath>fk_region</ElementPath>
23+
<Type>Integer</Type>
24+
</PropertyDefn>
25+
<PropertyDefn>
26+
<Name>ELEV</Name>
27+
<ElementPath>ELEV</ElementPath>
28+
<Type>Real</Type>
29+
</PropertyDefn>
30+
<PropertyDefn>
31+
<Name>NAME</Name>
32+
<ElementPath>NAME</ElementPath>
33+
<Type>String</Type>
34+
<Width>26</Width>
35+
</PropertyDefn>
36+
<PropertyDefn>
37+
<Name>USE</Name>
38+
<ElementPath>USE</ElementPath>
39+
<Type>String</Type>
40+
<Width>23</Width>
41+
</PropertyDefn>
42+
</GMLFeatureClass>
43+
</GMLFeatureClassList>
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<GMLFeatureClassList>
2+
<GMLFeatureClass>
3+
<Name>airportsvoronoidiagram</Name>
4+
<ElementPath>airportsvoronoidiagram</ElementPath>
5+
<!--POLYGON-->
6+
<GeometryType>3</GeometryType>
7+
<SRSName>EPSG:2964</SRSName>
8+
<DatasetSpecificInfo>
9+
<FeatureCount>76</FeatureCount>
10+
<ExtentXMin>-4480198.52221</ExtentXMin>
11+
<ExtentXMax>4615124.97899</ExtentXMax>
12+
<ExtentYMin>1433525.79887</ExtentYMin>
13+
<ExtentYMax>6502586.83035</ExtentYMax>
14+
</DatasetSpecificInfo>
15+
<PropertyDefn>
16+
<Name>ID</Name>
17+
<ElementPath>ID</ElementPath>
18+
<Type>Integer</Type>
19+
</PropertyDefn>
20+
<PropertyDefn>
21+
<Name>fk_region</Name>
22+
<ElementPath>fk_region</ElementPath>
23+
<Type>Integer</Type>
24+
</PropertyDefn>
25+
<PropertyDefn>
26+
<Name>ELEV</Name>
27+
<ElementPath>ELEV</ElementPath>
28+
<Type>Real</Type>
29+
</PropertyDefn>
30+
<PropertyDefn>
31+
<Name>NAME</Name>
32+
<ElementPath>NAME</ElementPath>
33+
<Type>String</Type>
34+
<Width>26</Width>
35+
</PropertyDefn>
36+
<PropertyDefn>
37+
<Name>USE</Name>
38+
<ElementPath>USE</ElementPath>
39+
<Type>String</Type>
40+
<Width>23</Width>
41+
</PropertyDefn>
42+
</GMLFeatureClass>
43+
</GMLFeatureClassList>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ogr:FeatureCollection
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://ogr.maptools.org/ line_substring.xsd"
5+
xmlns:ogr="http://ogr.maptools.org/"
6+
xmlns:gml="http://www.opengis.net/gml">
7+
<gml:boundedBy>
8+
<gml:Box>
9+
<gml:coord><gml:X>-0.8</gml:X><gml:Y>-3</gml:Y></gml:coord>
10+
<gml:coord><gml:X>7.8</gml:X><gml:Y>2</gml:Y></gml:coord>
11+
</gml:Box>
12+
</gml:boundedBy>
13+
14+
<gml:featureMember>
15+
<ogr:line_substring fid="lines.0">
16+
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>6.2,2.0 6.8,2.0</gml:coordinates></gml:LineString></ogr:geometryProperty>
17+
</ogr:line_substring>
18+
</gml:featureMember>
19+
<gml:featureMember>
20+
<ogr:line_substring fid="lines.1">
21+
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>-0.8,-1.0 -0.2,-1.0</gml:coordinates></gml:LineString></ogr:geometryProperty>
22+
</ogr:line_substring>
23+
</gml:featureMember>
24+
<gml:featureMember>
25+
<ogr:line_substring fid="lines.2">
26+
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>2.0,0.2 2.0,0.8</gml:coordinates></gml:LineString></ogr:geometryProperty>
27+
</ogr:line_substring>
28+
</gml:featureMember>
29+
<gml:featureMember>
30+
<ogr:line_substring fid="lines.3">
31+
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>3.2,1.0 3.8,1.0</gml:coordinates></gml:LineString></ogr:geometryProperty>
32+
</ogr:line_substring>
33+
</gml:featureMember>
34+
<gml:featureMember>
35+
<ogr:line_substring fid="lines.4">
36+
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>7.2,-3.0 7.8,-3.0</gml:coordinates></gml:LineString></ogr:geometryProperty>
37+
</ogr:line_substring>
38+
</gml:featureMember>
39+
<gml:featureMember>
40+
<ogr:line_substring fid="lines.5">
41+
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>6.14142135623731,-2.85857864376269 6.56568542494924,-2.43431457505076</gml:coordinates></gml:LineString></ogr:geometryProperty>
42+
</ogr:line_substring>
43+
</gml:featureMember>
44+
<gml:featureMember>
45+
<ogr:line_substring fid="lines.6">
46+
</ogr:line_substring>
47+
</gml:featureMember>
48+
</ogr:FeatureCollection>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
3+
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
4+
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
5+
<xs:complexType name="FeatureCollectionType">
6+
<xs:complexContent>
7+
<xs:extension base="gml:AbstractFeatureCollectionType">
8+
<xs:attribute name="lockId" type="xs:string" use="optional"/>
9+
<xs:attribute name="scope" type="xs:string" use="optional"/>
10+
</xs:extension>
11+
</xs:complexContent>
12+
</xs:complexType>
13+
<xs:element name="line_substring" type="ogr:line_substring_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="line_substring_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="geometryProperty" type="gml:LineStringPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
19+
</xs:sequence>
20+
</xs:extension>
21+
</xs:complexContent>
22+
</xs:complexType>
23+
</xs:schema>

python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml

+12
Original file line numberDiff line numberDiff line change
@@ -6043,6 +6043,18 @@ tests:
60436043
name: expected/drape_lines_m.shp
60446044
type: vector
60456045

6046+
- algorithm: native:linesubstring
6047+
name: Line substrings
6048+
params:
6049+
END_DISTANCE: 0.8
6050+
INPUT:
6051+
name: lines.gml
6052+
type: vector
6053+
START_DISTANCE: 0.2
6054+
results:
6055+
OUTPUT:
6056+
name: expected/line_substring.gml
6057+
type: vector
60466058

60476059

60486060
# See ../README.md for a description of the file format

src/analysis/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ SET(QGIS_ANALYSIS_SRCS
5252
processing/qgsalgorithmjoinwithlines.cpp
5353
processing/qgsalgorithmkmeansclustering.cpp
5454
processing/qgsalgorithmlineintersection.cpp
55+
processing/qgsalgorithmlinesubstring.cpp
5556
processing/qgsalgorithmloadlayer.cpp
5657
processing/qgsalgorithmmeancoordinates.cpp
5758
processing/qgsalgorithmmergelines.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/***************************************************************************
2+
qgsalgorithmlinesubstring.cpp
3+
---------------------
4+
begin : August 2018
5+
copyright : (C) 2018 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#include "qgsalgorithmlinesubstring.h"
19+
#include "qgsgeometrycollection.h"
20+
#include "qgscurve.h"
21+
22+
///@cond PRIVATE
23+
24+
QString QgsLineSubstringAlgorithm::name() const
25+
{
26+
return QStringLiteral( "linesubstring" );
27+
}
28+
29+
QString QgsLineSubstringAlgorithm::displayName() const
30+
{
31+
return QObject::tr( "Line substring" );
32+
}
33+
34+
QStringList QgsLineSubstringAlgorithm::tags() const
35+
{
36+
return QObject::tr( "linestring,curve,split,portion,reference,referencing,distance,interpolate" ).split( ',' );
37+
}
38+
39+
QString QgsLineSubstringAlgorithm::group() const
40+
{
41+
return QObject::tr( "Vector geometry" );
42+
}
43+
44+
QString QgsLineSubstringAlgorithm::groupId() const
45+
{
46+
return QStringLiteral( "vectorgeometry" );
47+
}
48+
49+
QString QgsLineSubstringAlgorithm::outputName() const
50+
{
51+
return QObject::tr( "Substring" );
52+
}
53+
54+
QString QgsLineSubstringAlgorithm::shortHelpString() const
55+
{
56+
return QObject::tr( "This algorithm returns the portion of a line (or curve) which falls "
57+
"between the specified start and end distances (measured from the "
58+
"beginning of the line).\n\n"
59+
"Z and M values are linearly interpolated from existing values." );
60+
}
61+
62+
QString QgsLineSubstringAlgorithm::shortDescription() const
63+
{
64+
return QObject::tr( "Returns the substring of lines which fall between start and end distances." );
65+
}
66+
67+
QList<int> QgsLineSubstringAlgorithm::inputLayerTypes() const
68+
{
69+
return QList<int>() << QgsProcessing::TypeVectorLine;
70+
}
71+
72+
QgsProcessing::SourceType QgsLineSubstringAlgorithm::outputLayerType() const
73+
{
74+
return QgsProcessing::TypeVectorLine;
75+
}
76+
77+
QgsLineSubstringAlgorithm *QgsLineSubstringAlgorithm::createInstance() const
78+
{
79+
return new QgsLineSubstringAlgorithm();
80+
}
81+
82+
void QgsLineSubstringAlgorithm::initParameters( const QVariantMap & )
83+
{
84+
std::unique_ptr< QgsProcessingParameterDistance> startDistance = qgis::make_unique< QgsProcessingParameterDistance >( QStringLiteral( "START_DISTANCE" ),
85+
QObject::tr( "Start distance" ), 0.0, QStringLiteral( "INPUT" ), false, 0 );
86+
startDistance->setIsDynamic( true );
87+
startDistance->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "Start Distance" ), QObject::tr( "Start distance" ), QgsPropertyDefinition::DoublePositive ) );
88+
startDistance->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
89+
addParameter( startDistance.release() );
90+
91+
std::unique_ptr< QgsProcessingParameterDistance> endDistance = qgis::make_unique< QgsProcessingParameterDistance >( QStringLiteral( "END_DISTANCE" ),
92+
QObject::tr( "End distance" ), 1.0, QStringLiteral( "INPUT" ), false, 0 );
93+
endDistance->setIsDynamic( true );
94+
endDistance->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "End Distance" ), QObject::tr( "End distance" ), QgsPropertyDefinition::DoublePositive ) );
95+
endDistance->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
96+
addParameter( endDistance.release() );
97+
}
98+
99+
QgsProcessingFeatureSource::Flag QgsLineSubstringAlgorithm::sourceFlags() const
100+
{
101+
// skip geometry checks - this algorithm doesn't care about invalid geometries
102+
return QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks;
103+
}
104+
105+
bool QgsLineSubstringAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
106+
{
107+
mStartDistance = parameterAsDouble( parameters, QStringLiteral( "START_DISTANCE" ), context );
108+
mDynamicStartDistance = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "START_DISTANCE" ) );
109+
if ( mDynamicStartDistance )
110+
mStartDistanceProperty = parameters.value( QStringLiteral( "START_DISTANCE" ) ).value< QgsProperty >();
111+
112+
mEndDistance = parameterAsDouble( parameters, QStringLiteral( "END_DISTANCE" ), context );
113+
mDynamicEndDistance = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "END_DISTANCE" ) );
114+
if ( mDynamicEndDistance )
115+
mEndDistanceProperty = parameters.value( QStringLiteral( "END_DISTANCE" ) ).value< QgsProperty >();
116+
117+
return true;
118+
}
119+
120+
QgsFeatureList QgsLineSubstringAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback * )
121+
{
122+
QgsFeature f = feature;
123+
if ( f.hasGeometry() )
124+
{
125+
const QgsGeometry geometry = f.geometry();
126+
double startDistance = mStartDistance;
127+
if ( mDynamicStartDistance )
128+
startDistance = mStartDistanceProperty.valueAsDouble( context.expressionContext(), startDistance );
129+
130+
double endDistance = mEndDistance;
131+
if ( mDynamicEndDistance )
132+
endDistance = mEndDistanceProperty.valueAsDouble( context.expressionContext(), endDistance );
133+
134+
const QgsCurve *curve = nullptr;
135+
if ( !geometry.isMultipart() )
136+
curve = qgsgeometry_cast< const QgsCurve * >( geometry.constGet() );
137+
else
138+
{
139+
if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geometry.constGet() ) )
140+
{
141+
if ( collection->numGeometries() > 0 )
142+
{
143+
curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
144+
}
145+
}
146+
}
147+
if ( curve )
148+
{
149+
std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
150+
QgsGeometry result( std::move( substring ) );
151+
f.setGeometry( result );
152+
}
153+
else
154+
{
155+
f.clearGeometry();
156+
}
157+
}
158+
return QgsFeatureList() << f;
159+
}
160+
161+
///@endcond
162+
163+

0 commit comments

Comments
 (0)