def decode_waypoint(self, fields, headers): # Ignore empty lines num_fields = len(fields) if num_fields == 0: return # Ignore comments if fields[0].startswith("*"): return if num_fields < len(BASE_HEADERS): raise ParserError( "Not enough fields provided. Expecting at minimum following fields: \nname, code, country, lat, lon, elev, style" ) if num_fields > len(BASE_HEADERS) + len(PLUS_HEADERS): raise ParserError( "Too many fields provided. Expecting at maximum following 14 fields" ) return { MAPPING[h]: getattr(self, "decode_%s" % MAPPING[h])(field.strip()) for h, field in zip(headers, fields) if MAPPING.get(h) }
def decode_style(self, style): try: style = int(style) except ValueError: raise ParserError('Reading style failed') if not (1 <= style <= 17): raise ParserError('Unknown waypoint style') return style
def decode_longitude(self, longitude): match = RE_LONGITUDE.match(longitude) if not match: raise ParserError('Reading longitude failed') longitude = int(match.group(1)) + float(match.group(2)) / 60. if not (0 <= longitude <= 180): raise ParserError('Longitude out of bounds') if match.group(3).upper() == 'W': longitude = -longitude return longitude
def decode_latitude(self, latitude): match = RE_LATITUDE.match(latitude) if not match: raise ParserError('Reading latitude failed') latitude = int(match.group(1)) + float(match.group(2)) / 60. if not (0 <= latitude <= 90): raise ParserError('Latitude out of bounds') if match.group(3).upper() == 'S': latitude = -latitude return latitude
def decode_longitude(self, line): match = RE_LONGITUDE.match(line[52:60]) if not match: raise ParserError('Reading longitude failed') lon = (int(match.group(2)) + int(match.group(3)) / 60. + int(match.group(4)) / 3600.) if not (0 <= lon <= 180): raise ParserError('Longitude out of bounds') if match.group(1) == 'W': lon = -lon return lon
def decode_latitude(self, line): match = RE_LATITUDE.match(line[45:52]) if not match: raise ParserError('Reading latitude failed') lat = (int(match.group(2)) + int(match.group(3)) / 60. + int(match.group(4)) / 3600.) if not (0 <= lat <= 90): raise ParserError('Latitude out of bounds') if match.group(1) == 'S': lat = -lat return lat
def decode_waypoint(self, fields): # Ignore header line if fields == ['name', 'code', 'country', 'lat', 'lon', 'elev', 'style', 'rwdir', 'rwlen', 'freq', 'desc']: return # Ignore empty lines num_fields = len(fields) if num_fields == 0: return # Ignore comments if fields[0].startswith('*'): return if num_fields != 11: raise ParserError('Fields are missing') fields = [field.strip() for field in fields] return { 'name': self.decode_name(fields[0]), 'code': self.decode_code(fields[1]), 'country': self.decode_country(fields[2]), 'latitude': self.decode_latitude(fields[3]), 'longitude': self.decode_longitude(fields[4]), 'elevation': self.decode_elevation(fields[5]), 'style': self.decode_style(fields[6]), 'runway_direction': self.decode_runway_direction(fields[7]), 'runway_length': self.decode_runway_length(fields[8]), 'frequency': self.decode_frequency(fields[9]), 'description': self.decode_description(fields[10]), }
def convert_waypoint(self, old): waypoint = {} waypoint['name'] = old['name'] waypoint['shortname'] = old['code'] waypoint['country'] = old['country'] waypoint['description'] = old['description'] waypoint['latitude'] = old['latitude'] waypoint['longitude'] = old['longitude'] waypoint['elevation'] = self.convert_elevation(old['elevation']) if old['style'] not in WAYPOINT_STYLE_MAPPING: raise ParserError('Unknown waypoint style') waypoint['classifiers'] = set(WAYPOINT_STYLE_MAPPING[old['style']]) if 'landable' in waypoint['classifiers']: waypoint['icao'] = None waypoint['runways'] = self.convert_runways(old['style'], old['runway_direction'], old['runway_length']) waypoint['frequencies'] = self.convert_frequencies( old['frequency']) return waypoint
def decode_waypoint(self, line): line = line.strip() if not line or line.startswith('$'): return # Check valid line length if len(line) != 64: raise ParserError('Line length does not match 64') return { 'shortform': self.decode_shortform(line), 'is_airfield': self.decode_is_airfield(line), 'is_unclear': self.decode_is_unclear(line), 'is_outlanding': self.decode_is_outlanding(line), 'shortform_zander': self.decode_shortform_zander(line), 'text': self.decode_text(line), 'icao': self.decode_icao(line), 'is_ulm': self.decode_is_ulm(line), 'field_number': self.decode_field_number(line), 'is_glidersite': self.decode_is_glidersite(line), 'runway_surface': self.decode_runway_surface(line), 'runway_length': self.decode_runway_length(line), 'runway_directions': self.decode_runway_directions(line), 'frequency': self.decode_frequency(line), 'elevation': self.decode_elevation(line), 'elevation_proved': self.decode_elevation_proved(line), 'latitude': self.decode_latitude(line), 'longitude': self.decode_longitude(line), 'ground_check_necessary': self.decode_ground_check_necessary(line), 'better_coordinates': self.decode_better_coordinates(line), 'country': self.decode_country(line), 'year_code': self.decode_year_code(line), 'source_code': self.decode_source_code(line), }
def decode_frequency(self, frequency): if not frequency: return None if not RE_FREQUENCY.match(frequency): raise ParserError('Reading frequency failed') return frequency
def decode_elevation(self, elevation): match = RE_ELEVATION.match(elevation) if not match: raise ParserError('Reading elevation failed') try: value = float(match.group(1)) except ValueError: value = None unit = match.group(2) if unit and unit.lower() not in ('m', 'ft'): raise ParserError('Unknown elevation unit') return { 'value': value, 'unit': unit, }
def decode_elevation(self, elevation): match = RE_ELEVATION.match(elevation) if not match: raise ParserError("Reading elevation failed") try: value = float(match.group(1)) except ValueError: value = None unit = match.group(2) if unit and unit.lower() not in ("m", "ft"): raise ParserError("Unknown elevation unit") return { "value": value, "unit": unit, }
def decode_runway_direction(self, runway_direction): if not runway_direction: return None try: runway_direction = int(runway_direction) except ValueError: raise ParserError('Reading runway direction failed') return runway_direction
def decode_waypoint(self, fields): # Ignore header line if fields == [ 'name', 'code', 'country', 'lat', 'lon', 'elev', 'style', 'rwdir', 'rwlen', 'freq', 'desc' ]: return # Ignore empty lines num_fields = len(fields) if num_fields == 0: return # Ignore comments if fields[0].startswith('*'): return if num_fields < 11: raise ParserError( 'Not enough fields provided. Expecting at minimum following 11 fileds:\nname,code,country,lat,lon,elev,style,rwdir,rwlen,freq,desc' ) if num_fields > 13: raise ParserError( 'Too many fields provided. Expecting at maximum following 13 fileds:\nname,code,country,lat,lon,elev,style,rwdir,rwlen,freq,desc,userdata,pics' ) fields = [field.strip() for field in fields] return { 'name': self.decode_name(fields[0]), 'code': self.decode_code(fields[1]), 'country': self.decode_country(fields[2]), 'latitude': self.decode_latitude(fields[3]), 'longitude': self.decode_longitude(fields[4]), 'elevation': self.decode_elevation(fields[5]), 'style': self.decode_style(fields[6]), 'runway_direction': self.decode_runway_direction(fields[7]), 'runway_length': self.decode_runway_length(fields[8]), 'frequency': self.decode_frequency(fields[9]), 'description': self.decode_description(fields[10]), }
def decode_distance(self, distance_str): if not distance_str: return { "value": None, "unit": None, } match = RE_DISTANCE.match(distance_str) if not match: raise ParserError("Reading neardis failed") try: value = float(match.group(1)) except ValueError: value = None unit = match.group(2) if unit and unit.lower() not in ("m", "ft", "km", "ml", "nm"): raise ParserError("Unknown distance unit") return { "value": value, "unit": unit, }
def decode_distance(self, distance_str): if not distance_str: return { 'value': None, 'unit': None, } match = RE_DISTANCE.match(distance_str) if not match: raise ParserError('Reading neardis failed') try: value = float(match.group(1)) except ValueError: value = None unit = match.group(2) if unit and unit.lower() not in ('m', 'ft', 'km', 'ml', 'nm'): raise ParserError('Unknown distance unit') return { 'value': value, 'unit': unit, }
def decode_runway_width(self, runway_width): if not runway_width: return { "value": None, "unit": None, } match = RE_RUNWAY_WIDTH.match(runway_width) if not match: raise ParserError("Reading runway width failed") try: value = float(match.group(1)) except ValueError: value = None unit = match.group(2) if unit and unit.lower() not in ("m", "nm", "ml"): raise ParserError("Unknown runway width unit") return { "value": value, "unit": unit, }
def decode_runway_length(self, runway_length): if not runway_length: return { 'value': None, 'unit': None, } match = RE_RUNWAY_LENGTH.match(runway_length) if not match: raise ParserError('Reading runway length failed') try: value = float(match.group(1)) except ValueError: value = None unit = match.group(2) if unit and unit.lower() not in ('m', 'nm', 'ml'): raise ParserError('Unknown runway length unit') return { 'value': value, 'unit': unit, }
def read(self, fp): waypoints = [] tasks = [] task_information = False reader = csv.reader(fp) headers = next(reader, []) if not all(h in headers for h in BASE_HEADERS): raise ParserError( "Headers must include at least include name, code, country, lat, lon, elev, style" ) for fields in reader: if fields == ["-----Related Tasks-----"]: task_information = True continue if task_information: if fields[0] == "Options": tasks[-1]["Options"] = self.decode_task_options(fields) elif fields[0].startswith("ObsZone"): tasks[-1]["obs_zones"].append( self.decode_task_obs_zone(fields)) else: tasks.append({ "name": self.decode_task_name(fields), "waypoints": self.decode_task_waypoints(fields), "options": None, "obs_zones": [], }) else: waypoint = self.decode_waypoint(fields, headers) if waypoint: waypoints.append(waypoint) return dict(waypoints=waypoints, tasks=tasks)
def decode_name(self, name): if not name: raise ParserError('Name field must not be empty') return name
def decode_country(self, country): if RE_COUNTRY.match(country): return country else: raise ParserError('Invalid country code')