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