Skip to content

Commit

Permalink
[pointclouds] Add an extent only 2d renderer for point clouds
Browse files Browse the repository at this point in the history
Useful when you just want an overview of the bounds of the layer in a
2d map. Also potentially usable as a temporary renderer for las/laz
files while we build the indexed representation of the file in the
background.
  • Loading branch information
nyalldawson committed Dec 4, 2020
1 parent 6894c85 commit def587f
Show file tree
Hide file tree
Showing 19 changed files with 745 additions and 18 deletions.
@@ -0,0 +1,89 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/pointcloud/qgspointcloudextentrenderer.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/





class QgsPointCloudExtentRenderer : QgsPointCloudRenderer
{
%Docstring
A renderer for 2d visualisation of point clouds which shows the dataset's extents using a fill symbol.

.. versionadded:: 3.18
%End

%TypeHeaderCode
#include "qgspointcloudextentrenderer.h"
%End
public:

QgsPointCloudExtentRenderer( QgsFillSymbol *symbol /Transfer/ = 0 );
%Docstring
Constructor for QgsPointCloudExtentRenderer.

Optionally the ``symbol`` to use for showing the extent can be specified. If specified, ownership is
transferred to the renderer. If no ``symbol`` is specified a default one will be created instead.
%End

virtual QString type() const;

virtual QgsPointCloudRenderer *clone() const;

virtual void renderBlock( const QgsPointCloudBlock *block, QgsPointCloudRenderContext &context );

virtual QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) const;


virtual void startRender( QgsPointCloudRenderContext &context );

virtual void stopRender( QgsPointCloudRenderContext &context );

virtual QList<QgsLayerTreeModelLegendNode *> createLegendNodes( QgsLayerTreeLayer *nodeLayer ) /Factory/;


static QgsPointCloudRenderer *create( QDomElement &element, const QgsReadWriteContext &context ) /Factory/;
%Docstring
Creates an extent renderer from an XML ``element``.
%End

void renderExtent( const QgsGeometry &extent, QgsPointCloudRenderContext &context );
%Docstring
Renders a polygon ``extent`` geometry to the specified render ``context``.
%End

static QgsFillSymbol *defaultFillSymbol() /Factory/;
%Docstring
Returns a new instance of the default fill symbol to use for showing point cloud extents.
%End

QgsFillSymbol *fillSymbol() const;
%Docstring
Returns the symbol used to render the cloud's extent.

.. seealso:: :py:func:`setFillSymbol`
%End

void setFillSymbol( QgsFillSymbol *symbol /Transfer/ );
%Docstring
Sets the ``symbol`` used to render the cloud's extent.

Ownership of ``symbol`` is transferred to the renderer.

.. seealso:: :py:func:`fillSymbol`
%End

};

