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.

linestring.py 5.7 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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