QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgselevationprofilelayertreeview.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgselevationprofilelayertreeview.cpp
3 -----------------
4 begin : April 2022
5 copyright : (C) 2022 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7***************************************************************************/
8
9
10/***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
20#include "qgslayertreenode.h"
21#include "qgslayertree.h"
22#include "qgssymbollayerutils.h"
26#include "qgsvectorlayer.h"
28#include "qgsmarkersymbol.h"
29#include "qgsfillsymbol.h"
30#include "qgsmaplayerutils.h"
31
32#include <QHeaderView>
33#include <QContextMenuEvent>
34#include <QMenu>
35#include <QMimeData>
36
37
39 : QgsLayerTreeModel( rootNode, parent )
40{
45}
46
47QVariant QgsElevationProfileLayerTreeModel::data( const QModelIndex &index, int role ) const
48{
49 switch ( role )
50 {
51 case Qt::DecorationRole:
52 {
54
55 if ( node && node->nodeType() == QgsLayerTreeNode::NodeLayer )
56 {
57 if ( QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer() )
58 {
59 std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
60
61 const int iconSize = scaleIconSize( 16 );
62 std::unique_ptr< QgsSymbol > symbol;
63 switch ( layer->type() )
64 {
65 case Qgis::LayerType::Vector:
66 {
67 QgsVectorLayerElevationProperties *elevationProperties = qgis::down_cast< QgsVectorLayerElevationProperties * >( layer->elevationProperties() );
68 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
69
70 switch ( elevationProperties->type() )
71 {
73 if ( ( vLayer->geometryType() == Qgis::GeometryType::Point && !elevationProperties->extrusionEnabled() )
74 || ( vLayer->geometryType() == Qgis::GeometryType::Line && !elevationProperties->extrusionEnabled() )
75 )
76 {
77 if ( QgsMarkerSymbol *markerSymbol = elevationProperties->profileMarkerSymbol() )
78 {
79 symbol.reset( markerSymbol->clone() );
80 }
81 }
82
83 if ( !symbol && vLayer->geometryType() == Qgis::GeometryType::Polygon && elevationProperties->extrusionEnabled() )
84 {
85 if ( QgsFillSymbol *fillSymbol = elevationProperties->profileFillSymbol() )
86 {
87 symbol.reset( fillSymbol->clone() );
88 }
89 }
90
91 if ( !symbol )
92 {
93 if ( QgsLineSymbol *lineSymbol = elevationProperties->profileLineSymbol() )
94 {
95 symbol.reset( lineSymbol->clone() );
96 }
97 }
98
99 if ( elevationProperties->respectLayerSymbology() )
100 {
101 if ( QgsSingleSymbolRenderer *renderer = dynamic_cast< QgsSingleSymbolRenderer * >( qobject_cast< QgsVectorLayer * >( layer )->renderer() ) )
102 {
103 if ( renderer->symbol()->type() == symbol->type() )
104 {
105 // take the whole renderer symbol if we can
106 symbol.reset( renderer->symbol()->clone() );
107 }
108 else
109 {
110 // otherwise emulate what happens when rendering the actual chart and just copy the color and opacity
111 symbol->setColor( renderer->symbol()->color() );
112 symbol->setOpacity( renderer->symbol()->opacity() );
113 }
114 }
115 else
116 {
117 // just use default layer icon
118 return QgsLayerTreeModel::data( index, role );
119 }
120 }
121 break;
122
124 switch ( elevationProperties->profileSymbology() )
125 {
127 if ( QgsLineSymbol *lineSymbol = elevationProperties->profileLineSymbol() )
128 {
129 symbol.reset( lineSymbol->clone() );
130 }
131 break;
133 if ( QgsFillSymbol *fillSymbol = elevationProperties->profileFillSymbol() )
134 {
135 symbol.reset( fillSymbol->clone() );
136 }
137 break;
138 }
139 break;
140
141 }
142 break;
143 }
144
145 case Qgis::LayerType::Raster:
146 {
147 QgsRasterLayerElevationProperties *rlProps = qgis::down_cast< QgsRasterLayerElevationProperties * >( layer->elevationProperties() );
148 switch ( rlProps->profileSymbology() )
149 {
151 if ( QgsLineSymbol *lineSymbol = rlProps->profileLineSymbol() )
152 {
153 symbol.reset( lineSymbol->clone() );
154 }
155 break;
157 if ( QgsFillSymbol *fillSymbol = rlProps->profileFillSymbol() )
158 {
159 symbol.reset( fillSymbol->clone() );
160 }
161 break;
162 }
163 break;
164 }
165
166 case Qgis::LayerType::Mesh:
167 {
168 QgsMeshLayerElevationProperties *mlProps = qgis::down_cast< QgsMeshLayerElevationProperties * >( layer->elevationProperties() );
169 switch ( mlProps->profileSymbology() )
170 {
172 if ( QgsLineSymbol *lineSymbol = mlProps->profileLineSymbol() )
173 {
174 symbol.reset( lineSymbol->clone() );
175 }
176 break;
178 if ( QgsFillSymbol *fillSymbol = mlProps->profileFillSymbol() )
179 {
180 symbol.reset( fillSymbol->clone() );
181 }
182 break;
183 }
184 break;
185 }
186
187 case Qgis::LayerType::Plugin:
188 case Qgis::LayerType::VectorTile:
189 case Qgis::LayerType::Annotation:
190 case Qgis::LayerType::PointCloud:
191 case Qgis::LayerType::Group:
192 break;
193 }
194 if ( !symbol )
195 break;
196
197 const QPixmap pix = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( iconSize, iconSize ), 0, context.get() );
198 return QIcon( pix );
199 }
200 }
201 break;
202 }
203
204 case Qt::ToolTipRole:
205 {
206 // override default tooltip with elevation specific one
208 if ( node && node->nodeType() == QgsLayerTreeNode::NodeLayer )
209 {
210 if ( QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer() )
211 {
212 QString title =
213 !layer->title().isEmpty() ? layer->title() :
214 !layer->shortName().isEmpty() ? layer->shortName() :
215 layer->name();
216
217 title = "<b>" + title.toHtmlEscaped() + "</b>";
218
219 QStringList parts;
220 parts << title;
221
222 const QString elevationPropertiesSummary = layer->elevationProperties() ? layer->elevationProperties()->htmlSummary() : QString();
223 if ( !elevationPropertiesSummary.isEmpty( ) )
224 parts << elevationPropertiesSummary;
225
226 return parts.join( QLatin1String( "<br/>" ) );
227 }
228 }
229 break;
230 }
231
232 default:
233 break;
234 }
235 return QgsLayerTreeModel::data( index, role );
236}
237
238bool QgsElevationProfileLayerTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
239{
240 if ( action == Qt::IgnoreAction )
241 return true;
242
243 if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
244 return false;
245
246 // don't accept drags from other layer trees -- only allow internal drag
247 const QString source = data->data( QStringLiteral( "application/qgis.layertree.source" ) );
248 if ( source.isEmpty() || source != QStringLiteral( ":0x%1" ).arg( reinterpret_cast<quintptr>( this ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ) )
249 return false;
250
251 return QgsLayerTreeModel::dropMimeData( data, action, row, column, parent );
252}
253
254QMimeData *QgsElevationProfileLayerTreeModel::mimeData( const QModelIndexList &indexes ) const
255{
256 QMimeData *mimeData = QgsLayerTreeModel::mimeData( indexes );
257 if ( mimeData )
258 {
259 mimeData->setData( QStringLiteral( "application/qgis.restrictlayertreemodelsubclass" ), "QgsElevationProfileLayerTreeModel" );
260 }
261 return mimeData;
262}
263
264
265//
266// QgsElevationProfileLayerTreeProxyModel
267//
268
270 : QSortFilterProxyModel( parent )
271 , mModel( model )
272{
273 setSourceModel( mModel );
274 setDynamicSortFilter( true );
275}
276
277bool QgsElevationProfileLayerTreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
278{
279 const QModelIndex sourceIndex = mModel->index( sourceRow, 0, sourceParent );
280 if ( QgsLayerTreeNode *node = mModel->index2node( sourceIndex ) )
281 {
282 switch ( node->nodeType() )
283 {
285 {
286 if ( QgsLayerTreeLayer *layerTreeLayer = QgsLayerTree::toLayer( node ) )
287 {
288 if ( QgsMapLayer *layer = layerTreeLayer->layer() )
289 {
290 // hide layers which don't have elevation
291 if ( !layer->elevationProperties() || !layer->elevationProperties()->hasElevation() )
292 return false;
293 }
294 }
295 break;
296 }
297
299 break;
300 }
301 return true;
302 }
303 else if ( QgsLayerTreeModelLegendNode *legendNode = mModel->index2legendNode( sourceIndex ) )
304 {
305 // we only show legend nodes for vector layers where the profile symbol is set to follow the layer's symbology
306 // (and the layer's renderer isn't a single symbol renderer)
307 if ( QgsLayerTreeLayer *layerTreeLayer = QgsLayerTree::toLayer( legendNode->layerNode() ) )
308 {
309 if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( layerTreeLayer->layer() ) )
310 {
311 if ( !qgis::down_cast< QgsVectorLayerElevationProperties * >( layer->elevationProperties() )->respectLayerSymbology() )
312 {
313 return false;
314 }
315 else if ( dynamic_cast< QgsSingleSymbolRenderer * >( qobject_cast< QgsVectorLayer * >( layer )->renderer() ) )
316 {
317 return false;
318 }
319 else
320 {
321 return true;
322 }
323 }
324 }
325 return false;
326 }
327 else
328 {
329 return false;
330 }
331}
332
333
335 : QTreeView( parent )
336 , mLayerTree( rootNode )
337{
338 mModel = new QgsElevationProfileLayerTreeModel( rootNode, this );
339 mProxyModel = new QgsElevationProfileLayerTreeProxyModel( mModel, this );
340
341 setHeaderHidden( true );
342
343 setDragEnabled( true );
344 setAcceptDrops( true );
345 setDropIndicatorShown( true );
346 setExpandsOnDoubleClick( false );
347
348 // Ensure legend graphics are scrollable
349 header()->setStretchLastSection( false );
350 header()->setSectionResizeMode( QHeaderView::ResizeToContents );
351
352 // If vertically scrolling by item, legend graphics can get clipped
353 setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
354
355 setDefaultDropAction( Qt::MoveAction );
356
357 setModel( mProxyModel );
358}
359
361{
362 if ( QgsLayerTreeNode *node = mModel->index2node( mProxyModel->mapToSource( index ) ) )
363 {
364 if ( QgsLayerTreeLayer *layerTreeLayerNode = mLayerTree->toLayer( node ) )
365 {
366 return layerTreeLayerNode->layer();
367 }
368 }
369 return nullptr;
370}
371
373{
374 const QList< QgsMapLayer * > layers = project->layers< QgsMapLayer * >().toList();
375
376 // sort layers so that types which are more likely to obscure others are rendered below
377 // e.g. vector features should be drawn above raster DEMS, or the DEM line may completely obscure
378 // the vector feature
379 QList< QgsMapLayer * > sortedLayers = QgsMapLayerUtils::sortLayersByType( layers,
380 {
381 Qgis::LayerType::Raster,
382 Qgis::LayerType::Mesh,
383 Qgis::LayerType::Vector,
384 Qgis::LayerType::PointCloud
385 } );
386
387 std::reverse( sortedLayers.begin(), sortedLayers.end() );
388 for ( QgsMapLayer *layer : std::as_const( sortedLayers ) )
389 {
390 QgsLayerTreeLayer *node = mLayerTree->addLayer( layer );
391 node->setItemVisibilityChecked( layer->elevationProperties() && layer->elevationProperties()->showByDefaultInElevationProfilePlots() );
392 }
393}
394
396{
397 header()->setMinimumSectionSize( viewport()->width() );
398 QTreeView::resizeEvent( event );
399}
@ ContinuousSurface
The features should be treated as representing values on a continuous surface (eg contour lines)
@ IndividualFeatures
Treat each feature as an individual object (eg buildings)
@ Line
The elevation surface will be rendered using a line symbol.
@ FillBelow
The elevation surface will be rendered using a fill symbol below the surface level.
A layer tree model subclass for elevation profiles.
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
QgsElevationProfileLayerTreeModel(QgsLayerTree *rootNode, QObject *parent=nullptr)
Construct a new tree model with given layer tree (root node must not be nullptr).
QMimeData * mimeData(const QModelIndexList &indexes) const override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
QgsElevationProfileLayerTreeProxyModel(QgsElevationProfileLayerTreeModel *model, QObject *parent=nullptr)
Constructor for QgsElevationProfileLayerTreeProxyModel.
QgsMapLayer * indexToLayer(const QModelIndex &index)
Converts a view index to a map layer.
QgsElevationProfileLayerTreeView(QgsLayerTree *rootNode, QWidget *parent=nullptr)
Construct a new tree view with given layer tree (root node must not be nullptr).
void resizeEvent(QResizeEvent *event) override
void populateInitialLayers(QgsProject *project)
Initially populates the tree view using layers from a project.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgsfillsymbol.h:30
QgsLayerTreeLayer * addLayer(QgsMapLayer *layer)
Append a new layer node for given map layer.
Layer tree node points to a map layer.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
The QgsLayerTreeModel class is model implementation for Qt item views framework.
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QModelIndex parent(const QModelIndex &child) const override
void setFlag(Flag f, bool on=true)
Enable or disable a model flag.
QgsRenderContext * createTemporaryRenderContext() const
Returns a temporary render context.
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given index.
QMimeData * mimeData(const QModelIndexList &indexes) const override
static int scaleIconSize(int standardSize)
Scales an layer tree model icon size to compensate for display pixel density, making the icon size hi...
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Returns legend node for given index.
@ AllowNodeChangeVisibility
Allow user to set node visibility with a checkbox.
@ ShowLegendAsTree
For legends that support it, will show them in a tree instead of a list (needs also ShowLegend)....
@ AllowNodeReorder
Allow reordering with drag'n'drop.
@ AllowLegendChangeState
Allow check boxes for legend nodes (if supported by layer's legend)
This class is a base class for nodes in a layer tree.
@ NodeGroup
Container of other groups and layers.
@ NodeLayer
Leaf node pointing to a layer.
NodeType nodeType() const
Find out about type of the node. It is usually shorter to use convenience functions from QgsLayerTree...
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:33
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:30
static QList< QgsMapLayer * > sortLayersByType(const QList< QgsMapLayer * > &layers, const QList< Qgis::LayerType > &order)
Sorts a list of map layers by their layer type, respecting the order of types specified.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
A marker symbol type, for rendering Point and MultiPoint geometries.
Mesh layer specific subclass of QgsMapLayerElevationProperties.
Qgis::ProfileSurfaceSymbology profileSymbology() const
Returns the symbology option used to render the mesh profile in elevation profile plots.
QgsLineSymbol * profileLineSymbol() const
Returns the line symbol used to render the mesh profile in elevation profile plots.
QgsFillSymbol * profileFillSymbol() const
Returns the fill symbol used to render the mesh profile in elevation profile plots.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:105
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
Definition: qgsproject.h:1194
Raster layer specific subclass of QgsMapLayerElevationProperties.
QgsLineSymbol * profileLineSymbol() const
Returns the line symbol used to render the raster profile in elevation profile plots.
Qgis::ProfileSurfaceSymbology profileSymbology() const
Returns the symbology option used to render the raster profile in elevation profile plots.
QgsFillSymbol * profileFillSymbol() const
Returns the fill symbol used to render the raster profile in elevation profile plots.
static QPixmap symbolPreviewPixmap(const QgsSymbol *symbol, QSize size, int padding=0, QgsRenderContext *customContext=nullptr, bool selected=false, const QgsExpressionContext *expressionContext=nullptr, const QgsLegendPatchShape *shape=nullptr)
Returns a pixmap preview for a color ramp.
Vector layer specific subclass of QgsMapLayerElevationProperties.
QgsFillSymbol * profileFillSymbol() const
Returns the symbol used to render polygons for the layer in elevation profile plots.
Qgis::VectorProfileType type() const
Returns the type of profile the layer represents.
QgsLineSymbol * profileLineSymbol() const
Returns the symbol used to render lines for the layer in elevation profile plots.
QgsMarkerSymbol * profileMarkerSymbol() const
Returns the symbol used to render points for the layer in elevation profile plots.
Qgis::ProfileSurfaceSymbology profileSymbology() const
Returns the symbology option used to render the vector profile in elevation profile plots.
bool respectLayerSymbology() const
Returns true if layer symbology should be respected when rendering elevation profile plots.
bool extrusionEnabled() const
Returns true if extrusion is enabled.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
QgsLayerTreeModelLegendNode * legendNode(const QString &rule, QgsLayerTreeModel &model)