/************************************************************************
* This file has been generated automatically from *
* *
* src/core/pointcloud/qgspointcloudextentrenderer.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
Expand Up @@ -138,6 +138,8 @@ Abstract base class for 2d point cloud renderers.
sipType = sipType_QgsPointCloudAttributeByRampRenderer;
else if ( type == QLatin1String( "classified" ) )
sipType = sipType_QgsPointCloudClassifiedRenderer;
else if ( type == QLatin1String( "extent" ) )
sipType = sipType_QgsPointCloudExtentRenderer;
else
sipType = 0;
%End
Expand Down
1 change: 1 addition & 0 deletions python/core/core_auto.sip
Expand Up @@ -449,6 +449,7 @@
%Include auto_generated/pointcloud/qgspointcloudattributebyramprenderer.sip
%Include auto_generated/pointcloud/qgspointcloudattributemodel.sip
%Include auto_generated/pointcloud/qgspointcloudclassifiedrenderer.sip
%Include auto_generated/pointcloud/qgspointcloudextentrenderer.sip
%Include auto_generated/pointcloud/qgspointcloudblock.sip
%Include auto_generated/pointcloud/qgspointcloudlayer.sip
%Include auto_generated/pointcloud/qgspointcloudlayerelevationproperties.sip
Expand Down
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -631,6 +631,7 @@ set(QGIS_CORE_SRCS
pointcloud/qgspointcloudattributebyramprenderer.cpp
pointcloud/qgspointcloudattributemodel.cpp
pointcloud/qgspointcloudclassifiedrenderer.cpp
pointcloud/qgspointcloudextentrenderer.cpp
pointcloud/qgspointcloudrequest.cpp
pointcloud/qgspointcloudblock.cpp
pointcloud/qgspointcloudlayer.cpp
Expand Down Expand Up @@ -1337,6 +1338,7 @@ set(QGIS_CORE_HDRS
pointcloud/qgspointcloudattributebyramprenderer.h
pointcloud/qgspointcloudattributemodel.h
pointcloud/qgspointcloudclassifiedrenderer.h
pointcloud/qgspointcloudextentrenderer.h
pointcloud/qgspointcloudrequest.h
pointcloud/qgspointcloudblock.h
pointcloud/qgspointcloudlayer.h
Expand Down
172 changes: 172 additions & 0 deletions src/core/pointcloud/qgspointcloudextentrenderer.cpp
@@ -0,0 +1,172 @@
/***************************************************************************
qgspointcloudextentrenderer.h
--------------------
begin : December 2020
copyright : (C) 2020 by Nyall Dawson
email : nyall dot dawson 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 "qgspointcloudextentrenderer.h"
#include "qgspointcloudblock.h"
#include "qgssymbollayerutils.h"
#include "qgssymbol.h"
#include "qgswkbtypes.h"
#include "qgspolygon.h"
#include "qgscurve.h"
#include "qgslinesymbollayer.h"
#include "qgslayertreemodellegendnode.h"

QgsPointCloudExtentRenderer::QgsPointCloudExtentRenderer( QgsFillSymbol *symbol )
: mFillSymbol( symbol ? symbol : defaultFillSymbol() )
{

}

QString QgsPointCloudExtentRenderer::type() const
{
return QStringLiteral( "extent" );
}

QgsPointCloudRenderer *QgsPointCloudExtentRenderer::clone() const
{
std::unique_ptr< QgsPointCloudExtentRenderer > res = qgis::make_unique< QgsPointCloudExtentRenderer >( mFillSymbol ? mFillSymbol->clone() : nullptr );
copyCommonProperties( res.get() );
return res.release();
}

void QgsPointCloudExtentRenderer::renderBlock( const QgsPointCloudBlock *, QgsPointCloudRenderContext & )
{

}

QgsPointCloudRenderer *QgsPointCloudExtentRenderer::create( QDomElement &element, const QgsReadWriteContext &context )
{
std::unique_ptr< QgsPointCloudExtentRenderer > r = qgis::make_unique< QgsPointCloudExtentRenderer >();

QDomElement symbolElem = element.firstChildElement( QStringLiteral( "symbol" ) );
if ( !symbolElem.isNull() )
{
r->mFillSymbol.reset( QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( symbolElem, context ) );
}

r->restoreCommonProperties( element, context );
return r.release();
}

void QgsPointCloudExtentRenderer::renderExtent( const QgsGeometry &extent, QgsPointCloudRenderContext &context )
{
auto transformRing = [&context]( QPolygonF & pts )
{
//transform the QPolygonF to screen coordinates
if ( context.renderContext().coordinateTransform().isValid() )
{
try
{
context.renderContext().coordinateTransform().transformPolygon( pts );
}
catch ( QgsCsException & )
{
// we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid
}
}

// remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors
pts.erase( std::remove_if( pts.begin(), pts.end(),
[]( const QPointF point )
{
return !std::isfinite( point.x() ) || !std::isfinite( point.y() );
} ), pts.end() );

QPointF *ptr = pts.data();
for ( int i = 0; i < pts.size(); ++i, ++ptr )
{
context.renderContext().mapToPixel().transformInPlace( ptr->rx(), ptr->ry() );
}
};

for ( auto it = extent.const_parts_begin(); it != extent.const_parts_end(); ++it )
{
if ( const QgsPolygon *polygon = qgsgeometry_cast< const QgsPolygon * >( *it ) )
{
QPolygonF exterior = polygon->exteriorRing()->asQPolygonF();
transformRing( exterior );
QVector<QPolygonF> rings;
rings.reserve( polygon->numInteriorRings() );
for ( int i = 0; i < polygon->numInteriorRings(); ++i )
{
QPolygonF ring = polygon->interiorRing( i )->asQPolygonF();
transformRing( ring );
rings.append( ring );
}

mFillSymbol->renderPolygon( exterior, rings.empty() ? nullptr : &rings, nullptr, context.renderContext() );
}
}
}

QgsFillSymbol *QgsPointCloudExtentRenderer::defaultFillSymbol()
{
std::unique_ptr< QgsSimpleLineSymbolLayer > layer = qgis::make_unique< QgsSimpleLineSymbolLayer >();
layer->setColor( QColor( 228, 26, 28 ) );
layer->setWidth( 0.960000 );
layer->setPenStyle( Qt::DotLine );
layer->setWidthUnit( QgsUnitTypes::RenderMillimeters );
return new QgsFillSymbol( QgsSymbolLayerList() << layer.release() );
}

QgsFillSymbol *QgsPointCloudExtentRenderer::fillSymbol() const
{
return mFillSymbol.get();
}

void QgsPointCloudExtentRenderer::setFillSymbol( QgsFillSymbol *symbol )
{
mFillSymbol.reset( symbol );
}

QDomElement QgsPointCloudExtentRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context ) const
{
QDomElement rendererElem = doc.createElement( QStringLiteral( "renderer" ) );

rendererElem.setAttribute( QStringLiteral( "type" ), type() );

QDomElement symbolElem = QgsSymbolLayerUtils::saveSymbol( QString(), mFillSymbol.get(), doc, context );
rendererElem.appendChild( symbolElem );

saveCommonProperties( rendererElem, context );
return rendererElem;
}

void QgsPointCloudExtentRenderer::startRender( QgsPointCloudRenderContext &context )
{
QgsPointCloudRenderer::startRender( context );
mFillSymbol->startRender( context.renderContext() );
}

void QgsPointCloudExtentRenderer::stopRender( QgsPointCloudRenderContext &context )
{
QgsPointCloudRenderer::stopRender( context );
mFillSymbol->stopRender( context.renderContext() );
}

QList<QgsLayerTreeModelLegendNode *> QgsPointCloudExtentRenderer::createLegendNodes( QgsLayerTreeLayer *nodeLayer )
{
QList<QgsLayerTreeModelLegendNode *> nodes;

QgsLegendSymbolItem extentItem( mFillSymbol.get(), QStringLiteral( "extent" ), QStringLiteral( "extent" ) );
QgsSymbolLegendNode *node = new QgsSymbolLegendNode( nodeLayer, extentItem );
node->setEmbeddedInParent( true );
nodes << node;

return nodes;
}

91 changes: 91 additions & 0 deletions src/core/pointcloud/qgspointcloudextentrenderer.h
@@ -0,0 +1,91 @@
/***************************************************************************
qgspointcloudextentrenderer.h
--------------------
begin : December 2020
copyright : (C) 2020 by Nyall Dawson
email : nyall dot dawson 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. *
* *
***************************************************************************/

