]> granicus.if.org Git - postgresql/commitdiff
Add point <-> polygon distance operator.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 15 Dec 2014 15:02:49 +0000 (17:02 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Mon, 15 Dec 2014 15:06:21 +0000 (17:06 +0200)
Alexander Korotkov, reviewed by Emre Hasegeli.

src/backend/utils/adt/geo_ops.c
src/include/catalog/pg_operator.h
src/include/catalog/pg_proc.h
src/include/utils/geo_decls.h
src/test/regress/expected/polygon.out
src/test/regress/sql/polygon.sql

index 54391fd7aba7d6ef67aedaca3a80a8ac906d67dd..946dc2893d5fa904b8711ee3d9b6eaec7da51d2e 100644 (file)
@@ -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;
 }
 
 
index 3b827fca53235443e6d12132bb72e73bbce2f106..88c737b82986b5b26b6630c6f58e5391c73a8186 100644 (file)
@@ -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");
 
index e5912ea8fa7c85165571368a5c94e2afa984515d..eace352ec0fdbf46f2bfc3ffcb38a89afb77867f 100644 (file)
@@ -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_ ));
index 60b5d0192997d8a095c3cfc39c10b7bd43d657e5..91610d85471e6b8f1b30e2be9b1c75347e472730 100644 (file)
@@ -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);
index 33388eb909314bcd82bf4735ef6609359af43afc..9d3e07708b18e232de5f8eeb1dee57f93f38d9a0 100644 (file)
@@ -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)
+
index d95fa9644762155129e3d3822bfef68cfa75e17a..b4d9539347568aa7a230daba65d1cefc20b745bf 100644 (file)
@@ -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;