void lwgeom_add_bbox_deep(LWGEOM *lwgeom, GBOX *gbox) { if ( lwgeom_is_empty(lwgeom) ) return; FLAGS_SET_BBOX(lwgeom->flags, 1); if ( ! ( gbox || lwgeom->bbox ) ) { lwgeom->bbox = gbox_new(lwgeom->flags); lwgeom_calculate_gbox(lwgeom, lwgeom->bbox); } else if ( gbox && ! lwgeom->bbox ) { lwgeom->bbox = gbox_clone(gbox); } if ( lwgeom_is_collection(lwgeom) ) { int i; LWCOLLECTION *lwcol = (LWCOLLECTION*)lwgeom; for ( i = 0; i < lwcol->ngeoms; i++ ) { lwgeom_add_bbox_deep(lwcol->geoms[i], lwgeom->bbox); } } }
static int lwcollection_calculate_gbox(LWCOLLECTION *coll, GBOX *gbox) { GBOX subbox; int i; int result = G_FAILURE; int first = LW_TRUE; assert(coll); if ( coll->ngeoms == 0 ) return G_FAILURE; subbox.flags = gbox->flags; for ( i = 0; i < coll->ngeoms; i++ ) { if ( lwgeom_calculate_gbox((LWGEOM*)(coll->geoms[i]), &subbox) == G_FAILURE ) { continue; } else { if ( first ) { gbox_duplicate(&subbox, gbox); first = LW_FALSE; } else { gbox_merge(&subbox, gbox); } result = G_SUCCESS; } } return result; }
/** * 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; }
Datum BOX3D_combine(PG_FUNCTION_ARGS) { BOX3D *box = (BOX3D*)PG_GETARG_POINTER(0); GSERIALIZED *geom = (GSERIALIZED*)PG_DETOAST_DATUM(PG_GETARG_POINTER(1)); LWGEOM *lwgeom = NULL; BOX3D *result = NULL; GBOX gbox; int rv; /* Can't do anything with null inputs */ if ( (box == NULL) && (geom == NULL) ) PG_RETURN_NULL(); /* Null geometry but non-null box, return the box */ if (geom == NULL) { 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 fload, we *must* calculate */ lwgeom = lwgeom_from_gserialized(geom); 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 == NULL ) 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 == NULL ) { PG_FREE_IF_COPY(geom, 1); result = box3d_from_gbox(&gbox); 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); PG_FREE_IF_COPY(geom, 1); PG_RETURN_POINTER(result); }
/** We have to check for overlapping bboxes */ int lw_dist2d_check_overlap(LWGEOM *lwg1,LWGEOM *lwg2) { LWDEBUG(2, "lw_dist2d_check_overlap is called"); if ( ! lwg1->bbox ) lwgeom_calculate_gbox(lwg1, lwg1->bbox); if ( ! lwg2->bbox ) lwgeom_calculate_gbox(lwg2, lwg2->bbox); /*Check if the geometries intersect. */ if ((lwg1->bbox->xmax<lwg2->bbox->xmin||lwg1->bbox->xmin>lwg2->bbox->xmax||lwg1->bbox->ymax<lwg2->bbox->ymin||lwg1->bbox->ymin>lwg2->bbox->ymax)) { LWDEBUG(3, "geometries bboxes did not overlap"); return LW_FALSE; } LWDEBUG(3, "geometries bboxes overlap"); return LW_TRUE; }
/** * Ensure there's a box in the LWGEOM. * If the box is already there just return, * else compute it. */ void lwgeom_add_bbox(LWGEOM *lwgeom) { /* an empty LWGEOM has no bbox */ if ( lwgeom_is_empty(lwgeom) ) return; if ( lwgeom->bbox ) return; FLAGS_SET_BBOX(lwgeom->flags, 1); lwgeom->bbox = gbox_new(lwgeom->flags); lwgeom_calculate_gbox(lwgeom, lwgeom->bbox); }
/** * 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. */ static int gserialized_datum_get_box2df_p(Datum gsdatum, BOX2DF *box2df) { GSERIALIZED *gpart; uint8_t flags; int result = LW_SUCCESS; POSTGIS_DEBUG(4, "entered function"); /* ** The most info we need is the 8 bytes of serialized header plus the ** of floats necessary to hold the bounding box. */ gpart = (GSERIALIZED*)PG_DETOAST_DATUM_SLICE(gsdatum, 0, 8 + sizeof(BOX2DF)); flags = gpart->flags; POSTGIS_DEBUGF(4, "got flags %d", gpart->flags); /* Do we even have a serialized bounding box? */ if ( FLAGS_GET_BBOX(flags) ) { /* Yes! Copy it out into the box! */ POSTGIS_DEBUG(4, "copying box out of serialization"); memcpy(box2df, gpart->data, sizeof(BOX2DF)); result = LW_SUCCESS; } else { /* No, we need to calculate it from the full object. */ GBOX gbox; GSERIALIZED *g = (GSERIALIZED*)PG_DETOAST_DATUM(gsdatum); LWGEOM *lwgeom = lwgeom_from_gserialized(g); 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 = box2df_from_gbox_p(&gbox, box2df); } if ( result == LW_SUCCESS ) { POSTGIS_DEBUGF(4, "got box2df %s", box2df_to_string(box2df)); } return result; }
Datum LWGEOM_to_BOX3D(PG_FUNCTION_ARGS) { GSERIALIZED *geom = (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(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); PG_RETURN_POINTER(result); }
/** This function is used to create a vertical line used for cases where one if the geometries lacks z-values. The vertical line crosses the 2d point that is closest and the z-range is from maxz to minz in the geoemtrie that has z values. */ static LWGEOM* create_v_line(const LWGEOM *lwgeom,double x, double y, int srid) { LWPOINT *lwpoints[2]; GBOX gbox; int rv = lwgeom_calculate_gbox(lwgeom, &gbox); if ( rv == LW_FAILURE ) return NULL; lwpoints[0] = lwpoint_make3dz(srid, x, y, gbox.zmin); lwpoints[1] = lwpoint_make3dz(srid, x, y, gbox.zmax); return (LWGEOM *)lwline_from_ptarray(srid, 2, lwpoints); }
LWGEOM* lwgeom_from_gserialized(const GSERIALIZED *g) { uint8_t g_flags = 0; int32_t g_srid = 0; uint32_t g_type = 0; uint8_t *data_ptr = NULL; LWGEOM *lwgeom = NULL; GBOX bbox; size_t g_size = 0; assert(g); g_srid = gserialized_get_srid(g); g_flags = g->flags; g_type = gserialized_get_type(g); LWDEBUGF(4, "Got type %d (%s), srid=%d", g_type, lwtype_name(g_type), g_srid); data_ptr = (uint8_t*)g->data; if ( FLAGS_GET_BBOX(g_flags) ) data_ptr += gbox_serialized_size(g_flags); lwgeom = lwgeom_from_gserialized_buffer(data_ptr, g_flags, &g_size); if ( ! lwgeom ) lwerror("lwgeom_from_gserialized: unable create geometry"); /* Ooops! */ lwgeom->type = g_type; lwgeom->flags = g_flags; if ( gserialized_read_gbox_p(g, &bbox) == LW_SUCCESS ) { lwgeom->bbox = gbox_copy(&bbox); } else if ( lwgeom_needs_bbox(lwgeom) && (lwgeom_calculate_gbox(lwgeom, &bbox) == LW_SUCCESS) ) { lwgeom->bbox = gbox_copy(&bbox); } else { lwgeom->bbox = NULL; } lwgeom_set_srid(lwgeom, g_srid); return lwgeom; }
Datum LWGEOM_to_BOX2D(PG_FUNCTION_ARGS) { GSERIALIZED *geom = (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); LWGEOM *lwgeom = lwgeom_from_gserialized(geom); GBOX gbox; /* Cannot box empty! */ if ( lwgeom_is_empty(lwgeom) ) PG_RETURN_NULL(); /* Cannot calculate box? */ if ( lwgeom_calculate_gbox(lwgeom, &gbox) == LW_FAILURE ) PG_RETURN_NULL(); /* Strip out higher dimensions */ FLAGS_SET_Z(gbox.flags, 0); FLAGS_SET_M(gbox.flags, 0); PG_RETURN_POINTER(gbox_copy(&gbox)); }
/* ** Return a geohash string for the geometry. <http://geohash.org> ** Where the precision is non-positive, calculate a precision based on the ** bounds of the feature. Big features have loose precision. ** Small features have tight precision. */ char *lwgeom_geohash(const LWGEOM *lwgeom, int precision) { GBOX gbox; GBOX gbox_bounds; double lat, lon; int result; gbox_init(&gbox); gbox_init(&gbox_bounds); result = lwgeom_calculate_gbox(lwgeom, &gbox); if ( result == LW_FAILURE ) return NULL; /* Return error if we are being fed something outside our working bounds */ if ( gbox.xmin < -180 || gbox.ymin < -90 || gbox.xmax > 180 || gbox.ymax > 90 ) { lwerror("Geohash requires inputs in decimal degrees."); return NULL; } /* What is the center of our geometry bounds? We'll use that to ** approximate location. */ lon = gbox.xmin + (gbox.xmax - gbox.xmin) / 2; lat = gbox.ymin + (gbox.ymax - gbox.ymin) / 2; if ( precision <= 0 ) { precision = lwgeom_geohash_precision(gbox, &gbox_bounds); } /* ** Return the geohash of the center, with a precision determined by the ** extent of the bounds. ** Possible change: return the point at the center of the precision bounds? */ return geohash_point(lon, lat, precision); }
/* ** Peak into a geography to find the bounding box. If the ** box is there, copy it out and return it. If not, calculate the box from the ** full geography and return the box based on that. If no box is available, ** return LW_FAILURE, otherwise LW_SUCCESS. */ int gserialized_get_gidx_p(GSERIALIZED *g, GIDX *gidx) { int result = LW_SUCCESS; POSTGIS_DEBUG(4, "entered function"); POSTGIS_DEBUGF(4, "got flags %d", g->flags); if ( FLAGS_GET_BBOX(g->flags) ) { int ndims = FLAGS_NDIMS_GIDX(g->flags); const size_t size = 2 * ndims * sizeof(float); POSTGIS_DEBUG(4, "copying box out of serialization"); memcpy(gidx->c, g->data, size); SET_VARSIZE(gidx, VARHDRSZ + size); } else { /* No, we need to calculate it from the full object. */ 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; }
/** * Read the bounding box off a serialization and calculate one if * it is not already there. */ int gserialized_get_gbox_p(const GSERIALIZED *g, GBOX *box) { /* Try to just read the serialized box. */ if ( gserialized_read_gbox_p(g, box) == LW_SUCCESS ) { return LW_SUCCESS; } /* No box? Try to peek into simpler geometries and */ /* derive a box without creating an lwgeom */ else if ( gserialized_peek_gbox_p(g, box) == LW_SUCCESS ) { return LW_SUCCESS; } /* Damn! Nothing for it but to create an lwgeom... */ /* See http://trac.osgeo.org/postgis/ticket/1023 */ else { LWGEOM *lwgeom = lwgeom_from_gserialized(g); int ret = lwgeom_calculate_gbox(lwgeom, box); gbox_float_round(box); lwgeom_free(lwgeom); return ret; } }
/** * Takes a potentially heterogeneous collection and returns a homogeneous * collection consisting only of the specified type. */ LWCOLLECTION* lwcollection_extract(LWCOLLECTION *col, int type) { int i = 0; LWGEOM **geomlist; LWCOLLECTION *outcol; int geomlistsize = 16; int geomlistlen = 0; uint8_t outtype; if ( ! col ) return NULL; switch (type) { case POINTTYPE: outtype = MULTIPOINTTYPE; break; case LINETYPE: outtype = MULTILINETYPE; break; case POLYGONTYPE: outtype = MULTIPOLYGONTYPE; break; default: lwerror("Only POLYGON, LINESTRING and POINT are supported by lwcollection_extract. %s requested.", lwtype_name(type)); return NULL; } geomlist = lwalloc(sizeof(LWGEOM*) * geomlistsize); /* Process each sub-geometry */ for ( i = 0; i < col->ngeoms; i++ ) { int subtype = col->geoms[i]->type; /* Don't bother adding empty sub-geometries */ if ( lwgeom_is_empty(col->geoms[i]) ) { continue; } /* Copy our sub-types into the output list */ if ( subtype == type ) { /* We've over-run our buffer, double the memory segment */ if ( geomlistlen == geomlistsize ) { geomlistsize *= 2; geomlist = lwrealloc(geomlist, sizeof(LWGEOM*) * geomlistsize); } geomlist[geomlistlen] = lwgeom_clone(col->geoms[i]); geomlistlen++; } /* Recurse into sub-collections */ if ( lwtype_is_collection( subtype ) ) { int j = 0; LWCOLLECTION *tmpcol = lwcollection_extract((LWCOLLECTION*)col->geoms[i], type); for ( j = 0; j < tmpcol->ngeoms; j++ ) { /* We've over-run our buffer, double the memory segment */ if ( geomlistlen == geomlistsize ) { geomlistsize *= 2; geomlist = lwrealloc(geomlist, sizeof(LWGEOM*) * geomlistsize); } geomlist[geomlistlen] = tmpcol->geoms[j]; geomlistlen++; } lwfree(tmpcol); } } if ( geomlistlen > 0 ) { GBOX gbox; outcol = lwcollection_construct(outtype, col->srid, NULL, geomlistlen, geomlist); lwgeom_calculate_gbox((LWGEOM *) outcol, &gbox); outcol->bbox = gbox_copy(&gbox); } else { lwfree(geomlist); outcol = lwcollection_construct_empty(outtype, col->srid, FLAGS_GET_Z(col->flags), FLAGS_GET_M(col->flags)); } return outcol; }