Commit 4520ba67 authored by Heikki Linnakangas's avatar Heikki Linnakangas

Add point <-> polygon distance operator.

Alexander Korotkov, reviewed by Emre Hasegeli.
parent ee3bec5e
...@@ -73,6 +73,7 @@ static double dist_ps_internal(Point *pt, LSEG *lseg); ...@@ -73,6 +73,7 @@ static double dist_ps_internal(Point *pt, LSEG *lseg);
static Point *line_interpt_internal(LINE *l1, LINE *l2); static Point *line_interpt_internal(LINE *l1, LINE *l2);
static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start); static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start);
static Point *lseg_interpt_internal(LSEG *l1, LSEG *l2); 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) ...@@ -2415,6 +2416,9 @@ lseg_interpt(PG_FUNCTION_ARGS)
* Minimum distance from one object to another. * Minimum distance from one object to another.
*-------------------------------------------------------------------*/ *-------------------------------------------------------------------*/
/*
* Distance from a point to a line
*/
Datum Datum
dist_pl(PG_FUNCTION_ARGS) dist_pl(PG_FUNCTION_ARGS)
{ {
...@@ -2431,6 +2435,9 @@ dist_pl_internal(Point *pt, LINE *line) ...@@ -2431,6 +2435,9 @@ dist_pl_internal(Point *pt, LINE *line)
HYPOT(line->A, line->B)); HYPOT(line->A, line->B));
} }
/*
* Distance from a point to a lseg
*/
Datum Datum
dist_ps(PG_FUNCTION_ARGS) dist_ps(PG_FUNCTION_ARGS)
{ {
...@@ -2494,7 +2501,7 @@ dist_ps_internal(Point *pt, LSEG *lseg) ...@@ -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 Datum
dist_ppath(PG_FUNCTION_ARGS) dist_ppath(PG_FUNCTION_ARGS)
...@@ -2550,6 +2557,9 @@ dist_ppath(PG_FUNCTION_ARGS) ...@@ -2550,6 +2557,9 @@ dist_ppath(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result); PG_RETURN_FLOAT8(result);
} }
/*
* Distance from a point to a box
*/
Datum Datum
dist_pb(PG_FUNCTION_ARGS) dist_pb(PG_FUNCTION_ARGS)
{ {
...@@ -2566,7 +2576,9 @@ dist_pb(PG_FUNCTION_ARGS) ...@@ -2566,7 +2576,9 @@ dist_pb(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result); PG_RETURN_FLOAT8(result);
} }
/*
* Distance from a lseg to a line
*/
Datum Datum
dist_sl(PG_FUNCTION_ARGS) dist_sl(PG_FUNCTION_ARGS)
{ {
...@@ -2589,7 +2601,9 @@ dist_sl(PG_FUNCTION_ARGS) ...@@ -2589,7 +2601,9 @@ dist_sl(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(result); PG_RETURN_FLOAT8(result);
} }
/*
* Distance from a lseg to a box
*/
Datum Datum
dist_sb(PG_FUNCTION_ARGS) dist_sb(PG_FUNCTION_ARGS)
{ {
...@@ -2608,7 +2622,9 @@ dist_sb(PG_FUNCTION_ARGS) ...@@ -2608,7 +2622,9 @@ dist_sb(PG_FUNCTION_ARGS)
PG_RETURN_DATUM(result); PG_RETURN_DATUM(result);
} }
/*
* Distance from a line to a box
*/
Datum Datum
dist_lb(PG_FUNCTION_ARGS) dist_lb(PG_FUNCTION_ARGS)
{ {
...@@ -2625,21 +2641,53 @@ dist_lb(PG_FUNCTION_ARGS) ...@@ -2625,21 +2641,53 @@ dist_lb(PG_FUNCTION_ARGS)
PG_RETURN_NULL(); PG_RETURN_NULL();
} }
/*
* Distance from a circle to a polygon
*/
Datum Datum
dist_cpoly(PG_FUNCTION_ARGS) dist_cpoly(PG_FUNCTION_ARGS)
{ {
CIRCLE *circle = PG_GETARG_CIRCLE_P(0); CIRCLE *circle = PG_GETARG_CIRCLE_P(0);
POLYGON *poly = PG_GETARG_POLYGON_P(1); POLYGON *poly = PG_GETARG_POLYGON_P(1);
float8 result; 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; float8 d;
int i; int i;
LSEG seg; LSEG seg;
if (point_inside(&(circle->center), poly->npts, poly->p) != 0) if (point_inside(pt, poly->npts, poly->p) != 0)
{ {
#ifdef GEODEBUG #ifdef GEODEBUG
printf("dist_cpoly- center inside of polygon\n"); printf("dist_ppoly_internal- point inside of polygon\n");
#endif #endif
PG_RETURN_FLOAT8(0.0); PG_RETURN_FLOAT8(0.0);
} }
...@@ -2649,9 +2697,9 @@ dist_cpoly(PG_FUNCTION_ARGS) ...@@ -2649,9 +2697,9 @@ dist_cpoly(PG_FUNCTION_ARGS)
seg.p[0].y = poly->p[0].y; seg.p[0].y = poly->p[0].y;
seg.p[1].x = poly->p[poly->npts - 1].x; seg.p[1].x = poly->p[poly->npts - 1].x;
seg.p[1].y = poly->p[poly->npts - 1].y; 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 #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 #endif
/* check distances for other segments */ /* check distances for other segments */
...@@ -2661,19 +2709,15 @@ dist_cpoly(PG_FUNCTION_ARGS) ...@@ -2661,19 +2709,15 @@ dist_cpoly(PG_FUNCTION_ARGS)
seg.p[0].y = poly->p[i].y; seg.p[0].y = poly->p[i].y;
seg.p[1].x = poly->p[i + 1].x; seg.p[1].x = poly->p[i + 1].x;
seg.p[1].y = poly->p[i + 1].y; seg.p[1].y = poly->p[i + 1].y;
d = dist_ps_internal(&circle->center, &seg); d = dist_ps_internal(pt, &seg);
#ifdef GEODEBUG #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 #endif
if (d < result) if (d < result)
result = d; result = d;
} }
result -= circle->radius; return result;
if (result < 0)
result = 0;
PG_RETURN_FLOAT8(result);
} }
......
...@@ -1016,6 +1016,8 @@ DATA(insert OID = 1521 ( "#" PGNSP PGUID l f f 0 604 23 0 0 poly_npo ...@@ -1016,6 +1016,8 @@ DATA(insert OID = 1521 ( "#" PGNSP PGUID l f f 0 604 23 0 0 poly_npo
DESCR("number of points"); DESCR("number of points");
DATA(insert OID = 1522 ( "<->" PGNSP PGUID b f f 600 718 701 0 0 dist_pc - - )); DATA(insert OID = 1522 ( "<->" PGNSP PGUID b f f 600 718 701 0 0 dist_pc - - ));
DESCR("distance between"); 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 - - )); DATA(insert OID = 1523 ( "<->" PGNSP PGUID b f f 718 604 701 0 0 dist_cpoly - - ));
DESCR("distance between"); DESCR("distance between");
......
...@@ -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 ...@@ -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 = 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 = 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 = 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 = 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 = 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_ ));
......
...@@ -395,6 +395,7 @@ extern Datum circle_radius(PG_FUNCTION_ARGS); ...@@ -395,6 +395,7 @@ extern Datum circle_radius(PG_FUNCTION_ARGS);
extern Datum circle_distance(PG_FUNCTION_ARGS); extern Datum circle_distance(PG_FUNCTION_ARGS);
extern Datum dist_pc(PG_FUNCTION_ARGS); extern Datum dist_pc(PG_FUNCTION_ARGS);
extern Datum dist_cpoly(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 circle_center(PG_FUNCTION_ARGS);
extern Datum cr_circle(PG_FUNCTION_ARGS); extern Datum cr_circle(PG_FUNCTION_ARGS);
extern Datum box_circle(PG_FUNCTION_ARGS); extern Datum box_circle(PG_FUNCTION_ARGS);
......
...@@ -279,3 +279,14 @@ SELECT '((200,800),(800,800),(800,200),(200,200))' && '(1000,1000,0,0)'::polygo ...@@ -279,3 +279,14 @@ SELECT '((200,800),(800,800),(800,200),(200,200))' && '(1000,1000,0,0)'::polygo
t t
(1 row) (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)
...@@ -172,3 +172,10 @@ SELECT '((0,4),(6,4),(1,2),(6,0),(0,0))'::polygon && '((2,1),(2,3),(3,3),(3,1))' ...@@ -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 '((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"; 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;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment