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