Skip to content

Commit 195d98f

Browse files
committedJan 21, 2019
[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)
1 parent e3fda18 commit 195d98f

32 files changed

+1114
-0
lines changed
 
Binary file not shown.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ogr:FeatureCollection
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_above.xsd"
5+
xmlns:ogr="http://ogr.maptools.org/"
6+
xmlns:gml="http://www.opengis.net/gml">
7+
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
8+
9+
<gml:featureMember>
10+
<ogr:surface_vol_above fid="surface_vol_above.0">
11+
<ogr:volume>0.06480527</ogr:volume>
12+
<ogr:deg2>0.00095901</ogr:deg2>
13+
<ogr:pixel_count>95901</ogr:pixel_count>
14+
</ogr:surface_vol_above>
15+
</gml:featureMember>
16+
</ogr:FeatureCollection>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/></head><body>
2+
<p>Analyzed file: /home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/dem.tif (band 1)</p>
3+
<p>Volume: 0.0648052733680633</p>
4+
<p>Pixel count: 95901</p>
5+
<p>Area: 0.0009590099999998638 deg2</p>
6+
</body></html>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
4+
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
5+
<xs:complexType name="FeatureCollectionType">
6+
<xs:complexContent>
7+
<xs:extension base="gml:AbstractFeatureCollectionType">
8+
<xs:attribute name="lockId" type="xs:string" use="optional"/>
9+
<xs:attribute name="scope" type="xs:string" use="optional"/>
10+
</xs:extension>
11+
</xs:complexContent>
12+
</xs:complexType>
13+
<xs:element name="surface_vol_above" type="ogr:surface_vol_above_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="surface_vol_above_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
19+
<xs:simpleType>
20+
<xs:restriction base="xs:decimal">
21+
<xs:totalDigits value="21"/>
22+
<xs:fractionDigits value="8"/>
23+
</xs:restriction>
24+
</xs:simpleType>
25+
</xs:element>
26+
<xs:element name="deg2" nillable="true" minOccurs="0" maxOccurs="1">
27+
<xs:simpleType>
28+
<xs:restriction base="xs:decimal">
29+
<xs:totalDigits value="21"/>
30+
<xs:fractionDigits value="8"/>
31+
</xs:restriction>
32+
</xs:simpleType>
33+
</xs:element>
34+
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
35+
<xs:simpleType>
36+
<xs:restriction base="xs:long">
37+
<xs:totalDigits value="20"/>
38+
</xs:restriction>
39+
</xs:simpleType>
40+
</xs:element>
41+
</xs:sequence>
42+
</xs:extension>
43+
</xs:complexContent>
44+
</xs:complexType>
45+
</xs:schema>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ogr:FeatureCollection
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_above_crs.xsd"
5+
xmlns:ogr="http://ogr.maptools.org/"
6+
xmlns:gml="http://www.opengis.net/gml">
7+
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
8+
9+
<gml:featureMember>
10+
<ogr:surface_vol_above_crs fid="surface_vol_above_crs.0">
11+
<ogr:volume>413784918.00990051</ogr:volume>
12+
<ogr:m2>11497732.58555590</ogr:m2>
13+
<ogr:pixel_count>64692</ogr:pixel_count>
14+
</ogr:surface_vol_above_crs>
15+
</gml:featureMember>
16+
</ogr:FeatureCollection>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/></head><body>
2+
<p>Analyzed file: /home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/custom/dem_crs.tif (band 1)</p>
3+
<p>Volume: 413784918.0099005</p>
4+
<p>Pixel count: 64692</p>
5+
<p>Area: 11497732.5855559 m2</p>
6+
</body></html>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
4+
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
5+
<xs:complexType name="FeatureCollectionType">
6+
<xs:complexContent>
7+
<xs:extension base="gml:AbstractFeatureCollectionType">
8+
<xs:attribute name="lockId" type="xs:string" use="optional"/>
9+
<xs:attribute name="scope" type="xs:string" use="optional"/>
10+
</xs:extension>
11+
</xs:complexContent>
12+
</xs:complexType>
13+
<xs:element name="surface_vol_above_crs" type="ogr:surface_vol_above_crs_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="surface_vol_above_crs_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
19+
<xs:simpleType>
20+
<xs:restriction base="xs:decimal">
21+
<xs:totalDigits value="21"/>
22+
<xs:fractionDigits value="8"/>
23+
</xs:restriction>
24+
</xs:simpleType>
25+
</xs:element>
26+
<xs:element name="m2" nillable="true" minOccurs="0" maxOccurs="1">
27+
<xs:simpleType>
28+
<xs:restriction base="xs:decimal">
29+
<xs:totalDigits value="21"/>
30+
<xs:fractionDigits value="8"/>
31+
</xs:restriction>
32+
</xs:simpleType>
33+
</xs:element>
34+
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
35+
<xs:simpleType>
36+
<xs:restriction base="xs:long">
37+
<xs:totalDigits value="20"/>
38+
</xs:restriction>
39+
</xs:simpleType>
40+
</xs:element>
41+
</xs:sequence>
42+
</xs:extension>
43+
</xs:complexContent>
44+
</xs:complexType>
45+
</xs:schema>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ogr:FeatureCollection
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_add.xsd"
5+
xmlns:ogr="http://ogr.maptools.org/"
6+
xmlns:gml="http://www.opengis.net/gml">
7+
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
8+
9+
<gml:featureMember>
10+
<ogr:surface_vol_add fid="surface_vol_add.0">
11+
<ogr:volume>0.06802753</ogr:volume>
12+
<ogr:deg2>0.00130550</ogr:deg2>
13+
<ogr:pixel_count>130550</ogr:pixel_count>
14+
</ogr:surface_vol_add>
15+
</gml:featureMember>
16+
</ogr:FeatureCollection>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/></head><body>
2+
<p>Analyzed file: /home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/dem.tif (band 1)</p>
3+
<p>Volume: 0.06802752691184032</p>
4+
<p>Pixel count: 130550</p>
5+
<p>Area: 0.001305499999999814 deg2</p>
6+
</body></html>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
4+
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
5+
<xs:complexType name="FeatureCollectionType">
6+
<xs:complexContent>
7+
<xs:extension base="gml:AbstractFeatureCollectionType">
8+
<xs:attribute name="lockId" type="xs:string" use="optional"/>
9+
<xs:attribute name="scope" type="xs:string" use="optional"/>
10+
</xs:extension>
11+
</xs:complexContent>
12+
</xs:complexType>
13+
<xs:element name="surface_vol_add" type="ogr:surface_vol_add_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="surface_vol_add_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
19+
<xs:simpleType>
20+
<xs:restriction base="xs:decimal">
21+
<xs:totalDigits value="21"/>
22+
<xs:fractionDigits value="8"/>
23+
</xs:restriction>
24+
</xs:simpleType>
25+
</xs:element>
26+
<xs:element name="deg2" nillable="true" minOccurs="0" maxOccurs="1">
27+
<xs:simpleType>
28+
<xs:restriction base="xs:decimal">
29+
<xs:totalDigits value="21"/>
30+
<xs:fractionDigits value="8"/>
31+
</xs:restriction>
32+
</xs:simpleType>
33+
</xs:element>
34+
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
35+
<xs:simpleType>
36+
<xs:restriction base="xs:long">
37+
<xs:totalDigits value="20"/>
38+
</xs:restriction>
39+
</xs:simpleType>
40+
</xs:element>
41+
</xs:sequence>
42+
</xs:extension>
43+
</xs:complexContent>
44+
</xs:complexType>
45+
</xs:schema>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ogr:FeatureCollection
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_add_crs.xsd"
5+
xmlns:ogr="http://ogr.maptools.org/"
6+
xmlns:gml="http://www.opengis.net/gml">
7+
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
8+
9+
<gml:featureMember>
10+
<ogr:surface_vol_add_crs fid="surface_vol_add_crs.0">
11+
<ogr:volume>893436802.88886535</ogr:volume>
12+
<ogr:m2>23202698.77333090</ogr:m2>
13+
<ogr:pixel_count>130550</ogr:pixel_count>
14+
</ogr:surface_vol_add_crs>
15+
</gml:featureMember>
16+
</ogr:FeatureCollection>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/></head><body>
2+
<p>Analyzed file: /home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/custom/dem_crs.tif (band 1)</p>
3+
<p>Volume: 893436802.8888654</p>
4+
<p>Pixel count: 130550</p>
5+
<p>Area: 23202698.7733309 m2</p>
6+
</body></html>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
4+
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
5+
<xs:complexType name="FeatureCollectionType">
6+
<xs:complexContent>
7+
<xs:extension base="gml:AbstractFeatureCollectionType">
8+
<xs:attribute name="lockId" type="xs:string" use="optional"/>
9+
<xs:attribute name="scope" type="xs:string" use="optional"/>
10+
</xs:extension>
11+
</xs:complexContent>
12+
</xs:complexType>
13+
<xs:element name="surface_vol_add_crs" type="ogr:surface_vol_add_crs_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="surface_vol_add_crs_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
19+
<xs:simpleType>
20+
<xs:restriction base="xs:decimal">
21+
<xs:totalDigits value="21"/>
22+
<xs:fractionDigits value="8"/>
23+
</xs:restriction>
24+
</xs:simpleType>
25+
</xs:element>
26+
<xs:element name="m2" nillable="true" minOccurs="0" maxOccurs="1">
27+
<xs:simpleType>
28+
<xs:restriction base="xs:decimal">
29+
<xs:totalDigits value="21"/>
30+
<xs:fractionDigits value="8"/>
31+
</xs:restriction>
32+
</xs:simpleType>
33+
</xs:element>
34+
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
35+
<xs:simpleType>
36+
<xs:restriction base="xs:long">
37+
<xs:totalDigits value="20"/>
38+
</xs:restriction>
39+
</xs:simpleType>
40+
</xs:element>
41+
</xs:sequence>
42+
</xs:extension>
43+
</xs:complexContent>
44+
</xs:complexType>
45+
</xs:schema>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ogr:FeatureCollection
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_below.xsd"
5+
xmlns:ogr="http://ogr.maptools.org/"
6+
xmlns:gml="http://www.opengis.net/gml">
7+
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
8+
9+
<gml:featureMember>
10+
<ogr:surface_vol_below fid="surface_vol_below.0">
11+
<ogr:volume>-0.00322225</ogr:volume>
12+
<ogr:deg2>0.00034600</ogr:deg2>
13+
<ogr:pixel_count>34600</ogr:pixel_count>
14+
</ogr:surface_vol_below>
15+
</gml:featureMember>
16+
</ogr:FeatureCollection>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/></head><body>
2+
<p>Analyzed file: /home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/dem.tif (band 1)</p>
3+
<p>Volume: -0.003222253543777008</p>
4+
<p>Pixel count: 34600</p>
5+
<p>Area: 0.0003459999999999508 deg2</p>
6+
</body></html>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
4+
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
5+
<xs:complexType name="FeatureCollectionType">
6+
<xs:complexContent>
7+
<xs:extension base="gml:AbstractFeatureCollectionType">
8+
<xs:attribute name="lockId" type="xs:string" use="optional"/>
9+
<xs:attribute name="scope" type="xs:string" use="optional"/>
10+
</xs:extension>
11+
</xs:complexContent>
12+
</xs:complexType>
13+
<xs:element name="surface_vol_below" type="ogr:surface_vol_below_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="surface_vol_below_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
19+
<xs:simpleType>
20+
<xs:restriction base="xs:decimal">
21+
<xs:totalDigits value="21"/>
22+
<xs:fractionDigits value="8"/>
23+
</xs:restriction>
24+
</xs:simpleType>
25+
</xs:element>
26+
<xs:element name="deg2" nillable="true" minOccurs="0" maxOccurs="1">
27+
<xs:simpleType>
28+
<xs:restriction base="xs:decimal">
29+
<xs:totalDigits value="21"/>
30+
<xs:fractionDigits value="8"/>
31+
</xs:restriction>
32+
</xs:simpleType>
33+
</xs:element>
34+
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
35+
<xs:simpleType>
36+
<xs:restriction base="xs:long">
37+
<xs:totalDigits value="20"/>
38+
</xs:restriction>
39+
</xs:simpleType>
40+
</xs:element>
41+
</xs:sequence>
42+
</xs:extension>
43+
</xs:complexContent>
44+
</xs:complexType>
45+
</xs:schema>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ogr:FeatureCollection
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_below_crs.xsd"
5+
xmlns:ogr="http://ogr.maptools.org/"
6+
xmlns:gml="http://www.opengis.net/gml">
7+
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
8+
9+
<gml:featureMember>
10+
<ogr:surface_vol_below_crs fid="surface_vol_below_crs.0">
11+
<ogr:volume>-479651884.87896484</ogr:volume>
12+
<ogr:m2>11669775.57607742</ogr:m2>
13+
<ogr:pixel_count>65660</ogr:pixel_count>
14+
</ogr:surface_vol_below_crs>
15+
</gml:featureMember>
16+
</ogr:FeatureCollection>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/></head><body>
2+
<p>Analyzed file: /home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/custom/dem_crs.tif (band 1)</p>
3+
<p>Volume: -479651884.8789648</p>
4+
<p>Pixel count: 65660</p>
5+
<p>Area: 11669775.57607742 m2</p>
6+
</body></html>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
4+
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
5+
<xs:complexType name="FeatureCollectionType">
6+
<xs:complexContent>
7+
<xs:extension base="gml:AbstractFeatureCollectionType">
8+
<xs:attribute name="lockId" type="xs:string" use="optional"/>
9+
<xs:attribute name="scope" type="xs:string" use="optional"/>
10+
</xs:extension>
11+
</xs:complexContent>
12+
</xs:complexType>
13+
<xs:element name="surface_vol_below_crs" type="ogr:surface_vol_below_crs_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="surface_vol_below_crs_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
19+
<xs:simpleType>
20+
<xs:restriction base="xs:decimal">
21+
<xs:totalDigits value="21"/>
22+
<xs:fractionDigits value="8"/>
23+
</xs:restriction>
24+
</xs:simpleType>
25+
</xs:element>
26+
<xs:element name="m2" nillable="true" minOccurs="0" maxOccurs="1">
27+
<xs:simpleType>
28+
<xs:restriction base="xs:decimal">
29+
<xs:totalDigits value="21"/>
30+
<xs:fractionDigits value="8"/>
31+
</xs:restriction>
32+
</xs:simpleType>
33+
</xs:element>
34+
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
35+
<xs:simpleType>
36+
<xs:restriction base="xs:long">
37+
<xs:totalDigits value="20"/>
38+
</xs:restriction>
39+
</xs:simpleType>
40+
</xs:element>
41+
</xs:sequence>
42+
</xs:extension>
43+
</xs:complexContent>
44+
</xs:complexType>
45+
</xs:schema>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ogr:FeatureCollection
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_gaps.xsd"
5+
xmlns:ogr="http://ogr.maptools.org/"
6+
xmlns:gml="http://www.opengis.net/gml">
7+
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
8+
9+
<gml:featureMember>
10+
<ogr:surface_vol_gaps fid="surface_vol_gaps.0">
11+
<ogr:volume>0.06382041</ogr:volume>
12+
<ogr:deg2>0.00094259</ogr:deg2>
13+
<ogr:pixel_count>94259</ogr:pixel_count>
14+
</ogr:surface_vol_gaps>
15+
</gml:featureMember>
16+
</ogr:FeatureCollection>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
4+
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
5+
<xs:complexType name="FeatureCollectionType">
6+
<xs:complexContent>
7+
<xs:extension base="gml:AbstractFeatureCollectionType">
8+
<xs:attribute name="lockId" type="xs:string" use="optional"/>
9+
<xs:attribute name="scope" type="xs:string" use="optional"/>
10+
</xs:extension>
11+
</xs:complexContent>
12+
</xs:complexType>
13+
<xs:element name="surface_vol_gaps" type="ogr:surface_vol_gaps_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="surface_vol_gaps_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
19+
<xs:simpleType>
20+
<xs:restriction base="xs:decimal">
21+
<xs:totalDigits value="21"/>
22+
<xs:fractionDigits value="8"/>
23+
</xs:restriction>
24+
</xs:simpleType>
25+
</xs:element>
26+
<xs:element name="deg2" nillable="true" minOccurs="0" maxOccurs="1">
27+
<xs:simpleType>
28+
<xs:restriction base="xs:decimal">
29+
<xs:totalDigits value="21"/>
30+
<xs:fractionDigits value="8"/>
31+
</xs:restriction>
32+
</xs:simpleType>
33+
</xs:element>
34+
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
35+
<xs:simpleType>
36+
<xs:restriction base="xs:long">
37+
<xs:totalDigits value="20"/>
38+
</xs:restriction>
39+
</xs:simpleType>
40+
</xs:element>
41+
</xs:sequence>
42+
</xs:extension>
43+
</xs:complexContent>
44+
</xs:complexType>
45+
</xs:schema>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ogr:FeatureCollection
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_subtract.xsd"
5+
xmlns:ogr="http://ogr.maptools.org/"
6+
xmlns:gml="http://www.opengis.net/gml">
7+
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
8+
9+
<gml:featureMember>
10+
<ogr:surface_vol_subtract fid="surface_vol_subtract.0">
11+
<ogr:volume>0.06158302</ogr:volume>
12+
<ogr:deg2>0.00130550</ogr:deg2>
13+
<ogr:pixel_count>130550</ogr:pixel_count>
14+
</ogr:surface_vol_subtract>
15+
</gml:featureMember>
16+
</ogr:FeatureCollection>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/></head><body>
2+
<p>Analyzed file: /home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/dem.tif (band 1)</p>
3+
<p>Volume: 0.06158301982428629</p>
4+
<p>Pixel count: 130550</p>
5+
<p>Area: 0.001305499999999814 deg2</p>
6+
</body></html>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
4+
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
5+
<xs:complexType name="FeatureCollectionType">
6+
<xs:complexContent>
7+
<xs:extension base="gml:AbstractFeatureCollectionType">
8+
<xs:attribute name="lockId" type="xs:string" use="optional"/>
9+
<xs:attribute name="scope" type="xs:string" use="optional"/>
10+
</xs:extension>
11+
</xs:complexContent>
12+
</xs:complexType>
13+
<xs:element name="surface_vol_subtract" type="ogr:surface_vol_subtract_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="surface_vol_subtract_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
19+
<xs:simpleType>
20+
<xs:restriction base="xs:decimal">
21+
<xs:totalDigits value="21"/>
22+
<xs:fractionDigits value="8"/>
23+
</xs:restriction>
24+
</xs:simpleType>
25+
</xs:element>
26+
<xs:element name="deg2" nillable="true" minOccurs="0" maxOccurs="1">
27+
<xs:simpleType>
28+
<xs:restriction base="xs:decimal">
29+
<xs:totalDigits value="21"/>
30+
<xs:fractionDigits value="8"/>
31+
</xs:restriction>
32+
</xs:simpleType>
33+
</xs:element>
34+
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
35+
<xs:simpleType>
36+
<xs:restriction base="xs:long">
37+
<xs:totalDigits value="20"/>
38+
</xs:restriction>
39+
</xs:simpleType>
40+
</xs:element>
41+
</xs:sequence>
42+
</xs:extension>
43+
</xs:complexContent>
44+
</xs:complexType>
45+
</xs:schema>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ogr:FeatureCollection
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://ogr.maptools.org/ surface_vol_subtract_crs.xsd"
5+
xmlns:ogr="http://ogr.maptools.org/"
6+
xmlns:gml="http://www.opengis.net/gml">
7+
<gml:boundedBy><gml:null>missing</gml:null></gml:boundedBy>
8+
9+
<gml:featureMember>
10+
<ogr:surface_vol_subtract_crs fid="surface_vol_subtract_crs.0">
11+
<ogr:volume>-65866966.86906432</ogr:volume>
12+
<ogr:m2>23202698.77333090</ogr:m2>
13+
<ogr:pixel_count>130550</ogr:pixel_count>
14+
</ogr:surface_vol_subtract_crs>
15+
</gml:featureMember>
16+
</ogr:FeatureCollection>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8"/></head><body>
2+
<p>Analyzed file: /home/nyall/dev/QGIS/python/plugins/processing/tests/testdata/custom/dem_crs.tif (band 1)</p>
3+
<p>Volume: -65866966.86906432</p>
4+
<p>Pixel count: 130550</p>
5+
<p>Area: 23202698.7733309 m2</p>
6+
</body></html>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
4+
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
5+
<xs:complexType name="FeatureCollectionType">
6+
<xs:complexContent>
7+
<xs:extension base="gml:AbstractFeatureCollectionType">
8+
<xs:attribute name="lockId" type="xs:string" use="optional"/>
9+
<xs:attribute name="scope" type="xs:string" use="optional"/>
10+
</xs:extension>
11+
</xs:complexContent>
12+
</xs:complexType>
13+
<xs:element name="surface_vol_subtract_crs" type="ogr:surface_vol_subtract_crs_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="surface_vol_subtract_crs_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="volume" nillable="true" minOccurs="0" maxOccurs="1">
19+
<xs:simpleType>
20+
<xs:restriction base="xs:decimal">
21+
<xs:totalDigits value="21"/>
22+
<xs:fractionDigits value="8"/>
23+
</xs:restriction>
24+
</xs:simpleType>
25+
</xs:element>
26+
<xs:element name="m2" nillable="true" minOccurs="0" maxOccurs="1">
27+
<xs:simpleType>
28+
<xs:restriction base="xs:decimal">
29+
<xs:totalDigits value="21"/>
30+
<xs:fractionDigits value="8"/>
31+
</xs:restriction>
32+
</xs:simpleType>
33+
</xs:element>
34+
<xs:element name="pixel_count" nillable="true" minOccurs="0" maxOccurs="1">
35+
<xs:simpleType>
36+
<xs:restriction base="xs:long">
37+
<xs:totalDigits value="20"/>
38+
</xs:restriction>
39+
</xs:simpleType>
40+
</xs:element>
41+
</xs:sequence>
42+
</xs:extension>
43+
</xs:complexContent>
44+
</xs:complexType>
45+
</xs:schema>

