# Ticket #4797: gis_measure_0_r5634.diff

File gis_measure_0_r5634.diff, 15.9 KB (added by robert.coup@…, 16 years ago)

Initial patch against r5634

• ## django/contrib/gis/measure.py

 """ Distance and Area objects to allow for sensible and convienient calculation and conversions. Inspired by GeoPy (http://exogen.case.edu/projects/geopy/) and Geoff Biggs' PhD work on dimensioned units for robotics. Copyright (c) 2007, Robert Coup """ from math import * from decimal import Decimal class Distance(object): UNITS = { 'm': 1.0, 'km': 1000.0, 'mi': 1609.344, 'ft': 0.3048, 'yd': 0.9144, 'nm': 1852.0, } def __init__(self, default_unit=None, **kwargs): self.m = 0.0 self._default_unit = 'm' for unit,value in kwargs.items(): if unit in self.UNITS: self.m += self.UNITS[unit] * value self._default_unit = unit else: raise AttributeError("Unknown unit type: " + unit) if default_unit: self._default_unit = default_unit def __getattr__(self, name): if name in self.UNITS: return self.m / self.UNITS[name] else: raise AttributeError("Unknown unit type: " + name) def __repr__(self): return "Distance(%s=%s)" % (self._default_unit, getattr(self, self._default_unit)) def __str__(self): return "%s %s" % (getattr(self, self._default_unit), self._default_unit) def __cmp__(self, other): if isinstance(other, Distance): return cmp(self.m, other.m) else: return NotImplemented def __add__(self, other): if isinstance(other, Distance): return Distance(default_unit=self._default_unit, m=(self.m + other.m)) else: raise TypeError("Distance must be added with Distance") def __iadd__(self, other): if isinstance(other, Distance): self.m += other.m return self else: raise TypeError("Distance must be added with Distance") def __sub__(self, other): if isinstance(other, Distance): return Distance(default_unit=self._default_unit, m=(self.m - other.m)) else: raise TypeError("Distance must be subtracted from Distance") def __isub__(self, other): if isinstance(other, Distance): self.m -= other.m return self else: raise TypeError("Distance must be subtracted from Distance") def __mul__(self, other): if isinstance(other, (int, float, long, Decimal)): return Distance(default_unit=self._default_unit, m=(self.m * float(other))) elif isinstance(other, Distance): return Area(default_unit='sq_' + self._default_unit, sq_m=(self.m * other.m)) else: raise TypeError("Distance must be multiplied with number or Distance") def __imul__(self, other): if isinstance(other, (int, float, long, Decimal)): self.m *= float(other) return self else: raise TypeError("Distance must be multiplied with number") def __div__(self, other): if isinstance(other, (int, float, long, Decimal)): return Distance(default_unit=self._default_unit, m=(self.m / float(other))) else: raise TypeError("Distance must be divided with number") def __idiv__(self, other): if isinstance(other, (int, float, long, Decimal)): self.m /= float(other) return self else: raise TypeError("Distance must be divided with number") def __nonzero__(self): return bool(self.m) class Area(object): UNITS = { 'sq_m': 1.0, 'sq_km': 1000000.0, 'sq_mi': 2589988.110336, 'sq_ft': 0.09290304, 'sq_yd': 0.83612736, 'sq_nm': 3429904.0, } def __init__(self, default_unit=None, **kwargs): self.sq_m = 0.0 self._default_unit = 'sq_m' for unit,value in kwargs.items(): if unit in self.UNITS: self.sq_m += self.UNITS[unit] * value self._default_unit = unit else: raise AttributeError("Unknown unit type: " + unit) if default_unit: self._default_unit = default_unit def __getattr__(self, name): if name in self.UNITS: return self.sq_m / self.UNITS[name] else: raise AttributeError("Unknown unit type: " + name) def __repr__(self): return "Area(%s=%s)" % (self._default_unit, getattr(self, self._default_unit)) def __str__(self): return "%s %s" % (getattr(self, self._default_unit), self._default_unit) def __cmp__(self, other): if isinstance(other, Area): return cmp(self.sq_m, other.sq_m) else: return NotImplemented def __add__(self, other): if isinstance(other, Area): return Area(default_unit=self._default_unit, sq_m=(self.sq_m + other.sq_m)) else: raise TypeError("Area must be added with Area") def __iadd__(self, other): if isinstance(other, Area): self.sq_m += other.sq_m return self else: raise TypeError("Area must be added with Area") def __sub__(self, other): if isinstance(other, Area): return Area(default_unit=self._default_unit, sq_m=(self.sq_m - other.sq_m)) else: raise TypeError("Area must be subtracted from Area") def __isub__(self, other): if isinstance(other, Area): self.sq_m -= other.sq_m return self else: raise TypeError("Area must be subtracted from Area") def __mul__(self, other): if isinstance(other, (int, float, long, Decimal)): return Area(default_unit=self._default_unit, sq_m=(self.sq_m * float(other))) else: raise TypeError("Area must be multiplied with number") def __imul__(self, other): if isinstance(other, (int, float, long, Decimal)): self.sq_m *= float(other) return self else: raise TypeError("Area must be multiplied with number") def __div__(self, other): if isinstance(other, (int, float, long, Decimal)): return Area(default_unit=self._default_unit, sq_m=(self.sq_m / float(other))) else: raise TypeError("Area must be divided with number") def __idiv__(self, other): if isinstance(other, (int, float, long, Decimal)): self.sq_m /= float(other) return self else: raise TypeError("Area must be divided with number") def __nonzero__(self): return bool(self.sq_m) # Shortcuts D = Distance A = Area
