123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- """
- The GDAL/OGR library uses an Envelope structure to hold the bounding
- box information for a geometry. The envelope (bounding box) contains
- two pairs of coordinates, one for the lower left coordinate and one
- for the upper right coordinate:
-
- +----------o Upper right; (max_x, max_y)
- | |
- | |
- | |
- Lower left (min_x, min_y) o----------+
- """
- from ctypes import Structure, c_double
-
- from django.contrib.gis.gdal.error import GDALException
-
-
- # The OGR definition of an Envelope is a C structure containing four doubles.
- # See the 'ogr_core.h' source file for more information:
- # https://www.gdal.org/ogr__core_8h_source.html
- class OGREnvelope(Structure):
- "Represent the OGREnvelope C Structure."
- _fields_ = [("MinX", c_double),
- ("MaxX", c_double),
- ("MinY", c_double),
- ("MaxY", c_double),
- ]
-
-
- class Envelope:
- """
- The Envelope object is a C structure that contains the minimum and
- maximum X, Y coordinates for a rectangle bounding box. The naming
- of the variables is compatible with the OGR Envelope structure.
- """
-
- def __init__(self, *args):
- """
- The initialization function may take an OGREnvelope structure, 4-element
- tuple or list, or 4 individual arguments.
- """
-
- if len(args) == 1:
- if isinstance(args[0], OGREnvelope):
- # OGREnvelope (a ctypes Structure) was passed in.
- self._envelope = args[0]
- elif isinstance(args[0], (tuple, list)):
- # A tuple was passed in.
- if len(args[0]) != 4:
- raise GDALException('Incorrect number of tuple elements (%d).' % len(args[0]))
- else:
- self._from_sequence(args[0])
- else:
- raise TypeError('Incorrect type of argument: %s' % type(args[0]))
- elif len(args) == 4:
- # Individual parameters passed in.
- # Thanks to ww for the help
- self._from_sequence([float(a) for a in args])
- else:
- raise GDALException('Incorrect number (%d) of arguments.' % len(args))
-
- # Checking the x,y coordinates
- if self.min_x > self.max_x:
- raise GDALException('Envelope minimum X > maximum X.')
- if self.min_y > self.max_y:
- raise GDALException('Envelope minimum Y > maximum Y.')
-
- def __eq__(self, other):
- """
- Return True if the envelopes are equivalent; can compare against
- other Envelopes and 4-tuples.
- """
- if isinstance(other, Envelope):
- return (self.min_x == other.min_x) and (self.min_y == other.min_y) and \
- (self.max_x == other.max_x) and (self.max_y == other.max_y)
- elif isinstance(other, tuple) and len(other) == 4:
- return (self.min_x == other[0]) and (self.min_y == other[1]) and \
- (self.max_x == other[2]) and (self.max_y == other[3])
- else:
- raise GDALException('Equivalence testing only works with other Envelopes.')
-
- def __str__(self):
- "Return a string representation of the tuple."
- return str(self.tuple)
-
- def _from_sequence(self, seq):
- "Initialize the C OGR Envelope structure from the given sequence."
- self._envelope = OGREnvelope()
- self._envelope.MinX = seq[0]
- self._envelope.MinY = seq[1]
- self._envelope.MaxX = seq[2]
- self._envelope.MaxY = seq[3]
-
- def expand_to_include(self, *args):
- """
- Modify the envelope to expand to include the boundaries of
- the passed-in 2-tuple (a point), 4-tuple (an extent) or
- envelope.
- """
- # We provide a number of different signatures for this method,
- # and the logic here is all about converting them into a
- # 4-tuple single parameter which does the actual work of
- # expanding the envelope.
- if len(args) == 1:
- if isinstance(args[0], Envelope):
- return self.expand_to_include(args[0].tuple)
- elif hasattr(args[0], 'x') and hasattr(args[0], 'y'):
- return self.expand_to_include(args[0].x, args[0].y, args[0].x, args[0].y)
- elif isinstance(args[0], (tuple, list)):
- # A tuple was passed in.
- if len(args[0]) == 2:
- return self.expand_to_include((args[0][0], args[0][1], args[0][0], args[0][1]))
- elif len(args[0]) == 4:
- (minx, miny, maxx, maxy) = args[0]
- if minx < self._envelope.MinX:
- self._envelope.MinX = minx
- if miny < self._envelope.MinY:
- self._envelope.MinY = miny
- if maxx > self._envelope.MaxX:
- self._envelope.MaxX = maxx
- if maxy > self._envelope.MaxY:
- self._envelope.MaxY = maxy
- else:
- raise GDALException('Incorrect number of tuple elements (%d).' % len(args[0]))
- else:
- raise TypeError('Incorrect type of argument: %s' % type(args[0]))
- elif len(args) == 2:
- # An x and an y parameter were passed in
- return self.expand_to_include((args[0], args[1], args[0], args[1]))
- elif len(args) == 4:
- # Individual parameters passed in.
- return self.expand_to_include(args)
- else:
- raise GDALException('Incorrect number (%d) of arguments.' % len(args[0]))
-
- @property
- def min_x(self):
- "Return the value of the minimum X coordinate."
- return self._envelope.MinX
-
- @property
- def min_y(self):
- "Return the value of the minimum Y coordinate."
- return self._envelope.MinY
-
- @property
- def max_x(self):
- "Return the value of the maximum X coordinate."
- return self._envelope.MaxX
-
- @property
- def max_y(self):
- "Return the value of the maximum Y coordinate."
- return self._envelope.MaxY
-
- @property
- def ur(self):
- "Return the upper-right coordinate."
- return (self.max_x, self.max_y)
-
- @property
- def ll(self):
- "Return the lower-left coordinate."
- return (self.min_x, self.min_y)
-
- @property
- def tuple(self):
- "Return a tuple representing the envelope."
- return (self.min_x, self.min_y, self.max_x, self.max_y)
-
- @property
- def wkt(self):
- "Return WKT representing a Polygon for this envelope."
- # TODO: Fix significant figures.
- return 'POLYGON((%s %s,%s %s,%s %s,%s %s,%s %s))' % \
- (self.min_x, self.min_y, self.min_x, self.max_y,
- self.max_x, self.max_y, self.max_x, self.min_y,
- self.min_x, self.min_y)
|