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