Skip to content

Commit

Permalink
Point cloud editing part 1
Browse files Browse the repository at this point in the history
  • Loading branch information
uclaros committed Dec 19, 2024
1 parent 80e56a4 commit 2401d3a
Show file tree
Hide file tree
Showing 26 changed files with 1,221 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
QgsPointCloudDataProvider.WriteLayerMetadata = QgsPointCloudDataProvider.Capability.WriteLayerMetadata
QgsPointCloudDataProvider.CreateRenderer = QgsPointCloudDataProvider.Capability.CreateRenderer
QgsPointCloudDataProvider.ContainSubIndexes = QgsPointCloudDataProvider.Capability.ContainSubIndexes
QgsPointCloudDataProvider.ChangeAttributeValues = QgsPointCloudDataProvider.Capability.ChangeAttributeValues
QgsPointCloudDataProvider.Capabilities = lambda flags=0: QgsPointCloudDataProvider.Capability(flags)
QgsPointCloudDataProvider.NotIndexed = QgsPointCloudDataProvider.PointCloudIndexGenerationState.NotIndexed
QgsPointCloudDataProvider.Indexing = QgsPointCloudDataProvider.PointCloudIndexGenerationState.Indexing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Responsible for reading native point cloud data and returning the indexed data.
WriteLayerMetadata,
CreateRenderer,
ContainSubIndexes,
ChangeAttributeValues,
};

typedef QFlags<QgsPointCloudDataProvider::Capability> Capabilities;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ Constructor - creates a point cloud layer
virtual QgsPointCloudDataProvider *dataProvider();


virtual bool supportsEditing() const;

virtual bool isEditable() const;

virtual bool isModified() const;


virtual bool readXml( const QDomNode &layerNode, QgsReadWriteContext &context );


Expand Down Expand Up @@ -201,6 +208,74 @@ Returns the status of point cloud statistics calculation

.. versionadded:: 3.26
%End

bool startEditing();
%Docstring
Makes the layer editable.

This starts an edit session on this layer. Changes made in this edit session will not
be made persistent until :py:func:`~QgsPointCloudLayer.commitChanges` is called, and can be reverted by calling
:py:func:`~QgsPointCloudLayer.rollBack`.

:return: ``True`` if the layer was successfully made editable, or ``False`` if the operation
failed (e.g. due to an underlying read-only data source, or lack of edit support
by the backend data provider).

.. seealso:: :py:func:`commitChanges`

.. seealso:: :py:func:`rollBack`

.. versionadded:: 3.42
%End

bool commitChanges( bool stopEditing = true );
%Docstring
Attempts to commit to the underlying data provider any buffered changes made since the
last to call to :py:func:`~QgsPointCloudLayer.startEditing`.

Returns the result of the attempt. If a commit fails (i.e. ``False`` is returned), the
in-memory changes are left untouched and are not discarded. This allows editing to
continue if the commit failed on e.g. a disallowed value for an attribute - the user
can re-edit and try again.

If the commit failed, an error message may returned by :py:func:`~QgsPointCloudLayer.commitError`.

By setting ``stopEditing`` to ``False``, the layer will stay in editing mode.
Otherwise the layer editing mode will be disabled if the commit is successful.

.. seealso:: :py:func:`startEditing`

.. seealso:: :py:func:`commitError`

.. seealso:: :py:func:`rollBack`

.. versionadded:: 3.42
%End

QString commitError() const;
%Docstring
Returns the last error message generated when attempting
to commit changes to the layer.

.. seealso:: :py:func:`commitChanges`

.. versionadded:: 3.42
%End

bool rollBack();
%Docstring
Stops a current editing operation and discards any uncommitted edits.

.. seealso:: :py:func:`startEditing`

.. seealso:: :py:func:`commitChanges`

.. versionadded:: 3.42
%End




signals:

void subsetStringChanged();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Responsible for reading native point cloud data and returning the indexed data.
WriteLayerMetadata,
CreateRenderer,
ContainSubIndexes,
ChangeAttributeValues,
};

typedef QFlags<QgsPointCloudDataProvider::Capability> Capabilities;
Expand Down
75 changes: 75 additions & 0 deletions python/core/auto_generated/pointcloud/qgspointcloudlayer.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ Constructor - creates a point cloud layer
virtual QgsPointCloudDataProvider *dataProvider();


virtual bool supportsEditing() const;

virtual bool isEditable() const;

virtual bool isModified() const;


virtual bool readXml( const QDomNode &layerNode, QgsReadWriteContext &context );


Expand Down Expand Up @@ -201,6 +208,74 @@ Returns the status of point cloud statistics calculation

.. versionadded:: 3.26
%End

bool startEditing();
%Docstring
Makes the layer editable.

This starts an edit session on this layer. Changes made in this edit session will not
be made persistent until :py:func:`~QgsPointCloudLayer.commitChanges` is called, and can be reverted by calling
:py:func:`~QgsPointCloudLayer.rollBack`.

