PostGIS  3.4.0dev-r@@SVN_REVISION@@
lwgeom_out_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 "postgres.h"
26 #include "utils/builtins.h"
27 #include "executor/spi.h"
28 #include "../postgis_config.h"
29 #include "lwgeom_pg.h"
30 #include "lwgeom_log.h"
31 #include "liblwgeom.h"
32 #include "mvt.h"
33 
34 #ifdef HAVE_LIBPROTOBUF
35 #include "vector_tile.pb-c.h"
36 #endif /* HAVE_LIBPROTOBUF */
37 
42 Datum ST_AsMVTGeom(PG_FUNCTION_ARGS)
43 {
44 #ifndef HAVE_LIBPROTOBUF
45  elog(ERROR, "ST_AsMVTGeom: Compiled without protobuf-c support");
46  PG_RETURN_NULL();
47 #else
48  GBOX *bounds = NULL;
49  int32_t extent = 0;
50  int32_t buffer = 0;
51  bool clip_geom = true;
52  GSERIALIZED *geom_in, *geom_out;
53  LWGEOM *lwgeom_in, *lwgeom_out;
54  uint8_t type = 0;
55 
56  if (PG_ARGISNULL(0))
57  {
58  PG_RETURN_NULL();
59  }
60 
61  if (PG_ARGISNULL(1))
62  {
63  elog(ERROR, "%s: Geometric bounds cannot be null", __func__);
64  PG_RETURN_NULL();
65  }
66  bounds = (GBOX *)PG_GETARG_POINTER(1);
67  if (bounds->xmax - bounds->xmin <= 0 || bounds->ymax - bounds->ymin <= 0)
68  {
69  elog(ERROR, "%s: Geometric bounds are too small", __func__);
70  PG_RETURN_NULL();
71  }
72 
73  extent = PG_ARGISNULL(2) ? 4096 : PG_GETARG_INT32(2);
74  if (extent <= 0)
75  {
76  elog(ERROR, "%s: Extent must be greater than 0", __func__);
77  PG_RETURN_NULL();
78  }
79 
80  buffer = PG_ARGISNULL(3) ? 256 : PG_GETARG_INT32(3);
81  clip_geom = PG_ARGISNULL(4) ? true : PG_GETARG_BOOL(4);
82 
83  geom_in = PG_GETARG_GSERIALIZED_P_COPY(0);
84  type = gserialized_get_type(geom_in);
85 
86  /* If possible, peek into the bounding box before deserializing it to discard small geometries
87  * We don't check COLLECTIONTYPE since that might be a collection of points */
89  {
90  GBOX gserialized_box;
91  /* We only apply the optimization if the bounding box is available */
92  if (gserialized_fast_gbox_p(geom_in, &gserialized_box) == LW_SUCCESS)
93  {
94  /* Shortcut to drop geometries smaller than the resolution */
95  double geom_width = gserialized_box.xmax - gserialized_box.xmin;
96  double geom_height = gserialized_box.ymax - gserialized_box.ymin;
97 
98  /* We use half of the square height and width as limit: We use this
99  * and not area so it works properly with lines */
100  double bounds_width = ((bounds->xmax - bounds->xmin) / extent) / 2.0;
101  double bounds_height = ((bounds->ymax - bounds->ymin) / extent) / 2.0;
102  if (geom_width < bounds_width && geom_height < bounds_height)
103  {
104  PG_RETURN_NULL();
105  }
106  }
107  }
108 
109  lwgeom_in = lwgeom_from_gserialized(geom_in);
110 
111  lwgeom_out = mvt_geom(lwgeom_in, bounds, extent, buffer, clip_geom);
112  if (lwgeom_out == NULL)
113  PG_RETURN_NULL();
114 
115  geom_out = geometry_serialize(lwgeom_out);
116  lwgeom_free(lwgeom_out);
117  PG_FREE_IF_COPY(geom_in, 0);
118  PG_RETURN_POINTER(geom_out);
119 #endif
120 }
121 
126 Datum pgis_asmvt_transfn(PG_FUNCTION_ARGS)
127 {
128 #ifndef HAVE_LIBPROTOBUF
129  elog(ERROR, "ST_AsMVT: Compiled without protobuf-c support");
130  PG_RETURN_NULL();
131 #else
132  MemoryContext aggcontext, old_context;
133  mvt_agg_context *ctx;
134 
135  /* We need to initialize the internal cache to access it later via postgis_oid() */
136  postgis_initialize_cache();
137 
138  if (!AggCheckCallContext(fcinfo, &aggcontext))
139  elog(ERROR, "%s called in non-aggregate context", __func__);
140 
141  if (PG_ARGISNULL(0)) {
142  old_context = MemoryContextSwitchTo(aggcontext);
143  ctx = palloc(sizeof(*ctx));
144  ctx->name = "default";
145  if (PG_NARGS() > 2 && !PG_ARGISNULL(2))
146  ctx->name = text_to_cstring(PG_GETARG_TEXT_P(2));
147  ctx->extent = 4096;
148  if (PG_NARGS() > 3 && !PG_ARGISNULL(3))
149  ctx->extent = PG_GETARG_INT32(3);
150  ctx->geom_name = NULL;
151  if (PG_NARGS() > 4 && !PG_ARGISNULL(4))
152  ctx->geom_name = text_to_cstring(PG_GETARG_TEXT_P(4));
153  if (PG_NARGS() > 5 && !PG_ARGISNULL(5))
154  ctx->id_name = text_to_cstring(PG_GETARG_TEXT_P(5));
155  else
156  ctx->id_name = NULL;
157 
158  ctx->trans_context = AllocSetContextCreate(aggcontext, "MVT transfn", ALLOCSET_DEFAULT_SIZES);
159 
160  MemoryContextSwitchTo(ctx->trans_context);
162  MemoryContextSwitchTo(old_context);
163  } else {
164  ctx = (mvt_agg_context *) PG_GETARG_POINTER(0);
165  }
166 
167  if (!type_is_rowtype(get_fn_expr_argtype(fcinfo->flinfo, 1)))
168  elog(ERROR, "%s: parameter row cannot be other than a rowtype", __func__);
169  ctx->row = PG_GETARG_HEAPTUPLEHEADER(1);
170 
171  old_context = MemoryContextSwitchTo(ctx->trans_context);
172  mvt_agg_transfn(ctx);
173  MemoryContextSwitchTo(old_context);
174 
175  PG_FREE_IF_COPY(ctx->row, 1);
176  PG_RETURN_POINTER(ctx);
177 #endif
178 }
179 
184 Datum pgis_asmvt_finalfn(PG_FUNCTION_ARGS)
185 {
186 #ifndef HAVE_LIBPROTOBUF
187  elog(ERROR, "ST_AsMVT: Compiled without protobuf-c support");
188  PG_RETURN_NULL();
189 #else
190  mvt_agg_context *ctx;
191  bytea *buf;
192  elog(DEBUG2, "%s called", __func__);
193  if (!AggCheckCallContext(fcinfo, NULL))
194  elog(ERROR, "%s called in non-aggregate context", __func__);
195 
196  if (PG_ARGISNULL(0))
197  {
198  bytea *emptybuf = palloc(VARHDRSZ);
199  SET_VARSIZE(emptybuf, VARHDRSZ);
200  PG_RETURN_BYTEA_P(emptybuf);
201  }
202 
203  ctx = (mvt_agg_context *) PG_GETARG_POINTER(0);
204  buf = mvt_agg_finalfn(ctx);
205  PG_RETURN_BYTEA_P(buf);
206 #endif
207 }
208 
210 Datum pgis_asmvt_serialfn(PG_FUNCTION_ARGS)
211 {
212 #ifndef HAVE_LIBPROTOBUF
213  elog(ERROR, "ST_AsMVT: Compiled without protobuf-c support");
214  PG_RETURN_NULL();
215 #else
216  mvt_agg_context *ctx;
217  bytea *result;
218  elog(DEBUG2, "%s called", __func__);
219  if (!AggCheckCallContext(fcinfo, NULL))
220  elog(ERROR, "%s called in non-aggregate context", __func__);
221 
222  if (PG_ARGISNULL(0))
223  {
224  bytea *emptybuf = palloc(VARHDRSZ);
225  SET_VARSIZE(emptybuf, VARHDRSZ);
226  PG_RETURN_BYTEA_P(emptybuf);
227  }
228 
229  ctx = (mvt_agg_context *) PG_GETARG_POINTER(0);
230  result = mvt_ctx_serialize(ctx);
231  if (ctx->trans_context)
232  MemoryContextDelete(ctx->trans_context);
233  ctx->trans_context = NULL;
234  PG_RETURN_BYTEA_P(result);
235 #endif
236 }
237 
238 
240 Datum pgis_asmvt_deserialfn(PG_FUNCTION_ARGS)
241 {
242 #ifndef HAVE_LIBPROTOBUF
243  elog(ERROR, "ST_AsMVT: Compiled without protobuf-c support");
244  PG_RETURN_NULL();
245 #else
246  MemoryContext aggcontext, oldcontext;
247  mvt_agg_context *ctx;
248  elog(DEBUG2, "%s called", __func__);
249  if (!AggCheckCallContext(fcinfo, &aggcontext))
250  elog(ERROR, "%s called in non-aggregate context", __func__);
251 
252  oldcontext = MemoryContextSwitchTo(aggcontext);
253  ctx = mvt_ctx_deserialize(PG_GETARG_BYTEA_P(0));
254  MemoryContextSwitchTo(oldcontext);
255 
256  PG_RETURN_POINTER(ctx);
257 #endif
258 }
259 
261 Datum pgis_asmvt_combinefn(PG_FUNCTION_ARGS)
262 {
263 #ifndef HAVE_LIBPROTOBUF
264  elog(ERROR, "ST_AsMVT: Compiled without protobuf-c support");
265  PG_RETURN_NULL();
266 #else
267  MemoryContext aggcontext, oldcontext;
268  mvt_agg_context *ctx, *ctx1, *ctx2;
269  elog(DEBUG2, "%s called", __func__);
270  if (!AggCheckCallContext(fcinfo, &aggcontext))
271  elog(ERROR, "%s called in non-aggregate context", __func__);
272 
273  ctx1 = (mvt_agg_context*)PG_GETARG_POINTER(0);
274  ctx2 = (mvt_agg_context*)PG_GETARG_POINTER(1);
275  oldcontext = MemoryContextSwitchTo(aggcontext);
276  ctx = mvt_ctx_combine(ctx1, ctx2);
277  MemoryContextSwitchTo(oldcontext);
278  PG_RETURN_POINTER(ctx);
279 #endif
280 }
281 
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition: cu_print.c:262
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
Definition: gserialized.c:239
uint32_t gserialized_get_type(const GSERIALIZED *g)
Extract the geometry type from the serialized form (it hides in the anonymous data area,...
Definition: gserialized.c:89
int gserialized_fast_gbox_p(const GSERIALIZED *g, GBOX *gbox)
Read the box from the GSERIALIZED or return #LWFAILURE if box is unavailable.
Definition: gserialized.c:77
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1155
#define MULTILINETYPE
Definition: liblwgeom.h:106
#define LINETYPE
Definition: liblwgeom.h:103
#define LW_SUCCESS
Definition: liblwgeom.h:97
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:107
#define POLYGONTYPE
Definition: liblwgeom.h:104
This library is the generic geometry handling section of PostGIS.
Datum ST_AsMVTGeom(PG_FUNCTION_ARGS)
Datum pgis_asmvt_serialfn(PG_FUNCTION_ARGS)
PG_FUNCTION_INFO_V1(ST_AsMVTGeom)
Process input parameters to mvt_geom and returned serialized geometry.
Datum pgis_asmvt_finalfn(PG_FUNCTION_ARGS)
Datum pgis_asmvt_transfn(PG_FUNCTION_ARGS)
Datum pgis_asmvt_deserialfn(PG_FUNCTION_ARGS)
Datum pgis_asmvt_combinefn(PG_FUNCTION_ARGS)
mvt_agg_context * mvt_ctx_combine(mvt_agg_context *ctx1, mvt_agg_context *ctx2)
Definition: mvt.c:1264
mvt_agg_context * mvt_ctx_deserialize(const bytea *ba)
Definition: mvt.c:1141
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:932
bytea * mvt_agg_finalfn(mvt_agg_context *ctx)
Finalize aggregation.
Definition: mvt.c:1297
bytea * mvt_ctx_serialize(mvt_agg_context *ctx)
Definition: mvt.c:1126
void mvt_agg_init_context(mvt_agg_context *ctx)
Initialize aggregation context.
Definition: mvt.c:989
void mvt_agg_transfn(mvt_agg_context *ctx)
Aggregation step.
Definition: mvt.c:1031
type
Definition: ovdump.py:42
Datum buffer(PG_FUNCTION_ARGS)
double ymax
Definition: liblwgeom.h:357
double xmax
Definition: liblwgeom.h:355
double ymin
Definition: liblwgeom.h:356
double xmin
Definition: liblwgeom.h:354
char * id_name
Definition: mvt.h:67
char * geom_name
Definition: mvt.h:71
MemoryContext trans_context
Definition: mvt.h:62
uint32_t extent
Definition: mvt.h:64
HeapTupleHeader row
Definition: mvt.h:74
char * name
Definition: mvt.h:63