QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsrastercontourrenderer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrastercontourrenderer.cpp
3 --------------------------------------
4 Date : March 2020
5 Copyright : (C) 2020 by Martin Dobias
6 Email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgsreadwritecontext.h"
19#include "qgssymbollayerutils.h"
21#include "qgslinesymbol.h"
22
23#include <gdal_alg.h>
24
26 : QgsRasterRenderer( input, QStringLiteral( "contour" ) )
27{
28 mContourSymbol.reset( static_cast<QgsLineSymbol *>( QgsLineSymbol::defaultSymbol( Qgis::GeometryType::Line ) ) );
29}
30
32
34{
35 QgsRasterContourRenderer *renderer = new QgsRasterContourRenderer( nullptr );
36 renderer->copyCommonProperties( this );
37 renderer->mContourSymbol.reset( mContourSymbol ? mContourSymbol->clone() : nullptr );
38 renderer->mContourIndexSymbol.reset( mContourIndexSymbol ? mContourIndexSymbol->clone() : nullptr );
39 renderer->mContourInterval = mContourInterval;
40 renderer->mContourIndexInterval = mContourIndexInterval;
41 renderer->mInputBand = mInputBand;
42 renderer->mDownscale = mDownscale;
43 return renderer;
44}
45
47{
48 if ( elem.isNull() )
49 {
50 return nullptr;
51 }
52
54 r->readXml( elem );
55
56 const int inputBand = elem.attribute( QStringLiteral( "band" ), QStringLiteral( "-1" ) ).toInt();
57 const double contourInterval = elem.attribute( QStringLiteral( "contour-interval" ), QStringLiteral( "100" ) ).toDouble();
58 const double contourIndexInterval = elem.attribute( QStringLiteral( "contour-index-interval" ), QStringLiteral( "0" ) ).toDouble();
59 const double downscale = elem.attribute( QStringLiteral( "downscale" ), QStringLiteral( "4" ) ).toDouble();
60
65
66 QDomElement symbolsElem = elem.firstChildElement( QStringLiteral( "symbols" ) );
67 if ( !symbolsElem.isNull() )
68 {
70 if ( symbolMap.contains( QStringLiteral( "contour" ) ) )
71 {
72 QgsSymbol *symbol = symbolMap.take( QStringLiteral( "contour" ) );
73 if ( symbol->type() == Qgis::SymbolType::Line )
74 r->setContourSymbol( static_cast<QgsLineSymbol *>( symbol ) );
75 }
76 if ( symbolMap.contains( QStringLiteral( "index-contour" ) ) )
77 {
78 QgsSymbol *symbol = symbolMap.take( QStringLiteral( "index-contour" ) );
79 if ( symbol->type() == Qgis::SymbolType::Line )
80 r->setContourIndexSymbol( static_cast<QgsLineSymbol *>( symbol ) );
81 }
82 }
83 return r;
84}
85
86void QgsRasterContourRenderer::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
87{
88 if ( parentElem.isNull() )
89 {
90 return;
91 }
92
93 QDomElement rasterRendererElem = doc.createElement( QStringLiteral( "rasterrenderer" ) );
94 _writeXml( doc, rasterRendererElem );
95
96 rasterRendererElem.setAttribute( QStringLiteral( "band" ), mInputBand );
97 rasterRendererElem.setAttribute( QStringLiteral( "contour-interval" ), mContourInterval );
98 rasterRendererElem.setAttribute( QStringLiteral( "contour-index-interval" ), mContourIndexInterval );
99 rasterRendererElem.setAttribute( QStringLiteral( "downscale" ), mDownscale );
100
101 QgsSymbolMap symbols;
102 symbols[QStringLiteral( "contour" )] = mContourSymbol.get();
103 if ( mContourIndexSymbol )
104 symbols[QStringLiteral( "index-contour" )] = mContourIndexSymbol.get();
105 const QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, QgsReadWriteContext() );
106 rasterRendererElem.appendChild( symbolsElem );
107
108 parentElem.appendChild( rasterRendererElem );
109}
110
112{
113 QPainter *painter;
114 double scaleX, scaleY;
119};
120
121CPLErr _rasterContourWriter( double dfLevel, int nPoints, double *padfX, double *padfY, void *ptr )
122{
123 Q_UNUSED( dfLevel )
124 ContourWriterData *crData = static_cast<ContourWriterData *>( ptr );
125 QPolygonF polygon( nPoints );
126 QPointF *d = polygon.data();
127 for ( int i = 0; i < nPoints; ++i )
128 {
129 d[i] = QPointF( padfX[i] * crData->scaleX, padfY[i] * crData->scaleY );
130 }
131
132 if ( crData->indexSymbol && !qgsDoubleNear( crData->indexInterval, 0 ) && qgsDoubleNear( fmod( dfLevel, crData->indexInterval ), 0 ) )
133 crData->indexSymbol->renderPolyline( polygon, nullptr, *crData->context );
134 else
135 crData->symbol->renderPolyline( polygon, nullptr, *crData->context );
136 return CE_None;
137}
138
139QgsRasterBlock *QgsRasterContourRenderer::block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback )
140{
141 Q_UNUSED( bandNo )
142
143 std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() );
144 if ( !mInput || !mContourSymbol )
145 {
146 return outputBlock.release();
147 }
148
149 const int inputWidth = static_cast<int>( round( width / mDownscale ) );
150 const int inputHeight = static_cast<int>( round( height / mDownscale ) );
151
152 std::unique_ptr< QgsRasterBlock > inputBlock( mInput->block( mInputBand, extent, inputWidth, inputHeight, feedback ) );
153 if ( !inputBlock || inputBlock->isEmpty() )
154 {
155 QgsDebugError( QStringLiteral( "No raster data!" ) );
156 return outputBlock.release();
157 }
158
159 if ( !inputBlock->convert( Qgis::DataType::Float64 ) ) // contouring algorithm requires double
160 return outputBlock.release();
161 double *scanline = reinterpret_cast<double *>( inputBlock->bits() );
162
163 QImage img( width, height, QImage::Format_ARGB32_Premultiplied );
164 img.fill( Qt::transparent );
165
166 QPainter p( &img );
167 p.setRenderHint( QPainter::Antialiasing );
168
170
171 ContourWriterData crData;
172 crData.painter = &p;
173 crData.scaleX = width / double( inputWidth );
174 crData.scaleY = height / double( inputHeight );
175 crData.symbol = mContourSymbol.get();
176 crData.indexSymbol = mContourIndexSymbol.get();
177 crData.indexInterval = mContourIndexInterval;
178 crData.context = &context;
179
180 crData.symbol->startRender( context );
181 if ( crData.indexSymbol )
182 crData.indexSymbol->startRender( context );
183
184 const double contourBase = 0.;
185 GDALContourGeneratorH cg = GDAL_CG_Create( inputBlock->width(), inputBlock->height(),
186 inputBlock->hasNoDataValue(), inputBlock->noDataValue(),
187 mContourInterval, contourBase,
188 _rasterContourWriter, static_cast<void *>( &crData ) );
189 for ( int i = 0; i < inputHeight; ++i )
190 {
191 if ( feedback && feedback->isCanceled() )
192 break;
193
194 GDAL_CG_FeedLine( cg, scanline );
195 scanline += inputWidth;
196 }
197 GDAL_CG_Destroy( cg );
198
199 crData.symbol->stopRender( context );
200 if ( crData.indexSymbol )
201 crData.indexSymbol->stopRender( context );
202
203 p.end();
204
205 outputBlock->setImage( &img );
206 return outputBlock.release();
207}
208
210{
211 QList<int> bandList;
212 if ( mInputBand != -1 )
213 {
214 bandList << mInputBand;
215 }
216 return bandList;
217}
218
219QList<QgsLayerTreeModelLegendNode *> QgsRasterContourRenderer::createLegendNodes( QgsLayerTreeLayer *nodeLayer )
220{
221 QList<QgsLayerTreeModelLegendNode *> nodes;
222
223 const QgsLegendSymbolItem contourItem( mContourSymbol.get(), QString::number( mContourInterval ), QStringLiteral( "contour" ) );
224 nodes << new QgsSymbolLegendNode( nodeLayer, contourItem );
225
226 if ( mContourIndexInterval > 0 )
227 {
228 const QgsLegendSymbolItem indexItem( mContourIndexSymbol.get(), QString::number( mContourIndexInterval ), QStringLiteral( "index" ) );
229 nodes << new QgsSymbolLegendNode( nodeLayer, indexItem );
230 }
231
232 return nodes;
233}
234
236{
237 return mInputBand;
238}
239
241{
242 if ( !mInput )
243 {
244 mInputBand = band;
245 return true;
246 }
247 else if ( band > 0 && band <= mInput->bandCount() )
248 {
249 mInputBand = band;
250 return true;
251 }
252 return false;
253}
254
256{
257 mContourSymbol.reset( symbol );
258}
259
261{
262 mContourIndexSymbol.reset( symbol );
263}
@ Float64
Sixty four bit floating point (double)
@ Line
Line symbol.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:53
Layer tree node points to a map layer.
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:30
void renderPolyline(const QPolygonF &points, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Renders the symbol along the line joining points, using the given render context.
Feedback object tailored for raster block reading.
Raster data container.
Raster renderer that generates contours on the fly for a source raster band.
int inputBand() const override
Returns the input band for the renderer, or -1 if no input band is available.
static QgsRasterRenderer * create(const QDomElement &elem, QgsRasterInterface *input)
Creates an instance of the renderer based on definition from XML (used by renderer registry)
QgsRasterContourRenderer * clone() const override
Clone itself, create deep copy.
void setDownscale(double scale)
Sets by how much the renderer will scale down the request to the data provider.
QgsRasterContourRenderer(QgsRasterInterface *input)
Creates a contour renderer.
void setContourIndexSymbol(QgsLineSymbol *symbol)
Sets the symbol of index contour lines.
void setContourInterval(double interval)
Sets the interval of contour lines generation.
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
void writeXml(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
QList< QgsLayerTreeModelLegendNode * > createLegendNodes(QgsLayerTreeLayer *nodeLayer) override
Creates a set of legend nodes representing the renderer.
~QgsRasterContourRenderer() override
double downscale() const
Returns by how much the renderer will scale down the request to the data provider.
void setContourIndexInterval(double interval)
Sets the interval of index contour lines (index contour lines are typical further apart and with a wi...
bool setInputBand(int band) override
Attempts to set the input band for the renderer.
double contourInterval() const
Returns the interval of contour lines generation.
double contourIndexInterval() const
Returns the interval of index contour lines (index contour lines are typical further apart and with a...
void setContourSymbol(QgsLineSymbol *symbol)
Sets the symbol used for contour lines. Takes ownership of the passed symbol.
QList< int > usesBands() const override
Returns a list of band numbers used by the renderer.
Base class for processing filters like renderers, reprojector, resampler etc.
virtual QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr)=0
Read block of data using given extent and size.
QgsRasterInterface * mInput
virtual QgsRectangle extent() const
Gets the extent of the interface.
virtual QgsRasterInterface * input() const
Current input.
Raster renderer pipe that applies colors to a raster.
void _writeXml(QDomDocument &doc, QDomElement &rasterRendererElem) const
Write upper class info into rasterrenderer element (called by writeXml method of subclasses)
int bandCount() const override
Gets number of bands.
void copyCommonProperties(const QgsRasterRenderer *other, bool copyMinMaxOrigin=true)
Copies common properties like opacity / transparency data from other renderer.
void readXml(const QDomElement &rendererElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
Contains information about the context of a rendering operation.
static QgsRenderContext fromQPainter(QPainter *painter)
Creates a default render context given a pixel based QPainter destination.
static QgsSymbolMap loadSymbols(QDomElement &element, const QgsReadWriteContext &context)
Reads a collection of symbols from XML and returns them in a map. Caller is responsible for deleting ...
static QDomElement saveSymbols(QgsSymbolMap &symbols, const QString &tagName, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a collection of symbols to XML with specified tagName for the top-level element.
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:94
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:877
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:156
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:829
static QgsSymbol * defaultSymbol(Qgis::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
Definition: qgssymbol.cpp:705
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5207
#define QgsDebugError(str)
Definition: qgslogger.h:38
CPLErr _rasterContourWriter(double dfLevel, int nPoints, double *padfX, double *padfY, void *ptr)
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition: qgsrenderer.h:45