QGIS API Documentation 3.41.0-Master (fda2aa46e9a)
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 "moc_qgselevationprofilelayertreeview.cpp"
21#include "qgslayertreenode.h"
22#include "qgslayertree.h"
23#include "qgssymbollayerutils.h"
27#include "qgsvectorlayer.h"
29#include "qgsmarkersymbol.h"
30#include "qgsfillsymbol.h"
31#include "qgsmaplayerutils.h"
32
33#include <QHeaderView>
34#include <QContextMenuEvent>
35#include <QMenu>
36#include <QMimeData>
37
38
47
48QVariant QgsElevationProfileLayerTreeModel::data( const QModelIndex &index, int role ) const
49{
50 switch ( role )
51 {
52 case Qt::DecorationRole:
53 {
55
56 if ( node && node->nodeType() == QgsLayerTreeNode::NodeLayer )
57 {
58 if ( QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer() )
59 {
60 std::unique_ptr<QgsRenderContext> context( createTemporaryRenderContext() );
61
62 const int iconSize = scaleIconSize( 16 );
63 std::unique_ptr< QgsSymbol > symbol;
64 switch ( layer->type() )
65 {
67 {
68 QgsVectorLayerElevationProperties *elevationProperties = qgis::down_cast< QgsVectorLayerElevationProperties * >( layer->elevationProperties() );
69 QgsVectorLayer *vLayer = qobject_cast< QgsVectorLayer * >( layer );
70
71 switch ( elevationProperties->type() )
72 {
74 if ( ( vLayer->geometryType() == Qgis::GeometryType::Point && !elevationProperties->extrusionEnabled() )
75 || ( vLayer->geometryType() == Qgis::GeometryType::Line && !elevationProperties->extrusionEnabled() )
76 )
77 {
78 if ( QgsMarkerSymbol *markerSymbol = elevationProperties->profileMarkerSymbol() )
79 {
80 symbol.reset( markerSymbol->clone() );
81 }
82 }
83
84 if ( !symbol && vLayer->geometryType() == Qgis::GeometryType::Polygon && elevationProperties->extrusionEnabled() )
85 {
86 if ( QgsFillSymbol *fillSymbol = elevationProperties->profileFillSymbol() )
87 {
88 symbol.reset( fillSymbol->clone() );
89 }
90 }
91
92 if ( !symbol )
93 {
94 if ( QgsLineSymbol *lineSymbol = elevationProperties->profileLineSymbol() )
95 {
96 symbol.reset( lineSymbol->clone() );
97 }
98 }
99
100 if ( elevationProperties->respectLayerSymbology() )
101 {
102 if ( QgsSingleSymbolRenderer *renderer = dynamic_cast< QgsSingleSymbolRenderer * >( qobject_cast< QgsVectorLayer * >( layer )->renderer() ) )
103 {
104 if ( renderer->symbol()->type() == symbol->type() )
105 {
106 // take the whole renderer symbol if we can
107 symbol.reset( renderer->symbol()->clone() );
108 }
109 else
110 {
111 // otherwise emulate what happens when rendering the actual chart and just copy the color and opacity
112 symbol->setColor( renderer->symbol()->color() );
113 symbol->setOpacity( renderer->symbol()->opacity() );
114 }
115 }
116 else
117 {
118 // just use default layer icon
119 return QgsLayerTreeModel::data( index, role );
120 }
121 }
122 break;
123
125 switch ( elevationProperties->profileSymbology() )
126 {
128 if ( QgsLineSymbol *lineSymbol = elevationProperties->profileLineSymbol() )
129 {
130 symbol.reset( lineSymbol->clone() );
131 }
132 break;
135 if ( QgsFillSymbol *fillSymbol = elevationProperties->profileFillSymbol() )
136 {
137 symbol.reset( fillSymbol->clone() );
138 }
139 break;
140 }
141 break;
142
143 }
144 break;
145 }
146
148 {
149 QgsRasterLayerElevationProperties *rlProps = qgis::down_cast< QgsRasterLayerElevationProperties * >( layer->elevationProperties() );
150 switch ( rlProps->profileSymbology() )
151 {
153 if ( QgsLineSymbol *lineSymbol = rlProps->profileLineSymbol() )
154 {
155 symbol.reset( lineSymbol->clone() );
156 }
157 break;
160 if ( QgsFillSymbol *fillSymbol = rlProps->profileFillSymbol() )
161 {
162 symbol.reset( fillSymbol->clone() );
163 }
164 break;
165 }
166 break;
167 }
168
170 {
171 QgsMeshLayerElevationProperties *mlProps = qgis::down_cast< QgsMeshLayerElevationProperties * >( layer->elevationProperties() );
172 switch ( mlProps->profileSymbology() )
173 {
175 if ( QgsLineSymbol *lineSymbol = mlProps->profileLineSymbol() )
176 {
177 symbol.reset( lineSymbol->clone() );
178 }
179 break;
182 if ( QgsFillSymbol *fillSymbol = mlProps->profileFillSymbol() )
183 {
184 symbol.reset( fillSymbol->clone() );
185 }
186 break;
187 }
188 break;
189 }
190
197 break;
198 }
199 if ( !symbol )
200 break;
201
202 const QPixmap pix = QgsSymbolLayerUtils::symbolPreviewPixmap( symbol.get(), QSize( iconSize, iconSize ), 0, context.get() );
203 return QIcon( pix );
204 }
205 }
206 break;
207 }
208
209 case Qt::ToolTipRole:
210 {
211 // override default tooltip with elevation specific one
213 if ( node && node->nodeType() == QgsLayerTreeNode::NodeLayer )
214 {
215 if ( QgsMapLayer *layer = QgsLayerTree::toLayer( node )->layer() )
216 {
217 QString title =
218 !layer->metadata().title().isEmpty() ? layer->metadata().title() :
219 !layer->serverProperties()->title().isEmpty() ? layer->serverProperties()->title() :
220 !layer->serverProperties()->shortName().isEmpty() ? layer->serverProperties()->shortName() :
221 layer->name();
222
223 title = "<b>" + title.toHtmlEscaped() + "</b>";
224
225 QStringList parts;
226 parts << title;
227
228 const QString elevationPropertiesSummary = layer->elevationProperties() ? layer->elevationProperties()->htmlSummary() : QString();
229 if ( !elevationPropertiesSummary.isEmpty( ) )
230 parts << elevationPropertiesSummary;
231
232 return parts.join( QLatin1String( "<br/>" ) );
233 }
234 }
235 break;
236 }
237
238 default:
239 break;
240 }
241 return QgsLayerTreeModel::data( index, role );
242}
243
244bool QgsElevationProfileLayerTreeModel::setData( const QModelIndex &index, const QVariant &value, int role )
245{
246 if ( QgsLayerTreeLayer *layerNode = qobject_cast< QgsLayerTreeLayer * >( index2node( index ) ) )
247 {
248 if ( role == Qt::CheckStateRole )
249 {
250 const bool checked = static_cast< Qt::CheckState >( value.toInt() ) == Qt::Checked;
251 if ( QgsMapLayer *layer = layerNode->layer() )
252 {
253 layer->setCustomProperty( QStringLiteral( "_include_in_elevation_profiles" ), checked );
254 }
255 }
256 }
257
258 return QgsLayerTreeModel::setData( index, value, role );
259}
260
261bool QgsElevationProfileLayerTreeModel::canDropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) const
262{
263 if ( action == Qt::IgnoreAction )
264 return true;
265
266 if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
267 return false;
268
269 // don't accept moves from other layer trees -- only allow internal drag
270 if ( action == Qt::MoveAction )
271 {
272 const QString source = data->data( QStringLiteral( "application/qgis.layertree.source" ) );
273 if ( source.isEmpty() || source != QStringLiteral( ":0x%1" ).arg( reinterpret_cast<quintptr>( this ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ) )
274 {
275 return false;
276 }
277 }
278
279 return QgsLayerTreeModel::canDropMimeData( data, action, row, column, parent );
280}
281
282bool QgsElevationProfileLayerTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
283{
284 if ( action == Qt::IgnoreAction )
285 return true;
286
287 if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
288 return false;
289
290 // don't accept moves from other layer trees -- only allow internal drag
291 const QString source = data->data( QStringLiteral( "application/qgis.layertree.source" ) );
292 if ( source.isEmpty() || source != QStringLiteral( ":0x%1" ).arg( reinterpret_cast<quintptr>( this ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ) )
293 {
294 if ( action == Qt::CopyAction )
295 {
296 QByteArray encodedLayerTreeData = data->data( QStringLiteral( "application/qgis.layertreemodeldata" ) );
297
298 QDomDocument layerTreeDoc;
299 if ( !layerTreeDoc.setContent( QString::fromUtf8( encodedLayerTreeData ) ) )
300 return false;
301
302 QDomElement rootLayerTreeElem = layerTreeDoc.documentElement();
303 if ( rootLayerTreeElem.tagName() != QLatin1String( "layer_tree_model_data" ) )
304 return false;
305
306 QList<QgsMapLayer *> layersToAdd;
307
308 QDomElement elem = rootLayerTreeElem.firstChildElement();
309 while ( !elem.isNull() )
310 {
311 std::unique_ptr< QgsLayerTreeNode > node( QgsLayerTreeNode::readXml( elem, QgsProject::instance() ) );
312 if ( node && QgsLayerTree::isLayer( node.get() ) )
313 {
314 if ( QgsMapLayer *layer = qobject_cast< QgsLayerTreeLayer * >( node.get() )->layer() )
315 {
316 layersToAdd << layer;
317 }
318 }
319 elem = elem.nextSiblingElement();
320 }
321
322 if ( !layersToAdd.empty() )
323 emit addLayers( layersToAdd );
324
325 return true;
326 }
327 else
328 {
329 return false;
330 }
331 }
332
333 return QgsLayerTreeModel::dropMimeData( data, action, row, column, parent );
334}
335
336QMimeData *QgsElevationProfileLayerTreeModel::mimeData( const QModelIndexList &indexes ) const
337{
338 QMimeData *mimeData = QgsLayerTreeModel::mimeData( indexes );
339 if ( mimeData )
340 {
341 mimeData->setData( QStringLiteral( "application/qgis.restrictlayertreemodelsubclass" ), "QgsElevationProfileLayerTreeModel" );
342 }
343 return mimeData;
344}
345
346
347//
348// QgsElevationProfileLayerTreeProxyModel
349//
350
352 : QSortFilterProxyModel( parent )
353 , mModel( model )
354{
355 setSourceModel( mModel );
356 setDynamicSortFilter( true );
357}
358
359bool QgsElevationProfileLayerTreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
360{
361 const QModelIndex sourceIndex = mModel->index( sourceRow, 0, sourceParent );
362 if ( QgsLayerTreeNode *node = mModel->index2node( sourceIndex ) )
363 {
364 switch ( node->nodeType() )
365 {
367 {
368 if ( QgsLayerTreeLayer *layerTreeLayer = QgsLayerTree::toLayer( node ) )
369 {
370 if ( QgsMapLayer *layer = layerTreeLayer->layer() )
371 {
372 // hide layers which don't have elevation
373 if ( !layer->elevationProperties() || !layer->elevationProperties()->hasElevation() )
374 return false;
375 }
376 }
377 break;
378 }
379
381 break;
382 }
383 return true;
384 }
385 else if ( QgsLayerTreeModelLegendNode *legendNode = mModel->index2legendNode( sourceIndex ) )
386 {
387 // we only show legend nodes for vector layers where the profile symbol is set to follow the layer's symbology
388 // (and the layer's renderer isn't a single symbol renderer)
389 if ( QgsLayerTreeLayer *layerTreeLayer = QgsLayerTree::toLayer( legendNode->layerNode() ) )
390 {
391 if ( QgsVectorLayer *layer = qobject_cast< QgsVectorLayer * >( layerTreeLayer->layer() ) )
392 {
393 if ( !qgis::down_cast< QgsVectorLayerElevationProperties * >( layer->elevationProperties() )->respectLayerSymbology() )
394 {
395 return false;
396 }
397 else if ( dynamic_cast< QgsSingleSymbolRenderer * >( qobject_cast< QgsVectorLayer * >( layer )->renderer() ) )
398 {
399 return false;
400 }
401 else
402 {
403 return true;
404 }
405 }
406 }
407 return false;
408 }
409 else
410 {
411 return false;
412 }
413}
414
415
417 : QTreeView( parent )
418 , mLayerTree( rootNode )
419{
420 mModel = new QgsElevationProfileLayerTreeModel( rootNode, this );
422 mProxyModel = new QgsElevationProfileLayerTreeProxyModel( mModel, this );
423
424 setHeaderHidden( true );
425
426 setDragEnabled( true );
427 setAcceptDrops( true );
428 setDropIndicatorShown( true );
429 setExpandsOnDoubleClick( false );
430
431 // Ensure legend graphics are scrollable
432 header()->setStretchLastSection( false );
433 header()->setSectionResizeMode( QHeaderView::ResizeToContents );
434
435 // If vertically scrolling by item, legend graphics can get clipped
436 setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
437
438 setDefaultDropAction( Qt::MoveAction );
439
440 setModel( mProxyModel );
441}
442
444{
445 if ( QgsLayerTreeNode *node = mModel->index2node( mProxyModel->mapToSource( index ) ) )
446 {
447 if ( QgsLayerTreeLayer *layerTreeLayerNode = mLayerTree->toLayer( node ) )
448 {
449 return layerTreeLayerNode->layer();
450 }
451 }
452 return nullptr;
453}
454
456{
457 const QList< QgsMapLayer * > layers = project->layers< QgsMapLayer * >().toList();
458
459 // sort layers so that types which are more likely to obscure others are rendered below
460 // e.g. vector features should be drawn above raster DEMS, or the DEM line may completely obscure
461 // the vector feature
462 QList< QgsMapLayer * > sortedLayers = QgsMapLayerUtils::sortLayersByType( layers,
463 {
468 } );
469
470 std::reverse( sortedLayers.begin(), sortedLayers.end() );
471 for ( QgsMapLayer *layer : std::as_const( sortedLayers ) )
472 {
473 QgsLayerTreeLayer *node = mLayerTree->addLayer( layer );
474
475 if ( layer->customProperty( QStringLiteral( "_include_in_elevation_profiles" ) ).isValid() )
476 {
477 node->setItemVisibilityChecked( layer->customProperty( QStringLiteral( "_include_in_elevation_profiles" ) ).toBool() );
478 }
479 else
480 {
481 node->setItemVisibilityChecked( layer->elevationProperties() && layer->elevationProperties()->showByDefaultInElevationProfilePlots() );
482 }
483 }
484}
485
490
492{
493 header()->setMinimumSectionSize( viewport()->width() );
494 QTreeView::resizeEvent( event );
495}
@ 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.
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:76
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.