Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature][processing] Initial port of Create Grid algorithm to C++ #32070

Merged
merged 17 commits into from Oct 9, 2019

Conversation

root676
Copy link
Contributor

@root676 root676 commented Sep 30, 2019

Description

This PR proposes the port of the Create Grid algorithm from Python to C++. No other functionality has been added to the algorithm, it is a 1:1 port of the original Python implementation. An overall algorithm speedup of multiple orders of magnitude can be achieved by this port.

Reviews and guidance (especially with prepare scripts, etc.) are very welcome as this is my first PR to the QGIS project.

Checklist

  • Original Python algorithm has been disabled.
  • Original Python algorithm has been dropped.
  • Commit messages are descriptive and explain the rationale for changes
  • Commits which fix bugs include Fixes #11111 at the bottom of the commit message
  • I have read the QGIS Coding Standards and this PR complies with them
  • New unit tests have been added for core changes
  • I have run the scripts/prepare-commit.sh script before each commit
  • I have evaluated whether it is appropriate for this PR to be backported, backport requests are left as label or comment

This commit includes the source files for the ported algorithm,
integration into cmake and disabling (not yet dropping) the python algorithm.
Copy link
Collaborator

@nyalldawson nyalldawson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great!

A couple of formatting changes needed, and I've made some extra suggestions on potential improvements we could add. Note that these aren't directly related to your c++ port, but would be nice to address while we're aiming for a super-fast algorithm!

@@ -180,7 +179,7 @@ def getAlgs(self):
FindProjection(),
GeometryByExpression(),
GeometryConvert(),
Grid(),
#Grid(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can totally remove this line

src/analysis/processing/qgsalgorithmgrid.cpp Outdated Show resolved Hide resolved
addParameter( new QgsProcessingParameterExtent(QStringLiteral("EXTENT"), QObject::tr( "Grid extent" )));

//add Distance Parameter for horizontal and vertical spacing
addParameter( new QgsProcessingParameterDistance( QStringLiteral( "HSPACING"), QObject::tr( "Horizontal Spacing" ), 1, QString( "CRS" ), false, 0, 1000000000.0));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
addParameter( new QgsProcessingParameterDistance( QStringLiteral( "HSPACING"), QObject::tr( "Horizontal Spacing" ), 1, QString( "CRS" ), false, 0, 1000000000.0));
addParameter( new QgsProcessingParameterDistance( QStringLiteral( "HSPACING"), QObject::tr( "Horizontal spacing" ), 1, QStringLiteral( "CRS" ), false, 0, 1000000000.0));


//add Distance Parameter for horizontal and vertical spacing
addParameter( new QgsProcessingParameterDistance( QStringLiteral( "HSPACING"), QObject::tr( "Horizontal Spacing" ), 1, QString( "CRS" ), false, 0, 1000000000.0));
addParameter( new QgsProcessingParameterDistance( QStringLiteral( "VSPACING"), QObject::tr( "Vertical Spacing" ), 1, QString( "CRS" ), false, 0, 1000000000.0));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
addParameter( new QgsProcessingParameterDistance( QStringLiteral( "VSPACING"), QObject::tr( "Vertical Spacing" ), 1, QString( "CRS" ), false, 0, 1000000000.0));
addParameter( new QgsProcessingParameterDistance( QStringLiteral( "VSPACING"), QObject::tr( "Vertical spacing" ), 1, QStringLiteral( "CRS" ), false, 0, 1000000000.0));

addParameter( new QgsProcessingParameterDistance( QStringLiteral( "VSPACING"), QObject::tr( "Vertical Spacing" ), 1, QString( "CRS" ), false, 0, 1000000000.0));

//add Distance Parameter for horizontal and vertical overlay
addParameter( new QgsProcessingParameterDistance( QStringLiteral( "HOVERLAY"), QObject::tr( "Horizontal Overlay" ), 0, QString( "CRS" ), false, 0, 1000000000.0));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
addParameter( new QgsProcessingParameterDistance( QStringLiteral( "HOVERLAY"), QObject::tr( "Horizontal Overlay" ), 0, QString( "CRS" ), false, 0, 1000000000.0));
addParameter( new QgsProcessingParameterDistance( QStringLiteral( "HOVERLAY"), QObject::tr( "Horizontal overlay" ), 0, QStringLiteral( "CRS" ), false, 0, 1000000000.0));

