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.

envelope.py 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. """
  2. The GDAL/OGR library uses an Envelope structure to hold the bounding
  3. box information for a geometry. The envelope (bounding box) contains
  4. two pairs of coordinates, one for the lower left coordinate and one
  5. for the upper right coordinate:
  6. +----------o Upper right; (max_x, max_y)
  7. | |
  8. | |
  9. | |
  10. Lower left (min_x, min_y) o----------+
  11. """
  12. from ctypes import Structure, c_double
  13. from django.contrib.gis.gdal.error import GDALException
  14. # The OGR definition of an Envelope is a C structure containing four doubles.
  15. # See the 'ogr_core.h' source file for more information:
  16. # https://www.gdal.org/ogr__core_8h_source.html
  17. class OGREnvelope(Structure):
  18. "Represent the OGREnvelope C Structure."
  19. _fields_ = [("MinX", c_double),
  20. ("MaxX", c_double),
  21. ("MinY", c_double),
  22. ("MaxY", c_double),
  23. ]
  24. class Envelope:
  25. """
  26. The Envelope object is a C structure that contains the minimum and
  27. maximum X, Y coordinates for a rectangle bounding box. The naming
  28. of the variables is compatible with the OGR Envelope structure.
  29. """
  30. def __init__(self, *args):
  31. """
  32. The initialization function may take an OGREnvelope structure, 4-element
  33. tuple or list, or 4 individual arguments.
  34. """
  35. if len(args) == 1:
  36. if isinstance(args[0], OGREnvelope):
  37. # OGREnvelope (a ctypes Structure) was passed in.
  38. self._envelope = args[0]
  39. elif isinstance(args[0], (tuple, list)):
  40. # A tuple was passed in.
  41. if len(args[0]) != 4:
  42. raise GDALException('Incorrect number of tuple elements (%d).' % len(args[0]))
  43. else:
  44. self._from_sequence(args[0])
  45. else:
  46. raise TypeError('Incorrect type of argument: %s' % type(args[0]))
  47. elif len(args) == 4:
  48. # Individual parameters passed in.
  49. # Thanks to ww for the help
  50. self._from_sequence([float(a) for a in args])
  51. else:
  52. raise GDALException('Incorrect number (%d) of arguments.' % len(args))
  53. # Checking the x,y coordinates
  54. if self.min_x > self.max_x:
  55. raise GDALException('Envelope minimum X > maximum X.')
  56. if self.min_y > self.max_y:
  57. raise GDALException('Envelope minimum Y > maximum Y.')
  58. def __eq__(self, other):
  59. """
  60. Return True if the envelopes are equivalent; can compare against
  61. other Envelopes and 4-tuples.
  62. """
  63. if isinstance(other, Envelope):
  64. return (self.min_x == other.min_x) and (self.min_y == other.min_y) and \
  65. (self.max_x == other.max_x) and (self.max_y == other.max_y)
  66. elif isinstance(other, tuple) and len(other) == 4:
  67. return (self.min_x == other[0]) and (self.min_y == other[1]) and \
  68. (self.max_x == other[2]) and (self.max_y == other[3])
  69. else:
  70. raise GDALException('Equivalence testing only works with other Envelopes.')
  71. def __str__(self):
  72. "Return a string representation of the tuple."
  73. return str(self.tuple)
  74. def _from_sequence(self, seq):
  75. "Initialize the C OGR Envelope structure from the given sequence."
  76. self._envelope = OGREnvelope()
  77. self._envelope.MinX = seq[0]
  78. self._envelope.MinY = seq[1]
  79. self._envelope.MaxX = seq[2]
  80. self._envelope.MaxY = seq[3]
  81. def expand_to_include(self, *args):
  82. """
  83. Modify the envelope to expand to include the boundaries of
  84. the passed-in 2-tuple (a point), 4-tuple (an extent) or
  85. envelope.
  86. """
  87. # We provide a number of different signatures for this method,
  88. # and the logic here is all about converting them into a
  89. # 4-tuple single parameter which does the actual work of
  90. # expanding the envelope.
  91. if len(args) == 1:
  92. if isinstance(args[0], Envelope):
  93. return self.expand_to_include(args[0].tuple)
  94. elif hasattr(args[0], 'x') and hasattr(args[0], 'y'):
  95. return self.expand_to_include(args[0].x, args[0].y, args[0].x, args[0].y)
  96. elif isinstance(args[0], (tuple, list)):
  97. # A tuple was passed in.
  98. if len(args[0]) == 2:
  99. return self.expand_to_include((args[0][0], args[0][1], args[0][0], args[0][1]))
  100. elif len(args[0]) == 4:
  101. (minx, miny, maxx, maxy) = args[0]
  102. if minx < self._envelope.MinX:
  103. self._envelope.MinX = minx
  104. if miny < self._envelope.MinY:
  105. self._envelope.MinY = miny
  106. if maxx > self._envelope.MaxX:
  107. self._envelope.MaxX = maxx
  108. if maxy > self._envelope.MaxY:
  109. self._envelope.MaxY = maxy
  110. else:
  111. raise GDALException('Incorrect number of tuple elements (%d).' % len(args[0]))
  112. else:
  113. raise TypeError('Incorrect type of argument: %s' % type(args[0]))
  114. elif len(args) == 2:
  115. # An x and an y parameter were passed in
  116. return self.expand_to_include((args[0], args[1], args[0], args[1]))
  117. elif len(args) == 4:
  118. # Individual parameters passed in.
  119. return self.expand_to_include(args)
  120. else:
  121. raise GDALException('Incorrect number (%d) of arguments.' % len(args[0]))
  122. @property
  123. def min_x(self):
  124. "Return the value of the minimum X coordinate."
  125. return self._envelope.MinX
  126. @property
  127. def min_y(self):
  128. "Return the value of the minimum Y coordinate."
  129. return self._envelope.MinY
  130. @property
  131. def max_x(self):
  132. "Return the value of the maximum X coordinate."
  133. return self._envelope.MaxX
  134. @property
  135. def max_y(self):
  136. "Return the value of the maximum Y coordinate."
  137. return self._envelope.MaxY
  138. @property
  139. def ur(self):
  140. "Return the upper-right coordinate."
  141. return (self.max_x, self.max_y)
  142. @property
  143. def ll(self):
  144. "Return the lower-left coordinate."
  145. return (self.min_x, self.min_y)
  146. @property
  147. def tuple(self):
  148. "Return a tuple representing the envelope."
  149. return (self.min_x, self.min_y, self.max_x, self.max_y)
  150. @property
  151. def wkt(self):
  152. "Return WKT representing a Polygon for this envelope."
  153. # TODO: Fix significant figures.
  154. return 'POLYGON((%s %s,%s %s,%s %s,%s %s,%s %s))' % \
  155. (self.min_x, self.min_y, self.min_x, self.max_y,
  156. self.max_x, self.max_y, self.max_x, self.min_y,
  157. self.min_x, self.min_y)