Skip to content

Commit

Permalink
Merge pull request #1158 from rapidsai/branch-23.06
Browse files Browse the repository at this point in the history
Forward-merge branch-23.06 to branch-23.08
  • Loading branch information
GPUtester authored May 26, 2023
2 parents ac40ed6 + 3300768 commit 7bff69d
Show file tree
Hide file tree
Showing 22 changed files with 970 additions and 237 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
)


def _basic_equals(lhs, rhs):
def _basic_equals_any(lhs, rhs):
"""Utility method that returns True if any point in the lhs geometry
is equal to a point in the rhs geometry."""
lhs = _multipoints_from_geometry(lhs)
Expand Down
13 changes: 8 additions & 5 deletions python/cuspatial/cuspatial/core/binpreds/feature_contains.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from cuspatial.core.binpreds.basic_predicates import (
_basic_contains_count,
_basic_equals,
_basic_equals_any,
_basic_equals_count,
_basic_intersects,
_basic_intersects_pli,
Expand Down Expand Up @@ -132,20 +132,23 @@ def _compute_predicate(self, lhs, rhs, preprocessor_result):

class PointPointContains(BinPred):
def _preprocess(self, lhs, rhs):
return _basic_equals(lhs, rhs)
return _basic_equals_any(lhs, rhs)


class LineStringPointContains(BinPred):
def _preprocess(self, lhs, rhs):
intersects = _basic_intersects(lhs, rhs)
equals = _basic_equals(lhs, rhs)
equals = _basic_equals_any(lhs, rhs)
return intersects & ~equals


class LineStringLineStringContainsPredicate(BinPred):
def _preprocess(self, lhs, rhs):
count = _basic_equals_count(lhs, rhs)
return count == rhs.sizes
pli = _basic_intersects_pli(lhs, rhs)
points = _points_and_lines_to_multipoints(pli[1], pli[0])
# Every point in B must be in the intersection
equals = _basic_equals_count(rhs, points) == rhs.sizes
return equals


"""DispatchDict listing the classes to use for each combination of
Expand Down
83 changes: 64 additions & 19 deletions python/cuspatial/cuspatial/core/binpreds/feature_covers.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,101 @@
# Copyright (c) 2023, NVIDIA CORPORATION.

from cuspatial.core.binpreds.binpred_interface import NotImplementedPredicate
from cuspatial.core.binpreds.basic_predicates import (
_basic_contains_any,
_basic_contains_count,
_basic_equals_count,
_basic_intersects_pli,
)
from cuspatial.core.binpreds.binpred_interface import (
BinPred,
ImpossiblePredicate,
NotImplementedPredicate,
)
from cuspatial.core.binpreds.feature_equals import EqualsPredicateBase
from cuspatial.core.binpreds.feature_intersects import (
LineStringPointIntersects,
PointLineStringIntersects,
)
from cuspatial.utils.binpred_utils import (
LineString,
MultiPoint,
Point,
Polygon,
_points_and_lines_to_multipoints,
_zero_series,
)


class CoversPredicateBase(EqualsPredicateBase):
"""Implements the covers predicate across different combinations of
geometry types. For example, a Point-Polygon covers predicate is
defined in terms of a Point-Point equals predicate. The initial release
implements covers predicates that depend only on the equals predicate, or
depend on no predicate, such as impossible cases like
`LineString.covers(Polygon)`.
For this initial release, cover is supported for the following types:
defined in terms of a Point-Polygon equals predicate.
Point.covers(Point)
Point.covers(Polygon)
LineString.covers(Polygon)
Polygon.covers(Point)
Polygon.covers(MultiPoint)
Polygon.covers(LineString)
Polygon.covers(Polygon)
"""

pass


class LineStringLineStringCovers(BinPred):
def _preprocess(self, lhs, rhs):
# A linestring A covers another linestring B iff
# no point in B is outside of A.
pli = _basic_intersects_pli(lhs, rhs)
points = _points_and_lines_to_multipoints(pli[1], pli[0])
# Every point in B must be in the intersection
equals = _basic_equals_count(rhs, points) == rhs.sizes
return equals


class PolygonPointCovers(BinPred):
def _preprocess(self, lhs, rhs):
return _basic_contains_any(lhs, rhs)


class PolygonLineStringCovers(BinPred):
def _preprocess(self, lhs, rhs):
# A polygon covers a linestring if all of the points in the linestring
# are in the interior or exterior of the polygon. This differs from
# a polygon that contains a linestring in that some point of the
# linestring must be in the interior of the polygon.
# Count the number of points from rhs in the interior of lhs
contains_count = _basic_contains_count(lhs, rhs)
# Now count the number of points from rhs in the boundary of lhs
pli = _basic_intersects_pli(lhs, rhs)
intersections = pli[1]
# There may be no intersection, so start with _zero_series
equality = _zero_series(len(rhs))
if len(intersections) > 0:
matching_length_multipoints = _points_and_lines_to_multipoints(
intersections, pli[0]
)
equality = _basic_equals_count(matching_length_multipoints, rhs)
covers = contains_count + equality >= rhs.sizes
return covers


class PolygonPolygonCovers(BinPred):
def _preprocess(self, lhs, rhs):
contains = lhs.contains(rhs)
return contains


DispatchDict = {
(Point, Point): CoversPredicateBase,
(Point, MultiPoint): NotImplementedPredicate,
(Point, LineString): PointLineStringIntersects,
(Point, Polygon): CoversPredicateBase,
(Point, LineString): ImpossiblePredicate,
(Point, Polygon): ImpossiblePredicate,
(MultiPoint, Point): NotImplementedPredicate,
(MultiPoint, MultiPoint): NotImplementedPredicate,
(MultiPoint, LineString): NotImplementedPredicate,
(MultiPoint, Polygon): NotImplementedPredicate,
(LineString, Point): LineStringPointIntersects,
(LineString, MultiPoint): NotImplementedPredicate,
(LineString, LineString): NotImplementedPredicate,
(LineString, LineString): LineStringLineStringCovers,
(LineString, Polygon): CoversPredicateBase,
(Polygon, Point): CoversPredicateBase,
(Polygon, Point): PolygonPointCovers,
(Polygon, MultiPoint): CoversPredicateBase,
(Polygon, LineString): CoversPredicateBase,
(Polygon, Polygon): CoversPredicateBase,
(Polygon, LineString): PolygonLineStringCovers,
(Polygon, Polygon): PolygonPolygonCovers,
}
58 changes: 46 additions & 12 deletions python/cuspatial/cuspatial/core/binpreds/feature_crosses.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
# Copyright (c) 2023, NVIDIA CORPORATION.

from cuspatial.core.binpreds.basic_predicates import (
_basic_equals_count,
_basic_intersects_count,
_basic_intersects_pli,
)
from cuspatial.core.binpreds.binpred_interface import (
BinPred,
ImpossiblePredicate,
NotImplementedPredicate,
)
from cuspatial.core.binpreds.feature_equals import EqualsPredicateBase
from cuspatial.core.binpreds.feature_intersects import IntersectsPredicateBase
from cuspatial.utils.binpred_utils import (
LineString,
MultiPoint,
Point,
Polygon,
_false_series,
_points_and_lines_to_multipoints,
)


Expand All @@ -30,6 +37,33 @@ class CrossesPredicateBase(EqualsPredicateBase):
pass


class LineStringLineStringCrosses(IntersectsPredicateBase):
def _compute_predicate(self, lhs, rhs, preprocessor_result):
# A linestring crosses another linestring iff
# they intersect, and none of the points of the
# intersection are in the boundary of the other
pli = _basic_intersects_pli(rhs, lhs)
intersections = _points_and_lines_to_multipoints(pli[1], pli[0])
equals = (_basic_equals_count(intersections, lhs) > 0) | (
_basic_equals_count(intersections, rhs) > 0
)
intersects = _basic_intersects_count(rhs, lhs) > 0
return intersects & ~equals


class LineStringPolygonCrosses(BinPred):
def _preprocess(self, lhs, rhs):
intersects = _basic_intersects_count(rhs, lhs) > 1
touches = rhs.touches(lhs)
contains = rhs.contains(lhs)
return ~touches & intersects & ~contains


class PolygonLineStringCrosses(LineStringPolygonCrosses):
def _preprocess(self, lhs, rhs):
return super()._preprocess(rhs, lhs)


class PointPointCrosses(CrossesPredicateBase):
def _preprocess(self, lhs, rhs):
"""Points can't cross other points, so we return False."""
Expand All @@ -38,19 +72,19 @@ def _preprocess(self, lhs, rhs):

DispatchDict = {
(Point, Point): PointPointCrosses,
(Point, MultiPoint): NotImplementedPredicate,
(Point, LineString): NotImplementedPredicate,
(Point, MultiPoint): ImpossiblePredicate,
(Point, LineString): ImpossiblePredicate,
(Point, Polygon): CrossesPredicateBase,
(MultiPoint, Point): NotImplementedPredicate,
(MultiPoint, MultiPoint): NotImplementedPredicate,
(MultiPoint, LineString): NotImplementedPredicate,
(MultiPoint, Polygon): NotImplementedPredicate,
(MultiPoint, Point): ImpossiblePredicate,
(MultiPoint, MultiPoint): ImpossiblePredicate,
(MultiPoint, LineString): ImpossiblePredicate,
(MultiPoint, Polygon): ImpossiblePredicate,
(LineString, Point): ImpossiblePredicate,
(LineString, MultiPoint): NotImplementedPredicate,
(LineString, LineString): NotImplementedPredicate,
(LineString, Polygon): NotImplementedPredicate,
(LineString, MultiPoint): ImpossiblePredicate,
(LineString, LineString): LineStringLineStringCrosses,
(LineString, Polygon): LineStringPolygonCrosses,
(Polygon, Point): CrossesPredicateBase,
(Polygon, MultiPoint): CrossesPredicateBase,
(Polygon, LineString): CrossesPredicateBase,
(Polygon, Polygon): CrossesPredicateBase,
(Polygon, LineString): PolygonLineStringCrosses,
(Polygon, Polygon): ImpossiblePredicate,
}
59 changes: 37 additions & 22 deletions python/cuspatial/cuspatial/core/binpreds/feature_disjoint.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# Copyright (c) 2023, NVIDIA CORPORATION.

from cuspatial.core.binpreds.basic_predicates import (
_basic_contains_any,
_basic_intersects,
)
from cuspatial.core.binpreds.binpred_interface import (
BinPred,
NotImplementedPredicate,
)
from cuspatial.core.binpreds.feature_intersects import (
IntersectsPredicateBase,
PointLineStringIntersects,
)
from cuspatial.core.binpreds.feature_intersects import IntersectsPredicateBase
from cuspatial.utils.binpred_utils import (
LineString,
MultiPoint,
Expand All @@ -16,7 +17,7 @@
)


class ContainsDisjoint(BinPred):
class DisjointByWayOfContains(BinPred):
def _preprocess(self, lhs, rhs):
"""Disjoint is the opposite of contains, so just implement contains
and then negate the result.
Expand All @@ -26,20 +27,22 @@ def _preprocess(self, lhs, rhs):
(Point, Polygon)
(Polygon, Point)
"""
from cuspatial.core.binpreds.binpred_dispatch import CONTAINS_DISPATCH
return ~_basic_contains_any(lhs, rhs)

predicate = CONTAINS_DISPATCH[(lhs.column_type, rhs.column_type)](
align=self.config.align
)
return ~predicate(lhs, rhs)


class PointLineStringDisjoint(PointLineStringIntersects):
def _postprocess(self, lhs, rhs, op_result):
class PointLineStringDisjoint(BinPred):
def _preprocess(self, lhs, rhs):
"""Disjoint is the opposite of intersects, so just implement intersects
and then negate the result."""
result = super()._postprocess(lhs, rhs, op_result)
return ~result
intersects = _basic_intersects(lhs, rhs)
return ~intersects


class PointPolygonDisjoint(BinPred):
def _preprocess(self, lhs, rhs):
intersects = _basic_intersects(lhs, rhs)
contains = _basic_contains_any(lhs, rhs)
return ~intersects & ~contains


class LineStringPointDisjoint(PointLineStringDisjoint):
Expand All @@ -56,21 +59,33 @@ def _postprocess(self, lhs, rhs, op_result):
return ~result


class LineStringPolygonDisjoint(BinPred):
def _preprocess(self, lhs, rhs):
intersects = _basic_intersects(lhs, rhs)
contains = _basic_contains_any(rhs, lhs)
return ~intersects & ~contains


class PolygonPolygonDisjoint(BinPred):
def _preprocess(self, lhs, rhs):
return ~_basic_contains_any(lhs, rhs) & ~_basic_contains_any(rhs, lhs)


DispatchDict = {
(Point, Point): ContainsDisjoint,
(Point, Point): DisjointByWayOfContains,
(Point, MultiPoint): NotImplementedPredicate,
(Point, LineString): PointLineStringDisjoint,
(Point, Polygon): ContainsDisjoint,
(Point, Polygon): PointPolygonDisjoint,
(MultiPoint, Point): NotImplementedPredicate,
(MultiPoint, MultiPoint): NotImplementedPredicate,
(MultiPoint, LineString): NotImplementedPredicate,
(MultiPoint, Polygon): NotImplementedPredicate,
(MultiPoint, Polygon): LineStringPolygonDisjoint,
(LineString, Point): LineStringPointDisjoint,
(LineString, MultiPoint): NotImplementedPredicate,
(LineString, LineString): LineStringLineStringDisjoint,
(LineString, Polygon): NotImplementedPredicate,
(Polygon, Point): ContainsDisjoint,
(LineString, Polygon): LineStringPolygonDisjoint,
(Polygon, Point): DisjointByWayOfContains,
(Polygon, MultiPoint): NotImplementedPredicate,
(Polygon, LineString): NotImplementedPredicate,
(Polygon, Polygon): NotImplementedPredicate,
(Polygon, LineString): DisjointByWayOfContains,
(Polygon, Polygon): PolygonPolygonDisjoint,
}
13 changes: 11 additions & 2 deletions python/cuspatial/cuspatial/core/binpreds/feature_equals.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from cuspatial.core.binpreds.binpred_interface import (
BinPred,
EqualsOpResult,
ImpossiblePredicate,
NotImplementedPredicate,
PreprocessorResult,
)
Expand Down Expand Up @@ -334,11 +335,19 @@ def _preprocess(self, lhs, rhs):
return _false_series(len(lhs))


class PolygonPolygonEquals(BinPred):
def _preprocess(self, lhs, rhs):
"""Two polygons are equal if they contain each other."""
lhs_contains_rhs = lhs.contains(rhs)
rhs_contains_lhs = rhs.contains(lhs)
return lhs_contains_rhs & rhs_contains_lhs


"""DispatchDict for Equals operations."""
DispatchDict = {
(Point, Point): EqualsPredicateBase,
(Point, MultiPoint): NotImplementedPredicate,
(Point, LineString): NotImplementedPredicate,
(Point, LineString): ImpossiblePredicate,
(Point, Polygon): EqualsPredicateBase,
(MultiPoint, Point): NotImplementedPredicate,
(MultiPoint, MultiPoint): MultiPointMultiPointEquals,
Expand All @@ -351,5 +360,5 @@ def _preprocess(self, lhs, rhs):
(Polygon, Point): EqualsPredicateBase,
(Polygon, MultiPoint): EqualsPredicateBase,
(Polygon, LineString): EqualsPredicateBase,
(Polygon, Polygon): EqualsPredicateBase,
(Polygon, Polygon): PolygonPolygonEquals,
}
Loading

0 comments on commit 7bff69d

Please sign in to comment.