Repository: Turbo87/utm

Issue:
Borders of zones 31V and 32V 
In `latlon_to_zone_number` you handle the zones 31V and 32V different from all the other zones.

Usually, you compute the zone number `[left..right)` and the zone letter `[bottom..top)`. But for the corrections of 31V and 32V you use `[left..right]` and `[bottom..top]`.

This leads to the following behavior:
* for 52°N 12°E, 33U is returned. (which, in my opinion is correct)
* for 60°N 12°E, 32V is returned. Shouldn't it be 33V?
* for 64°N 5°E, 32W is returned. Shouldn't it be 31W?

I would replace line 179 by
```python
if 56 <= latitude < 64 and 3 <= longitude < 12:
```

I didn't check your code for the corrections in the X-band in detail. (I'm not interrested in places this far north...) But I believe, we have the same issue there.


Discussion:

Patch:
diff --git a/utm/conversion.py b/utm/conversion.py
index ec30333..33fbfe9 100644
--- a/utm/conversion.py
+++ b/utm/conversion.py
@@ -176,7 +176,7 @@ def latitude_to_zone_letter(latitude):
 
 
 def latlon_to_zone_number(latitude, longitude):
-    if 56 <= latitude <= 64 and 3 <= longitude <= 12:
+    if 56 <= latitude < 64 and 3 <= longitude < 12:
         return 32
 
     if 72 <= latitude <= 84 and longitude >= 0:


Test patch:
diff --git a/test/test_utm.py b/test/test_utm.py
index 1ffcab2..5cbabe8 100644
--- a/test/test_utm.py
+++ b/test/test_utm.py
@@ -173,5 +173,57 @@ class BadInput(UTMTestCase):
         self.assertRaises(
             UTM.OutOfRangeError, UTM.to_latlon, 500000, 5000000, 32, 'Z')
 
+
+class Zone32V(unittest.TestCase):
+
+    def assert_zone_equal(self, result, expected_number, expected_letter):
+        self.assertEqual(result[2], expected_number)
+        self.assertEqual(result[3].upper(), expected_letter.upper())
+
+    def test_inside(self):
+        self.assert_zone_equal(UTM.from_latlon(56, 3), 32, 'V')
+        self.assert_zone_equal(UTM.from_latlon(56, 6), 32, 'V')
+        self.assert_zone_equal(UTM.from_latlon(56, 9), 32, 'V')
+        self.assert_zone_equal(UTM.from_latlon(56, 11.999999), 32, 'V')
+
+        self.assert_zone_equal(UTM.from_latlon(60, 3), 32, 'V')
+        self.assert_zone_equal(UTM.from_latlon(60, 6), 32, 'V')
+        self.assert_zone_equal(UTM.from_latlon(60, 9), 32, 'V')
+        self.assert_zone_equal(UTM.from_latlon(60, 11.999999), 32, 'V')
+
+        self.assert_zone_equal(UTM.from_latlon(63.999999, 3), 32, 'V')
+        self.assert_zone_equal(UTM.from_latlon(63.999999, 6), 32, 'V')
+        self.assert_zone_equal(UTM.from_latlon(63.999999, 9), 32, 'V')
+        self.assert_zone_equal(UTM.from_latlon(63.999999, 11.999999), 32, 'V')
+
+    def test_left_of(self):
+        self.assert_zone_equal(UTM.from_latlon(55.999999, 2.999999), 31, 'U')
+        self.assert_zone_equal(UTM.from_latlon(56, 2.999999), 31, 'V')
+        self.assert_zone_equal(UTM.from_latlon(60, 2.999999), 31, 'V')
+        self.assert_zone_equal(UTM.from_latlon(63.999999, 2.999999), 31, 'V')
+        self.assert_zone_equal(UTM.from_latlon(64, 2.999999), 31, 'W')
+
+    def test_right_of(self):
+        self.assert_zone_equal(UTM.from_latlon(55.999999, 12), 33, 'U')
+        self.assert_zone_equal(UTM.from_latlon(56, 12), 33, 'V')
+        self.assert_zone_equal(UTM.from_latlon(60, 12), 33, 'V')
+        self.assert_zone_equal(UTM.from_latlon(63.999999, 12), 33, 'V')
+        self.assert_zone_equal(UTM.from_latlon(64, 12), 33, 'W')
+
+    def test_below(self):
+        self.assert_zone_equal(UTM.from_latlon(55.999999, 3), 31, 'U')
+        self.assert_zone_equal(UTM.from_latlon(55.999999, 6), 32, 'U')
+        self.assert_zone_equal(UTM.from_latlon(55.999999, 9), 32, 'U')
+        self.assert_zone_equal(UTM.from_latlon(55.999999, 11.999999), 32, 'U')
+        self.assert_zone_equal(UTM.from_latlon(55.999999, 12), 33, 'U')
+
+    def test_above(self):
+        self.assert_zone_equal(UTM.from_latlon(64, 3), 31, 'W')
+        self.assert_zone_equal(UTM.from_latlon(64, 6), 32, 'W')
+        self.assert_zone_equal(UTM.from_latlon(64, 9), 32, 'W')
+        self.assert_zone_equal(UTM.from_latlon(64, 11.999999), 32, 'W')
+        self.assert_zone_equal(UTM.from_latlon(64, 12), 33, 'W')
+
+
 if __name__ == '__main__':
     unittest.main()