891 lines
22 KiB
C
891 lines
22 KiB
C
/**********************************************************************
|
|
*
|
|
* PostGIS - Spatial Types for PostgreSQL
|
|
* http://postgis.net
|
|
*
|
|
* PostGIS is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* PostGIS is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with PostGIS. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
**********************************************************************
|
|
*
|
|
* Copyright 2001-2005 Refractions Research Inc.
|
|
*
|
|
**********************************************************************/
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
#include <math.h>
|
|
#include <float.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include "access/gist.h"
|
|
#include "access/itup.h"
|
|
|
|
#include "fmgr.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/elog.h"
|
|
|
|
#include "../postgis_config.h"
|
|
|
|
#include "liblwgeom.h"
|
|
#include "liblwgeom_internal.h"
|
|
#include "lwgeom_pg.h"
|
|
|
|
|
|
/* ---- SRID(geometry) */
|
|
Datum LWGEOM_get_srid(PG_FUNCTION_ARGS);
|
|
/* ---- SetSRID(geometry, integer) */
|
|
Datum LWGEOM_set_srid(PG_FUNCTION_ARGS);
|
|
/* ---- GeometryType(geometry) */
|
|
Datum LWGEOM_getTYPE(PG_FUNCTION_ARGS);
|
|
Datum geometry_geometrytype(PG_FUNCTION_ARGS);
|
|
/* ---- NumPoints(geometry) */
|
|
Datum LWGEOM_numpoints_linestring(PG_FUNCTION_ARGS);
|
|
/* ---- NumGeometries(geometry) */
|
|
Datum LWGEOM_numgeometries_collection(PG_FUNCTION_ARGS);
|
|
/* ---- GeometryN(geometry, integer) */
|
|
Datum LWGEOM_geometryn_collection(PG_FUNCTION_ARGS);
|
|
/* ---- Dimension(geometry) */
|
|
Datum LWGEOM_dimension(PG_FUNCTION_ARGS);
|
|
/* ---- ExteriorRing(geometry) */
|
|
Datum LWGEOM_exteriorring_polygon(PG_FUNCTION_ARGS);
|
|
/* ---- InteriorRingN(geometry, integer) */
|
|
Datum LWGEOM_interiorringn_polygon(PG_FUNCTION_ARGS);
|
|
/* ---- NumInteriorRings(geometry) */
|
|
Datum LWGEOM_numinteriorrings_polygon(PG_FUNCTION_ARGS);
|
|
/* ---- PointN(geometry, integer) */
|
|
Datum LWGEOM_pointn_linestring(PG_FUNCTION_ARGS);
|
|
/* ---- X(geometry) */
|
|
Datum LWGEOM_x_point(PG_FUNCTION_ARGS);
|
|
/* ---- Y(geometry) */
|
|
Datum LWGEOM_y_point(PG_FUNCTION_ARGS);
|
|
/* ---- Z(geometry) */
|
|
Datum LWGEOM_z_point(PG_FUNCTION_ARGS);
|
|
/* ---- M(geometry) */
|
|
Datum LWGEOM_m_point(PG_FUNCTION_ARGS);
|
|
/* ---- StartPoint(geometry) */
|
|
Datum LWGEOM_startpoint_linestring(PG_FUNCTION_ARGS);
|
|
/* ---- EndPoint(geometry) */
|
|
Datum LWGEOM_endpoint_linestring(PG_FUNCTION_ARGS);
|
|
/* ---- AsText(geometry) */
|
|
Datum LWGEOM_asText(PG_FUNCTION_ARGS);
|
|
/* ---- AsBinary(geometry, [XDR|NDR]) */
|
|
Datum LWGEOM_asBinary(PG_FUNCTION_ARGS);
|
|
/* ---- GeometryFromText(text, integer) */
|
|
Datum LWGEOM_from_text(PG_FUNCTION_ARGS);
|
|
/* ---- GeomFromWKB(bytea, integer) */
|
|
Datum LWGEOM_from_WKB(PG_FUNCTION_ARGS);
|
|
/* ---- IsClosed(geometry) */
|
|
Datum LWGEOM_isclosed(PG_FUNCTION_ARGS);
|
|
|
|
/*------------------------------------------------------------------*/
|
|
|
|
/* getSRID(lwgeom) :: int4 */
|
|
PG_FUNCTION_INFO_V1(LWGEOM_get_srid);
|
|
Datum LWGEOM_get_srid(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_HEADER(0);
|
|
int32_t srid = gserialized_get_srid(geom);
|
|
PG_FREE_IF_COPY(geom,0);
|
|
PG_RETURN_INT32(srid);
|
|
}
|
|
|
|
/* setSRID(lwgeom, int4) :: lwgeom */
|
|
PG_FUNCTION_INFO_V1(LWGEOM_set_srid);
|
|
Datum LWGEOM_set_srid(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *g = (GSERIALIZED *)PG_DETOAST_DATUM_COPY(PG_GETARG_DATUM(0));
|
|
int32_t srid = PG_GETARG_INT32(1);
|
|
gserialized_set_srid(g, srid);
|
|
PG_RETURN_POINTER(g);
|
|
}
|
|
|
|
/* returns a string representation of this geometry's type */
|
|
PG_FUNCTION_INFO_V1(LWGEOM_getTYPE);
|
|
Datum LWGEOM_getTYPE(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *gser;
|
|
text *text_ob;
|
|
char *result;
|
|
uint8_t type;
|
|
static int maxtyplen = 20;
|
|
|
|
gser = PG_GETARG_GSERIALIZED_HEADER(0);
|
|
text_ob = palloc0(VARHDRSZ + maxtyplen);
|
|
result = VARDATA(text_ob);
|
|
|
|
type = gserialized_get_type(gser);
|
|
|
|
if (type == POINTTYPE)
|
|
strcpy(result,"POINT");
|
|
else if (type == MULTIPOINTTYPE)
|
|
strcpy(result,"MULTIPOINT");
|
|
else if (type == LINETYPE)
|
|
strcpy(result,"LINESTRING");
|
|
else if (type == CIRCSTRINGTYPE)
|
|
strcpy(result,"CIRCULARSTRING");
|
|
else if (type == COMPOUNDTYPE)
|
|
strcpy(result, "COMPOUNDCURVE");
|
|
else if (type == MULTILINETYPE)
|
|
strcpy(result,"MULTILINESTRING");
|
|
else if (type == MULTICURVETYPE)
|
|
strcpy(result, "MULTICURVE");
|
|
else if (type == POLYGONTYPE)
|
|
strcpy(result,"POLYGON");
|
|
else if (type == TRIANGLETYPE)
|
|
strcpy(result,"TRIANGLE");
|
|
else if (type == CURVEPOLYTYPE)
|
|
strcpy(result,"CURVEPOLYGON");
|
|
else if (type == MULTIPOLYGONTYPE)
|
|
strcpy(result,"MULTIPOLYGON");
|
|
else if (type == MULTISURFACETYPE)
|
|
strcpy(result, "MULTISURFACE");
|
|
else if (type == COLLECTIONTYPE)
|
|
strcpy(result,"GEOMETRYCOLLECTION");
|
|
else if (type == POLYHEDRALSURFACETYPE)
|
|
strcpy(result,"POLYHEDRALSURFACE");
|
|
else if (type == TINTYPE)
|
|
strcpy(result,"TIN");
|
|
else
|
|
strcpy(result,"UNKNOWN");
|
|
|
|
if ( gserialized_has_m(gser) && ! gserialized_has_z(gser) )
|
|
strcat(result, "M");
|
|
|
|
SET_VARSIZE(text_ob, strlen(result) + VARHDRSZ); /* size of string */
|
|
|
|
PG_FREE_IF_COPY(gser, 0);
|
|
|
|
PG_RETURN_TEXT_P(text_ob);
|
|
}
|
|
|
|
/* Matches lwutil.c::lwgeomTypeName */
|
|
static char *stTypeName[] = {"Unknown",
|
|
"ST_Point",
|
|
"ST_LineString",
|
|
"ST_Polygon",
|
|
"ST_MultiPoint",
|
|
"ST_MultiLineString",
|
|
"ST_MultiPolygon",
|
|
"ST_GeometryCollection",
|
|
"ST_CircularString",
|
|
"ST_CompoundCurve",
|
|
"ST_CurvePolygon",
|
|
"ST_MultiCurve",
|
|
"ST_MultiSurface",
|
|
"ST_PolyhedralSurface",
|
|
"ST_Triangle",
|
|
"ST_Tin"};
|
|
|
|
/* returns a string representation of this geometry's type */
|
|
PG_FUNCTION_INFO_V1(geometry_geometrytype);
|
|
Datum geometry_geometrytype(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *gser;
|
|
text *type_text;
|
|
|
|
/* Read just the header from the toasted tuple */
|
|
gser = PG_GETARG_GSERIALIZED_HEADER(0);
|
|
|
|
/* Build a text type to store things in */
|
|
type_text = cstring_to_text(stTypeName[gserialized_get_type(gser)]);
|
|
|
|
PG_FREE_IF_COPY(gser, 0);
|
|
PG_RETURN_TEXT_P(type_text);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* numpoints(LINESTRING) -- return the number of points in the
|
|
* linestring, or NULL if it is not a linestring
|
|
*/
|
|
PG_FUNCTION_INFO_V1(LWGEOM_numpoints_linestring);
|
|
Datum LWGEOM_numpoints_linestring(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
|
|
LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
|
|
int count = -1;
|
|
int type = lwgeom->type;
|
|
|
|
if ( type == LINETYPE || type == CIRCSTRINGTYPE || type == COMPOUNDTYPE )
|
|
count = lwgeom_count_vertices(lwgeom);
|
|
|
|
lwgeom_free(lwgeom);
|
|
PG_FREE_IF_COPY(geom, 0);
|
|
|
|
/* OGC says this functions is only valid on LINESTRING */
|
|
if ( count < 0 )
|
|
PG_RETURN_NULL();
|
|
|
|
PG_RETURN_INT32(count);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(LWGEOM_numgeometries_collection);
|
|
Datum LWGEOM_numgeometries_collection(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
|
|
LWGEOM *lwgeom;
|
|
int32 ret = 1;
|
|
|
|
lwgeom = lwgeom_from_gserialized(geom);
|
|
if (lwgeom_is_empty(lwgeom))
|
|
{
|
|
ret = 0;
|
|
}
|
|
else if (lwgeom_is_collection(lwgeom))
|
|
{
|
|
LWCOLLECTION *col = lwgeom_as_lwcollection(lwgeom);
|
|
ret = col->ngeoms;
|
|
}
|
|
lwgeom_free(lwgeom);
|
|
PG_FREE_IF_COPY(geom, 0);
|
|
PG_RETURN_INT32(ret);
|
|
}
|
|
|
|
/** 1-based offset */
|
|
PG_FUNCTION_INFO_V1(LWGEOM_geometryn_collection);
|
|
Datum LWGEOM_geometryn_collection(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
|
|
GSERIALIZED *result;
|
|
int type = gserialized_get_type(geom);
|
|
int32 idx;
|
|
LWCOLLECTION *coll;
|
|
LWGEOM *subgeom;
|
|
|
|
POSTGIS_DEBUG(2, "LWGEOM_geometryn_collection called.");
|
|
|
|
/* elog(NOTICE, "GeometryN called"); */
|
|
|
|
idx = PG_GETARG_INT32(1);
|
|
idx -= 1; /* index is 1-based */
|
|
|
|
if (gserialized_is_empty(geom))
|
|
{
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* call is valid on multi* geoms only */
|
|
if (type==POINTTYPE || type==LINETYPE || type==CIRCSTRINGTYPE ||
|
|
type==COMPOUNDTYPE || type==POLYGONTYPE ||
|
|
type==CURVEPOLYTYPE || type==TRIANGLETYPE)
|
|
{
|
|
if ( idx == 0 ) PG_RETURN_POINTER(geom);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
coll = lwgeom_as_lwcollection(lwgeom_from_gserialized(geom));
|
|
|
|
if ( idx < 0 ) PG_RETURN_NULL();
|
|
if ( idx >= (int32) coll->ngeoms ) PG_RETURN_NULL();
|
|
|
|
subgeom = coll->geoms[idx];
|
|
subgeom->srid = coll->srid;
|
|
|
|
/* COMPUTE_BBOX==TAINTING */
|
|
if ( coll->bbox ) lwgeom_add_bbox(subgeom);
|
|
|
|
result = geometry_serialize(subgeom);
|
|
|
|
lwcollection_free(coll);
|
|
PG_FREE_IF_COPY(geom, 0);
|
|
|
|
PG_RETURN_POINTER(result);
|
|
|
|
}
|
|
|
|
|
|
/** @brief
|
|
* returns 0 for points, 1 for lines, 2 for polygons, 3 for volume.
|
|
* returns max dimension for a collection.
|
|
*/
|
|
PG_FUNCTION_INFO_V1(LWGEOM_dimension);
|
|
Datum LWGEOM_dimension(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
|
|
LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
|
|
int dimension = -1;
|
|
|
|
dimension = lwgeom_dimension(lwgeom);
|
|
lwgeom_free(lwgeom);
|
|
PG_FREE_IF_COPY(geom, 0);
|
|
|
|
if ( dimension < 0 )
|
|
{
|
|
elog(NOTICE, "Could not compute geometry dimensions");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
PG_RETURN_INT32(dimension);
|
|
}
|
|
|
|
|
|
/**
|
|
* exteriorRing(GEOMETRY) -- find the first polygon in GEOMETRY
|
|
* @return its exterior ring (as a linestring).
|
|
* Return NULL if there is no POLYGON(..) in (first level of) GEOMETRY.
|
|
*/
|
|
PG_FUNCTION_INFO_V1(LWGEOM_exteriorring_polygon);
|
|
Datum LWGEOM_exteriorring_polygon(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
|
|
GSERIALIZED *result;
|
|
POINTARRAY *extring;
|
|
LWGEOM *lwgeom;
|
|
LWLINE *line;
|
|
GBOX *bbox=NULL;
|
|
int type = gserialized_get_type(geom);
|
|
|
|
POSTGIS_DEBUG(2, "LWGEOM_exteriorring_polygon called.");
|
|
|
|
if ( (type != POLYGONTYPE) &&
|
|
(type != CURVEPOLYTYPE) &&
|
|
(type != TRIANGLETYPE))
|
|
{
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
lwgeom = lwgeom_from_gserialized(geom);
|
|
|
|
if( lwgeom_is_empty(lwgeom) )
|
|
{
|
|
line = lwline_construct_empty(lwgeom->srid,
|
|
lwgeom_has_z(lwgeom),
|
|
lwgeom_has_m(lwgeom));
|
|
result = geometry_serialize(lwline_as_lwgeom(line));
|
|
}
|
|
else if ( type == POLYGONTYPE )
|
|
{
|
|
LWPOLY *poly = lwgeom_as_lwpoly(lwgeom);
|
|
|
|
/* Ok, now we have a polygon. Here is its exterior ring. */
|
|
extring = poly->rings[0];
|
|
|
|
/*
|
|
* This is a LWLINE constructed by exterior ring POINTARRAY
|
|
* If the input geom has a bbox, use it for
|
|
* the output geom, as exterior ring makes it up !
|
|
*/
|
|
if ( poly->bbox )
|
|
bbox = gbox_copy(poly->bbox);
|
|
|
|
line = lwline_construct(poly->srid, bbox, extring);
|
|
result = geometry_serialize((LWGEOM *)line);
|
|
|
|
lwgeom_release((LWGEOM *)line);
|
|
}
|
|
else if ( type == TRIANGLETYPE )
|
|
{
|
|
LWTRIANGLE *triangle = lwgeom_as_lwtriangle(lwgeom);
|
|
|
|
/*
|
|
* This is a LWLINE constructed by exterior ring POINTARRAY
|
|
* If the input geom has a bbox, use it for
|
|
* the output geom, as exterior ring makes it up !
|
|
*/
|
|
if ( triangle->bbox )
|
|
bbox = gbox_copy(triangle->bbox);
|
|
line = lwline_construct(triangle->srid, bbox, triangle->points);
|
|
|
|
result = geometry_serialize((LWGEOM *)line);
|
|
|
|
lwgeom_release((LWGEOM *)line);
|
|
}
|
|
else
|
|
{
|
|
LWCURVEPOLY *curvepoly = lwgeom_as_lwcurvepoly(lwgeom);
|
|
result = geometry_serialize(curvepoly->rings[0]);
|
|
}
|
|
|
|
lwgeom_free(lwgeom);
|
|
PG_FREE_IF_COPY(geom, 0);
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
/**
|
|
* NumInteriorRings(GEOMETRY) defined for Polygon and
|
|
* and CurvePolygon.
|
|
*
|
|
* @return the number of its interior rings (holes). NULL if not a polygon.
|
|
*/
|
|
PG_FUNCTION_INFO_V1(LWGEOM_numinteriorrings_polygon);
|
|
Datum LWGEOM_numinteriorrings_polygon(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
|
|
int type = gserialized_get_type(geom);
|
|
LWGEOM *lwgeom;
|
|
int result = -1;
|
|
|
|
if ( (type != POLYGONTYPE) &&
|
|
(type != CURVEPOLYTYPE) &&
|
|
(type != TRIANGLETYPE))
|
|
{
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
lwgeom = lwgeom_from_gserialized(geom);
|
|
if ( lwgeom_is_empty(lwgeom) )
|
|
{
|
|
result = 0;
|
|
}
|
|
else
|
|
{
|
|
const LWPOLY *poly = (LWPOLY*)lwgeom;
|
|
result = poly->nrings - 1;
|
|
}
|
|
|
|
lwgeom_free(lwgeom);
|
|
PG_FREE_IF_COPY(geom, 0);
|
|
|
|
if ( result < 0 )
|
|
PG_RETURN_NULL();
|
|
|
|
PG_RETURN_INT32(result);
|
|
}
|
|
|
|
/**
|
|
* InteriorRingN(GEOMETRY) -- find the first polygon in GEOMETRY, Index is 1-based.
|
|
* @return its Nth interior ring (as a linestring).
|
|
* Return NULL if there is no POLYGON(..) in (first level of) GEOMETRY.
|
|
*
|
|
*/
|
|
PG_FUNCTION_INFO_V1(LWGEOM_interiorringn_polygon);
|
|
Datum LWGEOM_interiorringn_polygon(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom;
|
|
int32 wanted_index;
|
|
LWCURVEPOLY *curvepoly = NULL;
|
|
LWPOLY *poly = NULL;
|
|
POINTARRAY *ring;
|
|
LWLINE *line;
|
|
LWGEOM *lwgeom;
|
|
GSERIALIZED *result;
|
|
GBOX *bbox = NULL;
|
|
int type;
|
|
|
|
POSTGIS_DEBUG(2, "LWGEOM_interiorringn_polygon called.");
|
|
|
|
wanted_index = PG_GETARG_INT32(1);
|
|
if ( wanted_index < 1 )
|
|
{
|
|
PG_RETURN_NULL(); /* index out of range */
|
|
}
|
|
|
|
geom = PG_GETARG_GSERIALIZED_P(0);
|
|
type = gserialized_get_type(geom);
|
|
|
|
if ( (type != POLYGONTYPE) && (type != CURVEPOLYTYPE) )
|
|
{
|
|
PG_FREE_IF_COPY(geom, 0);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
lwgeom = lwgeom_from_gserialized(geom);
|
|
if( lwgeom_is_empty(lwgeom) )
|
|
{
|
|
lwpoly_free(poly);
|
|
PG_FREE_IF_COPY(geom, 0);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
if ( type == POLYGONTYPE)
|
|
{
|
|
poly = lwgeom_as_lwpoly(lwgeom_from_gserialized(geom));
|
|
|
|
/* Ok, now we have a polygon. Let's see if it has enough holes */
|
|
if ( wanted_index >= (int32)poly->nrings )
|
|
{
|
|
lwpoly_free(poly);
|
|
PG_FREE_IF_COPY(geom, 0);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
ring = poly->rings[wanted_index];
|
|
|
|
/* COMPUTE_BBOX==TAINTING */
|
|
if ( poly->bbox )
|
|
{
|
|
bbox = lwalloc(sizeof(GBOX));
|
|
ptarray_calculate_gbox_cartesian(ring, bbox);
|
|
}
|
|
|
|
/* This is a LWLINE constructed by interior ring POINTARRAY */
|
|
line = lwline_construct(poly->srid, bbox, ring);
|
|
|
|
|
|
result = geometry_serialize((LWGEOM *)line);
|
|
lwline_release(line);
|
|
lwpoly_free(poly);
|
|
}
|
|
else
|
|
{
|
|
curvepoly = lwgeom_as_lwcurvepoly(lwgeom_from_gserialized(geom));
|
|
|
|
if (wanted_index >= (int32)curvepoly->nrings)
|
|
{
|
|
PG_FREE_IF_COPY(geom, 0);
|
|
lwgeom_release((LWGEOM *)curvepoly);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
result = geometry_serialize(curvepoly->rings[wanted_index]);
|
|
lwgeom_free((LWGEOM*)curvepoly);
|
|
}
|
|
|
|
PG_FREE_IF_COPY(geom, 0);
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
/**
|
|
* PointN(GEOMETRY,INTEGER) -- find the first linestring in GEOMETRY,
|
|
* @return the point at index INTEGER (1 is 1st point). Return NULL if
|
|
* there is no LINESTRING(..) in GEOMETRY or INTEGER is out of bounds.
|
|
*/
|
|
PG_FUNCTION_INFO_V1(LWGEOM_pointn_linestring);
|
|
Datum LWGEOM_pointn_linestring(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
|
|
int where = PG_GETARG_INT32(1);
|
|
LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
|
|
LWPOINT *lwpoint = NULL;
|
|
int type = lwgeom->type;
|
|
|
|
/* If index is negative, count backward */
|
|
if( where < 1 )
|
|
{
|
|
int count = -1;
|
|
if ( type == LINETYPE || type == CIRCSTRINGTYPE || type == COMPOUNDTYPE )
|
|
count = lwgeom_count_vertices(lwgeom);
|
|
if(count >0)
|
|
{
|
|
/* only work if we found the total point number */
|
|
/* converting where to positive backward indexing, +1 because 1 indexing */
|
|
where = where + count + 1;
|
|
}
|
|
if (where < 1)
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
if ( type == LINETYPE || type == CIRCSTRINGTYPE )
|
|
{
|
|
/* OGC index starts at one, so we substract first. */
|
|
lwpoint = lwline_get_lwpoint((LWLINE*)lwgeom, where - 1);
|
|
}
|
|
else if ( type == COMPOUNDTYPE )
|
|
{
|
|
lwpoint = lwcompound_get_lwpoint((LWCOMPOUND*)lwgeom, where - 1);
|
|
}
|
|
|
|
lwgeom_free(lwgeom);
|
|
PG_FREE_IF_COPY(geom, 0);
|
|
|
|
if ( ! lwpoint )
|
|
PG_RETURN_NULL();
|
|
|
|
PG_RETURN_POINTER(geometry_serialize(lwpoint_as_lwgeom(lwpoint)));
|
|
}
|
|
|
|
/**
|
|
* X(GEOMETRY) -- return X value of the point.
|
|
* @return an error if input is not a point.
|
|
*/
|
|
PG_FUNCTION_INFO_V1(LWGEOM_x_point);
|
|
Datum LWGEOM_x_point(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
|
|
POINT4D pt;
|
|
|
|
if (gserialized_get_type(geom) != POINTTYPE)
|
|
lwpgerror("Argument to ST_X() must have type POINT");
|
|
|
|
if (gserialized_peek_first_point(geom, &pt) == LW_FAILURE)
|
|
{
|
|
PG_RETURN_NULL();
|
|
}
|
|
PG_RETURN_FLOAT8(pt.x);
|
|
}
|
|
|
|
/**
|
|
* Y(GEOMETRY) -- return Y value of the point.
|
|
* Raise an error if input is not a point.
|
|
*/
|
|
PG_FUNCTION_INFO_V1(LWGEOM_y_point);
|
|
Datum LWGEOM_y_point(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
|
|
POINT4D pt;
|
|
|
|
if (gserialized_get_type(geom) != POINTTYPE)
|
|
lwpgerror("Argument to ST_Y() must have type POINT");
|
|
|
|
if (gserialized_peek_first_point(geom, &pt) == LW_FAILURE)
|
|
{
|
|
PG_RETURN_NULL();
|
|
}
|
|
PG_RETURN_FLOAT8(pt.y);
|
|
}
|
|
|
|
/**
|
|
* Z(GEOMETRY) -- return Z value of the point.
|
|
* @return NULL if there is no Z in the point.
|
|
* Raise an error if input is not a point.
|
|
*/
|
|
PG_FUNCTION_INFO_V1(LWGEOM_z_point);
|
|
Datum LWGEOM_z_point(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
|
|
POINT4D pt;
|
|
|
|
if (gserialized_get_type(geom) != POINTTYPE)
|
|
lwpgerror("Argument to ST_Z() must have type POINT");
|
|
|
|
if (!gserialized_has_z(geom) || (gserialized_peek_first_point(geom, &pt) == LW_FAILURE))
|
|
{
|
|
PG_RETURN_NULL();
|
|
}
|
|
PG_RETURN_FLOAT8(pt.z);
|
|
}
|
|
|
|
/** M(GEOMETRY) -- find the first POINT(..) in GEOMETRY, returns its M value.
|
|
* @return NULL if there is no POINT(..) in GEOMETRY.
|
|
* Return NULL if there is no M in this geometry.
|
|
*/
|
|
PG_FUNCTION_INFO_V1(LWGEOM_m_point);
|
|
Datum LWGEOM_m_point(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
|
|
POINT4D pt;
|
|
|
|
if (gserialized_get_type(geom) != POINTTYPE)
|
|
lwpgerror("Argument to ST_M() must have type POINT");
|
|
|
|
if (!gserialized_has_m(geom) || (gserialized_peek_first_point(geom, &pt) == LW_FAILURE))
|
|
{
|
|
PG_RETURN_NULL();
|
|
}
|
|
PG_RETURN_FLOAT8(pt.m);
|
|
}
|
|
|
|
/**
|
|
* ST_StartPoint(GEOMETRY)
|
|
* @return the first point of a geometry.
|
|
*/
|
|
PG_FUNCTION_INFO_V1(LWGEOM_startpoint_linestring);
|
|
Datum LWGEOM_startpoint_linestring(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
|
|
GSERIALIZED *ret;
|
|
LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
|
|
LWGEOM *lwpoint = NULL;
|
|
POINT4D pt;
|
|
|
|
if (lwgeom_startpoint(lwgeom, &pt) == LW_FAILURE)
|
|
{
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
lwpoint = (LWGEOM *)lwpoint_make(lwgeom->srid, lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom), &pt);
|
|
ret = geometry_serialize(lwpoint);
|
|
|
|
lwgeom_free(lwgeom);
|
|
lwgeom_free(lwpoint);
|
|
|
|
PG_FREE_IF_COPY(geom, 0);
|
|
PG_RETURN_POINTER(ret);
|
|
}
|
|
|
|
/** EndPoint(GEOMETRY) -- find the first linestring in GEOMETRY,
|
|
* @return the last point.
|
|
* Return NULL if there is no LINESTRING(..) in GEOMETRY
|
|
*/
|
|
PG_FUNCTION_INFO_V1(LWGEOM_endpoint_linestring);
|
|
Datum LWGEOM_endpoint_linestring(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
|
|
LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
|
|
LWPOINT *lwpoint = NULL;
|
|
int type = lwgeom->type;
|
|
|
|
if ( type == LINETYPE || type == CIRCSTRINGTYPE )
|
|
{
|
|
LWLINE *line = (LWLINE*)lwgeom;
|
|
if ( line->points )
|
|
lwpoint = lwline_get_lwpoint((LWLINE*)lwgeom, line->points->npoints - 1);
|
|
}
|
|
else if ( type == COMPOUNDTYPE )
|
|
{
|
|
lwpoint = lwcompound_get_endpoint((LWCOMPOUND*)lwgeom);
|
|
}
|
|
|
|
lwgeom_free(lwgeom);
|
|
PG_FREE_IF_COPY(geom, 0);
|
|
|
|
if ( ! lwpoint )
|
|
PG_RETURN_NULL();
|
|
|
|
PG_RETURN_POINTER(geometry_serialize(lwpoint_as_lwgeom(lwpoint)));
|
|
}
|
|
|
|
/**
|
|
* @brief Returns a geometry Given an OGC WKT (and optionally a SRID)
|
|
* @return a geometry.
|
|
* @note Note that this is a a stricter version
|
|
* of geometry_in, where we refuse to
|
|
* accept (HEX)WKB or EWKT.
|
|
*/
|
|
PG_FUNCTION_INFO_V1(LWGEOM_from_text);
|
|
Datum LWGEOM_from_text(PG_FUNCTION_ARGS)
|
|
{
|
|
text *wkttext = PG_GETARG_TEXT_P(0);
|
|
char *wkt = text_to_cstring(wkttext);
|
|
LWGEOM_PARSER_RESULT lwg_parser_result;
|
|
GSERIALIZED *geom_result = NULL;
|
|
LWGEOM *lwgeom;
|
|
|
|
POSTGIS_DEBUG(2, "LWGEOM_from_text");
|
|
POSTGIS_DEBUGF(3, "wkt: [%s]", wkt);
|
|
|
|
if (lwgeom_parse_wkt(&lwg_parser_result, wkt, LW_PARSER_CHECK_ALL) == LW_FAILURE )
|
|
PG_PARSER_ERROR(lwg_parser_result);
|
|
|
|
lwgeom = lwg_parser_result.geom;
|
|
|
|
if ( lwgeom->srid != SRID_UNKNOWN )
|
|
{
|
|
elog(WARNING, "OGC WKT expected, EWKT provided - use GeomFromEWKT() for this");
|
|
}
|
|
|
|
/* read user-requested SRID if any */
|
|
if ( PG_NARGS() > 1 )
|
|
lwgeom_set_srid(lwgeom, PG_GETARG_INT32(1));
|
|
|
|
geom_result = geometry_serialize(lwgeom);
|
|
lwgeom_parser_result_free(&lwg_parser_result);
|
|
|
|
PG_RETURN_POINTER(geom_result);
|
|
}
|
|
|
|
/**
|
|
* Given an OGC WKB (and optionally a SRID)
|
|
* return a geometry.
|
|
*
|
|
* @note that this is a wrapper around
|
|
* lwgeom_from_wkb, where we throw
|
|
* a warning if ewkb passed in
|
|
* accept EWKB.
|
|
*/
|
|
PG_FUNCTION_INFO_V1(LWGEOM_from_WKB);
|
|
Datum LWGEOM_from_WKB(PG_FUNCTION_ARGS)
|
|
{
|
|
bytea *bytea_wkb = PG_GETARG_BYTEA_P(0);
|
|
int32 srid = 0;
|
|
GSERIALIZED *geom;
|
|
LWGEOM *lwgeom;
|
|
uint8_t *wkb = (uint8_t*)VARDATA(bytea_wkb);
|
|
|
|
lwgeom = lwgeom_from_wkb(wkb, VARSIZE_ANY_EXHDR(bytea_wkb), LW_PARSER_CHECK_ALL);
|
|
if (!lwgeom)
|
|
lwpgerror("Unable to parse WKB");
|
|
|
|
geom = geometry_serialize(lwgeom);
|
|
lwgeom_free(lwgeom);
|
|
PG_FREE_IF_COPY(bytea_wkb, 0);
|
|
|
|
if ( gserialized_get_srid(geom) != SRID_UNKNOWN )
|
|
{
|
|
elog(WARNING, "OGC WKB expected, EWKB provided - use GeometryFromEWKB() for this");
|
|
}
|
|
|
|
if ( PG_NARGS() > 1 )
|
|
{
|
|
srid = PG_GETARG_INT32(1);
|
|
if ( srid != gserialized_get_srid(geom) )
|
|
gserialized_set_srid(geom, srid);
|
|
}
|
|
|
|
PG_RETURN_POINTER(geom);
|
|
}
|
|
|
|
/** convert LWGEOM to wkt (in TEXT format) */
|
|
PG_FUNCTION_INFO_V1(LWGEOM_asText);
|
|
Datum LWGEOM_asText(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
|
|
LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
|
|
|
|
int dbl_dig_for_wkt = OUT_DEFAULT_DECIMAL_DIGITS;
|
|
if (PG_NARGS() > 1) dbl_dig_for_wkt = PG_GETARG_INT32(1);
|
|
|
|
PG_RETURN_TEXT_P(lwgeom_to_wkt_varlena(lwgeom, WKT_ISO, dbl_dig_for_wkt));
|
|
}
|
|
|
|
|
|
/** convert LWGEOM to wkb (in BINARY format) */
|
|
PG_FUNCTION_INFO_V1(LWGEOM_asBinary);
|
|
Datum LWGEOM_asBinary(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom;
|
|
LWGEOM *lwgeom;
|
|
uint8_t variant = WKB_ISO;
|
|
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_NULL();
|
|
|
|
/* Get a 2D version of the geometry */
|
|
geom = PG_GETARG_GSERIALIZED_P(0);
|
|
lwgeom = lwgeom_from_gserialized(geom);
|
|
|
|
|
|
/* If user specified endianness, respect it */
|
|
if ( (PG_NARGS()>1) && (!PG_ARGISNULL(1)) )
|
|
{
|
|
text *wkb_endian = PG_GETARG_TEXT_P(1);
|
|
|
|
if ( ! strncmp(VARDATA(wkb_endian), "xdr", 3) ||
|
|
! strncmp(VARDATA(wkb_endian), "XDR", 3) )
|
|
{
|
|
variant = variant | WKB_XDR;
|
|
}
|
|
else
|
|
{
|
|
variant = variant | WKB_NDR;
|
|
}
|
|
}
|
|
|
|
/* Write to WKB and free the geometry */
|
|
PG_RETURN_BYTEA_P(lwgeom_to_wkb_varlena(lwgeom, variant));
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* @brief IsClosed(GEOMETRY) if geometry is a linestring then returns
|
|
* startpoint == endpoint. If its not a linestring then return NULL.
|
|
* If it's a collection containing multiple linestrings,
|
|
* @return true only if all the linestrings have startpoint=endpoint.
|
|
*/
|
|
PG_FUNCTION_INFO_V1(LWGEOM_isclosed);
|
|
Datum LWGEOM_isclosed(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
|
|
LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
|
|
int closed = lwgeom_is_closed(lwgeom);
|
|
|
|
lwgeom_free(lwgeom);
|
|
PG_FREE_IF_COPY(geom, 0);
|
|
PG_RETURN_BOOL(closed);
|
|
}
|