Skip to content
Permalink
Browse files

Merge pull request #5600 from Gustry/sortby

add order by expression algorithm
  • Loading branch information
nyalldawson committed Dec 2, 2017
2 parents b8b8c1a + 133051a commit 6b23e1f299cf9050e772f67dbd165be793a17cbd
@@ -25,7 +25,7 @@ How To
To add a new test please follow these steps:

1. **Run the algorithm** you want to test in QGIS from the processing toolbox. If the
result is a vector layer prefer GML as output for its support of mixed
result is a vector layer prefer GML, with its XSD, as output for its support of mixed
geometry types and good readability. Redirect output to
`python/plugins/processing/tests/testdata/expected`. For input layers prefer to use what's already there in the folder `testdata`. If you need extra data, put it into `testdata/custom`.

@@ -131,6 +131,8 @@ It couldn't be more trivial
type: vector
```

Add the expected GML and XSD in the folder.

#### Vector with tolerance

Sometimes different platforms create slightly different results which are
@@ -191,3 +193,10 @@ OUTPUT:
- 'Geometry: Line String'
- 'Feature Count: 6'
```

Running tests locally
------------------
```bash
ctest -V -R ProcessingQgisAlgorithmsTest
```
or one of the following value listed in the [CMakelists.txt](https://github.com/qgis/QGIS/blob/master/python/plugins/processing/tests/CMakeLists.txt)
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ order_by_expression.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>-1</gml:X><gml:Y>-3</gml:Y></gml:coord>
<gml:coord><gml:X>10</gml:X><gml:Y>6</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:order_by_expression fid="polys.1">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5,5 6,4 4,4 5,5</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>Aaaaa</ogr:name>
<ogr:intval>-33</ogr:intval>
<ogr:floatval>0</ogr:floatval>
</ogr:order_by_expression>
</gml:featureMember>
<gml:featureMember>
<ogr:order_by_expression fid="polys.3">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>6,1 10,1 10,-3 6,-3 6,1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs><gml:innerBoundaryIs><gml:LinearRing><gml:coordinates>7,0 7,-2 9,-2 9,0 7,0</gml:coordinates></gml:LinearRing></gml:innerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>ASDF</ogr:name>
<ogr:intval>0</ogr:intval>
<ogr:floatval xsi:nil="true"/>
</ogr:order_by_expression>
</gml:featureMember>
<gml:featureMember>
<ogr:order_by_expression fid="polys.5">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,2 6,1 6,-3 2,-1 2,2 3,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>elim</ogr:name>
<ogr:intval>2</ogr:intval>
<ogr:floatval>3.33</ogr:floatval>
</ogr:order_by_expression>
</gml:featureMember>
<gml:featureMember>
<ogr:order_by_expression fid="polys.0">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-1,-1 -1,3 3,3 3,2 2,2 2,-1 -1,-1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>aaaaa</ogr:name>
<ogr:intval>33</ogr:intval>
<ogr:floatval>44.123456</ogr:floatval>
</ogr:order_by_expression>
</gml:featureMember>
<gml:featureMember>
<ogr:order_by_expression fid="polys.4">
<ogr:name xsi:nil="true"/>
<ogr:intval>120</ogr:intval>
<ogr:floatval>-100291.43213</ogr:floatval>
</ogr:order_by_expression>
</gml:featureMember>
<gml:featureMember>
<ogr:order_by_expression fid="polys.2">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2,5 2,6 3,6 3,5 2,5</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>bbaaa</ogr:name>
<ogr:intval xsi:nil="true"/>
<ogr:floatval>0.123</ogr:floatval>
</ogr:order_by_expression>
</gml:featureMember>
</ogr:FeatureCollection>
@@ -0,0 +1,43 @@
<?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="order_by_expression" type="ogr:order_by_expression_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="order_by_expression_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="geometryProperty" type="gml:PolygonPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
<xs:element name="name" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="5"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="intval" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:totalDigits value="10"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="floatval" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
@@ -3411,6 +3411,24 @@ tests:
name: expected/add_field.gml
type: vector

- algorithm: native:orderbyexpression
name: Order by expression
params:
ASCENDING: true
EXPRESSION: intval
INPUT:
name: polys.gml
type: vector
NULLS_FIRST: false
results:
OUTPUT:
name: expected/order_by_expression.gml
type: vector
compare:
fields:
__all__:
precision: 4

- algorithm: qgis:randompointsinextent
name: Random point in extent, don't check result
params:
@@ -46,6 +46,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmmergelines.cpp
processing/qgsalgorithmminimumenclosingcircle.cpp
processing/qgsalgorithmmultiparttosinglepart.cpp
processing/qgsalgorithmorderbyexpression.cpp
processing/qgsalgorithmorientedminimumboundingbox.cpp
processing/qgsalgorithmpackage.cpp
processing/qgsalgorithmpromotetomultipart.cpp
@@ -0,0 +1,106 @@
/***************************************************************************
qgsalgorithmorderbyexpression.h
---------------------
begin : November 2017
copyright : (C) 2017 by Etienne Trimaille
email : etienne dot trimaille 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 "qgsalgorithmorderbyexpression.h"
#include "qgsfeaturerequest.h"


///@cond PRIVATE

QString QgsOrderByExpressionAlgorithm::name() const
{
return QStringLiteral( "orderbyexpression" );
}

QString QgsOrderByExpressionAlgorithm::displayName() const
{
return QObject::tr( "Order by expression" );
}

QStringList QgsOrderByExpressionAlgorithm::tags() const
{
return QObject::tr( "orderby,sort,expression,field" ).split( ',' );
}

QString QgsOrderByExpressionAlgorithm::group() const
{
return QObject::tr( "Vector general" );
}

void QgsOrderByExpressionAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
addParameter( new QgsProcessingParameterExpression( QStringLiteral( "EXPRESSION" ), QObject::tr( "Expression" ), QVariant(), QStringLiteral( "INPUT" ) ) );
addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "ASCENDING" ), QObject::tr( "Ascending" ), true ) );
addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "NULLS_FIRST" ), QObject::tr( "Nulls first" ), false ) );

addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Output layer" ) ) );
}

QString QgsOrderByExpressionAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm sorts a vector layer according to an expression. Be careful, it might not work as expected with some providers, the order might not be kept every time." );
}

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

QVariantMap QgsOrderByExpressionAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !source )
return QVariantMap();

QString expressionString = parameterAsExpression( parameters, QStringLiteral( "EXPRESSION" ), context );

bool ascending = parameterAsBool( parameters, QStringLiteral( "ASCENDING" ), context );
bool nullsFirst = parameterAsBool( parameters, QStringLiteral( "NULLS_FIRST" ), context );

QString sinkId;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, sinkId, source->fields(), source->wkbType(), source->sourceCrs() ) );
if ( !sink )
return QVariantMap();

long count = source->featureCount();
double step = count > 0 ? 100.0 / count : 1;
int current = 0;

QgsFeatureRequest request;
request.addOrderBy( expressionString, ascending, nullsFirst );

QgsFeature inFeature;
QgsFeatureIterator features = source->getFeatures( request );
while ( features.nextFeature( inFeature ) )
{
if ( feedback->isCanceled() )
{
break;
}
sink->addFeature( inFeature );
feedback->setProgress( current * step );
current++;
}

QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), sinkId );
return outputs;
}

///@endcond
@@ -0,0 +1,53 @@
/***************************************************************************
qgsalgorithmorderbyexpression.h
---------------------
begin : November 2017
copyright : (C) 2017 by Etienne Trimaille
email : etienne dot trimaille 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 QGSALGORITHMORDERBYEXPRESSION_H
#define QGSALGORITHMORDERBYEXPRESSION_H

#define SIP_NO_FILE

#include "qgis.h"
#include "qgsprocessingalgorithm.h"

///@cond PRIVATE

/**
* Native order by expression algorithm.
*/
class QgsOrderByExpressionAlgorithm : public QgsProcessingAlgorithm
{
public:
QgsOrderByExpressionAlgorithm() = default;
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
QString name() const override;
QString displayName() const override;
virtual QStringList tags() const override;
QString group() const override;
QString shortHelpString() const override;
QgsOrderByExpressionAlgorithm *createInstance() const override SIP_FACTORY;

protected:

virtual QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
};

///@endcond PRIVATE

#endif // QGSALGORITHMORDERBYEXPRESSION_H
@@ -43,6 +43,7 @@
#include "qgsalgorithmmergelines.h"
#include "qgsalgorithmminimumenclosingcircle.h"
#include "qgsalgorithmmultiparttosinglepart.h"
#include "qgsalgorithmorderbyexpression.h"
#include "qgsalgorithmorientedminimumboundingbox.h"
#include "qgsalgorithmpackage.h"
#include "qgsalgorithmpromotetomultipart.h"
@@ -122,6 +123,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsMergeLinesAlgorithm() );
addAlgorithm( new QgsMinimumEnclosingCircleAlgorithm() );
addAlgorithm( new QgsMultipartToSinglepartAlgorithm() );
addAlgorithm( new QgsOrderByExpressionAlgorithm() );
addAlgorithm( new QgsOrientedMinimumBoundingBoxAlgorithm() );
addAlgorithm( new QgsPackageAlgorithm() );
addAlgorithm( new QgsPromoteToMultipartAlgorithm() );

0 comments on commit 6b23e1f

Please sign in to comment.
You can’t perform that action at this time.