Skip to content

Commit 5b7eefa

Browse files
committed
[FEATURE][processing] New 'Raster pixels to polygons' algorithm
Converts a raster layer into a vector layer, with a polygon feature corresponding to each pixel from the raster and a single field containing the band value from the raster. Sponsored by SMEC/SJ
1 parent 39f0922 commit 5b7eefa

File tree

7 files changed

+1019
-1
lines changed

7 files changed

+1019
-1
lines changed

python/plugins/processing/tests/testdata/expected/vectorize.gml

Lines changed: 734 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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="vectorize" type="ogr:vectorize_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="vectorize_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="geometryProperty" type="gml:PolygonPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
19+
<xs:element name="pix_val" nillable="true" minOccurs="0" maxOccurs="1">
20+
<xs:simpleType>
21+
<xs:restriction base="xs:decimal">
22+
<xs:totalDigits value="21"/>
23+
<xs:fractionDigits value="8"/>
24+
</xs:restriction>
25+
</xs:simpleType>
26+
</xs:element>
27+
</xs:sequence>
28+
</xs:extension>
29+
</xs:complexContent>
30+
</xs:complexType>
31+
</xs:schema>

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5647,6 +5647,17 @@ tests:
56475647
hash: c29d14f71e8686f7445d53be646fce84702644f159fd0164ac38e861
56485648
type: rasterhash
56495649

5650-
5650+
- algorithm: native:pixelstopolygons
5651+
name: Pixels to polygons
5652+
params:
5653+
FIELD_NAME: pix_val
5654+
INPUT_RASTER:
5655+
name: raster.tif
5656+
type: raster
5657+
RASTER_BAND: 1
5658+
results:
5659+
OUTPUT:
5660+
name: expected/vectorize.gml
5661+
type: vector
56515662

