1 /***********************************************************************
2 * pc_point.c
3 *
4 * Pointclound point handling. Create, get and set values from the
5 * basic PCPOINT structure.
6 *
7 * PgSQL Pointcloud is free and open source software provided
8 * by the Government of Canada
9 * Copyright (c) 2013 Natural Resources Canada
10 *
11 ***********************************************************************/
12
13 #include "pc_api_internal.h"
14 #include "stringbuffer.h"
15
16 PCPOINT *
pc_point_make(const PCSCHEMA * s)17 pc_point_make(const PCSCHEMA *s)
18 {
19 size_t sz;
20 PCPOINT *pt;
21
22 if ( ! s )
23 {
24 pcerror("null schema passed into pc_point_make");
25 return NULL;
26 }
27
28 /* Width of the data area */
29 sz = s->size;
30 if ( ! sz )
31 {
32 pcerror("invalid size calculation in pc_point_make");
33 return NULL;
34 }
35
36 /* Make our own data area */
37 pt = pcalloc(sizeof(PCPOINT));
38 pt->data = pcalloc(sz);
39
40 /* Set up basic info */
41 pt->schema = s;
42 pt->readonly = PC_FALSE;
43 return pt;
44 };
45
46 PCPOINT *
pc_point_from_data(const PCSCHEMA * s,const uint8_t * data)47 pc_point_from_data(const PCSCHEMA *s, const uint8_t *data)
48 {
49 PCPOINT *pt;
50
51 if ( ! s )
52 {
53 pcerror("null schema passed into pc_point_from_data");
54 return NULL;
55 }
56
57 /* Reference the external data */
58 pt = pcalloc(sizeof(PCPOINT));
59 pt->data = (uint8_t*)data;
60
61 /* Set up basic info */
62 pt->schema = s;
63 pt->readonly = PC_TRUE;
64 return pt;
65 }
66
67
68 void
pc_point_free(PCPOINT * pt)69 pc_point_free(PCPOINT *pt)
70 {
71 if ( ! pt->readonly )
72 {
73 pcfree(pt->data);
74 }
75 pcfree(pt);
76 }
77
78 int
pc_point_get_double(const PCPOINT * pt,const PCDIMENSION * dim,double * val)79 pc_point_get_double(const PCPOINT *pt, const PCDIMENSION *dim, double *val)
80 {
81 uint8_t *ptr;
82 double d;
83
84 if ( ! dim ) return PC_FAILURE;
85
86 /* Read raw value from byte buffer */
87 ptr = pt->data + dim->byteoffset;
88 d = pc_double_from_ptr(ptr, dim->interpretation);
89 d = pc_value_scale_offset(d, dim);
90
91 *val = d;
92 return PC_SUCCESS;
93 }
94
95 int
pc_point_get_double_by_name(const PCPOINT * pt,const char * name,double * val)96 pc_point_get_double_by_name(const PCPOINT *pt, const char *name, double *val)
97 {
98 PCDIMENSION *dim;
99 dim = pc_schema_get_dimension_by_name(pt->schema, name);
100 return pc_point_get_double(pt, dim, val);
101 }
102
103 int
pc_point_get_double_by_index(const PCPOINT * pt,uint32_t idx,double * val)104 pc_point_get_double_by_index(const PCPOINT *pt, uint32_t idx, double *val)
105 {
106 PCDIMENSION *dim;
107 dim = pc_schema_get_dimension(pt->schema, idx);
108 return pc_point_get_double(pt, dim, val);
109 }
110
111 int
pc_point_set_double(PCPOINT * pt,const PCDIMENSION * dim,double val)112 pc_point_set_double(PCPOINT *pt, const PCDIMENSION *dim, double val)
113 {
114 uint8_t *ptr;
115
116 if ( ! dim ) return PC_FAILURE;
117
118 /* Remove scale and offsets */
119 val = pc_value_unscale_unoffset(val, dim);
120
121 /* Get pointer into byte buffer */
122 ptr = pt->data + dim->byteoffset;
123
124 return pc_double_to_ptr(ptr, dim->interpretation, val);
125 }
126
127 int
pc_point_set_double_by_index(PCPOINT * pt,uint32_t idx,double val)128 pc_point_set_double_by_index(PCPOINT *pt, uint32_t idx, double val)
129 {
130 PCDIMENSION *dim;
131 dim = pc_schema_get_dimension(pt->schema, idx);
132 return pc_point_set_double(pt, dim, val);
133 }
134
135 int
pc_point_set_double_by_name(PCPOINT * pt,const char * name,double val)136 pc_point_set_double_by_name(PCPOINT *pt, const char *name, double val)
137 {
138 PCDIMENSION *dim;
139 dim = pc_schema_get_dimension_by_name(pt->schema, name);
140 return pc_point_set_double(pt, dim, val);
141 }
142
143 int
pc_point_get_x(const PCPOINT * pt,double * val)144 pc_point_get_x(const PCPOINT *pt, double *val)
145 {
146 return pc_point_get_double(pt, pt->schema->xdim, val);
147 }
148
149 int
pc_point_get_y(const PCPOINT * pt,double * val)150 pc_point_get_y(const PCPOINT *pt, double *val)
151 {
152 return pc_point_get_double(pt, pt->schema->ydim, val);
153 }
154
155 int
pc_point_get_z(const PCPOINT * pt,double * val)156 pc_point_get_z(const PCPOINT *pt, double *val)
157 {
158 return pc_point_get_double(pt, pt->schema->zdim, val);
159 }
160
161 int
pc_point_get_m(const PCPOINT * pt,double * val)162 pc_point_get_m(const PCPOINT *pt, double *val)
163 {
164 return pc_point_get_double(pt, pt->schema->mdim, val);
165 }
166
167
168 int
pc_point_set_x(PCPOINT * pt,double val)169 pc_point_set_x(PCPOINT *pt, double val)
170 {
171 return pc_point_set_double(pt, pt->schema->xdim, val);
172 }
173
174 int
pc_point_set_y(PCPOINT * pt,double val)175 pc_point_set_y(PCPOINT *pt, double val)
176 {
177 return pc_point_set_double(pt, pt->schema->ydim, val);
178 }
179
180 int
pc_point_set_z(PCPOINT * pt,double val)181 pc_point_set_z(PCPOINT *pt, double val)
182 {
183 return pc_point_set_double(pt, pt->schema->zdim, val);
184 }
185
186 int
pc_point_set_m(PCPOINT * pt,double val)187 pc_point_set_m(PCPOINT *pt, double val)
188 {
189 return pc_point_set_double(pt, pt->schema->mdim, val);
190 }
191
192 char *
pc_point_to_string(const PCPOINT * pt)193 pc_point_to_string(const PCPOINT *pt)
194 {
195 /* { "pcid":1, "values":[<dim1>, <dim2>, <dim3>, <dim4>] }*/
196 stringbuffer_t *sb = stringbuffer_create();
197 char *str;
198 int i;
199
200 stringbuffer_aprintf(sb, "{\"pcid\":%d,\"pt\":[", pt->schema->pcid);
201 for ( i = 0; i < pt->schema->ndims; i++ )
202 {
203 double d;
204 if ( ! pc_point_get_double_by_index(pt, i, &d) )
205 {
206 pcerror("pc_point_to_string: unable to read double at position %d", i);
207 }
208 if ( i )
209 {
210 stringbuffer_append(sb, ",");
211 }
212 stringbuffer_aprintf(sb, "%g", d);
213 }
214 stringbuffer_append(sb, "]}");
215 str = stringbuffer_getstringcopy(sb);
216 stringbuffer_destroy(sb);
217 return str;
218 }
219
220
221 PCPOINT *
pc_point_from_double_array(const PCSCHEMA * s,double * array,uint32_t offset,uint32_t stride)222 pc_point_from_double_array(const PCSCHEMA *s, double *array, uint32_t offset, uint32_t stride)
223 {
224 int i;
225 PCPOINT *pt;
226
227 if ( ! s )
228 {
229 pcerror("null schema passed into pc_point_from_double_array");
230 return NULL;
231 }
232
233 if ( stride != s->ndims )
234 {
235 pcerror("number of elements in schema and array do not match in pc_point_from_double_array");
236 return NULL;
237 }
238
239 /* Reference the external data */
240 pt = pcalloc(sizeof(PCPOINT));
241 pt->data = pcalloc(s->size);
242 pt->schema = s;
243 pt->readonly = PC_FALSE;
244
245 for ( i = 0; i < stride; i++ )
246 {
247 if ( PC_FAILURE == pc_point_set_double_by_index(pt, i, array[offset + i]) )
248 {
249 pcerror("failed to write value into dimension %d in pc_point_from_double_array", i);
250 return NULL;
251 }
252 }
253
254 return pt;
255 }
256
257 PCPOINT *
pc_point_from_wkb(const PCSCHEMA * schema,uint8_t * wkb,size_t wkblen)258 pc_point_from_wkb(const PCSCHEMA *schema, uint8_t *wkb, size_t wkblen)
259 {
260 /*
261 byte: endianness (1 = NDR, 0 = XDR)
262 uint32: pcid (key to POINTCLOUD_SCHEMAS)
263 uchar[]: data (interpret relative to pcid)
264 */
265 const size_t hdrsz = 1+4; /* endian + pcid */
266 uint8_t wkb_endian;
267 uint8_t *data;
268 PCPOINT *pt;
269
270 if ( ! wkblen )
271 {
272 pcerror("pc_point_from_wkb: zero length wkb");
273 }
274
275 wkb_endian = wkb[0];
276
277 if ( (wkblen-hdrsz) != schema->size )
278 {
279 pcerror("pc_point_from_wkb: wkb size inconsistent with schema size");
280 }
281
282 if ( wkb_endian != machine_endian() )
283 {
284 /* uncompressed_bytes_flip_endian creates a flipped copy */
285 data = uncompressed_bytes_flip_endian(wkb+hdrsz, schema, 1);
286 }
287 else
288 {
289 data = pcalloc(schema->size);
290 memcpy(data, wkb+hdrsz, wkblen-hdrsz);
291 }
292
293 pt = pc_point_from_data(schema, data);
294 pt->readonly = PC_FALSE;
295 return pt;
296 }
297
298 uint8_t *
pc_point_to_wkb(const PCPOINT * pt,size_t * wkbsize)299 pc_point_to_wkb(const PCPOINT *pt, size_t *wkbsize)
300 {
301 /*
302 byte: endianness (1 = NDR, 0 = XDR)
303 uint32: pcid (key to POINTCLOUD_SCHEMAS)
304 uchar[]: data (interpret relative to pcid)
305 */
306 char endian = machine_endian();
307 size_t size = 1 + 4 + pt->schema->size;
308 uint8_t *wkb = pcalloc(size);
309 wkb[0] = endian; /* Write endian flag */
310 memcpy(wkb + 1, &(pt->schema->pcid), 4); /* Write PCID */
311 memcpy(wkb + 5, pt->data, pt->schema->size); /* Write data */
312 if ( wkbsize ) *wkbsize = size;
313 return wkb;
314 }
315
316 uint8_t *
pc_point_to_geometry_wkb(const PCPOINT * pt,size_t * wkbsize)317 pc_point_to_geometry_wkb(const PCPOINT *pt, size_t *wkbsize)
318 {
319 static uint32_t srid_mask = 0x20000000;
320 static uint32_t m_mask = 0x40000000;
321 static uint32_t z_mask = 0x80000000;
322 uint32_t wkbtype = 1; /* WKB POINT */
323 size_t size = 1 + 4 + 8 + 8; /* endian + type + dblX, + dblY */
324 uint8_t *wkb, *ptr;
325 uint32_t srid = pt->schema->srid;
326 double x, y, z, m;
327 int has_x = pc_point_get_x(pt, &x) == PC_SUCCESS;
328 int has_y = pc_point_get_y(pt, &y) == PC_SUCCESS;
329 int has_z = pc_point_get_z(pt, &z) == PC_SUCCESS;
330 int has_m = pc_point_get_m(pt, &m) == PC_SUCCESS;
331
332 if ( ! ( has_x && has_y ) )
333 return NULL;
334
335 if ( srid )
336 {
337 wkbtype |= srid_mask;
338 size += 4;
339 }
340
341 if ( has_z )
342 {
343 wkbtype |= z_mask;
344 size += 8;
345 }
346
347 if ( has_m )
348 {
349 wkbtype |= m_mask;
350 size += 8;
351 }
352
353 wkb = pcalloc(size);
354 ptr = wkb;
355
356 ptr[0] = machine_endian(); /* Endian flag */
357 ptr += 1;
358
359 memcpy(ptr, &wkbtype, 4); /* WKB type */
360 ptr += 4;
361
362 if ( srid != 0 )
363 {
364 memcpy(ptr, &srid, 4); /* SRID */
365 ptr += 4;
366 }
367
368 memcpy(ptr, &x, 8); /* X */
369 ptr += 8;
370
371 memcpy(ptr, &y, 8); /* Y */
372 ptr += 8;
373
374 if ( has_z )
375 {
376 memcpy(ptr, &z, 8); /* Z */
377 ptr += 8;
378 }
379
380 if ( has_m )
381 {
382 memcpy(ptr, &m, 8); /* M */
383 ptr += 8;
384 }
385
386 if ( wkbsize ) *wkbsize = size;
387 return wkb;
388 }
389
390
391
392
393 /**
394 * @brief this function convert a PCPOINT to an array of double containing
395 * all the dimension values of this point
396 *
397 * @param a pointer to the point to convert to double
398 *
399 * @return a pointer to an array of double containing all the dimensions
400 * of the point expressed as double precision
401 *
402 */
pc_point_to_double_array(const PCPOINT * p)403 double * pc_point_to_double_array(const PCPOINT *p)
404 {
405 int i;
406 double *a = (double *) pcalloc( p->schema->ndims * sizeof(double) );
407
408 for(i=0; i<p->schema->ndims; ++i)
409 {
410 pc_point_get_double_by_index(p, i, &(a[i]));
411 }
412
413 return a;
414 }
415