937 lines
23 KiB
C
937 lines
23 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 Mark Cave-Ayland <mark.cave-ayland@siriusit.co.uk>
|
|
* Copyright 2009-2017 Paul Ramsey <pramsey@cleverelephant.ca>
|
|
* Copyright 2018 Darafei Praliaskouski <me@komzpa.net>
|
|
*
|
|
**********************************************************************/
|
|
|
|
#include "postgres.h"
|
|
#include "fmgr.h"
|
|
#include "utils/elog.h"
|
|
#include "utils/geo_decls.h"
|
|
#include "gserialized_spgist_3d.h"
|
|
|
|
#include "../postgis_config.h"
|
|
#include "lwgeom_pg.h"
|
|
#include "liblwgeom.h"
|
|
#include "liblwgeom_internal.h"
|
|
#include "lwgeom_box3d.h"
|
|
|
|
#include <math.h>
|
|
#include <float.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
|
|
/**
|
|
* BOX3D_in - takes a string rep of BOX3D and returns internal rep
|
|
*
|
|
* example:
|
|
* "BOX3D(x1 y1 z1,x2 y2 z2)"
|
|
* or "BOX3D(x1 y1,x2 y2)" z1 and z2 = 0.0
|
|
*
|
|
*/
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_in);
|
|
Datum BOX3D_in(PG_FUNCTION_ARGS)
|
|
{
|
|
char *str = PG_GETARG_CSTRING(0);
|
|
int nitems;
|
|
BOX3D *box = (BOX3D *)palloc(sizeof(BOX3D));
|
|
box->zmin = 0;
|
|
box->zmax = 0;
|
|
|
|
if (strstr(str, "BOX3D(") != str)
|
|
{
|
|
pfree(box);
|
|
elog(ERROR, "BOX3D parser - doesn't start with BOX3D(");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
nitems = sscanf(str,
|
|
"BOX3D(%le %le %le ,%le %le %le)",
|
|
&box->xmin,
|
|
&box->ymin,
|
|
&box->zmin,
|
|
&box->xmax,
|
|
&box->ymax,
|
|
&box->zmax);
|
|
if (nitems != 6)
|
|
{
|
|
nitems = sscanf(str, "BOX3D(%le %le ,%le %le)", &box->xmin, &box->ymin, &box->xmax, &box->ymax);
|
|
if (nitems != 4)
|
|
{
|
|
pfree(box);
|
|
elog(
|
|
ERROR,
|
|
"BOX3D parser - couldn't parse. It should look like: BOX3D(xmin ymin zmin,xmax ymax zmax) or BOX3D(xmin ymin,xmax ymax)");
|
|
PG_RETURN_NULL();
|
|
}
|
|
}
|
|
|
|
if (box->xmin > box->xmax)
|
|
{
|
|
float tmp = box->xmin;
|
|
box->xmin = box->xmax;
|
|
box->xmax = tmp;
|
|
}
|
|
if (box->ymin > box->ymax)
|
|
{
|
|
float tmp = box->ymin;
|
|
box->ymin = box->ymax;
|
|
box->ymax = tmp;
|
|
}
|
|
if (box->zmin > box->zmax)
|
|
{
|
|
float tmp = box->zmin;
|
|
box->zmin = box->zmax;
|
|
box->zmax = tmp;
|
|
}
|
|
box->srid = SRID_UNKNOWN;
|
|
PG_RETURN_POINTER(box);
|
|
}
|
|
|
|
/**
|
|
* Takes an internal rep of a BOX3D and returns a string rep.
|
|
*
|
|
* example:
|
|
* "BOX3D(xmin ymin zmin, xmin ymin zmin)"
|
|
*/
|
|
PG_FUNCTION_INFO_V1(BOX3D_out);
|
|
Datum BOX3D_out(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *bbox = (BOX3D *)PG_GETARG_POINTER(0);
|
|
static const int precision = 15;
|
|
static int size = OUT_MAX_BYTES_DOUBLE * 6 + 5 + 2 + 4 + 5 + 1; /* double * 6 + "BOX3D"+ "()" + commas + null */
|
|
int i = 0;
|
|
char *result;
|
|
|
|
if (bbox == NULL)
|
|
{
|
|
result = palloc(5);
|
|
strcat(result, "NULL");
|
|
PG_RETURN_CSTRING(result);
|
|
}
|
|
|
|
result = (char *)palloc(size);
|
|
result[i++] = 'B';
|
|
result[i++] = 'O';
|
|
result[i++] = 'X';
|
|
result[i++] = '3';
|
|
result[i++] = 'D';
|
|
result[i++] = '(';
|
|
i += lwprint_double(bbox->xmin, precision, &result[i]);
|
|
result[i++] = ' ';
|
|
i += lwprint_double(bbox->ymin, precision, &result[i]);
|
|
result[i++] = ' ';
|
|
i += lwprint_double(bbox->zmin, precision, &result[i]);
|
|
result[i++] = ',';
|
|
i += lwprint_double(bbox->xmax, precision, &result[i]);
|
|
result[i++] = ' ';
|
|
i += lwprint_double(bbox->ymax, precision, &result[i]);
|
|
result[i++] = ' ';
|
|
i += lwprint_double(bbox->zmax, precision, &result[i]);
|
|
result[i++] = ')';
|
|
result[i++] = '\0';
|
|
|
|
PG_RETURN_CSTRING(result);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_to_BOX2D);
|
|
Datum BOX3D_to_BOX2D(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *in = (BOX3D *)PG_GETARG_POINTER(0);
|
|
GBOX *out = box3d_to_gbox(in);
|
|
PG_RETURN_POINTER(out);
|
|
}
|
|
|
|
static void
|
|
box3d_to_box_p(BOX3D *box, BOX *out)
|
|
{
|
|
if (!box)
|
|
return;
|
|
|
|
out->low.x = box->xmin;
|
|
out->low.y = box->ymin;
|
|
|
|
out->high.x = box->xmax;
|
|
out->high.y = box->ymax;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_to_BOX);
|
|
Datum BOX3D_to_BOX(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *in = (BOX3D *)PG_GETARG_POINTER(0);
|
|
BOX *box = palloc(sizeof(BOX));
|
|
|
|
box3d_to_box_p(in, box);
|
|
PG_RETURN_POINTER(box);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_to_LWGEOM);
|
|
Datum BOX3D_to_LWGEOM(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
|
|
POINTARRAY *pa;
|
|
GSERIALIZED *result;
|
|
POINT4D pt;
|
|
|
|
/**
|
|
* Alter BOX3D cast so that a valid geometry is always
|
|
* returned depending upon the size of the BOX3D. The
|
|
* code makes the following assumptions:
|
|
* - If the BOX3D is a single point then return a POINT geometry
|
|
* - If the BOX3D represents a line in any of X, Y or Z dimension,
|
|
* return a LINESTRING geometry
|
|
* - If the BOX3D represents a plane in the X, Y, or Z dimension,
|
|
* return a POLYGON geometry
|
|
* - Otherwise return a POLYHEDRALSURFACE geometry
|
|
*/
|
|
|
|
pa = ptarray_construct_empty(LW_TRUE, LW_FALSE, 5);
|
|
|
|
/* BOX3D is a point */
|
|
if ((box->xmin == box->xmax) && (box->ymin == box->ymax) && (box->zmin == box->zmax))
|
|
{
|
|
LWPOINT *lwpt = lwpoint_construct(SRID_UNKNOWN, NULL, pa);
|
|
|
|
pt.x = box->xmin;
|
|
pt.y = box->ymin;
|
|
pt.z = box->zmin;
|
|
ptarray_append_point(pa, &pt, LW_TRUE);
|
|
|
|
result = geometry_serialize(lwpoint_as_lwgeom(lwpt));
|
|
lwpoint_free(lwpt);
|
|
}
|
|
/* BOX3D is a line */
|
|
else if (((box->xmin == box->xmax || box->ymin == box->ymax) && box->zmin == box->zmax) ||
|
|
((box->xmin == box->xmax || box->zmin == box->zmax) && box->ymin == box->ymax) ||
|
|
((box->ymin == box->ymax || box->zmin == box->zmax) && box->xmin == box->xmax))
|
|
{
|
|
LWLINE *lwline = lwline_construct(SRID_UNKNOWN, NULL, pa);
|
|
|
|
pt.x = box->xmin;
|
|
pt.y = box->ymin;
|
|
pt.z = box->zmin;
|
|
ptarray_append_point(pa, &pt, LW_TRUE);
|
|
pt.x = box->xmax;
|
|
pt.y = box->ymax;
|
|
pt.z = box->zmax;
|
|
ptarray_append_point(pa, &pt, LW_TRUE);
|
|
|
|
result = geometry_serialize(lwline_as_lwgeom(lwline));
|
|
lwline_free(lwline);
|
|
}
|
|
/* BOX3D is a polygon in the X plane */
|
|
else if (box->xmin == box->xmax)
|
|
{
|
|
POINT4D points[4];
|
|
LWPOLY *lwpoly;
|
|
|
|
/* Initialize the 4 vertices of the polygon */
|
|
points[0] = (POINT4D){box->xmin, box->ymin, box->zmin, 0.0};
|
|
points[1] = (POINT4D){box->xmin, box->ymax, box->zmin, 0.0};
|
|
points[2] = (POINT4D){box->xmin, box->ymax, box->zmax, 0.0};
|
|
points[3] = (POINT4D){box->xmin, box->ymin, box->zmax, 0.0};
|
|
|
|
lwpoly = lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[0], &points[1], &points[2], &points[3]);
|
|
result = geometry_serialize(lwpoly_as_lwgeom(lwpoly));
|
|
lwpoly_free(lwpoly);
|
|
}
|
|
/* BOX3D is a polygon in the Y plane */
|
|
else if (box->ymin == box->ymax)
|
|
{
|
|
POINT4D points[4];
|
|
LWPOLY *lwpoly;
|
|
|
|
/* Initialize the 4 vertices of the polygon */
|
|
points[0] = (POINT4D){box->xmin, box->ymin, box->zmin, 0.0};
|
|
points[1] = (POINT4D){box->xmax, box->ymin, box->zmin, 0.0};
|
|
points[2] = (POINT4D){box->xmax, box->ymin, box->zmax, 0.0};
|
|
points[3] = (POINT4D){box->xmin, box->ymin, box->zmax, 0.0};
|
|
|
|
lwpoly = lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[0], &points[1], &points[2], &points[3]);
|
|
result = geometry_serialize(lwpoly_as_lwgeom(lwpoly));
|
|
lwpoly_free(lwpoly);
|
|
}
|
|
/* BOX3D is a polygon in the Z plane */
|
|
else if (box->zmin == box->zmax)
|
|
{
|
|
POINT4D points[4];
|
|
LWPOLY *lwpoly;
|
|
|
|
/* Initialize the 4 vertices of the polygon */
|
|
points[0] = (POINT4D){box->xmin, box->ymin, box->zmin, 0.0};
|
|
points[1] = (POINT4D){box->xmin, box->ymax, box->zmin, 0.0};
|
|
points[2] = (POINT4D){box->xmax, box->ymax, box->zmin, 0.0};
|
|
points[3] = (POINT4D){box->xmax, box->ymin, box->zmin, 0.0};
|
|
|
|
lwpoly = lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[0], &points[1], &points[2], &points[3]);
|
|
result = geometry_serialize(lwpoly_as_lwgeom(lwpoly));
|
|
lwpoly_free(lwpoly);
|
|
}
|
|
/* BOX3D is a polyhedron */
|
|
else
|
|
{
|
|
POINT4D points[8];
|
|
static const int ngeoms = 6;
|
|
LWGEOM **geoms = (LWGEOM **)lwalloc(sizeof(LWGEOM *) * ngeoms);
|
|
LWGEOM *geom = NULL;
|
|
|
|
/* Initialize the 8 vertices of the box */
|
|
points[0] = (POINT4D){box->xmin, box->ymin, box->zmin, 0.0};
|
|
points[1] = (POINT4D){box->xmin, box->ymax, box->zmin, 0.0};
|
|
points[2] = (POINT4D){box->xmax, box->ymax, box->zmin, 0.0};
|
|
points[3] = (POINT4D){box->xmax, box->ymin, box->zmin, 0.0};
|
|
points[4] = (POINT4D){box->xmin, box->ymin, box->zmax, 0.0};
|
|
points[5] = (POINT4D){box->xmin, box->ymax, box->zmax, 0.0};
|
|
points[6] = (POINT4D){box->xmax, box->ymax, box->zmax, 0.0};
|
|
points[7] = (POINT4D){box->xmax, box->ymin, box->zmax, 0.0};
|
|
|
|
/* add bottom polygon */
|
|
geoms[0] = lwpoly_as_lwgeom(
|
|
lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[0], &points[1], &points[2], &points[3]));
|
|
/* add top polygon */
|
|
geoms[1] = lwpoly_as_lwgeom(
|
|
lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[4], &points[7], &points[6], &points[5]));
|
|
/* add left polygon */
|
|
geoms[2] = lwpoly_as_lwgeom(
|
|
lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[0], &points[4], &points[5], &points[1]));
|
|
/* add right polygon */
|
|
geoms[3] = lwpoly_as_lwgeom(
|
|
lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[3], &points[2], &points[6], &points[7]));
|
|
/* add front polygon */
|
|
geoms[4] = lwpoly_as_lwgeom(
|
|
lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[0], &points[3], &points[7], &points[4]));
|
|
/* add back polygon */
|
|
geoms[5] = lwpoly_as_lwgeom(
|
|
lwpoly_construct_rectangle(LW_TRUE, LW_FALSE, &points[1], &points[5], &points[6], &points[2]));
|
|
|
|
geom = (LWGEOM *)lwcollection_construct(POLYHEDRALSURFACETYPE, SRID_UNKNOWN, NULL, ngeoms, geoms);
|
|
|
|
FLAGS_SET_SOLID(geom->flags, 1);
|
|
|
|
result = geometry_serialize(geom);
|
|
lwcollection_free((LWCOLLECTION *)geom);
|
|
}
|
|
|
|
gserialized_set_srid(result, box->srid);
|
|
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
/** Expand given box of 'd' units in all directions */
|
|
void
|
|
expand_box3d(BOX3D *box, double d)
|
|
{
|
|
box->xmin -= d;
|
|
box->ymin -= d;
|
|
box->zmin -= d;
|
|
|
|
box->xmax += d;
|
|
box->ymax += d;
|
|
box->zmax += d;
|
|
}
|
|
|
|
static void
|
|
expand_box3d_xyz(BOX3D *box, double dx, double dy, double dz)
|
|
{
|
|
box->xmin -= dx;
|
|
box->xmax += dx;
|
|
box->ymin -= dy;
|
|
box->ymax += dy;
|
|
box->zmin -= dz;
|
|
box->zmax += dz;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_expand);
|
|
Datum BOX3D_expand(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
|
|
BOX3D *result = (BOX3D *)palloc(sizeof(BOX3D));
|
|
memcpy(result, box, sizeof(BOX3D));
|
|
|
|
if (PG_NARGS() == 2)
|
|
{
|
|
/* Expand the box the same amount in all directions */
|
|
double d = PG_GETARG_FLOAT8(1);
|
|
expand_box3d(result, d);
|
|
}
|
|
else
|
|
{
|
|
double dx = PG_GETARG_FLOAT8(1);
|
|
double dy = PG_GETARG_FLOAT8(2);
|
|
double dz = PG_GETARG_FLOAT8(3);
|
|
|
|
expand_box3d_xyz(result, dx, dy, dz);
|
|
}
|
|
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
/**
|
|
* convert a GSERIALIZED to BOX3D
|
|
*
|
|
* NOTE: the bounding box is always recomputed as the cache is a BOX2D, not a BOX3D.
|
|
*
|
|
*/
|
|
PG_FUNCTION_INFO_V1(LWGEOM_to_BOX3D);
|
|
Datum LWGEOM_to_BOX3D(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0);
|
|
LWGEOM *lwgeom = lwgeom_from_gserialized(geom);
|
|
GBOX gbox;
|
|
BOX3D *result;
|
|
int rv = lwgeom_calculate_gbox(lwgeom, &gbox);
|
|
|
|
if (rv == LW_FAILURE)
|
|
PG_RETURN_NULL();
|
|
|
|
result = box3d_from_gbox(&gbox);
|
|
result->srid = lwgeom->srid;
|
|
|
|
lwgeom_free(lwgeom);
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_xmin);
|
|
Datum BOX3D_xmin(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
|
|
PG_RETURN_FLOAT8(Min(box->xmin, box->xmax));
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_ymin);
|
|
Datum BOX3D_ymin(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
|
|
PG_RETURN_FLOAT8(Min(box->ymin, box->ymax));
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_zmin);
|
|
Datum BOX3D_zmin(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
|
|
PG_RETURN_FLOAT8(Min(box->zmin, box->zmax));
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_xmax);
|
|
Datum BOX3D_xmax(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
|
|
PG_RETURN_FLOAT8(Max(box->xmin, box->xmax));
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_ymax);
|
|
Datum BOX3D_ymax(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
|
|
PG_RETURN_FLOAT8(Max(box->ymin, box->ymax));
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_zmax);
|
|
Datum BOX3D_zmax(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
|
|
PG_RETURN_FLOAT8(Max(box->zmin, box->zmax));
|
|
}
|
|
|
|
/**
|
|
* Used in the ST_Extent and ST_Extent3D aggregates, does not read the
|
|
* serialized cached bounding box (since that is floating point)
|
|
* but calculates the box in full from the underlying geometry.
|
|
*/
|
|
PG_FUNCTION_INFO_V1(BOX3D_combine);
|
|
Datum BOX3D_combine(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box = (BOX3D *)PG_GETARG_POINTER(0);
|
|
GSERIALIZED *geom = PG_ARGISNULL(1) ? NULL : (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
|
LWGEOM *lwgeom = NULL;
|
|
BOX3D *result = NULL;
|
|
GBOX gbox;
|
|
int32_t srid;
|
|
int rv;
|
|
|
|
/* Can't do anything with null inputs */
|
|
if (!box && !geom)
|
|
{
|
|
PG_RETURN_NULL();
|
|
}
|
|
/* Null geometry but non-null box, return the box */
|
|
else if (!geom)
|
|
{
|
|
result = palloc(sizeof(BOX3D));
|
|
memcpy(result, box, sizeof(BOX3D));
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
/*
|
|
* Deserialize geometry and *calculate* the box
|
|
* We can't use the cached box because it's float, we *must* calculate
|
|
*/
|
|
lwgeom = lwgeom_from_gserialized(geom);
|
|
srid = lwgeom->srid;
|
|
rv = lwgeom_calculate_gbox(lwgeom, &gbox);
|
|
lwgeom_free(lwgeom);
|
|
|
|
/* If we couldn't calculate the box, return what we know */
|
|
if (rv == LW_FAILURE)
|
|
{
|
|
PG_FREE_IF_COPY(geom, 1);
|
|
/* No geom box, no input box, so null return */
|
|
if (!box)
|
|
PG_RETURN_NULL();
|
|
result = palloc(sizeof(BOX3D));
|
|
memcpy(result, box, sizeof(BOX3D));
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
/* Null box and non-null geometry, just return the geometry box */
|
|
if (!box)
|
|
{
|
|
PG_FREE_IF_COPY(geom, 1);
|
|
result = box3d_from_gbox(&gbox);
|
|
result->srid = srid;
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
result = palloc(sizeof(BOX3D));
|
|
result->xmax = Max(box->xmax, gbox.xmax);
|
|
result->ymax = Max(box->ymax, gbox.ymax);
|
|
result->zmax = Max(box->zmax, gbox.zmax);
|
|
result->xmin = Min(box->xmin, gbox.xmin);
|
|
result->ymin = Min(box->ymin, gbox.ymin);
|
|
result->zmin = Min(box->zmin, gbox.zmin);
|
|
result->srid = srid;
|
|
|
|
PG_FREE_IF_COPY(geom, 1);
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_combine_BOX3D);
|
|
Datum BOX3D_combine_BOX3D(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box0 = (BOX3D *)(PG_ARGISNULL(0) ? NULL : PG_GETARG_POINTER(0));
|
|
BOX3D *box1 = (BOX3D *)(PG_ARGISNULL(1) ? NULL : PG_GETARG_POINTER(1));
|
|
BOX3D *result;
|
|
|
|
if (box0 && !box1)
|
|
PG_RETURN_POINTER(box0);
|
|
|
|
if (box1 && !box0)
|
|
PG_RETURN_POINTER(box1);
|
|
|
|
if (!box1 && !box0)
|
|
PG_RETURN_NULL();
|
|
|
|
result = palloc(sizeof(BOX3D));
|
|
result->xmax = Max(box0->xmax, box1->xmax);
|
|
result->ymax = Max(box0->ymax, box1->ymax);
|
|
result->zmax = Max(box0->zmax, box1->zmax);
|
|
result->xmin = Min(box0->xmin, box1->xmin);
|
|
result->ymin = Min(box0->ymin, box1->ymin);
|
|
result->zmin = Min(box0->zmin, box1->zmin);
|
|
result->srid = box0->srid;
|
|
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_construct);
|
|
Datum BOX3D_construct(PG_FUNCTION_ARGS)
|
|
{
|
|
GSERIALIZED *min = PG_GETARG_GSERIALIZED_P(0);
|
|
GSERIALIZED *max = PG_GETARG_GSERIALIZED_P(1);
|
|
BOX3D *result = palloc(sizeof(BOX3D));
|
|
LWGEOM *minpoint, *maxpoint;
|
|
POINT3DZ minp, maxp;
|
|
|
|
minpoint = lwgeom_from_gserialized(min);
|
|
maxpoint = lwgeom_from_gserialized(max);
|
|
|
|
if (minpoint->type != POINTTYPE || maxpoint->type != POINTTYPE)
|
|
{
|
|
elog(ERROR, "BOX3D_construct: args must be points");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
if (lwgeom_is_empty(minpoint) || lwgeom_is_empty(maxpoint) ){
|
|
elog(ERROR, "BOX3D_construct: args can not be empty points");
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
gserialized_error_if_srid_mismatch(min, max, __func__);
|
|
|
|
getPoint3dz_p(((LWPOINT *)minpoint)->point, 0, &minp);
|
|
getPoint3dz_p(((LWPOINT *)maxpoint)->point, 0, &maxp);
|
|
|
|
result->xmax = maxp.x;
|
|
result->ymax = maxp.y;
|
|
result->zmax = maxp.z;
|
|
|
|
result->xmin = minp.x;
|
|
result->ymin = minp.y;
|
|
result->zmin = minp.z;
|
|
|
|
result->srid = minpoint->srid;
|
|
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
/** needed for sp-gist support PostgreSQL 11+ **/
|
|
/*****************************************************************************
|
|
* BOX3D functions
|
|
*****************************************************************************/
|
|
|
|
/* contains? */
|
|
bool
|
|
BOX3D_contains_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
return (box1->xmax >= box2->xmax && box1->xmin <= box2->xmin) &&
|
|
(box1->ymax >= box2->ymax && box1->ymin <= box2->ymin) &&
|
|
(box1->zmax >= box2->zmax && box1->zmin <= box2->zmin);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_contains);
|
|
|
|
PGDLLEXPORT Datum BOX3D_contains(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
bool result = BOX3D_contains_internal(box1, box2);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/* contained by? */
|
|
bool
|
|
BOX3D_contained_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
return BOX3D_contains_internal(box2, box1);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_contained);
|
|
|
|
PGDLLEXPORT Datum BOX3D_contained(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
bool result = BOX3D_contained_internal(box1, box2);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/* overlaps? */
|
|
bool
|
|
BOX3D_overlaps_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
return (box1->xmin <= box2->xmax && box2->xmin <= box1->xmax) &&
|
|
(box1->ymin <= box2->ymax && box2->ymin <= box1->ymax) &&
|
|
(box1->zmin <= box2->zmax && box2->zmin <= box1->zmax);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_overlaps);
|
|
|
|
PGDLLEXPORT Datum BOX3D_overlaps(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
bool result = BOX3D_overlaps_internal(box1, box2);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/* same? */
|
|
bool
|
|
BOX3D_same_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
return (FPeq(box1->xmax, box2->xmax) && FPeq(box1->xmin, box2->xmin)) &&
|
|
(FPeq(box1->ymax, box2->ymax) && FPeq(box1->ymin, box2->ymin)) &&
|
|
(FPeq(box1->zmax, box2->zmax) && FPeq(box1->zmin, box2->zmin));
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_same);
|
|
|
|
PGDLLEXPORT Datum BOX3D_same(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
bool result = BOX3D_same_internal(box1, box2);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/* strictly left of? */
|
|
bool
|
|
BOX3D_left_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
return box1->xmax < box2->xmin;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_left);
|
|
|
|
PGDLLEXPORT Datum BOX3D_left(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
bool result = BOX3D_left_internal(box1, box2);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/* does not extend to right of? */
|
|
bool
|
|
BOX3D_overleft_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
return box1->xmax <= box2->xmax;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_overleft);
|
|
|
|
PGDLLEXPORT Datum BOX3D_overleft(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
bool result = BOX3D_overleft_internal(box1, box2);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/* strictly right of? */
|
|
bool
|
|
BOX3D_right_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
return box1->xmin > box2->xmax;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_right);
|
|
|
|
PGDLLEXPORT Datum BOX3D_right(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
bool result = BOX3D_right_internal(box1, box2);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/* does not extend to left of? */
|
|
bool
|
|
BOX3D_overright_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
return box1->xmin >= box2->xmin;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_overright);
|
|
|
|
PGDLLEXPORT Datum BOX3D_overright(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
bool result = BOX3D_overright_internal(box1, box2);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/* strictly below of? */
|
|
bool
|
|
BOX3D_below_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
return box1->ymax < box2->ymin;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_below);
|
|
|
|
PGDLLEXPORT Datum BOX3D_below(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
bool result = BOX3D_below_internal(box1, box2);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/* does not extend above of? */
|
|
bool
|
|
BOX3D_overbelow_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
return box1->ymax <= box2->ymax;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_overbelow);
|
|
|
|
PGDLLEXPORT Datum BOX3D_overbelow(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
bool result = BOX3D_overbelow_internal(box1, box2);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/* strictly above of? */
|
|
bool
|
|
BOX3D_above_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
return box1->ymin > box2->ymax;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_above);
|
|
|
|
PGDLLEXPORT Datum BOX3D_above(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
bool result = BOX3D_above_internal(box1, box2);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/* does not extend below of? */
|
|
bool
|
|
BOX3D_overabove_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
return box1->ymin >= box2->ymin;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_overabove);
|
|
|
|
PGDLLEXPORT Datum BOX3D_overabove(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
bool result = BOX3D_overabove_internal(box1, box2);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/* strictly in before of? */
|
|
bool
|
|
BOX3D_front_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
return box1->zmax < box2->zmin;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_front);
|
|
|
|
PGDLLEXPORT Datum BOX3D_front(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
bool result = BOX3D_front_internal(box1, box2);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/* does not extend to the after of? */
|
|
bool
|
|
BOX3D_overfront_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
return box1->zmax <= box2->zmax;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_overfront);
|
|
|
|
PGDLLEXPORT Datum BOX3D_overfront(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
bool result = BOX3D_overfront_internal(box1, box2);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/* strictly after of? */
|
|
bool
|
|
BOX3D_back_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
return box1->zmin > box2->zmax;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_back);
|
|
|
|
PGDLLEXPORT Datum BOX3D_back(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
bool result = BOX3D_back_internal(box1, box2);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/* does not extend to the before of? */
|
|
bool
|
|
BOX3D_overback_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
return box1->zmin >= box2->zmin;
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_overback);
|
|
|
|
PGDLLEXPORT Datum BOX3D_overback(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
bool result = BOX3D_overback_internal(box1, box2);
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
/* Minimum distance between 2 bounding boxes */
|
|
double
|
|
BOX3D_distance_internal(BOX3D *box1, BOX3D *box2)
|
|
{
|
|
double sqrDist = 0;
|
|
double d;
|
|
|
|
if (BOX3D_overlaps_internal(box1, box2))
|
|
return 0.0;
|
|
|
|
/* X axis */
|
|
if (box1->xmax < box2->xmin)
|
|
{
|
|
d = box1->xmax - box2->xmin;
|
|
sqrDist += d * d;
|
|
}
|
|
else if (box1->xmin > box2->xmax)
|
|
{
|
|
d = box1->xmin - box2->xmax;
|
|
sqrDist += d * d;
|
|
}
|
|
/* Y axis */
|
|
if (box1->ymax < box2->ymin)
|
|
{
|
|
d = box1->ymax - box2->ymin;
|
|
sqrDist += d * d;
|
|
}
|
|
else if (box1->ymin > box2->ymax)
|
|
{
|
|
d = box1->ymin - box2->ymax;
|
|
sqrDist += d * d;
|
|
}
|
|
/* Z axis */
|
|
if (box1->zmax < box2->zmin)
|
|
{
|
|
d = box1->zmax - box2->zmin;
|
|
sqrDist += d * d;
|
|
}
|
|
else if (box1->zmin > box2->zmax)
|
|
{
|
|
d = box1->zmin - box2->zmax;
|
|
sqrDist += d * d;
|
|
}
|
|
|
|
return sqrt(sqrDist);
|
|
}
|
|
|
|
PG_FUNCTION_INFO_V1(BOX3D_distance);
|
|
|
|
PGDLLEXPORT Datum BOX3D_distance(PG_FUNCTION_ARGS)
|
|
{
|
|
BOX3D *box1 = PG_GETARG_BOX3D_P(0);
|
|
BOX3D *box2 = PG_GETARG_BOX3D_P(1);
|
|
PG_RETURN_FLOAT8(BOX3D_distance_internal(box1, box2));
|
|
}
|