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