2536: Improve PlaceUtils.py a little bit

svn: r11621
This commit is contained in:
Benny Malengier 2009-01-13 23:49:32 +00:00
parent c1ef989533
commit 5d9f04b32a

View File

@ -1,9 +1,10 @@
# -*- python -*-
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham
# 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
@ -22,7 +23,6 @@
# $Id:PlaceUtils.py 9912 2008-01-22 09:17:46Z acraphae $
# Written by Benny Malengier
#-------------------------------------------------------------------------
#
@ -31,6 +31,12 @@
#-------------------------------------------------------------------------
from gettext import gettext as _
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
#
@ -66,6 +72,193 @@ if 'N' == South or 'S' == North or 'E' == West or 'W' == East:
translate_en_loc['W'] = 'W'
# end localisation part
#------------------
#
# helper functions
#
#------------------
def __convert_structure_to_float(sign, degs, mins=0, secs=0.0) :
"""helper function which converts a structure to a nice
representation
"""
v = float(degs)
if mins is not None:
v += float(mins) / 60.
if secs is not None:
v += secs / 3600.
if sign == "-":
v = v * -1.
return v
def __convert_using_float_repr(stringValue):
""" helper function that tries to convert the string using the float
representation
"""
try :
v = float(stringValue)
return v
except ValueError :
return None;
def __convert_using_colon_repr(stringValue):
""" helper function that tries to convert the string using the colon
representation
"""
if stringValue.find(r':') == -1 :
return None
l = stringValue.split(':')
if len(l) < 2 or len(l) > 3:
return None
l[0]=l[0].strip()
# if no characters before ':' nothing useful is input!
if len(l[0]) == 0:
return None
if l[0][0] == '-':
sign = '-'
l[0]=l[0][1:]
else:
sign = '+'
try:
degs = int(l[0])
if degs < 0:
return None
except:
return None
try:
mins = int(l[1])
if mins < 0 or mins >= 60:
return None
except:
return None
secs=0.
if len(l) == 3:
try:
secs = float(l[2])
if secs < 0. or secs >= 60.:
return None
except:
return None
return __convert_structure_to_float(sign, degs, mins, secs)
def __convert_using_classic_repr(stringValue, typedeg):
"""helper function that tries to convert the string using the colon
representation
"""
if stringValue.find(r'_') != -1:
return None # not a valid lat or lon
#exchange some characters
stringValue = stringValue.replace(u'°',r'_')
#allow to input ° as #
stringValue = stringValue.replace(r'#',r'_')
#allow to input " as ''
stringValue = stringValue.replace(r"''",r'"')
#allow some special unicode symbols
stringValue = stringValue.replace(u'\u2033',r'"')
stringValue = stringValue.replace(u'\u2032',r"'")
#ignore spaces, a regex with \s* would be better here...
stringValue = stringValue.replace(r' ', r'')
stringValue = stringValue.replace(r'\t', r'')
# get the degrees, must be present
if stringValue.find(r'_') == -1:
return None
l = stringValue.split(r'_')
if len(l) != 2:
return None
try:
degs = int(l[0]) #degrees must be integer value
if degs < 0:
return None
except:
return None
# next: minutes might be present once
l2 = l[1].split(r"'")
l3 = l2
mins = 0
if len(l2) > 2:
return None
if len(l2) == 2:
l3 = [l2[1],]
try:
mins = int(l2[0]) #minutes must be integer value
if mins < 0 or mins >= 60:
return None
except:
return None
# next: seconds might be present once
l3 = l3[0].split(r'"')
last = l3[0]
secs = 0.
if len(l3) > 2:
return None
if len(l3) == 2:
last = l3[1]
try:
secs = float(l3[0])
if secs < 0. or secs >= 60.:
return None
except:
return None
# last entry should be the direction
if typedeg == 'lat':
if last == 'N':
sign = '+'
elif last == 'S':
sign = '-'
else:
return None
elif typedeg == 'lon':
if last == 'E':
sign = '+'
elif last == 'W':
sign = '-'
else:
return None
else:
return None
return __convert_structure_to_float(sign, degs, mins, secs)
def __convert_float_val(val, typedeg = "lat"):
# function converting input to float, recognizing decimal input, or
# degree notation input. Only english input
# There is no check on maximum/minimum of degree
# In case of degree minutes seconds direction input,
# it is checked that degree >0, 0<= minutes <= 60,
# 0<= seconds <= 60, direction is in the directions dic.
#change , to . so that , input works in non , localization
#this is no problem, as a number like 100,000.20 cannot appear in
#lat/lon
#change XX,YY into XX.YY
if val.find(r'.') == -1 :
val = val.replace(u',', u'.')
# format: XX.YYYY
v = __convert_using_float_repr(val)
if v is not None:
return v
# format: XX:YY:ZZ
v = __convert_using_colon_repr(val)
if v is not None :
return v
# format: XX° YY' ZZ" [NSWE]
v = __convert_using_classic_repr(val, typedeg)
if v is not None :
return v
# no format succeeded
return None
#-------------------------------------------------------------------------
#
# conversion function
@ -97,175 +290,6 @@ def conv_lat_lon(latitude, longitude, format="D.D4"):
and -180 180th meridian
"""
#-------------------------------------------------------------------------
# begin internal function converting one input to float
#-------------------------------------------------------------------------
def convert_float_val(val, typedeg = "lat"):
# function converting input to float, recognizing decimal input, or
# degree notation input. Only english input
# There is no check on maximum/minimum of degree
# In case of degree minutes seconds direction input,
# it is checked that degree >0, 0<= minutes <= 60,
# 0<= seconds <= 60, direction is in the directions dic.
v = None
sign = None
secs = None
mins = None
degs = None
error = False
#change , to . so that , input works in non , localization
#this is no problem, as a number like 100,000.20 cannot appear in
#lat/lon
try :
test = float('10,11')
except ValueError :
#change 10,11 into 10.11
#if point is already present in val, we can do nothing
if val.find(r'.') == -1 :
val = val.replace( r',',r'.')
try :
test = float('10.11')
except ValueError :
#change 10.11 into 10,11
#if comma is already present in val, we can do nothing
if val.find(r',') == -1 :
val = val.replace( r'.',r',')
try:
v = float(val) #decimal notation, now float
except ValueError:
# look for : notation
if val.find(r':') != -1 :
l = val.split(':')
if len(l) < 2 or len(l) > 3:
error = True
l[0]=l[0].strip()
# if no characters before ':' nothing useful is input!
if len(l[0]) == 0:
return None
if l[0][0] == '-':
sign = '-'
l[0]=l[0][1:]
else:
sign = '+'
try:
degs = int(l[0])
if degs < 0:
error = True
except:
error = True
try:
mins = int(l[1])
if mins < 0 or mins >= 60:
error = True
except:
error = True
secs=0.
if len(l) == 3:
try:
secs = float(l[2])
if secs < 0. or secs >= 60.:
error = True
except:
error = True
# if nothing found yet, look for classical notation
if val.find(r'_') != -1:
error = True # not a valid lat or lon
val = val.replace( r'°',r'_')
#allow to input ° as #
val = val.replace( r'#',r'_')
#allow to input " as ''
val = val.replace( r"''",r'"')
#allow some special unicode symbols
val = val.replace( u'\u2033',r'"')
val = val.replace( u'\u2032',r"'")
#ignore spaces, a regex with \s* would be better here...
val = val.replace(r' ', r'')
val = val.replace(r'\t', r'')
# get the degrees, must be present to parse as old degree notation
if val.find(r'_') != -1:
l = val.split('_')
if len(l) != 2:
error = True
else:
try:
degs = int(l[0]) #degrees must be integer value
if degs < 0:
error = True
except:
error = True
# next: minutes might be present once
l2 = l[1].split(r"'")
l3 = l2
mins = 0
if len(l2) > 2:
error = True
if len(l2) == 2:
l3 = [l2[1],]
try:
mins = int(l2[0]) #minutes must be integer value
if mins < 0 or mins >= 60:
error = True
except:
error = True
# next: seconds might be present once
l3 = l3[0].split(r'"')
last = l3[0]
secs = 0.
if len(l3) > 2:
error = True
if len(l3) == 2:
last = l3[1]
try:
secs = float(l3[0])
if secs < 0. or secs >= 60.:
error = True
except:
error = True
# last entry should be the direction
last = last.strip() #remove leading/trailing spaces
if typedeg == 'lat':
if last == 'N':
sign = '+'
elif last == 'S':
sign = '-'
else:
error = True
if typedeg == 'lon':
if last == 'E':
sign = '+'
elif last == 'W':
sign = '-'
else:
error = True
# degs should have a value now
if degs is None:
error = True
if error:
return None
if v is not None:
return v
#we have a degree notation, convert to float
v = float(degs)
if secs is not None:
v += secs / 3600.
if mins is not None:
v += float(mins) / 60.
if sign =="-":
v = v * -1.
return v
#-------------------------------------------------------------------------
# end internal function converting one input to float
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
# begin convert function
#-------------------------------------------------------------------------
# we start the function changing latitude/longitude in english
if latitude.find('N') == -1 and latitude.find('S') == -1:
# entry is not in english, convert to english
@ -277,8 +301,8 @@ def conv_lat_lon(latitude, longitude, format="D.D4"):
longitude = longitude.replace(translate_en_loc['E'],'E')
# convert to float
lat_float = convert_float_val(latitude, 'lat')
lon_float = convert_float_val(longitude, 'lon')
lat_float = __convert_float_val(latitude, 'lat')
lon_float = __convert_float_val(longitude, 'lon')
# give output (localized if needed)
if lat_float is None or lon_float is None:
@ -365,7 +389,7 @@ def conv_lat_lon(latitude, longitude, format="D.D4"):
else:
str_lon = ("%d°%02d'%05.2f\"" % (deg_lon, min_lon+1, 0.)) \
+ dir_lon
return (str_lat, str_lon)
if format == "DEG-:":
@ -441,9 +465,6 @@ def conv_lat_lon(latitude, longitude, format="D.D4"):
str_lon = sign_lon + \
("%03d%02d%06.3f" % (deg_lon, min_lon+1, 0.))
return str_lat + str_lon
#-------------------------------------------------------------------------
# end convert function
#-------------------------------------------------------------------------
@ -486,7 +507,7 @@ if __name__ == '__main__':
lat, lon = '50.849888888888', '2.885897222222'
test_formats_success(lat,lon)
lat, lon = ' 50°50\'59.60"N', ' 2°53\'9.23"E'
lat, lon = u' 50°50\'59.60"N', u' 2°53\'9.23"E'
test_formats_success(lat,lon)
lat, lon = ' 50 : 50 : 59.60 ', ' -2:53 : 9.23 '
test_formats_success(lat,lon)
@ -494,24 +515,24 @@ if __name__ == '__main__':
test_formats_fail(lat,lon)
lat, lon = ' 50:50: 59.60', ' d u m my'
test_formats_fail(lat,lon)
lat, lon = ' 50°59.60"N', ' 2°53\'E'
lat, lon = u' 50°59.60"N', u' 2°53\'E'
test_formats_success(lat,lon)
lat, lon = ' 11° 11\' 11" N, 11° 11\' 11" O', ' '
lat, lon = u' 11° 11\' 11" N, 11° 11\' 11" O', ' '
test_formats_fail(lat,lon)
# very small negative
lat, lon = '-0.00006', '-0.00006'
test_formats_success(lat,lon)
# missing direction N/S
lat, lon = ' 50°59.60"', ' 2°53\'E'
lat, lon = u' 50°59.60"', u' 2°53\'E'
test_formats_fail(lat,lon)
# wrong direction on latitude
lat, lon = ' 50°59.60"E', ' 2°53\'N'
lat, lon = u' 50°59.60"E', u' 2°53\'N'
test_formats_fail(lat,lon)
# same as above
lat, lon = ' 50°59.99"E', ' 2°59\'59.99"N'
lat, lon = u' 50°59.99"E', u' 2°59\'59.99"N'
test_formats_fail(lat,lon)
# test precision
lat, lon = ' 50°59.99"S', ' 2°59\'59.99"E'
lat, lon = u' 50°59.99"S', u' 2°59\'59.99"E'
test_formats_success(lat,lon)
# to large value of lat
lat, lon = '90.849888888888', '2.885897222222'
@ -520,55 +541,55 @@ if __name__ == '__main__':
lat, lon = '90', '-180'
test_formats_success(lat,lon)
# extreme values allowed
lat, lon = '90° 00\' 00.00" S ', '179° 59\'59.99"W'
lat, lon = u'90° 00\' 00.00" S ', u'179° 59\'59.99"W'
test_formats_success(lat,lon)
# extreme value not allowed
lat, lon = '90° 00\' 00.00" N', '180° 00\'00.00" E'
lat, lon = u'90° 00\' 00.00" N', u'180° 00\'00.00" E'
test_formats_fail(lat,lon)
# extreme values allowed
lat, lon = '90: 00: 00.00 ', '-179: 59:59.99'
test_formats_success(lat,lon)
# extreme value not allowed
lat, lon = '90° 00\' 00.00" N', '180:00:00.00'
lat, lon = u'90° 00\' 00.00" N', '180:00:00.00'
test_formats_fail(lat,lon)
# extreme values not allowed
lat, lon = '90', '180'
test_formats_fail(lat,lon)
lat, lon = ' 89°59\'60"N', ' 2°53\'W'
lat, lon = u' 89°59\'60"N', u' 2°53\'W'
test_formats_fail(lat,lon)
lat, lon = ' 89°60\'00"N', ' 2°53\'W'
lat, lon = u' 89°60\'00"N', u' 2°53\'W'
test_formats_fail(lat,lon)
lat, lon = ' 89.1°40\'00"N', ' 2°53\'W'
lat, lon = u' 89.1°40\'00"N', u' 2°53\'W'
test_formats_fail(lat,lon)
lat, lon = ' 89°40\'00"N', ' 2°53.1\'W'
lat, lon = u' 89°40\'00"N', u' 2°53.1\'W'
test_formats_fail(lat,lon)
lat, lon = '0', '0'
test_formats_success(lat,lon,
"Special 0 value, crossing 0-meridian and equator")
# small values close to equator
lat, lon = ' 1°1"N', ' 1°1\'E'
lat, lon = u' 1°1"N', u' 1°1\'E'
test_formats_success(lat,lon)
# roundoff
lat, lon = ' 1°59.999"N', ' 1°59.999"E'
lat, lon = u' 1°59.999"N', u' 1°59.999"E'
test_formats_success(lat,lon,'Examples of round off and how it behaves')
lat, lon = ' 1°59\'59.9999"N', ' 1°59\'59.9999"E'
lat, lon = u' 1°59\'59.9999"N', u' 1°59\'59.9999"E'
test_formats_success(lat,lon,'Examples of round off and how it behaves')
lat, lon = '89°59\'59.9999"S', '179°59\'59.9999"W'
lat, lon = u'89°59\'59.9999"S', u'179°59\'59.9999"W'
test_formats_success(lat,lon,'Examples of round off and how it behaves')
lat, lon = '89°59\'59.9999"N', '179°59\'59.9999"E'
lat, lon = u'89°59\'59.9999"N', u'179°59\'59.9999"E'
test_formats_success(lat,lon,'Examples of round off and how it behaves')
#insane number of decimals:
lat, lon = '89°59\'59.99999999"N', '179°59\'59.99999999"E'
lat, lon = u'89°59\'59.99999999"N', u'179°59\'59.99999999"E'
test_formats_success(lat,lon,'Examples of round off and how it begaves')
#recognise '' as seconds "
lat, lon = '89°59\'59.99\'\' N', '179°59\'59.99\'\'E'
lat, lon = u'89°59\'59.99\'\' N', u'179°59\'59.99\'\'E'
test_formats_success(lat,lon, "input \" as ''")
#test localisation of , and . as delimiter
lat, lon = '50.849888888888', '2,885897222222'
test_formats_success(lat,lon, 'localisation of . and , ')
lat, lon = '89°59\'59.9999"S', '179°59\'59,9999"W'
lat, lon = u'89°59\'59.9999"S', u'179°59\'59,9999"W'
test_formats_success(lat,lon, 'localisation of . and , ')
lat, lon = '89°59\'1.599,999"S', '179°59\'59,9999"W'
lat, lon = u'89°59\'1.599,999"S', u'179°59\'59,9999"W'
test_formats_fail(lat,lon, 'localisation of . and , ')
#rest
lat, lon = '81.2', '-182.3'
@ -591,8 +612,10 @@ if __name__ == '__main__':
test_formats_success(lat,lon)
lat, lon = '+50: 0 : 1 : 1', '-2:1:2'
test_formats_fail(lat,lon)
lat, lon = '+61° 43\' 60.00"', '+17° 7\' 60.00"'
lat, lon = u'+61° 43\' 60.00"', u'+17° 7\' 60.00"'
test_formats_fail(lat,lon)
lat, lon = '+61° 44\' 00.00"N', '+17° 8\' 00.00"E'
lat, lon = u'+61° 44\' 00.00"N', u'+17° 8\' 00.00"E'
test_formats_success(lat,lon)
lat, lon = ': 0 : 1 : 1', ':1:2'
test_formats_fail(lat,lon)