PostGIS  2.5.1dev-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 
27 #ifdef HAVE_LIBPROTOBUF
28 
29 #if POSTGIS_PGSQL_VERSION >= 94
30 #include "utils/jsonb.h"
31 #endif
32 
33 #if POSTGIS_PGSQL_VERSION < 110
34 /* See trac ticket #3867 */
35 # define DatumGetJsonbP DatumGetJsonb
36 #endif
37 
38 #include "uthash.h"
39 
40 #define FEATURES_CAPACITY_INITIAL 50
41 
43 {
47 };
48 
50 {
51  MVT_POINT = 1,
52  MVT_LINE = 2,
54 };
55 
56 struct mvt_kv_key
57 {
58  char *name;
61 };
62 
64 {
65  char *string_value;
68 };
69 
71 {
72  float float_value;
75 };
76 
78 {
79  double double_value;
82 };
83 
85 {
86  uint64_t uint_value;
89 };
90 
92 {
93  int64_t sint_value;
96 };
97 
99 {
100  protobuf_c_boolean bool_value;
103 };
104 
105 static inline uint32_t c_int(enum mvt_cmd_id id, uint32_t count)
106 {
107  return (id & 0x7) | (count << 3);
108 }
109 
110 static inline uint32_t p_int(int32_t value)
111 {
112  return (value << 1) ^ (value >> 31);
113 }
114 
116  enum mvt_type type, POINTARRAY *pa, uint32_t *buffer,
117  int32_t *px, int32_t *py)
118 {
119  uint32_t offset = 0;
120  uint32_t i, c = 0;
121  int32_t dx, dy, x, y;
122  const POINT2D *p;
123 
124  /* loop points and add to buffer */
125  for (i = 0; i < pa->npoints; i++)
126  {
127  /* move offset for command */
128  if (i == 0 || (i == 1 && type > MVT_POINT))
129  offset++;
130  /* skip closing point for rings */
131  if (type == MVT_RING && i == pa->npoints - 1)
132  break;
133  p = getPoint2d_cp(pa, i);
134  x = p->x;
135  y = p->y;
136  dx = x - *px;
137  dy = y - *py;
138  buffer[offset++] = p_int(dx);
139  buffer[offset++] = p_int(dy);
140  *px = x;
141  *py = y;
142  c++;
143  }
144 
145  /* determine initial move and eventual line command */
146  if (type == MVT_POINT)
147  {
148  /* point or multipoint, use actual number of point count */
149  buffer[0] = c_int(CMD_MOVE_TO, c);
150  }
151  else
152  {
153  /* line or polygon, assume count 1 */
154  buffer[0] = c_int(CMD_MOVE_TO, 1);
155  /* line command with move point subtracted from count */
156  buffer[3] = c_int(CMD_LINE_TO, c - 1);
157  }
158 
159  /* add close command if ring */
160  if (type == MVT_RING)
161  buffer[offset++] = c_int(CMD_CLOSE_PATH, 1);
162 
163  return offset;
164 }
165 
167  enum mvt_type type,
168  POINTARRAY *pa, uint32_t *buffer)
169 {
170  int32_t px = 0, py = 0;
171  return encode_ptarray(ctx, type, pa, buffer, &px, &py);
172 }
173 
174 static void encode_point(mvt_agg_context *ctx, LWPOINT *point)
175 {
176  VectorTile__Tile__Feature *feature = ctx->feature;
177  feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POINT;
178  feature->has_type = 1;
179  feature->n_geometry = 3;
180  feature->geometry = palloc(sizeof(*feature->geometry) * 3);
181  encode_ptarray_initial(ctx, MVT_POINT, point->point, feature->geometry);
182 }
183 
184 static void encode_mpoint(mvt_agg_context *ctx, LWMPOINT *mpoint)
185 {
186  size_t c;
187  VectorTile__Tile__Feature *feature = ctx->feature;
188  // NOTE: inefficient shortcut LWMPOINT->LWLINE
189  LWLINE *lwline = lwline_from_lwmpoint(mpoint->srid, mpoint);
190  feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POINT;
191  feature->has_type = 1;
192  c = 1 + lwline->points->npoints * 2;
193  feature->geometry = palloc(sizeof(*feature->geometry) * c);
194  feature->n_geometry = encode_ptarray_initial(ctx, MVT_POINT,
195  lwline->points, feature->geometry);
196 }
197 
198 static void encode_line(mvt_agg_context *ctx, LWLINE *lwline)
199 {
200  size_t c;
201  VectorTile__Tile__Feature *feature = ctx->feature;
202  feature->type = VECTOR_TILE__TILE__GEOM_TYPE__LINESTRING;
203  feature->has_type = 1;
204  c = 2 + lwline->points->npoints * 2;
205  feature->geometry = palloc(sizeof(*feature->geometry) * c);
206  feature->n_geometry = encode_ptarray_initial(ctx, MVT_LINE,
207  lwline->points, feature->geometry);
208 }
209 
210 static void encode_mline(mvt_agg_context *ctx, LWMLINE *lwmline)
211 {
212  uint32_t i;
213  int32_t px = 0, py = 0;
214  size_t c = 0, offset = 0;
215  VectorTile__Tile__Feature *feature = ctx->feature;
216  feature->type = VECTOR_TILE__TILE__GEOM_TYPE__LINESTRING;
217  feature->has_type = 1;
218  for (i = 0; i < lwmline->ngeoms; i++)
219  c += 2 + lwmline->geoms[i]->points->npoints * 2;
220  feature->geometry = palloc(sizeof(*feature->geometry) * c);
221  for (i = 0; i < lwmline->ngeoms; i++)
222  offset += encode_ptarray(ctx, MVT_LINE,
223  lwmline->geoms[i]->points,
224  feature->geometry + offset, &px, &py);
225  feature->n_geometry = offset;
226 }
227 
228 static void encode_poly(mvt_agg_context *ctx, LWPOLY *lwpoly)
229 {
230  uint32_t i;
231  int32_t px = 0, py = 0;
232  size_t c = 0, offset = 0;
233  VectorTile__Tile__Feature *feature = ctx->feature;
234  feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POLYGON;
235  feature->has_type = 1;
236  for (i = 0; i < lwpoly->nrings; i++)
237  c += 3 + ((lwpoly->rings[i]->npoints - 1) * 2);
238  feature->geometry = palloc(sizeof(*feature->geometry) * c);
239  for (i = 0; i < lwpoly->nrings; i++)
240  offset += encode_ptarray(ctx, MVT_RING,
241  lwpoly->rings[i],
242  feature->geometry + offset, &px, &py);
243  feature->n_geometry = offset;
244 }
245 
246 static void encode_mpoly(mvt_agg_context *ctx, LWMPOLY *lwmpoly)
247 {
248  uint32_t i, j;
249  int32_t px = 0, py = 0;
250  size_t c = 0, offset = 0;
251  LWPOLY *poly;
252  VectorTile__Tile__Feature *feature = ctx->feature;
253  feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POLYGON;
254  feature->has_type = 1;
255  for (i = 0; i < lwmpoly->ngeoms; i++)
256  for (j = 0; poly = lwmpoly->geoms[i], j < poly->nrings; j++)
257  c += 3 + ((poly->rings[j]->npoints - 1) * 2);
258  feature->geometry = palloc(sizeof(*feature->geometry) * c);
259  for (i = 0; i < lwmpoly->ngeoms; i++)
260  for (j = 0; poly = lwmpoly->geoms[i], j < poly->nrings; j++)
261  offset += encode_ptarray(ctx, MVT_RING,
262  poly->rings[j], feature->geometry + offset,
263  &px, &py);
264  feature->n_geometry = offset;
265 }
266 
267 static void encode_geometry(mvt_agg_context *ctx, LWGEOM *lwgeom)
268 {
269  int type = lwgeom->type;
270 
271  switch (type)
272  {
273  case POINTTYPE:
274  return encode_point(ctx, (LWPOINT*)lwgeom);
275  case LINETYPE:
276  return encode_line(ctx, (LWLINE*)lwgeom);
277  case POLYGONTYPE:
278  return encode_poly(ctx, (LWPOLY*)lwgeom);
279  case MULTIPOINTTYPE:
280  return encode_mpoint(ctx, (LWMPOINT*)lwgeom);
281  case MULTILINETYPE:
282  return encode_mline(ctx, (LWMLINE*)lwgeom);
283  case MULTIPOLYGONTYPE:
284  return encode_mpoly(ctx, (LWMPOLY*)lwgeom);
285  default: elog(ERROR, "encode_geometry: '%s' geometry type not supported",
286  lwtype_name(type));
287  }
288 }
289 
290 static TupleDesc get_tuple_desc(mvt_agg_context *ctx)
291 {
292  Oid tupType = HeapTupleHeaderGetTypeId(ctx->row);
293  int32 tupTypmod = HeapTupleHeaderGetTypMod(ctx->row);
294  TupleDesc tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
295  return tupdesc;
296 }
297 
298 static uint32_t get_key_index_with_size(mvt_agg_context *ctx, const char *name, size_t size)
299 {
300  struct mvt_kv_key *kv;
301  HASH_FIND(hh, ctx->keys_hash, name, size, kv);
302  if (!kv)
303  return UINT32_MAX;
304  return kv->id;
305 }
306 
308 {
309  struct mvt_kv_key *kv;
310  size_t size = strlen(name);
311  kv = palloc(sizeof(*kv));
312  kv->id = ctx->keys_hash_i++;
313  kv->name = name;
314  HASH_ADD_KEYPTR(hh, ctx->keys_hash, name, size, kv);
315  return kv->id;
316 }
317 
319 {
320  uint32_t i, natts;
321  bool geom_found = false;
322 
323  POSTGIS_DEBUG(2, "parse_column_keys called");
324 
325  ctx->column_cache.tupdesc = get_tuple_desc(ctx);
326  natts = ctx->column_cache.tupdesc->natts;
327 
328  ctx->column_cache.column_keys_index = palloc(sizeof(uint32_t) * natts);
329  ctx->column_cache.column_oid = palloc(sizeof(uint32_t) * natts);
330  ctx->column_cache.values = palloc(sizeof(Datum) * natts);
331  ctx->column_cache.nulls = palloc(sizeof(bool) * natts);
332 
333  for (i = 0; i < natts; i++)
334  {
335 #if POSTGIS_PGSQL_VERSION < 110
336  Oid typoid = getBaseType(ctx->column_cache.tupdesc->attrs[i]->atttypid);
337  char *tkey = ctx->column_cache.tupdesc->attrs[i]->attname.data;
338 #else
339  Oid typoid = getBaseType(ctx->column_cache.tupdesc->attrs[i].atttypid);
340  char *tkey = ctx->column_cache.tupdesc->attrs[i].attname.data;
341 #endif
342 
343  ctx->column_cache.column_oid[i] = typoid;
344 #if POSTGIS_PGSQL_VERSION >= 94
345  if (typoid == JSONBOID)
346  {
348  continue;
349  }
350 #endif
351 
352  if (ctx->geom_name == NULL)
353  {
354  if (!geom_found && typoid == TypenameGetTypid("geometry"))
355  {
356  ctx->geom_index = i;
357  geom_found = true;
358  continue;
359  }
360  }
361  else
362  {
363  if (!geom_found && strcmp(tkey, ctx->geom_name) == 0)
364  {
365  ctx->geom_index = i;
366  geom_found = true;
367  continue;
368  }
369  }
370 
371  ctx->column_cache.column_keys_index[i] = add_key(ctx, pstrdup(tkey));
372  }
373 
374  if (!geom_found)
375  elog(ERROR, "parse_column_keys: no geometry column found");
376 }
377 
378 static void encode_keys(mvt_agg_context *ctx)
379 {
380  struct mvt_kv_key *kv;
381  size_t n_keys = ctx->keys_hash_i;
382  char **keys = palloc(n_keys * sizeof(*keys));
383  for (kv = ctx->keys_hash; kv != NULL; kv=kv->hh.next)
384  keys[kv->id] = kv->name;
385  ctx->layer->n_keys = n_keys;
386  ctx->layer->keys = keys;
387 
388  HASH_CLEAR(hh, ctx->keys_hash);
389 }
390 
391 static VectorTile__Tile__Value *create_value()
392 {
393  VectorTile__Tile__Value *value = palloc(sizeof(*value));
394  vector_tile__tile__value__init(value);
395  return value;
396 }
397 
398 #define MVT_CREATE_VALUES(kvtype, hash, hasfield, valuefield) \
399 { \
400  POSTGIS_DEBUG(2, "MVT_CREATE_VALUES called"); \
401  { \
402  struct kvtype *kv; \
403  for (kv = ctx->hash; kv != NULL; kv=kv->hh.next) \
404  { \
405  VectorTile__Tile__Value *value = create_value(); \
406  value->hasfield = 1; \
407  value->valuefield = kv->valuefield; \
408  values[kv->id] = value; \
409  } \
410  } \
411 }
412 
414 {
415  VectorTile__Tile__Value **values;
416  struct mvt_kv_string_value *kv;
417 
418  POSTGIS_DEBUG(2, "encode_values called");
419 
420  values = palloc(ctx->values_hash_i * sizeof(*values));
421  for (kv = ctx->string_values_hash; kv != NULL; kv=kv->hh.next)
422  {
423  VectorTile__Tile__Value *value = create_value();
424  value->string_value = kv->string_value;
425  values[kv->id] = value;
426  }
428  float_values_hash, has_float_value, float_value);
430  double_values_hash, has_double_value, double_value);
432  uint_values_hash, has_uint_value, uint_value);
434  sint_values_hash, has_sint_value, sint_value);
436  bool_values_hash, has_bool_value, bool_value);
437 
438  POSTGIS_DEBUGF(3, "encode_values n_values: %d", ctx->values_hash_i);
439  ctx->layer->n_values = ctx->values_hash_i;
440  ctx->layer->values = values;
441 
448 
449  pfree(ctx->column_cache.column_keys_index);
450  pfree(ctx->column_cache.column_oid);
451  pfree(ctx->column_cache.values);
452  pfree(ctx->column_cache.nulls);
453  ReleaseTupleDesc(ctx->column_cache.tupdesc);
454  memset(&ctx->column_cache, 0, sizeof(ctx->column_cache));
455 
456 }
457 
458 #define MVT_PARSE_VALUE(value, kvtype, hash, valuefield, size) \
459 { \
460  POSTGIS_DEBUG(2, "MVT_PARSE_VALUE called"); \
461  { \
462  struct kvtype *kv; \
463  HASH_FIND(hh, ctx->hash, &value, size, kv); \
464  if (!kv) \
465  { \
466  POSTGIS_DEBUG(4, "MVT_PARSE_VALUE value not found"); \
467  kv = palloc(sizeof(*kv)); \
468  POSTGIS_DEBUGF(4, "MVT_PARSE_VALUE new hash key: %d", \
469  ctx->values_hash_i); \
470  kv->id = ctx->values_hash_i++; \
471  kv->valuefield = value; \
472  HASH_ADD(hh, ctx->hash, valuefield, size, kv); \
473  } \
474  tags[ctx->row_columns*2] = k; \
475  tags[ctx->row_columns*2+1] = kv->id; \
476  } \
477 }
478 
479 #define MVT_PARSE_INT_VALUE(value) \
480 { \
481  if (value >= 0) \
482  { \
483  uint64_t cvalue = value; \
484  MVT_PARSE_VALUE(cvalue, mvt_kv_uint_value, \
485  uint_values_hash, uint_value, \
486  sizeof(uint64_t)) \
487  } \
488  else \
489  { \
490  int64_t cvalue = value; \
491  MVT_PARSE_VALUE(cvalue, mvt_kv_sint_value, \
492  sint_values_hash, sint_value, \
493  sizeof(int64_t)) \
494  } \
495 }
496 
497 #define MVT_PARSE_DATUM(type, kvtype, hash, valuefield, datumfunc, size) \
498 { \
499  type value = datumfunc(datum); \
500  MVT_PARSE_VALUE(value, kvtype, hash, valuefield, size); \
501 }
502 
503 #define MVT_PARSE_INT_DATUM(type, datumfunc) \
504 { \
505  type value = datumfunc(datum); \
506  MVT_PARSE_INT_VALUE(value); \
507 }
508 
510  char *value, size_t size, uint32_t *tags, uint32_t k)
511 {
512  struct mvt_kv_string_value *kv;
513  POSTGIS_DEBUG(2, "add_value_as_string called");
514  HASH_FIND(hh, ctx->string_values_hash, value, size, kv);
515  if (!kv)
516  {
517  POSTGIS_DEBUG(4, "add_value_as_string value not found");
518  kv = palloc(sizeof(*kv));
519  POSTGIS_DEBUGF(4, "add_value_as_string new hash key: %d",
520  ctx->values_hash_i);
521  kv->id = ctx->values_hash_i++;
522  kv->string_value = value;
524  size, kv);
525  }
526  tags[ctx->row_columns*2] = k;
527  tags[ctx->row_columns*2+1] = kv->id;
528 }
529 
531  char *value, uint32_t *tags, uint32_t k)
532 {
533  return add_value_as_string_with_size(ctx, value, strlen(value), tags, k);
534 }
535 
536 static void parse_datum_as_string(mvt_agg_context *ctx, Oid typoid,
537  Datum datum, uint32_t *tags, uint32_t k)
538 {
539  Oid foutoid;
540  bool typisvarlena;
541  char *value;
542  POSTGIS_DEBUG(2, "parse_value_as_string called");
543  getTypeOutputInfo(typoid, &foutoid, &typisvarlena);
544  value = OidOutputFunctionCall(foutoid, datum);
545  POSTGIS_DEBUGF(4, "parse_value_as_string value: %s", value);
546  add_value_as_string(ctx, value, tags, k);
547 }
548 
549 #if POSTGIS_PGSQL_VERSION >= 94
550 static uint32_t *parse_jsonb(mvt_agg_context *ctx, Jsonb *jb,
551  uint32_t *tags)
552 {
553  JsonbIterator *it;
554  JsonbValue v;
555  bool skipNested = false;
556  JsonbIteratorToken r;
557  uint32_t k;
558 
559  if (!JB_ROOT_IS_OBJECT(jb))
560  return tags;
561 
562  it = JsonbIteratorInit(&jb->root);
563 
564  while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
565  {
566  skipNested = true;
567 
568  if (r == WJB_KEY && v.type != jbvNull)
569  {
570 
571  k = get_key_index_with_size(ctx, v.val.string.val, v.val.string.len);
572  if (k == UINT32_MAX)
573  {
574  char *key;
575  uint32_t newSize = ctx->keys_hash_i + 1;
576 
577  key = palloc(v.val.string.len + 1);
578  memcpy(key, v.val.string.val, v.val.string.len);
579  key[v.val.string.len] = '\0';
580 
581  tags = repalloc(tags, newSize * 2 * sizeof(*tags));
582  k = add_key(ctx, key);
583  }
584 
585  r = JsonbIteratorNext(&it, &v, skipNested);
586 
587  if (v.type == jbvString)
588  {
589  char *value;
590  value = palloc(v.val.string.len + 1);
591  memcpy(value, v.val.string.val, v.val.string.len);
592  value[v.val.string.len] = '\0';
593  add_value_as_string(ctx, value, tags, k);
594  ctx->row_columns++;
595  }
596  else if (v.type == jbvBool)
597  {
598  MVT_PARSE_VALUE(v.val.boolean, mvt_kv_bool_value,
599  bool_values_hash, bool_value, sizeof(protobuf_c_boolean));
600  ctx->row_columns++;
601  }
602  else if (v.type == jbvNumeric)
603  {
604  char *str;
605  double d;
606  long l;
607  str = DatumGetCString(DirectFunctionCall1(numeric_out,
608  PointerGetDatum(v.val.numeric)));
609  d = strtod(str, NULL);
610  l = strtol(str, NULL, 10);
611  if ((long) d != l)
612  {
613  MVT_PARSE_VALUE(d, mvt_kv_double_value, double_values_hash,
614  double_value, sizeof(double));
615  }
616  else
617  {
619  }
620  ctx->row_columns++;
621  }
622  }
623  }
624 
625  return tags;
626 }
627 #endif
628 
629 static void parse_values(mvt_agg_context *ctx)
630 {
631  uint32_t n_keys = ctx->keys_hash_i;
632  uint32_t *tags = palloc(n_keys * 2 * sizeof(*tags));
633  uint32_t i;
634  mvt_column_cache cc = ctx->column_cache;
635  uint32_t natts = (uint32_t) cc.tupdesc->natts;
636 
637  HeapTupleData tuple;
638 
639  POSTGIS_DEBUG(2, "parse_values called");
640  ctx->row_columns = 0;
641 
642  /* Build a temporary HeapTuple control structure */
643  tuple.t_len = HeapTupleHeaderGetDatumLength(ctx->row);
644  ItemPointerSetInvalid(&(tuple.t_self));
645  tuple.t_tableOid = InvalidOid;
646  tuple.t_data = ctx->row;
647 
648  /* We use heap_deform_tuple as it costs only O(N) vs O(N^2) of GetAttributeByNum */
649  heap_deform_tuple(&tuple, cc.tupdesc, cc.values, cc.nulls);
650 
651  POSTGIS_DEBUGF(3, "parse_values natts: %d", natts);
652 
653  for (i = 0; i < natts; i++)
654  {
655  char *key;
656  Oid typoid;
657  uint32_t k;
658  Datum datum = cc.values[i];
659 
660  if (i == ctx->geom_index)
661  continue;
662 
663  if (cc.nulls[i])
664  {
665  POSTGIS_DEBUG(3, "parse_values isnull detected");
666  continue;
667  }
668 
669 #if POSTGIS_PGSQL_VERSION < 110
670  key = cc.tupdesc->attrs[i]->attname.data;
671 #else
672  key = cc.tupdesc->attrs[i].attname.data;
673 #endif
674  k = cc.column_keys_index[i];
675  typoid = cc.column_oid[i];
676 
677 #if POSTGIS_PGSQL_VERSION >= 94
678  if (k == UINT32_MAX && typoid != JSONBOID)
679  elog(ERROR, "parse_values: unexpectedly could not find parsed key name '%s'", key);
680  if (typoid == JSONBOID)
681  {
682  tags = parse_jsonb(ctx, DatumGetJsonbP(datum), tags);
683  continue;
684  }
685 #else
686  if (k == UINT32_MAX)
687  elog(ERROR, "parse_values: unexpectedly could not find parsed key name '%s'", key);
688 #endif
689 
690  switch (typoid)
691  {
692  case BOOLOID:
693  MVT_PARSE_DATUM(protobuf_c_boolean, mvt_kv_bool_value,
694  bool_values_hash, bool_value,
695  DatumGetBool, sizeof(protobuf_c_boolean));
696  break;
697  case INT2OID:
698  MVT_PARSE_INT_DATUM(int16_t, DatumGetInt16);
699  break;
700  case INT4OID:
701  MVT_PARSE_INT_DATUM(int32_t, DatumGetInt32);
702  break;
703  case INT8OID:
704  MVT_PARSE_INT_DATUM(int64_t, DatumGetInt64);
705  break;
706  case FLOAT4OID:
708  float_values_hash, float_value,
709  DatumGetFloat4, sizeof(float));
710  break;
711  case FLOAT8OID:
713  double_values_hash, double_value,
714  DatumGetFloat8, sizeof(double));
715  break;
716  default:
717  parse_datum_as_string(ctx, typoid, datum, tags, k);
718  break;
719  }
720  ctx->row_columns++;
721  }
722 
723 
724  ctx->feature->n_tags = ctx->row_columns * 2;
725  ctx->feature->tags = tags;
726 
727  POSTGIS_DEBUGF(3, "parse_values n_tags %zd", ctx->feature->n_tags);
728 }
729 
730 /* For a given geometry, look for the highest dimensional basic type, that is,
731  * point, line or polygon */
732 static uint8
734 {
735  switch(geom->type)
736  {
737  case POINTTYPE:
738  case LINETYPE:
739  case POLYGONTYPE:
740  return geom->type;
741  case MULTIPOINTTYPE:
742  case MULTILINETYPE:
743  case MULTIPOLYGONTYPE:
744  return geom->type - 3; /* Based on LWTYPE positions */
745  case COLLECTIONTYPE:
746  {
747  uint32_t i;
748  uint8 type = 0;
749  LWCOLLECTION *g = (LWCOLLECTION*)geom;
750  for (i = 0; i < g->ngeoms; i++)
751  {
752  LWGEOM *sg = g->geoms[i];
753  type = Max(type, lwgeom_get_basic_type(sg));
754  }
755  return type;
756  }
757  default:
758  elog(ERROR, "%s: Invalid type (%d)", __func__, geom->type);
759  }
760 }
761 
762 
769 static void
770 lwgeom_to_basic_type(LWGEOM *geom, uint8 original_type)
771 {
772  if (lwgeom_get_type(geom) == COLLECTIONTYPE)
773  {
774  LWCOLLECTION *g = (LWCOLLECTION*)geom;
775  LWCOLLECTION *gc = lwcollection_extract(g, original_type);
776  *g = *gc;
777  }
778 }
779 
788 LWGEOM *mvt_geom(LWGEOM *lwgeom, const GBOX *gbox, uint32_t extent, uint32_t buffer,
789  bool clip_geom)
790 {
791  AFFINE affine;
792  gridspec grid;
793  double width = gbox->xmax - gbox->xmin;
794  double height = gbox->ymax - gbox->ymin;
795  double resx, resy, res, fx, fy;
796  int preserve_collapsed = LW_TRUE;
797  const uint8_t basic_type = lwgeom_get_basic_type(lwgeom);
798  POSTGIS_DEBUG(2, "mvt_geom called");
799 
800  /* Short circuit out on EMPTY */
801  if (lwgeom_is_empty(lwgeom))
802  return NULL;
803 
804  if (width == 0 || height == 0)
805  elog(ERROR, "mvt_geom: bounds width or height cannot be 0");
806 
807  if (extent == 0)
808  elog(ERROR, "mvt_geom: extent cannot be 0");
809 
810  resx = width / extent;
811  resy = height / extent;
812  res = (resx < resy ? resx : resy)/2;
813  fx = extent / width;
814  fy = -(extent / height);
815 
816  /* Remove all non-essential points (under the output resolution) */
818  lwgeom_simplify_in_place(lwgeom, res, preserve_collapsed);
819 
820  /* If geometry has disappeared, you're done */
821  if (lwgeom_is_empty(lwgeom))
822  return NULL;
823 
824  if (clip_geom)
825  {
826  // We need to add an extra half pixel to include the points that
827  // fall into the bbox only after the coordinate transformation
828  double buffer_map_xunits = nextafterf(res, 0.0) + resx * buffer;
829  GBOX bgbox;
830  const GBOX *lwgeom_gbox = lwgeom_get_bbox(lwgeom);
831  bgbox = *gbox;
832  gbox_expand(&bgbox, buffer_map_xunits);
833  if (!gbox_overlaps_2d(lwgeom_gbox, &bgbox))
834  {
835  POSTGIS_DEBUG(3, "mvt_geom: geometry outside clip box");
836  return NULL;
837  }
838  if (!gbox_contains_2d(&bgbox, lwgeom_gbox))
839  {
840  double x0 = bgbox.xmin;
841  double y0 = bgbox.ymin;
842  double x1 = bgbox.xmax;
843  double y1 = bgbox.ymax;
844  const GBOX pre_clip_box = *lwgeom_get_bbox(lwgeom);
845  LWGEOM *clipped_geom = lwgeom_clip_by_rect(lwgeom, x0, y0, x1, y1);
846  if (clipped_geom == NULL || lwgeom_is_empty(clipped_geom))
847  {
848  POSTGIS_DEBUG(3, "mvt_geom: no geometry after clip");
849  return NULL;
850  }
851  /* For some polygons, the simplify step might have left them
852  * as invalid, which can cause clipping to return the complementary
853  * geometry of what it should */
854  if ((basic_type == POLYGONTYPE) &&
855  !gbox_contains_2d(&pre_clip_box, lwgeom_get_bbox(clipped_geom)))
856  {
857  /* TODO: Adapt this when and if Exception Policies are introduced.
858  * Other options would be to fix the geometry and retry
859  * or to calculate the difference between the 2 boxes.
860  */
861  POSTGIS_DEBUG(3, "mvt_geom: Invalid geometry after clipping");
862  lwgeom_free(clipped_geom);
863  return NULL;
864  }
865  lwgeom = clipped_geom;
866  }
867  }
868 
869  /* transform to tile coordinate space */
870  memset(&affine, 0, sizeof(affine));
871  affine.afac = fx;
872  affine.efac = fy;
873  affine.ifac = 1;
874  affine.xoff = -gbox->xmin * fx;
875  affine.yoff = -gbox->ymax * fy;
876  lwgeom_affine(lwgeom, &affine);
877 
878  /* snap to integer precision, removing duplicate points */
879  memset(&grid, 0, sizeof(gridspec));
880  grid.ipx = 0;
881  grid.ipy = 0;
882  grid.xsize = 1;
883  grid.ysize = 1;
884  lwgeom_grid_in_place(lwgeom, &grid);
885 
886  if (lwgeom == NULL || lwgeom_is_empty(lwgeom))
887  return NULL;
888 
889 
890  if (basic_type == POLYGONTYPE)
891  {
892  /* Force validation as per MVT spec */
893  lwgeom = lwgeom_make_valid(lwgeom);
894 
895  /* In image coordinates CW actually comes out a CCW, so we reverse */
896  lwgeom_force_clockwise(lwgeom);
897  lwgeom_reverse_in_place(lwgeom);
898  }
899 
900  /* if geometry collection extract highest dimensional geometry type */
901  if (lwgeom->type == COLLECTIONTYPE)
902  lwgeom_to_basic_type(lwgeom, basic_type);
903 
904  if (basic_type != lwgeom_get_basic_type(lwgeom))
905  {
906  /* Drop type changes to play nice with MVT renderers */
907  POSTGIS_DEBUG(3, "mvt_geom: Dropping geometry after type change");
908  return NULL;
909  }
910 
911  if (lwgeom == NULL || lwgeom_is_empty(lwgeom))
912  return NULL;
913 
914  return lwgeom;
915 }
916 
921 {
922  VectorTile__Tile__Layer *layer;
923 
924  POSTGIS_DEBUG(2, "mvt_agg_init_context called");
925 
926  if (ctx->extent == 0)
927  elog(ERROR, "mvt_agg_init_context: extent cannot be 0");
928 
929  ctx->tile = NULL;
931  ctx->keys_hash = NULL;
932  ctx->string_values_hash = NULL;
933  ctx->float_values_hash = NULL;
934  ctx->double_values_hash = NULL;
935  ctx->uint_values_hash = NULL;
936  ctx->sint_values_hash = NULL;
937  ctx->bool_values_hash = NULL;
938  ctx->values_hash_i = 0;
939  ctx->keys_hash_i = 0;
940  ctx->geom_index = UINT32_MAX;
941 
942  memset(&ctx->column_cache, 0, sizeof(ctx->column_cache));
943 
944  layer = palloc(sizeof(*layer));
945  vector_tile__tile__layer__init(layer);
946  layer->version = 2;
947  layer->name = ctx->name;
948  layer->has_extent = 1;
949  layer->extent = ctx->extent;
950  layer->features = palloc (ctx->features_capacity *
951  sizeof(*layer->features));
952 
953  ctx->layer = layer;
954 }
955 
964 {
965  bool isnull = false;
966  Datum datum;
967  GSERIALIZED *gs;
968  LWGEOM *lwgeom;
969  VectorTile__Tile__Feature *feature;
970  VectorTile__Tile__Layer *layer = ctx->layer;
971 /* VectorTile__Tile__Feature **features = layer->features; */
972  POSTGIS_DEBUG(2, "mvt_agg_transfn called");
973 
974  if (layer->n_features >= ctx->features_capacity)
975  {
976  size_t new_capacity = ctx->features_capacity * 2;
977  layer->features = repalloc(layer->features, new_capacity *
978  sizeof(*layer->features));
979  ctx->features_capacity = new_capacity;
980  POSTGIS_DEBUGF(3, "mvt_agg_transfn new_capacity: %zd", new_capacity);
981  }
982 
983  if (ctx->geom_index == UINT32_MAX)
984  parse_column_keys(ctx);
985 
986  datum = GetAttributeByNum(ctx->row, ctx->geom_index + 1, &isnull);
987  POSTGIS_DEBUGF(3, "mvt_agg_transfn ctx->geom_index: %d", ctx->geom_index);
988  POSTGIS_DEBUGF(3, "mvt_agg_transfn isnull: %u", isnull);
989  POSTGIS_DEBUGF(3, "mvt_agg_transfn datum: %lu", datum);
990  if (isnull) /* Skip rows that have null geometry */
991  {
992  POSTGIS_DEBUG(3, "mvt_agg_transfn got null geom");
993  return;
994  }
995 
996  feature = palloc(sizeof(*feature));
997  vector_tile__tile__feature__init(feature);
998 
999  ctx->feature = feature;
1000 
1001  gs = (GSERIALIZED *) PG_DETOAST_DATUM(datum);
1002  lwgeom = lwgeom_from_gserialized(gs);
1003 
1004  POSTGIS_DEBUGF(3, "mvt_agg_transfn encoded feature count: %zd", layer->n_features);
1005  layer->features[layer->n_features++] = feature;
1006 
1007  encode_geometry(ctx, lwgeom);
1008  lwgeom_free(lwgeom);
1009  // TODO: free detoasted datum?
1010  parse_values(ctx);
1011 }
1012 
1013 static VectorTile__Tile * mvt_ctx_to_tile(mvt_agg_context *ctx)
1014 {
1015  int n_layers = 1;
1016  VectorTile__Tile *tile;
1017  encode_keys(ctx);
1018  encode_values(ctx);
1019 
1020  tile = palloc(sizeof(VectorTile__Tile));
1021  vector_tile__tile__init(tile);
1022  tile->layers = palloc(sizeof(VectorTile__Tile__Layer*) * n_layers);
1023  tile->layers[0] = ctx->layer;
1024  tile->n_layers = n_layers;
1025  return tile;
1026 }
1027 
1029 {
1030  /* Fill out the file slot, if it's not already filled. */
1031  /* We should only have a filled slow when all the work of building */
1032  /* out the data is complete, so after a serialize/deserialize cycle */
1033  /* or after a context combine */
1034  size_t len;
1035  bytea *ba;
1036 
1037  if (!ctx->tile)
1038  {
1039  ctx->tile = mvt_ctx_to_tile(ctx);
1040  }
1041 
1042  /* Zero features => empty bytea output */
1043  if (ctx && ctx->layer && ctx->layer->n_features == 0)
1044  {
1045  bytea *ba = palloc(VARHDRSZ);
1046  SET_VARSIZE(ba, VARHDRSZ);
1047  return ba;
1048  }
1049 
1050  /* Serialize the Tile */
1051  len = VARHDRSZ + vector_tile__tile__get_packed_size(ctx->tile);
1052  ba = palloc(len);
1053  vector_tile__tile__pack(ctx->tile, (uint8_t*)VARDATA(ba));
1054  SET_VARSIZE(ba, len);
1055  return ba;
1056 }
1057 
1058 
1060 {
1061  return mvt_ctx_to_bytea(ctx);
1062 }
1063 
1064 static void * mvt_allocator(__attribute__((__unused__)) void *data, size_t size)
1065 {
1066  return palloc(size);
1067 }
1068 
1069 static void mvt_deallocator(__attribute__((__unused__)) void *data, void *ptr)
1070 {
1071  return pfree(ptr);
1072 }
1073 
1075 {
1076  ProtobufCAllocator allocator =
1077  {
1078  mvt_allocator,
1080  NULL
1081  };
1082 
1083  size_t len = VARSIZE(ba) - VARHDRSZ;
1084  VectorTile__Tile *tile = vector_tile__tile__unpack(&allocator, len, (uint8_t*)VARDATA(ba));
1085  mvt_agg_context *ctx = palloc(sizeof(mvt_agg_context));
1086  memset(ctx, 0, sizeof(mvt_agg_context));
1087  ctx->tile = tile;
1088  return ctx;
1089 }
1090 
1091 static VectorTile__Tile__Value *
1092 tile_value_copy(const VectorTile__Tile__Value *value)
1093 {
1094  VectorTile__Tile__Value *nvalue = palloc(sizeof(VectorTile__Tile__Value));
1095  memcpy(nvalue, value, sizeof(VectorTile__Tile__Value));
1096  if (value->string_value)
1097  nvalue->string_value = pstrdup(value->string_value);
1098  return nvalue;
1099 }
1100 
1101 static VectorTile__Tile__Feature *
1102 tile_feature_copy(const VectorTile__Tile__Feature *feature, int key_offset, int value_offset)
1103 {
1104  uint32_t i;
1105  VectorTile__Tile__Feature *nfeature;
1106 
1107  /* Null in => Null out */
1108  if (!feature) return NULL;
1109 
1110  /* Init object */
1111  nfeature = palloc(sizeof(VectorTile__Tile__Feature));
1112  vector_tile__tile__feature__init(nfeature);
1113 
1114  /* Copy settings straight over */
1115  nfeature->has_id = feature->has_id;
1116  nfeature->id = feature->id;
1117  nfeature->has_type = feature->has_type;
1118  nfeature->type = feature->type;
1119 
1120  /* Copy tags over, offsetting indexes so they match the dictionaries */
1121  /* at the Tile_Layer level */
1122  if (feature->n_tags > 0)
1123  {
1124  nfeature->n_tags = feature->n_tags;
1125  nfeature->tags = palloc(sizeof(uint32_t)*feature->n_tags);
1126  for (i = 0; i < feature->n_tags/2; i++)
1127  {
1128  nfeature->tags[2*i] = feature->tags[2*i] + key_offset;
1129  nfeature->tags[2*i+1] = feature->tags[2*i+1] + value_offset;
1130  }
1131  }
1132 
1133  /* Copy the raw geometry data over literally */
1134  if (feature->n_geometry > 0)
1135  {
1136  nfeature->n_geometry = feature->n_geometry;
1137  nfeature->geometry = palloc(sizeof(uint32_t)*feature->n_geometry);
1138  memcpy(nfeature->geometry, feature->geometry, sizeof(uint32_t)*feature->n_geometry);
1139  }
1140 
1141  /* Done */
1142  return nfeature;
1143 }
1144 
1145 static VectorTile__Tile__Layer *
1146 vectortile_layer_combine(const VectorTile__Tile__Layer *layer1, const VectorTile__Tile__Layer *layer2)
1147 {
1148  uint32_t i, j;
1149  int key2_offset, value2_offset;
1150  VectorTile__Tile__Layer *layer = palloc(sizeof(VectorTile__Tile__Layer));
1151  vector_tile__tile__layer__init(layer);
1152 
1153  /* Take globals from layer1 */
1154  layer->version = layer1->version;
1155  layer->name = pstrdup(layer1->name);
1156  layer->has_extent = layer1->has_extent;
1157  layer->extent = layer1->extent;
1158 
1159  /* Copy keys into new layer */
1160  j = 0;
1161  layer->n_keys = layer1->n_keys + layer2->n_keys;
1162  layer->keys = layer->n_keys ? palloc(layer->n_keys * sizeof(void*)) : NULL;
1163  for (i = 0; i < layer1->n_keys; i++)
1164  layer->keys[j++] = pstrdup(layer1->keys[i]);
1165  key2_offset = j;
1166  for (i = 0; i < layer2->n_keys; i++)
1167  layer->keys[j++] = pstrdup(layer2->keys[i]);
1168 
1169  /* Copy values into new layer */
1170  /* TODO, apply hash logic here too, so that merged tiles */
1171  /* retain unique value maps */
1172  layer->n_values = layer1->n_values + layer2->n_values;
1173  layer->values = layer->n_values ? palloc(layer->n_values * sizeof(void*)) : NULL;
1174  j = 0;
1175  for (i = 0; i < layer1->n_values; i++)
1176  layer->values[j++] = tile_value_copy(layer1->values[i]);
1177  value2_offset = j;
1178  for (i = 0; i < layer2->n_values; i++)
1179  layer->values[j++] = tile_value_copy(layer2->values[i]);
1180 
1181 
1182  layer->n_features = layer1->n_features + layer2->n_features;
1183  layer->features = layer->n_features ? palloc(layer->n_features * sizeof(void*)) : NULL;
1184  j = 0;
1185  for (i = 0; i < layer1->n_features; i++)
1186  layer->features[j++] = tile_feature_copy(layer1->features[i], 0, 0);
1187  for (i = 0; i < layer2->n_features; i++)
1188  layer->features[j++] = tile_feature_copy(layer2->features[i], key2_offset, value2_offset);
1189 
1190  return layer;
1191 }
1192 
1193 
1194 static VectorTile__Tile *
1195 vectortile_tile_combine(VectorTile__Tile *tile1, VectorTile__Tile *tile2)
1196 {
1197  uint32_t i, j;
1198  VectorTile__Tile *tile;
1199 
1200  /* Hopelessly messing up memory ownership here */
1201  if (tile1->n_layers == 0 && tile2->n_layers == 0)
1202  return tile1;
1203  else if (tile1->n_layers == 0)
1204  return tile2;
1205  else if (tile2->n_layers == 0)
1206  return tile1;
1207 
1208  tile = palloc(sizeof(VectorTile__Tile));
1209  vector_tile__tile__init(tile);
1210  tile->layers = palloc(sizeof(void*));
1211  tile->n_layers = 0;
1212 
1213  /* Merge all matching layers in the files (basically always only one) */
1214  for (i = 0; i < tile1->n_layers; i++)
1215  {
1216  for (j = 0; j < tile2->n_layers; j++)
1217  {
1218  VectorTile__Tile__Layer *l1 = tile1->layers[i];
1219  VectorTile__Tile__Layer *l2 = tile2->layers[j];
1220  if (strcmp(l1->name, l2->name)==0)
1221  {
1222  VectorTile__Tile__Layer *layer = vectortile_layer_combine(l1, l2);
1223  if (!layer)
1224  continue;
1225  tile->layers[tile->n_layers++] = layer;
1226  /* Add a spare slot at the end of the array */
1227  tile->layers = repalloc(tile->layers, (tile->n_layers+1) * sizeof(void*));
1228  }
1229  }
1230  }
1231  return tile;
1232 }
1233 
1235 {
1236  if (ctx1 || ctx2)
1237  {
1238  if (ctx1 && ! ctx2) return ctx1;
1239  if (ctx2 && ! ctx1) return ctx2;
1240  if (ctx1 && ctx2 && ctx1->tile && ctx2->tile)
1241  {
1242  mvt_agg_context *ctxnew = palloc(sizeof(mvt_agg_context));
1243  memset(ctxnew, 0, sizeof(mvt_agg_context));
1244  ctxnew->tile = vectortile_tile_combine(ctx1->tile, ctx2->tile);
1245  return ctxnew;
1246  }
1247  else
1248  {
1249  elog(DEBUG2, "ctx1->tile = %p", ctx1->tile);
1250  elog(DEBUG2, "ctx2->tile = %p", ctx2->tile);
1251  elog(ERROR, "%s: unable to combine contexts where tile attribute is null", __func__);
1252  return NULL;
1253  }
1254  }
1255  else
1256  {
1257  return NULL;
1258  }
1259 }
1260 
1268 {
1269  return mvt_ctx_to_bytea(ctx);
1270 }
1271 
1272 
1273 #endif
#define LINETYPE
Definition: liblwgeom.h:85
HeapTupleHeader row
Definition: mvt.h:64
Definition: mvt.c:51
#define HASH_ADD_KEYPTR(hh, head, keyptr, keylen_in, add)
Definition: uthash.h:329
uint32_t id
Definition: mvt.c:101
static VectorTile__Tile__Value * tile_value_copy(const VectorTile__Tile__Value *value)
Definition: mvt.c:1092
void lwgeom_remove_repeated_points_in_place(LWGEOM *in, double tolerance)
Definition: lwgeom.c:1603
unsigned int int32
Definition: shpopen.c:273
tuple res
Definition: window.py:78
struct mvt_kv_double_value * double_values_hash
Definition: mvt.h:72
struct mvt_kv_string_value * string_values_hash
Definition: mvt.h:70
uint32_t row_columns
Definition: mvt.h:78
char * name
Definition: mvt.h:60
uint64_t uint_value
Definition: mvt.c:86
LWCOLLECTION * lwcollection_extract(LWCOLLECTION *col, int type)
Takes a potentially heterogeneous collection and returns a homogeneous collection consisting only of ...
Definition: lwcollection.c:386
char * r
Definition: cu_in_wkt.c:24
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
void gbox_expand(GBOX *g, double d)
Move the box minimums down and the maximums up by the distance provided.
Definition: g_box.c:104
static void add_value_as_string(mvt_agg_context *ctx, char *value, uint32_t *tags, uint32_t k)
Definition: mvt.c:530
uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwgeom.c:923
void lwgeom_reverse_in_place(LWGEOM *lwgeom)
Reverse vertex order of LWGEOM.
Definition: lwgeom.c:102
uint32_t ngeoms
Definition: liblwgeom.h:496
mvt_column_cache column_cache
Definition: mvt.h:79
#define POLYGONTYPE
Definition: liblwgeom.h:86
static uint32_t c_int(enum mvt_cmd_id id, uint32_t count)
Definition: mvt.c:105
char * string_value
Definition: mvt.c:65
#define __attribute__(x)
Definition: liblwgeom.h:200
double xmax
Definition: liblwgeom.h:295
static bytea * mvt_ctx_to_bytea(mvt_agg_context *ctx)
Definition: mvt.c:1028
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1144
static VectorTile__Tile__Layer * vectortile_layer_combine(const VectorTile__Tile__Layer *layer1, const VectorTile__Tile__Layer *layer2)
Definition: mvt.c:1146
LWGEOM * lwgeom_clip_by_rect(const LWGEOM *geom1, double x0, double y0, double x1, double y1)
#define MULTIPOINTTYPE
Definition: liblwgeom.h:87
uint32_t id
Definition: mvt.c:59
uint32_t id
Definition: mvt.c:80
static VectorTile__Tile * mvt_ctx_to_tile(mvt_agg_context *ctx)
Definition: mvt.c:1013
uint32_t id
Definition: mvt.c:66
static void parse_values(mvt_agg_context *ctx)
Definition: mvt.c:629
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
data
Definition: ovdump.py:103
UT_hash_handle hh
Definition: mvt.c:95
VectorTile__Tile * tile
Definition: mvt.h:67
static void encode_point(mvt_agg_context *ctx, LWPOINT *point)
Definition: mvt.c:174
char * geom_name
Definition: mvt.h:62
struct mvt_kv_float_value * float_values_hash
Definition: mvt.h:71
uint32_t ngeoms
Definition: liblwgeom.h:509
POINTARRAY * point
Definition: liblwgeom.h:413
bool * nulls
Definition: mvt.h:54
double ifac
Definition: liblwgeom.h:272
void * next
Definition: uthash.h:1085
static void * mvt_allocator(__attribute__((__unused__)) void *data, size_t size)
Definition: mvt.c:1064
double xoff
Definition: liblwgeom.h:272
uint32_t nrings
Definition: liblwgeom.h:457
double afac
Definition: liblwgeom.h:272
static uint32_t p_int(int32_t value)
Definition: mvt.c:110
static uint32_t add_key(mvt_agg_context *ctx, char *name)
Definition: mvt.c:307
uint32_t id
Definition: mvt.c:73
mvt_agg_context * mvt_ctx_combine(mvt_agg_context *ctx1, mvt_agg_context *ctx2)
Definition: mvt.c:1234
void lwgeom_simplify_in_place(LWGEOM *igeom, double dist, int preserve_collapsed)
Definition: lwgeom.c:1748
unsigned int uint32_t
Definition: uthash.h:78
VectorTile__Tile__Layer * layer
Definition: mvt.h:66
double x
Definition: liblwgeom.h:330
static uint32_t encode_ptarray_initial(mvt_agg_context *ctx, enum mvt_type type, POINTARRAY *pa, uint32_t *buffer)
Definition: mvt.c:166
void lwgeom_force_clockwise(LWGEOM *lwgeom)
Force Right-hand-rule on LWGEOM polygons.
Definition: lwgeom.c:37
Datum buffer(PG_FUNCTION_ARGS)
double ymin
Definition: liblwgeom.h:296
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:218
protobuf_c_boolean bool_value
Definition: mvt.c:100
uint32_t keys_hash_i
Definition: mvt.h:77
Definition: mvt.c:53
double xmin
Definition: liblwgeom.h:294
LWGEOM * lwgeom_make_valid(LWGEOM *geom)
Attempts to make an invalid geometries valid w/out losing points.
size_t features_capacity
Definition: mvt.h:68
static void encode_mline(mvt_agg_context *ctx, LWMLINE *lwmline)
Definition: mvt.c:210
#define HASH_FIND(hh, head, keyptr, keylen, out)
Definition: uthash.h:132
UT_hash_handle hh
Definition: mvt.c:102
float float_value
Definition: mvt.c:72
LWPOLY ** geoms
Definition: liblwgeom.h:498
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:75
LWLINE * lwline_from_lwmpoint(int srid, const LWMPOINT *mpoint)
Definition: lwline.c:284
LWGEOM ** geoms
Definition: liblwgeom.h:511
static void encode_mpoint(mvt_agg_context *ctx, LWMPOINT *mpoint)
Definition: mvt.c:184
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:788
static TupleDesc get_tuple_desc(mvt_agg_context *ctx)
Definition: mvt.c:290
const GBOX * lwgeom_get_bbox(const LWGEOM *lwgeom)
Get a non-empty geometry bounding box, computing and caching it if not already there.
Definition: lwgeom.c:734
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:509
POINTARRAY ** rings
Definition: liblwgeom.h:459
int count
Definition: genraster.py:56
#define UINT32_MAX
Definition: lwin_wkt_lex.c:343
Definition: mvt.c:56
struct mvt_kv_uint_value * uint_values_hash
Definition: mvt.h:73
uint32_t geom_index
Definition: mvt.h:63
UT_hash_handle hh
Definition: mvt.c:81
double ymax
Definition: liblwgeom.h:297
#define MVT_PARSE_VALUE(value, kvtype, hash, valuefield, size)
Definition: mvt.c:458
double y
Definition: liblwgeom.h:330
uint32_t id
Definition: mvt.c:87
uint32_t id
Definition: mvt.c:94
uint32_t * column_keys_index
Definition: mvt.h:51
static void encode_line(mvt_agg_context *ctx, LWLINE *lwline)
Definition: mvt.c:198
static uint32_t * parse_jsonb(mvt_agg_context *ctx, Jsonb *jb, uint32_t *tags)
Definition: mvt.c:550
static void parse_datum_as_string(mvt_agg_context *ctx, Oid typoid, Datum datum, uint32_t *tags, uint32_t k)
Definition: mvt.c:536
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:89
Definition: mvt.c:52
struct mvt_kv_bool_value * bool_values_hash
Definition: mvt.h:75
uint32_t ngeoms
Definition: liblwgeom.h:483
LWLINE ** geoms
Definition: liblwgeom.h:485
static VectorTile__Tile__Feature * tile_feature_copy(const VectorTile__Tile__Feature *feature, int key_offset, int value_offset)
Definition: mvt.c:1102
int64_t sint_value
Definition: mvt.c:93
static uint32_t get_key_index_with_size(mvt_agg_context *ctx, const char *name, size_t size)
Definition: mvt.c:298
Datum * values
Definition: mvt.h:53
uint32_t values_hash_i
Definition: mvt.h:76
mvt_cmd_id
Definition: mvt.c:42
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
#define FEATURES_CAPACITY_INITIAL
Definition: mvt.c:40
#define HASH_CLEAR(hh, head)
Definition: uthash.h:993
uint32_t extent
Definition: mvt.h:61
UT_hash_handle hh
Definition: mvt.c:88
bytea * mvt_ctx_serialize(mvt_agg_context *ctx)
Definition: mvt.c:1059
VectorTile__Tile__Feature * feature
Definition: mvt.h:65
struct mvt_kv_key * keys_hash
Definition: mvt.h:69
static uint8 lwgeom_get_basic_type(LWGEOM *geom)
Definition: mvt.c:733
static void encode_poly(mvt_agg_context *ctx, LWPOLY *lwpoly)
Definition: mvt.c:228
struct mvt_kv_sint_value * sint_values_hash
Definition: mvt.h:74
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:84
double efac
Definition: liblwgeom.h:272
static VectorTile__Tile__Value * create_value()
Definition: mvt.c:391
static void encode_values(mvt_agg_context *ctx)
Definition: mvt.c:413
void lwgeom_grid_in_place(LWGEOM *lwgeom, const gridspec *grid)
Definition: lwgeom.c:2145
#define MVT_PARSE_INT_VALUE(value)
Definition: mvt.c:479
void mvt_agg_init_context(mvt_agg_context *ctx)
Initialize aggregation context.
Definition: mvt.c:920
UT_hash_handle hh
Definition: mvt.c:60
static void encode_mpoly(mvt_agg_context *ctx, LWMPOLY *lwmpoly)
Definition: mvt.c:246
uint8_t type
Definition: liblwgeom.h:398
type
Definition: ovdump.py:41
UT_hash_handle hh
Definition: mvt.c:67
double yoff
Definition: liblwgeom.h:272
#define MVT_PARSE_DATUM(type, kvtype, hash, valuefield, datumfunc, size)
Definition: mvt.c:497
int value
Definition: genraster.py:61
#define DatumGetJsonbP
Definition: mvt.c:35
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
mvt_type
Definition: mvt.c:49
char * name
Definition: mvt.c:58
static void encode_keys(mvt_agg_context *ctx)
Definition: mvt.c:378
uint32_t * column_oid
Definition: mvt.h:52
#define MULTILINETYPE
Definition: liblwgeom.h:88
double double_value
Definition: mvt.c:79
unsigned char uint8_t
Definition: uthash.h:79
TupleDesc tupdesc
Definition: mvt.h:55
static void parse_column_keys(mvt_agg_context *ctx)
Definition: mvt.c:318
int32_t srid
Definition: liblwgeom.h:469
UT_hash_handle hh
Definition: mvt.c:74
#define MVT_CREATE_VALUES(kvtype, hash, hasfield, valuefield)
Definition: mvt.c:398
mvt_agg_context * mvt_ctx_deserialize(const bytea *ba)
Definition: mvt.c:1074
static void encode_geometry(mvt_agg_context *ctx, LWGEOM *lwgeom)
Definition: mvt.c:267
static VectorTile__Tile * vectortile_tile_combine(VectorTile__Tile *tile1, VectorTile__Tile *tile2)
Definition: mvt.c:1195
#define MVT_PARSE_INT_DATUM(type, datumfunc)
Definition: mvt.c:503
static void 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:770
bytea * mvt_agg_finalfn(mvt_agg_context *ctx)
Finalize aggregation.
Definition: mvt.c:1267
void lwgeom_affine(LWGEOM *geom, const AFFINE *affine)
Definition: lwgeom.c:1969
#define COLLECTIONTYPE
Definition: liblwgeom.h:90
POINTARRAY * points
Definition: liblwgeom.h:424
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:364
void mvt_agg_transfn(mvt_agg_context *ctx)
Aggregation step.
Definition: mvt.c:963
Snap to grid.
uint32_t npoints
Definition: liblwgeom.h:373
static void mvt_deallocator(__attribute__((__unused__)) void *data, void *ptr)
Definition: mvt.c:1069
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:115