# Django 4.2.11 documentation

# Source code for django.contrib.gis.gdal.envelope

    """
     The GDAL/OGR library uses an Envelope structure to hold the bounding
     box information for a geometry.  The envelope (bounding box) contains
     two pairs of coordinates, one for the lower left coordinate and one
     for the upper right coordinate:

                               +----------o Upper right; (max_x, max_y)
                               |          |
                               |          |
                               |          |
     Lower left (min_x, min_y) o----------+
    """
    from ctypes import Structure, c_double

    from django.contrib.gis.gdal.error import GDALException

    # The OGR definition of an Envelope is a C structure containing four doubles.
    #  See the 'ogr_core.h' source file for more information:
    #   https://gdal.org/doxygen/ogr__core_8h_source.html
    class OGREnvelope(Structure):
        "Represent the OGREnvelope C Structure."
        _fields_ = [
            ("MinX", c_double),
            ("MaxX", c_double),
            ("MinY", c_double),
            ("MaxY", c_double),
        ]

    class Envelope:
        """
        The Envelope object is a C structure that contains the minimum and
        maximum X, Y coordinates for a rectangle bounding box.  The naming
        of the variables is compatible with the OGR Envelope structure.
        """

        def __init__(self, *args):
            """
            The initialization function may take an OGREnvelope structure, 4-element
            tuple or list, or 4 individual arguments.
            """

            if len(args) == 1:
                if isinstance(args[0], OGREnvelope):
                    # OGREnvelope (a ctypes Structure) was passed in.
                    self._envelope = args[0]
                elif isinstance(args[0], (tuple, list)):
                    # A tuple was passed in.
                    if len(args[0]) != 4:
                        raise GDALException(
                            "Incorrect number of tuple elements (%d)." % len(args[0])
                        )
                    else:
                        self._from_sequence(args[0])
                else:
                    raise TypeError("Incorrect type of argument: %s" % type(args[0]))
            elif len(args) == 4:
                # Individual parameters passed in.
                #  Thanks to ww for the help
                self._from_sequence([float(a) for a in args])
            else:
                raise GDALException("Incorrect number (%d) of arguments." % len(args))

            # Checking the x,y coordinates
            if self.min_x > self.max_x:
                raise GDALException("Envelope minimum X > maximum X.")
            if self.min_y > self.max_y:
                raise GDALException("Envelope minimum Y > maximum Y.")

        def __eq__(self, other):
            """
            Return True if the envelopes are equivalent; can compare against
            other Envelopes and 4-tuples.
            """
            if isinstance(other, Envelope):
                return (
                    (self.min_x == other.min_x)
                    and (self.min_y == other.min_y)
                    and (self.max_x == other.max_x)
                    and (self.max_y == other.max_y)
                )
            elif isinstance(other, tuple) and len(other) == 4:
                return (
                    (self.min_x == other[0])
                    and (self.min_y == other[1])
                    and (self.max_x == other[2])
                    and (self.max_y == other[3])
                )
            else:
                raise GDALException("Equivalence testing only works with other Envelopes.")

        def __str__(self):
            "Return a string representation of the tuple."
            return str(self.tuple)

        def _from_sequence(self, seq):
            "Initialize the C OGR Envelope structure from the given sequence."
            self._envelope = OGREnvelope()
            self._envelope.MinX = seq[0]
            self._envelope.MinY = seq[1]
            self._envelope.MaxX = seq[2]
            self._envelope.MaxY = seq[3]

        def expand_to_include(self, *args):
            """
            Modify the envelope to expand to include the boundaries of
            the passed-in 2-tuple (a point), 4-tuple (an extent) or
            envelope.
            """
            # We provide a number of different signatures for this method,
            # and the logic here is all about converting them into a
            # 4-tuple single parameter which does the actual work of
            # expanding the envelope.
            if len(args) == 1:
                if isinstance(args[0], Envelope):
                    return self.expand_to_include(args[0].tuple)
                elif hasattr(args[0], "x") and hasattr(args[0], "y"):
                    return self.expand_to_include(
                        args[0].x, args[0].y, args[0].x, args[0].y
                    )
                elif isinstance(args[0], (tuple, list)):
                    # A tuple was passed in.
                    if len(args[0]) == 2:
                        return self.expand_to_include(
                            (args[0][0], args[0][1], args[0][0], args[0][1])
                        )
                    elif len(args[0]) == 4:
                        (minx, miny, maxx, maxy) = args[0]
                        if minx < self._envelope.MinX:
                            self._envelope.MinX = minx
                        if miny < self._envelope.MinY:
                            self._envelope.MinY = miny
                        if maxx > self._envelope.MaxX:
                            self._envelope.MaxX = maxx
                        if maxy > self._envelope.MaxY:
                            self._envelope.MaxY = maxy
                    else:
                        raise GDALException(
                            "Incorrect number of tuple elements (%d)." % len(args[0])
                        )
                else:
                    raise TypeError("Incorrect type of argument: %s" % type(args[0]))
            elif len(args) == 2:
                # An x and an y parameter were passed in
                return self.expand_to_include((args[0], args[1], args[0], args[1]))
            elif len(args) == 4:
                # Individual parameters passed in.
                return self.expand_to_include(args)
            else:
                raise GDALException("Incorrect number (%d) of arguments." % len(args[0]))

        @property
        def min_x(self):
            "Return the value of the minimum X coordinate."
            return self._envelope.MinX

        @property
        def min_y(self):
            "Return the value of the minimum Y coordinate."
            return self._envelope.MinY

        @property
        def max_x(self):
            "Return the value of the maximum X coordinate."
            return self._envelope.MaxX

        @property
        def max_y(self):
            "Return the value of the maximum Y coordinate."
            return self._envelope.MaxY

        @property
        def ur(self):
            "Return the upper-right coordinate."
            return (self.max_x, self.max_y)

        @property
        def ll(self):
            "Return the lower-left coordinate."
            return (self.min_x, self.min_y)

        @property
        def tuple(self):
            "Return a tuple representing the envelope."
            return (self.min_x, self.min_y, self.max_x, self.max_y)

        @property
        def wkt(self):
            "Return WKT representing a Polygon for this envelope."
            # TODO: Fix significant figures.
            return "POLYGON((%s %s,%s %s,%s %s,%s %s,%s %s))" % (
                self.min_x,
                self.min_y,
                self.min_x,
                self.max_y,
                self.max_x,
                self.max_y,
                self.max_x,
                self.min_y,
                self.min_x,
                self.min_y,
            )