Skip to content

Commit

Permalink
[processing] Port Explode Lines to c++
Browse files Browse the repository at this point in the history
Aside from the performance benefits, the Python version of this
algorithm occasionally fails on Travis with odd errors. Hopefully
by porting to c++ it will fix these, or at least give useful
debug information in the event of a fail.

Also add support for curved input geometries.
  • Loading branch information
nyalldawson committed Apr 6, 2018
1 parent fa051d5 commit 1942854
Show file tree
Hide file tree
Showing 14 changed files with 279 additions and 106 deletions.
3 changes: 0 additions & 3 deletions python/plugins/processing/algs/help/qgis.yaml
Expand Up @@ -157,9 +157,6 @@ qgis:eliminateselectedpolygons: >
This algorithm combines selected polygons of the input layer with certain adjacent polygons by erasing their common boundary. The adjacent polygon can be either the one with the largest or smallest area or the one sharing the largest common boundary with the polygon to be eliminated. The selected features will always be eliminated whether the option "Use only selected features" is set or not.
Eliminate is normally used to get rid of sliver polygons, i.e. tiny polygons that are a result of polygon intersection processes where boundaries of the inputs are similar but not identical.

qgis:explodelines: >
This algorithm takes a lines layer and creates a new one in which each line is replaced by a set of lines representing the segments in the original line. Each line in the resulting layer contains only a start and an end point, with no intermediate nodes between them.

qgis:exportaddgeometrycolumns: >
This algorithm computes geometric properties of the features in a vector layer. It generates a new vector layer with the same content as the input one, but with additional attributes in its attributes table, containing geometric measurements.

Expand Down
99 changes: 0 additions & 99 deletions python/plugins/processing/algs/qgis/Explode.py

This file was deleted.

2 changes: 0 additions & 2 deletions python/plugins/processing/algs/qgis/QgisAlgorithmProvider.py
Expand Up @@ -60,7 +60,6 @@
from .Difference import Difference
from .EliminateSelection import EliminateSelection
from .ExecuteSQL import ExecuteSQL
from .Explode import Explode
from .ExportGeometryInfo import ExportGeometryInfo
from .ExtendLines import ExtendLines
from .ExtentFromLayer import ExtentFromLayer
Expand Down Expand Up @@ -180,7 +179,6 @@ def getAlgs(self):
Difference(),
EliminateSelection(),
ExecuteSQL(),
Explode(),
ExportGeometryInfo(),
ExtendLines(),
ExtentFromLayer(),
Expand Down
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]
@@ -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"]]
Binary file not shown.
Binary file not shown.
15 changes: 13 additions & 2 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Expand Up @@ -3477,7 +3477,7 @@ tests:
name: expected/voronoi_buffer.gml
type: vector

- algorithm: qgis:explodelines
- algorithm: native:explodelines
name: Explode lines
params:
INPUT:
Expand All @@ -3491,7 +3491,7 @@ tests:
fields:
fid: skip

- algorithm: qgis:explodelines
- algorithm: native:explodelines
name: Explode multilines
params:
INPUT:
Expand All @@ -3505,6 +3505,17 @@ tests:
fields:
fid: skip

- algorithm: native:explodelines
name: Explode compound curves
params:
INPUT:
name: custom/circular_strings.gpkg|layername=circular_strings_with_line
type: vector
results:
OUTPUT:
name: expected/explode_compound_curve.shp
type: vector

