[needs-docs][processing] Port SAGA raster surface volume to native QGIS alg

The SAGA version of this algorithm is of limited use in QGIS, because the
volume calculated is embedded only in the SAGA terminal output. This prevents
it being saved to a file, or reused within a model as an input to a later
model step.

It's also very user-unfriendly, because users must know to manually scan
the algorithm log to find the SAGA output.

Given that the maths here is trivial, this commit ports the algorithm across
to be a native QGIS c++ algorithm. The algorithm duplicates the SAGA alg
1:1, but outputs the volume (and area) to either a HTML report, or a vector
table. Additionally, the outputs are exported as numeric outputs from the
algorithm, allowing them to be re-used within models.

(It's also considerably faster, because it avoids the forced conversion
to SAGA raster format)

Fixes #8607 (properly, even though that report is closed)
This commit is contained in:
Nyall Dawson 2019-01-20 07:54:43 +10:00
parent e3fda18aca
commit 195d98f43a
32 changed files with 1114 additions and 0 deletions

Binary file not shown.

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_above.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
<gml:featureMember>
<ogr:surface_vol_above fid="surface_vol_above.0">
<ogr:volume>0.06480527</ogr:volume>
<ogr:deg2>0.00095901</ogr:deg2>
<ogr:pixel_count>95901</ogr:pixel_count>
</ogr:surface_vol_above>
</gml:featureMember>
</ogr:FeatureCollection>

View File

@ -0,0 +1,6 @@
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/></head><body>
<p>Analyzed file: /home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/dem.tif (band 1)</p>
<p>Volume: 0.0648052733680633</p>
<p>Pixel count: 95901</p>
<p>Area: 0.0009590099999998638 deg2</p>
</body></html>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureCollectionType">
<xs:attribute name="lockId" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="surface_vol_above" type="ogr:surface_vol_above_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="surface_vol_above_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="deg2" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:long">
<xs:totalDigits value="20"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_above_crs.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
<gml:featureMember>
<ogr:surface_vol_above_crs fid="surface_vol_above_crs.0">
<ogr:volume>413784918.00990051</ogr:volume>
<ogr:m2>11497732.58555590</ogr:m2>
<ogr:pixel_count>64692</ogr:pixel_count>
</ogr:surface_vol_above_crs>
</gml:featureMember>
</ogr:FeatureCollection>

View File

@ -0,0 +1,6 @@
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/></head><body>
<p>Analyzed file: /home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/custom/dem_crs.tif (band 1)</p>
<p>Volume: 413784918.0099005</p>
<p>Pixel count: 64692</p>
<p>Area: 11497732.5855559 m2</p>
</body></html>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureCollectionType">
<xs:attribute name="lockId" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="surface_vol_above_crs" type="ogr:surface_vol_above_crs_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="surface_vol_above_crs_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="m2" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:long">
<xs:totalDigits value="20"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_add.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
<gml:featureMember>
<ogr:surface_vol_add fid="surface_vol_add.0">
<ogr:volume>0.06802753</ogr:volume>
<ogr:deg2>0.00130550</ogr:deg2>
<ogr:pixel_count>130550</ogr:pixel_count>
</ogr:surface_vol_add>
</gml:featureMember>
</ogr:FeatureCollection>

View File

@ -0,0 +1,6 @@
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/></head><body>
<p>Analyzed file: /home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/dem.tif (band 1)</p>
<p>Volume: 0.06802752691184032</p>
<p>Pixel count: 130550</p>
<p>Area: 0.001305499999999814 deg2</p>
</body></html>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureCollectionType">
<xs:attribute name="lockId" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="surface_vol_add" type="ogr:surface_vol_add_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="surface_vol_add_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="deg2" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:long">
<xs:totalDigits value="20"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_add_crs.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
<gml:featureMember>
<ogr:surface_vol_add_crs fid="surface_vol_add_crs.0">
<ogr:volume>893436802.88886535</ogr:volume>
<ogr:m2>23202698.77333090</ogr:m2>
<ogr:pixel_count>130550</ogr:pixel_count>
</ogr:surface_vol_add_crs>
</gml:featureMember>
</ogr:FeatureCollection>

