mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-24 00:05:18 -05:00
[expressions] Make file function (such as base_file_name) gracefully
handle map layer values
These are treated as the file path to the layer.
E.g.
base_file_name(@some_variable_which_is_a_layer)
will return the base file name of the layer's source
This commit is contained in:
parent
45ca76cede
commit
2bd18332aa
@ -5,7 +5,7 @@
|
||||
"description": "Returns the base name of the file without the directory or file suffix.",
|
||||
"arguments": [{
|
||||
"arg": "path",
|
||||
"description": "a file path"
|
||||
"description": "a file path or a map layer value. If a map layer layer value is specified then the file source of the layer will be used."
|
||||
}],
|
||||
"examples": [{
|
||||
"expression": "base_file_name('/home/qgis/data/country_boundaries.shp')",
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"description": "Retrieves exif tag values from an image file.",
|
||||
"arguments": [{
|
||||
"arg": "path",
|
||||
"description": "An image file path."
|
||||
"description": "An image file path or a map layer value. If a map layer layer value is specified then the file source of the layer will be used."
|
||||
}, {
|
||||
"arg": "tag",
|
||||
"optional": true,
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"description": "Creates a point geometry from the exif geotags of an image file.",
|
||||
"arguments": [{
|
||||
"arg": "path",
|
||||
"description": "An image file path."
|
||||
"description": "An image file path or a map layer value. If a map layer layer value is specified then the file source of the layer will be used."
|
||||
}],
|
||||
"examples": [{
|
||||
"expression": "geom_to_wkt(exif_geotag('/my/photo.jpg'))",
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"description": "Returns true if a file path exists.",
|
||||
"arguments": [{
|
||||
"arg": "path",
|
||||
"description": "a file path"
|
||||
"description": "a file path or a map layer value. If a map layer layer value is specified then the file source of the layer will be used."
|
||||
}],
|
||||
"examples": [{
|
||||
"expression": "file_exists('/home/qgis/data/country_boundaries.shp')",
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"description": "Returns the name of a file (including the file extension), excluding the directory.",
|
||||
"arguments": [{
|
||||
"arg": "path",
|
||||
"description": "a file path"
|
||||
"description": "a file path or a map layer value. If a map layer layer value is specified then the file source of the layer will be used."
|
||||
}],
|
||||
"examples": [{
|
||||
"expression": "file_name('/home/qgis/data/country_boundaries.shp')",
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"description": "Returns the directory component of a file path. This does not include the file name.",
|
||||
"arguments": [{
|
||||
"arg": "path",
|
||||
"description": "a file path"
|
||||
"description": "a file path or a map layer value. If a map layer layer value is specified then the file source of the layer will be used."
|
||||
}],
|
||||
"examples": [{
|
||||
"expression": "file_path('/home/qgis/data/country_boundaries.shp')",
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"description": "Returns the size (in bytes) of a file.",
|
||||
"arguments": [{
|
||||
"arg": "path",
|
||||
"description": "a file path"
|
||||
"description": "a file path or a map layer value. If a map layer layer value is specified then the file source of the layer will be used."
|
||||
}],
|
||||
"examples": [{
|
||||
"expression": "file_size('/home/qgis/data/country_boundaries.geojson')",
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"description": "Returns the file suffix (extension) from a file path.",
|
||||
"arguments": [{
|
||||
"arg": "path",
|
||||
"description": "a file path"
|
||||
"description": "a file path or a map layer value. If a map layer layer value is specified then the file source of the layer will be used."
|
||||
}],
|
||||
"examples": [{
|
||||
"expression": "file_suffix('/home/qgis/data/country_boundaries.shp')",
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"description": "Returns true if a path corresponds to a directory.",
|
||||
"arguments": [{
|
||||
"arg": "path",
|
||||
"description": "a file path"
|
||||
"description": "a file path or a map layer value. If a map layer layer value is specified then the file source of the layer will be used."
|
||||
}],
|
||||
"examples": [{
|
||||
"expression": "is_directory('/home/qgis/data/country_boundaries.shp')",
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"description": "Returns true if a path corresponds to a file.",
|
||||
"arguments": [{
|
||||
"arg": "path",
|
||||
"description": "a file path"
|
||||
"description": "a file path or a map layer value. If a map layer layer value is specified then the file source of the layer will be used."
|
||||
}],
|
||||
"examples": [{
|
||||
"expression": "is_file('/home/qgis/data/country_boundaries.shp')",
|
||||
|
||||
@ -2464,14 +2464,24 @@ static QVariant fcnDateTimeFromEpoch( const QVariantList &values, const QgsExpre
|
||||
|
||||
static QVariant fcnExif( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
|
||||
{
|
||||
QString filepath = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
|
||||
const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
|
||||
if ( parent->hasEvalError() )
|
||||
{
|
||||
parent->setEvalErrorString( QObject::tr( "Function `exif` requires a value which represents a possible file path" ) );
|
||||
return QVariant();
|
||||
}
|
||||
QString tag = QgsExpressionUtils::getStringValue( values.at( 1 ), parent );
|
||||
return !tag.isNull() ? QgsExifTools::readTag( filepath, tag ) : QVariant( QgsExifTools::readTags( filepath ) );
|
||||
}
|
||||
|
||||
static QVariant fcnExifGeoTag( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
|
||||
{
|
||||
QString filepath = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
|
||||
const QString filepath = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
|
||||
if ( parent->hasEvalError() )
|
||||
{
|
||||
parent->setEvalErrorString( QObject::tr( "Function `exif_geotag` requires a value which represents a possible file path" ) );
|
||||
return QVariant();
|
||||
}
|
||||
bool ok;
|
||||
return QVariant::fromValue( QgsGeometry( new QgsPoint( QgsExifTools::getGeoTag( filepath, ok ) ) ) );
|
||||
}
|
||||
@ -6515,53 +6525,91 @@ static QVariant fcnEnvVar( const QVariantList &values, const QgsExpressionContex
|
||||
|
||||
static QVariant fcnBaseFileName( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
|
||||
{
|
||||
const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
|
||||
const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
|
||||
if ( parent->hasEvalError() )
|
||||
{
|
||||
parent->setEvalErrorString( QObject::tr( "Function `base_file_name` requires a value which represents a possible file path" ) );
|
||||
return QVariant();
|
||||
}
|
||||
return QFileInfo( file ).completeBaseName();
|
||||
}
|
||||
|
||||
static QVariant fcnFileSuffix( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
|
||||
{
|
||||
const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
|
||||
const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
|
||||
if ( parent->hasEvalError() )
|
||||
{
|
||||
parent->setEvalErrorString( QObject::tr( "Function `file_suffix` requires a value which represents a possible file path" ) );
|
||||
return QVariant();
|
||||
}
|
||||
return QFileInfo( file ).completeSuffix();
|
||||
}
|
||||
|
||||
static QVariant fcnFileExists( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
|
||||
{
|
||||
const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
|
||||
return QFileInfo::exists( file );
|
||||
const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
|
||||
if ( parent->hasEvalError() )
|
||||
{
|
||||
parent->setEvalErrorString( QObject::tr( "Function `file_exists` requires a value which represents a possible file path" ) );
|
||||
return QVariant();
|
||||
} return QFileInfo::exists( file );
|
||||
}
|
||||
|
||||
static QVariant fcnFileName( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
|
||||
{
|
||||
const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
|
||||
return QFileInfo( file ).fileName();
|
||||
const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
|
||||
if ( parent->hasEvalError() )
|
||||
{
|
||||
parent->setEvalErrorString( QObject::tr( "Function `file_name` requires a value which represents a possible file path" ) );
|
||||
return QVariant();
|
||||
} return QFileInfo( file ).fileName();
|
||||
}
|
||||
|
||||
static QVariant fcnPathIsFile( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
|
||||
{
|
||||
const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
|
||||
const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
|
||||
if ( parent->hasEvalError() )
|
||||
{
|
||||
parent->setEvalErrorString( QObject::tr( "Function `is_file` requires a value which represents a possible file path" ) );
|
||||
return QVariant();
|
||||
}
|
||||
return QFileInfo( file ).isFile();
|
||||
}
|
||||
|
||||
static QVariant fcnPathIsDir( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
|
||||
{
|
||||
const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
|
||||
const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
|
||||
if ( parent->hasEvalError() )
|
||||
{
|
||||
parent->setEvalErrorString( QObject::tr( "Function `is_directory` requires a value which represents a possible file path" ) );
|
||||
return QVariant();
|
||||
}
|
||||
return QFileInfo( file ).isDir();
|
||||
}
|
||||
|
||||
static QVariant fcnFilePath( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
|
||||
{
|
||||
const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
|
||||
const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
|
||||
if ( parent->hasEvalError() )
|
||||
{
|
||||
parent->setEvalErrorString( QObject::tr( "Function `file_path` requires a value which represents a possible file path" ) );
|
||||
return QVariant();
|
||||
}
|
||||
return QDir::toNativeSeparators( QFileInfo( file ).path() );
|
||||
}
|
||||
|
||||
static QVariant fcnFileSize( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * )
|
||||
{
|
||||
const QString file = QgsExpressionUtils::getStringValue( values.at( 0 ), parent );
|
||||
const QString file = QgsExpressionUtils::getFilePathValue( values.at( 0 ), parent );
|
||||
if ( parent->hasEvalError() )
|
||||
{
|
||||
parent->setEvalErrorString( QObject::tr( "Function `file_size` requires a value which represents a possible file path" ) );
|
||||
return QVariant();
|
||||
}
|
||||
return QFileInfo( file ).size();
|
||||
}
|
||||
|
||||
static QVariant fcnHash( const QString str, const QCryptographicHash::Algorithm algorithm )
|
||||
static QVariant fcnHash( const QString &str, const QCryptographicHash::Algorithm algorithm )
|
||||
{
|
||||
return QString( QCryptographicHash::hash( str.toUtf8(), algorithm ).toHex() );
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
#include "qgsexpressionnode.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgscolorrampimpl.h"
|
||||
#include "qgsproviderregistry.h"
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
@ -49,6 +50,26 @@ QgsGradientColorRamp QgsExpressionUtils::getRamp( const QVariant &value, QgsExpr
|
||||
return QgsGradientColorRamp();
|
||||
}
|
||||
|
||||
QString QgsExpressionUtils::getFilePathValue( const QVariant &value, QgsExpression *parent )
|
||||
{
|
||||
// if it's a map layer, return the file path of that layer...
|
||||
QString res;
|
||||
if ( QgsMapLayer *layer = getMapLayer( value, parent ) )
|
||||
{
|
||||
const QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( layer->providerType(), layer->source() );
|
||||
res = parts.value( QStringLiteral( "path" ) ).toString();
|
||||
}
|
||||
|
||||
if ( res.isEmpty() )
|
||||
res = value.toString();
|
||||
|
||||
if ( res.isEmpty() && !value.isNull() )
|
||||
{
|
||||
parent->setEvalErrorString( QObject::tr( "Cannot convert value to a file path" ) );
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
///@endcond
|
||||
|
||||
std::tuple<QVariant::Type, int> QgsExpressionUtils::determineResultType( const QString &expression, const QgsVectorLayer *layer, QgsFeatureRequest request, QgsExpressionContext context, bool *foundFeatures )
|
||||
|
||||
@ -419,6 +419,13 @@ class CORE_EXPORT QgsExpressionUtils
|
||||
return qobject_cast<QgsMeshLayer *>( getMapLayer( value, e ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to convert a \a value to a file path.
|
||||
*
|
||||
* \since QGIS 3.24
|
||||
*/
|
||||
static QString getFilePathValue( const QVariant &value, QgsExpression *parent );
|
||||
|
||||
static QVariantList getListValue( const QVariant &value, QgsExpression *parent )
|
||||
{
|
||||
if ( value.type() == QVariant::List || value.type() == QVariant::StringList )
|
||||
|
||||
@ -1990,35 +1990,43 @@ class TestQgsExpression: public QObject
|
||||
QTest::newRow( "base_file_name(NULL)" ) << QStringLiteral( "base_file_name(NULL)" ) << false << QVariant();
|
||||
QTest::newRow( "base_file_name('/home/qgis/test.qgs')" ) << QStringLiteral( "base_file_name('/home/qgis/test.qgs')" ) << false << QVariant( "test" );
|
||||
QTest::newRow( "base_file_name(points.shp)" ) << QStringLiteral( "base_file_name('%1/points.shp')" ).arg( TEST_DATA_DIR ) << false << QVariant( "points" );
|
||||
QTest::newRow( "base_file_name(map layer)" ) << QStringLiteral( "base_file_name('%1')" ).arg( mPointsLayer->id() ) << false << QVariant( "points" );
|
||||
QTest::newRow( "file_exists(NULL)" ) << QStringLiteral( "file_exists(NULL)" ) << false << QVariant();
|
||||
QTest::newRow( "file_exists('/home/qgis/test.qgs')" ) << QStringLiteral( "file_exists('/home/qgis/test.qgs')" ) << false << QVariant( false );
|
||||
QTest::newRow( "file_exists(points.shp)" ) << QStringLiteral( "file_exists('%1/points.shp')" ).arg( TEST_DATA_DIR ) << false << QVariant( true );
|
||||
QTest::newRow( "file_exists(map layer)" ) << QStringLiteral( "file_exists('%1')" ).arg( mPointsLayer->id() ) << false << QVariant( true );
|
||||
QTest::newRow( "file_name(5)" ) << QStringLiteral( "file_name(5)" ) << false << QVariant( "5" );
|
||||
QTest::newRow( "file_name(NULL)" ) << QStringLiteral( "file_name(NULL)" ) << false << QVariant();
|
||||
QTest::newRow( "file_name('/home/qgis/test.qgs')" ) << QStringLiteral( "file_name('/home/qgis/test.qgs')" ) << false << QVariant( "test.qgs" );
|
||||
QTest::newRow( "file_name(points.shp)" ) << QStringLiteral( "file_name('%1/points.shp')" ).arg( TEST_DATA_DIR ) << false << QVariant( "points.shp" );
|
||||
QTest::newRow( "file_name(map layer)" ) << QStringLiteral( "file_name('%1')" ).arg( mPointsLayer->id() ) << false << QVariant( "points.shp" );
|
||||
QTest::newRow( "file_path(5)" ) << QStringLiteral( "file_path(5)" ) << false << QVariant( "." );
|
||||
QTest::newRow( "file_path(NULL)" ) << QStringLiteral( "file_path(NULL)" ) << false << QVariant();
|
||||
QTest::newRow( "file_path('/home/qgis/test.qgs')" ) << QStringLiteral( "file_path('/home/qgis/test.qgs')" ) << false << QVariant( "/home/qgis" );
|
||||
QTest::newRow( "file_path(points.shp)" ) << QStringLiteral( "file_path('%1/points.shp')" ).arg( TEST_DATA_DIR ) << false << QVariant( TEST_DATA_DIR );
|
||||
QTest::newRow( "file_path(map layer)" ) << QStringLiteral( "file_path('%1')" ).arg( mPointsLayer->id() ) << false << QVariant( TEST_DATA_DIR );
|
||||
QTest::newRow( "file_size(5)" ) << QStringLiteral( "file_size(5)" ) << false << QVariant( 0LL );
|
||||
QTest::newRow( "file_size(NULL)" ) << QStringLiteral( "file_size(NULL)" ) << false << QVariant();
|
||||
QTest::newRow( "file_size('/home/qgis/test.qgs')" ) << QStringLiteral( "file_size('/home/qgis/test.qgs')" ) << false << QVariant( 0LL );
|
||||
QTest::newRow( "file_size(points.shp)" ) << QStringLiteral( "file_size('%1/points.shp')" ).arg( TEST_DATA_DIR ) << false << QVariant( 576LL );
|
||||
QTest::newRow( "file_size(map layer)" ) << QStringLiteral( "file_size('%1')" ).arg( mPointsLayer->id() ) << false << QVariant( 576LL );
|
||||
QTest::newRow( "file_suffix(5)" ) << QStringLiteral( "file_suffix(5)" ) << false << QVariant( "" );
|
||||
QTest::newRow( "file_suffix(NULL)" ) << QStringLiteral( "file_suffix(NULL)" ) << false << QVariant();
|
||||
QTest::newRow( "file_suffix('/home/qgis/test.qgs')" ) << QStringLiteral( "file_suffix('/home/qgis/test.qgs')" ) << false << QVariant( "qgs" );
|
||||
QTest::newRow( "file_suffix(points.shp)" ) << QStringLiteral( "file_suffix('%1/points.shp')" ).arg( TEST_DATA_DIR ) << false << QVariant( "shp" );
|
||||
QTest::newRow( "file_suffix(map layer)" ) << QStringLiteral( "file_suffix('%1')" ).arg( mPointsLayer->id() ) << false << QVariant( "shp" );
|
||||
QTest::newRow( "is_directory(5)" ) << QStringLiteral( "is_directory(5)" ) << false << QVariant( false );
|
||||
QTest::newRow( "is_directory(NULL)" ) << QStringLiteral( "is_directory(NULL)" ) << false << QVariant();
|
||||
QTest::newRow( "is_directory('/home/qgis/test.qgs')" ) << QStringLiteral( "is_directory('/home/qgis/test.qgs')" ) << false << QVariant( false );
|
||||
QTest::newRow( "is_directory(points.shp)" ) << QStringLiteral( "is_directory('%1/points.shp')" ).arg( TEST_DATA_DIR ) << false << QVariant( false );
|
||||
QTest::newRow( "is_directory(valid)" ) << QStringLiteral( "is_directory('%1')" ).arg( TEST_DATA_DIR ) << false << QVariant( true );
|
||||
QTest::newRow( "is_directory(map layer)" ) << QStringLiteral( "is_directory('%1')" ).arg( mPointsLayer->id() ) << false << QVariant( false );
|
||||
QTest::newRow( "is_file(5)" ) << QStringLiteral( "is_file(5)" ) << false << QVariant( false );
|
||||
QTest::newRow( "is_file(NULL)" ) << QStringLiteral( "is_file(NULL)" ) << false << QVariant();
|
||||
QTest::newRow( "is_file('/home/qgis/test.qgs')" ) << QStringLiteral( "is_file('/home/qgis/test.qgs')" ) << false << QVariant( false );
|
||||
QTest::newRow( "is_file(points.shp)" ) << QStringLiteral( "is_file('%1/points.shp')" ).arg( TEST_DATA_DIR ) << false << QVariant( true );
|
||||
QTest::newRow( "is_file(valid)" ) << QStringLiteral( "is_file('%1')" ).arg( TEST_DATA_DIR ) << false << QVariant( false );
|
||||
QTest::newRow( "is_file(map layer)" ) << QStringLiteral( "is_file('%1')" ).arg( mPointsLayer->id() ) << false << QVariant( true );
|
||||
|
||||
// hash functions
|
||||
QTest::newRow( "md5(NULL)" ) << QStringLiteral( "md5(NULL)" ) << false << QVariant();
|
||||
@ -4297,6 +4305,33 @@ class TestQgsExpression: public QObject
|
||||
#endif
|
||||
}
|
||||
|
||||
void testGetFilePathValue()
|
||||
{
|
||||
QgsExpression exp;
|
||||
// NULL value
|
||||
QString path = QgsExpressionUtils::getFilePathValue( QVariant(), &exp );
|
||||
QVERIFY( path.isEmpty() );
|
||||
QVERIFY( !exp.hasEvalError() );
|
||||
|
||||
// value which CANNOT be a file path
|
||||
path = QgsExpressionUtils::getFilePathValue( QVariant::fromValue( QgsGeometry() ), &exp );
|
||||
QVERIFY( path.isEmpty() );
|
||||
QVERIFY( exp.hasEvalError() );
|
||||
QCOMPARE( exp.evalErrorString(), QStringLiteral( "Cannot convert value to a file path" ) );
|
||||
|
||||
// good value
|
||||
exp = QgsExpression();
|
||||
path = QgsExpressionUtils::getFilePathValue( QVariant::fromValue( QStringLiteral( "/home/me/mine.txt" ) ), &exp );
|
||||
QCOMPARE( path, QStringLiteral( "/home/me/mine.txt" ) );
|
||||
QVERIFY( !exp.hasEvalError() );
|
||||
|
||||
// with map layer pointer -- should use layer path
|
||||
exp = QgsExpression();
|
||||
path = QgsExpressionUtils::getFilePathValue( QVariant::fromValue( mPointsLayer ), &exp );
|
||||
QCOMPARE( path, QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/points.shp" ) );
|
||||
QVERIFY( !exp.hasEvalError() );
|
||||
}
|
||||
|
||||
void test_env()
|
||||
{
|
||||
QgsExpressionContext context;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user