Merge pull request #5201 from nyalldawson/point_crs

[processing] Transparently handle CRS for point parameters
This commit is contained in:
Alexander Bruy 2017-09-25 09:47:46 +03:00 committed by GitHub
commit 14a48c0349
16 changed files with 473 additions and 68 deletions

View File

@ -696,12 +696,26 @@ class QgsProcessingAlgorithm
:rtype: QgsCoordinateReferenceSystem
%End
QgsPointXY parameterAsPoint( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context ) const;
QgsPointXY parameterAsPoint( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context,
const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() ) const;
%Docstring
Evaluates the parameter with matching ``name`` to a point.
If ``crs`` is set then the point will be automatically
reprojected so that it is in the specified ``crs``.
.. seealso:: parameterAsPointCrs()
:rtype: QgsPointXY
%End
QgsCoordinateReferenceSystem parameterAsPointCrs( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context );
%Docstring
Returns the coordinate reference system associated with an point parameter value.
.. seealso:: parameterAsPoint()
:rtype: QgsCoordinateReferenceSystem
%End
QString parameterAsFile( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context ) const;
%Docstring
Evaluates the parameter with matching ``name`` to a file/folder name.

View File

@ -570,12 +570,25 @@ class QgsProcessingParameters
:rtype: QgsCoordinateReferenceSystem
%End
static QgsPointXY parameterAsPoint( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context );
static QgsPointXY parameterAsPoint( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context,
const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() );
%Docstring
Evaluates the parameter with matching ``definition`` to a point.
If ``crs`` is set then the point will be automatically reprojected so that it is in the specified ``crs``.
.. seealso:: parameterAsPointCrs()
:rtype: QgsPointXY
%End
static QgsCoordinateReferenceSystem parameterAsPointCrs( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context );
%Docstring
Returns the coordinate reference system associated with an point parameter value.
.. seealso:: parameterAsPoint()
:rtype: QgsCoordinateReferenceSystem
%End
static QString parameterAsFile( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context );
%Docstring
Evaluates the parameter with matching ``definition`` to a file/folder name.
@ -817,6 +830,8 @@ class QgsProcessingParameterPoint : QgsProcessingParameterDefinition
virtual QString type() const;
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
static QgsProcessingParameterPoint *fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition ) /Factory/;
%Docstring

View File

