Add lazperf pcpatch

This commit is contained in:
Blottiere Paul 2016-06-14 10:14:31 +02:00
parent c15d4ef395
commit b3764bd5dc
38 changed files with 1885 additions and 14 deletions

4
.install-lazperf.sh Normal file
View File

@ -0,0 +1,4 @@
#!/bin/sh
set -ex
git clone https://github.com/verma/laz-perf.git
cd laz-perf; cmake .; make; sudo make install

View File

@ -3,6 +3,13 @@ language: c
before_install:
- sudo apt-get update
- sudo apt-get install -q postgresql-server-dev-9.3 libcunit1-dev valgrind
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
- sudo apt-get update -qq
install:
- sudo apt-get install -qq g++-4.8
- export CXX="g++-4.8"
- sh .install-lazperf.sh
addons:
postgresql: "9.3" # for "installcheck"
@ -10,9 +17,10 @@ addons:
before_script: ./autogen.sh
script:
- ./configure CFLAGS="-Wall -O2 -g"
- ./configure CFLAGS="-Wall -O2 -g" --with-lazperf=/usr/local/
- make
- make check
- valgrind --leak-check=full --error-exitcode=1 lib/cunit/cu_tester
- sudo make install
- make installcheck || { cat pgsql/regression.diffs && false; }
- cd tools/benchmark_compression && sh compression_benchmark.sh

View File

@ -102,7 +102,7 @@ include_directories (${ZLIB_INCLUDE_DIR})
#------------------------------------------------------------------------------
# cunit and ght
# cunit, ght and lazperf
if (WITH_TESTS)
find_package (CUnit)
@ -114,6 +114,12 @@ if (LIBGHT_FOUND)
set (HAVE_LIBGHT 1)
endif (LIBGHT_FOUND)
find_package (LazPerf)
if (LAZPERF_FOUND)
set (HAVE_LAZPERF 1)
endif (LAZPERF_FOUND)
#------------------------------------------------------------------------------
# use default install location if not specified

View File

@ -19,3 +19,6 @@ astyle:
-type f \
-exec astyle --style=ansi --indent=tab --suffix=none {} ';'
maintainer-clean:
rm -f config.log config.mk config.status lib/pc_config.h configure
rm -rf autom4te.cache build

View File

