Skip to content

Commit

Permalink
Move minimum layer extent calculation to c++
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Jun 5, 2017
1 parent 189f804 commit ba03f1a
Show file tree
Hide file tree
Showing 17 changed files with 113 additions and 46 deletions.
6 changes: 6 additions & 0 deletions python/core/processing/qgsprocessingutils.sip
Expand Up @@ -152,6 +152,12 @@ class QgsProcessingUtils
available in Python bindings as createFeatureSink()
%End

static QgsRectangle combineLayerExtents( const QList< QgsMapLayer *> layers, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() );
%Docstring
Combines the extent of several map ``layers``. If specified, the target ``crs``
will be used to transform the layer's extent to the desired output reference system.
:rtype: QgsRectangle
%End

};

Expand Down
3 changes: 3 additions & 0 deletions python/plugins/processing/algs/gdal/ClipByExtent.py
Expand Up @@ -88,6 +88,9 @@ def getConsoleCommands(self, parameters):
noData = self.getParameterValue(self.NO_DATA)
opts = self.getParameterValue(self.OPTIONS)
projwin = self.getParameterValue(self.PROJWIN)
layer = self.getParameterValue(self.INPUT)
if not projwin:
projwin = QgsProcessingUtils.combineLayerExtents([layer])

if noData is not None:
noData = str(noData)
Expand Down
2 changes: 2 additions & 0 deletions python/plugins/processing/algs/gdal/ogr2ogrclipextent.py
Expand Up @@ -69,6 +69,8 @@ def getConsoleCommands(self, parameters):
inLayer = self.getParameterValue(self.INPUT_LAYER)
ogrLayer = ogrConnectionString(inLayer)[1:-1]
clipExtent = self.getParameterValue(self.CLIP_EXTENT)
if not clipExtent:
clipExtent = QgsProcessingUtils.combineLayerExtents([inLayer])

output = self.getOutputFromName(self.OUTPUT_LAYER)
outFile = output.value
Expand Down
2 changes: 2 additions & 0 deletions python/plugins/processing/algs/gdal/ogr2ogrtopostgis.py
Expand Up @@ -203,6 +203,8 @@ def getConsoleCommands(self, parameters):
simplify = str(self.getParameterValue(self.SIMPLIFY))
segmentize = str(self.getParameterValue(self.SEGMENTIZE))
spat = self.getParameterValue(self.SPAT)
if not spat:
spat = QgsProcessingUtils.combineLayerExtents([inLayer])
clip = self.getParameterValue(self.CLIP)
where = str(self.getParameterValue(self.WHERE))
wherestring = '-where "' + where + '"'
Expand Down
2 changes: 2 additions & 0 deletions python/plugins/processing/algs/gdal/ogr2ogrtopostgislist.py
Expand Up @@ -205,6 +205,8 @@ def getConsoleCommands(self, parameters):
simplify = self.getParameterValue(self.SIMPLIFY)
segmentize = self.getParameterValue(self.SEGMENTIZE)
spat = self.getParameterValue(self.SPAT)
if not spat:
spat = QgsProcessingUtils.combineLayerExtents([inLayer])
clip = self.getParameterValue(self.CLIP)
where = self.getParameterValue(self.WHERE)
gt = self.getParameterValue(self.GT)
Expand Down
2 changes: 2 additions & 0 deletions python/plugins/processing/algs/gdal/rasterize.py
Expand Up @@ -106,6 +106,8 @@ def getConsoleCommands(self, parameters):
inLayer = self.getParameterValue(self.INPUT)
noData = self.getParameterValue(self.NO_DATA)
rastext = str(self.getParameterValue(self.RAST_EXT))
if not rastext:
rastext = QgsProcessingUtils.combineLayerExtents([inLayer])
opts = self.getParameterValue(self.OPTIONS)
out = self.getOutputValue(self.OUTPUT)

Expand Down
3 changes: 3 additions & 0 deletions python/plugins/processing/algs/gdal/translate.py
Expand Up @@ -104,12 +104,15 @@ def group(self):
return self.tr('Raster conversion')

def getConsoleCommands(self, parameters):
inLayer = self.getParameterValue(self.INPUT)
out = self.getOutputValue(translate.OUTPUT)
outsize = str(self.getParameterValue(self.OUTSIZE))
outsizePerc = str(self.getParameterValue(self.OUTSIZE_PERC))
noData = self.getParameterValue(self.NO_DATA)
expand = self.getParameterFromName(self.EXPAND).options[self.getParameterValue(self.EXPAND)][1]
projwin = str(self.getParameterValue(self.PROJWIN))
if not projwin:
projwin = QgsProcessingUtils.combineLayerExtents([inLayer])
crsId = self.getParameterValue(self.SRS)
sds = self.getParameterValue(self.SDS)
opts = self.getParameterValue(self.OPTIONS)
Expand Down
3 changes: 3 additions & 0 deletions python/plugins/processing/algs/gdal/warp.py
Expand Up @@ -122,10 +122,13 @@ def group(self):
return self.tr('Raster projections')

