aboutsummaryrefslogtreecommitdiff
path: root/test/gigs
diff options
context:
space:
mode:
authorMicah Cochran <micahcochran@users.noreply.github.com>2016-05-25 15:12:23 -0500
committerMicah Cochran <micahcochran@users.noreply.github.com>2016-05-25 15:12:23 -0500
commit4965b57742667280a0d1b25fb92607df1527ced8 (patch)
tree51fdc48c5a08da4a1f1fb04ed66858f466671298 /test/gigs
parentc5f751692e535cb30418e5695144e4c42c4fcf94 (diff)
downloadPROJ-4965b57742667280a0d1b25fb92607df1527ced8.tar.gz
PROJ-4965b57742667280a0d1b25fb92607df1527ced8.zip
Correct all test_json.py flake8 warnings and errors with the exception of some error E501 - 'line too long errors', lines with >79 characters.
Diffstat (limited to 'test/gigs')
-rw-r--r--test/gigs/test_json.py285
1 files changed, 146 insertions, 139 deletions
diff --git a/test/gigs/test_json.py b/test/gigs/test_json.py
index 82c27817..2c1f9f02 100644
--- a/test/gigs/test_json.py
+++ b/test/gigs/test_json.py
@@ -1,7 +1,7 @@
-# This is a framework to test GIGS, Geospatial Integrity of geoscience software.
-# Which is published by International Association of Oil & Gas Producers
-# as a resources for how geospatial software should give consistent and
-# expected results.
+# This is a framework to test GIGS, Geospatial Integrity of geoscience
+# software. Which is published by International Association of Oil & Gas
+# Producers as a resources for how geospatial software should give consistent
+# and expected results.
#
# This could be expanded to be used with other testing frameworks.
#
@@ -46,7 +46,7 @@ except ImportError as e_pyproj:
PY_MAJOR = sys.version_info[0]
PY2 = (PY_MAJOR == 2)
-## Python 2/3 Compatibility code #######################################
+# Python 2/3 Compatibility code ########################################
if PY_MAJOR >= 3:
# list-producing versions of the major Python iterating functions
# lzip acts like Python 2.x zip function.
@@ -60,16 +60,17 @@ else:
from __builtin__ import zip as lzip
########################################################################
+
def list_count_matches(coords, ex_coords, tolerance):
"""
counts coordinates in lists that match and don't match.
assumes that lists are the same length (they should be)
-
+
coords - coordinates
ex_cords - expected cooridnate
tolerance - difference allowed between the coordinates
-
- returns tuple (matches, non_matches)
+
+ returns tuple (matches, non_matches)
"""
matches, non_matches = 0, 0
iter_ex_coords = iter(ex_coords)
@@ -81,45 +82,51 @@ def list_count_matches(coords, ex_coords, tolerance):
non_matches = non_matches + 1
return (matches, non_matches)
-
+
+
def match_func(cor, exc, tolerance):
"""
Check if coordinate matches expected coordinate within a given tolerance.
-
- cor - coordinate
+
+ cor - coordinate
exc - expected coordinate
- tolerance - error rate
- type float coordinates elements will be checked based on this value
- type list/tuple coordinate elements will be checked based on the corresponding values
- return boolean for match
+ tolerance - error rate
+ float coordinate elements will be checked based on this value
+ list/tuple coordinate elements will be checked based on the
+ corresponding values
+ return bool
"""
if len(exc) == 3:
# coordinate triples
- coord_diff = abs(cor[0]-exc[0]), abs(cor[1]-exc[1]), abs(cor[2]-exc[2])
+ coord_diff = abs(cor[0] - exc[0]), abs(cor[1] - exc[1]), abs(cor[2] - exc[2])
if isinstance(tolerance, float):
matching = coord_diff < (tolerance, tolerance, tolerance)
- elif isinstance(tolerance, (list, tuple)): # FIXME is the length of the list, tuple atleast 3?
+ elif isinstance(tolerance, (list, tuple)): # FIXME is list's length atleast 3?
matching = coord_diff < tuple(tolerance)
else:
# assume coordinate pairs
- coord_diff = abs(cor[0]-exc[0]), abs(cor[1]-exc[1])
+ coord_diff = abs(cor[0] - exc[0]), abs(cor[1] - exc[1])
if isinstance(tolerance, float):
matching = coord_diff < (tolerance, tolerance)
- elif isinstance(tolerance, (list, tuple)): # FIXME is the length of the list, tuple atleast 2?
+ elif isinstance(tolerance, (list, tuple)): # FIXME is list's length atleast 2?
matching = coord_diff < tuple(tolerance)
-
+
if matching is False:
- logging.info('non-match, calculated coordinate: {c1}\n expected coordinate: {c2}\n difference:{res} \n tolerance: {tol}\n'.format(
- c1=cor, c2=exc, res=coord_diff, tol=tolerance))
- return matching
+ logging.info('non-match, calculated coordinate: {c1}\n'
+ 'expected coordinate: {c2}\n difference:{res}\n'
+ 'tolerance: {tol}\n'
+ ''.format(c1=cor, c2=exc, res=coord_diff, tol=tolerance))
+
+ return matching
# parse multiple tests and call TransformTest
-# TODO: needs some awareness of the driver, so driver_info function in TranfromTest classes can be called,
-# could allow a dummy instance of Driver and move all the initization code
-# to another function? Or allow dipatch function to check if everything is in order do a transform.
-# Not elegant.
-class TransformRunner(object):
+# TODO: needs some awareness of the driver, so driver_info function in
+# TranformTest classes can be called, could allow a dummy instance of
+# Driver and move all the initization code to another function? Or allow
+# dipatch function to check if everything is in order do a transform.
+# Not an elegant solution.
+class TransformRunner(object):
def __init__(self, fn_pattern, driver, **kwargs):
"""
fn_pattern - file name or file name pattern (example "*.json")
@@ -152,10 +159,10 @@ class TransformRunner(object):
raise ValueError('json input in an unknown type')
else:
raise TypeError('filename_pattern must be a valid filename or pattern')
-
+
self.runs = json_input
self.kwargs = kwargs
-
+
def dispatch(self):
"""
main loop to run all the tests
@@ -172,7 +179,7 @@ class TransformRunner(object):
total_matches += matches
total_no_matches += no_matches
success_code += no_matches
-
+
return total_matches, total_no_matches, success_code
@@ -185,96 +192,99 @@ class TransformTestBase(object):
"""
json_dict must dictonary from json
"""
- if not isinstance(json_dict, dict): # must be a json dictionary
- raise TypeError("json_source must be a dictionary type not {0}".format(type(json_dict)))
-
+ if not isinstance(json_dict, dict): # must be a json dictionary
+ raise TypeError("json_source must be a dictionary type not {0}"
+ "".format(type(json_dict)))
+
# require keys 'coordinates' and 'projections'
if 'coordinates' not in json_dict:
- raise KeyError("TransformTest.__init__ requires 'coordinates' key in json source input")
+ raise KeyError("TransformTest.__init__ requires 'coordinates' key"
+ " in json source input")
if 'projections' not in json_dict:
- raise KeyError("TransformTest.__init__ requires 'projections' key in json source input")
-
- logging.info('Number of coordinate pairs to test: {0}'.format(len(json_dict['coordinates'])))
-
+ raise KeyError("TransformTest.__init__ requires 'projections' key"
+ " in json source input")
+
+ logging.info('Number of coordinate pairs to test: {0}'.format(
+ len(json_dict['coordinates'])))
+
self.run_test_args = kwargs.get('test')
# unpack coordinates
self.coords_left, self.coords_right = lzip(*json_dict['coordinates'])
-
+
self.testobj = json_dict
-
+
def runner_conversion(self, **kwargs):
"""
tests a single conversion
-
+
return tuple (num_matches, num_no_matches)
"""
# get tolerance, if not set tolerance to a precise value
tolerances = kwargs.get('tolerances', [0.0000000000001, 0.0000000000001])
-
+
test_right = self.transform(self.proj_left, self.proj_right, self.coords_left)
test_left = self.transform(self.proj_right, self.proj_left, self.coords_right)
results1 = list_count_matches(test_right, self.coords_right, tolerances[1])
results2 = list_count_matches(test_left, self.coords_left, tolerances[0])
-
+
return (results1[0] + results2[0], results1[1] + results2[1])
def runner_roundtrip(self, **kwargs):
"""
rountrip test using pyproj.
-
+
times - number roundtrips to perform
tolerance - TODO explain the structure of why this is a list
-
+
return tuple (num_matches, num_no_matches)
"""
times = None
-
+
# get variables
times = int(kwargs.get('times'))
tolerances = kwargs.get('tolerances', [0.0000000000001, 0.0000000000001])
-
- # keep the transformations separate, so as to not cross contaminate the results.
+
+ # keep the transformations separate, so as to not cross contaminate the
+ # results.
# process roundtrip for the left coordinates - Test 1
test1_left = self.coords_left
for i in range(times):
test1_right = self.transform(self.proj_left, self.proj_right, test1_left)
test1_left = self.transform(self.proj_right, self.proj_left, test1_right)
-
+
# process roundtrip for the right coordinates - Test 2
test2_right = self.coords_right
for i in range(times):
test2_left = self.transform(self.proj_right, self.proj_left, test2_right)
test2_right = self.transform(self.proj_left, self.proj_right, test2_left)
-
- results = ( list_count_matches(test1_right, self.coords_right, tolerances[1]), \
- list_count_matches(test1_left, self.coords_left, tolerances[0]), \
- list_count_matches(test2_right, self.coords_right, tolerances[1]), \
- list_count_matches(test2_left, self.coords_left, tolerances[0])
- )
-
-
- # return (results1[0] + results2[0], results1[1] + results2[1])
- return tuple(sum(x) for x in lzip(*results))
+ results = (
+ list_count_matches(test1_right, self.coords_right, tolerances[1]),
+ list_count_matches(test1_left, self.coords_left, tolerances[0]),
+ list_count_matches(test2_right, self.coords_right, tolerances[1]),
+ list_count_matches(test2_left, self.coords_left, tolerances[0])
+ )
+
+ return tuple(sum(x) for x in lzip(*results))
- # TODO: I made this but, haven't tested it. Is this useful? (Currently, no. Not useful for GIGS.)
+ # TODO: Untested. Not useful for GIGS.
def runner_onedirection(self, **kwargs):
"""
Perform a conversion in only one direction, not both. Useful for
testing convergence of a coordinate system.
- return tuple (num_matches, num_no_matches)
+ return tuple (num_matches, num_no_matches)
"""
# get variables
direction = kwargs.get('direction')
# get tolerance, if not set tolerance to a precise value
tolerances = kwargs.get('tolerances', [0.0000000000001, 0.0000000000001])
-
+
if direction not in ('left-to-right', 'right-to-left'):
raise ValueError('direction must be left-to-right or right-to-left, not: {0}'.format(direction))
@@ -286,13 +296,13 @@ class TransformTestBase(object):
results = list_count_matches(test_dest_left, self.coords_left, tolerances[0])
else:
raise RuntimeError('Unexpected state of value direction "{0}" in runner_onedirection'.format(direction))
-
+
return results
-
+
# placeholder function
def transform(self, src_crs, dst_crs, coords):
pass
-
+
def dispatch(self):
"""
main
@@ -300,38 +310,38 @@ class TransformTestBase(object):
matches, no_matches = 0, 0
# convert to tuple
- run_tests = self.run_test_args,
+ run_tests = self.run_test_args,
if self.run_test_args is None:
run_tests = ('conversion', 'roundtrip')
-
logging.info('Testing: {0}'.format(self.testobj['description']))
for test in self.testobj['tests']:
m_res, nm_res = None, None
if test['type'] not in run_tests:
# skip test
- continue
+ continue
if test['type'] == 'conversion':
m_res, nm_res = self.runner_conversion(**test)
elif test['type'] == 'roundtrip':
m_res, nm_res = self.runner_roundtrip(**test)
-
+
if nm_res == 0:
logging.info(" {0}... All {1} match.".format(test['type'], m_res))
else:
- logging.info(" {0}... matches: {1} doesn't match: {2}".format(test['type'], m_res, nm_res))
-
+ logging.info(" {0}... matches: {1} doesn't match: {2}"
+ "".format(test['type'], m_res, nm_res))
+
matches += m_res
no_matches += nm_res
return matches, no_matches
-
- # placeholder function # TODO How should this be exposed?
+
+ # placeholder function -- TODO How should this be exposed?
def driver_info(self):
return "base class"
-
+
class TransformTestPyProj(TransformTestBase):
"""
@@ -340,29 +350,33 @@ class TransformTestPyProj(TransformTestBase):
def __init__(self, json_dict, kwargs):
# call super class
TransformTestBase.__init__(self, json_dict, kwargs)
-
+
# setup projections
try:
self.proj_left = pyproj.Proj(json_dict['projections'][0], preserve_units=True)
except RuntimeError as e:
- logging.error('pyproj raised a RuntimeError for projection string "{0}"'.format(json_dict['projections'][0]))
+ logging.error('pyproj raised a RuntimeError for projection string:'
+ ' "{0}"'.format(json_dict['projections'][0]))
raise RuntimeError(e)
try:
self.proj_right = pyproj.Proj(json_dict['projections'][1], preserve_units=True)
except RuntimeError as e:
- logging.error('pyproj raised a RuntimeError for projection string "{0}"'.format(json_dict['projections'][1]))
+ logging.error('pyproj raised a RuntimeError for projection string:'
+ ' "{0}"'.format(json_dict['projections'][1]))
raise RuntimeError(e)
-
+
def transform(self, src_crs, dst_crs, coords):
return self.pyproj_transform_ex(src_crs, dst_crs, coords)
def driver_info(self):
- return 'pyproj {0}\nproj4 {1}\n'.format(pyproj.__version__, self.proj4_version())
+ return 'pyproj {0}\nproj4 {1}\n'.format(
+ pyproj.__version__, self.proj4_version())
- # TODO: this is currently dead code, unneeded for the pyproj repo version as of 2016-05-24.
+ # TODO: currently dead code, unneeded for the pyproj repo. version
+ # as of 2016-05-24.
def proj4_version():
"""
- Gives the proj.4 library's version number. (requires pyproj to be installed)
+ Gives the proj.4 library's version number. (requires pyproj)
returns string, so proj.4 version 4.9.3 will return "4.9.3"
"""
try:
@@ -371,27 +385,22 @@ class TransformTestPyProj(TransformTestBase):
# for pyproj versions 1.9.5.1 and before, this will run
# Get PROJ4 version in a floating point number
proj4_ver_num = pyproj.Proj(proj='latlong').proj_version
-
- # reformats floating point number into string (4.90 becomes '4.9.0')
- # Exploits single number version numbers for proj4,
- return '.'.join( str(int(proj4_ver_num*100)) )
+
+ # convert float into a version string (4.90 becomes '4.9.0')
+ return '.'.join(str(int(proj4_ver_num * 100)))
def pyproj_transform_ex(self, proj_src, proj_dst, coords):
"""
wrapper for pyproj.transform to do all the zipping of the coordinates
-
+
returns coordinate list
"""
- # # convert Python 3.x+ zip object to a list
- # if PY_MAJOR > 2 and isinstance(coords, zip):
- # coords = list(coords)
-
# are these coordinate triples?
if len(coords[0]) == 3:
xi, yi, zi = lzip(*coords)
xo, yo, zo = pyproj.transform(proj_src, proj_dst, xi, yi, zi)
return lzip(xo, yo, zo)
-
+
# assume list of coordinate pairs
xi, yi = lzip(*coords)
xo, yo = pyproj.transform(proj_src, proj_dst, xi, yi)
@@ -402,20 +411,20 @@ class TransformTestCs2cs(TransformTestBase):
def __init__(self, json_dict, kwargs):
# call super class
TransformTestBase.__init__(self, json_dict, kwargs)
-
+
# copy proj4 projection strings
self.proj_left, self.proj_right = json_dict['projections']
-
+
self.exe = kwargs.get('exe', 'cs2cs')
-
+
# when the exe is not the default, check if the file exists
if self.exe == 'cs2cs' or not os.path.isfile(self.exe):
- raise(RuntimeError, 'cannot find cs2cs executable file: {}'.format(self.exe))
-
+ raise RuntimeError('cannot find cs2cs executable file: {}'
+ ''.format(self.exe))
def transform(self, src_crs, dst_crs, coords):
- pnt_list = []
# send points to a temporary file
+ # TODO Should this use with statement?
tmpfn = NamedTemporaryFile(mode='w+t', delete=False)
for point in coords:
# convert list of float values into a list of strings
@@ -423,98 +432,96 @@ class TransformTestCs2cs(TransformTestBase):
# print('POINT: {}'.format(point))
tmpfn.write(' '.join(point) + '\n')
-
tmpfn.flush()
-# command = "{path}{exe} {proj_from} +to {proj_to} -f %.13f {filename}".format( \
-# path=self.path_to_exe, exe=self.exe, proj_from=src_crs, \
-# proj_to=dst_crs, filename=tmpfn.name) ## WORKED ON WINDOWS Python 2.7
- command = "{exe} {proj_from} +to {proj_to} -f %.13f {filename}".format( \
+ command = "{exe} {proj_from} +to {proj_to} -f %.13f {filename}".format(
exe=self.exe, proj_from=src_crs, proj_to=dst_crs, filename=tmpfn.name)
- tmpfn.close() ## WORKED on WINDOWS Python 2.7
-
-
+ tmpfn.close()
+
logging.debug('Running Popen on command "{0}"'.format(command))
-
+
if platform.system() == 'Windows':
- shell=False
+ shell = False
else:
- # shell=True according to the subprocess documentation has some security implications
+ # shell=True according to the subprocess documentation has some
+ # security implications
# Linux seems to need this
- shell=True
-
- # call cs2cs
+ shell = True
+
+ # call cs2cs
outs = subprocess.check_output(command, shell=shell)
-
+
# delete temporary filename
os.unlink(tmpfn.name)
-
-
+
# print('RESULTS OUTS: {}\n'.format(outs))
# print('RESULTS ERRS: {}\n'.format(errs))
-
+
# outs
# print('RESULTS LINE: {}\n'.format([line.split() for line in outs]))
coords = []
-
+
# process output
for line in outs.splitlines():
# print("LINE: {}".format(line))
coord = []
for e in line.split():
coord.append(float(e))
-
+
coords.append(coord)
-
+
# print('COORDS: {}\n'.format(coords))
return coords
def driver_info(self):
- shell=True # see transform() for info.
+ shell = True # see transform() for info.
if platform.system() == 'Windows':
- shell=False
+ shell = False
outs = subprocess.check_output(self.exe, shell=shell)
return 'PROJ.4 version: ' + outs.splitlines()[0] + '\n'
-
-
+
if __name__ == '__main__':
-# logging.basicConfig(level=logging.DEBUG)
+ # logging.basicConfig(level=logging.DEBUG)
logging.basicConfig(level=logging.INFO)
parser = argparse.ArgumentParser(description='Test PROJ.4 using a JSON file.')
- parser.add_argument('-e', '--exe', \
- help="executable with path default: 'cs2cs' (only needed for cs2cs driver)")
+ parser.add_argument('-e', '--exe',
+ help="executable with path default: 'cs2cs' (needed for cs2cs driver)")
+
+ parser.add_argument('-d', '--driver', default='pyproj',
+ help='driver to test')
- parser.add_argument('-d', '--driver', default='pyproj', help='driver to test')
+ parser.add_argument('-t', '--test',
+ help='only run these test types (valid values: conversion or roundtrip)')
- parser.add_argument('-t', '--test', help='only run these test types (valid values: conversion or roundtrip)')
-
# get json file names and/or glob patterns
- parser.add_argument('testnames_pat_json', nargs=argparse.REMAINDER, help='single filename or glob wildcard patern')
-
- args = parser.parse_args()
-
+ parser.add_argument('testnames_pat_json', nargs=argparse.REMAINDER,
+ help='single filename or glob wildcard patern')
+
+ args = parser.parse_args()
+
# test that the arguments have sensible values
if args.driver not in ('cs2cs', 'pyproj'):
raise ValueError('driver "{}" is not a valid driver'.format(args.driver))
-
+
logging.info('Python {}'.format(sys.version))
logging.info('using driver: {}'.format(args.driver))
-
+
# there could be a version command for the TransformRunner TODO
match_results, nonmatch_results, success_code = 0, 0, 0
for test_name in args.testnames_pat_json:
-# print('TEST_NAME: {}'.format(test_name))
- tratst = TransformRunner(test_name, driver=args.driver, exe=args.exe, test=args.test)
+ tratst = TransformRunner(test_name, driver=args.driver, exe=args.exe,
+ test=args.test)
m_res, nm_res, success_cd = tratst.dispatch()
match_results += m_res
nonmatch_results += nm_res
success_code += success_cd
-
+
logging.info("----------------------------------------")
- logging.info("TOTAL: matches: {0} non-matching: {1}".format(match_results, nonmatch_results))
+ logging.info("TOTAL: matches: {0} non-matching: {1}"
+ "".format(match_results, nonmatch_results))
# exit status code is the number of non-matching results
# This should play nicely with Travis and similar testing.