#include #include #include #include #include #include "postgres.h" #include "access/gist.h" #include "access/itup.h" #include "fmgr.h" #include "utils/elog.h" #include "utils/geo_decls.h" #include "lwgeom_pg.h" #include "liblwgeom.h" #include "stringBuffer.h" /* #define PGIS_DEBUG */ /* forward defs */ Datum BOX2DFLOAT4_in(PG_FUNCTION_ARGS); Datum BOX2DFLOAT4_out(PG_FUNCTION_ARGS); Datum LWGEOM_to_BOX2DFLOAT4(PG_FUNCTION_ARGS); Datum BOX2DFLOAT4_expand(PG_FUNCTION_ARGS); Datum BOX2DFLOAT4_to_BOX3D(PG_FUNCTION_ARGS); Datum BOX2DFLOAT4_combine(PG_FUNCTION_ARGS); Datum BOX2DFLOAT4_to_LWGEOM(PG_FUNCTION_ARGS); Datum BOX2DFLOAT4_construct(PG_FUNCTION_ARGS); /* parser - "BOX(xmin ymin,xmax ymax)" */ PG_FUNCTION_INFO_V1(BOX2DFLOAT4_in); Datum BOX2DFLOAT4_in(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); int nitems; BOX2DFLOAT4 *box = (BOX2DFLOAT4 *) palloc(sizeof(BOX2DFLOAT4)); /*printf( "box3d_in gets '%s'\n",str); */ if (strstr(str,"BOX(") != str ) { pfree(box); elog(ERROR,"BOX2DFLOAT4 parser - doesnt start with BOX("); PG_RETURN_NULL(); } nitems = sscanf(str,"BOX(%f %f,%f %f)", &box->xmin,&box->ymin,&box->xmax,&box->ymax); if (nitems != 4) { pfree(box); elog(ERROR,"BOX2DFLOAT4 parser - couldnt parse. It should look like: BOX(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; } PG_RETURN_POINTER(box); } /*writer "BOX(xmin ymin,xmax ymax)" */ PG_FUNCTION_INFO_V1(BOX2DFLOAT4_out); Datum BOX2DFLOAT4_out(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box = (BOX2DFLOAT4 *) PG_GETARG_POINTER(0); char tmp[500]; /* big enough */ char *result; int size; size = sprintf(tmp,"BOX(%.15g %.15g,%.15g %.15g)", box->xmin, box->ymin, box->xmax, box->ymax); result= palloc(size+1); /* +1= null term */ memcpy(result,tmp,size+1); PG_RETURN_CSTRING(result); } /*convert a PG_LWGEOM to BOX2D */ PG_FUNCTION_INFO_V1(LWGEOM_to_BOX2DFLOAT4); Datum LWGEOM_to_BOX2DFLOAT4(PG_FUNCTION_ARGS) { PG_LWGEOM *lwgeom = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); BOX2DFLOAT4 *result; result = palloc(sizeof(BOX2DFLOAT4)); if ( ! getbox2d_p(SERIALIZED_FORM(lwgeom), result) ) { PG_RETURN_NULL(); /* must be the empty geometry */ } PG_RETURN_POINTER(result); } /*---------------------------------------------------------- * Relational operators for BOXes. * <, >, <=, >=, and == are based on box area. *---------------------------------------------------------*/ /* * box_same - are two boxes identical? */ PG_FUNCTION_INFO_V1(BOX2D_same); Datum BOX2D_same(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box1 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(0); BOX2DFLOAT4 *box2 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(1); PG_RETURN_BOOL(FPeq(box1->xmax, box2->xmax) && FPeq(box1->xmin, box2->xmin) && FPeq(box1->ymax, box2->ymax) && FPeq(box1->ymin, box2->ymin)); } /* * box_overlap - does box1 overlap box2? */ PG_FUNCTION_INFO_V1(BOX2D_overlap); Datum BOX2D_overlap(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box1 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(0); BOX2DFLOAT4 *box2 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(1); bool result; result = ((FPge(box1->xmax, box2->xmax) && FPle(box1->xmin, box2->xmax)) || (FPge(box2->xmax, box1->xmax) && FPle(box2->xmin, box1->xmax))) && ((FPge(box1->ymax, box2->ymax) && FPle(box1->ymin, box2->ymax)) || (FPge(box2->ymax, box1->ymax) && FPle(box2->ymin, box1->ymax))); PG_RETURN_BOOL(result); } /* * box_overleft - is the right edge of box1 to the left of * the right edge of box2? */ PG_FUNCTION_INFO_V1(BOX2D_overleft); Datum BOX2D_overleft(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box1 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(0); BOX2DFLOAT4 *box2 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(1); PG_RETURN_BOOL(FPle(box1->xmax, box2->xmax)); } /* * box_left - is box1 strictly left of box2? */ PG_FUNCTION_INFO_V1(BOX2D_left); Datum BOX2D_left(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box1 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(0); BOX2DFLOAT4 *box2 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(1); PG_RETURN_BOOL(FPlt(box1->xmax, box2->xmin)); } /* * box_right - is box1 strictly right of box2? */ PG_FUNCTION_INFO_V1(BOX2D_right); Datum BOX2D_right(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box1 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(0); BOX2DFLOAT4 *box2 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(1); PG_RETURN_BOOL(FPgt(box1->xmin, box2->xmax)); } /* * box_overright - is the left edge of box1 to the right of * the left edge of box2? */ PG_FUNCTION_INFO_V1(BOX2D_overright); Datum BOX2D_overright(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box1 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(0); BOX2DFLOAT4 *box2 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(1); PG_RETURN_BOOL(FPge(box1->xmin, box2->xmin)); } /* * box_overbelow - is the bottom edge of box1 below * the bottom edge of box2? */ PG_FUNCTION_INFO_V1(BOX2D_overbelow); Datum BOX2D_overbelow(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box1 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(0); BOX2DFLOAT4 *box2 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(1); PG_RETURN_BOOL(FPle(box1->ymin, box2->ymin)); } /* * box_below - is box1 strictly below box2? */ PG_FUNCTION_INFO_V1(BOX2D_below); Datum BOX2D_below(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box1 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(0); BOX2DFLOAT4 *box2 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(1); PG_RETURN_BOOL(FPlt(box1->ymax, box2->ymin)); } /* * box_above - is box1 strictly above box2? */ PG_FUNCTION_INFO_V1(BOX2D_above); Datum BOX2D_above(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box1 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(0); BOX2DFLOAT4 *box2 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(1); PG_RETURN_BOOL(FPgt(box1->ymin, box2->ymax)); } /* * box_overabove - the top edge of box1 above * the top edge of box2? */ PG_FUNCTION_INFO_V1(BOX2D_overabove); Datum BOX2D_overabove(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box1 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(0); BOX2DFLOAT4 *box2 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(1); PG_RETURN_BOOL(FPge(box1->ymax, box2->ymax)); } /* * box_contained - is box1 contained by box2? */ PG_FUNCTION_INFO_V1(BOX2D_contained); Datum BOX2D_contained(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box1 =(BOX2DFLOAT4 *) PG_GETARG_POINTER(0); BOX2DFLOAT4 *box2 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(1); PG_RETURN_BOOL(FPle(box1->xmax, box2->xmax) && FPge(box1->xmin, box2->xmin) && FPle(box1->ymax, box2->ymax) && FPge(box1->ymin, box2->ymin)); } /* * box_contain - does box1 contain box2? */ PG_FUNCTION_INFO_V1(BOX2D_contain); Datum BOX2D_contain(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box1 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(0); BOX2DFLOAT4 *box2 = (BOX2DFLOAT4 *) PG_GETARG_POINTER(1); PG_RETURN_BOOL(FPge(box1->xmax, box2->xmax) && FPle(box1->xmin, box2->xmin) && FPge(box1->ymax, box2->ymax) && FPle(box1->ymin, box2->ymin)); } PG_FUNCTION_INFO_V1(BOX2D_intersects); Datum BOX2D_intersects(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *a = (BOX2DFLOAT4 *) PG_GETARG_POINTER(0); BOX2DFLOAT4 *b = (BOX2DFLOAT4 *) PG_GETARG_POINTER(1); BOX2DFLOAT4 *n; n = (BOX2DFLOAT4 *) palloc(sizeof(BOX2DFLOAT4)); n->xmax = LWGEOM_Minf(a->xmax, b->xmax); n->ymax = LWGEOM_Minf(a->ymax, b->ymax); n->xmin = LWGEOM_Maxf(a->xmin, b->xmin); n->ymin = LWGEOM_Maxf(a->ymin, b->ymin); if (n->xmax < n->xmin || n->ymax < n->ymin) { pfree(n); /* Indicate "no intersection" by returning NULL pointer */ n = NULL; } PG_RETURN_POINTER(n); } /* * union of two BOX2Ds */ PG_FUNCTION_INFO_V1(BOX2D_union); Datum BOX2D_union(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *a = (BOX2DFLOAT4*) PG_GETARG_POINTER(0); BOX2DFLOAT4 *b = (BOX2DFLOAT4*) PG_GETARG_POINTER(1); BOX2DFLOAT4 *n; n = (BOX2DFLOAT4 *) lwalloc(sizeof(BOX2DFLOAT4)); if ( ! box2d_union_p(a,b,n) ) PG_RETURN_NULL(); PG_RETURN_POINTER(n); } /* * min(a,b) */ float LWGEOM_Minf(float a, float b) { if (aa) return b; return a; } PG_FUNCTION_INFO_V1(BOX2DFLOAT4_expand); Datum BOX2DFLOAT4_expand(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box = (BOX2DFLOAT4 *)PG_GETARG_POINTER(0); double d = PG_GETARG_FLOAT8(1); BOX2DFLOAT4 *result = (BOX2DFLOAT4 *)palloc(sizeof(BOX2DFLOAT4)); memcpy(result, box, sizeof(BOX2DFLOAT4)); expand_box2d(result, d); PG_RETURN_POINTER(result); } PG_FUNCTION_INFO_V1(BOX2DFLOAT4_to_BOX3D); Datum BOX2DFLOAT4_to_BOX3D(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box = (BOX2DFLOAT4 *)PG_GETARG_POINTER(0); BOX3D *result = palloc(sizeof(BOX3D)); box2df_to_box3d_p(box, result); PG_RETURN_POINTER(result); } #define KEEP_OBSOLETED_FUNX 1 #if KEEP_OBSOLETED_FUNX Datum BOX2DFLOAT4_xmin(PG_FUNCTION_ARGS); Datum BOX2DFLOAT4_ymin(PG_FUNCTION_ARGS); Datum BOX2DFLOAT4_xmax(PG_FUNCTION_ARGS); Datum BOX2DFLOAT4_ymax(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(BOX2DFLOAT4_xmin); Datum BOX2DFLOAT4_xmin(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box = (BOX2DFLOAT4 *)PG_GETARG_POINTER(0); PG_RETURN_FLOAT4(box->xmin); } PG_FUNCTION_INFO_V1(BOX2DFLOAT4_ymin); Datum BOX2DFLOAT4_ymin(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box = (BOX2DFLOAT4 *)PG_GETARG_POINTER(0); PG_RETURN_FLOAT4(box->ymin); } PG_FUNCTION_INFO_V1(BOX2DFLOAT4_xmax); Datum BOX2DFLOAT4_xmax(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box = (BOX2DFLOAT4 *)PG_GETARG_POINTER(0); PG_RETURN_FLOAT4(box->xmax); } PG_FUNCTION_INFO_V1(BOX2DFLOAT4_ymax); Datum BOX2DFLOAT4_ymax(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box = (BOX2DFLOAT4 *)PG_GETARG_POINTER(0); PG_RETURN_FLOAT4(box->ymax); } #endif PG_FUNCTION_INFO_V1(BOX2DFLOAT4_combine); Datum BOX2DFLOAT4_combine(PG_FUNCTION_ARGS) { Pointer box2d_ptr = PG_GETARG_POINTER(0); Pointer geom_ptr = PG_GETARG_POINTER(1); BOX2DFLOAT4 *a,*b; PG_LWGEOM *lwgeom; BOX2DFLOAT4 box, *result; if ( (box2d_ptr == NULL) && (geom_ptr == NULL) ) { PG_RETURN_NULL(); /* combine_box2d(null,null) => null */ } result = (BOX2DFLOAT4 *)palloc(sizeof(BOX2DFLOAT4)); if (box2d_ptr == NULL) { lwgeom = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); /* empty geom would make getbox2d_p return NULL */ if ( ! getbox2d_p(SERIALIZED_FORM(lwgeom), &box) ) PG_RETURN_NULL(); memcpy(result, &box, sizeof(BOX2DFLOAT4)); PG_RETURN_POINTER(result); } /* combine_bbox(BOX3D, null) => BOX3D */ if (geom_ptr == NULL) { memcpy(result, (char *)PG_GETARG_DATUM(0), sizeof(BOX2DFLOAT4)); PG_RETURN_POINTER(result); } /*combine_bbox(BOX3D, geometry) => union(BOX3D, geometry->bvol) */ lwgeom = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); if ( ! getbox2d_p(SERIALIZED_FORM(lwgeom), &box) ) { /* must be the empty geom */ memcpy(result, (char *)PG_GETARG_DATUM(0), sizeof(BOX2DFLOAT4)); PG_RETURN_POINTER(result); } a = (BOX2DFLOAT4 *)PG_GETARG_DATUM(0); b = &box; result->xmax = LWGEOM_Maxf(a->xmax, b->xmax); result->ymax = LWGEOM_Maxf(a->ymax, b->ymax); result->xmin = LWGEOM_Minf(a->xmin, b->xmin); result->ymin = LWGEOM_Minf(a->ymin, b->ymin); PG_RETURN_POINTER(result); } PG_FUNCTION_INFO_V1(BOX2DFLOAT4_to_LWGEOM); Datum BOX2DFLOAT4_to_LWGEOM(PG_FUNCTION_ARGS) { BOX2DFLOAT4 *box = (BOX2DFLOAT4 *)PG_GETARG_POINTER(0); POINT2D *pts = palloc(sizeof(POINT2D)*5); POINTARRAY *pa[1]; LWPOLY *poly; int wantbbox = 0; PG_LWGEOM *result; uchar *ser; /* Assign coordinates to POINT2D array */ pts[0].x = box->xmin; pts[0].y = box->ymin; pts[1].x = box->xmin; pts[1].y = box->ymax; pts[2].x = box->xmax; pts[2].y = box->ymax; pts[3].x = box->xmax; pts[3].y = box->ymin; pts[4].x = box->xmin; pts[4].y = box->ymin; /* Construct point array */ pa[0] = palloc(sizeof(POINTARRAY)); pa[0]->serialized_pointlist = (uchar *)pts; TYPE_SETZM(pa[0]->dims, 0, 0); pa[0]->npoints = 5; /* Construct polygon */ poly = lwpoly_construct(-1, NULL, 1, pa); /* Serialize polygon */ ser = lwpoly_serialize(poly); /* Construct PG_LWGEOM */ result = PG_LWGEOM_construct(ser, -1, wantbbox); PG_RETURN_POINTER(result); } PG_FUNCTION_INFO_V1(BOX2DFLOAT4_construct); Datum BOX2DFLOAT4_construct(PG_FUNCTION_ARGS) { PG_LWGEOM *min = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); PG_LWGEOM *max = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); BOX2DFLOAT4 *result = palloc(sizeof(BOX2DFLOAT4)); LWGEOM *minpoint, *maxpoint; POINT2D minp, maxp; minpoint = lwgeom_deserialize(SERIALIZED_FORM(min)); maxpoint = lwgeom_deserialize(SERIALIZED_FORM(max)); if ( TYPE_GETTYPE(minpoint->type) != POINTTYPE || TYPE_GETTYPE(maxpoint->type) != POINTTYPE ) { elog(ERROR, "BOX2DFLOAT4_construct: args must be points"); PG_RETURN_NULL(); } errorIfSRIDMismatch(minpoint->SRID, maxpoint->SRID); getPoint2d_p(((LWPOINT *)minpoint)->point, 0, &minp); getPoint2d_p(((LWPOINT *)maxpoint)->point, 0, &maxp); result->xmax = maxp.x; result->ymax = maxp.y; result->xmin = minp.x; result->ymin = minp.y; PG_RETURN_POINTER(result); }