Skip to content

Commit 526e590

Browse files
committed
[FEATURE][processing] Add "raster pixels to points" algorithm
Refactor the existing "raster pixels to polygons" algorithm and create a new "pixels to points" algorithm, which creates a point feature at the center of every pixel. nodata pixels are skipped.
1 parent 64de2a2 commit 526e590

File tree

8 files changed

+1005
-50
lines changed

8 files changed

+1005
-50
lines changed

python/plugins/processing/tests/testdata/expected/pixels_to_points.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="pixels_to_points" type="ogr:pixels_to_points_Type" substitutionGroup="gml:_Feature"/>
14+
<xs:complexType name="pixels_to_points_Type">
15+
<xs:complexContent>
16+
<xs:extension base="gml:AbstractFeatureType">
17+
<xs:sequence>
18+
<xs:element name="geometryProperty" type="gml:PointPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
19+
<xs:element name="VALUE" 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>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<GMLFeatureClassList>
2+
<GMLFeatureClass>
3+
<Name>pointsvoronoi1diagram</Name>
4+
<ElementPath>pointsvoronoi1diagram</ElementPath>
5+
<!--POLYGON-->
6+
<GeometryType>3</GeometryType>
7+
<SRSName>EPSG:4326</SRSName>
8+
<DatasetSpecificInfo>
9+
<FeatureCount>6</FeatureCount>
10+
<ExtentXMin>-0.60000</ExtentXMin>
11+
<ExtentXMax>0.20000</ExtentXMax>
12+
<ExtentYMin>51.40000</ExtentYMin>
13+
<ExtentYMax>51.60000</ExtentYMax>
14+
</DatasetSpecificInfo>
15+
<PropertyDefn>
16+
<Name>id</Name>
17+
<ElementPath>id</ElementPath>
18+
<Type>Integer</Type>
19+
</PropertyDefn>
20+
</GMLFeatureClass>
21+
</GMLFeatureClassList>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<GMLFeatureClassList>
2+
<GMLFeatureClass>
3+
<Name>pointsvoronoi1</Name>
4+
<ElementPath>pointsvoronoi1</ElementPath>
5+
<!--POINT-->
6+
<GeometryType>1</GeometryType>
7+
<SRSName>EPSG:4326</SRSName>
8+
<DatasetSpecificInfo>
9+
<FeatureCount>6</FeatureCount>
10+
<ExtentXMin>-0.60000</ExtentXMin>
11+
<ExtentXMax>0.20000</ExtentXMax>
12+
<ExtentYMin>51.40000</ExtentYMin>
13+
<ExtentYMax>51.60000</ExtentYMax>
14+
</DatasetSpecificInfo>
15+
<PropertyDefn>
16+
<Name>id</Name>
17+
<ElementPath>id</ElementPath>
18+
<Type>Integer</Type>
19+
</PropertyDefn>
20+
</GMLFeatureClass>
21+
</GMLFeatureClassList>

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5725,6 +5725,19 @@ tests:
57255725
name: expected/vectorize.gml
57265726
type: vector
57275727

5728+
- algorithm: native:pixelstopoints
5729+
name: Pixels to points
5730+
params:
5731+
FIELD_NAME: VALUE
5732+
INPUT_RASTER:
5733+
name: raster.tif
5734+
type: raster
5735+
RASTER_BAND: 1
5736+
results:
5737+
OUTPUT:
5738+
name: expected/pixels_to_points.gml
5739+
type: vector
5740+
57285741
- algorithm: native:kmeansclustering
57295742
name: K means, points, 3 clusters
57305743
params:

src/analysis/processing/qgsalgorithmvectorize.cpp

Lines changed: 123 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -21,42 +21,17 @@
2121

2222
///@cond PRIVATE
2323

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
24+
QString QgsVectorizeAlgorithmBase::group() const
5025
{
5126
return QObject::tr( "Vector creation" );
5227
}
5328

54-
QString QgsVectorizeAlgorithm::groupId() const
29+
QString QgsVectorizeAlgorithmBase::groupId() const
5530
{
5631
return QStringLiteral( "vectorcreation" );
5732
}
5833

