Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgspointcloudattributebyramprenderer.h
3 : : --------------------
4 : : begin : October 2020
5 : : copyright : (C) 2020 by Nyall Dawson
6 : : email : nyall dot dawson at gmail dot com
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 : :
18 : : #include "qgspointcloudattributebyramprenderer.h"
19 : : #include "qgspointcloudblock.h"
20 : : #include "qgsstyle.h"
21 : : #include "qgscolorramp.h"
22 : : #include "qgssymbollayerutils.h"
23 : : #include "qgslayertreemodellegendnode.h"
24 : : #include "qgscolorramplegendnode.h"
25 : :
26 : 0 : QgsPointCloudAttributeByRampRenderer::QgsPointCloudAttributeByRampRenderer()
27 : 0 : {
28 : 0 : mColorRampShader.setSourceColorRamp( QgsStyle::defaultStyle()->colorRamp( QStringLiteral( "Viridis" ) ) );
29 : 0 : mColorRampShader.classifyColorRamp( 5, -1, QgsRectangle(), nullptr );
30 : 0 : }
31 : :
32 : 0 : QString QgsPointCloudAttributeByRampRenderer::type() const
33 : : {
34 : 0 : return QStringLiteral( "ramp" );
35 : : }
36 : :
37 : 0 : QgsPointCloudRenderer *QgsPointCloudAttributeByRampRenderer::clone() const
38 : : {
39 : 0 : std::unique_ptr< QgsPointCloudAttributeByRampRenderer > res = std::make_unique< QgsPointCloudAttributeByRampRenderer >();
40 : 0 : res->mAttribute = mAttribute;
41 : 0 : res->mColorRampShader = mColorRampShader;
42 : 0 : res->mMin = mMin;
43 : 0 : res->mMax = mMax;
44 : :
45 : 0 : copyCommonProperties( res.get() );
46 : :
47 : 0 : return res.release();
48 : 0 : }
49 : :
50 : 0 : void QgsPointCloudAttributeByRampRenderer::renderBlock( const QgsPointCloudBlock *block, QgsPointCloudRenderContext &context )
51 : : {
52 : 0 : const QgsRectangle visibleExtent = context.renderContext().extent();
53 : :
54 : 0 : const char *ptr = block->data();
55 : 0 : int count = block->pointCount();
56 : 0 : const QgsPointCloudAttributeCollection request = block->attributes();
57 : :
58 : 0 : const std::size_t recordSize = request.pointRecordSize();
59 : 0 : int attributeOffset = 0;
60 : 0 : const QgsPointCloudAttribute *attribute = request.find( mAttribute, attributeOffset );
61 : 0 : if ( !attribute )
62 : 0 : return;
63 : 0 : const QgsPointCloudAttribute::DataType attributeType = attribute->type();
64 : :
65 : 0 : const QgsDoubleRange zRange = context.renderContext().zRange();
66 : 0 : const bool considerZ = !zRange.isInfinite();
67 : :
68 : 0 : const bool applyZOffset = attribute->name() == QLatin1String( "Z" );
69 : 0 : const bool applyXOffset = attribute->name() == QLatin1String( "X" );
70 : 0 : const bool applyYOffset = attribute->name() == QLatin1String( "Y" );
71 : :
72 : 0 : int rendered = 0;
73 : 0 : double x = 0;
74 : 0 : double y = 0;
75 : 0 : double z = 0;
76 : 0 : const QgsCoordinateTransform ct = context.renderContext().coordinateTransform();
77 : 0 : const bool reproject = ct.isValid();
78 : :
79 : 0 : int red = 0;
80 : 0 : int green = 0;
81 : 0 : int blue = 0;
82 : 0 : int alpha = 0;
83 : 0 : for ( int i = 0; i < count; ++i )
84 : : {
85 : 0 : if ( context.renderContext().renderingStopped() )
86 : : {
87 : 0 : break;
88 : : }
89 : :
90 : 0 : if ( considerZ )
91 : : {
92 : : // z value filtering is cheapest, if we're doing it...
93 : 0 : z = pointZ( context, ptr, i );
94 : 0 : if ( !zRange.contains( z ) )
95 : 0 : continue;
96 : 0 : }
97 : :
98 : 0 : pointXY( context, ptr, i, x, y );
99 : 0 : if ( visibleExtent.contains( x, y ) )
100 : : {
101 : 0 : if ( reproject )
102 : : {
103 : : try
104 : : {
105 : 0 : ct.transformInPlace( x, y, z );
106 : 0 : }
107 : : catch ( QgsCsException & )
108 : : {
109 : : continue;
110 : 0 : }
111 : 0 : }
112 : :
113 : 0 : double attributeValue = 0;
114 : 0 : context.getAttribute( ptr, i * recordSize + attributeOffset, attributeType, attributeValue );
115 : 0 :
116 : 0 : if ( applyXOffset )
117 : 0 : attributeValue = context.offset().x() + context.scale().x() * attributeValue;
118 : 0 : if ( applyYOffset )
119 : 0 : attributeValue = context.offset().y() + context.scale().y() * attributeValue;
120 : 0 : if ( applyZOffset )
121 : 0 : attributeValue = ( context.offset().z() + context.scale().z() * attributeValue ) * context.zValueScale() + context.zValueFixedOffset();
122 : :
123 : 0 : mColorRampShader.shade( attributeValue, &red, &green, &blue, &alpha );
124 : 0 : drawPoint( x, y, QColor( red, green, blue, alpha ), context );
125 : :
126 : 0 : rendered++;
127 : 0 : }
128 : 0 : }
129 : 0 : context.incrementPointsRendered( rendered );
130 : 0 : }
131 : :
132 : :
133 : 0 : QgsPointCloudRenderer *QgsPointCloudAttributeByRampRenderer::create( QDomElement &element, const QgsReadWriteContext &context )
134 : : {
135 : 0 : std::unique_ptr< QgsPointCloudAttributeByRampRenderer > r = std::make_unique< QgsPointCloudAttributeByRampRenderer >();
136 : :
137 : 0 : r->setAttribute( element.attribute( QStringLiteral( "attribute" ), QStringLiteral( "Intensity" ) ) );
138 : :
139 : 0 : QDomElement elemShader = element.firstChildElement( QStringLiteral( "colorrampshader" ) );
140 : 0 : r->mColorRampShader.readXml( elemShader, context );
141 : :
142 : 0 : r->setMinimum( element.attribute( QStringLiteral( "min" ), QStringLiteral( "0" ) ).toDouble() );
143 : 0 : r->setMaximum( element.attribute( QStringLiteral( "max" ), QStringLiteral( "100" ) ).toDouble() );
144 : :
145 : 0 : r->restoreCommonProperties( element, context );
146 : :
147 : 0 : return r.release();
148 : 0 : }
149 : :
150 : 0 : QDomElement QgsPointCloudAttributeByRampRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context ) const
151 : : {
152 : 0 : QDomElement rendererElem = doc.createElement( QStringLiteral( "renderer" ) );
153 : :
154 : 0 : rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "ramp" ) );
155 : 0 : rendererElem.setAttribute( QStringLiteral( "min" ), mMin );
156 : 0 : rendererElem.setAttribute( QStringLiteral( "max" ), mMax );
157 : :
158 : 0 : rendererElem.setAttribute( QStringLiteral( "attribute" ), mAttribute );
159 : :
160 : 0 : QDomElement elemShader = mColorRampShader.writeXml( doc, context );
161 : 0 : rendererElem.appendChild( elemShader );
162 : :
163 : 0 : saveCommonProperties( rendererElem, context );
164 : :
165 : 0 : return rendererElem;
166 : 0 : }
167 : :
168 : 0 : QSet<QString> QgsPointCloudAttributeByRampRenderer::usedAttributes( const QgsPointCloudRenderContext & ) const
169 : : {
170 : 0 : QSet<QString> res;
171 : 0 : res << mAttribute;
172 : 0 : return res;
173 : 0 : }
174 : :
175 : 0 : QList<QgsLayerTreeModelLegendNode *> QgsPointCloudAttributeByRampRenderer::createLegendNodes( QgsLayerTreeLayer *nodeLayer )
176 : : {
177 : 0 : QList<QgsLayerTreeModelLegendNode *> res;
178 : 0 : res << new QgsSimpleLegendNode( nodeLayer, mAttribute );
179 : :
180 : 0 : switch ( mColorRampShader.colorRampType() )
181 : : {
182 : : case QgsColorRampShader::Interpolated:
183 : : // for interpolated shaders we use a ramp legend node unless the settings flag
184 : : // to use the continuous legend is not set, in that case we fall through
185 : 0 : if ( mColorRampShader.sourceColorRamp() && ( ! mColorRampShader.legendSettings() || mColorRampShader.legendSettings()->useContinuousLegend() ) )
186 : : {
187 : 0 : res << new QgsColorRampLegendNode( nodeLayer, mColorRampShader.sourceColorRamp()->clone(),
188 : 0 : mColorRampShader.legendSettings() ? *mColorRampShader.legendSettings() : QgsColorRampLegendNodeSettings(),
189 : 0 : mColorRampShader.minimumValue(),
190 : 0 : mColorRampShader.maximumValue() );
191 : 0 : break;
192 : : }
193 : : Q_FALLTHROUGH();
194 : : case QgsColorRampShader::Discrete:
195 : : case QgsColorRampShader::Exact:
196 : : {
197 : : // for all others we use itemised lists
198 : 0 : QList< QPair< QString, QColor > > items;
199 : 0 : mColorRampShader.legendSymbologyItems( items );
200 : 0 : res.reserve( items.size() );
201 : 0 : for ( const QPair< QString, QColor > &item : std::as_const( items ) )
202 : : {
203 : 0 : res << new QgsRasterSymbolLegendNode( nodeLayer, item.second, item.first );
204 : : }
205 : : break;
206 : 0 : }
207 : : }
208 : 0 : return res;
209 : 0 : }
210 : :
211 : 0 : QString QgsPointCloudAttributeByRampRenderer::attribute() const
212 : : {
213 : 0 : return mAttribute;
214 : : }
215 : :
216 : 0 : void QgsPointCloudAttributeByRampRenderer::setAttribute( const QString &attribute )
217 : : {
218 : 0 : mAttribute = attribute;
219 : 0 : }
220 : :
221 : 0 : QgsColorRampShader QgsPointCloudAttributeByRampRenderer::colorRampShader() const
222 : : {
223 : 0 : return mColorRampShader;
224 : : }
225 : :
226 : 0 : void QgsPointCloudAttributeByRampRenderer::setColorRampShader( const QgsColorRampShader &shader )
227 : : {
228 : 0 : mColorRampShader = shader;
229 : 0 : }
230 : :
231 : 0 : double QgsPointCloudAttributeByRampRenderer::minimum() const
232 : : {
233 : 0 : return mMin;
234 : : }
235 : :
236 : 0 : void QgsPointCloudAttributeByRampRenderer::setMinimum( double minimum )
237 : : {
238 : 0 : mMin = minimum;
239 : 0 : }
240 : :
241 : 0 : double QgsPointCloudAttributeByRampRenderer::maximum() const
242 : : {
243 : 0 : return mMax;
244 : : }
245 : :
246 : 0 : void QgsPointCloudAttributeByRampRenderer::setMaximum( double value )
247 : : {
248 : 0 : mMax = value;
249 : 0 : }
250 : :
|