Add ability to read in elevation data
This commit is contained in:
parent
6577717cbf
commit
63eda6d69c
124
read_in.py
Normal file
124
read_in.py
Normal file
|
@ -0,0 +1,124 @@
|
|||
from emis_funky_funktions import *
|
||||
|
||||
from itertools import chain
|
||||
from typing import List, NamedTuple
|
||||
|
||||
def meters_to_millimeters(meters: float) -> int:
|
||||
"""
|
||||
Convert meters (floating point) to millimeters (int)
|
||||
|
||||
>>> meters_to_millimeters(1.9333473e+02)
|
||||
193334
|
||||
|
||||
>>> meters_to_millimeters(1.0)
|
||||
1000
|
||||
"""
|
||||
return int(1000 * meters)
|
||||
|
||||
def read_single_elevation(dat: str) -> Result[int, str]:
|
||||
"""
|
||||
Read in a single elevation
|
||||
|
||||
Expects a single, stringified floating point value, in meters, without a unit, e.g.
|
||||
"2.057e+02". If there is a parsing error, returns an error containing the input
|
||||
string. Otherwise, returns the elevation in millimeters.
|
||||
|
||||
>>> read_single_elevation("1.9333473e+02")
|
||||
Ok(val=193334)
|
||||
|
||||
>>> read_single_elevation("1.0")
|
||||
Ok(val=1000)
|
||||
|
||||
>>> read_single_elevation("10.0m")
|
||||
Err(err='10.0m')
|
||||
"""
|
||||
return map_res(
|
||||
meters_to_millimeters, # fuck floating points
|
||||
try_(replace(dat), float, dat)
|
||||
)
|
||||
|
||||
def read_elevation_line(line: str) -> Result[Iterator[int], Tuple[int, str]]:
|
||||
"""
|
||||
Reads a line of elevations
|
||||
|
||||
Each elevation should be in the format specified by `read_single_elevation`. Every
|
||||
element of the line is parsed and combined into an iterator, which is returned. If
|
||||
any parsing errors are encountered, the first error is returned. It takes the form of
|
||||
a tuple containing the column of the error (in terms of whitespace-deliniated words,
|
||||
not characters) and the string which could not be parsed.
|
||||
|
||||
Recall from the definition of `read_single_elevation()` that the return value is an
|
||||
integer number of millimeters.
|
||||
|
||||
As per the problem specification, the last five columns of the line are not read or
|
||||
parsed. Parsing errors in these columns therefor cannot be encountered, and any data
|
||||
in these columns will not be returned.
|
||||
|
||||
>>> map_res(list, read_elevation_line("1.9e2 18e1 181 179.1 ignored 170.1 1.8e2 18i+2 20e1"))
|
||||
Ok(val=[190000, 180000, 181000, 179100])
|
||||
|
||||
>>> map_res(list, read_elevation_line("1.9e2 18e 181 179.1 ignored 170.1 1.8e2 18i+2 20e1"))
|
||||
Err(err=(2, '18e'))
|
||||
"""
|
||||
return sequence([
|
||||
map_err(
|
||||
lambda err_text: (col_no + 1, err_text),
|
||||
read_single_elevation(elem)
|
||||
)
|
||||
# As per spec, we drop the last five entries of the file.
|
||||
for (col_no, elem) in enumerate(line.split()[:-5])
|
||||
])
|
||||
|
||||
class ErrorLocation(NamedTuple):
|
||||
"The location of an error within file"
|
||||
|
||||
line_no: int
|
||||
"The line on which the error occurred"
|
||||
|
||||
col_no: int
|
||||
"The column (1-indexed number of whitespace deliniated words) on which the error occurred"
|
||||
|
||||
invalid_entry: str
|
||||
"The text which failed to parse as a floating point value"
|
||||
|
||||
def read_elevations(lines: str) -> Result[Iterator[int], ErrorLocation]:
|
||||
"""
|
||||
Read an entire elevation file into a list
|
||||
|
||||
This parses each line in of the file using `read_elevation_line()`. Errors
|
||||
encountered are reported using an `ErrorLocation`. The resulting list is
|
||||
single-dimensional, but can be indexed arbitrarily, meaning for an input file with a
|
||||
constant number of columns per line, this data can be accessed as if it were a 2d
|
||||
square array.
|
||||
|
||||
However, keep in mind that, as with `read_elevation_line()`, the last five columns of
|
||||
each line are dropped/ignored, and are therefor not present in the returned data.
|
||||
|
||||
>>> map_res(list, read_elevations('''
|
||||
... 1 2 3 4 5 6 7 8 9e 10
|
||||
... 11 2.0 3e1 4 5 6 7 bwa 9 10
|
||||
... 11 2.0 3e0 4 5 ign err 8 9 10
|
||||
... '''))
|
||||
Ok(val=[1000, 2000, 3000, 4000, 5000, 11000, 2000, 30000, 4000, 5000, 11000, 2000, 3000, 4000, 5000])
|
||||
|
||||
>>> map_res(list, read_elevations('''
|
||||
... 1 2 3 4 5 6 7 8 9e 10
|
||||
... 11 ERR 3e1 4 5 6 7 bwa 9 10
|
||||
... 11 2.0 3e0 4 5 ign err 8 9 10
|
||||
... '''))
|
||||
Err(err=ErrorLocation(line_no=2, col_no=2, invalid_entry='ERR'))
|
||||
"""
|
||||
return map_res(
|
||||
chain.from_iterable,
|
||||
sequence([
|
||||
map_err(
|
||||
lambda err: ErrorLocation(line_no + 1, *err),
|
||||
read_elevation_line(line)
|
||||
)
|
||||
for (line_no, line) in enumerate(lines.strip().split('\n'))
|
||||
])
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import doctest
|
||||
doctest.testmod()
|
Loading…
Reference in a new issue