59-
void QgsVectorizeAlgorithm::initAlgorithm( const QVariantMap & )
34+
void QgsVectorizeAlgorithmBase::initAlgorithm( const QVariantMap & )
6035
{
6136
addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "INPUT_RASTER" ),
6237
QObject::tr( "Raster layer" ) ) );
@@ -65,10 +40,10 @@ void QgsVectorizeAlgorithm::initAlgorithm( const QVariantMap & )
6540
addParameter( new QgsProcessingParameterString( QStringLiteral( "FIELD_NAME" ),
6641
QObject::tr( "Field name" ), QStringLiteral( "VALUE" ) ) );
6742

68-
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Vectorized layer" ), QgsProcessing::TypeVectorPolygon ) );
43+
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), outputName(), outputType() ) );
6944
}
7045

71-
bool QgsVectorizeAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
46+
bool QgsVectorizeAlgorithmBase::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
7247
{
7348
QgsRasterLayer *layer = parameterAsRasterLayer( parameters, QStringLiteral( "INPUT_RASTER" ), context );
7449

@@ -90,14 +65,14 @@ bool QgsVectorizeAlgorithm::prepareAlgorithm( const QVariantMap &parameters, Qgs
9065
return true;
9166
}
9267

93-
QVariantMap QgsVectorizeAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
68+
QVariantMap QgsVectorizeAlgorithmBase::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
9469
{
9570
const QString fieldName = parameterAsString( parameters, QStringLiteral( "FIELD_NAME" ), context );
9671
QgsFields fields;
9772
fields.append( QgsField( fieldName, QVariant::Double, QString(), 20, 8 ) );
9873

9974
QString dest;
100-
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, QgsWkbTypes::Polygon, mCrs ) );
75+
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, sinkType(), mCrs ) );
10176
if ( !sink )
10277
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
10378

@@ -112,9 +87,6 @@ QVariantMap QgsVectorizeAlgorithm::processAlgorithm( const QVariantMap &paramete
11287
int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * mNbCellsYProvider / maxHeight ) );
11388
int nbBlocks = nbBlocksWidth * nbBlocksHeight;
11489

