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