From dcd4033a2cbf3d9a7601a688037dcbe933dc5c93 Mon Sep 17 00:00:00 2001 From: Sandro Santilli Date: Thu, 26 Aug 2004 12:55:30 +0000 Subject: [PATCH] Added distance(geometry) and all support functions. git-svn-id: http://svn.osgeo.org/postgis/trunk@752 b70326c6-7e19-0410-871a-916f4a2858ee --- lwgeom/MISSING_OBJECTS | 6 +- lwgeom/lwgeom.h | 9 + lwgeom/lwgeom_functions_basic.c | 632 +++++++++++++++++++++++++++++++- lwgeom/lwpostgis.sql.in | 6 + 4 files changed, 638 insertions(+), 15 deletions(-) diff --git a/lwgeom/MISSING_OBJECTS b/lwgeom/MISSING_OBJECTS index 5af71a425..d40c46ff3 100644 --- a/lwgeom/MISSING_OBJECTS +++ b/lwgeom/MISSING_OBJECTS @@ -22,10 +22,11 @@ FUNC: KEEPING FUNCTION: [asbinary(geometry, text)] FUNC: KEEPING FUNCTION: [nrings(geometry)] FUNC: KEEPING FUNCTION: [translate(geometry, double precision, double precision, double precision)] FUNC: KEEPING FUNCTION: [envelope(geometry)] + FUNC: KEEPING FUNCTION: [max_distance(geometry, geometry)] + FUNC: KEEPING FUNCTION: [optimistic_overlap(geometry, geometry, double precision)] FUNC: KEEPING FUNCTION: [segmentize(geometry, double precision)] -FUNC: KEEPING FUNCTION: [distance(geometry, geometry)] FUNC: KEEPING FUNCTION: [polyfromtext(geometry, integer)] FUNC: KEEPING FUNCTION: [polygonfromtext(geometry, integer)] FUNC: KEEPING FUNCTION: [polygonfromtext(geometry)] @@ -67,14 +68,11 @@ FNCAST: KEEPING FNCAST geometry(chip) (see CAST) FNCAST: KEEPING FNCAST geometry(box3d) (see CAST) FNCAST: KEEPING FNCAST geometry(text) (see CAST) FNCAST: KEEPING FNCAST box3dtobox(box3d) (see CAST) - FUNC: KEEPING FUNCTION: [point_inside_circle(geometry, double precision, double precision, double precision)] - FUNC: KEEPING FUNCTION: [centroid(geometry)] FUNC: KEEPING FUNCTION: [isring(geometry)] FUNC: KEEPING FUNCTION: [pointonsurface(geometry)] FUNC: KEEPING FUNCTION: [collector(geometry, geometry)] - FUNC: KEEPING FUNCTION: [intersection(geometry, geometry)] FUNC: KEEPING FUNCTION: [buffer(geometry, double precision)] FUNC: KEEPING FUNCTION: [convexhull(geometry)] diff --git a/lwgeom/lwgeom.h b/lwgeom/lwgeom.h index 8234c5501..0b5870b77 100644 --- a/lwgeom/lwgeom.h +++ b/lwgeom/lwgeom.h @@ -113,6 +113,8 @@ extern void getPoint2d_p(POINTARRAY *pa, int n, char *point); // Note that if you cast to a higher dimensional point you'll // possibly corrupt the POINTARRAY. extern char *getPoint(POINTARRAY *pa, int n); +//--- here is a macro equivalent, for speed... +//#define getPoint(x,n) &( (x)->serialized_pointlist[((x)->ndims*8)*(n)] ) // constructs a POINTARRAY. @@ -646,3 +648,10 @@ extern float LWGEOM_Maxf(float a, float b); extern BOX3D *lw_geom_getBB_simple(char *serialized_form); + +#if ! defined(__MINGW32__) +#define max(a,b) ((a) > (b) ? (a) : (b)) +#define min(a,b) ((a) <= (b) ? (a) : (b)) +#endif +#define abs(a) ((a) < (0) ? (-a) : (a)) + diff --git a/lwgeom/lwgeom_functions_basic.c b/lwgeom/lwgeom_functions_basic.c index d09c32c63..71e14a50f 100644 --- a/lwgeom/lwgeom_functions_basic.c +++ b/lwgeom/lwgeom_functions_basic.c @@ -6,24 +6,13 @@ #include #include -#include "access/gist.h" -#include "access/itup.h" -#include "access/rtree.h" - #include "fmgr.h" #include "utils/elog.h" - #include "lwgeom.h" - - - - //#define DEBUG -#include "wktparse.h" - Datum combine_box2d(PG_FUNCTION_ARGS); Datum LWGEOM_mem_size(PG_FUNCTION_ARGS); Datum LWGEOM_summary(PG_FUNCTION_ARGS); @@ -39,6 +28,7 @@ Datum LWGEOM_perimeter_poly(PG_FUNCTION_ARGS); Datum LWGEOM_force_2d(PG_FUNCTION_ARGS); Datum LWGEOM_force_3d(PG_FUNCTION_ARGS); Datum LWGEOM_force_collection(PG_FUNCTION_ARGS); +Datum LWGEOM_mindistance2d(PG_FUNCTION_ARGS); // internal char * lwgeom_summary_recursive(char *serialized, int offset); @@ -52,9 +42,478 @@ double lwgeom_pointarray_length2d(POINTARRAY *pts); double lwgeom_pointarray_length(POINTARRAY *pts); void lwgeom_force2d_recursive(char *serialized, char *optr, int *retsize); void lwgeom_force3d_recursive(char *serialized, char *optr, int *retsize); +double distance2d_pt_pt(POINT2D *p1, POINT2D *p2); +double distance2d_pt_seg(POINT2D *p, POINT2D *A, POINT2D *B); +double distance2d_seg_seg(POINT2D *A, POINT2D *B, POINT2D *C, POINT2D *D); +double distance2d_pt_ptarray(POINT2D *p, POINTARRAY *pa); +double distance2d_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2); +int pt_in_ring_2d(POINT2D *p, POINTARRAY *ring); +int pt_in_poly_2d(POINT2D *p, LWPOLY *poly); +double distance2d_ptarray_poly(POINTARRAY *pa, LWPOLY *poly); +double distance2d_point_point(LWPOINT *point1, LWPOINT *point2); +double distance2d_point_line(LWPOINT *point, LWLINE *line); +double distance2d_line_line(LWLINE *line1, LWLINE *line2); +double distance2d_point_poly(LWPOINT *point, LWPOLY *poly); +double distance2d_poly_poly(LWPOLY *poly1, LWPOLY *poly2); +double distance2d_line_poly(LWLINE *line, LWPOLY *poly); +double lwgeom_mindistance2d_recursive(char *lw1, char *lw2); + /*------------------------------------------------------------------*/ +// pt_in_ring_2d(): crossing number test for a point in a polygon +// input: p = a point, +// pa = vertex points of a ring V[n+1] with V[n]=V[0] +// returns: 0 = outside, 1 = inside +// +// Our polygons have first and last point the same, +// +int pt_in_ring_2d(POINT2D *p, POINTARRAY *ring) +{ + int cn = 0; // the crossing number counter + int i; + POINT2D *v1; + +#ifdef DEBUG + elog(NOTICE, "pt_in_ring_2d called"); +#endif + + // loop through all edges of the polygon + v1 = (POINT2D *)getPoint(ring, 0); + for (i=0; inpoints-2; i++) + { + double vt; + POINT2D *v2 = (POINT2D *)getPoint(ring, i+1); + + // edge from vertex i to vertex i+1 + if + ( + // an upward crossing + ((v1->y <= p->y) && (v2->y > p->y)) + // a downward crossing + || ((v1->y > p->y) && (v2->y <= p->y)) + ) + { + + vt = (double)(p->y - v1->y) / (v2->y - v1->y); + + // P.x x < v1->x + vt * (v2->x - v1->x)) + { + // a valid crossing of y=p.y right of p.x + ++cn; + } + } + v1 = v2; + } +#ifdef DEBUG + elog(NOTICE, "pt_in_ring_2d returning %d", cn&1); +#endif + return (cn&1); // 0 if even (out), and 1 if odd (in) +} + +// true if point is in poly (and not in its holes) +int pt_in_poly_2d(POINT2D *p, LWPOLY *poly) +{ + int i; + + // Not in outer ring + if ( ! pt_in_ring_2d(p, poly->rings[0]) ) return 0; + + // Check holes + for (i=1; inrings; i++) + { + // Inside a hole + if ( pt_in_ring_2d(p, poly->rings[i]) ) return 0; + } + + return 1; // In outer ring, not in holes +} + +double distance2d_pt_pt(POINT2D *p1, POINT2D *p2) +{ + return sqrt( + (p2->x-p1->x) * (p2->x-p1->x) + (p2->y-p1->y) * (p2->y-p1->y) + ); +} + +//distance2d from p to line A->B +double distance2d_pt_seg(POINT2D *p, POINT2D *A, POINT2D *B) +{ + double r,s; + + //if start==end, then use pt distance + if ( ( A->x == B->x) && (A->y == B->y) ) + return distance2d_pt_pt(p,A); + + //otherwise, we use comp.graphics.algorithms Frequently Asked Questions method + + /*(1) AC dot AB + r = --------- + ||AB||^2 + r has the following meaning: + r=0 P = A + r=1 P = B + r<0 P is on the backward extension of AB + r>1 P is on the forward extension of AB + 0x-A->x) * (B->x-A->x) + (p->y-A->y) * (B->y-A->y) )/( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) ); + + if (r<0) return distance2d_pt_pt(p,A); + if (r>1) return distance2d_pt_pt(p,B); + + + /*(2) + (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) + s = ----------------------------- + L^2 + + Then the distance from C to P = |s|*L. + + */ + + s = ( (A->y-p->y)*(B->x-A->x)- (A->x-p->x)*(B->y-A->y) ) / + ( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) ); + + return abs(s) * sqrt( + (B->x-A->x)*(B->x-A->x) + (B->y-A->y)*(B->y-A->y) + ); +} + +// find the minimum 2d distance from AB to CD +double distance2d_seg_seg(POINT2D *A, POINT2D *B, POINT2D *C, POINT2D *D) +{ + + double s_top, s_bot,s; + double r_top, r_bot,r; + +#ifdef DEBUG + elog(NOTICE, "distance2d_seg_seg [%g,%g]->[%g,%g] by [%g,%g]->[%g,%g]", + A->x,A->y,B->x,B->y, C->x,C->y, D->x, D->y); +#endif + + + //A and B are the same point + if ( ( A->x == B->x) && (A->y == B->y) ) + return distance2d_pt_seg(A,C,D); + + //U and V are the same point + + if ( ( C->x == D->x) && (C->y == D->y) ) + return distance2d_pt_seg(D,A,B); + + // AB and CD are line segments + /* from comp.graphics.algo + + Solving the above for r and s yields + (Ay-Cy)(Dx-Cx)-(Ax-Cx)(Dy-Cy) + r = ----------------------------- (eqn 1) + (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx) + + (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay) + s = ----------------------------- (eqn 2) + (Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx) + Let P be the position vector of the intersection point, then + P=A+r(B-A) or + Px=Ax+r(Bx-Ax) + Py=Ay+r(By-Ay) + By examining the values of r & s, you can also determine some other limiting conditions: + If 0<=r<=1 & 0<=s<=1, intersection exists + r<0 or r>1 or s<0 or s>1 line segments do not intersect + If the denominator in eqn 1 is zero, AB & CD are parallel + If the numerator in eqn 1 is also zero, AB & CD are collinear. + + */ + r_top = (A->y-C->y)*(D->x-C->x) - (A->x-C->x)*(D->y-C->y) ; + r_bot = (B->x-A->x)*(D->y-C->y) - (B->y-A->y)*(D->x-C->x) ; + + s_top = (A->y-C->y)*(B->x-A->x) - (A->x-C->x)*(B->y-A->y); + s_bot = (B->x-A->x)*(D->y-C->y) - (B->y-A->y)*(D->x-C->x); + + if ( (r_bot==0) || (s_bot == 0) ) + { + return ( + min(distance2d_pt_seg(A,C,D), + min(distance2d_pt_seg(B,C,D), + min(distance2d_pt_seg(C,A,B), + distance2d_pt_seg(D,A,B)) + ) + ) + ); + } + s = s_top/s_bot; + r= r_top/r_bot; + + if ((r<0) || (r>1) || (s<0) || (s>1) ) + { + //no intersection + return ( + min(distance2d_pt_seg(A,C,D), + min(distance2d_pt_seg(B,C,D), + min(distance2d_pt_seg(C,A,B), + distance2d_pt_seg(D,A,B)) + ) + ) + ); + + } + else + return -0; //intersection exists + +} + +//search all the segments of pointarray to see which one is closest to p1 +//Returns minimum distance between point and pointarray +double distance2d_pt_ptarray(POINT2D *p, POINTARRAY *pa) +{ + double result = 0; + int t; + POINT2D *start, *end; + + start = (POINT2D *)getPoint(pa, 0); + + for (t=1; tnpoints; t++) + { + double dist; + end = (POINT2D *)getPoint(pa, t); + dist = distance2d_pt_seg(p, start, end); + if (t==1) result = dist; + else result = min(result, dist); + + if ( result == 0 ) return 0; + + start = end; + } + + return result; +} + +// test each segment of l1 against each segment of l2. Return min +double distance2d_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2) +{ + double result = 99999999999.9; + bool result_okay = FALSE; //result is a valid min + int t,u; + POINT2D *start,*end; + POINT2D *start2,*end2; + +#ifdef DEBUG + elog(NOTICE, "distance2d_ptarray_ptarray called (points: %d-%d)", + l1->npoints, l2->npoints); +#endif + + start = (POINT2D *)getPoint(l1, 0); + for (t=1; tnpoints; t++) //for each segment in L1 + { + end = (POINT2D *)getPoint(l1, t); + + start2 = (POINT2D *)getPoint(l2, 0); + for (u=1; unpoints; u++) //for each segment in L2 + { + double dist; + + end2 = (POINT2D *)getPoint(l2, u); + + dist = distance2d_seg_seg(start, end, start2, end2); +//printf("line_line; seg %i * seg %i, dist = %g\n",t,u,dist_this); + + if (result_okay) + result = min(result,dist); + else + { + result_okay = TRUE; + result = dist; + } + +#ifdef DEBUG + elog(NOTICE, " seg%d-seg%d dist: %f, mindist: %f", + t, u, dist, result); +#endif + + if (result <= 0) return 0; //intersection + + start2 = end2; + } + start = end; + } + + return result; +} + +// Brute force. +// Test line-ring distance against each ring. +// If there's an intersection (distance==0) then return 0 (crosses boundary). +// Otherwise, test to see if any point is inside outer rings of polygon, +// but not in inner rings. +// If so, return 0 (line inside polygon), +// otherwise return min distance to a ring (could be outside +// polygon or inside a hole) +double distance2d_ptarray_poly(POINTARRAY *pa, LWPOLY *poly) +{ + POINT2D *pt; + int i; + double mindist = 0; + +#ifdef DEBUG + elog(NOTICE, "distance2d_ptarray_poly called (%d rings)", poly->nrings); +#endif + + for (i=0; inrings; i++) + { + double dist = distance2d_ptarray_ptarray(pa, poly->rings[i]); + if (i) mindist = min(mindist, dist); + else mindist = dist; +#ifdef DEBUG + elog(NOTICE, " distance from ring %d: %f, mindist: %f", + i, dist, mindist); +#endif + + if ( mindist <= 0 ) return 0.0; // intersection + } + + // No intersection, have to check if a point is + // inside polygon + pt = (POINT2D *)getPoint(pa, 0); + + // Outside outer ring, so min distance to a ring + // is the actual min distance + if ( ! pt_in_ring_2d(pt, poly->rings[0]) ) return mindist; + + // Its in the outer ring. + // Have to check if its inside a hole + for (i=1; inrings; i++) + { + if ( pt_in_ring_2d(pt, poly->rings[i]) ) + { + // Its inside a hole, then the actual + // distance is the min ring distance + return mindist; + } + } + + return 0.0; // Not in hole, so inside polygon +} + +double distance2d_point_point(LWPOINT *point1, LWPOINT *point2) +{ + POINT2D *p1 = (POINT2D *)getPoint(point1->point, 0); + POINT2D *p2 = (POINT2D *)getPoint(point2->point, 0); + return distance2d_pt_pt(p1, p2); +} + +double distance2d_point_line(LWPOINT *point, LWLINE *line) +{ + POINT2D *p = (POINT2D *)getPoint(point->point, 0); + POINTARRAY *pa = line->points; + return distance2d_pt_ptarray(p, pa); +} + +double distance2d_line_line(LWLINE *line1, LWLINE *line2) +{ + POINTARRAY *pa1 = line1->points; + POINTARRAY *pa2 = line2->points; + return distance2d_ptarray_ptarray(pa1, pa2); +} + +// 1. see if pt in outer boundary. if no, then treat the outer ring like a line +// 2. if in the boundary, test to see if its in a hole. +// if so, then return dist to hole, else return 0 (point in polygon) +double distance2d_point_poly(LWPOINT *point, LWPOLY *poly) +{ + POINT2D *p = (POINT2D *)getPoint(point->point, 0); + int i; + +#ifdef DEBUG + elog(NOTICE, "distance2d_point_poly called"); +#endif + + // Return distance to outer ring if not inside it + if ( ! pt_in_ring_2d(p, poly->rings[0]) ) + { +#ifdef DEBUG + elog(NOTICE, " not inside outer-ring"); +#endif + return distance2d_pt_ptarray(p, poly->rings[0]); + } + + // Inside the outer ring. + // Scan though each of the inner rings looking to + // see if its inside. If not, distance==0. + // Otherwise, distance = pt to ring distance + for (i=1; inrings; i++) + { + // Inside a hole. Distance = pt -> ring + if ( pt_in_ring_2d(p, poly->rings[i]) ) + { +#ifdef DEBUG + elog(NOTICE, " inside an hole"); +#endif + return distance2d_pt_ptarray(p, poly->rings[i]); + } + } + +#ifdef DEBUG + elog(NOTICE, " inside the polygon"); +#endif + return 0.0; // Is inside the polygon +} + +// Brute force. +// Test to see if any rings intersect. +// If yes, dist=0. +// Test to see if one inside the other and if they are inside holes. +// Find min distance ring-to-ring. +double distance2d_poly_poly(LWPOLY *poly1, LWPOLY *poly2) +{ + POINT2D *pt; + double mindist = 0; + int i; + +#ifdef DEBUG + elog(NOTICE, "distance2d_poly_poly called"); +#endif + + // if poly1 inside poly2 return 0 + pt = (POINT2D *)getPoint(poly1->rings[0], 0); + if ( pt_in_poly_2d(pt, poly2) ) return 0.0; + + // if poly2 inside poly1 return 0 + pt = (POINT2D *)getPoint(poly2->rings[0], 0); + if ( pt_in_poly_2d(pt, poly1) ) return 0.0; + +#ifdef DEBUG + elog(NOTICE, " polys not inside each other"); +#endif + + //foreach ring in Poly1 + // foreach ring in Poly2 + // if intersect, return 0 + for (i=0; inrings; i++) + { + double dist = distance2d_ptarray_poly(poly1->rings[i], poly2); + if (i) mindist = min(mindist, dist); + else mindist = dist; + +#ifdef DEBUG + elog(NOTICE, " ring%d dist: %f, mindist: %f", i, dist, mindist); +#endif + + if ( mindist <= 0 ) return 0.0; // intersection + } + + // otherwise return closest approach of rings (no intersection) + return mindist; + +} + +double distance2d_line_poly(LWLINE *line, LWPOLY *poly) +{ + return distance2d_ptarray_poly(line->points, poly); +} + + //find the 2d length of the given POINTARRAY (even if it's 3d) double lwgeom_pointarray_length2d(POINTARRAY *pts) { @@ -160,6 +619,134 @@ double lwgeom_polygon_perimeter2d(LWPOLY *poly) return result; } +double +lwgeom_mindistance2d_recursive(char *lw1, char *lw2) +{ + LWGEOM_INSPECTED *in1, *in2; + int i, j; + double mindist = -1; + + in1 = lwgeom_inspect(lw1); + in2 = lwgeom_inspect(lw2); + + for (i=0; ingeometries; i++) + { + char *g1 = lwgeom_getsubgeometry_inspected(in1, i); + int t1 = lwgeom_getType(g1[0]); + double dist=0; + + // it's a multitype... recurse + if ( t1 >= 4 ) + { + dist = lwgeom_mindistance2d_recursive(g1, lw2); + if ( dist == 0 ) return 0.0; // can't be closer + if ( mindist == -1 ) mindist = dist; + else mindist = min(dist, mindist); + continue; + } + + for (j=0; jngeometries; j++) + { + char *g2 = lwgeom_getsubgeometry_inspected(in2, j); + int t2 = lwgeom_getType(g2[0]); + + if ( t1 == POINTTYPE ) + { + if ( t2 == POINTTYPE ) + { + dist = distance2d_point_point( + lwpoint_deserialize(g1), + lwpoint_deserialize(g2) + ); + } + else if ( t2 == LINETYPE ) + { + dist = distance2d_point_line( + lwpoint_deserialize(g1), + lwline_deserialize(g2) + ); + } + else if ( t2 == POLYGONTYPE ) + { + dist = distance2d_point_poly( + lwpoint_deserialize(g1), + lwpoly_deserialize(g2) + ); + } + } + else if ( t1 == LINETYPE ) + { + if ( t2 == POINTTYPE ) + { + dist = distance2d_point_line( + lwpoint_deserialize(g2), + lwline_deserialize(g1) + ); + } + else if ( t2 == LINETYPE ) + { + dist = distance2d_line_line( + lwline_deserialize(g1), + lwline_deserialize(g2) + ); + } + else if ( t2 == POLYGONTYPE ) + { + dist = distance2d_line_poly( + lwline_deserialize(g1), + lwpoly_deserialize(g2) + ); + } + } + else if ( t1 == POLYGONTYPE ) + { + if ( t2 == POLYGONTYPE ) + { + dist = distance2d_poly_poly( + lwpoly_deserialize(g2), + lwpoly_deserialize(g1) + ); + } + else if ( t2 == POINTTYPE ) + { + dist = distance2d_point_poly( + lwpoint_deserialize(g2), + lwpoly_deserialize(g1) + ); + } + else if ( t2 == LINETYPE ) + { + dist = distance2d_line_poly( + lwline_deserialize(g2), + lwpoly_deserialize(g1) + ); + } + } + else // it's a multitype... recurse + { + dist = lwgeom_mindistance2d_recursive(g1, g2); + } + + if (mindist == -1 ) mindist = dist; + else mindist = min(dist, mindist); + +#ifdef DEBUG + elog(NOTICE, "dist %d-%d: %f - mindist: %f", + t1, t2, dist, mindist); +#endif + + + if (mindist <= 0.0) return 0.0; // can't be closer + + } + + } + + if (mindist<0) mindist = 0; + + return mindist; +} + /*------------------------------------------------------------------*/ @@ -992,3 +1579,26 @@ Datum LWGEOM_force_collection(PG_FUNCTION_ARGS) PG_RETURN_POINTER(result); } + +// Minimum 2d distance between objects in geom1 and geom2. +// Returns null if it doesnt exist (future null-safe version). +PG_FUNCTION_INFO_V1(LWGEOM_mindistance2d); +Datum LWGEOM_mindistance2d(PG_FUNCTION_ARGS) +{ + + LWGEOM *geom1 = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); + LWGEOM *geom2 = (LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); + double mindist; + + if (lwgeom_getSRID(geom1) != lwgeom_getSRID(geom2)) + { + elog(ERROR,"Operation on two GEOMETRIES with different SRIDs\n"); + PG_RETURN_NULL(); + } + + mindist = lwgeom_mindistance2d_recursive(SERIALIZED_FORM(geom1), + SERIALIZED_FORM(geom2)); + + PG_RETURN_FLOAT8(mindist); +} + diff --git a/lwgeom/lwpostgis.sql.in b/lwgeom/lwpostgis.sql.in index ad2244330..40ac726ec 100644 --- a/lwgeom/lwpostgis.sql.in +++ b/lwgeom/lwpostgis.sql.in @@ -993,6 +993,12 @@ CREATEFUNCTION distance_spheroid(geometry,geometry,spheroid) AS '@MODULE_FILENAME@','LWGEOM_distance_ellipsoid_point' LANGUAGE 'C' WITH (isstrict); +-- Minimum distance. 2d only. +CREATEFUNCTION distance(geometry,geometry) + RETURNS float8 + AS '@MODULE_FILENAME@', 'LWGEOM_mindistance2d' + LANGUAGE 'C' WITH (isstrict,iscachable); + ------------------------------------------------------------------------ -- 2.40.0