115-
double hCellSizeX = mRasterUnitsPerPixelX / 2.0;
116-
double hCellSizeY = mRasterUnitsPerPixelY / 2.0;
117-
11890
int iterLeft = 0;
11991
int iterTop = 0;
12092
int iterCols = 0;
@@ -141,9 +113,7 @@ QVariantMap QgsVectorizeAlgorithm::processAlgorithm( const QVariantMap &paramete
141113
{
142114
if ( !rasterBlock->isNoData( row, column ) )
143115
{
144-
145-
QgsGeometry pixelRectGeometry = QgsGeometry::fromRect( QgsRectangle( currentX - hCellSizeX, currentY - hCellSizeY, currentX + hCellSizeX, currentY + hCellSizeY ) );
146-
116+
QgsGeometry pixelRectGeometry = createGeometryForPixel( currentX, currentY, mRasterUnitsPerPixelX, mRasterUnitsPerPixelY );
147117
double value = rasterBlock->value( row, column );
148118

149119
QgsFeature f;
@@ -162,6 +132,121 @@ QVariantMap QgsVectorizeAlgorithm::processAlgorithm( const QVariantMap &paramete
162132
return outputs;
163133
}
164134

135+
//
136+
// QgsRasterPixelsToPolygonsAlgorithm
137+
//
138+
139+
QString QgsRasterPixelsToPolygonsAlgorithm::name() const
140+
{
141+
return QStringLiteral( "pixelstopolygons" );
142+
}
143+
144+
QString QgsRasterPixelsToPolygonsAlgorithm::displayName() const
145+
{
146+
return QObject::tr( "Raster pixels to polygons" );
147+
}
148+
149+
QStringList QgsRasterPixelsToPolygonsAlgorithm::tags() const
150+
{
151+
return QObject::tr( "vectorize,polygonize,raster,convert,pixels" ).split( ',' );
152+
}
153+
154+
QString QgsRasterPixelsToPolygonsAlgorithm::shortHelpString() const
155+
{
156+
return QObject::tr( "This algorithm converts a raster layer to a vector layer, by creating polygon features "
157+
"for each individual pixel's extent in the raster layer.\n\n"
158+
"Any nodata pixels are skipped in the output." );
159+
}
160+
161+
QString QgsRasterPixelsToPolygonsAlgorithm::shortDescription() const
162+
{
163+
return QObject::tr( "Creates a vector layer of polygons corresponding to each pixel in a raster layer." );
164+
}
165+
166+
QgsRasterPixelsToPolygonsAlgorithm *QgsRasterPixelsToPolygonsAlgorithm::createInstance() const
167+
{
168+
return new QgsRasterPixelsToPolygonsAlgorithm();
169+
}
170+
171+
QString QgsRasterPixelsToPolygonsAlgorithm::outputName() const
172+
{
173+
return QObject::tr( "Vector polygons" );
174+
}
175+
176+
QgsProcessing::SourceType QgsRasterPixelsToPolygonsAlgorithm::outputType() const
177+
{
178+
return QgsProcessing::TypeVectorPolygon;
179+
}
180+
181+
QgsWkbTypes::Type QgsRasterPixelsToPolygonsAlgorithm::sinkType() const
182+
{
183+
return QgsWkbTypes::Polygon;
184+
}
185+
186+
QgsGeometry QgsRasterPixelsToPolygonsAlgorithm::createGeometryForPixel( double centerX, double centerY, double pixelWidthX, double pixelWidthY ) const
187+
{
188+
const double hCellSizeX = pixelWidthX / 2.0;
189+
const double hCellSizeY = pixelWidthY / 2.0;
190+
return QgsGeometry::fromRect( QgsRectangle( centerX - hCellSizeX, centerY - hCellSizeY, centerX + hCellSizeX, centerY + hCellSizeY ) );
191+
}
192+
193+
194+
//
195+
// QgsRasterPixelsToPointsAlgorithm
196+
//
197+
198+
QString QgsRasterPixelsToPointsAlgorithm::name() const
199+
{
200+
return QStringLiteral( "pixelstopoints" );
201+
}
202+
203+
QString QgsRasterPixelsToPointsAlgorithm::displayName() const
204+
{
205+
return QObject::tr( "Raster pixels to points" );
206+
}
207+
208+
QStringList QgsRasterPixelsToPointsAlgorithm::tags() const
209+
{
210+
return QObject::tr( "vectorize,polygonize,raster,convert,pixels,centers" ).split( ',' );
211+
}
212+
213+
QString QgsRasterPixelsToPointsAlgorithm::shortHelpString() const
214+
{
215+
return QObject::tr( "This algorithm converts a raster layer to a vector layer, by creating point features "
216+
"for each individual pixel's center in the raster layer.\n\n"
217+
"Any nodata pixels are skipped in the output." );
218+
}
219+
220+
QString QgsRasterPixelsToPointsAlgorithm::shortDescription() const
221+
{
222+
return QObject::tr( "Creates a vector layer of points corresponding to each pixel in a raster layer." );
223+
}
224+
225+
QgsRasterPixelsToPointsAlgorithm *QgsRasterPixelsToPointsAlgorithm::createInstance() const
226+
{
227+
return new QgsRasterPixelsToPointsAlgorithm();
228+
}
229+
230+
QString QgsRasterPixelsToPointsAlgorithm::outputName() const
231+
{
232+
return QObject::tr( "Vector points" );
233+
}
234+
235+
QgsProcessing::SourceType QgsRasterPixelsToPointsAlgorithm::outputType() const
236+
{
237+
return QgsProcessing::TypeVectorPoint;
238+
}
239+
240+
QgsWkbTypes::Type QgsRasterPixelsToPointsAlgorithm::sinkType() const
241+
{
242+
return QgsWkbTypes::Point;
243+
}
244+
245+
QgsGeometry QgsRasterPixelsToPointsAlgorithm::createGeometryForPixel( double centerX, double centerY, double, double ) const
246+
{
247+
return QgsGeometry( new QgsPoint( centerX, centerY ) );
248+
}
249+
165250
///@endcond
166251

167252

0 commit comments

Comments
 (0)