:return: ``True`` if the layer was successfully made editable, or ``False`` if the operation
failed (e.g. due to an underlying read-only data source, or lack of edit support
by the backend data provider).

.. seealso:: :py:func:`commitChanges`

.. seealso:: :py:func:`rollBack`

.. versionadded:: 3.42
%End

bool commitChanges( bool stopEditing = true );
%Docstring
Attempts to commit to the underlying data provider any buffered changes made since the
last to call to :py:func:`~QgsPointCloudLayer.startEditing`.

Returns the result of the attempt. If a commit fails (i.e. ``False`` is returned), the
in-memory changes are left untouched and are not discarded. This allows editing to
continue if the commit failed on e.g. a disallowed value for an attribute - the user
can re-edit and try again.

If the commit failed, an error message may returned by :py:func:`~QgsPointCloudLayer.commitError`.

By setting ``stopEditing`` to ``False``, the layer will stay in editing mode.
Otherwise the layer editing mode will be disabled if the commit is successful.

.. seealso:: :py:func:`startEditing`

.. seealso:: :py:func:`commitError`

.. seealso:: :py:func:`rollBack`

.. versionadded:: 3.42
%End

QString commitError() const;
%Docstring
Returns the last error message generated when attempting
to commit changes to the layer.

.. seealso:: :py:func:`commitChanges`

.. versionadded:: 3.42
%End

bool rollBack();
%Docstring
Stops a current editing operation and discards any uncommitted edits.

.. seealso:: :py:func:`startEditing`

.. seealso:: :py:func:`commitChanges`

.. versionadded:: 3.42
%End




signals:

