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