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