QGIS API Documentation  2.99.0-Master (7fe5405)
qgspalettedrasterrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspalettedrasterrenderer.cpp
3  -----------------------------
4  begin : December 2011
5  copyright : (C) 2011 by Marco Hugentobler
6  email : marco at sourcepole dot ch
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 
19 #include "qgsrastertransparency.h"
20 #include "qgsrasterviewport.h"
21 
22 #include <QColor>
23 #include <QDomDocument>
24 #include <QDomElement>
25 #include <QImage>
26 #include <QVector>
27 
29  QColor* colorArray, int nColors, const QVector<QString>& labels ):
30  QgsRasterRenderer( input, QStringLiteral( "paletted" ) ), mBand( bandNumber ), mNColors( nColors ), mLabels( labels )
31 {
32  mColors = new QRgb[nColors];
33  for ( int i = 0; i < nColors; ++i )
34  {
35  mColors[i] = qPremultiply( colorArray[i].rgba() );
36  }
37  delete[] colorArray;
38 }
39 
40 QgsPalettedRasterRenderer::QgsPalettedRasterRenderer( QgsRasterInterface* input, int bandNumber, QRgb* colorArray, int nColors, const QVector<QString>& labels ):
41  QgsRasterRenderer( input, QStringLiteral( "paletted" ) ), mBand( bandNumber ), mColors( colorArray ), mNColors( nColors ), mLabels( labels )
42 {
43 }
44 
46 {
47  delete[] mColors;
48 }
49 
51 {
52  QgsPalettedRasterRenderer * renderer = new QgsPalettedRasterRenderer( nullptr, mBand, rgbArray(), mNColors );
53  renderer->copyCommonProperties( this );
54 
55  renderer->mLabels = mLabels;
56  return renderer;
57 }
58 
60 {
61  if ( elem.isNull() )
62  {
63  return nullptr;
64  }
65 
66  int bandNumber = elem.attribute( QStringLiteral( "band" ), QStringLiteral( "-1" ) ).toInt();
67  int nColors = 0;
68  QRgb* colors = nullptr;
69  QVector<QString> labels;
70 
71  QDomElement paletteElem = elem.firstChildElement( QStringLiteral( "colorPalette" ) );
72  if ( !paletteElem.isNull() )
73  {
74  QDomNodeList paletteEntries = paletteElem.elementsByTagName( QStringLiteral( "paletteEntry" ) );
75 
76  QDomElement entryElem;
77  int value;
78  nColors = 0;
79 
80  // We cannot believe that data are correct, check first max value
81  for ( int i = 0; i < paletteEntries.size(); ++i )
82  {
83  entryElem = paletteEntries.at( i ).toElement();
84  // Could be written as doubles (with .0000) in old project files
85  value = ( int )entryElem.attribute( QStringLiteral( "value" ), QStringLiteral( "0" ) ).toDouble();
86  if ( value >= nColors && value <= 10000 ) nColors = value + 1;
87  }
88  QgsDebugMsgLevel( QString( "nColors = %1" ).arg( nColors ), 4 );
89 
90  colors = new QRgb[ nColors ];
91 
92  for ( int i = 0; i < nColors; ++i )
93  {
94  entryElem = paletteEntries.at( i ).toElement();
95  value = ( int )entryElem.attribute( QStringLiteral( "value" ), QStringLiteral( "0" ) ).toDouble();
96  QgsDebugMsgLevel( entryElem.attribute( "color", "#000000" ), 4 );
97  if ( value >= 0 && value < nColors )
98  {
99  QColor color = QColor( entryElem.attribute( QStringLiteral( "color" ), QStringLiteral( "#000000" ) ) );
100  color.setAlpha( entryElem.attribute( QStringLiteral( "alpha" ), QStringLiteral( "255" ) ).toInt() );
101  colors[value] = qPremultiply( color.rgba() );
102  QString label = entryElem.attribute( QStringLiteral( "label" ) );
103  if ( !label.isEmpty() )
104  {
105  if ( value >= labels.size() ) labels.resize( value + 1 );
106  labels[value] = label;
107  }
108  }
109  else
110  {
111  QgsDebugMsg( QString( "value %1 out of range" ).arg( value ) );
112  }
113  }
114  }
115  QgsPalettedRasterRenderer* r = new QgsPalettedRasterRenderer( input, bandNumber, colors, nColors, labels );
116  r->readXml( elem );
117  return r;
118 }
119 
121 {
122  if ( mNColors < 1 )
123  {
124  return nullptr;
125  }
126  QColor* colorArray = new QColor[ mNColors ];
127  for ( int i = 0; i < mNColors; ++i )
128  {
129  colorArray[i] = QColor::fromRgba( qUnpremultiply( mColors[i] ) );
130  }
131  return colorArray;
132 }
133 
134 QRgb* QgsPalettedRasterRenderer::rgbArray() const
135 {
136  if ( mNColors < 1 )
137  {
138  return nullptr;
139  }
140  QRgb* rgbValues = new QRgb[mNColors];
141  for ( int i = 0; i < mNColors; ++i )
142  {
143  rgbValues[i] = mColors[i];
144  }
145  return rgbValues;
146 }
147 
148 void QgsPalettedRasterRenderer::setLabel( int idx, const QString& label )
149 {
150  if ( idx >= mLabels.size() )
151  {
152  mLabels.resize( idx + 1 );
153  }
154  mLabels[idx] = label;
155 }
156 
157 QgsRasterBlock * QgsPalettedRasterRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback* feedback )
158 {
159  QgsRasterBlock *outputBlock = new QgsRasterBlock();
160  if ( !mInput || mNColors == 0 )
161  {
162  return outputBlock;
163  }
164 
165  QgsRasterBlock *inputBlock = mInput->block( bandNo, extent, width, height, feedback );
166 
167  if ( !inputBlock || inputBlock->isEmpty() )
168  {
169  QgsDebugMsg( "No raster data!" );
170  delete inputBlock;
171  return outputBlock;
172  }
173 
174  double currentOpacity = mOpacity;
175 
176  //rendering is faster without considering user-defined transparency
177  bool hasTransparency = usesTransparency();
178 
179  QgsRasterBlock *alphaBlock = nullptr;
180 
181  if ( mAlphaBand > 0 && mAlphaBand != mBand )
182  {
183  alphaBlock = mInput->block( mAlphaBand, extent, width, height, feedback );
184  if ( !alphaBlock || alphaBlock->isEmpty() )
185  {
186  delete inputBlock;
187  delete alphaBlock;
188  return outputBlock;
189  }
190  }
191  else if ( mAlphaBand == mBand )
192  {
193  alphaBlock = inputBlock;
194  }
195 
196  if ( !outputBlock->reset( Qgis::ARGB32_Premultiplied, width, height ) )
197  {
198  delete inputBlock;
199  delete alphaBlock;
200  return outputBlock;
201  }
202 
203  QRgb myDefaultColor = NODATA_COLOR;
204 
205  //use direct data access instead of QgsRasterBlock::setValue
206  //because of performance
207  unsigned int* outputData = ( unsigned int* )( outputBlock->bits() );
208 
209  qgssize rasterSize = ( qgssize )width * height;
210  for ( qgssize i = 0; i < rasterSize; ++i )
211  {
212  if ( inputBlock->isNoData( i ) )
213  {
214  outputData[i] = myDefaultColor;
215  continue;
216  }
217  int val = ( int ) inputBlock->value( i );
218 
219  if ( !hasTransparency )
220  {
221  outputData[i] = mColors[val];
222  }
223  else
224  {
225  currentOpacity = mOpacity;
226  if ( mRasterTransparency )
227  {
228  currentOpacity = mRasterTransparency->alphaValue( val, mOpacity * 255 ) / 255.0;
229  }
230  if ( mAlphaBand > 0 )
231  {
232  currentOpacity *= alphaBlock->value( i ) / 255.0;
233  }
234 
235  QRgb c = mColors[val];
236  outputData[i] = qRgba( currentOpacity * qRed( c ), currentOpacity * qGreen( c ), currentOpacity * qBlue( c ), currentOpacity * qAlpha( c ) );
237  }
238  }
239 
240  delete inputBlock;
241  if ( mAlphaBand > 0 && mBand != mAlphaBand )
242  {
243  delete alphaBlock;
244  }
245 
246  return outputBlock;
247 }
248 
249 void QgsPalettedRasterRenderer::writeXml( QDomDocument& doc, QDomElement& parentElem ) const
250 {
251  if ( parentElem.isNull() )
252  {
253  return;
254  }
255 
256  QDomElement rasterRendererElem = doc.createElement( QStringLiteral( "rasterrenderer" ) );
257  _writeXml( doc, rasterRendererElem );
258 
259  rasterRendererElem.setAttribute( QStringLiteral( "band" ), mBand );
260  QDomElement colorPaletteElem = doc.createElement( QStringLiteral( "colorPalette" ) );
261  for ( int i = 0; i < mNColors; ++i )
262  {
263  QColor color = QColor::fromRgba( qUnpremultiply( mColors[i] ) );
264  QDomElement colorElem = doc.createElement( QStringLiteral( "paletteEntry" ) );
265  colorElem.setAttribute( QStringLiteral( "value" ), i );
266  colorElem.setAttribute( QStringLiteral( "color" ), color.name() );
267  colorElem.setAttribute( QStringLiteral( "alpha" ), color.alpha() );
268  if ( !label( i ).isEmpty() )
269  {
270  colorElem.setAttribute( QStringLiteral( "label" ), label( i ) );
271  }
272  colorPaletteElem.appendChild( colorElem );
273  }
274  rasterRendererElem.appendChild( colorPaletteElem );
275 
276  parentElem.appendChild( rasterRendererElem );
277 }
278 
279 void QgsPalettedRasterRenderer::legendSymbologyItems( QList< QPair< QString, QColor > >& symbolItems ) const
280 {
281  for ( int i = 0; i < mNColors; ++i )
282  {
283  QString lab = label( i ).isEmpty() ? QString::number( i ) : label( i );
284  symbolItems.push_back( qMakePair( lab, QColor::fromRgba( qUnpremultiply( mColors[i] ) ) ) );
285  }
286 }
287 
289 {
290  QList<int> bandList;
291  if ( mBand != -1 )
292  {
293  bandList << mBand;
294  }
295  return bandList;
296 }
A rectangle specified with double values.
Definition: qgsrectangle.h:36
Renderer for paletted raster images.
virtual QgsRectangle extent() const
Get the extent of the interface.
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
int alphaValue(double, int theGlobalTransparency=255) const
Returns the transparency value for a single value Pixel.
virtual QgsRasterInterface * input() const
Current input.
void setLabel(int idx, const QString &label)
Set category label.
void writeXml(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
bool isNoData(int row, int column)
Check if value at position is no data.
int nColors() const
Returns number of colors.
QgsRasterTransparency * mRasterTransparency
Raster transparency per color or value. Overwrites global alpha value.
QgsPalettedRasterRenderer(QgsRasterInterface *input, int bandNumber, QColor *colorArray, int nColors, const QVector< QString > &labels=QVector< QString >())
Renderer owns color array.
static const QRgb NODATA_COLOR
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:76
Raster data container.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:37
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.
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.
bool usesTransparency() const
bool isEmpty() const
Returns true if block is empty, i.e.
QString label(int idx) const
Return optional category label.
int mAlphaBand
Read alpha value from band.
void readXml(const QDomElement &rendererElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
Base class for processing filters like renderers, reprojector, resampler etc.
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
Definition: qgis.h:333
static QgsRasterRenderer * create(const QDomElement &elem, QgsRasterInterface *input)
QList< int > usesBands() const override
Returns a list of band numbers used by the renderer.
bool reset(Qgis::DataType theDataType, int theWidth, int theHeight)
Reset block.
QColor * colors() const
Returns copy of color array (caller takes ownership)
QgsPalettedRasterRenderer * clone() const override
Clone itself, create deep copy.
char * bits(int row, int column)
Get pointer to data.
void legendSymbologyItems(QList< QPair< QString, QColor > > &symbolItems) const override
Get symbology items if provided by renderer.
double value(int row, int column) const
Read a single value if type of block is numeric.
double mOpacity
Global alpha value (0-1)
QgsRasterInterface * mInput
Feedback object tailored for raster block reading.
Raster renderer pipe that applies colors to a raster.
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.