1 /***************************************************************************
2     qgspointcloudrendererpropertieswidget.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 "qgspointcloudrendererpropertieswidget.h"
16 
17 #include "qgis.h"
18 #include "qgspointcloudrendererregistry.h"
19 #include "qgsapplication.h"
20 #include "qgssymbolwidgetcontext.h"
21 #include "qgspointcloudrendererwidget.h"
22 #include "qgspointcloudlayer.h"
23 #include "qgspointcloudrenderer.h"
24 #include "qgspointcloudrgbrendererwidget.h"
25 #include "qgspointcloudattributebyramprendererwidget.h"
26 #include "qgspointcloudclassifiedrendererwidget.h"
27 #include "qgspointcloudextentrendererwidget.h"
28 
29 #include "qgspointcloudrgbrenderer.h"
30 #include "qgslogger.h"
31 
_initRenderer(const QString & name,QgsPointCloudRendererWidgetFunc f,const QString & iconName=QString ())32 static bool _initRenderer( const QString &name, QgsPointCloudRendererWidgetFunc f, const QString &iconName = QString() )
33 {
34   QgsPointCloudRendererAbstractMetadata *rendererAbstractMetadata = QgsApplication::pointCloudRendererRegistry()->rendererMetadata( name );
35   if ( !rendererAbstractMetadata )
36     return false;
37   QgsPointCloudRendererMetadata *rendererMetadata = dynamic_cast<QgsPointCloudRendererMetadata *>( rendererAbstractMetadata );
38   if ( !rendererMetadata )
39     return false;
40 
41   rendererMetadata->setWidgetFunction( f );
42 
43   if ( !iconName.isEmpty() )
44   {
45     rendererMetadata->setIcon( QgsApplication::getThemeIcon( iconName ) );
46   }
47 
48   QgsDebugMsgLevel( "Set for " + name, 2 );
49   return true;
50 }
51 
_initRendererWidgetFunctions()52 static void _initRendererWidgetFunctions()
53 {
54   static bool sInitialized = false;
55   if ( sInitialized )
56     return;
57 
58   _initRenderer( QStringLiteral( "extent" ), QgsPointCloudExtentRendererWidget::create, QStringLiteral( "styleicons/pointcloudextent.svg" ) );
59   _initRenderer( QStringLiteral( "rgb" ), QgsPointCloudRgbRendererWidget::create, QStringLiteral( "styleicons/multibandcolor.svg" ) );
60   _initRenderer( QStringLiteral( "ramp" ), QgsPointCloudAttributeByRampRendererWidget::create, QStringLiteral( "styleicons/singlebandpseudocolor.svg" ) );
61   _initRenderer( QStringLiteral( "classified" ), QgsPointCloudClassifiedRendererWidget::create, QStringLiteral( "styleicons/paletted.svg" ) );
62 
63   sInitialized = true;
64 }
65 
QgsPointCloudRendererPropertiesWidget(QgsPointCloudLayer * layer,QgsStyle * style,QWidget * parent)66 QgsPointCloudRendererPropertiesWidget::QgsPointCloudRendererPropertiesWidget( QgsPointCloudLayer *layer, QgsStyle *style, QWidget *parent )
67   : QgsMapLayerConfigWidget( layer, nullptr, parent )
68   , mLayer( layer )
69   , mStyle( style )
70 {
71   setupUi( this );
72 
73   layout()->setContentsMargins( 0, 0, 0, 0 );
74 
75   // initialize registry's widget functions
76   _initRendererWidgetFunctions();
77 
78   QgsPointCloudRendererRegistry *reg = QgsApplication::pointCloudRendererRegistry();
79   const QStringList renderers = reg->renderersList();
80   for ( const QString &name : renderers )
81   {
82     if ( QgsPointCloudRendererAbstractMetadata *m = reg->rendererMetadata( name ) )
83       cboRenderers->addItem( m->icon(), m->visibleName(), name );
84   }
85 
86   cboRenderers->setCurrentIndex( -1 ); // set no current renderer
87 
88   mPointStyleComboBox->addItem( tr( "Square" ), QgsPointCloudRenderer::Square );
89   mPointStyleComboBox->addItem( tr( "Circle" ), QgsPointCloudRenderer::Circle );
90 
91   connect( cboRenderers, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPointCloudRendererPropertiesWidget::rendererChanged );
92 
93   connect( mBlendModeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPointCloudRendererPropertiesWidget::emitWidgetChanged );
94   connect( mOpacityWidget, &QgsOpacityWidget::opacityChanged, this, &QgsPointCloudRendererPropertiesWidget::emitWidgetChanged );
95 
96   mPointSizeUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
97                                   << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
98 
99   connect( mPointSizeSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, &QgsPointCloudRendererPropertiesWidget::emitWidgetChanged );
100   connect( mPointSizeUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsPointCloudRendererPropertiesWidget::emitWidgetChanged );
101 
102   mMaxErrorUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
103                                  << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
104   mMaxErrorSpinBox->setClearValue( 0.3 );
105 
106   connect( mMaxErrorSpinBox, qOverload<double>( &QgsDoubleSpinBox::valueChanged ), this, &QgsPointCloudRendererPropertiesWidget::emitWidgetChanged );
107   connect( mMaxErrorUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsPointCloudRendererPropertiesWidget::emitWidgetChanged );
108 
109   connect( mPointStyleComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsPointCloudRendererPropertiesWidget::emitWidgetChanged );
110 
111   syncToLayer( layer );
112 }
113 
setContext(const QgsSymbolWidgetContext & context)114 void QgsPointCloudRendererPropertiesWidget::setContext( const QgsSymbolWidgetContext &context )
115 {
116   mMapCanvas = context.mapCanvas();
117   mMessageBar = context.messageBar();
118   if ( mActiveWidget )
119   {
120     mActiveWidget->setContext( context );
121   }
122 }
123 
syncToLayer(QgsMapLayer * layer)124 void QgsPointCloudRendererPropertiesWidget::syncToLayer( QgsMapLayer *layer )
125 {
126   mLayer = qobject_cast< QgsPointCloudLayer * >( layer );
127 
128   mBlockChangedSignal = true;
129   mOpacityWidget->setOpacity( mLayer->opacity() );
130   mBlendModeComboBox->setBlendMode( mLayer->blendMode() );
131 
132   if ( mLayer->renderer() )
133   {
134     // set current renderer from layer
135     const QString rendererName = mLayer->renderer()->type();
136 
137     const int rendererIdx = cboRenderers->findData( rendererName );
138     cboRenderers->setCurrentIndex( rendererIdx );
139 
140     // no renderer found... this mustn't happen
141     Q_ASSERT( rendererIdx != -1 && "there must be a renderer!" );
142 
143     mPointSizeSpinBox->setValue( mLayer->renderer()->pointSize() );
144     mPointSizeUnitWidget->setUnit( mLayer->renderer()->pointSizeUnit() );
145     mPointSizeUnitWidget->setMapUnitScale( mLayer->renderer()->pointSizeMapUnitScale() );
146 
147     mPointStyleComboBox->setCurrentIndex( mPointStyleComboBox->findData( mLayer->renderer()->pointSymbol() ) );
148 
149     mMaxErrorSpinBox->setValue( mLayer->renderer()->maximumScreenError() );
150     mMaxErrorUnitWidget->setUnit( mLayer->renderer()->maximumScreenErrorUnit() );
151   }
152 
153   mBlockChangedSignal = false;
154 }
155 
setDockMode(bool dockMode)156 void QgsPointCloudRendererPropertiesWidget::setDockMode( bool dockMode )
157 {
158   if ( mActiveWidget )
159     mActiveWidget->setDockMode( dockMode );
160   QgsMapLayerConfigWidget::setDockMode( dockMode );
161 }
162 
apply()163 void QgsPointCloudRendererPropertiesWidget::apply()
164 {
165   mLayer->setOpacity( mOpacityWidget->opacity() );
166   mLayer->setBlendMode( mBlendModeComboBox->blendMode() );
167 
168   if ( mActiveWidget )
169     mLayer->setRenderer( mActiveWidget->renderer() );
170   else if ( !cboRenderers->currentData().toString().isEmpty() )
171   {
172     QDomElement elem;
173     mLayer->setRenderer( QgsApplication::pointCloudRendererRegistry()->rendererMetadata( cboRenderers->currentData().toString() )->createRenderer( elem, QgsReadWriteContext() ) );
174   }
175 
176   mLayer->renderer()->setPointSize( mPointSizeSpinBox->value() );
177   mLayer->renderer()->setPointSizeUnit( mPointSizeUnitWidget->unit() );
178   mLayer->renderer()->setPointSizeMapUnitScale( mPointSizeUnitWidget->getMapUnitScale() );
179 
180   mLayer->renderer()->setPointSymbol( static_cast< QgsPointCloudRenderer::PointSymbol >( mPointStyleComboBox->currentData().toInt() ) );
181 
182   mLayer->renderer()->setMaximumScreenError( mMaxErrorSpinBox->value() );
183   mLayer->renderer()->setMaximumScreenErrorUnit( mMaxErrorUnitWidget->unit() );
184 }
185 
rendererChanged()186 void QgsPointCloudRendererPropertiesWidget::rendererChanged()
187 {
188   if ( cboRenderers->currentIndex() == -1 )
189   {
190     QgsDebugMsg( QStringLiteral( "No current item -- this should never happen!" ) );
191     return;
192   }
193 
194   const QString rendererName = cboRenderers->currentData().toString();
195 
196   //Retrieve the previous renderer: from the old active widget if possible, otherwise from the layer
197   std::unique_ptr< QgsPointCloudRenderer > oldRenderer;
198   std::unique_ptr< QgsPointCloudRenderer > newRenderer;
199   if ( mActiveWidget )
200     newRenderer.reset( mActiveWidget->renderer() );
201 
202   if ( newRenderer )
203   {
204     oldRenderer = std::move( newRenderer );
205   }
206   else
207   {
208     oldRenderer.reset( mLayer->renderer()->clone() );
209   }
210 
211   // get rid of old active widget (if any)
212   if ( mActiveWidget )
213   {
214     stackedWidget->removeWidget( mActiveWidget );
215 
216     delete mActiveWidget;
217     mActiveWidget = nullptr;
218   }
219 
220   QgsPointCloudRendererWidget *widget = nullptr;
221   QgsPointCloudRendererAbstractMetadata *rendererMetadata = QgsApplication::pointCloudRendererRegistry()->rendererMetadata( rendererName );
222   if ( rendererMetadata )
223     widget = rendererMetadata->createRendererWidget( mLayer, mStyle, oldRenderer.get() );
224   oldRenderer.reset();
225 
226   if ( widget )
227   {
228     // instantiate the widget and set as active
229     mActiveWidget = widget;
230     stackedWidget->addWidget( mActiveWidget );
231     stackedWidget->setCurrentWidget( mActiveWidget );
232 
233     if ( mMapCanvas || mMessageBar )
234     {
235       QgsSymbolWidgetContext context;
236       context.setMapCanvas( mMapCanvas );
237       context.setMessageBar( mMessageBar );
238       mActiveWidget->setContext( context );
239     }
240 
241     connect( mActiveWidget, &QgsPanelWidget::widgetChanged, this, &QgsPointCloudRendererPropertiesWidget::widgetChanged );
242     connect( mActiveWidget, &QgsPanelWidget::showPanel, this, &QgsPointCloudRendererPropertiesWidget::openPanel );
243     widget->setDockMode( dockMode() );
244   }
245   else
246   {
247     // set default "no edit widget available" page
248     stackedWidget->setCurrentWidget( pageNoWidget );
249   }
250   emitWidgetChanged();
251 }
252 
emitWidgetChanged()253 void QgsPointCloudRendererPropertiesWidget::emitWidgetChanged()
254 {
255   if ( !mBlockChangedSignal )
256     emit widgetChanged();
257 }
258 
259