# Django 4.2.11 documentation

# Source code for django.contrib.gis.geos.linestring

    from django.contrib.gis.geos import prototypes as capi
    from django.contrib.gis.geos.coordseq import GEOSCoordSeq
    from django.contrib.gis.geos.error import GEOSException
    from django.contrib.gis.geos.geometry import GEOSGeometry, LinearGeometryMixin
    from django.contrib.gis.geos.point import Point
    from django.contrib.gis.shortcuts import numpy

    class LineString(LinearGeometryMixin, GEOSGeometry):
        _init_func = capi.create_linestring
        _minlength = 2
        has_cs = True

        def __init__(self, *args, **kwargs):
            """
            Initialize on the given sequence -- may take lists, tuples, NumPy arrays
            of X,Y pairs, or Point objects.  If Point objects are used, ownership is
            _not_ transferred to the LineString object.

            Examples:
             ls = LineString((1, 1), (2, 2))
             ls = LineString([(1, 1), (2, 2)])
             ls = LineString(array([(1, 1), (2, 2)]))
             ls = LineString(Point(1, 1), Point(2, 2))
            """
            # If only one argument provided, set the coords array appropriately
            if len(args) == 1:
                coords = args[0]
            else:
                coords = args

            if not (
                isinstance(coords, (tuple, list))
                or numpy
                and isinstance(coords, numpy.ndarray)
            ):
                raise TypeError("Invalid initialization input for LineStrings.")

            # If SRID was passed in with the keyword arguments
            srid = kwargs.get("srid")

            ncoords = len(coords)
            if not ncoords:
                super().__init__(self._init_func(None), srid=srid)
                return

            if ncoords < self._minlength:
                raise ValueError(
                    "%s requires at least %d points, got %s."
                    % (
                        self.__class__.__name__,
                        self._minlength,
                        ncoords,
                    )
                )

            numpy_coords = not isinstance(coords, (tuple, list))
            if numpy_coords:
                shape = coords.shape  # Using numpy's shape.
                if len(shape) != 2:
                    raise TypeError("Too many dimensions.")
                self._checkdim(shape[1])
                ndim = shape[1]
            else:
                # Getting the number of coords and the number of dimensions -- which
                #  must stay the same, e.g., no LineString((1, 2), (1, 2, 3)).
                ndim = None
                # Incrementing through each of the coordinates and verifying
                for coord in coords:
                    if not isinstance(coord, (tuple, list, Point)):
                        raise TypeError(
                            "Each coordinate should be a sequence (list or tuple)"
                        )

                    if ndim is None:
                        ndim = len(coord)
                        self._checkdim(ndim)
                    elif len(coord) != ndim:
                        raise TypeError("Dimension mismatch.")

            # Creating a coordinate sequence object because it is easier to
            # set the points using its methods.
            cs = GEOSCoordSeq(capi.create_cs(ncoords, ndim), z=bool(ndim == 3))
            point_setter = cs._set_point_3d if ndim == 3 else cs._set_point_2d

            for i in range(ncoords):
                if numpy_coords:
                    point_coords = coords[i, :]
                elif isinstance(coords[i], Point):
                    point_coords = coords[i].tuple
                else:
                    point_coords = coords[i]
                point_setter(i, point_coords)

            # Calling the base geometry initialization with the returned pointer
            #  from the function.
            super().__init__(self._init_func(cs.ptr), srid=srid)

        def __iter__(self):
            "Allow iteration over this LineString."
            for i in range(len(self)):
                yield self[i]

        def __len__(self):
            "Return the number of points in this LineString."
            return len(self._cs)

        def _get_single_external(self, index):
            return self._cs[index]

        _get_single_internal = _get_single_external

        def _set_list(self, length, items):
            ndim = self._cs.dims
            hasz = self._cs.hasz  # I don't understand why these are different
            srid = self.srid

            # create a new coordinate sequence and populate accordingly
            cs = GEOSCoordSeq(capi.create_cs(length, ndim), z=hasz)
            for i, c in enumerate(items):
                cs[i] = c

            ptr = self._init_func(cs.ptr)
            if ptr:
                capi.destroy_geom(self.ptr)
                self.ptr = ptr
                if srid is not None:
                    self.srid = srid
                self._post_init()
            else:
                # can this happen?
                raise GEOSException("Geometry resulting from slice deletion was invalid.")

        def _set_single(self, index, value):
            self._cs[index] = value

        def _checkdim(self, dim):
            if dim not in (2, 3):
                raise TypeError("Dimension mismatch.")

        # #### Sequence Properties ####
        @property
        def tuple(self):
            "Return a tuple version of the geometry from the coordinate sequence."
            return self._cs.tuple

        coords = tuple

        def _listarr(self, func):
            """
            Return a sequence (list) corresponding with the given function.
            Return a numpy array if possible.
            """
            lst = [func(i) for i in range(len(self))]
            if numpy:
                return numpy.array(lst)  # ARRRR!
            else:
                return lst

        @property
        def array(self):
            "Return a numpy array for the LineString."
            return self._listarr(self._cs.__getitem__)

        @property
        def x(self):
            "Return a list or numpy array of the X variable."
            return self._listarr(self._cs.getX)

        @property
        def y(self):
            "Return a list or numpy array of the Y variable."
            return self._listarr(self._cs.getY)

        @property
        def z(self):
            "Return a list or numpy array of the Z variable."
            if not self.hasz:
                return None
            else:
                return self._listarr(self._cs.getZ)

    # LinearRings are LineStrings used within Polygons.

    class LinearRing(LineString):
        _minlength = 4
        _init_func = capi.create_linearring

        @property
        def is_counterclockwise(self):
            if self.empty:
                raise ValueError("Orientation of an empty LinearRing cannot be determined.")
            return self._cs.is_counterclockwise