pointcloud/lib/pc_point.c
2022-02-08 09:14:32 +01:00

396 lines
8.5 KiB
C

/***********************************************************************
* pc_point.c
*
* Pointclound point handling. Create, get and set values from the
* basic PCPOINT structure.
*
* PgSQL Pointcloud is free and open source software provided
* by the Government of Canada
* Copyright (c) 2013 Natural Resources Canada
*
***********************************************************************/
#include "pc_api_internal.h"
#include "stringbuffer.h"
PCPOINT *pc_point_make(const PCSCHEMA *s)
{
size_t sz;
PCPOINT *pt;
if (!s)
{
pcerror("null schema passed into pc_point_make");
return NULL;
}
/* Width of the data area */
sz = s->size;
if (!sz)
{
pcerror("invalid size calculation in pc_point_make");
return NULL;
}
/* Make our own data area */
pt = pcalloc(sizeof(PCPOINT));
pt->data = pcalloc(sz);
/* Set up basic info */
pt->schema = s;
pt->readonly = PC_FALSE;
return pt;
};
PCPOINT *pc_point_from_data(const PCSCHEMA *s, const uint8_t *data)
{
PCPOINT *pt;
if (!s)
{
pcerror("null schema passed into pc_point_from_data");
return NULL;
}
/* Reference the external data */
pt = pcalloc(sizeof(PCPOINT));
pt->data = (uint8_t *)data;
/* Set up basic info */
pt->schema = s;
pt->readonly = PC_TRUE;
return pt;
}
void pc_point_free(PCPOINT *pt)
{
if (!pt->readonly)
{
pcfree(pt->data);
}
pcfree(pt);
}
int pc_point_get_double(const PCPOINT *pt, const PCDIMENSION *dim, double *val)
{
uint8_t *ptr;
double d;
if (!dim)
return PC_FAILURE;
/* Read raw value from byte buffer */
ptr = pt->data + dim->byteoffset;
d = pc_double_from_ptr(ptr, dim->interpretation);
d = pc_value_scale_offset(d, dim);
*val = d;
return PC_SUCCESS;
}
int pc_point_get_double_by_name(const PCPOINT *pt, const char *name,
double *val)
{
PCDIMENSION *dim;
dim = pc_schema_get_dimension_by_name(pt->schema, name);
return pc_point_get_double(pt, dim, val);
}
int pc_point_get_double_by_index(const PCPOINT *pt, uint32_t idx, double *val)
{
PCDIMENSION *dim;
dim = pc_schema_get_dimension(pt->schema, idx);
return pc_point_get_double(pt, dim, val);
}
int pc_point_set_double(PCPOINT *pt, const PCDIMENSION *dim, double val)
{
uint8_t *ptr;
if (!dim)
return PC_FAILURE;
/* Remove scale and offsets */
val = pc_value_unscale_unoffset(val, dim);
/* Get pointer into byte buffer */
ptr = pt->data + dim->byteoffset;
return pc_double_to_ptr(ptr, dim->interpretation, val);
}
int pc_point_set_double_by_index(PCPOINT *pt, uint32_t idx, double val)
{
PCDIMENSION *dim;
dim = pc_schema_get_dimension(pt->schema, idx);
return pc_point_set_double(pt, dim, val);
}
int pc_point_set_double_by_name(PCPOINT *pt, const char *name, double val)
{
PCDIMENSION *dim;
dim = pc_schema_get_dimension_by_name(pt->schema, name);
return pc_point_set_double(pt, dim, val);
}
int pc_point_get_x(const PCPOINT *pt, double *val)
{
return pc_point_get_double(pt, pt->schema->xdim, val);
}
int pc_point_get_y(const PCPOINT *pt, double *val)
{
return pc_point_get_double(pt, pt->schema->ydim, val);
}
int pc_point_get_z(const PCPOINT *pt, double *val)
{
return pc_point_get_double(pt, pt->schema->zdim, val);
}
int pc_point_get_m(const PCPOINT *pt, double *val)
{
return pc_point_get_double(pt, pt->schema->mdim, val);
}
int pc_point_set_x(PCPOINT *pt, double val)
{
return pc_point_set_double(pt, pt->schema->xdim, val);
}
int pc_point_set_y(PCPOINT *pt, double val)
{
return pc_point_set_double(pt, pt->schema->ydim, val);
}
int pc_point_set_z(PCPOINT *pt, double val)
{
return pc_point_set_double(pt, pt->schema->zdim, val);
}
int pc_point_set_m(PCPOINT *pt, double val)
{
return pc_point_set_double(pt, pt->schema->mdim, val);
}
char *pc_point_to_string(const PCPOINT *pt)
{
/* { "pcid":1, "values":[<dim1>, <dim2>, <dim3>, <dim4>] }*/
stringbuffer_t *sb = stringbuffer_create();
char *str;
int i;
stringbuffer_aprintf(sb, "{\"pcid\":%d,\"pt\":[", pt->schema->pcid);
for (i = 0; i < pt->schema->ndims; i++)
{
double d;
if (!pc_point_get_double_by_index(pt, i, &d))
{
pcerror("pc_point_to_string: unable to read double at position %d", i);
}
if (i)
{
stringbuffer_append(sb, ",");
}
stringbuffer_aprintf(sb, "%g", d);
}
stringbuffer_append(sb, "]}");
str = stringbuffer_getstringcopy(sb);
stringbuffer_destroy(sb);
return str;
}
PCPOINT *pc_point_from_double_array(const PCSCHEMA *s, double *array,
uint32_t offset, uint32_t stride)
{
int i;
PCPOINT *pt;
if (!s)
{
pcerror("null schema passed into pc_point_from_double_array");
return NULL;
}
if (stride != s->ndims)
{
pcerror("number of elements in schema and array do not match in "
"pc_point_from_double_array");
return NULL;
}
/* Reference the external data */
pt = pcalloc(sizeof(PCPOINT));
pt->data = pcalloc(s->size);
pt->schema = s;
pt->readonly = PC_FALSE;
for (i = 0; i < stride; i++)
{
if (PC_FAILURE == pc_point_set_double_by_index(pt, i, array[offset + i]))
{
pcerror("failed to write value into dimension %d in "
"pc_point_from_double_array",
i);
return NULL;
}
}
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;
uint8_t *data;
PCPOINT *pt;
if (!wkblen)
{
pcerror("pc_point_from_wkb: zero length wkb");
}
wkb_endian = wkb[0];
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;
}
uint8_t *pc_point_to_geometry_wkb(const PCPOINT *pt, size_t *wkbsize)
{
static uint32_t srid_mask = 0x20000000;
static uint32_t m_mask = 0x40000000;
static uint32_t z_mask = 0x80000000;
uint32_t wkbtype = 1; /* WKB POINT */
size_t size = 1 + 4 + 8 + 8; /* endian + type + dblX, + dblY */
uint8_t *wkb, *ptr;
uint32_t srid = pt->schema->srid;
double x, y, z, m;
int has_x = pc_point_get_x(pt, &x) == PC_SUCCESS;
int has_y = pc_point_get_y(pt, &y) == PC_SUCCESS;
int has_z = pc_point_get_z(pt, &z) == PC_SUCCESS;
int has_m = pc_point_get_m(pt, &m) == PC_SUCCESS;
if (!(has_x && has_y))
return NULL;
if (srid)
{
wkbtype |= srid_mask;
size += 4;
}
if (has_z)
{
wkbtype |= z_mask;
size += 8;
}
if (has_m)
{
wkbtype |= m_mask;
size += 8;
}
wkb = pcalloc(size);
ptr = wkb;
ptr[0] = machine_endian(); /* Endian flag */
ptr += 1;
memcpy(ptr, &wkbtype, 4); /* WKB type */
ptr += 4;
if (srid != 0)
{
memcpy(ptr, &srid, 4); /* SRID */
ptr += 4;
}
memcpy(ptr, &x, 8); /* X */
ptr += 8;
memcpy(ptr, &y, 8); /* Y */
ptr += 8;
if (has_z)
{
memcpy(ptr, &z, 8); /* Z */
ptr += 8;
}
if (has_m)
{
memcpy(ptr, &m, 8); /* M */
ptr += 8;
}
if (wkbsize)
*wkbsize = size;
return wkb;
}
/**
* @brief this function convert a PCPOINT to an array of double containing
* all the dimension values of this point
*
* @param a pointer to the point to convert to double
*
* @return a pointer to an array of double containing all the dimensions
* of the point expressed as double precision
*
*/
double *pc_point_to_double_array(const PCPOINT *p)
{
int i;
double *a = (double *)pcalloc(p->schema->ndims * sizeof(double));
for (i = 0; i < p->schema->ndims; ++i)
{
pc_point_get_double_by_index(p, i, &(a[i]));
}
return a;
}