diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c index 54391fd..402ea40 100644 --- a/src/backend/utils/adt/geo_ops.c +++ b/src/backend/utils/adt/geo_ops.c @@ -2408,36 +2408,42 @@ lseg_interpt(PG_FUNCTION_ARGS) ** Routines for position comparisons of differently-typed ** 2D objects. ** ***********************************************************************/ /*--------------------------------------------------------------------- * dist_ * Minimum distance from one object to another. *-------------------------------------------------------------------*/ +/* + * Distance from a point to a line + */ Datum dist_pl(PG_FUNCTION_ARGS) { Point *pt = PG_GETARG_POINT_P(0); LINE *line = PG_GETARG_LINE_P(1); PG_RETURN_FLOAT8(dist_pl_internal(pt, line)); } static double dist_pl_internal(Point *pt, LINE *line) { return fabs((line->A * pt->x + line->B * pt->y + line->C) / HYPOT(line->A, line->B)); } +/* + * Distance from a point to a lseg + */ Datum dist_ps(PG_FUNCTION_ARGS) { Point *pt = PG_GETARG_POINT_P(0); LSEG *lseg = PG_GETARG_LSEG_P(1); PG_RETURN_FLOAT8(dist_ps_internal(pt, lseg)); } static double @@ -2487,21 +2493,21 @@ dist_ps_internal(Point *pt, LSEG *lseg) result = point_dt(pt, &lseg->p[0]); tmpdist = point_dt(pt, &lseg->p[1]); if (tmpdist < result) result = tmpdist; } return result; } /* - ** Distance from a point to a path + * Distance from a point to a path */ Datum dist_ppath(PG_FUNCTION_ARGS) { Point *pt = PG_GETARG_POINT_P(0); PATH *path = PG_GETARG_PATH_P(1); float8 result = 0.0; /* keep compiler quiet */ bool have_min = false; float8 tmp; int i; @@ -2543,37 +2549,42 @@ dist_ppath(PG_FUNCTION_ARGS) { result = tmp; have_min = true; } } break; } PG_RETURN_FLOAT8(result); } +/* + * Distance from a point to a box + */ Datum dist_pb(PG_FUNCTION_ARGS) { Point *pt = PG_GETARG_POINT_P(0); BOX *box = PG_GETARG_BOX_P(1); float8 result; Point *near; near = DatumGetPointP(DirectFunctionCall2(close_pb, PointPGetDatum(pt), BoxPGetDatum(box))); result = point_dt(near, pt); PG_RETURN_FLOAT8(result); } - +/* + * Distance from a lseg to a line + */ Datum dist_sl(PG_FUNCTION_ARGS) { LSEG *lseg = PG_GETARG_LSEG_P(0); LINE *line = PG_GETARG_LINE_P(1); float8 result, d2; if (has_interpt_sl(lseg, line)) result = 0.0; @@ -2582,57 +2593,63 @@ dist_sl(PG_FUNCTION_ARGS) result = dist_pl_internal(&lseg->p[0], line); d2 = dist_pl_internal(&lseg->p[1], line); /* XXX shouldn't we take the min not max? */ if (d2 > result) result = d2; } PG_RETURN_FLOAT8(result); } - +/* + * Distance from a lseg to a box + */ Datum dist_sb(PG_FUNCTION_ARGS) { LSEG *lseg = PG_GETARG_LSEG_P(0); BOX *box = PG_GETARG_BOX_P(1); Point *tmp; Datum result; tmp = DatumGetPointP(DirectFunctionCall2(close_sb, LsegPGetDatum(lseg), BoxPGetDatum(box))); result = DirectFunctionCall2(dist_pb, PointPGetDatum(tmp), BoxPGetDatum(box)); PG_RETURN_DATUM(result); } - +/* + * Distance from a line to a box + */ Datum dist_lb(PG_FUNCTION_ARGS) { #ifdef NOT_USED LINE *line = PG_GETARG_LINE_P(0); BOX *box = PG_GETARG_BOX_P(1); #endif /* need to think about this one for a while */ ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function \"dist_lb\" not implemented"))); PG_RETURN_NULL(); } - +/* + * Distance from a circle to a polygon + */ Datum dist_cpoly(PG_FUNCTION_ARGS) { CIRCLE *circle = PG_GETARG_CIRCLE_P(0); POLYGON *poly = PG_GETARG_POLYGON_P(1); float8 result; float8 d; int i; LSEG seg; @@ -2669,20 +2686,69 @@ dist_cpoly(PG_FUNCTION_ARGS) result = d; } result -= circle->radius; if (result < 0) result = 0; PG_RETURN_FLOAT8(result); } +/* + * Distance from a point to a polygon + */ +Datum +dist_ppoly(PG_FUNCTION_ARGS) +{ + Point *point = PG_GETARG_POINT_P(0); + POLYGON *poly = PG_GETARG_POLYGON_P(1); + float8 result; + float8 distance; + int i; + LSEG seg; + + if (point_inside(point, poly->npts, poly->p) != 0) + { +#ifdef GEODEBUG + printf("dist_ppoly- point inside of polygon\n"); +#endif + PG_RETURN_FLOAT8(0.0); + } + + /* initialize distance with segment between first and last points */ + seg.p[0].x = poly->p[0].x; + seg.p[0].y = poly->p[0].y; + seg.p[1].x = poly->p[poly->npts - 1].x; + seg.p[1].y = poly->p[poly->npts - 1].y; + result = dist_ps_internal(point, &seg); +#ifdef GEODEBUG + printf("dist_ppoly- segment 0/n distance is %f\n", result); +#endif + + /* check distances for other segments */ + for (i = 0; i < poly->npts - 1; i++) + { + seg.p[0].x = poly->p[i].x; + seg.p[0].y = poly->p[i].y; + seg.p[1].x = poly->p[i + 1].x; + seg.p[1].y = poly->p[i + 1].y; + distance = dist_ps_internal(point, &seg); +#ifdef GEODEBUG + printf("dist_ppoly- segment %d distance is %f\n", i + 1, distance); +#endif + if (distance < result) + result = distance; + } + + PG_RETURN_FLOAT8(result); +} + /*--------------------------------------------------------------------- * interpt_ * Intersection point of objects. * We choose to ignore the "point" of intersection between * lines and boxes, since there are typically two. *-------------------------------------------------------------------*/ /* Get intersection point of lseg and line; returns NULL if no intersection */ static Point * diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index f8b4a65..c31b8a8 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -1009,20 +1009,22 @@ DATA(insert OID = 1518 ( "*" PGNSP PGUID b f f 718 600 718 0 0 circle_ DESCR("multiply"); DATA(insert OID = 1519 ( "/" PGNSP PGUID b f f 718 600 718 0 0 circle_div_pt - - )); DESCR("divide"); DATA(insert OID = 1520 ( "<->" PGNSP PGUID b f f 718 718 701 1520 0 circle_distance - - )); DESCR("distance between"); DATA(insert OID = 1521 ( "#" PGNSP PGUID l f f 0 604 23 0 0 poly_npoints - - )); DESCR("number of points"); DATA(insert OID = 1522 ( "<->" PGNSP PGUID b f f 600 718 701 0 0 dist_pc - - )); DESCR("distance between"); +DATA(insert OID = 3591 ( "<->" PGNSP PGUID b f f 600 604 701 0 0 dist_ppoly - - )); +DESCR("distance between"); DATA(insert OID = 1523 ( "<->" PGNSP PGUID b f f 718 604 701 0 0 dist_cpoly - - )); DESCR("distance between"); /* additional geometric operators - thomas 1997-07-09 */ DATA(insert OID = 1524 ( "<->" PGNSP PGUID b f f 628 603 701 0 0 dist_lb - - )); DESCR("distance between"); DATA(insert OID = 1525 ( "?#" PGNSP PGUID b f f 601 601 16 1525 0 lseg_intersect - - )); DESCR("intersect"); DATA(insert OID = 1526 ( "?||" PGNSP PGUID b f f 601 601 16 1526 0 lseg_parallel - - )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 0af1248..95f0b74 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -806,20 +806,21 @@ DESCR("set bit"); DATA(insert OID = 749 ( overlay PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 17 "17 17 23 23" _null_ _null_ _null_ _null_ byteaoverlay _null_ _null_ _null_ )); DESCR("substitute portion of string"); DATA(insert OID = 752 ( overlay PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 17 "17 17 23" _null_ _null_ _null_ _null_ byteaoverlay_no_len _null_ _null_ _null_ )); DESCR("substitute portion of string"); DATA(insert OID = 725 ( dist_pl PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "600 628" _null_ _null_ _null_ _null_ dist_pl _null_ _null_ _null_ )); DATA(insert OID = 726 ( dist_lb PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "628 603" _null_ _null_ _null_ _null_ dist_lb _null_ _null_ _null_ )); DATA(insert OID = 727 ( dist_sl PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "601 628" _null_ _null_ _null_ _null_ dist_sl _null_ _null_ _null_ )); DATA(insert OID = 728 ( dist_cpoly PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "718 604" _null_ _null_ _null_ _null_ dist_cpoly _null_ _null_ _null_ )); DATA(insert OID = 729 ( poly_distance PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "604 604" _null_ _null_ _null_ _null_ poly_distance _null_ _null_ _null_ )); +DATA(insert OID = 3590 ( dist_ppoly PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 701 "600 604" _null_ _null_ _null_ _null_ dist_ppoly _null_ _null_ _null_ )); DATA(insert OID = 740 ( text_lt PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_lt _null_ _null_ _null_ )); DATA(insert OID = 741 ( text_le PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_le _null_ _null_ _null_ )); DATA(insert OID = 742 ( text_gt PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_gt _null_ _null_ _null_ )); DATA(insert OID = 743 ( text_ge PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "25 25" _null_ _null_ _null_ _null_ text_ge _null_ _null_ _null_ )); DATA(insert OID = 745 ( current_user PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 19 "" _null_ _null_ _null_ _null_ current_user _null_ _null_ _null_ )); DESCR("current user name"); DATA(insert OID = 746 ( session_user PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 19 "" _null_ _null_ _null_ _null_ session_user _null_ _null_ _null_ )); DESCR("session user name"); diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h index 60b5d01..91610d8 100644 --- a/src/include/utils/geo_decls.h +++ b/src/include/utils/geo_decls.h @@ -388,20 +388,21 @@ extern Datum circle_contain_pt(PG_FUNCTION_ARGS); extern Datum pt_contained_circle(PG_FUNCTION_ARGS); extern Datum circle_add_pt(PG_FUNCTION_ARGS); extern Datum circle_sub_pt(PG_FUNCTION_ARGS); extern Datum circle_mul_pt(PG_FUNCTION_ARGS); extern Datum circle_div_pt(PG_FUNCTION_ARGS); extern Datum circle_diameter(PG_FUNCTION_ARGS); extern Datum circle_radius(PG_FUNCTION_ARGS); extern Datum circle_distance(PG_FUNCTION_ARGS); extern Datum dist_pc(PG_FUNCTION_ARGS); extern Datum dist_cpoly(PG_FUNCTION_ARGS); +extern Datum dist_ppoly(PG_FUNCTION_ARGS); extern Datum circle_center(PG_FUNCTION_ARGS); extern Datum cr_circle(PG_FUNCTION_ARGS); extern Datum box_circle(PG_FUNCTION_ARGS); extern Datum circle_box(PG_FUNCTION_ARGS); extern Datum poly_circle(PG_FUNCTION_ARGS); extern Datum circle_poly(PG_FUNCTION_ARGS); extern Datum circle_area(PG_FUNCTION_ARGS); /* support routines for the GiST access method (access/gist/gistproc.c) */ extern Datum gist_box_compress(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/polygon.out b/src/test/regress/expected/polygon.out index b252902..b449e32 100644 --- a/src/test/regress/expected/polygon.out +++ b/src/test/regress/expected/polygon.out @@ -271,10 +271,22 @@ SELECT '((1,4),(1,1),(4,1),(4,2),(2,2),(2,4),(1,4))'::polygon && '((3,3),(4,3),( ------- f (1 row) SELECT '((200,800),(800,800),(800,200),(200,200))' && '(1000,1000,0,0)'::polygon AS "true"; true ------ t (1 row) +-- distance from a point +SELECT + '(0,0)'::point <-> '((0,0),(1,2),(2,1))'::polygon as on_corner, + '(1,1)'::point <-> '((0,0),(2,2),(1,3))'::polygon as on_segment, + '(2,2)'::point <-> '((0,0),(1,4),(3,1))'::polygon as inside, + '(3,3)'::point <-> '((0,2),(2,0),(2,2))'::polygon as near_corner, + '(4,4)'::point <-> '((0,0),(0,3),(4,0))'::polygon as near_segment; + on_corner | on_segment | inside | near_corner | near_segment +-----------+------------+--------+-----------------+-------------- + 0 | 0 | 0 | 1.4142135623731 | 3.2 +(1 row) + diff --git a/src/test/regress/sql/polygon.sql b/src/test/regress/sql/polygon.sql index 2dad566..20a7c36 100644 --- a/src/test/regress/sql/polygon.sql +++ b/src/test/regress/sql/polygon.sql @@ -164,10 +164,18 @@ SELECT polygon '(2.0,0.0),(2.0,4.0),(0.0,0.0)' && polygon '(3.0,1.0),(3.0,3.0),( SELECT '((0,4),(6,4),(1,2),(6,0),(0,0))'::polygon && '((2,1),(2,3),(3,3),(3,1))'::polygon AS "true"; -- +--+ *--* -- | | | | -- | | *--* -- | +----+ -- | | -- +-------+ SELECT '((1,4),(1,1),(4,1),(4,2),(2,2),(2,4),(1,4))'::polygon && '((3,3),(4,3),(4,4),(3,4),(3,3))'::polygon AS "false"; SELECT '((200,800),(800,800),(800,200),(200,200))' && '(1000,1000,0,0)'::polygon AS "true"; + +-- distance from a point +SELECT + '(0,0)'::point <-> '((0,0),(1,2),(2,1))'::polygon as on_corner, + '(1,1)'::point <-> '((0,0),(2,2),(1,3))'::polygon as on_segment, + '(2,2)'::point <-> '((0,0),(1,4),(3,1))'::polygon as inside, + '(3,3)'::point <-> '((0,2),(2,0),(2,2))'::polygon as near_corner, + '(4,4)'::point <-> '((0,0),(0,3),(4,0))'::polygon as near_segment;