mirror of
https://github.com/pgpointcloud/pointcloud.git
synced 2025-12-08 20:36:04 +00:00
Print an error if it does, and use pcerror from within the function to reduce the likelihood of returning NULL w/out an actual error. Fixes the crash reported in #31
436 lines
9.8 KiB
C
436 lines
9.8 KiB
C
/***********************************************************************
|
|
* pc_patch.c
|
|
*
|
|
* Pointclound patch handling. Create, get and set values from the
|
|
* basic PCPATCH structure.
|
|
*
|
|
* PgSQL Pointcloud is free and open source software provided
|
|
* by the Government of Canada
|
|
* Copyright (c) 2013 Natural Resources Canada
|
|
*
|
|
***********************************************************************/
|
|
|
|
#include <math.h>
|
|
#include <assert.h>
|
|
#include "pc_api_internal.h"
|
|
#include "stringbuffer.h"
|
|
|
|
int
|
|
pc_patch_compute_extent(PCPATCH *pa)
|
|
{
|
|
if ( ! pa ) return PC_FAILURE;
|
|
switch ( pa->type )
|
|
{
|
|
case PC_NONE:
|
|
return pc_patch_uncompressed_compute_extent((PCPATCH_UNCOMPRESSED*)pa);
|
|
case PC_GHT:
|
|
return pc_patch_ght_compute_extent((PCPATCH_GHT*)pa);
|
|
case PC_DIMENSIONAL:
|
|
return pc_patch_dimensional_compute_extent((PCPATCH_DIMENSIONAL*)pa);
|
|
}
|
|
return PC_FAILURE;
|
|
}
|
|
|
|
/**
|
|
* Calculate or re-calculate statistics for a patch.
|
|
*/
|
|
int
|
|
pc_patch_compute_stats(PCPATCH *pa)
|
|
{
|
|
if ( ! pa ) return PC_FAILURE;
|
|
|
|
switch ( pa->type )
|
|
{
|
|
case PC_NONE:
|
|
return pc_patch_uncompressed_compute_stats((PCPATCH_UNCOMPRESSED*)pa);
|
|
|
|
case PC_DIMENSIONAL:
|
|
{
|
|
PCPATCH_UNCOMPRESSED *pu = pc_patch_uncompressed_from_dimensional((PCPATCH_DIMENSIONAL*)pa);
|
|
pc_patch_uncompressed_compute_stats(pu);
|
|
pa->stats = pc_stats_clone(pu->stats);
|
|
pc_patch_uncompressed_free(pu);
|
|
return PC_SUCCESS;
|
|
}
|
|
case PC_GHT:
|
|
{
|
|
PCPATCH_UNCOMPRESSED *pu = pc_patch_uncompressed_from_ght((PCPATCH_GHT*)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);
|
|
return PC_FAILURE;
|
|
}
|
|
}
|
|
|
|
return PC_FAILURE;
|
|
}
|
|
|
|
void
|
|
pc_patch_free(PCPATCH *patch)
|
|
{
|
|
if ( patch->stats )
|
|
{
|
|
pc_stats_free( patch->stats );
|
|
patch->stats = NULL;
|
|
}
|
|
|
|
switch( patch->type )
|
|
{
|
|
case PC_NONE:
|
|
{
|
|
pc_patch_uncompressed_free((PCPATCH_UNCOMPRESSED*)patch);
|
|
break;
|
|
}
|
|
case PC_GHT:
|
|
{
|
|
pc_patch_ght_free((PCPATCH_GHT*)patch);
|
|
break;
|
|
}
|
|
case PC_DIMENSIONAL:
|
|
{
|
|
pc_patch_dimensional_free((PCPATCH_DIMENSIONAL*)patch);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
pcerror("%s: unknown compression type %d", __func__, patch->type);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
PCPATCH *
|
|
pc_patch_from_pointlist(const PCPOINTLIST *ptl)
|
|
{
|
|
return (PCPATCH*)pc_patch_uncompressed_from_pointlist(ptl);
|
|
}
|
|
|
|
|
|
PCPATCH *
|
|
pc_patch_compress(const PCPATCH *patch, void *userdata)
|
|
{
|
|
uint32_t schema_compression = patch->schema->compression;
|
|
uint32_t patch_compression = patch->type;
|
|
|
|
switch ( schema_compression )
|
|
{
|
|
case PC_DIMENSIONAL:
|
|
{
|
|
if ( patch_compression == PC_NONE )
|
|
{
|
|
/* Dimensionalize, dimensionally compress, return */
|
|
PCPATCH_DIMENSIONAL *pcdu = pc_patch_dimensional_from_uncompressed((PCPATCH_UNCOMPRESSED*)patch);
|
|
PCPATCH_DIMENSIONAL *pcdd = pc_patch_dimensional_compress(pcdu, (PCDIMSTATS*)userdata);
|
|
pc_patch_dimensional_free(pcdu);
|
|
return (PCPATCH*)pcdd;
|
|
}
|
|
else if ( patch_compression == PC_DIMENSIONAL )
|
|
{
|
|
/* Make sure it's compressed, return */
|
|
return (PCPATCH*)pc_patch_dimensional_compress((PCPATCH_DIMENSIONAL*)patch, (PCDIMSTATS*)userdata);
|
|
}
|
|
else if ( patch_compression == PC_GHT )
|
|
{
|
|
/* Uncompress, dimensionalize, dimensionally compress, return */
|
|
PCPATCH_UNCOMPRESSED *pcu = pc_patch_uncompressed_from_ght((PCPATCH_GHT*)patch);
|
|
PCPATCH_DIMENSIONAL *pcdu = pc_patch_dimensional_from_uncompressed(pcu);
|
|
PCPATCH_DIMENSIONAL *pcdc = pc_patch_dimensional_compress(pcdu, NULL);
|
|
pc_patch_dimensional_free(pcdu);
|
|
return (PCPATCH*)pcdc;
|
|
}
|
|
else
|
|
{
|
|
pcerror("%s: unknown patch compression type %d", __func__, patch_compression);
|
|
}
|
|
}
|
|
case PC_NONE:
|
|
{
|
|
if ( patch_compression == PC_NONE )
|
|
{
|
|
return (PCPATCH*)patch;
|
|
}
|
|
else if ( patch_compression == PC_DIMENSIONAL )
|
|
{
|
|
PCPATCH_UNCOMPRESSED *pcu = pc_patch_uncompressed_from_dimensional((PCPATCH_DIMENSIONAL*)patch);
|
|
return (PCPATCH*)pcu;
|
|
|
|
}
|
|
else if ( patch_compression == PC_GHT )
|
|
{
|
|
PCPATCH_UNCOMPRESSED *pcu = pc_patch_uncompressed_from_ght((PCPATCH_GHT*)patch);
|
|
return (PCPATCH*)pcu;
|
|
}
|
|
else
|
|
{
|
|
pcerror("%s: unknown patch compression type %d", __func__, patch_compression);
|
|
}
|
|
}
|
|
case PC_GHT:
|
|
{
|
|
if ( patch_compression == PC_NONE )
|
|
{
|
|
PCPATCH_GHT *pgc = pc_patch_ght_from_uncompressed((PCPATCH_UNCOMPRESSED*)patch);
|
|
if ( ! pgc ) pcerror("%s: ght compression failed", __func__);
|
|
return (PCPATCH*)pgc;
|
|
}
|
|
else if ( patch_compression == PC_DIMENSIONAL )
|
|
{
|
|
PCPATCH_UNCOMPRESSED *pcu = pc_patch_uncompressed_from_dimensional((PCPATCH_DIMENSIONAL*)patch);
|
|
PCPATCH_GHT *pgc = pc_patch_ght_from_uncompressed((PCPATCH_UNCOMPRESSED*)patch);
|
|
pc_patch_uncompressed_free(pcu);
|
|
return (PCPATCH*)pgc;
|
|
}
|
|
else if ( patch_compression == PC_GHT )
|
|
{
|
|
return (PCPATCH*)patch;
|
|
}
|
|
else
|
|
{
|
|
pcerror("%s: unknown patch compression type %d", __func__, patch_compression);
|
|
}
|
|
}
|
|
default:
|
|
{
|
|
pcerror("%s: unknown schema compression type %d", __func__, schema_compression);
|
|
}
|
|
}
|
|
|
|
pcerror("%s: fatal error", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PCPATCH *
|
|
pc_patch_uncompress(const PCPATCH *patch)
|
|
{
|
|
uint32_t patch_compression = patch->type;
|
|
|
|
if ( patch_compression == PC_DIMENSIONAL )
|
|
{
|
|
PCPATCH_UNCOMPRESSED *pu = pc_patch_uncompressed_from_dimensional((PCPATCH_DIMENSIONAL*)patch);
|
|
return (PCPATCH*)pu;
|
|
}
|
|
|
|
if ( patch_compression == PC_NONE )
|
|
{
|
|
return (PCPATCH*)patch;
|
|
}
|
|
|
|
if ( patch_compression == PC_GHT )
|
|
{
|
|
PCPATCH_UNCOMPRESSED *pu = pc_patch_uncompressed_from_ght((PCPATCH_GHT*)patch);
|
|
return (PCPATCH*)pu;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
PCPATCH *
|
|
pc_patch_from_wkb(const PCSCHEMA *s, uint8_t *wkb, size_t wkbsize)
|
|
{
|
|
/*
|
|
byte: endianness (1 = NDR, 0 = XDR)
|
|
uint32: pcid (key to POINTCLOUD_SCHEMAS)
|
|
uint32: compression (0 = no compression, 1 = dimensional, 2 = GHT)
|
|
uchar[]: data (interpret relative to pcid and compression)
|
|
*/
|
|
uint32_t compression, pcid;
|
|
PCPATCH *patch;
|
|
|
|
if ( ! wkbsize )
|
|
{
|
|
pcerror("%s: zero length wkb", __func__);
|
|
}
|
|
|
|
/*
|
|
* It is possible for the WKB compression to be different from the
|
|
* schema compression at this point. The schema compression is only
|
|
* forced at serialization time.
|
|
*/
|
|
pcid = wkb_get_pcid(wkb);
|
|
compression = wkb_get_compression(wkb);
|
|
|
|
if ( pcid != s->pcid )
|
|
{
|
|
pcerror("%s: wkb pcid (%d) not consistent with schema pcid (%d)", __func__, pcid, s->pcid);
|
|
}
|
|
|
|
switch ( compression )
|
|
{
|
|
case PC_NONE:
|
|
{
|
|
patch = pc_patch_uncompressed_from_wkb(s, wkb, wkbsize);
|
|
break;
|
|
}
|
|
case PC_DIMENSIONAL:
|
|
{
|
|
patch = pc_patch_dimensional_from_wkb(s, wkb, wkbsize);
|
|
break;
|
|
}
|
|
case PC_GHT:
|
|
{
|
|
patch = pc_patch_ght_from_wkb(s, wkb, wkbsize);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
/* Don't get here */
|
|
pcerror("%s: unknown compression '%d' requested", __func__, compression);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if ( PC_FAILURE == pc_patch_compute_extent(patch) )
|
|
pcerror("%s: pc_patch_compute_extent failed", __func__);
|
|
|
|
if ( PC_FAILURE == pc_patch_compute_stats(patch) )
|
|
pcerror("%s: pc_patch_compute_stats failed", __func__);
|
|
|
|
return patch;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t *
|
|
pc_patch_to_wkb(const PCPATCH *patch, size_t *wkbsize)
|
|
{
|
|
/*
|
|
byte: endianness (1 = NDR, 0 = XDR)
|
|
uint32: pcid (key to POINTCLOUD_SCHEMAS)
|
|
uint32: compression (0 = no compression, 1 = dimensional, 2 = GHT)
|
|
uchar[]: data (interpret relative to pcid and compression)
|
|
*/
|
|
switch ( patch->type )
|
|
{
|
|
case PC_NONE:
|
|
{
|
|
return pc_patch_uncompressed_to_wkb((PCPATCH_UNCOMPRESSED*)patch, wkbsize);
|
|
}
|
|
case PC_DIMENSIONAL:
|
|
{
|
|
return pc_patch_dimensional_to_wkb((PCPATCH_DIMENSIONAL*)patch, wkbsize);
|
|
}
|
|
case PC_GHT:
|
|
{
|
|
return pc_patch_ght_to_wkb((PCPATCH_GHT*)patch, wkbsize);
|
|
}
|
|
}
|
|
pcerror("%s: unknown compression requested '%d'", __func__, patch->schema->compression);
|
|
return NULL;
|
|
}
|
|
|
|
char *
|
|
pc_patch_to_string(const PCPATCH *patch)
|
|
{
|
|
switch( patch->type )
|
|
{
|
|
case PC_NONE:
|
|
return pc_patch_uncompressed_to_string((PCPATCH_UNCOMPRESSED*)patch);
|
|
case PC_DIMENSIONAL:
|
|
return pc_patch_dimensional_to_string((PCPATCH_DIMENSIONAL*)patch);
|
|
case PC_GHT:
|
|
return pc_patch_ght_to_string((PCPATCH_GHT*)patch);
|
|
}
|
|
pcerror("%s: unsupported compression %d requested", __func__, patch->type);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PCPATCH *
|
|
pc_patch_from_patchlist(PCPATCH **palist, int numpatches)
|
|
{
|
|
int i;
|
|
uint32_t totalpoints = 0;
|
|
PCPATCH_UNCOMPRESSED *paout;
|
|
const PCSCHEMA *schema = NULL;
|
|
uint8_t *buf;
|
|
|
|
assert(palist);
|
|
assert(numpatches);
|
|
|
|
/* All schemas better be the same... */
|
|
schema = palist[0]->schema;
|
|
|
|
/* How many points will this output have? */
|
|
for ( i = 0; i < numpatches; i++ )
|
|
{
|
|
if ( schema->pcid != palist[i]->schema->pcid )
|
|
{
|
|
pcerror("%s: inconsistent schemas in input", __func__);
|
|
return NULL;
|
|
}
|
|
totalpoints += palist[i]->npoints;
|
|
}
|
|
|
|
/* Blank output */
|
|
paout = pc_patch_uncompressed_make(schema, totalpoints);
|
|
buf = paout->data;
|
|
|
|
/* Uncompress dimensionals, copy uncompressed */
|
|
for ( i = 0; i < numpatches; i++ )
|
|
{
|
|
const PCPATCH *pa = palist[i];
|
|
|
|
/* Update bounds */
|
|
pc_bounds_merge(&(paout->bounds), &(pa->bounds));
|
|
|
|
switch ( pa->type )
|
|
{
|
|
case PC_DIMENSIONAL:
|
|
{
|
|
PCPATCH_UNCOMPRESSED *pu = pc_patch_uncompressed_from_dimensional((const PCPATCH_DIMENSIONAL*)pa);
|
|
size_t sz = pu->schema->size * pu->npoints;
|
|
memcpy(buf, pu->data, sz);
|
|
buf += sz;
|
|
pc_patch_uncompressed_free(pu);
|
|
break;
|
|
}
|
|
case PC_GHT:
|
|
{
|
|
PCPATCH_UNCOMPRESSED *pu = pc_patch_uncompressed_from_ght((const PCPATCH_GHT*)pa);
|
|
size_t sz = pu->schema->size * pu->npoints;
|
|
memcpy(buf, pu->data, sz);
|
|
buf += sz;
|
|
pc_patch_uncompressed_free(pu);
|
|
break;
|
|
}
|
|
case PC_NONE:
|
|
{
|
|
PCPATCH_UNCOMPRESSED *pu = (PCPATCH_UNCOMPRESSED*)pa;
|
|
size_t sz = pu->schema->size * pu->npoints;
|
|
memcpy(buf, pu->data, sz);
|
|
buf += sz;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
pcerror("%s: unknown compression type (%d)", __func__, pa->type);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
paout->npoints = totalpoints;
|
|
|
|
if ( PC_FAILURE == pc_patch_uncompressed_compute_stats(paout) )
|
|
{
|
|
pcerror("%s: stats computation failed", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
return (PCPATCH*)paout;
|
|
}
|
|
|