QGIS API Documentation  3.21.0-Master (56b4176581)
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 "qgslinesymbollayer.h"
19 #include "qgsreadwritecontext.h"
20 #include "qgssymbollayerutils.h"
22 #include "qgslinesymbol.h"
23 
24 #include <gdal_alg.h>
25 
27  : QgsRasterRenderer( input, QStringLiteral( "contour" ) )
28 {
29  mContourSymbol.reset( static_cast<QgsLineSymbol *>( QgsLineSymbol::defaultSymbol( QgsWkbTypes::LineGeometry ) ) );
30 }
31 
33 
35 {
36  QgsRasterContourRenderer *renderer = new QgsRasterContourRenderer( nullptr );
37  renderer->copyCommonProperties( this );
38  renderer->mContourSymbol.reset( mContourSymbol ? mContourSymbol->clone() : nullptr );
39  renderer->mContourIndexSymbol.reset( mContourIndexSymbol ? mContourIndexSymbol->clone() : nullptr );
40  renderer->mContourInterval = mContourInterval;
41  renderer->mContourIndexInterval = mContourIndexInterval;
42  renderer->mInputBand = mInputBand;
43  renderer->mDownscale = mDownscale;
44  return renderer;
45 }
46 
48 {
49  if ( elem.isNull() )
50  {
51  return nullptr;
52  }
53 
55  r->readXml( elem );
56 
57  const int inputBand = elem.attribute( QStringLiteral( "band" ), QStringLiteral( "-1" ) ).toInt();
58  const double contourInterval = elem.attribute( QStringLiteral( "contour-interval" ), QStringLiteral( "100" ) ).toDouble();
59  const double contourIndexInterval = elem.attribute( QStringLiteral( "contour-index-interval" ), QStringLiteral( "0" ) ).toDouble();
60  const double downscale = elem.attribute( QStringLiteral( "downscale" ), QStringLiteral( "4" ) ).toDouble();
61 
62  r->setInputBand( inputBand );
65  r->setDownscale( downscale );
66 
67  QDomElement symbolsElem = elem.firstChildElement( QStringLiteral( "symbols" ) );
68  if ( !symbolsElem.isNull() )
69  {
71  if ( symbolMap.contains( QStringLiteral( "contour" ) ) )
72  {
73  QgsSymbol *symbol = symbolMap.take( QStringLiteral( "contour" ) );
74  if ( symbol->type() == Qgis::SymbolType::Line )
75  r->setContourSymbol( static_cast<QgsLineSymbol *>( symbol ) );
76  }
77  if ( symbolMap.contains( QStringLiteral( "index-contour" ) ) )
78  {
79  QgsSymbol *symbol = symbolMap.take( QStringLiteral( "index-contour" ) );
80  if ( symbol->type() == Qgis::SymbolType::Line )
81  r->setContourIndexSymbol( static_cast<QgsLineSymbol *>( symbol ) );
82  }
83  }
84  return r;
85 }
86 
87 void QgsRasterContourRenderer::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
88 {
89  if ( parentElem.isNull() )
90  {
91  return;
92  }
93 
94  QDomElement rasterRendererElem = doc.createElement( QStringLiteral( "rasterrenderer" ) );
95  _writeXml( doc, rasterRendererElem );
96 
97  rasterRendererElem.setAttribute( QStringLiteral( "band" ), mInputBand );
98  rasterRendererElem.setAttribute( QStringLiteral( "contour-interval" ), mContourInterval );
99  rasterRendererElem.setAttribute( QStringLiteral( "contour-index-interval" ), mContourIndexInterval );
100  rasterRendererElem.setAttribute( QStringLiteral( "downscale" ), mDownscale );
101 
102  QgsSymbolMap symbols;
103  symbols[QStringLiteral( "contour" )] = mContourSymbol.get();
104  if ( mContourIndexSymbol )
105  symbols[QStringLiteral( "index-contour" )] = mContourIndexSymbol.get();
106  const QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, QgsReadWriteContext() );
107  rasterRendererElem.appendChild( symbolsElem );
108 
109  parentElem.appendChild( rasterRendererElem );
110 }
111 
113 {
114  QPainter *painter;
115  double scaleX, scaleY;
120 };
121 
122 CPLErr _rasterContourWriter( double dfLevel, int nPoints, double *padfX, double *padfY, void *ptr )
123 {
124  Q_UNUSED( dfLevel )
125  ContourWriterData *crData = static_cast<ContourWriterData *>( ptr );
126  QPolygonF polygon( nPoints );
127  QPointF *d = polygon.data();
128  for ( int i = 0; i < nPoints; ++i )
129  {
130  d[i] = QPointF( padfX[i] * crData->scaleX, padfY[i] * crData->scaleY );
131  }
132 
133  if ( crData->indexSymbol && !qgsDoubleNear( crData->indexInterval, 0 ) && qgsDoubleNear( fmod( dfLevel, crData->indexInterval ), 0 ) )
134  crData->indexSymbol->renderPolyline( polygon, nullptr, *crData->context );
135  else
136  crData->symbol->renderPolyline( polygon, nullptr, *crData->context );
137  return CE_None;
138 }
139 
140 QgsRasterBlock *QgsRasterContourRenderer::block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback )
141 {
142  Q_UNUSED( bandNo )
143 
144  std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() );
145  if ( !mInput || !mContourSymbol )
146  {
147  return outputBlock.release();
148  }
149 
150  const int inputWidth = static_cast<int>( round( width / mDownscale ) );
151  const int inputHeight = static_cast<int>( round( height / mDownscale ) );
152 
153  std::unique_ptr< QgsRasterBlock > inputBlock( mInput->block( mInputBand, extent, inputWidth, inputHeight, feedback ) );
154  if ( !inputBlock || inputBlock->isEmpty() )
155  {
156  QgsDebugMsg( QStringLiteral( "No raster data!" ) );
157  return outputBlock.release();
158  }
159 
160  if ( !inputBlock->convert( Qgis::DataType::Float64 ) ) // contouring algorithm requires double
161  return outputBlock.release();
162  double *scanline = reinterpret_cast<double *>( inputBlock->bits() );
163 
164  QImage img( width, height, QImage::Format_ARGB32_Premultiplied );
165  img.fill( Qt::transparent );
166 
167  QPainter p( &img );
168  p.setRenderHint( QPainter::Antialiasing );
169 
171 
172  ContourWriterData crData;
173  crData.painter = &p;
174  crData.scaleX = width / double( inputWidth );
175  crData.scaleY = height / double( inputHeight );
176  crData.symbol = mContourSymbol.get();
177  crData.indexSymbol = mContourIndexSymbol.get();
178  crData.indexInterval = mContourIndexInterval;
179  crData.context = &context;
180 
181  crData.symbol->startRender( context );
182  if ( crData.indexSymbol )
183  crData.indexSymbol->startRender( context );
184 
185  const double contourBase = 0.;
186  GDALContourGeneratorH cg = GDAL_CG_Create( inputBlock->width(), inputBlock->height(),
187  inputBlock->hasNoDataValue(), inputBlock->noDataValue(),
188  mContourInterval, contourBase,
189  _rasterContourWriter, static_cast<void *>( &crData ) );
190  for ( int i = 0; i < inputHeight; ++i )
191  {
192  if ( feedback && feedback->isCanceled() )
193  break;
194 
195  GDAL_CG_FeedLine( cg, scanline );
196  scanline += inputWidth;
197  }
198  GDAL_CG_Destroy( cg );
199 
200  crData.symbol->stopRender( context );
201  if ( crData.indexSymbol )
202  crData.indexSymbol->stopRender( context );
203 
204  p.end();
205 
206  outputBlock->setImage( &img );
207  return outputBlock.release();
208 }
209 
211 {
212  QList<int> bandList;
213  if ( mInputBand != -1 )
214  {
215  bandList << mInputBand;
216  }
217  return bandList;
218 }
219 
220 QList<QgsLayerTreeModelLegendNode *> QgsRasterContourRenderer::createLegendNodes( QgsLayerTreeLayer *nodeLayer )
221 {
222  QList<QgsLayerTreeModelLegendNode *> nodes;
223 
224  const QgsLegendSymbolItem contourItem( mContourSymbol.get(), QString::number( mContourInterval ), QStringLiteral( "contour" ) );
225  nodes << new QgsSymbolLegendNode( nodeLayer, contourItem );
226 
227  if ( mContourIndexInterval > 0 )
228  {
229  const QgsLegendSymbolItem indexItem( mContourIndexSymbol.get(), QString::number( mContourIndexInterval ), QStringLiteral( "index" ) );
230  nodes << new QgsSymbolLegendNode( nodeLayer, indexItem );
231  }
232 
233  return nodes;
234 }
235 
237 {
238  mContourSymbol.reset( symbol );
239 }
240 
242 {
243  mContourIndexSymbol.reset( symbol );
244 }
@ Float64
Sixty four bit floating point (double)
@ Line
Line symbol.
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
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.
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...
void setInputBand(int band)
Sets the number of the input raster band.
int inputBand() const
Returns the number of the input raster band.
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.
virtual QgsRasterInterface * input() const
Current input.
QgsRasterInterface * mInput
virtual QgsRectangle extent() const
Gets the extent of the interface.
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)
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:38
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:516
static QgsSymbol * defaultSymbol(QgsWkbTypes::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
Definition: qgssymbol.cpp:355
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:97
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:489
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1133
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
CPLErr _rasterContourWriter(double dfLevel, int nPoints, double *padfX, double *padfY, void *ptr)
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition: qgsrenderer.h:44