{
for(int row = 0; row < rows; row++)
{
double x = mGridExtent.xMinimum() + (col * mHSpacing - col * mHOverlay);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could potentially be moved up into the outer loop, as we don't need to calculate it for every row.

double x = mGridExtent.xMinimum() + (col * mHSpacing - col * mHOverlay);
double y = mGridExtent.yMaximum() - (row * mVSpacing - row * mVOverlay);

f.setGeometry(QgsGeometry().fromPointXY(QgsPoint( x, y )));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might get a little speed boost by simplifying this to:

f.setGeometry( QgsGeometry( new QgsPoint( x, y ) ) );

id++;
cnt++;

if(std::fmod(static_cast<double>(cnt),cnt_log) == 0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another potential speed boost (optional!):

Instead of calculating the mod for every feature, we could have an int variable of "int lastProgress". Then here this would become something like:

int thisProgress = cnt / cellcnt;
if ( thisProgress != lastProgress )
{
     lastProgress=  thisProgress;
     feedback->setProgress( lastProgress );
}

{
QgsFeature f = QgsFeature();

QVector<double> hSpace;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much more efficient c++ would be to use a fixed array size here, e.g.

double hSpace[2];
if( mHOverlay > 0)
{
    hSpace[0] = mHSpacing - mHOverlay;
    hSpace[1] = mHOverlay;
 }

etc

QVector<QgsPoint> lineNodes = QVector<QgsPoint>() << pt1 << pt2;

QgsPolyline line = QgsPolyline(lineNodes);
f.setGeometry(QgsGeometry::fromPolyline(line));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use an optimised line string constructor here (for raw speeeed!):

f.setGeometry( QgsGeometry( new QgsLineString( pt1, pt2 ) ) );

@nyalldawson
Copy link
Collaborator

Re the prepare-commit script -- you need to run this before you make the commit, and the files already need to be checked in to git. So there's two ways to do this:

  1. manually add the files to git, run prepare-commit, and then commit
    or
  2. (better) add the prepare-commit script as a git pre commit hook.

In order to update the formatting post-commit, try running scripts/astyle-all.sh which will apply the formatting script to all files in the repo. After this, you'll need to run scripts/remove_temporary_files.sh to cleanup the mess astyle-all leaves behind.

Ping me if you run into difficulties here!

Overall, a fantastic contribution -- great stuff!!

@nyalldawson nyalldawson added Optimization I feel the need... the need for speed! Processing Relating to QGIS Processing framework or individual Processing algorithms labels Sep 30, 2019

bool QgsGridAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
{
//retrieve grid type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need all these comments for obvious things?

@alexbruy
Copy link
Contributor

alexbruy commented Oct 2, 2019

@root676 there are some unrelated commits in your branch.

@root676
Copy link
Contributor Author

root676 commented Oct 2, 2019

@alexbruy thank you for the review! Yes - I have had a little trouble with the git branch, I have it now fixed locally and will update the PR as soon as I have the suggested changes implemented.

@root676
Copy link
Contributor Author

root676 commented Oct 3, 2019

Hi! I have fine tuned the code according to the suggestions. Should I also delete the original Python algorithm file from the repository?

@nyalldawson
Copy link
Collaborator

@root676 #32182 will fix the overly sensitive attribute value check which is causing the travis test failure here

@nyalldawson
Copy link
Collaborator

Should I also delete the original Python algorithm file from the repository?

Yes please!

The CRS of the output layer must be defined. The grid extent and the spacing values must be expressed in the coordinates and units of this CRS.

The top-left point (minX, maxY) is used as the reference point. That means that, at that point, an element is guaranteed to be placed. Unless the width and height of the selected extent is a multiple of the selected spacing, that is not true for the other points that define that extent.
This algorithm creates a vector layer with a grid covering a given extent. Elements in the grid can be points, lines or polygons.The size and/or placement of each element in the grid is defined using a horizontal and vertical spacing. The CRS of the output layer must be defined. The grid extent and the spacing values must be expressed in the coordinates and units of this CRS. The top-left point (minX, maxY) is used as the reference point. That means that, at that point, an element is guaranteed to be placed. Unless the width and height of the selected extent is a multiple of the selected spacing, that is not true for the other points that define that extent.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be totally removed -- it won't be used anymore

@nyalldawson nyalldawson closed this Oct 9, 2019
@nyalldawson nyalldawson reopened this Oct 9, 2019
@nyalldawson nyalldawson added the Squash! Remember to squash this PR, instead of merging or rebasing label Oct 9, 2019
@nyalldawson
Copy link
Collaborator

Ok, travis is happy!

@nyalldawson nyalldawson merged commit 9e9ade3 into qgis:master Oct 9, 2019
@nyalldawson
Copy link
Collaborator

Thanks for your hard work here @root676 !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Optimization I feel the need... the need for speed! Processing Relating to QGIS Processing framework or individual Processing algorithms Squash! Remember to squash this PR, instead of merging or rebasing
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants