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