- algorithm: qgis:findprojection
name: Find projection
params:
Expand Down
1 change: 1 addition & 0 deletions src/analysis/CMakeLists.txt
Expand Up @@ -30,6 +30,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmdissolve.cpp
processing/qgsalgorithmdropgeometry.cpp
processing/qgsalgorithmdropmzvalues.cpp
processing/qgsalgorithmexplode.cpp
processing/qgsalgorithmextenttolayer.cpp
processing/qgsalgorithmextractbyattribute.cpp
processing/qgsalgorithmextractbyexpression.cpp
Expand Down
201 changes: 201 additions & 0 deletions src/analysis/processing/qgsalgorithmexplode.cpp
@@ -0,0 +1,201 @@
/***************************************************************************
qgsalgorithmexplode.cpp
---------------------
begin : April 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 "qgsalgorithmexplode.h"
#include "qgscurve.h"
#include "qgslinestring.h"
#include "qgscircularstring.h"
#include "qgscompoundcurve.h"
#include "qgsgeometrycollection.h"

///@cond PRIVATE

QString QgsExplodeAlgorithm::name() const
{
return QStringLiteral( "explodelines" );
}

QString QgsExplodeAlgorithm::displayName() const
{
return QObject::tr( "Explode lines" );
}

QStringList QgsExplodeAlgorithm::tags() const
{
return QObject::tr( "segments,parts" ).split( ',' );
}

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

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

QString QgsExplodeAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm takes a lines layer and creates a new one in which each line is replaced by a set of "
"lines representing the segments in the original line. Each line in the resulting layer contains only a "
"start and an end point, with no intermediate nodes between them.\n\n"
"If the input layer consists of CircularStrings or CompoundCurves, the output layer will be of the "
"same type and contain only single curve segments." );
}

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

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

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

QString QgsExplodeAlgorithm::outputName() const
{
return QObject::tr( "Exploded" );
}

QgsWkbTypes::Type QgsExplodeAlgorithm::outputWkbType( QgsWkbTypes::Type inputWkbType ) const
{
return QgsWkbTypes::singleType( inputWkbType );
}

QgsFeatureList QgsExplodeAlgorithm::processFeature( const QgsFeature &f, QgsProcessingContext &, QgsProcessingFeedback * )
{
if ( !f.hasGeometry() )
{
return QgsFeatureList() << f;
}
else
{
const std::vector<QgsGeometry> parts = extractAsParts( f.geometry() );
QgsFeature outputFeature;
QgsFeatureList features;
features.reserve( parts.size() );
for ( const QgsGeometry &part : parts )
{
outputFeature.setAttributes( f.attributes() );
outputFeature.setGeometry( part );
features << outputFeature;
}
return features;
}
}

std::vector<QgsGeometry> QgsExplodeAlgorithm::extractAsParts( const QgsGeometry &geometry ) const
{
if ( geometry.isMultipart() )
{
std::vector<QgsGeometry> parts;
const QgsGeometryCollection *collection = qgsgeometry_cast< const QgsGeometryCollection * >( geometry.constGet() );
for ( int part = 0; part < collection->numGeometries(); ++part )
{
std::vector<QgsGeometry> segments = curveAsSingleSegments( qgsgeometry_cast< const QgsCurve * >( collection->geometryN( part ) ) );
parts.reserve( parts.size() + segments.size() );
std::move( std::begin( segments ), std::end( segments ), std::back_inserter( parts ) );
}
return parts;
}
else
{
return curveAsSingleSegments( qgsgeometry_cast< const QgsCurve * >( geometry.constGet() ) );
}
}

std::vector<QgsGeometry> QgsExplodeAlgorithm::curveAsSingleSegments( const QgsCurve *curve, bool useCompoundCurves ) const
{
std::vector<QgsGeometry> parts;
switch ( QgsWkbTypes::flatType( curve->wkbType() ) )
{
case QgsWkbTypes::LineString:
{
const QgsLineString *line = qgsgeometry_cast< const QgsLineString * >( curve );
for ( int i = 0; i < line->numPoints() - 1; ++i )
{
QgsPoint ptA = line->pointN( i );
QgsPoint ptB = line->pointN( i + 1 );
std::unique_ptr< QgsLineString > ls = qgis::make_unique< QgsLineString >( QVector< QgsPoint >() << ptA << ptB );
if ( !useCompoundCurves )
{
parts.emplace_back( QgsGeometry( std::move( ls ) ) );
}
else
{
std::unique_ptr< QgsCompoundCurve > cc = qgis::make_unique< QgsCompoundCurve >();
cc->addCurve( ls.release() );
parts.emplace_back( QgsGeometry( std::move( cc ) ) );
}
}
break;
}

case QgsWkbTypes::CircularString:
{
const QgsCircularString *string = qgsgeometry_cast< const QgsCircularString * >( curve );
for ( int i = 0; i < string->numPoints() - 2; i += 2 )
{
QgsPoint ptA = string->pointN( i );
QgsPoint ptB = string->pointN( i + 1 );
QgsPoint ptC = string->pointN( i + 2 );
std::unique_ptr< QgsCircularString > cs = qgis::make_unique< QgsCircularString >();
cs->setPoints( QgsPointSequence() << ptA << ptB << ptC );
if ( !useCompoundCurves )
{
parts.emplace_back( QgsGeometry( std::move( cs ) ) );
}
else
{
std::unique_ptr< QgsCompoundCurve > cc = qgis::make_unique< QgsCompoundCurve >();
cc->addCurve( cs.release() );
parts.emplace_back( QgsGeometry( std::move( cc ) ) );
}
}
break;
}

case QgsWkbTypes::CompoundCurve:
{
const QgsCompoundCurve *compoundCurve = qgsgeometry_cast< QgsCompoundCurve * >( curve );
for ( int i = 0; i < compoundCurve->nCurves(); ++i )
{
std::vector<QgsGeometry> segments = curveAsSingleSegments( compoundCurve->curveAt( i ), true );
parts.reserve( parts.size() + segments.size() );
std::move( std::begin( segments ), std::end( segments ), std::back_inserter( parts ) );
}
break;
}

default:
break;

}
return parts;
}

///@endcond



0 comments on commit 1942854

Please sign in to comment.