PostGIS  2.5.0dev-r@@SVN_REVISION@@
mvt.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  * http://postgis.net
5  *
6  * PostGIS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * PostGIS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
18  *
19  **********************************************************************
20  *
21  * Copyright (C) 2016-2017 Bj√∂rn Harrtell <bjorn@wololo.org>
22  *
23  **********************************************************************/
24 
25 #include "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 
42 enum mvt_cmd_id {
46 };
47 
48 enum mvt_type {
49  MVT_POINT = 1,
50  MVT_LINE = 2,
52 };
53 
54 struct mvt_kv_key {
55  char *name;
58 };
59 
61  char *string_value;
64 };
65 
67  float float_value;
70 };
71 
73  double double_value;
76 };
77 
79  uint64_t uint_value;
82 };
83 
85  int64_t sint_value;
88 };
89 
91  protobuf_c_boolean bool_value;
94 };
95 
96 static inline uint32_t c_int(enum mvt_cmd_id id, uint32_t count)
97 {
98  return (id & 0x7) | (count << 3);
99 }
100 
101 static inline uint32_t p_int(int32_t value)
102 {
103  return (value << 1) ^ (value >> 31);
104 }
105 
107  enum mvt_type type, POINTARRAY *pa, uint32_t *buffer,
108  int32_t *px, int32_t *py)
109 {
110  uint32_t offset = 0;
111  uint32_t i, c = 0;
112  int32_t dx, dy, x, y;
113  const POINT2D *p;
114 
115  /* loop points and add to buffer */
116  for (i = 0; i < pa->npoints; i++) {
117  /* move offset for command */
118  if (i == 0 || (i == 1 && type > MVT_POINT))
119  offset++;
120  /* skip closing point for rings */
121  if (type == MVT_RING && i == pa->npoints - 1)
122  break;
123  p = getPoint2d_cp(pa, i);
124  x = p->x;
125  y = p->y;
126  dx = x - *px;
127  dy = y - *py;
128  buffer[offset++] = p_int(dx);
129  buffer[offset++] = p_int(dy);
130  *px = x;
131  *py = y;
132  c++;
133  }
134 
135  /* determine initial move and eventual line command */
136  if (type == MVT_POINT) {
137  /* point or multipoint, use actual number of point count */
138  buffer[0] = c_int(CMD_MOVE_TO, c);
139  } else {
140  /* line or polygon, assume count 1 */
141  buffer[0] = c_int(CMD_MOVE_TO, 1);
142  /* line command with move point subtracted from count */
143  buffer[3] = c_int(CMD_LINE_TO, c - 1);
144  }
145 
146  /* add close command if ring */
147  if (type == MVT_RING)
148  buffer[offset++] = c_int(CMD_CLOSE_PATH, 1);
149 
150  return offset;
151 }
152 
154  enum mvt_type type,
155  POINTARRAY *pa, uint32_t *buffer)
156 {
157  int32_t px = 0, py = 0;
158  return encode_ptarray(ctx, type, pa, buffer, &px, &py);
159 }
160 
161 static void encode_point(mvt_agg_context *ctx, LWPOINT *point)
162 {
163  VectorTile__Tile__Feature *feature = ctx->feature;
164  feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POINT;
165  feature->has_type = 1;
166  feature->n_geometry = 3;
167  feature->geometry = palloc(sizeof(*feature->geometry) * 3);
168  encode_ptarray_initial(ctx, MVT_POINT, point->point, feature->geometry);
169 }
170 
171 static void encode_mpoint(mvt_agg_context *ctx, LWMPOINT *mpoint)
172 {
173  size_t c;
174  VectorTile__Tile__Feature *feature = ctx->feature;
175  // NOTE: inefficient shortcut LWMPOINT->LWLINE
176  LWLINE *lwline = lwline_from_lwmpoint(mpoint->srid, mpoint);
177  feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POINT;
178  feature->has_type = 1;
179  c = 1 + lwline->points->npoints * 2;
180  feature->geometry = palloc(sizeof(*feature->geometry) * c);
181  feature->n_geometry = encode_ptarray_initial(ctx, MVT_POINT,
182  lwline->points, feature->geometry);
183 }
184 
185 static void encode_line(mvt_agg_context *ctx, LWLINE *lwline)
186 {
187  size_t c;
188  VectorTile__Tile__Feature *feature = ctx->feature;
189  feature->type = VECTOR_TILE__TILE__GEOM_TYPE__LINESTRING;
190  feature->has_type = 1;
191  c = 2 + lwline->points->npoints * 2;
192  feature->geometry = palloc(sizeof(*feature->geometry) * c);
193  feature->n_geometry = encode_ptarray_initial(ctx, MVT_LINE,
194  lwline->points, feature->geometry);
195 }
196 
197 static void encode_mline(mvt_agg_context *ctx, LWMLINE *lwmline)
198 {
199  uint32_t i;
200  int32_t px = 0, py = 0;
201  size_t c = 0, offset = 0;
202  VectorTile__Tile__Feature *feature = ctx->feature;
203  feature->type = VECTOR_TILE__TILE__GEOM_TYPE__LINESTRING;
204  feature->has_type = 1;
205  for (i = 0; i < lwmline->ngeoms; i++)
206  c += 2 + lwmline->geoms[i]->points->npoints * 2;
207  feature->geometry = palloc(sizeof(*feature->geometry) * c);
208  for (i = 0; i < lwmline->ngeoms; i++)
209  offset += encode_ptarray(ctx, MVT_LINE,
210  lwmline->geoms[i]->points,
211  feature->geometry + offset, &px, &py);
212  feature->n_geometry = offset;
213 }
214 
215 static void encode_poly(mvt_agg_context *ctx, LWPOLY *lwpoly)
216 {
217  uint32_t i;
218  int32_t px = 0, py = 0;
219  size_t c = 0, offset = 0;
220  VectorTile__Tile__Feature *feature = ctx->feature;
221  feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POLYGON;
222  feature->has_type = 1;
223  for (i = 0; i < lwpoly->nrings; i++)
224  c += 3 + ((lwpoly->rings[i]->npoints - 1) * 2);
225  feature->geometry = palloc(sizeof(*feature->geometry) * c);
226  for (i = 0; i < lwpoly->nrings; i++)
227  offset += encode_ptarray(ctx, MVT_RING,
228  lwpoly->rings[i],
229  feature->geometry + offset, &px, &py);
230  feature->n_geometry = offset;
231 }
232 
233 static void encode_mpoly(mvt_agg_context *ctx, LWMPOLY *lwmpoly)
234 {
235  uint32_t i, j;
236  int32_t px = 0, py = 0;
237  size_t c = 0, offset = 0;
238  LWPOLY *poly;
239  VectorTile__Tile__Feature *feature = ctx->feature;
240  feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POLYGON;
241  feature->has_type = 1;
242  for (i = 0; i < lwmpoly->ngeoms; i++)
243  for (j = 0; poly = lwmpoly->geoms[i], j < poly->nrings; j++)
244  c += 3 + ((poly->rings[j]->npoints - 1) * 2);
245  feature->geometry = palloc(sizeof(*feature->geometry) * c);
246  for (i = 0; i < lwmpoly->ngeoms; i++)
247  for (j = 0; poly = lwmpoly->geoms[i], j < poly->nrings; j++)
248  offset += encode_ptarray(ctx, MVT_RING,
249  poly->rings[j], feature->geometry + offset,
250  &px, &py);
251  feature->n_geometry = offset;
252 }
253 
254 static void encode_geometry(mvt_agg_context *ctx, LWGEOM *lwgeom)
255 {
256  int type = lwgeom->type;
257 
258  switch (type) {
259  case POINTTYPE:
260  return encode_point(ctx, (LWPOINT*)lwgeom);
261  case LINETYPE:
262  return encode_line(ctx, (LWLINE*)lwgeom);
263  case POLYGONTYPE:
264  return encode_poly(ctx, (LWPOLY*)lwgeom);
265  case MULTIPOINTTYPE:
266  return encode_mpoint(ctx, (LWMPOINT*)lwgeom);
267  case MULTILINETYPE:
268  return encode_mline(ctx, (LWMLINE*)lwgeom);
269  case MULTIPOLYGONTYPE:
270  return encode_mpoly(ctx, (LWMPOLY*)lwgeom);
271  default: elog(ERROR, "encode_geometry: '%s' geometry type not supported",
272  lwtype_name(type));
273  }
274 }
275 
276 static TupleDesc get_tuple_desc(mvt_agg_context *ctx)
277 {
278  Oid tupType = HeapTupleHeaderGetTypeId(ctx->row);
279  int32 tupTypmod = HeapTupleHeaderGetTypMod(ctx->row);
280  TupleDesc tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
281  return tupdesc;
282 }
283 
285 {
286  struct mvt_kv_key *kv;
287  size_t size = strlen(name);
288  HASH_FIND(hh, ctx->keys_hash, name, size, kv);
289  if (!kv)
290  return UINT32_MAX;
291  return kv->id;
292 }
293 
295 {
296  struct mvt_kv_key *kv;
297  size_t size = strlen(name);
298  kv = palloc(sizeof(*kv));
299  kv->id = ctx->keys_hash_i++;
300  kv->name = name;
301  HASH_ADD_KEYPTR(hh, ctx->keys_hash, name, size, kv);
302  return kv->id;
303 }
304 
306 {
307  TupleDesc tupdesc = get_tuple_desc(ctx);
308  uint32_t natts = (uint32_t) tupdesc->natts;
309  uint32_t i;
310  bool geom_found = false;
311  char *key;
312  POSTGIS_DEBUG(2, "parse_column_keys called");
313 
314  for (i = 0; i < natts; i++) {
315 #if POSTGIS_PGSQL_VERSION < 110
316  Oid typoid = getBaseType(tupdesc->attrs[i]->atttypid);
317  char *tkey = tupdesc->attrs[i]->attname.data;
318 #else
319  Oid typoid = getBaseType(tupdesc->attrs[i].atttypid);
320  char *tkey = tupdesc->attrs[i].attname.data;
321 #endif
322 #if POSTGIS_PGSQL_VERSION >= 94
323  if (typoid == JSONBOID)
324  continue;
325 #endif
326  key = pstrdup(tkey);
327  if (ctx->geom_name == NULL) {
328  if (!geom_found && typoid == TypenameGetTypid("geometry")) {
329  ctx->geom_index = i;
330  geom_found = true;
331  continue;
332  }
333  } else {
334  if (!geom_found && strcmp(key, ctx->geom_name) == 0) {
335  ctx->geom_index = i;
336  geom_found = true;
337  continue;
338  }
339  }
340  add_key(ctx, key);
341  }
342  if (!geom_found)
343  elog(ERROR, "parse_column_keys: no geometry column found");
344  ReleaseTupleDesc(tupdesc);
345 }
346 
347 static void encode_keys(mvt_agg_context *ctx)
348 {
349  struct mvt_kv_key *kv;
350  size_t n_keys = ctx->keys_hash_i;
351  char **keys = palloc(n_keys * sizeof(*keys));
352  for (kv = ctx->keys_hash; kv != NULL; kv=kv->hh.next)
353  keys[kv->id] = kv->name;
354  ctx->layer->n_keys = n_keys;
355  ctx->layer->keys = keys;
356 
357  HASH_CLEAR(hh, ctx->keys_hash);
358 }
359 
360 static VectorTile__Tile__Value *create_value()
361 {
362  VectorTile__Tile__Value *value = palloc(sizeof(*value));
363  vector_tile__tile__value__init(value);
364  return value;
365 }
366 
367 #define MVT_CREATE_VALUES(kvtype, hash, hasfield, valuefield) \
368 { \
369  POSTGIS_DEBUG(2, "MVT_CREATE_VALUES called"); \
370  { \
371  struct kvtype *kv; \
372  for (kv = ctx->hash; kv != NULL; kv=kv->hh.next) { \
373  VectorTile__Tile__Value *value = create_value(); \
374  value->hasfield = 1; \
375  value->valuefield = kv->valuefield; \
376  values[kv->id] = value; \
377  } \
378  } \
379 }
380 
382 {
383  VectorTile__Tile__Value **values;
384  struct mvt_kv_string_value *kv;
385 
386  POSTGIS_DEBUG(2, "encode_values called");
387 
388  values = palloc(ctx->values_hash_i * sizeof(*values));
389  for (kv = ctx->string_values_hash; kv != NULL; kv=kv->hh.next) {
390  VectorTile__Tile__Value *value = create_value();
391  value->string_value = kv->string_value;
392  values[kv->id] = value;
393  }
395  float_values_hash, has_float_value, float_value);
397  double_values_hash, has_double_value, double_value);
399  uint_values_hash, has_uint_value, uint_value);
401  sint_values_hash, has_sint_value, sint_value);
403  bool_values_hash, has_bool_value, bool_value);
404 
405  POSTGIS_DEBUGF(3, "encode_values n_values: %d", ctx->values_hash_i);
406  ctx->layer->n_values = ctx->values_hash_i;
407  ctx->layer->values = values;
408 
415 }
416 
417 #define MVT_PARSE_VALUE(value, kvtype, hash, valuefield, size) \
418 { \
419  POSTGIS_DEBUG(2, "MVT_PARSE_VALUE called"); \
420  { \
421  struct kvtype *kv; \
422  HASH_FIND(hh, ctx->hash, &value, size, kv); \
423  if (!kv) { \
424  POSTGIS_DEBUG(4, "MVT_PARSE_VALUE value not found"); \
425  kv = palloc(sizeof(*kv)); \
426  POSTGIS_DEBUGF(4, "MVT_PARSE_VALUE new hash key: %d", \
427  ctx->values_hash_i); \
428  kv->id = ctx->values_hash_i++; \
429  kv->valuefield = value; \
430  HASH_ADD(hh, ctx->hash, valuefield, size, kv); \
431  } \
432  tags[ctx->c*2] = k; \
433  tags[ctx->c*2+1] = kv->id; \
434  } \
435 }
436 
437 #define MVT_PARSE_INT_VALUE(value) \
438 { \
439  if (value >= 0) { \
440  uint64_t cvalue = value; \
441  MVT_PARSE_VALUE(cvalue, mvt_kv_uint_value, \
442  uint_values_hash, uint_value, \
443  sizeof(uint64_t)) \
444  } else { \
445  int64_t cvalue = value; \
446  MVT_PARSE_VALUE(cvalue, mvt_kv_sint_value, \
447  sint_values_hash, sint_value, \
448  sizeof(int64_t)) \
449  } \
450 }
451 
452 #define MVT_PARSE_DATUM(type, kvtype, hash, valuefield, datumfunc, size) \
453 { \
454  type value = datumfunc(datum); \
455  MVT_PARSE_VALUE(value, kvtype, hash, valuefield, size); \
456 }
457 
458 #define MVT_PARSE_INT_DATUM(type, datumfunc) \
459 { \
460  type value = datumfunc(datum); \
461  MVT_PARSE_INT_VALUE(value); \
462 }
463 
465  char *value, uint32_t *tags, uint32_t k)
466 {
467  struct mvt_kv_string_value *kv;
468  size_t size = strlen(value);
469  POSTGIS_DEBUG(2, "add_value_as_string called");
470  HASH_FIND(hh, ctx->string_values_hash, value, size, kv);
471  if (!kv) {
472  POSTGIS_DEBUG(4, "add_value_as_string value not found");
473  kv = palloc(sizeof(*kv));
474  POSTGIS_DEBUGF(4, "add_value_as_string new hash key: %d",
475  ctx->values_hash_i);
476  kv->id = ctx->values_hash_i++;
477  kv->string_value = value;
479  size, kv);
480  }
481  tags[ctx->c*2] = k;
482  tags[ctx->c*2+1] = kv->id;
483 }
484 
485 static void parse_datum_as_string(mvt_agg_context *ctx, Oid typoid,
486  Datum datum, uint32_t *tags, uint32_t k)
487 {
488  Oid foutoid;
489  bool typisvarlena;
490  char *value;
491  POSTGIS_DEBUG(2, "parse_value_as_string called");
492  getTypeOutputInfo(typoid, &foutoid, &typisvarlena);
493  value = OidOutputFunctionCall(foutoid, datum);
494  POSTGIS_DEBUGF(4, "parse_value_as_string value: %s", value);
495  add_value_as_string(ctx, value, tags, k);
496 }
497 
498 #if POSTGIS_PGSQL_VERSION >= 94
499 static uint32_t *parse_jsonb(mvt_agg_context *ctx, Jsonb *jb,
500  uint32_t *tags)
501 {
502  JsonbIterator *it;
503  JsonbValue v;
504  bool skipNested = false;
505  JsonbIteratorToken r;
506  uint32_t k;
507 
508  if (!JB_ROOT_IS_OBJECT(jb))
509  return tags;
510 
511  it = JsonbIteratorInit(&jb->root);
512 
513  while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE) {
514  skipNested = true;
515 
516  if (r == WJB_KEY && v.type != jbvNull) {
517  char *key;
518  key = palloc(v.val.string.len + 1 * sizeof(char));
519  memcpy(key, v.val.string.val, v.val.string.len);
520  key[v.val.string.len] = '\0';
521 
522  k = get_key_index(ctx, key);
523  if (k == UINT32_MAX) {
524  uint32_t newSize = ctx->keys_hash_i + 1;
525  tags = repalloc(tags, newSize * 2 * sizeof(*tags));
526  k = add_key(ctx, key);
527  }
528 
529  r = JsonbIteratorNext(&it, &v, skipNested);
530 
531  if (v.type == jbvString) {
532  char *value;
533  value = palloc(v.val.string.len + 1 * sizeof(char));
534  memcpy(value, v.val.string.val, v.val.string.len);
535  value[v.val.string.len] = '\0';
536  add_value_as_string(ctx, value, tags, k);
537  ctx->c++;
538  } else if (v.type == jbvBool) {
539  MVT_PARSE_VALUE(v.val.boolean, mvt_kv_bool_value,
540  bool_values_hash, bool_value, sizeof(protobuf_c_boolean));
541  ctx->c++;
542  } else if (v.type == jbvNumeric) {
543  char *str;
544  double d;
545  long l;
546  str = DatumGetCString(DirectFunctionCall1(numeric_out,
547  PointerGetDatum(v.val.numeric)));
548  d = strtod(str, NULL);
549  l = strtol(str, NULL, 10);
550  if ((long) d != l) {
551  MVT_PARSE_VALUE(d, mvt_kv_double_value, double_values_hash,
552  double_value, sizeof(double));
553  } else {
555  }
556  ctx->c++;
557  }
558  }
559  }
560 
561  return tags;
562 }
563 #endif
564 
565 static void parse_values(mvt_agg_context *ctx)
566 {
567  uint32_t n_keys = ctx->keys_hash_i;
568  uint32_t *tags = palloc(n_keys * 2 * sizeof(*tags));
569  bool isnull;
570  uint32_t i, k;
571  TupleDesc tupdesc = get_tuple_desc(ctx);
572  uint32_t natts = (uint32_t) tupdesc->natts;
573  ctx->c = 0;
574  POSTGIS_DEBUG(2, "parse_values called");
575 
576  POSTGIS_DEBUGF(3, "parse_values natts: %d", natts);
577 
578  for (i = 0; i < natts; i++) {
579  char *key;
580  Oid typoid;
581  Datum datum;
582 
583  if (i == ctx->geom_index)
584  continue;
585 
586 #if POSTGIS_PGSQL_VERSION < 110
587  key = tupdesc->attrs[i]->attname.data;
588  typoid = getBaseType(tupdesc->attrs[i]->atttypid);
589 #else
590  key = tupdesc->attrs[i].attname.data;
591  typoid = getBaseType(tupdesc->attrs[i].atttypid);
592 #endif
593  datum = GetAttributeByNum(ctx->row, i+1, &isnull);
594  k = get_key_index(ctx, key);
595  if (isnull) {
596  POSTGIS_DEBUG(3, "parse_values isnull detected");
597  continue;
598  }
599 #if POSTGIS_PGSQL_VERSION >= 94
600  if (k == UINT32_MAX && typoid != JSONBOID)
601  elog(ERROR, "parse_values: unexpectedly could not find parsed key name '%s'", key);
602  if (typoid == JSONBOID) {
603  tags = parse_jsonb(ctx, DatumGetJsonbP(datum), tags);
604  continue;
605  }
606 #else
607  if (k == -1)
608  elog(ERROR, "parse_values: unexpectedly could not find parsed key name '%s'", key);
609 #endif
610 
611  switch (typoid) {
612  case BOOLOID:
613  MVT_PARSE_DATUM(protobuf_c_boolean, mvt_kv_bool_value,
614  bool_values_hash, bool_value,
615  DatumGetBool, sizeof(protobuf_c_boolean));
616  break;
617  case INT2OID:
618  MVT_PARSE_INT_DATUM(int16_t, DatumGetInt16);
619  break;
620  case INT4OID:
621  MVT_PARSE_INT_DATUM(int32_t, DatumGetInt32);
622  break;
623  case INT8OID:
624  MVT_PARSE_INT_DATUM(int64_t, DatumGetInt64);
625  break;
626  case FLOAT4OID:
628  float_values_hash, float_value,
629  DatumGetFloat4, sizeof(float));
630  break;
631  case FLOAT8OID:
633  double_values_hash, double_value,
634  DatumGetFloat8, sizeof(double));
635  break;
636  default:
637  parse_datum_as_string(ctx, typoid, datum, tags, k);
638  break;
639  }
640  ctx->c++;
641  }
642 
643  ReleaseTupleDesc(tupdesc);
644 
645  ctx->feature->n_tags = ctx->c * 2;
646  ctx->feature->tags = tags;
647 
648  POSTGIS_DEBUGF(3, "parse_values n_tags %zd", ctx->feature->n_tags);
649 }
650 
657 static void
659 {
660  if (lwgeom_get_type(geom) == COLLECTIONTYPE)
661  {
662  /* MVT doesn't handle generic collections, so we */
663  /* need to strip them down to a typed collection */
664  /* by finding the largest basic type available and */
665  /* using that as the basis of a typed collection. */
666  LWCOLLECTION *g = (LWCOLLECTION*)geom;
667  LWCOLLECTION *gc;
668  uint32_t i, maxtype = 0;
669  for (i = 0; i < g->ngeoms; i++)
670  {
671  LWGEOM *sg = g->geoms[i];
672  if (sg->type > maxtype && sg->type < COLLECTIONTYPE)
673  maxtype = sg->type;
674  }
675  if (maxtype > 3) maxtype -= 3;
676  /* Force the working geometry to be a simpler version */
677  /* of itself */
678  gc = lwcollection_extract(g, maxtype);
679  *g = *gc;
680  }
681  return;
682 }
683 
692 LWGEOM *mvt_geom(LWGEOM *lwgeom, const GBOX *gbox, uint32_t extent, uint32_t buffer,
693  bool clip_geom)
694 {
695  AFFINE affine;
696  gridspec grid;
697  double width = gbox->xmax - gbox->xmin;
698  double height = gbox->ymax - gbox->ymin;
699  double resx = width / extent;
700  double resy = height / extent;
701  double res = (resx < resy ? resx : resy)/2;
702  double fx = extent / width;
703  double fy = -(extent / height);
704  double buffer_map_xunits = resx * buffer;
705  int preserve_collapsed = LW_TRUE;
706  POSTGIS_DEBUG(2, "mvt_geom called");
707 
708  /* Short circuit out on EMPTY */
709  if (lwgeom_is_empty(lwgeom))
710  return NULL;
711 
712  if (width == 0 || height == 0)
713  elog(ERROR, "mvt_geom: bounds width or height cannot be 0");
714 
715  if (extent == 0)
716  elog(ERROR, "mvt_geom: extent cannot be 0");
717 
718  /* Remove all non-essential points (under the output resolution) */
720  lwgeom_simplify_in_place(lwgeom, res, preserve_collapsed);
721 
722  /* If geometry has disappeared, you're done */
723  if (lwgeom_is_empty(lwgeom))
724  return NULL;
725 
726  if (clip_geom)
727  {
728  GBOX bgbox;
729  const GBOX *lwgeom_gbox = lwgeom_get_bbox(lwgeom);;
730  bgbox = *gbox;
731  gbox_expand(&bgbox, buffer_map_xunits);
732  if (!gbox_overlaps_2d(lwgeom_gbox, &bgbox))
733  {
734  POSTGIS_DEBUG(3, "mvt_geom: geometry outside clip box");
735  return NULL;
736  }
737  if (!gbox_contains_2d(&bgbox, lwgeom_gbox))
738  {
739  double x0 = bgbox.xmin;
740  double y0 = bgbox.ymin;
741  double x1 = bgbox.xmax;
742  double y1 = bgbox.ymax;
743 #if POSTGIS_GEOS_VERSION < 35
744  LWPOLY *lwenv = lwpoly_construct_envelope(0, x0, y0, x1, y1);
745  lwgeom = lwgeom_intersection(lwgeom, lwpoly_as_lwgeom(lwenv));
746  lwpoly_free(lwenv);
747 #else
748  lwgeom = lwgeom_clip_by_rect(lwgeom, x0, y0, x1, y1);
749 #endif
750  POSTGIS_DEBUG(3, "mvt_geom: no geometry after clip");
751  if (lwgeom == NULL || lwgeom_is_empty(lwgeom))
752  return NULL;
753  }
754  }
755 
756  /* transform to tile coordinate space */
757  memset(&affine, 0, sizeof(affine));
758  affine.afac = fx;
759  affine.efac = fy;
760  affine.ifac = 1;
761  affine.xoff = -gbox->xmin * fx;
762  affine.yoff = -gbox->ymax * fy;
763  lwgeom_affine(lwgeom, &affine);
764 
765  /* snap to integer precision, removing duplicate points */
766  memset(&grid, 0, sizeof(gridspec));
767  grid.ipx = 0;
768  grid.ipy = 0;
769  grid.xsize = 1;
770  grid.ysize = 1;
771  lwgeom_grid_in_place(lwgeom, &grid);
772 
773  if (lwgeom == NULL || lwgeom_is_empty(lwgeom))
774  return NULL;
775 
776  /* if polygon(s) make valid and force clockwise as per MVT spec */
777  if (lwgeom->type == POLYGONTYPE ||
778  lwgeom->type == MULTIPOLYGONTYPE ||
779  lwgeom->type == COLLECTIONTYPE)
780  {
781  lwgeom = lwgeom_make_valid(lwgeom);
782  lwgeom_force_clockwise(lwgeom);
783  }
784 
785  /* if geometry collection extract highest dimensional geometry type */
786  if (lwgeom->type == COLLECTIONTYPE)
787  lwgeom_to_basic_type(lwgeom);
788 
789  if (lwgeom == NULL || lwgeom_is_empty(lwgeom))
790  return NULL;
791 
792  return lwgeom;
793 }
794 
799 {
800  VectorTile__Tile__Layer *layer;
801 
802  POSTGIS_DEBUG(2, "mvt_agg_init_context called");
803 
804  if (ctx->extent == 0)
805  elog(ERROR, "mvt_agg_init_context: extent cannot be 0");
806 
807  ctx->tile = NULL;
809  ctx->keys_hash = NULL;
810  ctx->string_values_hash = NULL;
811  ctx->float_values_hash = NULL;
812  ctx->double_values_hash = NULL;
813  ctx->uint_values_hash = NULL;
814  ctx->sint_values_hash = NULL;
815  ctx->bool_values_hash = NULL;
816  ctx->values_hash_i = 0;
817  ctx->keys_hash_i = 0;
818 
819  layer = palloc(sizeof(*layer));
820  vector_tile__tile__layer__init(layer);
821  layer->version = 2;
822  layer->name = ctx->name;
823  layer->has_extent = 1;
824  layer->extent = ctx->extent;
825  layer->features = palloc (ctx->features_capacity *
826  sizeof(*layer->features));
827 
828  ctx->layer = layer;
829 }
830 
839 {
840  bool isnull = false;
841  Datum datum;
842  GSERIALIZED *gs;
843  LWGEOM *lwgeom;
844  VectorTile__Tile__Feature *feature;
845  VectorTile__Tile__Layer *layer = ctx->layer;
846 /* VectorTile__Tile__Feature **features = layer->features; */
847  POSTGIS_DEBUG(2, "mvt_agg_transfn called");
848 
849  if (layer->n_features >= ctx->features_capacity) {
850  size_t new_capacity = ctx->features_capacity * 2;
851  layer->features = repalloc(layer->features, new_capacity *
852  sizeof(*layer->features));
853  ctx->features_capacity = new_capacity;
854  POSTGIS_DEBUGF(3, "mvt_agg_transfn new_capacity: %zd", new_capacity);
855  }
856 
857  if (layer->n_features == 0)
858  parse_column_keys(ctx);
859 
860  datum = GetAttributeByNum(ctx->row, ctx->geom_index + 1, &isnull);
861  POSTGIS_DEBUGF(3, "mvt_agg_transfn ctx->geom_index: %d", ctx->geom_index);
862  POSTGIS_DEBUGF(3, "mvt_agg_transfn isnull: %u", isnull);
863  POSTGIS_DEBUGF(3, "mvt_agg_transfn datum: %lu", datum);
864  if (isnull) /* Skip rows that have null geometry */
865  {
866  POSTGIS_DEBUG(3, "mvt_agg_transfn got null geom");
867  return;
868  }
869 
870  feature = palloc(sizeof(*feature));
871  vector_tile__tile__feature__init(feature);
872 
873  ctx->feature = feature;
874 
875  gs = (GSERIALIZED *) PG_DETOAST_DATUM(datum);
876  lwgeom = lwgeom_from_gserialized(gs);
877 
878  POSTGIS_DEBUGF(3, "mvt_agg_transfn encoded feature count: %zd", layer->n_features);
879  layer->features[layer->n_features++] = feature;
880 
881  encode_geometry(ctx, lwgeom);
882  lwgeom_free(lwgeom);
883  // TODO: free detoasted datum?
884  parse_values(ctx);
885 }
886 
887 static VectorTile__Tile * mvt_ctx_to_tile(mvt_agg_context *ctx)
888 {
889  int n_layers = 1;
890  VectorTile__Tile *tile;
891  encode_keys(ctx);
892  encode_values(ctx);
893 
894  tile = palloc(sizeof(VectorTile__Tile));
895  vector_tile__tile__init(tile);
896  tile->layers = palloc(sizeof(VectorTile__Tile__Layer*) * n_layers);
897  tile->layers[0] = ctx->layer;
898  tile->n_layers = n_layers;
899  return tile;
900 }
901 
903 {
904  /* Fill out the file slot, if it's not already filled. */
905  /* We should only have a filled slow when all the work of building */
906  /* out the data is complete, so after a serialize/deserialize cycle */
907  /* or after a context combine */
908  size_t len;
909  bytea *ba;
910 
911  if (!ctx->tile)
912  {
913  ctx->tile = mvt_ctx_to_tile(ctx);
914  }
915 
916  /* Zero features => empty bytea output */
917  if (ctx && ctx->layer && ctx->layer->n_features == 0)
918  {
919  bytea *ba = palloc(VARHDRSZ);
920  SET_VARSIZE(ba, VARHDRSZ);
921  return ba;
922  }
923 
924  /* Serialize the Tile */
925  len = VARHDRSZ + vector_tile__tile__get_packed_size(ctx->tile);
926  ba = palloc(len);
927  vector_tile__tile__pack(ctx->tile, (uint8_t*)VARDATA(ba));
928  SET_VARSIZE(ba, len);
929  return ba;
930 }
931 
932 
934 {
935  return mvt_ctx_to_bytea(ctx);
936 }
937 
938 static void * mvt_allocator(__attribute__((__unused__)) void *data, size_t size)
939 {
940  return palloc(size);
941 }
942 
943 static void mvt_deallocator(__attribute__((__unused__)) void *data, void *ptr)
944 {
945  return pfree(ptr);
946 }
947 
949 {
950  ProtobufCAllocator allocator = {
953  NULL
954  };
955 
956  size_t len = VARSIZE(ba) - VARHDRSZ;
957  VectorTile__Tile *tile = vector_tile__tile__unpack(&allocator, len, (uint8_t*)VARDATA(ba));
958  mvt_agg_context *ctx = palloc(sizeof(mvt_agg_context));
959  memset(ctx, 0, sizeof(mvt_agg_context));
960  ctx->tile = tile;
961  return ctx;
962 }
963 
964 static VectorTile__Tile__Value *
965 tile_value_copy(const VectorTile__Tile__Value *value)
966 {
967  VectorTile__Tile__Value *nvalue = palloc(sizeof(VectorTile__Tile__Value));
968  memcpy(nvalue, value, sizeof(VectorTile__Tile__Value));
969  if (value->string_value)
970  nvalue->string_value = pstrdup(value->string_value);
971  return nvalue;
972 }
973 
974 static VectorTile__Tile__Feature *
975 tile_feature_copy(const VectorTile__Tile__Feature *feature, int key_offset, int value_offset)
976 {
977  uint32_t i;
978  VectorTile__Tile__Feature *nfeature;
979 
980  /* Null in => Null out */
981  if (!feature) return NULL;
982 
983  /* Init object */
984  nfeature = palloc(sizeof(VectorTile__Tile__Feature));
985  vector_tile__tile__feature__init(nfeature);
986 
987  /* Copy settings straight over */
988  nfeature->has_id = feature->has_id;
989  nfeature->id = feature->id;
990  nfeature->has_type = feature->has_type;
991  nfeature->type = feature->type;
992 
993  /* Copy tags over, offsetting indexes so they match the dictionaries */
994  /* at the Tile_Layer level */
995  if (feature->n_tags > 0)
996  {
997  nfeature->n_tags = feature->n_tags;
998  nfeature->tags = palloc(sizeof(uint32_t)*feature->n_tags);
999  for (i = 0; i < feature->n_tags/2; i++)
1000  {
1001  nfeature->tags[2*i] = feature->tags[2*i] + key_offset;
1002  nfeature->tags[2*i+1] = feature->tags[2*i+1] + value_offset;
1003  }
1004  }
1005 
1006  /* Copy the raw geometry data over literally */
1007  if (feature->n_geometry > 0)
1008  {
1009  nfeature->n_geometry = feature->n_geometry;
1010  nfeature->geometry = palloc(sizeof(uint32_t)*feature->n_geometry);
1011  memcpy(nfeature->geometry, feature->geometry, sizeof(uint32_t)*feature->n_geometry);
1012  }
1013 
1014  /* Done */
1015  return nfeature;
1016 }
1017 
1018 static VectorTile__Tile__Layer *
1019 vectortile_layer_combine(const VectorTile__Tile__Layer *layer1, const VectorTile__Tile__Layer *layer2)
1020 {
1021  uint32_t i, j;
1022  int key2_offset, value2_offset;
1023  VectorTile__Tile__Layer *layer = palloc(sizeof(VectorTile__Tile__Layer));
1024  vector_tile__tile__layer__init(layer);
1025 
1026  /* Take globals from layer1 */
1027  layer->version = layer1->version;
1028  layer->name = pstrdup(layer1->name);
1029  layer->has_extent = layer1->has_extent;
1030  layer->extent = layer1->extent;
1031 
1032  /* Copy keys into new layer */
1033  j = 0;
1034  layer->n_keys = layer1->n_keys + layer2->n_keys;
1035  layer->keys = layer->n_keys ? palloc(layer->n_keys * sizeof(void*)) : NULL;
1036  for (i = 0; i < layer1->n_keys; i++)
1037  layer->keys[j++] = pstrdup(layer1->keys[i]);
1038  key2_offset = j;
1039  for (i = 0; i < layer2->n_keys; i++)
1040  layer->keys[j++] = pstrdup(layer2->keys[i]);
1041 
1042  /* Copy values into new layer */
1043  /* TODO, apply hash logic here too, so that merged tiles */
1044  /* retain unique value maps */
1045  layer->n_values = layer1->n_values + layer2->n_values;
1046  layer->values = layer->n_values ? palloc(layer->n_values * sizeof(void*)) : NULL;
1047  j = 0;
1048  for (i = 0; i < layer1->n_values; i++)
1049  layer->values[j++] = tile_value_copy(layer1->values[i]);
1050  value2_offset = j;
1051  for (i = 0; i < layer2->n_values; i++)
1052  layer->values[j++] = tile_value_copy(layer2->values[i]);
1053 
1054 
1055  layer->n_features = layer1->n_features + layer2->n_features;
1056  layer->features = layer->n_features ? palloc(layer->n_features * sizeof(void*)) : NULL;
1057  j = 0;
1058  for (i = 0; i < layer1->n_features; i++)
1059  layer->features[j++] = tile_feature_copy(layer1->features[i], 0, 0);
1060  value2_offset = j;
1061  for (i = 0; i < layer2->n_features; i++)
1062  layer->features[j++] = tile_feature_copy(layer2->features[i], key2_offset, value2_offset);
1063 
1064  return layer;
1065 }
1066 
1067 
1068 static VectorTile__Tile *
1069 vectortile_tile_combine(VectorTile__Tile *tile1, VectorTile__Tile *tile2)
1070 {
1071  uint32_t i, j;
1072  VectorTile__Tile *tile;
1073 
1074  /* Hopelessly messing up memory ownership here */
1075  if (tile1->n_layers == 0 && tile2->n_layers == 0)
1076  return tile1;
1077  else if (tile1->n_layers == 0)
1078  return tile2;
1079  else if (tile2->n_layers == 0)
1080  return tile1;
1081 
1082  tile = palloc(sizeof(VectorTile__Tile));
1083  vector_tile__tile__init(tile);
1084  tile->layers = palloc(sizeof(void*));
1085  tile->n_layers = 0;
1086 
1087  /* Merge all matching layers in the files (basically always only one) */
1088  for (i = 0; i < tile1->n_layers; i++)
1089  {
1090  for (j = 0; j < tile2->n_layers; j++)
1091  {
1092  VectorTile__Tile__Layer *l1 = tile1->layers[i];
1093  VectorTile__Tile__Layer *l2 = tile2->layers[j];
1094  if (strcmp(l1->name, l2->name)==0)
1095  {
1096  VectorTile__Tile__Layer *layer = vectortile_layer_combine(l1, l2);
1097  if (!layer)
1098  continue;
1099  tile->layers[tile->n_layers++] = layer;
1100  /* Add a spare slot at the end of the array */
1101  tile->layers = repalloc(tile->layers, (tile->n_layers+1) * sizeof(void*));
1102  }
1103  }
1104  }
1105  return tile;
1106 }
1107 
1109 {
1110  if (ctx1 || ctx2)
1111  {
1112  if (ctx1 && ! ctx2) return ctx1;
1113  if (ctx2 && ! ctx1) return ctx2;
1114  if (ctx1 && ctx2 && ctx1->tile && ctx2->tile)
1115  {
1116  mvt_agg_context *ctxnew = palloc(sizeof(mvt_agg_context));
1117  memset(ctxnew, 0, sizeof(mvt_agg_context));
1118  ctxnew->tile = vectortile_tile_combine(ctx1->tile, ctx2->tile);
1119  return ctxnew;
1120  }
1121  else
1122  {
1123  elog(DEBUG2, "ctx1->tile = %p", ctx1->tile);
1124  elog(DEBUG2, "ctx2->tile = %p", ctx2->tile);
1125  elog(ERROR, "%s: unable to combine contexts where tile attribute is null", __func__);
1126  return NULL;
1127  }
1128  }
1129  else
1130  {
1131  return NULL;
1132  }
1133 }
1134 
1142 {
1143  return mvt_ctx_to_bytea(ctx);
1144 }
1145 
1146 
1147 #endif
#define LINETYPE
Definition: liblwgeom.h:85
HeapTupleHeader row
Definition: mvt.h:54
Definition: mvt.c:49
#define HASH_ADD_KEYPTR(hh, head, keyptr, keylen_in, add)
Definition: uthash.h:329
uint32_t c
Definition: mvt.h:68
uint32_t id
Definition: mvt.c:92
static VectorTile__Tile__Value * tile_value_copy(const VectorTile__Tile__Value *value)
Definition: mvt.c:965
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:62
struct mvt_kv_string_value * string_values_hash
Definition: mvt.h:60
char * name
Definition: mvt.h:50
uint64_t uint_value
Definition: mvt.c:79
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:464
uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwgeom.c:923
uint32_t ngeoms
Definition: liblwgeom.h:496
#define POLYGONTYPE
Definition: liblwgeom.h:86
static uint32_t c_int(enum mvt_cmd_id id, uint32_t count)
Definition: mvt.c:96
char * string_value
Definition: mvt.c:61
#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:902
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:1019
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:56
static uint32_t get_key_index(mvt_agg_context *ctx, char *name)
Definition: mvt.c:284
uint32_t id
Definition: mvt.c:74
static VectorTile__Tile * mvt_ctx_to_tile(mvt_agg_context *ctx)
Definition: mvt.c:887
uint32_t id
Definition: mvt.c:62
static void parse_values(mvt_agg_context *ctx)
Definition: mvt.c:565
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:87
VectorTile__Tile * tile
Definition: mvt.h:57
static void encode_point(mvt_agg_context *ctx, LWPOINT *point)
Definition: mvt.c:161
char * geom_name
Definition: mvt.h:52
struct mvt_kv_float_value * float_values_hash
Definition: mvt.h:61
uint32_t ngeoms
Definition: liblwgeom.h:509
POINTARRAY * point
Definition: liblwgeom.h:413
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition: lwgeom.c:320
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:938
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:101
static uint32_t add_key(mvt_agg_context *ctx, char *name)
Definition: mvt.c:294
uint32_t id
Definition: mvt.c:68
mvt_agg_context * mvt_ctx_combine(mvt_agg_context *ctx1, mvt_agg_context *ctx2)
Definition: mvt.c:1108
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:56
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:153
LWPOLY * lwpoly_construct_envelope(int srid, double x1, double y1, double x2, double y2)
Definition: lwpoly.c:98
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:91
uint32_t keys_hash_i
Definition: mvt.h:67
Definition: mvt.c:51
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:58
static void encode_mline(mvt_agg_context *ctx, LWMLINE *lwmline)
Definition: mvt.c:197
void lwpoly_free(LWPOLY *poly)
Definition: lwpoly.c:175
#define HASH_FIND(hh, head, keyptr, keylen, out)
Definition: uthash.h:132
UT_hash_handle hh
Definition: mvt.c:93
float float_value
Definition: mvt.c:67
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
LWGEOM * lwgeom_intersection(const LWGEOM *geom1, const LWGEOM *geom2)
static void encode_mpoint(mvt_agg_context *ctx, LWMPOINT *mpoint)
Definition: mvt.c:171
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:692
static TupleDesc get_tuple_desc(mvt_agg_context *ctx)
Definition: mvt.c:276
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
POINTARRAY ** rings
Definition: liblwgeom.h:459
int count
Definition: genraster.py:56
#define UINT32_MAX
Definition: lwin_wkt_lex.c:343
Definition: mvt.c:54
struct mvt_kv_uint_value * uint_values_hash
Definition: mvt.h:63
uint32_t geom_index
Definition: mvt.h:53
static void lwgeom_to_basic_type(LWGEOM *geom)
In place process a collection to find a concrete geometry object and expose that as the actual object...
Definition: mvt.c:658
UT_hash_handle hh
Definition: mvt.c:75
double ymax
Definition: liblwgeom.h:297
#define MVT_PARSE_VALUE(value, kvtype, hash, valuefield, size)
Definition: mvt.c:417
double y
Definition: liblwgeom.h:330
uint32_t id
Definition: mvt.c:80
uint32_t id
Definition: mvt.c:86
static void encode_line(mvt_agg_context *ctx, LWLINE *lwline)
Definition: mvt.c:185
static uint32_t * parse_jsonb(mvt_agg_context *ctx, Jsonb *jb, uint32_t *tags)
Definition: mvt.c:499
static void parse_datum_as_string(mvt_agg_context *ctx, Oid typoid, Datum datum, uint32_t *tags, uint32_t k)
Definition: mvt.c:485
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:89
Definition: mvt.c:50
struct mvt_kv_bool_value * bool_values_hash
Definition: mvt.h:65
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:975
int64_t sint_value
Definition: mvt.c:85
uint32_t values_hash_i
Definition: mvt.h:66
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:51
UT_hash_handle hh
Definition: mvt.c:81
bytea * mvt_ctx_serialize(mvt_agg_context *ctx)
Definition: mvt.c:933
VectorTile__Tile__Feature * feature
Definition: mvt.h:55
struct mvt_kv_key * keys_hash
Definition: mvt.h:59
static void encode_poly(mvt_agg_context *ctx, LWPOLY *lwpoly)
Definition: mvt.c:215
struct mvt_kv_sint_value * sint_values_hash
Definition: mvt.h:64
#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:360
static void encode_values(mvt_agg_context *ctx)
Definition: mvt.c:381
void lwgeom_grid_in_place(LWGEOM *lwgeom, const gridspec *grid)
Definition: lwgeom.c:2145
#define MVT_PARSE_INT_VALUE(value)
Definition: mvt.c:437
void mvt_agg_init_context(mvt_agg_context *ctx)
Initialize aggregation context.
Definition: mvt.c:798
UT_hash_handle hh
Definition: mvt.c:57
static void encode_mpoly(mvt_agg_context *ctx, LWMPOLY *lwmpoly)
Definition: mvt.c:233
uint8_t type
Definition: liblwgeom.h:398
type
Definition: ovdump.py:41
UT_hash_handle hh
Definition: mvt.c:63
double yoff
Definition: liblwgeom.h:272
#define MVT_PARSE_DATUM(type, kvtype, hash, valuefield, datumfunc, size)
Definition: mvt.c:452
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:48
char * name
Definition: mvt.c:55
static void encode_keys(mvt_agg_context *ctx)
Definition: mvt.c:347
#define MULTILINETYPE
Definition: liblwgeom.h:88
double double_value
Definition: mvt.c:73
unsigned char uint8_t
Definition: uthash.h:79
static void parse_column_keys(mvt_agg_context *ctx)
Definition: mvt.c:305
int32_t srid
Definition: liblwgeom.h:469
UT_hash_handle hh
Definition: mvt.c:69
#define MVT_CREATE_VALUES(kvtype, hash, hasfield, valuefield)
Definition: mvt.c:367
mvt_agg_context * mvt_ctx_deserialize(const bytea *ba)
Definition: mvt.c:948
static void encode_geometry(mvt_agg_context *ctx, LWGEOM *lwgeom)
Definition: mvt.c:254
static VectorTile__Tile * vectortile_tile_combine(VectorTile__Tile *tile1, VectorTile__Tile *tile2)
Definition: mvt.c:1069
#define MVT_PARSE_INT_DATUM(type, datumfunc)
Definition: mvt.c:458
bytea * mvt_agg_finalfn(mvt_agg_context *ctx)
Finalize aggregation.
Definition: mvt.c:1141
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:838
Snap to grid.
uint32_t npoints
Definition: liblwgeom.h:373
static void mvt_deallocator(__attribute__((__unused__)) void *data, void *ptr)
Definition: mvt.c:943
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:106