• ## django/contrib/gis/tests/test_measure.py

 """ Distance and Area objects to allow for sensible and convienient calculation and conversions. Here are some tests. """ import unittest from django.contrib.gis.measure import Distance, Area, D, A class DistanceTest(unittest.TestCase): "Testing the Distance object" def testInit(self): "Testing initialisation from valid units" d = Distance(m=100) self.assertEqual(d.m, 100) d = D(m=100) self.assertEqual(d.m, 100) d = D(nm=100) self.assertEqual(d.m, 185200) def testInitInvalid(self): "Testing initialisation from invalid units" self.assertRaises(AttributeError, D, banana=100) def testAccess(self): "Testing access in different units" d = D(m=100) self.assertEqual(d.km, 0.1) self.assertAlmostEqual(d.ft, 328.084, 3) def testAccessInvalid(self): "Testing access in invalid units" d = D(m=100) self.failIf(hasattr(d, 'banana')) def testAddition(self): "Test addition & subtraction" d1 = D(m=100) d2 = D(m=200) d3 = d1 + d2 self.assertEqual(d3.m, 300) d3 += d1 self.assertEqual(d3.m, 400) d4 = d1 - d2 self.assertEqual(d4.m, -100) d4 -= d1 self.assertEqual(d4.m, -200) try: d5 = d1 + 1 except TypeError, e: pass else: self.fail('Distance + number should raise TypeError') try: d5 = d1 - 1 except TypeError, e: pass else: self.fail('Distance - number should raise TypeError') try: d1 += 1 except TypeError, e: pass else: self.fail('Distance += number should raise TypeError') try: d1 -= 1 except TypeError, e: pass else: self.fail('Distance -= number should raise TypeError') def testMultiplication(self): "Test multiplication & division" d1 = D(m=100) d3 = d1 * 2 self.assertEqual(d3.m, 200) d3 *= 5 self.assertEqual(d3.m, 1000) d4 = d1 / 2 self.assertEqual(d4.m, 50) d4 /= 5 self.assertEqual(d4.m, 10) a5 = d1 * D(m=10) self.assert_(isinstance(a5, Area)) self.assertEqual(a5.sq_m, 100*10) try: d1 *= D(m=1) except TypeError, e: pass else: self.fail('Distance *= Distance should raise TypeError') try: d5 = d1 / D(m=1) except TypeError, e: pass else: self.fail('Distance / Distance should raise TypeError') try: d1 /= D(m=1) except TypeError, e: pass else: self.fail('Distance /= Distance should raise TypeError') def testUnitConversions(self): "Testing default units during maths" d1 = D(m=100) d2 = D(km=1) d3 = d1 + d2 self.assertEqual(d3._default_unit, 'm') d4 = d2 + d1 self.assertEqual(d4._default_unit, 'km') d5 = d1 * 2 self.assertEqual(d5._default_unit, 'm') d6 = d1 / 2 self.assertEqual(d6._default_unit, 'm') def testComparisons(self): "Testing comparisons" d1 = D(m=100) d2 = D(km=1) d3 = D(km=0) self.assert_(d2 > d1) self.assert_(d1 == d1) self.assert_(d1 < d2) self.failIf(d3) def testUnitsStr(self): "Testing conversion to strings" d1 = D(m=100) d2 = D(km=3.5) self.assertEqual(str(d1), '100.0 m') self.assertEqual(str(d2), '3.5 km') self.assertEqual(repr(d1), 'Distance(m=100.0)') self.assertEqual(repr(d2), 'Distance(km=3.5)') class AreaTest(unittest.TestCase): "Testing the Area object" def testInit(self): "Testing initialisation from valid units" a = Area(sq_m=100) self.assertEqual(a.sq_m, 100) a = A(sq_m=100) self.assertEqual(a.sq_m, 100) a = A(sq_mi=100) self.assertEqual(a.sq_m, 258998811.0336) def testInitInvaliA(self): "Testing initialisation from invalid units" self.assertRaises(AttributeError, A, banana=100) def testAccess(self): "Testing access in different units" a = A(sq_m=100) self.assertEqual(a.sq_km, 0.0001) self.assertAlmostEqual(a.sq_ft, 1076.391, 3) def testAccessInvaliA(self): "Testing access in invalid units" a = A(sq_m=100) self.failIf(hasattr(a, 'banana')) def testAddition(self): "Test addition & subtraction" a1 = A(sq_m=100) a2 = A(sq_m=200) a3 = a1 + a2 self.assertEqual(a3.sq_m, 300) a3 += a1 self.assertEqual(a3.sq_m, 400) a4 = a1 - a2 self.assertEqual(a4.sq_m, -100) a4 -= a1 self.assertEqual(a4.sq_m, -200) try: a5 = a1 + 1 except TypeError, e: pass else: self.fail('Area + number should raise TypeError') try: a5 = a1 - 1 except TypeError, e: pass else: self.fail('Area - number should raise TypeError') try: a1 += 1 except TypeError, e: pass else: self.fail('Area += number should raise TypeError') try: a1 -= 1 except TypeError, e: pass else: self.fail('Area -= number should raise TypeError') def testMultiplication(self): "Test multiplication & division" a1 = A(sq_m=100) a3 = a1 * 2 self.assertEqual(a3.sq_m, 200) a3 *= 5 self.assertEqual(a3.sq_m, 1000) a4 = a1 / 2 self.assertEqual(a4.sq_m, 50) a4 /= 5 self.assertEqual(a4.sq_m, 10) try: a5 = a1 * A(sq_m=1) except TypeError, e: pass else: self.fail('Area * Area should raise TypeError') try: a1 *= A(sq_m=1) except TypeError, e: pass else: self.fail('Area *= Area should raise TypeError') try: a5 = a1 / A(sq_m=1) except TypeError, e: pass else: self.fail('Area / Area should raise TypeError') try: a1 /= A(sq_m=1) except TypeError, e: pass else: self.fail('Area /= Area should raise TypeError') def testUnitConversions(self): "Testing default units during maths" a1 = A(sq_m=100) a2 = A(sq_km=1) a3 = a1 + a2 self.assertEqual(a3._default_unit, 'sq_m') a4 = a2 + a1 self.assertEqual(a4._default_unit, 'sq_km') a5 = a1 * 2 self.assertEqual(a5._default_unit, 'sq_m') a6 = a1 / 2 self.assertEqual(a6._default_unit, 'sq_m') def testComparisons(self): "Testing comparisons" a1 = A(sq_m=100) a2 = A(sq_km=1) a3 = A(sq_km=0) self.assert_(a2 > a1) self.assert_(a1 == a1) self.assert_(a1 < a2) self.failIf(a3) def testUnitsStr(self): "Testing conversion to strings" a1 = A(sq_m=100) a2 = A(sq_km=3.5) self.assertEqual(str(a1), '100.0 sq_m') self.assertEqual(str(a2), '3.5 sq_km') self.assertEqual(repr(a1), 'Area(sq_m=100.0)') self.assertEqual(repr(a2), 'Area(sq_km=3.5)') def suite(): s = unittest.TestSuite() s.addTest(unittest.makeSuite(DistanceTest)) s.addTest(unittest.makeSuite(AreaTest)) return s def run(verbosity=2): unittest.TextTestRunner(verbosity=verbosity).run(suite()) if __name__=="__main__": run() No newline at end of file