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 2001-2005 Refractions Research Inc.
22 *
23 **********************************************************************/
24
25
26 #include "postgres.h"
27
28 #include <math.h>
29 #include <float.h>
30 #include <string.h>
31 #include <stdio.h>
32
33 #include "access/gist.h"
34 #include "access/itup.h"
35
36 #include "fmgr.h"
37 #include "utils/builtins.h"
38 #include "utils/elog.h"
39
40 #include "../postgis_config.h"
41
42 #include "liblwgeom.h"
43 #include "lwgeom_pg.h"
44
45
46 /* ---- SRID(geometry) */
47 Datum LWGEOM_get_srid(PG_FUNCTION_ARGS);
48 /* ---- SetSRID(geometry, integer) */
49 Datum LWGEOM_set_srid(PG_FUNCTION_ARGS);
50 /* ---- GeometryType(geometry) */
51 Datum LWGEOM_getTYPE(PG_FUNCTION_ARGS);
52 Datum geometry_geometrytype(PG_FUNCTION_ARGS);
53 /* ---- NumPoints(geometry) */
54 Datum LWGEOM_numpoints_linestring(PG_FUNCTION_ARGS);
55 /* ---- NumGeometries(geometry) */
56 Datum LWGEOM_numgeometries_collection(PG_FUNCTION_ARGS);
57 /* ---- GeometryN(geometry, integer) */
58 Datum LWGEOM_geometryn_collection(PG_FUNCTION_ARGS);
59 /* ---- Dimension(geometry) */
60 Datum LWGEOM_dimension(PG_FUNCTION_ARGS);
61 /* ---- ExteriorRing(geometry) */
62 Datum LWGEOM_exteriorring_polygon(PG_FUNCTION_ARGS);
63 /* ---- InteriorRingN(geometry, integer) */
64 Datum LWGEOM_interiorringn_polygon(PG_FUNCTION_ARGS);
65 /* ---- NumInteriorRings(geometry) */
66 Datum LWGEOM_numinteriorrings_polygon(PG_FUNCTION_ARGS);
67 /* ---- PointN(geometry, integer) */
68 Datum LWGEOM_pointn_linestring(PG_FUNCTION_ARGS);
69 /* ---- X(geometry) */
70 Datum LWGEOM_x_point(PG_FUNCTION_ARGS);
71 /* ---- Y(geometry) */
72 Datum LWGEOM_y_point(PG_FUNCTION_ARGS);
73 /* ---- Z(geometry) */
74 Datum LWGEOM_z_point(PG_FUNCTION_ARGS);
75 /* ---- M(geometry) */
76 Datum LWGEOM_m_point(PG_FUNCTION_ARGS);
77 /* ---- StartPoint(geometry) */
78 Datum LWGEOM_startpoint_linestring(PG_FUNCTION_ARGS);
79 /* ---- EndPoint(geometry) */
80 Datum LWGEOM_endpoint_linestring(PG_FUNCTION_ARGS);
81 /* ---- AsText(geometry) */
82 Datum LWGEOM_asText(PG_FUNCTION_ARGS);
83 /* ---- AsBinary(geometry, [XDR|NDR]) */
84 Datum LWGEOM_asBinary(PG_FUNCTION_ARGS);
85 /* ---- GeometryFromText(text, integer) */
86 Datum LWGEOM_from_text(PG_FUNCTION_ARGS);
87 /* ---- GeomFromWKB(bytea, integer) */
88 Datum LWGEOM_from_WKB(PG_FUNCTION_ARGS);
89 /* ---- IsClosed(geometry) */
90 Datum LWGEOM_isclosed(PG_FUNCTION_ARGS);
91
92 /*------------------------------------------------------------------*/
93
94 /* getSRID(lwgeom) :: int4 */
95 PG_FUNCTION_INFO_V1(LWGEOM_get_srid);
LWGEOM_get_srid(PG_FUNCTION_ARGS)96 Datum LWGEOM_get_srid(PG_FUNCTION_ARGS)
97 {
98 GSERIALIZED *geom=PG_GETARG_GSERIALIZED_P(0);
99 int32_t srid = gserialized_get_srid(geom);
100 PG_FREE_IF_COPY(geom,0);
101 PG_RETURN_INT32(srid);
102 }
103
104 /* setSRID(lwgeom, int4) :: lwgeom */
105 PG_FUNCTION_INFO_V1(LWGEOM_set_srid);
LWGEOM_set_srid(PG_FUNCTION_ARGS)106 Datum LWGEOM_set_srid(PG_FUNCTION_ARGS)
107 {
108 GSERIALIZED *g = (GSERIALIZED *)PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
109 int32_t srid = PG_GETARG_INT32(1);
110 gserialized_set_srid(g, srid);
111 PG_RETURN_POINTER(g);
112 }
113
114 /* returns a string representation of this geometry's type */
115 PG_FUNCTION_INFO_V1(LWGEOM_getTYPE);
LWGEOM_getTYPE(PG_FUNCTION_ARGS)116 Datum LWGEOM_getTYPE(PG_FUNCTION_ARGS)
117 {
118 GSERIALIZED *gser;
119 text *text_ob;
120 char *result;
121 uint8_t type;
122 static int maxtyplen = 20;
123
124 gser = PG_GETARG_GSERIALIZED_P_SLICE(0, 0, gserialized_max_header_size());
125 text_ob = palloc0(VARHDRSZ + maxtyplen);
126 result = VARDATA(text_ob);
127
128 type = gserialized_get_type(gser);
129
130 if (type == POINTTYPE)
131 strcpy(result,"POINT");
132 else if (type == MULTIPOINTTYPE)
133 strcpy(result,"MULTIPOINT");
134 else if (type == LINETYPE)
135 strcpy(result,"LINESTRING");
136 else if (type == CIRCSTRINGTYPE)
137 strcpy(result,"CIRCULARSTRING");
138 else if (type == COMPOUNDTYPE)
139 strcpy(result, "COMPOUNDCURVE");
140 else if (type == MULTILINETYPE)
141 strcpy(result,"MULTILINESTRING");
142 else if (type == MULTICURVETYPE)
143 strcpy(result, "MULTICURVE");
144 else if (type == POLYGONTYPE)
145 strcpy(result,"POLYGON");
146 else if (type == TRIANGLETYPE)
147 strcpy(result,"TRIANGLE");
148 else if (type == CURVEPOLYTYPE)
149 strcpy(result,"CURVEPOLYGON");
150 else if (type == MULTIPOLYGONTYPE)
151 strcpy(result,"MULTIPOLYGON");
152 else if (type == MULTISURFACETYPE)
153 strcpy(result, "MULTISURFACE");
154 else if (type == COLLECTIONTYPE)
155 strcpy(result,"GEOMETRYCOLLECTION");
156 else if (type == POLYHEDRALSURFACETYPE)
157 strcpy(result,"POLYHEDRALSURFACE");
158 else if (type == TINTYPE)
159 strcpy(result,"TIN");
160 else
161 strcpy(result,"UNKNOWN");
162
163 if ( gserialized_has_m(gser) && ! gserialized_has_z(gser) )
164 strcat(result, "M");
165
166 SET_VARSIZE(text_ob, strlen(result) + VARHDRSZ); /* size of string */
167
168 PG_FREE_IF_COPY(gser, 0);
169
170 PG_RETURN_TEXT_P(text_ob);
171 }
172
173 /* Matches lwutil.c::lwgeomTypeName */
174 static char *stTypeName[] = {"Unknown",
175 "ST_Point",
176 "ST_LineString",
177 "ST_Polygon",
178 "ST_MultiPoint",
179 "ST_MultiLineString",
180 "ST_MultiPolygon",
181 "ST_GeometryCollection",
182 "ST_CircularString",
183 "ST_CompoundCurve",
184 "ST_CurvePolygon",
185 "ST_MultiCurve",
186 "ST_MultiSurface",
187 "ST_PolyhedralSurface",
188 "ST_Triangle",
189 "ST_Tin"};
190
191 /* returns a string representation of this geometry's type */
192 PG_FUNCTION_INFO_V1(geometry_geometrytype);
geometry_geometrytype(PG_FUNCTION_ARGS)193 Datum geometry_geometrytype(PG_FUNCTION_ARGS)
194 {
195 GSERIALIZED *gser;
196 text *type_text;
197
198 /* Read just the header from the toasted tuple */
199 gser = PG_GETARG_GSERIALIZED_P_SLICE(0, 0, gserialized_max_header_size());
200
201 /* Build a text type to store things in */
202 type_text = cstring_to_text(stTypeName[gserialized_get_type(gser)]);
203
204 PG_FREE_IF_COPY(gser, 0);
205 PG_RETURN_TEXT_P(type_text);
206 }
207
208
209
210 /**
211 * numpoints(LINESTRING) -- return the number of points in the
212 * linestring, or NULL if it is not a linestring
213 */
214 PG_FUNCTION_INFO_V1(LWGEOM_numpoints_linestring);
LWGEOM_numpoints_linestring(PG_FUNCTION_ARGS)215 Datum LWGEOM_numpoints_linestring(PG_FUNCTION_ARGS)
216 {
217 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
218 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
219 int count = -1;
220 int type = lwgeom->type;
221
222 if ( type == LINETYPE || type == CIRCSTRINGTYPE || type == COMPOUNDTYPE )
223 count = lwgeom_count_vertices(lwgeom);
224
225 lwgeom_free(lwgeom);
226 PG_FREE_IF_COPY(geom, 0);
227
228 /* OGC says this functions is only valid on LINESTRING */
229 if ( count < 0 )
230 PG_RETURN_NULL();
231
232 PG_RETURN_INT32(count);
233 }
234
235 PG_FUNCTION_INFO_V1(LWGEOM_numgeometries_collection);
LWGEOM_numgeometries_collection(PG_FUNCTION_ARGS)236 Datum LWGEOM_numgeometries_collection(PG_FUNCTION_ARGS)
237 {
238 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
239 LWGEOM *lwgeom;
240 int32 ret = 1;
241
242 lwgeom = lwgeom_from_gserialized(geom);
243 if ( lwgeom_is_empty(lwgeom) )
244 {
245 ret = 0;
246 }
247 else if ( lwgeom_is_collection(lwgeom) )
248 {
249 LWCOLLECTION *col = lwgeom_as_lwcollection(lwgeom);
250 ret = col->ngeoms;
251 }
252 lwgeom_free(lwgeom);
253 PG_FREE_IF_COPY(geom, 0);
254 PG_RETURN_INT32(ret);
255 }
256
257 /** 1-based offset */
258 PG_FUNCTION_INFO_V1(LWGEOM_geometryn_collection);
LWGEOM_geometryn_collection(PG_FUNCTION_ARGS)259 Datum LWGEOM_geometryn_collection(PG_FUNCTION_ARGS)
260 {
261 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
262 GSERIALIZED *result;
263 int type = gserialized_get_type(geom);
264 int32 idx;
265 LWCOLLECTION *coll;
266 LWGEOM *subgeom;
267
268 POSTGIS_DEBUG(2, "LWGEOM_geometryn_collection called.");
269
270 /* elog(NOTICE, "GeometryN called"); */
271
272 idx = PG_GETARG_INT32(1);
273 idx -= 1; /* index is 1-based */
274
275 /* call is valid on multi* geoms only */
276 if (type==POINTTYPE || type==LINETYPE || type==CIRCSTRINGTYPE ||
277 type==COMPOUNDTYPE || type==POLYGONTYPE ||
278 type==CURVEPOLYTYPE || type==TRIANGLETYPE)
279 {
280 if ( idx == 0 ) PG_RETURN_POINTER(geom);
281 PG_RETURN_NULL();
282 }
283
284 coll = lwgeom_as_lwcollection(lwgeom_from_gserialized(geom));
285
286 if ( idx < 0 ) PG_RETURN_NULL();
287 if ( idx >= (int32) coll->ngeoms ) PG_RETURN_NULL();
288
289 subgeom = coll->geoms[idx];
290 subgeom->srid = coll->srid;
291
292 /* COMPUTE_BBOX==TAINTING */
293 if ( coll->bbox ) lwgeom_add_bbox(subgeom);
294
295 result = geometry_serialize(subgeom);
296
297 lwcollection_free(coll);
298 PG_FREE_IF_COPY(geom, 0);
299
300 PG_RETURN_POINTER(result);
301
302 }
303
304
305 /** @brief
306 * returns 0 for points, 1 for lines, 2 for polygons, 3 for volume.
307 * returns max dimension for a collection.
308 */
309 PG_FUNCTION_INFO_V1(LWGEOM_dimension);
LWGEOM_dimension(PG_FUNCTION_ARGS)310 Datum LWGEOM_dimension(PG_FUNCTION_ARGS)
311 {
312 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
313 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
314 int dimension = -1;
315
316 dimension = lwgeom_dimension(lwgeom);
317 lwgeom_free(lwgeom);
318 PG_FREE_IF_COPY(geom, 0);
319
320 if ( dimension < 0 )
321 {
322 elog(NOTICE, "Could not compute geometry dimensions");
323 PG_RETURN_NULL();
324 }
325
326 PG_RETURN_INT32(dimension);
327 }
328
329
330 /**
331 * exteriorRing(GEOMETRY) -- find the first polygon in GEOMETRY
332 * @return its exterior ring (as a linestring).
333 * Return NULL if there is no POLYGON(..) in (first level of) GEOMETRY.
334 */
335 PG_FUNCTION_INFO_V1(LWGEOM_exteriorring_polygon);
LWGEOM_exteriorring_polygon(PG_FUNCTION_ARGS)336 Datum LWGEOM_exteriorring_polygon(PG_FUNCTION_ARGS)
337 {
338 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
339 GSERIALIZED *result;
340 POINTARRAY *extring;
341 LWGEOM *lwgeom;
342 LWLINE *line;
343 GBOX *bbox=NULL;
344 int type = gserialized_get_type(geom);
345
346 POSTGIS_DEBUG(2, "LWGEOM_exteriorring_polygon called.");
347
348 if ( (type != POLYGONTYPE) &&
349 (type != CURVEPOLYTYPE) &&
350 (type != TRIANGLETYPE))
351 {
352 PG_RETURN_NULL();
353 }
354
355 lwgeom = lwgeom_from_gserialized(geom);
356
357 if( lwgeom_is_empty(lwgeom) )
358 {
359 line = lwline_construct_empty(lwgeom->srid,
360 lwgeom_has_z(lwgeom),
361 lwgeom_has_m(lwgeom));
362 result = geometry_serialize(lwline_as_lwgeom(line));
363 }
364 else if ( type == POLYGONTYPE )
365 {
366 LWPOLY *poly = lwgeom_as_lwpoly(lwgeom);
367
368 /* Ok, now we have a polygon. Here is its exterior ring. */
369 extring = poly->rings[0];
370
371 /*
372 * This is a LWLINE constructed by exterior ring POINTARRAY
373 * If the input geom has a bbox, use it for
374 * the output geom, as exterior ring makes it up !
375 */
376 if ( poly->bbox )
377 bbox = gbox_copy(poly->bbox);
378
379 line = lwline_construct(poly->srid, bbox, extring);
380 result = geometry_serialize((LWGEOM *)line);
381
382 lwgeom_release((LWGEOM *)line);
383 }
384 else if ( type == TRIANGLETYPE )
385 {
386 LWTRIANGLE *triangle = lwgeom_as_lwtriangle(lwgeom);
387
388 /*
389 * This is a LWLINE constructed by exterior ring POINTARRAY
390 * If the input geom has a bbox, use it for
391 * the output geom, as exterior ring makes it up !
392 */
393 if ( triangle->bbox )
394 bbox = gbox_copy(triangle->bbox);
395 line = lwline_construct(triangle->srid, bbox, triangle->points);
396
397 result = geometry_serialize((LWGEOM *)line);
398
399 lwgeom_release((LWGEOM *)line);
400 }
401 else
402 {
403 LWCURVEPOLY *curvepoly = lwgeom_as_lwcurvepoly(lwgeom);
404 result = geometry_serialize(curvepoly->rings[0]);
405 }
406
407 lwgeom_free(lwgeom);
408 PG_FREE_IF_COPY(geom, 0);
409 PG_RETURN_POINTER(result);
410 }
411
412 /**
413 * NumInteriorRings(GEOMETRY) defined for Polygon and
414 * and CurvePolygon.
415 *
416 * @return the number of its interior rings (holes). NULL if not a polygon.
417 */
418 PG_FUNCTION_INFO_V1(LWGEOM_numinteriorrings_polygon);
LWGEOM_numinteriorrings_polygon(PG_FUNCTION_ARGS)419 Datum LWGEOM_numinteriorrings_polygon(PG_FUNCTION_ARGS)
420 {
421 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
422 int type = gserialized_get_type(geom);
423 LWGEOM *lwgeom;
424 int result = -1;
425
426 if ( (type != POLYGONTYPE) &&
427 (type != CURVEPOLYTYPE) &&
428 (type != TRIANGLETYPE))
429 {
430 PG_RETURN_NULL();
431 }
432
433 lwgeom = lwgeom_from_gserialized(geom);
434 if ( lwgeom_is_empty(lwgeom) )
435 {
436 result = 0;
437 }
438 else
439 {
440 const LWPOLY *poly = (LWPOLY*)lwgeom;
441 result = poly->nrings - 1;
442 }
443
444 lwgeom_free(lwgeom);
445 PG_FREE_IF_COPY(geom, 0);
446
447 if ( result < 0 )
448 PG_RETURN_NULL();
449
450 PG_RETURN_INT32(result);
451 }
452
453 /**
454 * InteriorRingN(GEOMETRY) -- find the first polygon in GEOMETRY, Index is 1-based.
455 * @return its Nth interior ring (as a linestring).
456 * Return NULL if there is no POLYGON(..) in (first level of) GEOMETRY.
457 *
458 */
459 PG_FUNCTION_INFO_V1(LWGEOM_interiorringn_polygon);
LWGEOM_interiorringn_polygon(PG_FUNCTION_ARGS)460 Datum LWGEOM_interiorringn_polygon(PG_FUNCTION_ARGS)
461 {
462 GSERIALIZED *geom;
463 int32 wanted_index;
464 LWCURVEPOLY *curvepoly = NULL;
465 LWPOLY *poly = NULL;
466 POINTARRAY *ring;
467 LWLINE *line;
468 LWGEOM *lwgeom;
469 GSERIALIZED *result;
470 GBOX *bbox = NULL;
471 int type;
472
473 POSTGIS_DEBUG(2, "LWGEOM_interiorringn_polygon called.");
474
475 wanted_index = PG_GETARG_INT32(1);
476 if ( wanted_index < 1 )
477 {
478 PG_RETURN_NULL(); /* index out of range */
479 }
480
481 geom = PG_GETARG_GSERIALIZED_P(0);
482 type = gserialized_get_type(geom);
483
484 if ( (type != POLYGONTYPE) && (type != CURVEPOLYTYPE) )
485 {
486 PG_FREE_IF_COPY(geom, 0);
487 PG_RETURN_NULL();
488 }
489
490 lwgeom = lwgeom_from_gserialized(geom);
491 if( lwgeom_is_empty(lwgeom) )
492 {
493 lwpoly_free(poly);
494 PG_FREE_IF_COPY(geom, 0);
495 PG_RETURN_NULL();
496 }
497
498 if ( type == POLYGONTYPE)
499 {
500 poly = lwgeom_as_lwpoly(lwgeom_from_gserialized(geom));
501
502 /* Ok, now we have a polygon. Let's see if it has enough holes */
503 if ( wanted_index >= (int32)poly->nrings )
504 {
505 lwpoly_free(poly);
506 PG_FREE_IF_COPY(geom, 0);
507 PG_RETURN_NULL();
508 }
509
510 ring = poly->rings[wanted_index];
511
512 /* COMPUTE_BBOX==TAINTING */
513 if ( poly->bbox )
514 {
515 bbox = lwalloc(sizeof(GBOX));
516 ptarray_calculate_gbox_cartesian(ring, bbox);
517 }
518
519 /* This is a LWLINE constructed by interior ring POINTARRAY */
520 line = lwline_construct(poly->srid, bbox, ring);
521
522
523 result = geometry_serialize((LWGEOM *)line);
524 lwline_release(line);
525 lwpoly_free(poly);
526 }
527 else
528 {
529 curvepoly = lwgeom_as_lwcurvepoly(lwgeom_from_gserialized(geom));
530
531 if (wanted_index >= (int32)curvepoly->nrings)
532 {
533 PG_FREE_IF_COPY(geom, 0);
534 lwgeom_release((LWGEOM *)curvepoly);
535 PG_RETURN_NULL();
536 }
537
538 result = geometry_serialize(curvepoly->rings[wanted_index]);
539 lwgeom_free((LWGEOM*)curvepoly);
540 }
541
542 PG_FREE_IF_COPY(geom, 0);
543 PG_RETURN_POINTER(result);
544 }
545
546 /**
547 * PointN(GEOMETRY,INTEGER) -- find the first linestring in GEOMETRY,
548 * @return the point at index INTEGER (1 is 1st point). Return NULL if
549 * there is no LINESTRING(..) in GEOMETRY or INTEGER is out of bounds.
550 */
551 PG_FUNCTION_INFO_V1(LWGEOM_pointn_linestring);
LWGEOM_pointn_linestring(PG_FUNCTION_ARGS)552 Datum LWGEOM_pointn_linestring(PG_FUNCTION_ARGS)
553 {
554 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
555 int where = PG_GETARG_INT32(1);
556 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
557 LWPOINT *lwpoint = NULL;
558 int type = lwgeom->type;
559
560 /* If index is negative, count backward */
561 if( where < 1 )
562 {
563 int count = -1;
564 if ( type == LINETYPE || type == CIRCSTRINGTYPE || type == COMPOUNDTYPE )
565 count = lwgeom_count_vertices(lwgeom);
566 if(count >0)
567 {
568 /* only work if we found the total point number */
569 /* converting where to positive backward indexing, +1 because 1 indexing */
570 where = where + count + 1;
571 }
572 if (where < 1)
573 PG_RETURN_NULL();
574 }
575
576 if ( type == LINETYPE || type == CIRCSTRINGTYPE )
577 {
578 /* OGC index starts at one, so we substract first. */
579 lwpoint = lwline_get_lwpoint((LWLINE*)lwgeom, where - 1);
580 }
581 else if ( type == COMPOUNDTYPE )
582 {
583 lwpoint = lwcompound_get_lwpoint((LWCOMPOUND*)lwgeom, where - 1);
584 }
585
586 lwgeom_free(lwgeom);
587 PG_FREE_IF_COPY(geom, 0);
588
589 if ( ! lwpoint )
590 PG_RETURN_NULL();
591
592 PG_RETURN_POINTER(geometry_serialize(lwpoint_as_lwgeom(lwpoint)));
593 }
594
595 /**
596 * X(GEOMETRY) -- return X value of the point.
597 * @return an error if input is not a point.
598 */
599 PG_FUNCTION_INFO_V1(LWGEOM_x_point);
LWGEOM_x_point(PG_FUNCTION_ARGS)600 Datum LWGEOM_x_point(PG_FUNCTION_ARGS)
601 {
602 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
603 POINT4D pt;
604
605 if (gserialized_get_type(geom) != POINTTYPE)
606 lwpgerror("Argument to ST_X() must have type POINT");
607
608 if (gserialized_peek_first_point(geom, &pt) == LW_FAILURE)
609 {
610 PG_RETURN_NULL();
611 }
612 PG_RETURN_FLOAT8(pt.x);
613 }
614
615 /**
616 * Y(GEOMETRY) -- return Y value of the point.
617 * Raise an error if input is not a point.
618 */
619 PG_FUNCTION_INFO_V1(LWGEOM_y_point);
LWGEOM_y_point(PG_FUNCTION_ARGS)620 Datum LWGEOM_y_point(PG_FUNCTION_ARGS)
621 {
622 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
623 POINT4D pt;
624
625 if (gserialized_get_type(geom) != POINTTYPE)
626 lwpgerror("Argument to ST_Y() must have type POINT");
627
628 if (gserialized_peek_first_point(geom, &pt) == LW_FAILURE)
629 {
630 PG_RETURN_NULL();
631 }
632 PG_RETURN_FLOAT8(pt.y);
633 }
634
635 /**
636 * Z(GEOMETRY) -- return Z value of the point.
637 * @return NULL if there is no Z in the point.
638 * Raise an error if input is not a point.
639 */
640 PG_FUNCTION_INFO_V1(LWGEOM_z_point);
LWGEOM_z_point(PG_FUNCTION_ARGS)641 Datum LWGEOM_z_point(PG_FUNCTION_ARGS)
642 {
643 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
644 POINT4D pt;
645
646 if (gserialized_get_type(geom) != POINTTYPE)
647 lwpgerror("Argument to ST_Z() must have type POINT");
648
649 if (!gserialized_has_z(geom) || (gserialized_peek_first_point(geom, &pt) == LW_FAILURE))
650 {
651 PG_RETURN_NULL();
652 }
653 PG_RETURN_FLOAT8(pt.z);
654 }
655
656 /** M(GEOMETRY) -- find the first POINT(..) in GEOMETRY, returns its M value.
657 * @return NULL if there is no POINT(..) in GEOMETRY.
658 * Return NULL if there is no M in this geometry.
659 */
660 PG_FUNCTION_INFO_V1(LWGEOM_m_point);
LWGEOM_m_point(PG_FUNCTION_ARGS)661 Datum LWGEOM_m_point(PG_FUNCTION_ARGS)
662 {
663 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
664 POINT4D pt;
665
666 if (gserialized_get_type(geom) != POINTTYPE)
667 lwpgerror("Argument to ST_M() must have type POINT");
668
669 if (!gserialized_has_m(geom) || (gserialized_peek_first_point(geom, &pt) == LW_FAILURE))
670 {
671 PG_RETURN_NULL();
672 }
673 PG_RETURN_FLOAT8(pt.m);
674 }
675
676 /**
677 * ST_StartPoint(GEOMETRY)
678 * @return the first point of a linestring.
679 * Return NULL if there is no LINESTRING
680 */
681 PG_FUNCTION_INFO_V1(LWGEOM_startpoint_linestring);
LWGEOM_startpoint_linestring(PG_FUNCTION_ARGS)682 Datum LWGEOM_startpoint_linestring(PG_FUNCTION_ARGS)
683 {
684 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
685 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
686 LWPOINT *lwpoint = NULL;
687 int type = lwgeom->type;
688
689 if ( type == LINETYPE || type == CIRCSTRINGTYPE )
690 {
691 lwpoint = lwline_get_lwpoint((LWLINE*)lwgeom, 0);
692 }
693 else if ( type == COMPOUNDTYPE )
694 {
695 lwpoint = lwcompound_get_startpoint((LWCOMPOUND*)lwgeom);
696 }
697
698 lwgeom_free(lwgeom);
699 PG_FREE_IF_COPY(geom, 0);
700
701 if ( ! lwpoint )
702 PG_RETURN_NULL();
703
704 PG_RETURN_POINTER(geometry_serialize(lwpoint_as_lwgeom(lwpoint)));
705 }
706
707 /** EndPoint(GEOMETRY) -- find the first linestring in GEOMETRY,
708 * @return the last point.
709 * Return NULL if there is no LINESTRING(..) in GEOMETRY
710 */
711 PG_FUNCTION_INFO_V1(LWGEOM_endpoint_linestring);
LWGEOM_endpoint_linestring(PG_FUNCTION_ARGS)712 Datum LWGEOM_endpoint_linestring(PG_FUNCTION_ARGS)
713 {
714 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
715 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
716 LWPOINT *lwpoint = NULL;
717 int type = lwgeom->type;
718
719 if ( type == LINETYPE || type == CIRCSTRINGTYPE )
720 {
721 LWLINE *line = (LWLINE*)lwgeom;
722 if ( line->points )
723 lwpoint = lwline_get_lwpoint((LWLINE*)lwgeom, line->points->npoints - 1);
724 }
725 else if ( type == COMPOUNDTYPE )
726 {
727 lwpoint = lwcompound_get_endpoint((LWCOMPOUND*)lwgeom);
728 }
729
730 lwgeom_free(lwgeom);
731 PG_FREE_IF_COPY(geom, 0);
732
733 if ( ! lwpoint )
734 PG_RETURN_NULL();
735
736 PG_RETURN_POINTER(geometry_serialize(lwpoint_as_lwgeom(lwpoint)));
737 }
738
739 /**
740 * @brief Returns a geometry Given an OGC WKT (and optionally a SRID)
741 * @return a geometry.
742 * @note Note that this is a a stricter version
743 * of geometry_in, where we refuse to
744 * accept (HEX)WKB or EWKT.
745 */
746 PG_FUNCTION_INFO_V1(LWGEOM_from_text);
LWGEOM_from_text(PG_FUNCTION_ARGS)747 Datum LWGEOM_from_text(PG_FUNCTION_ARGS)
748 {
749 text *wkttext = PG_GETARG_TEXT_P(0);
750 char *wkt = text_to_cstring(wkttext);
751 LWGEOM_PARSER_RESULT lwg_parser_result;
752 GSERIALIZED *geom_result = NULL;
753 LWGEOM *lwgeom;
754
755 POSTGIS_DEBUG(2, "LWGEOM_from_text");
756 POSTGIS_DEBUGF(3, "wkt: [%s]", wkt);
757
758 if (lwgeom_parse_wkt(&lwg_parser_result, wkt, LW_PARSER_CHECK_ALL) == LW_FAILURE )
759 PG_PARSER_ERROR(lwg_parser_result);
760
761 lwgeom = lwg_parser_result.geom;
762
763 if ( lwgeom->srid != SRID_UNKNOWN )
764 {
765 elog(WARNING, "OGC WKT expected, EWKT provided - use GeomFromEWKT() for this");
766 }
767
768 /* read user-requested SRID if any */
769 if ( PG_NARGS() > 1 )
770 lwgeom_set_srid(lwgeom, PG_GETARG_INT32(1));
771
772 geom_result = geometry_serialize(lwgeom);
773 lwgeom_parser_result_free(&lwg_parser_result);
774
775 PG_RETURN_POINTER(geom_result);
776 }
777
778 /**
779 * Given an OGC WKB (and optionally a SRID)
780 * return a geometry.
781 *
782 * @note that this is a wrapper around
783 * lwgeom_from_wkb, where we throw
784 * a warning if ewkb passed in
785 * accept EWKB.
786 */
787 PG_FUNCTION_INFO_V1(LWGEOM_from_WKB);
LWGEOM_from_WKB(PG_FUNCTION_ARGS)788 Datum LWGEOM_from_WKB(PG_FUNCTION_ARGS)
789 {
790 bytea *bytea_wkb = PG_GETARG_BYTEA_P(0);
791 int32 srid = 0;
792 GSERIALIZED *geom;
793 LWGEOM *lwgeom;
794 uint8_t *wkb = (uint8_t*)VARDATA(bytea_wkb);
795
796 lwgeom = lwgeom_from_wkb(wkb, VARSIZE_ANY_EXHDR(bytea_wkb), LW_PARSER_CHECK_ALL);
797 if (!lwgeom)
798 lwpgerror("Unable to parse WKB");
799
800 geom = geometry_serialize(lwgeom);
801 lwgeom_free(lwgeom);
802 PG_FREE_IF_COPY(bytea_wkb, 0);
803
804 if ( gserialized_get_srid(geom) != SRID_UNKNOWN )
805 {
806 elog(WARNING, "OGC WKB expected, EWKB provided - use GeometryFromEWKB() for this");
807 }
808
809 if ( PG_NARGS() > 1 )
810 {
811 srid = PG_GETARG_INT32(1);
812 if ( srid != gserialized_get_srid(geom) )
813 gserialized_set_srid(geom, srid);
814 }
815
816 PG_RETURN_POINTER(geom);
817 }
818
819 /** convert LWGEOM to wkt (in TEXT format) */
820 PG_FUNCTION_INFO_V1(LWGEOM_asText);
LWGEOM_asText(PG_FUNCTION_ARGS)821 Datum LWGEOM_asText(PG_FUNCTION_ARGS)
822 {
823 GSERIALIZED *geom;
824 LWGEOM *lwgeom;
825 char *wkt;
826 size_t wkt_size;
827 text *result;
828 int dbl_dig_for_wkt = DBL_DIG;
829
830 POSTGIS_DEBUG(2, "Called.");
831
832 geom = PG_GETARG_GSERIALIZED_P(0);
833 lwgeom = lwgeom_from_gserialized(geom);
834
835 if (PG_NARGS() > 1) dbl_dig_for_wkt = PG_GETARG_INT32(1);
836
837 /* Write to WKT and free the geometry */
838 wkt = lwgeom_to_wkt(lwgeom, WKT_ISO, dbl_dig_for_wkt, &wkt_size);
839 lwgeom_free(lwgeom);
840 POSTGIS_DEBUGF(3, "WKT size = %u, WKT length = %u", (unsigned int)wkt_size, (unsigned int)strlen(wkt));
841
842 /* Write to text and free the WKT */
843 result = cstring_to_text(wkt);
844 lwfree(wkt);
845
846 /* Return the text */
847 PG_FREE_IF_COPY(geom, 0);
848 PG_RETURN_TEXT_P(result);
849 }
850
851
852 /** convert LWGEOM to wkb (in BINARY format) */
853 PG_FUNCTION_INFO_V1(LWGEOM_asBinary);
LWGEOM_asBinary(PG_FUNCTION_ARGS)854 Datum LWGEOM_asBinary(PG_FUNCTION_ARGS)
855 {
856 GSERIALIZED *geom;
857 LWGEOM *lwgeom;
858 uint8_t *wkb;
859 size_t wkb_size;
860 bytea *result;
861 uint8_t variant = WKB_ISO;
862
863 if (PG_ARGISNULL(0))
864 PG_RETURN_NULL();
865
866 /* Get a 2D version of the geometry */
867 geom = PG_GETARG_GSERIALIZED_P(0);
868 lwgeom = lwgeom_from_gserialized(geom);
869
870
871 /* If user specified endianness, respect it */
872 if ( (PG_NARGS()>1) && (!PG_ARGISNULL(1)) )
873 {
874 text *wkb_endian = PG_GETARG_TEXT_P(1);
875
876 if ( ! strncmp(VARDATA(wkb_endian), "xdr", 3) ||
877 ! strncmp(VARDATA(wkb_endian), "XDR", 3) )
878 {
879 variant = variant | WKB_XDR;
880 }
881 else
882 {
883 variant = variant | WKB_NDR;
884 }
885 }
886
887 /* Write to WKB and free the geometry */
888 wkb = lwgeom_to_wkb(lwgeom, variant, &wkb_size);
889 lwgeom_free(lwgeom);
890
891 /* Write to text and free the WKT */
892 result = palloc(wkb_size + VARHDRSZ);
893 memcpy(VARDATA(result), wkb, wkb_size);
894 SET_VARSIZE(result, wkb_size + VARHDRSZ);
895 lwfree(wkb);
896
897 /* Return the text */
898 PG_FREE_IF_COPY(geom, 0);
899 PG_RETURN_BYTEA_P(result);
900 }
901
902
903
904 /**
905 * @brief IsClosed(GEOMETRY) if geometry is a linestring then returns
906 * startpoint == endpoint. If its not a linestring then return NULL.
907 * If it's a collection containing multiple linestrings,
908 * @return true only if all the linestrings have startpoint=endpoint.
909 */
910 PG_FUNCTION_INFO_V1(LWGEOM_isclosed);
LWGEOM_isclosed(PG_FUNCTION_ARGS)911 Datum LWGEOM_isclosed(PG_FUNCTION_ARGS)
912 {
913 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
914 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
915 int closed = lwgeom_is_closed(lwgeom);
916
917 lwgeom_free(lwgeom);
918 PG_FREE_IF_COPY(geom, 0);
919 PG_RETURN_BOOL(closed);
920 }
921