Skip to content

Commit

Permalink
[FEATURE][processing] Add "raster pixels to points" algorithm
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nyalldawson committed Aug 9, 2018
1 parent 64de2a2 commit 526e590
Show file tree
Hide file tree
Showing 8 changed files with 1,005 additions and 50 deletions.
734 changes: 734 additions & 0 deletions python/plugins/processing/tests/testdata/expected/pixels_to_points.gml

Large diffs are not rendered by default.

@@ -0,0 +1,31 @@
<?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="pixels_to_points" type="ogr:pixels_to_points_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="pixels_to_points_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="geometryProperty" type="gml:PointPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
<xs:element name="VALUE" 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:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
@@ -0,0 +1,21 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>pointsvoronoi1diagram</Name>
<ElementPath>pointsvoronoi1diagram</ElementPath>
<!--POLYGON-->
<GeometryType>3</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>6</FeatureCount>
<ExtentXMin>-0.60000</ExtentXMin>
<ExtentXMax>0.20000</ExtentXMax>
<ExtentYMin>51.40000</ExtentYMin>
<ExtentYMax>51.60000</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>id</Name>
<ElementPath>id</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>
21 changes: 21 additions & 0 deletions python/plugins/processing/tests/testdata/pointsvoronoi1.gfs
@@ -0,0 +1,21 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>pointsvoronoi1</Name>
<ElementPath>pointsvoronoi1</ElementPath>
<!--POINT-->
<GeometryType>1</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>6</FeatureCount>
<ExtentXMin>-0.60000</ExtentXMin>
<ExtentXMax>0.20000</ExtentXMax>
<ExtentYMin>51.40000</ExtentYMin>
<ExtentYMax>51.60000</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>id</Name>
<ElementPath>id</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>
13 changes: 13 additions & 0 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Expand Up @@ -5725,6 +5725,19 @@ tests:
name: expected/vectorize.gml
type: vector

- algorithm: native:pixelstopoints
name: Pixels to points
params:
FIELD_NAME: VALUE
INPUT_RASTER:
name: raster.tif
type: raster
RASTER_BAND: 1
results:
OUTPUT:
name: expected/pixels_to_points.gml
type: vector

- algorithm: native:kmeansclustering
name: K means, points, 3 clusters
params:
Expand Down
161 changes: 123 additions & 38 deletions src/analysis/processing/qgsalgorithmvectorize.cpp
Expand Up @@ -21,42 +21,17 @@

///@cond PRIVATE

QString QgsVectorizeAlgorithm::name() const
{
return QStringLiteral( "pixelstopolygons" );
}

QString QgsVectorizeAlgorithm::displayName() const
{
return QObject::tr( "Raster pixels to polygons" );
}

QStringList QgsVectorizeAlgorithm::tags() const
{
return QObject::tr( "vectorize,polygonize,raster,convert,pixels" ).split( ',' );
}

QString QgsVectorizeAlgorithm::shortHelpString() const
{
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." );
}

QgsVectorizeAlgorithm *QgsVectorizeAlgorithm::createInstance() const
{
return new QgsVectorizeAlgorithm();
}

QString QgsVectorizeAlgorithm::group() const
QString QgsVectorizeAlgorithmBase::group() const
{
return QObject::tr( "Vector creation" );
}

QString QgsVectorizeAlgorithm::groupId() const
QString QgsVectorizeAlgorithmBase::groupId() const
{
return QStringLiteral( "vectorcreation" );
}

void QgsVectorizeAlgorithm::initAlgorithm( const QVariantMap & )
void QgsVectorizeAlgorithmBase::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "INPUT_RASTER" ),
QObject::tr( "Raster layer" ) ) );
Expand All @@ -65,10 +40,10 @@ void QgsVectorizeAlgorithm::initAlgorithm( const QVariantMap & )
addParameter( new QgsProcessingParameterString( QStringLiteral( "FIELD_NAME" ),
QObject::tr( "Field name" ), QStringLiteral( "VALUE" ) ) );

addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Vectorized layer" ), QgsProcessing::TypeVectorPolygon ) );
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), outputName(), outputType() ) );
}

bool QgsVectorizeAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
bool QgsVectorizeAlgorithmBase::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
{
QgsRasterLayer *layer = parameterAsRasterLayer( parameters, QStringLiteral( "INPUT_RASTER" ), context );

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

QVariantMap QgsVectorizeAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
QVariantMap QgsVectorizeAlgorithmBase::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
const QString fieldName = parameterAsString( parameters, QStringLiteral( "FIELD_NAME" ), context );
QgsFields fields;
fields.append( QgsField( fieldName, QVariant::Double, QString(), 20, 8 ) );

QString dest;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, QgsWkbTypes::Polygon, mCrs ) );
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, sinkType(), mCrs ) );
if ( !sink )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );

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

double hCellSizeX = mRasterUnitsPerPixelX / 2.0;
double hCellSizeY = mRasterUnitsPerPixelY / 2.0;