View File

@ -0,0 +1,6 @@
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/></head><body>
<p>Analyzed file: /home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/custom/dem_crs.tif (band 1)</p>
<p>Volume: 893436802.8888654</p>
<p>Pixel count: 130550</p>
<p>Area: 23202698.7733309 m2</p>
</body></html>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureCollectionType">
<xs:attribute name="lockId" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="surface_vol_add_crs" type="ogr:surface_vol_add_crs_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="surface_vol_add_crs_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="m2" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:long">
<xs:totalDigits value="20"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_below.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
<gml:featureMember>
<ogr:surface_vol_below fid="surface_vol_below.0">
<ogr:volume>-0.00322225</ogr:volume>
<ogr:deg2>0.00034600</ogr:deg2>
<ogr:pixel_count>34600</ogr:pixel_count>
</ogr:surface_vol_below>
</gml:featureMember>
</ogr:FeatureCollection>

View File

@ -0,0 +1,6 @@
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/></head><body>
<p>Analyzed file: /home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/dem.tif (band 1)</p>
<p>Volume: -0.003222253543777008</p>
<p>Pixel count: 34600</p>
<p>Area: 0.0003459999999999508 deg2</p>
</body></html>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureCollectionType">
<xs:attribute name="lockId" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="surface_vol_below" type="ogr:surface_vol_below_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="surface_vol_below_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="deg2" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:long">
<xs:totalDigits value="20"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_below_crs.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
<gml:featureMember>
<ogr:surface_vol_below_crs fid="surface_vol_below_crs.0">
<ogr:volume>-479651884.87896484</ogr:volume>
<ogr:m2>11669775.57607742</ogr:m2>
<ogr:pixel_count>65660</ogr:pixel_count>
</ogr:surface_vol_below_crs>
</gml:featureMember>
</ogr:FeatureCollection>

View File

@ -0,0 +1,6 @@
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/></head><body>
<p>Analyzed file: /home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/custom/dem_crs.tif (band 1)</p>
<p>Volume: -479651884.8789648</p>
<p>Pixel count: 65660</p>
<p>Area: 11669775.57607742 m2</p>
</body></html>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureCollectionType">
<xs:attribute name="lockId" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="surface_vol_below_crs" type="ogr:surface_vol_below_crs_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="surface_vol_below_crs_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="m2" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:long">
<xs:totalDigits value="20"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_gaps.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
<gml:featureMember>
<ogr:surface_vol_gaps fid="surface_vol_gaps.0">
<ogr:volume>0.06382041</ogr:volume>
<ogr:deg2>0.00094259</ogr:deg2>
<ogr:pixel_count>94259</ogr:pixel_count>
</ogr:surface_vol_gaps>
</gml:featureMember>
</ogr:FeatureCollection>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureCollectionType">
<xs:attribute name="lockId" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="surface_vol_gaps" type="ogr:surface_vol_gaps_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="surface_vol_gaps_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="deg2" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:long">
<xs:totalDigits value="20"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_subtract.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
<gml:featureMember>
<ogr:surface_vol_subtract fid="surface_vol_subtract.0">
<ogr:volume>0.06158302</ogr:volume>
<ogr:deg2>0.00130550</ogr:deg2>
<ogr:pixel_count>130550</ogr:pixel_count>
</ogr:surface_vol_subtract>
</gml:featureMember>
</ogr:FeatureCollection>

View File

