diff --git a/bin/p111.py b/bin/p111.py new file mode 100644 index 0000000..d599f4b --- /dev/null +++ b/bin/p111.py @@ -0,0 +1,200 @@ +#!/usr/bin/python3 + +""" +P1/11 parsing functions. +""" + +import math +import re +from datetime import datetime, timedelta, timezone +from parse_fwr import parse_fwr + +def _int (string): + return int(float(string)) + +def _date (string): + return datetime.strptime(string, "%Y:%m:%d").replace(tzinfo=timezone.utc) + +def _time (string): + try: + return datetime.strptime(string, "%H:%M:%S.%f").replace(tzinfo=timezone.utc).time() + except ValueError: + return datetime.strptime(string, "%H:%M:%S").replace(tzinfo=timezone.utc).time() + +def _datetime (string): + try: + return datetime.strptime(string, "%Y:%m:%d:%H:%M:%S.%f").replace(tzinfo=timezone.utc) + except ValueError: + try: + return datetime.strptime(string, "%Y:%m:%d:%H:%M:%S").replace(tzinfo=timezone.utc) + except ValueError: + try: + return datetime.strptime(string, "%Y:%j:%H:%M:%S.%f").replace(tzinfo=timezone.utc) + except ValueError: + return datetime.strptime(string, "%Y:%j:%H:%M:%S").replace(tzinfo=timezone.utc) + +def floatOrNone (string): + try: + return float(string) + except ValueError: + return None + +def parse_p111_file_info (string): + """ + Parse OGP File Identification Record + + Returns a dictionary of fields + """ + + defs = [ + ("Contents Description", str), + ("Format Code", int), + ("Format Version Number", float), + ("File Issue Number", int), + ("Date File Written", _date), + ("Time File Written", _time), + ("Name of File", str), + ("Prepared By", str) + ] + + record = string.split(",") + + # i[0][0] is the name + # i[0][1] is the type converter + # i[1] is the record value + data = dict([(i[0][0], i[0][1](i[1])) for i in zip(defs, record[1:])]) + data["tstamp"] = datetime.combine(data["Date File Written"], data["Time File Written"]).replace(tzinfo=timezone.utc) + data["type"] = "File Identification Record" + + return data + + +def parse_p111_header (string): + """ + Parse any HC, H1 header record. + + This function does not interpret the values of individual header types. + + Returns dictionary of fields. + """ + + record = string.split(",") + data = dict(enumerate([",".join(record[0:4])] + [record[4].rstrip()] + record[5:])) + data["type"] = "Common Header" if data[0] == "HC" else "P1/11 Header" + + return data + +def parse_p111_position (string): + """ + Parse a P1 position record. + + NOTE: Utterly ignores all of the header record definitions. + NOTE: It also ignores “Additional Quality Measures” and + “Additional Data Fields”. + + Returns dictionary of fields. + """ + + defs = [ + ("Record Identifier", str), + ("Record Version", int), + ("Acquisition Line Name", str), + ("Preplot Line Name", _int), + ("Acquisition Point Number", _int), + ("Preplot Point Number", _int), + ("Index Number", int), + ("Time", _datetime), + ("Object Ref. Number", int), + ("Object Short Name", str), + ("Record Type Number", int), + ("(Dummy field)", str), + ("CRS A Coordinate 1", floatOrNone), + ("CRS A Coordinate 2", floatOrNone), + ("CRS A Coordinate 3", floatOrNone), + ("CRS B Coordinate 1", floatOrNone), + ("CRS B Coordinate 2", floatOrNone), + ("CRS B Coordinate 3", floatOrNone), + ("CRS C Coordinate 1", floatOrNone), + ("CRS C Coordinate 2", floatOrNone), + ("CRS C Coordinate 3", floatOrNone), + ("Error Ellipse Horizontal Semimajor Axis or Radial Error Estimate", floatOrNone), + ("Error Ellipse Horizontal Semiminor Axis", floatOrNone), + ("Error Ellipse Horizontal Azimuth", floatOrNone), + ("Error Ellipse Vertical Axis or Height Error Estimate", floatOrNone) + ] + + record = string.split(",", 24) + + # i[0][0] is the name + # i[0][1] is the type converter + # i[1] is the record value + data = dict([(i[0][0], i[0][1](i[1])) for i in zip(defs, record)]) + data["tstamp"] = data["Time"] + data["type"] = "S" if record[0] == "S1" else "P" + + return data + + +def parse_line (string): + type = string[:3] + + if type == "OGP": + return parse_p111_file_info(string) + elif type == "HC,": + return parse_p111_header(string) + elif type == "H1,": + return parse_p111_header(string) + elif type == "P1,": + return parse_p111_position(string) + elif type == "S1,": + return parse_p111_position(string) + else: + # We ignore other records + return None + + +def p111_type(type, records): + return [ r for r in records if r["type"] == type ] + +def point_number(record): + if "Preplot Point Number" in record: + return record["Preplot Point Number"] + return None + +def line(record): + if "Preplot Line Name" in record: + return record["Preplot Line Name"] + return None + +def easting(record): + if "CRS A Coordinate 1" in record: + return record["CRS A Coordinate 1"] + return None + +def northing(record): + if "CRS A Coordinate 2" in record: + return record["CRS A Coordinate 2"] + return None + +def from_file(path, only_records=None, shot_range=None): + records = [] + with open(path) as fd: + cnt = 0 + line = fd.readline() + while line: + cnt = cnt + 1 + + record = parse_line(line) + if record is not None: + if only_records: + if not record["record_type"] in only_records: + continue + if shot_range: + shot = int(record["point_number"]) + if not (shot >= min(shot_range) and shot <= max(shot_range)): + continue + + records.append(record) + line = fd.readline() + + return records