int iterLeft = 0;
int iterTop = 0;
int iterCols = 0;
Expand All @@ -141,9 +113,7 @@ QVariantMap QgsVectorizeAlgorithm::processAlgorithm( const QVariantMap &paramete
{
if ( !rasterBlock->isNoData( row, column ) )
{

QgsGeometry pixelRectGeometry = QgsGeometry::fromRect( QgsRectangle( currentX - hCellSizeX, currentY - hCellSizeY, currentX + hCellSizeX, currentY + hCellSizeY ) );

QgsGeometry pixelRectGeometry = createGeometryForPixel( currentX, currentY, mRasterUnitsPerPixelX, mRasterUnitsPerPixelY );
double value = rasterBlock->value( row, column );

QgsFeature f;
Expand All @@ -162,6 +132,121 @@ QVariantMap QgsVectorizeAlgorithm::processAlgorithm( const QVariantMap &paramete
return outputs;
}

//
// QgsRasterPixelsToPolygonsAlgorithm
//

QString QgsRasterPixelsToPolygonsAlgorithm::name() const
{
return QStringLiteral( "pixelstopolygons" );
}

QString QgsRasterPixelsToPolygonsAlgorithm::displayName() const
{
return QObject::tr( "Raster pixels to polygons" );
}

QStringList QgsRasterPixelsToPolygonsAlgorithm::tags() const
{
return QObject::tr( "vectorize,polygonize,raster,convert,pixels" ).split( ',' );
}

QString QgsRasterPixelsToPolygonsAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm converts a raster layer to a vector layer, by creating polygon features "
"for each individual pixel's extent in the raster layer.\n\n"
"Any nodata pixels are skipped in the output." );
}

QString QgsRasterPixelsToPolygonsAlgorithm::shortDescription() const
{
return QObject::tr( "Creates a vector layer of polygons corresponding to each pixel in a raster layer." );
}

QgsRasterPixelsToPolygonsAlgorithm *QgsRasterPixelsToPolygonsAlgorithm::createInstance() const
{
return new QgsRasterPixelsToPolygonsAlgorithm();
}

QString QgsRasterPixelsToPolygonsAlgorithm::outputName() const
{
return QObject::tr( "Vector polygons" );
}

QgsProcessing::SourceType QgsRasterPixelsToPolygonsAlgorithm::outputType() const
{
return QgsProcessing::TypeVectorPolygon;
}

QgsWkbTypes::Type QgsRasterPixelsToPolygonsAlgorithm::sinkType() const
{
return QgsWkbTypes::Polygon;
}

QgsGeometry QgsRasterPixelsToPolygonsAlgorithm::createGeometryForPixel( double centerX, double centerY, double pixelWidthX, double pixelWidthY ) const
{
const double hCellSizeX = pixelWidthX / 2.0;
const double hCellSizeY = pixelWidthY / 2.0;
return QgsGeometry::fromRect( QgsRectangle( centerX - hCellSizeX, centerY - hCellSizeY, centerX + hCellSizeX, centerY + hCellSizeY ) );
}


//
// QgsRasterPixelsToPointsAlgorithm
//

QString QgsRasterPixelsToPointsAlgorithm::name() const
{
return QStringLiteral( "pixelstopoints" );
}

QString QgsRasterPixelsToPointsAlgorithm::displayName() const
{
return QObject::tr( "Raster pixels to points" );
}

QStringList QgsRasterPixelsToPointsAlgorithm::tags() const
{
return QObject::tr( "vectorize,polygonize,raster,convert,pixels,centers" ).split( ',' );
}

QString QgsRasterPixelsToPointsAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm converts a raster layer to a vector layer, by creating point features "
"for each individual pixel's center in the raster layer.\n\n"
"Any nodata pixels are skipped in the output." );
}

QString QgsRasterPixelsToPointsAlgorithm::shortDescription() const
{
return QObject::tr( "Creates a vector layer of points corresponding to each pixel in a raster layer." );
}

QgsRasterPixelsToPointsAlgorithm *QgsRasterPixelsToPointsAlgorithm::createInstance() const
{
return new QgsRasterPixelsToPointsAlgorithm();
}

QString QgsRasterPixelsToPointsAlgorithm::outputName() const
{
return QObject::tr( "Vector points" );
}

QgsProcessing::SourceType QgsRasterPixelsToPointsAlgorithm::outputType() const
{
return QgsProcessing::TypeVectorPoint;
}

QgsWkbTypes::Type QgsRasterPixelsToPointsAlgorithm::sinkType() const
{
return QgsWkbTypes::Point;
}

QgsGeometry QgsRasterPixelsToPointsAlgorithm::createGeometryForPixel( double centerX, double centerY, double, double ) const
{
return QgsGeometry( new QgsPoint( centerX, centerY ) );
}

///@endcond


0 comments on commit 526e590

Please sign in to comment.