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