|
- 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, ProjectInterpolateMixin,
- )
- from django.contrib.gis.geos.point import Point
- from django.contrib.gis.shortcuts import numpy
- from django.utils.six.moves import range
-
-
- class LineString(ProjectInterpolateMixin, GEOSGeometry):
- _init_func = capi.create_linestring
- _minlength = 2
- has_cs = True
-
- def __init__(self, *args, **kwargs):
- """
- Initializes 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 isinstance(coords, (tuple, list)):
- # Getting the number of coords and the number of dimensions -- which
- # must stay the same, e.g., no LineString((1, 2), (1, 2, 3)).
- ncoords = len(coords)
- if coords:
- ndim = len(coords[0])
- else:
- raise TypeError('Cannot initialize on empty sequence.')
- self._checkdim(ndim)
- # Incrementing through each of the coordinates and verifying
- for i in range(1, ncoords):
- if not isinstance(coords[i], (tuple, list, Point)):
- raise TypeError('each coordinate should be a sequence (list or tuple)')
- if len(coords[i]) != ndim:
- raise TypeError('Dimension mismatch.')
- numpy_coords = False
- elif numpy and isinstance(coords, numpy.ndarray):
- shape = coords.shape # Using numpy's shape.
- if len(shape) != 2:
- raise TypeError('Too many dimensions.')
- self._checkdim(shape[1])
- ncoords = shape[0]
- ndim = shape[1]
- numpy_coords = True
- else:
- raise TypeError('Invalid initialization input for LineStrings.')
-
- # Creating a coordinate sequence object because it is easier to
- # set the points using GEOSCoordSeq.__setitem__().
- cs = GEOSCoordSeq(capi.create_cs(ncoords, ndim), z=bool(ndim == 3))
-
- for i in range(ncoords):
- if numpy_coords:
- cs[i] = coords[i, :]
- elif isinstance(coords[i], Point):
- cs[i] = coords[i].tuple
- else:
- cs[i] = coords[i]
-
- # If SRID was passed in with the keyword arguments
- srid = kwargs.get('srid')
-
- # Calling the base geometry initialization with the returned pointer
- # from the function.
- super(LineString, self).__init__(self._init_func(cs.ptr), srid=srid)
-
- def __iter__(self):
- "Allows iteration over this LineString."
- for i in range(len(self)):
- yield self[i]
-
- def __len__(self):
- "Returns 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
-
- # 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
- self._post_init(self.srid)
- else:
- # can this happen?
- raise GEOSException('Geometry resulting from slice deletion was invalid.')
-
- def _set_single(self, index, value):
- self._checkindex(index)
- self._cs[index] = value
-
- def _checkdim(self, dim):
- if dim not in (2, 3):
- raise TypeError('Dimension mismatch.')
-
- # #### Sequence Properties ####
- @property
- def tuple(self):
- "Returns a tuple version of the geometry from the coordinate sequence."
- return self._cs.tuple
- coords = tuple
-
- def _listarr(self, func):
- """
- Internal routine that returns a sequence (list) corresponding with
- the given function. Will 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):
- "Returns a numpy array for the LineString."
- return self._listarr(self._cs.__getitem__)
-
- @property
- def merged(self):
- "Returns the line merge of this LineString."
- return self._topology(capi.geos_linemerge(self.ptr))
-
- @property
- def x(self):
- "Returns a list or numpy array of the X variable."
- return self._listarr(self._cs.getX)
-
- @property
- def y(self):
- "Returns a list or numpy array of the Y variable."
- return self._listarr(self._cs.getY)
-
- @property
- def z(self):
- "Returns 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
|