booleanWithin and booleanContains documentation misleading, confusing
boooleanWithin:
Boolean-within returns true if the first geometry is completely within the second geometry. The interiors of both geometries must intersect and, the interior and boundary of the primary (geometry a) must not intersect the exterior of the secondary (geometry b). Boolean-within returns the exact opposite result of the @turf/boolean-contains.
booleanContains:
Boolean-contains returns True if the second geometry is completely contained by the first geometry. The interiors of both geometries must intersect and, the interior and boundary of the secondary (geometry b) must not intersect the exterior of the primary (geometry a). Boolean-contains returns the exact opposite result of the @turf/boolean-within.
(Emphasis added)
This last sentence in both cases is pretty misleading. It reads, to me at least, as saying that booleanWithin(a, b) == !booleanContains(a,b). However, if a and b partially overlap, then both booleanWithin and booleanContains return false. And if a == b then both functions return true.
Other issues:
- I find the language used here to be quite confusing - especially the interior/boundary/exterior bit. Do points and linestrings even have interiors?
- The two geometries are referred to in four different ways, once each: first/second, primary/secondary, a/b, and feature1/feature2. I suggest it should just be "feature1's geometry"/"feature2's geometry".
- The hyphenated name
Boolean-contains/Boolean-withinshould (I think) either be camel case, or to fit the style of the other methods, not present: "Returns true if...". - I don't really understand what the point of "both geometries must intersect" is. In what case could the second part be true (a is not outside b) without the geometries intersecting? I can only think of the case where a is null, but that doesn't seem to be supported by these functions
So overall I'd suggest something like:
Boolean-within:
Returns
trueif no part offeature1's geometry is outside offeature2's geometry. This is equivalent tobooleanContains(feature2, feature1).
@stevage yeah the language is pretty tricky, a lot of the wording we tried to mimic PostGIS (eg ST_Within)
I find the language used here to be quite confusing - especially the interior/boundary/exterior bit. Do points and linestrings even have interiors?
There is a fair bit of implied knowledge re interior/boundary/exterior etc, perhaps start with some of this additional PostGIS doco and see if that helps with some of the concepts. In our current doco structure we don't have a good place to capture some of this background info...
The two geometries are referred to in four different ways, once each: first/second, primary/secondary, a/b, and feature1/feature2. I suggest it should just be "feature1's geometry"/"feature2's geometry".
Good pickup
The hyphenated name Boolean-contains/Boolean-within should (I think) either be camel case, or to fit the style of the other methods, not present: "Returns true if...".
Good pickup
I don't really understand what the point of "both geometries must intersect" is. In what case could the second part be true (a is not outside b) without the geometries intersecting? I can only think of the case where a is null, but that doesn't seem to be supported by these functions
Take a look at the PostGIS definition and see if that helps make it any clearer
Take a look at the PostGIS definition and see if that helps make it any clearer
I think you might be misinterpreting my suggestion for improvement to documentation as a support request? I mean, are we expecting users of Turf to be familiar with PostGIS, and if so, why?
I get it's a request to improve the doco - and I agree that users shouldn't need to be familiar with PostGIS so I'm all for improving the wording 👍
Stealing some notes from the linearthinking blog re why the intersect language is present and your question.
More precisely, the definition of contains is: Geometry A contains Geometry B if no points of B lie in the exterior of A, and at least one point of the interior of B lies in the interior of A That last clause causes the trap - because of it, a LineString which is completely contained in the boundary of a Polygon is not considered to be contained in that Polygon!
According to the article above Contains is a particularly nuanced definition, that fact that we are discussing exact wording proves it :)
So I think your proposed rewording perhaps isn't as accurate as it could be. We don't often think about interior/exterior/boundary with spatial data, but for these definitions & methods it matters. Even the word "intersect" has a specific meaning when it comes to these predicates.
Interesting, I just looked up the PostGIS definitions of interior/exterior/boundary and they a bit counter-intuitive for point and linestring:
The boundary of a geometry is the set of geometries of the next lower dimension. For POINTs, which have a dimension of 0, the boundary is the empty set. The boundary of a LINESTRING is the two endpoints. For POLYGONs, the boundary is the linework of the exterior and interior rings.
The interior of a geometry are those points of a geometry that are not in the boundary. For POINTs, the interior is the point itself. The interior of a LINESTRING is the set of points between the endpoints. For POLYGONs, the interior is the areal surface inside the polygon. Exterior
The exterior of a geometry is the rest of the space in which the geometry is embedded; in other words, all points not in the interior or on the boundary of the geometry. It is a 2-dimensional non-closed surface.
Ok, so each geometry has an interior, an exterior, and a boundary. The definition of "contains" from your quote is:
- zero points in B's boundary or interior are in A's exterior; and
- at least one point in B's interior is in A's interior
My definition was (paraphrased):
- zero points in B's boundary or interior are in A's exterior
So without the second bullet point, my definition would say that a linestring that goes from X to Y to Z "contains" a point X, which it shouldn't (because X isn't in the linestring's interior).
I think we should be aiming to make Turf more accessible than the average GIS product (including PostGIS), so maybe worth actually spelling out some noteworthy cases:
- one point contains another if they are identical
- one linestring contains another if the latter is a sub-section of it
- a linestring contains a point if the latter is on it somewhere, but not one of its endpoints
- a polygon contains a linestring if the latter is completely inside it or on its boundaries (but not only on its boundaries), and not intersecting any interior holes
I'll keep pondering.
This issue should, at least to a small degree, be improved by https://github.com/Turfjs/turf/pull/2848#issuecomment-3608633154