/** * Peak into a #GSERIALIZED datum to find the bounding box. If the * box is there, copy it out and return it. If not, calculate the box from the * full object and return the box based on that. If no box is available, * return #LW_FAILURE, otherwise #LW_SUCCESS. */ int gserialized_datum_get_gidx_p(Datum gsdatum, GIDX *gidx) { GSERIALIZED *gpart; int result = LW_SUCCESS; POSTGIS_DEBUG(4, "entered function"); /* ** The most info we need is the 8 bytes of serialized header plus the 32 bytes ** of floats necessary to hold the 8 floats of the largest XYZM index ** bounding box, so 40 bytes. */ gpart = (GSERIALIZED*)PG_DETOAST_DATUM_SLICE(gsdatum, 0, 40); POSTGIS_DEBUGF(4, "got flags %d", gpart->flags); /* Do we even have a serialized bounding box? */ if ( FLAGS_GET_BBOX(gpart->flags) ) { /* Yes! Copy it out into the GIDX! */ size_t size = gbox_serialized_size(gpart->flags); POSTGIS_DEBUG(4, "copying box out of serialization"); memcpy(gidx->c, gpart->data, size); /* if M is present but Z is not, pad Z and shift M */ if ( FLAGS_GET_M(gpart->flags) && ! FLAGS_GET_Z(gpart->flags) ) { size += 2 * sizeof(float); GIDX_SET_MIN(gidx,3,GIDX_GET_MIN(gidx,2)); GIDX_SET_MAX(gidx,3,GIDX_GET_MAX(gidx,2)); GIDX_SET_MIN(gidx,2,-1*FLT_MAX); GIDX_SET_MAX(gidx,2,FLT_MAX); } SET_VARSIZE(gidx, VARHDRSZ + size); result = LW_SUCCESS; } else { /* No, we need to calculate it from the full object. */ GSERIALIZED *g = (GSERIALIZED*)PG_DETOAST_DATUM(gsdatum); LWGEOM *lwgeom = lwgeom_from_gserialized(g); GBOX gbox; if ( lwgeom_calculate_gbox(lwgeom, &gbox) == LW_FAILURE ) { POSTGIS_DEBUG(4, "could not calculate bbox, returning failure"); lwgeom_free(lwgeom); return LW_FAILURE; } lwgeom_free(lwgeom); result = gidx_from_gbox_p(gbox, gidx); } if ( result == LW_SUCCESS ) { POSTGIS_DEBUGF(4, "got gidx %s", gidx_to_string(gidx)); } return result; }
/* Enlarge b_union to contain b_new. If b_new contains more dimensions than b_union, expand b_union to contain those dimensions. */ static void gidx_merge(GIDX **b_union, GIDX *b_new) { int i, dims_union, dims_new; Assert(b_union); Assert(*b_union); Assert(b_new); dims_union = GIDX_NDIMS(*b_union); dims_new = GIDX_NDIMS(b_new); POSTGIS_DEBUGF(4, "merging gidx (%s) into gidx (%s)", gidx_to_string(b_new), gidx_to_string(*b_union)); if ( dims_new > dims_union ) { POSTGIS_DEBUGF(5, "reallocating b_union from %d dims to %d dims", dims_union, dims_new); *b_union = (GIDX*)repalloc(*b_union, GIDX_SIZE(dims_new)); SET_VARSIZE(*b_union, VARSIZE(b_new)); dims_union = dims_new; } for ( i = 0; i < dims_new; i++ ) { /* Adjust minimums */ GIDX_SET_MIN(*b_union, i, Min(GIDX_GET_MIN(*b_union,i),GIDX_GET_MIN(b_new,i))); /* Adjust maximums */ GIDX_SET_MAX(*b_union, i, Max(GIDX_GET_MAX(*b_union,i),GIDX_GET_MAX(b_new,i))); } POSTGIS_DEBUGF(5, "merge complete (%s)", gidx_to_string(*b_union)); return; }
/* ** GIDX expansion, make d units bigger in all dimensions. */ void gidx_expand(GIDX *a, float d) { int i; POSTGIS_DEBUG(5, "entered function"); if ( a == NULL ) return; for (i = 0; i < GIDX_NDIMS(a); i++) { GIDX_SET_MIN(a, i, GIDX_GET_MIN(a, i) - d); GIDX_SET_MAX(a, i, GIDX_GET_MAX(a, i) + d); } }
/* Ensure all minimums are below maximums. */ static inline void gidx_validate(GIDX *b) { int i; Assert(b); POSTGIS_DEBUGF(5,"validating gidx (%s)", gidx_to_string(b)); for ( i = 0; i < GIDX_NDIMS(b); i++ ) { if ( GIDX_GET_MIN(b,i) > GIDX_GET_MAX(b,i) ) { float tmp; tmp = GIDX_GET_MIN(b,i); GIDX_SET_MIN(b,i,GIDX_GET_MAX(b,i)); GIDX_SET_MAX(b,i,tmp); } } return; }
/* Convert a double-based GBOX into a float-based GIDX, ensuring the float box is larger than the double box */ static int gidx_from_gbox_p(GBOX box, GIDX *a) { int ndims; ndims = (FLAGS_GET_GEODETIC(box.flags) ? 3 : FLAGS_NDIMS(box.flags)); SET_VARSIZE(a, VARHDRSZ + ndims * 2 * sizeof(float)); GIDX_SET_MIN(a,0,nextDown_f(box.xmin)); GIDX_SET_MAX(a,0,nextUp_f(box.xmax)); GIDX_SET_MIN(a,1,nextDown_f(box.ymin)); GIDX_SET_MAX(a,1,nextUp_f(box.ymax)); /* Geodetic indexes are always 3d, geocentric x/y/z */ if ( FLAGS_GET_GEODETIC(box.flags) ) { GIDX_SET_MIN(a,2,nextDown_f(box.zmin)); GIDX_SET_MAX(a,2,nextUp_f(box.zmax)); } else { /* Cartesian with Z implies Z is third dimension */ if ( FLAGS_GET_Z(box.flags) ) { GIDX_SET_MIN(a,2,nextDown_f(box.zmin)); GIDX_SET_MAX(a,2,nextUp_f(box.zmax)); if ( FLAGS_GET_M(box.flags) ) { GIDX_SET_MIN(a,3,nextDown_f(box.mmin)); GIDX_SET_MAX(a,3,nextUp_f(box.mmax)); } } /* Unless there's no Z, in which case M is third dimension */ else if ( FLAGS_GET_M(box.flags) ) { GIDX_SET_MIN(a,2,nextDown_f(box.mmin)); GIDX_SET_MAX(a,2,nextUp_f(box.mmax)); } } POSTGIS_DEBUGF(5, "converted %s to %s", gbox_to_string(&box), gidx_to_string(a)); return G_SUCCESS; }
/* Convert a double-based GBOX into a float-based GIDX, ensuring the float box is larger than the double box */ static int gidx_from_gbox_p(GBOX box, GIDX *a) { int ndims; ndims = FLAGS_NDIMS_GIDX(box.flags); SET_VARSIZE(a, VARHDRSZ + ndims * 2 * sizeof(float)); GIDX_SET_MIN(a,0,next_float_down(box.xmin)); GIDX_SET_MAX(a,0,next_float_up(box.xmax)); GIDX_SET_MIN(a,1,next_float_down(box.ymin)); GIDX_SET_MAX(a,1,next_float_up(box.ymax)); /* Geodetic indexes are always 3d, geocentric x/y/z */ if ( FLAGS_GET_GEODETIC(box.flags) ) { GIDX_SET_MIN(a,2,next_float_down(box.zmin)); GIDX_SET_MAX(a,2,next_float_up(box.zmax)); } else { /* Cartesian with Z implies Z is third dimension */ if ( FLAGS_GET_Z(box.flags) ) { GIDX_SET_MIN(a,2,next_float_down(box.zmin)); GIDX_SET_MAX(a,2,next_float_up(box.zmax)); } /* M is always fourth dimension, we pad if needed */ if ( FLAGS_GET_M(box.flags) ) { if ( ! FLAGS_GET_Z(box.flags) ) { GIDX_SET_MIN(a,2,-1*FLT_MAX); GIDX_SET_MAX(a,2,FLT_MAX); } GIDX_SET_MIN(a,3,next_float_down(box.mmin)); GIDX_SET_MAX(a,3,next_float_up(box.mmax)); } } POSTGIS_DEBUGF(5, "converted %s to %s", gbox_to_string(&box), gidx_to_string(a)); return LW_SUCCESS; }