@ -16,6 +16,7 @@ A PostgreSQL extension for storing point cloud (LIDAR) data.
- LibXML2 development packages must be installed, usually "libxml2-dev" or "libxml2-devel".
- CUnit packages must be installed, or [source built and installed](http://sourceforge.net/projects/cunit/ "CUnit").
- [Optional] GHT library may be installed for GHT compression support, [built from source](http://github.com/pramsey/libght/ "LibGHT")
- [Optional] LAZPERF library may be installed for LAZ compression support, [built from source](http://github.com/hobu/laz-perf "LAZPERF")
Tests can be disabled by passing ``WITH_TESTS=FALSE`` to cmake, e.g. ``cmake .. -DWITH_TESTS=FALSE``.
This removes the CUnit dependency.
@ -439,6 +440,7 @@ Now that you have created two tables, you'll see entries for them in the `pointc
> Allowed global compression schemes are:
> - auto -- determined by pcid
> - ght -- no compression config supported
> - laz -- no compression config supported
> - dimensional
> configuration is a comma-separated list of per-dimension
> compressions from this list:
@ -506,6 +508,7 @@ There are currently three supported compressions:
- **None**, which stores points and patches as byte arrays using the type and formats described in the schema document.
- **Dimensional**, which stores points the same as 'none' but stores patches as collections of dimensional data arrays, with an "appropriate" compression applied. Dimensional compression makes the most sense for smaller patch sizes, since small patches will tend to have more homogeneous dimensions.
- **GHT** or "GeoHash Tree", which stores the points in a tree where each node stores the common values shared by all nodes below. For larger patch sizes, GHT should provide effective compression and performance for patch-wise operations. You must build Pointcloud with libght support to make use of the GHT compression.
- **LAZ** or "LASZip". You must build Pointcloud with LAZPERF support to make use of the LAZ compression.
If no compression is declared in `<pc:metadata>`, then a compression of "none" is assumed.
@ -630,6 +633,17 @@ Where simple compression schemes fail, general purpose compression is applied to
GHT patches are much like dimensional patches, except their internal structure is more opaque. Use LibGHT to read the GHT data buffer out into a GHT tree in memory.
### Patch Binary (LAZ) ####
byte: endianness (1 = NDR, 0 = XDR)
uint32: pcid (key to POINTCLOUD_SCHEMAS)
uint32: 3 = LAZ compression
uint32: npoints
uint32: LAZ data size
data[]: LAZ data
LAZ patches are much like GHT patches. Use LAZPERF library to read the LAZ data buffer out into a LAZ buffer.
## Loading Data ##
The examples above show how to form patches from array of doubles, and well-known binary. You can write your own loader, using the uncompressed WKB format, or more simply you can load existing LIDAR files using the [PDAL](http://pointcloud.org "PDAL") processing and format conversion library.

View File

@ -0,0 +1,16 @@
# Find the LazPerf headers
#
# LAZPERF_INCLUDE_DIRS - The LazPerf include directory (directory where laz-perf/las.hpp was found)
# LAZPERF_FOUND - True if LazPerf found in system
FIND_PATH(LAZPERF_INCLUDE_DIR NAMES laz-perf/las.hpp)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LazPerf DEFAULT_MSG LAZPERF_INCLUDE_DIR)
IF(LAZPERF_FOUND)
SET(LAZPERF_INCLUDE_DIRS ${LAZPERF_INCLUDE_DIR})
ENDIF(LAZPERF_FOUND)
MARK_AS_ADVANCED(CLEAR LAZPERF_INCLUDE_DIR)

View File

@ -1,5 +1,6 @@
CC = @CC@
CFLAGS = @CFLAGS@
CXXFLAGS = -fPIC -std=c++0x
XML2_CPPFLAGS = @XML2_CPPFLAGS@
XML2_LDFLAGS = @XML2_LDFLAGS@
@ -16,4 +17,5 @@ GHT_LDFLAGS = @GHT_LDFLAGS@
PG_CONFIG = @PG_CONFIG@
PGXS = @PGXS@
LIB_A = libpc.a
LIB_A = libpc.a
LIB_A_LAZPERF = liblazperf.a

View File

@ -12,6 +12,7 @@ dnl ***********************************************************************/
AC_INIT()
AC_CONFIG_MACRO_DIR([macros])
AC_CONFIG_HEADERS([lib/pc_config.h])
AC_LANG([C++])
dnl
dnl Compilers
@ -325,6 +326,44 @@ fi
AC_SUBST([GHT_LDFLAGS])
AC_SUBST([GHT_CPPFLAGS])
dnl ===========================================================================
dnl Detect LazPerf
dnl ===========================================================================
AC_ARG_WITH([lazperf],
[AS_HELP_STRING([--with-lazperf=DIR], [specify the base lazperf installation directory])],
[LAZPERFDIR="$withval"], [LAZPERFDIR=""])
if test "x$LAZPERFDIR" = "xyes"; then
AC_MSG_ERROR([you must specify a parameter to --with-lazperf, e.g. --with-lazperf=/opt/local])
fi
if test "x$LAZPERFDIR" = "x"; then
AC_CHECK_HEADER([las.hpp],
[FOUND_LAZPERF="YES"],
[FOUND_LAZPERF="NO"])
elif test "x$GHTDIR" != "xno"; then
LAZPERF_CPPFLAGS="-I${LAZPERFDIR}/include/"
CFLAGS="$LAZPERF_CPPFLAGS"
CPPFLAGS="$LAZPERF_CPPFLAGS -std=c++0x"
AC_CHECK_HEADER([laz-perf/las.hpp],
[FOUND_LAZPERF="YES"],
[FOUND_LAZPERF="NO"])
fi
if test "x$FOUND_LAZPERF" = "xYES"; then
AC_DEFINE([HAVE_LAZPERF])
LAZPERF_STATUS="enabled"
if test $LAZPERFDIR; then
LAZPERF_STATUS="$LAZPERFDIR/include/laz-perf"
fi
else
LAZPERF_STATUS="disabled"
fi
AC_SUBST([LAZPERF_STATUS])
dnl ===========================================================================
dnl Figure out where this script is running
@ -357,5 +396,6 @@ AC_MSG_RESULT([ PostgreSQL version: ${PGSQL_FULL_VERSION}])
AC_MSG_RESULT([ Libxml2 config: ${XML2CONFIG}])
AC_MSG_RESULT([ Libxml2 version: ${LIBXML2_VERSION}])
AC_MSG_RESULT([ LibGHT status: ${GHT_STATUS}])
AC_MSG_RESULT([ LazPerf status: ${LAZPERF_STATUS}])
AC_MSG_RESULT([ CUnit status: ${CUNIT_STATUS}])
AC_MSG_RESULT()

View File

@ -9,6 +9,7 @@ set ( PC_SOURCES
pc_patch.c
pc_patch_dimensional.c
pc_patch_ght.c
pc_patch_lazperf.c
pc_patch_uncompressed.c
pc_point.c
pc_pointlist.c
@ -18,6 +19,10 @@ set ( PC_SOURCES
pc_val.c
)
set ( LAZPERF_SOURCES
lazperf_adapter.cpp
)
set ( PC_HEADERS
hashtable.h
stringbuffer.h
@ -27,6 +32,7 @@ set ( PC_HEADERS
)
add_library (libpc-static STATIC ${PC_SOURCES} ${PC_HEADERS})
add_library (liblazperf-static STATIC ${LAZPERF_SOURCES})
set_target_properties (libpc-static
PROPERTIES
@ -36,12 +42,23 @@ set_target_properties (libpc-static
COMPILE_FLAGS -fPIC
)
set_target_properties (liblazperf-static
PROPERTIES
OUTPUT_NAME "lazperf"
PREFIX "lib"
CLEAN_DIRECT_OUTPUT 1
COMPILE_FLAGS "-fPIC -std=c++0x"
)
target_link_libraries (libpc-static xml2)
target_link_libraries (libpc-static z)
target_link_libraries (libpc-static m)
if (LIBGHT_FOUND)
target_link_libraries (libpc-static ght)
endif (LIBGHT_FOUND)
if (LAZPERF_FOUND)
target_link_libraries (libpc-static liblazperf-static)
endif (LAZPERF_FOUND)
if (WITH_TESTS)
add_subdirectory (cunit)

View File

@ -21,16 +21,23 @@ OBJS = \
pc_util.o \
pc_val.o \
stringbuffer.o \
hashtable.o
hashtable.o \
pc_patch_lazperf.o
all: $(LIB_A)
OBJS_LAZPERF = \
lazperf_adapter.o
all: $(LIB_A) $(LIB_A_LAZPERF)
$(MAKE) -C cunit $@
$(LIB_A): $(OBJS)
ar rs $@ $^
$(LIB_A_LAZPERF): $(OBJS_LAZPERF)
ar rs $@ $^
clean:
@rm -f $(OBJS) $(LIB_A)
@rm -f $(OBJS) $(LIB_A) $(OBJS_LAZPERF) $(LIB_A_LAZPERF)
$(MAKE) -C cunit $@
install:

View File

@ -8,6 +8,7 @@ set (PC_TEST_SOURCES
cu_pc_bytes.c
cu_pc_schema.c
cu_pc_patch.c
cu_pc_patch_lazperf.c
cu_tester.c
)

View File

@ -13,7 +13,8 @@ OBJS = \
cu_pc_schema.o \
cu_pc_point.o \
cu_pc_patch.o \
cu_pc_patch_ght.o
cu_pc_patch_ght.o \
cu_pc_patch_lazperf.o
ifeq ($(CUNIT_LDFLAGS),)
# No cunit? Emit message and continue
@ -37,12 +38,15 @@ check: $(EXE)
endif
# Build the main unit test executable
$(EXE): $(OBJS) ../$(LIB_A)
$(CC) -o $@ $^ $(LDFLAGS) -lm
$(EXE): $(OBJS) ../$(LIB_A) ../$(LIB_A_LAZPERF)
$(CC) -o $@ $^ $(LDFLAGS) -lm -lstdc++
../$(LIB_A):
$(MAKE) -C .. $(LIB_A)
../$(LIB_A_LAZPERF):
$(MAKE) -C .. $(LIB_A_LAZPERF)
# Clean target
clean:
@rm -f $(OBJS)

View File

@ -0,0 +1,264 @@
/***********************************************************************
* cu_pc_patch_lazperf.c
*
* Testing for the LazPerf API functions
*
* Copyright (c) 2016 Paul Blottiere, Oslandia
*
***********************************************************************/
#include "CUnit/Basic.h"
#include "cu_tester.h"
/* GLOBALS ************************************************************/
static PCSCHEMA *simpleschema = NULL;
static const char *simplexmlfile = "data/simple-schema.xml";
/* Setup/teardown for this suite */
static int
init_suite(void)
{
char *xmlstr = file_to_str(simplexmlfile);
int rv = pc_schema_from_xml(xmlstr, &simpleschema);
pcfree(xmlstr);
if ( rv == PC_FAILURE ) return 1;
return 0;
}
static int
clean_suite(void)
{
pc_schema_free(simpleschema);
return 0;
}
static void
test_schema_compression_lazperf(void)
{
PCSCHEMA *myschema = NULL;
char *myxmlfile = "data/simple-schema-laz.xml";
char *xmlstr = file_to_str(myxmlfile);
int rv = pc_schema_from_xml(xmlstr, &myschema);
CU_ASSERT(rv == PC_SUCCESS);
int compression = myschema->compression;
CU_ASSERT_EQUAL(compression, PC_LAZPERF);
pc_schema_free(myschema);
pcfree(xmlstr);
}
#ifdef HAVE_LAZPERF
static void
test_patch_lazperf()
{
PCPOINT *pt;
int i;
int npts = 400;
PCPOINTLIST *pl;
PCPATCH_LAZPERF *pal;
PCPATCH_UNCOMPRESSED *paul, *pauref;
// build a list of points
pl = pc_pointlist_make(npts);
for ( i = 0; i < npts; i++ )
{
pt = pc_point_make(simpleschema);
pc_point_set_double_by_name(pt, "x", i*2.0);
pc_point_set_double_by_name(pt, "y", i*1.9);
pc_point_set_double_by_name(pt, "Z", i*0.34);
pc_point_set_double_by_name(pt, "intensity", 10);
pc_pointlist_add_point(pl, pt);
}
// compress the list in a lazperf patch
pal = pc_patch_lazperf_from_pointlist( pl );
// get an uncompressed patch from the lazperf patch
paul = pc_patch_uncompressed_from_lazperf( pal );
// get an uncompressed patch directly from the pointlist
pauref = pc_patch_uncompressed_from_pointlist( pl );
// test the number of points
CU_ASSERT_EQUAL(pal->npoints, pauref->npoints);
CU_ASSERT_EQUAL(paul->npoints, pauref->npoints);
// test bounds
CU_ASSERT_DOUBLE_EQUAL(pal->bounds.xmax, pauref->bounds.xmax, 0.0001);
CU_ASSERT_DOUBLE_EQUAL(paul->bounds.ymax, pauref->bounds.ymax, 0.000001);
// test type
CU_ASSERT_EQUAL(pal->type, PC_LAZPERF);
CU_ASSERT_EQUAL(paul->type, pauref->type);
// test readonly
CU_ASSERT_EQUAL(pauref->readonly, paul->readonly);
CU_ASSERT_EQUAL(pauref->readonly, pal->readonly);
// free
pc_pointlist_free(pl);
pc_patch_free( (PCPATCH*) pal );
pc_patch_free((PCPATCH*) paul);
pc_patch_free((PCPATCH*) pauref);
}
static void
test_pointlist_lazperf()
{
PCPOINT *pt;
int i;
int npts = 400;
PCPOINTLIST *pl1, *pl2;
PCPATCH_LAZPERF *pch1;
PCPATCH_UNCOMPRESSED *pa1, *pa2;
char *wkt1, *wkt2;
// build a list of points
pl1 = pc_pointlist_make(npts);
for ( i = 0; i < npts; i++ )
{
pt = pc_point_make(simpleschema);
pc_point_set_double_by_name(pt, "x", i*2.0);
pc_point_set_double_by_name(pt, "y", i*1.9);
pc_point_set_double_by_name(pt, "Z", i*0.34);
pc_point_set_double_by_name(pt, "intensity", 10);
pc_pointlist_add_point(pl1, pt);
}
// compress the list in a lazperf patch
pch1 = pc_patch_lazperf_from_pointlist( pl1 );
// decompress the lazperf patch in a pointlist
pl2 = pc_pointlist_from_lazperf(pch1);
// test that the string representation of pointlist is equal
pa1 = pc_patch_uncompressed_from_pointlist( pl1 );
pa2 = pc_patch_uncompressed_from_lazperf( pch1 );
wkt1 = pc_patch_uncompressed_to_string(pa1);
wkt2 = pc_patch_uncompressed_to_string(pa2);
CU_ASSERT_STRING_EQUAL(wkt1, wkt2);
pc_patch_free((PCPATCH*) pch1 );
pc_patch_free((PCPATCH*) pa1);
pc_patch_free((PCPATCH*) pa2);
pc_pointlist_free(pl1);
pc_pointlist_free(pl2);
pcfree(wkt1);
pcfree(wkt2);
}
static void
test_to_string_lazperf()
{
PCPOINT *pt;
int i;
int npts = 400;
PCPOINTLIST *pl;
PCPATCH_LAZPERF *pal;
PCPATCH_UNCOMPRESSED *pau;
char *str1, *str2;
// build a list of points
pl = pc_pointlist_make(npts);
for ( i = 0; i < npts; i++ )
{
pt = pc_point_make(simpleschema);
pc_point_set_double_by_name(pt, "x", i*2.0);
pc_point_set_double_by_name(pt, "y", i*1.9);
pc_point_set_double_by_name(pt, "Z", i*0.34);
pc_point_set_double_by_name(pt, "intensity", 10);
pc_pointlist_add_point(pl, pt);
}
// build patch
pau = pc_patch_uncompressed_from_pointlist(pl);
pal = pc_patch_lazperf_from_pointlist(pl);
// get string
str1 = pc_patch_uncompressed_to_string(pau);
str2 = pc_patch_lazperf_to_string(pal);
// compare
CU_ASSERT_STRING_EQUAL(str1, str2);
// free
pc_patch_free((PCPATCH*) pal);
pc_patch_free((PCPATCH*) pau);
pc_pointlist_free(pl);
pcfree(str1);
pcfree(str2);
}
static void
test_wkb_lazperf()
{
PCPOINT *pt;
int i;
int npts = 400;
PCPOINTLIST *pl;
PCPATCH_LAZPERF *pal1, *pal2;
PCPATCH_UNCOMPRESSED *pau;
uint8_t *wkb1, *wkb2;
size_t wkbsize;
// build a list of points
pl = pc_pointlist_make(npts);
for ( i = 0; i < npts; i++ )
{
pt = pc_point_make(simpleschema);
pc_point_set_double_by_name(pt, "x", i*2.0);
pc_point_set_double_by_name(pt, "y", i*1.9);
pc_point_set_double_by_name(pt, "Z", i*0.34);
pc_point_set_double_by_name(pt, "intensity", 10);
pc_pointlist_add_point(pl, pt);
}
// build patch lazperf
pal1 = pc_patch_lazperf_from_pointlist(pl);
// get the corresponding wkb
wkb1 = pc_patch_lazperf_to_wkb(pal1, &wkbsize);
// rebuild a lazperf patch thanks to the wkb
pal2 = (PCPATCH_LAZPERF*) pc_patch_lazperf_from_wkb( pal1->schema, wkb1, wkbsize);
// get the wkb reference
pau = pc_patch_uncompressed_from_pointlist(pl);
wkb2 = pc_patch_uncompressed_to_wkb( pau, &wkbsize );
// compare wkb
CU_ASSERT_STRING_EQUAL(wkb1, wkb2);
// free
pc_patch_free((PCPATCH*) pal1);
pc_patch_free((PCPATCH*) pal2);
pc_patch_free((PCPATCH*) pau);
pc_pointlist_free(pl);
pcfree(wkb1);
pcfree(wkb2);
}
#endif
/* REGISTER ***********************************************************/
CU_TestInfo lazperf_tests[] = {
#ifdef HAVE_LAZPERF
PC_TEST(test_schema_compression_lazperf),
PC_TEST(test_patch_lazperf),
PC_TEST(test_pointlist_lazperf),
PC_TEST(test_to_string_lazperf),
PC_TEST(test_wkb_lazperf),
#endif
CU_TEST_INFO_NULL
};
CU_SuiteInfo lazperf_suite = {"lazperf", init_suite, clean_suite, lazperf_tests};

View File

@ -18,6 +18,7 @@ extern CU_SuiteInfo patch_suite;
extern CU_SuiteInfo point_suite;
extern CU_SuiteInfo ght_suite;
extern CU_SuiteInfo bytes_suite;
extern CU_SuiteInfo lazperf_suite;
/**
* CUnit error handler
@ -52,6 +53,7 @@ int main(int argc, char *argv[])
point_suite,
ght_suite,
bytes_suite,
lazperf_suite,
CU_SUITE_INFO_NULL
};

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<pc:PointCloudSchema xmlns:pc="http://pointcloud.org/schemas/PC/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<pc:dimension>
<pc:position>1</pc:position>
<pc:size>4</pc:size>
<pc:description>X coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>X</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>2</pc:position>
<pc:size>4</pc:size>
<pc:description>Y coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>Y</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>3</pc:position>
<pc:size>4</pc:size>
<pc:description>Z coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>Z</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>4</pc:position>
<pc:size>2</pc:size>
<pc:description>The intensity value is the integer representation of the pulse return magnitude. This value is optional and system specific. However, it should always be included if available.</pc:description>
<pc:name>Intensity</pc:name>
<pc:interpretation>uint16_t</pc:interpretation>
<pc:scale>1</pc:scale>
</pc:dimension>
<pc:metadata>
<Metadata name="compression">laz</Metadata>
</pc:metadata>
</pc:PointCloudSchema>

260
lib/lazperf_adapter.cpp Normal file
View File

@ -0,0 +1,260 @@
/***********************************************************************
* lazperf_adapter.cpp
*
* LazPerf compression/decompression
*
* Copyright (c) 2016 Paul Blottiere, Oslandia
*
***********************************************************************/
#include "lazperf_adapter.hpp"
#ifdef HAVE_LAZPERF
/**********************************************************************
* C API
*/
size_t
lazperf_compress_from_uncompressed(const PCPATCH_UNCOMPRESSED *pa, uint8_t **compressed)
{
size_t size = 1;
LazPerfBuf buf;
LazPerfCompressor engine(pa->schema, buf);
if (engine.compress(pa->data, pa->datasize) == pa->npoints)
{
size = buf.buf.size();
*compressed = (uint8_t*) malloc(size);
*compressed = (uint8_t*) memcpy(*compressed, buf.data(), size);
}
// log
// lazperf_dump(pa);
// lazperf_dump(*compressed, size);
return size;
}
size_t
lazperf_uncompress_from_compressed(const PCPATCH_LAZPERF *pa, uint8_t **decompressed)
{
size_t size = -1;
size_t datasize = pa->schema->size * pa->npoints;
LazPerfBuf buf;
buf.putBytes(pa->lazperf, pa->lazperfsize);
LazPerfDecompressor engine(pa->schema, buf);
*decompressed = (uint8_t*) malloc(datasize);
if (engine.decompress(*decompressed, datasize) == pa->npoints)
size = buf.buf.size();
// log
// lazperf_dump(pa);
// lazperf_dump(*decompressed, datasize);
return size;
}
/**********************************************************************
* INTERNAL CPP
*/
// utility functions
void
lazperf_dump(uint8_t *data, const size_t size)
{
std::cout << "DUMP DATA: " << std::endl;
std::cout << " - datasize: " << size << std::endl;
std::cout << " - data: ";
for (int i = 0; i < size; ++i)
printf("%02x ", data[i]);
std::cout << std::endl;
}
void
lazperf_dump(const PCPATCH_UNCOMPRESSED* p)
{
std::cout << std::endl;
std::cout << "DUMP UNCOMPRESSED PATCH: " << std::endl;
std::cout << " - type: " << p->type << std::endl;
std::cout << " - schema->size " << p->schema->size << std::endl;
std::cout << " - readonly: " << p->readonly << std::endl;
std::cout << " - npoints: " << p->npoints << std::endl;
std::cout << " - maxpoints: " << p->maxpoints << std::endl;
std::cout << " - datasize: " << p->datasize << std::endl;
std::cout << " - data: ";
for (int i = 0; i < p->datasize; ++i)
printf("%02x ", p->data[i]);
std::cout << std::endl;
}
void
lazperf_dump(const PCPATCH_LAZPERF* p)
{
std::cout << std::endl;
std::cout << "DUMP LAZPERF PATCH: " << std::endl;
std::cout << " - type: " << p->type << std::endl;
std::cout << " - schema->size " << p->schema->size << std::endl;
std::cout << " - readonly: " << p->readonly << std::endl;
std::cout << " - npoints: " << p->npoints << std::endl;
std::cout << " - lazperfsize: " << p->lazperfsize << std::endl;
std::cout << " - lazperf: ";
for (int i = 0; i < p->lazperfsize; ++i)
printf("%02x ", p->lazperf[i]);
std::cout << std::endl;
}
// LazPerf class
template<typename LazPerfEngine, typename LazPerfCoder>
LazPerf<LazPerfEngine, LazPerfCoder>::LazPerf(const PCSCHEMA *pcschema, LazPerfBuf &buf)
: _pcschema(pcschema)
, _coder(buf)
, _pointsize(0)
{
}
template<typename LazPerfEngine, typename LazPerfCoder>
LazPerf<LazPerfEngine, LazPerfCoder>::~LazPerf()
{
}
template<typename LazPerfEngine, typename LazPerfCoder>
void
LazPerf<LazPerfEngine, LazPerfCoder>::initSchema()
{
for (int i = 0; i < _pcschema->ndims; i++)
addField(_pcschema->dims[i]);
}
template<typename LazPerfEngine, typename LazPerfCoder>
bool
LazPerf<LazPerfEngine, LazPerfCoder>::addField(const PCDIMENSION *dim)
{
bool rc = true;
switch(dim->interpretation)
{
case PC_INT8:
{
_engine->template add_field<I8>();
break;
}
case PC_UINT8:
{
_engine->template add_field<U8>();
break;
}
case PC_INT16:
{
_engine->template add_field<I16>();
break;
}
case PC_UINT16:
{
_engine->template add_field<U16>();
break;
}
case PC_INT32:
{
_engine->template add_field<I32>();
break;
}
case PC_UINT32:
{
_engine->template add_field<U32>();
break;
}
case PC_INT64:
{
//_engine->template add_field<I64>();
break;
}
case PC_UINT64:
{
//_engine->template add_field<U64>();
break;
}
case PC_DOUBLE:
{
//_engine->template add_field<F64>();
break;
}
case PC_FLOAT:
{
//_engine->template add_field<F32>();
break;
}
case PC_UNKNOWN:
default:
rc = false;
}
if (rc)
_pointsize += dim->size;
return rc;
}
// LazPerf Compressor
LazPerfCompressor::LazPerfCompressor(const PCSCHEMA *pcschema, LazPerfBuf &output)
: LazPerf(pcschema, output)
{
_engine = laszip::formats::make_dynamic_compressor(_coder);
initSchema();
}
LazPerfCompressor::~LazPerfCompressor()
{
}
size_t
LazPerfCompressor::compress(const uint8_t *input, const size_t inputsize)
{
size_t size = 0;
const uint8_t *end = input + inputsize;
while (input + _pointsize <= end)
{
_engine->compress((const char*) input);
input += _pointsize;
size++;
}
_coder.done();
return size;
}
// LazPerf Decompressor
LazPerfDecompressor::LazPerfDecompressor(const PCSCHEMA *pcschema, LazPerfBuf &input)
: LazPerf(pcschema, input)
{
_engine = laszip::formats::make_dynamic_decompressor(_coder);
initSchema();
}
LazPerfDecompressor::~LazPerfDecompressor()
{
}
size_t
LazPerfDecompressor::decompress(uint8_t *output, const size_t outputsize)
{
size_t size = 0;
const uint8_t *end = output + outputsize;
while (output + _pointsize <= end)
{
_engine->decompress((char*) output);
output += _pointsize;
size++;
}
return size;
}
#endif // HAVE_LAZPERF

118
lib/lazperf_adapter.hpp Normal file
View File

@ -0,0 +1,118 @@
/***********************************************************************
* lazperf_adapter.hpp
*
* LazPerf compression/decompression
*
* Copyright (c) 2016 Paul Blottiere, Oslandia
*
***********************************************************************/
#pragma once
#include "pc_api_internal.h"
#ifdef HAVE_LAZPERF
#include <laz-perf/common/common.hpp>
#include <laz-perf/compressor.hpp>
#include <laz-perf/decompressor.hpp>
#include <laz-perf/encoder.hpp>
#include <laz-perf/decoder.hpp>
#include <laz-perf/formats.hpp>
#include <laz-perf/las.hpp>
/**********************************************************************
* C API
*/
extern "C" {
size_t lazperf_compress_from_uncompressed(const PCPATCH_UNCOMPRESSED *pa, uint8_t **compressed);
size_t lazperf_uncompress_from_compressed(const PCPATCH_LAZPERF *pa, uint8_t **decompressed);
}
/**********************************************************************
* INTERNAL CPP
*/
// utility functions
void lazperf_dump( uint8_t* data, const size_t size );
void lazperf_dump( const PCPATCH_UNCOMPRESSED *p );
void lazperf_dump( const PCPATCH_LAZPERF *p );
// struct which capture data coming from the compressor
struct LazPerfBuf {
LazPerfBuf() : buf(), idx(0) {}
const uint8_t* data() {
return reinterpret_cast<const uint8_t*>(buf.data());
}
void putBytes(const unsigned char* b, size_t len) {
while(len --) {
buf.push_back(*b++);
}
}
void putByte(const unsigned char b) {
buf.push_back(b);
}
unsigned char getByte() {
return buf[idx++];
}
void getBytes(unsigned char *b, int len) {
for (int i = 0 ; i < len ; i ++) {
b[i] = getByte();
}
}
std::vector<unsigned char> buf;
size_t idx;
};
// some typedef
typedef laszip::encoders::arithmetic<LazPerfBuf> Encoder;
typedef laszip::decoders::arithmetic<LazPerfBuf> Decoder;
typedef laszip::formats::dynamic_field_compressor<Encoder>::ptr Compressor;
typedef laszip::formats::dynamic_field_decompressor<Decoder>::ptr Decompressor;
// LazPerf class
template<typename LazPerfEngine, typename LazPerfCoder>
class LazPerf {
public:
LazPerf( const PCSCHEMA *pcschema, LazPerfBuf &buf );
~LazPerf();
size_t pointsize() const { return _pointsize; }
protected:
void initSchema();
bool addField(const PCDIMENSION *dim);
const PCSCHEMA *_pcschema;
LazPerfCoder _coder;
LazPerfEngine _engine;
size_t _pointsize;
};
// compressor
class LazPerfCompressor : public LazPerf<Compressor, Encoder> {
public:
LazPerfCompressor( const PCSCHEMA *pcschema, LazPerfBuf &output);
~LazPerfCompressor();
size_t compress( const uint8_t *input, const size_t inputsize );
};
// decompressor
class LazPerfDecompressor : public LazPerf<Decompressor, Decoder> {
public:
LazPerfDecompressor( const PCSCHEMA *pcschema, LazPerfBuf &input );
~LazPerfDecompressor();
size_t decompress( uint8_t *data, const size_t datasize );
};
#endif // HAVE_LAZPERF

View File

@ -36,7 +36,8 @@ enum COMPRESSIONS
{
PC_NONE = 0,
PC_GHT = 1,
PC_DIMENSIONAL = 2
PC_DIMENSIONAL = 2,
PC_LAZPERF = 3
};
/**
@ -196,6 +197,12 @@ typedef struct
uint8_t *ght;
} PCPATCH_GHT;
typedef struct
{
PCPATCH_COMMON
size_t lazperfsize;
uint8_t *lazperf;
} PCPATCH_LAZPERF;
/* Global function signatures for memory/logging handlers. */

View File

@ -197,6 +197,16 @@ PCPOINTLIST* pc_pointlist_from_ght(const PCPATCH_GHT *pag);
PCPATCH_GHT* pc_patch_ght_filter(const PCPATCH_GHT *patch, uint32_t dimnum, PC_FILTERTYPE filter, double val1, double val2);
/* LAZPERF PATCHES */
PCPATCH_LAZPERF* pc_patch_lazperf_from_pointlist(const PCPOINTLIST *pl);
PCPATCH_LAZPERF* pc_patch_lazperf_from_uncompressed(const PCPATCH_UNCOMPRESSED *pa);
PCPOINTLIST* pc_pointlist_from_lazperf(const PCPATCH_LAZPERF *palaz);
PCPATCH_UNCOMPRESSED* pc_patch_uncompressed_from_lazperf(const PCPATCH_LAZPERF *palaz);
char* pc_patch_lazperf_to_string(const PCPATCH_LAZPERF *pa);
void pc_patch_lazperf_free(PCPATCH_LAZPERF *palaz);
uint8_t* pc_patch_lazperf_to_wkb(const PCPATCH_LAZPERF *patch, size_t *wkbsize);
PCPATCH* pc_patch_lazperf_from_wkb(const PCSCHEMA *schema, const uint8_t *wkb, size_t wkbsize);
/****************************************************************************
* BYTES
*/

View File

@ -4,6 +4,8 @@
#cmakedefine HAVE_LIBGHT ${HAVE_LIBGHT}
#cmakedefine HAVE_LAZPERF ${HAVE_LAZPERF}
#cmakedefine HAVE_CUNIT ${HAVE_CUNIT}
#cmakedefine PROJECT_SOURCE_DIR "${PROJECT_SOURCE_DIR}"

View File

@ -4,6 +4,8 @@
#undef HAVE_LIBGHT
#undef HAVE_LAZPERF
#undef HAVE_CUNIT
#undef PROJECT_SOURCE_DIR

View File

@ -275,6 +275,28 @@ pc_patch_filter(const PCPATCH *pa, uint32_t dimnum, PC_FILTERTYPE filter, double
paout = (PCPATCH*)pdl;
break;
}
case PC_LAZPERF:
{
PCBITMAP *map;
PCPATCH_UNCOMPRESSED *pu;
PCPATCH_UNCOMPRESSED *pau;
pau = pc_patch_uncompressed_from_lazperf( (PCPATCH_LAZPERF*) pa );
map = pc_patch_uncompressed_bitmap(pau, dimnum, filter, val1, val2);
if ( map->nset == 0 )
{
pc_bitmap_free(map);
return (PCPATCH*)pc_patch_uncompressed_make(pa->schema, -1);
}
pu = pc_patch_uncompressed_filter(pau, map);
pc_bitmap_free(map);
/* pc_patch_uncompressed_filter computes stats and bounds, so we're ready to return here */
/* TODO, it could/should compute bounds and stats while filtering the points */
paout = (PCPATCH*)pu;
break;
}
default:
pcerror("%s: failure", __func__);
return NULL;

View File

@ -26,6 +26,8 @@ pc_patch_compute_extent(PCPATCH *pa)
return pc_patch_ght_compute_extent((PCPATCH_GHT*)pa);
case PC_DIMENSIONAL:
return pc_patch_dimensional_compute_extent((PCPATCH_DIMENSIONAL*)pa);
case PC_LAZPERF:
return pc_patch_lazperf_compute_extent((PCPATCH_DIMENSIONAL*)pa);
}
return PC_FAILURE;
}
@ -59,6 +61,14 @@ pc_patch_compute_stats(PCPATCH *pa)
pc_patch_uncompressed_free(pu);
return PC_SUCCESS;
}
case PC_LAZPERF:
{
PCPATCH_UNCOMPRESSED *pu = pc_patch_uncompressed_from_lazperf((PCPATCH_LAZPERF*)pa);
pc_patch_uncompressed_compute_stats(pu);
pa->stats = pc_stats_clone(pu->stats);
pc_patch_uncompressed_free(pu);
return PC_SUCCESS;
}
default:
{
pcerror("%s: unknown compression type", __func__, pa->type);
@ -95,6 +105,11 @@ pc_patch_free(PCPATCH *patch)
pc_patch_dimensional_free((PCPATCH_DIMENSIONAL*)patch);
break;
}
case PC_LAZPERF:
{
pc_patch_lazperf_free((PCPATCH_LAZPERF*)patch);
break;
}
default:
{
pcerror("%s: unknown compression type %d", __func__, patch->type);
@ -143,6 +158,14 @@ pc_patch_compress(const PCPATCH *patch, void *userdata)
pc_patch_dimensional_free(pcdu);
return (PCPATCH*)pcdc;
}
else if ( patch_compression == PC_LAZPERF )
{
PCPATCH_UNCOMPRESSED *pcu = pc_patch_uncompressed_from_lazperf( (PCPATCH_LAZPERF*) patch );
PCPATCH_DIMENSIONAL *pal = pc_patch_dimensional_from_uncompressed( pcu );
PCPATCH_DIMENSIONAL *palc = pc_patch_dimensional_compress( pal, NULL );
pc_patch_dimensional_free(pal);
return (PCPATCH*) palc;
}
else
{
pcerror("%s: unknown patch compression type %d", __func__, patch_compression);
@ -165,6 +188,11 @@ pc_patch_compress(const PCPATCH *patch, void *userdata)
PCPATCH_UNCOMPRESSED *pcu = pc_patch_uncompressed_from_ght((PCPATCH_GHT*)patch);
return (PCPATCH*)pcu;
}
else if ( patch_compression == PC_LAZPERF )
{
PCPATCH_UNCOMPRESSED *pcu = pc_patch_uncompressed_from_lazperf( (PCPATCH_LAZPERF*)patch );
return (PCPATCH*)pcu;
}
else
{
pcerror("%s: unknown patch compression type %d", __func__, patch_compression);
@ -189,6 +217,42 @@ pc_patch_compress(const PCPATCH *patch, void *userdata)
{
return (PCPATCH*)patch;
}
else if ( patch_compression == PC_LAZPERF )
{
PCPATCH_LAZPERF *pal = pc_patch_lazperf_from_uncompressed((PCPATCH_UNCOMPRESSED*)patch);
return (PCPATCH*)pal;
}
else
{
pcerror("%s: unknown patch compression type %d", __func__, patch_compression);
}
}
case PC_LAZPERF:
{
if ( patch_compression == PC_NONE )
{
PCPATCH_LAZPERF *pgc = pc_patch_lazperf_from_uncompressed((PCPATCH_UNCOMPRESSED*)patch);
if ( ! pgc ) pcerror("%s: lazperf compression failed", __func__);
return (PCPATCH*)pgc;
}
else if ( patch_compression == PC_DIMENSIONAL )
{
PCPATCH_UNCOMPRESSED *pad = pc_patch_uncompressed_from_dimensional((PCPATCH_DIMENSIONAL*)patch);
PCPATCH_LAZPERF *pal = pc_patch_lazperf_from_uncompressed( (PCPATCH_UNCOMPRESSED*) pad );
pc_patch_uncompressed_free( pad );
return (PCPATCH*)pal;
}
else if ( patch_compression == PC_GHT )
{
PCPATCH_UNCOMPRESSED *pcu = pc_patch_uncompressed_from_ght((PCPATCH_GHT*)patch);
PCPATCH_LAZPERF *pal = pc_patch_lazperf_from_uncompressed((PCPATCH_UNCOMPRESSED*)patch);
pc_patch_uncompressed_free(pcu);
return (PCPATCH*)pcu;
}
else if ( patch_compression == PC_LAZPERF )
{
return (PCPATCH*)patch;
}
else
{
pcerror("%s: unknown patch compression type %d", __func__, patch_compression);
@ -227,6 +291,12 @@ pc_patch_uncompress(const PCPATCH *patch)
return (PCPATCH*)pu;
}
if ( patch_compression == PC_LAZPERF )
{
PCPATCH_UNCOMPRESSED *pu = pc_patch_uncompressed_from_lazperf( (PCPATCH_LAZPERF*)patch );
return (PCPATCH*) pu;
}
return NULL;
}
@ -279,6 +349,11 @@ pc_patch_from_wkb(const PCSCHEMA *s, uint8_t *wkb, size_t wkbsize)
patch = pc_patch_ght_from_wkb(s, wkb, wkbsize);
break;
}
case PC_LAZPERF:
{
patch = pc_patch_lazperf_from_wkb(s, wkb, wkbsize);
break;
}
default:
{
/* Don't get here */
@ -322,6 +397,10 @@ pc_patch_to_wkb(const PCPATCH *patch, size_t *wkbsize)
{
return pc_patch_ght_to_wkb((PCPATCH_GHT*)patch, wkbsize);
}
case PC_LAZPERF:
{
return pc_patch_lazperf_to_wkb((PCPATCH_LAZPERF*)patch, wkbsize);
}
}
pcerror("%s: unknown compression requested '%d'", __func__, patch->schema->compression);
return NULL;
@ -338,6 +417,8 @@ pc_patch_to_string(const PCPATCH *patch)
return pc_patch_dimensional_to_string((PCPATCH_DIMENSIONAL*)patch);
case PC_GHT:
return pc_patch_ght_to_string((PCPATCH_GHT*)patch);
case PC_LAZPERF:
return pc_patch_lazperf_to_string( (PCPATCH_LAZPERF*)patch );
}
pcerror("%s: unsupported compression %d requested", __func__, patch->type);
return NULL;
@ -413,6 +494,15 @@ pc_patch_from_patchlist(PCPATCH **palist, int numpatches)
buf += sz;
break;
}
case PC_LAZPERF:
{
PCPATCH_UNCOMPRESSED *pu = pc_patch_uncompressed_from_lazperf((const PCPATCH_LAZPERF*)pa);
size_t sz = pu->schema->size * pu->npoints;
memcpy(buf, pu->data, sz);
buf += sz;
pc_patch_uncompressed_free(pu);
break;
}
default:
{
pcerror("%s: unknown compression type (%d)", __func__, pa->type);

233
lib/pc_patch_lazperf.c Normal file
View File

@ -0,0 +1,233 @@
/***********************************************************************
* pc_patch_lazperf.c
*
* PgSQL Pointcloud is free and open source software provided
* by the Government of Canada
*
* Copyright (c) 2016 Paul Blottiere, Oslandia
*
***********************************************************************/
#include "pc_api_internal.h"
#include <assert.h>
void
pc_patch_lazperf_free(PCPATCH_LAZPERF *pal)
{
assert(pal);
pcfree(pal->lazperf);
pcfree(pal);
}
PCPATCH_LAZPERF *
pc_patch_lazperf_from_pointlist(const PCPOINTLIST *pdl)
{
PCPATCH_UNCOMPRESSED *patch = pc_patch_uncompressed_from_pointlist(pdl);
PCPATCH_LAZPERF *lazperfpatch = pc_patch_lazperf_from_uncompressed(patch);
pc_patch_free((PCPATCH*) patch);
return lazperfpatch;
}
PCPATCH_LAZPERF*
pc_patch_lazperf_from_uncompressed(const PCPATCH_UNCOMPRESSED *pa)
{
#ifndef HAVE_LAZPERF
pcerror("%s: lazperf support is not enabled", __func__);
return NULL;
#endif
PCPATCH_LAZPERF *palaz = NULL;
uint8_t *compressed;
// cpp call to get compressed data from pcpatch
size_t compressSize = lazperf_compress_from_uncompressed(pa, &compressed);
if (compressSize != -1)
{
palaz = pcalloc(sizeof(PCPATCH_LAZPERF));
palaz->type = PC_LAZPERF;
palaz->readonly = PC_FALSE;
palaz->schema = pa->schema;
// not optimal but we have to pass by the context manager otherwise
// a segfault happenned (sometimes) during a pcfree of lazperf field
palaz->lazperf = (uint8_t*) pcalloc(compressSize);
memcpy(palaz->lazperf, compressed, compressSize);
free(compressed);
palaz->npoints = pa->npoints;
palaz->bounds = pa->bounds;
palaz->stats = pc_stats_clone(pa->stats);
palaz->lazperfsize = compressSize;
}
else
pcerror("%s: LAZ compressionf failed", __func__);
return palaz;
}
PCPOINTLIST *
pc_pointlist_from_lazperf(const PCPATCH_LAZPERF *palaz)
{
PCPATCH_UNCOMPRESSED *pu = NULL;
pu = pc_patch_uncompressed_from_lazperf(palaz);
PCPOINTLIST *pl = pc_pointlist_from_uncompressed(pu);
pc_patch_free((PCPATCH *)pu);
return pl;
}
PCPATCH_UNCOMPRESSED*
pc_patch_uncompressed_from_lazperf(const PCPATCH_LAZPERF *palaz)
{
#ifndef HAVE_LAZPERF
pcerror("%s: lazperf support is not enabled", __func__);
return NULL;
#endif
PCPATCH_UNCOMPRESSED *pcu = NULL;
uint8_t *decompressed;
int i;
// cpp call to uncompressed data
size_t size = lazperf_uncompress_from_compressed(palaz, &decompressed);
if (size != -1)
{
pcu = pcalloc(sizeof(PCPATCH_UNCOMPRESSED));
pcu->type = PC_NONE;
pcu->readonly = PC_FALSE;
pcu->schema = palaz->schema;
pcu->npoints = palaz->npoints;
pcu->bounds = palaz->bounds;
pcu->stats = pc_stats_clone(palaz->stats);
// not optimal but we have to pass by the context manager otherwise
// a segfault happenned (sometimes) during a pcfree of lazperf field
pcu->data = (uint8_t*) pcalloc(palaz->schema->size * palaz->npoints);
memcpy(pcu->data, decompressed, palaz->schema->size * palaz->npoints);
free(decompressed);
pcu->maxpoints = palaz->npoints;
}
else
pcerror("%s: lazperf uncompression failed", __func__);
return pcu;
}
char *
pc_patch_lazperf_to_string(const PCPATCH_LAZPERF *pa)
{
#ifndef HAVE_LAZPERF
pcerror("%s: lazperf support is not enabled", __func__);
return NULL;
#endif
PCPATCH_UNCOMPRESSED *patch = pc_patch_uncompressed_from_lazperf(pa);
char *str = pc_patch_uncompressed_to_string(patch);
pc_patch_free((PCPATCH*) patch);
return str;
}
uint8_t *
pc_patch_lazperf_to_wkb(const PCPATCH_LAZPERF *patch, size_t *wkbsize)
{
#ifndef HAVE_LAZPERF
pcerror("%s: lazperf support is not enabled", __func__);
return NULL;
#else
/*
byte: endianness (1 = NDR, 0 = XDR)
uint32: pcid (key to POINTCLOUD_SCHEMAS)
uint32: compression
uint32: npoints
uint32: lazperfsize
uint8[]: lazperfbuffer
*/
uint8_t *buf;
char endian = machine_endian();
/* endian + pcid + compression + npoints + lazperfsize + lazperf */
size_t size = 1 + 4 + 4 + 4 + 4 + patch->lazperfsize;
uint8_t *wkb = pcalloc(size);
uint32_t compression = patch->type;
uint32_t npoints = patch->npoints;
uint32_t pcid = patch->schema->pcid;
uint32_t lazperfsize = patch->lazperfsize;
wkb[0] = endian; /* Write endian flag */
memcpy(wkb + 1, &pcid, 4); /* Write PCID */
memcpy(wkb + 5, &compression, 4); /* Write compression */
memcpy(wkb + 9, &npoints, 4); /* Write npoints */
memcpy(wkb + 13, &lazperfsize, 4); /* Write lazperf buffer size */
buf = wkb + 17;
memcpy(buf, patch->lazperf, patch->lazperfsize);
if (wkbsize)
*wkbsize = size;
return wkb;
#endif
}
PCPATCH *
pc_patch_lazperf_from_wkb(const PCSCHEMA *schema, const uint8_t *wkb, size_t wkbsize)
{
#ifndef HAVE_LAZPERF
pcerror("%s: lazperf support is not enabled", __func__);
return NULL;
#else
/*
byte: endianness (1 = NDR, 0 = XDR)
uint32: pcid (key to POINTCLOUD_SCHEMAS)
uint32: compression (0 = no compression, 1 = dimensional, 2 = GHT)
uint32: npoints
uint32: lazperfsize
uint8[]: lazerperfbuffer
*/
static size_t hdrsz = 1+4+4+4; /* endian + pcid + compression + npoints */
PCPATCH_LAZPERF *patch;
uint8_t swap_endian = (wkb[0] != machine_endian());
uint32_t npoints;
size_t datasize, lazperfsize;
const uint8_t *buf;
if (wkb_get_compression(wkb) != PC_LAZPERF)
{
pcerror("%s: call with wkb that is not LAZPERF compressed", __func__);
return NULL;
}
npoints = wkb_get_npoints(wkb);
patch = pcalloc(sizeof(PCPATCH_LAZPERF));
patch->type = PC_LAZPERF;
patch->readonly = PC_FALSE;
patch->schema = schema;
patch->npoints = npoints;
/* Start on the LAZPERF */
buf = wkb+hdrsz;
lazperfsize = wkb_get_int32(buf, swap_endian);
buf += 4;
/* Copy in the tree buffer */
patch->lazperfsize = lazperfsize;
patch->lazperf = pcalloc(lazperfsize);
memcpy(patch->lazperf, buf, lazperfsize);
return (PCPATCH*)patch;
#endif
}
int
pc_patch_lazperf_compute_extent(PCPATCH_LAZPERF *patch)
{
#ifndef HAVE_LAZPERF
pcerror("%s: lazperf support is not enabled", __func__);
return NULL;
#endif
PCPATCH_UNCOMPRESSED *pau = pc_patch_uncompressed_from_lazperf(patch);
return pc_patch_uncompressed_compute_extent(pau);
}

View File

@ -124,6 +124,10 @@ pc_pointlist_from_patch(const PCPATCH *patch)
{
return pc_pointlist_from_dimensional((PCPATCH_DIMENSIONAL*)patch);
}
case PC_LAZPERF:
{
return pc_pointlist_from_lazperf((PCPATCH_LAZPERF*)patch);
}
}
/* Don't get here */

