source: trunk/postgis/lwgeom_ogc.c

Last change on this file was 17593, checked in by Raul Marin, 5 years ago

Speed up ST_X, ST_Y, ST_Z and ST_M

Closes https://github.com/postgis/postgis/pull/435
Closes https://trac.osgeo.org/postgis/ticket/4449

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
  • Property svn:mime-type set to text/plain
File size: 23.2 KB
Line 
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) */
47Datum LWGEOM_get_srid(PG_FUNCTION_ARGS);
48/* ---- SetSRID(geometry, integer) */
49Datum LWGEOM_set_srid(PG_FUNCTION_ARGS);
50/* ---- GeometryType(geometry) */
51Datum LWGEOM_getTYPE(PG_FUNCTION_ARGS);
52Datum geometry_geometrytype(PG_FUNCTION_ARGS);
53/* ---- NumPoints(geometry) */
54Datum LWGEOM_numpoints_linestring(PG_FUNCTION_ARGS);
55/* ---- NumGeometries(geometry) */
56Datum LWGEOM_numgeometries_collection(PG_FUNCTION_ARGS);
57/* ---- GeometryN(geometry, integer) */
58Datum LWGEOM_geometryn_collection(PG_FUNCTION_ARGS);
59/* ---- Dimension(geometry) */
60Datum LWGEOM_dimension(PG_FUNCTION_ARGS);
61/* ---- ExteriorRing(geometry) */
62Datum LWGEOM_exteriorring_polygon(PG_FUNCTION_ARGS);
63/* ---- InteriorRingN(geometry, integer) */
64Datum LWGEOM_interiorringn_polygon(PG_FUNCTION_ARGS);
65/* ---- NumInteriorRings(geometry) */
66Datum LWGEOM_numinteriorrings_polygon(PG_FUNCTION_ARGS);
67/* ---- PointN(geometry, integer) */
68Datum LWGEOM_pointn_linestring(PG_FUNCTION_ARGS);
69/* ---- X(geometry) */
70Datum LWGEOM_x_point(PG_FUNCTION_ARGS);
71/* ---- Y(geometry) */
72Datum LWGEOM_y_point(PG_FUNCTION_ARGS);
73/* ---- Z(geometry) */
74Datum LWGEOM_z_point(PG_FUNCTION_ARGS);
75/* ---- M(geometry) */
76Datum LWGEOM_m_point(PG_FUNCTION_ARGS);
77/* ---- StartPoint(geometry) */
78Datum LWGEOM_startpoint_linestring(PG_FUNCTION_ARGS);
79/* ---- EndPoint(geometry) */
80Datum LWGEOM_endpoint_linestring(PG_FUNCTION_ARGS);
81/* ---- AsText(geometry) */
82Datum LWGEOM_asText(PG_FUNCTION_ARGS);
83/* ---- AsBinary(geometry, [XDR|NDR]) */
84Datum LWGEOM_asBinary(PG_FUNCTION_ARGS);
85/* ---- GeometryFromText(text, integer) */
86Datum LWGEOM_from_text(PG_FUNCTION_ARGS);
87/* ---- GeomFromWKB(bytea, integer) */
88Datum LWGEOM_from_WKB(PG_FUNCTION_ARGS);
89/* ---- IsClosed(geometry) */
90Datum LWGEOM_isclosed(PG_FUNCTION_ARGS);
91
92/*------------------------------------------------------------------*/
93
94/* getSRID(lwgeom) :: int4 */
95PG_FUNCTION_INFO_V1(LWGEOM_get_srid);
96Datum 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 */
105PG_FUNCTION_INFO_V1(LWGEOM_set_srid);
106Datum 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 */
115PG_FUNCTION_INFO_V1(LWGEOM_getTYPE);
116Datum 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 */
174static 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 */
192PG_FUNCTION_INFO_V1(geometry_geometrytype);
193Datum 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*/
214PG_FUNCTION_INFO_V1(LWGEOM_numpoints_linestring);
215Datum 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
235PG_FUNCTION_INFO_V1(LWGEOM_numgeometries_collection);
236Datum 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 */
258PG_FUNCTION_INFO_V1(LWGEOM_geometryn_collection);
259Datum 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*/
309PG_FUNCTION_INFO_V1(LWGEOM_dimension);
310Datum 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 */
335PG_FUNCTION_INFO_V1(LWGEOM_exteriorring_polygon);
336Datum 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*/
418PG_FUNCTION_INFO_V1(LWGEOM_numinteriorrings_polygon);
419Datum 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 */
459PG_FUNCTION_INFO_V1(LWGEOM_interiorringn_polygon);
460Datum 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 */
551PG_FUNCTION_INFO_V1(LWGEOM_pointn_linestring);
552Datum 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 */
599PG_FUNCTION_INFO_V1(LWGEOM_x_point);
600Datum 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 */
619PG_FUNCTION_INFO_V1(LWGEOM_y_point);
620Datum 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 */
640PG_FUNCTION_INFO_V1(LWGEOM_z_point);
641Datum 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 */
660PG_FUNCTION_INFO_V1(LWGEOM_m_point);
661Datum 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*/
681PG_FUNCTION_INFO_V1(LWGEOM_startpoint_linestring);
682Datum 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 */
711PG_FUNCTION_INFO_V1(LWGEOM_endpoint_linestring);
712Datum 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 */
746PG_FUNCTION_INFO_V1(LWGEOM_from_text);
747Datum 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 */
787PG_FUNCTION_INFO_V1(LWGEOM_from_WKB);
788Datum 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
798 if ( lwgeom_needs_bbox(lwgeom) )
799 lwgeom_add_bbox(lwgeom);
800
801 geom = geometry_serialize(lwgeom);
802 lwgeom_free(lwgeom);
803 PG_FREE_IF_COPY(bytea_wkb, 0);
804
805 if ( gserialized_get_srid(geom) != SRID_UNKNOWN )
806 {
807 elog(WARNING, "OGC WKB expected, EWKB provided - use GeometryFromEWKB() for this");
808 }
809
810 if ( PG_NARGS() > 1 )
811 {
812 srid = PG_GETARG_INT32(1);
813 if ( srid != gserialized_get_srid(geom) )
814 gserialized_set_srid(geom, srid);
815 }
816
817 PG_RETURN_POINTER(geom);
818}
819
820/** convert LWGEOM to wkt (in TEXT format) */
821PG_FUNCTION_INFO_V1(LWGEOM_asText);
822Datum LWGEOM_asText(PG_FUNCTION_ARGS)
823{
824 GSERIALIZED *geom;
825 LWGEOM *lwgeom;
826 char *wkt;
827 size_t wkt_size;
828 text *result;
829 int dbl_dig_for_wkt = DBL_DIG;
830
831 POSTGIS_DEBUG(2, "Called.");
832
833 geom = PG_GETARG_GSERIALIZED_P(0);
834 lwgeom = lwgeom_from_gserialized(geom);
835
836 if (PG_NARGS() > 1) dbl_dig_for_wkt = PG_GETARG_INT32(1);
837
838 /* Write to WKT and free the geometry */
839 wkt = lwgeom_to_wkt(lwgeom, WKT_ISO, dbl_dig_for_wkt, &wkt_size);
840 lwgeom_free(lwgeom);
841 POSTGIS_DEBUGF(3, "WKT size = %u, WKT length = %u", (unsigned int)wkt_size, (unsigned int)strlen(wkt));
842
843 /* Write to text and free the WKT */
844 result = cstring_to_text(wkt);
845 lwfree(wkt);
846
847 /* Return the text */
848 PG_FREE_IF_COPY(geom, 0);
849 PG_RETURN_TEXT_P(result);
850}
851
852
853/** convert LWGEOM to wkb (in BINARY format) */
854PG_FUNCTION_INFO_V1(LWGEOM_asBinary);
855Datum LWGEOM_asBinary(PG_FUNCTION_ARGS)
856{
857 GSERIALIZED *geom;
858 LWGEOM *lwgeom;
859 uint8_t *wkb;
860 size_t wkb_size;
861 bytea *result;
862 uint8_t variant = WKB_ISO;
863
864 if (PG_ARGISNULL(0))
865 PG_RETURN_NULL();
866
867 /* Get a 2D version of the geometry */
868 geom = PG_GETARG_GSERIALIZED_P(0);
869 lwgeom = lwgeom_from_gserialized(geom);
870
871
872 /* If user specified endianness, respect it */
873 if ( (PG_NARGS()>1) && (!PG_ARGISNULL(1)) )
874 {
875 text *wkb_endian = PG_GETARG_TEXT_P(1);
876
877 if ( ! strncmp(VARDATA(wkb_endian), "xdr", 3) ||
878 ! strncmp(VARDATA(wkb_endian), "XDR", 3) )
879 {
880 variant = variant | WKB_XDR;
881 }
882 else
883 {
884 variant = variant | WKB_NDR;
885 }
886 }
887
888 /* Write to WKB and free the geometry */
889 wkb = lwgeom_to_wkb(lwgeom, variant, &wkb_size);
890 lwgeom_free(lwgeom);
891
892 /* Write to text and free the WKT */
893 result = palloc(wkb_size + VARHDRSZ);
894 memcpy(VARDATA(result), wkb, wkb_size);
895 SET_VARSIZE(result, wkb_size + VARHDRSZ);
896 lwfree(wkb);
897
898 /* Return the text */
899 PG_FREE_IF_COPY(geom, 0);
900 PG_RETURN_BYTEA_P(result);
901}
902
903
904
905/**
906 * @brief IsClosed(GEOMETRY) if geometry is a linestring then returns
907 * startpoint == endpoint. If its not a linestring then return NULL.
908 * If it's a collection containing multiple linestrings,
909 * @return true only if all the linestrings have startpoint=endpoint.
910 */
911PG_FUNCTION_INFO_V1(LWGEOM_isclosed);
912Datum LWGEOM_isclosed(PG_FUNCTION_ARGS)
913{
914 GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
915 LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
916 int closed = lwgeom_is_closed(lwgeom);
917
918 lwgeom_free(lwgeom);
919 PG_FREE_IF_COPY(geom, 0);
920 PG_RETURN_BOOL(closed);
921}
Note: See TracBrowser for help on using the repository browser.