[postgis-devel] [PostGIS] #852: The use of tolerance in function point_in_ring is not robust.
PostGIS
trac at osgeo.org
Fri Mar 4 15:18:56 PST 2011
#852: The use of tolerance in function point_in_ring is not robust.
---------------------+------------------------------------------------------
Reporter: nicklas | Owner: pramsey
Type: defect | Status: new
Priority: medium | Milestone: PostGIS 2.0.0
Component: postgis | Version: 1.5.X
Keywords: |
---------------------+------------------------------------------------------
Description changed by nicklas:
Old description:
> I have taken a closer look at function point_in_ring because of my task
> in #846.
>
> There is two independent problems.
> the row numbers references the file lwgeom_functions_analytic.c
>
> 1) on line 1133 we find this
>
> {{{
> /* a point on the boundary of a ring is not contained. */
> if (fabs(side) < 1e-12)
> {
> }}}
> the problem here is that the calculated values "side" is not only related
> to the distance between the point and the segment but also to the length
> of the segment. That results in that the first example here returns false
> and the second true even if the point is on the exact same distance from
> the polygon in both cases.
>
> {{{
>
> select st_intersects('POINT(5 0.5000000000001)'::geometry,'POLYGON((0 0,
> 10 10, 1 0, 0 0))'::geometry);
>
> select st_intersects('POINT(0.5 0.500000000001)'::geometry, 'POLYGON((0
> 0, 1 1, 1 0, 0 0))'::geometry);
> }}}
>
> 2) the second problem is the construct to first check on which side of
> the segment the point is with a tolerance and then deciding if the point
> is within the endpoints without a tolerance. The second test will make
> the tolerance worthless if the segment is vertical or horizontal. That is
> actually the reason why the bug #845 showed.
>
> there is also a third problem with this tolerance but that doesn't cause
> anything wrong I think after twisting it around many times. But the use
> of tolerance in the macros in the begining of lilwgeom.h that looks like
> this
>
> {{{
> #define FP_TOLERANCE 1e-12
> #define FP_LT(A, B) (((A) + FP_TOLERANCE) < (B))
> #define FP_LTEQ(A, B) (((A) - FP_TOLERANCE) <= (B))
> #define FP_CONTAINS_BOTTOM(A, X, B) (FP_LTEQ(A, X) && FP_LT(X, B))
> }}}
> makes no difference in the result in any case. It causes only a small
> shift in the whole calculation. If it would have made any difference I am
> quite sure it would also have been one or many cases where the tolerance
> would have caused wrong answer. But now I think it is only confusing.
>
> The last issue I would like to just remove the tolerance part, but I
> suspect it is there for some reason. Does anyone remember? the regression
> tests passes on my computer if I remove it.
>
> The two first problems is worse. I guess we need some sort of tolerance
> to be sure to catch the boundary-cases. But this is not the right way, I
> think I have found. Both because it gives an unpredictable tolerance as
> shown in 1) and second because there is no tolerance at all in vertical
> and horizontal segments.
>
> I attach an image showing the point and polygon from #845 to illustrate.
> The distance betwen the point and the left boundary is about 3e-7 m.
>
> /Nicklas
New description:
I have taken a closer look at function point_in_ring because of my task in
#846.
There is two independent problems.
the row numbers references the file lwgeom_functions_analytic.c
1) on line 1133 we find this
{{{
/* a point on the boundary of a ring is not contained. */
if (fabs(side) < 1e-12)
{
}}}
the problem here is that the calculated values "side" is not only related
to the distance between the point and the segment but also to the length
of the segment. That results in that the first example here returns false
and the second true even if the point is on the exact same distance from
the polygon in both cases.
{{{
select st_intersects('POINT(0.5 0.5000000000001)'::geometry, 'POLYGON((0
0, 10 10, 1 0, 0 0))'::geometry);
select st_intersects('POINT(0.5 0.500000000001)'::geometry, 'POLYGON((0 0,
1 1, 1 0, 0 0))'::geometry);
}}}
2) the second problem is the construct to first check on which side of the
segment the point is with a tolerance and then deciding if the point is
within the endpoints without a tolerance. The second test will make the
tolerance worthless if the segment is vertical or horizontal. That is
actually the reason why the bug #845 showed.
there is also a third problem with this tolerance but that doesn't cause
anything wrong I think after twisting it around many times. But the use of
tolerance in the macros in the begining of lilwgeom.h that looks like this
{{{
#define FP_TOLERANCE 1e-12
#define FP_LT(A, B) (((A) + FP_TOLERANCE) < (B))
#define FP_LTEQ(A, B) (((A) - FP_TOLERANCE) <= (B))
#define FP_CONTAINS_BOTTOM(A, X, B) (FP_LTEQ(A, X) && FP_LT(X, B))
}}}
makes no difference in the result in any case. It causes only a small
shift in the whole calculation. If it would have made any difference I am
quite sure it would also have been one or many cases where the tolerance
would have caused wrong answer. But now I think it is only confusing.
The last issue I would like to just remove the tolerance part, but I
suspect it is there for some reason. Does anyone remember? the regression
tests passes on my computer if I remove it.
The two first problems is worse. I guess we need some sort of tolerance to
be sure to catch the boundary-cases. But this is not the right way, I
think I have found. Both because it gives an unpredictable tolerance as
shown in 1) and second because there is no tolerance at all in vertical
and horizontal segments.
I attach an image showing the point and polygon from #845 to illustrate.
The distance betwen the point and the left boundary is about 3e-7 m.
/Nicklas
--
--
Ticket URL: <http://trac.osgeo.org/postgis/ticket/852#comment:1>
PostGIS <http://trac.osgeo.org/postgis/>
The PostGIS Trac is used for bug, enhancement & task tracking, a user and developer wiki, and a view into the subversion code repository of PostGIS project.
More information about the postgis-devel
mailing list