class AIPP::LF::ENR21
FIR, TMA etc
Constants
- LOCATION_INDICATORS
Map airspace “<type> <name>” to location indicator
- NAME_BLACKLIST_RE
Airspaces to be ignored
- SERVICE_FIXES
Fix incomplete SIV service columns
- SOURCE_TYPES
Map source types to type and optional local type
Public Instance Methods
parse()
click to toggle source
# File lib/aipp/regions/LF/ENR-2.1.rb 38 def parse 39 prepare(html: read).css('tbody').each do |tbody| 40 airspace = nil 41 tbody.css('tr').to_enum.with_index(1).each do |tr, index| 42 if tr.attr(:id).match?(/--TXT_NAME/) 43 if airspace 44 if airspace.name.match? NAME_BLACKLIST_RE 45 verbose_info "Ignoring #{airspace.type} #{airspace.name}" unless airspace.type == :terminal_control_area 46 else 47 add airspace 48 end 49 end 50 airspace = airspace_from tr.css(:td).first 51 verbose_info "Parsing #{airspace.type} #{airspace.name}" unless airspace.type == :terminal_control_area 52 next 53 end 54 begin 55 tds = tr.css('td') 56 if airspace.type == :terminal_control_area && tds[0].text.blank_to_nil 57 if airspace.layers.any? 58 if airspace.name.match? NAME_BLACKLIST_RE 59 verbose_info "Ignoring #{airspace.type} #{airspace.name}" 60 else 61 add airspace 62 end 63 end 64 airspace = airspace_from tds[0] 65 verbose_info "Parsing #{airspace.type} #{airspace.name}" 66 end 67 if airspace 68 remarks = tds[-1].text 69 if tds[0].text.blank_to_nil 70 airspace.geometry = geometry_from tds[0].text 71 fail("geometry is not closed") unless airspace.geometry.closed? 72 end 73 layer = layer_from(tds[-3].text) 74 layer.class = class_from(tds[1].text) if tds.count == 5 75 layer.location_indicator = LOCATION_INDICATORS.fetch("#{airspace.type} #{airspace.name}", nil) 76 if airspace.local_type == 'SIV' # services parsed for SIV only 77 layer.add_services services_from(tds[-2], remarks) 78 end 79 layer.timetable = timetable_from! remarks 80 layer.remarks = remarks_from remarks 81 airspace.add_layer layer 82 end 83 rescue => error 84 warn("error parsing #{airspace.type} `#{airspace.name}' at ##{index}: #{error.message}", pry: error) 85 end 86 end 87 add airspace if airspace 88 end 89 end
Private Instance Methods
airspace_from(td)
click to toggle source
# File lib/aipp/regions/LF/ENR-2.1.rb 93 def airspace_from(td) 94 spans = td.children.split { _1.name == 'br' }.first.css(:span).drop_while { _1.text.match? '\s' } 95 source_type = spans[0].text.blank_to_nil 96 fail "unknown type `#{source_type}'" unless SOURCE_TYPES.has_key? source_type 97 AIXM.airspace( 98 name: anglicise(name: spans[1..-1].join(' ')), 99 type: SOURCE_TYPES.dig(source_type, :type), 100 local_type: SOURCE_TYPES.dig(source_type, :local_type) 101 ).tap do |airspace| 102 airspace.source = source(position: td.line) 103 end 104 end
class_from(text)
click to toggle source
# File lib/aipp/regions/LF/ENR-2.1.rb 106 def class_from(text) 107 text.strip 108 end
remarks_from(text)
click to toggle source
# File lib/aipp/regions/LF/ENR-2.1.rb 162 def remarks_from(text) 163 text.strip.gsub(/(\s)\s+/, '\1').blank_to_nil 164 end
services_from(td, remarks)
click to toggle source
# File lib/aipp/regions/LF/ENR-2.1.rb 110 def services_from(td, remarks) 111 text = td.text.cleanup 112 text = SERVICE_FIXES.fetch(text, text) # fix incomplete service columns 113 text.gsub!(/(info|app)\s+([\d.]{3,})/i, "\\1\n\\2") # put frequencies on separate line 114 text.gsub!(/(\d)\s*\/\s*(\d)/, "\\1\n\\2") # split frequencies onto separate lines 115 units, services = [], [] 116 text.split("\n").each do |line| 117 case line 118 when /^(.+(?:info|app))$/i # service 119 callsign = $1 120 service = AIXM.service( 121 # TODO: add source as soon as it is supported by components 122 # source: source(position: td.line), 123 type: :flight_information_service 124 ).tap do |service| 125 service.timetable = AIXM::H24 if remarks.match? /h\s?24/i 126 end 127 services << [service, callsign] 128 units.shift.add_service service 129 when /^(.*?)(\d{3}[.\d]*)(.*)$/ # frequency 130 label, freq, footnote = $1, $2, $3 131 service, callsign = services.last 132 frequency = AIXM.frequency( 133 transmission_f: AIXM.f(freq.to_f, :mhz), 134 callsigns: { en: callsign, fr: callsign } 135 ).tap do |frequency| 136 frequency.type = :standard 137 frequency.timetable = AIXM::H24 if remarks.match? /h\s?24/i 138 frequency.remarks = [ 139 (remarks.extract(/#{Regexp.escape(footnote.strip)}\s*([^\n]+)/).join(' / ') unless footnote.empty?), 140 label.strip 141 ].map(&:blank_to_nil).compact.join(' / ').blank_to_nil 142 end 143 service.add_frequency frequency 144 when /.*(?<!info|app|\d{3}|\))$/i # unit 145 unit = AIXM.unit( 146 source: source(position: td.line), 147 organisation: organisation_lf, # TODO: not yet implemented 148 name: line, 149 type: :flight_information_centre, 150 class: :icao 151 ) 152 units << ((u = find(unit).first) ? (unit = u) : (add unit)) 153 else 154 fail("cannot parse `#{text}'") 155 end 156 end 157 services = services.map(&:first) 158 fail("at least one service has no frequency") if services.any? { _1.frequencies.none? } 159 services 160 end