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