PostGIS  2.5.7dev-r@@SVN_REVISION@@
lwgeom_inout.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^
22  *
23  **********************************************************************/
24 
25 #include "postgres.h"
26 
27 #include "../postgis_config.h"
28 
29 #include <math.h>
30 #include <float.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <errno.h>
34 #include <assert.h>
35 
36 #include "access/gist.h"
37 #include "access/itup.h"
38 
39 #include "fmgr.h"
40 #include "utils/elog.h"
41 #include "mb/pg_wchar.h"
42 #include "lib/stringinfo.h" /* for binary input */
43 #include "utils/array.h"
44 #include "utils/builtins.h"
45 #include "utils/lsyscache.h"
46 #include "funcapi.h"
47 
48 #include "liblwgeom.h"
49 #include "lwgeom_pg.h"
50 #include "geography.h" /* for lwgeom_valid_typmod */
51 #include "lwgeom_transform.h"
52 
53 
54 #include "access/htup_details.h"
55 
56 
57 void elog_ERROR(const char* string);
58 
59 Datum LWGEOM_in(PG_FUNCTION_ARGS);
60 Datum LWGEOM_out(PG_FUNCTION_ARGS);
61 Datum LWGEOM_to_text(PG_FUNCTION_ARGS);
62 Datum LWGEOM_to_bytea(PG_FUNCTION_ARGS);
63 Datum LWGEOM_from_bytea(PG_FUNCTION_ARGS);
64 Datum LWGEOM_asHEXEWKB(PG_FUNCTION_ARGS);
65 Datum parse_WKT_lwgeom(PG_FUNCTION_ARGS);
66 Datum LWGEOM_recv(PG_FUNCTION_ARGS);
67 Datum LWGEOM_send(PG_FUNCTION_ARGS);
68 Datum LWGEOM_to_latlon(PG_FUNCTION_ARGS);
69 Datum WKBFromLWGEOM(PG_FUNCTION_ARGS);
70 Datum TWKBFromLWGEOM(PG_FUNCTION_ARGS);
71 Datum TWKBFromLWGEOMArray(PG_FUNCTION_ARGS);
72 Datum LWGEOMFromTWKB(PG_FUNCTION_ARGS);
73 
74 
75 /*
76  * LWGEOM_in(cstring)
77  * format is '[SRID=#;]wkt|wkb'
78  * LWGEOM_in( 'SRID=99;POINT(0 0)')
79  * LWGEOM_in( 'POINT(0 0)') --> assumes SRID=SRID_UNKNOWN
80  * LWGEOM_in( 'SRID=99;0101000000000000000000F03F000000000000004')
81  * LWGEOM_in( '0101000000000000000000F03F000000000000004')
82  * returns a GSERIALIZED object
83  */
85 Datum LWGEOM_in(PG_FUNCTION_ARGS)
86 {
87  char *input = PG_GETARG_CSTRING(0);
88  int32 geom_typmod = -1;
89  char *str = input;
90  LWGEOM_PARSER_RESULT lwg_parser_result;
91  LWGEOM *lwgeom;
92  GSERIALIZED *ret;
93  int srid = 0;
94 
95  if ( (PG_NARGS()>2) && (!PG_ARGISNULL(2)) ) {
96  geom_typmod = PG_GETARG_INT32(2);
97  }
98 
99  lwgeom_parser_result_init(&lwg_parser_result);
100 
101  /* Empty string. */
102  if ( str[0] == '\0' ) {
103  ereport(ERROR,(errmsg("parse error - invalid geometry")));
104  PG_RETURN_NULL();
105  }
106 
107  /* Starts with "SRID=" */
108  if( strncasecmp(str,"SRID=",5) == 0 )
109  {
110  /* Roll forward to semi-colon */
111  char *tmp = str;
112  while ( tmp && *tmp != ';' )
113  tmp++;
114 
115  /* Check next character to see if we have WKB */
116  if ( tmp && *(tmp+1) == '0' )
117  {
118  /* Null terminate the SRID= string */
119  *tmp = '\0';
120  /* Set str to the start of the real WKB */
121  str = tmp + 1;
122  /* Move tmp to the start of the numeric part */
123  tmp = input + 5;
124  /* Parse out the SRID number */
125  srid = atoi(tmp);
126  }
127  }
128 
129  /* WKB? Let's find out. */
130  if ( str[0] == '0' )
131  {
132  size_t hexsize = strlen(str);
133  unsigned char *wkb = bytes_from_hexbytes(str, hexsize);
134  /* TODO: 20101206: No parser checks! This is inline with current 1.5 behavior, but needs discussion */
135  lwgeom = lwgeom_from_wkb(wkb, hexsize/2, LW_PARSER_CHECK_NONE);
136  /* If we picked up an SRID at the head of the WKB set it manually */
137  if ( srid ) lwgeom_set_srid(lwgeom, srid);
138  /* Add a bbox if necessary */
139  if ( lwgeom_needs_bbox(lwgeom) ) lwgeom_add_bbox(lwgeom);
140  pfree(wkb);
141  ret = geometry_serialize(lwgeom);
142  lwgeom_free(lwgeom);
143  }
144  /* WKT then. */
145  else
146  {
147  if ( lwgeom_parse_wkt(&lwg_parser_result, str, LW_PARSER_CHECK_ALL) == LW_FAILURE )
148  {
149  PG_PARSER_ERROR(lwg_parser_result);
150  PG_RETURN_NULL();
151  }
152  lwgeom = lwg_parser_result.geom;
153  if ( lwgeom_needs_bbox(lwgeom) )
154  lwgeom_add_bbox(lwgeom);
155  ret = geometry_serialize(lwgeom);
156  lwgeom_parser_result_free(&lwg_parser_result);
157  }
158 
159  if ( geom_typmod >= 0 )
160  {
161  ret = postgis_valid_typmod(ret, geom_typmod);
162  POSTGIS_DEBUG(3, "typmod and geometry were consistent");
163  }
164  else
165  {
166  POSTGIS_DEBUG(3, "typmod was -1");
167  }
168 
169  /* Don't free the parser result (and hence lwgeom) until we have done */
170  /* the typemod check with lwgeom */
171 
172  PG_RETURN_POINTER(ret);
173 
174 }
175 
176 /*
177  * LWGEOM_to_latlon(GEOMETRY, text)
178  * NOTE: Geometry must be a point. It is assumed that the coordinates
179  * of the point are in a lat/lon projection, and they will be
180  * normalized in the output to -90-90 and -180-180.
181  *
182  * The text parameter is a format string containing the format for the
183  * resulting text, similar to a date format string. Valid tokens
184  * are "D" for degrees, "M" for minutes, "S" for seconds, and "C" for
185  * cardinal direction (NSEW). DMS tokens may be repeated to indicate
186  * desired width and precision ("SSS.SSSS" means " 1.0023").
187  * "M", "S", and "C" are optional. If "C" is omitted, degrees are
188  * shown with a "-" sign if south or west. If "S" is omitted,
189  * minutes will be shown as decimal with as many digits of precision
190  * as you specify. If "M" is omitted, degrees are shown as decimal
191  * with as many digits precision as you specify.
192  *
193  * If the format string is omitted (null or 0-length) a default
194  * format will be used.
195  *
196  * returns text
197  */
199 Datum LWGEOM_to_latlon(PG_FUNCTION_ARGS)
200 {
201  /* Get the parameters */
202  GSERIALIZED *pg_lwgeom = PG_GETARG_GSERIALIZED_P(0);
203  text *format_text = PG_GETARG_TEXT_P(1);
204 
205  LWGEOM *lwgeom;
206  char *format_str = NULL;
207 
208  char * formatted_str;
209  text * formatted_text;
210  char * tmp;
211 
212  /* Only supports points. */
213  uint8_t geom_type = gserialized_get_type(pg_lwgeom);
214  if (POINTTYPE != geom_type)
215  {
216  lwpgerror("Only points are supported, you tried type %s.", lwtype_name(geom_type));
217  }
218  /* Convert to LWGEOM type */
219  lwgeom = lwgeom_from_gserialized(pg_lwgeom);
220 
221  if (format_text == NULL) {
222  lwpgerror("ST_AsLatLonText: invalid format string (null");
223  PG_RETURN_NULL();
224  }
225 
226  format_str = text_to_cstring(format_text);
227  assert(format_str != NULL);
228 
229  /* The input string supposedly will be in the database encoding,
230  so convert to UTF-8. */
231  tmp = (char *)pg_do_encoding_conversion(
232  (uint8_t *)format_str, strlen(format_str), GetDatabaseEncoding(), PG_UTF8);
233  assert(tmp != NULL);
234  if ( tmp != format_str ) {
235  pfree(format_str);
236  format_str = tmp;
237  }
238 
239  /* Produce the formatted string. */
240  formatted_str = lwpoint_to_latlon((LWPOINT *)lwgeom, format_str);
241  assert(formatted_str != NULL);
242  pfree(format_str);
243 
244  /* Convert the formatted string from UTF-8 back to database encoding. */
245  tmp = (char *)pg_do_encoding_conversion(
246  (uint8_t *)formatted_str, strlen(formatted_str),
247  PG_UTF8, GetDatabaseEncoding());
248  assert(tmp != NULL);
249  if ( tmp != formatted_str) {
250  pfree(formatted_str);
251  formatted_str = tmp;
252  }
253 
254  /* Convert to the postgres output string type. */
255  formatted_text = cstring_to_text(formatted_str);
256  pfree(formatted_str);
257 
258  PG_RETURN_POINTER(formatted_text);
259 }
260 
261 /*
262  * LWGEOM_out(lwgeom) --> cstring
263  * output is 'SRID=#;<wkb in hex form>'
264  * ie. 'SRID=-99;0101000000000000000000F03F0000000000000040'
265  * WKB is machine endian
266  * if SRID=-1, the 'SRID=-1;' will probably not be present.
267  */
269 Datum LWGEOM_out(PG_FUNCTION_ARGS)
270 {
271  GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
272  LWGEOM *lwgeom;
273  char *hexwkb;
274  size_t hexwkb_size;
275 
276  lwgeom = lwgeom_from_gserialized(geom);
277  hexwkb = lwgeom_to_hexwkb(lwgeom, WKB_EXTENDED, &hexwkb_size);
278  lwgeom_free(lwgeom);
279 
280  PG_RETURN_CSTRING(hexwkb);
281 }
282 
283 /*
284  * AsHEXEWKB(geom, string)
285  */
287 Datum LWGEOM_asHEXEWKB(PG_FUNCTION_ARGS)
288 {
289  GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
290  LWGEOM *lwgeom;
291  char *hexwkb;
292  size_t hexwkb_size;
293  uint8_t variant = 0;
294  text *result;
295  text *type;
296  size_t text_size;
297 
298  /* If user specified endianness, respect it */
299  if ( (PG_NARGS()>1) && (!PG_ARGISNULL(1)) )
300  {
301  type = PG_GETARG_TEXT_P(1);
302 
303  if ( ! strncmp(VARDATA(type), "xdr", 3) ||
304  ! strncmp(VARDATA(type), "XDR", 3) )
305  {
306  variant = variant | WKB_XDR;
307  }
308  else
309  {
310  variant = variant | WKB_NDR;
311  }
312  }
313 
314  /* Create WKB hex string */
315  lwgeom = lwgeom_from_gserialized(geom);
316  hexwkb = lwgeom_to_hexwkb(lwgeom, variant | WKB_EXTENDED, &hexwkb_size);
317  lwgeom_free(lwgeom);
318 
319  /* Prepare the PgSQL text return type */
320  text_size = hexwkb_size - 1 + VARHDRSZ;
321  result = palloc(text_size);
322  memcpy(VARDATA(result), hexwkb, hexwkb_size - 1);
323  SET_VARSIZE(result, text_size);
324 
325  /* Clean up and return */
326  pfree(hexwkb);
327  PG_FREE_IF_COPY(geom, 0);
328  PG_RETURN_TEXT_P(result);
329 }
330 
331 
332 /*
333  * LWGEOM_to_text(lwgeom) --> text
334  * output is 'SRID=#;<wkb in hex form>'
335  * ie. 'SRID=-99;0101000000000000000000F03F0000000000000040'
336  * WKB is machine endian
337  * if SRID=-1, the 'SRID=-1;' will probably not be present.
338  */
340 Datum LWGEOM_to_text(PG_FUNCTION_ARGS)
341 {
342  GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
343  LWGEOM *lwgeom;
344  char *hexwkb;
345  size_t hexwkb_size;
346  text *result;
347 
348  /* Generate WKB hex text */
349  lwgeom = lwgeom_from_gserialized(geom);
350  hexwkb = lwgeom_to_hexwkb(lwgeom, WKB_EXTENDED, &hexwkb_size);
351  lwgeom_free(lwgeom);
352 
353  /* Copy into text obect */
354  result = cstring_to_text(hexwkb);
355  pfree(hexwkb);
356 
357  /* Clean up and return */
358  PG_FREE_IF_COPY(geom, 0);
359  PG_RETURN_TEXT_P(result);
360 }
361 
362 /*
363  * LWGEOMFromEWKB(wkb, [SRID] )
364  * NOTE: wkb is in *binary* not hex form.
365  *
366  * NOTE: this function parses EWKB (extended form)
367  * which also contains SRID info.
368  */
370 Datum LWGEOMFromEWKB(PG_FUNCTION_ARGS)
371 {
372  bytea *bytea_wkb = (bytea*)PG_GETARG_BYTEA_P(0);
373  int32 srid = 0;
374  GSERIALIZED *geom;
375  LWGEOM *lwgeom;
376  uint8_t *wkb = (uint8_t*)VARDATA(bytea_wkb);
377 
378  lwgeom = lwgeom_from_wkb(wkb, VARSIZE(bytea_wkb)-VARHDRSZ, LW_PARSER_CHECK_ALL);
379 
380  if ( ( PG_NARGS()>1) && ( ! PG_ARGISNULL(1) ))
381  {
382  srid = PG_GETARG_INT32(1);
383  lwgeom_set_srid(lwgeom, srid);
384  }
385 
386  if ( lwgeom_needs_bbox(lwgeom) )
387  lwgeom_add_bbox(lwgeom);
388 
389  geom = geometry_serialize(lwgeom);
390  lwgeom_free(lwgeom);
391  PG_FREE_IF_COPY(bytea_wkb, 0);
392  PG_RETURN_POINTER(geom);
393 }
394 /*
395  * LWGEOMFromTWKB(wkb)
396  * NOTE: twkb is in *binary* not hex form.
397  *
398  */
400 Datum LWGEOMFromTWKB(PG_FUNCTION_ARGS)
401 {
402  bytea *bytea_twkb = (bytea*)PG_GETARG_BYTEA_P(0);
403  GSERIALIZED *geom;
404  LWGEOM *lwgeom;
405  uint8_t *twkb = (uint8_t*)VARDATA(bytea_twkb);
406 
407  lwgeom = lwgeom_from_twkb(twkb, VARSIZE(bytea_twkb)-VARHDRSZ, LW_PARSER_CHECK_ALL);
408 
409  if ( lwgeom_needs_bbox(lwgeom) )
410  lwgeom_add_bbox(lwgeom);
411 
412  geom = geometry_serialize(lwgeom);
413  lwgeom_free(lwgeom);
414  PG_FREE_IF_COPY(bytea_twkb, 0);
415  PG_RETURN_POINTER(geom);
416 }
417 
418 /*
419  * WKBFromLWGEOM(lwgeom) --> wkb
420  * this will have no 'SRID=#;'
421  */
423 Datum WKBFromLWGEOM(PG_FUNCTION_ARGS)
424 {
425  GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
426  LWGEOM *lwgeom;
427  uint8_t *wkb;
428  size_t wkb_size;
429  uint8_t variant = 0;
430  bytea *result;
431  text *type;
432  /* If user specified endianness, respect it */
433  if ( (PG_NARGS()>1) && (!PG_ARGISNULL(1)) )
434  {
435  type = PG_GETARG_TEXT_P(1);
436 
437  if ( ! strncmp(VARDATA(type), "xdr", 3) ||
438  ! strncmp(VARDATA(type), "XDR", 3) )
439  {
440  variant = variant | WKB_XDR;
441  }
442  else
443  {
444  variant = variant | WKB_NDR;
445  }
446  }
447  wkb_size= VARSIZE(geom) - VARHDRSZ;
448  /* Create WKB hex string */
449  lwgeom = lwgeom_from_gserialized(geom);
450 
451  wkb = lwgeom_to_wkb(lwgeom, variant | WKB_EXTENDED , &wkb_size);
452  lwgeom_free(lwgeom);
453 
454  /* Prepare the PgSQL text return type */
455  result = palloc(wkb_size + VARHDRSZ);
456  memcpy(VARDATA(result), wkb, wkb_size);
457  SET_VARSIZE(result, wkb_size+VARHDRSZ);
458 
459  /* Clean up and return */
460  pfree(wkb);
461  PG_FREE_IF_COPY(geom, 0);
462  PG_RETURN_BYTEA_P(result);
463 }
464 
466 Datum TWKBFromLWGEOM(PG_FUNCTION_ARGS)
467 {
468  GSERIALIZED *geom;
469  LWGEOM *lwgeom;
470  uint8_t *twkb;
471  size_t twkb_size;
472  uint8_t variant = 0;
473  bytea *result;
474  srs_precision sp;
475 
476  /*check for null input since we cannot have the sql-function as strict.
477  That is because we use null as default for optional ID*/
478  if ( PG_ARGISNULL(0) ) PG_RETURN_NULL();
479 
480  geom = PG_GETARG_GSERIALIZED_P(0);
481 
482  /* Read sensible precision defaults (about one meter) given the srs */
483  sp = srid_axis_precision(fcinfo, gserialized_get_srid(geom), TWKB_DEFAULT_PRECISION);
484 
485  /* If user specified XY precision, use it */
486  if ( PG_NARGS() > 1 && ! PG_ARGISNULL(1) )
487  sp.precision_xy = PG_GETARG_INT32(1);
488 
489  /* If user specified Z precision, use it */
490  if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
491  sp.precision_z = PG_GETARG_INT32(2);
492 
493  /* If user specified M precision, use it */
494  if ( PG_NARGS() > 3 && ! PG_ARGISNULL(3) )
495  sp.precision_m = PG_GETARG_INT32(3);
496 
497  /* We don't permit ids for single geoemtries */
498  variant = variant & ~TWKB_ID;
499 
500  /* If user wants registered twkb sizes */
501  if ( PG_NARGS() > 4 && ! PG_ARGISNULL(4) && PG_GETARG_BOOL(4) )
502  variant |= TWKB_SIZE;
503 
504  /* If user wants bounding boxes */
505  if ( PG_NARGS() > 5 && ! PG_ARGISNULL(5) && PG_GETARG_BOOL(5) )
506  variant |= TWKB_BBOX;
507 
508  /* Create TWKB binary string */
509  lwgeom = lwgeom_from_gserialized(geom);
510  twkb = lwgeom_to_twkb(lwgeom, variant, sp.precision_xy, sp.precision_z, sp.precision_m, &twkb_size);
511 
512  /* Prepare the PgSQL text return type */
513  result = palloc(twkb_size + VARHDRSZ);
514  memcpy(VARDATA(result), twkb, twkb_size);
515  SET_VARSIZE(result, twkb_size + VARHDRSZ);
516 
517  PG_RETURN_BYTEA_P(result);
518 }
519 
520 
522 Datum TWKBFromLWGEOMArray(PG_FUNCTION_ARGS)
523 {
524  ArrayType *arr_geoms = NULL;
525  ArrayType *arr_ids = NULL;
526  int num_geoms, num_ids, i = 0;
527 
528  ArrayIterator iter_geoms, iter_ids;
529  bool null_geom, null_id;
530  Datum val_geom, val_id;
531 
532  int is_homogeneous = true;
533  uint32_t subtype = 0;
534  int has_z = 0;
535  int has_m = 0;
536  LWCOLLECTION *col = NULL;
537  int64_t *idlist = NULL;
538  uint8_t variant = 0;
539 
540  srs_precision sp;
541  uint8_t *twkb;
542  size_t twkb_size;
543  bytea *result;
544 
545  /* The first two arguments are required */
546  if ( PG_NARGS() < 2 || PG_ARGISNULL(0) || PG_ARGISNULL(1) )
547  PG_RETURN_NULL();
548 
549  arr_geoms = PG_GETARG_ARRAYTYPE_P(0);
550  arr_ids = PG_GETARG_ARRAYTYPE_P(1);
551 
552  num_geoms = ArrayGetNItems(ARR_NDIM(arr_geoms), ARR_DIMS(arr_geoms));
553  num_ids = ArrayGetNItems(ARR_NDIM(arr_ids), ARR_DIMS(arr_ids));
554 
555  if ( num_geoms != num_ids )
556  {
557  elog(ERROR, "size of geometry[] and integer[] arrays must match");
558  PG_RETURN_NULL();
559  }
560 
561  /* Loop through array and build a collection of geometry and */
562  /* a simple array of ids. If either side is NULL, skip it */
563 
564 #if POSTGIS_PGSQL_VERSION >= 95
565  iter_geoms = array_create_iterator(arr_geoms, 0, NULL);
566  iter_ids = array_create_iterator(arr_ids, 0, NULL);
567 #else
568  iter_geoms = array_create_iterator(arr_geoms, 0);
569  iter_ids = array_create_iterator(arr_ids, 0);
570 #endif
571 
572  while( array_iterate(iter_geoms, &val_geom, &null_geom) &&
573  array_iterate(iter_ids, &val_id, &null_id) )
574  {
575  LWGEOM *geom;
576  int32_t uid;
577 
578  if ( null_geom || null_id )
579  {
580  elog(NOTICE, "ST_AsTWKB skipping NULL entry at position %d", i);
581  continue;
582  }
583 
584  geom = lwgeom_from_gserialized((GSERIALIZED*)DatumGetPointer(val_geom));
585  uid = DatumGetInt64(val_id);
586 
587  /* Construct collection/idlist first time through */
588  if ( ! col )
589  {
590  has_z = lwgeom_has_z(geom);
591  has_m = lwgeom_has_m(geom);
592  col = lwcollection_construct_empty(COLLECTIONTYPE, lwgeom_get_srid(geom), has_z, has_m);
593  }
594  if ( ! idlist )
595  idlist = palloc0(num_geoms * sizeof(int64_t));
596 
597 
598  /* Check if there is differences in dimensionality*/
599  if( lwgeom_has_z(geom)!=has_z || lwgeom_has_m(geom)!=has_m)
600  {
601  elog(ERROR, "Geometries have different dimensionality");
602  PG_FREE_IF_COPY(arr_geoms, 0);
603  PG_FREE_IF_COPY(arr_ids, 1);
604  PG_RETURN_NULL();
605  }
606  /* Store the values */
607  lwcollection_add_lwgeom(col, geom);
608  idlist[i++] = uid;
609 
610  /* Grab the geometry type and note if all geometries share it */
611  /* If so, we can make this a homogeneous collection and save some space */
612  if ( lwgeom_get_type(geom) != subtype && subtype )
613  {
614  is_homogeneous = false;
615  }
616  else
617  {
618  subtype = lwgeom_get_type(geom);
619  }
620 
621  }
622  array_free_iterator(iter_geoms);
623  array_free_iterator(iter_ids);
624 
625  if(i==0)
626  {
627  elog(NOTICE, "No valid geometry - id pairs found");
628  PG_FREE_IF_COPY(arr_geoms, 0);
629  PG_FREE_IF_COPY(arr_ids, 1);
630  PG_RETURN_NULL();
631  }
632  if ( is_homogeneous )
633  {
634  col->type = lwtype_get_collectiontype(subtype);
635  }
636 
637  /* Read sensible precision defaults (about one meter) given the srs */
638  sp = srid_axis_precision(fcinfo, lwgeom_get_srid(lwcollection_as_lwgeom(col)), TWKB_DEFAULT_PRECISION);
639 
640  /* If user specified XY precision, use it */
641  if ( PG_NARGS() > 2 && ! PG_ARGISNULL(2) )
642  sp.precision_xy = PG_GETARG_INT32(2);
643 
644  /* If user specified Z precision, use it */
645  if ( PG_NARGS() > 3 && ! PG_ARGISNULL(3) )
646  sp.precision_z = PG_GETARG_INT32(3);
647 
648  /* If user specified M precision, use it */
649  if ( PG_NARGS() > 4 && ! PG_ARGISNULL(4) )
650  sp.precision_m = PG_GETARG_INT32(4);
651 
652  /* We are building an ID'ed output */
653  variant = TWKB_ID;
654 
655  /* If user wants registered twkb sizes */
656  if ( PG_NARGS() > 5 && ! PG_ARGISNULL(5) && PG_GETARG_BOOL(5) )
657  variant |= TWKB_SIZE;
658 
659  /* If user wants bounding boxes */
660  if ( PG_NARGS() > 6 && ! PG_ARGISNULL(6) && PG_GETARG_BOOL(6) )
661  variant |= TWKB_BBOX;
662 
663  /* Write out the TWKB */
665  idlist, variant,
666  sp.precision_xy, sp.precision_z, sp.precision_m,
667  &twkb_size);
668 
669  /* Convert to a bytea return type */
670  result = palloc(twkb_size + VARHDRSZ);
671  memcpy(VARDATA(result), twkb, twkb_size);
672  SET_VARSIZE(result, twkb_size + VARHDRSZ);
673 
674  /* Clean up */
675  pfree(twkb);
676  pfree(idlist);
677  lwcollection_free(col);
678  PG_FREE_IF_COPY(arr_geoms, 0);
679  PG_FREE_IF_COPY(arr_ids, 1);
680 
681  PG_RETURN_BYTEA_P(result);
682 }
683 
684 
685 /* puts a bbox inside the geometry */
687 Datum LWGEOM_addBBOX(PG_FUNCTION_ARGS)
688 {
689  GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
690  GSERIALIZED *result;
691  LWGEOM *lwgeom;
692 
693  lwgeom = lwgeom_from_gserialized(geom);
694  lwgeom_add_bbox(lwgeom);
695  result = geometry_serialize(lwgeom);
696 
697  PG_FREE_IF_COPY(geom, 0);
698  PG_RETURN_POINTER(result);
699 }
700 
701 /* removes a bbox from a geometry */
703 Datum LWGEOM_dropBBOX(PG_FUNCTION_ARGS)
704 {
705  GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
706 
707  /* No box? we're done already! */
708  if ( ! gserialized_has_bbox(geom) )
709  PG_RETURN_POINTER(geom);
710 
711  PG_RETURN_POINTER(gserialized_drop_gidx(geom));
712 }
713 
714 
715 /* for the wkt parser */
716 void elog_ERROR(const char* string)
717 {
718  elog(ERROR, "%s", string);
719 }
720 
721 /*
722 * This just does the same thing as the _in function,
723 * except it has to handle a 'text' input. First
724 * unwrap the text into a cstring, then call
725 * geometry_in
726 */
728 Datum parse_WKT_lwgeom(PG_FUNCTION_ARGS)
729 {
730  text *wkt_text = PG_GETARG_TEXT_P(0);
731  char *wkt;
732  Datum result;
733 
734  /* Unwrap the PgSQL text type into a cstring */
735  wkt = text_to_cstring(wkt_text);
736 
737  /* Now we call over to the geometry_in function */
738  result = DirectFunctionCall1(LWGEOM_in, CStringGetDatum(wkt));
739 
740  /* Return null on null */
741  if ( ! result )
742  PG_RETURN_NULL();
743 
744  PG_RETURN_DATUM(result);
745 }
746 
747 
748 /*
749  * This function must advance the StringInfo.cursor pointer
750  * and leave it at the end of StringInfo.buf. If it fails
751  * to do so the backend will raise an exception with message:
752  * ERROR: incorrect binary data format in bind parameter #
753  *
754  */
756 Datum LWGEOM_recv(PG_FUNCTION_ARGS)
757 {
758  StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
759  int32 geom_typmod = -1;
760  GSERIALIZED *geom;
761  LWGEOM *lwgeom;
762 
763  if ( (PG_NARGS()>2) && (!PG_ARGISNULL(2)) ) {
764  geom_typmod = PG_GETARG_INT32(2);
765  }
766 
767  lwgeom = lwgeom_from_wkb((uint8_t*)buf->data, buf->len, LW_PARSER_CHECK_ALL);
768 
769  if ( lwgeom_needs_bbox(lwgeom) )
770  lwgeom_add_bbox(lwgeom);
771 
772  /* Set cursor to the end of buffer (so the backend is happy) */
773  buf->cursor = buf->len;
774 
775  geom = geometry_serialize(lwgeom);
776  lwgeom_free(lwgeom);
777 
778  if ( geom_typmod >= 0 )
779  {
780  geom = postgis_valid_typmod(geom, geom_typmod);
781  POSTGIS_DEBUG(3, "typmod and geometry were consistent");
782  }
783  else
784  {
785  POSTGIS_DEBUG(3, "typmod was -1");
786  }
787 
788 
789  PG_RETURN_POINTER(geom);
790 }
791 
792 
793 
795 Datum LWGEOM_send(PG_FUNCTION_ARGS)
796 {
797  POSTGIS_DEBUG(2, "LWGEOM_send called");
798 
799  PG_RETURN_POINTER(
800  DatumGetPointer(
801  DirectFunctionCall1(
803  PG_GETARG_DATUM(0)
804  )));
805 }
806 
808 Datum LWGEOM_to_bytea(PG_FUNCTION_ARGS)
809 {
810  POSTGIS_DEBUG(2, "LWGEOM_to_bytea called");
811 
812  PG_RETURN_POINTER(
813  DatumGetPointer(
814  DirectFunctionCall1(
816  PG_GETARG_DATUM(0)
817  )));
818 }
819 
821 Datum LWGEOM_from_bytea(PG_FUNCTION_ARGS)
822 {
823  GSERIALIZED *result;
824 
825  POSTGIS_DEBUG(2, "LWGEOM_from_bytea start");
826 
827  result = (GSERIALIZED *)DatumGetPointer(DirectFunctionCall1(
828  LWGEOMFromEWKB, PG_GETARG_DATUM(0)));
829 
830  PG_RETURN_POINTER(result);
831 }
832 
static uint8_t variant
Definition: cu_in_twkb.c:26
int32_t gserialized_get_srid(const GSERIALIZED *s)
Extract the SRID from the serialized form (it is packed into three bytes so this is a handy function)...
Definition: g_serialized.c:100
LWGEOM * lwgeom_from_gserialized(const GSERIALIZED *g)
Allocate a new LWGEOM from a GSERIALIZED.
int gserialized_has_bbox(const GSERIALIZED *gser)
Check if a GSERIALIZED has a bounding box without deserializing first.
Definition: g_serialized.c:40
uint32_t gserialized_get_type(const GSERIALIZED *s)
Extract the geometry type from the serialized form (it hides in the anonymous data area,...
Definition: g_serialized.c:86
GSERIALIZED * postgis_valid_typmod(GSERIALIZED *gser, int32_t typmod)
Check the consistency of the metadata we want to enforce in the typmod: srid, type and dimensionality...
#define LW_PARSER_CHECK_ALL
Definition: liblwgeom.h:2006
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:300
uint32_t lwtype_get_collectiontype(uint8_t type)
Given an lwtype number, what homogeneous collection can hold it?
Definition: lwgeom.c:1120
#define COLLECTIONTYPE
Definition: liblwgeom.h:91
int32_t lwgeom_get_srid(const LWGEOM *geom)
Return SRID number.
Definition: lwgeom.c:916
uint8_t * lwgeom_to_twkb_with_idlist(const LWGEOM *geom, int64_t *idlist, uint8_t variant, int8_t precision_xy, int8_t precision_z, int8_t precision_m, size_t *twkb_size)
Convert LWGEOM to a char* in TWKB format.
Definition: lwout_twkb.c:571
LWCOLLECTION * lwcollection_construct_empty(uint8_t type, int srid, char hasz, char hasm)
Definition: lwcollection.c:94
#define LW_FAILURE
Definition: liblwgeom.h:79
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1144
#define LW_PARSER_CHECK_NONE
Definition: liblwgeom.h:2005
LWGEOM * lwgeom_from_twkb(const uint8_t *twkb, size_t twkb_size, char check)
WKB inputs must have a declared size, to prevent malformed WKB from reading off the end of the memory...
Definition: lwin_twkb.c:656
void lwgeom_parser_result_init(LWGEOM_PARSER_RESULT *parser_result)
Definition: lwin_wkt.c:879
int lwgeom_parse_wkt(LWGEOM_PARSER_RESULT *parser_result, char *wktstr, int parse_flags)
Parse a WKT geometry string into an LWGEOM structure.
uint32_t lwgeom_get_type(const LWGEOM *geom)
Return LWTYPE number.
Definition: lwgeom.c:923
uint8_t * lwgeom_to_wkb(const LWGEOM *geom, uint8_t variant, size_t *size_out)
Convert LWGEOM to a char* in WKB format.
Definition: lwout_wkb.c:764
uint8_t * bytes_from_hexbytes(const char *hexbuf, size_t hexsize)
Definition: lwin_wkb.c:91
int lwgeom_needs_bbox(const LWGEOM *geom)
Check whether or not a lwgeom is big enough to warrant a bounding box.
Definition: lwgeom.c:1197
int lwgeom_has_z(const LWGEOM *geom)
Return LW_TRUE if geometry has Z ordinates.
Definition: lwgeom.c:930
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:85
#define TWKB_ID
Definition: liblwgeom.h:2085
#define WKB_EXTENDED
Definition: liblwgeom.h:2068
char * lwgeom_to_hexwkb(const LWGEOM *geom, uint8_t variant, size_t *size_out)
Definition: lwout_wkb.c:848
char * lwpoint_to_latlon(const LWPOINT *p, const char *format)
Definition: lwprint.c:426
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:218
#define WKB_NDR
Definition: liblwgeom.h:2069
void lwcollection_free(LWCOLLECTION *col)
Definition: lwcollection.c:356
#define TWKB_DEFAULT_PRECISION
Definition: liblwgeom.h:2088
#define TWKB_SIZE
Definition: liblwgeom.h:2084
LWCOLLECTION * lwcollection_add_lwgeom(LWCOLLECTION *col, const LWGEOM *geom)
Appends geom to the collection managed by col.
Definition: lwcollection.c:187
LWGEOM * lwgeom_from_wkb(const uint8_t *wkb, const size_t wkb_size, const char check)
WKB inputs must have a declared size, to prevent malformed WKB from reading off the end of the memory...
Definition: lwin_wkb.c:789
int lwgeom_has_m(const LWGEOM *geom)
Return LW_TRUE if geometry has M ordinates.
Definition: lwgeom.c:937
void lwgeom_set_srid(LWGEOM *geom, int srid)
Set the SRID on an LWGEOM For collections, only the parent gets an SRID, all the children get SRID_UN...
void lwgeom_add_bbox(LWGEOM *lwgeom)
Compute a bbox if not already computed.
Definition: lwgeom.c:686
void lwgeom_parser_result_free(LWGEOM_PARSER_RESULT *parser_result)
Definition: lwin_wkt.c:885
#define TWKB_BBOX
Definition: liblwgeom.h:2083
#define WKB_XDR
Definition: liblwgeom.h:2070
uint8_t * lwgeom_to_twkb(const LWGEOM *geom, uint8_t variant, int8_t precision_xy, int8_t precision_z, int8_t precision_m, size_t *twkb_size)
Definition: lwout_twkb.c:618
This library is the generic geometry handling section of PostGIS.
Datum WKBFromLWGEOM(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:423
Datum parse_WKT_lwgeom(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:728
Datum TWKBFromLWGEOM(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:466
PG_FUNCTION_INFO_V1(LWGEOM_in)
Datum LWGEOM_in(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:85
Datum LWGEOM_to_latlon(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:199
Datum LWGEOM_out(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:269
Datum LWGEOM_to_bytea(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:808
Datum LWGEOMFromTWKB(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:400
Datum LWGEOM_from_bytea(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:821
Datum TWKBFromLWGEOMArray(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:522
Datum LWGEOM_send(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:795
Datum LWGEOM_dropBBOX(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:703
Datum LWGEOM_to_text(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:340
Datum LWGEOMFromEWKB(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:370
Datum LWGEOM_addBBOX(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:687
void elog_ERROR(const char *string)
Definition: lwgeom_inout.c:716
Datum LWGEOM_asHEXEWKB(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:287
Datum LWGEOM_recv(PG_FUNCTION_ARGS)
Definition: lwgeom_inout.c:756
type
Definition: ovdump.py:41
char * text_to_cstring(const text *textptr)
GSERIALIZED * geometry_serialize(LWGEOM *lwgeom)
unsigned int int32
Definition: shpopen.c:273
uint8_t type
Definition: liblwgeom.h:506
Parser result structure: returns the result of attempting to convert (E)WKT/(E)WKB to LWGEOM.
Definition: liblwgeom.h:2013
unsigned int uint32_t
Definition: uthash.h:78
unsigned char uint8_t
Definition: uthash.h:79