1 /***************************************************************************
2                          qgspointcloudrgbrendererwidget.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 /***************************************************************************
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 "qgspointcloudrgbrendererwidget.h"
19 #include "qgscontrastenhancement.h"
20 #include "qgspointcloudlayer.h"
21 #include "qgspointcloudrgbrenderer.h"
22 #include "qgsdoublevalidator.h"
23 
24 ///@cond PRIVATE
25 
QgsPointCloudRgbRendererWidget(QgsPointCloudLayer * layer,QgsStyle * style)26 QgsPointCloudRgbRendererWidget::QgsPointCloudRgbRendererWidget( QgsPointCloudLayer *layer, QgsStyle *style )
27   : QgsPointCloudRendererWidget( layer, style )
28 {
29   setupUi( this );
30   connect( mRedMinLineEdit, &QLineEdit::textChanged, this, &QgsPointCloudRgbRendererWidget::mRedMinLineEdit_textChanged );
31   connect( mRedMaxLineEdit, &QLineEdit::textChanged, this, &QgsPointCloudRgbRendererWidget::mRedMaxLineEdit_textChanged );
32   connect( mGreenMinLineEdit, &QLineEdit::textChanged, this, &QgsPointCloudRgbRendererWidget::mGreenMinLineEdit_textChanged );
33   connect( mGreenMaxLineEdit, &QLineEdit::textChanged, this, &QgsPointCloudRgbRendererWidget::mGreenMaxLineEdit_textChanged );
34   connect( mBlueMinLineEdit, &QLineEdit::textChanged, this, &QgsPointCloudRgbRendererWidget::mBlueMinLineEdit_textChanged );
35   connect( mBlueMaxLineEdit, &QLineEdit::textChanged, this, &QgsPointCloudRgbRendererWidget::mBlueMaxLineEdit_textChanged );
36   createValidators();
37 
38   mRedAttributeComboBox->setAllowEmptyAttributeName( true );
39   mGreenAttributeComboBox->setAllowEmptyAttributeName( true );
40   mBlueAttributeComboBox->setAllowEmptyAttributeName( true );
41 
42   //contrast enhancement algorithms
43   mContrastEnhancementAlgorithmComboBox->addItem( tr( "No Enhancement" ), QgsContrastEnhancement::NoEnhancement );
44   mContrastEnhancementAlgorithmComboBox->addItem( tr( "Stretch to MinMax" ), QgsContrastEnhancement::StretchToMinimumMaximum );
45   mContrastEnhancementAlgorithmComboBox->addItem( tr( "Stretch and Clip to MinMax" ), QgsContrastEnhancement::StretchAndClipToMinimumMaximum );
46   mContrastEnhancementAlgorithmComboBox->addItem( tr( "Clip to MinMax" ), QgsContrastEnhancement::ClipToMinimumMaximum );
47 
48   if ( layer )
49   {
50     mRedAttributeComboBox->setLayer( layer );
51     mGreenAttributeComboBox->setLayer( layer );
52     mBlueAttributeComboBox->setLayer( layer );
53 
54     setFromRenderer( layer->renderer() );
55   }
56 
57   connect( mRedAttributeComboBox, &QgsPointCloudAttributeComboBox::attributeChanged,
58            this, &QgsPointCloudRgbRendererWidget::redAttributeChanged );
59   connect( mGreenAttributeComboBox, &QgsPointCloudAttributeComboBox::attributeChanged,
60            this, &QgsPointCloudRgbRendererWidget::greenAttributeChanged );
61   connect( mBlueAttributeComboBox, &QgsPointCloudAttributeComboBox::attributeChanged,
62            this, &QgsPointCloudRgbRendererWidget::blueAttributeChanged );
63   connect( mContrastEnhancementAlgorithmComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPointCloudRgbRendererWidget::emitWidgetChanged );
64 
65   if ( layer )
66   {
67     // set nice initial values
68     redAttributeChanged();
69     greenAttributeChanged();
70     blueAttributeChanged();
71   }
72 }
73 
create(QgsPointCloudLayer * layer,QgsStyle * style,QgsPointCloudRenderer *)74 QgsPointCloudRendererWidget *QgsPointCloudRgbRendererWidget::create( QgsPointCloudLayer *layer, QgsStyle *style, QgsPointCloudRenderer * )
75 {
76   return new QgsPointCloudRgbRendererWidget( layer, style );
77 }
78 
renderer()79 QgsPointCloudRenderer *QgsPointCloudRgbRendererWidget::renderer()
80 {
81   if ( !mLayer )
82   {
83     return nullptr;
84   }
85 
86   std::unique_ptr< QgsPointCloudRgbRenderer > renderer = std::make_unique< QgsPointCloudRgbRenderer >();
87   renderer->setRedAttribute( mRedAttributeComboBox->currentAttribute() );
88   renderer->setGreenAttribute( mGreenAttributeComboBox->currentAttribute() );
89   renderer->setBlueAttribute( mBlueAttributeComboBox->currentAttribute() );
90 
91   setCustomMinMaxValues( renderer.get() );
92   return renderer.release();
93 }
94 
createValidators()95 void QgsPointCloudRgbRendererWidget::createValidators()
96 {
97   mRedMinLineEdit->setValidator( new QgsDoubleValidator( mRedMinLineEdit ) );
98   mRedMaxLineEdit->setValidator( new QgsDoubleValidator( mRedMinLineEdit ) );
99   mGreenMinLineEdit->setValidator( new QgsDoubleValidator( mGreenMinLineEdit ) );
100   mGreenMaxLineEdit->setValidator( new QgsDoubleValidator( mGreenMinLineEdit ) );
101   mBlueMinLineEdit->setValidator( new QgsDoubleValidator( mBlueMinLineEdit ) );
102   mBlueMaxLineEdit->setValidator( new QgsDoubleValidator( mBlueMinLineEdit ) );
103 }
104 
setCustomMinMaxValues(QgsPointCloudRgbRenderer * r)105 void QgsPointCloudRgbRendererWidget::setCustomMinMaxValues( QgsPointCloudRgbRenderer *r )
106 {
107   if ( !r )
108   {
109     return;
110   }
111 
112   if ( mContrastEnhancementAlgorithmComboBox->currentData().toInt() ==
113        QgsContrastEnhancement::NoEnhancement )
114   {
115     r->setRedContrastEnhancement( nullptr );
116     r->setGreenContrastEnhancement( nullptr );
117     r->setBlueContrastEnhancement( nullptr );
118     return;
119   }
120 
121   QgsContrastEnhancement *redEnhancement = nullptr;
122   QgsContrastEnhancement *greenEnhancement = nullptr;
123   QgsContrastEnhancement *blueEnhancement = nullptr;
124 
125   bool redMinOk, redMaxOk;
126   const double redMin = QgsDoubleValidator::toDouble( mRedMinLineEdit->text(), &redMinOk );
127   const double redMax = QgsDoubleValidator::toDouble( mRedMaxLineEdit->text(), &redMaxOk );
128   if ( redMinOk && redMaxOk && !mRedAttributeComboBox->currentAttribute().isEmpty() )
129   {
130     redEnhancement = new QgsContrastEnhancement( Qgis::DataType::UnknownDataType );
131     redEnhancement->setMinimumValue( redMin );
132     redEnhancement->setMaximumValue( redMax );
133   }
134 
135   bool greenMinOk, greenMaxOk;
136   const double greenMin = QgsDoubleValidator::toDouble( mGreenMinLineEdit->text(), &greenMinOk );
137   const double greenMax = QgsDoubleValidator::toDouble( mGreenMaxLineEdit->text(), &greenMaxOk );
138   if ( greenMinOk && greenMaxOk && !mGreenAttributeComboBox->currentAttribute().isEmpty() )
139   {
140     greenEnhancement = new QgsContrastEnhancement( Qgis::DataType::UnknownDataType );
141     greenEnhancement->setMinimumValue( greenMin );
142     greenEnhancement->setMaximumValue( greenMax );
143   }
144 
145   bool blueMinOk, blueMaxOk;
146   const double blueMin = QgsDoubleValidator::toDouble( mBlueMinLineEdit->text(), &blueMinOk );
147   const double blueMax = QgsDoubleValidator::toDouble( mBlueMaxLineEdit->text(), &blueMaxOk );
148   if ( blueMinOk && blueMaxOk && !mBlueAttributeComboBox->currentAttribute().isEmpty() )
149   {
150     blueEnhancement = new QgsContrastEnhancement( Qgis::DataType::UnknownDataType );
151     blueEnhancement->setMinimumValue( blueMin );
152     blueEnhancement->setMaximumValue( blueMax );
153   }
154 
155   if ( redEnhancement )
156   {
157     redEnhancement->setContrastEnhancementAlgorithm( static_cast< QgsContrastEnhancement::ContrastEnhancementAlgorithm >(
158           ( mContrastEnhancementAlgorithmComboBox->currentData().toInt() ) ) );
159   }
160   if ( greenEnhancement )
161   {
162     greenEnhancement->setContrastEnhancementAlgorithm( static_cast< QgsContrastEnhancement::ContrastEnhancementAlgorithm >(
163           ( mContrastEnhancementAlgorithmComboBox->currentData().toInt() ) ) );
164   }
165   if ( blueEnhancement )
166   {
167     blueEnhancement->setContrastEnhancementAlgorithm( static_cast< QgsContrastEnhancement::ContrastEnhancementAlgorithm >(
168           ( mContrastEnhancementAlgorithmComboBox->currentData().toInt() ) ) );
169   }
170   r->setRedContrastEnhancement( redEnhancement );
171   r->setGreenContrastEnhancement( greenEnhancement );
172   r->setBlueContrastEnhancement( blueEnhancement );
173 }
174 
mRedMinLineEdit_textChanged(const QString &)175 void QgsPointCloudRgbRendererWidget::mRedMinLineEdit_textChanged( const QString & )
176 {
177   minMaxModified();
178 }
179 
mRedMaxLineEdit_textChanged(const QString &)180 void QgsPointCloudRgbRendererWidget::mRedMaxLineEdit_textChanged( const QString & )
181 {
182   minMaxModified();
183 }
184 
mGreenMinLineEdit_textChanged(const QString &)185 void QgsPointCloudRgbRendererWidget::mGreenMinLineEdit_textChanged( const QString & )
186 {
187   minMaxModified();
188 }
189 
mGreenMaxLineEdit_textChanged(const QString &)190 void QgsPointCloudRgbRendererWidget::mGreenMaxLineEdit_textChanged( const QString & )
191 {
192   minMaxModified();
193 }
194 
mBlueMinLineEdit_textChanged(const QString &)195 void QgsPointCloudRgbRendererWidget::mBlueMinLineEdit_textChanged( const QString & )
196 {
197   minMaxModified();
198 }
199 
mBlueMaxLineEdit_textChanged(const QString &)200 void QgsPointCloudRgbRendererWidget::mBlueMaxLineEdit_textChanged( const QString & )
201 {
202   minMaxModified();
203 }
204 
emitWidgetChanged()205 void QgsPointCloudRgbRendererWidget::emitWidgetChanged()
206 {
207   if ( !mBlockChangedSignal )
208     emit widgetChanged();
209 }
210 
redAttributeChanged()211 void QgsPointCloudRgbRendererWidget::redAttributeChanged()
212 {
213   if ( mLayer && mLayer->dataProvider() )
214   {
215     const QVariant max = mLayer->dataProvider()->metadataStatistic( mRedAttributeComboBox->currentAttribute(), QgsStatisticalSummary::Max );
216     if ( max.isValid() )
217     {
218       const int maxValue = max.toInt();
219       mDisableMinMaxWidgetRefresh++;
220       mRedMinLineEdit->setText( QLocale().toString( 0 ) );
221 
222       // 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
223       // likely to be 8 bit or 16 bit color values
224       mRedMaxLineEdit->setText( QLocale().toString( maxValue > 255 ? 65535 : 255 ) );
225       mDisableMinMaxWidgetRefresh--;
226       emitWidgetChanged();
227     }
228   }
229 }
230 
greenAttributeChanged()231 void QgsPointCloudRgbRendererWidget::greenAttributeChanged()
232 {
233   if ( mLayer && mLayer->dataProvider() )
234   {
235     const QVariant max = mLayer->dataProvider()->metadataStatistic( mGreenAttributeComboBox->currentAttribute(), QgsStatisticalSummary::Max );
236     if ( max.isValid() )
237     {
238       const int maxValue = max.toInt();
239       mDisableMinMaxWidgetRefresh++;
240       mGreenMinLineEdit->setText( QLocale().toString( 0 ) );
241 
242       // 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
243       // likely to be 8 bit or 16 bit color values
244       mGreenMaxLineEdit->setText( QLocale().toString( maxValue > 255 ? 65535 : 255 ) );
245       mDisableMinMaxWidgetRefresh--;
246       emitWidgetChanged();
247     }
248   }
249 }
250 
blueAttributeChanged()251 void QgsPointCloudRgbRendererWidget::blueAttributeChanged()
252 {
253   if ( mLayer && mLayer->dataProvider() )
254   {
255     const QVariant max = mLayer->dataProvider()->metadataStatistic( mBlueAttributeComboBox->currentAttribute(), QgsStatisticalSummary::Max );
256     if ( max.isValid() )
257     {
258       const int maxValue = max.toInt();
259       mDisableMinMaxWidgetRefresh++;
260       mBlueMinLineEdit->setText( QLocale().toString( 0 ) );
261 
262       // 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
263       // likely to be 8 bit or 16 bit color values
264       mBlueMaxLineEdit->setText( QLocale().toString( maxValue > 255 ? 65535 : 255 ) );
265       mDisableMinMaxWidgetRefresh--;
266       emitWidgetChanged();
267     }
268   }
269 }
270 
minMaxModified()271 void QgsPointCloudRgbRendererWidget::minMaxModified()
272 {
273   if ( !mDisableMinMaxWidgetRefresh )
274   {
275     if ( ( QgsContrastEnhancement::ContrastEnhancementAlgorithm )( mContrastEnhancementAlgorithmComboBox->currentData().toInt() ) == QgsContrastEnhancement::NoEnhancement )
276     {
277       mContrastEnhancementAlgorithmComboBox->setCurrentIndex(
278         mContrastEnhancementAlgorithmComboBox->findData( ( int ) QgsContrastEnhancement::StretchToMinimumMaximum ) );
279     }
280     emitWidgetChanged();
281   }
282 }
283 
setMinMaxValue(const QgsContrastEnhancement * ce,QLineEdit * minEdit,QLineEdit * maxEdit)284 void QgsPointCloudRgbRendererWidget::setMinMaxValue( const QgsContrastEnhancement *ce, QLineEdit *minEdit, QLineEdit *maxEdit )
285 {
286   if ( !minEdit || !maxEdit )
287   {
288     return;
289   }
290 
291   if ( !ce )
292   {
293     minEdit->clear();
294     maxEdit->clear();
295     return;
296   }
297 
298   minEdit->setText( QLocale().toString( ce->minimumValue() ) );
299   maxEdit->setText( QLocale().toString( ce->maximumValue() ) );
300 
301   // QgsMultiBandColorRenderer is using individual contrast enhancements for each
302   // band, but this widget GUI has one for all
303   mContrastEnhancementAlgorithmComboBox->setCurrentIndex( mContrastEnhancementAlgorithmComboBox->findData(
304         static_cast< int >( ce->contrastEnhancementAlgorithm() ) ) );
305 }
306 
setFromRenderer(const QgsPointCloudRenderer * r)307 void QgsPointCloudRgbRendererWidget::setFromRenderer( const QgsPointCloudRenderer *r )
308 {
309   mBlockChangedSignal = true;
310   const QgsPointCloudRgbRenderer *mbcr = dynamic_cast<const QgsPointCloudRgbRenderer *>( r );
311   if ( mbcr )
312   {
313     mRedAttributeComboBox->setAttribute( mbcr->redAttribute() );
314     mGreenAttributeComboBox->setAttribute( mbcr->greenAttribute() );
315     mBlueAttributeComboBox->setAttribute( mbcr->blueAttribute() );
316 
317     mDisableMinMaxWidgetRefresh++;
318     setMinMaxValue( mbcr->redContrastEnhancement(), mRedMinLineEdit, mRedMaxLineEdit );
319     setMinMaxValue( mbcr->greenContrastEnhancement(), mGreenMinLineEdit, mGreenMaxLineEdit );
320     setMinMaxValue( mbcr->blueContrastEnhancement(), mBlueMinLineEdit, mBlueMaxLineEdit );
321     mDisableMinMaxWidgetRefresh--;
322   }
323   else
324   {
325     if ( mRedAttributeComboBox->findText( QStringLiteral( "Red" ) ) > -1 && mRedAttributeComboBox->findText( QStringLiteral( "Green" ) ) > -1 &&
326          mRedAttributeComboBox->findText( QStringLiteral( "Blue" ) ) > -1 )
327     {
328       mRedAttributeComboBox->setAttribute( QStringLiteral( "Red" ) );
329       mGreenAttributeComboBox->setAttribute( QStringLiteral( "Green" ) );
330       mBlueAttributeComboBox->setAttribute( QStringLiteral( "Blue" ) );
331     }
332     else
333     {
334       mRedAttributeComboBox->setCurrentIndex( mRedAttributeComboBox->count() > 1 ? 1 : 0 );
335       mGreenAttributeComboBox->setCurrentIndex( mGreenAttributeComboBox->count() > 2 ? 2 : 0 );
336       mBlueAttributeComboBox->setCurrentIndex( mBlueAttributeComboBox->count() > 3 ? 3 : 0 );
337     }
338   }
339   mBlockChangedSignal = false;
340 }
341 
342 ///@endcond
343