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