1 /**********************************************************************
2 *
3 * PostGIS - Spatial Types for PostgreSQL
4 * http://postgis.net
5 *
6 * PostGIS is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * PostGIS is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
18 *
19 **********************************************************************
20 *
21 * Copyright 2009 Paul Ramsey <pramsey@cleverelephant.ca>
22 * Copyright 2017 Darafei Praliaskouski <me@komzpa.net>
23 *
24 **********************************************************************/
25
26
27 #include "liblwgeom_internal.h"
28 #include "lwgeom_log.h"
29 #include "lwgeodetic.h"
30 #include "gserialized1.h"
31
32 /***********************************************************************
33 * GSERIALIZED metadata utility functions.
34 */
35
36 static int gserialized1_read_gbox_p(const GSERIALIZED *g, GBOX *gbox);
37
38
gserialized1_get_lwflags(const GSERIALIZED * g)39 lwflags_t gserialized1_get_lwflags(const GSERIALIZED *g)
40 {
41 lwflags_t lwflags = 0;
42 uint8_t gflags = g->gflags;
43 FLAGS_SET_Z(lwflags, G1FLAGS_GET_Z(gflags));
44 FLAGS_SET_M(lwflags, G1FLAGS_GET_M(gflags));
45 FLAGS_SET_BBOX(lwflags, G1FLAGS_GET_BBOX(gflags));
46 FLAGS_SET_GEODETIC(lwflags, G1FLAGS_GET_GEODETIC(gflags));
47 FLAGS_SET_SOLID(lwflags, G1FLAGS_GET_SOLID(gflags));
48 return lwflags;
49 }
50
lwflags_get_g1flags(lwflags_t lwflags)51 uint8_t lwflags_get_g1flags(lwflags_t lwflags)
52 {
53 uint8_t gflags = 0;
54 G1FLAGS_SET_Z(gflags, FLAGS_GET_Z(lwflags));
55 G1FLAGS_SET_M(gflags, FLAGS_GET_M(lwflags));
56 G1FLAGS_SET_BBOX(gflags, FLAGS_GET_BBOX(lwflags));
57 G1FLAGS_SET_GEODETIC(gflags, FLAGS_GET_GEODETIC(lwflags));
58 G1FLAGS_SET_SOLID(gflags, FLAGS_GET_SOLID(lwflags));
59 return gflags;
60 }
61
62
gserialized1_box_size(const GSERIALIZED * g)63 static size_t gserialized1_box_size(const GSERIALIZED *g)
64 {
65 if (G1FLAGS_GET_GEODETIC(g->gflags))
66 return 6 * sizeof(float);
67 else
68 return 2 * G1FLAGS_NDIMS(g->gflags) * sizeof(float);
69 }
70
71 /* handle missaligned uint32_t data */
gserialized1_get_uint32_t(const uint8_t * loc)72 static inline uint32_t gserialized1_get_uint32_t(const uint8_t *loc)
73 {
74 return *((uint32_t*)loc);
75 }
76
g1flags(int has_z,int has_m,int is_geodetic)77 uint8_t g1flags(int has_z, int has_m, int is_geodetic)
78 {
79 uint8_t gflags = 0;
80 if (has_z)
81 G1FLAGS_SET_Z(gflags, 1);
82 if (has_m)
83 G1FLAGS_SET_M(gflags, 1);
84 if (is_geodetic)
85 G1FLAGS_SET_GEODETIC(gflags, 1);
86 return gflags;
87 }
88
gserialized1_has_bbox(const GSERIALIZED * gser)89 int gserialized1_has_bbox(const GSERIALIZED *gser)
90 {
91 return G1FLAGS_GET_BBOX(gser->gflags);
92 }
93
gserialized1_has_z(const GSERIALIZED * gser)94 int gserialized1_has_z(const GSERIALIZED *gser)
95 {
96 return G1FLAGS_GET_Z(gser->gflags);
97 }
98
gserialized1_has_m(const GSERIALIZED * gser)99 int gserialized1_has_m(const GSERIALIZED *gser)
100 {
101 return G1FLAGS_GET_M(gser->gflags);
102 }
103
gserialized1_ndims(const GSERIALIZED * gser)104 int gserialized1_ndims(const GSERIALIZED *gser)
105 {
106 return G1FLAGS_NDIMS(gser->gflags);
107 }
108
gserialized1_is_geodetic(const GSERIALIZED * gser)109 int gserialized1_is_geodetic(const GSERIALIZED *gser)
110 {
111 return G1FLAGS_GET_GEODETIC(gser->gflags);
112 }
113
gserialized1_max_header_size(void)114 uint32_t gserialized1_max_header_size(void)
115 {
116 static const intptr_t size_of_gserialized_up_to_data = (intptr_t) & ((GSERIALIZED *)NULL)->data;
117 /* GSERIALIZED size + max bbox according gbox_serialized_size (XYZM*2) + extended flags + type */
118 return size_of_gserialized_up_to_data + 8 * sizeof(float) + sizeof(uint32_t);
119 }
120
gserialized1_header_size(const GSERIALIZED * gser)121 static uint32_t gserialized1_header_size(const GSERIALIZED *gser)
122 {
123 uint32_t sz = 8; /* varsize (4) + srid(3) + flags (1) */
124
125 if (gserialized1_has_bbox(gser))
126 sz += gserialized1_box_size(gser);
127
128 return sz;
129 }
130
gserialized1_get_type(const GSERIALIZED * g)131 uint32_t gserialized1_get_type(const GSERIALIZED *g)
132 {
133 uint32_t *ptr;
134 ptr = (uint32_t*)(g->data);
135 if ( G1FLAGS_GET_BBOX(g->gflags) )
136 {
137 ptr += (gserialized1_box_size(g) / sizeof(uint32_t));
138 }
139 return *ptr;
140 }
141
gserialized1_get_srid(const GSERIALIZED * s)142 int32_t gserialized1_get_srid(const GSERIALIZED *s)
143 {
144 int32_t srid = 0;
145 srid = srid | (s->srid[0] << 16);
146 srid = srid | (s->srid[1] << 8);
147 srid = srid | s->srid[2];
148 /* Only the first 21 bits are set. Slide up and back to pull
149 the negative bits down, if we need them. */
150 srid = (srid<<11)>>11;
151
152 /* 0 is our internal unknown value. We'll map back and forth here for now */
153 if ( srid == 0 )
154 return SRID_UNKNOWN;
155 else
156 return srid;
157 }
158
gserialized1_set_srid(GSERIALIZED * s,int32_t srid)159 void gserialized1_set_srid(GSERIALIZED *s, int32_t srid)
160 {
161 LWDEBUGF(3, "%s called with srid = %d", __func__, srid);
162
163 srid = clamp_srid(srid);
164
165 /* 0 is our internal unknown value.
166 * We'll map back and forth here for now */
167 if ( srid == SRID_UNKNOWN )
168 srid = 0;
169
170 s->srid[0] = (srid & 0x001F0000) >> 16;
171 s->srid[1] = (srid & 0x0000FF00) >> 8;
172 s->srid[2] = (srid & 0x000000FF);
173 }
174
175 static size_t gserialized1_is_empty_recurse(const uint8_t *p, int *isempty);
gserialized1_is_empty_recurse(const uint8_t * p,int * isempty)176 static size_t gserialized1_is_empty_recurse(const uint8_t *p, int *isempty)
177 {
178 int i;
179 int32_t type, num;
180
181 memcpy(&type, p, 4);
182 memcpy(&num, p+4, 4);
183
184 if ( lwtype_is_collection(type) )
185 {
186 size_t lz = 8;
187 for ( i = 0; i < num; i++ )
188 {
189 lz += gserialized1_is_empty_recurse(p+lz, isempty);
190 if ( ! *isempty )
191 return lz;
192 }
193 *isempty = LW_TRUE;
194 return lz;
195 }
196 else
197 {
198 *isempty = (num == 0 ? LW_TRUE : LW_FALSE);
199 return 8;
200 }
201 }
202
gserialized1_is_empty(const GSERIALIZED * g)203 int gserialized1_is_empty(const GSERIALIZED *g)
204 {
205 uint8_t *p = (uint8_t*)g;
206 int isempty = 0;
207 assert(g);
208
209 p += 8; /* Skip varhdr and srid/flags */
210 if(gserialized1_has_bbox(g))
211 p += gserialized1_box_size(g); /* Skip the box */
212
213 gserialized1_is_empty_recurse(p, &isempty);
214 return isempty;
215 }
216
217
218 /* Prototype for lookup3.c */
219 /* key = the key to hash */
220 /* length = length of the key */
221 /* pc = IN: primary initval, OUT: primary hash */
222 /* pb = IN: secondary initval, OUT: secondary hash */
223 void hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t *pb);
224
225 int32_t
gserialized1_hash(const GSERIALIZED * g1)226 gserialized1_hash(const GSERIALIZED *g1)
227 {
228 int32_t hval;
229 int32_t pb = 0, pc = 0;
230 /* Point to just the type/coordinate part of buffer */
231 size_t hsz1 = gserialized1_header_size(g1);
232 uint8_t *b1 = (uint8_t*)g1 + hsz1;
233 /* Calculate size of type/coordinate buffer */
234 size_t sz1 = SIZE_GET(g1->size);
235 size_t bsz1 = sz1 - hsz1;
236 /* Calculate size of srid/type/coordinate buffer */
237 int32_t srid = gserialized1_get_srid(g1);
238 size_t bsz2 = bsz1 + sizeof(int);
239 uint8_t *b2 = lwalloc(bsz2);
240 /* Copy srid into front of combined buffer */
241 memcpy(b2, &srid, sizeof(int));
242 /* Copy type/coordinates into rest of combined buffer */
243 memcpy(b2+sizeof(int), b1, bsz1);
244 /* Hash combined buffer */
245 hashlittle2(b2, bsz2, (uint32_t *)&pb, (uint32_t *)&pc);
246 lwfree(b2);
247 hval = pb ^ pc;
248 return hval;
249 }
250
gserialized1_read_gbox_p(const GSERIALIZED * g,GBOX * gbox)251 int gserialized1_read_gbox_p(const GSERIALIZED *g, GBOX *gbox)
252 {
253
254 /* Null input! */
255 if ( ! ( g && gbox ) ) return LW_FAILURE;
256
257 /* Initialize the flags on the box */
258 gbox->flags = gserialized1_get_lwflags(g);
259
260 /* Has pre-calculated box */
261 if ( G1FLAGS_GET_BBOX(g->gflags) )
262 {
263 int i = 0;
264 float *fbox = (float*)(g->data);
265 gbox->xmin = fbox[i++];
266 gbox->xmax = fbox[i++];
267 gbox->ymin = fbox[i++];
268 gbox->ymax = fbox[i++];
269
270 /* Geodetic? Read next dimension (geocentric Z) and return */
271 if ( G1FLAGS_GET_GEODETIC(g->gflags) )
272 {
273 gbox->zmin = fbox[i++];
274 gbox->zmax = fbox[i++];
275 return LW_SUCCESS;
276 }
277 /* Cartesian? Read extra dimensions (if there) and return */
278 if ( G1FLAGS_GET_Z(g->gflags) )
279 {
280 gbox->zmin = fbox[i++];
281 gbox->zmax = fbox[i++];
282 }
283 if ( G1FLAGS_GET_M(g->gflags) )
284 {
285 gbox->mmin = fbox[i++];
286 gbox->mmax = fbox[i++];
287 }
288 return LW_SUCCESS;
289 }
290 return LW_FAILURE;
291 }
292
293 /*
294 * Populate a bounding box *without* allocating an LWGEOM. Useful
295 * for some performance purposes.
296 */
297 int
gserialized1_peek_gbox_p(const GSERIALIZED * g,GBOX * gbox)298 gserialized1_peek_gbox_p(const GSERIALIZED *g, GBOX *gbox)
299 {
300 uint32_t type = gserialized1_get_type(g);
301
302 /* Peeking doesn't help if you already have a box or are geodetic */
303 if ( G1FLAGS_GET_GEODETIC(g->gflags) || G1FLAGS_GET_BBOX(g->gflags) )
304 {
305 return LW_FAILURE;
306 }
307
308 /* Boxes of points are easy peasy */
309 if ( type == POINTTYPE )
310 {
311 int i = 1; /* Start past <pointtype><padding> */
312 double *dptr = (double*)(g->data);
313
314 /* Read the empty flag */
315 int32_t *iptr = (int32_t *)(g->data);
316 int isempty = (iptr[1] == 0);
317
318 /* EMPTY point has no box */
319 if ( isempty ) return LW_FAILURE;
320
321 gbox->xmin = gbox->xmax = dptr[i++];
322 gbox->ymin = gbox->ymax = dptr[i++];
323 gbox->flags = gserialized1_get_lwflags(g);
324 if ( G1FLAGS_GET_Z(g->gflags) )
325 {
326 gbox->zmin = gbox->zmax = dptr[i++];
327 }
328 if ( G1FLAGS_GET_M(g->gflags) )
329 {
330 gbox->mmin = gbox->mmax = dptr[i++];
331 }
332 gbox_float_round(gbox);
333 return LW_SUCCESS;
334 }
335 /* We can calculate the box of a two-point cartesian line trivially */
336 else if ( type == LINETYPE )
337 {
338 int ndims = G1FLAGS_NDIMS(g->gflags);
339 int i = 0; /* Start at <linetype><npoints> */
340 double *dptr = (double*)(g->data);
341 int32_t *iptr = (int32_t *)(g->data);
342 int npoints = iptr[1]; /* Read the npoints */
343
344 /* This only works with 2-point lines */
345 if ( npoints != 2 )
346 return LW_FAILURE;
347
348 /* Advance to X */
349 /* Past <linetype><npoints> */
350 i++;
351 gbox->xmin = FP_MIN(dptr[i], dptr[i+ndims]);
352 gbox->xmax = FP_MAX(dptr[i], dptr[i+ndims]);
353
354 /* Advance to Y */
355 i++;
356 gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]);
357 gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]);
358
359 gbox->flags = gserialized1_get_lwflags(g);
360 if ( G1FLAGS_GET_Z(g->gflags) )
361 {
362 /* Advance to Z */
363 i++;
364 gbox->zmin = FP_MIN(dptr[i], dptr[i+ndims]);
365 gbox->zmax = FP_MAX(dptr[i], dptr[i+ndims]);
366 }
367 if ( G1FLAGS_GET_M(g->gflags) )
368 {
369 /* Advance to M */
370 i++;
371 gbox->mmin = FP_MIN(dptr[i], dptr[i+ndims]);
372 gbox->mmax = FP_MAX(dptr[i], dptr[i+ndims]);
373 }
374 gbox_float_round(gbox);
375 return LW_SUCCESS;
376 }
377 /* We can also do single-entry multi-points */
378 else if ( type == MULTIPOINTTYPE )
379 {
380 int i = 0; /* Start at <multipointtype><ngeoms> */
381 double *dptr = (double*)(g->data);
382 int32_t *iptr = (int32_t *)(g->data);
383 int ngeoms = iptr[1]; /* Read the ngeoms */
384 int npoints;
385
386 /* This only works with single-entry multipoints */
387 if ( ngeoms != 1 )
388 return LW_FAILURE;
389
390 /* Npoints is at <multipointtype><ngeoms><pointtype><npoints> */
391 npoints = iptr[3];
392
393 /* The check below is necessary because we can have a MULTIPOINT
394 * that contains a single, empty POINT (ngeoms = 1, npoints = 0) */
395 if ( npoints != 1 )
396 return LW_FAILURE;
397
398 /* Move forward two doubles (four ints) */
399 /* Past <multipointtype><ngeoms> */
400 /* Past <pointtype><npoints> */
401 i += 2;
402
403 /* Read the doubles from the one point */
404 gbox->xmin = gbox->xmax = dptr[i++];
405 gbox->ymin = gbox->ymax = dptr[i++];
406 gbox->flags = gserialized1_get_lwflags(g);
407 if ( G1FLAGS_GET_Z(g->gflags) )
408 {
409 gbox->zmin = gbox->zmax = dptr[i++];
410 }
411 if ( G1FLAGS_GET_M(g->gflags) )
412 {
413 gbox->mmin = gbox->mmax = dptr[i++];
414 }
415 gbox_float_round(gbox);
416 return LW_SUCCESS;
417 }
418 /* And we can do single-entry multi-lines with two vertices (!!!) */
419 else if ( type == MULTILINETYPE )
420 {
421 int ndims = G1FLAGS_NDIMS(g->gflags);
422 int i = 0; /* Start at <multilinetype><ngeoms> */
423 double *dptr = (double*)(g->data);
424 int32_t *iptr = (int32_t *)(g->data);
425 int ngeoms = iptr[1]; /* Read the ngeoms */
426 int npoints;
427
428 /* This only works with 1-line multilines */
429 if ( ngeoms != 1 )
430 return LW_FAILURE;
431
432 /* Npoints is at <multilinetype><ngeoms><linetype><npoints> */
433 npoints = iptr[3];
434
435 if ( npoints != 2 )
436 return LW_FAILURE;
437
438 /* Advance to X */
439 /* Move forward two doubles (four ints) */
440 /* Past <multilinetype><ngeoms> */
441 /* Past <linetype><npoints> */
442 i += 2;
443 gbox->xmin = FP_MIN(dptr[i], dptr[i+ndims]);
444 gbox->xmax = FP_MAX(dptr[i], dptr[i+ndims]);
445
446 /* Advance to Y */
447 i++;
448 gbox->ymin = FP_MIN(dptr[i], dptr[i+ndims]);
449 gbox->ymax = FP_MAX(dptr[i], dptr[i+ndims]);
450
451 gbox->flags = gserialized1_get_lwflags(g);
452 if ( G1FLAGS_GET_Z(g->gflags) )
453 {
454 /* Advance to Z */
455 i++;
456 gbox->zmin = FP_MIN(dptr[i], dptr[i+ndims]);
457 gbox->zmax = FP_MAX(dptr[i], dptr[i+ndims]);
458 }
459 if ( G1FLAGS_GET_M(g->gflags) )
460 {
461 /* Advance to M */
462 i++;
463 gbox->mmin = FP_MIN(dptr[i], dptr[i+ndims]);
464 gbox->mmax = FP_MAX(dptr[i], dptr[i+ndims]);
465 }
466 gbox_float_round(gbox);
467 return LW_SUCCESS;
468 }
469
470 return LW_FAILURE;
471 }
472
473 static inline void
gserialized1_copy_point(double * dptr,lwflags_t flags,POINT4D * out_point)474 gserialized1_copy_point(double *dptr, lwflags_t flags, POINT4D *out_point)
475 {
476 uint8_t dim = 0;
477 out_point->x = dptr[dim++];
478 out_point->y = dptr[dim++];
479
480 if (G1FLAGS_GET_Z(flags))
481 {
482 out_point->z = dptr[dim++];
483 }
484 if (G1FLAGS_GET_M(flags))
485 {
486 out_point->m = dptr[dim];
487 }
488 }
489
490 int
gserialized1_peek_first_point(const GSERIALIZED * g,POINT4D * out_point)491 gserialized1_peek_first_point(const GSERIALIZED *g, POINT4D *out_point)
492 {
493 uint8_t *geometry_start = ((uint8_t *)g->data);
494 if (gserialized1_has_bbox(g))
495 {
496 geometry_start += gserialized1_box_size(g);
497 }
498
499 uint32_t isEmpty = (((uint32_t *)geometry_start)[1]) == 0;
500 if (isEmpty)
501 {
502 return LW_FAILURE;
503 }
504
505 uint32_t type = (((uint32_t *)geometry_start)[0]);
506 /* Setup double_array_start depending on the geometry type */
507 double *double_array_start = NULL;
508 switch (type)
509 {
510 case (POINTTYPE):
511 /* For points we only need to jump over the type and npoints 32b ints */
512 double_array_start = (double *)(geometry_start + 2 * sizeof(uint32_t));
513 break;
514
515 default:
516 lwerror("%s is currently not implemented for type %d", __func__, type);
517 return LW_FAILURE;
518 }
519
520 gserialized1_copy_point(double_array_start, g->gflags, out_point);
521 return LW_SUCCESS;
522 }
523
524 /**
525 * Read the bounding box off a serialization and calculate one if
526 * it is not already there.
527 */
gserialized1_get_gbox_p(const GSERIALIZED * g,GBOX * box)528 int gserialized1_get_gbox_p(const GSERIALIZED *g, GBOX *box)
529 {
530 /* Try to just read the serialized box. */
531 if ( gserialized1_read_gbox_p(g, box) == LW_SUCCESS )
532 {
533 return LW_SUCCESS;
534 }
535 /* No box? Try to peek into simpler geometries and */
536 /* derive a box without creating an lwgeom */
537 else if ( gserialized1_peek_gbox_p(g, box) == LW_SUCCESS )
538 {
539 return LW_SUCCESS;
540 }
541 /* Damn! Nothing for it but to create an lwgeom... */
542 /* See http://trac.osgeo.org/postgis/ticket/1023 */
543 else
544 {
545 LWGEOM *lwgeom = lwgeom_from_gserialized(g);
546 int ret = lwgeom_calculate_gbox(lwgeom, box);
547 gbox_float_round(box);
548 lwgeom_free(lwgeom);
549 return ret;
550 }
551 }
552
553 /**
554 * Read the bounding box off a serialization and fail if
555 * it is not already there.
556 */
gserialized1_fast_gbox_p(const GSERIALIZED * g,GBOX * box)557 int gserialized1_fast_gbox_p(const GSERIALIZED *g, GBOX *box)
558 {
559 /* Try to just read the serialized box. */
560 if ( gserialized1_read_gbox_p(g, box) == LW_SUCCESS )
561 {
562 return LW_SUCCESS;
563 }
564 /* No box? Try to peek into simpler geometries and */
565 /* derive a box without creating an lwgeom */
566 else if ( gserialized1_peek_gbox_p(g, box) == LW_SUCCESS )
567 {
568 return LW_SUCCESS;
569 }
570 else
571 {
572 return LW_FAILURE;
573 }
574 }
575
576
577
578
579 /***********************************************************************
580 * Calculate the GSERIALIZED size for an LWGEOM.
581 */
582
583 /* Private functions */
584
585 static size_t gserialized1_from_any_size(const LWGEOM *geom); /* Local prototype */
586
gserialized1_from_lwpoint_size(const LWPOINT * point)587 static size_t gserialized1_from_lwpoint_size(const LWPOINT *point)
588 {
589 size_t size = 4; /* Type number. */
590
591 assert(point);
592
593 size += 4; /* Number of points (one or zero (empty)). */
594 size += point->point->npoints * FLAGS_NDIMS(point->flags) * sizeof(double);
595
596 LWDEBUGF(3, "point size = %d", size);
597
598 return size;
599 }
600
gserialized1_from_lwline_size(const LWLINE * line)601 static size_t gserialized1_from_lwline_size(const LWLINE *line)
602 {
603 size_t size = 4; /* Type number. */
604
605 assert(line);
606
607 size += 4; /* Number of points (zero => empty). */
608 size += line->points->npoints * FLAGS_NDIMS(line->flags) * sizeof(double);
609
610 LWDEBUGF(3, "linestring size = %d", size);
611
612 return size;
613 }
614
gserialized1_from_lwtriangle_size(const LWTRIANGLE * triangle)615 static size_t gserialized1_from_lwtriangle_size(const LWTRIANGLE *triangle)
616 {
617 size_t size = 4; /* Type number. */
618
619 assert(triangle);
620
621 size += 4; /* Number of points (zero => empty). */
622 size += triangle->points->npoints * FLAGS_NDIMS(triangle->flags) * sizeof(double);
623
624 LWDEBUGF(3, "triangle size = %d", size);
625
626 return size;
627 }
628
gserialized1_from_lwpoly_size(const LWPOLY * poly)629 static size_t gserialized1_from_lwpoly_size(const LWPOLY *poly)
630 {
631 size_t size = 4; /* Type number. */
632 uint32_t i = 0;
633
634 assert(poly);
635
636 size += 4; /* Number of rings (zero => empty). */
637 if ( poly->nrings % 2 )
638 size += 4; /* Padding to double alignment. */
639
640 for ( i = 0; i < poly->nrings; i++ )
641 {
642 size += 4; /* Number of points in ring. */
643 size += poly->rings[i]->npoints * FLAGS_NDIMS(poly->flags) * sizeof(double);
644 }
645
646 LWDEBUGF(3, "polygon size = %d", size);
647
648 return size;
649 }
650
gserialized1_from_lwcircstring_size(const LWCIRCSTRING * curve)651 static size_t gserialized1_from_lwcircstring_size(const LWCIRCSTRING *curve)
652 {
653 size_t size = 4; /* Type number. */
654
655 assert(curve);
656
657 size += 4; /* Number of points (zero => empty). */
658 size += curve->points->npoints * FLAGS_NDIMS(curve->flags) * sizeof(double);
659
660 LWDEBUGF(3, "circstring size = %d", size);
661
662 return size;
663 }
664
gserialized1_from_lwcollection_size(const LWCOLLECTION * col)665 static size_t gserialized1_from_lwcollection_size(const LWCOLLECTION *col)
666 {
667 size_t size = 4; /* Type number. */
668 uint32_t i = 0;
669
670 assert(col);
671
672 size += 4; /* Number of sub-geometries (zero => empty). */
673
674 for ( i = 0; i < col->ngeoms; i++ )
675 {
676 size_t subsize = gserialized1_from_any_size(col->geoms[i]);
677 size += subsize;
678 LWDEBUGF(3, "lwcollection subgeom(%d) size = %d", i, subsize);
679 }
680
681 LWDEBUGF(3, "lwcollection size = %d", size);
682
683 return size;
684 }
685
gserialized1_from_any_size(const LWGEOM * geom)686 static size_t gserialized1_from_any_size(const LWGEOM *geom)
687 {
688 LWDEBUGF(2, "Input type: %s", lwtype_name(geom->type));
689
690 switch (geom->type)
691 {
692 case POINTTYPE:
693 return gserialized1_from_lwpoint_size((LWPOINT *)geom);
694 case LINETYPE:
695 return gserialized1_from_lwline_size((LWLINE *)geom);
696 case POLYGONTYPE:
697 return gserialized1_from_lwpoly_size((LWPOLY *)geom);
698 case TRIANGLETYPE:
699 return gserialized1_from_lwtriangle_size((LWTRIANGLE *)geom);
700 case CIRCSTRINGTYPE:
701 return gserialized1_from_lwcircstring_size((LWCIRCSTRING *)geom);
702 case CURVEPOLYTYPE:
703 case COMPOUNDTYPE:
704 case MULTIPOINTTYPE:
705 case MULTILINETYPE:
706 case MULTICURVETYPE:
707 case MULTIPOLYGONTYPE:
708 case MULTISURFACETYPE:
709 case POLYHEDRALSURFACETYPE:
710 case TINTYPE:
711 case COLLECTIONTYPE:
712 return gserialized1_from_lwcollection_size((LWCOLLECTION *)geom);
713 default:
714 lwerror("Unknown geometry type: %d - %s", geom->type, lwtype_name(geom->type));
715 return 0;
716 }
717 }
718
719 /* Public function */
720
gserialized1_from_lwgeom_size(const LWGEOM * geom)721 size_t gserialized1_from_lwgeom_size(const LWGEOM *geom)
722 {
723 size_t size = 8; /* Header overhead. */
724 assert(geom);
725
726 if (geom->bbox)
727 size += gbox_serialized_size(geom->flags);
728
729 size += gserialized1_from_any_size(geom);
730 LWDEBUGF(3, "%s size = %d", __func__, size);
731
732 return size;
733 }
734
735 /***********************************************************************
736 * Serialize an LWGEOM into GSERIALIZED.
737 */
738
739 /* Private functions */
740
741 static size_t gserialized1_from_lwgeom_any(const LWGEOM *geom, uint8_t *buf);
742
gserialized1_from_lwpoint(const LWPOINT * point,uint8_t * buf)743 static size_t gserialized1_from_lwpoint(const LWPOINT *point, uint8_t *buf)
744 {
745 uint8_t *loc;
746 int ptsize = ptarray_point_size(point->point);
747 int type = POINTTYPE;
748
749 assert(point);
750 assert(buf);
751
752 if ( FLAGS_GET_ZM(point->flags) != FLAGS_GET_ZM(point->point->flags) )
753 lwerror("Dimensions mismatch in lwpoint");
754
755 LWDEBUGF(2, "%s (%p, %p) called", __func__, point, buf);
756
757 loc = buf;
758
759 /* Write in the type. */
760 memcpy(loc, &type, sizeof(uint32_t));
761 loc += sizeof(uint32_t);
762 /* Write in the number of points (0 => empty). */
763 memcpy(loc, &(point->point->npoints), sizeof(uint32_t));
764 loc += sizeof(uint32_t);
765
766 /* Copy in the ordinates. */
767 if ( point->point->npoints > 0 )
768 {
769 memcpy(loc, getPoint_internal(point->point, 0), ptsize);
770 loc += ptsize;
771 }
772
773 return (size_t)(loc - buf);
774 }
775
gserialized1_from_lwline(const LWLINE * line,uint8_t * buf)776 static size_t gserialized1_from_lwline(const LWLINE *line, uint8_t *buf)
777 {
778 uint8_t *loc;
779 int ptsize;
780 size_t size;
781 int type = LINETYPE;
782
783 assert(line);
784 assert(buf);
785
786 LWDEBUGF(2, "%s (%p, %p) called", __func__, line, buf);
787
788 if ( FLAGS_GET_Z(line->flags) != FLAGS_GET_Z(line->points->flags) )
789 lwerror("Dimensions mismatch in lwline");
790
791 ptsize = ptarray_point_size(line->points);
792
793 loc = buf;
794
795 /* Write in the type. */
796 memcpy(loc, &type, sizeof(uint32_t));
797 loc += sizeof(uint32_t);
798
799 /* Write in the npoints. */
800 memcpy(loc, &(line->points->npoints), sizeof(uint32_t));
801 loc += sizeof(uint32_t);
802
803 LWDEBUGF(3, "%s added npoints (%d)", __func__, line->points->npoints);
804
805 /* Copy in the ordinates. */
806 if ( line->points->npoints > 0 )
807 {
808 size = line->points->npoints * ptsize;
809 memcpy(loc, getPoint_internal(line->points, 0), size);
810 loc += size;
811 }
812 LWDEBUGF(3, "%s copied serialized_pointlist (%d bytes)", __func__, ptsize * line->points->npoints);
813
814 return (size_t)(loc - buf);
815 }
816
gserialized1_from_lwpoly(const LWPOLY * poly,uint8_t * buf)817 static size_t gserialized1_from_lwpoly(const LWPOLY *poly, uint8_t *buf)
818 {
819 uint32_t i;
820 uint8_t *loc;
821 int ptsize;
822 int type = POLYGONTYPE;
823
824 assert(poly);
825 assert(buf);
826
827 LWDEBUGF(2, "%s called", __func__);
828
829 ptsize = sizeof(double) * FLAGS_NDIMS(poly->flags);
830 loc = buf;
831
832 /* Write in the type. */
833 memcpy(loc, &type, sizeof(uint32_t));
834 loc += sizeof(uint32_t);
835
836 /* Write in the nrings. */
837 memcpy(loc, &(poly->nrings), sizeof(uint32_t));
838 loc += sizeof(uint32_t);
839
840 /* Write in the npoints per ring. */
841 for ( i = 0; i < poly->nrings; i++ )
842 {
843 memcpy(loc, &(poly->rings[i]->npoints), sizeof(uint32_t));
844 loc += sizeof(uint32_t);
845 }
846
847 /* Add in padding if necessary to remain double aligned. */
848 if ( poly->nrings % 2 )
849 {
850 memset(loc, 0, sizeof(uint32_t));
851 loc += sizeof(uint32_t);
852 }
853
854 /* Copy in the ordinates. */
855 for ( i = 0; i < poly->nrings; i++ )
856 {
857 POINTARRAY *pa = poly->rings[i];
858 size_t pasize;
859
860 if ( FLAGS_GET_ZM(poly->flags) != FLAGS_GET_ZM(pa->flags) )
861 lwerror("Dimensions mismatch in lwpoly");
862
863 pasize = pa->npoints * ptsize;
864 if ( pa->npoints > 0 )
865 memcpy(loc, getPoint_internal(pa, 0), pasize);
866 loc += pasize;
867 }
868 return (size_t)(loc - buf);
869 }
870
gserialized1_from_lwtriangle(const LWTRIANGLE * triangle,uint8_t * buf)871 static size_t gserialized1_from_lwtriangle(const LWTRIANGLE *triangle, uint8_t *buf)
872 {
873 uint8_t *loc;
874 int ptsize;
875 size_t size;
876 int type = TRIANGLETYPE;
877
878 assert(triangle);
879 assert(buf);
880
881 LWDEBUGF(2, "%s (%p, %p) called", __func__, triangle, buf);
882
883 if ( FLAGS_GET_ZM(triangle->flags) != FLAGS_GET_ZM(triangle->points->flags) )
884 lwerror("Dimensions mismatch in lwtriangle");
885
886 ptsize = ptarray_point_size(triangle->points);
887
888 loc = buf;
889
890 /* Write in the type. */
891 memcpy(loc, &type, sizeof(uint32_t));
892 loc += sizeof(uint32_t);
893
894 /* Write in the npoints. */
895 memcpy(loc, &(triangle->points->npoints), sizeof(uint32_t));
896 loc += sizeof(uint32_t);
897
898 LWDEBUGF(3, "%s added npoints (%d)", __func__, triangle->points->npoints);
899
900 /* Copy in the ordinates. */
901 if ( triangle->points->npoints > 0 )
902 {
903 size = triangle->points->npoints * ptsize;
904 memcpy(loc, getPoint_internal(triangle->points, 0), size);
905 loc += size;
906 }
907 LWDEBUGF(3, "%s copied serialized_pointlist (%d bytes)", __func__, ptsize * triangle->points->npoints);
908
909 return (size_t)(loc - buf);
910 }
911
gserialized1_from_lwcircstring(const LWCIRCSTRING * curve,uint8_t * buf)912 static size_t gserialized1_from_lwcircstring(const LWCIRCSTRING *curve, uint8_t *buf)
913 {
914 uint8_t *loc;
915 int ptsize;
916 size_t size;
917 int type = CIRCSTRINGTYPE;
918
919 assert(curve);
920 assert(buf);
921
922 if (FLAGS_GET_ZM(curve->flags) != FLAGS_GET_ZM(curve->points->flags))
923 lwerror("Dimensions mismatch in lwcircstring");
924
925
926 ptsize = ptarray_point_size(curve->points);
927 loc = buf;
928
929 /* Write in the type. */
930 memcpy(loc, &type, sizeof(uint32_t));
931 loc += sizeof(uint32_t);
932
933 /* Write in the npoints. */
934 memcpy(loc, &curve->points->npoints, sizeof(uint32_t));
935 loc += sizeof(uint32_t);
936
937 /* Copy in the ordinates. */
938 if ( curve->points->npoints > 0 )
939 {
940 size = curve->points->npoints * ptsize;
941 memcpy(loc, getPoint_internal(curve->points, 0), size);
942 loc += size;
943 }
944
945 return (size_t)(loc - buf);
946 }
947
gserialized1_from_lwcollection(const LWCOLLECTION * coll,uint8_t * buf)948 static size_t gserialized1_from_lwcollection(const LWCOLLECTION *coll, uint8_t *buf)
949 {
950 size_t subsize = 0;
951 uint8_t *loc;
952 uint32_t i;
953 int type;
954
955 assert(coll);
956 assert(buf);
957
958 type = coll->type;
959 loc = buf;
960
961 /* Write in the type. */
962 memcpy(loc, &type, sizeof(uint32_t));
963 loc += sizeof(uint32_t);
964
965 /* Write in the number of subgeoms. */
966 memcpy(loc, &coll->ngeoms, sizeof(uint32_t));
967 loc += sizeof(uint32_t);
968
969 /* Serialize subgeoms. */
970 for ( i=0; i<coll->ngeoms; i++ )
971 {
972 if (FLAGS_GET_ZM(coll->flags) != FLAGS_GET_ZM(coll->geoms[i]->flags))
973 lwerror("Dimensions mismatch in lwcollection");
974 subsize = gserialized1_from_lwgeom_any(coll->geoms[i], loc);
975 loc += subsize;
976 }
977
978 return (size_t)(loc - buf);
979 }
980
gserialized1_from_lwgeom_any(const LWGEOM * geom,uint8_t * buf)981 static size_t gserialized1_from_lwgeom_any(const LWGEOM *geom, uint8_t *buf)
982 {
983 assert(geom);
984 assert(buf);
985
986 LWDEBUGF(2, "Input type (%d) %s, hasz: %d hasm: %d",
987 geom->type, lwtype_name(geom->type),
988 FLAGS_GET_Z(geom->flags), FLAGS_GET_M(geom->flags));
989 LWDEBUGF(2, "LWGEOM(%p) uint8_t(%p)", geom, buf);
990
991 switch (geom->type)
992 {
993 case POINTTYPE:
994 return gserialized1_from_lwpoint((LWPOINT *)geom, buf);
995 case LINETYPE:
996 return gserialized1_from_lwline((LWLINE *)geom, buf);
997 case POLYGONTYPE:
998 return gserialized1_from_lwpoly((LWPOLY *)geom, buf);
999 case TRIANGLETYPE:
1000 return gserialized1_from_lwtriangle((LWTRIANGLE *)geom, buf);
1001 case CIRCSTRINGTYPE:
1002 return gserialized1_from_lwcircstring((LWCIRCSTRING *)geom, buf);
1003 case CURVEPOLYTYPE:
1004 case COMPOUNDTYPE:
1005 case MULTIPOINTTYPE:
1006 case MULTILINETYPE:
1007 case MULTICURVETYPE:
1008 case MULTIPOLYGONTYPE:
1009 case MULTISURFACETYPE:
1010 case POLYHEDRALSURFACETYPE:
1011 case TINTYPE:
1012 case COLLECTIONTYPE:
1013 return gserialized1_from_lwcollection((LWCOLLECTION *)geom, buf);
1014 default:
1015 lwerror("Unknown geometry type: %d - %s", geom->type, lwtype_name(geom->type));
1016 return 0;
1017 }
1018 return 0;
1019 }
1020
gserialized1_from_gbox(const GBOX * gbox,uint8_t * buf)1021 static size_t gserialized1_from_gbox(const GBOX *gbox, uint8_t *buf)
1022 {
1023 uint8_t *loc = buf;
1024 float f;
1025 size_t return_size;
1026
1027 assert(buf);
1028
1029 f = next_float_down(gbox->xmin);
1030 memcpy(loc, &f, sizeof(float));
1031 loc += sizeof(float);
1032
1033 f = next_float_up(gbox->xmax);
1034 memcpy(loc, &f, sizeof(float));
1035 loc += sizeof(float);
1036
1037 f = next_float_down(gbox->ymin);
1038 memcpy(loc, &f, sizeof(float));
1039 loc += sizeof(float);
1040
1041 f = next_float_up(gbox->ymax);
1042 memcpy(loc, &f, sizeof(float));
1043 loc += sizeof(float);
1044
1045 if ( FLAGS_GET_GEODETIC(gbox->flags) )
1046 {
1047 f = next_float_down(gbox->zmin);
1048 memcpy(loc, &f, sizeof(float));
1049 loc += sizeof(float);
1050
1051 f = next_float_up(gbox->zmax);
1052 memcpy(loc, &f, sizeof(float));
1053 loc += sizeof(float);
1054
1055 return_size = (size_t)(loc - buf);
1056 LWDEBUGF(4, "returning size %d", return_size);
1057 return return_size;
1058 }
1059
1060 if ( FLAGS_GET_Z(gbox->flags) )
1061 {
1062 f = next_float_down(gbox->zmin);
1063 memcpy(loc, &f, sizeof(float));
1064 loc += sizeof(float);
1065
1066 f = next_float_up(gbox->zmax);
1067 memcpy(loc, &f, sizeof(float));
1068 loc += sizeof(float);
1069
1070 }
1071
1072 if ( FLAGS_GET_M(gbox->flags) )
1073 {
1074 f = next_float_down(gbox->mmin);
1075 memcpy(loc, &f, sizeof(float));
1076 loc += sizeof(float);
1077
1078 f = next_float_up(gbox->mmax);
1079 memcpy(loc, &f, sizeof(float));
1080 loc += sizeof(float);
1081 }
1082 return_size = (size_t)(loc - buf);
1083 LWDEBUGF(4, "returning size %d", return_size);
1084 return return_size;
1085 }
1086
1087 /* Public function */
1088
gserialized1_from_lwgeom(LWGEOM * geom,size_t * size)1089 GSERIALIZED* gserialized1_from_lwgeom(LWGEOM *geom, size_t *size)
1090 {
1091 size_t expected_size = 0;
1092 size_t return_size = 0;
1093 uint8_t *serialized = NULL;
1094 uint8_t *ptr = NULL;
1095 GSERIALIZED *g = NULL;
1096 assert(geom);
1097
1098 /*
1099 ** See if we need a bounding box, add one if we don't have one.
1100 */
1101 if ( (! geom->bbox) && lwgeom_needs_bbox(geom) && (!lwgeom_is_empty(geom)) )
1102 {
1103 lwgeom_add_bbox(geom);
1104 }
1105
1106 /*
1107 ** Harmonize the flags to the state of the lwgeom
1108 */
1109 if ( geom->bbox )
1110 FLAGS_SET_BBOX(geom->flags, 1);
1111 else
1112 FLAGS_SET_BBOX(geom->flags, 0);
1113
1114 /* Set up the uint8_t buffer into which we are going to write the serialized geometry. */
1115 expected_size = gserialized1_from_lwgeom_size(geom);
1116 serialized = lwalloc(expected_size);
1117 ptr = serialized;
1118
1119 /* Move past size, srid and flags. */
1120 ptr += 8;
1121
1122 /* Write in the serialized form of the gbox, if necessary. */
1123 if ( geom->bbox )
1124 ptr += gserialized1_from_gbox(geom->bbox, ptr);
1125
1126 /* Write in the serialized form of the geometry. */
1127 ptr += gserialized1_from_lwgeom_any(geom, ptr);
1128
1129 /* Calculate size as returned by data processing functions. */
1130 return_size = ptr - serialized;
1131
1132 if ( expected_size != return_size ) /* Uh oh! */
1133 {
1134 lwerror("Return size (%d) not equal to expected size (%d)!", return_size, expected_size);
1135 return NULL;
1136 }
1137
1138 if ( size ) /* Return the output size to the caller if necessary. */
1139 *size = return_size;
1140
1141 g = (GSERIALIZED*)serialized;
1142
1143 /*
1144 ** We are aping PgSQL code here, PostGIS code should use
1145 ** VARSIZE to set this for real.
1146 */
1147 g->size = return_size << 2;
1148
1149 /* Set the SRID! */
1150 gserialized1_set_srid(g, geom->srid);
1151
1152 g->gflags = lwflags_get_g1flags(geom->flags);
1153
1154 return g;
1155 }
1156
1157 /***********************************************************************
1158 * De-serialize GSERIALIZED into an LWGEOM.
1159 */
1160
1161 static LWGEOM* lwgeom_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size);
1162
lwpoint_from_gserialized1_buffer(uint8_t * data_ptr,lwflags_t lwflags,size_t * size)1163 static LWPOINT* lwpoint_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size)
1164 {
1165 uint8_t *start_ptr = data_ptr;
1166 LWPOINT *point;
1167 uint32_t npoints = 0;
1168
1169 assert(data_ptr);
1170
1171 point = (LWPOINT*)lwalloc(sizeof(LWPOINT));
1172 point->srid = SRID_UNKNOWN; /* Default */
1173 point->bbox = NULL;
1174 point->type = POINTTYPE;
1175 point->flags = lwflags;
1176
1177 data_ptr += 4; /* Skip past the type. */
1178 npoints = gserialized1_get_uint32_t(data_ptr); /* Zero => empty geometry */
1179 data_ptr += 4; /* Skip past the npoints. */
1180
1181 if ( npoints > 0 )
1182 point->point = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 1, data_ptr);
1183 else
1184 point->point = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty point */
1185
1186 data_ptr += npoints * FLAGS_NDIMS(lwflags) * sizeof(double);
1187
1188 if ( size )
1189 *size = data_ptr - start_ptr;
1190
1191 return point;
1192 }
1193
lwline_from_gserialized1_buffer(uint8_t * data_ptr,lwflags_t lwflags,size_t * size)1194 static LWLINE* lwline_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size)
1195 {
1196 uint8_t *start_ptr = data_ptr;
1197 LWLINE *line;
1198 uint32_t npoints = 0;
1199
1200 assert(data_ptr);
1201
1202 line = (LWLINE*)lwalloc(sizeof(LWLINE));
1203 line->srid = SRID_UNKNOWN; /* Default */
1204 line->bbox = NULL;
1205 line->type = LINETYPE;
1206 line->flags = lwflags;
1207
1208 data_ptr += 4; /* Skip past the type. */
1209 npoints = gserialized1_get_uint32_t(data_ptr); /* Zero => empty geometry */
1210 data_ptr += 4; /* Skip past the npoints. */
1211
1212 if ( npoints > 0 )
1213 line->points = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, data_ptr);
1214
1215 else
1216 line->points = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty linestring */
1217
1218 data_ptr += FLAGS_NDIMS(lwflags) * npoints * sizeof(double);
1219
1220 if ( size )
1221 *size = data_ptr - start_ptr;
1222
1223 return line;
1224 }
1225
lwpoly_from_gserialized1_buffer(uint8_t * data_ptr,lwflags_t lwflags,size_t * size)1226 static LWPOLY* lwpoly_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size)
1227 {
1228 uint8_t *start_ptr = data_ptr;
1229 LWPOLY *poly;
1230 uint8_t *ordinate_ptr;
1231 uint32_t nrings = 0;
1232 uint32_t i = 0;
1233
1234 assert(data_ptr);
1235
1236 poly = (LWPOLY*)lwalloc(sizeof(LWPOLY));
1237 poly->srid = SRID_UNKNOWN; /* Default */
1238 poly->bbox = NULL;
1239 poly->type = POLYGONTYPE;
1240 poly->flags = lwflags;
1241
1242 data_ptr += 4; /* Skip past the polygontype. */
1243 nrings = gserialized1_get_uint32_t(data_ptr); /* Zero => empty geometry */
1244 poly->nrings = nrings;
1245 LWDEBUGF(4, "nrings = %d", nrings);
1246 data_ptr += 4; /* Skip past the nrings. */
1247
1248 ordinate_ptr = data_ptr; /* Start the ordinate pointer. */
1249 if ( nrings > 0)
1250 {
1251 poly->rings = (POINTARRAY**)lwalloc( sizeof(POINTARRAY*) * nrings );
1252 poly->maxrings = nrings;
1253 ordinate_ptr += nrings * 4; /* Move past all the npoints values. */
1254 if ( nrings % 2 ) /* If there is padding, move past that too. */
1255 ordinate_ptr += 4;
1256 }
1257 else /* Empty polygon */
1258 {
1259 poly->rings = NULL;
1260 poly->maxrings = 0;
1261 }
1262
1263 for ( i = 0; i < nrings; i++ )
1264 {
1265 uint32_t npoints = 0;
1266
1267 /* Read in the number of points. */
1268 npoints = gserialized1_get_uint32_t(data_ptr);
1269 data_ptr += 4;
1270
1271 /* Make a point array for the ring, and move the ordinate pointer past the ring ordinates. */
1272 poly->rings[i] = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, ordinate_ptr);
1273
1274 ordinate_ptr += sizeof(double) * FLAGS_NDIMS(lwflags) * npoints;
1275 }
1276
1277 if ( size )
1278 *size = ordinate_ptr - start_ptr;
1279
1280 return poly;
1281 }
1282
lwtriangle_from_gserialized1_buffer(uint8_t * data_ptr,lwflags_t lwflags,size_t * size)1283 static LWTRIANGLE* lwtriangle_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size)
1284 {
1285 uint8_t *start_ptr = data_ptr;
1286 LWTRIANGLE *triangle;
1287 uint32_t npoints = 0;
1288
1289 assert(data_ptr);
1290
1291 triangle = (LWTRIANGLE*)lwalloc(sizeof(LWTRIANGLE));
1292 triangle->srid = SRID_UNKNOWN; /* Default */
1293 triangle->bbox = NULL;
1294 triangle->type = TRIANGLETYPE;
1295 triangle->flags = lwflags;
1296
1297 data_ptr += 4; /* Skip past the type. */
1298 npoints = gserialized1_get_uint32_t(data_ptr); /* Zero => empty geometry */
1299 data_ptr += 4; /* Skip past the npoints. */
1300
1301 if ( npoints > 0 )
1302 triangle->points = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, data_ptr);
1303 else
1304 triangle->points = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty triangle */
1305
1306 data_ptr += FLAGS_NDIMS(lwflags) * npoints * sizeof(double);
1307
1308 if ( size )
1309 *size = data_ptr - start_ptr;
1310
1311 return triangle;
1312 }
1313
lwcircstring_from_gserialized1_buffer(uint8_t * data_ptr,lwflags_t lwflags,size_t * size)1314 static LWCIRCSTRING* lwcircstring_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size)
1315 {
1316 uint8_t *start_ptr = data_ptr;
1317 LWCIRCSTRING *circstring;
1318 uint32_t npoints = 0;
1319
1320 assert(data_ptr);
1321
1322 circstring = (LWCIRCSTRING*)lwalloc(sizeof(LWCIRCSTRING));
1323 circstring->srid = SRID_UNKNOWN; /* Default */
1324 circstring->bbox = NULL;
1325 circstring->type = CIRCSTRINGTYPE;
1326 circstring->flags = lwflags;
1327
1328 data_ptr += 4; /* Skip past the circstringtype. */
1329 npoints = gserialized1_get_uint32_t(data_ptr); /* Zero => empty geometry */
1330 data_ptr += 4; /* Skip past the npoints. */
1331
1332 if ( npoints > 0 )
1333 circstring->points = ptarray_construct_reference_data(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), npoints, data_ptr);
1334 else
1335 circstring->points = ptarray_construct(FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), 0); /* Empty circularstring */
1336
1337 data_ptr += FLAGS_NDIMS(lwflags) * npoints * sizeof(double);
1338
1339 if ( size )
1340 *size = data_ptr - start_ptr;
1341
1342 return circstring;
1343 }
1344
lwcollection_from_gserialized1_buffer(uint8_t * data_ptr,lwflags_t lwflags,size_t * size)1345 static LWCOLLECTION* lwcollection_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *size)
1346 {
1347 uint32_t type;
1348 uint8_t *start_ptr = data_ptr;
1349 LWCOLLECTION *collection;
1350 uint32_t ngeoms = 0;
1351 uint32_t i = 0;
1352
1353 assert(data_ptr);
1354
1355 type = gserialized1_get_uint32_t(data_ptr);
1356 data_ptr += 4; /* Skip past the type. */
1357
1358 collection = (LWCOLLECTION*)lwalloc(sizeof(LWCOLLECTION));
1359 collection->srid = SRID_UNKNOWN; /* Default */
1360 collection->bbox = NULL;
1361 collection->type = type;
1362 collection->flags = lwflags;
1363
1364 ngeoms = gserialized1_get_uint32_t(data_ptr);
1365 collection->ngeoms = ngeoms; /* Zero => empty geometry */
1366 data_ptr += 4; /* Skip past the ngeoms. */
1367
1368 if ( ngeoms > 0 )
1369 {
1370 collection->geoms = lwalloc(sizeof(LWGEOM*) * ngeoms);
1371 collection->maxgeoms = ngeoms;
1372 }
1373 else
1374 {
1375 collection->geoms = NULL;
1376 collection->maxgeoms = 0;
1377 }
1378
1379 /* Sub-geometries are never de-serialized with boxes (#1254) */
1380 FLAGS_SET_BBOX(lwflags, 0);
1381
1382 for ( i = 0; i < ngeoms; i++ )
1383 {
1384 uint32_t subtype = gserialized1_get_uint32_t(data_ptr);
1385 size_t subsize = 0;
1386
1387 if ( ! lwcollection_allows_subtype(type, subtype) )
1388 {
1389 lwerror("Invalid subtype (%s) for collection type (%s)", lwtype_name(subtype), lwtype_name(type));
1390 lwfree(collection);
1391 return NULL;
1392 }
1393 collection->geoms[i] = lwgeom_from_gserialized1_buffer(data_ptr, lwflags, &subsize);
1394 data_ptr += subsize;
1395 }
1396
1397 if ( size )
1398 *size = data_ptr - start_ptr;
1399
1400 return collection;
1401 }
1402
lwgeom_from_gserialized1_buffer(uint8_t * data_ptr,lwflags_t lwflags,size_t * g_size)1403 LWGEOM* lwgeom_from_gserialized1_buffer(uint8_t *data_ptr, lwflags_t lwflags, size_t *g_size)
1404 {
1405 uint32_t type;
1406
1407 assert(data_ptr);
1408
1409 type = gserialized1_get_uint32_t(data_ptr);
1410
1411 LWDEBUGF(2, "Got type %d (%s), hasz=%d hasm=%d geodetic=%d hasbox=%d", type, lwtype_name(type),
1412 FLAGS_GET_Z(lwflags), FLAGS_GET_M(lwflags), FLAGS_GET_GEODETIC(lwflags), FLAGS_GET_BBOX(lwflags));
1413
1414 switch (type)
1415 {
1416 case POINTTYPE:
1417 return (LWGEOM *)lwpoint_from_gserialized1_buffer(data_ptr, lwflags, g_size);
1418 case LINETYPE:
1419 return (LWGEOM *)lwline_from_gserialized1_buffer(data_ptr, lwflags, g_size);
1420 case CIRCSTRINGTYPE:
1421 return (LWGEOM *)lwcircstring_from_gserialized1_buffer(data_ptr, lwflags, g_size);
1422 case POLYGONTYPE:
1423 return (LWGEOM *)lwpoly_from_gserialized1_buffer(data_ptr, lwflags, g_size);
1424 case TRIANGLETYPE:
1425 return (LWGEOM *)lwtriangle_from_gserialized1_buffer(data_ptr, lwflags, g_size);
1426 case MULTIPOINTTYPE:
1427 case MULTILINETYPE:
1428 case MULTIPOLYGONTYPE:
1429 case COMPOUNDTYPE:
1430 case CURVEPOLYTYPE:
1431 case MULTICURVETYPE:
1432 case MULTISURFACETYPE:
1433 case POLYHEDRALSURFACETYPE:
1434 case TINTYPE:
1435 case COLLECTIONTYPE:
1436 return (LWGEOM *)lwcollection_from_gserialized1_buffer(data_ptr, lwflags, g_size);
1437 default:
1438 lwerror("Unknown geometry type: %d - %s", type, lwtype_name(type));
1439 return NULL;
1440 }
1441 }
1442
lwgeom_from_gserialized1(const GSERIALIZED * g)1443 LWGEOM* lwgeom_from_gserialized1(const GSERIALIZED *g)
1444 {
1445 lwflags_t lwflags = 0;
1446 int32_t srid = 0;
1447 uint32_t lwtype = 0;
1448 uint8_t *data_ptr = NULL;
1449 LWGEOM *lwgeom = NULL;
1450 GBOX bbox;
1451 size_t size = 0;
1452
1453 assert(g);
1454
1455 srid = gserialized1_get_srid(g);
1456 lwtype = gserialized1_get_type(g);
1457 lwflags = gserialized1_get_lwflags(g);
1458
1459 LWDEBUGF(4, "Got type %d (%s), srid=%d", lwtype, lwtype_name(lwtype), srid);
1460
1461 data_ptr = (uint8_t*)g->data;
1462 if (FLAGS_GET_BBOX(lwflags))
1463 data_ptr += gbox_serialized_size(lwflags);
1464
1465 lwgeom = lwgeom_from_gserialized1_buffer(data_ptr, lwflags, &size);
1466
1467 if ( ! lwgeom )
1468 lwerror("%s: unable create geometry", __func__); /* Ooops! */
1469
1470 lwgeom->type = lwtype;
1471 lwgeom->flags = lwflags;
1472
1473 if ( gserialized1_read_gbox_p(g, &bbox) == LW_SUCCESS )
1474 {
1475 lwgeom->bbox = gbox_copy(&bbox);
1476 }
1477 else if ( lwgeom_needs_bbox(lwgeom) && (lwgeom_calculate_gbox(lwgeom, &bbox) == LW_SUCCESS) )
1478 {
1479 lwgeom->bbox = gbox_copy(&bbox);
1480 }
1481 else
1482 {
1483 lwgeom->bbox = NULL;
1484 }
1485
1486 lwgeom_set_srid(lwgeom, srid);
1487
1488 return lwgeom;
1489 }
1490
gserialized1_get_float_box_p(const GSERIALIZED * g,size_t * ndims)1491 const float * gserialized1_get_float_box_p(const GSERIALIZED *g, size_t *ndims)
1492 {
1493 if (ndims)
1494 *ndims = G1FLAGS_NDIMS_BOX(g->gflags);
1495 if (!g) return NULL;
1496 if (!G1FLAGS_GET_BBOX(g->gflags)) return NULL;
1497 return (const float *)(g->data);
1498 }
1499
1500 /**
1501 * Update the bounding box of a #GSERIALIZED, allocating a fresh one
1502 * if there is not enough space to just write the new box in.
1503 * <em>WARNING</em> if a new object needs to be created, the
1504 * input pointer will have to be freed by the caller! Check
1505 * to see if input == output. Returns null if there's a problem
1506 * like mismatched dimensions.
1507 */
gserialized1_set_gbox(GSERIALIZED * g,GBOX * gbox)1508 GSERIALIZED* gserialized1_set_gbox(GSERIALIZED *g, GBOX *gbox)
1509 {
1510
1511 int g_ndims = G1FLAGS_NDIMS_BOX(g->gflags);
1512 int box_ndims = FLAGS_NDIMS_BOX(gbox->flags);
1513 GSERIALIZED *g_out = NULL;
1514 size_t box_size = 2 * g_ndims * sizeof(float);
1515 float *fbox;
1516 int fbox_pos = 0;
1517
1518 /* The dimensionality of the inputs has to match or we are SOL. */
1519 if ( g_ndims != box_ndims )
1520 {
1521 return NULL;
1522 }
1523
1524 /* Serialized already has room for a box. */
1525 if (G1FLAGS_GET_BBOX(g->gflags))
1526 {
1527 g_out = g;
1528 }
1529 /* Serialized has no box. We need to allocate enough space for the old
1530 data plus the box, and leave a gap in the memory segment to write
1531 the new values into.
1532 */
1533 else
1534 {
1535 size_t varsize_new = SIZE_GET(g->size) + box_size;
1536 uint8_t *ptr;
1537 g_out = lwalloc(varsize_new);
1538 /* Copy the head of g into place */
1539 memcpy(g_out, g, 8);
1540 /* Copy the body of g into place after leaving space for the box */
1541 ptr = g_out->data;
1542 ptr += box_size;
1543 memcpy(ptr, g->data, SIZE_GET(g->size) - 8);
1544 G1FLAGS_SET_BBOX(g_out->gflags, 1);
1545 SIZE_SET(g_out->size, varsize_new);
1546 }
1547
1548 /* Move bounds to nearest float values */
1549 gbox_float_round(gbox);
1550 /* Now write the float box values into the memory segement */
1551 fbox = (float*)(g_out->data);
1552 /* Copy in X/Y */
1553 fbox[fbox_pos++] = gbox->xmin;
1554 fbox[fbox_pos++] = gbox->xmax;
1555 fbox[fbox_pos++] = gbox->ymin;
1556 fbox[fbox_pos++] = gbox->ymax;
1557 /* Optionally copy in higher dims */
1558 if(gserialized1_has_z(g) || gserialized1_is_geodetic(g))
1559 {
1560 fbox[fbox_pos++] = gbox->zmin;
1561 fbox[fbox_pos++] = gbox->zmax;
1562 }
1563 if(gserialized1_has_m(g) && ! gserialized1_is_geodetic(g))
1564 {
1565 fbox[fbox_pos++] = gbox->mmin;
1566 fbox[fbox_pos++] = gbox->mmax;
1567 }
1568
1569 return g_out;
1570 }
1571
1572
1573 /**
1574 * Remove the bounding box from a #GSERIALIZED. Returns a freshly
1575 * allocated #GSERIALIZED every time.
1576 */
gserialized1_drop_gbox(GSERIALIZED * g)1577 GSERIALIZED* gserialized1_drop_gbox(GSERIALIZED *g)
1578 {
1579 int g_ndims = G1FLAGS_NDIMS_BOX(g->gflags);
1580 size_t box_size = 2 * g_ndims * sizeof(float);
1581 size_t g_out_size = SIZE_GET(g->size) - box_size;
1582 GSERIALIZED *g_out = lwalloc(g_out_size);
1583
1584 /* Copy the contents while omitting the box */
1585 if ( G1FLAGS_GET_BBOX(g->gflags) )
1586 {
1587 uint8_t *outptr = (uint8_t*)g_out;
1588 uint8_t *inptr = (uint8_t*)g;
1589 /* Copy the header (size+type) of g into place */
1590 memcpy(outptr, inptr, 8);
1591 outptr += 8;
1592 inptr += 8 + box_size;
1593 /* Copy parts after the box into place */
1594 memcpy(outptr, inptr, g_out_size - 8);
1595 G1FLAGS_SET_BBOX(g_out->gflags, 0);
1596 SIZE_SET(g_out->size, g_out_size);
1597 }
1598 /* No box? Nothing to do but copy and return. */
1599 else
1600 {
1601 memcpy(g_out, g, g_out_size);
1602 }
1603
1604 return g_out;
1605 }
1606
1607