You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

172 lines
5.7 KiB

  1. from django.contrib.gis.geos import prototypes as capi
  2. from django.contrib.gis.geos.coordseq import GEOSCoordSeq
  3. from django.contrib.gis.geos.error import GEOSException
  4. from django.contrib.gis.geos.geometry import (
  5. GEOSGeometry, ProjectInterpolateMixin,
  6. )
  7. from django.contrib.gis.geos.point import Point
  8. from django.contrib.gis.shortcuts import numpy
  9. from django.utils.six.moves import range
  10. class LineString(ProjectInterpolateMixin, GEOSGeometry):
  11. _init_func = capi.create_linestring
  12. _minlength = 2
  13. has_cs = True
  14. def __init__(self, *args, **kwargs):
  15. """
  16. Initializes on the given sequence -- may take lists, tuples, NumPy arrays
  17. of X,Y pairs, or Point objects. If Point objects are used, ownership is
  18. _not_ transferred to the LineString object.
  19. Examples:
  20. ls = LineString((1, 1), (2, 2))
  21. ls = LineString([(1, 1), (2, 2)])
  22. ls = LineString(array([(1, 1), (2, 2)]))
  23. ls = LineString(Point(1, 1), Point(2, 2))
  24. """
  25. # If only one argument provided, set the coords array appropriately
  26. if len(args) == 1:
  27. coords = args[0]
  28. else:
  29. coords = args
  30. if isinstance(coords, (tuple, list)):
  31. # Getting the number of coords and the number of dimensions -- which
  32. # must stay the same, e.g., no LineString((1, 2), (1, 2, 3)).
  33. ncoords = len(coords)
  34. if coords:
  35. ndim = len(coords[0])
  36. else:
  37. raise TypeError('Cannot initialize on empty sequence.')
  38. self._checkdim(ndim)
  39. # Incrementing through each of the coordinates and verifying
  40. for i in range(1, ncoords):
  41. if not isinstance(coords[i], (tuple, list, Point)):
  42. raise TypeError('each coordinate should be a sequence (list or tuple)')
  43. if len(coords[i]) != ndim:
  44. raise TypeError('Dimension mismatch.')
  45. numpy_coords = False
  46. elif numpy and isinstance(coords, numpy.ndarray):
  47. shape = coords.shape # Using numpy's shape.
  48. if len(shape) != 2:
  49. raise TypeError('Too many dimensions.')
  50. self._checkdim(shape[1])
  51. ncoords = shape[0]
  52. ndim = shape[1]
  53. numpy_coords = True
  54. else:
  55. raise TypeError('Invalid initialization input for LineStrings.')
  56. # Creating a coordinate sequence object because it is easier to
  57. # set the points using GEOSCoordSeq.__setitem__().
  58. cs = GEOSCoordSeq(capi.create_cs(ncoords, ndim), z=bool(ndim == 3))
  59. for i in range(ncoords):
  60. if numpy_coords:
  61. cs[i] = coords[i, :]
  62. elif isinstance(coords[i], Point):
  63. cs[i] = coords[i].tuple
  64. else:
  65. cs[i] = coords[i]
  66. # If SRID was passed in with the keyword arguments
  67. srid = kwargs.get('srid')
  68. # Calling the base geometry initialization with the returned pointer
  69. # from the function.
  70. super(LineString, self).__init__(self._init_func(cs.ptr), srid=srid)
  71. def __iter__(self):
  72. "Allows iteration over this LineString."
  73. for i in range(len(self)):
  74. yield self[i]
  75. def __len__(self):
  76. "Returns the number of points in this LineString."
  77. return len(self._cs)
  78. def _get_single_external(self, index):
  79. return self._cs[index]
  80. _get_single_internal = _get_single_external
  81. def _set_list(self, length, items):
  82. ndim = self._cs.dims
  83. hasz = self._cs.hasz # I don't understand why these are different
  84. # create a new coordinate sequence and populate accordingly
  85. cs = GEOSCoordSeq(capi.create_cs(length, ndim), z=hasz)
  86. for i, c in enumerate(items):
  87. cs[i] = c
  88. ptr = self._init_func(cs.ptr)
  89. if ptr:
  90. capi.destroy_geom(self.ptr)
  91. self.ptr = ptr
  92. self._post_init(self.srid)
  93. else:
  94. # can this happen?
  95. raise GEOSException('Geometry resulting from slice deletion was invalid.')
  96. def _set_single(self, index, value):
  97. self._checkindex(index)
  98. self._cs[index] = value
  99. def _checkdim(self, dim):
  100. if dim not in (2, 3):
  101. raise TypeError('Dimension mismatch.')
  102. # #### Sequence Properties ####
  103. @property
  104. def tuple(self):
  105. "Returns a tuple version of the geometry from the coordinate sequence."
  106. return self._cs.tuple
  107. coords = tuple
  108. def _listarr(self, func):
  109. """
  110. Internal routine that returns a sequence (list) corresponding with
  111. the given function. Will return a numpy array if possible.
  112. """
  113. lst = [func(i) for i in range(len(self))]
  114. if numpy:
  115. return numpy.array(lst) # ARRRR!
  116. else:
  117. return lst
  118. @property
  119. def array(self):
  120. "Returns a numpy array for the LineString."
  121. return self._listarr(self._cs.__getitem__)
  122. @property
  123. def merged(self):
  124. "Returns the line merge of this LineString."
  125. return self._topology(capi.geos_linemerge(self.ptr))
  126. @property
  127. def x(self):
  128. "Returns a list or numpy array of the X variable."
  129. return self._listarr(self._cs.getX)
  130. @property
  131. def y(self):
  132. "Returns a list or numpy array of the Y variable."
  133. return self._listarr(self._cs.getY)
  134. @property
  135. def z(self):
  136. "Returns a list or numpy array of the Z variable."
  137. if not self.hasz:
  138. return None
  139. else:
  140. return self._listarr(self._cs.getZ)
  141. # LinearRings are LineStrings used within Polygons.
  142. class LinearRing(LineString):
  143. _minlength = 4
  144. _init_func = capi.create_linearring