PostGIS  3.7.0dev-r@@SVN_REVISION@@
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 
49 {
52  CMD_CLOSE_PATH = 7
53 };
54 
56 {
57  MVT_POINT = 1,
58  MVT_LINE = 2,
59  MVT_RING = 3
60 };
61 
62 struct mvt_kv_key
63 {
64  char *name;
65  uint32_t id;
66  UT_hash_handle hh;
67 };
68 
69 struct mvt_kv_value {
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;
88  size_t tags_capacity;
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 
97 static 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 
108 static 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 
123 static 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 
139 static inline uint32_t c_int(enum mvt_cmd_id id, uint32_t count)
140 {
141  return (id & 0x7) | (count << 3);
142 }
143 
144 static inline uint32_t p_int(int32_t value)
145 {
146  return (value << 1) ^ (value >> 31);
147 }
148 
149 static 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 
198 static 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 
204 static 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);
209  encode_ptarray_initial(MVT_POINT, point->point, feature->geometry);
210 }
211 
212 static 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 
224 static 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 
234 static 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 
250 static 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 
266 static 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 
285 static 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 
308 static 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 
316 static 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 
325 static 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 
343  ctx->column_cache.tupdesc = get_tuple_desc(ctx);
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 
403 static void encode_keys(mvt_agg_context *ctx)
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 
506 static bool
507 add_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 */
534 static void
535 add_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. */
543 static inline void
544 parse_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 
556 static 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 
632 static 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 
668 static void parse_values(mvt_agg_context *ctx, struct feature_builder *feature)
669 {
670  uint32_t i;
671  mvt_column_cache cc = ctx->column_cache;
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 */
773 static 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 
813 static inline LWGEOM *
814 lwgeom_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 */
838 static LWGEOM *
839 mvt_unsafe_clip_by_box(LWGEOM *lwg_in, GBOX *clip_box)
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  */
870 static LWGEOM *
871 mvt_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 
894 static LWGEOM *
895 mvt_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 
931 LWGEOM *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 
1079 static 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 
1130 static void * mvt_allocator(__attribute__((__unused__)) void *data, size_t size)
1131 {
1132  return palloc(size);
1133 }
1134 
1135 static void mvt_deallocator(__attribute__((__unused__)) void *data, void *ptr)
1136 {
1137  return pfree(ptr);
1138 }
1139 
1141 {
1142  ProtobufCAllocator allocator =
1143  {
1144  mvt_allocator,
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 
1164 static VectorTile__Tile__Layer *
1165 vectortile_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 
1223 static VectorTile__Tile *
1224 vectortile_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.
Definition: gserialized.c:268
#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:1823
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1218
#define MULTILINETYPE
Definition: liblwgeom.h:106
#define LINETYPE
Definition: liblwgeom.h:103
#define MULTIPOINTTYPE
Definition: liblwgeom.h:105
int lwgeom_remove_repeated_points_in_place(LWGEOM *in, double tolerance)
Definition: lwgeom.c:1667
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:102
#define TINTYPE
Definition: liblwgeom.h:116
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:107
LWCOLLECTION * lwcollection_extract(const LWCOLLECTION *col, uint32_t type)
Definition: lwcollection.c:432
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:1097
#define POLYGONTYPE
Definition: liblwgeom.h:104
void lwgeom_affine(LWGEOM *geom, const AFFINE *affine)
Definition: lwgeom.c:2083
#define __attribute__(x)
Definition: liblwgeom.h:228
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
LWLINE * lwline_from_lwmpoint(int32_t srid, const LWMPOINT *mpoint)
Definition: lwline.c:275
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:755
#define TRIANGLETYPE
Definition: liblwgeom.h:115
#define FLAGS_SET_GEODETIC(flags, value)
Definition: liblwgeom.h:175
LWGEOM * lwgeom_clip_by_rect(const LWGEOM *geom1, double x0, double y0, double x1, double y1)
void lwgeom_grid_in_place(LWGEOM *lwgeom, gridspec *grid)
Definition: lwgeom.c:2289
#define str(s)
#define UINT32_MAX
Definition: lwin_wkt_lex.c:343
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 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 uint32_t get_key_index_with_size(mvt_agg_context *ctx, const char *name, size_t size)
Definition: mvt.c:316
static VectorTile__Tile__Layer * vectortile_layer_combine(VectorTile__Tile__Layer *layer, VectorTile__Tile__Layer *layer2)
Combine 2 layers.
Definition: mvt.c:1165
#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
mvt_agg_context * mvt_ctx_combine(mvt_agg_context *ctx1, mvt_agg_context *ctx2)
Definition: mvt.c:1263
mvt_agg_context * mvt_ctx_deserialize(const bytea *ba)
Definition: mvt.c:1140
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
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 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
#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
static LWGEOM * mvt_unsafe_clip_by_box(LWGEOM *lwg_in, GBOX *clip_box)
Definition: mvt.c:839
#define MVT_PARSE_INT_DATUM(type, datumfunc)
Definition: mvt.c:500
#define MVT_CREATE_VALUES(hash)
Definition: mvt.c:416
bytea * mvt_agg_finalfn(mvt_agg_context *ctx)
Finalize aggregation.
Definition: mvt.c:1296
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
bytea * mvt_ctx_serialize(mvt_agg_context *ctx)
Definition: mvt.c:1125
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 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 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
static VectorTile__Tile * mvt_ctx_to_tile(mvt_agg_context *ctx)
Definition: mvt.c:1079
static uint32_t add_key(mvt_agg_context *ctx, char *name)
Definition: mvt.c:325
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 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 void * mvt_allocator(__attribute__((__unused__)) void *data, size_t size)
Definition: mvt.c:1130
void mvt_agg_init_context(mvt_agg_context *ctx)
Initialize aggregation context.
Definition: mvt.c:988
static bytea * mvt_ctx_to_bytea(mvt_agg_context *ctx)
Definition: mvt.c:1094
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 LWGEOM * mvt_clip_and_validate(LWGEOM *lwgeom, uint8_t basic_type, uint32_t extent, uint32_t buffer, bool clip_geom)
Definition: mvt.c:895
static void parse_jsonb(mvt_agg_context *ctx, struct feature_builder *feature, Jsonb *jb)
Definition: mvt.c:556
static VectorTile__Tile * vectortile_tile_combine(VectorTile__Tile *tile1, VectorTile__Tile *tile2)
Definition: mvt.c:1224
void mvt_agg_transfn(mvt_agg_context *ctx)
Aggregation step.
Definition: mvt.c:1030
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
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 VectorTile__Tile__Feature * feature_build(struct feature_builder *builder)
Definition: mvt.c:108
static void encode_feature_geometry(struct feature_builder *feature, LWGEOM *lwgeom)
Definition: mvt.c:285
int value
Definition: genraster.py:62
int count
Definition: genraster.py:57
type
Definition: ovdump.py:42
data
Definition: ovdump.py:104
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:1398
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
Definition: mvt.c:63
VectorTile__Tile__Value value[1]
Definition: mvt.c:70
UT_hash_handle hh
Definition: mvt.c:72
uint32_t id
Definition: mvt.c:71