‎python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6793,4 +6793,186 @@ tests:
67936793
pk:
67946794
- pk
67956795

6796+
- algorithm: native:rastersurfacevolume
6797+
name: Surface volume above
6798+
params:
6799+
BAND: 1
6800+
INPUT:
6801+
name: dem.tif
6802+
type: raster
6803+
LEVEL: 100.0
6804+
METHOD: 0
6805+
results:
6806+
OUTPUT_HTML_FILE:
6807+
name: expected/surface_vol_above.html
6808+
type: regex
6809+
rules:
6810+
- 'Volume: 0.064805'
6811+
- 'Pixel count: 95901'
6812+
- 'Area: 0.000959'
6813+
OUTPUT_TABLE:
6814+
name: expected/surface_vol_above.gml
6815+
type: vector
6816+
6817+
- algorithm: native:rastersurfacevolume
6818+
name: Surface volume Below
6819+
params:
6820+
BAND: 1
6821+
INPUT:
6822+
name: dem.tif
6823+
type: raster
6824+
LEVEL: 100.0
6825+
METHOD: 1
6826+
results:
6827+
OUTPUT_HTML_FILE:
6828+
name: expected/surface_vol_below.html
6829+
type: regex
6830+
rules:
6831+
- 'Volume: -0.00322225'
6832+
- 'Pixel count: 34600'
6833+
- 'Area: 0.00034599'
6834+
OUTPUT_TABLE:
6835+
name: expected/surface_vol_below.gml
6836+
type: vector
6837+
6838+
- algorithm: native:rastersurfacevolume
6839+
name: Surface volume subtract below
6840+
params:
6841+
BAND: 1
6842+
INPUT:
6843+
name: dem.tif
6844+
type: raster
6845+
LEVEL: 100.0
6846+
METHOD: 2
6847+
results:
6848+
OUTPUT_HTML_FILE:
6849+
name: expected/surface_vol_subtract.html
6850+
type: regex
6851+
rules:
6852+
- 'Volume: 0.0615830'
6853+
- 'Pixel count: 130550'
6854+
- 'Area: 0.00130549'
6855+
OUTPUT_TABLE:
6856+
name: expected/surface_vol_subtract.gml
6857+
type: vector
6858+
6859+
- algorithm: native:rastersurfacevolume
6860+
name: Surface volume add below
6861+
params:
6862+
BAND: 1
6863+
INPUT:
6864+
name: dem.tif
6865+
type: raster
6866+
LEVEL: 100.0
6867+
METHOD: 3
6868+
results:
6869+
OUTPUT_HTML_FILE:
6870+
name: expected/surface_vol_add.html
6871+
type: regex
6872+
rules:
6873+
- 'Volume: 0.06802752'
6874+
- 'Pixel count: 130550'
6875+
- 'Area: 0.00130549'
6876+
OUTPUT_TABLE:
6877+
name: expected/surface_vol_add.gml
6878+
type: vector
6879+
6880+
- algorithm: native:rastersurfacevolume
6881+
name: Surface volume above (meters)
6882+
params:
6883+
BAND: 1
6884+
INPUT:
6885+
name: custom/dem_crs.tif
6886+
type: raster
6887+
LEVEL: 150.0
6888+
METHOD: 0
6889+
results:
6890+
OUTPUT_HTML_FILE:
6891+
name: expected/surface_vol_above_crs.html
6892+
type: regex
6893+
rules:
6894+
- 'Volume: 413784918\.'
6895+
- 'Pixel count: 64692'
6896+
- 'Area: 11497732\.'
6897+
OUTPUT_TABLE:
6898+
name: expected/surface_vol_above_crs.gml
6899+
type: vector
6900+
6901+
- algorithm: native:rastersurfacevolume
6902+
name: Surface volume below (meters)
6903+
params:
6904+
BAND: 1
6905+
INPUT:
6906+
name: custom/dem_crs.tif
6907+
type: raster
6908+
LEVEL: 150.0
6909+
METHOD: 1
6910+
results:
6911+
OUTPUT_HTML_FILE:
6912+
name: expected/surface_vol_below_crs.html
6913+
type: regex
6914+
rules:
6915+
- 'Volume: -479651884\.'
6916+
- 'Pixel count: 65660'
6917+
- 'Area: 11669775\.'
6918+
OUTPUT_TABLE:
6919+
name: expected/surface_vol_below_crs.gml
6920+
type: vector
6921+
6922+
- algorithm: native:rastersurfacevolume
6923+
name: Surface volume subtract below (meters)
6924+
params:
6925+
BAND: 1
6926+
INPUT:
6927+
name: custom/dem_crs.tif
6928+
type: raster
6929+
LEVEL: 150.0
6930+
METHOD: 2
6931+
results:
6932+
OUTPUT_HTML_FILE:
6933+
name: expected/surface_vol_subtract_crs.html
6934+
type: regex
6935+
rules:
6936+
- 'Volume: -65866966\.'
6937+
- 'Pixel count: 130550'
6938+
- 'Area: 23202698\.'
6939+
OUTPUT_TABLE:
6940+
name: expected/surface_vol_subtract_crs.gml
6941+
type: vector
6942+
6943+
- algorithm: native:rastersurfacevolume
6944+
name: Surface volume add below (meters)
6945+
params:
6946+
BAND: 1
6947+
INPUT:
6948+
name: custom/dem_crs.tif
6949+
type: raster
6950+
LEVEL: 150.0
6951+
METHOD: 3
6952+
results:
6953+
OUTPUT_HTML_FILE:
6954+
name: expected/surface_vol_add_crs.html
6955+
type: regex
6956+
rules:
6957+
- 'Volume: 893436802\.'
6958+
- 'Pixel count: 130550'
6959+
- 'Area: 23202698\.'
6960+
OUTPUT_TABLE:
6961+
name: expected/surface_vol_add_crs.gml
6962+
type: vector
6963+
6964+
- algorithm: native:rastersurfacevolume
6965+
name: Surface volume with gaps
6966+
params:
6967+
BAND: 1
6968+
INPUT:
6969+
name: custom/dem_gaps.tif
6970+
type: raster
6971+
LEVEL: 101.0
6972+
METHOD: 0
6973+
results:
6974+
OUTPUT_TABLE:
6975+
name: expected/surface_vol_gaps.gml
6976+
type: vector
6977+
67966978
# See ../README.md for a description of the file format