def getConsoleCommands(self, parameters):
inLayer = self.getParameterValue(self.INPUT)
srccrs = self.getParameterValue(self.SOURCE_SRS)
dstcrs = self.getParameterValue(self.DEST_SRS)
useRasterExtent = self.getParameterValue(self.USE_RASTER_EXTENT)
rasterExtent = self.getParameterValue(self.RASTER_EXTENT)
if not rasterExtent:
rasterExtent = QgsProcessingUtils.combineLayerExtents([inLayer])
extentCrs = self.getParameterValue(self.EXTENT_CRS)
opts = self.getParameterValue(self.OPTIONS)
noData = self.getParameterValue(self.NO_DATA)
Expand Down
3 changes: 3 additions & 0 deletions python/plugins/processing/algs/grass7/Grass7Algorithm.py
Expand Up @@ -371,6 +371,9 @@ def processInputs(self):

region = \
str(self.getParameterValue(self.GRASS_REGION_EXTENT_PARAMETER))
if not region:
region = QgsProcessingUtils.combineLayerExtents(layers)

regionCoords = region.split(',')
command = 'g.region'
command += ' n=' + str(regionCoords[3])
Expand Down
3 changes: 3 additions & 0 deletions python/plugins/processing/algs/grass7/nviz7.py
Expand Up @@ -102,6 +102,9 @@ def processAlgorithm(self, parameters, context, feedback):

region = \
str(self.getParameterValue(self.GRASS_REGION_EXTENT_PARAMETER))
if not region:
region = QgsProcessingUtils.combineLayerExtents(layers)

regionCoords = region.split(',')
command = 'g.region '
command += 'n=' + str(regionCoords[3])
Expand Down
2 changes: 2 additions & 0 deletions python/plugins/processing/algs/qgis/FindProjection.py
Expand Up @@ -87,6 +87,8 @@ def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context)

extent = self.getParameterValue(self.TARGET_AREA).split(',')
if not extent:
extent = QgsProcessingUtils.combineLayerExtents([layer])
target_crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.TARGET_AREA_CRS))

target_geom = QgsGeometry.fromRect(QgsRectangle(float(extent[0]), float(extent[2]),
Expand Down
2 changes: 2 additions & 0 deletions python/plugins/processing/algs/qgis/RasterCalculator.py
Expand Up @@ -127,6 +127,8 @@ def processAlgorithm(self, parameters, context, feedback):

output = self.getOutputValue(self.OUTPUT)
extentValue = self.getParameterValue(self.EXTENT)
if not extentValue:
extentValue = QgsProcessingUtils.combineLayerExtents(layersValue)

if extentValue:
extent = extentValue.split(',')
Expand Down
4 changes: 4 additions & 0 deletions python/plugins/processing/algs/saga/SagaAlgorithm.py
Expand Up @@ -204,6 +204,10 @@ def processAlgorithm(self, parameters, context, feedback):
raise GeoAlgorithmExecutionException(
self.tr('Unsupported file format'))

# TODO - set minimum extent
if not extent:
extent = QgsProcessingUtils.combineLayerExtents([layer])

# 2: Set parameters and outputs
command = self.undecoratedGroup + ' "' + self.cmdname + '"'
command += ' ' + ' '.join(self.hardcodedStrings)
Expand Down
47 changes: 1 addition & 46 deletions python/plugins/processing/core/parameters.py
Expand Up @@ -42,6 +42,7 @@
from qgis.core import (QgsRasterLayer, QgsVectorLayer, QgsMapLayer, QgsCoordinateReferenceSystem,
QgsExpressionContext, QgsExpressionContextUtils, QgsExpression, QgsExpressionContextScope,
QgsProject,
QgsRectangle,
QgsVectorFileWriter,
QgsProcessingParameterDefinition)

Expand Down Expand Up @@ -356,52 +357,6 @@ def fromScriptCode(self, line):
default = definition.strip()[len('extent') + 1:] or None
return ParameterExtent(name, descName, default, isOptional)

def evaluate(self, alg):
if self.flags() & QgsProcessingParameterDefinition.FlagOptional and not bool(self.value):
self.value = self.getMinCoveringExtent(alg)

def getMinCoveringExtent(self, alg):
first = True
found = False
context = dataobjects.createContext()
for param in alg.parameters:
if param.value:
if isinstance(param, (ParameterRaster, ParameterVector)):
if isinstance(param.value, (QgsRasterLayer,
QgsVectorLayer)):
layer = param.value
else:
layer = QgsProcessingUtils.mapLayerFromString(param.value, context)
if layer:
found = True
self.addToRegion(layer, first)
first = False
elif isinstance(param, ParameterMultipleInput):
layers = param.value.split(';')
for layername in layers:
layer = QgsProcessingUtils.mapLayerFromString(layername, context)
if layer:
found = True
self.addToRegion(layer, first)
first = False
if found:
return '{},{},{},{}'.format(
self.xmin, self.xmax, self.ymin, self.ymax)
else:
return None

def addToRegion(self, layer, first):
if first:
self.xmin = layer.extent().xMinimum()
self.xmax = layer.extent().xMaximum()
self.ymin = layer.extent().yMinimum()
self.ymax = layer.extent().yMaximum()
else:
self.xmin = min(self.xmin, layer.extent().xMinimum())
self.xmax = max(self.xmax, layer.extent().xMaximum())
self.ymin = min(self.ymin, layer.extent().yMinimum())
self.ymax = max(self.ymax, layer.extent().yMaximum())


class ParameterPoint(Parameter):

Expand Down
35 changes: 35 additions & 0 deletions src/core/processing/qgsprocessingutils.cpp
Expand Up @@ -18,6 +18,7 @@
#include "qgsprocessingutils.h"
#include "qgsproject.h"
#include "qgssettings.h"
#include "qgscsexception.h"
#include "qgsprocessingcontext.h"
#include "qgsvectorlayerexporter.h"
#include "qgsvectorfilewriter.h"
Expand Down Expand Up @@ -400,3 +401,37 @@ void QgsProcessingUtils::createFeatureSinkPython( QgsFeatureSink **sink, QString
}


QgsRectangle QgsProcessingUtils::combineLayerExtents( const QList<QgsMapLayer *> layers, const QgsCoordinateReferenceSystem &crs )
{
QgsRectangle extent;
Q_FOREACH ( QgsMapLayer *layer, layers )
{
if ( !layer )
continue;

if ( crs.isValid() )
{
//transform layer extent to target CRS
QgsCoordinateTransform ct( layer->crs(), crs );
try
{
QgsRectangle reprojExtent = ct.transformBoundingBox( layer->extent() );
extent.combineExtentWith( reprojExtent );
}
catch ( QgsCsException & )
{
// can't reproject... what to do here? hmmm?
// let's ignore this layer for now, but maybe we should just use the original extent?
}
}
else
{
extent.combineExtentWith( layer->extent() );
}

}
return extent;
}



5 changes: 5 additions & 0 deletions src/core/processing/qgsprocessingutils.h
Expand Up @@ -180,6 +180,11 @@ class CORE_EXPORT QgsProcessingUtils
const QgsCoordinateReferenceSystem &crs,
QgsProcessingContext &context ) SIP_PYNAME( createFeatureSink );

/**
* Combines the extent of several map \a layers. If specified, the target \a crs
* will be used to transform the layer's extent to the desired output reference system.
*/
static QgsRectangle combineLayerExtents( const QList< QgsMapLayer *> layers, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() );

private:

Expand Down
35 changes: 35 additions & 0 deletions tests/src/core/testqgsprocessing.cpp
Expand Up @@ -216,6 +216,7 @@ class TestQgsProcessing: public QObject
void parameterVectorLayer();
void parameterOutputVectorLayer();
void checkParamValues();
void combineLayerExtent();

private:

Expand Down Expand Up @@ -2181,5 +2182,39 @@ void TestQgsProcessing::checkParamValues()
a.checkParameterVals();
}

