mirror of
https://github.com/pgpointcloud/pointcloud.git
synced 2025-12-08 20:36:04 +00:00
Patch support, more unit tests, hexwkb,
wkb, string output.
This commit is contained in:
parent
ef1e0435f9
commit
fe5bf854b6
48
README.md
48
README.md
@ -34,19 +34,39 @@ Human-Readable Text
|
||||
( <dim1>, <dim2>, <dim3>, <dim4> ),
|
||||
( <dim1>, <dim2>, <dim3>, <dim4> ) ]
|
||||
|
||||
Uncompressed Binary
|
||||
-------------------
|
||||
Point Binary
|
||||
------------
|
||||
|
||||
PCPOINT
|
||||
byte: endianness (1 = NDR, 0 = XDR)
|
||||
uint32: pcid (key to POINTCLOUD_SCHEMAS)
|
||||
uchar[]: pointdata (interpret relative to pcid)
|
||||
|
||||
Patch Binary
|
||||
------------
|
||||
|
||||
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)
|
||||
|
||||
Patch Binary (Uncompressed)
|
||||
---------------------------
|
||||
|
||||
byte: endianness (1 = NDR, 0 = XDR)
|
||||
uint32: pcid (key to POINTCLOUD_SCHEMAS)
|
||||
uint32: 0 = no compression
|
||||
uint32: npoints
|
||||
pointdata[]: interpret relative to pcid
|
||||
|
||||
Patch Binary (Dimensional)
|
||||
--------------------------
|
||||
|
||||
byte: endianness (1 = NDR, 0 = XDR)
|
||||
uint32: pcid (key to POINTCLOUD_SCHEMAS)
|
||||
uint32: 1 = dimensional compression
|
||||
dimensional[]: dimensionally compressed data for each dimension
|
||||
|
||||
/* dimensional compression */
|
||||
byte: dimensional compression type (0 = none, 1 = significant bits, 2 = deflate, 3 = run-length)
|
||||
|
||||
byte: endianness (1 = NDR, 0 = XDR)
|
||||
uint32: pcid (key to POINTCLOUD_SCHEMAS)
|
||||
uchar[]: data (interpret relative to pcid)
|
||||
|
||||
|
||||
PCPATCH
|
||||
|
||||
byte: endianness (1 = NDR, 0 = XDR)
|
||||
uint32: pcid (key to POINTCLOUD_SCHEMAS)
|
||||
uint32: npoints
|
||||
uchar[]: data (interpret relative to pcid)
|
||||
/* TO DO, more on dimensional compression contents */
|
||||
|
||||
@ -8,6 +8,7 @@ OBJS = \
|
||||
pc_mem.o \
|
||||
pc_patch.o \
|
||||
pc_point.o \
|
||||
pc_pointlist.o \
|
||||
pc_schema.o \
|
||||
pc_util.o \
|
||||
pc_val.o \
|
||||
|
||||
@ -12,6 +12,7 @@ EXE = cu_tester
|
||||
OBJS = \
|
||||
cu_tester.o \
|
||||
cu_pc_schema.o \
|
||||
cu_pc_point.o \
|
||||
cu_pc_patch.o
|
||||
|
||||
# Build the unit tester
|
||||
|
||||
@ -13,7 +13,9 @@
|
||||
/* GLOBALS ************************************************************/
|
||||
|
||||
static PCSCHEMA *schema = NULL;
|
||||
const char *xmlfile = "data/pdal-schema.xml";
|
||||
static PCSCHEMA *simpleschema = NULL;
|
||||
static const char *xmlfile = "data/pdal-schema.xml";
|
||||
static const char *simplexmlfile = "data/simple-schema.xml";
|
||||
|
||||
/* Setup/teardown for this suite */
|
||||
static int
|
||||
@ -23,6 +25,12 @@ init_suite(void)
|
||||
int rv = pc_schema_from_xml(xmlstr, &schema);
|
||||
pcfree(xmlstr);
|
||||
if ( rv == PC_FAILURE ) return 1;
|
||||
|
||||
xmlstr = file_to_str(simplexmlfile);
|
||||
rv = pc_schema_from_xml(xmlstr, &simpleschema);
|
||||
pcfree(xmlstr);
|
||||
if ( rv == PC_FAILURE ) return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -30,6 +38,7 @@ static int
|
||||
clean_suite(void)
|
||||
{
|
||||
pc_schema_free(schema);
|
||||
pc_schema_free(simpleschema);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -63,9 +72,9 @@ test_endian_flip()
|
||||
CU_ASSERT_DOUBLE_EQUAL(a3, b3, 0.0000001);
|
||||
CU_ASSERT_DOUBLE_EQUAL(a4, b4, 0.0000001);
|
||||
|
||||
ptr = bytes_flip_endian(pt->data, schema, 1);
|
||||
ptr = uncompressed_bytes_flip_endian(pt->data, schema, 1);
|
||||
pcfree(pt->data);
|
||||
pt->data = bytes_flip_endian(ptr, schema, 1);
|
||||
pt->data = uncompressed_bytes_flip_endian(ptr, schema, 1);
|
||||
|
||||
b1 = pc_point_get_double_by_name(pt, "X");
|
||||
b2 = pc_point_get_double_by_name(pt, "Z");
|
||||
@ -77,7 +86,36 @@ test_endian_flip()
|
||||
CU_ASSERT_DOUBLE_EQUAL(a4, b4, 0.0000001);
|
||||
}
|
||||
|
||||
static void
|
||||
test_patch_hex_inout()
|
||||
{
|
||||
// 00 endian
|
||||
// 00000000 pcid
|
||||
// 00000000 compression
|
||||
// 00000002 npoints
|
||||
// 0000000200000003000000050006 pt1 (XYZi)
|
||||
// 0000000200000003000000050008 pt2 (XYZi)
|
||||
char *hexbuf = "0000000000000000000000000200000002000000030000000500060000000200000003000000050008";
|
||||
|
||||
double d;
|
||||
char *str;
|
||||
size_t hexsize = strlen(hexbuf);
|
||||
uint8_t *wkb = bytes_from_hexbytes(hexbuf, hexsize);
|
||||
PCPATCH *pa = pc_patch_from_wkb(simpleschema, wkb, hexsize/2);
|
||||
PCPOINTLIST *pl = pc_patch_to_points(pa);
|
||||
d = pc_point_get_double_by_name(pl->points[0], "X");
|
||||
CU_ASSERT_DOUBLE_EQUAL(d, 0.02, 0.000001);
|
||||
d = pc_point_get_double_by_name(pl->points[1], "Intensity");
|
||||
CU_ASSERT_DOUBLE_EQUAL(d, 8, 0.000001);
|
||||
|
||||
str = pc_patch_to_string(pa);
|
||||
CU_ASSERT_STRING_EQUAL(str, "[ 0 : (0.02, 0.03, 0.05, 6), (0.02, 0.03, 0.05, 8) ]");
|
||||
// printf("\n%s\n",str);
|
||||
|
||||
pc_pointlist_free(pl);
|
||||
pc_patch_free(pa);
|
||||
pcfree(wkb);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -85,6 +123,7 @@ test_endian_flip()
|
||||
|
||||
CU_TestInfo patch_tests[] = {
|
||||
PG_TEST(test_endian_flip),
|
||||
PG_TEST(test_patch_hex_inout),
|
||||
CU_TEST_INFO_NULL
|
||||
};
|
||||
|
||||
|
||||
93
libpc/cunit/cu_pc_point.c
Normal file
93
libpc/cunit/cu_pc_point.c
Normal file
@ -0,0 +1,93 @@
|
||||
/***********************************************************************
|
||||
* cu_pc_schema.c
|
||||
*
|
||||
* Testing for the schema API functions
|
||||
*
|
||||
* Portions Copyright (c) 2012, OpenGeo
|
||||
*
|
||||
***********************************************************************/
|
||||
|
||||
#include "CUnit/Basic.h"
|
||||
#include "cu_tester.h"
|
||||
|
||||
/* GLOBALS ************************************************************/
|
||||
|
||||
static PCSCHEMA *schema = NULL;
|
||||
static const char *xmlfile = "data/simple-schema.xml";
|
||||
|
||||
// SIMPLE SCHEMA
|
||||
// int32_t x
|
||||
// int32_t y
|
||||
// int32_t z
|
||||
// int16_t intensity
|
||||
|
||||
/* Setup/teardown for this suite */
|
||||
static int
|
||||
init_suite(void)
|
||||
{
|
||||
char *xmlstr = file_to_str(xmlfile);
|
||||
int rv = pc_schema_from_xml(xmlstr, &schema);
|
||||
pcfree(xmlstr);
|
||||
if ( rv == PC_FAILURE ) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
clean_suite(void)
|
||||
{
|
||||
pc_schema_free(schema);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* TESTS **************************************************************/
|
||||
|
||||
static void
|
||||
test_point_hex_inout()
|
||||
{
|
||||
// byte: endianness (1 = NDR, 0 = XDR)
|
||||
// uint32: pcid (key to POINTCLOUD_SCHEMAS)
|
||||
// uchar[]: pointdata (interpret relative to pcid)
|
||||
|
||||
double d;
|
||||
char *hexbuf = "00000000010000000100000002000000030004";
|
||||
size_t hexsize = strlen(hexbuf);
|
||||
uint8_t *wkb = bytes_from_hexbytes(hexbuf, hexsize);
|
||||
PCPOINT *pt = pc_point_from_wkb(schema, wkb, hexsize/2);
|
||||
d = pc_point_get_double_by_name(pt, "X");
|
||||
CU_ASSERT_DOUBLE_EQUAL(d, 0.01, 0.000001);
|
||||
d = pc_point_get_double_by_name(pt, "Y");
|
||||
CU_ASSERT_DOUBLE_EQUAL(d, 0.02, 0.000001);
|
||||
d = pc_point_get_double_by_name(pt, "Z");
|
||||
CU_ASSERT_DOUBLE_EQUAL(d, 0.03, 0.000001);
|
||||
d = pc_point_get_double_by_name(pt, "Intensity");
|
||||
CU_ASSERT_DOUBLE_EQUAL(d, 4, 0.0001);
|
||||
pc_point_free(pt);
|
||||
pcfree(wkb);
|
||||
|
||||
hexbuf = "01010000000100000002000000030000000500";
|
||||
hexsize = strlen(hexbuf);
|
||||
wkb = bytes_from_hexbytes(hexbuf, hexsize);
|
||||
pt = pc_point_from_wkb(schema, wkb, hexsize/2);
|
||||
d = pc_point_get_double_by_name(pt, "X");
|
||||
CU_ASSERT_DOUBLE_EQUAL(d, 0.01, 0.000001);
|
||||
d = pc_point_get_double_by_name(pt, "Y");
|
||||
CU_ASSERT_DOUBLE_EQUAL(d, 0.02, 0.000001);
|
||||
d = pc_point_get_double_by_name(pt, "Z");
|
||||
CU_ASSERT_DOUBLE_EQUAL(d, 0.03, 0.000001);
|
||||
d = pc_point_get_double_by_name(pt, "Intensity");
|
||||
CU_ASSERT_DOUBLE_EQUAL(d, 5, 0.0001);
|
||||
pc_point_free(pt);
|
||||
pcfree(wkb);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* REGISTER ***********************************************************/
|
||||
|
||||
CU_TestInfo point_tests[] = {
|
||||
PG_TEST(test_point_hex_inout),
|
||||
CU_TEST_INFO_NULL
|
||||
};
|
||||
|
||||
CU_SuiteInfo point_suite = {"point", init_suite, clean_suite, point_tests};
|
||||
@ -15,6 +15,7 @@
|
||||
/* ADD YOUR SUITE HERE (1 of 2) */
|
||||
extern CU_SuiteInfo schema_suite;
|
||||
extern CU_SuiteInfo patch_suite;
|
||||
extern CU_SuiteInfo point_suite;
|
||||
|
||||
/*
|
||||
** The main() function for setting up and running the tests.
|
||||
@ -28,6 +29,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
schema_suite,
|
||||
patch_suite,
|
||||
point_suite,
|
||||
CU_SUITE_INFO_NULL
|
||||
};
|
||||
|
||||
|
||||
@ -8,8 +8,6 @@
|
||||
<pc:interpretation>int32_t</pc:interpretation>
|
||||
<pc:scale>0.01</pc:scale>
|
||||
<pc:active>true</pc:active>
|
||||
<pc:uuid>2ee118d1-119e-4906-99c3-42934203f872</pc:uuid>
|
||||
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
|
||||
</pc:dimension>
|
||||
<pc:dimension>
|
||||
<pc:position>2</pc:position>
|
||||
@ -19,8 +17,6 @@
|
||||
<pc:interpretation>int32_t</pc:interpretation>
|
||||
<pc:scale>0.01</pc:scale>
|
||||
<pc:active>true</pc:active>
|
||||
<pc:uuid>87707eee-2f30-4979-9987-8ef747e30275</pc:uuid>
|
||||
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
|
||||
</pc:dimension>
|
||||
<pc:dimension>
|
||||
<pc:position>3</pc:position>
|
||||
@ -30,8 +26,6 @@
|
||||
<pc:interpretation>int32_t</pc:interpretation>
|
||||
<pc:scale>0.01</pc:scale>
|
||||
<pc:active>true</pc:active>
|
||||
<pc:uuid>e74b5e41-95e6-4cf2-86ad-e3f5a996da5d</pc:uuid>
|
||||
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
|
||||
</pc:dimension>
|
||||
<pc:dimension>
|
||||
<pc:position>4</pc:position>
|
||||
@ -41,8 +35,6 @@
|
||||
<pc:interpretation>uint16_t</pc:interpretation>
|
||||
<pc:scale>1</pc:scale>
|
||||
<pc:active>true</pc:active>
|
||||
<pc:uuid>61e90c9a-42fc-46c7-acd3-20d67bd5626f</pc:uuid>
|
||||
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
|
||||
</pc:dimension>
|
||||
<pc:dimension>
|
||||
<pc:position>5</pc:position>
|
||||
@ -52,8 +44,6 @@
|
||||
<pc:interpretation>uint8_t</pc:interpretation>
|
||||
<pc:scale>1</pc:scale>
|
||||
<pc:active>true</pc:active>
|
||||
<pc:uuid>ffe5e5f8-4cec-4560-abf0-448008f7b89e</pc:uuid>
|
||||
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
|
||||
</pc:dimension>
|
||||
<pc:dimension>
|
||||
<pc:position>6</pc:position>
|
||||
@ -63,8 +53,6 @@
|
||||
<pc:interpretation>uint8_t</pc:interpretation>
|
||||
<pc:scale>1</pc:scale>
|
||||
<pc:active>true</pc:active>
|
||||
<pc:uuid>7c28bfd4-a9ed-4fb2-b07f-931c076fbaf0</pc:uuid>
|
||||
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
|
||||
</pc:dimension>
|
||||
<pc:dimension>
|
||||
<pc:position>7</pc:position>
|
||||
@ -74,8 +62,6 @@
|
||||
<pc:interpretation>uint8_t</pc:interpretation>
|
||||
<pc:scale>1</pc:scale>
|
||||
<pc:active>true</pc:active>
|
||||
<pc:uuid>13019a2c-cf88-480d-a995-0162055fe5f9</pc:uuid>
|
||||
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
|
||||
</pc:dimension>
|
||||
<pc:dimension>
|
||||
<pc:position>8</pc:position>
|
||||
@ -85,8 +71,6 @@
|
||||
<pc:interpretation>uint8_t</pc:interpretation>
|
||||
<pc:scale>1</pc:scale>
|
||||
<pc:active>true</pc:active>
|
||||
<pc:uuid>108c18f2-5cc0-4669-ae9a-f41eb4006ea5</pc:uuid>
|
||||
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
|
||||
</pc:dimension>
|
||||
<pc:dimension>
|
||||
<pc:position>9</pc:position>
|
||||
@ -96,8 +80,6 @@
|
||||
<pc:interpretation>uint8_t</pc:interpretation>
|
||||
<pc:scale>1</pc:scale>
|
||||
<pc:active>true</pc:active>
|
||||
<pc:uuid>b4c67de9-cef1-432c-8909-7c751b2a4e0b</pc:uuid>
|
||||
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
|
||||
</pc:dimension>
|
||||
<pc:dimension>
|
||||
<pc:position>10</pc:position>
|
||||
@ -107,8 +89,6 @@
|
||||
<pc:interpretation>int8_t</pc:interpretation>
|
||||
<pc:scale>1</pc:scale>
|
||||
<pc:active>true</pc:active>
|
||||
<pc:uuid>aaadaf77-e0c9-4df0-81a7-27060794cd69</pc:uuid>
|
||||
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
|
||||
</pc:dimension>
|
||||
<pc:dimension>
|
||||
<pc:position>11</pc:position>
|
||||
@ -118,8 +98,6 @@
|
||||
<pc:interpretation>uint8_t</pc:interpretation>
|
||||
<pc:scale>1</pc:scale>
|
||||
<pc:active>true</pc:active>
|
||||
<pc:uuid>70eb558e-63d4-4804-b1db-fc2fd716927c</pc:uuid>
|
||||
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
|
||||
</pc:dimension>
|
||||
<pc:dimension>
|
||||
<pc:position>12</pc:position>
|
||||
@ -129,8 +107,6 @@
|
||||
<pc:interpretation>uint16_t</pc:interpretation>
|
||||
<pc:scale>1</pc:scale>
|
||||
<pc:active>true</pc:active>
|
||||
<pc:uuid>4e42e96a-6af0-4fdd-81cb-6216ff47bf6b</pc:uuid>
|
||||
<pc:parent_uuid>00000000-0000-0000-0000-000000000000</pc:parent_uuid>
|
||||
</pc:dimension>
|
||||
<pc:dimension>
|
||||
<pc:position>13</pc:position>
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
<pc:scale>0.01</pc:scale>
|
||||
</pc:dimension>
|
||||
<pc:dimension>
|
||||
<pc:position>2</pc:position>
|
||||
<pc:position>3</pc:position>
|
||||
<pc:size>4</pc:size>
|
||||
<pc:description>Z coordinate as a long integer. You must use the scale and offset information of the header to determine the double value.</pc:description>
|
||||
<pc:name>Z</pc:name>
|
||||
|
||||
@ -30,7 +30,8 @@
|
||||
enum COMPRESSIONS
|
||||
{
|
||||
PC_NONE = 0,
|
||||
PC_GHT = 1
|
||||
PC_GHT = 1,
|
||||
PC_DIMENSIONAL = 2
|
||||
};
|
||||
|
||||
/**
|
||||
@ -87,9 +88,17 @@ typedef struct
|
||||
{
|
||||
int8_t readonly;
|
||||
const PCSCHEMA *schema;
|
||||
uint8_t *data;
|
||||
uint8_t *data; /* A serialized version of the data */
|
||||
} PCPOINT;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int8_t readonly;
|
||||
uint32_t npoints;
|
||||
uint32_t maxpoints;
|
||||
PCPOINT **points;
|
||||
} PCPOINTLIST;
|
||||
|
||||
/**
|
||||
* Uncompressed Structure for in-memory handling
|
||||
* of patches. A read-only PgSQL patch can be wrapped in
|
||||
@ -100,11 +109,13 @@ typedef struct
|
||||
typedef struct
|
||||
{
|
||||
int8_t readonly;
|
||||
size_t npoints; /* How many points we have */
|
||||
size_t maxpoints; /* How man points we can hold (or 0 for read-only) */
|
||||
uint32_t npoints; /* How many points we have */
|
||||
uint32_t maxpoints; /* How man points we can hold (or 0 for read-only) */
|
||||
const PCSCHEMA *schema;
|
||||
double xmin, xmax, ymin, ymax;
|
||||
uint8_t *data;
|
||||
uint8_t compressed; /* Has compression been applied to the data buffer? */
|
||||
size_t datasize;
|
||||
uint8_t *data; /* A serialized version of the data */
|
||||
} PCPATCH;
|
||||
|
||||
|
||||
@ -146,16 +157,16 @@ void pc_install_default_handlers(void);
|
||||
* UTILITY
|
||||
*/
|
||||
|
||||
/** Read a hex string into binary buffer */
|
||||
/** Read the the PCID from WKB form of a POINT/PATCH */
|
||||
uint32_t wkb_get_pcid(uint8_t *wkb);
|
||||
/** Read the the npoints from WKB form of a PATCH */
|
||||
uint32_t wkb_get_compression(uint8_t *wkb);
|
||||
/** Flips the bytes of an int32_t */
|
||||
int32_t int32_flip_endian(int32_t val);
|
||||
/** Convert binary to hex */
|
||||
uint8_t* bytes_from_hexbytes(const char *hexbuf, size_t hexsize);
|
||||
/** Read the the PCID from WKB form of a POINT */
|
||||
uint32_t wkb_get_pcid(uint8_t *bytes);
|
||||
/** Get a pointer to the data buffer of the WKB form of a point */
|
||||
uint8_t* wkb_point_get_data_ptr(uint8_t *bytes);
|
||||
|
||||
/** Force a byte array into the machine endianness */
|
||||
uint8_t* bytes_flip_endian(const uint8_t *bytebuf, const PCSCHEMA *schema, uint32_t npoints);
|
||||
|
||||
/** Convert hex to binary */
|
||||
char* hexbytes_from_bytes(const uint8_t *bytebuf, size_t bytesize);
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
@ -176,6 +187,19 @@ PCDIMENSION* pc_schema_get_dimension_by_name(const PCSCHEMA *s, const char *name
|
||||
uint32_t pc_schema_is_valid(const PCSCHEMA *s);
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* PCPOINTLIST
|
||||
*/
|
||||
|
||||
/** Allocate a pointlist */
|
||||
PCPOINTLIST* pc_pointlist_make(uint32_t npoints);
|
||||
|
||||
/** Free a pointlist, including the points contained therein */
|
||||
void pc_pointlist_free(PCPOINTLIST *pl);
|
||||
|
||||
/** Add a point to the list, expanding buffer as necessary */
|
||||
void pc_pointlist_add_point(PCPOINTLIST *pl, PCPOINT *pt);
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* PCPOINT
|
||||
@ -183,27 +207,42 @@ uint32_t pc_schema_is_valid(const PCSCHEMA *s);
|
||||
|
||||
/** Create a new PCPOINT */
|
||||
PCPOINT* pc_point_make(const PCSCHEMA *s);
|
||||
|
||||
/** Create a new readonly PCPOINT on top of a data buffer */
|
||||
PCPOINT* pc_point_from_data(const PCSCHEMA *s, const uint8_t *data);
|
||||
/** Create a new read/write PCPOINT on top of a data buffer */
|
||||
PCPOINT* pc_point_from_data_rw(const PCSCHEMA *s, uint8_t *data);
|
||||
|
||||
/** Create a new read/write PCPOINT from a double array */
|
||||
PCPOINT* pc_point_from_double_array(const PCSCHEMA *s, double *array, uint32_t nelems);
|
||||
/** Frees the PTPOINT and data (if not readonly) does not free referenced schema */
|
||||
|
||||
/** Frees the PTPOINT and data (if not readonly). Does not free referenced schema */
|
||||
void pc_point_free(PCPOINT *pt);
|
||||
|
||||
/** Casts named dimension value to double and scale/offset appropriately before returning */
|
||||
double pc_point_get_double_by_name(const PCPOINT *pt, const char *name);
|
||||
|
||||
/** Casts dimension value to double and scale/offset appropriately before returning */
|
||||
double pc_point_get_double_by_index(const PCPOINT *pt, uint32_t idx);
|
||||
|
||||
/** Scales/offsets double, casts to appropriate dimension type, and writes into point */
|
||||
int pc_point_set_double_by_index(PCPOINT *pt, uint32_t idx, double val);
|
||||
|
||||
/** Scales/offsets double, casts to appropriate dimension type, and writes into point */
|
||||
int pc_point_set_double_by_name(PCPOINT *pt, const char *name, double val);
|
||||
|
||||
/** Returns X coordinate */
|
||||
double pc_point_get_x(const PCPOINT *pt);
|
||||
|
||||
/** Returns Y coordinate */
|
||||
double pc_point_get_y(const PCPOINT *pt);
|
||||
|
||||
/** Create a new readwrite PCPOINT from a hex byte array */
|
||||
PCPOINT* pc_point_from_wkb(const PCSCHEMA *s, uint8_t *wkb, size_t wkbsize);
|
||||
|
||||
/** Returns serialized form of point */
|
||||
uint8_t* pc_point_to_wkb(const PCPOINT *pt, size_t *wkbsize);
|
||||
|
||||
/** Returns text form of point */
|
||||
char* pc_point_to_string(const PCPOINT *pt);
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
@ -212,11 +251,34 @@ double pc_point_get_y(const PCPOINT *pt);
|
||||
|
||||
/** Create new empty PCPATCH */
|
||||
PCPATCH* pc_patch_make(const PCSCHEMA *s);
|
||||
|
||||
/** Create new PCPATCH from a PCPOINT set. Copies data, doesn't take ownership of points */
|
||||
PCPATCH* pc_patch_make_from_points(const PCPOINT **pts, uint32_t numpts);
|
||||
PCPATCH* pc_patch_from_points(const PCPOINT **pts, uint32_t numpts);
|
||||
|
||||
/** Returns a list of points extracted from patch */
|
||||
PCPOINTLIST* pc_patch_to_points(const PCPATCH *patch);
|
||||
|
||||
/** Free patch memory, respecting read-only status. Does not free referenced schema */
|
||||
void pc_patch_free(PCPATCH *patch);
|
||||
|
||||
/** Add a point to read/write PCPATCH */
|
||||
int pc_patch_add_point(PCPATCH *c, const PCPOINT *p);
|
||||
|
||||
/** Create a compressed copy, using the compression schema referenced in the PCSCHEMA */
|
||||
PCPATCH* pc_patch_compress(const PCPATCH *patch);
|
||||
|
||||
/** Create a full copy of the patch, including data, but not including PCSCHEMA */
|
||||
PCPATCH* pc_patch_clone(const PCPATCH *patch);
|
||||
|
||||
/** Create a new readwrite PCPOINT from a byte array */
|
||||
PCPATCH* pc_patch_from_wkb(const PCSCHEMA *s, uint8_t *wkb, size_t wkbsize);
|
||||
|
||||
/** Returns serialized form of point */
|
||||
uint8_t* pc_patch_to_wkb(const PCPATCH *patch, size_t *wkbsize);
|
||||
|
||||
/** Returns text form of patch */
|
||||
char* pc_patch_to_string(const PCPATCH *patch);
|
||||
|
||||
|
||||
|
||||
#endif /* _PC_API_H */
|
||||
@ -63,6 +63,12 @@ static size_t INTERPRETATION_SIZES[NUM_INTERPRETATIONS] =
|
||||
8, 4 /* PC_DOUBLE, PC_FLOAT */
|
||||
};
|
||||
|
||||
/** What is the endianness of this system? */
|
||||
char machine_endian(void);
|
||||
|
||||
/** Force a byte array into the machine endianness */
|
||||
uint8_t* uncompressed_bytes_flip_endian(const uint8_t *bytebuf, const PCSCHEMA *schema, uint32_t npoints);
|
||||
|
||||
/** Read interpretation type from buffer and cast to double */
|
||||
double pc_double_from_ptr(const uint8_t *ptr, uint32_t interpretation);
|
||||
|
||||
|
||||
348
libpc/pc_patch.c
348
libpc/pc_patch.c
@ -10,12 +10,14 @@
|
||||
|
||||
#include <math.h>
|
||||
#include "pc_api_internal.h"
|
||||
#include "stringbuffer.h"
|
||||
|
||||
PCPATCH *
|
||||
pc_patch_make(const PCSCHEMA *s)
|
||||
{
|
||||
PCPATCH *pch;
|
||||
uint32_t maxpoints = PCPATCH_DEFAULT_MAXPOINTS;
|
||||
size_t datasize;
|
||||
|
||||
if ( ! s )
|
||||
{
|
||||
@ -32,7 +34,10 @@ pc_patch_make(const PCSCHEMA *s)
|
||||
|
||||
/* Make our own data area */
|
||||
pch = pcalloc(sizeof(PCPATCH));
|
||||
pch->data = pcalloc(s->size * maxpoints);
|
||||
pch->compressed = PC_FALSE;
|
||||
datasize = s->size * maxpoints;
|
||||
pch->data = pcalloc(datasize);
|
||||
pch->datasize = datasize;
|
||||
|
||||
/* Initialize bounds */
|
||||
pch->xmin = pch->ymin = MAXFLOAT;
|
||||
@ -46,12 +51,58 @@ pc_patch_make(const PCSCHEMA *s)
|
||||
return pch;
|
||||
}
|
||||
|
||||
static int
|
||||
pc_patch_compute_extent_uncompressed(PCPATCH *patch)
|
||||
{
|
||||
int i;
|
||||
PCPOINT *pt = pc_point_from_data(patch->schema, patch->data);
|
||||
|
||||
/* Initialize bounds */
|
||||
patch->xmin = patch->ymin = MAXFLOAT;
|
||||
patch->xmax = patch->ymax = -1 * MAXFLOAT;
|
||||
|
||||
/* Calculate bounds */
|
||||
for ( i = 0; i < patch->npoints; i++ )
|
||||
{
|
||||
double x, y;
|
||||
/* Just push the data buffer forward by one point at a time */
|
||||
pt->data = patch->data + i * patch->schema->size;
|
||||
x = pc_point_get_x(pt);
|
||||
y = pc_point_get_y(pt);
|
||||
if ( patch->xmin > x ) patch->xmin = x;
|
||||
if ( patch->ymin > y ) patch->ymin = y;
|
||||
if ( patch->xmax < x ) patch->xmax = x;
|
||||
if ( patch->ymax < y ) patch->ymax = y;
|
||||
}
|
||||
|
||||
return PC_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
pc_patch_compute_extent(PCPATCH *patch)
|
||||
{
|
||||
switch( patch->schema->compression )
|
||||
{
|
||||
case PC_NONE:
|
||||
return pc_patch_compute_extent_uncompressed(patch);
|
||||
case PC_GHT:
|
||||
return PC_FAILURE;
|
||||
case PC_DIMENSIONAL:
|
||||
return PC_FAILURE;
|
||||
}
|
||||
return PC_FAILURE;
|
||||
}
|
||||
|
||||
void
|
||||
pc_patch_free(PCPATCH *patch)
|
||||
{
|
||||
if ( ! patch->readonly )
|
||||
{
|
||||
pcfree(patch->data);
|
||||
}
|
||||
pcfree(patch);
|
||||
}
|
||||
|
||||
int
|
||||
pc_patch_add_point(PCPATCH *c, const PCPOINT *p)
|
||||
{
|
||||
@ -76,6 +127,12 @@ pc_patch_add_point(PCPATCH *c, const PCPOINT *p)
|
||||
pcerror("pc_patch_add_point: cannot add point to readonly patch");
|
||||
return PC_FAILURE;
|
||||
}
|
||||
|
||||
if ( c->compressed && c->schema->compression != PC_NONE )
|
||||
{
|
||||
pcerror("pc_patch_add_point: cannot add point to compressed patch");
|
||||
return PC_FAILURE;
|
||||
}
|
||||
|
||||
sz = c->schema->size;
|
||||
|
||||
@ -83,7 +140,8 @@ pc_patch_add_point(PCPATCH *c, const PCPOINT *p)
|
||||
if ( c->npoints == c->maxpoints )
|
||||
{
|
||||
c->maxpoints *= 2;
|
||||
c->data = pcrealloc(c->data, c->maxpoints * sz);
|
||||
c->datasize = c->maxpoints * sz;
|
||||
c->data = pcrealloc(c->data, c->datasize);
|
||||
}
|
||||
|
||||
/* Copy the data buffer from point to patch */
|
||||
@ -102,8 +160,10 @@ pc_patch_add_point(PCPATCH *c, const PCPOINT *p)
|
||||
return PC_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
PCPATCH *
|
||||
pc_patch_make_from_points(const PCPOINT **pts, uint32_t numpts)
|
||||
pc_patch_from_points(const PCPOINT **pts, uint32_t numpts)
|
||||
{
|
||||
PCPATCH *pch;
|
||||
const PCSCHEMA *s;
|
||||
@ -112,13 +172,13 @@ pc_patch_make_from_points(const PCPOINT **pts, uint32_t numpts)
|
||||
|
||||
if ( ! numpts )
|
||||
{
|
||||
pcerror("zero point count passed into pc_patch_make_from_points");
|
||||
pcerror("zero point count passed into pc_patch_from_points");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( ! pts )
|
||||
{
|
||||
pcerror("null point array passed into pc_patch_make_from_points");
|
||||
pcerror("null point array passed into pc_patch_from_points");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -129,7 +189,7 @@ pc_patch_make_from_points(const PCPOINT **pts, uint32_t numpts)
|
||||
/* Confirm width of a point data buffer */
|
||||
if ( ! s->size )
|
||||
{
|
||||
pcerror("invalid point size in pc_patch_make_from_points");
|
||||
pcerror("invalid point size in pc_patch_from_points");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -144,6 +204,7 @@ pc_patch_make_from_points(const PCPOINT **pts, uint32_t numpts)
|
||||
|
||||
/* Set up basic info */
|
||||
pch->readonly = PC_FALSE;
|
||||
pch->compressed = PC_FALSE;
|
||||
pch->maxpoints = numpts;
|
||||
pch->schema = s;
|
||||
pch->npoints = 0;
|
||||
@ -154,7 +215,7 @@ pc_patch_make_from_points(const PCPOINT **pts, uint32_t numpts)
|
||||
{
|
||||
if ( pts[i]->schema != s )
|
||||
{
|
||||
pcerror("points do not share a schema in pc_patch_make_from_points");
|
||||
pcerror("points do not share a schema in pc_patch_from_points");
|
||||
return NULL;
|
||||
}
|
||||
memcpy(ptr, pts[i]->data, s->size);
|
||||
@ -163,10 +224,279 @@ pc_patch_make_from_points(const PCPOINT **pts, uint32_t numpts)
|
||||
}
|
||||
else
|
||||
{
|
||||
pcwarn("encountered null point in pc_patch_make_from_points");
|
||||
pcwarn("encountered null point in pc_patch_from_points");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return pch;
|
||||
}
|
||||
|
||||
|
||||
PCPOINTLIST *
|
||||
pc_patch_to_points_uncompressed(const PCPATCH *patch)
|
||||
{
|
||||
int i;
|
||||
PCPOINTLIST *pl;
|
||||
size_t pt_size = patch->schema->size;
|
||||
uint32_t npoints = patch->npoints;
|
||||
|
||||
pl = pc_pointlist_make(npoints);
|
||||
for ( i = 0; i < npoints; i++ )
|
||||
{
|
||||
pc_pointlist_add_point(pl, pc_point_from_data(patch->schema, patch->data + i*pt_size));
|
||||
}
|
||||
return pl;
|
||||
}
|
||||
|
||||
PCPOINTLIST *
|
||||
pc_patch_to_points(const PCPATCH *patch)
|
||||
{
|
||||
uint32_t compression = patch->schema->compression;
|
||||
|
||||
if ( ! patch->compressed )
|
||||
return pc_patch_to_points_uncompressed(patch);
|
||||
|
||||
switch ( compression )
|
||||
{
|
||||
case PC_NONE:
|
||||
{
|
||||
return pc_patch_to_points_uncompressed(patch);
|
||||
}
|
||||
case PC_GHT:
|
||||
{
|
||||
// return pc_patch_to_points_ght(patch);
|
||||
}
|
||||
case PC_DIMENSIONAL:
|
||||
{
|
||||
// return pc_patch_to_points_dimensional(patch);
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't get here */
|
||||
pcerror("pc_patch_to_points: unsupported compression type %d", compression);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static PCPATCH *
|
||||
pc_patch_compress_dimensional(const PCPATCH *patch)
|
||||
{
|
||||
pcerror("pc_patch_compress_dimensional unimplemented");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PCPATCH *
|
||||
pc_patch_compress_ght(const PCPATCH *patch)
|
||||
{
|
||||
pcerror("pc_patch_compress_ght unimplemented");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
PCPATCH *
|
||||
pc_patch_compress(const PCPATCH *patch)
|
||||
{
|
||||
uint32_t compression = patch->schema->compression;
|
||||
|
||||
if ( patch->compressed )
|
||||
return pc_patch_clone(patch);
|
||||
|
||||
switch ( compression )
|
||||
{
|
||||
case PC_NONE:
|
||||
{
|
||||
PCPATCH *newpatch = pc_patch_clone(patch);
|
||||
newpatch->compressed = PC_TRUE;
|
||||
return newpatch;
|
||||
}
|
||||
case PC_GHT:
|
||||
{
|
||||
return pc_patch_compress_ght(patch);
|
||||
}
|
||||
case PC_DIMENSIONAL:
|
||||
{
|
||||
return pc_patch_compress_dimensional(patch);
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't get here */
|
||||
pcerror("pc_patch_compress: unknown compression type %d", compression);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PCPATCH *
|
||||
pc_patch_clone(const PCPATCH *patch)
|
||||
{
|
||||
PCPATCH *newpatch = pcalloc(sizeof(PCPATCH));
|
||||
memcpy(newpatch, patch, sizeof(PCPATCH));
|
||||
newpatch->data = pcalloc(newpatch->datasize);
|
||||
memcpy(newpatch->data, patch->data, newpatch->datasize);
|
||||
return newpatch;
|
||||
}
|
||||
|
||||
|
||||
static PCPATCH *
|
||||
pc_patch_from_wkb_uncompressed(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)
|
||||
uint32: npoints
|
||||
pcpoint[]: data (interpret relative to pcid)
|
||||
*/
|
||||
static size_t hdrsz = 1+4+4+4; /* endian + pcid + compression + npoints */
|
||||
PCPATCH *patch;
|
||||
uint8_t *data;
|
||||
uint8_t swap_endian = (wkb[0] != machine_endian());
|
||||
uint32_t npoints;
|
||||
|
||||
if ( wkb_get_compression(wkb) != PC_NONE )
|
||||
{
|
||||
pcerror("pc_patch_from_wkb_uncompressed: call with wkb that is not uncompressed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
npoints = wkb_get_npoints(wkb);
|
||||
if ( (wkbsize - hdrsz) != (s->size * npoints) )
|
||||
{
|
||||
pcerror("pc_patch_from_wkb_uncompressed: wkb size and expected data size do not match");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( swap_endian )
|
||||
{
|
||||
data = uncompressed_bytes_flip_endian(wkb+hdrsz, s, npoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = pcalloc(npoints * s->size);
|
||||
memcpy(data, wkb+hdrsz, npoints*s->size);
|
||||
}
|
||||
|
||||
patch = pcalloc(sizeof(PCPATCH));
|
||||
patch->npoints = npoints;
|
||||
patch->maxpoints = npoints;
|
||||
patch->schema = s;
|
||||
patch->compressed = PC_TRUE; /* It's in whatever compression it came in */
|
||||
patch->datasize = (wkbsize - hdrsz);
|
||||
patch->data = data;
|
||||
|
||||
if ( PC_FAILURE == pc_patch_compute_extent(patch) )
|
||||
{
|
||||
pcerror("pc_patch_compute_extent failed");
|
||||
}
|
||||
|
||||
return patch;
|
||||
}
|
||||
|
||||
|
||||
|
||||
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;
|
||||
|
||||
if ( ! wkbsize )
|
||||
{
|
||||
pcerror("pc_patch_from_wkb: zero length wkb");
|
||||
}
|
||||
|
||||
pcid = wkb_get_pcid(wkb);
|
||||
compression = wkb_get_compression(wkb);
|
||||
|
||||
if ( compression != s->compression )
|
||||
{
|
||||
pcerror("pc_patch_from_wkb: wkb compression (%d) not consistent with schema compression (%d)", compression, s->compression);
|
||||
}
|
||||
if ( pcid != s->pcid )
|
||||
{
|
||||
pcerror("pc_patch_from_wkb: wkb pcid (%d) not consistent with schema pcid (%d)", pcid, s->pcid);
|
||||
}
|
||||
|
||||
switch ( compression )
|
||||
{
|
||||
case PC_NONE:
|
||||
{
|
||||
return pc_patch_from_wkb_uncompressed(s, wkb, wkbsize);
|
||||
}
|
||||
case PC_GHT:
|
||||
{
|
||||
pcerror("pc_patch_from_wkb: GHT compression not yet supported");
|
||||
return NULL;
|
||||
}
|
||||
case PC_DIMENSIONAL:
|
||||
{
|
||||
pcerror("pc_patch_from_wkb: Dimensional compression not yet supported");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't get here */
|
||||
pcerror("pc_patch_from_wkb: unknown compression '%d' requested", compression);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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)
|
||||
*/
|
||||
char endian = machine_endian();
|
||||
size_t size = 1 + 4 + 4 + patch->datasize;
|
||||
uint8_t *wkb = pcalloc(size);
|
||||
wkb[0] = endian; /* Write endian flag */
|
||||
memcpy(wkb + 1, &(patch->schema->pcid), 4); /* Write PCID */
|
||||
memcpy(wkb + 5, patch->data, patch->datasize); /* Write data */
|
||||
if ( wkbsize ) *wkbsize = size;
|
||||
return wkb;
|
||||
}
|
||||
|
||||
char *
|
||||
pc_patch_to_string(const PCPATCH *patch)
|
||||
{
|
||||
/* ( <pcid> : (<dim1>, <dim2>, <dim3>, <dim4>), (<dim1>, <dim2>, <dim3>, <dim4>))*/
|
||||
stringbuffer_t *sb = stringbuffer_create();
|
||||
PCPOINTLIST *pl;
|
||||
char *str;
|
||||
int i, j;
|
||||
|
||||
pl = pc_patch_to_points(patch);
|
||||
stringbuffer_aprintf(sb, "[ %d : ", patch->schema->pcid);
|
||||
for ( i = 0; i < pl->npoints; i++ )
|
||||
{
|
||||
PCPOINT *pt = pl->points[i];
|
||||
if ( i )
|
||||
{
|
||||
stringbuffer_append(sb, ", ");
|
||||
}
|
||||
stringbuffer_append(sb, "(");
|
||||
for ( j = 0; j < pt->schema->ndims; j++ )
|
||||
{
|
||||
if ( j )
|
||||
{
|
||||
stringbuffer_append(sb, ", ");
|
||||
}
|
||||
stringbuffer_aprintf(sb, "%g", pc_point_get_double_by_index(pt, j));
|
||||
}
|
||||
stringbuffer_append(sb, ")");
|
||||
}
|
||||
stringbuffer_append(sb, " ]");
|
||||
|
||||
/* All done, copy and clean up */
|
||||
pc_pointlist_free(pl);
|
||||
str = stringbuffer_getstringcopy(sb);
|
||||
stringbuffer_destroy(sb);
|
||||
|
||||
return str;
|
||||
}
|
||||
@ -64,14 +64,6 @@ pc_point_from_data(const PCSCHEMA *s, const uint8_t *data)
|
||||
return pt;
|
||||
}
|
||||
|
||||
PCPOINT *
|
||||
pc_point_from_data_rw(const PCSCHEMA *s, uint8_t *data)
|
||||
{
|
||||
PCPOINT *pt = pc_point_from_data(s, data);
|
||||
pt->readonly = PC_FALSE;
|
||||
return pt;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
pc_point_free(PCPOINT *pt)
|
||||
@ -168,7 +160,7 @@ pc_point_get_y(const PCPOINT *pt)
|
||||
}
|
||||
|
||||
char *
|
||||
pc_text_from_point(const PCPOINT *pt, size_t *sersize)
|
||||
pc_point_to_string(const PCPOINT *pt)
|
||||
{
|
||||
/* ( <pcid> : <dim1>, <dim2>, <dim3>, <dim4> )*/
|
||||
stringbuffer_t *sb = stringbuffer_create();
|
||||
@ -223,6 +215,65 @@ pc_point_from_double_array(const PCSCHEMA *s, double *array, uint32_t nelems)
|
||||
}
|
||||
|
||||
return pt;
|
||||
}
|
||||
}
|
||||
|
||||
PCPOINT *
|
||||
pc_point_from_wkb(const PCSCHEMA *schema, uint8_t *wkb, size_t wkblen)
|
||||
{
|
||||
/*
|
||||
byte: endianness (1 = NDR, 0 = XDR)
|
||||
uint32: pcid (key to POINTCLOUD_SCHEMAS)
|
||||
uchar[]: data (interpret relative to pcid)
|
||||
*/
|
||||
const size_t hdrsz = 1+4; /* endian + pcid */
|
||||
uint8_t wkb_endian;
|
||||
uint32_t pcid;
|
||||
uint8_t *data;
|
||||
PCPOINT *pt;
|
||||
|
||||
if ( ! wkblen )
|
||||
{
|
||||
pcerror("pc_point_from_wkb: zero length wkb");
|
||||
}
|
||||
|
||||
wkb_endian = wkb[0];
|
||||
pcid = wkb_get_pcid(wkb);
|
||||
|
||||
if ( (wkblen-hdrsz) != schema->size )
|
||||
{
|
||||
pcerror("pc_point_from_wkb: wkb size inconsistent with schema size");
|
||||
}
|
||||
|
||||
if ( wkb_endian != machine_endian() )
|
||||
{
|
||||
/* uncompressed_bytes_flip_endian creates a flipped copy */
|
||||
data = uncompressed_bytes_flip_endian(wkb+hdrsz, schema, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = pcalloc(schema->size);
|
||||
memcpy(data, wkb+hdrsz, wkblen-hdrsz);
|
||||
}
|
||||
|
||||
pt = pc_point_from_data(schema, data);
|
||||
pt->readonly = PC_FALSE;
|
||||
return pt;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
pc_point_to_wkb(const PCPOINT *pt, size_t *wkbsize)
|
||||
{
|
||||
/*
|
||||
byte: endianness (1 = NDR, 0 = XDR)
|
||||
uint32: pcid (key to POINTCLOUD_SCHEMAS)
|
||||
uchar[]: data (interpret relative to pcid)
|
||||
*/
|
||||
char endian = machine_endian();
|
||||
size_t size = 1 + 4 + pt->schema->size;
|
||||
uint8_t *wkb = pcalloc(size);
|
||||
wkb[0] = endian; /* Write endian flag */
|
||||
memcpy(wkb + 1, &(pt->schema->pcid), 4); /* Write PCID */
|
||||
memcpy(wkb + 5, pt->data, pt->schema->size); /* Write data */
|
||||
if ( wkbsize ) *wkbsize = size;
|
||||
return wkb;
|
||||
}
|
||||
|
||||
50
libpc/pc_pointlist.c
Normal file
50
libpc/pc_pointlist.c
Normal file
@ -0,0 +1,50 @@
|
||||
/***********************************************************************
|
||||
* pc_pointlist.c
|
||||
*
|
||||
* Point list handling. Create, get and set values from the
|
||||
* basic PCPOINTLIST structure.
|
||||
*
|
||||
* Portions Copyright (c) 2012, OpenGeo
|
||||
*
|
||||
***********************************************************************/
|
||||
|
||||
#include "pc_api_internal.h"
|
||||
|
||||
PCPOINTLIST *
|
||||
pc_pointlist_make(uint32_t npoints)
|
||||
{
|
||||
PCPOINTLIST *pl = pcalloc(sizeof(PCPOINTLIST));
|
||||
pl->points = pcalloc(sizeof(PCPOINT*) * npoints);
|
||||
pl->maxpoints = npoints;
|
||||
pl->npoints = 0;
|
||||
pl->readonly = PC_FALSE;
|
||||
return pl;
|
||||
}
|
||||
|
||||
void
|
||||
pc_pointlist_free(PCPOINTLIST *pl)
|
||||
{
|
||||
int i;
|
||||
for ( i = 0; i < pl->npoints; i++ )
|
||||
{
|
||||
pc_point_free(pl->points[i]);
|
||||
}
|
||||
pcfree(pl->points);
|
||||
pcfree(pl);
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
pc_pointlist_add_point(PCPOINTLIST *pl, PCPOINT *pt)
|
||||
{
|
||||
if ( pl->npoints >= pl->maxpoints )
|
||||
{
|
||||
if ( pl->maxpoints < 1 ) pl->maxpoints = 1;
|
||||
pl->maxpoints *= 2;
|
||||
pl->points = pcrealloc(pl->points, pl->maxpoints * sizeof(PCPOINT*));
|
||||
}
|
||||
|
||||
pl->points[pl->npoints] = pt;
|
||||
pl->npoints += 1;
|
||||
return;
|
||||
}
|
||||
@ -311,6 +311,10 @@ pc_schema_from_xml(const char *xml_str, PCSCHEMA **schema)
|
||||
d->interpretation = pc_interpretation_number(child->children->content);
|
||||
else if ( strcmp(child->name, "scale") == 0 )
|
||||
d->scale = atof(child->children->content);
|
||||
else if ( strcmp(child->name, "uuid") == 0 )
|
||||
/* Ignore this tag for now */ 1;
|
||||
else if ( strcmp(child->name, "parent_uuid") == 0 )
|
||||
/* Ignore this tag for now */ 1;
|
||||
else
|
||||
pcinfo("unhandled schema type element \"%s\" encountered", child->name);
|
||||
}
|
||||
|
||||
174
libpc/pc_util.c
174
libpc/pc_util.c
@ -9,8 +9,180 @@
|
||||
|
||||
#include "pc_api_internal.h"
|
||||
|
||||
/**********************************************************************************
|
||||
* WKB AND ENDIANESS UTILITIES
|
||||
*/
|
||||
|
||||
/* Our static character->number map. Anything > 15 is invalid */
|
||||
static uint8_t hex2char[256] = {
|
||||
/* not Hex characters */
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
/* 0-9 */
|
||||
0,1,2,3,4,5,6,7,8,9,20,20,20,20,20,20,
|
||||
/* A-F */
|
||||
20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20,
|
||||
/* not Hex characters */
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
/* a-f */
|
||||
20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
/* not Hex characters (upper 128 characters) */
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20
|
||||
};
|
||||
|
||||
|
||||
uint8_t*
|
||||
bytes_flip_endian(const uint8_t *bytebuf, const PCSCHEMA *schema, uint32_t npoints)
|
||||
bytes_from_hexbytes(const char *hexbuf, size_t hexsize)
|
||||
{
|
||||
uint8_t *buf = NULL;
|
||||
register uint8_t h1, h2;
|
||||
int i;
|
||||
|
||||
if( hexsize % 2 )
|
||||
pcerror("Invalid hex string, length (%d) has to be a multiple of two!", hexsize);
|
||||
|
||||
buf = pcalloc(hexsize/2);
|
||||
|
||||
if( ! buf )
|
||||
pcerror("Unable to allocate memory buffer.");
|
||||
|
||||
for( i = 0; i < hexsize/2; i++ )
|
||||
{
|
||||
h1 = hex2char[(int)hexbuf[2*i]];
|
||||
h2 = hex2char[(int)hexbuf[2*i+1]];
|
||||
if( h1 > 15 )
|
||||
pcerror("Invalid hex character (%c) encountered", hexbuf[2*i]);
|
||||
if( h2 > 15 )
|
||||
pcerror("Invalid hex character (%c) encountered", hexbuf[2*i+1]);
|
||||
/* First character is high bits, second is low bits */
|
||||
buf[i] = ((h1 & 0x0F) << 4) | (h2 & 0x0F);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
char*
|
||||
hexbytes_from_bytes(const uint8_t *bytebuf, size_t bytesize)
|
||||
{
|
||||
char *buf = pcalloc(2*bytesize + 1); /* 2 chars per byte + null terminator */
|
||||
int i;
|
||||
char *ptr = buf;
|
||||
|
||||
for ( i = 0; i < bytesize; i++ )
|
||||
{
|
||||
int incr = snprintf(ptr, 3, "%02X", bytebuf[i]);
|
||||
if ( incr < 0 )
|
||||
{
|
||||
pcerror("write failure in hexbytes_from_bytes");
|
||||
return NULL;
|
||||
}
|
||||
ptr += incr;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
char
|
||||
machine_endian(void)
|
||||
{
|
||||
static int check_int = 1; /* dont modify this!!! */
|
||||
return *((char *) &check_int); /* 0 = big endian | xdr,
|
||||
* 1 = little endian | ndr
|
||||
*/
|
||||
}
|
||||
|
||||
int32_t
|
||||
int32_flip_endian(int32_t val)
|
||||
{
|
||||
int i;
|
||||
uint8_t tmp;
|
||||
uint8_t b[4];
|
||||
memcpy(b, &val, 4);
|
||||
for ( i = 0; i < 2; i++ )
|
||||
{
|
||||
tmp = b[i];
|
||||
b[i] = b[3-i];
|
||||
b[3-i] = tmp;
|
||||
}
|
||||
memcpy(&val, b, 4);
|
||||
return val;
|
||||
}
|
||||
|
||||
int16_t
|
||||
int16_flip_endian(int16_t val)
|
||||
{
|
||||
int i;
|
||||
uint8_t tmp;
|
||||
uint8_t b[2];
|
||||
memcpy(b, &val, 2);
|
||||
tmp = b[0];
|
||||
b[0] = b[1];
|
||||
b[1] = tmp;
|
||||
memcpy(&val, b, 2);
|
||||
return val;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
wkb_get_pcid(uint8_t *wkb)
|
||||
{
|
||||
/* We expect the bytes to be in WKB format for PCPOINT/PCPATCH */
|
||||
/* byte 0: endian */
|
||||
/* byte 1-4: pcid */
|
||||
/* ...data... */
|
||||
uint32_t pcid;
|
||||
memcpy(&pcid, wkb + 1, 4);
|
||||
if ( wkb[0] != machine_endian() )
|
||||
{
|
||||
pcid = int32_flip_endian(pcid);
|
||||
}
|
||||
return pcid;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
wkb_get_compression(uint8_t *wkb)
|
||||
{
|
||||
/* We expect the bytes to be in WKB format for PCPATCH */
|
||||
/* byte 0: endian */
|
||||
/* byte 1-4: pcid */
|
||||
/* byte 5-8: compression */
|
||||
/* ...data... */
|
||||
uint32_t compression;
|
||||
memcpy(&compression, wkb+1+4, 4);
|
||||
if ( wkb[0] != machine_endian() )
|
||||
{
|
||||
compression = int32_flip_endian(compression);
|
||||
}
|
||||
return compression;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
wkb_get_npoints(uint8_t *wkb)
|
||||
{
|
||||
/* We expect the bytes to be in WKB format for PCPATCH */
|
||||
/* byte 0: endian */
|
||||
/* byte 1-4: pcid */
|
||||
/* byte 5-8: compression */
|
||||
/* byte 9-12: compression */
|
||||
/* ...data... */
|
||||
uint32_t npoints;
|
||||
memcpy(&npoints, wkb+1+4+4, 4);
|
||||
if ( wkb[0] != machine_endian() )
|
||||
{
|
||||
npoints = int32_flip_endian(npoints);
|
||||
}
|
||||
return npoints;
|
||||
}
|
||||
uint8_t*
|
||||
uncompressed_bytes_flip_endian(const uint8_t *bytebuf, const PCSCHEMA *schema, uint32_t npoints)
|
||||
{
|
||||
int i, j, k;
|
||||
size_t bufsize = schema->size * npoints;
|
||||
|
||||
0
pgsql/expected/pointcloud.out
Normal file
0
pgsql/expected/pointcloud.out
Normal file
@ -4,11 +4,15 @@
|
||||
/* In/out functions */
|
||||
Datum pcpoint_in(PG_FUNCTION_ARGS);
|
||||
Datum pcpoint_out(PG_FUNCTION_ARGS);
|
||||
Datum pcpatch_in(PG_FUNCTION_ARGS);
|
||||
Datum pcpatch_out(PG_FUNCTION_ARGS);
|
||||
|
||||
/* Other SQL functions */
|
||||
Datum PC_SchemaIsValid(PG_FUNCTION_ARGS);
|
||||
Datum PC_SchemaGetNDims(PG_FUNCTION_ARGS);
|
||||
Datum PC_MakePointFromArray(PG_FUNCTION_ARGS);
|
||||
Datum PC_PointAsText(PG_FUNCTION_ARGS);
|
||||
Datum PC_PatchAsText(PG_FUNCTION_ARGS);
|
||||
|
||||
PG_FUNCTION_INFO_V1(pcpoint_in);
|
||||
Datum pcpoint_in(PG_FUNCTION_ARGS)
|
||||
@ -51,18 +55,66 @@ Datum pcpoint_out(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PCPOINT *pcpt = NULL;
|
||||
SERIALIZED_POINT *serpt = NULL;
|
||||
uint8_t *wkb = NULL;
|
||||
size_t wkb_size = 0;
|
||||
char *hexwkb = NULL;
|
||||
|
||||
serpt = (SERIALIZED_POINT*)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
||||
pcpt = pc_point_deserialize(serpt);
|
||||
wkb = wkb_from_point(pcpt, &wkb_size);
|
||||
hexwkb = hexbytes_from_bytes(wkb, wkb_size);
|
||||
hexwkb = pc_point_to_hexwkb(pcpt);
|
||||
pc_point_free(pcpt);
|
||||
PG_RETURN_CSTRING(hexwkb);
|
||||
}
|
||||
|
||||
|
||||
PG_FUNCTION_INFO_V1(pcpatch_in);
|
||||
Datum pcpatch_in(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char *str = PG_GETARG_CSTRING(0);
|
||||
/* Datum geog_oid = PG_GETARG_OID(1); Not needed. */
|
||||
int32 pc_typmod = -1;
|
||||
PCPATCH *patch;
|
||||
SERIALIZED_PATCH *serpatch;
|
||||
|
||||
if ( (PG_NARGS()>2) && (!PG_ARGISNULL(2)) )
|
||||
{
|
||||
pc_typmod = PG_GETARG_INT32(2);
|
||||
}
|
||||
|
||||
/* Empty string. */
|
||||
if ( str[0] == '\0' )
|
||||
{
|
||||
ereport(ERROR,(errmsg("pcpatch parse error - empty string")));
|
||||
}
|
||||
|
||||
/* Binary or text form? Let's find out. */
|
||||
if ( str[0] == '0' )
|
||||
{
|
||||
/* Hex-encoded binary */
|
||||
patch = pc_patch_from_hexwkb(str, strlen(str));
|
||||
serpatch = pc_patch_serialize(patch);
|
||||
pc_patch_free(patch);
|
||||
}
|
||||
else
|
||||
{
|
||||
ereport(ERROR,(errmsg("parse error - support for text format not yet implemented")));
|
||||
}
|
||||
|
||||
PG_RETURN_POINTER(serpatch);
|
||||
}
|
||||
|
||||
PG_FUNCTION_INFO_V1(pcpatch_out);
|
||||
Datum pcpatch_out(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PCPATCH *patch = NULL;
|
||||
SERIALIZED_PATCH *serpatch = NULL;
|
||||
char *hexwkb = NULL;
|
||||
|
||||
serpatch = (SERIALIZED_PATCH*)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
||||
patch = pc_patch_deserialize(serpatch);
|
||||
hexwkb = pc_patch_to_hexwkb(patch);
|
||||
pc_patch_free(patch);
|
||||
PG_RETURN_CSTRING(hexwkb);
|
||||
}
|
||||
|
||||
PG_FUNCTION_INFO_V1(PC_SchemaIsValid);
|
||||
Datum PC_SchemaIsValid(PG_FUNCTION_ARGS)
|
||||
{
|
||||
@ -79,6 +131,7 @@ Datum PC_SchemaIsValid(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
valid = pc_schema_is_valid(schema);
|
||||
pc_schema_free(schema);
|
||||
PG_RETURN_BOOL(valid);
|
||||
}
|
||||
|
||||
@ -141,5 +194,40 @@ Datum PC_MakePointFromArray(PG_FUNCTION_ARGS)
|
||||
|
||||
serpt = pc_point_serialize(pt);
|
||||
pc_point_free(pt);
|
||||
pc_schema_free(schema);
|
||||
PG_RETURN_POINTER(serpt);
|
||||
}
|
||||
}
|
||||
|
||||
PG_FUNCTION_INFO_V1(PC_PointAsText);
|
||||
Datum PC_PointAsText(PG_FUNCTION_ARGS)
|
||||
{
|
||||
SERIALIZED_POINT *serpt = PG_GETARG_SERPOINT_P(0);
|
||||
text *txt;
|
||||
char *str;
|
||||
PCPOINT *pt = pc_point_deserialize(serpt);
|
||||
if ( ! pt )
|
||||
PG_RETURN_NULL();
|
||||
|
||||
str = pc_point_to_string(pt);
|
||||
pc_point_free(pt);
|
||||
txt = cstring_to_text(str);
|
||||
pfree(str);
|
||||
PG_RETURN_TEXT_P(txt);
|
||||
}
|
||||
|
||||
PG_FUNCTION_INFO_V1(PC_PatchAsText);
|
||||
Datum PC_PatchAsText(PG_FUNCTION_ARGS)
|
||||
{
|
||||
SERIALIZED_PATCH *serpatch = PG_GETARG_SERPATCH_P(0);
|
||||
text *txt;
|
||||
char *str;
|
||||
PCPATCH *patch = pc_patch_deserialize(serpatch);
|
||||
if ( ! patch )
|
||||
PG_RETURN_NULL();
|
||||
|
||||
str = pc_patch_to_string(patch);
|
||||
pc_patch_free(patch);
|
||||
txt = cstring_to_text(str);
|
||||
pfree(str);
|
||||
PG_RETURN_TEXT_P(txt);
|
||||
}
|
||||
|
||||
266
pgsql/pc_pgsql.c
266
pgsql/pc_pgsql.c
@ -93,207 +93,71 @@ _PG_fini(void)
|
||||
|
||||
|
||||
/**********************************************************************************
|
||||
* WKB AND ENDIANESS UTILITIES
|
||||
* PCPOINT WKB Handling
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* Our static character->number map. Anything > 15 is invalid */
|
||||
static uint8_t hex2char[256] = {
|
||||
/* not Hex characters */
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
/* 0-9 */
|
||||
0,1,2,3,4,5,6,7,8,9,20,20,20,20,20,20,
|
||||
/* A-F */
|
||||
20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20,
|
||||
/* not Hex characters */
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
/* a-f */
|
||||
20,10,11,12,13,14,15,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
/* not Hex characters (upper 128 characters) */
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,
|
||||
20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20
|
||||
};
|
||||
|
||||
|
||||
uint8_t*
|
||||
bytes_from_hexbytes(const char *hexbuf, size_t hexsize)
|
||||
{
|
||||
uint8_t *buf = NULL;
|
||||
register uint8_t h1, h2;
|
||||
int i;
|
||||
|
||||
if( hexsize % 2 )
|
||||
pcerror("Invalid hex string, length (%d) has to be a multiple of two!", hexsize);
|
||||
|
||||
buf = palloc(hexsize/2);
|
||||
|
||||
if( ! buf )
|
||||
pcerror("Unable to allocate memory buffer.");
|
||||
|
||||
for( i = 0; i < hexsize/2; i++ )
|
||||
{
|
||||
h1 = hex2char[(int)hexbuf[2*i]];
|
||||
h2 = hex2char[(int)hexbuf[2*i+1]];
|
||||
if( h1 > 15 )
|
||||
pcerror("Invalid hex character (%c) encountered", hexbuf[2*i]);
|
||||
if( h2 > 15 )
|
||||
pcerror("Invalid hex character (%c) encountered", hexbuf[2*i+1]);
|
||||
/* First character is high bits, second is low bits */
|
||||
buf[i] = ((h1 & 0x0F) << 4) | (h2 & 0x0F);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
char*
|
||||
hexbytes_from_bytes(const uint8_t *bytebuf, size_t bytesize)
|
||||
{
|
||||
char *buf = palloc(2*bytesize + 1); /* 2 chars per byte + null terminator */
|
||||
int i;
|
||||
char *ptr = buf;
|
||||
|
||||
for ( i = 0; i < bytesize; i++ )
|
||||
{
|
||||
int incr = snprintf(ptr, 3, "%02X", bytebuf[i]);
|
||||
if ( incr < 0 )
|
||||
{
|
||||
pcerror("write failure in hexbytes_from_bytes");
|
||||
return NULL;
|
||||
}
|
||||
ptr += incr;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
char
|
||||
machine_endian(void)
|
||||
{
|
||||
static int check_int = 1; /* dont modify this!!! */
|
||||
return *((char *) &check_int); /* 0 = big endian | xdr,
|
||||
* 1 = little endian | ndr
|
||||
*/
|
||||
}
|
||||
|
||||
static int32_t
|
||||
int32_flip_endian(int32_t val)
|
||||
{
|
||||
int i;
|
||||
uint8_t tmp;
|
||||
uint8_t b[4];
|
||||
memcpy(b, &val, 4);
|
||||
for ( i = 0; i < 2; i++ )
|
||||
{
|
||||
tmp = b[i];
|
||||
b[i] = b[3-i];
|
||||
b[3-i] = tmp;
|
||||
}
|
||||
memcpy(&val, b, 4);
|
||||
return val;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
wkb_get_pcid(uint8_t *wkb)
|
||||
{
|
||||
/* We expect the bytes to be in WKB format for PCPOINT/PCPATCH */
|
||||
/* byte 0: endian */
|
||||
/* byte 1-4: pcid */
|
||||
/* ...data... */
|
||||
|
||||
uint8_t wkb_endian = wkb[0];
|
||||
uint32_t pcid;
|
||||
memcpy(&pcid, wkb + 1, 4);
|
||||
if ( wkb_endian != machine_endian() )
|
||||
{
|
||||
pcid = int32_flip_endian(pcid);
|
||||
}
|
||||
return pcid;
|
||||
}
|
||||
|
||||
PCPOINT *
|
||||
pc_point_from_hexwkb(const char *hexwkb, size_t hexlen)
|
||||
{
|
||||
PCPOINT *pt;
|
||||
PCSCHEMA *schema;
|
||||
uint32_t pcid;
|
||||
uint8_t *wkb = bytes_from_hexbytes(hexwkb, hexlen);
|
||||
size_t wkblen = hexlen/2;
|
||||
pt = pc_point_from_wkb(wkb, wkblen);
|
||||
pcid = wkb_get_pcid(wkb);
|
||||
schema = pc_schema_get_by_id(pcid);
|
||||
pt = pc_point_from_wkb(schema, wkb, wkblen);
|
||||
pfree(wkb);
|
||||
return pt;
|
||||
}
|
||||
|
||||
PCPOINT *
|
||||
pc_point_from_wkb(uint8_t *wkb, size_t wkblen)
|
||||
char *
|
||||
pc_point_to_hexwkb(const PCPOINT *pt)
|
||||
{
|
||||
/*
|
||||
byte: endianness (1 = NDR, 0 = XDR)
|
||||
uint32: pcid (key to POINTCLOUD_SCHEMAS)
|
||||
uchar[]: data (interpret relative to pcid)
|
||||
*/
|
||||
const size_t hdrsz = 1+4; /* endian + pcid */
|
||||
uint8_t wkb_endian;
|
||||
uint32_t pcid;
|
||||
uint8_t *data;
|
||||
uint8_t *wkb;
|
||||
size_t wkb_size;
|
||||
char *hexwkb;
|
||||
|
||||
wkb = pc_point_to_wkb(pt, &wkb_size);
|
||||
hexwkb = hexbytes_from_bytes(wkb, wkb_size);
|
||||
pfree(wkb);
|
||||
return hexwkb;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************************
|
||||
* PCPATCH WKB Handling
|
||||
*/
|
||||
|
||||
PCPATCH *
|
||||
pc_patch_from_hexwkb(const char *hexwkb, size_t hexlen)
|
||||
{
|
||||
PCPATCH *patch;
|
||||
PCSCHEMA *schema;
|
||||
PCPOINT *pt;
|
||||
|
||||
if ( ! wkblen )
|
||||
{
|
||||
elog(ERROR, "pc_point_from_wkb: zero length wkb");
|
||||
}
|
||||
|
||||
wkb_endian = wkb[0];
|
||||
pcid = wkb_get_pcid(wkb);
|
||||
uint32_t pcid;
|
||||
uint8_t *wkb = bytes_from_hexbytes(hexwkb, hexlen);
|
||||
size_t wkblen = hexlen/2;
|
||||
pcid = wkb_get_pcid(wkb);
|
||||
schema = pc_schema_get_by_id(pcid);
|
||||
|
||||
if ( (wkblen-hdrsz) != schema->size )
|
||||
{
|
||||
elog(ERROR, "pc_point_from_wkb: wkb size inconsistent with schema size");
|
||||
}
|
||||
|
||||
if ( wkb_endian != machine_endian() )
|
||||
{
|
||||
/* bytes_flip_endian creates a flipped copy */
|
||||
data = bytes_flip_endian(wkb+hdrsz, schema, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = palloc(schema->size);
|
||||
memcpy(data, wkb+hdrsz, wkblen-hdrsz);
|
||||
}
|
||||
|
||||
pt = pc_point_from_data_rw(schema, data);
|
||||
return pt;
|
||||
patch = pc_patch_from_wkb(schema, wkb, wkblen);
|
||||
pfree(wkb);
|
||||
return patch;
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
wkb_from_point(const PCPOINT *pt, size_t *wkbsize)
|
||||
char *
|
||||
pc_patch_to_hexwkb(const PCPATCH *patch)
|
||||
{
|
||||
/*
|
||||
byte: endianness (1 = NDR, 0 = XDR)
|
||||
uint32: pcid (key to POINTCLOUD_SCHEMAS)
|
||||
uchar[]: data (interpret relative to pcid)
|
||||
*/
|
||||
char endian = machine_endian();
|
||||
size_t size = 1 + 4 + pt->schema->size;
|
||||
uint8_t *wkb = palloc(size);
|
||||
wkb[0] = endian; /* Write endian flag */
|
||||
memcpy(wkb + 1, &(pt->schema->pcid), 4); /* Write PCID */
|
||||
memcpy(wkb + 5, pt->data, pt->schema->size); /* Write data */
|
||||
*wkbsize = size;
|
||||
return wkb;
|
||||
uint8_t *wkb;
|
||||
size_t wkb_size;
|
||||
char *hexwkb;
|
||||
|
||||
wkb = pc_patch_to_wkb(patch, &wkb_size);
|
||||
hexwkb = hexbytes_from_bytes(wkb, wkb_size);
|
||||
pfree(wkb);
|
||||
return hexwkb;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********************************************************************************
|
||||
* PCID <=> PCSCHEMA translation via POINTCLOUD_FORMATS
|
||||
*/
|
||||
@ -409,43 +273,49 @@ pc_point_deserialize(const SERIALIZED_POINT *serpt)
|
||||
return pcpt;
|
||||
}
|
||||
|
||||
|
||||
SERIALIZED_PATCH *
|
||||
pc_patch_serialize(const PCPATCH *pcpch)
|
||||
{
|
||||
size_t serpch_size = sizeof(SERIALIZED_PATCH) - 1 + pcpch->schema->size * pcpch->npoints;
|
||||
SERIALIZED_PATCH *serpch = palloc(serpch_size);
|
||||
serpch->pcid = pcpch->schema->pcid;
|
||||
serpch->npoints = pcpch->npoints;
|
||||
serpch->xmin = pcpch->xmin;
|
||||
serpch->ymin = pcpch->ymin;
|
||||
serpch->xmax = pcpch->xmax;
|
||||
serpch->ymax = pcpch->ymax;
|
||||
memcpy(serpch->data, pcpch->data, pcpch->npoints * pcpch->schema->size);
|
||||
size_t serpch_size;
|
||||
PCPATCH *patch;
|
||||
SERIALIZED_PATCH *serpch;
|
||||
|
||||
/* Compress uncompressed patches before saving */
|
||||
patch = pc_patch_compress(pcpch);
|
||||
|
||||
/* Allocate */
|
||||
serpch_size = sizeof(SERIALIZED_PATCH) - 1 + patch->datasize;
|
||||
serpch = palloc(serpch_size);
|
||||
|
||||
/* Copy */
|
||||
serpch->pcid = patch->schema->pcid;
|
||||
serpch->npoints = patch->npoints;
|
||||
serpch->xmin = patch->xmin;
|
||||
serpch->ymin = patch->ymin;
|
||||
serpch->xmax = patch->xmax;
|
||||
serpch->ymax = patch->ymax;
|
||||
memcpy(serpch->data, patch->data, patch->datasize);
|
||||
SET_VARSIZE(serpch, serpch_size);
|
||||
return serpch;
|
||||
}
|
||||
|
||||
PCPATCH *
|
||||
pc_patch_deserlialize(const SERIALIZED_PATCH *serpatch)
|
||||
pc_patch_deserialize(const SERIALIZED_PATCH *serpatch)
|
||||
{
|
||||
PCPATCH *patch;
|
||||
PCSCHEMA *schema = pc_schema_get_by_id(serpatch->pcid);
|
||||
/* on-disk size - size:int32 - pcid:int32 - npoints:int32 - 4*minmax:double = patch data size */
|
||||
size_t pgsize = VARSIZE(serpatch) - 3*4 - 4*8;
|
||||
if ( schema->size*serpatch->npoints != pgsize )
|
||||
{
|
||||
elog(ERROR, "schema size and disk size mismatch, repair the schema");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Reference the external data */
|
||||
patch = pcalloc(sizeof(PCPATCH));
|
||||
patch->data = (uint8_t*)serpatch->data;
|
||||
|
||||
|
||||
/* Set up basic info */
|
||||
patch->schema = schema;
|
||||
patch->readonly = true;
|
||||
patch->compressed = true;
|
||||
patch->npoints = serpatch->npoints;
|
||||
patch->maxpoints = 0;
|
||||
patch->xmin = serpatch->xmin;
|
||||
patch->ymin = serpatch->ymin;
|
||||
patch->xmax = serpatch->xmax;
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
#define POINTCLOUD_FORMATS_SRID "srid"
|
||||
|
||||
#define PG_GETARG_SERPOINT_P(datum) (SERIALIZED_POINT*)PG_DETOAST_DATUM(PG_GETARG_DATUM(datum))
|
||||
#define PG_GETARG_SERPATCH_P(datum) (SERIALIZED_PATCH*)PG_DETOAST_DATUM(PG_GETARG_DATUM(datum))
|
||||
|
||||
/**
|
||||
* Serialized point type for clouds. Variable length, because there can be
|
||||
@ -63,23 +64,21 @@ SERIALIZED_POINT* pc_point_serialize(const PCPOINT *pcpt);
|
||||
/** Turn a byte buffer into a PCPOINT for processing */
|
||||
PCPOINT* pc_point_deserialize(const SERIALIZED_POINT *serpt);
|
||||
|
||||
/** Returns 1 for little (NDR) and 0 for big (XDR) */
|
||||
char machine_endian(void);
|
||||
|
||||
/** Turn a binary buffer into a hex string */
|
||||
char* hexbytes_from_bytes(const uint8_t *bytebuf, size_t bytesize);
|
||||
|
||||
/** Create a new readwrite PCPOINT from a hex byte array */
|
||||
PCPOINT* pc_point_from_wkb(uint8_t *wkb, size_t wkbsize);
|
||||
|
||||
/** Create a new readwrite PCPOINT from a hex string */
|
||||
PCPOINT* pc_point_from_hexwkb(const char *hexwkb, size_t hexlen);
|
||||
|
||||
/** Returns serialized form of point */
|
||||
uint8_t* wkb_from_point(const PCPOINT *pt, size_t *wkbsize);
|
||||
/** Create a hex representation of a PCPOINT */
|
||||
char* pc_point_to_hexwkb(const PCPOINT *pt);
|
||||
|
||||
|
||||
/** Turn a PCPATCH into a byte buffer suitable for saving in PgSQL */
|
||||
SERIALIZED_PATCH* pc_patch_serialize(const PCPATCH *pcpch);
|
||||
SERIALIZED_PATCH* pc_patch_serialize(const PCPATCH *patch);
|
||||
|
||||
/** Turn a byte buffer into a PCPATCH for processing */
|
||||
PCPATCH* pc_patch_deserlialize(const SERIALIZED_PATCH *serpatch);
|
||||
PCPATCH* pc_patch_deserialize(const SERIALIZED_PATCH *serpatch);
|
||||
|
||||
/** Create a new readwrite PCPATCH from a hex string */
|
||||
PCPATCH* pc_patch_from_hexwkb(const char *hexwkb, size_t hexlen);
|
||||
|
||||
/** Create a hex representation of a PCPOINT */
|
||||
char* pc_patch_to_hexwkb(const PCPATCH *patch);
|
||||
|
||||
@ -63,8 +63,47 @@ CREATE OR REPLACE FUNCTION PC_MakePoint(pcid integer, vals float8[])
|
||||
RETURNS pcpoint AS 'MODULE_PATHNAME', 'PC_MakePointFromArray'
|
||||
LANGUAGE 'c' IMMUTABLE STRICT;
|
||||
|
||||
CREATE OR REPLACE FUNCTION PC_AsText(p pcpoint)
|
||||
RETURNS text AS 'MODULE_PATHNAME', 'PC_PointAsText'
|
||||
LANGUAGE 'c' IMMUTABLE STRICT;
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- PCPATCH
|
||||
-------------------------------------------------------------------
|
||||
|
||||
CREATE OR REPLACE FUNCTION pcpatch_in(cstring)
|
||||
RETURNS pcpatch AS 'MODULE_PATHNAME', 'pcpatch_in'
|
||||
LANGUAGE 'c' IMMUTABLE STRICT;
|
||||
|
||||
CREATE OR REPLACE FUNCTION pcpatch_out(pcpatch)
|
||||
RETURNS cstring AS 'MODULE_PATHNAME', 'pcpatch_out'
|
||||
LANGUAGE 'c' IMMUTABLE STRICT;
|
||||
|
||||
CREATE TYPE pcpatch (
|
||||
internallength = variable,
|
||||
input = pcpatch_in,
|
||||
output = pcpatch_out,
|
||||
-- send = geometry_send,
|
||||
-- receive = geometry_recv,
|
||||
-- typmod_in = geometry_typmod_in,
|
||||
-- typmod_out = geometry_typmod_out,
|
||||
-- delimiter = ':',
|
||||
-- alignment = double,
|
||||
-- analyze = geometry_analyze,
|
||||
storage = main
|
||||
);
|
||||
|
||||
CREATE OR REPLACE FUNCTION PC_AsText(p pcpatch)
|
||||
RETURNS text AS 'MODULE_PATHNAME', 'PC_PatchAsText'
|
||||
LANGUAGE 'c' IMMUTABLE STRICT;
|
||||
|
||||
|
||||
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- SAMPLE DATA
|
||||
--
|
||||
|
||||
-- Sample data
|
||||
INSERT INTO pointcloud_formats (pcid, srid, schema) VALUES (1, 4326, '<?xml version="1.0" encoding="UTF-8"?>
|
||||
<pc:PointCloudSchema xmlns:pc="http://pointcloud.org/schemas/PC/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<pc:dimension>
|
||||
|
||||
0
pgsql/sql/pointcloud.sql
Normal file
0
pgsql/sql/pointcloud.sql
Normal file
Loading…
x
Reference in New Issue
Block a user