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