mirror of
https://github.com/pgpointcloud/pointcloud.git
synced 2025-12-08 20:36:04 +00:00
365 lines
9.5 KiB
C
365 lines
9.5 KiB
C
/***********************************************************************
|
|
* pc_filter.c
|
|
*
|
|
* Pointclound patch filtering.
|
|
*
|
|
* Copyright (c) 2013 OpenGeo
|
|
*
|
|
***********************************************************************/
|
|
|
|
#include "pc_api_internal.h"
|
|
#include <assert.h>
|
|
#include <float.h>
|
|
|
|
PCBITMAP *pc_bitmap_new(uint32_t npoints)
|
|
{
|
|
PCBITMAP *map = pcalloc(sizeof(PCBITMAP));
|
|
map->map = pcalloc(sizeof(uint8_t) * npoints);
|
|
map->npoints = npoints;
|
|
map->nset = 0;
|
|
return map;
|
|
}
|
|
|
|
void pc_bitmap_free(PCBITMAP *map)
|
|
{
|
|
if (map->map)
|
|
pcfree(map->map);
|
|
pcfree(map);
|
|
}
|
|
|
|
static inline void pc_bitmap_set(PCBITMAP *map, int i, int val)
|
|
{
|
|
uint8_t curval = map->map[i];
|
|
if (val && (!curval))
|
|
map->nset++;
|
|
if ((!val) && curval)
|
|
map->nset--;
|
|
|
|
map->map[i] = (val != 0);
|
|
}
|
|
|
|
void pc_bitmap_filter(PCBITMAP *map, PC_FILTERTYPE filter, int i, double d,
|
|
double val1, double val2)
|
|
{
|
|
switch (filter)
|
|
{
|
|
case PC_GT:
|
|
pc_bitmap_set(map, i, (d > val1 ? 1 : 0));
|
|
break;
|
|
case PC_LT:
|
|
pc_bitmap_set(map, i, (d < val1 ? 1 : 0));
|
|
break;
|
|
case PC_EQUAL:
|
|
pc_bitmap_set(map, i, (d == val1 ? 1 : 0));
|
|
break;
|
|
case PC_BETWEEN:
|
|
pc_bitmap_set(map, i, (d > val1 && d < val2 ? 1 : 0));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static PCBITMAP *pc_patch_uncompressed_bitmap(const PCPATCH_UNCOMPRESSED *pa,
|
|
uint32_t dimnum,
|
|
PC_FILTERTYPE filter, double val1,
|
|
double val2)
|
|
{
|
|
PCPOINT pt;
|
|
uint32_t i = 0;
|
|
uint8_t *buf = pa->data;
|
|
double d;
|
|
size_t sz = pa->schema->size;
|
|
PCBITMAP *map = pc_bitmap_new(pa->npoints);
|
|
|
|
pt.readonly = PC_TRUE;
|
|
pt.schema = pa->schema;
|
|
|
|
while (i < pa->npoints)
|
|
{
|
|
pt.data = buf;
|
|
pc_point_get_double(&pt, pa->schema->dims[dimnum], &d);
|
|
/* Apply the filter to the bitmap */
|
|
pc_bitmap_filter(map, filter, i, d, val1, val2);
|
|
/* Advance data pointer and counter */
|
|
buf += sz;
|
|
i++;
|
|
}
|
|
|
|
return map;
|
|
}
|
|
|
|
static PCPATCH_UNCOMPRESSED *
|
|
pc_patch_uncompressed_filter(const PCPATCH_UNCOMPRESSED *pu,
|
|
const PCBITMAP *map)
|
|
{
|
|
int i = 0;
|
|
size_t sz = pu->schema->size;
|
|
PCPATCH_UNCOMPRESSED *fpu = pc_patch_uncompressed_make(pu->schema, map->nset);
|
|
uint8_t *buf = pu->data;
|
|
uint8_t *fbuf = fpu->data;
|
|
|
|
assert(map->npoints == pu->npoints);
|
|
|
|
while (i < pu->npoints)
|
|
{
|
|
if (pc_bitmap_get(map, i))
|
|
{
|
|
memcpy(fbuf, buf, sz);
|
|
fbuf += sz;
|
|
}
|
|
buf += sz;
|
|
i++;
|
|
}
|
|
|
|
fpu->maxpoints = fpu->npoints = map->nset;
|
|
|
|
if (PC_FAILURE == pc_patch_uncompressed_compute_extent(fpu))
|
|
{
|
|
pcerror("%s: failed to compute patch extent", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
if (PC_FAILURE == pc_patch_uncompressed_compute_stats(fpu))
|
|
{
|
|
pcerror("%s: failed to compute patch stats", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
return fpu;
|
|
}
|
|
|
|
static PCBITMAP *pc_patch_dimensional_bitmap(const PCPATCH_DIMENSIONAL *pdl,
|
|
uint32_t dimnum,
|
|
PC_FILTERTYPE filter, double val1,
|
|
double val2)
|
|
{
|
|
assert(dimnum < pdl->schema->ndims);
|
|
double unscaled1 = pc_value_unscale_unoffset(val1, pdl->schema->dims[dimnum]);
|
|
double unscaled2 = pc_value_unscale_unoffset(val2, pdl->schema->dims[dimnum]);
|
|
|
|
return pc_bytes_bitmap(&(pdl->bytes[dimnum]), filter, unscaled1, unscaled2);
|
|
}
|
|
|
|
static PCPATCH_DIMENSIONAL *
|
|
pc_patch_dimensional_filter(const PCPATCH_DIMENSIONAL *pdl, const PCBITMAP *map)
|
|
{
|
|
int i = 0;
|
|
PCPATCH_DIMENSIONAL *fpdl = pc_patch_dimensional_clone(pdl);
|
|
|
|
fpdl->stats = pc_stats_clone(pdl->stats);
|
|
fpdl->npoints = map->nset;
|
|
|
|
for (i = 0; i < pdl->schema->ndims; i++)
|
|
{
|
|
PCDIMENSION *dim;
|
|
PCDOUBLESTAT stats;
|
|
stats.min = FLT_MAX;
|
|
stats.max = -1 * FLT_MAX;
|
|
stats.sum = 0;
|
|
fpdl->bytes[i] = pc_bytes_filter(&(pdl->bytes[i]), map, &stats);
|
|
|
|
/* Apply scale and offset */
|
|
dim = pdl->schema->dims[i];
|
|
stats.min = pc_value_scale_offset(stats.min, dim);
|
|
stats.max = pc_value_scale_offset(stats.max, dim);
|
|
stats.sum = pc_value_scale_offset(stats.sum, dim);
|
|
|
|
/* Save the X/Y stats for use in bounds later */
|
|
if (dim == pdl->schema->xdim)
|
|
{
|
|
fpdl->bounds.xmin = stats.min;
|
|
fpdl->bounds.xmax = stats.max;
|
|
}
|
|
else if (dim == pdl->schema->ydim)
|
|
{
|
|
fpdl->bounds.ymin = stats.min;
|
|
fpdl->bounds.ymax = stats.max;
|
|
}
|
|
|
|
pc_point_set_double_by_index(&(fpdl->stats->min), i, stats.min);
|
|
pc_point_set_double_by_index(&(fpdl->stats->max), i, stats.max);
|
|
pc_point_set_double_by_index(&(fpdl->stats->avg), i,
|
|
stats.sum / fpdl->npoints);
|
|
}
|
|
|
|
return fpdl;
|
|
}
|
|
|
|
/* See if it's possible for the filter to have any results, given the stats */
|
|
static int pc_patch_filter_has_results(const PCSTATS *stats, uint32_t dimnum,
|
|
PC_FILTERTYPE filter, double val1,
|
|
double val2)
|
|
{
|
|
double min, max;
|
|
pc_point_get_double_by_index(&(stats->min), dimnum, &min);
|
|
pc_point_get_double_by_index(&(stats->max), dimnum, &max);
|
|
switch (filter)
|
|
{
|
|
case PC_GT:
|
|
{
|
|
if (max < val1)
|
|
return PC_FALSE;
|
|
break;
|
|
}
|
|
case PC_LT:
|
|
{
|
|
if (min > val1)
|
|
return PC_FALSE;
|
|
break;
|
|
}
|
|
case PC_EQUAL:
|
|
{
|
|
if (min > val1 || max < val1)
|
|
return PC_FALSE;
|
|
break;
|
|
}
|
|
case PC_BETWEEN:
|
|
{
|
|
if (min > val2 || max < val1)
|
|
return PC_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
return PC_TRUE;
|
|
}
|
|
|
|
PCPATCH *pc_patch_filter(const PCPATCH *pa, uint32_t dimnum,
|
|
PC_FILTERTYPE filter, double val1, double val2)
|
|
{
|
|
if (!pa)
|
|
return NULL;
|
|
PCPATCH *paout;
|
|
|
|
/* If the stats say this filter returns an empty result, do that */
|
|
if (pa->stats &&
|
|
!pc_patch_filter_has_results(pa->stats, dimnum, filter, val1, val2))
|
|
{
|
|
/* Empty uncompressed patch to return */
|
|
paout = (PCPATCH *)pc_patch_uncompressed_make(pa->schema, 0);
|
|
return paout;
|
|
}
|
|
|
|
switch (pa->type)
|
|
{
|
|
case PC_NONE:
|
|
{
|
|
PCBITMAP *map = pc_patch_uncompressed_bitmap((PCPATCH_UNCOMPRESSED *)pa,
|
|
dimnum, filter, val1, val2);
|
|
PCPATCH_UNCOMPRESSED *pu;
|
|
if (map->nset == 0)
|
|
{
|
|
pc_bitmap_free(map);
|
|
return (PCPATCH *)pc_patch_uncompressed_make(pa->schema, 0);
|
|
}
|
|
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_DIMENSIONAL:
|
|
{
|
|
PCBITMAP *map = pc_patch_dimensional_bitmap((PCPATCH_DIMENSIONAL *)pa,
|
|
dimnum, filter, val1, val2);
|
|
PCPATCH_DIMENSIONAL *pdl;
|
|
if (map->nset == 0)
|
|
{
|
|
pc_bitmap_free(map);
|
|
return (PCPATCH *)pc_patch_uncompressed_make(pa->schema, 0);
|
|
}
|
|
pdl = pc_patch_dimensional_filter((PCPATCH_DIMENSIONAL *)pa, map);
|
|
pc_bitmap_free(map);
|
|
/* pc_patch_dimensional_filter computes both stats and bounds, so we're
|
|
* done*/
|
|
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);
|
|
pc_patch_free((PCPATCH *)pau);
|
|
return (PCPATCH *)pc_patch_uncompressed_make(pa->schema, 0);
|
|
}
|
|
|
|
pu = pc_patch_uncompressed_filter(pau, map);
|
|
pc_bitmap_free(map);
|
|
pc_patch_free((PCPATCH *)pau);
|
|
/* 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;
|
|
}
|
|
|
|
return paout;
|
|
}
|
|
|
|
PCPATCH *pc_patch_filter_lt_by_name(const PCPATCH *pa, const char *name,
|
|
double val)
|
|
{
|
|
/* Error out if we can't find the name */
|
|
PCDIMENSION *d = pc_schema_get_dimension_by_name(pa->schema, name);
|
|
if (!d)
|
|
return NULL;
|
|
|
|
return pc_patch_filter(pa, d->position, PC_LT, val, val);
|
|
}
|
|
|
|
PCPATCH *pc_patch_filter_gt_by_name(const PCPATCH *pa, const char *name,
|
|
double val)
|
|
{
|
|
/* Error out if we can't find the name */
|
|
PCDIMENSION *d = pc_schema_get_dimension_by_name(pa->schema, name);
|
|
if (!d)
|
|
return NULL;
|
|
|
|
return pc_patch_filter(pa, d->position, PC_GT, val, val);
|
|
}
|
|
|
|
PCPATCH *pc_patch_filter_equal_by_name(const PCPATCH *pa, const char *name,
|
|
double val)
|
|
{
|
|
/* Error out if we can't find the name */
|
|
PCDIMENSION *d = pc_schema_get_dimension_by_name(pa->schema, name);
|
|
if (!d)
|
|
return NULL;
|
|
|
|
return pc_patch_filter(pa, d->position, PC_EQUAL, val, val);
|
|
}
|
|
|
|
PCPATCH *pc_patch_filter_between_by_name(const PCPATCH *pa, const char *name,
|
|
double val1, double val2)
|
|
{
|
|
/* Ensure val1 < val2 always */
|
|
if (val1 > val2)
|
|
{
|
|
double tmp = val1;
|
|
val1 = val2;
|
|
val2 = tmp;
|
|
}
|
|
/* Error out if we can't find the name */
|
|
PCDIMENSION *d = pc_schema_get_dimension_by_name(pa->schema, name);
|
|
if (!d)
|
|
return NULL;
|
|
|
|
return pc_patch_filter(pa, d->position, PC_BETWEEN, val1, val2);
|
|
}
|