PostGIS 3.7.0dev-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 <stdbool.h>
26#include <string.h>
27#include <float.h>
28#include <math.h>
29
30#include "mvt.h"
31#include "lwgeom_geos.h"
32
33#ifdef HAVE_LIBPROTOBUF
34#include "utils/jsonb.h"
35
36#include "lwgeom_wagyu.h"
37
38#define uthash_fatal(msg) lwerror("uthash: fatal error (out of memory)")
39#define uthash_malloc(sz) palloc(sz)
40#define uthash_free(ptr,sz) pfree(ptr)
41/* Note: set UTHASH_FUNCTION (not HASH_FUNCTION) to change the hash function */
42#include "uthash.h"
43
44#include "vector_tile.pb-c.h"
45
46#define FEATURES_CAPACITY_INITIAL 50
47
54
56{
59 MVT_RING = 3
60};
61
63{
64 char *name;
65 uint32_t id;
66 UT_hash_handle hh;
67};
68
70 VectorTile__Tile__Value value[1];
71 uint32_t id;
72 UT_hash_handle hh;
73};
74
75
76/* Must be >= 2, otherwise an overflow will occur at the first grow, as tags come in pairs */
77#define TAGS_INITIAL_CAPACITY 20
78
79/* This structure keeps track of the capacity of
80 * the tags array while the feature is being built.
81 */
83 bool has_id;
84 uint64_t id;
85
86 /* A growable array of tags */
87 size_t n_tags;
89 uint32_t *tags;
90
91 /* The geometry of the feature. */
92 VectorTile__Tile__GeomType type;
93 size_t n_geometry;
94 uint32_t *geometry;
95};
96
97static void feature_init(struct feature_builder *builder)
98{
99 builder->has_id = false;
100 builder->n_tags = 0;
102 builder->tags = palloc(TAGS_INITIAL_CAPACITY * sizeof(*builder->tags));
103 builder->type = VECTOR_TILE__TILE__GEOM_TYPE__UNKNOWN;
104 builder->n_geometry = 0;
105 builder->geometry = NULL;
106}
107
108static VectorTile__Tile__Feature *feature_build(struct feature_builder *builder)
109{
110 VectorTile__Tile__Feature *feature = palloc(sizeof(*feature));
111 vector_tile__tile__feature__init(feature);
112
113 feature->has_id = builder->has_id;
114 feature->id = builder->id;
115 feature->n_tags = builder->n_tags;
116 feature->tags = builder->tags;
117 feature->type = builder->type;
118 feature->n_geometry = builder->n_geometry;
119 feature->geometry = builder->geometry;
120 return feature;
121}
122
123static void feature_add_property(struct feature_builder *builder, uint32_t key_id, uint32_t value_id)
124{
125 size_t new_n_tags = builder->n_tags + 2;
126 if (new_n_tags >= builder->tags_capacity)
127 {
128 size_t new_capacity = builder->tags_capacity * 2;
129 builder->tags = repalloc(builder->tags, new_capacity * sizeof(*builder->tags));
130 builder->tags_capacity = new_capacity;
131 }
132
133 builder->tags[builder->n_tags] = key_id;
134 builder->tags[builder->n_tags + 1] = value_id;
135 builder->n_tags = new_n_tags;
136}
137
138
139static inline uint32_t c_int(enum mvt_cmd_id id, uint32_t count)
140{
141 return (id & 0x7) | (count << 3);
142}
143
144static inline uint32_t p_int(int32_t value)
145{
146 return (value << 1) ^ (value >> 31);
147}
148
149static uint32_t encode_ptarray(enum mvt_type type, POINTARRAY *pa, uint32_t *buffer, int32_t *px, int32_t *py)
150{
151 uint32_t offset = 0;
152 uint32_t i, c = 0;
153 int32_t dx, dy, x, y;
154 const POINT2D *p;
155
156 /* loop points and add to buffer */
157 for (i = 0; i < pa->npoints; i++)
158 {
159 /* move offset for command */
160 if (i == 0 || (i == 1 && type > MVT_POINT))
161 offset++;
162 /* skip closing point for rings */
163 if (type == MVT_RING && i == pa->npoints - 1)
164 break;
165 p = getPoint2d_cp(pa, i);
166 x = p->x;
167 y = p->y;
168 dx = x - *px;
169 dy = y - *py;
170 buffer[offset++] = p_int(dx);
171 buffer[offset++] = p_int(dy);
172 *px = x;
173 *py = y;
174 c++;
175 }
176
177 /* determine initial move and eventual line command */
178 if (type == MVT_POINT)
179 {
180 /* point or multipoint, use actual number of point count */
181 buffer[0] = c_int(CMD_MOVE_TO, c);
182 }
183 else
184 {
185 /* line or polygon, assume count 1 */
186 buffer[0] = c_int(CMD_MOVE_TO, 1);
187 /* line command with move point subtracted from count */
188 buffer[3] = c_int(CMD_LINE_TO, c - 1);
189 }
190
191 /* add close command if ring */
192 if (type == MVT_RING)
193 buffer[offset++] = c_int(CMD_CLOSE_PATH, 1);
194
195 return offset;
196}
197
198static uint32_t encode_ptarray_initial(enum mvt_type type, POINTARRAY *pa, uint32_t *buffer)
199{
200 int32_t px = 0, py = 0;
201 return encode_ptarray(type, pa, buffer, &px, &py);
202}
203
204static void encode_point(struct feature_builder *feature, LWPOINT *point)
205{
206 feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POINT;
207 feature->n_geometry = 3;
208 feature->geometry = palloc(sizeof(*feature->geometry) * 3);
210}
211
212static void encode_mpoint(struct feature_builder *feature, LWMPOINT *mpoint)
213{
214 size_t c;
215 // NOTE: inefficient shortcut LWMPOINT->LWLINE
216 LWLINE *lwline = lwline_from_lwmpoint(mpoint->srid, mpoint);
217 feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POINT;
218 c = 1 + lwline->points->npoints * 2;
219 feature->geometry = palloc(sizeof(*feature->geometry) * c);
221 lwline->points, feature->geometry);
222}
223
224static void encode_line(struct feature_builder *feature, LWLINE *lwline)
225{
226 size_t c;
227 feature->type = VECTOR_TILE__TILE__GEOM_TYPE__LINESTRING;
228 c = 2 + lwline->points->npoints * 2;
229 feature->geometry = palloc(sizeof(*feature->geometry) * c);
231 lwline->points, feature->geometry);
232}
233
234static void encode_mline(struct feature_builder *feature, LWMLINE *lwmline)
235{
236 uint32_t i;
237 int32_t px = 0, py = 0;
238 size_t c = 0, offset = 0;
239 feature->type = VECTOR_TILE__TILE__GEOM_TYPE__LINESTRING;
240 for (i = 0; i < lwmline->ngeoms; i++)
241 c += 2 + lwmline->geoms[i]->points->npoints * 2;
242 feature->geometry = palloc(sizeof(*feature->geometry) * c);
243 for (i = 0; i < lwmline->ngeoms; i++)
244 offset += encode_ptarray(MVT_LINE,
245 lwmline->geoms[i]->points,
246 feature->geometry + offset, &px, &py);
247 feature->n_geometry = offset;
248}
249
250static void encode_poly(struct feature_builder *feature, LWPOLY *lwpoly)
251{
252 uint32_t i;
253 int32_t px = 0, py = 0;
254 size_t c = 0, offset = 0;
255 feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POLYGON;
256 for (i = 0; i < lwpoly->nrings; i++)
257 c += 3 + ((lwpoly->rings[i]->npoints - 1) * 2);
258 feature->geometry = palloc(sizeof(*feature->geometry) * c);
259 for (i = 0; i < lwpoly->nrings; i++)
260 offset += encode_ptarray(MVT_RING,
261 lwpoly->rings[i],
262 feature->geometry + offset, &px, &py);
263 feature->n_geometry = offset;
264}
265
266static void encode_mpoly(struct feature_builder *feature, LWMPOLY *lwmpoly)
267{
268 uint32_t i, j;
269 int32_t px = 0, py = 0;
270 size_t c = 0, offset = 0;
271 LWPOLY *poly;
272 feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POLYGON;
273 for (i = 0; i < lwmpoly->ngeoms; i++)
274 for (j = 0; poly = lwmpoly->geoms[i], j < poly->nrings; j++)
275 c += 3 + ((poly->rings[j]->npoints - 1) * 2);
276 feature->geometry = palloc(sizeof(*feature->geometry) * c);
277 for (i = 0; i < lwmpoly->ngeoms; i++)
278 for (j = 0; poly = lwmpoly->geoms[i], j < poly->nrings; j++)
279 offset += encode_ptarray(MVT_RING,
280 poly->rings[j], feature->geometry + offset,
281 &px, &py);
282 feature->n_geometry = offset;
283}
284
285static void encode_feature_geometry(struct feature_builder *feature, LWGEOM *lwgeom)
286{
287 int type = lwgeom->type;
288
289 switch (type)
290 {
291 case POINTTYPE:
292 return encode_point(feature, (LWPOINT*)lwgeom);
293 case LINETYPE:
294 return encode_line(feature, (LWLINE*)lwgeom);
295 case POLYGONTYPE:
296 return encode_poly(feature, (LWPOLY*)lwgeom);
297 case MULTIPOINTTYPE:
298 return encode_mpoint(feature, (LWMPOINT*)lwgeom);
299 case MULTILINETYPE:
300 return encode_mline(feature, (LWMLINE*)lwgeom);
301 case MULTIPOLYGONTYPE:
302 return encode_mpoly(feature, (LWMPOLY*)lwgeom);
303 default: elog(ERROR, "encode_feature_geometry: '%s' geometry type not supported",
304 lwtype_name(type));
305 }
306}
307
308static TupleDesc get_tuple_desc(mvt_agg_context *ctx)
309{
310 Oid tupType = HeapTupleHeaderGetTypeId(ctx->row);
311 int32 tupTypmod = HeapTupleHeaderGetTypMod(ctx->row);
312 TupleDesc tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
313 return tupdesc;
314}
315
316static uint32_t get_key_index_with_size(mvt_agg_context *ctx, const char *name, size_t size)
317{
318 struct mvt_kv_key *kv;
319 HASH_FIND(hh, ctx->keys_hash, name, size, kv);
320 if (!kv)
321 return UINT32_MAX;
322 return kv->id;
323}
324
325static uint32_t add_key(mvt_agg_context *ctx, char *name)
326{
327 struct mvt_kv_key *kv;
328 size_t size = strlen(name);
329 kv = palloc(sizeof(*kv));
330 kv->id = ctx->keys_hash_i++;
331 kv->name = name;
332 HASH_ADD_KEYPTR(hh, ctx->keys_hash, name, size, kv);
333 return kv->id;
334}
335
337{
338 uint32_t i, natts;
339 bool geom_found = false;
340
341 POSTGIS_DEBUG(2, "parse_column_keys called");
342
344 natts = ctx->column_cache.tupdesc->natts;
345
346 ctx->column_cache.column_keys_index = palloc(sizeof(uint32_t) * natts);
347 ctx->column_cache.column_oid = palloc(sizeof(uint32_t) * natts);
348 ctx->column_cache.values = palloc(sizeof(Datum) * natts);
349 ctx->column_cache.nulls = palloc(sizeof(bool) * natts);
350
351 for (i = 0; i < natts; i++)
352 {
353 Oid typoid = getBaseType(TupleDescAttr(ctx->column_cache.tupdesc, i)->atttypid);
354 char *tkey = TupleDescAttr(ctx->column_cache.tupdesc, i)->attname.data;
355
356 ctx->column_cache.column_oid[i] = typoid;
357
358 if (typoid == JSONBOID)
359 {
361 continue;
362 }
363
364 if (ctx->geom_name == NULL)
365 {
366 if (!geom_found && typoid == postgis_oid(GEOMETRYOID))
367 {
368 ctx->geom_index = i;
369 geom_found = true;
370 continue;
371 }
372 }
373 else
374 {
375 if (!geom_found && strcmp(tkey, ctx->geom_name) == 0)
376 {
377 ctx->geom_index = i;
378 geom_found = true;
379 continue;
380 }
381 }
382
383 if (ctx->id_name &&
384 (ctx->id_index == UINT32_MAX) &&
385 (strcmp(tkey, ctx->id_name) == 0) &&
386 (typoid == INT2OID || typoid == INT4OID || typoid == INT8OID))
387 {
388 ctx->id_index = i;
389 }
390 else
391 {
392 ctx->column_cache.column_keys_index[i] = add_key(ctx, pstrdup(tkey));
393 }
394 }
395
396 if (!geom_found)
397 elog(ERROR, "parse_column_keys: no geometry column found");
398
399 if (ctx->id_name != NULL && ctx->id_index == UINT32_MAX)
400 elog(ERROR, "mvt_agg_transfn: Could not find column '%s' of integer type", ctx->id_name);
401}
402
404{
405 struct mvt_kv_key *kv;
406 size_t n_keys = ctx->keys_hash_i;
407 char **keys = palloc(n_keys * sizeof(*keys));
408 for (kv = ctx->keys_hash; kv != NULL; kv=kv->hh.next)
409 keys[kv->id] = kv->name;
410 ctx->layer->n_keys = n_keys;
411 ctx->layer->keys = keys;
412
413 HASH_CLEAR(hh, ctx->keys_hash);
414}
415
416#define MVT_CREATE_VALUES(hash) \
417 { \
418 struct mvt_kv_value *kv; \
419 for (kv = hash; kv != NULL; kv = kv->hh.next) \
420 { \
421 values[kv->id] = kv->value; \
422 } \
423 }
424
426{
427 VectorTile__Tile__Value **values;
428 POSTGIS_DEBUG(2, "encode_values called");
429
430 values = palloc(ctx->values_hash_i * sizeof(*values));
437
438 POSTGIS_DEBUGF(3, "encode_values n_values: %d", ctx->values_hash_i);
439 ctx->layer->n_values = ctx->values_hash_i;
440 ctx->layer->values = values;
441
442 /* Since the tupdesc is part of Postgresql cache, we need to ensure we release it when we
443 * are done with it */
444 ReleaseTupleDesc(ctx->column_cache.tupdesc);
445 memset(&ctx->column_cache, 0, sizeof(ctx->column_cache));
446
447}
448
449#define MVT_PARSE_VALUE(hash, newvalue, size, pfvaluefield, pftype) \
450 { \
451 POSTGIS_DEBUG(2, "MVT_PARSE_VALUE called"); \
452 { \
453 struct mvt_kv_value *kv; \
454 unsigned hashv; \
455 HASH_VALUE(&newvalue, size, hashv); \
456 HASH_FIND_BYHASHVALUE(hh, ctx->hash, &newvalue, size, hashv, kv); \
457 if (!kv) \
458 { \
459 POSTGIS_DEBUG(4, "MVT_PARSE_VALUE value not found"); \
460 kv = palloc(sizeof(*kv)); \
461 POSTGIS_DEBUGF(4, "MVT_PARSE_VALUE new hash key: %d", ctx->values_hash_i); \
462 kv->id = ctx->values_hash_i++; \
463 vector_tile__tile__value__init(kv->value); \
464 kv->value->pfvaluefield = newvalue; \
465 kv->value->test_oneof_case = pftype; \
466 HASH_ADD_KEYPTR_BYHASHVALUE(hh, ctx->hash, &kv->value->pfvaluefield, size, hashv, kv); \
467 } \
468 feature_add_property(feature, k, kv->id); \
469 } \
470 }
471
472#define MVT_PARSE_INT_VALUE(value) \
473 { \
474 if (value >= 0) \
475 { \
476 uint64_t cvalue = value; \
477 MVT_PARSE_VALUE(uint_values_hash, \
478 cvalue, \
479 sizeof(uint64_t), \
480 uint_value, \
481 VECTOR_TILE__TILE__VALUE__TEST_ONEOF_UINT_VALUE); \
482 } \
483 else \
484 { \
485 int64_t cvalue = value; \
486 MVT_PARSE_VALUE(sint_values_hash, \
487 cvalue, \
488 sizeof(int64_t), \
489 sint_value, \
490 VECTOR_TILE__TILE__VALUE__TEST_ONEOF_SINT_VALUE); \
491 } \
492 }
493
494#define MVT_PARSE_DATUM(type, datumfunc, hash, size, pfvaluefield, pftype) \
495 { \
496 type value = datumfunc(datum); \
497 MVT_PARSE_VALUE(hash, value, size, pfvaluefield, pftype); \
498 }
499
500#define MVT_PARSE_INT_DATUM(type, datumfunc) \
501{ \
502 type value = datumfunc(datum); \
503 MVT_PARSE_INT_VALUE(value); \
504}
505
506static bool
507add_value_as_string_with_size(mvt_agg_context *ctx, struct feature_builder *feature, char *value, size_t size, uint32_t k)
508{
509 bool kept = false;
510 struct mvt_kv_value *kv;
511 unsigned hashv;
512 HASH_VALUE(value, size, hashv);
513 POSTGIS_DEBUG(2, "add_value_as_string called");
514 HASH_FIND_BYHASHVALUE(hh, ctx->string_values_hash, value, size, hashv, kv);
515 if (!kv)
516 {
517 POSTGIS_DEBUG(4, "add_value_as_string value not found");
518 kv = palloc(sizeof(*kv));
519 POSTGIS_DEBUGF(4, "add_value_as_string new hash key: %d",
520 ctx->values_hash_i);
521 kv->id = ctx->values_hash_i++;
522 vector_tile__tile__value__init(kv->value);
523 kv->value->string_value = value;
524 kv->value->test_oneof_case = VECTOR_TILE__TILE__VALUE__TEST_ONEOF_STRING_VALUE;
525 HASH_ADD_KEYPTR_BYHASHVALUE(hh, ctx->string_values_hash, kv->value->string_value, size, hashv, kv);
526 kept = true;
527 }
528
529 feature_add_property(feature, k, kv->id);
530 return kept;
531}
532
533/* Adds a string to the stored values. Gets ownership of the string */
534static void
535add_value_as_string(mvt_agg_context *ctx, struct feature_builder *feature, char *value, uint32_t k)
536{
537 bool kept = add_value_as_string_with_size(ctx, feature, value, strlen(value), k);
538 if (!kept)
539 pfree(value);
540}
541
542/* Adds a Datum to the stored values as a string. */
543static inline void
544parse_datum_as_string(mvt_agg_context *ctx, struct feature_builder *feature, Oid typoid, Datum datum, uint32_t k)
545{
546 Oid foutoid;
547 bool typisvarlena;
548 char *value;
549 POSTGIS_DEBUG(2, "parse_value_as_string called");
550 getTypeOutputInfo(typoid, &foutoid, &typisvarlena);
551 value = OidOutputFunctionCall(foutoid, datum);
552 POSTGIS_DEBUGF(4, "parse_value_as_string value: %s", value);
553 add_value_as_string(ctx, feature, value, k);
554}
555
556static void parse_jsonb(mvt_agg_context *ctx, struct feature_builder *feature, Jsonb *jb)
557{
558 JsonbIterator *it;
559 JsonbValue v;
560 bool skipNested = false;
561 JsonbIteratorToken r;
562 uint32_t k;
563
564 if (!JB_ROOT_IS_OBJECT(jb))
565 return;
566
567 it = JsonbIteratorInit(&jb->root);
568
569 while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
570 {
571 skipNested = true;
572
573 if (r == WJB_KEY && v.type != jbvNull)
574 {
575
576 k = get_key_index_with_size(ctx, v.val.string.val, v.val.string.len);
577 if (k == UINT32_MAX)
578 {
579 char *key = palloc(v.val.string.len + 1);
580 memcpy(key, v.val.string.val, v.val.string.len);
581 key[v.val.string.len] = '\0';
582 k = add_key(ctx, key);
583 }
584
585 r = JsonbIteratorNext(&it, &v, skipNested);
586
587 if (v.type == jbvString)
588 {
589 char *value = palloc(v.val.string.len + 1);
590 memcpy(value, v.val.string.val, v.val.string.len);
591 value[v.val.string.len] = '\0';
592 add_value_as_string(ctx, feature, value, k);
593 }
594 else if (v.type == jbvBool)
595 {
596 MVT_PARSE_VALUE(bool_values_hash,
597 v.val.boolean,
598 sizeof(protobuf_c_boolean),
599 bool_value,
600 VECTOR_TILE__TILE__VALUE__TEST_ONEOF_BOOL_VALUE);
601 }
602 else if (v.type == jbvNumeric)
603 {
604 char *str;
605 double d;
606 long l;
607 str = DatumGetCString(DirectFunctionCall1(numeric_out,
608 PointerGetDatum(v.val.numeric)));
609 d = strtod(str, NULL);
610 l = strtol(str, NULL, 10);
611
612 if (fabs(d - (double)l) > FLT_EPSILON)
613 {
614 MVT_PARSE_VALUE(double_values_hash,
615 d,
616 sizeof(double),
617 double_value,
618 VECTOR_TILE__TILE__VALUE__TEST_ONEOF_DOUBLE_VALUE);
619 }
620 else
621 {
623 }
624 }
625 }
626 }
627}
628
632static void set_feature_id(mvt_agg_context *ctx, struct feature_builder *feature, Datum datum, bool isNull)
633{
634 Oid typoid = ctx->column_cache.column_oid[ctx->id_index];
635 int64_t value = INT64_MIN;
636
637 if (isNull)
638 {
639 POSTGIS_DEBUG(3, "set_feature_id: Ignored null value");
640 return;
641 }
642
643 switch (typoid)
644 {
645 case INT2OID:
646 value = DatumGetInt16(datum);
647 break;
648 case INT4OID:
649 value = DatumGetInt32(datum);
650 break;
651 case INT8OID:
652 value = DatumGetInt64(datum);
653 break;
654 default:
655 elog(ERROR, "set_feature_id: Feature id type does not match");
656 }
657
658 if (value < 0)
659 {
660 POSTGIS_DEBUG(3, "set_feature_id: Ignored negative value");
661 return;
662 }
663
664 feature->has_id = true;
665 feature->id = (uint64_t) value;
666}
667
668static void parse_values(mvt_agg_context *ctx, struct feature_builder *feature)
669{
670 uint32_t i;
672 uint32_t natts = (uint32_t) cc.tupdesc->natts;
673
674 HeapTupleData tuple;
675
676 POSTGIS_DEBUG(2, "parse_values called");
677
678 /* Build a temporary HeapTuple control structure */
679 tuple.t_len = HeapTupleHeaderGetDatumLength(ctx->row);
680 ItemPointerSetInvalid(&(tuple.t_self));
681 tuple.t_tableOid = InvalidOid;
682 tuple.t_data = ctx->row;
683
684 /* We use heap_deform_tuple as it costs only O(N) vs O(N^2) of GetAttributeByNum */
685 heap_deform_tuple(&tuple, cc.tupdesc, cc.values, cc.nulls);
686
687 POSTGIS_DEBUGF(3, "parse_values natts: %d", natts);
688
689 for (i = 0; i < natts; i++)
690 {
691 char *key;
692 Oid typoid;
693 uint32_t k;
694 Datum datum = cc.values[i];
695
696 if (i == ctx->geom_index)
697 continue;
698
699 if (i == ctx->id_index)
700 {
701 set_feature_id(ctx, feature, datum, cc.nulls[i]);
702 continue;
703 }
704
705 if (cc.nulls[i])
706 {
707 POSTGIS_DEBUG(3, "parse_values isnull detected");
708 continue;
709 }
710
711 key = TupleDescAttr(cc.tupdesc, i)->attname.data;
712 k = cc.column_keys_index[i];
713 typoid = cc.column_oid[i];
714
715 if (k == UINT32_MAX && typoid != JSONBOID)
716 elog(ERROR, "parse_values: unexpectedly could not find parsed key name '%s'", key);
717 if (typoid == JSONBOID)
718 {
719 parse_jsonb(ctx, feature, DatumGetJsonbP(datum));
720 continue;
721 }
722
723 switch (typoid)
724 {
725 case BOOLOID:
726 MVT_PARSE_DATUM(protobuf_c_boolean,
727 DatumGetBool,
728 bool_values_hash,
729 sizeof(protobuf_c_boolean),
730 bool_value,
731 VECTOR_TILE__TILE__VALUE__TEST_ONEOF_BOOL_VALUE);
732 break;
733 case INT2OID:
734 MVT_PARSE_INT_DATUM(int16_t, DatumGetInt16);
735 break;
736 case INT4OID:
737 MVT_PARSE_INT_DATUM(int32_t, DatumGetInt32);
738 break;
739 case INT8OID:
740 MVT_PARSE_INT_DATUM(int64_t, DatumGetInt64);
741 break;
742 case FLOAT4OID:
743 MVT_PARSE_DATUM(float,
744 DatumGetFloat4,
745 float_values_hash,
746 sizeof(float),
747 float_value,
748 VECTOR_TILE__TILE__VALUE__TEST_ONEOF_FLOAT_VALUE);
749 break;
750 case FLOAT8OID:
751 MVT_PARSE_DATUM(double,
752 DatumGetFloat8,
753 double_values_hash,
754 sizeof(double),
755 double_value,
756 VECTOR_TILE__TILE__VALUE__TEST_ONEOF_DOUBLE_VALUE);
757 break;
758 case TEXTOID:
759 add_value_as_string(ctx, feature, text_to_cstring(DatumGetTextP(datum)), k);
760 break;
761 case CSTRINGOID:
762 add_value_as_string(ctx, feature, DatumGetCString(datum), k);
763 break;
764 default:
765 parse_datum_as_string(ctx, feature, typoid, datum, k);
766 break;
767 }
768 }
769}
770
771/* For a given geometry, look for the highest dimensional basic type, that is,
772 * point, line or polygon */
773static uint8_t
775{
776 switch(geom->type)
777 {
778 case POINTTYPE:
779 case LINETYPE:
780 case POLYGONTYPE:
781 return geom->type;
782 case TRIANGLETYPE:
783 return POLYGONTYPE;
784 case MULTIPOINTTYPE:
785 case MULTILINETYPE:
786 case MULTIPOLYGONTYPE:
787 return geom->type - 3; /* Based on LWTYPE positions */
788 case COLLECTIONTYPE:
789 case TINTYPE:
790 {
791 uint32_t i;
792 uint8_t type = 0;
793 LWCOLLECTION *g = (LWCOLLECTION*)geom;
794 for (i = 0; i < g->ngeoms; i++)
795 {
796 LWGEOM *sg = g->geoms[i];
797 type = Max(type, lwgeom_get_basic_type(sg));
798 }
799 return type;
800 }
801 default:
802 elog(ERROR, "%s: Invalid type (%d)", __func__, geom->type);
803 }
804}
805
806
813static inline LWGEOM *
814lwgeom_to_basic_type(LWGEOM *geom, uint8_t original_type)
815{
816 LWGEOM *geom_out = geom;
817 if (lwgeom_get_type(geom) == COLLECTIONTYPE)
818 {
819 LWCOLLECTION *g = (LWCOLLECTION*)geom;
820 geom_out = (LWGEOM *)lwcollection_extract(g, original_type);
821 }
822
823 /* If a collection only contains 1 geometry return than instead */
824 if (lwgeom_is_collection(geom_out))
825 {
826 LWCOLLECTION *g = (LWCOLLECTION *)geom_out;
827 if (g->ngeoms == 1)
828 {
829 geom_out = g->geoms[0];
830 }
831 }
832
833 geom_out->srid = geom->srid;
834 return geom_out;
835}
836
837/* Clips a geometry using lwgeom_clip_by_rect. Might return NULL */
838static LWGEOM *
840{
841 LWGEOM *geom_clipped;
842 GBOX geom_box;
843
844 gbox_init(&geom_box);
845 FLAGS_SET_GEODETIC(geom_box.flags, 0);
846 lwgeom_calculate_gbox(lwg_in, &geom_box);
847
848 if (!gbox_overlaps_2d(&geom_box, clip_box))
849 {
850 POSTGIS_DEBUG(3, "mvt_geom: geometry outside clip box");
851 return NULL;
852 }
853
854 if (gbox_contains_2d(clip_box, &geom_box))
855 {
856 POSTGIS_DEBUG(3, "mvt_geom: geometry contained fully inside the box");
857 return lwg_in;
858 }
859
860 geom_clipped = lwgeom_clip_by_rect(lwg_in, clip_box->xmin, clip_box->ymin, clip_box->xmax, clip_box->ymax);
861 if (!geom_clipped || lwgeom_is_empty(geom_clipped))
862 return NULL;
863 return geom_clipped;
864}
865
866/* Clips a geometry for MVT using GEOS.
867 * Does NOT work for polygons
868 * Might return NULL
869 */
870static LWGEOM *
871mvt_clip_and_validate_geos(LWGEOM *lwgeom, uint8_t basic_type, uint32_t extent, uint32_t buffer, bool clip_geom)
872{
873 LWGEOM *ng = lwgeom;
874 assert(lwgeom->type != POLYGONTYPE);
875 assert(lwgeom->type != MULTIPOLYGONTYPE);
876
877 if (clip_geom)
878 {
879 gridspec grid = {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0};
880 GBOX bgbox;
881 bgbox.xmax = bgbox.ymax = (double)extent + (double)buffer;
882 bgbox.xmin = bgbox.ymin = -(double)buffer;
883 bgbox.flags = 0;
884
885 ng = mvt_unsafe_clip_by_box(ng, &bgbox);
886
887 /* Make sure there is no pending float values (clipping can do that) */
888 lwgeom_grid_in_place(ng, &grid);
889 }
890
891 return ng;
892}
893
894static LWGEOM *
895mvt_clip_and_validate(LWGEOM *lwgeom, uint8_t basic_type, uint32_t extent, uint32_t buffer, bool clip_geom)
896{
897 GBOX clip_box = {0};
898 LWGEOM *clipped_lwgeom;
899
900 /* Wagyu only supports polygons. Default to geos for other types */
901 lwgeom = lwgeom_to_basic_type(lwgeom, POLYGONTYPE);
902 if (lwgeom->type != POLYGONTYPE && lwgeom->type != MULTIPOLYGONTYPE)
903 {
904 return mvt_clip_and_validate_geos(lwgeom, basic_type, extent, buffer, clip_geom);
905 }
906
907 if (!clip_geom)
908 {
909 /* With clipping disabled, we request a clip with the geometry bbox to force validation */
910 lwgeom_calculate_gbox(lwgeom, &clip_box);
911 }
912 else
913 {
914 clip_box.xmax = clip_box.ymax = (double)extent + (double)buffer;
915 clip_box.xmin = clip_box.ymin = -(double)buffer;
916 }
917
918 clipped_lwgeom = lwgeom_wagyu_clip_by_box(lwgeom, &clip_box);
919
920 return clipped_lwgeom;
921}
922
931LWGEOM *mvt_geom(LWGEOM *lwgeom, const GBOX *gbox, uint32_t extent, uint32_t buffer,
932 bool clip_geom)
933{
934 AFFINE affine = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
935 gridspec grid = {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0};
936 double width = gbox->xmax - gbox->xmin;
937 double height = gbox->ymax - gbox->ymin;
938 double fx, fy;
939 const uint8_t basic_type = lwgeom_get_basic_type(lwgeom);
940 int preserve_collapsed = LW_FALSE;
941 POSTGIS_DEBUG(2, "mvt_geom called");
942
943 /* Simplify it as soon as possible */
944 lwgeom = lwgeom_to_basic_type(lwgeom, basic_type);
945
946 /* Short circuit out on EMPTY */
947 if (lwgeom_is_empty(lwgeom))
948 return NULL;
949
950 fx = extent / width;
951 fy = -(extent / height);
952
953 /* If geometry has disappeared, you're done */
954 if (lwgeom_is_empty(lwgeom))
955 return NULL;
956
957 /* transform to tile coordinate space */
958 affine.afac = fx;
959 affine.efac = fy;
960 affine.ifac = 1;
961 affine.xoff = -gbox->xmin * fx;
962 affine.yoff = -gbox->ymax * fy;
963 lwgeom_affine(lwgeom, &affine);
964
965 /* Snap to integer precision, removing duplicate points */
966 lwgeom_grid_in_place(lwgeom, &grid);
967
968 /* Remove points on straight lines */
969 lwgeom_simplify_in_place(lwgeom, 0, preserve_collapsed);
970
971 /* Remove duplicates in multipoints */
972 if (lwgeom->type == MULTIPOINTTYPE)
974
975 if (!lwgeom || lwgeom_is_empty(lwgeom))
976 return NULL;
977
978 lwgeom = mvt_clip_and_validate(lwgeom, basic_type, extent, buffer, clip_geom);
979 if (!lwgeom || lwgeom_is_empty(lwgeom))
980 return NULL;
981
982 return lwgeom;
983}
984
989{
990 VectorTile__Tile__Layer *layer;
991
992 POSTGIS_DEBUG(2, "mvt_agg_init_context called");
993
994 if (ctx->extent == 0)
995 elog(ERROR, "mvt_agg_init_context: extent cannot be 0");
996
997 ctx->tile = NULL;
999 ctx->keys_hash = NULL;
1000 ctx->string_values_hash = NULL;
1001 ctx->float_values_hash = NULL;
1002 ctx->double_values_hash = NULL;
1003 ctx->uint_values_hash = NULL;
1004 ctx->sint_values_hash = NULL;
1005 ctx->bool_values_hash = NULL;
1006 ctx->values_hash_i = 0;
1007 ctx->keys_hash_i = 0;
1008 ctx->id_index = UINT32_MAX;
1009 ctx->geom_index = UINT32_MAX;
1010
1011 memset(&ctx->column_cache, 0, sizeof(ctx->column_cache));
1012
1013 layer = palloc(sizeof(*layer));
1014 vector_tile__tile__layer__init(layer);
1015 layer->version = 2;
1016 layer->name = ctx->name;
1017 layer->extent = ctx->extent;
1018 layer->features = palloc(ctx->features_capacity * sizeof(*layer->features));
1019
1020 ctx->layer = layer;
1021}
1022
1031{
1032 bool isnull = false;
1033 Datum datum;
1034 GSERIALIZED *gs;
1035 LWGEOM *lwgeom;
1037 VectorTile__Tile__Layer *layer = ctx->layer;
1038 POSTGIS_DEBUG(2, "mvt_agg_transfn called");
1039
1040 /* geom_index is the cached index of the geometry. if missing, it needs to be initialized */
1041 if (ctx->geom_index == UINT32_MAX)
1042 parse_column_keys(ctx);
1043
1044 /* Get the geometry column */
1045 datum = GetAttributeByNum(ctx->row, ctx->geom_index + 1, &isnull);
1046 if (isnull) /* Skip rows that have null geometry */
1047 return;
1048
1049 /* Allocate a new feature object */
1051
1052 /* Deserialize the geometry */
1053 gs = (GSERIALIZED *) PG_DETOAST_DATUM(datum);
1054 lwgeom = lwgeom_from_gserialized(gs);
1055
1056 /* Set the geometry of the feature */
1058 lwgeom_free(lwgeom);
1059 // TODO: free detoasted datum?
1060
1061 /* Parse properties */
1063
1064 /* Grow the features array of the layer */
1065 POSTGIS_DEBUGF(3, "mvt_agg_transfn encoded feature count: %zd", layer->n_features);
1066 if (layer->n_features >= ctx->features_capacity)
1067 {
1068 size_t new_capacity = ctx->features_capacity * 2;
1069 layer->features = repalloc(layer->features, new_capacity *
1070 sizeof(*layer->features));
1071 ctx->features_capacity = new_capacity;
1072 POSTGIS_DEBUGF(3, "mvt_agg_transfn new_capacity: %zd", new_capacity);
1073 }
1074
1075 /* Build and add the feature to the layer */
1076 layer->features[layer->n_features++] = feature_build(&feature_builder);
1077}
1078
1079static VectorTile__Tile * mvt_ctx_to_tile(mvt_agg_context *ctx)
1080{
1081 int n_layers = 1;
1082 VectorTile__Tile *tile;
1083 encode_keys(ctx);
1084 encode_values(ctx);
1085
1086 tile = palloc(sizeof(VectorTile__Tile));
1087 vector_tile__tile__init(tile);
1088 tile->layers = palloc(sizeof(VectorTile__Tile__Layer*) * n_layers);
1089 tile->layers[0] = ctx->layer;
1090 tile->n_layers = n_layers;
1091 return tile;
1092}
1093
1095{
1096 /* Fill out the tile slot, if it's not already filled. */
1097 /* We should only have a filled slot when all the work of building */
1098 /* out the data is complete, so after a serialize/deserialize cycle */
1099 /* or after a context combine */
1100 size_t len;
1101 bytea *ba;
1102
1103 if (!ctx->tile)
1104 {
1105 ctx->tile = mvt_ctx_to_tile(ctx);
1106 }
1107
1108 /* Zero features => empty bytea output */
1109 if (ctx && ctx->layer && ctx->layer->n_features == 0)
1110 {
1111 bytea* ba_empty = palloc(VARHDRSZ);
1112 SET_VARSIZE(ba_empty, VARHDRSZ);
1113 return ba_empty;
1114 }
1115
1116 /* Serialize the Tile */
1117 len = VARHDRSZ + vector_tile__tile__get_packed_size(ctx->tile);
1118 ba = palloc(len);
1119 vector_tile__tile__pack(ctx->tile, (uint8_t*)VARDATA(ba));
1120 SET_VARSIZE(ba, len);
1121 return ba;
1122}
1123
1124
1126{
1127 return mvt_ctx_to_bytea(ctx);
1128}
1129
1130static void * mvt_allocator(__attribute__((__unused__)) void *data, size_t size)
1131{
1132 return palloc(size);
1133}
1134
1135static void mvt_deallocator(__attribute__((__unused__)) void *data, void *ptr)
1136{
1137 return pfree(ptr);
1138}
1139
1141{
1142 ProtobufCAllocator allocator =
1143 {
1146 NULL
1147 };
1148
1149 size_t len = VARSIZE_ANY_EXHDR(ba);
1150 VectorTile__Tile *tile = vector_tile__tile__unpack(&allocator, len, (uint8_t*)VARDATA(ba));
1151 mvt_agg_context *ctx = palloc(sizeof(mvt_agg_context));
1152 memset(ctx, 0, sizeof(mvt_agg_context));
1153 ctx->tile = tile;
1154 return ctx;
1155}
1156
1164static VectorTile__Tile__Layer *
1165vectortile_layer_combine(VectorTile__Tile__Layer *layer, VectorTile__Tile__Layer *layer2)
1166{
1167 const uint32_t key_offset = layer->n_keys;
1168 const uint32_t value_offset = layer->n_values;
1169 const uint32_t feature_offset = layer->n_features;
1170
1171 if (!layer->n_keys)
1172 {
1173 layer->keys = layer2->keys;
1174 layer->n_keys = layer2->n_keys;
1175 }
1176 else if (layer2->n_keys)
1177 {
1178 layer->keys = repalloc(layer->keys, sizeof(char *) * (layer->n_keys + layer2->n_keys));
1179 memcpy(&layer->keys[key_offset], layer2->keys, sizeof(char *) * layer2->n_keys);
1180 layer->n_keys += layer2->n_keys;
1181 }
1182
1183 if (!layer->n_values)
1184 {
1185 layer->values = layer2->values;
1186 layer->n_values = layer2->n_values;
1187 }
1188 else if (layer2->n_values)
1189 {
1190 layer->values =
1191 repalloc(layer->values, sizeof(VectorTile__Tile__Value *) * (layer->n_values + layer2->n_values));
1192 memcpy(
1193 &layer->values[value_offset], layer2->values, sizeof(VectorTile__Tile__Value *) * layer2->n_values);
1194 layer->n_values += layer2->n_values;
1195 }
1196
1197 if (!layer->n_features)
1198 {
1199 layer->features = layer2->features;
1200 layer->n_features = layer2->n_features;
1201 }
1202 else if (layer2->n_features)
1203 {
1204 layer->features = repalloc(
1205 layer->features, sizeof(VectorTile__Tile__Feature *) * (layer->n_features + layer2->n_features));
1206 memcpy(&layer->features[feature_offset], layer2->features, sizeof(char *) * layer2->n_features);
1207 layer->n_features += layer2->n_features;
1208 /* We need to adapt the indexes of the copied features */
1209 for (uint32_t i = feature_offset; i < layer->n_features; i++)
1210 {
1211 for (uint32_t t = 0; t < layer->features[i]->n_tags; t += 2)
1212 {
1213 layer->features[i]->tags[t] += key_offset;
1214 layer->features[i]->tags[t + 1] += value_offset;
1215 }
1216 }
1217 }
1218
1219 return layer;
1220}
1221
1222
1223static VectorTile__Tile *
1224vectortile_tile_combine(VectorTile__Tile *tile1, VectorTile__Tile *tile2)
1225{
1226 uint32_t i, j;
1227 VectorTile__Tile *tile;
1228
1229 /* Hopelessly messing up memory ownership here */
1230 if (tile1->n_layers == 0 && tile2->n_layers == 0)
1231 return tile1;
1232 else if (tile1->n_layers == 0)
1233 return tile2;
1234 else if (tile2->n_layers == 0)
1235 return tile1;
1236
1237 tile = palloc(sizeof(VectorTile__Tile));
1238 vector_tile__tile__init(tile);
1239 tile->layers = palloc(sizeof(void*));
1240 tile->n_layers = 0;
1241
1242 /* Merge all matching layers in the files (basically always only one) */
1243 for (i = 0; i < tile1->n_layers; i++)
1244 {
1245 for (j = 0; j < tile2->n_layers; j++)
1246 {
1247 VectorTile__Tile__Layer *l1 = tile1->layers[i];
1248 VectorTile__Tile__Layer *l2 = tile2->layers[j];
1249 if (strcmp(l1->name, l2->name)==0)
1250 {
1251 VectorTile__Tile__Layer *layer = vectortile_layer_combine(l1, l2);
1252 if (!layer)
1253 continue;
1254 tile->layers[tile->n_layers++] = layer;
1255 /* Add a spare slot at the end of the array */
1256 tile->layers = repalloc(tile->layers, (tile->n_layers+1) * sizeof(void*));
1257 }
1258 }
1259 }
1260 return tile;
1261}
1262
1264{
1265 if (ctx1 || ctx2)
1266 {
1267 if (ctx1 && ! ctx2) return ctx1;
1268 if (ctx2 && ! ctx1) return ctx2;
1269 if (ctx1 && ctx2 && ctx1->tile && ctx2->tile)
1270 {
1271 mvt_agg_context *ctxnew = palloc(sizeof(mvt_agg_context));
1272 memset(ctxnew, 0, sizeof(mvt_agg_context));
1273 ctxnew->tile = vectortile_tile_combine(ctx1->tile, ctx2->tile);
1274 return ctxnew;
1275 }
1276 else
1277 {
1278 elog(DEBUG2, "ctx1->tile = %p", ctx1->tile);
1279 elog(DEBUG2, "ctx2->tile = %p", ctx2->tile);
1280 elog(ERROR, "%s: unable to combine contexts where tile attribute is null", __func__);
1281 return NULL;
1282 }
1283 }
1284 else
1285 {
1286 return NULL;
1287 }
1288}
1289
1297{
1298 return mvt_ctx_to_bytea(ctx);
1299}
1300
1301
1302#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.
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:94
#define COLLECTIONTYPE
Definition liblwgeom.h:108
int lwgeom_simplify_in_place(LWGEOM *igeom, double dist, int preserve_collapsed)
Definition lwgeom.c:1851
void lwgeom_free(LWGEOM *geom)
Definition lwgeom.c:1246
#define MULTILINETYPE
Definition liblwgeom.h:106
#define LINETYPE
Definition liblwgeom.h:103
LWGEOM * lwgeom_clip_by_rect(const LWGEOM *geom1, double x0, double y0, double x1, double y1)
#define MULTIPOINTTYPE
Definition liblwgeom.h:105
int lwgeom_remove_repeated_points_in_place(LWGEOM *in, double tolerance)
Definition lwgeom.c:1695
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition liblwgeom.h:102
#define TINTYPE
Definition liblwgeom.h:116
#define MULTIPOLYGONTYPE
Definition liblwgeom.h:107
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM contains sub-geometries or not This basically just checks that the struct ...
Definition lwgeom.c:1125
#define POLYGONTYPE
Definition liblwgeom.h:104
void lwgeom_affine(LWGEOM *geom, const AFFINE *affine)
Definition lwgeom.c:2111
#define __attribute__(x)
Definition liblwgeom.h:228
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:783
#define TRIANGLETYPE
Definition liblwgeom.h:115
#define FLAGS_SET_GEODETIC(flags, value)
Definition liblwgeom.h:175
void lwgeom_grid_in_place(LWGEOM *lwgeom, gridspec *grid)
Definition lwgeom.c:2317
LWCOLLECTION * lwcollection_extract(const LWCOLLECTION *col, uint32_t type)
LWLINE * lwline_from_lwmpoint(int32_t srid, const LWMPOINT *mpoint)
Definition lwline.c:285
#define str(s)
#define UINT32_MAX
static uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition lwinline.h:141
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:199
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:97
static uint32_t get_key_index_with_size(mvt_agg_context *ctx, const char *name, size_t size)
Definition mvt.c:316
static VectorTile__Tile * mvt_ctx_to_tile(mvt_agg_context *ctx)
Definition mvt.c:1079
static LWGEOM * mvt_clip_and_validate(LWGEOM *lwgeom, uint8_t basic_type, uint32_t extent, uint32_t buffer, bool clip_geom)
Definition mvt.c:895
#define MVT_PARSE_INT_VALUE(value)
Definition mvt.c:472
static void parse_values(mvt_agg_context *ctx, struct feature_builder *feature)
Definition mvt.c:668
static void encode_mpoly(struct feature_builder *feature, LWMPOLY *lwmpoly)
Definition mvt.c:266
static void encode_line(struct feature_builder *feature, LWLINE *lwline)
Definition mvt.c:224
#define MVT_PARSE_DATUM(type, datumfunc, hash, size, pfvaluefield, pftype)
Definition mvt.c:494
static bool add_value_as_string_with_size(mvt_agg_context *ctx, struct feature_builder *feature, char *value, size_t size, uint32_t k)
Definition mvt.c:507
mvt_agg_context * mvt_ctx_deserialize(const bytea *ba)
Definition mvt.c:1140
#define FEATURES_CAPACITY_INITIAL
Definition mvt.c:46
#define TAGS_INITIAL_CAPACITY
Definition mvt.c:77
static void encode_keys(mvt_agg_context *ctx)
Definition mvt.c:403
bytea * mvt_ctx_serialize(mvt_agg_context *ctx)
Definition mvt.c:1125
#define MVT_PARSE_INT_DATUM(type, datumfunc)
Definition mvt.c:500
#define MVT_CREATE_VALUES(hash)
Definition mvt.c:416
static void parse_datum_as_string(mvt_agg_context *ctx, struct feature_builder *feature, Oid typoid, Datum datum, uint32_t k)
Definition mvt.c:544
static void feature_add_property(struct feature_builder *builder, uint32_t key_id, uint32_t value_id)
Definition mvt.c:123
mvt_cmd_id
Definition mvt.c:49
@ CMD_MOVE_TO
Definition mvt.c:50
@ CMD_CLOSE_PATH
Definition mvt.c:52
@ CMD_LINE_TO
Definition mvt.c:51
static void mvt_deallocator(__attribute__((__unused__)) void *data, void *ptr)
Definition mvt.c:1135
static VectorTile__Tile__Feature * feature_build(struct feature_builder *builder)
Definition mvt.c:108
static void parse_column_keys(mvt_agg_context *ctx)
Definition mvt.c:336
static void encode_mline(struct feature_builder *feature, LWMLINE *lwmline)
Definition mvt.c:234
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:871
static uint32_t encode_ptarray_initial(enum mvt_type type, POINTARRAY *pa, uint32_t *buffer)
Definition mvt.c:198
static uint32_t c_int(enum mvt_cmd_id id, uint32_t count)
Definition mvt.c:139
static void set_feature_id(mvt_agg_context *ctx, struct feature_builder *feature, Datum datum, bool isNull)
Sets the feature id.
Definition mvt.c:632
static void encode_poly(struct feature_builder *feature, LWPOLY *lwpoly)
Definition mvt.c:250
mvt_agg_context * mvt_ctx_combine(mvt_agg_context *ctx1, mvt_agg_context *ctx2)
Definition mvt.c:1263
static uint32_t add_key(mvt_agg_context *ctx, char *name)
Definition mvt.c:325
static VectorTile__Tile__Layer * vectortile_layer_combine(VectorTile__Tile__Layer *layer, VectorTile__Tile__Layer *layer2)
Combine 2 layers.
Definition mvt.c:1165
void mvt_agg_init_context(mvt_agg_context *ctx)
Initialize aggregation context.
Definition mvt.c:988
static void encode_point(struct feature_builder *feature, LWPOINT *point)
Definition mvt.c:204
static void encode_mpoint(struct feature_builder *feature, LWMPOINT *mpoint)
Definition mvt.c:212
static TupleDesc get_tuple_desc(mvt_agg_context *ctx)
Definition mvt.c:308
mvt_type
Definition mvt.c:56
@ MVT_POINT
Definition mvt.c:57
@ MVT_LINE
Definition mvt.c:58
@ MVT_RING
Definition mvt.c:59
#define MVT_PARSE_VALUE(hash, newvalue, size, pfvaluefield, pftype)
Definition mvt.c:449
static void add_value_as_string(mvt_agg_context *ctx, struct feature_builder *feature, char *value, uint32_t k)
Definition mvt.c:535
static uint8_t lwgeom_get_basic_type(LWGEOM *geom)
Definition mvt.c:774
static VectorTile__Tile * vectortile_tile_combine(VectorTile__Tile *tile1, VectorTile__Tile *tile2)
Definition mvt.c:1224
static void * mvt_allocator(__attribute__((__unused__)) void *data, size_t size)
Definition mvt.c:1130
static void parse_jsonb(mvt_agg_context *ctx, struct feature_builder *feature, Jsonb *jb)
Definition mvt.c:556
static bytea * mvt_ctx_to_bytea(mvt_agg_context *ctx)
Definition mvt.c:1094
void mvt_agg_transfn(mvt_agg_context *ctx)
Aggregation step.
Definition mvt.c:1030
bytea * mvt_agg_finalfn(mvt_agg_context *ctx)
Finalize aggregation.
Definition mvt.c:1296
static void feature_init(struct feature_builder *builder)
Definition mvt.c:97
static void encode_values(mvt_agg_context *ctx)
Definition mvt.c:425
static uint32_t p_int(int32_t value)
Definition mvt.c:144
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:931
static LWGEOM * lwgeom_to_basic_type(LWGEOM *geom, uint8_t original_type)
In place process a collection to find a concrete geometry object and expose that as the actual object...
Definition mvt.c:814
static uint32_t encode_ptarray(enum mvt_type type, POINTARRAY *pa, uint32_t *buffer, int32_t *px, int32_t *py)
Definition mvt.c:149
static LWGEOM * mvt_unsafe_clip_by_box(LWGEOM *lwg_in, GBOX *clip_box)
Definition mvt.c:839
static void encode_feature_geometry(struct feature_builder *feature, LWGEOM *lwgeom)
Definition mvt.c:285
Datum buffer(PG_FUNCTION_ARGS)
unsigned int int32
Definition shpopen.c:54
double ifac
Definition liblwgeom.h:332
double xoff
Definition liblwgeom.h:332
double afac
Definition liblwgeom.h:332
double efac
Definition liblwgeom.h:332
double yoff
Definition liblwgeom.h:332
double ymax
Definition liblwgeom.h:357
double xmax
Definition liblwgeom.h:355
double ymin
Definition liblwgeom.h:356
double xmin
Definition liblwgeom.h:354
lwflags_t flags
Definition liblwgeom.h:353
uint32_t ngeoms
Definition liblwgeom.h:580
LWGEOM ** geoms
Definition liblwgeom.h:575
uint8_t type
Definition liblwgeom.h:462
int32_t srid
Definition liblwgeom.h:460
POINTARRAY * points
Definition liblwgeom.h:483
LWLINE ** geoms
Definition liblwgeom.h:547
uint32_t ngeoms
Definition liblwgeom.h:552
int32_t srid
Definition liblwgeom.h:534
uint32_t ngeoms
Definition liblwgeom.h:566
LWPOLY ** geoms
Definition liblwgeom.h:561
POINTARRAY * point
Definition liblwgeom.h:471
POINTARRAY ** rings
Definition liblwgeom.h:519
uint32_t nrings
Definition liblwgeom.h:524
double y
Definition liblwgeom.h:390
double x
Definition liblwgeom.h:390
uint32_t npoints
Definition liblwgeom.h:427
size_t n_tags
Definition mvt.c:87
uint32_t * tags
Definition mvt.c:89
VectorTile__Tile__GeomType type
Definition mvt.c:92
bool has_id
Definition mvt.c:83
size_t n_geometry
Definition mvt.c:93
uint64_t id
Definition mvt.c:84
uint32_t * geometry
Definition mvt.c:94
size_t tags_capacity
Definition mvt.c:88
Snap-to-grid.
Definition liblwgeom.h:1400
uint32_t geom_index
Definition mvt.h:72
uint32_t values_hash_i
Definition mvt.h:94
char * id_name
Definition mvt.h:67
struct mvt_kv_value * uint_values_hash
Definition mvt.h:89
char * geom_name
Definition mvt.h:71
size_t features_capacity
Definition mvt.h:78
VectorTile__Tile__Layer * layer
Definition mvt.h:76
uint32_t keys_hash_i
Definition mvt.h:96
struct mvt_kv_key * keys_hash
Definition mvt.h:83
struct mvt_kv_value * float_values_hash
Definition mvt.h:87
VectorTile__Tile * tile
Definition mvt.h:80
struct mvt_kv_value * bool_values_hash
Definition mvt.h:91
struct mvt_kv_value * string_values_hash
Definition mvt.h:86
uint32_t extent
Definition mvt.h:64
struct mvt_kv_value * double_values_hash
Definition mvt.h:88
uint32_t id_index
Definition mvt.h:68
HeapTupleHeader row
Definition mvt.h:74
mvt_column_cache column_cache
Definition mvt.h:98
struct mvt_kv_value * sint_values_hash
Definition mvt.h:90
char * name
Definition mvt.h:63
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
uint32_t id
Definition mvt.c:65
UT_hash_handle hh
Definition mvt.c:66
char * name
Definition mvt.c:64
VectorTile__Tile__Value value[1]
Definition mvt.c:70
UT_hash_handle hh
Definition mvt.c:72
uint32_t id
Definition mvt.c:71