Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgspointcloudrendererregistry.cpp 3 : : --------------------- 4 : : begin : November 2020 5 : : copyright : (C) 2020 by Nyall Dawson 6 : : email : nyall dot dawson 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 : : #include "qgspointcloudrendererregistry.h" 16 : : #include "qgspointcloudrenderer.h" 17 : : 18 : : // default renderers 19 : : #include "qgspointcloudattributebyramprenderer.h" 20 : : #include "qgspointcloudrgbrenderer.h" 21 : : #include "qgspointcloudclassifiedrenderer.h" 22 : : #include "qgspointcloudlayer.h" 23 : : #include "qgspointcloudextentrenderer.h" 24 : : 25 : 5 : QgsPointCloudRendererRegistry::QgsPointCloudRendererRegistry() 26 : : { 27 : : // add default renderers 28 : 10 : addRenderer( new QgsPointCloudRendererMetadata( QStringLiteral( "extent" ), 29 : 5 : QObject::tr( "Extent Only" ), 30 : : QgsPointCloudExtentRenderer::create ) ); 31 : 10 : addRenderer( new QgsPointCloudRendererMetadata( QStringLiteral( "ramp" ), 32 : 5 : QObject::tr( "Attribute by Ramp" ), 33 : : QgsPointCloudAttributeByRampRenderer::create ) ); 34 : 10 : addRenderer( new QgsPointCloudRendererMetadata( QStringLiteral( "rgb" ), 35 : 5 : QObject::tr( "RGB" ), 36 : : QgsPointCloudRgbRenderer::create ) ); 37 : 10 : addRenderer( new QgsPointCloudRendererMetadata( QStringLiteral( "classified" ), 38 : 5 : QObject::tr( "Classification" ), 39 : : QgsPointCloudClassifiedRenderer::create ) ); 40 : 5 : } 41 : : 42 : 5 : QgsPointCloudRendererRegistry::~QgsPointCloudRendererRegistry() 43 : : { 44 : 5 : qDeleteAll( mRenderers ); 45 : 5 : } 46 : : 47 : 20 : bool QgsPointCloudRendererRegistry::addRenderer( QgsPointCloudRendererAbstractMetadata *metadata ) 48 : : { 49 : 20 : if ( !metadata || mRenderers.contains( metadata->name() ) ) 50 : 0 : return false; 51 : : 52 : 20 : mRenderers[metadata->name()] = metadata; 53 : 20 : mRenderersOrder << metadata->name(); 54 : 20 : return true; 55 : 20 : } 56 : : 57 : 0 : bool QgsPointCloudRendererRegistry::removeRenderer( const QString &rendererName ) 58 : : { 59 : 0 : if ( !mRenderers.contains( rendererName ) ) 60 : 0 : return false; 61 : : 62 : 0 : delete mRenderers[rendererName]; 63 : 0 : mRenderers.remove( rendererName ); 64 : 0 : mRenderersOrder.removeAll( rendererName ); 65 : 0 : return true; 66 : 0 : } 67 : : 68 : 0 : QgsPointCloudRendererAbstractMetadata *QgsPointCloudRendererRegistry::rendererMetadata( const QString &rendererName ) 69 : : { 70 : 0 : return mRenderers.value( rendererName ); 71 : : } 72 : : 73 : 0 : QStringList QgsPointCloudRendererRegistry::renderersList() const 74 : : { 75 : 0 : QStringList renderers; 76 : 0 : for ( const QString &renderer : mRenderersOrder ) 77 : : { 78 : 0 : QgsPointCloudRendererAbstractMetadata *r = mRenderers.value( renderer ); 79 : 0 : if ( r ) 80 : 0 : renderers << renderer; 81 : : } 82 : 0 : return renderers; 83 : 0 : } 84 : : 85 : 0 : QgsPointCloudRenderer *QgsPointCloudRendererRegistry::defaultRenderer( const QgsPointCloudDataProvider *provider ) 86 : : { 87 : 0 : if ( !provider ) 88 : 0 : return new QgsPointCloudAttributeByRampRenderer(); 89 : : 90 : 0 : if ( ( provider->name() == QLatin1String( "pdal" ) ) && ( !provider->hasValidIndex() ) ) 91 : : { 92 : : // for now, default to extent renderer only for las/laz files 93 : 0 : return new QgsPointCloudExtentRenderer(); 94 : : } 95 : : 96 : 0 : const QgsPointCloudAttributeCollection attributes = provider->attributes(); 97 : : 98 : : //if red/green/blue attributes are present, then default to a RGB renderer 99 : 0 : if ( attributes.indexOf( QLatin1String( "Red" ) ) >= 0 && attributes.indexOf( QLatin1String( "Green" ) ) >= 0 && attributes.indexOf( QLatin1String( "Blue" ) ) >= 0 ) 100 : : { 101 : 0 : std::unique_ptr< QgsPointCloudRgbRenderer > renderer = std::make_unique< QgsPointCloudRgbRenderer >(); 102 : : 103 : : // set initial guess for rgb ranges 104 : 0 : const QVariant redMax = provider->metadataStatistic( QStringLiteral( "Red" ), QgsStatisticalSummary::Max ); 105 : 0 : const QVariant greenMax = provider->metadataStatistic( QStringLiteral( "Red" ), QgsStatisticalSummary::Max ); 106 : 0 : const QVariant blueMax = provider->metadataStatistic( QStringLiteral( "Red" ), QgsStatisticalSummary::Max ); 107 : 0 : if ( redMax.isValid() && greenMax.isValid() && blueMax.isValid() ) 108 : : { 109 : 0 : const int maxValue = std::max( blueMax.toInt(), std::max( redMax.toInt(), greenMax.toInt() ) ); 110 : : 111 : 0 : if ( maxValue == 0 ) 112 : : { 113 : : // r/g/b max value is 0 -- likely these attributes have been created by some process, but are empty. 114 : : // in any case they won't result in a useful render, so don't use RGB renderer for this dataset. 115 : 0 : renderer.reset(); 116 : 0 : } 117 : : else 118 : : { 119 : : // try and guess suitable range from input max values -- we don't just take the provider max value directly here, but rather see if it's 120 : : // likely to be 8 bit or 16 bit color values 121 : 0 : const int rangeGuess = maxValue > 255 ? 65535 : 255; 122 : : 123 : 0 : if ( rangeGuess > 255 ) 124 : : { 125 : : // looks like 16 bit colors, so default to a stretch contrast enhancement 126 : 0 : QgsContrastEnhancement contrast( Qgis::UnknownDataType ); 127 : 0 : contrast.setMinimumValue( 0 ); 128 : 0 : contrast.setMaximumValue( rangeGuess ); 129 : 0 : contrast.setContrastEnhancementAlgorithm( QgsContrastEnhancement::StretchToMinimumMaximum ); 130 : 0 : renderer->setRedContrastEnhancement( new QgsContrastEnhancement( contrast ) ); 131 : 0 : renderer->setGreenContrastEnhancement( new QgsContrastEnhancement( contrast ) ); 132 : 0 : renderer->setBlueContrastEnhancement( new QgsContrastEnhancement( contrast ) ); 133 : 0 : } 134 : : } 135 : 0 : } 136 : : 137 : 0 : if ( renderer ) 138 : 0 : return renderer.release(); 139 : 0 : } 140 : : 141 : : // otherwise try a classified renderer... 142 : 0 : if ( attributes.indexOf( QLatin1String( "Classification" ) ) >= 0 ) 143 : : { 144 : : // are any classifications present? 145 : 0 : QVariantList classes = provider->metadataClasses( QStringLiteral( "Classification" ) ); 146 : : // ignore "not classified" classes, and see if any are left... 147 : 0 : classes.removeAll( 0 ); 148 : 0 : classes.removeAll( 1 ); 149 : 0 : if ( !classes.empty() ) 150 : : { 151 : 0 : std::unique_ptr< QgsPointCloudClassifiedRenderer > renderer = std::make_unique< QgsPointCloudClassifiedRenderer >(); 152 : 0 : renderer->setAttribute( QStringLiteral( "Classification" ) ); 153 : 0 : return renderer.release(); 154 : 0 : } 155 : 0 : } 156 : : 157 : : // fallback to shading by Z 158 : 0 : std::unique_ptr< QgsPointCloudAttributeByRampRenderer > renderer = std::make_unique< QgsPointCloudAttributeByRampRenderer >(); 159 : 0 : renderer->setAttribute( QStringLiteral( "Z" ) ); 160 : : 161 : : // set initial range for z values if possible 162 : 0 : const QVariant zMin = provider->metadataStatistic( QStringLiteral( "Z" ), QgsStatisticalSummary::Min ); 163 : 0 : const QVariant zMax = provider->metadataStatistic( QStringLiteral( "Z" ), QgsStatisticalSummary::Max ); 164 : 0 : if ( zMin.isValid() && zMax.isValid() ) 165 : : { 166 : 0 : renderer->setMinimum( zMin.toDouble() ); 167 : 0 : renderer->setMaximum( zMax.toDouble() ); 168 : : 169 : 0 : QgsColorRampShader shader = renderer->colorRampShader(); 170 : 0 : shader.setMinimumValue( zMin.toDouble() ); 171 : 0 : shader.setMaximumValue( zMax.toDouble() ); 172 : 0 : shader.classifyColorRamp( 5, -1, QgsRectangle(), nullptr ); 173 : 0 : renderer->setColorRampShader( shader ); 174 : 0 : } 175 : 0 : return renderer.release(); 176 : 0 : } 177 : :