aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2021-12-13 17:33:32 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2021-12-13 17:33:32 -0500
commitc5c192d7bdfa78f19e735610488b1cc5ad6e41cc (patch)
treee7d931fdbe4f11798408ca28ce7ff931e829b401
parent3f323956128ff8589ce4d3a14e8b950837831803 (diff)
downloadpostgresql-c5c192d7bdfa78f19e735610488b1cc5ad6e41cc.tar.gz
postgresql-c5c192d7bdfa78f19e735610488b1cc5ad6e41cc.zip
Implement poly_distance().
geo_ops.c contains half a dozen functions that are just stubs throwing ERRCODE_FEATURE_NOT_SUPPORTED. Since it's been like that for more than twenty years, there's clearly not a lot of interest in filling in the stubs. However, I'm uncomfortable with deleting poly_distance(), since every other geometric type supports a distance-to-another-object- of-the-same-type function. We can easily add this capability by cribbing from poly_overlap() and path_distance(). It's possible that the (existing) test case for this will show some numeric instability, but hopefully the buildfarm will expose it if so. In passing, improve the documentation to try to explain why polygons are distinct from closed paths in the first place. Discussion: https://postgr.es/m/3426566.1638832718@sss.pgh.pa.us
-rw-r--r--doc/src/sgml/datatype.sgml5
-rw-r--r--src/backend/utils/adt/geo_ops.c77
-rw-r--r--src/test/regress/expected/geometry.out54
3 files changed, 123 insertions, 13 deletions
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 6929f3bb183..0932c812da5 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -3562,8 +3562,9 @@ SELECT person.name, holidays.num_weeks FROM person, holidays
<para>
Polygons are represented by lists of points (the vertexes of the
- polygon). Polygons are very similar to closed paths, but are
- stored differently and have their own set of support routines.
+ polygon). Polygons are very similar to closed paths; the essential
+ difference is that a polygon is considered to include the area
+ within it, while a path is not.
</para>
<para>
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index 9484dbc2273..e9c347cb8c6 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -3798,11 +3798,9 @@ poly_same(PG_FUNCTION_ARGS)
/*-----------------------------------------------------------------
* Determine if polygon A overlaps polygon B
*-----------------------------------------------------------------*/
-Datum
-poly_overlap(PG_FUNCTION_ARGS)
+static bool
+poly_overlap_internal(POLYGON *polya, POLYGON *polyb)
{
- POLYGON *polya = PG_GETARG_POLYGON_P(0);
- POLYGON *polyb = PG_GETARG_POLYGON_P(1);
bool result;
Assert(polya->npts > 0 && polyb->npts > 0);
@@ -3854,6 +3852,18 @@ poly_overlap(PG_FUNCTION_ARGS)
}
}
+ return result;
+}
+
+Datum
+poly_overlap(PG_FUNCTION_ARGS)
+{
+ POLYGON *polya = PG_GETARG_POLYGON_P(0);
+ POLYGON *polyb = PG_GETARG_POLYGON_P(1);
+ bool result;
+
+ result = poly_overlap_internal(polya, polyb);
+
/*
* Avoid leaking memory for toasted inputs ... needed for rtree indexes
*/
@@ -4071,16 +4081,63 @@ pt_contained_poly(PG_FUNCTION_ARGS)
Datum
poly_distance(PG_FUNCTION_ARGS)
{
-#ifdef NOT_USED
POLYGON *polya = PG_GETARG_POLYGON_P(0);
POLYGON *polyb = PG_GETARG_POLYGON_P(1);
-#endif
+ float8 min = 0.0; /* initialize to keep compiler quiet */
+ bool have_min = false;
+ float8 tmp;
+ int i,
+ j;
+ LSEG seg1,
+ seg2;
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("function \"poly_distance\" not implemented")));
+ /*
+ * Distance is zero if polygons overlap. We must check this because the
+ * path distance will not give the right answer if one poly is entirely
+ * within the other.
+ */
+ if (poly_overlap_internal(polya, polyb))
+ PG_RETURN_FLOAT8(0.0);
- PG_RETURN_NULL();
+ /*
+ * When they don't overlap, the distance calculation is identical to that
+ * for closed paths (i.e., we needn't care about the fact that polygons
+ * include their contained areas). See path_distance().
+ */
+ for (i = 0; i < polya->npts; i++)
+ {
+ int iprev;
+
+ if (i > 0)
+ iprev = i - 1;
+ else
+ iprev = polya->npts - 1;
+
+ for (j = 0; j < polyb->npts; j++)
+ {
+ int jprev;
+
+ if (j > 0)
+ jprev = j - 1;
+ else
+ jprev = polyb->npts - 1;
+
+ statlseg_construct(&seg1, &polya->p[iprev], &polya->p[i]);
+ statlseg_construct(&seg2, &polyb->p[jprev], &polyb->p[j]);
+
+ tmp = lseg_closept_lseg(NULL, &seg1, &seg2);
+ if (!have_min || float8_lt(tmp, min))
+ {
+ min = tmp;
+ have_min = true;
+ }
+ }
+ }
+
+ if (!have_min)
+ PG_RETURN_NULL();
+
+ PG_RETURN_FLOAT8(min);
}
diff --git a/src/test/regress/expected/geometry.out b/src/test/regress/expected/geometry.out
index 974e2ec43a4..eb717d36434 100644
--- a/src/test/regress/expected/geometry.out
+++ b/src/test/regress/expected/geometry.out
@@ -4189,7 +4189,59 @@ SELECT p1.f1, p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2 WHERE p1.f1 |&> p2.f1;
-- Distance to polygon
SELECT p1.f1, p2.f1, p1.f1 <-> p2.f1 FROM POLYGON_TBL p1, POLYGON_TBL p2;
-ERROR: function "poly_distance" not implemented
+ f1 | f1 | ?column?
+----------------------------+----------------------------+----------------
+ ((2,0),(2,4),(0,0)) | ((2,0),(2,4),(0,0)) | 0
+ ((2,0),(2,4),(0,0)) | ((3,1),(3,3),(1,0)) | 0
+ ((2,0),(2,4),(0,0)) | ((1,2),(3,4),(5,6),(7,8)) | 0
+ ((2,0),(2,4),(0,0)) | ((7,8),(5,6),(3,4),(1,2)) | 0
+ ((2,0),(2,4),(0,0)) | ((1,2),(7,8),(5,6),(3,-4)) | 0
+ ((2,0),(2,4),(0,0)) | ((0,0)) | 0
+ ((2,0),(2,4),(0,0)) | ((0,1),(0,1)) | 0.4472135955
+ ((3,1),(3,3),(1,0)) | ((2,0),(2,4),(0,0)) | 0
+ ((3,1),(3,3),(1,0)) | ((3,1),(3,3),(1,0)) | 0
+ ((3,1),(3,3),(1,0)) | ((1,2),(3,4),(5,6),(7,8)) | 0.707106781187
+ ((3,1),(3,3),(1,0)) | ((7,8),(5,6),(3,4),(1,2)) | 0.707106781187
+ ((3,1),(3,3),(1,0)) | ((1,2),(7,8),(5,6),(3,-4)) | 0
+ ((3,1),(3,3),(1,0)) | ((0,0)) | 1
+ ((3,1),(3,3),(1,0)) | ((0,1),(0,1)) | 1.38675049056
+ ((1,2),(3,4),(5,6),(7,8)) | ((2,0),(2,4),(0,0)) | 0
+ ((1,2),(3,4),(5,6),(7,8)) | ((3,1),(3,3),(1,0)) | 0.707106781187
+ ((1,2),(3,4),(5,6),(7,8)) | ((1,2),(3,4),(5,6),(7,8)) | 0
+ ((1,2),(3,4),(5,6),(7,8)) | ((7,8),(5,6),(3,4),(1,2)) | 0
+ ((1,2),(3,4),(5,6),(7,8)) | ((1,2),(7,8),(5,6),(3,-4)) | 0
+ ((1,2),(3,4),(5,6),(7,8)) | ((0,0)) | 2.2360679775
+ ((1,2),(3,4),(5,6),(7,8)) | ((0,1),(0,1)) | 1.41421356237
+ ((7,8),(5,6),(3,4),(1,2)) | ((2,0),(2,4),(0,0)) | 0
+ ((7,8),(5,6),(3,4),(1,2)) | ((3,1),(3,3),(1,0)) | 0.707106781187
+ ((7,8),(5,6),(3,4),(1,2)) | ((1,2),(3,4),(5,6),(7,8)) | 0
+ ((7,8),(5,6),(3,4),(1,2)) | ((7,8),(5,6),(3,4),(1,2)) | 0
+ ((7,8),(5,6),(3,4),(1,2)) | ((1,2),(7,8),(5,6),(3,-4)) | 0
+ ((7,8),(5,6),(3,4),(1,2)) | ((0,0)) | 2.2360679775
+ ((7,8),(5,6),(3,4),(1,2)) | ((0,1),(0,1)) | 1.41421356237
+ ((1,2),(7,8),(5,6),(3,-4)) | ((2,0),(2,4),(0,0)) | 0
+ ((1,2),(7,8),(5,6),(3,-4)) | ((3,1),(3,3),(1,0)) | 0
+ ((1,2),(7,8),(5,6),(3,-4)) | ((1,2),(3,4),(5,6),(7,8)) | 0
+ ((1,2),(7,8),(5,6),(3,-4)) | ((7,8),(5,6),(3,4),(1,2)) | 0
+ ((1,2),(7,8),(5,6),(3,-4)) | ((1,2),(7,8),(5,6),(3,-4)) | 0
+ ((1,2),(7,8),(5,6),(3,-4)) | ((0,0)) | 1.58113883008
+ ((1,2),(7,8),(5,6),(3,-4)) | ((0,1),(0,1)) | 1.26491106407
+ ((0,0)) | ((2,0),(2,4),(0,0)) | 0
+ ((0,0)) | ((3,1),(3,3),(1,0)) | 1
+ ((0,0)) | ((1,2),(3,4),(5,6),(7,8)) | 2.2360679775
+ ((0,0)) | ((7,8),(5,6),(3,4),(1,2)) | 2.2360679775
+ ((0,0)) | ((1,2),(7,8),(5,6),(3,-4)) | 1.58113883008
+ ((0,0)) | ((0,0)) | 0
+ ((0,0)) | ((0,1),(0,1)) | 1
+ ((0,1),(0,1)) | ((2,0),(2,4),(0,0)) | 0.4472135955
+ ((0,1),(0,1)) | ((3,1),(3,3),(1,0)) | 1.38675049056
+ ((0,1),(0,1)) | ((1,2),(3,4),(5,6),(7,8)) | 1.41421356237
+ ((0,1),(0,1)) | ((7,8),(5,6),(3,4),(1,2)) | 1.41421356237
+ ((0,1),(0,1)) | ((1,2),(7,8),(5,6),(3,-4)) | 1.26491106407
+ ((0,1),(0,1)) | ((0,0)) | 1
+ ((0,1),(0,1)) | ((0,1),(0,1)) | 0
+(49 rows)
+
--
-- Circles
--