589 lines
15 KiB
C
589 lines
15 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 2009-2011 Paul Ramsey <pramsey@cleverelephant.ca>
|
|
*
|
|
**********************************************************************/
|
|
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "../postgis_config.h"
|
|
|
|
#include <math.h>
|
|
#include <float.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include "utils/elog.h"
|
|
#include "utils/array.h"
|
|
#include "utils/builtins.h" /* for text_to_cstring */
|
|
#include "lib/stringinfo.h" /* For binary input */
|
|
#include "catalog/pg_type.h" /* for CSTRINGOID, INT4OID */
|
|
|
|
#include "liblwgeom.h" /* For standard geometry types. */
|
|
#include "liblwgeom_internal.h"
|
|
#include "lwgeom_cache.h"
|
|
#include "lwgeom_pg.h" /* For debugging macros. */
|
|
#include "geography.h" /* For utility functions. */
|
|
#include "lwgeom_transform.h"
|
|
|
|
Datum geography_in(PG_FUNCTION_ARGS);
|
|
Datum geography_out(PG_FUNCTION_ARGS);
|
|
|
|
Datum geography_as_text(PG_FUNCTION_ARGS);
|
|
Datum geography_from_text(PG_FUNCTION_ARGS);
|
|
Datum geography_as_geojson(PG_FUNCTION_ARGS);
|
|
Datum geography_as_gml(PG_FUNCTION_ARGS);
|
|
Datum geography_as_kml(PG_FUNCTION_ARGS);
|
|
Datum geography_as_svg(PG_FUNCTION_ARGS);
|
|
Datum geography_from_binary(PG_FUNCTION_ARGS);
|
|
Datum geography_from_geometry(PG_FUNCTION_ARGS);
|
|
Datum geometry_from_geography(PG_FUNCTION_ARGS);
|
|
Datum geography_send(PG_FUNCTION_ARGS);
|
|
Datum geography_recv(PG_FUNCTION_ARGS);
|
|
|
|
GSERIALIZED* gserialized_geography_from_lwgeom(LWGEOM *lwgeom, int32 geog_typmod);
|
|
|
|
/**
|
|
* The geography type only support POINT, LINESTRING, POLYGON, MULTI* variants
|
|
* of same, and GEOMETRYCOLLECTION. If the input type is not one of those, shut
|
|
* down the query.
|
|
*/
|
|
void geography_valid_type(uint8_t type)
|
|
{
|
|
if ( ! (
|
|
type == POINTTYPE ||
|
|
type == LINETYPE ||
|
|
type == POLYGONTYPE ||
|
|
type == MULTIPOINTTYPE ||
|
|
type == MULTILINETYPE ||
|
|
type == MULTIPOLYGONTYPE ||
|
|
type == COLLECTIONTYPE
|
|
) )
|
|
{
|
|
ereport(ERROR, (
|
|
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("Geography type does not support %s", lwtype_name(type) )));
|
|
|
|
}
|
|
}
|
|
|
|
GSERIALIZED* gserialized_geography_from_lwgeom(LWGEOM *lwgeom, int32 geog_typmod)
|
|
{
|
|
GSERIALIZED *g_ser = NULL;
|
|
|
|
/* Set geodetic flag */
|
|
lwgeom_set_geodetic(lwgeom, true);
|
|
|
|
/* Check that this is a type we can handle */
|
|
geography_valid_type(lwgeom->type);
|
|
|
|
/* Force the geometry to have valid geodetic coordinate range. */
|
|
lwgeom_nudge_geodetic(lwgeom);
|
|
if ( lwgeom_force_geodetic(lwgeom) == LW_TRUE )
|
|
{
|
|
ereport(NOTICE, (
|
|
errmsg_internal("Coordinate values were coerced into range [-180 -90, 180 90] for GEOGRAPHY" ))
|
|
);
|
|
}
|
|
|
|
/* Force default SRID to the default */
|
|
if ( (int)lwgeom->srid <= 0 )
|
|
lwgeom->srid = SRID_DEFAULT;
|
|
|
|
/*
|
|
** Serialize our lwgeom and set the geodetic flag so subsequent
|
|
** functions do the right thing.
|
|
*/
|
|
g_ser = geography_serialize(lwgeom);
|
|
|
|
/* Check for typmod agreement */
|
|
if ( geog_typmod >= 0 )
|
|
{
|
|
g_ser = postgis_valid_typmod(g_ser, geog_typmod);
|
|
POSTGIS_DEBUG(3, "typmod and geometry were consistent");
|
|
}
|
|
else
|
|
{
|
|
POSTGIS_DEBUG(3, "typmod was -1");
|
|
}
|
|
|
|
return g_ser;
|
|
}
|
|
|
|
|
|
/*
|
|
** geography_in(cstring) returns *GSERIALIZED
|
|
*/
|
|
PG_FUNCTION_INFO_V1(geography_in);
|
|
Datum geography_in(PG_FUNCTION_ARGS)
|
|
{
|
|
char *str = PG_GETARG_CSTRING(0);
|
|
/* Datum geog_oid = PG_GETARG_OID(1); Not needed. */
|
|
int32 geog_typmod = -1;
|
|
LWGEOM_PARSER_RESULT lwg_parser_result;
|
|
LWGEOM *lwgeom = NULL;
|
|
GSERIALIZED *g_ser = NULL;
|
|
|
|
if ( (PG_NARGS()>2) && (!PG_ARGISNULL(2)) ) {
|
|
geog_typmod = PG_GETARG_INT32(2);
|
|
}
|
|
|
|
lwgeom_parser_result_init(&lwg_parser_result);
|
|
|
|
/* Empty string. */
|
|
if ( str[0] == '\0' )
|
|
ereport(ERROR,(errmsg("parse error - invalid geometry")));
|
|
|
|
/* WKB? Let's find out. */
|
|
if ( str[0] == '0' )
|
|
{
|
|
/* TODO: 20101206: No parser checks! This is inline with current 1.5 behavior, but needs discussion */
|
|
lwgeom = lwgeom_from_hexwkb(str, LW_PARSER_CHECK_NONE);
|
|
/* Error out if something went sideways */
|
|
if ( ! lwgeom )
|
|
ereport(ERROR,(errmsg("parse error - invalid geometry")));
|
|
}
|
|
/* WKT then. */
|
|
else
|
|
{
|
|
if ( lwgeom_parse_wkt(&lwg_parser_result, str, LW_PARSER_CHECK_ALL) == LW_FAILURE )
|
|
PG_PARSER_ERROR(lwg_parser_result);
|
|
|
|
lwgeom = lwg_parser_result.geom;
|
|
}
|
|
|
|
/* Error on any SRID != default */
|
|
srid_check_latlong(lwgeom->srid);
|
|
|
|
/* Convert to gserialized */
|
|
g_ser = gserialized_geography_from_lwgeom(lwgeom, geog_typmod);
|
|
|
|
/* Clean up temporary object */
|
|
lwgeom_free(lwgeom);
|
|
|
|
|
|
PG_RETURN_POINTER(g_ser);
|
|
}
|
|
|
|
/*
|
|
** geography_out(*GSERIALIZED) returns cstring
|
|
*/
|
|
PG_FUNCTION_INFO_V1(geography_out);
|
|
Datum geography_out(PG_FUNCTION_ARGS)
|
|
{
|
|
|
|
GSERIALIZED *g = PG_GETARG_GSERIALIZED_P(0);
|
|
LWGEOM *lwgeom = lwgeom_from_gserialized(g);
|
|
PG_RETURN_CSTRING(lwgeom_to_hexwkb_buffer(lwgeom, WKB_EXTENDED));
|
|
}
|
|
|
|
|
|
/*
|
|
** geography_as_gml(*GSERIALIZED) returns text
|
|
*/
|
|
PG_FUNCTION_INFO_V1(geography_as_gml);
|
|
Datum geography_as_gml(PG_FUNCTION_ARGS)
|
|
{
|
|
LWGEOM *lwgeom = NULL;
|
|
GSERIALIZED *g = NULL;
|
|
lwvarlena_t *v;
|
|
int version;
|
|
const char *srs;
|
|
int32_t srid = SRID_DEFAULT;
|
|
int precision = -1;
|
|
int option = 0;
|
|
int lwopts = LW_GML_IS_DIMS;
|
|
static const char *default_prefix = "gml:";
|
|
const char *prefix = default_prefix;
|
|
char *prefix_buf = "";
|
|
text *prefix_text, *id_text = NULL;
|
|
const char *id = NULL;
|
|
char *id_buf;
|
|
|
|
/*
|
|
* Two potential callers, one starts with GML version,
|
|
* one starts with geography, and we check for initial
|
|
* argument type and then dynamically change what args
|
|
* we read based on presence/absence
|
|
*/
|
|
Oid first_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
|
|
int argnum = 0;
|
|
if (first_type != INT4OID)
|
|
{
|
|
version = 2;
|
|
}
|
|
else
|
|
{
|
|
/* Get the version */
|
|
version = PG_GETARG_INT32(argnum++);
|
|
if (version != 2 && version != 3)
|
|
{
|
|
elog(ERROR, "Only GML 2 and GML 3 are supported");
|
|
PG_RETURN_NULL();
|
|
}
|
|
}
|
|
|
|
/* Get the parameters, both callers have same order */
|
|
g = PG_GETARG_GSERIALIZED_P(argnum++);
|
|
precision = PG_GETARG_INT32(argnum++);
|
|
option = PG_GETARG_INT32(argnum++);
|
|
prefix_text = PG_GETARG_TEXT_P(argnum++);
|
|
id_text = PG_GETARG_TEXT_P(argnum++);
|
|
|
|
/* Convert to lwgeom so we can run the old functions */
|
|
lwgeom = lwgeom_from_gserialized(g);
|
|
|
|
/* Condition the prefix argument */
|
|
if (VARSIZE_ANY_EXHDR(prefix_text) > 0)
|
|
{
|
|
/* +2 is one for the ':' and one for term null */
|
|
prefix_buf = palloc(VARSIZE_ANY_EXHDR(prefix_text)+2);
|
|
memcpy(prefix_buf, VARDATA_ANY(prefix_text),
|
|
VARSIZE_ANY_EXHDR(prefix_text));
|
|
/* add colon and null terminate */
|
|
prefix_buf[VARSIZE_ANY_EXHDR(prefix_text)] = ':';
|
|
prefix_buf[VARSIZE_ANY_EXHDR(prefix_text)+1] = '\0';
|
|
prefix = prefix_buf;
|
|
}
|
|
else
|
|
{
|
|
prefix = "";
|
|
}
|
|
|
|
if (VARSIZE_ANY_EXHDR(id_text) > 0)
|
|
{
|
|
id_buf = palloc(VARSIZE_ANY_EXHDR(id_text)+1);
|
|
memcpy(id_buf, VARDATA(id_text), VARSIZE_ANY_EXHDR(id_text));
|
|
prefix_buf[VARSIZE_ANY_EXHDR(id_text)+1] = '\0';
|
|
id = id_buf;
|
|
}
|
|
|
|
if (option & 1)
|
|
srs = GetSRSCacheBySRID(fcinfo, srid, false);
|
|
else
|
|
srs = GetSRSCacheBySRID(fcinfo, srid, true);
|
|
if (!srs)
|
|
{
|
|
elog(ERROR, "SRID %d unknown in spatial_ref_sys table", SRID_DEFAULT);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
/* Revert lat/lon only with long SRS */
|
|
if (option & 1) lwopts |= LW_GML_IS_DEGREE;
|
|
if (option & 2) lwopts &= ~LW_GML_IS_DIMS;
|
|
if (option & 8)
|
|
{
|
|
elog(ERROR,
|
|
"Options %d passed to ST_AsGML(geography) sets "
|
|
"unsupported value 8",
|
|
option);
|
|
PG_RETURN_NULL();
|
|
}
|
|
if ((option & 4) || (option & 16) || (option & 32))
|
|
{
|
|
elog(ERROR,
|
|
"Options %d passed to ST_AsGML(geography) but are only "
|
|
"applicable to ST_AsGML(geometry)",
|
|
option);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
if (version == 2)
|
|
v = lwgeom_to_gml2(lwgeom, srs, precision, prefix);
|
|
else
|
|
v = lwgeom_to_gml3(lwgeom, srs, precision, lwopts, prefix, id);
|
|
|
|
if (!v)
|
|
PG_RETURN_NULL();
|
|
else
|
|
PG_RETURN_TEXT_P(v);
|
|
}
|
|
|
|
|
|
/*
|
|
** geography_as_kml(*GSERIALIZED) returns text
|
|
*/
|
|
PG_FUNCTION_INFO_V1(geography_as_kml);
|
|
Datum geography_as_kml(PG_FUNCTION_ARGS)
|
|
{
|
|
|
|
lwvarlena_t *kml;
|
|
static const char *default_prefix = "";
|
|
char *prefixbuf;
|
|
const char *prefix = default_prefix;
|
|
GSERIALIZED *g = PG_GETARG_GSERIALIZED_P(0);
|
|
int precision = PG_GETARG_INT32(1);
|
|
text *prefix_text = PG_GETARG_TEXT_P(2);
|
|
LWGEOM *lwgeom = lwgeom_from_gserialized(g);
|
|
|
|
/* Condition the precision */
|
|
if (precision < 0)
|
|
precision = 0;
|
|
|
|
if (VARSIZE_ANY_EXHDR(prefix_text) > 0)
|
|
{
|
|
/* +2 is one for the ':' and one for term null */
|
|
prefixbuf = palloc(VARSIZE_ANY_EXHDR(prefix_text)+2);
|
|
memcpy(prefixbuf, VARDATA(prefix_text),
|
|
VARSIZE_ANY_EXHDR(prefix_text));
|
|
/* add colon and null terminate */
|
|
prefixbuf[VARSIZE_ANY_EXHDR(prefix_text)] = ':';
|
|
prefixbuf[VARSIZE_ANY_EXHDR(prefix_text)+1] = '\0';
|
|
prefix = prefixbuf;
|
|
}
|
|
else
|
|
{
|
|
prefix = "";
|
|
}
|
|
|
|
kml = lwgeom_to_kml2(lwgeom, precision, prefix);
|
|
if (kml)
|
|
PG_RETURN_TEXT_P(kml);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
|
|
/*
|
|
** geography_as_svg(*GSERIALIZED) returns text
|
|
*/
|
|
PG_FUNCTION_INFO_V1(geography_as_svg);
|
|
Datum geography_as_svg(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *g = PG_GETARG_GSERIALIZED_P(0);
|
|
int relative = PG_GETARG_INT32(1) ? 1 : 0;
|
|
int precision = PG_GETARG_INT32(2);
|
|
LWGEOM *lwgeom = lwgeom_from_gserialized(g);
|
|
|
|
if (precision < 0)
|
|
precision = 0;
|
|
|
|
PG_RETURN_TEXT_P(lwgeom_to_svg(lwgeom, precision, relative));
|
|
}
|
|
|
|
|
|
/*
|
|
** geography_as_geojson(*GSERIALIZED) returns text
|
|
*/
|
|
PG_FUNCTION_INFO_V1(geography_as_geojson);
|
|
Datum geography_as_geojson(PG_FUNCTION_ARGS)
|
|
{
|
|
lwvarlena_t *geojson;
|
|
int has_bbox = 0;
|
|
const char *srs = NULL;
|
|
GSERIALIZED *g = PG_GETARG_GSERIALIZED_P(0);
|
|
int precision = PG_GETARG_INT32(1);
|
|
int option = PG_GETARG_INT32(2);
|
|
LWGEOM *lwgeom = lwgeom_from_gserialized(g);
|
|
|
|
if (precision < 0)
|
|
precision = 0;
|
|
|
|
/* Retrieve output option
|
|
* 0 = without option (default)
|
|
* 1 = bbox
|
|
* 2 = short crs
|
|
* 4 = long crs
|
|
*/
|
|
|
|
if (option & 2 || option & 4)
|
|
{
|
|
/* Geography only handle srid SRID_DEFAULT */
|
|
if (option & 2)
|
|
srs = GetSRSCacheBySRID(fcinfo, SRID_DEFAULT, true);
|
|
if (option & 4)
|
|
srs = GetSRSCacheBySRID(fcinfo, SRID_DEFAULT, false);
|
|
|
|
if (!srs)
|
|
{
|
|
elog(ERROR, "SRID SRID_DEFAULT unknown in spatial_ref_sys table");
|
|
PG_RETURN_NULL();
|
|
}
|
|
}
|
|
|
|
if (option & 1) has_bbox = 1;
|
|
|
|
geojson = lwgeom_to_geojson(lwgeom, srs, precision, has_bbox);
|
|
lwgeom_free(lwgeom);
|
|
PG_FREE_IF_COPY(g, 0);
|
|
|
|
PG_RETURN_TEXT_P(geojson);
|
|
}
|
|
|
|
|
|
/*
|
|
** geography_from_text(*char) returns *GSERIALIZED
|
|
**
|
|
** Convert text (varlena) to cstring and then call geography_in().
|
|
*/
|
|
PG_FUNCTION_INFO_V1(geography_from_text);
|
|
Datum geography_from_text(PG_FUNCTION_ARGS)
|
|
{
|
|
LWGEOM_PARSER_RESULT lwg_parser_result;
|
|
GSERIALIZED *g_ser = NULL;
|
|
text *wkt_text = PG_GETARG_TEXT_P(0);
|
|
|
|
/* Extract the cstring from the varlena */
|
|
char *wkt = text_to_cstring(wkt_text);
|
|
|
|
/* Pass the cstring to the input parser, and magic occurs! */
|
|
if ( lwgeom_parse_wkt(&lwg_parser_result, wkt, LW_PARSER_CHECK_ALL) == LW_FAILURE )
|
|
PG_PARSER_ERROR(lwg_parser_result);
|
|
|
|
/* Error on any SRID != default */
|
|
srid_check_latlong(lwg_parser_result.geom->srid);
|
|
|
|
/* Clean up string */
|
|
pfree(wkt);
|
|
g_ser = gserialized_geography_from_lwgeom(lwg_parser_result.geom, -1);
|
|
|
|
/* Clean up temporary object */
|
|
lwgeom_free(lwg_parser_result.geom);
|
|
|
|
PG_RETURN_POINTER(g_ser);
|
|
}
|
|
|
|
/*
|
|
** geography_from_binary(*char) returns *GSERIALIZED
|
|
*/
|
|
PG_FUNCTION_INFO_V1(geography_from_binary);
|
|
Datum geography_from_binary(PG_FUNCTION_ARGS)
|
|
{
|
|
char *wkb_bytea = (char*)PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
|
GSERIALIZED *gser = NULL;
|
|
size_t wkb_size = VARSIZE(wkb_bytea);
|
|
uint8_t *wkb = (uint8_t*)VARDATA(wkb_bytea);
|
|
LWGEOM *lwgeom = lwgeom_from_wkb(wkb, wkb_size, LW_PARSER_CHECK_NONE);
|
|
|
|
if ( ! lwgeom )
|
|
lwpgerror("Unable to parse WKB");
|
|
|
|
/* Error on any SRID != default */
|
|
srid_check_latlong(lwgeom->srid);
|
|
|
|
gser = gserialized_geography_from_lwgeom(lwgeom, -1);
|
|
lwgeom_free(lwgeom);
|
|
PG_RETURN_POINTER(gser);
|
|
}
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(geography_from_geometry);
|
|
Datum geography_from_geometry(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *g_ser = NULL;
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P_COPY(0);
|
|
LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
|
|
|
|
geography_valid_type(lwgeom_get_type(lwgeom));
|
|
|
|
/* Force default SRID */
|
|
if ( (int)lwgeom->srid <= 0 )
|
|
{
|
|
lwgeom->srid = SRID_DEFAULT;
|
|
}
|
|
|
|
/* Error on any SRID != default */
|
|
srid_check_latlong(lwgeom->srid);
|
|
|
|
/* Force the geometry to have valid geodetic coordinate range. */
|
|
lwgeom_nudge_geodetic(lwgeom);
|
|
if ( lwgeom_force_geodetic(lwgeom) == LW_TRUE )
|
|
{
|
|
ereport(NOTICE, (
|
|
errmsg_internal("Coordinate values were coerced into range [-180 -90, 180 90] for GEOGRAPHY" ))
|
|
);
|
|
}
|
|
|
|
/* force recalculate of box by dropping */
|
|
lwgeom_drop_bbox(lwgeom);
|
|
|
|
lwgeom_set_geodetic(lwgeom, true);
|
|
/* We are trusting geography_serialize will add a box if needed */
|
|
g_ser = geography_serialize(lwgeom);
|
|
|
|
|
|
lwgeom_free(lwgeom);
|
|
|
|
PG_FREE_IF_COPY(geom, 0);
|
|
PG_RETURN_POINTER(g_ser);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(geometry_from_geography);
|
|
Datum geometry_from_geography(PG_FUNCTION_ARGS)
|
|
{
|
|
LWGEOM *lwgeom = NULL;
|
|
GSERIALIZED *ret = NULL;
|
|
GSERIALIZED *g_ser = PG_GETARG_GSERIALIZED_P(0);
|
|
|
|
lwgeom = lwgeom_from_gserialized(g_ser);
|
|
|
|
/* Recalculate the boxes after re-setting the geodetic bit */
|
|
lwgeom_set_geodetic(lwgeom, false);
|
|
lwgeom_refresh_bbox(lwgeom);
|
|
|
|
/* We want "geometry" to think all our "geography" has an SRID, and the
|
|
implied SRID is the default, so we fill that in if our SRID is actually unknown. */
|
|
if ( (int)lwgeom->srid <= 0 )
|
|
lwgeom->srid = SRID_DEFAULT;
|
|
|
|
ret = geometry_serialize(lwgeom);
|
|
lwgeom_free(lwgeom);
|
|
|
|
PG_RETURN_POINTER(ret);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(geography_recv);
|
|
Datum geography_recv(PG_FUNCTION_ARGS)
|
|
{
|
|
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
|
int32 geog_typmod = -1;
|
|
LWGEOM *lwgeom = NULL;
|
|
GSERIALIZED *g_ser = NULL;
|
|
|
|
if ( (PG_NARGS()>2) && (!PG_ARGISNULL(2)) ) {
|
|
geog_typmod = PG_GETARG_INT32(2);
|
|
}
|
|
|
|
lwgeom = lwgeom_from_wkb((uint8_t*)buf->data, buf->len, LW_PARSER_CHECK_ALL);
|
|
|
|
/* Error on any SRID != default */
|
|
srid_check_latlong(lwgeom->srid);
|
|
|
|
g_ser = gserialized_geography_from_lwgeom(lwgeom, geog_typmod);
|
|
|
|
/* Clean up temporary object */
|
|
lwgeom_free(lwgeom);
|
|
|
|
/* Set cursor to the end of buffer (so the backend is happy) */
|
|
buf->cursor = buf->len;
|
|
|
|
PG_RETURN_POINTER(g_ser);
|
|
}
|
|
|
|
|
|
PG_FUNCTION_INFO_V1(geography_send);
|
|
Datum geography_send(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *g = PG_GETARG_GSERIALIZED_P(0);
|
|
LWGEOM *lwgeom = lwgeom_from_gserialized(g);
|
|
PG_RETURN_POINTER(lwgeom_to_wkb_varlena(lwgeom, WKB_EXTENDED));
|
|
}
|