/* * return -1 iff point outside polygon * return 0 iff point on boundary * return 1 iff point inside polygon */ int point_in_polygon(LWPOLY *polygon, LWPOINT *point) { int i, result, in_ring; POINT2D pt; POSTGIS_DEBUG(2, "point_in_polygon called."); getPoint2d_p(point->point, 0, &pt); /* assume bbox short-circuit has already been attempted */ /* everything is outside of an empty polygon */ if ( polygon->nrings == 0 ) return -1; in_ring = point_in_ring(polygon->rings[0], &pt); if ( in_ring == -1) /* outside the exterior ring */ { POSTGIS_DEBUG(3, "point_in_polygon: outside exterior ring."); return -1; } result = in_ring; for (i=1; i<polygon->nrings; i++) { in_ring = point_in_ring(polygon->rings[i], &pt); if (in_ring == 1) /* inside a hole => outside the polygon */ { POSTGIS_DEBUGF(3, "point_in_polygon: within hole %d.", i); return -1; } if (in_ring == 0) /* on the edge of a hole */ { POSTGIS_DEBUGF(3, "point_in_polygon: on edge of hole %d.", i); return 0; } } return result; /* -1 = outside, 0 = boundary, 1 = inside */ }
/* ** 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; }
/* Calculate the volume (in n-d units) of the GIDX */ static float gidx_volume(GIDX *a) { float result; int i; if ( a == NULL ) { /* elog(ERROR, "gidx_volume received a null argument"); */ return 0.0; } result = GIDX_GET_MAX(a,0) - GIDX_GET_MIN(a,0); for ( i = 1; i < GIDX_NDIMS(a); i++ ) result *= (GIDX_GET_MAX(a,i) - GIDX_GET_MIN(a,i)); POSTGIS_DEBUGF(5, "calculated volume of %s as %.12g", gidx_to_string(a), result); return result; }
/** * Support function. Based on two datums return true if * they satisfy the predicate and false otherwise. */ static int gserialized_datum_predicate_2d(Datum gs1, Datum gs2, box2df_predicate predicate) { BOX2DF b1, b2, *br1=NULL, *br2=NULL; POSTGIS_DEBUG(3, "entered function"); if (gserialized_datum_get_box2df_p(gs1, &b1) == LW_SUCCESS) br1 = &b1; if (gserialized_datum_get_box2df_p(gs2, &b2) == LW_SUCCESS) br2 = &b2; if ( predicate(br1, br2) ) { POSTGIS_DEBUGF(3, "got boxes %s and %s", br1 ? box2df_to_string(&b1) : "(null)", br2 ? box2df_to_string(&b2) : "(null)"); return LW_TRUE; } return LW_FALSE; }
/* 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; }
/** * Support function. Based on two datums return true if * they satisfy the predicate and false otherwise. */ static int gserialized_datum_predicate_2d(Datum gs1, Datum gs2, box2df_predicate predicate) { BOX2DF b1, b2; POSTGIS_DEBUG(3, "entered function"); /* Must be able to build box for each argument (ie, not empty geometry) and overlap boxes to return true. */ if ( (gserialized_datum_get_box2df_p(gs1, &b1) == LW_SUCCESS) && (gserialized_datum_get_box2df_p(gs2, &b2) == LW_SUCCESS) && predicate(&b1, &b2) ) { POSTGIS_DEBUGF(3, "got boxes %s and %s", box2df_to_string(&b1), box2df_to_string(&b2)); return LW_TRUE; } return LW_FALSE; }
/* Ensure all minimums are below maximums. */ static inline void box2df_validate(BOX2DF *b) { float tmp; POSTGIS_DEBUGF(5,"validating box2df (%s)", box2df_to_string(b)); if ( b->xmax < b->xmin ) { tmp = b->xmin; b->xmin = b->xmax; b->xmax = tmp; } if ( b->ymax < b->ymin ) { tmp = b->ymin; b->ymin = b->ymax; b->ymax = tmp; } return; }
Datum gserialized_distance_box_2d(PG_FUNCTION_ARGS) { BOX2DF b1, b2; Datum gs1 = PG_GETARG_DATUM(0); Datum gs2 = PG_GETARG_DATUM(1); POSTGIS_DEBUG(3, "entered function"); /* Must be able to build box for each argument (ie, not empty geometry). */ if ( (gserialized_datum_get_box2df_p(gs1, &b1) == LW_SUCCESS) && (gserialized_datum_get_box2df_p(gs2, &b2) == LW_SUCCESS) ) { double distance = box2df_distance(&b1, &b2); POSTGIS_DEBUGF(3, "got boxes %s and %s", box2df_to_string(&b1), box2df_to_string(&b2)); PG_RETURN_FLOAT8(distance); } PG_RETURN_FLOAT8(MAXFLOAT); }
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); } return result; }
/* Calculate the volume of the union of the boxes. Avoids creating an intermediate box. */ static float gidx_union_volume(GIDX *a, GIDX *b) { float result; int i; int ndims_a, ndims_b; POSTGIS_DEBUG(5,"entered function"); if ( a == NULL && b == NULL ) { elog(ERROR, "gidx_union_volume received two null arguments"); return 0.0; } if ( a == NULL ) return gidx_volume(b); if ( b == NULL ) return gidx_volume(a); /* Ensure 'a' has the most dimensions. */ gidx_dimensionality_check(&a, &b); ndims_a = GIDX_NDIMS(a); ndims_b = GIDX_NDIMS(b); /* Initialize with maximal length of first dimension. */ result = Max(GIDX_GET_MAX(a,0),GIDX_GET_MAX(b,0)) - Min(GIDX_GET_MIN(a,0),GIDX_GET_MIN(b,0)); /* Multiply by maximal length of remaining dimensions. */ for ( i = 1; i < ndims_b; i++ ) { result *= (Max(GIDX_GET_MAX(a,i),GIDX_GET_MAX(b,i)) - Min(GIDX_GET_MIN(a,i),GIDX_GET_MIN(b,i))); } /* Add in dimensions of higher dimensional box. */ for ( i = ndims_b; i < ndims_a; i++ ) { result *= (GIDX_GET_MAX(a,i) - GIDX_GET_MIN(a,i)); } POSTGIS_DEBUGF(5, "volume( %s union %s ) = %.12g", gidx_to_string(a), gidx_to_string(b), result); return result; }
Datum geography_gist_penalty(PG_FUNCTION_ARGS) { GISTENTRY *origentry = (GISTENTRY*) PG_GETARG_POINTER(0); GISTENTRY *newentry = (GISTENTRY*) PG_GETARG_POINTER(1); float *result = (float*) PG_GETARG_POINTER(2); GIDX *gbox_index_orig, *gbox_index_new; float size_union, size_orig; POSTGIS_DEBUG(4, "[GIST] 'penalty' function called"); gbox_index_orig = (GIDX*)DatumGetPointer(origentry->key); gbox_index_new = (GIDX*)DatumGetPointer(newentry->key); /* Drop out if we're dealing with null inputs. Shouldn't happen. */ if ( (gbox_index_orig == NULL) && (gbox_index_new == NULL) ) { POSTGIS_DEBUG(4, "[GIST] both inputs NULL! returning penalty of zero"); *result = 0.0; PG_RETURN_FLOAT8(*result); } /* Calculate the size difference of the boxes (volume difference in this case). */ size_union = gidx_union_volume(gbox_index_orig, gbox_index_new); size_orig = gidx_volume(gbox_index_orig); *result = size_union - size_orig; /* All things being equal, we prefer to expand small boxes rather than large boxes. NOTE: This code seemed to be causing badly balanced trees to be built and therefore has been commented out. Not sure why it didn't work, but it didn't. if( FP_IS_ZERO(*result) ) if( FP_IS_ZERO(size_orig) ) *result = 0.0; else *result = 1.0 - (1.0/(1.0 + size_orig)); else *result += 1.0; */ POSTGIS_DEBUGF(4, "[GIST] union size (%.12f), original size (%.12f), penalty (%.12f)", size_union, size_orig, *result); PG_RETURN_POINTER(result); }
/* 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; }
static void PROJ4SRSCacheDelete(MemoryContext context) { projPJ projection; /* Lookup the projPJ pointer in the global hash table so we can free it */ projection = GetPJHashEntry(context); if (!projection) elog(ERROR, "PROJ4SRSCacheDelete: Trying to delete non-existant projection object with MemoryContext key (%p)", (void *)context); POSTGIS_DEBUGF(3, "deleting projection object (%p) with MemoryContext key (%p)", projection, context); /* Free it */ pj_free(projection); /* Remove the hash entry as it is no longer needed */ DeletePJHashEntry(context); }
static PROJ4PortalCache *GetPROJ4SRSCache(FunctionCallInfoData *fcinfo) { PROJ4PortalCache *PROJ4Cache ; /* * If we have not already created PROJ4 cache for this portal * then create it */ if (fcinfo->flinfo->fn_extra == NULL) { MemoryContext old_context; old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); PROJ4Cache = palloc(sizeof(PROJ4PortalCache)); MemoryContextSwitchTo(old_context); if (PROJ4Cache) { int i; POSTGIS_DEBUGF(3, "Allocating PROJ4Cache for portal with transform() MemoryContext %p", fcinfo->flinfo->fn_mcxt); /* Put in any required defaults */ for (i = 0; i < PROJ4_CACHE_ITEMS; i++) { PROJ4Cache->PROJ4SRSCache[i].srid = SRID_UNKNOWN; PROJ4Cache->PROJ4SRSCache[i].projection = NULL; PROJ4Cache->PROJ4SRSCache[i].projection_mcxt = NULL; } PROJ4Cache->PROJ4SRSCacheCount = 0; PROJ4Cache->PROJ4SRSCacheContext = fcinfo->flinfo->fn_mcxt; /* Store the pointer in fcinfo->flinfo->fn_extra */ fcinfo->flinfo->fn_extra = PROJ4Cache; } } else { /* Use the existing cache */ PROJ4Cache = fcinfo->flinfo->fn_extra; } return PROJ4Cache ; }
static LWGEOM* parse_geojson_point(json_object *geojson, bool *hasz, int *root_srid) { LWGEOM *geom; POINTARRAY *pa; json_object* coords = NULL; POSTGIS_DEBUGF(3, "parse_geojson_point called with root_srid = %d.", *root_srid ); coords = findMemberByName( geojson, "coordinates" ); if ( ! coords ) geojson_lwerror("Unable to find 'coordinates' in GeoJSON string", 4); pa = ptarray_construct_empty(1, 0, 1); parse_geojson_coord(coords, hasz, pa); geom = (LWGEOM *) lwpoint_construct(*root_srid, NULL, pa); POSTGIS_DEBUG(2, "parse_geojson_point finished."); return geom; }
/* * Point is assumed to have an M value. * Return NULL if point is not in the given range (inclusive) * Return an LWPOINT *copy* otherwise. */ static LWGEOM * lwpoint_locate_between_m(LWPOINT *lwpoint, double m0, double m1) { POINT3DM p3dm; POSTGIS_DEBUGF(2, "lwpoint_locate_between called for lwpoint %p", lwpoint); lwpoint_getPoint3dm_p(lwpoint, &p3dm); if ( p3dm.m >= m0 && p3dm.m <= m1) { POSTGIS_DEBUG(3, " lwpoint... returning a clone of input"); return (LWGEOM *)lwpoint_clone(lwpoint); } else { POSTGIS_DEBUG(3, " lwpoint... returning a clone of input"); return NULL; } }
static void PreparedCacheDelete(MemoryContext context) { PrepGeomHashEntry* pghe; /* Lookup the hash entry pointer in the global hash table so we can free it */ pghe = GetPrepGeomHashEntry(context); if (!pghe) elog(ERROR, "PreparedCacheDelete: Trying to delete non-existant hash entry object with MemoryContext key (%p)", (void *)context); POSTGIS_DEBUGF(3, "deleting geom object (%p) and prepared geom object (%p) with MemoryContext key (%p)", pghe->geom, pghe->prepared_geom, context); /* Free them */ if ( pghe->prepared_geom ) GEOSPreparedGeom_destroy( pghe->prepared_geom ); if ( pghe->geom ) GEOSGeom_destroy( (GEOSGeometry *)pghe->geom ); /* Remove the hash entry as it is no longer needed */ DeletePrepGeomHashEntry(context); }
/* * Stick an array of points to the given gridspec. * Return "gridded" points in *outpts and their number in *outptsn. * * Two consecutive points falling on the same grid cell are collapsed * into one single point. * */ POINTARRAY * ptarray_grid(POINTARRAY *pa, gridspec *grid) { POINT4D pbuf; int ipn, opn; /* point numbers (input/output) */ POINTARRAY *dpa; POSTGIS_DEBUGF(2, "ptarray_grid called on %p", pa); dpa = ptarray_construct_empty(FLAGS_GET_Z(pa->flags),FLAGS_GET_M(pa->flags), pa->npoints); for (ipn=0, opn=0; ipn<pa->npoints; ++ipn) { getPoint4d_p(pa, ipn, &pbuf); if ( grid->xsize ) pbuf.x = rint((pbuf.x - grid->ipx)/grid->xsize) * grid->xsize + grid->ipx; if ( grid->ysize ) pbuf.y = rint((pbuf.y - grid->ipy)/grid->ysize) * grid->ysize + grid->ipy; if ( FLAGS_GET_Z(pa->flags) && grid->zsize ) pbuf.z = rint((pbuf.z - grid->ipz)/grid->zsize) * grid->zsize + grid->ipz; if ( FLAGS_GET_M(pa->flags) && grid->msize ) pbuf.m = rint((pbuf.m - grid->ipm)/grid->msize) * grid->msize + grid->ipm; ptarray_append_point(dpa, &pbuf, LW_FALSE); } return dpa; }
/* * Return a fully new allocated LWCOLLECTION * always tagged as COLLECTIONTYPE. */ static LWGEOM * lwcollection_locate_between_m(LWCOLLECTION *lwcoll, double m0, double m1) { int i; int ngeoms=0; LWGEOM **geoms; POSTGIS_DEBUGF(2, "lwcollection_locate_between_m called for lwcoll %p", lwcoll); geoms=lwalloc(sizeof(LWGEOM *)*lwcoll->ngeoms); for (i=0; i<lwcoll->ngeoms; i++) { LWGEOM *sub=lwgeom_locate_between_m(lwcoll->geoms[i], m0, m1); if ( sub != NULL ) geoms[ngeoms++] = sub; } if ( ngeoms == 0 ) return NULL; return (LWGEOM *)lwcollection_construct(COLLECTIONTYPE, lwcoll->srid, NULL, ngeoms, geoms); }
Datum LWGEOM_numinteriorrings_polygon(PG_FUNCTION_ARGS) { PG_LWGEOM *geom = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); LWGEOM_INSPECTED *inspected = NULL; LWGEOM *tmp = NULL; LWPOLY *poly = NULL; LWCURVEPOLY *curvepoly = NULL; int32 result; int i; POSTGIS_DEBUG(2, "LWGEOM_numinteriorrings called."); if (lwgeom_getType((uchar)SERIALIZED_FORM(geom)[0]) == CURVEPOLYTYPE) { tmp = (LWGEOM *)lwcurvepoly_deserialize(SERIALIZED_FORM(geom)); } else { inspected = lwgeom_inspect(SERIALIZED_FORM(geom)); for (i=0; i<inspected->ngeometries; i++) { tmp = lwgeom_getgeom_inspected(inspected, i); if (lwgeom_getType(tmp->type) == POLYGONTYPE || lwgeom_getType(tmp->type) == CURVEPOLYTYPE) break; } } if ( tmp == NULL ) { PG_FREE_IF_COPY(geom, 0); lwinspected_release(inspected); PG_RETURN_NULL(); } POSTGIS_DEBUGF(3, "Geometry of type %d found.", lwgeom_getType(tmp->type)); if (lwgeom_getType(tmp->type) == POLYGONTYPE) { poly = (LWPOLY *)tmp; /* Ok, now we have a polygon. Here is its number of holes */ result = poly->nrings-1; } else if (lwgeom_getType(tmp->type) == CURVEPOLYTYPE) { POSTGIS_DEBUG(3, "CurvePolygon found."); curvepoly = (LWCURVEPOLY *)tmp; result = curvepoly->nrings-1; } else { PG_FREE_IF_COPY(geom, 0); lwinspected_release(inspected); PG_RETURN_NULL(); } PG_FREE_IF_COPY(geom, 0); if (inspected != NULL) lwinspected_release(inspected); lwgeom_release((LWGEOM *)tmp); PG_RETURN_INT32(result); }
/* ** GetPrepGeomCache ** ** Pull the current prepared geometry from the cache or make ** one if there is not one available. Only prepare geometry ** if we are seeing a key for the second time. That way rapidly ** cycling keys don't cause too much preparing. */ PrepGeomCache* GetPrepGeomCache(FunctionCallInfoData *fcinfo, GSERIALIZED *pg_geom1, GSERIALIZED *pg_geom2) { MemoryContext old_context; PrepGeomCache* cache = fcinfo->flinfo->fn_extra; int copy_keys = 1; size_t pg_geom1_size = 0; size_t pg_geom2_size = 0; /* Make sure this isn't someone else's cache object. */ if ( cache && cache->type != 2 ) cache = NULL; if (!PrepGeomHash) CreatePrepGeomHash(); if ( pg_geom1 ) pg_geom1_size = VARSIZE(pg_geom1) + VARHDRSZ; if ( pg_geom2 ) pg_geom2_size = VARSIZE(pg_geom2) + VARHDRSZ; if ( cache == NULL) { /* ** Cache requested, but the cache isn't set up yet. ** Set it up, but don't prepare the geometry yet. ** That way if the next call is a cache miss we haven't ** wasted time preparing a geometry we don't need. */ PrepGeomHashEntry pghe; old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); cache = palloc(sizeof(PrepGeomCache)); MemoryContextSwitchTo(old_context); cache->type = 2; cache->prepared_geom = 0; cache->geom = 0; cache->argnum = 0; cache->pg_geom1 = 0; cache->pg_geom2 = 0; cache->pg_geom1_size = 0; cache->pg_geom2_size = 0; cache->context = MemoryContextCreate(T_AllocSetContext, 8192, &PreparedCacheContextMethods, fcinfo->flinfo->fn_mcxt, "PostGIS Prepared Geometry Context"); POSTGIS_DEBUGF(3, "GetPrepGeomCache: creating cache: %p", cache); pghe.context = cache->context; pghe.geom = 0; pghe.prepared_geom = 0; AddPrepGeomHashEntry( pghe ); fcinfo->flinfo->fn_extra = cache; POSTGIS_DEBUGF(3, "GetPrepGeomCache: adding context to hash: %p", cache); } else if ( pg_geom1 && cache->argnum != 2 && cache->pg_geom1_size == pg_geom1_size && memcmp(cache->pg_geom1, pg_geom1, pg_geom1_size) == 0) { if ( !cache->prepared_geom ) { /* ** Cache hit, but we haven't prepared our geometry yet. ** Prepare it. */ PrepGeomHashEntry* pghe; cache->geom = POSTGIS2GEOS( pg_geom1 ); cache->prepared_geom = GEOSPrepare( cache->geom ); cache->argnum = 1; POSTGIS_DEBUG(3, "GetPrepGeomCache: preparing obj in argument 1"); pghe = GetPrepGeomHashEntry(cache->context); pghe->geom = cache->geom; pghe->prepared_geom = cache->prepared_geom; POSTGIS_DEBUG(3, "GetPrepGeomCache: storing references to prepared obj in argument 1"); } else { /* ** Cache hit, and we're good to go. Do nothing. */ POSTGIS_DEBUG(3, "GetPrepGeomCache: cache hit, argument 1"); } /* We don't need new keys until we have a cache miss */ copy_keys = 0; } else if ( pg_geom2 && cache->argnum != 1 && cache->pg_geom2_size == pg_geom2_size && memcmp(cache->pg_geom2, pg_geom2, pg_geom2_size) == 0) { if ( !cache->prepared_geom ) { /* ** Cache hit on arg2, but we haven't prepared our geometry yet. ** Prepare it. */ PrepGeomHashEntry* pghe; cache->geom = POSTGIS2GEOS( pg_geom2 ); cache->prepared_geom = GEOSPrepare( cache->geom ); cache->argnum = 2; POSTGIS_DEBUG(3, "GetPrepGeomCache: preparing obj in argument 2"); pghe = GetPrepGeomHashEntry(cache->context); pghe->geom = cache->geom; pghe->prepared_geom = cache->prepared_geom; POSTGIS_DEBUG(3, "GetPrepGeomCache: storing references to prepared obj in argument 2"); } else { /* ** Cache hit, and we're good to go. Do nothing. */ POSTGIS_DEBUG(3, "GetPrepGeomCache: cache hit, argument 2"); } /* We don't need new keys until we have a cache miss */ copy_keys = 0; } else if ( cache->prepared_geom ) { /* ** No cache hits, so this must be a miss. ** Destroy the GEOS objects, empty the cache. */ PrepGeomHashEntry* pghe; pghe = GetPrepGeomHashEntry(cache->context); pghe->geom = 0; pghe->prepared_geom = 0; POSTGIS_DEBUGF(3, "GetPrepGeomCache: cache miss, argument %d", cache->argnum); GEOSPreparedGeom_destroy( cache->prepared_geom ); GEOSGeom_destroy( (GEOSGeometry *)cache->geom ); cache->prepared_geom = 0; cache->geom = 0; cache->argnum = 0; } if ( copy_keys && pg_geom1 ) { /* ** If this is a new key (cache miss) we flip into the function ** manager memory context and make a copy. We can't just store a pointer ** because this copy will be pfree'd at the end of this function ** call. */ POSTGIS_DEBUG(3, "GetPrepGeomCache: copying pg_geom1 into cache"); old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); if ( cache->pg_geom1 ) pfree(cache->pg_geom1); cache->pg_geom1 = palloc(pg_geom1_size); MemoryContextSwitchTo(old_context); memcpy(cache->pg_geom1, pg_geom1, pg_geom1_size); cache->pg_geom1_size = pg_geom1_size; } if ( copy_keys && pg_geom2 ) { POSTGIS_DEBUG(3, "GetPrepGeomCache: copying pg_geom2 into cache"); old_context = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); if ( cache->pg_geom2 ) pfree(cache->pg_geom2); cache->pg_geom2 = palloc(pg_geom2_size); MemoryContextSwitchTo(old_context); memcpy(cache->pg_geom2, pg_geom2, pg_geom2_size); cache->pg_geom2_size = pg_geom2_size; } return cache; }
static POINTARRAYSET ptarray_locate_between_m(POINTARRAY *ipa, double m0, double m1) { POINTARRAYSET ret; POINTARRAY *dpa=NULL; int i; ret.nptarrays=0; /* * We allocate space for as many pointarray as * segments in the input POINTARRAY, as worst * case is for each segment to cross the M range * window. * TODO: rework this to reduce used memory */ ret.ptarrays=lwalloc(sizeof(POINTARRAY *)*ipa->npoints-1); POSTGIS_DEBUGF(2, "ptarray_locate...: called for pointarray %p, m0:%g, m1:%g", ipa, m0, m1); for (i=1; i<ipa->npoints; i++) { POINT4D p1, p2; int clipval; getPoint4d_p(ipa, i-1, &p1); getPoint4d_p(ipa, i, &p2); POSTGIS_DEBUGF(3, " segment %d-%d [ %g %g %g %g - %g %g %g %g ]", i-1, i, p1.x, p1.y, p1.z, p1.m, p2.x, p2.y, p2.z, p2.m); clipval = clip_seg_by_m_range(&p1, &p2, m0, m1); /* segment completely outside, nothing to do */ if (! clipval ) continue; POSTGIS_DEBUGF(3, " clipped to: [ %g %g %g %g - %g %g %g %g ] clipval: %d", p1.x, p1.y, p1.z, p1.m, p2.x, p2.y, p2.z, p2.m, clipval); /* If no points have been accumulated so far, then if clipval != 0 the first point must be the start of the intersection */ if (dpa == NULL) { POSTGIS_DEBUGF(3, " 1 creating new POINTARRAY with first point %g,%g,%g,%g", p1.x, p1.y, p1.z, p1.m); dpa = ptarray_construct_empty(FLAGS_GET_Z(ipa->flags), FLAGS_GET_M(ipa->flags), ipa->npoints-i); ptarray_append_point(dpa, &p1, LW_TRUE); } /* Otherwise always add the next point, avoiding duplicates */ if (dpa) ptarray_append_point(dpa, &p2, LW_FALSE); /* * second point has been clipped */ if ( clipval & 0x0100 || i == ipa->npoints-1 ) { POSTGIS_DEBUGF(3, " closing pointarray %p with %d points", dpa, dpa->npoints); ret.ptarrays[ret.nptarrays++] = dpa; dpa = NULL; } } /* * if dpa!=NULL it means we didn't close it yet. * this should never happen. */ if ( dpa != NULL ) lwpgerror("Something wrong with algorithm"); return ret; }
Datum geom_from_geojson(PG_FUNCTION_ARGS) { #ifndef HAVE_LIBJSON elog(ERROR, "You need JSON-C for ST_GeomFromGeoJSON"); PG_RETURN_NULL(); #else /* HAVE_LIBJSON */ GSERIALIZED *geom; LWGEOM *lwgeom; text *geojson_input; int geojson_size; char *geojson; int root_srid=0; bool hasz=true; json_tokener* jstok = NULL; json_object* poObj = NULL; json_object* poObjSrs = NULL; /* Get the geojson stream */ if (PG_ARGISNULL(0)) PG_RETURN_NULL(); geojson_input = PG_GETARG_TEXT_P(0); geojson = text2cstring(geojson_input); geojson_size = VARSIZE(geojson_input) - VARHDRSZ; /* Begin to Parse json */ jstok = json_tokener_new(); poObj = json_tokener_parse_ex(jstok, geojson, -1); if( jstok->err != json_tokener_success) { char err[256]; snprintf(err, 256, "%s (at offset %d)", json_tokener_errors[jstok->err], jstok->char_offset); json_tokener_free(jstok); geojson_lwerror(err, 1); } json_tokener_free(jstok); poObjSrs = findMemberByName( poObj, "crs" ); if (poObjSrs != NULL) { json_object* poObjSrsType = findMemberByName( poObjSrs, "type" ); if (poObjSrsType != NULL) { json_object* poObjSrsProps = findMemberByName( poObjSrs, "properties" ); json_object* poNameURL = findMemberByName( poObjSrsProps, "name" ); const char* pszName = json_object_get_string( poNameURL ); root_srid = getSRIDbySRS(pszName); POSTGIS_DEBUGF(3, "getSRIDbySRS returned root_srid = %d.", root_srid ); } } lwgeom = parse_geojson(poObj, &hasz, &root_srid); lwgeom_add_bbox(lwgeom); if (root_srid && lwgeom->srid == -1) lwgeom->srid = root_srid; if (!hasz) { LWGEOM *tmp = lwgeom_force_2d(lwgeom); lwgeom_free(lwgeom); lwgeom = tmp; POSTGIS_DEBUG(2, "geom_from_geojson called."); } geom = geometry_serialize(lwgeom); lwgeom_free(lwgeom); PG_RETURN_POINTER(geom); #endif }
static void geojson_lwerror(char *msg, int error_code) { POSTGIS_DEBUGF(3, "ST_GeomFromGeoJSON ERROR %i", error_code); lwerror("%s", msg); }
/* * Line is assumed to have an M value. * * Return NULL if no parts of the line are in the given range (inclusive) * * Return an LWCOLLECTION with LWLINES and LWPOINT being consecutive * and isolated points on the line falling in the range. * * X,Y and Z (if present) ordinates are interpolated. * */ static LWGEOM * lwline_locate_between_m(LWLINE *lwline_in, double m0, double m1) { POINTARRAY *ipa=lwline_in->points; int i; LWGEOM **geoms; int ngeoms; int outtype; int typeflag=0; /* see flags below */ const int pointflag=0x01; const int lineflag=0x10; POINTARRAYSET paset=ptarray_locate_between_m(ipa, m0, m1); POSTGIS_DEBUGF(2, "lwline_locate_between called for lwline %p", lwline_in); POSTGIS_DEBUGF(3, " ptarray_locate... returned %d pointarrays", paset.nptarrays); if ( paset.nptarrays == 0 ) { return NULL; } ngeoms=paset.nptarrays; /* TODO: rework this to reduce used memory */ geoms=lwalloc(sizeof(LWGEOM *)*ngeoms); for (i=0; i<ngeoms; i++) { LWPOINT *lwpoint; LWLINE *lwline; POINTARRAY *pa=paset.ptarrays[i]; /* This is a point */ if ( pa->npoints == 1 ) { lwpoint = lwpoint_construct(lwline_in->srid, NULL, pa); geoms[i]=(LWGEOM *)lwpoint; typeflag|=pointflag; } /* This is a line */ else if ( pa->npoints > 1 ) { lwline = lwline_construct(lwline_in->srid, NULL, pa); geoms[i]=(LWGEOM *)lwline; typeflag|=lineflag; } /* This is a bug */ else { lwpgerror("ptarray_locate_between_m returned a POINARRAY set containing POINTARRAY with 0 points"); } } if ( ngeoms == 1 ) { return geoms[0]; } else { /* Choose best type */ if ( typeflag == 1 ) outtype=MULTIPOINTTYPE; else if ( typeflag == 2 ) outtype=MULTILINETYPE; else outtype = COLLECTIONTYPE; return (LWGEOM *)lwcollection_construct(outtype, lwline_in->srid, NULL, ngeoms, geoms); } }
static uint32 gserialized_typmod_in(ArrayType *arr, int is_geography) { uint32 typmod = 0; Datum *elem_values; int n = 0; int i = 0; if (ARR_ELEMTYPE(arr) != CSTRINGOID) ereport(ERROR, (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("typmod array must be type cstring[]"))); if (ARR_NDIM(arr) != 1) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("typmod array must be one-dimensional"))); if (ARR_HASNULL(arr)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("typmod array must not contain nulls"))); deconstruct_array(arr, CSTRINGOID, -2, false, 'c', /* hardwire cstring representation details */ &elem_values, NULL, &n); /* Set the SRID to the default value first */ if ( is_geography) TYPMOD_SET_SRID(typmod, SRID_DEFAULT); else TYPMOD_SET_SRID(typmod, SRID_UNKNOWN); for (i = 0; i < n; i++) { if ( i == 0 ) /* TYPE */ { char *s = DatumGetCString(elem_values[i]); uint8_t type = 0; int z = 0; int m = 0; if ( geometry_type_from_string(s, &type, &z, &m) == LW_FAILURE ) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Invalid geometry type modifier: %s", s))); } else { TYPMOD_SET_TYPE(typmod, type); if ( z ) TYPMOD_SET_Z(typmod); if ( m ) TYPMOD_SET_M(typmod); } } if ( i == 1 ) /* SRID */ { int srid = pg_atoi(DatumGetCString(elem_values[i]), sizeof(int32), '\0'); srid = clamp_srid(srid); POSTGIS_DEBUGF(3, "srid: %d", srid); if ( srid != SRID_UNKNOWN ) { TYPMOD_SET_SRID(typmod, srid); } } } pfree(elem_values); return typmod; }
/** * Check the consistency of the metadata we want to enforce in the typmod: * srid, type and dimensionality. If things are inconsistent, shut down the query. */ GSERIALIZED* postgis_valid_typmod(GSERIALIZED *gser, int32_t typmod) { int32 geom_srid = gserialized_get_srid(gser); int32 geom_type = gserialized_get_type(gser); int32 geom_z = gserialized_has_z(gser); int32 geom_m = gserialized_has_m(gser); int32 typmod_srid = TYPMOD_GET_SRID(typmod); int32 typmod_type = TYPMOD_GET_TYPE(typmod); int32 typmod_z = TYPMOD_GET_Z(typmod); int32 typmod_m = TYPMOD_GET_M(typmod); POSTGIS_DEBUG(2, "Entered function"); /* No typmod (-1) => no preferences */ if (typmod < 0) return gser; POSTGIS_DEBUGF(3, "Got geom(type = %d, srid = %d, hasz = %d, hasm = %d)", geom_type, geom_srid, geom_z, geom_m); POSTGIS_DEBUGF(3, "Got typmod(type = %d, srid = %d, hasz = %d, hasm = %d)", typmod_type, typmod_srid, typmod_z, typmod_m); /* * #3031: If a user is handing us a MULTIPOINT EMPTY but trying to fit it into * a POINT geometry column, there's a strong chance the reason she has * a MULTIPOINT EMPTY because we gave it to her during data dump, * converting the internal POINT EMPTY into a EWKB MULTIPOINT EMPTY * (because EWKB doesn't have a clean way to represent POINT EMPTY). * In such a case, it makes sense to turn the MULTIPOINT EMPTY back into a * point EMPTY, rather than throwing an error. */ if ( typmod_type == POINTTYPE && geom_type == MULTIPOINTTYPE && gserialized_is_empty(gser) ) { LWPOINT *empty_point = lwpoint_construct_empty(geom_srid, geom_z, geom_m); geom_type = POINTTYPE; pfree(gser); if ( gserialized_is_geodetic(gser) ) gser = geography_serialize(lwpoint_as_lwgeom(empty_point)); else gser = geometry_serialize(lwpoint_as_lwgeom(empty_point)); } /* Typmod has a preference for SRID? Geometry SRID had better match. */ if ( typmod_srid > 0 && typmod_srid != geom_srid ) { ereport(ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Geometry SRID (%d) does not match column SRID (%d)", geom_srid, typmod_srid) )); } /* Typmod has a preference for geometry type. */ if ( typmod_type > 0 && /* GEOMETRYCOLLECTION column can hold any kind of collection */ ((typmod_type == COLLECTIONTYPE && ! (geom_type == COLLECTIONTYPE || geom_type == MULTIPOLYGONTYPE || geom_type == MULTIPOINTTYPE || geom_type == MULTILINETYPE )) || /* Other types must be strictly equal. */ (typmod_type != geom_type)) ) { ereport(ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Geometry type (%s) does not match column type (%s)", lwtype_name(geom_type), lwtype_name(typmod_type)) )); } /* Mismatched Z dimensionality. */ if ( typmod_z && ! geom_z ) { ereport(ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Column has Z dimension but geometry does not" ))); } /* Mismatched Z dimensionality (other way). */ if ( geom_z && ! typmod_z ) { ereport(ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Geometry has Z dimension but column does not" ))); } /* Mismatched M dimensionality. */ if ( typmod_m && ! geom_m ) { ereport(ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Column has M dimension but geometry does not" ))); } /* Mismatched M dimensionality (other way). */ if ( geom_m && ! typmod_m ) { ereport(ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Geometry has M dimension but column does not" ))); } return gser; }
Datum check_authorization(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; char *colname; HeapTuple rettuple_ok; HeapTuple rettuple_fail; TupleDesc tupdesc; int SPIcode; char query[1024]; const char *pk_id = NULL; SPITupleTable *tuptable; HeapTuple tuple; char *lockcode; char *authtable = "authorization_table"; const char *op; #define ERRMSGLEN 256 char err_msg[ERRMSGLEN]; /* Make sure trigdata is pointing at what I expect */ if ( ! CALLED_AS_TRIGGER(fcinfo) ) { elog(ERROR,"check_authorization: not fired by trigger manager"); } if ( ! TRIGGER_FIRED_BEFORE(trigdata->tg_event) ) { elog(ERROR,"check_authorization: not fired *before* event"); } if ( TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event) ) { rettuple_ok = trigdata->tg_newtuple; rettuple_fail = NULL; op = "UPDATE"; } else if ( TRIGGER_FIRED_BY_DELETE(trigdata->tg_event) ) { rettuple_ok = trigdata->tg_trigtuple; rettuple_fail = NULL; op = "DELETE"; } else { elog(ERROR,"check_authorization: not fired by update or delete"); PG_RETURN_NULL(); } tupdesc = trigdata->tg_relation->rd_att; /* Connect to SPI manager */ SPIcode = SPI_connect(); if (SPIcode != SPI_OK_CONNECT) { elog(ERROR,"check_authorization: could not connect to SPI"); PG_RETURN_NULL() ; } colname = trigdata->tg_trigger->tgargs[0]; pk_id = SPI_getvalue(trigdata->tg_trigtuple, tupdesc, SPI_fnumber(tupdesc, colname)); POSTGIS_DEBUG(3, "check_authorization called"); sprintf(query,"SELECT authid FROM \"%s\" WHERE expires >= now() AND toid = '%d' AND rid = '%s'", authtable, trigdata->tg_relation->rd_id, pk_id); POSTGIS_DEBUGF(3 ,"about to execute :%s", query); SPIcode = SPI_exec(query,0); if (SPIcode !=SPI_OK_SELECT ) elog(ERROR,"couldnt execute to test for lock :%s",query); if (!SPI_processed ) { POSTGIS_DEBUGF(3, "there is NO lock on row '%s'", pk_id); SPI_finish(); return PointerGetDatum(rettuple_ok); } /* there is a lock - check to see if I have rights to it! */ tuptable = SPI_tuptable; tupdesc = tuptable->tupdesc; tuple = tuptable->vals[0]; lockcode = SPI_getvalue(tuple, tupdesc, 1); POSTGIS_DEBUGF(3, "there is a lock on row '%s' (auth: '%s').", pk_id, lockcode); /* * check to see if temp_lock_have_table table exists * (it might not exist if they own no locks) */ sprintf(query,"SELECT * FROM pg_class WHERE relname = 'temp_lock_have_table'"); SPIcode = SPI_exec(query,0); if (SPIcode != SPI_OK_SELECT ) elog(ERROR,"couldnt execute to test for lockkey temp table :%s",query); if (SPI_processed==0) { goto fail; } sprintf(query, "SELECT * FROM temp_lock_have_table WHERE xideq( transid, getTransactionID() ) AND lockcode ='%s'", lockcode); POSTGIS_DEBUGF(3, "about to execute :%s", query); SPIcode = SPI_exec(query,0); if (SPIcode != SPI_OK_SELECT ) elog(ERROR, "couldnt execute to test for lock aquire: %s", query); if (SPI_processed >0) { POSTGIS_DEBUG(3, "I own the lock - I can modify the row"); SPI_finish(); return PointerGetDatum(rettuple_ok); } fail: snprintf(err_msg, ERRMSGLEN, "%s where \"%s\" = '%s' requires authorization '%s'", op, colname, pk_id, lockcode); err_msg[ERRMSGLEN-1] = '\0'; #ifdef ABORT_ON_AUTH_FAILURE elog(ERROR, "%s", err_msg); #else elog(NOTICE, "%s", err_msg); #endif SPI_finish(); return PointerGetDatum(rettuple_fail); }
/* * Clip a segment by a range of measures. * Z and M values are interpolated in case of clipping. * * Returns a bitfield, flags being: * 0x0001 : segment intersects the range * 0x0010 : first point is modified * 0x0100 : second point is modified * * Values: * - 0 segment fully outside the range, no modifications * - 1 segment fully inside the range, no modifications * - 7 segment crosses the range, both points modified. * - 3 first point out, second in, first point modified * - 5 first point in, second out, second point modified */ static int clip_seg_by_m_range(POINT4D *p1, POINT4D *p2, double m0, double m1) { double dM0, dM1, dX, dY, dZ; POINT4D *tmp; int swapped=0; int ret=0; POSTGIS_DEBUGF(3, "m0: %g m1: %g", m0, m1); /* Handle corner case of m values being the same */ if ( p1->m == p2->m ) { /* out of range, no clipping */ if ( p1->m < m0 || p1->m > m1 ) return 0; /* inside range, no clipping */ return 1; } /* * Order points so that p1 has the smaller M */ if ( p1->m > p2->m ) { tmp=p2; p2=p1; p1=tmp; swapped=1; } /* * The M range is not intersected, segment * fully out of range, no clipping. */ if ( p2->m < m0 || p1->m > m1 ) return 0; /* * The segment is fully inside the range, * no clipping. */ if ( p1->m >= m0 && p2->m <= m1 ) return 1; /* * Segment intersects range, lets compute * the proportional location of the two * measures wrt p1/p2 m range. * * if p1 and p2 have the same measure * this should never be reached (either * both inside or both outside) * */ dM0=(m0-p1->m)/(p2->m-p1->m); /* delta-M0 */ dM1=(m1-p2->m)/(p2->m-p1->m); /* delta-M1 */ dX=p2->x-p1->x; dY=p2->y-p1->y; dZ=p2->z-p1->z; POSTGIS_DEBUGF(3, "dM0:%g dM1:%g", dM0, dM1); POSTGIS_DEBUGF(3, "dX:%g dY:%g dZ:%g", dX, dY, dZ); POSTGIS_DEBUGF(3, "swapped: %d", swapped); /* * First point out of range, project * it on the range */ if ( p1->m < m0 ) { /* * To prevent rounding errors, then if m0==m1 and p2 lies within the range, copy * p1 as a direct copy of p2 */ if (m0 == m1 && p2->m <= m1) { memcpy(p1, p2, sizeof(POINT4D)); POSTGIS_DEBUG(3, "Projected p1 on range (as copy of p2)"); } else { /* Otherwise interpolate coordinates */ p1->x += (dX*dM0); p1->y += (dY*dM0); p1->z += (dZ*dM0); p1->m = m0; POSTGIS_DEBUG(3, "Projected p1 on range"); } if ( swapped ) ret |= 0x0100; else ret |= 0x0010; } /* * Second point out of range, project * it on the range */ if ( p2->m > m1 ) { /* * To prevent rounding errors, then if m0==m1 and p1 lies within the range, copy * p2 as a direct copy of p1 */ if (m0 == m1 && p1->m >= m0) { memcpy(p2, p1, sizeof(POINT4D)); POSTGIS_DEBUG(3, "Projected p2 on range (as copy of p1)"); } else { /* Otherwise interpolate coordinates */ p2->x += (dX*dM1); p2->y += (dY*dM1); p2->z += (dZ*dM1); p2->m = m1; POSTGIS_DEBUG(3, "Projected p2 on range"); } if ( swapped ) ret |= 0x0010; else ret |= 0x0100; } /* Clipping occurred */ return ret; }