PostGIS 3.0.6dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches
mvt.c
Go to the documentation of this file.
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 (C) 2016-2017 Björn Harrtell <bjorn@wololo.org>
22 *
23 **********************************************************************/
24
25#include <string.h>
26#include <float.h>
27#include <math.h>
28
29#include "mvt.h"
30#include "lwgeom_geos.h"
31#include "pgsql_compat.h"
32
33#ifdef HAVE_LIBPROTOBUF
34#include "utils/jsonb.h"
35
36#if POSTGIS_PGSQL_VERSION < 110
37/* See trac ticket #3867 */
38# define DatumGetJsonbP DatumGetJsonb
39#endif
40
41#define uthash_fatal(msg) lwerror("uthash: fatal error (out of memory)")
42#define uthash_malloc(sz) palloc(sz)
43#define uthash_free(ptr,sz) pfree(ptr)
44/* Note: set UTHASH_FUNCTION (not HASH_FUNCTION) to change the hash function */
45#include "uthash.h"
46
47#define FEATURES_CAPACITY_INITIAL 50
48
55
57{
60 MVT_RING = 3
61};
62
64{
65 char *name;
66 uint32_t id;
67 UT_hash_handle hh;
68};
69
71{
73 uint32_t id;
74 UT_hash_handle hh;
75};
76
78{
80 uint32_t id;
81 UT_hash_handle hh;
82};
83
85{
87 uint32_t id;
88 UT_hash_handle hh;
89};
90
92{
93 uint64_t uint_value;
94 uint32_t id;
95 UT_hash_handle hh;
96};
97
99{
100 int64_t sint_value;
101 uint32_t id;
102 UT_hash_handle hh;
103};
104
106{
107 protobuf_c_boolean bool_value;
108 uint32_t id;
109 UT_hash_handle hh;
110};
111
112static inline uint32_t c_int(enum mvt_cmd_id id, uint32_t count)
113{
114 return (id & 0x7) | (count << 3);
115}
116
117static inline uint32_t p_int(int32_t value)
118{
119 return (value << 1) ^ (value >> 31);
120}
121
122static uint32_t encode_ptarray(__attribute__((__unused__)) mvt_agg_context *ctx,
123 enum mvt_type type, POINTARRAY *pa, uint32_t *buffer,
124 int32_t *px, int32_t *py)
125{
126 uint32_t offset = 0;
127 uint32_t i, c = 0;
128 int32_t dx, dy, x, y;
129 const POINT2D *p;
130
131 /* loop points and add to buffer */
132 for (i = 0; i < pa->npoints; i++)
133 {
134 /* move offset for command */
135 if (i == 0 || (i == 1 && type > MVT_POINT))
136 offset++;
137 /* skip closing point for rings */
138 if (type == MVT_RING && i == pa->npoints - 1)
139 break;
140 p = getPoint2d_cp(pa, i);
141 x = p->x;
142 y = p->y;
143 dx = x - *px;
144 dy = y - *py;
145 buffer[offset++] = p_int(dx);
146 buffer[offset++] = p_int(dy);
147 *px = x;
148 *py = y;
149 c++;
150 }
151
152 /* determine initial move and eventual line command */
153 if (type == MVT_POINT)
154 {
155 /* point or multipoint, use actual number of point count */
156 buffer[0] = c_int(CMD_MOVE_TO, c);
157 }
158 else
159 {
160 /* line or polygon, assume count 1 */
161 buffer[0] = c_int(CMD_MOVE_TO, 1);
162 /* line command with move point subtracted from count */
163 buffer[3] = c_int(CMD_LINE_TO, c - 1);
164 }
165
166 /* add close command if ring */
167 if (type == MVT_RING)
168 buffer[offset++] = c_int(CMD_CLOSE_PATH, 1);
169
170 return offset;
171}
172
174 enum mvt_type type,
175 POINTARRAY *pa, uint32_t *buffer)
176{
177 int32_t px = 0, py = 0;
178 return encode_ptarray(ctx, type, pa, buffer, &px, &py);
179}
180
181static void encode_point(mvt_agg_context *ctx, LWPOINT *point)
182{
183 VectorTile__Tile__Feature *feature = ctx->feature;
184 feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POINT;
185 feature->has_type = 1;
186 feature->n_geometry = 3;
187 feature->geometry = palloc(sizeof(*feature->geometry) * 3);
188 encode_ptarray_initial(ctx, MVT_POINT, point->point, feature->geometry);
189}
190
191static void encode_mpoint(mvt_agg_context *ctx, LWMPOINT *mpoint)
192{
193 size_t c;
194 VectorTile__Tile__Feature *feature = ctx->feature;
195 // NOTE: inefficient shortcut LWMPOINT->LWLINE
196 LWLINE *lwline = lwline_from_lwmpoint(mpoint->srid, mpoint);
197 feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POINT;
198 feature->has_type = 1;
199 c = 1 + lwline->points->npoints * 2;
200 feature->geometry = palloc(sizeof(*feature->geometry) * c);
201 feature->n_geometry = encode_ptarray_initial(ctx, MVT_POINT,
202 lwline->points, feature->geometry);
203}
204
205static void encode_line(mvt_agg_context *ctx, LWLINE *lwline)
206{
207 size_t c;
208 VectorTile__Tile__Feature *feature = ctx->feature;
209 feature->type = VECTOR_TILE__TILE__GEOM_TYPE__LINESTRING;
210 feature->has_type = 1;
211 c = 2 + lwline->points->npoints * 2;
212 feature->geometry = palloc(sizeof(*feature->geometry) * c);
213 feature->n_geometry = encode_ptarray_initial(ctx, MVT_LINE,
214 lwline->points, feature->geometry);
215}
216
217static void encode_mline(mvt_agg_context *ctx, LWMLINE *lwmline)
218{
219 uint32_t i;
220 int32_t px = 0, py = 0;
221 size_t c = 0, offset = 0;
222 VectorTile__Tile__Feature *feature = ctx->feature;
223 feature->type = VECTOR_TILE__TILE__GEOM_TYPE__LINESTRING;
224 feature->has_type = 1;
225 for (i = 0; i < lwmline->ngeoms; i++)
226 c += 2 + lwmline->geoms[i]->points->npoints * 2;
227 feature->geometry = palloc(sizeof(*feature->geometry) * c);
228 for (i = 0; i < lwmline->ngeoms; i++)
229 offset += encode_ptarray(ctx, MVT_LINE,
230 lwmline->geoms[i]->points,
231 feature->geometry + offset, &px, &py);
232 feature->n_geometry = offset;
233}
234
235static void encode_poly(mvt_agg_context *ctx, LWPOLY *lwpoly)
236{
237 uint32_t i;
238 int32_t px = 0, py = 0;
239 size_t c = 0, offset = 0;
240 VectorTile__Tile__Feature *feature = ctx->feature;
241 feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POLYGON;
242 feature->has_type = 1;
243 for (i = 0; i < lwpoly->nrings; i++)
244 c += 3 + ((lwpoly->rings[i]->npoints - 1) * 2);
245 feature->geometry = palloc(sizeof(*feature->geometry) * c);
246 for (i = 0; i < lwpoly->nrings; i++)
247 offset += encode_ptarray(ctx, MVT_RING,
248 lwpoly->rings[i],
249 feature->geometry + offset, &px, &py);
250 feature->n_geometry = offset;
251}
252
253static void encode_mpoly(mvt_agg_context *ctx, LWMPOLY *lwmpoly)
254{
255 uint32_t i, j;
256 int32_t px = 0, py = 0;
257 size_t c = 0, offset = 0;
258 LWPOLY *poly;
259 VectorTile__Tile__Feature *feature = ctx->feature;
260 feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POLYGON;
261 feature->has_type = 1;
262 for (i = 0; i < lwmpoly->ngeoms; i++)
263 for (j = 0; poly = lwmpoly->geoms[i], j < poly->nrings; j++)
264 c += 3 + ((poly->rings[j]->npoints - 1) * 2);
265 feature->geometry = palloc(sizeof(*feature->geometry) * c);
266 for (i = 0; i < lwmpoly->ngeoms; i++)
267 for (j = 0; poly = lwmpoly->geoms[i], j < poly->nrings; j++)
268 offset += encode_ptarray(ctx, MVT_RING,
269 poly->rings[j], feature->geometry + offset,
270 &px, &py);
271 feature->n_geometry = offset;
272}
273
274static void encode_geometry(mvt_agg_context *ctx, LWGEOM *lwgeom)
275{
276 int type = lwgeom->type;
277
278 switch (type)
279 {
280 case POINTTYPE:
281 return encode_point(ctx, (LWPOINT*)lwgeom);
282 case LINETYPE:
283 return encode_line(ctx, (LWLINE*)lwgeom);
284 case POLYGONTYPE:
285 return encode_poly(ctx, (LWPOLY*)lwgeom);
286 case MULTIPOINTTYPE:
287 return encode_mpoint(ctx, (LWMPOINT*)lwgeom);
288 case MULTILINETYPE:
289 return encode_mline(ctx, (LWMLINE*)lwgeom);
290 case MULTIPOLYGONTYPE:
291 return encode_mpoly(ctx, (LWMPOLY*)lwgeom);
292 default: elog(ERROR, "encode_geometry: '%s' geometry type not supported",
293 lwtype_name(type));
294 }
295}
296
297static TupleDesc get_tuple_desc(mvt_agg_context *ctx)
298{
299 Oid tupType = HeapTupleHeaderGetTypeId(ctx->row);
300 int32 tupTypmod = HeapTupleHeaderGetTypMod(ctx->row);
301 TupleDesc tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
302 return tupdesc;
303}
304
305static uint32_t get_key_index_with_size(mvt_agg_context *ctx, const char *name, size_t size)
306{
307 struct mvt_kv_key *kv;
308 HASH_FIND(hh, ctx->keys_hash, name, size, kv);
309 if (!kv)
310 return UINT32_MAX;
311 return kv->id;
312}
313
314static uint32_t add_key(mvt_agg_context *ctx, char *name)
315{
316 struct mvt_kv_key *kv;
317 size_t size = strlen(name);
318 kv = palloc(sizeof(*kv));
319 kv->id = ctx->keys_hash_i++;
320 kv->name = name;
321 HASH_ADD_KEYPTR(hh, ctx->keys_hash, name, size, kv);
322 return kv->id;
323}
324
326{
327 uint32_t i, natts;
328 bool geom_found = false;
329
330 POSTGIS_DEBUG(2, "parse_column_keys called");
331
333 natts = ctx->column_cache.tupdesc->natts;
334
335 ctx->column_cache.column_keys_index = palloc(sizeof(uint32_t) * natts);
336 ctx->column_cache.column_oid = palloc(sizeof(uint32_t) * natts);
337 ctx->column_cache.values = palloc(sizeof(Datum) * natts);
338 ctx->column_cache.nulls = palloc(sizeof(bool) * natts);
339
340 for (i = 0; i < natts; i++)
341 {
342 Oid typoid = getBaseType(TupleDescAttr(ctx->column_cache.tupdesc, i)->atttypid);
343 char *tkey = TupleDescAttr(ctx->column_cache.tupdesc, i)->attname.data;
344
345 ctx->column_cache.column_oid[i] = typoid;
346
347 if (typoid == JSONBOID)
348 {
350 continue;
351 }
352
353 if (ctx->geom_name == NULL)
354 {
355 if (!geom_found && typoid == postgis_oid(GEOMETRYOID))
356 {
357 ctx->geom_index = i;
358 geom_found = true;
359 continue;
360 }
361 }
362 else
363 {
364 if (!geom_found && strcmp(tkey, ctx->geom_name) == 0)
365 {
366 ctx->geom_index = i;
367 geom_found = true;
368 continue;
369 }
370 }
371
372 if (ctx->id_name &&
373 (ctx->id_index == UINT32_MAX) &&
374 (strcmp(tkey, ctx->id_name) == 0) &&
375 (typoid == INT2OID || typoid == INT4OID || typoid == INT8OID))
376 {
377 ctx->id_index = i;
378 }
379 else
380 {
381 ctx->column_cache.column_keys_index[i] = add_key(ctx, pstrdup(tkey));
382 }
383 }
384
385 if (!geom_found)
386 elog(ERROR, "parse_column_keys: no geometry column found");
387
388 if (ctx->id_name != NULL && ctx->id_index == UINT32_MAX)
389 elog(ERROR, "mvt_agg_transfn: Could not find column '%s' of integer type", ctx->id_name);
390}
391
393{
394 struct mvt_kv_key *kv;
395 size_t n_keys = ctx->keys_hash_i;
396 char **keys = palloc(n_keys * sizeof(*keys));
397 for (kv = ctx->keys_hash; kv != NULL; kv=kv->hh.next)
398 keys[kv->id] = kv->name;
399 ctx->layer->n_keys = n_keys;
400 ctx->layer->keys = keys;
401
402 HASH_CLEAR(hh, ctx->keys_hash);
403}
404
405static VectorTile__Tile__Value *create_value()
406{
407 VectorTile__Tile__Value *value = palloc(sizeof(*value));
408 vector_tile__tile__value__init(value);
409 return value;
410}
411
412#define MVT_CREATE_VALUES(kvtype, hash, hasfield, valuefield) \
413{ \
414 POSTGIS_DEBUG(2, "MVT_CREATE_VALUES called"); \
415 { \
416 struct kvtype *kv; \
417 for (kv = ctx->hash; kv != NULL; kv=kv->hh.next) \
418 { \
419 VectorTile__Tile__Value *value = create_value(); \
420 value->hasfield = 1; \
421 value->valuefield = kv->valuefield; \
422 values[kv->id] = value; \
423 } \
424 } \
425}
426
428{
429 VectorTile__Tile__Value **values;
430 struct mvt_kv_string_value *kv;
431
432 POSTGIS_DEBUG(2, "encode_values called");
433
434 values = palloc(ctx->values_hash_i * sizeof(*values));
435 for (kv = ctx->string_values_hash; kv != NULL; kv=kv->hh.next)
436 {
437 VectorTile__Tile__Value *value = create_value();
438 value->string_value = kv->string_value;
439 values[kv->id] = value;
440 }
442 float_values_hash, has_float_value, float_value);
444 double_values_hash, has_double_value, double_value);
446 uint_values_hash, has_uint_value, uint_value);
448 sint_values_hash, has_sint_value, sint_value);
450 bool_values_hash, has_bool_value, bool_value);
451
452 POSTGIS_DEBUGF(3, "encode_values n_values: %d", ctx->values_hash_i);
453 ctx->layer->n_values = ctx->values_hash_i;
454 ctx->layer->values = values;
455
456 HASH_CLEAR(hh, ctx->string_values_hash);
457 HASH_CLEAR(hh, ctx->float_values_hash);
458 HASH_CLEAR(hh, ctx->double_values_hash);
459 HASH_CLEAR(hh, ctx->uint_values_hash);
460 HASH_CLEAR(hh, ctx->sint_values_hash);
461 HASH_CLEAR(hh, ctx->bool_values_hash);
462
464 pfree(ctx->column_cache.column_oid);
465 pfree(ctx->column_cache.values);
466 pfree(ctx->column_cache.nulls);
467 ReleaseTupleDesc(ctx->column_cache.tupdesc);
468 memset(&ctx->column_cache, 0, sizeof(ctx->column_cache));
469
470}
471
472#define MVT_PARSE_VALUE(value, kvtype, hash, valuefield, size) \
473{ \
474 POSTGIS_DEBUG(2, "MVT_PARSE_VALUE called"); \
475 { \
476 struct kvtype *kv; \
477 HASH_FIND(hh, ctx->hash, &value, size, kv); \
478 if (!kv) \
479 { \
480 POSTGIS_DEBUG(4, "MVT_PARSE_VALUE value not found"); \
481 kv = palloc(sizeof(*kv)); \
482 POSTGIS_DEBUGF(4, "MVT_PARSE_VALUE new hash key: %d", \
483 ctx->values_hash_i); \
484 kv->id = ctx->values_hash_i++; \
485 kv->valuefield = value; \
486 HASH_ADD(hh, ctx->hash, valuefield, size, kv); \
487 } \
488 tags[ctx->row_columns*2] = k; \
489 tags[ctx->row_columns*2+1] = kv->id; \
490 } \
491}
492
493#define MVT_PARSE_INT_VALUE(value) \
494{ \
495 if (value >= 0) \
496 { \
497 uint64_t cvalue = value; \
498 MVT_PARSE_VALUE(cvalue, mvt_kv_uint_value, \
499 uint_values_hash, uint_value, \
500 sizeof(uint64_t)) \
501 } \
502 else \
503 { \
504 int64_t cvalue = value; \
505 MVT_PARSE_VALUE(cvalue, mvt_kv_sint_value, \
506 sint_values_hash, sint_value, \
507 sizeof(int64_t)) \
508 } \
509}
510
511#define MVT_PARSE_DATUM(type, kvtype, hash, valuefield, datumfunc, size) \
512{ \
513 type value = datumfunc(datum); \
514 MVT_PARSE_VALUE(value, kvtype, hash, valuefield, size); \
515}
516
517#define MVT_PARSE_INT_DATUM(type, datumfunc) \
518{ \
519 type value = datumfunc(datum); \
520 MVT_PARSE_INT_VALUE(value); \
521}
522
524 char *value, size_t size, uint32_t *tags, uint32_t k)
525{
526 struct mvt_kv_string_value *kv;
527 POSTGIS_DEBUG(2, "add_value_as_string called");
528 HASH_FIND(hh, ctx->string_values_hash, value, size, kv);
529 if (!kv)
530 {
531 POSTGIS_DEBUG(4, "add_value_as_string value not found");
532 kv = palloc(sizeof(*kv));
533 POSTGIS_DEBUGF(4, "add_value_as_string new hash key: %d",
534 ctx->values_hash_i);
535 kv->id = ctx->values_hash_i++;
536 kv->string_value = value;
537 HASH_ADD_KEYPTR(hh, ctx->string_values_hash, kv->string_value,
538 size, kv);
539 }
540 tags[ctx->row_columns*2] = k;
541 tags[ctx->row_columns*2+1] = kv->id;
542}
543
545 char *value, uint32_t *tags, uint32_t k)
546{
547 return add_value_as_string_with_size(ctx, value, strlen(value), tags, k);
548}
549
550static void parse_datum_as_string(mvt_agg_context *ctx, Oid typoid,
551 Datum datum, uint32_t *tags, uint32_t k)
552{
553 Oid foutoid;
554 bool typisvarlena;
555 char *value;
556 POSTGIS_DEBUG(2, "parse_value_as_string called");
557 getTypeOutputInfo(typoid, &foutoid, &typisvarlena);
558 value = OidOutputFunctionCall(foutoid, datum);
559 POSTGIS_DEBUGF(4, "parse_value_as_string value: %s", value);
560 add_value_as_string(ctx, value, tags, k);
561}
562
563static uint32_t *parse_jsonb(mvt_agg_context *ctx, Jsonb *jb,
564 uint32_t *tags)
565{
566 JsonbIterator *it;
567 JsonbValue v;
568 bool skipNested = false;
569 JsonbIteratorToken r;
570 uint32_t k;
571
572 if (!JB_ROOT_IS_OBJECT(jb))
573 return tags;
574
575 it = JsonbIteratorInit(&jb->root);
576
577 while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
578 {
579 skipNested = true;
580
581 if (r == WJB_KEY && v.type != jbvNull)
582 {
583
584 k = get_key_index_with_size(ctx, v.val.string.val, v.val.string.len);
585 if (k == UINT32_MAX)
586 {
587 char *key;
588 uint32_t newSize = ctx->keys_hash_i + 1;
589
590 key = palloc(v.val.string.len + 1);
591 memcpy(key, v.val.string.val, v.val.string.len);
592 key[v.val.string.len] = '\0';
593
594 tags = repalloc(tags, newSize * 2 * sizeof(*tags));
595 k = add_key(ctx, key);
596 }
597
598 r = JsonbIteratorNext(&it, &v, skipNested);
599
600 if (v.type == jbvString)
601 {
602 char *value;
603 value = palloc(v.val.string.len + 1);
604 memcpy(value, v.val.string.val, v.val.string.len);
605 value[v.val.string.len] = '\0';
606 add_value_as_string(ctx, value, tags, k);
607 ctx->row_columns++;
608 }
609 else if (v.type == jbvBool)
610 {
611 MVT_PARSE_VALUE(v.val.boolean, mvt_kv_bool_value,
612 bool_values_hash, bool_value, sizeof(protobuf_c_boolean));
613 ctx->row_columns++;
614 }
615 else if (v.type == jbvNumeric)
616 {
617 char *str;
618 double d;
619 long l;
620 str = DatumGetCString(DirectFunctionCall1(numeric_out,
621 PointerGetDatum(v.val.numeric)));
622 d = strtod(str, NULL);
623 l = strtol(str, NULL, 10);
624
625 if (fabs(d - (double)l) > FLT_EPSILON)
626 {
627 MVT_PARSE_VALUE(d, mvt_kv_double_value, double_values_hash,
628 double_value, sizeof(double));
629 }
630 else
631 {
633 }
634 ctx->row_columns++;
635 }
636 }
637 }
638
639 return tags;
640}
641
645static void set_feature_id(mvt_agg_context *ctx, Datum datum, bool isNull)
646{
647 Oid typoid = ctx->column_cache.column_oid[ctx->id_index];
648 int64_t value = INT64_MIN;
649
650 if (isNull)
651 {
652 POSTGIS_DEBUG(3, "set_feature_id: Ignored null value");
653 return;
654 }
655
656 switch (typoid)
657 {
658 case INT2OID:
659 value = DatumGetInt16(datum);
660 break;
661 case INT4OID:
662 value = DatumGetInt32(datum);
663 break;
664 case INT8OID:
665 value = DatumGetInt64(datum);
666 break;
667 default:
668 elog(ERROR, "set_feature_id: Feature id type does not match");
669 }
670
671 if (value < 0)
672 {
673 POSTGIS_DEBUG(3, "set_feature_id: Ignored negative value");
674 return;
675 }
676
677 ctx->feature->has_id = true;
678 ctx->feature->id = (uint64_t) value;
679}
680
682{
683 uint32_t n_keys = ctx->keys_hash_i;
684 uint32_t *tags = palloc(n_keys * 2 * sizeof(*tags));
685 uint32_t i;
687 uint32_t natts = (uint32_t) cc.tupdesc->natts;
688
689 HeapTupleData tuple;
690
691 POSTGIS_DEBUG(2, "parse_values called");
692 ctx->row_columns = 0;
693
694 /* Build a temporary HeapTuple control structure */
695 tuple.t_len = HeapTupleHeaderGetDatumLength(ctx->row);
696 ItemPointerSetInvalid(&(tuple.t_self));
697 tuple.t_tableOid = InvalidOid;
698 tuple.t_data = ctx->row;
699
700 /* We use heap_deform_tuple as it costs only O(N) vs O(N^2) of GetAttributeByNum */
701 heap_deform_tuple(&tuple, cc.tupdesc, cc.values, cc.nulls);
702
703 POSTGIS_DEBUGF(3, "parse_values natts: %d", natts);
704
705 for (i = 0; i < natts; i++)
706 {
707 char *key;
708 Oid typoid;
709 uint32_t k;
710 Datum datum = cc.values[i];
711
712 if (i == ctx->geom_index)
713 continue;
714
715 if (i == ctx->id_index)
716 {
717 set_feature_id(ctx, datum, cc.nulls[i]);
718 continue;
719 }
720
721 if (cc.nulls[i])
722 {
723 POSTGIS_DEBUG(3, "parse_values isnull detected");
724 continue;
725 }
726
727 key = TupleDescAttr(cc.tupdesc, i)->attname.data;
728 k = cc.column_keys_index[i];
729 typoid = cc.column_oid[i];
730
731 if (k == UINT32_MAX && typoid != JSONBOID)
732 elog(ERROR, "parse_values: unexpectedly could not find parsed key name '%s'", key);
733 if (typoid == JSONBOID)
734 {
735 tags = parse_jsonb(ctx, DatumGetJsonbP(datum), tags);
736 continue;
737 }
738
739 switch (typoid)
740 {
741 case BOOLOID:
742 MVT_PARSE_DATUM(protobuf_c_boolean, mvt_kv_bool_value,
743 bool_values_hash, bool_value,
744 DatumGetBool, sizeof(protobuf_c_boolean));
745 break;
746 case INT2OID:
747 MVT_PARSE_INT_DATUM(int16_t, DatumGetInt16);
748 break;
749 case INT4OID:
750 MVT_PARSE_INT_DATUM(int32_t, DatumGetInt32);
751 break;
752 case INT8OID:
753 MVT_PARSE_INT_DATUM(int64_t, DatumGetInt64);
754 break;
755 case FLOAT4OID:
757 float_values_hash, float_value,
758 DatumGetFloat4, sizeof(float));
759 break;
760 case FLOAT8OID:
762 double_values_hash, double_value,
763 DatumGetFloat8, sizeof(double));
764 break;
765 default:
766 parse_datum_as_string(ctx, typoid, datum, tags, k);
767 break;
768 }
769
770 ctx->row_columns++;
771 }
772
773
774 ctx->feature->n_tags = ctx->row_columns * 2;
775 ctx->feature->tags = tags;
776
777 POSTGIS_DEBUGF(3, "parse_values n_tags %zd", ctx->feature->n_tags);
778}
779
780/* For a given geometry, look for the highest dimensional basic type, that is,
781 * point, line or polygon */
782static uint8
784{
785 switch(geom->type)
786 {
787 case POINTTYPE:
788 case LINETYPE:
789 case POLYGONTYPE:
790 return geom->type;
791 case TRIANGLETYPE:
792 return POLYGONTYPE;
793 case MULTIPOINTTYPE:
794 case MULTILINETYPE:
795 case MULTIPOLYGONTYPE:
796 return geom->type - 3; /* Based on LWTYPE positions */
797 case COLLECTIONTYPE:
798 case TINTYPE:
799 {
800 uint32_t i;
801 uint8 type = 0;
802 LWCOLLECTION *g = (LWCOLLECTION*)geom;
803 for (i = 0; i < g->ngeoms; i++)
804 {
805 LWGEOM *sg = g->geoms[i];
806 type = Max(type, lwgeom_get_basic_type(sg));
807 }
808 return type;
809 }
810 default:
811 elog(ERROR, "%s: Invalid type (%d)", __func__, geom->type);
812 }
813}
814
815
822static inline LWGEOM *
823lwgeom_to_basic_type(LWGEOM *geom, uint8 original_type)
824{
825 LWGEOM *geom_out = geom;
826 if (lwgeom_get_type(geom) == COLLECTIONTYPE)
827 {
828 LWCOLLECTION *g = (LWCOLLECTION*)geom;
829 geom_out = (LWGEOM *)lwcollection_extract(g, original_type);
830 }
831
832 /* If a collection only contains 1 geometry return than instead */
833 if (lwgeom_is_collection(geom_out))
834 {
835 LWCOLLECTION *g = (LWCOLLECTION *)geom_out;
836 if (g->ngeoms == 1)
837 {
838 geom_out = g->geoms[0];
839 }
840 }
841
842 geom_out->srid = geom->srid;
843 return geom_out;
844}
845
846/* Clips a geometry using lwgeom_clip_by_rect. Might return NULL */
847static LWGEOM *
849{
850 LWGEOM *geom_clipped;
851 GBOX geom_box;
852
853 gbox_init(&geom_box);
854 FLAGS_SET_GEODETIC(geom_box.flags, 0);
855 lwgeom_calculate_gbox(lwg_in, &geom_box);
856
857 if (!gbox_overlaps_2d(&geom_box, clip_box))
858 {
859 POSTGIS_DEBUG(3, "mvt_geom: geometry outside clip box");
860 return NULL;
861 }
862
863 if (gbox_contains_2d(clip_box, &geom_box))
864 {
865 POSTGIS_DEBUG(3, "mvt_geom: geometry contained fully inside the box");
866 return lwg_in;
867 }
868
869 geom_clipped = lwgeom_clip_by_rect(lwg_in, clip_box->xmin, clip_box->ymin, clip_box->xmax, clip_box->ymax);
870 if (!geom_clipped || lwgeom_is_empty(geom_clipped))
871 return NULL;
872 return geom_clipped;
873}
874
882static LWGEOM *
884{
885 LWGEOM *geom_clipped, *envelope;
886 GBOX geom_box;
887 GEOSGeometry *geos_input, *geos_box, *geos_result;
888
889 gbox_init(&geom_box);
890 FLAGS_SET_GEODETIC(geom_box.flags, 0);
891 lwgeom_calculate_gbox(lwg_in, &geom_box);
892
893 if (!gbox_overlaps_2d(&geom_box, clip_box))
894 {
895 POSTGIS_DEBUG(3, "mvt_geom: geometry outside clip box");
896 return NULL;
897 }
898
899 if (gbox_contains_2d(clip_box, &geom_box))
900 {
901 POSTGIS_DEBUG(3, "mvt_geom: geometry contained fully inside the box");
902 return lwg_in;
903 }
904
905 initGEOS(lwnotice, lwgeom_geos_error);
906 if (!(geos_input = LWGEOM2GEOS(lwg_in, 1)))
907 return NULL;
908
909 envelope = (LWGEOM *)lwpoly_construct_envelope(
910 lwg_in->srid, clip_box->xmin, clip_box->ymin, clip_box->xmax, clip_box->ymax);
911 geos_box = LWGEOM2GEOS(envelope, 1);
912 lwgeom_free(envelope);
913 if (!geos_box)
914 {
915 GEOSGeom_destroy(geos_input);
916 return NULL;
917 }
918
919 geos_result = GEOSIntersection(geos_input, geos_box);
920 if (!geos_result)
921 {
922 POSTGIS_DEBUG(3, "mvt_geom: no geometry after intersection. Retrying after validation");
923 GEOSGeom_destroy(geos_input);
924 lwg_in = lwgeom_make_valid(lwg_in);
925 if (!(geos_input = LWGEOM2GEOS(lwg_in, 1)))
926 {
927 GEOSGeom_destroy(geos_box);
928 return NULL;
929 }
930 geos_result = GEOSIntersection(geos_input, geos_box);
931 if (!geos_result)
932 {
933 GEOSGeom_destroy(geos_box);
934 GEOSGeom_destroy(geos_input);
935 return NULL;
936 }
937 }
938
939 GEOSSetSRID(geos_result, lwg_in->srid);
940 geom_clipped = GEOS2LWGEOM(geos_result, 0);
941
942 GEOSGeom_destroy(geos_box);
943 GEOSGeom_destroy(geos_input);
944 GEOSGeom_destroy(geos_result);
945
946 if (!geom_clipped || lwgeom_is_empty(geom_clipped))
947 {
948 POSTGIS_DEBUG(3, "mvt_geom: no geometry after clipping");
949 return NULL;
950 }
951
952 return geom_clipped;
953}
954
961static LWGEOM *
962mvt_iterate_clip_by_box_geos(LWGEOM *lwgeom, GBOX *clip_gbox, uint8_t basic_type)
963{
964 if (basic_type != POLYGONTYPE)
965 {
966 return mvt_unsafe_clip_by_box(lwgeom, clip_gbox);
967 }
968
969 if (lwgeom->type != MULTIPOLYGONTYPE || ((LWMPOLY *)lwgeom)->ngeoms == 1)
970 {
971 return mvt_safe_clip_polygon_by_box(lwgeom, clip_gbox);
972 }
973 else
974 {
975 GBOX geom_box;
976 uint32_t i;
977 LWCOLLECTION *lwmg;
978 LWCOLLECTION *res;
979
980 gbox_init(&geom_box);
981 FLAGS_SET_GEODETIC(geom_box.flags, 0);
982 lwgeom_calculate_gbox(lwgeom, &geom_box);
983
984 lwmg = ((LWCOLLECTION *)lwgeom);
986 MULTIPOLYGONTYPE, lwgeom->srid, FLAGS_GET_Z(lwgeom->flags), FLAGS_GET_M(lwgeom->flags));
987 for (i = 0; i < lwmg->ngeoms; i++)
988 {
989 LWGEOM *clipped = mvt_safe_clip_polygon_by_box(lwcollection_getsubgeom(lwmg, i), clip_gbox);
990 if (clipped)
991 {
992 clipped = lwgeom_to_basic_type(clipped, POLYGONTYPE);
993 if (!lwgeom_is_empty(clipped) &&
994 (clipped->type == POLYGONTYPE || clipped->type == MULTIPOLYGONTYPE))
995 {
996 if (!lwgeom_is_collection(clipped))
997 {
998 lwcollection_add_lwgeom(res, clipped);
999 }
1000 else
1001 {
1002 uint32_t j;
1003 for (j = 0; j < ((LWCOLLECTION *)clipped)->ngeoms; j++)
1005 res, lwcollection_getsubgeom((LWCOLLECTION *)clipped, j));
1006 }
1007 }
1008 }
1009 }
1010 return lwcollection_as_lwgeom(res);
1011 }
1012}
1013
1019static LWGEOM *
1020mvt_grid_and_validate_geos(LWGEOM *ng, uint8_t basic_type)
1021{
1022 gridspec grid = {0, 0, 0, 0, 1, 1, 0, 0};
1023 ng = lwgeom_to_basic_type(ng, basic_type);
1024
1025 if (basic_type != POLYGONTYPE)
1026 {
1027 /* Make sure there is no pending float values (clipping can do that) */
1028 lwgeom_grid_in_place(ng, &grid);
1029 }
1030 else
1031 {
1032 /* For polygons we have to both snap to the integer grid and force validation.
1033 * The problem with this procedure is that snapping to the grid can create
1034 * an invalid geometry and making it valid can create float values; so
1035 * we iterate several times (up to 3) to generate a valid geom with int coordinates
1036 */
1037 GEOSGeometry *geo;
1038 uint32_t iterations = 0;
1039 static const uint32_t max_iterations = 3;
1040 bool valid = false;
1041
1042 /* Grid to int */
1043 lwgeom_grid_in_place(ng, &grid);
1044
1046 geo = LWGEOM2GEOS(ng, 0);
1047 if (!geo)
1048 return NULL;
1049 valid = GEOSisValid(geo) == 1;
1050
1051 while (!valid && iterations < max_iterations)
1052 {
1053#if POSTGIS_GEOS_VERSION < 38
1054 GEOSGeometry *geo_valid = LWGEOM_GEOS_makeValid(geo);
1055#else
1056 GEOSGeometry *geo_valid = GEOSMakeValid(geo);
1057#endif
1058
1059 GEOSGeom_destroy(geo);
1060 if (!geo_valid)
1061 return NULL;
1062
1063 ng = GEOS2LWGEOM(geo_valid, 0);
1064 GEOSGeom_destroy(geo_valid);
1065 if (!ng)
1066 return NULL;
1067
1068 lwgeom_grid_in_place(ng, &grid);
1069 ng = lwgeom_to_basic_type(ng, basic_type);
1070 geo = LWGEOM2GEOS(ng, 0);
1071 valid = GEOSisValid(geo) == 1;
1072 iterations++;
1073 }
1074 GEOSGeom_destroy(geo);
1075
1076 if (!valid)
1077 {
1078 POSTGIS_DEBUG(1, "mvt_geom: Could not transform into a valid MVT geometry");
1079 return NULL;
1080 }
1081
1082 /* In image coordinates CW actually comes out a CCW, so we reverse */
1085 }
1086 return ng;
1087}
1088
1089/* Clips and validates a geometry for MVT using GEOS
1090 * Might return NULL
1091 */
1092static LWGEOM *
1093mvt_clip_and_validate_geos(LWGEOM *lwgeom, uint8_t basic_type, uint32_t extent, uint32_t buffer, bool clip_geom)
1094{
1095 LWGEOM *ng = lwgeom;
1096
1097 if (clip_geom)
1098 {
1099 GBOX bgbox;
1100 gbox_init(&bgbox);
1101 bgbox.xmax = bgbox.ymax = (double)extent + (double)buffer;
1102 bgbox.xmin = bgbox.ymin = -(double)buffer;
1103 FLAGS_SET_GEODETIC(bgbox.flags, 0);
1104
1105 ng = mvt_iterate_clip_by_box_geos(lwgeom, &bgbox, basic_type);
1106 if (!ng || lwgeom_is_empty(ng))
1107 {
1108 POSTGIS_DEBUG(3, "mvt_geom: no geometry after clip");
1109 return NULL;
1110 }
1111 }
1112
1113 ng = mvt_grid_and_validate_geos(ng, basic_type);
1114
1115 /* Make sure we return the expected type */
1116 if (!ng || basic_type != lwgeom_get_basic_type(ng))
1117 {
1118 /* Drop type changes to play nice with MVT renderers */
1119 POSTGIS_DEBUG(1, "mvt_geom: Dropping geometry after type change");
1120 return NULL;
1121 }
1122
1123 return ng;
1124}
1125
1126#ifdef HAVE_WAGYU
1127
1128#include "lwgeom_wagyu.h"
1129
1130static LWGEOM *
1131mvt_clip_and_validate(LWGEOM *lwgeom, uint8_t basic_type, uint32_t extent, uint32_t buffer, bool clip_geom)
1132{
1133 GBOX clip_box = {0};
1134 LWGEOM *clipped_lwgeom;
1135
1136 /* Wagyu only supports polygons. Default to geos for other types */
1137 lwgeom = lwgeom_to_basic_type(lwgeom, POLYGONTYPE);
1138 if (lwgeom->type != POLYGONTYPE && lwgeom->type != MULTIPOLYGONTYPE)
1139 {
1140 return mvt_clip_and_validate_geos(lwgeom, basic_type, extent, buffer, clip_geom);
1141 }
1142
1143 if (!clip_geom)
1144 {
1145 /* With clipping disabled, we request a clip with the geometry bbox to force validation */
1146 lwgeom_calculate_gbox(lwgeom, &clip_box);
1147 }
1148 else
1149 {
1150 clip_box.xmax = clip_box.ymax = (double)extent + (double)buffer;
1151 clip_box.xmin = clip_box.ymin = -(double)buffer;
1152 }
1153
1154 clipped_lwgeom = lwgeom_wagyu_clip_by_box(lwgeom, &clip_box);
1155
1156 return clipped_lwgeom;
1157}
1158
1159#else /* ! HAVE_WAGYU */
1160
1161static LWGEOM *
1162mvt_clip_and_validate(LWGEOM *lwgeom, uint8_t basic_type, uint32_t extent, uint32_t buffer, bool clip_geom)
1163{
1164 return mvt_clip_and_validate_geos(lwgeom, basic_type, extent, buffer, clip_geom);
1165}
1166#endif
1167
1176LWGEOM *mvt_geom(LWGEOM *lwgeom, const GBOX *gbox, uint32_t extent, uint32_t buffer,
1177 bool clip_geom)
1178{
1179 AFFINE affine = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
1180 gridspec grid = {0, 0, 0, 0, 1, 1, 0, 0};
1181 double width = gbox->xmax - gbox->xmin;
1182 double height = gbox->ymax - gbox->ymin;
1183 double resx, resy, res, fx, fy;
1184 const uint8_t basic_type = lwgeom_get_basic_type(lwgeom);
1185 int preserve_collapsed = LW_FALSE;
1186 POSTGIS_DEBUG(2, "mvt_geom called");
1187
1188 /* Simplify it as soon as possible */
1189 lwgeom = lwgeom_to_basic_type(lwgeom, basic_type);
1190
1191 /* Short circuit out on EMPTY */
1192 if (lwgeom_is_empty(lwgeom))
1193 return NULL;
1194
1195 resx = width / extent;
1196 resy = height / extent;
1197 res = (resx < resy ? resx : resy)/2;
1198 fx = extent / width;
1199 fy = -(extent / height);
1200
1201 /* Remove all non-essential points (under the output resolution) */
1203 lwgeom_simplify_in_place(lwgeom, res, preserve_collapsed);
1204
1205 /* If geometry has disappeared, you're done */
1206 if (lwgeom_is_empty(lwgeom))
1207 return NULL;
1208
1209 /* transform to tile coordinate space */
1210 affine.afac = fx;
1211 affine.efac = fy;
1212 affine.ifac = 1;
1213 affine.xoff = -gbox->xmin * fx;
1214 affine.yoff = -gbox->ymax * fy;
1215 lwgeom_affine(lwgeom, &affine);
1216
1217 /* Snap to integer precision, removing duplicate points */
1218 lwgeom_grid_in_place(lwgeom, &grid);
1219
1220 if (!lwgeom || lwgeom_is_empty(lwgeom))
1221 return NULL;
1222
1223 lwgeom = mvt_clip_and_validate(lwgeom, basic_type, extent, buffer, clip_geom);
1224 if (!lwgeom || lwgeom_is_empty(lwgeom))
1225 return NULL;
1226
1227 return lwgeom;
1228}
1229
1234{
1235 VectorTile__Tile__Layer *layer;
1236
1237 POSTGIS_DEBUG(2, "mvt_agg_init_context called");
1238
1239 if (ctx->extent == 0)
1240 elog(ERROR, "mvt_agg_init_context: extent cannot be 0");
1241
1242 ctx->tile = NULL;
1244 ctx->keys_hash = NULL;
1245 ctx->string_values_hash = NULL;
1246 ctx->float_values_hash = NULL;
1247 ctx->double_values_hash = NULL;
1248 ctx->uint_values_hash = NULL;
1249 ctx->sint_values_hash = NULL;
1250 ctx->bool_values_hash = NULL;
1251 ctx->values_hash_i = 0;
1252 ctx->keys_hash_i = 0;
1253 ctx->id_index = UINT32_MAX;
1254 ctx->geom_index = UINT32_MAX;
1255
1256 memset(&ctx->column_cache, 0, sizeof(ctx->column_cache));
1257
1258 layer = palloc(sizeof(*layer));
1259 vector_tile__tile__layer__init(layer);
1260 layer->version = 2;
1261 layer->name = ctx->name;
1262 layer->has_extent = 1;
1263 layer->extent = ctx->extent;
1264 layer->features = palloc (ctx->features_capacity *
1265 sizeof(*layer->features));
1266
1267 ctx->layer = layer;
1268}
1269
1278{
1279 bool isnull = false;
1280 Datum datum;
1281 GSERIALIZED *gs;
1282 LWGEOM *lwgeom;
1283 VectorTile__Tile__Feature *feature;
1284 VectorTile__Tile__Layer *layer = ctx->layer;
1285/* VectorTile__Tile__Feature **features = layer->features; */
1286 POSTGIS_DEBUG(2, "mvt_agg_transfn called");
1287
1288 if (layer->n_features >= ctx->features_capacity)
1289 {
1290 size_t new_capacity = ctx->features_capacity * 2;
1291 layer->features = repalloc(layer->features, new_capacity *
1292 sizeof(*layer->features));
1293 ctx->features_capacity = new_capacity;
1294 POSTGIS_DEBUGF(3, "mvt_agg_transfn new_capacity: %zd", new_capacity);
1295 }
1296
1297 if (ctx->geom_index == UINT32_MAX)
1298 parse_column_keys(ctx);
1299
1300 datum = GetAttributeByNum(ctx->row, ctx->geom_index + 1, &isnull);
1301 POSTGIS_DEBUGF(3, "mvt_agg_transfn ctx->geom_index: %d", ctx->geom_index);
1302 POSTGIS_DEBUGF(3, "mvt_agg_transfn isnull: %u", isnull);
1303 POSTGIS_DEBUGF(3, "mvt_agg_transfn datum: %lu", datum);
1304 if (isnull) /* Skip rows that have null geometry */
1305 {
1306 POSTGIS_DEBUG(3, "mvt_agg_transfn got null geom");
1307 return;
1308 }
1309
1310 feature = palloc(sizeof(*feature));
1311 vector_tile__tile__feature__init(feature);
1312
1313 ctx->feature = feature;
1314
1315 gs = (GSERIALIZED *) PG_DETOAST_DATUM(datum);
1316 lwgeom = lwgeom_from_gserialized(gs);
1317
1318 POSTGIS_DEBUGF(3, "mvt_agg_transfn encoded feature count: %zd", layer->n_features);
1319 layer->features[layer->n_features++] = feature;
1320
1321 encode_geometry(ctx, lwgeom);
1322 lwgeom_free(lwgeom);
1323 // TODO: free detoasted datum?
1324 parse_values(ctx);
1325}
1326
1327static VectorTile__Tile * mvt_ctx_to_tile(mvt_agg_context *ctx)
1328{
1329 int n_layers = 1;
1330 VectorTile__Tile *tile;
1331 encode_keys(ctx);
1332 encode_values(ctx);
1333
1334 tile = palloc(sizeof(VectorTile__Tile));
1335 vector_tile__tile__init(tile);
1336 tile->layers = palloc(sizeof(VectorTile__Tile__Layer*) * n_layers);
1337 tile->layers[0] = ctx->layer;
1338 tile->n_layers = n_layers;
1339 return tile;
1340}
1341
1343{
1344 /* Fill out the file slot, if it's not already filled. */
1345 /* We should only have a filled slow when all the work of building */
1346 /* out the data is complete, so after a serialize/deserialize cycle */
1347 /* or after a context combine */
1348 size_t len;
1349 bytea *ba;
1350
1351 if (!ctx->tile)
1352 {
1353 ctx->tile = mvt_ctx_to_tile(ctx);
1354 }
1355
1356 /* Zero features => empty bytea output */
1357 if (ctx && ctx->layer && ctx->layer->n_features == 0)
1358 {
1359 bytea *ba = palloc(VARHDRSZ);
1360 SET_VARSIZE(ba, VARHDRSZ);
1361 return ba;
1362 }
1363
1364 /* Serialize the Tile */
1365 len = VARHDRSZ + vector_tile__tile__get_packed_size(ctx->tile);
1366 ba = palloc(len);
1367 vector_tile__tile__pack(ctx->tile, (uint8_t*)VARDATA(ba));
1368 SET_VARSIZE(ba, len);
1369 return ba;
1370}
1371
1372
1374{
1375 return mvt_ctx_to_bytea(ctx);
1376}
1377
1378static void * mvt_allocator(__attribute__((__unused__)) void *data, size_t size)
1379{
1380 return palloc(size);
1381}
1382
1383static void mvt_deallocator(__attribute__((__unused__)) void *data, void *ptr)
1384{
1385 return pfree(ptr);
1386}
1387
1389{
1390 ProtobufCAllocator allocator =
1391 {
1394 NULL
1395 };
1396
1397 size_t len = VARSIZE_ANY_EXHDR(ba);
1398 VectorTile__Tile *tile = vector_tile__tile__unpack(&allocator, len, (uint8_t*)VARDATA(ba));
1399 mvt_agg_context *ctx = palloc(sizeof(mvt_agg_context));
1400 memset(ctx, 0, sizeof(mvt_agg_context));
1401 ctx->tile = tile;
1402 return ctx;
1403}
1404
1405static VectorTile__Tile__Value *
1406tile_value_copy(const VectorTile__Tile__Value *value)
1407{
1408 VectorTile__Tile__Value *nvalue = palloc(sizeof(VectorTile__Tile__Value));
1409 memcpy(nvalue, value, sizeof(VectorTile__Tile__Value));
1410 if (value->string_value)
1411 nvalue->string_value = pstrdup(value->string_value);
1412 return nvalue;
1413}
1414
1415static VectorTile__Tile__Feature *
1416tile_feature_copy(const VectorTile__Tile__Feature *feature, int key_offset, int value_offset)
1417{
1418 uint32_t i;
1419 VectorTile__Tile__Feature *nfeature;
1420
1421 /* Null in => Null out */
1422 if (!feature) return NULL;
1423
1424 /* Init object */
1425 nfeature = palloc(sizeof(VectorTile__Tile__Feature));
1426 vector_tile__tile__feature__init(nfeature);
1427
1428 /* Copy settings straight over */
1429 nfeature->has_id = feature->has_id;
1430 nfeature->id = feature->id;
1431 nfeature->has_type = feature->has_type;
1432 nfeature->type = feature->type;
1433
1434 /* Copy tags over, offsetting indexes so they match the dictionaries */
1435 /* at the Tile_Layer level */
1436 if (feature->n_tags > 0)
1437 {
1438 nfeature->n_tags = feature->n_tags;
1439 nfeature->tags = palloc(sizeof(uint32_t)*feature->n_tags);
1440 for (i = 0; i < feature->n_tags/2; i++)
1441 {
1442 nfeature->tags[2*i] = feature->tags[2*i] + key_offset;
1443 nfeature->tags[2*i+1] = feature->tags[2*i+1] + value_offset;
1444 }
1445 }
1446
1447 /* Copy the raw geometry data over literally */
1448 if (feature->n_geometry > 0)
1449 {
1450 nfeature->n_geometry = feature->n_geometry;
1451 nfeature->geometry = palloc(sizeof(uint32_t)*feature->n_geometry);
1452 memcpy(nfeature->geometry, feature->geometry, sizeof(uint32_t)*feature->n_geometry);
1453 }
1454
1455 /* Done */
1456 return nfeature;
1457}
1458
1459static VectorTile__Tile__Layer *
1460vectortile_layer_combine(const VectorTile__Tile__Layer *layer1, const VectorTile__Tile__Layer *layer2)
1461{
1462 uint32_t i, j;
1463 int key2_offset, value2_offset;
1464 VectorTile__Tile__Layer *layer = palloc(sizeof(VectorTile__Tile__Layer));
1465 vector_tile__tile__layer__init(layer);
1466
1467 /* Take globals from layer1 */
1468 layer->version = layer1->version;
1469 layer->name = pstrdup(layer1->name);
1470 layer->has_extent = layer1->has_extent;
1471 layer->extent = layer1->extent;
1472
1473 /* Copy keys into new layer */
1474 j = 0;
1475 layer->n_keys = layer1->n_keys + layer2->n_keys;
1476 layer->keys = layer->n_keys ? palloc(layer->n_keys * sizeof(void*)) : NULL;
1477 for (i = 0; i < layer1->n_keys; i++)
1478 layer->keys[j++] = pstrdup(layer1->keys[i]);
1479 key2_offset = j;
1480 for (i = 0; i < layer2->n_keys; i++)
1481 layer->keys[j++] = pstrdup(layer2->keys[i]);
1482
1483 /* Copy values into new layer */
1484 /* TODO, apply hash logic here too, so that merged tiles */
1485 /* retain unique value maps */
1486 layer->n_values = layer1->n_values + layer2->n_values;
1487 layer->values = layer->n_values ? palloc(layer->n_values * sizeof(void*)) : NULL;
1488 j = 0;
1489 for (i = 0; i < layer1->n_values; i++)
1490 layer->values[j++] = tile_value_copy(layer1->values[i]);
1491 value2_offset = j;
1492 for (i = 0; i < layer2->n_values; i++)
1493 layer->values[j++] = tile_value_copy(layer2->values[i]);
1494
1495
1496 layer->n_features = layer1->n_features + layer2->n_features;
1497 layer->features = layer->n_features ? palloc(layer->n_features * sizeof(void*)) : NULL;
1498 j = 0;
1499 for (i = 0; i < layer1->n_features; i++)
1500 layer->features[j++] = tile_feature_copy(layer1->features[i], 0, 0);
1501 for (i = 0; i < layer2->n_features; i++)
1502 layer->features[j++] = tile_feature_copy(layer2->features[i], key2_offset, value2_offset);
1503
1504 return layer;
1505}
1506
1507
1508static VectorTile__Tile *
1509vectortile_tile_combine(VectorTile__Tile *tile1, VectorTile__Tile *tile2)
1510{
1511 uint32_t i, j;
1512 VectorTile__Tile *tile;
1513
1514 /* Hopelessly messing up memory ownership here */
1515 if (tile1->n_layers == 0 && tile2->n_layers == 0)
1516 return tile1;
1517 else if (tile1->n_layers == 0)
1518 return tile2;
1519 else if (tile2->n_layers == 0)
1520 return tile1;
1521
1522 tile = palloc(sizeof(VectorTile__Tile));
1523 vector_tile__tile__init(tile);
1524 tile->layers = palloc(sizeof(void*));
1525 tile->n_layers = 0;
1526
1527 /* Merge all matching layers in the files (basically always only one) */
1528 for (i = 0; i < tile1->n_layers; i++)
1529 {
1530 for (j = 0; j < tile2->n_layers; j++)
1531 {
1532 VectorTile__Tile__Layer *l1 = tile1->layers[i];
1533 VectorTile__Tile__Layer *l2 = tile2->layers[j];
1534 if (strcmp(l1->name, l2->name)==0)
1535 {
1536 VectorTile__Tile__Layer *layer = vectortile_layer_combine(l1, l2);
1537 if (!layer)
1538 continue;
1539 tile->layers[tile->n_layers++] = layer;
1540 /* Add a spare slot at the end of the array */
1541 tile->layers = repalloc(tile->layers, (tile->n_layers+1) * sizeof(void*));
1542 }
1543 }
1544 }
1545 return tile;
1546}
1547
1549{
1550 if (ctx1 || ctx2)
1551 {
1552 if (ctx1 && ! ctx2) return ctx1;
1553 if (ctx2 && ! ctx1) return ctx2;
1554 if (ctx1 && ctx2 && ctx1->tile && ctx2->tile)
1555 {
1556 mvt_agg_context *ctxnew = palloc(sizeof(mvt_agg_context));
1557 memset(ctxnew, 0, sizeof(mvt_agg_context));
1558 ctxnew->tile = vectortile_tile_combine(ctx1->tile, ctx2->tile);
1559 return ctxnew;
1560 }
1561 else
1562 {
1563 elog(DEBUG2, "ctx1->tile = %p", ctx1->tile);
1564 elog(DEBUG2, "ctx2->tile = %p", ctx2->tile);
1565 elog(ERROR, "%s: unable to combine contexts where tile attribute is null", __func__);
1566 return NULL;
1567 }
1568 }
1569 else
1570 {
1571 return NULL;
1572 }
1573}
1574
1582{
1583 return mvt_ctx_to_bytea(ctx);
1584}
1585
1586
1587#endif
char * r
Definition cu_in_wkt.c:24
int gbox_overlaps_2d(const GBOX *g1, const GBOX *g2)
Return LW_TRUE if the GBOX overlaps on the 2d plane, LW_FALSE otherwise.
Definition gbox.c:323
void gbox_init(GBOX *gbox)
Zero out all the entries in the GBOX.
Definition gbox.c:40
int gbox_contains_2d(const GBOX *g1, const GBOX *g2)
Return LW_TRUE if the first GBOX contains the second on the 2d plane, LW_FALSE otherwise.
Definition gbox.c:339
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
GEOSGeometry * LWGEOM2GEOS(const LWGEOM *lwgeom, uint8_t autofix)
LWGEOM * GEOS2LWGEOM(const GEOSGeometry *geom, uint8_t want3d)
void lwgeom_geos_error(const char *fmt,...)
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition lwutil.c:216
#define LW_FALSE
Definition liblwgeom.h:108
#define COLLECTIONTYPE
Definition liblwgeom.h:122
LWCOLLECTION * lwcollection_extract(LWCOLLECTION *col, int type)
Takes a potentially heterogeneous collection and returns a homogeneous collection consisting only of ...
int lwgeom_simplify_in_place(LWGEOM *igeom, double dist, int preserve_collapsed)
Definition lwgeom.c:1715
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1138
#define MULTILINETYPE
Definition liblwgeom.h:120
#define LINETYPE
Definition liblwgeom.h:117
LWGEOM * lwgeom_clip_by_rect(const LWGEOM *geom1, double x0, double y0, double x1, double y1)
#define MULTIPOINTTYPE
Definition liblwgeom.h:119
int lwgeom_remove_repeated_points_in_place(LWGEOM *in, double tolerance)
Definition lwgeom.c:1554
LWPOLY * lwpoly_construct_envelope(int32_t srid, double x1, double y1, double x2, double y2)
Definition lwpoly.c:98
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition liblwgeom.h:116
#define FLAGS_GET_Z(flags)
Definition liblwgeom.h:179
#define TINTYPE
Definition liblwgeom.h:130
#define MULTIPOLYGONTYPE
Definition liblwgeom.h:121
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM can contain sub-geometries or not.
Definition lwgeom.c:1079
#define POLYGONTYPE
Definition liblwgeom.h:118
void lwgeom_affine(LWGEOM *geom, const AFFINE *affine)
Definition lwgeom.c:1975
#define __attribute__(x)
Definition liblwgeom.h:242
#define FLAGS_GET_M(flags)
Definition liblwgeom.h:180
void lwgeom_force_clockwise(LWGEOM *lwgeom)
Force Right-hand-rule on LWGEOM polygons.
Definition lwgeom.c:37
int lwgeom_calculate_gbox(const LWGEOM *lwgeom, GBOX *gbox)
Calculate bounding box of a geometry, automatically taking into account whether it is cartesian or ge...
Definition lwgeom.c:737
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int32_t srid, char hasz, char hasm)
#define TRIANGLETYPE
Definition liblwgeom.h:129
#define FLAGS_SET_GEODETIC(flags, value)
Definition liblwgeom.h:189
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
LWGEOM * lwgeom_make_valid(LWGEOM *geom)
Attempts to make an invalid geometries valid w/out losing points.
LWLINE * lwline_from_lwmpoint(int32_t srid, const LWMPOINT *mpoint)
Definition lwline.c:275
LWGEOM * lwcollection_getsubgeom(LWCOLLECTION *col, int gnum)
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition lwgeom.c:291
void lwgeom_grid_in_place(LWGEOM *lwgeom, const gridspec *grid)
Definition lwgeom.c:2144
void lwgeom_reverse_in_place(LWGEOM *lwgeom)
Reverse vertex order of LWGEOM.
Definition lwgeom.c:102
#define str(s)
void lwnotice(const char *fmt,...)
Write a notice out to the notice handler.
Definition lwutil.c:177
#define UINT32_MAX
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition lwinline.h:135
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition lwinline.h:193
static const POINT2D * getPoint2d_cp(const POINTARRAY *pa, uint32_t n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from.
Definition lwinline.h:91
static void encode_poly(mvt_agg_context *ctx, LWPOLY *lwpoly)
Definition mvt.c:235
static uint32_t get_key_index_with_size(mvt_agg_context *ctx, const char *name, size_t size)
Definition mvt.c:305
static VectorTile__Tile * mvt_ctx_to_tile(mvt_agg_context *ctx)
Definition mvt.c:1327
static LWGEOM * mvt_clip_and_validate(LWGEOM *lwgeom, uint8_t basic_type, uint32_t extent, uint32_t buffer, bool clip_geom)
Definition mvt.c:1131
static void encode_mline(mvt_agg_context *ctx, LWMLINE *lwmline)
Definition mvt.c:217
static void parse_datum_as_string(mvt_agg_context *ctx, Oid typoid, Datum datum, uint32_t *tags, uint32_t k)
Definition mvt.c:550
static void encode_geometry(mvt_agg_context *ctx, LWGEOM *lwgeom)
Definition mvt.c:274
static void add_value_as_string_with_size(mvt_agg_context *ctx, char *value, size_t size, uint32_t *tags, uint32_t k)
Definition mvt.c:523
#define MVT_PARSE_INT_VALUE(value)
Definition mvt.c:493
static void encode_mpoly(mvt_agg_context *ctx, LWMPOLY *lwmpoly)
Definition mvt.c:253
static void encode_mpoint(mvt_agg_context *ctx, LWMPOINT *mpoint)
Definition mvt.c:191
mvt_agg_context * mvt_ctx_deserialize(const bytea *ba)
Definition mvt.c:1388
#define FEATURES_CAPACITY_INITIAL
Definition mvt.c:47
static void set_feature_id(mvt_agg_context *ctx, Datum datum, bool isNull)
Sets the feature id.
Definition mvt.c:645
static VectorTile__Tile__Layer * vectortile_layer_combine(const VectorTile__Tile__Layer *layer1, const VectorTile__Tile__Layer *layer2)
Definition mvt.c:1460
static void encode_keys(mvt_agg_context *ctx)
Definition mvt.c:392
static void encode_point(mvt_agg_context *ctx, LWPOINT *point)
Definition mvt.c:181
bytea * mvt_ctx_serialize(mvt_agg_context *ctx)
Definition mvt.c:1373
#define MVT_PARSE_INT_DATUM(type, datumfunc)
Definition mvt.c:517
static uint8 lwgeom_get_basic_type(LWGEOM *geom)
Definition mvt.c:783
mvt_cmd_id
Definition mvt.c:50
@ CMD_MOVE_TO
Definition mvt.c:51
@ CMD_CLOSE_PATH
Definition mvt.c:53
@ CMD_LINE_TO
Definition mvt.c:52
static void mvt_deallocator(__attribute__((__unused__)) void *data, void *ptr)
Definition mvt.c:1383
static LWGEOM * mvt_safe_clip_polygon_by_box(LWGEOM *lwg_in, GBOX *clip_box)
Clips an input geometry using GEOSIntersection It used to try to use GEOSClipByRect (as mvt_unsafe_cl...
Definition mvt.c:883
static void parse_column_keys(mvt_agg_context *ctx)
Definition mvt.c:325
#define DatumGetJsonbP
Definition mvt.c:38
static LWGEOM * lwgeom_to_basic_type(LWGEOM *geom, uint8 original_type)
In place process a collection to find a concrete geometry object and expose that as the actual object...
Definition mvt.c:823
static LWGEOM * mvt_clip_and_validate_geos(LWGEOM *lwgeom, uint8_t basic_type, uint32_t extent, uint32_t buffer, bool clip_geom)
Definition mvt.c:1093
static uint32_t c_int(enum mvt_cmd_id id, uint32_t count)
Definition mvt.c:112
static VectorTile__Tile__Value * create_value()
Definition mvt.c:405
mvt_agg_context * mvt_ctx_combine(mvt_agg_context *ctx1, mvt_agg_context *ctx2)
Definition mvt.c:1548
static uint32_t add_key(mvt_agg_context *ctx, char *name)
Definition mvt.c:314
static VectorTile__Tile__Feature * tile_feature_copy(const VectorTile__Tile__Feature *feature, int key_offset, int value_offset)
Definition mvt.c:1416
static LWGEOM * mvt_grid_and_validate_geos(LWGEOM *ng, uint8_t basic_type)
Given a geometry, it uses GEOS operations to make sure that it's valid according to the MVT spec and ...
Definition mvt.c:1020
void mvt_agg_init_context(mvt_agg_context *ctx)
Initialize aggregation context.
Definition mvt.c:1233
#define MVT_CREATE_VALUES(kvtype, hash, hasfield, valuefield)
Definition mvt.c:412
static TupleDesc get_tuple_desc(mvt_agg_context *ctx)
Definition mvt.c:297
mvt_type
Definition mvt.c:57
@ MVT_POINT
Definition mvt.c:58
@ MVT_LINE
Definition mvt.c:59
@ MVT_RING
Definition mvt.c:60
static uint32_t encode_ptarray_initial(mvt_agg_context *ctx, enum mvt_type type, POINTARRAY *pa, uint32_t *buffer)
Definition mvt.c:173
static uint32_t * parse_jsonb(mvt_agg_context *ctx, Jsonb *jb, uint32_t *tags)
Definition mvt.c:563
static VectorTile__Tile * vectortile_tile_combine(VectorTile__Tile *tile1, VectorTile__Tile *tile2)
Definition mvt.c:1509
static VectorTile__Tile__Value * tile_value_copy(const VectorTile__Tile__Value *value)
Definition mvt.c:1406
static void * mvt_allocator(__attribute__((__unused__)) void *data, size_t size)
Definition mvt.c:1378
static bytea * mvt_ctx_to_bytea(mvt_agg_context *ctx)
Definition mvt.c:1342
static LWGEOM * mvt_iterate_clip_by_box_geos(LWGEOM *lwgeom, GBOX *clip_gbox, uint8_t basic_type)
Clips the geometry using GEOSIntersection in a "safe way", cleaning the input if necessary and clippi...
Definition mvt.c:962
void mvt_agg_transfn(mvt_agg_context *ctx)
Aggregation step.
Definition mvt.c:1277
bytea * mvt_agg_finalfn(mvt_agg_context *ctx)
Finalize aggregation.
Definition mvt.c:1581
static void parse_values(mvt_agg_context *ctx)
Definition mvt.c:681
static void add_value_as_string(mvt_agg_context *ctx, char *value, uint32_t *tags, uint32_t k)
Definition mvt.c:544
static void encode_values(mvt_agg_context *ctx)
Definition mvt.c:427
static uint32_t p_int(int32_t value)
Definition mvt.c:117
#define MVT_PARSE_VALUE(value, kvtype, hash, valuefield, size)
Definition mvt.c:472
LWGEOM * mvt_geom(LWGEOM *lwgeom, const GBOX *gbox, uint32_t extent, uint32_t buffer, bool clip_geom)
Transform a geometry into vector tile coordinate space.
Definition mvt.c:1176
static void encode_line(mvt_agg_context *ctx, LWLINE *lwline)
Definition mvt.c:205
static uint32_t encode_ptarray(__attribute__((__unused__)) mvt_agg_context *ctx, enum mvt_type type, POINTARRAY *pa, uint32_t *buffer, int32_t *px, int32_t *py)
Definition mvt.c:122
static LWGEOM * mvt_unsafe_clip_by_box(LWGEOM *lwg_in, GBOX *clip_box)
Definition mvt.c:848
#define MVT_PARSE_DATUM(type, kvtype, hash, valuefield, datumfunc, size)
Definition mvt.c:511
Datum buffer(PG_FUNCTION_ARGS)
unsigned int int32
Definition shpopen.c:273
double ifac
Definition liblwgeom.h:318
double xoff
Definition liblwgeom.h:318
double afac
Definition liblwgeom.h:318
double efac
Definition liblwgeom.h:318
double yoff
Definition liblwgeom.h:318
double ymax
Definition liblwgeom.h:343
double xmax
Definition liblwgeom.h:341
double ymin
Definition liblwgeom.h:342
double xmin
Definition liblwgeom.h:340
lwflags_t flags
Definition liblwgeom.h:339
uint32_t ngeoms
Definition liblwgeom.h:566
LWGEOM ** geoms
Definition liblwgeom.h:561
uint8_t type
Definition liblwgeom.h:448
int32_t srid
Definition liblwgeom.h:446
lwflags_t flags
Definition liblwgeom.h:447
POINTARRAY * points
Definition liblwgeom.h:469
LWLINE ** geoms
Definition liblwgeom.h:533
uint32_t ngeoms
Definition liblwgeom.h:538
int32_t srid
Definition liblwgeom.h:520
uint32_t ngeoms
Definition liblwgeom.h:552
LWPOLY ** geoms
Definition liblwgeom.h:547
POINTARRAY * point
Definition liblwgeom.h:457
POINTARRAY ** rings
Definition liblwgeom.h:505
uint32_t nrings
Definition liblwgeom.h:510
double y
Definition liblwgeom.h:376
double x
Definition liblwgeom.h:376
uint32_t npoints
Definition liblwgeom.h:413
Snap-to-grid.
Definition liblwgeom.h:1341
uint32_t geom_index
Definition mvt.h:64
struct mvt_kv_string_value * string_values_hash
Definition mvt.h:71
struct mvt_kv_bool_value * bool_values_hash
Definition mvt.h:76
struct mvt_kv_uint_value * uint_values_hash
Definition mvt.h:74
uint32_t values_hash_i
Definition mvt.h:77
char * id_name
Definition mvt.h:61
char * geom_name
Definition mvt.h:63
size_t features_capacity
Definition mvt.h:69
VectorTile__Tile__Layer * layer
Definition mvt.h:67
struct mvt_kv_float_value * float_values_hash
Definition mvt.h:72
struct mvt_kv_sint_value * sint_values_hash
Definition mvt.h:75
uint32_t keys_hash_i
Definition mvt.h:78
VectorTile__Tile__Feature * feature
Definition mvt.h:66
struct mvt_kv_key * keys_hash
Definition mvt.h:70
VectorTile__Tile * tile
Definition mvt.h:68
uint32_t extent
Definition mvt.h:60
uint32_t id_index
Definition mvt.h:62
struct mvt_kv_double_value * double_values_hash
Definition mvt.h:73
HeapTupleHeader row
Definition mvt.h:65
mvt_column_cache column_cache
Definition mvt.h:80
uint32_t row_columns
Definition mvt.h:79
char * name
Definition mvt.h:59
bool * nulls
Definition mvt.h:53
TupleDesc tupdesc
Definition mvt.h:54
uint32_t * column_keys_index
Definition mvt.h:50
Datum * values
Definition mvt.h:52
uint32_t * column_oid
Definition mvt.h:51
UT_hash_handle hh
Definition mvt.c:109
protobuf_c_boolean bool_value
Definition mvt.c:107
uint32_t id
Definition mvt.c:108
uint32_t id
Definition mvt.c:87
double double_value
Definition mvt.c:86
UT_hash_handle hh
Definition mvt.c:88
float float_value
Definition mvt.c:79
UT_hash_handle hh
Definition mvt.c:81
uint32_t id
Definition mvt.c:80
uint32_t id
Definition mvt.c:66
UT_hash_handle hh
Definition mvt.c:67
char * name
Definition mvt.c:65
int64_t sint_value
Definition mvt.c:100
uint32_t id
Definition mvt.c:101
UT_hash_handle hh
Definition mvt.c:102
uint32_t id
Definition mvt.c:73
char * string_value
Definition mvt.c:72
UT_hash_handle hh
Definition mvt.c:74
uint32_t id
Definition mvt.c:94
UT_hash_handle hh
Definition mvt.c:95
uint64_t uint_value
Definition mvt.c:93