‎src/analysis/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ SET(QGIS_ANALYSIS_SRCS
7777
processing/qgsalgorithmprojectpointcartesian.cpp
7878
processing/qgsalgorithmpromotetomultipart.cpp
7979
processing/qgsalgorithmrasterlayeruniquevalues.cpp
80+
processing/qgsalgorithmrastersurfacevolume.cpp
8081
processing/qgsalgorithmrasterzonalstats.cpp
8182
processing/qgsalgorithmreclassifybylayer.cpp
8283
processing/qgsalgorithmremoveduplicatesbyattribute.cpp
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
/***************************************************************************
2+
qgsalgorithmrasterlayeruniquevalues.cpp
3+
---------------------
4+
begin : January 2019
5+
copyright : (C) 2019 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#include "qgsalgorithmrastersurfacevolume.h"
19+
#include "qgsstringutils.h"
20+
21+
///@cond PRIVATE
22+
23+
QString QgsRasterSurfaceVolumeAlgorithm::name() const
24+
{
25+
return QStringLiteral( "rastersurfacevolume" );
26+
}
27+
28+
QString QgsRasterSurfaceVolumeAlgorithm::displayName() const
29+
{
30+
return QObject::tr( "Raster surface volume" );
31+
}
32+
33+
QStringList QgsRasterSurfaceVolumeAlgorithm::tags() const
34+
{
35+
return QObject::tr( "sum,volume,area,height,terrain,dem,elevation" ).split( ',' );
36+
}
37+
38+
QString QgsRasterSurfaceVolumeAlgorithm::group() const
39+
{
40+
return QObject::tr( "Raster analysis" );
41+
}
42+
43+
QString QgsRasterSurfaceVolumeAlgorithm::groupId() const
44+
{
45+
return QStringLiteral( "rasteranalysis" );
46+
}
47+
48+
void QgsRasterSurfaceVolumeAlgorithm::initAlgorithm( const QVariantMap & )
49+
{
50+
addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "INPUT" ),
51+
QObject::tr( "Input layer" ) ) );
52+
addParameter( new QgsProcessingParameterBand( QStringLiteral( "BAND" ),
53+
QObject::tr( "Band number" ), 1, QStringLiteral( "INPUT" ) ) );
54+
addParameter( new QgsProcessingParameterNumber( QStringLiteral( "LEVEL" ),
55+
QObject::tr( "Base level" ), QgsProcessingParameterNumber::Double, 0 ) );
56+
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "METHOD" ),
57+
QObject::tr( "Method" ), QStringList()
58+
<< QObject::tr( "Count Only Above Base Level" )
59+
<< QObject::tr( "Count Only Below Base Level" )
60+
<< QObject::tr( "Subtract Volumes Below Base Level" )
61+
<< QObject::tr( "Add Volumes Below Base Level" ) ) );
62+
63+
addParameter( new QgsProcessingParameterFileDestination( QStringLiteral( "OUTPUT_HTML_FILE" ),
64+
QObject::tr( "Surface volume report" ), QObject::tr( "HTML files (*.html)" ), QVariant(), true ) );
65+
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT_TABLE" ),
66+
QObject::tr( "Surface volume table" ), QgsProcessing::TypeVector, QVariant(), true, false ) );
67+
68+
addOutput( new QgsProcessingOutputNumber( QStringLiteral( "VOLUME" ), QObject::tr( "Volume" ) ) );
69+
addOutput( new QgsProcessingOutputNumber( QStringLiteral( "PIXEL_COUNT" ), QObject::tr( "Pixel count" ) ) );
70+
addOutput( new QgsProcessingOutputNumber( QStringLiteral( "AREA" ), QObject::tr( "Area" ) ) );
71+
}
72+
73+
QString QgsRasterSurfaceVolumeAlgorithm::shortHelpString() const
74+
{
75+
return QObject::tr( "This algorithm calculates the volume under a raster grid's surface.\n\n"
76+
"Several methods of volume calculation are available, which control whether "
77+
"only values above or below the specified base level are considered, or "
78+
"whether volumes below the base level should be added or subtracted from the total volume.\n\n"
79+
"The algorithm outputs the calculated volume, the total area, and the total number of pixels analysed. "
80+
"If the 'Count Only Above Base Level' or 'Count Only Below Base Level' methods are used, "
81+
"then the calculated area and pixel count only includes pixels which are above or below the "
82+
"specified base level respectively.\n\n"
83+
"Units of the calculated volume are dependent on the coordinate reference system of "
84+
"the input raster file. For a CRS in meters, with a DEM height in meters, the calculated "
85+
"value will be in meters³. If instead the input raster is in a geographic coordinate system "
86+
"(e.g. latitude/longitude values), then the result will be in degrees² × meters, and an "
87+
"appropriate scaling factor will need to be applied in order to convert to meters³." );
88+
}
89+
90+
QString QgsRasterSurfaceVolumeAlgorithm::shortDescription() const
91+
{
92+
return QObject::tr( "Calculates the volume under a raster grid's surface." );
93+
}
94+
95+
QgsRasterSurfaceVolumeAlgorithm *QgsRasterSurfaceVolumeAlgorithm::createInstance() const
96+
{
97+
return new QgsRasterSurfaceVolumeAlgorithm();
98+
}
99+
100+
bool QgsRasterSurfaceVolumeAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
101+
{
102+
QgsRasterLayer *layer = parameterAsRasterLayer( parameters, QStringLiteral( "INPUT" ), context );
103+
int band = parameterAsInt( parameters, QStringLiteral( "BAND" ), context );
104+
105+
if ( !layer )
106+
throw QgsProcessingException( invalidRasterError( parameters, QStringLiteral( "INPUT" ) ) );
107+
108+
mBand = parameterAsInt( parameters, QStringLiteral( "BAND" ), context );
109+
if ( mBand < 1 || mBand > layer->bandCount() )
110+
throw QgsProcessingException( QObject::tr( "Invalid band number for BAND (%1): Valid values for input raster are 1 to %2" ).arg( mBand )
111+
.arg( layer->bandCount() ) );
112+
113+
mInterface.reset( layer->dataProvider()->clone() );
114+
mHasNoDataValue = layer->dataProvider()->sourceHasNoDataValue( band );
115+
mLayerWidth = layer->width();
116+
mLayerHeight = layer->height();
117+
mExtent = layer->extent();
118+
mCrs = layer->crs();
119+
mRasterUnitsPerPixelX = layer->rasterUnitsPerPixelX();
120+
mRasterUnitsPerPixelY = layer->rasterUnitsPerPixelY();
121+
mSource = layer->source();
122+
123+
mLevel = parameterAsDouble( parameters, QStringLiteral( "LEVEL" ), context );
124+
mMethod = static_cast< Method >( parameterAsEnum( parameters, QStringLiteral( "METHOD" ), context ) );
125+
return true;
126+
}
127+
128+
QVariantMap QgsRasterSurfaceVolumeAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
129+
{
130+
QString outputFile = parameterAsFileOutput( parameters, QStringLiteral( "OUTPUT_HTML_FILE" ), context );
131+
QString areaUnit = QgsUnitTypes::toAbbreviatedString( QgsUnitTypes::distanceToAreaUnit( mCrs.mapUnits() ) );
132+
133+
QString tableDest;
134+
std::unique_ptr< QgsFeatureSink > sink;
135+
if ( parameters.contains( QStringLiteral( "OUTPUT_TABLE" ) ) && parameters.value( QStringLiteral( "OUTPUT_TABLE" ) ).isValid() )
136+
{
137+
QgsFields outFields;
138+
outFields.append( QgsField( QStringLiteral( "volume" ), QVariant::Double, QString(), 20, 8 ) );
139+
outFields.append( QgsField( areaUnit.replace( QStringLiteral( "²" ), QStringLiteral( "2" ) ), QVariant::Double, QString(), 20, 8 ) );
140+
outFields.append( QgsField( QStringLiteral( "pixel_count" ), QVariant::LongLong ) );
141+
sink.reset( parameterAsSink( parameters, QStringLiteral( "OUTPUT_TABLE" ), context, tableDest, outFields, QgsWkbTypes::NoGeometry, QgsCoordinateReferenceSystem() ) );
142+
if ( !sink )
143+
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT_TABLE" ) ) );
144+
}
145+
146+
double volume = 0;
147+
long long count = 0;
148+
149+
int maxWidth = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_WIDTH;
150+
int maxHeight = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_HEIGHT;
151+
int nbBlocksWidth = static_cast< int >( std::ceil( 1.0 * mLayerWidth / maxWidth ) );
152+
int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * mLayerHeight / maxHeight ) );
153+
int nbBlocks = nbBlocksWidth * nbBlocksHeight;
154+
155+
QgsRasterIterator iter( mInterface.get() );
156+
iter.startRasterRead( mBand, mLayerWidth, mLayerHeight, mExtent );
157+
158+
int iterLeft = 0;
159+
int iterTop = 0;
160+
int iterCols = 0;
161+
int iterRows = 0;
162+
std::unique_ptr< QgsRasterBlock > rasterBlock;
163+
while ( iter.readNextRasterPart( mBand, iterCols, iterRows, rasterBlock, iterLeft, iterTop ) )
164+
{
165+
feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
166+
for ( int row = 0; row < iterRows; row++ )
167+
{
168+
if ( feedback->isCanceled() )
169+
break;
170+
for ( int column = 0; column < iterCols; column++ )
171+
{
172+
if ( mHasNoDataValue && rasterBlock->isNoData( row, column ) )
173+
{
174+
continue;
175+
}
176+
177+
const double z = rasterBlock->value( row, column ) - mLevel;
178+
179+
switch ( mMethod )
180+
{
181+
case CountOnlyAboveBaseLevel:
182+
if ( z > 0.0 )
183+
{
184+
volume += z;
185+
count++;
186+
}
187+
continue;
188+
189+
case CountOnlyBelowBaseLevel:
190+
if ( z < 0.0 )
191+
{
192+
volume += z;
193+
count++;
194+
}
195+
continue;
196+
197+
case SubtractVolumesBelowBaseLevel:
198+
volume += z;
199+
count++;
200+
continue;
201+
202+
case AddVolumesBelowBaseLevel:
203+
volume += std::fabs( z );
204+
count++;
205+
continue;
206+
}
207+
}
208+
}
209+
}
210+
211+
QVariantMap outputs;
212+
double pixelArea = mRasterUnitsPerPixelX * mRasterUnitsPerPixelY;
213+
double area = count * pixelArea;
214+
volume *= pixelArea;
215+
if ( !outputFile.isEmpty() )
216+
{
217+
QFile file( outputFile );
218+
if ( file.open( QIODevice::WriteOnly | QIODevice::Text ) )
219+
{
220+
const QString encodedAreaUnit = QgsStringUtils::ampersandEncode( areaUnit );
221+
222+
QTextStream out( &file );
223+
out << QStringLiteral( "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"/></head><body>\n" );
224+
out << QStringLiteral( "<p>%1: %2 (%3 %4)</p>\n" ).arg( QObject::tr( "Analyzed file" ), mSource, QObject::tr( "band" ) ).arg( mBand );
225+
out << QObject::tr( "<p>%1: %2</p>\n" ).arg( QObject::tr( "Volume" ), QString::number( volume, 'g', 16 ) );
226+
out << QObject::tr( "<p>%1: %2</p>\n" ).arg( QObject::tr( "Pixel count" ) ).arg( count );
227+
out << QObject::tr( "<p>%1: %2 %3</p>\n" ).arg( QObject::tr( "Area" ), QString::number( area, 'g', 16 ), encodedAreaUnit );
228+
out << QStringLiteral( "</body></html>" );
229+
outputs.insert( QStringLiteral( "OUTPUT_HTML_FILE" ), outputFile );
230+
}
231+
}
232+
233+
if ( sink )
234+
{
235+
QgsFeature f;
236+
f.setAttributes( QgsAttributes() << volume << area << count );
237+
sink->addFeature( f, QgsFeatureSink::FastInsert );
238+
outputs.insert( QStringLiteral( "OUTPUT_TABLE" ), tableDest );
239+
}
240+
outputs.insert( QStringLiteral( "VOLUME" ), volume );
241+
outputs.insert( QStringLiteral( "AREA" ), area );
242+
outputs.insert( QStringLiteral( "PIXEL_COUNT" ), count );
243+
return outputs;
244+
}
245+
246+
247+
///@endcond
248+
249+
250+
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/***************************************************************************
2+
qgsalgorithmrasterlayeruniquevalues.cpp
3+
---------------------
4+
begin : January 2019
5+
copyright : (C) 2019 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#ifndef QGSALGORITHMRASTERSURFACEVOLUME_H
19+
#define QGSALGORITHMRASTERSURFACEVOLUME_H
20+
21+
#define SIP_NO_FILE
22+
23+
#include "qgis.h"
24+
#include "qgsprocessingalgorithm.h"
25+
26+
///@cond PRIVATE
27+
28+
/**
29+
* Native raster layer unique values report algorithm.
30+
*/
31+
class QgsRasterSurfaceVolumeAlgorithm : public QgsProcessingAlgorithm
32+
{
33+
34+
public:
35+
36+
QgsRasterSurfaceVolumeAlgorithm() = default;
37+
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
38+
QString name() const override;
39+
QString displayName() const override;
40+
QStringList tags() const override;
41+
QString group() const override;
42+
QString groupId() const override;
43+
QString shortHelpString() const override;
44+
QString shortDescription() const override;
45+
QgsRasterSurfaceVolumeAlgorithm *createInstance() const override SIP_FACTORY;
46+
47+
protected:
48+
49+
bool prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
50+
QVariantMap processAlgorithm( const QVariantMap &parameters,
51+
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
52+
53+
private:
54+
55+
enum Method
56+
{
57+
CountOnlyAboveBaseLevel = 0,
58+
CountOnlyBelowBaseLevel,
59+
SubtractVolumesBelowBaseLevel,
60+
AddVolumesBelowBaseLevel
61+
};
62+
63+
std::unique_ptr< QgsRasterInterface > mInterface;
64+
bool mHasNoDataValue = false;
65+
int mBand = 1;
66+
int mLayerWidth;
67+
int mLayerHeight;
68+
QgsRectangle mExtent;
69+
QgsCoordinateReferenceSystem mCrs;
70+
double mRasterUnitsPerPixelX;
71+
double mRasterUnitsPerPixelY;
72+
double mLevel = 0;
73+
QString mSource;
74+
Method mMethod;
75+
76+
};
77+
78+
///@endcond PRIVATE
79+
80+
#endif // QGSALGORITHMRASTERSURFACEVOLUME_H
81+
82+

‎src/analysis/processing/qgsnativealgorithms.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
#include "qgsalgorithmprojectpointcartesian.h"
7373
#include "qgsalgorithmpromotetomultipart.h"
7474
#include "qgsalgorithmrasterlayeruniquevalues.h"
75+
#include "qgsalgorithmrastersurfacevolume.h"
7576
#include "qgsalgorithmrasterzonalstats.h"
7677
#include "qgsalgorithmreclassifybylayer.h"
7778
#include "qgsalgorithmremoveduplicatesbyattribute.h"
@@ -209,6 +210,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
209210
addAlgorithm( new QgsRasterLayerZonalStatsAlgorithm() );
210211
addAlgorithm( new QgsRasterPixelsToPointsAlgorithm() );
211212
addAlgorithm( new QgsRasterPixelsToPolygonsAlgorithm() );
213+
addAlgorithm( new QgsRasterSurfaceVolumeAlgorithm() );
212214
addAlgorithm( new QgsAlgorithmRemoveDuplicateVertices() );
213215
addAlgorithm( new QgsReclassifyByLayerAlgorithm() );
214216
addAlgorithm( new QgsReclassifyByTableAlgorithm() );

0 commit comments

Comments
 (0)
Please sign in to comment.