diff --git a/lib/cunit/cu_pc_patch_ght.c b/lib/cunit/cu_pc_patch_ght.c index cd5f1bf..24553c3 100644 --- a/lib/cunit/cu_pc_patch_ght.c +++ b/lib/cunit/cu_pc_patch_ght.c @@ -43,7 +43,7 @@ test_patch_ght() { PCPOINT *pt; int i; - int npts = 100; + static int npts = 100; PCPOINTLIST *pl1, *pl2; PCPATCH_GHT *pag; PCPATCH_UNCOMPRESSED *pu; @@ -61,6 +61,8 @@ test_patch_ght() } pag = pc_patch_ght_from_pointlist(pl1); + pc_pointlist_free(pl1); + pu = pc_patch_uncompressed_from_ght(pag); CU_ASSERT_EQUAL(npts, pag->npoints); CU_ASSERT_EQUAL(npts, pu->npoints); @@ -83,7 +85,50 @@ test_patch_ght() pc_patch_uncompressed_free(pu); pc_patch_ght_free(pag); +} + +static void +test_patch_ght_filtering() +{ + int dimnum = 2; /* Z */ + PCPOINT *pt; + int i; + static int npts = 100; + PCPOINTLIST *pl1, *pl2; + PCPATCH_GHT *pag, *pag_filtered; + + pl1 = pc_pointlist_make(npts); + + for ( i = 0; i < npts; i++ ) + { + pt = pc_point_make(simpleschema); + pc_point_set_double_by_name(pt, "x", 45 + i*0.000004); + pc_point_set_double_by_name(pt, "y", 45 + i*0.000001666); + pc_point_set_double_by_name(pt, "Z", 10 + i*0.34); + pc_point_set_double_by_name(pt, "intensity", 10); + pc_pointlist_add_point(pl1, pt); + } + + pag = pc_patch_ght_from_pointlist(pl1); pc_pointlist_free(pl1); + + pag_filtered = pc_patch_ght_filter(pag, dimnum, PC_LT, 10, 10); + CU_ASSERT_EQUAL(pag_filtered->npoints, 0); + pc_patch_ght_free(pag_filtered); + + pag_filtered = pc_patch_ght_filter(pag, dimnum, PC_LT, 11, 11); + CU_ASSERT_EQUAL(pag_filtered->npoints, 3); + pc_patch_ght_free(pag_filtered); + + pag_filtered = pc_patch_ght_filter(pag, dimnum, PC_GT, 11, 11); + CU_ASSERT_EQUAL(pag_filtered->npoints, 97); + pc_patch_ght_free(pag_filtered); + + pag_filtered = pc_patch_ght_filter(pag, dimnum, PC_BETWEEN, 11, 16); + CU_ASSERT_EQUAL(pag_filtered->npoints, 15); + pc_patch_ght_free(pag_filtered); + + pc_patch_ght_free(pag); } @@ -94,6 +139,7 @@ test_patch_ght() CU_TestInfo ght_tests[] = { #ifdef HAVE_LIBGHT PC_TEST(test_patch_ght), + PC_TEST(test_patch_ght_filtering), #endif CU_TEST_INFO_NULL }; diff --git a/lib/pc_api_internal.h b/lib/pc_api_internal.h index e374a71..bbbd234 100644 --- a/lib/pc_api_internal.h +++ b/lib/pc_api_internal.h @@ -189,7 +189,7 @@ int pc_patch_ght_compute_extent(PCPATCH_GHT *patch); uint8_t* pc_patch_ght_to_wkb(const PCPATCH_GHT *patch, size_t *wkbsize); PCPATCH* pc_patch_ght_from_wkb(const PCSCHEMA *schema, const uint8_t *wkb, size_t wkbsize); 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); /**************************************************************************** diff --git a/lib/pc_filter.c b/lib/pc_filter.c index db2a9b7..47655f2 100644 --- a/lib/pc_filter.c +++ b/lib/pc_filter.c @@ -178,25 +178,18 @@ pc_patch_filter(const PCPATCH *pa, uint32_t dimnum, PC_FILTERTYPE filter, double } pu = pc_patch_uncompressed_filter((PCPATCH_UNCOMPRESSED*)pa, 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; } case PC_GHT: { - PCPATCH_UNCOMPRESSED *pu = pc_patch_uncompressed_from_ght((PCPATCH_GHT*)pa); - PCBITMAP *map = pc_patch_uncompressed_bitmap(pu, dimnum, filter, val1, val2); - PCPATCH_UNCOMPRESSED *pu2; - PCPATCH_GHT *pgh; - if ( map->nset == 0 ) - { - pc_bitmap_free(map); - return (PCPATCH*)pc_patch_uncompressed_make(pa->schema, 0); - } - pu2 = pc_patch_uncompressed_filter(pu, map); - pgh = pc_patch_ght_from_uncompressed(pu2); - pc_patch_free((PCPATCH*)pu); - pc_patch_free((PCPATCH*)pu2); - paout = (PCPATCH*)pgh; + PCPATCH_GHT *pgh = pc_patch_ght_filter((PCPATCH_GHT*)pa, dimnum, filter, val1, val2); + /* pc_patch_ght_filter computes the bounds itself */ + /* TODO: add stats computation to pc_patch_ght_filter */ + /* pc_patch_ght_filter is just re-using the input stats, which is wrong */ + paout = (PCPATCH*)pgh; break; } case PC_DIMENSIONAL: @@ -211,18 +204,21 @@ pc_patch_filter(const PCPATCH *pa, uint32_t dimnum, PC_FILTERTYPE filter, double pdl = pc_patch_dimensional_filter((PCPATCH_DIMENSIONAL*)pa, map); pc_bitmap_free(map); paout = (PCPATCH*)pdl; + + /* pc_patch_dimensional_filter does not compute computes stats or bounds, so we need to do it here */ + /* TODO: this is expensive, we should do this while we are traversing the bytes in the filter process */ + if ( PC_FAILURE == pc_patch_compute_extent(paout) ) + pcerror("%s: pc_patch_compute_extent failed", __func__); + + if ( PC_FAILURE == pc_patch_compute_stats(paout) ) + pcerror("%s: pc_patch_compute_stats failed", __func__); + break; } default: pcerror("%s: failure", __func__); } - if ( PC_FAILURE == pc_patch_compute_extent(paout) ) - pcerror("%s: pc_patch_compute_extent failed", __func__); - - if ( PC_FAILURE == pc_patch_compute_stats(paout) ) - pcerror("%s: pc_patch_compute_stats failed", __func__); - return paout; } diff --git a/lib/pc_patch_ght.c b/lib/pc_patch_ght.c index 4ea9896..eed07a4 100644 --- a/lib/pc_patch_ght.c +++ b/lib/pc_patch_ght.c @@ -424,9 +424,9 @@ pc_patch_ght_compute_extent(PCPATCH_GHT *patch) return PC_FAILURE; patch->bounds.xmin = area.x.min; - patch->bounds.xmax = area.x.min; + patch->bounds.xmax = area.x.max; patch->bounds.ymin = area.y.min; - patch->bounds.ymax = area.y.min; + patch->bounds.ymax = area.y.max; ght_tree_free(tree); @@ -487,6 +487,114 @@ pc_patch_ght_to_wkb(const PCPATCH_GHT *patch, size_t *wkbsize) #endif } +PCPATCH_GHT * +pc_patch_ght_filter(const PCPATCH_GHT *patch, uint32_t dimnum, PC_FILTERTYPE filter, double val1, double val2) +{ +#ifndef HAVE_LIBGHT + pcerror("%s: libght 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: ghtsize + uint8[]: ghtbuffer + */ + + GhtTreePtr tree; + GhtTreePtr tree_filtered; + GhtErr err; + GhtWriterPtr writer; + GhtArea area; + const char *dimname; + const PCDIMENSION *dim; + PCPATCH_GHT *paght; + int npoints; + + /* Echo null back */ + if ( ! patch ) return NULL; + + /* Get a tree */ + tree = ght_tree_from_pc_patch(patch); + if ( ! tree ) pcerror("%s: call to ght_tree_from_pc_patch failed", __func__); + + /* Get dimname */ + dim = pc_schema_get_dimension(patch->schema, dimnum); + if ( ! dim ) pcerror("%s: invalid dimension number (%d)", __func__, dimnum); + dimname = dim->name; + + switch ( filter ) + { + case PC_GT: + err = ght_tree_filter_greater_than(tree, dimname, val1 > val2 ? val1 : val2, &tree_filtered); + break; + case PC_LT: + err = ght_tree_filter_less_than(tree, dimname, val1 < val2 ? val1 : val2, &tree_filtered); + break; + case PC_EQUAL: + err = ght_tree_filter_equal(tree, dimname, val1, &tree_filtered); + break; + case PC_BETWEEN: + err = ght_tree_filter_between(tree, dimname, val1, val2, &tree_filtered); + break; + default: + pcerror("%s: invalid filter type (%d)", __func__, filter); + } + + /* ght_tree_filter_* returns a tree with NULL tree element and npoints == 0 */ + /* for empty filter results (everything got filtered away) */ + if ( err != GHT_OK || ! tree_filtered ) + pcerror("%s: ght_tree_filter failed", __func__); + + /* Read numpoints left in patch */ + ght_tree_get_numpoints(tree_filtered, &(npoints)); + + /* Allocate a fresh GHT patch for output */ + paght = pcalloc(sizeof(PCPATCH_GHT)); + paght->type = PC_GHT; + paght->readonly = PC_FALSE; + paght->schema = patch->schema; + paght->npoints = npoints; + + /* No points, not much to do... */ + if ( ! npoints ) + { + paght->ghtsize = 0; + paght->ght = NULL; + } + else + { + /* Calculate bounds and save */ + if ( GHT_OK != ght_tree_get_extent(tree_filtered, &area) ) + pcerror("%s: ght_tree_get_extent failed", __func__); + + paght->bounds.xmin = area.x.min; + paght->bounds.xmax = area.x.max; + paght->bounds.ymin = area.y.min; + paght->bounds.ymax = area.y.max; + + /* TODO: Replace this; need to update stats too */ + paght->stats = pc_stats_clone(patch->stats); + + /* Convert the tree to a memory buffer */ + ght_writer_new_mem(&writer); + ght_tree_write(tree_filtered, writer); + ght_writer_get_size(writer, &(paght->ghtsize)); + paght->ght = pcalloc(paght->ghtsize); + ght_writer_get_bytes(writer, paght->ght); + ght_writer_free(writer); + } + + ght_tree_free(tree_filtered); + ght_tree_free(tree); + + return paght; + +#endif +} + PCPOINTLIST * pc_pointlist_from_ght(const PCPATCH_GHT *pag) diff --git a/pgsql/pc_access.c b/pgsql/pc_access.c index ada2e9c..99ca6b0 100644 --- a/pgsql/pc_access.c +++ b/pgsql/pc_access.c @@ -685,6 +685,7 @@ Datum pcpatch_filter(PG_FUNCTION_ARGS) } pfree(dim_name); + /* Always treat zero-point patches as SQL NULL */ if ( patch_filtered->npoints <= 0 ) { pc_patch_free(patch_filtered);