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