Skip to content

Commit

Permalink
- Add tests for COPC
Browse files Browse the repository at this point in the history
- Address some of Martin's reviews
  • Loading branch information
NEDJIMAbelgacem authored and wonder-sk committed Mar 31, 2022
1 parent 458748c commit 7faea46
Show file tree
Hide file tree
Showing 15 changed files with 710 additions and 113 deletions.
12 changes: 5 additions & 7 deletions CMakeLists.txt
Expand Up @@ -411,21 +411,19 @@ if(WITH_CORE)
message(STATUS "Qt WebKit support DISABLED.")
endif()

if (WITH_EPT) # EPT provider
find_package(ZSTD REQUIRED) # for decompression of point clouds
if (WITH_EPT OR WITH_COPC)
find_package(LazPerf) # for decompression of point clouds
if (NOT LazPerf_FOUND)
message(STATUS "Using embedded laz-perf")
endif()
endif()

if (WITH_EPT) # EPT provider
find_package(ZSTD REQUIRED) # for decompression of point clouds
set(HAVE_EPT TRUE) # used in qgsconfig.h
endif()

if (WITH_COPC) # COPC provider
find_package(ZSTD REQUIRED) # for decompression of point clouds
find_package(LazPerf) # for decompression of point clouds
if (NOT LazPerf_FOUND)
message(STATUS "Using embedded laz-perf")
endif()
set(HAVE_COPC TRUE) # used in qgsconfig.h
endif()

Expand Down
103 changes: 35 additions & 68 deletions src/core/CMakeLists.txt
Expand Up @@ -1929,43 +1929,13 @@ if (WITH_EPT)
${ZSTD_INCLUDE_DIR}
)

if (LazPerf_FOUND)
# Use system laz-perf
include_directories(SYSTEM
${LazPerf_INCLUDE_DIR}
)
else()
# Use embedded laz-perf from external/laz-perf
include_directories(SYSTEM
)

set(QGIS_CORE_SRCS ${QGIS_CORE_SRCS}
${CMAKE_SOURCE_DIR}/external/lazperf/charbuf.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/filestream.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/header.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/lazperf.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/readers.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/vlr.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_byte10.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_byte14.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_gpstime10.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_nir14.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_point10.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_point14.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_rgb10.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_rgb14.cpp
)
endif()

set(QGIS_CORE_SRCS ${QGIS_CORE_SRCS}
providers/ept/qgseptprovider.cpp
pointcloud/qgseptdecoder.cpp
pointcloud/qgseptpointcloudindex.cpp
pointcloud/qgsremoteeptpointcloudindex.cpp
)
set(QGIS_CORE_HDRS ${QGIS_CORE_HDRS}
providers/ept/qgseptprovider.h
pointcloud/qgseptdecoder.h
pointcloud/qgseptpointcloudindex.h
pointcloud/qgsremoteeptpointcloudindex.h
)
Expand All @@ -1976,50 +1946,53 @@ endif()
if (WITH_COPC)
include_directories(providers/copc)

