QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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 {
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;
134 if ( QgsFillSymbol *fillSymbol = elevationProperties->profileFillSymbol() )
135 {
136 symbol.reset( fillSymbol->clone() );
137 }
138 break;
139 }
140 break;
141
142 }
143 break;
144 }
145
147 {
148 QgsRasterLayerElevationProperties *rlProps = qgis::down_cast< QgsRasterLayerElevationProperties * >( layer->elevationProperties() );
149 switch ( rlProps->profileSymbology() )
150 {
152 if ( QgsLineSymbol *lineSymbol = rlProps->profileLineSymbol() )
153 {
154 symbol.reset( lineSymbol->clone() );
155 }
156 break;
159 if ( QgsFillSymbol *fillSymbol = rlProps->profileFillSymbol() )
160 {
161 symbol.reset( fillSymbol->clone() );
162 }
163 break;
164 }
165 break;
166 }
167
169 {
170 QgsMeshLayerElevationProperties *mlProps = qgis::down_cast< QgsMeshLayerElevationProperties * >( layer->elevationProperties() );
171 switch ( mlProps->profileSymbology() )
172 {
174 if ( QgsLineSymbol *lineSymbol = mlProps->profileLineSymbol() )
175 {
176 symbol.reset( lineSymbol->clone() );
177 }
178 break;
181 if ( QgsFillSymbol *fillSymbol = mlProps->profileFillSymbol() )
182 {
183 symbol.reset( fillSymbol->clone() );
184 }
185 break;
186 }
187 break;
188 }
189
196 break;
197 }
198 if ( !symbol )
199 break;
200
201 const QPixmap pix = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( iconSize, iconSize ), 0, context.get() );
202 return QIcon( pix );
203 }
204 }
205 break;
206 }
207
208 case Qt::ToolTipRole:
209 {
210 // override default tooltip with elevation specific one
212 if ( node && node->nodeType() == QgsLayerTreeNode::NodeLayer )
213 {
214 if ( QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer() )
215 {
216 QString title =
217 !layer->title().isEmpty() ? layer->title() :
218 !layer->shortName().isEmpty() ? layer->shortName() :
219 layer->name();
220
221 title = "<b>" + title.toHtmlEscaped() + "</b>";
222
223 QStringList parts;
224 parts << title;
225
226 const QString elevationPropertiesSummary = layer->elevationProperties() ? layer->elevationProperties()->htmlSummary() : QString();
227 if ( !elevationPropertiesSummary.isEmpty( ) )
228 parts << elevationPropertiesSummary;
229
230 return parts.join( QLatin1String( "<br/>" ) );
231 }
232 }
233 break;
234 }
235
236 default:
237 break;
238 }
239 return QgsLayerTreeModel::data( index, role );
240}
241
242bool QgsElevationProfileLayerTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
243{
244 if ( QgsLayerTreeLayer *layerNode = qobject_cast< QgsLayerTreeLayer * >( index2node( index ) ) )
245 {
246 if ( role == Qt::CheckStateRole )
247 {
248 const bool checked = static_cast< Qt::CheckState >( value.toInt() ) == Qt::Checked;
249 if ( QgsMapLayer *layer = layerNode->layer() )
250 {
251 layer->setCustomProperty( QStringLiteral( "_include_in_elevation_profiles" ), checked );
252 }
253 }
254 }
255
256 return QgsLayerTreeModel::setData( index, value, role );
257}
258
259bool QgsElevationProfileLayerTreeModel::canDropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) const
260{
261 if ( action == Qt::IgnoreAction )
262 return true;
263
264 if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
265 return false;
266
267 // don't accept moves from other layer trees -- only allow internal drag
268 if ( action == Qt::MoveAction )
269 {
270 const QString source = data->data( QStringLiteral( "application/qgis.layertree.source" ) );
271 if ( source.isEmpty() || source != QStringLiteral( ":0x%1" ).arg( reinterpret_cast<quintptr>( this ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ) )
272 {
273 return false;
274 }
275 }
276
277 return QgsLayerTreeModel::canDropMimeData( data, action, row, column, parent );
278}
279
280bool QgsElevationProfileLayerTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
281{
282 if ( action == Qt::IgnoreAction )
283 return true;
284
285 if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
286 return false;
287
288 // don't accept moves from other layer trees -- only allow internal drag
289 const QString source = data->data( QStringLiteral( "application/qgis.layertree.source" ) );
290 if ( source.isEmpty() || source != QStringLiteral( ":0x%1" ).arg( reinterpret_cast<quintptr>( this ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ) )
291 {
292 if ( action == Qt::CopyAction )
293 {
294 QByteArray encodedLayerTreeData = data->data( QStringLiteral( "application/qgis.layertreemodeldata" ) );
295
296 QDomDocument layerTreeDoc;
297 if ( !layerTreeDoc.setContent( QString::fromUtf8( encodedLayerTreeData ) ) )
298 return false;
299
300 QDomElement rootLayerTreeElem = layerTreeDoc.documentElement();
301 if ( rootLayerTreeElem.tagName() != QLatin1String( "layer_tree_model_data" ) )
302 return false;
303
304 QList<QgsMapLayer *> layersToAdd;
305
306 QDomElement elem = rootLayerTreeElem.firstChildElement();
307 while ( !elem.isNull() )
308 {
309 std::unique_ptr< QgsLayerTreeNode > node( QgsLayerTreeNode::readXml( elem, QgsProject::instance() ) );
310 if ( node && QgsLayerTree::isLayer( node.get() ) )
311 {
312 if ( QgsMapLayer *layer = qobject_cast< QgsLayerTreeLayer * >( node.get() )->layer() )
313 {
314 layersToAdd << layer;
315 }
316 }
317 elem = elem.nextSiblingElement();
318 }
319
320 if ( !layersToAdd.empty() )
321 emit addLayers( layersToAdd );
322
323 return true;
324 }
325 else
326 {
327 return false;
328 }
329 }
330
331 return QgsLayerTreeModel::dropMimeData( data, action, row, column, parent );
332}
333
334QMimeData *QgsElevationProfileLayerTreeModel::mimeData( const QModelIndexList &indexes ) const
335{
336 QMimeData *mimeData = QgsLayerTreeModel::mimeData( indexes );
337 if ( mimeData )
338 {
339 mimeData->setData( QStringLiteral( "application/qgis.restrictlayertreemodelsubclass" ), "QgsElevationProfileLayerTreeModel" );
340 }
341 return mimeData;
342}
343
344
345//
346// QgsElevationProfileLayerTreeProxyModel
347//
348
350 : QSortFilterProxyModel( parent )
351 , mModel( model )
352{
353 setSourceModel( mModel );
354 setDynamicSortFilter( true );
355}
356
357bool QgsElevationProfileLayerTreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
358{
359 const QModelIndex sourceIndex = mModel->index( sourceRow, 0, sourceParent );
360 if ( QgsLayerTreeNode *node = mModel->index2node( sourceIndex ) )
361 {
362 switch ( node->nodeType() )
363 {
365 {
366 if ( QgsLayerTreeLayer *layerTreeLayer = QgsLayerTree::toLayer( node ) )
367 {
368 if ( QgsMapLayer *layer = layerTreeLayer->layer() )
369 {
370 // hide layers which don't have elevation
371 if ( !layer->elevationProperties() || !layer->elevationProperties()->hasElevation() )
372 return false;
373 }
374 }
375 break;
376 }
377
379 break;
380 }
381 return true;
382 }
383 else if ( QgsLayerTreeModelLegendNode *legendNode = mModel->index2legendNode( sourceIndex ) )
384 {
385 // we only show legend nodes for vector layers where the profile symbol is set to follow the layer's symbology
386 // (and the layer's renderer isn't a single symbol renderer)
387 if ( QgsLayerTreeLayer *layerTreeLayer = QgsLayerTree::toLayer( legendNode->layerNode() ) )
388 {
389 if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( layerTreeLayer->layer() ) )
390 {
391 if ( !qgis::down_cast< QgsVectorLayerElevationProperties * >( layer->elevationProperties() )->respectLayerSymbology() )
392 {
393 return false;
394 }
395 else if ( dynamic_cast< QgsSingleSymbolRenderer * >( qobject_cast< QgsVectorLayer * >( layer )->renderer() ) )
396 {
397 return false;
398 }
399 else
400 {
401 return true;
402 }
403 }
404 }
405 return false;
406 }
407 else
408 {
409 return false;
410 }
411}
412
413
415 : QTreeView( parent )
416 , mLayerTree( rootNode )
417{
418 mModel = new QgsElevationProfileLayerTreeModel( rootNode, this );
420 mProxyModel = new QgsElevationProfileLayerTreeProxyModel( mModel, this );
421
422 setHeaderHidden( true );
423
424 setDragEnabled( true );
425 setAcceptDrops( true );
426 setDropIndicatorShown( true );
427 setExpandsOnDoubleClick( false );
428
429 // Ensure legend graphics are scrollable
430 header()->setStretchLastSection( false );
431 header()->setSectionResizeMode( QHeaderView::ResizeToContents );
432
433 // If vertically scrolling by item, legend graphics can get clipped
434 setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
435
436 setDefaultDropAction( Qt::MoveAction );
437
438 setModel( mProxyModel );
439}
440
442{
443 if ( QgsLayerTreeNode *node = mModel->index2node( mProxyModel->mapToSource( index ) ) )
444 {
445 if ( QgsLayerTreeLayer *layerTreeLayerNode = mLayerTree->toLayer( node ) )
446 {
447 return layerTreeLayerNode->layer();
448 }
449 }
450 return nullptr;
451}
452
454{
455 const QList< QgsMapLayer * > layers = project->layers< QgsMapLayer * >().toList();
456
457 // sort layers so that types which are more likely to obscure others are rendered below
458 // e.g. vector features should be drawn above raster DEMS, or the DEM line may completely obscure
459 // the vector feature
460 QList< QgsMapLayer * > sortedLayers = QgsMapLayerUtils::sortLayersByType( layers,
461 {
466 } );
467
468 std::reverse( sortedLayers.begin(), sortedLayers.end() );
469 for ( QgsMapLayer *layer : std::as_const( sortedLayers ) )
470 {
471 QgsLayerTreeLayer *node = mLayerTree->addLayer( layer );
472
473 if ( layer->customProperty( QStringLiteral( "_include_in_elevation_profiles" ) ).isValid() )
474 {
475 node->setItemVisibilityChecked( layer->customProperty( QStringLiteral( "_include_in_elevation_profiles" ) ).toBool() );
476 }
477 else
478 {
479 node->setItemVisibilityChecked( layer->elevationProperties() && layer->elevationProperties()->showByDefaultInElevationProfilePlots() );
480 }
481 }
482}
483
485{
486 return mProxyModel;
487}
488
490{
491 header()->setMinimumSectionSize( viewport()->width() );
492 QTreeView::resizeEvent( event );
493}
@ Polygon
Polygons.
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
@ 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.
@ FillAbove
The elevation surface will be rendered using a fill symbol above the surface level (since QGIS 3....
A layer tree model subclass for elevation profiles.
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
void addLayers(const QList< QgsMapLayer * > &layers)
Emitted when layers should be added to the profile, e.g.
QgsElevationProfileLayerTreeModel(QgsLayerTree *rootNode, QObject *parent=nullptr)
Construct a new tree model with given layer tree (root node must not be nullptr).
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
QMimeData * mimeData(const QModelIndexList &indexes) const override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
QgsElevationProfileLayerTreeProxyModel(QgsElevationProfileLayerTreeModel *model, QObject *parent=nullptr)
Constructor for QgsElevationProfileLayerTreeProxyModel.
QgsElevationProfileLayerTreeProxyModel * proxyModel()
Returns the view's proxy model.
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 addLayers(const QList< QgsMapLayer * > &layers)
Emitted when layers should be added to the profile, e.g.
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
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) 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.
static QgsLayerTreeNode * readXml(QDomElement &element, const QgsReadWriteContext &context)
Read layer tree from XML.
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:32
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:70
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:50
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:75
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:107
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:481
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
Definition: qgsproject.h:1181
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, const QgsScreenProperties &screen=QgsScreenProperties())
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)