@ -0,0 +1,6 @@
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/></head><body>
<p>Analyzed file: /home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/dem.tif (band 1)</p>
<p>Volume: 0.06158301982428629</p>
<p>Pixel count: 130550</p>
<p>Area: 0.001305499999999814 deg2</p>
</body></html>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureCollectionType">
<xs:attribute name="lockId" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="surface_vol_subtract" type="ogr:surface_vol_subtract_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="surface_vol_subtract_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="deg2" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:long">
<xs:totalDigits value="20"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_subtract_crs.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
<gml:featureMember>
<ogr:surface_vol_subtract_crs fid="surface_vol_subtract_crs.0">
<ogr:volume>-65866966.86906432</ogr:volume>
<ogr:m2>23202698.77333090</ogr:m2>
<ogr:pixel_count>130550</ogr:pixel_count>
</ogr:surface_vol_subtract_crs>
</gml:featureMember>
</ogr:FeatureCollection>

View File

@ -0,0 +1,6 @@
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/></head><body>
<p>Analyzed file: /home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/custom/dem_crs.tif (band 1)</p>
<p>Volume: -65866966.86906432</p>
<p>Pixel count: 130550</p>
<p>Area: 23202698.7733309 m2</p>
</body></html>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureCollectionType">
<xs:attribute name="lockId" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="surface_vol_subtract_crs" type="ogr:surface_vol_subtract_crs_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="surface_vol_subtract_crs_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="m2" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:decimal">
<xs:totalDigits value="21"/>
<xs:fractionDigits value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:long">
<xs:totalDigits value="20"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>

View File

@ -6793,4 +6793,186 @@ tests:
pk:
- pk
- algorithm: native:rastersurfacevolume
name: Surface volume above
params:
BAND: 1
INPUT:
name: dem.tif
type: raster
LEVEL: 100.0
METHOD: 0
results:
OUTPUT_HTML_FILE:
name: expected/surface_vol_above.html
type: regex
rules:
- 'Volume: 0.064805'
- 'Pixel count: 95901'
- 'Area: 0.000959'
OUTPUT_TABLE:
name: expected/surface_vol_above.gml
type: vector
- algorithm: native:rastersurfacevolume
name: Surface volume Below
params:
BAND: 1
INPUT:
name: dem.tif
type: raster
LEVEL: 100.0
METHOD: 1
results:
OUTPUT_HTML_FILE:
name: expected/surface_vol_below.html
type: regex
rules:
- 'Volume: -0.00322225'
- 'Pixel count: 34600'
- 'Area: 0.00034599'
OUTPUT_TABLE:
name: expected/surface_vol_below.gml
type: vector
- algorithm: native:rastersurfacevolume
name: Surface volume subtract below
params:
BAND: 1
INPUT:
name: dem.tif
type: raster
LEVEL: 100.0
METHOD: 2
results:
OUTPUT_HTML_FILE:
name: expected/surface_vol_subtract.html
type: regex
rules:
- 'Volume: 0.0615830'
- 'Pixel count: 130550'
- 'Area: 0.00130549'
OUTPUT_TABLE:
name: expected/surface_vol_subtract.gml
type: vector
- algorithm: native:rastersurfacevolume
name: Surface volume add below
params:
BAND: 1
INPUT:
name: dem.tif
type: raster
LEVEL: 100.0
METHOD: 3
results:
OUTPUT_HTML_FILE:
name: expected/surface_vol_add.html
type: regex
rules:
- 'Volume: 0.06802752'
- 'Pixel count: 130550'
- 'Area: 0.00130549'
OUTPUT_TABLE:
name: expected/surface_vol_add.gml
type: vector
- algorithm: native:rastersurfacevolume
name: Surface volume above (meters)
params:
BAND: 1
INPUT:
name: custom/dem_crs.tif
type: raster
LEVEL: 150.0
METHOD: 0
results:
OUTPUT_HTML_FILE:
name: expected/surface_vol_above_crs.html
type: regex
rules:
- 'Volume: 413784918\.'
- 'Pixel count: 64692'
- 'Area: 11497732\.'
OUTPUT_TABLE:
name: expected/surface_vol_above_crs.gml
type: vector
- algorithm: native:rastersurfacevolume
name: Surface volume below (meters)
params:
BAND: 1
INPUT:
name: custom/dem_crs.tif
type: raster
LEVEL: 150.0
METHOD: 1
results:
OUTPUT_HTML_FILE:
name: expected/surface_vol_below_crs.html
type: regex
rules:
- 'Volume: -479651884\.'
- 'Pixel count: 65660'
- 'Area: 11669775\.'
OUTPUT_TABLE:
name: expected/surface_vol_below_crs.gml
type: vector
- algorithm: native:rastersurfacevolume
name: Surface volume subtract below (meters)
params:
BAND: 1
INPUT:
name: custom/dem_crs.tif
type: raster
LEVEL: 150.0
METHOD: 2
results:
OUTPUT_HTML_FILE:
name: expected/surface_vol_subtract_crs.html
type: regex
rules:
- 'Volume: -65866966\.'
- 'Pixel count: 130550'
- 'Area: 23202698\.'
OUTPUT_TABLE:
name: expected/surface_vol_subtract_crs.gml
type: vector
- algorithm: native:rastersurfacevolume
name: Surface volume add below (meters)
params:
BAND: 1
INPUT:
name: custom/dem_crs.tif
type: raster
LEVEL: 150.0
METHOD: 3
results:
OUTPUT_HTML_FILE:
name: expected/surface_vol_add_crs.html
type: regex
rules:
- 'Volume: 893436802\.'
- 'Pixel count: 130550'
- 'Area: 23202698\.'
OUTPUT_TABLE:
name: expected/surface_vol_add_crs.gml
type: vector
- algorithm: native:rastersurfacevolume
name: Surface volume with gaps
params:
BAND: 1
INPUT:
name: custom/dem_gaps.tif
type: raster
LEVEL: 101.0
METHOD: 0
results:
OUTPUT_TABLE:
name: expected/surface_vol_gaps.gml
type: vector
# See ../README.md for a description of the file format