void subsetStringChanged();
Expand Down
4 changes: 2 additions & 2 deletions src/3d/qgspointcloudlayer3drenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,9 @@ Qt3DCore::QEntity *QgsPointCloudLayer3DRenderer::createEntity( Qgs3DMapSettings
const QgsCoordinateTransform coordinateTransform( pcl->crs3D(), map->crs(), map->transformContext() );

Qt3DCore::QEntity *entity = nullptr;
if ( pcl->dataProvider()->index() )
if ( pcl->index() )
{
entity = new QgsPointCloudLayerChunkedEntity( map, pcl->dataProvider()->index(), coordinateTransform, dynamic_cast<QgsPointCloud3DSymbol *>( mSymbol->clone() ), static_cast<float>( maximumScreenError() ), showBoundingBoxes(), static_cast<const QgsPointCloudLayerElevationProperties *>( pcl->elevationProperties() )->zScale(), static_cast<const QgsPointCloudLayerElevationProperties *>( pcl->elevationProperties() )->zOffset(), mPointBudget );
entity = new QgsPointCloudLayerChunkedEntity( map, pcl->index(), coordinateTransform, dynamic_cast<QgsPointCloud3DSymbol *>( mSymbol->clone() ), static_cast<float>( maximumScreenError() ), showBoundingBoxes(), static_cast<const QgsPointCloudLayerElevationProperties *>( pcl->elevationProperties() )->zScale(), static_cast<const QgsPointCloudLayerElevationProperties *>( pcl->elevationProperties() )->zOffset(), mPointBudget );
}
else if ( !pcl->dataProvider()->subIndexes().isEmpty() )
{
Expand Down
4 changes: 4 additions & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -858,11 +858,13 @@ set(QGIS_CORE_SRCS
pointcloud/qgspointcloudattributebyramprenderer.cpp
pointcloud/qgspointcloudattributemodel.cpp
pointcloud/qgspointcloudclassifiedrenderer.cpp
pointcloud/qgspointcloudeditingindex.cpp
pointcloud/qgspointcloudextentrenderer.cpp
pointcloud/qgspointcloudrequest.cpp
pointcloud/qgspointcloudblock.cpp
pointcloud/qgspointcloudblockrequest.cpp
pointcloud/qgspointcloudlayer.cpp
pointcloud/qgspointcloudlayereditutils.cpp
pointcloud/qgspointcloudlayerelevationproperties.cpp
pointcloud/qgspointcloudlayerprofilegenerator.cpp
pointcloud/qgspointcloudlayerrenderer.cpp
Expand Down Expand Up @@ -1726,11 +1728,13 @@ set(QGIS_CORE_HDRS
pointcloud/qgspointcloudattributebyramprenderer.h
pointcloud/qgspointcloudattributemodel.h
pointcloud/qgspointcloudclassifiedrenderer.h
pointcloud/qgspointcloudeditingindex.h
pointcloud/qgspointcloudextentrenderer.h
pointcloud/qgspointcloudrequest.h
pointcloud/qgspointcloudblock.h
pointcloud/qgspointcloudblockrequest.h
pointcloud/qgspointcloudlayer.h
pointcloud/qgspointcloudlayereditutils.h
pointcloud/qgspointcloudlayerelevationproperties.h
pointcloud/qgspointcloudlayerprofilegenerator.h
pointcloud/qgspointcloudlayerrenderer.h
Expand Down
1 change: 1 addition & 0 deletions src/core/pointcloud/qgspointclouddataprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class CORE_EXPORT QgsPointCloudDataProvider: public QgsDataProvider
WriteLayerMetadata = 1 << 1, //!< Provider can write layer metadata to the data store. See QgsDataProvider::writeLayerMetadata()
CreateRenderer = 1 << 2, //!< Provider can create 2D renderers using backend-specific formatting information. See QgsPointCloudDataProvider::createRenderer().
ContainSubIndexes = 1 << 3, //!< Provider can contain multiple indexes. Virtual point cloud files for example \since QGIS 3.32
ChangeAttributeValues = 1 << 4, //!< Provider can modify the values of point attributes. \since QGIS 3.42
};

Q_DECLARE_FLAGS( Capabilities, Capability )
Expand Down
141 changes: 141 additions & 0 deletions src/core/pointcloud/qgspointcloudeditingindex.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/***************************************************************************
qgspointcloudeditingindex.cpp
---------------------
begin : December 2024
copyright : (C) 2024 by Stefanos Natsis
email : uclaros at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgspointcloudeditingindex.h"
#include "qgspointcloudlayer.h"
#include "qgspointcloudlayereditutils.h"
#include "qgscoordinatereferencesystem.h"


QgsPointCloudEditingIndex::QgsPointCloudEditingIndex( QgsPointCloudLayer *layer )
: mIndex( layer ? layer->index() : nullptr )
{
if ( !layer ||
!layer->dataProvider() ||
!layer->dataProvider()->hasValidIndex() ||
!( layer->dataProvider()->capabilities() & QgsPointCloudDataProvider::Capability::ChangeAttributeValues ) )
return;

mAttributes = mIndex->attributes();
mScale = mIndex->scale();
mOffset = mIndex->offset();
mExtent = mIndex->extent();
mZMin = mIndex->zMin();
mZMax = mIndex->zMax();
mRootBounds = mIndex->rootNodeBounds();
mSpan = mIndex->span();
mIsValid = true;
}

std::unique_ptr<QgsPointCloudIndex> QgsPointCloudEditingIndex::clone() const
{
return nullptr;
}

void QgsPointCloudEditingIndex::load( const QString & )
{
return;
}

bool QgsPointCloudEditingIndex::isValid() const
{
return mIsValid && mIndex->isValid();
}

QgsPointCloudIndex::AccessType QgsPointCloudEditingIndex::accessType() const
{
return mIndex->accessType();
}

QgsCoordinateReferenceSystem QgsPointCloudEditingIndex::crs() const
{
return mIndex->crs();
}

qint64 QgsPointCloudEditingIndex::pointCount() const
{
return mIndex->pointCount();
}

QVariantMap QgsPointCloudEditingIndex::originalMetadata() const
{
return mIndex->originalMetadata();
}

bool QgsPointCloudEditingIndex::hasNode( const QgsPointCloudNodeId &n ) const
{
return mIndex->hasNode( n );
}

QgsPointCloudNode QgsPointCloudEditingIndex::getNode( const QgsPointCloudNodeId &id ) const
{
return mIndex->getNode( id );
}

std::unique_ptr< QgsPointCloudBlock > QgsPointCloudEditingIndex::nodeData( const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request )
{
if ( mEditedNodeData.contains( n ) )
{
const QByteArray data = mEditedNodeData.value( n );
int nPoints = data.size() / mIndex->attributes().pointRecordSize();

const QByteArray requestedData = QgsPointCloudLayerEditUtils::dataForAttributes( mIndex->attributes(), data, request );

std::unique_ptr<QgsPointCloudBlock> block = std::make_unique< QgsPointCloudBlock >(
nPoints,
request.attributes(),
requestedData,
mIndex->scale(),
mIndex->offset() );
return block;
}
else
{
return mIndex->nodeData( n, request );
}
}

QgsPointCloudBlockRequest *QgsPointCloudEditingIndex::asyncNodeData( const QgsPointCloudNodeId &, const QgsPointCloudRequest & )
{
Q_ASSERT( false );
return nullptr;
}

bool QgsPointCloudEditingIndex::commitChanges()
{
if ( !isModified() )
return true;

if ( !mIndex->updateNodeData( mEditedNodeData ) )
return false;

mEditedNodeData.clear();
return true;
}

bool QgsPointCloudEditingIndex::isModified() const
{
return !mEditedNodeData.isEmpty();
}

bool QgsPointCloudEditingIndex::updateNodeData( const QHash<QgsPointCloudNodeId, QByteArray> &data )
{
for ( auto it = data.constBegin(); it != data.constEnd(); ++it )
{
mEditedNodeData[it.key()] = it.value();
}

return true;
}
Loading

0 comments on commit 2401d3a

Please sign in to comment.