Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgspointcloudclassifiedrenderer.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 "qgspointcloudclassifiedrenderer.h"
19 : : #include "qgspointcloudblock.h"
20 : : #include "qgsstyle.h"
21 : : #include "qgscolorramp.h"
22 : : #include "qgssymbollayerutils.h"
23 : : #include "qgslayertreemodellegendnode.h"
24 : : #include "qgspointclouddataprovider.h"
25 : :
26 : 0 : QgsPointCloudCategory::QgsPointCloudCategory( const int value, const QColor &color, const QString &label, bool render )
27 : 0 : : mValue( value )
28 : 0 : , mColor( color )
29 : 0 : , mLabel( label )
30 : 0 : , mRender( render )
31 : : {
32 : 0 : }
33 : :
34 : :
35 : : //
36 : : // QgsPointCloudClassifiedRenderer
37 : : //
38 : :
39 : 0 : QgsPointCloudClassifiedRenderer::QgsPointCloudClassifiedRenderer()
40 : 0 : : mCategories( defaultCategories() )
41 : 0 : {
42 : 0 : }
43 : :
44 : 0 : QString QgsPointCloudClassifiedRenderer::type() const
45 : : {
46 : 0 : return QStringLiteral( "classified" );
47 : : }
48 : :
49 : 0 : QgsPointCloudRenderer *QgsPointCloudClassifiedRenderer::clone() const
50 : : {
51 : 0 : std::unique_ptr< QgsPointCloudClassifiedRenderer > res = std::make_unique< QgsPointCloudClassifiedRenderer >();
52 : 0 : res->mAttribute = mAttribute;
53 : 0 : res->mCategories = mCategories;
54 : :
55 : 0 : copyCommonProperties( res.get() );
56 : :
57 : 0 : return res.release();
58 : 0 : }
59 : :
60 : 0 : void QgsPointCloudClassifiedRenderer::renderBlock( const QgsPointCloudBlock *block, QgsPointCloudRenderContext &context )
61 : : {
62 : 0 : const QgsRectangle visibleExtent = context.renderContext().extent();
63 : :
64 : 0 : const char *ptr = block->data();
65 : 0 : int count = block->pointCount();
66 : 0 : const QgsPointCloudAttributeCollection request = block->attributes();
67 : :
68 : 0 : const std::size_t recordSize = request.pointRecordSize();
69 : 0 : int attributeOffset = 0;
70 : 0 : const QgsPointCloudAttribute *attribute = request.find( mAttribute, attributeOffset );
71 : 0 : if ( !attribute )
72 : 0 : return;
73 : 0 : const QgsPointCloudAttribute::DataType attributeType = attribute->type();
74 : :
75 : 0 : const QgsDoubleRange zRange = context.renderContext().zRange();
76 : 0 : const bool considerZ = !zRange.isInfinite();
77 : :
78 : 0 : int rendered = 0;
79 : 0 : double x = 0;
80 : 0 : double y = 0;
81 : 0 : double z = 0;
82 : 0 : const QgsCoordinateTransform ct = context.renderContext().coordinateTransform();
83 : 0 : const bool reproject = ct.isValid();
84 : :
85 : 0 : QHash< int, QColor > colors;
86 : 0 : for ( const QgsPointCloudCategory &category : std::as_const( mCategories ) )
87 : : {
88 : 0 : if ( !category.renderState() )
89 : 0 : continue;
90 : :
91 : 0 : colors.insert( category.value(), category.color() );
92 : : }
93 : :
94 : 0 : for ( int i = 0; i < count; ++i )
95 : : {
96 : 0 : if ( context.renderContext().renderingStopped() )
97 : : {
98 : 0 : break;
99 : : }
100 : :
101 : 0 : if ( considerZ )
102 : : {
103 : : // z value filtering is cheapest, if we're doing it...
104 : 0 : z = pointZ( context, ptr, i );
105 : 0 : if ( !zRange.contains( z ) )
106 : 0 : continue;
107 : 0 : }
108 : :
109 : 0 : int attributeValue = 0;
110 : 0 : context.getAttribute( ptr, i * recordSize + attributeOffset, attributeType, attributeValue );
111 : 0 : const QColor color = colors.value( attributeValue );
112 : 0 : if ( !color.isValid() )
113 : 0 : continue;
114 : :
115 : 0 : pointXY( context, ptr, i, x, y );
116 : 0 : if ( visibleExtent.contains( x, y ) )
117 : : {
118 : 0 : if ( reproject )
119 : : {
120 : : try
121 : : {
122 : 0 : ct.transformInPlace( x, y, z );
123 : 0 : }
124 : : catch ( QgsCsException & )
125 : : {
126 : : continue;
127 : 0 : }
128 : 0 : }
129 : :
130 : 0 : drawPoint( x, y, color, context );
131 : 0 : rendered++;
132 : 0 : }
133 : 0 : }
134 : 0 : context.incrementPointsRendered( rendered );
135 : 0 : }
136 : :
137 : 0 : bool QgsPointCloudClassifiedRenderer::willRenderPoint( const QVariantMap &pointAttributes )
138 : : {
139 : 0 : if ( !pointAttributes.contains( mAttribute ) )
140 : 0 : return false;
141 : : bool parsedCorrectly;
142 : 0 : int attributeInt = pointAttributes[ mAttribute ].toInt( &parsedCorrectly );
143 : 0 : if ( !parsedCorrectly )
144 : 0 : return false;
145 : 0 : for ( const QgsPointCloudCategory &category : std::as_const( mCategories ) )
146 : : {
147 : 0 : if ( category.value() == attributeInt )
148 : 0 : return category.renderState();
149 : : }
150 : 0 : return false;
151 : 0 : }
152 : :
153 : 0 : QgsPointCloudRenderer *QgsPointCloudClassifiedRenderer::create( QDomElement &element, const QgsReadWriteContext &context )
154 : : {
155 : 0 : std::unique_ptr< QgsPointCloudClassifiedRenderer > r = std::make_unique< QgsPointCloudClassifiedRenderer >();
156 : :
157 : 0 : r->setAttribute( element.attribute( QStringLiteral( "attribute" ), QStringLiteral( "Classification" ) ) );
158 : :
159 : 0 : QgsPointCloudCategoryList categories;
160 : 0 : const QDomElement catsElem = element.firstChildElement( QStringLiteral( "categories" ) );
161 : 0 : if ( !catsElem.isNull() )
162 : : {
163 : 0 : QDomElement catElem = catsElem.firstChildElement();
164 : 0 : while ( !catElem.isNull() )
165 : : {
166 : 0 : if ( catElem.tagName() == QLatin1String( "category" ) )
167 : : {
168 : 0 : const int value = catElem.attribute( QStringLiteral( "value" ) ).toInt();
169 : 0 : const QString label = catElem.attribute( QStringLiteral( "label" ) );
170 : 0 : const bool render = catElem.attribute( QStringLiteral( "render" ) ) != QLatin1String( "false" );
171 : 0 : const QColor color = QgsSymbolLayerUtils::decodeColor( catElem.attribute( QStringLiteral( "color" ) ) );
172 : 0 : categories.append( QgsPointCloudCategory( value, color, label, render ) );
173 : 0 : }
174 : 0 : catElem = catElem.nextSiblingElement();
175 : : }
176 : 0 : r->setCategories( categories );
177 : 0 : }
178 : :
179 : 0 : r->restoreCommonProperties( element, context );
180 : :
181 : 0 : return r.release();
182 : 0 : }
183 : :
184 : 0 : QgsPointCloudCategoryList QgsPointCloudClassifiedRenderer::defaultCategories()
185 : : {
186 : 0 : return QgsPointCloudCategoryList() << QgsPointCloudCategory( 1, QColor( "#AAAAAA" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 1 ) )
187 : 0 : << QgsPointCloudCategory( 2, QColor( "#AA5500" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 2 ) )
188 : 0 : << QgsPointCloudCategory( 3, QColor( "#00AAAA" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 3 ) )
189 : 0 : << QgsPointCloudCategory( 4, QColor( "#55FF55" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 4 ) )
190 : 0 : << QgsPointCloudCategory( 5, QColor( "#00AA00" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 5 ) )
191 : 0 : << QgsPointCloudCategory( 6, QColor( "#FF5555" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 6 ) )
192 : 0 : << QgsPointCloudCategory( 7, QColor( "#AA0000" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 7 ) )
193 : 0 : << QgsPointCloudCategory( 8, QColor( "#555555" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 8 ) )
194 : 0 : << QgsPointCloudCategory( 9, QColor( "#55FFFF" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 9 ) )
195 : 0 : << QgsPointCloudCategory( 10, QColor( "#AA00AA" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 10 ) )
196 : 0 : << QgsPointCloudCategory( 11, QColor( "#000000" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 11 ) )
197 : 0 : << QgsPointCloudCategory( 12, QColor( "#555555" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 12 ) )
198 : 0 : << QgsPointCloudCategory( 13, QColor( "#FFFF55" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 13 ) )
199 : 0 : << QgsPointCloudCategory( 14, QColor( "#FFFF55" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 14 ) )
200 : 0 : << QgsPointCloudCategory( 15, QColor( "#FF55FF" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 15 ) )
201 : 0 : << QgsPointCloudCategory( 16, QColor( "#FFFF55" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 16 ) )
202 : 0 : << QgsPointCloudCategory( 17, QColor( "#5555FF" ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 17 ) )
203 : 0 : << QgsPointCloudCategory( 18, QColor( 100, 100, 100 ), QgsPointCloudDataProvider::translatedLasClassificationCodes().value( 18 ) );
204 : 0 : }
205 : :
206 : 0 : QDomElement QgsPointCloudClassifiedRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context ) const
207 : : {
208 : 0 : QDomElement rendererElem = doc.createElement( QStringLiteral( "renderer" ) );
209 : :
210 : 0 : rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "classified" ) );
211 : 0 : rendererElem.setAttribute( QStringLiteral( "attribute" ), mAttribute );
212 : :
213 : : // categories
214 : 0 : QDomElement catsElem = doc.createElement( QStringLiteral( "categories" ) );
215 : 0 : for ( const QgsPointCloudCategory &category : mCategories )
216 : : {
217 : 0 : QDomElement catElem = doc.createElement( QStringLiteral( "category" ) );
218 : 0 : catElem.setAttribute( QStringLiteral( "value" ), QString::number( category.value() ) );
219 : 0 : catElem.setAttribute( QStringLiteral( "label" ), category.label() );
220 : 0 : catElem.setAttribute( QStringLiteral( "color" ), QgsSymbolLayerUtils::encodeColor( category.color() ) );
221 : 0 : catElem.setAttribute( QStringLiteral( "render" ), category.renderState() ? "true" : "false" );
222 : 0 : catsElem.appendChild( catElem );
223 : 0 : }
224 : 0 : rendererElem.appendChild( catsElem );
225 : :
226 : 0 : saveCommonProperties( rendererElem, context );
227 : :
228 : 0 : return rendererElem;
229 : 0 : }
230 : :
231 : 0 : QSet<QString> QgsPointCloudClassifiedRenderer::usedAttributes( const QgsPointCloudRenderContext & ) const
232 : : {
233 : 0 : QSet<QString> res;
234 : 0 : res << mAttribute;
235 : 0 : return res;
236 : 0 : }
237 : :
238 : 0 : QList<QgsLayerTreeModelLegendNode *> QgsPointCloudClassifiedRenderer::createLegendNodes( QgsLayerTreeLayer *nodeLayer )
239 : : {
240 : 0 : QList<QgsLayerTreeModelLegendNode *> nodes;
241 : :
242 : 0 : for ( const QgsPointCloudCategory &category : std::as_const( mCategories ) )
243 : : {
244 : 0 : nodes << new QgsRasterSymbolLegendNode( nodeLayer, category.color(), category.label(), nullptr, true, QString::number( category.value() ) );
245 : : }
246 : :
247 : 0 : return nodes;
248 : 0 : }
249 : :
250 : 0 : QStringList QgsPointCloudClassifiedRenderer::legendRuleKeys() const
251 : : {
252 : 0 : QStringList res;
253 : 0 : for ( const QgsPointCloudCategory &category : std::as_const( mCategories ) )
254 : : {
255 : 0 : res << QString::number( category.value() );
256 : : }
257 : 0 : return res;
258 : 0 : }
259 : :
260 : 0 : bool QgsPointCloudClassifiedRenderer::legendItemChecked( const QString &key )
261 : : {
262 : 0 : bool ok = false;
263 : 0 : const int value = key.toInt( &ok );
264 : 0 : if ( !ok )
265 : 0 : return false;
266 : :
267 : 0 : for ( const QgsPointCloudCategory &category : std::as_const( mCategories ) )
268 : : {
269 : 0 : if ( category.value() == value )
270 : 0 : return category.renderState();
271 : : }
272 : 0 : return false;
273 : 0 : }
274 : :
275 : 0 : void QgsPointCloudClassifiedRenderer::checkLegendItem( const QString &key, bool state )
276 : : {
277 : 0 : bool ok = false;
278 : 0 : const int value = key.toInt( &ok );
279 : 0 : if ( !ok )
280 : 0 : return;
281 : :
282 : 0 : for ( auto it = mCategories.begin(); it != mCategories.end(); ++it )
283 : : {
284 : 0 : if ( it->value() == value )
285 : : {
286 : 0 : it->setRenderState( state );
287 : 0 : return;
288 : : }
289 : 0 : }
290 : 0 : }
291 : :
292 : 0 : QString QgsPointCloudClassifiedRenderer::attribute() const
293 : : {
294 : 0 : return mAttribute;
295 : : }
296 : :
297 : 0 : void QgsPointCloudClassifiedRenderer::setAttribute( const QString &attribute )
298 : : {
299 : 0 : mAttribute = attribute;
300 : 0 : }
301 : :
302 : 0 : QgsPointCloudCategoryList QgsPointCloudClassifiedRenderer::categories() const
303 : : {
304 : 0 : return mCategories;
305 : : }
306 : :
307 : 0 : void QgsPointCloudClassifiedRenderer::setCategories( const QgsPointCloudCategoryList &categories )
308 : : {
309 : 0 : mCategories = categories;
310 : 0 : }
311 : :
312 : 0 : void QgsPointCloudClassifiedRenderer::addCategory( const QgsPointCloudCategory &category )
313 : : {
314 : 0 : mCategories.append( category );
315 : 0 : }
316 : :
|