mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-22 00:06:12 -05:00
- Add tests for COPC
- Address some of Martin's reviews
This commit is contained in:
parent
458748cb4a
commit
7faea46b7b
@ -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()
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
@ -1976,52 +1946,55 @@ endif()
|
||||
if (WITH_COPC)
|
||||
include_directories(providers/copc)
|
||||
|
||||
include_directories(SYSTEM
|
||||
${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/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 (WITH_EPT OR WITH_COPC)
|
||||
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}
|
||||
pointcloud/qgseptdecoder.cpp
|
||||
)
|
||||
set(QGIS_CORE_HDRS ${QGIS_CORE_HDRS}
|
||||
pointcloud/qgseptdecoder.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
# Libtasn1 is for DER-encoded PKI ASN.1 parsing/extracting workarounds
|
||||
include_directories(SYSTEM
|
||||
@ -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()
|
||||
|
@ -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>
|
||||
|
||||
@ -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 )
|
||||
{
|
||||
@ -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 );
|
||||
|
||||
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 );
|
||||
@ -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;
|
||||
}
|
||||
@ -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>();
|
||||
@ -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;
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
627
tests/src/providers/testqgscopcprovider.cpp
Normal file
627
tests/src/providers/testqgscopcprovider.cpp
Normal file
@ -0,0 +1,627 @@
|
||||
/***************************************************************************
|
||||
testqgseptprovider.cpp
|
||||
--------------------------------------
|
||||
Date : November 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 <limits>
|
||||
|
||||
#include "qgstest.h"
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QApplication>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <fstream>
|
||||
#include <QVector>
|
||||
|
||||
//qgis includes...
|
||||
#include "qgis.h"
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsproviderregistry.h"
|
||||
#include "qgscopcprovider.h"
|
||||
#include "qgseptprovider.h"
|
||||
#include "qgspointcloudlayer.h"
|
||||
#include "qgspointcloudindex.h"
|
||||
#include "qgspointcloudlayerelevationproperties.h"
|
||||
#include "qgsprovidersublayerdetails.h"
|
||||
#include "qgsgeometry.h"
|
||||
#include "qgseptdecoder.h"
|
||||
|
||||
/**
|
||||
* \ingroup UnitTests
|
||||
* This is a unit test for the COPC provider
|
||||
*/
|
||||
class TestQgsCopcProvider : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();// will be called before the first testfunction is executed.
|
||||
void cleanupTestCase();// will be called after the last testfunction was executed.
|
||||
void init() {}// will be called before each testfunction is executed.
|
||||
void cleanup() {}// will be called after every testfunction.
|
||||
|
||||
void filters();
|
||||
void encodeUri();
|
||||
void decodeUri();
|
||||
void preferredUri();
|
||||
void layerTypesForUri();
|
||||
void uriIsBlocklisted();
|
||||
void querySublayers();
|
||||
void brokenPath();
|
||||
void validLayer();
|
||||
void validLayerWithCopcHierarchy();
|
||||
void attributes();
|
||||
void calculateZRange();
|
||||
void testIdentify_data();
|
||||
void testIdentify();
|
||||
// void testExtraBytesAttributesExtraction();
|
||||
// void testExtraBytesAttributesValues();
|
||||
void testPointCloudIndex();
|
||||
|
||||
private:
|
||||
QString mTestDataDir;
|
||||
QString mReport;
|
||||
};
|
||||
|
||||
//runs before all tests
|
||||
void TestQgsCopcProvider::initTestCase()
|
||||
{
|
||||
// init QGIS's paths - true means that all path will be inited from prefix
|
||||
QgsApplication::init();
|
||||
QgsApplication::initQgis();
|
||||
|
||||
mTestDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
|
||||
mReport = QStringLiteral( "<h1>COPC Provider Tests</h1>\n" );
|
||||
}
|
||||
|
||||
//runs after all tests
|
||||
void TestQgsCopcProvider::cleanupTestCase()
|
||||
{
|
||||
QgsApplication::exitQgis();
|
||||
const QString myReportFile = QDir::tempPath() + "/qgistest.html";
|
||||
QFile myFile( myReportFile );
|
||||
if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) )
|
||||
{
|
||||
QTextStream myQTextStream( &myFile );
|
||||
myQTextStream << mReport;
|
||||
myFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
void TestQgsCopcProvider::filters()
|
||||
{
|
||||
QgsProviderMetadata *metadata = QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "copc" ) );
|
||||
QVERIFY( metadata );
|
||||
|
||||
QCOMPARE( metadata->filters( QgsProviderMetadata::FilterType::FilterPointCloud ), QStringLiteral( "COPC Point Clouds (*.copc.laz *.COPC.LAZ)" ) );
|
||||
QCOMPARE( metadata->filters( QgsProviderMetadata::FilterType::FilterVector ), QString() );
|
||||
|
||||
const QString registryPointCloudFilters = QgsProviderRegistry::instance()->filePointCloudFilters();
|
||||
QVERIFY( registryPointCloudFilters.contains( "(*.copc.laz *.COPC.LAZ)" ) );
|
||||
}
|
||||
|
||||
void TestQgsCopcProvider::encodeUri()
|
||||
{
|
||||
QgsProviderMetadata *metadata = QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "copc" ) );
|
||||
QVERIFY( metadata );
|
||||
|
||||
QVariantMap parts;
|
||||
parts.insert( QStringLiteral( "path" ), QStringLiteral( "/home/point_clouds/dataset.copc.laz" ) );
|
||||
QCOMPARE( metadata->encodeUri( parts ), QStringLiteral( "/home/point_clouds/dataset.copc.laz" ) );
|
||||
}
|
||||
|
||||
void TestQgsCopcProvider::decodeUri()
|
||||
{
|
||||
QgsProviderMetadata *metadata = QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "copc" ) );
|
||||
QVERIFY( metadata );
|
||||
|
||||
const QVariantMap parts = metadata->decodeUri( QStringLiteral( "/home/point_clouds/dataset.copc.laz" ) );
|
||||
QCOMPARE( parts.value( QStringLiteral( "path" ) ).toString(), QStringLiteral( "/home/point_clouds/dataset.copc.laz" ) );
|
||||
}
|
||||
|
||||
void TestQgsCopcProvider::preferredUri()
|
||||
{
|
||||
QgsProviderMetadata *copcMetadata = QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "copc" ) );
|
||||
QVERIFY( copcMetadata->capabilities() & QgsProviderMetadata::PriorityForUri );
|
||||
|
||||
// test that COPC is the preferred provider for .copc.laz uris
|
||||
QList<QgsProviderRegistry::ProviderCandidateDetails> candidates = QgsProviderRegistry::instance()->preferredProvidersForUri( QStringLiteral( "/home/test/dataset.copc.laz" ) );
|
||||
QCOMPARE( candidates.size(), 1 );
|
||||
QCOMPARE( candidates.at( 0 ).metadata()->key(), QStringLiteral( "copc" ) );
|
||||
QCOMPARE( candidates.at( 0 ).layerTypes(), QList< QgsMapLayerType >() << QgsMapLayerType::PointCloudLayer );
|
||||
|
||||
candidates = QgsProviderRegistry::instance()->preferredProvidersForUri( QStringLiteral( "/home/test/dataset.COPC.LAZ" ) );
|
||||
QCOMPARE( candidates.size(), 1 );
|
||||
QCOMPARE( candidates.at( 0 ).metadata()->key(), QStringLiteral( "copc" ) );
|
||||
QCOMPARE( candidates.at( 0 ).layerTypes(), QList< QgsMapLayerType >() << QgsMapLayerType::PointCloudLayer );
|
||||
|
||||
QVERIFY( !QgsProviderRegistry::instance()->shouldDeferUriForOtherProviders( QStringLiteral( "/home/test/dataset.copc.laz" ), QStringLiteral( "copc" ) ) );
|
||||
QVERIFY( QgsProviderRegistry::instance()->shouldDeferUriForOtherProviders( QStringLiteral( "/home/test/dataset.copc.laz" ), QStringLiteral( "ogr" ) ) );
|
||||
}
|
||||
|
||||
void TestQgsCopcProvider::layerTypesForUri()
|
||||
{
|
||||
QgsProviderMetadata *copcMetadata = QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "copc" ) );
|
||||
QVERIFY( copcMetadata->capabilities() & QgsProviderMetadata::LayerTypesForUri );
|
||||
|
||||
QCOMPARE( copcMetadata->validLayerTypesForUri( QStringLiteral( "/home/test/cloud.copc.laz" ) ), QList< QgsMapLayerType >() << QgsMapLayerType::PointCloudLayer );
|
||||
QCOMPARE( copcMetadata->validLayerTypesForUri( QStringLiteral( "/home/test/ept.json" ) ), QList< QgsMapLayerType >() );
|
||||
}
|
||||
|
||||
void TestQgsCopcProvider::uriIsBlocklisted()
|
||||
{
|
||||
QVERIFY( !QgsProviderRegistry::instance()->uriIsBlocklisted( QStringLiteral( "/home/test/ept.json" ) ) );
|
||||
QVERIFY( QgsProviderRegistry::instance()->uriIsBlocklisted( QStringLiteral( "/home/test/dataset.copc.laz" ) ) );
|
||||
}
|
||||
|
||||
void TestQgsCopcProvider::querySublayers()
|
||||
{
|
||||
// test querying sub layers for a ept layer
|
||||
QgsProviderMetadata *eptMetadata = QgsProviderRegistry::instance()->providerMetadata( QStringLiteral( "copc" ) );
|
||||
|
||||
// invalid uri
|
||||
QList< QgsProviderSublayerDetails >res = eptMetadata->querySublayers( QString() );
|
||||
QVERIFY( res.empty() );
|
||||
|
||||
// not a copc layer
|
||||
res = eptMetadata->querySublayers( QString( TEST_DATA_DIR ) + "/lines.shp" );
|
||||
QVERIFY( res.empty() );
|
||||
|
||||
// valid copc layer
|
||||
res = eptMetadata->querySublayers( mTestDataDir + "/point_clouds/copc/sunshine-coast.copc.laz" );
|
||||
QCOMPARE( res.count(), 1 );
|
||||
QCOMPARE( res.at( 0 ).name(), QStringLiteral( "sunshine-coast.copc" ) );
|
||||
QCOMPARE( res.at( 0 ).uri(), mTestDataDir + "/point_clouds/copc/sunshine-coast.copc.laz" );
|
||||
QCOMPARE( res.at( 0 ).providerKey(), QStringLiteral( "copc" ) );
|
||||
QCOMPARE( res.at( 0 ).type(), QgsMapLayerType::PointCloudLayer );
|
||||
|
||||
// make sure result is valid to load layer from
|
||||
const QgsProviderSublayerDetails::LayerOptions options{ QgsCoordinateTransformContext() };
|
||||
std::unique_ptr< QgsPointCloudLayer > ml( qgis::down_cast< QgsPointCloudLayer * >( res.at( 0 ).toLayer( options ) ) );
|
||||
QVERIFY( ml->isValid() );
|
||||
}
|
||||
|
||||
void TestQgsCopcProvider::brokenPath()
|
||||
{
|
||||
// test loading a bad layer URI
|
||||
std::unique_ptr< QgsPointCloudLayer > layer = std::make_unique< QgsPointCloudLayer >( QStringLiteral( "not valid" ), QStringLiteral( "layer" ), QStringLiteral( "copc" ) );
|
||||
QVERIFY( !layer->isValid() );
|
||||
}
|
||||
|
||||
void TestQgsCopcProvider::validLayer()
|
||||
{
|
||||
std::unique_ptr< QgsPointCloudLayer > layer = std::make_unique< QgsPointCloudLayer >( mTestDataDir + QStringLiteral( "point_clouds/copc/sunshine-coast.copc.laz" ), QStringLiteral( "layer" ), QStringLiteral( "copc" ) );
|
||||
QVERIFY( layer->isValid() );
|
||||
|
||||
QCOMPARE( layer->crs().authid(), QStringLiteral( "EPSG:28356" ) );
|
||||
QGSCOMPARENEAR( layer->extent().xMinimum(), 498062.0, 0.1 );
|
||||
QGSCOMPARENEAR( layer->extent().yMinimum(), 7050992.84, 0.1 );
|
||||
QGSCOMPARENEAR( layer->extent().xMaximum(), 498067.39, 0.1 );
|
||||
QGSCOMPARENEAR( layer->extent().yMaximum(), 7050997.04, 0.1 );
|
||||
QCOMPARE( layer->dataProvider()->polygonBounds().asWkt( 0 ), QStringLiteral( "Polygon ((498062 7050993, 498067 7050993, 498067 7050997, 498062 7050997, 498062 7050993))" ) );
|
||||
QCOMPARE( layer->dataProvider()->pointCount(), 253 );
|
||||
QCOMPARE( layer->pointCount(), 253 );
|
||||
|
||||
QVERIFY( layer->dataProvider()->index() );
|
||||
// all hierarchy is stored in a single node
|
||||
QVERIFY( layer->dataProvider()->index()->hasNode( IndexedPointCloudNode::fromString( "0-0-0-0" ) ) );
|
||||
QVERIFY( !layer->dataProvider()->index()->hasNode( IndexedPointCloudNode::fromString( "1-0-0-0" ) ) );
|
||||
}
|
||||
|
||||
#include "qgscopcpointcloudindex.h"
|
||||
|
||||
void TestQgsCopcProvider::validLayerWithCopcHierarchy()
|
||||
{
|
||||
std::unique_ptr< QgsPointCloudLayer > layer = std::make_unique< QgsPointCloudLayer >( mTestDataDir + QStringLiteral( "point_clouds/copc/lone-star.copc.laz" ), QStringLiteral( "layer" ), QStringLiteral( "copc" ) );
|
||||
QVERIFY( layer->isValid() );
|
||||
|
||||
QGSCOMPARENEAR( layer->extent().xMinimum(), 515368.6022, 0.1 );
|
||||
QGSCOMPARENEAR( layer->extent().yMinimum(), 4918340.364, 0.1 );
|
||||
QGSCOMPARENEAR( layer->extent().xMaximum(), 515401.043, 0.1 );
|
||||
QGSCOMPARENEAR( layer->extent().yMaximum(), 4918381.124, 0.1 );
|
||||
|
||||
QVERIFY( layer->dataProvider()->index() );
|
||||
// all hierarchy is stored in multiple nodes
|
||||
QVERIFY( layer->dataProvider()->index()->hasNode( IndexedPointCloudNode::fromString( "1-1-1-0" ) ) );
|
||||
QVERIFY( layer->dataProvider()->index()->hasNode( IndexedPointCloudNode::fromString( "2-3-3-1" ) ) );
|
||||
}
|
||||
|
||||
void TestQgsCopcProvider::attributes()
|
||||
{
|
||||
std::unique_ptr< QgsPointCloudLayer > layer = std::make_unique< QgsPointCloudLayer >( mTestDataDir + QStringLiteral( "point_clouds/copc/sunshine-coast.copc.laz" ), QStringLiteral( "layer" ), QStringLiteral( "copc" ) );
|
||||
QVERIFY( layer->isValid() );
|
||||
|
||||
const QgsPointCloudAttributeCollection attributes = layer->attributes();
|
||||
QCOMPARE( attributes.count(), 16 );
|
||||
QCOMPARE( attributes.at( 0 ).name(), QStringLiteral( "X" ) );
|
||||
QCOMPARE( attributes.at( 0 ).type(), QgsPointCloudAttribute::Int32 );
|
||||
QCOMPARE( attributes.at( 1 ).name(), QStringLiteral( "Y" ) );
|
||||
QCOMPARE( attributes.at( 1 ).type(), QgsPointCloudAttribute::Int32 );
|
||||
QCOMPARE( attributes.at( 2 ).name(), QStringLiteral( "Z" ) );
|
||||
QCOMPARE( attributes.at( 2 ).type(), QgsPointCloudAttribute::Int32 );
|
||||
QCOMPARE( attributes.at( 3 ).name(), QStringLiteral( "Intensity" ) );
|
||||
QCOMPARE( attributes.at( 3 ).type(), QgsPointCloudAttribute::UShort );
|
||||
QCOMPARE( attributes.at( 4 ).name(), QStringLiteral( "ReturnNumber" ) );
|
||||
QCOMPARE( attributes.at( 4 ).type(), QgsPointCloudAttribute::Char );
|
||||
QCOMPARE( attributes.at( 5 ).name(), QStringLiteral( "NumberOfReturns" ) );
|
||||
QCOMPARE( attributes.at( 5 ).type(), QgsPointCloudAttribute::Char );
|
||||
QCOMPARE( attributes.at( 6 ).name(), QStringLiteral( "ScanDirectionFlag" ) );
|
||||
QCOMPARE( attributes.at( 6 ).type(), QgsPointCloudAttribute::Char );
|
||||
QCOMPARE( attributes.at( 7 ).name(), QStringLiteral( "EdgeOfFlightLine" ) );
|
||||
QCOMPARE( attributes.at( 7 ).type(), QgsPointCloudAttribute::Char );
|
||||
QCOMPARE( attributes.at( 8 ).name(), QStringLiteral( "Classification" ) );
|
||||
QCOMPARE( attributes.at( 8 ).type(), QgsPointCloudAttribute::Char );
|
||||
QCOMPARE( attributes.at( 9 ).name(), QStringLiteral( "ScanAngleRank" ) );
|
||||
QCOMPARE( attributes.at( 9 ).type(), QgsPointCloudAttribute::Short );
|
||||
QCOMPARE( attributes.at( 10 ).name(), QStringLiteral( "UserData" ) );
|
||||
QCOMPARE( attributes.at( 10 ).type(), QgsPointCloudAttribute::Char );
|
||||
QCOMPARE( attributes.at( 11 ).name(), QStringLiteral( "PointSourceId" ) );
|
||||
QCOMPARE( attributes.at( 11 ).type(), QgsPointCloudAttribute::UShort );
|
||||
QCOMPARE( attributes.at( 12 ).name(), QStringLiteral( "GpsTime" ) );
|
||||
QCOMPARE( attributes.at( 12 ).type(), QgsPointCloudAttribute::Double );
|
||||
QCOMPARE( attributes.at( 13 ).name(), QStringLiteral( "Red" ) );
|
||||
QCOMPARE( attributes.at( 13 ).type(), QgsPointCloudAttribute::UShort );
|
||||
QCOMPARE( attributes.at( 14 ).name(), QStringLiteral( "Green" ) );
|
||||
QCOMPARE( attributes.at( 14 ).type(), QgsPointCloudAttribute::UShort );
|
||||
QCOMPARE( attributes.at( 15 ).name(), QStringLiteral( "Blue" ) );
|
||||
QCOMPARE( attributes.at( 15 ).type(), QgsPointCloudAttribute::UShort );
|
||||
}
|
||||
|
||||
void TestQgsCopcProvider::calculateZRange()
|
||||
{
|
||||
std::unique_ptr< QgsPointCloudLayer > layer = std::make_unique< QgsPointCloudLayer >( mTestDataDir + QStringLiteral( "point_clouds/copc/sunshine-coast.copc.laz" ), QStringLiteral( "layer" ), QStringLiteral( "copc" ) );
|
||||
QVERIFY( layer->isValid() );
|
||||
|
||||
QgsDoubleRange range = layer->elevationProperties()->calculateZRange( layer.get() );
|
||||
QGSCOMPARENEAR( range.lower(), 74.34, 0.01 );
|
||||
QGSCOMPARENEAR( range.upper(), 80.02, 0.01 );
|
||||
|
||||
static_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->setZScale( 2 );
|
||||
static_cast< QgsPointCloudLayerElevationProperties * >( layer->elevationProperties() )->setZOffset( 0.5 );
|
||||
|
||||
range = layer->elevationProperties()->calculateZRange( layer.get() );
|
||||
QGSCOMPARENEAR( range.lower(), 149.18, 0.01 );
|
||||
QGSCOMPARENEAR( range.upper(), 160.54, 0.01 );
|
||||
}
|
||||
|
||||
void TestQgsCopcProvider::testIdentify_data()
|
||||
{
|
||||
QTest::addColumn<QString>( "datasetPath" );
|
||||
|
||||
QTest::newRow( "copc" ) << mTestDataDir + QStringLiteral( "point_clouds/copc/sunshine-coast.copc.laz" );
|
||||
}
|
||||
|
||||
void TestQgsCopcProvider::testIdentify()
|
||||
{
|
||||
QFETCH( QString, datasetPath );
|
||||
|
||||
std::unique_ptr< QgsPointCloudLayer > layer = std::make_unique< QgsPointCloudLayer >( datasetPath, QStringLiteral( "layer" ), QStringLiteral( "copc" ) );
|
||||
|
||||
QVERIFY( layer->isValid() );
|
||||
|
||||
// identify 1 point click (rectangular point shape)
|
||||
{
|
||||
QgsPolygonXY polygon;
|
||||
QVector<QgsPointXY> ring;
|
||||
ring.push_back( QgsPointXY( 498062.50018404237926, 7050996.5845294082537 ) );
|
||||
ring.push_back( QgsPointXY( 498062.5405028705718, 7050996.5845294082537 ) );
|
||||
ring.push_back( QgsPointXY( 498062.5405028705718, 7050996.6248482363299 ) );
|
||||
ring.push_back( QgsPointXY( 498062.50018404237926, 7050996.6248482363299 ) );
|
||||
ring.push_back( QgsPointXY( 498062.50018404237926, 7050996.5845294082537 ) );
|
||||
polygon.push_back( ring );
|
||||
const float maxErrorInMapCoords = 0.0022857920266687870026;
|
||||
QVector<QMap<QString, QVariant>> points = layer->dataProvider()->identify( maxErrorInMapCoords, QgsGeometry::fromPolygonXY( polygon ) );
|
||||
QCOMPARE( points.size(), 1 );
|
||||
const QMap<QString, QVariant> identifiedPoint = points[0];
|
||||
QMap<QString, QVariant> expected;
|
||||
|
||||
expected[ QStringLiteral( "Blue" ) ] = 0;
|
||||
expected[ QStringLiteral( "Classification" ) ] = 2;
|
||||
expected[ QStringLiteral( "EdgeOfFlightLine" ) ] = 0;
|
||||
expected[ QStringLiteral( "GpsTime" ) ] = 268793.37257748609409;
|
||||
expected[ QStringLiteral( "Green" ) ] = 0;
|
||||
expected[ QStringLiteral( "Intensity" ) ] = 1765;
|
||||
expected[ QStringLiteral( "NumberOfReturns" ) ] = 1;
|
||||
expected[ QStringLiteral( "PointSourceId" ) ] = 7041;
|
||||
expected[ QStringLiteral( "Red" ) ] = 0;
|
||||
expected[ QStringLiteral( "ReturnNumber" ) ] = 1;
|
||||
expected[ QStringLiteral( "ScanAngleRank" ) ] = -59;
|
||||
expected[ QStringLiteral( "ScanDirectionFlag" ) ] = 1;
|
||||
expected[ QStringLiteral( "UserData" ) ] = 17;
|
||||
expected[ QStringLiteral( "X" ) ] = 498062.52;
|
||||
expected[ QStringLiteral( "Y" ) ] = 7050996.61;
|
||||
expected[ QStringLiteral( "Z" ) ] = 75.0;
|
||||
QVERIFY( identifiedPoint == expected );
|
||||
}
|
||||
|
||||
// identify 1 point (circular point shape)
|
||||
{
|
||||
QPolygonF polygon;
|
||||
polygon.push_back( QPointF( 498066.28873652569018, 7050994.9709538575262 ) );
|
||||
polygon.push_back( QPointF( 498066.21890226693358, 7050995.0112726856023 ) );
|
||||
polygon.push_back( QPointF( 498066.21890226693358, 7050995.0919103417546 ) );
|
||||
polygon.push_back( QPointF( 498066.28873652569018, 7050995.1322291698307 ) );
|
||||
polygon.push_back( QPointF( 498066.35857078444678, 7050995.0919103417546 ) );
|
||||
polygon.push_back( QPointF( 498066.35857078444678, 7050995.0112726856023 ) );
|
||||
polygon.push_back( QPointF( 498066.28873652569018, 7050994.9709538575262 ) );
|
||||
const float maxErrorInMapCoords = 0.0091431681066751480103;
|
||||
const QVector<QMap<QString, QVariant>> identifiedPoints = layer->dataProvider()->identify( maxErrorInMapCoords, QgsGeometry::fromQPolygonF( polygon ) );
|
||||
QVector<QMap<QString, QVariant>> expected;
|
||||
{
|
||||
QMap<QString, QVariant> point;
|
||||
point[ QStringLiteral( "Blue" ) ] = "0" ;
|
||||
point[ QStringLiteral( "Classification" ) ] = "2" ;
|
||||
point[ QStringLiteral( "EdgeOfFlightLine" ) ] = "0" ;
|
||||
point[ QStringLiteral( "GpsTime" ) ] = "268793.3373408913" ;
|
||||
point[ QStringLiteral( "Green" ) ] = "0" ;
|
||||
point[ QStringLiteral( "Intensity" ) ] = "278" ;
|
||||
point[ QStringLiteral( "NumberOfReturns" ) ] = "1" ;
|
||||
point[ QStringLiteral( "PointSourceId" ) ] = "7041" ;
|
||||
point[ QStringLiteral( "Red" ) ] = "0" ;
|
||||
point[ QStringLiteral( "ReturnNumber" ) ] = "1" ;
|
||||
point[ QStringLiteral( "ScanAngleRank" ) ] = "-59" ;
|
||||
point[ QStringLiteral( "ScanDirectionFlag" ) ] = "1" ;
|
||||
point[ QStringLiteral( "UserData" ) ] = "17" ;
|
||||
point[ QStringLiteral( "X" ) ] = "498066.27" ;
|
||||
point[ QStringLiteral( "Y" ) ] = "7050995.06" ;
|
||||
point[ QStringLiteral( "Z" ) ] = "74.60" ;
|
||||
expected.push_back( point );
|
||||
}
|
||||
|
||||
// compare values using toDouble() so that fuzzy comparison is used in case of
|
||||
// tiny rounding errors (e.g. 74.6 vs 74.60000000000001)
|
||||
QCOMPARE( identifiedPoints.count(), 1 );
|
||||
const QStringList keys = expected[0].keys();
|
||||
for ( const QString &k : keys )
|
||||
QCOMPARE( identifiedPoints[0][k].toDouble(), expected[0][k].toDouble() );
|
||||
}
|
||||
|
||||
// test rectangle selection
|
||||
{
|
||||
QPolygonF polygon;
|
||||
polygon.push_back( QPointF( 498063.24382022250211, 7050996.8638040581718 ) );
|
||||
polygon.push_back( QPointF( 498063.02206666755956, 7050996.8638040581718 ) );
|
||||
polygon.push_back( QPointF( 498063.02206666755956, 7050996.6360026793554 ) );
|
||||
polygon.push_back( QPointF( 498063.24382022250211, 7050996.6360026793554 ) );
|
||||
polygon.push_back( QPointF( 498063.24382022250211, 7050996.8638040581718 ) );
|
||||
|
||||
const float maxErrorInMapCoords = 0.0022857920266687870026;
|
||||
const QVector<QMap<QString, QVariant>> identifiedPoints = layer->dataProvider()->identify( maxErrorInMapCoords, QgsGeometry::fromQPolygonF( polygon ) );
|
||||
QVector<QMap<QString, QVariant>> expected;
|
||||
{
|
||||
QMap<QString, QVariant> point;
|
||||
point[ QStringLiteral( "Blue" ) ] = "0" ;
|
||||
point[ QStringLiteral( "Classification" ) ] = "2" ;
|
||||
point[ QStringLiteral( "EdgeOfFlightLine" ) ] = "0" ;
|
||||
point[ QStringLiteral( "GpsTime" ) ] = "268793.3813974548" ;
|
||||
point[ QStringLiteral( "Green" ) ] = "0" ;
|
||||
point[ QStringLiteral( "Intensity" ) ] = "1142" ;
|
||||
point[ QStringLiteral( "NumberOfReturns" ) ] = "1" ;
|
||||
point[ QStringLiteral( "PointSourceId" ) ] = "7041" ;
|
||||
point[ QStringLiteral( "Red" ) ] = "0" ;
|
||||
point[ QStringLiteral( "ReturnNumber" ) ] = "1" ;
|
||||
point[ QStringLiteral( "ScanAngleRank" ) ] = "-59" ;
|
||||
point[ QStringLiteral( "ScanDirectionFlag" ) ] = "1" ;
|
||||
point[ QStringLiteral( "UserData" ) ] = "17" ;
|
||||
point[ QStringLiteral( "X" ) ] = "498063.14" ;
|
||||
point[ QStringLiteral( "Y" ) ] = "7050996.79" ;
|
||||
point[ QStringLiteral( "Z" ) ] = "74.89" ;
|
||||
expected.push_back( point );
|
||||
}
|
||||
{
|
||||
QMap<QString, QVariant> point;
|
||||
point[ QStringLiteral( "Blue" ) ] = "0" ;
|
||||
point[ QStringLiteral( "Classification" ) ] = "3" ;
|
||||
point[ QStringLiteral( "EdgeOfFlightLine" ) ] = "0" ;
|
||||
point[ QStringLiteral( "GpsTime" ) ] = "269160.5176644815" ;
|
||||
point[ QStringLiteral( "Green" ) ] = "0" ;
|
||||
point[ QStringLiteral( "Intensity" ) ] = "1631" ;
|
||||
point[ QStringLiteral( "NumberOfReturns" ) ] = "1" ;
|
||||
point[ QStringLiteral( "PointSourceId" ) ] = "7042" ;
|
||||
point[ QStringLiteral( "Red" ) ] = "0" ;
|
||||
point[ QStringLiteral( "ReturnNumber" ) ] = "1" ;
|
||||
point[ QStringLiteral( "ScanAngleRank" ) ] = "48" ;
|
||||
point[ QStringLiteral( "ScanDirectionFlag" ) ] = "1" ;
|
||||
point[ QStringLiteral( "UserData" ) ] = "17" ;
|
||||
point[ QStringLiteral( "X" ) ] = "498063.11" ;
|
||||
point[ QStringLiteral( "Y" ) ] = "7050996.75" ;
|
||||
point[ QStringLiteral( "Z" ) ] = "74.90" ;
|
||||
expected.push_back( point );
|
||||
}
|
||||
|
||||
QVERIFY( expected.size() == identifiedPoints.size() );
|
||||
|
||||
const QStringList keys = expected[0].keys();
|
||||
for ( int i = 0; i < expected.size(); ++i )
|
||||
{
|
||||
for ( const QString &k : keys )
|
||||
QCOMPARE( identifiedPoints[i][k].toDouble(), expected[i][k].toDouble() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fix extrabytes tests
|
||||
//void TestQgsCopcProvider::testExtraBytesAttributesExtraction()
|
||||
//{
|
||||
// {
|
||||
// QString dataPath = mTestDataDir + QStringLiteral( "point_clouds/copc/extrabytes-dataset.copc.laz" );
|
||||
// std::ifstream file( dataPath.toStdString(), std::ios::binary );
|
||||
// QVector<QgsEptDecoder::ExtraBytesAttributeDetails> attributes = QgsEptDecoder::readExtraByteAttributes<std::ifstream>( file );
|
||||
// for ( QgsEptDecoder::ExtraBytesAttributeDetails attr : attributes )
|
||||
// {
|
||||
// qDebug() << attr.attribute << " " << attr.type << " " << attr.size << " " << attr.offset;
|
||||
// }
|
||||
// QCOMPARE( attributes.size(), 4 );
|
||||
|
||||
// QCOMPARE( attributes[0].attribute, QStringLiteral( "Amplitude" ) );
|
||||
// QCOMPARE( attributes[1].attribute, QStringLiteral( "Reflectance" ) );
|
||||
// QCOMPARE( attributes[2].attribute, QStringLiteral( "ClassFlags" ) );
|
||||
// QCOMPARE( attributes[3].attribute, QStringLiteral( "Deviation" ) );
|
||||
|
||||
// QCOMPARE( attributes[0].type, QgsPointCloudAttribute::Float );
|
||||
// QCOMPARE( attributes[1].type, QgsPointCloudAttribute::Float );
|
||||
// QCOMPARE( attributes[2].type, QgsPointCloudAttribute::UChar );
|
||||
// QCOMPARE( attributes[3].type, QgsPointCloudAttribute::Float );
|
||||
|
||||
// QCOMPARE( attributes[0].size, 4 );
|
||||
// QCOMPARE( attributes[1].size, 4 );
|
||||
// QCOMPARE( attributes[2].size, 1 );
|
||||
// QCOMPARE( attributes[3].size, 4 );
|
||||
|
||||
// QCOMPARE( attributes[0].offset, 43 );
|
||||
// QCOMPARE( attributes[1].offset, 39 );
|
||||
// QCOMPARE( attributes[2].offset, 38 );
|
||||
// QCOMPARE( attributes[3].offset, 34 );
|
||||
// }
|
||||
|
||||
// {
|
||||
// QString dataPath = mTestDataDir + QStringLiteral( "point_clouds/copc/no-extrabytes-dataset.copc.laz" );
|
||||
// std::ifstream file( dataPath.toStdString(), std::ios::binary );
|
||||
// QVector<QgsEptDecoder::ExtraBytesAttributeDetails> attributes = QgsEptDecoder::readExtraByteAttributes<std::ifstream>( file );
|
||||
// QCOMPARE( attributes.size(), 0 );
|
||||
// }
|
||||
//}
|
||||
|
||||
//void TestQgsCopcProvider::testExtraBytesAttributesValues()
|
||||
//{
|
||||
// QString dataPath = mTestDataDir + QStringLiteral( "point_clouds/copc/extrabytes-dataset.copc.laz" );
|
||||
// std::unique_ptr< QgsPointCloudLayer > layer = std::make_unique< QgsPointCloudLayer >( dataPath, QStringLiteral( "layer" ), QStringLiteral( "copc" ) );
|
||||
// QVERIFY( layer->isValid() );
|
||||
// {
|
||||
// for ( QgsPointCloudAttribute attr : layer->attributes().attributes() )
|
||||
// {
|
||||
// qDebug() << attr.name() << " " << attr.type();
|
||||
// }
|
||||
// const float maxErrorInMapCoords = 0.0015207174f;
|
||||
// QPolygonF polygon;
|
||||
// polygon.push_back( QPointF( 527919.2459517354, 6210983.5918774214 ) );
|
||||
// polygon.push_back( QPointF( 527919.0742796324, 6210983.5918774214 ) );
|
||||
// polygon.push_back( QPointF( 527919.0742796324, 6210983.4383113598 ) );
|
||||
// polygon.push_back( QPointF( 527919.2459517354, 6210983.4383113598 ) );
|
||||
// polygon.push_back( QPointF( 527919.2459517354, 6210983.5918774214 ) );
|
||||
|
||||
// const QVector<QMap<QString, QVariant>> identifiedPoints = layer->dataProvider()->identify( maxErrorInMapCoords, QgsGeometry::fromQPolygonF( polygon ) );
|
||||
|
||||
// QVector<QMap<QString, QVariant>> expectedPoints;
|
||||
// {
|
||||
// QMap<QString, QVariant> point;
|
||||
// point[ QStringLiteral( "Amplitude" ) ] = "4.409999847412109" ;
|
||||
// point[ QStringLiteral( "Blue" ) ] = "0" ;
|
||||
// point[ QStringLiteral( "ClassFlags" ) ] = "0" ;
|
||||
// point[ QStringLiteral( "Classification" ) ] = "5" ;
|
||||
// point[ QStringLiteral( "Deviation" ) ] = "2" ;
|
||||
// point[ QStringLiteral( "EdgeOfFlightLine" ) ] = "0" ;
|
||||
// point[ QStringLiteral( "GpsTime" ) ] = "302522582.235838" ;
|
||||
// point[ QStringLiteral( "Green" ) ] = "0" ;
|
||||
// point[ QStringLiteral( "Intensity" ) ] = "441" ;
|
||||
// point[ QStringLiteral( "NumberOfReturns" ) ] = "3" ;
|
||||
// point[ QStringLiteral( "PointSourceId" ) ] = "15017" ;
|
||||
// point[ QStringLiteral( "Red" ) ] = "0" ;
|
||||
// point[ QStringLiteral( "Reflectance" ) ] = "-17.829999923706055" ;
|
||||
// point[ QStringLiteral( "ReturnNumber" ) ] = "2" ;
|
||||
// point[ QStringLiteral( "ScanAngleRank" ) ] = "-6" ;
|
||||
// point[ QStringLiteral( "ScanDirectionFlag" ) ] = "0" ;
|
||||
// point[ QStringLiteral( "UserData" ) ] = "0" ;
|
||||
// point[ QStringLiteral( "X" ) ] = "527919.18" ;
|
||||
// point[ QStringLiteral( "Y" ) ] = "6210983.47" ;
|
||||
// point[ QStringLiteral( "Z" ) ] = "149.341" ;
|
||||
// expectedPoints.push_back( point );
|
||||
// }
|
||||
// {
|
||||
// QMap<QString, QVariant> point;
|
||||
// point[ QStringLiteral( "Amplitude" ) ] = "14.170000076293945" ;
|
||||
// point[ QStringLiteral( "Blue" ) ] = "0" ;
|
||||
// point[ QStringLiteral( "ClassFlags" ) ] = "0" ;
|
||||
// point[ QStringLiteral( "Classification" ) ] = "2" ;
|
||||
// point[ QStringLiteral( "Deviation" ) ] = "0" ;
|
||||
// point[ QStringLiteral( "EdgeOfFlightLine" ) ] = "0" ;
|
||||
// point[ QStringLiteral( "GpsTime" ) ] = "302522582.235839" ;
|
||||
// point[ QStringLiteral( "Green" ) ] = "0" ;
|
||||
// point[ QStringLiteral( "Intensity" ) ] = "1417" ;
|
||||
// point[ QStringLiteral( "NumberOfReturns" ) ] = "3" ;
|
||||
// point[ QStringLiteral( "PointSourceId" ) ] = "15017" ;
|
||||
// point[ QStringLiteral( "Red" ) ] = "0" ;
|
||||
// point[ QStringLiteral( "Reflectance" ) ] = "-8.050000190734863" ;
|
||||
// point[ QStringLiteral( "ReturnNumber" ) ] = "3" ;
|
||||
// point[ QStringLiteral( "ScanAngleRank" ) ] = "-6" ;
|
||||
// point[ QStringLiteral( "ScanDirectionFlag" ) ] = "0" ;
|
||||
// point[ QStringLiteral( "UserData" ) ] = "0" ;
|
||||
// point[ QStringLiteral( "X" ) ] = "527919.11" ;
|
||||
// point[ QStringLiteral( "Y" ) ] = "6210983.55" ;
|
||||
// point[ QStringLiteral( "Z" ) ] = "147.111" ;
|
||||
// expectedPoints.push_back( point );
|
||||
// }
|
||||
|
||||
// QCOMPARE( identifiedPoints, expectedPoints );
|
||||
// }
|
||||
//}
|
||||
|
||||
void TestQgsCopcProvider::testPointCloudIndex()
|
||||
{
|
||||
std::unique_ptr< QgsPointCloudLayer > layer = std::make_unique< QgsPointCloudLayer >( mTestDataDir + QStringLiteral( "point_clouds/copc/lone-star.copc.laz" ), QStringLiteral( "layer" ), QStringLiteral( "copc" ) );
|
||||
QVERIFY( layer->isValid() );
|
||||
|
||||
QgsPointCloudIndex *index = layer->dataProvider()->index();
|
||||
QVERIFY( index->isValid() );
|
||||
|
||||
QCOMPARE( index->nodePointCount( IndexedPointCloudNode::fromString( QStringLiteral( "0-0-0-0" ) ) ), 56721 );
|
||||
QCOMPARE( index->nodePointCount( IndexedPointCloudNode::fromString( QStringLiteral( "1-1-1-1" ) ) ), -1 );
|
||||
QCOMPARE( index->nodePointCount( IndexedPointCloudNode::fromString( QStringLiteral( "2-3-3-1" ) ) ), 446 );
|
||||
QCOMPARE( index->nodePointCount( IndexedPointCloudNode::fromString( QStringLiteral( "9-9-9-9" ) ) ), -1 );
|
||||
|
||||
QCOMPARE( index->pointCount(), 518862 );
|
||||
QCOMPARE( index->zMin(), 2322.89625 );
|
||||
QCOMPARE( index->zMax(), 2338.5755 );
|
||||
QCOMPARE( index->scale().toVector3D(), QVector3D( 0.0001, 0.0001, 0.0001 ) );
|
||||
QCOMPARE( index->offset().toVector3D(), QVector3D( 515385, 4918361, 2330.5 ) );
|
||||
QCOMPARE( index->span(), 128 );
|
||||
|
||||
QCOMPARE( index->nodeError( IndexedPointCloudNode::fromString( QStringLiteral( "0-0-0-0" ) ) ), 0.328125 );
|
||||
QCOMPARE( index->nodeError( IndexedPointCloudNode::fromString( QStringLiteral( "1-1-1-1" ) ) ), 0.1640625 );
|
||||
QCOMPARE( index->nodeError( IndexedPointCloudNode::fromString( QStringLiteral( "2-3-3-1" ) ) ), 0.08203125 );
|
||||
|
||||
{
|
||||
QgsPointCloudDataBounds bounds = index->nodeBounds( IndexedPointCloudNode::fromString( QStringLiteral( "0-0-0-0" ) ) );
|
||||
QCOMPARE( bounds.xMin(), -170000 );
|
||||
QCOMPARE( bounds.yMin(), -210000 );
|
||||
QCOMPARE( bounds.zMin(), -85000 );
|
||||
QCOMPARE( bounds.xMax(), 250000 );
|
||||
QCOMPARE( bounds.yMax(), 210000 );
|
||||
QCOMPARE( bounds.zMax(), 335000 );
|
||||
}
|
||||
|
||||
{
|
||||
QgsPointCloudDataBounds bounds = index->nodeBounds( IndexedPointCloudNode::fromString( QStringLiteral( "1-1-1-1" ) ) );
|
||||
QCOMPARE( bounds.xMin(), 40000 );
|
||||
QCOMPARE( bounds.yMin(), 0 );
|
||||
QCOMPARE( bounds.zMin(), 125000 );
|
||||
QCOMPARE( bounds.xMax(), 250000 );
|
||||
QCOMPARE( bounds.yMax(), 210000 );
|
||||
QCOMPARE( bounds.zMax(), 335000 );
|
||||
}
|
||||
|
||||
{
|
||||
QgsPointCloudDataBounds bounds = index->nodeBounds( IndexedPointCloudNode::fromString( QStringLiteral( "2-3-3-1" ) ) );
|
||||
QCOMPARE( bounds.xMin(), 145000 );
|
||||
QCOMPARE( bounds.yMin(), 105000 );
|
||||
QCOMPARE( bounds.zMin(), 20000 );
|
||||
QCOMPARE( bounds.xMax(), 250000 );
|
||||
QCOMPARE( bounds.yMax(), 210000 );
|
||||
QCOMPARE( bounds.zMax(), 125000 );
|
||||
}
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgsCopcProvider )
|
||||
#include "testqgscopcprovider.moc"
|
BIN
tests/testdata/point_clouds/copc/extrabytes-dataset.copc.laz
vendored
Normal file
BIN
tests/testdata/point_clouds/copc/extrabytes-dataset.copc.laz
vendored
Normal file
Binary file not shown.
BIN
tests/testdata/point_clouds/copc/lone-star.copc.laz
vendored
Normal file
BIN
tests/testdata/point_clouds/copc/lone-star.copc.laz
vendored
Normal file
Binary file not shown.
BIN
tests/testdata/point_clouds/copc/no-extrabytes-dataset.copc.laz
vendored
Normal file
BIN
tests/testdata/point_clouds/copc/no-extrabytes-dataset.copc.laz
vendored
Normal file
Binary file not shown.
0
tests/testdata/point_clouds/copc/norgb.copc.laz
vendored
Normal file
0
tests/testdata/point_clouds/copc/norgb.copc.laz
vendored
Normal file
BIN
tests/testdata/point_clouds/copc/rgb.copc.laz
vendored
Normal file
BIN
tests/testdata/point_clouds/copc/rgb.copc.laz
vendored
Normal file
Binary file not shown.
BIN
tests/testdata/point_clouds/copc/rgb16.copc.laz
vendored
Normal file
BIN
tests/testdata/point_clouds/copc/rgb16.copc.laz
vendored
Normal file
Binary file not shown.
BIN
tests/testdata/point_clouds/copc/sunshine-coast.copc.laz
vendored
Normal file
BIN
tests/testdata/point_clouds/copc/sunshine-coast.copc.laz
vendored
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user