diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c index e7c1160131..047afdbe1a 100644 --- a/src/backend/utils/adt/geo_ops.c +++ b/src/backend/utils/adt/geo_ops.c @@ -1,15 +1,25 @@ /*------------------------------------------------------------------------- * * geo_ops.c * 2D geometric operations * + * This module implements the geometric functions and operators. The + * geometric types are (from simple to more complicated): + * + * - point + * - line + * - line segment + * - box + * - circle + * - polygon + * * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * src/backend/utils/adt/geo_ops.c * *------------------------------------------------------------------------- */ #include "postgres.h" @@ -18,20 +28,53 @@ #include #include #include #include "libpq/pqformat.h" #include "miscadmin.h" #include "utils/float.h" #include "utils/fmgrprotos.h" #include "utils/geo_decls.h" +/* + * The functions to implement the types have the signature: + * + * void type_construct(Type *result, ...); + * + * The functions to implement operators usually have signatures like: + * + * void type1_operator_type2(Type *result, Type1 *obj1, Type2 *obj2); + * + * There are certain operators between different types. The functions + * that return the intersection point between 2 types have signature: + * + * bool type1_interpt_type2(Point *result, Type1 *obj1, Type2 *obj2); + * + * These return whether the two objects intersect, and set the intersection + * point to pre-allocated *result when it is not NULL. Those functions may be + * used to implement multiple SQL level operators. For example, determining + * whether two lines are parallel is done by checking whether they intersect. + * + * The functions that return whether an object contains another have + * the signature: + * + * bool type1_contain_type2(Type1, *obj1, Type2 *obj2); + * + * The functions to get the closest point on an object to the second object + * have the signature: + * + * float8 type1_closept_type2(Point *result, Type1 *obj1, Type2 *obj2); + * + * They return the shortest distance between the two objects, and set + * the closest point on the first object to the second object to pre-allocated + * *result when it is not NULL. + */ /* * Internal routines */ enum path_delim { PATH_NONE, PATH_OPEN, PATH_CLOSED }; @@ -57,44 +100,44 @@ static float8 line_closept_point(Point *result, LINE *line, Point *pt); /* Routines for line segments */ static inline void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2); static inline float8 lseg_sl(LSEG *lseg); static inline float8 lseg_invsl(LSEG *lseg); static bool lseg_interpt_line(Point *result, LSEG *lseg, LINE *line); static bool lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2); static int lseg_crossing(float8 x, float8 y, float8 px, float8 py); static bool lseg_contain_point(LSEG *lseg, Point *point); static float8 lseg_closept_point(Point *result, LSEG *lseg, Point *pt); static float8 lseg_closept_line(Point *result, LSEG *lseg, LINE *line); -static float8 lseg_closept_lseg(Point *result, LSEG *l1, LSEG *l2); +static float8 lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg); /* Routines for boxes */ static inline void box_construct(BOX *result, Point *pt1, Point *pt2); static void box_cn(Point *center, BOX *box); static bool box_ov(BOX *box1, BOX *box2); static float8 box_ar(BOX *box); static float8 box_ht(BOX *box); static float8 box_wd(BOX *box); static bool box_contain_point(BOX *box, Point *point); -static bool box_contain_box(BOX *box1, BOX *box2); +static bool box_contain_box(BOX *contains_box1, BOX *contained_box2); static bool box_contain_lseg(BOX *box, LSEG *lseg); static bool box_interpt_lseg(Point *result, BOX *box, LSEG *lseg); static float8 box_closept_point(Point *result, BOX *box, Point *point); static float8 box_closept_lseg(Point *result, BOX *box, LSEG *lseg); /* Routines for circles */ static float8 circle_ar(CIRCLE *circle); /* Routines for polygons */ static void make_bound_box(POLYGON *poly); static void poly_to_circle(CIRCLE *result, POLYGON *poly); static bool lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start); -static bool poly_contain_poly(POLYGON *polya, POLYGON *polyb); +static bool poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly); static bool plist_same(int npts, Point *p1, Point *p2); static float8 dist_ppoly_internal(Point *pt, POLYGON *poly); /* Routines for encoding and decoding */ static float8 single_decode(char *num, char **endptr_p, const char *type_name, const char *orig_string); static void single_encode(float8 x, StringInfo str); static void pair_decode(char *str, float8 *x, float8 *y, char **endptr_p, const char *type_name, const char *orig_string); static void pair_encode(float8 x, float8 y, StringInfo str); @@ -644,26 +687,26 @@ box_contain(PG_FUNCTION_ARGS) BOX *box1 = PG_GETARG_BOX_P(0); BOX *box2 = PG_GETARG_BOX_P(1); PG_RETURN_BOOL(box_contain_box(box1, box2)); } /* * Check whether the box is in the box or on its border */ static bool -box_contain_box(BOX *box1, BOX *box2) +box_contain_box(BOX *contains_box, BOX *contained_box) { - return FPge(box1->high.x, box2->high.x) && - FPle(box1->low.x, box2->low.x) && - FPge(box1->high.y, box2->high.y) && - FPle(box1->low.y, box2->low.y); + return FPge(contains_box->high.x, contained_box->high.x) && + FPle(contains_box->low.x, contained_box->low.x) && + FPge(contains_box->high.y, contained_box->high.y) && + FPle(contains_box->low.y, contained_box->low.y); } /* box_positionop - * is box1 entirely {above,below} box2? * * box_below_eq and box_above_eq are obsolete versions that (probably * erroneously) accept the equal-boundaries case. Since these are not * in sync with the box_left and box_right code, they are deprecated and * not supported in the PG 8.1 rtree operator class extension. @@ -1216,24 +1259,20 @@ line_interpt(PG_FUNCTION_ARGS) result = (Point *) palloc(sizeof(Point)); if (!line_interpt_line(result, l1, l2)) PG_RETURN_NULL(); PG_RETURN_POINT_P(result); } /* * Internal version of line_interpt * - * This returns true if two lines intersect (they do, if they are not - * parallel), false if they do not. This also sets the intersection point - * to *result, if it is not NULL. - * * NOTE: If the lines are identical then we will find they are parallel * and report "no intersection". This is a little weird, but since * there's no *unique* intersection, maybe it's appropriate behavior. * * If the lines have NaN constants, we will return true, and the intersection * point would have NaN coordinates. We shouldn't return false in this case * because that would mean the lines are parallel. */ static bool line_interpt_line(Point *result, LINE *l1, LINE *l2) @@ -2239,22 +2278,20 @@ lseg_center(PG_FUNCTION_ARGS) result->x = float8_div(float8_pl(lseg->p[0].x, lseg->p[1].x), 2.0); result->y = float8_div(float8_pl(lseg->p[0].y, lseg->p[1].y), 2.0); PG_RETURN_POINT_P(result); } /* * Find the intersection point of two segments (if any). * - * This returns true if two line segments intersect, false if they do not. - * This also sets the intersection point to *result, if it is not NULL. * This function is almost perfectly symmetric, even though it doesn't look * like it. See lseg_interpt_line() for the other half of it. */ static bool lseg_interpt_lseg(Point *result, LSEG *l1, LSEG *l2) { Point interpt; LINE tmp; line_construct(&tmp, &l2->p[0], lseg_sl(l2)); @@ -2501,24 +2538,20 @@ dist_ppoly_internal(Point *pt, POLYGON *poly) /*--------------------------------------------------------------------- * interpt_ * Intersection point of objects. * We choose to ignore the "point" of intersection between * lines and boxes, since there are typically two. *-------------------------------------------------------------------*/ /* * Check if the line segment intersects with the line - * - * This returns true if line segment intersects with line, false if they - * do not. This also sets the intersection point to *result, if it is not - * NULL. */ static bool lseg_interpt_line(Point *result, LSEG *lseg, LINE *line) { Point interpt; LINE tmp; /* * First, we promote the line segment to a line, because we know how * to find the intersection point of two lines. If they don't have @@ -2554,23 +2587,20 @@ lseg_interpt_line(Point *result, LSEG *lseg, LINE *line) } /*--------------------------------------------------------------------- * close_ * Point of closest proximity between objects. *-------------------------------------------------------------------*/ /* * The intersection point of a perpendicular of the line * through the point. - * - * This sets the closest point to the *result if it is not NULL and returns - * the distance to the closest point. */ static float8 line_closept_point(Point *result, LINE *line, Point *point) { Point closept; LINE tmp; /* * We drop a perpendicular to find the intersection point. Ordinarily * we should always find it, but that can fail in the presence of NaN @@ -2602,23 +2632,20 @@ close_pl(PG_FUNCTION_ARGS) if (isnan(line_closept_point(result, line, pt))) PG_RETURN_NULL(); PG_RETURN_POINT_P(result); } /* * Closest point on line segment to specified point. - * - * This sets the closest point to the *result if it is not NULL and returns - * the distance to the closest point. */ static float8 lseg_closept_point(Point *result, LSEG *lseg, Point *pt) { Point closept; LINE tmp; /* * To find the closest point, we draw a perpendicular line from the point * to the line segment. @@ -2643,62 +2670,59 @@ close_ps(PG_FUNCTION_ARGS) if (isnan(lseg_closept_point(result, lseg, pt))) PG_RETURN_NULL(); PG_RETURN_POINT_P(result); } /* * Closest point on line segment to line segment - * - * This sets the closest point to the *result if it is not NULL and returns - * the distance to the closest point. */ static float8 -lseg_closept_lseg(Point *result, LSEG *l1, LSEG *l2) +lseg_closept_lseg(Point *result, LSEG *on_lseg, LSEG *to_lseg)) { Point point; float8 dist, d; /* First, we handle the case when the line segments are intersecting. */ - if (lseg_interpt_lseg(result, l1, l2)) + if (lseg_interpt_lseg(result, on_lseg, to_lseg)) return 0.0; /* * Then, we find the closest points from the endpoints of the second * line segment, and keep the closest one. */ - dist = lseg_closept_point(result, l1, &l2->p[0]); - d = lseg_closept_point(&point, l1, &l2->p[1]); + dist = lseg_closept_point(result, on_lseg, &to_lseg->p[0]); + d = lseg_closept_point(&point, on_lseg, &to_lseg->p[1]); if (float8_lt(d, dist)) { dist = d; if (result != NULL) *result = point; } /* The closest point can still be one of the endpoints, so we test them. */ - d = lseg_closept_point(NULL, l2, &l1->p[0]); + d = lseg_closept_point(NULL, to_lseg, &on_lseg->p[0]); if (float8_lt(d, dist)) { dist = d; if (result != NULL) - *result = l1->p[0]; + *result = on_lseg->p[0]; } - d = lseg_closept_point(NULL, l2, &l1->p[1]); + d = lseg_closept_point(NULL, to_lseg, &on_lseg->p[1]); if (float8_lt(d, dist)) { dist = d; if (result != NULL) - *result = l1->p[1]; + *result = on_lseg->p[1]; } return dist; } Datum close_lseg(PG_FUNCTION_ARGS) { LSEG *l1 = PG_GETARG_LSEG_P(0); LSEG *l2 = PG_GETARG_LSEG_P(1); @@ -2711,23 +2735,20 @@ close_lseg(PG_FUNCTION_ARGS) if (isnan(lseg_closept_lseg(result, l2, l1))) PG_RETURN_NULL(); PG_RETURN_POINT_P(result); } /* * Closest point on or in box to specified point. - * - * This sets the closest point to the *result if it is not NULL and returns - * the distance to the closest point. */ static float8 box_closept_point(Point *result, BOX *box, Point *pt) { float8 dist, d; Point point, closept; LSEG lseg; @@ -2830,23 +2851,20 @@ close_sl(PG_FUNCTION_ARGS) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function \"close_sl\" not implemented"))); PG_RETURN_NULL(); } /* * Closest point on line segment to line. * - * This sets the closest point to the *result if it is not NULL and returns - * the distance to the closest point. - * * NOTE: When the lines are parallel, endpoints of one of the line segment * are FPeq(), in presence of NaN or Infinitive coordinates, or perhaps = * even because of simple roundoff issues, there may not be a single closest * point. We are likely to set the result to the second endpoint in these * cases. */ static float8 lseg_closept_line(Point *result, LSEG *lseg, LINE *line) { float8 dist1, @@ -2888,23 +2906,20 @@ close_ls(PG_FUNCTION_ARGS) if (isnan(lseg_closept_line(result, lseg, line))) PG_RETURN_NULL(); PG_RETURN_POINT_P(result); } /* * Closest point on or in box to line segment. - * - * This sets the closest point to the *result if it is not NULL and returns - * the distance to the closest point. */ static float8 box_closept_lseg(Point *result, BOX *box, LSEG *lseg) { float8 dist, d; Point point, closept; LSEG bseg; @@ -3814,43 +3829,43 @@ lseg_inside_poly(Point *a, Point *b, POLYGON *poly, int start) */ p.x = float8_div(float8_pl(t.p[0].x, t.p[1].x), 2.0); p.y = float8_div(float8_pl(t.p[0].y, t.p[1].y), 2.0); res = point_inside(&p, poly->npts, poly->p); } return res; } -/*----------------------------------------------------------------- - * Determine if polygon A contains polygon B. - *-----------------------------------------------------------------*/ +/* + * Check whether the polygon contains another + */ static bool -poly_contain_poly(POLYGON *polya, POLYGON *polyb) +poly_contain_poly(POLYGON *contains_poly, POLYGON *contained_poly) { int i; LSEG s; - Assert(polya->npts > 0 && polyb->npts > 0); + Assert(contains_poly->npts > 0 && contained_poly->npts > 0); /* - * Quick check to see if bounding box is contained. + * Quick check to see if bounding box is contained_poly. */ - if (!box_contain_box(&polya->boundbox, &polyb->boundbox)) + if (!box_contain_box(&contains_poly->boundbox, &contained_poly->boundbox)) return false; - s.p[0] = polyb->p[polyb->npts - 1]; + s.p[0] = contained_poly->p[contained_poly->npts - 1]; - for (i = 0; i < polyb->npts; i++) + for (i = 0; i < contained_poly->npts; i++) { - s.p[1] = polyb->p[i]; - if (!lseg_inside_poly(s.p, s.p + 1, polya, 0)) + s.p[1] = contained_poly->p[i]; + if (!lseg_inside_poly(s.p, s.p + 1, contains_poly, 0)) return false; s.p[0] = s.p[1]; } return true; } Datum poly_contain(PG_FUNCTION_ARGS) {