diff --git a/lib/cunit/cu_pc_patch.c b/lib/cunit/cu_pc_patch.c index 8ecb245..780e683 100644 --- a/lib/cunit/cu_pc_patch.c +++ b/lib/cunit/cu_pc_patch.c @@ -111,11 +111,19 @@ test_patch_hex_in() uint8_t *wkb = bytes_from_hexbytes(hexbuf, hexsize); PCPATCH *pa = pc_patch_from_wkb(simpleschema, wkb, hexsize/2); PCPOINTLIST *pl = pc_pointlist_from_patch(pa); + pc_point_get_double_by_name(pc_pointlist_get_point(pl, 0), "X", &d); CU_ASSERT_DOUBLE_EQUAL(d, 0.02, 0.000001); pc_point_get_double_by_name(pc_pointlist_get_point(pl, 1), "Intensity", &d); CU_ASSERT_DOUBLE_EQUAL(d, 8, 0.000001); + pc_point_get_double_by_name(&(pa->stats->min), "Intensity", &d); + CU_ASSERT_DOUBLE_EQUAL(d, 6, 0.000001); + pc_point_get_double_by_name(&(pa->stats->max), "Intensity", &d); + CU_ASSERT_DOUBLE_EQUAL(d, 8, 0.000001); + pc_point_get_double_by_name(&(pa->stats->avg), "Intensity", &d); + CU_ASSERT_DOUBLE_EQUAL(d, 7, 0.000001); + str = pc_patch_to_string(pa); CU_ASSERT_STRING_EQUAL(str, "{\"pcid\":0,\"pts\":[[0.02,0.03,0.05,6],[0.02,0.03,0.05,8]]}"); // printf("\n%s\n",str); diff --git a/lib/pc_api.h b/lib/pc_api.h index 39eb63a..eb800d3 100644 --- a/lib/pc_api.h +++ b/lib/pc_api.h @@ -142,8 +142,8 @@ typedef struct /* Used for generic patch statistics */ typedef struct { - PCPOINT max; PCPOINT min; + PCPOINT max; PCPOINT avg; } PCSTATS; @@ -263,6 +263,8 @@ PCSCHEMA* pc_schema_clone(const PCSCHEMA *s); void pc_schema_set_dimension(PCSCHEMA *s, PCDIMENSION *d); /** Check/set the x/y position in the dimension list */ void pc_schema_check_xy(PCSCHEMA *s); +/** Get the width in bytes of a single point in the schema */ +size_t pc_schema_get_size(const PCSCHEMA *s); /********************************************************************** @@ -376,7 +378,10 @@ int pc_bytes_serialize(const PCBYTES *pcb, uint8_t *buf, size_t *size); int pc_bytes_deserialize(const uint8_t *buf, const PCDIMENSION *dim, PCBYTES *pcb, int readonly, int flip_endian); /** Wrap serialized stats in a new stats objects */ -PCSTATS* pc_stats_new_from_data(const PCSCHEMA *schema, const uint8_t *mindata, const uint8_t *maxdata, const uint8_t *avgdata);\ +PCSTATS* pc_stats_new_from_data(const PCSCHEMA *schema, const uint8_t *mindata, const uint8_t *maxdata, const uint8_t *avgdata); + +/** Free a stats object */ +void pc_stats_free(PCSTATS *stats); /** Calculate stats from an existing patch */ int pc_patch_compute_stats(PCPATCH *patch); diff --git a/lib/pc_schema.c b/lib/pc_schema.c index daa1e4f..a720149 100644 --- a/lib/pc_schema.c +++ b/lib/pc_schema.c @@ -599,3 +599,9 @@ pc_schema_get_dimension_by_name(const PCSCHEMA *s, const char *name) return hashtable_search(s->namehash, name); } +size_t +pc_schema_get_size(const PCSCHEMA *s) +{ + return s->size; +} + diff --git a/pgsql/expected/pointcloud.out b/pgsql/expected/pointcloud.out index 95534e4..87547a1 100644 --- a/pgsql/expected/pointcloud.out +++ b/pgsql/expected/pointcloud.out @@ -2,7 +2,7 @@ CREATE EXTENSION pointcloud; SELECT PC_Version(); pc_version ------------ - 1.0 + 0.1 (1 row) INSERT INTO pointcloud_formats (pcid, srid, schema) @@ -272,7 +272,7 @@ SELECT Sum(PC_NumPoints(pa)) FROM pa_test_dim; SELECT Sum(PC_MemSize(pa)) FROM pa_test_dim; sum ----- - 516 + 684 (1 row) SELECT Sum(PC_PatchMax(pa,'x')) FROM pa_test_dim; @@ -308,7 +308,7 @@ SELECT Sum(PC_NumPoints(pa)) FROM pa_test_dim; SELECT Sum(PC_MemSize(pa)) FROM pa_test_dim; sum ------ - 8523 + 8733 (1 row) SELECT Max(PC_PatchMax(pa,'x')) FROM pa_test_dim; @@ -351,7 +351,7 @@ SELECT Sum(PC_NumPoints(pa)) FROM pa_test_ght; SELECT Sum(PC_MemSize(pa)) FROM pa_test_ght; sum ----- - 420 + 588 (1 row) SELECT Sum(PC_PatchMax(pa,'x')) FROM pa_test_ght; @@ -387,7 +387,7 @@ SELECT Sum(PC_NumPoints(pa)) FROM pa_test_ght; SELECT Sum(PC_MemSize(pa)) FROM pa_test_ght; sum ------- - 38471 + 38681 (1 row) SELECT Max(PC_PatchMax(pa,'x')) FROM pa_test_ght; diff --git a/pgsql/pc_access.c b/pgsql/pc_access.c index 6587721..f1d0c9d 100644 --- a/pgsql/pc_access.c +++ b/pgsql/pc_access.c @@ -21,6 +21,7 @@ Datum pcpatch_uncompress(PG_FUNCTION_ARGS); Datum pcpatch_numpoints(PG_FUNCTION_ARGS); Datum pcpatch_compression(PG_FUNCTION_ARGS); Datum pcpatch_intersects(PG_FUNCTION_ARGS); +Datum pcpatch_get_stat(PG_FUNCTION_ARGS); Datum pcpatch_size(PG_FUNCTION_ARGS); Datum pcpoint_size(PG_FUNCTION_ARGS); Datum pc_version(PG_FUNCTION_ARGS); @@ -582,3 +583,53 @@ Datum pc_version(PG_FUNCTION_ARGS) PG_RETURN_TEXT_P(version_text); } +/** +* Read a named dimension statistic from a PCPATCH +* PC_PatchMax(patch pcpatch, dimname text) returns Numeric +* PC_PatchMin(patch pcpatch, dimname text) returns Numeric +* PC_PatchAvg(patch pcpatch, dimname text) returns Numeric +*/ +PG_FUNCTION_INFO_V1(pcpatch_get_stat); +Datum pcpatch_get_stat(PG_FUNCTION_ARGS) +{ + static int stats_size_guess = 400; + SERIALIZED_PATCH *serpa = PG_GETHEADERX_SERPATCH_P(0, stats_size_guess); + PCSCHEMA *schema = pc_schema_from_pcid(serpa->pcid, fcinfo); + char *dim_str = text_to_cstring(PG_GETARG_TEXT_P(1)); + char *stat_str = text_to_cstring(PG_GETARG_TEXT_P(2)); + PCSTATS *stats; + float8 double_result; + int rv; + + if ( stats_size_guess < 3*schema->size ) + { + serpa = PG_GETHEADERX_SERPATCH_P(0, 3*schema->size); + } + + stats = pc_patch_stats_deserialize(schema, serpa->data); + + if ( ! stats ) + PG_RETURN_NULL(); + + /* Max */ + if ( 0 == strcasecmp("max", stat_str) ) + rv = pc_point_get_double_by_name(&(stats->max), dim_str, &double_result); + /* Min */ + else if ( 0 == strcasecmp("min", stat_str) ) + rv = pc_point_get_double_by_name(&(stats->min), dim_str, &double_result); + /* Avg */ + else if ( 0 == strcasecmp("avg", stat_str) ) + rv = pc_point_get_double_by_name(&(stats->avg), dim_str, &double_result); + /* Unsupported */ + else + elog(ERROR, "stat type \"%s\" is not supported", stat_str); + + pfree(stat_str); + pc_stats_free(stats); + + if ( ! rv ) + elog(ERROR, "dimension \"%s\" does not exist in schema", dim_str); + + pfree(dim_str); + PG_RETURN_DATUM(DirectFunctionCall1(float8_numeric, Float8GetDatum(double_result))); +} diff --git a/pgsql/pc_pgsql.c b/pgsql/pc_pgsql.c index ec1337b..161f0ee 100644 --- a/pgsql/pc_pgsql.c +++ b/pgsql/pc_pgsql.c @@ -471,24 +471,18 @@ pc_patch_serialized_size(const PCPATCH *patch) return -1; } -static uint8_t * +static size_t pc_patch_stats_serialize(uint8_t *buf, const PCSCHEMA *schema, const PCSTATS *stats) { size_t sz = schema->size; - /* Copy min */ memcpy(buf, stats->min.data, sz); - buf += sz; - /* Copy max */ - memcpy(buf, stats->max.data, sz); - buf += sz; - + memcpy(buf + sz, stats->max.data, sz); /* Copy avg */ - memcpy(buf, stats->avg.data, sz); - buf += sz; - - return buf; + memcpy(buf + 2*sz, stats->avg.data, sz); + + return sz*3; } /** @@ -496,7 +490,7 @@ pc_patch_stats_serialize(uint8_t *buf, const PCSCHEMA *schema, const PCSTATS *st * min, max, avg. Their size is the uncompressed buffer size for * a point, the schema->size. */ -static PCSTATS * +PCSTATS * pc_patch_stats_deserialize(const PCSCHEMA *schema, const uint8_t *buf) { size_t sz = schema->size; @@ -540,7 +534,7 @@ pc_patch_dimensional_serialize(const PCPATCH *patch_in) /* Write stats into the buffer */ if ( patch->stats ) { - buf = pc_patch_stats_serialize(buf, patch->schema, patch->stats); + buf += pc_patch_stats_serialize(buf, patch->schema, patch->stats); } else { @@ -592,7 +586,7 @@ pc_patch_ght_serialize(const PCPATCH *patch_in) /* Write stats into the buffer first */ if ( patch->stats ) { - buf = pc_patch_stats_serialize(buf, patch->schema, patch->stats); + buf += pc_patch_stats_serialize(buf, patch->schema, patch->stats); } else { @@ -638,7 +632,7 @@ pc_patch_uncompressed_serialize(const PCPATCH *patch_in) buf = serpch->data; if ( patch->stats ) { - buf = pc_patch_stats_serialize(buf, patch->schema, patch->stats); + buf += pc_patch_stats_serialize(buf, patch->schema, patch->stats); } else { @@ -753,18 +747,9 @@ pc_patch_uncompressed_deserialize(const SERIALIZED_PATCH *serpatch, const PCSCHE // } // SERIALIZED_PATCH; - PCPATCH_UNCOMPRESSED *patch; - uint8_t *buf = (uint8_t*)serpatch->data; + uint8_t *buf; size_t stats_size = 3*schema->size; // 3 pcpoints worth of stats - - /* Reference the external data */ - patch = pcalloc(sizeof(PCPATCH_UNCOMPRESSED)); - /* Advance data pointer past the stats serialization */ - patch->data = buf + stats_size; - /* Calculate the point data buffer size */ - patch->datasize = VARSIZE(serpatch) - sizeof(SERIALIZED_PATCH) + 1 - stats_size; - /* Point into the stats area */ - patch->stats = pc_patch_stats_deserialize(schema, serpatch->data); + PCPATCH_UNCOMPRESSED *patch = pcalloc(sizeof(PCPATCH_UNCOMPRESSED)); /* Set up basic info */ patch->type = serpatch->compression; @@ -774,6 +759,17 @@ pc_patch_uncompressed_deserialize(const SERIALIZED_PATCH *serpatch, const PCSCHE patch->maxpoints = 0; patch->bounds = serpatch->bounds; + buf = (uint8_t*)serpatch->data; + + /* Point into the stats area */ + patch->stats = pc_patch_stats_deserialize(schema, buf); + + /* Advance data pointer past the stats serialization */ + patch->data = buf + stats_size; + + /* Calculate the point data buffer size */ + patch->datasize = VARSIZE(serpatch) - sizeof(SERIALIZED_PATCH) + 1 - stats_size; + return (PCPATCH*)patch; } diff --git a/pgsql/pc_pgsql.h b/pgsql/pc_pgsql.h index fed64d6..2011367 100644 --- a/pgsql/pc_pgsql.h +++ b/pgsql/pc_pgsql.h @@ -29,6 +29,10 @@ #define PG_GETHEADER_SERPATCH_P(argnum) (SERIALIZED_PATCH*)PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(argnum), 0, sizeof(SERIALIZED_PATCH)) +#define PG_GETHEADERX_SERPATCH_P(argnum, extra) (SERIALIZED_PATCH*)PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(argnum), 0, sizeof(SERIALIZED_PATCH)+extra) + +#define PG_GETHEADER_STATS_P(argnum, statsize) (uint8_t*)(((SERIALIZED_PATCH*)PG_DETOAST_DATUM_SLICE(PG_GETARG_DATUM(argnum), 0, sizeof(SERIALIZED_PATCH) + statsize))->data) + #define AUTOCOMPRESS_NO 0 #define AUTOCOMPRESS_YES 1 @@ -113,4 +117,6 @@ char* pc_patch_to_hexwkb(const PCPATCH *patch); uint8_t* pc_patch_to_geometry_wkb_envelope(const SERIALIZED_PATCH *pa, const PCSCHEMA *schema, size_t *wkbsize); /** Read the first few bytes off an object to get the datum */ -uint32 pcid_from_datum(Datum d); \ No newline at end of file +uint32 pcid_from_datum(Datum d); + +PCSTATS* pc_patch_stats_deserialize(const PCSCHEMA *schema, const uint8_t *buf); diff --git a/pgsql/pointcloud--1.0.sql b/pgsql/pointcloud--1.0.sql index 9f92d7a..1bb7216 100644 --- a/pgsql/pointcloud--1.0.sql +++ b/pgsql/pointcloud--1.0.sql @@ -284,22 +284,16 @@ CREATE OR REPLACE FUNCTION PC_Explode(p pcpatch) -- SQL Utility Functions ------------------------------------------------------------------- --- Utility to get AVERAGE value from patch -CREATE OR REPLACE FUNCTION PC_PatchAvg(p pcpatch, attr text) - RETURNS numeric AS - 'WITH pts AS ( SELECT PC_Explode($1) AS pt) SELECT avg(PC_Get(pt, $2)) FROM pts' - LANGUAGE 'sql'; --- Utility to get MAXIMUM value from patch -CREATE OR REPLACE FUNCTION PC_PatchMax(p pcpatch, attr text) - RETURNS numeric AS - 'WITH pts AS ( SELECT PC_Explode($1) AS pt) SELECT max(PC_Get(pt, $2)) FROM pts' - LANGUAGE 'sql'; +CREATE OR REPLACE FUNCTION PC_PatchMax(p pcpatch, attr text, stat text default 'max') + RETURNS numeric AS 'MODULE_PATHNAME', 'pcpatch_get_stat' + LANGUAGE 'c' IMMUTABLE STRICT; --- Utility to get MINIMUM value from patch -CREATE OR REPLACE FUNCTION PC_PatchMin(p pcpatch, attr text) - RETURNS numeric AS - 'WITH pts AS ( SELECT PC_Explode($1) AS pt) SELECT min(PC_Get(pt, $2)) FROM pts' - LANGUAGE 'sql'; +CREATE OR REPLACE FUNCTION PC_PatchMin(p pcpatch, attr text, stat text default 'min') + RETURNS numeric AS 'MODULE_PATHNAME', 'pcpatch_get_stat' + LANGUAGE 'c' IMMUTABLE STRICT; +CREATE OR REPLACE FUNCTION PC_PatchAvg(p pcpatch, attr text, stat text default 'avg') + RETURNS numeric AS 'MODULE_PATHNAME', 'pcpatch_get_stat' + LANGUAGE 'c' IMMUTABLE STRICT;