/*********************************************************************** * 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 #include #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; }