void TestQgsProcessing::combineLayerExtent()
{
QgsRectangle ext = QgsProcessingUtils::combineLayerExtents( QList< QgsMapLayer *>() );
QVERIFY( ext.isNull() );

QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt

QString raster1 = testDataDir + "tenbytenraster.asc";
QString raster2 = testDataDir + "landsat.tif";
QFileInfo fi1( raster1 );
QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" );
QFileInfo fi2( raster2 );
QgsRasterLayer *r2 = new QgsRasterLayer( fi2.filePath(), "R2" );

ext = QgsProcessingUtils::combineLayerExtents( QList< QgsMapLayer *>() << r1 );
QGSCOMPARENEAR( ext.xMinimum(), 1535375.000000, 10 );
QGSCOMPARENEAR( ext.xMaximum(), 1535475, 10 );
QGSCOMPARENEAR( ext.yMinimum(), 5083255, 10 );
QGSCOMPARENEAR( ext.yMaximum(), 5083355, 10 );

ext = QgsProcessingUtils::combineLayerExtents( QList< QgsMapLayer *>() << r1 << r2 );
QGSCOMPARENEAR( ext.xMinimum(), 781662, 10 );
QGSCOMPARENEAR( ext.xMaximum(), 1535475, 10 );
QGSCOMPARENEAR( ext.yMinimum(), 3339523, 10 );
QGSCOMPARENEAR( ext.yMaximum(), 5083355, 10 );

// with reprojection
ext = QgsProcessingUtils::combineLayerExtents( QList< QgsMapLayer *>() << r1 << r2, QgsCoordinateReferenceSystem::fromEpsgId( 3785 ) );
QGSCOMPARENEAR( ext.xMinimum(), 1995320, 10 );
QGSCOMPARENEAR( ext.xMaximum(), 2008833, 10 );
QGSCOMPARENEAR( ext.yMinimum(), 3523084, 10 );
QGSCOMPARENEAR( ext.yMaximum(), 3536664, 10 );
}

QGSTEST_MAIN( TestQgsProcessing )
#include "testqgsprocessing.moc"

0 comments on commit ba03f1a

Please sign in to comment.