27 #ifdef HAVE_LIBPROTOBUF 29 #if POSTGIS_PGSQL_VERSION >= 94 30 #include "utils/jsonb.h" 33 #if POSTGIS_PGSQL_VERSION < 110 35 # define DatumGetJsonbP DatumGetJsonb 40 #define FEATURES_CAPACITY_INITIAL 50 98 return (
id & 0x7) | (count << 3);
103 return (value << 1) ^ (value >> 31);
108 int32_t *px, int32_t *py)
112 int32_t dx, dy,
x,
y;
115 for (i = 0; i < pa->
npoints; i++) {
117 if (i == 0 || (i == 1 && type >
MVT_POINT))
127 buffer[offset++] =
p_int(dx);
128 buffer[offset++] =
p_int(dy);
156 int32_t px = 0, py = 0;
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);
173 VectorTile__Tile__Feature *feature = ctx->
feature;
176 feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POINT;
177 feature->has_type = 1;
179 feature->geometry = palloc(
sizeof(*feature->geometry) * c);
181 lwline->
points, feature->geometry);
187 VectorTile__Tile__Feature *feature = ctx->
feature;
188 feature->type = VECTOR_TILE__TILE__GEOM_TYPE__LINESTRING;
189 feature->has_type = 1;
191 feature->geometry = palloc(
sizeof(*feature->geometry) * c);
193 lwline->
points, feature->geometry);
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++)
206 feature->geometry = palloc(
sizeof(*feature->geometry) * c);
207 for (i = 0; i < lwmline->
ngeoms; i++)
210 feature->geometry + offset, &px, &py);
211 feature->n_geometry = offset;
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++)
224 feature->geometry = palloc(
sizeof(*feature->geometry) * c);
225 for (i = 0; i < lwpoly->
nrings; i++)
228 feature->geometry + offset, &px, &py);
229 feature->n_geometry = offset;
235 int32_t px = 0, py = 0;
236 size_t c = 0, offset = 0;
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++)
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++)
248 poly->
rings[j], feature->geometry + offset,
250 feature->n_geometry = offset;
270 default: elog(ERROR,
"encode_geometry: '%s' geometry type not supported",
277 Oid tupType = HeapTupleHeaderGetTypeId(ctx->
row);
278 int32 tupTypmod = HeapTupleHeaderGetTypMod(ctx->
row);
279 TupleDesc tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
286 size_t size = strlen(name);
296 size_t size = strlen(name);
297 kv = palloc(
sizeof(*kv));
307 int natts = tupdesc->natts;
309 bool geom_found =
false;
311 POSTGIS_DEBUG(2,
"parse_column_keys called");
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;
318 Oid typoid = getBaseType(tupdesc->attrs[i].atttypid);
319 char *tkey = tupdesc->attrs[i].attname.data;
321 #if POSTGIS_PGSQL_VERSION >= 94 322 if (typoid == JSONBOID)
327 if (!geom_found && typoid == postgis_oid(GEOMETRYOID)) {
333 if (!geom_found && strcmp(key, ctx->
geom_name) == 0) {
342 elog(ERROR,
"parse_column_keys: no geometry column found");
343 ReleaseTupleDesc(tupdesc);
350 char **keys = palloc(n_keys *
sizeof(*keys));
353 ctx->
layer->n_keys = n_keys;
354 ctx->
layer->keys = keys;
361 VectorTile__Tile__Value *
value = palloc(
sizeof(*value));
362 vector_tile__tile__value__init(value);
366 #define MVT_CREATE_VALUES(kvtype, hash, hasfield, valuefield) \ 368 POSTGIS_DEBUG(2, "MVT_CREATE_VALUES called"); \ 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; \ 380 POSTGIS_DEBUG(2,
"encode_values called");
381 VectorTile__Tile__Value **values;
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);
400 POSTGIS_DEBUGF(3,
"encode_values n_values: %d", ctx->
values_hash_i);
402 ctx->
layer->values = values;
412 #define MVT_PARSE_VALUE(value, kvtype, hash, valuefield, size) \ 414 POSTGIS_DEBUG(2, "MVT_PARSE_VALUE called"); \ 416 HASH_FIND(hh, ctx->hash, &value, size, 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); \ 426 tags[ctx->c*2] = k; \ 427 tags[ctx->c*2+1] = kv->id; \ 430 #define MVT_PARSE_INT_VALUE(value) \ 433 uint64_t cvalue = value; \ 434 MVT_PARSE_VALUE(cvalue, mvt_kv_uint_value, \ 435 uint_values_hash, uint_value, \ 438 int64_t cvalue = value; \ 439 MVT_PARSE_VALUE(cvalue, mvt_kv_sint_value, \ 440 sint_values_hash, sint_value, \ 445 #define MVT_PARSE_DATUM(type, kvtype, hash, valuefield, datumfunc, size) \ 447 type value = datumfunc(datum); \ 448 MVT_PARSE_VALUE(value, kvtype, hash, valuefield, size); \ 451 #define MVT_PARSE_INT_DATUM(type, datumfunc) \ 453 type value = datumfunc(datum); \ 454 MVT_PARSE_INT_VALUE(value); \ 461 size_t size = strlen(value);
462 POSTGIS_DEBUG(2,
"add_value_as_string called");
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",
475 tags[ctx->
c*2+1] = kv->
id;
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);
491 #if POSTGIS_PGSQL_VERSION >= 94 497 bool skipNested =
false;
498 JsonbIteratorToken
r;
501 if (!JB_ROOT_IS_OBJECT(jb))
504 it = JsonbIteratorInit(&jb->root);
506 while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE) {
509 if (r == WJB_KEY && v.type != jbvNull) {
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';
518 tags = repalloc(tags, newSize * 2 *
sizeof(*tags));
522 r = JsonbIteratorNext(&it, &v, skipNested);
524 if (v.type == jbvString) {
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';
531 }
else if (v.type == jbvBool) {
533 bool_values_hash, bool_value,
sizeof(protobuf_c_boolean));
535 }
else if (v.type == jbvNumeric) {
537 str = DatumGetCString(DirectFunctionCall1(numeric_out,
538 PointerGetDatum(v.val.numeric)));
539 double d = strtod(str, NULL);
540 long l = strtol(str, NULL, 10);
543 double_value,
sizeof(
double));
558 POSTGIS_DEBUG(2,
"parse_values called");
560 uint32_t *tags = palloc(n_keys * 2 *
sizeof(*tags));
564 int natts = tupdesc->natts;
567 POSTGIS_DEBUGF(3,
"parse_values natts: %d", natts);
569 for (i = 0; i < natts; i++) {
577 #if POSTGIS_PGSQL_VERSION < 110 578 key = tupdesc->attrs[i]->attname.data;
579 typoid = getBaseType(tupdesc->attrs[i]->atttypid);
581 key = tupdesc->attrs[i].attname.data;
582 typoid = getBaseType(tupdesc->attrs[i].atttypid);
584 datum = GetAttributeByNum(ctx->
row, i+1, &isnull);
587 POSTGIS_DEBUG(3,
"parse_values isnull detected");
590 #if POSTGIS_PGSQL_VERSION >= 94 591 if (k == -1 && typoid != JSONBOID)
592 elog(ERROR,
"parse_values: unexpectedly could not find parsed key name '%s'", key);
593 if (typoid == JSONBOID) {
599 elog(ERROR,
"parse_values: unexpectedly could not find parsed key name '%s'", key);
605 bool_values_hash, bool_value,
606 DatumGetBool,
sizeof(protobuf_c_boolean));
619 float_values_hash, float_value,
620 DatumGetFloat4,
sizeof(
float));
624 double_values_hash, double_value,
625 DatumGetFloat8,
sizeof(
double));
634 ReleaseTupleDesc(tupdesc);
639 POSTGIS_DEBUGF(3,
"parse_values n_tags %zd", ctx->
feature->n_tags);
656 return geom->
type - 3;
662 for (i = 0; i < g->
ngeoms; i++)
670 elog(ERROR,
"%s: Invalid type (%d)", __func__, geom->
type);
696 geom_out = g->
geoms[0];
715 LWGEOM *lwgeom_out = NULL;
716 double width = gbox->
xmax - gbox->
xmin;
717 double height = gbox->
ymax - gbox->
ymin;
718 double fx = extent / width;
719 double fy = -(extent / height);
721 POSTGIS_DEBUG(2,
"mvt_geom called");
727 if (width == 0 || height == 0)
728 elog(ERROR,
"mvt_geom: bounds width or height cannot be 0");
731 elog(ERROR,
"mvt_geom: extent cannot be 0");
737 memset(&affine, 0,
sizeof(affine));
758 GBOX bgbox, lwgeom_gbox;
762 bgbox.
xmin = bgbox.
ymin = -(double)buffer;
769 POSTGIS_DEBUG(3,
"mvt_geom: geometry outside clip box");
776 #if POSTGIS_GEOS_VERSION < 35 785 POSTGIS_DEBUG(3,
"mvt_geom: no geometry after clip");
799 POSTGIS_DEBUG(3,
"mvt_geom: Invalid geometry after clipping");
804 lwgeom_out = clipped_geom;
827 POSTGIS_DEBUG(3,
"mvt_geom: Dropping geometry after type change");
850 VectorTile__Tile__Layer *layer;
852 POSTGIS_DEBUG(2,
"mvt_agg_init_context called");
855 elog(ERROR,
"mvt_agg_init_context: extent cannot be 0");
869 layer = palloc(
sizeof(*layer));
870 vector_tile__tile__layer__init(layer);
872 layer->name = ctx->
name;
873 layer->has_extent = 1;
874 layer->extent = ctx->
extent;
876 sizeof(*layer->features));
894 VectorTile__Tile__Feature *feature;
895 VectorTile__Tile__Layer *layer = ctx->
layer;
896 POSTGIS_DEBUG(2,
"mvt_agg_transfn called");
900 layer->features = repalloc(layer->features, new_capacity *
901 sizeof(*layer->features));
903 POSTGIS_DEBUGF(3,
"mvt_agg_transfn new_capacity: %zd", new_capacity);
909 datum = GetAttributeByNum(ctx->
row, ctx->
geom_index + 1, &isnull);
910 POSTGIS_DEBUGF(3,
"mvt_agg_transfn ctx->geom_index: %d", ctx->
geom_index);
911 POSTGIS_DEBUGF(3,
"mvt_agg_transfn isnull: %u", isnull);
912 POSTGIS_DEBUGF(3,
"mvt_agg_transfn datum: %lu", datum);
915 POSTGIS_DEBUG(3,
"mvt_agg_transfn got null geom");
919 feature = palloc(
sizeof(*feature));
920 vector_tile__tile__feature__init(feature);
927 POSTGIS_DEBUGF(3,
"mvt_agg_transfn encoded feature count: %zd", layer->n_features);
928 layer->features[layer->n_features++] = feature;
944 VectorTile__Tile__Layer *layers[1];
945 VectorTile__Tile tile = VECTOR_TILE__TILE__INIT;
949 POSTGIS_DEBUG(2,
"mvt_agg_finalfn called");
950 POSTGIS_DEBUGF(2,
"mvt_agg_finalfn n_features == %zd", ctx->
layer->n_features);
953 if (ctx->
layer->n_features == 0)
955 buf = palloc(VARHDRSZ);
956 SET_VARSIZE(buf, VARHDRSZ);
963 layers[0] = ctx->
layer;
966 tile.layers = layers;
968 len = vector_tile__tile__get_packed_size(&tile);
969 buf = palloc(
sizeof(*buf) * (len + VARHDRSZ));
970 vector_tile__tile__pack(&tile, buf + VARHDRSZ);
972 SET_VARSIZE(buf, VARHDRSZ + len);
static void parse_values(struct mvt_agg_context *ctx)
#define HASH_ADD_KEYPTR(hh, head, keyptr, keylen_in, add)
static uint32_t add_key(struct mvt_agg_context *ctx, char *name)
struct mvt_kv_double_value * double_values_hash
struct mvt_kv_string_value * string_values_hash
LWGEOM * lwgeom_grid(const LWGEOM *lwgeom, const gridspec *grid)
uint8_t * mvt_agg_finalfn(struct mvt_agg_context *ctx)
Finalize aggregation.
int lwgeom_is_collection(const LWGEOM *lwgeom)
Determine whether a LWGEOM can contain sub-geometries or not.
LWCOLLECTION * lwcollection_extract(LWCOLLECTION *col, int type)
Takes a potentially heterogeneous collection and returns a homogeneous collection consisting only of ...
static void encode_keys(struct mvt_agg_context *ctx)
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
static TupleDesc get_tuple_desc(struct mvt_agg_context *ctx)
static uint32_t c_int(enum mvt_cmd_id id, uint32_t count)
void lwgeom_free(LWGEOM *geom)
LWGEOM * lwgeom_clip_by_rect(const LWGEOM *geom1, double x0, double y0, double x1, double y1)
static void encode_point(struct mvt_agg_context *ctx, LWPOINT *point)
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.
static void parse_column_keys(struct mvt_agg_context *ctx)
LWGEOM * lwgeom_clone_deep(const LWGEOM *lwgeom)
Deep clone an LWGEOM, everything is copied.
#define FLAGS_SET_GEODETIC(flags, value)
struct mvt_kv_float_value * float_values_hash
static LWGEOM * lwgeom_to_basic_type(LWGEOM *geom, uint8 original_type)
In place process a collection to find a concrete geometry object and expose that as the actual object...
static void encode_values(struct mvt_agg_context *ctx)
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
static uint32_t * parse_jsonb(struct mvt_agg_context *ctx, Jsonb *jb, uint32_t *tags)
static void add_value_as_string(struct mvt_agg_context *ctx, char *value, uint32_t *tags, uint32_t k)
static uint32_t p_int(int32_t value)
static void parse_datum_as_string(struct mvt_agg_context *ctx, Oid typoid, Datum datum, uint32_t *tags, uint32_t k)
VectorTile__Tile__Layer * layer
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...
LWPOLY * lwpoly_construct_envelope(int srid, double x1, double y1, double x2, double y2)
void lwgeom_force_clockwise(LWGEOM *lwgeom)
Ensure the outer ring is clockwise oriented and all inner rings are counter-clockwise.
LWGEOM * mvt_geom(const LWGEOM *lwgeom, const GBOX *gbox, uint32_t extent, uint32_t buffer, bool clip_geom)
Transform a geometry into vector tile coordinate space.
Datum buffer(PG_FUNCTION_ARGS)
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
protobuf_c_boolean bool_value
void gbox_init(GBOX *gbox)
Zero out all the entries in the GBOX.
LWGEOM * lwgeom_make_valid(LWGEOM *geom)
Attempts to make an invalid geometries valid w/out losing points.
const POINT2D * getPoint2d_cp(const POINTARRAY *pa, int n)
Returns a POINT2D pointer into the POINTARRAY serialized_ptlist, suitable for reading from...
static void encode_mpoint(struct mvt_agg_context *ctx, LWMPOINT *mpoint)
void lwpoly_free(LWPOLY *poly)
#define HASH_FIND(hh, head, keyptr, keylen, out)
LWLINE * lwline_from_lwmpoint(int srid, const LWMPOINT *mpoint)
static void encode_mline(struct mvt_agg_context *ctx, LWMLINE *lwmline)
LWGEOM * lwgeom_intersection(const LWGEOM *geom1, const LWGEOM *geom2)
static void encode_geometry(struct mvt_agg_context *ctx, LWGEOM *lwgeom)
static uint32_t get_key_index(struct mvt_agg_context *ctx, char *name)
const GBOX * lwgeom_get_bbox(const LWGEOM *lwgeom)
Get a non-empty geometry bounding box, computing and caching it if not already there.
struct mvt_kv_uint_value * uint_values_hash
void mvt_agg_init_context(struct mvt_agg_context *ctx)
Initialize aggregation context.
#define MVT_PARSE_VALUE(value, kvtype, hash, valuefield, size)
static void encode_poly(struct mvt_agg_context *ctx, LWPOLY *lwpoly)
static void encode_mpoly(struct mvt_agg_context *ctx, LWMPOLY *lwmpoly)
struct mvt_kv_bool_value * bool_values_hash
static uint32_t encode_ptarray(struct mvt_agg_context *ctx, enum mvt_type type, POINTARRAY *pa, uint32_t *buffer, int32_t *px, int32_t *py)
void lwgeom_reverse(LWGEOM *lwgeom)
Reverse vertex order of LWGEOM.
int gbox_overlaps_2d(const GBOX *g1, const GBOX *g2)
Return LW_TRUE if the GBOX overlaps on the 2d plane, LW_FALSE otherwise.
#define FEATURES_CAPACITY_INITIAL
#define HASH_CLEAR(hh, head)
static void encode_line(struct mvt_agg_context *ctx, LWLINE *lwline)
static uint32_t encode_ptarray_initial(struct mvt_agg_context *ctx, enum mvt_type type, POINTARRAY *pa, uint32_t *buffer)
void mvt_agg_transfn(struct mvt_agg_context *ctx)
Aggregation step.
VectorTile__Tile__Feature * feature
struct mvt_kv_key * keys_hash
static uint8 lwgeom_get_basic_type(LWGEOM *geom)
struct mvt_kv_sint_value * sint_values_hash
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
static VectorTile__Tile__Value * create_value()
#define MVT_PARSE_INT_VALUE(value)
#define MVT_PARSE_DATUM(type, kvtype, hash, valuefield, datumfunc, size)
int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members) ...
#define MVT_CREATE_VALUES(kvtype, hash, hasfield, valuefield)
#define MVT_PARSE_INT_DATUM(type, datumfunc)
void lwgeom_affine(LWGEOM *geom, const AFFINE *affine)