include_directories(SYSTEM
${ZSTD_INCLUDE_DIR}
set(QGIS_CORE_SRCS ${QGIS_CORE_SRCS}
providers/copc/qgscopcprovider.cpp
pointcloud/qgscopcpointcloudindex.cpp
)
set(QGIS_CORE_HDRS ${QGIS_CORE_HDRS}
providers/copc/qgscopcprovider.h
pointcloud/qgscopcpointcloudindex.h
)

add_definitions( -DWITH_COPC )
endif()

if (WITH_EPT OR WITH_COPC)
if (LazPerf_FOUND)
# Use system laz-perf
include_directories(SYSTEM
${LazPerf_INCLUDE_DIR}
)
# Use system laz-perf
include_directories(SYSTEM
${LazPerf_INCLUDE_DIR}
)
else()
# Use embedded laz-perf from external/laz-perf
include_directories(SYSTEM
)
# Use embedded laz-perf from external/laz-perf
include_directories(SYSTEM
)

set(QGIS_CORE_SRCS ${QGIS_CORE_SRCS}
${CMAKE_SOURCE_DIR}/external/lazperf/charbuf.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/filestream.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/header.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/lazperf.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/readers.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/vlr.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_byte10.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_byte14.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_gpstime10.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_nir14.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_point10.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_point14.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_rgb10.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_rgb14.cpp
)
set(QGIS_CORE_SRCS ${QGIS_CORE_SRCS}
${CMAKE_SOURCE_DIR}/external/lazperf/charbuf.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/filestream.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/header.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/lazperf.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/readers.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/vlr.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_byte10.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_byte14.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_gpstime10.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_nir14.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_point10.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_point14.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_rgb10.cpp
${CMAKE_SOURCE_DIR}/external/lazperf/detail/field_rgb14.cpp
)
endif()

set(QGIS_CORE_SRCS ${QGIS_CORE_SRCS}
providers/copc/qgscopcprovider.cpp
pointcloud/qgseptdecoder.cpp
pointcloud/qgscopcpointcloudindex.cpp
)
set(QGIS_CORE_HDRS ${QGIS_CORE_HDRS}
providers/copc/qgscopcprovider.h
pointcloud/qgseptdecoder.h
pointcloud/qgscopcpointcloudindex.h
)

add_definitions( -DWITH_COPC )
endif()

if (APPLE)
Expand Down Expand Up @@ -2278,15 +2251,9 @@ if (WITH_EPT)
target_link_libraries(qgis_core
${ZSTD_LIBRARY}
)
if (LazPerf_FOUND)
target_link_libraries(qgis_core ${LazPerf_LIBRARY})
endif()
endif()

if (WITH_COPC)
target_link_libraries(qgis_core
${ZSTD_LIBRARY}
)
if (WITH_EPT OR WITH_COPC)
if (LazPerf_FOUND)
target_link_libraries(qgis_core ${LazPerf_LIBRARY})
endif()
Expand Down
55 changes: 33 additions & 22 deletions src/core/pointcloud/qgscopcpointcloudindex.cpp
Expand Up @@ -17,12 +17,6 @@

#include "qgscopcpointcloudindex.h"
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QTime>
#include <QtDebug>
#include <QQueue>

Expand Down Expand Up @@ -100,22 +94,20 @@ bool QgsCopcPointCloudIndex::loadSchema( const QString &filename )
// Attributes for COPC format
// COPC supports only PDRF 6, 7 and 8

// TODO: How to handle bitfields in LAZ

QgsPointCloudAttributeCollection attributes;
attributes.push_back( QgsPointCloudAttribute( "X", ( QgsPointCloudAttribute::DataType ) 9 ) );
attributes.push_back( QgsPointCloudAttribute( "Y", ( QgsPointCloudAttribute::DataType ) 9 ) );
attributes.push_back( QgsPointCloudAttribute( "Z", ( QgsPointCloudAttribute::DataType ) 9 ) );
attributes.push_back( QgsPointCloudAttribute( "Classification", ( QgsPointCloudAttribute::DataType ) 0 ) );
attributes.push_back( QgsPointCloudAttribute( "Intensity", ( QgsPointCloudAttribute::DataType ) 3 ) );
attributes.push_back( QgsPointCloudAttribute( "ReturnNumber", ( QgsPointCloudAttribute::DataType ) 0 ) );
attributes.push_back( QgsPointCloudAttribute( "NumberOfReturns", ( QgsPointCloudAttribute::DataType ) 0 ) );
attributes.push_back( QgsPointCloudAttribute( "ScanDirectionFlag", ( QgsPointCloudAttribute::DataType ) 0 ) );
attributes.push_back( QgsPointCloudAttribute( "EdgeOfFlightLine", ( QgsPointCloudAttribute::DataType ) 0 ) );
attributes.push_back( QgsPointCloudAttribute( "ScanAngleRank", ( QgsPointCloudAttribute::DataType ) 8 ) );
attributes.push_back( QgsPointCloudAttribute( "UserData", ( QgsPointCloudAttribute::DataType ) 0 ) );
attributes.push_back( QgsPointCloudAttribute( "PointSourceId", ( QgsPointCloudAttribute::DataType ) 3 ) );
attributes.push_back( QgsPointCloudAttribute( "GpsTime", ( QgsPointCloudAttribute::DataType ) 9 ) );
attributes.push_back( QgsPointCloudAttribute( "X", QgsPointCloudAttribute::Int32 ) );
attributes.push_back( QgsPointCloudAttribute( "Y", QgsPointCloudAttribute::Int32 ) );
attributes.push_back( QgsPointCloudAttribute( "Z", QgsPointCloudAttribute::Int32 ) );
attributes.push_back( QgsPointCloudAttribute( "Intensity", QgsPointCloudAttribute::UShort ) );
attributes.push_back( QgsPointCloudAttribute( "ReturnNumber", QgsPointCloudAttribute::Char ) );
attributes.push_back( QgsPointCloudAttribute( "NumberOfReturns", QgsPointCloudAttribute::Char ) );
attributes.push_back( QgsPointCloudAttribute( "ScanDirectionFlag", QgsPointCloudAttribute::Char ) );
attributes.push_back( QgsPointCloudAttribute( "EdgeOfFlightLine", QgsPointCloudAttribute::Char ) );
attributes.push_back( QgsPointCloudAttribute( "Classification", QgsPointCloudAttribute::Char ) );
attributes.push_back( QgsPointCloudAttribute( "ScanAngleRank", QgsPointCloudAttribute::Short ) );
attributes.push_back( QgsPointCloudAttribute( "UserData", QgsPointCloudAttribute::Char ) );
attributes.push_back( QgsPointCloudAttribute( "PointSourceId", QgsPointCloudAttribute::UShort ) );
attributes.push_back( QgsPointCloudAttribute( "GpsTime", QgsPointCloudAttribute::Double ) );

switch ( f.header().point_format_id )
{
Expand All @@ -136,7 +128,11 @@ bool QgsCopcPointCloudIndex::loadSchema( const QString &filename )
return false;
}

// TODO: add extrabyte attributes
QVector<QgsEptDecoder::ExtraBytesAttributeDetails> extrabyteAttributes = QgsEptDecoder::readExtraByteAttributes( file );
for ( QgsEptDecoder::ExtraBytesAttributeDetails attr : extrabyteAttributes )
{
attributes.push_back( QgsPointCloudAttribute( attr.attribute, attr.type ) );
}

setAttributes( attributes );

Expand Down Expand Up @@ -216,6 +212,21 @@ qint64 QgsCopcPointCloudIndex::pointCount() const

QVariant QgsCopcPointCloudIndex::metadataStatistic( const QString &attribute, QgsStatisticalSummary::Statistic statistic ) const
{
if ( attribute == QStringLiteral( "X" ) && statistic == QgsStatisticalSummary::Min )
return mExtent.xMinimum();
if ( attribute == QStringLiteral( "X" ) && statistic == QgsStatisticalSummary::Max )
return mExtent.xMaximum();

if ( attribute == QStringLiteral( "Y" ) && statistic == QgsStatisticalSummary::Min )
return mExtent.yMinimum();
if ( attribute == QStringLiteral( "Y" ) && statistic == QgsStatisticalSummary::Max )
return mExtent.yMaximum();

if ( attribute == QStringLiteral( "Z" ) && statistic == QgsStatisticalSummary::Min )
return mZMin;
if ( attribute == QStringLiteral( "Z" ) && statistic == QgsStatisticalSummary::Max )
return mZMax;

if ( !mMetadataStats.contains( attribute ) )
return QVariant();

Expand Down
2 changes: 1 addition & 1 deletion src/core/pointcloud/qgspointcloudindex.h
Expand Up @@ -262,7 +262,7 @@ class CORE_EXPORT QgsPointCloudIndex: public QObject
QgsDoubleRange nodeZRange( const IndexedPointCloudNode &node ) const;

//! Returns node's error in map units (used to determine in whether the node has enough detail for the current view)
virtual float nodeError( const IndexedPointCloudNode &n ) const;
float nodeError( const IndexedPointCloudNode &n ) const;

//! Returns scale
QgsVector3D scale() const;
Expand Down
13 changes: 6 additions & 7 deletions src/core/providers/copc/qgscopcprovider.cpp
Expand Up @@ -145,7 +145,7 @@ QgsCopcProvider *QgsCopcProviderMetadata::createProvider( const QString &uri, co
QList<QgsProviderSublayerDetails> QgsCopcProviderMetadata::querySublayers( const QString &uri, Qgis::SublayerQueryFlags, QgsFeedback * ) const
{
const QVariantMap parts = decodeUri( uri );
if ( parts.value( QStringLiteral( "isCopc" ), false ).toBool() )
if ( parts.value( QStringLiteral( "path" ) ).toString().endsWith( ".copc.laz", Qt::CaseSensitivity::CaseInsensitive ) )
{
QgsProviderSublayerDetails details;
details.setUri( uri );
Expand All @@ -164,8 +164,8 @@ int QgsCopcProviderMetadata::priorityForUri( const QString &uri ) const
{
const QVariantMap parts = decodeUri( uri );
const QFileInfo fi( parts.value( QStringLiteral( "path" ) ).toString() );
if ( fi.exists() && parts.value( QStringLiteral( "isCopc" ), false ).toBool() )
return 100;
if ( parts.value( QStringLiteral( "path" ) ).toString().endsWith( ".copc.laz", Qt::CaseSensitivity::CaseInsensitive ) )
return 101;

return 0;
}
Expand All @@ -174,7 +174,7 @@ QList<QgsMapLayerType> QgsCopcProviderMetadata::validLayerTypesForUri( const QSt
{
const QVariantMap parts = decodeUri( uri );
const QFileInfo fi( parts.value( QStringLiteral( "path" ) ).toString() );
if ( fi.exists() && parts.value( QStringLiteral( "isCopc" ), false ).toBool() )
if ( parts.value( QStringLiteral( "path" ) ).toString().endsWith( ".copc.laz", Qt::CaseSensitivity::CaseInsensitive ) )
return QList< QgsMapLayerType>() << QgsMapLayerType::PointCloudLayer;

return QList< QgsMapLayerType>();
Expand All @@ -189,7 +189,7 @@ bool QgsCopcProviderMetadata::uriIsBlocklisted( const QString &uri ) const
const QFileInfo fi( parts.value( QStringLiteral( "path" ) ).toString() );

// internal details only
if ( fi.exists() && parts.value( QStringLiteral( "isCopc" ), false ).toBool() )
if ( parts.value( QStringLiteral( "path" ) ).toString().endsWith( ".copc.laz", Qt::CaseSensitivity::CaseInsensitive ) )
return true;

return false;
Expand All @@ -200,7 +200,6 @@ QVariantMap QgsCopcProviderMetadata::decodeUri( const QString &uri ) const
const QString path = uri;
QVariantMap uriComponents;
uriComponents.insert( QStringLiteral( "path" ), path );
uriComponents.insert( QStringLiteral( "isCopc" ), uri.endsWith( ".copc.laz" ) );
return uriComponents;
}

Expand All @@ -215,7 +214,7 @@ QString QgsCopcProviderMetadata::filters( QgsProviderMetadata::FilterType type )
return QString();

case QgsProviderMetadata::FilterType::FilterPointCloud:
return QObject::tr( "COPC Point Clouds" ) + QStringLiteral( "COPC LAZ files (*.copc.laz *.COPC.LAZ)" );
return QObject::tr( "COPC Point Clouds" ) + QStringLiteral( " (*.copc.laz *.COPC.LAZ)" );
}
return QString();
}
Expand Down
4 changes: 0 additions & 4 deletions src/gui/providers/qgspointcloudsourceselect.cpp
Expand Up @@ -78,10 +78,6 @@ void QgsPointCloudSourceSelect::addButtonClicked()
// auto determine preferred provider for each path

const QList< QgsProviderRegistry::ProviderCandidateDetails > preferredProviders = QgsProviderRegistry::instance()->preferredProvidersForUri( mPath );
for ( QgsProviderRegistry::ProviderCandidateDetails p : preferredProviders )
{
qDebug() << p.metadata()->key();
}
// maybe we should raise an assert if preferredProviders size is 0 or >1? Play it safe for now...
if ( preferredProviders.empty() )
continue;
Expand Down
7 changes: 3 additions & 4 deletions tests/src/providers/CMakeLists.txt
Expand Up @@ -58,10 +58,9 @@ if (WITH_EPT)
add_qgis_test(testqgseptprovider.cpp MODULE provider LINKEDLIBRARIES qgis_core)
endif()

# TODO: test COPC
#if (WITH_COPC)
# add_qgis_test(testqgscopcprovider.cpp MODULE provider LINKEDLIBRARIES qgis_core)
#endif()
if (WITH_COPC)
add_qgis_test(testqgscopcprovider.cpp MODULE provider LINKEDLIBRARIES qgis_core)
endif()

if (WITH_PDAL)
include_directories(
Expand Down

0 comments on commit 7faea46

Please sign in to comment.