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