View File

@ -77,6 +77,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmprojectpointcartesian.cpp
processing/qgsalgorithmpromotetomultipart.cpp
processing/qgsalgorithmrasterlayeruniquevalues.cpp
processing/qgsalgorithmrastersurfacevolume.cpp
processing/qgsalgorithmrasterzonalstats.cpp
processing/qgsalgorithmreclassifybylayer.cpp
processing/qgsalgorithmremoveduplicatesbyattribute.cpp

View File

@ -0,0 +1,250 @@
/***************************************************************************
qgsalgorithmrasterlayeruniquevalues.cpp
---------------------
begin : January 2019
copyright : (C) 2019 by Nyall Dawson
email : nyall dot dawson 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. *
* *
***************************************************************************/
#include "qgsalgorithmrastersurfacevolume.h"
#include "qgsstringutils.h"
///@cond PRIVATE
QString QgsRasterSurfaceVolumeAlgorithm::name() const
{
return QStringLiteral( "rastersurfacevolume" );
}
QString QgsRasterSurfaceVolumeAlgorithm::displayName() const
{
return QObject::tr( "Raster surface volume" );
}
QStringList QgsRasterSurfaceVolumeAlgorithm::tags() const
{
return QObject::tr( "sum,volume,area,height,terrain,dem,elevation" ).split( ',' );
}
QString QgsRasterSurfaceVolumeAlgorithm::group() const
{
return QObject::tr( "Raster analysis" );
}
QString QgsRasterSurfaceVolumeAlgorithm::groupId() const
{
return QStringLiteral( "rasteranalysis" );
}
void QgsRasterSurfaceVolumeAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "INPUT" ),
QObject::tr( "Input layer" ) ) );
addParameter( new QgsProcessingParameterBand( QStringLiteral( "BAND" ),
QObject::tr( "Band number" ), 1, QStringLiteral( "INPUT" ) ) );
addParameter( new QgsProcessingParameterNumber( QStringLiteral( "LEVEL" ),
QObject::tr( "Base level" ), QgsProcessingParameterNumber::Double, 0 ) );
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "METHOD" ),
QObject::tr( "Method" ), QStringList()
<< QObject::tr( "Count Only Above Base Level" )
<< QObject::tr( "Count Only Below Base Level" )
<< QObject::tr( "Subtract Volumes Below Base Level" )
<< QObject::tr( "Add Volumes Below Base Level" ) ) );
addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT_HTML_FILE" ),
QObject::tr( "Surface volume report" ), QObject::tr( "HTML files (*.html)" ), QVariant(), true ) );
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT_TABLE" ),
QObject::tr( "Surface volume table" ), QgsProcessing::TypeVector, QVariant(), true, false ) );
addOutput( new QgsProcessingOutputNumber( QStringLiteral( "VOLUME" ), QObject::tr( "Volume" ) ) );
addOutput( new QgsProcessingOutputNumber( QStringLiteral( "PIXEL_COUNT" ), QObject::tr( "Pixel count" ) ) );
addOutput( new QgsProcessingOutputNumber( QStringLiteral( "AREA" ), QObject::tr( "Area" ) ) );
}
QString QgsRasterSurfaceVolumeAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm calculates the volume under a raster grid's surface.\n\n"
"Several methods of volume calculation are available, which control whether "
"only values above or below the specified base level are considered, or "
"whether volumes below the base level should be added or subtracted from the total volume.\n\n"
"The algorithm outputs the calculated volume, the total area, and the total number of pixels analysed. "
"If the 'Count Only Above Base Level' or 'Count Only Below Base Level' methods are used, "
"then the calculated area and pixel count only includes pixels which are above or below the "
"specified base level respectively.\n\n"
"Units of the calculated volume are dependent on the coordinate reference system of "
"the input raster file. For a CRS in meters, with a DEM height in meters, the calculated "
"value will be in meters³. If instead the input raster is in a geographic coordinate system "
"(e.g. latitude/longitude values), then the result will be in degrees² × meters, and an "
"appropriate scaling factor will need to be applied in order to convert to meters³." );
}
QString QgsRasterSurfaceVolumeAlgorithm::shortDescription() const
{
return QObject::tr( "Calculates the volume under a raster grid's surface." );
}
QgsRasterSurfaceVolumeAlgorithm *QgsRasterSurfaceVolumeAlgorithm::createInstance() const
{
return new QgsRasterSurfaceVolumeAlgorithm();
}
bool QgsRasterSurfaceVolumeAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
{
QgsRasterLayer *layer = parameterAsRasterLayer( parameters, QStringLiteral( "INPUT" ), context );
int band = parameterAsInt( parameters, QStringLiteral( "BAND" ), context );
if ( !layer )
throw QgsProcessingException( invalidRasterError( parameters, QStringLiteral( "INPUT" ) ) );
mBand = parameterAsInt( parameters, QStringLiteral( "BAND" ), context );
if ( mBand < 1 || mBand > layer->bandCount() )
throw QgsProcessingException( QObject::tr( "Invalid band number for BAND (%1): Valid values for input raster are 1 to %2" ).arg( mBand )
.arg( layer->bandCount() ) );
mInterface.reset( layer->dataProvider()->clone() );
mHasNoDataValue = layer->dataProvider()->sourceHasNoDataValue( band );
mLayerWidth = layer->width();
mLayerHeight = layer->height();
mExtent = layer->extent();
mCrs = layer->crs();
mRasterUnitsPerPixelX = layer->rasterUnitsPerPixelX();
mRasterUnitsPerPixelY = layer->rasterUnitsPerPixelY();
mSource = layer->source();
mLevel = parameterAsDouble( parameters, QStringLiteral( "LEVEL" ), context );
mMethod = static_cast< Method >( parameterAsEnum( parameters, QStringLiteral( "METHOD" ), context ) );
return true;
}
QVariantMap QgsRasterSurfaceVolumeAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
QString outputFile = parameterAsFileOutput( parameters, QStringLiteral( "OUTPUT_HTML_FILE" ), context );
QString areaUnit = QgsUnitTypes::toAbbreviatedString( QgsUnitTypes::distanceToAreaUnit( mCrs.mapUnits() ) );
QString tableDest;
std::unique_ptr< QgsFeatureSink > sink;
if ( parameters.contains( QStringLiteral( "OUTPUT_TABLE" ) ) && parameters.value( QStringLiteral( "OUTPUT_TABLE" ) ).isValid() )
{
QgsFields outFields;
outFields.append( QgsField( QStringLiteral( "volume" ), QVariant::Double, QString(), 20, 8 ) );
outFields.append( QgsField( areaUnit.replace( QStringLiteral( "²" ), QStringLiteral( "2" ) ), QVariant::Double, QString(), 20, 8 ) );
outFields.append( QgsField( QStringLiteral( "pixel_count" ), QVariant::LongLong ) );
sink.reset( parameterAsSink( parameters, QStringLiteral( "OUTPUT_TABLE" ), context, tableDest, outFields, QgsWkbTypes::NoGeometry, QgsCoordinateReferenceSystem() ) );
if ( !sink )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT_TABLE" ) ) );
}
double volume = 0;
long long count = 0;
int maxWidth = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_WIDTH;
int maxHeight = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_HEIGHT;
int nbBlocksWidth = static_cast< int >( std::ceil( 1.0 * mLayerWidth / maxWidth ) );
int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * mLayerHeight / maxHeight ) );
int nbBlocks = nbBlocksWidth * nbBlocksHeight;
QgsRasterIterator iter( mInterface.get() );
iter.startRasterRead( mBand, mLayerWidth, mLayerHeight, mExtent );
int iterLeft = 0;
int iterTop = 0;
int iterCols = 0;
int iterRows = 0;
std::unique_ptr< QgsRasterBlock > rasterBlock;
while ( iter.readNextRasterPart( mBand, iterCols, iterRows, rasterBlock, iterLeft, iterTop ) )
{
feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
for ( int row = 0; row < iterRows; row++ )
{
if ( feedback->isCanceled() )
break;
for ( int column = 0; column < iterCols; column++ )
{
if ( mHasNoDataValue && rasterBlock->isNoData( row, column ) )
{
continue;
}
const double z = rasterBlock->value( row, column ) - mLevel;
switch ( mMethod )
{
case CountOnlyAboveBaseLevel:
if ( z > 0.0 )
{
volume += z;
count++;
}
continue;
case CountOnlyBelowBaseLevel:
if ( z < 0.0 )
{
volume += z;
count++;
}
continue;
case SubtractVolumesBelowBaseLevel:
volume += z;
count++;
continue;
case AddVolumesBelowBaseLevel:
volume += std::fabs( z );
count++;
continue;
}
}
}
}
QVariantMap outputs;
double pixelArea = mRasterUnitsPerPixelX * mRasterUnitsPerPixelY;
double area = count * pixelArea;
volume *= pixelArea;
if ( !outputFile.isEmpty() )
{
QFile file( outputFile );
if ( file.open( QIODevice::WriteOnly | QIODevice::Text ) )
{
const QString encodedAreaUnit = QgsStringUtils::ampersandEncode( areaUnit );
QTextStream out( &file );
out << QStringLiteral( "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"/></head><body>\n" );
out << QStringLiteral( "<p>%1: %2 (%3 %4)</p>\n" ).arg( QObject::tr( "Analyzed file" ), mSource, QObject::tr( "band" ) ).arg( mBand );
out << QObject::tr( "<p>%1: %2</p>\n" ).arg( QObject::tr( "Volume" ), QString::number( volume, 'g', 16 ) );
out << QObject::tr( "<p>%1: %2</p>\n" ).arg( QObject::tr( "Pixel count" ) ).arg( count );
out << QObject::tr( "<p>%1: %2 %3</p>\n" ).arg( QObject::tr( "Area" ), QString::number( area, 'g', 16 ), encodedAreaUnit );
out << QStringLiteral( "</body></html>" );
outputs.insert( QStringLiteral( "OUTPUT_HTML_FILE" ), outputFile );
}
}
if ( sink )
{
QgsFeature f;
f.setAttributes( QgsAttributes() << volume << area << count );
sink->addFeature( f, QgsFeatureSink::FastInsert );
outputs.insert( QStringLiteral( "OUTPUT_TABLE" ), tableDest );
}
outputs.insert( QStringLiteral( "VOLUME" ), volume );
outputs.insert( QStringLiteral( "AREA" ), area );
outputs.insert( QStringLiteral( "PIXEL_COUNT" ), count );
return outputs;
}
///@endcond