@ -35,6 +35,7 @@ from qgis.core import (QgsWkbTypes,
QgsUnitTypes,
QgsFeature,
QgsFeatureSink,
QgsFeatureRequest,
QgsGeometry,
QgsFields,
QgsField,
@ -208,7 +209,7 @@ class ServiceAreaFromLayer(QgisAlgorithm):
multiplier * 1000.0 / 3600.0)
director.addStrategy(strategy)
builder = QgsGraphBuilder(context.project().crs(),
builder = QgsGraphBuilder(network.sourceCrs(),
True,
tolerance)

View File

@ -163,7 +163,7 @@ class ServiceAreaFromPoint(QgisAlgorithm):
def processAlgorithm(self, parameters, context, feedback):
network = self.parameterAsSource(parameters, self.INPUT, context)
startPoint = self.parameterAsPoint(parameters, self.START_POINT, context)
startPoint = self.parameterAsPoint(parameters, self.START_POINT, context, network.sourceCrs())
strategy = self.parameterAsEnum(parameters, self.STRATEGY, context)
travelCost = self.parameterAsDouble(parameters, self.TRAVEL_COST, context)
@ -200,7 +200,7 @@ class ServiceAreaFromPoint(QgisAlgorithm):
multiplier * 1000.0 / 3600.0)
director.addStrategy(strategy)
builder = QgsGraphBuilder(context.project().crs(),
builder = QgsGraphBuilder(network.sourceCrs(),
True,
tolerance)
feedback.pushInfo(self.tr('Building graph...'))

View File

@ -34,6 +34,7 @@ from qgis.PyQt.QtGui import QIcon
from qgis.core import (QgsWkbTypes,
QgsUnitTypes,
QgsFeature,
QgsFeatureRequest,
QgsFeatureSink,
QgsGeometry,
QgsFields,
@ -158,7 +159,7 @@ class ShortestPathLayerToPoint(QgisAlgorithm):
def processAlgorithm(self, parameters, context, feedback):
network = self.parameterAsSource(parameters, self.INPUT, context)
startPoints = self.parameterAsSource(parameters, self.START_POINTS, context)
endPoint = self.parameterAsPoint(parameters, self.END_POINT, context)
endPoint = self.parameterAsPoint(parameters, self.END_POINT, context, network.sourceCrs())
strategy = self.parameterAsEnum(parameters, self.STRATEGY, context)
directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context)
@ -206,7 +207,7 @@ class ShortestPathLayerToPoint(QgisAlgorithm):
multiplier = 3600
director.addStrategy(strategy)
builder = QgsGraphBuilder(context.project().crs(),
builder = QgsGraphBuilder(network.sourceCrs(),
True,
tolerance)
@ -236,7 +237,7 @@ class ShortestPathLayerToPoint(QgisAlgorithm):
nPoints = len(snappedPoints)
total = 100.0 / nPoints if nPoints else 1
for i in range(1, count + 1):
for i in range(1, nPoints + 1):
if feedback.isCanceled():
break

View File

@ -36,6 +36,7 @@ from qgis.core import (QgsWkbTypes,
QgsFeature,
QgsFeatureSink,
QgsGeometry,
QgsFeatureRequest,
QgsFields,
QgsField,
QgsMessageLog,
@ -157,7 +158,7 @@ class ShortestPathPointToLayer(QgisAlgorithm):
def processAlgorithm(self, parameters, context, feedback):
network = self.parameterAsSource(parameters, self.INPUT, context)
startPoint = self.parameterAsPoint(parameters, self.START_POINT, context)
startPoint = self.parameterAsPoint(parameters, self.START_POINT, context, network.sourceCrs())
endPoints = self.parameterAsSource(parameters, self.END_POINTS, context)
strategy = self.parameterAsEnum(parameters, self.STRATEGY, context)
@ -206,7 +207,7 @@ class ShortestPathPointToLayer(QgisAlgorithm):
multiplier = 3600
director.addStrategy(strategy)
builder = QgsGraphBuilder(context.project().crs(),
builder = QgsGraphBuilder(network.sourceCrs(),
True,
tolerance)
@ -237,7 +238,7 @@ class ShortestPathPointToLayer(QgisAlgorithm):
nPoints = len(snappedPoints)
total = 100.0 / nPoints if nPoints else 1
for i in range(1, count + 1):
for i in range(1, nPoints + 1):
if feedback.isCanceled():
break

View File

@ -160,8 +160,8 @@ class ShortestPathPointToPoint(QgisAlgorithm):
def processAlgorithm(self, parameters, context, feedback):
network = self.parameterAsSource(parameters, self.INPUT, context)
startPoint = self.parameterAsPoint(parameters, self.START_POINT, context)
endPoint = self.parameterAsPoint(parameters, self.END_POINT, context)
startPoint = self.parameterAsPoint(parameters, self.START_POINT, context, network.sourceCrs())
endPoint = self.parameterAsPoint(parameters, self.END_POINT, context, network.sourceCrs())
strategy = self.parameterAsEnum(parameters, self.STRATEGY, context)
directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context)
@ -206,7 +206,7 @@ class ShortestPathPointToPoint(QgisAlgorithm):
multiplier = 3600
director.addStrategy(strategy)
builder = QgsGraphBuilder(context.project().crs(),
builder = QgsGraphBuilder(network.sourceCrs(),
True,
tolerance)
feedback.pushInfo(self.tr('Building graph...'))

View File

@ -79,10 +79,14 @@ class ExtentSelectionPanel(BASE, WIDGET):
if param.defaultValue() is not None:
context = createContext()
rect = QgsProcessingParameters.parameterAsExtent(param, {param.name(): param.defaultValue()}, context)
crs = QgsProcessingParameters.parameterAsExtentCrs(param, {param.name(): param.defaultValue()}, context)
if not rect.isNull():
try:
s = '{},{},{},{}'.format(
rect.xMinimum(), rect.xMaximum(), rect.yMinimum(), rect.yMaximum())
if crs.isValid():
s += ' [' + crs.authid() + ']'
self.crs = crs
self.leText.setText(s)
except:
pass
@ -133,10 +137,6 @@ class ExtentSelectionPanel(BASE, WIDGET):
self.tr('Use extent from'), extents, False)
if ok:
self.setValueFromRect(QgsReferencedRectangle(extentsDict[item]["extent"], QgsCoordinateReferenceSystem(extentsDict[item]["authid"])))
if extentsDict[item]["authid"] != iface.mapCanvas().mapSettings().destinationCrs().authid():
iface.messageBar().pushMessage(self.tr("Warning"),
self.tr("The projection of the chosen layer is not the same as canvas projection! The selected extent might not be what was intended."),
QgsMessageBar.WARNING, 8)
def selectOnCanvas(self):
canvas = iface.mapCanvas()
@ -151,11 +151,14 @@ class ExtentSelectionPanel(BASE, WIDGET):
s = '{},{},{},{}'.format(
r.xMinimum(), r.xMaximum(), r.yMinimum(), r.yMaximum())
self.leText.setText(s)
try:
self.crs = r.crs()
except:
self.crs = QgsProject.instance().crs()
if self.crs.isValid():
s += ' [' + self.crs.authid() + ']'
self.leText.setText(s)
self.tool.reset()
canvas = iface.mapCanvas()
canvas.setMapTool(self.prevMapTool)
@ -165,13 +168,7 @@ class ExtentSelectionPanel(BASE, WIDGET):
def getValue(self):
if str(self.leText.text()).strip() != '':
try:
parts = self.leText.text().split(',')
parts = [float(p) for p in parts]
r = QgsReferencedRectangle(QgsRectangle(parts[0], parts[2], parts[1], parts[3]), self.crs)
return r
except:
return str(self.leText.text())
return str(self.leText.text())
else:
return None

View File

@ -25,22 +25,23 @@ __copyright__ = '(C) 2016, Alexander Bruy'
__revision__ = '$Format:%H$'
from qgis.PyQt.QtCore import Qt
from qgis.PyQt.QtCore import Qt, pyqtSignal
from qgis.gui import QgsMapToolEmitPoint
class PointMapTool(QgsMapToolEmitPoint):
complete = pyqtSignal()
def __init__(self, canvas):
QgsMapToolEmitPoint.__init__(self, canvas)
self.canvas = canvas
self.cursor = Qt.ArrowCursor
self.cursor = Qt.CrossCursor
def activate(self):
self.canvas.setCursor(self.cursor)
def canvasPressEvent(self, event):
pnt = self.toMapCoordinates(event.pos())
self.canvasClicked.emit(pnt, event.button())
def canvasReleaseEvent(self, event):
self.complete.emit()

View File

@ -28,6 +28,9 @@ __revision__ = '$Format:%H$'
import os
from qgis.core import (QgsProject,
QgsReferencedPointXY,
QgsPointXY)
from qgis.PyQt import uic
from qgis.utils import iface
@ -48,6 +51,7 @@ class PointSelectionPanel(BASE, WIDGET):
self.btnSelect.clicked.connect(self.selectOnCanvas)
self.dialog = dialog
self.crs = QgsProject.instance().crs()
if iface is not None:
canvas = iface.mapCanvas()
@ -55,6 +59,7 @@ class PointSelectionPanel(BASE, WIDGET):
self.tool = PointMapTool(canvas)
self.tool.canvasClicked.connect(self.updatePoint)
self.tool.complete.connect(self.pointPicked)
else:
self.prevMapTool = None
self.tool = None
@ -76,8 +81,12 @@ class PointSelectionPanel(BASE, WIDGET):
def updatePoint(self, point, button):
s = '{},{}'.format(point.x(), point.y())
self.crs = QgsProject.instance().crs()
if self.crs.isValid():
s += ' [' + self.crs.authid() + ']'
self.leText.setText(s)
def pointPicked(self):
canvas = iface.mapCanvas()
canvas.setMapTool(self.prevMapTool)
self.dialog.showNormal()

View File

@ -565,9 +565,14 @@ QgsGeometry QgsProcessingAlgorithm::parameterAsExtentGeometry( const QVariantMap
return QgsProcessingParameters::parameterAsExtentGeometry( parameterDefinition( name ), parameters, context, crs );
}
QgsPointXY QgsProcessingAlgorithm::parameterAsPoint( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context ) const
QgsPointXY QgsProcessingAlgorithm::parameterAsPoint( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context, const QgsCoordinateReferenceSystem &crs ) const
{
return QgsProcessingParameters::parameterAsPoint( parameterDefinition( name ), parameters, context );
return QgsProcessingParameters::parameterAsPoint( parameterDefinition( name ), parameters, context, crs );
}
QgsCoordinateReferenceSystem QgsProcessingAlgorithm::parameterAsPointCrs( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context )
{
return QgsProcessingParameters::parameterAsPointCrs( parameterDefinition( name ), parameters, context );
}
QString QgsProcessingAlgorithm::parameterAsFile( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context ) const

View File

@ -680,8 +680,21 @@ class CORE_EXPORT QgsProcessingAlgorithm
/**
* Evaluates the parameter with matching \a name to a point.
*
* If \a crs is set then the point will be automatically
* reprojected so that it is in the specified \a crs.
*
* \see parameterAsPointCrs()
*/
QgsPointXY parameterAsPoint( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context ) const;
QgsPointXY parameterAsPoint( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context,
const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() ) const;
/**
* Returns the coordinate reference system associated with an point parameter value.
*
* \see parameterAsPoint()
*/
QgsCoordinateReferenceSystem parameterAsPointCrs( const QVariantMap &parameters, const QString &name, QgsProcessingContext &context );
/**
* Evaluates the parameter with matching \a name to a file/folder name.

View File

@ -503,7 +503,14 @@ QgsRectangle QgsProcessingParameters::parameterAsExtent( const QgsProcessingPara
if ( crs.isValid() && rr.crs().isValid() && crs != rr.crs() )
{
QgsCoordinateTransform ct( rr.crs(), crs );
return ct.transformBoundingBox( rr );
try
{
return ct.transformBoundingBox( rr );
}
catch ( QgsCsException & )
{
QgsMessageLog::logMessage( QObject::tr( "Error transforming extent geometry" ) );
}
}
return rr;
}
@ -517,25 +524,56 @@ QgsRectangle QgsProcessingParameters::parameterAsExtent( const QgsProcessingPara
if ( rectText.isEmpty() )
return QgsRectangle();
QStringList parts = rectText.split( ',' );
if ( parts.count() == 4 )
QRegularExpression rx( "^(.*?)\\s*,\\s*(.*?),\\s*(.*?),\\s*(.*?)\\s*(?:\\[(.*)\\])?\\s*$" );
QRegularExpressionMatch match = rx.match( rectText );
if ( match.hasMatch() )
{
bool xMinOk = false;
double xMin = parts.at( 0 ).toDouble( &xMinOk );
double xMin = match.captured( 1 ).toDouble( &xMinOk );
bool xMaxOk = false;
double xMax = parts.at( 1 ).toDouble( &xMaxOk );
double xMax = match.captured( 2 ).toDouble( &xMaxOk );
bool yMinOk = false;
double yMin = parts.at( 2 ).toDouble( &yMinOk );
double yMin = match.captured( 3 ).toDouble( &yMinOk );
bool yMaxOk = false;
double yMax = parts.at( 3 ).toDouble( &yMaxOk );
double yMax = match.captured( 4 ).toDouble( &yMaxOk );
if ( xMinOk && xMaxOk && yMinOk && yMaxOk )
return QgsRectangle( xMin, yMin, xMax, yMax );
{
QgsRectangle rect( xMin, yMin, xMax, yMax );
QgsCoordinateReferenceSystem rectCrs( match.captured( 5 ) );
if ( crs.isValid() && rectCrs.isValid() && crs != rectCrs )
{
QgsCoordinateTransform ct( rectCrs, crs );
try
{
return ct.transformBoundingBox( rect );
}
catch ( QgsCsException & )
{
QgsMessageLog::logMessage( QObject::tr( "Error transforming extent geometry" ) );
}
}
return rect;
}
}
// try as layer extent
if ( QgsMapLayer *layer = QgsProcessingUtils::mapLayerFromString( rectText, context ) )
return layer->extent();
{
QgsRectangle rect = layer->extent();
if ( crs.isValid() && layer->crs().isValid() && crs != layer->crs() )
{
QgsCoordinateTransform ct( layer->crs(), crs );
try
{
return ct.transformBoundingBox( rect );
}
catch ( QgsCsException & )
{
QgsMessageLog::logMessage( QObject::tr( "Error transforming extent geometry" ) );
}
}
return rect;
}
return QgsRectangle();
}
@ -566,6 +604,70 @@ QgsGeometry QgsProcessingParameters::parameterAsExtentGeometry( const QgsProcess
}
}
QString rectText;
if ( val.canConvert<QgsProperty>() )
rectText = val.value< QgsProperty >().valueAsString( context.expressionContext(), definition->defaultValue().toString() );
else
rectText = val.toString();
if ( !rectText.isEmpty() )
{
QRegularExpression rx( "^(.*?)\\s*,\\s*(.*?),\\s*(.*?),\\s*(.*?)\\s*(?:\\[(.*)\\])?\\s*$" );
QRegularExpressionMatch match = rx.match( rectText );
if ( match.hasMatch() )
{
bool xMinOk = false;
double xMin = match.captured( 1 ).toDouble( &xMinOk );
bool xMaxOk = false;
double xMax = match.captured( 2 ).toDouble( &xMaxOk );
bool yMinOk = false;
double yMin = match.captured( 3 ).toDouble( &yMinOk );
bool yMaxOk = false;
double yMax = match.captured( 4 ).toDouble( &yMaxOk );
if ( xMinOk && xMaxOk && yMinOk && yMaxOk )
{
QgsRectangle rect( xMin, yMin, xMax, yMax );
QgsCoordinateReferenceSystem rectCrs( match.captured( 5 ) );
QgsGeometry g = QgsGeometry::fromRect( rect );
if ( crs.isValid() && rectCrs.isValid() && crs != rectCrs )
{
g = g.densifyByCount( 20 );
QgsCoordinateTransform ct( rectCrs, crs );
try
{
g.transform( ct );
}
catch ( QgsCsException & )
{
QgsMessageLog::logMessage( QObject::tr( "Error transforming extent geometry" ) );
}
return g;
}
}
}
}
// try as layer extent
if ( QgsMapLayer *layer = QgsProcessingUtils::mapLayerFromString( rectText, context ) )
{
QgsRectangle rect = layer->extent();
QgsGeometry g = QgsGeometry::fromRect( rect );
if ( crs.isValid() && layer->crs().isValid() && crs != layer->crs() )
{
g = g.densifyByCount( 20 );
QgsCoordinateTransform ct( layer->crs(), crs );
try
{
g.transform( ct );
}
catch ( QgsCsException & )
{
QgsMessageLog::logMessage( QObject::tr( "Error transforming extent geometry" ) );
}
}
return g;
}
return QgsGeometry::fromRect( parameterAsExtent( definition, parameters, context, crs ) );
}
@ -582,17 +684,57 @@ QgsCoordinateReferenceSystem QgsProcessingParameters::parameterAsExtentCrs( cons
}
}
QRegularExpression rx( "^(.*?)\\s*,\\s*(.*?),\\s*(.*?),\\s*(.*?)\\s*(?:\\[(.*)\\])?\\s*$" );
QString valueAsString = parameterAsString( definition, parameters, context );
QRegularExpressionMatch match = rx.match( valueAsString );
if ( match.hasMatch() )
{
QgsCoordinateReferenceSystem crs( match.captured( 5 ) );
if ( crs.isValid() )
return crs;
}
// try as layer crs
if ( QgsMapLayer *layer = QgsProcessingUtils::mapLayerFromString( valueAsString, context ) )
{
return layer->crs();
}
if ( context.project() )
return context.project()->crs();
else
return QgsCoordinateReferenceSystem();
}
QgsPointXY QgsProcessingParameters::parameterAsPoint( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context )
QgsPointXY QgsProcessingParameters::parameterAsPoint( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context, const QgsCoordinateReferenceSystem &crs )
{
if ( !definition )
return QgsPointXY();
QVariant val = parameters.value( definition->name() );
if ( val.canConvert< QgsPointXY >() )
{
return val.value<QgsPointXY>();
}
if ( val.canConvert< QgsReferencedPointXY >() )
{
QgsReferencedPointXY rp = val.value<QgsReferencedPointXY>();
if ( crs.isValid() && rp.crs().isValid() && crs != rp.crs() )
{
QgsCoordinateTransform ct( rp.crs(), crs );
try
{
return ct.transform( rp );
}
catch ( QgsCsException & )
{
QgsMessageLog::logMessage( QObject::tr( "Error transforming point geometry" ) );
}
}
return rp;
}
QString pointText = parameterAsString( definition, parameters, context );
if ( pointText.isEmpty() )
pointText = definition->defaultValue().toString();
@ -600,20 +742,71 @@ QgsPointXY QgsProcessingParameters::parameterAsPoint( const QgsProcessingParamet
if ( pointText.isEmpty() )
return QgsPointXY();
QStringList parts = pointText.split( ',' );
if ( parts.count() == 2 )
QRegularExpression rx( "^(.*?)\\s*,\\s*(.*?)\\s*(?:\\[(.*)\\])?\\s*$" );
QString valueAsString = parameterAsString( definition, parameters, context );
QRegularExpressionMatch match = rx.match( valueAsString );
if ( match.hasMatch() )
{
bool xOk = false;
double x = parts.at( 0 ).toDouble( &xOk );
double x = match.captured( 1 ).toDouble( &xOk );
bool yOk = false;
double y = parts.at( 1 ).toDouble( &yOk );
double y = match.captured( 2 ).toDouble( &yOk );
if ( xOk && yOk )
return QgsPointXY( x, y );
{
QgsPointXY pt( x, y );
QgsCoordinateReferenceSystem pointCrs( match.captured( 3 ) );
if ( crs.isValid() && pointCrs.isValid() && crs != pointCrs )
{
QgsCoordinateTransform ct( pointCrs, crs );
try
{
return ct.transform( pt );
}
catch ( QgsCsException & )
{
QgsMessageLog::logMessage( QObject::tr( "Error transforming point geometry" ) );
}
}
return pt;
}
}
return QgsPointXY();
}
QgsCoordinateReferenceSystem QgsProcessingParameters::parameterAsPointCrs( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context )
{
QVariant val = parameters.value( definition->name() );
if ( val.canConvert< QgsReferencedPointXY >() )
{
QgsReferencedPointXY rr = val.value<QgsReferencedPointXY>();
if ( rr.crs().isValid() )
{
return rr.crs();
}
}
QRegularExpression rx( "^(.*?)\\s*,\\s*(.*?)\\s*(?:\\[(.*)\\])?\\s*$" );
QString valueAsString = parameterAsString( definition, parameters, context );
QRegularExpressionMatch match = rx.match( valueAsString );
if ( match.hasMatch() )
{
QgsCoordinateReferenceSystem crs( match.captured( 3 ) );
if ( crs.isValid() )
return crs;
}
if ( context.project() )
return context.project()->crs();
else
return QgsCoordinateReferenceSystem();
}
QString QgsProcessingParameters::parameterAsFile( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context )
{
if ( !definition )
@ -1220,17 +1413,18 @@ bool QgsProcessingParameterExtent::checkValueIsAcceptable( const QVariant &input
return true;
}
QStringList parts = input.toString().split( ',' );
if ( parts.count() == 4 )
QRegularExpression rx( "^(.*?)\\s*,\\s*(.*?)\\s*,\\s*(.*?)\\s*,\\s*(.*?)\\s*(?:\\[(.*)\\])?\\s*$" );
QRegularExpressionMatch match = rx.match( input.toString() );
if ( match.hasMatch() )
{
bool xMinOk = false;
( void )parts.at( 0 ).toDouble( &xMinOk );
( void )match.captured( 1 ).toDouble( &xMinOk );
bool xMaxOk = false;
( void )parts.at( 1 ).toDouble( &xMaxOk );
( void )match.captured( 2 ).toDouble( &xMaxOk );
bool yMinOk = false;
( void )parts.at( 2 ).toDouble( &yMinOk );
( void )match.captured( 3 ).toDouble( &yMinOk );
bool yMaxOk = false;
( void )parts.at( 3 ).toDouble( &yMaxOk );
( void )match.captured( 4 ).toDouble( &yMaxOk );
if ( xMinOk && xMaxOk && yMinOk && yMaxOk )
return true;
}
@ -1247,7 +1441,7 @@ QString QgsProcessingParameterExtent::valueAsPythonString( const QVariant &value
if ( value.canConvert< QgsRectangle >() )
{
QgsRectangle r = value.value<QgsRectangle>();
return QStringLiteral( "QgsRectangle( %1, %2, %3, %4 )" ).arg( qgsDoubleToString( r.xMinimum() ),
return QStringLiteral( "'%1, %3, %2, %4'" ).arg( qgsDoubleToString( r.xMinimum() ),
qgsDoubleToString( r.yMinimum() ),
qgsDoubleToString( r.xMaximum() ),
qgsDoubleToString( r.yMaximum() ) );
@ -1255,7 +1449,7 @@ QString QgsProcessingParameterExtent::valueAsPythonString( const QVariant &value
if ( value.canConvert< QgsReferencedRectangle >() )
{
QgsReferencedRectangle r = value.value<QgsReferencedRectangle>();
return QStringLiteral( "QgsReferencedRectangle( QgsRectangle( %1, %2, %3, %4 ), QgsCoordinateReferenceSystem( '%5' ) )" ).arg( qgsDoubleToString( r.xMinimum() ),
return QStringLiteral( "'%1, %3, %2, %4 [%5]'" ).arg( qgsDoubleToString( r.xMinimum() ),
qgsDoubleToString( r.yMinimum() ),
qgsDoubleToString( r.xMaximum() ),
qgsDoubleToString( r.yMaximum() ), r.crs().authid() );
@ -1296,25 +1490,58 @@ bool QgsProcessingParameterPoint::checkValueIsAcceptable( const QVariant &input,
return true;
}
if ( input.canConvert< QgsPointXY >() )
{
return true;
}
if ( input.canConvert< QgsReferencedPointXY >() )
{
return true;
}
if ( input.type() == QVariant::String )
{
if ( input.toString().isEmpty() )
return mFlags & FlagOptional;
}
QStringList parts = input.toString().split( ',' );
if ( parts.count() == 2 )
QRegularExpression rx( "^(.*?)\\s*,\\s*(.*?)\\s*(?:\\[(.*)\\])?\\s*$" );
QRegularExpressionMatch match = rx.match( input.toString() );
if ( match.hasMatch() )
{
bool xOk = false;
( void )parts.at( 0 ).toDouble( &xOk );
( void )match.captured( 1 ).toDouble( &xOk );
bool yOk = false;
( void )parts.at( 1 ).toDouble( &yOk );
( void )match.captured( 2 ).toDouble( &yOk );
return xOk && yOk;
}
else
return false;
}
QString QgsProcessingParameterPoint::valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const
{
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
if ( value.canConvert< QgsPointXY >() )
{
QgsPointXY r = value.value<QgsPointXY>();
return QStringLiteral( "'%1,%2'" ).arg( qgsDoubleToString( r.x() ),
qgsDoubleToString( r.y() ) );
}
if ( value.canConvert< QgsReferencedPointXY >() )
{
QgsReferencedPointXY r = value.value<QgsReferencedPointXY>();
return QStringLiteral( "'%1,%2 [%3]'" ).arg( qgsDoubleToString( r.x() ),
qgsDoubleToString( r.y() ),
r.crs().authid() );
}
return QgsProcessingParameterDefinition::valueAsPythonString( value, context );
}
QgsProcessingParameterPoint *QgsProcessingParameterPoint::fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition )
{
return new QgsProcessingParameterPoint( name, description, definition, isOptional );

View File

@ -598,8 +598,20 @@ class CORE_EXPORT QgsProcessingParameters
/**
* Evaluates the parameter with matching \a definition to a point.
*
* If \a crs is set then the point will be automatically reprojected so that it is in the specified \a crs.
*
* \see parameterAsPointCrs()
*/
static QgsPointXY parameterAsPoint( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context );
static QgsPointXY parameterAsPoint( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context,
const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() );
/**
* Returns the coordinate reference system associated with an point parameter value.
*
* \see parameterAsPoint()
*/
static QgsCoordinateReferenceSystem parameterAsPointCrs( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context );
/**
* Evaluates the parameter with matching \a definition to a file/folder name.
@ -804,6 +816,7 @@ class CORE_EXPORT QgsProcessingParameterPoint : public QgsProcessingParameterDef
QgsProcessingParameterDefinition *clone() const override SIP_FACTORY;
QString type() const override { return typeName(); }
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const override;
/**
* Creates a new parameter using the definition from a script code.

View File

@ -151,7 +151,7 @@ void QgsApplication::init( QString profileFolder )
qRegisterMetaType<QgsFeatureIds>( "QgsFeatureIds" );
qRegisterMetaType<QgsMessageLog::MessageLevel>( "QgsMessageLog::MessageLevel" );
qRegisterMetaType<QgsReferencedRectangle>( "QgsReferencedRectangle" );
qRegisterMetaType<QgsReferencedRectangle>( "QgsReferencedPoint" );
qRegisterMetaType<QgsReferencedPointXY>( "QgsReferencedPointXY" );
QString prefixPath( getenv( "QGIS_PREFIX_PATH" ) ? getenv( "QGIS_PREFIX_PATH" ) : applicationDirPath() );
// QgsDebugMsg( QString( "prefixPath(): %1" ).arg( prefixPath ) );

View File

@ -1844,6 +1844,21 @@ void TestQgsProcessing::parameterExtent()
QVERIFY( !def->checkValueIsAcceptable( true ) );
QVERIFY( !def->checkValueIsAcceptable( 5 ) );
QVERIFY( def->checkValueIsAcceptable( "1,2,3,4" ) );
QVERIFY( def->checkValueIsAcceptable( " 1, 2 ,3 , 4 " ) );
QVERIFY( def->checkValueIsAcceptable( " 1, 2 ,3 , 4 ", &context ) );
QVERIFY( def->checkValueIsAcceptable( "-1.1,2,-3,-4" ) );
QVERIFY( def->checkValueIsAcceptable( "-1.1,2,-3,-4", &context ) );
QVERIFY( def->checkValueIsAcceptable( "-1.1,-2.2,-3.3,-4.4" ) );
QVERIFY( def->checkValueIsAcceptable( "-1.1,-2.2,-3.3,-4.4", &context ) );
QVERIFY( def->checkValueIsAcceptable( "1.1,2,3,4.4[EPSG:4326]" ) );
QVERIFY( def->checkValueIsAcceptable( "1.1,2,3,4.4[EPSG:4326]", &context ) );
QVERIFY( def->checkValueIsAcceptable( "1.1,2,3,4.4 [EPSG:4326]" ) );
QVERIFY( def->checkValueIsAcceptable( "1.1,2,3,4.4 [EPSG:4326]", &context ) );
QVERIFY( def->checkValueIsAcceptable( " -1.1, -2, -3, -4.4 [EPSG:4326] " ) );
QVERIFY( def->checkValueIsAcceptable( " -1.1, -2, -3, -4.4 [EPSG:4326] ", &context ) );
QVERIFY( def->checkValueIsAcceptable( "121774.38859446358,948723.6921024882,-264546.200347173,492749.6672022904 [EPSG:3785]" ) );
QVERIFY( def->checkValueIsAcceptable( "121774.38859446358,948723.6921024882,-264546.200347173,492749.6672022904 [EPSG:3785]", &context ) );
QVERIFY( !def->checkValueIsAcceptable( "" ) );
QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
QVERIFY( def->checkValueIsAcceptable( QgsRectangle( 1, 2, 3, 4 ) ) );
@ -1868,15 +1883,34 @@ void TestQgsProcessing::parameterExtent()
params.insert( "non_optional", raster1 );
QVERIFY( def->checkValueIsAcceptable( raster1 ) );
QCOMPARE( QgsProcessingParameters::parameterAsExtent( def.get(), params, context ), r1->extent() );
QCOMPARE( QgsProcessingParameters::parameterAsExtentCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
QGSCOMPARENEAR( ext.xMinimum(), 1535375, 100 );
QGSCOMPARENEAR( ext.xMaximum(), 1535475, 100 );
QGSCOMPARENEAR( ext.yMinimum(), 5083255, 100 );
QGSCOMPARENEAR( ext.yMaximum(), 5083355, 100 );
// string representing a non-project layer source
params.insert( "non_optional", raster2 );
QVERIFY( def->checkValueIsAcceptable( raster2 ) );
QCOMPARE( QgsProcessingParameters::parameterAsExtentCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:32633" ) );
ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context );
QGSCOMPARENEAR( ext.xMinimum(), 781662.375000, 10 );
QGSCOMPARENEAR( ext.xMaximum(), 793062.375000, 10 );
QGSCOMPARENEAR( ext.yMinimum(), 3339523.125000, 10 );
QGSCOMPARENEAR( ext.yMaximum(), 3350923.125000, 10 );
ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
QGSCOMPARENEAR( ext.xMinimum(), 17.924273, 0.01 );
QGSCOMPARENEAR( ext.xMaximum(), 18.045658, 0.01 );
QGSCOMPARENEAR( ext.yMinimum(), 30.151856, 0.01 );
QGSCOMPARENEAR( ext.yMaximum(), 30.257289, 0.01 );
QgsGeometry gExt = QgsProcessingParameters::parameterAsExtentGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
QCOMPARE( gExt.geometry()->vertexCount(), 85 );
ext = gExt.boundingBox();
QGSCOMPARENEAR( ext.xMinimum(), 17.924273, 0.01 );
QGSCOMPARENEAR( ext.xMaximum(), 18.045658, 0.01 );
QGSCOMPARENEAR( ext.yMinimum(), 30.151856, 0.01 );
QGSCOMPARENEAR( ext.yMaximum(), 30.257289, 0.01 );
// string representation of an extent
params.insert( "non_optional", QString( "1.1,2.2,3.3,4.4" ) );
@ -1894,6 +1928,27 @@ void TestQgsProcessing::parameterExtent()
QGSCOMPARENEAR( ext.yMinimum(), 3.3, 0.001 );
QGSCOMPARENEAR( ext.yMaximum(), 4.4, 0.001 );
// with crs in string
params.insert( "non_optional", QString( "1.1,3.3,2.2,4.4 [EPSG:4326]" ) );
QCOMPARE( QgsProcessingParameters::parameterAsExtentCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context );
QGSCOMPARENEAR( ext.xMinimum(), 1.1, 0.001 );
QGSCOMPARENEAR( ext.xMaximum(), 3.3, 0.001 );
QGSCOMPARENEAR( ext.yMinimum(), 2.2, 0.001 );
QGSCOMPARENEAR( ext.yMaximum(), 4.4, 0.001 );
ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
QGSCOMPARENEAR( ext.xMinimum(), 122451, 100 );
QGSCOMPARENEAR( ext.xMaximum(), 367354, 100 );
QGSCOMPARENEAR( ext.yMinimum(), 244963, 100 );
QGSCOMPARENEAR( ext.yMaximum(), 490287, 100 );
gExt = QgsProcessingParameters::parameterAsExtentGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
QCOMPARE( gExt.geometry()->vertexCount(), 85 );
ext = gExt.boundingBox();
QGSCOMPARENEAR( ext.xMinimum(), 122451, 100 );
QGSCOMPARENEAR( ext.xMaximum(), 367354, 100 );
QGSCOMPARENEAR( ext.yMinimum(), 244963, 100 );
QGSCOMPARENEAR( ext.yMaximum(), 490287, 100 );
// nonsense string
params.insert( "non_optional", QString( "i'm not a crs, and nothing you can do will make me one" ) );
QVERIFY( QgsProcessingParameters::parameterAsExtent( def.get(), params, context ).isNull() );
@ -1913,7 +1968,7 @@ void TestQgsProcessing::parameterExtent()
QGSCOMPARENEAR( ext.yMinimum(), 12.2, 0.001 );
QGSCOMPARENEAR( ext.yMaximum(), 14.4, 0.001 );
QgsGeometry gExt = QgsProcessingParameters::parameterAsExtentGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
gExt = QgsProcessingParameters::parameterAsExtentGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
QCOMPARE( gExt.exportToWkt( 1 ), QStringLiteral( "Polygon ((11.1 12.2, 13.3 12.2, 13.3 14.4, 11.1 14.4, 11.1 12.2))" ) );
p.setCrs( QgsCoordinateReferenceSystem( "EPSG:3785" ) );
@ -1949,8 +2004,9 @@ void TestQgsProcessing::parameterExtent()
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( r1 ), context ), QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) );
QCOMPARE( def->valueAsPythonString( raster2, context ), QString( "'" ) + testDataDir + QStringLiteral( "landsat.tif'" ) );
QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
QCOMPARE( def->valueAsPythonString( QgsRectangle( 11, 12, 13, 14 ), context ), QStringLiteral( "QgsRectangle( 11, 12, 13, 14 )" ) );
QCOMPARE( def->valueAsPythonString( QgsReferencedRectangle( QgsRectangle( 11, 12, 13, 14 ), QgsCoordinateReferenceSystem( "epsg:4326" ) ), context ), QStringLiteral( "QgsReferencedRectangle( QgsRectangle( 11, 12, 13, 14 ), QgsCoordinateReferenceSystem( 'EPSG:4326' ) )" ) );
QCOMPARE( def->valueAsPythonString( QgsRectangle( 11, 12, 13, 14 ), context ), QStringLiteral( "'11, 13, 12, 14'" ) );
QCOMPARE( def->valueAsPythonString( QgsReferencedRectangle( QgsRectangle( 11, 12, 13, 14 ), QgsCoordinateReferenceSystem( "epsg:4326" ) ), context ), QStringLiteral( "'11, 13, 12, 14 [EPSG:4326]'" ) );
QCOMPARE( def->valueAsPythonString( "1,2,3,4 [EPSG:4326]", context ), QStringLiteral( "'1,2,3,4 [EPSG:4326]'" ) );
QString code = def->asScriptCode();
QCOMPARE( code, QStringLiteral( "##non_optional=extent 1,2,3,4" ) );
@ -2006,10 +2062,19 @@ void TestQgsProcessing::parameterPoint()
QVERIFY( !def->checkValueIsAcceptable( true ) );
QVERIFY( !def->checkValueIsAcceptable( 5 ) );
QVERIFY( def->checkValueIsAcceptable( "1.1,2" ) );
QVERIFY( def->checkValueIsAcceptable( " 1.1, 2 " ) );
QVERIFY( def->checkValueIsAcceptable( "-1.1,2" ) );
QVERIFY( def->checkValueIsAcceptable( "1.1,-2" ) );
QVERIFY( def->checkValueIsAcceptable( "-1.1,-2" ) );
QVERIFY( def->checkValueIsAcceptable( "1.1,2[EPSG:4326]" ) );
QVERIFY( def->checkValueIsAcceptable( "1.1,2 [EPSG:4326]" ) );
QVERIFY( def->checkValueIsAcceptable( " -1.1, -2 [EPSG:4326] " ) );
QVERIFY( !def->checkValueIsAcceptable( "1.1,a" ) );
QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
QVERIFY( !def->checkValueIsAcceptable( "" ) );
QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
QVERIFY( def->checkValueIsAcceptable( QgsPointXY( 1, 2 ) ) );
QVERIFY( def->checkValueIsAcceptable( QgsReferencedPointXY( QgsPointXY( 1, 2 ), QgsCoordinateReferenceSystem( "EPSG:4326" ) ) ) );
// string representing a point
QVariantMap params;
@ -2019,13 +2084,56 @@ void TestQgsProcessing::parameterPoint()
QGSCOMPARENEAR( point.x(), 1.1, 0.001 );
QGSCOMPARENEAR( point.y(), 2.2, 0.001 );
// with target CRS - should make no difference, because source CRS is unknown
point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
QGSCOMPARENEAR( point.x(), 1.1, 0.001 );
QGSCOMPARENEAR( point.y(), 2.2, 0.001 );
// with CRS as string
params.insert( "non_optional", QString( "1.1,2.2[EPSG:4326]" ) );
QCOMPARE( QgsProcessingParameters::parameterAsPointCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
QGSCOMPARENEAR( point.x(), 122451, 100 );
QGSCOMPARENEAR( point.y(), 244963, 100 );
params.insert( "non_optional", QString( "1.1,2.2 [EPSG:4326]" ) );
QCOMPARE( QgsProcessingParameters::parameterAsPointCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
QGSCOMPARENEAR( point.x(), 122451, 100 );
QGSCOMPARENEAR( point.y(), 244963, 100 );
// nonsense string
params.insert( "non_optional", QString( "i'm not a crs, and nothing you can do will make me one" ) );
point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context );
QCOMPARE( point.x(), 0.0 );
QCOMPARE( point.y(), 0.0 );
// QgsPointXY
params.insert( "non_optional", QgsPointXY( 11.1, 12.2 ) );
point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context );
QGSCOMPARENEAR( point.x(), 11.1, 0.001 );
QGSCOMPARENEAR( point.y(), 12.2, 0.001 );
// with target CRS - should make no difference, because source CRS is unknown
point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
QGSCOMPARENEAR( point.x(), 11.1, 0.001 );
QGSCOMPARENEAR( point.y(), 12.2, 0.001 );
// QgsReferencedPointXY
params.insert( "non_optional", QgsReferencedPointXY( QgsPointXY( 1.1, 2.2 ), QgsCoordinateReferenceSystem( "EPSG:4326" ) ) );
point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context );
QGSCOMPARENEAR( point.x(), 1.1, 0.001 );
QGSCOMPARENEAR( point.y(), 2.2, 0.001 );
QCOMPARE( QgsProcessingParameters::parameterAsPointCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
// with target CRS
point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
QGSCOMPARENEAR( point.x(), 122451, 100 );
QGSCOMPARENEAR( point.y(), 244963, 100 );
QCOMPARE( def->valueAsPythonString( "1,2", context ), QStringLiteral( "'1,2'" ) );
QCOMPARE( def->valueAsPythonString( "1,2 [EPSG:4326]", context ), QStringLiteral( "'1,2 [EPSG:4326]'" ) );
QCOMPARE( def->valueAsPythonString( QgsPointXY( 11, 12 ), context ), QStringLiteral( "'11,12'" ) );
QCOMPARE( def->valueAsPythonString( QgsReferencedPointXY( QgsPointXY( 11, 12 ), QgsCoordinateReferenceSystem( "epsg:4326" ) ), context ), QStringLiteral( "'11,12 [EPSG:4326]'" ) );
QString code = def->asScriptCode();
QCOMPARE( code, QStringLiteral( "##non_optional=point 1,2" ) );