Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6387a42
commit 280d2e5
Showing
4 changed files
with
283 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
/*************************************************************************** | ||
qgsalgorithmdpointstolines.cpp | ||
--------------------- | ||
begin : November 2020 | ||
copyright : (C) 2020 by Stefanos Natsis | ||
email : uclaros 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 "qgsalgorithmpointstolines.h" | ||
#include "qgsvectorlayer.h" | ||
|
||
///@cond PRIVATE | ||
|
||
QString QgsPointsToLinesAlgorithm::name() const | ||
{ | ||
return QStringLiteral( "pointstolines" ); | ||
} | ||
|
||
QString QgsPointsToLinesAlgorithm::displayName() const | ||
{ | ||
return QObject::tr( "Points to lines" ); | ||
} | ||
|
||
QString QgsPointsToLinesAlgorithm::shortHelpString() const | ||
{ | ||
return QObject::tr( "This algorithm takes a point layer and connects its features creating a new line layer.\n\n" | ||
"An attribute or expression may be specified to define the order the points should be connected. " | ||
"If no order expression is specified, the fid is used.\n\n" | ||
"A natural sort can be used when sorting by a string attribute " | ||
"or expression (ie. place 'a9' before 'a10').\n\n" | ||
"An attribute can be selected to group points having the same value into the same resulting line." ); | ||
} | ||
|
||
QStringList QgsPointsToLinesAlgorithm::tags() const | ||
{ | ||
return QObject::tr( "create,lines,points,connect,convert,join" ).split( ',' ); | ||
} | ||
|
||
QString QgsPointsToLinesAlgorithm::group() const | ||
{ | ||
return QObject::tr( "Vector geometry" ); | ||
} | ||
|
||
QString QgsPointsToLinesAlgorithm::groupId() const | ||
{ | ||
return QStringLiteral( "vectorgeometry" ); | ||
} | ||
|
||
void QgsPointsToLinesAlgorithm::initAlgorithm( const QVariantMap & ) | ||
{ | ||
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), | ||
QObject::tr( "Input layer" ), QList< int >() << QgsProcessing::TypeVectorPoint ) ); | ||
addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "CLOSE_LINES" ), | ||
QObject::tr( "Create closed lines" ), false, true ) ); | ||
addParameter( new QgsProcessingParameterExpression( QStringLiteral( "ORDER_EXPRESSION" ), | ||
QObject::tr( "Order expression" ), QVariant(), QStringLiteral( "INPUT" ), true ) ); | ||
addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "NATURAL_SORT" ), | ||
QObject::tr( "Sort text containing numbers naturally" ), false, true ) ); | ||
addParameter( new QgsProcessingParameterField( QStringLiteral( "GROUP_FIELD" ), | ||
QObject::tr( "Line group field" ), QVariant(), QStringLiteral( "INPUT" ), QgsProcessingParameterField::Any, false, true ) ); | ||
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), | ||
QObject::tr( "Lines" ), QgsProcessing::TypeVectorLine ) ); | ||
addOutput( new QgsProcessingOutputNumber( QStringLiteral( "NUM_LINES" ), QObject::tr( "Number of lines" ) ) ); | ||
} | ||
|
||
QgsPointsToLinesAlgorithm *QgsPointsToLinesAlgorithm::createInstance() const | ||
{ | ||
return new QgsPointsToLinesAlgorithm(); | ||
} | ||
|
||
QVariantMap QgsPointsToLinesAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) | ||
{ | ||
std::unique_ptr< QgsProcessingFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); | ||
if ( !source ) | ||
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); | ||
|
||
const bool closeLines = parameterAsBool( parameters, QStringLiteral( "CLOSE_LINES" ), context ); | ||
|
||
QString orderExpression = parameterAsString( parameters, QStringLiteral( "ORDER_EXPRESSION" ), context ); | ||
// If no order expression is given, default to the fid | ||
if ( orderExpression.isEmpty() ) | ||
orderExpression = QString( "$id" ); | ||
QgsExpressionContext expressionContext = createExpressionContext( parameters, context, source.get() ); | ||
QgsExpression expression = QgsExpression( orderExpression ); | ||
if ( expression.hasParserError() ) | ||
throw QgsProcessingException( expression.parserErrorString() ); | ||
|
||
QStringList requiredFields = QStringList( expression.referencedColumns().values() ); | ||
expression.prepare( &expressionContext ); | ||
|
||
QgsFields outputFields = QgsFields(); | ||
const QString groupField = parameterAsString( parameters, QStringLiteral( "GROUP_FIELD" ), context ); | ||
if ( ! groupField.isEmpty() ) | ||
{ | ||
const QgsField field = source->fields().field( groupField ); | ||
requiredFields.append( field.name() ); | ||
outputFields.append( field ); | ||
} | ||
|
||
const bool naturalSort = parameterAsBool( parameters, QStringLiteral( "NATURAL_SORT" ), context ); | ||
QCollator collator; | ||
collator.setNumericMode( true ); | ||
|
||
QgsWkbTypes::Type wkbType = QgsWkbTypes::LineString; | ||
if ( QgsWkbTypes::hasM( source->wkbType() ) ) | ||
wkbType = QgsWkbTypes::addM( wkbType ); | ||
if ( QgsWkbTypes::hasZ( source->wkbType() ) ) | ||
wkbType = QgsWkbTypes::addZ( wkbType ); | ||
|
||
QString dest; | ||
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, outputFields, wkbType, source->sourceCrs() ) ); | ||
if ( !sink ) | ||
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); | ||
|
||
// Store the points in a hash with the group identifier as the key | ||
QHash< QVariant, QVector< QPair< QVariant, const QgsPoint * > > > allPoints; | ||
|
||
QgsFeatureRequest request = QgsFeatureRequest().setSubsetOfAttributes( requiredFields, source->fields() ); | ||
QgsFeatureIterator fit = source->getFeatures( request, QgsProcessingFeatureSource::FlagSkipGeometryValidityChecks ); | ||
QgsFeature f; | ||
const double totalPoints = source->featureCount() > 0 ? 100.0 / source->featureCount() : 0; | ||
long currentPoint = 0; | ||
feedback->setProgressText( QObject::tr( "Loading points…" ) ); | ||
while ( fit.nextFeature( f ) ) | ||
{ | ||
if ( feedback->isCanceled() ) | ||
{ | ||
break; | ||
} | ||
feedback->setProgress( currentPoint * totalPoints ); | ||
|
||
if ( f.hasGeometry() && ! f.geometry().isNull() ) | ||
{ | ||
expressionContext.setFeature( f ); | ||
const QVariant orderValue = expression.evaluate( &expressionContext ); | ||
|
||
// If no group field is specified then an invalid QVariant is returned, we're ok with that as our only group | ||
const QVariant groupValue = f.attribute( groupField ); | ||
if ( ! allPoints.keys().contains( groupValue ) ) | ||
allPoints[ groupValue ] = QVector< QPair< QVariant, const QgsPoint * > >(); | ||
const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( f.geometry().constGet()->clone() ); | ||
allPoints[ groupValue ] << QPair< QVariant, const QgsPoint *>( orderValue, point ); | ||
} | ||
++currentPoint; | ||
} | ||
|
||
int lineCount = 0; | ||
currentPoint = 0; | ||
QHashIterator< QVariant, QVector< QPair< QVariant, const QgsPoint * > > > hit( allPoints ); | ||
feedback->setProgressText( QObject::tr( "Creating lines…" ) ); | ||
while ( hit.hasNext() ) | ||
{ | ||
hit.next(); | ||
if ( feedback->isCanceled() ) | ||
{ | ||
break; | ||
} | ||
auto pairs = hit.value(); | ||
|
||
if ( naturalSort ) | ||
{ | ||
std::sort( pairs.begin(), | ||
pairs.end(), | ||
[&collator]( const QPair< const QVariant, const QgsPoint * > &pair1, | ||
const QPair< const QVariant, const QgsPoint * > &pair2 ) | ||
{ | ||
return collator.compare( pair1.first.toString(), pair2.first.toString() ) < 0; | ||
} ); | ||
} | ||
else | ||
{ | ||
std::sort( pairs.begin(), | ||
pairs.end(), | ||
[]( const QPair< const QVariant, const QgsPoint * > &pair1, | ||
const QPair< const QVariant, const QgsPoint * > &pair2 ) | ||
{ | ||
return pair1.first < pair2.first; | ||
} ); | ||
} | ||
|
||
|
||
QVector<QgsPoint> linePoints; | ||
for ( const auto pair : pairs ) | ||
{ | ||
if ( feedback->isCanceled() ) | ||
{ | ||
break; | ||
} | ||
feedback->setProgress( currentPoint * totalPoints ); | ||
linePoints.append( *pair.second ); | ||
++currentPoint; | ||
} | ||
if ( linePoints.size() < 2 ) | ||
{ | ||
feedback->pushInfo( QObject::tr( "Skipping line with group %1 : insufficient vertices" ).arg( hit.key().toString() ) ); | ||
continue; | ||
} | ||
if ( closeLines && linePoints.size() > 2 && linePoints.first() != linePoints.last() ) | ||
linePoints.append( linePoints.first() ); | ||
|
||
QgsFeature outputFeature; | ||
QgsAttributes attrs; | ||
attrs.append( hit.key() ); | ||
outputFeature.setGeometry( QgsGeometry::fromPolyline( linePoints ) ); | ||
outputFeature.setAttributes( attrs ); | ||
sink->addFeature( outputFeature, QgsFeatureSink::FastInsert ); | ||
++lineCount; | ||
} | ||
|
||
|
||
QVariantMap outputs; | ||
outputs.insert( QStringLiteral( "OUTPUT" ), dest ); | ||
outputs.insert( QStringLiteral( "NUM_LINESS" ), lineCount ); | ||
return outputs; | ||
} | ||
|
||
///@endcond |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/*************************************************************************** | ||
qgsalgorithmpointstolines.h | ||
--------------------- | ||
begin : November 2020 | ||
copyright : (C) 2020 by Stefanos Natsis | ||
email : uclaros 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 QGSALGORITHMPOINTSTOLINES_H | ||
#define QGSALGORITHMPOINTSTOLINES_H | ||
|
||
#define SIP_NO_FILE | ||
|
||
#include "qgis_sip.h" | ||
#include "qgsprocessingalgorithm.h" | ||
|
||
///@cond PRIVATE | ||
|
||
/** | ||
* Native points to lines algorithm. | ||
*/ | ||
class QgsPointsToLinesAlgorithm : public QgsProcessingAlgorithm | ||
{ | ||
|
||
public: | ||
|
||
QgsPointsToLinesAlgorithm() = default; | ||
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override; | ||
QString name() const override; | ||
QString displayName() const override; | ||
QStringList tags() const override; | ||
QString group() const override; | ||
QString groupId() const override; | ||
QString shortHelpString() const override; | ||
QgsPointsToLinesAlgorithm *createInstance() const override SIP_FACTORY; | ||
|
||
protected: | ||
|
||
QVariantMap processAlgorithm( const QVariantMap ¶meters, | ||
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override; | ||
}; | ||
|
||
///@endcond PRIVATE | ||
|
||
#endif // QGSALGORITHMPOINTSTOLINES_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters