# Source code for temporal.spatial_extent

    """
    Spatial extents classes for map layer and space time datasets

    Usage:

    .. code-block:: python

        >>> import grass.temporal as tgis
        >>> tgis.init()
        >>> extent = tgis.RasterSpatialExtent(
        ... ident="raster@PERMANENT", north=90, south=90, east=180, west=180,
        ... top=100, bottom=-20)
        >>> extent = tgis.Raster3DSpatialExtent(
        ... ident="raster3d@PERMANENT", north=90, south=90, east=180, west=180,
        ... top=100, bottom=-20)
        >>> extent = tgis.VectorSpatialExtent(
        ... ident="vector@PERMANENT", north=90, south=90, east=180, west=180,
        ... top=100, bottom=-20)
        >>> extent = tgis.STRDSSpatialExtent(
        ... ident="strds@PERMANENT", north=90, south=90, east=180, west=180,
        ... top=100, bottom=-20)
        >>> extent = tgis.STR3DSSpatialExtent(
        ... ident="str3ds@PERMANENT", north=90, south=90, east=180, west=180,
        ... top=100, bottom=-20)
        >>> extent = tgis.STVDSSpatialExtent(
        ... ident="stvds@PERMANENT", north=90, south=90, east=180, west=180,
        ... top=100, bottom=-20)

    (C) 2012-2013 by the GRASS Development Team
    This program is free software under the GNU General Public
    License (>=v2). Read the file COPYING that comes with GRASS
    for details.

    :authors: Soeren Gebbert
    """
    from __future__ import print_function
    from .base import SQLDatabaseInterface

    class SpatialExtent(SQLDatabaseInterface):
        """This is the spatial extent base class for all maps and space time datasets

        This class implements a three dimensional axis aligned bounding box
        and functions to compute topological relationships

        Usage:

        .. code-block:: python

            >>> init()
            >>> extent = SpatialExtent(table="raster_spatial_extent",
            ... ident="soil@PERMANENT", north=90, south=90, east=180, west=180,
            ... top=100, bottom=-20)
            >>> extent.id
            'soil@PERMANENT'
            >>> extent.north
            90.0
            >>> extent.south
            90.0
            >>> extent.east
            180.0
            >>> extent.west
            180.0
            >>> extent.top
            100.0
            >>> extent.bottom
            -20.0
            >>> extent.print_info()
             +-------------------- Spatial extent ----------------------------------------+
             | North:...................... 90.0
             | South:...................... 90.0
             | East:.. .................... 180.0
             | West:....................... 180.0
             | Top:........................ 100.0
             | Bottom:..................... -20.0
            >>> extent.print_shell_info()
            north=90.0
            south=90.0
            east=180.0
            west=180.0
            top=100.0
            bottom=-20.0

        """

        def __init__(
            self,
            table=None,
            ident=None,
            north=None,
            south=None,
            east=None,
            west=None,
            top=None,
            bottom=None,
            proj="XY",
        ):

            SQLDatabaseInterface.__init__(self, table, ident)
            self.set_id(ident)
            self.set_spatial_extent_from_values(north, south, east, west, top, bottom)
            self.set_projection(proj)

        def overlapping_2d(self, extent):
            """Return True if this (A) and the provided spatial extent (B) overlaps
            in two dimensional space.
            Code is lend from wind_overlap.c in lib/gis

            Overlapping includes the spatial relations:

            - contain
            - in
            - cover
            - covered
            - equivalent

            .. code-block:: python

                 >>> A = SpatialExtent(north=80, south=20, east=60, west=10)
                 >>> B = SpatialExtent(north=80, south=20, east=60, west=10)
                 >>> A.overlapping_2d(B)
                 True

            :param extent: The spatial extent to check overlapping with
            :return: True or False
            """

            if self.get_projection() != extent.get_projection():
                self.msgr.error(
                    _(
                        "Projections are different. Unable to compute "
                        "overlapping_2d for spatial extents"
                    )
                )
                return False

            N = extent.get_north()
            S = extent.get_south()
            E = extent.get_east()
            W = extent.get_west()

            # Adjust the east and west in case of LL projection
            if self.get_projection() == "LL":
                while E < self.get_west():
                    E += 360.0
                    W += 360.0

                while W > self.get_east():
                    E -= 360.0
                    W -= 360.0

            if self.get_north() <= S:
                return False

            if self.get_south() >= N:
                return False

            if self.get_east() <= W:
                return False

            if self.get_west() >= E:
                return False

            return True

        def overlapping(self, extent):
            """Return True if this (A) and the provided spatial
            extent (B) overlaps in three dimensional space.

            Overlapping includes the spatial relations:

            - contain
            - in
            - cover
            - covered
            - equivalent

            Usage:

            .. code-block:: python

                 >>> A = SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
                 >>> B = SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
                 >>> A.overlapping(B)
                 True

            :param extent: The spatial extent to check overlapping with
            :return: True or False
            """

            if not self.overlapping_2d(extent):
                return False

            T = extent.get_top()
            B = extent.get_bottom()

            if self.get_top() <= B:
                return False

            if self.get_bottom() >= T:
                return False

            return True

        def intersect_2d(self, extent):
            """Return the two dimensional intersection as spatial_extent
               object or None in case no intersection was found.

            :param extent: The spatial extent to intersect with
            :return: The intersection spatial extent
            """

            if not self.overlapping_2d(extent):
                return None

            eN = extent.get_north()
            eS = extent.get_south()
            eE = extent.get_east()
            eW = extent.get_west()

            N = self.get_north()
            S = self.get_south()
            E = self.get_east()
            W = self.get_west()

            # Adjust the east and west in case of LL projection
            if self.get_projection() == "LL":
                while eE < W:
                    eE += 360.0
                    eW += 360.0

                while eW > E:
                    eE -= 360.0
                    eW -= 360.0

            # Compute the extent
            nN = N
            nS = S
            nE = E
            nW = W

            if W < eW:
                nW = eW
            if E > eE:
                nE = eE
            if N > eN:
                nN = eN
            if S < eS:
                nS = eS

            new = SpatialExtent(
                north=nN,
                south=nS,
                east=nE,
                west=nW,
                top=0,
                bottom=0,
                proj=self.get_projection(),
            )
            return new

        def intersect(self, extent):
            """Return the three dimensional intersection as spatial_extent
            object or None in case no intersection was found.

            Usage:

            .. code-block:: python

                >>> A = SpatialExtent(north=80, south=20, east=60, west=10,
                ... bottom=-50, top=50)
                >>> B = SpatialExtent(north=80, south=20, east=60, west=10,
                ... bottom=-50, top=50)
                >>> C = A.intersect(B)
                >>> C.print_info()
                 +-------------------- Spatial extent ----------------------------------------+
                 | North:...................... 80.0
                 | South:...................... 20.0
                 | East:.. .................... 60.0
                 | West:....................... 10.0
                 | Top:........................ 50.0
                 | Bottom:..................... -50.0
                >>> B = SpatialExtent(north=40, south=30, east=60, west=10,
                ... bottom=-50, top=50)
                >>> C = A.intersect(B)
                >>> C.print_info()
                 +-------------------- Spatial extent ----------------------------------------+
                 | North:...................... 40.0
                 | South:...................... 30.0
                 | East:.. .................... 60.0
                 | West:....................... 10.0
                 | Top:........................ 50.0
                 | Bottom:..................... -50.0
                >>> B = SpatialExtent(north=40, south=30, east=60, west=30,
                ... bottom=-50, top=50)
                >>> C = A.intersect(B)
                >>> C.print_info()
                 +-------------------- Spatial extent ----------------------------------------+
                 | North:...................... 40.0
                 | South:...................... 30.0
                 | East:.. .................... 60.0
                 | West:....................... 30.0
                 | Top:........................ 50.0
                 | Bottom:..................... -50.0
                >>> B = SpatialExtent(north=40, south=30, east=60, west=30,
                ... bottom=-30, top=50)
                >>> C = A.intersect(B)
                >>> C.print_info()
                 +-------------------- Spatial extent ----------------------------------------+
                 | North:...................... 40.0
                 | South:...................... 30.0
                 | East:.. .................... 60.0
                 | West:....................... 30.0
                 | Top:........................ 50.0
                 | Bottom:..................... -30.0
                >>> B = SpatialExtent(north=40, south=30, east=60, west=30,
                ... bottom=-30, top=30)
                >>> C = A.intersect(B)
                >>> C.print_info()
                 +-------------------- Spatial extent ----------------------------------------+
                 | North:...................... 40.0
                 | South:...................... 30.0
                 | East:.. .................... 60.0
                 | West:....................... 30.0
                 | Top:........................ 30.0
                 | Bottom:..................... -30.0

             :param extent: The spatial extent to intersect with
             :return: The intersection spatial extent
            """

            if not self.overlapping(extent):
                return None

            new = self.intersect_2d(extent)

            eT = extent.get_top()
            eB = extent.get_bottom()

            T = self.get_top()
            B = self.get_bottom()

            nT = T
            nB = B

            if B < eB:
                nB = eB
            if T > eT:
                nT = eT

            new.set_top(nT)
            new.set_bottom(nB)

            return new

        def union_2d(self, extent):
            """Return the two dimensional union as spatial_extent
               object or None in case the extents does not overlap or meet.

            :param extent: The spatial extent to create a union with
            :return: The union spatial extent
            """
            if not self.overlapping_2d(extent) and not self.meet_2d(extent):
                return None

            return self.disjoint_union_2d(extent)

        def disjoint_union_2d(self, extent):
            """Return the two dimensional union as spatial_extent.

            :param extent: The spatial extent to create a union with
            :return: The union spatial extent
            """
            eN = extent.get_north()
            eS = extent.get_south()
            eE = extent.get_east()
            eW = extent.get_west()

            N = self.get_north()
            S = self.get_south()
            E = self.get_east()
            W = self.get_west()

            # Adjust the east and west in case of LL projection
            if self.get_projection() == "LL":
                while eE < W:
                    eE += 360.0
                    eW += 360.0

                while eW > E:
                    eE -= 360.0
                    eW -= 360.0

            # Compute the extent
            nN = N
            nS = S
            nE = E
            nW = W

            if W > eW:
                nW = eW
            if E < eE:
                nE = eE
            if N < eN:
                nN = eN
            if S > eS:
                nS = eS

            new = SpatialExtent(
                north=nN,
                south=nS,
                east=nE,
                west=nW,
                top=0,
                bottom=0,
                proj=self.get_projection(),
            )
            return new

        def union(self, extent):
            """Return the three dimensional union as spatial_extent
               object or None in case the extents does not overlap or meet.

            :param extent: The spatial extent to create a union with
            :return: The union spatial extent
            """
            if not self.overlapping(extent) and not self.meet(extent):
                return None

            return self.disjoint_union(extent)

        def disjoint_union(self, extent):
            """Return the three dimensional union as spatial_extent .

            Usage:

            .. code-block:: python

                >>> A = SpatialExtent(north=80, south=20, east=60, west=10,
                ... bottom=-50, top=50)
                >>> B = SpatialExtent(north=80, south=20, east=60, west=10,
                ... bottom=-50, top=50)
                >>> C = A.disjoint_union(B)
                >>> C.print_info()
                 +-------------------- Spatial extent ----------------------------------------+
                 | North:...................... 80.0
                 | South:...................... 20.0
                 | East:.. .................... 60.0
                 | West:....................... 10.0
                 | Top:........................ 50.0
                 | Bottom:..................... -50.0
                >>> B = SpatialExtent(north=40, south=30, east=60, west=10,
                ... bottom=-50, top=50)
                >>> C = A.disjoint_union(B)
                >>> C.print_info()
                 +-------------------- Spatial extent ----------------------------------------+
                 | North:...................... 80.0
                 | South:...................... 20.0
                 | East:.. .................... 60.0
                 | West:....................... 10.0
                 | Top:........................ 50.0
                 | Bottom:..................... -50.0
                >>> B = SpatialExtent(north=40, south=30, east=60, west=30,
                ... bottom=-50, top=50)
                >>> C = A.disjoint_union(B)
                >>> C.print_info()
                 +-------------------- Spatial extent ----------------------------------------+
                 | North:...................... 80.0
                 | South:...................... 20.0
                 | East:.. .................... 60.0
                 | West:....................... 10.0
                 | Top:........................ 50.0
                 | Bottom:..................... -50.0
                >>> B = SpatialExtent(north=40, south=30, east=60, west=30,
                ... bottom=-30, top=50)
                >>> C = A.disjoint_union(B)
                >>> C.print_info()
                 +-------------------- Spatial extent ----------------------------------------+
                 | North:...................... 80.0
                 | South:...................... 20.0
                 | East:.. .................... 60.0
                 | West:....................... 10.0
                 | Top:........................ 50.0
                 | Bottom:..................... -50.0
                >>> B = SpatialExtent(north=40, south=30, east=60, west=30,
                ... bottom=-30, top=30)
                >>> C = A.disjoint_union(B)
                >>> C.print_info()
                 +-------------------- Spatial extent ----------------------------------------+
                 | North:...................... 80.0
                 | South:...................... 20.0
                 | East:.. .................... 60.0
                 | West:....................... 10.0
                 | Top:........................ 50.0
                 | Bottom:..................... -50.0
                >>> A = SpatialExtent(north=80, south=20, east=60, west=10,
                ... bottom=-50, top=50)
                >>> B = SpatialExtent(north=90, south=80, east=70, west=20,
                ... bottom=-30, top=60)
                >>> C = A.disjoint_union(B)
                >>> C.print_info()
                 +-------------------- Spatial extent ----------------------------------------+
                 | North:...................... 90.0
                 | South:...................... 20.0
                 | East:.. .................... 70.0
                 | West:....................... 10.0
                 | Top:........................ 60.0
                 | Bottom:..................... -50.0

             :param extent: The spatial extent to create a disjoint union with
             :return: The union spatial extent
            """

            new = self.disjoint_union_2d(extent)

            eT = extent.get_top()
            eB = extent.get_bottom()

            T = self.get_top()
            B = self.get_bottom()

            nT = T
            nB = B

            if B > eB:
                nB = eB
            if T < eT:
                nT = eT

            new.set_top(nT)
            new.set_bottom(nB)

            return new

        def is_in_2d(self, extent):
            """Return True if this extent (A) is located in the provided spatial
            extent (B) in two dimensions.

            ::

                 _____
                |A _  |
                | |_| |
                |_____|B

            :param extent: The spatial extent
            :return: True or False
            """
            if self.get_projection() != extent.get_projection():
                self.msgr.error(
                    _(
                        "Projections are different. Unable to compute "
                        "is_in_2d for spatial extents"
                    )
                )
                return False

            eN = extent.get_north()
            eS = extent.get_south()
            eE = extent.get_east()
            eW = extent.get_west()

            N = self.get_north()
            S = self.get_south()
            E = self.get_east()
            W = self.get_west()

            # Adjust the east and west in case of LL projection
            if self.get_projection() == "LL":
                while eE < W:
                    eE += 360.0
                    eW += 360.0

                while eW > E:
                    eE -= 360.0
                    eW -= 360.0

            if W <= eW:
                return False
            if E >= eE:
                return False
            if N >= eN:
                return False
            if S <= eS:
                return False

            return True

        def is_in(self, extent):
            """Return True if this extent (A) is located in the provided spatial
            extent (B) in three dimensions.

            Usage:

            .. code-block:: python

                >>> A = SpatialExtent(north=79, south=21, east=59, west=11,
                ... bottom=-49, top=49)
                >>> B = SpatialExtent(north=80, south=20, east=60, west=10,
                ... bottom=-50, top=50)
                >>> A.is_in(B)
                True
                >>> B.is_in(A)
                False

            :param extent: The spatial extent
            :return: True or False
            """
            if not self.is_in_2d(extent):
                return False

            eT = extent.get_top()
            eB = extent.get_bottom()

            T = self.get_top()
            B = self.get_bottom()

            if B <= eB:
                return False
            if T >= eT:
                return False

            return True

        def contain_2d(self, extent):
            """Return True if this extent (A) contains the provided spatial
            extent (B) in two dimensions.

            Usage:

            .. code-block:: python

                >>> A = SpatialExtent(north=80, south=20, east=60, west=10)
                >>> B = SpatialExtent(north=79, south=21, east=59, west=11)
                >>> A.contain_2d(B)
                True
                >>> B.contain_2d(A)
                False

            :param extent: The spatial extent
            :return: True or False
            """
            return extent.is_in_2d(self)

        def contain(self, extent):
            """Return True if this extent (A) contains the provided spatial
            extent (B) in three dimensions.

            Usage:

            .. code-block:: python

                >>> A = SpatialExtent(north=80, south=20, east=60, west=10,
                ... bottom=-50, top=50)
                >>> B = SpatialExtent(north=79, south=21, east=59, west=11,
                ... bottom=-49, top=49)
                >>> A.contain(B)
                True
                >>> B.contain(A)
                False

            :param extent: The spatial extent
            :return: True or False
            """
            return extent.is_in(self)

        def equivalent_2d(self, extent):
            """Return True if this extent (A) is equal to the provided spatial
            extent (B) in two dimensions.

            Usage:

            .. code-block:: python

                >>> A = SpatialExtent(north=80, south=20, east=60, west=10)
                >>> B = SpatialExtent(north=80, south=20, east=60, west=10)
                >>> A.equivalent_2d(B)
                True
                >>> B.equivalent_2d(A)
                True

            :param extent: The spatial extent
            :return: True or False
            """
            if self.get_projection() != extent.get_projection():
                self.msgr.error(
                    _(
                        "Projections are different. Unable to compute "
                        "equivalent_2d for spatial extents"
                    )
                )
                return False

            eN = extent.get_north()
            eS = extent.get_south()
            eE = extent.get_east()
            eW = extent.get_west()

            N = self.get_north()
            S = self.get_south()
            E = self.get_east()
            W = self.get_west()

            # Adjust the east and west in case of LL projection
            if self.get_projection() == "LL":
                while eE < W:
                    eE += 360.0
                    eW += 360.0

                while eW > E:
                    eE -= 360.0
                    eW -= 360.0

            if W != eW:
                return False
            if E != eE:
                return False
            if N != eN:
                return False
            if S != eS:
                return False

            return True

        def equivalent(self, extent):
            """Return True if this extent (A) is equal to the provided spatial
            extent (B) in three dimensions.

            Usage:

            .. code-block:: python

                >>> A = SpatialExtent(north=80, south=20, east=60, west=10,
                ... bottom=-50, top=50)
                >>> B = SpatialExtent(north=80, south=20, east=60, west=10,
                ... bottom=-50, top=50)
                >>> A.equivalent(B)
                True
                >>> B.equivalent(A)
                True

            :param extent: The spatial extent
            :return: True or False
            """

            if not self.equivalent_2d(extent):
                return False

            eT = extent.get_top()
            eB = extent.get_bottom()

            T = self.get_top()
            B = self.get_bottom()

            if B != eB:
                return False
            if T != eT:
                return False

            return True

        def cover_2d(self, extent):
            """Return True if this extent (A) covers the provided spatial
            extent (B) in two dimensions.

            ::

                 _____    _____    _____    _____
                |A  __|  |__  A|  |A | B|  |B | A|
                |  |B |  | B|  |  |  |__|  |__|  |
                |__|__|  |__|__|  |_____|  |_____|

                 _____    _____    _____    _____
                |A|B| |  |A  __|  |A _  |  |__  A|
                | |_| |  |  |__|B | |B| | B|__|  |
                |_____|  |_____|  |_|_|_|  |_____|

                 _____    _____    _____    _____
                |A|B  |  |_____|A |A|B|A|  |_____|A
                | |   |  |B    |  | | | |  |_____|B
                |_|___|  |_____|  |_|_|_|  |_____|A

            The following cases are excluded:

            - contain
            - in
            - equivalent

            :param extent: The spatial extent
            :return: True or False
            """

            if self.get_projection() != extent.get_projection():
                self.msgr.error(
                    _(
                        "Projections are different. Unable to compute"
                        " cover_2d for spatial extents"
                    )
                )
                return False

            # Exclude equivalent_2d
            if self.equivalent_2d(extent):
                return False

            eN = extent.get_north()
            eS = extent.get_south()
            eE = extent.get_east()
            eW = extent.get_west()

            N = self.get_north()
            S = self.get_south()
            E = self.get_east()
            W = self.get_west()

            # Adjust the east and west in case of LL projection
            if self.get_projection() == "LL":
                while eE < W:
                    eE += 360.0
                    eW += 360.0

                while eW > E:
                    eE -= 360.0
                    eW -= 360.0

            # Edges of extent located outside of self are not allowed
            if E <= eW:
                return False
            if W >= eE:
                return False
            if N <= eS:
                return False
            if S >= eN:
                return False

            # First we check that at least one edge of extent meets an edge of self
            if W != eW and E != eE and N != eN and S != eS:
                return False

            # We check that at least one edge of extent is located in self
            edge_count = 0
            if W < eW and E > eW:
                edge_count += 1
            if E > eE and W < eE:
                edge_count += 1
            if N > eN and S < eN:
                edge_count += 1
            if S < eS and N > eS:
                edge_count += 1

            if edge_count == 0:
                return False

            return True

        def cover(self, extent):
            """Return True if this extent covers the provided spatial
            extent in three dimensions.

            The following cases are excluded:

            - contain
            - in
            - equivalent

            :param extent: The spatial extent
            :return: True or False
            """
            if self.get_projection() != extent.get_projection():
                self.msgr.error(
                    _(
                        "Projections are different. Unable to compute "
                        "cover for spatial extents"
                    )
                )
                return False

            # Exclude equivalent_2d
            if self.equivalent_2d(extent):
                return False

            eN = extent.get_north()
            eS = extent.get_south()
            eE = extent.get_east()
            eW = extent.get_west()

            eT = extent.get_top()
            eB = extent.get_bottom()

            N = self.get_north()
            S = self.get_south()
            E = self.get_east()
            W = self.get_west()

            T = self.get_top()
            B = self.get_bottom()

            # Adjust the east and west in case of LL projection
            if self.get_projection() == "LL":
                while eE < W:
                    eE += 360.0
                    eW += 360.0

                while eW > E:
                    eE -= 360.0
                    eW -= 360.0

            # Edges of extent located outside of self are not allowed
            if E <= eW:
                return False
            if W >= eE:
                return False
            if N <= eS:
                return False
            if S >= eN:
                return False
            if T <= eB:
                return False
            if B >= eT:
                return False

            # First we check that at least one edge of extent meets an edge of self
            if W != eW and E != eE and N != eN and S != eS and B != eB and T != eT:
                return False

            # We check that at least one edge of extent is located in self
            edge_count = 0
            if W < eW and E > eW:
                edge_count += 1
            if E > eE and W < eE:
                edge_count += 1
            if N > eN and S < eN:
                edge_count += 1
            if S < eS and N > eS:
                edge_count += 1
            if N > eN and S < eN:
                edge_count += 1
            if S < eS and N > eS:
                edge_count += 1
            if T > eT and B < eT:
                edge_count += 1
            if B < eB and T > eB:
                edge_count += 1

            if edge_count == 0:
                return False

            return True

        def covered_2d(self, extent):
            """Return True if this extent is covered by the provided spatial
            extent in two dimensions.

            The following cases are excluded:

            - contain
            - in
            - equivalent

            :param extent: The spatial extent
            :return: True or False
            """

            return extent.cover_2d(self)

        def covered(self, extent):
            """Return True if this extent is covered by the provided spatial
            extent in three dimensions.

            The following cases are excluded:

            - contain
            - in
            - equivalent

            :param extent: The spatial extent
            :return: True or False
            """

            return extent.cover(self)

        def overlap_2d(self, extent):
            """Return True if this extent (A) overlaps with the provided spatial
            extent (B) in two dimensions.
            Code is lend from wind_overlap.c in lib/gis

            ::

                 _____
                |A  __|__
                |  |  | B|
                |__|__|  |
                   |_____|

            The following cases are excluded:

            - contain
            - in
            - cover
            - covered
            - equivalent

            :param extent: The spatial extent
            :return: True or False
            """

            if self.contain_2d(extent):
                return False

            if self.is_in_2d(extent):
                return False

            if self.cover_2d(extent):
                return False

            if self.covered_2d(extent):
                return False

            if self.equivalent_2d(extent):
                return False

            N = extent.get_north()
            S = extent.get_south()
            E = extent.get_east()
            W = extent.get_west()

            # Adjust the east and west in case of LL projection
            if self.get_projection() == "LL":
                while E < self.get_west():
                    E += 360.0
                    W += 360.0

                while W > self.get_east():
                    E -= 360.0
                    W -= 360.0

            if self.get_north() <= S:
                return False

            if self.get_south() >= N:
                return False

            if self.get_east() <= W:
                return False

            if self.get_west() >= E:
                return False

            return True

        def overlap(self, extent):
            """Return True if this extent overlaps with the provided spatial
            extent in three dimensions.

            The following cases are excluded:

            - contain
            - in
            - cover
            - covered
            - equivalent

            :param extent: The spatial extent
            :return: True or False
            """

            if self.is_in(extent):
                return False

            if self.contain(extent):
                return False

            if self.cover(extent):
                return False

            if self.covered(extent):
                return False

            if self.equivalent(extent):
                return False

            N = extent.get_north()
            S = extent.get_south()
            E = extent.get_east()
            W = extent.get_west()
            T = extent.get_top()
            B = extent.get_bottom()

            # Adjust the east and west in case of LL projection
            if self.get_projection() == "LL":
                while E < self.get_west():
                    E += 360.0
                    W += 360.0

                while W > self.get_east():
                    E -= 360.0
                    W -= 360.0

            if self.get_north() <= S:
                return False

            if self.get_south() >= N:
                return False

            if self.get_east() <= W:
                return False

            if self.get_west() >= E:
                return False

            if self.get_top() <= B:
                return False

            if self.get_bottom() >= T:
                return False

            return True

        def meet_2d(self, extent):
            """Return True if this extent (A) meets with the provided spatial
            extent (B) in two dimensions.

            ::

                  _____ _____
                 |  A  |  B  |
                 |_____|     |
                       |_____|
                  _____ _____
                 |  B  |  A  |
                 |     |     |
                 |_____|_____|
                   ___
                  | A |
                  |   |
                  |___|
                 |  B  |
                 |     |
                 |_____|
                  _____
                 |  B  |
                 |     |
                 |_____|_
                   |  A  |
                   |     |
                   |_____|

            :param extent: The spatial extent
            :return: True or False
            """

            eN = extent.get_north()
            eS = extent.get_south()
            eE = extent.get_east()
            eW = extent.get_west()

            N = self.get_north()
            S = self.get_south()
            E = self.get_east()
            W = self.get_west()

            # Adjust the east and west in case of LL projection
            if self.get_projection() == "LL":
                while eE < W:
                    eE += 360.0
                    eW += 360.0

                while eW > E:
                    eE -= 360.0
                    eW -= 360.0

            edge = None
            edge_count = 0

            if E == eW:
                edge = "E"
                edge_count += 1
            if W == eE:
                edge = "W"
                edge_count += 1
            if N == eS:
                edge = "N"
                edge_count += 1
            if S == eN:
                edge = "S"
                edge_count += 1

            # Meet a a single edge only
            if edge_count != 1:
                return False

            # Check boundaries of the faces
            if edge == "E" or edge == "W":
                if N < eS or S > eN:
                    return False

            if edge == "N" or edge == "S":
                if E < eW or W > eE:
                    return False

            return True

        def meet(self, extent):
            """Return True if this extent meets with the provided spatial
            extent in three dimensions.

            :param extent: The spatial extent
            :return: True or False
            """
            eN = extent.get_north()
            eS = extent.get_south()
            eE = extent.get_east()
            eW = extent.get_west()

            eT = extent.get_top()
            eB = extent.get_bottom()

            N = self.get_north()
            S = self.get_south()
            E = self.get_east()
            W = self.get_west()

            T = self.get_top()
            B = self.get_bottom()

            # Adjust the east and west in case of LL projection
            if self.get_projection() == "LL":
                while eE < W:
                    eE += 360.0
                    eW += 360.0

                while eW > E:
                    eE -= 360.0
                    eW -= 360.0

            edge = None
            edge_count = 0

            if E == eW:
                edge = "E"
                edge_count += 1
            if W == eE:
                edge = "W"
                edge_count += 1
            if N == eS:
                edge = "N"
                edge_count += 1
            if S == eN:
                edge = "S"
                edge_count += 1
            if T == eB:
                edge = "T"
                edge_count += 1
            if B == eT:
                edge = "B"
                edge_count += 1

            # Meet a single edge only
            if edge_count != 1:
                return False

            # Check boundaries of the faces
            if edge == "E" or edge == "W":
                if N < eS or S > eN:
                    return False
                if T < eB or B > eT:
                    return False

            if edge == "N" or edge == "S":
                if E < eW or W > eE:
                    return False
                if T < eB or B > eT:
                    return False

            if edge == "T" or edge == "B":
                if E < eW or W > eE:
                    return False
                if N < eS or S > eN:
                    return False

            return True

        def disjoint_2d(self, extent):
            """Return True if this extent (A) is disjoint with the provided spatial
            extent (B) in three dimensions.

            ::

                  _____
                 |  A  |
                 |_____|
                 _______
                |   B   |
                |_______|

            :param extent: The spatial extent
            :return: True or False
            """

            if self.is_in_2d(extent):
                return False

            if self.contain_2d(extent):
                return False

            if self.cover_2d(extent):
                return False

            if self.covered_2d(extent):
                return False

            if self.equivalent_2d(extent):
                return False

            if self.overlapping_2d(extent):
                return False

            if self.meet_2d(extent):
                return False

            return True

        def disjoint(self, extent):
            """Return True if this extent is disjoint with the provided spatial
            extent in three dimensions.

            :param extent: The spatial extent
            :return: True or False
            """

            if self.is_in(extent):
                return False

            if self.contain(extent):
                return False

            if self.cover(extent):
                return False

            if self.covered(extent):
                return False

            if self.equivalent(extent):
                return False

            if self.overlapping(extent):
                return False

            if self.meet(extent):
                return False

            return True

        def spatial_relation_2d(self, extent):
            """Returns the two dimensional spatial relation between this
            extent and the provided spatial extent in two dimensions.

            Spatial relations are:

            - disjoint
            - meet
            - overlap
            - cover
            - covered
            - in
            - contain
            - equivalent

            Usage: see self.spatial_relation()
            """

            if self.equivalent_2d(extent):
                return "equivalent"
            if self.contain_2d(extent):
                return "contain"
            if self.is_in_2d(extent):
                return "in"
            if self.cover_2d(extent):
                return "cover"
            if self.covered_2d(extent):
                return "covered"
            if self.overlap_2d(extent):
                return "overlap"
            if self.meet_2d(extent):
                return "meet"
            if self.disjoint_2d(extent):
                return "disjoint"

            return "unknown"

        def spatial_relation(self, extent):
            """Returns the two dimensional spatial relation between this
            extent and the provided spatial extent in three dimensions.

            Spatial relations are:

            - disjoint
            - meet
            - overlap
            - cover
            - covered
            - in
            - contain
            - equivalent

            Usage:

            .. code-block:: python

                >>> A = SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
                >>> B = SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
                >>> A.spatial_relation(B)
                'equivalent'
                >>> B.spatial_relation(A)
                'equivalent'
                >>> B = SpatialExtent(north=70, south=20, east=60, west=10, bottom=-50, top=50)
                >>> A.spatial_relation_2d(B)
                'cover'
                >>> A.spatial_relation(B)
                'cover'
                >>> B = SpatialExtent(north=70, south=30, east=60, west=10, bottom=-50, top=50)
                >>> A.spatial_relation_2d(B)
                'cover'
                >>> A.spatial_relation(B)
                'cover'
                >>> B.spatial_relation_2d(A)
                'covered'
                >>> B.spatial_relation(A)
                'covered'
                >>> B = SpatialExtent(north=70, south=30, east=50, west=10, bottom=-50, top=50)
                >>> A.spatial_relation_2d(B)
                'cover'
                >>> B.spatial_relation_2d(A)
                'covered'
                >>> A.spatial_relation(B)
                'cover'
                >>> B = SpatialExtent(north=70, south=30, east=50, west=20, bottom=-50, top=50)
                >>> B.spatial_relation(A)
                'covered'
                >>> B = SpatialExtent(north=70, south=30, east=50, west=20, bottom=-50, top=50)
                >>> A.spatial_relation_2d(B)
                'contain'
                >>> A.spatial_relation(B)
                'cover'
                >>> B = SpatialExtent(north=70, south=30, east=50, west=20, bottom=-40, top=50)
                >>> A.spatial_relation(B)
                'cover'
                >>> B = SpatialExtent(north=70, south=30, east=50, west=20, bottom=-40, top=40)
                >>> A.spatial_relation(B)
                'contain'
                >>> B.spatial_relation(A)
                'in'
                >>> B = SpatialExtent(north=90, south=30, east=50, west=20, bottom=-40, top=40)
                >>> A.spatial_relation_2d(B)
                'overlap'
                >>> A.spatial_relation(B)
                'overlap'
                >>> B = SpatialExtent(north=90, south=5, east=70, west=5, bottom=-40, top=40)
                >>> A.spatial_relation_2d(B)
                'in'
                >>> A.spatial_relation(B)
                'overlap'
                >>> B = SpatialExtent(north=90, south=5, east=70, west=5, bottom=-40, top=60)
                >>> A.spatial_relation(B)
                'overlap'
                >>> B = SpatialExtent(north=90, south=5, east=70, west=5, bottom=-60, top=60)
                >>> A.spatial_relation(B)
                'in'
                >>> A = SpatialExtent(north=80, south=60, east=60, west=10, bottom=-50, top=50)
                >>> B = SpatialExtent(north=60, south=20, east=60, west=10, bottom=-50, top=50)
                >>> A.spatial_relation_2d(B)
                'meet'
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=60, south=40, east=60, west=10, bottom=-50, top=50)
                >>> B = SpatialExtent(north=80, south=60, east=60, west=10, bottom=-50, top=50)
                >>> A.spatial_relation_2d(B)
                'meet'
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=40, east=60, west=40, bottom=-50, top=50)
                >>> B = SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
                >>> A.spatial_relation_2d(B)
                'meet'
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
                >>> B = SpatialExtent(north=90, south=30, east=60, west=40, bottom=-50, top=50)
                >>> A.spatial_relation_2d(B)
                'meet'
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
                >>> B = SpatialExtent(north=70, south=50, east=60, west=40, bottom=-50, top=50)
                >>> A.spatial_relation_2d(B)
                'meet'
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
                >>> B = SpatialExtent(north=60, south=20, east=60, west=40, bottom=-50, top=50)
                >>> A.spatial_relation_2d(B)
                'meet'
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
                >>> B = SpatialExtent(north=40, south=20, east=60, west=40, bottom=-50, top=50)
                >>> A.spatial_relation_2d(B)
                'disjoint'
                >>> A.spatial_relation(B)
                'disjoint'
                >>> A = SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
                >>> B = SpatialExtent(north=60, south=20, east=60, west=40, bottom=-60, top=60)
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
                >>> B = SpatialExtent(north=90, south=30, east=60, west=40, bottom=-40, top=40)
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
                >>> B = SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
                >>> B = SpatialExtent(north=80, south=50, east=60, west=30, bottom=-50, top=0)
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
                >>> B = SpatialExtent(north=70, south=50, east=50, west=30, bottom=-50, top=0)
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
                >>> B = SpatialExtent(north=90, south=30, east=70, west=10, bottom=-50, top=0)
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
                >>> B = SpatialExtent(north=70, south=30, east=50, west=10, bottom=-50, top=0)
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
                >>> B = SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
                >>> B = SpatialExtent(north=80, south=50, east=60, west=30, bottom=0, top=50)
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
                >>> B = SpatialExtent(north=70, south=50, east=50, west=30, bottom=0, top=50)
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
                >>> B = SpatialExtent(north=90, south=30, east=70, west=10, bottom=0, top=50)
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
                >>> B = SpatialExtent(north=70, south=30, east=50, west=10, bottom=0, top=50)
                >>> A.spatial_relation(B)
                'meet'
                >>> A = SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
                >>> B = SpatialExtent(north=90, south=81, east=60, west=10, bottom=-50, top=50)
                >>> A.spatial_relation(B)
                'disjoint'
                >>> A = SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
                >>> B = SpatialExtent(north=90, south=80, east=60, west=10, bottom=-50, top=50)
                >>> A.spatial_relation(B)
                'meet'

            """

            if self.equivalent(extent):
                return "equivalent"
            if self.contain(extent):
                return "contain"
            if self.is_in(extent):
                return "in"
            if self.cover(extent):
                return "cover"
            if self.covered(extent):
                return "covered"
            if self.overlap(extent):
                return "overlap"
            if self.meet(extent):
                return "meet"
            if self.disjoint(extent):
                return "disjoint"

            return "unknown"

        def set_spatial_extent_from_values(self, north, south, east, west, top, bottom):
            """Set the three dimensional spatial extent

            :param north: The northern edge
            :param south: The southern edge
            :param east: The eastern edge
            :param west: The western edge
            :param top: The top edge
            :param bottom: The bottom edge
            """

            self.set_north(north)
            self.set_south(south)
            self.set_east(east)
            self.set_west(west)
            self.set_top(top)
            self.set_bottom(bottom)

        def set_spatial_extent(self, spatial_extent):
            """Set the three dimensional spatial extent

            :param spatial_extent: An object of type SpatialExtent or its
                                   subclasses
            """

            self.set_north(spatial_extent.get_north())
            self.set_south(spatial_extent.get_south())
            self.set_east(spatial_extent.get_east())
            self.set_west(spatial_extent.get_west())
            self.set_top(spatial_extent.get_top())
            self.set_bottom(spatial_extent.get_bottom())

        def set_projection(self, proj):
            """Set the projection of the spatial extent it should be XY or LL.
            As default the projection is XY
            """
            if proj is None or (proj != "XY" and proj != "LL"):
                self.D["proj"] = "XY"
            else:
                self.D["proj"] = proj

        def set_spatial_extent_from_values_2d(self, north, south, east, west):
            """Set the two dimensional spatial extent from values

            :param north: The northern edge
            :param south: The southern edge
            :param east: The eastern edge
            :param west: The western edge
            """

            self.set_north(north)
            self.set_south(south)
            self.set_east(east)
            self.set_west(west)

        def set_spatial_extent_2d(self, spatial_extent):
            """Set the three dimensional spatial extent

            :param spatial_extent: An object of type SpatialExtent or its
                                   subclasses
            """

            self.set_north(spatial_extent.north)
            self.set_south(spatial_extent.south)
            self.set_east(spatial_extent.east)
            self.set_west(spatial_extent.west)

        def set_id(self, ident):
            """Convenient method to set the unique identifier (primary key)"""
            self.ident = ident
            self.D["id"] = ident

        def set_north(self, north):
            """Set the northern edge of the map"""
            if north is not None:
                self.D["north"] = float(north)
            else:
                self.D["north"] = None

        def set_south(self, south):
            """Set the southern edge of the map"""
            if south is not None:
                self.D["south"] = float(south)
            else:
                self.D["south"] = None

        def set_west(self, west):
            """Set the western edge of the map"""
            if west is not None:
                self.D["west"] = float(west)
            else:
                self.D["west"] = None

        def set_east(self, east):
            """Set the eastern edge of the map"""
            if east is not None:
                self.D["east"] = float(east)
            else:
                self.D["east"] = None

        def set_top(self, top):
            """Set the top edge of the map"""
            if top is not None:
                self.D["top"] = float(top)
            else:
                self.D["top"] = None

        def set_bottom(self, bottom):
            """Set the bottom edge of the map"""
            if bottom is not None:
                self.D["bottom"] = float(bottom)
            else:
                self.D["bottom"] = None

        def get_id(self):
            """Convenient method to get the unique identifier (primary key)
            :return: None if not found
            """
            if "id" in self.D:
                return self.D["id"]
            else:
                return None

        def get_projection(self):
            """Get the projection of the spatial extent"""
            return self.D["proj"]

        def get_volume(self):
            """Compute the volume of the extent, in case z is zero
            (top == bottom or top - bottom = 1) the area is returned"""

            if self.get_projection() == "LL":
                self.msgr.error(
                    _("Volume computation is not supported " "for LL projections")
                )

            area = self.get_area()

            bbox = self.get_spatial_extent_as_tuple()

            z = abs(bbox[4] - bbox[5])

            if z == 0:
                z = 1.0

            return area * z

        def get_area(self):
            """Compute the area of the extent, extent in z direction is ignored"""

            if self.get_projection() == "LL":
                self.msgr.error(
                    _("Area computation is not supported " "for LL projections")
                )

            bbox = self.get_spatial_extent_as_tuple()

            y = abs(bbox[0] - bbox[1])
            x = abs(bbox[2] - bbox[3])

            return x * y

        def get_spatial_extent_as_tuple(self):
            """Return a tuple (north, south, east, west, top, bottom)
            of the spatial extent"""

            return (self.north, self.south, self.east, self.west, self.top, self.bottom)

        def get_spatial_extent_as_tuple_2d(self):
            """Return a tuple (north, south, east, west,) of the 2d spatial extent"""
            return (self.north, self.south, self.east, self.west)

        def get_north(self):
            """Get the northern edge of the map
            :return: None if not found"""
            if "north" in self.D:
                return self.D["north"]
            else:
                return None

        def get_south(self):
            """Get the southern edge of the map
            :return: None if not found"""
            if "south" in self.D:
                return self.D["south"]
            else:
                return None

        def get_east(self):
            """Get the eastern edge of the map
            :return: None if not found"""
            if "east" in self.D:
                return self.D["east"]
            else:
                return None

        def get_west(self):
            """Get the western edge of the map
            :return: None if not found"""
            if "west" in self.D:
                return self.D["west"]
            else:
                return None

        def get_top(self):
            """Get the top edge of the map
            :return: None if not found"""
            if "top" in self.D:
                return self.D["top"]
            else:
                return None

        def get_bottom(self):
            """Get the bottom edge of the map
            :return: None if not found"""
            if "bottom" in self.D:
                return self.D["bottom"]
            else:
                return None

        id = property(fget=get_id, fset=set_id)
        north = property(fget=get_north, fset=set_north)
        south = property(fget=get_south, fset=set_south)
        east = property(fget=get_east, fset=set_east)
        west = property(fget=get_west, fset=set_west)
        top = property(fget=get_top, fset=set_top)
        bottom = property(fget=get_bottom, fset=set_bottom)

        def print_info(self):
            """Print information about this class in human readable style"""
            #      0123456789012345678901234567890
            print(
                " +-------------------- Spatial extent ----------------------------------------+"
            )
            print(" | North:...................... " + str(self.get_north()))
            print(" | South:...................... " + str(self.get_south()))
            print(" | East:.. .................... " + str(self.get_east()))
            print(" | West:....................... " + str(self.get_west()))
            print(" | Top:........................ " + str(self.get_top()))
            print(" | Bottom:..................... " + str(self.get_bottom()))

        def print_shell_info(self):
            """Print information about this class in shell style"""
            print("north=" + str(self.get_north()))
            print("south=" + str(self.get_south()))
            print("east=" + str(self.get_east()))
            print("west=" + str(self.get_west()))
            print("top=" + str(self.get_top()))
            print("bottom=" + str(self.get_bottom()))

    ###############################################################################

    class RasterSpatialExtent(SpatialExtent):
        def __init__(
            self,
            ident=None,
            north=None,
            south=None,
            east=None,
            west=None,
            top=None,
            bottom=None,
        ):
            SpatialExtent.__init__(
                self, "raster_spatial_extent", ident, north, south, east, west, top, bottom
            )

    class Raster3DSpatialExtent(SpatialExtent):
        def __init__(
            self,
            ident=None,
            north=None,
            south=None,
            east=None,
            west=None,
            top=None,
            bottom=None,
        ):
            SpatialExtent.__init__(
                self,
                "raster3d_spatial_extent",
                ident,
                north,
                south,
                east,
                west,
                top,
                bottom,
            )

    class VectorSpatialExtent(SpatialExtent):
        def __init__(
            self,
            ident=None,
            north=None,
            south=None,
            east=None,
            west=None,
            top=None,
            bottom=None,
        ):
            SpatialExtent.__init__(
                self, "vector_spatial_extent", ident, north, south, east, west, top, bottom
            )

    class STRDSSpatialExtent(SpatialExtent):
        def __init__(
            self,
            ident=None,
            north=None,
            south=None,
            east=None,
            west=None,
            top=None,
            bottom=None,
        ):
            SpatialExtent.__init__(
                self, "strds_spatial_extent", ident, north, south, east, west, top, bottom
            )

    class STR3DSSpatialExtent(SpatialExtent):
        def __init__(
            self,
            ident=None,
            north=None,
            south=None,
            east=None,
            west=None,
            top=None,
            bottom=None,
        ):
            SpatialExtent.__init__(
                self, "str3ds_spatial_extent", ident, north, south, east, west, top, bottom
            )

    class STVDSSpatialExtent(SpatialExtent):
        def __init__(
            self,
            ident=None,
            north=None,
            south=None,
            east=None,
            west=None,
            top=None,
            bottom=None,
        ):
            SpatialExtent.__init__(
                self, "stvds_spatial_extent", ident, north, south, east, west, top, bottom
            )

    ###############################################################################

    if __name__ == "__main__":
        import doctest

        doctest.testmod()

Help Index | Topics Index | Keywords Index | Full Index

© 2003-2024 GRASS Development Team, GRASS GIS 8.2.2dev Reference Manual