View File

@ -100,6 +100,8 @@ pc_compression_name(int num)
return "ght";
case PC_DIMENSIONAL:
return "dimensional";
case PC_LAZPERF:
return "laz";
default:
return "UNKNOWN";
}
@ -117,6 +119,12 @@ pc_compression_number(const char *str)
return PC_DIMENSIONAL;
}
if ( (str[0] == 'l' || str[0] == 'L') &&
(strcasecmp(str, "laz") == 0) )
{
return PC_LAZPERF;
}
if ( (str[0] == 'g' || str[0] == 'G') &&
(strcasecmp(str, "ght") == 0) )
{

View File

@ -33,9 +33,13 @@ ifneq ($(GHT_LDFLAGS),)
REGRESS += pointcloud-ght
endif
ifneq ($(LAZPERF_STATUS), "disabled")
REGRESS += pointcloud-laz
endif
# Add in build/link flags for lib
PG_CPPFLAGS += -I../lib
SHLIB_LINK += ../lib/$(LIB_A) $(filter -lm, $(LIBS)) $(XML2_LDFLAGS) $(ZLIB_LDFLAGS) $(GHT_LDFLAGS)
SHLIB_LINK += ../lib/$(LIB_A) ../lib/$(LIB_A_LAZPERF) -lstdc++ $(filter -lm, $(LIBS)) $(XML2_LDFLAGS) $(ZLIB_LDFLAGS) $(GHT_LDFLAGS)
# We are going to use PGXS for sure
include $(PGXS)

View File

@ -0,0 +1,276 @@
INSERT INTO pointcloud_formats (pcid, srid, schema)
VALUES (5, 0,
'<?xml version="1.0" encoding="UTF-8"?>
<pc:PointCloudSchema xmlns:pc="http://pointcloud.org/schemas/PC/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<pc:dimension>
<pc:position>1</pc:position>
<pc:size>4</pc:size>
<pc:description>X coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>X</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>2</pc:position>
<pc:size>4</pc:size>
<pc:description>Y coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>Y</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>3</pc:position>
<pc:size>4</pc:size>
<pc:description>Z coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>Z</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>4</pc:position>
<pc:size>2</pc:size>
<pc:description>The intensity value is the integer representation of the pulse return magnitude. This value is optional and system specific. However, it should always be included if available.</pc:description>
<pc:name>Intensity</pc:name>
<pc:interpretation>uint16_t</pc:interpretation>
<pc:scale>1</pc:scale>
</pc:dimension>
<pc:metadata>
<Metadata name="compression">laz</Metadata>
</pc:metadata>
</pc:PointCloudSchema>'
);
CREATE TABLE IF NOT EXISTS pa_test_laz (
pa PCPATCH(5)
);
\d pa_test_laz
Table "public.pa_test_laz"
Column | Type | Modifiers
--------+------------+-----------
pa | pcpatch(5) |
INSERT INTO pa_test_laz (pa) VALUES ('0000000005000000000000000200000002000000030000000500060000000200000003000000050008');
INSERT INTO pa_test_laz (pa) VALUES ('000000000500000000000000020000000600000007000000050006000000090000000A00000005000A');
INSERT INTO pa_test_laz (pa) VALUES ('0000000005000000000000000200000002000000030000000500060000000200000003000000050003');
INSERT INTO pa_test_laz (pa) VALUES ('0000000005000000000000000200000002000000030000000500060000000200000003000000050001');
SELECT pc_explode(pa) FROM pa_test_laz;
pc_explode
----------------------------------------
01050000000200000003000000050000000600
01050000000200000003000000050000000800
01050000000600000007000000050000000600
0105000000090000000A000000050000000A00
01050000000200000003000000050000000600
01050000000200000003000000050000000300
01050000000200000003000000050000000600
01050000000200000003000000050000000100
(8 rows)
SELECT pc_astext(pc_explode(pa)) FROM pa_test_laz;
pc_astext
------------------------------------
{"pcid":5,"pt":[0.02,0.03,0.05,6]}
{"pcid":5,"pt":[0.02,0.03,0.05,8]}
{"pcid":5,"pt":[0.06,0.07,0.05,6]}
{"pcid":5,"pt":[0.09,0.1,0.05,10]}
{"pcid":5,"pt":[0.02,0.03,0.05,6]}
{"pcid":5,"pt":[0.02,0.03,0.05,3]}
{"pcid":5,"pt":[0.02,0.03,0.05,6]}
{"pcid":5,"pt":[0.02,0.03,0.05,1]}
(8 rows)
SELECT * FROM pa_test_laz;
pa
------------------------------------------------------------------------------
01050000000300000002000000140000000200000003000000050000000600000005A10000
0105000000030000000200000015000000060000000700000005000000060013884A3A000000
01050000000300000002000000150000000200000003000000050000000600000006D8000000
0105000000030000000200000015000000020000000300000005000000060000000B1E000000
(4 rows)
SELECT Sum(PC_NumPoints(pa)) FROM pa_test_laz;
sum
-----
8
(1 row)
SELECT Sum(PC_MemSize(pa)) FROM pa_test_laz;
sum
-----
487
(1 row)
SELECT Sum(PC_PatchMax(pa,'x')) FROM pa_test_laz;
sum
------
0.15
(1 row)
SELECT Sum(PC_PatchMin(pa,'x')) FROM pa_test_laz;
sum
------
0.12
(1 row)
DELETE FROM pa_test_laz;
INSERT INTO pa_test_laz (pa)
SELECT PC_Patch(PC_MakePoint(5, ARRAY[x,y,z,intensity]))
FROM (
SELECT
-127+a/100.0 AS x,
45+a/100.0 AS y,
1.0*a AS z,
a/10 AS intensity,
a/400 AS gid
FROM generate_series(1,1600) AS a
) AS values GROUP BY gid;
SELECT pc_explode(pa) FROM pa_test_laz LIMIT 20;
pc_explode
----------------------------------------
0105000000A4D4FFFFD417000000710200A000
0105000000F4CFFFFF24130000409C00002800
0105000000F5CFFFFF25130000A49C00002800
0105000000F6CFFFFF26130000089D00002800
0105000000F7CFFFFF271300006C9D00002800
0105000000F8CFFFFF28130000D09D00002800
0105000000F9CFFFFF29130000349E00002800
0105000000FACFFFFF2A130000989E00002800
0105000000FBCFFFFF2B130000FC9E00002800
0105000000FCCFFFFF2C130000609F00002800
0105000000FDCFFFFF2D130000C49F00002800
0105000000FECFFFFF2E13000028A000002900
0105000000FFCFFFFF2F1300008CA000002900
010500000000D0FFFF30130000F0A000002900
010500000001D0FFFF3113000054A100002900
010500000002D0FFFF32130000B8A100002900
010500000003D0FFFF331300001CA200002900
010500000004D0FFFF3413000080A200002900
010500000005D0FFFF35130000E4A200002900
010500000006D0FFFF3613000048A300002900
(20 rows)
SELECT pc_astext(pc_explode(pa)) FROM pa_test_laz LIMIT 20;
pc_astext
----------------------------------------
{"pcid":5,"pt":[-111,61,1600,160]}
{"pcid":5,"pt":[-123,49,400,40]}
{"pcid":5,"pt":[-122.99,49.01,401,40]}
{"pcid":5,"pt":[-122.98,49.02,402,40]}
{"pcid":5,"pt":[-122.97,49.03,403,40]}
{"pcid":5,"pt":[-122.96,49.04,404,40]}
{"pcid":5,"pt":[-122.95,49.05,405,40]}
{"pcid":5,"pt":[-122.94,49.06,406,40]}
{"pcid":5,"pt":[-122.93,49.07,407,40]}
{"pcid":5,"pt":[-122.92,49.08,408,40]}
{"pcid":5,"pt":[-122.91,49.09,409,40]}
{"pcid":5,"pt":[-122.9,49.1,410,41]}
{"pcid":5,"pt":[-122.89,49.11,411,41]}
{"pcid":5,"pt":[-122.88,49.12,412,41]}
{"pcid":5,"pt":[-122.87,49.13,413,41]}
{"pcid":5,"pt":[-122.86,49.14,414,41]}
{"pcid":5,"pt":[-122.85,49.15,415,41]}
{"pcid":5,"pt":[-122.84,49.16,416,41]}
{"pcid":5,"pt":[-122.83,49.17,417,41]}
{"pcid":5,"pt":[-122.82,49.18,418,41]}
(20 rows)
SELECT * FROM pa_test_laz LIMIT 20;
pa
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0105000000030000000100000012000000A4D4FFFFD417000000710200A00001000000
01050000000300000090010000F4000000F4CFFFFF24130000409C0000280003F27D65F909E1B4FF163125F3217D67B1A6E9728B7246DB622DAD94954405C57B953BE39649652F560A1AC414A371CE3967FBC090345EE0B9E0B3E7B024BBE31373EAA273E1696F7D46E70DCAB367103ECFDA0DF36E4F77901A2C8CE2F4718BDF6680076FFB02B320FA9ED30DFF79A17102E4E48D349A3098EC43031CE4E6B294D0A4442354A6626BCDEF20DF2C154144DA5FD99CE0DADBF4C7153ED51B4D43A3A5A0DBF1FBA5CA5C6C9630C1CA7662A002A217E22CF7357FA19EFCBA6F6C7E91035176B2DF753BD5954A923FA6A95C848053BCE36381D7EA15D928A88612B1E49BD6000000
01050000000300000090010000F400000014D3FFFF44160000C0D40100780003F27D65F909E1B4FF163125F3217D67B1A6E9728B7246DB622DAD94954405C57B953BE39649652F560A1AC414A371CE3967FBC090345EE0B9E0B3E7B024BBE31373EAA273E1696F7D46E70DCAB367103ECFDA0DF36E4F77901A2C8CE2F4718BDF6680076FFB02B320FA9ED30DFF79A17102E4E48D349A3098EC43031CE4E6B294D0A4442354A6626BCDEF20DF2C154144DA5FD99CE0DADBF4C7153ED51B4D43A3A5A0DBF1FBA5CA5C6C9630C1CA7662A002A217E22CF7357FA19EFCBA6F6C7E91035176B2DF753BD5954A923FA6A95C848053BCE36381D7EA15D928A88612B1E49BD6000000
0105000000030000008F010000F400000065CEFFFF9511000064000000000003F27D65F909E1B4FF163125F3217D67B1A6E9728B7246DB622DAD94954405D5CF50EBF47F3F94647B9C1238B6EFFF7200D0A51A36EC28ABD9AAAB0C6351B91042FA4DFB9A4C97A00E088A4033DAF7FC2FC798FFDEF31FC02D5C881C2A92086291FDC6832A9E966831DC62112CFBE18094BF4B398D29C9A29EB9868F208DBB735025F1EAA03FD8D064FB7B012CD72D8E6CC5E82B221E17B55000071A6B70136AAB145FB0F48BE8ADB1D33E88E13D7DE876B582245BC78DB10A9CB31673C349380CF301873FF20D65406448D9889258A582908A6D2E85B58918B4B98E50BC5ADB5B11DB000000
01050000000300000090010000F400000084D1FFFFB414000080380100500003F27D65F909E1B4FF163125F3217D67B1A6E9728B7246DB622DAD94954405C57B953BE39649652F560A1AC414A371CE3967FBC090345EE0B9E0B3E7B024BBE31373EAA273E1696F7D46E70DCAB367103ECFDA0DF36E4F77901A2C8CE2F4718BDF6680076FFB02B320FA9ED30DFF79A17102E4E48D349A3098EC43031CE4E6B294D0A4442354A6626BCDEF20DF2C154144DA5FD99CE0DADBF4C7153ED51B4D43A3A5A0DBF1FBA5CA5C6C9630C1CA7662A002A217E22CF7357FA19EFCBA6F6C7E91035176B2DF753BD5954A923FA6A95C848053BCE36381D7EA15D928A88612B1E49BD6000000
(5 rows)
SELECT Sum(PC_NumPoints(pa)) FROM pa_test_laz;
sum
------
1600
(1 row)
SELECT Sum(PC_MemSize(pa)) FROM pa_test_laz;
sum
------
1499
(1 row)
SELECT Max(PC_PatchMax(pa,'x')) FROM pa_test_laz;
max
------
-111
(1 row)
SELECT Min(PC_PatchMin(pa,'x')) FROM pa_test_laz;
min
---------
-126.99
(1 row)
SELECT Min(PC_PatchMin(pa,'z')) FROM pa_test_laz;
min
-----
1
(1 row)
SELECT pc_astext(PC_FilterLessThan(pa, 'z', 5)) FROM pa_test_laz;
pc_astext
----------------------------------------------------------------------------------------------------
{"pcid":5,"pts":[[-126.99,45.01,1,0],[-126.98,45.02,2,0],[-126.97,45.03,3,0],[-126.96,45.04,4,0]]}
(5 rows)
SELECT pc_astext(PC_FilterGreaterThan(pa, 'z', 1595)) FROM pa_test_laz;
pc_astext
------------------------------------------------------------------------------------------------------------------------
{"pcid":5,"pts":[[-111,61,1600,160]]}
{"pcid":5,"pts":[[-111.04,60.96,1596,159],[-111.03,60.97,1597,159],[-111.02,60.98,1598,159],[-111.01,60.99,1599,159]]}
(5 rows)
SELECT pc_astext(PC_FilterEquals(pa, 'z', 500)) FROM pa_test_laz;
pc_astext
-------------------------------------
{"pcid":5,"pts":[[-122,50,500,50]]}
(5 rows)
SELECT pc_astext(PC_FilterBetween(pa, 'z', 500, 505)) FROM pa_test_laz;
pc_astext
----------------------------------------------------------------------------------------------------------------
{"pcid":5,"pts":[[-121.99,50.01,501,50],[-121.98,50.02,502,50],[-121.97,50.03,503,50],[-121.96,50.04,504,50]]}
(5 rows)
DELETE FROM pa_test_laz;
INSERT INTO pa_test_laz( pa ) VALUES ('01050000000300000004000000210000000000000000000000000000000a004417593a34c1c5f74f83179fc2448960000000');
SELECT pc_explode(pa) FROM pa_test_laz;
pc_explode
----------------------------------------
01050000000000000000000000000000000A00
0105000000C8000000BE000000220000000A00
0105000000900100007C010000440000000A00
0105000000580200003A020000660000000A00
(4 rows)
SELECT pc_astext(pc_explode(pa)) FROM pa_test_laz;
pc_astext
---------------------------------
{"pcid":5,"pt":[0,0,0,10]}
{"pcid":5,"pt":[2,1.9,0.34,10]}
{"pcid":5,"pt":[4,3.8,0.68,10]}
{"pcid":5,"pt":[6,5.7,1.02,10]}
(4 rows)
TRUNCATE pointcloud_formats;

View File

@ -470,7 +470,8 @@ FROM p1, ( values
('dimensional','rle'),
('dimensional','zlib'),
('dimensional','sigbits'),
('dimensional','auto')
('dimensional','auto'),
('laz','null')
-- ,('ght',null) -- fails due to https://github.com/pgpointcloud/pointcloud/issues/35
) dimcompr(compr,sc)
ORDER BY compr,sc,v;
@ -536,7 +537,22 @@ ORDER BY compr,sc,v;
compr | 5 | dimensional | zlib | t
compr | 6 | dimensional | zlib | t
compr | 7 | dimensional | zlib | t
(60 rows)
compr | -7 | laz | null | f
compr | -6 | laz | null | f
compr | -5 | laz | null | f
compr | -4 | laz | null | f
compr | -3 | laz | null | f
compr | -2 | laz | null | f
compr | -1 | laz | null | f
compr | 0 | laz | null | f
compr | 1 | laz | null | f
compr | 2 | laz | null | f
compr | 3 | laz | null | f
compr | 4 | laz | null | f
compr | 5 | laz | null | f
compr | 6 | laz | null | f
compr | 7 | laz | null | f
(75 rows)
SELECT PC_Summary(PC_Compress(PC_Patch(PC_MakePoint(10,ARRAY[1,1,1,1,1,1,1])),
'dimensional'))::json->'compr';

View File

@ -625,6 +625,9 @@ Datum pcpatch_compress(PG_FUNCTION_ARGS)
else if ( strcmp(compr_in, "ght") == 0 ) {
schema->compression = PC_GHT;
}
else if ( strcmp(compr_in, "laz") == 0 ) {
schema->compression = PC_LAZPERF;
}
else {
elog(ERROR, "Unrecognized compression '%s'. Please specify 'auto','dimensional' or 'ght'", compr_in);
}

View File

@ -466,6 +466,12 @@ pc_patch_serialized_size(const PCPATCH *patch)
{
return common_size + stats_size + pc_patch_dimensional_serialized_size((PCPATCH_DIMENSIONAL*)patch);
}
case PC_LAZPERF:
{
static size_t lazsize_size = 4;
PCPATCH_LAZPERF *pg = (PCPATCH_LAZPERF*)patch;
return common_size + stats_size + lazsize_size + pg->lazperfsize;
}
default:
{
pcerror("%s: unknown compresed %d", __func__, patch->type);
@ -606,6 +612,45 @@ pc_patch_ght_serialize(const PCPATCH *patch_in)
return serpch;
}
static SERIALIZED_PATCH *
pc_patch_lazperf_serialize(const PCPATCH *patch_in)
{
size_t serpch_size = pc_patch_serialized_size(patch_in);
SERIALIZED_PATCH *serpch = pcalloc(serpch_size);
const PCPATCH_LAZPERF *patch = (PCPATCH_LAZPERF*)patch_in;
uint32_t lazsize = patch->lazperfsize;
uint8_t *buf = serpch->data;
assert(patch);
assert(patch->type == PC_LAZPERF);
/* Copy basics */
serpch->pcid = patch->schema->pcid;
serpch->npoints = patch->npoints;
serpch->bounds = patch->bounds;
serpch->compression = patch->type;
/* Write stats into the buffer first */
if ( patch->stats )
{
buf += pc_patch_stats_serialize(buf, patch->schema, patch->stats);
}
else
{
pcerror("%s: stats missing!", __func__);
}
/* Write buffer size */
memcpy(buf, &(lazsize), 4);
buf += 4;
/* Write buffer */
memcpy(buf, patch->lazperf, patch->lazperfsize);
SET_VARSIZE(serpch, serpch_size);
return serpch;
}
static SERIALIZED_PATCH *
pc_patch_uncompressed_serialize(const PCPATCH *patch_in)
{
@ -693,6 +738,11 @@ pc_patch_serialize(const PCPATCH *patch_in, void *userdata)
serpatch = pc_patch_ght_serialize(patch);
break;
}
case PC_LAZPERF:
{
serpatch = pc_patch_lazperf_serialize(patch);
break;
}
default:
{
pcerror("%s: unsupported compression type %d", __func__, patch->type);
@ -880,6 +930,39 @@ pc_patch_ght_deserialize(const SERIALIZED_PATCH *serpatch, const PCSCHEMA *schem
return (PCPATCH*)patch;
}
static PCPATCH *
pc_patch_lazperf_deserialize(const SERIALIZED_PATCH *serpatch, const PCSCHEMA *schema)
{
PCPATCH_LAZPERF *patch;
uint32_t lazperfsize;
int npoints = serpatch->npoints;
size_t stats_size = pc_stats_size(schema);
uint8_t *buf = (uint8_t*)serpatch->data + stats_size;
/* Reference the external data */
patch = pcalloc(sizeof(PCPATCH_LAZPERF));
/* Set up basic info */
patch->type = serpatch->compression;
patch->schema = schema;
patch->readonly = true;
patch->npoints = npoints;
patch->bounds = serpatch->bounds;
/* Point into the stats area */
patch->stats = pc_patch_stats_deserialize(schema, serpatch->data);
/* Set up buffer */
memcpy(&lazperfsize, buf, 4);
patch->lazperfsize = lazperfsize;
buf += 4;
patch->lazperf = pcalloc( patch->lazperfsize );
memcpy(patch->lazperf, buf, patch->lazperfsize);
return (PCPATCH*)patch;
}
PCPATCH *
pc_patch_deserialize(const SERIALIZED_PATCH *serpatch, const PCSCHEMA *schema)
{
@ -891,6 +974,8 @@ pc_patch_deserialize(const SERIALIZED_PATCH *serpatch, const PCSCHEMA *schema)
return pc_patch_dimensional_deserialize(serpatch, schema);
case PC_GHT:
return pc_patch_ght_deserialize(serpatch, schema);
case PC_LAZPERF:
return pc_patch_lazperf_deserialize(serpatch, schema);
}
pcerror("%s: unsupported compression type", __func__);
return NULL;

View File

@ -0,0 +1,98 @@
INSERT INTO pointcloud_formats (pcid, srid, schema)
VALUES (5, 0,
'<?xml version="1.0" encoding="UTF-8"?>
<pc:PointCloudSchema xmlns:pc="http://pointcloud.org/schemas/PC/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<pc:dimension>
<pc:position>1</pc:position>
<pc:size>4</pc:size>
<pc:description>X coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>X</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>2</pc:position>
<pc:size>4</pc:size>
<pc:description>Y coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>Y</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>3</pc:position>
<pc:size>4</pc:size>
<pc:description>Z coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>Z</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>4</pc:position>
<pc:size>2</pc:size>
<pc:description>The intensity value is the integer representation of the pulse return magnitude. This value is optional and system specific. However, it should always be included if available.</pc:description>
<pc:name>Intensity</pc:name>
<pc:interpretation>uint16_t</pc:interpretation>
<pc:scale>1</pc:scale>
</pc:dimension>
<pc:metadata>
<Metadata name="compression">laz</Metadata>
</pc:metadata>
</pc:PointCloudSchema>'
);
CREATE TABLE IF NOT EXISTS pa_test_laz (
pa PCPATCH(5)
);
\d pa_test_laz
INSERT INTO pa_test_laz (pa) VALUES ('0000000005000000000000000200000002000000030000000500060000000200000003000000050008');
INSERT INTO pa_test_laz (pa) VALUES ('000000000500000000000000020000000600000007000000050006000000090000000A00000005000A');
INSERT INTO pa_test_laz (pa) VALUES ('0000000005000000000000000200000002000000030000000500060000000200000003000000050003');
INSERT INTO pa_test_laz (pa) VALUES ('0000000005000000000000000200000002000000030000000500060000000200000003000000050001');
SELECT pc_explode(pa) FROM pa_test_laz;
SELECT pc_astext(pc_explode(pa)) FROM pa_test_laz;
SELECT * FROM pa_test_laz;
SELECT Sum(PC_NumPoints(pa)) FROM pa_test_laz;
SELECT Sum(PC_MemSize(pa)) FROM pa_test_laz;
SELECT Sum(PC_PatchMax(pa,'x')) FROM pa_test_laz;
SELECT Sum(PC_PatchMin(pa,'x')) FROM pa_test_laz;
DELETE FROM pa_test_laz;
INSERT INTO pa_test_laz (pa)
SELECT PC_Patch(PC_MakePoint(5, ARRAY[x,y,z,intensity]))
FROM (
SELECT
-127+a/100.0 AS x,
45+a/100.0 AS y,
1.0*a AS z,
a/10 AS intensity,
a/400 AS gid
FROM generate_series(1,1600) AS a
) AS values GROUP BY gid;
SELECT pc_explode(pa) FROM pa_test_laz LIMIT 20;
SELECT pc_astext(pc_explode(pa)) FROM pa_test_laz LIMIT 20;
SELECT * FROM pa_test_laz LIMIT 20;
SELECT Sum(PC_NumPoints(pa)) FROM pa_test_laz;
SELECT Sum(PC_MemSize(pa)) FROM pa_test_laz;
SELECT Max(PC_PatchMax(pa,'x')) FROM pa_test_laz;
SELECT Min(PC_PatchMin(pa,'x')) FROM pa_test_laz;
SELECT Min(PC_PatchMin(pa,'z')) FROM pa_test_laz;
SELECT pc_astext(PC_FilterLessThan(pa, 'z', 5)) FROM pa_test_laz;
SELECT pc_astext(PC_FilterGreaterThan(pa, 'z', 1595)) FROM pa_test_laz;
SELECT pc_astext(PC_FilterEquals(pa, 'z', 500)) FROM pa_test_laz;
SELECT pc_astext(PC_FilterBetween(pa, 'z', 500, 505)) FROM pa_test_laz;
DELETE FROM pa_test_laz;
INSERT INTO pa_test_laz( pa ) VALUES ('01050000000300000004000000210000000000000000000000000000000a004417593a34c1c5f74f83179fc2448960000000');
SELECT pc_explode(pa) FROM pa_test_laz;
SELECT pc_astext(pc_explode(pa)) FROM pa_test_laz;
TRUNCATE pointcloud_formats;

View File

@ -318,7 +318,8 @@ FROM p1, ( values
('dimensional','rle'),
('dimensional','zlib'),
('dimensional','sigbits'),
('dimensional','auto')
('dimensional','auto'),
('laz','null')
-- ,('ght',null) -- fails due to https://github.com/pgpointcloud/pointcloud/issues/35
) dimcompr(compr,sc)
ORDER BY compr,sc,v;

View File

@ -0,0 +1,12 @@
#! /bin/sh
DB=compression_benchmark
createdb $DB
psql -d $DB -f pointcloud.sql > /dev/null 2>&1
psql -d $DB -f pointcloud-laz.sql > /dev/null 2>&1
psql -d $DB -f pointcloud-dim.sql > /dev/null 2>&1
psql -d $DB -f getsize.sql
dropdb $DB

View File

@ -0,0 +1,5 @@
SELECT
relname as "Table",
pg_size_pretty(pg_total_relation_size(relid)) As "Size",
pg_size_pretty(pg_total_relation_size(relid) - pg_relation_size(relid)) as "External Size"
FROM pg_catalog.pg_statio_user_tables ORDER BY pg_total_relation_size(relid) DESC;

View File

@ -0,0 +1,63 @@
create EXTENSION if not exists pointcloud;
INSERT INTO pointcloud_formats (pcid, srid, schema)
VALUES (5, 0,
'<?xml version="1.0" encoding="UTF-8"?>
<pc:PointCloudSchema xmlns:pc="http://pointcloud.org/schemas/PC/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<pc:dimension>
<pc:position>1</pc:position>
<pc:size>4</pc:size>
<pc:description>X coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>X</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>2</pc:position>
<pc:size>4</pc:size>
<pc:description>Y coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>Y</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>3</pc:position>
<pc:size>4</pc:size>
<pc:description>Z coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>Z</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>4</pc:position>
<pc:size>2</pc:size>
<pc:description>The intensity value is the integer representation of the pulse return magnitude. This value is optional and system specific. However, it should always be included if available.</pc:description>
<pc:name>Intensity</pc:name>
<pc:interpretation>uint16_t</pc:interpretation>
<pc:scale>1</pc:scale>
</pc:dimension>
<pc:metadata>
<Metadata name="compression">dimensional</Metadata>
</pc:metadata>
</pc:PointCloudSchema>'
);
CREATE TABLE IF NOT EXISTS pa_compression_dimensional (
pa PCPATCH(5)
);
\d pa_compression_dimensional
INSERT INTO pa_compression_dimensional (pa)
SELECT PC_Patch(PC_MakePoint(5, ARRAY[x,y,z,intensity]))
FROM (
SELECT
-127+a/100.0 AS x,
45+a/100.0 AS y,
1.0*a AS z,
a/10 AS intensity,
a/400 AS gid
FROM generate_series(1,100000) AS a
) AS values GROUP BY gid;
TRUNCATE pointcloud_formats;

View File

@ -0,0 +1,63 @@
create EXTENSION if not exists pointcloud;
INSERT INTO pointcloud_formats (pcid, srid, schema)
VALUES (5, 0,
'<?xml version="1.0" encoding="UTF-8"?>
<pc:PointCloudSchema xmlns:pc="http://pointcloud.org/schemas/PC/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<pc:dimension>
<pc:position>1</pc:position>
<pc:size>4</pc:size>
<pc:description>X coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>X</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>2</pc:position>
<pc:size>4</pc:size>
<pc:description>Y coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>Y</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>3</pc:position>
<pc:size>4</pc:size>
<pc:description>Z coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>Z</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>4</pc:position>
<pc:size>2</pc:size>
<pc:description>The intensity value is the integer representation of the pulse return magnitude. This value is optional and system specific. However, it should always be included if available.</pc:description>
<pc:name>Intensity</pc:name>
<pc:interpretation>uint16_t</pc:interpretation>
<pc:scale>1</pc:scale>
</pc:dimension>
<pc:metadata>
<Metadata name="compression">laz</Metadata>
</pc:metadata>
</pc:PointCloudSchema>'
);
CREATE TABLE IF NOT EXISTS pa_compression_laz (
pa PCPATCH(5)
);
\d pa_compression_laz
INSERT INTO pa_compression_laz (pa)
SELECT PC_Patch(PC_MakePoint(5, ARRAY[x,y,z,intensity]))
FROM (
SELECT
-127+a/100.0 AS x,
45+a/100.0 AS y,
1.0*a AS z,
a/10 AS intensity,
a/400 AS gid
FROM generate_series(1,100000) AS a
) AS values GROUP BY gid;
TRUNCATE pointcloud_formats;

View File

@ -0,0 +1,63 @@
create EXTENSION if not exists pointcloud;
INSERT INTO pointcloud_formats (pcid, srid, schema)
VALUES (5, 0,
'<?xml version="1.0" encoding="UTF-8"?>
<pc:PointCloudSchema xmlns:pc="http://pointcloud.org/schemas/PC/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<pc:dimension>
<pc:position>1</pc:position>
<pc:size>4</pc:size>
<pc:description>X coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>X</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>2</pc:position>
<pc:size>4</pc:size>
<pc:description>Y coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>Y</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>3</pc:position>
<pc:size>4</pc:size>
<pc:description>Z coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
<pc:name>Z</pc:name>
<pc:interpretation>int32_t</pc:interpretation>
<pc:scale>0.01</pc:scale>
</pc:dimension>
<pc:dimension>
<pc:position>4</pc:position>
<pc:size>2</pc:size>
<pc:description>The intensity value is the integer representation of the pulse return magnitude. This value is optional and system specific. However, it should always be included if available.</pc:description>
<pc:name>Intensity</pc:name>
<pc:interpretation>uint16_t</pc:interpretation>
<pc:scale>1</pc:scale>
</pc:dimension>
<pc:metadata>
<Metadata name="compression">none</Metadata>
</pc:metadata>
</pc:PointCloudSchema>'
);
CREATE TABLE IF NOT EXISTS pa_compression_none (
pa PCPATCH(5)
);
\d pa_compression_none
INSERT INTO pa_compression_none (pa)
SELECT PC_Patch(PC_MakePoint(5, ARRAY[x,y,z,intensity]))
FROM (
SELECT
-127+a/100.0 AS x,
45+a/100.0 AS y,
1.0*a AS z,
a/10 AS intensity,
a/400 AS gid
FROM generate_series(1,100000) AS a
) AS values GROUP BY gid;
TRUNCATE pointcloud_formats;