#ifndef QGSPOINTCLOUDEXTENTRENDERER_H
#define QGSPOINTCLOUDEXTENTRENDERER_H

#include "qgspointcloudrenderer.h"
#include "qgis_core.h"
#include "qgis_sip.h"

class QgsFillSymbol;

/**
* \ingroup core
* A renderer for 2d visualisation of point clouds which shows the dataset's extents using a fill symbol.
*
* \since QGIS 3.18
*/
class CORE_EXPORT QgsPointCloudExtentRenderer : public QgsPointCloudRenderer
{
public:

/**
* Constructor for QgsPointCloudExtentRenderer.
*
* Optionally the \a symbol to use for showing the extent can be specified. If specified, ownership is
* transferred to the renderer. If no \a symbol is specified a default one will be created instead.
*/
QgsPointCloudExtentRenderer( QgsFillSymbol *symbol SIP_TRANSFER = nullptr );

QString type() const override;
QgsPointCloudRenderer *clone() const override;
void renderBlock( const QgsPointCloudBlock *block, QgsPointCloudRenderContext &context ) override;
QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) const override;

void startRender( QgsPointCloudRenderContext &context ) override;
void stopRender( QgsPointCloudRenderContext &context ) override;
QList<QgsLayerTreeModelLegendNode *> createLegendNodes( QgsLayerTreeLayer *nodeLayer ) override SIP_FACTORY;

/**
* Creates an extent renderer from an XML \a element.
*/
static QgsPointCloudRenderer *create( QDomElement &element, const QgsReadWriteContext &context ) SIP_FACTORY;

/**
* Renders a polygon \a extent geometry to the specified render \a context.
*/
void renderExtent( const QgsGeometry &extent, QgsPointCloudRenderContext &context );

/**
* Returns a new instance of the default fill symbol to use for showing point cloud extents.
*/
static QgsFillSymbol *defaultFillSymbol() SIP_FACTORY;

/**
* Returns the symbol used to render the cloud's extent.
*
* \see setFillSymbol()
*/
QgsFillSymbol *fillSymbol() const;

/**
* Sets the \a symbol used to render the cloud's extent.
*
* Ownership of \a symbol is transferred to the renderer.
*
* \see fillSymbol()
*/
void setFillSymbol( QgsFillSymbol *symbol SIP_TRANSFER );

private:

std::unique_ptr< QgsFillSymbol > mFillSymbol;

};

#endif // QGSPOINTCLOUDEXTENTRENDERER_H

0 comments on commit def587f

Please sign in to comment.