From f1787d5a165aebf746ea5a1ce10cb5c587e98855 Mon Sep 17 00:00:00 2001 From: Jaisen Mathai Date: Sat, 19 Dec 2015 00:04:44 -0800 Subject: [PATCH] Closes gh-31 Fix decimal to degree, minute, second conversion by making seconds more precice and supporting negative values --- elodie/geolocation.py | 28 +++++++++------------------- elodie/tests/geolocation_test.py | 19 +++++++++++++------ 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/elodie/geolocation.py b/elodie/geolocation.py index 14b6ddf..6c80080 100644 --- a/elodie/geolocation.py +++ b/elodie/geolocation.py @@ -12,13 +12,11 @@ from elodie import constants class Fraction(fractions.Fraction): """Only create Fractions from floats. - >>> Fraction(0.3) Fraction(3, 10) >>> Fraction(1.1) Fraction(11, 10) """ - def __new__(cls, value, ignore=None): """Should be compatible with Python 2.6, though untested.""" return fractions.Fraction.from_float(value).limit_denominator(99999) @@ -47,28 +45,20 @@ def coordinates_by_name(name): return None def decimal_to_dms(decimal): - """Convert decimal degrees into degrees, minutes, seconds. + # if decimal is negative we need to make the degrees and minutes negative also + sign = 1 + if(decimal < 0): + sign = -1 + - >>> decimal_to_dms(50.445891) - [Fraction(50, 1), Fraction(26, 1), Fraction(113019, 2500)] - >>> decimal_to_dms(-125.976893) - [Fraction(125, 1), Fraction(58, 1), Fraction(92037, 2500)] - """ - # @TODO figure out a better and more proper way to do seconds degrees = int(decimal) subminutes = abs((decimal - int(decimal)) * 60) - minutes = int(subminutes) - subseconds = abs((subminutes - int(subminutes)) * 60) - return (pyexiv2.Rational(degrees, 1), pyexiv2.Rational(minutes, 1), pyexiv2.Rational(subseconds, 1)) + minutes = int(subminutes) * sign + subseconds = abs((subminutes - int(subminutes)) * 60) * sign + subseconds_fraction = Fraction(subseconds) + return (pyexiv2.Rational(degrees, 1), pyexiv2.Rational(minutes, 1), pyexiv2.Rational(subseconds_fraction.numerator, subseconds_fraction.denominator)) def dms_to_decimal(degrees, minutes, seconds, sign=' '): - """Convert degrees, minutes, seconds into decimal degrees. - - >>> dms_to_decimal(10, 10, 10) - 10.169444444444444 - >>> dms_to_decimal(8, 9, 10, 'S') - -8.152777777777779 - """ return (-1 if sign[0] in 'SWsw' else 1) * ( float(degrees) + float(minutes) / 60 + diff --git a/elodie/tests/geolocation_test.py b/elodie/tests/geolocation_test.py index dc4d4d5..56fb25e 100644 --- a/elodie/tests/geolocation_test.py +++ b/elodie/tests/geolocation_test.py @@ -1,8 +1,8 @@ # Project imports import os -import sys - +import random import re +import sys sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))))) @@ -12,9 +12,16 @@ from elodie import geolocation os.environ['TZ'] = 'GMT' def test_decimal_to_dms(): - target_decimal_value = 37.383336725 - dms = geolocation.decimal_to_dms(target_decimal_value) - check_value = dms[0].to_float() + dms[1].to_float() / 60 + dms[2].to_float() / 3600 + for x in range(0, 1000): + target_decimal_value = random.uniform(0.0, 180.0) + if(x % 2 == 1): + target_decimal_value = target_decimal_value * -1 + + dms = geolocation.decimal_to_dms(target_decimal_value) + check_value = dms[0].to_float() + dms[1].to_float() / 60 + dms[2].to_float() / 3600 - assert target_decimal_value == check_value, '%s does not match %s' % (check_value, target_decimal_value) + target_decimal_value = round(target_decimal_value, 8) + check_value = round(check_value, 8) + + assert target_decimal_value == check_value, '%s does not match %s' % (check_value, target_decimal_value)