Skip to content

Commit

Permalink
[FEATURE][processing] Line substring algorithm
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nyalldawson committed Aug 15, 2018
1 parent a4386d6 commit 7ef5631
Show file tree
Hide file tree
Showing 10 changed files with 406 additions and 0 deletions.
43 changes: 43 additions & 0 deletions python/plugins/processing/tests/testdata/airports.gfs
@@ -0,0 +1,43 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>airports</Name>
<ElementPath>airports</ElementPath>
<!--POINT-->
<GeometryType>1</GeometryType>
<SRSName>EPSG:2964</SRSName>
<DatasetSpecificInfo>
<FeatureCount>76</FeatureCount>
<ExtentXMin>-4480198.52221</ExtentXMin>
<ExtentXMax>4615124.97899</ExtentXMax>
<ExtentYMin>1433525.79887</ExtentYMin>
<ExtentYMax>6502586.83035</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>ID</Name>
<ElementPath>ID</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
<PropertyDefn>
<Name>fk_region</Name>
<ElementPath>fk_region</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
<PropertyDefn>
<Name>ELEV</Name>
<ElementPath>ELEV</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>NAME</Name>
<ElementPath>NAME</ElementPath>
<Type>String</Type>
<Width>26</Width>
</PropertyDefn>
<PropertyDefn>
<Name>USE</Name>
<ElementPath>USE</ElementPath>
<Type>String</Type>
<Width>23</Width>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>
Binary file not shown.
@@ -0,0 +1,43 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>airportsvoronoidiagram</Name>
<ElementPath>airportsvoronoidiagram</ElementPath>
<!--POLYGON-->
<GeometryType>3</GeometryType>
<SRSName>EPSG:2964</SRSName>
<DatasetSpecificInfo>
<FeatureCount>76</FeatureCount>
<ExtentXMin>-4480198.52221</ExtentXMin>
<ExtentXMax>4615124.97899</ExtentXMax>
<ExtentYMin>1433525.79887</ExtentYMin>
<ExtentYMax>6502586.83035</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>ID</Name>
<ElementPath>ID</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
<PropertyDefn>
<Name>fk_region</Name>
<ElementPath>fk_region</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
<PropertyDefn>
<Name>ELEV</Name>
<ElementPath>ELEV</ElementPath>
<Type>Real</Type>
</PropertyDefn>
<PropertyDefn>
<Name>NAME</Name>
<ElementPath>NAME</ElementPath>
<Type>String</Type>
<Width>26</Width>
</PropertyDefn>
<PropertyDefn>
<Name>USE</Name>
<ElementPath>USE</ElementPath>
<Type>String</Type>
<Width>23</Width>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ line_substring.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>-0.8</gml:X><gml:Y>-3</gml:Y></gml:coord>
<gml:coord><gml:X>7.8</gml:X><gml:Y>2</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:line_substring fid="lines.0">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>6.2,2.0 6.8,2.0</gml:coordinates></gml:LineString></ogr:geometryProperty>
</ogr:line_substring>
</gml:featureMember>
<gml:featureMember>
<ogr:line_substring fid="lines.1">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>-0.8,-1.0 -0.2,-1.0</gml:coordinates></gml:LineString></ogr:geometryProperty>
</ogr:line_substring>
</gml:featureMember>
<gml:featureMember>
<ogr:line_substring fid="lines.2">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>2.0,0.2 2.0,0.8</gml:coordinates></gml:LineString></ogr:geometryProperty>
</ogr:line_substring>
</gml:featureMember>
<gml:featureMember>
<ogr:line_substring fid="lines.3">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>3.2,1.0 3.8,1.0</gml:coordinates></gml:LineString></ogr:geometryProperty>
</ogr:line_substring>
</gml:featureMember>
<gml:featureMember>
<ogr:line_substring fid="lines.4">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>7.2,-3.0 7.8,-3.0</gml:coordinates></gml:LineString></ogr:geometryProperty>
</ogr:line_substring>
</gml:featureMember>
<gml:featureMember>
<ogr:line_substring fid="lines.5">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>6.14142135623731,-2.85857864376269 6.56568542494924,-2.43431457505076</gml:coordinates></gml:LineString></ogr:geometryProperty>
</ogr:line_substring>
</gml:featureMember>
<gml:featureMember>
<ogr:line_substring fid="lines.6">
</ogr:line_substring>
</gml:featureMember>
</ogr:FeatureCollection>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureCollectionType">
<xs:attribute name="lockId" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="line_substring" type="ogr:line_substring_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="line_substring_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="geometryProperty" type="gml:LineStringPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
12 changes: 12 additions & 0 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Expand Up @@ -6043,6 +6043,18 @@ tests:
name: expected/drape_lines_m.shp name: expected/drape_lines_m.shp
type: vector type: vector


- algorithm: native:linesubstring
name: Line substrings
params:
END_DISTANCE: 0.8
INPUT:
name: lines.gml
type: vector
START_DISTANCE: 0.2
results:
OUTPUT:
name: expected/line_substring.gml
type: vector