View File

@ -0,0 +1,82 @@
/***************************************************************************
qgsalgorithmrasterlayeruniquevalues.cpp
---------------------
begin : January 2019
copyright : (C) 2019 by Nyall Dawson
email : nyall dot dawson 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. *
* *
***************************************************************************/
#ifndef QGSALGORITHMRASTERSURFACEVOLUME_H
#define QGSALGORITHMRASTERSURFACEVOLUME_H
#define SIP_NO_FILE
#include "qgis.h"
#include "qgsprocessingalgorithm.h"
///@cond PRIVATE
/**
* Native raster layer unique values report algorithm.
*/
class QgsRasterSurfaceVolumeAlgorithm : public QgsProcessingAlgorithm
{
public:
QgsRasterSurfaceVolumeAlgorithm() = default;
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
QString name() const override;
QString displayName() const override;
QStringList tags() const override;
QString group() const override;
QString groupId() const override;
QString shortHelpString() const override;
QString shortDescription() const override;
QgsRasterSurfaceVolumeAlgorithm *createInstance() const override SIP_FACTORY;
protected:
bool prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
private:
enum Method
{
CountOnlyAboveBaseLevel = 0,
CountOnlyBelowBaseLevel,
SubtractVolumesBelowBaseLevel,
AddVolumesBelowBaseLevel
};
std::unique_ptr< QgsRasterInterface > mInterface;
bool mHasNoDataValue = false;
int mBand = 1;
int mLayerWidth;
int mLayerHeight;
QgsRectangle mExtent;
QgsCoordinateReferenceSystem mCrs;
double mRasterUnitsPerPixelX;
double mRasterUnitsPerPixelY;
double mLevel = 0;
QString mSource;
Method mMethod;
};
///@endcond PRIVATE
#endif // QGSALGORITHMRASTERSURFACEVOLUME_H

View File

@ -72,6 +72,7 @@
#include "qgsalgorithmprojectpointcartesian.h"
#include "qgsalgorithmpromotetomultipart.h"
#include "qgsalgorithmrasterlayeruniquevalues.h"
#include "qgsalgorithmrastersurfacevolume.h"
#include "qgsalgorithmrasterzonalstats.h"
#include "qgsalgorithmreclassifybylayer.h"
#include "qgsalgorithmremoveduplicatesbyattribute.h"
@ -209,6 +210,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsRasterLayerZonalStatsAlgorithm() );
addAlgorithm( new QgsRasterPixelsToPointsAlgorithm() );
addAlgorithm( new QgsRasterPixelsToPolygonsAlgorithm() );
addAlgorithm( new QgsRasterSurfaceVolumeAlgorithm() );
addAlgorithm( new QgsAlgorithmRemoveDuplicateVertices() );
addAlgorithm( new QgsReclassifyByLayerAlgorithm() );
addAlgorithm( new QgsReclassifyByTableAlgorithm() );