From 4520ba67691fc13bb8be2cc47dd0bf8c7ab94205 Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Mon, 15 Dec 2014 17:02:49 +0200 Subject: [PATCH] Add point <-> polygon distance operator. Alexander Korotkov, reviewed by Emre Hasegeli. --- src/backend/utils/adt/geo_ops.c | 76 +++++++++++++++++++++------ src/include/catalog/pg_operator.h | 2 + src/include/catalog/pg_proc.h | 1 + src/include/utils/geo_decls.h | 1 + src/test/regress/expected/polygon.out | 11 ++++ src/test/regress/sql/polygon.sql | 7 +++ 6 files changed, 82 insertions(+), 16 deletions(-) diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c index 54391fd7ab..946dc2893d 100644 --- a/src/backend/utils/adt/geo_ops.c +++ b/src/backend/utils/adt/geo_ops.c @@ -73,6 +73,7 @@ static double dist_ps_internal(Point *pt, LSEG *lseg); static Point *line_interpt_internal(LINE *l1, LINE *l2); static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start); static Point *lseg_interpt_internal(LSEG *l1, LSEG *l2); +static double dist_ppoly_internal(Point *pt, POLYGON *poly); /* @@ -2415,6 +2416,9 @@ lseg_interpt(PG_FUNCTION_ARGS) * Minimum distance from one object to another. *-------------------------------------------------------------------*/ +/* + * Distance from a point to a line + */ Datum dist_pl(PG_FUNCTION_ARGS) { @@ -2431,6 +2435,9 @@ dist_pl_internal(Point *pt, LINE *line) HYPOT(line->A, line->B)); } +/* + * Distance from a point to a lseg + */ Datum dist_ps(PG_FUNCTION_ARGS) { @@ -2494,7 +2501,7 @@ dist_ps_internal(Point *pt, LSEG *lseg) } /* - ** Distance from a point to a path + * Distance from a point to a path */ Datum dist_ppath(PG_FUNCTION_ARGS) @@ -2550,6 +2557,9 @@ dist_ppath(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(result); } +/* + * Distance from a point to a box + */ Datum dist_pb(PG_FUNCTION_ARGS) { @@ -2566,7 +2576,9 @@ dist_pb(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(result); } - +/* + * Distance from a lseg to a line + */ Datum dist_sl(PG_FUNCTION_ARGS) { @@ -2589,7 +2601,9 @@ dist_sl(PG_FUNCTION_ARGS) PG_RETURN_FLOAT8(result); } - +/* + * Distance from a lseg to a box + */ Datum dist_sb(PG_FUNCTION_ARGS) { @@ -2608,7 +2622,9 @@ dist_sb(PG_FUNCTION_ARGS) PG_RETURN_DATUM(result); } - +/* + * Distance from a line to a box + */ Datum dist_lb(PG_FUNCTION_ARGS) { @@ -2625,21 +2641,53 @@ dist_lb(PG_FUNCTION_ARGS) 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; + + /* calculate distance to center, and subtract radius */ + result = dist_ppoly_internal(&circle->center, poly); + + 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; + + result = dist_ppoly_internal(point, poly); + + PG_RETURN_FLOAT8(result); +} + +static double +dist_ppoly_internal(Point *pt, POLYGON *poly) +{ + float8 result; float8 d; int i; LSEG seg; - if (point_inside(&(circle->center), poly->npts, poly->p) != 0) + if (point_inside(pt, poly->npts, poly->p) != 0) { #ifdef GEODEBUG - printf("dist_cpoly- center inside of polygon\n"); + printf("dist_ppoly_internal- point inside of polygon\n"); #endif PG_RETURN_FLOAT8(0.0); } @@ -2649,9 +2697,9 @@ dist_cpoly(PG_FUNCTION_ARGS) 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(&circle->center, &seg); + result = dist_ps_internal(pt, &seg); #ifdef GEODEBUG - printf("dist_cpoly- segment 0/n distance is %f\n", result); + printf("dist_ppoly_internal- segment 0/n distance is %f\n", result); #endif /* check distances for other segments */ @@ -2661,19 +2709,15 @@ dist_cpoly(PG_FUNCTION_ARGS) 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; - d = dist_ps_internal(&circle->center, &seg); + d = dist_ps_internal(pt, &seg); #ifdef GEODEBUG - printf("dist_cpoly- segment %d distance is %f\n", (i + 1), d); + printf("dist_ppoly_internal- segment %d distance is %f\n", (i + 1), d); #endif if (d < result) result = d; } - result -= circle->radius; - if (result < 0) - result = 0; - - PG_RETURN_FLOAT8(result); + return result; } diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index 3b827fca53..88c737b829 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -1016,6 +1016,8 @@ DATA(insert OID = 1521 ( "#" PGNSP PGUID l f f 0 604 23 0 0 poly_npo 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 = 3276 ( "<->" 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"); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e5912ea8fa..eace352ec0 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -844,6 +844,7 @@ DATA(insert OID = 726 ( dist_lb PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 70 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 = 3275 ( 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_ )); diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h index 60b5d01929..91610d8547 100644 --- a/src/include/utils/geo_decls.h +++ b/src/include/utils/geo_decls.h @@ -395,6 +395,7 @@ 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); diff --git a/src/test/regress/expected/polygon.out b/src/test/regress/expected/polygon.out index 33388eb909..9d3e07708b 100644 --- a/src/test/regress/expected/polygon.out +++ b/src/test/regress/expected/polygon.out @@ -279,3 +279,14 @@ SELECT '((200,800),(800,800),(800,200),(200,200))' && '(1000,1000,0,0)'::polygo 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 d95fa96447..b4d9539347 100644 --- a/src/test/regress/sql/polygon.sql +++ b/src/test/regress/sql/polygon.sql @@ -172,3 +172,10 @@ SELECT '((0,4),(6,4),(1,2),(6,0),(0,0))'::polygon && '((2,1),(2,3),(3,3),(3,1))' -- +-------+ 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; -- 2.40.0