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