Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- class EnvelopeError(Exception):
- pass
- class Envelope(object):
- """
- A 2D envelope specifying the outer bounds of any spatial data
- """
- def __init__(self, x_min, y_min, x_max, y_max):
- self.x_min = x_min + 0.0
- self.y_min = y_min + 0.0
- self.x_max = x_max + 0.0
- self.y_max = y_max + 0.0
- try:
- self._assert_valid_envelope()
- except AssertionError:
- err_str = 'Invalid envelope shape'
- raise EnvelopeError(err_str)
- def __setattr__(self, attr, val):
- try:
- self._change_coordinate(attr, val)
- except KeyError:
- self.__dict__[attr] = val
- def _is_defined(self):
- # Specify the minimum attributes needed to define the
- # bounding box
- attrs = ('x_min', 'y_min', 'x_max', 'y_max')
- for attr in attrs:
- if attr not in self.__dict__:
- return False
- return True
- def _change_coordinate(self, attr, val):
- # Get the existing value in case we need to roll it back
- old_val = self.__dict__[attr]
- self.__dict__[attr] = val
- # Test this envelope
- try:
- self._assert_valid_envelope()
- except AssertionError:
- # Roll back the change and then raise the error
- self.__dict__[attr] = old_val
- err_str = 'New value not valid for envelope'
- raise EnvelopeError(err_str)
- def _assert_valid_envelope(self):
- # Minimal bounds checking
- assert self.x_min < self.x_max
- assert self.y_min < self.y_max
- class RasterEnvelope(Envelope):
- """
- A 2D envelope specifying the outer bounds and cell size of
- raster data
- """
- def __init__(self, x_min, y_max, cell_size, **kwargs):
- self.x_min = x_min + 0.0
- self.y_max = y_max + 0.0
- self.cell_size = cell_size + 0.0
- try:
- self.n_cols = kwargs['n_cols'] + 0
- self.n_rows = kwargs['n_rows'] + 0
- self.x_max = self.x_min + (self.n_cols * self.cell_size)
- self.y_min = self.y_max - (self.n_rows * self.cell_size)
- except KeyError:
- try:
- self.x_max = kwargs['x_max'] + 0.0
- self.y_min = kwargs['y_min'] + 0.0
- self.n_cols = int((self.x_max - self.x_min) / self.cell_size)
- self.n_rows = int((self.y_max - self.y_min) / self.cell_size)
- except KeyError:
- err_str = 'Either the number of rows and columns or '
- err_str += 'the lower-right coordinate was not specified'
- raise EnvelopeError(err_str)
- try:
- self._assert_valid_envelope()
- except:
- err_str = 'Invalid envelope shape'
- raise EnvelopeError(err_str)
- # Ensure that the coordinates snap to window based on
- # cell_size. Change x_max and y_min if incorrect
- self.x_max = self.x_min + (self.n_cols * self.cell_size)
- self.y_min = self.y_max - (self.n_rows * self.cell_size)
- def __setattr__(self, attr, val):
- if attr in ('x_min', 'y_min', 'x_max', 'y_max'):
- try:
- self._change_coordinate(attr, val)
- except KeyError:
- self.__dict__[attr] = val
- elif attr in ('n_cols', 'n_rows'):
- try:
- assert val + 0
- assert val > 0
- self.__dict__[attr] = int(val)
- except TypeError:
- err_str = 'Number of rows or columns must be numeric'
- raise EnvelopeError(err_str)
- except AssertionError:
- err_str = 'Number of rows or columns must be greater than 0'
- raise EnvelopeError(err_str)
- else:
- self.__dict__[attr] = val
- if self._is_defined():
- if attr in ('x_min', 'y_min', 'x_max', 'y_max'):
- self.__dict__['n_cols'] = \
- int((self.x_max - self.x_min) / self.cell_size)
- self.__dict__['n_rows'] = \
- int((self.y_max - self.y_min) / self.cell_size)
- self.__dict__['x_max'] = \
- self.x_min + (self.n_cols * self.cell_size)
- self.__dict__['y_min'] = \
- self.y_max - (self.n_rows * self.cell_size)
- if __name__ == '__main__':
- import unittest
- class EnvelopeTest(unittest.TestCase):
- def test_default(self):
- e = Envelope(0.0, 0.0, 10.0, 10.0)
- self.assertEqual(e.x_min, 0.0)
- self.assertEqual(e.y_min, 0.0)
- self.assertEqual(e.x_max, 10.0)
- self.assertEqual(e.y_max, 10.0)
- def test_incorrect_type(self):
- self.assertRaises(TypeError, Envelope, 's', 'p', 'a', 'm')
- def test_insufficient_arguments(self):
- self.assertRaises(TypeError, Envelope, 0.0, 0.0, 10.0)
- self.assertRaises(TypeError, Envelope, 0.0, 0.0, 10.0, 10.0, 10.0)
- def test_incorrect_dimensions(self):
- self.assertRaises(EnvelopeError, Envelope, 0.0, 0.0, 0.0, 0.0)
- self.assertRaises(EnvelopeError, Envelope, 10.0, 10.0, 0.0, 0.0)
- def test_change_window_errors(self):
- e = Envelope(0.0, 0.0, 10.0, 10.0)
- self.assertRaises(EnvelopeError, setattr, e, 'x_min', 20.0)
- self.assertRaises(EnvelopeError, setattr, e, 'x_max', -10.0)
- self.assertRaises(EnvelopeError, setattr, e, 'y_min', 20.0)
- self.assertRaises(EnvelopeError, setattr, e, 'y_max', -10.0)
- def test_additional_attributes(self):
- e = Envelope(0.0, 0.0, 10.0, 10.0)
- e.bob = 'silly_test'
- self.assertEqual(e.x_min, 0.0)
- e.bob = 'silly_test_2'
- self.assertEqual(e.x_min, 0.0)
- self.assertRaises(EnvelopeError, setattr, e, 'x_min', 20.0)
- class RasterEnvelopeTest(unittest.TestCase):
- def test_default(self):
- re = RasterEnvelope(0.0, 10.0, 1.0, x_max=10.0, y_min=0.0)
- self.assertEqual(re.x_min, 0.0)
- self.assertEqual(re.y_min, 0.0)
- self.assertEqual(re.x_max, 10.0)
- self.assertEqual(re.y_max, 10.0)
- self.assertEqual(re.cell_size, 1.0)
- re = RasterEnvelope(0.0, 10.0, 1.0, n_cols=10, n_rows=10)
- self.assertEqual(re.x_min, 0.0)
- self.assertEqual(re.y_min, 0.0)
- self.assertEqual(re.x_max, 10.0)
- self.assertEqual(re.y_max, 10.0)
- self.assertEqual(re.cell_size, 1.0)
- def test_insufficient_arguments(self):
- self.assertRaises(EnvelopeError, RasterEnvelope, 0.0, 10.0, 1.0)
- self.assertRaises(EnvelopeError, RasterEnvelope, 0.0, 10.0, 1.0,
- x_max=10.0, n_rows=10)
- def test_incorrect_dimensions(self):
- self.assertRaises(EnvelopeError, RasterEnvelope, 0.0, 10.0, 1.0,
- x_max=-10.0, y_min=20.0)
- self.assertRaises(EnvelopeError, RasterEnvelope, 0.0, 10.0, 1.0,
- n_cols=0, n_rows=0)
- def test_change_window(self):
- re = RasterEnvelope(0.0, 10.0, 1.0, x_max=10.0, y_min=0.0)
- self.assertRaises(EnvelopeError, setattr, re, 'x_min', 20.0)
- self.assertEqual(re.x_min, 0.0)
- self.assertRaises(EnvelopeError, setattr, re, 'n_cols', -1)
- self.assertEqual(re.n_cols, 10)
- re.x_max = 8.0
- self.assertEquals(re.n_cols, 8)
- re.n_cols = 9
- self.assertEquals(re.x_max, 9.0)
- def test_snapping(self):
- # Note that x_min, y_max stay constant for this class which
- # is probably a limitation of its usefulness ...
- #
- # For now, check that changing a dimension less than the
- # cell size resnaps it to the original
- re = RasterEnvelope(0.0, 10.0, 1.0, x_max=10.0, y_min=0.0)
- re.x_max = 10.1
- self.assertEqual(re.x_max, 10.0)
- re.n_cols = 10.1
- self.assertEqual(re.n_cols, 10)
- def test_nonsensical_changes(self):
- re = RasterEnvelope(0.0, 10.0, 1.0, x_max=10.0, y_min=0.0)
- self.assertRaises(EnvelopeError, setattr, re, 'x_min', 'spam')
- self.assertRaises(EnvelopeError, setattr, re, 'n_cols', 'spam')
- unittest.main()
Advertisement
Add Comment
Please, Sign In to add comment