QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
Loading...
Searching...
No Matches
qgspointcloudlayerundocommand.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspointcloudlayerundocommand.cpp
3 ---------------------
4 begin : January 2025
5 copyright : (C) 2025 by Stefanos Natsis
6 email : uclaros at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
19#include "qgseventtracing.h"
21#include "qgspointcloudlayer.h"
23
24#include <QString>
25#include <QtConcurrentMap>
26
27using namespace Qt::StringLiterals;
28
32
34 QgsPointCloudLayer *layer, const QHash<int, QHash<QgsPointCloudNodeId, QVector<int>>> &mappedPoints, const QgsPointCloudAttribute &attribute, double value
35)
37 , mAttribute( attribute )
38 , mNewValue( value )
39{
40 QgsEventTracing::ScopedEvent _trace( u"PointCloud"_s, u"QgsPointCloudLayerUndoCommand constructor"_s );
41
42 QList<NodeProcessData> processData;
43 for ( auto it = mappedPoints.constBegin(); it != mappedPoints.constEnd(); ++it )
44 {
45 const int position = it.key();
47 if ( position < 0 )
48 index = mLayer->index();
49 else
50 index = mLayer->subIndexes().at( position ).index();
51 const auto &nodes = it.value();
52 for ( auto nodeIt = nodes.constBegin(); nodeIt != nodes.constEnd(); ++nodeIt )
53 {
54 processData.append( { position, index, nodeIt.key(), nodeIt.value() } );
55 }
56 }
57
58 std::function mapFn = [attribute]( const NodeProcessData &data ) {
59 QgsPointCloudIndex index = data.index;
60 QgsPointCloudEditingIndex *editIndex = static_cast<QgsPointCloudEditingIndex *>( index.get() );
61 QgsPointCloudNodeId n = data.nodeId;
62 const QVector<int> &points = data.points;
63
64 PerNodeData perNodeData;
65
66 if ( editIndex->isNodeModified( n ) )
67 {
68 const QgsPointCloudAttributeCollection allAttributes = index.attributes();
70 req.setAttributes( allAttributes );
71 // we want to iterate all points so we have the correct point indexes within the node
73 std::unique_ptr<QgsPointCloudBlock> block = index.nodeData( n, req );
74 const char *ptr = block->data();
75 block->attributes().find( attribute.name(), perNodeData.attributeOffset );
76 const int size = block->pointRecordSize();
77 for ( const int point : points )
78 {
79 const int offset = point * size + perNodeData.attributeOffset;
80 const double oldValue = attribute.convertValueToDouble( ptr + offset );
81 perNodeData.oldPointValues[point] = oldValue;
82 }
83 }
84 else
85 {
86 // If this is the first time this node is edited, we don't need the previous values, we will just discard the node from the edit index when undoing
87 // we still need the keys in mPointValues though as they are the points to be modified in the Redo stage, so we populate them with some NaNs
88 perNodeData.firstEdit = true;
89 for ( const int point : points )
90 {
91 perNodeData.oldPointValues[point] = std::numeric_limits<double>::quiet_NaN();
92 }
93 }
94
95 return std::pair { data.position, std::pair { n, perNodeData } };
96 };
97
98 std::function reduceFn = []( QMap<int, QHash<QgsPointCloudNodeId, PerNodeData>> &res, const std::pair<int, std::pair<QgsPointCloudNodeId, PerNodeData>> &pair ) {
99 res[pair.first][pair.second.first] = pair.second.second;
100 };
101
102 mPerNodeData = QtConcurrent::blockingMappedReduced<QMap<int, QHash<QgsPointCloudNodeId, PerNodeData>>>( processData, std::move( mapFn ), std::move( reduceFn ) );
103}
104
106{
107 undoRedoPrivate( true );
108}
109
111{
112 undoRedoPrivate( false );
113}
114
115void QgsPointCloudLayerUndoCommandChangeAttribute::undoRedoPrivate( bool isUndo )
116{
117 QgsEventTracing::ScopedEvent _trace( u"PointCloud"_s, u"QgsPointCloudLayerUndoCommand::undoRedoPrivate"_s );
118
119 QgsPointCloudAttribute attribute = mAttribute;
120 double newValue = mNewValue;
121
122 for ( auto it = mPerNodeData.begin(); it != mPerNodeData.end(); ++it )
123 {
124 const int position = it.key();
125 QHash<QgsPointCloudNodeId, PerNodeData> &nodesData = it.value();
126
127 QgsPointCloudIndex index;
128 if ( position < 0 )
129 index = mLayer->index();
130 else
131 index = mLayer->subIndexes().at( position ).index();
132 QgsPointCloudEditingIndex *editIndex = dynamic_cast<QgsPointCloudEditingIndex *>( index.get() );
133 QgsCopcPointCloudIndex *copcIndex = dynamic_cast<QgsCopcPointCloudIndex *>( editIndex->backingIndex().get() );
134
135 QtConcurrent::blockingMap( nodesData.keyValueBegin(), nodesData.keyValueEnd(), [editIndex, copcIndex, isUndo, attribute, newValue]( std::pair<const QgsPointCloudNodeId &, PerNodeData &> pair ) {
136 QgsPointCloudNodeId node = pair.first;
137 PerNodeData &perNodeData = pair.second;
138
139 QByteArray chunkData = editIndex->rawEditedNodeData( node );
140 if ( chunkData.isEmpty() ) // Not edited yet
141 chunkData = copcIndex->rawNodeData( node );
142
143 QByteArray data;
144 if ( isUndo && perNodeData.firstEdit )
145 {
146 editIndex->resetNodeEdits( node );
147 }
148 else if ( isUndo )
149 {
150 data = QgsPointCloudLayerEditUtils::updateChunkValues( copcIndex, chunkData, attribute, node, perNodeData.oldPointValues );
151 editIndex->updateNodeData( { { node, data } } );
152 }
153 else
154 {
155 data = QgsPointCloudLayerEditUtils::updateChunkValues( copcIndex, chunkData, attribute, node, perNodeData.oldPointValues, newValue );
156 editIndex->updateNodeData( { { node, data } } );
157 }
158 } );
159
160 for ( auto itNode = nodesData.constBegin(); itNode != nodesData.constEnd(); itNode++ )
161 {
162 emit mLayer->chunkAttributeValuesChanged( itNode.key(), position );
163 }
164 }
165}
A collection of point cloud attributes.
Attribute for point cloud data pair of name and size in bytes.
QString name() const
Returns name of the attribute.
double convertValueToDouble(const char *ptr) const
Returns the attribute's value as a double for data pointed to by ptr.
A QgsPointCloudIndex that is used as an editing buffer when editing point cloud data.
QgsPointCloudIndex backingIndex() const
Returns index for the underlying non-edited data.
bool isNodeModified(QgsPointCloudNodeId n) const
Returns true if this node was modified.
bool updateNodeData(const QHash< QgsPointCloudNodeId, QByteArray > &data) override
Tries to update the data for the specified nodes.
Smart pointer for QgsAbstractPointCloudIndex.
std::unique_ptr< QgsPointCloudBlock > nodeData(const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request)
Returns node data block.
QgsAbstractPointCloudIndex * get()
Returns pointer to the implementation class.
QgsPointCloudAttributeCollection attributes() const
Returns all attributes that are stored in the file.
static QByteArray updateChunkValues(QgsCopcPointCloudIndex *copcIndex, const QByteArray &chunkData, const QgsPointCloudAttribute &attribute, const QgsPointCloudNodeId &n, const QHash< int, double > &pointValues, std::optional< double > newValue=std::nullopt)
Sets new classification value for the given points in voxel and return updated chunk data.
QgsPointCloudLayerUndoCommandChangeAttribute(QgsPointCloudLayer *layer, const QHash< int, QHash< QgsPointCloudNodeId, QVector< int > > > &mappedPoints, const QgsPointCloudAttribute &attribute, double value)
Constructor for QgsPointCloudLayerUndoCommandChangeAttribute.
QgsPointCloudLayerUndoCommand(QgsPointCloudLayer *layer)
Ctor.
Represents a map layer supporting display of point clouds.
QgsPointCloudIndex index() const
Returns the point cloud index associated with the layer.
QVector< QgsPointCloudSubIndex > subIndexes() const
Returns point cloud indexes associated with the layer (only if the layer has a virtual point cloud da...
Represents an indexed point cloud node's position in octree.
Point cloud data request.
void setIgnoreIndexFilterEnabled(bool enable)
When enable is true, the request will ignore the point cloud index's filter expression and use an emp...
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Set attributes filter in the request.