Add unit test for latitide/longitude conversion

svn: r22792
This commit is contained in:
Nick Hall 2013-08-01 20:46:11 +00:00
parent 9a9d7ccf03
commit d174f4c44b

View File

@ -0,0 +1,297 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2007-2009 B. Malengier
# Copyright (C) 2009 Swoon on bug tracker
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
from __future__ import unicode_literals
#-------------------------------------------------------------------------
#
# Standard python modules
#
#-------------------------------------------------------------------------
import unittest
import math
#-------------------------------------------------------------------------
#
# Gramps modules
#
#-------------------------------------------------------------------------
from gramps.gen.utils.place import conv_lat_lon
#-------------------------------------------------------------------------
#
# PlaceTest class
#
#-------------------------------------------------------------------------
class PlaceTest(unittest.TestCase):
def _test_formats_success(self, lat, lon):
for format_ in ["D.D4", "D.D8", "DEG", "DEG-:", "RT90", "GEDCOM"]:
res1, res2 = conv_lat_lon(lat, lon, format_)
self.assertIsNotNone(res1, 'Failed for %s %s' % (lat, lon))
self.assertIsNotNone(res2, 'Failed for %s %s' % (lat, lon))
for format_ in ["ISO-D", "ISO-DM", "ISO-DMS"]:
res = conv_lat_lon(lat, lon, format_)
self.assertIsNotNone(res, 'Failed for %s %s' % (lat, lon))
def _test_formats_fail(self, lat, lon):
res1, res2 = conv_lat_lon(lat, lon)
self.assertIsNone(res1, 'Expected failure for %s %s' % (lat, lon))
self.assertIsNone(res2, 'Expected failure for %s %s' % (lat, lon))
def test_decimal(self):
lat, lon = '50.849888888888', '2.885897222222'
self._test_formats_success(lat, lon)
def test_dms(self):
lat, lon = ' 50°50\'59.60"N', ' 2°53\'9.23"E'
self._test_formats_success(lat, lon)
def test_colon(self):
lat, lon = ' 50 : 50 : 59.60 ', ' -2:53 : 9.23 '
self._test_formats_success(lat, lon)
lat, lon = '+50:1', '-2:1:2'
self._test_formats_success(lat, lon)
def test_bad_latitude(self):
lat, lon = ' dummy', ' 2#53 \' 9.23 " E '
self._test_formats_fail(lat, lon)
def test_bad_longitude(self):
lat, lon = ' 50:50: 59.60', ' d u m my'
self._test_formats_fail(lat, lon)
def test_dm_ds(self):
lat, lon = ' 50°59.60"N', ' 2°53\'E'
self._test_formats_success(lat, lon)
def test_both_in_latitude(self):
lat, lon = ' 11° 11\' 11" N, 11° 11\' 11" O', ' '
self._test_formats_fail(lat, lon)
def test_very_small_negative(self):
lat, lon = '-0.00006', '-0.00006'
self._test_formats_success(lat, lon)
def test_missing_direction(self):
lat, lon = ' 50°59.60"', ' 2°53\'E'
self._test_formats_fail(lat, lon)
def test_wrong_direction(self):
lat, lon = ' 50°59.60"E', ' 2°53\'N'
self._test_formats_fail(lat, lon)
lat, lon = ' 50°59.99"E', ' 2°59\'59.99"N'
self._test_formats_fail(lat, lon)
def test_precision(self):
lat, lon = ' 50°59.99"S', ' 2°59\'59.99"E'
self._test_formats_success(lat, lon)
lat, lon = 'N50.849888888888', 'E2.885897222222'
self._test_formats_success(lat, lon)
def test_large_latitude(self):
lat, lon = '90.849888888888', '2.885897222222'
self._test_formats_fail(lat, lon)
lat, lon = '-91.2', '-1'
self._test_formats_fail(lat, lon)
def test_extreme_values(self):
lat, lon = '90', '-180'
self._test_formats_success(lat, lon)
lat, lon = '90° 00\' 00.00" S ', '179° 59\'59.99"W'
self._test_formats_success(lat, lon)
lat, lon = '90° 00\' 00.00" N', '180° 00\'00.00" E'
self._test_formats_fail(lat, lon)
lat, lon = '90: 00: 00.00 ', '-179: 59:59.99'
self._test_formats_success(lat, lon)
lat, lon = '90° 00\' 00.00" N', '180:00:00.00'
self._test_formats_fail(lat, lon)
lat, lon = '90', '180'
self._test_formats_fail(lat, lon)
lat, lon = ' 89°59\'60"N', ' 2°53\'W'
self._test_formats_fail(lat, lon)
lat, lon = ' 89°60\'00"N', ' 2°53\'W'
self._test_formats_fail(lat, lon)
lat, lon = ' 89.1°40\'00"N', ' 2°53\'W'
self._test_formats_fail(lat, lon)
lat, lon = ' 89°40\'00"N', ' 2°53.1\'W'
self._test_formats_fail(lat, lon)
lat, lon = '+61° 43\' 60.00"', '+17° 7\' 60.00"'
self._test_formats_fail(lat, lon)
def test_zero_crossing(self):
lat, lon = '0', '0'
self._test_formats_success(lat, lon)
def test_near_equator(self):
lat, lon = ' 1°1"N', ' 1°1\'E'
self._test_formats_success(lat, lon)
def test_roundoff(self):
lat, lon = ' 1°59.999"N', ' 1°59.999"E'
self._test_formats_success(lat, lon)
lat, lon = ' 1°59\'59.9999"N', ' 1°59\'59.9999"E'
self._test_formats_success(lat, lon)
lat, lon = '89°59\'59.9999"S', '179°59\'59.9999"W'
self._test_formats_success(lat, lon)
lat, lon = '89°59\'59.9999"N', '179°59\'59.9999"E'
self._test_formats_success(lat, lon)
def test_number_of_decimals(self):
lat, lon = '89°59\'59.99999999"N', '179°59\'59.99999999"E'
self._test_formats_success(lat, lon)
def test_quote_notation(self):
lat, lon = '89°59\'59.99\'\' N', '179°59\'59.99\'\'E'
self._test_formats_success(lat, lon)
def test_decimal_localization(self):
lat, lon = '50.849888888888', '2,885897222222'
self._test_formats_success(lat, lon)
lat, lon = '89°59\'59.9999"S', '179°59\'59,9999"W'
self._test_formats_success(lat, lon)
lat, lon = '89°59\'1.599,999"S', '179°59\'59,9999"W'
self._test_formats_fail(lat, lon)
def test_large_longitude(self):
lat, lon = '81.2', '-182.3'
self._test_formats_fail(lat, lon)
def test_bad_sign(self):
lat, lon = '++50:10:1', '2:1:2'
self._test_formats_fail(lat, lon)
lat, lon = '-50:10:1', '-+2:1:2'
self._test_formats_success(lat, lon)
def test_missing_minute(self):
lat, lon = '-50::1', '-2:1:2'
self._test_formats_fail(lat, lon)
lat, lon = '+50:', '-2:1:2'
self._test_formats_fail(lat, lon)
def test_extra_whitespace(self):
lat, lon = '- 50 : 2 : 1 ', '-2:1:2'
self._test_formats_success(lat, lon)
lat, lon = '+ 50:2 : 1', '-2:1:2'
self._test_formats_success(lat, lon)
def test_sign_and_direction(self):
lat, lon = '+61° 44\' 00.00"N', '+17° 8\' 00.00"E'
self._test_formats_success(lat, lon)
def test_extra_colon(self):
lat, lon = '+50: 0 : 1 : 1', '-2:1:2'
self._test_formats_fail(lat, lon)
def test_leading_colon(self):
lat, lon = ': 0 : 1 : 1', ':1:2'
self._test_formats_fail(lat, lon)
def test_degree_symbol(self):
lat, lon = 'N 50º52\'21.92"', 'E 124º52\'21.92"'
self._test_formats_success(lat, lon)
lat, lon = 'S 50º52\'21.92"', 'W 124º52\'21.92"'
self._test_formats_success(lat, lon)
def test_RT90_conversion(self):
"""A given lat/lon is converted to RT90 and back"""
x, y = conv_lat_lon('59:40:9.09', '12:58:57.74', 'RT90')
lat, lon = conv_SWED_RT90_WGS84(float(x), float(y))
expetced_lat = 59.0 + 40.0/60. + 9.09/3600.0
expetced_lon = 12.0 + 58.0/60.0 + 57.74/3600.0
self.assertAlmostEqual(lat, expetced_lat, places=3)
self.assertAlmostEqual(lon, expetced_lon, places=3)
def conv_SWED_RT90_WGS84(X, Y):
"""
Input is X and Y coordinates in RT90 as float
Output is lat and long in degrees, float as tuple
"""
# Some constants used for conversion to/from Swedish RT90
f = 1.0/298.257222101
e2 = f*(2.0-f)
n = f/(2.0-f)
L0 = math.radians(15.8062845294) # 15 deg 48 min 22.624306 sec
k0 = 1.00000561024
a = 6378137.0 # meter
at = a/(1.0+n)*(1.0+ 1.0/4.0* pow(n,2)+1.0/64.0*pow(n,4))
FN = -667.711 # m
FE = 1500064.274 # m
xi = (X - FN)/(k0*at)
eta = (Y - FE)/(k0*at)
D1 = 1.0/2.0*n - 2.0/3.0*pow(n,2) + 37.0/96.0*pow(n,3) - 1.0/360.0*pow(n,4)
D2 = 1.0/48.0*pow(n,2) + 1.0/15.0*pow(n,3) - 437.0/1440.0*pow(n,4)
D3 = 17.0/480.0*pow(n,3) - 37.0/840.0*pow(n,4)
D4 = 4397.0/161280.0*pow(n,4)
xip = xi - D1*math.sin(2.0*xi)*math.cosh(2.0*eta) - \
D2*math.sin(4.0*xi)*math.cosh(4.0*eta) - \
D3*math.sin(6.0*xi)*math.cosh(6.0*eta) - \
D4*math.sin(8.0*xi)*math.cosh(8.0*eta)
etap = eta - D1*math.cos(2.0*xi)*math.sinh(2.0*eta) - \
D2*math.cos(4.0*xi)*math.sinh(4.0*eta) - \
D3*math.cos(6.0*xi)*math.sinh(6.0*eta) - \
D4*math.cos(8.0*xi)*math.sinh(8.0*eta)
psi = math.asin(math.sin(xip)/math.cosh(etap))
DL = math.atan2(math.sinh(etap),math.cos(xip))
LON = L0 + DL
A = e2 + pow(e2,2) + pow(e2,3) + pow(e2,4)
B = -1.0/6.0*(7.0*pow(e2,2) + 17*pow(e2,3) + 30*pow(e2,4))
C = 1.0/120.0*(224.0*pow(e2,3) + 889.0*pow(e2,4))
D = 1.0/1260.0*(4279.0*pow(e2,4))
E = A + B*pow(math.sin(psi),2) + \
C*pow(math.sin(psi),4) + \
D*pow(math.sin(psi),6)
LAT = psi + math.sin(psi)*math.cos(psi)*E
LAT = math.degrees(LAT)
LON = math.degrees(LON)
return LAT, LON