Sunday 2 December 2018

QGIS Plugin to convert DOF (Digital Obstacle File) data


To use FAA DOF in GIS systems  effectively, we need to store them in format wchich is more friemdly for GIS purposes such as:


  • CSV: it is easy to load into GIS by defining delimiter character and fields with latitude and longitude, etc.   
  • KML: we can import kml format diectly into GIS software, display it uisng Google Earth
  • SHP : popular geospatial vector data format, store both: geometric location and attribute information
Each of this formats has drawbacks:
CSV, KML - are text files with no support for spatial indexing and etc, so for large datasest manipulation on data can be time consuming
SHP - support spatial indexing but has other limitations (e. g. maximum size of file is 2 GB)

Constructor:

def __init__(self):
    self.oas_code = ''
    self.obs_number = ''
    self.verif_stat_code = ''
    self.lat_dms = ''
    self.lon_dms = ''
    self.obs_type = ''
    self.agl_height = None
    self.amsl_height = None
    self.lighting_code = ''
    self.h_acc_code = ''
    self.v_acc_code = ''
    self.marking_code = ''
    self.jdate = ''
    self.lat_dd = None
    self.lon_dd = None
    self.ctry_name = ''
    self.verif_stat_desc = ''
    self.marking_desc = ''
    self.lighting_desc = ''
    self.h_acc_value = ''
    self.h_acc_uom = ''
    self.v_acc_value = ''
    self.v_acc_uom = ''  

Conversion DMS to DD:


@staticmethod
def faa_dof_dms2dd(dms):
    h = dms[-1]
    dms_m = dms[:-1]
    dms_t = dms_m.split(' ')
    d = float(dms_t[0])
    m = float(dms_t[1])
    s = float(dms_t[2])
    dd = d + m / 60 + s / 3600
    if h in ['W', 'S']:
        dd = - dd
    return dd

Method that parses line od DOF and assign values to attributes.
Notes:
  1. If value is 'empty' for obstacle, arbitrary values are assigne to indicate that source file hss no value
  2. Which subset of characters are assgineged to attributes are 'na sztywno' coded in method. This solution has one significant disadvantage: if structure of DOF file will change, we also change mthod to ensure that correct values from DOF are assigned to attributes. How to avoid this at some level will be discussed in the future when I will be writinng plugin to cover eTOD (electronic terrain and obstacle data).

def parse_dof_line(self, line, coordinates_dd=False, decode_values=False):
    self.oas_code = line[0:2]
    self.obs_number = line[0:9]
    self.verif_stat_code = line[10]
    self.lat_dms = line[35:47]
    self.lon_dms = line[48:61]
    self.obs_type = line[62:80].rstrip()
    if self.obs_type == '':
        self.obs_type = 'NO DATA'
    self.agl_height = line[83:88].lstrip('0')
    if self.agl_height == '':
        self.agl_height = -9999
    self.amsl_height = line[89:94].lstrip('0')
    if self.amsl_height == '':
        self.amsl_height = -9999
    self.lighting_code = line[95]
    if self.lighting_code == '':
        self.lighting_code = 'NO DATA'
    self.h_acc_code = line[97]
    if self.h_acc_code == '':
        self.h_acc_code = 'NO DATA'
    self.v_acc_code = line[99]
    if self.v_acc_code == '':
        self.v_acc_code = 'NO DATA'
    self.marking_code = line[101]
    if self.marking_code == '':
        self.marking_code = 'NO DATA'
    self.jdate = line[120:127]
    if self.jdate == '':
        self.jdate = 'NO DATA'
    if coordinates_dd is False:
        self.lat_dd = None
        self.lon_dd = None
    else:
        self.lat_dd = self.faa_dof_dms2dd(self.lat_dms)
        self.lon_dd = self.faa_dof_dms2dd(self.lon_dms)
    if decode_values is False:
        self.ctry_name = ''
        self.verif_stat_desc = ''
        self.marking_desc = ''
        self.lighting_desc = ''
        self.h_acc_value = ''
        self.h_acc_uom = ''
        self.v_acc_value = ''
        self.v_acc_uom = ''
    else:
        try:
            self.ctry_name = oas_codes.get(self.oas_code)
        except KeyError:
            self.ctry_name = ''
        try:
            self.verif_stat_desc = verif_stat_codes.get(self.verif_stat_code)
        except KeyError:
            self.verif_stat_desc = ''
        try:
            self.marking_desc = marking_codes.get(self.marking_code)
        except KeyError:
            self.marking_desc = ''
        try:
            self.lighting_desc = lighting_codes.get(self.lighting_code)
        except KeyError:
            self.lighting_desc = ''
        try:
            self.v_acc_value = v_acc_codes.get(self.v_acc_code, -999)
            self.v_acc_uom = 'FEET'
        except KeyError:
            self.v_acc_value = -999
            self.v_acc_uom = ''
        try:
            if self.h_acc_code == '':
                self.h_acc_value = -9999
                self.h_acc_uom = 'NO DATA'
            else:
                h_acc_decode = h_acc_codes.get(self.h_acc_code)
                if h_acc_decode is None:
                    self.h_acc_value = -999
                    self.h_acc_uom = 'NO DATA'
                else:
                    self.h_acc_value = h_acc_decode[0]
                    self.h_acc_uom = h_acc_decode[1]
        except KeyError:
            self.v_acc_value = -9999
            self.v_acc_uom = 'NO DATA'

Now, we can edit code that is responisble for:
  1. inetarction of our plugin with the user (buttons etc.)
  2. wrting content from DAT file into output file (CSV, SHP, KML)

No comments:

Post a Comment