mirror of
https://git.osgeo.org/gitea/postgis/postgis
synced 2024-10-26 10:02:50 +00:00
975 lines
21 KiB
C
975 lines
21 KiB
C
|
/*************************************************************
|
||
|
postGIS - geometric types for postgres
|
||
|
|
||
|
This software is copyrighted (2001).
|
||
|
|
||
|
This is free software; you can redistribute it and/or modify
|
||
|
it under the GNU General Public Licence. See the file "COPYING".
|
||
|
|
||
|
More Info? See the documentation, join the mailing list
|
||
|
(postgis@yahoogroups.com), or see the web page
|
||
|
(http://postgis.refractions.net).
|
||
|
|
||
|
*************************************************************/
|
||
|
|
||
|
|
||
|
|
||
|
#include "postgres.h"
|
||
|
|
||
|
|
||
|
#include <math.h>
|
||
|
#include <float.h>
|
||
|
#include <string.h>
|
||
|
#include <stdio.h>
|
||
|
#include <errno.h>
|
||
|
|
||
|
#include "access/gist.h"
|
||
|
#include "access/itup.h"
|
||
|
#include "access/rtree.h"
|
||
|
|
||
|
|
||
|
#include "fmgr.h"
|
||
|
|
||
|
|
||
|
#include "postgis.h"
|
||
|
#include "utils/elog.h"
|
||
|
|
||
|
|
||
|
//Norman Vine found this problem for compiling under cygwin
|
||
|
// it defines BYTE_ORDER and LITTLE_ENDIAN
|
||
|
|
||
|
#ifdef __CYGWIN__
|
||
|
#include <sys/param.h> // FOR ENDIAN DEFINES
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#define SHOW_DIGS_DOUBLE 15
|
||
|
#define MAX_DIGS_DOUBLE (SHOW_DIGS_DOUBLE + 6 + 1 + 3 +1)
|
||
|
|
||
|
|
||
|
// #define DEBUG_GIST
|
||
|
//#define DEBUG_GIST2
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/**************************************************************************
|
||
|
* GENERAL PURPOSE GEOMETRY FUNCTIONS
|
||
|
**************************************************************************/
|
||
|
|
||
|
|
||
|
double line_length2d(LINE3D *line)
|
||
|
{
|
||
|
int i;
|
||
|
POINT3D *frm,*to;
|
||
|
double dist = 0.0;
|
||
|
|
||
|
if (line->npoints <2)
|
||
|
return 0.0; //must have >1 point to make sense
|
||
|
|
||
|
frm = &line->points[0];
|
||
|
|
||
|
for (i=1; i<line->npoints;i++)
|
||
|
{
|
||
|
to = &line->points[i];
|
||
|
|
||
|
dist += sqrt( ( (frm->x - to->x)*(frm->x - to->x) ) +
|
||
|
( (frm->y - to->y)*(frm->y - to->y) ) );
|
||
|
|
||
|
frm = to;
|
||
|
}
|
||
|
return dist;
|
||
|
}
|
||
|
|
||
|
double line_length3d(LINE3D *line)
|
||
|
{
|
||
|
int i;
|
||
|
POINT3D *frm,*to;
|
||
|
double dist = 0.0;
|
||
|
|
||
|
if (line->npoints <2)
|
||
|
return 0.0; //must have >1 point to make sense
|
||
|
|
||
|
frm = &line->points[0];
|
||
|
|
||
|
for (i=1; i<line->npoints;i++)
|
||
|
{
|
||
|
to = &line->points[i];
|
||
|
|
||
|
dist += sqrt( ( (frm->x - to->x)*(frm->x - to->x) ) +
|
||
|
((frm->y - to->y)*(frm->y - to->y) ) +
|
||
|
((frm->z - to->z)*(frm->z - to->z) ) );
|
||
|
|
||
|
frm = to;
|
||
|
}
|
||
|
return dist;
|
||
|
}
|
||
|
|
||
|
|
||
|
//find the "length of a geometry"
|
||
|
// length3d(point) = 0
|
||
|
// length3d(line) = length of line
|
||
|
// length3d(polygon) = 0 -- could make sense to return sum(ring perimeter)
|
||
|
// uses euclidian 3d length
|
||
|
|
||
|
PG_FUNCTION_INFO_V1(length3d);
|
||
|
Datum length3d(PG_FUNCTION_ARGS)
|
||
|
{
|
||
|
GEOMETRY *geom1 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
||
|
int32 *offsets1;
|
||
|
char *o1;
|
||
|
int32 type1,j;
|
||
|
LINE3D *line;
|
||
|
double dist = 0.0;
|
||
|
|
||
|
offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
|
||
|
|
||
|
|
||
|
//now have to do a scan of each object
|
||
|
|
||
|
for (j=0; j< geom1->nobjs; j++) //for each object in geom1
|
||
|
{
|
||
|
o1 = (char *) geom1 +offsets1[j] ;
|
||
|
type1= geom1->objType[j];
|
||
|
if (type1 == LINETYPE) //LINESTRING
|
||
|
{
|
||
|
line = (LINE3D *) o1;
|
||
|
dist += line_length3d(line);
|
||
|
}
|
||
|
}
|
||
|
PG_RETURN_FLOAT4(dist);
|
||
|
}
|
||
|
|
||
|
//find the "length of a geometry"
|
||
|
// length3d(point) = 0
|
||
|
// length3d(line) = length of line
|
||
|
// length3d(polygon) = 0 -- could make sense to return sum(ring perimeter)
|
||
|
// uses euclidian 2d length
|
||
|
|
||
|
PG_FUNCTION_INFO_V1(length2d);
|
||
|
Datum length2d(PG_FUNCTION_ARGS)
|
||
|
{
|
||
|
GEOMETRY *geom1 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
||
|
int32 *offsets1;
|
||
|
char *o1;
|
||
|
int32 type1,j;
|
||
|
LINE3D *line;
|
||
|
double dist = 0.0;
|
||
|
|
||
|
offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
|
||
|
|
||
|
|
||
|
//now have to do a scan of each object
|
||
|
|
||
|
for (j=0; j< geom1->nobjs; j++) //for each object in geom1
|
||
|
{
|
||
|
o1 = (char *) geom1 +offsets1[j] ;
|
||
|
type1= geom1->objType[j];
|
||
|
if (type1 == LINETYPE) //LINESTRING
|
||
|
{
|
||
|
line = (LINE3D *) o1;
|
||
|
dist += line_length2d(line);
|
||
|
}
|
||
|
}
|
||
|
PG_RETURN_FLOAT4(dist);
|
||
|
}
|
||
|
|
||
|
|
||
|
//find the 2d area of the outer ring - sum (area 2d of inner rings)
|
||
|
// Could use a more numerically stable calculator...
|
||
|
double polygon_area2d_old(POLYGON3D *poly1)
|
||
|
{
|
||
|
double poly_area=0.0, ringarea=0.0;
|
||
|
int i,j,ring,pt_offset;
|
||
|
POINT3D *pts1;
|
||
|
|
||
|
|
||
|
pts1 = (POINT3D *) ( (char *)&(poly1->npoints[poly1->nrings] ) );
|
||
|
pts1 = (POINT3D *) MAXALIGN(pts1);
|
||
|
|
||
|
|
||
|
|
||
|
pt_offset = 0; //index to first point in ring
|
||
|
for (ring = 0; ring < poly1->nrings; ring++)
|
||
|
{
|
||
|
ringarea = 0.0;
|
||
|
|
||
|
for (i=0;i<(poly1->npoints[ring]);i++)
|
||
|
{
|
||
|
j = (i+1) % (poly1->npoints[ring]);
|
||
|
ringarea += pts1[pt_offset+ i].x * pts1[pt_offset+j].y - pts1[pt_offset+ i].y * pts1[pt_offset+j].x;
|
||
|
}
|
||
|
|
||
|
ringarea /= 2.0;
|
||
|
|
||
|
ringarea = fabs(ringarea );
|
||
|
if (ring != 0) //outer
|
||
|
ringarea = -1.0*ringarea ; // its a hole
|
||
|
|
||
|
poly_area += ringarea;
|
||
|
|
||
|
pt_offset += poly1->npoints[ring];
|
||
|
}
|
||
|
|
||
|
return poly_area;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//calculate the area of all the subobj in a polygon
|
||
|
// area(point) = 0
|
||
|
// area (line) = 0
|
||
|
// area(polygon) = find its 2d area
|
||
|
PG_FUNCTION_INFO_V1(area2d);
|
||
|
Datum area2d(PG_FUNCTION_ARGS)
|
||
|
{
|
||
|
GEOMETRY *geom1 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
||
|
int32 *offsets1;
|
||
|
char *o1;
|
||
|
int32 type1,j;
|
||
|
POLYGON3D *poly;
|
||
|
double area = 0.0;
|
||
|
|
||
|
offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
|
||
|
|
||
|
//now have to do a scan of each object
|
||
|
|
||
|
for (j=0; j< geom1->nobjs; j++) //for each object in geom1
|
||
|
{
|
||
|
o1 = (char *) geom1 +offsets1[j] ;
|
||
|
type1= geom1->objType[j];
|
||
|
if (type1 == POLYGONTYPE) //POLYGON
|
||
|
{
|
||
|
poly = (POLYGON3D *) o1;
|
||
|
area += polygon_area2d_old(poly);
|
||
|
}
|
||
|
}
|
||
|
PG_RETURN_FLOAT4(area);
|
||
|
}
|
||
|
|
||
|
double polygon_perimeter3d(POLYGON3D *poly1)
|
||
|
{
|
||
|
double poly_perimeter=0.0, ring_perimeter=0.0;
|
||
|
int i,ring,pt_offset;
|
||
|
POINT3D *pts1;
|
||
|
POINT3D *to,*frm;
|
||
|
|
||
|
|
||
|
pts1 = (POINT3D *) ( (char *)&(poly1->npoints[poly1->nrings] ) );
|
||
|
pts1 = (POINT3D *) MAXALIGN(pts1);
|
||
|
|
||
|
|
||
|
|
||
|
pt_offset = 0; //index to first point in ring
|
||
|
for (ring = 0; ring < poly1->nrings; ring++)
|
||
|
{
|
||
|
ring_perimeter = 0.0;
|
||
|
|
||
|
|
||
|
frm = &pts1[pt_offset];
|
||
|
|
||
|
for (i=1; i<poly1->npoints[ring];i++)
|
||
|
{
|
||
|
to = &pts1[pt_offset+ i];
|
||
|
|
||
|
ring_perimeter += sqrt( ( (frm->x - to->x)*(frm->x - to->x) ) +
|
||
|
((frm->y - to->y)*(frm->y - to->y) ) +
|
||
|
((frm->z - to->z)*(frm->z - to->z) ) );
|
||
|
|
||
|
frm = to;
|
||
|
}
|
||
|
|
||
|
poly_perimeter += ring_perimeter;
|
||
|
|
||
|
pt_offset += poly1->npoints[ring];
|
||
|
}
|
||
|
|
||
|
return poly_perimeter;
|
||
|
|
||
|
}
|
||
|
|
||
|
double polygon_perimeter2d(POLYGON3D *poly1)
|
||
|
{
|
||
|
double poly_perimeter=0.0, ring_perimeter=0.0;
|
||
|
int i,ring,pt_offset;
|
||
|
POINT3D *pts1;
|
||
|
POINT3D *to,*frm;
|
||
|
|
||
|
|
||
|
pts1 = (POINT3D *) ( (char *)&(poly1->npoints[poly1->nrings] ) );
|
||
|
pts1 = (POINT3D *) MAXALIGN(pts1);
|
||
|
|
||
|
|
||
|
pt_offset = 0; //index to first point in ring
|
||
|
for (ring = 0; ring < poly1->nrings; ring++)
|
||
|
{
|
||
|
ring_perimeter = 0.0;
|
||
|
|
||
|
|
||
|
frm = &pts1[pt_offset];
|
||
|
|
||
|
for (i=1; i<poly1->npoints[ring];i++)
|
||
|
{
|
||
|
to = &pts1[pt_offset+ i];
|
||
|
|
||
|
ring_perimeter += sqrt( ( (frm->x - to->x)*(frm->x - to->x) ) +
|
||
|
((frm->y - to->y)*(frm->y - to->y) ) );
|
||
|
|
||
|
frm = to;
|
||
|
}
|
||
|
|
||
|
poly_perimeter += ring_perimeter;
|
||
|
|
||
|
pt_offset += poly1->npoints[ring];
|
||
|
}
|
||
|
|
||
|
return poly_perimeter;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//calculate the perimeter of polys (sum of length of all rings)
|
||
|
PG_FUNCTION_INFO_V1(perimeter3d);
|
||
|
Datum perimeter3d(PG_FUNCTION_ARGS)
|
||
|
{
|
||
|
GEOMETRY *geom1 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
||
|
int32 *offsets1;
|
||
|
char *o1;
|
||
|
int32 type1,j;
|
||
|
POLYGON3D *poly;
|
||
|
double area = 0.0;
|
||
|
|
||
|
offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
|
||
|
|
||
|
//now have to do a scan of each object
|
||
|
|
||
|
for (j=0; j< geom1->nobjs; j++) //for each object in geom1
|
||
|
{
|
||
|
o1 = (char *) geom1 +offsets1[j] ;
|
||
|
type1= geom1->objType[j];
|
||
|
if (type1 == POLYGONTYPE) //POLYGON
|
||
|
{
|
||
|
poly = (POLYGON3D *) o1;
|
||
|
area += polygon_perimeter3d(poly);
|
||
|
}
|
||
|
}
|
||
|
PG_RETURN_FLOAT4(area);
|
||
|
}
|
||
|
|
||
|
//calculate the perimeter of polys (sum of length of all rings)
|
||
|
PG_FUNCTION_INFO_V1(perimeter2d);
|
||
|
Datum perimeter2d(PG_FUNCTION_ARGS)
|
||
|
{
|
||
|
GEOMETRY *geom1 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
||
|
int32 *offsets1;
|
||
|
char *o1;
|
||
|
int32 type1,j;
|
||
|
POLYGON3D *poly;
|
||
|
double area = 0.0;
|
||
|
|
||
|
offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
|
||
|
|
||
|
//now have to do a scan of each object
|
||
|
|
||
|
for (j=0; j< geom1->nobjs; j++) //for each object in geom1
|
||
|
{
|
||
|
o1 = (char *) geom1 +offsets1[j] ;
|
||
|
type1= geom1->objType[j];
|
||
|
if (type1 == POLYGONTYPE) //POLYGON
|
||
|
{
|
||
|
poly = (POLYGON3D *) o1;
|
||
|
area += polygon_perimeter2d(poly);
|
||
|
}
|
||
|
}
|
||
|
PG_RETURN_FLOAT4(area);
|
||
|
}
|
||
|
|
||
|
|
||
|
// cn_PnPoly(): crossing number test for a point in a polygon
|
||
|
// input: P = a point,
|
||
|
// V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
|
||
|
// returns: 0 = outside, 1 = inside
|
||
|
//
|
||
|
// Our polygons have first and last point the same,
|
||
|
//
|
||
|
|
||
|
int PIP( POINT3D *P, POINT3D *V, int n )
|
||
|
{
|
||
|
int cn = 0; // the crossing number counter
|
||
|
int i;
|
||
|
|
||
|
double vt;
|
||
|
|
||
|
// loop through all edges of the polygon
|
||
|
|
||
|
for (i=0; i< (n-1) ; i++)
|
||
|
{ // edge from V[i] to V[i+1]
|
||
|
|
||
|
if (((V[i].y <= P->y) && (V[i+1].y > P->y)) // an upward crossing
|
||
|
|| ((V[i].y > P->y) && (V[i+1].y <= P->y))) // a downward crossing
|
||
|
{
|
||
|
|
||
|
vt = (double)(P->y - V[i].y) / (V[i+1].y - V[i].y);
|
||
|
if (P->x < V[i].x + vt * (V[i+1].x - V[i].x)) // P.x <intersect
|
||
|
++cn; // a valid crossing of y=P.y right of P.x
|
||
|
}
|
||
|
}
|
||
|
return (cn&1); // 0 if even (out), and 1 if odd (in)
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//this one is easy - is the point inside a box?
|
||
|
bool point_truely_inside(POINT3D *point, BOX3D *box)
|
||
|
{
|
||
|
return (
|
||
|
(point->x >= box->LLB.x) && (point->x <= box->URT.x) &&
|
||
|
(point->y >= box->LLB.y) && (point->y <= box->URT.y)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
|
||
|
// see if a linestring is inside a box - check to see if each its line
|
||
|
// segments are inside the box
|
||
|
// NOTE: all this is happening in 2d - third dimention is ignored
|
||
|
//
|
||
|
//use a modified CohenSutherland line clipping algoritm
|
||
|
//
|
||
|
//Step one - compute outcodes for Pi and Pi+1
|
||
|
//
|
||
|
//
|
||
|
// | |
|
||
|
// 1001 | 1000 | 1010
|
||
|
// | |
|
||
|
// ----------+-----------+------------
|
||
|
// | (inside) |
|
||
|
// 0001 | | 0010
|
||
|
// | 0000 |
|
||
|
// ----------+-----------+------------
|
||
|
// | |
|
||
|
// 0101 | 0100 | 0110
|
||
|
// | |
|
||
|
//
|
||
|
// If Pi or Pi+1 have code 0000 then this line seg is inside the box (trivial accept)
|
||
|
// if Pi && Pi+1 != 0 then seg is NOT in the box (trivial reject)
|
||
|
//
|
||
|
// Otherwise the line seg might traverse through the box.
|
||
|
// => see if the seg intersects any of the walls of the box.
|
||
|
//
|
||
|
|
||
|
int compute_outcode( POINT3D *p, BOX3D *box)
|
||
|
{
|
||
|
int Code=0;
|
||
|
|
||
|
//printf("compute outcode:\n");
|
||
|
//print_box(box);
|
||
|
//printf("point = [%g,%g]\n",p->x,p->y);
|
||
|
if (p->y > box->URT.y )
|
||
|
Code = 8;
|
||
|
else
|
||
|
{
|
||
|
if (p->y <box->LLB.y )
|
||
|
{
|
||
|
Code = 4;
|
||
|
}
|
||
|
}
|
||
|
if (p->x > box->URT.x )
|
||
|
{
|
||
|
return (Code+2);
|
||
|
}
|
||
|
if (p->x <box->LLB.x )
|
||
|
{
|
||
|
return (Code+1);
|
||
|
}
|
||
|
|
||
|
return (Code);
|
||
|
}
|
||
|
|
||
|
|
||
|
bool lineseg_inside_box( POINT3D *P1, POINT3D *P2, BOX3D *box)
|
||
|
{
|
||
|
int outcode_p1, outcode_p2;
|
||
|
double Ax,Ay, Bx,By, Cx,Cy, Dx,Dy;
|
||
|
double r,s;
|
||
|
|
||
|
outcode_p1 = compute_outcode(P1, box);
|
||
|
if (outcode_p1 ==0)
|
||
|
return TRUE;
|
||
|
|
||
|
outcode_p2 = compute_outcode(P2, box);
|
||
|
if (outcode_p2 ==0)
|
||
|
return TRUE;
|
||
|
if ((outcode_p1 & outcode_p2) != 0)
|
||
|
return FALSE;
|
||
|
|
||
|
|
||
|
if ((outcode_p1 + outcode_p2) == 12)
|
||
|
return TRUE; //vertically slices box
|
||
|
|
||
|
if ((outcode_p1 + outcode_p2) == 3)
|
||
|
return TRUE; //horizontally slices box
|
||
|
|
||
|
|
||
|
//printf("have to do boundry calcs\n");
|
||
|
|
||
|
// know that its a diagonal line
|
||
|
|
||
|
//this is the tough case - it may or maynot intersect the box
|
||
|
//see if it intersects the horizonal box lines
|
||
|
|
||
|
// from comp.graphics.algo's faq:
|
||
|
/*
|
||
|
(Ay-Cy)(Dx-Cx)-(Ax-Cx)(Dy-Cy)
|
||
|
r = ------------------------------------
|
||
|
(Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
|
||
|
|
||
|
(Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
|
||
|
s = -----------------------------------
|
||
|
(Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
|
||
|
|
||
|
*/
|
||
|
// if 0<= r&s <=1 then intersection exists
|
||
|
|
||
|
Ax = P1->x; Ay = P1->y;
|
||
|
Bx = P2->x; By = P2->y;
|
||
|
|
||
|
//top hor part of box
|
||
|
Cx = box->LLB.x; Cy= box->URT.y;
|
||
|
Dx = box->URT.x; Dy= box->URT.y;
|
||
|
|
||
|
r= ((Ay-Cy)*(Dx-Cx)-(Ax-Cx)*(Dy-Cy) )/
|
||
|
((Bx-Ax)*(Dy-Cy)-(By-Ay)*(Dx-Cx) ) ;
|
||
|
|
||
|
s= ((Ay-Cy)*(Bx-Ax)-(Ax-Cx)*(By-Ay))/
|
||
|
((Bx-Ax)*(Dy-Cy)-(By-Ay)*(Dx-Cx) ) ;
|
||
|
|
||
|
if ( (r>=0) && (r<=1) && (s>=0) && (s<=1) )
|
||
|
return TRUE;
|
||
|
|
||
|
//bottom hor part of box
|
||
|
Cx = box->LLB.x; Cy= box->LLB.y;
|
||
|
Dx = box->URT.x; Dy= box->LLB.y;
|
||
|
|
||
|
r= ((Ay-Cy)*(Dx-Cx)-(Ax-Cx)*(Dy-Cy) )/
|
||
|
((Bx-Ax)*(Dy-Cy)-(By-Ay)*(Dx-Cx) ) ;
|
||
|
|
||
|
s= ((Ay-Cy)*(Bx-Ax)-(Ax-Cx)*(By-Ay))/
|
||
|
((Bx-Ax)*(Dy-Cy)-(By-Ay)*(Dx-Cx) ) ;
|
||
|
if ( (r>=0) && (r<=1) && (s>=0) && (s<=1) )
|
||
|
return TRUE;
|
||
|
|
||
|
return FALSE;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
//give the work to an easier process
|
||
|
bool linestring_inside_box(POINT3D *pts, int npoints, BOX3D *box)
|
||
|
{
|
||
|
POINT3D *frm,*to;
|
||
|
int i;
|
||
|
|
||
|
if (npoints <2)
|
||
|
return FALSE; //must have >1 point to make sense
|
||
|
|
||
|
frm = &pts[0];
|
||
|
|
||
|
for (i=1; i< npoints;i++)
|
||
|
{
|
||
|
to = &pts[i];
|
||
|
|
||
|
if (lineseg_inside_box( frm, to, box))
|
||
|
return TRUE;
|
||
|
|
||
|
frm = to;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
//this ones the most complex
|
||
|
// 1. treat the outer ring as a line and see if its inside
|
||
|
// + if it is, then poly is inside box
|
||
|
// 2. The polygon could be completely contained inside the box
|
||
|
// + line detector (step 1) would have found this
|
||
|
// 3. The polygon could be completely surrounding the box
|
||
|
// + point-in-polygon of one of the box corners
|
||
|
// + COMPLEXITY: box could be totally inside a single hole
|
||
|
// +test all holes like step 1. Any that pass arent "bad" holes
|
||
|
// + each of the bad holes do a point-in-polygon if true =-> totally in hole
|
||
|
|
||
|
bool polygon_truely_inside(POLYGON3D *poly, BOX3D *box)
|
||
|
{
|
||
|
bool outer_ring_inside,outer_ring_surrounds;
|
||
|
POINT3D *pts1;
|
||
|
POINT3D test_point;
|
||
|
int32 ring;
|
||
|
int point_offset;
|
||
|
|
||
|
pts1 = (POINT3D *) ( (char *)&(poly->npoints[poly->nrings] ) );
|
||
|
pts1 = (POINT3D *) MAXALIGN(pts1);
|
||
|
|
||
|
|
||
|
//step 1 (and 2)
|
||
|
outer_ring_inside = linestring_inside_box(pts1, poly->npoints[0], box);
|
||
|
|
||
|
if (outer_ring_inside)
|
||
|
return TRUE;
|
||
|
|
||
|
test_point.x = box->LLB.x;
|
||
|
test_point.y = box->LLB.y;
|
||
|
//.z unimportant
|
||
|
|
||
|
outer_ring_surrounds = PIP(&test_point, pts1,poly->npoints[0] );
|
||
|
|
||
|
if (!(outer_ring_surrounds))
|
||
|
return FALSE; // disjoing
|
||
|
|
||
|
//printf("polygon - have to check holes\n");
|
||
|
|
||
|
//step 3
|
||
|
|
||
|
point_offset = poly->npoints[0];
|
||
|
for (ring =1; ring< poly->nrings; ring ++)
|
||
|
{
|
||
|
if (!( linestring_inside_box(&pts1[point_offset], poly->npoints[ring], box) ))
|
||
|
{
|
||
|
//doesnt intersect the box
|
||
|
// so if one of the corners of the box is inside the hole, it totally
|
||
|
// surrounds the box
|
||
|
|
||
|
if (PIP(&test_point, &pts1[point_offset],poly->npoints[ring] ) )
|
||
|
return FALSE;
|
||
|
}
|
||
|
point_offset += poly->npoints[ring];
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//This is more complicated because all the points in the line can be outside the box,
|
||
|
// but one of the edges can cross into the box.
|
||
|
// We send the work off to another function because its generically usefull for
|
||
|
// polygons too
|
||
|
bool line_truely_inside( LINE3D *line, BOX3D *box)
|
||
|
{
|
||
|
return linestring_inside_box(&line->points[0], line->npoints, box);
|
||
|
}
|
||
|
|
||
|
//is anything in geom1 really inside the the bvol (2d only) defined in geom2?
|
||
|
// send each object to its proper handler
|
||
|
PG_FUNCTION_INFO_V1(truly_inside);
|
||
|
Datum truly_inside(PG_FUNCTION_ARGS)
|
||
|
{
|
||
|
GEOMETRY *geom1 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
||
|
GEOMETRY *geom2 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
||
|
BOX3D *the_bbox;
|
||
|
int32 *offsets1;
|
||
|
char *o1;
|
||
|
int32 type1,j;
|
||
|
POLYGON3D *poly;
|
||
|
LINE3D *line;
|
||
|
POINT3D *point;
|
||
|
|
||
|
the_bbox = &geom2->bvol;
|
||
|
|
||
|
offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
|
||
|
|
||
|
//now have to do a scan of each object
|
||
|
|
||
|
for (j=0; j< geom1->nobjs; j++) //for each object in geom1
|
||
|
{
|
||
|
o1 = (char *) geom1 +offsets1[j] ;
|
||
|
type1= geom1->objType[j];
|
||
|
|
||
|
if (type1 == POINTTYPE) //point
|
||
|
{
|
||
|
point = (POINT3D *) o1;
|
||
|
if ( point_truely_inside(point, the_bbox))
|
||
|
PG_RETURN_BOOL(TRUE);
|
||
|
}
|
||
|
|
||
|
if (type1 == LINETYPE) //line
|
||
|
{
|
||
|
line = (LINE3D *) o1;
|
||
|
if ( line_truely_inside(line, the_bbox))
|
||
|
PG_RETURN_BOOL(TRUE);
|
||
|
}
|
||
|
|
||
|
if (type1 == POLYGONTYPE) //POLYGON
|
||
|
{
|
||
|
poly = (POLYGON3D *) o1;
|
||
|
|
||
|
if ( polygon_truely_inside(poly, the_bbox))
|
||
|
PG_RETURN_BOOL(TRUE);
|
||
|
}
|
||
|
}
|
||
|
PG_RETURN_BOOL(FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//number of points in an object
|
||
|
PG_FUNCTION_INFO_V1(npoints);
|
||
|
Datum npoints(PG_FUNCTION_ARGS)
|
||
|
{
|
||
|
GEOMETRY *geom1 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
||
|
int32 *offsets1;
|
||
|
char *o1;
|
||
|
int32 type1,j,i;
|
||
|
POLYGON3D *poly;
|
||
|
LINE3D *line;
|
||
|
int32 numb_points = 0;
|
||
|
|
||
|
offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
|
||
|
|
||
|
//now have to do a scan of each object
|
||
|
|
||
|
for (j=0; j< geom1->nobjs; j++) //for each object in geom1
|
||
|
{
|
||
|
o1 = (char *) geom1 +offsets1[j] ;
|
||
|
type1= geom1->objType[j];
|
||
|
|
||
|
if (type1 == POINTTYPE) //point
|
||
|
{
|
||
|
numb_points ++;
|
||
|
}
|
||
|
|
||
|
if (type1 == LINETYPE) //line
|
||
|
{
|
||
|
line = (LINE3D *) o1;
|
||
|
numb_points += line->npoints;
|
||
|
}
|
||
|
|
||
|
if (type1 == POLYGONTYPE) //POLYGON
|
||
|
{
|
||
|
poly = (POLYGON3D *) o1;
|
||
|
for (i=0; i<poly->nrings;i++)
|
||
|
{
|
||
|
numb_points += poly->npoints[i];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PG_RETURN_INT32(numb_points);
|
||
|
}
|
||
|
|
||
|
//number of rings in an object
|
||
|
PG_FUNCTION_INFO_V1(nrings);
|
||
|
Datum nrings(PG_FUNCTION_ARGS)
|
||
|
{
|
||
|
GEOMETRY *geom1 = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
||
|
int32 *offsets1;
|
||
|
char *o1;
|
||
|
int32 type1,j;
|
||
|
POLYGON3D *poly;
|
||
|
int32 numb_rings = 0;
|
||
|
|
||
|
offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
|
||
|
|
||
|
//now have to do a scan of each object
|
||
|
|
||
|
for (j=0; j< geom1->nobjs; j++) //for each object in geom1
|
||
|
{
|
||
|
o1 = (char *) geom1 +offsets1[j] ;
|
||
|
type1= geom1->objType[j];
|
||
|
|
||
|
if (type1 == POLYGONTYPE) //POLYGON
|
||
|
{
|
||
|
poly = (POLYGON3D *) o1;
|
||
|
numb_rings += poly->nrings;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PG_RETURN_INT32(numb_rings);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void translate_points(POINT3D *pt, int npoints,double x_off, double y_off, double z_off)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
//printf("translating %i points by [%g,%g,%g]\n",npoints,x_off,y_off,z_off);
|
||
|
if (npoints <1)
|
||
|
return; //nothing to do
|
||
|
|
||
|
for (i=0;i<npoints;i++)
|
||
|
{
|
||
|
//printf("before: [%g,%g,%g]\n", pt[i].x, pt[i].y,pt[i].z);
|
||
|
|
||
|
pt[i].x += x_off;
|
||
|
pt[i].y += y_off;
|
||
|
pt[i].z += z_off;
|
||
|
|
||
|
//printf("after: [%g,%g,%g]\n", pt[i].x, pt[i].y,pt[i].z);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//translate geometry
|
||
|
PG_FUNCTION_INFO_V1(translate);
|
||
|
Datum translate(PG_FUNCTION_ARGS)
|
||
|
{
|
||
|
GEOMETRY *geom = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
||
|
GEOMETRY *geom1;
|
||
|
double x_off = PG_GETARG_FLOAT8(1);
|
||
|
double y_off = PG_GETARG_FLOAT8(2);
|
||
|
double z_off = PG_GETARG_FLOAT8(3);
|
||
|
int32 *offsets1;
|
||
|
char *o1;
|
||
|
int32 type,j,i;
|
||
|
POLYGON3D *poly;
|
||
|
POINT3D *point,*pts;
|
||
|
LINE3D *line;
|
||
|
int numb_points;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//make a copy of geom so we can return a new version
|
||
|
|
||
|
geom1 = (GEOMETRY *) palloc (geom->size);
|
||
|
memcpy(geom1, geom, geom->size);
|
||
|
|
||
|
|
||
|
offsets1 = (int32 *) ( ((char *) &(geom1->objType[0] ))+ sizeof(int32) * geom1->nobjs ) ;
|
||
|
|
||
|
//now have to do a scan of each object
|
||
|
|
||
|
for (j=0; j< geom1->nobjs; j++) //for each object in geom1
|
||
|
{
|
||
|
o1 = (char *) geom1 +offsets1[j] ;
|
||
|
type= geom1->objType[j];
|
||
|
|
||
|
if (type == POINTTYPE) //point
|
||
|
{
|
||
|
point = (POINT3D *) o1;
|
||
|
translate_points(point, 1,x_off,y_off,z_off);
|
||
|
}
|
||
|
|
||
|
if (type == LINETYPE) //line
|
||
|
{
|
||
|
line = (LINE3D *) o1;
|
||
|
translate_points(&(line->points[0]), line->npoints,x_off,y_off,z_off);
|
||
|
}
|
||
|
|
||
|
if (type == POLYGONTYPE) //POLYGON
|
||
|
{
|
||
|
poly = (POLYGON3D *) o1;
|
||
|
//find where the points are and where to put them
|
||
|
numb_points =0;
|
||
|
for (i=0; i<poly->nrings;i++)
|
||
|
{
|
||
|
numb_points += poly->npoints[i];
|
||
|
}
|
||
|
pts = (POINT3D *) ( (char *)&(poly->npoints[poly->nrings] ) );
|
||
|
pts = (POINT3D *) MAXALIGN(pts);
|
||
|
translate_points(pts, numb_points,x_off,y_off,z_off);
|
||
|
|
||
|
|
||
|
}
|
||
|
}
|
||
|
PG_RETURN_POINTER(geom1);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// set the is3d flag so the system think the geometry is 2d or 3d
|
||
|
|
||
|
PG_FUNCTION_INFO_V1(force_2d);
|
||
|
Datum force_2d(PG_FUNCTION_ARGS)
|
||
|
{
|
||
|
GEOMETRY *geom = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
||
|
|
||
|
geom->is3d = FALSE;
|
||
|
PG_RETURN_POINTER(geom);
|
||
|
}
|
||
|
|
||
|
|
||
|
PG_FUNCTION_INFO_V1(force_3d);
|
||
|
Datum force_3d(PG_FUNCTION_ARGS)
|
||
|
{
|
||
|
GEOMETRY *geom = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
|
||
|
|
||
|
geom->is3d = TRUE;
|
||
|
PG_RETURN_POINTER(geom);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
//NULL safe bbox union
|
||
|
// combine_bbox(BOX3D, geometry) => union BOX3D and geometry->bvol
|
||
|
// For finding the extent of a set of rows using the extent() aggregate
|
||
|
|
||
|
PG_FUNCTION_INFO_V1(combine_bbox);
|
||
|
Datum combine_bbox(PG_FUNCTION_ARGS)
|
||
|
{
|
||
|
Pointer box3d_ptr = PG_GETARG_POINTER(0);
|
||
|
Pointer geom_ptr = PG_GETARG_POINTER(1);
|
||
|
BOX3D *a,*b;
|
||
|
GEOMETRY *geom;
|
||
|
BOX3D *box;
|
||
|
|
||
|
if ( (box3d_ptr == NULL) && (geom_ptr == NULL) )
|
||
|
{
|
||
|
PG_RETURN_NULL(); // combine_bbox(null,null) => null
|
||
|
}
|
||
|
|
||
|
if (box3d_ptr == NULL)
|
||
|
{
|
||
|
geom = (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1));
|
||
|
PG_RETURN_POINTER(&(geom->bvol)); // combine_bbox(null, geometry) => geometry->bvol
|
||
|
}
|
||
|
|
||
|
if (geom_ptr == NULL)
|
||
|
{
|
||
|
PG_RETURN_POINTER( PG_GETARG_DATUM(0) ); // combine_bbox(BOX3D, null) => BOX3D
|
||
|
}
|
||
|
|
||
|
//combine_bbox(BOX3D, geometry) => union(BOX3D, geometry->bvol)
|
||
|
|
||
|
box = (BOX3D *) palloc(sizeof(BOX3D));
|
||
|
|
||
|
a = (BOX3D *) PG_GETARG_DATUM(0) ;
|
||
|
b= &(( (GEOMETRY *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)) )->bvol);
|
||
|
|
||
|
box->LLB.x = b->LLB.x;
|
||
|
box->LLB.y = b->LLB.y;
|
||
|
box->LLB.z = b->LLB.z;
|
||
|
|
||
|
box->URT.x = b->URT.x;
|
||
|
box->URT.y = b->URT.y;
|
||
|
box->URT.z = b->URT.z;
|
||
|
|
||
|
if (a->LLB.x < b->LLB.x)
|
||
|
box->LLB.x = a->LLB.x;
|
||
|
if (a->LLB.y < b->LLB.y)
|
||
|
box->LLB.y = a->LLB.y;
|
||
|
if (a->LLB.z < b->LLB.z)
|
||
|
box->LLB.z = a->LLB.z;
|
||
|
|
||
|
if (a->URT.x > b->URT.x)
|
||
|
box->URT.x = a->URT.x;
|
||
|
if (a->URT.y > b->URT.y)
|
||
|
box->URT.y = a->URT.y;
|
||
|
if (a->URT.z > b->URT.z)
|
||
|
box->URT.z = a->URT.z;
|
||
|
|
||
|
PG_RETURN_POINTER( box );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|