56525663
# 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
@@ -82,6 +82,7 @@ SET(QGIS_ANALYSIS_SRCS
8282
processing/qgsalgorithmtranslate.cpp
8383
processing/qgsalgorithmunion.cpp
8484
processing/qgsalgorithmuniquevalueindex.cpp
85+
processing/qgsalgorithmvectorize.cpp
8586
processing/qgsalgorithmwedgebuffers.cpp
8687
processing/qgsalgorithmzonalhistogram.cpp
8788

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/***************************************************************************
2+
qgsalgorithmvectorize.cpp
3+
---------------------
4+
begin : June, 2018
5+
copyright : (C) 2018 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 "qgsalgorithmvectorize.h"
19+
#include "qgis.h"
20+
#include "qgsprocessing.h"
21+
22+
///@cond PRIVATE
23+
24+
QString QgsVectorizeAlgorithm::name() const
25+
{
26+
return QStringLiteral( "pixelstopolygons" );
27+
}
28+
29+
QString QgsVectorizeAlgorithm::displayName() const
30+
{
31+
return QObject::tr( "Raster pixels to polygons" );
32+
}
33+
34+
QStringList QgsVectorizeAlgorithm::tags() const
35+
{
36+
return QObject::tr( "vectorize,polygonize,raster,convert,pixels" ).split( ',' );
37+
}
38+
39+
QString QgsVectorizeAlgorithm::shortHelpString() const
40+
{
41+
return QObject::tr( "This algorithm converts a raster layer to a vector layer, by creating polygon features for each individual pixel in the raster layer." );
42+
}
43+
44+
QgsVectorizeAlgorithm *QgsVectorizeAlgorithm::createInstance() const
45+
{
46+
return new QgsVectorizeAlgorithm();
47+
}
48+
49+
QString QgsVectorizeAlgorithm::group() const
50+
{
51+
return QObject::tr( "Vector creation" );
52+
}
53+
54+
QString QgsVectorizeAlgorithm::groupId() const
55+
{
56+
return QStringLiteral( "vectorcreation" );
57+
}
58+
59+
void QgsVectorizeAlgorithm::initAlgorithm( const QVariantMap & )
60+
{
61+
addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "INPUT_RASTER" ),
62+
QObject::tr( "Raster layer" ) ) );
63+
addParameter( new QgsProcessingParameterBand( QStringLiteral( "RASTER_BAND" ),
64+
QObject::tr( "Band number" ), 1, QStringLiteral( "INPUT_RASTER" ) ) );
65+
addParameter( new QgsProcessingParameterString( QStringLiteral( "FIELD_NAME" ),
66+
QObject::tr( "Field name" ), QStringLiteral( "VALUE" ) ) );
67+
68+
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Vectorized layer" ), QgsProcessing::TypeVectorPolygon ) );
69+
}
70+
71+
bool QgsVectorizeAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
72+
{
73+
QgsRasterLayer *layer = parameterAsRasterLayer( parameters, QStringLiteral( "INPUT_RASTER" ), context );
74+
75+
if ( !layer )
76+
throw QgsProcessingException( invalidRasterError( parameters, QStringLiteral( "INPUT_RASTER" ) ) );
77+
78+
mBand = parameterAsInt( parameters, QStringLiteral( "RASTER_BAND" ), context );
79+
if ( mBand < 1 || mBand > layer->bandCount() )
80+
throw QgsProcessingException( QObject::tr( "Invalid band number for RASTER_BAND (%1): Valid values for input raster are 1 to %2" ).arg( mBand )
81+
.arg( layer->bandCount() ) );
82+
83+
mInterface.reset( layer->dataProvider()->clone() );
84+
mExtent = layer->extent();
85+
mCrs = layer->crs();
86+
mRasterUnitsPerPixelX = std::abs( layer->rasterUnitsPerPixelX() );
87+
mRasterUnitsPerPixelY = std::abs( layer->rasterUnitsPerPixelY() );
88+
mNbCellsXProvider = mInterface->xSize();
89+
mNbCellsYProvider = mInterface->ySize();
90+
return true;
91+
}
92+
93+
QVariantMap QgsVectorizeAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
94+
{
95+
const QString fieldName = parameterAsString( parameters, QStringLiteral( "FIELD_NAME" ), context );
96+
QgsFields fields;
97+
fields.append( QgsField( fieldName, QVariant::Double, QString(), 20, 8 ) );
98+
99+
QString dest;
100+
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, QgsWkbTypes::Polygon, mCrs ) );
101+
if ( !sink )
102+
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
103+
104+
105+
int maxWidth = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_WIDTH;
106+
int maxHeight = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_HEIGHT;
107+
108+
QgsRasterIterator iter( mInterface.get() );
109+
iter.startRasterRead( mBand, mNbCellsXProvider, mNbCellsYProvider, mExtent );
110+
111+
int nbBlocksWidth = static_cast< int >( std::ceil( 1.0 * mNbCellsXProvider / maxWidth ) );
112+
int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * mNbCellsYProvider / maxHeight ) );
113+
int nbBlocks = nbBlocksWidth * nbBlocksHeight;
114+
115+
double hCellSizeX = mRasterUnitsPerPixelX / 2.0;
116+
double hCellSizeY = mRasterUnitsPerPixelY / 2.0;
117+
118+
int iterLeft = 0;
119+
int iterTop = 0;
120+
int iterCols = 0;
121+
int iterRows = 0;
122+
std::unique_ptr< QgsRasterBlock > rasterBlock;
123+
QgsRectangle blockExtent;
124+
while ( iter.readNextRasterPart( mBand, iterCols, iterRows, rasterBlock, iterLeft, iterTop, &blockExtent ) )
125+
{
126+
if ( feedback )
127+
feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
128+
if ( feedback && feedback->isCanceled() )
129+
break;
130+
131+
double currentY = blockExtent.yMaximum() - 0.5 * mRasterUnitsPerPixelY;
132+
133+
for ( int row = 0; row < iterRows; row++ )
134+
{
135+
if ( feedback && feedback->isCanceled() )
136+
break;
137+
138+
double currentX = blockExtent.xMinimum() + 0.5 * mRasterUnitsPerPixelX;
139+
140+
for ( int column = 0; column < iterCols; column++ )
141+
{
142+
if ( !rasterBlock->isNoData( row, column ) )
143+
{
144+
145+
QgsGeometry pixelRectGeometry = QgsGeometry::fromRect( QgsRectangle( currentX - hCellSizeX, currentY - hCellSizeY, currentX + hCellSizeX, currentY + hCellSizeY ) );
146+
147+
double value = rasterBlock->value( row, column );
148+
149+
QgsFeature f;
150+
f.setGeometry( pixelRectGeometry );
151+
f.setAttributes( QgsAttributes() << value );
152+
sink->addFeature( f, QgsFeatureSink::FastInsert );
153+
}
154+
currentX += mRasterUnitsPerPixelX;
155+
}
156+
currentY -= mRasterUnitsPerPixelY;
157+
}
158+
}
159+
160+
QVariantMap outputs;
161+
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
162+
return outputs;
163+
}
164+
165+
///@endcond
166+
167+
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/***************************************************************************
2+
qgsalgorithmvectorize.h
3+
---------------------
4+
begin : June, 2018
5+
copyright : (C) 2018 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 QGSALGORITHMVECTORIZE_H
19+
#define QGSALGORITHMVECTORIZE_H
20+
21+
#define SIP_NO_FILE
22+
23+
#include "qgis.h"
24+
#include "qgsprocessingalgorithm.h"
25+
#include "qgsreclassifyutils.h"
26+
27+
///@cond PRIVATE
28+
29+
/**
30+
* Native vectorize algorithm
31+
*/
32+
class QgsVectorizeAlgorithm : public QgsProcessingAlgorithm
33+
{
34+
public:
35+
36+
QgsVectorizeAlgorithm() = default;
37+
QString name() const override;
38+
QString displayName() const override;
39+
QStringList tags() const override;
40+
QString shortHelpString() const override;
41+
QgsVectorizeAlgorithm *createInstance() const override SIP_FACTORY;
42+
QString group() const override final;
43+
QString groupId() const override final;
44+
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
45+
46+
protected:
47+
48+
bool prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
49+
50+
QVariantMap processAlgorithm( const QVariantMap &parameters,
51+
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
52+
53+
std::unique_ptr< QgsRasterInterface > mInterface;
54+
55+
Qgis::DataType mDataType = Qgis::Float32;
56+
double mNoDataValue = -9999;
57+
int mBand = 1;
58+
QgsRectangle mExtent;
59+
QgsCoordinateReferenceSystem mCrs;
60+
double mRasterUnitsPerPixelX = 0;
61+
double mRasterUnitsPerPixelY = 0;
62+
int mNbCellsXProvider = 0;
63+
int mNbCellsYProvider = 0;
64+
QgsReclassifyUtils::RasterClass::BoundsType mBoundsType = QgsReclassifyUtils::RasterClass::IncludeMax;
65+
bool mUseNoDataForMissingValues = false;
66+
};
67+
68+
///@endcond PRIVATE
69+
70+
#endif // QGSALGORITHMVECTORIZE_H
71+
72+

src/analysis/processing/qgsnativealgorithms.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
#include "qgsalgorithmtranslate.h"
8080
#include "qgsalgorithmunion.h"
8181
#include "qgsalgorithmuniquevalueindex.h"
82+
#include "qgsalgorithmvectorize.h"
8283
#include "qgsalgorithmwedgebuffers.h"
8384
#include "qgsalgorithmzonalhistogram.h"
8485

@@ -189,6 +190,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
189190
addAlgorithm( new QgsTranslateAlgorithm() );
190191
addAlgorithm( new QgsUnionAlgorithm() );
191192
addAlgorithm( new QgsVariableWidthBufferByMAlgorithm() );
193+
addAlgorithm( new QgsVectorizeAlgorithm() );
192194
addAlgorithm( new QgsWedgeBuffersAlgorithm() );
193195
addAlgorithm( new QgsZonalHistogramAlgorithm() );
194196
}

0 commit comments

Comments
 (0)