# See ../README.md for a description of the file format # See ../README.md for a description of the file format
1 change: 1 addition & 0 deletions src/analysis/CMakeLists.txt
Expand Up @@ -52,6 +52,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmjoinwithlines.cpp processing/qgsalgorithmjoinwithlines.cpp
processing/qgsalgorithmkmeansclustering.cpp processing/qgsalgorithmkmeansclustering.cpp
processing/qgsalgorithmlineintersection.cpp processing/qgsalgorithmlineintersection.cpp
processing/qgsalgorithmlinesubstring.cpp
processing/qgsalgorithmloadlayer.cpp processing/qgsalgorithmloadlayer.cpp
processing/qgsalgorithmmeancoordinates.cpp processing/qgsalgorithmmeancoordinates.cpp
processing/qgsalgorithmmergelines.cpp processing/qgsalgorithmmergelines.cpp
Expand Down
163 changes: 163 additions & 0 deletions src/analysis/processing/qgsalgorithmlinesubstring.cpp
@@ -0,0 +1,163 @@
/***************************************************************************
qgsalgorithmlinesubstring.cpp
---------------------
begin : August 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 "qgsalgorithmlinesubstring.h"
#include "qgsgeometrycollection.h"
#include "qgscurve.h"

///@cond PRIVATE

QString QgsLineSubstringAlgorithm::name() const
{
return QStringLiteral( "linesubstring" );
}

QString QgsLineSubstringAlgorithm::displayName() const
{
return QObject::tr( "Line substring" );
}

QStringList QgsLineSubstringAlgorithm::tags() const
{
return QObject::tr( "linestring,curve,split,portion,reference,referencing,distance,interpolate" ).split( ',' );
}

QString QgsLineSubstringAlgorithm::group() const
{
return QObject::tr( "Vector geometry" );
}

QString QgsLineSubstringAlgorithm::groupId() const
{
return QStringLiteral( "vectorgeometry" );
}

QString QgsLineSubstringAlgorithm::outputName() const
{
return QObject::tr( "Substring" );
}

QString QgsLineSubstringAlgorithm::shortHelpString() const
{
return QObject::tr( "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).\n\n"
"Z and M values are linearly interpolated from existing values." );
}

QString QgsLineSubstringAlgorithm::shortDescription() const
{
return QObject::tr( "Returns the substring of lines which fall between start and end distances." );
}

QList<int> QgsLineSubstringAlgorithm::inputLayerTypes() const
{
return QList<int>() << QgsProcessing::TypeVectorLine;
}

QgsProcessing::SourceType QgsLineSubstringAlgorithm::outputLayerType() const
{
return QgsProcessing::TypeVectorLine;
}

QgsLineSubstringAlgorithm *QgsLineSubstringAlgorithm::createInstance() const
{
return new QgsLineSubstringAlgorithm();
}

void QgsLineSubstringAlgorithm::initParameters( const QVariantMap & )
{
std::unique_ptr< QgsProcessingParameterDistance> startDistance = qgis::make_unique< QgsProcessingParameterDistance >( QStringLiteral( "START_DISTANCE" ),
QObject::tr( "Start distance" ), 0.0, QStringLiteral( "INPUT" ), false, 0 );
startDistance->setIsDynamic( true );
startDistance->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "Start Distance" ), QObject::tr( "Start distance" ), QgsPropertyDefinition::DoublePositive ) );
startDistance->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( startDistance.release() );

std::unique_ptr< QgsProcessingParameterDistance> endDistance = qgis::make_unique< QgsProcessingParameterDistance >( QStringLiteral( "END_DISTANCE" ),
QObject::tr( "End distance" ), 1.0, QStringLiteral( "INPUT" ), false, 0 );
endDistance->setIsDynamic( true );
endDistance->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "End Distance" ), QObject::tr( "End distance" ), QgsPropertyDefinition::DoublePositive ) );
endDistance->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
addParameter( endDistance.release() );
}

QgsProcessingFeatureSource::Flag QgsLineSubstringAlgorithm::sourceFlags() const
{
// skip geometry checks - this algorithm doesn't care about invalid geometries
return QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks;
}

bool QgsLineSubstringAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
{
mStartDistance = parameterAsDouble( parameters, QStringLiteral( "START_DISTANCE" ), context );
mDynamicStartDistance = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "START_DISTANCE" ) );
if ( mDynamicStartDistance )
mStartDistanceProperty = parameters.value( QStringLiteral( "START_DISTANCE" ) ).value< QgsProperty >();

mEndDistance = parameterAsDouble( parameters, QStringLiteral( "END_DISTANCE" ), context );
mDynamicEndDistance = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "END_DISTANCE" ) );
if ( mDynamicEndDistance )
mEndDistanceProperty = parameters.value( QStringLiteral( "END_DISTANCE" ) ).value< QgsProperty >();

return true;
}

QgsFeatureList QgsLineSubstringAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback * )
{
QgsFeature f = feature;
if ( f.hasGeometry() )
{
const QgsGeometry geometry = f.geometry();
double startDistance = mStartDistance;
if ( mDynamicStartDistance )
startDistance = mStartDistanceProperty.valueAsDouble( context.expressionContext(), startDistance );

double endDistance = mEndDistance;
if ( mDynamicEndDistance )
endDistance = mEndDistanceProperty.valueAsDouble( context.expressionContext(), endDistance );

const QgsCurve *curve = nullptr;
if ( !geometry.isMultipart() )
curve = qgsgeometry_cast< const QgsCurve * >( geometry.constGet() );
else
{
if ( const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geometry.constGet() ) )
{
if ( collection->numGeometries() > 0 )
{
curve = qgsgeometry_cast< const QgsCurve * >( collection->geometryN( 0 ) );
}
}
}
if ( curve )
{
std::unique_ptr< QgsCurve > substring( curve->curveSubstring( startDistance, endDistance ) );
QgsGeometry result( std::move( substring ) );
f.setGeometry( result );
}
else
{
f.clearGeometry();
}
}
return QgsFeatureList() << f;
}

///@endcond


0 comments on commit 7ef5631

Please sign in to comment.