diff --git a/CMakeLists.txt b/CMakeLists.txt index d6f289a7db8f..ae54e2cb7f5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -480,6 +480,7 @@ IF (PEDANTIC) ENDIF (NOT USING_NMAKE AND NOT USING_NINJA) # disable warnings + SET(_warnings "${_warnings} /wd4091 ") # 'typedef': ignored on left of '' when no variable is declared (occurs in MS DbgHelp.h header) SET(_warnings "${_warnings} /wd4100 ") # unused formal parameters SET(_warnings "${_warnings} /wd4127 ") # constant conditional expressions (used in Qt template classes) SET(_warnings "${_warnings} /wd4190 ") # 'identifier' has C-linkage specified, but returns UDT 'identifier2' which is incompatible with C @@ -487,6 +488,7 @@ IF (PEDANTIC) SET(_warnings "${_warnings} /wd4244 ") # conversion from '...' to '...' possible loss of data SET(_warnings "${_warnings} /wd4251 ") # needs to have dll-interface to be used by clients of class (occurs in Qt template classes) SET(_warnings "${_warnings} /wd4275 ") # non dll-interface class '...' used as base for dll-interface class '...' + SET(_warnings "${_warnings} /wd4290 ") # c++ exception specification ignored except to indicate a function is not __declspec(nothrow) (occurs in sip generated bindings) SET(_warnings "${_warnings} /wd4456 ") # declaration of '...' hides previous local declaration SET(_warnings "${_warnings} /wd4457 ") # declaration of '...' hides a function parameter SET(_warnings "${_warnings} /wd4458 ") # declaration of '...' hides class member diff --git a/cmake/FindGRASS.cmake b/cmake/FindGRASS.cmake index f71ab77f04d6..337d3491b661 100644 --- a/cmake/FindGRASS.cmake +++ b/cmake/FindGRASS.cmake @@ -167,7 +167,7 @@ IF (UNIX) ENDFOREACH(VERSION_MINOR) ELSE (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") FOREACH (PATH /usr/lib64 /usr/lib) - FOREACH (VERSION grass70, grass72, grass74) + FOREACH (VERSION grass76, grass74, grass72, grass70) LIST(APPEND GRASS_PATHS "${PATH}/${VERSION}") ENDFOREACH (VERSION) ENDFOREACH (PATH) @@ -178,10 +178,10 @@ ENDIF (UNIX) IF (APPLE) IF (GRASS_FIND_VERSION EQUAL 7) LIST(APPEND GRASS_PATHS - /Applications/GRASS-7.0.app/Contents/MacOS - /Applications/GRASS-7.1.app/Contents/MacOS - /Applications/GRASS-7.2.app/Contents/MacOS + /Applications/GRASS-7.6.app/Contents/MacOS /Applications/GRASS-7.4.app/Contents/MacOS + /Applications/GRASS-7.2.app/Contents/MacOS + /Applications/GRASS-7.0.app/Contents/MacOS ) ENDIF () LIST(APPEND GRASS_PATHS /Applications/GRASS.app/Contents/Resources) diff --git a/debian/python-qgis-common.install b/debian/python-qgis-common.install index 40aec0b0b1bf..7a2577709de8 100644 --- a/debian/python-qgis-common.install +++ b/debian/python-qgis-common.install @@ -1,2 +1,4 @@ usr/share/qgis/python/* +usr/lib/python*/*-packages/qgis/processing/* +usr/lib/python*/*-packages/qgis/testing/* usr/lib/python*/*-packages/PyQt?/uic/widget-plugins/qgis_customwidgets.py diff --git a/debian/python-qgis.install.in b/debian/python-qgis.install.in index 730c956e236a..a686cede0572 100644 --- a/debian/python-qgis.install.in +++ b/debian/python-qgis.install.in @@ -4,6 +4,5 @@ usr/lib/python*/*-packages/qgis/core/* usr/lib/python*/*-packages/qgis/gui/* usr/lib/python*/*-packages/qgis/analysis/* usr/lib/python*/*-packages/qgis/PyQt/* -usr/lib/python*/*-packages/qgis/testing/* usr/lib/python*/*-packages/qgis/server/* #sid buster bionic cosmic#usr/lib/python*/*-packages/qgis/3d/* diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 85ccd1b63351..9ad52ff23cbc 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -78,6 +78,7 @@ IF(WITH_APIDOC) ${CMAKE_SOURCE_DIR}/src/core/raster ${CMAKE_SOURCE_DIR}/src/core/scalebar ${CMAKE_SOURCE_DIR}/src/core/symbology + ${CMAKE_SOURCE_DIR}/src/core/validity ${CMAKE_SOURCE_DIR}/src/gui ${CMAKE_SOURCE_DIR}/src/gui/auth ${CMAKE_SOURCE_DIR}/src/gui/attributetable @@ -87,7 +88,9 @@ IF(WITH_APIDOC) ${CMAKE_SOURCE_DIR}/src/gui/layertree ${CMAKE_SOURCE_DIR}/src/gui/layout ${CMAKE_SOURCE_DIR}/src/gui/locator + ${CMAKE_SOURCE_DIR}/src/gui/ogr ${CMAKE_SOURCE_DIR}/src/gui/processing + ${CMAKE_SOURCE_DIR}/src/gui/processing/models ${CMAKE_SOURCE_DIR}/src/gui/raster ${CMAKE_SOURCE_DIR}/src/gui/symbology ${CMAKE_SOURCE_DIR}/src/analysis @@ -96,6 +99,7 @@ IF(WITH_APIDOC) ${CMAKE_SOURCE_DIR}/src/analysis/processing ${CMAKE_SOURCE_DIR}/src/analysis/raster ${CMAKE_SOURCE_DIR}/src/analysis/vector + ${CMAKE_SOURCE_DIR}/src/analysis/vector/geometry_checker ${CMAKE_SOURCE_DIR}/src/3d ${CMAKE_SOURCE_DIR}/src/3d/chunks ${CMAKE_SOURCE_DIR}/src/3d/symbols diff --git a/ms-windows/QGIS-Installer.nsi b/ms-windows/QGIS-Installer.nsi index 2ea56fde5902..618ce01a818f 100644 --- a/ms-windows/QGIS-Installer.nsi +++ b/ms-windows/QGIS-Installer.nsi @@ -37,14 +37,15 @@ RequestExecutionLevel admin !addplugindir osgeo4w/untgz !addplugindir osgeo4w/nsis +!addplugindir osgeo4w/inetc ;---------------------------------------------------------------------------------------------------------------------------- ;Publisher variables !define PUBLISHER "QGIS Development Team" -!define WEB_SITE "http://qgis.org" -!define WIKI_PAGE "http://qgis.org/en/docs/" +!define WEB_SITE "https://qgis.org" +!define WIKI_PAGE "https://qgis.org/en/docs/" ;---------------------------------------------------------------------------------------------------------------------------- @@ -404,9 +405,9 @@ Function DownloadDataSet download: SetShellVarContext current InitPluginsDir - NSISdl::download "$HTTP_PATH/$ARCHIVE_NAME" "$TEMP\$ARCHIVE_NAME" - Pop $0 - StrCmp $0 "success" download_ok download_failed + inetc::get /caption "$ARCHIVE_NAME" /canceltext "Cancel" "$HTTP_PATH/$ARCHIVE_NAME" "$TEMP\$ARCHIVE_NAME" /end + Pop $0 # return value = exit code, "OK" means OK + StrCmp $0 "OK" download_ok download_failed download_ok: InitPluginsDir @@ -443,7 +444,7 @@ Section /O "North Carolina Data Set" SecNorthCarolinaSDB ;Set the size (in KB) of the unpacked archive file AddSize 293314 - StrCpy $HTTP_PATH "http://grass.osgeo.org/sampledata" + StrCpy $HTTP_PATH "https://grass.osgeo.org/sampledata" StrCpy $ARCHIVE_NAME "nc_spm_latest.tar.gz" StrCpy $EXTENDED_ARCHIVE_NAME "North Carolina" StrCpy $ORIGINAL_UNTAR_FOLDER "nc_spm_08" @@ -461,7 +462,7 @@ Section /O "South Dakota (Spearfish) Data Set" SecSpearfishSDB ;Set the size (in KB) of the unpacked archive file AddSize 42171 - StrCpy $HTTP_PATH "http://grass.osgeo.org/sampledata" + StrCpy $HTTP_PATH "https://grass.osgeo.org/sampledata" StrCpy $ARCHIVE_NAME "spearfish_grass60data-0.3.tar.gz" StrCpy $EXTENDED_ARCHIVE_NAME "South Dakota (Spearfish)" StrCpy $ORIGINAL_UNTAR_FOLDER "spearfish60" @@ -479,7 +480,7 @@ Section /O "Alaska Data Set" SecAlaskaSDB ;Set the size (in KB) of the unpacked archive file AddSize 33914 - StrCpy $HTTP_PATH "http://qgis.org/downloads/data" + StrCpy $HTTP_PATH "https://qgis.org/downloads/data" StrCpy $ARCHIVE_NAME "qgis_sample_data.tar.gz" StrCpy $EXTENDED_ARCHIVE_NAME "Alaska" StrCpy $ORIGINAL_UNTAR_FOLDER "qgis_sample_data" diff --git a/ms-windows/osgeo4w/creatensis.pl b/ms-windows/osgeo4w/creatensis.pl index fc2edbc448f6..820650c43057 100755 --- a/ms-windows/osgeo4w/creatensis.pl +++ b/ms-windows/osgeo4w/creatensis.pl @@ -78,6 +78,9 @@ BEGIN system "wget $wgetopt -c http://qgis.org/downloads/Untgz.zip" unless -f "Untgz.zip"; die "download of Untgz.zip failed" if $?; +system "wget $wgetopt -c https://qgis.org/downloads/Inetc.zip" unless -f "Inetc.zip"; +die "download of Inetc.zip failed" if $?; + my %dep; my %file; my %lic; @@ -386,6 +389,12 @@ sub getDeps { die "unpacking Untgz.zip failed" if $?; } +unless(-d "inetc") { + mkdir "inetc", 0755; + system "unzip -p $packages/Inetc.zip Plugins/x86-ansi/INetC.dll >inetc/INetC.dll"; + die "unpacking Inetc.zip failed" if $?; +} + chdir ".."; diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 602f6c5e391d..c5196c7e14fe 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -116,6 +116,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/src/core/raster ${CMAKE_SOURCE_DIR}/src/core/scalebar ${CMAKE_SOURCE_DIR}/src/core/symbology + ${CMAKE_SOURCE_DIR}/src/core/validity ${CMAKE_SOURCE_DIR}/src/plugins ${CMAKE_BINARY_DIR} # qgsconfig.h, qgsversion.h diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in index 581e602af2d9..bf1998936ff0 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsfeaturepool.sip.in @@ -27,12 +27,16 @@ A feature pool is based on a vector layer and caches features. #include "qgsfeaturepool.h" %End public: + QgsFeaturePool( QgsVectorLayer *layer ); +%Docstring +Creates a new feature pool for ``layer``. +%End virtual ~QgsFeaturePool(); bool getFeature( QgsFeatureId id, QgsFeature &feature, QgsFeedback *feedback = 0 ); %Docstring -Retrieve the feature with the specified ``id`` into ``feature``. +Retrieves the feature with the specified ``id`` into ``feature``. It will be retrieved from the cache or from the underlying layer if unavailable. If the feature is neither available from the cache nor from the layer it will return false. If ``feedback`` is specified, the call may return if the feedback is canceled. @@ -55,7 +59,7 @@ Implementations will remove the feature from the layer or from the data provider QgsVectorLayer *layer() const; %Docstring -Get a pointer to the underlying layer. +Gets a pointer to the underlying layer. May return a ``None`` if the layer has been deleted. This must only be called from the main thread. %End diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in index 9e28feaba7d0..1b24c68b9290 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheck.sip.in @@ -13,10 +13,90 @@ class QgsGeometryCheck { %Docstring -This class manages all known geometry check factories. +This class implements a geometry check. -QgsGeometryCheckRegistry is not usually directly created, but rather accessed through -:py:func:`QgsAnalysis.geometryCheckRegistry()` +Geometry checks run over a set of features and can detect errors like topological +or other issues which are reported in the geometry validation panel in QGIS and +help a user to create valid geometries. + +Implementing a custom geometry check consists of the following parts + +### Writing the check + +A new subclass of QgsGeometryCheck needs to be written and at least the following +abstract methods need to be implemented: + +- `compatibleGeometryTypes()` + +A list of geometry types to which this check applies + +- `resolutionMethods()` + +A list of names for (automated) resolution methods that can be used to fix errors of this type + +- `description()` + +A description for the geometry check. + +- `id()` + +A unique id for this check. + +- `checkType()` + +One of QgsGeometryCheck.LayerCheck, QgsGeometryCheck.FeatureCheck,QgsGeometryCheck.FeatureNodeCheck + +- \link collectErrors() `collectErrors(featurePools, errors, messages, feedback, ids)`\endlink + +This method will be called to validate geometries. All geometries which should be validated are passed +into this method with the parameter ids and should be retrieved from the available featurePools to make +use of caching. New errors should be appended to the error list and other message strings to messages. +The method needs to return a tuple (errors, messages). + +### Creating a geometry check factory + +A Geometry check factory manages meta information for checks. There will always be one single +geometry check factory created per check type, but it's possible that multiple QgsGeometryCheck +instances are created and used in parallel. + +A new subclass of QgsGeometryCheckFactory needs to be written and at least the following +abstract methods need to be implemented: + +- \link QgsGeometryCheckFactory.createGeometryCheck() `createGeometryCheck(context, configuration)`\endlink + +Needs to return a new subclassed QgsGeometryCheck object that has been written in the previous step. + +- \link QgsGeometryCheckFactory.id() `id()\endlink + +A unique id for this geometry check. + +- \link QgsGeometryCheckFactory.description() `description()\endlink + +A description for this geometry check that can be presented to the user for more explanation. + +- \link QgsGeometryCheckFactory.isCompatible() `QgsGeometryCheckFactory.isCompatible(layer)`\endlink + +Returns a boolean that determines if this check is available for a given layer. This often +checks for the geometry type of the layer. + +- \link QgsGeometryCheckFactory.flags() `flags()`\endlink + +Returns additional flags for a geometry check. If unsure return QgsGeometryCheck.AvailableInValidation. + +- \link QgsGeometryCheckFactory.checkType() `checkType()`\endlink + +Returns the type of this geometry check. + +### Registering the geometry check + +Finally the geometry check factory needs to be registered in QGIS, so the system +is aware of the available geometry checks. + +.. code-block:: python + + # Make sure you always keep a + checkFactory = MyGeometryCheckFactory() + QgsAnalysis.geometryCheckRegistry().registerGeometryCheck(checkFactory) .. note:: @@ -164,6 +244,10 @@ Returns the context protected: + + + + }; /************************************************************************ diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in index 2897170b279b..643049ec0428 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerror.sip.in @@ -27,8 +27,21 @@ This represents an error reported by a geometry check. #include "qgsgeometrycheckerror.h" %End public: - enum Status { StatusPending, StatusFixFailed, StatusFixed, StatusObsolete }; - enum ValueType { ValueLength, ValueArea, ValueOther }; + + enum Status + { + StatusPending, + StatusFixFailed, + StatusFixed, + StatusObsolete + }; + + enum ValueType + { + ValueLength, + ValueArea, + ValueOther + }; QgsGeometryCheckError( const QgsGeometryCheck *check, const QgsGeometryCheckerUtils::LayerFeature &layerFeature, @@ -36,25 +49,98 @@ This represents an error reported by a geometry check. QgsVertexId vidx = QgsVertexId(), const QVariant &value = QVariant(), ValueType valueType = ValueOther ); +%Docstring +Create a new geometry check error with the parent ``check`` and for the +``layerFeature`` pair at the ``errorLocation``. Optionally the vertex can be +specified via ``vixd`` and a ``value`` with its ``value`` Type for +additional information. +%End virtual ~QgsGeometryCheckError(); const QgsGeometryCheck *check() const; +%Docstring +The geometry check that created this error. +%End + const QString &layerId() const; +%Docstring +The id of the layer on which this error has been detected. +%End + QgsFeatureId featureId() const; +%Docstring +The id of the feature on which this error has been detected. +%End + QgsGeometry geometry() const; +%Docstring +The geometry of the error in map units. +%End + virtual QgsRectangle affectedAreaBBox() const; +%Docstring +The bounding box of the affected area of the error. +%End + virtual QString description() const; +%Docstring +The error description. By default the description of the parent check +will be returned. +%End + const QgsPointXY &location() const; +%Docstring +The location of the error in map units. +%End + QVariant value() const; +%Docstring +An additional value for the error. +Lengths and areas are provided in map units. + +.. seealso:: :py:func:`valueType` +%End + ValueType valueType() const; +%Docstring +The type of the value. + +.. seealso:: :py:func:`value` +%End + const QgsVertexId &vidx() const; +%Docstring +The id of the affected vertex. May be valid or not, depending on the +check. +%End + Status status() const; +%Docstring +The status of the error. +%End + QString resolutionMessage() const; +%Docstring +A message with details, how the error has been resolved. +%End + void setFixed( int method ); +%Docstring +Set the status to fixed and specify the ``method`` that has been used to +fix the error. +%End + void setFixFailed( const QString &reason ); +%Docstring +Set the error status to failed and specify the ``reason`` for failure. +%End + void setObsolete(); +%Docstring +Set the error status to obsolete. +%End virtual bool isEqual( QgsGeometryCheckError *other ) const; %Docstring @@ -71,12 +157,13 @@ If this returns true, it can be used to update existing errors after re-checking virtual void update( const QgsGeometryCheckError *other ); %Docstring -Update this error with the information from \other. +Update this error with the information from ``other``. Will be used to update existing errors whenever they are re-checked. %End protected: + QgsGeometryCheckError( const QgsGeometryCheck *check, const QString &layerId, QgsFeatureId featureId, @@ -85,6 +172,14 @@ Will be used to update existing errors whenever they are re-checked. QgsVertexId vidx = QgsVertexId(), const QVariant &value = QVariant(), ValueType valueType = ValueOther ); +%Docstring +Create a new geometry check error with the parent ``check`` and for the +layer with ``layerId`` and ``featureId``. +The ``geometry`` of the error and the ``errorLocation`` need to be +specified in map coordinates. +Optionally the vertex can be specified via ``vixd`` and a ``value`` with +its ``value`` Type for additional information. +%End }; diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in index abd27671a9f6..a1c57694a1cf 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckerutils.sip.in @@ -10,7 +10,6 @@ - class QgsGeometryCheckerUtils { %Docstring @@ -28,8 +27,16 @@ Contains utilities required for geometry checks. #include "qgsgeometrycheckerutils.h" %End public: + class LayerFeature { +%Docstring + +A layer feature combination to uniquely identify and access a feature in +a set of layers. + +.. versionadded:: 3.4 +%End %TypeHeaderCode #include "qgsgeometrycheckerutils.h" @@ -62,7 +69,11 @@ Returns the geometry of this feature. If useMapCrs was specified, it will already be reprojected into the CRS specified in the context specified in the constructor. %End + QString id() const; +%Docstring +Returns a combination of the layerId and the feature id. +%End bool operator==( const QgsGeometryCheckerUtils::LayerFeature &other ) const; bool operator!=( const QgsGeometryCheckerUtils::LayerFeature &other ) const; @@ -75,6 +86,12 @@ Returns if the geometry is reprojected to the map CRS or not. class LayerFeatures { +%Docstring + +Contains a set of layers and feature ids in those layers to pass to a geometry check. + +.. versionadded:: 3.4 +%End %TypeHeaderCode #include "qgsgeometrycheckerutils.h" diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckfactory.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckfactory.sip.in index c2dea49db5a2..f3bd002848e8 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckfactory.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckfactory.sip.in @@ -34,7 +34,7 @@ A factory for geometry checks. virtual QgsGeometryCheck *createGeometryCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ) const = 0 /Factory/; %Docstring -Create a new geometry check with ``context`` and ``configuration``. +Creates a new geometry check with ``context`` and ``configuration``. %End virtual QString id() const = 0; @@ -66,6 +66,13 @@ The type of this check. template class QgsGeometryCheckFactoryT : QgsGeometryCheckFactory { +%Docstring +Template to create a factory for a geometry check. + +.. note:: + + Not available in Python bindings. +%End %TypeHeaderCode #include "qgsgeometrycheckfactory.h" diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckregistry.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckregistry.sip.in index 0e1c5f107c4c..033206e00109 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckregistry.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgsgeometrycheckregistry.sip.in @@ -50,14 +50,14 @@ Ownership is transferred to the caller. QList geometryCheckFactories( QgsVectorLayer *layer, QgsGeometryCheck::CheckType type, QgsGeometryCheck::Flags flags = 0 ) const; %Docstring -Get all geometry check factories that are compatible with ``layer`` and have all of the ``flags`` set. +Returns all geometry check factories that are compatible with ``layer`` and have all of the ``flags`` set. .. versionadded:: 3.4 %End void registerGeometryCheck( QgsGeometryCheckFactory *checkFactory /Transfer/ ); %Docstring -Register a new geometry check factory. +Registers a new geometry check factory. .. versionadded:: 3.4 %End diff --git a/python/analysis/auto_generated/vector/geometry_checker/qgssinglegeometrycheck.sip.in b/python/analysis/auto_generated/vector/geometry_checker/qgssinglegeometrycheck.sip.in index 9c50a9accf66..b78e0e58902d 100644 --- a/python/analysis/auto_generated/vector/geometry_checker/qgssinglegeometrycheck.sip.in +++ b/python/analysis/auto_generated/vector/geometry_checker/qgssinglegeometrycheck.sip.in @@ -28,13 +28,17 @@ An error from a QgsSingleGeometryCheck. #include "qgssinglegeometrycheck.h" %End public: + QgsSingleGeometryCheckError( const QgsSingleGeometryCheck *check, const QgsGeometry &geometry, const QgsGeometry &errorLocation, const QgsVertexId &vertexId = QgsVertexId() ); +%Docstring +Creates a new single geometry check error. +%End virtual ~QgsSingleGeometryCheckError(); virtual void update( const QgsSingleGeometryCheckError *other ); %Docstring -Update this error with the information from \other. +Update this error with the information from ``other``. Will be used to update existing errors whenever they are re-checked. %End @@ -93,7 +97,11 @@ The single error can be obtained via singleError. #include "qgssinglegeometrycheck.h" %End public: + QgsGeometryCheckErrorSingle( QgsSingleGeometryCheckError *singleError, const QgsGeometryCheckerUtils::LayerFeature &layerFeature ); +%Docstring +Creates a new error for a :py:class:`QgsSingleGeometryCheck`. +%End QgsSingleGeometryCheckError *singleError() const; %Docstring @@ -119,8 +127,12 @@ Subclasses need to implement the processGeometry method. #include "qgssinglegeometrycheck.h" %End public: + QgsSingleGeometryCheck( const QgsGeometryCheckContext *context, const QVariantMap &configuration ); +%Docstring +Creates a new single geometry check. +%End virtual void collectErrors( const QMap &featurePools, QList &errors, diff --git a/python/core/auto_generated/layout/qgslayoutitemmapitem.sip.in b/python/core/auto_generated/layout/qgslayoutitemmapitem.sip.in index 99e41f4462f1..5ea1247d472b 100644 --- a/python/core/auto_generated/layout/qgslayoutitemmapitem.sip.in +++ b/python/core/auto_generated/layout/qgslayoutitemmapitem.sip.in @@ -22,6 +22,15 @@ An item which is drawn inside a QgsLayoutItemMap, e.g., a grid or map overview. %End public: + enum StackingPosition + { + StackBelowMap, + StackBelowMapLayer, + StackAboveMapLayer, + StackBelowMapLabels, + StackAboveMapLabels, + }; + QgsLayoutItemMapItem( const QString &name, QgsLayoutItemMap *map ); %Docstring Constructor for QgsLayoutItemMapItem, attached to the specified ``map``. @@ -111,6 +120,60 @@ Returns whether the item will be drawn. virtual bool usesAdvancedEffects() const; %Docstring Returns true if the item is drawn using advanced effects, such as blend modes. +%End + + StackingPosition stackingPosition() const; +%Docstring +Returns the item's stacking position, which specifies where the in the map's +stack the item should be rendered. + +.. seealso:: :py:func:`setStackingPosition` + +.. seealso:: :py:func:`stackingLayer` + +.. versionadded:: 3.6 +%End + + void setStackingPosition( StackingPosition position ); +%Docstring +Sets the item's stacking ``position``, which specifies where the in the map's +stack the item should be rendered. + +.. seealso:: :py:func:`stackingPosition` + +.. seealso:: :py:func:`setStackingLayer` + +.. versionadded:: 3.6 +%End + + QgsMapLayer *stackingLayer() const; +%Docstring +Returns the item's stacking layer, which specifies where the in the map's +stack the item should be rendered. + +This setting is only used when stackingPosition() is StackBelowMapLayer or +StackAboveMapLayer. + +.. seealso:: :py:func:`setStackingLayer` + +.. seealso:: :py:func:`stackingPosition` + +.. versionadded:: 3.6 +%End + + void setStackingLayer( QgsMapLayer *layer ); +%Docstring +Sets the item's stacking ``layer``, which specifies where the in the map's +stack the item should be rendered. + +This setting is only used when stackingPosition() is StackBelowMapLayer or +StackAboveMapLayer. + +.. seealso:: :py:func:`stackingLayer` + +.. seealso:: :py:func:`setStackingPosition` + +.. versionadded:: 3.6 %End protected: @@ -119,9 +182,9 @@ Returns true if the item is drawn using advanced effects, such as blend modes. -}; +}; class QgsLayoutItemMapItemStack { @@ -180,9 +243,13 @@ map item after restoration from XML. .. seealso:: :py:func:`readXml` %End - void drawItems( QPainter *painter ); + void drawItems( QPainter *painter, bool ignoreStacking = true ); %Docstring Draws the items from the stack on a specified ``painter``. + +If ``ignoreStacking`` is true, then all items will be drawn, regardless +of their actual stacking position settings. If it is false, only items +which are set to stack above the map item will be drawn. %End bool containsAdvancedEffects() const; diff --git a/python/core/auto_generated/layout/qgslayoutitemmapoverview.sip.in b/python/core/auto_generated/layout/qgslayoutitemmapoverview.sip.in index 814e7be8ba6a..dffa90623663 100644 --- a/python/core/auto_generated/layout/qgslayoutitemmapoverview.sip.in +++ b/python/core/auto_generated/layout/qgslayoutitemmapoverview.sip.in @@ -102,6 +102,14 @@ Returns a list of QgsLayoutItemMapOverviews contained by the stack. virtual bool readXml( const QDomElement &elem, const QDomDocument &doc, const QgsReadWriteContext &context ); + QList< QgsMapLayer * > modifyMapLayerList( const QList< QgsMapLayer * > &layers ); +%Docstring +Alters the list of map ``layers`` which will be rendered for the link map item, inserting +temporary layers which represent overview extents as required. + +.. versionadded:: 3.6 +%End + }; class QgsLayoutItemMapOverview : QgsLayoutItemMapItem @@ -127,6 +135,7 @@ Constructor for QgsLayoutItemMapOverview. :param name: friendly display name for overview :param map: QgsLayoutItemMap the overview is attached to %End + ~QgsLayoutItemMapOverview(); virtual void draw( QPainter *painter ); @@ -216,6 +225,17 @@ Sets whether the extent of the map is forced to center on the overview %Docstring Reconnects signals for overview map, so that overview correctly follows changes to source map's extent. +%End + + QgsVectorLayer *asMapLayer(); +%Docstring +Returns a vector layer to render as part of the QgsLayoutItemMap render, containing +a feature representing the overview extent (and with an appropriate renderer set matching +the overview's frameSymbol() ). + +Ownership of the layer remain with the overview item. + +.. versionadded:: 3.6 %End public slots: diff --git a/python/core/auto_generated/qgsapplication.sip.in b/python/core/auto_generated/qgsapplication.sip.in index 691d8da98492..43a679e5ae5d 100644 --- a/python/core/auto_generated/qgsapplication.sip.in +++ b/python/core/auto_generated/qgsapplication.sip.in @@ -668,6 +668,13 @@ Returns the application's image cache, used for caching resampled versions of ra Returns the application's network content registry used for fetching temporary files during QGIS session .. versionadded:: 3.2 +%End + + static QgsValidityCheckRegistry *validityCheckRegistry(); +%Docstring +Returns the application's validity check registry, used for managing validity checks. + +.. versionadded:: 3.6 %End static QgsSymbolLayerRegistry *symbolLayerRegistry(); diff --git a/python/core/auto_generated/qgsauxiliarystorage.sip.in b/python/core/auto_generated/qgsauxiliarystorage.sip.in index 105024cc655d..81eda639f697 100644 --- a/python/core/auto_generated/qgsauxiliarystorage.sip.in +++ b/python/core/auto_generated/qgsauxiliarystorage.sip.in @@ -291,7 +291,7 @@ the target filename if the auxiliary storage is opened in copy mode. QString errorString() const; %Docstring Returns the underlying error string describing potential errors -hapenning in saveAs(). Empty by default. +happening in saveAs(). Empty by default. .. versionadded:: 3.4 %End diff --git a/python/core/auto_generated/qgsblockingnetworkrequest.sip.in b/python/core/auto_generated/qgsblockingnetworkrequest.sip.in deleted file mode 100644 index f4c231aa90a2..000000000000 --- a/python/core/auto_generated/qgsblockingnetworkrequest.sip.in +++ /dev/null @@ -1,146 +0,0 @@ -/************************************************************************ - * This file has been generated automatically from * - * * - * src/core/qgsblockingnetworkrequest.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ - - - -class QgsBlockingNetworkRequest : QObject -{ -%Docstring -A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy -and authentication settings. - -This class should be used whenever a blocking network request is required. Unlike implementations -which rely on QApplication.processEvents() or creation of a QEventLoop, this class is completely -thread safe and can be used on either the main thread or background threads without issue. - -Redirects are automatically handled by the class. - -After completion of a request, the reply content should be retrieved by calling getReplyContent(). -This method returns a QgsNetworkReplyContent container, which is safe and cheap to copy and pass -between threads without issue. - -.. versionadded:: 3.6 -%End - -%TypeHeaderCode -#include "qgsblockingnetworkrequest.h" -%End - public: - - enum ErrorCode - { - NoError, - NetworkError, - TimeoutError, - ServerExceptionError, - }; - - explicit QgsBlockingNetworkRequest(); -%Docstring -Constructor for QgsBlockingNetworkRequest -%End - - ~QgsBlockingNetworkRequest(); - - ErrorCode get( QNetworkRequest &request, bool forceRefresh = false, QgsFeedback *feedback = 0 ); -%Docstring -Performs a "get" operation on the specified ``request``. - -If ``forceRefresh`` is false then previously cached replies may be used for the request. If -it is set to true then a new query is always performed. - -If an authCfg() has been set, then any authentication configuration required will automatically be applied to -``request``. There is no need to manually apply the authentication to the request prior to calling -this method. - -The optional ``feedback`` argument can be used to abort ongoing requests. - -The method will return NoError if the get operation was successful. The contents of the reply can be retrieved -by calling reply(). - -If an error was encountered then a specific ErrorCode will be returned, and a detailed error message -can be retrieved by calling errorMessage(). - -.. seealso:: :py:func:`post` -%End - - ErrorCode post( QNetworkRequest &request, const QByteArray &data, bool forceRefresh = false, QgsFeedback *feedback = 0 ); -%Docstring -Performs a "post" operation on the specified ``request``, using the given ``data``. - -If ``forceRefresh`` is false then previously cached replies may be used for the request. If -it is set to true then a new query is always performed. - -If an authCfg() has been set, then any authentication configuration required will automatically be applied to -``request``. There is no need to manually apply the authentication to the request prior to calling -this method. - -The optional ``feedback`` argument can be used to abort ongoing requests. - -The method will return NoError if the get operation was successful. The contents of the reply can be retrieved -by calling reply(). - -If an error was encountered then a specific ErrorCode will be returned, and a detailed error message -can be retrieved by calling errorMessage(). - -.. seealso:: :py:func:`get` -%End - - QString errorMessage() const; -%Docstring -Returns the error message string, after a get() or post() request has been made.\ -%End - - QgsNetworkReplyContent reply() const; -%Docstring -Returns the content of the network reply, after a get() or post() request has been made. -%End - - QString authCfg() const; -%Docstring -Returns the authentication config id which will be used during the request. - -.. seealso:: :py:func:`setAuthCfg` -%End - - void setAuthCfg( const QString &authCfg ); -%Docstring -Sets the authentication config id which should be used during the request. - -.. seealso:: :py:func:`authCfg` -%End - - public slots: - - void abort(); -%Docstring -Aborts the network request immediately. -%End - - signals: - - void downloadProgress( qint64, qint64 ); -%Docstring -Emitted when when data arrives during a request. -%End - - void downloadFinished(); -%Docstring -Emitted once a request has finished downloading. -%End - -}; - - -/************************************************************************ - * This file has been generated automatically from * - * * - * src/core/qgsblockingnetworkrequest.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ diff --git a/python/core/auto_generated/qgsbrowsermodel.sip.in b/python/core/auto_generated/qgsbrowsermodel.sip.in index cf054c1759b9..2cfeeb25406f 100644 --- a/python/core/auto_generated/qgsbrowsermodel.sip.in +++ b/python/core/auto_generated/qgsbrowsermodel.sip.in @@ -117,6 +117,19 @@ items, i.e. it does not fetch children. %End + QModelIndex findUri( const QString &uri, QModelIndex index = QModelIndex() ); +%Docstring +Returns index of layer item with given uri. It only searches in currently fetched +items, i.e. it does not fetch children. + +:param uri: item uri +:param index: the current index of the parent (to search for children) + +:return: model index, invalid if item not found + +.. versionadded:: 3.6 +%End + void connectItem( QgsDataItem *item ) /Deprecated/; %Docstring diff --git a/python/core/auto_generated/qgsdataitem.sip.in b/python/core/auto_generated/qgsdataitem.sip.in index ca56eadf8fae..442bb45d9df6 100644 --- a/python/core/auto_generated/qgsdataitem.sip.in +++ b/python/core/auto_generated/qgsdataitem.sip.in @@ -201,6 +201,7 @@ Items that return valid URI will be returned in mime data when dragging a select Fast, Collapse, Rename, + Delete, }; typedef QFlags Capabilities; @@ -485,6 +486,11 @@ Returns the string representation of the given ``layerType`` Returns the icon name of the given ``layerType`` .. versionadded:: 3 +%End + + virtual bool deleteLayer(); +%Docstring +Delete this layer item %End protected: diff --git a/python/core/auto_generated/qgsdataprovider.sip.in b/python/core/auto_generated/qgsdataprovider.sip.in index d02419fcfd88..a172819a0d8e 100644 --- a/python/core/auto_generated/qgsdataprovider.sip.in +++ b/python/core/auto_generated/qgsdataprovider.sip.in @@ -392,7 +392,7 @@ Returns true if metadata was successfully written to the data provider. void fullExtentCalculated(); %Docstring -Emitted whenever a deffered extent calculation is completed by the provider. +Emitted whenever a deferred extent calculation is completed by the provider. Layers should connect to this signal and update their cached extents whenever it is emitted. diff --git a/python/core/auto_generated/qgsdistancearea.sip.in b/python/core/auto_generated/qgsdistancearea.sip.in index 350766db54ca..ce03037b5aa1 100644 --- a/python/core/auto_generated/qgsdistancearea.sip.in +++ b/python/core/auto_generated/qgsdistancearea.sip.in @@ -64,6 +64,19 @@ Sets source spatial reference system ``crs``. Returns the source spatial reference system. .. seealso:: :py:func:`setSourceCrs` + +.. seealso:: :py:func:`ellipsoidCrs` +%End + + QgsCoordinateReferenceSystem ellipsoidCrs() const; +%Docstring +Returns the ellipsoid (destination) spatial reference system. + +.. seealso:: :py:func:`sourceCrs` + +.. seealso:: :py:func:`ellipsoid` + +.. versionadded:: 3.6 %End bool setEllipsoid( const QString &ellipsoid ); @@ -99,6 +112,8 @@ ellipsoid if a valid ellipsoid has been set. .. seealso:: :py:func:`setEllipsoid` .. seealso:: :py:func:`willUseEllipsoid` + +.. seealso:: :py:func:`ellipsoidCrs` %End double ellipsoidSemiMajor() const; @@ -353,6 +368,46 @@ Datum of Australia Technical Manual", Chapter 4. --> latitudes outside these bounds cause the calculations to become unstable and can return invalid results .. versionadded:: 3.0 +%End + + QVector > geodesicLine( const QgsPointXY &p1, const QgsPointXY &p2, double interval, bool breakLine = false ) const; +%Docstring +Calculates the geodesic line between ``p1`` and ``p2``, which represents the shortest path on the +ellipsoid between these two points. + +The ellipsoid settings defined on this QgsDistanceArea object will be used during the calculations. + +``p1`` and ``p2`` must be in the sourceCrs() of this QgsDistanceArea object. The returned line +will also be in this same CRS. + +The ``interval`` parameter gives the maximum distance between points on the computed line. +This argument is always specified in meters. A shorter distance results in a denser line, +at the cost of extra computing time. + +If the geodesic line crosses the international date line and ``breakLine`` is true, then +the line will be split into two parts, broken at the date line. In this case the function +will return two lines, corresponding to the portions at either side of the date line. + +.. versionadded:: 3.6 +%End + + double latitudeGeodesicCrossesDateLine( const QgsPointXY &p1, const QgsPointXY &p2, double &fractionAlongLine /Out/ ) const; +%Docstring +Calculates the latitude at which the geodesic line joining ``p1`` and ``p2`` crosses +the international date line (longitude +/- 180 degrees). + +The ellipsoid settings defined on this QgsDistanceArea object will be used during the calculations. + +``p1`` and ``p2`` must be in the ellipsoidCrs() of this QgsDistanceArea object. The returned latitude +will also be in this same CRS. + +:param p1: Starting point, in ellipsoidCrs() +:param p2: Ending point, in ellipsoidCrs() + +:return: - the latitude at which the geodesic crosses the date line + - fractionAlongLine: will be set to the fraction along the geodesic line joining ``p1`` to ``p2`` at which the date line crossing occurs. + +.. versionadded:: 3.6 %End }; diff --git a/python/core/auto_generated/qgslegendrenderer.sip.in b/python/core/auto_generated/qgslegendrenderer.sip.in index 627f65b9a4f4..743d2c7cff7e 100644 --- a/python/core/auto_generated/qgslegendrenderer.sip.in +++ b/python/core/auto_generated/qgslegendrenderer.sip.in @@ -27,35 +27,70 @@ All spacing and sizes are in millimeters. #include "qgslegendrenderer.h" %End public: + QgsLegendRenderer( QgsLayerTreeModel *legendModel, const QgsLegendSettings &settings ); %Docstring -Construct legend renderer. The ownership of legend model does not change +Constructor for QgsLegendRenderer. The ownership of the legend model is not changed, +and the model must exist for the lifetime of this renderer. %End QSizeF minimumSize(); %Docstring -Run the layout algorithm and determine the size required for legend +Runs the layout algorithm and returns the minimum size required for the legend. + +.. seealso:: :py:func:`setLegendSize` + +.. seealso:: :py:func:`legendSize` %End void setLegendSize( QSizeF s ); %Docstring Sets the preferred resulting legend size. + +If the size is null, the legend will be drawn with the minimum possible size to fit its content. + +.. seealso:: :py:func:`legendSize` + +.. seealso:: :py:func:`minimumSize` %End QSizeF legendSize() const; %Docstring -Find out preferred legend size set by the client. If null, the legend will be drawn with the minimum size +Returns the preferred legend size set by the client. + +If the returned size is null, the legend will be drawn with the minimum possible size to fit its content. + +.. seealso:: :py:func:`minimumSize` + +.. seealso:: :py:func:`setLegendSize` %End void drawLegend( QPainter *painter ); %Docstring -Draw the legend with given painter. It will occupy the area reported in legendSize(). -Painter should be scaled beforehand so that units correspond to millimeters. +Draws the legend with given ``painter``. The legend will occupy the area reported in legendSize(). +The ``painter`` should be scaled beforehand so that units correspond to millimeters. %End + void drawLegend( QgsRenderContext &context ); +%Docstring +Draws the legend using a given render ``context``. The legend will occupy the area reported in legendSize(). + +.. versionadded:: 3.6 +%End static void setNodeLegendStyle( QgsLayerTreeNode *node, QgsLegendStyle::Style style ); +%Docstring +Sets the ``style`` of a ``node``. + +.. seealso:: :py:func:`nodeLegendStyle` +%End + static QgsLegendStyle::Style nodeLegendStyle( QgsLayerTreeNode *node, QgsLayerTreeModel *model ); +%Docstring +Returns the style for the given ``node``, within the specified ``model``. + +.. seealso:: :py:func:`setNodeLegendStyle` +%End }; diff --git a/python/core/auto_generated/qgsmaprendererjob.sip.in b/python/core/auto_generated/qgsmaprendererjob.sip.in index daef61d8d00e..7bd7e562a576 100644 --- a/python/core/auto_generated/qgsmaprendererjob.sip.in +++ b/python/core/auto_generated/qgsmaprendererjob.sip.in @@ -183,6 +183,7 @@ emitted when asynchronous rendering is finished (or canceled). + }; diff --git a/python/core/auto_generated/qgsnetworkreply.sip.in b/python/core/auto_generated/qgsnetworkreply.sip.in deleted file mode 100644 index 5edcb1b62f25..000000000000 --- a/python/core/auto_generated/qgsnetworkreply.sip.in +++ /dev/null @@ -1,115 +0,0 @@ -/************************************************************************ - * This file has been generated automatically from * - * * - * src/core/qgsnetworkreply.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ - - - -class QgsNetworkReplyContent -{ -%Docstring -Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between threads. - -.. versionadded:: 3.6 -%End - -%TypeHeaderCode -#include "qgsnetworkreply.h" -%End - public: - - QgsNetworkReplyContent(); -%Docstring -Default constructor for an empty reply. -%End - - explicit QgsNetworkReplyContent( QNetworkReply *reply ); -%Docstring -Constructor for QgsNetworkReplyContent, populated from the specified ``reply``. -%End - - void clear(); -%Docstring -Clears the reply, resetting it back to a default, empty reply. -%End - - QVariant attribute( QNetworkRequest::Attribute code ) const; -%Docstring -Returns the attribute associated with the ``code``. If the attribute has not been set, it returns an -invalid QVariant. - -You can expect the default values listed in QNetworkRequest.Attribute to be -applied to the values returned by this function. - -.. seealso:: :py:func:`attributes` -%End - - - QByteArray content() const; -%Docstring -Returns the raw reply content. -%End - - QNetworkReply::NetworkError error() const; -%Docstring -Returns the reply's error message, or QNetworkReply.NoError if no -error was encountered. - -.. seealso:: :py:func:`errorString` -%End - - QString errorString() const; -%Docstring -Returns the error text for the reply, or an empty string if no -error was encountered. - -.. seealso:: :py:func:`error` -%End - - - bool hasRawHeader( const QByteArray &headerName ) const; -%Docstring -Returns true if the reply contains a header with the specified ``headerName``. - -.. seealso:: :py:func:`rawHeaderPairs` - -.. seealso:: :py:func:`rawHeaderList` - -.. seealso:: :py:func:`rawHeader` -%End - - QList rawHeaderList() const; -%Docstring -Returns a list of raw header names contained within the reply. - -.. seealso:: :py:func:`rawHeaderPairs` - -.. seealso:: :py:func:`hasRawHeader` - -.. seealso:: :py:func:`rawHeader` -%End - - QByteArray rawHeader( const QByteArray &headerName ) const; -%Docstring -Returns the content of the header with the specified ``headerName``, or an -empty QByteArray if the specified header was not found in the reply. - -.. seealso:: :py:func:`rawHeaderPairs` - -.. seealso:: :py:func:`hasRawHeader` - -.. seealso:: :py:func:`rawHeaderList` -%End - -}; - -/************************************************************************ - * This file has been generated automatically from * - * * - * src/core/qgsnetworkreply.h * - * * - * Do not edit manually ! Edit header and run scripts/sipify.pl again * - ************************************************************************/ diff --git a/python/core/auto_generated/qgsproject.sip.in b/python/core/auto_generated/qgsproject.sip.in index 6636b3eb9203..aef8b9ade4b1 100644 --- a/python/core/auto_generated/qgsproject.sip.in +++ b/python/core/auto_generated/qgsproject.sip.in @@ -720,6 +720,7 @@ Retrieve a pointer to a registered layer by layer ID. .. seealso:: :py:func:`mapLayers` %End + QList mapLayersByName( const QString &layerName ) const; %Docstring Retrieve a list of matching registered layers by layer name. diff --git a/python/core/auto_generated/qgsstatisticalsummary.sip.in b/python/core/auto_generated/qgsstatisticalsummary.sip.in index 2e9892293120..ab16d31d90bc 100644 --- a/python/core/auto_generated/qgsstatisticalsummary.sip.in +++ b/python/core/auto_generated/qgsstatisticalsummary.sip.in @@ -46,6 +46,8 @@ specifying the statistic in the constructor or via setStatistics(). FirstQuartile, ThirdQuartile, InterQuartileRange, + First, + Last, All }; typedef QFlags Statistics; @@ -214,6 +216,24 @@ be calculated. %Docstring Returns calculated range (difference between maximum and minimum values). A NaN value may be returned if the range cannot be calculated. +%End + + double first() const; +%Docstring +Returns the first value obtained. A NaN value may be returned if no values were encountered. + +.. seealso:: :py:func:`last` + +.. versionadded:: 3.6 +%End + + double last() const; +%Docstring +Returns the last value obtained. A NaN value may be returned if no values were encountered. + +.. seealso:: :py:func:`first` + +.. versionadded:: 3.6 %End double stDev() const; @@ -296,9 +316,18 @@ be calculated. static QString displayName( QgsStatisticalSummary::Statistic statistic ); %Docstring -Returns the friendly display name for a statistic +Returns the friendly display name for a ``statistic``. + +.. seealso:: :py:func:`shortName` +%End + + static QString shortName( QgsStatisticalSummary::Statistic statistic ); +%Docstring +Returns a short, friendly display name for a ``statistic``, suitable for use in a field name. + +.. seealso:: :py:func:`displayName` -:param statistic: statistic to return name for +.. versionadded:: 3.6 %End }; diff --git a/python/core/auto_generated/raster/qgsrastertransparency.sip.in b/python/core/auto_generated/raster/qgsrastertransparency.sip.in index ad0790f111d7..cbad9defa659 100644 --- a/python/core/auto_generated/raster/qgsrastertransparency.sip.in +++ b/python/core/auto_generated/raster/qgsrastertransparency.sip.in @@ -87,7 +87,7 @@ Searches through the transparency list, and if a match is found, the global tran by the stored transparency value. :param value: the needle to search for in the transparency hay stack -:param globalTransparency: the overal transparency level for the layer +:param globalTransparency: the overall transparency level for the layer %End diff --git a/python/core/auto_generated/symbology/qgscategorizedsymbolrenderer.sip.in b/python/core/auto_generated/symbology/qgscategorizedsymbolrenderer.sip.in index dfdba924e9d4..3804f0f4025c 100644 --- a/python/core/auto_generated/symbology/qgscategorizedsymbolrenderer.sip.in +++ b/python/core/auto_generated/symbology/qgscategorizedsymbolrenderer.sip.in @@ -12,7 +12,7 @@ class QgsRendererCategory { %Docstring -categorized renderer +Represents an individual category (class) from a QgsCategorizedSymbolRenderer. %End %TypeHeaderCode @@ -27,22 +27,71 @@ Constructor for QgsRendererCategory. QgsRendererCategory( const QVariant &value, QgsSymbol *symbol /Transfer/, const QString &label, bool render = true ); %Docstring -takes ownership of symbol +Constructor for a new QgsRendererCategory, with the specified ``value`` and ``symbol``. + +If ``value`` is a list, then the category will match any of the values from this list. + +The ownership of ``symbol`` is transferred to the category. + +The ``label`` argument specifies the label used for this category in legends and the layer tree. + +The ``render`` argument indicates whether the category should initially be rendered and appear checked in the layer tree. %End QgsRendererCategory( const QgsRendererCategory &cat ); %Docstring -copy constructor +Copy constructor. %End - QVariant value() const; +%Docstring +Returns the value corresponding to this category. + +If the returned value is a list, then the category will match any of the values from this list. + +.. seealso:: :py:func:`setValue` +%End + QgsSymbol *symbol() const; +%Docstring +Returns the symbol which will be used to render this category. + +.. seealso:: :py:func:`setSymbol` +%End + QString label() const; +%Docstring +Returns the label for this category, which is used to represent the category within +legends and the layer tree. + +.. seealso:: :py:func:`setLabel` +%End void setValue( const QVariant &value ); +%Docstring +Sets the ``value`` corresponding to this category. + +If ``value`` is a list, then the category will match any of the values from this list. + +.. seealso:: :py:func:`value` +%End + void setSymbol( QgsSymbol *s /Transfer/ ); +%Docstring +Sets the symbol which will be used to render this category. + +Ownership of the symbol is transferred to the category. + +.. seealso:: :py:func:`symbol` +%End + void setLabel( const QString &label ); +%Docstring +Sets the ``label`` for this category, which is used to represent the category within +legends and the layer tree. + +.. seealso:: :py:func:`label` +%End bool renderState() const; %Docstring @@ -62,9 +111,16 @@ Sets whether the category is currently enabled and should be rendered. .. versionadded:: 2.5 %End + QString dump() const; +%Docstring +Returns a string representing the categories settings, used for debugging purposes only. +%End void toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const; +%Docstring +Converts the category to a matching SLD rule, within the specified DOM document and ``element``. +%End protected: @@ -82,6 +138,14 @@ class QgsCategorizedSymbolRenderer : QgsFeatureRenderer public: QgsCategorizedSymbolRenderer( const QString &attrName = QString(), const QgsCategoryList &categories = QgsCategoryList() ); +%Docstring +Constructor for QgsCategorizedSymbolRenderer. + +The ``attrName`` argument specifies the layer's field name, or expression, which the categories will be matched against. + +A list of renderer ``categories`` can optionally be specified. If no categories are specified in the constructor, they +can be added later by calling addCategory(). +%End virtual QgsSymbol *symbolForFeature( const QgsFeature &feature, QgsRenderContext &context ) const; @@ -118,47 +182,139 @@ symbol for the renderer. %End const QgsCategoryList &categories() const; +%Docstring +Returns a list of all categories recognized by the renderer. +%End int categoryIndexForValue( const QVariant &val ); %Docstring -Returns index of category with specified value (-1 if not found) +Returns the index for the category with the specified value (or -1 if not found). %End int categoryIndexForLabel( const QString &val ); %Docstring -Returns index of category with specified label (-1 if not found or not unique) +Returns the index of the category with the specified label (or -1 if the label was not found, or is not unique). .. versionadded:: 2.5 %End bool updateCategoryValue( int catIndex, const QVariant &value ); +%Docstring +Changes the value for the category with the specified index. + +If ``value`` is a list, then the category will match any of the values from this list. + +.. seealso:: :py:func:`updateCategorySymbol` + +.. seealso:: :py:func:`updateCategoryLabel` + +.. seealso:: :py:func:`updateCategoryRenderState` +%End + bool updateCategorySymbol( int catIndex, QgsSymbol *symbol /Transfer/ ); +%Docstring +Changes the ``symbol`` for the category with the specified index. + +Ownership of ``symbol`` is transferred to the renderer. + +.. seealso:: :py:func:`updateCategoryValue` + +.. seealso:: :py:func:`updateCategoryLabel` + +.. seealso:: :py:func:`updateCategoryRenderState` +%End + bool updateCategoryLabel( int catIndex, const QString &label ); +%Docstring +Changes the ``label`` for the category with the specified index. + +A category's label is used to represent the category within +legends and the layer tree. + +.. seealso:: :py:func:`updateCategoryValue` + +.. seealso:: :py:func:`updateCategoryLabel` + +.. seealso:: :py:func:`updateCategoryRenderState` +%End bool updateCategoryRenderState( int catIndex, bool render ); %Docstring +Changes the render state for the category with the specified index. + +The render state indicates whether or not the category will be rendered, +and is reflected in whether the category is checked with the project's layer tree. + +.. seealso:: :py:func:`updateCategoryValue` + +.. seealso:: :py:func:`updateCategorySymbol` + +.. seealso:: :py:func:`updateCategoryLabel` .. versionadded:: 2.5 %End void addCategory( const QgsRendererCategory &category ); +%Docstring +Adds a new ``category`` to the renderer. + +.. seealso:: :py:func:`categories` +%End + bool deleteCategory( int catIndex ); +%Docstring +Deletes the category with the specified index from the renderer. + +.. seealso:: :py:func:`deleteAllCategories` +%End + void deleteAllCategories(); +%Docstring +Deletes all existing categories from the renderer. + +.. seealso:: :py:func:`deleteCategory` +%End void moveCategory( int from, int to ); %Docstring -Moves the category at index position from to index position to. +Moves an existing category at index position from to index position to. %End void sortByValue( Qt::SortOrder order = Qt::AscendingOrder ); +%Docstring +Sorts the existing categories by their value. + +.. seealso:: :py:func:`sortByLabel` +%End + void sortByLabel( Qt::SortOrder order = Qt::AscendingOrder ); +%Docstring +Sorts the existing categories by their label. + +.. seealso:: :py:func:`sortByValue` +%End QString classAttribute() const; +%Docstring +Returns the class attribute for the renderer, which is the field name +or expression string from the layer which will be matched against the +renderer categories. + +.. seealso:: :py:func:`setClassAttribute` +%End + void setClassAttribute( const QString &attr ); +%Docstring +Sets the class attribute for the renderer, which is the field name +or expression string from the layer which will be matched against the +renderer categories. + +.. seealso:: :py:func:`classAttribute` +%End static QgsFeatureRenderer *create( QDomElement &element, const QgsReadWriteContext &context ) /Factory/; %Docstring -create renderer from XML element +Creates a categorized renderer from an XML ``element``. %End virtual QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ); diff --git a/python/core/auto_generated/validity/qgsabstractvaliditycheck.sip.in b/python/core/auto_generated/validity/qgsabstractvaliditycheck.sip.in new file mode 100644 index 000000000000..c353f249780f --- /dev/null +++ b/python/core/auto_generated/validity/qgsabstractvaliditycheck.sip.in @@ -0,0 +1,138 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/validity/qgsabstractvaliditycheck.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + +class QgsValidityCheckResult +{ +%Docstring +Represents an individual result from a validity check run by a QgsAbstractValidityCheck subclass. + +Results can either be warnings or critical errors, as dictated by the type member. Critical error +are errors which are serious enough to prevent an operation from proceeding, while a warning +result will be communicated to users, but not prevent them from proceeding. + +.. versionadded:: 3.6 +%End + +%TypeHeaderCode +#include "qgsabstractvaliditycheck.h" +%End + public: + + enum Type + { + Warning, + Critical, + }; + + Type type; + + QString title; + + QString detailedDescription; + + QString checkId; + +}; + + +class QgsAbstractValidityCheck : QObject +{ +%Docstring +Abstract base class for individual validity checks. + +Validity checks represent objects which can run a test using a :py:class:`QgsValidityCheckContext`, and return +the results of the check via QgsValidityCheckResult objects. + +Checks can be used for many different use cases, e.g. validating a layout's contents before allowing +an export to occur, or validating the contents of a Processing model (and warning if required plugin based +providers are not available or if compulsory algorithm parameters have not been populated). + +Subclasses must indicate the type of check they represent via the checkType() method. When checks are performed, +all the registered checks with a matching check type are performed sequentially. The check type also +dictates the subclass of the QgsValidityCheckContext which is given to the subclass' runCheck method. + +Checks must be registered in the application's :py:class:`QgsValidityCheckRegistry`, which is accessible via +:py:func:`QgsApplication.validityCheckRegistry()` + +.. seealso:: :py:class:`QgsValidityCheckRegistry` + +.. versionadded:: 3.6 +%End + +%TypeHeaderCode +#include "qgsabstractvaliditycheck.h" +%End + public: + + enum Type + { + TypeLayoutCheck, + TypeUserCheck, + }; + + virtual QgsAbstractValidityCheck *create() const = 0 /Factory/; +%Docstring +Creates a new instance of the check and returns it. +%End + + virtual QString id() const = 0; +%Docstring +Returns the unique ID of the check. + +This is a non-translated, non-user visible string identifying the check. +%End + + virtual int checkType() const = 0; +%Docstring +Returns the type of the check. +%End + + virtual bool prepareCheck( const QgsValidityCheckContext *context, QgsFeedback *feedback ); +%Docstring +Prepares the check for execution, and returns true if the check can be run. + +This method is always called from the main thread, and subclasses can implement +it to do preparatory steps which are not thread safe (e.g. obtaining feature +sources from vector layers). It is followed by a call to runCheck(), which +may be performed in a background thread. + +Individual calls to prepareCheck()/runCheck() are run on a new instance of the +check (see create()), so subclasses can safely store state from the prepareCheck() method +ready for the subsequent runCheck() method. + +The ``context`` argument gives the wider in which the check is being run. +%End + + virtual QList< QgsValidityCheckResult > runCheck( const QgsValidityCheckContext *context, QgsFeedback *feedback ) = 0; +%Docstring +Runs the check and returns a list of results. If the check is "passed" and no warnings or errors are generated, +then an empty list should be returned. + +This method may be called in a background thread, so subclasses should take care to ensure that +only thread-safe methods are used. It is always preceded by a call to prepareCheck(). + +If a check needs to perform non-thread-safe tests, these should be implemented within prepareCheck() +and stored in the subclass instance to be returned by this method. + +The ``context`` argument gives the wider in which the check is being run. +%End + +}; + + + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/validity/qgsabstractvaliditycheck.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/core/auto_generated/validity/qgsvaliditycheckcontext.sip.in b/python/core/auto_generated/validity/qgsvaliditycheckcontext.sip.in new file mode 100644 index 000000000000..d206efe06294 --- /dev/null +++ b/python/core/auto_generated/validity/qgsvaliditycheckcontext.sip.in @@ -0,0 +1,82 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/validity/qgsvaliditycheckcontext.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + +class QgsValidityCheckContext +{ +%Docstring +Base class for validity check contexts. + +QgsAbstractValidityCheck subclasses are passed a QgsValidityCheckContext subclass which +encapsulates the context around that particular check type. For instance, a QgsAbstractValidityCheck +of the QgsAbstractValidityCheck.TypeLayoutCheck type will be passed a QgsLayoutValidityCheckContext +context, containing a reference to the QgsLayout to be checked. + +.. versionadded:: 3.6 +%End + +%TypeHeaderCode +#include "qgsvaliditycheckcontext.h" +%End +%ConvertToSubClassCode + if ( dynamic_cast( sipCpp ) != NULL ) + sipType = sipType_QgsLayoutValidityCheckContext; + else + sipType = 0; +%End + public: + + enum ContextType + { + TypeLayoutContext, + TypeUserContext, + }; + + virtual int type() const = 0; +%Docstring +Returns the context type. +%End + + virtual ~QgsValidityCheckContext(); + +}; + +class QgsLayoutValidityCheckContext : QgsValidityCheckContext +{ +%Docstring +Validity check context for print layout validation. + +QgsLayoutValidityCheckContext are passed to QgsAbstractValidityCheck subclasses which +indicate they are of the QgsAbstractValidityCheck.TypeLayoutCheck type. + +.. versionadded:: 3.6 +%End + +%TypeHeaderCode +#include "qgsvaliditycheckcontext.h" +%End + public: + + QgsLayoutValidityCheckContext( QgsLayout *layout ); +%Docstring +Constructor for QgsLayoutValidityCheckContext for the specified ``layout``. +%End + + virtual int type() const; + + QgsLayout *layout; + +}; +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/validity/qgsvaliditycheckcontext.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/core/auto_generated/validity/qgsvaliditycheckregistry.sip.in b/python/core/auto_generated/validity/qgsvaliditycheckregistry.sip.in new file mode 100644 index 000000000000..ca7fa6a78981 --- /dev/null +++ b/python/core/auto_generated/validity/qgsvaliditycheckregistry.sip.in @@ -0,0 +1,80 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/validity/qgsvaliditycheckregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + +class QgsValidityCheckRegistry +{ +%Docstring +This class keeps a list of QgsAbstractValidityCheck checks which can be used when +performing validity checks. + +QgsValidityCheckRegistry is not usually directly created, but rather accessed through +:py:func:`QgsApplication.validityCheckRegistry()` + +.. versionadded:: 3.6 +%End + +%TypeHeaderCode +#include "qgsvaliditycheckregistry.h" +%End + public: + + QgsValidityCheckRegistry(); + + ~QgsValidityCheckRegistry(); + + + QList checks() const; +%Docstring +Returns the list of available checks. +%End + + QList checks( int type ) const; +%Docstring +Returns the list of all available checks of the matching ``type``. +%End + + void addCheck( QgsAbstractValidityCheck *check /Transfer/ ); +%Docstring +Adds a ``check`` to the registry. Ownership of the check +is transferred to the registry. +%End + + void removeCheck( QgsAbstractValidityCheck *check ); +%Docstring +Removes a ``check`` from the registry. +The check object is automatically deleted. +%End + + QList< QgsValidityCheckResult > runChecks( int type, const QgsValidityCheckContext *context, QgsFeedback *feedback ) const; +%Docstring +Runs all checks of the specified ``type`` and returns a list of results. + +If all checks are "passed" and no warnings or errors are generated, then +an empty list will be returned. + +The ``context`` argument gives the wider in which the check is being run. + +The ``feedback`` argument is used to give progress reports and to support +cancelation of long-running checks. + +This is a blocking call, which will run all matching checks in the main +thread and only return when they have all completed. +%End + + private: + QgsValidityCheckRegistry( const QgsValidityCheckRegistry &rh ); +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/validity/qgsvaliditycheckregistry.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index f2dc3f5b04f3..aee4bc036b6d 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -80,7 +80,6 @@ %Include auto_generated/qgsmargins.sip %Include auto_generated/qgsmimedatautils.sip %Include auto_generated/qgsmultirenderchecker.sip -%Include auto_generated/qgsnetworkreply.sip %Include auto_generated/qgsobjectcustomproperties.sip %Include auto_generated/qgsogcutils.sip %Include auto_generated/qgsoptional.sip @@ -306,6 +305,9 @@ %Include auto_generated/fieldformatter/qgsvaluemapfieldformatter.sip %Include auto_generated/fieldformatter/qgsvaluerelationfieldformatter.sip %Include auto_generated/geocms/geonode/qgsgeonodeconnection.sip +%Include auto_generated/validity/qgsabstractvaliditycheck.sip +%Include auto_generated/validity/qgsvaliditycheckcontext.sip +%Include auto_generated/validity/qgsvaliditycheckregistry.sip %Include auto_generated/gps/qgsqtlocationconnection.sip %Include auto_generated/gps/qgsgpsconnectionregistry.sip %Include auto_generated/qgsabstractcontentcache.sip @@ -313,7 +315,6 @@ %Include auto_generated/qgsactionscoperegistry.sip %Include auto_generated/qgsanimatedicon.sip %Include auto_generated/qgsauxiliarystorage.sip -%Include auto_generated/qgsblockingnetworkrequest.sip %Include auto_generated/qgsbrowsermodel.sip %Include auto_generated/qgsbrowserproxymodel.sip %Include auto_generated/qgscoordinatereferencesystem.sip diff --git a/python/gui/auto_generated/qgsproxystyle.sip.in b/python/gui/auto_generated/qgsproxystyle.sip.in index 166393898644..ea602fe6d3c2 100644 --- a/python/gui/auto_generated/qgsproxystyle.sip.in +++ b/python/gui/auto_generated/qgsproxystyle.sip.in @@ -31,6 +31,7 @@ The base style for the QProxyStyle will be set to match the current QGIS applica %End }; + /************************************************************************ * This file has been generated automatically from * * * diff --git a/python/gui/auto_generated/qgsvaliditycheckresultswidget.sip.in b/python/gui/auto_generated/qgsvaliditycheckresultswidget.sip.in new file mode 100644 index 000000000000..1e0d47ff390b --- /dev/null +++ b/python/gui/auto_generated/qgsvaliditycheckresultswidget.sip.in @@ -0,0 +1,108 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsvaliditycheckresultswidget.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + +class QgsValidityCheckResultsModel : QAbstractItemModel +{ +%Docstring +A QAbstractItemModel subclass for displaying the results from a QgsAbtractValidityCheck. + +.. versionadded:: 3.6 +%End + +%TypeHeaderCode +#include "qgsvaliditycheckresultswidget.h" +%End + public: + + enum Roles + { + DescriptionRole, + }; + + QgsValidityCheckResultsModel( const QList< QgsValidityCheckResult > &results, QObject *parent /TransferThis/ = 0 ); +%Docstring +Constructor for QgsValidityCheckResultsModel, showing the specified list of checks ``results``. +%End + + virtual QModelIndex index( int row, int column, const QModelIndex &parent ) const; + + virtual QModelIndex parent( const QModelIndex &child ) const; + + virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const; + + virtual int columnCount( const QModelIndex &parent ) const; + + virtual QVariant data( const QModelIndex &index, int role ) const; + +}; + +class QgsValidityCheckResultsWidget : QWidget +{ +%Docstring +A reusable widget which displays a summary of the results from a :py:class:`QgsAbstractValidityCheck` (or checks). + +.. versionadded:: 3.6 +%End + +%TypeHeaderCode +#include "qgsvaliditycheckresultswidget.h" +%End + public: + + QgsValidityCheckResultsWidget( QWidget *parent /TransferThis/ ); +%Docstring +Constructor for QgsValidityCheckResultsWidget, with the specified ``parent`` widget. +%End + + void setDescription( const QString &description ); +%Docstring +Sets a ``description`` label to show at the top of the widget, e.g. notifying users of +why they are being shown the warnings. +%End + + void setResults( const QList< QgsValidityCheckResult > &results ); +%Docstring +Sets the list of check ``results`` to show in the dialog. +%End + + static bool runChecks( int type, const QgsValidityCheckContext *context, const QString &title, const QString &description, QWidget *parent = 0 ); +%Docstring +Runs all registered validity checks of the given ``type``, and if any warnings or critical +errors are encountered then displays them to users in a dialog. + +The ``context`` argument must specify the correct QgsValidityCheckContext subclass for the +given check ``type``. + +The ``title`` argument is used as the dialog's title, and the ``description`` text will be shown +to users as an explanation of why the checks are being run. + +The ``parent`` argument can be used to give a parent widget for the created dialogs. + +If any critical errors are encountered by the checks, then users will not be allowed to click OK +on the dialog and proceed with the operation. The function will return false. + +Returns true if no warnings were encountered (and no dialog was shown to users), or if only +warnings were shown and the user clicked OK after being shown these warnings. + +This method is a blocking method, and runs all checks in the main thread. +%End + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/qgsvaliditycheckresultswidget.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/gui/gui_auto.sip b/python/gui/gui_auto.sip index 6d8c7ccd852d..70b681d943c9 100644 --- a/python/gui/gui_auto.sip +++ b/python/gui/gui_auto.sip @@ -212,6 +212,7 @@ %Include auto_generated/qgstextpreview.sip %Include auto_generated/qgstreewidgetitem.sip %Include auto_generated/qgsunitselectionwidget.sip +%Include auto_generated/qgsvaliditycheckresultswidget.sip %Include auto_generated/qgsvariableeditorwidget.sip %Include auto_generated/qgsvscrollarea.sip %Include auto_generated/qgsfiledownloaderdialog.sip diff --git a/python/plugins/db_manager/ui/DlgImportVector.ui b/python/plugins/db_manager/ui/DlgImportVector.ui index acc1d6c550e1..fd576ac3a5fc 100755 --- a/python/plugins/db_manager/ui/DlgImportVector.ui +++ b/python/plugins/db_manager/ui/DlgImportVector.ui @@ -442,6 +442,7 @@ 211 553 + diff --git a/python/plugins/processing/algs/gdal/AssignProjection.py b/python/plugins/processing/algs/gdal/AssignProjection.py index 7f6200e6acd2..64859d1a7822 100644 --- a/python/plugins/processing/algs/gdal/AssignProjection.py +++ b/python/plugins/processing/algs/gdal/AssignProjection.py @@ -92,12 +92,9 @@ def getConsoleCommands(self, parameters, context, feedback, executing=True): arguments.append(fileName) - commands = [] + commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] if isWindows(): - commands = ['cmd.exe', '/C ', self.commandName() + '.bat', - GdalUtils.escapeAndJoin(arguments)] - else: - commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] + commands.insert(0, 'python3') self.setOutputValue(self.OUTPUT, fileName) return commands diff --git a/python/plugins/processing/algs/gdal/ColorRelief.py b/python/plugins/processing/algs/gdal/ColorRelief.py index bce8de844145..6f3323903e06 100644 --- a/python/plugins/processing/algs/gdal/ColorRelief.py +++ b/python/plugins/processing/algs/gdal/ColorRelief.py @@ -62,6 +62,7 @@ def initAlgorithm(self, config=None): self.tr('Input layer'))) self.addParameter(QgsProcessingParameterBand(self.BAND, self.tr('Band number'), + 1, parentLayerParameterName=self.INPUT)) self.addParameter(QgsProcessingParameterBoolean(self.COMPUTE_EDGES, self.tr('Compute edges'), diff --git a/python/plugins/processing/algs/gdal/GdalAlgorithm.py b/python/plugins/processing/algs/gdal/GdalAlgorithm.py index df176d80bda1..21b6720bdd29 100644 --- a/python/plugins/processing/algs/gdal/GdalAlgorithm.py +++ b/python/plugins/processing/algs/gdal/GdalAlgorithm.py @@ -142,16 +142,6 @@ def processAlgorithm(self, parameters, context, feedback): return results - def helpUrl(self): - helpPath = GdalUtils.gdalHelpPath() - if helpPath == '': - return None - - if os.path.exists(helpPath): - return QUrl.fromLocalFile(os.path.join(helpPath, '{}.html'.format(self.commandName()))).toString() - else: - return helpPath + '{}.html'.format(self.commandName()) - def commandName(self): parameters = {} for param in self.parameterDefinitions(): diff --git a/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py b/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py index c6289b073f2f..d219897aedd1 100644 --- a/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py +++ b/python/plugins/processing/algs/gdal/GdalAlgorithmProvider.py @@ -103,18 +103,12 @@ def load(self): ProcessingConfig.settingIcons[self.name()] = self.icon() ProcessingConfig.addSetting(Setting(self.name(), 'ACTIVATE_GDAL', self.tr('Activate'), True)) - ProcessingConfig.addSetting(Setting( - self.name(), - GdalUtils.GDAL_HELP_PATH, - self.tr('Location of GDAL docs'), - GdalUtils.gdalHelpPath())) ProcessingConfig.readSettings() self.refreshAlgorithms() return True def unload(self): ProcessingConfig.removeSetting('ACTIVATE_GDAL') - ProcessingConfig.removeSetting(GdalUtils.GDAL_HELP_PATH) def isActive(self): return ProcessingConfig.getSetting('ACTIVATE_GDAL') diff --git a/python/plugins/processing/algs/gdal/GdalUtils.py b/python/plugins/processing/algs/gdal/GdalUtils.py index 37607eaf043e..807e03ab28e7 100644 --- a/python/plugins/processing/algs/gdal/GdalUtils.py +++ b/python/plugins/processing/algs/gdal/GdalUtils.py @@ -157,10 +157,10 @@ def getSupportedRasters(): if extensions: GdalUtils.supportedRasters[shortName] = extensions # Only creatable rasters can be referenced in output rasters - if ((gdal.DCAP_CREATE in metadata - and metadata[gdal.DCAP_CREATE] == 'YES') - or (gdal.DCAP_CREATECOPY in metadata - and metadata[gdal.DCAP_CREATECOPY] == 'YES')): + if ((gdal.DCAP_CREATE in metadata and + metadata[gdal.DCAP_CREATE] == 'YES') or + (gdal.DCAP_CREATECOPY in metadata and + metadata[gdal.DCAP_CREATECOPY] == 'YES')): GdalUtils.supportedOutputRasters[shortName] = extensions return GdalUtils.supportedRasters @@ -240,24 +240,6 @@ def version(): def readableVersion(): return gdal.VersionInfo('RELEASE_NAME') - @staticmethod - def gdalHelpPath(): - helpPath = ProcessingConfig.getSetting(GdalUtils.GDAL_HELP_PATH) - - if helpPath is None: - if isWindows(): - pass - elif isMac(): - pass - else: - searchPaths = ['/usr/share/doc/libgdal-doc/gdal'] - for path in searchPaths: - if os.path.exists(path): - helpPath = os.path.abspath(path) - break - - return helpPath if helpPath is not None else 'http://www.gdal.org/' - @staticmethod def ogrConnectionStringFromLayer(layer): """Generates OGR connection string from a layer diff --git a/python/plugins/processing/algs/gdal/aspect.py b/python/plugins/processing/algs/gdal/aspect.py index 28a101bd106c..dc02bd8737a2 100644 --- a/python/plugins/processing/algs/gdal/aspect.py +++ b/python/plugins/processing/algs/gdal/aspect.py @@ -59,6 +59,7 @@ def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, self.tr('Input layer'))) self.addParameter(QgsProcessingParameterBand(self.BAND, self.tr('Band number'), + 1, parentLayerParameterName=self.INPUT)) self.addParameter(QgsProcessingParameterBoolean(self.TRIG_ANGLE, self.tr('Return trigonometric angle instead of azimuth'), diff --git a/python/plugins/processing/algs/gdal/contour.py b/python/plugins/processing/algs/gdal/contour.py index 5ffb0623a8eb..3b66d55a538f 100644 --- a/python/plugins/processing/algs/gdal/contour.py +++ b/python/plugins/processing/algs/gdal/contour.py @@ -66,6 +66,7 @@ def initAlgorithm(self, config=None): self.tr('Input layer'))) self.addParameter(QgsProcessingParameterBand(self.BAND, self.tr('Band number'), + 1, parentLayerParameterName=self.INPUT)) self.addParameter(QgsProcessingParameterNumber(self.INTERVAL, self.tr('Interval between contour lines'), diff --git a/python/plugins/processing/algs/gdal/fillnodata.py b/python/plugins/processing/algs/gdal/fillnodata.py index 2f9f754587f9..6bb2a9169478 100644 --- a/python/plugins/processing/algs/gdal/fillnodata.py +++ b/python/plugins/processing/algs/gdal/fillnodata.py @@ -59,6 +59,7 @@ def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, self.tr('Input layer'))) self.addParameter(QgsProcessingParameterBand(self.BAND, self.tr('Band number'), + 1, parentLayerParameterName=self.INPUT)) self.addParameter(QgsProcessingParameterNumber(self.DISTANCE, self.tr('Maximum distance (in pixels) to search out for values to interpolate'), @@ -128,12 +129,8 @@ def getConsoleCommands(self, parameters, context, feedback, executing=True): arguments.append(raster.source()) arguments.append(out) - commands = [] + commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] if isWindows(): - commands = ['cmd.exe', '/C ', self.commandName() + '.bat', - GdalUtils.escapeAndJoin(arguments)] - else: - commands = [self.commandName() + '.py', - GdalUtils.escapeAndJoin(arguments)] + commands.insert(0, 'python3') return commands diff --git a/python/plugins/processing/algs/gdal/gdal2tiles.py b/python/plugins/processing/algs/gdal/gdal2tiles.py index 2d9c3ea670f8..935dad13fa43 100644 --- a/python/plugins/processing/algs/gdal/gdal2tiles.py +++ b/python/plugins/processing/algs/gdal/gdal2tiles.py @@ -35,7 +35,6 @@ QgsProcessingParameterString, QgsProcessingParameterNumber, QgsProcessingParameterBoolean, - QgsProcessingOutputFolder, QgsProcessingParameterFolderDestination) from processing.algs.gdal.GdalAlgorithm import GdalAlgorithm from processing.algs.gdal.GdalUtils import GdalUtils @@ -230,12 +229,8 @@ def getConsoleCommands(self, parameters, context, feedback, executing=True): arguments.append(inLayer.source()) arguments.append(self.parameterAsString(parameters, self.OUTPUT, context)) - commands = [] + commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] if isWindows(): - commands = ['cmd.exe', '/C ', self.commandName() + '.bat', - GdalUtils.escapeAndJoin(arguments)] - else: - commands = [self.commandName() + '.py', - GdalUtils.escapeAndJoin(arguments)] + commands.insert(0, 'python3') return commands diff --git a/python/plugins/processing/algs/gdal/gdal2xyz.py b/python/plugins/processing/algs/gdal/gdal2xyz.py index 2555d092437d..03372f35dda9 100644 --- a/python/plugins/processing/algs/gdal/gdal2xyz.py +++ b/python/plugins/processing/algs/gdal/gdal2xyz.py @@ -26,7 +26,6 @@ __revision__ = '$Format:%H$' from qgis.core import (QgsProcessingAlgorithm, - QgsProcessing, QgsProcessingException, QgsProcessingParameterRasterLayer, QgsProcessingParameterBand, @@ -53,6 +52,7 @@ def initAlgorithm(self, config=None): self.tr('Input layer'))) self.addParameter(QgsProcessingParameterBand(self.BAND, self.tr('Band number'), + 1, parentLayerParameterName=self.INPUT)) self.addParameter(QgsProcessingParameterBoolean(self.CSV, self.tr('Output comma-separated values'), @@ -94,11 +94,8 @@ def getConsoleCommands(self, parameters, context, feedback, executing=True): arguments.append(raster.source()) arguments.append(self.parameterAsFileOutput(parameters, self.OUTPUT, context)) - commands = [] + commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] if isWindows(): - commands = ['cmd.exe', '/C ', self.commandName() + '.bat', - GdalUtils.escapeAndJoin(arguments)] - else: - commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] + commands.insert(0, 'python3') return commands diff --git a/python/plugins/processing/algs/gdal/gdaladdo.py b/python/plugins/processing/algs/gdal/gdaladdo.py index 121548118b62..46f29211663d 100644 --- a/python/plugins/processing/algs/gdal/gdaladdo.py +++ b/python/plugins/processing/algs/gdal/gdaladdo.py @@ -30,6 +30,7 @@ from qgis.PyQt.QtGui import QIcon from qgis.core import (QgsProcessingException, + QgsProcessingParameterDefinition, QgsProcessingParameterRasterLayer, QgsProcessingParameterEnum, QgsProcessingParameterString, @@ -82,12 +83,17 @@ def initAlgorithm(self, config=None): self.tr('Resampling method'), options=[i[0] for i in self.methods], allowMultiple=False, - defaultValue=0)) + defaultValue=0, + optional=True)) params.append(QgsProcessingParameterEnum(self.FORMAT, self.tr('Overviews format'), options=self.formats, allowMultiple=False, - defaultValue=0)) + defaultValue=0, + optional=True)) + for p in params: + p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) + self.addParameter(p) self.addOutput(QgsProcessingOutputRasterLayer(self.OUTPUT, self.tr('Pyramidized'))) diff --git a/python/plugins/processing/algs/gdal/hillshade.py b/python/plugins/processing/algs/gdal/hillshade.py index 2a9b38c4ffa5..ab2d7bfa3477 100644 --- a/python/plugins/processing/algs/gdal/hillshade.py +++ b/python/plugins/processing/algs/gdal/hillshade.py @@ -65,6 +65,7 @@ def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, self.tr('Input layer'))) self.addParameter(QgsProcessingParameterBand(self.BAND, self.tr('Band number'), + 1, parentLayerParameterName=self.INPUT)) self.addParameter(QgsProcessingParameterNumber(self.Z_FACTOR, self.tr('Z factor (vertical exaggeration)'), diff --git a/python/plugins/processing/algs/gdal/merge.py b/python/plugins/processing/algs/gdal/merge.py index 9d3a47f8197a..d19fd21d048d 100644 --- a/python/plugins/processing/algs/gdal/merge.py +++ b/python/plugins/processing/algs/gdal/merge.py @@ -37,8 +37,7 @@ QgsProcessingParameterString, QgsProcessingParameterBoolean, QgsProcessingParameterNumber, - QgsProcessingParameterRasterDestination, - QgsProcessingUtils) + QgsProcessingParameterRasterDestination) from processing.algs.gdal.GdalAlgorithm import GdalAlgorithm from processing.algs.gdal.GdalUtils import GdalUtils @@ -166,11 +165,8 @@ def getConsoleCommands(self, parameters, context, feedback, executing=True): arguments.append('--optfile') arguments.append(list_file) - commands = [] + commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] if isWindows(): - commands = ['cmd.exe', '/C ', self.commandName() + '.bat', - GdalUtils.escapeAndJoin(arguments)] - else: - commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] + commands.insert(0, 'python3') return commands diff --git a/python/plugins/processing/algs/gdal/pct2rgb.py b/python/plugins/processing/algs/gdal/pct2rgb.py index bbe08cecd5a8..53779d2033ca 100644 --- a/python/plugins/processing/algs/gdal/pct2rgb.py +++ b/python/plugins/processing/algs/gdal/pct2rgb.py @@ -57,6 +57,7 @@ def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, self.tr('Input layer'))) self.addParameter(QgsProcessingParameterBand(self.BAND, self.tr('Band number'), + 1, parentLayerParameterName=self.INPUT)) self.addParameter(QgsProcessingParameterBoolean(self.RGBA, self.tr('Generate a RGBA file'), @@ -101,11 +102,8 @@ def getConsoleCommands(self, parameters, context, feedback, executing=True): if self.parameterAsBool(parameters, self.RGBA, context): arguments.append('-rgba') - commands = [] + commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] if isWindows(): - commands = ['cmd.exe', '/C ', self.commandName() + '.bat', - GdalUtils.escapeAndJoin(arguments)] - else: - commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] + commands.insert(0, 'python3') return commands diff --git a/python/plugins/processing/algs/gdal/polygonize.py b/python/plugins/processing/algs/gdal/polygonize.py index fefbf62f2772..55e60256606b 100644 --- a/python/plugins/processing/algs/gdal/polygonize.py +++ b/python/plugins/processing/algs/gdal/polygonize.py @@ -28,7 +28,6 @@ import os from qgis.PyQt.QtGui import QIcon -from qgis.PyQt.QtCore import QFileInfo from qgis.core import (QgsProcessing, QgsProcessingException, @@ -59,6 +58,7 @@ def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, self.tr('Input layer'))) self.addParameter(QgsProcessingParameterBand(self.BAND, self.tr('Band number'), + 1, parentLayerParameterName=self.INPUT)) self.addParameter(QgsProcessingParameterString(self.FIELD, self.tr('Name of the field to create'), @@ -110,15 +110,13 @@ def getConsoleCommands(self, parameters, context, feedback, executing=True): if outFormat: arguments.append('-f {}'.format(outFormat)) - arguments.append(GdalUtils.ogrLayerName(output)) + layerName = GdalUtils.ogrLayerName(output) + if layerName: + arguments.append(layerName) arguments.append(self.parameterAsString(parameters, self.FIELD, context)) - commands = [] + commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] if isWindows(): - commands = ['cmd.exe', '/C ', self.commandName() + '.bat', - GdalUtils.escapeAndJoin(arguments)] - else: - commands = [self.commandName() + '.py', - GdalUtils.escapeAndJoin(arguments)] + commands.insert(0, 'python3') return commands diff --git a/python/plugins/processing/algs/gdal/proximity.py b/python/plugins/processing/algs/gdal/proximity.py index 833d2ca08d64..4cafad2ad9a5 100644 --- a/python/plugins/processing/algs/gdal/proximity.py +++ b/python/plugins/processing/algs/gdal/proximity.py @@ -75,6 +75,7 @@ def initAlgorithm(self, config=None): self.tr('Input layer'))) self.addParameter(QgsProcessingParameterBand(self.BAND, self.tr('Band number'), + 1, parentLayerParameterName=self.INPUT)) self.addParameter(QgsProcessingParameterString(self.VALUES, self.tr('A list of pixel values in the source image to be considered target pixels'), @@ -185,12 +186,8 @@ def getConsoleCommands(self, parameters, context, feedback, executing=True): arguments.append(inLayer.source()) arguments.append(out) - commands = [] + commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] if isWindows(): - commands = ['cmd.exe', '/C ', self.commandName() + '.bat', - GdalUtils.escapeAndJoin(arguments)] - else: - commands = [self.commandName() + '.py', - GdalUtils.escapeAndJoin(arguments)] + commands.insert(0, 'python3') return commands diff --git a/python/plugins/processing/algs/gdal/retile.py b/python/plugins/processing/algs/gdal/retile.py index cf6ba367bbbb..7e4f6154569b 100644 --- a/python/plugins/processing/algs/gdal/retile.py +++ b/python/plugins/processing/algs/gdal/retile.py @@ -33,7 +33,6 @@ QgsProcessingParameterString, QgsProcessingParameterNumber, QgsProcessingParameterBoolean, - QgsProcessingOutputFolder, QgsProcessingParameterFileDestination, QgsProcessingParameterFolderDestination) from processing.algs.gdal.GdalAlgorithm import GdalAlgorithm @@ -210,12 +209,8 @@ def getConsoleCommands(self, parameters, context, feedback, executing=True): layers = [l.source() for l in self.parameterAsLayerList(parameters, self.INPUT, context)] arguments.extend(layers) - commands = [] + commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] if isWindows(): - commands = ['cmd.exe', '/C ', self.commandName() + '.bat', - GdalUtils.escapeAndJoin(arguments)] - else: - commands = [self.commandName() + '.py', - GdalUtils.escapeAndJoin(arguments)] + commands.insert(0, 'python3') return commands diff --git a/python/plugins/processing/algs/gdal/rgb2pct.py b/python/plugins/processing/algs/gdal/rgb2pct.py index ac123c42c0bd..450c395127fe 100644 --- a/python/plugins/processing/algs/gdal/rgb2pct.py +++ b/python/plugins/processing/algs/gdal/rgb2pct.py @@ -1,4 +1,3 @@ - # -*- coding: utf-8 -*- """ @@ -95,10 +94,8 @@ def getConsoleCommands(self, parameters, context, feedback, executing=True): arguments.append(raster.source()) arguments.append(out) + commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] if isWindows(): - commands = ['cmd.exe', '/C ', self.commandName() + '.bat', - GdalUtils.escapeAndJoin(arguments)] - else: - commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] + commands.insert(0, 'python3') return commands diff --git a/python/plugins/processing/algs/gdal/roughness.py b/python/plugins/processing/algs/gdal/roughness.py index 75e648f5442f..7065fd8d2085 100644 --- a/python/plugins/processing/algs/gdal/roughness.py +++ b/python/plugins/processing/algs/gdal/roughness.py @@ -57,6 +57,7 @@ def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, self.tr('Input layer'))) self.addParameter(QgsProcessingParameterBand(self.BAND, self.tr('Band number'), + 1, parentLayerParameterName=self.INPUT)) self.addParameter(QgsProcessingParameterBoolean(self.COMPUTE_EDGES, self.tr('Compute edges'), diff --git a/python/plugins/processing/algs/gdal/sieve.py b/python/plugins/processing/algs/gdal/sieve.py index 7f8eb59450aa..e9a86c763c1d 100644 --- a/python/plugins/processing/algs/gdal/sieve.py +++ b/python/plugins/processing/algs/gdal/sieve.py @@ -104,7 +104,7 @@ def getConsoleCommands(self, parameters, context, feedback, executing=True): if self.parameterAsBool(parameters, self.NO_MASK, context): arguments.append('-nomask') - mask = self.parameterAsRasterLayer(parameters, self.INPUT, context) + mask = self.parameterAsRasterLayer(parameters, self.MASK_LAYER, context) if mask: arguments.append('-mask {}'.format(mask.source())) @@ -119,11 +119,8 @@ def getConsoleCommands(self, parameters, context, feedback, executing=True): arguments.append(raster.source()) arguments.append(out) - commands = [] + commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] if isWindows(): - commands = ['cmd.exe', '/C ', self.commandName() + '.bat', - GdalUtils.escapeAndJoin(arguments)] - else: - commands = [self.commandName() + '.py', GdalUtils.escapeAndJoin(arguments)] + commands.insert(0, 'python3') return commands diff --git a/python/plugins/processing/algs/gdal/slope.py b/python/plugins/processing/algs/gdal/slope.py index 47c156ae4f20..386f9aff8ead 100644 --- a/python/plugins/processing/algs/gdal/slope.py +++ b/python/plugins/processing/algs/gdal/slope.py @@ -61,6 +61,7 @@ def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, self.tr('Input layer'))) self.addParameter(QgsProcessingParameterBand(self.BAND, self.tr('Band number'), + 1, parentLayerParameterName=self.INPUT)) self.addParameter(QgsProcessingParameterNumber(self.SCALE, self.tr('Ratio of vertical units to horizontal'), diff --git a/python/plugins/processing/algs/gdal/tpi.py b/python/plugins/processing/algs/gdal/tpi.py index 65f81f821244..431bf467c452 100644 --- a/python/plugins/processing/algs/gdal/tpi.py +++ b/python/plugins/processing/algs/gdal/tpi.py @@ -56,6 +56,7 @@ def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, self.tr('Input layer'))) self.addParameter(QgsProcessingParameterBand(self.BAND, self.tr('Band number'), + 1, parentLayerParameterName=self.INPUT)) self.addParameter(QgsProcessingParameterBoolean(self.COMPUTE_EDGES, self.tr('Compute edges'), diff --git a/python/plugins/processing/algs/gdal/tri.py b/python/plugins/processing/algs/gdal/tri.py index 71cfe7c1b004..0301ae57cfcb 100644 --- a/python/plugins/processing/algs/gdal/tri.py +++ b/python/plugins/processing/algs/gdal/tri.py @@ -55,6 +55,7 @@ def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT, self.tr('Input layer'))) self.addParameter(QgsProcessingParameterBand(self.BAND, self.tr('Band number'), + 1, parentLayerParameterName=self.INPUT)) self.addParameter(QgsProcessingParameterBoolean(self.COMPUTE_EDGES, self.tr('Compute edges'), diff --git a/python/plugins/processing/algs/grass7/description/r.mapcalc.simple.txt b/python/plugins/processing/algs/grass7/description/r.mapcalc.simple.txt new file mode 100644 index 000000000000..ad233ab05538 --- /dev/null +++ b/python/plugins/processing/algs/grass7/description/r.mapcalc.simple.txt @@ -0,0 +1,11 @@ +r.mapcalc.simple +Calculate new raster map from a r.mapcalc expression. +Raster (r.*) +QgsProcessingParameterRasterLayer|a|Raster layer A|False +QgsProcessingParameterRasterLayer|b|Raster layer B|True +QgsProcessingParameterRasterLayer|c|Raster layer C|True +QgsProcessingParameterRasterLayer|d|Raster layer D|True +QgsProcessingParameterRasterLayer|e|Raster layer E|True +QgsProcessingParameterRasterLayer|f|Raster layer F|True +QgsProcessingParameterString|expression|Formula|A*2 +QgsProcessingParameterRasterDestination|output|Calculated diff --git a/python/plugins/processing/algs/grass7/description/r.mapcalc.txt b/python/plugins/processing/algs/grass7/description/r.mapcalc.txt deleted file mode 100644 index 06751ab650f6..000000000000 --- a/python/plugins/processing/algs/grass7/description/r.mapcalc.txt +++ /dev/null @@ -1,9 +0,0 @@ -r.mapcalc -Raster map calculator. -Raster (r.*) -QgsProcessingParameterMultipleLayers|maps|Raster maps used in the calculator|3|None|True -QgsProcessingParameterString|expression|Expression to evaluate. Syntax e.g. `raster_out=raster1+raster2`. Use original filenames, without extension|None|True|True -QgsProcessingParameterFile|file|File containing expression(s) to evaluate (same rule for raster names than above)|QgsProcessingParameterFile.File|txt|None|True -QgsProcessingParameterString|seed|Integer seed for rand() function|None|False|True -*QgsProcessingParameterBoolean|-s|Generate random seed (result is non-deterministic)|False -QgsProcessingParameterFolderDestination|output_dir|Results Directory diff --git a/python/plugins/processing/algs/grass7/ext/r_mapcalc.py b/python/plugins/processing/algs/grass7/ext/r_mapcalc.py deleted file mode 100644 index 9df6f4edd690..000000000000 --- a/python/plugins/processing/algs/grass7/ext/r_mapcalc.py +++ /dev/null @@ -1,65 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -*************************************************************************** - r_mapcalc.py - ------------ - Date : February 2016 - Copyright : (C) 2016 by Médéric Ribreux - Email : medspx at medspx dot fr -*************************************************************************** -* * -* 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. * -* * -*************************************************************************** -""" - -__author__ = 'Médéric Ribreux' -__date__ = 'February 2016' -__copyright__ = '(C) 2016, Médéric Ribreux' - -# This will get replaced with a git SHA1 when you do a git archive - -__revision__ = '$Format:%H$' - -import os - - -def checkParameterValuesBeforeExecuting(alg, parameters, context): - """ Verify if we have the right parameters """ - if (alg.parameterAsString(parameters, 'expression', context) - and alg.parameterAsString(parameters, 'file', context)): - return False, alg.tr("You need to set either inline expression or a rules file!") - - return True, None - - -def processInputs(alg, parameters, context, feedback): - # We will use the same raster names than in QGIS to name the rasters in GRASS - rasters = alg.parameterAsLayerList(parameters, 'maps', context) - for idx, raster in enumerate(rasters): - rasterName = os.path.splitext( - os.path.basename(raster.source()))[0] - alg.inputLayers.append(raster) - alg.setSessionProjectionFromLayer(raster) - command = 'r.in.gdal input="{0}" output="{1}" --overwrite -o'.format( - os.path.normpath(raster.source()), - rasterName) - alg.commands.append(command) - - alg.removeParameter('maps') - alg.postInputs() - - -def processCommand(alg, parameters, context, feedback): - alg.processCommand(parameters, context, feedback, True) - - -def processOutputs(alg, parameters, context, feedback): - # We need to export every raster from the GRASSDB - alg.exportRasterLayersIntoDirectory('output_dir', - parameters, context, - wholeDB=True) diff --git a/python/plugins/processing/algs/qgis/IdwInterpolation.py b/python/plugins/processing/algs/qgis/IdwInterpolation.py index dd91ef7d9eb6..d2f9f0204560 100644 --- a/python/plugins/processing/algs/qgis/IdwInterpolation.py +++ b/python/plugins/processing/algs/qgis/IdwInterpolation.py @@ -31,9 +31,9 @@ from qgis.core import (QgsRectangle, QgsProcessingUtils, - QgsProcessingParameterDefinition, QgsProcessingParameterNumber, QgsProcessingParameterExtent, + QgsProcessingParameterDefinition, QgsProcessingParameterRasterDestination, QgsProcessingException) from qgis.analysis import (QgsInterpolator, @@ -41,52 +41,16 @@ QgsGridFileWriter) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm +from processing.algs.qgis.ui.InterpolationWidgets import ParameterInterpolationData, ParameterPixelSize pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] -class ParameterInterpolationData(QgsProcessingParameterDefinition): - - def __init__(self, name='', description=''): - super().__init__(name, description) - self.setMetadata({ - 'widget_wrapper': 'processing.algs.qgis.ui.InterpolationDataWidget.InterpolationDataWidgetWrapper' - }) - - def type(self): - return 'idw_interpolation_data' - - def clone(self): - return ParameterInterpolationData(self.name(), self.description()) - - @staticmethod - def parseValue(value): - if value is None: - return None - - if value == '': - return None - - if isinstance(value, str): - return value if value != '' else None - else: - return ParameterInterpolationData.dataToString(value) - - @staticmethod - def dataToString(data): - s = '' - for c in data: - s += '{}::~::{}::~::{:d}::~::{:d};'.format(c[0], - c[1], - c[2], - c[3]) - return s[:-1] - - class IdwInterpolation(QgisAlgorithm): INTERPOLATION_DATA = 'INTERPOLATION_DATA' DISTANCE_COEFFICIENT = 'DISTANCE_COEFFICIENT' + PIXEL_SIZE = 'PIXEL_SIZE' COLUMNS = 'COLUMNS' ROWS = 'ROWS' EXTENT = 'EXTENT' @@ -111,15 +75,31 @@ def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterNumber(self.DISTANCE_COEFFICIENT, self.tr('Distance coefficient P'), type=QgsProcessingParameterNumber.Double, minValue=0.0, maxValue=99.99, defaultValue=2.0)) - self.addParameter(QgsProcessingParameterNumber(self.COLUMNS, - self.tr('Number of columns'), - minValue=0, maxValue=10000000, defaultValue=300)) - self.addParameter(QgsProcessingParameterNumber(self.ROWS, - self.tr('Number of rows'), - minValue=0, maxValue=10000000, defaultValue=300)) self.addParameter(QgsProcessingParameterExtent(self.EXTENT, self.tr('Extent'), optional=False)) + pixel_size_param = ParameterPixelSize(self.PIXEL_SIZE, + self.tr('Output raster size'), + layersData=self.INTERPOLATION_DATA, + extent=self.EXTENT, + minValue=0.0, + default=0.1) + self.addParameter(pixel_size_param) + + cols_param = QgsProcessingParameterNumber(self.COLUMNS, + self.tr('Number of columns'), + optional=True, + minValue=0, maxValue=10000000) + cols_param.setFlags(cols_param.flags() | QgsProcessingParameterDefinition.FlagHidden) + self.addParameter(cols_param) + + rows_param = QgsProcessingParameterNumber(self.ROWS, + self.tr('Number of rows'), + optional=True, + minValue=0, maxValue=10000000) + rows_param.setFlags(rows_param.flags() | QgsProcessingParameterDefinition.FlagHidden) + self.addParameter(rows_param) + self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Interpolated'))) @@ -132,11 +112,17 @@ def displayName(self): def processAlgorithm(self, parameters, context, feedback): interpolationData = ParameterInterpolationData.parseValue(parameters[self.INTERPOLATION_DATA]) coefficient = self.parameterAsDouble(parameters, self.DISTANCE_COEFFICIENT, context) - columns = self.parameterAsInt(parameters, self.COLUMNS, context) - rows = self.parameterAsInt(parameters, self.ROWS, context) bbox = self.parameterAsExtent(parameters, self.EXTENT, context) + pixel_size = self.parameterAsDouble(parameters, self.PIXEL_SIZE, context) output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) + columns = self.parameterAsInt(parameters, self.COLUMNS, context) + rows = self.parameterAsInt(parameters, self.ROWS, context) + if columns == 0: + columns = max(round(bbox.width() / pixel_size) + 1, 1) + if rows == 0: + rows = max(round(bbox.height() / pixel_size) + 1, 1) + if interpolationData is None: raise QgsProcessingException( self.tr('You need to specify at least one input layer.')) diff --git a/python/plugins/processing/algs/qgis/RasterCalculator.py b/python/plugins/processing/algs/qgis/RasterCalculator.py index 7055bc4fae01..9210edc0fe29 100644 --- a/python/plugins/processing/algs/qgis/RasterCalculator.py +++ b/python/plugins/processing/algs/qgis/RasterCalculator.py @@ -166,6 +166,11 @@ def _cellsize(layer): entry.bandNumber = n + 1 entries.append(entry) + # Append any missing entry from the current project + for entry in QgsRasterCalculatorEntry.rasterEntries(): + if not [e for e in entries if e.ref == entry.ref]: + entries.append(entry) + output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) width = round((bbox.xMaximum() - bbox.xMinimum()) / cellsize) @@ -230,8 +235,8 @@ def mappedNameToLayer(self, lyr, expression, layersDict, context): expContextAlgInputsScope = context.expressionContext().scope(indexOfScope) # check for the layers that are mapped as input in a model - # to do this check in the latest scope all passed variables - # to look for a variable that is a layer or a string filename ç + # to do this check in the latest scope all passed variables + # to look for a variable that is a layer or a string filename # to a layer varDescription = None for varName in expContextAlgInputsScope.variableNames(): @@ -257,7 +262,7 @@ def mappedNameToLayer(self, lyr, expression, layersDict, context): # but var in expression is called simply # 'Output' from algorithm 'calc1' - # get the translatin string to use to parse the description + # get the translation string to use to parse the description # HAVE to use the same translated string as in # https://github.com/qgis/QGIS/blob/master/src/core/processing/models/qgsprocessingmodelalgorithm.cpp#L516 translatedDesc = self.tr("Output '%1' from algorithm '%2'") diff --git a/python/plugins/processing/algs/qgis/TinInterpolation.py b/python/plugins/processing/algs/qgis/TinInterpolation.py index 0839017a4bb3..f215ad15d123 100644 --- a/python/plugins/processing/algs/qgis/TinInterpolation.py +++ b/python/plugins/processing/algs/qgis/TinInterpolation.py @@ -31,10 +31,10 @@ from qgis.core import (QgsProcessingUtils, QgsProcessing, - QgsProcessingParameterDefinition, QgsProcessingParameterEnum, QgsProcessingParameterNumber, QgsProcessingParameterExtent, + QgsProcessingParameterDefinition, QgsProcessingParameterRasterDestination, QgsWkbTypes, QgsProcessingParameterFeatureSink, @@ -45,51 +45,15 @@ QgsGridFileWriter) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm +from processing.algs.qgis.ui.InterpolationWidgets import ParameterInterpolationData, ParameterPixelSize pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] -class ParameterInterpolationData(QgsProcessingParameterDefinition): - - def __init__(self, name='', description=''): - super().__init__(name, description) - self.setMetadata({ - 'widget_wrapper': 'processing.algs.qgis.ui.InterpolationDataWidget.InterpolationDataWidgetWrapper' - }) - - def type(self): - return 'tin_interpolation_data' - - def clone(self): - return ParameterInterpolationData(self.name(), self.description()) - - @staticmethod - def parseValue(value): - if value is None: - return None - - if value == '': - return None - - if isinstance(value, str): - return value if value != '' else None - else: - return ParameterInterpolationData.dataToString(value) - - @staticmethod - def dataToString(data): - s = '' - for c in data: - s += '{}::~:: {}::~:: {:d}::~:: {:d};'.format(c[0], - c[1], - c[2], - c[3]) - return s[:-1] - - class TinInterpolation(QgisAlgorithm): INTERPOLATION_DATA = 'INTERPOLATION_DATA' METHOD = 'METHOD' + PIXEL_SIZE = 'PIXEL_SIZE' COLUMNS = 'COLUMNS' ROWS = 'ROWS' EXTENT = 'EXTENT' @@ -119,15 +83,31 @@ def initAlgorithm(self, config=None): self.tr('Interpolation method'), options=self.METHODS, defaultValue=0)) - self.addParameter(QgsProcessingParameterNumber(self.COLUMNS, - self.tr('Number of columns'), - minValue=0, maxValue=10000000, defaultValue=300)) - self.addParameter(QgsProcessingParameterNumber(self.ROWS, - self.tr('Number of rows'), - minValue=0, maxValue=10000000, defaultValue=300)) self.addParameter(QgsProcessingParameterExtent(self.EXTENT, self.tr('Extent'), optional=False)) + pixel_size_param = ParameterPixelSize(self.PIXEL_SIZE, + self.tr('Output raster size'), + layersData=self.INTERPOLATION_DATA, + extent=self.EXTENT, + minValue=0.0, + default=0.1) + self.addParameter(pixel_size_param) + + cols_param = QgsProcessingParameterNumber(self.COLUMNS, + self.tr('Number of columns'), + optional=True, + minValue=0, maxValue=10000000) + cols_param.setFlags(cols_param.flags() | QgsProcessingParameterDefinition.FlagHidden) + self.addParameter(cols_param) + + rows_param = QgsProcessingParameterNumber(self.ROWS, + self.tr('Number of rows'), + optional=True, + minValue=0, maxValue=10000000) + rows_param.setFlags(rows_param.flags() | QgsProcessingParameterDefinition.FlagHidden) + self.addParameter(rows_param) + self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Interpolated'))) @@ -147,11 +127,17 @@ def displayName(self): def processAlgorithm(self, parameters, context, feedback): interpolationData = ParameterInterpolationData.parseValue(parameters[self.INTERPOLATION_DATA]) method = self.parameterAsEnum(parameters, self.METHOD, context) - columns = self.parameterAsInt(parameters, self.COLUMNS, context) - rows = self.parameterAsInt(parameters, self.ROWS, context) bbox = self.parameterAsExtent(parameters, self.EXTENT, context) + pixel_size = self.parameterAsDouble(parameters, self.PIXEL_SIZE, context) output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) + columns = self.parameterAsInt(parameters, self.COLUMNS, context) + rows = self.parameterAsInt(parameters, self.ROWS, context) + if columns == 0: + columns = max(round(bbox.width() / pixel_size) + 1, 1) + if rows == 0: + rows = max(round(bbox.height() / pixel_size) + 1, 1) + if interpolationData is None: raise QgsProcessingException( self.tr('You need to specify at least one input layer.')) diff --git a/python/plugins/processing/algs/qgis/ui/InterpolationDataWidget.py b/python/plugins/processing/algs/qgis/ui/InterpolationDataWidget.py deleted file mode 100644 index 0049ac0f0847..000000000000 --- a/python/plugins/processing/algs/qgis/ui/InterpolationDataWidget.py +++ /dev/null @@ -1,171 +0,0 @@ -# -*- coding: utf-8 -*- - -""" -*************************************************************************** - InterpolationDataWidget.py - --------------------- - Date : December 2016 - Copyright : (C) 2016 by Alexander Bruy - Email : alexander dot bruy 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. * -* * -*************************************************************************** -""" - -__author__ = 'Alexander Bruy' -__date__ = 'December 2016' -__copyright__ = '(C) 2016, Alexander Bruy' - -# This will get replaced with a git SHA1 when you do a git archive - -__revision__ = '$Format:%H$' - -import os - -from qgis.PyQt import uic -from qgis.PyQt.QtCore import pyqtSlot -from qgis.PyQt.QtWidgets import (QTreeWidgetItem, - QComboBox - ) -from qgis.core import (QgsApplication, - QgsMapLayer, - QgsMapLayerProxyModel, - QgsWkbTypes, - QgsProcessingUtils - ) -from qgis.core import QgsFieldProxyModel -from qgis.analysis import QgsInterpolator - -from processing.gui.wrappers import WidgetWrapper -from processing.tools import dataobjects - -pluginPath = os.path.dirname(__file__) -WIDGET, BASE = uic.loadUiType(os.path.join(pluginPath, 'interpolationdatawidgetbase.ui')) - - -class InterpolationDataWidget(BASE, WIDGET): - - def __init__(self): - super(InterpolationDataWidget, self).__init__(None) - self.setupUi(self) - - self.btnAdd.setIcon(QgsApplication.getThemeIcon('/symbologyAdd.svg')) - self.btnRemove.setIcon(QgsApplication.getThemeIcon('/symbologyRemove.svg')) - - self.cmbLayers.layerChanged.connect(self.layerChanged) - self.cmbLayers.setFilters(QgsMapLayerProxyModel.VectorLayer) - self.cmbFields.setFilters(QgsFieldProxyModel.Numeric) - self.cmbFields.setLayer(self.cmbLayers.currentLayer()) - - @pyqtSlot() - def on_btnAdd_clicked(self): - layer = self.cmbLayers.currentLayer() - - attribute = '' - if self.chkUseZCoordinate.isChecked(): - attribute = 'Z_COORD' - else: - attribute = self.cmbFields.currentField() - - self.addLayerData(layer.name(), attribute) - - @pyqtSlot() - def on_btnRemove_clicked(self): - item = self.layersTree.currentItem() - if not item: - return - self.layersTree.invisibleRootItem().removeChild(item) - - def layerChanged(self, layer): - self.chkUseZCoordinate.setEnabled(False) - self.chkUseZCoordinate.setChecked(False) - - if layer is None or not layer.isValid(): - return - - provider = layer.dataProvider() - if not provider: - return - - if QgsWkbTypes.hasZ(provider.wkbType()): - self.chkUseZCoordinate.setEnabled(True) - - self.cmbFields.setLayer(layer) - - def addLayerData(self, layerName, attribute): - item = QTreeWidgetItem() - item.setText(0, layerName) - item.setText(1, attribute) - self.layersTree.addTopLevelItem(item) - - comboBox = QComboBox() - comboBox.addItem(self.tr('Points')) - comboBox.addItem(self.tr('Structure lines')) - comboBox.addItem(self.tr('Break lines')) - comboBox.setCurrentIndex(0) - self.layersTree.setItemWidget(item, 2, comboBox) - - def setValue(self, value): - self.layersTree.clear() - rows = value.split(';') - for i, r in enumerate(rows): - v = r.split('::~::') - self.addLayerData(v[0], v[1]) - - comboBox = self.layersTree.itemWidget(self.layersTree.topLevelItem(i), 2) - comboBox.setCurrentIndex(comboBox.findText(v[3])) - - def value(self): - layers = '' - context = dataobjects.createContext() - for i in range(self.layersTree.topLevelItemCount()): - item = self.layersTree.topLevelItem(i) - if item: - layerName = item.text(0) - layer = QgsProcessingUtils.mapLayerFromString(layerName, context) - if not layer: - continue - - provider = layer.dataProvider() - if not provider: - continue - - interpolationAttribute = item.text(1) - interpolationSource = QgsInterpolator.ValueAttribute - if interpolationAttribute == 'Z_COORD': - interpolationSource = QgsInterpolator.ValueZ - fieldIndex = -1 - else: - fieldIndex = layer.fields().indexFromName(interpolationAttribute) - - comboBox = self.layersTree.itemWidget(self.layersTree.topLevelItem(i), 2) - inputTypeName = comboBox.currentText() - if inputTypeName == self.tr('Points'): - inputType = QgsInterpolator.SourcePoints - elif inputTypeName == self.tr('Structure lines'): - inputType = QgsInterpolator.SourceStructureLines - else: - inputType = QgsInterpolator.SourceBreakLines - - layers += '{}::~::{:d}::~::{:d}::~::{:d};'.format(layer.source(), - interpolationSource, - fieldIndex, - inputType) - return layers[:-1] - - -class InterpolationDataWidgetWrapper(WidgetWrapper): - - def createWidget(self): - return InterpolationDataWidget() - - def setValue(self, value): - self.widget.setValue(value) - - def value(self): - return self.widget.value() diff --git a/python/plugins/processing/algs/qgis/ui/InterpolationWidgets.py b/python/plugins/processing/algs/qgis/ui/InterpolationWidgets.py new file mode 100644 index 000000000000..1267ea74cb3e --- /dev/null +++ b/python/plugins/processing/algs/qgis/ui/InterpolationWidgets.py @@ -0,0 +1,402 @@ +# -*- coding: utf-8 -*- + +""" +*************************************************************************** + InterpolationDataWidget.py + --------------------- + Date : December 2016 + Copyright : (C) 2016 by Alexander Bruy + Email : alexander dot bruy 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. * +* * +*************************************************************************** +""" + +__author__ = 'Alexander Bruy' +__date__ = 'December 2016' +__copyright__ = '(C) 2016, Alexander Bruy' + +# This will get replaced with a git SHA1 when you do a git archive + +__revision__ = '$Format:%H$' + +import os + +from qgis.PyQt import uic +from qgis.PyQt.QtCore import pyqtSignal +from qgis.PyQt.QtWidgets import (QTreeWidgetItem, + QComboBox + ) +from qgis.core import (QgsApplication, + QgsMapLayer, + QgsMapLayerProxyModel, + QgsWkbTypes, + QgsRectangle, + QgsReferencedRectangle, + QgsCoordinateReferenceSystem, + QgsProcessingUtils, + QgsProcessingParameterNumber, + QgsProcessingParameterDefinition + ) +from qgis.core import QgsFieldProxyModel +from qgis.analysis import QgsInterpolator + +from processing.gui.wrappers import WidgetWrapper, DIALOG_STANDARD +from processing.tools import dataobjects + +pluginPath = os.path.dirname(__file__) + + +class ParameterInterpolationData(QgsProcessingParameterDefinition): + + def __init__(self, name='', description=''): + super().__init__(name, description) + self.setMetadata({ + 'widget_wrapper': 'processing.algs.qgis.ui.InterpolationWidgets.InterpolationDataWidgetWrapper' + }) + + def type(self): + return 'idw_interpolation_data' + + def clone(self): + return ParameterInterpolationData(self.name(), self.description()) + + @staticmethod + def parseValue(value): + if value is None: + return None + + if value == '': + return None + + if isinstance(value, str): + return value if value != '' else None + else: + return ParameterInterpolationData.dataToString(value) + + @staticmethod + def dataToString(data): + s = '' + for c in data: + s += '{}::~::{}::~::{:d}::~::{:d};'.format(c[0], + c[1], + c[2], + c[3]) + return s[:-1] + + +WIDGET, BASE = uic.loadUiType(os.path.join(pluginPath, 'interpolationdatawidgetbase.ui')) + + +class InterpolationDataWidget(BASE, WIDGET): + + hasChanged = pyqtSignal() + + def __init__(self): + super(InterpolationDataWidget, self).__init__(None) + self.setupUi(self) + + self.btnAdd.setIcon(QgsApplication.getThemeIcon('/symbologyAdd.svg')) + self.btnRemove.setIcon(QgsApplication.getThemeIcon('/symbologyRemove.svg')) + + self.btnAdd.clicked.connect(self.addLayer) + self.btnRemove.clicked.connect(self.removeLayer) + + self.cmbLayers.layerChanged.connect(self.layerChanged) + self.cmbLayers.setFilters(QgsMapLayerProxyModel.VectorLayer) + self.cmbFields.setFilters(QgsFieldProxyModel.Numeric) + self.cmbFields.setLayer(self.cmbLayers.currentLayer()) + + def addLayer(self): + layer = self.cmbLayers.currentLayer() + + attribute = '' + if self.chkUseZCoordinate.isChecked(): + attribute = 'Z_COORD' + else: + attribute = self.cmbFields.currentField() + + self._addLayerData(layer.name(), attribute) + self.hasChanged.emit() + + def removeLayer(self): + item = self.layersTree.currentItem() + if not item: + return + self.layersTree.invisibleRootItem().removeChild(item) + self.hasChanged.emit() + + def layerChanged(self, layer): + self.chkUseZCoordinate.setEnabled(False) + self.chkUseZCoordinate.setChecked(False) + + if layer is None or not layer.isValid(): + return + + provider = layer.dataProvider() + if not provider: + return + + if QgsWkbTypes.hasZ(provider.wkbType()): + self.chkUseZCoordinate.setEnabled(True) + + self.cmbFields.setLayer(layer) + + def _addLayerData(self, layerName, attribute): + item = QTreeWidgetItem() + item.setText(0, layerName) + item.setText(1, attribute) + self.layersTree.addTopLevelItem(item) + + comboBox = QComboBox() + comboBox.addItem(self.tr('Points')) + comboBox.addItem(self.tr('Structure lines')) + comboBox.addItem(self.tr('Break lines')) + comboBox.setCurrentIndex(0) + self.layersTree.setItemWidget(item, 2, comboBox) + + def setValue(self, value): + self.layersTree.clear() + rows = value.split(';') + for i, r in enumerate(rows): + v = r.split('::~::') + self.addLayerData(v[0], v[1]) + + comboBox = self.layersTree.itemWidget(self.layersTree.topLevelItem(i), 2) + comboBox.setCurrentIndex(comboBox.findText(v[3])) + self.hasChanged.emit() + + def value(self): + layers = '' + context = dataobjects.createContext() + for i in range(self.layersTree.topLevelItemCount()): + item = self.layersTree.topLevelItem(i) + if item: + layerName = item.text(0) + layer = QgsProcessingUtils.mapLayerFromString(layerName, context) + if not layer: + continue + + provider = layer.dataProvider() + if not provider: + continue + + interpolationAttribute = item.text(1) + interpolationSource = QgsInterpolator.ValueAttribute + if interpolationAttribute == 'Z_COORD': + interpolationSource = QgsInterpolator.ValueZ + fieldIndex = -1 + else: + fieldIndex = layer.fields().indexFromName(interpolationAttribute) + + comboBox = self.layersTree.itemWidget(self.layersTree.topLevelItem(i), 2) + inputTypeName = comboBox.currentText() + if inputTypeName == self.tr('Points'): + inputType = QgsInterpolator.SourcePoints + elif inputTypeName == self.tr('Structure lines'): + inputType = QgsInterpolator.SourceStructureLines + else: + inputType = QgsInterpolator.SourceBreakLines + + layers += '{}::~::{:d}::~::{:d}::~::{:d};'.format(layer.source(), + interpolationSource, + fieldIndex, + inputType) + return layers[:-1] + + +class InterpolationDataWidgetWrapper(WidgetWrapper): + + def createWidget(self): + widget = InterpolationDataWidget() + widget.hasChanged.connect(lambda: self.widgetValueHasChanged.emit(self)) + return widget + + def setValue(self, value): + self.widget.setValue(value) + + def value(self): + return self.widget.value() + + +class ParameterPixelSize(QgsProcessingParameterNumber): + + def __init__(self, name='', description='', layersData=None, extent=None, minValue=None, default=None, optional=False): + QgsProcessingParameterNumber.__init__(self, name, description, QgsProcessingParameterNumber.Double, default, optional, minValue) + self.setMetadata({ + 'widget_wrapper': 'processing.algs.qgis.ui.InterpolationWidgets.PixelSizeWidgetWrapper' + }) + + self.layersData = layersData + self.extent = extent + self.layers = [] + + def clone(self): + copy = ParameterPixelSize(self.name(), self.description(), self.layersData, self.extent, self.minimum(), self.defaultValue(), self.flags() & QgsProcessingParameterDefinition.FlagOptional) + return copy + + +WIDGET, BASE = uic.loadUiType(os.path.join(pluginPath, 'RasterResolutionWidget.ui')) + + +class PixelSizeWidget(BASE, WIDGET): + + def __init__(self): + super(PixelSizeWidget, self).__init__(None) + self.setupUi(self) + self.context = dataobjects.createContext() + + self.extent = QgsRectangle() + self.layers = [] + + self.mCellXSpinBox.setShowClearButton(False) + self.mCellYSpinBox.setShowClearButton(False) + self.mRowsSpinBox.setShowClearButton(False) + self.mColumnsSpinBox.setShowClearButton(False) + + self.mCellYSpinBox.valueChanged.connect(self.mCellXSpinBox.setValue) + self.mCellXSpinBox.valueChanged.connect(self.pixelSizeChanged) + self.mRowsSpinBox.valueChanged.connect(self.rowsChanged) + self.mColumnsSpinBox.valueChanged.connect(self.columnsChanged) + + def setLayers(self, layersData): + self.extent = QgsRectangle() + self.layers = [] + for row in layersData.split(';'): + v = row.split('::~::') + # need to keep a reference until interpolation is complete + layer = QgsProcessingUtils.variantToSource(v[0], self.context) + if layer: + self.layers.append(layer) + bbox = layer.sourceExtent() + if self.extent.isEmpty(): + self.extent = bbox + else: + self.extent.combineExtentWith(bbox) + + self.pixelSizeChanged() + + def setExtent(self, extent): + if extent is not None: + tokens = extent.split(' ')[0].split(',') + ext = QgsRectangle(float(tokens[0]), float(tokens[2]), float(tokens[1]), float(tokens[3])) + if len(tokens) > 1: + self.extent = QgsReferencedRectangle(ext, QgsCoordinateReferenceSystem(tokens[1][1:-1])) + else: + self.extent = ext + self.pixelSizeChanged() + + def pixelSizeChanged(self): + cell_size = self.mCellXSpinBox.value() + if cell_size <= 0: + return + + self.mCellYSpinBox.blockSignals(True) + self.mCellYSpinBox.setValue(cell_size) + self.mCellYSpinBox.blockSignals(False) + rows = max(round(self.extent.height() / cell_size) + 1, 1) + cols = max(round(self.extent.width() / cell_size) + 1, 1) + self.mRowsSpinBox.blockSignals(True) + self.mRowsSpinBox.setValue(rows) + self.mRowsSpinBox.blockSignals(False) + self.mColumnsSpinBox.blockSignals(True) + self.mColumnsSpinBox.setValue(cols) + self.mColumnsSpinBox.blockSignals(False) + + def rowsChanged(self): + rows = self.mRowsSpinBox.value() + if rows <= 0: + return + cell_size = self.extent.height() / rows + cols = max(round(self.extent.width() / cell_size) + 1, 1) + self.mColumnsSpinBox.blockSignals(True) + self.mColumnsSpinBox.setValue(cols) + self.mColumnsSpinBox.blockSignals(False) + for w in [self.mCellXSpinBox, self.mCellYSpinBox]: + w.blockSignals(True) + w.setValue(cell_size) + w.blockSignals(False) + + def columnsChanged(self): + cols = self.mColumnsSpinBox.value() + if cols < 2: + return + cell_size = self.extent.width() / (cols - 1) + rows = max(round(self.extent.height() / cell_size), 1) + self.mRowsSpinBox.blockSignals(True) + self.mRowsSpinBox.setValue(rows) + self.mRowsSpinBox.blockSignals(False) + for w in [self.mCellXSpinBox, self.mCellYSpinBox]: + w.blockSignals(True) + w.setValue(cell_size) + w.blockSignals(False) + + def setValue(self, value): + try: + numeric_value = float(value) + except: + return False + + self.mCellXSpinBox.setValue(numeric_value) + self.mCellYSpinBox.setValue(numeric_value) + return True + + def value(self): + return self.mCellXSpinBox.value() + + +class PixelSizeWidgetWrapper(WidgetWrapper): + + def __init__(self, param, dialog, row=0, col=0, **kwargs): + super().__init__(param, dialog, row, col, **kwargs) + self.context = dataobjects.createContext() + + def _panel(self): + return PixelSizeWidget() + + def createWidget(self): + if self.dialogType == DIALOG_STANDARD: + return self._panel() + else: + w = QgsDoubleSpinBox() + w.setShowClearButton(False) + w.setMinimum(0) + w.setMaximum(99999999999) + w.setDecimals(6) + w.setToolTip(self.tr('Resolution of each pixel in output raster, in layer units')) + return w + + def postInitialize(self, wrappers): + if self.dialogType != DIALOG_STANDARD: + return + + for wrapper in wrappers: + if wrapper.parameterDefinition().name() == self.param.layersData: + self.setLayers(wrapper.parameterValue()) + wrapper.widgetValueHasChanged.connect(self.layersChanged) + elif wrapper.parameterDefinition().name() == self.param.extent: + self.setExtent(wrapper.parameterValue()) + wrapper.widgetValueHasChanged.connect(self.extentChanged) + + def layersChanged(self, wrapper): + self.setLayers(wrapper.parameterValue()) + + def setLayers(self, layersData): + self.widget.setLayers(layersData) + + def extentChanged(self, wrapper): + self.setExtent(wrapper.parameterValue()) + + def setExtent(self, extent): + self.widget.setExtent(extent) + + def setValue(self, value): + return self.widget.setValue(value) + + def value(self): + return self.widget.value() diff --git a/python/plugins/processing/algs/saga/description/FillSinksXXL(WangLiu).txt b/python/plugins/processing/algs/saga/description/FillSinksXXL(WangLiu).txt index 2f9c12bb99f5..ed69b7d3213c 100644 --- a/python/plugins/processing/algs/saga/description/FillSinksXXL(WangLiu).txt +++ b/python/plugins/processing/algs/saga/description/FillSinksXXL(WangLiu).txt @@ -1,5 +1,5 @@ Fill Sinks XXL (Wang & Liu) ta_preprocessor QgsProcessingParameterRasterLayer|ELEV|DEM|None|False -QgsProcessingParameterNumber|MINSLOPE|Minimum Slope [Degree]|QgsProcessingParameterNumber.Double|0.01|False|0.0|None +QgsProcessingParameterNumber|MINSLOPE|Minimum Slope [Degree]|QgsProcessingParameterNumber.Double|0.1|False|0.0|None QgsProcessingParameterRasterDestination|FILLED|Filled DEM diff --git a/python/plugins/processing/algs/saga/description/Resampling.txt b/python/plugins/processing/algs/saga/description/Resampling.txt index 6a063813e796..b07e8e07c8a9 100644 --- a/python/plugins/processing/algs/saga/description/Resampling.txt +++ b/python/plugins/processing/algs/saga/description/Resampling.txt @@ -3,8 +3,8 @@ grid_tools QgsProcessingParameterRasterLayer|INPUT|Grid|None|False QgsProcessingParameterBoolean|KEEP_TYPE|Preserve Data Type|True Hardcoded|-TARGET_DEFINITION 0 -QgsProcessingParameterEnum|SCALE_UP|Upscaling Method|[0] Nearest Neighbor;[1] Bilinear Interpolation;[2] Inverse Distance Interpolation;[3] Bicubic Spline Interpolation;[4] B-Spline Interpolation;[5] Mean Value;[6] Mean Value (cell area weighted);[7] Minimum Value;[8] Maximum Value;[9] Majority -QgsProcessingParameterEnum|SCALE_DOWN|Downscaling Method|[0] Nearest Neighbor;[1] Bilinear Interpolation;[2] Inverse Distance Interpolation;[3] Bicubic Spline Interpolation;[4] B-Spline Interpolation +QgsProcessingParameterEnum|SCALE_UP|Upscaling Method|[0] Nearest Neighbor;[1] Bilinear Interpolation;[2] Bicubic Spline Interpolation;[3] B-Spline Interpolation;[4] Mean Value;[5] Mean Value (cell area weighted);[6] Minimum Value;[7] Maximum Value;[8] Majority|False|5 +QgsProcessingParameterEnum|SCALE_DOWN|Downscaling Method|[0] Nearest Neighbor;[1] Bilinear Interpolation;[2] Bicubic Spline Interpolation;[3] B-Spline Interpolation|False|3 QgsProcessingParameterExtent|TARGET_USER_XMIN TARGET_USER_XMAX TARGET_USER_YMIN TARGET_USER_YMAX|Output extent|None|True QgsProcessingParameterNumber|TARGET_USER_SIZE|Cellsize|QgsProcessingParameterNumber.Double|100.0|False|None|None QgsProcessingParameterEnum|TARGET_USER_FITS|Fit|[0] nodes;[1] cells diff --git a/python/plugins/processing/gui/AlgorithmExecutor.py b/python/plugins/processing/gui/AlgorithmExecutor.py index 3dfde908456b..dcb0b8e9d472 100644 --- a/python/plugins/processing/gui/AlgorithmExecutor.py +++ b/python/plugins/processing/gui/AlgorithmExecutor.py @@ -34,6 +34,7 @@ QgsMessageLog, QgsProcessingException, QgsProcessingFeatureSourceDefinition, + QgsProcessingFeatureSource, QgsProcessingParameters, QgsProject, QgsFeatureRequest, @@ -94,6 +95,13 @@ def execute_in_place_run(alg, parameters, context=None, feedback=None, raise_exc if context is None: context = dataobjects.createContext(feedback) + # Only feature based algs have sourceFlags + try: + if alg.sourceFlags() & QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks: + context.setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck) + except AttributeError: + pass + active_layer = parameters['INPUT'] # Run some checks and prepare the layer for in-place execution by: diff --git a/python/plugins/processing/gui/ExtentSelectionPanel.py b/python/plugins/processing/gui/ExtentSelectionPanel.py index 1615edd61aca..1ada74f25542 100644 --- a/python/plugins/processing/gui/ExtentSelectionPanel.py +++ b/python/plugins/processing/gui/ExtentSelectionPanel.py @@ -31,7 +31,7 @@ from qgis.PyQt import uic from qgis.PyQt.QtWidgets import QMenu, QAction, QInputDialog from qgis.PyQt.QtGui import QCursor -from qgis.PyQt.QtCore import QCoreApplication +from qgis.PyQt.QtCore import QCoreApplication, pyqtSignal from qgis.gui import QgsMessageBar from qgis.utils import iface @@ -56,10 +56,14 @@ class ExtentSelectionPanel(BASE, WIDGET): + hasChanged = pyqtSignal() + def __init__(self, dialog, param): super(ExtentSelectionPanel, self).__init__(None) self.setupUi(self) + self.leText.textChanged.connect(lambda: self.hasChanged.emit()) + self.dialog = dialog self.param = param self.crs = QgsProject.instance().crs() diff --git a/python/plugins/processing/gui/wrappers.py b/python/plugins/processing/gui/wrappers.py index c4fb995f0386..4aa7a98f8e15 100755 --- a/python/plugins/processing/gui/wrappers.py +++ b/python/plugins/processing/gui/wrappers.py @@ -395,7 +395,9 @@ class ExtentWidgetWrapper(WidgetWrapper): def createWidget(self): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): - return ExtentSelectionPanel(self.dialog, self.parameterDefinition()) + widget = ExtentSelectionPanel(self.dialog, self.parameterDefinition()) + widget.hasChanged.connect(lambda: self.widgetValueHasChanged.emit(self)) + return widget else: widget = QComboBox() widget.setEditable(True) diff --git a/python/plugins/processing/tests/GdalAlgorithmsTest.py b/python/plugins/processing/tests/GdalAlgorithmsTest.py index a24b5f64568c..32d02f22d34d 100644 --- a/python/plugins/processing/tests/GdalAlgorithmsTest.py +++ b/python/plugins/processing/tests/GdalAlgorithmsTest.py @@ -59,6 +59,11 @@ from processing.algs.gdal.warp import warp from processing.algs.gdal.fillnodata import fillnodata from processing.algs.gdal.rearrange_bands import rearrange_bands +from processing.algs.gdal.gdaladdo import gdaladdo +from processing.algs.gdal.sieve import sieve +from processing.algs.gdal.gdal2xyz import gdal2xyz +from processing.algs.gdal.polygonize import polygonize + from processing.tools.system import isWindows from qgis.core import (QgsProcessingContext, @@ -2369,6 +2374,230 @@ def testPointsAlongLines(self): '-dialect sqlite -sql "SELECT ST_Line_Interpolate_Point(geometry, 0.2) AS geometry,* FROM \'polys2\'" ' + '-f "ESRI Shapefile"']) + def testGdalAddo(self): + context = QgsProcessingContext() + feedback = QgsProcessingFeedback() + source = os.path.join(testDataPath, 'dem.tif') + + with tempfile.TemporaryDirectory() as outdir: + alg = gdaladdo() + alg.initAlgorithm() + + # defaults + self.assertEqual( + alg.getConsoleCommands({'INPUT': source, + 'LEVELS': '2 4 8 16', + 'CLEAN': False, + 'RESAMPLING': 0, + 'FORMAT': 0}, context, feedback), + ['gdaladdo', + source + ' ' + '-r nearest 2 4 8 16']) + + # with "clean" option + self.assertEqual( + alg.getConsoleCommands({'INPUT': source, + 'LEVELS': '2 4 8 16', + 'CLEAN': True, + 'RESAMPLING': 0, + 'FORMAT': 0}, context, feedback), + ['gdaladdo', + source + ' ' + '-r nearest -clean 2 4 8 16']) + + # ovr format + self.assertEqual( + alg.getConsoleCommands({'INPUT': source, + 'LEVELS': '2 4 8 16', + 'CLEAN': False, + 'RESAMPLING': 0, + 'FORMAT': 1}, context, feedback), + ['gdaladdo', + source + ' ' + '-r nearest -ro 2 4 8 16']) + + # Erdas format + self.assertEqual( + alg.getConsoleCommands({'INPUT': source, + 'LEVELS': '2 4 8 16', + 'CLEAN': False, + 'RESAMPLING': 0, + 'FORMAT': 2}, context, feedback), + ['gdaladdo', + source + ' ' + '-r nearest --config USE_RRD YES 2 4 8 16']) + + # custom resampling method format + self.assertEqual( + alg.getConsoleCommands({'INPUT': source, + 'LEVELS': '2 4 8 16', + 'CLEAN': False, + 'RESAMPLING': 4, + 'FORMAT': 0}, context, feedback), + ['gdaladdo', + source + ' ' + '-r cubicspline 2 4 8 16']) + + # more levels + self.assertEqual( + alg.getConsoleCommands({'INPUT': source, + 'LEVELS': '2 4 8 16 32 64', + 'CLEAN': False, + 'RESAMPLING': 0, + 'FORMAT': 0}, context, feedback), + ['gdaladdo', + source + ' ' + '-r nearest 2 4 8 16 32 64']) + + # without advanced params + self.assertEqual( + alg.getConsoleCommands({'INPUT': source, + 'LEVELS': '2 4 8 16', + 'CLEAN': False}, context, feedback), + ['gdaladdo', + source + ' ' + '-r nearest 2 4 8 16']) + + def testSieve(self): + context = QgsProcessingContext() + feedback = QgsProcessingFeedback() + source = os.path.join(testDataPath, 'dem.tif') + mask = os.path.join(testDataPath, 'raster.tif') + + with tempfile.TemporaryDirectory() as outdir: + outsource = outdir + '/check.tif' + alg = sieve() + alg.initAlgorithm() + + # defaults + self.assertEqual( + alg.getConsoleCommands({'INPUT': source, + 'THRESHOLD': 10, + 'EIGHT_CONNECTEDNESS': False, + 'NO_MASK': False, + 'MASK_LAYER': None, + 'OUTPUT': outsource}, context, feedback), + ['gdal_sieve.py', + '-st 10 -4 -of GTiff ' + + source + ' ' + + outsource]) + + # Eight connectedness and custom threshold + self.assertEqual( + alg.getConsoleCommands({'INPUT': source, + 'THRESHOLD': 16, + 'EIGHT_CONNECTEDNESS': True, + 'NO_MASK': False, + 'MASK_LAYER': None, + 'OUTPUT': outsource}, context, feedback), + ['gdal_sieve.py', + '-st 16 -8 -of GTiff ' + + source + ' ' + + outsource]) + + # without default mask layer + self.assertEqual( + alg.getConsoleCommands({'INPUT': source, + 'THRESHOLD': 10, + 'EIGHT_CONNECTEDNESS': False, + 'NO_MASK': True, + 'MASK_LAYER': None, + 'OUTPUT': outsource}, context, feedback), + ['gdal_sieve.py', + '-st 10 -4 -nomask -of GTiff ' + + source + ' ' + + outsource]) + + # defaults with external validity mask + self.assertEqual( + alg.getConsoleCommands({'INPUT': source, + 'THRESHOLD': 10, + 'EIGHT_CONNECTEDNESS': False, + 'NO_MASK': False, + 'MASK_LAYER': mask, + 'OUTPUT': outsource}, context, feedback), + ['gdal_sieve.py', + '-st 10 -4 -mask ' + + mask + + ' -of GTiff ' + + source + ' ' + + outsource]) + + def testGdal2Xyz(self): + context = QgsProcessingContext() + feedback = QgsProcessingFeedback() + source = os.path.join(testDataPath, 'dem.tif') + + with tempfile.TemporaryDirectory() as outdir: + outsource = outdir + '/check.csv' + alg = gdal2xyz() + alg.initAlgorithm() + + # defaults + self.assertEqual( + alg.getConsoleCommands({'INPUT': source, + 'BAND': 1, + 'CSV': False, + 'OUTPUT': outsource}, context, feedback), + ['gdal2xyz.py', + '-band 1 ' + + source + ' ' + + outsource]) + + # csv output + self.assertEqual( + alg.getConsoleCommands({'INPUT': source, + 'BAND': 1, + 'CSV': True, + 'OUTPUT': outsource}, context, feedback), + ['gdal2xyz.py', + '-band 1 -csv ' + + source + ' ' + + outsource]) + + def testGdalPolygonize(self): + context = QgsProcessingContext() + feedback = QgsProcessingFeedback() + source = os.path.join(testDataPath, 'dem.tif') + + with tempfile.TemporaryDirectory() as outdir: + outsource = outdir + '/check.shp' + alg = polygonize() + alg.initAlgorithm() + + # defaults + self.assertEqual( + alg.getConsoleCommands({'INPUT': source, + 'BAND': 1, + 'FIELD': 'DN', + 'EIGHT_CONNECTEDNESS': False, + 'OUTPUT': outsource}, context, feedback), + ['gdal_polygonize.py', + source + ' ' + + outsource + ' ' + + '-b 1 -f "ESRI Shapefile" DN' + ]) + + # 8 connectedness + self.assertEqual( + alg.getConsoleCommands({'INPUT': source, + 'BAND': 1, + 'FIELD': 'DN', + 'EIGHT_CONNECTEDNESS': True, + 'OUTPUT': outsource}, context, feedback), + ['gdal_polygonize.py', + source + ' ' + + outsource + ' ' + + '-8 -b 1 -f "ESRI Shapefile" DN' + ]) + + # custom output format + outsource = outdir + '/check.gpkg' + self.assertEqual( + alg.getConsoleCommands({'INPUT': source, + 'BAND': 1, + 'FIELD': 'DN', + 'EIGHT_CONNECTEDNESS': False, + 'OUTPUT': outsource}, context, feedback), + ['gdal_polygonize.py', + source + ' ' + + outsource + ' ' + + '-b 1 -f "GPKG" DN' + ]) + class TestGdalOgrToPostGis(unittest.TestCase): diff --git a/python/plugins/processing/tests/testdata/custom/dem_zones.tif b/python/plugins/processing/tests/testdata/custom/dem_zones.tif new file mode 100644 index 000000000000..e01a19767358 Binary files /dev/null and b/python/plugins/processing/tests/testdata/custom/dem_zones.tif differ diff --git a/python/plugins/processing/tests/testdata/custom/dem_zones_crs.tif b/python/plugins/processing/tests/testdata/custom/dem_zones_crs.tif new file mode 100644 index 000000000000..d5458727c9ad Binary files /dev/null and b/python/plugins/processing/tests/testdata/custom/dem_zones_crs.tif differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_all.dbf b/python/plugins/processing/tests/testdata/expected/extract_m_all.dbf new file mode 100644 index 000000000000..6ae7255219d7 Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_m_all.dbf differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_all.prj b/python/plugins/processing/tests/testdata/expected/extract_m_all.prj new file mode 100644 index 000000000000..a30c00a55de1 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_m_all.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_all.qpj b/python/plugins/processing/tests/testdata/expected/extract_m_all.qpj new file mode 100644 index 000000000000..5fbc831e7433 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_m_all.qpj @@ -0,0 +1 @@ +GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]] diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_all.shp b/python/plugins/processing/tests/testdata/expected/extract_m_all.shp new file mode 100644 index 000000000000..6b4658679625 Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_m_all.shp differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_all.shx b/python/plugins/processing/tests/testdata/expected/extract_m_all.shx new file mode 100644 index 000000000000..243332237346 Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_m_all.shx differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_first.dbf b/python/plugins/processing/tests/testdata/expected/extract_m_first.dbf new file mode 100644 index 000000000000..dc462e670093 Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_m_first.dbf differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_first.prj b/python/plugins/processing/tests/testdata/expected/extract_m_first.prj new file mode 100644 index 000000000000..a30c00a55de1 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_m_first.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_first.qpj b/python/plugins/processing/tests/testdata/expected/extract_m_first.qpj new file mode 100644 index 000000000000..5fbc831e7433 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_m_first.qpj @@ -0,0 +1 @@ +GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]] diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_first.shp b/python/plugins/processing/tests/testdata/expected/extract_m_first.shp new file mode 100644 index 000000000000..6b4658679625 Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_m_first.shp differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_first.shx b/python/plugins/processing/tests/testdata/expected/extract_m_first.shx new file mode 100644 index 000000000000..243332237346 Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_m_first.shx differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_none.dbf b/python/plugins/processing/tests/testdata/expected/extract_m_none.dbf new file mode 100644 index 000000000000..79e30bb549d7 Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_m_none.dbf differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_none.prj b/python/plugins/processing/tests/testdata/expected/extract_m_none.prj new file mode 100644 index 000000000000..a30c00a55de1 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_m_none.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_none.qpj b/python/plugins/processing/tests/testdata/expected/extract_m_none.qpj new file mode 100644 index 000000000000..5fbc831e7433 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_m_none.qpj @@ -0,0 +1 @@ +GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]] diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_none.shp b/python/plugins/processing/tests/testdata/expected/extract_m_none.shp new file mode 100644 index 000000000000..a0a4405be68d Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_m_none.shp differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_none.shx b/python/plugins/processing/tests/testdata/expected/extract_m_none.shx new file mode 100644 index 000000000000..7699aa20d55f Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_m_none.shx differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_points.dbf b/python/plugins/processing/tests/testdata/expected/extract_m_points.dbf new file mode 100644 index 000000000000..e01840c427e0 Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_m_points.dbf differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_points.prj b/python/plugins/processing/tests/testdata/expected/extract_m_points.prj new file mode 100644 index 000000000000..a30c00a55de1 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_m_points.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_points.qpj b/python/plugins/processing/tests/testdata/expected/extract_m_points.qpj new file mode 100644 index 000000000000..5fbc831e7433 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_m_points.qpj @@ -0,0 +1 @@ +GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]] diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_points.shp b/python/plugins/processing/tests/testdata/expected/extract_m_points.shp new file mode 100644 index 000000000000..0231b3a5a81b Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_m_points.shp differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_m_points.shx b/python/plugins/processing/tests/testdata/expected/extract_m_points.shx new file mode 100644 index 000000000000..c219ef372eee Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_m_points.shx differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_all.dbf b/python/plugins/processing/tests/testdata/expected/extract_z_all.dbf new file mode 100644 index 000000000000..e6eeb46d1760 Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_z_all.dbf differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_all.prj b/python/plugins/processing/tests/testdata/expected/extract_z_all.prj new file mode 100644 index 000000000000..a30c00a55de1 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_z_all.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_all.qpj b/python/plugins/processing/tests/testdata/expected/extract_z_all.qpj new file mode 100644 index 000000000000..5fbc831e7433 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_z_all.qpj @@ -0,0 +1 @@ +GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]] diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_all.shp b/python/plugins/processing/tests/testdata/expected/extract_z_all.shp new file mode 100644 index 000000000000..c6769961e9ec Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_z_all.shp differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_all.shx b/python/plugins/processing/tests/testdata/expected/extract_z_all.shx new file mode 100644 index 000000000000..2b1a727a6636 Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_z_all.shx differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_first.dbf b/python/plugins/processing/tests/testdata/expected/extract_z_first.dbf new file mode 100644 index 000000000000..94ded4ae66f6 Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_z_first.dbf differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_first.prj b/python/plugins/processing/tests/testdata/expected/extract_z_first.prj new file mode 100644 index 000000000000..a30c00a55de1 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_z_first.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_first.qpj b/python/plugins/processing/tests/testdata/expected/extract_z_first.qpj new file mode 100644 index 000000000000..5fbc831e7433 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_z_first.qpj @@ -0,0 +1 @@ +GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]] diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_first.shp b/python/plugins/processing/tests/testdata/expected/extract_z_first.shp new file mode 100644 index 000000000000..c6769961e9ec Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_z_first.shp differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_first.shx b/python/plugins/processing/tests/testdata/expected/extract_z_first.shx new file mode 100644 index 000000000000..2b1a727a6636 Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_z_first.shx differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_none.dbf b/python/plugins/processing/tests/testdata/expected/extract_z_none.dbf new file mode 100644 index 000000000000..448bb844ea67 Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_z_none.dbf differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_none.prj b/python/plugins/processing/tests/testdata/expected/extract_z_none.prj new file mode 100644 index 000000000000..a30c00a55de1 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_z_none.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_none.qpj b/python/plugins/processing/tests/testdata/expected/extract_z_none.qpj new file mode 100644 index 000000000000..5fbc831e7433 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_z_none.qpj @@ -0,0 +1 @@ +GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]] diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_none.shp b/python/plugins/processing/tests/testdata/expected/extract_z_none.shp new file mode 100644 index 000000000000..27dd420e40cd Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_z_none.shp differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_none.shx b/python/plugins/processing/tests/testdata/expected/extract_z_none.shx new file mode 100644 index 000000000000..a0a0c32ec7bd Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_z_none.shx differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_points.dbf b/python/plugins/processing/tests/testdata/expected/extract_z_points.dbf new file mode 100644 index 000000000000..7a23aaadb7fd Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_z_points.dbf differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_points.prj b/python/plugins/processing/tests/testdata/expected/extract_z_points.prj new file mode 100644 index 000000000000..a30c00a55de1 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_z_points.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_points.qpj b/python/plugins/processing/tests/testdata/expected/extract_z_points.qpj new file mode 100644 index 000000000000..5fbc831e7433 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/extract_z_points.qpj @@ -0,0 +1 @@ +GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4326"]] diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_points.shp b/python/plugins/processing/tests/testdata/expected/extract_z_points.shp new file mode 100644 index 000000000000..0231b3a5a81b Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_z_points.shp differ diff --git a/python/plugins/processing/tests/testdata/expected/extract_z_points.shx b/python/plugins/processing/tests/testdata/expected/extract_z_points.shx new file mode 100644 index 000000000000..c219ef372eee Binary files /dev/null and b/python/plugins/processing/tests/testdata/expected/extract_z_points.shx differ diff --git a/python/plugins/processing/tests/testdata/expected/raster_zonal_stats.csv b/python/plugins/processing/tests/testdata/expected/raster_zonal_stats.csv new file mode 100644 index 000000000000..59567196e92c --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/raster_zonal_stats.csv @@ -0,0 +1,5 @@ +zone,deg2,sum,count,min,max,mean +6.00000000,0.00034562,5371074.34954071,"34562",85.50000000,243.00000000,155.40403766 +4.00000000,0.00013810,1263010.46405792,"13810",86.30000305,93.90000153,91.45622477 +3.00000000,0.00026125,4375224.02449036,"26125",111.69999695,232.43850708,167.47268993 +1.00000000,0.00037237,4668982.96012115,"37237",85.00000000,219.19999695,125.38558316 diff --git a/python/plugins/processing/tests/testdata/expected/raster_zonal_stats_reproj.csv b/python/plugins/processing/tests/testdata/expected/raster_zonal_stats_reproj.csv new file mode 100644 index 000000000000..71f3dd8cf1d3 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/raster_zonal_stats_reproj.csv @@ -0,0 +1,5 @@ +zone,deg2,sum,count,min,max,mean +6.00000000,0.00034585,5378373.70841217,"34585",85.50000000,243.00000000,155.51174522 +4.00000000,0.00013848,1266379.78169250,"13848",86.30000305,93.90000153,91.44856887 +3.00000000,0.00026132,4379265.06565094,"26132",111.69999695,232.43850708,167.58246845 +1.00000000,0.00037290,4677866.67560577,"37290",85.00000000,219.19999695,125.44560675 diff --git a/python/plugins/processing/tests/testdata/expected/raster_zonal_stats_zone_crs_ref.csv b/python/plugins/processing/tests/testdata/expected/raster_zonal_stats_zone_crs_ref.csv new file mode 100644 index 000000000000..20aecb7e1341 --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/raster_zonal_stats_zone_crs_ref.csv @@ -0,0 +1,5 @@ +zone,m2,sum,count,min,max,mean +6.00000000,3055987.38515208,6069863.29187775,"39025",85.50000000,243.00000000,155.53781658 +4.00000000,1226310.37671958,1432050.30903625,"15660",86.30000305,93.90000153,91.44637989 +3.00000000,2310490.91092664,4944274.10529327,"29505",111.69999695,232.43850708,167.57410965 +1.00000000,3297568.96319678,5283155.23725128,"42110",85.00000000,219.19999695,125.46082254 diff --git a/python/plugins/processing/tests/testdata/expected/raster_zonal_stats_zone_ref.csv b/python/plugins/processing/tests/testdata/expected/raster_zonal_stats_zone_ref.csv new file mode 100644 index 000000000000..59567196e92c --- /dev/null +++ b/python/plugins/processing/tests/testdata/expected/raster_zonal_stats_zone_ref.csv @@ -0,0 +1,5 @@ +zone,deg2,sum,count,min,max,mean +6.00000000,0.00034562,5371074.34954071,"34562",85.50000000,243.00000000,155.40403766 +4.00000000,0.00013810,1263010.46405792,"13810",86.30000305,93.90000153,91.45622477 +3.00000000,0.00026125,4375224.02449036,"26125",111.69999695,232.43850708,167.47268993 +1.00000000,0.00037237,4668982.96012115,"37237",85.00000000,219.19999695,125.38558316 diff --git a/python/plugins/processing/tests/testdata/gdal_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/gdal_algorithm_tests.yaml index 82277a7414cb..d36ddc568043 100644 --- a/python/plugins/processing/tests/testdata/gdal_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/gdal_algorithm_tests.yaml @@ -168,20 +168,6 @@ tests: - 'Band 1 Block=373x5 Type=Float32, ColorInterp=Gray' - ' NoData Value=-99999' -# Disabled as gdal2xyz.py is not available on Travis -# - algorithm: gdal:gdal2xyz -# name: gdal2xyz -# params: -# BAND: 1 -# CSV: false -# INPUT: -# name: dem.tif -# type: raster -# results: -# OUTPUT: -# name: expected/gdal/xyz.csv -# type: file - - algorithm: gdal:tileindex name: Tile index (gdaltindex) params: @@ -349,41 +335,6 @@ tests: hash: fff4a08498e93494f3f2cf1a9074451e6fd68341849aedc9e2c45e6a type: rasterhash -# Disabled as gdal_poligonize.py is not available on Travis -# - algorithm: gdal:polygonize -# name: Polygonize -# params: -# BAND: 1 -# EIGHT_CONNECTEDNESS: false -# FIELD: DN -# INPUT: -# name: dem.tif -# type: raster -# results: -# OUTPUT: -# name: expected/gdal/polygonize.gml -# type: vector - -# Disabled as gdal_proximity.py is not available on Travis -# - algorithm: gdal:proximity -# name: Proximity -# params: -# BAND: 1 -# DATA_TYPE: 5 -# INPUT: -# name: dem.tif -# type: raster -# MAX_DISTANCE: 0.0 -# NODATA: 0.0 -# OPTIONS: '' -# REPLACE: 0.0 -# UNITS: 1 -# VALUES: '90' -# results: -# OUTPUT: -# hash: 32802271d1ce083ca14078bfefaef6300ae8809af11f6a4270583d0c -# type: rasterhash - - algorithm: gdal:rasterize name: Test (gdal:rasterize) params: @@ -420,24 +371,6 @@ tests: hash: ee2b317e022da1001378fac60c9b613a74d3566b9870f9d121e6e322 type: rasterhash -# Disabled as gdal_sieve is not available on Travis -# - algorithm: gdal:sieve -# name: Sieve -# params: -# EIGHT_CONNECTEDNESS: false -# INPUT: -# name: dem.tif -# type: raster -# MASK_LAYER: -# name: dem.tif -# type: raster -# NO_MASK: false -# THRESHOLD: 10 -# results: -# OUTPUT: -# hash: 1ea6a8c838add299dc3f6f9f529eb5945664f68bae97be9ca80b1754 -# type: rasterhash - - algorithm: gdal:slope name: Slope params: diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index 73ad4010cc96..9ebcab0182ee 100755 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -1777,6 +1777,76 @@ tests: - algorithm: qgis:idwinterpolation name: IDW interpolation using attribute + params: + DISTANCE_COEFFICIENT: 2.0 + EXTENT: 0.0,8.0,-5.0,3.0 [EPSG:4326] + INTERPOLATION_DATA: + name: pointsz.gml::~::0::~::1::~::0 + type: interpolation + PIXEL_SIZE: 0.026667 + results: + OUTPUT: + hash: + - 76d59e1a9905e97fde7da598022d50ec7de15994e61a59e2b6dc952f + - 6d2da87e58dfe8fdfb3a1b66543bc68870f7e3292a9ca2674ea3a523 + type: rasterhash + + - algorithm: qgis:idwinterpolation + name: IDW interpolation using Z value + params: + DISTANCE_COEFFICIENT: 2.0 + EXTENT: 0.0,8.0,-5.0,3.0 [EPSG:4326] + INTERPOLATION_DATA: + name: pointsz.gml::~::1::~::1::~::0 + type: interpolation + PIXEL_SIZE: 0.026667 + results: + OUTPUT: + hash: + - 76d59e1a9905e97fde7da598022d50ec7de15994e61a59e2b6dc952f + - 6d2da87e58dfe8fdfb3a1b66543bc68870f7e3292a9ca2674ea3a523 + type: rasterhash + + - algorithm: qgis:tininterpolation + name: TIN interpolation using attribute + params: + EXTENT: 0.0,8.0,-5.0,3.0 [EPSG:4326] + INTERPOLATION_DATA: + name: pointsz.gml::~::0::~::1::~::0 + type: interpolation + METHOD: 0 + PIXEL_SIZE: 0.026667 + results: + OUTPUT: + hash: + - 1e07ea5c90a16f87df812042d3e89dd5b1216defa7714921f306de94 + - 6e533a4c2c2e8ef5ca62814a7ad6dd29cb0f0f6eea85baf2a2802870 + type: rasterhash + #TRIANGULATION_FILE: + # name: expected/triangulation.gml + # type: vector + + - algorithm: qgis:tininterpolation + name: TIN interpolation using Z value + params: + EXTENT: 0.0,8.0,-5.0,3.0 [EPSG:4326] + INTERPOLATION_DATA: + name: pointsz.gml::~::1::~::-1::~::0 + type: interpolation + METHOD: 0 + PIXEL_SIZE: 0.026667 + results: + OUTPUT: + hash: + - 1e07ea5c90a16f87df812042d3e89dd5b1216defa7714921f306de94 + - 6d2da87e58dfe8fdfb3a1b66543bc68870f7e3292a9ca2674ea3a523 + type: rasterhash + #TRIANULATION_FILE: + # name: expected/triangulation.gml + # type: vector + + - algorithm: qgis:idwinterpolation + name: IDW interpolation using attribute (old parameters) params: COLUMNS: 300 DISTANCE_COEFFICIENT: 2.0 @@ -1793,7 +1863,7 @@ tests: type: rasterhash - algorithm: qgis:idwinterpolation - name: IDW interpolation using Z value + name: IDW interpolation using Z value (old parameters) params: COLUMNS: 300 DISTANCE_COEFFICIENT: 2.0 @@ -1810,7 +1880,7 @@ tests: type: rasterhash - algorithm: qgis:tininterpolation - name: TIN interpolation using attribute + name: TIN interpolation using attribute (old parameters) params: COLUMNS: 300 EXTENT: 0, 8, -5, 3 @@ -1830,7 +1900,7 @@ tests: # type: vector - algorithm: qgis:tininterpolation - name: TIN interpolation using Z value + name: TIN interpolation using Z value (old parameters) params: COLUMNS: 300 EXTENT: 0, 8, -5, 3 @@ -6360,6 +6430,178 @@ tests: name: expected/force_rhr_multipolys.gml type: vector + - algorithm: native:extractzvalues + name: Extract z, first value only + params: + COLUMN_PREFIX: z_ + INPUT: + name: lines_z.shp + type: vector + SUMMARIES: + - 0 + results: + OUTPUT: + name: expected/extract_z_first.shp + type: vector + + - algorithm: native:extractzvalues + name: Extract z, all stats + params: + COLUMN_PREFIX: zs_ + INPUT: + name: lines_z.shp + type: vector + SUMMARIES: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - 12 + - 13 + - 14 + - 15 + results: + OUTPUT: + name: expected/extract_z_all.shp + type: vector + + - algorithm: native:extractzvalues + name: Extract z, points + params: + COLUMN_PREFIX: z_ + INPUT: + name: custom/pointszm.shp + type: vector + SUMMARIES: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - 12 + - 13 + - 14 + - 15 + results: + OUTPUT: + name: expected/extract_z_points.shp + type: vector + + - algorithm: native:extractzvalues + name: Extract z, no z values + params: + COLUMN_PREFIX: z_ + INPUT: + name: lines.gml|layername=lines + type: vector + SUMMARIES: + - 0 + results: + OUTPUT: + name: expected/extract_z_none.shp + type: vector + + - algorithm: native:extractmvalues + name: Extract m first only + params: + COLUMN_PREFIX: m_ + INPUT: + name: lines_m.shp + type: vector + SUMMARIES: + - 0 + results: + OUTPUT: + name: expected/extract_m_first.shp + type: vector + + - algorithm: native:extractmvalues + name: Extract m, all stats + params: + COLUMN_PREFIX: ms_ + INPUT: + name: lines_m.shp + type: vector + SUMMARIES: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - 12 + - 13 + - 14 + - 15 + results: + OUTPUT: + name: expected/extract_m_all.shp + type: vector + + - algorithm: native:extractmvalues + name: Extract m, points + params: + COLUMN_PREFIX: mp_ + INPUT: + name: custom/pointszm.shp + type: vector + SUMMARIES: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - 12 + - 13 + - 14 + - 15 + results: + OUTPUT: + name: expected/extract_m_points.shp + type: vector + + - algorithm: native:extractmvalues + name: Extract m, no m values present + params: + COLUMN_PREFIX: m_ + INPUT: + name: points.gml|layername=points + type: vector + SUMMARIES: + - 0 + results: + OUTPUT: + name: expected/extract_m_none.shp + type: vector + - algorithm: native:splitlinesbylength name: Split multilines by length params: @@ -6407,5 +6649,70 @@ tests: name: expected/delete_duplicates_with_nulls.gml type: vector + - algorithm: native:rasterlayerzonalstats + name: Raster layer zonal stats, same CRS + params: + BAND: 1 + INPUT: + name: dem.tif + type: raster + ZONES: + name: custom/dem_zones.tif + type: raster + ZONES_BAND: 1 + results: + OUTPUT_TABLE: + name: expected/raster_zonal_stats.csv + type: vector + + - algorithm: native:rasterlayerzonalstats + name: Raster layer zonal stats, reprojected + params: + BAND: 1 + INPUT: + name: dem.tif + type: raster + ZONES: + name: custom/dem_zones_crs.tif + type: raster + ZONES_BAND: 1 + results: + OUTPUT_TABLE: + name: expected/raster_zonal_stats_reproj.csv + type: vector + + - algorithm: native:rasterlayerzonalstats + name: Raster layer zonal stats, zones ref + params: + BAND: 1 + INPUT: + name: dem.tif + type: raster + REF_LAYER: 1 + ZONES: + name: custom/dem_zones.tif + type: raster + ZONES_BAND: 1 + results: + OUTPUT_TABLE: + name: expected/raster_zonal_stats_zone_ref.csv + type: vector + + - algorithm: native:rasterlayerzonalstats + name: Raster layer zonal stats reprojected, zones ref + params: + BAND: 1 + INPUT: + name: dem.tif + type: raster + REF_LAYER: 1 + ZONES: + name: custom/dem_zones_crs.tif + type: raster + ZONES_BAND: 1 + results: + OUTPUT_TABLE: + name: expected/raster_zonal_stats_zone_crs_ref.csv + type: vector # See ../README.md for a description of the file format diff --git a/python/plugins/processing/tests/testdata/saga_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/saga_algorithm_tests.yaml index 128be85f2c64..3efde82d979f 100644 --- a/python/plugins/processing/tests/testdata/saga_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/saga_algorithm_tests.yaml @@ -564,3 +564,107 @@ tests: name: expected/saga/zonal_grid_stats.dbf type: vector + - algorithm: saga:resampling + name: Resample down + params: + INPUT: + name: dem.tif + type: raster + KEEP_TYPE: true + SCALE_DOWN: 3 + SCALE_UP: 5 + TARGET_USER_FITS: 0 + TARGET_USER_SIZE: 0.0005 + results: + OUTPUT: + hash: 1b3a17a724c489ea2eea60b39bc1b9c79778832bd41e982a4e9fb09f + type: rasterhash + + - algorithm: saga:resampling + name: Resample up + params: + INPUT: + name: dem.tif + type: raster + KEEP_TYPE: true + SCALE_DOWN: 3 + SCALE_UP: 5 + TARGET_USER_FITS: 0 + TARGET_USER_SIZE: 5.0e-05 + results: + OUTPUT: + hash: 32008adf7520fee2848d689fcb53d0f8ac862bcd162575b03fefc7f1 + type: rasterhash + + - algorithm: saga:resamplingfilter + name: Resampling filter + params: + GRID: + name: dem.tif + type: raster + SCALE: 10.0 + results: + HIPASS: + hash: cf992e69d506924bb59a5fbabd86617e263cb951a61c1b3c0b10012b + type: rasterhash + LOPASS: + hash: f36069a8ceb7ffe839a34f66b82eebacb80143e9df4ed54485bf8293 + type: rasterhash + + - algorithm: saga:fillsinks + name: Fill sinks (Planchon/Darboux) + params: + DEM: + name: dem.tif + type: raster + MINSLOPE: 0.01 + results: + RESULT: + hash: aa197cdf1b0bac1595bd337bab08ae3cddb82ddb4d06c17b528a1d43 + type: rasterhash + + - algorithm: saga:fillsinksqmofesp + name: Fill sinks (QM of ESP) + params: + DEM: + name: dem.tif + type: raster + DZFILL: 0.01 + results: + FILLED: + hash: ebd24bb7d011f0c4d87d1e4748e89d90d5a78cfa820070ece87df4d0 + type: rasterhash + SINKS: + hash: 83d1936c0421783372573c9dcd04e6415e9db911c5fa254e74c929b0 + type: rasterhash + + - algorithm: saga:fillsinkswangliu + name: Fill sinks Wang & Liu + params: + ELEV: + name: dem.tif + type: raster + MINSLOPE: 0.01 + results: + FDIR: + hash: 24367e9ca83c757628f329029d82341fe9111138e019138c949c34c0 + type: rasterhash + FILLED: + hash: aa197cdf1b0bac1595bd337bab08ae3cddb82ddb4d06c17b528a1d43 + type: rasterhash + WSHED: + hash: d891f15de1c742986647d619788c7b16a21909bf9c06c70579a4797c + type: rasterhash + + - algorithm: saga:fillsinksxxlwangliu + name: Fill Sinks Wang & Lui XXL + params: + ELEV: + name: dem.tif + type: raster + MINSLOPE: 0.1 + results: + FILLED: + hash: aa197cdf1b0bac1595bd337bab08ae3cddb82ddb4d06c17b528a1d43 + type: rasterhash + diff --git a/python/plugins/processing/tools/postgis.py b/python/plugins/processing/tools/postgis.py index e2c2c1bf3de8..c197fbfb6c12 100644 --- a/python/plugins/processing/tools/postgis.py +++ b/python/plugins/processing/tools/postgis.py @@ -112,7 +112,7 @@ class TableConstraint(object): match_types = {'u': 'UNSPECIFIED', 'f': 'FULL', 'p': 'PARTIAL'} def __init__(self, row): - (self.name, con_type, self.is_defferable, self.is_deffered, keys) = row[:5] + (self.name, con_type, self.is_deferable, self.is_deferred, keys) = row[:5] self.keys = list(map(int, keys.split(' '))) self.con_type = TableConstraint.types[con_type] # Convert to enum if self.con_type == TableConstraint.TypeCheck: diff --git a/python/server/auto_generated/qgsaccesscontrol.sip.in b/python/server/auto_generated/qgsaccesscontrol.sip.in index 8b6f8eaadb90..575018181485 100644 --- a/python/server/auto_generated/qgsaccesscontrol.sip.in +++ b/python/server/auto_generated/qgsaccesscontrol.sip.in @@ -12,7 +12,6 @@ - class QgsAccessControl : QgsFeatureFilterProvider { %Docstring diff --git a/python/server/auto_generated/qgsbufferserverrequest.sip.in b/python/server/auto_generated/qgsbufferserverrequest.sip.in index 6cc8a48ee984..a76a3c4e0681 100644 --- a/python/server/auto_generated/qgsbufferserverrequest.sip.in +++ b/python/server/auto_generated/qgsbufferserverrequest.sip.in @@ -8,7 +8,6 @@ - class QgsBufferServerRequest : QgsServerRequest { %Docstring diff --git a/python/server/auto_generated/qgsserverprojectutils.sip.in b/python/server/auto_generated/qgsserverprojectutils.sip.in index 4a811f2bdf94..799dcf721ce1 100644 --- a/python/server/auto_generated/qgsserverprojectutils.sip.in +++ b/python/server/auto_generated/qgsserverprojectutils.sip.in @@ -152,6 +152,15 @@ Returns the quality for WMS images defined in a QGIS project. :param project: the QGIS project :return: quality if defined in project, -1 otherwise. +%End + + int wmsMaxAtlasFeatures( const QgsProject &project ); +%Docstring +Returns the maximum number of atlas features which can be printed in a request + +:param project: the QGIS project + +:return: the number of atlas features %End bool wmsUseLayerIds( const QgsProject &project ); diff --git a/resources/function_help/json/nullif b/resources/function_help/json/nullif new file mode 100644 index 000000000000..a557755fe1ce --- /dev/null +++ b/resources/function_help/json/nullif @@ -0,0 +1,11 @@ +{ + "name": "nullif", + "type": "function", + "description": "Returns a null value if value1 equals value2; otherwise it returns value1. This can be used to conditionally substitute values with NULL.", + "arguments": [ {"arg":"value1", "description": "The value that should either be used or substituted with NULL."}, + {"arg":"value2", "description": "The control value that will trigger the NULL substitution."}], + "examples": [ { "expression":"nullif('(none)', '(none)')", "returns":"NULL"}, + { "expression":"nullif('text', '(none)')", "returns":"'text'"}, + { "expression":"nullif(\"name\", '')", "returns":"NULL, if name is an empty string (or already NULL), the name in any other case."} ] +} + diff --git a/resources/function_help/json/sqlite_fetch_and_increment b/resources/function_help/json/sqlite_fetch_and_increment new file mode 100644 index 000000000000..1ded3beaa895 --- /dev/null +++ b/resources/function_help/json/sqlite_fetch_and_increment @@ -0,0 +1,16 @@ +{ + "name": "sqlite_fetch_and_increment", + "type": "function", + "description": "Manage autoincrementing values in sqlite databases.

