/*********************************************************************** * 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":[, , , ] }*/ 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; ischema->ndims; ++i) { pc_point_get_double_by_index(p, i, &(a[i])); } return a; }