SQlite default values can only be applied on insert and not prefetched.

This makes it impossible to acquire an incremented primary key via AUTO_INCREMENT before creating the row in the database. Sidenote: with postgres, this works via the option evaluate default values.

When adding new features with relations, it is really nice to be able to already add children for a parent, while the parents form is still open and hence the parent feature uncommitted.

To get around this limitation, this function can be used to manage sequence values in a separate table on sqlite based formats like gpkg.

The sequence table will be filtered for a sequence id (filter_attribute and filter_value) and the current value of the id_field will be incremented by 1 ond the incremented value returned.

If additional columns require values to be specified, the default_value map can be used for this purpose.

Note
This function modifies the target sqlite table. It is intended for usage with default value configurations for attributes.

", + "arguments": [ + {"arg":"database", "description":"Path to the sqlite file"}, + {"arg":"table", "description":"Name of the table that manages the sequences"}, + {"arg":"id_field", "description":"Name of the field that contains the current value"}, + {"arg":"filter_attribute", "description":"Name the field that contains a unique identifier for this sequence. Must have a UNIQUE index."}, + {"arg":"filter_value", "description":"Name of the sequence to use."}, + {"arg":"default_values", "description":"Map with default values for additional columns on the table. The values need to be fully quoted. Functions are allowed.", "optional": true} + ], + "examples": [ + { "expression":"sqlite_fetch_and_increment(layer_property(@layer, 'path'), 'sequence_table', 'last_unique_id', 'sequence_id', 'global', map('last_change','date(''now'')','user','''' || @user_account_name || ''''))", "returns":"0"} + ] +} diff --git a/resources/themes/Blend of Gray/icons/arrow-down.svg b/resources/themes/Blend of Gray/icons/arrow-down.svg new file mode 100644 index 000000000000..c66a52859e7f --- /dev/null +++ b/resources/themes/Blend of Gray/icons/arrow-down.svg @@ -0,0 +1 @@ + diff --git a/resources/themes/Blend of Gray/icons/arrow-up.svg b/resources/themes/Blend of Gray/icons/arrow-up.svg new file mode 100644 index 000000000000..3c991d18fd59 --- /dev/null +++ b/resources/themes/Blend of Gray/icons/arrow-up.svg @@ -0,0 +1 @@ + diff --git a/resources/themes/Blend of Gray/icons/caret-down.svg b/resources/themes/Blend of Gray/icons/caret-down.svg new file mode 100644 index 000000000000..159b85175e6a --- /dev/null +++ b/resources/themes/Blend of Gray/icons/caret-down.svg @@ -0,0 +1 @@ + diff --git a/resources/themes/Blend of Gray/icons/caret-right.svg b/resources/themes/Blend of Gray/icons/caret-right.svg new file mode 100644 index 000000000000..dcaec276801a --- /dev/null +++ b/resources/themes/Blend of Gray/icons/caret-right.svg @@ -0,0 +1 @@ + diff --git a/resources/themes/Blend of Gray/icons/close.svg b/resources/themes/Blend of Gray/icons/close.svg new file mode 100644 index 000000000000..7acc0a7804c6 --- /dev/null +++ b/resources/themes/Blend of Gray/icons/close.svg @@ -0,0 +1 @@ +image/svg+xml